@zengrid/shared 1.0.2 → 1.0.4

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 (78) hide show
  1. package/dist/index.cjs.js +1157 -13
  2. package/dist/index.esm.js +1137 -1
  3. package/dist/package.json +1 -1
  4. package/dist/src/algorithms/filter/bloom-filter.d.ts.map +1 -1
  5. package/dist/src/algorithms/filter/index.d.ts.map +1 -1
  6. package/dist/src/algorithms/graph/bfs.d.ts.map +1 -1
  7. package/dist/src/algorithms/graph/dfs.d.ts.map +1 -1
  8. package/dist/src/algorithms/graph/graph.interface.d.ts.map +1 -1
  9. package/dist/src/algorithms/graph/index.d.ts.map +1 -1
  10. package/dist/src/algorithms/index.d.ts.map +1 -1
  11. package/dist/src/algorithms/pattern/index.d.ts.map +1 -1
  12. package/dist/src/algorithms/pattern/sequence-detector.d.ts.map +1 -1
  13. package/dist/src/algorithms/search/binary-search.d.ts.map +1 -1
  14. package/dist/src/algorithms/search/index.d.ts.map +1 -1
  15. package/dist/src/algorithms/sorting/comparators.d.ts.map +1 -1
  16. package/dist/src/algorithms/sorting/index.d.ts.map +1 -1
  17. package/dist/src/algorithms/sorting/sorting.interface.d.ts.map +1 -1
  18. package/dist/src/algorithms/sorting/timsort.d.ts.map +1 -1
  19. package/dist/src/data-structures/column-store/column-store.d.ts.map +1 -1
  20. package/dist/src/data-structures/column-store/column-store.interface.d.ts.map +1 -1
  21. package/dist/src/data-structures/column-store/index.d.ts.map +1 -1
  22. package/dist/src/data-structures/command-stack/command-stack.d.ts.map +1 -1
  23. package/dist/src/data-structures/command-stack/command-stack.interface.d.ts.map +1 -1
  24. package/dist/src/data-structures/command-stack/index.d.ts.map +1 -1
  25. package/dist/src/data-structures/dependency-graph/dependency-graph.d.ts.map +1 -1
  26. package/dist/src/data-structures/dependency-graph/dependency-graph.interface.d.ts.map +1 -1
  27. package/dist/src/data-structures/dependency-graph/index.d.ts.map +1 -1
  28. package/dist/src/data-structures/disjoint-set/disjoint-set.d.ts.map +1 -1
  29. package/dist/src/data-structures/disjoint-set/disjoint-set.interface.d.ts.map +1 -1
  30. package/dist/src/data-structures/disjoint-set/index.d.ts.map +1 -1
  31. package/dist/src/data-structures/index-map/index-map.d.ts.map +1 -1
  32. package/dist/src/data-structures/index-map/index-map.interface.d.ts.map +1 -1
  33. package/dist/src/data-structures/index-map/index.d.ts.map +1 -1
  34. package/dist/src/data-structures/index.d.ts.map +1 -1
  35. package/dist/src/data-structures/interval-tree/index.d.ts.map +1 -1
  36. package/dist/src/data-structures/interval-tree/interval-tree.d.ts.map +1 -1
  37. package/dist/src/data-structures/interval-tree/interval-tree.interface.d.ts.map +1 -1
  38. package/dist/src/data-structures/lru-cache/index.d.ts.map +1 -1
  39. package/dist/src/data-structures/lru-cache/lru-cache.d.ts.map +1 -1
  40. package/dist/src/data-structures/lru-cache/lru-cache.interface.d.ts.map +1 -1
  41. package/dist/src/data-structures/prefix-sum-array/index.d.ts.map +1 -1
  42. package/dist/src/data-structures/prefix-sum-array/prefix-sum-array.d.ts.map +1 -1
  43. package/dist/src/data-structures/prefix-sum-array/prefix-sum-array.interface.d.ts.map +1 -1
  44. package/dist/src/data-structures/rtree/index.d.ts.map +1 -1
  45. package/dist/src/data-structures/rtree/rtree.d.ts.map +1 -1
  46. package/dist/src/data-structures/rtree/rtree.interface.d.ts.map +1 -1
  47. package/dist/src/data-structures/segment-tree/index.d.ts.map +1 -1
  48. package/dist/src/data-structures/segment-tree/segment-tree.d.ts.map +1 -1
  49. package/dist/src/data-structures/segment-tree/segment-tree.interface.d.ts.map +1 -1
  50. package/dist/src/data-structures/skip-list/index.d.ts.map +1 -1
  51. package/dist/src/data-structures/skip-list/skip-list.d.ts.map +1 -1
  52. package/dist/src/data-structures/skip-list/skip-list.interface.d.ts.map +1 -1
  53. package/dist/src/data-structures/sparse-matrix/index.d.ts.map +1 -1
  54. package/dist/src/data-structures/sparse-matrix/sparse-matrix.d.ts.map +1 -1
  55. package/dist/src/data-structures/sparse-matrix/sparse-matrix.interface.d.ts.map +1 -1
  56. package/dist/src/data-structures/suffix-array/index.d.ts.map +1 -1
  57. package/dist/src/data-structures/suffix-array/suffix-array.d.ts.map +1 -1
  58. package/dist/src/data-structures/suffix-array/suffix-array.interface.d.ts.map +1 -1
  59. package/dist/src/data-structures/trie/index.d.ts.map +1 -1
  60. package/dist/src/data-structures/trie/trie.d.ts.map +1 -1
  61. package/dist/src/data-structures/trie/trie.interface.d.ts.map +1 -1
  62. package/dist/src/index.d.ts +7 -3
  63. package/dist/src/index.d.ts.map +1 -1
  64. package/dist/src/patterns/coordinator.d.ts.map +1 -1
  65. package/dist/src/patterns/event-emitter.d.ts.map +1 -1
  66. package/dist/src/patterns/factory.d.ts.map +1 -1
  67. package/dist/src/patterns/index.d.ts.map +1 -1
  68. package/dist/src/patterns/mediator.d.ts.map +1 -1
  69. package/dist/src/patterns/object-pool.d.ts.map +1 -1
  70. package/dist/src/patterns/observer.d.ts.map +1 -1
  71. package/dist/src/patterns/operation-mode.d.ts.map +1 -1
  72. package/dist/src/patterns/state-machine.d.ts.map +1 -1
  73. package/dist/src/types/common.d.ts.map +1 -1
  74. package/dist/src/types/index.d.ts.map +1 -1
  75. package/dist/src/utils/index.d.ts.map +1 -1
  76. package/dist/src/utils/timing.d.ts.map +1 -1
  77. package/dist/src/utils/timing.interface.d.ts.map +1 -1
  78. package/package.json +1 -1
package/dist/index.cjs.js CHANGED
@@ -1187,7 +1187,7 @@ class CommandStack {
1187
1187
  /**
1188
1188
  * Common aggregation operations
1189
1189
  */
1190
- var AggregationType;
1190
+ exports.AggregationType = void 0;
1191
1191
  (function (AggregationType) {
1192
1192
  AggregationType["SUM"] = "sum";
1193
1193
  AggregationType["MIN"] = "min";
@@ -1196,7 +1196,7 @@ var AggregationType;
1196
1196
  AggregationType["LCM"] = "lcm";
1197
1197
  AggregationType["PRODUCT"] = "product";
1198
1198
  AggregationType["CUSTOM"] = "custom";
1199
- })(AggregationType || (AggregationType = {}));
1199
+ })(exports.AggregationType || (exports.AggregationType = {}));
1200
1200
  /**
1201
1201
  * Predefined aggregation functions and identities
1202
1202
  */
