Skip to content
This repository was archived by the owner on Aug 29, 2024. It is now read-only.

#76 - Better break-up fraction. #158

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
42 changes: 34 additions & 8 deletions lib/checks/canFindDenominatorInNumerator.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const Node = require('../node');

// Returns true if by adding a term you can simplify part of the function into an integer
// e.g. (2x+1)/(2x+3) -> True
// Returns true if by adding a term you can simplify part of the function into
// an integer
// e.g. (2x+1)/(2x+3) -> True because of the following simplification
// (2x+1)/(2x+3) -> (2x + 3)/(2x + 3) - 2/(2x + 3) -> 1 - 2/(2x + 3)
// e.g. (2x+1)/(2x^2 + 3) -> False
function canFindDenominatorInNumerator(node) {
if (!Node.Type.isOperator(node) || node.op !== '/' ) {
Expand All @@ -10,7 +12,6 @@ function canFindDenominatorInNumerator(node) {
if (node.args.length !== 2) {
return false;
}

let numerator = node.args[0];
let denominator = node.args[1];
if (Node.Type.isParenthesis(numerator)) {
Expand All @@ -19,17 +20,42 @@ function canFindDenominatorInNumerator(node) {
if (Node.Type.isParenthesis(denominator)) {
denominator = denominator.content;
}
if (!(numerator.op === '+' || numerator.op === '-' ||
Copy link
Contributor

Choose a reason for hiding this comment

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

can you add a comment explaining why you check for these things and why these are the only conditions that make us keep going?

Copy link
Author

Choose a reason for hiding this comment

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

Okay

denominator.op === '+' || numerator.op === '-')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you have numerator.op === '-' twice?

Copy link
Author

Choose a reason for hiding this comment

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

whoops that should be denominator.op

return false;
}
if (denominator.op !== '+') {
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you have this condition? what about (2x + 2) / 2x

Copy link
Author

Choose a reason for hiding this comment

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

I was thinking we don't need to use this extended version on it. The original breakUpNumerator function works fine for this case. But I think you've highlighted an interesting point, anyone reading the code would be confused.

Copy link
Contributor

Choose a reason for hiding this comment

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

ooo true. Maybe just add a comment saying where the other case is handled then!

return false;
}

let numeratorFirstTerm;
if (numerator.op === '+') {
numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm concerned that we can just assume it'll be a polynomial term

(what if the second arg is a polynomial term? what if there are more than 2 args?)

Sorry this got more complicated haha. If you want, we can chat on gitter or something and figure out how to attack this

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, this is what I wanted to talk about actually. One of the main issues is that it's not sorted, so I wanted to ask you all if we should add a sorting function which will run before this. Or if you don't see any merit in that, we could add some logic to find the highest polynomial term right into the function (or outside of it if you want).

Copy link
Author

Choose a reason for hiding this comment

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

yeah sure, I'm down to chat on gitter

Copy link
Contributor

Choose a reason for hiding this comment

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

sweet - okay I actually am busy all day tomorrow >.> (first day of work!!)

does Wed evening work for you?

Copy link
Contributor

Choose a reason for hiding this comment

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

or can we chat asynchronously - I can try to think about solutions to this tomorrow

Copy link
Author

Choose a reason for hiding this comment

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

uhh yeah lets just chat asynchronously then, no worries take your time, good luck!

Copy link
Contributor

@evykassirer evykassirer May 10, 2017

Choose a reason for hiding this comment

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

I think you could start with doing this rule only if there are exactly one or two args, the first is always a polynomial term, and the second if it exists is always a constant

i.e. come up with very limited cases and then test for them before going forward and do nothing if none of those cases work

and then after merging this you could add some more cases if they're easy enough

what do you think? @

Copy link
Author

Choose a reason for hiding this comment

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

oh whoops didn't see this, sure sounds good!

}
else if (numerator.op === '*') {
numeratorFirstTerm = new Node.PolynomialTerm(numerator);
}

let denominatorFirstTerm;
if (denominator.op === '+') {
denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]);
}
else if (denominator.op === '*') {
denominatorFirstTerm = new Node.PolynomialTerm(denominator);
}

if (numerator.op !== '+') {
if (!(numeratorFirstTerm)) {
return false;
}
if (!('args' in denominator)) {
if (!(denominatorFirstTerm)) {
return false;
}

if (!(numeratorFirstTerm.getSymbolName() === 'x' && denominatorFirstTerm.getSymbolName() === 'x')) {
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 it's better to just say they have the same symbol. Then if they share the symbol y, it'd work too!

Copy link
Author

Choose a reason for hiding this comment

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

oh true, I didn't think about the symbol being a different name. The reason I did it to equal 'x' was to restrict it to the case of x^1

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah I think just doing getSymbolName() === getSymbolName() would work!

Copy link
Author

@eltonlaw eltonlaw May 9, 2017

Choose a reason for hiding this comment

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

wait I'm not really getting the use of getSymbolName() actually, what other values can the symbol even take, it seems like it always returns 'x'. I originally thought the getSymbolName would grab the exponent as well, but that's actually covered by getExponentNode.

Is getSymbolName just for when there are multiple variables? Cause that seems way out of the scope for now anyways.

Would it be better to check that denominatorFirstTerm.getExponentNode() === numeratorFirstTerm.getExponentNode() or check if they equal undefined? I guess we could keep the getSymbolName === getSymbolName there just for good measure. Is this correct or am I missing something?

Copy link
Contributor

Choose a reason for hiding this comment

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

using getSymbolName makes sure the symbol in numerator and denominator are the same - i.e. both x or both y or whatever - you could take any example with xs and change them to ys and it should also work (actually can you add a test for that? ^_^)

numeratorFirstTerm.getSymbolName() === denominatorFirstTerm.getSymbolName() is what I was thinking

return false;
}
const numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]);
const denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]);

return numeratorFirstTerm.getSymbolName() === denominatorFirstTerm.getSymbolName();
return true;
}

