mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
[binary-tree] Merge the operation of modifying the value into the insert query process to improve performance. [benchmark] Enhance performance test cases and move existing performance tests from unit tests to the 'performance' section. Improve the benchmark test report. Refine the logic of the report generator.
This commit is contained in:
parent
9a87fe13b6
commit
4614773413
29
README.md
29
README.md
|
@ -916,3 +916,32 @@ optimal approach to data structure design.
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
## Benchmark
|
||||
<div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>avl-tree</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>add 1000 randomly</td><td>2.39</td><td>418.91</td><td>22</td><td>0.00</td><td>6.47e-5</td></tr><tr><td>delete 1000 randomly</td><td>0.06</td><td>1.77e+4</td><td>917</td><td>5.66e-5</td><td>1.90e-6</td></tr><tr><td>addMany 1000</td><td>3.32</td><td>301.33</td><td>17</td><td>0.00</td><td>5.90e-4</td></tr><tr><td>get 1000</td><td>55.03</td><td>18.17</td><td>1</td><td>0.06</td><td>0.00</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>binary-tree</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>add 1000</td><td>66.49</td><td>15.04</td><td>3</td><td>0.07</td><td>0.00</td></tr><tr><td>delete 1000</td><td>24.04</td><td>41.60</td><td>1106</td><td>0.02</td><td>0.04</td></tr><tr><td>addMany 1000</td><td>9.05</td><td>110.44</td><td>6</td><td>0.01</td><td>2.88e-4</td></tr><tr><td>get 1000</td><td>32.12</td><td>31.13</td><td>2</td><td>0.03</td><td>6.75e-4</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>bst</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>add 1000 randomly</td><td>2.27</td><td>440.62</td><td>23</td><td>0.00</td><td>1.42e-4</td></tr><tr><td>delete 1000 randomly</td><td>0.05</td><td>2.19e+4</td><td>1174</td><td>4.57e-5</td><td>1.97e-6</td></tr><tr><td>addMany 1000 balanced</td><td>2.93</td><td>341.38</td><td>19</td><td>0.00</td><td>1.13e-4</td></tr><tr><td>get 1000</td><td>62.85</td><td>15.91</td><td>1</td><td>0.06</td><td>0.01</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>heap</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>add & 1000</td><td>0.35</td><td>2878.39</td><td>149</td><td>3.47e-4</td><td>1.93e-5</td></tr><tr><td>fib add & pop 1000</td><td>3.98</td><td>251.35</td><td>14</td><td>0.00</td><td>1.32e-4</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>doubly-linked-list</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>unshift 1000000</td><td>193.19</td><td>5.18</td><td>1</td><td>0.19</td><td>0.04</td></tr><tr><td>unshift & shift 1000000</td><td>170.35</td><td>5.87</td><td>1</td><td>0.17</td><td>0.03</td></tr><tr><td>insertBefore 1000</td><td>0.03</td><td>3.43e+4</td><td>1888</td><td>2.91e-5</td><td>6.03e-6</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>singly-linked-list</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>push & pop 1000</td><td>1.79</td><td>560.08</td><td>29</td><td>0.00</td><td>7.70e-5</td></tr><tr><td>insertBefore 1000</td><td>2.31</td><td>433.45</td><td>22</td><td>0.00</td><td>5.52e-5</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>max-priority-queue</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>refill & poll 1000000</td><td>1859.40</td><td>0.54</td><td>1</td><td>1.86</td><td>0.03</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>deque</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>push 1000000</td><td>215.00</td><td>4.65</td><td>1</td><td>0.22</td><td>0.01</td></tr><tr><td>shift 1000000</td><td>25.04</td><td>39.94</td><td>3</td><td>0.03</td><td>0.00</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>queue</span></div>
|
||||
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>executed times</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>push 1000000</td><td>41.81</td><td>23.92</td><td>2</td><td>0.04</td><td>0.00</td></tr><tr><td>push & shift 1000000</td><td>79.17</td><td>12.63</td><td>1</td><td>0.08</td><td>8.70e-4</td></tr></table></div>
|
||||
</div>
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "data-structure-typed",
|
||||
"version": "1.41.3",
|
||||
"version": "1.41.4",
|
||||
"description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/mjs/index.js",
|
||||
|
@ -31,7 +31,7 @@
|
|||
"reformat": "npm run reformat:src && npm run reformat:test",
|
||||
"update:subs": "npm i avl-tree-typed binary-tree-typed bst-typed heap-typed --save-dev",
|
||||
"install:all-subs": "npm i avl-tree-typed binary-tree-typed bst-typed deque-typed directed-graph-typed doubly-linked-list-typed graph-typed heap-typed linked-list-typed max-heap-typed max-priority-queue-typed min-heap-typed min-priority-queue-typed priority-queue-typed singly-linked-list-typed stack-typed tree-multiset-typed trie-typed undirected-graph-typed queue-typed --save-dev",
|
||||
"test": "jest",
|
||||
"test": "jest --runInBand",
|
||||
"test:integration": "npm run update:subs && jest --config jest.integration.config.js",
|
||||
"benchmark": "ts-node test/performance/reportor.ts",
|
||||
"check:deps": "dependency-cruiser src",
|
||||
|
|
|
@ -179,7 +179,10 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
|
|||
while (queue.size > 0) {
|
||||
const cur = queue.shift();
|
||||
if (cur) {
|
||||
if (newNode && cur.key === newNode.key) return;
|
||||
if (newNode && cur.key === newNode.key) {
|
||||
cur.value = newNode.value;
|
||||
return;
|
||||
}
|
||||
const inserted = this._addTo(newNode, cur);
|
||||
if (inserted !== undefined) return inserted;
|
||||
if (cur.left) queue.push(cur.left);
|
||||
|
@ -201,16 +204,16 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
|
|||
return;
|
||||
}
|
||||
|
||||
const key = typeof keyOrNode === 'number' ? keyOrNode : keyOrNode ? keyOrNode.key : undefined;
|
||||
const existNode = key !== undefined ? this.getNode(key, (node: N) => node.key) : undefined;
|
||||
// const key = typeof keyOrNode === 'number' ? keyOrNode : keyOrNode ? keyOrNode.key : undefined;
|
||||
// const existNode = key !== undefined ? this.getNode(key, (node: N) => node.key) : undefined;
|
||||
|
||||
if (this.root) {
|
||||
if (existNode) {
|
||||
existNode.value = value;
|
||||
inserted = existNode;
|
||||
} else {
|
||||
inserted = _bfs(this.root, needInsert);
|
||||
}
|
||||
// if (existNode) {
|
||||
// existNode.value = value;
|
||||
// inserted = existNode;
|
||||
// } else {
|
||||
inserted = _bfs(this.root, needInsert);
|
||||
// }
|
||||
} else {
|
||||
this._setRoot(needInsert);
|
||||
if (needInsert !== null) {
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', {value: true});
|
||||
exports.isDebugTest = void 0;
|
||||
exports.isDebugTest = false;
|
|
@ -0,0 +1,24 @@
|
|||
import {AVLTree} from '../../../../src';
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude, randomInt, randomIntArray} from '../../../utils';
|
||||
|
||||
const suite = new Benchmark.Suite();
|
||||
const avl = new AVLTree<number>();
|
||||
const {N_LOG_N} = magnitude;
|
||||
|
||||
suite
|
||||
.add(`add ${N_LOG_N} randomly`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) avl.add(randomInt(0, N_LOG_N));
|
||||
})
|
||||
.add(`delete ${N_LOG_N} randomly`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) avl.delete(randomInt(0, N_LOG_N));
|
||||
})
|
||||
.add(`addMany ${N_LOG_N}`, () => {
|
||||
const arr = randomIntArray(N_LOG_N);
|
||||
avl.addMany(arr);
|
||||
})
|
||||
.add(`get ${N_LOG_N}`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) avl.get(randomInt(-N_LOG_N, N_LOG_N));
|
||||
});
|
||||
|
||||
export {suite};
|
|
@ -1,18 +1,25 @@
|
|||
import {BinaryTree} from '../../../../src';
|
||||
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude, randomInt, randomIntArray} from '../../../utils';
|
||||
|
||||
export const suite = new Benchmark.Suite();
|
||||
const bt = new BinaryTree<number>();
|
||||
const suite = new Benchmark.Suite();
|
||||
const biTree = new BinaryTree<number>();
|
||||
const {N_LOG_N} = magnitude;
|
||||
|
||||
suite
|
||||
.add('add 1000', () => {
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
bt.add(i);
|
||||
}
|
||||
.add(`add ${N_LOG_N}`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) biTree.add(randomInt(-N_LOG_N, N_LOG_N));
|
||||
})
|
||||
.add('add & delete 1000', () => {
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
bt.delete(i);
|
||||
}
|
||||
.add(`delete ${N_LOG_N}`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) biTree.delete(randomInt(-N_LOG_N, N_LOG_N));
|
||||
})
|
||||
.add(`addMany ${N_LOG_N}`, () => {
|
||||
biTree.clear();
|
||||
const arr = randomIntArray(N_LOG_N);
|
||||
biTree.addMany(arr);
|
||||
})
|
||||
.add(`get ${N_LOG_N}`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) biTree.get(randomInt(-N_LOG_N, N_LOG_N));
|
||||
});
|
||||
|
||||
export {suite};
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
import {BST} from '../../../../src';
|
||||
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude, randomInt, randomIntArray} from '../../../utils';
|
||||
|
||||
export const suite = new Benchmark.Suite();
|
||||
const bt = new BST<number>();
|
||||
const suite = new Benchmark.Suite();
|
||||
const bst = new BST<number>();
|
||||
const {N_LOG_N} = magnitude;
|
||||
|
||||
suite
|
||||
.add('add 1000', () => {
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
bt.add(i);
|
||||
}
|
||||
.add(`add ${N_LOG_N} randomly`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) bst.add(randomInt(0, N_LOG_N));
|
||||
})
|
||||
.add('add & delete 1000', () => {
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
bt.delete(i);
|
||||
}
|
||||
.add(`delete ${N_LOG_N} randomly`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) bst.delete(randomInt(0, N_LOG_N));
|
||||
})
|
||||
.add(`addMany ${N_LOG_N} balanced`, () => {
|
||||
const arr = randomIntArray(N_LOG_N);
|
||||
bst.addMany(arr);
|
||||
})
|
||||
.add(`get ${N_LOG_N}`, () => {
|
||||
for (let i = 0; i < N_LOG_N; i++) bst.get(randomInt(-N_LOG_N, N_LOG_N));
|
||||
});
|
||||
|
||||
export {suite};
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import {FibonacciHeap, Heap} from '../../../../src';
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude} from '../../../utils';
|
||||
|
||||
const suite = new Benchmark.Suite();
|
||||
const {N_LOG_N} = magnitude;
|
||||
|
||||
suite
|
||||
.add(`add & ${N_LOG_N}`, () => {
|
||||
const heap = new Heap<number>({comparator: (a, b) => b - a});
|
||||
|
||||
for (let i = 0; i < N_LOG_N; i++) {
|
||||
heap.add(i);
|
||||
}
|
||||
|
||||
for (let i = 0; i < N_LOG_N; i++) {
|
||||
heap.pop();
|
||||
}
|
||||
})
|
||||
.add(`fib add & pop ${N_LOG_N}`, () => {
|
||||
const fbHeap = new FibonacciHeap<number>();
|
||||
for (let i = 1; i <= N_LOG_N; i++) {
|
||||
fbHeap.push(i);
|
||||
}
|
||||
for (let i = 1; i <= N_LOG_N; i++) {
|
||||
fbHeap.pop();
|
||||
}
|
||||
});
|
||||
|
||||
export {suite};
|
|
@ -0,0 +1,40 @@
|
|||
import {DoublyLinkedList, DoublyLinkedListNode} from '../../../../src';
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude} from '../../../utils';
|
||||
|
||||
const suite = new Benchmark.Suite();
|
||||
const {LINEAR, N_LOG_N} = magnitude;
|
||||
|
||||
suite
|
||||
.add(`unshift ${LINEAR}`, () => {
|
||||
const list = new DoublyLinkedList<number>();
|
||||
|
||||
for (let i = 0; i < LINEAR; i++) {
|
||||
list.unshift(i);
|
||||
}
|
||||
})
|
||||
.add(`unshift & shift ${LINEAR}`, () => {
|
||||
const list = new DoublyLinkedList<number>();
|
||||
|
||||
for (let i = 0; i < LINEAR; i++) {
|
||||
list.unshift(i);
|
||||
}
|
||||
for (let i = 0; i < LINEAR; i++) {
|
||||
list.shift();
|
||||
}
|
||||
})
|
||||
.add(`insertBefore ${N_LOG_N}`, () => {
|
||||
const doublyList = new DoublyLinkedList<number>();
|
||||
let midNode: DoublyLinkedListNode | null = null;
|
||||
const midIndex = Math.floor(N_LOG_N / 2);
|
||||
for (let i = 0; i < N_LOG_N; i++) {
|
||||
doublyList.push(i);
|
||||
if (i === midIndex) {
|
||||
midNode = doublyList.getNode(i);
|
||||
} else if (i > midIndex && midNode) {
|
||||
doublyList.insertBefore(midNode, i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export {suite};
|
|
@ -0,0 +1,34 @@
|
|||
import {SinglyLinkedList, SinglyLinkedListNode} from '../../../../src';
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude} from '../../../utils';
|
||||
|
||||
const suite = new Benchmark.Suite();
|
||||
const {N_LOG_N} = magnitude;
|
||||
|
||||
suite
|
||||
.add(`push & pop ${N_LOG_N}`, () => {
|
||||
const list = new SinglyLinkedList<number>();
|
||||
|
||||
for (let i = 0; i < N_LOG_N; i++) {
|
||||
list.push(i);
|
||||
}
|
||||
|
||||
for (let i = 0; i < N_LOG_N; i++) {
|
||||
list.pop();
|
||||
}
|
||||
})
|
||||
.add(`insertBefore ${N_LOG_N}`, () => {
|
||||
const singlyList = new SinglyLinkedList<number>();
|
||||
let midSinglyNode: SinglyLinkedListNode | null = null;
|
||||
const midIndex = Math.floor(N_LOG_N / 2);
|
||||
for (let i = 0; i < N_LOG_N; i++) {
|
||||
singlyList.push(i);
|
||||
if (i === midIndex) {
|
||||
midSinglyNode = singlyList.getNode(i);
|
||||
} else if (i > midIndex && midSinglyNode) {
|
||||
singlyList.insertBefore(midSinglyNode.value, i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export {suite};
|
|
@ -0,0 +1,19 @@
|
|||
import {MaxPriorityQueue} from '../../../../src';
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude} from '../../../utils';
|
||||
|
||||
const suite = new Benchmark.Suite();
|
||||
const {LINEAR} = magnitude;
|
||||
|
||||
suite.add(`refill & poll ${LINEAR}`, () => {
|
||||
const nodes = Array.from(
|
||||
new Set<number>(Array.from(new Array(LINEAR), () => Math.floor(Math.random() * LINEAR * 100)))
|
||||
);
|
||||
const maxPQ = new MaxPriorityQueue<number>();
|
||||
maxPQ.refill(nodes);
|
||||
while (maxPQ.size > 0) {
|
||||
maxPQ.poll();
|
||||
}
|
||||
});
|
||||
|
||||
export {suite};
|
|
@ -1,18 +1,20 @@
|
|||
import {Deque} from '../../../../src';
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude} from '../../../utils';
|
||||
|
||||
export const suite = new Benchmark.Suite();
|
||||
const {LINEAR} = magnitude;
|
||||
|
||||
suite
|
||||
.add('push', () => {
|
||||
.add(`push ${LINEAR}`, () => {
|
||||
const deque = new Deque<number>();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
for (let i = 0; i < LINEAR; i++) {
|
||||
deque.push(i);
|
||||
}
|
||||
})
|
||||
.add('shift', () => {
|
||||
.add(`shift ${LINEAR}`, () => {
|
||||
const deque = new Deque<number>();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
for (let i = 0; i < LINEAR; i++) {
|
||||
deque.push(i);
|
||||
deque.shift();
|
||||
}
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
import {Queue} from '../../../../src';
|
||||
|
||||
import * as Benchmark from 'benchmark';
|
||||
import {magnitude} from '../../../utils';
|
||||
|
||||
export const suite = new Benchmark.Suite();
|
||||
const suite = new Benchmark.Suite();
|
||||
const {LINEAR} = magnitude;
|
||||
|
||||
suite
|
||||
.add('push 1000000', () => {
|
||||
.add(`push ${LINEAR}`, () => {
|
||||
const queue = new Queue<number>();
|
||||
for (let i = 0; i < 1000000; i++) {
|
||||
|
||||
for (let i = 0; i < LINEAR; i++) {
|
||||
queue.push(i);
|
||||
}
|
||||
})
|
||||
.add('push & shift 1000000', () => {
|
||||
.add(`push & shift ${LINEAR}`, () => {
|
||||
const queue = new Queue<number>();
|
||||
for (let i = 0; i < 1000000; i++) {
|
||||
|
||||
for (let i = 0; i < LINEAR; i++) {
|
||||
queue.push(i);
|
||||
queue.shift();
|
||||
}
|
||||
});
|
||||
|
||||
export {suite};
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as Benchmark from 'benchmark';
|
|||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as fastGlob from 'fast-glob';
|
||||
import {numberFix, render, Color} from './utils';
|
||||
import {Color, numberFix, render} from '../utils';
|
||||
import {PerformanceTest} from './types';
|
||||
|
||||
const reportDistPath = 'benchmark';
|
||||
|
@ -15,7 +15,7 @@ let testFileCount = 0,
|
|||
completedCount = 0;
|
||||
|
||||
const performanceTests: PerformanceTest[] = [];
|
||||
const {GREEN, BOLD, END} = Color;
|
||||
const {GREEN, BOLD, RED, END, YELLOW, CYAN, BG_YELLOW} = Color;
|
||||
|
||||
testFiles.forEach((file: string) => {
|
||||
testFileCount++;
|
||||
|
@ -37,6 +37,17 @@ const composeReport = () => {
|
|||
<meta charset="UTF-8">
|
||||
<title>performance of data-structure-typed</title>
|
||||
<style>
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#json-to-html {
|
||||
padding: 0 10px 20px;
|
||||
}
|
||||
|
||||
.json-to-html-label {
|
||||
font-size: 2rem;
|
||||
margin: 2rem 0 0 3px;
|
||||
}
|
||||
.content table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
|
@ -64,9 +75,10 @@ const composeReport = () => {
|
|||
</head>
|
||||
<body>
|
||||
<div id="json-to-html">`;
|
||||
let htmlTables = '';
|
||||
for (const r in report) {
|
||||
if (report.hasOwnProperty(r)) {
|
||||
html += render(report[r].testName, report[r].benchmarks, {
|
||||
htmlTables += render(report[r].testName, report[r].benchmarks, {
|
||||
plainHtml: true,
|
||||
'<>': 'table',
|
||||
html: [
|
||||
|
@ -82,17 +94,54 @@ const composeReport = () => {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
html += htmlTables;
|
||||
html += `</div>
|
||||
</body>
|
||||
</html>`;
|
||||
writeIntoMarkdown(htmlTables);
|
||||
fs.writeFileSync(htmlFilePath, html);
|
||||
console.log(`Performance ${BOLD}${GREEN}report${END} file generated`);
|
||||
};
|
||||
|
||||
function writeIntoMarkdown(html: string) {
|
||||
const parentDirectory = path.resolve(__dirname, '../..'); // The path to the parent directory
|
||||
const markdownFilePath = path.join(parentDirectory, 'README.md'); // Path to README.md file
|
||||
const textToInsert = html;
|
||||
|
||||
// Read the original README.md file
|
||||
fs.readFile(markdownFilePath, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error('Unable to read README.md file:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the location in the README.md file where you want to insert the text, for example under a specific tag
|
||||
const insertMarker = '## Benchmark';
|
||||
|
||||
const index = data.indexOf(insertMarker);
|
||||
if (index === -1) {
|
||||
console.error('Unable to find insertion point');
|
||||
return;
|
||||
}
|
||||
|
||||
// insert text
|
||||
const updatedMarkdown =
|
||||
data.slice(0, index + insertMarker.length) + '\n' + textToInsert + data.slice(index + insertMarker.length);
|
||||
|
||||
// Try writing the modified content back to the README.md file
|
||||
fs.writeFile(markdownFilePath, updatedMarkdown, 'utf8', err => {
|
||||
if (err) {
|
||||
console.error('Unable to write to README.md file:', err);
|
||||
} else {
|
||||
console.log('The text has been successfully inserted into the README.md file!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
performanceTests.forEach(item => {
|
||||
const {suite, testName, file} = item;
|
||||
console.log(`testing file ${GREEN}${file}${END}`);
|
||||
console.log(`${BG_YELLOW}Running in${END}: ${CYAN}${file}${END}`);
|
||||
|
||||
if (suite) {
|
||||
suite
|
||||
|
@ -103,16 +152,20 @@ performanceTests.forEach(item => {
|
|||
'test name': benchmark.name,
|
||||
'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
|
||||
'executions per sec': numberFix(benchmark.hz, 2),
|
||||
// 'executed times': numberFix(benchmark.count, 2),
|
||||
'executed times': numberFix(benchmark.count, 0),
|
||||
'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
||||
'sample deviation': numberFix(benchmark.stats.deviation, 2)
|
||||
}));
|
||||
report[testName].testName = testName;
|
||||
|
||||
const isDone = completedCount === performanceTests.length;
|
||||
console.log(
|
||||
`test files: ${GREEN}${testFileCount}${END}. suites: ${GREEN}${performanceTests.length}${END}. completed suites: ${GREEN}${completedCount}${END}`
|
||||
`Files: ${GREEN}${testFileCount}${END} `,
|
||||
`Suites: ${GREEN}${performanceTests.length}${END} `,
|
||||
`Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : RED}${
|
||||
performanceTests.length
|
||||
}${END}`
|
||||
);
|
||||
if (completedCount === performanceTests.length) {
|
||||
if (isDone) {
|
||||
composeReport();
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
export * from './utils';
|
||||
export * from './reportor';
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './json2html';
|
|
@ -1,4 +0,0 @@
|
|||
export * from './json2html';
|
||||
export * from './number';
|
||||
export * from './is';
|
||||
export * from './console';
|
|
@ -1,9 +0,0 @@
|
|||
export function numberFix(num: number, decimalPlaces: number): string {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
'use strict';
|
||||
var __createBinding =
|
||||
(this && this.__createBinding) ||
|
||||
(Object.create
|
||||
? function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return m[k];
|
||||
}
|
||||
};
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}
|
||||
: function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
});
|
||||
var __exportStar =
|
||||
(this && this.__exportStar) ||
|
||||
function (m, exports) {
|
||||
for (var p in m)
|
||||
if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, '__esModule', {value: true});
|
||||
__exportStar(require('./utils'), exports);
|
|
@ -1,2 +0,0 @@
|
|||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', {value: true});
|
|
@ -1,29 +0,0 @@
|
|||
'use strict';
|
||||
var __createBinding =
|
||||
(this && this.__createBinding) ||
|
||||
(Object.create
|
||||
? function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return m[k];
|
||||
}
|
||||
};
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}
|
||||
: function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
});
|
||||
var __exportStar =
|
||||
(this && this.__exportStar) ||
|
||||
function (m, exports) {
|
||||
for (var p in m)
|
||||
if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, '__esModule', {value: true});
|
||||
__exportStar(require('./big-o'), exports);
|
|
@ -1 +1,2 @@
|
|||
export * from './big-o';
|
||||
export * from './json2html';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {RBTNColor, RBTreeNode, RedBlackTree, NIL} from '../../../../src';
|
||||
import {getRandomInt} from '../../../utils';
|
||||
import {NIL, RBTNColor, RBTreeNode, RedBlackTree} from '../../../../src';
|
||||
import {randomInt} from '../../../utils';
|
||||
import {isDebugTest} from '../../../config';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
|
@ -428,8 +428,8 @@ describe('RedBlackTree', () => {
|
|||
|
||||
it('should fix the tree after insertion and deletion', () => {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
tree.insert(getRandomInt(-100, 1000));
|
||||
tree.delete(getRandomInt(-100, 1000));
|
||||
tree.insert(randomInt(-100, 1000));
|
||||
tree.delete(randomInt(-100, 1000));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {DoublyLinkedList, DoublyLinkedListNode} from '../../../../src';
|
||||
import {bigO, magnitude} from '../../../utils';
|
||||
|
||||
describe('DoublyLinkedListNode', () => {
|
||||
it('should DoublyLinkedListNode', () => {
|
||||
|
@ -399,25 +398,3 @@ describe('DoublyLinkedList Operation Test', () => {
|
|||
expect(shiftedObj).toBe(obj1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DoublyLinkedList Performance Test', () => {
|
||||
it('should the push and pop methods adhere to a time complexity of O(n) and executed correctly under large scale data', () => {
|
||||
const list = new DoublyLinkedList<number>();
|
||||
|
||||
const startPushTime = performance.now();
|
||||
for (let i = 0; i < magnitude.LINEAR; i++) {
|
||||
list.unshift(i);
|
||||
}
|
||||
expect(performance.now() - startPushTime).toBeLessThan(bigO.LINEAR * 20);
|
||||
|
||||
expect(list.length).toBeGreaterThan(0);
|
||||
const startPopTime = performance.now();
|
||||
for (let i = 0; i < magnitude.LINEAR; i++) {
|
||||
list.shift();
|
||||
}
|
||||
expect(performance.now() - startPopTime).toBeLessThan(bigO.LINEAR * 100);
|
||||
|
||||
expect(list.pop()).toBeUndefined();
|
||||
expect(list.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,35 +1,8 @@
|
|||
import {DoublyLinkedList, DoublyLinkedListNode, SinglyLinkedList, SinglyLinkedListNode} from '../../../../src';
|
||||
import {bigO, magnitude} from '../../../utils';
|
||||
// import {DoublyLinkedList, DoublyLinkedListNode, SinglyLinkedList, SinglyLinkedListNode} from '../../../../src';
|
||||
// import {bigO, magnitude} from '../../../utils';
|
||||
|
||||
describe('LinkedList Performance Test', () => {
|
||||
it('should DoublyLinkedList insertBefore faster than SinglyLinkedList', () => {
|
||||
const doublyList = new DoublyLinkedList<number>();
|
||||
|
||||
const startPushTime = performance.now();
|
||||
let midNode: DoublyLinkedListNode | null = null;
|
||||
const midIndex = Math.floor(magnitude.SQUARED / 2);
|
||||
for (let i = 0; i < magnitude.SQUARED; i++) {
|
||||
doublyList.push(i);
|
||||
if (i === midIndex) {
|
||||
midNode = doublyList.getNode(i);
|
||||
} else if (i > midIndex && midNode) {
|
||||
doublyList.insertBefore(midNode, i);
|
||||
}
|
||||
}
|
||||
const doublyListPushCost = performance.now() - startPushTime;
|
||||
|
||||
const singlyList = new SinglyLinkedList<number>();
|
||||
let midSinglyNode: SinglyLinkedListNode | null = null;
|
||||
|
||||
for (let i = 0; i < magnitude.SQUARED; i++) {
|
||||
singlyList.push(i);
|
||||
if (i === midIndex) {
|
||||
midSinglyNode = singlyList.getNode(i);
|
||||
} else if (i > midIndex && midSinglyNode) {
|
||||
singlyList.insertBefore(midSinglyNode.value, i);
|
||||
}
|
||||
}
|
||||
|
||||
expect(doublyListPushCost).toBeLessThan(bigO.SQUARED * 10);
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {SinglyLinkedList, SinglyLinkedListNode} from '../../../../src';
|
||||
import {bigO, magnitude} from '../../../utils';
|
||||
|
||||
describe('SinglyLinkedListNode', () => {
|
||||
it('should SinglyLinkedList', () => {
|
||||
|
@ -395,26 +394,6 @@ describe('SinglyLinkedList Operation Test', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('SinglyLinkedList Performance Test', () => {
|
||||
it('should the push and pop methods adhere to a time complexity of O(n) and executed correctly under large scale data', () => {
|
||||
const list = new SinglyLinkedList<number>();
|
||||
|
||||
const startPushTime = performance.now();
|
||||
for (let i = 0; i < magnitude.LINEAR; i++) {
|
||||
list.push(i);
|
||||
}
|
||||
expect(performance.now() - startPushTime).toBeLessThan(bigO.LINEAR * 20);
|
||||
|
||||
const startPopTime = performance.now();
|
||||
|
||||
for (let i = 0; i < magnitude.LINEAR; i++) {
|
||||
list.pop();
|
||||
}
|
||||
|
||||
// expect(performance.now() - startPopTime).toBeLessThan(bigO.LINEAR);
|
||||
expect(performance.now() - startPopTime).toBeLessThan(bigO.LINEAR * 400);
|
||||
});
|
||||
});
|
||||
describe('SinglyLinkedList', () => {
|
||||
let list: SinglyLinkedList<number>;
|
||||
|
||||
|
|
|
@ -315,7 +315,7 @@ describe('Matrix2D', () => {
|
|||
it('should correctly rotate a matrix', () => {
|
||||
const radians = Math.PI / 4; // 45 degrees
|
||||
const rotationMatrix = Matrix2D.rotate(radians);
|
||||
console.log(JSON.stringify(rotationMatrix.m));
|
||||
isDebug && console.log(JSON.stringify(rotationMatrix.m));
|
||||
expect(rotationMatrix.m).toEqual([
|
||||
[0.7071067811865476, -0.7071067811865475, 0],
|
||||
[0.7071067811865475, 0.7071067811865476, 0],
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {MaxPriorityQueue} from '../../../../src';
|
||||
import {bigO, magnitude} from '../../../utils';
|
||||
|
||||
describe('MaxPriorityQueue Operation Test', () => {
|
||||
it('should add elements and maintain heap property', () => {
|
||||
|
@ -72,34 +71,3 @@ describe('MaxPriorityQueue Operation Test', () => {
|
|||
expect(maxPQ.poll()?.keyA).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('MaxPriorityQueue Performance Test', () => {
|
||||
it('should the poll method adheres to a time complexity of O(log n) and executed correctly under large scale distinct data', () => {
|
||||
const nodes = Array.from(
|
||||
new Set<number>(Array.from(new Array(magnitude.LINEAR), () => Math.floor(Math.random() * magnitude.LINEAR * 100)))
|
||||
);
|
||||
expect(nodes.length).toBeGreaterThan(magnitude.LINEAR / 2);
|
||||
const maxPQ = new MaxPriorityQueue<number>();
|
||||
maxPQ.refill(nodes);
|
||||
let prev = Number.MAX_SAFE_INTEGER;
|
||||
const startTime = performance.now();
|
||||
while (maxPQ.size > 0) {
|
||||
const polled = maxPQ.poll();
|
||||
if (polled) {
|
||||
prev = polled;
|
||||
}
|
||||
}
|
||||
const cost = performance.now() - startTime;
|
||||
expect(cost).toBeLessThan(bigO.LINEAR * 30);
|
||||
expect(prev).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should sorted.length to be the same as original data', () => {
|
||||
// const magnitude = 1000;
|
||||
// const maxPriorityQueue = new MaxPriorityQueue<number>({nodes: Array.from(new Array<number>(magnitude), () => Math.floor(Math.random() * magnitude))});
|
||||
// const nodeCount = maxPriorityQueue.getNodes().length;
|
||||
// const sorted = maxPriorityQueue.sort();
|
||||
//
|
||||
// expect(sorted.length).toBe(nodeCount); // TODO Plan to support sorting of duplicate elements.
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {PriorityQueue} from '../../../../src';
|
||||
import {getRandomInt} from '../../../utils';
|
||||
import {randomInt} from '../../../utils';
|
||||
|
||||
describe('PriorityQueue Operation Test', () => {
|
||||
it('should PriorityQueue poll, pee, heapify, toArray work well', function () {
|
||||
|
@ -44,7 +44,7 @@ describe('PriorityQueue Operation Test', () => {
|
|||
|
||||
describe('Priority Queue Performance Test', () => {
|
||||
it('should numeric heap work well', function () {
|
||||
const values = Array.from(new Array(10000), () => getRandomInt(1, 10000000));
|
||||
const values = Array.from(new Array(10000), () => randomInt(1, 10000000));
|
||||
const minPriorityQueue = new PriorityQueue<number>({comparator: (a, b) => a - b});
|
||||
minPriorityQueue.refill(values);
|
||||
const sorted = minPriorityQueue.sort();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {LinkedListQueue, Queue} from '../../../../src';
|
||||
import {bigO, magnitude} from '../../../utils';
|
||||
import {bigO} from '../../../utils';
|
||||
import {isDebugTest} from '../../../config';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
|
@ -17,23 +17,6 @@ describe('Queue Operation Test', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Queue Performance Test', () => {
|
||||
it('should numeric queue performance well', function () {
|
||||
const queue = new Queue<number>();
|
||||
for (let i = 0; i < magnitude.LINEAR; i++) {
|
||||
queue.enqueue(i);
|
||||
}
|
||||
let last: number | undefined = 0;
|
||||
|
||||
const startTime = performance.now();
|
||||
for (let i = 0; i < magnitude.LINEAR; i++) {
|
||||
last = queue.dequeue();
|
||||
}
|
||||
expect(last).toBe(magnitude.LINEAR - 1);
|
||||
expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Queue', () => {
|
||||
let queue: Queue<number>;
|
||||
|
||||
|
@ -51,24 +34,8 @@ describe('Queue', () => {
|
|||
expect(queue.peek()).toBe(1);
|
||||
expect(queue.size).toBe(2);
|
||||
});
|
||||
|
||||
// it('should shift elements from the front of the queue', () => {
|
||||
// queue.push(1);
|
||||
// queue.push(2);
|
||||
// const shifted = queue.shift();
|
||||
// expect(shifted).toBe(1);
|
||||
// expect(queue.peek()).toBe(2);
|
||||
// expect(queue.size).toBe(1);
|
||||
// });
|
||||
//
|
||||
// it('should peek at the front of the queue', () => {
|
||||
// queue.push(1);
|
||||
// queue.push(2);
|
||||
// expect(queue.peek()).toBe(1);
|
||||
// });
|
||||
|
||||
// Add more test cases for other methods of Queue.
|
||||
});
|
||||
|
||||
describe('Queue', () => {
|
||||
let queue: Queue<number>;
|
||||
|
||||
|
@ -196,8 +163,6 @@ describe('LinkedListQueue', () => {
|
|||
queue.enqueue('B');
|
||||
expect(queue.peek()).toBe('A');
|
||||
});
|
||||
|
||||
// Add more test cases for other methods of LinkedListQueue.
|
||||
});
|
||||
|
||||
describe('Queue Performance Test', () => {
|
||||
|
@ -224,7 +189,6 @@ describe('Queue Performance Test', () => {
|
|||
for (let i = 0; i < dataSize; i++) {
|
||||
queue2.shift();
|
||||
}
|
||||
console.log(`Array Performance Test: ${performance.now() - startTime2} ms`);
|
||||
expect(performance.now() - startTime2).toBeLessThan(bigO.CUBED * 100);
|
||||
});
|
||||
|
||||
|
@ -237,7 +201,7 @@ describe('Queue Performance Test', () => {
|
|||
for (let i = 0; i < dataSize; i++) {
|
||||
queue.dequeue();
|
||||
}
|
||||
console.log(`LinkedListQueue Performance Test: ${performance.now() - startTime} ms`);
|
||||
// console.log(`LinkedListQueue Performance Test: ${performance.now() - startTime} ms`);
|
||||
expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100);
|
||||
});
|
||||
});
|
||||
|
|
5
test/utils/array.ts
Normal file
5
test/utils/array.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import {randomInt} from './number';
|
||||
|
||||
export const randomIntArray = (length: number, min: number = -1000, max: number = 1000) => {
|
||||
return new Array<number>(length).fill(0).map(() => randomInt(min, max));
|
||||
};
|
|
@ -1,222 +0,0 @@
|
|||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', {value: true});
|
||||
exports.logBigOMetrics = exports.logBigOMetricsWrap = exports.bigO = exports.magnitude = void 0;
|
||||
var config_1 = require('../config');
|
||||
|
||||
var isDebug = config_1.isDebugTest;
|
||||
var orderReducedBy = 2; // reduction of bigO's order compared to the baseline bigO
|
||||
exports.magnitude = {
|
||||
CONSTANT: Math.floor(Number.MAX_SAFE_INTEGER / Math.pow(10, orderReducedBy)),
|
||||
LOG_N: Math.pow(10, 9 - orderReducedBy),
|
||||
LINEAR: Math.pow(10, 6 - orderReducedBy),
|
||||
N_LOG_N: Math.pow(10, 5 - orderReducedBy),
|
||||
SQUARED: Math.pow(10, 4 - orderReducedBy),
|
||||
CUBED: Math.pow(10, 3 - orderReducedBy),
|
||||
FACTORIAL: 20 - orderReducedBy
|
||||
};
|
||||
exports.bigO = {
|
||||
CONSTANT: exports.magnitude.CONSTANT / 100000,
|
||||
LOG_N: Math.log2(exports.magnitude.LOG_N) / 1000,
|
||||
LINEAR: exports.magnitude.LINEAR / 1000,
|
||||
N_LOG_N: (exports.magnitude.N_LOG_N * Math.log2(exports.magnitude.LOG_N)) / 1000,
|
||||
SQUARED: Math.pow(exports.magnitude.SQUARED, 2) / 1000,
|
||||
CUBED: Math.pow(exports.magnitude.SQUARED, 3) / 1000,
|
||||
FACTORIAL: 10000
|
||||
};
|
||||
|
||||
function findPotentialN(input) {
|
||||
var longestArray = [];
|
||||
var mostProperties = {};
|
||||
|
||||
function recurse(obj) {
|
||||
if (Array.isArray(obj)) {
|
||||
if (obj.length > longestArray.length) {
|
||||
longestArray = obj;
|
||||
}
|
||||
} else if (typeof obj === 'object' && obj !== null) {
|
||||
var keys = Object.keys(obj);
|
||||
if (keys.length > Object.keys(mostProperties).length) {
|
||||
mostProperties = obj;
|
||||
}
|
||||
keys.forEach(function (key) {
|
||||
recurse(obj[key]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(input)) {
|
||||
input.forEach(function (item) {
|
||||
recurse(item);
|
||||
});
|
||||
} else {
|
||||
recurse(input);
|
||||
}
|
||||
// return [longestArray, mostProperties] : [any[], { [key: string]: any }];
|
||||
return Math.max(longestArray.length, Object.keys(mostProperties).length);
|
||||
}
|
||||
|
||||
function linearRegression(x, y) {
|
||||
var n = x.length;
|
||||
var sumX = x.reduce(function (acc, val) {
|
||||
return acc + val;
|
||||
}, 0);
|
||||
var sumY = y.reduce(function (acc, val) {
|
||||
return acc + val;
|
||||
}, 0);
|
||||
var sumXSquared = x.reduce(function (acc, val) {
|
||||
return acc + Math.pow(val, 2);
|
||||
}, 0);
|
||||
var sumXY = x.reduce(function (acc, val, i) {
|
||||
return acc + val * y[i];
|
||||
}, 0);
|
||||
var slope = (n * sumXY - sumX * sumY) / (n * sumXSquared - Math.pow(sumX, 2));
|
||||
var intercept = (sumY - slope * sumX) / n;
|
||||
var yHat = x.map(function (val) {
|
||||
return slope * val + intercept;
|
||||
});
|
||||
var totalVariation = y
|
||||
.map(function (val, i) {
|
||||
return Math.pow(val - yHat[i], 2);
|
||||
})
|
||||
.reduce(function (acc, val) {
|
||||
return acc + val;
|
||||
}, 0);
|
||||
var explainedVariation = y
|
||||
.map(function (val) {
|
||||
return Math.pow(val - sumY / n, 2);
|
||||
})
|
||||
.reduce(function (acc, val) {
|
||||
return acc + val;
|
||||
}, 0);
|
||||
var rSquared = 1 - totalVariation / explainedVariation;
|
||||
return {slope: slope, intercept: intercept, rSquared: rSquared};
|
||||
}
|
||||
|
||||
function estimateBigO(runtimes, dataSizes) {
|
||||
// Make sure the input runtimes and data sizes have the same length
|
||||
if (runtimes.length !== dataSizes.length) {
|
||||
return 'Lengths of input arrays do not match';
|
||||
}
|
||||
// Create an array to store the computational complexity of each data point
|
||||
var complexities = [];
|
||||
// Traverse different possible complexities
|
||||
var complexitiesToCheck = [
|
||||
'O(1)',
|
||||
'O(log n)',
|
||||
'O(n)',
|
||||
'O(n log n)',
|
||||
'O(n^2)' // squared time complexity
|
||||
];
|
||||
var _loop_1 = function (complexity) {
|
||||
// Calculate data points for fitting
|
||||
var fittedData = dataSizes.map(function (size) {
|
||||
if (complexity === 'O(1)') {
|
||||
return 1; // constant time complexity
|
||||
} else if (complexity === 'O(log n)') {
|
||||
return Math.log(size);
|
||||
} else if (complexity === 'O(n)') {
|
||||
return size;
|
||||
} else if (complexity === 'O(n log n)') {
|
||||
return size * Math.log(size);
|
||||
} else if (complexity === 'O(n^2)') {
|
||||
return Math.pow(size, 2);
|
||||
} else {
|
||||
return Math.pow(size, 10);
|
||||
}
|
||||
});
|
||||
// Fit the data points using linear regression analysis
|
||||
var regressionResult = linearRegression(fittedData, runtimes);
|
||||
// Check the R-squared value of the fit. It is usually considered a valid fit if it is greater than 0.9.
|
||||
if (regressionResult.rSquared >= 0.9) {
|
||||
complexities.push(complexity);
|
||||
}
|
||||
};
|
||||
for (var _i = 0, complexitiesToCheck_1 = complexitiesToCheck; _i < complexitiesToCheck_1.length; _i++) {
|
||||
var complexity = complexitiesToCheck_1[_i];
|
||||
_loop_1(complexity);
|
||||
}
|
||||
// If there is no valid fitting result, return "cannot estimate", otherwise return the estimated time complexity
|
||||
if (complexities.length === 0) {
|
||||
return 'Unable to estimate';
|
||||
} else {
|
||||
return complexities.join(' or ');
|
||||
}
|
||||
}
|
||||
|
||||
var methodLogs = new Map();
|
||||
|
||||
function logBigOMetricsWrap(fn, args, fnName) {
|
||||
var startTime = performance.now();
|
||||
var result = fn(args);
|
||||
var endTime = performance.now();
|
||||
var runTime = endTime - startTime;
|
||||
var methodName = ''.concat(fnName);
|
||||
if (!methodLogs.has(methodName)) {
|
||||
methodLogs.set(methodName, []);
|
||||
}
|
||||
var methodLog = methodLogs.get(methodName);
|
||||
var maxDataSize = args.length === 1 && typeof args[0] === 'number' ? args[0] : findPotentialN(args);
|
||||
if (methodLog) {
|
||||
methodLog.push([runTime, maxDataSize]);
|
||||
if (methodLog.length >= 20) {
|
||||
isDebug && console.log('triggered', methodName, methodLog);
|
||||
var bigO_1 = estimateBigO(
|
||||
methodLog.map(function (_a) {
|
||||
var runTime = _a[0];
|
||||
return runTime;
|
||||
}),
|
||||
methodLog.map(function (_a) {
|
||||
var runTime = _a[0];
|
||||
return runTime;
|
||||
})
|
||||
);
|
||||
isDebug && console.log('Estimated Big O: '.concat(bigO_1));
|
||||
methodLogs.delete(methodName);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
exports.logBigOMetricsWrap = logBigOMetricsWrap;
|
||||
|
||||
function logBigOMetrics(target, propertyKey, descriptor) {
|
||||
var originalMethod = descriptor.value;
|
||||
descriptor.value = function () {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
var startTime = performance.now();
|
||||
var result = originalMethod.apply(this, args);
|
||||
var endTime = performance.now();
|
||||
var runTime = endTime - startTime;
|
||||
var methodName = ''.concat(target.constructor.name, '.').concat(propertyKey);
|
||||
if (!methodLogs.has(methodName)) {
|
||||
methodLogs.set(methodName, []);
|
||||
}
|
||||
var methodLog = methodLogs.get(methodName);
|
||||
var maxDataSize = args.length === 1 && typeof args[0] === 'number' ? args[0] : findPotentialN(args);
|
||||
if (methodLog) {
|
||||
methodLog.push([runTime, maxDataSize]);
|
||||
if (methodLog.length >= 20) {
|
||||
isDebug && console.log('triggered', methodName, methodLog);
|
||||
var bigO_2 = estimateBigO(
|
||||
methodLog.map(function (_a) {
|
||||
var runTime = _a[0];
|
||||
return runTime;
|
||||
}),
|
||||
methodLog.map(function (_a) {
|
||||
var runTime = _a[0];
|
||||
return runTime;
|
||||
})
|
||||
);
|
||||
isDebug && console.log('Estimated Big O: '.concat(bigO_2));
|
||||
methodLogs.delete(methodName);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
exports.logBigOMetrics = logBigOMetrics;
|
|
@ -2,15 +2,15 @@ import {AnyFunction} from '../types';
|
|||
import {isDebugTest} from '../config';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
const orderReducedBy = 2; // reduction of bigO's order compared to the baseline bigO
|
||||
const orderReducedBy = 1; // reduction of bigO's order compared to the baseline bigO
|
||||
|
||||
export const magnitude = {
|
||||
CONSTANT: Math.floor(Number.MAX_SAFE_INTEGER / Math.pow(10, orderReducedBy)),
|
||||
LOG_N: Math.pow(10, 9 - orderReducedBy),
|
||||
LINEAR: Math.pow(10, 6 - orderReducedBy),
|
||||
N_LOG_N: Math.pow(10, 5 - orderReducedBy),
|
||||
SQUARED: Math.pow(10, 4 - orderReducedBy),
|
||||
CUBED: Math.pow(10, 3 - orderReducedBy),
|
||||
CONSTANT: Math.pow(10, 9),
|
||||
LOG_N: Math.pow(10, 8 - orderReducedBy),
|
||||
LINEAR: Math.pow(10, 7 - orderReducedBy),
|
||||
N_LOG_N: Math.pow(10, 4 - orderReducedBy),
|
||||
SQUARED: Math.pow(10, 3 - orderReducedBy),
|
||||
CUBED: Math.pow(10, 2 - orderReducedBy),
|
||||
FACTORIAL: 20 - orderReducedBy
|
||||
};
|
||||
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
'use strict';
|
||||
var __createBinding =
|
||||
(this && this.__createBinding) ||
|
||||
(Object.create
|
||||
? function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return m[k];
|
||||
}
|
||||
};
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}
|
||||
: function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
});
|
||||
var __exportStar =
|
||||
(this && this.__exportStar) ||
|
||||
function (m, exports) {
|
||||
for (var p in m)
|
||||
if (p !== 'default' && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, '__esModule', {value: true});
|
||||
__exportStar(require('./number'), exports);
|
||||
__exportStar(require('./big-o'), exports);
|
|
@ -1,2 +1,6 @@
|
|||
export * from './number';
|
||||
export * from './array';
|
||||
export * from './big-o';
|
||||
export * from './json2html';
|
||||
export * from './is';
|
||||
export * from './console';
|
||||
|
|
|
@ -104,6 +104,7 @@ function drawTable(arr: any[]): string {
|
|||
function drawRow(headers: string[], rowObj: any): string {
|
||||
return '<td>' + headers.map(header => rowObj[header]).join('</td><td>') + '</td>';
|
||||
}
|
||||
|
||||
const cols = Object.keys(arr[0]);
|
||||
const content = arr.map(rowObj => drawRow(cols, rowObj));
|
||||
const headingHtml = '<tr><th>' + cols.join('</th><th>') + '</th></tr>';
|
|
@ -1,14 +0,0 @@
|
|||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', {value: true});
|
||||
exports.getMSB = exports.getRandomInt = void 0;
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
exports.getRandomInt = getRandomInt;
|
||||
var getMSB = function (value) {
|
||||
if (value <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return 1 << (31 - Math.clz32(value));
|
||||
};
|
||||
exports.getMSB = getMSB;
|
|
@ -1,3 +1,13 @@
|
|||
export function getRandomInt(min: number, max: number) {
|
||||
export function randomInt(min: number, max: number) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
export function numberFix(num: number, decimalPlaces: number): string {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue