@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.
- package/dist/index.cjs.js +1157 -13
- package/dist/index.esm.js +1137 -1
- package/dist/package.json +1 -1
- package/dist/src/algorithms/filter/bloom-filter.d.ts.map +1 -1
- package/dist/src/algorithms/filter/index.d.ts.map +1 -1
- package/dist/src/algorithms/graph/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/graph/dfs.d.ts.map +1 -1
- package/dist/src/algorithms/graph/graph.interface.d.ts.map +1 -1
- package/dist/src/algorithms/graph/index.d.ts.map +1 -1
- package/dist/src/algorithms/index.d.ts.map +1 -1
- package/dist/src/algorithms/pattern/index.d.ts.map +1 -1
- package/dist/src/algorithms/pattern/sequence-detector.d.ts.map +1 -1
- package/dist/src/algorithms/search/binary-search.d.ts.map +1 -1
- package/dist/src/algorithms/search/index.d.ts.map +1 -1
- package/dist/src/algorithms/sorting/comparators.d.ts.map +1 -1
- package/dist/src/algorithms/sorting/index.d.ts.map +1 -1
- package/dist/src/algorithms/sorting/sorting.interface.d.ts.map +1 -1
- package/dist/src/algorithms/sorting/timsort.d.ts.map +1 -1
- package/dist/src/data-structures/column-store/column-store.d.ts.map +1 -1
- package/dist/src/data-structures/column-store/column-store.interface.d.ts.map +1 -1
- package/dist/src/data-structures/column-store/index.d.ts.map +1 -1
- package/dist/src/data-structures/command-stack/command-stack.d.ts.map +1 -1
- package/dist/src/data-structures/command-stack/command-stack.interface.d.ts.map +1 -1
- package/dist/src/data-structures/command-stack/index.d.ts.map +1 -1
- package/dist/src/data-structures/dependency-graph/dependency-graph.d.ts.map +1 -1
- package/dist/src/data-structures/dependency-graph/dependency-graph.interface.d.ts.map +1 -1
- package/dist/src/data-structures/dependency-graph/index.d.ts.map +1 -1
- package/dist/src/data-structures/disjoint-set/disjoint-set.d.ts.map +1 -1
- package/dist/src/data-structures/disjoint-set/disjoint-set.interface.d.ts.map +1 -1
- package/dist/src/data-structures/disjoint-set/index.d.ts.map +1 -1
- package/dist/src/data-structures/index-map/index-map.d.ts.map +1 -1
- package/dist/src/data-structures/index-map/index-map.interface.d.ts.map +1 -1
- package/dist/src/data-structures/index-map/index.d.ts.map +1 -1
- package/dist/src/data-structures/index.d.ts.map +1 -1
- package/dist/src/data-structures/interval-tree/index.d.ts.map +1 -1
- package/dist/src/data-structures/interval-tree/interval-tree.d.ts.map +1 -1
- package/dist/src/data-structures/interval-tree/interval-tree.interface.d.ts.map +1 -1
- package/dist/src/data-structures/lru-cache/index.d.ts.map +1 -1
- package/dist/src/data-structures/lru-cache/lru-cache.d.ts.map +1 -1
- package/dist/src/data-structures/lru-cache/lru-cache.interface.d.ts.map +1 -1
- package/dist/src/data-structures/prefix-sum-array/index.d.ts.map +1 -1
- package/dist/src/data-structures/prefix-sum-array/prefix-sum-array.d.ts.map +1 -1
- package/dist/src/data-structures/prefix-sum-array/prefix-sum-array.interface.d.ts.map +1 -1
- package/dist/src/data-structures/rtree/index.d.ts.map +1 -1
- package/dist/src/data-structures/rtree/rtree.d.ts.map +1 -1
- package/dist/src/data-structures/rtree/rtree.interface.d.ts.map +1 -1
- package/dist/src/data-structures/segment-tree/index.d.ts.map +1 -1
- package/dist/src/data-structures/segment-tree/segment-tree.d.ts.map +1 -1
- package/dist/src/data-structures/segment-tree/segment-tree.interface.d.ts.map +1 -1
- package/dist/src/data-structures/skip-list/index.d.ts.map +1 -1
- package/dist/src/data-structures/skip-list/skip-list.d.ts.map +1 -1
- package/dist/src/data-structures/skip-list/skip-list.interface.d.ts.map +1 -1
- package/dist/src/data-structures/sparse-matrix/index.d.ts.map +1 -1
- package/dist/src/data-structures/sparse-matrix/sparse-matrix.d.ts.map +1 -1
- package/dist/src/data-structures/sparse-matrix/sparse-matrix.interface.d.ts.map +1 -1
- package/dist/src/data-structures/suffix-array/index.d.ts.map +1 -1
- package/dist/src/data-structures/suffix-array/suffix-array.d.ts.map +1 -1
- package/dist/src/data-structures/suffix-array/suffix-array.interface.d.ts.map +1 -1
- package/dist/src/data-structures/trie/index.d.ts.map +1 -1
- package/dist/src/data-structures/trie/trie.d.ts.map +1 -1
- package/dist/src/data-structures/trie/trie.interface.d.ts.map +1 -1
- package/dist/src/index.d.ts +7 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/patterns/coordinator.d.ts.map +1 -1
- package/dist/src/patterns/event-emitter.d.ts.map +1 -1
- package/dist/src/patterns/factory.d.ts.map +1 -1
- package/dist/src/patterns/index.d.ts.map +1 -1
- package/dist/src/patterns/mediator.d.ts.map +1 -1
- package/dist/src/patterns/object-pool.d.ts.map +1 -1
- package/dist/src/patterns/observer.d.ts.map +1 -1
- package/dist/src/patterns/operation-mode.d.ts.map +1 -1
- package/dist/src/patterns/state-machine.d.ts.map +1 -1
- package/dist/src/types/common.d.ts.map +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/timing.d.ts.map +1 -1
- package/dist/src/utils/timing.interface.d.ts.map +1 -1
- 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 };
|