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

multiset

parent f9419bb4
No related branches found
No related tags found
No related merge requests found
......@@ -7,25 +7,72 @@
interface IntPair { fst: number, snd: number }
namespace IntPair {
const { _int32, _float64 } = (function() {
const { _int32, _float64, _int32_1, _float64_1 } = (function() {
const data = new ArrayBuffer(8);
return { _int32: new Int32Array(data), _float64: new Float64Array(data) };
const data_1 = new ArrayBuffer(8);
return {
_int32: new Int32Array(data),
_float64: new Float64Array(data),
_int32_1: new Int32Array(data_1),
_float64_1: new Float64Array(data_1)
};
}());
export function is(x: any): x is IntPair {
return !!x && typeof x.fst === 'number' && typeof x.snd === 'number';
}
export function create(fst: number, snd: number) { return { fst, snd }; }
export function zero(): IntPair { return { fst: 0, snd: 0 }; }
export function set(fst: number, snd: number): number {
export function set1(fst: number, snd: number): number {
_int32[0] = fst;
_int32[1] = snd;
return _float64[0];
}
export function set(p: IntPair): number {
_int32[0] = p.fst;
_int32[1] = p.snd;
return _float64[0];
}
export function get(packed: number, target: IntPair): IntPair {
_float64[0] = packed;
target.fst = _int32[0];
target.snd = _int32[1];
return target;
}
export function get1(packed: number): IntPair {
return get(packed, zero());
}
export function fst(packed: number): number {
_float64[0] = packed;
return _int32[0];
}
export function snd(packed: number): number {
_float64[0] = packed;
return _int32[1];
}
export function compare(a: number, b: number) {
_float64[0] = a;
_float64_1[0] = b;
const x = _int32[0] - _int32_1[0];
if (x !== 0) return x;
return _int32[1] - _int32_1[1];
}
export function compareInArray(xs: ArrayLike<number>, i: number, j: number) {
_float64[0] = xs[i];
_float64_1[0] = xs[j];
const x = _int32[0] - _int32_1[0];
if (x !== 0) return x;
return _int32[1] - _int32_1[1];
}
}
export default IntPair
\ No newline at end of file
......@@ -10,11 +10,11 @@
* "Idiomatic" usage is to use the move function, because it does not make any allocations.
*
* const it = ...;
* for (let v = it.move(); it.hasNext; v = it.move()) { ... }
* for (let v = it.move(); !it.done; v = it.move()) { ... }
*/
interface Iterator<T> {
[Symbol.iterator](): Iterator<T>,
readonly hasNext: boolean,
readonly done: boolean,
next(): { done: boolean, value: T },
move(): T
}
......@@ -25,24 +25,24 @@ class ArrayIteratorImpl<T> implements Iterator<T> {
private length: number = 0;
private lastValue: T;
[Symbol.iterator]() { return this; };
hasNext: boolean;
[Symbol.iterator]() { return new ArrayIteratorImpl(this.xs); };
done: boolean;
next() {
const value = this.move();
return { value, done: !this.hasNext };
return { value, done: this.done };
}
move() {
const index = ++this.index;
if (index < this.length) this.lastValue = this.xs[index];
else this.hasNext = false;
else this.done = true;
return this.lastValue;
}
constructor(xs: ArrayLike<T>) {
this.length = xs.length;
this.hasNext = xs.length > 0;
this.done = xs.length === 0;
this.xs = xs;
this.index = -1;
// try to avoid deoptimization with undefined values
......@@ -54,23 +54,23 @@ class ArrayIteratorImpl<T> implements Iterator<T> {
class RangeIteratorImpl implements Iterator<number> {
private value: number;
[Symbol.iterator]() { return this; };
hasNext: boolean;
[Symbol.iterator]() { return new RangeIteratorImpl(this.min, this.max); };
done: boolean;
next() {
const value = this.value;
return { value, done: !this.hasNext }
const value = this.move();
return { value, done: this.done }
}
move() {
++this.value;
this.hasNext = this.value <= this.max;
this.done = this.value > this.max;
return this.value;
}
constructor(min: number, private max: number) {
constructor(private min: number, private max: number) {
this.value = min - 1;
this.hasNext = max >= min;
this.done = max < min;
return this;
}
}
......@@ -80,12 +80,6 @@ namespace Iterator {
export function Array<T>(xs: ArrayLike<T>): Iterator<T> { return new ArrayIteratorImpl<T>(xs); }
export function Value(value: number): Iterator<number> { return new RangeIteratorImpl(value, value); }
export function Range(min: number, max: number): Iterator<number> { return new RangeIteratorImpl(min, max); }
export function toArray<T>(it: Iterator<T>): T[] {
const ret = [];
for (let v = it.move(); it.hasNext; v = it.move()) ret[ret.length] = v;
return ret;
}
}
export default Iterator
\ No newline at end of file
/**
* Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import OrderedSet from './ordered-set'
import Iterator from './iterator'
import IntPair from './int-pair'
import { sortArray } from './sort'
type MultiSetElements = { [id: number]: OrderedSet, keys: OrderedSet }
type MultiSet = number | MultiSetElements
namespace MultiSet {
export const Empty: MultiSet = { keys: OrderedSet.Empty };
export function keys(set: MultiSet): OrderedSet {
if (typeof set === 'number') return OrderedSet.ofSingleton(set);
return set.keys;
}
export function hasKey(set: MultiSet, key: number): boolean {
if (typeof set === 'number') return IntPair.fst(set) === key;
return set.keys.has(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];
}
function isArrayLike(x: any): x is ArrayLike<number> {
return x && (typeof x.length === 'number' && (x instanceof Array || !!x.buffer));
}
export function create(data: number | ArrayLike<number> | IntPair | { [id: number]: OrderedSet }): MultiSet {
if (typeof data === 'number') return data;
if (IntPair.is(data)) return IntPair.set(data);
if (isArrayLike(data)) return ofPackedPairs(data);
const keys = [];
for (const _k of Object.keys(data)) {
const k = +_k;
if (data[k].size > 0) keys[keys.length] = k;
}
if (!keys.length) return Empty;
sortArray(keys);
const ret = Object.create(null);
ret.keys = OrderedSet.ofSortedArray(keys);
for (const k of keys) ret[k] = data[k];
return ret;
}
class PairIterator implements Iterator<IntPair> {
private yielded = false;
[Symbol.iterator]() { return new PairIterator(this.pair); };
done = false;
next() { const value = this.move(); return { value, done: this.done } }
move() { this.done = this.yielded; this.yielded = true; return this.pair; }
constructor(private pair: IntPair) { }
}
class ElementsIterator implements Iterator<IntPair> {
private pair = IntPair.zero();
private unit = 0;
private currentIndex = -1;
private currentIterator: Iterator<number> = Iterator.Empty;
[Symbol.iterator]() { return new ElementsIterator(this.elements); };
done: boolean;
next() { const value = this.move(); return { value, done: this.done } }
move() {
if (this.done) return this.pair;
let next = this.currentIterator.move();
if (this.currentIterator.done) {
if (!this.advanceIterator()) {
this.done = true;
return this.pair;
}
next = this.currentIterator.move();
}
this.pair.snd = next;
return this.pair;
}
private advanceIterator() {
const keys = this.elements.keys;
if (++this.currentIndex >= keys.size) return false;
this.unit = keys.elementAt(this.currentIndex);
this.pair.fst = this.unit;
this.currentIterator = this.elements[this.unit].elements();
return true;
}
constructor(private elements: MultiSetElements) {
this.done = elements.keys.size === 0;
this.advanceIterator();
}
}
export function values(set: MultiSet): Iterator<IntPair> {
if (typeof set === 'number') return new PairIterator(IntPair.get1(set));
return new ElementsIterator(set);
}
function ofPackedPairs(xs: ArrayLike<number>): MultiSet {
if (xs.length === 0) return Empty;
sortArray(xs, IntPair.compareInArray);
const p = IntPair.zero();
IntPair.get(xs[0], p);
let currentKey = p.fst;
let keys = [];
let currentSet = [p.snd];
let ret = Object.create(null);
for (let i = 1, _i = xs.length; i < _i; i++) {
IntPair.get(xs[i], p);
if (p.fst === currentKey) {
currentSet[currentSet.length] = p.snd;
} else {
ret[currentKey] = OrderedSet.ofSortedArray(currentSet);
keys[keys.length] = currentKey;
currentKey = p.fst;
currentSet = [p.snd];
}
}
ret[currentKey] = OrderedSet.ofSortedArray(currentSet);
keys[keys.length] = currentKey;
ret.keys = OrderedSet.ofSortedArray(keys);
return ret;
}
export function union(sets: ArrayLike<MultiSet>): MultiSet {
return 0 as any;
}
}
export default MultiSet
\ No newline at end of file
......@@ -9,11 +9,19 @@ import IntPair from '../collections/int-pair'
import * as Sort from '../collections/sort'
import OrderedSet from '../collections/ordered-set'
import LinkedIndex from '../collections/linked-index'
import MultiSet from '../collections/multi-set'
function iteratorToArray<T>(it: Iterator<T>): T[] {
const ret = [];
for (let v = it.move(); !it.done; v = it.move()) ret[ret.length] = v;
return ret;
}
describe('basic iterators', () => {
function check<T>(name: string, iter: Iterator<T>, expected: T[]) {
it(name, () => {
expect(Iterator.toArray(iter)).toEqual(expected);
expect(iteratorToArray(iter)).toEqual(expected);
});
}
......@@ -28,7 +36,7 @@ describe('int pair', () => {
const p = IntPair.zero();
for (let i = 0; i < 10; i++) {
for (let j = -10; j < 5; j++) {
const t = IntPair.set(i, j);
const t = IntPair.set1(i, j);
IntPair.get(t, p);
expect(p.fst).toBe(i);
expect(p.snd).toBe(j);
......@@ -124,7 +132,7 @@ describe('range set', () => {
function testEq(name: string, set: OrderedSet, expected: number[]) {
it(name, () => {
// copy the arrays to ensure "compatibility" between typed and native arrays
expect(Array.prototype.slice.call(Iterator.toArray(set.elements()))).toEqual(Array.prototype.slice.call(expected));
expect(Array.prototype.slice.call(iteratorToArray(set.elements()))).toEqual(Array.prototype.slice.call(expected));
});
}
......@@ -259,4 +267,39 @@ describe('linked-index', () => {
expect(index.has(0)).toBe(false);
expect(index.has(1)).toBe(false);
});
});
describe('multiset', () => {
const p = (i: number, j: number) => IntPair.create(i, j);
const r = (i: number, j: number) => IntPair.set1(i, j);
function iteratorPairsToArray(it: Iterator<IntPair>): IntPair[] {
const ret = [];
for (let v = it.move(); !it.done; v = it.move()) ret[ret.length] = IntPair.create(v.fst, v.snd);
return ret;
}
it('singleton pair', () => {
const set = MultiSet.create(p(10, 11));
expect(iteratorPairsToArray(MultiSet.values(set))).toEqual([p(10, 11)]);
});
it('singleton number', () => {
const set = MultiSet.create(r(10, 11));
expect(iteratorPairsToArray(MultiSet.values(set))).toEqual([p(10, 11)]);
});
it('multi', () => {
const set = MultiSet.create({
1: OrderedSet.ofSortedArray([4, 6, 7]),
3: OrderedSet.ofRange(0, 1),
});
expect(iteratorPairsToArray(MultiSet.values(set))).toEqual([p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]);
});
it('packed pairs', () => {
const set = MultiSet.create([r(1, 3), r(0, 1), r(0, 6), r(0, 2)]);
expect(iteratorPairsToArray(MultiSet.values(set))).toEqual([p(0, 1), p(0, 2), p(0, 6), p(1, 3)]);
});
});
\ No newline at end of file
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