// // MPFunctionValue.m // MathPad // // Created by Kim Wittenburg on 11.10.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPElementaryFunctionTerm.h" #import "MPParsedExpression.h" #import "MPMathRules.h" @implementation MPElementaryFunctionTerm { NSDecimalNumber *(^_function)(NSDecimalNumber *); } - (instancetype)initWithFunctionIdentifier:(NSString *)function term:(MPTerm *)term { self = [super init]; if (self) { NSAssert(function != nil, @"function must not be nil."); NSAssert(term != nil, @"term must not be nil."); double (*func)(double); BOOL takesArc = NO; BOOL returnsArc = NO; if ([function isEqualToString:@"sin"]) { func = &sin; takesArc = YES; } else if ([function isEqualToString:@"cos"]) { func = &cos; takesArc = YES; } else if ([function isEqualToString:@"tan"]) { func = &tan; takesArc = YES; } else if ([function isEqualToString:@"asin"] || [function isEqualToString:@"arcsin"]) { func = &asin; returnsArc = YES; } else if ([function isEqualToString:@"acos"] || [function isEqualToString:@"arccos"]) { func = &acos; returnsArc = YES; } else if ([function isEqualToString:@"atan"] || [function isEqualToString:@"arctan"]) { func = &atan; returnsArc = YES; } else if ([function isEqualToString:@"lg"] || [function isEqualToString:@"log"]) { func = &log10; } else if ([function isEqualToString:@"ln"]) { func = &log; } else { NSAssert(true, @"function must be one of (sin, cos, tan, asin, arcsin, acos, arccos, atan, arctan, lg, log, ln)."); } [self setFunction:func takesArcValue:takesArc returnsArcValue:returnsArc]; _term = term; _functionIdentifier = function.copy; } return self; } - (void)setFunction:(double (*)(double))function takesArcValue:(BOOL)takesArc returnsArcValue:(BOOL)returnsArc { 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; }; } - (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; } } - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error { NSDecimalNumber *value = [self.term evaluate:error]; if (!value) { return nil; } NSDecimalNumber *result = _function(value); if ([result isEqualToNumber:[NSDecimalNumber notANumber]]) { result = nil; if (error) { NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"%@ is not defined for the value %@.", nil), self.functionIdentifier, [value descriptionWithLocale:[NSLocale currentLocale]]]; *error = [NSError errorWithDomain:MPMathKitErrorDomain code:102 userInfo:@{NSLocalizedDescriptionKey: errorDescription}]; } } return result; } @end