data-structure-typed 1.36.7 → 1.36.9
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/.eslintrc.js +1 -1
- package/CHANGELOG.md +3 -1
- package/README.md +8 -0
- package/dist/data-structures/binary-tree/avl-tree.d.ts +5 -5
- package/dist/data-structures/binary-tree/avl-tree.js +6 -6
- package/dist/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/data-structures/binary-tree/binary-tree.d.ts +18 -85
- package/dist/data-structures/binary-tree/binary-tree.js +76 -128
- package/dist/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/data-structures/binary-tree/tree-multiset.d.ts +9 -16
- package/dist/data-structures/binary-tree/tree-multiset.js +10 -20
- package/dist/data-structures/binary-tree/tree-multiset.js.map +1 -1
- package/dist/data-structures/hash/hash-map.d.ts +1 -1
- package/dist/data-structures/hash/hash-map.js +1 -1
- package/dist/data-structures/hash/hash-table.d.ts +3 -3
- package/dist/data-structures/hash/hash-table.js +3 -3
- package/dist/data-structures/heap/heap.d.ts +136 -11
- package/dist/data-structures/heap/heap.js +293 -13
- package/dist/data-structures/heap/heap.js.map +1 -1
- package/dist/data-structures/linked-list/skip-linked-list.d.ts +3 -3
- package/dist/data-structures/linked-list/skip-linked-list.js +3 -3
- package/dist/data-structures/queue/deque.d.ts +2 -2
- package/dist/data-structures/queue/deque.js +2 -2
- package/dist/data-structures/queue/queue.js +1 -1
- package/dist/data-structures/trie/trie.d.ts +2 -2
- package/dist/data-structures/trie/trie.js +2 -2
- package/dist/interfaces/binary-tree.d.ts +1 -1
- package/lib/data-structures/binary-tree/avl-tree.d.ts +5 -5
- package/lib/data-structures/binary-tree/avl-tree.js +6 -6
- package/lib/data-structures/binary-tree/binary-tree.d.ts +18 -85
- package/lib/data-structures/binary-tree/binary-tree.js +76 -128
- package/lib/data-structures/binary-tree/tree-multiset.d.ts +9 -16
- package/lib/data-structures/binary-tree/tree-multiset.js +10 -20
- package/lib/data-structures/hash/hash-map.d.ts +1 -1
- package/lib/data-structures/hash/hash-map.js +1 -1
- package/lib/data-structures/hash/hash-table.d.ts +3 -3
- package/lib/data-structures/hash/hash-table.js +3 -3
- package/lib/data-structures/heap/heap.d.ts +136 -11
- package/lib/data-structures/heap/heap.js +290 -12
- package/lib/data-structures/linked-list/skip-linked-list.d.ts +3 -3
- package/lib/data-structures/linked-list/skip-linked-list.js +3 -3
- package/lib/data-structures/queue/deque.d.ts +2 -2
- package/lib/data-structures/queue/deque.js +2 -2
- package/lib/data-structures/queue/queue.js +1 -1
- package/lib/data-structures/trie/trie.d.ts +2 -2
- package/lib/data-structures/trie/trie.js +2 -2
- package/lib/interfaces/binary-tree.d.ts +1 -1
- package/package.json +7 -6
- package/src/data-structures/binary-tree/avl-tree.ts +6 -6
- package/src/data-structures/binary-tree/binary-tree.ts +79 -214
- package/src/data-structures/binary-tree/rb-tree.ts +3 -3
- package/src/data-structures/binary-tree/tree-multiset.ts +10 -21
- package/src/data-structures/hash/hash-map.ts +1 -1
- package/src/data-structures/hash/hash-table.ts +3 -3
- package/src/data-structures/heap/heap.ts +340 -16
- package/src/data-structures/linked-list/skip-linked-list.ts +3 -3
- package/src/data-structures/queue/deque.ts +2 -2
- package/src/data-structures/queue/queue.ts +1 -1
- package/src/data-structures/trie/trie.ts +2 -2
- package/src/interfaces/binary-tree.ts +1 -1
- package/test/types/index.ts +1 -0
- package/test/types/utils/big-o.ts +1 -0
- package/test/types/utils/index.ts +1 -0
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +14 -14
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/bst.test.ts +28 -28
- package/test/unit/data-structures/binary-tree/overall.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/rb-tree.test.ts +1 -1
- package/test/unit/data-structures/binary-tree/tree-multiset.test.ts +28 -28
- package/test/unit/data-structures/graph/directed-graph.test.ts +1 -1
- package/test/unit/data-structures/graph/undirected-graph.test.ts +1 -1
- package/test/unit/data-structures/hash/hash-map.test.ts +2 -2
- package/test/unit/data-structures/hash/hash-table.test.ts +5 -5
- package/test/unit/data-structures/heap/heap.test.ts +192 -1
- package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +7 -7
- package/test/unit/data-structures/linked-list/skip-list.test.ts +2 -2
- package/test/unit/data-structures/priority-queue/max-priority-queue.test.ts +1 -1
- package/test/unit/data-structures/queue/deque.test.ts +3 -3
- package/test/unit/data-structures/trie/trie.test.ts +5 -5
- package/test/utils/big-o.ts +199 -0
- package/test/utils/index.ts +1 -1
- package/umd/bundle.min.js +1 -1
- package/umd/bundle.min.js.map +1 -1
- package/test/utils/magnitude.ts +0 -21
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {MaxHeap, MinHeap} from '../../../../src';
|
|
1
|
+
import {FibonacciHeap, MaxHeap, MinHeap} from '../../../../src';
|
|
2
|
+
import {logBigOMetricsWrap} from '../../../utils';
|
|
2
3
|
|
|
3
4
|
describe('Heap Operation Test', () => {
|
|
4
5
|
it('should numeric heap work well', function () {
|
|
@@ -60,3 +61,193 @@ describe('Heap Operation Test', () => {
|
|
|
60
61
|
}
|
|
61
62
|
});
|
|
62
63
|
});
|
|
64
|
+
|
|
65
|
+
describe('FibonacciHeap', () => {
|
|
66
|
+
let heap: FibonacciHeap<number>;
|
|
67
|
+
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
heap = new FibonacciHeap<number>();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('push & peek', () => {
|
|
73
|
+
heap.push(10);
|
|
74
|
+
heap.push(5);
|
|
75
|
+
expect(heap.peek()).toBe(5);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('pop', () => {
|
|
79
|
+
heap.push(10);
|
|
80
|
+
heap.push(5);
|
|
81
|
+
heap.push(15);
|
|
82
|
+
expect(heap.pop()).toBe(5);
|
|
83
|
+
expect(heap.pop()).toBe(10);
|
|
84
|
+
expect(heap.pop()).toBe(15);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('pop on an empty heap', () => {
|
|
88
|
+
expect(heap.pop()).toBeUndefined();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('size', () => {
|
|
92
|
+
expect(heap.size).toBe(0);
|
|
93
|
+
heap.push(10);
|
|
94
|
+
expect(heap.size).toBe(1);
|
|
95
|
+
heap.pop();
|
|
96
|
+
expect(heap.size).toBe(0);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('clear', () => {
|
|
100
|
+
heap.push(10);
|
|
101
|
+
heap.push(5);
|
|
102
|
+
heap.clear();
|
|
103
|
+
expect(heap.size).toBe(0);
|
|
104
|
+
expect(heap.peek()).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('custom comparator', () => {
|
|
108
|
+
const maxHeap = new FibonacciHeap<number>((a, b) => b - a);
|
|
109
|
+
maxHeap.push(10);
|
|
110
|
+
maxHeap.push(5);
|
|
111
|
+
expect(maxHeap.peek()).toBe(10);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('FibonacciHeap', () => {
|
|
116
|
+
let heap: FibonacciHeap<number>;
|
|
117
|
+
|
|
118
|
+
beforeEach(() => {
|
|
119
|
+
heap = new FibonacciHeap<number>();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should initialize an empty heap', () => {
|
|
123
|
+
expect(heap.size).toBe(0);
|
|
124
|
+
expect(heap.peek()).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should push items into the heap and update size', () => {
|
|
128
|
+
heap.push(10);
|
|
129
|
+
heap.push(5);
|
|
130
|
+
|
|
131
|
+
expect(heap.size).toBe(2);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should peek the minimum item', () => {
|
|
135
|
+
heap.push(10);
|
|
136
|
+
heap.push(5);
|
|
137
|
+
heap.push(15);
|
|
138
|
+
|
|
139
|
+
expect(heap.peek()).toBe(5);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should pop the minimum item and update size', () => {
|
|
143
|
+
heap.push(10);
|
|
144
|
+
heap.push(5);
|
|
145
|
+
heap.push(15);
|
|
146
|
+
|
|
147
|
+
const minItem = heap.pop();
|
|
148
|
+
|
|
149
|
+
expect(minItem).toBe(5);
|
|
150
|
+
expect(heap.size).toBe(2);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should correctly merge two heaps', () => {
|
|
154
|
+
const heap1 = new FibonacciHeap<number>();
|
|
155
|
+
const heap2 = new FibonacciHeap<number>();
|
|
156
|
+
|
|
157
|
+
heap1.push(10);
|
|
158
|
+
heap2.push(5);
|
|
159
|
+
|
|
160
|
+
heap1.merge(heap2);
|
|
161
|
+
|
|
162
|
+
expect(heap1.size).toBe(2);
|
|
163
|
+
expect(heap1.peek()).toBe(5);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should clear the heap', () => {
|
|
167
|
+
heap.push(10);
|
|
168
|
+
heap.push(5);
|
|
169
|
+
|
|
170
|
+
heap.clear();
|
|
171
|
+
|
|
172
|
+
expect(heap.size).toBe(0);
|
|
173
|
+
expect(heap.peek()).toBeUndefined();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should handle custom comparators', () => {
|
|
177
|
+
const customComparator = (a: number, b: number) => b - a;
|
|
178
|
+
const customHeap = new FibonacciHeap<number>(customComparator);
|
|
179
|
+
|
|
180
|
+
customHeap.push(10);
|
|
181
|
+
customHeap.push(5);
|
|
182
|
+
customHeap.push(15);
|
|
183
|
+
|
|
184
|
+
expect(customHeap.peek()).toBe(15);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('FibonacciHeap Merge', () => {
|
|
188
|
+
it('should merge two Fibonacci heaps correctly', () => {
|
|
189
|
+
const heap1 = new FibonacciHeap<number>();
|
|
190
|
+
heap1.push(5).push(10);
|
|
191
|
+
|
|
192
|
+
const heap2 = new FibonacciHeap<number>();
|
|
193
|
+
heap2.push(3).push(7);
|
|
194
|
+
|
|
195
|
+
heap1.merge(heap2);
|
|
196
|
+
|
|
197
|
+
expect(heap1.size).toBe(4); // Combined size of both heaps
|
|
198
|
+
expect(heap2.size).toBe(0); // Merged heap should be empty
|
|
199
|
+
expect(heap1.peek()).toBe(3); // Minimum element should be 3
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('FibonacciHeap Stress Test', () => {
|
|
205
|
+
it('should handle a large number of elements efficiently', () => {
|
|
206
|
+
const testByMagnitude = (magnitude: number) => {
|
|
207
|
+
const heap = new FibonacciHeap<number>();
|
|
208
|
+
|
|
209
|
+
// Add 1000 elements to the heap
|
|
210
|
+
for (let i = 1; i <= magnitude; i++) {
|
|
211
|
+
heap.push(i);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Verify that the minimum element is 1 (smallest element)
|
|
215
|
+
expect(heap.peek()).toBe(1);
|
|
216
|
+
|
|
217
|
+
// Remove all 1000 elements from the heap
|
|
218
|
+
const elements = [];
|
|
219
|
+
while (heap.size > 0) {
|
|
220
|
+
elements.push(heap.pop());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Verify that all elements were removed in ascending order
|
|
224
|
+
for (let i = 1; i <= magnitude; i++) {
|
|
225
|
+
expect(elements[i - 1]).toBe(i);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Verify that the heap is now empty
|
|
229
|
+
expect(heap.size).toBe(0);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
testByMagnitude(1000);
|
|
233
|
+
|
|
234
|
+
// [
|
|
235
|
+
// 10, 100, 1000, 5000, 10000, 20000, 50000, 75000, 100000,
|
|
236
|
+
// 150000, 200000, 250000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000
|
|
237
|
+
// ].forEach(m => logBigOMetricsWrap<typeof testByMagnitude>(testByMagnitude, [m]));
|
|
238
|
+
[
|
|
239
|
+
10, 100, 1000, 5000, 10000, 20000, 50000, 75000, 100000, 150000, 200000, 250000, 300000, 400000, 500000, 600000,
|
|
240
|
+
700000, 800000, 900000, 1000000
|
|
241
|
+
].forEach(m =>
|
|
242
|
+
logBigOMetricsWrap(
|
|
243
|
+
(c: number) => {
|
|
244
|
+
const result: number[] = [];
|
|
245
|
+
for (let i = 0; i < c; i++) result.push(i);
|
|
246
|
+
return result;
|
|
247
|
+
},
|
|
248
|
+
[m],
|
|
249
|
+
'loopPush'
|
|
250
|
+
)
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
@@ -18,7 +18,7 @@ describe('SinglyLinkedList Operation Test', () => {
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
describe('pop', () => {
|
|
21
|
-
it('should
|
|
21
|
+
it('should delete and return the last element of the list', () => {
|
|
22
22
|
list.push(1);
|
|
23
23
|
list.push(2);
|
|
24
24
|
const popped = list.pop();
|
|
@@ -33,7 +33,7 @@ describe('SinglyLinkedList Operation Test', () => {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
describe('shift', () => {
|
|
36
|
-
it('should
|
|
36
|
+
it('should delete and return the first element of the list', () => {
|
|
37
37
|
list.push(1);
|
|
38
38
|
list.push(2);
|
|
39
39
|
const shifted = list.shift();
|
|
@@ -109,7 +109,7 @@ describe('SinglyLinkedList Operation Test', () => {
|
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
describe('removeValue', () => {
|
|
112
|
-
it('should
|
|
112
|
+
it('should delete the first occurrence of a value from the list', () => {
|
|
113
113
|
list.push(1);
|
|
114
114
|
list.push(2);
|
|
115
115
|
list.push(3);
|
|
@@ -240,8 +240,8 @@ describe('SinglyLinkedList Operation Test', () => {
|
|
|
240
240
|
});
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
-
describe('
|
|
244
|
-
it('should
|
|
243
|
+
describe('delete', () => {
|
|
244
|
+
it('should delete and return the element at the specified index', () => {
|
|
245
245
|
list.push(1);
|
|
246
246
|
list.push(2);
|
|
247
247
|
list.push(3);
|
|
@@ -256,7 +256,7 @@ describe('SinglyLinkedList Operation Test', () => {
|
|
|
256
256
|
expect(removed).toBeUndefined();
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
-
it('should
|
|
259
|
+
it('should delete and return the first element', () => {
|
|
260
260
|
list.push(1);
|
|
261
261
|
list.push(2);
|
|
262
262
|
const removed = list.deleteAt(0);
|
|
@@ -264,7 +264,7 @@ describe('SinglyLinkedList Operation Test', () => {
|
|
|
264
264
|
expect(list.toArray()).toEqual([2]);
|
|
265
265
|
});
|
|
266
266
|
|
|
267
|
-
it('should
|
|
267
|
+
it('should delete and return the last element', () => {
|
|
268
268
|
list.push(1);
|
|
269
269
|
list.push(2);
|
|
270
270
|
const removed = list.deleteAt(1);
|
|
@@ -25,12 +25,12 @@ describe('SkipList', () => {
|
|
|
25
25
|
expect(skipList.get(0)).toBeUndefined();
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
it('should
|
|
28
|
+
it('should delete elements correctly', () => {
|
|
29
29
|
skipList.add(1, 'One');
|
|
30
30
|
skipList.add(2, 'Two');
|
|
31
31
|
skipList.add(3, 'Three');
|
|
32
32
|
|
|
33
|
-
skipList.
|
|
33
|
+
skipList.delete(2);
|
|
34
34
|
|
|
35
35
|
expect(skipList.get(2)).toBeUndefined(); // 修改这里的断言
|
|
36
36
|
});
|
|
@@ -27,7 +27,7 @@ describe('MaxPriorityQueue Operation Test', () => {
|
|
|
27
27
|
expect(priorityQueue.poll()?.keyA).toBe(1);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
it('should return and
|
|
30
|
+
it('should return and delete the smallest element', () => {
|
|
31
31
|
const priorityQueue = new MaxPriorityQueue<number>();
|
|
32
32
|
priorityQueue.add(5);
|
|
33
33
|
priorityQueue.add(3);
|
|
@@ -16,7 +16,7 @@ describe('Deque Tests', () => {
|
|
|
16
16
|
expect(deque.peekLast()).toBe(2);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
it('should
|
|
19
|
+
it('should delete elements from the beginning and end', () => {
|
|
20
20
|
deque.addFirst(1);
|
|
21
21
|
deque.addLast(2);
|
|
22
22
|
deque.pollFirst();
|
|
@@ -69,7 +69,7 @@ describe('Deque Tests', () => {
|
|
|
69
69
|
expect(objectDeque.peekLast()).toBe('two');
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
it('should
|
|
72
|
+
it('should delete elements from the beginning and end', () => {
|
|
73
73
|
objectDeque.addFirst('one');
|
|
74
74
|
objectDeque.addLast('two');
|
|
75
75
|
objectDeque.pollFirst();
|
|
@@ -106,7 +106,7 @@ describe('Deque Tests', () => {
|
|
|
106
106
|
expect(arrayDeque.peekLast()).toBe(2);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
it('should
|
|
109
|
+
it('should delete elements from the beginning and end', () => {
|
|
110
110
|
arrayDeque.addFirst(1);
|
|
111
111
|
arrayDeque.addLast(2);
|
|
112
112
|
arrayDeque.pollFirst();
|
|
@@ -81,15 +81,15 @@ describe('Trie', () => {
|
|
|
81
81
|
expect(words).toEqual(['apple', 'application', 'app']);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
it('should
|
|
84
|
+
it('should delete words from Trie', () => {
|
|
85
85
|
const trie = new Trie();
|
|
86
86
|
trie.add('apple');
|
|
87
87
|
trie.add('app');
|
|
88
88
|
expect(trie.has('apple')).toBe(true);
|
|
89
|
-
trie.
|
|
89
|
+
trie.delete('apple');
|
|
90
90
|
expect(trie.has('apple')).toBe(false);
|
|
91
91
|
expect(trie.has('app')).toBe(true);
|
|
92
|
-
trie.
|
|
92
|
+
trie.delete('app');
|
|
93
93
|
expect(trie.has('app')).toBe(false);
|
|
94
94
|
});
|
|
95
95
|
|
|
@@ -772,9 +772,9 @@ describe('Trie operations', () => {
|
|
|
772
772
|
test('Remove Words', () => {
|
|
773
773
|
trie.add('apple');
|
|
774
774
|
trie.add('banana');
|
|
775
|
-
expect(trie.
|
|
775
|
+
expect(trie.delete('apple')).toBe(true);
|
|
776
776
|
expect(trie.has('apple')).toBe(false);
|
|
777
|
-
expect(trie.
|
|
777
|
+
expect(trie.delete('cherry')).toBe(false);
|
|
778
778
|
});
|
|
779
779
|
|
|
780
780
|
test('Case Sensitivity', () => {
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import {AnyFunction} from '../types';
|
|
2
|
+
|
|
3
|
+
const orderReducedBy = 2; // reduction of bigO's order compared to the baseline bigO
|
|
4
|
+
|
|
5
|
+
export const magnitude = {
|
|
6
|
+
CONSTANT: Math.floor(Number.MAX_SAFE_INTEGER / Math.pow(10, orderReducedBy)),
|
|
7
|
+
LOG_N: Math.pow(10, 9 - orderReducedBy),
|
|
8
|
+
LINEAR: Math.pow(10, 6 - orderReducedBy),
|
|
9
|
+
N_LOG_N: Math.pow(10, 5 - orderReducedBy),
|
|
10
|
+
SQUARED: Math.pow(10, 4 - orderReducedBy),
|
|
11
|
+
CUBED: Math.pow(10, 3 - orderReducedBy),
|
|
12
|
+
FACTORIAL: 20 - orderReducedBy
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const bigO = {
|
|
16
|
+
CONSTANT: magnitude.CONSTANT / 100000,
|
|
17
|
+
LOG_N: Math.log2(magnitude.LOG_N) / 1000,
|
|
18
|
+
LINEAR: magnitude.LINEAR / 1000,
|
|
19
|
+
N_LOG_N: (magnitude.N_LOG_N * Math.log2(magnitude.LOG_N)) / 1000,
|
|
20
|
+
SQUARED: Math.pow(magnitude.SQUARED, 2) / 1000,
|
|
21
|
+
CUBED: Math.pow(magnitude.SQUARED, 3) / 1000,
|
|
22
|
+
FACTORIAL: 10000
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function findPotentialN(input: any): number {
|
|
26
|
+
let longestArray: any[] = [];
|
|
27
|
+
let mostProperties: {[key: string]: any} = {};
|
|
28
|
+
|
|
29
|
+
function recurse(obj: any) {
|
|
30
|
+
if (Array.isArray(obj)) {
|
|
31
|
+
if (obj.length > longestArray.length) {
|
|
32
|
+
longestArray = obj;
|
|
33
|
+
}
|
|
34
|
+
} else if (typeof obj === 'object' && obj !== null) {
|
|
35
|
+
const keys = Object.keys(obj);
|
|
36
|
+
if (keys.length > Object.keys(mostProperties).length) {
|
|
37
|
+
mostProperties = obj;
|
|
38
|
+
}
|
|
39
|
+
keys.forEach(key => {
|
|
40
|
+
recurse(obj[key]);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (Array.isArray(input)) {
|
|
46
|
+
input.forEach(item => {
|
|
47
|
+
recurse(item);
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
recurse(input);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// return [longestArray, mostProperties] : [any[], { [key: string]: any }];
|
|
54
|
+
return Math.max(longestArray.length, Object.keys(mostProperties).length);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function linearRegression(x: number[], y: number[]) {
|
|
58
|
+
const n = x.length;
|
|
59
|
+
|
|
60
|
+
const sumX = x.reduce((acc, val) => acc + val, 0);
|
|
61
|
+
const sumY = y.reduce((acc, val) => acc + val, 0);
|
|
62
|
+
|
|
63
|
+
const sumXSquared = x.reduce((acc, val) => acc + val ** 2, 0);
|
|
64
|
+
const sumXY = x.reduce((acc, val, i) => acc + val * y[i], 0);
|
|
65
|
+
|
|
66
|
+
const slope = (n * sumXY - sumX * sumY) / (n * sumXSquared - sumX ** 2);
|
|
67
|
+
const intercept = (sumY - slope * sumX) / n;
|
|
68
|
+
|
|
69
|
+
const yHat = x.map(val => slope * val + intercept);
|
|
70
|
+
|
|
71
|
+
const totalVariation = y.map((val, i) => (val - yHat[i]) ** 2).reduce((acc, val) => acc + val, 0);
|
|
72
|
+
const explainedVariation = y.map(val => (val - sumY / n) ** 2).reduce((acc, val) => acc + val, 0);
|
|
73
|
+
|
|
74
|
+
const rSquared = 1 - totalVariation / explainedVariation;
|
|
75
|
+
|
|
76
|
+
return {slope, intercept, rSquared};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function estimateBigO(runtimes: number[], dataSizes: number[]): string {
|
|
80
|
+
// Make sure the input runtimes and data sizes have the same length
|
|
81
|
+
if (runtimes.length !== dataSizes.length) {
|
|
82
|
+
return 'Lengths of input arrays do not match';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create an array to store the computational complexity of each data point
|
|
86
|
+
const complexities: string[] = [];
|
|
87
|
+
|
|
88
|
+
// Traverse different possible complexities
|
|
89
|
+
const complexitiesToCheck: string[] = [
|
|
90
|
+
'O(1)', // constant time complexity
|
|
91
|
+
'O(log n)', // Logarithmic time complexity
|
|
92
|
+
'O(n)', // linear time complexity
|
|
93
|
+
'O(n log n)', // linear logarithmic time complexity
|
|
94
|
+
'O(n^2)' // squared time complexity
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
for (const complexity of complexitiesToCheck) {
|
|
98
|
+
// Calculate data points for fitting
|
|
99
|
+
const fittedData: number[] = dataSizes.map(size => {
|
|
100
|
+
if (complexity === 'O(1)') {
|
|
101
|
+
return 1; // constant time complexity
|
|
102
|
+
} else if (complexity === 'O(log n)') {
|
|
103
|
+
return Math.log(size);
|
|
104
|
+
} else if (complexity === 'O(n)') {
|
|
105
|
+
return size;
|
|
106
|
+
} else if (complexity === 'O(n log n)') {
|
|
107
|
+
return size * Math.log(size);
|
|
108
|
+
} else if (complexity === 'O(n^2)') {
|
|
109
|
+
return size ** 2;
|
|
110
|
+
} else {
|
|
111
|
+
return size ** 10;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Fit the data points using linear regression analysis
|
|
116
|
+
const regressionResult = linearRegression(fittedData, runtimes);
|
|
117
|
+
|
|
118
|
+
// Check the R-squared value of the fit. It is usually considered a valid fit if it is greater than 0.9.
|
|
119
|
+
if (regressionResult.rSquared >= 0.9) {
|
|
120
|
+
complexities.push(complexity);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// If there is no valid fitting result, return "cannot estimate", otherwise return the estimated time complexity
|
|
125
|
+
if (complexities.length === 0) {
|
|
126
|
+
return 'Unable to estimate';
|
|
127
|
+
} else {
|
|
128
|
+
return complexities.join(' or ');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const methodLogs: Map<string, [number, number][]> = new Map();
|
|
133
|
+
|
|
134
|
+
export function logBigOMetricsWrap<F extends AnyFunction>(fn: F, args: Parameters<F>, fnName: string) {
|
|
135
|
+
const startTime = performance.now();
|
|
136
|
+
const result = fn(args);
|
|
137
|
+
const endTime = performance.now();
|
|
138
|
+
const runTime = endTime - startTime;
|
|
139
|
+
const methodName = `${fnName}`;
|
|
140
|
+
if (!methodLogs.has(methodName)) {
|
|
141
|
+
methodLogs.set(methodName, []);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const methodLog = methodLogs.get(methodName);
|
|
145
|
+
|
|
146
|
+
const maxDataSize = args.length === 1 && typeof args[0] === 'number' ? args[0] : findPotentialN(args);
|
|
147
|
+
if (methodLog) {
|
|
148
|
+
methodLog.push([runTime, maxDataSize]);
|
|
149
|
+
|
|
150
|
+
if (methodLog.length >= 20) {
|
|
151
|
+
console.log('triggered', methodName, methodLog);
|
|
152
|
+
const bigO = estimateBigO(
|
|
153
|
+
methodLog.map(([runTime]) => runTime),
|
|
154
|
+
methodLog.map(([runTime]) => runTime)
|
|
155
|
+
);
|
|
156
|
+
console.log(`Estimated Big O: ${bigO}`);
|
|
157
|
+
methodLogs.delete(methodName);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function logBigOMetrics(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
165
|
+
const originalMethod = descriptor.value;
|
|
166
|
+
|
|
167
|
+
descriptor.value = function (...args: any[]) {
|
|
168
|
+
const startTime = performance.now();
|
|
169
|
+
const result = originalMethod.apply(this, args);
|
|
170
|
+
const endTime = performance.now();
|
|
171
|
+
const runTime = endTime - startTime;
|
|
172
|
+
|
|
173
|
+
const methodName = `${target.constructor.name}.${propertyKey}`;
|
|
174
|
+
if (!methodLogs.has(methodName)) {
|
|
175
|
+
methodLogs.set(methodName, []);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const methodLog = methodLogs.get(methodName);
|
|
179
|
+
|
|
180
|
+
const maxDataSize = args.length === 1 && typeof args[0] === 'number' ? args[0] : findPotentialN(args);
|
|
181
|
+
if (methodLog) {
|
|
182
|
+
methodLog.push([runTime, maxDataSize]);
|
|
183
|
+
|
|
184
|
+
if (methodLog.length >= 20) {
|
|
185
|
+
console.log('triggered', methodName, methodLog);
|
|
186
|
+
const bigO = estimateBigO(
|
|
187
|
+
methodLog.map(([runTime]) => runTime),
|
|
188
|
+
methodLog.map(([runTime]) => runTime)
|
|
189
|
+
);
|
|
190
|
+
console.log(`Estimated Big O: ${bigO}`);
|
|
191
|
+
methodLogs.delete(methodName);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return result;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return descriptor;
|
|
199
|
+
}
|
package/test/utils/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './number';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './big-o';
|