data-structure-typed 2.2.3 → 2.2.5

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +70 -11
  3. package/dist/cjs/index.cjs +341 -75
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs-legacy/index.cjs +343 -75
  6. package/dist/cjs-legacy/index.cjs.map +1 -1
  7. package/dist/esm/index.mjs +341 -75
  8. package/dist/esm/index.mjs.map +1 -1
  9. package/dist/esm-legacy/index.mjs +343 -75
  10. package/dist/esm-legacy/index.mjs.map +1 -1
  11. package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -2
  12. package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +5 -5
  13. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +2 -3
  14. package/dist/types/data-structures/binary-tree/bst.d.ts +142 -28
  15. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +2 -2
  16. package/dist/types/data-structures/binary-tree/tree-counter.d.ts +4 -5
  17. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +5 -5
  18. package/dist/types/types/data-structures/binary-tree/bst.d.ts +5 -5
  19. package/dist/umd/data-structure-typed.js +343 -75
  20. package/dist/umd/data-structure-typed.js.map +1 -1
  21. package/dist/umd/data-structure-typed.min.js +3 -3
  22. package/dist/umd/data-structure-typed.min.js.map +1 -1
  23. package/package.json +2 -2
  24. package/src/data-structures/binary-tree/avl-tree-counter.ts +1 -2
  25. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +9 -8
  26. package/src/data-structures/binary-tree/avl-tree.ts +4 -5
  27. package/src/data-structures/binary-tree/bst.ts +495 -85
  28. package/src/data-structures/binary-tree/red-black-tree.ts +1 -2
  29. package/src/data-structures/binary-tree/tree-counter.ts +5 -7
  30. package/src/data-structures/binary-tree/tree-multi-map.ts +7 -8
  31. package/src/types/data-structures/binary-tree/bst.ts +5 -5
  32. package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +2 -2
  33. package/test/unit/data-structures/binary-tree/bst.test.ts +459 -8
  34. package/test/unit/data-structures/binary-tree/overall.test.ts +2 -2
  35. package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +1 -1
  36. package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +2 -2
@@ -8,7 +8,6 @@
8
8
 
