Skip to content
Snippets Groups Projects
Commit fbd7c35f authored by David Sehnal's avatar David Sehnal
Browse files

collections refactoring

parent 2f78edea
No related branches found
No related tags found
No related merge requests found
...@@ -11,46 +11,76 @@ import { sortArray } from './sort' ...@@ -11,46 +11,76 @@ import { sortArray } from './sort'
import { hash1 } from './hash-functions' import { hash1 } from './hash-functions'
/** A map-like representation of integer set */ /** A map-like representation of integer set */
interface MultiSet { /*'@type': 'int-multi-set'*/ } interface MultiSet { '@type': 'int-multi-set' }
namespace MultiSet { namespace MultiSet {
export const Empty: MultiSet = { offsets: [0], hashCode: 0, keys: OrderedSet.Empty } as MultiSetElements as any; 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 { export function create(data: IntTuple | ArrayLike<IntTuple> | IntTuple | { [id: number]: OrderedSet }): MultiSet {
if (typeof data === 'number') return data; if (typeof data === 'number') return data;
if (IntTuple.is(data)) return IntTuple.pack1(data) as number; if (IntTuple.is(data)) return IntTuple.pack1(data) as any;
if (isArrayLike(data)) return ofTuples(data); if (isArrayLike(data)) return ofTuples(data) as any;
return ofObject(data as { [id: number]: OrderedSet }); return ofObject(data as { [id: number]: OrderedSet }) as any;
} }
export function keys(set: MultiSet): OrderedSet { 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 default MultiSet
/** Long and painful implementation starts here */
interface MultiSetElements { [id: number]: OrderedSet, offsets: number[], hashCode: number, keys: OrderedSet }
type MultiSetImpl = IntTuple | MultiSetElements
function keysI(set: MultiSetImpl): OrderedSet {
if (typeof set === 'number') return OrderedSet.ofSingleton(set); if (typeof set === 'number') return OrderedSet.ofSingleton(set);
return (set as MultiSetElements).keys; return (set as MultiSetElements).keys;
} }
export function keyCount(set: MultiSet): number { function keyCountI(set: MultiSetImpl): number {
if (typeof set === 'number') return 1; if (typeof set === 'number') return 1;
return OrderedSet.size((set as MultiSetElements).keys); return OrderedSet.size((set as MultiSetElements).keys);
} }
export function hasKey(set: MultiSet, key: number): boolean { function hasKeyI(set: MultiSetImpl, key: number): boolean {
if (typeof set === 'number') return IntTuple.fst(set) === key; if (typeof set === 'number') return IntTuple.fst(set) === key;
return OrderedSet.has((set as MultiSetElements).keys, key); return OrderedSet.has((set as MultiSetElements).keys, key);
} }
export function getKey(set: MultiSet, index: number): number { function getKeyI(set: MultiSetImpl, index: number): number {
if (typeof set === 'number') return IntTuple.fst(set); if (typeof set === 'number') return IntTuple.fst(set);
return OrderedSet.getAt((set as MultiSetElements).keys, index); return OrderedSet.getAt((set as MultiSetElements).keys, index);
} }
export function has(set: MultiSet, t: IntTuple): boolean { function hasI(set: MultiSetImpl, t: IntTuple): boolean {
if (typeof set === 'number') return IntTuple.areEqual(t, set); if (typeof set === 'number') return IntTuple.areEqual(t, set);
IntTuple.unpack(t, _hasP); IntTuple.unpack(t, _hasP);
return OrderedSet.has((set as MultiSetElements).keys, _hasP.fst) ? OrderedSet.has((set as MultiSetElements)[_hasP.fst], _hasP.snd) : false; return OrderedSet.has((set as MultiSetElements).keys, _hasP.fst) ? OrderedSet.has((set as MultiSetElements)[_hasP.fst], _hasP.snd) : false;
} }
const _hasP = IntTuple.zero(); const _hasP = IntTuple.zero();
export function getByKey(set: MultiSet, key: number): OrderedSet { function getByKeyI(set: MultiSetImpl, key: number): OrderedSet {
if (typeof set === 'number') { if (typeof set === 'number') {
IntTuple.unpack(set, _gS); IntTuple.unpack(set, _gS);
return _gS.fst === key ? OrderedSet.ofSingleton(_gS.snd) : OrderedSet.Empty; return _gS.fst === key ? OrderedSet.ofSingleton(_gS.snd) : OrderedSet.Empty;
...@@ -59,35 +89,35 @@ namespace MultiSet { ...@@ -59,35 +89,35 @@ namespace MultiSet {
} }
const _gS = IntTuple.zero(); const _gS = IntTuple.zero();
export function getByIndex(set: MultiSet, index: number): OrderedSet { function getByIndexI(set: MultiSetImpl, index: number): OrderedSet {
if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(IntTuple.snd(set)) : OrderedSet.Empty; if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(IntTuple.snd(set)) : OrderedSet.Empty;
const key = OrderedSet.getAt((set as MultiSetElements).keys, index); const key = OrderedSet.getAt((set as MultiSetElements).keys, index);
return (set as MultiSetElements)[key] || OrderedSet.Empty; return (set as MultiSetElements)[key] || OrderedSet.Empty;
} }
export function getAt(set: MultiSet, i: number): IntTuple { function getAtI(set: MultiSetImpl, i: number): IntTuple {
if (typeof set === 'number') return set; if (typeof set === 'number') return set;
return getAtE(set as MultiSetElements, i); return getAtE(set as MultiSetElements, i);
} }
export function indexOf(set: MultiSet, t: IntTuple) { function indexOfI(set: MultiSetImpl, t: IntTuple) {
if (typeof set === 'number') return IntTuple.areEqual(set, t) ? 0 : -1; if (typeof set === 'number') return IntTuple.areEqual(set, t) ? 0 : -1;
return indexOfE(set as MultiSetElements, t); return indexOfE(set as MultiSetElements, t);
} }
/** Number elements in the "child" sets */ /** Number elements in the "child" sets */
export function size(set: MultiSet) { function sizeI(set: MultiSetImpl) {
if (typeof set === 'number') return 1; if (typeof set === 'number') return 1;
return (set as MultiSetElements).offsets[(set as MultiSetElements).offsets.length - 1]; return (set as MultiSetElements).offsets[(set as MultiSetElements).offsets.length - 1];
} }
export function hashCode(set: MultiSet) { function hashCodeI(set: MultiSetImpl) {
if (typeof set === 'number') return IntTuple.hashCode(set); if (typeof set === 'number') return IntTuple.hashCode(set);
if ((set as MultiSetElements).hashCode !== -1) return (set as MultiSetElements).hashCode; if ((set as MultiSetElements).hashCode !== -1) return (set as MultiSetElements).hashCode;
return computeHash((set as MultiSetElements)); return computeHash((set as MultiSetElements));
} }
export function areEqual(a: MultiSet, b: MultiSet): boolean { function areEqualI(a: MultiSetImpl, b: MultiSetImpl) {
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return IntTuple.areEqual(a, b); if (typeof b === 'number') return IntTuple.areEqual(a, b);
return false; return false;
...@@ -96,7 +126,7 @@ namespace MultiSet { ...@@ -96,7 +126,7 @@ namespace MultiSet {
return areEqualEE(a as MultiSetElements, b as MultiSetElements); return areEqualEE(a as MultiSetElements, b as MultiSetElements);
} }
export function areIntersecting(a: MultiSet, b: MultiSet): boolean { function areIntersectingI(a: MultiSetImpl, b: MultiSetImpl) {
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return IntTuple.areEqual(a, b); if (typeof b === 'number') return IntTuple.areEqual(a, b);
return areIntersectingNE(a, b as MultiSetElements); return areIntersectingNE(a, b as MultiSetElements);
...@@ -105,29 +135,29 @@ namespace MultiSet { ...@@ -105,29 +135,29 @@ namespace MultiSet {
return areIntersectingEE(a as MultiSetElements, b as MultiSetElements); return areIntersectingEE(a as MultiSetElements, b as MultiSetElements);
} }
export function intersect(a: MultiSet, b: MultiSet): MultiSet { function intersectI(a: MultiSetImpl, b: MultiSetImpl) {
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return IntTuple.areEqual(a, b) ? a : Empty; if (typeof b === 'number') return IntTuple.areEqual(a, b) ? a : MultiSet.Empty;
return intersectNE(a, b as MultiSetElements); return intersectNE(a, b as MultiSetElements);
} }
if (typeof b === 'number') return intersectNE(b, a as MultiSetElements); if (typeof b === 'number') return intersectNE(b, a as MultiSetElements);
return intersectEE(a as MultiSetElements, b as MultiSetElements); return intersectEE(a as MultiSetElements, b as MultiSetElements);
} }
export function subtract(a: MultiSet, b: MultiSet): MultiSet { function subtractI(a: MultiSetImpl, b: MultiSetImpl) {
if (typeof a === 'number') { if (typeof a === 'number') {
if (typeof b === 'number') return IntTuple.areEqual(a, b) ? Empty : a; if (typeof b === 'number') return IntTuple.areEqual(a, b) ? MultiSet.Empty : a;
return subtractNE(a, b as MultiSetElements); return subtractNE(a, b as MultiSetElements);
} }
if (typeof b === 'number') return subtractEN(a as MultiSetElements, b); if (typeof b === 'number') return subtractEN(a as MultiSetElements, b);
return subtractEE(a as MultiSetElements, b as MultiSetElements); return subtractEE(a as MultiSetElements, b as MultiSetElements);
} }
export function union(a: MultiSet, b: MultiSet): MultiSet { function unionI(a: MultiSetImpl, b: MultiSetImpl) {
return findUnion([a, b]); return findUnion([a, b]);
} }
export function unionMany(sets: ArrayLike<MultiSet>): MultiSet { function unionManyI(sets: ArrayLike<MultiSetImpl>) {
return findUnion(sets); return findUnion(sets);
} }
...@@ -175,13 +205,10 @@ namespace MultiSet { ...@@ -175,13 +205,10 @@ namespace MultiSet {
} }
} }
export function values(set: MultiSet): Iterator<IntTuple.Unpacked> { function valuesI(set: MultiSetImpl): Iterator<IntTuple.Unpacked> {
if (typeof set === 'number') return Iterator.Value(IntTuple.unpack1(set)); if (typeof set === 'number') return Iterator.Value(IntTuple.unpack1(set));
return new ElementsIterator(set as MultiSetElements); return new ElementsIterator(set as MultiSetElements);
} }
}
interface MultiSetElements { [id: number]: OrderedSet, offsets: number[], hashCode: number, keys: OrderedSet }
function isArrayLike(x: any): x is ArrayLike<number> { function isArrayLike(x: any): x is ArrayLike<number> {
return x && (typeof x.length === 'number' && (x instanceof Array || !!x.buffer)); return x && (typeof x.length === 'number' && (x instanceof Array || !!x.buffer));
...@@ -259,7 +286,7 @@ function normalizeArray(xs: number[]) { ...@@ -259,7 +286,7 @@ function normalizeArray(xs: number[]) {
return xs; return xs;
} }
function ofTuples(xs: ArrayLike<IntTuple>): MultiSet { function ofTuples(xs: ArrayLike<IntTuple>) {
if (xs.length === 0) return MultiSet.Empty; if (xs.length === 0) return MultiSet.Empty;
const sets: { [key: number]: number[] } = Object.create(null); const sets: { [key: number]: number[] } = Object.create(null);
const p = IntTuple.zero(); const p = IntTuple.zero();
...@@ -322,7 +349,7 @@ function computeHash(set: MultiSetElements) { ...@@ -322,7 +349,7 @@ function computeHash(set: MultiSetElements) {
hash = (31 * hash + k) | 0; hash = (31 * hash + k) | 0;
hash = (31 * hash + OrderedSet.hashCode(set[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); hash = hash1(hash);
set.hashCode = hash; set.hashCode = hash;
return hash; return hash;
...@@ -330,7 +357,7 @@ function computeHash(set: MultiSetElements) { ...@@ -330,7 +357,7 @@ function computeHash(set: MultiSetElements) {
function areEqualEE(a: MultiSetElements, b: MultiSetElements) { function areEqualEE(a: MultiSetElements, b: MultiSetElements) {
if (a === b) return true; 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; const keys = a.keys;
if (!OrderedSet.areEqual(keys, b.keys)) return false; if (!OrderedSet.areEqual(keys, b.keys)) return false;
...@@ -393,7 +420,7 @@ function subtractNE(a: IntTuple, b: MultiSetElements) { ...@@ -393,7 +420,7 @@ function subtractNE(a: IntTuple, b: MultiSetElements) {
} }
const _sEN = IntTuple.zero(); const _sEN = IntTuple.zero();
function subtractEN(a: MultiSetElements, b: IntTuple): MultiSet { function subtractEN(a: MultiSetElements, b: IntTuple): MultiSetImpl {
const aKeys = a.keys; const aKeys = a.keys;
IntTuple.unpack(b, _sEN); IntTuple.unpack(b, _sEN);
if (!OrderedSet.has(aKeys, _sEN.fst) || !OrderedSet.has(a[_sEN.fst], _sEN.snd)) return a; 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) { ...@@ -439,10 +466,10 @@ function subtractEE(a: MultiSetElements, b: MultiSetElements) {
return ofObjectOrdered(OrderedSet.ofSortedArray(keys), ret); 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) return MultiSet.Empty;
if (sets.length === 1) return sets[0]; 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 eCount = { count: 0 };
const ns = unionN(sets, eCount); const ns = unionN(sets, eCount);
...@@ -452,14 +479,14 @@ function findUnion(sets: ArrayLike<MultiSet>) { ...@@ -452,14 +479,14 @@ function findUnion(sets: ArrayLike<MultiSet>) {
const s = sets[i]; const s = sets[i];
if (typeof s !== 'number') unionInto(ret, s as MultiSetElements); 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); if (typeof ns === 'number') unionIntoN(ret, ns);
else unionInto(ret, ns as MultiSetElements); else unionInto(ret, ns as MultiSetElements);
} }
return ofObject(ret); return ofObject(ret);
} }
function unionN(sets: ArrayLike<MultiSet>, eCount: { count: number }) { function unionN(sets: ArrayLike<MultiSetImpl>, eCount: { count: number }) {
let countN = 0, countE = 0; let countN = 0, countE = 0;
for (let i = 0, _i = sets.length; i < _i; i++) { for (let i = 0, _i = sets.length; i < _i; i++) {
if (typeof sets[i] === 'number') countN++; if (typeof sets[i] === 'number') countN++;
...@@ -497,5 +524,3 @@ function unionIntoN(data: { [key: number]: OrderedSet }, a: IntTuple) { ...@@ -497,5 +524,3 @@ function unionIntoN(data: { [key: number]: OrderedSet }, a: IntTuple) {
data[_uIN.fst] = OrderedSet.ofSingleton(_uIN.snd); data[_uIN.fst] = OrderedSet.ofSingleton(_uIN.snd);
} }
} }
\ No newline at end of file
export default MultiSet
\ No newline at end of file
...@@ -22,21 +22,22 @@ namespace OrderedSet { ...@@ -22,21 +22,22 @@ namespace OrderedSet {
return xs as any; 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 has: (set: OrderedSet, x: number) => boolean = hasI as any;
export const indexOf: (set: OrderedSet, x: number) => number = indexOfI 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 getAt: (set: OrderedSet, i: number) => number = getAtI as any;
export const min: (set: OrderedSet) => number = minI as any; export const min: (set: OrderedSet) => number = minI as any;
export const max: (set: OrderedSet) => number = maxI 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 hashCode: (set: OrderedSet) => number = hashCodeI as any;
export const areEqual: BinaryTest = areEqualI as any; export const areEqual: (a: OrderedSet, b: OrderedSet) => boolean = areEqualI as any;
export const areIntersecting: BinaryTest = areIntersectingI as any; export const areIntersecting: (a: OrderedSet, b: OrderedSet) => boolean = areIntersectingI as any;
export const isSubset: BinaryTest = isSubsetI as any; export const isSubset: (a: OrderedSet, b: OrderedSet) => boolean = isSubsetI as any;
export const union: BinaryOp = unionI as any; export const union: (a: OrderedSet, b: OrderedSet) => OrderedSet = unionI as any;
export const intersect: BinaryOp = intersectI as any; export const intersect: (a: OrderedSet, b: OrderedSet) => OrderedSet = intersectI as any;
export const subtract: BinaryOp = subtractI 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 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; export const getIntervalRange: (set: OrderedSet, min: number, max: number) => { start: number, end: number } = getIntervalRangeI as any;
...@@ -46,9 +47,6 @@ export default OrderedSet ...@@ -46,9 +47,6 @@ export default OrderedSet
/** Long and painful implementation starts here */ /** Long and painful implementation starts here */
type BinaryTest = (a: OrderedSet, b: OrderedSet) => boolean
type BinaryOp = (a: OrderedSet, b: OrderedSet) => OrderedSet
type Range = IntTuple type Range = IntTuple
type SortedArray = ArrayLike<number> type SortedArray = ArrayLike<number>
type OrderedSetImpl = Range | SortedArray type OrderedSetImpl = Range | SortedArray
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment