feat: Add comments to currently run operations
Fixes #8 Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
23a0653d23
commit
c7c27300f6
7 changed files with 79 additions and 39 deletions
30
avl.js
30
avl.js
|
@ -41,28 +41,32 @@ class AVLTree extends RankedTree {
|
||||||
if (rotatingAroundRoot) {
|
if (rotatingAroundRoot) {
|
||||||
this.root = newRoot;
|
this.root = newRoot;
|
||||||
}
|
}
|
||||||
this.record();
|
this.record("Fixing 0-child by single rotation: by z");
|
||||||
|
|
||||||
z.demote();
|
z.demote();
|
||||||
this.record();
|
this.record("Fixing 0-child by single rotation: demoting z");
|
||||||
} else if (nodeDifference(y) == 1) {
|
} else if (nodeDifference(y) == 1) {
|
||||||
rotateLeft(x);
|
rotateLeft(x);
|
||||||
this.record();
|
this.record(
|
||||||
|
"Fixing 0-child by double-rotation: first rotation by x"
|
||||||
|
);
|
||||||
|
|
||||||
newRoot = rotateRight(z);
|
newRoot = rotateRight(z);
|
||||||
if (rotatingAroundRoot) {
|
if (rotatingAroundRoot) {
|
||||||
this.root = newRoot;
|
this.root = newRoot;
|
||||||
}
|
}
|
||||||
this.record();
|
this.record(
|
||||||
|
"Fixing 0-child by double-rotation: second rotation by z"
|
||||||
|
);
|
||||||
|
|
||||||
y.promote();
|
y.promote();
|
||||||
this.record();
|
this.record("Fixing 0-child by double-rotation: promoting y");
|
||||||
|
|
||||||
x.demote();
|
x.demote();
|
||||||
this.record();
|
this.record("Fixing 0-child by double-rotation: demoting x");
|
||||||
|
|
||||||
z.demote();
|
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.parent.promote();
|
||||||
x = x.parent;
|
x = x.parent;
|
||||||
|
|
||||||
this.record();
|
this.record("Insertion rebalance: promoting (0, 1) parent");
|
||||||
|
|
||||||
diffs = nodeDifferences(x.parent).sort();
|
diffs = nodeDifferences(x.parent).sort();
|
||||||
}
|
}
|
||||||
|
@ -122,7 +126,7 @@ class AVLTree extends RankedTree {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rotateRight(y);
|
rotateRight(y);
|
||||||
this.record();
|
this.record("AVL deletion, fixing by double-rotation: by y");
|
||||||
|
|
||||||
newRoot = rotateLeft(x);
|
newRoot = rotateLeft(x);
|
||||||
break;
|
break;
|
||||||
|
@ -130,13 +134,13 @@ class AVLTree extends RankedTree {
|
||||||
if (rotatingAroundRoot) {
|
if (rotatingAroundRoot) {
|
||||||
this.root = newRoot;
|
this.root = newRoot;
|
||||||
}
|
}
|
||||||
this.record();
|
this.record("AVL deletion, fixing by rotation by x");
|
||||||
|
|
||||||
[newRoot.left, newRoot.right, newRoot]
|
[newRoot.left, newRoot.right, newRoot]
|
||||||
.filter((x) => x)
|
.filter((x) => x)
|
||||||
.forEach((n) => {
|
.forEach((n) => {
|
||||||
updateRank(n);
|
updateRank(n);
|
||||||
this.record();
|
this.record("Updating rank of nodes affected by rotations");
|
||||||
});
|
});
|
||||||
|
|
||||||
return factor == 0;
|
return factor == 0;
|
||||||
|
@ -149,7 +153,9 @@ class AVLTree extends RankedTree {
|
||||||
switch (factor) {
|
switch (factor) {
|
||||||
case 0:
|
case 0:
|
||||||
updateRank(x);
|
updateRank(x);
|
||||||
this.record();
|
this.record(
|
||||||
|
"Updating rank of node affected by the propagation of delete"
|
||||||
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case -1:
|
case -1:
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
let lRecorder = new Recorder(
|
let lRecorder = new Recorder(
|
||||||
d3.select("#left").graphviz(),
|
d3.select("#left").graphviz(),
|
||||||
|
null,
|
||||||
"left"
|
"left"
|
||||||
).renderAtOnce();
|
).renderAtOnce();
|
||||||
let rRecorder = new Recorder(
|
let rRecorder = new Recorder(
|
||||||
d3.select("#right").graphviz(),
|
d3.select("#right").graphviz(),
|
||||||
|
null,
|
||||||
"right"
|
"right"
|
||||||
).renderAtOnce();
|
).renderAtOnce();
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
<input type="submit" value="Delete" />
|
<input type="submit" value="Delete" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<p>Current operation: <span id="comment"></span></p>
|
||||||
<div id="graph" style="text-align: center"></div>
|
<div id="graph" style="text-align: center"></div>
|
||||||
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
|
||||||
|
|
|
@ -68,11 +68,11 @@ class RankedTree {
|
||||||
return this.toDot().join("\n");
|
return this.toDot().join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
record() {
|
record(message) {
|
||||||
if (!this.recorder) {
|
if (!this.recorder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.recorder.record(this);
|
this.recorder.record(this, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
rank() {
|
rank() {
|
||||||
|
@ -92,7 +92,7 @@ class RankedTree {
|
||||||
|
|
||||||
if (!this.root) {
|
if (!this.root) {
|
||||||
this.root = insertedNode;
|
this.root = insertedNode;
|
||||||
this.record();
|
this.record("Inserting key to the root");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class RankedTree {
|
||||||
} else {
|
} else {
|
||||||
parent.right = insertedNode;
|
parent.right = insertedNode;
|
||||||
}
|
}
|
||||||
this.record();
|
this.record("Inserting key");
|
||||||
|
|
||||||
this.insertRebalance(insertedNode);
|
this.insertRebalance(insertedNode);
|
||||||
}
|
}
|
||||||
|
@ -146,12 +146,14 @@ class RankedTree {
|
||||||
|
|
||||||
node.value = successor.value;
|
node.value = successor.value;
|
||||||
successor.value = null;
|
successor.value = null;
|
||||||
this.record();
|
this.record(
|
||||||
|
"Replacing the value of deleted node with successor value"
|
||||||
|
);
|
||||||
|
|
||||||
return this.deleteNode(successor);
|
return this.deleteNode(successor);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.record();
|
this.record("Replacing the node with one of its children");
|
||||||
|
|
||||||
return [y, parent];
|
return [y, parent];
|
||||||
}
|
}
|
||||||
|
|
27
recorder.js
27
recorder.js
|
@ -1,8 +1,23 @@
|
||||||
const DURATION = 500;
|
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 {
|
class Recorder {
|
||||||
constructor(graph, id) {
|
constructor(graph, textBox, id) {
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
|
this.textBox = textBox;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
||||||
this.rendered = -1;
|
this.rendered = -1;
|
||||||
|
@ -20,8 +35,6 @@ class Recorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
unblockRendering() {
|
unblockRendering() {
|
||||||
console.log(this);
|
|
||||||
console.log(`Unblocked renderer #${this.id}`);
|
|
||||||
this.rendering = false;
|
this.rendering = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +53,7 @@ class Recorder {
|
||||||
if (this.atOnce) {
|
if (this.atOnce) {
|
||||||
if (this.rendered != this.states.length - 1) {
|
if (this.rendered != this.states.length - 1) {
|
||||||
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;
|
return;
|
||||||
|
@ -57,7 +70,7 @@ class Recorder {
|
||||||
this.rendering = true;
|
this.rendering = true;
|
||||||
|
|
||||||
this.rendered++;
|
this.rendered++;
|
||||||
this.graph.renderDot(this.states[this.rendered]);
|
this.states[this.rendered].render(this.graph, this.textBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
previous() {
|
previous() {
|
||||||
|
@ -68,9 +81,9 @@ class Recorder {
|
||||||
throw "not implemented!";
|
throw "not implemented!";
|
||||||
}
|
}
|
||||||
|
|
||||||
record(tree) {
|
record(tree, message) {
|
||||||
// TODO: adjust join if necessary
|
// TODO: adjust join if necessary
|
||||||
this.states.push(tree.toDot().join(""));
|
this.states.push(new Record(tree.toDot().join(""), message));
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
let tree = new WAVLTree();
|
||||||
tree.recorder = recorder;
|
tree.recorder = recorder;
|
||||||
|
|
38
wavl.js
38
wavl.js
|
@ -36,36 +36,48 @@ class WAVLTree extends AVLTree {
|
||||||
if (rotatingAroundRoot) {
|
if (rotatingAroundRoot) {
|
||||||
this.root = newRoot;
|
this.root = newRoot;
|
||||||
}
|
}
|
||||||
this.record();
|
this.record(
|
||||||
|
"Final step of deletion rebalance: single rotation by parent of y"
|
||||||
|
);
|
||||||
|
|
||||||
y.promote();
|
y.promote();
|
||||||
this.record();
|
this.record("Final step of deletion rebalance: promotion of y");
|
||||||
|
|
||||||
z.demote();
|
z.demote();
|
||||||
this.record();
|
this.record("Final step of deletion rebalance: demotion of z");
|
||||||
|
|
||||||
if (z.type() == LEAF) {
|
if (z.type() == LEAF) {
|
||||||
z.demote();
|
z.demote();
|
||||||
this.record();
|
this.record(
|
||||||
|
"Final step of deletion rebalance: demotion of leaf z"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if (wDiff == 2 && v && v.parent) {
|
} else if (wDiff == 2 && v && v.parent) {
|
||||||
rotateRight(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);
|
newRoot = rotateLeft(v.parent);
|
||||||
if (rotatingAroundRoot) {
|
if (rotatingAroundRoot) {
|
||||||
this.root = newRoot;
|
this.root = newRoot;
|
||||||
}
|
}
|
||||||
this.record();
|
this.record(
|
||||||
|
"Final step of deletion rebalance: second of double rotation by parent of v"
|
||||||
|
);
|
||||||
|
|
||||||
v.promote().promote();
|
v.promote().promote();
|
||||||
this.record();
|
this.record(
|
||||||
|
"Final step of deletion rebalance: double promotion of v"
|
||||||
|
);
|
||||||
|
|
||||||
y.demote();
|
y.demote();
|
||||||
this.record();
|
this.record("Final step of deletion rebalance: demotion of y");
|
||||||
|
|
||||||
z.demote().demote();
|
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]))
|
(yDiff == 2 || nodeDifferences(y).equals([2, 2]))
|
||||||
) {
|
) {
|
||||||
parent.demote();
|
parent.demote();
|
||||||
this.record();
|
this.record("Propagating error by demoting parent");
|
||||||
|
|
||||||
if (yDiff != 2) {
|
if (yDiff != 2) {
|
||||||
y.demote();
|
y.demote();
|
||||||
this.record();
|
this.record("Demoting y");
|
||||||
}
|
}
|
||||||
|
|
||||||
x = parent;
|
x = parent;
|
||||||
|
@ -139,12 +151,12 @@ class WAVLTree extends AVLTree {
|
||||||
deleteFixup(y, parent) {
|
deleteFixup(y, parent) {
|
||||||
if (nodeDifferences(y).equals([2, 2])) {
|
if (nodeDifferences(y).equals([2, 2])) {
|
||||||
y.demote();
|
y.demote();
|
||||||
this.record();
|
this.record("Starting deletion rebalance by demoting (2, 2) node");
|
||||||
|
|
||||||
parent = y.parent;
|
parent = y.parent;
|
||||||
} else if (nodeDifferences(parent).equals([2, 2])) {
|
} else if (nodeDifferences(parent).equals([2, 2])) {
|
||||||
parent.demote();
|
parent.demote();
|
||||||
this.record();
|
this.record("Starting deletion rebalance by demoting (2, 2) node");
|
||||||
|
|
||||||
parent = parent.parent;
|
parent = parent.parent;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue