data-structure-typed 1.54.1 → 1.54.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/LICENSE +2 -2
- package/README.md +14 -1
- package/README_zh-CN.md +1 -1
- package/benchmark/report.html +4 -1
- package/benchmark/report.json +76 -17
- package/dist/cjs/data-structures/binary-tree/avl-tree-counter.d.ts +21 -20
- package/dist/cjs/data-structures/binary-tree/avl-tree-counter.js +8 -7
- package/dist/cjs/data-structures/binary-tree/avl-tree-counter.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.d.ts +12 -12
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js +2 -2
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/avl-tree.d.ts +25 -21
- package/dist/cjs/data-structures/binary-tree/avl-tree.js +12 -8
- package/dist/cjs/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/binary-tree.d.ts +111 -225
- package/dist/cjs/data-structures/binary-tree/binary-tree.js +177 -144
- package/dist/cjs/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/bst.d.ts +59 -53
- package/dist/cjs/data-structures/binary-tree/bst.js +75 -119
- package/dist/cjs/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/red-black-tree.d.ts +18 -18
- package/dist/cjs/data-structures/binary-tree/red-black-tree.js +6 -6
- package/dist/cjs/data-structures/binary-tree/red-black-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/tree-counter.d.ts +19 -19
- package/dist/cjs/data-structures/binary-tree/tree-counter.js +12 -12
- package/dist/cjs/data-structures/binary-tree/tree-counter.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.d.ts +12 -12
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js +2 -2
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/cjs/types/data-structures/binary-tree/binary-tree.d.ts +1 -0
- package/dist/cjs/types/data-structures/binary-tree/bst.d.ts +1 -1
- package/dist/cjs/types/data-structures/binary-tree/index.d.ts +1 -1
- package/dist/cjs/types/data-structures/binary-tree/index.js +1 -1
- package/dist/cjs/types/data-structures/binary-tree/index.js.map +1 -1
- package/dist/cjs/types/data-structures/binary-tree/{rb-tree.js → red-black-tree.js} +1 -1
- package/dist/cjs/types/data-structures/binary-tree/red-black-tree.js.map +1 -0
- package/dist/cjs/types/data-structures/binary-tree/tree-counter.d.ts +1 -1
- package/dist/cjs/types/data-structures/binary-tree/tree-multi-map.d.ts +1 -1
- package/dist/cjs/utils/utils.d.ts +2 -2
- package/dist/esm/data-structures/binary-tree/avl-tree-counter.d.ts +21 -20
- package/dist/esm/data-structures/binary-tree/avl-tree-counter.js +9 -8
- package/dist/esm/data-structures/binary-tree/avl-tree-counter.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/avl-tree-multi-map.d.ts +12 -12
- package/dist/esm/data-structures/binary-tree/avl-tree-multi-map.js +3 -3
- package/dist/esm/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/avl-tree.d.ts +25 -21
- package/dist/esm/data-structures/binary-tree/avl-tree.js +13 -9
- package/dist/esm/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/binary-tree.d.ts +111 -225
- package/dist/esm/data-structures/binary-tree/binary-tree.js +181 -148
- package/dist/esm/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/bst.d.ts +59 -53
- package/dist/esm/data-structures/binary-tree/bst.js +76 -120
- package/dist/esm/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/red-black-tree.d.ts +18 -18
- package/dist/esm/data-structures/binary-tree/red-black-tree.js +7 -7
- package/dist/esm/data-structures/binary-tree/red-black-tree.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/tree-counter.d.ts +19 -19
- package/dist/esm/data-structures/binary-tree/tree-counter.js +13 -13
- package/dist/esm/data-structures/binary-tree/tree-counter.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/tree-multi-map.d.ts +12 -12
- package/dist/esm/data-structures/binary-tree/tree-multi-map.js +3 -3
- package/dist/esm/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/esm/types/data-structures/binary-tree/binary-tree.d.ts +1 -0
- package/dist/esm/types/data-structures/binary-tree/bst.d.ts +1 -1
- package/dist/esm/types/data-structures/binary-tree/index.d.ts +1 -1
- package/dist/esm/types/data-structures/binary-tree/index.js +1 -1
- package/dist/esm/types/data-structures/binary-tree/index.js.map +1 -1
- package/dist/esm/types/data-structures/binary-tree/red-black-tree.js +2 -0
- package/dist/esm/types/data-structures/binary-tree/red-black-tree.js.map +1 -0
- package/dist/esm/types/data-structures/binary-tree/tree-counter.d.ts +1 -1
- package/dist/esm/types/data-structures/binary-tree/tree-multi-map.d.ts +1 -1
- package/dist/esm/utils/utils.d.ts +2 -2
- package/dist/umd/data-structure-typed.js +296 -279
- package/dist/umd/data-structure-typed.min.js +5 -12
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +9 -14
- package/src/data-structures/binary-tree/avl-tree-counter.ts +30 -23
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +25 -15
- package/src/data-structures/binary-tree/avl-tree.ts +35 -29
- package/src/data-structures/binary-tree/binary-tree.ts +469 -252
- package/src/data-structures/binary-tree/bst.ts +141 -143
- package/src/data-structures/binary-tree/red-black-tree.ts +27 -35
- package/src/data-structures/binary-tree/tree-counter.ts +33 -27
- package/src/data-structures/binary-tree/tree-multi-map.ts +25 -17
- package/src/types/data-structures/binary-tree/binary-tree.ts +1 -0
- package/src/types/data-structures/binary-tree/bst.ts +1 -1
- package/src/types/data-structures/binary-tree/index.ts +1 -1
- package/src/types/data-structures/binary-tree/tree-counter.ts +1 -1
- package/src/types/data-structures/binary-tree/tree-multi-map.ts +1 -1
- package/src/utils/utils.ts +2 -2
- package/test/integration/compile.mjs +135 -0
- package/test/performance/data-structures/binary-tree/avl-tree.test.mjs +71 -0
- package/test/performance/data-structures/binary-tree/red-black-tree.test.mjs +81 -0
- package/test/performance/reportor.mjs +505 -0
- package/test/performance/reportor.ts +1 -1
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +7 -7
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +5 -6
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +72 -5
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +132 -82
- package/test/unit/data-structures/binary-tree/bst.test.ts +12 -12
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +4 -12
- package/test/unit/data-structures/binary-tree/tree-counter.test.ts +4 -4
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +6 -6
- package/test/utils/json2html.ts +0 -154
- package/dist/cjs/types/data-structures/binary-tree/rb-tree.js.map +0 -1
- package/dist/esm/types/data-structures/binary-tree/rb-tree.js +0 -2
- package/dist/esm/types/data-structures/binary-tree/rb-tree.js.map +0 -1
- /package/dist/cjs/types/data-structures/binary-tree/{rb-tree.d.ts → red-black-tree.d.ts} +0 -0
- /package/dist/esm/types/data-structures/binary-tree/{rb-tree.d.ts → red-black-tree.d.ts} +0 -0
- /package/src/types/data-structures/binary-tree/{rb-tree.ts → red-black-tree.ts} +0 -0
- /package/test/performance/data-structures/binary-tree/{rb-tree.test.ts → red-black-tree.test.ts} +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AVLTree } from 'data-structure-typed';
|
|
2
|
+
import Benchmark from 'benchmark';
|
|
3
|
+
|
|
4
|
+
const magnitude = {
|
|
5
|
+
THOUSAND: 1000,
|
|
6
|
+
TEN_THOUSAND: 10000,
|
|
7
|
+
HUNDRED_THOUSAND: 100000,
|
|
8
|
+
MILLION: 1000000,
|
|
9
|
+
TEN_MILLION: 10000000,
|
|
10
|
+
BILLION: 100000000
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function getRandomIntArray(length = 1000, min = -1000, max = 1000, unique = true) {
|
|
14
|
+
if (unique) {
|
|
15
|
+
if (max - min + 1 < length) {
|
|
16
|
+
throw new Error('Range too small for unique values with the specified length');
|
|
17
|
+
}
|
|
18
|
+
const allNumbers = Array.from({ length: max - min + 1 }, (_, i) => i + min);
|
|
19
|
+
for (let i = allNumbers.length - 1; i > 0; i--) {
|
|
20
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
21
|
+
[allNumbers[i], allNumbers[j]] = [allNumbers[j], allNumbers[i]];
|
|
22
|
+
}
|
|
23
|
+
return allNumbers.slice(0, length);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return Array.from({ length }, () => Math.floor(Math.random() * (max - min + 1)) + min);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const suite = new Benchmark.Suite();
|
|
31
|
+
const avlTree = new AVLTree();
|
|
32
|
+
const { HUNDRED_THOUSAND } = magnitude;
|
|
33
|
+
const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true);
|
|
34
|
+
suite
|
|
35
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => {
|
|
36
|
+
avlTree.clear();
|
|
37
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
38
|
+
avlTree.add(randomArray[i]);
|
|
39
|
+
})
|
|
40
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
|
41
|
+
avlTree.clear();
|
|
42
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
43
|
+
avlTree.add(i);
|
|
44
|
+
})
|
|
45
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
|
46
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
47
|
+
avlTree.get(randomArray[i]);
|
|
48
|
+
})
|
|
49
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
|
50
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
51
|
+
avlTree.getNode(randomArray[i]);
|
|
52
|
+
})
|
|
53
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} iterator`, () => {
|
|
54
|
+
const entries = [...avlTree];
|
|
55
|
+
return entries.length === HUNDRED_THOUSAND;
|
|
56
|
+
})
|
|
57
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete orderly`, () => {
|
|
58
|
+
avlTree.clear();
|
|
59
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
60
|
+
avlTree.add(i);
|
|
61
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
62
|
+
avlTree.delete(i);
|
|
63
|
+
})
|
|
64
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete randomly`, () => {
|
|
65
|
+
avlTree.clear();
|
|
66
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
67
|
+
avlTree.add(randomArray[i]);
|
|
68
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
69
|
+
avlTree.delete(randomArray[i]);
|
|
70
|
+
});
|
|
71
|
+
export { suite };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { RedBlackTree } from 'data-structure-typed';
|
|
2
|
+
import Benchmark from 'benchmark';
|
|
3
|
+
|
|
4
|
+
const magnitude = {
|
|
5
|
+
THOUSAND: 1000,
|
|
6
|
+
TEN_THOUSAND: 10000,
|
|
7
|
+
HUNDRED_THOUSAND: 100000,
|
|
8
|
+
MILLION: 1000000,
|
|
9
|
+
TEN_MILLION: 10000000,
|
|
10
|
+
BILLION: 100000000
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function getRandomIntArray(length = 1000, min = -1000, max = 1000, unique = true) {
|
|
14
|
+
if (unique) {
|
|
15
|
+
if (max - min + 1 < length) {
|
|
16
|
+
throw new Error('Range too small for unique values with the specified length');
|
|
17
|
+
}
|
|
18
|
+
const allNumbers = Array.from({ length: max - min + 1 }, (_, i) => i + min);
|
|
19
|
+
for (let i = allNumbers.length - 1; i > 0; i--) {
|
|
20
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
21
|
+
[allNumbers[i], allNumbers[j]] = [allNumbers[j], allNumbers[i]];
|
|
22
|
+
}
|
|
23
|
+
return allNumbers.slice(0, length);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return Array.from({ length }, () => Math.floor(Math.random() * (max - min + 1)) + min);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const suite = new Benchmark.Suite();
|
|
30
|
+
const rbTree = new RedBlackTree();
|
|
31
|
+
const rbTreeNodeMode = new RedBlackTree([], { isMapMode: false });
|
|
32
|
+
const { HUNDRED_THOUSAND } = magnitude;
|
|
33
|
+
const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true);
|
|
34
|
+
suite
|
|
35
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => {
|
|
36
|
+
rbTree.clear();
|
|
37
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
38
|
+
rbTree.add(randomArray[i]);
|
|
39
|
+
})
|
|
40
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
|
41
|
+
rbTree.clear();
|
|
42
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
43
|
+
rbTree.add(i);
|
|
44
|
+
})
|
|
45
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
|
46
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
47
|
+
rbTree.get(randomArray[i]);
|
|
48
|
+
})
|
|
49
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
|
50
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
51
|
+
rbTree.getNode(randomArray[i]);
|
|
52
|
+
})
|
|
53
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode add randomly`, () => {
|
|
54
|
+
rbTreeNodeMode.clear();
|
|
55
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
56
|
+
rbTreeNodeMode.add(randomArray[i]);
|
|
57
|
+
})
|
|
58
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode get`, () => {
|
|
59
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
60
|
+
rbTreeNodeMode.get(randomArray[i]);
|
|
61
|
+
})
|
|
62
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} iterator`, () => {
|
|
63
|
+
const entries = [...rbTree];
|
|
64
|
+
return entries.length === HUNDRED_THOUSAND;
|
|
65
|
+
})
|
|
66
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete orderly`, () => {
|
|
67
|
+
rbTree.clear();
|
|
68
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
69
|
+
rbTree.add(i);
|
|
70
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
71
|
+
rbTree.delete(i);
|
|
72
|
+
})
|
|
73
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete randomly`, () => {
|
|
74
|
+
rbTree.clear();
|
|
75
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
76
|
+
rbTree.add(randomArray[i]);
|
|
77
|
+
for (let i = 0; i < randomArray.length; i++)
|
|
78
|
+
rbTree.delete(randomArray[i]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export { suite };
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import fastGlob from 'fast-glob';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const isNumber = (value) => {
|
|
7
|
+
return typeof value === 'number';
|
|
8
|
+
};
|
|
9
|
+
const isString = (value) => {
|
|
10
|
+
return typeof value === 'string';
|
|
11
|
+
};
|
|
12
|
+
const isBoolean = (value) => {
|
|
13
|
+
return typeof value === 'boolean';
|
|
14
|
+
};
|
|
15
|
+
const isDate = (value) => {
|
|
16
|
+
return value instanceof Date;
|
|
17
|
+
};
|
|
18
|
+
const isNull = (value) => {
|
|
19
|
+
return value === null;
|
|
20
|
+
};
|
|
21
|
+
const isUndefined = (value) => {
|
|
22
|
+
return typeof value === 'undefined';
|
|
23
|
+
};
|
|
24
|
+
const isFunction = (value) => {
|
|
25
|
+
return typeof value === 'function';
|
|
26
|
+
};
|
|
27
|
+
const isObject = (value) => {
|
|
28
|
+
return typeof value === 'object';
|
|
29
|
+
};
|
|
30
|
+
const isArray = (value) => {
|
|
31
|
+
return Array.isArray(value);
|
|
32
|
+
};
|
|
33
|
+
const isEqual = (objA, objB) => {
|
|
34
|
+
if (objA === objB) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (typeof objA !== 'object' || typeof objB !== 'object' || objA === null || objB === null) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const keysA = Object.keys(objA);
|
|
41
|
+
const keysB = Object.keys(objB);
|
|
42
|
+
if (keysA.length !== keysB.length) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
for (const key of keysA) {
|
|
46
|
+
if (!keysB.includes(key)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (!isEqual(objA[key], objB[key])) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function toggleJS(options) {
|
|
57
|
+
if (options === null || options === void 0 ? void 0 : options.plainHtml) {
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return 'onclick="json-to-html.toggleVisibility(this);return false"';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function makeLabelDiv(options, level, keyName, datatype) {
|
|
65
|
+
if (typeof keyName === 'number') {
|
|
66
|
+
return `<div class='index'><span class='json-to-html-label'>${keyName} </span></div>`;
|
|
67
|
+
}
|
|
68
|
+
else if (typeof keyName === 'string') {
|
|
69
|
+
if (datatype === 'array') {
|
|
70
|
+
return `<div class='collapsible level${level}' ${toggleJS(options)}><span class='json-to-html-label'>${keyName}</span></div>`;
|
|
71
|
+
}
|
|
72
|
+
else if (datatype === 'object') {
|
|
73
|
+
return `<div class='attribute collapsible level${level}' ${toggleJS(options)}><span class='json-to-html-label'>${keyName}:</span></div>`;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
return `<div class='leaf level${level}'><span class='json-to-html-label'>${keyName}:</span></div>`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return '';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function getContentClass(keyName) {
|
|
84
|
+
if (typeof keyName === 'string') {
|
|
85
|
+
return 'content';
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function isPlainObject(val) {
|
|
92
|
+
let lastKey;
|
|
93
|
+
let lastOwnKey;
|
|
94
|
+
for (const key in val) {
|
|
95
|
+
if (val.hasOwnProperty(key)) {
|
|
96
|
+
lastOwnKey = key;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const key in val) {
|
|
100
|
+
lastKey = key;
|
|
101
|
+
}
|
|
102
|
+
return lastOwnKey === lastKey;
|
|
103
|
+
}
|
|
104
|
+
function isLeafValue(val) {
|
|
105
|
+
return (isNumber(val) ||
|
|
106
|
+
isString(val) ||
|
|
107
|
+
isBoolean(val) ||
|
|
108
|
+
isDate(val) ||
|
|
109
|
+
isNull(val) ||
|
|
110
|
+
isUndefined(val) ||
|
|
111
|
+
isNaN(val) ||
|
|
112
|
+
isFunction(val) ||
|
|
113
|
+
!isPlainObject(val));
|
|
114
|
+
}
|
|
115
|
+
function isLeafObject(obj) {
|
|
116
|
+
if (!isObject(obj)) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
for (const key in obj) {
|
|
120
|
+
const val = obj[key];
|
|
121
|
+
if (!isLeafValue(val)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
function isTable(arr) {
|
|
128
|
+
if (!isArray(arr)) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (arr.length === 0 || !isObject(arr[0])) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
let nonCompliant = arr.find(row => !isLeafObject(row));
|
|
136
|
+
if (nonCompliant) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const cols = Object.keys(arr[0]);
|
|
141
|
+
nonCompliant = arr.find((row) => !isEqual(cols, Object.keys(row)));
|
|
142
|
+
return !nonCompliant;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function drawTable(arr) {
|
|
147
|
+
function drawRow(headers, rowObj) {
|
|
148
|
+
return '<td>' + headers.map(header => rowObj[header]).join('</td><td>') + '</td>';
|
|
149
|
+
}
|
|
150
|
+
const cols = Object.keys(arr[0]);
|
|
151
|
+
const content = arr.map(rowObj => drawRow(cols, rowObj));
|
|
152
|
+
const headingHtml = '<tr><th>' + cols.join('</th><th>') + '</th></tr>';
|
|
153
|
+
const contentHtml = '<tr>' + content.join('</tr><tr>') + '</tr>';
|
|
154
|
+
return '<table style="display: table; width:100%; table-layout: fixed;">' + headingHtml + contentHtml + '</table>';
|
|
155
|
+
}
|
|
156
|
+
function _render(name, data, options, level, altRow) {
|
|
157
|
+
const contentClass = getContentClass(name);
|
|
158
|
+
if (isArray(data)) {
|
|
159
|
+
const title = makeLabelDiv(options, level, `${name}`, 'array');
|
|
160
|
+
let subs;
|
|
161
|
+
if (isTable(data)) {
|
|
162
|
+
subs = drawTable(data);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
subs =
|
|
166
|
+
"<div class='altRows'>" +
|
|
167
|
+
data
|
|
168
|
+
.map((val, idx) => _render(idx.toString(), val, options, level + 1, idx % 2))
|
|
169
|
+
.join("</div><div class='altRows'>") +
|
|
170
|
+
'</div>';
|
|
171
|
+
}
|
|
172
|
+
return `<div class="json-to-html-collapse clearfix ${altRow}">
|
|
173
|
+
${title}
|
|
174
|
+
<div class="${contentClass}">${subs}</div>
|
|
175
|
+
</div>`;
|
|
176
|
+
}
|
|
177
|
+
else if (isLeafValue(data)) {
|
|
178
|
+
const title = makeLabelDiv(options, level, name);
|
|
179
|
+
if (isFunction(data)) {
|
|
180
|
+
return `${title}<span class='json-to-html-value'> -function() can't _render-</span>`;
|
|
181
|
+
}
|
|
182
|
+
else if (!isPlainObject(data)) {
|
|
183
|
+
if (isFunction(data.toString)) {
|
|
184
|
+
return `${title}<span class='json-to-html-value'> ${data.toString()}</span>`;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
return `${title}<span class='json-to-html-value'> -instance object, can't render-</span>`;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
return `${title}<span class='json-to-html-value'> ${data}</span>`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const title = makeLabelDiv(options, level, name, 'object');
|
|
196
|
+
let count = 0;
|
|
197
|
+
const subs = '<div>' +
|
|
198
|
+
Object.entries(data)
|
|
199
|
+
.map(([key, val]) => _render(key, val, options, level + 1, count++ % 2))
|
|
200
|
+
.join('</div><div>') +
|
|
201
|
+
'</div>';
|
|
202
|
+
const inner = `<div class="json-to-html-expand clearfix ${altRow}">
|
|
203
|
+
${title}
|
|
204
|
+
<div class="${contentClass}">${subs}</div>
|
|
205
|
+
</div>`;
|
|
206
|
+
return `${level === 0 ? "<div id='json-to-html'>" : ''}
|
|
207
|
+
${inner}
|
|
208
|
+
${level === 0 ? '</div>' : ''}`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
export function render(name, json, options) {
|
|
212
|
+
return `${_render(name, json, options, 0, 0)}`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
217
|
+
const __dirname = path.dirname(__filename);
|
|
218
|
+
|
|
219
|
+
function numberFix(num, decimalPlaces) {
|
|
220
|
+
if (num > 10000 || num < 0.001) {
|
|
221
|
+
const [mantissa, exponent] = num.toExponential().split('e');
|
|
222
|
+
const formattedMantissa = Number(mantissa).toFixed(decimalPlaces);
|
|
223
|
+
return `${formattedMantissa}e${exponent}`;
|
|
224
|
+
} else {
|
|
225
|
+
return num.toFixed(decimalPlaces);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const ConsoleColor = {
|
|
229
|
+
END: '\x1b[0m',
|
|
230
|
+
BOLD: '\x1b[1m',
|
|
231
|
+
DIM: '\x1b[2m',
|
|
232
|
+
ITALIC: '\x1b[3m',
|
|
233
|
+
UNDERLINE: '\x1b[4m',
|
|
234
|
+
INVERSE: '\x1b[7m',
|
|
235
|
+
STRIKETHROUGH: '\x1b[9m',
|
|
236
|
+
NO_BOLD: '\x1b[22m',
|
|
237
|
+
NO_ITALIC: '\x1b[23m',
|
|
238
|
+
NO_UNDERLINE: '\x1b[24m',
|
|
239
|
+
NO_INVERSE: '\x1b[27m',
|
|
240
|
+
NO_STRIKETHROUGH: '\x1b[29m',
|
|
241
|
+
BLACK: '\x1b[30m',
|
|
242
|
+
RED: '\x1b[31m',
|
|
243
|
+
GREEN: '\x1b[32m',
|
|
244
|
+
YELLOW: '\x1b[33m',
|
|
245
|
+
BLUE: '\x1b[34m',
|
|
246
|
+
MAGENTA: '\x1b[35m',
|
|
247
|
+
GRAY: '\x1b[90m',
|
|
248
|
+
CYAN: '\x1b[36m',
|
|
249
|
+
WHITE: '\x1b[37m',
|
|
250
|
+
BG_BLACK: '\x1b[40m',
|
|
251
|
+
BG_RED: '\x1b[41m',
|
|
252
|
+
BG_GREEN: '\x1b[42m',
|
|
253
|
+
BG_YELLOW: '\x1b[43m',
|
|
254
|
+
BG_BLUE: '\x1b[44m',
|
|
255
|
+
BG_MAGENTA: '\x1b[45m',
|
|
256
|
+
BG_CYAN: '\x1b[46m',
|
|
257
|
+
BG_WHITE: '\x1b[47m'
|
|
258
|
+
};
|
|
259
|
+
const args = process.argv.slice(2);
|
|
260
|
+
const { GREEN, BOLD, END, YELLOW, GRAY, CYAN, BG_YELLOW } = ConsoleColor;
|
|
261
|
+
const isOnlyOrdered = true;
|
|
262
|
+
const runOrder = [
|
|
263
|
+
'heap',
|
|
264
|
+
'avl-tree',
|
|
265
|
+
'red-black-tree',
|
|
266
|
+
'doubly-linked-list',
|
|
267
|
+
'directed-graph',
|
|
268
|
+
'queue',
|
|
269
|
+
'deque',
|
|
270
|
+
'hash-map',
|
|
271
|
+
'trie',
|
|
272
|
+
'stack'
|
|
273
|
+
// 'singly-linked-list',
|
|
274
|
+
// 'priority-queue',
|
|
275
|
+
// 'binary-tree-overall'
|
|
276
|
+
];
|
|
277
|
+
const getRelativePath = file => {
|
|
278
|
+
return path.relative(__dirname, file);
|
|
279
|
+
};
|
|
280
|
+
const coloredLabeled = (label, file) => {
|
|
281
|
+
|
|
282
|
+
const relativeFilePath = getRelativePath(file);
|
|
283
|
+
const directory = path.dirname(relativeFilePath);
|
|
284
|
+
const fileName = path.basename(relativeFilePath);
|
|
285
|
+
return `${BG_YELLOW} ${label} ${END} ${GRAY}${directory}/${END}${CYAN}${fileName}${END}`;
|
|
286
|
+
};
|
|
287
|
+
const parentDirectory = path.resolve(__dirname, '../..');
|
|
288
|
+
const reportDistPath = path.join(parentDirectory, 'benchmark');
|
|
289
|
+
const testDir = path.join(__dirname, 'data-structures');
|
|
290
|
+
let allFiles = fastGlob.sync(path.join(testDir, '**', '*.test.mjs'));
|
|
291
|
+
|
|
292
|
+
let testFiles;
|
|
293
|
+
let isIndividual = false;
|
|
294
|
+
if (args.length > 0) {
|
|
295
|
+
console.log(`arguments: ${args.join(' ')}`);
|
|
296
|
+
testFiles = allFiles.filter(file => args.every(word => file.includes(word)));
|
|
297
|
+
isIndividual = true;
|
|
298
|
+
console.log(
|
|
299
|
+
`${testFiles.map(file => coloredLabeled('Found', file)).join(`
|
|
300
|
+
`)}`
|
|
301
|
+
);
|
|
302
|
+
} else {
|
|
303
|
+
isIndividual = false;
|
|
304
|
+
testFiles = allFiles;
|
|
305
|
+
}
|
|
306
|
+
const report = {};
|
|
307
|
+
let completedCount = 0;
|
|
308
|
+
const performanceTests = [];
|
|
309
|
+
for (const file of testFiles) {
|
|
310
|
+
const testName = path.basename(file, '.test.mjs');
|
|
311
|
+
const testFunction = await import(file);
|
|
312
|
+
const { suite } = testFunction;
|
|
313
|
+
if (suite)
|
|
314
|
+
performanceTests.push({
|
|
315
|
+
testName,
|
|
316
|
+
suite,
|
|
317
|
+
file
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const composeReport = () => {
|
|
322
|
+
if (!fs.existsSync(reportDistPath))
|
|
323
|
+
fs.mkdirSync(reportDistPath, {
|
|
324
|
+
recursive: true
|
|
325
|
+
});
|
|
326
|
+
const filePath = path.join(reportDistPath, 'report.json');
|
|
327
|
+
const htmlFilePath = path.join(reportDistPath, 'report.html');
|
|
328
|
+
fs.writeFileSync(filePath, JSON.stringify(report, null, 2));
|
|
329
|
+
let html = `<!DOCTYPE html>
|
|
330
|
+
<html lang="en">
|
|
331
|
+
<head>
|
|
332
|
+
<meta charset="UTF-8">
|
|
333
|
+
<title>performance of data-structure-typed</title>
|
|
334
|
+
<style>
|
|
335
|
+
*{
|
|
336
|
+
box-sizing: border-box;
|
|
337
|
+
}
|
|
338
|
+
#json-to-html {
|
|
339
|
+
padding: 0 10px 20px;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.json-to-html-label {
|
|
343
|
+
font-size: 2rem;
|
|
344
|
+
margin: 2rem 0 0 3px;
|
|
345
|
+
}
|
|
346
|
+
.content table {
|
|
347
|
+
width: 100%;
|
|
348
|
+
table-layout: fixed;
|
|
349
|
+
border-collapse: collapse;
|
|
350
|
+
margin-top: 10px;
|
|
351
|
+
font-size: 16px;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.content table th,
|
|
355
|
+
.content table td {
|
|
356
|
+
padding: 8px 12px;
|
|
357
|
+
text-align: left;
|
|
358
|
+
border: 1px solid #ddd;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.content table th {
|
|
362
|
+
background-color: #f2f2f2;
|
|
363
|
+
font-weight: bold;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.content table tr:nth-child(odd) {
|
|
367
|
+
background-color: #ffffff;
|
|
368
|
+
}
|
|
369
|
+
</style>
|
|
370
|
+
</head>
|
|
371
|
+
<body>
|
|
372
|
+
<div id="json-to-html">`;
|
|
373
|
+
let htmlTables = '';
|
|
374
|
+
for (const r in report) {
|
|
375
|
+
if (report.hasOwnProperty(r)) {
|
|
376
|
+
htmlTables += render(report[r].testName, report[r].benchmarks, {
|
|
377
|
+
plainHtml: true,
|
|
378
|
+
'<>': 'table',
|
|
379
|
+
html: [
|
|
380
|
+
{
|
|
381
|
+
'<>': 'tr',
|
|
382
|
+
html: [
|
|
383
|
+
{
|
|
384
|
+
'<>': 'td',
|
|
385
|
+
html: '${name}'
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
'<>': 'td',
|
|
389
|
+
html: '${periodMS}'
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
'<>': 'td',
|
|
393
|
+
html: '${mean}'
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
htmlTables += `
|
|
402
|
+
|
|
403
|
+
`;
|
|
404
|
+
html += htmlTables;
|
|
405
|
+
html += `</div>
|
|
406
|
+
</body>
|
|
407
|
+
</html>`;
|
|
408
|
+
if (!isIndividual)
|
|
409
|
+
replaceMarkdownContent(
|
|
410
|
+
'[//]: # (No deletion!!! Start of Replace Section)', // Start tag
|
|
411
|
+
'[//]: # (No deletion!!! End of Replace Section)', // end identifier
|
|
412
|
+
htmlTables // New content to be inserted
|
|
413
|
+
);
|
|
414
|
+
fs.writeFileSync(htmlFilePath, html);
|
|
415
|
+
console.log(`Performance ${BOLD}${GREEN}report${END} file generated in file://${BOLD}${GREEN}${htmlFilePath}${END}`);
|
|
416
|
+
};
|
|
417
|
+
function replaceMarkdownContent(startMarker, endMarker, newText) {
|
|
418
|
+
const filePath = path.join(parentDirectory, 'README.md'); // Path to README.md file
|
|
419
|
+
fs.readFile(filePath, 'utf8', (err, data) => {
|
|
420
|
+
if (err) {
|
|
421
|
+
console.error(`Unable to read ${filePath}:`, err);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
// Find the start and end markers in the content
|
|
425
|
+
const startIndex = data.indexOf(startMarker);
|
|
426
|
+
const endIndex = data.indexOf(endMarker, startIndex + 1);
|
|
427
|
+
if (startIndex === -1 || endIndex === -1) {
|
|
428
|
+
console.error('Unable to find start or end marker');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
// Replace the old content with the new text
|
|
432
|
+
const updatedMarkdown = data.slice(0, startIndex + startMarker.length) + '\n' + newText + data.slice(endIndex);
|
|
433
|
+
// Try writing the modified content back to the file
|
|
434
|
+
fs.writeFile(filePath, updatedMarkdown, 'utf8', err => {
|
|
435
|
+
if (err) {
|
|
436
|
+
console.error(`Unable to write to ${filePath}:`, err);
|
|
437
|
+
} else {
|
|
438
|
+
console.log(`The content has been successfully replaced in file://${BOLD}${GREEN}${filePath}${END}`);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
const sortedPerformanceTests = (
|
|
444
|
+
isOnlyOrdered ? [...performanceTests].filter(test => runOrder.includes(test.testName)) : [...performanceTests]
|
|
445
|
+
).sort((a, b) => {
|
|
446
|
+
const indexA = runOrder.indexOf(a.testName);
|
|
447
|
+
const indexB = runOrder.indexOf(b.testName);
|
|
448
|
+
// If both a and b are in the runOrder, sort them according to their indices in the runOrder.
|
|
449
|
+
if (indexA !== -1 && indexB !== -1) {
|
|
450
|
+
return indexA - indexB;
|
|
451
|
+
}
|
|
452
|
+
// If there is only 'a' in the runOrder, then place 'b' in front.
|
|
453
|
+
if (indexA !== -1) {
|
|
454
|
+
return 1;
|
|
455
|
+
}
|
|
456
|
+
// If only b is in the runOrder, then a should be placed before it.
|
|
457
|
+
if (indexB !== -1) {
|
|
458
|
+
return -1;
|
|
459
|
+
}
|
|
460
|
+
// If neither a nor b are in runOrder, keep their original runOrder
|
|
461
|
+
return 0;
|
|
462
|
+
});
|
|
463
|
+
console.log(
|
|
464
|
+
`${GREEN} Matched Suites (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`
|
|
465
|
+
);
|
|
466
|
+
console.log(
|
|
467
|
+
`${GREEN} Running Suites (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}`
|
|
468
|
+
);
|
|
469
|
+
sortedPerformanceTests.forEach(item => {
|
|
470
|
+
const { suite, testName, file } = item;
|
|
471
|
+
console.log(coloredLabeled('Running', file));
|
|
472
|
+
if (suite) {
|
|
473
|
+
let runTime = 0;
|
|
474
|
+
suite
|
|
475
|
+
.on('complete', function () {
|
|
476
|
+
completedCount++;
|
|
477
|
+
report[testName] = {};
|
|
478
|
+
report[testName].benchmarks = this.map(benchmark => {
|
|
479
|
+
runTime += benchmark.times.elapsed;
|
|
480
|
+
return {
|
|
481
|
+
'test name': benchmark.name,
|
|
482
|
+
'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
|
|
483
|
+
// 'executions per sec': numberFix(benchmark.hz, 2),
|
|
484
|
+
// 'executed times': numberFix(benchmark.count, 0),
|
|
485
|
+
'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
|
486
|
+
'sample deviation': numberFix(benchmark.stats.deviation, 2)
|
|
487
|
+
};
|
|
488
|
+
});
|
|
489
|
+
report[testName].testName = testName;
|
|
490
|
+
const isDone = completedCount === sortedPerformanceTests.length;
|
|
491
|
+
runTime = Number(runTime.toFixed(2));
|
|
492
|
+
const isTimeWarn = runTime > 120;
|
|
493
|
+
console.log(
|
|
494
|
+
// `Files: ${GREEN}${testFileCount}${END} `,
|
|
495
|
+
// `Suites: ${GREEN}${performanceTests.length}${END} `,
|
|
496
|
+
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${sortedPerformanceTests.length}${END}`,
|
|
497
|
+
`Time Costs: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
|
498
|
+
);
|
|
499
|
+
if (isDone) {
|
|
500
|
+
composeReport();
|
|
501
|
+
}
|
|
502
|
+
})
|
|
503
|
+
.run({ async: false });
|
|
504
|
+
}
|
|
505
|
+
});
|