// // Returns a positive constant on the LHS or RHS as appropriate. // private static KeyValuePair <double, double> HandleConstants(FlatEquation eq) { double lhs = CollectConstants(eq.lhsExps); double rhs = CollectConstants(eq.rhsExps); double simpLeft = lhs > rhs ? lhs - rhs : 0; double simpRight = rhs > lhs ? rhs - lhs : 0; return(new KeyValuePair <double, double>(simpLeft, simpRight)); }
// // Check for (basically) atomic equations involving one unknown and constants otherwise. // private static FlatEquation SimplifyForMultipliersAndConstants(FlatEquation inEq) { if (inEq.lhsExps.Count != 1 || inEq.rhsExps.Count != 1) { return(inEq); } // // Figure out what we're looking at. // NumericValue value = null; GroundedClause unknown = null; if (inEq.lhsExps[0] is NumericValue) { value = inEq.lhsExps[0] as NumericValue; unknown = inEq.rhsExps[0]; } else if (inEq.rhsExps[0] is NumericValue) { value = inEq.rhsExps[0] as NumericValue; unknown = inEq.lhsExps[0]; } // Not the type of equation we were looking for. else { return(inEq); } // // Divide both sides to simplify. // if (unknown.multiplier != 1) { NumericValue newValue = new NumericValue(value.DoubleValue / unknown.multiplier); // reset the multiplier unknown.multiplier = 1; return(new FlatEquation(Utilities.MakeList <GroundedClause>(unknown), Utilities.MakeList <GroundedClause>(newValue))); } // Nothing happened so return original return(inEq); }
private static FlatEquation CombineLikeTermsAcrossEqual(FlatEquation eq) { // The new simplified side of the equation List <GroundedClause> leftSimp = new List <GroundedClause>(); List <GroundedClause> rightSimp = new List <GroundedClause>(); // The resultant constant values on the left / right sides KeyValuePair <double, double> constantPair = HandleConstants(eq); bool[] rightCheckedExp = new bool[eq.rhsExps.Count]; foreach (GroundedClause lExp in eq.lhsExps) { if (!(lExp is NumericValue)) { int rightExpIndex = StructuralIndex(eq.rhsExps, lExp); // // Left expression has like term on the right? // // it doesn't have a like term if (rightExpIndex == -1) { leftSimp.Add(lExp); } // // Expression has like term on the right // else { rightCheckedExp[rightExpIndex] = true; GroundedClause rExp = eq.rhsExps[rightExpIndex]; GroundedClause copyExp = lExp.DeepCopy(); // Seek to keep positive values for the resultant, simplified expression if (lExp.multiplier - rExp.multiplier > 0) { copyExp.multiplier = lExp.multiplier - rExp.multiplier; leftSimp.Add(copyExp); } else if (rExp.multiplier - lExp.multiplier > 0) { copyExp.multiplier = rExp.multiplier - lExp.multiplier; rightSimp.Add(copyExp); } else // Cancelation of the terms { } } } } // Pick up all the expressions on the right hand side which were not like terms of those on the left for (int i = 0; i < eq.rhsExps.Count; i++) { if (!rightCheckedExp[i] && !(eq.rhsExps[i] is NumericValue)) { rightSimp.Add(eq.rhsExps[i]); } } // // Add back the constant to the correct side // if (constantPair.Key != 0) { leftSimp.Add(new NumericValue(constantPair.Key)); } if (constantPair.Value != 0) { rightSimp.Add(new NumericValue(constantPair.Value)); } // // Now check coefficients: both sides all have coefficients that evenly divide the other side. // if (leftSimp.Any() && rightSimp.Any()) { // Calculate the gcd int gcd = leftSimp[0].multiplier; for (int i = 1; i < leftSimp.Count; i++) { gcd = Utilities.GCD(gcd, leftSimp[i].multiplier); } foreach (GroundedClause rExp in rightSimp) { gcd = Utilities.GCD(gcd, rExp.multiplier); } if (gcd != 1) { // Divide all expressions by the gcd foreach (GroundedClause lExp in leftSimp) { lExp.multiplier /= gcd; } foreach (GroundedClause rExp in rightSimp) { rExp.multiplier /= gcd; } } } // Check for extreme case in which one side has no elements; in this case, add a zero if (!leftSimp.Any()) { leftSimp.Add(new NumericValue(0)); } if (!rightSimp.Any()) { rightSimp.Add(new NumericValue(0)); } return(new FlatEquation(leftSimp, rightSimp)); }
private static FlatEquation CombineLikeTerms(FlatEquation eq) { return(new FlatEquation(CombineSideLikeTerms(eq.lhsExps), CombineSideLikeTerms(eq.rhsExps))); }
//private static readonly string NAME = "Simplification"; // // Given an equation, simplify algebraically using the following notions: // A + A = B -> 2A = B // A + B = B + C -> A = C // A + B = 2B + C -> A = B + C // public static Equation Simplify(Equation original) { // Do we have an equation? if (original == null) { throw new ArgumentException(); } // Is the equation 0 = 0? This should be allowed at it indicates a tautology if (original.lhs.Equals(new NumericValue(0)) && original.rhs.Equals(new NumericValue(0))) { throw new ArgumentException("Should not have an equation that is 0 = 0: " + original.ToString()); } // // Ideally, flattening would: // Remove all subtractions -> adding a negative instead // Distribute subtraction or multiplication over addition // // Flatten the equation so that each side is a sum of atomic expressions Equation copyEq = (Equation)original.DeepCopy(); FlatEquation flattened = new FlatEquation(copyEq.lhs.CollectTerms(), copyEq.rhs.CollectTerms()); //Debug.WriteLine("Equation prior to simplification: " + flattened.ToString()); // Combine terms only on each side (do not cross =) FlatEquation combined = CombineLikeTerms(flattened); //Debug.WriteLine("Equation after like terms combined on both sides: " + combined); // Combine terms across the equal sign FlatEquation across = CombineLikeTermsAcrossEqual(combined); //Debug.WriteLine("Equation after simplifying both sides: " + across); FlatEquation constSimplify = SimplifyForMultipliersAndConstants(across); // // Inflate the equation // Equation inflated = null; GroundedClause singleLeftExp = InflateEntireSide(constSimplify.lhsExps); GroundedClause singleRightExp = InflateEntireSide(constSimplify.rhsExps); if (original is AlgebraicSegmentEquation) { inflated = new AlgebraicSegmentEquation(singleLeftExp, singleRightExp); } else if (original is GeometricSegmentEquation) { inflated = new GeometricSegmentEquation(singleLeftExp, singleRightExp); } else if (original is AlgebraicAngleEquation) { inflated = new AlgebraicAngleEquation(singleLeftExp, singleRightExp); } else if (original is GeometricAngleEquation) { inflated = new GeometricAngleEquation(singleLeftExp, singleRightExp); } else if (original is AlgebraicArcEquation) { inflated = new AlgebraicArcEquation(singleLeftExp, singleRightExp); } else if (original is GeometricArcEquation) { inflated = new GeometricArcEquation(singleLeftExp, singleRightExp); } else if (original is AlgebraicAngleArcEquation) { inflated = new AlgebraicAngleArcEquation(singleLeftExp, singleRightExp); } else if (original is GeometricAngleArcEquation) { inflated = new GeometricAngleArcEquation(singleLeftExp, singleRightExp); } // If simplifying didn't do anything, return the original equation if (inflated.Equals(original)) { return(original); } // // 0 = 0 should not be allowable. // if (inflated.lhs.Equals(new NumericValue(0)) && inflated.rhs.Equals(new NumericValue(0))) { return(null); } return(inflated); }