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