From f5949c8347907f7748a7504cbf6b81a9a2d91edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 11:48:40 +0200 Subject: [PATCH 01/21] :construction: progress: First draft without explicit leaves. Everything went as planned except that we needed to mock the child of the deleted node when it is an implicit leaf. The losses: This change puts additional load on: - delete_case{3,4,5}, - insert_case3, and - rotate_{left,right} This load comes from nullchecks before color checks and parent assignments. These checks should only be needed at the lowest levels of the fixed path because of the red-black tree properties. They could perhaps be circumvented entirely by adding additional mocked leaves to the sibling of the child of the deleted node. This should somehow be amortized by the facts that there is at most one deletion per node created and that each node now costs less to create (because of the allowed null pointers, see gains). If property access is always preceded by a nullcheck and if an explicit preceding nullcheck is somehow optimized away by the compiler, then perhaps nothing needs to be done. We have to profile this. The gains: - all Leaf children are replaced by null (>1/6 space save) - all isLeaf() calls are replaced by nullchecks: really faster? Should be since I assume each method call must be preceded by a null check internally? Also we should rewrite delete_one_child to completely avoid this mocking in case the deleted node is red. I left a TODO note about this. This is progress on #104. --- mangle.json | 5 ++--- src/debug/_debug.js | 9 ++------- src/deletion/delete_case2.js | 1 + src/deletion/delete_case3.js | 8 ++++---- src/deletion/delete_case4.js | 8 +++++--- src/deletion/delete_case5.js | 9 ++++++--- src/deletion/delete_mocked_leaf.js | 17 +++++++++++++++++ src/deletion/delete_one_child.js | 11 +++++++++-- src/family/predecessor.js | 2 +- src/family/sibling.js | 4 ++-- src/family/uncle.js | 7 ++----- src/insertion/insert.js | 4 ++-- src/insertion/insert_case2.js | 4 ++-- src/insertion/insert_case3.js | 6 +++--- src/insertion/insert_case4.js | 4 ++-- src/insertion/insert_case5.js | 8 ++++---- src/rotate/rotate_left.js | 6 +++--- src/rotate/rotate_right.js | 6 +++--- src/search/search.js | 7 ++----- src/traversal/inordertraversal.js | 4 ++-- src/traversal/rangetraversal.js | 8 ++++---- src/types/Leaf.js | 12 +----------- src/types/Node.js | 20 ++++---------------- src/types/RedBlackTree.js | 9 ++++----- 24 files changed, 87 insertions(+), 92 deletions(-) create mode 100644 src/deletion/delete_mocked_leaf.js diff --git a/mangle.json b/mangle.json index 2b50a8f..fc409ca 100644 --- a/mangle.json +++ b/mangle.json @@ -2,14 +2,13 @@ "minify": { "mangle": { "properties": { - "regex": "^(_color|isLeaf)$" + "regex": "^_color$" } } }, "props": { "props": { - "$_color": "c", - "$isLeaf": "L" + "$_color": "c" } } } \ No newline at end of file diff --git a/src/debug/_debug.js b/src/debug/_debug.js index 5dcbecf..75873a4 100644 --- a/src/debug/_debug.js +++ b/src/debug/_debug.js @@ -1,6 +1,5 @@ import assert from 'assert'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import BLACK from '../color/BLACK.js'; /** @@ -14,15 +13,11 @@ const _debug = ({red, black}) => { * Recursively constructs a prettyprint string for the red-black tree rooted at * root. * - * @param {Node|Leaf} root - The root of the tree. + * @param {Node} root - The root of the tree. * @returns {string} */ const debug = (root) => { - assert(root instanceof Node || root instanceof Leaf); - if (root.isLeaf()) { - assert(root instanceof Leaf); - return black('L'); - } + if (root === null) return black('L'); assert(root instanceof Node); diff --git a/src/deletion/delete_case2.js b/src/deletion/delete_case2.js index f77604a..6b905de 100644 --- a/src/deletion/delete_case2.js +++ b/src/deletion/delete_case2.js @@ -25,6 +25,7 @@ const delete_case2 = (n) => { assert(n.parent !== null); const s = sibling(n); + assert(s instanceof Node); /** * If n's sibling is red, prepare for and go to case 4. diff --git a/src/deletion/delete_case3.js b/src/deletion/delete_case3.js index c8985ac..eeda5bb 100644 --- a/src/deletion/delete_case3.js +++ b/src/deletion/delete_case3.js @@ -34,15 +34,15 @@ const delete_case3 = (n) => { * B >B * / \ / \ * >B B B R - * / \ / \ --> / \ / \ - * - - B B - - B B + * / \ / \ --> / \ / \ + * - - B B - - B B * / \ / \ / \ / \ * - - - - - - - - */ if ( n.parent._color === BLACK && - s.left._color === BLACK && - s.right._color === BLACK + (s.left === null || s.left._color === BLACK) && + (s.right === null || s.right._color === BLACK) ) { s._color = RED; delete_case1(n.parent); diff --git a/src/deletion/delete_case4.js b/src/deletion/delete_case4.js index 713f57a..120336f 100644 --- a/src/deletion/delete_case4.js +++ b/src/deletion/delete_case4.js @@ -26,7 +26,9 @@ const delete_case4 = (n) => { assert(s instanceof Node); assert(s._color === BLACK); assert( - n.parent._color === RED || s.left._color === RED || s.right._color === RED, + n.parent._color === RED || + s.left?._color === RED || + s.right?._color === RED, ); /** @@ -46,8 +48,8 @@ const delete_case4 = (n) => { if ( // The parent color test is always true when coming from case 2 n.parent._color === RED && - s.left._color === BLACK && - s.right._color === BLACK + (s.left === null || s.left._color === BLACK) && + (s.right === null || s.right._color === BLACK) ) { s._color = RED; n.parent._color = BLACK; diff --git a/src/deletion/delete_case5.js b/src/deletion/delete_case5.js index 1969d86..bd6a633 100644 --- a/src/deletion/delete_case5.js +++ b/src/deletion/delete_case5.js @@ -27,7 +27,7 @@ const delete_case5 = (n) => { const s = sibling(n); assert(s instanceof Node); assert(s._color === BLACK); - assert(s.left._color === RED || s.right._color === RED); + assert(s.left?._color === RED || s.right?._color === RED); // The following statements just force the red n's sibling child to be on // the left of the left of the parent, or right of the right, so case 6 @@ -44,11 +44,14 @@ const delete_case5 = (n) => { * / \ * - - */ - if (n === n.parent.left && s.right._color === BLACK) { + if (n === n.parent.left && (s.right === null || s.right._color === BLACK)) { s._color = RED; s.left._color = BLACK; rotate_right(s); - } else if (n === n.parent.right && s.left._color === BLACK) { + } else if ( + n === n.parent.right && + (s.left === null || s.left._color === BLACK) + ) { /** * ? ? * / \ / \ diff --git a/src/deletion/delete_mocked_leaf.js b/src/deletion/delete_mocked_leaf.js new file mode 100644 index 0000000..ac38fde --- /dev/null +++ b/src/deletion/delete_mocked_leaf.js @@ -0,0 +1,17 @@ +import assert from 'assert'; +import Leaf from '../types/Leaf.js'; + +/** + * Delete leaf L. + * + * @param {Leaf} L - The leaf to delete. + */ +const delete_mocked_leaf = (L) => { + assert(L instanceof Leaf); + assert(L.parent !== null); + + if (L === L.parent.left) L.parent.left = null; + else L.parent.right = null; +}; + +export default delete_mocked_leaf; diff --git a/src/deletion/delete_one_child.js b/src/deletion/delete_one_child.js index 382504e..9a3e34a 100644 --- a/src/deletion/delete_one_child.js +++ b/src/deletion/delete_one_child.js @@ -7,6 +7,8 @@ import Leaf from '../types/Leaf.js'; import replace_node from './replace_node.js'; import delete_case2 from './delete_case2.js'; +import delete_mocked_leaf from './delete_mocked_leaf.js'; + /** * Delete a node n that has at most a single non-leaf child. * @@ -25,9 +27,12 @@ const delete_one_child = (n) => { // The right child of n is always a LEAF because either n is a subtree // predecessor or it is the only child of its parent by the red-black tree // properties - assert(n.right instanceof Leaf); + assert(n.right === null); + + // Mock leaf if there is no left child + const child = n.left === null ? new Leaf(null) : n.left; - const child = n.left; + // TODO skip creating mocked leaf if n._color === RED // Replace n with its left child replace_node(n, child); @@ -48,6 +53,8 @@ const delete_one_child = (n) => { // child suffices. This is a NO-OP. assert(child._color === BLACK); } + + if (child instanceof Leaf) delete_mocked_leaf(child); }; export default delete_one_child; diff --git a/src/family/predecessor.js b/src/family/predecessor.js index 27f8159..b62a3fe 100644 --- a/src/family/predecessor.js +++ b/src/family/predecessor.js @@ -12,7 +12,7 @@ const predecessor = (node) => { assert(node.left instanceof Node); let pred = node.left; - while (!pred.right.isLeaf()) { + while (pred.right !== null) { assert(pred.right instanceof Node); pred = pred.right; } diff --git a/src/family/sibling.js b/src/family/sibling.js index 4616602..5a8632c 100644 --- a/src/family/sibling.js +++ b/src/family/sibling.js @@ -6,11 +6,11 @@ import Leaf from '../types/Leaf.js'; * Computes the sibling of the input node. * * @param {Node|Leaf} node - The input node. - * @returns {Node|Leaf} + * @returns {Node} */ const sibling = (node) => { assert(node instanceof Node || node instanceof Leaf); - // We only use this function when node HAS a sibling. + // We only use this function when node HAS a non-leaf sibling. assert(node.parent !== null); return node === node.parent.left ? node.parent.right : node.parent.left; diff --git a/src/family/uncle.js b/src/family/uncle.js index 4bb1454..9698487 100644 --- a/src/family/uncle.js +++ b/src/family/uncle.js @@ -1,6 +1,5 @@ import assert from 'assert'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import grandparent from './grandparent.js'; /** @@ -8,15 +7,13 @@ import grandparent from './grandparent.js'; * exist. * * @param {Node} node - The input node. - * @returns {Node|Leaf} + * @returns {Node} */ const uncle = (node) => { assert(node instanceof Node); const g = grandparent(node); assert(g !== null); - const u = node.parent === g.left ? g.right : g.left; - assert(u instanceof Node || u instanceof Leaf); - return u; + return node.parent === g.left ? g.right : g.left; }; export default uncle; diff --git a/src/insertion/insert.js b/src/insertion/insert.js index 854f58d..3fc0933 100644 --- a/src/insertion/insert.js +++ b/src/insertion/insert.js @@ -22,7 +22,7 @@ const insert = (compare, A, B) => { if (compare(B.key, A.key) < 0) { const node = A.left; - if (node.isLeaf()) { + if (node === null) { A.left = B; break; } @@ -32,7 +32,7 @@ const insert = (compare, A, B) => { } else { const node = A.right; - if (node.isLeaf()) { + if (node === null) { A.right = B; break; } diff --git a/src/insertion/insert_case2.js b/src/insertion/insert_case2.js index 0f45433..5522976 100644 --- a/src/insertion/insert_case2.js +++ b/src/insertion/insert_case2.js @@ -15,8 +15,8 @@ import insert_case3 from './insert_case3.js'; const insert_case2 = (n) => { assert(n instanceof Node); assert(n._color === RED); - assert(n.left._color === BLACK); - assert(n.right._color === BLACK); + assert(n.left === null || n.left._color === BLACK); + assert(n.right === null || n.right._color === BLACK); assert(n.parent !== null); /** diff --git a/src/insertion/insert_case3.js b/src/insertion/insert_case3.js index 5d9b8bc..189a851 100644 --- a/src/insertion/insert_case3.js +++ b/src/insertion/insert_case3.js @@ -19,8 +19,8 @@ import insert_case4 from './insert_case4.js'; const insert_case3 = (n) => { assert(n instanceof Node); assert(n._color === RED); - assert(n.left._color === BLACK); - assert(n.right._color === BLACK); + assert(n.left === null || n.left._color === BLACK); + assert(n.right === null || n.right._color === BLACK); assert(n.parent !== null); assert(n.parent._color === RED); const u = uncle(n); @@ -39,7 +39,7 @@ const insert_case3 = (n) => { * - - - - */ - if (u._color === RED) { + if (u !== null && u._color === RED) { n.parent._color = BLACK; u._color = BLACK; const g = grandparent(n); diff --git a/src/insertion/insert_case4.js b/src/insertion/insert_case4.js index 97505f0..999ef31 100644 --- a/src/insertion/insert_case4.js +++ b/src/insertion/insert_case4.js @@ -22,8 +22,8 @@ import insert_case5 from './insert_case5.js'; const insert_case4 = (n) => { assert(n instanceof Node); assert(n._color === RED); - assert(n.left._color === BLACK); - assert(n.right._color === BLACK); + assert(n.left === null || n.left._color === BLACK); + assert(n.right === null || n.right._color === BLACK); assert(n.parent !== null); assert(n.parent._color === RED); const g = grandparent(n); diff --git a/src/insertion/insert_case5.js b/src/insertion/insert_case5.js index 1aa439d..b4f561d 100644 --- a/src/insertion/insert_case5.js +++ b/src/insertion/insert_case5.js @@ -20,8 +20,8 @@ import grandparent from '../family/grandparent.js'; const insert_case5 = (n) => { assert(n instanceof Node); assert(n._color === RED); - assert(n.left._color === BLACK); - assert(n.right._color === BLACK); + assert(n.left === null || n.left._color === BLACK); + assert(n.right === null || n.right._color === BLACK); assert(n.parent !== null); assert(n.parent._color === RED); const g = grandparent(n); @@ -45,7 +45,7 @@ const insert_case5 = (n) => { */ assert(g.left instanceof Node); assert(n === g.left.left); - assert(g.right._color === BLACK); + assert(g.right === null || g.right._color === BLACK); rotate_right(g); } else { /** @@ -62,7 +62,7 @@ const insert_case5 = (n) => { */ assert(g.right instanceof Node); assert(n === g.right.right); - assert(g.left._color === BLACK); + assert(g.left === null || g.left._color === BLACK); rotate_left(g); } }; diff --git a/src/rotate/rotate_left.js b/src/rotate/rotate_left.js index 51bf7ea..c645022 100644 --- a/src/rotate/rotate_left.js +++ b/src/rotate/rotate_left.js @@ -33,9 +33,9 @@ const rotate_left = (A) => { B.left = a; B.right = b; - a.parent = B; - b.parent = B; - c.parent = A; + if (a !== null) a.parent = B; + if (b !== null) b.parent = B; + if (c !== null) c.parent = A; }; export default rotate_left; diff --git a/src/rotate/rotate_right.js b/src/rotate/rotate_right.js index 9ad8cdb..ff0ef26 100644 --- a/src/rotate/rotate_right.js +++ b/src/rotate/rotate_right.js @@ -33,9 +33,9 @@ const rotate_right = (B) => { A.left = b; A.right = c; - a.parent = B; - b.parent = A; - c.parent = A; + if (a !== null) a.parent = B; + if (b !== null) b.parent = A; + if (c !== null) c.parent = A; }; export default rotate_right; diff --git a/src/search/search.js b/src/search/search.js index a400739..37de5b2 100644 --- a/src/search/search.js +++ b/src/search/search.js @@ -18,14 +18,11 @@ const search = (compare, root, key) => { return root; } - const child = d < 0 ? root.left : root.right; + root = d < 0 ? root.left : root.right; - if (child.isLeaf()) { + if (root === null) { return null; } - - assert(child instanceof Node); - root = child; } }; diff --git a/src/traversal/inordertraversal.js b/src/traversal/inordertraversal.js index 398ab56..a0c3451 100644 --- a/src/traversal/inordertraversal.js +++ b/src/traversal/inordertraversal.js @@ -9,7 +9,7 @@ import Node from '../types/Node.js'; */ export default function* inordertraversal(node) { assert(node instanceof Node); - if (!node.left.isLeaf()) { + if (node.left !== null) { // Yield the nodes on the left recursively. Those nodes are all smaller // than (or equal to) the current node by the binary search tree // properties. @@ -20,7 +20,7 @@ export default function* inordertraversal(node) { // Yield the current node. yield node.key; - if (!node.right.isLeaf()) { + if (node.right !== null) { // Yield the nodes on the right recursively. Those nodes are all larger // than (or equal to) the current node by the binary search tree // properties. diff --git a/src/traversal/rangetraversal.js b/src/traversal/rangetraversal.js index d26307b..c4db2c7 100644 --- a/src/traversal/rangetraversal.js +++ b/src/traversal/rangetraversal.js @@ -15,27 +15,27 @@ export default function* rangetraversal(compare, root, left, right) { if (compare(root.key, left) < 0) { // If the root lies to the left of the interval, we can discard the // entire left subtree. - if (!root.right.isLeaf()) { + if (root.right !== null) { assert(root.right instanceof Node); yield* rangetraversal(compare, root.right, left, right); } } else if (compare(root.key, right) >= 0) { // If the root lies to the right of the interval, we can discard the // entire right subtree. - if (!root.left.isLeaf()) { + if (root.left !== null) { assert(root.left instanceof Node); yield* rangetraversal(compare, root.left, left, right); } } else { // Otherwise just recurse on both subtrees and yield the root in // between. - if (!root.left.isLeaf()) { + if (root.left !== null) { assert(root.left instanceof Node); yield* rangetraversal(compare, root.left, left, right); } yield root.key; - if (!root.right.isLeaf()) { + if (root.right !== null) { assert(root.right instanceof Node); yield* rangetraversal(compare, root.right, left, right); } diff --git a/src/types/Leaf.js b/src/types/Leaf.js index 3e8a0a1..383a167 100644 --- a/src/types/Leaf.js +++ b/src/types/Leaf.js @@ -9,19 +9,9 @@ import Node from './Node.js'; * @param {Node} parent - The parent node in the tree. */ export default function Leaf(parent) { - assert(parent instanceof Node); + assert(parent === null || parent instanceof Node); /** @constant {number} The color of the node. */ this._color = BLACK; /** @member {Node} The parent node. */ this.parent = parent; } - -/** - * Returns true if the Leaf object is a leaf. This - * always returns true. - * - * @returns {boolean} - */ -Leaf.prototype.isLeaf = function () { - return true; -}; diff --git a/src/types/Node.js b/src/types/Node.js index e4bc1aa..c74cd66 100644 --- a/src/types/Node.js +++ b/src/types/Node.js @@ -1,5 +1,3 @@ -import Leaf from './Leaf.js'; - /** * An internal node. This node can be red or black. * @@ -10,22 +8,12 @@ import Leaf from './Leaf.js'; export default function Node(color, key) { /** @member {number} The color of the node. */ this._color = color; - /** @member {Node|Leaf} The left child */ - this.left = new Leaf(this); - /** @member {Node|Leaf} The right child */ - this.right = new Leaf(this); + /** @member {Node} The left child */ + this.left = null; + /** @member {Node} The right child */ + this.right = null; /** @member {Node} The parent node. */ this.parent = null; /** @member {any} The key held by this node. */ this.key = key; } - -/** - * Returns true if the Node object is a leaf. This - * always returns false. - * - * @returns {boolean} - */ -Node.prototype.isLeaf = function () { - return false; -}; diff --git a/src/types/RedBlackTree.js b/src/types/RedBlackTree.js index e0ec5b0..919b838 100644 --- a/src/types/RedBlackTree.js +++ b/src/types/RedBlackTree.js @@ -1,6 +1,5 @@ import assert from 'assert'; import Node from './Node.js'; -import Leaf from './Leaf.js'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import predecessor from '../family/predecessor.js'; @@ -95,7 +94,7 @@ export default class RedBlackTree { * @param {Node} node - The input node to delete. */ _delete(node) { - if (!node.left.isLeaf()) { + if (node.left !== null) { // Replace node's key with predecessor's key const pred = predecessor(node); node.key = pred.key; @@ -103,14 +102,14 @@ export default class RedBlackTree { // note: this node can only have one non-leaf child // because the tree is a red-black tree delete_one_child(pred); - } else if (!node.right.isLeaf()) { + } else if (node.right !== null) { // Replace node's key with successor's key // If there is no left child, then there can only be one right // child. const succ = node.right; assert(succ instanceof Node); - assert(succ.left instanceof Leaf); - assert(succ.right instanceof Leaf); + assert(succ.left === null); + assert(succ.right === null); node.key = succ.key; // Delete successor node // note: this node can only have one non-leaf child From 6d8bd392a3d53168e21a2719daa3db1bdc07c3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 14:42:55 +0200 Subject: [PATCH 02/21] :bicyclist: perf(delete_one_child): Hoist red input case. This avoids creating a mocked leaf in that case. --- src/deletion/delete_mocked_leaf.js | 17 ------------ src/deletion/delete_one_child.js | 43 ++++++++++++++++++------------ src/deletion/prune_subtree.js | 18 +++++++++++++ 3 files changed, 44 insertions(+), 34 deletions(-) delete mode 100644 src/deletion/delete_mocked_leaf.js create mode 100644 src/deletion/prune_subtree.js diff --git a/src/deletion/delete_mocked_leaf.js b/src/deletion/delete_mocked_leaf.js deleted file mode 100644 index ac38fde..0000000 --- a/src/deletion/delete_mocked_leaf.js +++ /dev/null @@ -1,17 +0,0 @@ -import assert from 'assert'; -import Leaf from '../types/Leaf.js'; - -/** - * Delete leaf L. - * - * @param {Leaf} L - The leaf to delete. - */ -const delete_mocked_leaf = (L) => { - assert(L instanceof Leaf); - assert(L.parent !== null); - - if (L === L.parent.left) L.parent.left = null; - else L.parent.right = null; -}; - -export default delete_mocked_leaf; diff --git a/src/deletion/delete_one_child.js b/src/deletion/delete_one_child.js index 9a3e34a..351984d 100644 --- a/src/deletion/delete_one_child.js +++ b/src/deletion/delete_one_child.js @@ -7,7 +7,7 @@ import Leaf from '../types/Leaf.js'; import replace_node from './replace_node.js'; import delete_case2 from './delete_case2.js'; -import delete_mocked_leaf from './delete_mocked_leaf.js'; +import prune_subtree from './prune_subtree.js'; /** * Delete a node n that has at most a single non-leaf child. @@ -29,32 +29,41 @@ const delete_one_child = (n) => { // properties assert(n.right === null); + if (n._color === RED) { + // If n is red then its child can only be black. Replacing n with its + // child suffices. + const child = n.left; + if (child === null) { + prune_subtree(n); + } else { + assert(child._color === BLACK); + replace_node(n, child); + } + + return; + } + + assert(n._color === BLACK); + // Mock leaf if there is no left child const child = n.left === null ? new Leaf(null) : n.left; - // TODO skip creating mocked leaf if n._color === RED - // Replace n with its left child replace_node(n, child); // If n is black, deleting it reduces the black-height of every path going // through it by 1. - if (n._color === BLACK) { - // We can easily fix this when its left child is an - // internal red node: change the color of the left child to black and - // replace n with it. - if (child._color === RED) child._color = BLACK; - // Otherwise, there are more things to fix. - else { - delete_case2(child); - } - } else { - // If n is red then its child can only be black. Replacing n with its - // child suffices. This is a NO-OP. - assert(child._color === BLACK); + // We can easily fix this when its left child is an + // internal red node: change the color of the left child to black and + // replace n with it. + if (child._color === RED) child._color = BLACK; + // Otherwise, there are more things to fix. + else { + delete_case2(child); } - if (child instanceof Leaf) delete_mocked_leaf(child); + // Delete mocked leaf + if (child instanceof Leaf) prune_subtree(child); }; export default delete_one_child; diff --git a/src/deletion/prune_subtree.js b/src/deletion/prune_subtree.js new file mode 100644 index 0000000..517eaa0 --- /dev/null +++ b/src/deletion/prune_subtree.js @@ -0,0 +1,18 @@ +import assert from 'assert'; +import Node from '../types/Node.js'; +import Leaf from '../types/Leaf.js'; + +/** + * Prune subtree rooted at input node. + * + * @param {Node|Leaf} root - The leaf to delete. + */ +const prune_subtree = (root) => { + assert(root instanceof Node || root instanceof Leaf); + assert(root.parent !== null); + + if (root === root.parent.left) root.parent.left = null; + else root.parent.right = null; +}; + +export default prune_subtree; From 9d8349e5a71ce86a87134301c013507656035dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 16:02:19 +0200 Subject: [PATCH 03/21] :wrench: config(cover): Fix coverage report generation. --- package.json | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index fd879b4..935a824 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "build-gh-pages": "npm run build-docs", "ci:test": "npm run lint-config && npm run lint && npm run cover", "commit-msg": "commitlint --edit", - "cover": "c8 --all --src src --reporter=lcov npm test", + "cover": "NODE_ENV=cover c8 --all --src src --reporter=lcov npm test", "debug": "NODE_ENV=debug npm run test -- -st --fail-fast", "dev": "npm run lint-config-and-fix && npm run lint-and-fix && npm run cover -- -- -st --fail-fast", "install-hooks": "husky install", @@ -146,6 +146,29 @@ ] ] }, + "cover": { + "sourceMaps": "inline", + "presets": [ + [ + "@babel/preset-env", + { + "targets": "current node" + } + ] + ], + "plugins": [ + [ + "transform-remove-console", + { + "exclude": [ + "log", + "error", + "warn" + ] + } + ] + ] + }, "development": { "presets": [ "babel-preset-power-assert" From eaa7d870bb4c5a67b6127ceadf0f1afbe629dd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 16:25:21 +0200 Subject: [PATCH 04/21] :recycle: refactor(delete): Split "no-child" and "exactly-one-child" cases. The coverage report seems to indicate that some additional pruning is possible in delete_one_child. Need to investigate. --- src/deletion/delete_no_child.js | 48 ++++++++++++++++++++++++ src/deletion/delete_one_child.js | 64 ++++++++++++-------------------- src/deletion/replace_node.js | 2 +- src/types/RedBlackTree.js | 26 +++++++------ 4 files changed, 86 insertions(+), 54 deletions(-) create mode 100644 src/deletion/delete_no_child.js diff --git a/src/deletion/delete_no_child.js b/src/deletion/delete_no_child.js new file mode 100644 index 0000000..41d5958 --- /dev/null +++ b/src/deletion/delete_no_child.js @@ -0,0 +1,48 @@ +import assert from 'assert'; +import BLACK from '../color/BLACK.js'; +import RED from '../color/RED.js'; +import Node from '../types/Node.js'; +import Leaf from '../types/Leaf.js'; + +import replace_node from './replace_node.js'; +import delete_case2 from './delete_case2.js'; + +import prune_subtree from './prune_subtree.js'; + +/** + * Delete a node n that has no non-leaf child. + * + * Precondition: + * - n has no non-leaf child. + * - n is not the root + * + * @param {Node} n - The node to delete. + */ +const delete_no_child = (n) => { + assert(n instanceof Node); + assert(n.parent !== null); + assert(n.left === null); + assert(n.right === null); + + if (n._color === RED) { + prune_subtree(n); + return; + } + + assert(n._color === BLACK); + + // Mock leaf since there is no left child + const leaf = new Leaf(null); + + // Replace n with the mocked leaf + replace_node(n, leaf); + + // If n is black, deleting it reduces the black-height of every path going + // through it by 1. Leaf is black, so there are more things to fix. + delete_case2(leaf); + + // Delete mocked leaf + prune_subtree(leaf); +}; + +export default delete_no_child; diff --git a/src/deletion/delete_one_child.js b/src/deletion/delete_one_child.js index 351984d..5489712 100644 --- a/src/deletion/delete_one_child.js +++ b/src/deletion/delete_one_child.js @@ -2,20 +2,18 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import replace_node from './replace_node.js'; import delete_case2 from './delete_case2.js'; -import prune_subtree from './prune_subtree.js'; - /** - * Delete a node n that has at most a single non-leaf child. + * Delete a node n with one non-leaf left child and one leaf right + * child. * * Precondition: - * - n has at most one non-leaf child. + * - n has exactly one non-leaf child. * - n is not the root - * - if n has a non-leaf child, then it is its left child. + * - n's only non-leaf child is n's left child. * - hence, n's right child is a leaf * * @param {Node} n - The node to delete. @@ -23,47 +21,31 @@ import prune_subtree from './prune_subtree.js'; const delete_one_child = (n) => { assert(n instanceof Node); assert(n.parent !== null); - // Precondition: n's right child is a leaf. - // The right child of n is always a LEAF because either n is a subtree - // predecessor or it is the only child of its parent by the red-black tree - // properties + assert(n.left instanceof Node); assert(n.right === null); - if (n._color === RED) { - // If n is red then its child can only be black. Replacing n with its - // child suffices. - const child = n.left; - if (child === null) { - prune_subtree(n); - } else { - assert(child._color === BLACK); - replace_node(n, child); - } - - return; - } - - assert(n._color === BLACK); - - // Mock leaf if there is no left child - const child = n.left === null ? new Leaf(null) : n.left; + const child = n.left; - // Replace n with its left child + // Replace n with its only child replace_node(n, child); - // If n is black, deleting it reduces the black-height of every path going - // through it by 1. - // We can easily fix this when its left child is an - // internal red node: change the color of the left child to black and - // replace n with it. - if (child._color === RED) child._color = BLACK; - // Otherwise, there are more things to fix. - else { - delete_case2(child); + if (n._color === BLACK) { + // If n is black, deleting it reduces the black-height of every path going + // through it by 1. + // We can easily fix this when its only child is an + // internal red node: change the color of the child to black and + // replace n with it. + if (child._color === RED) child._color = BLACK; + // Otherwise, there are more things to fix. + else { + delete_case2(child); + } + } else { + assert(n._color === RED); + // If n is red then its child can only be black. Replacing n with its + // child suffices. This has already been done above. + assert(child._color === BLACK); } - - // Delete mocked leaf - if (child instanceof Leaf) prune_subtree(child); }; export default delete_one_child; diff --git a/src/deletion/replace_node.js b/src/deletion/replace_node.js index ae4c302..2ef46a0 100644 --- a/src/deletion/replace_node.js +++ b/src/deletion/replace_node.js @@ -11,7 +11,7 @@ import Leaf from '../types/Leaf.js'; const replace_node = (A, B) => { assert(A instanceof Node); assert(B instanceof Node || B instanceof Leaf); - // We never apply delete_one_child on the root + // We never apply delete_one_child or delete_no_child on the root assert(A.parent !== null); if (A === A.parent.left) A.parent.left = B; diff --git a/src/types/RedBlackTree.js b/src/types/RedBlackTree.js index 919b838..10b3ff9 100644 --- a/src/types/RedBlackTree.js +++ b/src/types/RedBlackTree.js @@ -6,6 +6,7 @@ import predecessor from '../family/predecessor.js'; import insert from '../insertion/insert.js'; import insert_case2 from '../insertion/insert_case2.js'; import delete_one_child from '../deletion/delete_one_child.js'; +import delete_no_child from '../deletion/delete_no_child.js'; import search from '../search/search.js'; import inordertraversal from '../traversal/inordertraversal.js'; import rangetraversal from '../traversal/rangetraversal.js'; @@ -21,6 +22,7 @@ export default class RedBlackTree { * @param {Function} compare - The comparison function for node keys. */ constructor(compare) { + assert(compare instanceof Function); /** @member {Function} The comparison function for node keys. */ this.compare = compare; /** @member {Node} The root of the tree. */ @@ -94,31 +96,31 @@ export default class RedBlackTree { * @param {Node} node - The input node to delete. */ _delete(node) { + assert(node instanceof Node); if (node.left !== null) { // Replace node's key with predecessor's key const pred = predecessor(node); node.key = pred.key; // Delete predecessor node - // note: this node can only have one non-leaf child - // because the tree is a red-black tree - delete_one_child(pred); + // NOTE: this node can only have one non-leaf (left) child because + // of red-black tree invariant. + if (pred.left === null) { + delete_no_child(pred); + } else { + delete_one_child(pred); + } } else if (node.right !== null) { // Replace node's key with successor's key - // If there is no left child, then there can only be one right - // child. + // NOTE: Since there is no left child, then there can only be one + // right child by the red-black tree invariant. const succ = node.right; - assert(succ instanceof Node); - assert(succ.left === null); - assert(succ.right === null); node.key = succ.key; // Delete successor node - // note: this node can only have one non-leaf child - // because the tree is a red-black tree - delete_one_child(succ); + delete_no_child(succ); } else if (node === this.root) { this.root = null; } else { - delete_one_child(node); + delete_no_child(node); } } From 7ccb61f31f1698013258d11b81b0b5f5018bf176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 16:40:03 +0200 Subject: [PATCH 05/21] :recycle: refactor(delete_one_child): Remove impossible execution paths. Indeed the previous coverage report gave the right insight. --- src/deletion/delete_one_child.js | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/deletion/delete_one_child.js b/src/deletion/delete_one_child.js index 5489712..5ab00f6 100644 --- a/src/deletion/delete_one_child.js +++ b/src/deletion/delete_one_child.js @@ -4,7 +4,6 @@ import RED from '../color/RED.js'; import Node from '../types/Node.js'; import replace_node from './replace_node.js'; -import delete_case2 from './delete_case2.js'; /** * Delete a node n with one non-leaf left child and one leaf right @@ -15,37 +14,27 @@ import delete_case2 from './delete_case2.js'; * - n is not the root * - n's only non-leaf child is n's left child. * - hence, n's right child is a leaf + * - hence, n's left child is RED + * - hence, n is BLACK * * @param {Node} n - The node to delete. */ const delete_one_child = (n) => { assert(n instanceof Node); + assert(n._color === BLACK); assert(n.parent !== null); assert(n.left instanceof Node); + assert(n.left._color === RED); assert(n.right === null); const child = n.left; - - // Replace n with its only child + // If n is black, deleting it reduces the black-height of every path going + // through it by 1. + // We can easily fix this when its only child is an + // internal RED node: change the color of the child to black and + // replace n with it. replace_node(n, child); - - if (n._color === BLACK) { - // If n is black, deleting it reduces the black-height of every path going - // through it by 1. - // We can easily fix this when its only child is an - // internal red node: change the color of the child to black and - // replace n with it. - if (child._color === RED) child._color = BLACK; - // Otherwise, there are more things to fix. - else { - delete_case2(child); - } - } else { - assert(n._color === RED); - // If n is red then its child can only be black. Replacing n with its - // child suffices. This has already been done above. - assert(child._color === BLACK); - } + child._color = BLACK; }; export default delete_one_child; From 25178631a5c0021d9ed65d260b6f9f36a7c7aa75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 16:42:54 +0200 Subject: [PATCH 06/21] :truck: refactor: Rename prune_subtree to prune. --- src/deletion/delete_no_child.js | 6 +++--- src/deletion/{prune_subtree.js => prune.js} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/deletion/{prune_subtree.js => prune.js} (85%) diff --git a/src/deletion/delete_no_child.js b/src/deletion/delete_no_child.js index 41d5958..f7b644d 100644 --- a/src/deletion/delete_no_child.js +++ b/src/deletion/delete_no_child.js @@ -7,7 +7,7 @@ import Leaf from '../types/Leaf.js'; import replace_node from './replace_node.js'; import delete_case2 from './delete_case2.js'; -import prune_subtree from './prune_subtree.js'; +import prune from './prune.js'; /** * Delete a node n that has no non-leaf child. @@ -25,7 +25,7 @@ const delete_no_child = (n) => { assert(n.right === null); if (n._color === RED) { - prune_subtree(n); + prune(n); return; } @@ -42,7 +42,7 @@ const delete_no_child = (n) => { delete_case2(leaf); // Delete mocked leaf - prune_subtree(leaf); + prune(leaf); }; export default delete_no_child; diff --git a/src/deletion/prune_subtree.js b/src/deletion/prune.js similarity index 85% rename from src/deletion/prune_subtree.js rename to src/deletion/prune.js index 517eaa0..68dc729 100644 --- a/src/deletion/prune_subtree.js +++ b/src/deletion/prune.js @@ -7,7 +7,7 @@ import Leaf from '../types/Leaf.js'; * * @param {Node|Leaf} root - The leaf to delete. */ -const prune_subtree = (root) => { +const prune = (root) => { assert(root instanceof Node || root instanceof Leaf); assert(root.parent !== null); @@ -15,4 +15,4 @@ const prune_subtree = (root) => { else root.parent.right = null; }; -export default prune_subtree; +export default prune; From e6574c209840bebc0e9024b9f531106a7bf0f160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 16:48:12 +0200 Subject: [PATCH 07/21] :sparkles: feat: Export delete_no_child and prune. --- src/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.js b/src/index.js index 0c764a6..e6f911c 100644 --- a/src/index.js +++ b/src/index.js @@ -9,7 +9,9 @@ export {default as delete_case3} from './deletion/delete_case3.js'; export {default as delete_case4} from './deletion/delete_case4.js'; export {default as delete_case5} from './deletion/delete_case5.js'; export {default as delete_case6} from './deletion/delete_case6.js'; +export {default as delete_no_child} from './deletion/delete_no_child.js'; export {default as delete_one_child} from './deletion/delete_one_child.js'; +export {default as prune} from './deletion/prune.js'; export {default as replace_node} from './deletion/replace_node.js'; export {default as grandparent} from './family/grandparent.js'; export {default as predecessor} from './family/predecessor.js'; From 64aaedcbe28712d58b2b065317fa9211ebfcce59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 16:52:15 +0200 Subject: [PATCH 08/21] :recycle: refactor(delete_case1): Do not allow Leaf input. --- src/deletion/delete_case1.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/deletion/delete_case1.js b/src/deletion/delete_case1.js index f1fbffa..06b9905 100644 --- a/src/deletion/delete_case1.js +++ b/src/deletion/delete_case1.js @@ -1,7 +1,6 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import delete_case2 from './delete_case2.js'; /** @@ -10,10 +9,10 @@ import delete_case2 from './delete_case2.js'; * - all root-leaf paths going through n have a black height of b - 1 * - all other root-leaf paths have a black height of b * - * @param {Node|Leaf} n - The input node. + * @param {Node} n - The input node. */ const delete_case1 = (n) => { - assert(n instanceof Node || n instanceof Leaf); + assert(n instanceof Node); assert(n._color === BLACK); // If n is the root, there is nothing to do: // - all paths go through n, and From 56a8aae67b4696de8a15b03aed7f42c377fb42cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:06:04 +0200 Subject: [PATCH 09/21] :recycle: refactor: Remove usage of class Leaf. This avoids using multiple Hidden Classes and enables inline caching. Fixes #107. --- src/deletion/delete_case2.js | 5 ++--- src/deletion/delete_case3.js | 5 ++--- src/deletion/delete_case4.js | 5 ++--- src/deletion/delete_case5.js | 5 ++--- src/deletion/delete_case6.js | 5 ++--- src/deletion/delete_no_child.js | 7 ++++--- src/deletion/prune.js | 5 ++--- src/deletion/replace_node.js | 5 ++--- src/family/sibling.js | 5 ++--- src/index.js | 1 - src/types/Leaf.js | 17 ----------------- 11 files changed, 20 insertions(+), 45 deletions(-) delete mode 100644 src/types/Leaf.js diff --git a/src/deletion/delete_case2.js b/src/deletion/delete_case2.js index 6b905de..943273e 100644 --- a/src/deletion/delete_case2.js +++ b/src/deletion/delete_case2.js @@ -2,7 +2,6 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import rotate_left from '../rotate/rotate_left.js'; import rotate_right from '../rotate/rotate_right.js'; import sibling from '../family/sibling.js'; @@ -17,10 +16,10 @@ import delete_case4 from './delete_case4.js'; * - all other root-leaf paths have a black height of b * - n is not the root * - * @param {Node|Leaf} n - The input node. + * @param {Node} n - The input node. */ const delete_case2 = (n) => { - assert(n instanceof Node || n instanceof Leaf); + assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); diff --git a/src/deletion/delete_case3.js b/src/deletion/delete_case3.js index eeda5bb..a627d38 100644 --- a/src/deletion/delete_case3.js +++ b/src/deletion/delete_case3.js @@ -2,7 +2,6 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import sibling from '../family/sibling.js'; import delete_case1 from './delete_case1.js'; @@ -16,10 +15,10 @@ import delete_case4 from './delete_case4.js'; * - n is not the root * - n's sibling is black * - * @param {Node|Leaf} n - The input node. + * @param {Node} n - The input node. */ const delete_case3 = (n) => { - assert(n instanceof Node || n instanceof Leaf); + assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); const s = sibling(n); diff --git a/src/deletion/delete_case4.js b/src/deletion/delete_case4.js index 120336f..ede4957 100644 --- a/src/deletion/delete_case4.js +++ b/src/deletion/delete_case4.js @@ -2,7 +2,6 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import sibling from '../family/sibling.js'; import delete_case5 from './delete_case5.js'; @@ -16,10 +15,10 @@ import delete_case5 from './delete_case5.js'; * - n's sibling is black * - n's parent and n's sibling's children cannot all be black * - * @param {Node|Leaf} n - The input node. + * @param {Node} n - The input node. */ const delete_case4 = (n) => { - assert(n instanceof Node || n instanceof Leaf); + assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); const s = sibling(n); diff --git a/src/deletion/delete_case5.js b/src/deletion/delete_case5.js index bd6a633..9f0ea95 100644 --- a/src/deletion/delete_case5.js +++ b/src/deletion/delete_case5.js @@ -2,7 +2,6 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import rotate_left from '../rotate/rotate_left.js'; import rotate_right from '../rotate/rotate_right.js'; import sibling from '../family/sibling.js'; @@ -18,10 +17,10 @@ import delete_case6 from './delete_case6.js'; * - n's sibling is black * - at least one of n's sibling's children is red * - * @param {Node|Leaf} n - The input node. + * @param {Node} n - The input node. */ const delete_case5 = (n) => { - assert(n instanceof Node || n instanceof Leaf); + assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); const s = sibling(n); diff --git a/src/deletion/delete_case6.js b/src/deletion/delete_case6.js index 9f5f2c4..8ec3830 100644 --- a/src/deletion/delete_case6.js +++ b/src/deletion/delete_case6.js @@ -2,7 +2,6 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import rotate_left from '../rotate/rotate_left.js'; import rotate_right from '../rotate/rotate_right.js'; import sibling from '../family/sibling.js'; @@ -17,10 +16,10 @@ import sibling from '../family/sibling.js'; * - if n is a left child, the right child of n's sibling is red * - if n is a right child, the left child of n's sibling is red * - * @param {Node|Leaf} n - The input node. + * @param {Node} n - The input node. */ const delete_case6 = (n) => { - assert(n instanceof Node || n instanceof Leaf); + assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); const s = sibling(n); diff --git a/src/deletion/delete_no_child.js b/src/deletion/delete_no_child.js index f7b644d..1b8e54d 100644 --- a/src/deletion/delete_no_child.js +++ b/src/deletion/delete_no_child.js @@ -2,7 +2,6 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; import replace_node from './replace_node.js'; import delete_case2 from './delete_case2.js'; @@ -32,13 +31,15 @@ const delete_no_child = (n) => { assert(n._color === BLACK); // Mock leaf since there is no left child - const leaf = new Leaf(null); + // We use key = n.key to avoid mixing types, but this property is never + // accessed. + const leaf = new Node(BLACK, n.key); // Replace n with the mocked leaf replace_node(n, leaf); // If n is black, deleting it reduces the black-height of every path going - // through it by 1. Leaf is black, so there are more things to fix. + // through it by 1. The leaf is black, so there are more things to fix. delete_case2(leaf); // Delete mocked leaf diff --git a/src/deletion/prune.js b/src/deletion/prune.js index 68dc729..74853ad 100644 --- a/src/deletion/prune.js +++ b/src/deletion/prune.js @@ -1,14 +1,13 @@ import assert from 'assert'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; /** * Prune subtree rooted at input node. * - * @param {Node|Leaf} root - The leaf to delete. + * @param {Node} root - The root of the subtree to prune. */ const prune = (root) => { - assert(root instanceof Node || root instanceof Leaf); + assert(root instanceof Node); assert(root.parent !== null); if (root === root.parent.left) root.parent.left = null; diff --git a/src/deletion/replace_node.js b/src/deletion/replace_node.js index 2ef46a0..1f480f6 100644 --- a/src/deletion/replace_node.js +++ b/src/deletion/replace_node.js @@ -1,16 +1,15 @@ import assert from 'assert'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; /** * Replaces node A by node B. * * @param {Node} A - The node to replace. - * @param {Node|Leaf} B - The replacement node. + * @param {Node} B - The replacement node. */ const replace_node = (A, B) => { assert(A instanceof Node); - assert(B instanceof Node || B instanceof Leaf); + assert(B instanceof Node); // We never apply delete_one_child or delete_no_child on the root assert(A.parent !== null); diff --git a/src/family/sibling.js b/src/family/sibling.js index 5a8632c..7d40268 100644 --- a/src/family/sibling.js +++ b/src/family/sibling.js @@ -1,15 +1,14 @@ import assert from 'assert'; import Node from '../types/Node.js'; -import Leaf from '../types/Leaf.js'; /** * Computes the sibling of the input node. * - * @param {Node|Leaf} node - The input node. + * @param {Node} node - The input node. * @returns {Node} */ const sibling = (node) => { - assert(node instanceof Node || node instanceof Leaf); + assert(node instanceof Node); // We only use this function when node HAS a non-leaf sibling. assert(node.parent !== null); diff --git a/src/index.js b/src/index.js index e6f911c..3e439f0 100644 --- a/src/index.js +++ b/src/index.js @@ -28,6 +28,5 @@ export {default as rotate_right} from './rotate/rotate_right.js'; export {default as search} from './search/search.js'; export {default as inordertraversal} from './traversal/inordertraversal.js'; export {default as rangetraversal} from './traversal/rangetraversal.js'; -export {default as Leaf} from './types/Leaf.js'; export {default as Node} from './types/Node.js'; export {default as RedBlackTree} from './types/RedBlackTree.js'; diff --git a/src/types/Leaf.js b/src/types/Leaf.js deleted file mode 100644 index 383a167..0000000 --- a/src/types/Leaf.js +++ /dev/null @@ -1,17 +0,0 @@ -import assert from 'assert'; -import BLACK from '../color/BLACK.js'; -import Node from './Node.js'; - -/** - * A black leaf node. - * - * @class - * @param {Node} parent - The parent node in the tree. - */ -export default function Leaf(parent) { - assert(parent === null || parent instanceof Node); - /** @constant {number} The color of the node. */ - this._color = BLACK; - /** @member {Node} The parent node. */ - this.parent = parent; -} From ccf1b454452bf1bdfc2f151f3d9709b69cad9158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:18:22 +0200 Subject: [PATCH 10/21] :wrench: config(babel): Make current node default. This makes the config easier to parse. --- package.json | 87 ++++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 935a824..d88c7fb 100644 --- a/package.json +++ b/package.json @@ -104,9 +104,18 @@ [ "@babel/preset-env", { - "targets": [ - "defaults", - "maintained node versions" + "targets": "current node" + } + ] + ], + "plugins": [ + [ + "transform-remove-console", + { + "exclude": [ + "log", + "error", + "warn" ] } ] @@ -114,23 +123,6 @@ "env": { "debug": { "presets": [ - [ - "@babel/preset-env", - { - "targets": "current node" - } - ], - "babel-preset-power-assert" - ] - }, - "test": { - "presets": [ - [ - "@babel/preset-env", - { - "targets": "current node" - } - ], "babel-preset-power-assert" ], "plugins": [ @@ -138,6 +130,7 @@ "transform-remove-console", { "exclude": [ + "debug", "log", "error", "warn" @@ -146,59 +139,45 @@ ] ] }, + "test": { + "presets": [ + "babel-preset-power-assert" + ] + }, "cover": { "sourceMaps": "inline", "presets": [ - [ - "@babel/preset-env", - { - "targets": "current node" - } - ] - ], - "plugins": [ - [ - "transform-remove-console", - { - "exclude": [ - "log", - "error", - "warn" - ] - } - ] + "babel-preset-power-assert" ] }, "development": { "presets": [ - "babel-preset-power-assert" - ], - "plugins": [ [ - "transform-remove-console", + "@babel/preset-env", { - "exclude": [ - "log", - "error", - "warn" + "targets": [ + "defaults", + "maintained node versions" ] } - ] + ], + "babel-preset-power-assert" ] }, "production": { - "plugins": [ - "babel-plugin-unassert", + "presets": [ [ - "transform-remove-console", + "@babel/preset-env", { - "exclude": [ - "log", - "error", - "warn" + "targets": [ + "defaults", + "maintained node versions" ] } ] + ], + "plugins": [ + "babel-plugin-unassert" ] } } From 7ae67da4cc0d9a7e12cc346b36d066d0378143f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:47:28 +0200 Subject: [PATCH 11/21] :truck: refactor: Rename delete_case1 to delete_case0. The goal is to have case number zero for the base case of the recursion. I had to do each of these renaming operations individually as otherwise git would register a consequent amount of churn. --- src/deletion/{delete_case1.js => delete_case0.js} | 4 ++-- src/deletion/delete_case3.js | 4 ++-- src/index.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/deletion/{delete_case1.js => delete_case0.js} (90%) diff --git a/src/deletion/delete_case1.js b/src/deletion/delete_case0.js similarity index 90% rename from src/deletion/delete_case1.js rename to src/deletion/delete_case0.js index 06b9905..bcdc33d 100644 --- a/src/deletion/delete_case1.js +++ b/src/deletion/delete_case0.js @@ -11,7 +11,7 @@ import delete_case2 from './delete_case2.js'; * * @param {Node} n - The input node. */ -const delete_case1 = (n) => { +const delete_case0 = (n) => { assert(n instanceof Node); assert(n._color === BLACK); // If n is the root, there is nothing to do: @@ -20,4 +20,4 @@ const delete_case1 = (n) => { if (n.parent !== null) delete_case2(n); }; -export default delete_case1; +export default delete_case0; diff --git a/src/deletion/delete_case3.js b/src/deletion/delete_case3.js index a627d38..8caec23 100644 --- a/src/deletion/delete_case3.js +++ b/src/deletion/delete_case3.js @@ -4,7 +4,7 @@ import RED from '../color/RED.js'; import Node from '../types/Node.js'; import sibling from '../family/sibling.js'; -import delete_case1 from './delete_case1.js'; +import delete_case0 from './delete_case0.js'; import delete_case4 from './delete_case4.js'; /** @@ -44,7 +44,7 @@ const delete_case3 = (n) => { (s.right === null || s.right._color === BLACK) ) { s._color = RED; - delete_case1(n.parent); + delete_case0(n.parent); } // Otherwise, go to case 4. diff --git a/src/index.js b/src/index.js index 3e439f0..992a102 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ export {default as from} from './api/from.js'; export {default as BLACK} from './color/BLACK.js'; export {default as RED} from './color/RED.js'; export {default as _debug} from './debug/_debug.js'; -export {default as delete_case1} from './deletion/delete_case1.js'; +export {default as delete_case0} from './deletion/delete_case0.js'; export {default as delete_case2} from './deletion/delete_case2.js'; export {default as delete_case3} from './deletion/delete_case3.js'; export {default as delete_case4} from './deletion/delete_case4.js'; From 9652fb124619f8656049d80b73336bc07a025cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:56:50 +0200 Subject: [PATCH 12/21] :truck: refactor: Rename delete_case2 to delete_case1. --- src/deletion/delete_case0.js | 4 ++-- src/deletion/{delete_case2.js => delete_case1.js} | 4 ++-- src/deletion/delete_no_child.js | 4 ++-- src/index.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/deletion/{delete_case2.js => delete_case1.js} (95%) diff --git a/src/deletion/delete_case0.js b/src/deletion/delete_case0.js index bcdc33d..22e1ddb 100644 --- a/src/deletion/delete_case0.js +++ b/src/deletion/delete_case0.js @@ -1,7 +1,7 @@ import assert from 'assert'; import BLACK from '../color/BLACK.js'; import Node from '../types/Node.js'; -import delete_case2 from './delete_case2.js'; +import delete_case1 from './delete_case1.js'; /** * Preconditions: @@ -17,7 +17,7 @@ const delete_case0 = (n) => { // If n is the root, there is nothing to do: // - all paths go through n, and // - n is black. - if (n.parent !== null) delete_case2(n); + if (n.parent !== null) delete_case1(n); }; export default delete_case0; diff --git a/src/deletion/delete_case2.js b/src/deletion/delete_case1.js similarity index 95% rename from src/deletion/delete_case2.js rename to src/deletion/delete_case1.js index 943273e..f67d9be 100644 --- a/src/deletion/delete_case2.js +++ b/src/deletion/delete_case1.js @@ -18,7 +18,7 @@ import delete_case4 from './delete_case4.js'; * * @param {Node} n - The input node. */ -const delete_case2 = (n) => { +const delete_case1 = (n) => { assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); @@ -49,4 +49,4 @@ const delete_case2 = (n) => { else delete_case3(n); }; -export default delete_case2; +export default delete_case1; diff --git a/src/deletion/delete_no_child.js b/src/deletion/delete_no_child.js index 1b8e54d..7b00891 100644 --- a/src/deletion/delete_no_child.js +++ b/src/deletion/delete_no_child.js @@ -4,7 +4,7 @@ import RED from '../color/RED.js'; import Node from '../types/Node.js'; import replace_node from './replace_node.js'; -import delete_case2 from './delete_case2.js'; +import delete_case1 from './delete_case1.js'; import prune from './prune.js'; @@ -40,7 +40,7 @@ const delete_no_child = (n) => { // If n is black, deleting it reduces the black-height of every path going // through it by 1. The leaf is black, so there are more things to fix. - delete_case2(leaf); + delete_case1(leaf); // Delete mocked leaf prune(leaf); diff --git a/src/index.js b/src/index.js index 992a102..2d9bc8c 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,7 @@ export {default as BLACK} from './color/BLACK.js'; export {default as RED} from './color/RED.js'; export {default as _debug} from './debug/_debug.js'; export {default as delete_case0} from './deletion/delete_case0.js'; -export {default as delete_case2} from './deletion/delete_case2.js'; +export {default as delete_case1} from './deletion/delete_case1.js'; export {default as delete_case3} from './deletion/delete_case3.js'; export {default as delete_case4} from './deletion/delete_case4.js'; export {default as delete_case5} from './deletion/delete_case5.js'; From 5fd0cf9a51fdef533be33d8c900c5cb36dc4bf06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:57:00 +0200 Subject: [PATCH 13/21] :truck: refactor: Rename delete_case3 to delete_case2. --- src/deletion/delete_case1.js | 4 ++-- src/deletion/{delete_case3.js => delete_case2.js} | 4 ++-- src/index.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/deletion/{delete_case3.js => delete_case2.js} (96%) diff --git a/src/deletion/delete_case1.js b/src/deletion/delete_case1.js index f67d9be..2e97d59 100644 --- a/src/deletion/delete_case1.js +++ b/src/deletion/delete_case1.js @@ -6,7 +6,7 @@ import rotate_left from '../rotate/rotate_left.js'; import rotate_right from '../rotate/rotate_right.js'; import sibling from '../family/sibling.js'; -import delete_case3 from './delete_case3.js'; +import delete_case2 from './delete_case2.js'; import delete_case4 from './delete_case4.js'; /** @@ -46,7 +46,7 @@ const delete_case1 = (n) => { } // Otherwise, go to case 3. - else delete_case3(n); + else delete_case2(n); }; export default delete_case1; diff --git a/src/deletion/delete_case3.js b/src/deletion/delete_case2.js similarity index 96% rename from src/deletion/delete_case3.js rename to src/deletion/delete_case2.js index 8caec23..6909be9 100644 --- a/src/deletion/delete_case3.js +++ b/src/deletion/delete_case2.js @@ -17,7 +17,7 @@ import delete_case4 from './delete_case4.js'; * * @param {Node} n - The input node. */ -const delete_case3 = (n) => { +const delete_case2 = (n) => { assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); @@ -51,4 +51,4 @@ const delete_case3 = (n) => { else delete_case4(n); }; -export default delete_case3; +export default delete_case2; diff --git a/src/index.js b/src/index.js index 2d9bc8c..52a9784 100644 --- a/src/index.js +++ b/src/index.js @@ -5,7 +5,7 @@ export {default as RED} from './color/RED.js'; export {default as _debug} from './debug/_debug.js'; export {default as delete_case0} from './deletion/delete_case0.js'; export {default as delete_case1} from './deletion/delete_case1.js'; -export {default as delete_case3} from './deletion/delete_case3.js'; +export {default as delete_case2} from './deletion/delete_case2.js'; export {default as delete_case4} from './deletion/delete_case4.js'; export {default as delete_case5} from './deletion/delete_case5.js'; export {default as delete_case6} from './deletion/delete_case6.js'; From 1bfdbd752e3afc4057de2c73328a28c6bf251222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:57:10 +0200 Subject: [PATCH 14/21] :truck: refactor: Rename delete_case4 to delete_case3. --- src/deletion/delete_case1.js | 4 ++-- src/deletion/delete_case2.js | 4 ++-- src/deletion/{delete_case4.js => delete_case3.js} | 4 ++-- src/index.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/deletion/{delete_case4.js => delete_case3.js} (96%) diff --git a/src/deletion/delete_case1.js b/src/deletion/delete_case1.js index 2e97d59..3b08cb4 100644 --- a/src/deletion/delete_case1.js +++ b/src/deletion/delete_case1.js @@ -7,7 +7,7 @@ import rotate_right from '../rotate/rotate_right.js'; import sibling from '../family/sibling.js'; import delete_case2 from './delete_case2.js'; -import delete_case4 from './delete_case4.js'; +import delete_case3 from './delete_case3.js'; /** * Preconditions: @@ -42,7 +42,7 @@ const delete_case1 = (n) => { s._color = BLACK; if (n === n.parent.left) rotate_left(n.parent); else rotate_right(n.parent); - delete_case4(n); + delete_case3(n); } // Otherwise, go to case 3. diff --git a/src/deletion/delete_case2.js b/src/deletion/delete_case2.js index 6909be9..3e85560 100644 --- a/src/deletion/delete_case2.js +++ b/src/deletion/delete_case2.js @@ -5,7 +5,7 @@ import Node from '../types/Node.js'; import sibling from '../family/sibling.js'; import delete_case0 from './delete_case0.js'; -import delete_case4 from './delete_case4.js'; +import delete_case3 from './delete_case3.js'; /** * Preconditions: @@ -48,7 +48,7 @@ const delete_case2 = (n) => { } // Otherwise, go to case 4. - else delete_case4(n); + else delete_case3(n); }; export default delete_case2; diff --git a/src/deletion/delete_case4.js b/src/deletion/delete_case3.js similarity index 96% rename from src/deletion/delete_case4.js rename to src/deletion/delete_case3.js index ede4957..856a2df 100644 --- a/src/deletion/delete_case4.js +++ b/src/deletion/delete_case3.js @@ -17,7 +17,7 @@ import delete_case5 from './delete_case5.js'; * * @param {Node} n - The input node. */ -const delete_case4 = (n) => { +const delete_case3 = (n) => { assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); @@ -58,4 +58,4 @@ const delete_case4 = (n) => { else delete_case5(n); }; -export default delete_case4; +export default delete_case3; diff --git a/src/index.js b/src/index.js index 52a9784..e0f56b0 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ export {default as _debug} from './debug/_debug.js'; export {default as delete_case0} from './deletion/delete_case0.js'; export {default as delete_case1} from './deletion/delete_case1.js'; export {default as delete_case2} from './deletion/delete_case2.js'; -export {default as delete_case4} from './deletion/delete_case4.js'; +export {default as delete_case3} from './deletion/delete_case3.js'; export {default as delete_case5} from './deletion/delete_case5.js'; export {default as delete_case6} from './deletion/delete_case6.js'; export {default as delete_no_child} from './deletion/delete_no_child.js'; From 3a6cc9d9b287df92520bf3b3c109f7b34b3e489a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:57:20 +0200 Subject: [PATCH 15/21] :truck: refactor: Rename delete_case5 to delete_case4. --- src/deletion/delete_case3.js | 4 ++-- src/deletion/{delete_case5.js => delete_case4.js} | 4 ++-- src/index.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/deletion/{delete_case5.js => delete_case4.js} (97%) diff --git a/src/deletion/delete_case3.js b/src/deletion/delete_case3.js index 856a2df..bbeb718 100644 --- a/src/deletion/delete_case3.js +++ b/src/deletion/delete_case3.js @@ -4,7 +4,7 @@ import RED from '../color/RED.js'; import Node from '../types/Node.js'; import sibling from '../family/sibling.js'; -import delete_case5 from './delete_case5.js'; +import delete_case4 from './delete_case4.js'; /** * Preconditions: @@ -55,7 +55,7 @@ const delete_case3 = (n) => { } // Otherwise, go to case 5. - else delete_case5(n); + else delete_case4(n); }; export default delete_case3; diff --git a/src/deletion/delete_case5.js b/src/deletion/delete_case4.js similarity index 97% rename from src/deletion/delete_case5.js rename to src/deletion/delete_case4.js index 9f0ea95..2bc5bc6 100644 --- a/src/deletion/delete_case5.js +++ b/src/deletion/delete_case4.js @@ -19,7 +19,7 @@ import delete_case6 from './delete_case6.js'; * * @param {Node} n - The input node. */ -const delete_case5 = (n) => { +const delete_case4 = (n) => { assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); @@ -70,4 +70,4 @@ const delete_case5 = (n) => { delete_case6(n); }; -export default delete_case5; +export default delete_case4; diff --git a/src/index.js b/src/index.js index e0f56b0..d2bf0c8 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,7 @@ export {default as delete_case0} from './deletion/delete_case0.js'; export {default as delete_case1} from './deletion/delete_case1.js'; export {default as delete_case2} from './deletion/delete_case2.js'; export {default as delete_case3} from './deletion/delete_case3.js'; -export {default as delete_case5} from './deletion/delete_case5.js'; +export {default as delete_case4} from './deletion/delete_case4.js'; export {default as delete_case6} from './deletion/delete_case6.js'; export {default as delete_no_child} from './deletion/delete_no_child.js'; export {default as delete_one_child} from './deletion/delete_one_child.js'; From 22e1f103b2626242025851ed867879b5fd254bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:57:30 +0200 Subject: [PATCH 16/21] :truck: refactor: Rename delete_case6 to delete_case5. --- src/deletion/delete_case4.js | 4 ++-- src/deletion/{delete_case6.js => delete_case5.js} | 4 ++-- src/index.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/deletion/{delete_case6.js => delete_case5.js} (96%) diff --git a/src/deletion/delete_case4.js b/src/deletion/delete_case4.js index 2bc5bc6..e57911d 100644 --- a/src/deletion/delete_case4.js +++ b/src/deletion/delete_case4.js @@ -6,7 +6,7 @@ import rotate_left from '../rotate/rotate_left.js'; import rotate_right from '../rotate/rotate_right.js'; import sibling from '../family/sibling.js'; -import delete_case6 from './delete_case6.js'; +import delete_case5 from './delete_case5.js'; /** * Preconditions: @@ -67,7 +67,7 @@ const delete_case4 = (n) => { rotate_left(s); } - delete_case6(n); + delete_case5(n); }; export default delete_case4; diff --git a/src/deletion/delete_case6.js b/src/deletion/delete_case5.js similarity index 96% rename from src/deletion/delete_case6.js rename to src/deletion/delete_case5.js index 8ec3830..93e5eb2 100644 --- a/src/deletion/delete_case6.js +++ b/src/deletion/delete_case5.js @@ -18,7 +18,7 @@ import sibling from '../family/sibling.js'; * * @param {Node} n - The input node. */ -const delete_case6 = (n) => { +const delete_case5 = (n) => { assert(n instanceof Node); assert(n._color === BLACK); assert(n.parent !== null); @@ -61,4 +61,4 @@ const delete_case6 = (n) => { } }; -export default delete_case6; +export default delete_case5; diff --git a/src/index.js b/src/index.js index d2bf0c8..e6b50bf 100644 --- a/src/index.js +++ b/src/index.js @@ -8,7 +8,7 @@ export {default as delete_case1} from './deletion/delete_case1.js'; export {default as delete_case2} from './deletion/delete_case2.js'; export {default as delete_case3} from './deletion/delete_case3.js'; export {default as delete_case4} from './deletion/delete_case4.js'; -export {default as delete_case6} from './deletion/delete_case6.js'; +export {default as delete_case5} from './deletion/delete_case5.js'; export {default as delete_no_child} from './deletion/delete_no_child.js'; export {default as delete_one_child} from './deletion/delete_one_child.js'; export {default as prune} from './deletion/prune.js'; From c77e38b8a2f334922f1ecf06cf975dc025d24d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:57:40 +0200 Subject: [PATCH 17/21] :truck: refactor: Rename insert_case1 to insert_case0. --- src/index.js | 2 +- src/insertion/insert.js | 2 +- src/insertion/{insert_case1.js => insert_case0.js} | 4 ++-- src/insertion/insert_case3.js | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/insertion/{insert_case1.js => insert_case0.js} (91%) diff --git a/src/index.js b/src/index.js index e6b50bf..67f80bf 100644 --- a/src/index.js +++ b/src/index.js @@ -18,7 +18,7 @@ export {default as predecessor} from './family/predecessor.js'; export {default as sibling} from './family/sibling.js'; export {default as uncle} from './family/uncle.js'; export {default as insert} from './insertion/insert.js'; -export {default as insert_case1} from './insertion/insert_case1.js'; +export {default as insert_case0} from './insertion/insert_case0.js'; export {default as insert_case2} from './insertion/insert_case2.js'; export {default as insert_case3} from './insertion/insert_case3.js'; export {default as insert_case4} from './insertion/insert_case4.js'; diff --git a/src/insertion/insert.js b/src/insertion/insert.js index 3fc0933..f41d3c6 100644 --- a/src/insertion/insert.js +++ b/src/insertion/insert.js @@ -10,7 +10,7 @@ import Node from '../types/Node.js'; * search tree. * For our red-black tree, all that is left to do is fix the red-black tree * properties in case they have been violated by this insertion. This is fixed - * by {@link insert_case1}. + * by {@link insert_case0}. * * @param {Function} compare - The comparison function to use. * @param {Node} A - The root of the tree. diff --git a/src/insertion/insert_case1.js b/src/insertion/insert_case0.js similarity index 91% rename from src/insertion/insert_case1.js rename to src/insertion/insert_case0.js index 1b0f2bf..08b9a26 100644 --- a/src/insertion/insert_case1.js +++ b/src/insertion/insert_case0.js @@ -11,7 +11,7 @@ import insert_case2 from './insert_case2.js'; * * @param {Node} n - The input node. */ -const insert_case1 = (n) => { +const insert_case0 = (n) => { assert(n instanceof Node); assert(n._color === RED); assert(n.left._color === BLACK); @@ -27,4 +27,4 @@ const insert_case1 = (n) => { else insert_case2(n); }; -export default insert_case1; +export default insert_case0; diff --git a/src/insertion/insert_case3.js b/src/insertion/insert_case3.js index 189a851..888deb3 100644 --- a/src/insertion/insert_case3.js +++ b/src/insertion/insert_case3.js @@ -4,7 +4,7 @@ import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import uncle from '../family/uncle.js'; import grandparent from '../family/grandparent.js'; -import insert_case1 from './insert_case1.js'; +import insert_case0 from './insert_case0.js'; import insert_case4 from './insert_case4.js'; /** @@ -28,7 +28,7 @@ const insert_case3 = (n) => { /** * If n has a non-leaf uncle and this uncle is red then we simply * repaint the parent and the uncle of n in black, the grandparent of - * n in red, then call insert_case1 on n's grandparent. + * n in red, then call insert_case0 on n's grandparent. * * B >R * / \ / \ @@ -44,7 +44,7 @@ const insert_case3 = (n) => { u._color = BLACK; const g = grandparent(n); g._color = RED; - insert_case1(g); + insert_case0(g); } else insert_case4(n); }; From 31612013edbffc7ad63980fbe950db8653b0c172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:57:50 +0200 Subject: [PATCH 18/21] :truck: refactor: Rename insert_case2 to insert_case1. --- src/index.js | 2 +- src/insertion/insert_case0.js | 4 ++-- src/insertion/{insert_case2.js => insert_case1.js} | 4 ++-- src/types/RedBlackTree.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/insertion/{insert_case2.js => insert_case1.js} (92%) diff --git a/src/index.js b/src/index.js index 67f80bf..2402273 100644 --- a/src/index.js +++ b/src/index.js @@ -19,7 +19,7 @@ export {default as sibling} from './family/sibling.js'; export {default as uncle} from './family/uncle.js'; export {default as insert} from './insertion/insert.js'; export {default as insert_case0} from './insertion/insert_case0.js'; -export {default as insert_case2} from './insertion/insert_case2.js'; +export {default as insert_case1} from './insertion/insert_case1.js'; export {default as insert_case3} from './insertion/insert_case3.js'; export {default as insert_case4} from './insertion/insert_case4.js'; export {default as insert_case5} from './insertion/insert_case5.js'; diff --git a/src/insertion/insert_case0.js b/src/insertion/insert_case0.js index 08b9a26..923093e 100644 --- a/src/insertion/insert_case0.js +++ b/src/insertion/insert_case0.js @@ -2,7 +2,7 @@ import assert from 'assert'; import Node from '../types/Node.js'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; -import insert_case2 from './insert_case2.js'; +import insert_case1 from './insert_case1.js'; /** * Preconditions: @@ -24,7 +24,7 @@ const insert_case0 = (n) => { * - - */ if (n.parent === null) n._color = BLACK; - else insert_case2(n); + else insert_case1(n); }; export default insert_case0; diff --git a/src/insertion/insert_case2.js b/src/insertion/insert_case1.js similarity index 92% rename from src/insertion/insert_case2.js rename to src/insertion/insert_case1.js index 5522976..af21f47 100644 --- a/src/insertion/insert_case2.js +++ b/src/insertion/insert_case1.js @@ -12,7 +12,7 @@ import insert_case3 from './insert_case3.js'; * * @param {Node} n - The input node. */ -const insert_case2 = (n) => { +const insert_case1 = (n) => { assert(n instanceof Node); assert(n._color === RED); assert(n.left === null || n.left._color === BLACK); @@ -33,4 +33,4 @@ const insert_case2 = (n) => { insert_case3(n); }; -export default insert_case2; +export default insert_case1; diff --git a/src/types/RedBlackTree.js b/src/types/RedBlackTree.js index 10b3ff9..e161abc 100644 --- a/src/types/RedBlackTree.js +++ b/src/types/RedBlackTree.js @@ -4,7 +4,7 @@ import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; import predecessor from '../family/predecessor.js'; import insert from '../insertion/insert.js'; -import insert_case2 from '../insertion/insert_case2.js'; +import insert_case1 from '../insertion/insert_case1.js'; import delete_one_child from '../deletion/delete_one_child.js'; import delete_no_child from '../deletion/delete_no_child.js'; import search from '../search/search.js'; @@ -49,7 +49,7 @@ export default class RedBlackTree { } else { const node = new Node(RED, key); insert(this.compare, this.root, node); - insert_case2(node); + insert_case1(node); } } From 33a4d313c71ea2e34e4150698f17bf3adc11aca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:58:00 +0200 Subject: [PATCH 19/21] :truck: refactor: Rename insert_case3 to insert_case2. --- src/index.js | 2 +- src/insertion/insert_case1.js | 4 ++-- src/insertion/{insert_case3.js => insert_case2.js} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/insertion/{insert_case3.js => insert_case2.js} (96%) diff --git a/src/index.js b/src/index.js index 2402273..18e53a9 100644 --- a/src/index.js +++ b/src/index.js @@ -20,7 +20,7 @@ export {default as uncle} from './family/uncle.js'; export {default as insert} from './insertion/insert.js'; export {default as insert_case0} from './insertion/insert_case0.js'; export {default as insert_case1} from './insertion/insert_case1.js'; -export {default as insert_case3} from './insertion/insert_case3.js'; +export {default as insert_case2} from './insertion/insert_case2.js'; export {default as insert_case4} from './insertion/insert_case4.js'; export {default as insert_case5} from './insertion/insert_case5.js'; export {default as rotate_left} from './rotate/rotate_left.js'; diff --git a/src/insertion/insert_case1.js b/src/insertion/insert_case1.js index af21f47..56180a1 100644 --- a/src/insertion/insert_case1.js +++ b/src/insertion/insert_case1.js @@ -2,7 +2,7 @@ import assert from 'assert'; import Node from '../types/Node.js'; import BLACK from '../color/BLACK.js'; import RED from '../color/RED.js'; -import insert_case3 from './insert_case3.js'; +import insert_case2 from './insert_case2.js'; /** * Preconditions: @@ -30,7 +30,7 @@ const insert_case1 = (n) => { */ if (n.parent._color === BLACK) return; - insert_case3(n); + insert_case2(n); }; export default insert_case1; diff --git a/src/insertion/insert_case3.js b/src/insertion/insert_case2.js similarity index 96% rename from src/insertion/insert_case3.js rename to src/insertion/insert_case2.js index 888deb3..cb0cbf5 100644 --- a/src/insertion/insert_case3.js +++ b/src/insertion/insert_case2.js @@ -16,7 +16,7 @@ import insert_case4 from './insert_case4.js'; * * @param {Node} n - The input node. */ -const insert_case3 = (n) => { +const insert_case2 = (n) => { assert(n instanceof Node); assert(n._color === RED); assert(n.left === null || n.left._color === BLACK); @@ -48,4 +48,4 @@ const insert_case3 = (n) => { } else insert_case4(n); }; -export default insert_case3; +export default insert_case2; From 5d6cedcb8b90d959cfa0bacdf517636c987ba665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:58:10 +0200 Subject: [PATCH 20/21] :truck: refactor: Rename insert_case4 to insert_case3. --- src/index.js | 2 +- src/insertion/insert_case2.js | 4 ++-- src/insertion/{insert_case4.js => insert_case3.js} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/insertion/{insert_case4.js => insert_case3.js} (97%) diff --git a/src/index.js b/src/index.js index 18e53a9..69cd938 100644 --- a/src/index.js +++ b/src/index.js @@ -21,7 +21,7 @@ export {default as insert} from './insertion/insert.js'; export {default as insert_case0} from './insertion/insert_case0.js'; export {default as insert_case1} from './insertion/insert_case1.js'; export {default as insert_case2} from './insertion/insert_case2.js'; -export {default as insert_case4} from './insertion/insert_case4.js'; +export {default as insert_case3} from './insertion/insert_case3.js'; export {default as insert_case5} from './insertion/insert_case5.js'; export {default as rotate_left} from './rotate/rotate_left.js'; export {default as rotate_right} from './rotate/rotate_right.js'; diff --git a/src/insertion/insert_case2.js b/src/insertion/insert_case2.js index cb0cbf5..22b522c 100644 --- a/src/insertion/insert_case2.js +++ b/src/insertion/insert_case2.js @@ -5,7 +5,7 @@ import RED from '../color/RED.js'; import uncle from '../family/uncle.js'; import grandparent from '../family/grandparent.js'; import insert_case0 from './insert_case0.js'; -import insert_case4 from './insert_case4.js'; +import insert_case3 from './insert_case3.js'; /** * Preconditions: @@ -45,7 +45,7 @@ const insert_case2 = (n) => { const g = grandparent(n); g._color = RED; insert_case0(g); - } else insert_case4(n); + } else insert_case3(n); }; export default insert_case2; diff --git a/src/insertion/insert_case4.js b/src/insertion/insert_case3.js similarity index 97% rename from src/insertion/insert_case4.js rename to src/insertion/insert_case3.js index 999ef31..3e81af1 100644 --- a/src/insertion/insert_case4.js +++ b/src/insertion/insert_case3.js @@ -19,7 +19,7 @@ import insert_case5 from './insert_case5.js'; * * @param {Node} n - The input node. */ -const insert_case4 = (n) => { +const insert_case3 = (n) => { assert(n instanceof Node); assert(n._color === RED); assert(n.left === null || n.left._color === BLACK); @@ -89,4 +89,4 @@ const insert_case4 = (n) => { insert_case5(n); }; -export default insert_case4; +export default insert_case3; From d935d6b0bacae5de08a6d4017c6e11194ffa9e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Ooms?= Date: Wed, 31 Mar 2021 17:58:20 +0200 Subject: [PATCH 21/21] :truck: refactor: Rename insert_case5 to insert_case4. --- src/index.js | 2 +- src/insertion/insert_case3.js | 10 +++++----- src/insertion/{insert_case5.js => insert_case4.js} | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/insertion/{insert_case5.js => insert_case4.js} (97%) diff --git a/src/index.js b/src/index.js index 69cd938..e3d1f23 100644 --- a/src/index.js +++ b/src/index.js @@ -22,7 +22,7 @@ export {default as insert_case0} from './insertion/insert_case0.js'; export {default as insert_case1} from './insertion/insert_case1.js'; export {default as insert_case2} from './insertion/insert_case2.js'; export {default as insert_case3} from './insertion/insert_case3.js'; -export {default as insert_case5} from './insertion/insert_case5.js'; +export {default as insert_case4} from './insertion/insert_case4.js'; export {default as rotate_left} from './rotate/rotate_left.js'; export {default as rotate_right} from './rotate/rotate_right.js'; export {default as search} from './search/search.js'; diff --git a/src/insertion/insert_case3.js b/src/insertion/insert_case3.js index 3e81af1..fbde812 100644 --- a/src/insertion/insert_case3.js +++ b/src/insertion/insert_case3.js @@ -5,7 +5,7 @@ import RED from '../color/RED.js'; import rotate_left from '../rotate/rotate_left.js'; import rotate_right from '../rotate/rotate_right.js'; import grandparent from '../family/grandparent.js'; -import insert_case5 from './insert_case5.js'; +import insert_case4 from './insert_case4.js'; /** * Preconditions: @@ -15,7 +15,7 @@ import insert_case5 from './insert_case5.js'; * - n's parent is red. * - n's uncle is black. * - * Here we fix the input subtree to pass the preconditions of {@link insert_case5}. + * Here we fix the input subtree to pass the preconditions of {@link insert_case4}. * * @param {Node} n - The input node. */ @@ -30,7 +30,7 @@ const insert_case3 = (n) => { /** * If the path from g to n makes a left-right, change it to a left-left - * with {@link rotate_left}. Then call {@link insert_case5} on the old + * with {@link rotate_left}. Then call {@link insert_case4} on the old * parent of n. * * B B @@ -60,7 +60,7 @@ const insert_case3 = (n) => { } else if (n === n.parent.left && n.parent === g.right) { /** * If the path from g to n makes a right-left, change it to a right-right - * with {@link rotate_right}. Then call {@link insert_case5} on the old + * with {@link rotate_right}. Then call {@link insert_case4} on the old * parent of n. * * B B @@ -86,7 +86,7 @@ const insert_case3 = (n) => { // n = n.right ; } - insert_case5(n); + insert_case4(n); }; export default insert_case3; diff --git a/src/insertion/insert_case5.js b/src/insertion/insert_case4.js similarity index 97% rename from src/insertion/insert_case5.js rename to src/insertion/insert_case4.js index b4f561d..14fff50 100644 --- a/src/insertion/insert_case5.js +++ b/src/insertion/insert_case4.js @@ -17,7 +17,7 @@ import grandparent from '../family/grandparent.js'; * * @param {Node} n - The input node. */ -const insert_case5 = (n) => { +const insert_case4 = (n) => { assert(n instanceof Node); assert(n._color === RED); assert(n.left === null || n.left._color === BLACK); @@ -67,4 +67,4 @@ const insert_case5 = (n) => { } }; -export default insert_case5; +export default insert_case4;