diff --git a/README.md b/README.md index 2e8d7dc..0ac942e 100644 --- a/README.md +++ b/README.md @@ -832,43 +832,34 @@ Version 11.7.9 [//]: # (No deletion!!! Start of Replace Section)
heap
-
test nametime taken (ms)executions per secsample deviation
100,000 add6.63150.923.07e-4
100,000 add & poll35.0928.508.68e-4
-
-
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add89.6511.150.00
100,000 add randomly92.7410.780.01
100,000 get1.09921.217.98e-5
100,000 iterator25.7638.820.00
100,000 add & delete orderly159.436.270.02
100,000 add & delete randomly239.834.170.00
-
-
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push45.0122.220.01
100,000 push & shift6.24160.170.01
Native JS Array 100,000 push & shift2227.550.450.22
-
-
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push20.2149.470.00
1,000,000 push & pop26.8537.240.01
1,000,000 push & shift27.5736.270.00
100,000 push & shift2.61382.644.60e-4
Native JS Array 100,000 push & shift2152.900.460.22
100,000 unshift & shift2.51398.743.60e-4
Native JS Array 100,000 unshift & shift4376.450.230.30
-
-
hash-map
-
test nametime taken (ms)executions per secsample deviation
1,000,000 set86.7911.520.03
Native JS Map 1,000,000 set207.964.810.01
Native JS Set 1,000,000 add169.325.910.02
1,000,000 set & get81.3012.300.02
Native JS Map 1,000,000 set & get272.313.670.01
Native JS Set 1,000,000 add & has237.784.210.02
1,000,000 ObjKey set & get374.052.670.06
Native JS Map 1,000,000 ObjKey set & get345.512.890.06
Native JS Set 1,000,000 ObjKey add & has286.453.490.05
-
-
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push41.6024.045.38e-4
100,000 getWords81.0412.340.00
+
test nametime taken (ms)sample mean (secs)sample deviation
100,000 add6.850.013.38e-4
100,000 add & poll35.350.048.44e-4
avl-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add299.033.340.00
100,000 add randomly364.392.740.02
100,000 get1.08921.788.08e-5
100,000 iterator27.6036.230.00
100,000 add & delete orderly488.822.050.00
100,000 add & delete randomly649.461.540.01
+
test nametime taken (ms)sample mean (secs)sample deviation
100,000 add302.890.300.01
100,000 add randomly381.830.380.00
100,000 get0.605.95e-42.33e-4
100,000 getNode150.610.150.00
100,000 iterator28.230.030.00
100,000 add & delete orderly505.570.510.01
100,000 add & delete randomly677.360.680.00
-
binary-tree-overall
-
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add randomly7.76128.901.09e-4
10,000 RBTree get randomly0.119328.865.44e-6
10,000 RBTree add & delete randomly21.7246.042.07e-4
10,000 AVLTree add randomly27.4536.433.65e-4
10,000 AVLTree get randomly0.119432.494.04e-7
10,000 AVLTree add & delete randomly51.0319.606.60e-4
-
-
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.101.03e+41.04e-6
1,000 addEdge6.01166.291.12e-4
1,000 getVertex0.101.04e+41.71e-6
1,000 getEdge23.7242.150.00
tarjan194.375.140.00
topologicalSort152.916.540.02
+
rb-tree
+
test nametime taken (ms)sample mean (secs)sample deviation
100,000 add212.770.219.84e-4
100,000 add randomly163.700.160.00
100,000 get1.190.002.44e-4
100,000 getNode347.390.350.01
100,000 node mode add randomly162.260.160.00
100,000 node mode get344.900.340.00
100,000 iterator27.480.030.00
100,000 add & delete orderly386.330.390.00
100,000 add & delete randomly520.660.520.00
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push188.445.310.04
1,000,000 unshift177.485.630.02
1,000,000 unshift & shift161.096.210.04
1,000,000 addBefore277.843.600.08
+
test nametime taken (ms)sample mean (secs)sample deviation
1,000,000 push179.280.180.02
1,000,000 unshift197.220.200.05
1,000,000 unshift & shift153.160.150.00
1,000,000 addBefore247.300.250.03
-
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift193.995.150.05
10,000 push & pop232.824.300.01
10,000 addBefore285.443.500.02
+
directed-graph
+
test nametime taken (ms)sample mean (secs)sample deviation
1,000 addVertex0.109.92e-51.16e-6
1,000 addEdge6.440.010.00
1,000 getVertex0.109.82e-51.13e-6
1,000 getEdge22.600.020.00
tarjan186.560.190.00
topologicalSort145.420.150.01
-
priority-queue
-
test nametime taken (ms)executions per secsample deviation
100,000 add30.0233.312.68e-4
100,000 add & poll89.2111.210.00
+
queue
+
test nametime taken (ms)sample mean (secs)sample deviation
1,000,000 push47.740.050.02
100,000 push & shift5.390.011.25e-4
Native JS Array 100,000 push & shift2225.502.230.10
+
+
deque
+
test nametime taken (ms)sample mean (secs)sample deviation
1,000,000 push22.880.020.01
1,000,000 push & pop27.950.030.01
1,000,000 push & shift29.830.030.01
100,000 push & shift2.710.009.03e-4
Native JS Array 100,000 push & shift2182.032.180.04
100,000 unshift & shift2.610.008.71e-4
Native JS Array 100,000 unshift & shift4185.904.190.04
+
+
hash-map
+
test nametime taken (ms)sample mean (secs)sample deviation
1,000,000 set253.450.250.07
Native JS Map 1,000,000 set228.900.230.02
Native JS Set 1,000,000 add179.650.180.01
1,000,000 set & get234.960.230.06
Native JS Map 1,000,000 set & get284.900.280.01
Native JS Set 1,000,000 add & has254.900.250.03
1,000,000 ObjKey set & get403.740.400.10
Native JS Map 1,000,000 ObjKey set & get340.180.340.07
Native JS Set 1,000,000 ObjKey add & has300.250.300.06
+
+
trie
+
test nametime taken (ms)sample mean (secs)sample deviation
100,000 push44.110.048.55e-4
100,000 getWords86.670.090.00
stack
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push42.7323.400.00
1,000,000 push & pop50.5219.790.02
+
test nametime taken (ms)sample mean (secs)sample deviation
1,000,000 push43.180.040.01
1,000,000 push & pop48.400.050.02
[//]: # (No deletion!!! End of Replace Section) diff --git a/package.json b/package.json index d10b485..5486d08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.53.6", + "version": "1.53.7", "description": "Javascript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python", "main": "dist/cjs/index.js", "module": "dist/mjs/index.js", diff --git a/src/data-structures/binary-tree/avl-tree-multi-map.ts b/src/data-structures/binary-tree/avl-tree-multi-map.ts index 9cd2c98..1d6af87 100644 --- a/src/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -143,8 +143,9 @@ export class AVLTreeMultiMap< return new AVLTreeMultiMap([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - comparator: this._comparator, + extractComparable: this._extractComparable, toEntryFn: this._toEntryFn, + isReverse: this._isReverse, ...options }) as TREE; } diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 1b3635d..718402a 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -113,8 +113,9 @@ export class AVLTree< return new AVLTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - comparator: this._comparator, + extractComparable: this._extractComparable, toEntryFn: this._toEntryFn, + isReverse: this._isReverse, ...options }) as TREE; } diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index f7462b0..2306c51 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -272,15 +272,15 @@ export class BinaryTree< const key = keyNodeEntryOrRaw[0]; if (key === null) return null; if (key === undefined) return; - return this.getNodeByKey(key, iterationType); + return this.getNode(key, this._root, iterationType); } if (this._toEntryFn) { const [key] = this._toEntryFn(keyNodeEntryOrRaw as R); - if (this.isKey(key)) return this.getNodeByKey(key); + if (this.isKey(key)) return this.getNode(key); } - if (this.isKey(keyNodeEntryOrRaw)) return this.getNodeByKey(keyNodeEntryOrRaw, iterationType); + if (this.isKey(keyNodeEntryOrRaw)) return this.getNode(keyNodeEntryOrRaw, this._root, iterationType); return; } @@ -521,6 +521,10 @@ export class BinaryTree< /** * Time Complexity: O(k * n) * Space Complexity: O(1) + * + * The `merge` function in TypeScript merges another binary tree into the current tree by adding all + * elements from the other tree. + * @param anotherTree - `BinaryTree` */ merge(anotherTree: BinaryTree) { this.addMany(anotherTree, []); @@ -730,23 +734,6 @@ export class BinaryTree< return this.search(keyNodeEntryRawOrPredicate, true, node => node, startNode, iterationType)[0] ?? null; } - /** - * Time Complexity: O(n) - * Space Complexity: O(log n) - * - * The function `getNodeByKey` retrieves a node by its key from a binary tree structure. - * @param {K} key - The `key` parameter is the value used to search for a specific node in a data - * structure. - * @param {IterationType} iterationType - The `iterationType` parameter is a type of iteration that - * specifies how the tree nodes should be traversed when searching for a node with the given key. It - * is an optional parameter with a default value of `this.iterationType`. - * @returns The `getNodeByKey` function is returning an optional binary tree node - * (`OptNodeOrNull`). - */ - getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptNodeOrNull { - return this.getNode(key, this._root, iterationType); - } - /** * Time Complexity: O(n) * Space Complexity: O(log n) @@ -1170,7 +1157,6 @@ export class BinaryTree< iterationType: IterationType = this.iterationType ): ReturnType { if (this.isNIL(startNode)) return callback(undefined); - // TODO support get right most by passing key in startNode = this.ensureNode(startNode); if (!startNode) return callback(startNode); @@ -2245,6 +2231,9 @@ export class BinaryTree< } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The _clearNodes function sets the root node to undefined and resets the size to 0. */ protected _clearNodes() { @@ -2253,6 +2242,9 @@ export class BinaryTree< } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The _clearValues function clears all values stored in the _store object. */ protected _clearValues() { diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index d359c60..31faccc 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -11,6 +11,7 @@ import type { BSTNOptKeyOrNode, BSTOptions, BTNRep, + Comparable, Comparator, CP, DFSOrderPattern, @@ -168,9 +169,9 @@ export class BST< super([], options); if (options) { - const { comparator, isReverse } = options; + const { extractComparable, isReverse } = options; + if (typeof extractComparable === 'function') this._extractComparable = extractComparable; if (isReverse !== undefined) this._isReverse = isReverse; - if (comparator !== undefined) this._comparator = comparator; } if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); @@ -186,9 +187,14 @@ export class BST< return this._root; } - protected _isReverse = false; + protected _isReverse: boolean = false; - get isReverse() { + /** + * The above function is a getter method in TypeScript that returns the value of the private property + * `_isReverse`. + * @returns The `isReverse` property of the object, which is a boolean value. + */ + get isReverse(): boolean { return this._isReverse; } @@ -215,8 +221,9 @@ export class BST< return new BST([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - comparator: this._comparator, + extractComparable: this._extractComparable, toEntryFn: this._toEntryFn, + isReverse: this._isReverse, ...options }) as TREE; } @@ -281,7 +288,7 @@ export class BST< * this._DEFAULT_COMPARATOR`. */ override isKey(key: any): key is K { - return isComparable(key, this._compare !== this._DEFAULT_COMPARATOR); + return isComparable(key, this._extractComparable !== undefined); } /** @@ -453,8 +460,13 @@ export class BST< } /** - * Time Complexity: O(k * n) + * Time Complexity: O(n) * Space Complexity: O(1) + * + * The `merge` function overrides the base class method by adding elements from another + * binary search tree. + * @param anotherTree - `anotherTree` is an instance of a Binary Search Tree (BST) with key type `K`, + * value type `V`, return type `R`, node type `NODE`, and tree type `TREE`. */ override merge(anotherTree: BST) { this.addMany(anotherTree, [], false); @@ -629,23 +641,6 @@ export class BST< return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined; } - /** - * Time Complexity: O(log n) - * Space Complexity: O(1) - * - * The function `getNodeByKey` returns a node with a specific key from a tree data structure. - * @param {K} key - The key parameter is the value used to search for a specific node in the tree. It - * is typically a unique identifier or a value that can be used to determine the position of the node - * in the tree structure. - * @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter is an optional - * parameter that specifies the type of iteration to be used when searching for a node in the tree. - * It has a default value of `'ITERATIVE'`. - * @returns The method is returning a NODE object or undefined. - */ - override getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptNode { - return this.getNode(key, this._root, iterationType); - } - /** * Time complexity: O(n) * Space complexity: O(n) @@ -764,7 +759,7 @@ export class BST< const dfs = (cur: NODE) => { const compared = this._compare(cur.key, targetKey); if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur)); - + // TODO here can be optimized to O(log n) if (this.isRealNode(cur.left)) dfs(cur.left); if (this.isRealNode(cur.right)) dfs(cur.right); }; @@ -894,19 +889,26 @@ export class BST< return balanced; } - protected _DEFAULT_COMPARATOR = (a: K, b: K): number => { + protected _comparator: Comparator = (a: K, b: K): number => { + if (isComparable(a) && isComparable(b)) { + if (a > b) return 1; + if (a < b) return -1; + return 0; + } + if (this._extractComparable) { + if (this._extractComparable(a) > this._extractComparable(b)) return 1; + if (this._extractComparable(a) < this._extractComparable(b)) return -1; + return 0; + } if (typeof a === 'object' || typeof b === 'object') { throw TypeError( - `When comparing object types, a custom comparator must be defined in the constructor's options parameter.` + `When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.` ); } - if (a > b) return 1; - if (a < b) return -1; + return 0; }; - protected _comparator: Comparator = this._DEFAULT_COMPARATOR; - /** * The function returns the value of the _comparator property. * @returns The `_comparator` property is being returned. @@ -915,6 +917,17 @@ export class BST< return this._comparator; } + protected _extractComparable?: (key: K) => Comparable; + + /** + * This function returns the value of the `_extractComparable` property. + * @returns The method `extractComparable()` is being returned, which is a getter method for the + * `_extractComparable` property. + */ + get extractComparable() { + return this._extractComparable; + } + /** * The function sets the root of a tree-like structure and updates the parent property of the new * root. diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index d4e35e2..cf1bfaa 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -123,7 +123,7 @@ export class RedBlackTree< return new RedBlackTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - comparator: this._comparator, + extractComparable: this._extractComparable, toEntryFn: this._toEntryFn, ...options }) as TREE; diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts index aa4ba5f..ee38f54 100644 --- a/src/data-structures/binary-tree/tree-multi-map.ts +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -139,7 +139,7 @@ export class TreeMultiMap< return new TreeMultiMap([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - comparator: this._comparator, + extractComparable: this._extractComparable, toEntryFn: this._toEntryFn, ...options }) as TREE; diff --git a/src/data-structures/linked-list/doubly-linked-list.ts b/src/data-structures/linked-list/doubly-linked-list.ts index 9430390..db616cc 100644 --- a/src/data-structures/linked-list/doubly-linked-list.ts +++ b/src/data-structures/linked-list/doubly-linked-list.ts @@ -819,7 +819,9 @@ export class DoublyLinkedList extends IterableElementBase, newElementOrNode: E | DoublyLinkedListNode ): boolean { - const existingNode: DoublyLinkedListNode | undefined = this.getNode(existingElementOrNode); + const existingNode: DoublyLinkedListNode | undefined = this.isNode(existingElementOrNode) + ? existingElementOrNode + : this.getNode(existingElementOrNode); if (existingNode) { const newNode = this._ensureNode(newElementOrNode); @@ -856,7 +858,9 @@ export class DoublyLinkedList extends IterableElementBase, newElementOrNode: E | DoublyLinkedListNode): boolean { - const existingNode: DoublyLinkedListNode | undefined = this.getNode(existingElementOrNode); + const existingNode: DoublyLinkedListNode | undefined = this.isNode(existingElementOrNode) + ? existingElementOrNode + : this.getNode(existingElementOrNode); if (existingNode) { const newNode = this._ensureNode(newElementOrNode); diff --git a/src/types/common.ts b/src/types/common.ts index a4f07a2..9e17049 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -23,5 +23,3 @@ export type OptValue = V | undefined; export type IterableWithSizeOrLength = IterableWithSize | IterableWithLength; export type CRUD = 'CREATED' | 'READ' | 'UPDATED' | 'DELETED'; - -export type Arithmetic = number | bigint; diff --git a/src/types/data-structures/binary-tree/bst.ts b/src/types/data-structures/binary-tree/bst.ts index 138e662..453b460 100644 --- a/src/types/data-structures/binary-tree/bst.ts +++ b/src/types/data-structures/binary-tree/bst.ts @@ -1,13 +1,13 @@ import { BST, BSTNode } from '../../../data-structures'; import type { BinaryTreeOptions } from './binary-tree'; -import { Comparator } from '../../common'; +import { Comparable } from '../../utils'; export type BSTNodeNested = BSTNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BSTNested> = BST>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BSTOptions = BinaryTreeOptions & { - comparator?: Comparator; + extractComparable?: (key: K) => Comparable isReverse?: boolean; } diff --git a/src/types/utils/utils.ts b/src/types/utils/utils.ts index 8896b20..b81f742 100644 --- a/src/types/utils/utils.ts +++ b/src/types/utils/utils.ts @@ -7,17 +7,23 @@ export type SpecifyOptional = Omit & Partial ComparablePrimitive | ComparableObject; - toString?: () => string; - } - | { - toString: () => string; - } -); +export interface BaseComparableObject { + [key: string]: unknown; +} + +export interface ValueComparableObject extends BaseComparableObject { + valueOf: () => ComparablePrimitive | ValueComparableObject; + toString?: () => string; +} + +export interface StringComparableObject extends BaseComparableObject { + toString: () => string; +} + +export type ComparableObject = ValueComparableObject | StringComparableObject; export type Comparable = ComparablePrimitive | Date | ComparableObject; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index cc6f5e5..cfc92f7 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -226,7 +226,8 @@ export const roundFixed = (num: number, digit: number = 10) => { */ function isPrimitiveComparable(value: unknown): value is ComparablePrimitive { const valueType = typeof value; - if (valueType === 'number') return !Number.isNaN(value); + if (valueType === 'number') return true; + // if (valueType === 'number') return !Number.isNaN(value); return valueType === 'bigint' || valueType === 'string' || valueType === 'boolean'; } @@ -274,7 +275,8 @@ export function isComparable(value: unknown, isForceObjectComparable = false): v if (isPrimitiveComparable(value)) return true; if (typeof value !== 'object') return false; - if (value instanceof Date) return !Number.isNaN(value.getTime()); + if (value instanceof Date) return true; + // if (value instanceof Date) return !Number.isNaN(value.getTime()); if (isForceObjectComparable) return true; const comparableValue = tryObjectToPrimitive(value); if (comparableValue === null || comparableValue === undefined) return false; diff --git a/test/performance/data-structures/binary-tree/avl-tree.test.ts b/test/performance/data-structures/binary-tree/avl-tree.test.ts index 1fb736f..3648f38 100644 --- a/test/performance/data-structures/binary-tree/avl-tree.test.ts +++ b/test/performance/data-structures/binary-tree/avl-tree.test.ts @@ -19,6 +19,9 @@ suite .add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => { for (let i = 0; i < randomArray.length; i++) avlTree.get(randomArray[i]); }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => { + for (let i = 0; i < randomArray.length; i++) avlTree.getNode(randomArray[i]); + }) .add(`${HUNDRED_THOUSAND.toLocaleString()} iterator`, () => { const entries = [...avlTree]; return entries.length === HUNDRED_THOUSAND; diff --git a/test/performance/data-structures/binary-tree/rb-tree.test.ts b/test/performance/data-structures/binary-tree/rb-tree.test.ts index f11867b..f46021c 100644 --- a/test/performance/data-structures/binary-tree/rb-tree.test.ts +++ b/test/performance/data-structures/binary-tree/rb-tree.test.ts @@ -23,9 +23,12 @@ suite .add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => { for (let i = 0; i < randomArray.length; i++) rbTree.get(randomArray[i]); }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => { + for (let i = 0; i < randomArray.length; i++) rbTree.getNode(randomArray[i]); + }) .add(`${HUNDRED_THOUSAND.toLocaleString()} node mode add randomly`, () => { rbTreeNodeMode.clear(); - for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]); + for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.add(randomArray[i]); }) .add(`${HUNDRED_THOUSAND.toLocaleString()} node mode get`, () => { for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.get(randomArray[i]); diff --git a/test/performance/reportor.ts b/test/performance/reportor.ts index f782345..f67ceab 100644 --- a/test/performance/reportor.ts +++ b/test/performance/reportor.ts @@ -8,6 +8,22 @@ import { PerformanceTest } from './types'; const args = process.argv.slice(2); const { GREEN, BOLD, END, YELLOW, GRAY, CYAN, BG_YELLOW } = ConsoleColor; +const isOnlyOrdered = true; +const runOrder = [ + 'heap', + 'avl-tree', + 'rb-tree', + 'doubly-linked-list', + 'directed-graph', + 'queue', + 'deque', + 'hash-map', + 'trie', + 'stack' + // 'singly-linked-list', + // 'priority-queue', + // 'binary-tree-overall' +]; const getRelativePath = (file: string) => { return path.relative(__dirname, file); @@ -80,7 +96,7 @@ const composeReport = () => { #json-to-html { padding: 0 10px 20px; } - + .json-to-html-label { font-size: 2rem; margin: 2rem 0 0 3px; @@ -92,19 +108,19 @@ const composeReport = () => { margin-top: 10px; font-size: 16px; } - + .content table th, .content table td { padding: 8px 12px; text-align: left; border: 1px solid #ddd; } - + .content table th { background-color: #f2f2f2; font-weight: bold; } - + .content table tr:nth-child(odd) { background-color: #ffffff; } @@ -188,46 +204,35 @@ function replaceMarkdownContent(startMarker: string, endMarker: string, newText: }); } -const order = [ - 'heap', - 'rb-tree', - 'queue', - 'deque', - 'hash-map', - 'trie', - 'avl-tree', - 'binary-tree-overall', - 'directed-graph', - 'doubly-linked-list', - 'singly-linked-list', - 'priority-queue', - 'stack' -]; +const sortedPerformanceTests = ( + isOnlyOrdered ? [...performanceTests].filter(test => runOrder.includes(test.testName)) : [...performanceTests] +).sort((a, b) => { + const indexA = runOrder.indexOf(a.testName); + const indexB = runOrder.indexOf(b.testName); -const sortedPerformanceTests = [...performanceTests].sort((a, b) => { - const indexA = order.indexOf(a.testName); - const indexB = order.indexOf(b.testName); - - // If both a and b are in the order, sort them according to their indices in the order. + // If both a and b are in the runOrder, sort them according to their indices in the runOrder. if (indexA !== -1 && indexB !== -1) { return indexA - indexB; } - // If there is only 'a' in the order, then place 'b' in front. + // If there is only 'a' in the runOrder, then place 'b' in front. if (indexA !== -1) { return 1; } - // If only b is in the order, then a should be placed before it. + // If only b is in the runOrder, then a should be placed before it. if (indexB !== -1) { return -1; } - // If neither a nor b are in order, keep their original order + // If neither a nor b are in runOrder, keep their original runOrder return 0; }); -console.log(`${GREEN} Found tests${END}: ${sortedPerformanceTests.map(test => test.testName)}`); +console.log(`${GREEN} Found tests (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`); +console.log( + `${GREEN} Running tests (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}` +); sortedPerformanceTests.forEach(item => { const { suite, testName, file } = item; @@ -245,22 +250,22 @@ sortedPerformanceTests.forEach(item => { return { 'test name': benchmark.name, 'time taken (ms)': numberFix(benchmark.times.period * 1000, 2), - 'executions per sec': numberFix(benchmark.hz, 2), + // 'executions per sec': numberFix(benchmark.hz, 2), // 'executed times': numberFix(benchmark.count, 0), - // 'sample mean (secs)': numberFix(benchmark.stats.mean, 2), + 'sample mean (secs)': numberFix(benchmark.stats.mean, 2), 'sample deviation': numberFix(benchmark.stats.deviation, 2) }; }); report[testName].testName = testName; - const isDone = completedCount === performanceTests.length; + const isDone = completedCount === sortedPerformanceTests.length; runTime = Number(runTime.toFixed(2)); const isTimeWarn = runTime > 120; console.log( // `Files: ${GREEN}${testFileCount}${END} `, // `Suites: ${GREEN}${performanceTests.length}${END} `, - `Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${performanceTests.length}${END}`, - `Time: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}` + `Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${sortedPerformanceTests.length}${END}`, + `Time Costs: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}` ); if (isDone) { composeReport(); diff --git a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts index d6cdbbf..65070b5 100644 --- a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts @@ -736,7 +736,7 @@ describe('AVLTree toEntryFn', () => { { obj: { id: 5 } } ]) ).toThrowError( - `When comparing object types, a custom comparator must be defined in the constructor's options parameter.` + `When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.` ); }); @@ -744,7 +744,7 @@ describe('AVLTree toEntryFn', () => { const tree = new AVLTreeMultiMap<{ obj: { id: number } }, number>( [{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }], { - comparator: (a, b) => a.obj.id - b.obj.id + extractComparable: key => key.obj.id } ); diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index 803b6fe..b62eb6e 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -103,9 +103,9 @@ describe('BinaryTree addMany', () => { [undefined, 22, 44, 33] ); expect(tree.get(2)).toBe(22); - expect(tree.get(tree.getNodeByKey(3))).toBe(33); - expect(tree.get(tree.getNodeByKey(4))).toBe(44); - expect(tree.get(tree.getNodeByKey(1))).toBe(1); + expect(tree.get(tree.getNode(3))).toBe(33); + expect(tree.get(tree.getNode(4))).toBe(44); + expect(tree.get(tree.getNode(1))).toBe(1); }); it('should addMany undefined and null', () => { @@ -349,7 +349,7 @@ describe('BinaryTree', () => { expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true); expect(tree.getNodes(2, false, null)).toEqual([]); expect(tree.getNodes(undefined)).toEqual([]); - expect(tree.getNodes(tree.getNodeByKey(2), false, tree.root)).toEqual([tree.getNodeByKey(2)]); + expect(tree.getNodes(tree.getNode(2), false, tree.root)).toEqual([tree.getNode(2)]); }); describe('should isKey', () => { @@ -362,9 +362,9 @@ describe('BinaryTree', () => { expect(tree.isKey(-Infinity)).toBe(true); }); - it('NaN should not be a key', () => { - expect(tree.isKey(NaN)).toBe(false); - }); + // it('NaN should not be a key', () => { + // expect(tree.isKey(NaN)).toBe(false); + // }); it('strings should be a key', () => { expect(tree.isKey('hello')).toBe(true); @@ -400,9 +400,9 @@ describe('BinaryTree', () => { expect(tree.isKey(new Date('2024-01-01'))).toBe(true); }); - it('invalid Date objects should not be a key', () => { - expect(tree.isKey(new Date('invalid'))).toBe(false); - }); + // it('invalid Date objects should not be a key', () => { + // expect(tree.isKey(new Date('invalid'))).toBe(false); + // }); }); describe('arrays', () => { diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 0c510b9..82ab088 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -974,7 +974,7 @@ describe('BST operations test recursively', () => { if (isTestStackOverflow) { it('should getLeftMost', () => { - const bst = new BST([], { comparator: (a, b) => b - a }); + const bst = new BST([], { extractComparable: key => key }); for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i); expect(() => { @@ -1009,7 +1009,7 @@ describe('BST isBST', function () { it('isBST when variant is Max', () => { const bst = new BST([1, 2, 3, 9, 8, 5, 6, 7, 4], { - comparator: (a, b) => b - a + isReverse: true }); bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]); expect(bst.isBST()).toBe(true); @@ -1550,10 +1550,12 @@ describe('classic use', () => { // Test case for finding elements in a given range it('@example Find elements in a range', () => { - const bst = new BST([10, 5, 15, 3, 7, 12, 18], { isReverse: true }); + const bst = new BST([10, 5, 15, 3, 7, 12, 18]); expect(bst.search(new Range(5, 10))).toEqual([10, 5, 7]); - expect(bst.search(new Range(4, 12))).toEqual([10, 5, 7, 12]); + expect(bst.search(new Range(4, 12))).toEqual([10, 12, 5, 7]); + expect(bst.search(new Range(4, 12, true, false))).toEqual([10, 5, 7]); expect(bst.search(new Range(15, 20))).toEqual([15, 18]); + expect(bst.search(new Range(15, 20, false))).toEqual([18]); }); // Test case for Huffman coding simulation diff --git a/test/unit/data-structures/binary-tree/overall.test.ts b/test/unit/data-structures/binary-tree/overall.test.ts index 5e7579c..4f9b858 100644 --- a/test/unit/data-structures/binary-tree/overall.test.ts +++ b/test/unit/data-structures/binary-tree/overall.test.ts @@ -58,11 +58,7 @@ describe('Overall BinaryTree Test', () => { it('Should clone a BST works fine', () => { const bst = new BST([3, 6, 7, 1, 9], { iterationType: 'RECURSIVE', - comparator: (a, b) => { - if (a > b) return -1; - if (a < b) return 1; - return 0; - } + isReverse: true }); expect(bst.size).toBe(5); expect(bst.root?.key).toBe(6); @@ -70,7 +66,7 @@ describe('Overall BinaryTree Test', () => { expect(bst.root?.left?.right?.key).toBe(7); expect(bst.root?.right?.key).toBe(3); expect(bst.root?.right?.right?.key).toBe(1); - expect(bst.getNodeByKey(9)?.right?.key).toBe(7); + expect(bst.getNode(9)?.right?.key).toBe(7); expect(bst.getHeight()).toBe(2); expect(bst.has(9)).toBe(true); expect(bst.has(7)).toBe(true); @@ -81,7 +77,7 @@ describe('Overall BinaryTree Test', () => { expect(bst.root?.left?.key).toBe(9); expect(bst.root?.right?.key).toBe(3); expect(bst.root?.right?.right?.key).toBe(1); - expect(bst.getNodeByKey(6)?.left?.key).toBe(9); + expect(bst.getNode(6)?.left?.key).toBe(9); expect(bst.getHeight()).toBe(2); expect(bst.has(9)).toBe(true); expect(bst.has(7)).toBe(false); @@ -92,7 +88,7 @@ describe('Overall BinaryTree Test', () => { expect(clonedBST.root?.left?.key).toBe(9); expect(clonedBST.root?.right?.key).toBe(3); expect(clonedBST.root?.right?.right?.key).toBe(1); - expect(clonedBST.getNodeByKey(6)?.left?.key).toBe(9); + expect(clonedBST.getNode(6)?.left?.key).toBe(9); expect(clonedBST.getHeight()).toBe(2); expect(clonedBST.has(9)).toBe(true); expect(clonedBST.has(7)).toBe(false); @@ -102,11 +98,7 @@ describe('Overall BinaryTree Test', () => { it('Should clone a AVLTree works fine', () => { const avl = new AVLTree([3, 6, 7, 1, 9], { iterationType: 'RECURSIVE', - comparator: (a, b) => { - if (a > b) return -1; - if (a < b) return 1; - return 0; - } + isReverse: true }); expect(avl.size).toBe(5); avl.add(2); @@ -117,7 +109,7 @@ describe('Overall BinaryTree Test', () => { expect(avl.root?.left?.left?.key).toBe(9); expect(avl.root?.right?.key).toBe(1); expect(avl.root?.right?.left?.key).toBe(2); - expect(avl.getNodeByKey(7)?.left?.key).toBe(9); + expect(avl.getNode(7)?.left?.key).toBe(9); expect(avl.getHeight()).toBe(3); expect(avl.has(9)).toBe(true); expect(avl.has(7)).toBe(true); @@ -128,7 +120,7 @@ describe('Overall BinaryTree Test', () => { expect(avl.root?.left?.key).toBe(5); expect(avl.root?.right?.key).toBe(1); expect(avl.root?.right?.left?.key).toBe(2); - expect(avl.getNodeByKey(6)?.left?.key).toBe(undefined); + expect(avl.getNode(6)?.left?.key).toBe(undefined); expect(avl.getHeight()).toBe(3); expect(avl.has(9)).toBe(true); expect(avl.has(7)).toBe(false); @@ -139,7 +131,7 @@ describe('Overall BinaryTree Test', () => { expect(clonedAVL.root?.left?.key).toBe(5); expect(clonedAVL.root?.right?.key).toBe(1); expect(clonedAVL.root?.right?.left?.key).toBe(2); - expect(clonedAVL.getNodeByKey(6)?.left?.key).toBe(undefined); + expect(clonedAVL.getNode(6)?.left?.key).toBe(undefined); expect(clonedAVL.getHeight()).toBe(3); expect(clonedAVL.has(9)).toBe(true); expect(clonedAVL.has(7)).toBe(false); @@ -162,7 +154,7 @@ describe('Overall BinaryTree Test', () => { expect(tmm.root?.left?.left?.key).toBe(NaN); expect(tmm.root?.right?.key).toBe(7); expect(tmm.root?.right?.left?.key).toBe(5); - expect(tmm.getNodeByKey(7)?.left?.key).toBe(5); + expect(tmm.getNode(7)?.left?.key).toBe(5); expect(tmm.getHeight()).toBe(3); expect(tmm.has(9)).toBe(true); expect(tmm.has(7)).toBe(true); @@ -174,7 +166,7 @@ describe('Overall BinaryTree Test', () => { expect(tmm.root?.left?.key).toBe(1); expect(tmm.root?.right?.key).toBe(9); expect(tmm.root?.right?.left?.key).toBe(5); - expect(tmm.getNodeByKey(6)?.left?.key).toBe(NaN); + expect(tmm.getNode(6)?.left?.key).toBe(NaN); expect(tmm.getHeight()).toBe(3); expect(tmm.has(9)).toBe(true); expect(tmm.has(7)).toBe(false); @@ -187,7 +179,7 @@ describe('Overall BinaryTree Test', () => { expect(clonedTMM.root?.left?.key).toBe(1); expect(clonedTMM.root?.right?.key).toBe(5); expect(clonedTMM.root?.right?.left?.key).toBe(4); - expect(clonedTMM.getNodeByKey(6)?.left?.key).toBe(NaN); + expect(clonedTMM.getNode(6)?.left?.key).toBe(NaN); expect(clonedTMM.getHeight()).toBe(3); expect(clonedTMM.has(9)).toBe(true); expect(clonedTMM.has(7)).toBe(false); @@ -209,7 +201,7 @@ describe('Overall BinaryTree Test', () => { expect(rbTree.root?.left?.left?.key).toBe(NaN); expect(rbTree.root?.right?.key).toBe(7); expect(rbTree.root?.right?.left?.key).toBe(5); - expect(rbTree.getNodeByKey(7)?.left?.key).toBe(5); + expect(rbTree.getNode(7)?.left?.key).toBe(5); expect(rbTree.getHeight()).toBe(3); expect(rbTree.has(9)).toBe(true); expect(rbTree.has(7)).toBe(true); @@ -220,7 +212,7 @@ describe('Overall BinaryTree Test', () => { expect(rbTree.root?.left?.key).toBe(1); expect(rbTree.root?.right?.key).toBe(9); expect(rbTree.root?.right?.left?.key).toBe(5); - expect(rbTree.getNodeByKey(6)?.left?.key).toBe(NaN); + expect(rbTree.getNode(6)?.left?.key).toBe(NaN); expect(rbTree.getHeight()).toBe(3); expect(rbTree.has(9)).toBe(true); expect(rbTree.has(7)).toBe(false); @@ -232,7 +224,7 @@ describe('Overall BinaryTree Test', () => { expect(clonedRbTree.root?.left?.key).toBe(1); expect(clonedRbTree.root?.right?.key).toBe(5); expect(clonedRbTree.root?.right?.left?.key).toBe(4); - expect(clonedRbTree.getNodeByKey(6)?.left?.key).toBe(NaN); + expect(clonedRbTree.getNode(6)?.left?.key).toBe(NaN); expect(clonedRbTree.getHeight()).toBe(3); expect(clonedRbTree.has(9)).toBe(true); expect(clonedRbTree.has(7)).toBe(false); diff --git a/test/unit/utils/utils.test.ts b/test/unit/utils/utils.test.ts index a0319e0..d3a3f12 100644 --- a/test/unit/utils/utils.test.ts +++ b/test/unit/utils/utils.test.ts @@ -16,9 +16,9 @@ describe('isComparable', () => { expect(isComparable(-Infinity)).toBe(true); }); - it('NaN should not be comparable', () => { - expect(isComparable(NaN)).toBe(false); - }); + // it('NaN should not be comparable', () => { + // expect(isComparable(NaN)).toBe(false); + // }); it('strings should be comparable', () => { expect(isComparable('hello')).toBe(true); @@ -54,9 +54,9 @@ describe('isComparable', () => { expect(isComparable(new Date('2024-01-01'))).toBe(true); }); - it('invalid Date objects should not be comparable', () => { - expect(isComparable(new Date('invalid'))).toBe(false); - }); + // it('invalid Date objects should not be comparable', () => { + // expect(isComparable(new Date('invalid'))).toBe(false); + // }); }); describe('arrays', () => {