Browse Source

collections

David Sehnal 7 years ago
parent
commit
8abc0de5fb

+ 30 - 34
src/perf-tests/ordered-set.ts

@@ -1,34 +1,30 @@
-import * as B from 'benchmark'
-import OrderedSet from '../structure/collections/ordered-set'
-import IntPair from '../structure/collections/int-pair'
-
-
-const range = OrderedSet.ofRange(0, 100);
-const pairSet = IntPair.pack1(0, 100);
-
-namespace PairSet {
-    const pair = IntPair.zero();
-    export function has(p: number, x: number) {
-        IntPair.unpack(p, pair);
-        return x >= pair.fst && x <= pair.snd;
-    }
-}
-
-const suite = new B.Suite();
-
-const values: number[] = [];
-for (let i = 0; i < 1000000; i++) values[i] = (Math.random() * 1000) | 0;
-
-let idx = 0;
-
-suite
-    .add('range', () => range.has(idx % values.length))
-    .add('pair', () => PairSet.has(pairSet, idx % values.length))
-    .on('cycle', (e: any) => {
-        console.log(String(e.target));
-    })
-//    .run();
-
-console.log(IntPair.pack1(0, -20));
-console.log(IntPair.unpack1(IntPair.pack1(0, -20)));
-
+// import * as B from 'benchmark'
+// import OrderedSet from '../structure/collections/ordered-set'
+// import OrderedSet1 from '../structure/collections/ordered-set.1'
+
+// const range = OrderedSet.ofRange(0, 100);
+// const range1 = OrderedSet1.ofRange(0, 100);
+// // const pairSet = IntPair.pack1(0, 100);
+
+// // namespace PairSet {
+// //     const pair = IntPair.zero();
+// //     export function has(p: number, x: number) {
+// //         IntPair.unpack(p, pair);
+// //         return x >= pair.fst && x <= pair.snd;
+// //     }
+// // }
+
+// const suite = new B.Suite();
+
+// const values: number[] = [];
+// for (let i = 0; i < 1000000; i++) values[i] = (Math.random() * 1000) | 0;
+
+// let idx = 0;
+
+// suite
+//     .add('range', () => range.has(idx % values.length))
+//     .add('range1', () => OrderedSet1.has(range1, idx % values.length))
+//     .on('cycle', (e: any) => {
+//         console.log(String(e.target));
+//     })
+//     .run();

+ 38 - 36
src/structure/collections/multi-set.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import OrderedSet from './ordered-set.1'
+import OrderedSet from './ordered-set'
 import Iterator from './iterator'
 import IntPair from './int-pair'
 import { sortArray } from './sort'
