Skip to content
Snippets Groups Projects
Commit 156b14e3 authored by David Sehnal's avatar David Sehnal
Browse files

moved some code from ordered set to sorted array

parent 6b79fa85
No related branches found
No related tags found
No related merge requests found
...@@ -46,7 +46,7 @@ export function areIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) { ...@@ -46,7 +46,7 @@ export function areIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
if (I.is(b)) return I.areIntersecting(a, b); if (I.is(b)) return I.areIntersecting(a, b);
return areIntersectingSI(b, a); return areIntersectingSI(b, a);
} else if (I.is(b)) return areIntersectingSI(a, b); } else if (I.is(b)) return areIntersectingSI(a, b);
return areIntersectingSS(a, b); return S.areIntersecting(a, b);
} }
/** Check if the 2nd argument is a subset of the 1st */ /** Check if the 2nd argument is a subset of the 1st */
...@@ -55,7 +55,7 @@ export function isSubset(a: OrderedSetImpl, b: OrderedSetImpl) { ...@@ -55,7 +55,7 @@ export function isSubset(a: OrderedSetImpl, b: OrderedSetImpl) {
if (I.is(b)) return I.isSubInterval(a, b); if (I.is(b)) return I.isSubInterval(a, b);
return isSubsetIS(a, b); return isSubsetIS(a, b);
} else if (I.is(b)) return isSubsetSI(a, b); } else if (I.is(b)) return isSubsetSI(a, b);
return isSubsetSS(a, b); return S.isSubset(a, b);
} }
export function findPredecessorIndex(set: OrderedSetImpl, x: number) { export function findPredecessorIndex(set: OrderedSetImpl, x: number) {
...@@ -75,7 +75,7 @@ export function union(a: OrderedSetImpl, b: OrderedSetImpl) { ...@@ -75,7 +75,7 @@ export function union(a: OrderedSetImpl, b: OrderedSetImpl) {
if (I.is(b)) return unionII(a, b); if (I.is(b)) return unionII(a, b);
return unionSI(b, a); return unionSI(b, a);
} else if (I.is(b)) return unionSI(a, b); } else if (I.is(b)) return unionSI(a, b);
return unionSS(a, b); return ofSortedArray(S.union(a, b));
} }
export function intersect(a: OrderedSetImpl, b: OrderedSetImpl) { export function intersect(a: OrderedSetImpl, b: OrderedSetImpl) {
...@@ -83,7 +83,7 @@ export function intersect(a: OrderedSetImpl, b: OrderedSetImpl) { ...@@ -83,7 +83,7 @@ export function intersect(a: OrderedSetImpl, b: OrderedSetImpl) {
if (I.is(b)) return I.intersect(a, b); if (I.is(b)) return I.intersect(a, b);
return intersectSI(b, a); return intersectSI(b, a);
} else if (I.is(b)) return intersectSI(a, b); } else if (I.is(b)) return intersectSI(a, b);
return intersectSS(a, b); return ofSortedArray(S.intersect(a, b));
} }
export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) { export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) {
...@@ -91,26 +91,7 @@ export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) { ...@@ -91,26 +91,7 @@ export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) {
if (I.is(b)) return subtractII(a, b); if (I.is(b)) return subtractII(a, b);
return subtractIS(a, b); return subtractIS(a, b);
} else if (I.is(b)) return subtractSI(a, b); } else if (I.is(b)) return subtractSI(a, b);
return subtractSS(a, b); return ofSortedArray(S.subtract(a, b));
}
const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 };
// for small sets, just gets the whole range, for large sets does a bunch of binary searches
function getSuitableIntersectionRange(a: S, b: S) {
const la = a.length, lb = b.length;
const ratio = la / lb;
if (ratio <= 0.5 || ratio >= 2 || (la >= 128 && lb >= 128)) {
_maxIntRangeRet.startI = S.findPredecessorIndex(a, S.start(b));
_maxIntRangeRet.startJ = S.findPredecessorIndex(b, S.start(a));
_maxIntRangeRet.endI = S.findPredecessorIndex(a, S.end(b));
_maxIntRangeRet.endJ = S.findPredecessorIndex(b, S.end(a));
} else {
_maxIntRangeRet.startI = 0;
_maxIntRangeRet.startJ = 0;
_maxIntRangeRet.endI = la;
_maxIntRangeRet.endJ = lb;
}
return _maxIntRangeRet;
} }
function areEqualIS(a: I, b: S) { return I.size(a) === S.size(b) && I.start(a) === S.start(b) && I.end(a) === S.end(b); } function areEqualIS(a: I, b: S) { return I.size(a) === S.size(b) && I.start(a) === S.start(b) && I.end(a) === S.end(b); }
...@@ -119,19 +100,6 @@ function areIntersectingSI(a: S, b: I) { ...@@ -119,19 +100,6 @@ function areIntersectingSI(a: S, b: I) {
return areRangesIntersecting(a, b); return areRangesIntersecting(a, b);
} }
function areIntersectingSS(a: S, b: S) {
if (a === b) return true;
let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b);
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else return true;
}
return false;
}
function isSubsetSI(a: S, b: I) { function isSubsetSI(a: S, b: I) {
const minB = I.min(b), maxB = I.max(b); const minB = I.min(b), maxB = I.max(b);
if (maxB - minB + 1 === 0) return true; if (maxB - minB + 1 === 0) return true;
...@@ -148,24 +116,6 @@ function isSubsetIS(a: I, b: S) { ...@@ -148,24 +116,6 @@ function isSubsetIS(a: I, b: S) {
return minB >= minA && maxA <= maxB; return minB >= minA && maxA <= maxB;
} }
function isSubsetSS(a: S, b: S) {
if (a === b) return true;
const lenB = b.length;
let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b);
// must be able to advance by lenB elements
if (endJ - j < lenB || endI - i < lenB) return false;
let equal = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; equal++; }
}
return equal === lenB;
}
function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) { function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
const sa = size(a), sb = size(b); const sa = size(a), sb = size(b);
if (sa === 0 && sb === 0) return true; if (sa === 0 && sb === 0) return true;
...@@ -213,56 +163,6 @@ function unionSI(a: S, b: I) { ...@@ -213,56 +163,6 @@ function unionSI(a: S, b: I) {
return ofSortedArray(indices); return ofSortedArray(indices);
} }
function unionSS(a: S, b: S) {
if (a === b) return a;
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;
let commonCount = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; commonCount++; }
}
const lenA = a.length, lenB = b.length;
// A === B || B is subset of A ==> A
if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return a;
// A is subset of B ===> B
if (commonCount === lenA) return b;
const resultSize = lenA + lenB - commonCount;
const l = Math.min(a[0], b[0]), r = Math.max(a[lenA - 1], b[lenB - 1]);
// is this just a range?
if (resultSize === r - l + 1) {
return I.ofRange(l, r);
}
const indices = new Int32Array(lenA + lenB - commonCount);
let offset = 0;
// insert the "prefixes"
for (let k = 0; k < sI; k++) indices[offset++] = a[k];
for (let k = 0; k < sJ; k++) indices[offset++] = b[k];
// insert the common part
i = sI;
j = sJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { indices[offset++] = x; i++; }
else if (x > y) { indices[offset++] = y; j++; }
else { indices[offset++] = x; i++; j++; }
}
// insert the "tail"
for (; i < lenA; i++) indices[offset++] = a[i];
for (; j < lenB; j++) indices[offset++] = b[j];
return ofSortedArray(indices);
}
function intersectSI(a: S, b: I) { function intersectSI(a: S, b: I) {
if (!I.size(b)) return Empty; if (!I.size(b)) return Empty;
...@@ -279,41 +179,6 @@ function intersectSI(a: S, b: I) { ...@@ -279,41 +179,6 @@ function intersectSI(a: S, b: I) {
return ofSortedArray(indices); return ofSortedArray(indices);
} }
function intersectSS(a: S, b: S) {
if (a === b) return a;
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;
let commonCount = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; commonCount++; }
}
const lenA = a.length, lenB = b.length;
// no common elements
if (!commonCount) return Empty;
// A === B || B is subset of A ==> B
if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return b;
// A is subset of B ==> A
if (commonCount === lenA) return a;
const indices = new Int32Array(commonCount);
let offset = 0;
i = sI;
j = sJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { indices[offset++] = x; i++; j++; }
}
return ofSortedArray(indices);
}
function subtractII(a: I, b: I) { function subtractII(a: I, b: I) {
if (I.areEqual(a, b)) return Empty; if (I.areEqual(a, b)) return Empty;
if (!I.areIntersecting(a, b)) return a; if (!I.areIntersecting(a, b)) return a;
...@@ -387,44 +252,4 @@ function subtractIS(a: I, b: S) { ...@@ -387,44 +252,4 @@ function subtractIS(a: I, b: S) {
} }
for (let i = last + 1; i <= max; i++) ret[offset++] = i; for (let i = last + 1; i <= max; i++) ret[offset++] = i;
return ofSortedArray(ret); return ofSortedArray(ret);
}
function subtractSS(a: S, b: S) {
if (a === b) return Empty;
const lenA = a.length;
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;
let commonCount = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; commonCount++; }
}
// A isnt intersecting B ===> A
if (!commonCount) return a;
// A === B || A is subset of B ===> Empty
if (commonCount >= lenA) return Empty;
const indices = new Int32Array(lenA - commonCount);
let offset = 0;
// insert the "prefix"
for (let k = 0; k < sI; k++) indices[offset++] = a[k];
i = sI;
j = sJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { indices[offset++] = x; i++; }
else if (x > y) { j++; }
else { i++; j++; }
}
// insert the "tail"
for (; i < lenA; i++) indices[offset++] = a[i];
return ofSortedArray(indices);
} }
\ No newline at end of file
...@@ -10,6 +10,9 @@ import Interval from '../interval' ...@@ -10,6 +10,9 @@ import Interval from '../interval'
type Nums = ArrayLike<number> type Nums = ArrayLike<number>
export const Empty: Nums = []
export function ofSortedArray(xs: Nums) { export function ofSortedArray(xs: Nums) {
if (xs.length < 1) throw new Error('Sorted arrays must be non-empty.'); if (xs.length < 1) throw new Error('Sorted arrays must be non-empty.');
return xs; return xs;
...@@ -101,4 +104,172 @@ function binarySearchPredIndexRange(xs: Nums, value: number, start: number, end: ...@@ -101,4 +104,172 @@ function binarySearchPredIndexRange(xs: Nums, value: number, start: number, end:
} }
if (min > max) return max + 1; if (min > max) return max + 1;
return xs[min] >= value ? min : min + 1; return xs[min] >= value ? min : min + 1;
}
export function areIntersecting(a: Nums, b: Nums) {
if (a === b) return true;
let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b);
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else return true;
}
return false;
}
export function isSubset(a: Nums, b: Nums) {
if (a === b) return true;
const lenB = b.length;
let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b);
// must be able to advance by lenB elements
if (endJ - j < lenB || endI - i < lenB) return false;
let equal = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; equal++; }
}
return equal === lenB;
}
export function union(a: Nums, b: Nums) {
if (a === b) return a;
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;
let commonCount = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; commonCount++; }
}
const lenA = a.length, lenB = b.length;
// A === B || B is subset of A ==> A
if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return a;
// A is subset of B ===> B
if (commonCount === lenA) return b;
const indices = new Int32Array(lenA + lenB - commonCount);
let offset = 0;
// insert the "prefixes"
for (let k = 0; k < sI; k++) indices[offset++] = a[k];
for (let k = 0; k < sJ; k++) indices[offset++] = b[k];
// insert the common part
i = sI;
j = sJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { indices[offset++] = x; i++; }
else if (x > y) { indices[offset++] = y; j++; }
else { indices[offset++] = x; i++; j++; }
}
// insert the "tail"
for (; i < lenA; i++) indices[offset++] = a[i];
for (; j < lenB; j++) indices[offset++] = b[j];
return ofSortedArray(indices);
}
export function intersect(a: Nums, b: Nums) {
if (a === b) return a;
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;
let commonCount = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; commonCount++; }
}
const lenA = a.length, lenB = b.length;
// no common elements
if (!commonCount) return Empty;
// A === B || B is subset of A ==> B
if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return b;
// A is subset of B ==> A
if (commonCount === lenA) return a;
const indices = new Int32Array(commonCount);
let offset = 0;
i = sI;
j = sJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { indices[offset++] = x; i++; j++; }
}
return ofSortedArray(indices);
}
export function subtract(a: Nums, b: Nums) {
if (a === b) return Empty;
const lenA = a.length;
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;
let commonCount = 0;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { i++; }
else if (x > y) { j++; }
else { i++; j++; commonCount++; }
}
// A isnt intersecting B ===> A
if (!commonCount) return a;
// A === B || A is subset of B ===> Empty
if (commonCount >= lenA) return Empty;
const indices = new Int32Array(lenA - commonCount);
let offset = 0;
// insert the "prefix"
for (let k = 0; k < sI; k++) indices[offset++] = a[k];
i = sI;
j = sJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { indices[offset++] = x; i++; }
else if (x > y) { j++; }
else { i++; j++; }
}
// insert the "tail"
for (; i < lenA; i++) indices[offset++] = a[i];
return ofSortedArray(indices);
}
const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 };
// for small sets, just gets the whole range, for large sets does a bunch of binary searches
export function getSuitableIntersectionRange(a: Nums, b: Nums) {
const la = a.length, lb = b.length;
const ratio = la / lb;
if (ratio <= 0.5 || ratio >= 2 || (la >= 128 && lb >= 128)) {
_maxIntRangeRet.startI = findPredecessorIndex(a, start(b));
_maxIntRangeRet.startJ = findPredecessorIndex(b, start(a));
_maxIntRangeRet.endI = findPredecessorIndex(a, end(b));
_maxIntRangeRet.endJ = findPredecessorIndex(b, end(a));
} else {
_maxIntRangeRet.startI = 0;
_maxIntRangeRet.startJ = 0;
_maxIntRangeRet.endI = la;
_maxIntRangeRet.endJ = lb;
}
return _maxIntRangeRet;
} }
\ No newline at end of file
...@@ -25,12 +25,18 @@ namespace SortedArray { ...@@ -25,12 +25,18 @@ namespace SortedArray {
export const hashCode: (array: SortedArray) => number = Impl.hashCode as any; export const hashCode: (array: SortedArray) => number = Impl.hashCode as any;
export const areEqual: (a: SortedArray, b: SortedArray) => boolean = Impl.areEqual as any; export const areEqual: (a: SortedArray, b: SortedArray) => boolean = Impl.areEqual as any;
export const areIntersecting: (a: SortedArray, b: SortedArray) => boolean = Impl.areIntersecting as any;
export const isSubset: (a: SortedArray, b: SortedArray) => boolean = Impl.isSubset as any;
export const union: (a: SortedArray, b: SortedArray) => SortedArray = Impl.union as any;
export const intersect: (a: SortedArray, b: SortedArray) => SortedArray = Impl.intersect as any;
export const subtract: (a: SortedArray, b: SortedArray) => SortedArray = Impl.subtract as any;
export const findPredecessorIndex: (array: SortedArray, x: number) => number = Impl.findPredecessorIndex as any; export const findPredecessorIndex: (array: SortedArray, x: number) => number = Impl.findPredecessorIndex as any;
export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any; export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any;
export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any; export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any;
} }
interface SortedArray extends ReadonlyArray<number> { '@type': 'int-sorted-array' } interface SortedArray extends ArrayLike<number> { '@type': 'int-sorted-array' }
export default SortedArray export default SortedArray
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment