From 24964629fca405c26f07fe8873ef44c2f4211a1a Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Wed, 19 Jul 2017 15:40:47 -0500 Subject: [PATCH 01/10] Creates three substeps for factorSumProductRule --- lib/factor/factorQuadratic.js | 50 ++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index 14c40992..a6705054 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -209,6 +209,8 @@ function factorPerfectSquare(node, symbol, aValue, bValue, cValue, negate) { // e.g. x^2 + 3x + 2 -> (x + 1)(x + 2) or // or 2x^2 + 5x + 3 -> (2x - 1)(x + 3) function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { + let newNode; + if (bValue && cValue) { // we factor out the gcd first, providing us with a modified expression to // factor with new a, b and c values @@ -225,9 +227,9 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { for (const pair of factorPairs) { if (pair[0] + pair[1] === bValue) { // To factor, we go through some transformations - // TODO: these should be actual substeps - // 1. Break apart the middle term into two terms using our factor pair (p and q): - // e.g. ax^2 + bx + c -> ax^2 + px + qx + c + // TODO: these should be actual substeps *alaina* + // 1. Break apart the middle term into two terms using our factor pair + // (p and q): e.g. ax^2 + bx + c -> ax^2 + px + qx + c // 2. Consider the first two terms together and the second two terms // together (this doesn't require any actual change to the expression) // e.g. first group: [ax^2 + px] and second group: [qx + c] @@ -235,9 +237,34 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { // e.g first group: [ux(rx + s)] and second group [v(rx + s)] // 4. Finish factoring by combining the factored terms through grouping: // e.g. (ux + v)(rx + s) + const substeps = []; + let status; + + // 1. Break apart the middle term into two terms using our factor pair + // (p and q) const pValue = pair[0]; const qValue = pair[1]; + // SUBSTEP: ax^2 + bx + c -> ax^2 + px + qx + c + const sq = Node.Creator.constant(2); + const a = Node.Creator.constant(aValue); + const b = Node.Creator.constant(bValue); + const c = Node.Creator.constant(cValue); + const p = Node.Creator.constant(pValue); + const q = Node.Creator.constant(qValue); + const ax2 = Node.Creator.polynomialTerm(symbol, sq, a); + const px = Node.Creator.polynomialTerm(symbol, null, p); + const qx = Node.Creator.polynomialTerm(symbol, null, q); + newNode = Node.Creator.operator('+', [ax2, px, qx, c], true); + status = newNode.status.nodeChanged( + ChangeTypes.SIMPLIFY_ARITHMETIC, node, newNode); + substeps.push(status); + newNode = Node.Status.resetChangeGroups(status.newNode); + + // 2. Consider the first two terms together and the second two terms + // together - does not require substep + + // 3A. Factor the first group // factor the first group to get u, r and s: // u = gcd(a, p), r = a/u and s = p/u const u = Node.Creator.constant(math.gcd(aValue, pValue)); @@ -249,6 +276,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const firstParen = Node.Creator.parenthesis( Node.Creator.operator('+', [rx, s])); + // 3B. Factor the second group // factor the second group to get v, we don't need to find r and s again // v = gcd(c, q) let vValue = math.gcd(cValue, qValue); @@ -257,13 +285,21 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { } const v = Node.Creator.constant(vValue); + // SUBSTEP: ux(rx + s) + v(rx + s) + const firstGroup = Node.Creator.operator('*', [ux, firstParen], true); + const secondGroup = Node.Creator.operator('*', [v, firstParen], true); + newNode = Node.Creator.operator('+', [firstGroup, secondGroup]); + status = newNode.status.nodeChanged( + ChangeTypes.FACTOR_SYMBOL, node, newNode); + substeps.push(status); + newNode = Node.Status.resetChangeGroups(status.newNode); + // create the second parenthesis const ux = Node.Creator.polynomialTerm(symbol, null, u); const secondParen = Node.Creator.parenthesis( Node.Creator.operator('+', [ux, v])); // create a node in the general factored form for expression - let newNode; if (gcd === 1) { newNode = Node.Creator.operator( '*', [firstParen, secondParen], true); @@ -277,6 +313,12 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { newNode = Negative.negate(newNode); } + // SUBSTEP: (ux + v)(rx + s) + status = newNode.status.nodeChanged( + ChangeTypes.FACTOR_SUM_PRODUCT_RULE, node, newNode); + substeps.push(status); + newNode = Node.Status.resetChangeGroups(status.newNode); + return Node.Status.nodeChanged( ChangeTypes.FACTOR_SUM_PRODUCT_RULE, node, newNode); } From e2495a28d597320dc8b77219c4ff31174268628c Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Fri, 21 Jul 2017 10:33:11 -0500 Subject: [PATCH 02/10] Fixes casing for all Node.Status.nodeChanged() --- lib/factor/factorQuadratic.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index a6705054..ff617ccf 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -256,7 +256,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const px = Node.Creator.polynomialTerm(symbol, null, p); const qx = Node.Creator.polynomialTerm(symbol, null, q); newNode = Node.Creator.operator('+', [ax2, px, qx, c], true); - status = newNode.status.nodeChanged( + status = Node.Status.nodeChanged( ChangeTypes.SIMPLIFY_ARITHMETIC, node, newNode); substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); @@ -285,19 +285,20 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { } const v = Node.Creator.constant(vValue); + // create the second parenthesis + const ux = Node.Creator.polynomialTerm(symbol, null, u); + const secondParen = Node.Creator.parenthesis( + Node.Creator.operator('+', [ux, v])); + // SUBSTEP: ux(rx + s) + v(rx + s) const firstGroup = Node.Creator.operator('*', [ux, firstParen], true); const secondGroup = Node.Creator.operator('*', [v, firstParen], true); newNode = Node.Creator.operator('+', [firstGroup, secondGroup]); - status = newNode.status.nodeChanged( + status = Node.Status.nodeChanged( ChangeTypes.FACTOR_SYMBOL, node, newNode); substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); - // create the second parenthesis - const ux = Node.Creator.polynomialTerm(symbol, null, u); - const secondParen = Node.Creator.parenthesis( - Node.Creator.operator('+', [ux, v])); // create a node in the general factored form for expression if (gcd === 1) { @@ -314,7 +315,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { } // SUBSTEP: (ux + v)(rx + s) - status = newNode.status.nodeChanged( + status = Node.Status.nodeChanged( ChangeTypes.FACTOR_SUM_PRODUCT_RULE, node, newNode); substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); From 3704369fac9ea6359fd0b87f242dce4bed762a9a Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Sat, 22 Jul 2017 13:21:06 -0500 Subject: [PATCH 03/10] Removes unused const b --- lib/factor/factorQuadratic.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index ff617ccf..a221a5e1 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -248,7 +248,6 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { // SUBSTEP: ax^2 + bx + c -> ax^2 + px + qx + c const sq = Node.Creator.constant(2); const a = Node.Creator.constant(aValue); - const b = Node.Creator.constant(bValue); const c = Node.Creator.constant(cValue); const p = Node.Creator.constant(pValue); const q = Node.Creator.constant(qValue); From aee480f613f0d414d09821a01b465c7beb01cd96 Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Sat, 22 Jul 2017 21:57:56 -0500 Subject: [PATCH 04/10] Adds substeps 2, 3A, and 3B in factorQuadratic --- lib/ChangeTypes.js | 4 ++- lib/factor/factorQuadratic.js | 57 ++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/lib/ChangeTypes.js b/lib/ChangeTypes.js index e0ceaf34..157d19ad 100644 --- a/lib/ChangeTypes.js +++ b/lib/ChangeTypes.js @@ -35,12 +35,14 @@ module.exports = { // e.g. 2 - - 3 -> 2 + 3 RESOLVE_DOUBLE_MINUS: 'RESOLVE_DOUBLE_MINUS', - // COLLECT AND COMBINE + // COLLECT AND COMBINE AND BREAK UP // e.g. 2 + x + 3 + x -> 5 + 2x COLLECT_AND_COMBINE_LIKE_TERMS: 'COLLECT_AND_COMBINE_LIKE_TERMS', // e.g. x + 2 + x^2 + x + 4 -> x^2 + (x + x) + (4 + 2) COLLECT_LIKE_TERMS: 'COLLECT_LIKE_TERMS', + // e.g. 2x^2 + 4x + 2 -> 2x^2 + 2x + 2x + 2 + BREAK_UP_TERM: 'BREAK_UP_TERM', // MULTIPLYING CONSTANT POWERS // e.g. 10^2 * 10^3 -> 10^(2+3) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index a221a5e1..7622359b 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -227,7 +227,6 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { for (const pair of factorPairs) { if (pair[0] + pair[1] === bValue) { // To factor, we go through some transformations - // TODO: these should be actual substeps *alaina* // 1. Break apart the middle term into two terms using our factor pair // (p and q): e.g. ax^2 + bx + c -> ax^2 + px + qx + c // 2. Consider the first two terms together and the second two terms @@ -240,44 +239,57 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const substeps = []; let status; - // 1. Break apart the middle term into two terms using our factor pair - // (p and q) + // STEP 1 const pValue = pair[0]; const qValue = pair[1]; - - // SUBSTEP: ax^2 + bx + c -> ax^2 + px + qx + c - const sq = Node.Creator.constant(2); const a = Node.Creator.constant(aValue); const c = Node.Creator.constant(cValue); const p = Node.Creator.constant(pValue); const q = Node.Creator.constant(qValue); - const ax2 = Node.Creator.polynomialTerm(symbol, sq, a); + const ax2 = Node.Creator.polynomialTerm(symbol, Node.Creator.constant(2), a); const px = Node.Creator.polynomialTerm(symbol, null, p); const qx = Node.Creator.polynomialTerm(symbol, null, q); + + // SUBSTEP: ax^2 + bx + c -> ax^2 + px + qx + c newNode = Node.Creator.operator('+', [ax2, px, qx, c], true); status = Node.Status.nodeChanged( - ChangeTypes.SIMPLIFY_ARITHMETIC, node, newNode); + ChangeTypes.BREAK_UP_TERM, node, newNode); substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); - // 2. Consider the first two terms together and the second two terms - // together - does not require substep + // STEP 2 + const firstTerm = Node.Creator.parenthesis( + Node.Creator.operator('+', [ax2, px])); + const secondTerm = Node.Creator.parenthesis( + Node.Creator.operator('+', [qx, c])); + + // SUBSTEP: ax^2 + px + qx + c -> (ax^2 + px) + (qx + c) + newNode = Node.Creator.operator('+', [firstTerm, secondTerm], true); + status = Node.Status.nodeChanged( + ChangeTypes.COLLECT_LIKE_TERMS, node, newNode); + substeps.push(status); + newNode = Node.Status.resetChangeGroups(status.newNode); - // 3A. Factor the first group - // factor the first group to get u, r and s: - // u = gcd(a, p), r = a/u and s = p/u + // STEP 3A (first group) const u = Node.Creator.constant(math.gcd(aValue, pValue)); const r = Node.Creator.constant(aValue/u); const s = Node.Creator.constant(pValue/u); + const ux = Node.Creator.polynomialTerm(symbol, null, u); // create the first group's part that's in parentheses: (rx + s) const rx = Node.Creator.polynomialTerm(symbol, null, r); const firstParen = Node.Creator.parenthesis( Node.Creator.operator('+', [rx, s])); - // 3B. Factor the second group - // factor the second group to get v, we don't need to find r and s again - // v = gcd(c, q) + // SUBSTEP: (ax^2 + px) + (qx + c) -> ux(rx + s) + (qx + c) + const firstFactoredGroup = Node.Creator.operator('*', [ux, firstParen], true); + newNode = Node.Creator.operator('+', [firstFactoredGroup, secondTerm], true); + status = Node.Status.nodeChanged( + ChangeTypes.FACTOR_SYMBOL, node, newNode); + substeps.push(status); + newNode = Node.Status.resetChangeGroups(status.newNode); + + // STEP 3B (second group) let vValue = math.gcd(cValue, qValue); if (qValue < 0) { vValue = vValue * -1; @@ -285,21 +297,18 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const v = Node.Creator.constant(vValue); // create the second parenthesis - const ux = Node.Creator.polynomialTerm(symbol, null, u); const secondParen = Node.Creator.parenthesis( Node.Creator.operator('+', [ux, v])); - // SUBSTEP: ux(rx + s) + v(rx + s) - const firstGroup = Node.Creator.operator('*', [ux, firstParen], true); - const secondGroup = Node.Creator.operator('*', [v, firstParen], true); - newNode = Node.Creator.operator('+', [firstGroup, secondGroup]); + // SUBSTEP: ux(rx + s) + (qx + c) -> ux(rx + s) + v(rx + s) + const secondFactoredGroup = Node.Creator.operator('*', [v, firstParen], true); + newNode = Node.Creator.operator('+', [firstFactoredGroup, secondFactoredGroup], true); status = Node.Status.nodeChanged( ChangeTypes.FACTOR_SYMBOL, node, newNode); substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); - - // create a node in the general factored form for expression + // STEP 4 if (gcd === 1) { newNode = Node.Creator.operator( '*', [firstParen, secondParen], true); @@ -313,7 +322,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { newNode = Negative.negate(newNode); } - // SUBSTEP: (ux + v)(rx + s) + // SUBSTEP: ux(rx + s) + v(rx + s) -> (ux + v)(rx + s) status = Node.Status.nodeChanged( ChangeTypes.FACTOR_SUM_PRODUCT_RULE, node, newNode); substeps.push(status); From 10a4e9ef42fce53467f371bcd00f828468d37a64 Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Thu, 3 Aug 2017 06:21:30 -0500 Subject: [PATCH 05/10] Fixes substep comments & moves BREAK_UP_TERM to factoring changegroups --- lib/ChangeTypes.js | 4 ++-- lib/factor/factorQuadratic.js | 15 +++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/ChangeTypes.js b/lib/ChangeTypes.js index 157d19ad..9f8c9e5a 100644 --- a/lib/ChangeTypes.js +++ b/lib/ChangeTypes.js @@ -41,8 +41,6 @@ module.exports = { COLLECT_AND_COMBINE_LIKE_TERMS: 'COLLECT_AND_COMBINE_LIKE_TERMS', // e.g. x + 2 + x^2 + x + 4 -> x^2 + (x + x) + (4 + 2) COLLECT_LIKE_TERMS: 'COLLECT_LIKE_TERMS', - // e.g. 2x^2 + 4x + 2 -> 2x^2 + 2x + 2x + 2 - BREAK_UP_TERM: 'BREAK_UP_TERM', // MULTIPLYING CONSTANT POWERS // e.g. 10^2 * 10^3 -> 10^(2+3) @@ -187,4 +185,6 @@ module.exports = { FACTOR_PERFECT_SQUARE: 'FACTOR_PERFECT_SQUARE', // e.g. x^2 + 3x + 2 -> (x + 1)(x + 2) FACTOR_SUM_PRODUCT_RULE: 'FACTOR_SUM_PRODUCT_RULE', + // e.g. 2x^2 + 4x + 2 -> 2x^2 + 2x + 2x + 2 + BREAK_UP_TERM: 'BREAK_UP_TERM', }; diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index 7622359b..e088d314 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -239,7 +239,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const substeps = []; let status; - // STEP 1 + // SUBSTEP 1: ax^2 + bx + c -> ax^2 + px + qx + c const pValue = pair[0]; const qValue = pair[1]; const a = Node.Creator.constant(aValue); @@ -250,27 +250,25 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const px = Node.Creator.polynomialTerm(symbol, null, p); const qx = Node.Creator.polynomialTerm(symbol, null, q); - // SUBSTEP: ax^2 + bx + c -> ax^2 + px + qx + c newNode = Node.Creator.operator('+', [ax2, px, qx, c], true); status = Node.Status.nodeChanged( ChangeTypes.BREAK_UP_TERM, node, newNode); substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); - // STEP 2 + // STEP 2: ax^2 + px + qx + c -> (ax^2 + px) + (qx + c) const firstTerm = Node.Creator.parenthesis( Node.Creator.operator('+', [ax2, px])); const secondTerm = Node.Creator.parenthesis( Node.Creator.operator('+', [qx, c])); - // SUBSTEP: ax^2 + px + qx + c -> (ax^2 + px) + (qx + c) newNode = Node.Creator.operator('+', [firstTerm, secondTerm], true); status = Node.Status.nodeChanged( ChangeTypes.COLLECT_LIKE_TERMS, node, newNode); substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); - // STEP 3A (first group) + // SUBSTEP 3A: (ax^2 + px) + (qx + c) -> ux(rx + s) + (qx + c) const u = Node.Creator.constant(math.gcd(aValue, pValue)); const r = Node.Creator.constant(aValue/u); const s = Node.Creator.constant(pValue/u); @@ -281,7 +279,6 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const firstParen = Node.Creator.parenthesis( Node.Creator.operator('+', [rx, s])); - // SUBSTEP: (ax^2 + px) + (qx + c) -> ux(rx + s) + (qx + c) const firstFactoredGroup = Node.Creator.operator('*', [ux, firstParen], true); newNode = Node.Creator.operator('+', [firstFactoredGroup, secondTerm], true); status = Node.Status.nodeChanged( @@ -289,7 +286,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); - // STEP 3B (second group) + // STEP 3B: ux(rx + s) + (qx + c) -> ux(rx + s) + v(rx + s) let vValue = math.gcd(cValue, qValue); if (qValue < 0) { vValue = vValue * -1; @@ -300,7 +297,6 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const secondParen = Node.Creator.parenthesis( Node.Creator.operator('+', [ux, v])); - // SUBSTEP: ux(rx + s) + (qx + c) -> ux(rx + s) + v(rx + s) const secondFactoredGroup = Node.Creator.operator('*', [v, firstParen], true); newNode = Node.Creator.operator('+', [firstFactoredGroup, secondFactoredGroup], true); status = Node.Status.nodeChanged( @@ -308,7 +304,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { substeps.push(status); newNode = Node.Status.resetChangeGroups(status.newNode); - // STEP 4 + // STEP 4: ux(rx + s) + v(rx + s) -> (ux + v)(rx + s) if (gcd === 1) { newNode = Node.Creator.operator( '*', [firstParen, secondParen], true); @@ -322,7 +318,6 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { newNode = Negative.negate(newNode); } - // SUBSTEP: ux(rx + s) + v(rx + s) -> (ux + v)(rx + s) status = Node.Status.nodeChanged( ChangeTypes.FACTOR_SUM_PRODUCT_RULE, node, newNode); substeps.push(status); From 551d5dcb452d0c37c375cb8459274b0d84bfebbe Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Wed, 9 Aug 2017 10:34:43 -0500 Subject: [PATCH 06/10] Adds tests for factorSumProductRule substeps --- lib/factor/factorQuadratic.js | 2 +- test/factor/factorQuadratic.test.js | 61 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index e088d314..71b66da0 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -324,7 +324,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { newNode = Node.Status.resetChangeGroups(status.newNode); return Node.Status.nodeChanged( - ChangeTypes.FACTOR_SUM_PRODUCT_RULE, node, newNode); + ChangeTypes.FACTOR_SUM_PRODUCT_RULE, node, newNode, true, substeps); } } } diff --git a/test/factor/factorQuadratic.test.js b/test/factor/factorQuadratic.test.js index 8abc9fc2..0060c3f1 100644 --- a/test/factor/factorQuadratic.test.js +++ b/test/factor/factorQuadratic.test.js @@ -46,3 +46,64 @@ describe('factorQuadratic', function () { ]; tests.forEach(t => testFactorQuadratic(t[0], t[1])); }); + +function testFactorSumProductRuleSubsteps(exprString, outputList) { + const lastString = outputList[outputList.length - 1]; + TestUtil.testSubsteps(factorQuadratic, exprString, outputList, lastString); +} + +describe('factorSumProductRule', function() { + const tests = [ + // sum product rule + ['x^2 + 3x + 2', + ['x^2 + x + 2x + 2', + '(x^2 + x) + (2x + 2)', + 'x * (x + 1) + (2x + 2)', + 'x * (x + 1) + 2 * (x + 1)', + '(x + 1) * (x + 2)'] + ], + ['x^2 - 3x + 2', + ['x^2 - x - 2x + 2', + '(x^2 - x) + (-2x + 2)', + 'x * (x - 1) + (-2x + 2)', + 'x * (x - 1) - 2 * (x - 1)', + '(x - 1) * (x - 2)'] + ], + ['x^2 + x - 2', + ['x^2 - x + 2x - 2', + '(x^2 - x) + (2x - 2)', + 'x * (x - 1) + (2x - 2)', + 'x * (x - 1) + 2 * (x - 1)', + '(x - 1) * (x + 2)'] + ], + ['-x^2 - 3x - 2', + ['x^2 + x + 2x + 2', // per factorQuadratic, negative is added back in at the end + '(x^2 + x) + (2x + 2)', + 'x * (x + 1) + (2x + 2)', + 'x * (x + 1) + 2 * (x + 1)', + '-(x + 1) * (x + 2)'] + ], + ['2x^2 + 5x + 3', + ['2x^2 + 2x + 3x + 3', + '(2x^2 + 2x) + (3x + 3)', + '2x * (x + 1) + (3x + 3)', + '2x * (x + 1) + 3 * (x + 1)', + '(x + 1) * (2x + 3)'] + ], + ['2x^2 - 5x - 3', + ['2x^2 + x - 6x - 3', + '(2x^2 + x) + (-6x - 3)', + 'x * (2x + 1) + (-6x - 3)', + 'x * (2x + 1) - 3 * (2x + 1)', + '(2x + 1) * (x - 3)'] + ], + ['2x^2 - 5x + 3', + ['2x^2 - 2x - 3x + 3', + '(2x^2 - 2x) + (-3x + 3)', + '2x * (x - 1) + (-3x + 3)', + '2x * (x - 1) - 3 * (x - 1)', + '(x - 1) * (2x - 3)'] + ], + ]; + tests.forEach(t => testFactorSumProductRuleSubsteps(t[0], t[1])); +}); From 2470634013abfea29a347c6e94adf3b1545361e4 Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Wed, 9 Aug 2017 14:30:25 -0500 Subject: [PATCH 07/10] Fixes unary minus edge case for factorSumProductRule tests --- lib/factor/factorQuadratic.js | 12 ++++++++++++ test/factor/factorQuadratic.test.js | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index 71b66da0..ec79a6f1 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -251,6 +251,9 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const qx = Node.Creator.polynomialTerm(symbol, null, q); newNode = Node.Creator.operator('+', [ax2, px, qx, c], true); + if (negate) { + newNode = Negative.negate(newNode); + } status = Node.Status.nodeChanged( ChangeTypes.BREAK_UP_TERM, node, newNode); substeps.push(status); @@ -263,6 +266,9 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { Node.Creator.operator('+', [qx, c])); newNode = Node.Creator.operator('+', [firstTerm, secondTerm], true); + if (negate) { + newNode = Negative.negate(newNode); + } status = Node.Status.nodeChanged( ChangeTypes.COLLECT_LIKE_TERMS, node, newNode); substeps.push(status); @@ -281,6 +287,9 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const firstFactoredGroup = Node.Creator.operator('*', [ux, firstParen], true); newNode = Node.Creator.operator('+', [firstFactoredGroup, secondTerm], true); + if (negate) { + newNode = Negative.negate(newNode); + } status = Node.Status.nodeChanged( ChangeTypes.FACTOR_SYMBOL, node, newNode); substeps.push(status); @@ -299,6 +308,9 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const secondFactoredGroup = Node.Creator.operator('*', [v, firstParen], true); newNode = Node.Creator.operator('+', [firstFactoredGroup, secondFactoredGroup], true); + if (negate) { + newNode = Negative.negate(newNode); + } status = Node.Status.nodeChanged( ChangeTypes.FACTOR_SYMBOL, node, newNode); substeps.push(status); diff --git a/test/factor/factorQuadratic.test.js b/test/factor/factorQuadratic.test.js index 0060c3f1..0a5d1e42 100644 --- a/test/factor/factorQuadratic.test.js +++ b/test/factor/factorQuadratic.test.js @@ -77,10 +77,10 @@ describe('factorSumProductRule', function() { '(x - 1) * (x + 2)'] ], ['-x^2 - 3x - 2', - ['x^2 + x + 2x + 2', // per factorQuadratic, negative is added back in at the end - '(x^2 + x) + (2x + 2)', - 'x * (x + 1) + (2x + 2)', - 'x * (x + 1) + 2 * (x + 1)', + ['-(x^2 + x + 2x + 2)', // per factorQuadratic, negative is added back in at the end + '-((x^2 + x) + (2x + 2))', + '-(x * (x + 1) + (2x + 2))', + '-(x * (x + 1) + 2 * (x + 1))', '-(x + 1) * (x + 2)'] ], ['2x^2 + 5x + 3', From 5328883f1a56a961aec8db602455b075fac31874 Mon Sep 17 00:00:00 2001 From: Alaina Kafkes Date: Wed, 9 Aug 2017 15:02:57 -0500 Subject: [PATCH 08/10] Adds optional substep for unary minus edge cases in factorSumProductRule --- lib/factor/factorQuadratic.js | 19 ++++++++++++++++--- test/factor/factorQuadratic.test.js | 3 ++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index ec79a6f1..46857493 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -239,14 +239,27 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const substeps = []; let status; + const a = Node.Creator.constant(aValue); + const b = Node.Creator.constant(bValue); + const c = Node.Creator.constant(cValue); + const ax2 = Node.Creator.polynomialTerm(symbol, Node.Creator.constant(2), a); + const bx = Node.Creator.polynomialTerm(symbol, null, b); + + // OPTIONAL SUBSTEP: ax^2 + bx + c -> -(-ax^2 - bx - c) + if (negate) { + newNode = Node.Creator.operator('+', [ax2, bx, c], true); + newNode = Negative.negate(newNode); + status = Node.Status.nodeChanged( + ChangeTypes.REARRANGE_COEFF, node, newNode); + substeps.push(status); + newNode = Node.Status.resetChangeGroups(status.newNode); + } + // SUBSTEP 1: ax^2 + bx + c -> ax^2 + px + qx + c const pValue = pair[0]; const qValue = pair[1]; - const a = Node.Creator.constant(aValue); - const c = Node.Creator.constant(cValue); const p = Node.Creator.constant(pValue); const q = Node.Creator.constant(qValue); - const ax2 = Node.Creator.polynomialTerm(symbol, Node.Creator.constant(2), a); const px = Node.Creator.polynomialTerm(symbol, null, p); const qx = Node.Creator.polynomialTerm(symbol, null, q); diff --git a/test/factor/factorQuadratic.test.js b/test/factor/factorQuadratic.test.js index 0a5d1e42..0247d824 100644 --- a/test/factor/factorQuadratic.test.js +++ b/test/factor/factorQuadratic.test.js @@ -77,7 +77,8 @@ describe('factorSumProductRule', function() { '(x - 1) * (x + 2)'] ], ['-x^2 - 3x - 2', - ['-(x^2 + x + 2x + 2)', // per factorQuadratic, negative is added back in at the end + ['-(x^2 + 3x + 2)', + '-(x^2 + x + 2x + 2)', '-((x^2 + x) + (2x + 2))', '-(x * (x + 1) + (2x + 2))', '-(x * (x + 1) + 2 * (x + 1))', From b2b06ac570eef596cd2747011ab97f0af19bbc1b Mon Sep 17 00:00:00 2001 From: Evy Kassirer Date: Wed, 9 Aug 2017 13:21:40 -0700 Subject: [PATCH 09/10] clarify comment such a tiny nit I didn't want to ask you to do it :p --- lib/factor/factorQuadratic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index 46857493..5eca4f50 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -245,7 +245,7 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const ax2 = Node.Creator.polynomialTerm(symbol, Node.Creator.constant(2), a); const bx = Node.Creator.polynomialTerm(symbol, null, b); - // OPTIONAL SUBSTEP: ax^2 + bx + c -> -(-ax^2 - bx - c) + // OPTIONAL SUBSTEP (this happens iff a is negative): ax^2 + bx + c -> -(-ax^2 - bx - c) if (negate) { newNode = Node.Creator.operator('+', [ax2, bx, c], true); newNode = Negative.negate(newNode); From ffc309813e7170d0f425ea7c3f874c5db1c26dfa Mon Sep 17 00:00:00 2001 From: Evy Kassirer Date: Wed, 9 Aug 2017 13:22:44 -0700 Subject: [PATCH 10/10] oops line length --- lib/factor/factorQuadratic.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/factor/factorQuadratic.js b/lib/factor/factorQuadratic.js index 5eca4f50..e276784a 100644 --- a/lib/factor/factorQuadratic.js +++ b/lib/factor/factorQuadratic.js @@ -245,7 +245,8 @@ function factorSumProductRule(node, symbol, aValue, bValue, cValue, negate) { const ax2 = Node.Creator.polynomialTerm(symbol, Node.Creator.constant(2), a); const bx = Node.Creator.polynomialTerm(symbol, null, b); - // OPTIONAL SUBSTEP (this happens iff a is negative): ax^2 + bx + c -> -(-ax^2 - bx - c) + // OPTIONAL SUBSTEP (this happens iff a is negative) + // ax^2 + bx + c -> -(-ax^2 - bx - c) if (negate) { newNode = Node.Creator.operator('+', [ax2, bx, c], true); newNode = Negative.negate(newNode);