Skip to content
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

adding factoring support #104

Merged
merged 8 commits into from
Feb 10, 2017
Merged
195 changes: 115 additions & 80 deletions lib/ChangeTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,114 +3,147 @@
module.exports = {
NO_CHANGE: 'NO_CHANGE',

// e.g. |-3| -> 3
ABSOLUTE_VALUE: 'ABSOLUTE_VALUE',
// ARITHMETIC

// e.g. 2 + 2 -> 4 or 2 * 2 -> 4
SIMPLIFY_ARITHMETIC: 'SIMPLIFY_ARITHMETIC',

// BASICS

// e.g. 2/-1 -> -2
DIVISION_BY_NEGATIVE_ONE: 'DIVISION_BY_NEGATIVE_ONE',
// e.g. 2/1 -> 2
DIVISION_BY_ONE: 'DIVISION_BY_ONE',
// e.g. x * 0 -> 0
MULTIPLY_BY_ZERO: 'MULTIPLY_BY_ZERO',
// e.g. x * 2 -> 2x
REARRANGE_COEFF: 'REARRANGE_COEFF',
// e.g. x ^ 0 -> 1
REDUCE_EXPONENT_BY_ZERO: 'REDUCE_EXPONENT_BY_ZERO',
// e.g. 0/1 -> 0
REDUCE_ZERO_NUMERATOR: 'REDUCE_ZERO_NUMERATOR',
// e.g. 2 + 0 -> 2
REMOVE_ADDING_ZERO: 'REMOVE_ADDING_ZERO',
// e.g. x ^ 1 -> x
REMOVE_EXPONENT_BY_ONE: 'REMOVE_EXPONENT_BY_ONE',
// e.g. 1 ^ x -> 1
REMOVE_EXPONENT_BASE_ONE: 'REMOVE_EXPONENT_BASE_ONE',
// e.g. x * -1 -> -x
REMOVE_MULTIPLYING_BY_NEGATIVE_ONE: 'REMOVE_MULTIPLYING_BY_NEGATIVE_ONE',
// e.g. x * 1 -> x
REMOVE_MULTIPLYING_BY_ONE: 'REMOVE_MULTIPLYING_BY_ONE',
// e.g. 2 - - 3 -> 2 + 3
RESOLVE_DOUBLE_MINUS: 'RESOLVE_DOUBLE_MINUS',

// COLLECT AND COMBINE

// 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',

// ADDING POLYNOMIALS

// e.g. 2x + x -> 2x + 1x
ADD_COEFFICIENT_OF_ONE: 'ADD_COEFFICIENT_OF_ONE',
// e.g. x^2 * x -> x^2 * x^1
ADD_EXPONENT_OF_ONE: 'ADD_EXPONENT_OF_ONE',
// e.g. 1/2 + 1/3 -> 5/6
ADD_FRACTIONS: 'ADD_FRACTIONS',
// e.g. (1 + 2)/3 -> 3/3
ADD_NUMERATORS: 'ADD_NUMERATORS',
// e.g. x^2 + x^2 -> 2x^2
ADD_POLYNOMIAL_TERMS: 'ADD_POLYNOMIAL_TERMS',
// e.g. 2x^2 + 3x^2 + 5x^2 -> (2+3+5)x^2
GROUP_COEFFICIENTS: 'GROUP_COEFFICIENTS',
// e.g. -x + 2x => -1*x + 2x
UNARY_MINUS_TO_NEGATIVE_ONE: 'UNARY_MINUS_TO_NEGATIVE_ONE',

// MULTIPLYING POLYNOMIALS

// e.g. x^2 * x -> x^2 * x^1
ADD_EXPONENT_OF_ONE: 'ADD_EXPONENT_OF_ONE',
// e.g. x^2 * x^3 * x^1 -> x^(2 + 3 + 1)
COLLECT_EXPONENTS: 'COLLECT_EXPONENTS',
// e.g. 2x * 3x -> (2 * 3)(x * x)
MULTIPLY_COEFFICIENTS: 'MULTIPLY_COEFFICIENTS',
// e.g. 2x * x -> 2x ^ 2
MULTIPLY_POLYNOMIAL_TERMS: 'MULTIPLY_POLYNOMIAL_TERMS',

// FRACTIONS

// e.g. (x + 2)/2 -> x/2 + 2/2
BREAK_UP_FRACTION: 'BREAK_UP_FRACTION',
// e.g. nthRoot(x ^ 2, 4) -> nthRoot(x, 2)
CANCEL_EXPONENT: 'CANCEL_EXPONENT',
// e.g. nthRoot(x ^ 2, 2) -> x
CANCEL_EXPONENT_AND_ROOT: 'CANCEL_EXPONENT_AND_ROOT',
// e.g. -2/-3 => 2/3
CANCEL_MINUSES: 'CANCEL_MINUSES',
// e.g. 2x/2 -> x
CANCEL_TERMS: 'CANCEL_TERMS',
// e.g. nthRoot(x ^ 4, 2) -> x ^ 2
CANCEL_ROOT: 'CANCEL_ROOT',
// e.g. 2 + x + 3 + x -> 5 + 2x
COLLECT_AND_COMBINE_LIKE_TERMS: 'COLLECT_AND_COMBINE_LIKE_TERMS',
// e.g. x^2 * x^3 * x^1 -> x^(2 + 3 + 1)
COLLECT_EXPONENTS: 'COLLECT_EXPONENTS',
// e.g. x + 2 + x^2 + x + 4 -> x^2 + (x + x) + (4 + 2)
COLLECT_LIKE_TERMS: 'COLLECT_LIKE_TERMS',
// e.g. 2/6 + 1/4 -> (2*2)/(6*2) + (1*3)/(4*3)
COMMON_DENOMINATOR: 'COMMON_DENOMINATOR',
// e.g. 2/6 -> 1/3
SIMPLIFY_FRACTION: 'SIMPLIFY_FRACTION',
// e.g. 2/-3 -> -2/3
SIMPLIFY_SIGNS: 'SIMPLIFY_SIGNS',

// ADDING FRACTIONS

// e.g. 1/2 + 1/3 -> 5/6
ADD_FRACTIONS: 'ADD_FRACTIONS',
// e.g. (1 + 2)/3 -> 3/3
ADD_NUMERATORS: 'ADD_NUMERATORS',
// e.g. (2+1)/5
COMBINE_NUMERATORS: 'COMBINE_NUMERATORS',
// e.g. nthRoot(2, 2) * nthRoot(3, 2) -> nthRoot(2 * 3, 2)
COMBINE_UNDER_ROOT: 'COMBINE_UNDER_ROOT',
// e.g. 2/6 + 1/4 -> (2*2)/(6*2) + (1*3)/(4*3)
COMMON_DENOMINATOR: 'COMMON_DENOMINATOR',
// e.g. 3 + 1/2 -> 6/2 + 1/2 (for addition)
CONVERT_INTEGER_TO_FRACTION: 'CONVERT_INTEGER_TO_FRACTION',
// e.g. 2 * 2 * 2 -> 2 ^ 3
CONVERT_MULTIPLICATION_TO_EXPONENT: 'CONVERT_MULTIPLICATION_TO_EXPONENT',
// e.g. 1.2 + 1/2 -> 1.2 + 0.5
DIVIDE_FRACTION_FOR_ADDITION: 'DIVIDE_FRACTION_FOR_ADDITION',
// e.g. (2*2)/(6*2) + (1*3)/(4*3) -> (2*2)/12 + (1*3)/12
MULTIPLY_DENOMINATORS: 'MULTIPLY_DENOMINATORS',
// e.g. (2*2)/12 + (1*3)/12 -> 4/12 + 3/12
MULTIPLY_NUMERATORS: 'MULTIPLY_NUMERATORS',

// MULTIPLYING FRACTIONS

// e.g. 1/2 * 2/3 -> 2/6
MULTIPLY_FRACTIONS: 'MULTIPLY_FRACTIONS',

// DIVISION

// e.g. 2/3/4 -> 2/(3*4)
SIMPLIFY_DIVISION: 'SIMPLIFY_DIVISION',
// e.g. x/(2/3) -> x * 3/2
MULTIPLY_BY_INVERSE: 'MULTIPLY_BY_INVERSE',

// DISTRIBUTION

// e.g. 2(x + y) -> 2x + 2y
DISTRIBUTE: 'DISTRIBUTE',
// e.g. -(2 + x) -> -2 - x
DISTRIBUTE_NEGATIVE_ONE: 'DISTRIBUTE_NEGATIVE_ONE',
// e.g. 2 * 4x + 2*5 --> 8x + 10 (as part of distribution)
SIMPLIFY_TERMS: 'SIMPLIFY_TERMS',

// ABSOLUTE
// e.g. |-3| -> 3
ABSOLUTE_VALUE: 'ABSOLUTE_VALUE',

// ROOTS
// e.g. nthRoot(x ^ 2, 4) -> nthRoot(x, 2)
CANCEL_EXPONENT: 'CANCEL_EXPONENT',
// e.g. nthRoot(x ^ 2, 2) -> x
CANCEL_EXPONENT_AND_ROOT: 'CANCEL_EXPONENT_AND_ROOT',
// e.g. nthRoot(x ^ 4, 2) -> x ^ 2
CANCEL_ROOT: 'CANCEL_ROOT',
// e.g. nthRoot(2, 2) * nthRoot(3, 2) -> nthRoot(2 * 3, 2)
COMBINE_UNDER_ROOT: 'COMBINE_UNDER_ROOT',
// e.g. 2 * 2 * 2 -> 2 ^ 3
CONVERT_MULTIPLICATION_TO_EXPONENT: 'CONVERT_MULTIPLICATION_TO_EXPONENT',
// e.g. nthRoot(2 * x) -> nthRoot(2) * nthRoot(x)
DISTRIBUTE_NTH_ROOT: 'DISTRIBUTE_NTH_ROOT',
// e.g. 1.2 + 1/2 -> 1.2 + 0.5
DIVIDE_FRACTION_FOR_ADDITION: 'DIVIDE_FRACTION_FOR_ADDITION',
// e.g. 2/-1 -> -2
DIVISION_BY_NEGATIVE_ONE: 'DIVISION_BY_NEGATIVE_ONE',
// e.g. 2/1 -> 2
DIVISION_BY_ONE: 'DIVISION_BY_ONE',
// e.g. nthRoot(4) * nthRoot(x^2) -> 2 * x
EVALUATE_DISTRIBUTED_NTH_ROOT: 'EVALUATE_DISTRIBUTED_NTH_ROOT',
// e.g. 12 -> 2 * 2 * 3
FACTOR_INTO_PRIMES: 'FACTOR_INTO_PRIMES',
// e.g. 2x^2 + 3x^2 + 5x^2 -> (2+3+5)x^2
GROUP_COEFFICIENTS: 'GROUP_COEFFICIENTS',
// e.g. nthRoot(2 * 2 * 2, 2) -> nthRoot((2 * 2) * 2)
GROUP_TERMS_BY_ROOT: 'GROUP_TERMS_BY_ROOT',
// e.g. x/(2/3) -> x * 3/2
MULTIPLY_BY_INVERSE: 'MULTIPLY_BY_INVERSE',
// e.g. x * 0 -> 0
MULTIPLY_BY_ZERO: 'MULTIPLY_BY_ZERO',
// e.g. 2x * 3x -> (2 * 3)(x * x)
MULTIPLY_COEFFICIENTS: 'MULTIPLY_COEFFICIENTS',
// e.g. 1/2 * 2/3 -> 2/6
MULTIPLY_FRACTIONS: 'MULTIPLY_FRACTIONS',
// e.g. (2*2)/(6*2) + (1*3)/(4*3) -> (2*2)/12 + (1*3)/12
MULTIPLY_DENOMINATORS: 'MULTIPLY_DENOMINATORS',
// e.g. (2*2)/12 + (1*3)/12 -> 4/12 + 3/12
MULTIPLY_NUMERATORS: 'MULTIPLY_NUMERATORS',
// e.g. 2x * x -> 2x ^ 2
MULTIPLY_POLYNOMIAL_TERMS: 'MULTIPLY_POLYNOMIAL_TERMS',
// e.g. nthRoot(4) -> 2
NTH_ROOT_VALUE: 'NTH_ROOT_VALUE',
// e.g. x * 2 -> 2x
REARRANGE_COEFF: 'REARRANGE_COEFF',
// e.g. 2 + 0 -> 2
REMOVE_ADDING_ZERO: 'REMOVE_ADDING_ZERO',
// e.g. x ^ 1 -> x
REMOVE_EXPONENT_BY_ONE: 'REMOVE_EXPONENT_BY_ONE',
// e.g. 1 ^ x -> 1
REMOVE_EXPONENT_BASE_ONE: 'REMOVE_EXPONENT_BASE_ONE',
// e.g. x * -1 -> -x
REMOVE_MULTIPLYING_BY_NEGATIVE_ONE: 'REMOVE_MULTIPLYING_BY_NEGATIVE_ONE',
// e.g. x * 1 -> x
REMOVE_MULTIPLYING_BY_ONE: 'REMOVE_MULTIPLYING_BY_ONE',
// e.g. x ^ 0 -> 1
REDUCE_EXPONENT_BY_ZERO: 'REDUCE_EXPONENT_BY_ZERO',
// e.g. 0/1 -> 0
REDUCE_ZERO_NUMERATOR: 'REDUCE_ZERO_NUMERATOR',
// e.g. 2 - - 3 -> 2 + 3
RESOLVE_DOUBLE_MINUS: 'RESOLVE_DOUBLE_MINUS',
// e.g. 2 + 2 -> 4 or 2 * 2 -> 4
SIMPLIFY_ARITHMETIC: 'SIMPLIFY_ARITHMETIC',
// e.g. 2/3/4 -> 2/(3*4)
SIMPLIFY_DIVISION: 'SIMPLIFY_DIVISION',
// e.g. 2/6 -> 1/3
SIMPLIFY_FRACTION: 'SIMPLIFY_FRACTION',
// e.g. 2/-3 -> -2/3
SIMPLIFY_SIGNS: 'SIMPLIFY_SIGNS',
// e.g. 2 * 4x + 2*5 --> 8x + 10 (as part of distribution)
SIMPLIFY_TERMS: 'SIMPLIFY_TERMS',
// e.g. -x + 2x => -1*x + 2x
UNARY_MINUS_TO_NEGATIVE_ONE: 'UNARY_MINUS_TO_NEGATIVE_ONE',

// Expression change types:
// SOLVING FOR A VARIABLE

// e.g. x - 3 = 2 -> x - 3 + 3 = 2 + 3
ADD_TO_BOTH_SIDES: 'ADD_TO_BOTH_SIDES',
Expand All @@ -131,12 +164,14 @@ module.exports = {
// e.g. 2 = x -> x = 2
SWAP_SIDES: 'SWAP_SIDES',

// CONSTANT EQUATION

// e.g. 2 = 2
STATEMENT_IS_TRUE: 'STATEMENT_IS_TRUE',
// e.g. 2 = 3
STATEMENT_IS_FALSE: 'STATEMENT_IS_FALSE',

// Factoring change types:
// FACTORING

// e.g. x^2 - 4x -> x(x - 4)
FACTOR_SYMBOL: 'FACTOR_SYMBOL',
Expand Down
12 changes: 8 additions & 4 deletions lib/checks/isQuadratic.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ function isQuadratic(node) {
return false;
}

// get the number of symbols in the expression and ensure there's only one
Copy link
Contributor

Choose a reason for hiding this comment

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

sorry I'm being really picky now so you don't have to change this but

I think this comment is pretty close to what the code is doing, I meant more like
"make sure only one symbol appears in the expression, i.e. just x and not both x and y"

the point of confusion for people could be that it's a set (which you show in the var name which is helpful, so it's probably fine) and there can be multiple symbol terms (multiple symbols) as long as they're of the same symbol name

const symbolSet = Symbols.getSymbolsInExpression(node);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this might be confusing to an outsider - add a comment explaining what this is checking?

if (symbolSet.size !== 1) {
return false;
}

const secondDegreeTerms = node.args.filter(isPolynomialTermOfDegree('2'));
const firstDegreeTerms = node.args.filter(isPolynomialTermOfDegree('1'));
const secondDegreeTerms = node.args.filter(isPolynomialTermOfDegree(2));
const firstDegreeTerms = node.args.filter(isPolynomialTermOfDegree(1));
const constantTerms = node.args.filter(Node.Type.isConstant);
Copy link
Contributor

Choose a reason for hiding this comment

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

what if there are other args that don't fall into one of these buckets? we should return false right? so I guess check that the length of each of these add up to the number of args


// Check that there is one second degree term and at most one first degree
Expand All @@ -29,19 +30,22 @@ function isQuadratic(node) {
}

// check that there are no terms that don't fall into these groups
if (secondDegreeTerms.length + firstDegreeTerms.length + constantTerms.length !== node.args.length) {
if ((secondDegreeTerms.length + firstDegreeTerms.length +
constantTerms.length) !== node.args.length) {
return false;
}

return true;
}

// Given a degree, returns a function that checks if a node
// is a polynomial term of the given degree.
function isPolynomialTermOfDegree(degree) {
Copy link
Contributor

Choose a reason for hiding this comment

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

add a docstring that also makes it clear we have to pass a string as the degree (which is unfortunate :p)

return function(node) {
if (Node.PolynomialTerm.isPolynomialTerm(node)) {
const polyTerm = new Node.PolynomialTerm(node);
const exponent = polyTerm.getExponentNode(true);
return exponent && exponent.value === degree;
return exponent && parseFloat(exponent.value) === degree;
}
return false;
};
Expand Down
Loading