data-structure-typed 2.2.8 → 2.3.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/.github/workflows/ci.yml +9 -0
- package/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/README_CN.md +1 -1
- package/dist/cjs/index.cjs +584 -76
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +588 -79
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +584 -76
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +588 -79
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/base/linear-base.d.ts +6 -6
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +3 -4
- package/dist/types/data-structures/binary-tree/bst.d.ts +2 -1
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +150 -20
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +3 -3
- package/dist/types/interfaces/binary-tree.d.ts +1 -1
- package/dist/umd/data-structure-typed.js +588 -79
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +4 -3
- package/src/data-structures/base/linear-base.ts +2 -12
- package/src/data-structures/binary-tree/binary-tree.ts +5 -6
- package/src/data-structures/binary-tree/bst.ts +79 -4
- package/src/data-structures/binary-tree/red-black-tree.ts +583 -73
- package/src/data-structures/binary-tree/tree-counter.ts +21 -9
- package/src/data-structures/queue/deque.ts +10 -0
- package/src/interfaces/binary-tree.ts +1 -1
- package/test/unit/data-structures/base/iterable-element-base.coverage.test.ts +106 -0
- package/test/unit/data-structures/base/iterable-element-base.more-branches.coverage.test.ts +61 -0
- package/test/unit/data-structures/base/linear-base.array.coverage.test.ts +168 -0
- package/test/unit/data-structures/base/linear-base.concat-else.coverage.test.ts +82 -0
- package/test/unit/data-structures/base/linear-base.coverage.test.ts +72 -0
- package/test/unit/data-structures/base/linear-base.more-branches.coverage.test.ts +417 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches-3.coverage.test.ts +146 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches.coverage.test.ts +93 -0
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.coverage.test.ts +108 -0
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.more-branches-2.coverage.test.ts +85 -0
- package/test/unit/data-structures/binary-tree/avl-tree-node.familyPosition-root-left.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/avl-tree.more-branches-2.coverage.test.ts +99 -0
- package/test/unit/data-structures/binary-tree/binary-indexed-tree.more-branches.coverage.test.ts +18 -0
- package/test/unit/data-structures/binary-tree/binary-tree.more-branches.coverage.test.ts +56 -0
- package/test/unit/data-structures/binary-tree/binary-tree.remaining-branches.coverage.test.ts +229 -0
- package/test/unit/data-structures/binary-tree/bst.bound-by-predicate.coverage.test.ts +33 -0
- package/test/unit/data-structures/binary-tree/bst.coverage.test.ts +94 -0
- package/test/unit/data-structures/binary-tree/bst.deletebykey.coverage.test.ts +70 -0
- package/test/unit/data-structures/binary-tree/bst.deletewhere.coverage.test.ts +37 -0
- package/test/unit/data-structures/binary-tree/bst.floor-lower-predicate.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/bst.floor-setmany.coverage.test.ts +72 -0
- package/test/unit/data-structures/binary-tree/bst.getnode.range-ensure.coverage.test.ts +22 -0
- package/test/unit/data-structures/binary-tree/bst.misc-branches.coverage.test.ts +100 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-2.coverage.test.ts +133 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-3.coverage.test.ts +45 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-4.coverage.test.ts +36 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-5.coverage.test.ts +40 -0
- package/test/unit/data-structures/binary-tree/bst.more.coverage.test.ts +39 -0
- package/test/unit/data-structures/binary-tree/bst.node-family.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/bst.range-pruning.coverage.test.ts +43 -0
- package/test/unit/data-structures/binary-tree/bst.search-fastpath.coverage.test.ts +30 -0
- package/test/unit/data-structures/binary-tree/bst.test.ts +25 -55
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-corruption-repair.coverage.test.ts +66 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-max-update.coverage.test.ts +18 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-null.coverage.test.ts +53 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-stale-cache.coverage.test.ts +25 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-update.coverage.test.ts +23 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-delete.coverage.test.ts +49 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-edge.coverage.test.ts +37 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-stale-insert.coverage.test.ts +39 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.coverage.test.ts +334 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.delete-fixup.coverage.test.ts +68 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.delete-successor.coverage.test.ts +75 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.factories.coverage.test.ts +26 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-compare-update.coverage.test.ts +74 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-no-update.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-nullish.coverage.test.ts +61 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-defined.coverage.test.ts +35 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-undefined.coverage.test.ts +43 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-more.coverage.test.ts +99 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint.coverage.test.ts +60 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.insert-cache-nullish.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.insert-header-parent-nullish.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.internal-walk.coverage.test.ts +57 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.minmax-cache.test.ts +65 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.misc-inputs.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-2.coverage.test.ts +121 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-3.coverage.test.ts +55 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-4.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.predsucc.coverage.test.ts +40 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.remaining-branches.coverage.test.ts +123 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.set-inputs.coverage.test.ts +64 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-parent-cache.coverage.test.ts +79 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-remaining.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-uncovered.coverage.test.ts +74 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.update-branches.coverage.test.ts +30 -0
- package/test/unit/data-structures/binary-tree/segment-tree.more-branches.coverage.test.ts +31 -0
- package/test/unit/data-structures/binary-tree/tree-counter.coverage.test.ts +115 -0
- package/test/unit/data-structures/binary-tree/tree-counter.more-branches.coverage.test.ts +244 -0
- package/test/unit/data-structures/binary-tree/tree-counter.test.ts +4 -2
- package/test/unit/data-structures/binary-tree/tree-multi-map.coverage.test.ts +104 -0
- package/test/unit/data-structures/binary-tree/tree-multi-map.more-branches-2.coverage.test.ts +59 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-2.coverage.test.ts +40 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-3.coverage.test.ts +65 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-4.coverage.test.ts +98 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-5.coverage.test.ts +51 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches.coverage.test.ts +62 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches-2.coverage.test.ts +38 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches-3.coverage.test.ts +25 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches.coverage.test.ts +82 -0
- package/test/unit/data-structures/graph/map-graph.more-branches.coverage.test.ts +22 -0
- package/test/unit/data-structures/graph/undirected-graph.more-branches-2.coverage.test.ts +35 -0
- package/test/unit/data-structures/graph/undirected-graph.more-branches.coverage.test.ts +87 -0
- package/test/unit/data-structures/hash/hash-map.more-branches.coverage.test.ts +64 -0
- package/test/unit/data-structures/hash/hash-map.toEntryFn-branch.coverage.test.ts +9 -0
- package/test/unit/data-structures/heap/heap.misc-branches.coverage.test.ts +110 -0
- package/test/unit/data-structures/heap/heap.remaining-branches.coverage.test.ts +22 -0
- package/test/unit/data-structures/heap/max-heap.coverage.test.ts +29 -0
- package/test/unit/data-structures/linked-list/doubly-linked-list.more-branches.coverage.test.ts +72 -0
- package/test/unit/data-structures/linked-list/linked-list.unshiftMany-else.coverage.test.ts +15 -0
- package/test/unit/data-structures/linked-list/singly-linked-list.coverage.test.ts +221 -0
- package/test/unit/data-structures/linked-list/singly-linked-list.more-branches.coverage.test.ts +86 -0
- package/test/unit/data-structures/linked-list/skip-linked-list.more-branches.coverage.test.ts +31 -0
- package/test/unit/data-structures/matrix/matrix.more-branches.coverage.test.ts +81 -0
- package/test/unit/data-structures/matrix/matrix.pivotElement-nullish.coverage.test.ts +28 -0
- package/test/unit/data-structures/priority-queue/max-priority-queue.more-branches.coverage.test.ts +10 -0
- package/test/unit/data-structures/priority-queue/priority-queue.coverage.test.ts +21 -0
- package/test/unit/data-structures/queue/deque.coverage.test.ts +173 -0
- package/test/unit/data-structures/queue/deque.more-branches-2.coverage.test.ts +39 -0
- package/test/unit/data-structures/queue/deque.more-branches-3.coverage.test.ts +9 -0
- package/test/unit/data-structures/queue/deque.more-branches.coverage.test.ts +95 -0
- package/test/unit/data-structures/queue/queue.coverage.test.ts +138 -0
- package/test/unit/data-structures/queue/queue.more-branches-2.coverage.test.ts +27 -0
- package/test/unit/data-structures/stack/stack.coverage.test.ts +112 -0
- package/test/unit/data-structures/tree/tree.more-branches.coverage.test.ts +9 -0
- package/test/unit/data-structures/trie/trie.more-branches-2.coverage.test.ts +51 -0
- package/test/utils/patch.ts +33 -0
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
10
|
BinaryTreeDeleteResult,
|
|
11
|
-
BSTNOptKeyOrNode,
|
|
11
|
+
BSTNOptKeyOrNode, BTNRep,
|
|
12
12
|
EntryCallback,
|
|
13
13
|
FamilyPosition,
|
|
14
|
-
IterationType,
|
|
14
|
+
IterationType, NodePredicate,
|
|
15
15
|
OptNode,
|
|
16
16
|
RBTNColor,
|
|
17
17
|
TreeCounterOptions
|
|
18
18
|
} from '../../types';
|
|
19
19
|
import { BSTNode } from './bst';
|
|
20
20
|
import { IBinaryTree } from '../../interfaces';
|
|
21
|
-
import { RedBlackTree } from './red-black-tree';
|
|
21
|
+
import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* RB-tree node with an extra 'count' field; keeps parent/child links.
|
|
@@ -274,21 +274,21 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
|
|
|
274
274
|
/**
|
|
275
275
|
* Delete a node (or decrement its count) and rebalance if needed.
|
|
276
276
|
* @remarks Time O(log N), Space O(1)
|
|
277
|
-
* @param
|
|
277
|
+
* @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node.
|
|
278
278
|
* @param [ignoreCount] - If true, remove the node regardless of its count.
|
|
279
279
|
* @returns Array of deletion results including deleted node and a rebalance hint when present.
|
|
280
280
|
*/
|
|
281
281
|
override delete(
|
|
282
|
-
|
|
282
|
+
keyNodeEntryRawOrPredicate: BTNRep<K, V, TreeCounterNode<K, V>> | NodePredicate<TreeCounterNode<K, V> | null>,
|
|
283
283
|
ignoreCount = false
|
|
284
284
|
): BinaryTreeDeleteResult<TreeCounterNode<K, V>>[] {
|
|
285
|
-
if (
|
|
285
|
+
if (keyNodeEntryRawOrPredicate === null) return [];
|
|
286
286
|
|
|
287
287
|
const results: BinaryTreeDeleteResult<TreeCounterNode<K, V>>[] = [];
|
|
288
288
|
|
|
289
289
|
let nodeToDelete: OptNode<TreeCounterNode<K, V>>;
|
|
290
|
-
if (this._isPredicate(
|
|
291
|
-
else nodeToDelete = this.isRealNode(
|
|
290
|
+
if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
|
|
291
|
+
else nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate) ? keyNodeEntryRawOrPredicate : this.getNode(keyNodeEntryRawOrPredicate);
|
|
292
292
|
if (!nodeToDelete) {
|
|
293
293
|
return results;
|
|
294
294
|
}
|
|
@@ -331,9 +331,10 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
|
|
|
331
331
|
}
|
|
332
332
|
} else {
|
|
333
333
|
if (ignoreCount || nodeToDelete.count <= 1) {
|
|
334
|
+
// Removing the successor from its original position should NOT change total count;
|
|
335
|
+
// only removing the target node should affect the aggregate count.
|
|
334
336
|
if (successor.right !== null) {
|
|
335
337
|
this._transplant(successor, successor.right);
|
|
336
|
-
this._count -= nodeToDelete.count;
|
|
337
338
|
}
|
|
338
339
|
} else {
|
|
339
340
|
nodeToDelete.count--;
|
|
@@ -450,7 +451,18 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
|
|
|
450
451
|
override clone(): this {
|
|
451
452
|
const out = this._createInstance<K, V, R>();
|
|
452
453
|
this._clone(out as unknown as any);
|
|
454
|
+
|
|
455
|
+
// Preserve aggregate count and per-node counts.
|
|
453
456
|
(out as any)._count = (this as any)._count;
|
|
457
|
+
|
|
458
|
+
// NOTE: RedBlackTree._clone copies structure/keys/values, but TreeCounter nodes also track `count`.
|
|
459
|
+
// Copy counts by key to keep getComputedCount() consistent.
|
|
460
|
+
for (const node of this.dfs(n => n, 'IN')) {
|
|
461
|
+
if (!node) continue;
|
|
462
|
+
const outNode = (out as unknown as TreeCounter<K, V, R>).getNode(node.key) as any;
|
|
463
|
+
if (outNode) outNode.count = node.count;
|
|
464
|
+
}
|
|
465
|
+
|
|
454
466
|
return out as unknown as this;
|
|
455
467
|
}
|
|
456
468
|
|
|
@@ -880,6 +880,16 @@ export class Deque<E = any, R = any> extends LinearBase<E, R> {
|
|
|
880
880
|
|
|
881
881
|
protected _setBucketSize(size: number): void {
|
|
882
882
|
this._bucketSize = size;
|
|
883
|
+
|
|
884
|
+
// When adjusting bucketSize on a freshly created empty deque (common in helpers like cut/splice/clone),
|
|
885
|
+
// we must also realign internal pointers/buckets to avoid `_getBucketAndPosition` producing out-of-range
|
|
886
|
+
// indices based on the previous bucketSize.
|
|
887
|
+
if (this._length === 0) {
|
|
888
|
+
this._buckets = [new Array(this._bucketSize)];
|
|
889
|
+
this._bucketCount = 1;
|
|
890
|
+
this._bucketFirst = this._bucketLast = 0;
|
|
891
|
+
this._firstInBucket = this._lastInBucket = this._bucketSize >> 1;
|
|
892
|
+
}
|
|
883
893
|
}
|
|
884
894
|
|
|
885
895
|
/**
|
|
@@ -50,7 +50,7 @@ export interface IBinaryTree<K = any, V = any, R = any> {
|
|
|
50
50
|
|
|
51
51
|
// Accept BTNRep, predicate, or raw R for deletion
|
|
52
52
|
delete(
|
|
53
|
-
keyNodeEntryRawOrPredicate:
|
|
53
|
+
keyNodeEntryRawOrPredicate: BTNRep<K, V, BinaryTreeNode<K, V>> | NodePredicate<BinaryTreeNode<K, V> | null>
|
|
54
54
|
): BinaryTreeDeleteResult<BinaryTreeNode<K, V>>[];
|
|
55
55
|
|
|
56
56
|
clear(): void;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { IterableElementBase } from '../../../../src/data-structures/base/iterable-element-base';
|
|
2
|
+
|
|
3
|
+
class TestIterable extends IterableElementBase<number, number> {
|
|
4
|
+
private _arr: number[];
|
|
5
|
+
|
|
6
|
+
constructor(arr: number[] = [], opts?: any) {
|
|
7
|
+
super(opts);
|
|
8
|
+
this._arr = [...arr];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
protected *_getIterator(): IterableIterator<number> {
|
|
12
|
+
yield* this._arr;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isEmpty(): boolean {
|
|
16
|
+
return this._arr.length === 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
clear(): void {
|
|
20
|
+
this._arr.length = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
clone(): this {
|
|
24
|
+
return new TestIterable(this._arr, { toElementFn: this.toElementFn }) as unknown as this;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
map<EM, RM>(cb: any, options?: any, thisArg?: unknown): IterableElementBase<EM, RM> {
|
|
28
|
+
const out = this._arr.map((v, i) => cb.call(thisArg, v, i, this));
|
|
29
|
+
// For tests we keep it minimal; toElementFn unused.
|
|
30
|
+
return new TestIterable(out as any, options) as any;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
mapSame(cb: any, thisArg?: unknown): this {
|
|
34
|
+
return new TestIterable(this._arr.map((v, i) => cb.call(thisArg, v, i, this))) as unknown as this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
filter(pred: any, thisArg?: unknown): this {
|
|
38
|
+
const out: number[] = [];
|
|
39
|
+
let i = 0;
|
|
40
|
+
for (const v of this) {
|
|
41
|
+
if (pred.call(thisArg, v, i++, this)) out.push(v);
|
|
42
|
+
}
|
|
43
|
+
return new TestIterable(out) as unknown as this;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Coverage-focused tests for IterableElementBase.
|
|
49
|
+
* Keep existing @example tests intact.
|
|
50
|
+
*/
|
|
51
|
+
describe('IterableElementBase coverage', () => {
|
|
52
|
+
it('constructor rejects non-function toElementFn when truthy', () => {
|
|
53
|
+
expect(() => new TestIterable([], { toElementFn: 123 as any })).toThrow(TypeError);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('every/some/forEach/find cover thisArg and non-thisArg paths', () => {
|
|
57
|
+
const t = new TestIterable([1, 2, 3]);
|
|
58
|
+
|
|
59
|
+
// thisArg === undefined path
|
|
60
|
+
expect(t.every(v => v > 0)).toBe(true);
|
|
61
|
+
expect(t.some(v => v === 2)).toBe(true);
|
|
62
|
+
|
|
63
|
+
// thisArg path
|
|
64
|
+
const ctx = { mul: 2, seen: [] as number[] };
|
|
65
|
+
expect(
|
|
66
|
+
t.every(function (this: any, v: number) {
|
|
67
|
+
return v * this.mul > 0;
|
|
68
|
+
}, ctx)
|
|
69
|
+
).toBe(true);
|
|
70
|
+
|
|
71
|
+
t.forEach(function (this: any, v: number) {
|
|
72
|
+
this.seen.push(v);
|
|
73
|
+
}, ctx);
|
|
74
|
+
expect(ctx.seen).toEqual([1, 2, 3]);
|
|
75
|
+
|
|
76
|
+
const found = t.find(function (this: any, v: number) {
|
|
77
|
+
return v === 3;
|
|
78
|
+
}, ctx);
|
|
79
|
+
expect(found).toBe(3);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('reduce throws on empty without initialValue, works with/without initialValue', () => {
|
|
83
|
+
const empty = new TestIterable([]);
|
|
84
|
+
expect(() => empty.reduce((acc: number, v: number) => acc + v)).toThrow(TypeError);
|
|
85
|
+
|
|
86
|
+
const t = new TestIterable([1, 2, 3]);
|
|
87
|
+
expect(t.reduce((acc: number, v: number) => acc + v, 0)).toBe(6);
|
|
88
|
+
|
|
89
|
+
// no initialValue uses first element
|
|
90
|
+
expect(t.reduce((acc: number, v: number) => acc + v)).toBe(6);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('values/toArray/toVisual/print/has basics', () => {
|
|
94
|
+
const t = new TestIterable([1, 2]);
|
|
95
|
+
expect([...t.values()]).toEqual([1, 2]);
|
|
96
|
+
expect(t.toArray()).toEqual([1, 2]);
|
|
97
|
+
expect(t.toVisual()).toEqual([1, 2]);
|
|
98
|
+
expect(t.has(2)).toBe(true);
|
|
99
|
+
expect(t.has(3)).toBe(false);
|
|
100
|
+
|
|
101
|
+
const spy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
102
|
+
t.print();
|
|
103
|
+
expect(spy).toHaveBeenCalled();
|
|
104
|
+
spy.mockRestore();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ElementCallback, IterableElementBaseOptions } from 'src';
|
|
2
|
+
import { IterableElementBase } from '../../../../src/data-structures/base/iterable-element-base';
|
|
3
|
+
|
|
4
|
+
class NumIter extends IterableElementBase<number, number> {
|
|
5
|
+
override isEmpty(): boolean {
|
|
6
|
+
throw new Error('Method not implemented.');
|
|
7
|
+
}
|
|
8
|
+
override clear(): void {
|
|
9
|
+
throw new Error('Method not implemented.');
|
|
10
|
+
}
|
|
11
|
+
override clone(): this {
|
|
12
|
+
throw new Error('Method not implemented.');
|
|
13
|
+
}
|
|
14
|
+
override map<EM, RM>(
|
|
15
|
+
callback: ElementCallback<number, number, EM>,
|
|
16
|
+
options?: IterableElementBaseOptions<EM, RM> | undefined,
|
|
17
|
+
thisArg?: unknown
|
|
18
|
+
): IterableElementBase<EM, RM> {
|
|
19
|
+
throw new Error('Method not implemented.');
|
|
20
|
+
}
|
|
21
|
+
override mapSame(callback: ElementCallback<number, number, number>, thisArg?: unknown): this {
|
|
22
|
+
throw new Error('Method not implemented.');
|
|
23
|
+
}
|
|
24
|
+
override filter(predicate: ElementCallback<number, number, boolean>, thisArg?: unknown): this {
|
|
25
|
+
throw new Error('Method not implemented.');
|
|
26
|
+
}
|
|
27
|
+
protected override _getIterator(...args: unknown[]): IterableIterator<number> {
|
|
28
|
+
throw new Error('Method not implemented.');
|
|
29
|
+
}
|
|
30
|
+
constructor(private readonly data: number[]) {
|
|
31
|
+
super();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override *[Symbol.iterator](): IterableIterator<number> {
|
|
35
|
+
for (const n of this.data) yield n;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('IterableElementBase remaining branch coverage', () => {
|
|
40
|
+
it('every() uses thisArg branch (fn.call) and can early-return false', () => {
|
|
41
|
+
const it = new NumIter([1, 2, 3]);
|
|
42
|
+
|
|
43
|
+
const ctx = { limit: 2 };
|
|
44
|
+
const res = it.every(function (this: any, v: number) {
|
|
45
|
+
return v <= this.limit;
|
|
46
|
+
}, ctx);
|
|
47
|
+
|
|
48
|
+
expect(res).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('some() uses thisArg branch (fn.call) and can early-return true', () => {
|
|
52
|
+
const it = new NumIter([1, 2, 3]);
|
|
53
|
+
|
|
54
|
+
const ctx = { pick: 2 };
|
|
55
|
+
const res = it.some(function (this: any, v: number) {
|
|
56
|
+
return v === this.pick;
|
|
57
|
+
}, ctx);
|
|
58
|
+
|
|
59
|
+
expect(res).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { IterableElementBase } from 'src';
|
|
2
|
+
import { LinearBase } from '../../../../src/data-structures/base/linear-base';
|
|
3
|
+
import type { ElementCallback, IterableElementBaseOptions, LinearBaseOptions } from '../../../../src/types';
|
|
4
|
+
|
|
5
|
+
class TestArrayLinear extends LinearBase<number, number> {
|
|
6
|
+
override isEmpty(): boolean {
|
|
7
|
+
throw new Error('Method not implemented.');
|
|
8
|
+
}
|
|
9
|
+
override map<EM, RM>(
|
|
10
|
+
callback: ElementCallback<number, number, EM>,
|
|
11
|
+
options?: IterableElementBaseOptions<EM, RM> | undefined,
|
|
12
|
+
thisArg?: unknown
|
|
13
|
+
): IterableElementBase<EM, RM> {
|
|
14
|
+
throw new Error('Method not implemented.');
|
|
15
|
+
}
|
|
16
|
+
override mapSame(callback: ElementCallback<number, number, number>, thisArg?: unknown): this {
|
|
17
|
+
throw new Error('Method not implemented.');
|
|
18
|
+
}
|
|
19
|
+
override filter(predicate: ElementCallback<number, number, boolean>, thisArg?: unknown): this {
|
|
20
|
+
throw new Error('Method not implemented.');
|
|
21
|
+
}
|
|
22
|
+
private _arr: number[];
|
|
23
|
+
|
|
24
|
+
constructor(values: number[] = [], options?: LinearBaseOptions<number, number>) {
|
|
25
|
+
super(options);
|
|
26
|
+
this._arr = values.slice();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get length(): number {
|
|
30
|
+
return this._arr.length;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
clear(): void {
|
|
34
|
+
this._arr.length = 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setAt(index: number, value: number): boolean {
|
|
38
|
+
if (index < 0 || index >= this._arr.length) return false;
|
|
39
|
+
this._arr[index] = value;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
clone(): this {
|
|
44
|
+
const out = this._createInstance();
|
|
45
|
+
out.pushMany(this._arr);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reverse(): this {
|
|
50
|
+
this._arr.reverse();
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
push(elementOrNode: number): boolean {
|
|
55
|
+
if (this.maxLen > 0 && this._arr.length >= this.maxLen) return false;
|
|
56
|
+
this._arr.push(elementOrNode);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pushMany(elements: Iterable<number>): boolean[] {
|
|
61
|
+
const out: boolean[] = [];
|
|
62
|
+
for (const e of elements) out.push(this.push(e));
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
delete(elementOrNode: number | undefined): boolean {
|
|
67
|
+
if (elementOrNode === undefined) return false;
|
|
68
|
+
const idx = this._arr.indexOf(elementOrNode);
|
|
69
|
+
if (idx === -1) return false;
|
|
70
|
+
this._arr.splice(idx, 1);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
at(index: number): number | undefined {
|
|
75
|
+
return this._arr[index];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
deleteAt(pos: number): number | undefined {
|
|
79
|
+
if (pos < 0 || pos >= this._arr.length) return undefined;
|
|
80
|
+
return this._arr.splice(pos, 1)[0];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
addAt(index: number, newElementOrNode: number): boolean {
|
|
84
|
+
if (this.maxLen > 0 && this._arr.length >= this.maxLen) return false;
|
|
85
|
+
if (index < 0) index = 0;
|
|
86
|
+
if (index > this._arr.length) index = this._arr.length;
|
|
87
|
+
this._arr.splice(index, 0, newElementOrNode);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override toArray(): number[] {
|
|
92
|
+
return this._arr.slice();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected _createInstance(options?: LinearBaseOptions<number, number>): this {
|
|
96
|
+
return new TestArrayLinear([], options) as unknown as this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
protected *_getIterator(): IterableIterator<number> {
|
|
100
|
+
for (const v of this._arr) yield v;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected *_getReverseIterator(): IterableIterator<number> {
|
|
104
|
+
for (let i = this._arr.length - 1; i >= 0; i--) yield this._arr[i];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
describe('LinearBase coverage (array-backed subclass)', () => {
|
|
109
|
+
it('constructor maxLen branch: accept positive int and ignore non-int/invalid', () => {
|
|
110
|
+
const a = new TestArrayLinear([], { maxLen: 3 });
|
|
111
|
+
expect(a.maxLen).toBe(3);
|
|
112
|
+
|
|
113
|
+
const b = new TestArrayLinear([], { maxLen: 2.2 } as any);
|
|
114
|
+
expect(b.maxLen).toBe(-1);
|
|
115
|
+
|
|
116
|
+
const c = new TestArrayLinear([], { maxLen: -5 } as any);
|
|
117
|
+
expect(c.maxLen).toBe(-1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('indexOf/lastIndexOf cover empty and fromIndex normalization', () => {
|
|
121
|
+
const empty = new TestArrayLinear();
|
|
122
|
+
expect(empty.indexOf(1)).toBe(-1);
|
|
123
|
+
expect(empty.lastIndexOf(1)).toBe(-1);
|
|
124
|
+
|
|
125
|
+
const list = new TestArrayLinear([1, 2, 3, 2, 1]);
|
|
126
|
+
|
|
127
|
+
expect(list.indexOf(2, -2)).toBe(3); // length + (-2) => 3
|
|
128
|
+
expect(list.indexOf(1, -999)).toBe(0); // clamp to 0
|
|
129
|
+
expect(list.lastIndexOf(2, 999)).toBe(3); // clamp to length-1
|
|
130
|
+
|
|
131
|
+
// fromIndex < 0 => length + fromIndex (may still be negative) -> returns -1
|
|
132
|
+
expect(list.lastIndexOf(2, -999)).toBe(-1);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('concat/sort/join/toReversedArray exercise LinearBase helpers', () => {
|
|
136
|
+
const a = new TestArrayLinear([3, 1, 2]);
|
|
137
|
+
const b = new TestArrayLinear([9]);
|
|
138
|
+
|
|
139
|
+
const c = a.concat(b, 7);
|
|
140
|
+
expect(c.toArray()).toEqual([3, 1, 2, 9, 7]);
|
|
141
|
+
|
|
142
|
+
expect(c.join('-')).toBe('3-1-2-9-7');
|
|
143
|
+
expect(c.toReversedArray()).toEqual([7, 9, 2, 1, 3]);
|
|
144
|
+
|
|
145
|
+
c.sort((x, y) => x - y);
|
|
146
|
+
expect(c.toArray()).toEqual([1, 2, 3, 7, 9]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('slice/splice/fill cover negative indices and early-return branches', () => {
|
|
150
|
+
const list = new TestArrayLinear([1, 2, 3, 4]);
|
|
151
|
+
|
|
152
|
+
// slice negative end/start
|
|
153
|
+
expect(list.slice(1, -1).toArray()).toEqual([2, 3]);
|
|
154
|
+
expect(list.slice(-1, -1).toArray()).toEqual([]);
|
|
155
|
+
|
|
156
|
+
// splice clamps start/deleteCount
|
|
157
|
+
const removed = list.splice(-2, 999, 8, 9);
|
|
158
|
+
expect(removed.toArray()).toEqual([3, 4]);
|
|
159
|
+
expect(list.toArray()).toEqual([1, 2, 8, 9]);
|
|
160
|
+
|
|
161
|
+
// fill clamps and early return when start>=end
|
|
162
|
+
list.fill(0, 3, 2);
|
|
163
|
+
expect(list.toArray()).toEqual([1, 2, 8, 9]);
|
|
164
|
+
|
|
165
|
+
list.fill(5, -999, 999);
|
|
166
|
+
expect(list.toArray()).toEqual([5, 5, 5, 5]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ElementCallback, IterableElementBaseOptions, IterableElementBase } from 'src';
|
|
2
|
+
import { LinearBase, LinkedListNode } from '../../../../src/data-structures/base/linear-base';
|
|
3
|
+
|
|
4
|
+
type R = number;
|
|
5
|
+
|
|
6
|
+
class ArrayLinear extends LinearBase<number, R> {
|
|
7
|
+
override setAt(index: number, value: number): boolean {
|
|
8
|
+
throw new Error('Method not implemented.');
|
|
9
|
+
}
|
|
10
|
+
override reverse(): this {
|
|
11
|
+
throw new Error('Method not implemented.');
|
|
12
|
+
}
|
|
13
|
+
override delete(elementOrNode: number | LinkedListNode<number> | undefined): boolean {
|
|
14
|
+
throw new Error('Method not implemented.');
|
|
15
|
+
}
|
|
16
|
+
override deleteAt(pos: number): number | undefined {
|
|
17
|
+
throw new Error('Method not implemented.');
|
|
18
|
+
}
|
|
19
|
+
override addAt(index: number, newElementOrNode: number | LinkedListNode<number>): boolean {
|
|
20
|
+
throw new Error('Method not implemented.');
|
|
21
|
+
}
|
|
22
|
+
protected override _getReverseIterator(...args: any[]): IterableIterator<number> {
|
|
23
|
+
throw new Error('Method not implemented.');
|
|
24
|
+
}
|
|
25
|
+
override isEmpty(): boolean {
|
|
26
|
+
throw new Error('Method not implemented.');
|
|
27
|
+
}
|
|
28
|
+
override clear(): void {
|
|
29
|
+
throw new Error('Method not implemented.');
|
|
30
|
+
}
|
|
31
|
+
override map<EM, RM>(
|
|
32
|
+
callback: ElementCallback<number, number, EM>,
|
|
33
|
+
options?: IterableElementBaseOptions<EM, RM> | undefined,
|
|
34
|
+
thisArg?: unknown
|
|
35
|
+
): IterableElementBase<EM, RM> {
|
|
36
|
+
throw new Error('Method not implemented.');
|
|
37
|
+
}
|
|
38
|
+
override mapSame(callback: ElementCallback<number, number, number>, thisArg?: unknown): this {
|
|
39
|
+
throw new Error('Method not implemented.');
|
|
40
|
+
}
|
|
41
|
+
override filter(predicate: ElementCallback<number, number, boolean>, thisArg?: unknown): this {
|
|
42
|
+
throw new Error('Method not implemented.');
|
|
43
|
+
}
|
|
44
|
+
protected _data: number[];
|
|
45
|
+
constructor(iter: Iterable<number> = []) {
|
|
46
|
+
super();
|
|
47
|
+
this._data = Array.from(iter);
|
|
48
|
+
}
|
|
49
|
+
override get length(): number {
|
|
50
|
+
return this._data.length;
|
|
51
|
+
}
|
|
52
|
+
protected _getIterator(): IterableIterator<number> {
|
|
53
|
+
return this._data[Symbol.iterator]();
|
|
54
|
+
}
|
|
55
|
+
protected _createInstance(): this {
|
|
56
|
+
return new ArrayLinear() as any;
|
|
57
|
+
}
|
|
58
|
+
override clone(): this {
|
|
59
|
+
return new ArrayLinear(this._data) as any;
|
|
60
|
+
}
|
|
61
|
+
override push(element: number): boolean {
|
|
62
|
+
this._data.push(element);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
override pushMany(elements: Iterable<number>): boolean[] {
|
|
66
|
+
const ans: boolean[] = [];
|
|
67
|
+
for (const e of elements) ans.push(this.push(e));
|
|
68
|
+
return ans;
|
|
69
|
+
}
|
|
70
|
+
override at(index: number): number | undefined {
|
|
71
|
+
return this._data[index];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe('LinearBase concat else-branch coverage', () => {
|
|
76
|
+
it('concat(item) uses else-branch when item is not a LinearBase', () => {
|
|
77
|
+
const l = new ArrayLinear([1, 2]);
|
|
78
|
+
// Use any-call to avoid TS overload narrowing affecting runtime call site.
|
|
79
|
+
const out = (l as any).concat(3);
|
|
80
|
+
expect(out.toArray()).toEqual([1, 2, 3]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DoublyLinkedList } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Coverage-focused tests for LinearBase helpers (via DoublyLinkedList).
|
|
5
|
+
* Keep existing @example tests intact.
|
|
6
|
+
*/
|
|
7
|
+
describe('LinearBase coverage (via DoublyLinkedList)', () => {
|
|
8
|
+
it('indexOf/lastIndexOf handle fromIndex bounds and negatives', () => {
|
|
9
|
+
const list = new DoublyLinkedList<number | undefined>();
|
|
10
|
+
list.pushMany([1, 2, 3, 2, 1]);
|
|
11
|
+
|
|
12
|
+
expect(list.indexOf(2)).toBe(1);
|
|
13
|
+
expect(list.indexOf(2, 2)).toBe(3);
|
|
14
|
+
|
|
15
|
+
// negative fromIndex clamps to 0
|
|
16
|
+
expect(list.indexOf(1, -999)).toBe(0);
|
|
17
|
+
|
|
18
|
+
expect(list.lastIndexOf(2)).toBe(3);
|
|
19
|
+
// fromIndex >= length clamps to length-1
|
|
20
|
+
expect(list.lastIndexOf(1, 999)).toBe(4);
|
|
21
|
+
// negative fromIndex path (current implementation returns -1 for -1 specifically)
|
|
22
|
+
// (We mainly want to exercise the branch; behavior is asserted as-is.)
|
|
23
|
+
expect(list.lastIndexOf(2, -1)).toBe(-1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('findIndex skips undefined elements', () => {
|
|
27
|
+
const list = new DoublyLinkedList<number | undefined>();
|
|
28
|
+
list.pushMany([undefined, 1, undefined, 2]);
|
|
29
|
+
|
|
30
|
+
const spy = jest.fn((x: number) => x === 2);
|
|
31
|
+
expect(list.findIndex(spy as any)).toBe(3);
|
|
32
|
+
|
|
33
|
+
// predicate should only be called for defined elements
|
|
34
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('slice/splice cover defaults and head-insert path', () => {
|
|
38
|
+
const list = new DoublyLinkedList<number>();
|
|
39
|
+
list.pushMany([1, 2, 3, 4]);
|
|
40
|
+
|
|
41
|
+
// slice defaults
|
|
42
|
+
expect(list.slice().toArray()).toEqual([1, 2, 3, 4]);
|
|
43
|
+
expect(list.slice(1, -1).toArray()).toEqual([2, 3]);
|
|
44
|
+
|
|
45
|
+
// splice: insert at head (previousNode undefined)
|
|
46
|
+
const removed0 = list.splice(0, 0, 9, 8);
|
|
47
|
+
expect(removed0.toArray()).toEqual([]);
|
|
48
|
+
expect(list.toArray()).toEqual([9, 8, 1, 2, 3, 4]);
|
|
49
|
+
|
|
50
|
+
// splice: delete + insert in middle
|
|
51
|
+
const removed = list.splice(2, 2, 7);
|
|
52
|
+
expect(removed.toArray()).toEqual([1, 2]);
|
|
53
|
+
expect(list.toArray()).toEqual([9, 8, 7, 3, 4]);
|
|
54
|
+
|
|
55
|
+
// splice: negative start
|
|
56
|
+
const removed2 = list.splice(-1, 1);
|
|
57
|
+
expect(removed2.toArray()).toEqual([4]);
|
|
58
|
+
expect(list.toArray()).toEqual([9, 8, 7, 3]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('reduceRight covers initialValue defaulting', () => {
|
|
62
|
+
const list = new DoublyLinkedList<number>();
|
|
63
|
+
list.pushMany([1, 2, 3]);
|
|
64
|
+
|
|
65
|
+
// no initialValue => accumulator defaults (0 as U)
|
|
66
|
+
const sum = list.reduceRight((acc: number, x: number) => acc + x);
|
|
67
|
+
expect(sum).toBe(6);
|
|
68
|
+
|
|
69
|
+
const sum2 = list.reduceRight((acc: number, x: number) => acc + x, 10);
|
|
70
|
+
expect(sum2).toBe(16);
|
|
71
|
+
});
|
|
72
|
+
});
|