module.exports = canFindDenominatorInNumerator;
26 changes: 20 additions & 6 deletions lib/simplifyExpression/breakUpNumeratorSearch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ function breakUpNumerator(node) {
const fractionList = [];
let denominator = node.args[1];

// Check if we can add a constant to make the fraction nicer
// fraction e.g. (2+x)/(5+x) -> (5+x)/(5+x) + 3/(5+x)
// Check if we can add/substract a constant to make the fraction nicer
Copy link
Contributor

Choose a reason for hiding this comment

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

substract -> subtract

// fraction e.g. (2+x)/(5+x) -> (5+x)/(5+x) - 3/(5+x)
if (canFindDenominatorInNumerator(node)) {
let denominatorParenRemoved = false;
if (Node.Type.isParenthesis(denominator)) {
Expand All @@ -41,12 +41,26 @@ function breakUpNumerator(node) {
const newNumerator = [];

// The constant value difference between the numerator and the denominator
const numeratorConstant = parseInt(numerator.args[1].value);
const denominatorConstant = parseInt(denominator.args[1].value);
const addedConstant = numeratorConstant - denominatorConstant;
const num_n = numerator.args.length;
const den_n = denominator.args.length;
const numeratorFirstTerm = new Node.PolynomialTerm(numerator.args[0]);
const denominatorFirstTerm = new Node.PolynomialTerm(denominator.args[0]);
const numeratorPolyCoeff = numeratorFirstTerm.getCoeffValue();
const denominatorPolyCoeff = denominatorFirstTerm.getCoeffValue();
const multiplier = numeratorPolyCoeff / denominatorPolyCoeff;

const numeratorConstant = parseInt(numerator.args[num_n-1].value) || 0;
const denominatorConstant = parseInt(denominator.args[den_n-1].value) || 0;
const addedConstant = numeratorConstant - (denominatorConstant * multiplier);

if (multiplier === 1) {
newNumerator.push(denominator);
}
else {
const multiplierNode = Node.Creator.constant(multiplier);
newNumerator.push(Node.Creator.operator('*', [multiplierNode, denominator]));
}
newNumerator.push(Node.Creator.constant(addedConstant));
newNumerator.push(denominator);

numerator = newNumerator;

Expand Down
3 changes: 3 additions & 0 deletions test/checks/checks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ describe('canSimplifyPolynomialTerms addition', function() {
describe('canSimplifyPolynomialTerms denominator in numerator', function() {
const tests = [
['(2x + 3)/(2x + 2)', true],
Copy link
Contributor

Choose a reason for hiding this comment

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

can you add a bunch more tests? especially a cases that should be false

you can use some of my comments above for edge cases to test for too :)

['(2x+3)/(2x)', false],
['(5x + 3)/(4)', false],
['(2x)/(2x + 3)', true],
];
tests.forEach(t => testCanCombine(t[0], t[1]));
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ describe('breakUpNumerator', function() {
['(x+3+y)/3', '(x / 3 + 3/3 + y / 3)'],
['(2+x)/4', '(2/4 + x / 4)'],
['2(x+3)/3', '2 * (x / 3 + 3/3)'],
['(2x + 3)/(2x + 2)', '(1 / (2x + 2) + (2x + 2) / (2x + 2))'],
['(2x + 3)/(2x + 2)', '((2x + 2) / (2x + 2) + 1 / (2x + 2))'],
['(2x - 3)/(2x + 2)', '((2x + 2) / (2x + 2) - 5 / (2x + 2))'],
['(2x + 3)/(2x)', '(2x / (2x) + 3 / (2x))'],
['(3 + 2x)/(2x)', '(3 / (2x) + 2x / (2x))'],
['(4x + 3)/(2x + 2)', '(2 * (2x + 2) / (2x + 2) - 1 / (2x + 2))'],
// ['(2x)/(3 + 2x)', '((3 + 2x) / (3 + 2x) - 3 / (3 + 2x))'],
Copy link
Contributor

Choose a reason for hiding this comment

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

why are these commented out?

Copy link
Author

Choose a reason for hiding this comment

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

As mentioned earlier, I didn't know what to do without some sort of way to sort it.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, cool - sorry for missing that

// ['(2x)/(2x + 3)', '((2x + 3) / (2x + 3)) - 3 / (2x + 3)'],
Copy link
Contributor

Choose a reason for hiding this comment

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

if these tests pass, uncomment them

otherwise leave a TODO with what we'll need to do in the future to make them pass

];
tests.forEach(t => testBreakUpNumeratorSearch(t[0], t[1]));
});