diff --git a/src/perf-tests/ordered-set.ts b/src/perf-tests/ordered-set.ts deleted file mode 100644 index 8086e7746e04c2bf9ddd76b59b9a7fd89e8e7608..0000000000000000000000000000000000000000 --- a/src/perf-tests/ordered-set.ts +++ /dev/null @@ -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(); diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts new file mode 100644 index 0000000000000000000000000000000000000000..7797d8907f3840b85eb2ae902c925b3dca9a7d3c --- /dev/null +++ b/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(); diff --git a/src/structure/collections/int-pair.ts b/src/structure/collections/int-pair.ts index 939f358116a4bf304cba9aee9ac4b903377510e0..910e796fd6c147e18c6509ac96eb8ef6b6a61927 100644 --- a/src/structure/collections/int-pair.ts +++ b/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]); } } diff --git a/src/structure/collections/multi-set.ts b/src/structure/collections/multi-set.ts index c3846abdabac757b07d4250f0606d903070e037f..1ab9f9ff30c49d6d1ebc559d4b64eb29163df99a 100644 --- a/src/structure/collections/multi-set.ts +++ b/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); } } diff --git a/src/structure/collections/ordered-set.ts b/src/structure/collections/ordered-set.ts index cab641230f439cedf9832ac6bb02819630f07bb7..0aa5ca57eec081fdf755591bc5aba13927b45e50 100644 --- a/src/structure/collections/ordered-set.ts +++ b/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; } diff --git a/src/structure/spec/collections.spec.ts b/src/structure/spec/collections.spec.ts index b9c084d8386d08fb4395e30ad47688d1f63c7d83..60978930d7aa649aad87b82e493d20dc2e6e45f9 100644 --- a/src/structure/spec/collections.spec.ts +++ b/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', () => {