diff --git a/lib/simplifyExpression/fractionsSearch/addConstantFractions.js b/lib/simplifyExpression/fractionsSearch/addConstantFractions.js index 4dd0629d..f761c73b 100644 --- a/lib/simplifyExpression/fractionsSearch/addConstantFractions.js +++ b/lib/simplifyExpression/fractionsSearch/addConstantFractions.js @@ -19,7 +19,8 @@ function addConstantFractions(node) { if (!Node.Type.isOperator(node) || node.op !== '+') { return Node.Status.noChange(node); } - if (!node.args.every(n => Node.Type.isIntegerFraction(n, true))) { + if (!node.args.every(n => Node.Type.isIntegerFraction(n, true) || + hasIntegerMultiplicationDenominator(n))) { return Node.Status.noChange(node); } const denominators = node.args.map(fraction => { @@ -47,6 +48,16 @@ function addConstantFractions(node) { newNode = Node.Status.resetChangeGroups(status.newNode); } + const newDenominators = newNode.args.map(fraction => { + return fraction.args[1]; + }); + if (!newDenominators.every(denominator => hasEqualNumberOfArgs(denominator, newDenominators[0]))){ + // Multiply out the denominators + status = evaluateDenominators(newNode); + substeps.push(status); + newNode = Node.Status.resetChangeGroups(status.newNode); + } + // 2A. Now that they all have the same denominator, combine the numerators // e.g. 2/3 + 5/3 -> (2+5)/3 status = combineNumeratorsAboveCommonDenominator(newNode); @@ -136,15 +147,15 @@ function makeCommonDenominator(node) { if (missingFactor !== 1) { const missingFactorNode = Node.Creator.constant(missingFactor); const newNumerator = Node.Creator.parenthesis( - Node.Creator.operator('*', [child.args[0], missingFactorNode])); + Node.Creator.operator('*', [child.args[0], missingFactorNode])); const newDeominator = Node.Creator.parenthesis( - Node.Creator.operator('*', [child.args[1], missingFactorNode])); + Node.Creator.operator('*', [child.args[1], missingFactorNode])); newNode.args[i] = Node.Creator.operator('/', [newNumerator, newDeominator]); } }); return Node.Status.nodeChanged( - ChangeTypes.COMMON_DENOMINATOR, node, newNode); + ChangeTypes.COMMON_DENOMINATOR, node, newNode); } function evaluateDenominators(node) { @@ -169,4 +180,41 @@ function evaluateNumerators(node) { ChangeTypes.MULTIPLY_NUMERATORS, node, newNode); } +function hasIntegerMultiplicationDenominator(node) { + if (!Node.Type.isOperator(node, '/')) { + return false; + } + var denominator; + if (Node.Type.isParenthesis(node.args[1])) { + denominator = node.args[1].content; + } + else { + denominator = node.args[1]; + } + if (!Node.Type.isOperator(denominator, '*')) { + return false; + } + if (!denominator.args.every(n => (Node.Type.isConstant(n, true) || + Number.isInteger(parseFloat(n.value))))){ + return false; + } + return true; +} + +function hasEqualNumberOfArgs(n1, n2){ + let node1 = n1.cloneDeep(); + let node2 = n2.cloneDeep(); + node1 = Node.Type.isParenthesis(node1) ? node1.content : node1; + node2 = Node.Type.isParenthesis(node2) ? node2.content : node2; + let length1 = 1; + let length2 = 1; + if (node1.args) { + length1 = node1.args.length; + } + if (node2.args) { + length2 = node2.args.length; + } + return length1 === length2; +} + module.exports = addConstantFractions; diff --git a/lib/simplifyExpression/fractionsSearch/cancelLikeTerms.js b/lib/simplifyExpression/fractionsSearch/cancelLikeTerms.js index 9f4fb550..213d185a 100644 --- a/lib/simplifyExpression/fractionsSearch/cancelLikeTerms.js +++ b/lib/simplifyExpression/fractionsSearch/cancelLikeTerms.js @@ -55,8 +55,9 @@ function cancelLikeTerms(node) { // away because we always adjust the exponent in the numerator) else if (isMultiplicationOfTerms(numerator) && !isMultiplicationOfTerms(denominator)) { - const numeratorArgs = Node.Type.isParenthesis(numerator) ? - numerator.content.args : numerator.args; + const prioritizedNumerator = prioritizeEqualConstantArgs(numerator, denominator); + const numeratorArgs = Node.Type.isParenthesis(prioritizedNumerator) ? + prioritizedNumerator.content.args : prioritizedNumerator.args; for (let i = 0; i < numeratorArgs.length; i++) { const cancelStatus = cancelTerms(numeratorArgs[i], denominator); if (cancelStatus.hasChanged) { @@ -93,8 +94,9 @@ function cancelLikeTerms(node) { // e.g. x / (x^2*y) => x^(1-2) / y else if (isMultiplicationOfTerms(denominator) && !isMultiplicationOfTerms(numerator)) { - const denominatorArgs = Node.Type.isParenthesis(denominator) ? - denominator.content.args : denominator.args; + const prioritizedDenominator = prioritizeEqualConstantArgs(denominator, numerator); + const denominatorArgs = Node.Type.isParenthesis(prioritizedDenominator) ? + prioritizedDenominator.content.args : prioritizedDenominator.args; for (let i = 0; i < denominatorArgs.length; i++) { const cancelStatus = cancelTerms(numerator, denominatorArgs[i]); if (cancelStatus.hasChanged) { @@ -125,6 +127,43 @@ function cancelLikeTerms(node) { numerator.content.args : numerator.args; const denominatorArgs = Node.Type.isParenthesis(denominator) ? denominator.content.args : denominator.args; + + const likeTerms = getIndicesOfFirstTwoLikeTerms(numeratorArgs, denominatorArgs); + if (likeTerms){ + const cancelStatus = cancelTerms(numeratorArgs[likeTerms.numeratorIndex], + denominatorArgs[likeTerms.denominatorIndex]); + if (cancelStatus.hasChanged) { + if (cancelStatus.numerator) { + numeratorArgs[likeTerms.numeratorIndex] = cancelStatus.numerator; + } + // if the cancelling out got rid of the numerator node, we remove it + // from the list + else { + numeratorArgs.splice(likeTerms.numeratorIndex, 1); + // if the numerator is now a "multiplication" of only one term, + // change it to just that term + if (numeratorArgs.length === 1) { + newNode.args[0] = numeratorArgs[0]; + } + } + if (cancelStatus.denominator) { + denominatorArgs[likeTerms.denominatorIndex] = cancelStatus.denominator; + } + // if the cancelling out got rid of the denominator node, we remove it + // from the list + else { + denominatorArgs.splice(likeTerms.denominatorIndex, 1); + // if the denominator is now a "multiplication" of only one term, + // change it to just that term + if (denominatorArgs.length === 1) { + newNode.args[1] = denominatorArgs[0]; + } + } + return Node.Status.nodeChanged( + ChangeTypes.CANCEL_TERMS, node, newNode); + } + } + for (let i = 0; i < numeratorArgs.length; i++) { for (let j = 0; j < denominatorArgs.length; j++) { const cancelStatus = cancelTerms(numeratorArgs[i], denominatorArgs[j]); @@ -414,4 +453,39 @@ function cancelCoeffs(numerator, denominator){ return new CancelOutStatus(newNumerator, newDenominator, true); } +function prioritizeEqualConstantArgs(multiplicationNode, compareNode){ + const isParens = Node.Type.isParenthesis(multiplicationNode); + let content = multiplicationNode; + if (isParens){ + content = multiplicationNode.content; + } + const multiplicationFactors = content.args; + for (let i=0; i (5*3)/(2*3) let status = findGCD(newNode, gcd, numeratorValue, denominatorValue); @@ -68,15 +74,18 @@ function findGCD(node, gcd, numeratorValue, denominatorValue) { const gcdNode = Node.Creator.constant(gcd); gcdNode.changeGroup = 1; - const intermediateNumerator = Node.Creator.parenthesis(Node.Creator.operator( - '*', [Node.Creator.constant(numeratorValue/gcd), gcdNode])); + let intermediateNumerator = Node.Creator.constant(numeratorValue); + if (numeratorValue / gcd !== 1) { + intermediateNumerator = Node.Creator.parenthesis(Node.Creator.operator( + '*', [Node.Creator.constant(numeratorValue / gcd), gcdNode])); + } const intermediateDenominator = Node.Creator.parenthesis(Node.Creator.operator( - '*', [Node.Creator.constant(denominatorValue/gcd), gcdNode])); + '*', [Node.Creator.constant(denominatorValue / gcd), gcdNode])); newNode = Node.Creator.operator( - '/', [intermediateNumerator, intermediateDenominator]); + '/', [intermediateNumerator, intermediateDenominator]); return Node.Status.nodeChanged( - ChangeTypes.FIND_GCD, node, newNode, false); + ChangeTypes.FIND_GCD, node, newNode, false); } // Returns a substep where the GCD is cancelled out of numerator and denominator