data-structure-typed 2.2.4 → 2.2.6

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 (30) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/cjs/index.cjs +327 -57
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs-legacy/index.cjs +329 -57
  5. package/dist/cjs-legacy/index.cjs.map +1 -1
  6. package/dist/esm/index.mjs +327 -57
  7. package/dist/esm/index.mjs.map +1 -1
  8. package/dist/esm-legacy/index.mjs +329 -57
  9. package/dist/esm-legacy/index.mjs.map +1 -1
  10. package/dist/types/data-structures/base/iterable-entry-base.d.ts +6 -0
  11. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +14 -57
  12. package/dist/types/data-structures/binary-tree/bst.d.ts +110 -96
  13. package/dist/umd/data-structure-typed.js +329 -57
  14. package/dist/umd/data-structure-typed.js.map +1 -1
  15. package/dist/umd/data-structure-typed.min.js +3 -3
  16. package/dist/umd/data-structure-typed.min.js.map +1 -1
  17. package/package.json +5 -3
  18. package/src/common/index.ts +2 -4
  19. package/src/data-structures/base/iterable-entry-base.ts +9 -0
  20. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +3 -1
  21. package/src/data-structures/binary-tree/binary-tree.ts +67 -0
  22. package/src/data-structures/binary-tree/bst.ts +658 -71
  23. package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +1 -1
  24. package/test/unit/data-structures/binary-tree/bst.test.ts +1385 -318
  25. package/test/unit/data-structures/binary-tree/overall.test.ts +2 -2
  26. package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +1 -1
  27. package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +13 -11
  28. package/tsup.config.js +6 -0
  29. package/tsup.leetcode.config.js +3 -0
  30. package/tsup.node.config.js +12 -0
@@ -492,7 +492,6 @@ describe('BST operations test', () => {
492
492
  it('should search in range', () => {
493
493
  const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
494
494
  expect(bst.rangeSearch([4, 12])).toEqual([5, 7, 10, 12]);
495
- expect(() => bst.rangeSearch([12, 4])).toThrow('low must be less than or equal to high');
496
495
  expect(bst.rangeSearch([12, 12])).toEqual([12]);
497
496
  });
498
497
  });
