Skip to content
159 changes: 157 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 @@ -422,7 +422,128 @@ 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)
{
let currentNode = tree[currentNodeIndex];
if (!checkDividendIsProduct(tree, currentNode))
{
currentNodeIndex = findNextInDFS(tree, 0, currentNodeIndex);
continue;
}

let currentRightNode = tree[currentNode.rightNode];
// If / is at front of list, we need to swap the / and * around.
if (currentNodeIndex == 0)
{
currentRightNode.parent = -1;
let temp = currentRightNode;
tree[currentNode.rightNode] = currentNode;
tree[0] = temp;

// Fix children
let b = tree[currentNode.leftNode];
b.parent = tree.indexOf(currentNode);

let c = tree[currentRightNode.leftNode];
c.parent = 0;
}

// If not, make * child of /s parent
else
{
let currentNodeParent = tree[currentNode.parent];
currentRightNode.parent = currentNode.parent;
if (currentNodeParent.leftNode == currentNodeIndex)
currentNodeParent.leftNode = tree.indexOf(currentRightNode);
else
currentNodeParent.rightNode = tree.indexOf(currentRightNode);
}

// Check quotient is in form ac/b, and needs to be transformed
// (Current node is /, and right child is *
// Make a (right child of *) the right child of /
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 indentation is inconsistent with the second comment line. The comment continuation should align with the start of the previous comment line or be properly formatted as a multi-line comment.

Suggested change
// Make a (right child of *) the right child of /
// Make a (right child of *) the right child of /

Copilot uses AI. Check for mistakes.
currentNode.rightNode = currentRightNode.rightNode;
let a = tree[currentNode.rightNode];
a.parent = tree.indexOf(currentNode);

// Make / the right child of *
currentRightNode.rightNode = tree.indexOf(currentNode);
currentNode.parent = tree.indexOf(currentRightNode);
Comment on lines +469 to +473
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.

After tree manipulation that modifies indices by pushing new nodes and swapping positions, using tree.indexOf() to search for nodes can be inefficient and error-prone. The code relies on object reference equality, but if the tree is modified extensively, this could lead to unexpected behavior. Consider tracking indices directly or using a more robust identification system for nodes.

Copilot uses AI. Check for mistakes.

currentNodeIndex = findNextInDFS(tree, 0, tree.indexOf(currentNode));
}

// Convert all quotients to form 1/b * a
Comment on lines +477 to +478
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[currentNodeIndex];
if (!checkDividendIsNot1(tree, currentNode))
{
currentNodeIndex = findNextInDFS(tree, 0, currentNodeIndex);
continue;
}

let a = tree[currentNode.rightNode];

// Add * node
let multiplyNode = {
content: '*',
type: "operator",
leftNode: tree.indexOf(a),
rightNode: currentNodeIndex,
parent: currentNode.parent,
depth: -1
};

tree.push(multiplyNode);
Comment on lines +492 to +501
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.

When creating the multiplyNode, leftNode is set to tree.indexOf(a) which is the index before the multiplyNode is pushed to the tree. However, after tree.push(multiplyNode), if currentNodeIndex is 0, the code swaps positions of nodes in the tree array. This swap invalidates the leftNode index that was stored. The leftNode should be recalculated or set after all tree modifications are complete.

Copilot uses AI. Check for mistakes.

// If / is at front of list, we need to swap the / and * around.
if (currentNodeIndex == 0)
{
let temp = multiplyNode;
tree[tree.indexOf(multiplyNode)] = currentNode;
tree[0] = temp;

multiplyNode.parent = -1;
multiplyNode.rightNode = tree.indexOf(currentNode);
// Fix children of /
let b = tree[currentNode.leftNode];
b.parent = tree.indexOf(currentNode);
}
Comment on lines +504 to +515
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.

When currentNodeIndex is 0, the code swaps multiplyNode and currentNode positions in the tree array. However, the multiplyNode.leftNode value was set to tree.indexOf(a) before the swap. After the swap at lines 496-497, this index may no longer point to the correct node if 'a' was also at index 0 or at the position where multiplyNode ends up. The leftNode reference should be updated after the swap to ensure it points to the correct node.

Copilot uses AI. Check for mistakes.

// If not, make * the right node of its parent
else
{
let multiplyNodeParent = tree[currentNode.parent];
multiplyNodeParent.rightNode = tree.indexOf(multiplyNode);
}

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

// Add 1 node
let oneNode = {
content: '1',
type: "number",
leftNode: -1,
rightNode: -1,
parent: tree.indexOf(currentNode),
depth: -1
};

tree.push(oneNode);
currentNode.rightNode = tree.indexOf(oneNode);

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

Comment on lines +540 to +542
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.indexOf(currentNode));
}

currentNode = rootNodeIndex;
while (currentNodeIndex != -1)
{
let currentNode = tree[currentNodeIndex];
Expand Down Expand Up @@ -534,6 +655,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 != "operator" || node.content != '/')
return false;

let currentRightNode = tree[node.rightNode];
if (currentRightNode.type != "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 +675 to +678
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 +675 to +681
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 != "operator" || node.content != '/')
return false;

let currentRightNode = tree[node.rightNode];
if (currentRightNode.type == "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