Skip to content

Commit 8fed652

Browse files
♻️ refactor: Fix docs, sprinkle asserts.
The entire thing is now sort-of type-checked. `tsc` does not report any errors.
1 parent 5ab26ad commit 8fed652

27 files changed

+281
-88
lines changed

src/adt/Leaf.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
import assert from 'assert';
12
import {BLACK} from '../color/BLACK.js';
3+
import {Node} from './Node.js';
24

35
/**
46
* A black leaf node.
57
*
6-
* @constructor
8+
* @class
79
* @param {Node} parent - The parent node in the tree.
8-
* @returns {Leaf}
910
*/
1011
export function Leaf(parent) {
12+
assert(parent instanceof Node);
13+
/** @constant {number} The color of the node. */
1114
this._color = BLACK;
15+
/** @member {Node} The parent node. */
1216
this.parent = parent;
1317
}
1418

src/adt/Node.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@ import {Leaf} from './Leaf.js';
33
/**
44
* An internal node. This node can be red or black.
55
*
6-
* @constructor
7-
* @param {Number} color - The color of the node.
8-
* @param {Key} key - The key of the node.
9-
* @returns {Node}
6+
* @class
7+
* @param {number} color - The color of the node.
8+
* @param {any} key - The key of the node.
109
*/
1110
export function Node(color, key) {
11+
/** @member {number} The color of the node. */
1212
this._color = color;
13+
/** @member {Node|Leaf} The left child */
1314
this.left = new Leaf(this);
15+
/** @member {Node|Leaf} The right child */
1416
this.right = new Leaf(this);
17+
/** @member {Node} The parent node. */
1518
this.parent = null;
19+
/** @member {any} The key held by this node. */
1620
this.key = key;
1721
}
1822

