diff --git a/.prettierignore b/.prettierignore index 3ad4bf6..0f7504b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,6 @@ src/types/data-structures/binary-tree/binary-tree.ts src/types/data-structures/binary-tree/bst.ts src/types/data-structures/binary-tree/avl-tree.ts -src/types/data-structures/binary-tree/rb-tree.ts +src/types/data-structures/binary-tree/red-black-tree.ts src/types/data-structures/binary-tree/tree-multi-map.ts src/types/data-structures/binary-tree/avl-tree-multi-map.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index dd63c42..fc1be59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - [Semantic Versioning](https://semver.org/spec/v2.0.0.html) - [`auto-changelog`](https://github.com/CookPete/auto-changelog) -## [v1.54.1](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming) +## [v1.54.2](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming) ### Changes diff --git a/LICENSE b/LICENSE index ef3b183..6f2894f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Tyler Zeng +Copyright (c) 2022 Pablo Zeng Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index eb15c76..e189a0c 100644 --- a/README.md +++ b/README.md @@ -830,37 +830,28 @@ Version 11.7.9 ***Our performance testing is conducted directly on the TypeScript source code. The actual performance of the compiled JavaScript code is generally 3 times higher. We have compared it with C++, and it is only 30% slower than C++.*** +Try it [on gitpod](https://gitpod.io#snapshot/93383de4-ca4c-4854-8c80-4359e681a96f) + +Just run +```shell +pnpm perf:rbtree +``` + +```html +1,000,000 add randomly: 1.367s +1,000,000 add: 374.859ms +1,000,000 get: 8.025ms +1,000,000 getNode: 1.293s +``` + + [//]: # (No deletion!!! Start of Replace Section)
-
heap
-
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)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
+
test nametime taken (ms)sample mean (secs)sample deviation
100,000 add randomly526.460.530.01
100,000 add474.380.470.00
100,000 get0.727.17e-43.47e-4
100,000 getNode252.580.250.01
100,000 iterator24.840.020.00
100,000 add & delete orderly717.480.720.02
100,000 add & delete randomly898.450.900.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)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
-
-
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
-
-
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)sample mean (secs)sample deviation
1,000,000 push43.180.040.01
1,000,000 push & pop48.400.050.02
+
red-black-tree
+
test nametime taken (ms)sample mean (secs)sample deviation
100,000 add randomly144.490.140.01
100,000 add190.530.190.00
100,000 get0.919.14e-42.11e-4
100,000 getNode323.660.320.00
100,000 node mode add randomly139.710.140.00
100,000 node mode get322.390.327.58e-4
100,000 iterator27.070.030.00
100,000 add & delete orderly349.170.350.00
100,000 add & delete randomly482.610.480.01
[//]: # (No deletion!!! End of Replace Section) diff --git a/README_zh-CN.md b/README_zh-CN.md index b126f34..002c633 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -1161,7 +1161,7 @@ avl2.print();
bst
test nametime taken (ms)executions per secsample deviation
10,000 add randomly55.0418.170.01
10,000 add & delete randomly129.857.700.01
10,000 addMany50.4019.840.01
10,000 get63.3915.780.01
-
rb-tree
+
red-black-tree
test nametime taken (ms)executions per secsample deviation
100,000 add113.258.830.02
100,000 add & delete randomly305.283.280.03
100,000 getNode73.2013.660.03
100,000 add & iterator159.806.260.06
comparison
diff --git a/package-lock.json b/package-lock.json index a12d272..a5ddd90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "data-structure-typed", - "version": "1.54.1", + "version": "1.54.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-structure-typed", - "version": "1.54.1", + "version": "1.54.2", "license": "MIT", "devDependencies": { "@eslint/compat": "^1.2.2", @@ -19,11 +19,11 @@ "@typescript-eslint/eslint-plugin": "^8.12.1", "@typescript-eslint/parser": "^8.12.1", "auto-changelog": "^2.5.0", - "avl-tree-typed": "^1.54.0", + "avl-tree-typed": "^1.54.1", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.54.0", - "bst-typed": "^1.54.0", - "data-structure-typed": "^1.54.0", + "binary-tree-typed": "^1.54.1", + "bst-typed": "^1.54.1", + "data-structure-typed": "^1.54.2", "dependency-cruiser": "^16.5.0", "doctoc": "^2.2.1", "eslint": "^9.13.0", @@ -32,7 +32,7 @@ "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", "fast-glob": "^3.3.2", - "heap-typed": "^1.54.0", + "heap-typed": "^1.54.1", "istanbul-badges-readme": "^1.9.0", "jest": "^29.7.0", "js-sdsl": "^4.4.2", @@ -4083,9 +4083,9 @@ } }, "node_modules/data-structure-typed": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.54.1.tgz", - "integrity": "sha512-N6KmfDASsj4rHQdAlO/1tRQdXZri9waQz/AAFJtd76dHx5lIie0ldK5R2Co+CqG2KHxGNf8M8HlQtNUXGgFGoQ==", + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.54.2.tgz", + "integrity": "sha512-H4Ct6XqKsGk7O6/6mb9MHsZdrp5fAYHTgv2Tb+LrbTHnbenKu2ZfM0wP7fbrkhrRPzCFFvZKKDTfSatgykolmw==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index e9aebc7..6265cc1 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "gen:examples": "ts-node scripts/testToExample.ts", "test:in-band": "jest --runInBand", "test": "npm run test:in-band", - "test:integration": "npm run update:subs && jest --config jest.integration.config.js && tsc test/integration/compile.ts", + "test:integration": "npm run update:subs && jest --config jest.integration.config.js && tsc test/integration/compile.ts && node test/integration/compile.mjs", "test:perf": "npm run build:cjs && npm run build:esm && ts-node test/performance/reportor.ts", "check": "tsc --noEmit", "check:circular-refs": "dependency-cruiser src", @@ -65,11 +65,11 @@ "@typescript-eslint/eslint-plugin": "^8.12.1", "@typescript-eslint/parser": "^8.12.1", "auto-changelog": "^2.5.0", - "avl-tree-typed": "^1.54.0", + "avl-tree-typed": "^1.54.1", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.54.0", - "bst-typed": "^1.54.0", - "data-structure-typed": "^1.54.0", + "binary-tree-typed": "^1.54.1", + "bst-typed": "^1.54.1", + "data-structure-typed": "^1.54.2", "dependency-cruiser": "^16.5.0", "doctoc": "^2.2.1", "eslint": "^9.13.0", @@ -78,7 +78,7 @@ "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", "fast-glob": "^3.3.2", - "heap-typed": "^1.54.0", + "heap-typed": "^1.54.1", "istanbul-badges-readme": "^1.9.0", "jest": "^29.7.0", "js-sdsl": "^4.4.2", diff --git a/src/utils/utils.ts b/src/utils/utils.ts index cfc92f7..7bf3c0e 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,8 +1,8 @@ /** * data-structure-typed * - * @author Tyler Zeng - * @copyright Copyright (c) 2022 Tyler Zeng + * @author Pablo Zeng + * @copyright Copyright (c) 2022 Pablo Zeng * @license MIT License */ import type { Comparable, ComparablePrimitive, Thunk, ToThunkFn, TrlAsyncFn, TrlFn } from '../types'; diff --git a/test/integration/compile.mjs b/test/integration/compile.mjs index 4279d64..9be4345 100644 --- a/test/integration/compile.mjs +++ b/test/integration/compile.mjs @@ -13,28 +13,28 @@ const entries = [ [8, '8'] ]; const queue = new Queue(orgArr); -queue.print(); + // [6, 1, 2, 7, 5, 3, 4, 9, 8] const deque = new Deque(orgArr); -deque.print(); + // [6, 1, 2, 7, 5, 3, 4, 9, 8] const sList = new SinglyLinkedList(orgArr); -sList.print(); + // [6, 1, 2, 7, 5, 3, 4, 9, 8] const dList = new DoublyLinkedList(orgArr); -dList.print(); + // [6, 1, 2, 7, 5, 3, 4, 9, 8] const stack = new Stack(orgArr); -stack.print(); + // [6, 1, 2, 7, 5, 3, 4, 9, 8] const minHeap = new MinHeap(orgArr); -minHeap.print(); + // [1, 5, 2, 7, 6, 3, 4, 9, 8] const maxPQ = new MaxPriorityQueue(orgArr); -maxPQ.print(); + // [9, 8, 4, 7, 5, 2, 3, 1, 6] const biTree = new BinaryTree(entries); -biTree.print(); + // ___6___ // / \ // ___1_ _2_ @@ -43,7 +43,7 @@ biTree.print(); // / \ // 9 8 const bst = new BST(entries); -bst.print(); + // _____5___ // / \ // _2_ _7_ @@ -52,7 +52,7 @@ bst.print(); // \ \ // 4 9 const rbTree = new RedBlackTree(entries); -rbTree.print(); + // ___4___ // / \ // _2_ _6___ @@ -61,7 +61,7 @@ rbTree.print(); // / \ // 7 9 const avl = new AVLTree(entries); -avl.print(); + // ___4___ // / \ // _2_ _6___ @@ -70,7 +70,7 @@ avl.print(); // / \ // 7 9 const treeMulti = new TreeMultiMap(entries); -treeMulti.print(); + // ___4___ // / \ // _2_ _6___ @@ -79,10 +79,10 @@ treeMulti.print(); // / \ // 7 9 const hm = new HashMap(entries); -hm.print(); + // [[6, "6"], [1, "1"], [2, "2"], [7, "7"], [5, "5"], [3, "3"], [4, "4"], [9, "9"], [8, "8"]] const rbTreeH = new RedBlackTree(hm); -rbTreeH.print(); + // ___4___ // / \ // _2_ _6___ @@ -91,10 +91,10 @@ rbTreeH.print(); // / \ // 7 9 const pq = new MinPriorityQueue(orgArr); -pq.print(); + // [1, 5, 2, 7, 6, 3, 4, 9, 8] const bst1 = new BST(pq); -bst1.print(); + // _____5___ // / \ // _2_ _7_ @@ -103,10 +103,10 @@ bst1.print(); // \ \ // 4 9 const dq1 = new Deque(orgArr); -dq1.print(); + // [6, 1, 2, 7, 5, 3, 4, 9, 8] const rbTree1 = new RedBlackTree(dq1); -rbTree1.print(); + // _____5___ // / \ // _2___ _7___ @@ -115,13 +115,13 @@ rbTree1.print(); // / / // 3 8 const trie2 = new Trie(orgStrArr); -trie2.print(); + // ['trie', 'trial', 'triangle', 'trick', 'trip', 'tree', 'trend', 'track', 'trace', 'transmit'] const heap2 = new Heap(trie2, { comparator: (a, b) => Number(a) - Number(b) }); -heap2.print(); + // ['transmit', 'trace', 'tree', 'trend', 'track', 'trial', 'trip', 'trie', 'trick', 'triangle'] const dq2 = new Deque(heap2); -dq2.print(); + // ['transmit', 'trace', 'tree', 'trend', 'track', 'trial', 'trip', 'trie', 'trick', 'triangle'] const entries2 = dq2.map((el, i) => [i, el]); const avl2 = new AVLTree(entries2); diff --git a/test/performance/data-structures/binary-tree/avl-tree.test.js b/test/performance/data-structures/binary-tree/avl-tree.test.js deleted file mode 100644 index 4120887..0000000 --- a/test/performance/data-structures/binary-tree/avl-tree.test.js +++ /dev/null @@ -1,45 +0,0 @@ -import { AVLTree } from '../../../../'; -import * as Benchmark from 'benchmark'; -// import { getRandomIntArray, magnitude } from '../../../utils'; -// const suite = new Benchmark.Suite(); -// const avlTree = new AVLTree(); -// const { HUNDRED_THOUSAND } = magnitude; -// const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true); -// suite -// .add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => { -// avlTree.clear(); -// for (let i = 0; i < randomArray.length; i++) -// avlTree.add(randomArray[i]); -// }) -// .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => { -// avlTree.clear(); -// for (let i = 0; i < randomArray.length; i++) -// avlTree.add(i); -// }) -// .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; -// }) -// .add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete orderly`, () => { -// avlTree.clear(); -// for (let i = 0; i < randomArray.length; i++) -// avlTree.add(i); -// for (let i = 0; i < randomArray.length; i++) -// avlTree.delete(i); -// }) -// .add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete randomly`, () => { -// avlTree.clear(); -// for (let i = 0; i < randomArray.length; i++) -// avlTree.add(randomArray[i]); -// for (let i = 0; i < randomArray.length; i++) -// avlTree.delete(randomArray[i]); -// }); -// export { suite }; diff --git a/test/performance/data-structures/binary-tree/avl-tree.test.mjs b/test/performance/data-structures/binary-tree/avl-tree.test.mjs new file mode 100644 index 0000000..2c8d098 --- /dev/null +++ b/test/performance/data-structures/binary-tree/avl-tree.test.mjs @@ -0,0 +1,71 @@ +import { AVLTree } from 'data-structure-typed'; +import Benchmark from 'benchmark'; + +const magnitude = { + THOUSAND: 1000, + TEN_THOUSAND: 10000, + HUNDRED_THOUSAND: 100000, + MILLION: 1000000, + TEN_MILLION: 10000000, + BILLION: 100000000 +}; + +function getRandomIntArray(length = 1000, min = -1000, max = 1000, unique = true) { + if (unique) { + if (max - min + 1 < length) { + throw new Error('Range too small for unique values with the specified length'); + } + const allNumbers = Array.from({ length: max - min + 1 }, (_, i) => i + min); + for (let i = allNumbers.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [allNumbers[i], allNumbers[j]] = [allNumbers[j], allNumbers[i]]; + } + return allNumbers.slice(0, length); + } + else { + return Array.from({ length }, () => Math.floor(Math.random() * (max - min + 1)) + min); + } +} + +const suite = new Benchmark.Suite(); +const avlTree = new AVLTree(); +const { HUNDRED_THOUSAND } = magnitude; +const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true); +suite + .add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => { + avlTree.clear(); + for (let i = 0; i < randomArray.length; i++) + avlTree.add(randomArray[i]); + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => { + avlTree.clear(); + for (let i = 0; i < randomArray.length; i++) + avlTree.add(i); + }) + .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; + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete orderly`, () => { + avlTree.clear(); + for (let i = 0; i < randomArray.length; i++) + avlTree.add(i); + for (let i = 0; i < randomArray.length; i++) + avlTree.delete(i); + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete randomly`, () => { + avlTree.clear(); + for (let i = 0; i < randomArray.length; i++) + avlTree.add(randomArray[i]); + for (let i = 0; i < randomArray.length; i++) + avlTree.delete(randomArray[i]); + }); +export { suite }; diff --git a/test/performance/data-structures/binary-tree/red-black-tree.test.mjs b/test/performance/data-structures/binary-tree/red-black-tree.test.mjs new file mode 100644 index 0000000..ea1ee98 --- /dev/null +++ b/test/performance/data-structures/binary-tree/red-black-tree.test.mjs @@ -0,0 +1,81 @@ +import { RedBlackTree } from 'data-structure-typed'; +import Benchmark from 'benchmark'; + +const magnitude = { + THOUSAND: 1000, + TEN_THOUSAND: 10000, + HUNDRED_THOUSAND: 100000, + MILLION: 1000000, + TEN_MILLION: 10000000, + BILLION: 100000000 +}; + +function getRandomIntArray(length = 1000, min = -1000, max = 1000, unique = true) { + if (unique) { + if (max - min + 1 < length) { + throw new Error('Range too small for unique values with the specified length'); + } + const allNumbers = Array.from({ length: max - min + 1 }, (_, i) => i + min); + for (let i = allNumbers.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [allNumbers[i], allNumbers[j]] = [allNumbers[j], allNumbers[i]]; + } + return allNumbers.slice(0, length); + } + else { + return Array.from({ length }, () => Math.floor(Math.random() * (max - min + 1)) + min); + } +} +const suite = new Benchmark.Suite(); +const rbTree = new RedBlackTree(); +const rbTreeNodeMode = new RedBlackTree([], { isMapMode: false }); +const { HUNDRED_THOUSAND } = magnitude; +const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true); +suite + .add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => { + rbTree.clear(); + for (let i = 0; i < randomArray.length; i++) + rbTree.add(randomArray[i]); + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => { + rbTree.clear(); + for (let i = 0; i < randomArray.length; i++) + rbTree.add(i); + }) + .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++) + rbTreeNodeMode.add(randomArray[i]); + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} node mode get`, () => { + for (let i = 0; i < randomArray.length; i++) + rbTreeNodeMode.get(randomArray[i]); + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} iterator`, () => { + const entries = [...rbTree]; + return entries.length === HUNDRED_THOUSAND; + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete orderly`, () => { + rbTree.clear(); + for (let i = 0; i < randomArray.length; i++) + rbTree.add(i); + for (let i = 0; i < randomArray.length; i++) + rbTree.delete(i); + }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete randomly`, () => { + rbTree.clear(); + for (let i = 0; i < randomArray.length; i++) + rbTree.add(randomArray[i]); + for (let i = 0; i < randomArray.length; i++) + rbTree.delete(randomArray[i]); + }); + +export { suite }; diff --git a/test/performance/data-structures/binary-tree/rb-tree.test.ts b/test/performance/data-structures/binary-tree/red-black-tree.test.ts similarity index 100% rename from test/performance/data-structures/binary-tree/rb-tree.test.ts rename to test/performance/data-structures/binary-tree/red-black-tree.test.ts diff --git a/test/performance/reportor.js b/test/performance/reportor.mjs similarity index 54% rename from test/performance/reportor.js rename to test/performance/reportor.mjs index e55d69e..90dc2fc 100644 --- a/test/performance/reportor.js +++ b/test/performance/reportor.mjs @@ -1,14 +1,268 @@ import * as path from 'path'; import * as fs from 'fs'; -import * as fastGlob from 'fast-glob'; -import { ConsoleColor, numberFix, render } from '../utils'; +import fastGlob from 'fast-glob'; +import { fileURLToPath } from 'url'; + +const isNumber = (value) => { + return typeof value === 'number'; +}; +const isString = (value) => { + return typeof value === 'string'; +}; +const isBoolean = (value) => { + return typeof value === 'boolean'; +}; +const isDate = (value) => { + return value instanceof Date; +}; +const isNull = (value) => { + return value === null; +}; +const isUndefined = (value) => { + return typeof value === 'undefined'; +}; +const isFunction = (value) => { + return typeof value === 'function'; +}; +const isObject = (value) => { + return typeof value === 'object'; +}; +const isArray = (value) => { + return Array.isArray(value); +}; +const isEqual = (objA, objB) => { + if (objA === objB) { + return true; + } + if (typeof objA !== 'object' || typeof objB !== 'object' || objA === null || objB === null) { + return false; + } + const keysA = Object.keys(objA); + const keysB = Object.keys(objB); + if (keysA.length !== keysB.length) { + return false; + } + for (const key of keysA) { + if (!keysB.includes(key)) { + return false; + } + if (!isEqual(objA[key], objB[key])) { + return false; + } + } + return true; +}; + +function toggleJS(options) { + if (options === null || options === void 0 ? void 0 : options.plainHtml) { + return ''; + } + else { + return 'onclick="json-to-html.toggleVisibility(this);return false"'; + } +} +function makeLabelDiv(options, level, keyName, datatype) { + if (typeof keyName === 'number') { + return `
${keyName} 
`; + } + else if (typeof keyName === 'string') { + if (datatype === 'array') { + return `
${keyName}
`; + } + else if (datatype === 'object') { + return `
${keyName}:
`; + } + else { + return `
${keyName}:
`; + } + } + else { + return ''; + } +} +function getContentClass(keyName) { + if (typeof keyName === 'string') { + return 'content'; + } + else { + return ''; + } +} +function isPlainObject(val) { + let lastKey; + let lastOwnKey; + for (const key in val) { + if (val.hasOwnProperty(key)) { + lastOwnKey = key; + } + } + for (const key in val) { + lastKey = key; + } + return lastOwnKey === lastKey; +} +function isLeafValue(val) { + return (isNumber(val) || + isString(val) || + isBoolean(val) || + isDate(val) || + isNull(val) || + isUndefined(val) || + isNaN(val) || + isFunction(val) || + !isPlainObject(val)); +} +function isLeafObject(obj) { + if (!isObject(obj)) { + return false; + } + for (const key in obj) { + const val = obj[key]; + if (!isLeafValue(val)) { + return false; + } + } + return true; +} +function isTable(arr) { + if (!isArray(arr)) { + return false; + } + if (arr.length === 0 || !isObject(arr[0])) { + return false; + } + else { + let nonCompliant = arr.find(row => !isLeafObject(row)); + if (nonCompliant) { + return false; + } + else { + const cols = Object.keys(arr[0]); + nonCompliant = arr.find((row) => !isEqual(cols, Object.keys(row))); + return !nonCompliant; + } + } +} +function drawTable(arr) { + function drawRow(headers, rowObj) { + return '' + headers.map(header => rowObj[header]).join('') + ''; + } + const cols = Object.keys(arr[0]); + const content = arr.map(rowObj => drawRow(cols, rowObj)); + const headingHtml = '' + cols.join('') + ''; + const contentHtml = '' + content.join('') + ''; + return '' + headingHtml + contentHtml + '
'; +} +function _render(name, data, options, level, altRow) { + const contentClass = getContentClass(name); + if (isArray(data)) { + const title = makeLabelDiv(options, level, `${name}`, 'array'); + let subs; + if (isTable(data)) { + subs = drawTable(data); + } + else { + subs = + "
" + + data + .map((val, idx) => _render(idx.toString(), val, options, level + 1, idx % 2)) + .join("
") + + '
'; + } + return `
+ ${title} +
${subs}
+
`; + } + else if (isLeafValue(data)) { + const title = makeLabelDiv(options, level, name); + if (isFunction(data)) { + return `${title}  -function() can't _render-`; + } + else if (!isPlainObject(data)) { + if (isFunction(data.toString)) { + return `${title}  ${data.toString()}`; + } + else { + return `${title}  -instance object, can't render-`; + } + } + else { + return `${title}  ${data}`; + } + } + else { + const title = makeLabelDiv(options, level, name, 'object'); + let count = 0; + const subs = '
' + + Object.entries(data) + .map(([key, val]) => _render(key, val, options, level + 1, count++ % 2)) + .join('
') + + '
'; + const inner = `
+ ${title} +
${subs}
+
`; + return `${level === 0 ? "
" : ''} + ${inner} + ${level === 0 ? '
' : ''}`; + } +} +export function render(name, json, options) { + return `${_render(name, json, options, 0, 0)}`; +} + + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +function numberFix(num, decimalPlaces) { + if (num > 10000 || num < 0.001) { + const [mantissa, exponent] = num.toExponential().split('e'); + const formattedMantissa = Number(mantissa).toFixed(decimalPlaces); + return `${formattedMantissa}e${exponent}`; + } else { + return num.toFixed(decimalPlaces); + } +} +const ConsoleColor = { + END: '\x1b[0m', + BOLD: '\x1b[1m', + DIM: '\x1b[2m', + ITALIC: '\x1b[3m', + UNDERLINE: '\x1b[4m', + INVERSE: '\x1b[7m', + STRIKETHROUGH: '\x1b[9m', + NO_BOLD: '\x1b[22m', + NO_ITALIC: '\x1b[23m', + NO_UNDERLINE: '\x1b[24m', + NO_INVERSE: '\x1b[27m', + NO_STRIKETHROUGH: '\x1b[29m', + BLACK: '\x1b[30m', + RED: '\x1b[31m', + GREEN: '\x1b[32m', + YELLOW: '\x1b[33m', + BLUE: '\x1b[34m', + MAGENTA: '\x1b[35m', + GRAY: '\x1b[90m', + CYAN: '\x1b[36m', + WHITE: '\x1b[37m', + BG_BLACK: '\x1b[40m', + BG_RED: '\x1b[41m', + BG_GREEN: '\x1b[42m', + BG_YELLOW: '\x1b[43m', + BG_BLUE: '\x1b[44m', + BG_MAGENTA: '\x1b[45m', + BG_CYAN: '\x1b[46m', + BG_WHITE: '\x1b[47m' +}; 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', + 'red-black-tree', 'doubly-linked-list', 'directed-graph', 'queue', @@ -24,6 +278,7 @@ const getRelativePath = file => { return path.relative(__dirname, file); }; const coloredLabeled = (label, file) => { + const relativeFilePath = getRelativePath(file); const directory = path.dirname(relativeFilePath); const fileName = path.basename(relativeFilePath); @@ -32,7 +287,8 @@ const coloredLabeled = (label, file) => { const parentDirectory = path.resolve(__dirname, '../..'); const reportDistPath = path.join(parentDirectory, 'benchmark'); const testDir = path.join(__dirname, 'data-structures'); -const allFiles = fastGlob.sync(path.join(testDir, '**', '*.test.js')); +let allFiles = fastGlob.sync(path.join(testDir, '**', '*.test.mjs')); + let testFiles; let isIndividual = false; if (args.length > 0) { @@ -50,9 +306,9 @@ if (args.length > 0) { const report = {}; let completedCount = 0; const performanceTests = []; -testFiles.forEach(file => { - const testName = path.basename(file, '.test.ts'); - const testFunction = require(file); +for (const file of testFiles) { + const testName = path.basename(file, '.test.mjs'); + const testFunction = await import(file); const { suite } = testFunction; if (suite) performanceTests.push({ @@ -60,7 +316,8 @@ testFiles.forEach(file => { suite, file }); -}); +} + const composeReport = () => { if (!fs.existsSync(reportDistPath)) fs.mkdirSync(reportDistPath, { diff --git a/test/performance/reportor.ts b/test/performance/reportor.ts index ebf63ff..8b6cac8 100644 --- a/test/performance/reportor.ts +++ b/test/performance/reportor.ts @@ -12,7 +12,7 @@ const isOnlyOrdered = true; const runOrder = [ 'heap', 'avl-tree', - 'rb-tree', + 'red-black-tree', 'doubly-linked-list', 'directed-graph', 'queue', diff --git a/test/utils/json2html.ts b/test/utils/json2html.ts index 4ffc152..c27ed2c 100644 --- a/test/utils/json2html.ts +++ b/test/utils/json2html.ts @@ -160,159 +160,5 @@ function _render(name: string, data: any, options: Json2htmlOptions, level: numb } export function render(name: string, json: any, options: Json2htmlOptions): string { - // return `${head}${_render('', json, options, 0, 0)}`; return `${_render(name, json, options, 0, 0)}`; } - -// const head = ` -// `;