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

collections

parent d48e496c
No related branches found
No related tags found
No related merge requests found
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
/**
* Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
interface SetLike<T> {
readonly size: number;
add(a: T): boolean;
has(a: T): boolean;
}
class HashSetImpl<T> implements SetLike<T> {
size: number = 0;
private byHash: { [hash: number]: T[] } = Object.create(null);
add(a: T) {
const hash = this.getHash(a);
if (this.byHash[hash]) {
const xs = this.byHash[hash];
for (const x of xs) {
if (this.areEqual(a, x)) return false;
}
xs[xs.length] = a;
this.size++;
return true;
} else {
this.byHash[hash] = [a];
this.size++;
return true;
}
}
has(v: T) {
const hash = this.getHash(v);
if (!this.byHash[hash]) return false;
for (const x of this.byHash[hash]) {
if (this.areEqual(v, x)) return true;
}
return false;
}
constructor(private getHash: (v: T) => any, private areEqual: (a: T, b: T) => boolean) { }
}
function HashSet<T>(getHash: (v: T) => any, areEqual: (a: T, b: T) => boolean): SetLike<T> {
return new HashSetImpl<T>(getHash, areEqual);
}
export default HashSet;
\ 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>
*/
/** A data structure useful for graph traversal */
interface LinkedIndex {
readonly head: number,
has(i: number): boolean,
remove(i: number): void
}
function LinkedIndex(size: number): LinkedIndex {
return new LinkedIndexImpl(size);
}
class LinkedIndexImpl implements LinkedIndex {
private prev: Int32Array;
private next: Int32Array;
head: number;
remove(i: number) {
const { prev, next } = this;
const p = prev[i], n = next[i];
if (p >= 0) {
next[p] = n;
prev[i] = -1;
}
if (n >= 0) {
prev[n] = p;
next[i] = -1;
}
if (i === this.head) {
if (p < 0) this.head = n;
else this.head = p;
}
}
has(i: number) {
return this.prev[i] >= 0 || this.next[i] >= 0;
}
constructor(size: number) {
this.head = size > 0 ? 0 : -1;
this.prev = new Int32Array(size);
this.next = new Int32Array(size);
for (let i = 0; i < size; i++) {
this.next[i] = i + 1;
this.prev[i] = i - 1;
}
this.prev[0] = -1;
this.next[size - 1] = -1;
}
}
export default LinkedIndex;
\ No newline at end of file
// TODO: fixed length doubly linked list used for graph traversal
\ No newline at end of file
......@@ -21,6 +21,29 @@ namespace RangeSet {
toArray(): ArrayLike<number>
}
export function hashCode(a: RangeSet) {
// hash of tuple (size, min, max, mid)
const { size } = a;
let hash = 23;
if (!size) return hash;
hash = 31 * hash + size;
hash = 31 * hash + a.elementAt(0);
hash = 31 * hash + a.elementAt(size - 1);
if (size > 2) hash = 31 * hash + a.elementAt(size >> 1);
return hash;
}
export function areEqual(a: RangeSet, b: RangeSet) {
if (a === b) return true;
if (a instanceof RangeImpl) {
if (b instanceof RangeImpl) return a.min === b.min && a.max === b.max;
return equalAR(b as ArrayImpl, a);
} else if (b instanceof RangeImpl) {
return equalAR(a as ArrayImpl, b);
}
return equalAA(a as ArrayImpl, b as ArrayImpl);
}
export function union(a: RangeSet, b: RangeSet) {
if (a instanceof RangeImpl) {
if (b instanceof RangeImpl) return unionRR(a, b);
......@@ -107,6 +130,20 @@ namespace RangeSet {
return -1;
}
function equalAR(a: ArrayImpl, b: RangeImpl) {
return a.size === b.size && a.min === b.min && a.max === b.max;
}
function equalAA(a: ArrayImpl, b: ArrayImpl) {
if (a.size !== b.size || a.min !== b.min || a.max !== b.max) return false;
const { size, values: xs } = a;
const { values: ys } = b;
for (let i = 0; i < size; i++) {
if (xs[i] !== ys[i]) return false;
}
return true;
}
function areRangesIntersecting(a: Impl, b: Impl) {
return a.size > 0 && b.size > 0 && a.max >= b.min && a.min <= b.max;
}
......@@ -150,6 +187,8 @@ namespace RangeSet {
function unionAA(xs: ArrayLike<number>, ys: ArrayLike<number>) {
const la = xs.length, lb = ys.length;
// sorted list merge.
let i = 0, j = 0, resultSize = 0;
while (i < la && j < lb) {
const x = xs[i], y = ys[j];
......@@ -203,6 +242,8 @@ namespace RangeSet {
function intersectAA(xs: ArrayLike<number>, ys: ArrayLike<number>) {
const la = xs.length, lb = ys.length;
// a variation on sorted list merge.
let i = 0, j = 0, resultSize = 0;
while (i < la && j < lb) {
const x = xs[i], y = ys[j];
......
......@@ -137,6 +137,12 @@ describe('range set', () => {
testEq('range', range, [1, 2, 3, 4]);
testEq('sorted array', arr, [1, 3, 6]);
expect(RangeSet.areEqual(empty, singleton)).toBe(false);
expect(RangeSet.areEqual(singleton, singleton)).toBe(true);
expect(RangeSet.areEqual(range, singleton)).toBe(false);
expect(RangeSet.areEqual(arr, RangeSet.ofSortedArray([1, 3, 6]))).toBe(true);
expect(RangeSet.areEqual(arr, RangeSet.ofSortedArray([1, 4, 6]))).toBe(false);
expect(empty.has(10)).toBe(false);
expect(empty.indexOf(10)).toBe(-1);
......
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