Skip to content

Commit

Permalink
Merge pull request #104 from socraticorg/ae_quadratic
Browse files Browse the repository at this point in the history
adding factoring support
  • Loading branch information
aelnaiem committed Feb 10, 2017
2 parents 16d320c + 915f539 commit 906cddf
Show file tree
Hide file tree
Showing 11 changed files with 487 additions and 96 deletions.
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

0 comments on commit 906cddf

Please sign in to comment.