Browse Source

ordered set

David Sehnal 7 years ago
parent
commit
cf39be5f55

+ 5 - 5
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 {

+ 16 - 24
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;
     }

+ 194 - 46
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

+ 93 - 68
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 = [];