@@ -1015,7 +1014,7 @@ describe('BST isBST', function () {
1015
1014
 
1016
1015
  it('isBST when variant is Max', () => {
1017
1016
  const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
1018
- comparator: (a, b) => b - a,
1017
+ comparator: (a, b) => b - a
1019
1018
  });
1020
1019
  bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
1021
1020
  expect(bst.isBST()).toBe(true);
@@ -1539,7 +1538,7 @@ describe('BST iterative methods not map mode test', () => {
1539
1538
  describe('BST constructor and comparator edge cases', () => {
1540
1539
  it('should support comparator', () => {
1541
1540
  const bst = new BST<number>([], {
1542
- comparator: (a, b) => b - a,
1541
+ comparator: (a, b) => b - a
1543
1542
  });
1544
1543
  bst.add(1);
1545
1544
  bst.add(2);
@@ -1613,7 +1612,7 @@ describe('BST _keyValueNodeOrEntryToNodeAndValue edge', () => {
1613
1612
  });
1614
1613
  });
1615
1614
 
1616
- describe('BST lowerBound and upperBound', () => {
1615
+ describe('BST ceiling and higher', () => {
1617
1616
  let bst: BST<number, string>;
1618
1617
 
1619
1618
  beforeEach(() => {
@@ -1621,184 +1620,184 @@ describe('BST lowerBound and upperBound', () => {
1621
1620
  bst = new BST<number, string>([10, 5, 15, 3, 7, 12, 20, 1, 4, 6, 8, 11, 13, 18, 25]);
1622
1621
  });
1623
1622
 
1624
- describe('lowerBound', () => {
1625
- it('should return the node with exact key match', () => {
1623
+ describe('ceiling', () => {
1624
+ it('should return the key with exact key match', () => {
1626
1625
  // Test for key that exists in tree
1627
- const node = bst.lowerBound(10);
1628
- expect(node).toBeDefined();
1629
- expect(node?.key).toBe(10);
1626
+ const key = bst.ceiling(10);
1627
+ expect(key).toBeDefined();
1628
+ expect(key).toBe(10);
1630
1629
  });
1631
1630
 
1632
- it('should return the smallest node >= key when exact match not found', () => {
1631
+ it('should return the smallest key >= key when exact match not found', () => {
1633
1632
  // Test for key 9 (doesn't exist, should return 10)
1634
- const node = bst.lowerBound(9);
1635
- expect(node).toBeDefined();
1636
- expect(node?.key).toBe(10);
1633
+ const key = bst.ceiling(9);
1634
+ expect(key).toBeDefined();
1635
+ expect(key).toBe(10);
1637
1636
  });
1638
1637
 
1639
- it('should return node with key >= search key from multiple candidates', () => {
1638
+ it('should return key with key >= search key from multiple candidates', () => {
1640
1639
  // Test for key 6 (exists, should return 6)
1641
- const node = bst.lowerBound(6);
1642
- expect(node).toBeDefined();
1643
- expect(node?.key).toBe(6);
1640
+ const key = bst.ceiling(6);
1641
+ expect(key).toBeDefined();
1642
+ expect(key).toBe(6);
1644
1643
 
1645
1644
  // Test for key 7 (exists, should return 7)
1646
- const node2 = bst.lowerBound(7);
1647
- expect(node2).toBeDefined();
1648
- expect(node2?.key).toBe(7);
1645
+ const key2 = bst.ceiling(7);
1646
+ expect(key2).toBeDefined();
1647
+ expect(key2).toBe(7);
1649
1648
  });
1650
1649
 
1651
1650
  it('should return smallest key >= search key for non-existent key in middle range', () => {
1652
1651
  // Test for key 14 (doesn't exist, should return 15)
1653
- const node = bst.lowerBound(14);
1654
- expect(node).toBeDefined();
1655
- expect(node?.key).toBe(15);
1652
+ const key = bst.ceiling(14);
1653
+ expect(key).toBeDefined();
1654
+ expect(key).toBe(15);
1656
1655
 
1657
1656
  // Test for key 11.5 (doesn't exist, should return 12)
1658
- const node2 = bst.lowerBound(11.5);
1659
- expect(node2).toBeDefined();
1660
- expect(node2?.key).toBe(12);
1657
+ const key2 = bst.ceiling(11.5);
1658
+ expect(key2).toBeDefined();
1659
+ expect(key2).toBe(12);
1661
1660
  });
1662
1661
 
1663
1662
  it('should return smallest key when search key is less than minimum', () => {
1664
1663
  // Test for key 0 (should return 1, the minimum)
1665
- const node = bst.lowerBound(0);
1666
- expect(node).toBeDefined();
1667
- expect(node?.key).toBe(1);
1664
+ const key = bst.ceiling(0);
1665
+ expect(key).toBeDefined();
1666
+ expect(key).toBe(1);
1668
1667
 
1669
1668
  // Test for key -100 (should return 1, the minimum)
1670
- const node2 = bst.lowerBound(-100);
1671
- expect(node2).toBeDefined();
1672
- expect(node2?.key).toBe(1);
1669
+ const key2 = bst.ceiling(-100);
1670
+ expect(key2).toBeDefined();
1671
+ expect(key2).toBe(1);
1673
1672
  });
1674
1673
 
1675
1674
  it('should return undefined when search key is greater than maximum', () => {
1676
1675
  // Test for key 100 (no key >= 100, should return undefined)
1677
- const node = bst.lowerBound(100);
1678
- expect(node).toBeUndefined();
1676
+ const key = bst.ceiling(100);
1677
+ expect(key).toBeUndefined();
1679
1678
 
1680
1679
  // Test for key 26 (no key >= 26, should return undefined)
1681
- const node2 = bst.lowerBound(26);
1682
- expect(node2).toBeUndefined();
1680
+ const key2 = bst.ceiling(26);
1681
+ expect(key2).toBeUndefined();
1683
1682
  });
1684
1683
 
1685
- it('should return correct node for edge cases', () => {
1684
+ it('should return correct key for edge cases', () => {
1686
1685
  // Test for key 25 (maximum, should return 25)
1687
- const node = bst.lowerBound(25);
1688
- expect(node).toBeDefined();
1689
- expect(node?.key).toBe(25);
1686
+ const key = bst.ceiling(25);
1687
+ expect(key).toBeDefined();
1688
+ expect(key).toBe(25);
1690
1689
 
1691
1690
  // Test for key 1 (minimum, should return 1)
1692
- const node2 = bst.lowerBound(1);
1693
- expect(node2).toBeDefined();
1694
- expect(node2?.key).toBe(1);
1691
+ const key2 = bst.ceiling(1);
1692
+ expect(key2).toBeDefined();
1693
+ expect(key2).toBe(1);
1695
1694
  });
1696
1695
  });
1697
1696
 
1698
- describe('upperBound', () => {
1697
+ describe('higher', () => {
1699
1698
  it('should return the smallest key > search key', () => {
1700
1699
  // Test for key 10 (exists, should return next key > 10, which is 11)
1701
- const node = bst.upperBound(10);
1702
- expect(node).toBeDefined();
1703
- expect(node?.key).toBe(11);
1700
+ const key = bst.higher(10);
1701
+ expect(key).toBeDefined();
1702
+ expect(key).toBe(11);
1704
1703
  });
1705
1704
 
1706
1705
  it('should return the smallest key > search key for non-existent key', () => {
1707
1706
  // Test for key 9 (doesn't exist, should return 10, the smallest key > 9)
1708
- const node = bst.upperBound(9);
1709
- expect(node).toBeDefined();
1710
- expect(node?.key).toBe(10);
1707
+ const key = bst.higher(9);
1708
+ expect(key).toBeDefined();
1709
+ expect(key).toBe(10);
1711
1710
 
1712
1711
  // Test for key 14 (doesn't exist, should return 15)
1713
- const node2 = bst.upperBound(14);
1714
- expect(node2).toBeDefined();
1715
- expect(node2?.key).toBe(15);
1712
+ const key2 = bst.higher(14);
1713
+ expect(key2).toBeDefined();
1714
+ expect(key2).toBe(15);
1716
1715
  });
1717
1716
 
1718
1717
  it('should skip equal keys and return the next larger key', () => {
1719
1718
  // Test for key 5 (exists, should return 6, the smallest key > 5)
1720
- const node = bst.upperBound(5);
1721
- expect(node).toBeDefined();
1722
- expect(node?.key).toBe(6);
1719
+ const key = bst.higher(5);
1720
+ expect(key).toBeDefined();
1721
+ expect(key).toBe(6);
1723
1722
 
1724
1723
  // Test for key 20 (exists, should return 25)
1725
- const node2 = bst.upperBound(20);
1726
- expect(node2).toBeDefined();
1727
- expect(node2?.key).toBe(25);
1724
+ const key2 = bst.higher(20);
1725
+ expect(key2).toBeDefined();
1726
+ expect(key2).toBe(25);
1728
1727
  });
1729
1728
 
1730
1729
  it('should return smallest key > search key for non-existent key in middle range', () => {
1731
1730
  // Test for key 11.5 (doesn't exist, should return 12)
1732
- const node = bst.upperBound(11.5);
1733
- expect(node).toBeDefined();
1734
- expect(node?.key).toBe(12);
1731
+ const key = bst.higher(11.5);
1732
+ expect(key).toBeDefined();
1733
+ expect(key).toBe(12);
1735
1734
 
1736
1735
  // Test for key 19 (doesn't exist, should return 20)
1737
- const node2 = bst.upperBound(19);
1738
- expect(node2).toBeDefined();
1739
- expect(node2?.key).toBe(20);
1736
+ const key2 = bst.higher(19);
1737
+ expect(key2).toBeDefined();
1738
+ expect(key2).toBe(20);
1740
1739
  });
1741
1740
 
1742
1741
  it('should return smallest key when search key is less than minimum', () => {
1743
1742
  // Test for key 0 (should return 1, the minimum)
1744
- const node = bst.upperBound(0);
1745
- expect(node).toBeDefined();
1746
- expect(node?.key).toBe(1);
1743
+ const key = bst.higher(0);
1744
+ expect(key).toBeDefined();
1745
+ expect(key).toBe(1);
1747
1746
 
1748
1747
  // Test for key -100 (should return 1, the minimum)
1749
- const node2 = bst.upperBound(-100);
1750
- expect(node2).toBeDefined();
1751
- expect(node2?.key).toBe(1);
1748
+ const key2 = bst.higher(-100);
1749
+ expect(key2).toBeDefined();
1750
+ expect(key2).toBe(1);
1752
1751
  });
1753
1752
 
1754
1753
  it('should return undefined when search key is >= maximum', () => {
1755
1754
  // Test for key 100 (no key > 100, should return undefined)
1756
- const node = bst.upperBound(100);
1757
- expect(node).toBeUndefined();
1755
+ const key = bst.higher(100);
1756
+ expect(key).toBeUndefined();
1758
1757
 
1759
1758
  // Test for key 25 (maximum, no key > 25, should return undefined)
1760
- const node2 = bst.upperBound(25);
1761
- expect(node2).toBeUndefined();
1759
+ const key2 = bst.higher(25);
1760
+ expect(key2).toBeUndefined();
1762
1761
 
1763
1762
  // Test for key 26 (no key > 26, should return undefined)
1764
- const node3 = bst.upperBound(26);
1765
- expect(node3).toBeUndefined();
1763
+ const key3 = bst.higher(26);
1764
+ expect(key3).toBeUndefined();
1766
1765
  });
1767
1766
 
1768
- it('should return correct node for edge cases', () => {
1767
+ it('should return correct key for edge cases', () => {
1769
1768
  // Test for key 1 (minimum, should return next key 3)
1770
- const node = bst.upperBound(1);
1771
- expect(node).toBeDefined();
1772
- expect(node?.key).toBe(3);
1769
+ const key = bst.higher(1);
1770
+ expect(key).toBeDefined();
1771
+ expect(key).toBe(3);
1773
1772
 
1774
1773
  // Test for key 24 (doesn't exist, should return 25)
1775
- const node2 = bst.upperBound(24);
1776
- expect(node2).toBeDefined();
1777
- expect(node2?.key).toBe(25);
1774
+ const key2 = bst.higher(24);
1775
+ expect(key2).toBeDefined();
1776
+ expect(key2).toBe(25);
1778
1777
  });
1779
1778
  });
1780
1779
 
1781
- describe('lowerBound vs upperBound comparison', () => {
1782
- it('should demonstrate difference between lowerBound and upperBound', () => {
1780
+ describe('ceiling vs higher comparison', () => {
1781
+ it('should demonstrate difference between ceiling and higher', () => {
1783
1782
  const searchKey = 12;
1784
1783
 
1785
- // lowerBound(12) should return 12 (key >= 12)
1786
- const lower = bst.lowerBound(searchKey);
1787
- expect(lower?.key).toBe(12);
1784
+ // ceiling(12) should return 12 (key >= 12)
1785
+ const lower = bst.ceiling(searchKey);
1786
+ expect(lower).toBe(12);
1788
1787
 
1789
- // upperBound(12) should return 13 (key > 12)
1790
- const upper = bst.upperBound(searchKey);
1791
- expect(upper?.key).toBe(13);
1788
+ // higher(12) should return 13 (key > 12)
1789
+ const upper = bst.higher(searchKey);
1790
+ expect(upper).toBe(13);
1792
1791
  });
1793
1792
 
1794
1793
  it('should return same result for non-existent key in both methods', () => {
1795
1794
  const searchKey = 11.5;
1796
1795
 
1797
- // Both should return 12 (smallest key >= 11.5 for lowerBound, smallest key > 11.5 for upperBound)
1798
- const lower = bst.lowerBound(searchKey);
1799
- const upper = bst.upperBound(searchKey);
1800
- expect(lower?.key).toBe(12);
1801
- expect(upper?.key).toBe(12);
1796
+ // Both should return 12 (smallest key >= 11.5 for ceiling, smallest key > 11.5 for higher)
1797
+ const lower = bst.ceiling(searchKey);
1798
+ const upper = bst.higher(searchKey);
1799
+ expect(lower).toBe(12);
1800
+ expect(upper).toBe(12);
1802
1801
  });
1803
1802
  });
1804
1803
 
@@ -1825,90 +1824,90 @@ describe('BST lowerBound and upperBound', () => {
1825
1824
  keys.forEach(k => bst.add(k, `val-${k}`));
1826
1825
  });
1827
1826
 
1828
- describe('lowerBound (First node >= key)', () => {
1827
+ describe('ceiling (First key >= key)', () => {
1829
1828
  test('should return strict match if key exists', () => {
1830
1829
  // Case: Key exists
1831
- expect(bst.lowerBound(10)?.key).toBe(10);
1832
- expect(bst.lowerBound(6)?.key).toBe(6);
1833
- expect(bst.lowerBound(20)?.key).toBe(20);
1830
+ expect(bst.ceiling(10)).toBe(10);
1831
+ expect(bst.ceiling(6)).toBe(6);
1832
+ expect(bst.ceiling(20)).toBe(20);
1834
1833
  });
1835
1834
 
1836
- test('should return next larger node if key does not exist', () => {
1835
+ test('should return next larger key if key does not exist', () => {
1837
1836
  // Case: Key doesn't exist, but falls within range
1838
- expect(bst.lowerBound(7)?.key).toBe(8); // 7 -> 8
1839
- expect(bst.lowerBound(11)?.key).toBe(12); // 11 -> 12
1840
- expect(bst.lowerBound(3)?.key).toBe(5); // 3 -> 5
1837
+ expect(bst.ceiling(7)).toBe(8); // 7 -> 8
1838
+ expect(bst.ceiling(11)).toBe(12); // 11 -> 12
1839
+ expect(bst.ceiling(3)).toBe(5); // 3 -> 5
1841
1840
  });
1842
1841
 
1843
- test('should return smallest node if key is smaller than min', () => {
1842
+ test('should return smallest key if key is smaller than min', () => {
1844
1843
  // Case: Key is smaller than the minimum value in the tree
1845
- expect(bst.lowerBound(0)?.key).toBe(2);
1846
- expect(bst.lowerBound(-100)?.key).toBe(2);
1844
+ expect(bst.ceiling(0)).toBe(2);
1845
+ expect(bst.ceiling(-100)).toBe(2);
1847
1846
  });
1848
1847
 
1849
1848
  test('should return undefined if key is larger than max', () => {
1850
1849
  // Case: Key is larger than the maximum value in the tree
1851
- expect(bst.lowerBound(21)).toBeUndefined();
1852
- expect(bst.lowerBound(100)).toBeUndefined();
1850
+ expect(bst.ceiling(21)).toBeUndefined();
1851
+ expect(bst.ceiling(100)).toBeUndefined();
1853
1852
  });
1854
1853
 
1855
1854
  test('should work with IterationType.RECURSIVE explicitly', () => {
1856
- expect(bst.lowerBound(7, 'RECURSIVE')?.key).toBe(8);
1857
- expect(bst.lowerBound(10, 'RECURSIVE')?.key).toBe(10);
1858
- expect(bst.lowerBound(21, 'RECURSIVE')).toBeUndefined();
1855
+ expect(bst.ceiling(7)).toBe(8);
1856
+ expect(bst.ceiling(10)).toBe(10);
1857
+ expect(bst.ceiling(21)).toBeUndefined();
1859
1858
  });
1860
1859
 
1861
1860
  test('should work with IterationType.ITERATIVE explicitly', () => {
1862
- expect(bst.lowerBound(7, 'ITERATIVE')?.key).toBe(8);
1863
- expect(bst.lowerBound(10, 'ITERATIVE')?.key).toBe(10);
1864
- expect(bst.lowerBound(21, 'ITERATIVE')).toBeUndefined();
1861
+ expect(bst.ceiling(7)).toBe(8);
1862
+ expect(bst.ceiling(10)).toBe(10);
1863
+ expect(bst.ceiling(21)).toBeUndefined();
1865
1864
  });
1866
1865
  });
1867
1866
 
1868
- describe('upperBound (First node > key)', () => {
1869
- test('should return next larger node even if key exists', () => {
1867
+ describe('higher (First key > key)', () => {
1868
+ test('should return next larger key even if key exists', () => {
1870
1869
  // Case: Key exists, but we want strictly greater
1871
- expect(bst.upperBound(10)?.key).toBe(12); // > 10 is 12
1872
- expect(bst.upperBound(6)?.key).toBe(8); // > 6 is 8
1873
- expect(bst.upperBound(20)).toBeUndefined(); // > 20 is undefined
1870
+ expect(bst.higher(10)).toBe(12); // > 10 is 12
1871
+ expect(bst.higher(6)).toBe(8); // > 6 is 8
1872
+ expect(bst.higher(20)).toBeUndefined(); // > 20 is undefined
1874
1873
  });
1875
1874
 
1876
- test('should return next larger node if key does not exist', () => {
1875
+ test('should return next larger key if key does not exist', () => {
1877
1876
  // Case: Key doesn't exist
1878
- expect(bst.upperBound(7)?.key).toBe(8);
1879
- expect(bst.upperBound(11)?.key).toBe(12);
1877
+ expect(bst.higher(7)).toBe(8);
1878
+ expect(bst.higher(11)).toBe(12);
1880
1879
  });
1881
1880
 
1882
1881
  test('should return undefined if key is larger than or equal to max', () => {
1883
- expect(bst.upperBound(20)).toBeUndefined();
1884
- expect(bst.upperBound(21)).toBeUndefined();
1882
+ expect(bst.higher(20)).toBeUndefined();
1883
+ expect(bst.higher(21)).toBeUndefined();
1885
1884
  });
1886
1885
 
1887
1886
  test('should work with IterationType.RECURSIVE explicitly', () => {
1888
- expect(bst.upperBound(10, 'RECURSIVE')?.key).toBe(12);
1889
- expect(bst.upperBound(20, 'RECURSIVE')).toBeUndefined();
1887
+ expect(bst.higher(10)).toBe(12);
1888
+ expect(bst.higher(20)).toBeUndefined();
1890
1889
  });
1891
1890
  });
1892
1891
 
1893
1892
  describe('Edge Cases', () => {
1894
1893
  test('should handle empty tree', () => {
1895
1894
  const emptyTree = new BST<number, string>();
1896
- expect(emptyTree.lowerBound(10)).toBeUndefined();
1897
- expect(emptyTree.upperBound(10)).toBeUndefined();
1895
+ expect(emptyTree.ceiling(10)).toBeUndefined();
1896
+ expect(emptyTree.higher(10)).toBeUndefined();
1898
1897
  });
1899
1898
 
1900
1899
  test('should handle single node tree', () => {
1901
1900
  const singleNodeTree = new BST<number, string>();
1902
1901
  singleNodeTree.add(10);
1903
1902
 
1904
- // lowerBound
1905
- expect(singleNodeTree.lowerBound(5)?.key).toBe(10);
1906
- expect(singleNodeTree.lowerBound(10)?.key).toBe(10);
1907
- expect(singleNodeTree.lowerBound(15)).toBeUndefined();
1903
+ // ceiling
1904
+ expect(singleNodeTree.ceiling(5)).toBe(10);
1905
+ expect(singleNodeTree.ceiling(10)).toBe(10);
1906
+ expect(singleNodeTree.ceiling(15)).toBeUndefined();
1908
1907
 
1909
- // upperBound
1910
- expect(singleNodeTree.upperBound(5)?.key).toBe(10);
1911
- expect(singleNodeTree.upperBound(10)).toBeUndefined();
1908
+ // higher
1909
+ expect(singleNodeTree.higher(5)).toBe(10);
1910
+ expect(singleNodeTree.higher(10)).toBeUndefined();
1912
1911
  });
1913
1912
  });
1914
1913
 
@@ -1928,9 +1927,9 @@ describe('BST lowerBound and upperBound', () => {
1928
1927
  const testQueries = generateRandomArray(200, 0, 1000);
1929
1928
 
1930
1929
  testQueries.forEach(queryKey => {
1931
- // 1. Verify lowerBound
1932
- const recLower = fuzzTree.lowerBound(queryKey, 'RECURSIVE')?.key;
1933
- const iterLower = fuzzTree.lowerBound(queryKey, 'ITERATIVE')?.key;
1930
+ // 1. Verify ceiling
1931
+ const recLower = fuzzTree.ceiling(queryKey);
1932
+ const iterLower = fuzzTree.ceiling(queryKey);
1934
1933
 
1935
1934
  // Verify consistency between recursive and iterative
1936
1935
  expect(recLower).toBe(iterLower);
@@ -1939,9 +1938,9 @@ describe('BST lowerBound and upperBound', () => {
1939
1938
  const expectedLower = sortedKeys.find(k => k >= queryKey);
1940
1939
  expect(recLower).toBe(expectedLower);
1941
1940
 
1942
- // 2. Verify upperBound
1943
- const recUpper = fuzzTree.upperBound(queryKey, 'RECURSIVE')?.key;
1944
- const iterUpper = fuzzTree.upperBound(queryKey, 'ITERATIVE')?.key;
1941
+ // 2. Verify higher
1942
+ const recUpper = fuzzTree.higher(queryKey);
1943
+ const iterUpper = fuzzTree.higher(queryKey);
1945
1944
 
1946
1945
  // Verify consistency
1947
1946
  expect(recUpper).toBe(iterUpper);
@@ -1959,14 +1958,14 @@ describe('BST lowerBound and upperBound', () => {
1959
1958
  bst = new BST<number, string>();
1960
1959
  });
1961
1960
 
1962
- it('lowerBound should return undefined on empty tree', () => {
1963
- const node = bst.lowerBound(10);
1964
- expect(node).toBeUndefined();
1961
+ it('ceiling should return undefined on empty tree', () => {
1962
+ const key = bst.ceiling(10);
1963
+ expect(key).toBeUndefined();
1965
1964
  });
1966
1965
 
1967
- it('upperBound should return undefined on empty tree', () => {
1968
- const node = bst.upperBound(10);
1969
- expect(node).toBeUndefined();
1966
+ it('higher should return undefined on empty tree', () => {
1967
+ const key = bst.higher(10);
1968
+ expect(key).toBeUndefined();
1970
1969
  });
1971
1970
  });
1972
1971
 
@@ -1976,259 +1975,1327 @@ describe('BST lowerBound and upperBound', () => {
1976
1975
  bst.add(10, 'ten');
1977
1976
  });
1978
1977
 
1979
- it('lowerBound should return the node if key matches', () => {
1980
- const node = bst.lowerBound(10);
1981
- expect(node).toBeDefined();
1982
- expect(node?.key).toBe(10);
1978
+ it('ceiling should return the key if key matches', () => {
1979
+ const key = bst.ceiling(10);
1980
+ expect(key).toBeDefined();
1981
+ expect(key).toBe(10);
1983
1982
  });
1984
1983
 
1985
- it('lowerBound should return the node if search key is less', () => {
1986
- const node = bst.lowerBound(5);
1987
- expect(node).toBeDefined();
1988
- expect(node?.key).toBe(10);
1984
+ it('ceiling should return the key if search key is less', () => {
1985
+ const key = bst.ceiling(5);
1986
+ expect(key).toBeDefined();
1987
+ expect(key).toBe(10);
1989
1988
  });
1990
1989
 
1991
- it('lowerBound should return undefined if search key is greater', () => {
1992
- const node = bst.lowerBound(15);
1993
- expect(node).toBeUndefined();
1990
+ it('ceiling should return undefined if search key is greater', () => {
1991
+ const key = bst.ceiling(15);
1992
+ expect(key).toBeUndefined();
1994
1993
  });
1995
1994
 
1996
- it('upperBound should return undefined if key matches', () => {
1997
- const node = bst.upperBound(10);
1998
- expect(node).toBeUndefined();
1995
+ it('higher should return undefined if key matches', () => {
1996
+ const key = bst.higher(10);
1997
+ expect(key).toBeUndefined();
1999
1998
  });
2000
1999
 
2001
- it('upperBound should return the node if search key is less', () => {
2002
- const node = bst.upperBound(5);
2003
- expect(node).toBeDefined();
2004
- expect(node?.key).toBe(10);
2000
+ it('higher should return the key if search key is less', () => {
2001
+ const key = bst.higher(5);
2002
+ expect(key).toBeDefined();
2003
+ expect(key).toBe(10);
2005
2004
  });
2006
2005
 
2007
- it('upperBound should return undefined if search key is greater', () => {
2008
- const node = bst.upperBound(15);
2009
- expect(node).toBeUndefined();
2006
+ it('higher should return undefined if search key is greater', () => {
2007
+ const key = bst.higher(15);
2008
+ expect(key).toBeUndefined();
2010
2009
  });
2011
2010
  });
2012
2011
 
2013
2012
  describe('practical LeetCode use case: Range search', () => {
2014
2013
  it('should find all keys in range [12, 20]', () => {
2015
- // Use lowerBound to find start of range and upperBound to find end
2016
- const start = bst.lowerBound(12);
2014
+ // Use ceiling to find start of range and higher to find end
2015
+ const start = bst.ceiling(12);
2017
2016
 
2018
- // Simple traversal simulation (for test only, real iteration is more complex)
2019
- // We manually add keys we know are in range to verify logic works
2020
- // The lowerBound(12) returns 12. upperBound(20) returns 25 (or null if max).
2021
- // Logic: we want >= 12 and <= 20.
2022
-
2023
- expect(start?.key).toBe(12);
2017
+ expect(start).toBe(12);
2024
2018
 
2025
2019
  // Let's verify we found the correct bounds
2026
- expect(start?.key).toBeGreaterThanOrEqual(12);
2020
+ expect(start).toBeGreaterThanOrEqual(12);
2027
2021
 
2028
2022
  // Check overlap
2029
- const rangeStart = bst.lowerBound(12);
2030
- const rangeEndInclusive = bst.lowerBound(20); // Should be 20
2023
+ const rangeStart = bst.ceiling(12);
2024
+ const rangeEndInclusive = bst.ceiling(20); // Should be 20
2031
2025
 
2032
- expect(rangeStart?.key).toBe(12);
2033
- expect(rangeEndInclusive?.key).toBe(20);
2026
+ expect(rangeStart).toBe(12);
2027
+ expect(rangeEndInclusive).toBe(20);
2034
2028
  });
2035
2029
 
2036
2030
  it('should help identify interval overlaps', () => {
2037
2031
  // Check if interval [14, 19] overlaps with any existing interval
2038
- // Use upperBound to find the first key > 19
2039
- const nextKey = bst.upperBound(19);
2040
- // Use lowerBound to find the first key >= 14
2041
- const firstInRange = bst.lowerBound(14);
2032
+ // Use higher to find the first key > 19
2033
+ const nextKey = bst.higher(19);
2034
+ // Use ceiling to find the first key >= 14
2035
+ const firstInRange = bst.ceiling(14);
2042
2036
 
2043
- expect(firstInRange?.key).toBe(15);
2044
- expect(nextKey?.key).toBe(20);
2037
+ expect(firstInRange).toBe(15);
2038
+ expect(nextKey).toBe(20);
2045
2039
  });
2046
2040
  });
2047
2041
  });
2048
2042
 
2049
- describe('BST Advanced Bound Methods Tests', () => {
2043
+ describe('BST Range Query Methods', () => {
2050
2044
  let bst: BST<number, string>;
2051
2045
 
2052
- // Helper: Generate random number array
2053
- const generateRandomArray = (size: number, min: number, max: number) => {
2054
- return Array.from({ length: size }, () => Math.floor(Math.random() * (max - min + 1)) + min);
2055
- };
2056
-
2057
2046
  beforeEach(() => {
2058
- // Construct a standard test tree
2059
- // Structure:
2060
- // 10
2061
- // / \
2062
- // 5 15
2063
- // / \ / \
2064
- // 2 8 12 20
2065
- // / \
2066
- // 6 9
2067
- bst = new BST<number, string>();
2068
- const keys = [10, 5, 15, 2, 8, 12, 20, 6, 9];
2069
- keys.forEach(k => bst.add(k, `val-${k}`));
2047
+ // Create a balanced BST: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20]
2048
+ bst = new BST([10, 5, 15, 3, 7, 13, 17, 1, 4, 6, 9, 11, 14, 16, 19, 20]);
2070
2049
  });
2071
2050
 
2072
- describe('Polymorphic Input Support', () => {
2073
- test('should accept raw Key', () => {
2074
- expect(bst.lowerBound(7)?.key).toBe(8);
2075
- expect(bst.upperBound(7)?.key).toBe(8);
2051
+ describe('ceiling - finds >= key (minimum value >= target)', () => {
2052
+ test('should find ceiling when key exists', () => {
2053
+ const result = bst.ceiling(10);
2054
+ expect(result).toBeDefined();
2055
+ expect(result).toBe(10);
2056
+ });
2057
+
2058
+ test('should find ceiling when key does not exist but higher value exists', () => {
2059
+ const result = bst.ceiling(8);
2060
+ expect(result).toBeDefined();
2061
+ expect(result).toBe(9);
2062
+ });
2063
+
2064
+ test('should return undefined when no ceiling exists (key greater than all)', () => {
2065
+ const result = bst.ceiling(100);
2066
+ expect(result).toBeUndefined();
2067
+ });
2068
+
2069
+ test('should find minimum element as ceiling for key smaller than all', () => {
2070
+ const result = bst.ceiling(-10);
2071
+ expect(result).toBeDefined();
2072
+ expect(result).toBe(1);
2073
+ });
2074
+
2075
+ test('should handle ceiling with node input', () => {
2076
+ const targetNode = bst.getNode(7);
2077
+ expect(targetNode).toBeDefined();
2078
+ const result = bst.ceiling(targetNode!);
2079
+ expect(result).toBe(7);
2080
+ });
2081
+
2082
+ test('should handle ceiling with entry input', () => {
2083
+ const result = bst.ceiling([11, 'test']);
2084
+ expect(result).toBeDefined();
2085
+ expect(result).toBe(11);
2086
+ });
2087
+
2088
+ test('should handle null/undefined inputs', () => {
2089
+ expect(bst.ceiling(null)).toBeUndefined();
2090
+ expect(bst.ceiling(undefined)).toBeUndefined();
2076
2091
  });
2077
2092
 
2078
- test('should accept BSTNode object', () => {
2079
- // Find a node first
2080
- const node5 = bst.getNode(5);
2081
- expect(node5).toBeDefined();
2093
+ test('should work with ITERATIVE mode', () => {
2094
+ const result = bst.ceiling(12);
2095
+ expect(result).toBeDefined();
2096
+ expect(result).toBe(13);
2097
+ });
2098
+
2099
+ test('should work with RECURSIVE mode', () => {
2100
+ const result = bst.ceiling(12);
2101
+ expect(result).toBeDefined();
2102
+ expect(result).toBe(13);
2103
+ });
2104
+
2105
+ test('should find exact match as ceiling', () => {
2106
+ const result = bst.ceiling(15);
2107
+ expect(result).toBeDefined();
2108
+ expect(result).toBe(15);
2109
+ });
2110
+ });
2111
+
2112
+ describe('higher - finds > key (minimum value > target)', () => {
2113
+ test('should find higher when key exists (exclude exact match)', () => {
2114
+ const result = bst.higher(10);
2115
+ expect(result).toBeDefined();
2116
+ expect(result).toBe(11);
2117
+ expect(result).not.toBe(10);
2118
+ });
2119
+
2120
+ test('should find higher when key does not exist', () => {
2121
+ const result = bst.higher(8);
2122
+ expect(result).toBeDefined();
2123
+ expect(result).toBe(9);
2124
+ });
2125
+
2126
+ test('should return undefined when no higher exists (key >= all)', () => {
2127
+ const result = bst.higher(20);
2128
+ expect(result).toBeUndefined();
2129
+ });
2130
+
2131
+ test('should find minimum element as higher for key < all', () => {
2132
+ const result = bst.higher(-10);
2133
+ expect(result).toBeDefined();
2134
+ expect(result).toBe(1);
2135
+ });
2136
+
2137
+ test('should not return the key itself', () => {
2138
+ const result = bst.higher(7);
2139
+ expect(result).not.toBe(7);
2140
+ expect(result).toBe(9);
2141
+ });
2142
+
2143
+ test('should handle higher with node input', () => {
2144
+ const targetNode = bst.getNode(5);
2145
+ expect(targetNode).toBeDefined();
2146
+ const result = bst.higher(targetNode!);
2147
+ expect(result).toBeGreaterThan(5);
2148
+ expect(result).toBe(6);
2149
+ });
2150
+
2151
+ test('should work with ITERATIVE mode', () => {
2152
+ const result = bst.higher(13);
2153
+ expect(result).toBeDefined();
2154
+ expect(result).toBe(14);
2155
+ });
2156
+
2157
+ test('should work with RECURSIVE mode', () => {
2158
+ const result = bst.higher(13);
2159
+ expect(result).toBeDefined();
2160
+ expect(result).toBe(14);
2161
+ });
2162
+ });
2163
+
2164
+ describe('floor - finds <= key (maximum value <= target)', () => {
2165
+ test('should find floor when key exists', () => {
2166
+ const result = bst.floor(10);
2167
+ expect(result).toBeDefined();
2168
+ expect(result).toBe(10);
2169
+ });
2170
+
2171
+ test('should find floor when key does not exist but lower value exists', () => {
2172
+ const result = bst.floor(12);
2173
+ expect(result).toBeDefined();
2174
+ expect(result).toBe(11);
2175
+ });
2176
+
2177
+ test('should return undefined when no floor exists (key less than all)', () => {
2178
+ const result = bst.floor(-10);
2179
+ expect(result).toBeUndefined();
2180
+ });
2082
2181
 
2083
- // Use the node as input for lowerBound (should return itself)
2084
- expect(bst.lowerBound(node5)?.key).toBe(5);
2182
+ test('should find maximum element as floor for key greater than all', () => {
2183
+ const result = bst.floor(100);
2184
+ expect(result).toBeDefined();
2185
+ expect(result).toBe(20);
2186
+ });
2085
2187
 
2086
- // Use the node as input for upperBound (should return next larger)
2087
- expect(bst.upperBound(node5)?.key).toBe(6);
2188
+ test('should handle floor with node input', () => {
2189
+ const targetNode = bst.getNode(13);
2190
+ expect(targetNode).toBeDefined();
2191
+ const result = bst.floor(targetNode!);
2192
+ expect(result).toBe(13);
2088
2193
  });
2089
2194
 
2090
- test('should accept Entry tuple [key, value]', () => {
2091
- // Input as [key, value] tuple
2092
- const entry: [number, string] = [7, 'val-7'];
2195
+ test('should handle floor with entry input', () => {
2196
+ const result = bst.floor([16, 'test']);
2197
+ expect(result).toBeDefined();
2198
+ expect(result).toBe(16);
2199
+ });
2093
2200
 
2094
- expect(bst.lowerBound(entry)?.key).toBe(8);
2095
- expect(bst.upperBound(entry)?.key).toBe(8);
2201
+ test('should handle null/undefined inputs', () => {
2202
+ expect(bst.floor(null)).toBeUndefined();
2203
+ expect(bst.floor(undefined)).toBeUndefined();
2204
+ });
2096
2205
 
2097
- const existingEntry: [number, string] = [10, 'val-10'];
2098
- expect(bst.lowerBound(existingEntry)?.key).toBe(10);
2099
- expect(bst.upperBound(existingEntry)?.key).toBe(12);
2206
+ test('should work with ITERATIVE mode', () => {
2207
+ const result = bst.floor(12);
2208
+ expect(result).toBeDefined();
2209
+ expect(result).toBe(11);
2100
2210
  });
2101
2211
 
2102
- test('should accept Predicate function (Linear Search Fallback)', () => {
2103
- // Predicate: Find first node with key > 11 (Expect 12)
2104
- // Note: Predicate search uses in-order traversal
2105
- const predicate = (node: any) => node.key > 11;
2212
+ test('should work with RECURSIVE mode', () => {
2213
+ const result = bst.floor(12);
2214
+ expect(result).toBeDefined();
2215
+ expect(result).toBe(11);
2216
+ });
2106
2217
 
2107
- // For predicate, lowerBound and upperBound behave identically
2108
- // (they just return the first match of the predicate)
2109
- expect(bst.lowerBound(predicate)?.key).toBe(12);
2110
- expect(bst.upperBound(predicate)?.key).toBe(12);
2218
+ test('should find exact match as floor', () => {
2219
+ const result = bst.floor(15);
2220
+ expect(result).toBeDefined();
2221
+ expect(result).toBe(15);
2222
+ });
2111
2223
 
2112
- // Predicate: Find specific value
2113
- expect(bst.lowerBound(n => n.key === 6)?.key).toBe(6);
2224
+ test('should correctly find floor between two keys', () => {
2225
+ const result = bst.floor(8);
2226
+ expect(result).toBeDefined();
2227
+ expect(result).toBe(7);
2228
+ expect(result).toBeLessThan(8);
2114
2229
  });
2115
2230
  });
2116
2231
 
2117
- describe('Iteration Types with Polymorphic Inputs', () => {
2118
- test('should work recursively with all types', () => {
2119
- const type = 'RECURSIVE';
2232
+ describe('lower - finds < key (maximum value < target)', () => {
2233
+ test('should find lower when key exists (exclude exact match)', () => {
2234
+ const result = bst.lower(10);
2235
+ expect(result).toBeDefined();
2236
+ expect(result).toBe(9);
2237
+ expect(result).not.toBe(10);
2238
+ });
2239
+
2240
+ test('should find lower when key does not exist', () => {
2241
+ const result = bst.lower(12);
2242
+ expect(result).toBeDefined();
2243
+ expect(result).toBe(11);
2244
+ });
2245
+
2246
+ test('should return undefined when no lower exists (key <= all)', () => {
2247
+ const result = bst.lower(1);
2248
+ expect(result).toBeUndefined();
2249
+ });
2250
+
2251
+ test('should find maximum element as lower for key > all', () => {
2252
+ const result = bst.lower(100);
2253
+ expect(result).toBeDefined();
2254
+ expect(result).toBe(20);
2255
+ });
2256
+
2257
+ test('should not return the key itself', () => {
2258
+ const result = bst.lower(15);
2259
+ expect(result).not.toBe(15);
2260
+ expect(result).toBe(14);
2261
+ });
2120
2262
 
2121
- expect(bst.lowerBound(7, type)?.key).toBe(8);
2122
- expect(bst.lowerBound(bst.getNode(5), type)?.key).toBe(5);
2123
- expect(bst.lowerBound([7, 'val'], type)?.key).toBe(8);
2124
- expect(bst.lowerBound(n => n.key > 11, type)?.key).toBe(12);
2263
+ test('should handle lower with node input', () => {
2264
+ const targetNode = bst.getNode(13);
2265
+ expect(targetNode).toBeDefined();
2266
+ const result = bst.lower(targetNode!);
2267
+ expect(result).toBeLessThan(13);
2268
+ expect(result).toBe(11);
2125
2269
  });
2126
2270
 
2127
- test('should work iteratively with all types', () => {
2128
- const type = 'ITERATIVE';
2271
+ test('should handle lower with entry input', () => {
2272
+ const result = bst.lower([17, 'test']);
2273
+ expect(result).toBeDefined();
2274
+ expect(result).toBe(16);
2275
+ });
2276
+
2277
+ test('should work with ITERATIVE mode', () => {
2278
+ const result = bst.lower(14);
2279
+ expect(result).toBeDefined();
2280
+ expect(result).toBe(13);
2281
+ });
2129
2282
 
2130
- expect(bst.lowerBound(7, type)?.key).toBe(8);
2131
- expect(bst.lowerBound(bst.getNode(5), type)?.key).toBe(5);
2132
- expect(bst.lowerBound([7, 'val'], type)?.key).toBe(8);
2133
- expect(bst.lowerBound(n => n.key > 11, type)?.key).toBe(12);
2283
+ test('should work with RECURSIVE mode', () => {
2284
+ const result = bst.lower(14);
2285
+ expect(result).toBeDefined();
2286
+ expect(result).toBe(13);
2134
2287
  });
2135
2288
  });
2136
2289
 
2137
- describe('Complex Edge Cases', () => {
2138
- test('should return undefined for null/undefined input', () => {
2139
- expect(bst.lowerBound(null)).toBeUndefined();
2140
- expect(bst.lowerBound(undefined)).toBeUndefined();
2290
+ describe('Edge cases and special scenarios', () => {
2291
+ test('single element tree - ceiling', () => {
2292
+ const singleBst = new BST([5]);
2293
+ expect(singleBst.ceiling(5)).toBe(5);
2294
+ expect(singleBst.ceiling(3)).toBe(5);
2295
+ expect(singleBst.ceiling(7)).toBeUndefined();
2141
2296
  });
2142
2297
 
2143
- test('should handle predicate returning false for all nodes', () => {
2144
- expect(bst.lowerBound(n => n.key > 100)).toBeUndefined();
2298
+ test('single element tree - higher', () => {
2299
+ const singleBst = new BST([5]);
2300
+ expect(singleBst.higher(5)).toBeUndefined();
2301
+ expect(singleBst.higher(3)).toBe(5);
2302
+ });
2303
+
2304
+ test('single element tree - floor', () => {
2305
+ const singleBst = new BST([5]);
2306
+ expect(singleBst.floor(5)).toBe(5);
2307
+ expect(singleBst.floor(7)).toBe(5);
2308
+ expect(singleBst.floor(3)).toBeUndefined();
2309
+ });
2310
+
2311
+ test('single element tree - lower', () => {
2312
+ const singleBst = new BST([5]);
2313
+ expect(singleBst.lower(5)).toBeUndefined();
2314
+ expect(singleBst.lower(7)).toBe(5);
2315
+ });
2316
+
2317
+ test('empty tree handling', () => {
2318
+ const emptyBst = new BST<number, string>();
2319
+ expect(emptyBst.ceiling(5)).toBeUndefined();
2320
+ expect(emptyBst.higher(5)).toBeUndefined();
2321
+ expect(emptyBst.floor(5)).toBeUndefined();
2322
+ expect(emptyBst.lower(5)).toBeUndefined();
2323
+ });
2324
+
2325
+ test('ceiling and floor of adjacent keys', () => {
2326
+ const ceiling = bst.ceiling(5);
2327
+ const floor = bst.floor(6);
2328
+ expect(ceiling).toBe(5);
2329
+ expect(floor).toBe(6);
2330
+ });
2331
+
2332
+ test('higher and lower of adjacent keys', () => {
2333
+ const higher = bst.higher(5);
2334
+ const lower = bst.lower(6);
2335
+ expect(higher).toBe(6);
2336
+ expect(lower).toBe(5);
2145
2337
  });
2146
2338
  });
2147
2339
 
2148
- describe('Consistency Check (Fuzz Testing)', () => {
2149
- test('All input methods should yield consistent results for Raw Key and Entry', () => {
2150
- const fuzzTree = new BST<number, number>();
2151
- const randomKeys = generateRandomArray(100, 0, 200);
2152
- [...new Set(randomKeys)].forEach(k => fuzzTree.add(k));
2340
+ describe('Predicate-based search', () => {
2341
+ test('ceiling with predicate function', () => {
2342
+ const result = bst.ceiling((node: BSTNode<number, string>) => node.key >= 10);
2343
+ expect(result).toBeDefined();
2344
+ expect(result).toBeGreaterThanOrEqual(10);
2345
+ });
2153
2346
 
2154
- const queries = generateRandomArray(50, 0, 200);
2347
+ test('floor with predicate function', () => {
2348
+ const result = bst.floor((node: BSTNode<number, string>) => node.key <= 15);
2349
+ expect(result).toBeDefined();
2350
+ expect(result).toBeLessThanOrEqual(15);
2351
+ });
2155
2352
 
2156
- queries.forEach(q => {
2157
- // 1. Raw Key
2158
- const resKey = fuzzTree.lowerBound(q);
2353
+ test('higher with predicate function', () => {
2354
+ const result = bst.higher((node: BSTNode<number, string>) => node.key > 10);
2355
+ expect(result).toBeDefined();
2356
+ expect(result).toBeGreaterThan(10);
2357
+ });
2159
2358
 
2160
- // 2. Entry
2161
- const resEntry = fuzzTree.lowerBound([q, undefined]);
2359
+ test('lower with predicate function', () => {
2360
+ const result = bst.lower((node: BSTNode<number, string>) => node.key < 15);
2361
+ expect(result).toBeDefined();
2362
+ expect(result).toBeLessThan(15);
2363
+ });
2364
+ });
2162
2365
 
2163
- // Verify consistency between Key and Entry
2164
- expect(resKey?.key).toBe(resEntry?.key);
2366
+ describe('Custom comparator', () => {
2367
+ test('should work with reverse order comparator', () => {
2368
+ const reverseBst = new BST([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20], {
2369
+ comparator: (a: number, b: number) => b - a // reverse order
2165
2370
  });
2371
+
2372
+ // In reverse order tree: keys are stored in descending order
2373
+ // ceiling (>=) should still work correctly
2374
+ const ceiling = reverseBst.ceiling(10);
2375
+ expect(ceiling).toBeDefined();
2376
+ expect(ceiling).toBeLessThanOrEqual(10);
2166
2377
  });
2167
2378
 
2168
- test('BSTNode input should work correctly', () => {
2169
- const fuzzTree = new BST<number, number>();
2170
- const randomKeys = generateRandomArray(50, 0, 100);
2171
- const uniqueKeys = [...new Set(randomKeys)];
2172
- uniqueKeys.forEach(k => fuzzTree.add(k));
2173
-
2174
- // Test with actual nodes from the tree
2175
- for (const key of uniqueKeys) {
2176
- const node = fuzzTree.getNode(key);
2177
- if (node) {
2178
- // lowerBound with node should return itself (since node.key >= key)
2179
- expect(fuzzTree.lowerBound(node)?.key).toBe(key);
2180
-
2181
- // upperBound with node should return next larger
2182
- const upperRes = fuzzTree.upperBound(node);
2183
- if (upperRes) {
2184
- expect(upperRes.key).toBeGreaterThan(key);
2185
- }
2379
+ test('should work with string comparator', () => {
2380
+ const stringBst = new BST(
2381
+ [
2382
+ { name: 'Alice', id: 1 },
2383
+ { name: 'Bob', id: 2 },
2384
+ { name: 'Charlie', id: 3 },
2385
+ { name: 'David', id: 4 },
2386
+ { name: 'Eve', id: 5 }
2387
+ ],
2388
+ {
2389
+ comparator: (a, b) => a.name.localeCompare(b.name)
2186
2390
  }
2391
+ );
2392
+
2393
+ const ceiling = stringBst.ceiling({ name: 'Bob', id: 0 });
2394
+ expect(ceiling).toBeDefined();
2395
+ expect(ceiling?.name).toBe('Bob');
2396
+ });
2397
+ });
2398
+
2399
+ describe('Performance and correctness validation', () => {
2400
+ test('all range methods return keys in order', () => {
2401
+ const ceiling = bst.ceiling(10);
2402
+ const higher = bst.higher(10);
2403
+ const floor = bst.floor(10);
2404
+ const lower = bst.lower(10);
2405
+
2406
+ expect(floor).toBeLessThanOrEqual(10);
2407
+ expect(ceiling).toBeGreaterThanOrEqual(10);
2408
+ expect(higher).toBeGreaterThan(10);
2409
+ expect(lower).toBeLessThan(10);
2410
+ });
2411
+
2412
+ test('range query iteration with ceiling/higher', () => {
2413
+ const results: number[] = [];
2414
+ let key = bst.ceiling(5);
2415
+ let count = 0;
2416
+ while (key && key <= 15 && count < 20) {
2417
+ results.push(key);
2418
+ key = bst.higher(key);
2419
+ count++;
2420
+ }
2421
+
2422
+ // Should iterate through keys 5, 6, 7, ..., 15
2423
+ expect(results.length).toBeGreaterThan(0);
2424
+ expect(results[0]).toBeGreaterThanOrEqual(5);
2425
+ expect(results[results.length - 1]).toBeLessThanOrEqual(15);
2426
+ // Verify ascending order
2427
+ for (let i = 1; i < results.length; i++) {
2428
+ expect(results[i]).toBeGreaterThan(results[i - 1]);
2429
+ }
2430
+ });
2431
+
2432
+ test('range query iteration with floor/lower', () => {
2433
+ const results: number[] = [];
2434
+ let key = bst.floor(15);
2435
+ let count = 0;
2436
+ while (key && key >= 5 && count < 20) {
2437
+ results.push(key);
2438
+ key = bst.lower(key);
2439
+ count++;
2440
+ }
2441
+
2442
+ // Should iterate through keys 15, 14, 13, ..., 5
2443
+ expect(results.length).toBeGreaterThan(0);
2444
+ expect(results[0]).toBeLessThanOrEqual(15);
2445
+ expect(results[results.length - 1]).toBeGreaterThanOrEqual(5);
2446
+ // Verify descending order
2447
+ for (let i = 1; i < results.length; i++) {
2448
+ expect(results[i]).toBeLessThan(results[i - 1]);
2449
+ }
2450
+ });
2451
+ });
2452
+
2453
+ describe('Boundary value testing', () => {
2454
+ test('boundary: ceiling at min value', () => {
2455
+ const result = bst.ceiling(1);
2456
+ expect(result).toBe(1);
2457
+ });
2458
+
2459
+ test('boundary: floor at max value', () => {
2460
+ const result = bst.floor(20);
2461
+ expect(result).toBe(20);
2462
+ });
2463
+
2464
+ test('boundary: higher at second-last value', () => {
2465
+ const result = bst.higher(19);
2466
+ expect(result).toBe(20);
2467
+ });
2468
+
2469
+ test('boundary: lower at second value', () => {
2470
+ const result = bst.lower(3);
2471
+ expect(result).toBe(1);
2472
+ });
2473
+
2474
+ test('boundary: ceiling slightly below min', () => {
2475
+ const result = bst.ceiling(0);
2476
+ expect(result).toBe(1);
2477
+ });
2478
+
2479
+ test('boundary: floor slightly above max', () => {
2480
+ const result = bst.floor(21);
2481
+ expect(result).toBe(20);
2482
+ });
2483
+
2484
+ test('boundary: higher at max (should be undefined)', () => {
2485
+ const result = bst.higher(20);
2486
+ expect(result).toBeUndefined();
2487
+ });
2488
+
2489
+ test('boundary: lower at min (should be undefined)', () => {
2490
+ const result = bst.lower(1);
2491
+ expect(result).toBeUndefined();
2492
+ });
2493
+ });
2494
+ });
2495
+
2496
+ describe('BST Comparator Tests', () => {
2497
+ describe('Default Comparator with Primitive Types', () => {
2498
+ let bst: BST<number>;
2499
+
2500
+ beforeEach(() => {
2501
+ bst = new BST<number>();
2502
+ });
2503
+
2504
+ it('should compare two numbers correctly - a > b', () => {
2505
+ const result = bst['_compare'](5, 3);
2506
+ expect(result).toBe(1);
2507
+ });
2508
+
2509
+ it('should compare two numbers correctly - a < b', () => {
2510
+ const result = bst['_compare'](2, 8);
2511
+ expect(result).toBe(-1);
2512
+ });
2513
+
2514
+ it('should compare two numbers correctly - a === b', () => {
2515
+ const result = bst['_compare'](5, 5);
2516
+ expect(result).toBe(0);
2517
+ });
2518
+
2519
+ it('should compare negative numbers correctly', () => {
2520
+ const result1 = bst['_compare'](-5, -3);
2521
+ const result2 = bst['_compare'](-10, 0);
2522
+ expect(result1).toBe(-1);
2523
+ expect(result2).toBe(-1);
2524
+ });
2525
+
2526
+ it('should compare zero correctly', () => {
2527
+ const result1 = bst['_compare'](0, 5);
2528
+ const result2 = bst['_compare'](0, 0);
2529
+ const result3 = bst['_compare'](-5, 0);
2530
+ expect(result1).toBe(-1);
2531
+ expect(result2).toBe(0);
2532
+ expect(result3).toBe(-1);
2533
+ });
2534
+
2535
+ it('should compare decimal numbers correctly', () => {
2536
+ const result1 = bst['_compare'](3.14, 3.15);
2537
+ const result2 = bst['_compare'](2.5, 2.5);
2538
+ expect(result1).toBe(-1);
2539
+ expect(result2).toBe(0);
2540
+ });
2541
+
2542
+ it('should compare very large numbers correctly', () => {
2543
+ const result1 = bst['_compare'](Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER - 1);
2544
+ const result2 = bst['_compare'](1e10, 1e9);
2545
+ expect(result1).toBe(1);
2546
+ expect(result2).toBe(1);
2547
+ });
2548
+ });
2549
+
2550
+ describe('Default Comparator with String Types', () => {
2551
+ let bst: BST<string>;
2552
+
2553
+ beforeEach(() => {
2554
+ bst = new BST<string>();
2555
+ });
2556
+
2557
+ it('should compare strings alphabetically - a > b', () => {
2558
+ const result = bst['_compare']('zebra', 'apple');
2559
+ expect(result).toBe(1);
2560
+ });
2561
+
2562
+ it('should compare strings alphabetically - a < b', () => {
2563
+ const result = bst['_compare']('apple', 'zebra');
2564
+ expect(result).toBe(-1);
2565
+ });
2566
+
2567
+ it('should compare identical strings', () => {
2568
+ const result = bst['_compare']('hello', 'hello');
2569
+ expect(result).toBe(0);
2570
+ });
2571
+
2572
+ it('should compare string prefixes correctly', () => {
2573
+ const result1 = bst['_compare']('app', 'apple');
2574
+ const result2 = bst['_compare']('apple', 'app');
2575
+ expect(result1).toBe(-1);
2576
+ expect(result2).toBe(1);
2577
+ });
2578
+
2579
+ it('should compare case-sensitive strings', () => {
2580
+ const result1 = bst['_compare']('Apple', 'apple');
2581
+ const result2 = bst['_compare']('apple', 'Apple');
2582
+ expect(result1).toBe(-1);
2583
+ expect(result2).toBe(1);
2584
+ });
2585
+
2586
+ it('should compare empty strings', () => {
2587
+ const result1 = bst['_compare']('', '');
2588
+ const result2 = bst['_compare']('', 'a');
2589
+ const result3 = bst['_compare']('a', '');
2590
+ expect(result1).toBe(0);
2591
+ expect(result2).toBe(-1);
2592
+ expect(result3).toBe(1);
2593
+ });
2594
+
2595
+ it('should compare special characters in strings', () => {
2596
+ const result1 = bst['_compare']('!', 'a');
2597
+ const result2 = bst['_compare']('a', '1');
2598
+ expect(result1).toBe(-1);
2599
+ expect(result2).toBe(1);
2600
+ });
2601
+ });
2602
+
2603
+ describe('Custom Comparator', () => {
2604
+ interface Person {
2605
+ name: string;
2606
+ age: number;
2607
+ }
2608
+
2609
+ it('should accept custom comparator for objects - compare by age', () => {
2610
+ const comparator = (a: Person, b: Person): number => {
2611
+ if (a.age > b.age) return 1;
2612
+ if (a.age < b.age) return -1;
2613
+ return 0;
2614
+ };
2615
+
2616
+ const bst = new BST<Person>([], { comparator });
2617
+
2618
+ const p1 = { name: 'Alice', age: 30 };
2619
+ const p2 = { name: 'Bob', age: 25 };
2620
+ const p3 = { name: 'Charlie', age: 30 };
2621
+
2622
+ expect(bst['_compare'](p1, p2)).toBe(1);
2623
+ expect(bst['_compare'](p2, p1)).toBe(-1);
2624
+ expect(bst['_compare'](p1, p3)).toBe(0);
2625
+ });
2626
+
2627
+ it('should accept custom comparator - compare by name length', () => {
2628
+ const comparator = (a: string, b: string): number => {
2629
+ if (a.length > b.length) return 1;
2630
+ if (a.length < b.length) return -1;
2631
+ return 0;
2632
+ };
2633
+
2634
+ const bst = new BST<string>([], { comparator });
2635
+
2636
+ expect(bst['_compare']('hello', 'hi')).toBe(1);
2637
+ expect(bst['_compare']('hi', 'hello')).toBe(-1);
2638
+ expect(bst['_compare']('abc', 'def')).toBe(0);
2639
+ });
2640
+
2641
+ it('should accept custom comparator - reverse order', () => {
2642
+ const comparator = (a: number, b: number): number => {
2643
+ if (a < b) return 1;
2644
+ if (a > b) return -1;
2645
+ return 0;
2646
+ };
2647
+
2648
+ const bst = new BST<number>([], { comparator });
2649
+
2650
+ expect(bst['_compare'](5, 3)).toBe(-1);
2651
+ expect(bst['_compare'](2, 8)).toBe(1);
2652
+ expect(bst['_compare'](5, 5)).toBe(0);
2653
+ });
2654
+
2655
+ it('should throw error when comparing object types without custom comparator', () => {
2656
+ const bst = new BST<{ value: number }>();
2657
+
2658
+ expect(() => {
2659
+ bst['_compare']({ value: 1 }, { value: 2 });
2660
+ }).toThrow("When comparing object type keys, a custom comparator must be provided in the constructor's options!");
2661
+ });
2662
+ });
2663
+
2664
+ describe('Comparator Usage in BST Operations', () => {
2665
+ let bst: BST<number>;
2666
+
2667
+ beforeEach(() => {
2668
+ bst = new BST<number>();
2669
+ });
2670
+
2671
+ it('should correctly insert elements based on comparator', () => {
2672
+ bst.add(10);
2673
+ bst.add(5);
2674
+ bst.add(15);
2675
+ bst.add(3);
2676
+ bst.add(7);
2677
+
2678
+ expect(bst.has(10)).toBe(true);
2679
+ expect(bst.has(5)).toBe(true);
2680
+ expect(bst.has(15)).toBe(true);
2681
+ expect(bst.has(3)).toBe(true);
2682
+ expect(bst.has(7)).toBe(true);
2683
+ expect(bst.size).toBe(5);
2684
+ });
2685
+
2686
+ it('should maintain BST property with in-order traversal', () => {
2687
+ bst.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
2688
+
2689
+ const inOrder: (number | undefined)[] = bst.dfs(node => node?.key);
2690
+
2691
+ for (let i = 1; i < inOrder.length; i++) {
2692
+ expect(inOrder[i]! >= inOrder[i - 1]!).toBe(true);
2693
+ }
2694
+ });
2695
+
2696
+ it('should correctly find min and max using comparator', () => {
2697
+ bst.addMany([15, 10, 20, 8, 12, 18, 25]);
2698
+
2699
+ const arr = Array.from(bst.keys());
2700
+ const min = Math.min(...arr);
2701
+ const max = Math.max(...arr);
2702
+
2703
+ expect(bst.has(min)).toBe(true);
2704
+ expect(bst.has(max)).toBe(true);
2705
+ });
2706
+
2707
+ it('should correctly delete elements maintaining BST property', () => {
2708
+ bst.addMany([10, 5, 15, 3, 7, 12, 18]);
2709
+ bst.delete(10);
2710
+
2711
+ expect(bst.has(10)).toBe(false);
2712
+ const inOrder: (number | undefined)[] = bst.dfs(node => node?.key);
2713
+
2714
+ for (let i = 1; i < inOrder.length; i++) {
2715
+ expect(inOrder[i]! >= inOrder[i - 1]!).toBe(true);
2187
2716
  }
2188
2717
  });
2189
2718
 
2190
- test('Predicate input should find matching nodes', () => {
2191
- const fuzzTree = new BST<number, number>();
2192
- const randomKeys = generateRandomArray(50, 0, 100);
2193
- const uniqueKeys = [...new Set(randomKeys)];
2194
- uniqueKeys.forEach(k => fuzzTree.add(k));
2719
+ it('should correctly search using comparator', () => {
2720
+ bst.addMany([20, 10, 30, 5, 15, 25, 35]);
2195
2721
 
2196
- // Test various predicates
2197
- const predicates = [(n: any) => n.key > 50, (n: any) => n.key < 30, (n: any) => n.key >= 25 && n.key <= 75];
2722
+ const result = bst.search(15);
2723
+ expect(result.length).toBeGreaterThan(0);
2724
+ expect(result[0]).toBe(15);
2725
+ });
2726
+ });
2727
+
2728
+ describe('Comparator Edge Cases', () => {
2729
+ describe('with numbers', () => {
2730
+ let bst: BST<number>;
2198
2731
 
2199
- predicates.forEach(predicate => {
2200
- const result = fuzzTree.lowerBound(predicate);
2201
- if (result) {
2202
- // Verify the result actually satisfies the predicate
2203
- expect(predicate(result)).toBe(true);
2732
+ beforeEach(() => {
2733
+ bst = new BST<number>();
2734
+ });
2735
+
2736
+ it('should handle Infinity correctly', () => {
2737
+ const result1 = bst['_compare'](Infinity, 100);
2738
+ const result2 = bst['_compare'](100, Infinity);
2739
+ expect(result1).toBe(1);
2740
+ expect(result2).toBe(-1);
2741
+ });
2742
+
2743
+ it('should handle -Infinity correctly', () => {
2744
+ const result1 = bst['_compare'](-Infinity, -100);
2745
+ const result2 = bst['_compare'](-100, -Infinity);
2746
+ expect(result1).toBe(-1);
2747
+ expect(result2).toBe(1);
2748
+ });
2749
+
2750
+ it('should handle NaN correctly', () => {
2751
+ const result1 = bst['_compare'](NaN, 5);
2752
+ const result2 = bst['_compare'](5, NaN);
2753
+ expect(result1).toBe(0);
2754
+ expect(result2).toBe(0);
2755
+ });
2756
+ });
2757
+
2758
+ describe('with strings', () => {
2759
+ let bst: BST<string>;
2760
+
2761
+ beforeEach(() => {
2762
+ bst = new BST<string>();
2763
+ });
2764
+
2765
+ it('should handle Unicode characters', () => {
2766
+ const result1 = bst['_compare']('😀', 'abc');
2767
+ const result2 = bst['_compare']('中文', 'English');
2768
+ expect(typeof result1).toBe('number');
2769
+ expect(typeof result2).toBe('number');
2770
+ });
2771
+
2772
+ it('should handle whitespace strings', () => {
2773
+ const result1 = bst['_compare'](' ', ' ');
2774
+ const result2 = bst['_compare']('\n', '\t');
2775
+ expect(typeof result1).toBe('number');
2776
+ expect(typeof result2).toBe('number');
2777
+ });
2778
+
2779
+ it('should maintain consistency with multiple comparisons', () => {
2780
+ const a = 'apple';
2781
+ const b = 'banana';
2782
+ const c = 'cherry';
2783
+
2784
+ const ab = bst['_compare'](a, b);
2785
+ const bc = bst['_compare'](b, c);
2786
+ const ac = bst['_compare'](a, c);
2787
+
2788
+ if (ab < 0 && bc < 0) {
2789
+ expect(ac).toBeLessThan(0);
2204
2790
  }
2205
2791
  });
2206
2792
  });
2793
+ });
2794
+
2795
+ describe('Comparator with BST Bound Operations', () => {
2796
+ let bst: BST<number>;
2797
+
2798
+ beforeEach(() => {
2799
+ bst = new BST<number>();
2800
+ bst.addMany([10, 5, 15, 3, 7, 12, 18, 1, 4, 6, 8, 11, 13, 16, 20]);
2801
+ });
2802
+
2803
+ it('should find ceiling using comparator', () => {
2804
+ const result = bst.ceiling(7);
2805
+ expect(result).toBe(7);
2806
+
2807
+ const result2 = bst.ceiling(7.5);
2808
+ expect(result2).toBeGreaterThanOrEqual(7.5);
2809
+ });
2810
+
2811
+ it('should find floor using comparator', () => {
2812
+ const result = bst.floor(7);
2813
+ expect(result).toBe(7);
2814
+
2815
+ const result2 = bst.floor(7.5);
2816
+ expect(result2!).toBeLessThanOrEqual(7.5);
2817
+ });
2818
+
2819
+ it('should find higher using comparator', () => {
2820
+ const result = bst.higher(7);
2821
+ expect(result).toBeGreaterThan(7);
2822
+ });
2823
+
2824
+ it('should find lower using comparator', () => {
2825
+ const result = bst.lower(7);
2826
+ expect(result).toBeLessThan(7);
2827
+ });
2828
+ });
2829
+
2830
+ describe('Comparator Consistency', () => {
2831
+ let bst: BST<number>;
2832
+
2833
+ beforeEach(() => {
2834
+ bst = new BST<number>();
2835
+ });
2207
2836
 
2208
- test('Consistency between RECURSIVE and ITERATIVE modes', () => {
2209
- const fuzzTree = new BST<number, number>();
2210
- const randomKeys = generateRandomArray(50, 0, 100);
2211
- [...new Set(randomKeys)].forEach(k => fuzzTree.add(k));
2837
+ it('should satisfy reflexivity: a compared with itself returns 0', () => {
2838
+ const values = [0, 1, -1, 100, -100, 0.5];
2212
2839
 
2213
- const queries = generateRandomArray(30, 0, 100);
2840
+ for (const val of values) {
2841
+ expect(bst['_compare'](val, val)).toBe(0);
2842
+ }
2843
+ });
2214
2844
 
2215
- queries.forEach(q => {
2216
- // Test with Key
2217
- const recKey = fuzzTree.lowerBound(q, 'RECURSIVE');
2218
- const iterKey = fuzzTree.lowerBound(q, 'ITERATIVE');
2219
- expect(recKey?.key).toBe(iterKey?.key);
2845
+ it('should satisfy antisymmetry: if a > b then b < a', () => {
2846
+ const pairs = [
2847
+ [5, 3],
2848
+ [10, 2],
2849
+ [100, 1]
2850
+ ];
2851
+
2852
+ for (const [a, b] of pairs) {
2853
+ const cmp1 = bst['_compare'](a, b);
2854
+ const cmp2 = bst['_compare'](b, a);
2855
+ expect(Math.sign(cmp1)).toBe(-Math.sign(cmp2));
2856
+ }
2857
+ });
2858
+
2859
+ it('should satisfy transitivity: if a < b and b < c then a < c', () => {
2860
+ const a = 1;
2861
+ const b = 5;
2862
+ const c = 10;
2220
2863
 
2221
- // Test with Entry
2222
- const recEntry = fuzzTree.lowerBound([q, undefined], 'RECURSIVE');
2223
- const iterEntry = fuzzTree.lowerBound([q, undefined], 'ITERATIVE');
2224
- expect(recEntry?.key).toBe(iterEntry?.key);
2864
+ const cmpAB = bst['_compare'](a, b);
2865
+ const cmpBC = bst['_compare'](b, c);
2866
+ const cmpAC = bst['_compare'](a, c);
2225
2867
 
2226
- // Test upperBound too
2227
- const recUpper = fuzzTree.upperBound(q, 'RECURSIVE');
2228
- const iterUpper = fuzzTree.upperBound(q, 'ITERATIVE');
2229
- expect(recUpper?.key).toBe(iterUpper?.key);
2868
+ if (cmpAB < 0 && cmpBC < 0) {
2869
+ expect(cmpAC).toBeLessThan(0);
2870
+ }
2871
+ });
2872
+ });
2873
+
2874
+ describe('Custom Comparator with BST Operations', () => {
2875
+ interface Student {
2876
+ name: string;
2877
+ grade: number;
2878
+ }
2879
+
2880
+ it('should work with custom comparator for complex operations', () => {
2881
+ const comparator = (a: Student, b: Student): number => {
2882
+ if (a.grade !== b.grade) {
2883
+ return a.grade > b.grade ? 1 : -1;
2884
+ }
2885
+ return a.name.localeCompare(b.name);
2886
+ };
2887
+
2888
+ const bst = new BST<Student>([], { comparator });
2889
+
2890
+ const students: Student[] = [
2891
+ { name: 'Alice', grade: 85 },
2892
+ { name: 'Bob', grade: 90 },
2893
+ { name: 'Charlie', grade: 85 },
2894
+ { name: 'David', grade: 95 }
2895
+ ];
2896
+
2897
+ students.forEach(s => bst.add(s));
2898
+
2899
+ expect(bst.size).toBe(4);
2900
+ expect(bst.has(students[0])).toBe(true);
2901
+ });
2902
+
2903
+ it('should handle custom comparator with add and search', () => {
2904
+ const comparator = (a: number[], b: number[]): number => {
2905
+ const sumA = a.reduce((acc, val) => acc + val, 0);
2906
+ const sumB = b.reduce((acc, val) => acc + val, 0);
2907
+ if (sumA > sumB) return 1;
2908
+ if (sumA < sumB) return -1;
2909
+ return 0;
2910
+ };
2911
+
2912
+ const bst = new BST<number[]>([], { comparator });
2913
+
2914
+ bst.add([1, 2, 3]);
2915
+ bst.add([2, 2, 2]);
2916
+ bst.add([1, 1, 1]);
2917
+
2918
+ expect(bst.size).toBeGreaterThan(0);
2919
+ });
2920
+ });
2921
+
2922
+ describe('Composite Key Comparator Tests', () => {
2923
+ interface CompositeKey {
2924
+ departmentId: number;
2925
+ employeeId: number;
2926
+ }
2927
+
2928
+ it('should compare composite keys by multiple fields - primary then secondary', () => {
2929
+ const comparator = (a: CompositeKey, b: CompositeKey): number => {
2930
+ if (a.departmentId !== b.departmentId) {
2931
+ return a.departmentId > b.departmentId ? 1 : -1;
2932
+ }
2933
+ if (a.employeeId !== b.employeeId) {
2934
+ return a.employeeId > b.employeeId ? 1 : -1;
2935
+ }
2936
+ return 0;
2937
+ };
2938
+
2939
+ const bst = new BST<CompositeKey>([], { comparator });
2940
+
2941
+ const key1 = { departmentId: 1, employeeId: 101 };
2942
+ const key2 = { departmentId: 1, employeeId: 102 };
2943
+ const key3 = { departmentId: 2, employeeId: 101 };
2944
+
2945
+ expect(bst['_compare'](key1, key2)).toBe(-1);
2946
+ expect(bst['_compare'](key2, key3)).toBe(-1);
2947
+ expect(bst['_compare'](key1, key1)).toBe(0);
2948
+ });
2949
+
2950
+ it('should maintain BST property with composite keys', () => {
2951
+ const comparator = (a: CompositeKey, b: CompositeKey): number => {
2952
+ if (a.departmentId !== b.departmentId) {
2953
+ return a.departmentId > b.departmentId ? 1 : -1;
2954
+ }
2955
+ return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
2956
+ };
2957
+
2958
+ const bst = new BST<CompositeKey>([], { comparator });
2959
+
2960
+ const keys = [
2961
+ { departmentId: 2, employeeId: 105 },
2962
+ { departmentId: 1, employeeId: 101 },
2963
+ { departmentId: 1, employeeId: 103 },
2964
+ { departmentId: 2, employeeId: 102 },
2965
+ { departmentId: 1, employeeId: 102 }
2966
+ ];
2967
+
2968
+ keys.forEach(k => bst.add(k));
2969
+
2970
+ expect(bst.size).toBe(5);
2971
+ const inOrder = bst.dfs(node => node?.key);
2972
+
2973
+ for (let i = 1; i < inOrder.length; i++) {
2974
+ const cmp = comparator(inOrder[i - 1]!, inOrder[i]!);
2975
+ expect(cmp).toBeLessThanOrEqual(0);
2976
+ }
2977
+ });
2978
+
2979
+ it('should search with composite keys', () => {
2980
+ const comparator = (a: CompositeKey, b: CompositeKey): number => {
2981
+ if (a.departmentId !== b.departmentId) {
2982
+ return a.departmentId > b.departmentId ? 1 : -1;
2983
+ }
2984
+ return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
2985
+ };
2986
+
2987
+ const bst = new BST<CompositeKey>([], { comparator });
2988
+
2989
+ const keys = [
2990
+ { departmentId: 1, employeeId: 101 },
2991
+ { departmentId: 1, employeeId: 103 },
2992
+ { departmentId: 2, employeeId: 102 }
2993
+ ];
2994
+
2995
+ keys.forEach(k => bst.add(k));
2996
+
2997
+ const searchKey = keys[1];
2998
+ const result = bst.search(searchKey);
2999
+
3000
+ expect(result.length).toBeGreaterThan(0);
3001
+ expect(result[0]).toEqual(searchKey);
3002
+ });
3003
+
3004
+ it('should handle deletion with composite keys', () => {
3005
+ const comparator = (a: CompositeKey, b: CompositeKey): number => {
3006
+ if (a.departmentId !== b.departmentId) {
3007
+ return a.departmentId > b.departmentId ? 1 : -1;
3008
+ }
3009
+ return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
3010
+ };
3011
+
3012
+ const bst = new BST<CompositeKey>([], { comparator });
3013
+
3014
+ const keys = [
3015
+ { departmentId: 1, employeeId: 101 },
3016
+ { departmentId: 1, employeeId: 103 },
3017
+ { departmentId: 2, employeeId: 102 }
3018
+ ];
3019
+
3020
+ keys.forEach(k => bst.add(k));
3021
+
3022
+ const keyToDelete = keys[1];
3023
+ const deleted = bst.delete(keyToDelete);
3024
+
3025
+ expect(deleted.length).toBeGreaterThan(0);
3026
+ expect(bst.has(keyToDelete)).toBe(false);
3027
+ expect(bst.size).toBe(2);
3028
+ });
3029
+
3030
+ it('should find ceiling/floor with composite keys', () => {
3031
+ const comparator = (a: CompositeKey, b: CompositeKey): number => {
3032
+ if (a.departmentId !== b.departmentId) {
3033
+ return a.departmentId > b.departmentId ? 1 : -1;
3034
+ }
3035
+ return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
3036
+ };
3037
+
3038
+ const bst = new BST<CompositeKey>([], { comparator });
3039
+
3040
+ const keys = [
3041
+ { departmentId: 1, employeeId: 101 },
3042
+ { departmentId: 1, employeeId: 105 },
3043
+ { departmentId: 2, employeeId: 102 }
3044
+ ];
3045
+
3046
+ keys.forEach(k => bst.add(k));
3047
+
3048
+ const searchKey = { departmentId: 1, employeeId: 103 };
3049
+ const ceiling = bst.ceiling(searchKey);
3050
+ const floor = bst.floor(searchKey);
3051
+
3052
+ expect(ceiling).toBeDefined();
3053
+ expect(floor).toBeDefined();
3054
+ });
3055
+ });
3056
+
3057
+ describe('Key-Value Storage with Comparator', () => {
3058
+ interface PersonKey {
3059
+ id: number;
3060
+ country: string;
3061
+ }
3062
+
3063
+ interface PersonValue {
3064
+ name: string;
3065
+ email: string;
3066
+ age: number;
3067
+ }
3068
+
3069
+ it('should store and retrieve key-value pairs with composite keys', () => {
3070
+ const comparator = (a: PersonKey, b: PersonKey): number => {
3071
+ const countryCompare = a.country.localeCompare(b.country);
3072
+ if (countryCompare !== 0) return countryCompare;
3073
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
3074
+ };
3075
+
3076
+ const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
3077
+
3078
+ const key1: PersonKey = { id: 1, country: 'USA' };
3079
+ const value1: PersonValue = { name: 'Alice', email: 'alice@example.com', age: 30 };
3080
+
3081
+ const key2: PersonKey = { id: 2, country: 'Canada' };
3082
+ const value2: PersonValue = { name: 'Bob', email: 'bob@example.com', age: 25 };
3083
+
3084
+ bst.add([key1, value1]);
3085
+ bst.add([key2, value2]);
3086
+
3087
+ expect(bst.size).toBe(2);
3088
+ expect(bst.get(key1)).toEqual(value1);
3089
+ expect(bst.get(key2)).toEqual(value2);
3090
+ });
3091
+
3092
+ it('should update values when adding duplicate keys', () => {
3093
+ const comparator = (a: PersonKey, b: PersonKey): number => {
3094
+ const countryCompare = a.country.localeCompare(b.country);
3095
+ if (countryCompare !== 0) return countryCompare;
3096
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
3097
+ };
3098
+
3099
+ const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
3100
+
3101
+ const key: PersonKey = { id: 1, country: 'USA' };
3102
+ const value1: PersonValue = { name: 'Alice', email: 'alice@example.com', age: 30 };
3103
+ const value2: PersonValue = { name: 'Alice Updated', email: 'alice.new@example.com', age: 31 };
3104
+
3105
+ bst.add([key, value1]);
3106
+ expect(bst.get(key)).toEqual(value1);
3107
+
3108
+ bst.add([key, value2]);
3109
+ expect(bst.size).toBe(1);
3110
+ expect(bst.get(key)).toEqual(value2);
3111
+ });
3112
+
3113
+ it('should retrieve all entries in sorted order by key', () => {
3114
+ const comparator = (a: PersonKey, b: PersonKey): number => {
3115
+ const countryCompare = a.country.localeCompare(b.country);
3116
+ if (countryCompare !== 0) return countryCompare;
3117
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
3118
+ };
3119
+
3120
+ const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
3121
+
3122
+ const entries: Array<[PersonKey, PersonValue]> = [
3123
+ [
3124
+ { id: 1, country: 'USA' },
3125
+ { name: 'Alice', email: 'alice@usa.com', age: 30 }
3126
+ ],
3127
+ [
3128
+ { id: 2, country: 'Canada' },
3129
+ { name: 'Bob', email: 'bob@ca.com', age: 25 }
3130
+ ],
3131
+ [
3132
+ { id: 1, country: 'Canada' },
3133
+ { name: 'Charlie', email: 'charlie@ca.com', age: 28 }
3134
+ ]
3135
+ ];
3136
+
3137
+ entries.forEach(([key, value]) => bst.add([key, value]));
3138
+
3139
+ expect(bst.size).toBe(3);
3140
+
3141
+ const values: (PersonValue | undefined)[] = [];
3142
+ for (const [key] of entries) {
3143
+ const value = bst.get(key);
3144
+ if (value) values.push(value);
3145
+ }
3146
+
3147
+ expect(values.length).toBeGreaterThan(0);
3148
+ });
3149
+
3150
+ it('should delete key-value pairs correctly', () => {
3151
+ const comparator = (a: PersonKey, b: PersonKey): number => {
3152
+ const countryCompare = a.country.localeCompare(b.country);
3153
+ if (countryCompare !== 0) return countryCompare;
3154
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
3155
+ };
3156
+
3157
+ const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
3158
+
3159
+ const key1: PersonKey = { id: 1, country: 'USA' };
3160
+ const value1: PersonValue = { name: 'Alice', email: 'alice@example.com', age: 30 };
3161
+
3162
+ const key2: PersonKey = { id: 2, country: 'Canada' };
3163
+ const value2: PersonValue = { name: 'Bob', email: 'bob@example.com', age: 25 };
3164
+
3165
+ bst.add([key1, value1]);
3166
+ bst.add([key2, value2]);
3167
+
3168
+ bst.delete(key1);
3169
+
3170
+ expect(bst.has(key1)).toBe(false);
3171
+ expect(bst.has(key2)).toBe(true);
3172
+ expect(bst.size).toBe(1);
3173
+ expect(bst.get(key1)).toBeUndefined();
3174
+ });
3175
+
3176
+ it('should search and retrieve values with composite keys', () => {
3177
+ const comparator = (a: PersonKey, b: PersonKey): number => {
3178
+ const countryCompare = a.country.localeCompare(b.country);
3179
+ if (countryCompare !== 0) return countryCompare;
3180
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
3181
+ };
3182
+
3183
+ const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
3184
+
3185
+ const entries: Array<[PersonKey, PersonValue]> = [
3186
+ [
3187
+ { id: 1, country: 'USA' },
3188
+ { name: 'Alice', email: 'alice@usa.com', age: 30 }
3189
+ ],
3190
+ [
3191
+ { id: 2, country: 'USA' },
3192
+ { name: 'Amy', email: 'amy@usa.com', age: 28 }
3193
+ ],
3194
+ [
3195
+ { id: 1, country: 'Canada' },
3196
+ { name: 'Bob', email: 'bob@ca.com', age: 25 }
3197
+ ]
3198
+ ];
3199
+
3200
+ entries.forEach(([key, value]) => bst.add([key, value]));
3201
+
3202
+ const searchKey: PersonKey = entries[1][0];
3203
+ const value = bst.get(searchKey);
3204
+
3205
+ expect(value).toEqual({ name: 'Amy', email: 'amy@usa.com', age: 28 });
3206
+ });
3207
+
3208
+ it('should map over key-value pairs maintaining comparator order', () => {
3209
+ const comparator = (a: PersonKey, b: PersonKey): number => {
3210
+ const countryCompare = a.country.localeCompare(b.country);
3211
+ if (countryCompare !== 0) return countryCompare;
3212
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
3213
+ };
3214
+
3215
+ const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
3216
+
3217
+ const entries: Array<[PersonKey, PersonValue]> = [
3218
+ [
3219
+ { id: 1, country: 'USA' },
3220
+ { name: 'Alice', email: 'alice@usa.com', age: 30 }
3221
+ ],
3222
+ [
3223
+ { id: 2, country: 'Canada' },
3224
+ { name: 'Bob', email: 'bob@ca.com', age: 25 }
3225
+ ]
3226
+ ];
3227
+
3228
+ entries.forEach(([key, value]) => bst.add([key, value]));
3229
+
3230
+ const mapped = bst.map((value, key) => [key, value?.name], { comparator, isMapMode: false });
3231
+
3232
+ expect(mapped.size).toBe(2);
3233
+ const names = Array.from(mapped.values());
3234
+ expect(names).toContain('Alice');
3235
+ expect(names).toContain('Bob');
3236
+ });
3237
+
3238
+ it('should perform range search on key-value pairs', () => {
3239
+ const comparator = (a: number, b: number): number => {
3240
+ return a > b ? 1 : a < b ? -1 : 0;
3241
+ };
3242
+
3243
+ const bst = new BST<number, string>([], { comparator, isMapMode: true });
3244
+
3245
+ for (let i = 1; i <= 10; i++) {
3246
+ bst.add([i, `value-${i}`]);
3247
+ }
3248
+
3249
+ const result = bst.rangeSearch([3, 7]);
3250
+
3251
+ expect(result.length).toBeGreaterThan(0);
3252
+ result.forEach(key => {
3253
+ expect(key).toBeGreaterThanOrEqual(3);
3254
+ expect(key).toBeLessThanOrEqual(7);
2230
3255
  });
2231
3256
  });
3257
+
3258
+ it('should maintain sorted iteration over key-value pairs', () => {
3259
+ const comparator = (a: { priority: number; id: number }, b: { priority: number; id: number }): number => {
3260
+ if (a.priority !== b.priority) {
3261
+ return a.priority > b.priority ? 1 : -1;
3262
+ }
3263
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
3264
+ };
3265
+
3266
+ type TaskKey = { priority: number; id: number };
3267
+ type TaskValue = { title: string; completed: boolean };
3268
+
3269
+ const bst = new BST<TaskKey, TaskValue>([], { comparator, isMapMode: true });
3270
+
3271
+ const tasks: Array<[TaskKey, TaskValue]> = [
3272
+ [
3273
+ { priority: 2, id: 1 },
3274
+ { title: 'Task 1', completed: false }
3275
+ ],
3276
+ [
3277
+ { priority: 1, id: 2 },
3278
+ { title: 'Task 2', completed: false }
3279
+ ],
3280
+ [
3281
+ { priority: 3, id: 3 },
3282
+ { title: 'Task 3', completed: true }
3283
+ ],
3284
+ [
3285
+ { priority: 1, id: 1 },
3286
+ { title: 'Task 4', completed: false }
3287
+ ]
3288
+ ];
3289
+
3290
+ tasks.forEach(([key, value]) => bst.add([key, value]));
3291
+
3292
+ expect(bst.size).toBe(4);
3293
+
3294
+ const inOrder = bst.dfs(node => node?.key.priority);
3295
+ for (let i = 1; i < inOrder.length; i++) {
3296
+ expect(inOrder[i]! >= inOrder[i - 1]!).toBe(true);
3297
+ }
3298
+ });
2232
3299
  });
2233
3300
  });
2234
3301
 
@@ -2369,7 +3436,7 @@ describe('classic use', () => {
2369
3436
  return findFirstCommon(path1, path2);
2370
3437
  };
2371
3438
 
2372
- function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
3439
+ function findFirstCommon(arr1: (number | undefined)[], arr2: (number | undefined)[]): number | undefined {
2373
3440
  for (const num of arr1) {
2374
3441
  if (arr2.indexOf(num) !== -1) {
2375
3442
  return num;