Skip to content
132 changes: 130 additions & 2 deletions pages/index/script.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// TODO: Eventually, put this in a separate file
let RAW_SOLUTION = "1/2 x^2 sin(2x) + 1/2 x cos(2x) - 1/4 sin(2x) + c";
let SOLUTION = normaliseTree(strToTree(RAW_SOLUTION));
let SOLUTION = normaliseTree(strToTree(RAW_SOLUTION)); // TODO: Add normaliseTree back
let answerText = document.getElementById("answerText"); // Answer that appears on win modal
answerText.innerText = `\\(${RAW_SOLUTION}\\)`;

Expand Down Expand Up @@ -382,7 +382,101 @@ function postfixToTree(components, index=0, parentIndex=-1, depth=0)
// RETURNS: list normalised tree
function normaliseTree(tree, rootNodeIndex=0)
{
let currentNodeIndex = rootNodeIndex;
// Convert all quotients to form a/b * c
let currentNodeIndex = 0;
while (currentNodeIndex != -1)
{
// Check quotient is in form ac/b, and needs to be transformed
// (Current node is /, and right child is *)
let currentNode = tree.Get(currentNodeIndex);
if (!checkDividendIsProduct(tree, currentNode))
{
currentNodeIndex = findNextInDFS(tree, 0, currentNodeIndex);
continue;
}

let divisionNode = currentNode;
// Make a (right child of *) the right child of /
let multiplicationNode = tree.Get(divisionNode.rightNode);

// If / is root node, we need to make * root node
if (tree.Find(currentNode) == tree.root)
{
tree.root = tree.Find(multiplyNode);
}

// If not, make * the child of /'s parent
else
{
let divisionNodeParent = tree.Get(divisionNode.parent);
if (divisionNodeParent.leftNode == tree.Find(divisionNode))
{
divisionNodeParent.leftNode = tree.Find(multiplicationNode);
}
else if (divisionNodeParent.rightNode == tree.Find(divisionNode))
{
divisionNodeParent.rightNode = tree.Find(multiplicationNode);
}
}

divisionNode.rightNode = multiplicationNode.rightNode;
let a = tree.Get(divisionNode.rightNode);
a.parent = tree.Find(divisionNode);

// Make / the right child of *
multiplicationNode.rightNode = tree.Find(divisionNode);
divisionNode.parent = tree.Find(multiplicationNode);

// TODO: If / is at front of list, we need to swap the / and * around.
currentNodeIndex = findNextInDFS(tree, 0, tree.Find(divisionNode));
}

// Convert all quotients to form 1/b * a
Comment on lines +433 to +434
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The comment indentation is inconsistent - it should be aligned with the rest of the code block. This line appears to have extra leading tabs.

Suggested change
// Convert all quotients to form 1/b * a
// Convert all quotients to form 1/b * a

Copilot uses AI. Check for mistakes.
currentNodeIndex = 0;
while (currentNodeIndex != -1)
{
let currentNode = tree.Get(currentNodeIndex);
if (!checkDividendIsNot1(tree, currentNode))
{
currentNodeIndex = findNextInDFS(tree, 0, currentNodeIndex);
continue;
}

let a = tree.Get(currentNode.rightNode);

// Add * node
let multiplyNode = new Node("operator", Operator.MULTIPLICATION, leftNode=tree.Find(a), rightNode=currentNodeIndex);

// If / is root node, we need to make * root node
if (tree.Find(currentNode) == tree.root)
{
tree.AddAsRoot(multiplyNode);
}

// If not, make * the right node of its parent
else
{
let multiplyNodeParent = tree.Get(currentNode.parent);
multiplyNodeParent.rightNode = -1;
tree.Add(multiplyNode, multiplyNodeParent);
}

currentNode.parent = tree.Find(multiplyNode);
a.parent = tree.Find(multiplyNode);

// Add 1 node
let oneNode = newNode("number", '1');
tree.Add(oneNode, currentNode);

currentNode.rightNode = tree.Find(oneNode);

// Change current node index to index of * (to traverse over a)
currentNodeIndex = tree.Find(multiplyNode);

Comment on lines 473 to 475
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The variable currentNodeIndex is set to tree.indexOf(multiplyNode) at line 530, but then immediately overwritten at line 532 with findNextInDFS result. The assignment at line 530 appears to be dead code and serves no purpose. Either remove this line or clarify its intent in a comment.

Suggested change
// Change current node index to index of * (to traverse over a)
currentNodeIndex = tree.indexOf(multiplyNode);
// Move to the next node in DFS after the updated current node

Copilot uses AI. Check for mistakes.
currentNodeIndex = findNextInDFS(tree, 0, tree.Find(currentNode));
}

currentNodeIndex = tree.root;
while (currentNodeIndex != -1)
{
let currentNode = tree.Get(currentNodeIndex);
Expand Down Expand Up @@ -494,6 +588,40 @@ function findCommutativeNodes(tree, opNodeIndex, operator)
"parents": commutativeParentsList};
}

