diff --git a/package-lock.json b/package-lock.json index 47e6bce95bb6fce12ea990a40b6cdc0d9bde06e8..cfd951679b3a503d8fb86785c98a3e978ea34abc 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index 6e12b2ee17dfd788b1e1a22b3e5612844619a464..8b2461c3bca1f54cf53d321765792e1699f3f797 100644 --- a/package.json +++ b/package.json @@ -30,21 +30,21 @@ "license": "MIT", "devDependencies": { "@types/benchmark": "^1.0.30", - "@types/jest": "^21.1.4", - "@types/node": "^8.0.46", + "@types/jest": "^21.1.5", + "@types/node": "^8.0.47", "benchmark": "^2.1.4", "download-cli": "^1.0.5", "jest": "^21.2.1", "rollup": "^0.50.0", "rollup-plugin-buble": "^0.16.0", - "rollup-plugin-commonjs": "^8.2.4", + "rollup-plugin-commonjs": "^8.2.5", "rollup-plugin-json": "^2.3.0", "rollup-plugin-node-resolve": "^3.0.0", "rollup-watch": "^4.3.1", "ts-jest": "^21.1.3", "tslint": "^5.8.0", "typescript": "^2.5.3", - "uglify-js": "^3.1.4", + "uglify-js": "^3.1.5", "util.promisify": "^1.0.0" }, "dependencies": {} diff --git a/src/mol-base/_spec/collections.spec.ts b/src/mol-base/_spec/collections.spec.ts index 71330f576fbc08c238a3cd8208d4fe18bdd2438e..4b93ce45a8476b9f82ff5a7b7e5157e5876f7349 100644 --- a/src/mol-base/_spec/collections.spec.ts +++ b/src/mol-base/_spec/collections.spec.ts @@ -32,13 +32,11 @@ describe('basic iterators', () => { describe('int pair', () => { it('works', () => { - const p = IntTuple.zero(); for (let i = 0; i < 10; i++) { for (let j = -10; j < 5; j++) { - const t = IntTuple.pack(i, j); - IntTuple.unpack(t, p); - expect(p.fst).toBe(i); - expect(p.snd).toBe(j); + const t = IntTuple.create(i, j); + expect(IntTuple.fst(t)).toBe(i); + expect(IntTuple.snd(t)).toBe(j); } } }) diff --git a/src/mol-base/collections/int-tuple.ts b/src/mol-base/collections/int-tuple.ts index 64e97ca8280bceec8e12937ddc711ae5b0041106..da27ab08f9dce6c63e9f7bfcb23a96314a58c300 100644 --- a/src/mol-base/collections/int-tuple.ts +++ b/src/mol-base/collections/int-tuple.ts @@ -13,7 +13,7 @@ import { hash2 } from './hash-functions' interface IntTuple { '@type': 'int-tuple' } namespace IntTuple { - export interface Unpacked { fst: number, snd: number } + export const Zero: IntTuple = 0 as any; const { _int32, _float64, _int32_1, _float64_1 } = (function() { const data = new ArrayBuffer(8); @@ -26,36 +26,16 @@ namespace IntTuple { }; }()); - export function is(x: any): x is Unpacked { - return !!x && typeof x.fst === 'number' && typeof x.snd === 'number'; + export function is(x: any): x is IntTuple { + return typeof x === 'number'; } - export function create(fst: number, snd: number) { return { fst, snd }; } - export function zero(): Unpacked { return { fst: 0, snd: 0 }; } - - export function pack(fst: number, snd: number): IntTuple { + export function create(fst: number, snd: number): IntTuple { _int32[0] = fst; _int32[1] = snd; return _float64[0] as any; } - export function pack1(t: Unpacked): IntTuple { - _int32[0] = t.fst; - _int32[1] = t.snd; - return _float64[0] as any; - } - - export function unpack(t: IntTuple, target: Unpacked): Unpacked { - _float64[0] = t as any; - target.fst = _int32[0]; - target.snd = _int32[1]; - return target; - } - - export function unpack1(packed: IntTuple): Unpacked { - return unpack(packed, zero()); - } - export function fst(t: IntTuple): number { _float64[0] = t as any; return _int32[0]; diff --git a/src/mol-base/collections/interval.ts b/src/mol-base/collections/interval.ts new file mode 100644 index 0000000000000000000000000000000000000000..722d48d395b56036bf5c21ff70c08bafdcbdb555 --- /dev/null +++ b/src/mol-base/collections/interval.ts @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import IntTuple from './int-tuple' + +/** Closed/Open inteval [a, b) (iterate as a <= i < b) */ +namespace Interval { + export const Empty: Interval = Impl.Empty as any; + + // export const create: (min: number, max: number) => Interval = Impl.create as any; + + // export const indexOf: (set: Interval, x: number) => number = Impl.indexOf as any; + // export const getAt: (set: Interval, i: number) => number = Impl.getAt as any; + + // export const start: (set: Interval) => number = Impl.start as any; + // export const end: (set: Interval) => number = Impl.end as any; + + // export const min: (set: Interval) => number = Impl.min as any; + // export const max: (set: Interval) => number = Impl.max as any; + // export const size: (set: Interval) => number = Impl.size as any; + // export const hashCode: (set: Interval) => number = Impl.hashCode as any; + + // export const areEqual: (a: Interval, b: Interval) => boolean = Impl.areEqual as any; + // export const areIntersecting: (a: Interval, b: Interval) => boolean = Impl.areIntersecting as any; + // export const isSubset: (a: Interval, b: Interval) => boolean = Impl.isSubset as any; + +} + +interface Interval { '@type': 'int-interval' } + +export default Interval + +namespace Impl {export const Empty = IntTuple.Zero; + + export function create(min: number, max: number) { return max < min ? Empty : IntTuple.create(min, max + 1); } + + export const start = IntTuple.fst + export const end = IntTuple.snd + export const min = IntTuple.fst + export function max(i: IntTuple) { return IntTuple.snd(i) + 1; } + export function size(i: IntTuple) { return IntTuple.snd(i) - IntTuple.fst(i); } + + export function has(int: IntTuple, v: number) { return IntTuple.fst(int) <= v && v < IntTuple.snd(int); } +} \ No newline at end of file diff --git a/src/mol-base/collections/ordered-set/base.ts b/src/mol-base/collections/ordered-set/base.ts index 1eddd1ad55713e0abc5d5bc7bf0626cab537f154..b37bc56e165df6dff3c503b053ac60859c3aecf3 100644 --- a/src/mol-base/collections/ordered-set/base.ts +++ b/src/mol-base/collections/ordered-set/base.ts @@ -11,9 +11,9 @@ type Range = IntTuple type SortedArray = ArrayLike<number> type OrderedSetImpl = Range | SortedArray -export const Empty: OrderedSetImpl = IntTuple.pack(0, -1) as any; -export function ofSingleton(value: number): OrderedSetImpl { return IntTuple.pack(value, value) as any; } -export function ofRange(min: number, max: number): OrderedSetImpl { return max < min ? Empty : IntTuple.pack(min, max) as any; } +export const Empty: OrderedSetImpl = IntTuple.create(0, -1) as any; +export function ofSingleton(value: number): OrderedSetImpl { return IntTuple.create(value, value) as any; } +export function ofRange(min: number, max: number): OrderedSetImpl { return max < min ? Empty : IntTuple.create(min, max) as any; } /** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */ export function ofSortedArray(xs: SortedArray): OrderedSetImpl { if (!xs.length) return Empty; @@ -120,10 +120,9 @@ const minR = IntTuple.fst const maxR = IntTuple.snd const equalRR = IntTuple.areEqual -const _eR = IntTuple.zero(); -function sizeR(set: Range) { IntTuple.unpack(set, _eR); return _eR.snd - _eR.fst + 1; } -function hasR(set: Range, x: number) { IntTuple.unpack(set, _eR); return x >= _eR.fst && x <= _eR.snd; } -function indexOfR(set: Range, x: number) { IntTuple.unpack(set, _eR); return x >= _eR.fst && x <= _eR.snd ? x - _eR.fst : -1; } +function sizeR(set: Range) { return maxR(set) - minR(set) + 1; } +function hasR(set: Range, x: number) { return x >= minR(set) && x <= maxR(set); } +function indexOfR(set: Range, x: number) { const m = minR(set); return x >= m && x <= maxR(set) ? x - m : -1; } function elementAtR(set: Range, i: number) { return IntTuple.fst(set) + i; } function hasA(set: SortedArray, x: number) { return x >= set[0] && x <= set[set.length - 1] && binarySearch(set, x) >= 0; } @@ -169,12 +168,12 @@ function binarySearchPredIndexRange(xs: SortedArray, value: number, start: numbe return xs[min] >= value ? min : min + 1; } -const _rsiR = IntTuple.zero(); function rangeSearchIndex(r: Range, value: number) { - IntTuple.unpack(r, _rsiR); - if (value < _rsiR.fst) return 0; - if (value > _rsiR.snd) return _rsiR.snd - _rsiR.fst + 1; - return value - _rsiR.fst; + const min = minR(r); + if (value < min) return 0; + const max = maxR(r); + if (value > max) return max - min + 1; + return value - min; } const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 }; @@ -262,15 +261,13 @@ function unionRR(a: Range, b: Range) { return ofSortedArray(arr); } -const _uAR = IntTuple.zero(); function unionAR(a: SortedArray, b: Range) { const bSize = size(b); if (!bSize) return a; // is the array fully contained in the range? if (isRangeSubset(b, a)) return b; - IntTuple.unpack(b, _uAR); - const min = _uAR.fst, max = _uAR.snd; + const min = minR(b), max = maxR(b); const { start, end } = getStartEnd(a, min, max); const indices = new Int32Array(start + (a.length - end) + bSize); @@ -332,22 +329,16 @@ function unionAA(a: SortedArray, b: SortedArray) { return ofSortedArray(indices); } -const _iRA = IntTuple.zero(), _iRB = IntTuple.zero(); function intersectRR(a: Range, b: Range) { if (!areRangesIntersecting(a, b)) return Empty; if (IntTuple.areEqual(a, b)) return a; - - IntTuple.unpack(a, _iRA); - IntTuple.unpack(b, _iRB); - return ofRange(Math.max(_iRA.fst, _iRB.fst), Math.min(_iRA.snd, _iRB.snd)); + return ofRange(Math.max(minR(a), minR(b)), Math.min(maxR(a), maxR(b))); } -const _iAR = IntTuple.zero(); function intersectAR(a: SortedArray, r: Range) { if (!size(r)) return Empty; - IntTuple.unpack(r, _iAR); - const { start, end } = getStartEnd(a, _iAR.fst, _iAR.snd); + const { start, end } = getStartEnd(a, minR(r), maxR(r)); const resultSize = end - start; if (!resultSize) return Empty; @@ -394,41 +385,37 @@ function intersectAA(a: SortedArray, b: SortedArray) { return ofSortedArray(indices); } -const _sRA = IntTuple.zero(), _sRB = IntTuple.zero(); function substractRR(a: Range, b: Range) { if (IntTuple.areEqual(a, b)) return Empty; - IntTuple.unpack(a, _sRA); - IntTuple.unpack(b, _sRB); + const minA = minR(a), maxA = maxR(a); + const minB = minR(b), maxB = maxR(b); - if (_sRA.snd < _sRA.fst || _sRB.snd < _sRB.fst) return a; + if (maxA < minA || maxB < minB) return a; // is A subset of B? ==> Empty if (isRangeSubset(b, a)) return 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; - if (l <= 0) return ofRange(_sRB.snd + 1, _sRB.snd + r); - if (r <= 0) return ofRange(_sRA.fst, _sRA.fst + l - 1); + const l = minB - minA, r = maxA - maxB; + if (l <= 0) return ofRange(maxB + 1, maxB + r); + if (r <= 0) return ofRange(minA, minA + l - 1); 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; + for (let i = 0; i < l; i++) ret[offset++] = minA + i; + for (let i = 1; i <= r; i++) ret[offset++] = maxB + i; return 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 ofRange(_sRA.fst, _sRB.fst - 1); - return ofRange(_sRB.snd + 1, _sRA.snd); + if (minA < minB) return ofRange(minA, minB - 1); + return ofRange(maxB + 1, maxA); } -const _sAR = IntTuple.zero(); function subtractAR(a: SortedArray, b: Range) { - IntTuple.unpack(b, _sAR); - // is empty? - if (_sAR.snd < _sAR.fst) return a; + const min = minR(b), max = maxR(b); + if (max < min) return a; - const min = _sAR.fst, max = _sAR.snd; const { start, end } = getStartEnd(a, min, max); const resultSize = a.length - (end - start); // A is subset of B @@ -443,14 +430,12 @@ function subtractAR(a: SortedArray, b: Range) { return ofSortedArray(ret); } -const _sAR1 = IntTuple.zero(); function subtractRA(a: Range, b: SortedArray) { - IntTuple.unpack(a, _sAR1); + const min = minR(a), max = maxR(a); // is empty? - if (_sAR1.snd < _sAR1.fst) return a; + if (max < min) return a; - const min = _sAR1.fst, max = _sAR1.snd; const rSize = max - min + 1; const { start, end } = getStartEnd(b, min, max); const commonCount = end - start; diff --git a/src/mol-data/_spec/atom-set.spec.ts b/src/mol-data/_spec/atom-set.spec.ts index 603909f1efa9a86dee9780f35387795d9a31e95c..b34cbc2ca25abdb916009da38a7758b07ae50b59 100644 --- a/src/mol-data/_spec/atom-set.spec.ts +++ b/src/mol-data/_spec/atom-set.spec.ts @@ -10,26 +10,25 @@ import AtomSet from '../atom-set' describe('atom set', () => { const p = (i: number, j: number) => IntTuple.create(i, j); - const r = (i: number, j: number) => IntTuple.pack(i, j); - function setToPairs(set: AtomSet): ArrayLike<IntTuple.Unpacked> { - const ret = []; + function setToPairs(set: AtomSet): ArrayLike<IntTuple> { + const ret: IntTuple[] = []; const it = AtomSet.atoms(set); - for (let v = it.move(); !it.done; v = it.move()) ret[ret.length] = IntTuple.create(v.fst, v.snd); + for (let v = it.move(); !it.done; v = it.move()) ret[ret.length] = v; return ret; } it('singleton pair', () => { const set = AtomSet.create(p(10, 11)); expect(setToPairs(set)).toEqual([p(10, 11)]); - expect(AtomSet.hasAtom(set, r(10, 11))).toBe(true); - expect(AtomSet.hasAtom(set, r(11, 11))).toBe(false); - expect(AtomSet.getAtomAt(set, 0)).toBe(r(10, 11)); + expect(AtomSet.hasAtom(set, p(10, 11))).toBe(true); + expect(AtomSet.hasAtom(set, p(11, 11))).toBe(false); + expect(AtomSet.getAtomAt(set, 0)).toBe(p(10, 11)); expect(AtomSet.atomCount(set)).toBe(1); }); it('singleton number', () => { - const set = AtomSet.create(r(10, 11)); + const set = AtomSet.create(p(10, 11)); expect(setToPairs(set)).toEqual([p(10, 11)]); }); @@ -41,11 +40,11 @@ describe('atom set', () => { const ret = [p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]; expect(AtomSet.atomCount(set)).toBe(ret.length); expect(setToPairs(set)).toEqual([p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]); - expect(AtomSet.hasAtom(set, r(10, 11))).toBe(false); - expect(AtomSet.hasAtom(set, r(3, 0))).toBe(true); - expect(AtomSet.hasAtom(set, r(1, 7))).toBe(true); + expect(AtomSet.hasAtom(set, p(10, 11))).toBe(false); + expect(AtomSet.hasAtom(set, p(3, 0))).toBe(true); + expect(AtomSet.hasAtom(set, p(1, 7))).toBe(true); for (let i = 0; i < AtomSet.atomCount(set); i++) { - expect(AtomSet.getAtomAt(set, i)).toBe(IntTuple.pack1(ret[i])); + expect(IntTuple.areEqual(AtomSet.getAtomAt(set, i), ret[i])).toBe(true); } }); @@ -55,7 +54,7 @@ describe('atom set', () => { 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); + control[control.length] = p(i * i, j * j + 1); set[set.length] = j * j + 1; } sets[i * i] = OrderedSet.ofSortedArray(set); @@ -71,17 +70,17 @@ describe('atom set', () => { }); it('packed pairs', () => { - const set = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); + const set = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); expect(setToPairs(set)).toEqual([p(0, 1), p(0, 2), p(0, 6), p(1, 3)]); }); it('equality', () => { - const a = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); - const b = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); - const c = AtomSet.create([r(1, 3), r(0, 4), r(0, 6), r(0, 2)]); - const d = AtomSet.create([r(1, 3)]); - const e = AtomSet.create([r(1, 3)]); - const f = AtomSet.create([r(3, 3)]); + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const c = AtomSet.create([p(1, 3), p(0, 4), p(0, 6), p(0, 2)]); + const d = AtomSet.create([p(1, 3)]); + const e = AtomSet.create([p(1, 3)]); + const f = AtomSet.create([p(3, 3)]); expect(AtomSet.areEqual(a, a)).toBe(true); expect(AtomSet.areEqual(a, b)).toBe(true); @@ -93,13 +92,13 @@ describe('atom set', () => { }); it('are intersecting', () => { - const a = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); - const b = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); - const c = AtomSet.create([r(1, 3), r(0, 4), r(0, 6), r(0, 2)]); - const d = AtomSet.create([r(1, 3)]); - const e = AtomSet.create([r(1, 3)]); - const f = AtomSet.create([r(3, 3)]); - const g = AtomSet.create([r(10, 3), r(8, 1), r(7, 6), r(3, 2)]); + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const c = AtomSet.create([p(1, 3), p(0, 4), p(0, 6), p(0, 2)]); + const d = AtomSet.create([p(1, 3)]); + const e = AtomSet.create([p(1, 3)]); + const f = AtomSet.create([p(3, 3)]); + const g = AtomSet.create([p(10, 3), p(8, 1), p(7, 6), p(3, 2)]); expect(AtomSet.areIntersecting(a, a)).toBe(true); expect(AtomSet.areIntersecting(a, b)).toBe(true); @@ -112,10 +111,10 @@ describe('atom set', () => { }); it('intersection', () => { - const a = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); - const b = AtomSet.create([r(10, 3), r(0, 1), r(0, 6), r(4, 2)]); - const c = AtomSet.create([r(1, 3)]); - const d = AtomSet.create([r(2, 3)]); + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(10, 3), p(0, 1), p(0, 6), p(4, 2)]); + const c = AtomSet.create([p(1, 3)]); + const d = AtomSet.create([p(2, 3)]); expect(AtomSet.intersect(a, a)).toBe(a); expect(setToPairs(AtomSet.intersect(a, b))).toEqual([p(0, 1), p(0, 6)]); expect(setToPairs(AtomSet.intersect(a, c))).toEqual([p(1, 3)]); @@ -123,11 +122,11 @@ describe('atom set', () => { }); it('subtract', () => { - const a = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); - const a1 = AtomSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]); - const b = AtomSet.create([r(10, 3), r(0, 1), r(0, 6), r(4, 2)]); - const c = AtomSet.create([r(1, 3)]); - const d = AtomSet.create([r(2, 3)]); + const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const a1 = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const b = AtomSet.create([p(10, 3), p(0, 1), p(0, 6), p(4, 2)]); + const c = AtomSet.create([p(1, 3)]); + const d = AtomSet.create([p(2, 3)]); expect(setToPairs(AtomSet.subtract(a, a))).toEqual([]); expect(setToPairs(AtomSet.subtract(a, a1))).toEqual([]); expect(setToPairs(AtomSet.subtract(a, b))).toEqual([p(0, 2), p(1, 3)]); @@ -138,11 +137,11 @@ describe('atom set', () => { }); it('union', () => { - const a = AtomSet.create([r(1, 3), r(0, 1)]); - const a1 = AtomSet.create([r(1, 3), r(0, 1)]); - const b = AtomSet.create([r(10, 3), r(0, 1)]); - const c = AtomSet.create([r(1, 3)]); - const d = AtomSet.create([r(2, 3)]); + const a = AtomSet.create([p(1, 3), p(0, 1)]); + const a1 = AtomSet.create([p(1, 3), p(0, 1)]); + const b = AtomSet.create([p(10, 3), p(0, 1)]); + const c = AtomSet.create([p(1, 3)]); + const d = AtomSet.create([p(2, 3)]); expect(AtomSet.unionMany([a])).toBe(a); expect(AtomSet.union(a, a)).toBe(a); expect(setToPairs(AtomSet.union(a, a))).toEqual([p(0, 1), p(1, 3)]); diff --git a/src/mol-data/atom-set.ts b/src/mol-data/atom-set.ts index f28399dd62c12c57153ef697b6e89acc7e2fbb68..abf072be780461fefdbb56551f321751edd152a8 100644 --- a/src/mol-data/atom-set.ts +++ b/src/mol-data/atom-set.ts @@ -27,7 +27,7 @@ namespace AtomSet { export const hasAtom: (set: AtomSet, x: IntTuple) => boolean = Base.hasTuple as any; export const indexOfAtom: (set: AtomSet, x: IntTuple) => number = Base.indexOf as any; export const getAtomAt: (set: AtomSet, i: number) => IntTuple = Base.getAt as any; - export const atoms: (set: AtomSet) => Iterator<IntTuple.Unpacked> = Base.values as any; + export const atoms: (set: AtomSet) => Iterator<IntTuple> = Base.values as any; export const atomCount: (set: AtomSet) => number = Base.size as any; diff --git a/src/mol-data/atom-set/base.ts b/src/mol-data/atom-set/base.ts index 2147fc755b89b1e6e7abfb47688706241e1e424f..ea1aff390ac057e3fcacd0633f0972012e5d8aa9 100644 --- a/src/mol-data/atom-set/base.ts +++ b/src/mol-data/atom-set/base.ts @@ -17,9 +17,8 @@ export type AtomSetImpl = IntTuple | AtomSetElements export const Empty: AtomSetImpl = { offsets: [0], hashCode: 0, keys: OrderedSet.Empty }; -export function create(data: IntTuple | ArrayLike<IntTuple> | IntTuple | { [id: number]: OrderedSet }): AtomSetImpl { - if (typeof data === 'number') return data; - if (IntTuple.is(data)) return IntTuple.pack1(data); +export function create(data: IntTuple | ArrayLike<IntTuple> | { [id: number]: OrderedSet }): AtomSetImpl { + if (typeof data === 'number' || IntTuple.is(data)) return data; if (isArrayLike(data)) return ofTuples(data); return ofObject(data as { [id: number]: OrderedSet }); } @@ -50,19 +49,17 @@ export function getKey(set: AtomSetImpl, index: number): number { export function hasTuple(set: AtomSetImpl, t: IntTuple): boolean { if (typeof set === 'number') return IntTuple.areEqual(t, set); - IntTuple.unpack(t, _hasP); - return OrderedSet.has((set as AtomSetElements).keys, _hasP.fst) ? OrderedSet.has((set as AtomSetElements)[_hasP.fst], _hasP.snd) : false; + const unit = IntTuple.fst(t); + return OrderedSet.has((set as AtomSetElements).keys, unit) + ? OrderedSet.has((set as AtomSetElements)[unit], IntTuple.snd(t)) : false; } -const _hasP = IntTuple.zero(); export function getByKey(set: AtomSetImpl, key: number): OrderedSet { if (typeof set === 'number') { - IntTuple.unpack(set, _gS); - return _gS.fst === key ? OrderedSet.ofSingleton(_gS.snd) : OrderedSet.Empty; + return IntTuple.fst(set) === key ? OrderedSet.ofSingleton(IntTuple.snd(set)) : OrderedSet.Empty; } return OrderedSet.has((set as AtomSetElements).keys, key) ? (set as AtomSetElements)[key] : OrderedSet.Empty; } -const _gS = IntTuple.zero(); export function getByIndex(set: AtomSetImpl, index: number): OrderedSet { if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(IntTuple.snd(set)) : OrderedSet.Empty; @@ -136,9 +133,8 @@ export function unionMany(sets: ArrayLike<AtomSetImpl>) { return findUnion(sets); } -class ElementsIterator implements Iterator<IntTuple.Unpacked> { - private pair = IntTuple.zero(); - +class ElementsIterator implements Iterator<IntTuple> { + private unit: number = 0; private keyCount: number; private setIndex = -1; private currentIndex = 0; @@ -150,14 +146,13 @@ class ElementsIterator implements Iterator<IntTuple.Unpacked> { next() { const value = this.move(); return { value, done: this.done } } move() { - if (this.done) return this.pair; + if (this.done) return IntTuple.Zero; if (this.currentIndex >= this.currentSize) { - if (!this.advance()) return this.pair; + if (!this.advance()) return IntTuple.Zero; } - this.pair.snd = OrderedSet.getAt(this.currentSet, this.currentIndex++); - return this.pair; + return IntTuple.create(this.unit, OrderedSet.getAt(this.currentSet, this.currentIndex++)); } private advance() { @@ -165,9 +160,8 @@ class ElementsIterator implements Iterator<IntTuple.Unpacked> { this.done = true; return false; } - const unit = OrderedSet.getAt(this.elements.keys, this.setIndex); - this.pair.fst = unit; - this.currentSet = this.elements[unit]; + this.unit = OrderedSet.getAt(this.elements.keys, this.setIndex); + this.currentSet = this.elements[this.unit]; this.currentIndex = 0; this.currentSize = OrderedSet.size(this.currentSet); return true; @@ -180,8 +174,8 @@ class ElementsIterator implements Iterator<IntTuple.Unpacked> { } } -export function values(set: AtomSetImpl): Iterator<IntTuple.Unpacked> { - if (typeof set === 'number') return Iterator.Value(IntTuple.unpack1(set)); +export function values(set: AtomSetImpl): Iterator<IntTuple> { + if (typeof set === 'number') return Iterator.Value(set as IntTuple); return new ElementsIterator(set as AtomSetElements); } @@ -198,7 +192,7 @@ function ofObject(data: { [id: number]: OrderedSet }) { if (!keys.length) return Empty; if (keys.length === 1) { const set = data[keys[0]]; - if (OrderedSet.size(set) === 1) return IntTuple.pack(keys[0], OrderedSet.getAt(set, 0)); + if (OrderedSet.size(set) === 1) return IntTuple.create(keys[0], OrderedSet.getAt(set, 0)); } return ofObject1(keys, data); } @@ -207,7 +201,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 IntTuple.pack(k, OrderedSet.getAt(set, 0)); + if (OrderedSet.size(set) === 1) return IntTuple.create(k, OrderedSet.getAt(set, 0)); } sortArray(keys); return _createObjectOrdered(OrderedSet.ofSortedArray(keys), data); @@ -217,7 +211,7 @@ function ofObjectOrdered(keys: OrderedSet, data: { [id: number]: OrderedSet }) { if (OrderedSet.size(keys) === 1) { const k = OrderedSet.getAt(keys, 0); const set = data[k]; - if (OrderedSet.size(set) === 1) return IntTuple.pack(k, OrderedSet.getAt(set, 0)); + if (OrderedSet.size(set) === 1) return IntTuple.create(k, OrderedSet.getAt(set, 0)); } return _createObjectOrdered(keys, data); } @@ -264,12 +258,12 @@ function normalizeArray(xs: number[]) { function ofTuples(xs: ArrayLike<IntTuple>) { if (xs.length === 0) return Empty; const sets: { [key: number]: number[] } = Object.create(null); - const p = IntTuple.zero(); for (let i = 0, _i = xs.length; i < _i; i++) { - IntTuple.unpack(xs[i], p); - const set = sets[p.fst]; - if (set) set[set.length] = p.snd; - else sets[p.fst] = [p.snd]; + const x = xs[i]; + const u = IntTuple.fst(x), v = IntTuple.snd(x); + const set = sets[u]; + if (set) set[set.length] = v; + else sets[u] = [v]; } const ret: { [key: number]: OrderedSet } = Object.create(null); const keys = []; @@ -302,16 +296,15 @@ function getAtE(set: AtomSetElements, i: number): IntTuple { if (o >= offsets.length - 1) return 0 as any; const k = OrderedSet.getAt(keys, o); const e = OrderedSet.getAt(set[k], i - offsets[o]); - return IntTuple.pack(k, e); + return IntTuple.create(k, e); } -const _iOE = IntTuple.zero(); function indexOfE(set: AtomSetElements, t: IntTuple) { - IntTuple.unpack(t, _iOE); const { keys } = set; - const setIdx = OrderedSet.indexOf(keys, _iOE.fst); + const u = IntTuple.fst(t); + const setIdx = OrderedSet.indexOf(keys, u); if (setIdx < 0) return -1; - const o = OrderedSet.indexOf(set[_iOE.fst], _iOE.snd); + const o = OrderedSet.indexOf(set[u], IntTuple.snd(t)); if (o < 0) return -1; return set.offsets[setIdx] + o; } @@ -343,10 +336,9 @@ function areEqualEE(a: AtomSetElements, b: AtomSetElements) { return true; } -const _aeP = IntTuple.zero(); function areIntersectingNE(a: IntTuple, b: AtomSetElements) { - IntTuple.unpack(a, _aeP); - return OrderedSet.has(b.keys, _aeP.fst) && OrderedSet.has(b[_aeP.fst], _aeP.snd); + const u = IntTuple.fst(a); + return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], IntTuple.snd(a)); } function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) { @@ -361,10 +353,9 @@ function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) { return false; } -const _nP = IntTuple.zero(); function intersectNE(a: IntTuple, b: AtomSetElements) { - IntTuple.unpack(a, _nP); - return OrderedSet.has(b.keys, _nP.fst) && OrderedSet.has(b[_nP.fst], _nP.snd) ? a : Empty; + const u = IntTuple.fst(a); + return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], IntTuple.snd(a)) ? a : Empty; } function intersectEE(a: AtomSetElements, b: AtomSetElements) { @@ -388,22 +379,28 @@ function intersectEE(a: AtomSetElements, b: AtomSetElements) { return ofObjectOrdered(OrderedSet.ofSortedArray(keys), ret); } -const _sNE = IntTuple.zero(); function subtractNE(a: IntTuple, b: AtomSetElements) { - IntTuple.unpack(a, _sNE); - return OrderedSet.has(b.keys, _sNE.fst) && OrderedSet.has(b[_sNE.fst], _sNE.snd) ? Empty : a; + const u = IntTuple.fst(a); + return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], IntTuple.snd(a)) ? Empty : a; } -const _sEN = IntTuple.zero(); function subtractEN(a: AtomSetElements, b: IntTuple): AtomSetImpl { const aKeys = a.keys; - IntTuple.unpack(b, _sEN); - if (!OrderedSet.has(aKeys, _sEN.fst) || !OrderedSet.has(a[_sEN.fst], _sEN.snd)) return a; - const set = a[_sEN.fst]; + const u = IntTuple.fst(b), v = IntTuple.snd(b); + if (!OrderedSet.has(aKeys, u) || !OrderedSet.has(a[u], v)) return a; + const set = a[u]; if (OrderedSet.size(set) === 1) { - return ofObjectOrdered(OrderedSet.subtract(a.keys, OrderedSet.ofSingleton(_sEN.fst)), a); + // remove the entire unit. + return ofObjectOrdered(OrderedSet.subtract(a.keys, OrderedSet.ofSingleton(u)), a); } else { - return ofObjectOrdered(OrderedSet.subtract(set, OrderedSet.ofSingleton(_sEN.snd)), a); + const ret: { [key: number]: OrderedSet } = Object.create(null); + for (let i = 0, _i = OrderedSet.size(a.keys); i < _i; i++) { + const k = OrderedSet.getAt(a.keys, i); + if (k === u) { + ret[k] = OrderedSet.subtract(set, OrderedSet.ofSingleton(v)); + } else ret[k] = a[k]; + } + return ofObjectOrdered(a.keys, ret); } } @@ -489,13 +486,12 @@ function unionInto(data: { [key: number]: OrderedSet }, a: AtomSetElements) { } } -const _uIN = IntTuple.zero(); function unionIntoN(data: { [key: number]: OrderedSet }, a: IntTuple) { - IntTuple.unpack(a, _uIN); - const set = data[_uIN.fst]; + const u = IntTuple.fst(a); + const set = data[u]; if (set) { - data[_uIN.fst] = OrderedSet.union(set, OrderedSet.ofSingleton(_uIN.snd)); + data[u] = OrderedSet.union(set, OrderedSet.ofSingleton(IntTuple.snd(a))); } else { - data[_uIN.fst] = OrderedSet.ofSingleton(_uIN.snd); + data[u] = OrderedSet.ofSingleton(IntTuple.snd(a)); } } \ No newline at end of file diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts index 1fba7dfe17b01e231f6974fb76b777e751cc0b3a..4930c3e84826bcea4508daa695e64b4203d441f5 100644 --- a/src/perf-tests/sets.ts +++ b/src/perf-tests/sets.ts @@ -27,7 +27,7 @@ export namespace Iteration { export function iterators() { let s = 0; const it = AtomSet.atoms(ms); - for (let v = it.move(); !it.done; v = it.move()) s += v.snd; + for (let v = it.move(); !it.done; v = it.move()) s += IntTuple.snd(v); return s; } @@ -230,6 +230,43 @@ export namespace Build { } } +export namespace Tuples { + function createData(n: number) { + const ret: IntTuple[] = new Float64Array(n) as any; + for (let i = 0; i < n; i++) { + ret[i] = IntTuple.create(i, i * i + 1); + } + return ret; + } + + function sum1(data: ArrayLike<IntTuple>) { + let s = 0; + for (let i = 0, _i = data.length; i < _i; i++) { + s += IntTuple.fst(data[i]) + IntTuple.snd(data[i]); + } + return s; + } + + function sum2(data: ArrayLike<IntTuple>) { + let s = 0; + for (let i = 0, _i = data.length; i < _i; i++) { + const t = data[i]; + s += IntTuple.fst(t) + IntTuple.snd(t); + } + return s; + } + + export function run() { + const suite = new B.Suite(); + const data = createData(10000); + suite + .add('sum fst/snd', () => sum1(data)) + .add('sum fst/snd 1', () => sum2(data)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + export function testSegments() { const data = OrdSet.ofSortedArray([4, 9, 10, 11, 14, 15, 16]); const segs = OrdSet.ofSortedArray([0, 4, 10, 12, 13, 15, 25]); @@ -242,4 +279,10 @@ export function testSegments() { } } -Union.run(); \ No newline at end of file +Tuples.run(); + +// interface AA { kind: 'a' } +// //interface BB { kind: 'b' } +// interface AB { kind: 'a' | 'b' } +// declare const a: AA; +// export const ab: AB = a;