From fbd7c35f980e7313969a70da702b5286344394a6 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Tue, 24 Oct 2017 16:19:12 +0200 Subject: [PATCH] collections refactoring --- src/structure/collections/multi-set.ts | 303 ++++++++++++----------- src/structure/collections/ordered-set.ts | 18 +- 2 files changed, 172 insertions(+), 149 deletions(-) diff --git a/src/structure/collections/multi-set.ts b/src/structure/collections/multi-set.ts index c3a9d2ef4..5b56e8955 100644 --- a/src/structure/collections/multi-set.ts +++ b/src/structure/collections/multi-set.ts @@ -11,177 +11,204 @@ import { sortArray } from './sort' import { hash1 } from './hash-functions' /** A map-like representation of integer set */ -interface MultiSet { /*'@type': 'int-multi-set'*/ } +interface MultiSet { '@type': 'int-multi-set' } namespace MultiSet { export const Empty: MultiSet = { offsets: [0], hashCode: 0, keys: OrderedSet.Empty } as MultiSetElements as any; export function create(data: IntTuple | ArrayLike<IntTuple> | IntTuple | { [id: number]: OrderedSet }): MultiSet { if (typeof data === 'number') return data; - if (IntTuple.is(data)) return IntTuple.pack1(data) as number; - if (isArrayLike(data)) return ofTuples(data); - return ofObject(data as { [id: number]: OrderedSet }); - } + if (IntTuple.is(data)) return IntTuple.pack1(data) as any; + if (isArrayLike(data)) return ofTuples(data) as any; + return ofObject(data as { [id: number]: OrderedSet }) as any; + } + + export const keys: (set: MultiSet) => OrderedSet = keysI as any; + export const keyCount: (set: MultiSet) => number = keyCountI as any; + export const hasKey: (set: MultiSet, key: number) => boolean = hasKeyI as any; + export const geyKey: (set: MultiSet, i: number) => number = getKeyI as any; + export const getByKey: (set: MultiSet, key: number) => OrderedSet = getByKeyI as any; + export const getByIndex: (set: MultiSet, i: number) => OrderedSet = getByIndexI as any; + export const has: (set: MultiSet, x: IntTuple) => boolean = hasI as any; + export const indexOf: (set: MultiSet, x: IntTuple) => number = indexOfI as any; + export const getAt: (set: MultiSet, i: number) => IntTuple = getAtI as any; + export const values: (set: MultiSet) => Iterator<IntTuple.Unpacked> = valuesI as any; + + export const size: (set: MultiSet) => number = sizeI as any; + export const hashCode: (set: MultiSet) => number = hashCodeI as any; + + export const areEqual: (a: MultiSet, b: MultiSet) => boolean = areEqualI as any; + export const areIntersecting: (a: MultiSet, b: MultiSet) => boolean = areIntersectingI as any; + + export const union: (a: MultiSet, b: MultiSet) => MultiSet = unionI as any; + export const unionMany: (sets: MultiSet[]) => MultiSet = unionManyI as any; + export const intersect: (a: MultiSet, b: MultiSet) => MultiSet = intersectI as any; + export const subtract: (a: MultiSet, b: MultiSet) => MultiSet = subtractI as any; +} - export function keys(set: MultiSet): OrderedSet { - if (typeof set === 'number') return OrderedSet.ofSingleton(set); - return (set as MultiSetElements).keys; - } +export default MultiSet - export function keyCount(set: MultiSet): number { - if (typeof set === 'number') return 1; - return OrderedSet.size((set as MultiSetElements).keys); - } +/** Long and painful implementation starts here */ - export function hasKey(set: MultiSet, key: number): boolean { - if (typeof set === 'number') return IntTuple.fst(set) === key; - return OrderedSet.has((set as MultiSetElements).keys, key); - } +interface MultiSetElements { [id: number]: OrderedSet, offsets: number[], hashCode: number, keys: OrderedSet } +type MultiSetImpl = IntTuple | MultiSetElements - export function getKey(set: MultiSet, index: number): number { - if (typeof set === 'number') return IntTuple.fst(set); - return OrderedSet.getAt((set as MultiSetElements).keys, index); - } +function keysI(set: MultiSetImpl): OrderedSet { + if (typeof set === 'number') return OrderedSet.ofSingleton(set); + return (set as MultiSetElements).keys; +} - export function has(set: MultiSet, t: IntTuple): boolean { - if (typeof set === 'number') return IntTuple.areEqual(t, set); - IntTuple.unpack(t, _hasP); - return OrderedSet.has((set as MultiSetElements).keys, _hasP.fst) ? OrderedSet.has((set as MultiSetElements)[_hasP.fst], _hasP.snd) : false; - } - const _hasP = IntTuple.zero(); +function keyCountI(set: MultiSetImpl): number { + if (typeof set === 'number') return 1; + return OrderedSet.size((set as MultiSetElements).keys); +} - export function getByKey(set: MultiSet, key: number): OrderedSet { - if (typeof set === 'number') { - IntTuple.unpack(set, _gS); - return _gS.fst === key ? OrderedSet.ofSingleton(_gS.snd) : OrderedSet.Empty; - } - return OrderedSet.has((set as MultiSetElements).keys, key) ? (set as MultiSetElements)[key] : OrderedSet.Empty; - } - const _gS = IntTuple.zero(); +function hasKeyI(set: MultiSetImpl, key: number): boolean { + if (typeof set === 'number') return IntTuple.fst(set) === key; + return OrderedSet.has((set as MultiSetElements).keys, key); +} - export function getByIndex(set: MultiSet, index: number): OrderedSet { - if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(IntTuple.snd(set)) : OrderedSet.Empty; - const key = OrderedSet.getAt((set as MultiSetElements).keys, index); - return (set as MultiSetElements)[key] || OrderedSet.Empty; - } +function getKeyI(set: MultiSetImpl, index: number): number { + if (typeof set === 'number') return IntTuple.fst(set); + return OrderedSet.getAt((set as MultiSetElements).keys, index); +} - export function getAt(set: MultiSet, i: number): IntTuple { - if (typeof set === 'number') return set; - return getAtE(set as MultiSetElements, i); - } +function hasI(set: MultiSetImpl, t: IntTuple): boolean { + if (typeof set === 'number') return IntTuple.areEqual(t, set); + IntTuple.unpack(t, _hasP); + return OrderedSet.has((set as MultiSetElements).keys, _hasP.fst) ? OrderedSet.has((set as MultiSetElements)[_hasP.fst], _hasP.snd) : false; +} +const _hasP = IntTuple.zero(); - export function indexOf(set: MultiSet, t: IntTuple) { - if (typeof set === 'number') return IntTuple.areEqual(set, t) ? 0 : -1; - return indexOfE(set as MultiSetElements, t); +function getByKeyI(set: MultiSetImpl, key: number): OrderedSet { + if (typeof set === 'number') { + IntTuple.unpack(set, _gS); + return _gS.fst === key ? OrderedSet.ofSingleton(_gS.snd) : OrderedSet.Empty; } + return OrderedSet.has((set as MultiSetElements).keys, key) ? (set as MultiSetElements)[key] : OrderedSet.Empty; +} +const _gS = IntTuple.zero(); - /** Number elements in the "child" sets */ - export function size(set: MultiSet) { - if (typeof set === 'number') return 1; - return (set as MultiSetElements).offsets[(set as MultiSetElements).offsets.length - 1]; - } +function getByIndexI(set: MultiSetImpl, index: number): OrderedSet { + if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(IntTuple.snd(set)) : OrderedSet.Empty; + const key = OrderedSet.getAt((set as MultiSetElements).keys, index); + return (set as MultiSetElements)[key] || OrderedSet.Empty; +} - export function hashCode(set: MultiSet) { - if (typeof set === 'number') return IntTuple.hashCode(set); - if ((set as MultiSetElements).hashCode !== -1) return (set as MultiSetElements).hashCode; - return computeHash((set as MultiSetElements)); - } +function getAtI(set: MultiSetImpl, i: number): IntTuple { + if (typeof set === 'number') return set; + return getAtE(set as MultiSetElements, i); +} - export function areEqual(a: MultiSet, b: MultiSet): boolean { - if (typeof a === 'number') { - if (typeof b === 'number') return IntTuple.areEqual(a, b); - return false; - } - if (typeof b === 'number') return false; - return areEqualEE(a as MultiSetElements, b as MultiSetElements); - } +function indexOfI(set: MultiSetImpl, t: IntTuple) { + if (typeof set === 'number') return IntTuple.areEqual(set, t) ? 0 : -1; + return indexOfE(set as MultiSetElements, t); +} - export function areIntersecting(a: MultiSet, b: MultiSet): boolean { - if (typeof a === 'number') { - if (typeof b === 'number') return IntTuple.areEqual(a, b); - return areIntersectingNE(a, b as MultiSetElements); - } - if (typeof b === 'number') return areIntersectingNE(b, a as MultiSetElements); - return areIntersectingEE(a as MultiSetElements, b as MultiSetElements); - } +/** Number elements in the "child" sets */ +function sizeI(set: MultiSetImpl) { + if (typeof set === 'number') return 1; + return (set as MultiSetElements).offsets[(set as MultiSetElements).offsets.length - 1]; +} - export function intersect(a: MultiSet, b: MultiSet): MultiSet { - if (typeof a === 'number') { - if (typeof b === 'number') return IntTuple.areEqual(a, b) ? a : Empty; - return intersectNE(a, b as MultiSetElements); - } - if (typeof b === 'number') return intersectNE(b, a as MultiSetElements); - return intersectEE(a as MultiSetElements, b as MultiSetElements); +function hashCodeI(set: MultiSetImpl) { + if (typeof set === 'number') return IntTuple.hashCode(set); + if ((set as MultiSetElements).hashCode !== -1) return (set as MultiSetElements).hashCode; + return computeHash((set as MultiSetElements)); +} + +function areEqualI(a: MultiSetImpl, b: MultiSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return IntTuple.areEqual(a, b); + return false; } + if (typeof b === 'number') return false; + return areEqualEE(a as MultiSetElements, b as MultiSetElements); +} - export function subtract(a: MultiSet, b: MultiSet): MultiSet { - if (typeof a === 'number') { - if (typeof b === 'number') return IntTuple.areEqual(a, b) ? Empty : a; - return subtractNE(a, b as MultiSetElements); - } - if (typeof b === 'number') return subtractEN(a as MultiSetElements, b); - return subtractEE(a as MultiSetElements, b as MultiSetElements); +function areIntersectingI(a: MultiSetImpl, b: MultiSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return IntTuple.areEqual(a, b); + return areIntersectingNE(a, b as MultiSetElements); } + if (typeof b === 'number') return areIntersectingNE(b, a as MultiSetElements); + return areIntersectingEE(a as MultiSetElements, b as MultiSetElements); +} - export function union(a: MultiSet, b: MultiSet): MultiSet { - return findUnion([a, b]); +function intersectI(a: MultiSetImpl, b: MultiSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return IntTuple.areEqual(a, b) ? a : MultiSet.Empty; + return intersectNE(a, b as MultiSetElements); } + if (typeof b === 'number') return intersectNE(b, a as MultiSetElements); + return intersectEE(a as MultiSetElements, b as MultiSetElements); +} - export function unionMany(sets: ArrayLike<MultiSet>): MultiSet { - return findUnion(sets); +function subtractI(a: MultiSetImpl, b: MultiSetImpl) { + if (typeof a === 'number') { + if (typeof b === 'number') return IntTuple.areEqual(a, b) ? MultiSet.Empty : a; + return subtractNE(a, b as MultiSetElements); } + if (typeof b === 'number') return subtractEN(a as MultiSetElements, b); + return subtractEE(a as MultiSetElements, b as MultiSetElements); +} - class ElementsIterator implements Iterator<IntTuple.Unpacked> { - private pair = IntTuple.zero(); +function unionI(a: MultiSetImpl, b: MultiSetImpl) { + return findUnion([a, b]); +} - private keyCount: number; - private setIndex = -1; - private currentIndex = 0; - private currentSize = 0; - private currentSet: OrderedSet = OrderedSet.Empty; +function unionManyI(sets: ArrayLike<MultiSetImpl>) { + return findUnion(sets); +} - [Symbol.iterator]() { return new ElementsIterator(this.elements); }; - done: boolean; - next() { const value = this.move(); return { value, done: this.done } } +class ElementsIterator implements Iterator<IntTuple.Unpacked> { + private pair = IntTuple.zero(); - move() { - if (this.done) return this.pair; + private keyCount: number; + private setIndex = -1; + private currentIndex = 0; + private currentSize = 0; + private currentSet: OrderedSet = OrderedSet.Empty; - if (this.currentIndex >= this.currentSize) { - if (!this.advance()) return this.pair; - } + [Symbol.iterator]() { return new ElementsIterator(this.elements); }; + done: boolean; + next() { const value = this.move(); return { value, done: this.done } } - this.pair.snd = OrderedSet.getAt(this.currentSet, this.currentIndex++); - return this.pair; - } + move() { + if (this.done) return this.pair; - private advance() { - if (++this.setIndex >= this.keyCount) { - this.done = true; - return false; - } - const unit = OrderedSet.getAt(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; + if (this.currentIndex >= this.currentSize) { + if (!this.advance()) return this.pair; } - constructor(private elements: MultiSetElements) { - this.keyCount = OrderedSet.size(elements.keys); - this.done = this.keyCount === 0; - this.advance(); + this.pair.snd = OrderedSet.getAt(this.currentSet, this.currentIndex++); + return this.pair; + } + + private advance() { + if (++this.setIndex >= this.keyCount) { + this.done = true; + return false; } + const unit = OrderedSet.getAt(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; } - export function values(set: MultiSet): Iterator<IntTuple.Unpacked> { - if (typeof set === 'number') return Iterator.Value(IntTuple.unpack1(set)); - return new ElementsIterator(set as MultiSetElements); + constructor(private elements: MultiSetElements) { + this.keyCount = OrderedSet.size(elements.keys); + this.done = this.keyCount === 0; + this.advance(); } } -interface MultiSetElements { [id: number]: OrderedSet, offsets: number[], hashCode: number, keys: OrderedSet } +function valuesI(set: MultiSetImpl): Iterator<IntTuple.Unpacked> { + if (typeof set === 'number') return Iterator.Value(IntTuple.unpack1(set)); + return new ElementsIterator(set as MultiSetElements); +} function isArrayLike(x: any): x is ArrayLike<number> { return x && (typeof x.length === 'number' && (x instanceof Array || !!x.buffer)); @@ -259,7 +286,7 @@ function normalizeArray(xs: number[]) { return xs; } -function ofTuples(xs: ArrayLike<IntTuple>): MultiSet { +function ofTuples(xs: ArrayLike<IntTuple>) { if (xs.length === 0) return MultiSet.Empty; const sets: { [key: number]: number[] } = Object.create(null); const p = IntTuple.zero(); @@ -322,7 +349,7 @@ function computeHash(set: MultiSetElements) { hash = (31 * hash + k) | 0; hash = (31 * hash + OrderedSet.hashCode(set[k])) | 0; } - hash = (31 * hash + MultiSet.size(set)) | 0; + hash = (31 * hash + sizeI(set)) | 0; hash = hash1(hash); set.hashCode = hash; return hash; @@ -330,7 +357,7 @@ function computeHash(set: MultiSetElements) { function areEqualEE(a: MultiSetElements, b: MultiSetElements) { if (a === b) return true; - if (MultiSet.size(a) !== MultiSet.size(a)) return false; + if (sizeI(a) !== sizeI(a)) return false; const keys = a.keys; if (!OrderedSet.areEqual(keys, b.keys)) return false; @@ -393,7 +420,7 @@ function subtractNE(a: IntTuple, b: MultiSetElements) { } const _sEN = IntTuple.zero(); -function subtractEN(a: MultiSetElements, b: IntTuple): MultiSet { +function subtractEN(a: MultiSetElements, b: IntTuple): MultiSetImpl { const aKeys = a.keys; IntTuple.unpack(b, _sEN); if (!OrderedSet.has(aKeys, _sEN.fst) || !OrderedSet.has(a[_sEN.fst], _sEN.snd)) return a; @@ -439,10 +466,10 @@ function subtractEE(a: MultiSetElements, b: MultiSetElements) { return ofObjectOrdered(OrderedSet.ofSortedArray(keys), ret); } -function findUnion(sets: ArrayLike<MultiSet>) { +function findUnion(sets: ArrayLike<MultiSetImpl>) { if (!sets.length) return MultiSet.Empty; if (sets.length === 1) return sets[0]; - if (sets.length === 2 && MultiSet.areEqual(sets[0], sets[1])) return sets[0]; + if (sets.length === 2 && areEqualI(sets[0], sets[1])) return sets[0]; const eCount = { count: 0 }; const ns = unionN(sets, eCount); @@ -452,14 +479,14 @@ function findUnion(sets: ArrayLike<MultiSet>) { const s = sets[i]; if (typeof s !== 'number') unionInto(ret, s as MultiSetElements); } - if (MultiSet.size(ns) > 0) { + if (sizeI(ns as MultiSetImpl) > 0) { if (typeof ns === 'number') unionIntoN(ret, ns); else unionInto(ret, ns as MultiSetElements); } return ofObject(ret); } -function unionN(sets: ArrayLike<MultiSet>, eCount: { count: number }) { +function unionN(sets: ArrayLike<MultiSetImpl>, eCount: { count: number }) { let countN = 0, countE = 0; for (let i = 0, _i = sets.length; i < _i; i++) { if (typeof sets[i] === 'number') countN++; @@ -496,6 +523,4 @@ function unionIntoN(data: { [key: number]: OrderedSet }, a: IntTuple) { } else { data[_uIN.fst] = OrderedSet.ofSingleton(_uIN.snd); } -} - -export default MultiSet \ No newline at end of file +} \ No newline at end of file diff --git a/src/structure/collections/ordered-set.ts b/src/structure/collections/ordered-set.ts index a1d5e2e2d..2fc4b15b5 100644 --- a/src/structure/collections/ordered-set.ts +++ b/src/structure/collections/ordered-set.ts @@ -22,21 +22,22 @@ namespace OrderedSet { return xs as any; } - export const size: (set: OrderedSet) => number = sizeI as any; export const has: (set: OrderedSet, x: number) => boolean = hasI as any; export const indexOf: (set: OrderedSet, x: number) => number = indexOfI as any; export const getAt: (set: OrderedSet, i: number) => number = getAtI as any; + export const min: (set: OrderedSet) => number = minI as any; export const max: (set: OrderedSet) => number = maxI as any; + export const size: (set: OrderedSet) => number = sizeI as any; export const hashCode: (set: OrderedSet) => number = hashCodeI as any; - export const areEqual: BinaryTest = areEqualI as any; - export const areIntersecting: BinaryTest = areIntersectingI as any; - export const isSubset: BinaryTest = isSubsetI as any; + export const areEqual: (a: OrderedSet, b: OrderedSet) => boolean = areEqualI as any; + export const areIntersecting: (a: OrderedSet, b: OrderedSet) => boolean = areIntersectingI as any; + export const isSubset: (a: OrderedSet, b: OrderedSet) => boolean = isSubsetI as any; - export const union: BinaryOp = unionI as any; - export const intersect: BinaryOp = intersectI as any; - export const subtract: BinaryOp = subtractI as any; + export const union: (a: OrderedSet, b: OrderedSet) => OrderedSet = unionI as any; + export const intersect: (a: OrderedSet, b: OrderedSet) => OrderedSet = intersectI as any; + export const subtract: (a: OrderedSet, b: OrderedSet) => OrderedSet = subtractI as any; export const getInsertionIndex: (set: OrderedSet, x: number) => number = getInsertionIndexI as any; export const getIntervalRange: (set: OrderedSet, min: number, max: number) => { start: number, end: number } = getIntervalRangeI as any; @@ -46,9 +47,6 @@ export default OrderedSet /** Long and painful implementation starts here */ -type BinaryTest = (a: OrderedSet, b: OrderedSet) => boolean -type BinaryOp = (a: OrderedSet, b: OrderedSet) => OrderedSet - type Range = IntTuple type SortedArray = ArrayLike<number> type OrderedSetImpl = Range | SortedArray -- GitLab