// // MPFunctionValue.m // MathPad // // Created by Kim Wittenburg on 11.10.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPFunctionValue.h" #import "MPTokenStream.h" #import "MPToken.h" #import "MPExpression.h" #import "MPMathRules.h" double defaultFunction(double value) {return value;}; @implementation MPFunctionValue { NSDecimalNumber *(^_function)(NSDecimalNumber *); } - (id)init { self = [super init]; if (self) { } return self; } - (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream { switch (tokenStream.currentToken.tokenType) { case MPSinToken: [tokenStream currentTokenConsumed]; _functionName = @"sin"; return [self initWithTokenStream:tokenStream function:&sin takesArcValue:YES returnsArcValue:NO]; case MPCosToken: [tokenStream currentTokenConsumed]; _functionName = @"cos"; return [self initWithTokenStream:tokenStream function:&cos takesArcValue:YES returnsArcValue:NO]; case MPTanToken: [tokenStream currentTokenConsumed]; _functionName = @"tan"; return [self initWithTokenStream:tokenStream function:&tan takesArcValue:YES returnsArcValue:NO]; case MPASinToken: [tokenStream currentTokenConsumed]; _functionName = @"asin"; return [self initWithTokenStream:tokenStream function:&asin takesArcValue:YES returnsArcValue:NO]; case MPACosToken: [tokenStream currentTokenConsumed]; _functionName = @"acos"; return [self initWithTokenStream:tokenStream function:&acos takesArcValue:YES returnsArcValue:NO]; case MPATanToken: [tokenStream currentTokenConsumed]; _functionName = @"atan"; return [self initWithTokenStream:tokenStream function:&atan takesArcValue:YES returnsArcValue:NO]; default: @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Expected Function" userInfo:nil]; break; } return self; } - (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream function:(double (*)(double))function takesArcValue:(BOOL)takesArc returnsArcValue:(BOOL)returnsArc { self = [self init]; if (self) { _valueGroup = [[MPValueGroup alloc] initWithTokenStream:tokenStream]; id __weak weakSelf = self; _function = ^(NSDecimalNumber *value){ if (takesArc) { value = [weakSelf convertToRadiantsIfNecessary:value]; } NSDecimalNumber *returnValue = [[NSDecimalNumber alloc] initWithDouble:function(value.doubleValue)]; if (returnsArc) { returnValue = [weakSelf convertToDegreesIfNecessary:returnValue]; } return returnValue; }; } return self; } - (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees { if ([MPMathRules sharedRules].isUsingDegrees) { // * M_PI / 180 return [[degrees decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithInteger:180]]; } else { return degrees; } } - (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants { if ([MPMathRules sharedRules].isUsingDegrees) { // * 180 / M_PI return [[radiants decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:180]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]]; } else { return radiants; } } - (BOOL)validate:(NSError *__autoreleasing *)error { return [self.valueGroup validate:error]; } - (NSDecimalNumber *)evaluate { return _function([self.valueGroup evaluate]); } @end