9
9
  import type {
10
10
  BinaryTreeDeleteResult,
11
- BinaryTreeOptions,
12
11
  CRUD,
13
12
  EntryCallback,
14
13
  FamilyPosition,
@@ -463,7 +462,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
463
462
 
464
463
  override map<MK = K, MV = V, MR = any>(
465
464
  callback: EntryCallback<K, V | undefined, [MK, MV]>,
466
- options?: Partial<BinaryTreeOptions<MK, MV, MR>>,
465
+ options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
467
466
  thisArg?: unknown
468
467
  ): RedBlackTree<MK, MV, MR> {
469
468
  const out = this._createLike<MK, MV, MR>([], options);
@@ -8,7 +8,6 @@
8
8
 
9
9
  import type {
10
10
  BinaryTreeDeleteResult,
11
- BinaryTreeOptions,
12
11
  BSTNOptKeyOrNode,
13
12
  EntryCallback,
14
13
  FamilyPosition,
@@ -17,7 +16,6 @@ import type {
17
16
  RBTNColor,
18
17
  TreeCounterOptions
19
18
  } from '../../types';
20
- import { BSTOptions } from '../../types';
21
19
  import { BSTNode } from './bst';
22
20
  import { IBinaryTree } from '../../interfaces';
23
21
  import { RedBlackTree } from './red-black-tree';
@@ -432,7 +430,7 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
432
430
  */
433
431
  override map<MK = K, MV = V, MR = any>(
434
432
  callback: EntryCallback<K, V | undefined, [MK, MV]>,
435
- options?: Partial<BinaryTreeOptions<MK, MV, MR>>,
433
+ options?: Partial<TreeCounterOptions<MK, MV, MR>>,
436
434
  thisArg?: unknown
437
435
  ): TreeCounter<MK, MV, MR> {
438
436
  const out = this._createLike<MK, MV, MR>([], options);
@@ -465,10 +463,10 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
465
463
  * @param [options] - Optional constructor options for the like-kind instance.
466
464
  * @returns An empty like-kind instance.
467
465
  */
468
- protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<BSTOptions<TK, TV, TR>>): this {
466
+ protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<TreeCounterOptions<TK, TV, TR>>): this {
469
467
  const Ctor = this.constructor as unknown as new (
470
468
  iter?: Iterable<TK | BSTNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
471
- opts?: BSTOptions<TK, TV, TR>
469
+ opts?: TreeCounterOptions<TK, TV, TR>
472
470
  ) => this;
473
471
  return new Ctor([], { ...this._snapshotOptions<TK, TV, TR>(), ...(options ?? {}) }) as unknown as this;
474
472
  }
@@ -485,11 +483,11 @@ export class TreeCounter<K = any, V = any, R = any> extends RedBlackTree<K, V, R
485
483
  */
486
484
  protected override _createLike<TK = K, TV = V, TR = R>(
487
485
  iter: Iterable<TK | BSTNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR> = [],
488
- options?: Partial<BSTOptions<TK, TV, TR>>
486
+ options?: Partial<TreeCounterOptions<TK, TV, TR>>
489
487
  ): TreeCounter<TK, TV, TR> {
490
488
  const Ctor = this.constructor as unknown as new (
491
489
  iter?: Iterable<TK | BSTNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
492
- opts?: BSTOptions<TK, TV, TR>
490
+ opts?: TreeCounterOptions<TK, TV, TR>
493
491
  ) => TreeCounter<TK, TV, TR>;
494
492
  return new Ctor(iter as unknown as Iterable<TK | any>, {
495
493
  ...this._snapshotOptions<TK, TV, TR>(),
@@ -12,7 +12,6 @@ import type {
12
12
  EntryCallback,
13
13
  FamilyPosition,
14
14
  RBTNColor,
15
- RedBlackTreeOptions,
16
15
  TreeMultiMapOptions
17
16
  } from '../../types';
18
17
  import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
@@ -476,13 +475,13 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
476
475
 
477
476
  override map<MK = K, MVArr extends unknown[] = V[], MR = any>(
478
477
  callback: EntryCallback<K, V[] | undefined, [MK, MVArr]>,
479
- options?: Partial<RedBlackTreeOptions<MK, MVArr, MR>>,
478
+ options?: Partial<TreeMultiMapOptions<MK, MVArr, MR>>,
480
479
  thisArg?: unknown
481
480
  ): TreeMultiMap<MK, ElemOf<MVArr>, MR>;
482
481
 
483
482
  override map<MK = K, MV = V[], MR = any>(
484
483
  callback: EntryCallback<K, V[] | undefined, [MK, MV]>,
485
- options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
484
+ options?: Partial<TreeMultiMapOptions<MK, MV, MR>>,
486
485
  thisArg?: unknown
487
486
  ): RedBlackTree<MK, MV, MR>;
488
487
 
@@ -499,7 +498,7 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
499
498
  */
500
499
  override map<MK, MV, MR extends object>(
501
500
  callback: EntryCallback<K, V[] | undefined, [MK, MV]>,
502
- options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
501
+ options?: Partial<TreeMultiMapOptions<MK, MV, MR>>,
503
502
  thisArg?: unknown
504
503
  ): RedBlackTree<MK, MV, MR> {
505
504
  const out = this._createLike<MK, MV, MR>([], options);
@@ -517,10 +516,10 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
517
516
  * @param [options] - Optional constructor options for the like-kind instance.
518
517
  * @returns An empty like-kind instance.
519
518
  */
520
- protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<RedBlackTreeOptions<TK, TV, TR>>): this {
519
+ protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<TreeMultiMapOptions<TK, TV, TR>>): this {
521
520
  const Ctor = this.constructor as unknown as new (
522
521
  iter?: Iterable<TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
523
- opts?: RedBlackTreeOptions<TK, TV, TR>
522
+ opts?: TreeMultiMapOptions<TK, TV, TR>
524
523
  ) => RedBlackTree<TK, TV, TR>;
525
524
  return new Ctor([], { ...(this._snapshotOptions?.<TK, TV, TR>() ?? {}), ...(options ?? {}) }) as unknown as this;
526
525
  }
@@ -539,11 +538,11 @@ export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[]
539
538
  iter: Iterable<
540
539
  TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR
541
540
  > = [],
542
- options?: Partial<RedBlackTreeOptions<TK, TV, TR>>
541
+ options?: Partial<TreeMultiMapOptions<TK, TV, TR>>
543
542
  ): RedBlackTree<TK, TV, TR> {
544
543
  const Ctor = this.constructor as unknown as new (
545
544
  iter?: Iterable<TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
546
- opts?: RedBlackTreeOptions<TK, TV, TR>
545
+ opts?: TreeMultiMapOptions<TK, TV, TR>
547
546
  ) => RedBlackTree<TK, TV, TR>;
548
547
  return new Ctor(iter, { ...(this._snapshotOptions?.<TK, TV, TR>() ?? {}), ...(options ?? {}) });
549
548
  }
@@ -1,10 +1,10 @@
1
1
  import type { BinaryTreeOptions } from './binary-tree';
2
- import { Comparable } from '../../utils';
3
- import { OptValue } from '../../common';
2
+ import type { Comparator, OptValue } from '../../common';
4
3
 
5
- export type BSTOptions<K, V, R> = Omit<BinaryTreeOptions<K, V, R>, 'isDuplicate'> & {
6
- specifyComparable?: (key: K) => Comparable
7
- isReverse?: boolean;
4
+ type BSTBaseOptions<K, V, R> = Omit<BinaryTreeOptions<K, V, R>, 'isDuplicate'>;
5
+
6
+ export type BSTOptions<K, V, R> = BSTBaseOptions<K, V, R> & {
7
+ comparator?: Comparator<K>;
8
8
  }
9
9
 
10
10
  export type BSTNOptKey<K> = K | undefined;
@@ -739,7 +739,7 @@ describe('AVLTreeCounter toEntryFn', () => {
739
739
  { obj: { id: 5 } }
740
740
  ])
741
741
  ).toThrowError(
742
- `When comparing object types, a custom specifyComparable must be defined in the constructor's options.`
742
+ `When comparing object type keys, a custom comparator must be provided in the constructor's options!`
743
743
  );
744
744
  });
745
745
 
@@ -747,7 +747,7 @@ describe('AVLTreeCounter toEntryFn', () => {
747
747
  const avlCounter = new AVLTreeCounter<{ obj: { id: number } }, number>(
748
748
  [{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
749
749
  {
750
- specifyComparable: key => key.obj.id
750
+ comparator: (a, b) => a.obj.id - b.obj.id
751
751
  }
752
752
  );
753
753
 
@@ -980,7 +980,7 @@ describe('BST operations test recursively', () => {
980
980
 
981
981
  if (isTestStackOverflow) {
982
982
  it('should getLeftMost', () => {
983
- const bst = new BST<number>([], { specifyComparable: key => key });
983
+ const bst = new BST<number>([]);
984
984
  for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i);
985
985
 
986
986
  expect(() => {
@@ -1015,7 +1015,7 @@ describe('BST isBST', function () {
1015
1015
 
1016
1016
  it('isBST when variant is Max', () => {
1017
1017
  const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
1018
- isReverse: true
1018
+ comparator: (a, b) => b - a
1019
1019
  });
1020
1020
  bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
1021
1021
  expect(bst.isBST()).toBe(true);
@@ -1537,19 +1537,16 @@ describe('BST iterative methods not map mode test', () => {
1537
1537
  });
1538
1538
 
1539
1539
  describe('BST constructor and comparator edge cases', () => {
1540
- it('should support specifyComparable and isReverse', () => {
1540
+ it('should support comparator', () => {
1541
1541
  const bst = new BST<number>([], {
1542
- specifyComparable: k => -k,
1543
- isReverse: true
1542
+ comparator: (a, b) => b - a
1544
1543
  });
1545
1544
  bst.add(1);
1546
1545
  bst.add(2);
1547
- expect(bst.isReverse).toBe(true);
1548
- expect(bst['_specifyComparable']).toBeDefined();
1549
1546
  expect([...bst.keys()]).toEqual([2, 1]);
1550
1547
  });
1551
1548
 
1552
- it('should throw if compare object key without specifyComparable', () => {
1549
+ it('should throw if compare object key without comparator', () => {
1553
1550
  const bst = new BST<any>();
1554
1551
  expect(() => bst.comparator({ a: 1 }, { a: 2 })).toThrow();
1555
1552
  });
@@ -2235,6 +2232,460 @@ describe('BST Advanced Bound Methods Tests', () => {
2235
2232
  });
2236
2233
  });
2237
2234
 
2235
+ describe('BST Range Query Methods', () => {
2236
+ let bst: BST<number, string>;
2237
+
2238
+ beforeEach(() => {
2239
+ // Create a balanced BST: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20]
2240
+ bst = new BST([10, 5, 15, 3, 7, 13, 17, 1, 4, 6, 9, 11, 14, 16, 19, 20]);
2241
+ });
2242
+
2243
+ describe('ceilingEntry - finds >= key (minimum value >= target)', () => {
2244
+ test('should find ceiling when key exists', () => {
2245
+ const result = bst.ceilingEntry(10);
2246
+ expect(result).toBeDefined();
2247
+ expect(result?.key).toBe(10);
2248
+ });
2249
+
2250
+ test('should find ceiling when key does not exist but higher value exists', () => {
2251
+ const result = bst.ceilingEntry(8);
2252
+ expect(result).toBeDefined();
2253
+ expect(result?.key).toBe(9);
2254
+ });
2255
+
2256
+ test('should return undefined when no ceiling exists (key greater than all)', () => {
2257
+ const result = bst.ceilingEntry(100);
2258
+ expect(result).toBeUndefined();
2259
+ });
2260
+
2261
+ test('should find minimum element as ceiling for key smaller than all', () => {
2262
+ const result = bst.ceilingEntry(-10);
2263
+ expect(result).toBeDefined();
2264
+ expect(result?.key).toBe(1);
2265
+ });
2266
+
2267
+ test('should handle ceiling with node input', () => {
2268
+ const targetNode = bst.getNode(7);
2269
+ expect(targetNode).toBeDefined();
2270
+ const result = bst.ceilingEntry(targetNode!);
2271
+ expect(result?.key).toBe(7);
2272
+ });
2273
+
2274
+ test('should handle ceiling with entry input', () => {
2275
+ const result = bst.ceilingEntry([11, 'test']);
2276
+ expect(result).toBeDefined();
2277
+ expect(result?.key).toBe(11);
2278
+ });
2279
+
2280
+ test('should handle null/undefined inputs', () => {
2281
+ expect(bst.ceilingEntry(null)).toBeUndefined();
2282
+ expect(bst.ceilingEntry(undefined)).toBeUndefined();
2283
+ });
2284
+
2285
+ test('should work with ITERATIVE mode', () => {
2286
+ const result = bst.ceilingEntry(12, 'ITERATIVE');
2287
+ expect(result).toBeDefined();
2288
+ expect(result?.key).toBe(13);
2289
+ });
2290
+
2291
+ test('should work with RECURSIVE mode', () => {
2292
+ const result = bst.ceilingEntry(12, 'RECURSIVE');
2293
+ expect(result).toBeDefined();
2294
+ expect(result?.key).toBe(13);
2295
+ });
2296
+
2297
+ test('should find exact match as ceiling', () => {
2298
+ const result = bst.ceilingEntry(15);
2299
+ expect(result).toBeDefined();
2300
+ expect(result?.key).toBe(15);
2301
+ });
2302
+ });
2303
+
2304
+ describe('higherEntry - finds > key (minimum value > target)', () => {
2305
+ test('should find higher when key exists (exclude exact match)', () => {
2306
+ const result = bst.higherEntry(10);
2307
+ expect(result).toBeDefined();
2308
+ expect(result?.key).toBe(11);
2309
+ expect(result?.key).not.toBe(10);
2310
+ });
2311
+
2312
+ test('should find higher when key does not exist', () => {
2313
+ const result = bst.higherEntry(8);
2314
+ expect(result).toBeDefined();
2315
+ expect(result?.key).toBe(9);
2316
+ });
2317
+
2318
+ test('should return undefined when no higher exists (key >= all)', () => {
2319
+ const result = bst.higherEntry(20);
2320
+ expect(result).toBeUndefined();
2321
+ });
2322
+
2323
+ test('should find minimum element as higher for key < all', () => {
2324
+ const result = bst.higherEntry(-10);
2325
+ expect(result).toBeDefined();
2326
+ expect(result?.key).toBe(1);
2327
+ });
2328
+
2329
+ test('should not return the key itself', () => {
2330
+ const result = bst.higherEntry(7);
2331
+ expect(result?.key).not.toBe(7);
2332
+ expect(result?.key).toBe(9);
2333
+ });
2334
+
2335
+ test('should handle higher with node input', () => {
2336
+ const targetNode = bst.getNode(5);
2337
+ expect(targetNode).toBeDefined();
2338
+ const result = bst.higherEntry(targetNode!);
2339
+ expect(result?.key).toBeGreaterThan(5);
2340
+ expect(result?.key).toBe(6);
2341
+ });
2342
+
2343
+ test('should work with ITERATIVE mode', () => {
2344
+ const result = bst.higherEntry(13, 'ITERATIVE');
2345
+ expect(result).toBeDefined();
2346
+ expect(result?.key).toBe(14);
2347
+ });
2348
+
2349
+ test('should work with RECURSIVE mode', () => {
2350
+ const result = bst.higherEntry(13, 'RECURSIVE');
2351
+ expect(result).toBeDefined();
2352
+ expect(result?.key).toBe(14);
2353
+ });
2354
+ });
2355
+
2356
+ describe('floorEntry - finds <= key (maximum value <= target)', () => {
2357
+ test('should find floor when key exists', () => {
2358
+ const result = bst.floorEntry(10);
2359
+ expect(result).toBeDefined();
2360
+ expect(result?.key).toBe(10);
2361
+ });
2362
+
2363
+ test('should find floor when key does not exist but lower value exists', () => {
2364
+ const result = bst.floorEntry(12);
2365
+ expect(result).toBeDefined();
2366
+ expect(result?.key).toBe(11);
2367
+ });
2368
+
2369
+ test('should return undefined when no floor exists (key less than all)', () => {
2370
+ const result = bst.floorEntry(-10);
2371
+ expect(result).toBeUndefined();
2372
+ });
2373
+
2374
+ test('should find maximum element as floor for key greater than all', () => {
2375
+ const result = bst.floorEntry(100);
2376
+ expect(result).toBeDefined();
2377
+ expect(result?.key).toBe(20);
2378
+ });
2379
+
2380
+ test('should handle floor with node input', () => {
2381
+ const targetNode = bst.getNode(13);
2382
+ expect(targetNode).toBeDefined();
2383
+ const result = bst.floorEntry(targetNode!);
2384
+ expect(result?.key).toBe(13);
2385
+ });
2386
+
2387
+ test('should handle floor with entry input', () => {
2388
+ const result = bst.floorEntry([16, 'test']);
2389
+ expect(result).toBeDefined();
2390
+ expect(result?.key).toBe(16);
2391
+ });
2392
+
2393
+ test('should handle null/undefined inputs', () => {
2394
+ expect(bst.floorEntry(null)).toBeUndefined();
2395
+ expect(bst.floorEntry(undefined)).toBeUndefined();
2396
+ });
2397
+
2398
+ test('should work with ITERATIVE mode', () => {
2399
+ const result = bst.floorEntry(12, 'ITERATIVE');
2400
+ expect(result).toBeDefined();
2401
+ expect(result?.key).toBe(11);
2402
+ });
2403
+
2404
+ test('should work with RECURSIVE mode', () => {
2405
+ const result = bst.floorEntry(12, 'RECURSIVE');
2406
+ expect(result).toBeDefined();
2407
+ expect(result?.key).toBe(11);
2408
+ });
2409
+
2410
+ test('should find exact match as floor', () => {
2411
+ const result = bst.floorEntry(15);
2412
+ expect(result).toBeDefined();
2413
+ expect(result?.key).toBe(15);
2414
+ });
2415
+
2416
+ test('should correctly find floor between two keys', () => {
2417
+ const result = bst.floorEntry(8);
2418
+ expect(result).toBeDefined();
2419
+ expect(result?.key).toBe(7);
2420
+ expect(result?.key).toBeLessThan(8);
2421
+ });
2422
+ });
2423
+
2424
+ describe('lowerEntry - finds < key (maximum value < target)', () => {
2425
+ test('should find lower when key exists (exclude exact match)', () => {
2426
+ const result = bst.lowerEntry(10);
2427
+ expect(result).toBeDefined();
2428
+ expect(result?.key).toBe(9);
2429
+ expect(result?.key).not.toBe(10);
2430
+ });
2431
+
2432
+ test('should find lower when key does not exist', () => {
2433
+ const result = bst.lowerEntry(12);
2434
+ expect(result).toBeDefined();
2435
+ expect(result?.key).toBe(11);
2436
+ });
2437
+
2438
+ test('should return undefined when no lower exists (key <= all)', () => {
2439
+ const result = bst.lowerEntry(1);
2440
+ expect(result).toBeUndefined();
2441
+ });
2442
+
2443
+ test('should find maximum element as lower for key > all', () => {
2444
+ const result = bst.lowerEntry(100);
2445
+ expect(result).toBeDefined();
2446
+ expect(result?.key).toBe(20);
2447
+ });
2448
+
2449
+ test('should not return the key itself', () => {
2450
+ const result = bst.lowerEntry(15);
2451
+ expect(result?.key).not.toBe(15);
2452
+ expect(result?.key).toBe(14);
2453
+ });
2454
+
2455
+ test('should handle lower with node input', () => {
2456
+ const targetNode = bst.getNode(13);
2457
+ expect(targetNode).toBeDefined();
2458
+ const result = bst.lowerEntry(targetNode!);
2459
+ expect(result?.key).toBeLessThan(13);
2460
+ expect(result?.key).toBe(11);
2461
+ });
2462
+
2463
+ test('should handle lower with entry input', () => {
2464
+ const result = bst.lowerEntry([17, 'test']);
2465
+ expect(result).toBeDefined();
2466
+ expect(result?.key).toBe(16);
2467
+ });
2468
+
2469
+ test('should work with ITERATIVE mode', () => {
2470
+ const result = bst.lowerEntry(14, 'ITERATIVE');
2471
+ expect(result).toBeDefined();
2472
+ expect(result?.key).toBe(13);
2473
+ });
2474
+
2475
+ test('should work with RECURSIVE mode', () => {
2476
+ const result = bst.lowerEntry(14, 'RECURSIVE');
2477
+ expect(result).toBeDefined();
2478
+ expect(result?.key).toBe(13);
2479
+ });
2480
+ });
2481
+
2482
+ describe('Edge cases and special scenarios', () => {
2483
+ test('single element tree - ceiling', () => {
2484
+ const singleBst = new BST([5]);
2485
+ expect(singleBst.ceilingEntry(5)?.key).toBe(5);
2486
+ expect(singleBst.ceilingEntry(3)?.key).toBe(5);
2487
+ expect(singleBst.ceilingEntry(7)).toBeUndefined();
2488
+ });
2489
+
2490
+ test('single element tree - higher', () => {
2491
+ const singleBst = new BST([5]);
2492
+ expect(singleBst.higherEntry(5)).toBeUndefined();
2493
+ expect(singleBst.higherEntry(3)?.key).toBe(5);
2494
+ });
2495
+
2496
+ test('single element tree - floor', () => {
2497
+ const singleBst = new BST([5]);
2498
+ expect(singleBst.floorEntry(5)?.key).toBe(5);
2499
+ expect(singleBst.floorEntry(7)?.key).toBe(5);
2500
+ expect(singleBst.floorEntry(3)).toBeUndefined();
2501
+ });
2502
+
2503
+ test('single element tree - lower', () => {
2504
+ const singleBst = new BST([5]);
2505
+ expect(singleBst.lowerEntry(5)).toBeUndefined();
2506
+ expect(singleBst.lowerEntry(7)?.key).toBe(5);
2507
+ });
2508
+
2509
+ test('empty tree handling', () => {
2510
+ const emptyBst = new BST<number, string>();
2511
+ expect(emptyBst.ceilingEntry(5)).toBeUndefined();
2512
+ expect(emptyBst.higherEntry(5)).toBeUndefined();
2513
+ expect(emptyBst.floorEntry(5)).toBeUndefined();
2514
+ expect(emptyBst.lowerEntry(5)).toBeUndefined();
2515
+ });
2516
+
2517
+ test('ceiling and floor of adjacent keys', () => {
2518
+ const ceiling = bst.ceilingEntry(5);
2519
+ const floor = bst.floorEntry(6);
2520
+ expect(ceiling?.key).toBe(5);
2521
+ bst.print()
2522
+ expect(floor?.key).toBe(6);
2523
+ });
2524
+
2525
+ test('higher and lower of adjacent keys', () => {
2526
+ const higher = bst.higherEntry(5);
2527
+ const lower = bst.lowerEntry(6);
2528
+ expect(higher?.key).toBe(6);
2529
+ expect(lower?.key).toBe(5);
2530
+ });
2531
+ });
2532
+
2533
+ describe('Predicate-based search', () => {
2534
+ test('ceiling with predicate function', () => {
2535
+ const result = bst.ceilingEntry((node: BSTNode<number, string>) => node.key >= 10);
2536
+ expect(result).toBeDefined();
2537
+ expect(result?.key).toBeGreaterThanOrEqual(10);
2538
+ });
2539
+
2540
+ test('floor with predicate function', () => {
2541
+ const result = bst.floorEntry((node: BSTNode<number, string>) => node.key <= 15);
2542
+ expect(result).toBeDefined();
2543
+ expect(result?.key).toBeLessThanOrEqual(15);
2544
+ });
2545
+
2546
+ test('higher with predicate function', () => {
2547
+ const result = bst.higherEntry((node: BSTNode<number, string>) => node.key > 10);
2548
+ expect(result).toBeDefined();
2549
+ expect(result?.key).toBeGreaterThan(10);
2550
+ });
2551
+
2552
+ test('lower with predicate function', () => {
2553
+ const result = bst.lowerEntry((node: BSTNode<number, string>) => node.key < 15);
2554
+ expect(result).toBeDefined();
2555
+ expect(result?.key).toBeLessThan(15);
2556
+ });
2557
+ });
2558
+
2559
+ describe('Custom comparator', () => {
2560
+ test('should work with reverse order comparator', () => {
2561
+ const reverseBst = new BST([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20], {
2562
+ comparator: (a: number, b: number) => b - a // reverse order
2563
+ });
2564
+
2565
+ // In reverse order tree: keys are stored in descending order
2566
+ // ceiling (>=) should still work correctly
2567
+ const ceiling = reverseBst.ceilingEntry(10);
2568
+ expect(ceiling).toBeDefined();
2569
+ expect(ceiling?.key).toBeLessThanOrEqual(10);
2570
+ });
2571
+
2572
+ test('should work with string comparator', () => {
2573
+ const stringBst = new BST(
2574
+ [
2575
+ { name: 'Alice', id: 1 },
2576
+ { name: 'Bob', id: 2 },
2577
+ { name: 'Charlie', id: 3 },
2578
+ { name: 'David', id: 4 },
2579
+ { name: 'Eve', id: 5 }
2580
+ ],
2581
+ {
2582
+ comparator: (a, b) => a.name.localeCompare(b.name)
2583
+ }
2584
+ );
2585
+
2586
+ const ceiling = stringBst.ceilingEntry({ name: 'Bob', id: 0 });
2587
+ expect(ceiling).toBeDefined();
2588
+ expect(ceiling?.key.name).toBe('Bob');
2589
+ });
2590
+ });
2591
+
2592
+ describe('Performance and correctness validation', () => {
2593
+ test('all range methods return nodes in order', () => {
2594
+ const ceiling = bst.ceilingEntry(10);
2595
+ const higher = bst.higherEntry(10);
2596
+ const floor = bst.floorEntry(10);
2597
+ const lower = bst.lowerEntry(10);
2598
+
2599
+ expect(floor?.key).toBeLessThanOrEqual(10);
2600
+ expect(ceiling?.key).toBeGreaterThanOrEqual(10);
2601
+ expect(higher?.key).toBeGreaterThan(10);
2602
+ expect(lower?.key).toBeLessThan(10);
2603
+ });
2604
+
2605
+ test('range query iteration with ceiling/higher', () => {
2606
+ const results: number[] = [];
2607
+ let node = bst.ceilingEntry(5);
2608
+ let count = 0;
2609
+ while (node && node.key <= 15 && count < 20) {
2610
+ results.push(node.key);
2611
+ node = bst.higherEntry(node.key);
2612
+ count++;
2613
+ }
2614
+
2615
+ // Should iterate through nodes 5, 6, 7, ..., 15
2616
+ expect(results.length).toBeGreaterThan(0);
2617
+ expect(results[0]).toBeGreaterThanOrEqual(5);
2618
+ expect(results[results.length - 1]).toBeLessThanOrEqual(15);
2619
+ // Verify ascending order
2620
+ for (let i = 1; i < results.length; i++) {
2621
+ expect(results[i]).toBeGreaterThan(results[i - 1]);
2622
+ }
2623
+ });
2624
+
2625
+ test('range query iteration with floor/lower', () => {
2626
+ const results: number[] = [];
2627
+ let node = bst.floorEntry(15);
2628
+ let count = 0;
2629
+ while (node && node.key >= 5 && count < 20) {
2630
+ results.push(node.key);
2631
+ node = bst.lowerEntry(node.key);
2632
+ count++;
2633
+ }
2634
+
2635
+ // Should iterate through nodes 15, 14, 13, ..., 5
2636
+ expect(results.length).toBeGreaterThan(0);
2637
+ expect(results[0]).toBeLessThanOrEqual(15);
2638
+ expect(results[results.length - 1]).toBeGreaterThanOrEqual(5);
2639
+ // Verify descending order
2640
+ for (let i = 1; i < results.length; i++) {
2641
+ expect(results[i]).toBeLessThan(results[i - 1]);
2642
+ }
2643
+ });
2644
+ });
2645
+
2646
+ describe('Boundary value testing', () => {
2647
+ test('boundary: ceiling at min value', () => {
2648
+ const result = bst.ceilingEntry(1);
2649
+ expect(result?.key).toBe(1);
2650
+ });
2651
+
2652
+ test('boundary: floor at max value', () => {
2653
+ const result = bst.floorEntry(20);
2654
+ expect(result?.key).toBe(20);
2655
+ });
2656
+
2657
+ test('boundary: higher at second-last value', () => {
2658
+ const result = bst.higherEntry(19);
2659
+ expect(result?.key).toBe(20);
2660
+ });
2661
+
2662
+ test('boundary: lower at second value', () => {
2663
+ const result = bst.lowerEntry(3);
2664
+ expect(result?.key).toBe(1);
2665
+ });
2666
+
2667
+ test('boundary: ceiling slightly below min', () => {
2668
+ const result = bst.ceilingEntry(0);
2669
+ expect(result?.key).toBe(1);
2670
+ });
2671
+
2672
+ test('boundary: floor slightly above max', () => {
2673
+ const result = bst.floorEntry(21);
2674
+ expect(result?.key).toBe(20);
2675
+ });
2676
+
2677
+ test('boundary: higher at max (should be undefined)', () => {
2678
+ const result = bst.higherEntry(20);
2679
+ expect(result).toBeUndefined();
2680
+ });
2681
+
2682
+ test('boundary: lower at min (should be undefined)', () => {
2683
+ const result = bst.lowerEntry(1);
2684
+ expect(result).toBeUndefined();
2685
+ });
2686
+ });
2687
+ });
2688
+
2238
2689
  describe('classic use', () => {
2239
2690
  it('@example basic BST creation and add operation', () => {
2240
2691
  // Create a simple BST with numeric keys
@@ -58,7 +58,7 @@ describe('Overall BinaryTree Test', () => {
58
58
  it('Should clone a BST works fine', () => {
59
59
  const bst = new BST<number>([3, 6, 7, 1, 9], {
60
60
  iterationType: 'RECURSIVE',
61
- isReverse: true
61
+ comparator: (a, b) => b - a
62
62
  });
63
63
  expect(bst.size).toBe(5);
64
64
  expect(bst.root?.key).toBe(6);
@@ -100,7 +100,7 @@ describe('Overall BinaryTree Test', () => {
100
100
  it('Should clone a AVLTree works fine', () => {
101
101
  const avl = new AVLTree<number>([3, 6, 7, 1, 9], {
102
102
  iterationType: 'RECURSIVE',
103
- isReverse: true
103
+ comparator: (a, b) => b - a
104
104
  });
105
105
  expect(avl.size).toBe(5);
106
106
  avl.add(2);