From aee08a1f3eda61231f1c7c2a294ac890743a0287 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Thu, 20 Apr 2017 23:17:46 -0400 Subject: [PATCH 01/10] Issue #76 - Better break-up fraction. This is only solves the initial case posed of (2x+3)/(2x+2). The output is now (2x+2)/(2x+2) + (1)/(2x+2). --- lib/checks/canSimplifyPolynomialTerms.js | 2 ++ lib/checks/index.js | 2 ++ .../breakUpNumeratorSearch/index.js | 31 +++++++++++++++++-- test/checks/checks.test.js | 7 +++++ .../breakUpNumeratorSearch.test.js | 1 + 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/checks/canSimplifyPolynomialTerms.js b/lib/checks/canSimplifyPolynomialTerms.js index eed134d0..5e105f6b 100644 --- a/lib/checks/canSimplifyPolynomialTerms.js +++ b/lib/checks/canSimplifyPolynomialTerms.js @@ -1,4 +1,5 @@ const canAddLikeTermPolynomialNodes = require('./canAddLikeTermPolynomialNodes'); +const canFindDenominatorInNumerator = require('./canFindDenominatorInNumerator'); const canMultiplyLikeTermPolynomialNodes = require('./canMultiplyLikeTermPolynomialNodes'); const canRearrangeCoefficient = require('./canRearrangeCoefficient'); @@ -6,6 +7,7 @@ const canRearrangeCoefficient = require('./canRearrangeCoefficient'); // polynomial terms that can be combined in some way. function canSimplifyPolynomialTerms(node) { return (canAddLikeTermPolynomialNodes(node) || + canFindDenominatorInNumerator(node) || canMultiplyLikeTermPolynomialNodes(node) || canRearrangeCoefficient(node)); } diff --git a/lib/checks/index.js b/lib/checks/index.js index 5bb07252..92825cc8 100644 --- a/lib/checks/index.js +++ b/lib/checks/index.js @@ -1,4 +1,5 @@ const canAddLikeTermPolynomialNodes = require('./canAddLikeTermPolynomialNodes'); +const canFindDenominatorInNumerator = require('./canFindDenominatorInNumerator'); const canMultiplyLikeTermPolynomialNodes = require('./canMultiplyLikeTermPolynomialNodes'); const canRearrangeCoefficient = require('./canRearrangeCoefficient'); const canSimplifyPolynomialTerms = require('./canSimplifyPolynomialTerms'); @@ -8,6 +9,7 @@ const resolvesToConstant = require('./resolvesToConstant'); module.exports = { canAddLikeTermPolynomialNodes, + canFindDenominatorInNumerator, canMultiplyLikeTermPolynomialNodes, canRearrangeCoefficient, canSimplifyPolynomialTerms, diff --git a/lib/simplifyExpression/breakUpNumeratorSearch/index.js b/lib/simplifyExpression/breakUpNumeratorSearch/index.js index 2dcff7a2..7b355c0a 100644 --- a/lib/simplifyExpression/breakUpNumeratorSearch/index.js +++ b/lib/simplifyExpression/breakUpNumeratorSearch/index.js @@ -1,3 +1,4 @@ +const canFindDenominatorInNumerator = require('../../checks/canFindDenominatorInNumerator'); const ChangeTypes = require('../../ChangeTypes'); const Node = require('../../node'); const TreeSearch = require('../../TreeSearch'); @@ -27,8 +28,33 @@ function breakUpNumerator(node) { // At this point, we know that node is a fraction and its numerator is a sum // of terms that can't be collected or combined, so we should break it up. const fractionList = []; - const denominator = node.args[1]; - numerator.args.forEach(arg => { + let denominator = node.args[1]; + + // Check if we can add a constant to make the fraction nicer + // fraction e.g. (2+x)/(5+x) -> (5+x)/(5+x) + 3/(5+x) + if (canFindDenominatorInNumerator(node)) { + let denominatorParenRemoved = false; + if (Node.Type.isParenthesis(denominator)) { + denominatorParenRemoved = true; + denominator = denominator.content; + } + const newNumerator = []; + + // The constant value difference between the numerator and the denominator + const numeratorConstant = parseInt(numerator.args[1].value); + const denominatorConstant = parseInt(denominator.args[1].value); + const addedConstant = numeratorConstant - denominatorConstant; + + newNumerator.push(Node.Creator.constant(addedConstant)); + newNumerator.push(denominator); + + numerator = newNumerator; + + if (denominatorParenRemoved) { + denominator = Node.Creator.parenthesis(denominator); + } + } + numerator.forEach(arg => { const newFraction = Node.Creator.operator('/', [arg, denominator]); newFraction.changeGroup = 1; fractionList.push(newFraction); @@ -41,5 +67,4 @@ function breakUpNumerator(node) { return Node.Status.nodeChanged( ChangeTypes.BREAK_UP_FRACTION, node, newNode, false); } - module.exports = search; diff --git a/test/checks/checks.test.js b/test/checks/checks.test.js index 4df3005f..1f89034a 100644 --- a/test/checks/checks.test.js +++ b/test/checks/checks.test.js @@ -29,3 +29,10 @@ describe('canSimplifyPolynomialTerms addition', function() { ]; tests.forEach(t => testCanCombine(t[0], t[1])); }); + +describe('canSimplifyPolynomialTerms denominator in numerator', function() { + const tests = [ + ['(2x + 3)/(2x + 2)', true], + ]; + tests.forEach(t => testCanCombine(t[0], t[1])); +}); diff --git a/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js b/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js index 73aef5c1..450f6734 100644 --- a/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js +++ b/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js @@ -11,6 +11,7 @@ describe('breakUpNumerator', function() { ['(x+3+y)/3', '(x / 3 + 3/3 + y / 3)'], ['(2+x)/4', '(2/4 + x / 4)'], ['2(x+3)/3', '2 * (x / 3 + 3/3)'], + ['(2x + 3)/(2x + 2)', '(1 / (2x + 2) + (2x + 2) / (2x + 2))'], ]; tests.forEach(t => testBreakUpNumeratorSearch(t[0], t[1])); }); From 6135dad72a0572f157c378edb6735711b13fdd32 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Thu, 20 Apr 2017 23:24:30 -0400 Subject: [PATCH 02/10] add check for denonimator in numerator --- lib/checks/canFindDenominatorInNumerator.js | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 lib/checks/canFindDenominatorInNumerator.js diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js new file mode 100644 index 00000000..02c95859 --- /dev/null +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -0,0 +1,35 @@ +const Node = require('../node'); + +// Returns true if by adding a term you can simplify part of the function into an integer +// e.g. (2x+1)/(2x+3) -> True +// e.g. (2x+1)/(2x^2 + 3) -> False +function canFindDenominatorInNumerator(node) { + if (!Node.Type.isOperator(node) || node.op !== '/' ) { + return false; + } + if (node.args.length !== 2) { + return false; + } + + let numerator = node.args[0]; + let denominator = node.args[1]; + if (Node.Type.isParenthesis(numerator)) { + numerator = numerator.content; + } + if (Node.Type.isParenthesis(denominator)) { + denominator = denominator.content; + } + + if (numerator.op !== '+') { + return false; + } + if (!('args' in denominator)) { + return false; + } + const numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]); + const denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]); + + return numeratorFirstTerm.getSymbolName() === denominatorFirstTerm.getSymbolName(); +} + +module.exports = canFindDenominatorInNumerator; From 322a247d040e4efe66004686d6f41b66e7257305 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Thu, 4 May 2017 10:43:33 -0400 Subject: [PATCH 03/10] Issue #76 - Better break-up fraction Almost completely covers all cases of (ax^1 + b) / (cx^1 + d). The only current holes in this function are for cases where 1) Constant and polynomial term are not ordered 2) When b is equal to 0 'checks/canFindDenonimatorInNumerator': The checks are more specific than before. 'breakUpNumeratorSearch/index.js': Included a multiplier variable to see how much times the denominator is in the numerator. Instead of using numerator.args[1] it's now the args[num_n-1] to get the last index, this way as long as it's sorted we'll always get the constant. 'checks/checks.test.js': More test cases, included falses 'breakUpNumeratorSearch/breakUpNumeratorSearch.test.js': More test cases --- lib/checks/canFindDenominatorInNumerator.js | 39 +++++++++++++++---- .../breakUpNumeratorSearch/index.js | 25 +++++++++--- test/checks/checks.test.js | 3 ++ .../breakUpNumeratorSearch.test.js | 8 +++- 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js index 02c95859..ad7cb69a 100644 --- a/lib/checks/canFindDenominatorInNumerator.js +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -1,7 +1,9 @@ const Node = require('../node'); -// Returns true if by adding a term you can simplify part of the function into an integer -// e.g. (2x+1)/(2x+3) -> True +// Returns true if by adding a term you can simplify part of the function into +// an integer +// e.g. (2x+1)/(2x+3) -> True because of the following simplification +// (2x+1)/(2x+3) -> (2x + 3)/(2x + 3) - 2/(2x + 3) -> 1 - 2/(2x + 3) // e.g. (2x+1)/(2x^2 + 3) -> False function canFindDenominatorInNumerator(node) { if (!Node.Type.isOperator(node) || node.op !== '/' ) { @@ -10,7 +12,6 @@ function canFindDenominatorInNumerator(node) { if (node.args.length !== 2) { return false; } - let numerator = node.args[0]; let denominator = node.args[1]; if (Node.Type.isParenthesis(numerator)) { @@ -19,17 +20,39 @@ function canFindDenominatorInNumerator(node) { if (Node.Type.isParenthesis(denominator)) { denominator = denominator.content; } + if (!(numerator.op === '+' || numerator.op === '-' || + denominator.op === '+' || numerator.op === '-')) { + return false; + } + if (denominator.op !== '+') { + return false; + } - if (numerator.op !== '+') { + let numeratorFirstTerm; + if (numerator.op === '+') { + numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]); + } else if (numerator.op === '*') { + numeratorFirstTerm = new Node.PolynomialTerm(numerator) + } + + let denominatorFirstTerm; + if (denominator.op === '+') { + denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]); + } else if (denominator.op === '*') { + denominatorFirstTerm = new Node.PolynomialTerm(denominator) + } + if (!(numeratorFirstTerm)) { return false; } - if (!('args' in denominator)) { + if (!(denominatorFirstTerm)) { + return false; + } + + if (!(numeratorFirstTerm.getSymbolName() === 'x' && denominatorFirstTerm.getSymbolName() === 'x')) { return false; } - const numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]); - const denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]); - return numeratorFirstTerm.getSymbolName() === denominatorFirstTerm.getSymbolName(); + return true; } module.exports = canFindDenominatorInNumerator; diff --git a/lib/simplifyExpression/breakUpNumeratorSearch/index.js b/lib/simplifyExpression/breakUpNumeratorSearch/index.js index 7b355c0a..b6ad64ed 100644 --- a/lib/simplifyExpression/breakUpNumeratorSearch/index.js +++ b/lib/simplifyExpression/breakUpNumeratorSearch/index.js @@ -30,8 +30,8 @@ function breakUpNumerator(node) { const fractionList = []; let denominator = node.args[1]; - // Check if we can add a constant to make the fraction nicer - // fraction e.g. (2+x)/(5+x) -> (5+x)/(5+x) + 3/(5+x) + // Check if we can add/substract a constant to make the fraction nicer + // fraction e.g. (2+x)/(5+x) -> (5+x)/(5+x) - 3/(5+x) if (canFindDenominatorInNumerator(node)) { let denominatorParenRemoved = false; if (Node.Type.isParenthesis(denominator)) { @@ -41,12 +41,25 @@ function breakUpNumerator(node) { const newNumerator = []; // The constant value difference between the numerator and the denominator - const numeratorConstant = parseInt(numerator.args[1].value); - const denominatorConstant = parseInt(denominator.args[1].value); - const addedConstant = numeratorConstant - denominatorConstant; + num_n = numerator.args.length + den_n = denominator.args.length + numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]) + denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]) + const numeratorPolyCoeff = numeratorFirstTerm.getCoeffValue() + const denominatorPolyCoeff = denominatorFirstTerm.getCoeffValue() + const multiplier = numeratorPolyCoeff / denominatorPolyCoeff + const numeratorConstant = parseInt(numerator.args[num_n-1].value) || 0; + const denominatorConstant = parseInt(denominator.args[den_n-1].value) || 0; + const addedConstant = numeratorConstant - (denominatorConstant * multiplier); + + if (multiplier == 1) { + newNumerator.push(denominator); + } else { + multiplierNode = Node.Creator.constant(multiplier) + newNumerator.push(Node.Creator.operator('*', [multiplierNode, denominator])); + } newNumerator.push(Node.Creator.constant(addedConstant)); - newNumerator.push(denominator); numerator = newNumerator; diff --git a/test/checks/checks.test.js b/test/checks/checks.test.js index 1f89034a..4b13596c 100644 --- a/test/checks/checks.test.js +++ b/test/checks/checks.test.js @@ -33,6 +33,9 @@ describe('canSimplifyPolynomialTerms addition', function() { describe('canSimplifyPolynomialTerms denominator in numerator', function() { const tests = [ ['(2x + 3)/(2x + 2)', true], + ['(2x+3)/(2x)', false], + ['(5x + 3)/(4)', false], + ['(2x)/(2x + 3)', true], ]; tests.forEach(t => testCanCombine(t[0], t[1])); }); diff --git a/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js b/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js index 450f6734..410cc5dd 100644 --- a/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js +++ b/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js @@ -11,7 +11,13 @@ describe('breakUpNumerator', function() { ['(x+3+y)/3', '(x / 3 + 3/3 + y / 3)'], ['(2+x)/4', '(2/4 + x / 4)'], ['2(x+3)/3', '2 * (x / 3 + 3/3)'], - ['(2x + 3)/(2x + 2)', '(1 / (2x + 2) + (2x + 2) / (2x + 2))'], + ['(2x + 3)/(2x + 2)', '((2x + 2) / (2x + 2) + 1 / (2x + 2))'], + ['(2x - 3)/(2x + 2)', '((2x + 2) / (2x + 2) - 5 / (2x + 2))'], + ['(2x + 3)/(2x)', '(2x / (2x) + 3 / (2x))'], + ['(3 + 2x)/(2x)', '(3 / (2x) + 2x / (2x))'], + ['(4x + 3)/(2x + 2)', '(2 * (2x + 2) / (2x + 2) - 1 / (2x + 2))'], +// ['(2x)/(3 + 2x)', '((3 + 2x) / (3 + 2x) - 3 / (3 + 2x))'], +// ['(2x)/(2x + 3)', '((2x + 3) / (2x + 3)) - 3 / (2x + 3)'], ]; tests.forEach(t => testBreakUpNumeratorSearch(t[0], t[1])); }); From 3c1cac0625072eb1443453655e8e8b3d9285b2d2 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Thu, 4 May 2017 11:02:42 -0400 Subject: [PATCH 04/10] lint --- lib/checks/canFindDenominatorInNumerator.js | 17 ++++++++------ .../breakUpNumeratorSearch/index.js | 23 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js index ad7cb69a..fa71e3bf 100644 --- a/lib/checks/canFindDenominatorInNumerator.js +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -1,9 +1,9 @@ const Node = require('../node'); -// Returns true if by adding a term you can simplify part of the function into +// Returns true if by adding a term you can simplify part of the function into // an integer -// e.g. (2x+1)/(2x+3) -> True because of the following simplification -// (2x+1)/(2x+3) -> (2x + 3)/(2x + 3) - 2/(2x + 3) -> 1 - 2/(2x + 3) +// e.g. (2x+1)/(2x+3) -> True because of the following simplification +// (2x+1)/(2x+3) -> (2x + 3)/(2x + 3) - 2/(2x + 3) -> 1 - 2/(2x + 3) // e.g. (2x+1)/(2x^2 + 3) -> False function canFindDenominatorInNumerator(node) { if (!Node.Type.isOperator(node) || node.op !== '/' ) { @@ -31,16 +31,19 @@ function canFindDenominatorInNumerator(node) { let numeratorFirstTerm; if (numerator.op === '+') { numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]); - } else if (numerator.op === '*') { - numeratorFirstTerm = new Node.PolynomialTerm(numerator) + } + else if (numerator.op === '*') { + numeratorFirstTerm = new Node.PolynomialTerm(numerator); } let denominatorFirstTerm; if (denominator.op === '+') { denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]); - } else if (denominator.op === '*') { - denominatorFirstTerm = new Node.PolynomialTerm(denominator) } + else if (denominator.op === '*') { + denominatorFirstTerm = new Node.PolynomialTerm(denominator); + } + if (!(numeratorFirstTerm)) { return false; } diff --git a/lib/simplifyExpression/breakUpNumeratorSearch/index.js b/lib/simplifyExpression/breakUpNumeratorSearch/index.js index b6ad64ed..f60be186 100644 --- a/lib/simplifyExpression/breakUpNumeratorSearch/index.js +++ b/lib/simplifyExpression/breakUpNumeratorSearch/index.js @@ -41,22 +41,23 @@ function breakUpNumerator(node) { const newNumerator = []; // The constant value difference between the numerator and the denominator - num_n = numerator.args.length - den_n = denominator.args.length - numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]) - denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]) - const numeratorPolyCoeff = numeratorFirstTerm.getCoeffValue() - const denominatorPolyCoeff = denominatorFirstTerm.getCoeffValue() - const multiplier = numeratorPolyCoeff / denominatorPolyCoeff + const num_n = numerator.args.length; + const den_n = denominator.args.length; + const numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]); + const denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]); + const numeratorPolyCoeff = numeratorFirstTerm.getCoeffValue(); + const denominatorPolyCoeff = denominatorFirstTerm.getCoeffValue(); + const multiplier = numeratorPolyCoeff / denominatorPolyCoeff; const numeratorConstant = parseInt(numerator.args[num_n-1].value) || 0; - const denominatorConstant = parseInt(denominator.args[den_n-1].value) || 0; + const denominatorConstant = parseInt(denominator.args[den_n-1].value) || 0; const addedConstant = numeratorConstant - (denominatorConstant * multiplier); - if (multiplier == 1) { + if (multiplier === 1) { newNumerator.push(denominator); - } else { - multiplierNode = Node.Creator.constant(multiplier) + } + else { + const multiplierNode = Node.Creator.constant(multiplier); newNumerator.push(Node.Creator.operator('*', [multiplierNode, denominator])); } newNumerator.push(Node.Creator.constant(addedConstant)); From 1ccdc3bc0b4c57c976db96a22ea237247733723f Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Mon, 15 May 2017 17:26:11 -0400 Subject: [PATCH 05/10] added test cases, checks are more rigorous --- lib/checks/canFindDenominatorInNumerator.js | 59 +++++++++++++++---- .../breakUpNumeratorSearch/index.js | 2 +- ...canMultiplyLikeTermPolynomialNodes.test.js | 2 +- test/checks/checks.test.js | 19 +++++- 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js index fa71e3bf..b70579f0 100644 --- a/lib/checks/canFindDenominatorInNumerator.js +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -6,7 +6,7 @@ const Node = require('../node'); // (2x+1)/(2x+3) -> (2x + 3)/(2x + 3) - 2/(2x + 3) -> 1 - 2/(2x + 3) // e.g. (2x+1)/(2x^2 + 3) -> False function canFindDenominatorInNumerator(node) { - if (!Node.Type.isOperator(node) || node.op !== '/' ) { + if (node.op !== '/' ) { return false; } if (node.args.length !== 2) { @@ -20,29 +20,59 @@ function canFindDenominatorInNumerator(node) { if (Node.Type.isParenthesis(denominator)) { denominator = denominator.content; } - if (!(numerator.op === '+' || numerator.op === '-' || - denominator.op === '+' || numerator.op === '-')) { + + let n_args_length; + // If numerator is '*' op, it signifies a single 'ax', should be assigned a + // length of 1 + if ('args' in numerator && numerator.op !== '*') { + n_args_length = numerator.args.length; + } + else { + n_args_length = 1; + } + let d_args_length; + if ('args' in denominator) { + d_args_length = denominator.args.length; + } + else { + d_args_length = 1; + } + + // If numerator isn't length 2 or length 1 with a polynomial return false + if (!(n_args_length === 2)) { + if (!(n_args_length === 1 || Node.Type.isConstant(numerator))) { + return false; + } + } + if (!(d_args_length === 2)) { return false; } - if (denominator.op !== '+') { + if (!(denominator.op === '+' || denominator.op === '-')) { return false; } - + // Check if second argument is a constant if numerator has two arguments + if (n_args_length === 2) { + if (!(Node.Type.isConstant(numerator.args[1]))) { + return false; + } + } + // Check if denominator's second argument is a constant + if (!(Node.Type.isConstant(denominator.args[1]))) { + return false; + } + // Defines the first term depending on whether there's a coefficient value + // with the first term let numeratorFirstTerm; if (numerator.op === '+') { numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]); } - else if (numerator.op === '*') { + else { numeratorFirstTerm = new Node.PolynomialTerm(numerator); } - let denominatorFirstTerm; if (denominator.op === '+') { denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]); } - else if (denominator.op === '*') { - denominatorFirstTerm = new Node.PolynomialTerm(denominator); - } if (!(numeratorFirstTerm)) { return false; @@ -50,8 +80,13 @@ function canFindDenominatorInNumerator(node) { if (!(denominatorFirstTerm)) { return false; } - - if (!(numeratorFirstTerm.getSymbolName() === 'x' && denominatorFirstTerm.getSymbolName() === 'x')) { + // If an exponent exists, return false + if (numeratorFirstTerm.getExponentNode() || + denominatorFirstTerm.getExponentNode()) { + return false; + } + if (!(numeratorFirstTerm.getSymbolName() === + denominatorFirstTerm.getSymbolName())) { return false; } diff --git a/lib/simplifyExpression/breakUpNumeratorSearch/index.js b/lib/simplifyExpression/breakUpNumeratorSearch/index.js index f60be186..1fd40baa 100644 --- a/lib/simplifyExpression/breakUpNumeratorSearch/index.js +++ b/lib/simplifyExpression/breakUpNumeratorSearch/index.js @@ -30,7 +30,7 @@ function breakUpNumerator(node) { const fractionList = []; let denominator = node.args[1]; - // Check if we can add/substract a constant to make the fraction nicer + // Check if we can add/subtract a constant to make the fraction nicer // fraction e.g. (2+x)/(5+x) -> (5+x)/(5+x) - 3/(5+x) if (canFindDenominatorInNumerator(node)) { let denominatorParenRemoved = false; diff --git a/test/canMultiplyLikeTermPolynomialNodes.test.js b/test/canMultiplyLikeTermPolynomialNodes.test.js index d176dc4d..2d70f2e9 100644 --- a/test/canMultiplyLikeTermPolynomialNodes.test.js +++ b/test/canMultiplyLikeTermPolynomialNodes.test.js @@ -10,7 +10,7 @@ describe('can multiply like term polynomials', () => { const tests = [ ['x^2 * x * x', true], ['x^2 * 3x * x', false], - ['y * y^3', true] + ['y * y^3', true], ]; tests.forEach(t => testCanBeMultiplied(t[0], t[1])); }); diff --git a/test/checks/checks.test.js b/test/checks/checks.test.js index 4b13596c..14264317 100644 --- a/test/checks/checks.test.js +++ b/test/checks/checks.test.js @@ -32,10 +32,23 @@ describe('canSimplifyPolynomialTerms addition', function() { describe('canSimplifyPolynomialTerms denominator in numerator', function() { const tests = [ + ['(x+1)/(x-2)', true], + ['(2x)/(x+4)', true], + ['(x)/(x+4)', true], + ['(x)/(2x+4)', true], + ['(x+3)/(x)', false], // Normal breakup function already solves this ['(2x + 3)/(2x + 2)', true], - ['(2x+3)/(2x)', false], - ['(5x + 3)/(4)', false], - ['(2x)/(2x + 3)', true], + ['(2x+3)/(2x)', false], // Normal breakup function already solves this + ['(2x)/(2x + 2)', true], + ['(5x + 3)/(4)', false], // Normal breakup function already solves this + // Not supported yet + ['(2x)/(2 + 2x)', false], + ['(2 + 2x)/(3x + 4)', false], + ['(x + 3)/(2x^2 + 5)', false], + ['(3x^2 + 3)/(2x^2 + 5)', false], + ['(5x^2 + 3)/(2x + 5)', false], + ['(5x^2-4x + 3)/(2x + 5)', false], + ['(-4x + 3)/(2x^2 + 5x +7)', false], ]; tests.forEach(t => testCanCombine(t[0], t[1])); }); From fd8ebf0f7a2516fe140c3e7b99efc7f92c4bec97 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Mon, 15 May 2017 17:39:32 -0400 Subject: [PATCH 06/10] add comments --- lib/checks/canFindDenominatorInNumerator.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js index b70579f0..2edcccde 100644 --- a/lib/checks/canFindDenominatorInNumerator.js +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -44,13 +44,15 @@ function canFindDenominatorInNumerator(node) { return false; } } + // Function doesn't support denominators with args > 2 + // Tf d_args_length < 2 the normal functionality already covers it if (!(d_args_length === 2)) { return false; } if (!(denominator.op === '+' || denominator.op === '-')) { return false; } - // Check if second argument is a constant if numerator has two arguments + // Check if numerator's second argument is a constant if numerator has two arguments if (n_args_length === 2) { if (!(Node.Type.isConstant(numerator.args[1]))) { return false; @@ -73,18 +75,12 @@ function canFindDenominatorInNumerator(node) { if (denominator.op === '+') { denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]); } - - if (!(numeratorFirstTerm)) { - return false; - } - if (!(denominatorFirstTerm)) { - return false; - } - // If an exponent exists, return false + // If an exponent exists (aka not x^1), return false if (numeratorFirstTerm.getExponentNode() || denominatorFirstTerm.getExponentNode()) { return false; } + // Check that the symbols are the same, Ex. (x+1)/(y+1) would not pass if (!(numeratorFirstTerm.getSymbolName() === denominatorFirstTerm.getSymbolName())) { return false; From f7796513ca3c63d2544266a8fe333e8352f20927 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Tue, 16 May 2017 07:15:46 -0400 Subject: [PATCH 07/10] update --- lib/checks/canFindDenominatorInNumerator.js | 46 +++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js index 2edcccde..14698aef 100644 --- a/lib/checks/canFindDenominatorInNumerator.js +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -5,6 +5,19 @@ const Node = require('../node'); // e.g. (2x+1)/(2x+3) -> True because of the following simplification // (2x+1)/(2x+3) -> (2x + 3)/(2x + 3) - 2/(2x + 3) -> 1 - 2/(2x + 3) // e.g. (2x+1)/(2x^2 + 3) -> False +// ========================================================================= +// CHECKS +// - Check for division in parent node +// - Check that the number of arguments in parent node is 2 +// - Check that the number of numerator args is equal to 2 or 1. In the case +// - of 1, we need that node to have a symbol (so it can't be a constant) +// - Check that denominator has two args +// - Check that the denominator op is '+' or '-' +// - If the numerator has 2 args, check that the second arg is a constant node +// - Check if the denominator's second arg is a constant node +// - Check to see that the denominator and numerator both don't have exponents +// - Check to see that the denominator and numerator have the same symbol + function canFindDenominatorInNumerator(node) { if (node.op !== '/' ) { return false; @@ -21,45 +34,45 @@ function canFindDenominatorInNumerator(node) { denominator = denominator.content; } - let n_args_length; + let numeratorArgsLength; // If numerator is '*' op, it signifies a single 'ax', should be assigned a - // length of 1 - if ('args' in numerator && numerator.op !== '*') { - n_args_length = numerator.args.length; + // length of 2 + if ('args' in numerator && + (numerator.op === '+' || numerator.op === '-')) { + numeratorArgsLength = numerator.args.length; } else { - n_args_length = 1; + numeratorArgsLength = 1; } - let d_args_length; + let denominatorArgsLength; if ('args' in denominator) { - d_args_length = denominator.args.length; + denominatorArgsLength = denominator.args.length; } else { - d_args_length = 1; + denominatorArgsLength = 1; } - // If numerator isn't length 2 or length 1 with a polynomial return false - if (!(n_args_length === 2)) { - if (!(n_args_length === 1 || Node.Type.isConstant(numerator))) { + if (numeratorArgsLength !== 2) { + if (!(numeratorArgsLength === 1) && Node.Type.isConstant(numerator)) { return false; } } // Function doesn't support denominators with args > 2 - // Tf d_args_length < 2 the normal functionality already covers it - if (!(d_args_length === 2)) { + // If denominatorArgsLength = 1 the normal functionality already covers it + if (denominatorArgsLength !== 2) { return false; } if (!(denominator.op === '+' || denominator.op === '-')) { return false; } // Check if numerator's second argument is a constant if numerator has two arguments - if (n_args_length === 2) { - if (!(Node.Type.isConstant(numerator.args[1]))) { + if (numeratorArgsLength === 2) { + if (!Node.Type.isConstant(numerator.args[1])) { return false; } } // Check if denominator's second argument is a constant - if (!(Node.Type.isConstant(denominator.args[1]))) { + if (!Node.Type.isConstant(denominator.args[1])) { return false; } // Defines the first term depending on whether there's a coefficient value @@ -85,7 +98,6 @@ function canFindDenominatorInNumerator(node) { denominatorFirstTerm.getSymbolName())) { return false; } - return true; } From 41e9223e6d59601342c2751a4c97f5a93ab642f4 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Tue, 16 May 2017 07:32:33 -0400 Subject: [PATCH 08/10] squash line 55-56 into one if statement --- lib/checks/canFindDenominatorInNumerator.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js index 14698aef..e8fadecb 100644 --- a/lib/checks/canFindDenominatorInNumerator.js +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -51,11 +51,10 @@ function canFindDenominatorInNumerator(node) { else { denominatorArgsLength = 1; } - // If numerator isn't length 2 or length 1 with a polynomial return false - if (numeratorArgsLength !== 2) { - if (!(numeratorArgsLength === 1) && Node.Type.isConstant(numerator)) { - return false; - } + // If numerator args isn't length 2 or length 1 with a polynomial return false + if (numeratorArgsLength !== 2 && + (!(numeratorArgsLength === 1 && !Node.Type.isConstant(numerator)))) { + return false; } // Function doesn't support denominators with args > 2 // If denominatorArgsLength = 1 the normal functionality already covers it From 308a90ce1829ae752e3e5836c88da8782a187f17 Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Sat, 20 May 2017 22:14:03 -0400 Subject: [PATCH 09/10] Add changes --- lib/checks/canFindDenominatorInNumerator.js | 49 +++++++++------------ 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/lib/checks/canFindDenominatorInNumerator.js b/lib/checks/canFindDenominatorInNumerator.js index e8fadecb..f7beaf30 100644 --- a/lib/checks/canFindDenominatorInNumerator.js +++ b/lib/checks/canFindDenominatorInNumerator.js @@ -5,26 +5,20 @@ const Node = require('../node'); // e.g. (2x+1)/(2x+3) -> True because of the following simplification // (2x+1)/(2x+3) -> (2x + 3)/(2x + 3) - 2/(2x + 3) -> 1 - 2/(2x + 3) // e.g. (2x+1)/(2x^2 + 3) -> False -// ========================================================================= +// ============================================================================== // CHECKS // - Check for division in parent node -// - Check that the number of arguments in parent node is 2 -// - Check that the number of numerator args is equal to 2 or 1. In the case -// - of 1, we need that node to have a symbol (so it can't be a constant) -// - Check that denominator has two args -// - Check that the denominator op is '+' or '-' -// - If the numerator has 2 args, check that the second arg is a constant node -// - Check if the denominator's second arg is a constant node -// - Check to see that the denominator and numerator both don't have exponents +// - Numerator has to be addition/subtraction of a polynomial term to the power +// of 1 and a constant term, in that order OR a polynomial term to the +// power of 1 +// - Denominator has to be addition/subtraction of a polynomial term to the power +// of 1 and a constant term, in that order. // - Check to see that the denominator and numerator have the same symbol function canFindDenominatorInNumerator(node) { if (node.op !== '/' ) { return false; } - if (node.args.length !== 2) { - return false; - } let numerator = node.args[0]; let denominator = node.args[1]; if (Node.Type.isParenthesis(numerator)) { @@ -35,35 +29,36 @@ function canFindDenominatorInNumerator(node) { } let numeratorArgsLength; - // If numerator is '*' op, it signifies a single 'ax', should be assigned a - // length of 2 - if ('args' in numerator && - (numerator.op === '+' || numerator.op === '-')) { + // If numerator has args, but it's just a polynomial term, length is 1 + // Ex. 3x/2x+3 => numeratorArgsLength=1 + if (Node.PolynomialTerm.isPolynomialTerm(numerator)) { + numeratorArgsLength = 1; + } + // If numerator has args and args are two seperate values length is 2 + // Ex. 3x+4/2x+3 => numeratorArgsLength=2 + else if (numerator.op === '+' || numerator.op === '-') { numeratorArgsLength = numerator.args.length; } + // If numerator doesn't have args and isn't a polynomial term, there's + // nothing the added functionality can do + // Ex. 3/(2x + 3) => False else { - numeratorArgsLength = 1; + return false; } let denominatorArgsLength; - if ('args' in denominator) { + if (denominator.op === '+' || denominator.op === '-') { denominatorArgsLength = denominator.args.length; } + // If denominator doesn't have args, it's length is 1. This case is already + // resolved by splitting the denominator into all the numerators + // Ex. (x + 3)/2x => x/2x + 3/2x else { - denominatorArgsLength = 1; - } - // If numerator args isn't length 2 or length 1 with a polynomial return false - if (numeratorArgsLength !== 2 && - (!(numeratorArgsLength === 1 && !Node.Type.isConstant(numerator)))) { return false; } // Function doesn't support denominators with args > 2 - // If denominatorArgsLength = 1 the normal functionality already covers it if (denominatorArgsLength !== 2) { return false; } - if (!(denominator.op === '+' || denominator.op === '-')) { - return false; - } // Check if numerator's second argument is a constant if numerator has two arguments if (numeratorArgsLength === 2) { if (!Node.Type.isConstant(numerator.args[1])) { From d88605f054dd52e0b734e791aa5d0b4cc85d5fdb Mon Sep 17 00:00:00 2001 From: EltonLaw Date: Sat, 27 May 2017 03:26:32 -0400 Subject: [PATCH 10/10] Add TODO's --- .../breakUpNumeratorSearch/breakUpNumeratorSearch.test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js b/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js index 410cc5dd..b3901a88 100644 --- a/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js +++ b/test/simplifyExpression/breakUpNumeratorSearch/breakUpNumeratorSearch.test.js @@ -16,8 +16,10 @@ describe('breakUpNumerator', function() { ['(2x + 3)/(2x)', '(2x / (2x) + 3 / (2x))'], ['(3 + 2x)/(2x)', '(3 / (2x) + 2x / (2x))'], ['(4x + 3)/(2x + 2)', '(2 * (2x + 2) / (2x + 2) - 1 / (2x + 2))'], -// ['(2x)/(3 + 2x)', '((3 + 2x) / (3 + 2x) - 3 / (3 + 2x))'], -// ['(2x)/(2x + 3)', '((2x + 3) / (2x + 3)) - 3 / (2x + 3)'], + // TODO: Pre-sort numerator and denominator 'args' + // ['(2x)/(3 + 2x)', '((3 + 2x) / (3 + 2x) - 3 / (3 + 2x))'], + // TODO: Fix beginning checks in 'breakUpNumeratorSearch' + // ['(2x)/(2x + 3)', '((2x + 3) / (2x + 3)) - 3 / (2x + 3)'], ]; tests.forEach(t => testBreakUpNumeratorSearch(t[0], t[1])); });