diff --git a/lib/__test__/rule-list.test.js b/lib/__test__/rule-list.test.js index 32f70c0..19da48c 100644 --- a/lib/__test__/rule-list.test.js +++ b/lib/__test__/rule-list.test.js @@ -172,21 +172,86 @@ describe('rules', () => { ['x^((x + 1) / -1)', 'x^(-(x + 1) / 1)'], ]) - suite('add numerators', rules.COMBINE_NUMERATORS, [ + suite('rewrite fractional polynomial', rules.REWRITE_FRACTIONAL_POLYNOMIAL, [ + ['2x/3', '2 / 3 x'], + ['2x^2/3', '2 / 3 x^2'], + ['2(x+1)^2/3', '2 / 3 (x + 1)^2'], + ['2xyz/3', '2 / 3 xyz'], + ]) + + suite('combine 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'], ['(1/3 + 2/3) / x', '(1 + 2) / 3 / x'], ]) - suite('common denominators', rules.COMMON_DENOMINATOR, [ + suite('convert to fraction', rules.CONVERT_TO_FRACTION, [ + ['2/3 + x', '2 / 3 + x / 1'], + ['2/3 - x', '2 / 3 - x / 1'], + ['2/3 - x - 2', '2 / 3 - x / 1 - 2 / 1'], + ['2/(3 + x) + 2 + (x + 1)^2', '2 / (3 + x) + 2 / 1 + (x + 1)^2 / 1'], + ['2.2 + 3/4', '11 / 5 + 3 / 4'], + ['1.33 + 2.12 + 2/3', '133 / 100 + 53 / 25 + 2 / 3'], + ['1.44 - 2 - 3/4', '36 / 25 - 2 / 1 - 3 / 4'], + ['-2 + 3.33 + 2/3', '-2 / 1 + 333 / 100 + 2 / 3'] + ]) + + suite('cancel like terms', rules.CANCEL_LIKE_TERMS, [ + // when all factors cancel + ['((x+1)(x+2)) / ((x+1)(x+2))', '1'], + ['(x y z) / (x y z)', '1'], + // one factor in numerator, none in denominator + ['(12(x+1)) / (x+1)', '12'], + ['(x y z) / (y z)', 'x'], + // TODO: handle nthRoots + //['nthRoot(x^2,2) nthRoot(x^3,3) / nthRoot(x^2,2)'] + // multiple factors in numerator, none in denominator + ['(12(x+1)^2) / (x+1)', '12 (x + 1)^1'], + ['(x y z w) / (z w)', 'x y'], + // one factor in denominator, none in numerator + ['(x+1)^2 / (12(x+1)^2)', '1 / 12'], + ['(x y z) / (x y z w)', '1 / w'], + // multiple factors in denominator, none in numerator + ['(x+1)^2 / (12 (x+1)^3)', '1 / (12 (x + 1)^1)'], + ['(x y z) / (x y z a b c)', '1 / (a b c)'], + // one factor in numerator and denominator + ['((x+1)^2 (x+2)^3) / ((x+1)^2 (x+3)^3)', '(x + 2)^3 / (x + 3)^3'], + ['(x y z) / (y z w)', 'x / w'], + // one factor in denominator, multiple in numerator + ['(12(x+1)^2 (2x+1)^2) / (x+1)^3 ', '(12 (2 x + 1)^2) / (x + 1)^1'], + // one factor in numerator, multiple in denominator + ['(x+1)^3 / (12(x+1)^2 (x+2)^3)', '(x + 1)^1 / (12 (x + 2)^3)'], + ['(x y z) / (y z a b)', 'x / (a b)'], + // multiple factors in both numerator and denominator + ['(12(x+1)^2 (x+2)^2) / ((x+2)^3 (x+4)^2)', '(12 (x + 1)^2) / ((x + 2)^1 (x + 4)^2)'], + ['(a b c d) / (d e f g)', '(a b c) / (e f g)'], + // testing simplify fractions + ['(12(x+1)^2) / (8(x+2))', '(3 (x + 1)^2) / (2 (x + 2))'], + ]) + + suite('common denominators', rules.COMMON_DENOMINATOR, [ ['2/6 + 1/4', '(2 * 2) / (6 * 2) + (1 * 3) / (4 * 3)'], + ['2/(3 + x) + 3/(2 + x)', '(2 (2 + x)) / ((3 + x) (2 + x)) + (3 (3 + x)) / ((3 + x) (2 + x))'], + ['2/5 + 1/(3x + 7)', '(2 (3 x + 7)) / (5 (3 x + 7)) + (1 * 5) / (5 (3 x + 7))'], ['2/6 - 1/4', '(2 * 2) / (6 * 2) - (1 * 3) / (4 * 3)'], ['2/6 + 1/4 - 2/5', '(2 * 10) / (6 * 10) + (1 * 15) / (4 * 15) - (2 * 12) / (5 * 12)'], - ['2/6 + 1/4 - 3/4', '(2 * 2) / (6 * 2) + (1 * 3) / (4 * 3) - (3 * 3) / (4 * 3)'], - // TODO: return the original expression if the denominators are already - // the same? - ['2/4 - 1/4', '(2 * 1) / (4 * 1) - (1 * 1) / (4 * 1)'], + ['2/6 + 1/4 - 3/4', '(2 * 2) / (6 * 2) + (1 * 3) / (4 * 3) - (3 * 3) / (4 * 3)'], + ['(3 + x)/2 + 2/(3 + x) + 2/3', '((3 + x) 3 (3 + x)) / (6 (3 + x)) + (2 * 6) / (6 (3 + x)) + (2 2 (3 + x)) / (6 (3 + x))'], + // Some terms have same denom + ['2/3 + 2/3 + 1/4 + 1/4', '(2 * 4) / (3 * 4) + (2 * 4) / (3 * 4) + (1 * 3) / (4 * 3) + (1 * 3) / (4 * 3)'], + // Number surrounded by fraction + ['3 + 2/3', '(3 * 3) / 3 + 2 / 3'], + ['x + 2 + 3/4', '(x * 4) / 4 + (2 * 4) / 4 + 3 / 4'], + ['(x + 1)^2 + 2/3', '((x + 1)^2 * 3) / 3 + 2 / 3'], + ['2(x + 1)^2 + 2/3', '(2 (x + 1)^2 * 3) / 3 + 2 / 3'], + ['2/3 - x - 2', '2 / 3 - (x * 3) / 3 - (2 * 3) / 3'], + ['2/3 - x/3 - 2/4', '(2 * 4) / (3 * 4) - (x * 4) / (3 * 4) - (2 * 3) / (4 * 3)'], + ['-2 + 3 - 2/4', '-(2 * 4) / 4 + (3 * 4) / 4 - 2 / 4'], + ['2 / x + 3 / (x + 1)^2', '(2 (x + 1)^2) / (x (x + 1)^2) + (3 x) / (x (x + 1)^2)'], + ['2 / (4 * x) + 2 / y', '(2 y) / (4 x y) + (2 (4 x)) / (4 x y)'], + // TODO: case doesn't work, but is fine because polynomials should be simplified + //['2 / (x * x) + 3 / (x * y) + 4 / (y * y)', ''], ]) suite('multiply fractions', rules.MULTIPLY_FRACTIONS, [ @@ -491,3 +556,4 @@ describe('canApplyRule', () => { }) }) }) + diff --git a/lib/rule-list.js b/lib/rule-list.js index 45f7c5e..30d83c6 100644 --- a/lib/rule-list.js +++ b/lib/rule-list.js @@ -5,8 +5,8 @@ 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 {clone, getRanges} from './utils' +import {isPolynomialTerm, getCoefficient, getVariableFactors, isPolynomial, getCoefficientsAndConstants, isImplicit} from './rules/collect-like-terms.js' +import {clone, getRanges, flattenOperands, removeUnnecessaryParentheses} from './utils' const defineRuleString = (matchPattern, rewritePattern, constraints) => { const matchAST = parse(matchPattern) @@ -82,6 +82,7 @@ export const EVALUATE_POWER = defineRuleString( // e.g. -(-3) -> 3 export const NEGATION = defineRuleString('--#a', '#a') +// e.g. x * 5 -> 5 x export const REARRANGE_COEFF = defineRuleString('#b * #a', '#a #b', {a: query.isNumber, b: isPolynomialTerm}) // ARITHMETIC @@ -133,7 +134,6 @@ export const RESOLVE_DOUBLE_MINUS = defineRuleString('#a - -#b', '#a + #b') // e.g -3 * -2 -> 3 * 2 export const MULTIPLY_NEGATIVES = defineRuleString('-#a * -#b', '#a * #b') - // FRACTIONS // e.g. (x + 1) / 2 -> x / 2 + 1 / 2 @@ -143,9 +143,6 @@ export const BREAK_UP_FRACTION = // e.g. -2/-3 => 2/3 export const CANCEL_MINUSES = defineRuleString('-#a / -#b', '#a / #b') -// e.g. 2x/2 -> x -// CANCEL_TERMS: 'CANCEL_TERMS', - // e.g. 2/6 -> 1/3 export const SIMPLIFY_FRACTION = defineRuleString( '#a / #b', @@ -155,17 +152,471 @@ export const SIMPLIFY_FRACTION = defineRuleString( // e.g. 2/-3 -> -2/3 export const SIMPLIFY_SIGNS = defineRuleString('#a / -#b', '-#a / #b') +// e.g. 2x/3 -> 2/3 x +export const REWRITE_FRACTIONAL_POLYNOMIAL = defineRuleString( + '#a #b / #c', '#a / #c #b', {a: query.isNumber, b: isPolynomialTerm, c: query.isNumber} +) // ADDING FRACTIONS +// e.g. 2/5 + 2/5 -> (2 + 2)/5 export const COMBINE_NUMERATORS = defineRuleString('#a_0 / #b + ...', '(#a_0 + ...) / #b') -export const COMMON_DENOMINATOR = - defineRuleString( - '#a_0 / #b_0 + ...', - '(#a_0 * #eval(lcm(#b_0, ...) / #b_0)) / (#b_0 * #eval(lcm(#b_0, ...) / #b_0)) + ...' - ) + +export const getNegatives = (arr) => arr.map(arg => query.isNeg(arg)) + +export const isFraction = (node) => query.isNeg(node) + ? query.isDiv(node.args[0]) + : query.isDiv(node) + +export const getNumerator = (node) => query.isNeg(node) + ? node.args[0].args[0] + : node.args[0] + +export const getDenominator = (node) => query.isNeg(node) + ? node.args[0].args[1] + : node.args[1] + +export const isDecimal = (node) => query.isNumber(node) && query.getValue(node) % 1 != 0 + +export const decimal_to_fraction = (node) => { + // split up the decimal + const [int, dec] = node.value.toString().split('.') + + // e.g. .2 -> 2/10 + const fraction = build.div(build.number(parseInt(dec)), build.number(parseInt('1' + '0'.repeat(dec.length)))) + + // simplified the fraction if possible + const simplified = canApplyRule(SIMPLIFY_FRACTION, fraction) + ? applyRule(SIMPLIFY_FRACTION, fraction) + : fraction + + const newNumerator = parseInt(int) * query.getValue(simplified.args[1]) + query.getValue(simplified.args[0]) + return build.div(build.number(newNumerator), simplified.args[1]) +} + +// e.g. 2 + 3/2 -> 2/1 + 3/2 +// e.g. 1.2 + 3/2 -> 6/5 + 3/2 +export const CONVERT_TO_FRACTION = defineRule( + (node) => { + if (query.isAdd(node)) { + const terms = node.args + + // only some of the terms are fractions + const hasFraction = terms.some(term => isFraction(term)) + && !terms.every(term => isFraction(term)) + + return hasFraction ? {node} : null + } + }, + + (node) => { + let terms = node.args + + const negatives = getNegatives(terms) + + terms = terms.map(function(term) { + let newTerm + if (isDecimal(term)) { + // 2.2 and -2.2 + newTerm = query.isNeg(term) + ? decimal_to_fraction(term.args[0]) + : decimal_to_fraction(term) + } else if (isPolynomialTerm(term)) { + newTerm = query.isNeg(term) + ? build.div(term.args[0], build.number(1)) + : build.div(term, build.number(1)) + } else { + newTerm = query.isNeg(term) + ? term.args[0] + : term + } + return newTerm + }) + + const result = build.add( + ...terms.map((term, i) => negatives[i] + ? build.neg(term, {wasMinus: true}) + : term)) + return result + } +) + +// TODO: handle multivariable polynomials +// Get degree of a polynomial term +// e.g. 6x^2 -> 2 +const getExponent = (node) => { + if (query.isNumber(node)) { + return 0 + } else if (query.isIdentifier(node) || isPolynomial(node)){ + return 1 + } else if (query.isPow(node)) { + return query.getValue(node.args[1]) + } else if (query.isMul(node)){ + return getExponent(node.args[1]) + } else if (query.isNeg(node)) { + const variable = node.args[0] + return getExponent(variable.args[1]) + } else { + return null + } +} + +// returns true if two nodes have the same base variable +// e.g. (x + 1)^1 and (x + 1)^3 , true +// e.g. x^2, (x + 1)^2, false +export const hasSameBase = (node1, node2) => { + return print(node1) == print(node2) + || query.isPow(node1) && print(node1.args[0]) == print(node2) + || query.isPow(node2) && print(node1) == print(node2.args[0]) + || query.isPow(node1) && query.isPow(node2) && print(node1.args[0]) == print(node2.args[0]) +} + +// TODO: handle cancelling nthRoots +// e.g. (12(x+1)) / (x+1) -> 12 / 1 +export const CANCEL_LIKE_TERMS = defineRule( + (node) => { + /* + Must be div node and either numerator or denominator is a mul node (or both) + Does not match in cases like ((x+1) / (x+1)) + There must also be a common factor between numerator and denominator + */ + + const canSimplify = query.isDiv(node) + && (query.isMul(node.args[0]) || query.isMul(node.args[1])) + + if (canSimplify) { + // checks for common factor + let [numerator, denominator] = node.args + let num = query.isMul(numerator) ? numerator.args : [numerator] + let denom = query.isMul(denominator) ? denominator.args : [denominator] + + for (var i in num) { + for (var j in denom) { + if (num[i] && denom[j]) { + const frac = build.div(num[i], denom[j]) + + if (canApplyRule(SIMPLIFY_FRACTION, frac)) { + return {node} + } + + if (hasSameBase(num[i], denom[j])){ + return {node} + } + } + } + } + } + + return null + }, + + (node) => { + /* + Split up the numerator if it is a mul node. + For each term in the numerator, if it also appears in the denominator + cancel the exponents. If exponent is 0, remove the factor completely. + */ + + node = clone(node) + + let [numerator, denominator] = node.args + let num = query.isMul(numerator) ? numerator.args : [numerator] + let denom = query.isMul(denominator) ? denominator.args : [denominator] + + for (var i in num) { + for (var j in denom) { + // if they are defined + if (num[i] && denom[j]) { + const frac = build.div(num[i], denom[j]) + + // TODO: refactor this into a helper + if (hasSameBase(num[i], denom[j])){ + const newExponent = getExponent(num[i]) - getExponent(denom[j]) + const i1 = num.indexOf(num[i]) + const i2 = denom.indexOf(denom[j]) + + if (newExponent > 0) { + delete denom[i2] + const base = num[i].args[0] + num[i] = build.pow(base, build.number(newExponent)) + } else if (newExponent == 0) { + delete num[i1] + delete denom[i2] + } else { + // remove the factor from numerator + delete num[i1] + const base = denom[j].args[0] + denom[j] = build.pow(base, build.number(Math.abs(newExponent))) + } + } + + if (canApplyRule(SIMPLIFY_FRACTION, frac)) { + const newFraction = applyRule(SIMPLIFY_FRACTION, frac) + const [newNum, newDenom] = newFraction.args + + if (query.getValue(newNum) == 1 && query.getValue(newDenom) == 1) { + delete num[i] + delete denom[j] + } else if (query.getValue(newNum) == 1) { + delete num[i] + denom[j] = newDenom + } else if (query.getValue(newDenom) == 1) { + delete denom[j] + num[i] = newNum + } else { + num[i] = newNum + denom[j] = newDenom + } + } + } + } + } + + // Remove the empty elements in the array + num = num.filter(String) + denom = denom.filter(String) + const implicitN = isImplicit(numerator) + const implicitD = isImplicit(denominator) + + let result + + if (denom.length == 0) { + if (num.length == 0) { + // when all factors cancel + result = build.number(1) + } else if (num.length == 1) { + // one factor in numerator, none in denominator + result = num[0] + } else { + // multiple factors in numerator, none in denominator + result = build.apply('mul', num, {implicit:implicitN}) + } + } else if (num.length == 0) { + if (denom.length == 1) { + // one factor in denominator, none in numerator + result = build.div(build.number(1), denom[0]) + } else { + // multiple factors in denominator, none in numerator + result = build.div(build.number(1), build.apply('mul', denom, {implicit:implicitD})) + } + } else if (denom.length == 1) { + if (num.length == 1) { + // one factor in numerator and denominator + result = build.div(num[0], denom[0]) + } else { + // one factor in denominator, multiple in numerator + result = build.div(build.apply('mul', num, {implicit:implicitN}), denom[0]) + } + } else if (num.length == 1) { + // one factor in numerator, multiple in denominator + result = build.div(num[0], build.apply('mul', denom, {implicit:implicitD})) + } else { + // multiple factors in both numerator and denominator + result = build.div(build.apply('mul', num, {implicit:implicitN}), build.apply('mul', denom, {implicit:implicitD})) + } + + return result + } +) + +/* + Lcm helper returns a dictionary with the highest power of each variable + in the given array and the lcm of all the number nodes if any. + + Nodes can include polynomialTerms, polynomials, numbers, identifiers, etc. + + e.g. [x, (x+1)^2, x^2 y, y^2, 3, 4] -> {x: 2, x + 1: 2, y: 2} and 12 + */ +export const lcm_helper = (arr, vars = {}) => { + // key to store LCM + vars['LCM'] = 1 + + arr.forEach(function(term){ + if(query.isNumber(term)){ + vars['LCM'] = lcm(query.getValue(term), vars['LCM']) + } + + else if (query.isIdentifier(term) || isPolynomial(term)) { + // a, xyz, x+1, x^2+2x+1 + // exponent is one + if(!(vars[print(term)])) { + vars[print(term)] = 1 + } + } + + else if (isPolynomialTerm(term)) { + // x^2, (x^2y^1), (x+1)^2 + if (query.isPow(term)) { + const [base, exponent] = term.args + if (!vars[print(base)]) { + vars[print(base)] = query.getValue(exponent) + } else { + vars[print(base)] = Math.max(query.getValue(exponent), vars[print(base)]) + } + } else if (query.isMul(term)) { + // recursively loop through all the terms in the mul node + // and continue building the dictionary + lcm_helper(term.args, vars) + } + } + }) + return {vars} +} + + +/* + Finds common denominator in any scenario + + e.g. 2/6 + 1/4 -> (2 * 2) / (6 * 2) + (1 * 3) / (4 * 3) + e.g. 2/(3 + x) + 2/3 -> (2 * 3)/ (3 * (3 + x)) + 2 * (3 + x) / (3 * (3 + x)) + e.g. 3 + 2/3 -> (3 * 3) / 3 + 2 / 3 +*/ + +export const COMMON_DENOMINATOR = defineRule( + (node) => { + if (query.isAdd(node)) { + const terms = node.args + + /* + Match only if at least one of the term is a fraction + and if there are two different denoms. + */ + const hasFraction = terms.some(term => isFraction(term)) + + let sameDenom + if (terms.every(term => isFraction(term))) { + const denom = getDenominator(terms[0]) + + // TODO: equals function in math-evaluator + sameDenom = terms.every(term => + print(getDenominator(term)) == print(denom)) + } + + return hasFraction && !sameDenom ? {node} : null + } + }, + + (node) => { + // convert all decimals to fractions if there are any + node = canApplyRule(CONVERT_TO_FRACTION, node) + ? applyRule(CONVERT_TO_FRACTION, node) + : node + + let terms = node.args + + // an array storing the index where fraction is negative + const negatives = getNegatives(terms) + + // get numerators and denominators of all fractions + let nums = terms.map(term => getNumerator(term)) + let denoms = terms.map(term => getDenominator(term)) + let newDenoms + let newNumerators + + /* + If all denoms are numbers, the new denom is + [denom * (LCM / denom)] and the new numerators + is [num * (LCM / denom)] for each fraction. + e.g. 2/3 + 2/4 -> (2 * 4) / (3 * 4) + (2 * 3) / (4 * 3) + + Else: some denoms are non integers + The new denominator is the LCM of all the polynomial and integer terms, + take the lowest power of each variable. + e.g. 2/3 + 2/(x+1)^2 + 2/(x+1) + newDenom = 3 * (x+1)^2 + + Note: We assume here that all arithmetic and polynomial multiplication + has been simplified. + e.g. 2/(2 * 2 * x * x) + 2/3 -> 2/(4 * x^2) + 2/3 + + The new numerator is [old num * cancelLikeTerms(newDenom/oldDenom)] + e.g. 2/(3+x) + 2/(2+x) + The new numerator of the first term: + old num: 3+x + newDenom = (3+x)(2+x) + cancelLikeTerms(newDenom/oldDenom) ((3+x)(2+x)) / (3+x) -> 2+x + New first term: 2 * (2 + x) + */ + + if(denoms.every(denom => query.isNumber(denom))) { + denoms = denoms.map(denom => query.getValue(denom)) + const LCM = lcm(...denoms) + + // remove multiplication by 1 + newDenoms = denoms.map(function(denom) { + if (denom == 1) { + return build.number(LCM /denom) + } else if (LCM / denom == 1) { + return build.number(denom) + } else { + return build.mul(build.number(denom), + build.number(LCM / denom)) + } + }) + + newNumerators = nums.map(function(num, i) { + if (LCM / denoms[i] == 1) { + return num + } else { + return build.mul(num, build.number(LCM / denoms[i])) + } + }) + + } else { + const {vars} = lcm_helper(denoms) + + const LCM = vars['LCM'] + delete vars['LCM'] + + // newDenoms is the product of all terms in vars + if (LCM == 1) { + newDenoms = build.implicitMul(...Object.keys(vars).map( + base => vars[base] == 1 + ? parse(base) + : build.pow(parse(base), build.number(vars[base])) + )) + } else { + newDenoms = build.implicitMul(build.number(LCM), ...Object.keys(vars).map( + base => vars[base] == 1 + ? parse(base) + : build.pow(parse(base), build.number(vars[base])) + )) + } + + // cancelLikeTerms(newDenom, oldDenom) = new numerator + newNumerators = nums.map(function(num, i) { + const frac = build.div(newDenoms, denoms[i]) + const simplified = applyRule(CANCEL_LIKE_TERMS, frac) + if (query.isNumber(simplified)) { + return build.mul(num, simplified) + } else if (isPolynomialTerm(simplified)) { + return removeUnnecessaryParentheses(build.implicitMul(num, build.parens(simplified))) + } else { + return flattenOperands(build.implicitMul(num, simplified)) + } + }) + } + + /* + The newDenoms variable is an array when all the denoms + are numbers because all the newDenoms are different. + */ + + const result = Array.isArray(newDenoms) + ? build.add(...newDenoms.map( + (den, i) => negatives[i] + ? build.neg(build.div(newNumerators[i], den), {wasMinus: true}) + : build.div(newNumerators[i], den))) + : build.add(...newNumerators.map( + (num, i) => negatives[i] + ? build.neg(build.div(num, newDenoms), {wasMinus: true}) + : build.div(num, newDenoms))) + + return result + } +) // Have a 'negatives' array which marks things as being negative or not for // items in a variable length node. When we're populating that variable length diff --git a/lib/rules/collect-like-terms.js b/lib/rules/collect-like-terms.js index 25e439f..663ca4c 100644 --- a/lib/rules/collect-like-terms.js +++ b/lib/rules/collect-like-terms.js @@ -11,7 +11,7 @@ const populatePatternString = (pattern, placeholders) => populatePattern(parse(p const pattern = parse('#a #x') const constantPattern = parse('#a') -const isPolynomial = (node) => { +export const isPolynomial = (node) => { return query.isAdd(node) && node.args.every(isPolynomialTerm) } @@ -28,7 +28,7 @@ export const isPolynomialTerm = (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)) { @@ -90,9 +90,9 @@ const sortVariables = (variables) => variables.sort( (a, b) => getVariableFactorName(a) > getVariableFactorName(b)) -const isImplicit = (node) => { +export const isImplicit = (node) => { if (query.isMul(node)) { - return node.implicit + return node.implicit ? true : false } else if (query.isNeg(node)) { return isImplicit(node.args[0]) } else { @@ -100,31 +100,37 @@ const isImplicit = (node) => { } } -const getCoefficientsAndConstants = (node) => { - const coefficientMap = {} - const constants = [] +/* + Given any node, return a dictionary with variable keys and coefficient values. + Also return a list of all the constant terms. + Only when all the variables are polynomialTerms, not polynomials. - node.args.forEach(arg => { - if (query.isNumber(arg)) { - constants.push(arg) - } else { - const sortedVariables = sortVariables(getVariableFactors(arg)) - - const coefficient = getCoefficient(arg) - const implicit = isImplicit(arg) - - const key = sortedVariables.length > 1 - ? print(build.applyNode('mul', sortedVariables, {implicit})) - : print(sortedVariables[0]) - - if (!(key in coefficientMap)) { - coefficientMap[key] = [] - } + e.g. 12x^2 + 5x + 2 -> {coefficientMap: [x^2 : 12, x : 5], constants: [2]} +*/ - coefficientMap[key].push(coefficient) +export const getCoefficientsAndConstants = (node, coefficientMap = {}, constants = []) => { + if (query.isNumber(node)) { + constants.push(node) + } else if (isPolynomialTerm(node)) { + const coefficient = getCoefficient(node) + const implicit = isImplicit(node) + + const sortedVariables = sortVariables(getVariableFactors(node)) + const key = sortedVariables.length > 1 + ? print(build.applyNode('mul', sortedVariables, {implicit})) + : print(sortedVariables[0]) + + if (!(key in coefficientMap)) { + coefficientMap[key] = [] } - }) - + + coefficientMap[key].push(coefficient) + } else if (query.isApply(node)) { + node.args.forEach(arg => { + getCoefficientsAndConstants(arg, coefficientMap, constants) + }) + } + return {coefficientMap, constants} } @@ -133,6 +139,7 @@ const COLLECT_LIKE_TERMS = defineRule( let hasLikeTerms = false if (isPolynomial(node)) { const {constants, coefficientMap} = getCoefficientsAndConstants(node) + hasLikeTerms = constants.length > 1 || Object.keys(coefficientMap) .some(key => coefficientMap[key].length > 1) diff --git a/package.json b/package.json index a425489..574cc16 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "author": "Kevin Barabash ", "license": "MIT", "dependencies": { + "jest-cli": "^20.0.4", "math-evaluator": "^0.0.9", - "math-nodes": "^0.1.6", + "math-nodes": "^0.1.8", "math-parser": "^0.10.4", "math-traverse": "^0.2.2" }, diff --git a/yarn.lock b/yarn.lock index e3d8def..cd6481d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,8 +33,8 @@ acorn@^3.0.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" acorn@^4.0.3, acorn@^4.0.4: - version "4.0.11" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" acorn@^5.0.0, acorn@^5.0.1: version "5.0.3" @@ -76,8 +76,8 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" ansi-styles@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.0.0.tgz#5404e93a544c4fec7f048262977bebfe3155e0c1" + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.1.0.tgz#09c202d5c917ec23188caa5c9cb9179cd9547750" dependencies: color-convert "^1.0.0" @@ -95,8 +95,8 @@ append-transform@^0.4.0: default-require-extensions "^1.0.0" aproba@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" + version "1.1.2" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" are-we-there-yet@~1.1.2: version "1.1.4" @@ -652,12 +652,12 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24 to-fast-properties "^1.0.1" babylon@^6.13.0, babylon@^6.17.0, babylon@^6.17.2: - version "6.17.3" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48" + version "6.17.4" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" base64-js@^1.0.2: version "1.2.0" @@ -694,10 +694,10 @@ boom@2.x.x: hoek "2.x.x" brace-expansion@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" dependencies: - balanced-match "^0.4.1" + balanced-match "^1.0.0" concat-map "0.0.1" braces@^1.8.2: @@ -781,10 +781,6 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" -buffer-shims@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - buffer-xor@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -1135,10 +1131,6 @@ domain-browser@^1.1.1: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" -double-ended-queue@^2.1.0-0: - version "2.1.0-0" - resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" - ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -1183,8 +1175,8 @@ error-ex@^1.2.0: is-arrayish "^0.2.1" es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.21" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.21.tgz#19a725f9e51d0300bbc1e8e821109fd9daf55925" + version "0.10.23" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38" dependencies: es6-iterator "2" es6-symbol "~3.1" @@ -1320,24 +1312,20 @@ esquery@^1.0.0: estraverse "^4.0.0" esrecurse@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" dependencies: - estraverse "~4.1.0" + estraverse "^4.1.0" object-assign "^4.0.1" estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" -estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" -estraverse@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" - esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -1503,11 +1491,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" fsevents@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + version "1.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" dependencies: nan "^2.3.0" - node-pre-gyp "^0.6.29" + node-pre-gyp "^0.6.36" fstream-ignore@^1.0.5: version "1.0.5" @@ -1584,8 +1572,8 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: path-is-absolute "^1.0.0" globals@^9.0.0, globals@^9.14.0: - version "9.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286" + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" globby@^5.0.0: version "5.0.0" @@ -1730,7 +1718,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -1801,8 +1789,8 @@ is-ci@^1.0.10: ci-info "^1.0.0" is-dotfile@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" is-equal-shallow@^0.1.3: version "0.1.3" @@ -1849,12 +1837,18 @@ is-my-json-valid@^2.10.0: jsonpointer "^4.0.0" xtend "^4.0.0" -is-number@^2.0.2, is-number@^2.1.0: +is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" dependencies: kind-of "^3.0.2" +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -2189,12 +2183,6 @@ jest@^20.0.4: dependencies: jest-cli "^20.0.4" -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" @@ -2287,6 +2275,12 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.1.5" +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -2361,11 +2355,11 @@ loose-envify@^1.0.0: js-tokens "^3.0.0" lru-cache@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" dependencies: - pseudomap "^1.0.1" - yallist "^2.0.0" + pseudomap "^1.0.2" + yallist "^2.1.2" makeerror@1.0.x: version "1.0.11" @@ -2379,22 +2373,18 @@ math-evaluator@^0.0.9: dependencies: babel-loader "^7.0.0" -math-nodes@^0.1.2: - version "0.1.5" - resolved "https://registry.yarnpkg.com/math-nodes/-/math-nodes-0.1.5.tgz#dfd25c7a9458b91413288ebfcf525cd3a80af6c7" - -math-nodes@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/math-nodes/-/math-nodes-0.1.6.tgz#ac38eba233d24c7d3094a0e99035054e55058167" +math-nodes@^0.1.7, math-nodes@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/math-nodes/-/math-nodes-0.1.8.tgz#a035f7d2d6e819bd923e529c15fc4bd276bb6337" math-parser@^0.10.4: - version "0.10.4" - resolved "https://registry.yarnpkg.com/math-parser/-/math-parser-0.10.4.tgz#4e753a5b3ed2976486228d181388ecab932f3e22" + version "0.10.5" + resolved "https://registry.yarnpkg.com/math-parser/-/math-parser-0.10.5.tgz#01399e8209e08272a688f096eac4e926c55a86c7" dependencies: - math-nodes "^0.1.2" - math-traverse "^0.2.1" + math-nodes "^0.1.7" + math-traverse "^0.2.2" -math-traverse@^0.2.1, math-traverse@^0.2.2: +math-traverse@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/math-traverse/-/math-traverse-0.2.2.tgz#f3ad3483cc62429249d4deda4da8a336f6877adf" @@ -2529,9 +2519,9 @@ node-notifier@^5.0.2: shellwords "^0.1.0" which "^1.2.12" -node-pre-gyp@^0.6.29: - version "0.6.34" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7" +node-pre-gyp@^0.6.36: + version "0.6.36" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" dependencies: mkdirp "^0.5.1" nopt "^4.0.1" @@ -2579,8 +2569,8 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" "nwmatcher@>= 1.3.9 < 2.0.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.0.tgz#b4389362170e7ef9798c3c7716d80ebc0106fccf" + version "1.4.1" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f" oauth-sign@~0.8.1: version "0.8.2" @@ -2816,7 +2806,7 @@ prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" -pseudomap@^1.0.1: +pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -2851,15 +2841,17 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" randomatic@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" dependencies: - is-number "^2.0.2" - kind-of "^3.0.2" + is-number "^3.0.0" + kind-of "^4.0.0" randombytes@^2.0.0, randombytes@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" + version "2.0.5" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79" + dependencies: + safe-buffer "^5.1.0" rc@^1.1.7: version "1.2.1" @@ -2886,14 +2878,14 @@ read-pkg@^1.0.0: path-type "^1.0.0" readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6: - version "2.2.9" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" + version "2.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.0.tgz#640f5dcda88c91a8dc60787145629170813a1ed2" dependencies: - buffer-shims "~1.0.0" core-util-is "~1.0.0" - inherits "~2.0.1" + inherits "~2.0.3" isarray "~1.0.0" process-nextick-args "~1.0.6" + safe-buffer "~5.1.0" string_decoder "~1.0.0" util-deprecate "~1.0.1" @@ -2962,8 +2954,8 @@ regjsparser@^0.1.4: jsesc "~0.5.0" remove-trailing-separator@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + version "1.0.2" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511" repeat-element@^1.1.2: version "1.1.2" @@ -3071,7 +3063,11 @@ rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" -safe-buffer@^5.0.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" + +safe-buffer@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" @@ -3124,8 +3120,8 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" shelljs@^0.7.5: - version "0.7.7" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.7.tgz#b2f5c77ef97148f4b4f6e22682e10bba8667cff1" + version "0.7.8" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -3205,8 +3201,8 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -3215,7 +3211,6 @@ sshpk@^1.7.0: optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" jsbn "~0.1.0" tweetnacl "~0.14.0" @@ -3227,8 +3222,8 @@ stream-browserify@^2.0.1: readable-stream "^2.0.2" stream-http@^2.3.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.1.tgz#546a51741ad5a6b07e9e31b0b10441a917df528a" + version "2.7.2" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -3262,10 +3257,10 @@ string_decoder@^0.10.25: resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" string_decoder@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.1.tgz#62e200f039955a6810d8df0a33ffc0f013662d98" + version "1.0.2" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a533737b7491e179" dependencies: - safe-buffer "^5.0.1" + safe-buffer "~5.0.1" stringstream@~0.0.4: version "0.0.5" @@ -3296,8 +3291,8 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" supports-color@^3.1.0, supports-color@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: has-flag "^1.0.0" @@ -3356,10 +3351,8 @@ text-table@~0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" throat@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-3.1.0.tgz#ef22d8855963b3fdc626d043508f24c4cdf7d3c3" - dependencies: - double-ended-queue "^2.1.0-0" + version "3.2.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-3.2.0.tgz#50cb0670edbc40237b9e347d7e1f88e4620af836" through@^2.3.6: version "2.3.8" @@ -3426,8 +3419,8 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" uglify-js@^2.6, uglify-js@^2.8.27: - version "2.8.27" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.27.tgz#47787f912b0f242e5b984343be8e35e95f694c9c" + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" dependencies: source-map "~0.5.1" yargs "~3.10.0" @@ -3466,8 +3459,8 @@ util@0.10.3, util@^0.10.3: inherits "2.0.1" uuid@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" validate-npm-package-license@^3.0.1: version "3.0.1" @@ -3624,7 +3617,7 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" -yallist@^2.0.0: +yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"