@@ -30,7 +30,7 @@ namespace MultiSet {
 
     export function hasKey(set: MultiSet, key: number): boolean {
         if (typeof set === 'number') return IntPair.fst(set) === key;
-        return set.keys.has(key);
+        return OrderedSet.has(set.keys, key);
     }
 
     export function get(set: MultiSet, key: number): OrderedSet {
@@ -102,6 +102,7 @@ namespace MultiSet {
         private pair = IntPair.zero();
         private unit = 0;
 
+        private keyCount: number;
         private setIndex = -1;
         private currentIndex = 0;
         private currentSize = 0;
@@ -125,7 +126,7 @@ namespace MultiSet {
 
         private advance() {
             const keys = this.elements.keys;
-            if (++this.setIndex >= keys.size) {
+            if (++this.setIndex >= this.keyCount) {
                 this.done = true;
                 return false;
             }
@@ -133,12 +134,13 @@ namespace MultiSet {
             this.pair.fst = this.unit;
             this.currentSet = this.elements[this.unit];
             this.currentIndex = 0;
-            this.currentSize = this.currentSet.size;
+            this.currentSize = OrderedSet.size(this.currentSet);
             return true;
         }
 
         constructor(private elements: MultiSetElements) {
-            this.done = elements.keys.size === 0;
+            this.keyCount = OrderedSet.size(elements.keys);
+            this.done = this.keyCount === 0;
             this.advance();
         }
     }
@@ -159,12 +161,12 @@ function ofObject(data: { [id: number]: OrderedSet }) {
     const keys = [];
     for (const _k of Object.keys(data)) {
         const k = +_k;
-        if (data[k].size > 0) keys[keys.length] = k;
+        if (OrderedSet.size(data[k]) > 0) keys[keys.length] = k;
     }
     if (!keys.length) return MultiSet.Empty;
     if (keys.length === 1) {
         const set = data[keys[0]];
-        if (set.size === 1) return IntPair.pack1(keys[0], set.elementAt(0));
+        if (OrderedSet.size(set) === 1) return IntPair.pack1(keys[0], OrderedSet.elementAt(set, 0));
     }
     return ofObject1(keys, data);
 }
@@ -173,17 +175,17 @@ function ofObject1(keys: number[], data: { [id: number]: OrderedSet }) {
     if (keys.length === 1) {
         const k = keys[0];
         const set = data[k];
-        if (set.size === 1) return IntPair.pack1(k, set.elementAt(0));
+        if (OrderedSet.size(set) === 1) return IntPair.pack1(k, OrderedSet.elementAt(set, 0));
     }
     sortArray(keys);
     return _createObjectOrdered(OrderedSet.ofSortedArray(keys), data);
 }
 
 function ofObjectOrdered(keys: OrderedSet, data: { [id: number]: OrderedSet }) {
-    if (keys.size === 1) {
-        const k = keys.elementAt(0);
+    if (OrderedSet.size(keys) === 1) {
+        const k = OrderedSet.elementAt(keys, 0);
         const set = data[k];
-        if (set.size === 1) return IntPair.pack1(k, set.elementAt(0));
+        if (OrderedSet.size(set) === 1) return IntPair.pack1(k, OrderedSet.elementAt(set, 0));
     }
     return _createObjectOrdered(keys, data);
 }
@@ -192,11 +194,11 @@ function _createObjectOrdered(keys: OrderedSet, data: { [id: number]: OrderedSet
     const ret: MultiSetElements = Object.create(null);
     ret.keys = keys;
     let size = 0;
-    for (let i = 0, _i = keys.size; i < _i; i++) {
-        const k = keys.elementAt(i);
+    for (let i = 0, _i = OrderedSet.size(keys); i < _i; i++) {
+        const k = OrderedSet.elementAt(keys, i);
         const set = data[k];
         ret[k] = set;
-        size += set.size;
+        size += OrderedSet.size(set);
     }
     ret.size = size;
     ret.hashCode = -1;
@@ -248,8 +250,8 @@ function ofPackedPairs(xs: ArrayLike<number>): MultiSet {
 function computeHash(set: MultiSetElements) {
     const { keys } = set;
     let hash = 23;
-    for (let i = 0, _i = keys.size; i < _i; i++) {
-        const k = keys.elementAt(i);
+    for (let i = 0, _i = OrderedSet.size(keys); i < _i; i++) {
+        const k = OrderedSet.elementAt(keys, i);
         hash = (31 * hash + k) | 0;
         hash = (31 * hash + OrderedSet.hashCode(set[k])) | 0;
     }
@@ -265,8 +267,8 @@ function areEqualEE(a: MultiSetElements, b: MultiSetElements) {
 
     const keys = a.keys;
     if (!OrderedSet.areEqual(keys, b.keys)) return false;
-    for (let i = 0, _i = keys.size; i < _i; i++) {
-        const k = keys.elementAt(i);
+    for (let i = 0, _i = OrderedSet.size(keys); i < _i; i++) {
+        const k = OrderedSet.elementAt(keys, i);
         if (!OrderedSet.areEqual(a[k], b[k])) return false;
     }
     return true;
@@ -274,7 +276,7 @@ function areEqualEE(a: MultiSetElements, b: MultiSetElements) {
 
 function areIntersectingNE(a: number, b: MultiSetElements) {
     IntPair.unpack(a, pair);
-    return b.keys.has(pair.fst) && b[pair.fst].has(pair.snd);
+    return OrderedSet.has(b.keys, pair.fst) && OrderedSet.has(b[pair.fst], pair.snd);
 }
 
 function areIntersectingEE(a: MultiSetElements, b: MultiSetElements) {
@@ -283,15 +285,15 @@ function areIntersectingEE(a: MultiSetElements, b: MultiSetElements) {
     if (!OrderedSet.areIntersecting(a.keys, b.keys)) return false;
     const { start, end } = OrderedSet.getIntervalRange(keysA, OrderedSet.min(keysB), OrderedSet.max(keysB));
     for (let i = start; i < end; i++) {
-        const k = keysA.elementAt(i);
-        if (keysB.has(k) && OrderedSet.areIntersecting(a[k], b[k])) return true;
+        const k = OrderedSet.elementAt(keysA, i);
+        if (OrderedSet.has(keysB, k) && OrderedSet.areIntersecting(a[k], b[k])) return true;
     }
     return false;
 }
 
 function intersectNE(a: number, b: MultiSetElements) {
     IntPair.unpack(a, pair);
-    return b.keys.has(pair.fst) && b[pair.fst].has(pair.snd) ? a : MultiSet.Empty;
+    return OrderedSet.has(b.keys, pair.fst) && OrderedSet.has(b[pair.fst], pair.snd) ? a : MultiSet.Empty;
 }
 
 function intersectEE(a: MultiSetElements, b: MultiSetElements) {
@@ -303,10 +305,10 @@ function intersectEE(a: MultiSetElements, b: MultiSetElements) {
 
     const keys = [], ret = Object.create(null);
     for (let i = start; i < end; i++) {
-        const k = keysA.elementAt(i);
-        if (keysB.has(k)) {
+        const k = OrderedSet.elementAt(keysA, i);
+        if (OrderedSet.has(keysB, k)) {
             const intersection = OrderedSet.intersect(a[k], b[k]);
-            if (intersection.size > 0) {
+            if (OrderedSet.size(intersection) > 0) {
                 keys[keys.length] = k;
                 ret[k] = intersection;
             }
@@ -317,15 +319,15 @@ function intersectEE(a: MultiSetElements, b: MultiSetElements) {
 
 function subtractNE(a: number, b: MultiSetElements) {
     IntPair.unpack(a, pair);
-    return b.keys.has(pair.fst) && b[pair.fst].has(pair.snd) ? MultiSet.Empty : a;
+    return OrderedSet.has(b.keys, pair.fst) && OrderedSet.has(b[pair.fst], pair.snd) ? MultiSet.Empty : a;
 }
 
 function subtractEN(a: MultiSetElements, b: number): MultiSet {
     const aKeys =  a.keys;
     IntPair.unpack(b, pair);
-    if (!aKeys.has(pair.fst) || !a[pair.fst].has(pair.snd)) return a;
+    if (!OrderedSet.has(aKeys, pair.fst) || !OrderedSet.has(a[pair.fst], pair.snd)) return a;
     const set = a[pair.fst];
-    if (set.size === 1) {
+    if (OrderedSet.size(set) === 1) {
         return ofObjectOrdered(OrderedSet.subtract(a.keys, OrderedSet.ofSingleton(pair.fst)), a);
     } else {
         return { ...a, [pair.fst]: OrderedSet.subtract(set, OrderedSet.ofSingleton(pair.snd)), size: a.size - 1, hashCode: -1 }
@@ -341,15 +343,15 @@ function subtractEE(a: MultiSetElements, b: MultiSetElements) {
 
     const keys = [], ret = Object.create(null);
     for (let i = 0; i < start; i++) {
-        const k = keysA.elementAt(i);
+        const k = OrderedSet.elementAt(keysA, i);
         keys[keys.length] = k;
         ret[k] = a[k];
     }
     for (let i = start; i < end; i++) {
-        const k = keysA.elementAt(i);
-        if (keysB.has(k)) {
+        const k = OrderedSet.elementAt(keysA, i);
+        if (OrderedSet.has(keysB, k)) {
             const subtraction = OrderedSet.subtract(a[k], b[k]);
-            if (subtraction.size > 0) {
+            if (OrderedSet.size(subtraction) > 0) {
                 keys[keys.length] = k;
                 ret[k] = subtraction;
             }
@@ -358,8 +360,8 @@ function subtractEE(a: MultiSetElements, b: MultiSetElements) {
             ret[k] = a[k];
         }
     }
-    for (let i = end, _i = keysA.size; i < _i; i++) {
-        const k = keysA.elementAt(i);
+    for (let i = end, _i = OrderedSet.size(keysA); i < _i; i++) {
+        const k = OrderedSet.elementAt(keysA, i);
         keys[keys.length] = k;
         ret[k] = a[k];
     }
@@ -406,8 +408,8 @@ function unionN(sets: ArrayLike<MultiSet>, eCount: { count: number }) {
 
 function unionInto(data: { [key: number]: OrderedSet }, a: MultiSetElements) {
     const keys = a.keys;
-    for (let i = 0, _i = keys.size; i < _i; i++) {
-        const k = keys.elementAt(i);
+    for (let i = 0, _i = OrderedSet.size(keys); i < _i; i++) {
+        const k = OrderedSet.elementAt(keys, i);
         const set = data[k];
         if (set) data[k] = OrderedSet.union(set, a[k]);
         else data[k] = a[k];

+ 0 - 476
src/structure/collections/ordered-set.1.ts

@@ -1,476 +0,0 @@
-/**
- * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import IntPair from './int-pair'
-import { hash3, hash4 } from './hash-functions'
-
-type Nums = ArrayLike<number>
-type OrderedSet = number | Nums
-
-/** An immutable ordered set. */
-namespace OrderedSet {
-    export function ofSingleton(value: number): OrderedSet { return IntPair.pack1(value, value); }
-    export function ofRange(min: number, max: number): OrderedSet { return max < min ? Empty : IntPair.pack1(min, max); }
-    /** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */
-    export function ofSortedArray(xs: Nums): OrderedSet {
-        if (!xs.length) return Empty;
-        // 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]);
-        return xs;
-    }
-    export const Empty: OrderedSet = IntPair.pack1(0, -1);
-
-    export function size(set: OrderedSet) { return typeof set === 'number' ? sizeR(set) : set.length; }
-    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); }
-    export function elementAt(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]; }
-    export function max(set: OrderedSet) { return typeof set === 'number' ? maxR(set) : set[set.length - 1]; }
-
-    export function hashCode(set: OrderedSet) {
-        // hash of tuple (size, min, max, mid)
-        const s = size(set);
-        if (!s) return 0;
-        if (s > 2) return hash4(s, elementAt(set, 0), elementAt(set, s - 1), elementAt(set, s >> 1));
-        return hash3(s, elementAt(set, 0), elementAt(set, s - 1));
-    }
-    // TODO: possibly add more hash functions to allow for multilevel hashing.
-
-    export function areEqual(a: OrderedSet, b: OrderedSet) {
-        if (typeof a === 'number') {
-            if (typeof b === 'number') return equalRR(a, b);
-            return false;
-        } else if (typeof b === 'number') return false;
-        else if (a === b) return true;
-        return equalAA(a, b);
-    }
-
-    export function areIntersecting(a: OrderedSet, b: OrderedSet) {
-        // if at least one is "range", they must now intersect
-        if (typeof a === 'number') {
-            if (typeof b === 'number') return equalRR(a, b) || areRangesIntersecting(a, b);
-            return areRangesIntersecting(a, b);
-        }
-        if (!areRangesIntersecting(a, b)) return false;
-        else if (typeof b === 'number') return false;
-        if (a === b) return true;
-        return areIntersectingAA(a, b);
-    }
-
-    /** Check if the 2nd argument is a subset of the 1st */
-    export function isSubset(set: OrderedSet, toTest: OrderedSet) {
-        if (set === toTest) return true;
-        if (!isRangeSubset(set, toTest)) return false;
-        const testSize = size(toTest);
-        if (typeof set === 'number' || !testSize) return true;
-        if (typeof toTest === 'number') return indexOf(set, maxR(toTest)) - indexOf(set, minR(toTest)) + 1 === testSize;
-        return isSubsetAA(set, toTest);
-    }
-
-    export function getInsertionIndex(set: OrderedSet, x: number) {
-        return typeof set === 'number' ? rangeSearchIndex(set, x) : binarySearchIndex(set, x);
-    }
-
-    export function getIntervalRange(set: OrderedSet, min: number, max: number) {
-        const { start, end } = getStartEnd(set, min, max);
-        return { start, end };
-    }
-
-    export function union(a: OrderedSet, b: OrderedSet) {
-        if (a === b) return a;
-        if (typeof a === 'number') {
-            if (typeof b === 'number') return unionRR(a, b);
-            return unionAR(b, a);
-        } else if (typeof b === 'number') {
-            return unionAR(a, b);
-        } else return unionAA(a, b);
-    }
-
-    export function intersect(a: OrderedSet, b: OrderedSet) {
-        if (a === b) return a;
-        if (typeof a === 'number') {
-            if (typeof b === 'number') return intersectRR(a, b);
-            return intersectAR(b, a);
-        } else if (typeof b === 'number') {
-            return intersectAR(a, b);
-        } else {
-            if (!areRangesIntersecting(a, b)) return Empty;
-            return intersectAA(a, b);
-        }
-    }
-
-    export function subtract(a: OrderedSet, b: OrderedSet) {
-        if (a === b) return Empty;
-        if (!areRangesIntersecting(a, b)) return a;
-
-        if (typeof a === 'number') {
-            if (typeof b === 'number') return substractRR(a, b);
-            return subtractRA(a, b);
-        } else if (typeof b === 'number') {
-            return subtractAR(a, b);
-        } else {
-            return subtractAA(a, b);
-        }
-    }
-}
-
-import S = OrderedSet
-
-const minR = IntPair.fst
-const maxR = IntPair.snd
-const equalRR = IntPair.areEqual
-
-const _eR = IntPair.zero();
-function sizeR(set: number) { IntPair.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 indexOfR(set: number, x: number) { IntPair.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 hasA(set: Nums, 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 binarySearch(xs: Nums, value: number) {
-    let min = 0, max = xs.length - 1;
-    while (min <= max) {
-        if (min + 11 > max) {
-            for (let i = min; i <= max; i++) {
-                if (value === xs[i]) return i;
-            }
-            return -1;
-        }
-
-        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;
-    }
-    return -1;
-}
-
-function binarySearchIndex(xs: Nums, 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 _rsiR = IntPair.zero();
-function rangeSearchIndex(r: number, value: number) {
-    IntPair.unpack(r, _rsiR);
-    if (value < _rsiR.fst) return 0;
-    if (value > _rsiR.snd) return _rsiR.snd - _rsiR.fst + 1;
-    return value - _rsiR.fst;
-}
-
-const _maxIntRangeRet = { i: 0, j: 0, endA: 0, endB: 0 };
-function getMaxIntersectionRange(xs: Nums, ys: Nums) {
-    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(set: OrderedSet, min: number, max: number) {
-    _startEndRet.start = S.getInsertionIndex(set, min);
-    let end = S.getInsertionIndex(set, max);
-    if (end < S.size(set) && S.elementAt(set, end) === max) end++;
-    _startEndRet.end = end;
-    return _startEndRet;
-}
-
-function equalAA(a: Nums, b: Nums) {
-    let size = a.length;
-    if (a.length !== b.length || a[0] !== b[0] || a[size - 1] !== b[size - 1]) return false;
-    for (let i = 0; i < size; i++) {
-        if (a[i] !== b[i]) return false;
-    }
-    return true;
-}
-
-function areIntersectingAA(xs: Nums, ys: Nums) {
-    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++; }
-        else return true;
-    }
-    return false;
-}
-
-function isSubsetAA(xs: Nums, ys: Nums) {
-    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 === lenB;
-}
-
-function areRangesIntersecting(a: OrderedSet, b: OrderedSet) {
-    return S.size(a) > 0 && S.size(b) > 0 && S.max(a) >= S.min(b) && S.min(a) <= S.max(b);
-}
-
-function isRangeSubset(a: OrderedSet, b: OrderedSet) {
-    if (!S.size(a)) return S.size(b) === 0;
-    if (!S.size(b)) return true;
-    return S.min(a) <= S.min(b) && S.max(a) >= S.max(b);
-}
-
-function unionRR(a: number, b: number) {
-    const sizeA = S.size(a), sizeB = S.size(b);
-    if (!sizeA) return b;
-    if (!sizeB) return a;
-    const minA = minR(a), minB = minR(b);
-    if (areRangesIntersecting(a, b)) return S.ofRange(Math.min(minA, minB), Math.max(maxR(a), maxR(b)));
-    let lSize, lMin, rSize, rMin;
-    if (minR(a) < minR(b)) { lSize = sizeA; lMin = minA; rSize = sizeB; rMin = minB; }
-    else { lSize = sizeB; lMin = minB; rSize = sizeA; rMin = minA; }
-    const arr = new Int32Array(sizeA + sizeB);
-    for (let i = 0; i < lSize; i++) arr[i] = i + lMin;
-    for (let i = 0; i < rSize; i++) arr[i + lSize] = i + rMin;
-    return S.ofSortedArray(arr);
-}
-
-const _uAR = IntPair.zero();
-function unionAR(a: Nums, b: number) {
-    const bSize = S.size(b);
-    if (!bSize) return a;
-    // is the array fully contained in the range?
-    if (isRangeSubset(b, a)) return b;
-
-    IntPair.unpack(b, _uAR);
-    const min = _uAR.fst, max = _uAR.snd;
-    const { start, end } = getStartEnd(a, min, max);
-
-    const size = start + (a.length - end) + bSize;
-    const indices = new Int32Array(size);
-    let offset = 0;
-    for (let i = 0; i < start; i++) indices[offset++] = a[i];
-    for (let i = min; i <= max; i++) indices[offset++] = i;
-    for (let i = end, _i = a.length; i < _i; i++) indices[offset] = a[i];
-
-    return OrderedSet.ofSortedArray(indices);
-}
-
-function unionAA(a: Nums, b: Nums) {
-    const lenA = a.length, lenB = b.length;
-
-    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
-    let i = sI, j = sJ;
-    let commonCount = 0;
-    while (i <= endA && j <= endB) {
-        const x = a[i], y = b[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 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 OrderedSet.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++] = a[k];
-
-    // insert the common part
-    i = sI;
-    j = sJ;
-    while (i <= endA && j <= endB) {
-        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 OrderedSet.ofSortedArray(indices);
-}
-
-const _iRA = IntPair.zero(), _iRB = IntPair.zero();
-function intersectRR(a: number, b: number) {
-    if (!areRangesIntersecting(a, b)) return OrderedSet.Empty;
-
-    IntPair.unpack(a, _iRA);
-    IntPair.unpack(b, _iRB);
-    return OrderedSet.ofRange(Math.max(_iRA.fst, _iRB.fst), Math.min(_iRA.snd, _iRB.snd));
-}
-
-const _iAR = IntPair.zero();
-function intersectAR(a: Nums, r: number) {
-    if (!S.size(r)) return OrderedSet.Empty;
-
-    IntPair.unpack(r, _iAR);
-    const { start, end } = getStartEnd(a, _iAR.fst, _iAR.snd);
-    const resultSize = end - start;
-    if (!resultSize) return OrderedSet.Empty;
-
-    const indices = new Int32Array(resultSize);
-    let offset = 0;
-    for (let i = start; i < end; i++) {
-        indices[offset++] = a[i];
-    }
-    return OrderedSet.ofSortedArray(indices);
-}
-
-function intersectAA(xs: Nums, ys: Nums) {
-    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++; }
-        else { i++; j++; resultSize++; }
-    }
-
-    if (!resultSize) return OrderedSet.Empty;
-
-    const indices = new Int32Array(resultSize);
-    let offset = 0;
-    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++; }
-        else { indices[offset++] = x; i++; j++; }
-    }
-
-    return OrderedSet.ofSortedArray(indices);
-}
-
-const _sRA = IntPair.zero(), _sRB = IntPair.zero();
-function substractRR(a: number, b: number) {
-    IntPair.unpack(a, _sRA);
-    IntPair.unpack(b, _sRB);
-
-    if (_sRA.snd < _sRA.fst || _sRB.snd < _sRB.fst) 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 = _sRB.fst - _sRA.fst, r = _sRA.snd - _sRB.snd;
-        const ret = new Int32Array(l + r);
-        let offset = 0;
-        for (let i = 0; i < l; i++) ret[offset++] = _sRA.fst + i;
-        for (let i = 1; i <= r; i++) ret[offset++] = _sRB.snd + i;
-        return OrderedSet.ofSortedArray(ret);
-    }
-    // non intersecting ranges are handled by top-level substract.
-    // at this point, b either contains rA.fst or rA.snd, but not both.
-    if (_sRA.fst < _sRB.fst) return OrderedSet.ofRange(_sRA.fst, _sRB.fst - 1);
-    return OrderedSet.ofRange(_sRB.snd + 1, _sRA.snd);
-}
-
-const _sAR = IntPair.zero();
-function subtractAR(a: Nums, r: number) {
-    IntPair.unpack(r, _sAR);
-    if (_sAR.snd < _sAR.fst) return a;
-
-    const min = _sAR.fst, max = _sAR.snd;
-    const { start, end } = getStartEnd(a, min, max);
-    const size = a.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++] = a[i];
-    for (let i = end, _i = a.length; i < _i; i++) ret[offset++] = a[i];
-    return OrderedSet.ofSortedArray(ret);
-}
-
-const _sAR1 = IntPair.zero();
-function subtractRA(r: number, b: Nums) {
-    IntPair.unpack(r, _sAR1);
-    if (_sAR1.snd < _sAR1.fst) return r;
-
-    const min = _sAR1.fst, max = _sAR1.snd;
-    const rSize = max - min + 1;
-    const { start, end } = getStartEnd(b, min, max);
-    const commonCount = end - start;
-    const resultSize = rSize - commonCount;
-    if (resultSize <= 0) return OrderedSet.Empty;
-    const ret = new Int32Array(resultSize);
-    const li = b.length - 1;
-    const fst = b[Math.min(start, li)], last = b[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(b, i) < 0) ret[offset++] = i;
-    }
-    for (let i = last + 1; i <= max; i++) ret[offset++] = i;
-    return OrderedSet.ofSortedArray(ret);
-}
-
-function subtractAA(a: Nums, b: Nums) {
-    const lenA = a.length;
-
-    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
-    let i = sI, j = sJ;
-    let commonCount = 0;
-    while (i <= endA && j <= endB) {
-        const x = a[i], y = b[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++] = a[k];
-
-    i = sI;
-    j = sJ;
-    while (i <= endA && j <= endB) {
-        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 OrderedSet.ofSortedArray(indices);
-}
-
-export default OrderedSet

+ 190 - 197
src/structure/collections/ordered-set.ts

@@ -4,127 +4,101 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import IntPair from './int-pair'
 import { hash3, hash4 } from './hash-functions'
 
-/** An immutable ordered set. */
-interface OrderedSet {
-    readonly size: number,
-    has(x: number): boolean,
-    indexOf(x: number): number,
-    elementAt(i: number): number
-}
-
-interface Impl extends OrderedSet {
-    readonly min: number,
-    readonly max: number
-}
-
-class RangeImpl implements Impl {
-    size: number;
-    has(x: number) { return x >= this.min && x <= this.max; }
-    indexOf(x: number) { return x >= this.min && x <= this.max ? x - this.min : -1; }
-    elementAt(i: number) { return this.min + i; }
-
-    constructor(public min: number, public max: number) {
-        this.size = max - min + 1;
-    }
-}
-
-class ArrayImpl implements Impl {
-    size: number;
-    public min: number;
-    public max: number;
-    has(x: number) { return x >= this.min && x <= this.max && binarySearch(this.values, x) >= 0; }
-    indexOf(x: number) { return x >= this.min && x <= this.max ? binarySearch(this.values, x) : -1; }
-    elementAt(i: number) { return this.values[i]; }
-
-    constructor(public values: ArrayLike<number>) {
-        this.min = values[0];
-        this.max = values[values.length - 1];
-        this.size = values.length;
-    }
-}
+type Nums = ArrayLike<number>
+type OrderedSet = number | Nums
 
+/** An immutable ordered set. */
 namespace OrderedSet {
-    export function ofSingleton(value: number): OrderedSet { return new RangeImpl(value, value); }
-    export function ofRange(min: number, max: number): OrderedSet { return max < min ? Empty : new RangeImpl(min, max); }
+    export function ofSingleton(value: number): OrderedSet { return IntPair.pack1(value, value); }
+    export function ofRange(min: number, max: number): OrderedSet { return max < min ? Empty : IntPair.pack1(min, max); }
     /** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */
-    export function ofSortedArray(xs: ArrayLike<number>): OrderedSet {
+    export function ofSortedArray(xs: Nums): OrderedSet {
         if (!xs.length) return Empty;
         // 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]);
-        return new ArrayImpl(xs);
+        return xs;
     }
-    export const Empty: OrderedSet = new RangeImpl(0, -1);
+    export const Empty: OrderedSet = IntPair.pack1(0, -1);
 
-    export function isEmpty(a: OrderedSet) { return a.size === 0; }
-    export function min(a: OrderedSet) { return (a as Impl).min; }
-    export function max(a: OrderedSet) { return (a as Impl).max; }
+    export function size(set: OrderedSet) { return typeof set === 'number' ? sizeR(set) : set.length; }
+    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); }
+    export function elementAt(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]; }
+    export function max(set: OrderedSet) { return typeof set === 'number' ? maxR(set) : set[set.length - 1]; }
 
-    export function hashCode(a: OrderedSet) {
+    export function hashCode(set: OrderedSet) {
         // hash of tuple (size, min, max, mid)
-        const { size } = a;
-        if (!size) return 0;
-        if (size > 2) return hash4(size, a.elementAt(0), a.elementAt(size - 1), a.elementAt(size >> 0));
-        return hash3(size, a.elementAt(0), a.elementAt(size - 1));
+        const s = size(set);
+        if (!s) return 0;
+        if (s > 2) return hash4(s, elementAt(set, 0), elementAt(set, s - 1), elementAt(set, s >> 1));
+        return hash3(s, elementAt(set, 0), elementAt(set, s - 1));
     }
     // TODO: possibly add more hash functions to allow for multilevel hashing.
 
     export function areEqual(a: OrderedSet, b: OrderedSet) {
-        if (a === b) return true;
-        if (a instanceof RangeImpl) {
-            if (b instanceof RangeImpl) return a.min === b.min && a.max === b.max;
-            return equalAR(b as ArrayImpl, a);
-        } else if (b instanceof RangeImpl) {
-            return equalAR(a as ArrayImpl, b);
-        }
-        return equalAA(a as ArrayImpl, b as ArrayImpl);
+        if (typeof a === 'number') {
+            if (typeof b === 'number') return equalRR(a, b);
+            return false;
+        } else if (typeof b === 'number') return false;
+        else if (a === b) return true;
+        return equalAA(a, b);
     }
 
     export function areIntersecting(a: OrderedSet, b: OrderedSet) {
-        if (a === b) return true;
-        if (!areRangesIntersecting(a, b)) return false;
         // if at least one is "range", they must now intersect
-        if (a instanceof RangeImpl || b instanceof RangeImpl) return true;
-        return areIntersectingAA((a as ArrayImpl).values, (b as ArrayImpl).values);
+        if (typeof a === 'number') {
+            if (typeof b === 'number') return equalRR(a, b) || areRangesIntersecting(a, b);
+            return areRangesIntersecting(a, b);
+        }
+        if (!areRangesIntersecting(a, b)) return false;
+        else if (typeof b === 'number') return false;
+        if (a === b) return true;
+        return areIntersectingAA(a, b);
     }
 
     /** Check if the 2nd argument is a subset of the 1st */
-    export function isSubset(a: OrderedSet, toTest: OrderedSet) {
-        if (a === toTest) return true;
-        if (!isRangeSubset(a, toTest)) return false;
-        if (!toTest.size || a instanceof RangeImpl) return true;
-        if (toTest instanceof RangeImpl) return a.indexOf(max(toTest)) - a.indexOf(min(toTest)) + 1 === toTest.size;
-        return isSubsetAA((a as ArrayImpl).values, (toTest as ArrayImpl).values);
+    export function isSubset(set: OrderedSet, toTest: OrderedSet) {
+        if (set === toTest) return true;
+        if (!isRangeSubset(set, toTest)) return false;
+        const testSize = size(toTest);
+        if (typeof set === 'number' || !testSize) return true;
+        if (typeof toTest === 'number') return indexOf(set, maxR(toTest)) - indexOf(set, minR(toTest)) + 1 === testSize;
+        return isSubsetAA(set, toTest);
+    }
+
+    export function getInsertionIndex(set: OrderedSet, x: number) {
+        return typeof set === 'number' ? rangeSearchIndex(set, x) : binarySearchIndex(set, x);
     }
 
-    export function getIntervalRange(a: OrderedSet, min: number, max: number) {
-        const { start, end } = a instanceof RangeImpl
-            ? getStartEndR(a, min, max)
-            : getStartEndA((a as ArrayImpl).values, min, max);
+    export function getIntervalRange(set: OrderedSet, min: number, max: number) {
+        const { start, end } = getStartEnd(set, min, max);
         return { start, end };
     }
 
     export function union(a: OrderedSet, b: OrderedSet) {
         if (a === b) return a;
-        if (a instanceof RangeImpl) {
-            if (b instanceof RangeImpl) return unionRR(a, b);
-            return unionAR(b as ArrayImpl, a);
-        } else if (b instanceof RangeImpl) {
-            return unionAR(a as ArrayImpl, b);
-        } else return unionAA(a as ArrayImpl, b as ArrayImpl);
+        if (typeof a === 'number') {
+            if (typeof b === 'number') return unionRR(a, b);
+            return unionAR(b, a);
+        } else if (typeof b === 'number') {
+            return unionAR(a, b);
+        } else return unionAA(a, b);
     }
 
     export function intersect(a: OrderedSet, b: OrderedSet) {
         if (a === b) return a;
-        if (a instanceof RangeImpl) {
-            if (b instanceof RangeImpl) return intersectRR(a, b);
-            return intersectAR(b as ArrayImpl, a);
-        } else if (b instanceof RangeImpl) {
-            return intersectAR(a as ArrayImpl, b);
+        if (typeof a === 'number') {
+            if (typeof b === 'number') return intersectRR(a, b);
+            return intersectAR(b, a);
+        } else if (typeof b === 'number') {
+            return intersectAR(a, b);
         } else {
             if (!areRangesIntersecting(a, b)) return Empty;
-            return intersectAA((a as ArrayImpl).values, (b as ArrayImpl).values);
+            return intersectAA(a, b);
         }
     }
 
@@ -132,21 +106,33 @@ namespace OrderedSet {
         if (a === b) return Empty;
         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);
+        if (typeof a === 'number') {
+            if (typeof b === 'number') return substractRR(a, b);
+            return subtractRA(a, b);
+        } else if (typeof b === 'number') {
+            return subtractAR(a, b);
         } else {
-            return subtractAA(a as ArrayImpl, b as ArrayImpl);
+            return subtractAA(a, b);
         }
     }
 }
 
-function min(a: OrderedSet) { return (a as Impl).min; }
-function max(a: OrderedSet) { return (a as Impl).max; }
+import S = OrderedSet
+
+const minR = IntPair.fst
+const maxR = IntPair.snd
+const equalRR = IntPair.areEqual
+
+const _eR = IntPair.zero();
+function sizeR(set: number) { IntPair.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 indexOfR(set: number, x: number) { IntPair.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 binarySearch(xs: ArrayLike<number>, value: number) {
+function hasA(set: Nums, 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 binarySearch(xs: Nums, value: number) {
     let min = 0, max = xs.length - 1;
     while (min <= max) {
         if (min + 11 > max) {
@@ -165,7 +151,7 @@ function binarySearch(xs: ArrayLike<number>, value: number) {
     return -1;
 }
 
-function binarySearchIndex(xs: ArrayLike<number>, value: number) {
+function binarySearchIndex(xs: Nums, value: number) {
     let min = 0, max = xs.length - 1;
     while (min < max) {
         const mid = (min + max) >> 1;
@@ -178,8 +164,16 @@ function binarySearchIndex(xs: ArrayLike<number>, value: number) {
     return xs[min] >= value ? min : min + 1;
 }
 
+const _rsiR = IntPair.zero();
+function rangeSearchIndex(r: number, value: number) {
+    IntPair.unpack(r, _rsiR);
+    if (value < _rsiR.fst) return 0;
+    if (value > _rsiR.snd) return _rsiR.snd - _rsiR.fst + 1;
+    return value - _rsiR.fst;
+}
+
 const _maxIntRangeRet = { i: 0, j: 0, endA: 0, endB: 0 };
-function getMaxIntersectionRange(xs: ArrayLike<number>, ys: ArrayLike<number>) {
+function getMaxIntersectionRange(xs: Nums, ys: Nums) {
     const la = xs.length - 1, lb = ys.length - 1;
     _maxIntRangeRet.i = binarySearchIndex(xs, ys[0]);
     _maxIntRangeRet.j = binarySearchIndex(ys, xs[0]);
@@ -190,40 +184,24 @@ function getMaxIntersectionRange(xs: ArrayLike<number>, ys: ArrayLike<number>) {
 
 const _startEndRet = { start: 0, end: 0 };
 
-function getStartEndR(r: RangeImpl, min: number, max: number) {
-    if (max < min) {
-        _startEndRet.start = 0;
-        _startEndRet.end = 0;
-        return _startEndRet;
-    }
-    _startEndRet.start = min <= r.max ? Math.max(r.min, min) - r.min : r.size;
-    _startEndRet.end = max >= r.min ? Math.min(r.max, max) - r.min + 1 : r.size;
-    return _startEndRet;
-}
-
-function getStartEndA(xs: ArrayLike<number>, min: number, max: number) {
-    _startEndRet.start = binarySearchIndex(xs, min);
-    let end = binarySearchIndex(xs, max);
-    if (xs[end] === max) end++;
+function getStartEnd(set: OrderedSet, min: number, max: number) {
+    _startEndRet.start = S.getInsertionIndex(set, min);
+    let end = S.getInsertionIndex(set, max);
+    if (end < S.size(set) && S.elementAt(set, 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;
-}
-
-function equalAA(a: ArrayImpl, b: ArrayImpl) {
-    if (a.size !== b.size || a.min !== b.min || a.max !== b.max) return false;
-    const { size, values: xs } = a;
-    const { values: ys } = b;
+function equalAA(a: Nums, b: Nums) {
+    let size = a.length;
+    if (a.length !== b.length || a[0] !== b[0] || a[size - 1] !== b[size - 1]) return false;
     for (let i = 0; i < size; i++) {
-        if (xs[i] !== ys[i]) return false;
+        if (a[i] !== b[i]) return false;
     }
     return true;
 }
 
-function areIntersectingAA(xs: ArrayLike<number>, ys: ArrayLike<number>) {
+function areIntersectingAA(xs: Nums, ys: Nums) {
     let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys);
     while (i <= endA && j <= endB) {
         const x = xs[i], y = ys[j];
@@ -234,7 +212,7 @@ function areIntersectingAA(xs: ArrayLike<number>, ys: ArrayLike<number>) {
     return false;
 }
 
-function isSubsetAA(xs: ArrayLike<number>, ys: ArrayLike<number>) {
+function isSubsetAA(xs: Nums, ys: Nums) {
     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
@@ -251,56 +229,59 @@ function isSubsetAA(xs: ArrayLike<number>, ys: ArrayLike<number>) {
 }
 
 function areRangesIntersecting(a: OrderedSet, b: OrderedSet) {
-    return a.size > 0 && b.size > 0 && max(a) >= min(b) && min(a) <= max(b);
+    return S.size(a) > 0 && S.size(b) > 0 && S.max(a) >= S.min(b) && S.min(a) <= S.max(b);
 }
 
 function isRangeSubset(a: OrderedSet, b: OrderedSet) {
-    if (!a.size) return b.size === 0;
-    if (!b.size) return true;
-    return min(a) <= min(b) && max(a) >= max(b);
+    if (!S.size(a)) return S.size(b) === 0;
+    if (!S.size(b)) return true;
+    return S.min(a) <= S.min(b) && S.max(a) >= S.max(b);
 }
 
-function unionRR(a: RangeImpl, b: RangeImpl) {
-    if (!a.size) return b;
-    if (!b.size) return a;
-    if (areRangesIntersecting(a, b)) return OrderedSet.ofRange(Math.min(a.min, b.min), Math.max(a.max, b.max));
-    let l, r;
-    if (a.min < b.min) { l = a; r = b; }
-    else { l = b; r = a; }
-    const arr = new Int32Array(a.size + b.size);
-    for (let i = 0; i < l.size; i++) arr[i] = i + l.min;
-    for (let i = 0; i < r.size; i++) arr[i + l.size] = i + r.min;
-    return OrderedSet.ofSortedArray(arr);
+function unionRR(a: number, b: number) {
+    const sizeA = S.size(a), sizeB = S.size(b);
+    if (!sizeA) return b;
+    if (!sizeB) return a;
+    const minA = minR(a), minB = minR(b);
+    if (areRangesIntersecting(a, b)) return S.ofRange(Math.min(minA, minB), Math.max(maxR(a), maxR(b)));
+    let lSize, lMin, rSize, rMin;
+    if (minR(a) < minR(b)) { lSize = sizeA; lMin = minA; rSize = sizeB; rMin = minB; }
+    else { lSize = sizeB; lMin = minB; rSize = sizeA; rMin = minA; }
+    const arr = new Int32Array(sizeA + sizeB);
+    for (let i = 0; i < lSize; i++) arr[i] = i + lMin;
+    for (let i = 0; i < rSize; i++) arr[i + lSize] = i + rMin;
+    return S.ofSortedArray(arr);
 }
 
-function unionAR(a: ArrayImpl, b: RangeImpl) {
-    if (!b.size) return a;
+const _uAR = IntPair.zero();
+function unionAR(a: Nums, b: number) {
+    const bSize = S.size(b);
+    if (!bSize) return a;
     // is the array fully contained in the range?
-    if (a.min >= b.min && a.max <= b.max) return b;
+    if (isRangeSubset(b, a)) return b;
 
-    const xs = a.values;
-    const { min, max } = b;
-    const { start, end } = getStartEndA(xs, min, max);
+    IntPair.unpack(b, _uAR);
+    const min = _uAR.fst, max = _uAR.snd;
+    const { start, end } = getStartEnd(a, min, max);
 
-    const size = start + (xs.length - end) + b.size;
+    const size = start + (a.length - end) + bSize;
     const indices = new Int32Array(size);
     let offset = 0;
-    for (let i = 0; i < start; i++) indices[offset++] = xs[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 = end, _i = xs.length; i < _i; i++) indices[offset] = xs[i];
+    for (let i = end, _i = a.length; i < _i; i++) indices[offset] = a[i];
 
     return OrderedSet.ofSortedArray(indices);
 }
 
-function unionAA(a: ArrayImpl, b: ArrayImpl) {
-    const xs = a.values, ys = b.values;
-    const lenA = xs.length, lenB = ys.length;
+function unionAA(a: Nums, b: Nums) {
+    const lenA = a.length, lenB = b.length;
 
-    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys);
+    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
     let i = sI, j = sJ;
     let commonCount = 0;
     while (i <= endA && j <= endB) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else { i++; j++; commonCount++; }
@@ -310,7 +291,7 @@ function unionAA(a: ArrayImpl, b: ArrayImpl) {
     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));
+    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 OrderedSet.ofRange(l, r);
@@ -320,48 +301,53 @@ function unionAA(a: ArrayImpl, b: ArrayImpl) {
     let offset = 0;
 
     // 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];
+    for (let k = 0; k < sI; k++) indices[offset++] = a[k];
+    for (let k = 0; k < sJ; k++) indices[offset++] = a[k];
 
     // insert the common part
     i = sI;
     j = sJ;
     while (i <= endA && j <= endB) {
-        const x = xs[i], y = ys[j];
+        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++] = xs[i];
-    for (; j < lenB; j++) indices[offset++] = ys[j];
+    for (; i < lenA; i++) indices[offset++] = a[i];
+    for (; j < lenB; j++) indices[offset++] = b[j];
 
     return OrderedSet.ofSortedArray(indices);
 }
 
-function intersectRR(a: RangeImpl, b: RangeImpl) {
+const _iRA = IntPair.zero(), _iRB = IntPair.zero();
+function intersectRR(a: number, b: number) {
     if (!areRangesIntersecting(a, b)) return OrderedSet.Empty;
-    return OrderedSet.ofRange(Math.max(a.min, b.min), Math.min(a.max, b.max));
+
+    IntPair.unpack(a, _iRA);
+    IntPair.unpack(b, _iRB);
+    return OrderedSet.ofRange(Math.max(_iRA.fst, _iRB.fst), Math.min(_iRA.snd, _iRB.snd));
 }
 
-function intersectAR(a: ArrayImpl, r: RangeImpl) {
-    if (!r.size) return OrderedSet.Empty;
+const _iAR = IntPair.zero();
+function intersectAR(a: Nums, r: number) {
+    if (!S.size(r)) return OrderedSet.Empty;
 
-    const xs = a.values;
-    const { start, end } = getStartEndA(xs, r.min, r.max);
+    IntPair.unpack(r, _iAR);
+    const { start, end } = getStartEnd(a, _iAR.fst, _iAR.snd);
     const resultSize = end - start;
     if (!resultSize) return OrderedSet.Empty;
 
     const indices = new Int32Array(resultSize);
     let offset = 0;
     for (let i = start; i < end; i++) {
-        indices[offset++] = xs[i];
+        indices[offset++] = a[i];
     }
     return OrderedSet.ofSortedArray(indices);
 }
 
-function intersectAA(xs: ArrayLike<number>, ys: ArrayLike<number>) {
+function intersectAA(xs: Nums, ys: Nums) {
     let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys);
     let i = sI, j = sJ;
     let resultSize = 0;
@@ -388,69 +374,76 @@ 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;
+const _sRA = IntPair.zero(), _sRB = IntPair.zero();
+function substractRR(a: number, b: number) {
+    IntPair.unpack(a, _sRA);
+    IntPair.unpack(b, _sRB);
+
+    if (_sRA.snd < _sRA.fst || _sRB.snd < _sRB.fst) 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 l = _sRB.fst - _sRA.fst, r = _sRA.snd - _sRB.snd;
         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;
+        for (let i = 0; i < l; i++) ret[offset++] = _sRA.fst + i;
+        for (let i = 1; i <= r; i++) ret[offset++] = _sRB.snd + 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);
+    // at this point, b either contains rA.fst or rA.snd, but not both.
+    if (_sRA.fst < _sRB.fst) return OrderedSet.ofRange(_sRA.fst, _sRB.fst - 1);
+    return OrderedSet.ofRange(_sRB.snd + 1, _sRA.snd);
 }
 
-function subtractAR(a: ArrayImpl, r: RangeImpl) {
-    if (!r.size) return a;
+const _sAR = IntPair.zero();
+function subtractAR(a: Nums, r: number) {
+    IntPair.unpack(r, _sAR);
+    if (_sAR.snd < _sAR.fst) return a;
 
-    const xs = a.values;
-    const { min, max } = r;
-    const { start, end } = getStartEndA(xs, min, max);
-    const size = xs.length - (end - start);
+    const min = _sAR.fst, max = _sAR.snd;
+    const { start, end } = getStartEnd(a, min, max);
+    const size = a.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];
+    for (let i = 0; i < start; i++) ret[offset++] = a[i];
+    for (let i = end, _i = a.length; i < _i; i++) ret[offset++] = a[i];
     return OrderedSet.ofSortedArray(ret);
 }
 
-function subtractRA(r: RangeImpl, ys: ArrayLike<number>) {
-    if (!r.size) return r;
+const _sAR1 = IntPair.zero();
+function subtractRA(r: number, b: Nums) {
+    IntPair.unpack(r, _sAR1);
+    if (_sAR1.snd < _sAR1.fst) return r;
 
-    const { min, max } = r;
-    const { start, end } = getStartEndA(ys, min, max);
+    const min = _sAR1.fst, max = _sAR1.snd;
+    const rSize = max - min + 1;
+    const { start, end } = getStartEnd(b, min, max);
     const commonCount = end - start;
-    const resultSize = r.size - commonCount;
+    const resultSize = rSize - 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)];
+    const li = b.length - 1;
+    const fst = b[Math.min(start, li)], last = b[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;
+        if (binarySearch(b, 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;
+function subtractAA(a: Nums, b: Nums) {
+    const lenA = a.length;
 
-    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys);
+    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
     let i = sI, j = sJ;
     let commonCount = 0;
     while (i <= endA && j <= endB) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else { i++; j++; commonCount++; }
@@ -463,19 +456,19 @@ function subtractAA(a: ArrayImpl, b: ArrayImpl) {
     let offset = 0;
 
     // insert the "prefix"
-    for (let k = 0; k < sI; k++) indices[offset++] = xs[k];
+    for (let k = 0; k < sI; k++) indices[offset++] = a[k];
 
     i = sI;
     j = sJ;
     while (i <= endA && j <= endB) {
-        const x = xs[i], y = ys[j];
+        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++] = xs[i];
+    for (; i < lenA; i++) indices[offset++] = a[i];
 
     return OrderedSet.ofSortedArray(indices);
 }

+ 23 - 168
src/structure/spec/collections.spec.ts

@@ -8,7 +8,6 @@ import Iterator from '../collections/iterator'
 import IntPair from '../collections/int-pair'
 import * as Sort from '../collections/sort'
 import OrderedSet from '../collections/ordered-set'
-import OrderedSet1 from '../collections/ordered-set.1'
 import LinkedIndex from '../collections/linked-index'
 import MultiSet from '../collections/multi-set'
 
@@ -129,13 +128,13 @@ describe('qsort-dual array', () => {
     test('shuffled', data, true);
 })
 
-function ordSetToArray(set: OrderedSet) {
-    const ret = [];
-    for (let i = 0; i < set.size; i++) ret.push(set.elementAt(i));
-    return ret;
-}
-
 describe('ordered set', () => {
+    function ordSetToArray(set: OrderedSet) {
+        const ret = [];
+        for (let i = 0, _i = OrderedSet.size(set); i < _i; i++) ret.push(OrderedSet.elementAt(set, i));
+        return ret;
+    }
+
     function testEq(name: string, set: OrderedSet, expected: number[]) {
         it(name, () => {
             // copy the arrays to ensure "compatibility" between typed and native arrays
@@ -189,23 +188,23 @@ describe('ordered set', () => {
     });
 
     it('access/membership', () => {
-        expect(empty.has(10)).toBe(false);
-        expect(empty.indexOf(10)).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(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(arr136.has(3)).toBe(true);
-        expect(arr136.has(4)).toBe(false);
-        expect(arr136.indexOf(3)).toBe(1);
-        expect(arr136.indexOf(11)).toBe(-1);
+        expect(OrderedSet.has(empty, 10)).toBe(false);
+        expect(OrderedSet.indexOf(empty, 10)).toBe(-1);
+
+        expect(OrderedSet.has(singleton10, 10)).toBe(true);
+        expect(OrderedSet.has(singleton10, 11)).toBe(false);
+        expect(OrderedSet.indexOf(singleton10, 10)).toBe(0);
+        expect(OrderedSet.indexOf(singleton10, 11)).toBe(-1);
+
+        expect(OrderedSet.has(range1_4, 4)).toBe(true);
+        expect(OrderedSet.has(range1_4, 5)).toBe(false);
+        expect(OrderedSet.indexOf(range1_4, 4)).toBe(3);
+        expect(OrderedSet.indexOf(range1_4, 11)).toBe(-1);
+
+        expect(OrderedSet.has(arr136, 3)).toBe(true);
+        expect(OrderedSet.has(arr136, 4)).toBe(false);
+        expect(OrderedSet.indexOf(arr136, 3)).toBe(1);
+        expect(OrderedSet.indexOf(arr136, 11)).toBe(-1);
     });
 
     it('interval range', () => {
@@ -273,150 +272,6 @@ describe('ordered set', () => {
     testEq('subtract AA2', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([0, 1, 6])), [3]);
 });
 
-describe('ordered set 1', () => {
-    function ordSet1ToArray(set: OrderedSet1) {
-        const ret = [];
-        for (let i = 0, _i = OrderedSet1.size(set); i < _i; i++) ret.push(OrderedSet1.elementAt(set, i));
-        return ret;
-    }
-
-    function testEq(name: string, set: OrderedSet1, expected: number[]) {
-        it(name, () => {
-            // copy the arrays to ensure "compatibility" between typed and native arrays
-            expect(Array.prototype.slice.call(ordSet1ToArray(set))).toEqual(Array.prototype.slice.call(expected));
-        });
-    }
-
-    const empty = OrderedSet1.Empty;
-    const singleton10 = OrderedSet1.ofSingleton(10);
-    const range1_4 = OrderedSet1.ofRange(1, 4);
-    const arr136 = OrderedSet1.ofSortedArray([1, 3, 6]);
-
-    testEq('empty', empty, []);
-    testEq('singleton', singleton10, [10]);
-    testEq('range', range1_4, [1, 2, 3, 4]);
-    testEq('sorted array', arr136, [1, 3, 6]);
-
-    it('equality', () => {
-        expect(OrderedSet1.areEqual(empty, singleton10)).toBe(false);
-        expect(OrderedSet1.areEqual(singleton10, singleton10)).toBe(true);
-        expect(OrderedSet1.areEqual(range1_4, singleton10)).toBe(false);
-        expect(OrderedSet1.areEqual(arr136, OrderedSet1.ofSortedArray([1, 3, 6]))).toBe(true);
-        expect(OrderedSet1.areEqual(arr136, OrderedSet1.ofSortedArray([1, 4, 6]))).toBe(false);
-    });
-
-    it('areIntersecting', () => {
-        expect(OrderedSet1.areIntersecting(range1_4, arr136)).toBe(true);
-        expect(OrderedSet1.areIntersecting(empty, empty)).toBe(true);
-        expect(OrderedSet1.areIntersecting(empty, singleton10)).toBe(false);
-        expect(OrderedSet1.areIntersecting(empty, range1_4)).toBe(false);
-        expect(OrderedSet1.areIntersecting(empty, arr136)).toBe(false);
-    });
-
-    it('isSubset', () => {
-        expect(OrderedSet1.isSubset(singleton10, empty)).toBe(true);
-        expect(OrderedSet1.isSubset(range1_4, empty)).toBe(true);
-        expect(OrderedSet1.isSubset(arr136, empty)).toBe(true);
-        expect(OrderedSet1.isSubset(empty, empty)).toBe(true);
-        expect(OrderedSet1.isSubset(empty, singleton10)).toBe(false);
-        expect(OrderedSet1.isSubset(empty, range1_4)).toBe(false);
-        expect(OrderedSet1.isSubset(empty, arr136)).toBe(false);
-
-        expect(OrderedSet1.isSubset(singleton10, range1_4)).toBe(false);
-        expect(OrderedSet1.isSubset(range1_4, OrderedSet1.ofRange(2, 3))).toBe(true);
-        expect(OrderedSet1.isSubset(arr136, range1_4)).toBe(false);
-        expect(OrderedSet1.isSubset(arr136, arr136)).toBe(true);
-        expect(OrderedSet1.isSubset(arr136, OrderedSet1.ofSortedArray([1, 3]))).toBe(true);
-        expect(OrderedSet1.isSubset(arr136, OrderedSet1.ofSortedArray([1, 3, 7]))).toBe(false);
-        expect(OrderedSet1.isSubset(OrderedSet1.ofSortedArray([0, 1, 2, 3, 7, 10]), OrderedSet1.ofSortedArray([1, 3, 7]))).toBe(true);
-        expect(OrderedSet1.isSubset(arr136, OrderedSet1.ofSortedArray([1, 3, 10, 45]))).toBe(false);
-    });
-
-    it('access/membership', () => {
-        expect(OrderedSet1.has(empty, 10)).toBe(false);
-        expect(OrderedSet1.indexOf(empty, 10)).toBe(-1);
-
-        expect(OrderedSet1.has(singleton10, 10)).toBe(true);
-        expect(OrderedSet1.has(singleton10, 11)).toBe(false);
-        expect(OrderedSet1.indexOf(singleton10, 10)).toBe(0);
-        expect(OrderedSet1.indexOf(singleton10, 11)).toBe(-1);
-
-        expect(OrderedSet1.has(range1_4, 4)).toBe(true);
-        expect(OrderedSet1.has(range1_4, 5)).toBe(false);
-        expect(OrderedSet1.indexOf(range1_4, 4)).toBe(3);
-        expect(OrderedSet1.indexOf(range1_4, 11)).toBe(-1);
-
-        expect(OrderedSet1.has(arr136, 3)).toBe(true);
-        expect(OrderedSet1.has(arr136, 4)).toBe(false);
-        expect(OrderedSet1.indexOf(arr136, 3)).toBe(1);
-        expect(OrderedSet1.indexOf(arr136, 11)).toBe(-1);
-    });
-
-    it('interval range', () => {
-        expect(OrderedSet1.getIntervalRange(empty, 9, 11)).toEqual({ start: 0, end: 0 });
-        expect(OrderedSet1.getIntervalRange(empty, -9, -6)).toEqual({ start: 0, end: 0 });
-        expect(OrderedSet1.getIntervalRange(singleton10, 9, 11)).toEqual({ start: 0, end: 1 });
-        expect(OrderedSet1.getIntervalRange(range1_4, 2, 3)).toEqual({ start: 1, end: 3 });
-        expect(OrderedSet1.getIntervalRange(range1_4, -10, 2)).toEqual({ start: 0, end: 2 });
-        expect(OrderedSet1.getIntervalRange(range1_4, -10, 20)).toEqual({ start: 0, end: 4 });
-        expect(OrderedSet1.getIntervalRange(range1_4, 3, 20)).toEqual({ start: 2, end: 4 });
-        expect(OrderedSet1.getIntervalRange(arr136, 0, 1)).toEqual({ start: 0, end: 1 });
-        expect(OrderedSet1.getIntervalRange(arr136, 0, 3)).toEqual({ start: 0, end: 2 });
-        expect(OrderedSet1.getIntervalRange(arr136, 0, 4)).toEqual({ start: 0, end: 2 });
-        expect(OrderedSet1.getIntervalRange(arr136, 2, 4)).toEqual({ start: 1, end: 2 });
-        expect(OrderedSet1.getIntervalRange(arr136, 2, 7)).toEqual({ start: 1, end: 3 });
-    })
-
-    testEq('union ES', OrderedSet1.union(empty, singleton10), [10]);
-    testEq('union ER', OrderedSet1.union(empty, range1_4), [1, 2, 3, 4]);
-    testEq('union EA', OrderedSet1.union(empty, arr136), [1, 3, 6]);
-    testEq('union SS', OrderedSet1.union(singleton10, OrderedSet1.ofSingleton(16)), [10, 16]);
-    testEq('union SR', OrderedSet1.union(range1_4, singleton10), [1, 2, 3, 4, 10]);
-    testEq('union SA', OrderedSet1.union(arr136, singleton10), [1, 3, 6, 10]);
-    testEq('union SA1', OrderedSet1.union(arr136, OrderedSet1.ofSingleton(3)), [1, 3, 6]);
-    testEq('union RR', OrderedSet1.union(range1_4, range1_4), [1, 2, 3, 4]);
-    testEq('union RR1', OrderedSet1.union(range1_4, OrderedSet1.ofRange(6, 7)), [1, 2, 3, 4, 6, 7]);
-    testEq('union RR2', OrderedSet1.union(range1_4, OrderedSet1.ofRange(3, 5)), [1, 2, 3, 4, 5]);
-    testEq('union RA', OrderedSet1.union(range1_4, arr136), [1, 2, 3, 4, 6]);
-    testEq('union AA', OrderedSet1.union(arr136, OrderedSet1.ofSortedArray([2, 4, 6, 7])), [1, 2, 3, 4, 6, 7]);
-    testEq('union AA1', OrderedSet1.union(arr136, OrderedSet1.ofSortedArray([2, 3, 4, 6, 7])), [1, 2, 3, 4, 6, 7]);
-    testEq('union AA2', OrderedSet1.union(arr136, OrderedSet1.ofSortedArray([2, 4, 5, 6, 7])), [1, 2, 3, 4, 5, 6, 7]);
-
-    testEq('intersect ES', OrderedSet1.intersect(empty, singleton10), []);
-    testEq('intersect ER', OrderedSet1.intersect(empty, range1_4), []);
-    testEq('intersect EA', OrderedSet1.intersect(empty, arr136), []);
-    testEq('intersect SS', OrderedSet1.intersect(singleton10, OrderedSet1.ofSingleton(16)), []);
-    testEq('intersect SS1', OrderedSet1.intersect(singleton10, singleton10), [10]);
-    testEq('intersect SR', OrderedSet1.intersect(range1_4, singleton10), []);
-    testEq('intersect RR', OrderedSet1.intersect(range1_4, range1_4), [1, 2, 3, 4]);
-    testEq('intersect RR2', OrderedSet1.intersect(range1_4, OrderedSet1.ofRange(3, 5)), [3, 4]);
-    testEq('intersect RA', OrderedSet1.intersect(range1_4, arr136), [1, 3]);
-    testEq('intersect AA', OrderedSet1.intersect(arr136, OrderedSet1.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]);
-
-    testEq('subtract ES', OrderedSet1.subtract(empty, singleton10), []);
-    testEq('subtract ER', OrderedSet1.subtract(empty, range1_4), []);
-    testEq('subtract EA', OrderedSet1.subtract(empty, arr136), []);
-    testEq('subtract SS', OrderedSet1.subtract(singleton10, OrderedSet1.ofSingleton(16)), [10]);
-    testEq('subtract SS1', OrderedSet1.subtract(singleton10, singleton10), []);
-    testEq('subtract SR', OrderedSet1.subtract(range1_4, singleton10), [1, 2, 3, 4]);
-    testEq('subtract SR1', OrderedSet1.subtract(range1_4, OrderedSet1.ofSingleton(4)), [1, 2, 3]);
-    testEq('subtract SR2', OrderedSet1.subtract(range1_4, OrderedSet1.ofSingleton(3)), [1, 2, 4]);
-    testEq('subtract RR', OrderedSet1.subtract(range1_4, range1_4), []);
-    testEq('subtract RR1', OrderedSet1.subtract(range1_4, OrderedSet1.ofRange(3, 5)), [1, 2]);
-
-    testEq('subtract RA', OrderedSet1.subtract(range1_4, arr136), [2, 4]);
-    testEq('subtract RA1', OrderedSet1.subtract(range1_4, OrderedSet1.ofSortedArray([0, 1, 2, 3, 4, 7])), []);
-    testEq('subtract RA2', OrderedSet1.subtract(range1_4, OrderedSet1.ofSortedArray([0, 2, 3])), [1, 4]);
-
-    testEq('subtract AR', OrderedSet1.subtract(arr136, range1_4), [6]);
-    testEq('subtract AR1', OrderedSet1.subtract(arr136, OrderedSet1.ofRange(0, 10)), []);
-    testEq('subtract AR1', OrderedSet1.subtract(arr136, OrderedSet1.ofRange(2, 10)), [1]);
-
-    testEq('subtract AA', OrderedSet1.subtract(arr136, arr136), []);
-    testEq('subtract AA1', OrderedSet1.subtract(arr136, OrderedSet1.ofSortedArray([2, 3, 4, 6, 7])), [1]);
-    testEq('subtract AA2', OrderedSet1.subtract(arr136, OrderedSet1.ofSortedArray([0, 1, 6])), [3]);
-});
-
 
 describe('linked-index', () => {
     it('initial state', () => {