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