diff --git a/.gitignore b/.gitignore index 6e8308c..724f1f6 100644 --- a/.gitignore +++ b/.gitignore @@ -328,3 +328,4 @@ ASALocalRun/ .vscode/launch.json .vscode/tasks.json .vscode/settings.json +*.diagsession diff --git a/AbMath/AbMath.dll b/AbMath/AbMath.dll index 45e61c3..c597360 100644 Binary files a/AbMath/AbMath.dll and b/AbMath/AbMath.dll differ diff --git a/AbMath/Discrete/Apportionment/Appropriations.cs b/AbMath/Apportionment/Appropriations.cs similarity index 100% rename from AbMath/Discrete/Apportionment/Appropriations.cs rename to AbMath/Apportionment/Appropriations.cs diff --git a/AbMath/Discrete/Apportionment/Extensions.cs b/AbMath/Apportionment/Extensions.cs similarity index 100% rename from AbMath/Discrete/Apportionment/Extensions.cs rename to AbMath/Apportionment/Extensions.cs diff --git a/AbMath/Discrete/Apportionment/IApportionment.cs b/AbMath/Apportionment/IApportionment.cs similarity index 100% rename from AbMath/Discrete/Apportionment/IApportionment.cs rename to AbMath/Apportionment/IApportionment.cs diff --git a/AbMath/Discrete/Apportionment/Methods/Adams.cs b/AbMath/Apportionment/Methods/Adams.cs similarity index 100% rename from AbMath/Discrete/Apportionment/Methods/Adams.cs rename to AbMath/Apportionment/Methods/Adams.cs diff --git a/AbMath/Discrete/Apportionment/Methods/Hamilton.cs b/AbMath/Apportionment/Methods/Hamilton.cs similarity index 100% rename from AbMath/Discrete/Apportionment/Methods/Hamilton.cs rename to AbMath/Apportionment/Methods/Hamilton.cs diff --git a/AbMath/Discrete/Apportionment/Methods/Hunnington Hill.cs b/AbMath/Apportionment/Methods/Hunnington Hill.cs similarity index 100% rename from AbMath/Discrete/Apportionment/Methods/Hunnington Hill.cs rename to AbMath/Apportionment/Methods/Hunnington Hill.cs diff --git a/AbMath/Discrete/Apportionment/Methods/Jefferson.cs b/AbMath/Apportionment/Methods/Jefferson.cs similarity index 100% rename from AbMath/Discrete/Apportionment/Methods/Jefferson.cs rename to AbMath/Apportionment/Methods/Jefferson.cs diff --git a/AbMath/Discrete/Apportionment/Methods/Webster.cs b/AbMath/Apportionment/Methods/Webster.cs similarity index 100% rename from AbMath/Discrete/Apportionment/Methods/Webster.cs rename to AbMath/Apportionment/Methods/Webster.cs diff --git a/AbMath/Calculator/AST.cs b/AbMath/Calculator/AST.cs index 9783225..86482d3 100644 --- a/AbMath/Calculator/AST.cs +++ b/AbMath/Calculator/AST.cs @@ -3,6 +3,9 @@ using System.Collections.Specialized; using System.Diagnostics; using System.Linq; +using System.Text; +using AbMath.Calculator.Simplifications; +using AbMath.Utilities; namespace AbMath.Calculator { @@ -13,83 +16,498 @@ public class AST /// /// Sqrt - /// Log - - /// Imaginary - /// Division - /// Exponent - /// Subtraction - /// Addition - /// Multiplication - /// Swap - - /// Trig - + /// Trig - All other trig conversions. [Currently encomposes all the below trig rules] /// Trig Half Angle - Converts fractions to trig functions when appropriate /// Trig Half Angle Expansion - Converts trig functions to fractions /// Power Reduction - Converts trig functions to fractions /// Power Expansion - Converts fractions to trig functions /// Double Angle - Converts double angles to trig functions /// Double Angle Reduction - Converts trig functions to double angles + /// Sum - deals with all sum functions etc. /// Constants - /// - private enum SimplificationMode + public enum SimplificationMode { - Sqrt, Log, Imaginary, Division, Exponent, Subtraction, Addition, Multiplication, Swap, + Sqrt, Log, Division, Exponent, Subtraction, Addition, Multiplication, Swap, Trig, TrigHalfAngle, TrigHalfAngleExpansion, TrigPowerReduction, TrigPowerExpansion, TrigDoubleAngle, TrigDoubleAngleReduction, - Constants, Compress, COUNT + Sum, + Integral, + Constants, + Misc, + Compress, COUNT } private RPN _rpn; private RPN.DataStore _data; - private bool debug => _rpn.Data.DebugMode; + private bool debug => _data.DebugMode; private readonly RPN.Token _derive = new RPN.Token("derive", 1, RPN.Type.Function); + private readonly RPN.Token _sum = new RPN.Token("sum", 5, RPN.Type.Function); public event EventHandler Logger; public event EventHandler Output; + private OptimizerRuleEngine ruleManager; + + private Logger logger; public AST(RPN rpn) { _rpn = rpn; _data = rpn.Data; + logger = _data.Logger; RPN.Node.ResetCounter(); } + /// + /// Generates the rule set for all simplifications + /// + private void GenerateRuleSetSimplifications() + { + GenerateSqrtSimplifications(); + GenerateLogSimplifications(); + GenerateSubtractionSimplifications(); + GenerateDivisionSimplifications(); + GenerateMultiplicationSimplifications(); + GenerateAdditionSimplifications(); + GenerateExponentSimplifications(); + GenerateTrigSimplifications(); + GenerateSumSimplifications(); + GenerateIntegralSimplifications(); + GenerateMiscSimplifications(); + } + + private void GenerateSqrtSimplifications() + { + Rule negative = new Rule(Sqrt.SqrtNegativeNumbersRunnable, Sqrt.SqrtNegativeNumbers, "sqrt(-c) -> i Imaginary Number to Root"); + Rule sqrt = new Rule(Sqrt.SqrtToFuncRunnable, Sqrt.SqrtToFunc, "sqrt(g(x))^2 - > g(x)"); + Rule abs = new Rule(Sqrt.SqrtToAbsRunnable, Sqrt.SqrtToAbs, "sqrt(g(x)^2) -> abs(g(x))"); + Rule sqrtPower = new Rule(Sqrt.SqrtPowerFourRunnable, Sqrt.SqrtPowerFour, "sqrt(g(x)^n) where n is a multiple of 4. -> g(x)^n/2"); + + ruleManager.Add(SimplificationMode.Sqrt, negative); + ruleManager.Add(SimplificationMode.Sqrt, sqrt); + ruleManager.Add(SimplificationMode.Sqrt, abs); + ruleManager.Add(SimplificationMode.Sqrt, sqrtPower); + } + + private void GenerateLogSimplifications() + { + Rule logToLn = new Rule(Log.LogToLnRunnable, Log.LogToLn, "log(e,f(x)) - > ln(f(x))"); + + //This rule only can be a preprocessor rule and therefore should not be added to the rule manager! + Rule LnToLog = new Rule(Log.LnToLogRunnable, Log.LnToLog, "ln(f(x)) -> log(e,f(x))"); + + //These are candidates for preprocessing and post processing: + Rule logOne = new Rule(Log.LogOneRunnable,Log.LogOne,"log(b,1) -> 0"); + Rule logIdentical = new Rule(Log.LogIdentitcalRunnable, Log.LogIdentitcal, "log(b,b) -> 1"); + Rule logPowerExpansion = new Rule(Log.LogExponentExpansionRunnable, Log.LogExponentExpansion, "log(b,R^c) -> c * log(b,R)"); + + logOne.AddPreProcessingRule(LnToLog).AddPostProcessingRule(logToLn); + logIdentical.AddPreProcessingRule(LnToLog).AddPostProcessingRule(logToLn); + logPowerExpansion.AddPreProcessingRule(LnToLog).AddPostProcessingRule(logToLn); + + Rule logPower = new Rule(Log.LogPowerRunnable, Log.LogPower, "b^log(b,x) -> x"); + Rule logSummation = new Rule(Log.LogSummationRunnable, Log.LogSummation, "log(b,R) + log(b,S) -> log(b,R*S)"); + Rule logSubtraction = new Rule(Log.LogSubtractionRunnable, Log.LogSubtraction, "log(b,R) - log(b,S) -> log(b,R/S)"); + + //TODO: lnPower e^ln(f(x)) -> f(x) + //TODO: log(b,R^c) -> c * log(b,R) + //TODO: ln(e) -> 1 + + Rule lnSummation = new Rule(Log.LnSummationRunnable, Log.LnSummation, "ln(R) + ln(S) -> log(e,R) + log(e,S) -> ln(R*S)"); + Rule lnSubtraction = new Rule(Log.LnSubtractionRunnable, Log.LnSubtraction, "ln(R) - ln(S) -> log(e,R) - log(e,S) -> ln(R/S)"); + Rule lnPowerExpansion = new Rule(Log.LnPowerRuleRunnable, Log.LnPowerRule, "ln(R^c) -> c*ln(R)"); + + ruleManager.Add(SimplificationMode.Log, logOne); + ruleManager.Add(SimplificationMode.Log, logIdentical); + ruleManager.Add(SimplificationMode.Log, logPower); + ruleManager.Add(SimplificationMode.Log, logPowerExpansion); + + ruleManager.Add(SimplificationMode.Log, logSummation); + ruleManager.Add(SimplificationMode.Log, logSubtraction); + ruleManager.Add(SimplificationMode.Log, lnSummation); + ruleManager.Add(SimplificationMode.Log, lnSubtraction); + + ruleManager.Add(SimplificationMode.Log, lnPowerExpansion); + + ruleManager.Add(SimplificationMode.Log, logToLn); + } + + private void GenerateSubtractionSimplifications() + { + Rule setRule = new Rule(Subtraction.setRule, null, "Subtraction Set Rule"); + ruleManager.AddSetRule(SimplificationMode.Subtraction, setRule); + + Rule sameFunction = new Rule(Subtraction.SameFunctionRunnable, Subtraction.SameFunction, "f(x) - f(x) -> 0"); + Rule coefficientOneReduction = new Rule(Subtraction.CoefficientOneReductionRunnable, Subtraction.CoefficientOneReduction, "cf(x) - f(x) -> (c - 1)f(x)"); + Rule subtractionByZero = new Rule(Subtraction.SubtractionByZeroRunnable, Subtraction.SubtractionByZero, "f(x) - 0 -> f(x)"); + Rule ZeroSubtractedFunction = new Rule(Subtraction.ZeroSubtractedByFunctionRunnable, Subtraction.ZeroSubtractedByFunction,"0 - f(x) -> -f(x)"); + Rule subtractionDivisionCommmonDenominator = new Rule(Subtraction.SubtractionDivisionCommonDenominatorRunnable, + Subtraction.SubtractionDivisionCommonDenominator, "f(x)/g(x) - h(x)/g(x) -> [f(x) - h(x)]/g(x)"); + Rule coefficientReduction = new Rule(Subtraction.CoefficientReductionRunnable, Subtraction.CoefficientReduction, "Cf(x) - cf(x) -> (C - c)f(x)"); + + Rule constantToAddition = new Rule(Subtraction.ConstantToAdditionRunnable, Subtraction.ConstantToAddition, "f(x) - (-c) -> f(x) + c"); + Rule functionToAddition = new Rule(Subtraction.FunctionToAdditionRunnable, Subtraction.FunctionToAddition, "f(x) - (-c * g(x)) -> f(x) + c *g(x)"); + Rule distributive = new Rule(Subtraction.DistributiveSimpleRunnable, Subtraction.DistributiveSimple, "f(x) - (g(x) - h(x)) -> f(x) - g(x) + h(x) -> (f(x) + h(x)) - g(x)"); + + ruleManager.Add(SimplificationMode.Subtraction, sameFunction); + ruleManager.Add(SimplificationMode.Subtraction, coefficientOneReduction); + ruleManager.Add(SimplificationMode.Subtraction, subtractionByZero); + ruleManager.Add(SimplificationMode.Subtraction, ZeroSubtractedFunction); + ruleManager.Add(SimplificationMode.Subtraction, subtractionDivisionCommmonDenominator); + ruleManager.Add(SimplificationMode.Subtraction, coefficientReduction); + ruleManager.Add(SimplificationMode.Subtraction, constantToAddition); + ruleManager.Add(SimplificationMode.Subtraction, functionToAddition); + ruleManager.Add(SimplificationMode.Subtraction, distributive); + + //TODO: f(x)/g(x) /pm i(x)/j(x) -> [f(x)j(x)]/g(x)j(x) /pm i(x)g(x)/g(x)j(x) -> [f(x)j(x) /pm g(x)i(x)]/[g(x)j(x)] + } + + private void GenerateDivisionSimplifications() + { + Rule setRule = new Rule(Division.setRule, null, "Division Set Rule"); + + Rule divisionByZero = new Rule(Division.DivisionByZeroRunnable, Division.DivisionByZero, "f(x)/0 -> NaN"); + Rule divisionByOne = new Rule(Division.DivisionByOneRunnable, Division.DivisionByOne, "f(x)/1 -> f(x)"); + Rule gcd = new Rule(Division.GCDRunnable, Division.GCD, "(cC)/(cX) -> C/X"); + Rule divisionFlip = new Rule(Division.DivisionFlipRunnable, Division.DivisionFlip, "(f(x)/g(x))/(h(x)/j(x)) - > (f(x)j(x))/(g(x)h(x))"); + + Rule constantCancelation = new Rule(Division.DivisionCancelingRunnable, Division.DivisionCanceling, "(c * f(x))/c -> f(x) where c is not 0"); + + Rule powerReduction = new Rule(Division.PowerReductionRunnable, Division.PowerReduction, "Power Reduction"); + Rule divisionFlipTwo = new Rule(Division.DivisionFlipTwoRunnable, Division.DivisionFlipTwo, "[f(x)/g(x)]/ h(x) -> [f(x)/g(x)]/[h(x)/1] - > f(x)/[g(x) * h(x)]"); + + Rule factorialSimplificationToOne = new Rule(Division.FactorialCancellationRunnable, Division.FactorialCancellation, "f(x)!/f(x)! -> 1"); + Rule factorialComplexSimplification = new Rule(Division.FactorialRemovedRunnable, Division.FactorialRemoved, "[f(x)(x!)]/x! -> f(x)"); + ruleManager.AddSetRule(SimplificationMode.Division, setRule); + + ruleManager.Add(SimplificationMode.Division, divisionByZero); + ruleManager.Add(SimplificationMode.Division, divisionByOne); + ruleManager.Add(SimplificationMode.Division, gcd); + ruleManager.Add(SimplificationMode.Division, divisionFlip); + ruleManager.Add(SimplificationMode.Division, constantCancelation); + ruleManager.Add(SimplificationMode.Division, powerReduction); + ruleManager.Add(SimplificationMode.Division, divisionFlipTwo); + ruleManager.Add(SimplificationMode.Division, factorialSimplificationToOne); + ruleManager.Add(SimplificationMode.Division, factorialComplexSimplification); + + //TODO: 0/c when c is a constant or an expression that on solving will be a constant. + + //TODO: (c_0 * f(x))/c_1 where c_0, c_1 share a gcd that is not 1 and c_0 and c_1 are integers + //TODO: (c_0 * f(x))/(c_1 * g(x)) where ... + } + + private void GenerateMultiplicationSimplifications() + { + //TODO: If one of the leafs is a division and the other a number or variable + //TODO: Replace the requirement that we cannot do a simplification when a division is present to + //that we cannot do a simplification when a division has a variable in the denominator! + + Rule setRule = new Rule(Multiplication.setRule, null, "Multiplication Set Rule"); + Rule toExponent = new Rule(Multiplication.multiplicationToExponentRunnable, Multiplication.multiplicationToExponent, "f(x) * f(x) -> f(x)^2"); + Rule simplificationByOne = new Rule(Multiplication.multiplicationByOneRunnable, Multiplication.multiplicationByOne, "1 * f(x) -> f(x)"); + Rule simplificationByOneComplex = new Rule(Multiplication.multiplicationByOneComplexRunnable, Multiplication.multiplicationByOneComplex, "1 * f(x) || f(x) * 1 -> f(x)"); + + Rule multiplyByZero = new Rule(Multiplication.multiplicationByZeroRunnable, Multiplication.multiplicationByZero, "0 * f(x) -> 0"); + + Rule increaseExponent = new Rule(Multiplication.increaseExponentRunnable, Multiplication.increaseExponent, "R1: f(x)^n * f(x) -> f(x)^(n + 1)"); + Rule increaseExponentTwo = new Rule(Multiplication.increaseExponentTwoRunnable, Multiplication.increaseExponentTwo, "R2: f(x)^n * f(x) -> f(x)^(n + 1)"); + Rule increaseExponentThree = new Rule(Multiplication.increaseExponentThreeRunnable, Multiplication.increaseExponentThree, "R3: f(x)^n * f(x) -> f(x)^(n + 1"); + + Rule expressionDivision = new Rule(Multiplication.expressionTimesDivisionRunnable, Multiplication.expressionTimesDivision, "f(x) * [g(x)/h(x)] -> [f(x) * g(x)]/h(x)"); + Rule DivisionDivision = new Rule(Multiplication.divisionTimesDivisionRunnable, Multiplication.divisionTimesDivision, "[f(x)/g(x)] * [h(x)/j(x)] -> [f(x) * h(x)]/[g(x) * j(x)]"); + Rule negativeTimesNegative = new Rule(Multiplication.negativeTimesnegativeRunnable, Multiplication.negativeTimesnegative, "(-c)(-C) -> (c)(C)"); + Rule complexNegativeTimesNegative = new Rule(Multiplication.complexNegativeNegativeRunnable, Multiplication.complexNegativeNegative, "Complex: A negative times a negative is always positive."); + Rule negativeByConstant = new Rule(Multiplication.negativeTimesConstantRunnable, Multiplication.negativeTimesConstant, "-1 * c -> -c"); + Rule constantByNegative = new Rule(Multiplication.constantTimesNegativeRunnable, Multiplication.constantTimesNegative, "c * -1 -> -c"); + Rule negativeOneDistributed = new Rule(Multiplication.negativeOneDistributedRunnable, Multiplication.negativeOneDistributed, "-1[f(x) - g(x)] -> -f(x) + g(x) -> g(x) - f(x)"); + Rule dualNode = new Rule(Multiplication.dualNodeMultiplicationRunnable, + Multiplication.dualNodeMultiplication, "Dual Node"); + + + ruleManager.AddSetRule(SimplificationMode.Multiplication, setRule); + ruleManager.Add(SimplificationMode.Multiplication, toExponent); + ruleManager.Add(SimplificationMode.Multiplication, simplificationByOne); + ruleManager.Add(SimplificationMode.Multiplication, simplificationByOneComplex); + ruleManager.Add(SimplificationMode.Multiplication, multiplyByZero); + + ruleManager.Add(SimplificationMode.Multiplication, increaseExponent); + ruleManager.Add(SimplificationMode.Multiplication, increaseExponentTwo); + ruleManager.Add(SimplificationMode.Multiplication, increaseExponentThree); + + ruleManager.Add(SimplificationMode.Multiplication, dualNode); + ruleManager.Add(SimplificationMode.Multiplication, expressionDivision); + ruleManager.Add(SimplificationMode.Multiplication, DivisionDivision); + ruleManager.Add(SimplificationMode.Multiplication, negativeTimesNegative); + ruleManager.Add(SimplificationMode.Multiplication, complexNegativeTimesNegative); + ruleManager.Add(SimplificationMode.Multiplication, negativeByConstant); + ruleManager.Add(SimplificationMode.Multiplication, constantByNegative); + ruleManager.Add(SimplificationMode.Multiplication, negativeOneDistributed); + } + + private void GenerateAdditionSimplifications() + { + Rule setRule = new Rule(Addition.setRule, null, "Addition Set Rule"); + ruleManager.AddSetRule(SimplificationMode.Addition,setRule); + + Rule additionToMultiplication = new Rule(Addition.AdditionToMultiplicationRunnable, Addition.AdditionToMultiplication, "f(x) + f(x) -> 2 * f(x)"); + Rule zeroAddition = new Rule(Addition.ZeroAdditionRunnable, Addition.ZeroAddition, "f(x) + 0 -> f(x)"); + Rule simpleCoefficient = new Rule(Addition.SimpleCoefficientRunnable, Addition.SimpleCoefficient, "cf(x) + f(x) -> (c + 1)f(x) + 0"); + Rule complexCoefficient = new Rule(Addition.ComplexCoefficientRunnable, Addition.ComplexCoefficient, "cf(x) + Cf(x) -> (c + C)f(x) + 0"); + Rule additionSwap = new Rule(Addition.AdditionSwapRunnable, Addition.AdditionSwap, "-f(x) + g(x) -> g(x) - f(x)"); + Rule toSubtractionRuleOne = new Rule(Addition.AdditionToSubtractionRuleOneRunnable, Addition.AdditionToSubtractionRuleOne, "Addition can be converted to subtraction R1"); + Rule toSubtractionRuleTwo = new Rule(Addition.AdditionToSubtractionRuleTwoRunnable, Addition.AdditionToSubtractionRuleTwo, "Addition can be converted to subtraction R2"); + Rule complex = new Rule(Addition.ComplexNodeAdditionRunnable, Addition.ComplexNodeAddition, "f(x) + f(x) - g(x) -> 2 * f(x) - g(x)"); + Rule division = new Rule(Addition.DivisionAdditionRunnable, Addition.DivisionAddition, "f(x)/g(x) + h(x)/g(x) -> [f(x) + h(x)]/g(x)"); + + ruleManager.Add(SimplificationMode.Addition, additionToMultiplication); + ruleManager.Add(SimplificationMode.Addition, zeroAddition); + ruleManager.Add(SimplificationMode.Addition, simpleCoefficient); + ruleManager.Add(SimplificationMode.Addition, complexCoefficient); + ruleManager.Add(SimplificationMode.Addition, additionSwap); + ruleManager.Add(SimplificationMode.Addition, toSubtractionRuleOne); + ruleManager.Add(SimplificationMode.Addition, toSubtractionRuleTwo); + ruleManager.Add(SimplificationMode.Addition, complex); + ruleManager.Add(SimplificationMode.Addition, division); + //TODO: -c * f(x) + g(x) -> g(x) - c * f(x) + //TODO: f(x)/g(x) + i(x)/j(x) -> [f(x)j(x)]/g(x)j(x) + i(x)g(x)/g(x)j(x) -> [f(x)j(x) + g(x)i(x)]/[g(x)j(x)] + } + + private void GenerateExponentSimplifications() + { + Rule setRule = new Rule(Exponent.setRule, null, "Exponent Set Rule"); + ruleManager.AddSetRule(SimplificationMode.Exponent, setRule); + + Rule functionRaisedToOne = new Rule(Exponent.functionRaisedToOneRunnable, Exponent.functionRaisedToOne, "f(x)^1 -> f(x)"); + Rule functionRaisedToZero = new Rule(Exponent.functionRaisedToZeroRunnable, Exponent.functionRaisedToZero, "f(x)^0 -> 1"); + Rule zeroRaisedToConstant = new Rule(Exponent.zeroRaisedToConstantRunnable, Exponent.zeroRaisedToConstant, "0^c where c > 0 -> 0"); + Rule oneRaisedToFunction = new Rule(Exponent.oneRaisedToFunctionRunnable, Exponent.oneRaisedToFunction, "1^(fx) -> 1"); + + Rule toDivision = new Rule(Exponent.toDivisionRunnable, Exponent.toDivision, "f(x)^-c -> 1/f(x)^c"); + Rule toSqrt = new Rule(Exponent.toSqrtRunnable, Exponent.toSqrt, "f(x)^0.5 -> sqrt( f(x) )"); + + Rule exponentToExponent = new Rule(Exponent.ExponentToExponentRunnable, Exponent.ExponentToExponent, + "(f(x)^c)^a -> f(x)^[c * a]"); + Rule negativeConstantRaisedToAPowerOfTwo = new Rule(Exponent.NegativeConstantRaisedToAPowerOfTwoRunnable, + Exponent.NegativeConstantRaisedToAPowerOfTwo, "c_1^c_2 where c_2 is even and c_1 < 0 -> [-1 * c_1]^c_2"); + Rule constantRaisedToConstant = new Rule(Exponent.ConstantRaisedToConstantRunnable, Exponent.ConstantRaisedToConstant, "c^k -> a"); + Rule absRaisedToPowerTwo = new Rule(Exponent.AbsRaisedToPowerofTwoRunnable, Exponent.AbsRaisedToPowerofTwo, "abs(f(x))^2 -> [ sqrt(f(x) ^ 2) ]^2 -> sqrt(f(x)^2)^2 -> f(x)^2"); + + ruleManager.Add(SimplificationMode.Exponent, functionRaisedToOne); + ruleManager.Add(SimplificationMode.Exponent, functionRaisedToZero); + ruleManager.Add(SimplificationMode.Exponent, zeroRaisedToConstant); + ruleManager.Add(SimplificationMode.Exponent, oneRaisedToFunction); + ruleManager.Add(SimplificationMode.Exponent, toDivision); + ruleManager.Add(SimplificationMode.Exponent, toSqrt); + ruleManager.Add(SimplificationMode.Exponent, exponentToExponent); + + ruleManager.Add(SimplificationMode.Exponent, negativeConstantRaisedToAPowerOfTwo); + ruleManager.Add(SimplificationMode.Exponent, constantRaisedToConstant); + ruleManager.Add(SimplificationMode.Exponent, absRaisedToPowerTwo); + } + + private void GenerateTrigSimplifications() + { + //TODO: + //[f(x) * cos(x)]/[g(x) * sin(x)] -> [f(x) * cot(x)]/g(x) + //[f(x) * sin(x)]/cos(x) -> f(x) * tan(x) + //sin(x)/[f(x) * cos(x)] -> tan(x)/f(x) + //[f(x) * sin(x)]/[g(x) * cos(x)] -> [f(x) * tan(x)]/g(x) + + //TODO: [1 + tan(f(x))^2] -> sec(f(x))^2 + //TODO: [cot(f(x))^2 + 1] -> csc(f(x))^2 + + //These will probably violate domain constraints ? + //TODO: sec(x)^2 - tan(x)^2 = 1 + //TODO: cot(x)^2 + 1 = csc(x)^2 + //TODO: csc(x)^2 - cot(x)^2 = 1 + + //TODO: Double Angle (->) (I wonder if double angle could be done through power reduction instead...) + //[cos(x)^2 - sin(x)^2] = cos(2x) + //1 - 2sin(x)^2 = cos(2x) + //2cos(x)^2 - 1 = cos(2x) + //2sin(x)cos(x) = sin(2x) + //[2tan(x)]/1 - tan(x)^2] = tan(2x) + + //TODO: Power Reducing (Exclusive with Power Expansion) + //[1 - cos(2x)]/2 <- sin(x)^2 + //[1 + cos(2x)]/2 <- cos(x)^2 + //[1 - cos(2x)]/[1 + cos(2x)] <- tan(x)^2 + + //TODO: Power Expansion (Exclusive with Power Reduction) + //[1 - cos(2x)]/2 -> sin(x)^2 + //[1 + cos(2x)]/2 -> cos(x)^2 + //[1 - cos(2x)]/[1 + cos(2x)] -> tan(x)^2 + + //TODO: Shifting + //cos(x - pi/2) = sin(x) + //sin(pi/2 - x) = sin(pi/2 + x) = cos(x) + + //Complex Conversions + Rule CosOverSinToCotComplex = new Rule(Trig.CosOverSinComplexRunnable, Trig.CosOverSinComplex, "[f(x) * cos(x)]/sin(x) -> f(x) * cot(x)"); + Rule CosOverSinToCotComplexTwo = new Rule(Trig.CosOverSinToCotComplexRunnable, Trig.CosOverSinToCotComplex, "cos(x)/[f(x) * sin(x)] -> cot(x)/f(x)"); + + ruleManager.Add(SimplificationMode.Trig, CosOverSinToCotComplex); + ruleManager.Add(SimplificationMode.Trig, CosOverSinToCotComplexTwo); + + //Simple Conversions + Rule CosOverSinToCot = new Rule(Trig.CosOverSinToCotRunnable, Trig.CosOverSinToCot, "cos(x)/sin(x) -> cot(x)"); + Rule SinOverCosToTan = new Rule(Trig.SinOverCosRunnable, Trig.SinOverCos, "sin(x)/cos(x) -> tan(x)"); + Rule SecUnderToCos = new Rule(Trig.SecUnderToCosRunnable, Trig.SecUnderToCos, "f(x)/sec(g(x)) -> f(x)cos(g(x))"); + Rule CscUnderToSin = new Rule(Trig.CscUnderToSinRunnable, Trig.CscUnderToSin, "f(x)/csc(g(x)) -> f(x)sin(g(x))"); + Rule CotUnderToTan = new Rule(Trig.CotUnderToTanRunnable, Trig.CotUnderToTan, "f(x)/cot(g(x)) -> f(x)tan(g(x))"); + Rule CosUnderToSec = new Rule(Trig.CosUnderToSecRunnable, Trig.CosUnderToSec, "f(x)/cos(g(x)) -> f(x)sec(g(x))"); + Rule SinUnderToCsc = new Rule(Trig.SinUnderToCscRunnable, Trig.SinUnderToCsc, "f(x)/sin(g(x)) -> f(x)csc(g(x))"); + Rule TanUnderToCot = new Rule(Trig.TanUnderToCotRunnable, Trig.TanUnderToCot, "f(x)/tan(g(x)) -> f(x)cot(g(x))"); + + ruleManager.Add(SimplificationMode.Trig, CosOverSinToCot); + ruleManager.Add(SimplificationMode.Trig, SinOverCosToTan); + ruleManager.Add(SimplificationMode.Trig, SecUnderToCos); + ruleManager.Add(SimplificationMode.Trig, CscUnderToSin); + ruleManager.Add(SimplificationMode.Trig, CotUnderToTan); + ruleManager.Add(SimplificationMode.Trig, CosUnderToSec); + ruleManager.Add(SimplificationMode.Trig, SinUnderToCsc); + ruleManager.Add(SimplificationMode.Trig, TanUnderToCot); + + //Even Identity + Rule CosEven = new Rule(Trig.CosEvenIdentityRunnable, Trig.CosEvenIdentity, "cos(-f(x)) -> cos(f(x))"); + Rule SecEven = new Rule(Trig.SecEvenIdentityRunnable, Trig.SecEvenIdentity, "sec(-f(x)) -> sec(f(x))"); + + ruleManager.Add(SimplificationMode.Trig, CosEven); + ruleManager.Add(SimplificationMode.Trig, SecEven); + + //Odd Identity + Rule SinOdd = new Rule(Trig.SinOddIdentityRunnable, Trig.SinOddIdentity, "sin(-f(x)) -> -1 * sin(f(x))"); + Rule TanOdd = new Rule(Trig.TanOddIdentityRunnable, Trig.TanOddIdentity, "tan(-f(x)) -> -1 * tan(f(x))"); + Rule CotOdd = new Rule(Trig.CotOddIdentityRunnable, Trig.CotOddIdentity, "cot(-f(x)) -> -1 * cot(f(x))"); + Rule CscOdd = new Rule(Trig.CscOddIdentityRunnable, Trig.CscOddIdentity, "csc(-f(x)) -> -1 * csc(f(x))"); + + ruleManager.Add(SimplificationMode.Trig, SinOdd); + ruleManager.Add(SimplificationMode.Trig, TanOdd); + ruleManager.Add(SimplificationMode.Trig, CotOdd); + ruleManager.Add(SimplificationMode.Trig, CscOdd); + + Rule TrigIdentitySinToCos = new Rule(Trig.TrigIdentitySinToCosRunnable, Trig.TrigIdentitySinToCos, "1 - sin(x)^2 -> cos(x)^2"); + Rule TrigIdentityCosToSin = new Rule(Trig.TrigIdentityCosToSinRunnable, Trig.TrigIdentityCosToSin, "1 - cos(x)^2 -> sin(x)^2"); + Rule TrigIdentitySinPlusCos = new Rule(Trig.TrigIdentitySinPlusCosRunnable, Trig.TrigIdentitySinPlusCos, "sin²(x) + cos²(x) -> 1"); + + ruleManager.Add(SimplificationMode.Trig, TrigIdentitySinPlusCos); + ruleManager.Add(SimplificationMode.Trig, TrigIdentitySinToCos); + ruleManager.Add(SimplificationMode.Trig, TrigIdentityCosToSin); + + } + + private void GenerateSumSimplifications() + { + Rule setRule = new Rule(Sum.setUp, null, "Sum Set Rule"); + + ruleManager.AddSetRule(SimplificationMode.Sum, setRule); + + Rule propagation = new Rule(Sum.PropagationRunnable, Sum.Propagation, "sum(f(x) + g(x),x,a,b) -> sum(f(x),x,a,b) + sum(g(x),x,a,b)"); //No bounds change + Rule coefficient = new Rule(Sum.CoefficientRunnable, Sum.Coefficient, "sum(k * f(x),x,a,b) -> k * sum(f(x),x,a,b)"); //No bounds change + Rule coefficientDivision = new Rule(Sum.CoefficientDivisionRunnable,Sum.CoefficientDivision,"sum(f(x)/k,x,a,b) -> sum(f(x),x,a,b)/k"); //No bounds change! + + Rule variable = new Rule(Sum.VariableRunnable, Sum.Variable, "sum(x,x,0,a) -> [a(a + 1)]/2"); + Rule constantComplex = new Rule(Sum.ConstantComplexRunnable, Sum.ConstantComplex, "sum(k,x,a,b) -> k(b - a + 1)"); + Rule power = new Rule(Sum.PowerRunnable, Sum.Power, "sum(x^p,x,1,n) -> [1/(p + 1)] * sum( (-1)^j * [(p + 1)!]/ [j! * (p - j + 1)!] * B_j * n^(p - j + 1),j,0,p)"); + + ruleManager.Add(SimplificationMode.Sum, propagation); + ruleManager.Add(SimplificationMode.Sum, coefficient); + ruleManager.Add(SimplificationMode.Sum, coefficientDivision); + + ruleManager.Add(SimplificationMode.Sum, variable); + ruleManager.Add(SimplificationMode.Sum, power); + ruleManager.Add(SimplificationMode.Sum, constantComplex); + + //TODO: + // sum(f(x),x,a,b) -> sum(f(x),x,0,b) - sum(f(x),x,0,a - 1) + // sum(x^p,x,0,n) -> 0^p + sum(x^p,x,1,n) + } + + private void GenerateIntegralSimplifications() + { + Rule setRule = new Rule(Integrate.setUp, null, "Integral Set Rule"); + Rule propagation = new Rule(Integrate.PropagationRunnable, Integrate.Propagation, "integrate(f(x) + g(x),x,a,b) -> integrate(f(x),x,a,b) + integrate(g(x),x,a,b)"); //No bounds change + Rule constants = new Rule(Integrate.ConstantsRunnable, Integrate.Constants, "integrate(k,x,a,b) -> k(b - a)"); + Rule coefficient = new Rule(Integrate.CoefficientRunnable, Integrate.Coefficient,"integrate(cf(x),x,a,b) -> c*integrate(f(x),x,a,b)"); + + Rule singleVariable = new Rule(Integrate.SingleVariableRunnable, Integrate.SingleVariable,"integrate(x,x,a,b) -> (b^2 - a^2)/2"); + + ruleManager.AddSetRule(SimplificationMode.Integral, setRule); + + ruleManager.Add(SimplificationMode.Integral, propagation); + ruleManager.Add(SimplificationMode.Integral, constants); + //2) Coefficient + // integrate(c*sin(x),x,a,b) -> c * integrate(sin(x),x,a,b) + // integrate(sin(y)*x,x,a,b) + + ruleManager.Add(SimplificationMode.Integral, coefficient); + + //3) Coefficient Division + // integrate(f(x)/c,x,a,b) -> integrate(f(x),x,a,b)/c + //3.5) Coefficient Division Two + // integrate(c/f(x),x,a,b) -> c * integrate(1/f(x),x,a,b) + ruleManager.Add(SimplificationMode.Integral, singleVariable); + //5) Power (n = -1) + // integrate(1/x,x,a,b) -> ln(b) - ln(a) -> ln(b/a) + //6) Power + // integrate(x^n,x,a,b) -> (b^(n + 1) - a^(n + 1))/(n + 1) where n is a number that is not -1 + //7) Euler Exponent + // integrate(e^x,x,a,b) -> e^b - e^a + //8) Exponent + // integrate(k^x,x,a,b) -> [b^x]/ln(b) - [a^x]/ln(a) + //9) Cos + // integrate(cos(x),x,a,b) -> sin(b) - sin(a) + //10) Sin + // integrate(sin(x),x,a,b) -> -cos(b) - -cos(a) -> cos(a) - cos(b) + //11) sec(x)^2 + // integrate(sec(x)^2,x,a,b) -> tan(b) - tan(a) + //12) sec(x)tan(x) + // integrate(...,x,a,b) -> sec(b) - sec(a) + //13) 1/sqrt{1 - x^2} + // integrate(...,x,a,b) -> arcsin(b) - arcsin(a) + //14) 1/(1 + x^2) + // integrate(...,x,a,b) -> arctan(b) - arctan(a) + //15) tan(x) + // integrate(tan(x),x,a,b) -> -ln(abs(cos(b)) - -ln(abs(cos(a)) -> ln(abs(cos(a)) - ln(abs(cos(b)) + //16) cot(x) + // integrate(cot(x),x,a,b) -> ln(abs(sin(b))) - ln(abs(sin(a))) + //17) sec(x) -> ln( abs(sec(x) + tan(x)) ) + //18) csc(x) -> -ln( abs(csc(x) + cot(x)) ) + //All other ones fall back to numerical integration + } + + private void GenerateMiscSimplifications() + { + Rule factorial = new Rule(Misc.ZeroFactorialRunnable, Misc.ZeroFactorial, "(0! || 1!) -> 1"); + + ruleManager.Add(SimplificationMode.Misc, factorial); + } + public RPN.Node Generate(RPN.Token[] input) { Stopwatch SW = new Stopwatch(); SW.Start(); - - Stack stack = new Stack(5); - - for (int i = 0; i < input.Length; i++) - { - RPN.Node node = new RPN.Node(input[i]); - if (node.IsOperator() || node.IsFunction()) - { - //Due to the nature of PostFix we know that all children - //of a function or operator have already been processed before this point - //this ensures we do not have any overflows or exceptions. - RPN.Node[] range = new RPN.Node[node.Token.Arguments]; - for (int j = 0; j < node.Token.Arguments; j++) - { - range[j] = stack.Pop(); - } - node.AddChild(range); - } - stack.Push(node); //Push new tree into the stack - } + RPN.Node node = RPN.Node.Generate(input); //This prevents the reassignment of the root node if (Root is null) { - Root = stack.Peek(); + Root = node; } SW.Stop(); _rpn.Data.AddTimeRecord("AST Generate", SW); - return stack.Pop(); + return node; } /// @@ -101,6 +519,16 @@ public AST Simplify() Normalize(); Stopwatch sw = new Stopwatch(); + + ruleManager = new OptimizerRuleEngine(logger); + //Let us generate the rules here if not already creates + GenerateRuleSetSimplifications(); + ruleManager.debug = false; + if (debug) + { + ruleManager.debug = true; + } + sw.Start(); int pass = 0; string hash = string.Empty; @@ -125,14 +553,14 @@ public AST Simplify() } else { - tracker.Add(hash, new OptimizationTracker() {count = 1, Hash = hash}); + tracker.Add(hash, new OptimizationTracker() { count = 1, Hash = hash }); } _data.AddTimeRecord("AST.GetHash", sw1); if (debug) { - Write($"{pass}. {Root.ToInfix()}."); + Write($"Pass:{pass} {Root.ToInfix(_data)}."); } Simplify(Root); @@ -141,7 +569,18 @@ public AST Simplify() sw.Stop(); _data.AddTimeRecord("AST Simplify", sw); - Normalize(); + if (debug) + { + Write("Before being normalized the tree looks like:"); + Write(Root.ToInfix(_data)); + Write(Root.Print()); + } + + Normalize(); //This distorts the tree :( + + Write(""); + Write(ruleManager.ToString()); + return this; } @@ -152,8 +591,8 @@ private void Normalize() Stopwatch sw = new Stopwatch(); sw.Start(); - _rpn.Data.AddFunction("internal_product", new RPN.Function()); - _rpn.Data.AddFunction("internal_sum", new RPN.Function()); + _rpn.Data.AddFunction("internal_product", new Function()); + _rpn.Data.AddFunction("internal_sum", new Function()); expand(Root); InternalSwap(Root); @@ -168,12 +607,11 @@ private void Normalize() private void Simplify(RPN.Node node) { #if DEBUG - Write(Root.ToInfix()); + Write(Root.ToInfix(_data)); #endif - + //We want to reduce this! Simplify(node, SimplificationMode.Sqrt); Simplify(node, SimplificationMode.Log); - Simplify(node, SimplificationMode.Imaginary); Simplify(node, SimplificationMode.Division); Simplify(node, SimplificationMode.Exponent); //This will make all negative exponennts into divisions @@ -181,11 +619,15 @@ private void Simplify(RPN.Node node) Simplify(node, SimplificationMode.Addition); Simplify(node, SimplificationMode.Trig); Simplify(node, SimplificationMode.Multiplication); - Simplify(node, SimplificationMode.Swap); + Simplify(node, SimplificationMode.Swap); + Simplify(node, SimplificationMode.Misc); + Simplify(node, SimplificationMode.Sum); + Simplify(node, SimplificationMode.Integral); + Swap(node); #if DEBUG - Write(Root.ToInfix()); + Write(Root.ToInfix(_data)); #endif } @@ -197,753 +639,38 @@ private void Simplify(RPN.Node node, SimplificationMode mode) Stack stack = new Stack(); stack.Push(node); while (stack.Count > 0) - { - node = stack.Pop(); - //Write(node.GetHash()); - - //If Root is a number abort. - if (Root.IsNumber()) - { - return; - } - - if (node.IsNumber() || node.IsConstant()) - { - continue; - } - - if (mode == SimplificationMode.Sqrt) - { - if (node.IsExponent() && node.Children[0].IsNumber(2) && node.Children[1].IsSqrt()) - { - if (debug) - { - Write("\tsqrt(g(x))^2 -> g(x)"); - } - - Assign(node, node.Children[1].Children[0]); - } - else if (node.IsSqrt() && node.Children[0].IsExponent() && node.Children[0].Children[0].IsNumber(2)) - { - if (debug) - { - Write("\tsqrt(g(x)^2) -> abs(g(x))"); - } - - RPN.Node abs = new RPN.Node(new[] {node.Children[0].Children[1]}, - new RPN.Token("abs", 1, RPN.Type.Function)); - Assign(node, abs); - } - else if (node.IsSqrt() && node.Children[0].IsExponent() && - node.Children[0].Children[0].IsNumber() && - node.Children[0].Children[0].GetNumber() % 4 == 0) - { - if (debug) - { - Write("\tsqrt(g(x)^n) where n is a multiple of 4. -> g(x)^n/2"); - } - - RPN.Node exponent = - new RPN.Node( - new[] - { - new RPN.Node(node.Children[0].Children[0].GetNumber() / 2), - node.Children[0].Children[1] - }, new RPN.Token("^", 2, RPN.Type.Operator)); - Assign(node, exponent); - } - } - else if (mode == SimplificationMode.Log) - { - RPN.Node temp = null; - if (node.Token.IsLog() && node.Children[0].IsNumber(1)) - { - if (debug) - { - Write("\tlog(b,1) -> 0"); - } - - temp = new RPN.Node(0); - } - else if (node.Token.IsLog() && node.ChildrenAreIdentical()) - { - if (debug) - { - Write("\tlog(b,b) -> 1"); - } - - temp = new RPN.Node(1); - } - else if (node.IsExponent() && node.Children[0].IsLog() && - node.Children[0].Children[1].Matches(node.Children[1])) - { - if (debug) - { - Write($"\tb^log(b,x) -> x"); - } - - temp = node.Children[0].Children[0]; - } - else if (node.IsLog() && node.Children[0].IsExponent() && - !node.Children[0].Children[1].IsVariable()) - { - if (debug) - { - Write("\tlog(b,R^c) -> c * log(b,R)"); - } - - RPN.Node exponent = node.Children[0]; - RPN.Node baseNode = exponent.Children[1]; - RPN.Node power = exponent.Children[0]; - - RPN.Node log = new RPN.Node(new[] {Clone(baseNode), node.Children[1]}, - new RPN.Token("log", 2, RPN.Type.Function)); - RPN.Node multiply = new RPN.Node(new[] {log, power}, new RPN.Token("*", 2, RPN.Type.Operator)); - temp = multiply; - } - else if (node.IsLn() && node.Children[0].IsExponent() && !node.Children[0].Children[1].IsVariable()) - { - if (debug) - { - Write("\tln(R^c) -> log(e,R^c) -> c * ln(R)"); - } - - RPN.Node exponent = node.Children[0]; - RPN.Node power = exponent.Children[0]; - - RPN.Node log = new RPN.Node(new[] {exponent.Children[1]}, - new RPN.Token("ln", 1, RPN.Type.Function)); - RPN.Node multiply = new RPN.Node(new[] {log, power}, new RPN.Token("*", 2, RPN.Type.Operator)); - temp = multiply; - } - else if ((node.IsAddition() || node.IsSubtraction()) && node.Children[0].IsLog() && - node.Children[1].IsLog() && - node.Children[0].Children[1].Matches(node.Children[1].Children[1])) - { - RPN.Node parameter; - if (node.IsAddition()) - { - Write("\tlog(b,R) + log(b,S) -> log(b,R*S)"); - parameter = new RPN.Node(new[] {node.Children[0].Children[0], node.Children[1].Children[0]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - } - else - { - Write("\tlog(b,R) - log(b,S) -> log(b,R/S)"); - parameter = new RPN.Node(new[] {node.Children[0].Children[0], node.Children[1].Children[0]}, - new RPN.Token("/", 2, RPN.Type.Operator)); - } - - RPN.Node baseNode = node.Children[0].Children[1]; - RPN.Node log = new RPN.Node(new[] {parameter, baseNode}, - new RPN.Token("log", 2, RPN.Type.Function)); - temp = log; - } - else if ((node.IsAddition() || node.IsSubtraction()) && node.Children[0].IsLn() && - node.Children[1].IsLn()) - { - RPN.Node parameter; - if (node.IsAddition()) - { - Write("\tln(R) + ln(S) -> log(e,R) + log(e,S) -> ln(R*S)"); - parameter = new RPN.Node(new[] {node.Children[0].Children[0], node.Children[1].Children[0]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - } - else - { - Write("\tln(R) - ln(S) -> log(e,R) - log(e,S) -> ln(R/S)"); - parameter = new RPN.Node(new[] {node.Children[0].Children[0], node.Children[1].Children[0]}, - new RPN.Token("/", 2, RPN.Type.Operator)); - } - - RPN.Node ln = new RPN.Node(new[] {parameter}, new RPN.Token("ln", 1, RPN.Type.Function)); - temp = ln; - } - - if (temp != null) - { - Assign(node, temp); - } - } - else if (mode == SimplificationMode.Imaginary && node.IsSqrt()) - { - //Any sqrt function with a negative number -> Imaginary number to the root node - //An imaginary number propagates anyways - if (node.Children[0].IsLessThanNumber(0)) - { - SetRoot(new RPN.Node(double.NaN)); - Write($"\tSqrt Imaginary Number -> Root."); - } - - //MAYBE: Any sqrt function with any non-positive number -> Cannot simplify further?? - } - else if (mode == SimplificationMode.Division && node.IsDivision()) - { - //if there are any divide by zero exceptions -> NaN to the root node - //NaN propagate anyways - if (node.Children[0].IsNumber(0)) - { - SetRoot(new RPN.Node(double.NaN)); - Write("\tDivision by zero -> Root"); - } - else if (node.Children[0].IsNumber(1)) - { - Write("\tDivision by one"); - Assign(node, node.Children[1]); - } - //gcd if the leafs are both numbers since the values of the leafs themselves are changed - //we don't have to worry about if the node is the root or not - else if (node.Children[0].IsInteger() && node.Children[1].IsInteger()) - { - double num1 = node.Children[0].GetNumber(); - double num2 = node.Children[1].GetNumber(); - double gcd = RPN.DoFunctions.Gcd(new double[] {num1, num2}); - - node.Replace(node.Children[0], new RPN.Node((num1 / gcd))); - node.Replace(node.Children[1], new RPN.Node((num2 / gcd))); - Write("\tDivision GCD."); - } - else if (node.Children[0].IsDivision() && node.Children[1].IsDivision()) - { - Write("\tDivison Flip"); - RPN.Node[] numerator = {node.Children[0].Children[1], node.Children[1].Children[1]}; - RPN.Node[] denominator = {node.Children[0].Children[0], node.Children[1].Children[0]}; - - RPN.Node top = new RPN.Node(new[] {denominator[0], numerator[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node bottom = new RPN.Node(new[] {denominator[1], numerator[0]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {bottom, top}, new RPN.Token("/", 2, RPN.Type.Operator)); - Assign(node, division); - } - else if (node[1].IsMultiplication() && node[0].IsNumberOrConstant() && - node[0].Matches(node[1, 1]) && !node[1, 1].IsNumber(0)) - { - Write("\t(c * f(x))/c -> f(x) where c is not 0"); - Assign(node, node[1, 0]); - } - else if (node[0].IsExponent() && node[1].IsExponent() && node[0, 0].IsInteger() && - node[1, 0].IsInteger() && node[0, 1].Matches(node[1, 1])) - { - int reduction = Math.Min((int) node[0, 0].GetNumber(), (int) node[1, 0].GetNumber()) - 1; - node[0, 0].Replace(node[0, 0].GetNumber() - reduction); - node[1, 0].Replace(node[1, 0].GetNumber() - reduction); - Write("\tPower Reduction"); - } - else if (node[1].IsDivision()) - { - Write("\t[f(x)/g(x)]/ h(x) -> [f(x)/g(x)]/[h(x)/1] - > f(x)/[g(x) * h(x)]"); - RPN.Node numerator = node[1, 1]; - RPN.Node denominator = new RPN.Node(new[] {node[0], node[1, 0]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {denominator, numerator}, - new RPN.Token("/", 2, RPN.Type.Operator)); - Assign(node, division); - } - - //TODO: (c_0 * f(x))/c_1 where c_0, c_1 share a gcd that is not 1 and c_0 and c_1 are integers - //TODO: (c_0 * f(x))/(c_1 * g(x)) where ... - } - else if (mode == SimplificationMode.Subtraction && node.IsSubtraction()) - { - //3sin(x) - 3sin(x) - if (node.ChildrenAreIdentical() && !node.containsDomainViolation()) - { - Write("\tSimplification: Subtraction"); - Assign(node, new RPN.Node(0)); - } - //3sin(x) - 2sin(x) - else if (node.Children[0].IsMultiplication() && node.Children[1].IsMultiplication()) - { - if (node.Children[0].Children[1].IsNumber() && node.Children[1].Children[1].IsNumber() && - node.Children[0].Children[0].Matches(node.Children[1].Children[0])) - { - Write("\tSimplification: Subtraction Dual Node"); - double coefficient = node.Children[1].Children[1].GetNumber() - - node.Children[0].Children[1].GetNumber(); - node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(0)); - node.Children[1].Replace(node.Children[1].Children[1], new RPN.Node(coefficient)); - } - } - //3sin(x) - sin(x) - else if (node.Children[1].IsMultiplication() && node.Children[1].Children[1].IsNumber() && - node.Children[1].Children[0].Matches(node.Children[0])) - { - Write("\tSimplification: Subtraction: Dual Node: Sub one."); - node.Replace(node.Children[0], new RPN.Node(0)); - node.Children[1].Replace(node.Children[1].Children[1], - new RPN.Node(node.Children[1].Children[1].GetNumber() - 1)); - } - //3sin(x) - 0 - else if (node.Children[0].IsNumber(0)) - { - //Root case - Assign(node, node.Children[1]); - Write("\tSubtraction by zero."); - } - //0 - 3sin(x) - else if (node.Children[1].IsNumber(0)) - { - RPN.Node multiply = new RPN.Node(new[] {new RPN.Node(-1), node.Children[0]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - - Write($"\tSubtraction by zero. Case 2."); - Assign(node, multiply); - } - else if (node[0].IsDivision() && node[1].IsDivision() && node[0, 0].Matches(node[1, 0])) - { - Write("\tf(x)/g(x) - h(x)/g(x) -> [f(x) - h(x)]/g(x)"); - RPN.Node subtraction = new RPN.Node(new[] {node[0, 1], node[1, 1]}, - new RPN.Token("-", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {node[0, 0], subtraction}, - new RPN.Token("/", 2, RPN.Type.Operator)); - Assign(node, division); - } - else if (node[0].IsMultiplication() && node[0, 1].IsNumber(-1)) - { - //(cos(x)^2)-(-1*(sin(x)^2)) - Write("\tf(x) - (-1 * g(x)) -> f(x) + g(x)"); - node[0,1].Replace(1); - node.Replace(new RPN.Token("+", 2, RPN.Type.Operator)); - } - //TODO: ((-2*(cos(x)^2))+(2*(sin(x)^2))) - else if (node[0].IsMultiplication() && node[0, 1].IsLessThanNumber(0)) - { - //f(x) - (-c * g(x)) -> f(x) + c *g(x) - } - else if (node[0].IsNumber() && node[0].IsLessThanNumber(0)) - { - Write("\tf(x) - (-c) -> f(x) + c"); - RPN.Node multiplication = new RPN.Node(new [] { Clone(node[0]), new RPN.Node(-1) }, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node addition = new RPN.Node(new [] {multiplication, node[1]}, new RPN.Token("+", 2, RPN.Type.Operator)); - Assign(node, addition); - Simplify(multiplication, SimplificationMode.Multiplication); - } - - //TODO: f(x)/g(x) - i(x)/j(x) -> [f(x)j(x)]/g(x)j(x) - i(x)g(x)/g(x)j(x) -> [f(x)j(x) - g(x)i(x)]/[g(x)j(x)] - } - else if (mode == SimplificationMode.Addition && node.IsAddition()) - { - //Is root and leafs have the same hash - if (node.ChildrenAreIdentical()) - { - RPN.Node multiply = new RPN.Node(new[] {node.Children[0], new RPN.Node(2)}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiply); - Write("\tSimplification: Addition -> Multiplication"); - } - //Both nodes are multiplications with - //the parent node being addition - //Case: 2sin(x) + 3sin(x) - else if (node.Children[0].IsMultiplication() && node.Children[1].IsMultiplication()) - { - if (node.Children[0].Children[1].IsNumber() && node.Children[1].Children[1].IsNumber() && - node.Children[0].Children[0].Matches(node.Children[1].Children[0])) - { - Write("\tSimplification: Addition"); - double sum = (node.Children[0].Children[1].GetNumber() + - node.Children[1].Children[1].GetNumber()); - node.Children[1].Replace(node.Children[1].Children[1], new RPN.Node(sum)); - node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(0)); - } - } - //Zero addition - else if (node.Children[0].IsNumber(0)) - { - Write("\tZero Addition."); - Assign(node, node.Children[1]); - } - //Case: 0 + sin(x) - else if (node.Children[1].IsNumber(0)) - { - //Child 1 is the expression in this case. - Write("\tZero Addition. Case 2."); - Assign(node, node.Children[0]); - } - //7sin(x) + sin(x) - //C0: Anything - //C1:C0: Compare hash to C0. - else if (node.Children[1].IsMultiplication() && node.Children[1].Children[1].IsNumber() && - node.Children[1].Children[0].Matches(node.Children[0])) - { - Write("\tSimplification Addition Dual Node."); - node.Children[0].Remove(new RPN.Node(0)); - node.Children[1].Replace(node.Children[1].Children[1], - new RPN.Node(node.Children[1].Children[1].GetNumber() + 1)); - } - else if (node.Children[0].IsMultiplication() && node.Children[0].Children[1].IsLessThanNumber(0)) - { - Write("\tAddition can be converted to subtraction"); - node.Replace("-"); - node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(1)); - } - else if (node[0].IsLessThanNumber(0) && node[1].IsMultiplication()) - { - Write("\tAddition can be converted to subtraction"); - node.Replace("-"); - node.Replace(node[0], new RPN.Node(Math.Abs(node[0].GetNumber()))); - } - else if (node.Children[0].IsSubtraction() && node[1].Matches(node[0, 1])) - { - Write("\tf(x) + f(x) - g(x) -> 2 * f(x) - g(x)"); - - node[0].Replace(node[0, 1], new RPN.Node(0)); - RPN.Node multiplication = new RPN.Node(new[] {node[1], new RPN.Node(2)}, - new RPN.Token("*", 2, RPN.Type.Operator)); - node.Replace(node[1], multiplication); - } - else if (node[1].IsMultiplication() && node[1, 1].IsNumber(-1)) - { - Write("\t-f(x) + g(x) -> g(x) - f(x)"); - node[1, 1].Replace(1); - node.Swap(0, 1); - node.Replace(new RPN.Token("-", 2, RPN.Type.Operator)); - } - - //TODO: -c * f(x) + g(x) -> g(x) - c * f(x) - - //TODO f(x)/g(x) + h(x)/g(x) -> [f(x) + h(x)]/g(x) - //TODO: f(x)/g(x) + i(x)/j(x) -> [f(x)j(x)]/g(x)j(x) + i(x)g(x)/g(x)j(x) -> [f(x)j(x) + g(x)i(x)]/[g(x)j(x)] - } - else if (mode == SimplificationMode.Trig) - { - - if (node.IsAddition() && - node.Children[0].IsExponent() && - node.Children[1].IsExponent() && - node.Children[0].Children[0].IsNumber(2) && - node.Children[1].Children[0].IsNumber(2) && - (node.Children[0].Children[1].IsFunction("cos") || - node.Children[0].Children[1].IsFunction("sin")) && - (node.Children[1].Children[1].IsFunction("sin") || - node.Children[1].Children[1].IsFunction("cos")) && - !node.ChildrenAreIdentical() && - !node.containsDomainViolation() && - node.Children[0].Children[1].Children[0].Matches(node.Children[1].Children[1].Children[0]) - ) - { - RPN.Node head = new RPN.Node(1); - Write("\tsin²(x) + cos²(x) -> 1"); - Assign(node, head); - } - else if (node.IsDivision() && node.Children[0].IsFunction("sin") && - node.Children[1].IsFunction("cos") && - node.Children[0].Children[0].Matches(node.Children[1].Children[0])) - { - Write("\tcos(x)/sin(x) -> cot(x)"); - RPN.Node cot = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("cot", 1, RPN.Type.Function)); - Assign(node, cot); - } - else if (node.IsDivision() && node.Children[0].IsFunction("cos") && - node.Children[1].IsFunction("sin") && - node.Children[0].Children[0].Matches(node.Children[1].Children[0])) - { - Write("\tsin(x)/cos(x) -> tan(x)"); - RPN.Node tan = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("tan", 1, RPN.Type.Function)); - Assign(node, tan); - } - else if (node.IsDivision() && node.Children[1].IsMultiplication() && - node.Children[0].IsFunction("sin") && node.Children[1].Children[0].IsFunction("cos") && - node.Children[0].Children[0].Matches(node.Children[1].Children[0].Children[0])) - { - Write("\t[f(x) * cos(x)]/sin(x) -> f(x) * cot(x)"); - RPN.Node cot = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("cot", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {cot, node.Children[1].Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsDivision() && node.Children[0].IsFunction("sec")) - { - Write("\tf(x)/sec(g(x)) -> f(x)cos(g(x))"); - RPN.Node cos = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("cos", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {cos, node.Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsDivision() && node.Children[0].IsFunction("csc")) - { - Write("\tf(x)/csc(g(x)) -> f(x)sin(g(x))"); - RPN.Node sin = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("sin", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {sin, node.Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsDivision() && node.Children[0].IsFunction("cot")) - { - Write("\tf(x)/cot(g(x)) -> f(x)tan(g(x))"); - RPN.Node tan = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("tan", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {tan, node.Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsDivision() && node.Children[0].IsFunction("cos")) - { - Write("\tf(x)/cos(g(x)) -> f(x)sec(g(x))"); - RPN.Node sec = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("sec", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {sec, node.Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsDivision() && node.Children[0].IsFunction("sin")) - { - Write("\tf(x)/sin(g(x)) -> f(x)csc(g(x))"); - RPN.Node csc = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("csc", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {csc, node.Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsDivision() && node.Children[0].IsFunction("tan")) - { - Write("\tf(x)/tan(g(x)) -> f(x)cot(g(x))"); - RPN.Node cot = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("cot", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {cot, node.Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsFunction("cos") && node.Children[0].IsMultiplication() && - node.Children[0].Children[1].IsNumber(-1)) - { - Write("\tcos(-f(x)) -> cos(f(x))"); - node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(1)); - } - else if (node.IsFunction("sec") && node.Children[0].IsMultiplication() && - node.Children[0].Children[1].IsNumber(-1)) - { - Write("\tsec(-f(x)) -> sec(f(x))"); - node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(1)); - } - else if (node.IsFunction("sin") && node.Children[0].IsMultiplication() && - node.Children[0].Children[1].IsNumber(-1)) - { - Write("\tsin(-f(x)) -> -1 * sin(f(x))"); - RPN.Node sin = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("sin", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {sin, node.Children[0].Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsFunction("tan") && node.Children[0].IsMultiplication() && - node.Children[0].Children[1].IsNumber(-1)) - { - Write("\ttan(-f(x)) -> -1 * tan(f(x))"); - RPN.Node tan = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("tan", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {tan, node.Children[0].Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsFunction("csc") && node.Children[0].IsMultiplication() && - node.Children[0].Children[1].IsNumber(-1)) - { - Write("\tcsc(-f(x)) -> -1 * csc(f(x))"); - RPN.Node csc = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("csc", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {csc, node.Children[0].Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsFunction("cot") && node.Children[0].IsMultiplication() && - node.Children[0].Children[1].IsNumber(-1)) - { - Write("\tcot(-f(x)) -> -1 * cot(f(x))"); - RPN.Node cot = new RPN.Node(new[] {node.Children[0].Children[0]}, - new RPN.Token("cot", 1, RPN.Type.Function)); - RPN.Node multiplication = new RPN.Node(new[] {cot, node.Children[0].Children[1]}, - new RPN.Token("*", 2, RPN.Type.Operator)); - Assign(node, multiplication); - } - else if (node.IsSubtraction() && node[0].IsExponent() && node[1].IsNumber(1) && node[0, 0].IsNumber(2) && node[0, 1].IsFunction("sin")) - { - Write("\t1 - sin(x)^2 -> cos(x)^2"); - RPN.Node cos = new RPN.Node(new[] { node[0,1,0] }, new RPN.Token("cos",1, RPN.Type.Function)); - RPN.Node exponent = new RPN.Node(new [] {node[0,0], cos}, new RPN.Token("^",2, RPN.Type.Operator)); - Assign(node, exponent); - } - else if (node.IsSubtraction() && node[0].IsExponent() && node[1].IsNumber(1) && node[0, 0].IsNumber(2) && node[0, 1].IsFunction("cos")) - { - Write("\t1 - cos(x)^2 -> sin(x)^2"); - RPN.Node sin = new RPN.Node(new[] { node[0, 1, 0] }, new RPN.Token("sin", 1, RPN.Type.Function)); - RPN.Node exponent = new RPN.Node(new[] { node[0, 0], sin }, new RPN.Token("^", 2, RPN.Type.Operator)); - Assign(node, exponent); - } - else if (node.IsDivision() && node[0].IsMultiplication() && node[1].IsFunction("cos") && node[0, 0].IsFunction("sin") && node[0, 0, 0].Matches(node[1, 0])) - { - Write("\tcos(x)/(f(x) * sin(x)) -> cot(x)/f(x)"); - //cos(x)/[sin(x) * f(x)] -> cot(x)/f(x) is also implemented due to swapping rules. - - RPN.Node cot = new RPN.Node(new [] {node[1,0]}, new RPN.Token("cot", 1, RPN.Type.Function)); - RPN.Node division = new RPN.Node(new [] {node[0,1], cot}, new RPN.Token("/", 2, RPN.Type.Operator)); - Assign(node, division); - } - //TODO: - //[f(x) * cos(x)]/[g(x) * sin(x)] -> [f(x) * cot(x)]/g(x) - - //[f(x) * sin(x)]/cos(x) -> f(x) * tan(x) - //sin(x)/[f(x) * cos(x)] -> tan(x)/f(x) - //[f(x) * sin(x)]/[g(x) * cos(x)] -> [f(x) * tan(x)]/g(x) - - //TODO: [1 + tan(f(x))^2] -> sec(f(x))^2 - //TODO: [cot(f(x))^2 + 1] -> csc(f(x))^2 - - //These will probably violate domain constraints ? - //TODO: sec(x)^2 - tan(x)^2 = 1 - //TODO: cot(x)^2 + 1 = csc(x)^2 - //TODO: csc(x)^2 - cot(x)^2 = 1 - - //TODO: Double Angle - //[cos(x)^2 - sin(x)^2] = cos(2x) - //1 - 2sin(x)^2 = cos(2x) - //2cos(x)^2 - 1 = cos(2x) - //2sin(x)cos(x) = sin(2x) - //[2tan(x)]/1 - tan(x)^2] = tan(2x) - - //TODO: Power Reducing - //[1 - cos(2x)]/2 = sin(x)^2 - //[1 + cos(2x)]/2 = cos(x)^2 - //[1 - cos(2x)]/[1 + cos(2x)] = tan(x)^2 - - - } - else if (mode == SimplificationMode.Multiplication && node.IsMultiplication()) - { - //TODO: If one of the leafs is a division and the other a number or variable - if (node.ChildrenAreIdentical()) - { - RPN.Node head = new RPN.Node(new[] {new RPN.Node(2), node.Children[0]}, - new RPN.Token("^", 2, RPN.Type.Operator)); - Assign(node, head); - Write("\tSimplification: Multiplication -> Exponent"); - } - else if (node.Children[0].IsNumber(1) || node.Children[1].IsNumber(1)) - { - RPN.Node temp = node.Children[1].IsNumber(1) ? node.Children[0] : node.Children[1]; - Assign(node, temp); - Write($"\tMultiplication by one simplification."); - } - //TODO: Replace the requirement that we cannot do a simplification when a division is present to - //that we cannot do a simplification when a division has a variable in the denominator! - else if ((node.Children[1].IsNumber(0) || node.Children[0].IsNumber(0)) && !node.containsDomainViolation()) - { - Write($"\tMultiplication by zero simplification."); - Assign(node, new RPN.Node(0)); - } - //sin(x)sin(x)sin(x) -> sin(x)^3 - else if (node.Children[1].IsExponent() && node.Children[1].Children[0].IsNumber() && - node.Children[0].Matches(node.Children[1].Children[1])) - { - Write("\tIncrease Exponent"); - node.Replace(node.Children[0], new RPN.Node(1)); - node.Replace(node.Children[1].Children[0], - new RPN.Node(node.Children[1].Children[0].GetNumber() + 1)); - } - else if (node.Children[0].IsExponent() && node.Children[1].IsMultiplication() && - node.Children[0].Children[0].IsGreaterThanNumber(0) && node.Children[1].Children[0] - .Matches(node.Children[0].Children[1])) - { - Write($"\tIncrease Exponent 2:"); - RPN.Node temp = node.Children[0].Children[0]; - temp.Replace(temp.GetNumber() + 1); - node.Children[1].Children[0].Remove(new RPN.Node(1)); - } - else if (node.Children[0].IsExponent() && node.Children[1].IsMultiplication() && - node.Children[0].Children[1].Matches(node.Children[1])) - { - Write("\tIncrease Exponent 3"); - RPN.Node temp = node.Children[0].Children[0]; - temp.Replace(temp.GetNumber() + 1); - node.Children[1].Remove(new RPN.Node(1)); - } - else if (node.Children[1].IsNumber() && node.Children[0].IsMultiplication() && - node.Children[0].Children[1].IsNumber() && !node.Children[0].Children[0].IsNumber()) - { - Write($"\tDual Node Multiplication."); - double num1 = double.Parse(node.Children[0].Children[1].Token.Value); - double num2 = double.Parse(node.Children[1].Token.Value); + { + node = stack.Pop(); - node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(1)); - node.Replace(node.Children[1], new RPN.Node(num1 * num2)); - } - else if ((node.Children[0].IsDivision() || node.Children[1].IsDivision()) && - !(node.Children[0].IsDivision() && node.Children[1].IsDivision())) + //If Root is a number abort. + if (Root.IsNumber()) + { + return; + } + + if (node.IsNumber() || node.IsConstant()) + { + continue; + } + + //This is the rule manager execution code + if (ruleManager.CanRunSet(mode, node)) + { + RPN.Node assignment = ruleManager.Execute(mode, node); + if (assignment != null) { - Write($"\tExpression times a division -> Division "); - RPN.Node division; - RPN.Node expression; - if (node.Children[0].IsDivision()) + if (assignment.IsNumber() && assignment.Token.Value == "NaN") { - division = node.Children[0]; - expression = node.Children[1]; + SetRoot(assignment); } else { - division = node.Children[1]; - expression = node.Children[0]; + Assign(node, assignment); } - - RPN.Node numerator = division.Children[1]; - RPN.Node multiply = new RPN.Node(new[] {Clone(numerator), Clone(expression)}, - new RPN.Token("*", 2, RPN.Type.Operator)); - numerator.Remove(multiply); - expression.Remove(new RPN.Node(1)); - } - else if (node.Children[0].IsDivision() && node.Children[1].IsDivision()) - { - Write($"\tDivision times a division -> Division"); - RPN.Node[] numerator = {node.Children[0].Children[1], node.Children[1].Children[1]}; - RPN.Node[] denominator = {node.Children[0].Children[0], node.Children[1].Children[0]}; - RPN.Token multiply = new RPN.Token("*", 2, RPN.Type.Operator); - - RPN.Node top = new RPN.Node(numerator, multiply); - RPN.Node bottom = new RPN.Node(denominator, multiply); - RPN.Node division = new RPN.Node(new[] {bottom, top}, new RPN.Token("/", 2, RPN.Type.Operator)); - - node.Children[0].Remove(division); - node.Children[1].Remove(new RPN.Node(1)); - } - else if (node.Children[0].IsLessThanNumber(0) && node.Children[1].IsLessThanNumber(0)) - { - Write("\tA negative times a negative is always positive."); - node.Replace(node.Children[0], - new RPN.Node(Math.Abs(double.Parse(node.Children[0].Token.Value)))); - node.Replace(node.Children[1], - new RPN.Node(Math.Abs(double.Parse(node.Children[1].Token.Value)))); - } - else if (node[0].IsMultiplication() && node[0, 1].IsLessThanNumber(0) && - node[1].IsLessThanNumber(0)) - { - Write("\tComplex: A negative times a negative is always positive."); - node.Replace(node[0, 1], new RPN.Node(Math.Abs(node[0, 1].GetNumber()))); - node.Replace(node[1], new RPN.Node(Math.Abs(node[1].GetNumber()))); - } - else if (node[0].IsNumber(-1) && node[1].IsNumber()) - { - Write("\t-1 * c -> -c"); - node.Replace(node[0], new RPN.Node(1)); - node.Replace(node[1], new RPN.Node(node[1].GetNumber() * -1)); - } - else if (node[0].IsNumber() && node[1].IsNumber(-1)) - { - Write("\tc * -1 -> -c"); - node.Replace(node[1], new RPN.Node(1)); - node.Replace(node[0], new RPN.Node(node[0].GetNumber() * -1)); - } - else if (node[0].IsSubtraction() && node[1].IsNumber(-1)) - { - Write("\t-1[f(x) - g(x)] -> -f(x) + g(x) -> g(x) - f(x)"); - node[0].Swap(0, 1); - node[1].Replace(1); } } - else if (mode == SimplificationMode.Swap) + + if (mode == SimplificationMode.Swap) { //We can do complex swapping in here if (node.IsMultiplication() && node.Children[0].IsMultiplication() && @@ -964,81 +691,14 @@ private void Simplify(RPN.Node node, SimplificationMode mode) Write($"\tComplex Swap: Tri Node Multiplication Swap"); RPN.Node multiply = new RPN.Node( - new[] {Clone(node.Children[0].Children[1]), Clone(node.Children[1].Children[1])}, + new[] { Clone(node.Children[0].Children[1]), Clone(node.Children[1].Children[1]) }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Children[1].Children[1].Remove(multiply); node.Children[0].Children[1].Remove(new RPN.Node(1)); } } } - else if (mode == SimplificationMode.Exponent && node.IsExponent()) - { - RPN.Node baseNode = node.Children[1]; - RPN.Node power = node.Children[0]; - if (power.IsNumber(1)) - { - Write("\tf(x)^1 -> f(x)"); - Assign(node, baseNode); - power.Delete(); - node.Delete(); - } - else if (power.IsNumber(0)) - { - Write("\tf(x)^0 -> 1"); - node.Replace(1); - node.Children.Clear(); - } - else if (baseNode.IsNumber(1)) - { - Write("\t1^(fx) -> 1"); - node.Replace(1); - node.Children.Clear(); - } - else if (power.IsLessThanNumber(0)) - { - RPN.Node powerClone = new RPN.Node(new[] {new RPN.Node(-1), Clone(power)}, - new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node exponent = new RPN.Node(new[] {powerClone, Clone(baseNode)}, - new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {exponent, new RPN.Node(1)}, - new RPN.Token("/", 2, RPN.Type.Operator)); - Assign(power.Parent, division); - Write($"\tf(x)^-c -> 1/f(x)^c"); - } - else if (power.IsNumber(0.5)) - { - RPN.Node sqrt = new RPN.Node(new[] {Clone(baseNode)}, - new RPN.Token("sqrt", 1, RPN.Type.Function)); - Assign(power.Parent, sqrt); - Write("\tf(x)^0.5 -> sqrt( f(x) )"); - } - else if ((power.IsNumber() || power.IsConstant()) && baseNode.IsExponent() && - (baseNode.Children[0].IsNumber() || baseNode.Children[0].IsConstant())) - { - Write("\t(f(x)^c)^a -> f(x)^[c * a]"); - RPN.Node multiply; - - if (power.IsNumber() && baseNode.Children[0].IsNumber()) - { - multiply = new RPN.Node(power.GetNumber() * baseNode.Children[0].GetNumber()); - } - else - { - multiply = new RPN.Node(new[] {Clone(power), Clone(baseNode.Children[0])}, - new RPN.Token("*", 2, RPN.Type.Operator)); - } - - RPN.Node func = Clone(baseNode.Children[1]); - RPN.Node exponent = new RPN.Node(new[] {multiply, func}, - new RPN.Token("^", 2, RPN.Type.Operator)); - Assign(power.Parent, exponent); - } - else if (power.IsNumberOrConstant() && baseNode.IsLessThanNumber(0) && power.GetNumber() % 2 == 0) - { - Write("c_1^c_2 where c_2 % 2 = 0 and c_1 < 0 -> [-1 * c_1]^c_2"); - node.Replace(baseNode, new RPN.Node(-1 * baseNode.GetNumber())); - } - } + //Typically does not get invoked... else if (mode == SimplificationMode.Constants) { if (node.IsMultiplication()) @@ -1049,18 +709,43 @@ private void Simplify(RPN.Node node, SimplificationMode mode) } else if (node[0].IsNumber() && node[1].IsNumber()) { - if ((int) (node[0].GetNumber() * node[1].GetNumber()) == + if ((int)(node[0].GetNumber() * node[1].GetNumber()) == node[0].GetNumber() * node[1].GetNumber()) { Solve(node); } } - + } else if (node.IsExponent() && node[0].IsInteger() && node[1].IsInteger()) { Solve(node); } + else if (node.IsOperator("!")) + { + if (node[0].IsNumber(0) || node[0].IsNumber(1)) + { + Solve(node); + } + } + } + else if (mode == SimplificationMode.Misc) + { + if (node.IsOperator() && node.Children.Any(t => t.IsGreaterThanNumber(-1) && t.IsLessThanNumber(1) && !t.IsNumber(0))) + { + Write("\tDecimal to Fraction"); + for (int i = 0; i < node.Children.Count; i++) + { + if (node[i].IsGreaterThanNumber(-1) && node[i].IsLessThanNumber(1)) + { + var f = Extensions.getDecimalFormatToNode(node[i].GetNumber()); + if (f != null) + { + node.Replace(node[i], f); + } + } + } + } } //Propagate down the tree IF there is a root @@ -1070,10 +755,9 @@ private void Simplify(RPN.Node node, SimplificationMode mode) return; } - + SW.Stop(); _data.AddTimeRecord("AST.Simplify:Compute", SW); - SW.Restart(); //Propagate down the tree for (int i = (node.Children.Count - 1); i >= 0; i--) @@ -1107,31 +791,7 @@ private void Swap(RPN.Node node) //Addition operator if (node.IsAddition()) { - if (node[1].IsNumberOrConstant() && !node[0].IsNumberOrConstant()) - { - Write("\tNode Swap: Constants and numbers always yield."); - node.Swap(0, 1); - } - else if (node[1].IsVariable() && node[0].IsMultiplication() && !node[0].IsSolveable()) - { - Write($"\tNode Swap: Single variables yields to generic expression"); - node.Swap(0, 1); - } - else if (node[1].IsVariable() && node[0].IsExponent()) - { - Write("\tNode Swap: Single variables yields to exponent"); - node.Swap(0, 1); - } - else if (node[1].IsMultiplication() && node[0].IsMultiplication() && - !node.Children[1].Children.Any(n => n.IsExponent()) && - node.Children[0].Children.Any(n => n.IsExponent())) - { - Write("\tNode Swap:Straight multiplication gives way to multiplication with an exponent"); - node.Swap(0, 1); - } - - //TODO: A straight exponent should give way to a multiplication with an exponent if... - //TODO: Swapping exponent with non exponent + node.Children.Sort(); } //Multiplication operator else if (node.IsMultiplication()) @@ -1195,98 +855,27 @@ private void InternalSwap(RPN.Node node) unvisited.Enqueue(node.Children[i]); } - if (node.IsFunction("internal_sum") || node.IsFunction("sum")) + if (node.IsFunction("internal_sum") || node.IsFunction("total")) { - /* - 1) A constant or number should always be swapped with any other expression if it comes before another expression if - that expression is not a constant or number. - 2) An expression that has a multiplication or exponent can only be swapped if it has a higher exponent or coefficient - - Swapping should be done til there are no more changes on the tree. - */ - //x + 2 + 2x -> 2x + x + 2 - //x + 2 + x^2 -> x^2 + x + 2 - //5 + x^3 + x + x^2 + 2x + 3x^2 - node.Children.Reverse(); - string hash = string.Empty; - while (node.GetHash() != hash) - { - hash = node.GetHash(); - //Swapping code here - for (int i = 0; i < node.Children.Count; i++) - { - if (i - 1 < 0) - { - continue; - } - - //Constants and numbers should give way. - if ((node.Children[i - 1].IsNumber() || node.Children[i - 1].IsConstant()) && - !(node.Children[i].IsNumber() || node.Children[i].IsConstant())) - { - node.Swap(i - 1, i); - Write($"\tConstants and numbers always yield: Swap {i - 1} and {i}. {node.ToInfix()}"); - } - //Single variables give way to other expressions that are not constants and numbers - else if (node.Children[i - 1].IsVariable() && - (node.Children[i].IsMultiplication() || - node.Children[i].IsFunction("internal_product")) && - !node.Children[i].IsSolveable()) - { - node.Swap(i - 1, i); - Write( - $"\tSingle variables yields to generic expression: Swap {i - 1} and {i}. {node.ToInfix()}"); - } - //Single variable gives way to exponent - else if (node.Children[i - 1].IsVariable() && node.Children[i].IsExponent()) - { - node.Children.Swap(i - 1, i); - Write($"\tSingle variables yields to exponent: Swap {i - 1} and {i}. {node.ToInfix()}"); - } - //Straight multiplication gives way to multiplication with an exponent - else if ((node.Children[i - 1].IsMultiplication() || - node.Children[i].IsFunction("internal_product")) && - !node.Children[i - 1].Children.Any(n => n.IsExponent()) && - (node.Children[i].IsMultiplication() || - node.Children[i].IsFunction("internal_product")) && - node.Children[i].Children.Any(n => n.IsExponent())) - { - node.Children.Swap(i - 1, i); - Write( - $"\tStraight multiplication gives way to multiplication with an exponent: Swap {i - 1} and {i}. {node.ToInfix()}"); - } - //A straight exponent should give way to a multiplication with an exponent if... - else if (node.Children[i - 1].IsExponent() && - (node.Children[i].IsMultiplication() || - node.Children[i].IsFunction("internal_product")) && - node.Children[i].Children[0].IsExponent()) - { - //its degree is higher or equal - if (node[i - 1, 0].IsNumberOrConstant() && node[i, 0, 0].IsNumberOrConstant() && - node[i, 0, 0].IsGreaterThanOrEqualToNumber(node[i - 1, 0].GetNumber())) - { - node.Children.Swap(i - 1, i); - Write( - $"\tA straight exponent should give way to a multiplication with an exponent if its degree is higher or equal : Swap {i - 1} and {i}. {node.ToInfix()}"); - } + node.Children.Sort(); + InternalSimplification(node, new RPN.Token("*", 2, RPN.Type.Operator), new RPN.Node(0)); - //TODO: its degree is an expression and the straight exponent's is not an expression - } - else if ((node.Children[i].IsMultiplication() || - node.Children[i].IsFunction("internal_product")) && - node.Children[i].Children[1].IsExponent() && - !node.Children[i].Children[0].IsExponent()) - { - node.Children[i].Children.Swap(0, 1); - Write("\tSwapping exponent with nonexponent"); - } - } - } - //Write(node.Print()); //TODO + Write($"After Auto Sort: {node.ToInfix(_data)}"); } else if (node.IsFunction("internal_product") || node.IsFunction("product")) { node.Children.Reverse(); + + //Simplification + InternalSimplification(node, new RPN.Token("^",2, RPN.Type.Operator), new RPN.Node(1) ); + //Sort order for multiplication + //1) Numbers or Constants + //2) Exponents of constants + //3) Exponents of variables + //4) Variables + //5) Functions (sorted alphabetically) + //6) Expressions (Everything else) + string hash = string.Empty; while (node.GetHash() != hash) { @@ -1304,16 +893,6 @@ Swapping should be done til there are no more changes on the tree. node.Children.Swap(i - 1, i); Write("\tNumbers and constants take way."); } - else if (node[i - 1].Matches(node.Children[i])) - { - - Write("\tIP: f(x) * f(x) -> f(x) ^ 2"); - - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), node[i]}, - new RPN.Token("^", 2, RPN.Type.Operator)); - node.Replace(node[i], exponent); - node.Replace(node[i - 1], new RPN.Node(1)); - } //Sort functions alphabetically else if (node[i - 1].IsFunction() && node[i].IsFunction() && !node[i - 1].IsConstant() && !node[i].IsConstant()) @@ -1339,15 +918,66 @@ Swapping should be done til there are no more changes on the tree. Write("\tVariable yields to Exponent"); node.Children.Swap(i - 1, i); } - - //TODO: Exponents and other expressions right of way } } + + + Write(node.Print()); } } } + void InternalSimplification(RPN.Node node, RPN.Token Operator, RPN.Node replacement) + { + Dictionary> hashDictionary = new Dictionary>(); + hashDictionary.Clear(); + string hash = string.Empty; + //This tracks everything + for (int i = 0; i < node.Children.Count; i++) + { + hash = node.Children[i].GetHash(); + if (!hashDictionary.ContainsKey(hash)) + { + List temp = new List(); + temp.Add(node.Children[i]); + hashDictionary.Add(hash, temp); + } + else + { + hashDictionary[hash].Add(node.Children[i]); + } + } + + //This simplifies everything + foreach (var kv in hashDictionary) + { + if (kv.Value.Count > 1) + { + Write("\t" + kv.Key + " with a count of " + kv.Value.Count + " and infix of " + kv.Value[0].ToInfix(_data)); + + RPN.Node exponent; + if (Operator.IsExponent()) + { + exponent = new RPN.Node(new[] {new RPN.Node(kv.Value.Count), kv.Value[0]}, Operator); + } + else + { + exponent = new RPN.Node(new[] { kv.Value[0], new RPN.Node(kv.Value.Count) }, Operator); + } + + foreach (var nv in kv.Value) + { + Write($"\t\t Replacing {nv.ID} with {replacement.ToInfix(_data)}"); + node.Replace(nv, replacement.Clone()); + } + + node.AddChild(exponent); + } + } + } + + /// /// Simplifies or evaluates meta functions that /// cannot be easily represented or understood by PostFix. @@ -1362,10 +992,9 @@ public AST MetaFunctions() //it will not appear to be a function if (!_rpn.Data.Functions.ContainsKey("derive")) { - _rpn.Data.AddFunction("derive", new RPN.Function { Arguments = 1, MaxArguments = 1, MinArguments = 1 }); + _rpn.Data.AddFunction("derive", new Function { Arguments = 1, MaxArguments = 1, MinArguments = 1 }); } - - bool go = MetaFunctions(Root); + MetaFunctions(Root); if (_rpn.Data.Functions.ContainsKey("derive")) { @@ -1376,11 +1005,8 @@ public AST MetaFunctions() _data.AddTimeRecord("AST MetaFunctions", SW); - if (go) - { - Simplify(); - } + Simplify(); return this; } @@ -1388,130 +1014,157 @@ public AST MetaFunctions() private bool MetaFunctions(RPN.Node node) { //Propagate down the tree + bool fooBar = false; for (int i = 0; i < node.Children.Count; i++) { MetaFunctions(node.Children[i]); } - if (node.IsFunction() && _data.MetaFunctions.Contains(node.Token.Value)) + if (!node.IsFunction() || !_data.MetaFunctions.Contains(node.Token.Value)) { - if (node.IsFunction("integrate")) - { - double answer = double.NaN; - if (node.Children.Count == 4) - { - node.Children.Insert(0, new RPN.Node(0.001)); - } - - if (node.Children.Count == 5) - { - answer = MetaCommands.Integrate(_rpn, - node.Children[4], - node.Children[3], - node.Children[2], - node.Children[1], - node.Children[0]); - } + return false; + } - RPN.Node temp = new RPN.Node( answer); - Assign(node, temp); - } - else if (node.IsFunction("table")) + if (node.IsFunction("integrate")) + { + double answer = double.NaN; + if (node.Children.Count == 4) { - string table; - - if (node.Children.Count == 4) - { - node.Children.Insert(0, new RPN.Node(0.001)); - } + node.Children.Insert(0, new RPN.Node(0.001)); + } - table = MetaCommands.Table(_rpn, + if (node.Children.Count == 5) + { + answer = MetaCommands.Integrate(_rpn, node.Children[4], node.Children[3], node.Children[2], node.Children[1], node.Children[0]); - - stdout(table); - SetRoot(new RPN.Node( double.NaN)); } - else if (node.IsFunction("derivative")) - { - if (node.Children.Count == 2) - { - GenerateDerivativeAndReplace(node.Children[1]); - Derive(node.Children[0]); - Assign(node, node.Children[1]); - node.Delete(); - } - else if (node.Children.Count == 3) - { - if (!node[0].IsNumberOrConstant() && (int)node[0].GetNumber() == node[0].GetNumber()) - { - throw new Exception("Expected a number or constant"); - } - //This code is suspect! - int count = (int)node[0].GetNumber(); + RPN.Node temp = new RPN.Node(answer); + Assign(node, temp); + } + else if (node.IsFunction("table")) + { + string table; - node.RemoveChild(node[0]); + if (node.Children.Count == 4) + { + node.Children.Insert(0, new RPN.Node(0.001)); + } - - for (int i = 0; i < count; i++) - { - GenerateDerivativeAndReplace(node.Children[1]); - Derive(node.Children[0]); - Simplify(node); - } - Assign(node, node.Children[1]); - node.Delete(); - + table = MetaCommands.Table(_rpn, + node.Children[4], + node.Children[3], + node.Children[2], + node.Children[1], + node.Children[0]); - } + stdout(table); + SetRoot(new RPN.Node(double.NaN)); + } + else if (node.IsFunction("derivative")) + { + if (node.Children.Count == 2) + { + GenerateDerivativeAndReplace(node.Children[1]); + Derive(node.Children[0]); + Assign(node, node.Children[1]); + node.Delete(); } - else if (node.IsFunction("solve")) + else if (node.Children.Count == 3) { - if (node.Children.Count == 2) + if (!node[0].IsNumberOrConstant() && (int)node[0].GetNumber() == node[0].GetNumber()) { - node.AddChild(new RPN.Node(new RPN.Token("=", 2, RPN.Type.Operator))); + throw new Exception("Expected a number or constant"); } - else - { - node.Swap(0, 2); - node[2].Children.Clear(); - } - - Write(node.Print()); - RPN.Node temp = node[2]; - node.RemoveChild(temp); + //This code is suspect! + int count = (int)node[0].GetNumber(); - Algebra(node, ref temp ); + node.RemoveChild(node[0]); - temp.AddChild(new [] {node[0], node[1]}); - //This is done to fix a bug - if (!temp.IsOperator("=")) + for (int i = 0; i < count; i++) { - temp.Swap(0, 1); + GenerateDerivativeAndReplace(node.Children[1]); + Derive(node.Children[0]); + Simplify(node); } + Assign(node, node.Children[1]); + node.Delete(); - node.AddChild(temp); - Write($"{node[1].ToInfix()} {node[2].ToInfix()} {node[0].ToInfix()}"); - node.RemoveChild(temp); - Write(temp.ToInfix()); - Assign(node, temp); } - else if (node.IsFunction("sum")) + } + else if (node.IsFunction("solve")) + { + if (node.Children.Count == 2) + { + node.AddChild(new RPN.Node(new RPN.Token("=", 2, RPN.Type.Operator))); + } + else { + node.Swap(0, 2); + node[2].Children.Clear(); + } + + Write(node.Print()); + + RPN.Node temp = node[2]; + node.RemoveChild(temp); + Algebra(node, ref temp); + + temp.AddChild(new[] { node[0], node[1] }); + + //This is done to fix a bug + if (!temp.IsOperator("=")) + { + temp.Swap(0, 1); } - return true; + node.AddChild(temp); + Write($"{node[1].ToInfix(_data)} {node[2].ToInfix(_data)} {node[0].ToInfix(_data)}"); + node.RemoveChild(temp); + + Write(temp.ToInfix(_data)); + Assign(node, temp); + } + else if (node.IsFunction("sum")) + { + //0 - end + //1 - start + //2 - variable + //3 - expression + Write($"\tSolving the sum! : {node[3].ToInfix(_data)}"); + PostFix math = new PostFix(_rpn); + double start = math.Compute(node[1].ToPostFix().ToArray()); + double end = math.Compute(node[0].ToPostFix().ToArray()); + double DeltaX = end - start; + int max = (int)Math.Ceiling(DeltaX); + + double PrevAnswer = 0; + + math.SetPolish(node[3].ToPostFix().ToArray()); + double sum = 0; + + for (int x = 0; x <= max; x++) + { + double RealX = start + x; + math.SetVariable("ans", PrevAnswer); + math.SetVariable(node[2].Token.Value, RealX); + double answer = math.Compute(); + PrevAnswer = answer; + sum += answer; + math.Reset(); + } + Assign(node, new RPN.Node(sum)); } - return false; + return true; } private void Solve(RPN.Node node) @@ -1525,11 +1178,11 @@ private void Solve(RPN.Node node) bool isSolveable = node.IsSolveable(); //Functions that are not constants and/or meta functions - if ( (node.IsFunction() && ! (node.IsConstant() || _data.MetaFunctions.Contains(node.Token.Value)) || node.IsOperator()) && isSolveable) + if ((node.IsFunction() && !(node.IsConstant() || _data.MetaFunctions.Contains(node.Token.Value)) || node.IsOperator()) && isSolveable) { PostFix math = new PostFix(_rpn); double answer = math.Compute(node.ToPostFix().ToArray()); - Assign(node, new RPN.Node( answer)); + Assign(node, new RPN.Node(answer)); //Since we solved something lower in the tree we may be now able //to solve something higher up in the tree! Solve(node.Parent); @@ -1553,14 +1206,14 @@ private AST Derive(RPN.Node variable) Simplify(Root); //Simplify(Root, SimplificationMode.Constants); - Write($"Starting to derive ROOT: {Root.ToInfix()}"); + Write($"Starting to derive ROOT: {Root.ToInfix(_data)}"); Derive(Root, variable); Write("\tSimplifying Post!\n"); Simplify(Root); //Simplify(Root, SimplificationMode.Constants); Write(""); - + return this; } @@ -1568,28 +1221,25 @@ private void Derive(RPN.Node foo, RPN.Node variable) { //We do not know in advance the depth of a tree //and given a big enough expression the current recursion - //is more likley to fail compared to an itterative approach. + //is more likely to fail compared to an iterative approach. Stack stack = new Stack(); stack.Push(foo); - //Write(foo.ToInfix()); - string v = variable.ToInfix(); + //Write(foo.ToInfix(_data)); + string v = variable.ToInfix(_data); RPN.Node node = null; while (stack.Count > 0) { node = stack.Pop(); - //Write($"Current Node: {node.ToInfix()}"); + //Write($"Current Node: {node.ToInfix(_data)}"); //Propagate down the tree for (int i = (node.Children.Count - 1); i >= 0; i--) { stack.Push(node.Children[i]); - //Write($"Pushing {node.Children[i]} {stack.Count}"); } - if (node.Token.Value != "derive") { - //Write($"Skipping Node: {node.ToInfix()}"); continue; } @@ -1597,8 +1247,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string f_x = node.Children[0].Children[0].ToInfix(); - string g_x = node.Children[0].Children[1].ToInfix(); + string f_x = node.Children[0].Children[0].ToInfix(_data); + string g_x = node.Children[0].Children[1].ToInfix(_data); Write($"\td/d{v}[ {f_x} ± {g_x} ] -> d/d{v}( {f_x} ) ± d/d{v}( {g_x} )"); } else @@ -1620,7 +1270,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - Write($"\td/d{v}[ {node.Children[0].ToInfix()} ] -> 0"); + Write($"\td/d{v}[ {node.Children[0].ToInfix(_data)} ] -> 0"); } else { @@ -1636,7 +1286,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - Write($"\td/d{v}[ {node.Children[0].ToInfix()} ] -> 1"); + Write($"\td/d{v}[ {node.Children[0].ToInfix(_data)} ] -> 1"); } else { @@ -1657,7 +1307,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) if (debug) { Write( - $"\td/d{v}[ {node.Children[0].Children[0].ToInfix()} * {node.Children[0].Children[1].ToInfix()} ] -> 0"); + $"\td/d{v}[ {node.Children[0].Children[0].ToInfix(_data)} * {node.Children[0].Children[1].ToInfix(_data)} ] -> 0"); } else { @@ -1675,7 +1325,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) if (debug) { Write( - $"\td/d{v}[ {node.Children[0].Children[1].ToInfix()} * {node.Children[0].Children[0].ToInfix()}] -> d/d{v}[ {node.Children[0].Children[1].ToInfix()} ] * {node.Children[0].Children[0].ToInfix()}"); + $"\td/d{v}[ {node.Children[0].Children[1].ToInfix(_data)} * {node.Children[0].Children[0].ToInfix(_data)}] -> d/d{v}[ {node.Children[0].Children[1].ToInfix(_data)} ] * {node.Children[0].Children[0].ToInfix(_data)}"); } else { @@ -1693,8 +1343,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string constant = node.Children[0].Children[1].ToInfix(); - string expr = node.Children[0].Children[0].ToInfix(); + string constant = node.Children[0].Children[1].ToInfix(_data); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ {constant} * {expr}] -> {constant} * d/d{v}[ {expr} ]"); } else @@ -1719,8 +1369,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) if (debug) { - string f = fNode.ToInfix(); - string g = gNode.ToInfix(); + string f = fNode.ToInfix(_data); + string g = gNode.ToInfix(_data); Write($"\td/d{v}[ {f} * {g} ] -> {f} * d/d{v}[ {g} ] + d/d{v}[ {f} ] * {g}"); } else @@ -1728,13 +1378,13 @@ private void Derive(RPN.Node foo, RPN.Node variable) Write($"\td/dx[ f(x) * g(x) ] -> f(x) * d/dx[ g(x) ] + d/dx[ f(x) ] * g(x)"); } - RPN.Node fDerivative = new RPN.Node(new[] {Clone(fNode)}, _derive); - RPN.Node gDerivative = new RPN.Node(new[] {Clone(gNode)}, _derive); + RPN.Node fDerivative = new RPN.Node(new[] { Clone(fNode) }, _derive); + RPN.Node gDerivative = new RPN.Node(new[] { Clone(gNode) }, _derive); - RPN.Node multiply1 = new RPN.Node(new[] {gDerivative, fNode}, multiply); - RPN.Node multiply2 = new RPN.Node(new[] {fDerivative, gNode}, multiply); + RPN.Node multiply1 = new RPN.Node(new[] { gDerivative, fNode }, multiply); + RPN.Node multiply2 = new RPN.Node(new[] { fDerivative, gNode }, multiply); - RPN.Node add = new RPN.Node(new[] {multiply1, multiply2}, + RPN.Node add = new RPN.Node(new[] { multiply1, multiply2 }, new RPN.Token("+", 2, RPN.Type.Operator)); //Remove myself from the tree @@ -1745,6 +1395,22 @@ private void Derive(RPN.Node foo, RPN.Node variable) stack.Push(gDerivative); } } + else if (node[0].IsDivision() && node[0, 0].IsNumberOrConstant()) + { + if (debug) + { + string f_x = node[0, 1].ToInfix(_data); + string k = node[0, 0].ToInfix(_data); + Write($"\td/d{v}[ {f_x}/{k} ] -> d/d{v}{f_x}]/{k}"); + } + else + { + Write("\td/dx[ f(x)/k ] -> d/dx[f(x)]/k"); + } + GenerateDerivativeAndReplace(node[0,1]); + stack.Push(node[0, 1]); + node.Remove(); + } else if (node.Children[0].IsDivision()) { //Quotient Rule @@ -1753,22 +1419,22 @@ private void Derive(RPN.Node foo, RPN.Node variable) RPN.Node numerator = node.Children[0].Children[1]; RPN.Node denominator = node.Children[0].Children[0]; - RPN.Node numeratorDerivative = new RPN.Node(new[] {Clone(numerator)}, _derive); - RPN.Node denominatorDerivative = new RPN.Node(new[] {Clone(denominator)}, _derive); + RPN.Node numeratorDerivative = new RPN.Node(new[] { Clone(numerator) }, _derive); + RPN.Node denominatorDerivative = new RPN.Node(new[] { Clone(denominator) }, _derive); - RPN.Node multiplicationOne = new RPN.Node(new[] {numeratorDerivative, denominator}, multiply); - RPN.Node multiplicationTwo = new RPN.Node(new[] {denominatorDerivative, numerator}, multiply); + RPN.Node multiplicationOne = new RPN.Node(new[] { numeratorDerivative, denominator }, multiply); + RPN.Node multiplicationTwo = new RPN.Node(new[] { denominatorDerivative, numerator }, multiply); - RPN.Node subtraction = new RPN.Node(new[] {multiplicationTwo, multiplicationOne}, + RPN.Node subtraction = new RPN.Node(new[] { multiplicationTwo, multiplicationOne }, new RPN.Token("-", 2, RPN.Type.Operator)); - RPN.Node denominatorSquared = new RPN.Node(new[] {new RPN.Node(2), Clone(denominator)}, + RPN.Node denominatorSquared = new RPN.Node(new[] { new RPN.Node(2), Clone(denominator) }, new RPN.Token("^", 2, RPN.Type.Operator)); if (debug) { - string n = numerator.ToInfix(); - string d = denominator.ToInfix(); + string n = numerator.ToInfix(_data); + string d = denominator.ToInfix(_data); Write($"\td/d{v}[ {n} / {d} ] -> [ d/d{v}( {n} ) * {d} - {n} * d/d{v}( {d} ) ]/{d}^2"); } else @@ -1796,8 +1462,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string b = baseNode.ToInfix(); - string p = power.ToInfix(); + string b = baseNode.ToInfix(_data); + string p = power.ToInfix(_data); Write($"\td/d{v}[ {b}^{p} ] -> {p} * {b}^({p} - 1)"); } else @@ -1814,20 +1480,20 @@ private void Derive(RPN.Node foo, RPN.Node variable) RPN.Node one = new RPN.Node(1); //(n - 1) - RPN.Node subtraction = new RPN.Node(new[] {one, powerClone}, + RPN.Node subtraction = new RPN.Node(new[] { one, powerClone }, new RPN.Token("-", 2, RPN.Type.Operator)); //x^(n - 1) - exponent = new RPN.Node(new RPN.Node[] {subtraction, baseNode}, + exponent = new RPN.Node(new RPN.Node[] { subtraction, baseNode }, new RPN.Token("^", 2, RPN.Type.Operator)); } else { - exponent = new RPN.Node(new RPN.Node[] {new RPN.Node(powerClone.GetNumber() - 1), baseNode}, + exponent = new RPN.Node(new RPN.Node[] { new RPN.Node(powerClone.GetNumber() - 1), baseNode }, new RPN.Token("^", 2, RPN.Type.Operator)); } - RPN.Node multiplication = new RPN.Node(new[] {exponent, power}, + RPN.Node multiplication = new RPN.Node(new[] { exponent, power }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node.Children[0], multiplication); @@ -1839,8 +1505,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string b = baseNode.ToInfix(); - string p = power.ToInfix(); + string b = baseNode.ToInfix(_data); + string p = power.ToInfix(_data); Write($"\td/d{v}[ {b}^{p} ] -> {p} * {b}^({p} - 1) * d/d{v}[ {b} ]"); } else @@ -1848,7 +1514,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) Write("\td/dx[ f(x)^n ] -> n * f(x)^(n - 1) * d/dx[ f(x) ]"); } - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(baseNode)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(baseNode) }, _derive); RPN.Node powerClone = Clone(power); @@ -1856,7 +1522,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) if (power.IsConstant()) { RPN.Node one = new RPN.Node(1); - subtraction = new RPN.Node(new[] {one, powerClone}, + subtraction = new RPN.Node(new[] { one, powerClone }, new RPN.Token("-", 2, RPN.Type.Operator)); } else @@ -1865,11 +1531,11 @@ private void Derive(RPN.Node foo, RPN.Node variable) } //Replace n with (n - 1) - RPN.Node exponent = new RPN.Node(new RPN.Node[] {subtraction, baseNode}, + RPN.Node exponent = new RPN.Node(new RPN.Node[] { subtraction, baseNode }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node temp = new RPN.Node(new[] {exponent, power}, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node multiply = new RPN.Node(new[] {bodyDerive, temp}, + RPN.Node temp = new RPN.Node(new[] { exponent, power }, new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node multiply = new RPN.Node(new[] { bodyDerive, temp }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node.Children[0], multiply); @@ -1882,7 +1548,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string p = power.ToInfix(); + string p = power.ToInfix(_data); Write($"\td/d{v}[ e^{p} ] -> d/d{v}[ {p} ] * e^{p}"); } else @@ -1891,8 +1557,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node exponent = baseNode.Parent; - RPN.Node powerDerivative = new RPN.Node(new[] {Clone(power)}, _derive); - RPN.Node multiply = new RPN.Node(new[] {powerDerivative, exponent}, + RPN.Node powerDerivative = new RPN.Node(new[] { Clone(power) }, _derive); + RPN.Node multiply = new RPN.Node(new[] { powerDerivative, exponent }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(power.Parent, multiply); //Delete self from the tree @@ -1903,8 +1569,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string b = baseNode.ToInfix(); - string p = power.ToInfix(); + string b = baseNode.ToInfix(_data); + string p = power.ToInfix(_data); Write($"\td/d{v}[ {b}^{p} ] -> ln({b}) * {b}^{p} * d/d{v}[ {p} ]"); } else @@ -1913,10 +1579,10 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node exponent = baseNode.Parent; - RPN.Node ln = new RPN.Node(new[] {Clone(baseNode)}, new RPN.Token("ln", 1, RPN.Type.Function)); - RPN.Node powerDerivative = new RPN.Node(new[] {Clone(power)}, _derive); - RPN.Node temp = new RPN.Node(new[] {exponent, ln}, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node multiply = new RPN.Node(new[] {temp, powerDerivative}, + RPN.Node ln = new RPN.Node(new[] { Clone(baseNode) }, new RPN.Token("ln", 1, RPN.Type.Function)); + RPN.Node powerDerivative = new RPN.Node(new[] { Clone(power) }, _derive); + RPN.Node temp = new RPN.Node(new[] { exponent, ln }, new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node multiply = new RPN.Node(new[] { temp, powerDerivative }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(power.Parent, multiply); @@ -1928,8 +1594,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string b = baseNode.ToInfix(); - string p = power.ToInfix(); + string b = baseNode.ToInfix(_data); + string p = power.ToInfix(_data); Write($"\td/d{v}[ {b}^{p} ] -> {b}^{p} * d/d{v}[ {b} * ln( {p} ) ]"); } else @@ -1938,11 +1604,11 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node exponent = Clone(baseNode.Parent); - RPN.Node ln = new RPN.Node(new[] {Clone(baseNode)}, new RPN.Token("ln", 1, RPN.Type.Function)); - RPN.Node temp = new RPN.Node(new[] {Clone(power), ln}, + RPN.Node ln = new RPN.Node(new[] { Clone(baseNode) }, new RPN.Token("ln", 1, RPN.Type.Function)); + RPN.Node temp = new RPN.Node(new[] { Clone(power), ln }, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node derive = new RPN.Node(new[] {temp}, _derive); - RPN.Node multiply = new RPN.Node(new[] {exponent, derive}, + RPN.Node derive = new RPN.Node(new[] { temp }, _derive); + RPN.Node multiply = new RPN.Node(new[] { exponent, derive }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(power.Parent, multiply); @@ -1959,7 +1625,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ sin({expr}) ] -> cos({expr}) * d/d{v}[ {expr} ]"); } else @@ -1969,11 +1635,11 @@ private void Derive(RPN.Node foo, RPN.Node variable) RPN.Node body = node.Children[0].Children[0]; - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node cos = new RPN.Node(new[] {body}, new RPN.Token("cos", 1, RPN.Type.Function)); + RPN.Node cos = new RPN.Node(new[] { body }, new RPN.Token("cos", 1, RPN.Type.Function)); - RPN.Node multiply = new RPN.Node(new[] {cos, bodyDerive}, new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node multiply = new RPN.Node(new[] { cos, bodyDerive }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node.Children[0], multiply); //Delete self from the tree @@ -1985,7 +1651,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ cos({expr}) ] -> -sin({expr}) * d/d{v}[ {expr} ]"); } else @@ -1994,12 +1660,12 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = node.Children[0].Children[0]; - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node sin = new RPN.Node(new[] {body}, new RPN.Token("sin", 1, RPN.Type.Function)); - RPN.Node negativeOneMultiply = new RPN.Node(new[] {new RPN.Node(-1), sin}, + RPN.Node sin = new RPN.Node(new[] { body }, new RPN.Token("sin", 1, RPN.Type.Function)); + RPN.Node negativeOneMultiply = new RPN.Node(new[] { new RPN.Node(-1), sin }, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node multiply = new RPN.Node(new[] {negativeOneMultiply, bodyDerive}, + RPN.Node multiply = new RPN.Node(new[] { negativeOneMultiply, bodyDerive }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node.Children[0], multiply); @@ -2012,7 +1678,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ tan({expr}) ] -> sec({expr})^2 * d/d{v}[ {expr} ]"); } else @@ -2021,13 +1687,13 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = node.Children[0].Children[0]; - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node sec = new RPN.Node(new[] {body}, new RPN.Token("sec", 1, RPN.Type.Function)); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), sec}, + RPN.Node sec = new RPN.Node(new[] { body }, new RPN.Token("sec", 1, RPN.Type.Function)); + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), sec }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node multiply = new RPN.Node(new[] {exponent, bodyDerive}, + RPN.Node multiply = new RPN.Node(new[] { exponent, bodyDerive }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node.Children[0], multiply); //Delete self from the tree @@ -2039,7 +1705,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ sec({expr}) ] -> tan({expr}) * sec({expr}) * d/d{v}[ {expr} ]"); } else @@ -2050,12 +1716,12 @@ private void Derive(RPN.Node foo, RPN.Node variable) RPN.Token multiplyToken = new RPN.Token("*", 2, RPN.Type.Operator); RPN.Node body = node.Children[0].Children[0]; - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); RPN.Node sec = node.Children[0]; - RPN.Node tan = new RPN.Node(new[] {Clone(body)}, new RPN.Token("tan", 1, RPN.Type.Function)); - RPN.Node temp = new RPN.Node(new[] {sec, tan}, multiplyToken); - RPN.Node multiply = new RPN.Node(new[] {bodyDerive, temp}, multiplyToken); + RPN.Node tan = new RPN.Node(new[] { Clone(body) }, new RPN.Token("tan", 1, RPN.Type.Function)); + RPN.Node temp = new RPN.Node(new[] { sec, tan }, multiplyToken); + RPN.Node multiply = new RPN.Node(new[] { bodyDerive, temp }, multiplyToken); node.Replace(node.Children[0], multiply); //Delete self from the tree @@ -2067,7 +1733,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ csc({expr}) ] -> - cot({expr}) * csc({expr}) * d/d{v}[ {expr} ] "); } else @@ -2078,14 +1744,14 @@ private void Derive(RPN.Node foo, RPN.Node variable) RPN.Token multiplyToken = new RPN.Token("*", 2, RPN.Type.Operator); RPN.Node body = node.Children[0].Children[0]; - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); RPN.Node csc = node.Children[0]; - RPN.Node cot = new RPN.Node(new[] {Clone(body)}, new RPN.Token("cot", 1, RPN.Type.Function)); + RPN.Node cot = new RPN.Node(new[] { Clone(body) }, new RPN.Token("cot", 1, RPN.Type.Function)); - RPN.Node temp = new RPN.Node(new[] {csc, cot}, multiplyToken); - RPN.Node multiply = new RPN.Node(new[] {temp, bodyDerive}, multiplyToken); + RPN.Node temp = new RPN.Node(new[] { csc, cot }, multiplyToken); + RPN.Node multiply = new RPN.Node(new[] { temp, bodyDerive }, multiplyToken); - node.Replace(node.Children[0], new RPN.Node(new[] {new RPN.Node(-1), multiply}, multiplyToken)); + node.Replace(node.Children[0], new RPN.Node(new[] { new RPN.Node(-1), multiply }, multiplyToken)); //Delete self from the tree node.Remove(); //Chain Rule @@ -2095,7 +1761,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ cot({expr}) ] -> -csc({expr})^2 * d/d{v}[ {expr} ]"); } else @@ -2104,14 +1770,14 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = node.Children[0].Children[0]; - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); - RPN.Node csc = new RPN.Node(new[] {body}, new RPN.Token("csc", 1, RPN.Type.Function)); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), csc}, + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); + RPN.Node csc = new RPN.Node(new[] { body }, new RPN.Token("csc", 1, RPN.Type.Function)); + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), csc }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node temp = new RPN.Node(new[] {new RPN.Node(-1), exponent}, + RPN.Node temp = new RPN.Node(new[] { new RPN.Node(-1), exponent }, new RPN.Token("*", 2, RPN.Type.Operator)); RPN.Node multiply = - new RPN.Node(new[] {bodyDerive, temp}, new RPN.Token("*", 2, RPN.Type.Operator)); + new RPN.Node(new[] { bodyDerive, temp }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node.Children[0], multiply); //Delete self from the tree @@ -2123,7 +1789,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ arcsin({expr}) ] -> d/d{v}[ {expr} ]/sqrt(1 - {expr}^2)"); } else @@ -2132,15 +1798,15 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = Clone(node.Children[0].Children[0]); - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), body}, + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), body }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node subtraction = new RPN.Node(new[] {exponent, new RPN.Node(1)}, + RPN.Node subtraction = new RPN.Node(new[] { exponent, new RPN.Node(1) }, new RPN.Token("-", 2, RPN.Type.Operator)); - RPN.Node sqrt = new RPN.Node(new[] {subtraction}, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node sqrt = new RPN.Node(new[] { subtraction }, new RPN.Token("sqrt", 1, RPN.Type.Function)); RPN.Node division = - new RPN.Node(new[] {sqrt, bodyDerive}, new RPN.Token("/", 2, RPN.Type.Operator)); + new RPN.Node(new[] { sqrt, bodyDerive }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node.Children[0], division); //Delete self from the tree @@ -2152,7 +1818,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ arccos({expr}) ] -> -1 * d/d{v}[ {expr} ]/sqrt(1 - {expr}^2)"); } else @@ -2161,17 +1827,17 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = Clone(node.Children[0].Children[0]); - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), body}, + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), body }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node subtraction = new RPN.Node(new[] {exponent, new RPN.Node(1)}, + RPN.Node subtraction = new RPN.Node(new[] { exponent, new RPN.Node(1) }, new RPN.Token("-", 2, RPN.Type.Operator)); - RPN.Node sqrt = new RPN.Node(new[] {subtraction}, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node sqrt = new RPN.Node(new[] { subtraction }, new RPN.Token("sqrt", 1, RPN.Type.Function)); RPN.Node division = - new RPN.Node(new[] {sqrt, bodyDerive}, new RPN.Token("/", 2, RPN.Type.Operator)); + new RPN.Node(new[] { sqrt, bodyDerive }, new RPN.Token("/", 2, RPN.Type.Operator)); - RPN.Node multiplication = new RPN.Node(new[] {new RPN.Node(-1), division}, + RPN.Node multiplication = new RPN.Node(new[] { new RPN.Node(-1), division }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node.Children[0], multiplication); @@ -2184,7 +1850,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ arctan({expr}) ] -> d/d{v}[ {expr} ]/(1 + {expr}^2)"); } else @@ -2193,13 +1859,13 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = Clone(node.Children[0].Children[0]); - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), body}, + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), body }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node add = new RPN.Node(new[] {new RPN.Node(1), exponent}, + RPN.Node add = new RPN.Node(new[] { new RPN.Node(1), exponent }, new RPN.Token("+", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {add, bodyDerive}, new RPN.Token("/", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new[] { add, bodyDerive }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node.Children[0], division); //Delete self from the tree @@ -2211,7 +1877,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ arccot({expr}) ] -> -1 * d/d{v}[ {expr} ]/(1 + {expr}^2)"); } else @@ -2220,15 +1886,15 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = Clone(node.Children[0].Children[0]); - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), body}, + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), body }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node add = new RPN.Node(new[] {new RPN.Node(1), exponent}, + RPN.Node add = new RPN.Node(new[] { new RPN.Node(1), exponent }, new RPN.Token("+", 2, RPN.Type.Operator)); - RPN.Node multiplication = new RPN.Node(new[] {new RPN.Node(-1), bodyDerive}, + RPN.Node multiplication = new RPN.Node(new[] { new RPN.Node(-1), bodyDerive }, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {add, multiplication}, + RPN.Node division = new RPN.Node(new[] { add, multiplication }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node.Children[0], division); @@ -2241,7 +1907,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ arcsec({expr}) ] -> d/d{v}[ {expr} ]/( {expr} * sqrt({expr}^2 - 1 ) )"); } else @@ -2250,17 +1916,18 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = Clone(node.Children[0].Children[0]); - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), body}, + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), body }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node subtraction = new RPN.Node(new[] {new RPN.Node(1), exponent}, + RPN.Node subtraction = new RPN.Node(new[] { new RPN.Node(1), exponent }, new RPN.Token("-", 2, RPN.Type.Operator)); - RPN.Node sqrt = new RPN.Node(new[] {subtraction}, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node sqrt = new RPN.Node(new[] { subtraction }, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node abs = new RPN.Node(new[] { body.Clone() }, new RPN.Token("abs", 1, RPN.Type.Function)); RPN.Node denominator = - new RPN.Node(new[] {sqrt, Clone(body)}, new RPN.Token("*", 2, RPN.Type.Operator)); + new RPN.Node(new[] { sqrt, abs }, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {denominator, bodyDerive}, + RPN.Node division = new RPN.Node(new[] { denominator, bodyDerive }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node.Children[0], division); @@ -2273,7 +1940,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ arccsc({expr}) ] -> -1 * d/d{v}[ {expr} ]/( {expr} * sqrt({expr}^2 - 1 ) )"); } else @@ -2282,18 +1949,19 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = Clone(node.Children[0].Children[0]); - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), body}, + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), body }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node subtraction = new RPN.Node(new[] {new RPN.Node(1), exponent}, + RPN.Node subtraction = new RPN.Node(new[] { new RPN.Node(1), exponent }, new RPN.Token("-", 2, RPN.Type.Operator)); - RPN.Node sqrt = new RPN.Node(new[] {subtraction}, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node sqrt = new RPN.Node(new[] { subtraction }, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node abs = new RPN.Node(new[] { body.Clone() }, new RPN.Token("abs", 1, RPN.Type.Function)); RPN.Node denominator = - new RPN.Node(new[] {sqrt, Clone(body)}, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node multiplication = new RPN.Node(new[] {new RPN.Node(-1), bodyDerive}, + new RPN.Node(new[] { sqrt, abs }, new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node multiplication = new RPN.Node(new[] { new RPN.Node(-1), bodyDerive }, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {denominator, multiplication}, + RPN.Node division = new RPN.Node(new[] { denominator, multiplication }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node.Children[0], division); @@ -2309,7 +1977,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\tsqrt({expr}) -> {expr}^0.5"); } else @@ -2318,7 +1986,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = node.Children[0].Children[0]; - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(.5), body}, + RPN.Node OneHalf = new RPN.Node(0.5); + RPN.Node exponent = new RPN.Node(new[] { OneHalf, body }, new RPN.Token("^", 2, RPN.Type.Operator)); node.Replace(node.Children[0], exponent); stack.Push(node); @@ -2327,7 +1996,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\td/d{v}[ ln({expr}) ] -> d/d{v}[ {expr} ]/{expr}"); } else @@ -2336,9 +2005,9 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = node.Children[0].Children[0]; - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); RPN.Node division = - new RPN.Node(new[] {body, bodyDerive}, new RPN.Token("/", 2, RPN.Type.Operator)); + new RPN.Node(new[] { body, bodyDerive }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node.Children[0], division); //Delete self from the tree @@ -2355,8 +2024,8 @@ private void Derive(RPN.Node foo, RPN.Node variable) if (debug) { - string b = body.ToInfix(); - string p = power.ToInfix(); + string b = body.ToInfix(_data); + string p = power.ToInfix(_data); Write($"\td/d{v}[ log({b},{p}) ] -> d/d{v}[ {p} ]/({p} * ln({b}))"); } else @@ -2364,10 +2033,10 @@ private void Derive(RPN.Node foo, RPN.Node variable) Write("\td/dx[ log(b,g(x)) ] -> d/dx[ g(x) ]/(g(x) * ln(b))"); } - RPN.Node bodyDerive = new RPN.Node(new[] {Clone(body)}, _derive); - RPN.Node multiply = new RPN.Node(new[] {body, new RPN.Node(new[] {power}, ln)}, + RPN.Node bodyDerive = new RPN.Node(new[] { Clone(body) }, _derive); + RPN.Node multiply = new RPN.Node(new[] { body, new RPN.Node(new[] { power }, ln) }, new RPN.Token("*", 2, RPN.Type.Operator)); - RPN.Node division = new RPN.Node(new[] {multiply, bodyDerive}, + RPN.Node division = new RPN.Node(new[] { multiply, bodyDerive }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node.Children[0], division); @@ -2380,7 +2049,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) { if (debug) { - string expr = node.Children[0].Children[0].ToInfix(); + string expr = node.Children[0].Children[0].ToInfix(_data); Write($"\tabs({expr}) -> sqrt( {expr}^2 )"); } else @@ -2389,16 +2058,16 @@ private void Derive(RPN.Node foo, RPN.Node variable) } RPN.Node body = node.Children[0].Children[0]; - RPN.Node exponent = new RPN.Node(new[] {new RPN.Node(2), body}, + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(2), body }, new RPN.Token("^", 2, RPN.Type.Operator)); - RPN.Node sqrt = new RPN.Node(new[] {exponent}, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node sqrt = new RPN.Node(new[] { exponent }, new RPN.Token("sqrt", 1, RPN.Type.Function)); node.Replace(node.Children[0], sqrt); stack.Push(node); } - else if (node.Children[0].IsFunction("sum")) + else if (node.Children[0].IsFunction("total")) { - Write("\tExploding sum"); + Write("\tExploding total"); explode(node.Children[0]); stack.Push(node); stack.Push(node); @@ -2412,7 +2081,7 @@ private void Derive(RPN.Node foo, RPN.Node variable) else { throw new NotImplementedException( - $"Derivative of {node.Children[0].ToInfix()} not known at this time."); + $"Derivative of {node.Children[0].ToInfix(_data)} not known at this time."); } } } @@ -2436,7 +2105,7 @@ private void Algebra(RPN.Node node, ref RPN.Node equality) { Write("\tSolving for one Side."); Solve(node[1]); - } + } //TODO: Quadratic Formula @@ -2464,7 +2133,7 @@ private void Algebra(RPN.Node node, ref RPN.Node equality) } node[1].Replace(temp, new RPN.Node(0)); - RPN.Node subtraction = new RPN.Node(new [] {temp, node[0]}, new RPN.Token("-", 2, RPN.Type.Operator)); + RPN.Node subtraction = new RPN.Node(new[] { temp, node[0] }, new RPN.Token("-", 2, RPN.Type.Operator)); node.Replace(node[0], subtraction); Simplify(node[1], SimplificationMode.Addition); @@ -2506,12 +2175,12 @@ private void Algebra(RPN.Node node, ref RPN.Node equality) { temp = node[1, 1]; Write("\tcf(x) = g(x) -> f(x) = g(x)/c"); -; } + ; } if (temp != null) { node[1].Replace(temp, new RPN.Node(1)); - RPN.Node division = new RPN.Node(new [] {temp, node[0]}, new RPN.Token("/", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new[] { temp, node[0] }, new RPN.Token("/", 2, RPN.Type.Operator)); node.Replace(node[0], division); } } @@ -2523,7 +2192,7 @@ private void Algebra(RPN.Node node, ref RPN.Node equality) RPN.Node temp = node[1, 0]; node[1].Replace(temp, new RPN.Node(1)); - RPN.Node multiplication = new RPN.Node(new [] {temp, node[0]}, new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node multiplication = new RPN.Node(new[] { temp, node[0] }, new RPN.Token("*", 2, RPN.Type.Operator)); node.Replace(node[0], multiplication); Simplify(node, SimplificationMode.Division); } @@ -2538,8 +2207,8 @@ private void Algebra(RPN.Node node, ref RPN.Node equality) { Write("\tf(x)^2 = g(x) -> abs(f(x)) = sqrt(g(x))"); - RPN.Node abs = new RPN.Node(new [] {node[1, 1]}, new RPN.Token("abs", 1, RPN.Type.Function)); - RPN.Node sqrt = new RPN.Node(new[] {node[0]}, new RPN.Token("sqrt", 1, RPN.Type.Function)); + RPN.Node abs = new RPN.Node(new[] { node[1, 1] }, new RPN.Token("abs", 1, RPN.Type.Function)); + RPN.Node sqrt = new RPN.Node(new[] { node[0] }, new RPN.Token("sqrt", 1, RPN.Type.Function)); node.Replace(node[1], abs); node.Replace(node[0], sqrt); @@ -2561,7 +2230,7 @@ private void explode(RPN.Node node) RPN.Token multiplication = new RPN.Token("*", 2, RPN.Type.Operator); //convert a sum to a series of additions - if (node.IsFunction("internal_sum") || node.IsFunction("sum")) + if (node.IsFunction("internal_sum") || node.IsFunction("total")) { if (node.Children.Count == 2) { @@ -2569,7 +2238,7 @@ private void explode(RPN.Node node) node.Children.Reverse(); } else - { + { Assign(node, gen(add)); } } @@ -2601,7 +2270,7 @@ private void explode(RPN.Node node) results.AddRange(node.Children[i].ToPostFix()); } - results.Add(new RPN.Token("sum", node.Children.Count, RPN.Type.Function)); + results.Add(new RPN.Token("total", node.Children.Count, RPN.Type.Function)); results.Add(new RPN.Token(node.Children.Count)); results.Add(division); RPN.Node temp = Generate(results.ToArray()); @@ -2611,6 +2280,7 @@ private void explode(RPN.Node node) RPN.Node gen(RPN.Token token) { + if (node.Children.Count == 1) { node.Remove(); @@ -2620,9 +2290,14 @@ RPN.Node gen(RPN.Token token) //TODO: Convert this from using ToPostFix to automatically generating a new correct tree! //Prep stage + if (token.IsAddition()) + { + node.Children.Reverse(); + } + Queue additions = new Queue(node.Children.Count); - additions.Enqueue(new RPN.Node(new [] {node[0], node[1]}, token)); - for (int i = 2; i + 1< node.Children.Count; i += 2) + additions.Enqueue(new RPN.Node(new[] { node[0], node[1] }, token)); + for (int i = 2; i + 1 < node.Children.Count; i += 2) { additions.Enqueue(new RPN.Node(new[] { node[i], node[i + 1] }, token)); } @@ -2635,7 +2310,7 @@ RPN.Node gen(RPN.Token token) while (additions.Count > 1) { - RPN.Node[] temp = new[] {additions.Dequeue(), additions.Dequeue()}; + RPN.Node[] temp = new[] { additions.Dequeue(), additions.Dequeue() }; temp.Reverse(); additions.Enqueue(new RPN.Node(temp, token)); } @@ -2649,6 +2324,8 @@ RPN.Node gen(RPN.Token token) } //This is fall back code! + + List results = new List(node.Children.Count); results.AddRange(node.Children[0].ToPostFix()); results.AddRange(node.Children[1].ToPostFix()); @@ -2673,7 +2350,8 @@ RPN.Node gen(RPN.Token token) /// /// Converts a series of multiplications, additions, or subtractions - /// into a new node to see if there are additional simplifications that can be made + /// into a new node to see if there are additional simplifications that can be made. + /// (IE converts f(x) + g(x) + h(x) -> internal_sum(f(x),g(x),h(x)) ) /// /// private void expand(RPN.Node node) @@ -2735,6 +2413,11 @@ private void expand(RPN.Node node) } } + /// + /// Converts internal_sum and internal_product back into a series of + /// multiplications and additions. + /// + /// private void compress(RPN.Node node) { Queue unvisited = new Queue(); @@ -2757,7 +2440,7 @@ private void compress(RPN.Node node) private void GenerateDerivativeAndReplace(RPN.Node child) { - RPN.Node temp = new RPN.Node( new[] { Clone( child ) }, _derive); + RPN.Node temp = new RPN.Node(new[] { child.Clone() }, _derive); temp.Parent = child.Parent; child.Parent?.Replace(child, temp); @@ -2767,7 +2450,7 @@ private void GenerateDerivativeAndReplace(RPN.Node child) private RPN.Node Clone(RPN.Node node) { - return Generate(node.ToPostFix().ToArray()); + return node.Clone(); } /// @@ -2800,18 +2483,18 @@ private void Assign(RPN.Node node, RPN.Node assign) } catch (IndexOutOfRangeException ex) { - throw new Exception($"node: {node.ToInfix()}, assign: {assign.ToInfix()}", ex); + throw new Exception($"node: {node.ToInfix(_data)}, assign: {assign.ToInfix(_data)}", ex); } } private void Write(string message) { - Logger?.Invoke(this, message); + logger.Log(Channels.Debug, message); } private void stdout(string message) { - Output?.Invoke(this, message); + logger.Log(Channels.Output, message); } } @@ -2821,4 +2504,7 @@ public struct OptimizationTracker public string Hash; public int count; } + + + } \ No newline at end of file diff --git a/AbMath/Calculator/Data.cs b/AbMath/Calculator/Data.cs index c0e653e..f201d8a 100644 --- a/AbMath/Calculator/Data.cs +++ b/AbMath/Calculator/Data.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Collections.Generic; using System.Diagnostics; -using CLI; +using AbMath.Utilities; namespace AbMath.Calculator { @@ -25,6 +25,10 @@ public class DataStore private List _time; private readonly Dictionary _variableStore; + protected internal object LockObject = new object(); + + protected internal Logger Logger; + /// /// A list of all the functions that are supported /// by this calculator. @@ -124,7 +128,7 @@ public class DataStore /// Determines the default format of CLI Tables /// based on the value of MarkdownTables /// - public Format DefaultFormat => (MarkdownTables) ? CLI.Format.MarkDown : CLI.Format.Default ; + public Format DefaultFormat => (MarkdownTables) ? Utilities.Format.MarkDown : Utilities.Format.Default ; public DataStore(string equation) { @@ -140,6 +144,7 @@ public DataStore(string equation) _time = new List(4); _variableStore = new Dictionary(); + Logger = new Logger(); DefaultFunctions(); DefaultOperators(); @@ -206,13 +211,13 @@ public Tables TimeRecords() Title = "Time" }); - times.Add(new Schema() { Column = "Type", Width = 30 }); - times.Add(new Schema() {Column = "# Calls", Width = 8}); - times.Add(new Schema() { Column = "Time (ms)", Width = 10 }); + times.Add(new Schema("Type")); + times.Add(new Schema("# Calls")); + times.Add(new Schema("Time (ms)")); - times.Add(new Schema() { Column = "Ticks", Width = 8 }); - times.Add(new Schema() { Column = "% Milliseconds", Width = 16 }); - times.Add(new Schema() { Column = "% Ticks", Width = 9 }); + times.Add(new Schema("Ticks")); + times.Add(new Schema("% Milliseconds")); + times.Add(new Schema("% Ticks")); double miliseconds = TotalMilliseconds; double steps = TotalSteps; @@ -334,6 +339,7 @@ public bool IsRightBracket(string value) public Type Resolve(string value) { + if (string.IsNullOrEmpty(value) || value == ".") { return Type.Null; } if (IsNumber(value)) { return Type.Number; } if (IsOperator(value)) { return Type.Operator; } if (IsFunction(value)) { return Type.Function; } @@ -364,428 +370,160 @@ private void DefaultOperators() { AddOperator("^", new Operator(Assoc.Right, 5, 2, DoOperators.Power)); AddOperator("E", new Operator(Assoc.Right, 5, 2, DoOperators.E)); + AddOperator("!", new Operator(Assoc.Left, 5, 1, DoOperators.Factorial)); - AddOperator("!", new Operator - { - Assoc = Assoc.Left, - Weight = 5, - Arguments = 1, - Compute = DoOperators.Factorial - }); + AddOperator("%", new Operator(Assoc.Left, 4, 2, DoOperators.Mod)); - AddOperator("%", new Operator - { - Assoc = Assoc.Left, - Weight = 4, - Arguments = 2, - Compute = DoOperators.Mod - }); - - AddOperator("/", new Operator - { - Assoc = Assoc.Left, - Weight = 4, - Arguments = 2, - Compute = DoOperators.Divide - }); + AddOperator("/", new Operator(Assoc.Left, 4, 2, DoOperators.Divide)); - AddOperator("*", new Operator - { - Assoc = Assoc.Left, - Weight = 4, - Arguments = 2, - Compute = DoOperators.Multiply - }); + AddOperator("*", new Operator(Assoc.Left, 4, 2, DoOperators.Multiply)); - AddOperator("+", new Operator - { - Assoc = Assoc.Left, - Weight = 3, - Arguments = 2, - Compute = DoOperators.Add - }); + AddOperator("+", new Operator(Assoc.Left, 3, 2, DoOperators.Add)); - AddOperator("++", new Operator - { - Assoc = Assoc.Left, - Weight = 3, - Arguments = 1, - Compute = DoOperators.AddSelf - }); + AddOperator("++", new Operator(Assoc.Left, 3, 1, DoOperators.AddSelf)); - AddOperator("−", new Operator - { - Assoc = Assoc.Left, - Weight = 3, - Arguments = 2, - Compute = DoOperators.Subtract - }); + AddOperator("−", new Operator(Assoc.Left, 3, 2, DoOperators.Subtract)); - AddOperator("-", new Operator - { - Assoc = Assoc.Left, - Weight = 3, - Arguments = 2, - Compute = DoOperators.Subtract - }); + AddOperator("-", new Operator(Assoc.Left, 3, 2, DoOperators.Subtract)); #region Evaluation - AddOperator(">", new Operator - { - Assoc = Assoc.Left, - Weight = 2, - Arguments = 2, - Compute = DoOperators.GreaterThan - }); + AddOperator(">", new Operator(Assoc.Left, 2, 2, DoOperators.GreaterThan)); - AddOperator("<", new Operator - { - Assoc = Assoc.Left, - Weight = 2, - Arguments = 2, - Compute = DoOperators.LessThan - }); + AddOperator("<", new Operator(Assoc.Left, 2, 2, DoOperators.LessThan)); - AddOperator("=", new Operator - { - Assoc = Assoc.Left, - Weight = 2, - Arguments = 2, - Compute = DoOperators.Equals - }); + AddOperator("=", new Operator(Assoc.Left, 2, 2, DoOperators.Equals)); - AddOperator("==", new Operator - { - Assoc = Assoc.Left, - Weight = 2, - Arguments = 2, - Compute = DoOperators.Equals - }); + AddOperator("==", new Operator(Assoc.Left, 2, 2, DoOperators.Equals)); - AddOperator(">=", new Operator - { - Assoc = Assoc.Left, - Weight = 2, - Arguments = 2, - Compute = DoOperators.GreaterThanOrEquals - }); + AddOperator(">=", new Operator(Assoc.Left, 2, 2, DoOperators.GreaterThanOrEquals)); - AddOperator("<=", new Operator - { - Assoc = Assoc.Left, - Weight = 2, - Arguments = 2, - Compute = DoOperators.LessThanOrEquals - }); + AddOperator("<=", new Operator(Assoc.Left, 2, 2, DoOperators.LessThanOrEquals)); #endregion #region Logic - AddOperator("!=", new Operator - { - Assoc = Assoc.Left, - Weight = 1, - Arguments = 2, - Compute = DoOperators.NotEquals - }); + AddOperator("!=", new Operator(Assoc.Left, 1, 2, DoOperators.NotEquals)); - AddOperator("&&", new Operator - { - Assoc = Assoc.Left, - Weight = 1, - Arguments = 2, - Compute = DoOperators.And - }); + AddOperator("&&", new Operator(Assoc.Left, 1, 2, DoOperators.And)); - AddOperator("||", new Operator - { - Assoc = Assoc.Left, - Weight = 1, - Arguments = 2, - Compute = DoOperators.Or - }); + AddOperator("||", new Operator(Assoc.Left, 1, 2, DoOperators.Or)); #endregion } private void DefaultFunctions() { -#region Trig - AddFunction("sin", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Sin - }); + #region Trig + AddFunction("sin", new Function(1, 1, 1, DoFunctions.Sin)); - AddFunction("cos", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Cos - }); + AddFunction("cos", new Function(1, 1, 1, DoFunctions.Cos)); - AddFunction("tan", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Tan - }); + AddFunction("tan", new Function(1, 1, 1, DoFunctions.Tan)); - AddFunction("sec", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Sec - }); + AddFunction("sec", new Function(1, 1, 1, DoFunctions.Sec)); - AddFunction("csc", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Csc - }); + AddFunction("csc", new Function(1, 1, 1, DoFunctions.Csc)); - AddFunction("cot", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Cot - }); + AddFunction("cot", new Function(1, 1, 1, DoFunctions.Cot)); - AddFunction("arcsin", new Function - { - Arguments = 1, - Compute = DoFunctions.Arcsin, - MaxArguments = 1, - MinArguments = 1 - }); + AddFunction("arcsin", new Function(1, 1, 1, DoFunctions.Arcsin)); - AddFunction("arccos", new Function - { - Arguments = 1, - Compute = DoFunctions.Arccos, - MaxArguments = 1, - MinArguments = 1 - }); + AddFunction("arccos", new Function(1, 1, 1, DoFunctions.Arccos)); - AddFunction("arctan", new Function - { - Arguments = 1, - Compute = DoFunctions.Arctan, - MaxArguments = 1, - MinArguments = 1 - }); + AddFunction("arctan", new Function(1, 1, 1, DoFunctions.Arctan)); - AddFunction("arcsec", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Arcsec - }); + AddFunction("arcsec", new Function(1, 1, 1, DoFunctions.Arcsec)); - AddFunction("arccsc", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Arccsc - }); + AddFunction("arccsc", new Function(1, 1, 1, DoFunctions.Arccsc)); - AddFunction("arccot", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Arccot - }); + AddFunction("arccot", new Function(1, 1, 1, DoFunctions.Arccot)); - AddFunction("rad", new Function() - { - Arguments = 1, - Compute = DoFunctions.rad, - MaxArguments = 1, - MinArguments = 1 - }); + AddFunction("rad", new Function(1, 1, 1, DoFunctions.rad)); - AddFunction("deg", new Function() - { - Arguments = 1, - Compute = DoFunctions.deg, - MaxArguments = 1, - MinArguments = 1 - }); -#endregion + AddFunction("deg", new Function(1, 1, 1, DoFunctions.deg)); + #endregion - AddFunction("max", new Function - { - MinArguments = 2, - Arguments = 2, - MaxArguments = int.MaxValue, - Compute = DoFunctions.Max - }); + Description max = new Description(); + max.Add("max(a,b,...)","Returns the highest value of all the passed in parameters."); + AddFunction("max", new Function(2,2,int.MaxValue,DoFunctions.Max, max)); - AddFunction("min", new Function - { - MinArguments = 2, - Arguments = 2, - MaxArguments = int.MaxValue, - Compute = DoFunctions.Min - }); + Description min = new Description(); + min.Add("min(a,b,...)","Returns the lowest value of all the passed in parameters."); + AddFunction("min", new Function(2,2,int.MaxValue,DoFunctions.Min, min)); - AddFunction("sqrt", new Function - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Sqrt - }); + Description sqrt = new Description(); + sqrt.Add("sqrt(f(x))","Returns the square root of f(x)."); + AddFunction("sqrt", new Function(1,1,1,DoFunctions.Sqrt, sqrt)); - AddFunction("round", new Function - { - MinArguments = 1, - Arguments = 2, - MaxArguments = 2, - Compute = DoFunctions.Round - }); + Description round = new Description(); + round.Add("round(a)","Rounds 'a' to the nearest integer"); + round.Add("round(a,b)", "Rounds 'a' to the 'b' position."); + round.Add("round(2.3) = 2"); + round.Add("round(2.6) = 3"); + round.Add("round(2.555,0) = 3"); + round.Add("round(2.555,1) = 2.6"); + round.Add("round(2.555,2) = 2.56"); + AddFunction("round", new Function(1, 2, 2, DoFunctions.Round, round)); - AddFunction("gcd", new Function - { - MinArguments = 2, - Arguments = 2, - MaxArguments = 2, - Compute = DoFunctions.Gcd - }); + Description gcd = new Description(); + gcd.Add("gcd(a,b)", "The greatest common denominator of 'a' and 'b'"); + AddFunction("gcd", new Function(2, 2, 2, DoFunctions.Gcd, gcd)); - AddFunction("lcm", new Function - { - MinArguments = 2, - Arguments = 2, - MaxArguments = 2, - Compute = DoFunctions.Lcm - }); + Description lcm = new Description("lcm(a,b)","The least common multiple of 'a' and 'b'"); + AddFunction("lcm", new Function(2, 2, 2, DoFunctions.Lcm, lcm)); - AddFunction("ln", new Function - { - Arguments = 1, - MaxArguments = 1, - MinArguments = 1, - Compute = DoFunctions.ln - }); + Description ln = new Description("ln(a)","Takes the natural log of 'a'. Equivalent to log(e,a)."); + AddFunction("ln", new Function(1, 1, 1, DoFunctions.ln, ln)); - AddFunction("log", new Function - { - MinArguments = 1, - Arguments = 2, - MaxArguments = 2, - Compute = DoFunctions.Log - }); + Description log = new Description("log(b,x)","Takes the log of 'x' with a base of 'b'.\nx = b^y <-> log(b,x) = y"); + log.Add("log(x)","Returns the natural log of a specified number"); + AddFunction("log", new Function(1,2,2, DoFunctions.Log, log)); -#region Constants - AddFunction("π", new Function - { - Arguments = 0, - MinArguments = 0, - MaxArguments = 0, - Compute = DoFunctions.Pi - }); + #region Constants + Description pi = new Description("π", "Returns the value of π."); + AddFunction("π", new Function(0, 0, 0, DoFunctions.Pi, pi)); - AddFunction("e", new Function - { - Arguments = 0, - MinArguments = 0, - MaxArguments = 0, - Compute = DoFunctions.EContstant - }); -#endregion + Description euler = new Description("e","Returns the euler number"); + AddFunction("e", new Function(0, 0, 0, DoFunctions.EContstant, euler)); + #endregion - AddFunction("bounded",new Function() - { - Arguments = 3, - MinArguments = 3, - MaxArguments = 3, - Compute = DoFunctions.Bounded - } - ); + Description bounded = new Description("bounded(low,x,high)","Returns low if (x < low)\nReturns high if (x > high)\nReturns x otherwise."); + AddFunction("bounded",new Function(3, 3, 3, DoFunctions.Bounded, bounded)); - AddFunction("total", new Function() - { - Arguments = 1, - MinArguments = 1, - MaxArguments = int.MaxValue, - Compute = DoFunctions.Sum - } - ); + Description total = new Description("total(a_0,...,a_n)","Totals up and returns the sum of all parameters."); + AddFunction("total", new Function(1, 1, int.MaxValue, DoFunctions.Sum, total)); - AddFunction("sum", new Function() - { - Arguments = 1, - MinArguments = 4, - MaxArguments = 5 - }); + Description sum = new Description("sum(f(x),x,a,b)","Computes or returns the sum of f(x) from 'a' to 'b'.\n'x' shall represent the index variable."); + AddFunction("sum", new Function(4, 4, 4, sum)); - AddFunction("avg", new Function() - { - Arguments = 1, - MinArguments = 1, - MaxArguments = int.MaxValue, - Compute = DoFunctions.Avg - }); + Description avg = new Description("avg(a,...,b)","Returns the average of all the passed in parameters."); + AddFunction("avg", new Function(1,1,int.MaxValue, DoFunctions.Avg, avg)); - AddFunction("random", new Function() - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 2, - Compute = DoFunctions.Random - } - ); + Description random = new Description("random()","Returns a non-negative random integer number."); + random.Add("random(ceiling)","Returns a non-negative integer number that is below the ceiling"); + random.Add("random(min,max)","Returns a random integer that is between the min and maximum."); + AddFunction("random", new Function(0, 0, 2, DoFunctions.Random, random)); - AddFunction("rand", new Function() - { - MinArguments = 0, - Arguments = 0, - MaxArguments = 0, - Compute = DoFunctions.Random - }); + Description rand = new Description("rand()", "Returns a non-negative random integer number."); + AddFunction("rand", new Function(0, 0, 0, DoFunctions.Random, rand)); - AddFunction("seed", new Function() - { - MinArguments = 1, - Arguments = 1, - MaxArguments = 1, - Compute = DoFunctions.Seed - }); + Description seed = new Description("seed(a)","Sets the seed for the random number generator."); + AddFunction("seed", new Function(1, 1, 1, DoFunctions.Seed, seed)); - AddFunction("abs", new Function() - { - Arguments = 1, - Compute = DoFunctions.Abs, - MaxArguments = 1, - MinArguments = 1 - }); + Description abs = new Description("abs(x)","Returns the absolute value of 'x'."); + AddFunction("abs", new Function(1, 1, 1, DoFunctions.Abs, abs)); - AddFunction("Γ", new Function() - { - Arguments = 1, - Compute = DoFunctions.Gamma, - MaxArguments = 1, - MinArguments = 1 - }); + Description binomial = new Description("binomial(n,k)","Returns the value of (n!)/[k!(n - k)!].\nThis is the equivalent of (n choose k).\nRestrictions:0 <= k <= n"); + AddFunction("binomial", new Function(2,2,2,DoFunctions.Binomial, binomial)); + + Description gamma = new Description("Γ(x)", "The gamma function is related to factorials as: Γ(x) = (x - 1)!.\nSince the gamma function is really hard to compute we are using Gergő Nemes Approximation."); + AddFunction("Γ", new Function(1, 1, 1, DoFunctions.Gamma, gamma)); #region MetaCommands - AddFunction("derivative", new Function() - { - Arguments = 2, - MinArguments = 2, - MaxArguments = 3, - }); + Description derivative = new Description("derivative(f(x),x)","Takes the derivative of f(x) in respect to x."); + derivative.Add("derivative(f(x),x,n)",""); + derivative.Add("derivative(f(x),x,2) = derivative(derivative(f(x),x),x)"); + AddFunction("derivative", new Function(2, 2, 3, derivative)); + AddFunction("integrate", new Function() { Arguments = 4, diff --git a/AbMath/Calculator/Description.cs b/AbMath/Calculator/Description.cs new file mode 100644 index 0000000..d95dd5f --- /dev/null +++ b/AbMath/Calculator/Description.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator +{ + /// + /// The description for functions and operators. + /// + public class Description + { + private List _signature; + private List _blurbs; + private List _examples; + + public IReadOnlyList Signatures => _signature.AsReadOnly(); + public IReadOnlyList Blurbs => _blurbs.AsReadOnly(); + + public Description() + { + _signature = new List(); + _blurbs = new List(); + _examples = new List(); + } + + public Description(string signature, string blurb) + { + _signature = new List(); + _blurbs = new List(); + _examples = new List(); + this.Add(signature, blurb); + } + + public void Add(string signature, string blurb) + { + if (signature == null || blurb == null) + { + throw new ArgumentNullException(signature,"The parameter cannot be null for the description."); + } + _signature.Add(signature); + _blurbs.Add(blurb); + } + + public void Add(string example) + { + if (example == null) + { + throw new ArgumentNullException(example, "The example parameter cannot be null."); + } + _examples.Add(example); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < _signature.Count; i++) + { + sb.AppendLine(_signature[i]); + sb.AppendLine(_blurbs[i]); + } + + if (_examples.Count > 0) + { + sb.AppendLine("\nExamples:"); + } + + foreach (var ex in _examples) + { + sb.AppendLine(ex); + } + + return sb.ToString(); + } + } +} diff --git a/AbMath/Calculator/Extensions.cs b/AbMath/Calculator/Extensions.cs index 14a7765..c8813ca 100644 --- a/AbMath/Calculator/Extensions.cs +++ b/AbMath/Calculator/Extensions.cs @@ -178,6 +178,16 @@ public static string GetDecimalFormat(double n) } private static string getDecimalFormat(double value, double accuracy = 1E-4, int maxIteration = 10000) + { + RPN.Node foo = getDecimalFormatToNode(value, accuracy, maxIteration); + if (foo == null) + { + return null; + } + return foo.ToInfix(); + } + + public static RPN.Node getDecimalFormatToNode(double value, double accuracy = 1E-4, int maxIteration = 10000) { //Algorithm from stack overflow. try @@ -248,8 +258,11 @@ private static string getDecimalFormat(double value, double accuracy = 1E-4, int return null; } + RPN.Node f = new RPN.Node(new RPN.Node[] {new RPN.Node(denominator), new RPN.Node(numerator * sign)}, + new RPN.Token("/", 2, RPN.Type.Operator)); + // Middle is our best fraction - return $"{numerator * sign}/{denominator}"; + return f; } i++; @@ -265,5 +278,6 @@ private static string getDecimalFormat(double value, double accuracy = 1E-4, int return null; } } + } } \ No newline at end of file diff --git a/AbMath/Calculator/Function.cs b/AbMath/Calculator/Function.cs new file mode 100644 index 0000000..93815af --- /dev/null +++ b/AbMath/Calculator/Function.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator +{ + /// + /// A list of related information of functions. + /// + public struct Function + { + public int Arguments; + public int MaxArguments; + public int MinArguments; + + public RPN.Run Compute; + public Description Description; + + /// + /// For meta functions + /// + /// + /// + /// + /// + public Function(int Min, int Args, int Max, Description description) + { + MinArguments = Min; + Arguments = Args; + MaxArguments = Max; + Compute = null; + Description = description; + } + + /// + /// For functions without descriptions + /// + /// + /// + /// + /// + public Function(int min, int args, int max, RPN.Run compute) + { + MinArguments = min; + Arguments = args; + MaxArguments = max; + Compute = compute; + Description = null; + } + + /// + /// For functions with descriptions + /// + /// + /// + /// + /// + /// + public Function(int min, int args, int max, RPN.Run compute, Description description) + { + MinArguments = min; + Arguments = args; + MaxArguments = max; + Compute = compute; + Description = description; + } + } +} diff --git a/AbMath/Calculator/IRPN.cs b/AbMath/Calculator/IRPN.cs index 6dfc035..42ee055 100644 --- a/AbMath/Calculator/IRPN.cs +++ b/AbMath/Calculator/IRPN.cs @@ -7,13 +7,11 @@ public enum Assoc { Left, Right }; public interface IShunt { T[] ShuntYard(List tokens); - event EventHandler Logger; } public interface ITokenizer { List Tokenize(); - event EventHandler Logger; } public interface IEvaluator diff --git a/AbMath/Calculator/Math/DoFunctions.cs b/AbMath/Calculator/Math/DoFunctions.cs index 50eba63..7ea50c0 100644 --- a/AbMath/Calculator/Math/DoFunctions.cs +++ b/AbMath/Calculator/Math/DoFunctions.cs @@ -8,6 +8,13 @@ public static class DoFunctions { private static Random _rand; + public static double Binomial(params double[] arguments) + { + double n = arguments[0]; + double k = arguments[1]; + return DoOperators.Factorial(n) / (DoOperators.Factorial(k) * DoOperators.Factorial(n - k)); + } + public static double Gamma(params double[] arguments) { //If args[0] is an integer use factorials diff --git a/AbMath/Calculator/MetaCommands/Meta-Tables.cs b/AbMath/Calculator/MetaCommands/Meta-Tables.cs index d469ca6..09b7ca3 100644 --- a/AbMath/Calculator/MetaCommands/Meta-Tables.cs +++ b/AbMath/Calculator/MetaCommands/Meta-Tables.cs @@ -1,5 +1,5 @@ using System; -using CLI; +using AbMath.Utilities; namespace AbMath.Calculator { @@ -26,8 +26,8 @@ public static string Table(RPN rpn, RPN.Node expression, RPN.Node variable, RPN. Format = rpn.Data.DefaultFormat, Title = "Table" }); - table.Add(new Schema {Column = $"{variable.Token.Value}", Width = 26}); - table.Add(new Schema { Column = $"f({variable.Token.Value})", Width = 26 }); + table.Add(new Schema(variable.Token.Value)); + table.Add(new Schema($"f({variable.Token.Value})")); for (int x = 0; x <= max; x++) { diff --git a/AbMath/Calculator/Node.cs b/AbMath/Calculator/Node.cs index 2b3fb87..0e4dbf4 100644 --- a/AbMath/Calculator/Node.cs +++ b/AbMath/Calculator/Node.cs @@ -8,7 +8,7 @@ namespace AbMath.Calculator { public partial class RPN { - public class Node + public class Node : IComparable { public int ID; public Token Token; @@ -18,13 +18,15 @@ public class Node private List _children; private MD5 _md5; + + private static object myLock = new object(); private static int counter = 0; public Node(Node[] children, Token token) { this._children = new List(); AssignChildren(children); - this.ID = counter++; + this.ID = NextCounter(); Parent = null; Token = token; } @@ -32,7 +34,7 @@ public Node(Node[] children, Token token) public Node(double number) { _children = new List(0); - this.ID = counter++; + this.ID = NextCounter(); Parent = null; Token = new RPN.Token(number); } @@ -40,7 +42,7 @@ public Node(double number) public Node(Token token) { _children = new List(0); - this.ID = counter++; + this.ID = NextCounter(); Parent = null; Token = token; } @@ -63,6 +65,36 @@ public Node this[int i] set => _children[i]._children[j]._children[k] = value; } + public static Node Generate(RPN.Token[] input) + { + Stack stack = new Stack(5); + + for (int i = 0; i < input.Length; i++) + { + RPN.Node node = new RPN.Node(input[i]); + if (node.IsOperator() || node.IsFunction()) + { + //Due to the nature of PostFix we know that all children + //of a function or operator have already been processed before this point + //this ensures we do not have any overflows or exceptions. + RPN.Node[] range = new RPN.Node[node.Token.Arguments]; + for (int j = 0; j < node.Token.Arguments; j++) + { + range[j] = stack.Pop(); + } + node.AddChild(range); + } + stack.Push(node); //Push new tree into the stack + } + + return stack.Pop(); + } + + public Node Clone() + { + return Generate(this.ToPostFix().ToArray()); + } + /// /// Replaces in the tree the node with @@ -83,7 +115,7 @@ public void Replace(Node node, Node replacement) /// The replacement public void Replace(int identification, Node node) { - for (int i = 0; i < Children.Count; i++) + for (int i = 0; i < _children.Count; i++) { if (_children[i].ID == identification) { @@ -117,7 +149,7 @@ public void Replace(double number) return; } - this.ID = counter++; + this.ID = NextCounter(); this.Token.Value = number.ToString(); } @@ -128,7 +160,7 @@ public void Replace(double number) /// public void Replace(RPN.Token token) { - this.ID = counter++; + this.ID = NextCounter(); this.Token = token; } @@ -139,7 +171,7 @@ public void Replace(RPN.Token token) /// public void Replace(string token) { - this.ID = counter++; + this.ID = NextCounter(); this.Token.Value = token; } @@ -180,6 +212,7 @@ public void Remove(Node replacement) public void Delete() { Parent = null; + Token = null; _children.Clear(); } @@ -371,9 +404,39 @@ public double GetNumber() return double.Parse(Token.Value); } - public bool containsDomainViolation() + public Function? GetFunction(RPN.DataStore data) + { + if (!IsFunction()) + { + return null; + } + + return data.Functions[this.Token.Value]; + } + + public Operator? GetOperator(RPN.DataStore data) + { + if (!IsOperator()) + { + return null; + } + + return data.Operators[this.Token.Value]; + } + + public bool ContainsDomainViolation() + { + return Contains(new RPN.Token("/", 2, RPN.Type.Operator)); + } + + public bool Contains(RPN.Node node) { - return this.ToPostFix().Contains(new RPN.Token("/", 2, RPN.Type.Operator)); + return Contains(node.Token); + } + + public bool Contains(RPN.Token token) + { + return this.ToPostFix().Contains(token); } public bool IsNumberOrConstant() @@ -396,6 +459,11 @@ public bool IsInteger() return IsNumber() && ((int)GetNumber()) == GetNumber(); } + public bool IsInteger(int number) + { + return IsInteger() && GetNumber() == number; + } + public bool IsLessThanNumber(double number) { return Token.IsNumber() && double.Parse(Token.Value) < number; @@ -451,6 +519,16 @@ public bool IsVariable() return Token.IsVariable(); } + public bool IsVariable(Node node) + { + return IsVariable(node.Token.Value); + } + + public bool IsVariable(string variable) + { + return Token.IsVariable() && Token.Value == variable; + } + public bool IsAddition() { return Token.IsAddition(); @@ -539,15 +617,15 @@ public bool IsSolveable() return PostFix(node); } - public string ToInfix() + public string ToInfix(RPN.DataStore? data = null) { - return ToInfix(this); + return ToInfix(this, data); } - public string ToInfix(RPN.Node node) + public string ToInfix(RPN.Node node, RPN.DataStore? data) { StringBuilder infix = new StringBuilder(); - Infix(node, infix); + Infix(node, infix, data); return infix.ToString(); } @@ -594,7 +672,7 @@ public List ToPrefix() /// /// /// - private void Infix(RPN.Node node, StringBuilder infix) + private void Infix(RPN.Node node, StringBuilder infix, RPN.DataStore? data) { //TODO: Implement nonrecursive algorithim! if (node is null) @@ -602,34 +680,100 @@ private void Infix(RPN.Node node, StringBuilder infix) return; } + //Rules from https://stackoverflow.com/questions/14175177/how-to-walk-binary-abstract-syntax-tree-to-generate-infix-notation-with-minimall + + bool parenthesis = true; + if (node.Parent == null) + { + parenthesis = false; + } + else if (data != null && node.IsOperator() && node.Parent.IsOperator() && data.Operators[node.Token.Value].Weight > + data.Operators[node.Parent.Token.Value].Weight) + { + parenthesis = false; + } + else if (node.IsOperator("+") && node.Parent.IsOperator("+")) + { + parenthesis = false; + } + else if (node.IsOperator("*") && node.Parent.IsOperator("*")) + { + parenthesis = false; + } + else if (node.IsOperator() && node.Parent.IsFunction() && !node.Parent.IsConstant()) + { + parenthesis = false; //abs(f(x) + g(x)) + } + //TODO: Correctly Implement Associative rules + //Operators with left and right + if (node.Children.Count == 2 && node.Token.IsOperator()) { - infix.Append("("); - Infix(node.Children[1], infix); - infix.Append(node.Token.Value); - Infix(node.Children[0], infix); - infix.Append(")"); + if (parenthesis) { + infix.Append("("); + } + + bool printOperator = true; + + if (node.IsOperator("*")) + { + if (node[0].IsFunction() && node[1].IsFunction()) + { + printOperator = false; //sin(x)cos(x) + } + else if (node[0].IsNumber() && node[1].IsFunction()) + { + printOperator = false; //2sin(x) + } + else if (node[0].IsFunction() && node[1].IsNumber()) + { + printOperator = false; //sin(x)2 + } + } + + Infix(node.Children[1], infix, data); + + + if (printOperator) + { + infix.Append(node.Token.Value); //The operator + } + + Infix(node.Children[0], infix, data); + + if (parenthesis) + { + infix.Append(")"); + } + return; } + //Operators that only have one child if (node.Children.Count == 1 && node.Token.IsOperator()) { + if (node.IsOperator("!")) + { + infix.Append(node[0].ToInfix()); + infix.Append(node.Token.Value); + return; + } + infix.Append(node.Token.Value); - Infix(node.Children[0], infix); + Infix(node.Children[0], infix, data); return; } - //Functions - //Functions + //Functions that have at least one child if (node.Children.Count > 0 && node.Token.IsFunction()) { infix.Append(node.Token.Value); infix.Append("("); for (int i = (node.Children.Count - 1); i >= 0; i--) { - Infix(node.Children[i], infix); + Infix(node.Children[i], infix, data); if (i > 0) { infix.Append(","); @@ -647,7 +791,167 @@ private void Infix(RPN.Node node, StringBuilder infix) public static void ResetCounter() { - counter = 0; + MutateCounter(0); + } + + public static int NextCounter() + { + lock (myLock) + { + counter++; + } + return counter; + } + + private static void MutateCounter(int value) + { + lock (myLock) + { + counter = value; + } + } + + public override bool Equals(Object obj) + { + if (obj == null || this.GetType() != obj.GetType()) + { + return false; + } + + return this.GetHash() == ((Node) (obj)).GetHash(); + } + + /// + /// Compares two nodes to each other. + /// The value it returns is dependent + /// on its parent and the structure of the tree. + /// + /// It is expected that two nodes will share a same + /// parent when being compared. + /// + /// + /// + public int CompareTo(Node other) + { + //Assume that if you have no parent you really cannot sort. + if (other.Parent == null || this.Parent == null) + { + return 0; + } + + //If we have different parents we cannot be sorted! + if (other.Parent.ID != this.Parent.ID) + { + return 0; + } + + /** + * Map + * 0 -> other + * 1 -> this + */ + + //Here we know we have the same parent and that parent exists! + if (this.Parent.IsAddition() || this.Parent.IsFunction("internal_sum") || this.Parent.IsFunction("total")) + { + //Numbers and constants should yield to everything + if (this.IsNumberOrConstant() && !other.IsNumberOrConstant()) + { + return -1; + } + + if (!this.IsNumberOrConstant() && other.IsNumberOrConstant()) + { + return 1; + } + + if (this.IsNumberOrConstant() && other.IsNumberOrConstant()) + { + return this.GetNumber().CompareTo(other.GetNumber()); + } + + //Something that can be solved should yield like constants and numbers do! + if (this.IsSolveable() && !other.IsSolveable()) + { + return -1; + } + + if (!this.IsSolveable() && other.IsSolveable()) + { + return 1; + } + + if (this.IsSolveable() && other.IsSolveable()) + { + return 0; + } + + //Single Variables should yield to other expressions + if (this.IsVariable() && !other.IsVariable()) + { + return -1; + } + + if (!this.IsVariable() && other.IsVariable()) + { + return 1; + } + + if (this.IsVariable() && other.IsVariable()) + { + return 0; + } + + //Multiplication should yield to exponents + if (this.IsMultiplication() && other.IsExponent()) + { + return -1; + } + if (this.IsExponent() && other.IsMultiplication()) + { + return 1; + } + + //Multiplication should yield to multiplications that contain exponents + if (this.IsMultiplication() && other.IsMultiplication()) + { + //This does not have an exponent and the other one does + if (!this.Children.Any(n => n.IsExponent()) && other.Children.Any(n => n.IsExponent())) + { + return -1; + } + else if (!other.Children.Any(n => n.IsExponent()) && this.Children.Any(n => n.IsExponent())) + { + return 1; + } + } + //Exponents compared to other exponents + if (this.IsExponent() && other.IsExponent()) + { + //Constant powers should yield to non constant powers + + //Constant powers should yield depending on their value! + if (this[0].IsNumberOrConstant() && other[0].IsNumberOrConstant()) + { + return this[0].GetNumber().CompareTo(other[0].GetNumber()); + } + } + + //TODO: A straight exponent should give way to a multiplication with an exponent if... + //TODO: Swapping exponent with non exponent + } + else if (this.Parent.IsMultiplication() || this.Parent.IsFunction("internal_product")) + { + //Sort order for multiplication + //1) Numbers or Constants + //2) Exponents of constants + //3) Exponents of variables + //4) Variables + //5) Functions (sorted alphabetically) + //6) Expressions (Everything else) + } + + return 0; } } } diff --git a/AbMath/Calculator/Operator.cs b/AbMath/Calculator/Operator.cs new file mode 100644 index 0000000..ced6a47 --- /dev/null +++ b/AbMath/Calculator/Operator.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator +{ + public struct Operator + { + public int Weight; + public int Arguments; + public Assoc Assoc; + public RPN.Run Compute; + public Description Description; + + /// + /// For operators without description + /// + /// + /// + /// + /// + public Operator(Assoc assoc, int weight, int arguments, RPN.Run compute) + { + Weight = weight; + Arguments = arguments; + Assoc = assoc; + Compute = compute; + Description = null; + } + + /// + /// For operators with a description + /// + /// + /// + /// + /// + /// + public Operator(Assoc assoc, int weight, int arguments, RPN.Run compute, Description description) + { + Weight = weight; + Arguments = arguments; + Assoc = assoc; + Compute = compute; + Description = description; + } + } +} diff --git a/AbMath/Calculator/OptimizerRuleEngine.cs b/AbMath/Calculator/OptimizerRuleEngine.cs new file mode 100644 index 0000000..fed8dc4 --- /dev/null +++ b/AbMath/Calculator/OptimizerRuleEngine.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using AbMath.Utilities; + +namespace AbMath.Calculator +{ + /// + /// The Rule Engine that powers all of the Abstract Syntax Tree (AST) simplifications. + /// It allows us to quickly manage large number of rules. + /// + public class OptimizerRuleEngine + { + public static Dictionary> ruleSet { get; private set; } + public static Dictionary setRule { get; private set; } + + private Dictionary ruleSetTracker; + private Dictionary canExecuteTracker; + private Dictionary hits; + + public bool debug; + + private Logger logger; + private static HashSet contains; + + public OptimizerRuleEngine(Logger logger, bool debugMode = false) + { + if (ruleSet == null) + { + ruleSet = new Dictionary>(); + } + + if (contains == null) + { + contains = new HashSet(); + } + + if (setRule == null) + { + setRule = new Dictionary(); + } + + hits = new Dictionary(); + debug = debugMode; + this.logger = logger; + + if (debug) + { + ruleSetTracker = new Dictionary(); + canExecuteTracker = new Dictionary(); + } + } + + public void Add(AST.SimplificationMode mode, Rule rule) + { + //If the rule already exists we should not + //add it! + if (contains.Contains(rule)) + { + return; + } + + contains.Add(rule); + rule.Logger += Write; + + //When the key already exists we just add onto the existing list + if (ruleSet.ContainsKey(mode)) + { + List rules = ruleSet[mode]; + rules.Add(rule); + ruleSet[mode] = rules; + return; + } + //When the key does not exist we should create a list and add the rule onto it + ruleSet.Add(mode, new List { rule }); + hits.Add(mode, 0); + + if (debug) + { + Stopwatch sw = new Stopwatch(); + sw.Reset(); + ruleSetTracker.Add(mode, sw); + + Stopwatch sw2 = new Stopwatch(); + sw2.Reset(); + canExecuteTracker.Add(mode, sw2); + } + } + + public void AddSetRule(AST.SimplificationMode mode, Rule rule) + { + if (!this.HasSetRule(mode)) + { + setRule.Add(mode, rule); + } + } + + public List Get(AST.SimplificationMode mode) + { + return ruleSet[mode]; + } + + public Rule GetSetRule(AST.SimplificationMode mode) + { + return setRule[mode]; + } + + /// + /// This executes any possible simplification in the appropriate + /// set. + /// + /// The set to look in + /// The node to apply over + /// A new node that is the result of the application of the rule or null + /// when no rule could be run + public RPN.Node Execute(AST.SimplificationMode mode, RPN.Node node) + { + if (!ruleSet.ContainsKey(mode)) + { + throw new KeyNotFoundException("The optimization set was not found"); + } + + List rules = ruleSet[mode]; + Stopwatch sw = null; + + if (debug) + { + if (ruleSetTracker == null) + { + ruleSetTracker = new Dictionary(); + } + + if (ruleSetTracker.ContainsKey(mode)) + { + sw = ruleSetTracker[mode]; + } + else + { + sw = new Stopwatch(); + ruleSetTracker.Add(mode, sw); + } + + sw.Start(); + } + + if (!hits.ContainsKey(mode)) + { + hits.Add(mode, 0); + } + + for (int i = 0; i < rules.Count; i++) + { + Rule rule = rules[i]; + if (rule.CanExecute(node)) + { + RPN.Node temp = rule.Execute(node); + if (debug) + { + sw.Stop(); + } + + if (rule.DebugMode()) + { + Write("The output of : " + temp.ToInfix()); + } + + hits[mode] = hits[mode] + 1; + return temp; + } + } + + if (debug) + { + sw.Stop(); + } + + return null; + } + + public bool ContainsSet(AST.SimplificationMode mode) + { + return ruleSet.ContainsKey(mode); + } + + public bool HasSetRule(AST.SimplificationMode mode) + { + return setRule.ContainsKey(mode); + } + + public bool CanRunSet(AST.SimplificationMode mode, RPN.Node node) + { + if (debug) + { + if (canExecuteTracker == null) + { + canExecuteTracker = new Dictionary(); + } + + if (!canExecuteTracker.ContainsKey(mode)) + { + Stopwatch temp = new Stopwatch(); + temp.Reset(); + canExecuteTracker.Add(mode, temp); + } + } + + bool result = false; + Stopwatch sw = null; + if (debug) + { + sw = canExecuteTracker[mode]; + sw.Start(); + } + + result = ContainsSet(mode) && (!HasSetRule(mode) || GetSetRule(mode).CanExecute(node)); + if (debug) + { + sw.Stop(); + } + return result; + } + + private void Write(object obj, string message) + { + Write(message); + } + + private void Write(string message) + { + logger.Log(Channels.Debug, message); + } + + public override string ToString() + { + return this.ToString(null); + } + + public string ToString(string format) + { + Config config = new Config() { Format = Format.Default, Title = "Simplification Rule Set Overview" }; + if (format == "%M") + { + config.Format = Format.MarkDown; + } + + Tables ruleTables = new Tables(config); + ruleTables.Add(new Schema("Name")); + ruleTables.Add(new Schema("Count")); + ruleTables.Add(new Schema("Set Rule")); + ruleTables.Add(new Schema("Execution Time (ms | Ticks)")); + ruleTables.Add(new Schema("Check Time (ms | Ticks)")); + ruleTables.Add(new Schema("Hits")); + + int totalRules = 0; + long totalExecutionElapsedMilliseconds = 0; + long totalExecutionElappsedTicks = 0; + long totalCheckElapsedMilliseconds = 0; + long totalCheckElapsedTicks = 0; + int totalHits = 0; + foreach (var KV in ruleSet) + { + string executionTime = "-"; + string checkTime = "-"; + string hit = "-"; + + if (debug && ruleSetTracker != null && ruleSetTracker.ContainsKey(KV.Key)) + { + executionTime = ruleSetTracker[KV.Key].ElapsedMilliseconds.ToString() + " | " + ruleSetTracker[KV.Key].ElapsedTicks.ToString("N0"); + checkTime = canExecuteTracker[KV.Key].ElapsedMilliseconds.ToString() + " | " + canExecuteTracker[KV.Key].ElapsedTicks.ToString("N0"); + + totalExecutionElapsedMilliseconds += ruleSetTracker[KV.Key].ElapsedMilliseconds; + totalExecutionElappsedTicks += ruleSetTracker[KV.Key].ElapsedTicks; + + totalCheckElapsedMilliseconds += canExecuteTracker[KV.Key].ElapsedMilliseconds; + totalCheckElapsedTicks += canExecuteTracker[KV.Key].ElapsedTicks; + } + + if (hits.ContainsKey(KV.Key)) + { + hit = hits[KV.Key].ToString(); + totalHits += hits[KV.Key]; + } + + totalRules += KV.Value.Count; + + string[] row = new string[] { KV.Key.ToString(), KV.Value.Count.ToString(), setRule.ContainsKey(KV.Key) ? "✓" : "X", executionTime, checkTime, hit }; + ruleTables.Add(row); + } + string[] total = new string[] { "Total", totalRules.ToString(), "", $"{totalExecutionElapsedMilliseconds} | {totalExecutionElappsedTicks}", $"{totalCheckElapsedMilliseconds} | {totalCheckElapsedTicks}", totalHits.ToString() }; + ruleTables.Add(total); + + return ruleTables.ToString(); + } + } +} diff --git a/AbMath/Calculator/PostFix/PostFix.cs b/AbMath/Calculator/PostFix.cs similarity index 96% rename from AbMath/Calculator/PostFix/PostFix.cs rename to AbMath/Calculator/PostFix.cs index a35c7bb..b9cdd3c 100644 --- a/AbMath/Calculator/PostFix/PostFix.cs +++ b/AbMath/Calculator/PostFix.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.Collections.Generic; -using CLI; namespace AbMath.Calculator { @@ -81,7 +80,7 @@ public double Compute(RPN.Token[] input) break; case RPN.Type.Operator: { - RPN.Operator Operator = _dataStore.Operators[token.Value]; + Operator Operator = _dataStore.Operators[token.Value]; double[] arguments = GetArguments(token.Arguments); double ans = Operator.Compute(arguments); _stack.Push(ans); @@ -90,7 +89,7 @@ public double Compute(RPN.Token[] input) case RPN.Type.Function: { //Looks up the function in the Dict - RPN.Function function = _dataStore.Functions[token.Value]; + Function function = _dataStore.Functions[token.Value]; double[] arguments = GetArguments(token.Arguments); double ans = function.Compute(arguments); diff --git a/AbMath/Calculator/Reverse Polish Notation.cs b/AbMath/Calculator/Reverse Polish Notation.cs index 7593914..9112bb4 100644 --- a/AbMath/Calculator/Reverse Polish Notation.cs +++ b/AbMath/Calculator/Reverse Polish Notation.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Security.Cryptography; +using AbMath.Utilities; namespace AbMath.Calculator { @@ -34,39 +36,6 @@ public enum Type {LParen,RParen,Number,Variable,Function,Operator,Null}; /// public event EventHandler Output; - public struct Operator - { - public int Weight; - public int Arguments; - public Assoc Assoc; - public Run Compute; - - public Operator(Assoc assoc, int weight, int arguments, Run compute ) - { - this.Weight = weight; - this.Arguments = arguments; - this.Assoc = assoc; - this.Compute = compute; - } - } - - public struct Function - { - public int Arguments; - public int MaxArguments; - public int MinArguments; - - public Run Compute; - - public Function(int Min, int Args, int Max, Run compute) - { - this.MinArguments = Min; - this.Arguments = Args; - this.MaxArguments = Max; - this.Compute = compute; - } - } - public struct TimeRecord { public string Type; @@ -143,20 +112,25 @@ private void Startup() public RPN Compute() { - _tokenizer.Logger += Logger; - Tokens = _tokenizer.Tokenize(); + //We bind late because otherwise there is no way that someone can attach into + //either of the below channels! + Data.Logger.Bind(Channels.Debug, Logger); + Data.Logger.Bind(Channels.Output, Output); - _shunt.Logger += Logger; + Tokens = _tokenizer.Tokenize(); Data.Polish = _shunt.ShuntYard( this.Tokens ); //Generate an Abstract Syntax Tree + + var logger = Data.Logger; + AST ast = new AST(this); ast.Output += Output; ast.Logger += Logger; ast.Generate(this.Data.Polish); - - Write("AST RPN : " + ast.Root.ToPostFix().Print()); + + logger.Log(Channels.Debug, "AST RPN : " + ast.Root.ToPostFix().Print()); //Simplify the Abstract Syntax Tree //This can take quite a lot of time @@ -164,19 +138,15 @@ public RPN Compute() ast.MetaFunctions(); this.Data.Polish = ast.Root.ToPostFix().ToArray(); - this.Data.SimplifiedEquation = ast.Root.ToInfix(); + this.Data.SimplifiedEquation = ast.Root.ToInfix(this.Data); - Write(""); - Write("AST Simplified RPN : " + this.Data.Polish.Print()); - Write("AST Simplified Infix : " + this.Data.SimplifiedEquation); - Write( ast.Root.Print()); + logger.Log(Channels.Debug, ""); + logger.Log(Channels.Debug, "AST Simplified RPN : " + Data.Polish.Print()); + logger.Log(Channels.Debug, "AST Simplified Infix : " + Data.SimplifiedEquation); + logger.Log(Channels.Debug, ast.Root.Print()); + //TODO: We should flush the Logger here return this; } - - private void Write(string message) - { - Logger?.Invoke(this, message); - } } } diff --git a/AbMath/Calculator/Rule.cs b/AbMath/Calculator/Rule.cs new file mode 100644 index 0000000..79610a2 --- /dev/null +++ b/AbMath/Calculator/Rule.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator +{ + /// + /// The rules class has been created to reduce complexity of the code + /// by allowing us to add rules for optimizations + /// and derivatives. + /// + public class Rule + { + public event EventHandler Logger; + + public delegate bool isRunnable(RPN.Node node); + public delegate RPN.Node Run(RPN.Node node); + + private isRunnable CanRun; + + private Run Compute; + + public string Name { get; private set; } + + private Rule preRule; + private Rule postRule; + + private bool debug; + + public Rule(isRunnable isRunnable, Run run, string name, bool debugMode = false) + { + CanRun = isRunnable; + Compute = run; + Name = name; + debug = debugMode; + } + + /// + /// Adds a pre processing rule. + /// The rule is optional and does not have to be run + /// for the main rule to be run. + /// + /// + /// + public Rule AddPreProcessingRule(Rule rule) + { + preRule = rule; + return this; + } + + /// + /// Adds a post processing rule. + /// The rule is optional and does not have to be run + /// for the main rule to be run. + /// + /// + /// + public Rule AddPostProcessingRule(Rule rule) + { + postRule = rule; + return this; + } + + public RPN.Node Execute(RPN.Node node) + { + RPN.Node assignment = PreOrPostprocess(preRule, node); + if (assignment != null) + { + node = assignment; + } + + Write($"\t{Name}"); + node = Compute.Invoke(node); + + assignment = PreOrPostprocess(postRule, node); + if (assignment != null) + { + node = assignment; + } + + return node; + + } + + private RPN.Node PreOrPostprocess(Rule rule, RPN.Node node) + { + if (rule != null) + { + //if we the pre rule confirms it is applicable run it! + if (rule.CanExecute(node)) + { + Write($"\t{rule.Name}"); + return rule.Execute(node.Clone()); + } + } + + return null; + } + + public bool CanExecute(RPN.Node node) + { + if (preRule != null && preRule.CanExecute(node)) + { + return CanRun.Invoke(preRule.Execute(node)); + } + + return CanRun.Invoke(node); + } + + public bool DebugMode() + { + return debug; + } + + private void Write(string message) + { + Logger?.Invoke(this, message); + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(Object obj) + { + if (obj == null || obj.GetType() != this.GetType()) + { + return false; + } + + Rule temp = (Rule) obj; + return temp.Name == this.Name; + } + + } +} diff --git a/AbMath/Calculator/Shunt.cs b/AbMath/Calculator/Shunt.cs index f14f54c..5305469 100644 --- a/AbMath/Calculator/Shunt.cs +++ b/AbMath/Calculator/Shunt.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using CLI; +using AbMath.Utilities; namespace AbMath.Calculator { @@ -41,11 +41,12 @@ public class Shunt : IShunt //See https://web.archive.org/web/20181008151605/http://wcipeg.com/wiki/Shunting_yard_algorithm#Variadic_functions private Stack _arity; - public event EventHandler Logger; + private Logger logger; public Shunt(DataStore dataStore) { _dataStore = dataStore; + logger = dataStore.Logger; _multiply = new Token("*", 2, Type.Operator); _division = new Token("/", 2, Type.Operator); @@ -68,16 +69,16 @@ public Token[] ShuntYard(List tokens) if (_dataStore.DebugMode) { _tables = new Tables(new Config { Title = "Shunting Yard Algorithm", Format = _dataStore.DefaultFormat }); - _tables.Add(new Schema {Column = "#", Width = 3}); - _tables.Add(new Schema {Column = "Token", Width = 10}); - _tables.Add(new Schema {Column = "Stack Count", Width = 12}); - _tables.Add(new Schema {Column = "Stack ", Width = 12}); - _tables.Add(new Schema {Column = "Arity", Width = 5}); - _tables.Add(new Schema {Column = "Arity Peek", Width = 11}); - _tables.Add(new Schema {Column = "Type", Width = 12}); - _tables.Add(new Schema {Column = "Left | Right", Width = 10}); - _tables.Add(new Schema {Column = "RPN", Width = 20}); - _tables.Add(new Schema {Column = "Action", Width = 7}); + _tables.Add(new Schema("#", 3)); + _tables.Add(new Schema("Token")); + _tables.Add(new Schema("Stack Count")); + _tables.Add(new Schema("Stack")); + _tables.Add(new Schema("Arity")); + _tables.Add(new Schema("Arity Peek")); + _tables.Add(new Schema("Type")); + _tables.Add(new Schema("Left | Right")); + _tables.Add(new Schema("RPN")); + _tables.Add(new Schema("Action")); } string action = string.Empty; @@ -274,9 +275,9 @@ public Token[] ShuntYard(List tokens) if (_dataStore.DebugMode) { _arityTables = new Tables(new Config { Title = "Arity", Format = _dataStore.DefaultFormat }); - _arityTables.Add(new Schema {Column = "#", Width = 3}); - _arityTables.Add(new Schema {Column = "Token", Width = 10}); - _arityTables.Add(new Schema {Column = "Arity", Width = 5}); + _arityTables.Add(new Schema("#", 3)); + _arityTables.Add(new Schema("Token")); + _arityTables.Add(new Schema("Arity")); } for (int i = 0; i < _output.Count; i++) @@ -294,7 +295,7 @@ public Token[] ShuntYard(List tokens) Function function = _dataStore.Functions[token.Value]; //See if we can apply casting //Cast sum to total if it has more than the possible arguments since thats what the user probably wanted - if (token.Value == "sum" && token.Arguments > function.MinArguments) + if (token.Value == "sum" && token.Arguments > function.MaxArguments) { Write("Casting sum to total since it exceeds max arguments for sum"); _output[i] = new Token("total", token.Arguments, RPN.Type.Function); @@ -302,7 +303,10 @@ public Token[] ShuntYard(List tokens) //The function has an incorrect number of arguments! else if (function.MinArguments > token.Arguments || token.Arguments > function.MaxArguments) { - throw new InvalidOperationException($"The function {token.Value} expected between {function.MinArguments} to {function.MaxArguments} arguments but has received {token.Arguments} instead."); + if (function.Description is null) { + throw new InvalidOperationException($"The function {token.Value} expected between {function.MinArguments} to {function.MaxArguments} arguments but has received {token.Arguments} instead."); + } + throw new InvalidOperationException($"The function {token.Value} expected between {function.MinArguments} to {function.MaxArguments} arguments but has received {token.Arguments} instead.The help for the function is displayed below:\n{function.Description.ToString()}"); } } @@ -499,7 +503,7 @@ private void Dump() void Write(string message) { - Logger?.Invoke(this, message); + logger.Log(Channels.Debug, message); } } } diff --git a/AbMath/Calculator/Simplifications/Addition.cs b/AbMath/Calculator/Simplifications/Addition.cs new file mode 100644 index 0000000..b3e8b1a --- /dev/null +++ b/AbMath/Calculator/Simplifications/Addition.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Addition + { + public static bool setRule(RPN.Node node) + { + return node.IsAddition(); + } + + public static bool AdditionToMultiplicationRunnable(RPN.Node node) + { + return node[0].Matches(node[1]); + } + + public static RPN.Node AdditionToMultiplication(RPN.Node node) + { + RPN.Node multiply = new RPN.Node(new[] { node[0], new RPN.Node(2) }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiply; + } + + public static bool ZeroAdditionRunnable(RPN.Node node) + { + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && (node[0].IsNumber(0) || node[1].IsNumber(0)); + } + + public static RPN.Node ZeroAddition(RPN.Node node) + { + if (node[0].IsNumber(0)) + { + return node[1]; + } + + return node[0]; + } + + public static bool AdditionSwapRunnable(RPN.Node node) + { + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && node[1].IsMultiplication() && + node[1, 1].IsNumber(-1); + } + + public static RPN.Node AdditionSwap(RPN.Node node) + { + node[1, 1].Replace(1); + node.Swap(0, 1); + node.Replace(new RPN.Token("-", 2, RPN.Type.Operator)); + return node; + } + + public static bool SimpleCoefficientRunnable(RPN.Node node) + { + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && node[1].IsMultiplication() && + node[1,1].IsNumber() && node[1,0].Matches(node[0]); + } + + public static RPN.Node SimpleCoefficient(RPN.Node node) + { + node[0].Remove(new RPN.Node(0)); + node[1].Replace(node[1, 1], new RPN.Node(node[1,1].GetNumber() + 1)); + return node; + } + + public static bool ComplexCoefficientRunnable(RPN.Node node) + { + return node[0].IsMultiplication() && node[1].IsMultiplication() && + node[0, 1].IsNumber() && node[1, 1].IsNumber() && + node[0, 0].Matches(node[1, 0]); + } + + public static RPN.Node ComplexCoefficient(RPN.Node node) + { + double sum = (node.Children[0].Children[1].GetNumber() + + node.Children[1].Children[1].GetNumber()); + node.Children[1].Replace(node.Children[1].Children[1], new RPN.Node(sum)); + node[0].Remove(new RPN.Node(0)); + return node; + } + + public static bool AdditionToSubtractionRuleOneRunnable(RPN.Node node) + { + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && node[0].IsMultiplication() && + node[0, 1].IsNumber(-1); + } + + public static RPN.Node AdditionToSubtractionRuleOne(RPN.Node node) + { + node.Replace("-"); + node[0].Replace(node[0,1], new RPN.Node(1)); + return node; + } + + public static bool AdditionToSubtractionRuleTwoRunnable(RPN.Node node) + { + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && node[0].IsLessThanNumber(0) && + node[1].IsMultiplication(); + } + + public static RPN.Node AdditionToSubtractionRuleTwo(RPN.Node node) + { + node.Replace("-"); + node.Replace(node[0], new RPN.Node(System.Math.Abs(node[0].GetNumber()))); + return node; + } + + public static bool ComplexNodeAdditionRunnable(RPN.Node node) + { + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && node[0].IsSubtraction() && + node[1].Matches(node[0, 1]); + } + + public static RPN.Node ComplexNodeAddition(RPN.Node node) + { + node[0].Replace(node[0, 1], new RPN.Node(0)); + RPN.Node multiplication = new RPN.Node(new[] { node[1], new RPN.Node(2) }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return node; + } + + public static bool DivisionAdditionRunnable(RPN.Node node) + { + return node[0].IsDivision() && node[1].IsDivision() && node[0, 0].Matches(node[1, 0]); + } + + public static RPN.Node DivisionAddition(RPN.Node node) + { + RPN.Node addition = new RPN.Node(new RPN.Node[] {node[0,1], node[1,1]}, new RPN.Token("+", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new RPN.Node[] {node[0,0], addition}, new RPN.Token("/",2,RPN.Type.Operator)); + return division; + } + } +} diff --git a/AbMath/Calculator/Simplifications/Division.cs b/AbMath/Calculator/Simplifications/Division.cs new file mode 100644 index 0000000..7e82941 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Division.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Division + { + public static bool setRule(RPN.Node node) + { + return node.IsDivision(); + } + + public static bool DivisionByZeroRunnable(RPN.Node node) + { + return node[0].IsNumber(0); + } + + public static RPN.Node DivisionByZero(RPN.Node node) + { + return new RPN.Node(double.NaN); + } + + public static bool DivisionByOneRunnable(RPN.Node node) + { + return node[0].IsNumber(1); + } + + public static RPN.Node DivisionByOne(RPN.Node node) + { + return node[1]; + } + + public static bool GCDRunnable(RPN.Node node) + { + return node.Children[0].IsInteger() && node.Children[1].IsInteger(); + } + + public static RPN.Node GCD(RPN.Node node) + { + double num1 = node.Children[0].GetNumber(); + double num2 = node.Children[1].GetNumber(); + double gcd = RPN.DoFunctions.Gcd(new double[] { num1, num2 }); + + node.Replace(node.Children[0], new RPN.Node((num1 / gcd))); + node.Replace(node.Children[1], new RPN.Node((num2 / gcd))); + return node; + } + + public static bool DivisionFlipRunnable(RPN.Node node) + { + return node.Children[0].IsDivision() && node.Children[1].IsDivision(); + } + + public static RPN.Node DivisionFlip(RPN.Node node) + { + RPN.Node[] numerator = { node.Children[0].Children[1], node.Children[1].Children[1] }; + RPN.Node[] denominator = { node.Children[0].Children[0], node.Children[1].Children[0] }; + + RPN.Node top = new RPN.Node(new[] { denominator[0], numerator[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node bottom = new RPN.Node(new[] { denominator[1], numerator[0] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new[] { bottom, top }, new RPN.Token("/", 2, RPN.Type.Operator)); + return division; + } + + public static bool DivisionFlipTwoRunnable(RPN.Node node) + { + return node[1].IsDivision(); + } + + public static RPN.Node DivisionFlipTwo(RPN.Node node) + { + RPN.Node numerator = node[1, 1]; + RPN.Node denominator = new RPN.Node(new[] { node[0], node[1, 0] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new[] { denominator, numerator }, + new RPN.Token("/", 2, RPN.Type.Operator)); + return division; + } + + public static bool DivisionCancelingRunnable(RPN.Node node) + { + return node[1].IsMultiplication() && (node[0].IsNumberOrConstant()) && node[0].Matches(node[1, 1]) && !node[1, 1].IsNumber(0); + } + + public static RPN.Node DivisionCanceling(RPN.Node node) + { + return node[1, 0]; + } + + public static bool PowerReductionRunnable(RPN.Node node) + { + return node[0].IsExponent() && node[1].IsExponent() && node[0, 0].IsInteger() && node[1, 0].IsInteger() && node[0, 1].Matches(node[1, 1]); + } + + public static RPN.Node PowerReduction(RPN.Node node) + { + int reduction = System.Math.Min((int)node[0, 0].GetNumber(), (int)node[1, 0].GetNumber()) - 1; + node[0, 0].Replace(node[0, 0].GetNumber() - reduction); + node[1, 0].Replace(node[1, 0].GetNumber() - reduction); + return node; + } + + //f(x)!/f(x)! -> 1 + public static bool FactorialCancellationRunnable(RPN.Node node) + { + return node[0].IsOperator("!") && node[1].IsOperator("!") && node[0,0].Matches(node[1,0]) && !node[0,0].ContainsDomainViolation(); + } + + public static RPN.Node FactorialCancellation(RPN.Node node) + { + return new RPN.Node(1); + } + + //[f(x)(x!)]/x! -> f(x) + public static bool FactorialRemovedRunnable(RPN.Node node) + { + return node[1].IsMultiplication() && node[1, 0].IsOperator("!") && node[0].Matches(node[1, 0]); + } + + public static RPN.Node FactorialRemoved(RPN.Node node) + { + return node[1, 1]; + } + } +} diff --git a/AbMath/Calculator/Simplifications/Exponent.cs b/AbMath/Calculator/Simplifications/Exponent.cs new file mode 100644 index 0000000..15ff2f8 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Exponent.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Exponent + { + + public static bool setRule(RPN.Node node) + { + return node.IsExponent(); + } + + public static bool functionRaisedToOneRunnable(RPN.Node node) + { + return node[0].IsNumber(1); + } + + public static RPN.Node functionRaisedToOne(RPN.Node node) + { + return node[1]; + } + + public static bool functionRaisedToZeroRunnable(RPN.Node node) + { + return node[0].IsNumber(0); + } + + public static RPN.Node functionRaisedToZero(RPN.Node node) + { + return new RPN.Node(1); + } + + public static bool zeroRaisedToConstantRunnable(RPN.Node node) + { + return node[1].IsNumber(0) && node[0].IsGreaterThanNumber(0); + } + + public static RPN.Node zeroRaisedToConstant(RPN.Node node) + { + return new RPN.Node(0); + } + + public static bool oneRaisedToFunctionRunnable(RPN.Node node) + { + return node[1].IsNumber(1); + } + + public static RPN.Node oneRaisedToFunction(RPN.Node node) + { + return new RPN.Node(1); + } + + public static bool toDivisionRunnable(RPN.Node node) + { + return node[0].IsLessThanNumber(0); + } + + public static RPN.Node toDivision(RPN.Node node) + { + RPN.Node exponent = new RPN.Node(new[] { new RPN.Node(-1 * node[0].GetNumber()), node[1].Clone() }, + new RPN.Token("^", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new[] { exponent, new RPN.Node(1) }, + new RPN.Token("/", 2, RPN.Type.Operator)); + return division; + } + + public static bool toSqrtRunnable(RPN.Node node) + { + return node[0].IsNumber(0.5) || (node[0].IsDivision() && node[0, 0].IsNumber(2) && node[0,1].IsNumber(1)); + } + + public static RPN.Node toSqrt(RPN.Node node) + { + return new RPN.Node(new[] { node[1] }, new RPN.Token("sqrt", 1, RPN.Type.Function)); + } + + public static bool ExponentToExponentRunnable(RPN.Node node) + { + return node[1].IsExponent(); + } + + public static RPN.Node ExponentToExponent(RPN.Node node) + { + RPN.Node multiply; + + if (node[0].IsNumber() && node[1, 0].IsNumber()) + { + multiply = new RPN.Node(node[0].GetNumber() * node[1, 0].GetNumber()); + } + else + { + multiply = new RPN.Node(new[] { node[0].Clone(), node[1, 0].Clone() }, + new RPN.Token("*", 2, RPN.Type.Operator)); + } + + RPN.Node exponent = new RPN.Node(new[] { multiply, node[1, 1].Clone() }, + new RPN.Token("^", 2, RPN.Type.Operator)); + return exponent; + } + + public static bool ConstantRaisedToConstantRunnable(RPN.Node node) + { + return node[0].IsInteger() && node[1].IsInteger(); + } + + public static RPN.Node ConstantRaisedToConstant(RPN.Node node) + { + return new RPN.Node( Math.Pow( node[1].GetNumber() , node[0].GetNumber() ) ); + } + + public static bool NegativeConstantRaisedToAPowerOfTwoRunnable(RPN.Node node) + { + return node[0].IsNumberOrConstant() && node[1].IsLessThanNumber(0) && node[0].GetNumber() % 2 == 0; + } + + public static RPN.Node NegativeConstantRaisedToAPowerOfTwo(RPN.Node node) + { + node.Replace(node[1], new RPN.Node(-1 * node[1].GetNumber())); + return node; + } + + public static bool AbsRaisedToPowerofTwoRunnable(RPN.Node node) + { + return node[0].IsNumber(2) && node[1].IsFunction("abs"); + } + + public static RPN.Node AbsRaisedToPowerofTwo(RPN.Node node) + { + node.Replace(node[1], node[1, 0]); + return node; + } + + } +} diff --git a/AbMath/Calculator/Simplifications/Integrate.cs b/AbMath/Calculator/Simplifications/Integrate.cs new file mode 100644 index 0000000..2a3d2e1 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Integrate.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Integrate + { + private static readonly RPN.Token _integrate = new RPN.Token("integrate", 4, RPN.Type.Function); + public static bool setUp(RPN.Node node) + { + return node.IsFunction("integrate"); + } + + public static bool PropagationRunnable(RPN.Node node) + { + return node.Children.Count == 4 && (node[3].IsAddition() || node[3].IsSubtraction()); + } + + public static RPN.Node Propagation(RPN.Node node) + { + RPN.Token addToken = new RPN.Token("+", 2, RPN.Type.Operator); + + RPN.Node integral = new RPN.Node(new RPN.Node[] { node[0].Clone(), node[1].Clone(), node[2].Clone(), node[3, 1].Clone() }, _integrate); + node.Replace(node[3], node[3, 0]); //This saves a simplification step later + RPN.Node addition = new RPN.Node(new RPN.Node[] { integral, node.Clone() }, addToken); + + return addition; + } + + public static bool ConstantsRunnable(RPN.Node node) + { + return node[3].IsNumberOrConstant() || !node[3].Contains(node[2]); + } + + public static RPN.Node Constants(RPN.Node node) + { + RPN.Node subtraction = new RPN.Node(new RPN.Node[] {node[1], node[0]}, new RPN.Token("-",2,RPN.Type.Operator)); + RPN.Node multiplication = new RPN.Node(new RPN.Node[] {subtraction, node[3]}, new RPN.Token("*",2, RPN.Type.Operator)); + return multiplication; + } + + public static bool CoefficientRunnable(RPN.Node node) + { + return node[3].IsMultiplication() && (node[3, 1].IsNumberOrConstant() || !node[3, 1].Contains(node[2])); + } + + public static RPN.Node Coefficient(RPN.Node node) + { + RPN.Node coefficient = node[3, 1].Clone(); + RPN.Node integral = new RPN.Node(new RPN.Node[] { node[0].Clone(), node[1].Clone(), node[2].Clone(), node[3, 0].Clone() }, _integrate); + RPN.Node multiplication = new RPN.Node(new RPN.Node[] {integral, coefficient}, new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool SingleVariableRunnable(RPN.Node node) + { + return node[3].IsVariable() && node[3].IsVariable(node[2]); + } + + public static RPN.Node SingleVariable(RPN.Node node) + { + RPN.Node end = new RPN.Node(new RPN.Node[] { new RPN.Node(2), node[0] }, new RPN.Token("^", 2, RPN.Type.Operator)); + RPN.Node start = new RPN.Node(new RPN.Node[] { new RPN.Node(2), node[1] }, new RPN.Token("^", 2, RPN.Type.Operator)); + RPN.Node subtraction = new RPN.Node(new RPN.Node[] {start, end}, new RPN.Token("-", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new RPN.Node[] {new RPN.Node(2), subtraction}, new RPN.Token("/", 2, RPN.Type.Operator)); + return division; + } + } +} diff --git a/AbMath/Calculator/Simplifications/Log.cs b/AbMath/Calculator/Simplifications/Log.cs new file mode 100644 index 0000000..32e1246 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Log.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AbMath.Calculator; + +namespace AbMath.Calculator.Simplifications +{ + public static class Log + { + public static bool LogOneRunnable(RPN.Node node) + { + return node.IsLog() && node.Children[0].IsNumber(1); + } + + public static RPN.Node LogOne(RPN.Node node) + { + return new RPN.Node(0); + } + + public static bool LogIdentitcalRunnable(RPN.Node node) + { + return node.IsLog() && node.ChildrenAreIdentical(); + } + + public static RPN.Node LogIdentitcal(RPN.Node node) + { + return new RPN.Node(1); + } + + public static bool LogPowerRunnable(RPN.Node node) + { + return node.IsExponent() && node.Children[0].IsLog() && node.Children[0].Children[1].Matches(node.Children[1]); + } + + public static RPN.Node LogPower(RPN.Node node) + { + return node.Children[0].Children[0]; + } + + public static bool LogExponentExpansionRunnable(RPN.Node node) + { + return node.IsLog() && node.Children[0].IsExponent() && !node.Children[0].Children[1].IsVariable(); + } + + public static RPN.Node LogExponentExpansion(RPN.Node node) + { + RPN.Node exponent = node.Children[0]; + RPN.Node baseNode = exponent.Children[1]; + RPN.Node power = exponent.Children[0]; + + RPN.Node log = new RPN.Node(new[] { baseNode.Clone(), node.Children[1] }, new RPN.Token("log", 2, RPN.Type.Function)); + return new RPN.Node(new[] { log, power }, new RPN.Token("*", 2, RPN.Type.Operator)); + } + + public static bool LogToLnRunnable(RPN.Node node) + { + return node.IsLog() && node[1].IsConstant("e"); + } + + public static RPN.Node LogToLn(RPN.Node node) + { + return new RPN.Node(new RPN.Node[] { node[0] }, new RPN.Token("ln", 1, RPN.Type.Function)); + } + + public static bool LnToLogRunnable(RPN.Node node) + { + return node.IsLn(); + } + + public static RPN.Node LnToLog(RPN.Node node) + { + RPN.Node e = new RPN.Node(new RPN.Token("e", 0, RPN.Type.Function)); + RPN.Node log = new RPN.Node(new RPN.Node[] { node[0].Clone(), e }, new RPN.Token("log", 2, RPN.Type.Function)); + return log; + } + + public static bool LogSummationRunnable(RPN.Node node) + { + return node.IsAddition() && node.Children[0].IsLog() && + node.Children[1].IsLog() && + node.Children[0].Children[1].Matches(node.Children[1].Children[1]); + } + + public static RPN.Node LogSummation(RPN.Node node) + { + RPN.Node parameter = new RPN.Node(new[] { node.Children[0].Children[0], node.Children[1].Children[0] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node baseNode = node.Children[0].Children[1]; + RPN.Node log = new RPN.Node(new[] { parameter, baseNode }, + new RPN.Token("log", 2, RPN.Type.Function)); + return log; + } + + public static bool LogSubtractionRunnable(RPN.Node node) + { + return node.IsSubtraction() && node.Children[0].IsLog() && + node.Children[1].IsLog() && + node.Children[0].Children[1].Matches(node.Children[1].Children[1]); + } + + public static RPN.Node LogSubtraction(RPN.Node node) + { + RPN.Node parameter = new RPN.Node(new[] { node.Children[0].Children[0], node.Children[1].Children[0] }, + new RPN.Token("/", 2, RPN.Type.Operator)); + RPN.Node baseNode = node.Children[0].Children[1]; + RPN.Node log = new RPN.Node(new[] { parameter, baseNode }, + new RPN.Token("log", 2, RPN.Type.Function)); + return log; + } + + public static bool LnSummationRunnable(RPN.Node node) + { + return node.IsAddition() && node.Children[0].IsLn() && node.Children[1].IsLn(); + } + + public static RPN.Node LnSummation(RPN.Node node) + { + RPN.Node parameter = new RPN.Node(new[] { node.Children[0].Children[0], node.Children[1].Children[0] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node ln = new RPN.Node(new[] { parameter }, + new RPN.Token("ln", 1, RPN.Type.Function)); + return ln; + } + + public static bool LnSubtractionRunnable(RPN.Node node) + { + return node.IsSubtraction() && node.Children[0].IsLn() && node.Children[1].IsLn(); + } + + public static RPN.Node LnSubtraction(RPN.Node node) + { + RPN.Node parameter = new RPN.Node(new[] { node.Children[0].Children[0], node.Children[1].Children[0] }, + new RPN.Token("/", 2, RPN.Type.Operator)); + RPN.Node ln = new RPN.Node(new[] { parameter }, + new RPN.Token("ln", 1, RPN.Type.Function)); + return ln; + } + + public static bool LnPowerRuleRunnable(RPN.Node node) + { + return node.IsLn() && node[0].IsExponent() && !node[0,0].IsVariable(); + } + + public static RPN.Node LnPowerRule(RPN.Node node) + { + RPN.Node exponent = node.Children[0]; + RPN.Node power = exponent.Children[0]; + + RPN.Node log = new RPN.Node(new[] { exponent.Children[1] }, + new RPN.Token("ln", 1, RPN.Type.Function)); + RPN.Node multiply = new RPN.Node(new[] { log, power }, new RPN.Token("*", 2, RPN.Type.Operator)); + return multiply; + } + } +} diff --git a/AbMath/Calculator/Simplifications/Misc.cs b/AbMath/Calculator/Simplifications/Misc.cs new file mode 100644 index 0000000..abe3884 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Misc.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public class Misc + { + public static bool ZeroFactorialRunnable(RPN.Node node) + { + return node.IsOperator("!") && ( node[0].IsNumber(0) || node[0].IsNumber(1) ); + } + + public static RPN.Node ZeroFactorial(RPN.Node node) + { + return new RPN.Node(1); + } + } +} diff --git a/AbMath/Calculator/Simplifications/Multiplication.cs b/AbMath/Calculator/Simplifications/Multiplication.cs new file mode 100644 index 0000000..7eaac76 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Multiplication.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Multiplication + { + public static bool setRule(RPN.Node node) + { + return node.IsMultiplication(); + } + + public static bool multiplicationToExponentRunnable(RPN.Node node) + { + return node.ChildrenAreIdentical(); + } + + public static RPN.Node multiplicationToExponent(RPN.Node node) + { + return new RPN.Node(new[] { new RPN.Node(2), node[0] }, new RPN.Token("^", 2, RPN.Type.Operator)); + } + + public static bool multiplicationByOneRunnable(RPN.Node node) + { + return node.Children[0].IsNumber(1) || node.Children[1].IsNumber(1); + } + + public static RPN.Node multiplicationByOne(RPN.Node node) + { + return node.Children[1].IsNumber(1) ? node.Children[0] : node.Children[1]; + } + + public static bool multiplicationByZeroRunnable(RPN.Node node) + { + return (node.Children[1].IsNumber(0) || node.Children[0].IsNumber(0)) && !node.ContainsDomainViolation(); + } + + public static RPN.Node multiplicationByZero(RPN.Node node) + { + return new RPN.Node(0); + } + + public static bool increaseExponentRunnable(RPN.Node node) + { + /* * + * | f(x) + * | ^ + * | + * | > Number + * | > f(x) + */ + return node[1].IsExponent() && node[1,0].IsNumber() && node[0].Matches(node[1,1]); + } + + public static RPN.Node increaseExponent(RPN.Node node) + { + node.Replace(node[0], new RPN.Node(1)); + node.Replace(node[1,0], + new RPN.Node(node[1,0].GetNumber() + 1)); + return node; + } + + public static bool increaseExponentTwoRunnable(RPN.Node node) + { + /** + * * + * | ^ + * | c > 0 + * | f(x) + * | * + * f(x) + */ + return node[0].IsExponent() && node[1].IsMultiplication() && + node[0,0].IsGreaterThanNumber(0) && node[1,0].Matches(node[0,1]); + } + + public static RPN.Node increaseExponentTwo(RPN.Node node) + { + RPN.Node temp = node.Children[0].Children[0]; + temp.Replace(temp.GetNumber() + 1); + node.Children[1].Children[0].Remove(new RPN.Node(1)); + return node; + } + + public static bool increaseExponentThreeRunnable(RPN.Node node) + { + /** + * * + * | > ^ + * | ? + * | f(x) + * | > * + * ? + * f + */ + return node.Children[0].IsExponent() && node.Children[1].IsMultiplication() && + node.Children[0].Children[1].Matches(node.Children[1]); + } + + public static RPN.Node increaseExponentThree(RPN.Node node) + { + RPN.Node temp = node.Children[0].Children[0]; + temp.Replace(temp.GetNumber() + 1); + node.Children[1].Remove(new RPN.Node(1)); + return node; + } + + public static bool dualNodeMultiplicationRunnable(RPN.Node node) + { + return node[1].IsNumber() && node[0].IsMultiplication() && node[0,1].IsNumber() && !node[0,0].IsNumber(); + } + + public static RPN.Node dualNodeMultiplication(RPN.Node node) + { + double num1 = node[0,1].GetNumber(); + double num2 = node[1].GetNumber(); + + node[0].Replace(node[0, 1], new RPN.Node(1)); + node.Replace(node[1], new RPN.Node(num1 * num2)); + return node; + } + + public static bool multiplicationByOneComplexRunnable(RPN.Node node) + { + return node[0].IsNumber(1) || node[1].IsNumber(1); + } + + public static RPN.Node multiplicationByOneComplex(RPN.Node node) + { + return node[1].IsNumber(1) ? node.Children[0] : node.Children[1]; + } + + public static bool expressionTimesDivisionRunnable(RPN.Node node) + { + return node[0].IsDivision() ^ node[1].IsDivision(); + } + + public static RPN.Node expressionTimesDivision(RPN.Node node) + { + RPN.Node division; + RPN.Node expression; + if (node.Children[0].IsDivision()) + { + division = node.Children[0]; + expression = node.Children[1]; + } + else + { + division = node.Children[1]; + expression = node.Children[0]; + } + + RPN.Node numerator = division.Children[1]; + RPN.Node multiply = new RPN.Node(new[] { numerator.Clone(), expression.Clone() }, + new RPN.Token("*", 2, RPN.Type.Operator)); + numerator.Remove(multiply); + expression.Remove(new RPN.Node(1)); + return node; + } + + public static bool divisionTimesDivisionRunnable(RPN.Node node) + { + return node[0].IsDivision() && node[1].IsDivision(); + } + + public static RPN.Node divisionTimesDivision(RPN.Node node) + { + RPN.Node[] numerator = { node.Children[0].Children[1], node.Children[1].Children[1] }; + RPN.Node[] denominator = { node.Children[0].Children[0], node.Children[1].Children[0] }; + + RPN.Token multiply = new RPN.Token("*", 2, RPN.Type.Operator); + RPN.Token div = new RPN.Token("/", 2, RPN.Type.Operator); + + RPN.Node top = new RPN.Node(numerator, multiply); + RPN.Node bottom = new RPN.Node(denominator, multiply); + RPN.Node division = new RPN.Node(new[] { bottom, top }, div); + + node.Children[0].Remove(division); + node.Children[1].Remove(new RPN.Node(1)); + return node; + } + + + public static bool negativeTimesnegativeRunnable(RPN.Node node) + { + return node[0].IsLessThanNumber(0) && node[1].IsLessThanNumber(0); + } + + public static RPN.Node negativeTimesnegative(RPN.Node node) + { + node[0].Remove(new RPN.Node(node[0].GetNumber() * -1)); + node[1].Remove(new RPN.Node(node[1].GetNumber() * -1)); + return node; + } + + public static bool complexNegativeNegativeRunnable(RPN.Node node) + { + return node[0].IsMultiplication() && node[0, 1].IsLessThanNumber(0) && + node[1].IsLessThanNumber(0); + } + + public static RPN.Node complexNegativeNegative(RPN.Node node) + { + node.Replace(node[0, 1], new RPN.Node(System.Math.Abs(node[0, 1].GetNumber()))); + node.Replace(node[1], new RPN.Node(System.Math.Abs(node[1].GetNumber()))); + return node; + } + + public static bool negativeTimesConstantRunnable(RPN.Node node) + { + return node[0].IsNumber(-1) && node[1].IsNumber(); + } + + public static RPN.Node negativeTimesConstant(RPN.Node node) + { + node.Replace(node[0], new RPN.Node(1)); + node.Replace(node[1], new RPN.Node(node[1].GetNumber() * -1)); + return node; + } + + public static bool constantTimesNegativeRunnable(RPN.Node node) + { + return node[0].IsNumber() && node[1].IsNumber(-1); + } + + public static RPN.Node constantTimesNegative(RPN.Node node) + { + node.Replace(node[1], new RPN.Node(1)); + node.Replace(node[0], new RPN.Node(node[0].GetNumber() * -1)); + return node; + } + + public static bool negativeOneDistributedRunnable(RPN.Node node) + { + return node[0].IsSubtraction() && node[1].IsNumber(-1); + } + + public static RPN.Node negativeOneDistributed(RPN.Node node) + { + node[0].Swap(0, 1); + node[1].Replace(1); + return node; + } + } +} \ No newline at end of file diff --git a/AbMath/Calculator/Simplifications/Sqrt.cs b/AbMath/Calculator/Simplifications/Sqrt.cs new file mode 100644 index 0000000..e1c4301 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Sqrt.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Sqrt + { + public static bool SqrtNegativeNumbersRunnable(RPN.Node node) + { + return node.IsSqrt() && node[0].IsLessThanNumber(0); + } + + public static RPN.Node SqrtNegativeNumbers(RPN.Node node) + { + return new RPN.Node(double.NaN); + } + + + public static bool SqrtToFuncRunnable(RPN.Node node) + { + return node.IsExponent() && node.Children[0].IsNumber(2) && node.Children[1].IsSqrt(); + } + + public static RPN.Node SqrtToFunc(RPN.Node node) + { + return node.Children[1].Children[0]; + } + + public static bool SqrtToAbsRunnable(RPN.Node node) + { + return node.IsSqrt() && node.Children[0].IsExponent() && node.Children[0].Children[0].IsNumber(2); + } + + public static RPN.Node SqrtToAbs(RPN.Node node) + { + return new RPN.Node(new[] { node.Children[0].Children[1] }, new RPN.Token("abs", 1, RPN.Type.Function)); + } + + public static bool SqrtPowerFourRunnable(RPN.Node node) + { + return node.IsSqrt() && node.Children[0].IsExponent() && + node.Children[0].Children[0].IsNumber() && + node.Children[0].Children[0].GetNumber() % 4 == 0; + } + + public static RPN.Node SqrtPowerFour(RPN.Node node) + { + return new RPN.Node(new[] {new RPN.Node(node.Children[0].Children[0].GetNumber() / 2), node.Children[0].Children[1]}, new RPN.Token("^", 2, RPN.Type.Operator)); + } + } + + + + +} diff --git a/AbMath/Calculator/Simplifications/Subtraction.cs b/AbMath/Calculator/Simplifications/Subtraction.cs new file mode 100644 index 0000000..2caf194 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Subtraction.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Subtraction + { + public static bool setRule(RPN.Node node) + { + return node.IsSubtraction(); + } + + public static bool SameFunctionRunnable(RPN.Node node) + { + return node.ChildrenAreIdentical() && !node.ContainsDomainViolation(); + } + + public static RPN.Node SameFunction(RPN.Node node) + { + return new RPN.Node(0); + } + + public static bool CoefficientOneReductionRunnable(RPN.Node node) + { + return node.Children[1].IsMultiplication() && node.Children[1].Children[1].IsNumber() && + node.Children[1].Children[0].Matches(node.Children[0]); + } + + public static RPN.Node CoefficientOneReduction(RPN.Node node) + { + node.Replace(node.Children[0], new RPN.Node(0)); + node.Children[1].Replace(node.Children[1].Children[1], + new RPN.Node(node.Children[1].Children[1].GetNumber() - 1)); + return node; + } + + public static bool SubtractionByZeroRunnable(RPN.Node node) + { + return node.Children[0].IsNumber(0); + } + + public static RPN.Node SubtractionByZero(RPN.Node node) + { + return node.Children[1]; + } + + public static bool ZeroSubtractedByFunctionRunnable(RPN.Node node) + { + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && node[1].IsNumber(0); + } + + public static RPN.Node ZeroSubtractedByFunction(RPN.Node node) + { + return new RPN.Node(new[] { new RPN.Node(-1), node.Children[0] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + } + + public static bool SubtractionDivisionCommonDenominatorRunnable(RPN.Node node) + { + return node[0].IsDivision() && node[1].IsDivision() && node[0, 0].Matches(node[1, 0]); + } + + + public static RPN.Node SubtractionDivisionCommonDenominator(RPN.Node node) + { + RPN.Node subtraction = new RPN.Node(new[] { node[0, 1], node[1, 1] }, + new RPN.Token("-", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new[] { node[0, 0], subtraction }, + new RPN.Token("/", 2, RPN.Type.Operator)); + return division; + } + + public static bool CoefficientReductionRunnable(RPN.Node node) + { + return (node[0].IsMultiplication() && node[1].IsMultiplication()) && + node.Children[0].Children[1].IsNumber() && node.Children[1].Children[1].IsNumber() && + node.Children[0].Children[0].Matches(node.Children[1].Children[0]); + } + + public static RPN.Node CoefficientReduction(RPN.Node node) + { + double coefficient = node.Children[1].Children[1].GetNumber() - node.Children[0].Children[1].GetNumber(); + node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(0)); + node.Children[1].Replace(node.Children[1].Children[1], new RPN.Node(coefficient)); + return node; + } + + public static bool ConstantToAdditionRunnable(RPN.Node node) + { + return node[0].IsNumber() && node[0].IsLessThanNumber(0); + } + + public static RPN.Node ConstantToAddition(RPN.Node node) + { + RPN.Node addition = new RPN.Node(new[] { new RPN.Node(node[0].GetNumber() * -1), node[1] }, new RPN.Token("+", 2, RPN.Type.Operator)); + return addition; + } + + public static bool FunctionToAdditionRunnable(RPN.Node node) + { + //(cos(x)^2)-(-1*(sin(x)^2)) + //(cos(x)^2)-(-2*(sin(x)^2)) + //((-2*(cos(x)^2))+(2*(sin(x)^2))) + return !(node[0].IsMultiplication() && node[1].IsMultiplication()) && node[0].IsMultiplication() && + node[0, 1].IsLessThanNumber(0); + } + + public static RPN.Node FunctionToAddition(RPN.Node node) + { + node[0, 1].Replace(node[0, 1].GetNumber() * -1); + node.Replace(new RPN.Token("+", 2, RPN.Type.Operator)); + return node; + } + + public static bool DistributiveSimpleRunnable(RPN.Node node) + { + return node[0].IsSubtraction(); + } + + public static RPN.Node DistributiveSimple(RPN.Node node) + { + //f(x) - (g(x) - h(x)) -> f(x) - g(x) + h(x) -> (f(x) + h(x)) - g(x) + //We want to do this automatically + RPN.Node add = new RPN.Node(new RPN.Node[] {node[0, 0], node[1]}, new RPN.Token("+", 2, RPN.Type.Operator)); + RPN.Node sub = new RPN.Node(new RPN.Node[] {node[0,1], add}, new RPN.Token("-", 2, RPN.Type.Operator)); + return sub; + } + } +} diff --git a/AbMath/Calculator/Simplifications/Sum.cs b/AbMath/Calculator/Simplifications/Sum.cs new file mode 100644 index 0000000..a1e9b0b --- /dev/null +++ b/AbMath/Calculator/Simplifications/Sum.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Sum + { + private static readonly RPN.Token _sum = new RPN.Token("sum", 4, RPN.Type.Function); + + public static bool setUp(RPN.Node node) + { + return node.IsFunction("sum"); + } + + /// + /// Code from: https://rosettacode.org/wiki/Bernoulli_numbers + /// + /// + /// + public static RPN.Node getBernoulliNumber(int n) + { + if (n < 0) + { + throw new ArgumentOutOfRangeException("The Bernoulli number is not defined for values below zero."); + } + + BigInteger f; + BigInteger[] nu = new BigInteger[n + 1], + de = new BigInteger[n + 1]; + for (int m = 0; m <= n; m++) + { + nu[m] = 1; de[m] = m + 1; + for (int j = m; j > 0; j--) + if ((f = BigInteger.GreatestCommonDivisor( + nu[j - 1] = j * (de[j] * nu[j - 1] - de[j - 1] * nu[j]), + de[j - 1] *= de[j])) != BigInteger.One) + { nu[j - 1] /= f; de[j - 1] /= f; } + } + + if (n == 1) + { + nu[0] = -1; + } + + return new RPN.Node(new[] {new RPN.Node((double) de[0]), new RPN.Node((double) nu[0])}, + new RPN.Token("/", 2, RPN.Type.Operator)); + } + + public static bool PropagationRunnable(RPN.Node node) + { + return node[3].IsAddition() || node[3].IsSubtraction(); + } + + public static RPN.Node Propagation(RPN.Node node) + { + RPN.Token addToken = new RPN.Token("+", 2, RPN.Type.Operator); + + RPN.Node sum = new RPN.Node(new RPN.Node[] { node[0].Clone(), node[1].Clone(), node[2].Clone(), node[3, 1].Clone() }, _sum); + node.Replace(node[3], node[3,0]); //This saves a simplification step later + RPN.Node addition = new RPN.Node(new RPN.Node[] { sum, node.Clone() }, addToken); + + return addition; + } + + public static bool VariableRunnable(RPN.Node node) + { + return (node[1].IsNumber(0) || node[1].IsNumber(1)) && node[3].IsVariable() && node[3].Matches(node[2]); + } + + public static RPN.Node Variable(RPN.Node node) + { + RPN.Node one = new RPN.Node(1); + RPN.Node two = new RPN.Node(2); + + RPN.Node addition = new RPN.Node(new RPN.Node[] {node[0].Clone(), one}, new RPN.Token("+", 2, RPN.Type.Operator)); + RPN.Node multiplication = new RPN.Node(new RPN.Node[] {addition, node[0]}, new RPN.Token("*", 2, RPN.Type.Operator)); + RPN.Node division = new RPN.Node(new RPN.Node[] {two, multiplication}, new RPN.Token("/", 2, RPN.Type.Operator)); + return division; + } + + public static bool ConstantComplexRunnable(RPN.Node node) + { + return node[3].IsNumberOrConstant() || (node[3].IsVariable() && !node[3].Matches(node[2])) ; + } + + public static RPN.Node ConstantComplex(RPN.Node node) + { + RPN.Node subtraction = new RPN.Node(new RPN.Node[] {node[1], node[0]}, new RPN.Token("-", 2, RPN.Type.Operator)); + RPN.Node addition = new RPN.Node(new RPN.Node[] {subtraction, new RPN.Node(1)}, new RPN.Token("+", 2, RPN.Type.Operator)); + RPN.Node multiplication = new RPN.Node(new RPN.Node[] {node[3], addition}, new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool CoefficientRunnable(RPN.Node node) + { + return node[3].IsMultiplication() && node[3, 1].IsNumberOrConstant(); + } + + public static RPN.Node Coefficient(RPN.Node node) + { + RPN.Node sum = new RPN.Node(new RPN.Node[] {node[0].Clone(), node[1].Clone(), node[2].Clone(), node[3, 0]}, _sum); + RPN.Node multiplication = new RPN.Node(new RPN.Node[] {sum, node[3, 1] }, new RPN.Token("*", 2, RPN.Type.Operator)); + + return multiplication; + } + + public static bool CoefficientDivisionRunnable(RPN.Node node) + { + return node[3].IsDivision() && node[3, 0].IsNumberOrConstant(); + } + + public static RPN.Node CoefficientDivision(RPN.Node node) + { + RPN.Node sum = new RPN.Node(new RPN.Node[] { node[0].Clone(), node[1].Clone(), node[2].Clone(), node[3, 1] }, _sum); + RPN.Node division = new RPN.Node(new RPN.Node[] {node[3,0], sum}, new RPN.Token("/",2,RPN.Type.Operator)); + + return division; + + } + + public static bool PowerRunnable(RPN.Node node) + { + return ( node[1].IsInteger(1) || node[1].IsNumber(0) ) //start point must be 0 or 1 since 0^(c) = 0 when c > 0. + && node[3].IsExponent() + && node[3, 0].IsInteger() && + node[3, 0].IsGreaterThanNumber(0) && //ensures power is positive + node[3, 1].Matches(node[2]); + } + + public static RPN.Node Power(RPN.Node node) + { + RPN.Node power = node[3, 0].Clone(); + RPN.Node end = node[0].Clone(); + + RPN.Node one = new RPN.Node(1); + + RPN.Token _add = new RPN.Token("+", 2, RPN.Type.Operator); + RPN.Token _mul = new RPN.Token("*", 2, RPN.Type.Operator); + RPN.Token _div = new RPN.Token("/", 2, RPN.Type.Operator); + RPN.Token _exp = new RPN.Token("^", 2, RPN.Type.Operator); + RPN.Token _fac = new RPN.Token("!", 1, RPN.Type.Operator); + RPN.Token _total = new RPN.Token("total", (int)power.GetNumber(), RPN.Type.Function); + + RPN.Node total = new RPN.Node(_total); + + double max = power.GetNumber(); + + RPN.Node numeratorAddition = new RPN.Node(power.GetNumber() + 1); //(p + 1) + + for (int i = 0; i <= max; i++) + { + RPN.Node j = new RPN.Node(i); + RPN.Node subtraction = new RPN.Node(power.GetNumber() - j.GetNumber()); //(p - j) + RPN.Node addition = new RPN.Node(subtraction.GetNumber() + 1); //(p - j + 1) + RPN.Node exponent = new RPN.Node(new RPN.Node[] { addition.Clone(), end.Clone() }, _exp); //n^(p - j + 1) + + RPN.Node bernoulli = Sum.getBernoulliNumber(i); //B(j) + + + RPN.Node numerator = new RPN.Node(new RPN.Node[] { numeratorAddition.Clone() }, _fac); //(p + 1)! + + RPN.Node denominatorFactorial = new RPN.Node(new RPN.Node[] { addition.Clone() }, _fac); //(p - j + 1)! + RPN.Node jFactorial = new RPN.Node(new RPN.Node[] { j.Clone() }, _fac); //j! + RPN.Node denominator = new RPN.Node(new RPN.Node[] { denominatorFactorial, jFactorial }, _mul); // j! * (p - j + 1)! + + RPN.Node fraction = new RPN.Node(new RPN.Node[] { denominator, numerator }, _div); + + RPN.Node negativeOneExponent = new RPN.Node(new RPN.Node[] { j.Clone(), new RPN.Node(-1) }, _exp); //(-1)^j + + RPN.Node temp = new RPN.Node(new RPN.Node[] { fraction, negativeOneExponent }, _mul); + RPN.Node tempTwo = new RPN.Node(new RPN.Node[] { exponent, bernoulli }, _mul); + RPN.Node multiplication = new RPN.Node(new RPN.Node[] { tempTwo, temp }, _mul); + total.AddChild(multiplication); + } + + RPN.Node division = new RPN.Node(new RPN.Node[] { numeratorAddition.Clone(), total }, _div); + return division; + } + } +} diff --git a/AbMath/Calculator/Simplifications/Trig.cs b/AbMath/Calculator/Simplifications/Trig.cs new file mode 100644 index 0000000..2106e63 --- /dev/null +++ b/AbMath/Calculator/Simplifications/Trig.cs @@ -0,0 +1,274 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AbMath.Calculator.Simplifications +{ + public static class Trig + { + public static bool CosOverSinToCotRunnable(RPN.Node node) + { + return node.IsDivision() && node.Children[0].IsFunction("sin") && + node.Children[1].IsFunction("cos") && + node.Children[0].Children[0].Matches(node.Children[1].Children[0]); + } + + public static RPN.Node CosOverSinToCot(RPN.Node node) + { + RPN.Node cot = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("cot", 1, RPN.Type.Function)); + return cot; + } + + public static bool SinOverCosRunnable(RPN.Node node) + { + return node.IsDivision() && node.Children[0].IsFunction("cos") && + node.Children[1].IsFunction("sin") && + node.Children[0].Children[0].Matches(node.Children[1].Children[0]); + } + + public static RPN.Node SinOverCos(RPN.Node node) + { + RPN.Node tan = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("tan", 1, RPN.Type.Function)); + return tan; + } + + public static bool CosOverSinComplexRunnable(RPN.Node node) + { + return node.IsDivision() && node[1].IsMultiplication() && + node[0].IsFunction("sin") && node[1,0].IsFunction("cos") && + node[0,0].Matches(node[1,0,0]); + } + + public static RPN.Node CosOverSinComplex(RPN.Node node) + { + RPN.Node cot = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("cot", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { cot, node.Children[1].Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool SecUnderToCosRunnable(RPN.Node node) + { + return node.IsDivision() && node.Children[0].IsFunction("sec"); + } + + public static RPN.Node SecUnderToCos(RPN.Node node) + { + RPN.Node cos = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("cos", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { cos, node.Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool CscUnderToSinRunnable(RPN.Node node) + { + return node.IsDivision() && node[0].IsFunction("csc"); + } + + public static RPN.Node CscUnderToSin(RPN.Node node) + { + RPN.Node sin = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("sin", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { sin, node.Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool CotUnderToTanRunnable(RPN.Node node) + { + return node.IsDivision() && node[0].IsFunction("cot"); + } + + public static RPN.Node CotUnderToTan(RPN.Node node) + { + RPN.Node tan = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("tan", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { tan, node.Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + + public static bool CosUnderToSecRunnable(RPN.Node node) + { + return node.IsDivision() && node[0].IsFunction("cos"); + } + + public static RPN.Node CosUnderToSec(RPN.Node node) + { + RPN.Node sec = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("sec", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { sec, node.Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool SinUnderToCscRunnable(RPN.Node node) + { + return node.IsDivision() && node[0].IsFunction("sin"); + } + + public static RPN.Node SinUnderToCsc(RPN.Node node) + { + RPN.Node csc = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("csc", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { csc, node.Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool TanUnderToCotRunnable(RPN.Node node) + { + return node.IsDivision() && node.Children[0].IsFunction("tan"); + } + + public static RPN.Node TanUnderToCot(RPN.Node node) + { + RPN.Node cot = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("cot", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { cot, node.Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return node; + } + + public static bool CosEvenIdentityRunnable(RPN.Node node) + { + return node.IsFunction("cos") && node[0].IsMultiplication() && node[0,1].IsNumber(-1); + } + + public static RPN.Node CosEvenIdentity(RPN.Node node) + { + node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(1)); + return node; + } + + public static bool SecEvenIdentityRunnable(RPN.Node node) + { + return node.IsFunction("sec") && node[0].IsMultiplication() && node[0,1].IsNumber(-1); + } + + public static RPN.Node SecEvenIdentity(RPN.Node node) + { + node.Children[0].Replace(node.Children[0].Children[1], new RPN.Node(1)); + return node; + } + + public static bool SinOddIdentityRunnable(RPN.Node node) + { + return node.IsFunction("sin") && node[0].IsMultiplication() && node[0, 1].IsNumber(-1); + } + + public static RPN.Node SinOddIdentity(RPN.Node node) + { + RPN.Node cot = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("sin", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { cot, node.Children[0].Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool TanOddIdentityRunnable(RPN.Node node) + { + return node.IsFunction("tan") && node[0].IsMultiplication() && node[0, 1].IsNumber(-1); + } + + public static RPN.Node TanOddIdentity(RPN.Node node) + { + RPN.Node cot = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("tan", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { cot, node.Children[0].Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool CotOddIdentityRunnable(RPN.Node node) + { + return node.IsFunction("cot") && node[0].IsMultiplication() && node[0, 1].IsNumber(-1); + } + + public static RPN.Node CotOddIdentity(RPN.Node node) + { + RPN.Node cot = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("cot", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { cot, node.Children[0].Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + + public static bool CscOddIdentityRunnable(RPN.Node node) + { + return node.IsFunction("csc") && node[0].IsMultiplication() && node[0, 1].IsNumber(-1); + } + + public static RPN.Node CscOddIdentity(RPN.Node node) + { + RPN.Node csc = new RPN.Node(new[] { node.Children[0].Children[0] }, + new RPN.Token("csc", 1, RPN.Type.Function)); + RPN.Node multiplication = new RPN.Node(new[] { csc, node.Children[0].Children[1] }, + new RPN.Token("*", 2, RPN.Type.Operator)); + return multiplication; + } + public static bool TrigIdentitySinToCosRunnable(RPN.Node node) + { + return node.IsSubtraction() && node[0].IsExponent() && node[1].IsNumber(1) && node[0, 0].IsNumber(2) && + node[0, 1].IsFunction("sin"); + } + + public static RPN.Node TrigIdentitySinToCos(RPN.Node node) + { + RPN.Node cos = new RPN.Node(new[] { node[0, 1, 0] }, new RPN.Token("cos", 1, RPN.Type.Function)); + RPN.Node exponent = new RPN.Node(new[] { node[0, 0], cos }, new RPN.Token("^", 2, RPN.Type.Operator)); + return exponent; + } + + public static bool TrigIdentityCosToSinRunnable(RPN.Node node) + { + return node.IsSubtraction() && node[0].IsExponent() && node[1].IsNumber(1) && node[0, 0].IsNumber(2) && + node[0, 1].IsFunction("cos"); + } + + public static RPN.Node TrigIdentityCosToSin(RPN.Node node) + { + RPN.Node sin = new RPN.Node(new[] { node[0, 1, 0] }, new RPN.Token("sin", 1, RPN.Type.Function)); + RPN.Node exponent = new RPN.Node(new[] { node[0, 0], sin }, new RPN.Token("^", 2, RPN.Type.Operator)); + return exponent; + } + + public static bool TrigIdentitySinPlusCosRunnable(RPN.Node node) + { + return node.IsAddition() && + node[0].IsExponent() && + node[1].IsExponent() && + node[0,0].IsNumber(2) && + node[1,0].IsNumber(2) && + (node[0,1].IsFunction("cos") || node[0,1].IsFunction("sin")) && + (node[1,1].IsFunction("sin") || node[1,1].IsFunction("cos")) && + !node.ChildrenAreIdentical() && + !node.ContainsDomainViolation() && + node[0,1,0].Matches(node[1,1,0]); + } + + public static RPN.Node TrigIdentitySinPlusCos(RPN.Node node) + { + return new RPN.Node(1); + } + + public static bool CosOverSinToCotComplexRunnable(RPN.Node node) + { + return node.IsDivision() && node[0].IsMultiplication() && node[1].IsFunction("cos") && + node[0, 0].IsFunction("sin") && node[0, 0, 0].Matches(node[1, 0]); + } + + public static RPN.Node CosOverSinToCotComplex(RPN.Node node) + { + //cos(x)/[sin(x) * f(x)] -> cot(x)/f(x) is also implemented due to swapping rules. + RPN.Node cot = new RPN.Node(new[] { node[1, 0] }, new RPN.Token("cot", 1, RPN.Type.Function)); + RPN.Node division = new RPN.Node(new[] { node[0, 1], cot }, new RPN.Token("/", 2, RPN.Type.Operator)); + return division; + } + } +} diff --git a/AbMath/Calculator/Tokenizer.cs b/AbMath/Calculator/Tokenizer.cs index 68dbe2d..439745b 100644 --- a/AbMath/Calculator/Tokenizer.cs +++ b/AbMath/Calculator/Tokenizer.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using CLI; +using AbMath.Utilities; namespace AbMath.Calculator { @@ -21,7 +21,6 @@ public class Tokenizer : ITokenizer private List _tokens; private string _rule; - public event EventHandler Logger; public Tokenizer(DataStore dataStore) { @@ -36,12 +35,12 @@ public List Tokenize() if (_dataStore.DebugMode) { _tables = new Tables(new Config {Title = "Tokenizer", Format = _dataStore.DefaultFormat}); - _tables.Add(new Schema {Column = "#", Width = 3}); - _tables.Add(new Schema {Column = "Character", Width = 10}); - _tables.Add(new Schema {Column = "Code", Width = 5}); - _tables.Add(new Schema {Column = "Token", Width = 15}); - _tables.Add(new Schema {Column = "# Tokens", Width = 11}); - _tables.Add(new Schema {Column = "Action", Width = 16}); + _tables.Add(new Schema("#", 3)); + _tables.Add(new Schema("Character")); + _tables.Add(new Schema("Code")); + _tables.Add(new Schema("Token")); + _tables.Add(new Schema("# Tokens")); + _tables.Add(new Schema("Action")); } string token = string.Empty; @@ -51,6 +50,7 @@ public List Tokenize() ReadOnlySpan equationSpan = Equation.AsSpan(); ReadOnlySpan localSpan = null; + int end = Equation.Length - 1; for (int i = 0; i < length; i++) { @@ -76,82 +76,86 @@ public List Tokenize() } else { + //Resolve things ahead of time + Type prevType = _dataStore.Resolve(_prevToken); + Type characterType = _dataStore.Resolve(_character); + Type tokenType = _dataStore.Resolve(token); + Type readAheadType = _dataStore.Resolve(_readAhead); - if (_dataStore.IsOperator(_character + _readAhead)) + if (characterType == Type.Operator && readAheadType == Type.Operator) { - WriteToken("Operator", ref token); + WriteToken("Operator", ref token, tokenType); token = _character + _readAhead; - WriteToken("Operator", ref token); + WriteToken("Operator", ref token, Type.Operator); i += 1; } //Unary Input at the start of the input or after another operator or left parenthesis - else if ((i == 0 && _dataStore.IsUnary(_character)) || (_tokens.Count > 0 && (_dataStore.IsOperator(_prevToken) || _dataStore.IsLeftBracket(_prevToken) || _prevToken == ",") && _dataStore.IsUnary(_character) && !_dataStore.IsNumber(token))) - { + else if ((i == 0 && _dataStore.IsUnary(_character)) || (_tokens.Count > 0 && (prevType == Type.Operator || prevType == Type.LParen || _prevToken == ",") && _dataStore.IsUnary(_character) && tokenType != Type.Number)) + { _rule = "Unary"; token += _character; - if (!(string.IsNullOrWhiteSpace(_readAhead)) && (_dataStore.IsVariable(_readAhead) || _dataStore.IsLeftBracket(_readAhead))) + if (!(string.IsNullOrWhiteSpace(_readAhead)) && (readAheadType == Type.Variable || readAheadType == Type.LParen)) { token += "1"; WriteToken("Unary", ref token); } } - else if (token == "-." && _dataStore.IsNumber(_character)) + else if (token == "-." && characterType == Type.Number) { _rule = "Decimal Append"; token += _character; } //Token is a number //Character is [LB, FUNC, Variable] - else if ( _dataStore.IsNumber(token) && (_dataStore.IsLeftBracket(_character) || _dataStore.IsFunction(_character) || _dataStore.IsVariable(_character))) + else if ( tokenType == Type.Number && (characterType == Type.LParen || characterType == Type.Function || characterType == Type.Variable)) { WriteToken("Left Implicit", ref token); token = _character; - if (_dataStore.IsLeftBracket(_character) || (i == (Equation.Length - 1))) + if (characterType == Type.LParen || (i == (Equation.Length - 1))) { - WriteToken("Left Implicit", ref token); + WriteToken("Left Implicit", ref token, characterType); } } //Token is a variable //Character is a number - else if (_dataStore.IsNumber(_character) && _dataStore.IsVariable(token)) + else if (characterType == Type.Number && tokenType == Type.Variable) { - WriteToken("Left Implicit 2", ref token); + WriteToken("Left Implicit 2", ref token, tokenType); token = _character; if (_dataStore.IsLeftBracket(_character) || (i == (Equation.Length - 1))) { - WriteToken("Left Implicit 2", ref token); + WriteToken("Left Implicit 2", ref token, characterType); } } - else if (_dataStore.IsFunction(token) && _dataStore.IsLeftBracket(_character)) + else if (tokenType == Type.Function && characterType == Type.LParen) { - WriteToken("Function Start", ref token); + WriteToken("Function Start", ref token, tokenType); token = _character; - WriteToken("Function Start", ref token); + WriteToken("Function Start", ref token, characterType); } - else if (_dataStore.IsFunction(token) && (_dataStore.IsRightBracket(_character) || _dataStore.IsOperator(_character))) + else if (tokenType == Type.Function && (characterType == Type.RParen || characterType == Type.Operator)) { - WriteToken("Function End", ref token); + WriteToken("Function End", ref token, tokenType); token = _character; - WriteToken("Function End", ref token); + WriteToken("Function End", ref token, characterType); } - - else if ( ( _dataStore.IsNumber(token) || _dataStore.IsVariable(token) ) && (_dataStore.IsLeftBracket(_character) || _dataStore.IsRightBracket(_character) || _dataStore.IsOperator(_character))) + else if ( (tokenType == Type.Number || tokenType == Type.Variable) && (characterType == Type.LParen || characterType == Type.RParen || characterType == Type.Operator)) { - WriteToken("Edge Case 1", ref token); + WriteToken("Edge Case 1", ref token, tokenType); token = _character; - WriteToken("Edge Case 1", ref token); + WriteToken("Edge Case 1", ref token, characterType); } - else if (_dataStore.IsOperator(_character)) + else if (characterType == Type.Operator) { token += _character; WriteToken("Operator", ref token); } - else if (_dataStore.IsLeftBracket(_character) || _dataStore.IsRightBracket(_character)) + else if ( characterType == Type.LParen || characterType == Type.RParen) { token += _character; WriteToken("Bracket", ref token); } - else if (i == (Equation.Length - 1)) + else if (i == end) { token += _character; WriteToken("End of String", ref token); @@ -177,11 +181,6 @@ public List Tokenize() if (_dataStore.DebugMode) { Write(_tables.ToString()); - - if (_tables.SuggestedRedraw) - { - Write(_tables.Redraw()); - } } sw.Stop(); @@ -198,7 +197,7 @@ public List Tokenize() /// and sets the previous token. /// /// - private void WriteToken(string rule,ref string tokens) + private void WriteToken(string rule,ref string tokens, Type type = Type.Null) { if (string.IsNullOrWhiteSpace(tokens) && tokens != ",") { @@ -212,32 +211,22 @@ private void WriteToken(string rule,ref string tokens) _rule = rule; + if (type == Type.Null) + { + type = _dataStore.Resolve(tokens); + } + Token token; - switch (_dataStore.Resolve(tokens)) + switch (type) { - case Type.Number: - token = new Token(tokens, 0, Type.Number); - break; case Type.Function: token = new Token(tokens, _dataStore.Functions[tokens].Arguments, Type.Function); break; case Type.Operator: token = new Token(tokens, _dataStore.Operators[tokens].Arguments, Type.Operator); break; - case Type.LParen: - token = new Token(tokens, 0, Type.LParen); - break; - case Type.RParen: - token = new Token(tokens, 0, Type.RParen); - break; - case Type.Variable: - token = new Token(tokens, 0, Type.Variable); - break; - case Type.Null: - token = new Token(tokens, 0, Type.Null); - break; default: - token = new Token(tokens, 0, _dataStore.Resolve(tokens)); + token = new Token(tokens, 0, type); break; } _tokens.Add(token); @@ -248,16 +237,10 @@ private void WriteToken(string rule,ref string tokens) private void Write(string message) { - if (_dataStore.DebugMode) - { - Logger?.Invoke(this, message); - } + var logger = _dataStore.Logger; + logger.Log(Channels.Debug, message); } - private void Log(string message) - { - Logger?.Invoke(this, message); - } } } } diff --git a/AbMath/Utilities/Logger.cs b/AbMath/Utilities/Logger.cs new file mode 100644 index 0000000..dd66bd3 --- /dev/null +++ b/AbMath/Utilities/Logger.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using AbMath.Calculator.Simplifications; + +namespace AbMath.Utilities +{ + public class Logger + { + private object lockingObject = new object(); + + private Dictionary> queues; + private Dictionary> handlers; + + public Logger() + { + queues = new Dictionary>(); + queues.Add(Channels.Debug, new Queue()); + queues.Add(Channels.Output, new Queue()); + + handlers = new Dictionary>(); + } + + //TODO: Make this async? + public void Log(Channels channel, string message) + { + queues[channel].Enqueue(message); + + //If there is no handler return + if (!handlers.ContainsKey(channel) || handlers[channel] == null) + { + return; + } + + while (queues[channel].Count > 0) + { + EventHandler handler = handlers[channel]; + lock (lockingObject) + { + handler.Invoke(this, queues[channel].Dequeue()); + } + } + } + + public void Bind(Channels channel, EventHandler handler) + { + handlers[channel] = handler; + } + } + + public enum Channels + { + Debug, Output + } +} diff --git a/AbMath/Tables.cs b/AbMath/Utilities/Tables.cs similarity index 68% rename from AbMath/Tables.cs rename to AbMath/Utilities/Tables.cs index 116e825..ac28aed 100644 --- a/AbMath/Tables.cs +++ b/AbMath/Utilities/Tables.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace CLI +namespace AbMath.Utilities { public enum Format { Default, MarkDown }; public struct Config @@ -13,6 +13,18 @@ public struct Config public struct Schema { + public Schema(string column) + { + this.Column = column; + this.Width = column.Length + Tables.Padding; + } + + public Schema(string column, int width) + { + this.Column = column; + this.Width = width; + } + public string Column { get; set; } public int Width { get; set; } } @@ -86,7 +98,14 @@ public struct CharacterSheet public char BottomRight; } - public class Tables + public class Tables + { + public const int LeftPadding = 1; + public const int RightPadding = 1; + public const int Padding = LeftPadding + RightPadding; + } + + public class Tables : Tables { private List schemas; private List data; @@ -123,6 +142,13 @@ public Tables(Config Config) public Tables Add(Schema schema) { + if (data.Count > 0) + { + //If we have data already we cannot add to the schema + throw new Exception("You cannot add to the schema after you have added a row to your table."); + } + //If the schema width is bad we should adjust it without telling the user! + schema.Width = Math.Max(schema.Column.Length + RightPadding, schema.Width); schemas.Add(schema); return this; } @@ -138,9 +164,7 @@ public Tables Add(T[] row) if (row[i].ToString().Length > schemas[i].Width) { - Schema schema = schemas[i]; - schema.Width = row[i].ToString().Length; - schemas[i] = schema; + schemas[i] = new Schema(schemas[i].Column, row[i].ToString().Length + RightPadding); } } data.Add(row); @@ -163,14 +187,26 @@ public string GenerateHeaders() if (config.Format == Format.Default) { - sb.AppendLine($"{Sheet.TopLeft}{"".PadRight(sum, Sheet.Continue)}{Sheet.TopRight}"); - sb.AppendLine( - $"{Sheet.Down}{"".PadRight(floor - Length)}{config.Title}{"".PadRight(ceiling)}{Sheet.Down}"); - sb.AppendLine(Lines(new char[] { Sheet.MidLeft, Sheet.MidTerminate, Sheet.MidRight })); + sb.Append(Sheet.TopLeft); + sb.Append(Sheet.Continue, sum); + sb.Append(Sheet.TopRight); + sb.AppendLine(); + + sb.Append(Sheet.Down); + sb.Append(' ', floor - Length); + sb.Append(config.Title); + sb.Append(' ', ceiling); + sb.Append(Sheet.Down); + sb.AppendLine(); + + Lines(new char[] {Sheet.MidLeft, Sheet.MidTerminate, Sheet.MidRight}, sb); + sb.AppendLine(); } else { - sb.AppendLine($"# {config.Title}"); + sb.Append("# "); + sb.Append(config.Title); + sb.AppendLine(); } sb.Append(Sheet.Down); @@ -178,7 +214,7 @@ public string GenerateHeaders() for (int i = 0; i < schemas.Count; i++) { int dif = schemas[i].Width - schemas[i].Column.Length; - sb.Append(Row(schemas[i].Column, dif, i)); + Row(schemas[i].Column, dif, i, sb); md?.Append("|-"); } @@ -192,35 +228,19 @@ public string GenerateBody() { var sb = new StringBuilder(); for (int i = 0; i < data.Count; i++) - { - sb.AppendLine( GenerateRow(i)); - } - return sb.ToString(); - } - - public string GenerateNextRow() - { - return GenerateRow(data.Count - 1); - } - - private string GenerateRow(int index) - { - if (index > data.Count) - { - throw new IndexOutOfRangeException($"The Index was {index} but the max is {data.Count}!"); - } - - if (schemas.Count != data[index].Length) - { - throw new ArgumentOutOfRangeException($"Was given {data[index].Length} args but expected {schemas.Count}"); - } + { + if (schemas.Count != data[i].Length) + { + throw new ArgumentOutOfRangeException($"Was given {data[i].Length} args but expected {schemas.Count}"); + } + sb.Append(Sheet.Down); + for (int j = 0; j < schemas.Count; j++) + { + Row(data[i][j].ToString() ?? string.Empty, schemas[j].Width - data[i][j].ToString().Length, j, sb); + } - var sb = new StringBuilder(); - sb.Append(Sheet.Down); - for (int i = 0; i < schemas.Count; i++) - { - sb.Append(Row(data[index][i].ToString() ?? string.Empty, schemas[i].Width - data[index][i].ToString().Length, i)); + sb.Append("\n"); } return sb.ToString(); } @@ -229,16 +249,16 @@ private string GenerateRow(int index) //any addtional footers? public string GenerateFooter() { - string footer = Lines(new char[] { Sheet.BottomLeft, Sheet.BottomTerminate, Sheet.BottomRight }); + StringBuilder sb = new StringBuilder(); + Lines(new char[] { Sheet.BottomLeft, Sheet.BottomTerminate, Sheet.BottomRight }, sb); if (cursor.Exists) { cursor.endy = Console.CursorTop + 1; } - return footer; + return sb.ToString(); } - private string Lines(char[] chars) { - var sb = new StringBuilder(); + private void Lines(char[] chars, StringBuilder sb) { sb.Append(chars[0]); for (int i = 0; i < schemas.Count; i++) @@ -253,24 +273,24 @@ private string Lines(char[] chars) { } } sb.Append($"{chars[2]}"); - - return sb.ToString(); } - private string Row(string output, int dif, int i) { - var temp = string.Empty; - if (dif <= 0) + private void Row(string output, int dif, int i, StringBuilder sb) { + //If the diff is negative it means that the column is not big enough! + if (dif < 0) { //Overrides user width suggestion when an overflow occurs. - schemas[i] = new Schema { Column = schemas[i].Column, Width = schemas[i].Width + Math.Abs(dif) }; - SuggestedRedraw = true; - temp = (i == 0) ? $"{output}{Sheet.Down}" : $" {output} {Sheet.Down}"; + throw new Exception($"Table overflow occured!\n{config.Title}\n{output}\n{i}\n{dif}\n{schemas[i].Width}"); } - else + + if (i != 0) { - temp = (i == 0) ? $"{output}{"".PadRight(dif)} {Sheet.Down}" : $" {output}{"".PadRight(dif)} {Sheet.Down}"; + sb.Append(" "); } - return temp; + + sb.Append(output); + sb.Append(' ', dif + 1); + sb.Append(Sheet.Down); } private int TableWidth() @@ -311,7 +331,12 @@ void Clear(int y) public override string ToString() { - return GenerateHeaders() +"\n"+ GenerateBody() + GenerateFooter(); + StringBuilder sb = new StringBuilder(); + sb.Append(GenerateHeaders()); + sb.AppendLine(); + sb.Append(GenerateBody()); + sb.Append(GenerateFooter()); + return sb.ToString(); } } } diff --git a/Unit Tester/Apportionment/Calculator/AST.cs b/Unit Tester/Apportionment/Calculator/AST.cs index f377390..7228d94 100644 --- a/Unit Tester/Apportionment/Calculator/AST.cs +++ b/Unit Tester/Apportionment/Calculator/AST.cs @@ -18,7 +18,7 @@ public void IncreaseExponent() public void ComplexIncreaseExponent() { RPN rpn = new RPN("(x(x + 1))(x(x + 1))(x(x + 1))").Compute(); - Assert.AreEqual("x 1 + x * 3 ^", rpn.Polish.Print()); + Assert.AreEqual("x 1 + 3 ^ x 3 ^ *", rpn.Polish.Print()); } [Test] @@ -28,6 +28,27 @@ public void OneRaisedExponent() Assert.AreEqual("1", rpn.Polish.Print()); } + [Test] + public void ExponentRaisedToExponent() + { + RPN rpn = new RPN("(x^2)^(-0.5)").Compute(); + Assert.AreEqual("1 x abs /", rpn.Polish.Print()); + } + + [Test] + public void ZeroRaisedToConstant() + { + RPN rpn = new RPN("0^2").Compute(); + Assert.AreEqual("0", rpn.Polish.Print()); + } + + [Test] + public void AbsRaisedToPowerTwo() + { + RPN rpn = new RPN("abs(x)^2").Compute(); + Assert.AreEqual("x 2 ^", rpn.Polish.Print()); + } + [Test] public void TrigIdentiySinAndCos() { @@ -69,8 +90,8 @@ public void LogAddOrSub() [Test] public void LnAddOrSub() { - RPN rpn = new RPN("ln(2) + ln(1/2)").Compute(); - Assert.AreEqual("1 ln", rpn.Polish.Print()); + RPN rpn = new RPN("ln(2) + ln(1/3)").Compute(); + Assert.AreEqual("2 3 / ln", rpn.Polish.Print()); rpn.SetEquation("ln(2) - ln(3)").Compute(); Assert.AreEqual("2 3 / ln", rpn.Polish.Print()); @@ -87,7 +108,7 @@ public void ExpressionTimesDivision() public void DivisionTimesDivision() { RPN rpn = new RPN("(3/4)(1/4)").Compute(); - Assert.AreEqual("3 4 2 ^ /", rpn.Polish.Print()); + Assert.AreEqual("3 16 /", rpn.Polish.Print()); } [Test] @@ -160,6 +181,9 @@ public void Exponent_Log_Power() rpn.SetEquation("(2x)^log(2x,2)").Compute(); Assert.AreEqual("2", rpn.Polish.Print()); + + rpn.SetEquation("ln(x^2)").Compute(); + Assert.AreEqual("2 x ln *", rpn.Polish.Print()); } [Test] @@ -258,6 +282,13 @@ public void Power() Assert.AreEqual("2 3 * x 3 ^ *", rpn.Polish.Print()); } + [Test] + public void PowerToPower() + { + RPN rpn = new RPN("((x^2)^c)^a").Compute(); + Assert.AreEqual("x 2 c * a * ^", rpn.Polish.Print()); + } + [Test] public void ZeroMultiplicationDivision() { @@ -293,5 +324,19 @@ public void CosSinToCot() { rpn.SetEquation("cos(x^3)/(sin(x^3) * x^2)").Compute(); Assert.AreEqual("x 3 ^ cot x 2 ^ /", rpn.Polish.Print()); } + + [Test] + public void LnPowerRule() + { + RPN rpn = new RPN("ln(x^2)").Compute(); + Assert.AreEqual("2 x ln *", rpn.Polish.Print()); + } + + [Test] + public void DivisionAddRule() + { + RPN rpn = new RPN("x/y + z/y").Compute(); + Assert.AreEqual("x z + y /", rpn.Polish.Print()); + } } } \ No newline at end of file diff --git a/Unit Tester/Apportionment/Calculator/Derivative.cs b/Unit Tester/Apportionment/Calculator/Derivative.cs index 3ac11ab..cb1f3c6 100644 --- a/Unit Tester/Apportionment/Calculator/Derivative.cs +++ b/Unit Tester/Apportionment/Calculator/Derivative.cs @@ -51,6 +51,13 @@ public void ProductRule() Assert.AreEqual("x cos 2 ^ x sin 2 ^ -", test.Polish.Print()); } + [Test] + public void QuotientConstantRule() + { + RPN test = new RPN("derivative((x^2)/24,x)").Compute(); + Assert.AreEqual("2 x * 24 /", test.Polish.Print()); + } + [Test] public void QuotientRule() { @@ -93,25 +100,25 @@ public void BaseExponentSimple() public void Sqrt() { RPN test = new RPN("derivative(sqrt(x),x)").Compute(); - Assert.AreEqual("0.5 x sqrt /", test.Polish.Print()); + Assert.AreEqual("1 2 x sqrt * /", test.Polish.Print()); test.SetEquation("derivative(sqrt(x + 3),x)").Compute(); - Assert.AreEqual("0.5 x 3 + sqrt /", test.Polish.Print()); + Assert.AreEqual("1 2 x 3 + sqrt * /", test.Polish.Print()); } [Test] public void Abs() { RPN test = new RPN("derivative( abs(x^2), x)").Compute(); - //Assert.AreEqual("0.5 x 2 ^ * 2 2 ^ x * * x 2 ^ /", test.Polish.Print()); - Assert.AreEqual("2 2 ^ 0.5 x 3 ^ * * x 2 ^ /", test.Polish.Print()); + + Assert.AreEqual("4 x 2 ^ * x * 2 x 2 ^ * /", test.Polish.Print()); } [Test] public void Ln() { RPN test = new RPN("derivative(ln(x^2),x)").Compute(); - Assert.AreEqual("2 x * x 2 ^ /", test.Polish.Print()); + Assert.AreEqual("2 x /", test.Polish.Print()); } [Test] @@ -219,20 +226,20 @@ public void ArcCot() public void ArcSec() { RPN test = new RPN("derivative( arcsec(x), x)").Compute(); - Assert.AreEqual("1 x x 2 ^ 1 - sqrt * /", test.Polish.Print()); + Assert.AreEqual("1 x abs x 2 ^ 1 - sqrt * /", test.Polish.Print()); test.SetEquation("derivative( arcsec(x^2), x)").Compute(); - Assert.AreEqual("2 x * x 2 ^ x 4 ^ 1 - sqrt * /", test.Polish.Print()); + Assert.AreEqual("2 x * x 2 ^ abs x 4 ^ 1 - sqrt * /", test.Polish.Print()); } [Test] public void ArcCsc() { RPN test = new RPN("derivative( arccsc(x), x)").Compute(); - Assert.AreEqual("-1 x x 2 ^ 1 - sqrt * /", test.Polish.Print()); + Assert.AreEqual("-1 x abs x 2 ^ 1 - sqrt * /", test.Polish.Print()); test.SetEquation("derivative( arccsc(x^2), x)").Compute(); - Assert.AreEqual("-2 x * x 2 ^ x 4 ^ 1 - sqrt * /", test.Polish.Print()); + Assert.AreEqual("-2 x * x 2 ^ abs x 4 ^ 1 - sqrt * /", test.Polish.Print()); } [Test] diff --git a/Unit Tester/Apportionment/Calculator/Extensions.cs b/Unit Tester/Apportionment/Calculator/Extensions.cs new file mode 100644 index 0000000..7719137 --- /dev/null +++ b/Unit Tester/Apportionment/Calculator/Extensions.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AbMath.Calculator; +using AbMath.Calculator.Simplifications; +using NUnit.Framework; + +namespace AbMath.Tests +{ + [TestFixture] + public class Extensions + { + [Test] + public void Bernoulli() + { + Assert.AreEqual("1 1 /", bernoulli(0)); + Assert.AreEqual("-1 2 /", bernoulli(1)); + Assert.AreEqual("1 6 /", bernoulli(2)); + Assert.AreEqual("-1 30 /", bernoulli(4)); + Assert.AreEqual("1 42 /", bernoulli(6)); + Assert.AreEqual("-1 30 /", bernoulli(8)); + Assert.AreEqual("5 66 /", bernoulli(10)); + Assert.AreEqual("-691 2730 /", bernoulli(12)); + Assert.AreEqual("7 6 /", bernoulli(14)); + Assert.AreEqual("-3617 510 /", bernoulli(16)); + Assert.AreEqual("43867 798 /", bernoulli(18)); + Assert.AreEqual("-174611 330 /", bernoulli(20)); + Assert.AreEqual("854513 138 /", bernoulli(22)); + Assert.AreEqual("-236364091 2730 /", bernoulli(24)); + Assert.AreEqual("8553103 6 /", bernoulli(26)); + Assert.AreEqual("-23749461029 870 /", bernoulli(28)); + Assert.AreEqual("8615841276005 14322 /", bernoulli(30)); + Assert.AreEqual("-7709321041217 510 /", bernoulli(32)); + Assert.AreEqual("2577687858367 6 /", bernoulli(34)); + + Assert.AreEqual("-2.63152715530535E+19 1919190 /", bernoulli(36)); + Assert.AreEqual("2.92999391384156E+15 6 /", bernoulli(38)); + Assert.AreEqual("-2.61082718496449E+20 13530 /", bernoulli(40)); + Assert.AreEqual("1.52009764391807E+21 1806 /", bernoulli(42)); + Assert.AreEqual("-2.7833269579301E+22 690 /", bernoulli(44)); + Assert.AreEqual("5.96451111593912E+23 282 /", bernoulli(46)); + Assert.AreEqual("-5.60940336899782E+27 46410 /", bernoulli(48)); + Assert.AreEqual("4.9505720524108E+26 66 /", bernoulli(50)); + Assert.AreEqual("-8.0116571813549E+29 1590 /", bernoulli(52)); + Assert.AreEqual("2.91499636348849E+31 798 /", bernoulli(54)); + Assert.AreEqual("-2.47939292931323E+33 870 /", bernoulli(56)); + Assert.AreEqual("8.448361334888E+34 354 /", bernoulli(58)); + Assert.AreEqual("-1.21523314048376E+42 56786730 /", bernoulli(60)); + Assert.AreEqual("1.23005854340869E+37 6 /", bernoulli(62)); + } + + [Test] + public void BernoulliOdd() + { + Assert.AreEqual("0 1 /", bernoulli(3)); + Assert.AreEqual("0 1 /", bernoulli(5)); + Assert.AreEqual("0 1 /", bernoulli(7)); + Assert.AreEqual("0 1 /", bernoulli(9)); + Assert.AreEqual("0 1 /", bernoulli(11)); + Assert.AreEqual("0 1 /", bernoulli(13)); + Assert.AreEqual("0 1 /", bernoulli(15)); + Assert.AreEqual("0 1 /", bernoulli(17)); + Assert.AreEqual("0 1 /", bernoulli(19)); + } + + private string bernoulli(int n) + { + return Sum.getBernoulliNumber(n).ToPostFix().Print(); + } + } +} diff --git a/Unit Tester/Apportionment/Calculator/Implicit.cs b/Unit Tester/Apportionment/Calculator/Implicit.cs index 45bfa82..9e7e39c 100644 --- a/Unit Tester/Apportionment/Calculator/Implicit.cs +++ b/Unit Tester/Apportionment/Calculator/Implicit.cs @@ -5,6 +5,7 @@ namespace AbMath.Tests { [TestFixture] + [Parallelizable] public class ImplicitShunting { [Test] @@ -81,7 +82,7 @@ public void MultipleFunctions() public void Unary() { RPN test = new RPN("-(3^2)").Compute(); - Assert.AreEqual("-1 3 2 ^ *", test.Polish.Print()); + Assert.AreEqual("-9", test.Polish.Print()); } } } diff --git a/Unit Tester/Apportionment/Calculator/Integrate.cs b/Unit Tester/Apportionment/Calculator/Integrate.cs new file mode 100644 index 0000000..65041a5 --- /dev/null +++ b/Unit Tester/Apportionment/Calculator/Integrate.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AbMath.Calculator; +using NUnit.Framework; + +namespace AbMath.Tests +{ + [TestFixture] + public class Integrate + { + [Test] + public void Constants() + { + RPN rpn = new RPN("integrate(y,x,a,b)").Compute(); + Assert.AreEqual("y b a - *", rpn.Polish.Print()); + + rpn.SetEquation("integrate(sin(y),x,a,b)").Compute(); + Assert.AreEqual("y sin b a - *", rpn.Polish.Print()); + + rpn.SetEquation("integrate(sin(y)cos(y),x,a,b)").Compute(); + Assert.AreEqual("y cos y sin * b a - *", rpn.Polish.Print()); + } + + [Test] + public void Coefficient() + { + RPN rpn = new RPN("integrate(c*x,x,a,b)").Compute(); + Assert.AreEqual("c b 2 ^ a 2 ^ - * 2 /", rpn.Polish.Print()); + + rpn.SetEquation("integrate(5*x,x,a,b)").Compute(); + Assert.AreEqual("5 b 2 ^ a 2 ^ - * 2 /", rpn.Polish.Print()); + } + + [Test] + public void SingleVariable() + { + RPN rpn = new RPN("integrate(x,x,a,b)").Compute(); + Assert.AreEqual("b 2 ^ a 2 ^ - 2 /", rpn.Polish.Print()); + } + + } +} diff --git a/Unit Tester/Apportionment/Calculator/PostFix.cs b/Unit Tester/Apportionment/Calculator/PostFix.cs index 14aa863..0c3a05b 100644 --- a/Unit Tester/Apportionment/Calculator/PostFix.cs +++ b/Unit Tester/Apportionment/Calculator/PostFix.cs @@ -5,6 +5,7 @@ namespace AbMath.Tests { [TestFixture] + [Parallelizable] public class PostFixTest { [Test] diff --git a/Unit Tester/Apportionment/Calculator/Solver.cs b/Unit Tester/Apportionment/Calculator/Solver.cs index 392992a..f5a3d64 100644 --- a/Unit Tester/Apportionment/Calculator/Solver.cs +++ b/Unit Tester/Apportionment/Calculator/Solver.cs @@ -5,6 +5,7 @@ namespace AbMath.Tests { [TestFixture] + [Parallelizable] public class Solver { [Test] diff --git a/Unit Tester/Apportionment/Calculator/SubtractionTest.cs b/Unit Tester/Apportionment/Calculator/SubtractionTest.cs new file mode 100644 index 0000000..5daf343 --- /dev/null +++ b/Unit Tester/Apportionment/Calculator/SubtractionTest.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AbMath.Calculator; +using NUnit.Framework; +using NUnit.Framework.Internal; + +namespace AbMath.Tests +{ + [TestFixture] + public class SubtractionTest + { + [Test] + public void DistributiveSimple() + { + RPN rpn = new RPN("f - (g - h)").Compute(); + Assert.AreEqual("f h + g -", rpn.Polish.Print()); + } + } +} diff --git a/Unit Tester/Apportionment/Calculator/SumTest.cs b/Unit Tester/Apportionment/Calculator/SumTest.cs new file mode 100644 index 0000000..c243291 --- /dev/null +++ b/Unit Tester/Apportionment/Calculator/SumTest.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AbMath.Calculator; +using NUnit.Framework; + +namespace AbMath.Tests +{ + [TestFixture] + public class SumTest + { + [Test] + public void ConstantDivisionFactorial() + { + RPN test = new RPN("sum(x/50,x,0,b)").Compute(); + Assert.AreEqual("b b 1 + * 2 50 * /", test.Polish.Print()); + } + } +} diff --git a/Unit Tester/Apportionment/Calculator/Tokenizer.cs b/Unit Tester/Apportionment/Calculator/Tokenizer.cs index 52245f3..b968c4b 100644 --- a/Unit Tester/Apportionment/Calculator/Tokenizer.cs +++ b/Unit Tester/Apportionment/Calculator/Tokenizer.cs @@ -66,14 +66,10 @@ public void SimpleAdd() public void MultiTermAdd() { RPN test = new RPN("2 + 3 + 2").Compute(); - Assert.AreEqual("2 3 + 2 +", test.Polish.Print()); - } + Assert.AreEqual("4 3 +", test.Polish.Print()); - [Test] - public void MultiTermAddNoSpace() - { - RPN test = new RPN("2+3+2").Compute(); - Assert.AreEqual("2 3 + 2 +", test.Polish.Print()); + test = new RPN("2+3+2").Compute(); + Assert.AreEqual("4 3 +", test.Polish.Print()); } [Test] @@ -87,7 +83,7 @@ public void SimpleSubtract() public void Wikipedia() { RPN test = new RPN("3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3").Compute(); - Assert.AreEqual("4 2 * 1 5 - 2 3 ^ ^ / 3 +", test.Polish.Print()); + Assert.AreEqual("4 2 * 1 5 - 8 ^ / 3 +", test.Polish.Print()); } [Test] @@ -150,7 +146,7 @@ public void Aliasing() public void UnaryStart() { RPN test = new RPN("-2 + 4").Compute(); - Assert.AreEqual("-2 4 +", test.Polish.Print()); + Assert.AreEqual("4 -2 +", test.Polish.Print()); } [Test] @@ -170,7 +166,7 @@ public void MixedDivisionMultiplication() Assert.AreEqual("1 2 x * /", test.Polish.Print()); test.SetEquation("8/2(2 + 2)").Compute(); - Assert.AreEqual("8 2 2 2 ^ * /", test.Polish.Print()); + Assert.AreEqual("8 2 4 * /", test.Polish.Print()); test.Data.ImplicitMultiplicationPriority = false; @@ -178,7 +174,7 @@ public void MixedDivisionMultiplication() Assert.AreEqual("x 2 /", test.Polish.Print()); test.SetEquation("8/2(2 + 2)").Compute(); - Assert.AreEqual("4 2 2 ^ *", test.Polish.Print()); + Assert.AreEqual("16", test.Polish.Print()); } [Test] @@ -199,5 +195,12 @@ public void DoubleTokenize() test.SetEquation("2x + 2").Compute(); Assert.AreEqual("2 x * 2 +", test.Polish.Print()); } + + [Test] + public void testFactorialInfix() + { + RPN test = new RPN("x!").Compute(); + Assert.AreEqual("x!", test.Data.SimplifiedEquation); + } } } diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..a551aa1 --- /dev/null +++ b/config.yml @@ -0,0 +1,12 @@ + version: 2.1 + + orbs: + win: circleci/windows@2.2.0 + + jobs: + build: + executor: win/default + + steps: + - checkout + - run: dotnet build