-
Notifications
You must be signed in to change notification settings - Fork 1
Multiplying polynomials #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 44 commits
04bb0ee
f37f48c
5745443
b462d8a
3ca4c7f
adcbc10
3cb89d4
75ab164
3beef3e
c35f087
20bc705
336c5c3
3dbb8cd
2335275
d91eb26
7fdbd7e
e8e86a1
7a3e271
fbd4938
3ea7468
96d2bb9
caa0ae0
14a091f
aa21a98
08b3ca1
03ffdf1
648f0f7
632b525
7403644
2cb2f76
fa1c35c
85f51d0
9f81447
ac44821
56d4872
e02838b
06914d0
510afb7
eb95348
2e67877
b485d9e
7784dd7
ef26741
894bbbc
d73a190
9741fbf
ba2d8f6
da48fc1
d5d9576
8bf1170
9d07321
5b4a5f2
bb1dbd4
0e1724a
2599741
26b3697
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,10 @@ | ||
| import {parse, print} from 'math-parser' | ||
| import evaluate from 'math-evaluator' | ||
| import {query} from 'math-nodes' | ||
| import {traverse} from 'math-traverse' | ||
| import {build, query} from 'math-nodes' | ||
| import {traverse, replace} from 'math-traverse' | ||
|
|
||
| import {defineRule, definePatternRule} from './matcher' | ||
| import {defineRule, definePatternRule, canApplyRule, applyRule} from './matcher' | ||
| import {isPolynomialTerm, getCoefficient, getVariableFactors, getCoefficientsAndConstants} from './rules/collect-like-terms.js' | ||
| import {clone, getRanges} from './utils' | ||
|
|
||
| const defineRuleString = (matchPattern, rewritePattern, constraints) => { | ||
|
|
@@ -151,7 +152,7 @@ export const SIMPLIFY_SIGNS = defineRuleString('#a / -#b', '-#a / #b') | |
|
|
||
| // ADDING FRACTIONS | ||
|
|
||
| export const ADD_NUMERATORS = | ||
| export const COMBINE_NUMERATORS = | ||
| defineRuleString('#a_0 / #b + ...', '(#a_0 + ...) / #b') | ||
|
|
||
| export const COMMON_DENOMINATOR = | ||
|
|
@@ -184,9 +185,60 @@ export const ABSOLUTE_VALUE = defineRuleString('|-#a|', '#a') | |
|
|
||
| // MULTIPLYING POLYNOMIALS | ||
|
|
||
| // e.g. 6x y z -> 6 (x^1y^1z^1) (where x, y, z are | ||
| // separate identifiers) | ||
|
|
||
| export const ADD_EXPONENT_OF_ONE_HELPER = defineRule( | ||
| (node) => { | ||
| const hasConstantVariable = query.isMul(node) && node.args.some(query.isIdentifier) | ||
|
|
||
| return (hasConstantVariable) ? {node} : null | ||
| }, | ||
|
|
||
| (node) => { | ||
| const result = build.applyNode( | ||
| 'mul', | ||
| node.args.map(arg => { | ||
| if (query.isIdentifier(arg)){ | ||
| return build.applyNode('pow', [arg, build.numberNode(1)]) | ||
| } | ||
| return arg | ||
| }), {implicit: node.implicit} | ||
| ) | ||
| return result | ||
| } | ||
| ) | ||
|
|
||
| // e.g. x^2 * x -> x^2 * x^1 | ||
| // export const ADD_EXPONENT_OF_ONE = ... | ||
| export const ADD_EXPONENT_OF_ONE = defineRule( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This rule could be implemented without the helper once we address #23. Can you add a TODO here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
| (node) => { | ||
| let isMulOfPolynomials = false | ||
|
|
||
| if (query.isMul(node)) { | ||
| const {constants, coefficientMap} = getCoefficientsAndConstants(node) | ||
| isMulOfPolynomials = Object.keys(coefficientMap).length > 1 | ||
| || Object.keys(coefficientMap) | ||
| .some(key => coefficientMap[key].length > 1) | ||
|
||
| } | ||
|
|
||
| return (isMulOfPolynomials && !node.implicit) ? {node} : null | ||
| }, | ||
|
|
||
| (node) => { | ||
| const result = build.applyNode( | ||
| 'mul', | ||
| node.args.map(arg => { | ||
| if (canApplyRule(ADD_EXPONENT_OF_ONE_HELPER, arg)) { | ||
| return applyRule(ADD_EXPONENT_OF_ONE_HELPER, arg) | ||
| } else if (query.isIdentifier(arg)) { | ||
| return build.applyNode('pow', [arg, build.numberNode(1)]) | ||
| } | ||
| return arg | ||
|
||
| }) | ||
| ) | ||
| return result | ||
| } | ||
| ) | ||
|
|
||
| // EXPONENT RULES | ||
|
|
||
|
|
@@ -196,6 +248,97 @@ export const PRODUCT_RULE = defineRuleString('#a^#b_0 * ...', '#a^(#b_0 + ...)') | |
| // e.g. x^5 / x^3 -> x^(5 - 3) | ||
| export const QUOTIENT_RULE = defineRuleString('#a^#p / #a^#q', '#a^(#p - #q)') | ||
|
|
||
| // e.g. 3x^2 * 2x^2 -> (3 * 2)(x^2 * x^2) | ||
| export const MULTIPLY_COEFFICIENTS = defineRule( | ||
| (node) => { | ||
| return canApplyRule(ADD_EXPONENT_OF_ONE, node) ? {node} : null | ||
|
||
| }, | ||
| (node) => { | ||
| const terms = [] | ||
| const coeffs = [] | ||
| traverse(node, { | ||
| enter(node) { | ||
| if(query.isPow(node)){ | ||
| terms.push(node) | ||
| } | ||
| } | ||
| }) | ||
| node.args.forEach(arg => | ||
| coeffs.push(getCoefficient(arg))) | ||
|
|
||
| const newCoeff = build.applyNode( | ||
| 'mul', | ||
| coeffs | ||
| ) | ||
| const newVariable = build.applyNode( | ||
| 'mul', | ||
| terms | ||
| ) | ||
| const result = build.applyNode( | ||
| 'mul', | ||
| [newCoeff, newVariable], | ||
| {implicit: true} | ||
| ) | ||
| return result | ||
| } | ||
| ) | ||
|
|
||
| // e.g. 3x^3 * y^2 -> 3 (x^3 y^2) | ||
| export const MULTIPLY_POLYNOMIAL_TERMS = defineRule( | ||
| (node) => { | ||
| return canApplyRule(MULTIPLY_COEFFICIENTS, node) ? {node} : null | ||
| }, | ||
|
|
||
| (node) => { | ||
| const terms = {} | ||
| traverse(node, { | ||
| enter(node) { | ||
| if(query.isPow(node)){ | ||
| const variable = print(node.args[0]) | ||
| const exponent = node.args[1] | ||
| if(!(variable in terms)){ | ||
| terms[variable] = [query.getValue(exponent)] | ||
| } else { | ||
| terms[variable].push(query.getValue(exponent)) | ||
| } | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| let newVariable | ||
|
|
||
| if(Object.keys(terms).length > 1) { | ||
| newVariable = build.applyNode( | ||
| 'mul', | ||
| Object.keys(terms).map(key => { | ||
| const exponent = terms[key].reduce((a,b) => a + b) | ||
| const expression = `${key}^${exponent}` | ||
| return parse(expression) | ||
| }), {implicit: true} | ||
| ) | ||
| } else { | ||
| newVariable = Object.keys(terms).map(key => { | ||
| const exponent = terms[key].reduce((a,b) => a + b) | ||
| const expression = `${key}^${exponent}` | ||
| return parse(expression) | ||
| }) | ||
| newVariable = newVariable[0] | ||
| } | ||
|
|
||
| let newCoeff = 1 | ||
| node.args.forEach(arg => newCoeff *= query.getValue(getCoefficient(arg))) | ||
|
|
||
| const newCoeffNode = build.numberNode(newCoeff) | ||
|
|
||
| const result = build.applyNode( | ||
| 'mul', | ||
| [newCoeffNode, newVariable], | ||
| {implicit: true} | ||
| ) | ||
| return result | ||
| } | ||
| ) | ||
|
|
||
| // e.g. (a * b)^x -> a^x * b^x | ||
| export const POWER_OF_A_PRODUCT = | ||
| defineRuleString('(#a_0 * ...)^#b', '#a_0^#b * ...') | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -165,7 +165,7 @@ describe('rules', () => { | |
| ['x^((x + 1) / -1)', 'x^(-(x + 1) / 1)'], | ||
| ]) | ||
|
|
||
| suite('add numerators', rules.ADD_NUMERATORS, [ | ||
| suite('add numerators', rules.COMBINE_NUMERATORS, [ | ||
| ['1/3 + 2/3', '(1 + 2) / 3'], | ||
| ['1/x + 2/x + 3/x', '(1 + 2 + 3) / x'], | ||
| ['2/3 - 1/3', '(2 - 1) / 3'], | ||
|
|
@@ -210,6 +210,17 @@ describe('rules', () => { | |
| ['x^(|-(x + 1)|)', 'x^(x + 1)'], | ||
| ]) | ||
|
|
||
| suite('adding exponent of one helper', rules.ADD_EXPONENT_OF_ONE_HELPER, [ | ||
| ['6x y z', '6 x^1 y^1 z^1'], | ||
| ['2x y^2 z', '2 x^1 y^2 z^1'], | ||
| ]) | ||
|
|
||
| suite('adding exponent of one', rules.ADD_EXPONENT_OF_ONE, [ | ||
| ['x^2 * x', 'x^2 * x^1'], | ||
| ['x^2 * 2 * x * x', 'x^2 * 2 * x^1 * x^1'], | ||
| ['2x + 3x^2 * x y z', '2 x + 3 x^2 * x^1 y^1 z^1'], | ||
| ]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix test cases and uncomment. |
||
|
|
||
| suite('collects like terms', rules.COLLECT_LIKE_TERMS, [ | ||
| ['2x + 1 - 2x', '(2 x - 2 x) + 1'], | ||
| ['2x + 1 - x', '(2 x - x) + 1'], | ||
|
|
@@ -294,6 +305,20 @@ describe('rules', () => { | |
| ['x^-a / x^-b', 'x^(-a - -b)'], | ||
| ]) | ||
|
|
||
| suite('multiplying coefficients', rules.MULTIPLY_COEFFICIENTS, [ | ||
| ['x^2 * x^1', '(1 * 1) (x^2 * x^1)'], | ||
| ['3x^2 * x^2', '(3 * 1) (x^2 * x^2)'], | ||
| ['x^3 * 2y^2', '(1 * 2) (x^3 * y^2)'], | ||
| ['x^3 + 2x + 3x^1 * 5x^1', 'x^3 + 2 x + (3 * 5) (x^1 * x^1)'], | ||
|
||
| ]) | ||
|
|
||
| suite('multiplying polynomials', rules.MULTIPLY_POLYNOMIAL_TERMS, [ | ||
| ['x^2 * x^1', '1 x^3'], | ||
| ['3x^2 * x^2', '3 x^4'], | ||
| ['x^3 * 2y^2', '2 (x^3 y^2)'], | ||
| ['x^3 + 2x + 3x^1 * 5x^1', 'x^3 + 2 x + 15 x^2'], | ||
| ]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does a couple of steps:
Mathsteps still wants all of the steps. I feel like we need to take a step back and think about we can combine multiple steps. We could use |
||
|
|
||
| suite('power of a product', rules.POWER_OF_A_PRODUCT, [ | ||
| ['(2*3)^x', '2^x * 3^x'], | ||
| ['(2*3*5)^x', '2^x * 3^x * 5^x'], | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use an
elseblock here or change this to ternary statement?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed