Browse Source

collections

David Sehnal 7 years ago
parent
commit
d07bca0ec8

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

@@ -1,30 +0,0 @@
-// 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();

+ 78 - 0
src/perf-tests/sets.ts

@@ -0,0 +1,78 @@
+import * as B from 'benchmark'
+import IntPair from '../structure/collections/int-pair'
+import OrdSet from '../structure/collections/ordered-set'
+import MSet from '../structure/collections/multi-set'
+
+namespace Iteration {
+    const U = 1000, V = 1000;
+
+    const control: number[] = [];
+    const sets = Object.create(null);
+    for (let i = 1; i < U; i++) {
+        const set = [];
+        for (let j = 1; j < V; j++) {
+            control[control.length] = j * j + 1;
+            set[set.length] = j * j + 1;
+        }
+        sets[i * i] = OrdSet.ofSortedArray(set);
+    }
+    const ms = MSet.create(sets);
+
+    export function native() {
+        let s = 0;
+        for (let i = 0, _i = control.length; i < _i; i++) s += control[i];
+        return s;
+    }
+
+    export function iterators() {
+        let s = 0;
+        const it = MSet.values(ms);
+        for (let v = it.move(); !it.done; v = it.move()) s += v.snd;
+        return s;
+    }
+
+    export function elementAt() {
+        let s = 0;
+        for (let i = 0, _i = MSet.size(ms); i < _i; i++) s += IntPair.snd(MSet.get(ms, i));
+        return s;
+    }
+
+    export function manual() {
+        let s = 0;
+        const keys = MSet.keys(ms);
+        for (let i = 0, _i = OrdSet.size(keys); i < _i; i++) {
+            const set = MSet.getSetByKey(ms, OrdSet.get(keys, i));
+            for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
+                s += OrdSet.get(set, j);
+            }
+        }
+        return s;
+    }
+
+    export function manual1() {
+        let s = 0;
+        for (let i = 0, _i = MSet.componentCount(ms); i < _i; i++) {
+            const set = MSet.getSetByIndex(ms, i);
+            for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
+                s += OrdSet.get(set, j);
+            }
+        }
+        return s;
+    }
+}
+
+const suite = new B.Suite();
+
+// const values: number[] = [];
+// for (let i = 0; i < 1000000; i++) values[i] = (Math.random() * 1000) | 0;
+
+console.log(Iteration.native(), Iteration.iterators(), Iteration.elementAt(), Iteration.manual(), Iteration.manual1());
+
+suite
+    .add('native', () => Iteration.native())
+    .add('iterators', () => Iteration.iterators())
+    .add('manual', () => Iteration.manual())
+    .add('manual1', () => Iteration.manual1())
+    .add('el at', () => Iteration.elementAt())
+    .on('cycle', (e: any) => console.log(String(e.target)))
+    .run();

+ 14 - 12
src/structure/collections/int-pair.ts

@@ -9,6 +9,8 @@ import { hash2 } from './hash-functions'
 interface IntPair { fst: number, snd: number }
 
 namespace IntPair {
+    export interface Packed extends Number { }
+
     const { _int32, _float64, _int32_1, _float64_1 } = (function() {
         const data = new ArrayBuffer(8);
         const data_1 = new ArrayBuffer(8);
@@ -39,30 +41,30 @@ namespace IntPair {
         return _float64[0];
     }
 
-    export function unpack(packed: number, target: IntPair): IntPair {
-        _float64[0] = packed;
+    export function unpack(packed: Packed, target: IntPair): IntPair {
+        _float64[0] = packed as number;
         target.fst = _int32[0];
         target.snd = _int32[1];
         return target;
     }
 
-    export function unpack1(packed: number): IntPair {
+    export function unpack1(packed: Packed): IntPair {
         return unpack(packed, zero());
     }
 
-    export function fst(packed: number): number {
-        _float64[0] = packed;
+    export function fst(packed: Packed): number {
+        _float64[0] = packed as number;
         return _int32[0];
     }
 
-    export function snd(packed: number): number {
-        _float64[0] = packed;
+    export function snd(packed: Packed): number {
+        _float64[0] = packed as number;
         return _int32[1];
     }
 
-    export function areEqual(a: number, b: number) {
-        _float64[0] = a;
-        _float64_1[0] = b;
+    export function areEqual(a: Packed, b: Packed) {
+        _float64[0] = a as number;
+        _float64_1[0] = b as number;
         return _int32[0] === _int32_1[0] && _int32[1] === _int32_1[1];
     }
 
@@ -82,8 +84,8 @@ namespace IntPair {
         return _int32[1] - _int32_1[1];
     }
 
-    export function packedHashCode(packed: number) {
-        _float64[0] = packed;
+    export function packedHashCode(packed: Packed) {
+        _float64[0] = packed as number;
         return hash2(_int32[0], _int32[1]);
     }
 }

+ 105 - 49
src/structure/collections/multi-set.ts

@@ -10,17 +10,17 @@ import IntPair from './int-pair'
 import { sortArray } from './sort'
 import { hash1 } from './hash-functions'
 
-type MultiSetElements = { [id: number]: OrderedSet, size: number, hashCode: number, keys: OrderedSet }
+type MultiSetElements = { [id: number]: OrderedSet, offsets: number[], hashCode: number, keys: OrderedSet }
 type MultiSet = number | MultiSetElements
 
 namespace MultiSet {
-    export const Empty: MultiSet = { size: 0, hashCode: 0, keys: OrderedSet.Empty };
+    export const Empty: MultiSet = { offsets: [0], hashCode: 0, keys: OrderedSet.Empty };
 
-    export function create(data: number | ArrayLike<number> | IntPair | { [id: number]: OrderedSet }): MultiSet {
+    export function create(data: IntPair.Packed | ArrayLike<IntPair.Packed> | IntPair | { [id: number]: OrderedSet }): MultiSet {
         if (typeof data === 'number') return data;
         if (IntPair.is(data)) return IntPair.pack(data);
         if (isArrayLike(data)) return ofPackedPairs(data);
-        return ofObject(data);
+        return ofObject(data as { [id: number]: OrderedSet });
     }
 
     export function keys(set: MultiSet): OrderedSet {
@@ -28,20 +28,58 @@ namespace MultiSet {
         return set.keys;
     }
 
+    export function componentCount(set: MultiSet): OrderedSet {
+        if (typeof set === 'number') return 1;
+        return OrderedSet.size(set.keys);
+    }
+
     export function hasKey(set: MultiSet, key: number): boolean {
         if (typeof set === 'number') return IntPair.fst(set) === key;
         return OrderedSet.has(set.keys, key);
     }
 
-    export function get(set: MultiSet, key: number): OrderedSet {
-        if (!hasKey(set, key)) return OrderedSet.Empty;
-        if (typeof set === 'number') return OrderedSet.ofSingleton(IntPair.snd(set));
-        return set[key];
+    const _hasP = IntPair.zero();
+    export function has(set: MultiSet, pair: number): boolean {
+        if (typeof set === 'number') {
+            return IntPair.areEqual(pair, set);
+        }
+        IntPair.unpack(pair, _hasP);
+        return OrderedSet.has(set.keys, _hasP.fst) ? OrderedSet.has(set[_hasP.fst], _hasP.snd) : false;
+    }
+
+    const _gS = IntPair.zero();
+    export function getSetByKey(set: MultiSet, key: number): OrderedSet {
+        if (typeof set === 'number') {
+            IntPair.unpack(set, _gS);
+            return _gS.fst === key ? OrderedSet.ofSingleton(_gS.snd) : OrderedSet.Empty;
+        }
+        return OrderedSet.has(set.keys, key) ? set[key] : OrderedSet.Empty;
+    }
+
+    export function getKey(set: MultiSet, index: number): number {
+        if (typeof set === 'number') return IntPair.fst(set);
+        return OrderedSet.get(set.keys, index);
+    }
+
+    export function getSetByIndex(set: MultiSet, index: number): OrderedSet {
+        if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(IntPair.snd(set)) : OrderedSet.Empty;
+        const key = OrderedSet.get(set.keys, index);
+        return set[key] || OrderedSet.Empty;
+    }
+
+    export function get(set: MultiSet, i: number): IntPair.Packed {
+        if (typeof set === 'number') return set;
+        const { offsets, keys } = set;
+        const o = getOffsetIndex(offsets, i);
+        if (o >= offsets.length - 1) return 0;
+        const k = OrderedSet.get(keys, o);
+        const e = OrderedSet.get(set[k], i - offsets[o]);
+        return IntPair.pack1(k, e);
     }
 
     export function size(set: MultiSet) {
         if (typeof set === 'number') return 0;
-        return set.size;
+        return set.offsets[set.offsets.length - 1];
     }
 
     export function hashCode(set: MultiSet) {
@@ -100,7 +138,6 @@ namespace MultiSet {
 
     class ElementsIterator implements Iterator<IntPair> {
         private pair = IntPair.zero();
-        private unit = 0;
 
         private keyCount: number;
         private setIndex = -1;
@@ -119,20 +156,18 @@ namespace MultiSet {
                 if (!this.advance()) return this.pair;
             }
 
-            const next = OrderedSet.elementAt(this.currentSet, this.currentIndex++);
-            this.pair.snd = next;
+            this.pair.snd = OrderedSet.get(this.currentSet, this.currentIndex++);
             return this.pair;
         }
 
         private advance() {
-            const keys = this.elements.keys;
             if (++this.setIndex >= this.keyCount) {
                 this.done = true;
                 return false;
             }
-            this.unit = OrderedSet.elementAt(keys, this.setIndex);
-            this.pair.fst = this.unit;
-            this.currentSet = this.elements[this.unit];
+            const unit = OrderedSet.get(this.elements.keys, this.setIndex);
+            this.pair.fst = unit;
+            this.currentSet = this.elements[unit];
             this.currentIndex = 0;
             this.currentSize = OrderedSet.size(this.currentSet);
             return true;
@@ -151,8 +186,6 @@ namespace MultiSet {
     }
 }
 
-const pair = IntPair.zero();
-
 function isArrayLike(x: any): x is ArrayLike<number> {
     return x && (typeof x.length === 'number' && (x instanceof Array || !!x.buffer));
 }
@@ -166,7 +199,7 @@ function ofObject(data: { [id: number]: OrderedSet }) {
     if (!keys.length) return MultiSet.Empty;
     if (keys.length === 1) {
         const set = data[keys[0]];
-        if (OrderedSet.size(set) === 1) return IntPair.pack1(keys[0], OrderedSet.elementAt(set, 0));
+        if (OrderedSet.size(set) === 1) return IntPair.pack1(keys[0], OrderedSet.get(set, 0));
     }
     return ofObject1(keys, data);
 }
@@ -175,7 +208,7 @@ function ofObject1(keys: number[], data: { [id: number]: OrderedSet }) {
     if (keys.length === 1) {
         const k = keys[0];
         const set = data[k];
-        if (OrderedSet.size(set) === 1) return IntPair.pack1(k, OrderedSet.elementAt(set, 0));
+        if (OrderedSet.size(set) === 1) return IntPair.pack1(k, OrderedSet.get(set, 0));
     }
     sortArray(keys);
     return _createObjectOrdered(OrderedSet.ofSortedArray(keys), data);
@@ -183,9 +216,9 @@ function ofObject1(keys: number[], data: { [id: number]: OrderedSet }) {
 
 function ofObjectOrdered(keys: OrderedSet, data: { [id: number]: OrderedSet }) {
     if (OrderedSet.size(keys) === 1) {
-        const k = OrderedSet.elementAt(keys, 0);
+        const k = OrderedSet.get(keys, 0);
         const set = data[k];
-        if (OrderedSet.size(set) === 1) return IntPair.pack1(k, OrderedSet.elementAt(set, 0));
+        if (OrderedSet.size(set) === 1) return IntPair.pack1(k, OrderedSet.get(set, 0));
     }
     return _createObjectOrdered(keys, data);
 }
@@ -193,14 +226,16 @@ function ofObjectOrdered(keys: OrderedSet, data: { [id: number]: OrderedSet }) {
 function _createObjectOrdered(keys: OrderedSet, data: { [id: number]: OrderedSet }) {
     const ret: MultiSetElements = Object.create(null);
     ret.keys = keys;
+    const offsets = [0];
     let size = 0;
     for (let i = 0, _i = OrderedSet.size(keys); i < _i; i++) {
-        const k = OrderedSet.elementAt(keys, i);
+        const k = OrderedSet.get(keys, i);
         const set = data[k];
         ret[k] = set;
         size += OrderedSet.size(set);
+        offsets[offsets.length] = size;
     }
-    ret.size = size;
+    ret.offsets = offsets;
     ret.hashCode = -1;
     return ret;
 }
@@ -247,15 +282,30 @@ function ofPackedPairs(xs: ArrayLike<number>): MultiSet {
     return ofObject1(keys, ret);
 }
 
+function getOffsetIndex(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;
+    }
+    return value < xs[min] ? min - 1 : min;
+}
+
 function computeHash(set: MultiSetElements) {
     const { keys } = set;
     let hash = 23;
     for (let i = 0, _i = OrderedSet.size(keys); i < _i; i++) {
-        const k = OrderedSet.elementAt(keys, i);
+        const k = OrderedSet.get(keys, i);
         hash = (31 * hash + k) | 0;
         hash = (31 * hash + OrderedSet.hashCode(set[k])) | 0;
     }
-    hash = (31 * hash + set.size) | 0;
+    hash = (31 * hash + MultiSet.size(set)) | 0;
     hash = hash1(hash);
     set.hashCode = hash;
     return hash;
@@ -263,20 +313,22 @@ function computeHash(set: MultiSetElements) {
 
 function areEqualEE(a: MultiSetElements, b: MultiSetElements) {
     if (a === b) return true;
-    if (a.size !== b.size) return false;
+    if (MultiSet.size(a) !== MultiSet.size(a)) return false;
 
     const keys = a.keys;
     if (!OrderedSet.areEqual(keys, b.keys)) return false;
     for (let i = 0, _i = OrderedSet.size(keys); i < _i; i++) {
-        const k = OrderedSet.elementAt(keys, i);
+        const k = OrderedSet.get(keys, i);
         if (!OrderedSet.areEqual(a[k], b[k])) return false;
     }
     return true;
 }
 
+
+const _aeP = IntPair.zero();
 function areIntersectingNE(a: number, b: MultiSetElements) {
-    IntPair.unpack(a, pair);
-    return OrderedSet.has(b.keys, pair.fst) && OrderedSet.has(b[pair.fst], pair.snd);
+    IntPair.unpack(a, _aeP);
+    return OrderedSet.has(b.keys, _aeP.fst) && OrderedSet.has(b[_aeP.fst], _aeP.snd);
 }
 
 function areIntersectingEE(a: MultiSetElements, b: MultiSetElements) {
@@ -285,15 +337,16 @@ 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 = OrderedSet.elementAt(keysA, i);
+        const k = OrderedSet.get(keysA, i);
         if (OrderedSet.has(keysB, k) && OrderedSet.areIntersecting(a[k], b[k])) return true;
     }
     return false;
 }
 
+const _nP = IntPair.zero();
 function intersectNE(a: number, b: MultiSetElements) {
-    IntPair.unpack(a, pair);
-    return OrderedSet.has(b.keys, pair.fst) && OrderedSet.has(b[pair.fst], pair.snd) ? a : MultiSet.Empty;
+    IntPair.unpack(a, _nP);
+    return OrderedSet.has(b.keys, _nP.fst) && OrderedSet.has(b[_nP.fst], _nP.snd) ? a : MultiSet.Empty;
 }
 
 function intersectEE(a: MultiSetElements, b: MultiSetElements) {
@@ -305,7 +358,7 @@ function intersectEE(a: MultiSetElements, b: MultiSetElements) {
 
     const keys = [], ret = Object.create(null);
     for (let i = start; i < end; i++) {
-        const k = OrderedSet.elementAt(keysA, i);
+        const k = OrderedSet.get(keysA, i);
         if (OrderedSet.has(keysB, k)) {
             const intersection = OrderedSet.intersect(a[k], b[k]);
             if (OrderedSet.size(intersection) > 0) {
@@ -317,20 +370,22 @@ function intersectEE(a: MultiSetElements, b: MultiSetElements) {
     return ofObjectOrdered(OrderedSet.ofSortedArray(keys), ret);
 }
 
+const _sNE = IntPair.zero();
 function subtractNE(a: number, b: MultiSetElements) {
-    IntPair.unpack(a, pair);
-    return OrderedSet.has(b.keys, pair.fst) && OrderedSet.has(b[pair.fst], pair.snd) ? MultiSet.Empty : a;
+    IntPair.unpack(a, _sNE);
+    return OrderedSet.has(b.keys, _sNE.fst) && OrderedSet.has(b[_sNE.fst], _sNE.snd) ? MultiSet.Empty : a;
 }
 
+const _sEN = IntPair.zero();
 function subtractEN(a: MultiSetElements, b: number): MultiSet {
     const aKeys =  a.keys;
-    IntPair.unpack(b, pair);
-    if (!OrderedSet.has(aKeys, pair.fst) || !OrderedSet.has(a[pair.fst], pair.snd)) return a;
-    const set = a[pair.fst];
+    IntPair.unpack(b, _sEN);
+    if (!OrderedSet.has(aKeys, _sEN.fst) || !OrderedSet.has(a[_sEN.fst], _sEN.snd)) return a;
+    const set = a[_sEN.fst];
     if (OrderedSet.size(set) === 1) {
-        return ofObjectOrdered(OrderedSet.subtract(a.keys, OrderedSet.ofSingleton(pair.fst)), a);
+        return ofObjectOrdered(OrderedSet.subtract(a.keys, OrderedSet.ofSingleton(_sEN.fst)), a);
     } else {
-        return { ...a, [pair.fst]: OrderedSet.subtract(set, OrderedSet.ofSingleton(pair.snd)), size: a.size - 1, hashCode: -1 }
+        return ofObjectOrdered(OrderedSet.subtract(set, OrderedSet.ofSingleton(_sEN.snd)), a);
     }
 }
 
@@ -343,12 +398,12 @@ function subtractEE(a: MultiSetElements, b: MultiSetElements) {
 
     const keys = [], ret = Object.create(null);
     for (let i = 0; i < start; i++) {
-        const k = OrderedSet.elementAt(keysA, i);
+        const k = OrderedSet.get(keysA, i);
         keys[keys.length] = k;
         ret[k] = a[k];
     }
     for (let i = start; i < end; i++) {
-        const k = OrderedSet.elementAt(keysA, i);
+        const k = OrderedSet.get(keysA, i);
         if (OrderedSet.has(keysB, k)) {
             const subtraction = OrderedSet.subtract(a[k], b[k]);
             if (OrderedSet.size(subtraction) > 0) {
@@ -361,7 +416,7 @@ function subtractEE(a: MultiSetElements, b: MultiSetElements) {
         }
     }
     for (let i = end, _i = OrderedSet.size(keysA); i < _i; i++) {
-        const k = OrderedSet.elementAt(keysA, i);
+        const k = OrderedSet.get(keysA, i);
         keys[keys.length] = k;
         ret[k] = a[k];
     }
@@ -409,20 +464,21 @@ 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 = OrderedSet.size(keys); i < _i; i++) {
-        const k = OrderedSet.elementAt(keys, i);
+        const k = OrderedSet.get(keys, i);
         const set = data[k];
         if (set) data[k] = OrderedSet.union(set, a[k]);
         else data[k] = a[k];
     }
 }
 
+const _uIN = IntPair.zero();
 function unionIntoN(data: { [key: number]: OrderedSet }, a: number) {
-    IntPair.unpack(a, pair);
-    const set = data[pair.fst];
+    IntPair.unpack(a, _uIN);
+    const set = data[_uIN.fst];
     if (set) {
-        data[pair.fst] = OrderedSet.union(set, OrderedSet.ofSingleton(pair.snd));
+        data[_uIN.fst] = OrderedSet.union(set, OrderedSet.ofSingleton(_uIN.snd));
     } else {
-        data[pair.fst] = OrderedSet.ofSingleton(pair.snd);
+        data[_uIN.fst] = OrderedSet.ofSingleton(_uIN.snd);
     }
 }
 

+ 4 - 4
src/structure/collections/ordered-set.ts

@@ -26,7 +26,7 @@ namespace OrderedSet {
     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 get(set: OrderedSet, i: number) { return typeof set === 'number' ? elementAtR(set, i) : set[i]; }
     export function min(set: OrderedSet) { return typeof set === 'number' ? minR(set) : set[0]; }
     export function max(set: OrderedSet) { return typeof set === 'number' ? maxR(set) : set[set.length - 1]; }
 
@@ -34,8 +34,8 @@ namespace 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));
+        if (s > 2) return hash4(s, get(set, 0), get(set, s - 1), get(set, s >> 1));
+        return hash3(s, get(set, 0), get(set, s - 1));
     }
     // TODO: possibly add more hash functions to allow for multilevel hashing.
 
@@ -187,7 +187,7 @@ 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++;
+    if (end < S.size(set) && S.get(set, end) === max) end++;
     _startEndRet.end = end;
     return _startEndRet;
 }

+ 29 - 1
src/structure/spec/collections.spec.ts

@@ -131,7 +131,7 @@ describe('qsort-dual array', () => {
 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));
+        for (let i = 0, _i = OrderedSet.size(set); i < _i; i++) ret.push(OrderedSet.get(set, i));
         return ret;
     }
 
@@ -330,6 +330,9 @@ describe('multiset', () => {
     it('singleton pair', () => {
         const set = MultiSet.create(p(10, 11));
         expect(setToPairs(set)).toEqual([p(10, 11)]);
+        expect(MultiSet.has(set, r(10, 11))).toBe(true);
+        expect(MultiSet.has(set, r(11, 11))).toBe(false);
+        expect(MultiSet.get(set, 0)).toBe(r(10, 11));
     });
 
     it('singleton number', () => {
@@ -342,7 +345,32 @@ describe('multiset', () => {
             1: OrderedSet.ofSortedArray([4, 6, 7]),
             3: OrderedSet.ofRange(0, 1),
         });
+        const ret = [p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)];
+        expect(MultiSet.size(set)).toBe(ret.length);
         expect(setToPairs(set)).toEqual([p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]);
+        expect(MultiSet.has(set, r(10, 11))).toBe(false);
+        expect(MultiSet.has(set, r(3, 0))).toBe(true);
+        expect(MultiSet.has(set, r(1, 7))).toBe(true);
+        for (let i = 0; i < MultiSet.size(set); i++) {
+            expect(MultiSet.get(set, i)).toBe(IntPair.pack(ret[i]));
+        }
+    });
+
+    it('element at', () => {
+        const control = [];
+        const sets = Object.create(null);
+        for (let i = 1; i < 10; i++) {
+            const set = [];
+            for (let j = 1; j < 7; j++) {
+                control[control.length] = r(i * i, j * j + 1);
+                set[set.length] = j * j + 1;
+            }
+            sets[i * i] = OrderedSet.ofSortedArray(set);
+        }
+        const ms = MultiSet.create(sets);
+        for (let i = 0; i < control.length; i++) {
+            expect(IntPair.areEqual(MultiSet.get(ms, i), control[i])).toBe(true);
+        }
     });
 
     it('packed pairs', () => {