Projektstart
This commit is contained in:
563
backend/node_modules/mnemonist/utils/merge.js
generated
vendored
Normal file
563
backend/node_modules/mnemonist/utils/merge.js
generated
vendored
Normal file
@@ -0,0 +1,563 @@
|
||||
/* eslint no-constant-condition: 0 */
|
||||
/**
|
||||
* Mnemonist Merge Helpers
|
||||
* ========================
|
||||
*
|
||||
* Various merge algorithms used to handle sorted lists. Note that the given
|
||||
* functions are optimized and won't accept mixed arguments.
|
||||
*
|
||||
* Note: maybe this piece of code belong to sortilege, along with binary-search.
|
||||
*/
|
||||
var typed = require('./typed-arrays.js'),
|
||||
isArrayLike = require('./iterables.js').isArrayLike,
|
||||
binarySearch = require('./binary-search.js'),
|
||||
FibonacciHeap = require('../fibonacci-heap.js');
|
||||
|
||||
// TODO: update to use exponential search
|
||||
// TODO: when not knowing final length => should use plain arrays rather than
|
||||
// same type as input
|
||||
|
||||
/**
|
||||
* Merge two sorted array-like structures into one.
|
||||
*
|
||||
* @param {array} a - First array.
|
||||
* @param {array} b - Second array.
|
||||
* @return {array}
|
||||
*/
|
||||
function mergeArrays(a, b) {
|
||||
|
||||
// One of the arrays is empty
|
||||
if (a.length === 0)
|
||||
return b.slice();
|
||||
if (b.length === 0)
|
||||
return a.slice();
|
||||
|
||||
// Finding min array
|
||||
var tmp;
|
||||
|
||||
if (a[0] > b[0]) {
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
// If array have non overlapping ranges, we can just concatenate them
|
||||
var aEnd = a[a.length - 1],
|
||||
bStart = b[0];
|
||||
|
||||
if (aEnd <= bStart) {
|
||||
if (typed.isTypedArray(a))
|
||||
return typed.concat(a, b);
|
||||
return a.concat(b);
|
||||
}
|
||||
|
||||
// Initializing target
|
||||
var array = new a.constructor(a.length + b.length);
|
||||
|
||||
// Iterating until we overlap
|
||||
var i, l, v;
|
||||
|
||||
for (i = 0, l = a.length; i < l; i++) {
|
||||
v = a[i];
|
||||
|
||||
if (v <= bStart)
|
||||
array[i] = v;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Handling overlap
|
||||
var aPointer = i,
|
||||
aLength = a.length,
|
||||
bPointer = 0,
|
||||
bLength = b.length,
|
||||
aHead,
|
||||
bHead;
|
||||
|
||||
while (aPointer < aLength && bPointer < bLength) {
|
||||
aHead = a[aPointer];
|
||||
bHead = b[bPointer];
|
||||
|
||||
if (aHead <= bHead) {
|
||||
array[i++] = aHead;
|
||||
aPointer++;
|
||||
}
|
||||
else {
|
||||
array[i++] = bHead;
|
||||
bPointer++;
|
||||
}
|
||||
}
|
||||
|
||||
// Filling
|
||||
while (aPointer < aLength)
|
||||
array[i++] = a[aPointer++];
|
||||
while (bPointer < bLength)
|
||||
array[i++] = b[bPointer++];
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the union of two already unique sorted array-like structures into one.
|
||||
*
|
||||
* @param {array} a - First array.
|
||||
* @param {array} b - Second array.
|
||||
* @return {array}
|
||||
*/
|
||||
function unionUniqueArrays(a, b) {
|
||||
|
||||
// One of the arrays is empty
|
||||
if (a.length === 0)
|
||||
return b.slice();
|
||||
if (b.length === 0)
|
||||
return a.slice();
|
||||
|
||||
// Finding min array
|
||||
var tmp;
|
||||
|
||||
if (a[0] > b[0]) {
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
// If array have non overlapping ranges, we can just concatenate them
|
||||
var aEnd = a[a.length - 1],
|
||||
bStart = b[0];
|
||||
|
||||
if (aEnd < bStart) {
|
||||
if (typed.isTypedArray(a))
|
||||
return typed.concat(a, b);
|
||||
return a.concat(b);
|
||||
}
|
||||
|
||||
// Initializing target
|
||||
var array = new a.constructor();
|
||||
|
||||
// Iterating until we overlap
|
||||
var i, l, v;
|
||||
|
||||
for (i = 0, l = a.length; i < l; i++) {
|
||||
v = a[i];
|
||||
|
||||
if (v < bStart)
|
||||
array.push(v);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Handling overlap
|
||||
var aPointer = i,
|
||||
aLength = a.length,
|
||||
bPointer = 0,
|
||||
bLength = b.length,
|
||||
aHead,
|
||||
bHead;
|
||||
|
||||
while (aPointer < aLength && bPointer < bLength) {
|
||||
aHead = a[aPointer];
|
||||
bHead = b[bPointer];
|
||||
|
||||
if (aHead <= bHead) {
|
||||
|
||||
if (array.length === 0 || array[array.length - 1] !== aHead)
|
||||
array.push(aHead);
|
||||
|
||||
aPointer++;
|
||||
}
|
||||
else {
|
||||
if (array.length === 0 || array[array.length - 1] !== bHead)
|
||||
array.push(bHead);
|
||||
|
||||
bPointer++;
|
||||
}
|
||||
}
|
||||
|
||||
// Filling
|
||||
// TODO: it's possible to optimize a bit here, since the condition is only
|
||||
// relevant the first time
|
||||
while (aPointer < aLength) {
|
||||
aHead = a[aPointer++];
|
||||
|
||||
if (array.length === 0 || array[array.length - 1] !== aHead)
|
||||
array.push(aHead);
|
||||
}
|
||||
while (bPointer < bLength) {
|
||||
bHead = b[bPointer++];
|
||||
|
||||
if (array.length === 0 || array[array.length - 1] !== bHead)
|
||||
array.push(bHead);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the intersection of two already unique sorted array-like structures into one.
|
||||
*
|
||||
* @param {array} a - First array.
|
||||
* @param {array} b - Second array.
|
||||
* @return {array}
|
||||
*/
|
||||
exports.intersectionUniqueArrays = function(a, b) {
|
||||
|
||||
// One of the arrays is empty
|
||||
if (a.length === 0 || b.length === 0)
|
||||
return new a.constructor(0);
|
||||
|
||||
// Finding min array
|
||||
var tmp;
|
||||
|
||||
if (a[0] > b[0]) {
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
// If array have non overlapping ranges, there is no intersection
|
||||
var aEnd = a[a.length - 1],
|
||||
bStart = b[0];
|
||||
|
||||
if (aEnd < bStart)
|
||||
return new a.constructor(0);
|
||||
|
||||
// Initializing target
|
||||
var array = new a.constructor();
|
||||
|
||||
// Handling overlap
|
||||
var aPointer = binarySearch.lowerBound(a, bStart),
|
||||
aLength = a.length,
|
||||
bPointer = 0,
|
||||
bLength = binarySearch.upperBound(b, aEnd),
|
||||
aHead,
|
||||
bHead;
|
||||
|
||||
while (aPointer < aLength && bPointer < bLength) {
|
||||
aHead = a[aPointer];
|
||||
bHead = b[bPointer];
|
||||
|
||||
if (aHead < bHead) {
|
||||
aPointer = binarySearch.lowerBound(a, bHead, aPointer + 1);
|
||||
}
|
||||
else if (aHead > bHead) {
|
||||
bPointer = binarySearch.lowerBound(b, aHead, bPointer + 1);
|
||||
}
|
||||
else {
|
||||
array.push(aHead);
|
||||
aPointer++;
|
||||
bPointer++;
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
/**
|
||||
* Merge k sorted array-like structures into one.
|
||||
*
|
||||
* @param {array<array>} arrays - Arrays to merge.
|
||||
* @return {array}
|
||||
*/
|
||||
function kWayMergeArrays(arrays) {
|
||||
var length = 0,
|
||||
max = -Infinity,
|
||||
al,
|
||||
i,
|
||||
l;
|
||||
|
||||
var filtered = [];
|
||||
|
||||
for (i = 0, l = arrays.length; i < l; i++) {
|
||||
al = arrays[i].length;
|
||||
|
||||
if (al === 0)
|
||||
continue;
|
||||
|
||||
filtered.push(arrays[i]);
|
||||
|
||||
length += al;
|
||||
|
||||
if (al > max)
|
||||
max = al;
|
||||
}
|
||||
|
||||
if (filtered.length === 0)
|
||||
return new arrays[0].constructor(0);
|
||||
|
||||
if (filtered.length === 1)
|
||||
return filtered[0].slice();
|
||||
|
||||
if (filtered.length === 2)
|
||||
return mergeArrays(filtered[0], filtered[1]);
|
||||
|
||||
arrays = filtered;
|
||||
|
||||
var array = new arrays[0].constructor(length);
|
||||
|
||||
var PointerArray = typed.getPointerArray(max);
|
||||
|
||||
var pointers = new PointerArray(arrays.length);
|
||||
|
||||
// TODO: benchmark vs. a binomial heap
|
||||
var heap = new FibonacciHeap(function(a, b) {
|
||||
a = arrays[a][pointers[a]];
|
||||
b = arrays[b][pointers[b]];
|
||||
|
||||
if (a < b)
|
||||
return -1;
|
||||
|
||||
if (a > b)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
heap.push(i);
|
||||
|
||||
i = 0;
|
||||
|
||||
var p,
|
||||
v;
|
||||
|
||||
while (heap.size) {
|
||||
p = heap.pop();
|
||||
v = arrays[p][pointers[p]++];
|
||||
array[i++] = v;
|
||||
|
||||
if (pointers[p] < arrays[p].length)
|
||||
heap.push(p);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the union of k sorted unique array-like structures into one.
|
||||
*
|
||||
* @param {array<array>} arrays - Arrays to merge.
|
||||
* @return {array}
|
||||
*/
|
||||
function kWayUnionUniqueArrays(arrays) {
|
||||
var max = -Infinity,
|
||||
al,
|
||||
i,
|
||||
l;
|
||||
|
||||
var filtered = [];
|
||||
|
||||
for (i = 0, l = arrays.length; i < l; i++) {
|
||||
al = arrays[i].length;
|
||||
|
||||
if (al === 0)
|
||||
continue;
|
||||
|
||||
filtered.push(arrays[i]);
|
||||
|
||||
if (al > max)
|
||||
max = al;
|
||||
}
|
||||
|
||||
if (filtered.length === 0)
|
||||
return new arrays[0].constructor(0);
|
||||
|
||||
if (filtered.length === 1)
|
||||
return filtered[0].slice();
|
||||
|
||||
if (filtered.length === 2)
|
||||
return unionUniqueArrays(filtered[0], filtered[1]);
|
||||
|
||||
arrays = filtered;
|
||||
|
||||
var array = new arrays[0].constructor();
|
||||
|
||||
var PointerArray = typed.getPointerArray(max);
|
||||
|
||||
var pointers = new PointerArray(arrays.length);
|
||||
|
||||
// TODO: benchmark vs. a binomial heap
|
||||
var heap = new FibonacciHeap(function(a, b) {
|
||||
a = arrays[a][pointers[a]];
|
||||
b = arrays[b][pointers[b]];
|
||||
|
||||
if (a < b)
|
||||
return -1;
|
||||
|
||||
if (a > b)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
heap.push(i);
|
||||
|
||||
var p,
|
||||
v;
|
||||
|
||||
while (heap.size) {
|
||||
p = heap.pop();
|
||||
v = arrays[p][pointers[p]++];
|
||||
|
||||
if (array.length === 0 || array[array.length - 1] !== v)
|
||||
array.push(v);
|
||||
|
||||
if (pointers[p] < arrays[p].length)
|
||||
heap.push(p);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the intersection of k sorted array-like structures into one.
|
||||
*
|
||||
* @param {array<array>} arrays - Arrays to merge.
|
||||
* @return {array}
|
||||
*/
|
||||
exports.kWayIntersectionUniqueArrays = function(arrays) {
|
||||
var max = -Infinity,
|
||||
maxStart = -Infinity,
|
||||
minEnd = Infinity,
|
||||
first,
|
||||
last,
|
||||
al,
|
||||
i,
|
||||
l;
|
||||
|
||||
for (i = 0, l = arrays.length; i < l; i++) {
|
||||
al = arrays[i].length;
|
||||
|
||||
// If one of the arrays is empty, so is the intersection
|
||||
if (al === 0)
|
||||
return [];
|
||||
|
||||
if (al > max)
|
||||
max = al;
|
||||
|
||||
first = arrays[i][0];
|
||||
last = arrays[i][al - 1];
|
||||
|
||||
if (first > maxStart)
|
||||
maxStart = first;
|
||||
|
||||
if (last < minEnd)
|
||||
minEnd = last;
|
||||
}
|
||||
|
||||
// Full overlap is impossible
|
||||
if (maxStart > minEnd)
|
||||
return [];
|
||||
|
||||
// Only one value
|
||||
if (maxStart === minEnd)
|
||||
return [maxStart];
|
||||
|
||||
// NOTE: trying to outsmart I(D,I(C,I(A,B))) is pointless unfortunately...
|
||||
// NOTE: I tried to be very clever about bounds but it does not seem
|
||||
// to improve the performance of the algorithm.
|
||||
var a, b,
|
||||
array = arrays[0],
|
||||
aPointer,
|
||||
bPointer,
|
||||
aLimit,
|
||||
bLimit,
|
||||
aHead,
|
||||
bHead,
|
||||
start = maxStart;
|
||||
|
||||
for (i = 1; i < l; i++) {
|
||||
a = array;
|
||||
b = arrays[i];
|
||||
|
||||
// Change that to `[]` and observe some perf drops on V8...
|
||||
array = new Array();
|
||||
|
||||
aPointer = 0;
|
||||
bPointer = binarySearch.lowerBound(b, start);
|
||||
|
||||
aLimit = a.length;
|
||||
bLimit = b.length;
|
||||
|
||||
while (aPointer < aLimit && bPointer < bLimit) {
|
||||
aHead = a[aPointer];
|
||||
bHead = b[bPointer];
|
||||
|
||||
if (aHead < bHead) {
|
||||
aPointer = binarySearch.lowerBound(a, bHead, aPointer + 1);
|
||||
}
|
||||
else if (aHead > bHead) {
|
||||
bPointer = binarySearch.lowerBound(b, aHead, bPointer + 1);
|
||||
}
|
||||
else {
|
||||
array.push(aHead);
|
||||
aPointer++;
|
||||
bPointer++;
|
||||
}
|
||||
}
|
||||
|
||||
if (array.length === 0)
|
||||
return array;
|
||||
|
||||
start = array[0];
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variadic merging all of the given arrays.
|
||||
*
|
||||
* @param {...array}
|
||||
* @return {array}
|
||||
*/
|
||||
exports.merge = function() {
|
||||
if (arguments.length === 2) {
|
||||
if (isArrayLike(arguments[0]))
|
||||
return mergeArrays(arguments[0], arguments[1]);
|
||||
}
|
||||
else {
|
||||
if (isArrayLike(arguments[0]))
|
||||
return kWayMergeArrays(arguments);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variadic function performing the union of all the given unique arrays.
|
||||
*
|
||||
* @param {...array}
|
||||
* @return {array}
|
||||
*/
|
||||
exports.unionUnique = function() {
|
||||
if (arguments.length === 2) {
|
||||
if (isArrayLike(arguments[0]))
|
||||
return unionUniqueArrays(arguments[0], arguments[1]);
|
||||
}
|
||||
else {
|
||||
if (isArrayLike(arguments[0]))
|
||||
return kWayUnionUniqueArrays(arguments);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variadic function performing the intersection of all the given unique arrays.
|
||||
*
|
||||
* @param {...array}
|
||||
* @return {array}
|
||||
*/
|
||||
exports.intersectionUnique = function() {
|
||||
if (arguments.length === 2) {
|
||||
if (isArrayLike(arguments[0]))
|
||||
return exports.intersectionUniqueArrays(arguments[0], arguments[1]);
|
||||
}
|
||||
else {
|
||||
if (isArrayLike(arguments[0]))
|
||||
return exports.kWayIntersectionUniqueArrays(arguments);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
Reference in New Issue
Block a user