Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
04bb0ee
added rules for patterns w/o constraints
aliang8 Apr 30, 2017
f37f48c
added rules test, some errors
aliang8 Apr 30, 2017
5745443
rm file
aliang8 Apr 30, 2017
b462d8a
rm file
aliang8 Apr 30, 2017
3ca4c7f
format
aliang8 Apr 30, 2017
adcbc10
fixed tabing, remove comments, added rules, need to check variables
aliang8 Apr 30, 2017
3cb89d4
added extra test cases, not done
aliang8 Apr 30, 2017
75ab164
not sure what's up with these tests
aliang8 Apr 30, 2017
3beef3e
finished adding cases, still some errors
aliang8 May 1, 2017
c35f087
fixed merge errors
aliang8 May 2, 2017
20bc705
one last test
aliang8 May 2, 2017
336c5c3
fixed tests and yarn
aliang8 May 2, 2017
3dbb8cd
done
aliang8 May 2, 2017
2335275
removed lint, added TODO
aliang8 May 2, 2017
d91eb26
merging
aliang8 May 5, 2017
7fdbd7e
rebasing
aliang8 May 7, 2017
e8e86a1
merge
aliang8 May 9, 2017
7a3e271
merged
aliang8 May 10, 2017
fbd4938
merge
aliang8 May 14, 2017
3ea7468
merge
aliang8 May 15, 2017
96d2bb9
merged
aliang8 May 18, 2017
caa0ae0
addingPolynomials
aliang8 May 20, 2017
14a091f
fixed comments
aliang8 May 20, 2017
aa21a98
added more tests, one of them fails
aliang8 May 20, 2017
08b3ca1
remove . file
aliang8 May 20, 2017
03ffdf1
merge
aliang8 May 20, 2017
648f0f7
remove alphabetize
aliang8 May 20, 2017
632b525
fixed comments
aliang8 May 20, 2017
7403644
removed comment
aliang8 May 20, 2017
2cb2f76
starting to work on LCM
aliang8 May 20, 2017
fa1c35c
fractional polynomial and rearrange coeffs
aliang8 May 21, 2017
85f51d0
remove . file
aliang8 May 21, 2017
9f81447
fixed failing case
aliang8 May 21, 2017
ac44821
multiplyingPolynomials
aliang8 May 21, 2017
56d4872
rules.js
aliang8 May 21, 2017
e02838b
multiplyingPolynomials
aliang8 May 21, 2017
06914d0
merge
aliang8 May 21, 2017
510afb7
fixed comments
aliang8 May 21, 2017
eb95348
working on adding one exponent
aliang8 May 21, 2017
2e67877
exponent of one
aliang8 May 22, 2017
b485d9e
fixed adding exponent of one
aliang8 May 22, 2017
7784dd7
remove comment
aliang8 May 22, 2017
ef26741
fixing comments
aliang8 May 23, 2017
894bbbc
fixing last comment
aliang8 May 23, 2017
d73a190
Merge branch 'master' into multiplyingPolynomials
aliang8 May 26, 2017
9741fbf
minor fixes
aliang8 May 28, 2017
ba2d8f6
minor fixes
aliang8 May 28, 2017
da48fc1
merge
aliang8 Jun 8, 2017
d5d9576
merge
aliang8 Jun 8, 2017
8bf1170
change rules
aliang8 Jun 8, 2017
9d07321
fixed test cases
aliang8 Jun 8, 2017
5b4a5f2
merge
aliang8 Jun 11, 2017
bb1dbd4
merge with master
aliang8 Jun 15, 2017
0e1724a
update multiplying polynomial functionality
aliang8 Jun 15, 2017
2599741
remove random
aliang8 Jun 15, 2017
26b3697
fixed weird behavior
aliang8 Jun 19, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions lib/__test__/rule-list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,12 @@ describe('rules', () => {
['(2^3)^x', '8^x'],
])

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'],
['2 + 3x^2 * x y z', '2 + 3 x^2 * (x^1 * y^1 * z^1)'],
])

