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
204 changes: 125 additions & 79 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,8 +164,21 @@ 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

// e.g. x^2 - 4x -> x(x - 4)
FACTOR_SYMBOL: 'FACTOR_SYMBOL',
// e.g. x^2 - 4 -> (x - 2)(x + 2)
FACTOR_DIFFERENCE_OF_SQUARES: 'FACTOR_DIFFERENCE_OF_SQUARES',
// e.g. x^2 + 2x + 1 -> (x + 1)^2
FACTOR_PERFECT_SQUARE: 'FACTOR_PERFECT_SQUARE',
// e.g. x^2 + 3x + 2 -> (x + 1)(x + 2)
FACTOR_SUM_PRODUCT_RULE: 'FACTOR_SUM_PRODUCT_RULE',
};
2 changes: 2 additions & 0 deletions lib/checks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const canMultiplyLikeTermPolynomialNodes = require('./canMultiplyLikeTermPolynom
const canRearrangeCoefficient = require('./canRearrangeCoefficient');
const canSimplifyPolynomialTerms = require('./canSimplifyPolynomialTerms');
const hasUnsupportedNodes = require('./hasUnsupportedNodes');
const isQuadratic = require('./isQuadratic');
const resolvesToConstant = require('./resolvesToConstant');

module.exports = {
Expand All @@ -11,5 +12,6 @@ module.exports = {
canRearrangeCoefficient,
canSimplifyPolynomialTerms,
hasUnsupportedNodes,
isQuadratic,
resolvesToConstant,
};
54 changes: 54 additions & 0 deletions lib/checks/isQuadratic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const Node = require('../node');
const Symbols = require('../Symbols');

// Given a node, will determine if the expression is in the form of a quadratic
// e.g. `x^2 + 2x + 1` OR `x^2 - 1` but not `x^3 + x^2 + x + 1`
function isQuadratic(node) {
if (!Node.Type.isOperator(node, '+')) {
return false;
}

if (node.args.length > 3) {
return false;
}

// make sure only one symbol appears in the expression
const symbolSet = Symbols.getSymbolsInExpression(node);
if (symbolSet.size !== 1) {
return false;
}

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

// Check that there is one second degree term and at most one first degree
// term and at most one constant term
if (secondDegreeTerms.length !== 1 || firstDegreeTerms.length > 1 ||
constantTerms.length > 1) {
return false;
}

// check that there are no terms that don't fall into these groups
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) {
return function(node) {
if (Node.PolynomialTerm.isPolynomialTerm(node)) {
const polyTerm = new Node.PolynomialTerm(node);
const exponent = polyTerm.getExponentNode(true);
return exponent && parseFloat(exponent.value) === degree;
}
return false;
};
}

module.exports = isQuadratic;
7 changes: 5 additions & 2 deletions lib/factor/ConstantFactors.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ ConstantFactors.getPrimeFactors = function(number){
ConstantFactors.getFactorPairs = function(number){
const factors = [];

const root = Math.sqrt(number);
for (var divisor = 1; divisor <= root; divisor++) {
const bound = Math.floor(Math.sqrt(Math.abs(number)));
for (var divisor = -bound; divisor <= bound; divisor++) {
if (divisor === 0) {
continue;
}
if (number % divisor === 0) {
const quotient = number / divisor;
factors.push([divisor, quotient]);
Expand Down
Loading