src/adt/RedBlackTree.js

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import assert from 'assert';
12
import {Node} from './Node.js';
23
import {BLACK, RED} from '../color/index.js';
34
import {predecessor} from '../family/predecessor.js';
@@ -15,10 +16,11 @@ export class RedBlackTree {
1516
* Constructs a new empty red-black tree.
1617
*
1718
* @param {Function} compare - The comparison function for node keys.
18-
* @returns {RedBlackTree}
1919
*/
2020
constructor(compare) {
21+
/** @member {Function} The comparison function for node keys. */
2122
this.compare = compare;
23+
/** @member {Node} The root of the tree. */
2224
this.root = null;
2325
}
2426

@@ -34,7 +36,7 @@ export class RedBlackTree {
3436
/**
3537
* Adds a key to the tree.
3638
*
37-
* @param {Key} key - The key to add.
39+
* @param {any} key - The key to add.
3840
*/
3941
add(key) {
4042
if (this.root === null) {
@@ -51,7 +53,7 @@ export class RedBlackTree {
5153
* Returns the first node whose key equals the input key.
5254
* If no such node exists, returns <code>null</code>.
5355
*
54-
* @param {Key} key - The input key.
56+
* @param {any} key - The input key.
5557
* @returns {Node}
5658
*/
5759
_search(key) {
@@ -64,8 +66,8 @@ export class RedBlackTree {
6466
* in this way (with {@link RedBlackTree#_search}. If no such key exists
6567
* in the tree, returns <code>null</code>.
6668
*
67-
* @param {Key} key - The input key.
68-
* @returns {Key}
69+
* @param {any} key - The input key.
70+
* @returns {any}
6971
*/
7072
get(key) {
7173
const node = this._search(key);
@@ -76,7 +78,7 @@ export class RedBlackTree {
7678
* Returns <code>true</code> if and only if the tree contains the input
7779
* key.
7880
*
79-
* @param {Key} key - The input key.
81+
* @param {any} key - The input key.
8082
* @returns {Boolean}
8183
*/
8284
has(key) {
@@ -102,6 +104,7 @@ export class RedBlackTree {
102104
// If there is no left child, then there can only be one right
103105
// child.
104106
const succ = node.right;
107+
assert(succ instanceof Node);
105108
node.key = succ.key;
106109
// Delete successor node
107110
// note: this node can only have one non-leaf child
@@ -120,7 +123,7 @@ export class RedBlackTree {
120123
* (with {@link RedBlackTree#_delete}). If such a node is found and deleted
121124
* then return <code>true</code>. Return <code>false</code> otherwise.
122125
*
123-
* @param {Key} key - The input key.
126+
* @param {any} key - The input key.
124127
* @returns {Boolean} - Whether the key existed in the tree before removal.
125128
*/
126129
remove(key) {
@@ -134,9 +137,9 @@ export class RedBlackTree {
134137
/**
135138
* Returns an in order iterator over the keys of the tree that lie in the
136139
* interval [left, right[.
137-
* @param {Key} left - The left bound of the interval.
138-
* @param {Key} right - The right bound of the interval.
139-
* @returns {Iterator}
140+
* @param {any} left - The left bound of the interval.
141+
* @param {any} right - The right bound of the interval.
142+
* @returns {IterableIterator}
140143
*/
141144
*range(left, right) {
142145
if (this.root !== null)
@@ -146,7 +149,7 @@ export class RedBlackTree {
146149
/**
147150
* Returns an in order iterator over the keys of the tree.
148151
*
149-
* @returns {Iterator}
152+
* @returns {IterableIterator}
150153
*/
151154
*items() {
152155
if (this.root !== null) yield* inordertraversal(this.root);
@@ -163,7 +166,7 @@ export class RedBlackTree {
163166
* Constructs a red-black tree from an input iterable.
164167
*
165168
* @param {Function} compare - The comparison function to use.
166-
* @param {Iterbale} iterable - The input iterable.
169+
* @param {Iterable} iterable - The input iterable.
167170
* @returns {RedBlackTree}
168171
*/
169172
static from(compare, iterable) {

src/debug/debug.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import assert from 'assert';
2+
import {Node} from '../adt/Node.js';
3+
import {Leaf} from '../adt/Leaf.js';
14
import {BLACK} from '../color/BLACK.js';
25

36
/**
@@ -11,11 +14,17 @@ export const _debug = ({red, black}) => {
1114
* Recursively constructs a prettyprint string for the red-black tree rooted at
1215
* <code>root</code>.
1316
*
14-
* @param {Node} root - The root of the tree.
17+
* @param {Node|Leaf} root - The root of the tree.
1518
* @returns {string}
1619
*/
1720
const debug = (root) => {
18-
if (root.isLeaf()) return black('L');
21+
assert(root instanceof Node || root instanceof Leaf);
22+
if (root.isLeaf()) {
23+
assert(root instanceof Leaf);
24+
return black('L');
25+
}
26+
27+
assert(root instanceof Node);
1928

2029
const repr = root._color === BLACK ? black(root.key) : red(root.key);
2130

src/deletion/delete_case1.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import assert from 'assert';
2+
import {BLACK} from '../color/BLACK.js';
3+
import {Node} from '../adt/Node.js';
4+
import {Leaf} from '../adt/Leaf.js';
15
import {delete_case2} from './delete_case2.js';
26

37
/**
@@ -6,10 +10,13 @@ import {delete_case2} from './delete_case2.js';
610
* - all root-leaf paths going through n have a black height of b - 1
711
* - all other root-leaf paths have a black height of b
812
*
9-
* @param {Node} n - The input node.
13+
* @param {Node|Leaf} n - The input node.
1014
*/
1115
export const delete_case1 = (n) => {
12-
// If n is the root, there is nothing to do: all paths go through n, and n
13-
// is black.
16+
assert(n instanceof Node || n instanceof Leaf);
17+
assert(n._color === BLACK);
18+
// If n is the root, there is nothing to do:
19+
// - all paths go through n, and
20+
// - n is black.
1421
if (n.parent !== null) delete_case2(n);
1522
};

src/deletion/delete_case2.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import assert from 'assert';
12
import {BLACK, RED} from '../color/index.js';
3+
import {Node} from '../adt/Node.js';
4+
import {Leaf} from '../adt/Leaf.js';
25
import {rotate_left, rotate_right} from '../rotate/index.js';
36
import {sibling} from '../family/sibling.js';
47

@@ -12,9 +15,13 @@ import {delete_case4} from './delete_case4.js';
1215
* - all other root-leaf paths have a black height of b
1316
* - n is not the root
1417
*
15-
* @param {Node} n - The input node.
18+
* @param {Node|Leaf} n - The input node.
1619
*/
1720
export const delete_case2 = (n) => {
21+
assert(n instanceof Node || n instanceof Leaf);
22+
assert(n._color === BLACK);
23+
assert(n.parent !== null);
24+
1825
const s = sibling(n);
1926

2027
/**

src/deletion/delete_case3.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import assert from 'assert';
12
import {BLACK, RED} from '../color/index.js';
3+
import {Node} from '../adt/Node.js';
4+
import {Leaf} from '../adt/Leaf.js';
25
import {sibling} from '../family/sibling.js';
36

47
import {delete_case1} from './delete_case1.js';
@@ -12,10 +15,15 @@ import {delete_case4} from './delete_case4.js';
1215
* - n is not the root
1316
* - n's sibling is black
1417
*
15-
* @param {Node} n - The input node.
18+
* @param {Node|Leaf} n - The input node.
1619
*/
1720
export const delete_case3 = (n) => {
21+
assert(n instanceof Node || n instanceof Leaf);
22+
assert(n._color === BLACK);
23+
assert(n.parent !== null);
1824
const s = sibling(n);
25+
assert(s instanceof Node);
26+
assert(s._color === BLACK);
1927

2028
/**
2129
* If n's parent is black and n's sibling's children are black, then

src/deletion/delete_case4.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import assert from 'assert';
12
import {BLACK, RED} from '../color/index.js';
3+
import {Node} from '../adt/Node.js';
4+
import {Leaf} from '../adt/Leaf.js';
25
import {sibling} from '../family/sibling.js';
36

47
import {delete_case5} from './delete_case5.js';
@@ -12,10 +15,18 @@ import {delete_case5} from './delete_case5.js';
1215
* - n's sibling is black
1316
* - n's parent and n's sibling's children cannot all be black
1417
*
15-
* @param {Node} n - The input node.
18+
* @param {Node|Leaf} n - The input node.
1619
*/
1720
export const delete_case4 = (n) => {
21+
assert(n instanceof Node || n instanceof Leaf);
22+
assert(n._color === BLACK);
23+
assert(n.parent !== null);
1824
const s = sibling(n);
25+
assert(s instanceof Node);
26+
assert(s._color === BLACK);
27+
assert(
28+
n.parent._color === RED || s.left._color === RED || s.right._color === RED,
29+
);
1930

2031
/**
2132
* If n's parent is red and n's sibling's children are black, then swap n's

src/deletion/delete_case5.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import assert from 'assert';
12
import {BLACK, RED} from '../color/index.js';
3+
import {Node} from '../adt/Node.js';
4+
import {Leaf} from '../adt/Leaf.js';
25
import {rotate_left, rotate_right} from '../rotate/index.js';
36
import {sibling} from '../family/sibling.js';
47

@@ -13,10 +16,16 @@ import {delete_case6} from './delete_case6.js';
1316
* - n's sibling is black
1417
* - at least one of n's sibling's children is red
1518
*
16-
* @param {Node} n - The input node.
19+
* @param {Node|Leaf} n - The input node.
1720
*/
1821
export const delete_case5 = (n) => {
22+
assert(n instanceof Node || n instanceof Leaf);
23+
assert(n._color === BLACK);
24+
assert(n.parent !== null);
1925
const s = sibling(n);
26+
assert(s instanceof Node);
27+
assert(s._color === BLACK);
28+
assert(s.left._color === RED || s.right._color === RED);
2029

2130
// The following statements just force the red n's sibling child to be on
2231
// the left of the left of the parent, or right of the right, so case 6

src/deletion/delete_case6.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import assert from 'assert';
12
import {BLACK} from '../color/BLACK.js';
3+
import {RED} from '../color/RED.js';
4+
import {Node} from '../adt/Node.js';
5+
import {Leaf} from '../adt/Leaf.js';
26
import {rotate_left, rotate_right} from '../rotate/index.js';
37
import {sibling} from '../family/sibling.js';
48

@@ -12,10 +16,15 @@ import {sibling} from '../family/sibling.js';
1216
* - if n is a left child, the right child of n's sibling is red
1317
* - if n is a right child, the left child of n's sibling is red
1418
*
15-
* @param {Node} n - The input node.
19+
* @param {Node|Leaf} n - The input node.
1620
*/
1721
export const delete_case6 = (n) => {
22+
assert(n instanceof Node || n instanceof Leaf);
23+
assert(n._color === BLACK);
24+
assert(n.parent !== null);
1825
const s = sibling(n);
26+
assert(s instanceof Node);
27+
assert(s._color === BLACK);
1928

2029
/**
2130
* Increment the black height of all root-leaf paths going through n by
@@ -39,12 +48,14 @@ export const delete_case6 = (n) => {
3948
n.parent._color = BLACK;
4049

4150
if (n === n.parent.left) {
51+
assert(s.right._color === RED);
4252
s.right._color = BLACK;
4353
rotate_left(n.parent);
4454
}
4555

4656
// Symmetric case
4757
else {
58+
assert(s.left._color === RED);
4859
s.left._color = BLACK;
4960
rotate_right(n.parent);
5061
}

src/deletion/delete_one_child.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import assert from 'assert';
12
import {BLACK, RED} from '../color/index.js';
3+
import {Node} from '../adt/Node.js';
4+
import {Leaf} from '../adt/Leaf.js';
25

36
import {replace_node} from './replace_node.js';
47
import {delete_case1} from './delete_case1.js';
@@ -9,16 +12,18 @@ import {delete_case1} from './delete_case1.js';
912
* Precondition:
1013
* - n has at most one non-leaf child.
1114
* - if n has a non-leaf child, then it is its left child.
15+
* - hence, n's right child is a leaf
1216
*
1317
* @param {Node} n - The node to delete.
1418
*/
1519
export const delete_one_child = (n) => {
16-
// Precondition: n has at most one non-leaf child.
17-
// assert( n.right.isLeaf() || n.left.isLeaf());
20+
assert(n instanceof Node);
21+
// Precondition: n's right child is a leaf.
22+
// The right child of n is always a LEAF because either n is a subtree
23+
// predecessor or it is the only child of its parent by the red-black tree
24+
// properties
25+
assert(n.right instanceof Leaf);
1826

19-
// const child = n.right.isLeaf() ? n.left : n.right;
20-
// n.right is always a LEAF because either n is a subtree predecessor or it
21-
// is the only child of its parent by the red-black tree properties
2227
const child = n.left;
2328

2429
// Replace n with its left child
@@ -32,7 +37,9 @@ export const delete_one_child = (n) => {
3237
// replace n with it.
3338
if (child._color === RED) child._color = BLACK;
3439
// Otherwise, there are more things to fix.
35-
else delete_case1(child);
40+
else {
41+
delete_case1(child);
42+
}
3643
}
3744

3845
// Else {

0 commit comments

Comments
 (0)