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,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
......@@ -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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment