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

collections

parent d48e496c
Branches
Tags
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.
Please register or to comment