@obinexusmk2/hypernum 0.1.0
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/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/index.cjs +3425 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1449 -0
- package/dist/index.js +3284 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +8 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/types/config/config-loader.d.ts +56 -0
- package/dist/types/config/config-loader.d.ts.map +1 -0
- package/dist/types/config/config-parser.d.ts +28 -0
- package/dist/types/config/config-parser.d.ts.map +1 -0
- package/dist/types/config/config-resolver.d.ts +21 -0
- package/dist/types/config/config-resolver.d.ts.map +1 -0
- package/dist/types/config/config-source.d.ts +27 -0
- package/dist/types/config/config-source.d.ts.map +1 -0
- package/dist/types/config/index.d.ts +68 -0
- package/dist/types/config/index.d.ts.map +1 -0
- package/dist/types/core/common.d.ts +169 -0
- package/dist/types/core/common.d.ts.map +1 -0
- package/dist/types/core/config.d.ts +197 -0
- package/dist/types/core/config.d.ts.map +1 -0
- package/dist/types/core/constants.d.ts +88 -0
- package/dist/types/core/constants.d.ts.map +1 -0
- package/dist/types/core/errors.d.ts +97 -0
- package/dist/types/core/errors.d.ts.map +1 -0
- package/dist/types/core/hypernum.d.ts +60 -0
- package/dist/types/core/hypernum.d.ts.map +1 -0
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/core/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +33 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/operations/arithmetic.d.ts +72 -0
- package/dist/types/operations/arithmetic.d.ts.map +1 -0
- package/dist/types/operations/bitwise.d.ts +98 -0
- package/dist/types/operations/bitwise.d.ts.map +1 -0
- package/dist/types/operations/comparison.d.ts +94 -0
- package/dist/types/operations/comparison.d.ts.map +1 -0
- package/dist/types/operations/conversion.d.ts +79 -0
- package/dist/types/operations/conversion.d.ts.map +1 -0
- package/dist/types/operations/factorial.d.ts +58 -0
- package/dist/types/operations/factorial.d.ts.map +1 -0
- package/dist/types/operations/index.d.ts +6 -0
- package/dist/types/operations/index.d.ts.map +1 -0
- package/dist/types/operations/power.d.ts +49 -0
- package/dist/types/operations/power.d.ts.map +1 -0
- package/dist/types/storage/Heap.d.ts +95 -0
- package/dist/types/storage/Heap.d.ts.map +1 -0
- package/dist/types/storage/index.d.ts +2 -0
- package/dist/types/storage/index.d.ts.map +1 -0
- package/dist/types/structures/ackermann.d.ts +74 -0
- package/dist/types/structures/ackermann.d.ts.map +1 -0
- package/dist/types/structures/big-array.d.ts +102 -0
- package/dist/types/structures/big-array.d.ts.map +1 -0
- package/dist/types/structures/index.d.ts +5 -0
- package/dist/types/structures/index.d.ts.map +1 -0
- package/dist/types/structures/number-tree.d.ts +114 -0
- package/dist/types/structures/number-tree.d.ts.map +1 -0
- package/dist/types/structures/power-tower.d.ts +74 -0
- package/dist/types/structures/power-tower.d.ts.map +1 -0
- package/dist/types/utils/formatting.d.ts +45 -0
- package/dist/types/utils/formatting.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +5 -0
- package/dist/types/utils/index.d.ts.map +1 -0
- package/dist/types/utils/parser.d.ts +39 -0
- package/dist/types/utils/parser.d.ts.map +1 -0
- package/dist/types/utils/precision.d.ts +57 -0
- package/dist/types/utils/precision.d.ts.map +1 -0
- package/dist/types/utils/validation.d.ts +28 -0
- package/dist/types/utils/validation.d.ts.map +1 -0
- package/package.json +164 -0
- package/rollup.config.js +162 -0
- package/src/config/config-loader.ts +226 -0
- package/src/config/config-parser.ts +161 -0
- package/src/config/config-resolver.ts +52 -0
- package/src/config/config-source.ts +32 -0
- package/src/config/index.ts +159 -0
- package/src/core/common.ts +185 -0
- package/src/core/config.ts +393 -0
- package/src/core/constants.ts +102 -0
- package/src/core/errors.ts +203 -0
- package/src/core/hypernum.ts +241 -0
- package/src/core/index.ts +5 -0
- package/src/index.ts +183 -0
- package/src/operations/arithmetic.ts +333 -0
- package/src/operations/bitwise.ts +367 -0
- package/src/operations/comparison.ts +272 -0
- package/src/operations/conversion.ts +400 -0
- package/src/operations/factorial.ts +279 -0
- package/src/operations/index.ts +5 -0
- package/src/operations/power.ts +316 -0
- package/src/storage/Heap.ts +238 -0
- package/src/storage/index.ts +1 -0
- package/src/structures/ackermann.ts +233 -0
- package/src/structures/big-array.ts +306 -0
- package/src/structures/index.ts +4 -0
- package/src/structures/number-tree.ts +404 -0
- package/src/structures/power-tower.ts +278 -0
- package/src/types/common.d.ts +357 -0
- package/src/types/core.d.ts +161 -0
- package/src/types/index.d.ts +2 -0
- package/src/utils/formatting.ts +246 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/parser.ts +245 -0
- package/src/utils/precision.ts +217 -0
- package/src/utils/validation.ts +183 -0
- package/tsconfig.json +84 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { Comparator } from '@/core';
|
|
2
|
+
import { MinHeap, MaxHeap } from '../storage/Heap';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Interface for segment tree node operations
|
|
6
|
+
*/
|
|
7
|
+
export interface SegmentTreeNode<T> { value: T;
|
|
8
|
+
lazy?: T;
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Type for BigArray operation result
|
|
15
|
+
*/
|
|
16
|
+
export type OperationResult<T> = {
|
|
17
|
+
success: boolean;
|
|
18
|
+
value?: T;
|
|
19
|
+
error?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for BigArray initialization
|
|
24
|
+
*/
|
|
25
|
+
export interface BigArrayOptions<T> {
|
|
26
|
+
initialCapacity?: number;
|
|
27
|
+
growthFactor?: number;
|
|
28
|
+
comparator?: Comparator<T>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A specialized array implementation for handling large numbers and providing
|
|
33
|
+
* efficient operations with segment tree support
|
|
34
|
+
*/
|
|
35
|
+
export class BigArray<T> {
|
|
36
|
+
private data: T[];
|
|
37
|
+
private segmentTree: Array<SegmentTreeNode<T> | null>;
|
|
38
|
+
private readonly growthFactor: number;
|
|
39
|
+
private readonly comparator: Comparator<T>;
|
|
40
|
+
private size: number;
|
|
41
|
+
private capacity: number;
|
|
42
|
+
|
|
43
|
+
constructor(options: BigArrayOptions<T> = {}) {
|
|
44
|
+
const {
|
|
45
|
+
initialCapacity = 16,
|
|
46
|
+
growthFactor = 2,
|
|
47
|
+
comparator = ((a: T, b: T): -1 | 0 | 1 => {
|
|
48
|
+
if (a < b) return -1;
|
|
49
|
+
if (a > b) return 1;
|
|
50
|
+
return 0;
|
|
51
|
+
}) as Comparator<T>
|
|
52
|
+
} = options;
|
|
53
|
+
|
|
54
|
+
this.capacity = initialCapacity;
|
|
55
|
+
this.growthFactor = growthFactor;
|
|
56
|
+
this.comparator = comparator;
|
|
57
|
+
this.size = 0;
|
|
58
|
+
this.data = new Array(this.capacity);
|
|
59
|
+
this.segmentTree = new Array(4 * this.capacity).fill(null);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Gets the current size of the array
|
|
64
|
+
*/
|
|
65
|
+
public getSize(): number {
|
|
66
|
+
return this.size;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets the current capacity of the array
|
|
71
|
+
*/
|
|
72
|
+
public getCapacity(): number {
|
|
73
|
+
return this.capacity;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resizes the internal array when needed
|
|
78
|
+
*/
|
|
79
|
+
private resize(newCapacity: number): void {
|
|
80
|
+
const newData = new Array(newCapacity);
|
|
81
|
+
for (let i = 0; i < this.size; i++) {
|
|
82
|
+
newData[i] = this.data[i];
|
|
83
|
+
}
|
|
84
|
+
this.data = newData;
|
|
85
|
+
this.capacity = newCapacity;
|
|
86
|
+
this.rebuildSegmentTree();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Appends an element to the end of the array
|
|
91
|
+
*/
|
|
92
|
+
public push(value: T): OperationResult<number> {
|
|
93
|
+
try {
|
|
94
|
+
if (this.size >= this.capacity) {
|
|
95
|
+
this.resize(this.capacity * this.growthFactor);
|
|
96
|
+
}
|
|
97
|
+
this.data[this.size] = value;
|
|
98
|
+
this.updateSegmentTree(0, this.size, value);
|
|
99
|
+
this.size++;
|
|
100
|
+
return { success: true, value: this.size - 1 };
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: error instanceof Error ? error.message : 'Unknown error during push'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Removes and returns the last element
|
|
111
|
+
*/
|
|
112
|
+
public pop(): OperationResult<T> {
|
|
113
|
+
if (this.size === 0) {
|
|
114
|
+
return { success: false, error: 'Array is empty' };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const value = this.data[this.size - 1];
|
|
118
|
+
this.size--;
|
|
119
|
+
|
|
120
|
+
// Shrink array if it's too sparse
|
|
121
|
+
if (this.size < this.capacity / (this.growthFactor * 2)) {
|
|
122
|
+
this.resize(Math.max(16, Math.floor(this.capacity / this.growthFactor)));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { success: true, value };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Gets element at specified index
|
|
130
|
+
*/
|
|
131
|
+
public get(index: number): OperationResult<T> {
|
|
132
|
+
if (index < 0 || index >= this.size) {
|
|
133
|
+
return { success: false, error: 'Index out of bounds' };
|
|
134
|
+
}
|
|
135
|
+
return { success: true, value: this.data[index] };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Sets element at specified index
|
|
140
|
+
*/
|
|
141
|
+
public set(index: number, value: T): OperationResult<T> {
|
|
142
|
+
if (index < 0 || index >= this.size) {
|
|
143
|
+
return { success: false, error: 'Index out of bounds' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const oldValue = this.data[index];
|
|
147
|
+
this.data[index] = value;
|
|
148
|
+
this.updateSegmentTree(0, index, value);
|
|
149
|
+
|
|
150
|
+
return { success: true, value: oldValue };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Rebuilds the segment tree after major changes
|
|
155
|
+
*/
|
|
156
|
+
private rebuildSegmentTree(): void {
|
|
157
|
+
this.segmentTree = new Array(4 * this.capacity).fill(null);
|
|
158
|
+
if (this.size > 0) {
|
|
159
|
+
this.buildSegmentTree(0, 0, this.size - 1);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Builds a segment tree node recursively
|
|
165
|
+
*/
|
|
166
|
+
private buildSegmentTree(node: number, start: number, end: number): void {
|
|
167
|
+
if (start === end) {
|
|
168
|
+
this.segmentTree[node] = {
|
|
169
|
+
value: this.data[start] as T,
|
|
170
|
+
start,
|
|
171
|
+
end
|
|
172
|
+
};
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const mid = Math.floor((start + end) / 2);
|
|
177
|
+
this.buildSegmentTree(2 * node + 1, start, mid);
|
|
178
|
+
this.buildSegmentTree(2 * node + 2, mid + 1, end);
|
|
179
|
+
|
|
180
|
+
const leftNode = this.segmentTree[2 * node + 1];
|
|
181
|
+
const rightNode = this.segmentTree[2 * node + 2];
|
|
182
|
+
|
|
183
|
+
if (leftNode && rightNode) {
|
|
184
|
+
this.segmentTree[node] = {
|
|
185
|
+
value: this.comparator(leftNode.value, rightNode.value) >= 0
|
|
186
|
+
? leftNode.value
|
|
187
|
+
: rightNode.value,
|
|
188
|
+
start,
|
|
189
|
+
end
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Updates the segment tree after a value change
|
|
196
|
+
*/
|
|
197
|
+
private updateSegmentTree(node: number, index: number, value: T): void {
|
|
198
|
+
if (!this.segmentTree[node]) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const currentNode = this.segmentTree[node]!;
|
|
203
|
+
if (currentNode.start === currentNode.end) {
|
|
204
|
+
currentNode.value = value;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const mid = Math.floor((currentNode.start + currentNode.end) / 2);
|
|
209
|
+
if (index <= mid) {
|
|
210
|
+
this.updateSegmentTree(2 * node + 1, index, value);
|
|
211
|
+
} else {
|
|
212
|
+
this.updateSegmentTree(2 * node + 2, index, value);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const leftNode = this.segmentTree[2 * node + 1];
|
|
216
|
+
const rightNode = this.segmentTree[2 * node + 2];
|
|
217
|
+
|
|
218
|
+
if (leftNode && rightNode) {
|
|
219
|
+
currentNode.value = this.comparator(leftNode.value, rightNode.value) >= 0
|
|
220
|
+
? leftNode.value
|
|
221
|
+
: rightNode.value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Queries the maximum value in a range
|
|
227
|
+
*/
|
|
228
|
+
public queryRange(start: number, end: number): OperationResult<T> {
|
|
229
|
+
if (start < 0 || end >= this.size || start > end) {
|
|
230
|
+
return { success: false, error: 'Invalid range' };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const result = this.querySegmentTree(0, start, end);
|
|
234
|
+
return result
|
|
235
|
+
? { success: true, value: result }
|
|
236
|
+
: { success: false, error: 'Range query failed' };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Recursively queries the segment tree
|
|
241
|
+
*/
|
|
242
|
+
private querySegmentTree(node: number, queryStart: number, queryEnd: number): T | null {
|
|
243
|
+
const currentNode = this.segmentTree[node];
|
|
244
|
+
if (!currentNode) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (queryStart <= currentNode.start && queryEnd >= currentNode.end) {
|
|
249
|
+
return currentNode.value;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (queryEnd < currentNode.start || queryStart > currentNode.end) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const leftResult = this.querySegmentTree(2 * node + 1, queryStart, queryEnd);
|
|
257
|
+
const rightResult = this.querySegmentTree(2 * node + 2, queryStart, queryEnd);
|
|
258
|
+
|
|
259
|
+
if (leftResult === null) return rightResult;
|
|
260
|
+
if (rightResult === null) return leftResult;
|
|
261
|
+
|
|
262
|
+
return this.comparator(leftResult, rightResult) >= 0 ? leftResult : rightResult;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Creates a heap from the current array
|
|
267
|
+
*/
|
|
268
|
+
public toHeap(isMin: boolean = true): MinHeap<T> | MaxHeap<T> {
|
|
269
|
+
const heap = isMin
|
|
270
|
+
? new MinHeap<T>(this.comparator)
|
|
271
|
+
: new MaxHeap<T>(this.comparator);
|
|
272
|
+
|
|
273
|
+
for (let i = 0; i < this.size; i++) {
|
|
274
|
+
if (this.data[i] !== undefined) {
|
|
275
|
+
if (this.data[i] !== undefined) {
|
|
276
|
+
heap.push(this.data[i] as T);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return heap;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Sorts the array in-place
|
|
286
|
+
*/
|
|
287
|
+
public sort(ascending: boolean = true): void {
|
|
288
|
+
const heap = this.toHeap(!ascending);
|
|
289
|
+
for (let i = this.size - 1; i >= 0; i--) {
|
|
290
|
+
const value = heap.pop();
|
|
291
|
+
if (value !== undefined) {
|
|
292
|
+
this.data[i] = value;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
this.rebuildSegmentTree();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Returns array as native array
|
|
300
|
+
*/
|
|
301
|
+
public toArray(): T[] {
|
|
302
|
+
return this.data.slice(0, this.size);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export default BigArray;
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { Comparator } from "@/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for tree node statistics
|
|
5
|
+
*/
|
|
6
|
+
interface NodeStats {
|
|
7
|
+
height: number;
|
|
8
|
+
size: number;
|
|
9
|
+
sum: bigint;
|
|
10
|
+
min: bigint;
|
|
11
|
+
max: bigint;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Interface for tree traversal configuration
|
|
16
|
+
*/
|
|
17
|
+
interface TraversalConfig {
|
|
18
|
+
includeStats?: boolean;
|
|
19
|
+
skipSubtrees?: boolean;
|
|
20
|
+
maxDepth?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Class representing a node in the number tree
|
|
25
|
+
*/
|
|
26
|
+
class NumberNode {
|
|
27
|
+
value: bigint;
|
|
28
|
+
left: NumberNode | null;
|
|
29
|
+
right: NumberNode | null;
|
|
30
|
+
parent: NumberNode | null;
|
|
31
|
+
height: number;
|
|
32
|
+
size: number;
|
|
33
|
+
sum: bigint;
|
|
34
|
+
|
|
35
|
+
constructor(value: bigint | string | number) {
|
|
36
|
+
this.value = typeof value === 'bigint' ? value : BigInt(value);
|
|
37
|
+
this.left = null;
|
|
38
|
+
this.right = null;
|
|
39
|
+
this.parent = null;
|
|
40
|
+
this.height = 1;
|
|
41
|
+
this.size = 1;
|
|
42
|
+
this.sum = this.value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Updates node statistics based on children
|
|
47
|
+
*/
|
|
48
|
+
updateStats(): void {
|
|
49
|
+
this.height = 1 + Math.max(
|
|
50
|
+
this.left?.height ?? 0,
|
|
51
|
+
this.right?.height ?? 0
|
|
52
|
+
);
|
|
53
|
+
this.size = 1 + (this.left?.size ?? 0) + (this.right?.size ?? 0);
|
|
54
|
+
this.sum = this.value +
|
|
55
|
+
(this.left?.sum ?? BigInt(0)) +
|
|
56
|
+
(this.right?.sum ?? BigInt(0));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gets balance factor of the node
|
|
61
|
+
*/
|
|
62
|
+
getBalance(): number {
|
|
63
|
+
return (this.left?.height ?? 0) - (this.right?.height ?? 0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Gets complete statistics for the node and its subtree
|
|
68
|
+
*/
|
|
69
|
+
getStats(): NodeStats {
|
|
70
|
+
return {
|
|
71
|
+
height: this.height,
|
|
72
|
+
size: this.size,
|
|
73
|
+
sum: this.sum,
|
|
74
|
+
min: this.findMin().value,
|
|
75
|
+
max: this.findMax().value
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Finds minimum value node in the subtree
|
|
81
|
+
*/
|
|
82
|
+
findMin(): NumberNode {
|
|
83
|
+
let current: NumberNode = this;
|
|
84
|
+
while (current.left) {
|
|
85
|
+
current = current.left;
|
|
86
|
+
}
|
|
87
|
+
return current;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Finds maximum value node in the subtree
|
|
92
|
+
*/
|
|
93
|
+
findMax(): NumberNode {
|
|
94
|
+
let current: NumberNode = this;
|
|
95
|
+
while (current.right) {
|
|
96
|
+
current = current.right;
|
|
97
|
+
}
|
|
98
|
+
return current;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* AVL Tree implementation specialized for handling large numbers
|
|
104
|
+
*/
|
|
105
|
+
export class NumberTree {
|
|
106
|
+
private root: NumberNode | null;
|
|
107
|
+
private readonly comparator: Comparator<bigint>;
|
|
108
|
+
|
|
109
|
+
constructor(comparator?: Comparator<bigint>) {
|
|
110
|
+
this.root = null;
|
|
111
|
+
this.comparator = comparator ?? ((a: bigint, b: bigint): -1 | 0 | 1 => {
|
|
112
|
+
if (a < b) return -1;
|
|
113
|
+
if (a > b) return 1;
|
|
114
|
+
return 0;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Gets the root node if it exists
|
|
120
|
+
*/
|
|
121
|
+
public getRoot(): NumberNode | null {
|
|
122
|
+
return this.root;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Inserts a new value into the tree
|
|
127
|
+
*/
|
|
128
|
+
public insert(value: bigint | string | number): NumberNode {
|
|
129
|
+
const newValue = typeof value === 'bigint' ? value : BigInt(value);
|
|
130
|
+
this.root = this.insertNode(this.root, newValue);
|
|
131
|
+
return this.find(newValue)!;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Recursively inserts a new node
|
|
136
|
+
*/
|
|
137
|
+
private insertNode(node: NumberNode | null, value: bigint): NumberNode {
|
|
138
|
+
if (!node) {
|
|
139
|
+
return new NumberNode(value);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const compareResult = this.comparator(value, node.value);
|
|
143
|
+
if (compareResult < 0) {
|
|
144
|
+
node.left = this.insertNode(node.left, value);
|
|
145
|
+
node.left.parent = node;
|
|
146
|
+
} else if (compareResult > 0) {
|
|
147
|
+
node.right = this.insertNode(node.right, value);
|
|
148
|
+
node.right.parent = node;
|
|
149
|
+
} else {
|
|
150
|
+
return node; // Duplicate value, return existing node
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
node.updateStats();
|
|
154
|
+
return this.balance(node);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Balances a node using AVL rotations
|
|
159
|
+
*/
|
|
160
|
+
private balance(node: NumberNode): NumberNode {
|
|
161
|
+
const balance = node.getBalance();
|
|
162
|
+
|
|
163
|
+
// Left heavy
|
|
164
|
+
if (balance > 1) {
|
|
165
|
+
if (node.left && node.left.getBalance() < 0) {
|
|
166
|
+
node.left = this.rotateLeft(node.left);
|
|
167
|
+
}
|
|
168
|
+
return this.rotateRight(node);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Right heavy
|
|
172
|
+
if (balance < -1) {
|
|
173
|
+
if (node.right && node.right.getBalance() > 0) {
|
|
174
|
+
node.right = this.rotateRight(node.right);
|
|
175
|
+
}
|
|
176
|
+
return this.rotateLeft(node);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return node;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Performs left rotation
|
|
184
|
+
*/
|
|
185
|
+
private rotateLeft(node: NumberNode): NumberNode {
|
|
186
|
+
const rightChild = node.right!;
|
|
187
|
+
const rightLeftChild = rightChild.left;
|
|
188
|
+
|
|
189
|
+
rightChild.left = node;
|
|
190
|
+
node.right = rightLeftChild;
|
|
191
|
+
|
|
192
|
+
if (rightLeftChild) {
|
|
193
|
+
rightLeftChild.parent = node;
|
|
194
|
+
}
|
|
195
|
+
rightChild.parent = node.parent;
|
|
196
|
+
node.parent = rightChild;
|
|
197
|
+
|
|
198
|
+
node.updateStats();
|
|
199
|
+
rightChild.updateStats();
|
|
200
|
+
|
|
201
|
+
return rightChild;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Performs right rotation
|
|
206
|
+
*/
|
|
207
|
+
private rotateRight(node: NumberNode): NumberNode {
|
|
208
|
+
const leftChild = node.left!;
|
|
209
|
+
const leftRightChild = leftChild.right;
|
|
210
|
+
|
|
211
|
+
leftChild.right = node;
|
|
212
|
+
node.left = leftRightChild;
|
|
213
|
+
|
|
214
|
+
if (leftRightChild) {
|
|
215
|
+
leftRightChild.parent = node;
|
|
216
|
+
}
|
|
217
|
+
leftChild.parent = node.parent;
|
|
218
|
+
node.parent = leftChild;
|
|
219
|
+
|
|
220
|
+
node.updateStats();
|
|
221
|
+
leftChild.updateStats();
|
|
222
|
+
|
|
223
|
+
return leftChild;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Removes a value from the tree
|
|
228
|
+
*/
|
|
229
|
+
public remove(value: bigint | string | number): boolean {
|
|
230
|
+
const searchValue = typeof value === 'bigint' ? value : BigInt(value);
|
|
231
|
+
const nodeToRemove = this.find(searchValue);
|
|
232
|
+
|
|
233
|
+
if (!nodeToRemove) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.root = this.removeNode(this.root, searchValue);
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Recursively removes a node
|
|
243
|
+
*/
|
|
244
|
+
private removeNode(node: NumberNode | null, value: bigint): NumberNode | null {
|
|
245
|
+
if (!node) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const compareResult = this.comparator(value, node.value);
|
|
250
|
+
if (compareResult < 0) {
|
|
251
|
+
node.left = this.removeNode(node.left, value);
|
|
252
|
+
if (node.left) {
|
|
253
|
+
node.left.parent = node;
|
|
254
|
+
}
|
|
255
|
+
} else if (compareResult > 0) {
|
|
256
|
+
node.right = this.removeNode(node.right, value);
|
|
257
|
+
if (node.right) {
|
|
258
|
+
node.right.parent = node;
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
// Node to delete found
|
|
262
|
+
if (!node.left) {
|
|
263
|
+
return node.right;
|
|
264
|
+
}
|
|
265
|
+
if (!node.right) {
|
|
266
|
+
return node.left;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Node has two children
|
|
270
|
+
const successor = node.right.findMin();
|
|
271
|
+
node.value = successor.value;
|
|
272
|
+
node.right = this.removeNode(node.right, successor.value);
|
|
273
|
+
if (node.right) {
|
|
274
|
+
node.right.parent = node;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
node.updateStats();
|
|
279
|
+
return this.balance(node);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Finds a node by value
|
|
284
|
+
*/
|
|
285
|
+
public find(value: bigint | string | number): NumberNode | null {
|
|
286
|
+
const searchValue = typeof value === 'bigint' ? value : BigInt(value);
|
|
287
|
+
let current = this.root;
|
|
288
|
+
|
|
289
|
+
while (current) {
|
|
290
|
+
const compareResult = this.comparator(searchValue, current.value);
|
|
291
|
+
if (compareResult === 0) {
|
|
292
|
+
return current;
|
|
293
|
+
}
|
|
294
|
+
current = compareResult < 0 ? current.left : current.right;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Traverses the tree in specified order and returns values
|
|
302
|
+
*/
|
|
303
|
+
public traverse(order: 'inOrder' | 'preOrder' | 'postOrder' = 'inOrder',
|
|
304
|
+
config: TraversalConfig = {}): bigint[] {
|
|
305
|
+
const result: bigint[] = [];
|
|
306
|
+
|
|
307
|
+
const traverse = (node: NumberNode | null, depth: number = 0): void => {
|
|
308
|
+
if (!node || (config.maxDepth !== undefined && depth >= config.maxDepth)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (order === 'preOrder') {
|
|
313
|
+
result.push(node.value);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (!config.skipSubtrees) {
|
|
317
|
+
traverse(node.left, depth + 1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (order === 'inOrder') {
|
|
321
|
+
result.push(node.value);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (!config.skipSubtrees) {
|
|
325
|
+
traverse(node.right, depth + 1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (order === 'postOrder') {
|
|
329
|
+
result.push(node.value);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
traverse(this.root);
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Gets overall tree statistics
|
|
339
|
+
*/
|
|
340
|
+
public getTreeStats(): NodeStats | null {
|
|
341
|
+
return this.root?.getStats() ?? null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Gets the nth smallest value in the tree
|
|
346
|
+
*/
|
|
347
|
+
public getNthValue(n: number): bigint | null {
|
|
348
|
+
if (!this.root || n < 1 || n > this.root.size) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const findNth = (node: NumberNode | null, position: number): bigint | null => {
|
|
353
|
+
if (!node) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const leftSize = node.left?.size ?? 0;
|
|
358
|
+
|
|
359
|
+
if (position === leftSize + 1) {
|
|
360
|
+
return node.value;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (position <= leftSize) {
|
|
364
|
+
return findNth(node.left, position);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return findNth(node.right, position - leftSize - 1);
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
return findNth(this.root, n);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Gets a range of values between start and end (inclusive)
|
|
375
|
+
*/
|
|
376
|
+
public getRange(start: bigint | string | number,
|
|
377
|
+
end: bigint | string | number): bigint[] {
|
|
378
|
+
const startValue = typeof start === 'bigint' ? start : BigInt(start);
|
|
379
|
+
const endValue = typeof end === 'bigint' ? end : BigInt(end);
|
|
380
|
+
const result: bigint[] = [];
|
|
381
|
+
|
|
382
|
+
const collectRange = (node: NumberNode | null): void => {
|
|
383
|
+
if (!node) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (this.comparator(node.value, startValue) >= 0 &&
|
|
388
|
+
this.comparator(node.value, endValue) <= 0) {
|
|
389
|
+
collectRange(node.left);
|
|
390
|
+
result.push(node.value);
|
|
391
|
+
collectRange(node.right);
|
|
392
|
+
} else if (this.comparator(node.value, startValue) > 0) {
|
|
393
|
+
collectRange(node.left);
|
|
394
|
+
} else {
|
|
395
|
+
collectRange(node.right);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
collectRange(this.root);
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export default NumberTree;
|