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

collections

parent 4e2fe6b2
Branches
Tags
No related merge requests found
import * as B from 'benchmark' import * as B from 'benchmark'
import IntPair from '../structure/collections/int-pair' import IntTuple from '../structure/collections/int-tuple'
import OrdSet from '../structure/collections/ordered-set' import OrdSet from '../structure/collections/ordered-set'
import MSet from '../structure/collections/multi-set' import MSet from '../structure/collections/multi-set'
...@@ -33,7 +33,7 @@ namespace Iteration { ...@@ -33,7 +33,7 @@ namespace Iteration {
export function elementAt() { export function elementAt() {
let s = 0; let s = 0;
for (let i = 0, _i = MSet.size(ms); i < _i; i++) s += IntPair.snd(MSet.get(ms, i)); for (let i = 0, _i = MSet.size(ms); i < _i; i++) s += IntTuple.snd(MSet.getAt(ms, i));
return s; return s;
} }
...@@ -41,9 +41,9 @@ namespace Iteration { ...@@ -41,9 +41,9 @@ namespace Iteration {
let s = 0; let s = 0;
const keys = MSet.keys(ms); const keys = MSet.keys(ms);
for (let i = 0, _i = OrdSet.size(keys); i < _i; i++) { for (let i = 0, _i = OrdSet.size(keys); i < _i; i++) {
const set = MSet.getSetByKey(ms, OrdSet.get(keys, i)); const set = MSet.getByKey(ms, OrdSet.getAt(keys, i));
for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
s += OrdSet.get(set, j); s += OrdSet.getAt(set, j);
} }
} }
return s; return s;
...@@ -51,10 +51,10 @@ namespace Iteration { ...@@ -51,10 +51,10 @@ namespace Iteration {
export function manual1() { export function manual1() {
let s = 0; let s = 0;
for (let i = 0, _i = MSet.getSetCount(ms); i < _i; i++) { for (let i = 0, _i = MSet.keyCount(ms); i < _i; i++) {
const set = MSet.getSetByIndex(ms, i); const set = MSet.getByIndex(ms, i);
for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
s += OrdSet.get(set, j); s += OrdSet.getAt(set, j);
} }
} }
return s; return s;
......
...@@ -44,3 +44,11 @@ export function hash4(i: number, j: number, k: number, l: number) { ...@@ -44,3 +44,11 @@ export function hash4(i: number, j: number, k: number, l: number) {
a = a ^ (a >> 11); a = a ^ (a >> 11);
return a; return a;
} }
/**
* A unique number for each pair of integers
* Biggest representable pair is (67108863, 67108863) (limit imposed by Number.MAX_SAFE_INTEGER)
*/
export function cantorPairing(a: number, b: number) {
return (a + b) * (a + b + 1) / 2 + b;
}
\ No newline at end of file
...@@ -6,10 +6,14 @@ ...@@ -6,10 +6,14 @@
import { hash2 } from './hash-functions' import { hash2 } from './hash-functions'
interface IntPair { fst: number, snd: number } /**
* Represents a pair of two integers as a double,
* Caution: === does not work, because of NaN, use IntTuple.areEqual for equality
*/
interface IntTuple extends Number { }
namespace IntPair { namespace IntTuple {
export interface Packed extends Number { } export interface Unpacked { fst: number, snd: number }
const { _int32, _float64, _int32_1, _float64_1 } = (function() { const { _int32, _float64, _int32_1, _float64_1 } = (function() {
const data = new ArrayBuffer(8); const data = new ArrayBuffer(8);
...@@ -22,72 +26,73 @@ namespace IntPair { ...@@ -22,72 +26,73 @@ namespace IntPair {
}; };
}()); }());
export function is(x: any): x is IntPair { export function is(x: any): x is Unpacked {
return !!x && typeof x.fst === 'number' && typeof x.snd === 'number'; return !!x && typeof x.fst === 'number' && typeof x.snd === 'number';
} }
export function create(fst: number, snd: number) { return { fst, snd }; } export function create(fst: number, snd: number) { return { fst, snd }; }
export function zero(): IntPair { return { fst: 0, snd: 0 }; } export function zero(): Unpacked { return { fst: 0, snd: 0 }; }
export function pack1(fst: number, snd: number): number { export function pack(fst: number, snd: number): IntTuple {
_int32[0] = fst; _int32[0] = fst;
_int32[1] = snd; _int32[1] = snd;
return _float64[0]; return _float64[0];
} }
export function pack(p: IntPair): number { export function pack1(t: Unpacked): IntTuple {
_int32[0] = p.fst; _int32[0] = t.fst;
_int32[1] = p.snd; _int32[1] = t.snd;
return _float64[0]; return _float64[0];
} }
export function unpack(packed: Packed, target: IntPair): IntPair { export function unpack(t: IntTuple, target: Unpacked): Unpacked {
_float64[0] = packed as number; _float64[0] = t as number;
target.fst = _int32[0]; target.fst = _int32[0];
target.snd = _int32[1]; target.snd = _int32[1];
return target; return target;
} }
export function unpack1(packed: Packed): IntPair { export function unpack1(packed: IntTuple): Unpacked {
return unpack(packed, zero()); return unpack(packed, zero());
} }
export function fst(packed: Packed): number { export function fst(t: IntTuple): number {
_float64[0] = packed as number; _float64[0] = t as number;
return _int32[0]; return _int32[0];
} }
export function snd(packed: Packed): number { export function snd(t: IntTuple): number {
_float64[0] = packed as number; _float64[0] = t as number;
return _int32[1]; return _int32[1];
} }
export function areEqual(a: Packed, b: Packed) { /** Normal equality does not work, because NaN === NaN ~> false */
export function areEqual(a: IntTuple, b: IntTuple) {
_float64[0] = a as number; _float64[0] = a as number;
_float64_1[0] = b as number; _float64_1[0] = b as number;
return _int32[0] === _int32_1[0] && _int32[1] === _int32_1[1]; return _int32[0] === _int32_1[0] && _int32[1] === _int32_1[1];
} }
export function compare(a: number, b: number) { export function compare(a: IntTuple, b: IntTuple) {
_float64[0] = a; _float64[0] = a as number;
_float64_1[0] = b; _float64_1[0] = b as number;
const x = _int32[0] - _int32_1[0]; const x = _int32[0] - _int32_1[0];
if (x !== 0) return x; if (x !== 0) return x;
return _int32[1] - _int32_1[1]; return _int32[1] - _int32_1[1];
} }
export function compareInArray(xs: ArrayLike<number>, i: number, j: number) { export function compareInArray(xs: ArrayLike<IntTuple>, i: number, j: number) {
_float64[0] = xs[i]; _float64[0] = xs[i] as number;
_float64_1[0] = xs[j]; _float64_1[0] = xs[j] as number;
const x = _int32[0] - _int32_1[0]; const x = _int32[0] - _int32_1[0];
if (x !== 0) return x; if (x !== 0) return x;
return _int32[1] - _int32_1[1]; return _int32[1] - _int32_1[1];
} }
export function packedHashCode(packed: Packed) { export function hashCode(t: IntTuple) {
_float64[0] = packed as number; _float64[0] = t as number;
return hash2(_int32[0], _int32[1]); return hash2(_int32[0], _int32[1]);
} }
} }
export default IntPair export default IntTuple
\ No newline at end of file \ No newline at end of file
This diff is collapsed.
...@@ -4,51 +4,80 @@ ...@@ -4,51 +4,80 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import IntPair from './int-pair' import IntTuple from './int-tuple'
import { hash3, hash4 } from './hash-functions' import { hash3, hash4 } from './hash-functions'
type Nums = ArrayLike<number>
type OrderedSet = number | Nums
/** An immutable ordered set. */ /** An immutable ordered set. */
interface OrderedSet { '@type': 'int-ordered-set' }
namespace OrderedSet { namespace OrderedSet {
export function ofSingleton(value: number): OrderedSet { return IntPair.pack1(value, value); } export const Empty: OrderedSet = IntTuple.pack(0, -1) as any;
export function ofRange(min: number, max: number): OrderedSet { return max < min ? Empty : IntPair.pack1(min, max); } export function ofSingleton(value: number): OrderedSet { return IntTuple.pack(value, value) as any; }
export function ofRange(min: number, max: number): OrderedSet { return max < min ? Empty : IntTuple.pack(min, max) as any; }
/** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */ /** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */
export function ofSortedArray(xs: Nums): OrderedSet { export function ofSortedArray(xs: SortedArray): OrderedSet {
if (!xs.length) return Empty; if (!xs.length) return Empty;
// check if the array is just a range // check if the array is just a range
if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return ofRange(xs[0], xs[xs.length - 1]); if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return ofRange(xs[0], xs[xs.length - 1]);
return xs; return xs as any;
}
export const size: (set: OrderedSet) => number = sizeI as any;
export const has: (set: OrderedSet, x: number) => boolean = hasI as any;
export const indexOf: (set: OrderedSet, x: number) => number = indexOfI as any;
export const getAt: (set: OrderedSet, i: number) => number = getAtI as any;
export const min: (set: OrderedSet) => number = minI as any;
export const max: (set: OrderedSet) => number = maxI as any;
export const hashCode: (set: OrderedSet) => number = hashCodeI as any;
export const areEqual: BinaryTest = areEqualI as any;
export const areIntersecting: BinaryTest = areIntersectingI as any;
export const isSubset: BinaryTest = isSubsetI as any;
export const union: BinaryOp = unionI as any;
export const intersect: BinaryOp = intersectI as any;
export const subtract: BinaryOp = subtractI as any;
export const getInsertionIndex: (set: OrderedSet, x: number) => number = getInsertionIndexI as any;
export const getIntervalRange: (set: OrderedSet, min: number, max: number) => { start: number, end: number } = getIntervalRangeI as any;
} }
export const Empty: OrderedSet = IntPair.pack1(0, -1);
export function size(set: OrderedSet) { return typeof set === 'number' ? sizeR(set) : set.length; } export default OrderedSet
export function has(set: OrderedSet, x: number) { return typeof set === 'number' ? hasR(set, x) : hasA(set, x); }
export function indexOf(set: OrderedSet, x: number) { return typeof set === 'number' ? indexOfR(set, x) : indexOfA(set, x); } /** Long and painful implementation starts here */
export function get(set: OrderedSet, i: number) { return typeof set === 'number' ? elementAtR(set, i) : set[i]; }
export function min(set: OrderedSet) { return typeof set === 'number' ? minR(set) : set[0]; } type BinaryTest = (a: OrderedSet, b: OrderedSet) => boolean
export function max(set: OrderedSet) { return typeof set === 'number' ? maxR(set) : set[set.length - 1]; } type BinaryOp = (a: OrderedSet, b: OrderedSet) => OrderedSet
type Range = IntTuple
type SortedArray = ArrayLike<number>
type OrderedSetImpl = Range | SortedArray
function sizeI(set: OrderedSetImpl) { return typeof set === 'number' ? sizeR(set) : (set as SortedArray).length; }
function hasI(set: OrderedSetImpl, x: number) { return typeof set === 'number' ? hasR(set, x) : hasA(set as SortedArray, x); }
function indexOfI(set: OrderedSetImpl, x: number) { return typeof set === 'number' ? indexOfR(set, x) : indexOfA(set as SortedArray, x); }
function getAtI(set: OrderedSetImpl, i: number) { return typeof set === 'number' ? elementAtR(set, i) : (set as SortedArray)[i]; }
function minI(set: OrderedSetImpl) { return typeof set === 'number' ? minR(set) : (set as SortedArray)[0]; }
function maxI(set: OrderedSetImpl) { return typeof set === 'number' ? maxR(set) : (set as SortedArray)[(set as SortedArray).length - 1]; }
export function hashCode(set: OrderedSet) { function hashCodeI(set: OrderedSetImpl) {
// hash of tuple (size, min, max, mid) // hash of tuple (size, min, max, mid)
const s = size(set); const s = sizeI(set);
if (!s) return 0; if (!s) return 0;
if (s > 2) return hash4(s, get(set, 0), get(set, s - 1), get(set, s >> 1)); if (s > 2) return hash4(s, getAtI(set, 0), getAtI(set, s - 1), getAtI(set, s >> 1));
return hash3(s, get(set, 0), get(set, s - 1)); return hash3(s, getAtI(set, 0), getAtI(set, s - 1));
} }
// TODO: possibly add more hash functions to allow for multilevel hashing. // TODO: possibly add more hash functions to allow for multilevel hashing.
export function areEqual(a: OrderedSet, b: OrderedSet) { function areEqualI(a: OrderedSetImpl, b: OrderedSetImpl) {
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return equalRR(a, b); if (typeof b === 'number') return equalRR(a, b);
return false; return false;
} else if (typeof b === 'number') return false; } else if (typeof b === 'number') return false;
else if (a === b) return true; return equalAA(a as SortedArray, b as SortedArray);
return equalAA(a, b);
} }
export function areIntersecting(a: OrderedSet, b: OrderedSet) { function areIntersectingI(a: OrderedSetImpl, b: OrderedSetImpl) {
// if at least one is "range", they must now intersect // if at least one is "range", they must now intersect
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return equalRR(a, b) || areRangesIntersecting(a, b); if (typeof b === 'number') return equalRR(a, b) || areRangesIntersecting(a, b);
...@@ -56,83 +85,75 @@ namespace OrderedSet { ...@@ -56,83 +85,75 @@ namespace OrderedSet {
} }
if (!areRangesIntersecting(a, b)) return false; if (!areRangesIntersecting(a, b)) return false;
else if (typeof b === 'number') return false; else if (typeof b === 'number') return false;
if (a === b) return true; return areIntersectingAA(a as SortedArray, b as SortedArray);
return areIntersectingAA(a, b);
} }
/** Check if the 2nd argument is a subset of the 1st */ /** Check if the 2nd argument is a subset of the 1st */
export function isSubset(set: OrderedSet, toTest: OrderedSet) { function isSubsetI(set: OrderedSetImpl, toTest: OrderedSetImpl) {
if (set === toTest) return true;
if (!isRangeSubset(set, toTest)) return false; if (!isRangeSubset(set, toTest)) return false;
const testSize = size(toTest); const testSize = sizeI(toTest);
if (typeof set === 'number' || !testSize) return true; if (typeof set === 'number' || !testSize) return true;
if (typeof toTest === 'number') return indexOf(set, maxR(toTest)) - indexOf(set, minR(toTest)) + 1 === testSize; if (typeof toTest === 'number') return indexOfI(set, maxR(toTest)) - indexOfI(set, minR(toTest)) + 1 === testSize;
return isSubsetAA(set, toTest); return isSubsetAA(set as SortedArray, toTest as SortedArray);
} }
export function getInsertionIndex(set: OrderedSet, x: number) { function getInsertionIndexI(set: OrderedSetImpl, x: number) {
return typeof set === 'number' ? rangeSearchIndex(set, x) : binarySearchIndex(set, x); return typeof set === 'number' ? rangeSearchIndex(set, x) : binarySearchIndex(set as SortedArray, x);
} }
export function getIntervalRange(set: OrderedSet, min: number, max: number) { function getIntervalRangeI(set: OrderedSetImpl, min: number, max: number) {
const { start, end } = getStartEnd(set, min, max); const { start, end } = getStartEnd(set, min, max);
return { start, end }; return { start, end };
} }
export function union(a: OrderedSet, b: OrderedSet) { function unionI(a: OrderedSetImpl, b: OrderedSetImpl) {
if (a === b) return a;
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return unionRR(a, b); if (typeof b === 'number') return unionRR(a, b);
return unionAR(b, a); return unionAR(b as SortedArray, a);
} else if (typeof b === 'number') { } else if (typeof b === 'number') {
return unionAR(a, b); return unionAR(a as SortedArray, b);
} else return unionAA(a, b); } else return unionAA(a as SortedArray, b as SortedArray);
} }
export function intersect(a: OrderedSet, b: OrderedSet) { function intersectI(a: OrderedSetImpl, b: OrderedSetImpl) {
if (a === b) return a;
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return intersectRR(a, b); if (typeof b === 'number') return intersectRR(a, b);
return intersectAR(b, a); return intersectAR(b as SortedArray, a);
} else if (typeof b === 'number') { } else if (typeof b === 'number') {
return intersectAR(a, b); return intersectAR(a as SortedArray, b);
} else { } else {
if (!areRangesIntersecting(a, b)) return Empty; if (!areRangesIntersecting(a, b)) return OrderedSet.Empty;
return intersectAA(a, b); return intersectAA(a as SortedArray, b as SortedArray);
} }
} }
export function subtract(a: OrderedSet, b: OrderedSet) { function subtractI(a: OrderedSetImpl, b: OrderedSetImpl) {
if (a === b) return Empty;
if (!areRangesIntersecting(a, b)) return a; if (!areRangesIntersecting(a, b)) return a;
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return substractRR(a, b); if (typeof b === 'number') return substractRR(a, b);
return subtractRA(a, b); return subtractRA(a, b as SortedArray);
} else if (typeof b === 'number') { } else if (typeof b === 'number') {
return subtractAR(a, b); return subtractAR(a as SortedArray, b);
} else { } else {
return subtractAA(a, b); return subtractAA(a as SortedArray, b as SortedArray);
} }
} }
}
import S = OrderedSet
const minR = IntPair.fst const minR = IntTuple.fst
const maxR = IntPair.snd const maxR = IntTuple.snd
const equalRR = IntPair.areEqual const equalRR = IntTuple.areEqual
const _eR = IntPair.zero(); const _eR = IntTuple.zero();
function sizeR(set: number) { IntPair.unpack(set, _eR); return _eR.snd - _eR.fst + 1; } function sizeR(set: Range) { IntTuple.unpack(set, _eR); return _eR.snd - _eR.fst + 1; }
function hasR(set: number, x: number) { IntPair.unpack(set, _eR); return x >= _eR.fst && x <= _eR.snd; } function hasR(set: Range, x: number) { IntTuple.unpack(set, _eR); return x >= _eR.fst && x <= _eR.snd; }
function indexOfR(set: number, x: number) { IntPair.unpack(set, _eR); return x >= _eR.fst && x <= _eR.snd ? x - _eR.fst : -1; } function indexOfR(set: Range, x: number) { IntTuple.unpack(set, _eR); return x >= _eR.fst && x <= _eR.snd ? x - _eR.fst : -1; }
function elementAtR(set: number, i: number) { return IntPair.fst(set) + i; } function elementAtR(set: Range, i: number) { return IntTuple.fst(set) + i; }
function hasA(set: Nums, x: number) { return x >= set[0] && x <= set[set.length - 1] && binarySearch(set, x) >= 0; } function hasA(set: SortedArray, x: number) { return x >= set[0] && x <= set[set.length - 1] && binarySearch(set, x) >= 0; }
function indexOfA(set: Nums, x: number) { return x >= set[0] && x <= set[set.length - 1] ? binarySearch(set, x) : -1; } function indexOfA(set: SortedArray, x: number) { return x >= set[0] && x <= set[set.length - 1] ? binarySearch(set, x) : -1; }
function binarySearch(xs: Nums, value: number) { function binarySearch(xs: SortedArray, value: number) {
let min = 0, max = xs.length - 1; let min = 0, max = xs.length - 1;
while (min <= max) { while (min <= max) {
if (min + 11 > max) { if (min + 11 > max) {
...@@ -151,7 +172,7 @@ function binarySearch(xs: Nums, value: number) { ...@@ -151,7 +172,7 @@ function binarySearch(xs: Nums, value: number) {
return -1; return -1;
} }
function binarySearchIndex(xs: Nums, value: number) { function binarySearchIndex(xs: SortedArray, value: number) {
let min = 0, max = xs.length - 1; let min = 0, max = xs.length - 1;
while (min < max) { while (min < max) {
const mid = (min + max) >> 1; const mid = (min + max) >> 1;
...@@ -164,16 +185,16 @@ function binarySearchIndex(xs: Nums, value: number) { ...@@ -164,16 +185,16 @@ function binarySearchIndex(xs: Nums, value: number) {
return xs[min] >= value ? min : min + 1; return xs[min] >= value ? min : min + 1;
} }
const _rsiR = IntPair.zero(); const _rsiR = IntTuple.zero();
function rangeSearchIndex(r: number, value: number) { function rangeSearchIndex(r: Range, value: number) {
IntPair.unpack(r, _rsiR); IntTuple.unpack(r, _rsiR);
if (value < _rsiR.fst) return 0; if (value < _rsiR.fst) return 0;
if (value > _rsiR.snd) return _rsiR.snd - _rsiR.fst + 1; if (value > _rsiR.snd) return _rsiR.snd - _rsiR.fst + 1;
return value - _rsiR.fst; return value - _rsiR.fst;
} }
const _maxIntRangeRet = { i: 0, j: 0, endA: 0, endB: 0 }; const _maxIntRangeRet = { i: 0, j: 0, endA: 0, endB: 0 };
function getMaxIntersectionRange(xs: Nums, ys: Nums) { function getMaxIntersectionRange(xs: SortedArray, ys: SortedArray) {
const la = xs.length - 1, lb = ys.length - 1; const la = xs.length - 1, lb = ys.length - 1;
_maxIntRangeRet.i = binarySearchIndex(xs, ys[0]); _maxIntRangeRet.i = binarySearchIndex(xs, ys[0]);
_maxIntRangeRet.j = binarySearchIndex(ys, xs[0]); _maxIntRangeRet.j = binarySearchIndex(ys, xs[0]);
...@@ -184,15 +205,16 @@ function getMaxIntersectionRange(xs: Nums, ys: Nums) { ...@@ -184,15 +205,16 @@ function getMaxIntersectionRange(xs: Nums, ys: Nums) {
const _startEndRet = { start: 0, end: 0 }; const _startEndRet = { start: 0, end: 0 };
function getStartEnd(set: OrderedSet, min: number, max: number) { function getStartEnd(set: OrderedSetImpl, min: number, max: number) {
_startEndRet.start = S.getInsertionIndex(set, min); _startEndRet.start = getInsertionIndexI(set, min);
let end = S.getInsertionIndex(set, max); let end = getInsertionIndexI(set, max);
if (end < S.size(set) && S.get(set, end) === max) end++; if (end < sizeI(set) && getAtI(set, end) === max) end++;
_startEndRet.end = end; _startEndRet.end = end;
return _startEndRet; return _startEndRet;
} }
function equalAA(a: Nums, b: Nums) { function equalAA(a: SortedArray, b: SortedArray) {
if (a === b) return true;
let size = a.length; let size = a.length;
if (a.length !== b.length || a[0] !== b[0] || a[size - 1] !== b[size - 1]) return false; if (a.length !== b.length || a[0] !== b[0] || a[size - 1] !== b[size - 1]) return false;
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
...@@ -201,7 +223,9 @@ function equalAA(a: Nums, b: Nums) { ...@@ -201,7 +223,9 @@ function equalAA(a: Nums, b: Nums) {
return true; return true;
} }
function areIntersectingAA(xs: Nums, ys: Nums) { function areIntersectingAA(xs: SortedArray, ys: SortedArray) {
if (xs === ys) return true;
let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys); let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys);
while (i <= endA && j <= endB) { while (i <= endA && j <= endB) {
const x = xs[i], y = ys[j]; const x = xs[i], y = ys[j];
...@@ -212,7 +236,9 @@ function areIntersectingAA(xs: Nums, ys: Nums) { ...@@ -212,7 +236,9 @@ function areIntersectingAA(xs: Nums, ys: Nums) {
return false; return false;
} }
function isSubsetAA(xs: Nums, ys: Nums) { function isSubsetAA(xs: SortedArray, ys: SortedArray) {
if (xs === ys) return true;
const lenB = ys.length; const lenB = ys.length;
let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys); let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys);
// the 2nd array must be able to advance by at least lenB elements // the 2nd array must be able to advance by at least lenB elements
...@@ -228,44 +254,45 @@ function isSubsetAA(xs: Nums, ys: Nums) { ...@@ -228,44 +254,45 @@ function isSubsetAA(xs: Nums, ys: Nums) {
return equal === lenB; return equal === lenB;
} }
function areRangesIntersecting(a: OrderedSet, b: OrderedSet) { function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
return S.size(a) > 0 && S.size(b) > 0 && S.max(a) >= S.min(b) && S.min(a) <= S.max(b); return sizeI(a) > 0 && sizeI(b) > 0 && maxI(a) >= minI(b) && minI(a) <= maxI(b);
} }
function isRangeSubset(a: OrderedSet, b: OrderedSet) { function isRangeSubset(a: OrderedSetImpl, b: OrderedSetImpl) {
if (!S.size(a)) return S.size(b) === 0; if (!sizeI(a)) return sizeI(b) === 0;
if (!S.size(b)) return true; if (!sizeI(b)) return true;
return S.min(a) <= S.min(b) && S.max(a) >= S.max(b); return minI(a) <= minI(b) && maxI(a) >= maxI(b);
} }
function unionRR(a: number, b: number) { function unionRR(a: Range, b: Range) {
const sizeA = S.size(a), sizeB = S.size(b); if (IntTuple.areEqual(a, b)) return a;
const sizeA = sizeR(a), sizeB = sizeR(b);
if (!sizeA) return b; if (!sizeA) return b;
if (!sizeB) return a; if (!sizeB) return a;
const minA = minR(a), minB = minR(b); const minA = minR(a), minB = minR(b);
if (areRangesIntersecting(a, b)) return S.ofRange(Math.min(minA, minB), Math.max(maxR(a), maxR(b))); if (areRangesIntersecting(a as number, b as number)) return OrderedSet.ofRange(Math.min(minA, minB), Math.max(maxR(a), maxR(b)));
let lSize, lMin, rSize, rMin; let lSize, lMin, rSize, rMin;
if (minR(a) < minR(b)) { lSize = sizeA; lMin = minA; rSize = sizeB; rMin = minB; } if (minR(a) < minR(b)) { lSize = sizeA; lMin = minA; rSize = sizeB; rMin = minB; }
else { lSize = sizeB; lMin = minB; rSize = sizeA; rMin = minA; } else { lSize = sizeB; lMin = minB; rSize = sizeA; rMin = minA; }
const arr = new Int32Array(sizeA + sizeB); const arr = new Int32Array(sizeA + sizeB);
for (let i = 0; i < lSize; i++) arr[i] = i + lMin; for (let i = 0; i < lSize; i++) arr[i] = i + lMin;
for (let i = 0; i < rSize; i++) arr[i + lSize] = i + rMin; for (let i = 0; i < rSize; i++) arr[i + lSize] = i + rMin;
return S.ofSortedArray(arr); return OrderedSet.ofSortedArray(arr);
} }
const _uAR = IntPair.zero(); const _uAR = IntTuple.zero();
function unionAR(a: Nums, b: number) { function unionAR(a: SortedArray, b: Range) {
const bSize = S.size(b); const bSize = sizeI(b);
if (!bSize) return a; if (!bSize) return a;
// is the array fully contained in the range? // is the array fully contained in the range?
if (isRangeSubset(b, a)) return b; if (isRangeSubset(b, a)) return b;
IntPair.unpack(b, _uAR); IntTuple.unpack(b, _uAR);
const min = _uAR.fst, max = _uAR.snd; const min = _uAR.fst, max = _uAR.snd;
const { start, end } = getStartEnd(a, min, max); const { start, end } = getStartEnd(a, min, max);
const size = start + (a.length - end) + bSize; const indices = new Int32Array(start + (a.length - end) + bSize);
const indices = new Int32Array(size);
let offset = 0; let offset = 0;
for (let i = 0; i < start; i++) indices[offset++] = a[i]; for (let i = 0; i < start; i++) indices[offset++] = a[i];
for (let i = min; i <= max; i++) indices[offset++] = i; for (let i = min; i <= max; i++) indices[offset++] = i;
...@@ -274,7 +301,9 @@ function unionAR(a: Nums, b: number) { ...@@ -274,7 +301,9 @@ function unionAR(a: Nums, b: number) {
return OrderedSet.ofSortedArray(indices); return OrderedSet.ofSortedArray(indices);
} }
function unionAA(a: Nums, b: Nums) { function unionAA(a: SortedArray, b: SortedArray) {
if (a === b) return a;
const lenA = a.length, lenB = b.length; const lenA = a.length, lenB = b.length;
let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b); let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
...@@ -321,20 +350,21 @@ function unionAA(a: Nums, b: Nums) { ...@@ -321,20 +350,21 @@ function unionAA(a: Nums, b: Nums) {
return OrderedSet.ofSortedArray(indices); return OrderedSet.ofSortedArray(indices);
} }
const _iRA = IntPair.zero(), _iRB = IntPair.zero(); const _iRA = IntTuple.zero(), _iRB = IntTuple.zero();
function intersectRR(a: number, b: number) { function intersectRR(a: Range, b: Range) {
if (!areRangesIntersecting(a, b)) return OrderedSet.Empty; if (!areRangesIntersecting(a, b)) return OrderedSet.Empty;
if (IntTuple.areEqual(a, b)) return a;
IntPair.unpack(a, _iRA); IntTuple.unpack(a, _iRA);
IntPair.unpack(b, _iRB); IntTuple.unpack(b, _iRB);
return OrderedSet.ofRange(Math.max(_iRA.fst, _iRB.fst), Math.min(_iRA.snd, _iRB.snd)); return OrderedSet.ofRange(Math.max(_iRA.fst, _iRB.fst), Math.min(_iRA.snd, _iRB.snd));
} }
const _iAR = IntPair.zero(); const _iAR = IntTuple.zero();
function intersectAR(a: Nums, r: number) { function intersectAR(a: SortedArray, r: Range) {
if (!S.size(r)) return OrderedSet.Empty; if (!sizeI(r)) return OrderedSet.Empty;
IntPair.unpack(r, _iAR); IntTuple.unpack(r, _iAR);
const { start, end } = getStartEnd(a, _iAR.fst, _iAR.snd); const { start, end } = getStartEnd(a, _iAR.fst, _iAR.snd);
const resultSize = end - start; const resultSize = end - start;
if (!resultSize) return OrderedSet.Empty; if (!resultSize) return OrderedSet.Empty;
...@@ -347,7 +377,9 @@ function intersectAR(a: Nums, r: number) { ...@@ -347,7 +377,9 @@ function intersectAR(a: Nums, r: number) {
return OrderedSet.ofSortedArray(indices); return OrderedSet.ofSortedArray(indices);
} }
function intersectAA(xs: Nums, ys: Nums) { function intersectAA(xs: SortedArray, ys: SortedArray) {
if (xs === ys) return xs;
let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys); let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys);
let i = sI, j = sJ; let i = sI, j = sJ;
let resultSize = 0; let resultSize = 0;
...@@ -374,10 +406,12 @@ function intersectAA(xs: Nums, ys: Nums) { ...@@ -374,10 +406,12 @@ function intersectAA(xs: Nums, ys: Nums) {
return OrderedSet.ofSortedArray(indices); return OrderedSet.ofSortedArray(indices);
} }
const _sRA = IntPair.zero(), _sRB = IntPair.zero(); const _sRA = IntTuple.zero(), _sRB = IntTuple.zero();
function substractRR(a: number, b: number) { function substractRR(a: Range, b: Range) {
IntPair.unpack(a, _sRA); if (IntTuple.areEqual(a, b)) return OrderedSet.Empty;
IntPair.unpack(b, _sRB);
IntTuple.unpack(a, _sRA);
IntTuple.unpack(b, _sRB);
if (_sRA.snd < _sRA.fst || _sRB.snd < _sRB.fst) return a; if (_sRA.snd < _sRA.fst || _sRB.snd < _sRB.fst) return a;
// is A subset of B? ==> Empty // is A subset of B? ==> Empty
...@@ -397,9 +431,11 @@ function substractRR(a: number, b: number) { ...@@ -397,9 +431,11 @@ function substractRR(a: number, b: number) {
return OrderedSet.ofRange(_sRB.snd + 1, _sRA.snd); return OrderedSet.ofRange(_sRB.snd + 1, _sRA.snd);
} }
const _sAR = IntPair.zero(); const _sAR = IntTuple.zero();
function subtractAR(a: Nums, r: number) { function subtractAR(a: SortedArray, b: Range) {
IntPair.unpack(r, _sAR); IntTuple.unpack(b, _sAR);
// is empty?
if (_sAR.snd < _sAR.fst) return a; if (_sAR.snd < _sAR.fst) return a;
const min = _sAR.fst, max = _sAR.snd; const min = _sAR.fst, max = _sAR.snd;
...@@ -413,10 +449,12 @@ function subtractAR(a: Nums, r: number) { ...@@ -413,10 +449,12 @@ function subtractAR(a: Nums, r: number) {
return OrderedSet.ofSortedArray(ret); return OrderedSet.ofSortedArray(ret);
} }
const _sAR1 = IntPair.zero(); const _sAR1 = IntTuple.zero();
function subtractRA(r: number, b: Nums) { function subtractRA(a: Range, b: SortedArray) {
IntPair.unpack(r, _sAR1); IntTuple.unpack(a, _sAR1);
if (_sAR1.snd < _sAR1.fst) return r;
// is empty?
if (_sAR1.snd < _sAR1.fst) return a;
const min = _sAR1.fst, max = _sAR1.snd; const min = _sAR1.fst, max = _sAR1.snd;
const rSize = max - min + 1; const rSize = max - min + 1;
...@@ -436,7 +474,9 @@ function subtractRA(r: number, b: Nums) { ...@@ -436,7 +474,9 @@ function subtractRA(r: number, b: Nums) {
return OrderedSet.ofSortedArray(ret); return OrderedSet.ofSortedArray(ret);
} }
function subtractAA(a: Nums, b: Nums) { function subtractAA(a: SortedArray, b: SortedArray) {
if (a === b) return OrderedSet.Empty;
const lenA = a.length; const lenA = a.length;
let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b); let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
...@@ -472,5 +512,3 @@ function subtractAA(a: Nums, b: Nums) { ...@@ -472,5 +512,3 @@ function subtractAA(a: Nums, b: Nums) {
return OrderedSet.ofSortedArray(indices); return OrderedSet.ofSortedArray(indices);
} }
\ No newline at end of file
export default OrderedSet
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
*/ */
import Iterator from '../collections/iterator' import Iterator from '../collections/iterator'
import IntPair from '../collections/int-pair' import IntTuple from '../collections/int-tuple'
import * as Sort from '../collections/sort' import * as Sort from '../collections/sort'
import OrderedSet from '../collections/ordered-set' import OrderedSet from '../collections/ordered-set'
import LinkedIndex from '../collections/linked-index' import LinkedIndex from '../collections/linked-index'
...@@ -33,11 +33,11 @@ describe('basic iterators', () => { ...@@ -33,11 +33,11 @@ describe('basic iterators', () => {
describe('int pair', () => { describe('int pair', () => {
it('works', () => { it('works', () => {
const p = IntPair.zero(); const p = IntTuple.zero();
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
for (let j = -10; j < 5; j++) { for (let j = -10; j < 5; j++) {
const t = IntPair.pack1(i, j); const t = IntTuple.pack(i, j);
IntPair.unpack(t, p); IntTuple.unpack(t, p);
expect(p.fst).toBe(i); expect(p.fst).toBe(i);
expect(p.snd).toBe(j); expect(p.snd).toBe(j);
} }
...@@ -131,7 +131,7 @@ describe('qsort-dual array', () => { ...@@ -131,7 +131,7 @@ describe('qsort-dual array', () => {
describe('ordered set', () => { describe('ordered set', () => {
function ordSetToArray(set: OrderedSet) { function ordSetToArray(set: OrderedSet) {
const ret = []; const ret = [];
for (let i = 0, _i = OrderedSet.size(set); i < _i; i++) ret.push(OrderedSet.get(set, i)); for (let i = 0, _i = OrderedSet.size(set); i < _i; i++) ret.push(OrderedSet.getAt(set, i));
return ret; return ret;
} }
...@@ -317,13 +317,13 @@ describe('linked-index', () => { ...@@ -317,13 +317,13 @@ describe('linked-index', () => {
}); });
describe('multiset', () => { describe('multiset', () => {
const p = (i: number, j: number) => IntPair.create(i, j); const p = (i: number, j: number) => IntTuple.create(i, j);
const r = (i: number, j: number) => IntPair.pack1(i, j); const r = (i: number, j: number) => IntTuple.pack(i, j);
function setToPairs(set: MultiSet): IntPair[] { function setToPairs(set: MultiSet): ArrayLike<IntTuple.Unpacked> {
const ret = []; const ret = [];
const it = MultiSet.values(set); const it = MultiSet.values(set);
for (let v = it.move(); !it.done; v = it.move()) ret[ret.length] = IntPair.create(v.fst, v.snd); for (let v = it.move(); !it.done; v = it.move()) ret[ret.length] = IntTuple.create(v.fst, v.snd);
return ret; return ret;
} }
...@@ -332,7 +332,8 @@ describe('multiset', () => { ...@@ -332,7 +332,8 @@ describe('multiset', () => {
expect(setToPairs(set)).toEqual([p(10, 11)]); expect(setToPairs(set)).toEqual([p(10, 11)]);
expect(MultiSet.has(set, r(10, 11))).toBe(true); expect(MultiSet.has(set, r(10, 11))).toBe(true);
expect(MultiSet.has(set, r(11, 11))).toBe(false); expect(MultiSet.has(set, r(11, 11))).toBe(false);
expect(MultiSet.get(set, 0)).toBe(r(10, 11)); expect(MultiSet.getAt(set, 0)).toBe(r(10, 11));
expect(MultiSet.size(set)).toBe(1);
}); });
it('singleton number', () => { it('singleton number', () => {
...@@ -352,12 +353,12 @@ describe('multiset', () => { ...@@ -352,12 +353,12 @@ describe('multiset', () => {
expect(MultiSet.has(set, r(3, 0))).toBe(true); expect(MultiSet.has(set, r(3, 0))).toBe(true);
expect(MultiSet.has(set, r(1, 7))).toBe(true); expect(MultiSet.has(set, r(1, 7))).toBe(true);
for (let i = 0; i < MultiSet.size(set); i++) { for (let i = 0; i < MultiSet.size(set); i++) {
expect(MultiSet.get(set, i)).toBe(IntPair.pack(ret[i])); expect(MultiSet.getAt(set, i)).toBe(IntTuple.pack1(ret[i]));
} }
}); });
it('element at', () => { it('element at / index of', () => {
const control = []; const control: IntTuple[] = [];
const sets = Object.create(null); const sets = Object.create(null);
for (let i = 1; i < 10; i++) { for (let i = 1; i < 10; i++) {
const set = []; const set = [];
...@@ -369,7 +370,11 @@ describe('multiset', () => { ...@@ -369,7 +370,11 @@ describe('multiset', () => {
} }
const ms = MultiSet.create(sets); const ms = MultiSet.create(sets);
for (let i = 0; i < control.length; i++) { for (let i = 0; i < control.length; i++) {
expect(IntPair.areEqual(MultiSet.get(ms, i), control[i])).toBe(true); expect(IntTuple.areEqual(MultiSet.getAt(ms, i), control[i])).toBe(true);
}
for (let i = 0; i < control.length; i++) {
expect(MultiSet.indexOf(ms, control[i])).toBe(i);
} }
}); });
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment