diff --git a/src/structure/collections/int-pair.ts b/src/structure/collections/int-pair.ts index 4cdf0199e32d5163839da065f5efd40480a156cd..a0c655d1193f358d263ae04e6786d803efc64434 100644 --- a/src/structure/collections/int-pair.ts +++ b/src/structure/collections/int-pair.ts @@ -25,27 +25,27 @@ namespace IntPair { export function create(fst: number, snd: number) { return { fst, snd }; } export function zero(): IntPair { return { fst: 0, snd: 0 }; } - export function set1(fst: number, snd: number): number { + export function pack1(fst: number, snd: number): number { _int32[0] = fst; _int32[1] = snd; return _float64[0]; } - export function set(p: IntPair): number { + export function pack(p: IntPair): number { _int32[0] = p.fst; _int32[1] = p.snd; return _float64[0]; } - export function get(packed: number, target: IntPair): IntPair { + export function unpack(packed: number, target: IntPair): IntPair { _float64[0] = packed; target.fst = _int32[0]; target.snd = _int32[1]; return target; } - export function get1(packed: number): IntPair { - return get(packed, zero()); + export function unpack1(packed: number): IntPair { + return unpack(packed, zero()); } export function fst(packed: number): number { diff --git a/src/structure/collections/multi-set.ts b/src/structure/collections/multi-set.ts index 491d6d9caacdb3b20a7c68b6d7669713e4c8092c..f7cf53a154d1c6cd4c63a696fba53a2f55b69aa5 100644 --- a/src/structure/collections/multi-set.ts +++ b/src/structure/collections/multi-set.ts @@ -37,7 +37,7 @@ namespace MultiSet { export function create(data: number | ArrayLike<number> | IntPair | { [id: number]: OrderedSet }): MultiSet { if (typeof data === 'number') return data; - if (IntPair.is(data)) return IntPair.set(data); + if (IntPair.is(data)) return IntPair.pack(data); if (isArrayLike(data)) return ofPackedPairs(data); const keys = []; for (const _k of Object.keys(data)) { @@ -103,38 +103,30 @@ namespace MultiSet { } export function values(set: MultiSet): Iterator<IntPair> { - if (typeof set === 'number') return new PairIterator(IntPair.get1(set)); + if (typeof set === 'number') return new PairIterator(IntPair.unpack1(set)); return new ElementsIterator(set); } function ofPackedPairs(xs: ArrayLike<number>): MultiSet { if (xs.length === 0) return Empty; - - sortArray(xs, IntPair.compareInArray); + const sets: { [key: number]: number[] } = Object.create(null); const p = IntPair.zero(); - IntPair.get(xs[0], p); - let currentKey = p.fst; - let keys = []; - let currentSet = [p.snd]; - let ret = Object.create(null); - for (let i = 1, _i = xs.length; i < _i; i++) { - IntPair.get(xs[i], p); - if (p.fst === currentKey) { - currentSet[currentSet.length] = p.snd; - } else { - ret[currentKey] = OrderedSet.ofSortedArray(currentSet); - keys[keys.length] = currentKey; - - currentKey = p.fst; - currentSet = [p.snd]; - } + for (let i = 0, _i = xs.length; i < _i; i++) { + IntPair.unpack(xs[i], p); + const set = sets[p.fst]; + if (set) set[set.length] = p.snd; + else sets[p.fst] = [p.snd]; } - ret[currentKey] = OrderedSet.ofSortedArray(currentSet); - keys[keys.length] = currentKey; - ret.keys = OrderedSet.ofSortedArray(keys); - return ret; + const ret: { [key: number]: OrderedSet } = Object.create(null); + for (const _k of Object.keys(sets)) { + const k = +_k; + ret[k] = OrderedSet.ofSortedArray(sortArray(sets[k])); + } + return create(ret); } + // TODO: union, intersection, subtraction + export function union(sets: ArrayLike<MultiSet>): MultiSet { return 0 as any; } diff --git a/src/structure/collections/ordered-set.ts b/src/structure/collections/ordered-set.ts index 38b5e33c883b05f2dcaaed9d5b685c20a292cdb5..96dacd5067fcae8d776cf47eb152797310b73046 100644 --- a/src/structure/collections/ordered-set.ts +++ b/src/structure/collections/ordered-set.ts @@ -109,7 +109,7 @@ namespace OrderedSet { return unionAR(b as ArrayImpl, a); } else if (b instanceof RangeImpl) { return unionAR(a as ArrayImpl, b); - } else return unionAA((a as ArrayImpl).values, (b as ArrayImpl).values); + } else return unionAA(a as ArrayImpl, b as ArrayImpl); } export function intersect(a: OrderedSet, b: OrderedSet) { @@ -123,6 +123,19 @@ namespace OrderedSet { return intersectAA((a as ArrayImpl).values, (b as ArrayImpl).values); } } + + export function subtract(a: OrderedSet, b: OrderedSet) { + if (!areRangesIntersecting(a, b)) return a; + + if (a instanceof RangeImpl) { + if (b instanceof RangeImpl) return substractRR(a, b); + return subtractRA(a, (b as ArrayImpl).values); + } else if (b instanceof RangeImpl) { + return subtractAR(a as ArrayImpl, b); + } else { + return subtractAA(a as ArrayImpl, b as ArrayImpl); + } + } } function min(a: OrderedSet) { return (a as Impl).min; } @@ -147,6 +160,38 @@ function binarySearch(xs: ArrayLike<number>, value: number) { return -1; } +function binarySearchIndex(xs: ArrayLike<number>, value: number) { + let min = 0, max = xs.length - 1; + while (min < max) { + const mid = (min + max) >> 1; + const v = xs[mid]; + if (value < v) max = mid - 1; + else if (value > v) min = mid + 1; + else return mid; + } + if (min > max) return max + 1; + return xs[min] >= value ? min : min + 1; +} + +const _maxIntRangeRet = { i: 0, j: 0, endA: 0, endB: 0 }; +function getMaxIntersectionRange(xs: ArrayLike<number>, ys: ArrayLike<number>) { + const la = xs.length - 1, lb = ys.length - 1; + _maxIntRangeRet.i = binarySearchIndex(xs, ys[0]); + _maxIntRangeRet.j = binarySearchIndex(ys, xs[0]); + _maxIntRangeRet.endA = Math.min(binarySearchIndex(xs, ys[lb]), la); + _maxIntRangeRet.endB = Math.min(binarySearchIndex(ys, xs[la]), lb); + return _maxIntRangeRet; +} + +const _startEndRet = { start: 0, end: 0 }; +function getStartEnd(xs: ArrayLike<number>, min: number, max: number) { + _startEndRet.start = binarySearchIndex(xs, min); + let end = binarySearchIndex(xs, max); + if (xs[end] === max) end++; + _startEndRet.end = end; + return _startEndRet; +} + function equalAR(a: ArrayImpl, b: RangeImpl) { return a.size === b.size && a.min === b.min && a.max === b.max; } @@ -162,9 +207,8 @@ function equalAA(a: ArrayImpl, b: ArrayImpl) { } function areIntersectingAA(xs: ArrayLike<number>, ys: ArrayLike<number>) { - const la = xs.length, lb = ys.length; - let i = 0, j = 0; - while (i < la && j < lb) { + let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys); + while (i <= endA && j <= endB) { const x = xs[i], y = ys[j]; if (x < y) { i++; } else if (x > y) { j++; } @@ -174,15 +218,19 @@ function areIntersectingAA(xs: ArrayLike<number>, ys: ArrayLike<number>) { } function isSubsetAA(xs: ArrayLike<number>, ys: ArrayLike<number>) { - const la = xs.length, lb = ys.length; - let i = 0, j = 0, equal = 0; - while (i < la && j < lb) { + const lenB = ys.length; + let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys); + // the 2nd array must be able to advance by at least lenB elements + if (endB - j + 1 < lenB || endA - j + 1 < lenB) return false; + + let equal = 0; + while (i <= endA && j <= endB) { const x = xs[i], y = ys[j]; if (x < y) { i++; } else if (x > y) { j++; } else { i++; j++; equal++; } } - return equal === lb; + return equal === lenB; } function areRangesIntersecting(a: OrderedSet, b: OrderedSet) { @@ -215,11 +263,7 @@ function unionAR(a: ArrayImpl, b: RangeImpl) { const xs = a.values; const { min, max } = b; - - let start = 0, end = xs.length - 1; - while (xs[start] < min) { start++; } - while (xs[end] > max) { end--; } - end++; + const { start, end } = getStartEnd(xs, min, max); const size = start + (xs.length - end) + b.size; const indices = new Int32Array(size); @@ -231,33 +275,50 @@ function unionAR(a: ArrayImpl, b: RangeImpl) { return OrderedSet.ofSortedArray(indices); } -function unionAA(xs: ArrayLike<number>, ys: ArrayLike<number>) { - const la = xs.length, lb = ys.length; - - // sorted list merge. +function unionAA(a: ArrayImpl, b: ArrayImpl) { + const xs = a.values, ys = b.values; + const lenA = xs.length, lenB = ys.length; - let i = 0, j = 0, resultSize = 0; - while (i < la && j < lb) { + let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys); + let i = sI, j = sJ; + let commonCount = 0; + while (i <= endA && j <= endB) { const x = xs[i], y = ys[j]; - resultSize++; if (x < y) { i++; } else if (x > y) { j++; } - else { i++; j++; } + else { i++; j++; commonCount++; } } - resultSize += Math.max(la - i, lb - j); - const indices = new Int32Array(resultSize); + if (!commonCount) return a; + if (commonCount >= lenA) return OrderedSet.Empty + + const resultSize = lenA + lenB - commonCount; + const l = Math.min(min(a), min(b)), r = Math.max(max(a), max(b)); + // is this just a range? + if (resultSize === r - l + 1) { + return OrderedSet.ofRange(l, r); + } + + const indices = new Int32Array(lenA + lenB - commonCount); let offset = 0; - i = 0; - j = 0; - while (i < la && j < lb) { + + // insert the "prefixes" + for (let k = 0; k < sI; k++) indices[offset++] = xs[k]; + for (let k = 0; k < sJ; k++) indices[offset++] = xs[k]; + + // insert the common part + i = sI; + j = sJ; + while (i <= endA && j <= endB) { const x = xs[i], y = ys[j]; if (x < y) { indices[offset++] = x; i++; } else if (x > y) { indices[offset++] = y; j++; } else { indices[offset++] = x; i++; j++; } } - for (; i < la; i++) { indices[offset++] = xs[i]; } - for (; j < lb; j++) { indices[offset++] = ys[j]; } + + // insert the "tail" + for (; i < lenA; i++) indices[offset++] = xs[i]; + for (; j < lenB; j++) indices[offset++] = ys[j]; return OrderedSet.ofSortedArray(indices); } @@ -268,31 +329,26 @@ function intersectRR(a: RangeImpl, b: RangeImpl) { } function intersectAR(a: ArrayImpl, r: RangeImpl) { - const xs = a.values; - let resultSize = 0; - for (let i = 0, _i = xs.length; i < _i; i++) { - if (r.has(xs[i])) resultSize++; - } + if (!r.size) return OrderedSet.Empty; + const xs = a.values; + const { start, end } = getStartEnd(xs, r.min, r.max); + const resultSize = end - start; if (!resultSize) return OrderedSet.Empty; const indices = new Int32Array(resultSize); let offset = 0; - - for (let i = 0, _i = xs.length; i < _i; i++) { - if (r.has(xs[i])) indices[offset++] = xs[i]; + for (let i = start; i < end; i++) { + indices[offset++] = xs[i]; } - return OrderedSet.ofSortedArray(indices); } function intersectAA(xs: ArrayLike<number>, ys: ArrayLike<number>) { - const la = xs.length, lb = ys.length; - - // a variation on sorted list merge. - - let i = 0, j = 0, resultSize = 0; - while (i < la && j < lb) { + let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys); + let i = sI, j = sJ; + let resultSize = 0; + while (i <= endA && j <= endB) { const x = xs[i], y = ys[j]; if (x < y) { i++; } else if (x > y) { j++; } @@ -303,9 +359,9 @@ function intersectAA(xs: ArrayLike<number>, ys: ArrayLike<number>) { const indices = new Int32Array(resultSize); let offset = 0; - i = 0; - j = 0; - while (i < la && j < lb) { + i = sI; + j = sJ; + while (i <= endA && j <= endB) { const x = xs[i], y = ys[j]; if (x < y) { i++; } else if (x > y) { j++; } @@ -315,4 +371,96 @@ function intersectAA(xs: ArrayLike<number>, ys: ArrayLike<number>) { return OrderedSet.ofSortedArray(indices); } +function substractRR(a: RangeImpl, b: RangeImpl) { + if (a.size === 0 || b.size === 0) return a; + // is A subset of B? ==> Empty + if (isRangeSubset(b, a)) return OrderedSet.Empty; + if (isRangeSubset(a, b)) { + // this splits the interval into two, gotta represent it as a set. + const l = b.min - a.min, r = a.max - b.max; + const ret = new Int32Array(l + r); + let offset = 0; + for (let i = 0; i < l; i++) ret[offset++] = a.min + i; + for (let i = 1; i <= r; i++) ret[offset++] = b.max + i; + return OrderedSet.ofSortedArray(ret); + } + // non intersecting ranges are handled by top-level substract. + // at this point, b either contains a.min or a.max, but not both. + if (a.min < b.min) return OrderedSet.ofRange(a.min, b.min - 1); + return OrderedSet.ofRange(b.max + 1, a.max); +} + +function subtractAR(a: ArrayImpl, r: RangeImpl) { + if (!r.size) return a; + + const xs = a.values; + const { min, max } = r; + const { start, end } = getStartEnd(xs, min, max); + const size = xs.length - (end - start); + if (size <= 0) return OrderedSet.Empty; + const ret = new Int32Array(size); + let offset = 0; + for (let i = 0; i < start; i++) ret[offset++] = xs[i]; + for (let i = end, _i = xs.length; i < _i; i++) ret[offset++] = xs[i]; + return OrderedSet.ofSortedArray(ret); +} + +function subtractRA(r: RangeImpl, ys: ArrayLike<number>) { + if (!r.size) return r; + + const { min, max } = r; + const { start, end } = getStartEnd(ys, min, max); + const commonCount = end - start; + const resultSize = r.size - commonCount; + if (resultSize <= 0) return OrderedSet.Empty; + const ret = new Int32Array(resultSize); + const li = ys.length - 1; + const fst = ys[Math.min(start, li)], last = ys[Math.min(end, li)]; + let offset = 0; + for (let i = min; i < fst; i++) ret[offset++] = i; + for (let i = fst; i <= last; i++) { + if (binarySearch(ys, i) < 0) ret[offset++] = i; + } + for (let i = last + 1; i <= max; i++) ret[offset++] = i; + return OrderedSet.ofSortedArray(ret); +} + +function subtractAA(a: ArrayImpl, b: ArrayImpl) { + const xs = a.values, ys = b.values; + const lenA = xs.length; + + let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys); + let i = sI, j = sJ; + let commonCount = 0; + while (i <= endA && j <= endB) { + const x = xs[i], y = ys[j]; + if (x < y) { i++; } + else if (x > y) { j++; } + else { i++; j++; commonCount++; } + } + + if (!commonCount) return a; + if (commonCount >= lenA) return OrderedSet.Empty; + + const indices = new Int32Array(lenA - commonCount); + let offset = 0; + + // insert the "prefix" + for (let k = 0; k < sI; k++) indices[offset++] = xs[k]; + + i = sI; + j = sJ; + while (i <= endA && j <= endB) { + const x = xs[i], y = ys[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++] = xs[i]; + + return OrderedSet.ofSortedArray(indices); +} + export default OrderedSet \ No newline at end of file diff --git a/src/structure/spec/collections.spec.ts b/src/structure/spec/collections.spec.ts index 1e6bde4b15766d9365588cd5565f137f8bf825eb..709eedef81a7acf8ee18656ff299f16a062f75d4 100644 --- a/src/structure/spec/collections.spec.ts +++ b/src/structure/spec/collections.spec.ts @@ -36,8 +36,8 @@ describe('int pair', () => { const p = IntPair.zero(); for (let i = 0; i < 10; i++) { for (let j = -10; j < 5; j++) { - const t = IntPair.set1(i, j); - IntPair.get(t, p); + const t = IntPair.pack1(i, j); + IntPair.unpack(t, p); expect(p.fst).toBe(i); expect(p.snd).toBe(j); } @@ -137,93 +137,118 @@ describe('range set', () => { } const empty = OrderedSet.Empty; - const singleton = OrderedSet.ofSingleton(10); - const range = OrderedSet.ofRange(1, 4); - const arr = OrderedSet.ofSortedArray([1, 3, 6]); + const singleton10 = OrderedSet.ofSingleton(10); + const range1_4 = OrderedSet.ofRange(1, 4); + const arr136 = OrderedSet.ofSortedArray([1, 3, 6]); testEq('empty', empty, []); - testEq('singleton', singleton, [10]); - testEq('range', range, [1, 2, 3, 4]); - testEq('sorted array', arr, [1, 3, 6]); + testEq('singleton', singleton10, [10]); + testEq('range', range1_4, [1, 2, 3, 4]); + testEq('sorted array', arr136, [1, 3, 6]); it('equality', () => { - expect(OrderedSet.areEqual(empty, singleton)).toBe(false); - expect(OrderedSet.areEqual(singleton, singleton)).toBe(true); - expect(OrderedSet.areEqual(range, singleton)).toBe(false); - expect(OrderedSet.areEqual(arr, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(true); - expect(OrderedSet.areEqual(arr, OrderedSet.ofSortedArray([1, 4, 6]))).toBe(false); + expect(OrderedSet.areEqual(empty, singleton10)).toBe(false); + expect(OrderedSet.areEqual(singleton10, singleton10)).toBe(true); + expect(OrderedSet.areEqual(range1_4, singleton10)).toBe(false); + expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(true); + expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 4, 6]))).toBe(false); }); it('areIntersecting', () => { - expect(OrderedSet.areIntersecting(range, arr)).toBe(true); + expect(OrderedSet.areIntersecting(range1_4, arr136)).toBe(true); expect(OrderedSet.areIntersecting(empty, empty)).toBe(true); - expect(OrderedSet.areIntersecting(empty, singleton)).toBe(false); - expect(OrderedSet.areIntersecting(empty, range)).toBe(false); - expect(OrderedSet.areIntersecting(empty, arr)).toBe(false); + expect(OrderedSet.areIntersecting(empty, singleton10)).toBe(false); + expect(OrderedSet.areIntersecting(empty, range1_4)).toBe(false); + expect(OrderedSet.areIntersecting(empty, arr136)).toBe(false); }); it('isSubset', () => { - expect(OrderedSet.isSubset(singleton, empty)).toBe(true); - expect(OrderedSet.isSubset(range, empty)).toBe(true); - expect(OrderedSet.isSubset(arr, empty)).toBe(true); + expect(OrderedSet.isSubset(singleton10, empty)).toBe(true); + expect(OrderedSet.isSubset(range1_4, empty)).toBe(true); + expect(OrderedSet.isSubset(arr136, empty)).toBe(true); expect(OrderedSet.isSubset(empty, empty)).toBe(true); - expect(OrderedSet.isSubset(empty, singleton)).toBe(false); - expect(OrderedSet.isSubset(empty, range)).toBe(false); - expect(OrderedSet.isSubset(empty, arr)).toBe(false); - - expect(OrderedSet.isSubset(singleton, range)).toBe(false); - expect(OrderedSet.isSubset(range, OrderedSet.ofRange(2, 3))).toBe(true); - expect(OrderedSet.isSubset(arr, range)).toBe(false); - expect(OrderedSet.isSubset(arr, arr)).toBe(true); - expect(OrderedSet.isSubset(arr, OrderedSet.ofSortedArray([1, 3]))).toBe(true); - expect(OrderedSet.isSubset(arr, OrderedSet.ofSortedArray([1, 3, 7]))).toBe(false); - expect(OrderedSet.isSubset(arr, OrderedSet.ofSortedArray([1, 3, 10, 45]))).toBe(false); + expect(OrderedSet.isSubset(empty, singleton10)).toBe(false); + expect(OrderedSet.isSubset(empty, range1_4)).toBe(false); + expect(OrderedSet.isSubset(empty, arr136)).toBe(false); + + expect(OrderedSet.isSubset(singleton10, range1_4)).toBe(false); + expect(OrderedSet.isSubset(range1_4, OrderedSet.ofRange(2, 3))).toBe(true); + expect(OrderedSet.isSubset(arr136, range1_4)).toBe(false); + expect(OrderedSet.isSubset(arr136, arr136)).toBe(true); + expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3]))).toBe(true); + expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3, 7]))).toBe(false); + expect(OrderedSet.isSubset(OrderedSet.ofSortedArray([0, 1, 2, 3, 7, 10]), OrderedSet.ofSortedArray([1, 3, 7]))).toBe(true); + expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3, 10, 45]))).toBe(false); }); it('access/membership', () => { expect(empty.has(10)).toBe(false); expect(empty.indexOf(10)).toBe(-1); - expect(singleton.has(10)).toBe(true); - expect(singleton.has(11)).toBe(false); - expect(singleton.indexOf(10)).toBe(0); - expect(singleton.indexOf(11)).toBe(-1); + expect(singleton10.has(10)).toBe(true); + expect(singleton10.has(11)).toBe(false); + expect(singleton10.indexOf(10)).toBe(0); + expect(singleton10.indexOf(11)).toBe(-1); - expect(range.has(4)).toBe(true); - expect(range.has(5)).toBe(false); - expect(range.indexOf(4)).toBe(3); - expect(range.indexOf(11)).toBe(-1); + expect(range1_4.has(4)).toBe(true); + expect(range1_4.has(5)).toBe(false); + expect(range1_4.indexOf(4)).toBe(3); + expect(range1_4.indexOf(11)).toBe(-1); - expect(arr.has(3)).toBe(true); - expect(arr.has(4)).toBe(false); - expect(arr.indexOf(3)).toBe(1); - expect(arr.indexOf(11)).toBe(-1); + expect(arr136.has(3)).toBe(true); + expect(arr136.has(4)).toBe(false); + expect(arr136.indexOf(3)).toBe(1); + expect(arr136.indexOf(11)).toBe(-1); }); - testEq('union ES', OrderedSet.union(empty, singleton), [10]); - testEq('union ER', OrderedSet.union(empty, range), [1, 2, 3, 4]); - testEq('union EA', OrderedSet.union(empty, arr), [1, 3, 6]); - testEq('union SS', OrderedSet.union(singleton, OrderedSet.ofSingleton(16)), [10, 16]); - testEq('union SR', OrderedSet.union(range, singleton), [1, 2, 3, 4, 10]); - testEq('union SA', OrderedSet.union(arr, singleton), [1, 3, 6, 10]); - testEq('union SA1', OrderedSet.union(arr, OrderedSet.ofSingleton(3)), [1, 3, 6]); - testEq('union RR', OrderedSet.union(range, range), [1, 2, 3, 4]); - testEq('union RR1', OrderedSet.union(range, OrderedSet.ofRange(6, 7)), [1, 2, 3, 4, 6, 7]); - testEq('union RR2', OrderedSet.union(range, OrderedSet.ofRange(3, 5)), [1, 2, 3, 4, 5]); - testEq('union RA', OrderedSet.union(range, arr), [1, 2, 3, 4, 6]); - testEq('union AA', OrderedSet.union(arr, OrderedSet.ofSortedArray([2, 4, 6, 7])), [1, 2, 3, 4, 6, 7]); - testEq('union AA1', OrderedSet.union(arr, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1, 2, 3, 4, 6, 7]); - - testEq('intersect ES', OrderedSet.intersect(empty, singleton), []); - testEq('intersect ER', OrderedSet.intersect(empty, range), []); - testEq('intersect EA', OrderedSet.intersect(empty, arr), []); - testEq('intersect SS', OrderedSet.intersect(singleton, OrderedSet.ofSingleton(16)), []); - testEq('intersect SS', OrderedSet.intersect(singleton, singleton), [10]); - testEq('intersect SR', OrderedSet.intersect(range, singleton), []); - testEq('intersect RR', OrderedSet.intersect(range, range), [1, 2, 3, 4]); - testEq('intersect RR2', OrderedSet.intersect(range, OrderedSet.ofRange(3, 5)), [3, 4]); - testEq('intersect RA', OrderedSet.intersect(range, arr), [1, 3]); - testEq('intersect AA', OrderedSet.intersect(arr, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]); + testEq('union ES', OrderedSet.union(empty, singleton10), [10]); + testEq('union ER', OrderedSet.union(empty, range1_4), [1, 2, 3, 4]); + testEq('union EA', OrderedSet.union(empty, arr136), [1, 3, 6]); + testEq('union SS', OrderedSet.union(singleton10, OrderedSet.ofSingleton(16)), [10, 16]); + testEq('union SR', OrderedSet.union(range1_4, singleton10), [1, 2, 3, 4, 10]); + testEq('union SA', OrderedSet.union(arr136, singleton10), [1, 3, 6, 10]); + testEq('union SA1', OrderedSet.union(arr136, OrderedSet.ofSingleton(3)), [1, 3, 6]); + testEq('union RR', OrderedSet.union(range1_4, range1_4), [1, 2, 3, 4]); + testEq('union RR1', OrderedSet.union(range1_4, OrderedSet.ofRange(6, 7)), [1, 2, 3, 4, 6, 7]); + testEq('union RR2', OrderedSet.union(range1_4, OrderedSet.ofRange(3, 5)), [1, 2, 3, 4, 5]); + testEq('union RA', OrderedSet.union(range1_4, arr136), [1, 2, 3, 4, 6]); + testEq('union AA', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 4, 6, 7])), [1, 2, 3, 4, 6, 7]); + testEq('union AA1', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1, 2, 3, 4, 6, 7]); + testEq('union AA2', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 4, 5, 6, 7])), [1, 2, 3, 4, 5, 6, 7]); + + testEq('intersect ES', OrderedSet.intersect(empty, singleton10), []); + testEq('intersect ER', OrderedSet.intersect(empty, range1_4), []); + testEq('intersect EA', OrderedSet.intersect(empty, arr136), []); + testEq('intersect SS', OrderedSet.intersect(singleton10, OrderedSet.ofSingleton(16)), []); + testEq('intersect SS1', OrderedSet.intersect(singleton10, singleton10), [10]); + testEq('intersect SR', OrderedSet.intersect(range1_4, singleton10), []); + testEq('intersect RR', OrderedSet.intersect(range1_4, range1_4), [1, 2, 3, 4]); + testEq('intersect RR2', OrderedSet.intersect(range1_4, OrderedSet.ofRange(3, 5)), [3, 4]); + testEq('intersect RA', OrderedSet.intersect(range1_4, arr136), [1, 3]); + testEq('intersect AA', OrderedSet.intersect(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]); + + testEq('subtract ES', OrderedSet.subtract(empty, singleton10), []); + testEq('subtract ER', OrderedSet.subtract(empty, range1_4), []); + testEq('subtract EA', OrderedSet.subtract(empty, arr136), []); + testEq('subtract SS', OrderedSet.subtract(singleton10, OrderedSet.ofSingleton(16)), [10]); + testEq('subtract SS1', OrderedSet.subtract(singleton10, singleton10), []); + testEq('subtract SR', OrderedSet.subtract(range1_4, singleton10), [1, 2, 3, 4]); + testEq('subtract SR1', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(4)), [1, 2, 3]); + testEq('subtract SR2', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(3)), [1, 2, 4]); + testEq('subtract RR', OrderedSet.subtract(range1_4, range1_4), []); + testEq('subtract RR1', OrderedSet.subtract(range1_4, OrderedSet.ofRange(3, 5)), [1, 2]); + + testEq('subtract RA', OrderedSet.subtract(range1_4, arr136), [2, 4]); + testEq('subtract RA1', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 1, 2, 3, 4, 7])), []); + testEq('subtract RA2', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 2, 3])), [1, 4]); + + testEq('subtract AR', OrderedSet.subtract(arr136, range1_4), [6]); + testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(0, 10)), []); + testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(2, 10)), [1]); + + testEq('subtract AA', OrderedSet.subtract(arr136, arr136), []); + testEq('subtract AA1', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1]); + testEq('subtract AA2', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([0, 1, 6])), [3]); }); describe('linked-index', () => { @@ -271,7 +296,7 @@ describe('linked-index', () => { describe('multiset', () => { const p = (i: number, j: number) => IntPair.create(i, j); - const r = (i: number, j: number) => IntPair.set1(i, j); + const r = (i: number, j: number) => IntPair.pack1(i, j); function iteratorPairsToArray(it: Iterator<IntPair>): IntPair[] { const ret = [];