suite('product rule', rules.PRODUCT_RULE, [
['10^2 * 10^5 * 10^3', '10^(2 + 5 + 3)'],
['x^a * x^b * x^c', 'x^(a + b + c)'],
Expand All @@ -380,6 +386,31 @@ describe('rules', () => {
['x^-a / x^-b', 'x^(-a - -b)'],
])

suite('multiplying coefficients', rules.MULTIPLY_COEFFICIENTS, [
['x^2 * y^2', 'x^2 * y^2'],
['x^2y^2z^2 * 2x^2', '2 (x^2 * y^2 * z^2 * x^2)'],
['3x^2 * x^2', '3 (x^2 * x^2)'],
['x^3 * y^2', 'x^3 * y^2'],
['x^3 + 2x^1 + 3x^1 * 5x^1', 'x^3 + 2 x^1 + (3 * 5) (x^1 * x^1)'],
['x^3 * x^3 * x^3', 'x^3 * x^3 * x^3'],
['x^1 * x^1 * x^1 * x^1 * x^1', 'x^1 * x^1 * x^1 * x^1 * x^1'],
['2/3x^1 * 3x^1', '(2 / 3 * 3) (x^1 * x^1)'],
['2/3(x + 1)^1 * 2x^3', '(2 / 3 * 2) ((x + 1)^1 * x^3)'],
['(x + 1)^3 * 2(x + 3)^3', '2 ((x + 1)^3 * (x + 3)^3)'],
])

suite('simplfy coefficients', rules.SIMPLIFY_COEFFICIENTS, [
['(3 * 2)(x^2 * y^3)', '6 (x^2 * y^3)'],
])

suite('multiplying polynomial terms', rules.MULTIPLY_POLYNOMIAL_TERMS, [
['x^2 * x^1', 'x^3'],
['x^3 * y^2', 'x^3 y^2'],
['x^3 + x^1 + x^1 * x^1 * y^3', 'x^3 + x^1 + x^2 y^3'],
['(x+1)^2 * (x+1)^3', '(x + 1)^5'],
['x^1 * x^3 * (2x + 3)^2', 'x^4 (2 x + 3)^2'],
])

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'],
Expand Down
124 changes: 119 additions & 5 deletions lib/rule-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {build, query} from 'math-nodes'
import {traverse} from 'math-traverse'

import {defineRule, definePatternRule, applyRule, canApplyRule} from './rule'
import {isPolynomialTerm, getCoefficient, getVariableFactors} from './rules/collect-like-terms.js'
import {isPolynomialTerm, getCoefficient, getVariableFactors, getCoefficientsAndConstants} from './rules/collect-like-terms.js'
import {clone, getRanges} from './utils'

const defineRuleString = (matchPattern, rewritePattern, constraints) => {
Expand Down Expand Up @@ -350,10 +350,46 @@ export const GROUP_TERMS_BY_ROOT = defineRule(
// e.g nthRoot(9, 2) -> 3
export const NTH_ROOT_VALUE = defineRuleString('nthRoot(#a, #b)', '#eval(nthRoot(#a, #b))', {a: query.isNumber, b: query.isNumber})

// MULTIPLYING POLYNOMIALS

// e.g. x^2 * x -> x^2 * x^1
// export const ADD_EXPONENT_OF_ONE = ...
export const ADD_EXPONENT_OF_ONE = defineRule(
(node) => {
let hasIdentifier = false
if (query.isMul(node)) {
for (var i in node.args) {
let term = node.args[i]
// check if there is a variable with exponent 1
hasIdentifier = query.isMul(term)
? term.some(arg => {return query.isIdentifier(arg)})
: query.isIdentifier(term)
}
}

return hasIdentifier ? {node} : null
},

(node) => {
const result = build.applyNode(
'mul',
node.args.map(term => {
let one = build.number(1)
if (query.isMul(term)) {
term.args(arg => {
if (query.isIdentifier(arg)){
return build.apply('pow', [term, one])
} else {
return arg
}
})
} else if (query.isIdentifier(term)) {
return build.apply('pow', [term, one])
} else {
return term
}
})
)
return result
}
)

// EXPONENT RULES

Expand All @@ -363,6 +399,82 @@ 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) => {
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 terms = []
const coeffs = []
traverse(node, {
enter(node) {
if(query.isPow(node)){
terms.push(node)
}
}
})
node.args.forEach(function(arg) {
if (query.getValue(getCoefficient(arg)) != 1) {
coeffs.push(getCoefficient(arg))
}
})

let poly = build.mul(...terms)

const result = coeffs.length > 1
? build.implicitMul(build.mul(...coeffs), poly)
: coeffs.length == 1
? build.implicitMul(coeffs[0], poly)
: poly
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please no nested ternaries. They're hard to understand and even harder understand with this formatting.


return result
}
)

// e.g. (3 * 2)(x^3 y^2) -> 6 x^3 y^2
export const SIMPLIFY_COEFFICIENTS = defineRuleString(
'(#a_0 * ...) #b', '#eval(#a_0 * ...) #b'
)

// done after SIMPLIFY_COEFFICIENTS
// e.g. x^3 * x^2 * y^2 -> x^5 * y^2
export const MULTIPLY_POLYNOMIAL_TERMS = defineRule(
(node) => {
let coefficientOfOne = false
if(query.isMul(node)){
coefficientOfOne = node.args.every(term => {
return isPolynomialTerm(term)
&& query.getValue(getCoefficient(term)) == 1
})
}

return coefficientOfOne ? {node} : null
},

(node) => {
let result = node
while(canApplyRule(PRODUCT_RULE, result)) {
result = applyRule(PRODUCT_RULE, result)
}
while(canApplyRule(SIMPLIFY_ARITHMETIC, result)) {
result = applyRule(SIMPLIFY_ARITHMETIC, result)
}
return query.isMul(result)
? build.apply('mul', result.args, {implicit: true})
: result
}
)

// e.g. (a * b)^x -> a^x * b^x
export const POWER_OF_A_PRODUCT =
defineRuleString('(#a_0 * ...)^#b', '#a_0^#b * ...')
Expand Down Expand Up @@ -403,7 +515,9 @@ export const DISTRIBUTE_NEGATIVE_ONE =
// COLLECT AND COMBINE
export {default as COLLECT_LIKE_TERMS} from './rules/collect-like-terms'

export const FRACTIONAL_POLYNOMIALS = defineRuleString('#a #b/#c', '#a / #c #b')
export const FRACTIONAL_POLYNOMIALS = defineRuleString(
'#a #b/#c', '#a / #c #b'
)

export {ADD_POLYNOMIAL_TERMS} from './rules/collect-like-terms'

Expand Down
25 changes: 15 additions & 10 deletions lib/rules/collect-like-terms.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,30 @@ const isPolynomial = (node) => {
return query.isAdd(node) && node.args.every(isPolynomialTerm)
}

// x, 2x, xy, 2xy, x^2, ...
// isFactor matches x, 2, x^2
// x, 2x, xy, 2xy, x^2, (x+1)^2 ...
// match either #x, #x^#b, or #a where #x is an identifier and #b and #a are numbers
// but really we want #b to match either a number or a fraction with numbers for
// numerator and denominator

export const isPolynomialTerm = (node) => {
if (query.isNumber(node)) {
if (query.isNumber(node) || isConstantFraction(node)) {
return true
} else if (query.isIdentifier(node)) {
return true
} else if (query.isPow(node)) {
const [base, exponent] = node.args
return query.isIdentifier(base) && isPolynomialTerm(exponent)
return (query.isIdentifier(base) || isPolynomial(base)) && isPolynomialTerm(exponent)
} else if (query.isNeg(node)) {
return isPolynomialTerm(node.args[0])
} else if (query.isMul(node)) {
return node.args.every(isPolynomialTerm)
}
}

export const isConstantFraction = (node) => {
return query.isDiv(node) && query.isNumber(node.args[0]) && query.isNumber(node.args[1])
}

export const getCoefficient = (node) => {
if (query.isNumber(node)) {
return node
Expand All @@ -46,7 +49,7 @@ export const getCoefficient = (node) => {
result.wasMinus = node.wasMinus
return result
} else if (query.isMul(node)) {
const numbers = node.args.filter(query.isNumber)
const numbers = node.args.filter(arg => query.isNumber(arg) || isConstantFraction(arg))
if (numbers.length > 1) {
return build.applyNode('mul', numbers)
} else if (numbers.length > 0) {
Expand All @@ -57,10 +60,11 @@ export const getCoefficient = (node) => {
}
}

// isFactor matches x, x^2, (x + 1)^2
export const isVariableFactor = (node) =>
query.isIdentifier(node) ||
query.isPow(node) && query.isIdentifier(node.args[0]) &&
(query.isNumber(node.args[1]) || isVariableFactor(node.args[1]))
query.isPow(node) && (query.isNumber(node.args[1]) || isVariableFactor(node.args[1]))



export const getVariableFactors = (node) => {
Expand All @@ -76,7 +80,7 @@ export const getVariableFactors = (node) => {
}
}

const getVariableFactorName = (node) => {
export const getVariableFactorName = (node) => {
if (query.isIdentifier(node)) {
return node.name
} else if (query.isPow(node)) {
Expand All @@ -100,12 +104,13 @@ const isImplicit = (node) => {
}
}

const getCoefficientsAndConstants = (node) => {
// TODO: fix the case of single polynomials ex. 2x^2
export const getCoefficientsAndConstants = (node) => {
const coefficientMap = {}
const constants = []

node.args.forEach(arg => {
if (query.isNumber(arg)) {
if (query.isNumber(arg) || isConstantFraction(arg)) {
constants.push(arg)
} else {
const sortedVariables = sortVariables(getVariableFactors(arg))
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-polyfill": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"eslint": "^3.15.0",
"eslint": "^3.19.0",
"jest": "^20.0.4",
"pre-commit": "^1.2.2",
"webpack": "^2.6.1"
Expand Down