// Checks whether current node is a /, and if its dividend is a product.
// Essentially, check for divisions in form of ab/c (as these need to be normalised to a/b * c)
// INPUTS: tree, node in tree
// RETURNS: bool true if dividend is product, false if not
function checkDividendIsProduct(tree, node)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The function name uses "dividend" but it should be "divisor". In the expression a/b, "a" is the dividend (numerator) and "b" is the divisor (denominator). This function checks if the right child (which represents "a" in the tree structure) is a product, but the comment and function name are misleading. The function should be named checkDivisorIsProduct or the comment should clarify the tree structure.

Copilot uses AI. Check for mistakes.
{
// Check if node is /, and right child is *
if (node.type != NodeType.OPERATOR || node.content != '/')
return false;

let currentRightNode = tree.Get(node.rightNode);
if (currentRightNode.type != NodeType.OPERATOR || currentRightNode.content != '*')
return false;

return true;
}

// Checks whether current node is a /, and is dividend is something other than 1
// Essentially, checks for divisions in the form of a/b (as these need to be normalised to 1/b * a)
// INPUTS: tree, node in tree
// RETURNS: bool true is dividend is not 1, false if it is
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The comment has a typo: "true is dividend" should be "true if dividend".

Suggested change
// RETURNS: bool true is dividend is not 1, false if it is
// RETURNS: bool true if dividend is not 1, false if it is

Copilot uses AI. Check for mistakes.
Comment on lines +608 to +611
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The comment refers to "is dividend" but should say "its dividend" for proper grammar.

Suggested change
// Checks whether current node is a /, and is dividend is something other than 1
// Essentially, checks for divisions in the form of a/b (as these need to be normalised to 1/b * a)
// INPUTS: tree, node in tree
// RETURNS: bool true is dividend is not 1, false if it is
// Checks whether current node is a /, and its dividend is something other than 1
// Essentially, checks for divisions in the form of a/b (as these need to be normalised to 1/b * a)
// INPUTS: tree, node in tree
// RETURNS: bool true if its dividend is not 1, false if it is

Copilot uses AI. Check for mistakes.
function checkDividendIsNot1(tree, node)
{
// Check if node is /, and right child is not 1
Comment on lines +608 to +614
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

Similarly to checkDividendIsProduct, this function name uses "dividend" but should use "divisor" for mathematical accuracy. In a/b, the function checks if "a" (the right node) is not "1", but "a" would be the divisor in the tree representation, not the dividend. The function should be named checkDivisorIsNot1 for clarity.

Suggested change
// Checks whether current node is a /, and is dividend is something other than 1
// Essentially, checks for divisions in the form of a/b (as these need to be normalised to 1/b * a)
// INPUTS: tree, node in tree
// RETURNS: bool true is dividend is not 1, false if it is
function checkDividendIsNot1(tree, node)
{
// Check if node is /, and right child is not 1
// Checks whether current node is a /, and its divisor is something other than 1.
// Essentially, checks for divisions in the form of a/b (as these need to be normalised to 1/b * a).
// INPUTS: tree, node in tree
// RETURNS: bool true if divisor is not 1, false if it is
function checkDivisorIsNot1(tree, node)
{
// Check if node is /, and right child (the divisor) is not 1

Copilot uses AI. Check for mistakes.
if (node.type != NodeType.OPERATOR || node.content != '/')
return false;

let currentRightNode = tree.Get(node.rightNode);
if (currentRightNode.type == NodeType.NUMBER && currentRightNode.content == '1')
return false;

return true;
}

// Compares 2 binary trees, and returns true if their contents are equal.
// INPUTS: 2 binary trees - b1, b2
// RETURNS: bool - are they equal?
Expand Down
9 changes: 9 additions & 0 deletions pages/index/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,15 @@ class Tree {
this.#_body.push(node);
return;
}

// Add a node to the tree as the root node. Note: The previous root node will need its parent node to be assigned separately.
// INPUTS: Node to add as root
// RETURNS: none.
AddAsRoot(node) {
this.#_body.push(node);
this.root = this.Find(node);
return;
}

// Remove a specified node from the graph.
// INPUTS: Node to remove
Expand Down