diff --git a/avl.js b/avl.js index 6128e33..9479c13 100644 --- a/avl.js +++ b/avl.js @@ -41,28 +41,32 @@ class AVLTree extends RankedTree { if (rotatingAroundRoot) { this.root = newRoot; } - this.record(); + this.record("Fixing 0-child by single rotation: by z"); z.demote(); - this.record(); + this.record("Fixing 0-child by single rotation: demoting z"); } else if (nodeDifference(y) == 1) { rotateLeft(x); - this.record(); + this.record( + "Fixing 0-child by double-rotation: first rotation by x" + ); newRoot = rotateRight(z); if (rotatingAroundRoot) { this.root = newRoot; } - this.record(); + this.record( + "Fixing 0-child by double-rotation: second rotation by z" + ); y.promote(); - this.record(); + this.record("Fixing 0-child by double-rotation: promoting y"); x.demote(); - this.record(); + this.record("Fixing 0-child by double-rotation: demoting x"); z.demote(); - this.record(); + this.record("Fixing 0-child by double-rotation: demoting z"); } } @@ -72,7 +76,7 @@ class AVLTree extends RankedTree { x.parent.promote(); x = x.parent; - this.record(); + this.record("Insertion rebalance: promoting (0, 1) parent"); diffs = nodeDifferences(x.parent).sort(); } @@ -122,7 +126,7 @@ class AVLTree extends RankedTree { break; default: rotateRight(y); - this.record(); + this.record("AVL deletion, fixing by double-rotation: by y"); newRoot = rotateLeft(x); break; @@ -130,13 +134,13 @@ class AVLTree extends RankedTree { if (rotatingAroundRoot) { this.root = newRoot; } - this.record(); + this.record("AVL deletion, fixing by rotation by x"); [newRoot.left, newRoot.right, newRoot] .filter((x) => x) .forEach((n) => { updateRank(n); - this.record(); + this.record("Updating rank of nodes affected by rotations"); }); return factor == 0; @@ -149,7 +153,9 @@ class AVLTree extends RankedTree { switch (factor) { case 0: updateRank(x); - this.record(); + this.record( + "Updating rank of node affected by the propagation of delete" + ); return false; case -1: diff --git a/comparator_visualization.js b/comparator_visualization.js index e3fbd5d..c73541d 100644 --- a/comparator_visualization.js +++ b/comparator_visualization.js @@ -1,9 +1,11 @@ let lRecorder = new Recorder( d3.select("#left").graphviz(), + null, "left" ).renderAtOnce(); let rRecorder = new Recorder( d3.select("#right").graphviz(), + null, "right" ).renderAtOnce(); diff --git a/index.html b/index.html index 0c5db4c..fd2f609 100755 --- a/index.html +++ b/index.html @@ -32,6 +32,7 @@ +

Current operation:

diff --git a/ranked_tree.js b/ranked_tree.js index 1f436c7..86c3bf3 100644 --- a/ranked_tree.js +++ b/ranked_tree.js @@ -68,11 +68,11 @@ class RankedTree { return this.toDot().join("\n"); } - record() { + record(message) { if (!this.recorder) { return; } - this.recorder.record(this); + this.recorder.record(this, message); } rank() { @@ -92,7 +92,7 @@ class RankedTree { if (!this.root) { this.root = insertedNode; - this.record(); + this.record("Inserting key to the root"); return; } @@ -108,7 +108,7 @@ class RankedTree { } else { parent.right = insertedNode; } - this.record(); + this.record("Inserting key"); this.insertRebalance(insertedNode); } @@ -146,12 +146,14 @@ class RankedTree { node.value = successor.value; successor.value = null; - this.record(); + this.record( + "Replacing the value of deleted node with successor value" + ); return this.deleteNode(successor); } - this.record(); + this.record("Replacing the node with one of its children"); return [y, parent]; } diff --git a/recorder.js b/recorder.js index eb88514..a0c270d 100644 --- a/recorder.js +++ b/recorder.js @@ -1,8 +1,23 @@ const DURATION = 500; +class Record { + constructor(tree, message) { + this.tree = tree; + this.message = message; + } + + render(graph, textBox) { + graph.renderDot(this.tree); + if (textBox) { + textBox.textContent = this.message; + } + } +} + class Recorder { - constructor(graph, id) { + constructor(graph, textBox, id) { this.graph = graph; + this.textBox = textBox; this.id = id; this.rendered = -1; @@ -20,8 +35,6 @@ class Recorder { } unblockRendering() { - console.log(this); - console.log(`Unblocked renderer #${this.id}`); this.rendering = false; } @@ -40,7 +53,7 @@ class Recorder { if (this.atOnce) { if (this.rendered != this.states.length - 1) { this.rendered = this.states.length - 1; - this.graph.renderDot(this.states[this.rendered]); + this.states[this.rendered].render(this.graph, this.textBox); } return; @@ -57,7 +70,7 @@ class Recorder { this.rendering = true; this.rendered++; - this.graph.renderDot(this.states[this.rendered]); + this.states[this.rendered].render(this.graph, this.textBox); } previous() { @@ -68,9 +81,9 @@ class Recorder { throw "not implemented!"; } - record(tree) { + record(tree, message) { // TODO: adjust join if necessary - this.states.push(tree.toDot().join("")); + this.states.push(new Record(tree.toDot().join(""), message)); this.render(); } } diff --git a/visualization.js b/visualization.js index 019d62f..c46e4f7 100644 --- a/visualization.js +++ b/visualization.js @@ -1,4 +1,8 @@ -let recorder = new Recorder(d3.select("#graph").graphviz(), "graph"); +let recorder = new Recorder( + d3.select("#graph").graphviz(), + document.getElementById("comment"), + "graph" +); let tree = new WAVLTree(); tree.recorder = recorder; diff --git a/wavl.js b/wavl.js index b62fc0f..51eb31e 100644 --- a/wavl.js +++ b/wavl.js @@ -36,36 +36,48 @@ class WAVLTree extends AVLTree { if (rotatingAroundRoot) { this.root = newRoot; } - this.record(); + this.record( + "Final step of deletion rebalance: single rotation by parent of y" + ); y.promote(); - this.record(); + this.record("Final step of deletion rebalance: promotion of y"); z.demote(); - this.record(); + this.record("Final step of deletion rebalance: demotion of z"); if (z.type() == LEAF) { z.demote(); - this.record(); + this.record( + "Final step of deletion rebalance: demotion of leaf z" + ); } } else if (wDiff == 2 && v && v.parent) { rotateRight(v.parent); - this.record(); + this.record( + "Final step of deletion rebalance: first of double rotation by parent of v" + ); newRoot = rotateLeft(v.parent); if (rotatingAroundRoot) { this.root = newRoot; } - this.record(); + this.record( + "Final step of deletion rebalance: second of double rotation by parent of v" + ); v.promote().promote(); - this.record(); + this.record( + "Final step of deletion rebalance: double promotion of v" + ); y.demote(); - this.record(); + this.record("Final step of deletion rebalance: demotion of y"); z.demote().demote(); - this.record(); + this.record( + "Final step of deletion rebalance: double demotion of z" + ); } } @@ -85,11 +97,11 @@ class WAVLTree extends AVLTree { (yDiff == 2 || nodeDifferences(y).equals([2, 2])) ) { parent.demote(); - this.record(); + this.record("Propagating error by demoting parent"); if (yDiff != 2) { y.demote(); - this.record(); + this.record("Demoting y"); } x = parent; @@ -139,12 +151,12 @@ class WAVLTree extends AVLTree { deleteFixup(y, parent) { if (nodeDifferences(y).equals([2, 2])) { y.demote(); - this.record(); + this.record("Starting deletion rebalance by demoting (2, 2) node"); parent = y.parent; } else if (nodeDifferences(parent).equals([2, 2])) { parent.demote(); - this.record(); + this.record("Starting deletion rebalance by demoting (2, 2) node"); parent = parent.parent; }