This repository was archived by the owner on Aug 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 282
Expand file tree
/
Copy pathdivideByGCD.js
More file actions
110 lines (92 loc) · 3.85 KB
/
divideByGCD.js
File metadata and controls
110 lines (92 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
const math = require('mathjs');
const ChangeTypes = require('../../ChangeTypes');
const evaluate = require('../../util/evaluate');
const Node = require('../../node');
// Simplifies a fraction (with constant numerator and denominator) by dividing
// the top and bottom by the GCD, if possible.
// e.g. 2/4 --> 1/2 10/5 --> 2x
// Also simplified negative signs
// e.g. -1/-3 --> 1/3 4/-5 --> -4/5
// Note that -4/5 doesn't need to be simplified.
// Note that our goal is for the denominator to always be positive. If it
// isn't, we can simplify signs.
// Returns a Node.Status object
function divideByGCD(fraction) {
if (!Node.Type.isOperator(fraction) || fraction.op !== '/') {
return Node.Status.noChange(fraction);
}
// If it's not an integer fraction, all we can do is simplify signs
if (!Node.Type.isIntegerFraction(fraction, true)) {
return Node.Status.noChange(fraction);
}
const substeps = [];
let newNode = fraction.cloneDeep();
const numeratorValue = parseInt(evaluate(fraction.args[0]));
const denominatorValue = parseInt(evaluate(fraction.args[1]));
// The gcd is what we're dividing the numerator and denominator by.
let gcd = math.gcd(numeratorValue, denominatorValue);
// A greatest common denominator is technically defined as always positive,
// but since our goal is to reduce negative signs or move them to the
// numerator, a negative denominator always means we want to flip signs
// of both numerator and denominator.
// e.g. -1/-3 --> 1/3 4/-5 --> -4/5
if (denominatorValue < 0) {
gcd *= -1;
}
if (gcd === 1) {
return Node.Status.noChange(fraction);
}
if (numeratorValue === denominatorValue) {
newNode = Node.Creator.constant(1);
return Node.Status.nodeChanged(
ChangeTypes.SIMPLIFY_FRACTION, fraction, newNode, true);
}
// STEP 1: Find GCD
// e.g. 15/6 -> (5*3)/(2*3)
let status = findGCD(newNode, gcd, numeratorValue, denominatorValue);
substeps.push(status);
newNode = Node.Status.resetChangeGroups(status.newNode);
// STEP 2: Cancel GCD
// (5*3)/(2*3) -> 5/2
status = cancelGCD(newNode, gcd, numeratorValue, denominatorValue);
substeps.push(status);
newNode = Node.Status.resetChangeGroups(status.newNode);
return Node.Status.nodeChanged(
ChangeTypes.SIMPLIFY_FRACTION, fraction, newNode, true, substeps);
}
// Returns a substep where the GCD is factored out of numerator and denominator
// e.g. 15/6 -> (5*3)/(2*3)
function findGCD(node, gcd, numeratorValue, denominatorValue) {
let newNode = node.cloneDeep();
// manually set change group of the GCD nodes to be the same
const gcdNode = Node.Creator.constant(gcd);
gcdNode.changeGroup = 1;
let intermediateNumerator = Node.Creator.constant(numeratorValue);
if (numeratorValue / gcd !== 1) {
intermediateNumerator = Node.Creator.parenthesis(Node.Creator.operator(
'*', [Node.Creator.constant(numeratorValue / gcd), gcdNode]));
}
const intermediateDenominator = Node.Creator.parenthesis(Node.Creator.operator(
'*', [Node.Creator.constant(denominatorValue / gcd), gcdNode]));
newNode = Node.Creator.operator(
'/', [intermediateNumerator, intermediateDenominator]);
return Node.Status.nodeChanged(
ChangeTypes.FIND_GCD, node, newNode, false);
}
// Returns a substep where the GCD is cancelled out of numerator and denominator
// e.g. (5*3)/(2*3) -> 5/2
function cancelGCD(node, gcd, numeratorValue, denominatorValue) {
let newNode;
const newNumeratorNode = Node.Creator.constant(numeratorValue/gcd);
const newDenominatorNode = Node.Creator.constant(denominatorValue/gcd);
if (parseFloat(newDenominatorNode.value) === 1) {
newNode = newNumeratorNode;
}
else {
newNode = Node.Creator.operator(
'/', [newNumeratorNode, newDenominatorNode]);
}
return Node.Status.nodeChanged(
ChangeTypes.CANCEL_GCD, node, newNode, false);
}
module.exports = divideByGCD;