@@ -1324,10 +1324,10 @@ class SegmentTree {
1324
1324
  constructor(options) {
1325
1325
  this.arr = options.values ? [...options.values] : [];
1326
1326
  this.n = this.arr.length;
1327
- this._type = options.type ?? AggregationType.CUSTOM;
1327
+ this._type = options.type ?? exports.AggregationType.CUSTOM;
1328
1328
  this.useLazy = options.lazy ?? true;
1329
1329
  // Determine aggregation function and identity
1330
- if (this._type === AggregationType.CUSTOM) {
1330
+ if (this._type === exports.AggregationType.CUSTOM) {
1331
1331
  if (!options.customAggregate || options.identity === undefined) {
1332
1332
  throw new Error('CUSTOM aggregation type requires customAggregate function and identity value');
1333
1333
  }
@@ -1353,17 +1353,17 @@ class SegmentTree {
1353
1353
  */
1354
1354
  getAggregationConfig(type) {
1355
1355
  switch (type) {
1356
- case AggregationType.SUM:
1356
+ case exports.AggregationType.SUM:
1357
1357
  return Aggregations.sum;
1358
- case AggregationType.MIN:
1358
+ case exports.AggregationType.MIN:
1359
1359
  return Aggregations.min;
1360
- case AggregationType.MAX:
1360
+ case exports.AggregationType.MAX:
1361
1361
  return Aggregations.max;
1362
- case AggregationType.GCD:
1362
+ case exports.AggregationType.GCD:
1363
1363
  return Aggregations.gcd;
1364
- case AggregationType.LCM:
1364
+ case exports.AggregationType.LCM:
1365
1365
  return Aggregations.lcm;
1366
- case AggregationType.PRODUCT:
1366
+ case exports.AggregationType.PRODUCT:
1367
1367
  return Aggregations.product;
1368
1368
  default:
1369
1369
  throw new Error(`Unknown aggregation type: ${type}`);
@@ -1409,7 +1409,7 @@ class SegmentTree {
1409
1409
  * For MIN/MAX: just use the update value
1410
1410
  */
1411
1411
  applyUpdate(nodeValue, updateValue, rangeSize) {
1412
- if (this._type === AggregationType.SUM) {
1412
+ if (this._type === exports.AggregationType.SUM) {
1413
1413
  // For sum, we need to multiply by range size
1414
1414
  const update = updateValue * rangeSize;
1415
1415
  return this.aggregate(nodeValue, update);
@@ -1599,7 +1599,7 @@ class SegmentTree {
1599
1599
  /**
1600
1600
  * Create SegmentTree from array
1601
1601
  */
1602
- static from(values, type = AggregationType.SUM) {
1602
+ static from(values, type = exports.AggregationType.SUM) {
1603
1603
  return new SegmentTree({ values, type });
1604
1604
  }
1605
1605
  /**
@@ -1608,7 +1608,7 @@ class SegmentTree {
1608
1608
  static custom(values, aggregate, identity) {
1609
1609
  return new SegmentTree({
1610
1610
  values,
1611
- type: AggregationType.CUSTOM,
1611
+ type: exports.AggregationType.CUSTOM,
1612
1612
  customAggregate: aggregate,
1613
1613
  identity,
1614
1614
  });
@@ -4791,6 +4791,1142 @@ class SuffixArray {
4791
4791
  }
4792
4792
  }
4793
4793
 
4794
+ /**
4795
+ * Sequence Detection for Autofill
4796
+ *
4797
+ * Detects patterns in data sequences for intelligent autofill.
4798
+ *
4799
+ * **Supported Patterns:**
4800
+ * - Arithmetic sequences: 1, 2, 3, 4, ...
4801
+ * - Geometric sequences: 2, 4, 8, 16, ...
4802
+ * - Date sequences: Jan 1, Jan 2, Jan 3, ...
4803
+ * - Day sequences: Mon, Tue, Wed, ...
4804
+ * - Text patterns: Item 1, Item 2, Item 3, ...
4805
+ *
4806
+ * @example
4807
+ * ```typescript
4808
+ * // Detect arithmetic sequence
4809
+ * const pattern = detectSequence([1, 2, 3, 4]);
4810
+ * // pattern: { type: 'arithmetic', step: 1 }
4811
+ *
4812
+ * // Generate next values
4813
+ * const next = generateSequence(pattern, 5, 3);
4814
+ * // next: [5, 6, 7]
4815
+ * ```
4816
+ */
4817
+ /**
4818
+ * Detect sequence pattern from an array of values
4819
+ *
4820
+ * @param values - Array of values (at least 2 required)
4821
+ * @returns Detected pattern
4822
+ */
4823
+ function detectSequence(values) {
4824
+ if (values.length < 2) {
4825
+ return { type: 'none', confidence: 0 };
4826
+ }
4827
+ // Try numeric sequences first
4828
+ if (values.every(v => typeof v === 'number' || !isNaN(Number(v)))) {
4829
+ const numbers = values.map(v => Number(v));
4830
+ // Check arithmetic sequence
4831
+ const arithmeticPattern = detectArithmetic(numbers);
4832
+ if (arithmeticPattern.confidence > 0.8) {
4833
+ return arithmeticPattern;
4834
+ }
4835
+ // Check geometric sequence
4836
+ const geometricPattern = detectGeometric(numbers);
4837
+ if (geometricPattern.confidence > 0.8) {
4838
+ return geometricPattern;
4839
+ }
4840
+ }
4841
+ // Try text patterns
4842
+ const textPattern = detectTextPattern(values.map(v => String(v)));
4843
+ if (textPattern.confidence > 0.7) {
4844
+ return textPattern;
4845
+ }
4846
+ // Check for constant value
4847
+ if (values.every(v => v === values[0])) {
4848
+ return { type: 'constant', confidence: 1.0 };
4849
+ }
4850
+ return { type: 'none', confidence: 0 };
4851
+ }
4852
+ /**
4853
+ * Detect arithmetic sequence (constant difference)
4854
+ */
4855
+ function detectArithmetic(numbers) {
4856
+ if (numbers.length < 2) {
4857
+ return { type: 'none', confidence: 0 };
4858
+ }
4859
+ const differences = [];
4860
+ for (let i = 1; i < numbers.length; i++) {
4861
+ differences.push(numbers[i] - numbers[i - 1]);
4862
+ }
4863
+ // Check if all differences are the same (within small tolerance)
4864
+ const avgDiff = differences.reduce((a, b) => a + b, 0) / differences.length;
4865
+ const variance = differences.reduce((sum, d) => sum + Math.pow(d - avgDiff, 2), 0) / differences.length;
4866
+ if (variance < 0.01) {
4867
+ return {
4868
+ type: 'arithmetic',
4869
+ step: avgDiff,
4870
+ confidence: 1.0,
4871
+ };
4872
+ }
4873
+ return { type: 'none', confidence: 0 };
4874
+ }
4875
+ /**
4876
+ * Detect geometric sequence (constant ratio)
4877
+ */
4878
+ function detectGeometric(numbers) {
4879
+ if (numbers.length < 2 || numbers.some(n => n === 0)) {
4880
+ return { type: 'none', confidence: 0 };
4881
+ }
4882
+ const ratios = [];
4883
+ for (let i = 1; i < numbers.length; i++) {
4884
+ ratios.push(numbers[i] / numbers[i - 1]);
4885
+ }
4886
+ // Check if all ratios are the same
4887
+ const avgRatio = ratios.reduce((a, b) => a + b, 0) / ratios.length;
4888
+ const variance = ratios.reduce((sum, r) => sum + Math.pow(r - avgRatio, 2), 0) / ratios.length;
4889
+ if (variance < 0.01) {
4890
+ return {
4891
+ type: 'geometric',
4892
+ ratio: avgRatio,
4893
+ confidence: 1.0,
4894
+ };
4895
+ }
4896
+ return { type: 'none', confidence: 0 };
4897
+ }
4898
+ /**
4899
+ * Detect text pattern with number suffix
4900
+ * E.g., "Item 1", "Item 2", "Item 3"
4901
+ */
4902
+ function detectTextPattern(strings) {
4903
+ if (strings.length < 2) {
4904
+ return { type: 'none', confidence: 0 };
4905
+ }
4906
+ // Extract text prefix and number suffix
4907
+ const parts = strings.map(s => {
4908
+ const match = s.match(/^(.*?)(\d+)$/);
4909
+ if (match) {
4910
+ return { prefix: match[1], number: parseInt(match[2], 10) };
4911
+ }
4912
+ return null;
4913
+ });
4914
+ // Check if all have the same prefix
4915
+ if (parts.every(p => p !== null)) {
4916
+ const prefix = parts[0].prefix;
4917
+ if (parts.every(p => p.prefix === prefix)) {
4918
+ const numbers = parts.map(p => p.number);
4919
+ const arithmeticPattern = detectArithmetic(numbers);
4920
+ if (arithmeticPattern.type === 'arithmetic') {
4921
+ return {
4922
+ type: 'text',
4923
+ template: prefix + '{n}',
4924
+ step: arithmeticPattern.step,
4925
+ confidence: 0.9,
4926
+ };
4927
+ }
4928
+ }
4929
+ }
4930
+ return { type: 'none', confidence: 0 };
4931
+ }
4932
+ /**
4933
+ * Generate next values in a sequence
4934
+ *
4935
+ * @param pattern - Detected sequence pattern
4936
+ * @param startValue - Starting value
4937
+ * @param count - Number of values to generate
4938
+ * @returns Array of generated values
4939
+ */
4940
+ function generateSequence(pattern, startValue, count) {
4941
+ const result = [];
4942
+ switch (pattern.type) {
4943
+ case 'arithmetic':
4944
+ {
4945
+ let current = Number(startValue);
4946
+ for (let i = 0; i < count; i++) {
4947
+ result.push(current);
4948
+ current += pattern.step;
4949
+ }
4950
+ }
4951
+ break;
4952
+ case 'geometric':
4953
+ {
4954
+ let current = Number(startValue);
4955
+ for (let i = 0; i < count; i++) {
4956
+ result.push(current);
4957
+ current *= pattern.ratio;
4958
+ }
4959
+ }
4960
+ break;
4961
+ case 'text':
4962
+ {
4963
+ // Extract number from start value
4964
+ const match = String(startValue).match(/^(.*?)(\d+)$/);
4965
+ if (match) {
4966
+ let num = parseInt(match[2], 10);
4967
+ const prefix = match[1];
4968
+ for (let i = 0; i < count; i++) {
4969
+ result.push(prefix + num);
4970
+ num += pattern.step ?? 1;
4971
+ }
4972
+ }
4973
+ }
4974
+ break;
4975
+ case 'constant':
4976
+ for (let i = 0; i < count; i++) {
4977
+ result.push(startValue);
4978
+ }
4979
+ break;
4980
+ default:
4981
+ // No pattern - just repeat the start value
4982
+ for (let i = 0; i < count; i++) {
4983
+ result.push(startValue);
4984
+ }
4985
+ }
4986
+ return result;
4987
+ }
4988
+ /**
4989
+ * Autofill a range based on detected pattern
4990
+ *
4991
+ * @param source - Source values to detect pattern from
4992
+ * @param targetLength - Desired length of result
4993
+ * @returns Autofilled array
4994
+ */
4995
+ function autofill(source, targetLength) {
4996
+ if (source.length === 0)
4997
+ return [];
4998
+ if (targetLength <= source.length)
4999
+ return source.slice(0, targetLength);
5000
+ const pattern = detectSequence(source);
5001
+ if (pattern.type === 'none' || pattern.confidence < 0.5) {
5002
+ // No pattern - just repeat the source cyclically
5003
+ const result = [...source];
5004
+ while (result.length < targetLength) {
5005
+ result.push(source[result.length % source.length]);
5006
+ }
5007
+ return result.slice(0, targetLength);
5008
+ }
5009
+ // Generate remaining values based on pattern
5010
+ const remaining = targetLength - source.length;
5011
+ const lastValue = source[source.length - 1];
5012
+ const generated = generateSequence(pattern, lastValue, remaining + 1).slice(1); // Skip first (same as last)
5013
+ return [...source, ...generated];
5014
+ }
5015
+
5016
+ /**
5017
+ * Bloom Filter - Probabilistic Data Structure
5018
+ *
5019
+ * A space-efficient probabilistic data structure for testing set membership.
5020
+ * May have false positives but never false negatives.
5021
+ *
5022
+ * **Time Complexity:**
5023
+ * - Add: O(k) where k = number of hash functions
5024
+ * - Check: O(k)
5025
+ *
5026
+ * **Space Complexity:** O(m) where m = bit array size
5027
+ *
5028
+ * **Use Cases:**
5029
+ * - Quick "definitely not in set" checks (e.g., "does this filter value exist?")
5030
+ * - Cache optimization (avoid expensive lookups for non-existent items)
5031
+ * - Duplicate detection in large datasets
5032
+ * - Spell checkers
5033
+ *
5034
+ * @example
5035
+ * ```typescript
5036
+ * const filter = new BloomFilter(1000, 3); // 1000 bits, 3 hash functions
5037
+ *
5038
+ * // Add some items
5039
+ * filter.add('apple');
5040
+ * filter.add('banana');
5041
+ * filter.add('cherry');
5042
+ *
5043
+ * filter.contains('apple'); // true (definitely in set)
5044
+ * filter.contains('grape'); // false (definitely NOT in set)
5045
+ * filter.contains('apples'); // might be true (false positive possible)
5046
+ * ```
5047
+ *
5048
+ * @example Grid Filter Optimization
5049
+ * ```typescript
5050
+ * // Quickly check if a filter value might exist before scanning column
5051
+ * class ColumnFilter {
5052
+ * private bloomFilter = new BloomFilter(10000, 4);
5053
+ *
5054
+ * indexColumn(values: any[]): void {
5055
+ * values.forEach(v => this.bloomFilter.add(String(v)));
5056
+ * }
5057
+ *
5058
+ * mightContain(value: string): boolean {
5059
+ * return this.bloomFilter.contains(value);
5060
+ * }
5061
+ * }
5062
+ * ```
5063
+ */
5064
+ class BloomFilter {
5065
+ /**
5066
+ * Create a new Bloom Filter
5067
+ *
5068
+ * @param size - Size of the bit array (larger = fewer false positives)
5069
+ * @param hashCount - Number of hash functions (more = fewer false positives, slower)
5070
+ */
5071
+ constructor(size, hashCount = 3) {
5072
+ this.size = size;
5073
+ this.hashCount = hashCount;
5074
+ this.bitArray = new Array(size).fill(false);
5075
+ }
5076
+ /**
5077
+ * Add an item to the filter
5078
+ *
5079
+ * @param item - Item to add
5080
+ */
5081
+ add(item) {
5082
+ const hashes = this.getHashes(item);
5083
+ for (const hash of hashes) {
5084
+ this.bitArray[hash] = true;
5085
+ }
5086
+ }
5087
+ /**
5088
+ * Check if an item might be in the set
5089
+ *
5090
+ * @param item - Item to check
5091
+ * @returns true if item might be in set, false if definitely NOT in set
5092
+ */
5093
+ contains(item) {
5094
+ const hashes = this.getHashes(item);
5095
+ for (const hash of hashes) {
5096
+ if (!this.bitArray[hash]) {
5097
+ return false; // Definitely not in set
5098
+ }
5099
+ }
5100
+ return true; // Might be in set (could be false positive)
5101
+ }
5102
+ /**
5103
+ * Clear the filter
5104
+ */
5105
+ clear() {
5106
+ this.bitArray.fill(false);
5107
+ }
5108
+ /**
5109
+ * Get the false positive probability
5110
+ *
5111
+ * P(false positive) ≈ (1 - e^(-kn/m))^k
5112
+ * where k = hash functions, n = items added, m = bit array size
5113
+ *
5114
+ * @param itemCount - Number of items added
5115
+ * @returns Estimated false positive probability
5116
+ */
5117
+ getFalsePositiveRate(itemCount) {
5118
+ const k = this.hashCount;
5119
+ const m = this.size;
5120
+ const n = itemCount;
5121
+ return Math.pow(1 - Math.exp((-k * n) / m), k);
5122
+ }
5123
+ /**
5124
+ * Calculate optimal bit array size for desired false positive rate
5125
+ *
5126
+ * m = -(n * ln(p)) / (ln(2)^2)
5127
+ *
5128
+ * @param itemCount - Expected number of items
5129
+ * @param falsePositiveRate - Desired false positive rate (e.g., 0.01 for 1%)
5130
+ * @returns Optimal bit array size
5131
+ */
5132
+ static optimalSize(itemCount, falsePositiveRate) {
5133
+ return Math.ceil(-(itemCount * Math.log(falsePositiveRate)) / Math.pow(Math.log(2), 2));
5134
+ }
5135
+ /**
5136
+ * Calculate optimal number of hash functions
5137
+ *
5138
+ * k = (m/n) * ln(2)
5139
+ *
5140
+ * @param bitArraySize - Size of bit array
5141
+ * @param itemCount - Expected number of items
5142
+ * @returns Optimal number of hash functions
5143
+ */
5144
+ static optimalHashCount(bitArraySize, itemCount) {
5145
+ return Math.ceil((bitArraySize / itemCount) * Math.log(2));
5146
+ }
5147
+ // ==================== Private Methods ====================
5148
+ /**
5149
+ * Generate multiple hash values for an item
5150
+ */
5151
+ getHashes(item) {
5152
+ const hashes = [];
5153
+ // Use double hashing: h_i(x) = h1(x) + i * h2(x)
5154
+ const hash1 = this.hashFunc1(item);
5155
+ const hash2 = this.hashFunc2(item);
5156
+ for (let i = 0; i < this.hashCount; i++) {
5157
+ const hash = (hash1 + i * hash2) % this.size;
5158
+ hashes.push(Math.abs(hash));
5159
+ }
5160
+ return hashes;
5161
+ }
5162
+ /**
5163
+ * First hash function (simple polynomial hash)
5164
+ */
5165
+ hashFunc1(str) {
5166
+ let hash = 0;
5167
+ for (let i = 0; i < str.length; i++) {
5168
+ hash = (hash * 31 + str.charCodeAt(i)) % this.size;
5169
+ }
5170
+ return hash;
5171
+ }
5172
+ /**
5173
+ * Second hash function (FNV-1a variant)
5174
+ */
5175
+ hashFunc2(str) {
5176
+ let hash = 2166136261; // FNV offset basis
5177
+ for (let i = 0; i < str.length; i++) {
5178
+ hash ^= str.charCodeAt(i);
5179
+ hash = (hash * 16777619) % this.size; // FNV prime
5180
+ }
5181
+ return hash;
5182
+ }
5183
+ }
5184
+
5185
+ /**
5186
+ * Helper to handle null values in comparisons
5187
+ */
5188
+ function compareWithNulls(a, b, nullPosition, compareFn) {
5189
+ const aIsNull = a === null || a === undefined || Number.isNaN(a);
5190
+ const bIsNull = b === null || b === undefined || Number.isNaN(b);
5191
+ if (aIsNull && bIsNull)
5192
+ return 0;
5193
+ if (aIsNull) {
5194
+ return nullPosition === 'first' ? -1 : nullPosition === 'last' ? 1 : 0;
5195
+ }
5196
+ if (bIsNull) {
5197
+ return nullPosition === 'first' ? 1 : nullPosition === 'last' ? -1 : 0;
5198
+ }
5199
+ return compareFn(a, b);
5200
+ }
5201
+ /**
5202
+ * Create a numeric comparator
5203
+ *
5204
+ * @param nullPosition - Where to place null values in sort order
5205
+ * @returns Comparator function
5206
+ *
5207
+ * @example
5208
+ * ```typescript
5209
+ * const numbers = [3, null, 1, undefined, 2];
5210
+ * numbers.sort(numericComparator('last'));
5211
+ * // [1, 2, 3, null, undefined]
5212
+ * ```
5213
+ */
5214
+ function numericComparator(nullPosition = 'last') {
5215
+ return (a, b) => {
5216
+ return compareWithNulls(a, b, nullPosition, (x, y) => {
5217
+ const numX = Number(x);
5218
+ const numY = Number(y);
5219
+ if (Number.isNaN(numX) && Number.isNaN(numY))
5220
+ return 0;
5221
+ if (Number.isNaN(numX))
5222
+ return nullPosition === 'last' ? 1 : -1;
5223
+ if (Number.isNaN(numY))
5224
+ return nullPosition === 'last' ? -1 : 1;
5225
+ return numX - numY;
5226
+ });
5227
+ };
5228
+ }
5229
+ /**
5230
+ * Create a string comparator
5231
+ *
5232
+ * @param options - Comparator options
5233
+ * @returns Comparator function
5234
+ *
5235
+ * @example
5236
+ * ```typescript
5237
+ * const names = ['Bob', 'alice', 'Charlie'];
5238
+ * names.sort(stringComparator({ caseInsensitive: true }));
5239
+ * // ['alice', 'Bob', 'Charlie']
5240
+ * ```
5241
+ */
5242
+ function stringComparator(options = {}) {
5243
+ const { caseInsensitive = false, localeCompare = false, locale = 'en-US', nullPosition = 'last', } = options;
5244
+ return (a, b) => {
5245
+ return compareWithNulls(a, b, nullPosition, (x, y) => {
5246
+ let strA = String(x);
5247
+ let strB = String(y);
5248
+ if (caseInsensitive) {
5249
+ strA = strA.toLowerCase();
5250
+ strB = strB.toLowerCase();
5251
+ }
5252
+ if (localeCompare) {
5253
+ return strA.localeCompare(strB, locale);
5254
+ }
5255
+ return strA < strB ? -1 : strA > strB ? 1 : 0;
5256
+ });
5257
+ };
5258
+ }
5259
+
5260
+ /**
5261
+ * Minimum gallop threshold
5262
+ * How many times one run must "win" before we switch to galloping mode
5263
+ *
5264
+ * JavaScript Note: Set higher than Python's default (7) due to function call overhead.
5265
+ * Galloping mode has more overhead in JS, so we require more consecutive wins
5266
+ * to justify entering gallop mode.
5267
+ */
5268
+ const MIN_GALLOP = 32;
5269
+ /**
5270
+ * Minimum merge threshold for stack invariants
5271
+ */
5272
+ const MIN_MERGE = 32;
5273
+ /**
5274
+ * Timsort - Production-grade stable O(n log n) sorting algorithm
5275
+ *
5276
+ * This is a full implementation of Timsort with all optimizations:
5277
+ * - Natural run detection (finds already-sorted sequences)
5278
+ * - Galloping mode (binary search optimization during merges)
5279
+ * - Stack-based merge strategy (maintains merge invariants)
5280
+ * - Reusable temporary buffer (reduces allocations)
5281
+ *
5282
+ * Used by Python, Java, and V8's Array.sort().
5283
+ *
5284
+ * @param array - Array to sort (modified in-place)
5285
+ * @param comparator - Comparison function
5286
+ * @param options - Timsort options
5287
+ * @returns The sorted array
5288
+ *
5289
+ * @complexity
5290
+ * - Time: O(n log n) worst case, O(n) best case (already sorted)
5291
+ * - Space: O(n) for temporary merge buffer
5292
+ */
5293
+ function timsort(array, comparator, options = {}) {
5294
+ const n = array.length;
5295
+ if (n < 2) {
5296
+ return array;
5297
+ }
5298
+ const sorter = new TimsortImpl(array, comparator, options);
5299
+ sorter.sort();
5300
+ return array;
5301
+ }
5302
+ /**
5303
+ * Sort indices based on values without modifying the values
5304
+ *
5305
+ * @param indices - Array of indices to sort
5306
+ * @param getValue - Function to get value at an index
5307
+ * @param comparator - Comparison function for values
5308
+ * @param options - Timsort options
5309
+ * @returns Sorted array of indices
5310
+ *
5311
+ * @example
5312
+ * ```typescript
5313
+ * const values = ['Charlie', 'Alice', 'Bob'];
5314
+ * const indices = [0, 1, 2];
5315
+ *
5316
+ * const sorted = timsortIndices(
5317
+ * indices,
5318
+ * (i) => values[i],
5319
+ * (a, b) => a.localeCompare(b)
5320
+ * );
5321
+ * // sorted = [1, 2, 0] (Alice, Bob, Charlie)
5322
+ * ```
5323
+ */
5324
+ function timsortIndices(indices, getValue, comparator, options = {}) {
5325
+ // Create index comparator
5326
+ const indexComparator = (idxA, idxB) => {
5327
+ return comparator(getValue(idxA), getValue(idxB));
5328
+ };
5329
+ return timsort(indices, indexComparator, options);
5330
+ }
5331
+ /**
5332
+ * TimsortImpl - Internal implementation class
5333
+ * Maintains state for the sorting process
5334
+ */
5335
+ class TimsortImpl {
5336
+ constructor(array, comparator, options) {
5337
+ this.array = array;
5338
+ this.comparator = comparator;
5339
+ this.minGallop = MIN_GALLOP;
5340
+ this.configuredMinRun = options.minRun;
5341
+ this.runStack = [];
5342
+ // Pre-allocate temporary buffer (reused across all merges)
5343
+ const tmpSize = Math.floor(array.length / 2);
5344
+ this.tmp = new Array(tmpSize);
5345
+ }
5346
+ /**
5347
+ * Main sort entry point
5348
+ */
5349
+ sort() {
5350
+ const n = this.array.length;
5351
+ // Calculate optimal minRun (use configured value if provided)
5352
+ const minRun = this.configuredMinRun ?? this.computeMinRun(n);
5353
+ let lo = 0;
5354
+ // Identify and sort runs
5355
+ while (lo < n) {
5356
+ // Find next run (either ascending or descending)
5357
+ let runLen = this.countRunAndMakeAscending(lo);
5358
+ // If run is too short, extend it with insertion sort
5359
+ if (runLen < minRun) {
5360
+ const force = Math.min(n - lo, minRun);
5361
+ this.binaryInsertionSort(lo, lo + force, lo + runLen);
5362
+ runLen = force;
5363
+ }
5364
+ // Push run onto stack and maybe merge
5365
+ this.pushRun(lo, runLen);
5366
+ this.mergeCollapse();
5367
+ lo += runLen;
5368
+ }
5369
+ // Force merge all remaining runs
5370
+ this.mergeForceCollapse();
5371
+ }
5372
+ /**
5373
+ * Count length of run starting at lo and reverse if descending
5374
+ * Returns length of run (at least 2 if array has 2+ elements)
5375
+ */
5376
+ countRunAndMakeAscending(lo) {
5377
+ const n = this.array.length;
5378
+ if (lo + 1 >= n) {
5379
+ return 1;
5380
+ }
5381
+ let runHi = lo + 1;
5382
+ // Check if descending
5383
+ if (this.comparator(this.array[runHi], this.array[lo]) < 0) {
5384
+ // Descending run - find end and reverse
5385
+ runHi++;
5386
+ while (runHi < n && this.comparator(this.array[runHi], this.array[runHi - 1]) < 0) {
5387
+ runHi++;
5388
+ }
5389
+ this.reverseRange(lo, runHi);
5390
+ }
5391
+ else {
5392
+ // Ascending run - find end
5393
+ while (runHi < n && this.comparator(this.array[runHi], this.array[runHi - 1]) >= 0) {
5394
+ runHi++;
5395
+ }
5396
+ }
5397
+ return runHi - lo;
5398
+ }
5399
+ /**
5400
+ * Reverse array[lo:hi]
5401
+ */
5402
+ reverseRange(lo, hi) {
5403
+ hi--;
5404
+ while (lo < hi) {
5405
+ const temp = this.array[lo];
5406
+ this.array[lo++] = this.array[hi];
5407
+ this.array[hi--] = temp;
5408
+ }
5409
+ }
5410
+ /**
5411
+ * Binary insertion sort - more efficient than regular insertion sort
5412
+ * Sorts array[lo:hi] where array[lo:start] is already sorted
5413
+ */
5414
+ binaryInsertionSort(lo, hi, start) {
5415
+ if (start === lo) {
5416
+ start++;
5417
+ }
5418
+ for (; start < hi; start++) {
5419
+ const pivot = this.array[start];
5420
+ // Binary search to find insertion point
5421
+ let left = lo;
5422
+ let right = start;
5423
+ while (left < right) {
5424
+ const mid = (left + right) >>> 1;
5425
+ if (this.comparator(pivot, this.array[mid]) < 0) {
5426
+ right = mid;
5427
+ }
5428
+ else {
5429
+ left = mid + 1;
5430
+ }
5431
+ }
5432
+ // Shift elements and insert
5433
+ const n = start - left;
5434
+ if (n > 0) {
5435
+ for (let i = start; i > left; i--) {
5436
+ this.array[i] = this.array[i - 1];
5437
+ }
5438
+ this.array[left] = pivot;
5439
+ }
5440
+ }
5441
+ }
5442
+ /**
5443
+ * Push a run onto the pending-run stack
5444
+ */
5445
+ pushRun(runBase, runLen) {
5446
+ this.runStack.push({ start: runBase, length: runLen });
5447
+ }
5448
+ /**
5449
+ * Merge runs to maintain stack invariants
5450
+ *
5451
+ * Invariants (from Python's timsort):
5452
+ * 1. runLen[i - 2] > runLen[i - 1] + runLen[i]
5453
+ * 2. runLen[i - 1] > runLen[i]
5454
+ */
5455
+ mergeCollapse() {
5456
+ while (this.runStack.length > 1) {
5457
+ let n = this.runStack.length - 2;
5458
+ if ((n > 0 && this.runStack[n - 1].length <= this.runStack[n].length + this.runStack[n + 1].length) ||
5459
+ (n > 1 && this.runStack[n - 2].length <= this.runStack[n - 1].length + this.runStack[n].length)) {
5460
+ // Merge the smaller of the two pairs
5461
+ if (this.runStack[n - 1].length < this.runStack[n + 1].length) {
5462
+ n--;
5463
+ }
5464
+ this.mergeAt(n);
5465
+ }
5466
+ else if (this.runStack[n].length <= this.runStack[n + 1].length) {
5467
+ this.mergeAt(n);
5468
+ }
5469
+ else {
5470
+ break; // Invariants are satisfied
5471
+ }
5472
+ }
5473
+ }
5474
+ /**
5475
+ * Force merge all runs on the stack
5476
+ */
5477
+ mergeForceCollapse() {
5478
+ while (this.runStack.length > 1) {
5479
+ let n = this.runStack.length - 2;
5480
+ if (n > 0 && this.runStack[n - 1].length < this.runStack[n + 1].length) {
5481
+ n--;
5482
+ }
5483
+ this.mergeAt(n);
5484
+ }
5485
+ }
5486
+ /**
5487
+ * Merge the two runs at stack indices i and i+1
5488
+ */
5489
+ mergeAt(i) {
5490
+ const run1 = this.runStack[i];
5491
+ const run2 = this.runStack[i + 1];
5492
+ // Merge run1 with run2
5493
+ this.mergeRuns(run1.start, run1.length, run2.start, run2.length);
5494
+ // Update stack
5495
+ this.runStack[i] = { start: run1.start, length: run1.length + run2.length };
5496
+ this.runStack.splice(i + 1, 1);
5497
+ }
5498
+ /**
5499
+ * Merge two adjacent runs with galloping optimization
5500
+ */
5501
+ mergeRuns(base1, len1, base2, len2) {
5502
+ // Optimization: Find where first element of run2 goes in run1
5503
+ // Elements before that can be ignored
5504
+ const k = this.gallopRight(this.array[base2], base1, len1, 0);
5505
+ base1 += k;
5506
+ len1 -= k;
5507
+ if (len1 === 0) {
5508
+ return;
5509
+ }
5510
+ // Optimization: Find where last element of run1 goes in run2
5511
+ // Elements after that can be ignored
5512
+ len2 = this.gallopLeft(this.array[base1 + len1 - 1], base2, len2, len2 - 1);
5513
+ if (len2 === 0) {
5514
+ return;
5515
+ }
5516
+ // Merge remaining runs, using tmp array for smaller run
5517
+ if (len1 <= len2) {
5518
+ this.mergeLo(base1, len1, base2, len2);
5519
+ }
5520
+ else {
5521
+ this.mergeHi(base1, len1, base2, len2);
5522
+ }
5523
+ }
5524
+ /**
5525
+ * Merge with run1 in temporary array (run1 is smaller)
5526
+ */
5527
+ mergeLo(base1, len1, base2, len2) {
5528
+ // Copy run1 to tmp
5529
+ for (let i = 0; i < len1; i++) {
5530
+ this.tmp[i] = this.array[base1 + i];
5531
+ }
5532
+ let cursor1 = 0; // Index in tmp
5533
+ let cursor2 = base2; // Index in array
5534
+ let dest = base1; // Index in array
5535
+ // Move first element of run2
5536
+ this.array[dest++] = this.array[cursor2++];
5537
+ len2--;
5538
+ if (len2 === 0) {
5539
+ // Run2 exhausted, copy remaining tmp
5540
+ for (let i = 0; i < len1; i++) {
5541
+ this.array[dest + i] = this.tmp[cursor1 + i];
5542
+ }
5543
+ return;
5544
+ }
5545
+ if (len1 === 1) {
5546
+ // Run1 has single element, copy run2 then the element
5547
+ for (let i = 0; i < len2; i++) {
5548
+ this.array[dest + i] = this.array[cursor2 + i];
5549
+ }
5550
+ this.array[dest + len2] = this.tmp[cursor1];
5551
+ return;
5552
+ }
5553
+ let minGallop = this.minGallop;
5554
+ outer: while (true) {
5555
+ let count1 = 0; // Number of times run1 won in a row
5556
+ let count2 = 0; // Number of times run2 won in a row
5557
+ // Simple merge until one run starts "winning" consistently
5558
+ do {
5559
+ if (this.comparator(this.array[cursor2], this.tmp[cursor1]) < 0) {
5560
+ this.array[dest++] = this.array[cursor2++];
5561
+ count2++;
5562
+ count1 = 0;
5563
+ len2--;
5564
+ if (len2 === 0)
5565
+ break outer;
5566
+ }
5567
+ else {
5568
+ this.array[dest++] = this.tmp[cursor1++];
5569
+ count1++;
5570
+ count2 = 0;
5571
+ len1--;
5572
+ if (len1 === 1)
5573
+ break outer;
5574
+ }
5575
+ } while ((count1 | count2) < minGallop);
5576
+ // One run is winning consistently - switch to galloping mode
5577
+ do {
5578
+ count1 = this.gallopRightInTmp(this.array[cursor2], cursor1, len1, 0);
5579
+ if (count1 !== 0) {
5580
+ for (let i = 0; i < count1; i++) {
5581
+ this.array[dest++] = this.tmp[cursor1++];
5582
+ }
5583
+ len1 -= count1;
5584
+ if (len1 <= 1)
5585
+ break outer;
5586
+ }
5587
+ this.array[dest++] = this.array[cursor2++];
5588
+ len2--;
5589
+ if (len2 === 0)
5590
+ break outer;
5591
+ count2 = this.gallopLeft(this.tmp[cursor1], cursor2, len2, 0);
5592
+ if (count2 !== 0) {
5593
+ for (let i = 0; i < count2; i++) {
5594
+ this.array[dest++] = this.array[cursor2++];
5595
+ }
5596
+ len2 -= count2;
5597
+ if (len2 === 0)
5598
+ break outer;
5599
+ }
5600
+ this.array[dest++] = this.tmp[cursor1++];
5601
+ len1--;
5602
+ if (len1 === 1)
5603
+ break outer;
5604
+ minGallop--;
5605
+ } while (count1 >= MIN_GALLOP || count2 >= MIN_GALLOP);
5606
+ if (minGallop < 0)
5607
+ minGallop = 0;
5608
+ minGallop += 2; // Penalize leaving gallop mode
5609
+ }
5610
+ this.minGallop = minGallop < 1 ? 1 : minGallop;
5611
+ // Handle remaining elements
5612
+ if (len1 === 1) {
5613
+ for (let i = 0; i < len2; i++) {
5614
+ this.array[dest + i] = this.array[cursor2 + i];
5615
+ }
5616
+ this.array[dest + len2] = this.tmp[cursor1];
5617
+ }
5618
+ else if (len1 > 0) {
5619
+ for (let i = 0; i < len1; i++) {
5620
+ this.array[dest + i] = this.tmp[cursor1 + i];
5621
+ }
5622
+ }
5623
+ }
5624
+ /**
5625
+ * Merge with run2 in temporary array (run2 is smaller)
5626
+ */
5627
+ mergeHi(base1, len1, base2, len2) {
5628
+ // Copy run2 to tmp
5629
+ for (let i = 0; i < len2; i++) {
5630
+ this.tmp[i] = this.array[base2 + i];
5631
+ }
5632
+ let cursor1 = base1 + len1 - 1; // Index in array (run1, from end)
5633
+ let cursor2 = len2 - 1; // Index in tmp (from end)
5634
+ let dest = base2 + len2 - 1; // Index in array (from end)
5635
+ // Move last element of run1
5636
+ this.array[dest--] = this.array[cursor1--];
5637
+ len1--;
5638
+ if (len1 === 0) {
5639
+ // Run1 exhausted, copy remaining tmp
5640
+ for (let i = 0; i < len2; i++) {
5641
+ this.array[dest - len2 + 1 + i] = this.tmp[i];
5642
+ }
5643
+ return;
5644
+ }
5645
+ if (len2 === 1) {
5646
+ // Run2 has single element
5647
+ dest -= len1;
5648
+ cursor1 -= len1;
5649
+ for (let i = len1 - 1; i >= 0; i--) {
5650
+ this.array[dest + 1 + i] = this.array[cursor1 + 1 + i];
5651
+ }
5652
+ this.array[dest] = this.tmp[cursor2];
5653
+ return;
5654
+ }
5655
+ let minGallop = this.minGallop;
5656
+ outer: while (true) {
5657
+ let count1 = 0;
5658
+ let count2 = 0;
5659
+ do {
5660
+ if (this.comparator(this.tmp[cursor2], this.array[cursor1]) < 0) {
5661
+ this.array[dest--] = this.array[cursor1--];
5662
+ count1++;
5663
+ count2 = 0;
5664
+ len1--;
5665
+ if (len1 === 0)
5666
+ break outer;
5667
+ }
5668
+ else {
5669
+ this.array[dest--] = this.tmp[cursor2--];
5670
+ count2++;
5671
+ count1 = 0;
5672
+ len2--;
5673
+ if (len2 === 1)
5674
+ break outer;
5675
+ }
5676
+ } while ((count1 | count2) < minGallop);
5677
+ do {
5678
+ count1 = len1 - this.gallopRight(this.tmp[cursor2], base1, len1, cursor1 - base1);
5679
+ if (count1 !== 0) {
5680
+ dest -= count1;
5681
+ cursor1 -= count1;
5682
+ len1 -= count1;
5683
+ for (let i = count1 - 1; i >= 0; i--) {
5684
+ this.array[dest + 1 + i] = this.array[cursor1 + 1 + i];
5685
+ }
5686
+ if (len1 === 0)
5687
+ break outer;
5688
+ }
5689
+ this.array[dest--] = this.tmp[cursor2--];
5690
+ len2--;
5691
+ if (len2 === 1)
5692
+ break outer;
5693
+ count2 = len2 - this.gallopLeftInTmp(this.array[cursor1], 0, len2, cursor2);
5694
+ if (count2 !== 0) {
5695
+ dest -= count2;
5696
+ cursor2 -= count2;
5697
+ len2 -= count2;
5698
+ for (let i = 0; i < count2; i++) {
5699
+ this.array[dest + 1 + i] = this.tmp[cursor2 + 1 + i];
5700
+ }
5701
+ if (len2 <= 1)
5702
+ break outer;
5703
+ }
5704
+ this.array[dest--] = this.array[cursor1--];
5705
+ len1--;
5706
+ if (len1 === 0)
5707
+ break outer;
5708
+ minGallop--;
5709
+ } while (count1 >= MIN_GALLOP || count2 >= MIN_GALLOP);
5710
+ if (minGallop < 0)
5711
+ minGallop = 0;
5712
+ minGallop += 2;
5713
+ }
5714
+ this.minGallop = minGallop < 1 ? 1 : minGallop;
5715
+ if (len2 === 1) {
5716
+ dest -= len1;
5717
+ cursor1 -= len1;
5718
+ for (let i = len1 - 1; i >= 0; i--) {
5719
+ this.array[dest + 1 + i] = this.array[cursor1 + 1 + i];
5720
+ }
5721
+ this.array[dest] = this.tmp[cursor2];
5722
+ }
5723
+ else if (len2 > 0) {
5724
+ for (let i = 0; i < len2; i++) {
5725
+ this.array[dest - len2 + 1 + i] = this.tmp[i];
5726
+ }
5727
+ }
5728
+ }
5729
+ /**
5730
+ * Gallop right: Find position where key belongs in array[base:base+len]
5731
+ * Returns offset k where array[base+k-1] < key <= array[base+k]
5732
+ * Uses exponential search followed by binary search
5733
+ */
5734
+ gallopRight(key, base, len, hint) {
5735
+ let lastOfs = 0;
5736
+ let ofs = 1;
5737
+ if (this.comparator(key, this.array[base + hint]) < 0) {
5738
+ // Gallop left until array[base+hint-ofs] < key <= array[base+hint-lastOfs]
5739
+ const maxOfs = hint + 1;
5740
+ while (ofs < maxOfs && this.comparator(key, this.array[base + hint - ofs]) < 0) {
5741
+ lastOfs = ofs;
5742
+ ofs = (ofs << 1) + 1;
5743
+ if (ofs <= 0)
5744
+ ofs = maxOfs; // Overflow
5745
+ }
5746
+ if (ofs > maxOfs)
5747
+ ofs = maxOfs;
5748
+ // Make offsets relative to base
5749
+ const tmp = lastOfs;
5750
+ lastOfs = hint - ofs;
5751
+ ofs = hint - tmp;
5752
+ }
5753
+ else {
5754
+ // Gallop right until array[base+hint+lastOfs] < key <= array[base+hint+ofs]
5755
+ const maxOfs = len - hint;
5756
+ while (ofs < maxOfs && this.comparator(key, this.array[base + hint + ofs]) >= 0) {
5757
+ lastOfs = ofs;
5758
+ ofs = (ofs << 1) + 1;
5759
+ if (ofs <= 0)
5760
+ ofs = maxOfs;
5761
+ }
5762
+ if (ofs > maxOfs)
5763
+ ofs = maxOfs;
5764
+ // Make offsets relative to base
5765
+ lastOfs += hint;
5766
+ ofs += hint;
5767
+ }
5768
+ // Binary search in array[base+lastOfs:base+ofs]
5769
+ lastOfs++;
5770
+ while (lastOfs < ofs) {
5771
+ const m = lastOfs + ((ofs - lastOfs) >>> 1);
5772
+ if (this.comparator(key, this.array[base + m]) < 0) {
5773
+ ofs = m;
5774
+ }
5775
+ else {
5776
+ lastOfs = m + 1;
5777
+ }
5778
+ }
5779
+ return ofs;
5780
+ }
5781
+ /**
5782
+ * Gallop left: Like gallopRight, but finds position where key < array[base+k]
5783
+ */
5784
+ gallopLeft(key, base, len, hint) {
5785
+ let lastOfs = 0;
5786
+ let ofs = 1;
5787
+ if (this.comparator(key, this.array[base + hint]) > 0) {
5788
+ const maxOfs = len - hint;
5789
+ while (ofs < maxOfs && this.comparator(key, this.array[base + hint + ofs]) > 0) {
5790
+ lastOfs = ofs;
5791
+ ofs = (ofs << 1) + 1;
5792
+ if (ofs <= 0)
5793
+ ofs = maxOfs;
5794
+ }
5795
+ if (ofs > maxOfs)
5796
+ ofs = maxOfs;
5797
+ lastOfs += hint;
5798
+ ofs += hint;
5799
+ }
5800
+ else {
5801
+ const maxOfs = hint + 1;
5802
+ while (ofs < maxOfs && this.comparator(key, this.array[base + hint - ofs]) <= 0) {
5803
+ lastOfs = ofs;
5804
+ ofs = (ofs << 1) + 1;
5805
+ if (ofs <= 0)
5806
+ ofs = maxOfs;
5807
+ }
5808
+ if (ofs > maxOfs)
5809
+ ofs = maxOfs;
5810
+ const tmp = lastOfs;
5811
+ lastOfs = hint - ofs;
5812
+ ofs = hint - tmp;
5813
+ }
5814
+ lastOfs++;
5815
+ while (lastOfs < ofs) {
5816
+ const m = lastOfs + ((ofs - lastOfs) >>> 1);
5817
+ if (this.comparator(key, this.array[base + m]) > 0) {
5818
+ lastOfs = m + 1;
5819
+ }
5820
+ else {
5821
+ ofs = m;
5822
+ }
5823
+ }
5824
+ return ofs;
5825
+ }
5826
+ /**
5827
+ * Gallop right in tmp array: Find position where key belongs in tmp[base:base+len]
5828
+ */
5829
+ gallopRightInTmp(key, base, len, hint) {
5830
+ let lastOfs = 0;
5831
+ let ofs = 1;
5832
+ if (this.comparator(key, this.tmp[base + hint]) < 0) {
5833
+ const maxOfs = hint + 1;
5834
+ while (ofs < maxOfs && this.comparator(key, this.tmp[base + hint - ofs]) < 0) {
5835
+ lastOfs = ofs;
5836
+ ofs = (ofs << 1) + 1;
5837
+ if (ofs <= 0)
5838
+ ofs = maxOfs;
5839
+ }
5840
+ if (ofs > maxOfs)
5841
+ ofs = maxOfs;
5842
+ const tmp = lastOfs;
5843
+ lastOfs = hint - ofs;
5844
+ ofs = hint - tmp;
5845
+ }
5846
+ else {
5847
+ const maxOfs = len - hint;
5848
+ while (ofs < maxOfs && this.comparator(key, this.tmp[base + hint + ofs]) >= 0) {
5849
+ lastOfs = ofs;
5850
+ ofs = (ofs << 1) + 1;
5851
+ if (ofs <= 0)
5852
+ ofs = maxOfs;
5853
+ }
5854
+ if (ofs > maxOfs)
5855
+ ofs = maxOfs;
5856
+ lastOfs += hint;
5857
+ ofs += hint;
5858
+ }
5859
+ lastOfs++;
5860
+ while (lastOfs < ofs) {
5861
+ const m = lastOfs + ((ofs - lastOfs) >>> 1);
5862
+ if (this.comparator(key, this.tmp[base + m]) < 0) {
5863
+ ofs = m;
5864
+ }
5865
+ else {
5866
+ lastOfs = m + 1;
5867
+ }
5868
+ }
5869
+ return ofs;
5870
+ }
5871
+ /**
5872
+ * Gallop left in tmp array
5873
+ */
5874
+ gallopLeftInTmp(key, base, len, hint) {
5875
+ let lastOfs = 0;
5876
+ let ofs = 1;
5877
+ if (this.comparator(key, this.tmp[base + hint]) > 0) {
5878
+ const maxOfs = len - hint;
5879
+ while (ofs < maxOfs && this.comparator(key, this.tmp[base + hint + ofs]) > 0) {
5880
+ lastOfs = ofs;
5881
+ ofs = (ofs << 1) + 1;
5882
+ if (ofs <= 0)
5883
+ ofs = maxOfs;
5884
+ }
5885
+ if (ofs > maxOfs)
5886
+ ofs = maxOfs;
5887
+ lastOfs += hint;
5888
+ ofs += hint;
5889
+ }
5890
+ else {
5891
+ const maxOfs = hint + 1;
5892
+ while (ofs < maxOfs && this.comparator(key, this.tmp[base + hint - ofs]) <= 0) {
5893
+ lastOfs = ofs;
5894
+ ofs = (ofs << 1) + 1;
5895
+ if (ofs <= 0)
5896
+ ofs = maxOfs;
5897
+ }
5898
+ if (ofs > maxOfs)
5899
+ ofs = maxOfs;
5900
+ const tmp = lastOfs;
5901
+ lastOfs = hint - ofs;
5902
+ ofs = hint - tmp;
5903
+ }
5904
+ lastOfs++;
5905
+ while (lastOfs < ofs) {
5906
+ const m = lastOfs + ((ofs - lastOfs) >>> 1);
5907
+ if (this.comparator(key, this.tmp[base + m]) > 0) {
5908
+ lastOfs = m + 1;
5909
+ }
5910
+ else {
5911
+ ofs = m;
5912
+ }
5913
+ }
5914
+ return ofs;
5915
+ }
5916
+ /**
5917
+ * Compute optimal minRun value
5918
+ * Returns a value in range [32, 64] such that n/minRun is close to a power of 2
5919
+ */
5920
+ computeMinRun(n) {
5921
+ let r = 0;
5922
+ while (n >= MIN_MERGE) {
5923
+ r |= n & 1;
5924
+ n >>= 1;
5925
+ }
5926
+ return n + r;
5927
+ }
5928
+ }
5929
+
4794
5930
  /**
4795
5931
  * @fileoverview Timing utilities for performance optimization
4796
5932
  * @module @zengrid/shared/utils/timing
@@ -6208,6 +7344,7 @@ const VERSION = '0.1.0';
6208
7344
 
6209
7345
  exports.Aggregations = Aggregations;
6210
7346
  exports.BaseCoordinator = BaseCoordinator;
7347
+ exports.BloomFilter = BloomFilter;
6211
7348
  exports.ColumnStore = ColumnStore;
6212
7349
  exports.CommandStack = CommandStack;
6213
7350
  exports.DependencyGraph = DependencyGraph;
@@ -6238,13 +7375,20 @@ exports.Subject = Subject;
6238
7375
  exports.SuffixArray = SuffixArray;
6239
7376
  exports.Trie = Trie;
6240
7377
  exports.VERSION = VERSION;
7378
+ exports.autofill = autofill;
6241
7379
  exports.binarySearch = binarySearch;
6242
7380
  exports.binarySearchLeft = binarySearchLeft;
6243
7381
  exports.binarySearchRight = binarySearchRight;
6244
7382
  exports.createRAFBatchScheduler = createRAFBatchScheduler;
6245
7383
  exports.debounce = debounce;
6246
7384
  exports.delay = delay;
7385
+ exports.detectSequence = detectSequence;
7386
+ exports.generateSequence = generateSequence;
7387
+ exports.numericComparator = numericComparator;
6247
7388
  exports.resolveOperationMode = resolveOperationMode;
6248
7389
  exports.skipListDefaultComparator = defaultComparator;
7390
+ exports.stringComparator = stringComparator;
6249
7391
  exports.throttle = throttle;
6250
7392
  exports.timeout = timeout;
7393
+ exports.timsort = timsort;
7394
+ exports.timsortIndices = timsortIndices;