Aktueller Stand

This commit is contained in:
2026-01-22 19:05:45 +01:00
parent 85dee61a4d
commit e280e4eadb
1967 changed files with 397327 additions and 74093 deletions

View File

@@ -286,6 +286,51 @@ For a detailed discussion about it, take a look at [this issue](https://github.c
[Error codes](https://github.com/nearform/fast-jwt/blob/master/src/error.js) exported by `TOKEN_ERROR_CODES`.
## Error Handling
When using the verifier, errors can occur due to various reasons such as an expired token, an invalid signature, or a malformed token. `fast-jwt` throws a `TokenError` when such issues are encountered. You can catch this error and inspect its `code` property to determine the specific cause of the error. The possible values for the `code` property are listed in the `TOKEN_ERROR_CODES` object (see the "Token Error Codes" section above for more details).
```javascript
const { createVerifier, createSigner, TOKEN_ERROR_CODES } = require('fast-jwt')
// Example 1: Handling an expired token
const sign = createSigner({ key: 'secret', expiresIn: '1ms' }) // Token expires almost immediately
const verify = createVerifier({ key: 'secret' })
const expiredToken = sign({ foo: 'bar' })
// Wait for a moment to ensure the token expires
setTimeout(() => {
try {
verify(expiredToken)
} catch (err) {
if (err.code === TOKEN_ERROR_CODES.expired) {
console.error('Token verification failed because the token has expired.')
// Handle expired token error (e.g., prompt user to re-authenticate)
} else {
console.error('An unexpected error occurred:', err.message)
}
}
}, 100); // Wait 100ms, which is longer than the token's 1ms validity
// Example 2: Handling an invalid signature (e.g., wrong secret)
const correctSigner = createSigner({ key: 'correct-secret' })
const verifierWithWrongKey = createVerifier({ key: 'wrong-secret' })
const tokenSignedWithCorrectKey = correctSigner({ data: 'payload' })
try {
verifierWithWrongKey(tokenSignedWithCorrectKey)
} catch (err) {
if (err.code === TOKEN_ERROR_CODES.invalidSignature) {
console.error('Token verification failed due to an invalid signature. This might be due to a key mismatch.')
// Handle invalid signature error
} else {
console.error('An unexpected error occurred:', err.message)
}
}
```
## JWKS
JWKS is supported via [get-jwks](https://github.com/nearform/get-jwks). Check out the documentation for integration examples.

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Guillaume Plique (Yomguithereal)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,112 +0,0 @@
[![Build Status](https://github.com/Yomguithereal/mnemonist/actions/workflows/tests.yml/badge.svg)](https://github.com/Yomguithereal/mnemonist/actions)
# Mnemonist
Mnemonist is a curated collection of data structures for the JavaScript language.
It gathers classic data structures (think heap, trie etc.) as well as more exotic ones such as Buckhard-Keller trees etc.
It strives at being:
* As performant as possible for a high-level language.
* Completely modular (don't need to import the whole library just to use a simple heap).
* Simple & straightforward to use and consistent with JavaScript standard objects' API.
* Completely typed and comfortably usable with Typescript.
## Installation
```
npm install --save mnemonist
```
## Documentation
Full documentation for the library can be found [here](https://yomguithereal.github.io/mnemonist).
**Classics**
* [Heap](https://yomguithereal.github.io/mnemonist/heap)
* [Linked List](https://yomguithereal.github.io/mnemonist/linked-list)
* [LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache), [LRUMap](https://yomguithereal.github.io/mnemonist/lru-map)
* [MultiMap](https://yomguithereal.github.io/mnemonist/multi-map)
* [MultiSet](https://yomguithereal.github.io/mnemonist/multi-set)
* [Queue](https://yomguithereal.github.io/mnemonist/queue)
* [Set (helpers)](https://yomguithereal.github.io/mnemonist/set)
* [Stack](https://yomguithereal.github.io/mnemonist/stack)
* [Trie](https://yomguithereal.github.io/mnemonist/trie)
* [TrieMap](https://yomguithereal.github.io/mnemonist/trie-map)
**Low-level & structures for very specific use cases**
* [Circular Buffer](https://yomguithereal.github.io/mnemonist/circular-buffer)
* [Fixed Deque](https://yomguithereal.github.io/mnemonist/fixed-deque)
* [Fibonacci Heap](https://yomguithereal.github.io/mnemonist/fibonacci-heap)
* [Fixed Reverse Heap](https://yomguithereal.github.io/mnemonist/fixed-reverse-heap)
* [Fixed Stack](https://yomguithereal.github.io/mnemonist/fixed-stack)
* [Hashed Array Tree](https://yomguithereal.github.io/mnemonist/hashed-array-tree)
* [Static DisjointSet](https://yomguithereal.github.io/mnemonist/static-disjoint-set)
* [SparseQueueSet](https://yomguithereal.github.io/mnemonist/sparse-queue-set)
* [SparseMap](https://yomguithereal.github.io/mnemonist/sparse-map)
* [SparseSet](https://yomguithereal.github.io/mnemonist/sparse-set)
* [Suffix Array](https://yomguithereal.github.io/mnemonist/suffix-array)
* [Generalized Suffix Array](https://yomguithereal.github.io/mnemonist/generalized-suffix-array)
* [Vector](https://yomguithereal.github.io/mnemonist/vector)
**Information retrieval & Natural language processing**
* [Fuzzy Map](https://yomguithereal.github.io/mnemonist/fuzzy-map)
* [Fuzzy MultiMap](https://yomguithereal.github.io/mnemonist/fuzzy-multi-map)
* [Inverted Index](https://yomguithereal.github.io/mnemonist/inverted-index)
* [Passjoin Index](https://yomguithereal.github.io/mnemonist/passjoin-index)
* [SymSpell](https://yomguithereal.github.io/mnemonist/symspell)
**Space & time indexation**
* [Static IntervalTree](https://yomguithereal.github.io/mnemonist/static-interval-tree)
* [KD-Tree](https://yomguithereal.github.io/mnemonist/kd-tree)
**Metric space indexation**
* [Burkhard-Keller Tree](https://yomguithereal.github.io/mnemonist/bk-tree)
* [Vantage Point Tree](https://yomguithereal.github.io/mnemonist/vp-tree)
**Probabilistic & succinct data structures**
* [BitSet](https://yomguithereal.github.io/mnemonist/bit-set)
* [BitVector](https://yomguithereal.github.io/mnemonist/bit-vector)
* [Bloom Filter](https://yomguithereal.github.io/mnemonist/bloom-filter)
**Utility classes**
* [BiMap](https://yomguithereal.github.io/mnemonist/bi-map)
* [DefaultMap](https://yomguithereal.github.io/mnemonist/default-map)
* [DefaultWeakMap](https://yomguithereal.github.io/mnemonist/default-weak-map)
---
Note that this list does not include a `Graph` data structure, whose implementation is usually far too complex for the scope of this library.
However, we advise the reader to take a look at the [`graphology`](https://graphology.github.io/) library instead.
Don't find the data structure you need? Maybe we can work it out [together](https://github.com/Yomguithereal/mnemonist/issues).
## Contribution
Contributions are obviously welcome. Be sure to lint the code & add relevant unit tests.
```
# Installing
git clone git@github.com:Yomguithereal/mnemonist.git
cd mnemonist
npm install
# Linting
npm run lint
# Running the unit tests
npm test
```
## License
[MIT](LICENSE.txt)

View File

@@ -1,52 +0,0 @@
/**
* Mnemonist BiMap Typings
* ========================
*/
export class InverseMap<K, V> implements Iterable<[K, V]> {
// Members
size: number;
inverse: BiMap<V, K>;
// Constructor
constructor(original: BiMap<K, V>);
// Methods
clear(): void;
set(key: K, value: V): this;
delete(key: K): boolean;
has(key: K): boolean;
get(key: K): V | undefined;
forEach(callback: (value: V, key: K, map: this) => void, scope?: any): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
inspect(): any;
}
export default class BiMap<K, V> implements Iterable<[K, V]> {
// Members
size: number;
inverse: InverseMap<V, K>;
// Constructor
constructor();
// Methods
clear(): void;
set(key: K, value: V): this;
delete(key: K): boolean;
has(key: K): boolean;
get(key: K): V | undefined;
forEach(callback: (value: V, key: K, map: this) => void, scope?: any): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
inspect(): any;
// Statics
static from<I, J>(iterable: Iterable<[I, J]> | {[key: string]: J}): BiMap<I, J>;
}

View File

@@ -1,195 +0,0 @@
/**
* Mnemonist BiMap
* ================
*
* JavaScript implementation of a BiMap.
*/
var forEach = require('obliterator/foreach');
/**
* Inverse Map.
*
* @constructor
*/
function InverseMap(original) {
this.size = 0;
this.items = new Map();
this.inverse = original;
}
/**
* BiMap.
*
* @constructor
*/
function BiMap() {
this.size = 0;
this.items = new Map();
this.inverse = new InverseMap(this);
}
/**
* Method used to clear the map.
*
* @return {undefined}
*/
function clear() {
this.size = 0;
this.items.clear();
this.inverse.items.clear();
}
BiMap.prototype.clear = clear;
InverseMap.prototype.clear = clear;
/**
* Method used to set a relation.
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {BiMap|InverseMap}
*/
function set(key, value) {
// First we need to attempt to see if the relation is not flawed
if (this.items.has(key)) {
var currentValue = this.items.get(key);
// The relation already exists, we do nothing
if (currentValue === value)
return this;
else
this.inverse.items.delete(currentValue);
}
if (this.inverse.items.has(value)) {
var currentKey = this.inverse.items.get(value);
if (currentKey === key)
return this;
else
this.items.delete(currentKey);
}
// Here we actually add the relation
this.items.set(key, value);
this.inverse.items.set(value, key);
// Size
this.size = this.items.size;
this.inverse.size = this.inverse.items.size;
return this;
}
BiMap.prototype.set = set;
InverseMap.prototype.set = set;
/**
* Method used to delete a relation.
*
* @param {any} key - Key.
* @return {boolean}
*/
function del(key) {
if (this.items.has(key)) {
var currentValue = this.items.get(key);
this.items.delete(key);
this.inverse.items.delete(currentValue);
// Size
this.size = this.items.size;
this.inverse.size = this.inverse.items.size;
return true;
}
return false;
}
BiMap.prototype.delete = del;
InverseMap.prototype.delete = del;
/**
* Mapping some Map prototype function unto our two classes.
*/
var METHODS = ['has', 'get', 'forEach', 'keys', 'values', 'entries'];
METHODS.forEach(function(name) {
BiMap.prototype[name] = InverseMap.prototype[name] = function() {
return Map.prototype[name].apply(this.items, arguments);
};
});
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined') {
BiMap.prototype[Symbol.iterator] = BiMap.prototype.entries;
InverseMap.prototype[Symbol.iterator] = InverseMap.prototype.entries;
}
/**
* Convenience known methods.
*/
BiMap.prototype.inspect = function() {
var dummy = {
left: this.items,
right: this.inverse.items
};
// Trick so that node displays the name of the constructor
Object.defineProperty(dummy, 'constructor', {
value: BiMap,
enumerable: false
});
return dummy;
};
if (typeof Symbol !== 'undefined')
BiMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = BiMap.prototype.inspect;
InverseMap.prototype.inspect = function() {
var dummy = {
left: this.inverse.items,
right: this.items
};
// Trick so that node displays the name of the constructor
Object.defineProperty(dummy, 'constructor', {
value: InverseMap,
enumerable: false
});
return dummy;
};
if (typeof Symbol !== 'undefined')
InverseMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = InverseMap.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a bimap.
*
* @param {Iterable} iterable - Target iterable.
* @return {BiMap}
*/
BiMap.from = function(iterable) {
var bimap = new BiMap();
forEach(iterable, function(value, key) {
bimap.set(key, value);
});
return bimap;
};
/**
* Exporting.
*/
module.exports = BiMap;

View File

@@ -1,29 +0,0 @@
/**
* Mnemonist BitSet Typings
* =========================
*/
export default class BitSet implements Iterable<number> {
// Members
length: number;
size: number;
// Constructor
constructor(length: number);
// Methods
clear(): void;
set(index: number, value?: boolean | number): void;
reset(index: number, value: boolean | number): void;
flip(index: number, value: boolean | number): void;
get(index: number): number;
test(index: number): boolean;
rank(r: number): number;
select(r: number): number;
forEach(callback: (index: number, value: number, set: this) => void, scope?: any): void;
values(): IterableIterator<number>;
entries(): IterableIterator<[number, number]>;
[Symbol.iterator](): IterableIterator<number>;
inspect(): any;
toJSON(): Array<number>;
}

View File

@@ -1,379 +0,0 @@
/**
* Mnemonist BitSet
* =================
*
* JavaScript implementation of a fixed-size BitSet based upon a Uint32Array.
*
* Notes:
* - (i >> 5) is the same as ((i / 32) | 0)
* - (i & 0x0000001f) is the same as (i % 32)
* - I could use a Float64Array to store more in less blocks but I would lose
* the benefits of byte comparison to keep track of size without popcounts.
*/
var Iterator = require('obliterator/iterator'),
bitwise = require('./utils/bitwise.js');
/**
* BitSet.
*
* @constructor
*/
function BitSet(length) {
// Properties
this.length = length;
this.clear();
// Methods
// Statics
}
/**
* Method used to clear the bit set.
*
* @return {undefined}
*/
BitSet.prototype.clear = function() {
// Properties
this.size = 0;
this.array = new Uint32Array(Math.ceil(this.length / 32));
};
/**
* Method used to set the given bit's value.
*
* @param {number} index - Target bit index.
* @param {number} value - Value to set.
* @return {BitSet}
*/
BitSet.prototype.set = function(index, value) {
var byteIndex = index >> 5,
pos = index & 0x0000001f,
oldBytes = this.array[byteIndex],
newBytes;
if (value === 0 || value === false)
newBytes = this.array[byteIndex] &= ~(1 << pos);
else
newBytes = this.array[byteIndex] |= (1 << pos);
// The operands of all bitwise operators are converted to *signed* 32-bit integers.
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Signed_32-bit_integers
// Shifting by 31 changes the sign (i.e. 1 << 31 = -2147483648).
// Therefore, get unsigned representation by applying '>>> 0'.
newBytes = newBytes >>> 0;
// Updating size
if (newBytes > oldBytes)
this.size++;
else if (newBytes < oldBytes)
this.size--;
return this;
};
/**
* Method used to reset the given bit's value.
*
* @param {number} index - Target bit index.
* @return {BitSet}
*/
BitSet.prototype.reset = function(index) {
var byteIndex = index >> 5,
pos = index & 0x0000001f,
oldBytes = this.array[byteIndex],
newBytes;
newBytes = this.array[byteIndex] &= ~(1 << pos);
// Updating size
if (newBytes < oldBytes)
this.size--;
return this;
};
/**
* Method used to flip the value of the given bit.
*
* @param {number} index - Target bit index.
* @return {BitSet}
*/
BitSet.prototype.flip = function(index) {
var byteIndex = index >> 5,
pos = index & 0x0000001f,
oldBytes = this.array[byteIndex];
var newBytes = this.array[byteIndex] ^= (1 << pos);
// Get unsigned representation.
newBytes = newBytes >>> 0;
// Updating size
if (newBytes > oldBytes)
this.size++;
else if (newBytes < oldBytes)
this.size--;
return this;
};
/**
* Method used to get the given bit's value.
*
* @param {number} index - Target bit index.
* @return {number}
*/
BitSet.prototype.get = function(index) {
var byteIndex = index >> 5,
pos = index & 0x0000001f;
return (this.array[byteIndex] >> pos) & 1;
};
/**
* Method used to test the given bit's value.
*
* @param {number} index - Target bit index.
* @return {BitSet}
*/
BitSet.prototype.test = function(index) {
return Boolean(this.get(index));
};
/**
* Method used to return the number of 1 from the beginning of the set up to
* the ith index.
*
* @param {number} i - Ith index (cannot be > length).
* @return {number}
*/
BitSet.prototype.rank = function(i) {
if (this.size === 0)
return 0;
var byteIndex = i >> 5,
pos = i & 0x0000001f,
r = 0;
// Accessing the bytes before the last one
for (var j = 0; j < byteIndex; j++)
r += bitwise.table8Popcount(this.array[j]);
// Handling masked last byte
var maskedByte = this.array[byteIndex] & ((1 << pos) - 1);
r += bitwise.table8Popcount(maskedByte);
return r;
};
/**
* Method used to return the position of the rth 1 in the set or -1 if the
* set is empty.
*
* Note: usually select is implemented using binary search over rank but I
* tend to think the following linear implementation is faster since here
* rank is O(n) anyway.
*
* @param {number} r - Rth 1 to select (should be < length).
* @return {number}
*/
BitSet.prototype.select = function(r) {
if (this.size === 0)
return -1;
// TODO: throw?
if (r >= this.length)
return -1;
var byte,
b = 32,
p = 0,
c = 0;
for (var i = 0, l = this.array.length; i < l; i++) {
byte = this.array[i];
// The byte is empty, let's continue
if (byte === 0)
continue;
// TODO: This branching might not be useful here
if (i === l - 1)
b = this.length % 32 || 32;
// TODO: popcount should speed things up here
for (var j = 0; j < b; j++, p++) {
c += (byte >> j) & 1;
if (c === r)
return p;
}
}
};
/**
* Method used to iterate over the bit set's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
BitSet.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
var length = this.length,
byte,
bit,
b = 32;
for (var i = 0, l = this.array.length; i < l; i++) {
byte = this.array[i];
if (i === l - 1)
b = length % 32 || 32;
for (var j = 0; j < b; j++) {
bit = (byte >> j) & 1;
callback.call(scope, bit, i * 32 + j);
}
}
};
/**
* Method used to create an iterator over a set's values.
*
* @return {Iterator}
*/
BitSet.prototype.values = function() {
var length = this.length,
inner = false,
byte,
bit,
array = this.array,
l = array.length,
i = 0,
j = -1,
b = 32;
return new Iterator(function next() {
if (!inner) {
if (i >= l)
return {
done: true
};
if (i === l - 1)
b = length % 32 || 32;
byte = array[i++];
inner = true;
j = -1;
}
j++;
if (j >= b) {
inner = false;
return next();
}
bit = (byte >> j) & 1;
return {
value: bit
};
});
};
/**
* Method used to create an iterator over a set's entries.
*
* @return {Iterator}
*/
BitSet.prototype.entries = function() {
var length = this.length,
inner = false,
byte,
bit,
array = this.array,
index,
l = array.length,
i = 0,
j = -1,
b = 32;
return new Iterator(function next() {
if (!inner) {
if (i >= l)
return {
done: true
};
if (i === l - 1)
b = length % 32 || 32;
byte = array[i++];
inner = true;
j = -1;
}
j++;
index = (~-i) * 32 + j;
if (j >= b) {
inner = false;
return next();
}
bit = (byte >> j) & 1;
return {
value: [index, bit]
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
BitSet.prototype[Symbol.iterator] = BitSet.prototype.values;
/**
* Convenience known methods.
*/
BitSet.prototype.inspect = function() {
var proxy = new Uint8Array(this.length);
this.forEach(function(bit, i) {
proxy[i] = bit;
});
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: BitSet,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
BitSet.prototype[Symbol.for('nodejs.util.inspect.custom')] = BitSet.prototype.inspect;
BitSet.prototype.toJSON = function() {
return Array.from(this.array);
};
/**
* Exporting.
*/
module.exports = BitSet;

View File

@@ -1,42 +0,0 @@
/**
* Mnemonist BitVector Typings
* ============================
*/
type BitVectorOptions = {
initialLength?: number;
initialCapacity?: number;
policy?: (capacity: number) => number;
}
export default class BitVector implements Iterable<number> {
// Members
capacity: number;
length: number;
size: number;
// Constructor
constructor(length: number);
constructor(options: BitVectorOptions);
// Methods
clear(): void;
set(index: number, value?: boolean | number): this;
reset(index: number, value: boolean | number): void;
flip(index: number, value: boolean | number): void;
reallocate(capacity: number): this;
grow(capacity?: number): this;
resize(length: number): this;
push(value: boolean | number): number;
pop(): number | undefined;
get(index: number): number;
test(index: number): boolean;
rank(r: number): number;
select(r: number): number;
forEach(callback: (index: number, value: number, set: this) => void, scope?: any): void;
values(): IterableIterator<number>;
entries(): IterableIterator<[number, number]>;
[Symbol.iterator](): IterableIterator<number>;
inspect(): any;
toJSON(): Array<number>;
}

View File

@@ -1,550 +0,0 @@
/**
* Mnemonist BitVector
* ====================
*
* JavaScript implementation of a dynamic BitSet based upon a Uint32Array.
*
* Notes:
* - (i >> 5) is the same as ((i / 32) | 0)
* - (i & 0x0000001f) is the same as (i % 32)
* - I could use a Float64Array to store more in less blocks but I would lose
* the benefits of byte comparison to keep track of size without popcounts.
*/
var Iterator = require('obliterator/iterator'),
bitwise = require('./utils/bitwise.js');
/**
* Constants.
*/
var DEFAULT_GROWING_POLICY = function(capacity) {
return Math.max(1, Math.ceil(capacity * 1.5));
};
/**
* Helpers.
*/
function createByteArray(capacity) {
return new Uint32Array(Math.ceil(capacity / 32));
}
/**
* BitVector.
*
* @constructor
*/
function BitVector(initialLengthOrOptions) {
var initialLength = initialLengthOrOptions || 0,
policy = DEFAULT_GROWING_POLICY;
if (typeof initialLengthOrOptions === 'object') {
initialLength = (
initialLengthOrOptions.initialLength ||
initialLengthOrOptions.initialCapacity ||
0
);
policy = initialLengthOrOptions.policy || policy;
}
this.size = 0;
this.length = initialLength;
this.capacity = Math.ceil(this.length / 32) * 32;
this.policy = policy;
this.array = createByteArray(this.capacity);
}
/**
* Method used to set the given bit's value.
*
* @param {number} index - Target bit index.
* @param {number|boolean} value - Value to set.
* @return {BitVector}
*/
BitVector.prototype.set = function(index, value) {
// Out of bounds?
if (this.length < index)
throw new Error('BitVector.set: index out of bounds.');
var byteIndex = index >> 5,
pos = index & 0x0000001f,
oldBytes = this.array[byteIndex],
newBytes;
if (value === 0 || value === false)
newBytes = this.array[byteIndex] &= ~(1 << pos);
else
newBytes = this.array[byteIndex] |= (1 << pos);
// Get unsigned representation.
newBytes = newBytes >>> 0;
// Updating size
if (newBytes > oldBytes)
this.size++;
else if (newBytes < oldBytes)
this.size--;
return this;
};
/**
* Method used to reset the given bit's value.
*
* @param {number} index - Target bit index.
* @return {BitVector}
*/
BitVector.prototype.reset = function(index) {
var byteIndex = index >> 5,
pos = index & 0x0000001f,
oldBytes = this.array[byteIndex],
newBytes;
newBytes = this.array[byteIndex] &= ~(1 << pos);
// Updating size
if (newBytes < oldBytes)
this.size--;
return this;
};
/**
* Method used to flip the value of the given bit.
*
* @param {number} index - Target bit index.
* @return {BitVector}
*/
BitVector.prototype.flip = function(index) {
var byteIndex = index >> 5,
pos = index & 0x0000001f,
oldBytes = this.array[byteIndex];
var newBytes = this.array[byteIndex] ^= (1 << pos);
// Get unsigned representation.
newBytes = newBytes >>> 0;
// Updating size
if (newBytes > oldBytes)
this.size++;
else if (newBytes < oldBytes)
this.size--;
return this;
};
/**
* Method used to apply the growing policy.
*
* @param {number} [override] - Override capacity.
* @return {number}
*/
BitVector.prototype.applyPolicy = function(override) {
var newCapacity = this.policy(override || this.capacity);
if (typeof newCapacity !== 'number' || newCapacity < 0)
throw new Error('mnemonist/bit-vector.applyPolicy: policy returned an invalid value (expecting a positive integer).');
if (newCapacity <= this.capacity)
throw new Error('mnemonist/bit-vector.applyPolicy: policy returned a less or equal capacity to allocate.');
// TODO: we should probably check that the returned number is an integer
// Ceil to nearest 32
return Math.ceil(newCapacity / 32) * 32;
};
/**
* Method used to reallocate the underlying array.
*
* @param {number} capacity - Target capacity.
* @return {BitVector}
*/
BitVector.prototype.reallocate = function(capacity) {
var virtualCapacity = capacity;
capacity = Math.ceil(capacity / 32) * 32;
if (virtualCapacity < this.length)
this.length = virtualCapacity;
if (capacity === this.capacity)
return this;
var oldArray = this.array;
var storageLength = capacity / 32;
if (storageLength === this.array.length)
return this;
if (storageLength > this.array.length) {
this.array = new Uint32Array(storageLength);
this.array.set(oldArray, 0);
}
else {
this.array = oldArray.slice(0, storageLength);
}
this.capacity = capacity;
return this;
};
/**
* Method used to grow the array.
*
* @param {number} [capacity] - Optional capacity to match.
* @return {BitVector}
*/
BitVector.prototype.grow = function(capacity) {
var newCapacity;
if (typeof capacity === 'number') {
if (this.capacity >= capacity)
return this;
// We need to match the given capacity
newCapacity = this.capacity;
while (newCapacity < capacity)
newCapacity = this.applyPolicy(newCapacity);
this.reallocate(newCapacity);
return this;
}
// We need to run the policy once
newCapacity = this.applyPolicy();
this.reallocate(newCapacity);
return this;
};
/**
* Method used to resize the array. Won't deallocate.
*
* @param {number} length - Target length.
* @return {BitVector}
*/
BitVector.prototype.resize = function(length) {
if (length === this.length)
return this;
if (length < this.length) {
this.length = length;
return this;
}
this.length = length;
this.reallocate(length);
return this;
};
/**
* Method used to push a value in the set.
*
* @param {number|boolean} value
* @return {BitVector}
*/
BitVector.prototype.push = function(value) {
if (this.capacity === this.length)
this.grow();
if (value === 0 || value === false)
return ++this.length;
this.size++;
var index = this.length++,
byteIndex = index >> 5,
pos = index & 0x0000001f;
this.array[byteIndex] |= (1 << pos);
return this.length;
};
/**
* Method used to pop the last value of the set.
*
* @return {number} - The popped value.
*/
BitVector.prototype.pop = function() {
if (this.length === 0)
return;
var index = --this.length;
var byteIndex = index >> 5,
pos = index & 0x0000001f;
return (this.array[byteIndex] >> pos) & 1;
};
/**
* Method used to get the given bit's value.
*
* @param {number} index - Target bit index.
* @return {number}
*/
BitVector.prototype.get = function(index) {
if (this.length < index)
return undefined;
var byteIndex = index >> 5,
pos = index & 0x0000001f;
return (this.array[byteIndex] >> pos) & 1;
};
/**
* Method used to test the given bit's value.
*
* @param {number} index - Target bit index.
* @return {BitVector}
*/
BitVector.prototype.test = function(index) {
if (this.length < index)
return false;
return Boolean(this.get(index));
};
/**
* Method used to return the number of 1 from the beginning of the set up to
* the ith index.
*
* @param {number} i - Ith index (cannot be > length).
* @return {number}
*/
BitVector.prototype.rank = function(i) {
if (this.size === 0)
return 0;
var byteIndex = i >> 5,
pos = i & 0x0000001f,
r = 0;
// Accessing the bytes before the last one
for (var j = 0; j < byteIndex; j++)
r += bitwise.table8Popcount(this.array[j]);
// Handling masked last byte
var maskedByte = this.array[byteIndex] & ((1 << pos) - 1);
r += bitwise.table8Popcount(maskedByte);
return r;
};
/**
* Method used to return the position of the rth 1 in the set or -1 if the
* set is empty.
*
* Note: usually select is implemented using binary search over rank but I
* tend to think the following linear implementation is faster since here
* rank is O(n) anyway.
*
* @param {number} r - Rth 1 to select (should be < length).
* @return {number}
*/
BitVector.prototype.select = function(r) {
if (this.size === 0)
return -1;
// TODO: throw?
if (r >= this.length)
return -1;
var byte,
b = 32,
p = 0,
c = 0;
for (var i = 0, l = this.array.length; i < l; i++) {
byte = this.array[i];
// The byte is empty, let's continue
if (byte === 0)
continue;
// TODO: This branching might not be useful here
if (i === l - 1)
b = this.length % 32 || 32;
// TODO: popcount should speed things up here
for (var j = 0; j < b; j++, p++) {
c += (byte >> j) & 1;
if (c === r)
return p;
}
}
};
/**
* Method used to iterate over the bit set's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
BitVector.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
var length = this.length,
byte,
bit,
b = 32;
for (var i = 0, l = this.array.length; i < l; i++) {
byte = this.array[i];
if (i === l - 1)
b = length % 32 || 32;
for (var j = 0; j < b; j++) {
bit = (byte >> j) & 1;
callback.call(scope, bit, i * 32 + j);
}
}
};
/**
* Method used to create an iterator over a set's values.
*
* @return {Iterator}
*/
BitVector.prototype.values = function() {
var length = this.length,
inner = false,
byte,
bit,
array = this.array,
l = array.length,
i = 0,
j = -1,
b = 32;
return new Iterator(function next() {
if (!inner) {
if (i >= l)
return {
done: true
};
if (i === l - 1)
b = length % 32 || 32;
byte = array[i++];
inner = true;
j = -1;
}
j++;
if (j >= b) {
inner = false;
return next();
}
bit = (byte >> j) & 1;
return {
value: bit
};
});
};
/**
* Method used to create an iterator over a set's entries.
*
* @return {Iterator}
*/
BitVector.prototype.entries = function() {
var length = this.length,
inner = false,
byte,
bit,
array = this.array,
index,
l = array.length,
i = 0,
j = -1,
b = 32;
return new Iterator(function next() {
if (!inner) {
if (i >= l)
return {
done: true
};
if (i === l - 1)
b = length % 32 || 32;
byte = array[i++];
inner = true;
j = -1;
}
j++;
index = (~-i) * 32 + j;
if (j >= b) {
inner = false;
return next();
}
bit = (byte >> j) & 1;
return {
value: [index, bit]
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
BitVector.prototype[Symbol.iterator] = BitVector.prototype.values;
/**
* Convenience known methods.
*/
BitVector.prototype.inspect = function() {
var proxy = new Uint8Array(this.length);
this.forEach(function(bit, i) {
proxy[i] = bit;
});
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: BitVector,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
BitVector.prototype[Symbol.for('nodejs.util.inspect.custom')] = BitVector.prototype.inspect;
BitVector.prototype.toJSON = function() {
return Array.from(this.array.slice(0, (this.length >> 5) + 1));
};
/**
* Exporting.
*/
module.exports = BitVector;

View File

@@ -1,24 +0,0 @@
/**
* Mnemonist BKTree Typings
* =========================
*/
type DistanceFunction<T> = (a: T, b: T) => number;
export default class BKTree<T> {
// Members
distance: DistanceFunction<T>;
size: number;
// Constructor
constructor(distance: DistanceFunction<T>);
// Methods
add(item: T): this;
search(n: number, query: T): Array<{item: T, distance: number}>;
toJSON(): object;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}, distance: DistanceFunction<I>): BKTree<I>;
}

View File

@@ -1,180 +0,0 @@
/* eslint no-constant-condition: 0 */
/**
* Mnemonist BK Tree
* ==================
*
* Implementation of a Burkhard-Keller tree, allowing fast lookups of words
* that lie within a specified distance of the query word.
*
* [Reference]:
* https://en.wikipedia.org/wiki/BK-tree
*
* [Article]:
* W. Burkhard and R. Keller. Some approaches to best-match file searching,
* CACM, 1973
*/
var forEach = require('obliterator/foreach');
/**
* BK Tree.
*
* @constructor
* @param {function} distance - Distance function to use.
*/
function BKTree(distance) {
if (typeof distance !== 'function')
throw new Error('mnemonist/BKTree.constructor: given `distance` should be a function.');
this.distance = distance;
this.clear();
}
/**
* Method used to add an item to the tree.
*
* @param {any} item - Item to add.
* @return {BKTree}
*/
BKTree.prototype.add = function(item) {
// Initializing the tree with the first given word
if (!this.root) {
this.root = {
item: item,
children: {}
};
this.size++;
return this;
}
var node = this.root,
d;
while (true) {
d = this.distance(item, node.item);
if (!node.children[d])
break;
node = node.children[d];
}
node.children[d] = {
item: item,
children: {}
};
this.size++;
return this;
};
/**
* Method used to query the tree.
*
* @param {number} n - Maximum distance between query & item.
* @param {any} query - Query
* @return {BKTree}
*/
BKTree.prototype.search = function(n, query) {
if (!this.root)
return [];
var found = [],
stack = [this.root],
node,
child,
d,
i,
l;
while (stack.length) {
node = stack.pop();
d = this.distance(query, node.item);
if (d <= n)
found.push({item: node.item, distance: d});
for (i = d - n, l = d + n + 1; i < l; i++) {
child = node.children[i];
if (child)
stack.push(child);
}
}
return found;
};
/**
* Method used to clear the tree.
*
* @return {undefined}
*/
BKTree.prototype.clear = function() {
// Properties
this.size = 0;
this.root = null;
};
/**
* Convenience known methods.
*/
BKTree.prototype.toJSON = function() {
return this.root;
};
BKTree.prototype.inspect = function() {
var array = [],
stack = [this.root],
node,
d;
while (stack.length) {
node = stack.pop();
if (!node)
continue;
array.push(node.item);
for (d in node.children)
stack.push(node.children[d]);
}
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: BKTree,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
BKTree.prototype[Symbol.for('nodejs.util.inspect.custom')] = BKTree.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a tree.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} distance - Distance function.
* @return {Heap}
*/
BKTree.from = function(iterable, distance) {
var tree = new BKTree(distance);
forEach(iterable, function(value) {
tree.add(value);
});
return tree;
};
/**
* Exporting.
*/
module.exports = BKTree;

View File

@@ -1,29 +0,0 @@
/**
* Mnemonist BloomFilter Typings
* ==============================
*/
type BloomFilterOptions = {
capacity: number;
errorRate?: number;
}
export default class BloomFilter {
// Members
capacity: number;
errorRate: number;
hashFunctions: number;
// Constructor
constructor(capacity: number);
constructor(options: BloomFilterOptions);
// Methods
clear(): void;
add(string: string): this;
test(string: string): boolean;
toJSON(): Uint8Array;
// Statics
static from(iterable: Iterable<string>, options?: number | BloomFilterOptions): BloomFilter;
}

View File

@@ -1,186 +0,0 @@
/**
* Mnemonist Bloom Filter
* =======================
*
* Bloom Filter implementation relying on MurmurHash3.
*/
var murmurhash3 = require('./utils/murmurhash3.js'),
forEach = require('obliterator/foreach');
/**
* Constants.
*/
var LN2_SQUARED = Math.LN2 * Math.LN2;
/**
* Defaults.
*/
var DEFAULTS = {
errorRate: 0.005
};
/**
* Function used to convert a string into a Uint16 byte array.
*
* @param {string} string - Target string.
* @return {Uint16Array}
*/
function stringToByteArray(string) {
var array = new Uint16Array(string.length),
i,
l;
for (i = 0, l = string.length; i < l; i++)
array[i] = string.charCodeAt(i);
return array;
}
/**
* Function used to hash the given byte array.
*
* @param {number} length - Length of the filter's byte array.
* @param {number} seed - Seed to use for the hash function.
* @param {Uint16Array} - Byte array representing the string.
* @return {number} - The hash.
*
* @note length * 8 should probably already be computed as well as seeds.
*/
function hashArray(length, seed, array) {
var hash = murmurhash3((seed * 0xFBA4C795) & 0xFFFFFFFF, array);
return hash % (length * 8);
}
/**
* Bloom Filter.
*
* @constructor
* @param {number|object} capacityOrOptions - Capacity or options.
*/
function BloomFilter(capacityOrOptions) {
var options = {};
if (!capacityOrOptions)
throw new Error('mnemonist/BloomFilter.constructor: a BloomFilter must be created with a capacity.');
if (typeof capacityOrOptions === 'object')
options = capacityOrOptions;
else
options.capacity = capacityOrOptions;
// Handling capacity
if (typeof options.capacity !== 'number' || options.capacity <= 0)
throw new Error('mnemonist/BloomFilter.constructor: `capacity` option should be a positive integer.');
this.capacity = options.capacity;
// Handling error rate
this.errorRate = options.errorRate || DEFAULTS.errorRate;
if (typeof this.errorRate !== 'number' || options.errorRate <= 0)
throw new Error('mnemonist/BloomFilter.constructor: `errorRate` option should be a positive float.');
this.clear();
}
/**
* Method used to clear the filter.
*
* @return {undefined}
*/
BloomFilter.prototype.clear = function() {
// Optimizing number of bits & number of hash functions
var bits = -1 / LN2_SQUARED * this.capacity * Math.log(this.errorRate),
length = (bits / 8) | 0;
this.hashFunctions = (length * 8 / this.capacity * Math.LN2) | 0;
// Creating the data array
this.data = new Uint8Array(length);
return;
};
/**
* Method used to add an string to the filter.
*
* @param {string} string - Item to add.
* @return {BloomFilter}
*
* @note Should probably create a hash function working directly on a string.
*/
BloomFilter.prototype.add = function(string) {
// Converting the string to a byte array
var array = stringToByteArray(string);
// Applying the n hash functions
for (var i = 0, l = this.hashFunctions; i < l; i++) {
var index = hashArray(this.data.length, i, array),
position = (1 << (7 & index));
this.data[index >> 3] |= position;
}
return this;
};
/**
* Method used to test the given string.
*
* @param {string} string - Item to test.
* @return {boolean}
*/
BloomFilter.prototype.test = function(string) {
// Converting the string to a byte array
var array = stringToByteArray(string);
// Applying the n hash functions
for (var i = 0, l = this.hashFunctions; i < l; i++) {
var index = hashArray(this.data.length, i, array);
if (!(this.data[index >> 3] & (1 << (7 & index))))
return false;
}
return true;
};
/**
* Convenience known methods.
*/
BloomFilter.prototype.toJSON = function() {
return this.data;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a filter.
*
* @param {Iterable} iterable - Target iterable.
* @return {BloomFilter}
*/
BloomFilter.from = function(iterable, options) {
if (!options) {
options = iterable.length || iterable.size;
if (typeof options !== 'number')
throw new Error('BloomFilter.from: could not infer the filter\'s capacity. Try passing it as second argument.');
}
var filter = new BloomFilter(options);
forEach(iterable, function(value) {
filter.add(value);
});
return filter;
};
/**
* Exporting.
*/
module.exports = BloomFilter;

View File

@@ -1,34 +0,0 @@
/**
* Mnemonist CircularBuffer Typings
* =================================
*/
import {IArrayLikeConstructor, TypedArray} from './utils/types';
export default class CircularBuffer<T> implements Iterable<T> {
// Members
capacity: number;
size: number;
// Constructor
constructor(ArrayClass: IArrayLikeConstructor, capacity: number);
// Methods
clear(): void;
push(item: T): number;
unshift(item: T): number;
pop(): T | undefined;
shift(): T | undefined;
peekFirst(): T | undefined;
peekLast(): T | undefined;
get(index: number): T | undefined;
forEach(callback: (item: T, index: number, buffer: this) => void, scope?: any): void;
toArray(): Array<T> | TypedArray;
values(): IterableIterator<T>;
entries(): IterableIterator<[number, T]>;
[Symbol.iterator](): IterableIterator<T>;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}, ArrayClass: IArrayLikeConstructor, capacity?: number): CircularBuffer<I>;
}

View File

@@ -1,140 +0,0 @@
/**
* Mnemonist CircularBuffer
* =========================
*
* Circular buffer implementation fit to use as a finite deque.
*/
var iterables = require('./utils/iterables.js'),
FixedDeque = require('./fixed-deque');
/**
* CircularBuffer.
*
* @constructor
*/
function CircularBuffer(ArrayClass, capacity) {
if (arguments.length < 2)
throw new Error('mnemonist/circular-buffer: expecting an Array class and a capacity.');
if (typeof capacity !== 'number' || capacity <= 0)
throw new Error('mnemonist/circular-buffer: `capacity` should be a positive number.');
this.ArrayClass = ArrayClass;
this.capacity = capacity;
this.items = new ArrayClass(this.capacity);
this.clear();
}
/**
* Pasting most of the prototype from FixedDeque.
*/
function paste(name) {
CircularBuffer.prototype[name] = FixedDeque.prototype[name];
}
Object.keys(FixedDeque.prototype).forEach(paste);
if (typeof Symbol !== 'undefined')
Object.getOwnPropertySymbols(FixedDeque.prototype).forEach(paste);
/**
* Method used to append a value to the buffer.
*
* @param {any} item - Item to append.
* @return {number} - Returns the new size of the buffer.
*/
CircularBuffer.prototype.push = function(item) {
var index = this.start + this.size;
if (index >= this.capacity)
index -= this.capacity;
this.items[index] = item;
// Overwriting?
if (this.size === this.capacity) {
index++;
// Wrapping around?
if (index >= this.capacity) {
this.start = 0;
}
else {
this.start = index;
}
return this.size;
}
return ++this.size;
};
/**
* Method used to prepend a value to the buffer.
*
* @param {any} item - Item to prepend.
* @return {number} - Returns the new size of the buffer.
*/
CircularBuffer.prototype.unshift = function(item) {
var index = this.start - 1;
if (this.start === 0)
index = this.capacity - 1;
this.items[index] = item;
// Overwriting
if (this.size === this.capacity) {
this.start = index;
return this.size;
}
this.start = index;
return ++this.size;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a circular buffer.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} ArrayClass - Array class to use.
* @param {number} capacity - Desired capacity.
* @return {FiniteStack}
*/
CircularBuffer.from = function(iterable, ArrayClass, capacity) {
if (arguments.length < 3) {
capacity = iterables.guessLength(iterable);
if (typeof capacity !== 'number')
throw new Error('mnemonist/circular-buffer.from: could not guess iterable length. Please provide desired capacity as last argument.');
}
var buffer = new CircularBuffer(ArrayClass, capacity);
if (iterables.isArrayLike(iterable)) {
var i, l;
for (i = 0, l = iterable.length; i < l; i++)
buffer.items[i] = iterable[i];
buffer.size = l;
return buffer;
}
iterables.forEach(iterable, function(value) {
buffer.push(value);
});
return buffer;
};
/**
* Exporting.
*/
module.exports = CircularBuffer;

View File

@@ -1,515 +0,0 @@
/* eslint no-constant-condition: 0 */
/**
* Mnemonist CritBitTreeMap
* =========================
*
* JavaScript implementation of a crit-bit tree, also called PATRICIA tree.
* This tree is a basically a bitwise radix tree and is supposedly much more
* efficient than a standard Trie.
*
* [References]:
* https://cr.yp.to/critbit.html
* https://www.imperialviolet.org/binary/critbit.pdf
*/
var bitwise = require('./utils/bitwise.js');
/**
* Helpers.
*/
/**
* Helper returning the direction we need to take given a key and an
* encoded critbit.
*
* @param {string} key - Target key.
* @param {number} critbit - Packed address of byte + mask.
* @return {number} - 0, left or 1, right.
*/
function getDirection(key, critbit) {
var byteIndex = critbit >> 8;
if (byteIndex > key.length - 1)
return 0;
var byte = key.charCodeAt(byteIndex),
mask = critbit & 0xff;
return (1 + (byte | mask)) >> 8;
}
/**
* Helper returning the packed address of byte + mask or -1 if strings
* are identical.
*
* @param {string} a - First key.
* @param {string} b - Second key.
* @return {number} - Packed address of byte + mask.
*/
function findCriticalBit(a, b) {
var i = 0,
tmp;
// Swapping so a is the shortest
if (a.length > b.length) {
tmp = b;
b = a;
a = tmp;
}
var l = a.length,
mask;
while (i < l) {
if (a[i] !== b[i]) {
mask = bitwise.criticalBit8Mask(
a.charCodeAt(i),
b.charCodeAt(i)
);
return (i << 8) | mask;
}
i++;
}
// Strings are identical
if (a.length === b.length)
return -1;
// NOTE: x ^ 0 is the same as x
mask = bitwise.criticalBit8Mask(b.charCodeAt(i));
return (i << 8) | mask;
}
/**
* Class representing a crit-bit tree's internal node.
*
* @constructor
* @param {number} critbit - Packed address of byte + mask.
*/
function InternalNode(critbit) {
this.critbit = critbit;
this.left = null;
this.right = null;
}
/**
* Class representing a crit-bit tree's external node.
* Note that it is possible to replace those nodes by flat arrays.
*
* @constructor
* @param {string} key - Node's key.
* @param {any} value - Arbitrary value.
*/
function ExternalNode(key, value) {
this.key = key;
this.value = value;
}
/**
* CritBitTreeMap.
*
* @constructor
*/
function CritBitTreeMap() {
// Properties
this.root = null;
this.size = 0;
this.clear();
}
/**
* Method used to clear the CritBitTreeMap.
*
* @return {undefined}
*/
CritBitTreeMap.prototype.clear = function() {
// Properties
this.root = null;
this.size = 0;
};
/**
* Method used to set the value of the given key in the trie.
*
* @param {string} key - Key to set.
* @param {any} value - Arbitrary value.
* @return {CritBitTreeMap}
*/
CritBitTreeMap.prototype.set = function(key, value) {
// Tree is empty
if (this.size === 0) {
this.root = new ExternalNode(key, value);
this.size++;
return this;
}
// Walk state
var node = this.root,
ancestors = [],
path = [],
ancestor,
parent,
child,
critbit,
internal,
left,
leftPath,
best,
dir,
i,
l;
// Walking the tree
while (true) {
// Traversing an internal node
if (node instanceof InternalNode) {
dir = getDirection(key, node.critbit);
// Going left & creating key if not yet there
if (dir === 0) {
if (!node.left) {
node.left = new ExternalNode(key, value);
return this;
}
ancestors.push(node);
path.push(true);
node = node.left;
}
// Going right & creating key if not yet there
else {
if (!node.right) {
node.right = new ExternalNode(key, value);
return this;
}
ancestors.push(node);
path.push(false);
node = node.right;
}
}
// Reaching an external node
else {
// 1. Creating a new external node
critbit = findCriticalBit(key, node.key);
// Key is identical, we just replace the value
if (critbit === -1) {
node.value = value;
return this;
}
this.size++;
internal = new InternalNode(critbit);
left = getDirection(key, critbit) === 0;
// TODO: maybe setting opposite pointer is not necessary
if (left) {
internal.left = new ExternalNode(key, value);
internal.right = node;
}
else {
internal.left = node;
internal.right = new ExternalNode(key, value);
}
// 2. Bubbling up
best = -1;
l = ancestors.length;
for (i = l - 1; i >= 0; i--) {
ancestor = ancestors[i];
if (ancestor.critbit > critbit)
continue;
best = i;
break;
}
// Do we need to attach to the root?
if (best < 0) {
this.root = internal;
// Need to rewire parent as child?
if (l > 0) {
parent = ancestors[0];
if (left)
internal.right = parent;
else
internal.left = parent;
}
}
// Simple case without rotation
else if (best === l - 1) {
parent = ancestors[best];
leftPath = path[best];
if (leftPath)
parent.left = internal;
else
parent.right = internal;
}
// Full rotation
else {
parent = ancestors[best];
leftPath = path[best];
child = ancestors[best + 1];
if (leftPath)
parent.left = internal;
else
parent.right = internal;
if (left)
internal.right = child;
else
internal.left = child;
}
return this;
}
}
};
/**
* Method used to get the value attached to the given key in the tree or
* undefined if not found.
*
* @param {string} key - Key to get.
* @return {any}
*/
CritBitTreeMap.prototype.get = function(key) {
// Walk state
var node = this.root,
dir;
// Walking the tree
while (true) {
// Dead end
if (node === null)
return;
// Traversing an internal node
if (node instanceof InternalNode) {
dir = getDirection(key, node.critbit);
node = dir ? node.right : node.left;
}
// Reaching an external node
else {
if (node.key !== key)
return;
return node.value;
}
}
};
/**
* Method used to return whether the given key exists in the tree.
*
* @param {string} key - Key to test.
* @return {boolean}
*/
CritBitTreeMap.prototype.has = function(key) {
// Walk state
var node = this.root,
dir;
// Walking the tree
while (true) {
// Dead end
if (node === null)
return false;
// Traversing an internal node
if (node instanceof InternalNode) {
dir = getDirection(key, node.critbit);
node = dir ? node.right : node.left;
}
// Reaching an external node
else {
return node.key === key;
}
}
};
/**
* Method used to delete the given key from the tree and return whether the
* key did exist or not.
*
* @param {string} key - Key to delete.
* @return {boolean}
*/
CritBitTreeMap.prototype.delete = function(key) {
// Walk state
var node = this.root,
dir;
var parent = null,
grandParent = null,
wentLeftForParent = false,
wentLeftForGrandparent = false;
// Walking the tree
while (true) {
// Dead end
if (node === null)
return false;
// Traversing an internal node
if (node instanceof InternalNode) {
dir = getDirection(key, node.critbit);
if (dir === 0) {
grandParent = parent;
wentLeftForGrandparent = wentLeftForParent;
parent = node;
wentLeftForParent = true;
node = node.left;
}
else {
grandParent = parent;
wentLeftForGrandparent = wentLeftForParent;
parent = node;
wentLeftForParent = false;
node = node.right;
}
}
// Reaching an external node
else {
if (key !== node.key)
return false;
this.size--;
// Rewiring
if (parent === null) {
this.root = null;
}
else if (grandParent === null) {
if (wentLeftForParent)
this.root = parent.right;
else
this.root = parent.left;
}
else {
if (wentLeftForGrandparent) {
if (wentLeftForParent) {
grandParent.left = parent.right;
}
else {
grandParent.left = parent.left;
}
}
else {
if (wentLeftForParent) {
grandParent.right = parent.right;
}
else {
grandParent.right = parent.left;
}
}
}
return true;
}
}
};
/**
* Method used to iterate over the tree in key order.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
CritBitTreeMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
// Inorder traversal of the tree
var current = this.root,
stack = [];
while (true) {
if (current !== null) {
stack.push(current);
current = current instanceof InternalNode ? current.left : null;
}
else {
if (stack.length > 0) {
current = stack.pop();
if (current instanceof ExternalNode)
callback.call(scope, current.value, current.key);
current = current instanceof InternalNode ? current.right : null;
}
else {
break;
}
}
}
};
/**
* Convenience known methods.
*/
CritBitTreeMap.prototype.inspect = function() {
return this;
};
if (typeof Symbol !== 'undefined')
CritBitTreeMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = CritBitTreeMap.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a CritBitTreeMap.
*
* @param {Iterable} iterable - Target iterable.
* @return {CritBitTreeMap}
*/
// CritBitTreeMap.from = function(iterable) {
// };
/**
* Exporting.
*/
module.exports = CritBitTreeMap;

View File

@@ -1,29 +0,0 @@
/**
* Mnemonist DefaultMap Typings
* =============================
*/
export default class DefaultMap<K, V> implements Iterable<[K, V]> {
// Members
size: number;
// Constructor
constructor(factory: (key: K, index: number) => V);
// Methods
clear(): void;
set(key: K, value: V): this;
delete(key: K): boolean;
has(key: K): boolean;
get(key: K): V;
peek(key: K): V | undefined;
forEach(callback: (value: V, key: K, map: this) => void, scope?: any): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
inspect(): any;
// Statics
static autoIncrement(): number;
}

View File

@@ -1,162 +0,0 @@
/**
* Mnemonist DefaultMap
* =====================
*
* JavaScript implementation of a default map that will return a constructed
* value any time one tries to access an inexisting key. It's quite similar
* to python's defaultdict.
*/
/**
* DefaultMap.
*
* @constructor
*/
function DefaultMap(factory) {
if (typeof factory !== 'function')
throw new Error('mnemonist/DefaultMap.constructor: expecting a function.');
this.items = new Map();
this.factory = factory;
this.size = 0;
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
DefaultMap.prototype.clear = function() {
// Properties
this.items.clear();
this.size = 0;
};
/**
* Method used to get the value set for given key. If the key does not exist,
* the value will be created using the provided factory.
*
* @param {any} key - Target key.
* @return {any}
*/
DefaultMap.prototype.get = function(key) {
var value = this.items.get(key);
if (typeof value === 'undefined') {
value = this.factory(key, this.size);
this.items.set(key, value);
this.size++;
}
return value;
};
/**
* Method used to get the value set for given key. If the key does not exist,
* a value won't be created.
*
* @param {any} key - Target key.
* @return {any}
*/
DefaultMap.prototype.peek = function(key) {
return this.items.get(key);
};
/**
* Method used to set a value for given key.
*
* @param {any} key - Target key.
* @param {any} value - Value.
* @return {DefaultMap}
*/
DefaultMap.prototype.set = function(key, value) {
this.items.set(key, value);
this.size = this.items.size;
return this;
};
/**
* Method used to test the existence of a key in the map.
*
* @param {any} key - Target key.
* @return {boolean}
*/
DefaultMap.prototype.has = function(key) {
return this.items.has(key);
};
/**
* Method used to delete target key.
*
* @param {any} key - Target key.
* @return {boolean}
*/
DefaultMap.prototype.delete = function(key) {
var deleted = this.items.delete(key);
this.size = this.items.size;
return deleted;
};
/**
* Method used to iterate over each of the key/value pairs.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
DefaultMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
this.items.forEach(callback, scope);
};
/**
* Iterators.
*/
DefaultMap.prototype.entries = function() {
return this.items.entries();
};
DefaultMap.prototype.keys = function() {
return this.items.keys();
};
DefaultMap.prototype.values = function() {
return this.items.values();
};
/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
DefaultMap.prototype[Symbol.iterator] = DefaultMap.prototype.entries;
/**
* Convenience known methods.
*/
DefaultMap.prototype.inspect = function() {
return this.items;
};
if (typeof Symbol !== 'undefined')
DefaultMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = DefaultMap.prototype.inspect;
/**
* Typical factories.
*/
DefaultMap.autoIncrement = function() {
var i = 0;
return function() {
return i++;
};
};
/**
* Exporting.
*/
module.exports = DefaultMap;

View File

@@ -1,18 +0,0 @@
/**
* Mnemonist DefaultWeakMap Typings
* ================================
*/
export default class DefaultWeakMap<K extends object, V> {
// Constructor
constructor(factory: (key: K) => V);
// Methods
clear(): void;
set(key: K, value: V): this;
delete(key: K): boolean;
has(key: K): boolean;
get(key: K): V;
peek(key: K): V | undefined;
inspect(): any;
}

View File

@@ -1,108 +0,0 @@
/**
* Mnemonist DefaultWeakMap
* =========================
*
* JavaScript implementation of a default weak map that will return a constructed
* value any time one tries to access an non-existing key. It is similar to
* DefaultMap but uses ES6 WeakMap that only holds weak reference to keys.
*/
/**
* DefaultWeakMap.
*
* @constructor
*/
function DefaultWeakMap(factory) {
if (typeof factory !== 'function')
throw new Error('mnemonist/DefaultWeakMap.constructor: expecting a function.');
this.items = new WeakMap();
this.factory = factory;
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
DefaultWeakMap.prototype.clear = function() {
// Properties
this.items = new WeakMap();
};
/**
* Method used to get the value set for given key. If the key does not exist,
* the value will be created using the provided factory.
*
* @param {any} key - Target key.
* @return {any}
*/
DefaultWeakMap.prototype.get = function(key) {
var value = this.items.get(key);
if (typeof value === 'undefined') {
value = this.factory(key);
this.items.set(key, value);
}
return value;
};
/**
* Method used to get the value set for given key. If the key does not exist,
* a value won't be created.
*
* @param {any} key - Target key.
* @return {any}
*/
DefaultWeakMap.prototype.peek = function(key) {
return this.items.get(key);
};
/**
* Method used to set a value for given key.
*
* @param {any} key - Target key.
* @param {any} value - Value.
* @return {DefaultMap}
*/
DefaultWeakMap.prototype.set = function(key, value) {
this.items.set(key, value);
return this;
};
/**
* Method used to test the existence of a key in the map.
*
* @param {any} key - Target key.
* @return {boolean}
*/
DefaultWeakMap.prototype.has = function(key) {
return this.items.has(key);
};
/**
* Method used to delete target key.
*
* @param {any} key - Target key.
* @return {boolean}
*/
DefaultWeakMap.prototype.delete = function(key) {
return this.items.delete(key);
};
/**
* Convenience known methods.
*/
DefaultWeakMap.prototype.inspect = function() {
return this.items;
};
if (typeof Symbol !== 'undefined')
DefaultWeakMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = DefaultWeakMap.prototype.inspect;
/**
* Exporting.
*/
module.exports = DefaultWeakMap;

View File

@@ -1,71 +0,0 @@
/**
* Mnemonist FibonacciHeap Typings
* ================================
*/
type FibonacciHeapComparator<T> = (a: T, b: T) => number;
export default class FibonacciHeap<T> {
// Members
size: number;
// Constructor
constructor(comparator?: FibonacciHeapComparator<T>);
// Methods
clear(): void;
push(item: T): number;
peek(): T | undefined;
pop(): T | undefined;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
comparator?: FibonacciHeapComparator<I>
): FibonacciHeap<I>;
}
export class MinFibonacciHeap<T> {
// Members
size: number;
// Constructor
constructor(comparator?: FibonacciHeapComparator<T>);
// Methods
clear(): void;
push(item: T): number;
peek(): T | undefined;
pop(): T | undefined;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
comparator?: FibonacciHeapComparator<I>
): FibonacciHeap<I>;
}
export class MaxFibonacciHeap<T> {
// Members
size: number;
// Constructor
constructor(comparator?: FibonacciHeapComparator<T>);
// Methods
clear(): void;
push(item: T): number;
peek(): T | undefined;
pop(): T | undefined;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
comparator?: FibonacciHeapComparator<I>
): FibonacciHeap<I>;
}

View File

@@ -1,321 +0,0 @@
/* eslint no-constant-condition: 0 */
/**
* Mnemonist Fibonacci Heap
* =========================
*
* Fibonacci heap implementation.
*/
var comparators = require('./utils/comparators.js'),
forEach = require('obliterator/foreach');
var DEFAULT_COMPARATOR = comparators.DEFAULT_COMPARATOR,
reverseComparator = comparators.reverseComparator;
/**
* Fibonacci Heap.
*
* @constructor
*/
function FibonacciHeap(comparator) {
this.clear();
this.comparator = comparator || DEFAULT_COMPARATOR;
if (typeof this.comparator !== 'function')
throw new Error('mnemonist/FibonacciHeap.constructor: given comparator should be a function.');
}
/**
* Method used to clear the heap.
*
* @return {undefined}
*/
FibonacciHeap.prototype.clear = function() {
// Properties
this.root = null;
this.min = null;
this.size = 0;
};
/**
* Function used to create a node.
*
* @param {any} item - Target item.
* @return {object}
*/
function createNode(item) {
return {
item: item,
degree: 0
};
}
/**
* Function used to merge the given node with the root list.
*
* @param {FibonacciHeap} heap - Target heap.
* @param {Node} node - Target node.
*/
function mergeWithRoot(heap, node) {
if (!heap.root) {
heap.root = node;
}
else {
node.right = heap.root.right;
node.left = heap.root;
heap.root.right.left = node;
heap.root.right = node;
}
}
/**
* Method used to push an item into the heap.
*
* @param {any} item - Item to push.
* @return {number}
*/
FibonacciHeap.prototype.push = function(item) {
var node = createNode(item);
node.left = node;
node.right = node;
mergeWithRoot(this, node);
if (!this.min || this.comparator(node.item, this.min.item) <= 0)
this.min = node;
return ++this.size;
};
/**
* Method used to get the "first" item of the heap.
*
* @return {any}
*/
FibonacciHeap.prototype.peek = function() {
return this.min ? this.min.item : undefined;
};
/**
* Function used to consume the given linked list.
*
* @param {Node} head - Head node.
* @param {array}
*/
function consumeLinkedList(head) {
var nodes = [],
node = head,
flag = false;
while (true) {
if (node === head && flag)
break;
else if (node === head)
flag = true;
nodes.push(node);
node = node.right;
}
return nodes;
}
/**
* Function used to remove the target node from the root list.
*
* @param {FibonacciHeap} heap - Target heap.
* @param {Node} node - Target node.
*/
function removeFromRoot(heap, node) {
if (heap.root === node)
heap.root = node.right;
node.left.right = node.right;
node.right.left = node.left;
}
/**
* Function used to merge the given node with the child list of a root node.
*
* @param {Node} parent - Parent node.
* @param {Node} node - Target node.
*/
function mergeWithChild(parent, node) {
if (!parent.child) {
parent.child = node;
}
else {
node.right = parent.child.right;
node.left = parent.child;
parent.child.right.left = node;
parent.child.right = node;
}
}
/**
* Function used to link one node to another in the root list.
*
* @param {FibonacciHeap} heap - Target heap.
* @param {Node} y - Y node.
* @param {Node} x - X node.
*/
function link(heap, y, x) {
removeFromRoot(heap, y);
y.left = y;
y.right = y;
mergeWithChild(x, y);
x.degree++;
y.parent = x;
}
/**
* Function used to consolidate the heap.
*
* @param {FibonacciHeap} heap - Target heap.
*/
function consolidate(heap) {
var A = new Array(heap.size),
nodes = consumeLinkedList(heap.root),
i, l, x, y, d, t;
for (i = 0, l = nodes.length; i < l; i++) {
x = nodes[i];
d = x.degree;
while (A[d]) {
y = A[d];
if (heap.comparator(x.item, y.item) > 0) {
t = x;
x = y;
y = t;
}
link(heap, y, x);
A[d] = null;
d++;
}
A[d] = x;
}
for (i = 0; i < heap.size; i++) {
if (A[i] && heap.comparator(A[i].item, heap.min.item) <= 0)
heap.min = A[i];
}
}
/**
* Method used to retrieve & remove the "first" item of the heap.
*
* @return {any}
*/
FibonacciHeap.prototype.pop = function() {
if (!this.size)
return undefined;
var z = this.min;
if (z.child) {
var nodes = consumeLinkedList(z.child),
node,
i,
l;
for (i = 0, l = nodes.length; i < l; i++) {
node = nodes[i];
mergeWithRoot(this, node);
delete node.parent;
}
}
removeFromRoot(this, z);
if (z === z.right) {
this.min = null;
this.root = null;
}
else {
this.min = z.right;
consolidate(this);
}
this.size--;
return z.item;
};
/**
* Convenience known methods.
*/
FibonacciHeap.prototype.inspect = function() {
var proxy = {
size: this.size
};
if (this.min && 'item' in this.min)
proxy.top = this.min.item;
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: FibonacciHeap,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
FibonacciHeap.prototype[Symbol.for('nodejs.util.inspect.custom')] = FibonacciHeap.prototype.inspect;
/**
* Fibonacci Maximum Heap.
*
* @constructor
*/
function MaxFibonacciHeap(comparator) {
this.clear();
this.comparator = comparator || DEFAULT_COMPARATOR;
if (typeof this.comparator !== 'function')
throw new Error('mnemonist/FibonacciHeap.constructor: given comparator should be a function.');
this.comparator = reverseComparator(this.comparator);
}
MaxFibonacciHeap.prototype = FibonacciHeap.prototype;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a heap.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} comparator - Custom comparator function.
* @return {FibonacciHeap}
*/
FibonacciHeap.from = function(iterable, comparator) {
var heap = new FibonacciHeap(comparator);
forEach(iterable, function(value) {
heap.push(value);
});
return heap;
};
MaxFibonacciHeap.from = function(iterable, comparator) {
var heap = new MaxFibonacciHeap(comparator);
forEach(iterable, function(value) {
heap.push(value);
});
return heap;
};
/**
* Exporting.
*/
FibonacciHeap.MinFibonacciHeap = FibonacciHeap;
FibonacciHeap.MaxFibonacciHeap = MaxFibonacciHeap;
module.exports = FibonacciHeap;

View File

@@ -1,427 +0,0 @@
/* eslint no-constant-condition: 0 */
/* eslint-disable */
/**
* Mnemonist FixedFixedCritBitTreeMap
* ===================================
*
* TODO...
*
* [References]:
* https://cr.yp.to/critbit.html
* https://www.imperialviolet.org/binary/critbit.pdf
*/
var bitwise = require('./utils/bitwise.js'),
typed = require('./utils/typed-arrays.js');
/**
* Helpers.
*/
/**
* Helper returning the direction we need to take given a key and an
* encoded critbit.
*
* @param {string} key - Target key.
* @param {number} critbit - Packed address of byte + mask.
* @return {number} - 0, left or 1, right.
*/
function getDirection(key, critbit) {
var byteIndex = critbit >> 8;
if (byteIndex > key.length - 1)
return 0;
var byte = key.charCodeAt(byteIndex),
mask = critbit & 0xff;
return byte & mask;
}
/**
* Helper returning the packed address of byte + mask or -1 if strings
* are identical.
*
* @param {string} a - First key.
* @param {string} b - Second key.
* @return {number} - Packed address of byte + mask.
*/
function findCriticalBit(a, b) {
var i = 0,
tmp;
// Swapping so a is the shortest
if (a.length > b.length) {
tmp = b;
b = a;
a = tmp;
}
var l = a.length,
mask;
while (i < l) {
if (a[i] !== b[i]) {
mask = bitwise.msb8(
a.charCodeAt(i) ^ b.charCodeAt(i)
);
return (i << 8) | mask;
}
i++;
}
// Strings are identical
if (a.length === b.length)
return -1;
// NOTE: x ^ 0 is the same as x
mask = bitwise.msb8(b.charCodeAt(i));
return (i << 8) | mask;
}
/**
* FixedCritBitTreeMap.
*
* @constructor
*/
function FixedCritBitTreeMap(capacity) {
if (typeof capacity !== 'number' || capacity <= 0)
throw new Error('mnemonist/fixed-critbit-tree-map: `capacity` should be a positive number.');
// Properties
this.capacity = capacity;
this.offset = 0;
this.root = 0;
this.size = 0;
var PointerArray = typed.getSignedPointerArray(capacity + 1);
this.keys = new Array(capacity);
this.values = new Array(capacity);
this.lefts = new PointerArray(capacity - 1);
this.rights = new PointerArray(capacity - 1);
this.critbits = new Uint32Array(capacity);
}
/**
* Method used to clear the FixedCritBitTreeMap.
*
* @return {undefined}
*/
FixedCritBitTreeMap.prototype.clear = function() {
// Properties
// TODO...
this.root = null;
this.size = 0;
};
/**
* Method used to set the value of the given key in the trie.
*
* @param {string} key - Key to set.
* @param {any} value - Arbitrary value.
* @return {FixedCritBitTreeMap}
*/
FixedCritBitTreeMap.prototype.set = function(key, value) {
var pointer;
// TODO: yell if capacity is already full!
// Tree is empty
if (this.size === 0) {
this.keys[0] = key;
this.values[0] = value;
this.size++;
this.root = -1;
return this;
}
// Walk state
var pointer = this.root,
newPointer,
leftOrRight,
opposite,
ancestors = [],
path = [],
ancestor,
parent,
child,
critbit,
internal,
best,
dir,
i,
l;
// Walking the tree
while (true) {
// Traversing an internal node
if (pointer > 0) {
pointer -= 1;
// Choosing the correct direction
dir = getDirection(key, this.critbits[pointer]);
leftOrRight = dir === 0 ? this.lefts : this.rights;
newPointer = leftOrRight[pointer];
if (newPointer === 0) {
// Creating a fitting external node
pointer = this.size++;
leftOrRight[newPointer] = -(pointer + 1);
this.keys[pointer] = key;
this.values[pointer] = value;
return this;
}
ancestors.push(pointer);
path.push(dir);
pointer = newPointer;
}
// Reaching an external node
else {
pointer = -pointer;
pointer -= 1;
// 1. Creating a new external node
critbit = findCriticalBit(key, this.keys[pointer]);
// Key is identical, we just replace the value
if (critbit === -1) {
this.values[pointer] = value;
return this;
}
internal = this.offset++;
newPointer = this.size++;
this.keys[newPointer] = key;
this.values[newPointer] = value;
this.critbits[internal] = critbit;
dir = getDirection(key, critbit);
leftOrRight = dir === 0 ? this.lefts : this.rights;
opposite = dir === 0 ? this.rights : this.lefts;
leftOrRight[internal] = -(newPointer + 1);
opposite[internal] = -(pointer + 1);
// 2. Bubbling up
best = -1;
l = ancestors.length;
for (i = l - 1; i >= 0; i--) {
ancestor = ancestors[i];
// TODO: this can be made faster
if ((this.critbits[ancestor] >> 8) > (critbit >> 8)) {
continue;
}
else if ((this.critbits[ancestor] >> 8) === (critbit >> 8)) {
if ((this.critbits[ancestor] & 0xff) < (critbit & 0xff))
continue;
}
best = i;
break;
}
// Do we need to attach to the root?
if (best < 0) {
this.root = internal + 1;
// Need to rewire parent as child?
if (l > 0) {
parent = ancestors[0];
opposite[internal] = parent + 1;
}
}
// Simple case without rotation
else if (best === l - 1) {
parent = ancestors[best];
dir = path[best];
leftOrRight = dir === 0 ? this.lefts : this.rights;
leftOrRight[parent] = internal + 1;
}
// Full rotation
else {
parent = ancestors[best];
dir = path[best];
child = ancestors[best + 1];
opposite[internal] = child + 1;
leftOrRight = dir === 0 ? this.lefts : this.rights;
leftOrRight[parent] = internal + 1;
}
return this;
}
}
};
/**
* Method used to get the value attached to the given key in the tree or
* undefined if not found.
*
* @param {string} key - Key to get.
* @return {any}
*/
FixedCritBitTreeMap.prototype.get = function(key) {
// Walk state
var pointer = this.root,
dir;
// Walking the tree
while (true) {
// Dead end
if (pointer === 0)
return;
// Traversing an internal node
if (pointer > 0) {
pointer -= 1;
dir = getDirection(key, this.critbits[pointer]);
pointer = dir === 0 ? this.lefts[pointer] : this.rights[pointer];
}
// Reaching an external node
else {
pointer = -pointer;
pointer -= 1;
if (this.keys[pointer] !== key)
return;
return this.values[pointer];
}
}
};
/**
* Method used to return whether the given key exists in the tree.
*
* @param {string} key - Key to test.
* @return {boolean}
*/
FixedCritBitTreeMap.prototype.has = function(key) {
// Walk state
var pointer = this.root,
dir;
// Walking the tree
while (true) {
// Dead end
if (pointer === 0)
return false;
// Traversing an internal node
if (pointer > 0) {
pointer -= 1;
dir = getDirection(key, this.critbits[pointer]);
pointer = dir === 0 ? this.lefts[pointer] : this.rights[pointer];
}
// Reaching an external node
else {
pointer = -pointer;
pointer -= 1;
return this.keys[pointer] === key;
}
}
};
/**
* Method used to iterate over the tree in key order.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
FixedCritBitTreeMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
// Inorder traversal of the tree
var current = this.root,
stack = [],
p;
while (true) {
if (current !== 0) {
stack.push(current);
current = current > 0 ? this.lefts[current - 1] : 0;
}
else {
if (stack.length > 0) {
current = stack.pop();
if (current < 0) {
p = -current;
p -= 1;
callback.call(scope, this.values[p], this.keys[p]);
}
current = current > 0 ? this.rights[current - 1] : 0;
}
else {
break;
}
}
}
};
/**
* Convenience known methods.
*/
FixedCritBitTreeMap.prototype.inspect = function() {
return this;
};
if (typeof Symbol !== 'undefined')
FixedCritBitTreeMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = FixedCritBitTreeMap.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a FixedCritBitTreeMap.
*
* @param {Iterable} iterable - Target iterable.
* @return {FixedCritBitTreeMap}
*/
// FixedCritBitTreeMap.from = function(iterable) {
// };
/**
* Exporting.
*/
module.exports = FixedCritBitTreeMap;

View File

@@ -1,34 +0,0 @@
/**
* Mnemonist FixedDeque Typings
* =============================
*/
import {IArrayLikeConstructor, TypedArray} from './utils/types';
export default class FixedDeque<T> implements Iterable<T> {
// Members
capacity: number;
size: number;
// Constructor
constructor(ArrayClass: IArrayLikeConstructor, capacity: number);
// Methods
clear(): void;
push(item: T): number;
unshift(item: T): number;
pop(): T | undefined;
shift(): T | undefined;
peekFirst(): T | undefined;
peekLast(): T | undefined;
get(index: number): T | undefined;
forEach(callback: (item: T, index: number, buffer: this) => void, scope?: any): void;
toArray(): Array<T> | TypedArray;
values(): IterableIterator<T>;
entries(): IterableIterator<[number, T]>;
[Symbol.iterator](): IterableIterator<T>;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}, ArrayClass: IArrayLikeConstructor, capacity?: number): FixedDeque<I>;
}

View File

@@ -1,357 +0,0 @@
/**
* Mnemonist FixedDeque
* =====================
*
* Fixed capacity double-ended queue implemented as ring deque.
*/
var iterables = require('./utils/iterables.js'),
Iterator = require('obliterator/iterator');
/**
* FixedDeque.
*
* @constructor
*/
function FixedDeque(ArrayClass, capacity) {
if (arguments.length < 2)
throw new Error('mnemonist/fixed-deque: expecting an Array class and a capacity.');
if (typeof capacity !== 'number' || capacity <= 0)
throw new Error('mnemonist/fixed-deque: `capacity` should be a positive number.');
this.ArrayClass = ArrayClass;
this.capacity = capacity;
this.items = new ArrayClass(this.capacity);
this.clear();
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
FixedDeque.prototype.clear = function() {
// Properties
this.start = 0;
this.size = 0;
};
/**
* Method used to append a value to the deque.
*
* @param {any} item - Item to append.
* @return {number} - Returns the new size of the deque.
*/
FixedDeque.prototype.push = function(item) {
if (this.size === this.capacity)
throw new Error('mnemonist/fixed-deque.push: deque capacity (' + this.capacity + ') exceeded!');
var index = this.start + this.size;
if (index >= this.capacity)
index -= this.capacity;
this.items[index] = item;
return ++this.size;
};
/**
* Method used to prepend a value to the deque.
*
* @param {any} item - Item to prepend.
* @return {number} - Returns the new size of the deque.
*/
FixedDeque.prototype.unshift = function(item) {
if (this.size === this.capacity)
throw new Error('mnemonist/fixed-deque.unshift: deque capacity (' + this.capacity + ') exceeded!');
var index = this.start - 1;
if (this.start === 0)
index = this.capacity - 1;
this.items[index] = item;
this.start = index;
return ++this.size;
};
/**
* Method used to pop the deque.
*
* @return {any} - Returns the popped item.
*/
FixedDeque.prototype.pop = function() {
if (this.size === 0)
return;
this.size--;
var index = this.start + this.size;
if (index >= this.capacity)
index -= this.capacity;
return this.items[index];
};
/**
* Method used to shift the deque.
*
* @return {any} - Returns the shifted item.
*/
FixedDeque.prototype.shift = function() {
if (this.size === 0)
return;
var index = this.start;
this.size--;
this.start++;
if (this.start === this.capacity)
this.start = 0;
return this.items[index];
};
/**
* Method used to peek the first value of the deque.
*
* @return {any}
*/
FixedDeque.prototype.peekFirst = function() {
if (this.size === 0)
return;
return this.items[this.start];
};
/**
* Method used to peek the last value of the deque.
*
* @return {any}
*/
FixedDeque.prototype.peekLast = function() {
if (this.size === 0)
return;
var index = this.start + this.size - 1;
if (index >= this.capacity)
index -= this.capacity;
return this.items[index];
};
/**
* Method used to get the desired value of the deque.
*
* @param {number} index
* @return {any}
*/
FixedDeque.prototype.get = function(index) {
if (this.size === 0 || index >= this.capacity)
return;
index = this.start + index;
if (index >= this.capacity)
index -= this.capacity;
return this.items[index];
};
/**
* Method used to iterate over the deque.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
FixedDeque.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
var c = this.capacity,
l = this.size,
i = this.start,
j = 0;
while (j < l) {
callback.call(scope, this.items[i], j, this);
i++;
j++;
if (i === c)
i = 0;
}
};
/**
* Method used to convert the deque to a JavaScript array.
*
* @return {array}
*/
// TODO: optional array class as argument?
FixedDeque.prototype.toArray = function() {
// Optimization
var offset = this.start + this.size;
if (offset < this.capacity)
return this.items.slice(this.start, offset);
var array = new this.ArrayClass(this.size),
c = this.capacity,
l = this.size,
i = this.start,
j = 0;
while (j < l) {
array[j] = this.items[i];
i++;
j++;
if (i === c)
i = 0;
}
return array;
};
/**
* Method used to create an iterator over the deque's values.
*
* @return {Iterator}
*/
FixedDeque.prototype.values = function() {
var items = this.items,
c = this.capacity,
l = this.size,
i = this.start,
j = 0;
return new Iterator(function() {
if (j >= l)
return {
done: true
};
var value = items[i];
i++;
j++;
if (i === c)
i = 0;
return {
value: value,
done: false
};
});
};
/**
* Method used to create an iterator over the deque's entries.
*
* @return {Iterator}
*/
FixedDeque.prototype.entries = function() {
var items = this.items,
c = this.capacity,
l = this.size,
i = this.start,
j = 0;
return new Iterator(function() {
if (j >= l)
return {
done: true
};
var value = items[i];
i++;
if (i === c)
i = 0;
return {
value: [j++, value],
done: false
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
FixedDeque.prototype[Symbol.iterator] = FixedDeque.prototype.values;
/**
* Convenience known methods.
*/
FixedDeque.prototype.inspect = function() {
var array = this.toArray();
array.type = this.ArrayClass.name;
array.capacity = this.capacity;
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: FixedDeque,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
FixedDeque.prototype[Symbol.for('nodejs.util.inspect.custom')] = FixedDeque.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a deque.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} ArrayClass - Array class to use.
* @param {number} capacity - Desired capacity.
* @return {FiniteStack}
*/
FixedDeque.from = function(iterable, ArrayClass, capacity) {
if (arguments.length < 3) {
capacity = iterables.guessLength(iterable);
if (typeof capacity !== 'number')
throw new Error('mnemonist/fixed-deque.from: could not guess iterable length. Please provide desired capacity as last argument.');
}
var deque = new FixedDeque(ArrayClass, capacity);
if (iterables.isArrayLike(iterable)) {
var i, l;
for (i = 0, l = iterable.length; i < l; i++)
deque.items[i] = iterable[i];
deque.size = l;
return deque;
}
iterables.forEach(iterable, function(value) {
deque.push(value);
});
return deque;
};
/**
* Exporting.
*/
module.exports = FixedDeque;

View File

@@ -1,25 +0,0 @@
/**
* Mnemonist FixedReverseHeap Typings
* ===================================
*/
import {IArrayLikeConstructor, TypedArray} from './utils/types';
type HeapComparator<T> = (a: T, b: T) => number;
export default class FixedReverseHeap<T> {
// Members
capacity: number;
size: number;
// Constructor
constructor(ArrayClass: IArrayLikeConstructor, comparator: HeapComparator<T>, capacity: number);
constructor(ArrayClass: IArrayLikeConstructor, capacity: number);
// Methods
clear(): void;
push(item: T): number;
consume(): Array<T> | TypedArray;
toArray(): Array<T> | TypedArray;
inspect(): any;
}

View File

@@ -1,209 +0,0 @@
/**
* Mnemonist Fixed Reverse Heap
* =============================
*
* Static heap implementation with fixed capacity. It's a "reverse" heap
* because it stores the elements in reverse so we can replace the worst
* item in logarithmic time. As such, one cannot pop this heap but can only
* consume it at the end. This structure is very efficient when trying to
* find the n smallest/largest items from a larger query (k nearest neigbors
* for instance).
*/
var comparators = require('./utils/comparators.js'),
Heap = require('./heap.js');
var DEFAULT_COMPARATOR = comparators.DEFAULT_COMPARATOR,
reverseComparator = comparators.reverseComparator;
/**
* Helper functions.
*/
/**
* Function used to sift up.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @param {number} size - Heap's true size.
* @param {number} i - Index.
*/
function siftUp(compare, heap, size, i) {
var endIndex = size,
startIndex = i,
item = heap[i],
childIndex = 2 * i + 1,
rightIndex;
while (childIndex < endIndex) {
rightIndex = childIndex + 1;
if (
rightIndex < endIndex &&
compare(heap[childIndex], heap[rightIndex]) >= 0
) {
childIndex = rightIndex;
}
heap[i] = heap[childIndex];
i = childIndex;
childIndex = 2 * i + 1;
}
heap[i] = item;
Heap.siftDown(compare, heap, startIndex, i);
}
/**
* Fully consumes the given heap.
*
* @param {function} ArrayClass - Array class to use.
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @param {number} size - True size of the heap.
* @return {array}
*/
function consume(ArrayClass, compare, heap, size) {
var l = size,
i = l;
var array = new ArrayClass(size),
lastItem,
item;
while (i > 0) {
lastItem = heap[--i];
if (i !== 0) {
item = heap[0];
heap[0] = lastItem;
siftUp(compare, heap, --size, 0);
lastItem = item;
}
array[i] = lastItem;
}
return array;
}
/**
* Binary Minimum FixedReverseHeap.
*
* @constructor
* @param {function} ArrayClass - The class of array to use.
* @param {function} comparator - Comparator function.
* @param {number} capacity - Maximum number of items to keep.
*/
function FixedReverseHeap(ArrayClass, comparator, capacity) {
// Comparator can be omitted
if (arguments.length === 2) {
capacity = comparator;
comparator = null;
}
this.ArrayClass = ArrayClass;
this.capacity = capacity;
this.items = new ArrayClass(capacity);
this.clear();
this.comparator = comparator || DEFAULT_COMPARATOR;
if (typeof capacity !== 'number' && capacity <= 0)
throw new Error('mnemonist/FixedReverseHeap.constructor: capacity should be a number > 0.');
if (typeof this.comparator !== 'function')
throw new Error('mnemonist/FixedReverseHeap.constructor: given comparator should be a function.');
this.comparator = reverseComparator(this.comparator);
}
/**
* Method used to clear the heap.
*
* @return {undefined}
*/
FixedReverseHeap.prototype.clear = function() {
// Properties
this.size = 0;
};
/**
* Method used to push an item into the heap.
*
* @param {any} item - Item to push.
* @return {number}
*/
FixedReverseHeap.prototype.push = function(item) {
// Still some place
if (this.size < this.capacity) {
this.items[this.size] = item;
Heap.siftDown(this.comparator, this.items, 0, this.size);
this.size++;
}
// Heap is full, we need to replace worst item
else {
if (this.comparator(item, this.items[0]) > 0)
Heap.replace(this.comparator, this.items, item);
}
return this.size;
};
/**
* Method used to peek the worst item in the heap.
*
* @return {any}
*/
FixedReverseHeap.prototype.peek = function() {
return this.items[0];
};
/**
* Method used to consume the heap fully and return its items as a sorted array.
*
* @return {array}
*/
FixedReverseHeap.prototype.consume = function() {
var items = consume(this.ArrayClass, this.comparator, this.items, this.size);
this.size = 0;
return items;
};
/**
* Method used to convert the heap to an array. Note that it basically clone
* the heap and consumes it completely. This is hardly performant.
*
* @return {array}
*/
FixedReverseHeap.prototype.toArray = function() {
return consume(this.ArrayClass, this.comparator, this.items.slice(0, this.size), this.size);
};
/**
* Convenience known methods.
*/
FixedReverseHeap.prototype.inspect = function() {
var proxy = this.toArray();
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: FixedReverseHeap,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
FixedReverseHeap.prototype[Symbol.for('nodejs.util.inspect.custom')] = FixedReverseHeap.prototype.inspect;
/**
* Exporting.
*/
module.exports = FixedReverseHeap;

View File

@@ -1,36 +0,0 @@
/**
* Mnemonist FixedStack Typings
* =============================
*/
import {IArrayLikeConstructor, TypedArray} from './utils/types';
export default class FixedStack<T> implements Iterable<T> {
// Members
capacity: number;
size: number;
// Constructor
constructor(ArrayClass: IArrayLikeConstructor, capacity: number);
// Methods
clear(): void;
push(item: T): number;
pop(): T | undefined;
peek(): T | undefined;
forEach(callback: (item: T, index: number, stack: this) => void, scope?: any): void;
toArray(): Array<T> | TypedArray;
values(): IterableIterator<T>;
entries(): IterableIterator<[number, T]>;
[Symbol.iterator](): IterableIterator<T>;
toString(): string;
toJSON(): Iterable<T>;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
ArrayClass: IArrayLikeConstructor,
capacity?: number
): FixedStack<I>;
}

View File

@@ -1,242 +0,0 @@
/**
* Mnemonist FixedStack
* =====================
*
* The fixed stack is a stack whose capacity is defined beforehand and that
* cannot be exceeded. This class is really useful when combined with
* byte arrays to save up some memory and avoid memory re-allocation, hence
* speeding up computations.
*
* This has however a downside: you need to know the maximum size you stack
* can have during your iteration (which is not too difficult to compute when
* performing, say, a DFS on a balanced binary tree).
*/
var Iterator = require('obliterator/iterator'),
iterables = require('./utils/iterables.js');
/**
* FixedStack
*
* @constructor
* @param {function} ArrayClass - Array class to use.
* @param {number} capacity - Desired capacity.
*/
function FixedStack(ArrayClass, capacity) {
if (arguments.length < 2)
throw new Error('mnemonist/fixed-stack: expecting an Array class and a capacity.');
if (typeof capacity !== 'number' || capacity <= 0)
throw new Error('mnemonist/fixed-stack: `capacity` should be a positive number.');
this.capacity = capacity;
this.ArrayClass = ArrayClass;
this.items = new this.ArrayClass(this.capacity);
this.clear();
}
/**
* Method used to clear the stack.
*
* @return {undefined}
*/
FixedStack.prototype.clear = function() {
// Properties
this.size = 0;
};
/**
* Method used to add an item to the stack.
*
* @param {any} item - Item to add.
* @return {number}
*/
FixedStack.prototype.push = function(item) {
if (this.size === this.capacity)
throw new Error('mnemonist/fixed-stack.push: stack capacity (' + this.capacity + ') exceeded!');
this.items[this.size++] = item;
return this.size;
};
/**
* Method used to retrieve & remove the last item of the stack.
*
* @return {any}
*/
FixedStack.prototype.pop = function() {
if (this.size === 0)
return;
return this.items[--this.size];
};
/**
* Method used to get the last item of the stack.
*
* @return {any}
*/
FixedStack.prototype.peek = function() {
return this.items[this.size - 1];
};
/**
* Method used to iterate over the stack.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
FixedStack.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
for (var i = 0, l = this.items.length; i < l; i++)
callback.call(scope, this.items[l - i - 1], i, this);
};
/**
* Method used to convert the stack to a JavaScript array.
*
* @return {array}
*/
FixedStack.prototype.toArray = function() {
var array = new this.ArrayClass(this.size),
l = this.size - 1,
i = this.size;
while (i--)
array[i] = this.items[l - i];
return array;
};
/**
* Method used to create an iterator over a stack's values.
*
* @return {Iterator}
*/
FixedStack.prototype.values = function() {
var items = this.items,
l = this.size,
i = 0;
return new Iterator(function() {
if (i >= l)
return {
done: true
};
var value = items[l - i - 1];
i++;
return {
value: value,
done: false
};
});
};
/**
* Method used to create an iterator over a stack's entries.
*
* @return {Iterator}
*/
FixedStack.prototype.entries = function() {
var items = this.items,
l = this.size,
i = 0;
return new Iterator(function() {
if (i >= l)
return {
done: true
};
var value = items[l - i - 1];
return {
value: [i++, value],
done: false
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
FixedStack.prototype[Symbol.iterator] = FixedStack.prototype.values;
/**
* Convenience known methods.
*/
FixedStack.prototype.toString = function() {
return this.toArray().join(',');
};
FixedStack.prototype.toJSON = function() {
return this.toArray();
};
FixedStack.prototype.inspect = function() {
var array = this.toArray();
array.type = this.ArrayClass.name;
array.capacity = this.capacity;
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: FixedStack,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
FixedStack.prototype[Symbol.for('nodejs.util.inspect.custom')] = FixedStack.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a stack.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} ArrayClass - Array class to use.
* @param {number} capacity - Desired capacity.
* @return {FixedStack}
*/
FixedStack.from = function(iterable, ArrayClass, capacity) {
if (arguments.length < 3) {
capacity = iterables.guessLength(iterable);
if (typeof capacity !== 'number')
throw new Error('mnemonist/fixed-stack.from: could not guess iterable length. Please provide desired capacity as last argument.');
}
var stack = new FixedStack(ArrayClass, capacity);
if (iterables.isArrayLike(iterable)) {
var i, l;
for (i = 0, l = iterable.length; i < l; i++)
stack.items[i] = iterable[i];
stack.size = l;
return stack;
}
iterables.forEach(iterable, function(value) {
stack.push(value);
});
return stack;
};
/**
* Exporting.
*/
module.exports = FixedStack;

View File

@@ -1,33 +0,0 @@
/**
* Mnemonist FuzzyMap Typings
* ==========================
*/
type HashFunction<K> = (key: any) => K;
type HashFunctionsTuple<K> = [HashFunction<K>, HashFunction<K>];
export default class FuzzyMap<K, V> implements Iterable<V> {
// Members
size: number;
// Constructor
constructor(hashFunction: HashFunction<K>);
constructor(hashFunctionsTuple: HashFunctionsTuple<K>);
// Methods
clear(): void;
add(key: V): this;
set(key: K, value: V): this;
get(key: any): V | undefined;
has(key: any): boolean;
forEach(callback: (value: V, key: V) => void, scope?: this): void;
values(): IterableIterator<V>;
[Symbol.iterator](): IterableIterator<V>;
inspect(): any;
// Statics
static from<I, J>(
iterable: Iterable<[I, J]> | {[key: string]: J},
hashFunction: HashFunction<I> | HashFunctionsTuple<I>,
): FuzzyMap<I, J>;
}

View File

@@ -1,185 +0,0 @@
/**
* Mnemonist Fuzzy Map
* ====================
*
* The fuzzy map is a map whose keys are processed by a function before
* read/write operations. This can often result in multiple keys accessing
* the same resource (example: a map with lowercased keys).
*/
var forEach = require('obliterator/foreach');
var identity = function(x) {
return x;
};
/**
* FuzzyMap.
*
* @constructor
* @param {array|function} descriptor - Hash functions descriptor.
*/
function FuzzyMap(descriptor) {
this.items = new Map();
this.clear();
if (Array.isArray(descriptor)) {
this.writeHashFunction = descriptor[0];
this.readHashFunction = descriptor[1];
}
else {
this.writeHashFunction = descriptor;
this.readHashFunction = descriptor;
}
if (!this.writeHashFunction)
this.writeHashFunction = identity;
if (!this.readHashFunction)
this.readHashFunction = identity;
if (typeof this.writeHashFunction !== 'function')
throw new Error('mnemonist/FuzzyMap.constructor: invalid hash function given.');
if (typeof this.readHashFunction !== 'function')
throw new Error('mnemonist/FuzzyMap.constructor: invalid hash function given.');
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
FuzzyMap.prototype.clear = function() {
this.items.clear();
// Properties
this.size = 0;
};
/**
* Method used to add an item to the FuzzyMap.
*
* @param {any} item - Item to add.
* @return {FuzzyMap}
*/
FuzzyMap.prototype.add = function(item) {
var key = this.writeHashFunction(item);
this.items.set(key, item);
this.size = this.items.size;
return this;
};
/**
* Method used to set an item in the FuzzyMap using the given key.
*
* @param {any} key - Key to use.
* @param {any} item - Item to add.
* @return {FuzzyMap}
*/
FuzzyMap.prototype.set = function(key, item) {
key = this.writeHashFunction(key);
this.items.set(key, item);
this.size = this.items.size;
return this;
};
/**
* Method used to retrieve an item from the FuzzyMap.
*
* @param {any} key - Key to use.
* @return {any}
*/
FuzzyMap.prototype.get = function(key) {
key = this.readHashFunction(key);
return this.items.get(key);
};
/**
* Method used to test the existence of an item in the map.
*
* @param {any} key - Key to check.
* @return {boolean}
*/
FuzzyMap.prototype.has = function(key) {
key = this.readHashFunction(key);
return this.items.has(key);
};
/**
* Method used to iterate over each of the FuzzyMap's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
FuzzyMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
this.items.forEach(function(value) {
callback.call(scope, value, value);
});
};
/**
* Method returning an iterator over the FuzzyMap's values.
*
* @return {FuzzyMapIterator}
*/
FuzzyMap.prototype.values = function() {
return this.items.values();
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
FuzzyMap.prototype[Symbol.iterator] = FuzzyMap.prototype.values;
/**
* Convenience known method.
*/
FuzzyMap.prototype.inspect = function() {
var array = Array.from(this.items.values());
Object.defineProperty(array, 'constructor', {
value: FuzzyMap,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
FuzzyMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = FuzzyMap.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {array|function} descriptor - Hash functions descriptor.
* @param {boolean} useSet - Whether to use #.set or #.add
* @return {FuzzyMap}
*/
FuzzyMap.from = function(iterable, descriptor, useSet) {
var map = new FuzzyMap(descriptor);
forEach(iterable, function(value, key) {
if (useSet)
map.set(key, value);
else
map.add(value);
});
return map;
};
/**
* Exporting.
*/
module.exports = FuzzyMap;

View File

@@ -1,36 +0,0 @@
/**
* Mnemonist FuzzyMultiMap Typings
* ================================
*/
type HashFunction<K> = (key: any) => K;
type HashFunctionsTuple<K> = [HashFunction<K>, HashFunction<K>];
type FuzzyMultiMapContainer = ArrayConstructor | SetConstructor;
export default class FuzzyMultiMap<K, V> implements Iterable<V> {
// Members
dimension: number;
size: number;
// Constructor
constructor(hashFunction: HashFunction<K>, Container?: FuzzyMultiMapContainer);
constructor(hashFunctions: HashFunctionsTuple<K>, Container?: FuzzyMultiMapContainer);
// Methods
clear(): void;
add(value: V): this;
set(key: K, value: V): this;
get(key: any): Array<V> | Set<V> | undefined;
has(key: any): boolean;
forEach(callback: (value: V, key: V) => void, scope?: any): void;
values(): IterableIterator<V>;
[Symbol.iterator](): IterableIterator<V>;
inspect(): any;
// Statics
static from<I, J>(
iterable: Iterable<[I, J]> | {[key: string]: J},
hashFunction: HashFunction<I> | HashFunctionsTuple<I>,
Container?: FuzzyMultiMapContainer
): FuzzyMultiMap<I, J>;
}

View File

@@ -1,196 +0,0 @@
/**
* Mnemonist FuzzyMultiMap
* ========================
*
* Same as the fuzzy map but relying on a MultiMap rather than a Map.
*/
var MultiMap = require('./multi-map.js'),
forEach = require('obliterator/foreach');
var identity = function(x) {
return x;
};
/**
* FuzzyMultiMap.
*
* @constructor
* @param {array|function} descriptor - Hash functions descriptor.
* @param {function} Container - Container to use.
*/
function FuzzyMultiMap(descriptor, Container) {
this.items = new MultiMap(Container);
this.clear();
if (Array.isArray(descriptor)) {
this.writeHashFunction = descriptor[0];
this.readHashFunction = descriptor[1];
}
else {
this.writeHashFunction = descriptor;
this.readHashFunction = descriptor;
}
if (!this.writeHashFunction)
this.writeHashFunction = identity;
if (!this.readHashFunction)
this.readHashFunction = identity;
if (typeof this.writeHashFunction !== 'function')
throw new Error('mnemonist/FuzzyMultiMap.constructor: invalid hash function given.');
if (typeof this.readHashFunction !== 'function')
throw new Error('mnemonist/FuzzyMultiMap.constructor: invalid hash function given.');
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
FuzzyMultiMap.prototype.clear = function() {
this.items.clear();
// Properties
this.size = 0;
this.dimension = 0;
};
/**
* Method used to add an item to the index.
*
* @param {any} item - Item to add.
* @return {FuzzyMultiMap}
*/
FuzzyMultiMap.prototype.add = function(item) {
var key = this.writeHashFunction(item);
this.items.set(key, item);
this.size = this.items.size;
this.dimension = this.items.dimension;
return this;
};
/**
* Method used to set an item in the index using the given key.
*
* @param {any} key - Key to use.
* @param {any} item - Item to add.
* @return {FuzzyMultiMap}
*/
FuzzyMultiMap.prototype.set = function(key, item) {
key = this.writeHashFunction(key);
this.items.set(key, item);
this.size = this.items.size;
this.dimension = this.items.dimension;
return this;
};
/**
* Method used to retrieve an item from the index.
*
* @param {any} key - Key to use.
* @return {any}
*/
FuzzyMultiMap.prototype.get = function(key) {
key = this.readHashFunction(key);
return this.items.get(key);
};
/**
* Method used to test the existence of an item in the map.
*
* @param {any} key - Key to check.
* @return {boolean}
*/
FuzzyMultiMap.prototype.has = function(key) {
key = this.readHashFunction(key);
return this.items.has(key);
};
/**
* Method used to iterate over each of the index's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
FuzzyMultiMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
this.items.forEach(function(value) {
callback.call(scope, value, value);
});
};
/**
* Method returning an iterator over the index's values.
*
* @return {FuzzyMultiMapIterator}
*/
FuzzyMultiMap.prototype.values = function() {
return this.items.values();
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
FuzzyMultiMap.prototype[Symbol.iterator] = FuzzyMultiMap.prototype.values;
/**
* Convenience known method.
*/
FuzzyMultiMap.prototype.inspect = function() {
var array = Array.from(this);
Object.defineProperty(array, 'constructor', {
value: FuzzyMultiMap,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
FuzzyMultiMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = FuzzyMultiMap.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {array|function} descriptor - Hash functions descriptor.
* @param {function} Container - Container to use.
* @param {boolean} useSet - Whether to use #.set or #.add
* @return {FuzzyMultiMap}
*/
FuzzyMultiMap.from = function(iterable, descriptor, Container, useSet) {
if (arguments.length === 3) {
if (typeof Container === 'boolean') {
useSet = Container;
Container = Array;
}
}
var map = new FuzzyMultiMap(descriptor, Container);
forEach(iterable, function(value, key) {
if (useSet)
map.set(key, value);
else
map.add(value);
});
return map;
};
/**
* Exporting.
*/
module.exports = FuzzyMultiMap;

View File

@@ -1,32 +0,0 @@
/**
* Mnemonist HashedArrayTree Typings
* ==================================
*/
import {IArrayLikeConstructor} from './utils/types';
type HashedArrayTreeOptions = {
initialCapacity?: number;
initialLength?: number;
blockSize?: number;
}
export default class HashedArrayTree<T> {
// Members
blockSize: number;
capacity: number;
length: number;
// Constructor
constructor(ArrayClass: IArrayLikeConstructor, capacity: number);
constructor(ArrayClass: IArrayLikeConstructor, options: HashedArrayTreeOptions);
// Methods
set(index: number, value: T): this;
get(index: number): T | undefined;
grow(capacity: number): this;
resize(length: number): this;
push(value: T): number;
pop(): T | undefined;
inspect(): any;
}

View File

@@ -1,209 +0,0 @@
/**
* Mnemonist HashedArrayTree
* ==========================
*
* Abstract implementation of a hashed array tree representing arrays growing
* dynamically.
*/
/**
* Defaults.
*/
var DEFAULT_BLOCK_SIZE = 1024;
/**
* Helpers.
*/
function powerOfTwo(x) {
return (x & (x - 1)) === 0;
}
/**
* HashedArrayTree.
*
* @constructor
* @param {function} ArrayClass - An array constructor.
* @param {number|object} initialCapacityOrOptions - Self-explanatory.
*/
function HashedArrayTree(ArrayClass, initialCapacityOrOptions) {
if (arguments.length < 1)
throw new Error('mnemonist/hashed-array-tree: expecting at least a byte array constructor.');
var initialCapacity = initialCapacityOrOptions || 0,
blockSize = DEFAULT_BLOCK_SIZE,
initialLength = 0;
if (typeof initialCapacityOrOptions === 'object') {
initialCapacity = initialCapacityOrOptions.initialCapacity || 0;
initialLength = initialCapacityOrOptions.initialLength || 0;
blockSize = initialCapacityOrOptions.blockSize || DEFAULT_BLOCK_SIZE;
}
if (!blockSize || !powerOfTwo(blockSize))
throw new Error('mnemonist/hashed-array-tree: block size should be a power of two.');
var capacity = Math.max(initialLength, initialCapacity),
initialBlocks = Math.ceil(capacity / blockSize);
this.ArrayClass = ArrayClass;
this.length = initialLength;
this.capacity = initialBlocks * blockSize;
this.blockSize = blockSize;
this.offsetMask = blockSize - 1;
this.blockMask = Math.log2(blockSize);
// Allocating initial blocks
this.blocks = new Array(initialBlocks);
for (var i = 0; i < initialBlocks; i++)
this.blocks[i] = new this.ArrayClass(this.blockSize);
}
/**
* Method used to set a value.
*
* @param {number} index - Index to edit.
* @param {any} value - Value.
* @return {HashedArrayTree}
*/
HashedArrayTree.prototype.set = function(index, value) {
// Out of bounds?
if (this.length < index)
throw new Error('HashedArrayTree(' + this.ArrayClass.name + ').set: index out of bounds.');
var block = index >> this.blockMask,
i = index & this.offsetMask;
this.blocks[block][i] = value;
return this;
};
/**
* Method used to get a value.
*
* @param {number} index - Index to retrieve.
* @return {any}
*/
HashedArrayTree.prototype.get = function(index) {
if (this.length < index)
return;
var block = index >> this.blockMask,
i = index & this.offsetMask;
return this.blocks[block][i];
};
/**
* Method used to grow the array.
*
* @param {number} capacity - Optional capacity to accomodate.
* @return {HashedArrayTree}
*/
HashedArrayTree.prototype.grow = function(capacity) {
if (typeof capacity !== 'number')
capacity = this.capacity + this.blockSize;
if (this.capacity >= capacity)
return this;
while (this.capacity < capacity) {
this.blocks.push(new this.ArrayClass(this.blockSize));
this.capacity += this.blockSize;
}
return this;
};
/**
* Method used to resize the array. Won't deallocate.
*
* @param {number} length - Target length.
* @return {HashedArrayTree}
*/
HashedArrayTree.prototype.resize = function(length) {
if (length === this.length)
return this;
if (length < this.length) {
this.length = length;
return this;
}
this.length = length;
this.grow(length);
return this;
};
/**
* Method used to push a value into the array.
*
* @param {any} value - Value to push.
* @return {number} - Length of the array.
*/
HashedArrayTree.prototype.push = function(value) {
if (this.capacity === this.length)
this.grow();
var index = this.length;
var block = index >> this.blockMask,
i = index & this.offsetMask;
this.blocks[block][i] = value;
return ++this.length;
};
/**
* Method used to pop the last value of the array.
*
* @return {number} - The popped value.
*/
HashedArrayTree.prototype.pop = function() {
if (this.length === 0)
return;
var lastBlock = this.blocks[this.blocks.length - 1];
var i = (--this.length) & this.offsetMask;
return lastBlock[i];
};
/**
* Convenience known methods.
*/
HashedArrayTree.prototype.inspect = function() {
var proxy = new this.ArrayClass(this.length),
block;
for (var i = 0, l = this.length; i < l; i++) {
block = i >> this.blockMask;
proxy[i] = this.blocks[block][i & this.offsetMask];
}
proxy.type = this.ArrayClass.name;
proxy.items = this.length;
proxy.capacity = this.capacity;
proxy.blockSize = this.blockSize;
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: HashedArrayTree,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
HashedArrayTree.prototype[Symbol.for('nodejs.util.inspect.custom')] = HashedArrayTree.prototype.inspect;
/**
* Exporting.
*/
module.exports = HashedArrayTree;

View File

@@ -1,96 +0,0 @@
/**
* Mnemonist Heap Typings
* =======================
*/
type HeapComparator<T> = (a: T, b: T) => number;
export default class Heap<T> {
// Members
size: number;
// Constructor
constructor(comparator?: HeapComparator<T>);
// Methods
clear(): void;
push(item: T): number;
peek(): T | undefined;
pop(): T | undefined;
replace(item: T): T | undefined;
pushpop(item: T): T | undefined;
toArray(): Array<T>;
consume(): Array<T>;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
comparator?: HeapComparator<I>
): Heap<I>;
}
export class MinHeap<T> {
// Members
size: number;
// Constructor
constructor(comparator?: HeapComparator<T>);
// Methods
clear(): void;
push(item: T): number;
peek(): T | undefined;
pop(): T | undefined;
replace(item: T): T | undefined;
pushpop(item: T): T | undefined;
toArray(): Array<T>;
consume(): Array<T>;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
comparator?: HeapComparator<I>
): Heap<I>;
}
export class MaxHeap<T> {
// Members
size: number;
// Constructor
constructor(comparator?: HeapComparator<T>);
// Methods
clear(): void;
push(item: T): number;
peek(): T | undefined;
pop(): T | undefined;
replace(item: T): T | undefined;
pushpop(item: T): T | undefined;
toArray(): Array<T>;
consume(): Array<T>;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
comparator?: HeapComparator<I>
): Heap<I>;
}
// Static helpers
export function push<T>(comparator: HeapComparator<T>, heap: Array<T>, item: T): void;
export function pop<T>(comparator: HeapComparator<T>, heap: Array<T>): T;
export function replace<T>(comparator: HeapComparator<T>, heap: Array<T>, item: T): T;
export function pushpop<T>(comparator: HeapComparator<T>, heap: Array<T>, item: T): T;
export function heapify<T>(comparator: HeapComparator<T>, array: Array<T>): void;
export function consume<T>(comparator: HeapComparator<T>, heap: Array<T>): Array<T>;
export function nsmallest<T>(comparator: HeapComparator<T>, n: number, values: Iterable<T>): Array<T>;
export function nsmallest<T>(n: number, values: Iterable<T>): Array<T>;
export function nlargest<T>(comparator: HeapComparator<T>, n: number, values: Iterable<T>): Array<T>;
export function nlargest<T>(n: number, values: Iterable<T>): Array<T>;

View File

@@ -1,576 +0,0 @@
/**
* Mnemonist Binary Heap
* ======================
*
* Binary heap implementation.
*/
var forEach = require('obliterator/foreach'),
comparators = require('./utils/comparators.js'),
iterables = require('./utils/iterables.js');
var DEFAULT_COMPARATOR = comparators.DEFAULT_COMPARATOR,
reverseComparator = comparators.reverseComparator;
/**
* Heap helper functions.
*/
/**
* Function used to sift down.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @param {number} startIndex - Starting index.
* @param {number} i - Index.
*/
function siftDown(compare, heap, startIndex, i) {
var item = heap[i],
parentIndex,
parent;
while (i > startIndex) {
parentIndex = (i - 1) >> 1;
parent = heap[parentIndex];
if (compare(item, parent) < 0) {
heap[i] = parent;
i = parentIndex;
continue;
}
break;
}
heap[i] = item;
}
/**
* Function used to sift up.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @param {number} i - Index.
*/
function siftUp(compare, heap, i) {
var endIndex = heap.length,
startIndex = i,
item = heap[i],
childIndex = 2 * i + 1,
rightIndex;
while (childIndex < endIndex) {
rightIndex = childIndex + 1;
if (
rightIndex < endIndex &&
compare(heap[childIndex], heap[rightIndex]) >= 0
) {
childIndex = rightIndex;
}
heap[i] = heap[childIndex];
i = childIndex;
childIndex = 2 * i + 1;
}
heap[i] = item;
siftDown(compare, heap, startIndex, i);
}
/**
* Function used to push an item into a heap represented by a raw array.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @param {any} item - Item to push.
*/
function push(compare, heap, item) {
heap.push(item);
siftDown(compare, heap, 0, heap.length - 1);
}
/**
* Function used to pop an item from a heap represented by a raw array.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @return {any}
*/
function pop(compare, heap) {
var lastItem = heap.pop();
if (heap.length !== 0) {
var item = heap[0];
heap[0] = lastItem;
siftUp(compare, heap, 0);
return item;
}
return lastItem;
}
/**
* Function used to pop the heap then push a new value into it, thus "replacing"
* it.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @param {any} item - The item to push.
* @return {any}
*/
function replace(compare, heap, item) {
if (heap.length === 0)
throw new Error('mnemonist/heap.replace: cannot pop an empty heap.');
var popped = heap[0];
heap[0] = item;
siftUp(compare, heap, 0);
return popped;
}
/**
* Function used to push an item in the heap then pop the heap and return the
* popped value.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @param {any} item - The item to push.
* @return {any}
*/
function pushpop(compare, heap, item) {
var tmp;
if (heap.length !== 0 && compare(heap[0], item) < 0) {
tmp = heap[0];
heap[0] = item;
item = tmp;
siftUp(compare, heap, 0);
}
return item;
}
/**
* Converts and array into an abstract heap in linear time.
*
* @param {function} compare - Comparison function.
* @param {array} array - Target array.
*/
function heapify(compare, array) {
var n = array.length,
l = n >> 1,
i = l;
while (--i >= 0)
siftUp(compare, array, i);
}
/**
* Fully consumes the given heap.
*
* @param {function} compare - Comparison function.
* @param {array} heap - Array storing the heap's data.
* @return {array}
*/
function consume(compare, heap) {
var l = heap.length,
i = 0;
var array = new Array(l);
while (i < l)
array[i++] = pop(compare, heap);
return array;
}
/**
* Function used to retrieve the n smallest items from the given iterable.
*
* @param {function} compare - Comparison function.
* @param {number} n - Number of top items to retrieve.
* @param {any} iterable - Arbitrary iterable.
* @param {array}
*/
function nsmallest(compare, n, iterable) {
if (arguments.length === 2) {
iterable = n;
n = compare;
compare = DEFAULT_COMPARATOR;
}
var reverseCompare = reverseComparator(compare);
var i, l, v;
var min = Infinity;
var result;
// If n is equal to 1, it's just a matter of finding the minimum
if (n === 1) {
if (iterables.isArrayLike(iterable)) {
for (i = 0, l = iterable.length; i < l; i++) {
v = iterable[i];
if (min === Infinity || compare(v, min) < 0)
min = v;
}
result = new iterable.constructor(1);
result[0] = min;
return result;
}
forEach(iterable, function(value) {
if (min === Infinity || compare(value, min) < 0)
min = value;
});
return [min];
}
if (iterables.isArrayLike(iterable)) {
// If n > iterable length, we just clone and sort
if (n >= iterable.length)
return iterable.slice().sort(compare);
result = iterable.slice(0, n);
heapify(reverseCompare, result);
for (i = n, l = iterable.length; i < l; i++)
if (reverseCompare(iterable[i], result[0]) > 0)
replace(reverseCompare, result, iterable[i]);
// NOTE: if n is over some number, it becomes faster to consume the heap
return result.sort(compare);
}
// Correct for size
var size = iterables.guessLength(iterable);
if (size !== null && size < n)
n = size;
result = new Array(n);
i = 0;
forEach(iterable, function(value) {
if (i < n) {
result[i] = value;
}
else {
if (i === n)
heapify(reverseCompare, result);
if (reverseCompare(value, result[0]) > 0)
replace(reverseCompare, result, value);
}
i++;
});
if (result.length > i)
result.length = i;
// NOTE: if n is over some number, it becomes faster to consume the heap
return result.sort(compare);
}
/**
* Function used to retrieve the n largest items from the given iterable.
*
* @param {function} compare - Comparison function.
* @param {number} n - Number of top items to retrieve.
* @param {any} iterable - Arbitrary iterable.
* @param {array}
*/
function nlargest(compare, n, iterable) {
if (arguments.length === 2) {
iterable = n;
n = compare;
compare = DEFAULT_COMPARATOR;
}
var reverseCompare = reverseComparator(compare);
var i, l, v;
var max = -Infinity;
var result;
// If n is equal to 1, it's just a matter of finding the maximum
if (n === 1) {
if (iterables.isArrayLike(iterable)) {
for (i = 0, l = iterable.length; i < l; i++) {
v = iterable[i];
if (max === -Infinity || compare(v, max) > 0)
max = v;
}
result = new iterable.constructor(1);
result[0] = max;
return result;
}
forEach(iterable, function(value) {
if (max === -Infinity || compare(value, max) > 0)
max = value;
});
return [max];
}
if (iterables.isArrayLike(iterable)) {
// If n > iterable length, we just clone and sort
if (n >= iterable.length)
return iterable.slice().sort(reverseCompare);
result = iterable.slice(0, n);
heapify(compare, result);
for (i = n, l = iterable.length; i < l; i++)
if (compare(iterable[i], result[0]) > 0)
replace(compare, result, iterable[i]);
// NOTE: if n is over some number, it becomes faster to consume the heap
return result.sort(reverseCompare);
}
// Correct for size
var size = iterables.guessLength(iterable);
if (size !== null && size < n)
n = size;
result = new Array(n);
i = 0;
forEach(iterable, function(value) {
if (i < n) {
result[i] = value;
}
else {
if (i === n)
heapify(compare, result);
if (compare(value, result[0]) > 0)
replace(compare, result, value);
}
i++;
});
if (result.length > i)
result.length = i;
// NOTE: if n is over some number, it becomes faster to consume the heap
return result.sort(reverseCompare);
}
/**
* Binary Minimum Heap.
*
* @constructor
* @param {function} comparator - Comparator function to use.
*/
function Heap(comparator) {
this.clear();
this.comparator = comparator || DEFAULT_COMPARATOR;
if (typeof this.comparator !== 'function')
throw new Error('mnemonist/Heap.constructor: given comparator should be a function.');
}
/**
* Method used to clear the heap.
*
* @return {undefined}
*/
Heap.prototype.clear = function() {
// Properties
this.items = [];
this.size = 0;
};
/**
* Method used to push an item into the heap.
*
* @param {any} item - Item to push.
* @return {number}
*/
Heap.prototype.push = function(item) {
push(this.comparator, this.items, item);
return ++this.size;
};
/**
* Method used to retrieve the "first" item of the heap.
*
* @return {any}
*/
Heap.prototype.peek = function() {
return this.items[0];
};
/**
* Method used to retrieve & remove the "first" item of the heap.
*
* @return {any}
*/
Heap.prototype.pop = function() {
if (this.size !== 0)
this.size--;
return pop(this.comparator, this.items);
};
/**
* Method used to pop the heap, then push an item and return the popped
* item.
*
* @param {any} item - Item to push into the heap.
* @return {any}
*/
Heap.prototype.replace = function(item) {
return replace(this.comparator, this.items, item);
};
/**
* Method used to push the heap, the pop it and return the pooped item.
*
* @param {any} item - Item to push into the heap.
* @return {any}
*/
Heap.prototype.pushpop = function(item) {
return pushpop(this.comparator, this.items, item);
};
/**
* Method used to consume the heap fully and return its items as a sorted array.
*
* @return {array}
*/
Heap.prototype.consume = function() {
this.size = 0;
return consume(this.comparator, this.items);
};
/**
* Method used to convert the heap to an array. Note that it basically clone
* the heap and consumes it completely. This is hardly performant.
*
* @return {array}
*/
Heap.prototype.toArray = function() {
return consume(this.comparator, this.items.slice());
};
/**
* Convenience known methods.
*/
Heap.prototype.inspect = function() {
var proxy = this.toArray();
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: Heap,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
Heap.prototype[Symbol.for('nodejs.util.inspect.custom')] = Heap.prototype.inspect;
/**
* Binary Maximum Heap.
*
* @constructor
* @param {function} comparator - Comparator function to use.
*/
function MaxHeap(comparator) {
this.clear();
this.comparator = comparator || DEFAULT_COMPARATOR;
if (typeof this.comparator !== 'function')
throw new Error('mnemonist/MaxHeap.constructor: given comparator should be a function.');
this.comparator = reverseComparator(this.comparator);
}
MaxHeap.prototype = Heap.prototype;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a heap.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} comparator - Custom comparator function.
* @return {Heap}
*/
Heap.from = function(iterable, comparator) {
var heap = new Heap(comparator);
var items;
// If iterable is an array, we can be clever about it
if (iterables.isArrayLike(iterable))
items = iterable.slice();
else
items = iterables.toArray(iterable);
heapify(heap.comparator, items);
heap.items = items;
heap.size = items.length;
return heap;
};
MaxHeap.from = function(iterable, comparator) {
var heap = new MaxHeap(comparator);
var items;
// If iterable is an array, we can be clever about it
if (iterables.isArrayLike(iterable))
items = iterable.slice();
else
items = iterables.toArray(iterable);
heapify(heap.comparator, items);
heap.items = items;
heap.size = items.length;
return heap;
};
/**
* Exporting.
*/
Heap.siftUp = siftUp;
Heap.siftDown = siftDown;
Heap.push = push;
Heap.pop = pop;
Heap.replace = replace;
Heap.pushpop = pushpop;
Heap.heapify = heapify;
Heap.consume = consume;
Heap.nsmallest = nsmallest;
Heap.nlargest = nlargest;
Heap.MinHeap = Heap;
Heap.MaxHeap = MaxHeap;
module.exports = Heap;

View File

@@ -1,48 +0,0 @@
/**
* Mnemonist Typings
* ==================
*
* Gathering the library's typings.
*/
import * as set from './set';
export {default as BiMap, InverseMap} from './bi-map';
export {default as BitSet} from './bit-set';
export {default as BitVector} from './bit-vector';
export {default as BKTree} from './bk-tree';
export {default as BloomFilter} from './bloom-filter';
export {default as CircularBuffer} from './circular-buffer';
export {default as DefaultMap} from './default-map';
export {default as DefaultWeakMap} from './default-weak-map';
export {default as FixedDeque} from './fixed-deque';
export {default as FibonacciHeap, MinFibonacciHeap, MaxFibonacciHeap} from './fibonacci-heap';
export {default as FixedReverseHeap} from './fixed-reverse-heap';
export {default as FixedStack} from './fixed-stack';
export {default as FuzzyMap} from './fuzzy-map';
export {default as FuzzyMultiMap} from './fuzzy-multi-map';
export {default as HashedArrayTree} from './hashed-array-tree';
export {default as Heap, MinHeap, MaxHeap} from './heap';
export {default as InvertedIndex} from './inverted-index';
export {default as KDTree} from './kd-tree';
export {default as LinkedList} from './linked-list';
export {default as LRUCache} from './lru-cache';
export {default as LRUCacheWithDelete} from './lru-cache-with-delete';
export {default as LRUMap} from './lru-map';
export {default as LRUMapWithDelete} from './lru-map-with-delete';
export {default as MultiMap} from './multi-map';
export {default as MultiSet} from './multi-set';
export {default as PassjoinIndex} from './passjoin-index';
export {default as Queue} from './queue';
export {set};
export {default as SparseQueueSet} from './sparse-queue-set';
export {default as SparseMap} from './sparse-map';
export {default as SparseSet} from './sparse-set';
export {default as Stack} from './stack';
export {default as StaticDisjointSet} from './static-disjoint-set';
export {default as StaticIntervalTree} from './static-interval-tree';
export {default as SuffixArray, GeneralizedSuffixArray} from './suffix-array';
export {default as SymSpell} from './symspell';
export {default as Trie} from './trie';
export {default as TrieMap} from './trie-map';
export {default as Vector, Uint8Vector, Uint8ClampedVector, Int8Vector, Uint16Vector, Int16Vector, Uint32Vector, Int32Vector, Float32Vector, Float64Vector, PointerVector} from './vector';
export {default as VPTree} from './vp-tree';

View File

@@ -1,69 +0,0 @@
/**
* Mnemonist Library Endpoint (CommonJS)
* ======================================
*
* Exporting every data structure through a unified endpoint. Consumers
* of this library should prefer the modular access though.
*/
var Heap = require('./heap.js'),
FibonacciHeap = require('./fibonacci-heap.js'),
SuffixArray = require('./suffix-array.js'),
Vector = require('./vector.js');
module.exports = {
BiMap: require('./bi-map.js'),
BitSet: require('./bit-set.js'),
BitVector: require('./bit-vector.js'),
BloomFilter: require('./bloom-filter.js'),
BKTree: require('./bk-tree.js'),
CircularBuffer: require('./circular-buffer.js'),
DefaultMap: require('./default-map.js'),
DefaultWeakMap: require('./default-weak-map.js'),
FixedDeque: require('./fixed-deque.js'),
StaticDisjointSet: require('./static-disjoint-set.js'),
FibonacciHeap: FibonacciHeap,
MinFibonacciHeap: FibonacciHeap.MinFibonacciHeap,
MaxFibonacciHeap: FibonacciHeap.MaxFibonacciHeap,
FixedReverseHeap: require('./fixed-reverse-heap.js'),
FuzzyMap: require('./fuzzy-map.js'),
FuzzyMultiMap: require('./fuzzy-multi-map.js'),
HashedArrayTree: require('./hashed-array-tree.js'),
Heap: Heap,
MinHeap: Heap.MinHeap,
MaxHeap: Heap.MaxHeap,
StaticIntervalTree: require('./static-interval-tree.js'),
InvertedIndex: require('./inverted-index.js'),
KDTree: require('./kd-tree.js'),
LinkedList: require('./linked-list.js'),
LRUCache: require('./lru-cache.js'),
LRUCacheWithDelete: require('./lru-cache-with-delete.js'),
LRUMap: require('./lru-map.js'),
LRUMapWithDelete: require('./lru-map-with-delete.js'),
MultiMap: require('./multi-map.js'),
MultiSet: require('./multi-set.js'),
PassjoinIndex: require('./passjoin-index.js'),
Queue: require('./queue.js'),
FixedStack: require('./fixed-stack.js'),
Stack: require('./stack.js'),
SuffixArray: SuffixArray,
GeneralizedSuffixArray: SuffixArray.GeneralizedSuffixArray,
set: require('./set.js'),
SparseQueueSet: require('./sparse-queue-set.js'),
SparseMap: require('./sparse-map.js'),
SparseSet: require('./sparse-set.js'),
SymSpell: require('./symspell.js'),
Trie: require('./trie.js'),
TrieMap: require('./trie-map.js'),
Vector: Vector,
Uint8Vector: Vector.Uint8Vector,
Uint8ClampedVector: Vector.Uint8ClampedVector,
Int8Vector: Vector.Int8Vector,
Uint16Vector: Vector.Uint16Vector,
Int16Vector: Vector.Int16Vector,
Uint32Vector: Vector.Uint32Vector,
Int32Vector: Vector.Int32Vector,
Float32Vector: Vector.Float32Vector,
Float64Vector: Vector.Float64Vector,
PointerVector: Vector.PointerVector,
VPTree: require('./vp-tree.js')
};

View File

@@ -1,67 +0,0 @@
/**
* Mnemonist Library Endpoint (ESM)
* =================================
*
* Exporting every data structure through a unified endpoint.
*/
import * as set from './set.js';
import {default as FibonacciHeap} from './fibonacci-heap.js';
const MinFibonacciHeap = FibonacciHeap.MinFibonacciHeap;
const MaxFibonacciHeap = FibonacciHeap.MaxFibonacciHeap;
import {default as Heap} from './heap.js';
const MinHeap = Heap.MinHeap;
const MaxHeap = Heap.MaxHeap;
import {default as SuffixArray} from './suffix-array.js';
const GeneralizedSuffixArray = SuffixArray.GeneralizedSuffixArray;
import {default as Vector} from './vector.js';
const Uint8Vector = Vector.Uint8Vector;
const Uint8ClampedVector = Vector.Uint8ClampedVector;
const Int8Vector = Vector.Int8Vector;
const Uint16Vector = Vector.Uint16Vector;
const Int16Vector = Vector.Int16Vector;
const Uint32Vector = Vector.Uint32Vector;
const Int32Vector = Vector.Int32Vector;
const Float32Vector = Vector.Float32Vector;
const Float64Vector = Vector.Float64Vector;
const PointerVector = Vector.PointerVector;
export {default as BiMap} from './bi-map.js';
export {default as BitSet} from './bit-set.js';
export {default as BitVector} from './bit-vector.js';
export {default as BloomFilter} from './bloom-filter.js';
export {default as BKTree} from './bk-tree.js';
export {default as CircularBuffer} from './circular-buffer.js';
export {default as DefaultMap} from './default-map.js';
export {default as DefaultWeakMap} from './default-weak-map.js';
export {default as FixedDeque} from './fixed-deque.js';
export {default as StaticDisjointSet} from './static-disjoint-set.js';
export {FibonacciHeap, MinFibonacciHeap, MaxFibonacciHeap};
export {default as FixedReverseHeap} from './fixed-reverse-heap.js';
export {default as FuzzyMap} from './fuzzy-map.js';
export {default as FuzzyMultiMap} from './fuzzy-multi-map.js';
export {default as HashedArrayTree} from './hashed-array-tree.js';
export {Heap, MinHeap, MaxHeap};
export {default as StaticIntervalTree} from './static-interval-tree.js';
export {default as InvertedIndex} from './inverted-index.js';
export {default as KDTree} from './kd-tree.js';
export {default as LinkedList} from './linked-list.js';
export {default as LRUCache} from './lru-cache.js';
export {default as LRUCacheWithDelete} from './lru-cache-with-delete.js';
export {default as LRUMap} from './lru-map.js';
export {default as LRUMapWithDelete} from './lru-map-with-delete.js';
export {default as MultiMap} from './multi-map.js';
export {default as MultiSet} from './multi-set.js';
export {default as PassjoinIndex} from './passjoin-index.js';
export {default as Queue} from './queue.js';
export {default as FixedStack} from './fixed-stack.js';
export {default as Stack} from './stack.js';
export {SuffixArray, GeneralizedSuffixArray};
export {set};
export {default as SparseQueueSet} from './sparse-queue-set.js';
export {default as SparseMap} from './sparse-map.js';
export {default as SparseSet} from './sparse-set.js';
export {default as SymSpell} from './symspell.js';
export {default as Trie} from './trie.js';
export {default as TrieMap} from './trie-map.js';
export {Vector, Uint8Vector, Uint8ClampedVector, Int8Vector, Uint16Vector, Int16Vector, Uint32Vector, Int32Vector, Float32Vector, Float64Vector, PointerVector};
export {default as VPTree} from './vp-tree.js';

View File

@@ -1,33 +0,0 @@
/**
* Mnemonist InvertedIndex Typings
* ================================
*/
type Tokenizer = (key: any) => Array<string>;
type TokenizersTuple = [Tokenizer, Tokenizer];
export default class InvertedIndex<D> implements Iterable<D> {
// Members
dimension: number;
size: number;
// Constructor
constructor(tokenizer?: Tokenizer);
constructor(tokenizers?: TokenizersTuple);
// Methods
clear(): void;
add(document: D): this;
get(query: any): Array<D>;
forEach(callback: (document: D, index: number, invertedIndex: this) => void, scope?: any): void;
documents(): IterableIterator<D>;
tokens(): IterableIterator<string>;
[Symbol.iterator](): IterableIterator<D>;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
tokenizer?: Tokenizer | TokenizersTuple
): InvertedIndex<I>;
}

View File

@@ -1,249 +0,0 @@
/**
* Mnemonist Inverted Index
* =========================
*
* JavaScript implementation of an inverted index.
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach'),
helpers = require('./utils/merge.js');
function identity(x) {
return x;
}
/**
* InvertedIndex.
*
* @constructor
* @param {function} tokenizer - Tokenizer function.
*/
function InvertedIndex(descriptor) {
this.clear();
if (Array.isArray(descriptor)) {
this.documentTokenizer = descriptor[0];
this.queryTokenizer = descriptor[1];
}
else {
this.documentTokenizer = descriptor;
this.queryTokenizer = descriptor;
}
if (!this.documentTokenizer)
this.documentTokenizer = identity;
if (!this.queryTokenizer)
this.queryTokenizer = identity;
if (typeof this.documentTokenizer !== 'function')
throw new Error('mnemonist/InvertedIndex.constructor: document tokenizer is not a function.');
if (typeof this.queryTokenizer !== 'function')
throw new Error('mnemonist/InvertedIndex.constructor: query tokenizer is not a function.');
}
/**
* Method used to clear the InvertedIndex.
*
* @return {undefined}
*/
InvertedIndex.prototype.clear = function() {
// Properties
this.items = [];
this.mapping = new Map();
this.size = 0;
this.dimension = 0;
};
/**
* Method used to add a document to the index.
*
* @param {any} doc - Item to add.
* @return {InvertedIndex}
*/
InvertedIndex.prototype.add = function(doc) {
// Increasing size
this.size++;
// Storing document
var key = this.items.length;
this.items.push(doc);
// Tokenizing the document
var tokens = this.documentTokenizer(doc);
if (!Array.isArray(tokens))
throw new Error('mnemonist/InvertedIndex.add: tokenizer function should return an array of tokens.');
// Indexing
var done = new Set(),
token,
container;
for (var i = 0, l = tokens.length; i < l; i++) {
token = tokens[i];
if (done.has(token))
continue;
done.add(token);
container = this.mapping.get(token);
if (!container) {
container = [];
this.mapping.set(token, container);
}
container.push(key);
}
this.dimension = this.mapping.size;
return this;
};
/**
* Method used to query the index in a AND fashion.
*
* @param {any} query - Query
* @return {Set} - Intersection of documents matching the query.
*/
InvertedIndex.prototype.get = function(query) {
// Early termination
if (!this.size)
return [];
// First we need to tokenize the query
var tokens = this.queryTokenizer(query);
if (!Array.isArray(tokens))
throw new Error('mnemonist/InvertedIndex.query: tokenizer function should return an array of tokens.');
if (!tokens.length)
return [];
var results = this.mapping.get(tokens[0]),
c,
i,
l;
if (typeof results === 'undefined' || results.length === 0)
return [];
if (tokens.length > 1) {
for (i = 1, l = tokens.length; i < l; i++) {
c = this.mapping.get(tokens[i]);
if (typeof c === 'undefined' || c.length === 0)
return [];
results = helpers.intersectionUniqueArrays(results, c);
}
}
var docs = new Array(results.length);
for (i = 0, l = docs.length; i < l; i++)
docs[i] = this.items[results[i]];
return docs;
};
/**
* Method used to iterate over each of the documents.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
InvertedIndex.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
for (var i = 0, l = this.documents.length; i < l; i++)
callback.call(scope, this.documents[i], i, this);
};
/**
* Method returning an iterator over the index's documents.
*
* @return {Iterator}
*/
InvertedIndex.prototype.documents = function() {
var documents = this.items,
l = documents.length,
i = 0;
return new Iterator(function() {
if (i >= l)
return {
done: true
};
var value = documents[i++];
return {
value: value,
done: false
};
});
};
/**
* Method returning an iterator over the index's tokens.
*
* @return {Iterator}
*/
InvertedIndex.prototype.tokens = function() {
return this.mapping.keys();
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
InvertedIndex.prototype[Symbol.iterator] = InvertedIndex.prototype.documents;
/**
* Convenience known methods.
*/
InvertedIndex.prototype.inspect = function() {
var array = this.items.slice();
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: InvertedIndex,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
InvertedIndex.prototype[Symbol.for('nodejs.util.inspect.custom')] = InvertedIndex.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a InvertedIndex.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} tokenizer - Tokenizer function.
* @return {InvertedIndex}
*/
InvertedIndex.from = function(iterable, descriptor) {
var index = new InvertedIndex(descriptor);
forEach(iterable, function(doc) {
index.add(doc);
});
return index;
};
/**
* Exporting.
*/
module.exports = InvertedIndex;

View File

@@ -1,28 +0,0 @@
/**
* Mnemonist KDTree Typings
* =========================
*/
import {IArrayLike} from './utils/types';
export default class KDTree<V> {
// Members
dimensions: number;
size: number;
visited: number;
// Constructor
private constructor(dimensions: number, build: any);
// Methods
nearestNeighbor(point: Array<number>): V;
kNearestNeighbors(k: number, point: Array<number>): Array<V>;
linearKNearestNeighbors(k: number, point: Array<number>): Array<V>;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<[I, Array<number>]>, dimensions: number): KDTree<I>;
static fromAxes(axes: IArrayLike): KDTree<number>;
static fromAxes<I>(axes: IArrayLike, labels: Array<I>): KDTree<I>;
}

View File

@@ -1,447 +0,0 @@
/**
* Mnemonist KDTree
* =================
*
* Low-level JavaScript implementation of a k-dimensional tree.
*/
var iterables = require('./utils/iterables.js');
var typed = require('./utils/typed-arrays.js');
var createTupleComparator = require('./utils/comparators.js').createTupleComparator;
var FixedReverseHeap = require('./fixed-reverse-heap.js');
var inplaceQuickSortIndices = require('./sort/quick.js').inplaceQuickSortIndices;
/**
* Helper function used to compute the squared distance between a query point
* and an indexed points whose values are stored in a tree's axes.
*
* Note that squared distance is used instead of euclidean to avoid
* costly sqrt computations.
*
* @param {number} dimensions - Number of dimensions.
* @param {array} axes - Axes data.
* @param {number} pivot - Pivot.
* @param {array} point - Query point.
* @return {number}
*/
function squaredDistanceAxes(dimensions, axes, pivot, b) {
var d;
var dist = 0,
step;
for (d = 0; d < dimensions; d++) {
step = axes[d][pivot] - b[d];
dist += step * step;
}
return dist;
}
/**
* Helper function used to reshape input data into low-level axes data.
*
* @param {number} dimensions - Number of dimensions.
* @param {array} data - Data in the shape [label, [x, y, z...]]
* @return {object}
*/
function reshapeIntoAxes(dimensions, data) {
var l = data.length;
var axes = new Array(dimensions),
labels = new Array(l),
axis;
var PointerArray = typed.getPointerArray(l);
var ids = new PointerArray(l);
var d, i, row;
var f = true;
for (d = 0; d < dimensions; d++) {
axis = new Float64Array(l);
for (i = 0; i < l; i++) {
row = data[i];
axis[i] = row[1][d];
if (f) {
labels[i] = row[0];
ids[i] = i;
}
}
f = false;
axes[d] = axis;
}
return {axes: axes, ids: ids, labels: labels};
}
/**
* Helper function used to build a kd-tree from axes data.
*
* @param {number} dimensions - Number of dimensions.
* @param {array} axes - Axes.
* @param {array} ids - Indices to sort.
* @param {array} labels - Point labels.
* @return {object}
*/
function buildTree(dimensions, axes, ids, labels) {
var l = labels.length;
// NOTE: +1 because we need to keep 0 as null pointer
var PointerArray = typed.getPointerArray(l + 1);
// Building the tree
var pivots = new PointerArray(l),
lefts = new PointerArray(l),
rights = new PointerArray(l);
var stack = [[0, 0, ids.length, -1, 0]],
step,
parent,
direction,
median,
pivot,
lo,
hi;
var d, i = 0;
while (stack.length !== 0) {
step = stack.pop();
d = step[0];
lo = step[1];
hi = step[2];
parent = step[3];
direction = step[4];
inplaceQuickSortIndices(axes[d], ids, lo, hi);
l = hi - lo;
median = lo + (l >>> 1); // Fancy floor(l / 2)
pivot = ids[median];
pivots[i] = pivot;
if (parent > -1) {
if (direction === 0)
lefts[parent] = i + 1;
else
rights[parent] = i + 1;
}
d = (d + 1) % dimensions;
// Right
if (median !== lo && median !== hi - 1) {
stack.push([d, median + 1, hi, i, 1]);
}
// Left
if (median !== lo) {
stack.push([d, lo, median, i, 0]);
}
i++;
}
return {
axes: axes,
labels: labels,
pivots: pivots,
lefts: lefts,
rights: rights
};
}
/**
* KDTree.
*
* @constructor
*/
function KDTree(dimensions, build) {
this.dimensions = dimensions;
this.visited = 0;
this.axes = build.axes;
this.labels = build.labels;
this.pivots = build.pivots;
this.lefts = build.lefts;
this.rights = build.rights;
this.size = this.labels.length;
}
/**
* Method returning the query's nearest neighbor.
*
* @param {array} query - Query point.
* @return {any}
*/
KDTree.prototype.nearestNeighbor = function(query) {
var bestDistance = Infinity,
best = null;
var dimensions = this.dimensions,
axes = this.axes,
pivots = this.pivots,
lefts = this.lefts,
rights = this.rights;
var visited = 0;
function recurse(d, node) {
visited++;
var left = lefts[node],
right = rights[node],
pivot = pivots[node];
var dist = squaredDistanceAxes(
dimensions,
axes,
pivot,
query
);
if (dist < bestDistance) {
best = pivot;
bestDistance = dist;
if (dist === 0)
return;
}
var dx = axes[d][pivot] - query[d];
d = (d + 1) % dimensions;
// Going the correct way?
if (dx > 0) {
if (left !== 0)
recurse(d, left - 1);
}
else {
if (right !== 0)
recurse(d, right - 1);
}
// Going the other way?
if (dx * dx < bestDistance) {
if (dx > 0) {
if (right !== 0)
recurse(d, right - 1);
}
else {
if (left !== 0)
recurse(d, left - 1);
}
}
}
recurse(0, 0);
this.visited = visited;
return this.labels[best];
};
var KNN_HEAP_COMPARATOR_3 = createTupleComparator(3);
var KNN_HEAP_COMPARATOR_2 = createTupleComparator(2);
/**
* Method returning the query's k nearest neighbors.
*
* @param {number} k - Number of nearest neighbor to retrieve.
* @param {array} query - Query point.
* @return {array}
*/
// TODO: can do better by improving upon static-kdtree here
KDTree.prototype.kNearestNeighbors = function(k, query) {
if (k <= 0)
throw new Error('mnemonist/kd-tree.kNearestNeighbors: k should be a positive number.');
k = Math.min(k, this.size);
if (k === 1)
return [this.nearestNeighbor(query)];
var heap = new FixedReverseHeap(Array, KNN_HEAP_COMPARATOR_3, k);
var dimensions = this.dimensions,
axes = this.axes,
pivots = this.pivots,
lefts = this.lefts,
rights = this.rights;
var visited = 0;
function recurse(d, node) {
var left = lefts[node],
right = rights[node],
pivot = pivots[node];
var dist = squaredDistanceAxes(
dimensions,
axes,
pivot,
query
);
heap.push([dist, visited++, pivot]);
var point = query[d],
split = axes[d][pivot],
dx = point - split;
d = (d + 1) % dimensions;
// Going the correct way?
if (point < split) {
if (left !== 0) {
recurse(d, left - 1);
}
}
else {
if (right !== 0) {
recurse(d, right - 1);
}
}
// Going the other way?
if (dx * dx < heap.peek()[0] || heap.size < k) {
if (point < split) {
if (right !== 0) {
recurse(d, right - 1);
}
}
else {
if (left !== 0) {
recurse(d, left - 1);
}
}
}
}
recurse(0, 0);
this.visited = visited;
var best = heap.consume();
for (var i = 0; i < best.length; i++)
best[i] = this.labels[best[i][2]];
return best;
};
/**
* Method returning the query's k nearest neighbors by linear search.
*
* @param {number} k - Number of nearest neighbor to retrieve.
* @param {array} query - Query point.
* @return {array}
*/
KDTree.prototype.linearKNearestNeighbors = function(k, query) {
if (k <= 0)
throw new Error('mnemonist/kd-tree.kNearestNeighbors: k should be a positive number.');
k = Math.min(k, this.size);
var heap = new FixedReverseHeap(Array, KNN_HEAP_COMPARATOR_2, k);
var i, l, dist;
for (i = 0, l = this.size; i < l; i++) {
dist = squaredDistanceAxes(
this.dimensions,
this.axes,
this.pivots[i],
query
);
heap.push([dist, i]);
}
var best = heap.consume();
for (i = 0; i < best.length; i++)
best[i] = this.labels[this.pivots[best[i][1]]];
return best;
};
/**
* Convenience known methods.
*/
KDTree.prototype.inspect = function() {
var dummy = new Map();
dummy.dimensions = this.dimensions;
Object.defineProperty(dummy, 'constructor', {
value: KDTree,
enumerable: false
});
var i, j, point;
for (i = 0; i < this.size; i++) {
point = new Array(this.dimensions);
for (j = 0; j < this.dimensions; j++)
point[j] = this.axes[j][i];
dummy.set(this.labels[i], point);
}
return dummy;
};
if (typeof Symbol !== 'undefined')
KDTree.prototype[Symbol.for('nodejs.util.inspect.custom')] = KDTree.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {number} dimensions - Space dimensions.
* @return {KDTree}
*/
KDTree.from = function(iterable, dimensions) {
var data = iterables.toArray(iterable);
var reshaped = reshapeIntoAxes(dimensions, data);
var result = buildTree(dimensions, reshaped.axes, reshaped.ids, reshaped.labels);
return new KDTree(dimensions, result);
};
/**
* Static @.from function building a KDTree from given axes.
*
* @param {Iterable} iterable - Target iterable.
* @param {number} dimensions - Space dimensions.
* @return {KDTree}
*/
KDTree.fromAxes = function(axes, labels) {
if (!labels)
labels = typed.indices(axes[0].length);
var dimensions = axes.length;
var result = buildTree(axes.length, axes, typed.indices(labels.length), labels);
return new KDTree(dimensions, result);
};
/**
* Exporting.
*/
module.exports = KDTree;

View File

@@ -1,32 +0,0 @@
/**
* Mnemonist LinkedList Typings
* =============================
*/
export default class LinkedList<T> implements Iterable<T> {
// Members
size: number;
// Constructor
constructor();
// Methods
clear(): void;
first(): T | undefined;
last(): T | undefined;
peek(): T | undefined;
push(value: T): number;
shift(): T | undefined;
unshift(value: T): number;
forEach(callback: (value: T, index: number, list: this) => void, scope?: any): void;
toArray(): Array<T>;
values(): IterableIterator<T>;
entries(): IterableIterator<[number, T]>;
[Symbol.iterator](): IterableIterator<T>;
toString(): string;
toJSON(): Array<T>;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}): LinkedList<I>;
}

View File

@@ -1,261 +0,0 @@
/**
* Mnemonist Linked List
* ======================
*
* Singly linked list implementation. Uses raw JavaScript objects as nodes
* as benchmarks proved it was the fastest thing to do.
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach');
/**
* Linked List.
*
* @constructor
*/
function LinkedList() {
this.clear();
}
/**
* Method used to clear the list.
*
* @return {undefined}
*/
LinkedList.prototype.clear = function() {
// Properties
this.head = null;
this.tail = null;
this.size = 0;
};
/**
* Method used to get the first item of the list.
*
* @return {any}
*/
LinkedList.prototype.first = function() {
return this.head ? this.head.item : undefined;
};
LinkedList.prototype.peek = LinkedList.prototype.first;
/**
* Method used to get the last item of the list.
*
* @return {any}
*/
LinkedList.prototype.last = function() {
return this.tail ? this.tail.item : undefined;
};
/**
* Method used to add an item at the end of the list.
*
* @param {any} item - The item to add.
* @return {number}
*/
LinkedList.prototype.push = function(item) {
var node = {item: item, next: null};
if (!this.head) {
this.head = node;
this.tail = node;
}
else {
this.tail.next = node;
this.tail = node;
}
this.size++;
return this.size;
};
/**
* Method used to add an item at the beginning of the list.
*
* @param {any} item - The item to add.
* @return {number}
*/
LinkedList.prototype.unshift = function(item) {
var node = {item: item, next: null};
if (!this.head) {
this.head = node;
this.tail = node;
}
else {
if (!this.head.next)
this.tail = this.head;
node.next = this.head;
this.head = node;
}
this.size++;
return this.size;
};
/**
* Method used to retrieve & remove the first item of the list.
*
* @return {any}
*/
LinkedList.prototype.shift = function() {
if (!this.size)
return undefined;
var node = this.head;
this.head = node.next;
this.size--;
return node.item;
};
/**
* Method used to iterate over the list.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
LinkedList.prototype.forEach = function(callback, scope) {
if (!this.size)
return;
scope = arguments.length > 1 ? scope : this;
var n = this.head,
i = 0;
while (n) {
callback.call(scope, n.item, i, this);
n = n.next;
i++;
}
};
/**
* Method used to convert the list into an array.
*
* @return {array}
*/
LinkedList.prototype.toArray = function() {
if (!this.size)
return [];
var array = new Array(this.size);
for (var i = 0, l = this.size, n = this.head; i < l; i++) {
array[i] = n.item;
n = n.next;
}
return array;
};
/**
* Method used to create an iterator over a list's values.
*
* @return {Iterator}
*/
LinkedList.prototype.values = function() {
var n = this.head;
return new Iterator(function() {
if (!n)
return {
done: true
};
var value = n.item;
n = n.next;
return {
value: value,
done: false
};
});
};
/**
* Method used to create an iterator over a list's entries.
*
* @return {Iterator}
*/
LinkedList.prototype.entries = function() {
var n = this.head,
i = 0;
return new Iterator(function() {
if (!n)
return {
done: true
};
var value = n.item;
n = n.next;
i++;
return {
value: [i - 1, value],
done: false
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
LinkedList.prototype[Symbol.iterator] = LinkedList.prototype.values;
/**
* Convenience known methods.
*/
LinkedList.prototype.toString = function() {
return this.toArray().join(',');
};
LinkedList.prototype.toJSON = function() {
return this.toArray();
};
LinkedList.prototype.inspect = function() {
var array = this.toArray();
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: LinkedList,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
LinkedList.prototype[Symbol.for('nodejs.util.inspect.custom')] = LinkedList.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a list.
*
* @param {Iterable} iterable - Target iterable.
* @return {LinkedList}
*/
LinkedList.from = function(iterable) {
var list = new LinkedList();
forEach(iterable, function(value) {
list.push(value);
});
return list;
};
/**
* Exporting.
*/
module.exports = LinkedList;

View File

@@ -1,13 +0,0 @@
/**
* Mnemonist LRUCacheWithDelete Typings
* =====================================
*/
import LRUCache from './lru-cache';
export default class LRUCacheWithDelete<K, V> extends LRUCache<K, V> {
delete(key: K): boolean;
remove<T>(key: K, missing?: T): V | T;
}

View File

@@ -1,287 +0,0 @@
/**
* Mnemonist LRUCacheWithDelete
* =============================
*
* An extension of LRUCache with delete functionality.
*/
var LRUCache = require('./lru-cache.js'),
forEach = require('obliterator/foreach'),
typed = require('./utils/typed-arrays.js'),
iterables = require('./utils/iterables.js');
// The only complication with deleting items is that the LRU's
// performance depends on having a fixed-size list of pointers; the
// doubly-linked-list is happy to expand and contract.
//
// On delete, we record the position of the former item's pointer in a
// list of "holes" in the pointer array. On insert, if there is a hole
// the new pointer slots in to fill the hole; otherwise, it is
// appended as usual. (Note: we are only talking here about the
// internal pointer list. setting or getting an item promotes it
// to the top of the LRU ranking no matter what came before)
function LRUCacheWithDelete(Keys, Values, capacity) {
if (arguments.length < 2) {
LRUCache.call(this, Keys);
}
else {
LRUCache.call(this, Keys, Values, capacity);
}
var PointerArray = typed.getPointerArray(this.capacity);
this.deleted = new PointerArray(this.capacity);
this.deletedSize = 0;
}
for (var k in LRUCache.prototype)
LRUCacheWithDelete.prototype[k] = LRUCache.prototype[k];
if (typeof Symbol !== 'undefined')
LRUCacheWithDelete.prototype[Symbol.iterator] = LRUCache.prototype[Symbol.iterator];
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
LRUCacheWithDelete.prototype.clear = function() {
LRUCache.prototype.clear.call(this);
this.deletedSize = 0;
};
/**
* Method used to set the value for the given key in the cache.
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {undefined}
*/
LRUCacheWithDelete.prototype.set = function(key, value) {
var pointer = this.items[key];
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
this.V[pointer] = value;
return;
}
// The cache is not yet full
if (this.size < this.capacity) {
if (this.deletedSize > 0) {
// If there is a "hole" in the pointer list, reuse it
pointer = this.deleted[--this.deletedSize];
}
else {
// otherwise append to the pointer list
pointer = this.size;
}
this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
delete this.items[this.K[pointer]];
}
// Storing key & value
this.items[key] = pointer;
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
};
/**
* Method used to set the value for the given key in the cache
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {{evicted: boolean, key: any, value: any}} An object containing the
* key and value of an item that was overwritten or evicted in the set
* operation, as well as a boolean indicating whether it was evicted due to
* limited capacity. Return value is null if nothing was evicted or overwritten
* during the set operation.
*/
LRUCacheWithDelete.prototype.setpop = function(key, value) {
var oldValue = null;
var oldKey = null;
var pointer = this.items[key];
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
oldValue = this.V[pointer];
this.V[pointer] = value;
return {evicted: false, key: key, value: oldValue};
}
// The cache is not yet full
if (this.size < this.capacity) {
if (this.deletedSize > 0) {
// If there is a "hole" in the pointer list, reuse it
pointer = this.deleted[--this.deletedSize];
}
else {
// otherwise append to the pointer list
pointer = this.size;
}
this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
oldValue = this.V[pointer];
oldKey = this.K[pointer];
delete this.items[oldKey];
}
// Storing key & value
this.items[key] = pointer;
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
// Return object if eviction took place, otherwise return null
if (oldKey) {
return {evicted: true, key: oldKey, value: oldValue};
}
else {
return null;
}
};
/**
* Method used to delete the entry for the given key in the cache.
*
* @param {any} key - Key.
* @return {boolean} - true if the item was present
*/
LRUCacheWithDelete.prototype.delete = function(key) {
var pointer = this.items[key];
if (typeof pointer === 'undefined') {
return false;
}
delete this.items[key];
if (this.size === 1) {
this.size = 0;
this.head = 0;
this.tail = 0;
this.deletedSize = 0;
return true;
}
var previous = this.backward[pointer],
next = this.forward[pointer];
if (this.head === pointer) {
this.head = next;
}
if (this.tail === pointer) {
this.tail = previous;
}
this.forward[previous] = next;
this.backward[next] = previous;
this.size--;
this.deleted[this.deletedSize++] = pointer;
return true;
};
/**
* Method used to remove and return the value for the given key in the cache.
*
* @param {any} key - Key.
* @param {any} [missing=undefined] - Value to return if item is absent
* @return {any} The value, if present; the missing indicator if absent
*/
LRUCacheWithDelete.prototype.remove = function(key, missing = undefined) {
var pointer = this.items[key];
if (typeof pointer === 'undefined') {
return missing;
}
var dead = this.V[pointer];
delete this.items[key];
if (this.size === 1) {
this.size = 0;
this.head = 0;
this.tail = 0;
this.deletedSize = 0;
return dead;
}
var previous = this.backward[pointer],
next = this.forward[pointer];
if (this.head === pointer) {
this.head = next;
}
if (this.tail === pointer) {
this.tail = previous;
}
this.forward[previous] = next;
this.backward[next] = previous;
this.size--;
this.deleted[this.deletedSize++] = pointer;
return dead;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} Keys - Array class for storing keys.
* @param {function} Values - Array class for storing values.
* @param {number} capacity - Cache's capacity.
* @return {LRUCacheWithDelete}
*/
LRUCacheWithDelete.from = function(iterable, Keys, Values, capacity) {
if (arguments.length < 2) {
capacity = iterables.guessLength(iterable);
if (typeof capacity !== 'number')
throw new Error('mnemonist/lru-cache.from: could not guess iterable length. Please provide desired capacity as last argument.');
}
else if (arguments.length === 2) {
capacity = Keys;
Keys = null;
Values = null;
}
var cache = new LRUCacheWithDelete(Keys, Values, capacity);
forEach(iterable, function(value, key) {
cache.set(key, value);
});
return cache;
};
module.exports = LRUCacheWithDelete;

View File

@@ -1,43 +0,0 @@
/**
* Mnemonist LRUCache Typings
* ===========================
*/
import {IArrayLikeConstructor} from './utils/types';
export default class LRUCache<K, V> implements Iterable<[K, V]> {
// Members
capacity: number;
size: number;
// Constructor
constructor(capacity: number);
constructor(KeyArrayClass: IArrayLikeConstructor, ValueArrayClass: IArrayLikeConstructor, capacity: number);
// Methods
clear(): void;
set(key: K, value: V): this;
setpop(key: K, value: V): {evicted: boolean, key: K, value: V};
get(key: K): V | undefined;
peek(key: K): V | undefined;
has(key: K): boolean;
forEach(callback: (value: V, key: K, cache: this) => void, scope?: any): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
inspect(): any;
// Statics
static from<I, J>(
iterable: Iterable<[I, J]> | {[key: string]: J},
KeyArrayClass: IArrayLikeConstructor,
ValueArrayClass: IArrayLikeConstructor,
capacity?: number
): LRUCache<I, J>;
static from<I, J>(
iterable: Iterable<[I, J]> | {[key: string]: J},
capacity?: number
): LRUCache<I, J>;
}

View File

@@ -1,436 +0,0 @@
/**
* Mnemonist LRUCache
* ===================
*
* JavaScript implementation of the LRU Cache data structure. To save up
* memory and allocations this implementation represents its underlying
* doubly-linked list as static arrays and pointers. Thus, memory is allocated
* only once at instantiation and JS objects are never created to serve as
* pointers. This also means this implementation does not trigger too many
* garbage collections.
*
* Note that to save up memory, a LRU Cache can be implemented using a singly
* linked list by storing predecessors' pointers as hashmap values.
* However, this means more hashmap lookups and would probably slow the whole
* thing down. What's more, pointers are not the things taking most space in
* memory.
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach'),
typed = require('./utils/typed-arrays.js'),
iterables = require('./utils/iterables.js');
/**
* LRUCache.
*
* @constructor
* @param {function} Keys - Array class for storing keys.
* @param {function} Values - Array class for storing values.
* @param {number} capacity - Desired capacity.
*/
function LRUCache(Keys, Values, capacity) {
if (arguments.length < 2) {
capacity = Keys;
Keys = null;
Values = null;
}
this.capacity = capacity;
if (typeof this.capacity !== 'number' || this.capacity <= 0)
throw new Error('mnemonist/lru-cache: capacity should be positive number.');
else if (!isFinite(this.capacity) || Math.floor(this.capacity) !== this.capacity)
throw new Error('mnemonist/lru-cache: capacity should be a finite positive integer.');
var PointerArray = typed.getPointerArray(capacity);
this.forward = new PointerArray(capacity);
this.backward = new PointerArray(capacity);
this.K = typeof Keys === 'function' ? new Keys(capacity) : new Array(capacity);
this.V = typeof Values === 'function' ? new Values(capacity) : new Array(capacity);
// Properties
this.size = 0;
this.head = 0;
this.tail = 0;
this.items = {};
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
LRUCache.prototype.clear = function() {
this.size = 0;
this.head = 0;
this.tail = 0;
this.items = {};
};
/**
* Method used to splay a value on top.
*
* @param {number} pointer - Pointer of the value to splay on top.
* @return {LRUCache}
*/
LRUCache.prototype.splayOnTop = function(pointer) {
var oldHead = this.head;
if (this.head === pointer)
return this;
var previous = this.backward[pointer],
next = this.forward[pointer];
if (this.tail === pointer) {
this.tail = previous;
}
else {
this.backward[next] = previous;
}
this.forward[previous] = next;
this.backward[oldHead] = pointer;
this.head = pointer;
this.forward[pointer] = oldHead;
return this;
};
/**
* Method used to set the value for the given key in the cache.
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {undefined}
*/
LRUCache.prototype.set = function(key, value) {
var pointer = this.items[key];
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
this.V[pointer] = value;
return;
}
// The cache is not yet full
if (this.size < this.capacity) {
pointer = this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
delete this.items[this.K[pointer]];
}
// Storing key & value
this.items[key] = pointer;
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
};
/**
* Method used to set the value for the given key in the cache
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {{evicted: boolean, key: any, value: any}} An object containing the
* key and value of an item that was overwritten or evicted in the set
* operation, as well as a boolean indicating whether it was evicted due to
* limited capacity. Return value is null if nothing was evicted or overwritten
* during the set operation.
*/
LRUCache.prototype.setpop = function(key, value) {
var oldValue = null;
var oldKey = null;
var pointer = this.items[key];
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
oldValue = this.V[pointer];
this.V[pointer] = value;
return {evicted: false, key: key, value: oldValue};
}
// The cache is not yet full
if (this.size < this.capacity) {
pointer = this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
oldValue = this.V[pointer];
oldKey = this.K[pointer];
delete this.items[oldKey];
}
// Storing key & value
this.items[key] = pointer;
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
// Return object if eviction took place, otherwise return null
if (oldKey) {
return {evicted: true, key: oldKey, value: oldValue};
}
else {
return null;
}
};
/**
* Method used to check whether the key exists in the cache.
*
* @param {any} key - Key.
* @return {boolean}
*/
LRUCache.prototype.has = function(key) {
return key in this.items;
};
/**
* Method used to get the value attached to the given key. Will move the
* related key to the front of the underlying linked list.
*
* @param {any} key - Key.
* @return {any}
*/
LRUCache.prototype.get = function(key) {
var pointer = this.items[key];
if (typeof pointer === 'undefined')
return;
this.splayOnTop(pointer);
return this.V[pointer];
};
/**
* Method used to get the value attached to the given key. Does not modify
* the ordering of the underlying linked list.
*
* @param {any} key - Key.
* @return {any}
*/
LRUCache.prototype.peek = function(key) {
var pointer = this.items[key];
if (typeof pointer === 'undefined')
return;
return this.V[pointer];
};
/**
* Method used to iterate over the cache's entries using a callback.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
LRUCache.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
var i = 0,
l = this.size;
var pointer = this.head,
keys = this.K,
values = this.V,
forward = this.forward;
while (i < l) {
callback.call(scope, values[pointer], keys[pointer], this);
pointer = forward[pointer];
i++;
}
};
/**
* Method used to create an iterator over the cache's keys from most
* recently used to least recently used.
*
* @return {Iterator}
*/
LRUCache.prototype.keys = function() {
var i = 0,
l = this.size;
var pointer = this.head,
keys = this.K,
forward = this.forward;
return new Iterator(function() {
if (i >= l)
return {done: true};
var key = keys[pointer];
i++;
if (i < l)
pointer = forward[pointer];
return {
done: false,
value: key
};
});
};
/**
* Method used to create an iterator over the cache's values from most
* recently used to least recently used.
*
* @return {Iterator}
*/
LRUCache.prototype.values = function() {
var i = 0,
l = this.size;
var pointer = this.head,
values = this.V,
forward = this.forward;
return new Iterator(function() {
if (i >= l)
return {done: true};
var value = values[pointer];
i++;
if (i < l)
pointer = forward[pointer];
return {
done: false,
value: value
};
});
};
/**
* Method used to create an iterator over the cache's entries from most
* recently used to least recently used.
*
* @return {Iterator}
*/
LRUCache.prototype.entries = function() {
var i = 0,
l = this.size;
var pointer = this.head,
keys = this.K,
values = this.V,
forward = this.forward;
return new Iterator(function() {
if (i >= l)
return {done: true};
var key = keys[pointer],
value = values[pointer];
i++;
if (i < l)
pointer = forward[pointer];
return {
done: false,
value: [key, value]
};
});
};
/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
LRUCache.prototype[Symbol.iterator] = LRUCache.prototype.entries;
/**
* Convenience known methods.
*/
LRUCache.prototype.inspect = function() {
var proxy = new Map();
var iterator = this.entries(),
step;
while ((step = iterator.next(), !step.done))
proxy.set(step.value[0], step.value[1]);
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: LRUCache,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
LRUCache.prototype[Symbol.for('nodejs.util.inspect.custom')] = LRUCache.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} Keys - Array class for storing keys.
* @param {function} Values - Array class for storing values.
* @param {number} capacity - Cache's capacity.
* @return {LRUCache}
*/
LRUCache.from = function(iterable, Keys, Values, capacity) {
if (arguments.length < 2) {
capacity = iterables.guessLength(iterable);
if (typeof capacity !== 'number')
throw new Error('mnemonist/lru-cache.from: could not guess iterable length. Please provide desired capacity as last argument.');
}
else if (arguments.length === 2) {
capacity = Keys;
Keys = null;
Values = null;
}
var cache = new LRUCache(Keys, Values, capacity);
forEach(iterable, function(value, key) {
cache.set(key, value);
});
return cache;
};
/**
* Exporting.
*/
module.exports = LRUCache;

View File

@@ -1,13 +0,0 @@
/**
* Mnemonist LRUMapWithDelete Typings
* ===================================
*/
import LRUMap from './lru-map';
export default class LRUMapWithDelete<K, V> extends LRUMap<K, V> {
delete(key: K): boolean;
remove<T>(key: K, missing?: T): V | T;
}

View File

@@ -1,287 +0,0 @@
/**
* Mnemonist LRUMapWithDelete
* ===========================
*
* An extension of LRUMap with delete functionality.
*/
var LRUMap = require('./lru-map.js'),
forEach = require('obliterator/foreach'),
typed = require('./utils/typed-arrays.js'),
iterables = require('./utils/iterables.js');
// The only complication with deleting items is that the LRU's
// performance depends on having a fixed-size list of pointers; the
// doubly-linked-list is happy to expand and contract.
//
// On delete, we record the position of the former item's pointer in a
// list of "holes" in the pointer array. On insert, if there is a hole
// the new pointer slots in to fill the hole; otherwise, it is
// appended as usual. (Note: we are only talking here about the
// internal pointer list. setting or getting an item promotes it
// to the top of the LRU ranking no matter what came before)
function LRUMapWithDelete(Keys, Values, capacity) {
if (arguments.length < 2) {
LRUMap.call(this, Keys);
}
else {
LRUMap.call(this, Keys, Values, capacity);
}
var PointerArray = typed.getPointerArray(this.capacity);
this.deleted = new PointerArray(this.capacity);
this.deletedSize = 0;
}
for (var k in LRUMap.prototype)
LRUMapWithDelete.prototype[k] = LRUMap.prototype[k];
if (typeof Symbol !== 'undefined')
LRUMapWithDelete.prototype[Symbol.iterator] = LRUMap.prototype[Symbol.iterator];
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
LRUMapWithDelete.prototype.clear = function() {
LRUMap.prototype.clear.call(this);
this.deletedSize = 0;
};
/**
* Method used to set the value for the given key in the cache.
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {undefined}
*/
LRUMapWithDelete.prototype.set = function(key, value) {
var pointer = this.items.get(key);
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
this.V[pointer] = value;
return;
}
// The cache is not yet full
if (this.size < this.capacity) {
if (this.deletedSize > 0) {
// If there is a "hole" in the pointer list, reuse it
pointer = this.deleted[--this.deletedSize];
}
else {
// otherwise append to the pointer list
pointer = this.size;
}
this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
this.items.delete(this.K[pointer]);
}
// Storing key & value
this.items.set(key, pointer);
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
};
/**
* Method used to set the value for the given key in the cache
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {{evicted: boolean, key: any, value: any}} An object containing the
* key and value of an item that was overwritten or evicted in the set
* operation, as well as a boolean indicating whether it was evicted due to
* limited capacity. Return value is null if nothing was evicted or overwritten
* during the set operation.
*/
LRUMapWithDelete.prototype.setpop = function(key, value) {
var oldValue = null;
var oldKey = null;
var pointer = this.items.get(key);
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
oldValue = this.V[pointer];
this.V[pointer] = value;
return {evicted: false, key: key, value: oldValue};
}
// The cache is not yet full
if (this.size < this.capacity) {
if (this.deletedSize > 0) {
// If there is a "hole" in the pointer list, reuse it
pointer = this.deleted[--this.deletedSize];
}
else {
// otherwise append to the pointer list
pointer = this.size;
}
this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
oldValue = this.V[pointer];
oldKey = this.K[pointer];
this.items.delete(oldKey);
}
// Storing key & value
this.items.set(key, pointer);
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
// Return object if eviction took place, otherwise return null
if (oldKey) {
return {evicted: true, key: oldKey, value: oldValue};
}
else {
return null;
}
};
/**
* Method used to delete the entry for the given key in the cache.
*
* @param {any} key - Key.
* @return {boolean} - true if the item was present
*/
LRUMapWithDelete.prototype.delete = function(key) {
var pointer = this.items.get(key);
if (typeof pointer === 'undefined') {
return false;
}
this.items.delete(key);
if (this.size === 1) {
this.size = 0;
this.head = 0;
this.tail = 0;
this.deletedSize = 0;
return true;
}
var previous = this.backward[pointer],
next = this.forward[pointer];
if (this.head === pointer) {
this.head = next;
}
if (this.tail === pointer) {
this.tail = previous;
}
this.forward[previous] = next;
this.backward[next] = previous;
this.size--;
this.deleted[this.deletedSize++] = pointer;
return true;
};
/**
* Method used to remove and return the value for the given key in the cache.
*
* @param {any} key - Key.
* @param {any} [missing=undefined] - Value to return if item is absent
* @return {any} The value, if present; the missing indicator if absent
*/
LRUMapWithDelete.prototype.remove = function(key, missing = undefined) {
var pointer = this.items.get(key);
if (typeof pointer === 'undefined') {
return missing;
}
var dead = this.V[pointer];
this.items.delete(key);
if (this.size === 1) {
this.size = 0;
this.head = 0;
this.tail = 0;
this.deletedSize = 0;
return dead;
}
var previous = this.backward[pointer],
next = this.forward[pointer];
if (this.head === pointer) {
this.head = next;
}
if (this.tail === pointer) {
this.tail = previous;
}
this.forward[previous] = next;
this.backward[next] = previous;
this.size--;
this.deleted[this.deletedSize++] = pointer;
return dead;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} Keys - Array class for storing keys.
* @param {function} Values - Array class for storing values.
* @param {number} capacity - Cache's capacity.
* @return {LRUMapWithDelete}
*/
LRUMapWithDelete.from = function(iterable, Keys, Values, capacity) {
if (arguments.length < 2) {
capacity = iterables.guessLength(iterable);
if (typeof capacity !== 'number')
throw new Error('mnemonist/lru-map.from: could not guess iterable length. Please provide desired capacity as last argument.');
}
else if (arguments.length === 2) {
capacity = Keys;
Keys = null;
Values = null;
}
var cache = new LRUMapWithDelete(Keys, Values, capacity);
forEach(iterable, function(value, key) {
cache.set(key, value);
});
return cache;
};
module.exports = LRUMapWithDelete;

View File

@@ -1,43 +0,0 @@
/**
* Mnemonist LRUMap Typings
* =========================
*/
import {IArrayLikeConstructor} from './utils/types';
export default class LRUMap<K, V> implements Iterable<[K, V]> {
// Members
capacity: number;
size: number;
// Constructor
constructor(capacity: number);
constructor(KeyArrayClass: IArrayLikeConstructor, ValueArrayClass: IArrayLikeConstructor, capacity: number);
// Methods
clear(): void;
set(key: K, value: V): this;
setpop(key: K, value: V): {evicted: boolean, key: K, value: V};
get(key: K): V | undefined;
peek(key: K): V | undefined;
has(key: K): boolean;
forEach(callback: (value: V, key: K, cache: this) => void, scope?: any): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
inspect(): any;
// Statics
static from<I, J>(
iterable: Iterable<[I, J]> | {[key: string]: J},
KeyArrayClass: IArrayLikeConstructor,
ValueArrayClass: IArrayLikeConstructor,
capacity?: number
): LRUMap<I, J>;
static from<I, J>(
iterable: Iterable<[I, J]> | {[key: string]: J},
capacity?: number
): LRUMap<I, J>;
}

View File

@@ -1,261 +0,0 @@
/**
* Mnemonist LRUMap
* =================
*
* Variant of the LRUCache class that leverages an ES6 Map instead of an object.
* It might be faster for some use case but it is still hard to understand
* when a Map can outperform an object in v8.
*/
var LRUCache = require('./lru-cache.js'),
forEach = require('obliterator/foreach'),
typed = require('./utils/typed-arrays.js'),
iterables = require('./utils/iterables.js');
/**
* LRUMap.
*
* @constructor
* @param {function} Keys - Array class for storing keys.
* @param {function} Values - Array class for storing values.
* @param {number} capacity - Desired capacity.
*/
function LRUMap(Keys, Values, capacity) {
if (arguments.length < 2) {
capacity = Keys;
Keys = null;
Values = null;
}
this.capacity = capacity;
if (typeof this.capacity !== 'number' || this.capacity <= 0)
throw new Error('mnemonist/lru-map: capacity should be positive number.');
else if (!isFinite(this.capacity) || Math.floor(this.capacity) !== this.capacity)
throw new Error('mnemonist/lru-map: capacity should be a finite positive integer.');
var PointerArray = typed.getPointerArray(capacity);
this.forward = new PointerArray(capacity);
this.backward = new PointerArray(capacity);
this.K = typeof Keys === 'function' ? new Keys(capacity) : new Array(capacity);
this.V = typeof Values === 'function' ? new Values(capacity) : new Array(capacity);
// Properties
this.size = 0;
this.head = 0;
this.tail = 0;
this.items = new Map();
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
LRUMap.prototype.clear = function() {
this.size = 0;
this.head = 0;
this.tail = 0;
this.items.clear();
};
/**
* Method used to set the value for the given key in the cache.
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {undefined}
*/
LRUMap.prototype.set = function(key, value) {
var pointer = this.items.get(key);
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
this.V[pointer] = value;
return;
}
// The cache is not yet full
if (this.size < this.capacity) {
pointer = this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
this.items.delete(this.K[pointer]);
}
// Storing key & value
this.items.set(key, pointer);
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
};
/**
* Method used to set the value for the given key in the cache.
*
* @param {any} key - Key.
* @param {any} value - Value.
* @return {{evicted: boolean, key: any, value: any}} An object containing the
* key and value of an item that was overwritten or evicted in the set
* operation, as well as a boolean indicating whether it was evicted due to
* limited capacity. Return value is null if nothing was evicted or overwritten
* during the set operation.
*/
LRUMap.prototype.setpop = function(key, value) {
var oldValue = null;
var oldKey = null;
var pointer = this.items.get(key);
// The key already exists, we just need to update the value and splay on top
if (typeof pointer !== 'undefined') {
this.splayOnTop(pointer);
oldValue = this.V[pointer];
this.V[pointer] = value;
return {evicted: false, key: key, value: oldValue};
}
// The cache is not yet full
if (this.size < this.capacity) {
pointer = this.size++;
}
// Cache is full, we need to drop the last value
else {
pointer = this.tail;
this.tail = this.backward[pointer];
oldValue = this.V[pointer];
oldKey = this.K[pointer];
this.items.delete(oldKey);
}
// Storing key & value
this.items.set(key, pointer);
this.K[pointer] = key;
this.V[pointer] = value;
// Moving the item at the front of the list
this.forward[pointer] = this.head;
this.backward[this.head] = pointer;
this.head = pointer;
// Return object if eviction took place, otherwise return null
if (oldKey) {
return {evicted: true, key: oldKey, value: oldValue};
}
else {
return null;
}
};
/**
* Method used to check whether the key exists in the cache.
*
* @param {any} key - Key.
* @return {boolean}
*/
LRUMap.prototype.has = function(key) {
return this.items.has(key);
};
/**
* Method used to get the value attached to the given key. Will move the
* related key to the front of the underlying linked list.
*
* @param {any} key - Key.
* @return {any}
*/
LRUMap.prototype.get = function(key) {
var pointer = this.items.get(key);
if (typeof pointer === 'undefined')
return;
this.splayOnTop(pointer);
return this.V[pointer];
};
/**
* Method used to get the value attached to the given key. Does not modify
* the ordering of the underlying linked list.
*
* @param {any} key - Key.
* @return {any}
*/
LRUMap.prototype.peek = function(key) {
var pointer = this.items.get(key);
if (typeof pointer === 'undefined')
return;
return this.V[pointer];
};
/**
* Methods that can be reused as-is from LRUCache.
*/
LRUMap.prototype.splayOnTop = LRUCache.prototype.splayOnTop;
LRUMap.prototype.forEach = LRUCache.prototype.forEach;
LRUMap.prototype.keys = LRUCache.prototype.keys;
LRUMap.prototype.values = LRUCache.prototype.values;
LRUMap.prototype.entries = LRUCache.prototype.entries;
/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
LRUMap.prototype[Symbol.iterator] = LRUMap.prototype.entries;
/**
* Convenience known methods.
*/
LRUMap.prototype.inspect = LRUCache.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {function} Keys - Array class for storing keys.
* @param {function} Values - Array class for storing values.
* @param {number} capacity - Cache's capacity.
* @return {LRUMap}
*/
LRUMap.from = function(iterable, Keys, Values, capacity) {
if (arguments.length < 2) {
capacity = iterables.guessLength(iterable);
if (typeof capacity !== 'number')
throw new Error('mnemonist/lru-cache.from: could not guess iterable length. Please provide desired capacity as last argument.');
}
else if (arguments.length === 2) {
capacity = Keys;
Keys = null;
Values = null;
}
var cache = new LRUMap(Keys, Values, capacity);
forEach(iterable, function(value, key) {
cache.set(key, value);
});
return cache;
};
/**
* Exporting.
*/
module.exports = LRUMap;

View File

@@ -1,447 +0,0 @@
/**
* Mnemonist MultiArray
* =====================
*
* Memory-efficient representation of an array of arrays. In JavaScript and
* most high-level languages, creating objects has a cost. This implementation
* is therefore able to represent nested containers without needing to create
* objects. This works by storing singly linked lists in a single flat array.
* However, this means that this structure comes with some read/write
* overhead but consume very few memory.
*
* This structure should be particularly suited to indices that will need to
* merge arrays anyway when queried and that are quite heavily hit (such as
* an inverted index or a quad tree).
*
* Note: the implementation does not require to keep track of head pointers
* but this comes with some advantages such as not needing to offset pointers
* by 1 and being able to perform in-order iteration. This remains quite lean
* in memory and does not hinder performance whatsoever.
*/
var typed = require('./utils/typed-arrays.js'),
Vector = require('./vector.js'),
Iterator = require('obliterator/iterator');
var PointerVector = Vector.PointerVector;
/**
* MultiArray.
*
* @constructor
*/
function MultiArray(Container, capacity) {
this.capacity = capacity || null;
this.Container = Container || Array;
this.hasFixedCapacity = this.capacity !== null;
if (typeof this.Container !== 'function')
throw new Error('mnemonist/multi-array.constructor: container should be a function.');
this.clear();
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
MultiArray.prototype.clear = function() {
// Properties
this.size = 0;
this.dimension = 0;
// NOTE: #.heads, #.tails & #.lengths have a length equal to the dimension of
// the array, while #.pointers has a length equal to its size.
// Storage
if (this.hasFixedCapacity) {
var capacity = this.capacity;
var PointerArray = typed.getPointerArray(capacity);
var policy = function(currentCapacity) {
var newCapacity = Math.max(1, Math.ceil(currentCapacity * 1.5));
// Clamping max allocation
return Math.min(newCapacity, capacity);
};
var initialCapacity = Math.max(8, capacity);
this.tails = new Vector(PointerArray, {policy: policy, initialCapacity: initialCapacity});
this.lengths = new Vector(PointerArray, {policy: policy, initialCapacity: initialCapacity});
this.pointers = new PointerArray(capacity);
this.items = new this.Container(capacity);
}
else {
this.tails = new PointerVector();
this.lengths = new PointerVector();
this.pointers = new PointerVector();
this.items = new this.Container();
}
};
/**
* Method used to add an item to the container at the given index.
*
* @param {number} index - Index of the container.
* @param {any} item - Item to add.
* @return {MultiArray}
*/
MultiArray.prototype.set = function(index, item) {
var pointer = this.size;
// TODO: this can be factorized!
if (this.hasFixedCapacity) {
if (index >= this.capacity || this.size === this.capacity)
throw new Error('mnemonist/multi-array: attempting to allocate further than capacity.');
// This linked list does not exist yet. Let's create it
if (index >= this.dimension) {
// We may be required to grow the vectors
this.dimension = index + 1;
this.tails.grow(this.dimension);
this.lengths.grow(this.dimension);
this.tails.resize(this.dimension);
this.lengths.resize(this.dimension);
this.lengths.array[index] = 1;
}
// Appending to the list
else {
this.pointers[pointer] = this.tails.array[index];
this.lengths.array[index]++;
}
this.tails.array[index] = pointer;
this.items[pointer] = item;
}
else {
// This linked list does not exist yet. Let's create it
if (index >= this.dimension) {
// We may be required to grow the vectors
this.dimension = index + 1;
this.tails.grow(this.dimension);
this.lengths.grow(this.dimension);
this.tails.resize(this.dimension);
this.lengths.resize(this.dimension);
this.pointers.push(0);
this.lengths.array[index] = 1;
}
// Appending to the list
else {
this.pointers.push(this.tails.array[index]);
this.lengths.array[index]++;
}
this.tails.array[index] = pointer;
this.items.push(item);
}
this.size++;
return this;
};
/**
* Method used to push a new container holding the given value.
* Note: it might be useful to make this function able to take an iterable
* or variadic someday. For the time being it's just a convenience for
* implementing compact multi maps and such.
*
* @param {any} item - Item to add.
* @return {MultiArray}
*/
MultiArray.prototype.push = function(item) {
var pointer = this.size,
index = this.dimension;
if (this.hasFixedCapacity) {
if (index >= this.capacity || this.size === this.capacity)
throw new Error('mnemonist/multi-array: attempting to allocate further than capacity.');
this.items[pointer] = item;
}
else {
this.items.push(item);
this.pointers.push(0);
}
this.lengths.push(1);
this.tails.push(pointer);
this.dimension++;
this.size++;
return this;
};
/**
* Method used to get the desired container.
*
* @param {number} index - Index of the container.
* @return {array}
*/
MultiArray.prototype.get = function(index) {
if (index >= this.dimension)
return;
var pointers = this.hasFixedCapacity ? this.pointers : this.pointers.array;
var pointer = this.tails.array[index],
length = this.lengths.array[index],
i = length;
var array = new this.Container(length);
while (i !== 0) {
array[--i] = this.items[pointer];
pointer = pointers[pointer];
}
return array;
};
/**
* Method used to check if a container exists at the given index.
*
* @param {number} index - Index of the container.
* @return {boolean}
*/
MultiArray.prototype.has = function(index) {
return index < this.dimension;
};
/**
* Method used to get the size of the container stored at given index.
*
* @param {number} index - Index of the container.
* @return {number}
*/
MultiArray.prototype.multiplicity = function(index) {
if (index >= this.dimension)
return 0;
return this.lengths.array[index];
};
MultiArray.prototype.count = MultiArray.prototype.multiplicity;
/**
* Method used to iterate over the structure's containers.
*
* @return {Iterator}
*/
MultiArray.prototype.containers = function() {
var self = this,
l = this.dimension,
i = 0;
return new Iterator(function() {
if (i >= l)
return {done: true};
return {value: self.get(i++)};
});
};
/**
* Method used to iterate over the structure's associations.
*
* @return {Iterator}
*/
MultiArray.prototype.associations = function() {
var self = this,
l = this.dimension,
i = 0;
return new Iterator(function() {
if (i >= l)
return {done: true};
var data = {value: [i, self.get(i)]};
i++;
return data;
});
};
/**
* Method used to iterate over the structure's values in the global insertion
* order.
*
* @param {number} [index] - Optionally, iterate over the values of a single
* container at index.
* @return {Iterator}
*/
MultiArray.prototype.values = function(index) {
var items = this.items,
length,
i = 0;
if (typeof index === 'number') {
if (index >= this.dimension)
return Iterator.empty();
length = this.lengths.array[index];
items = this.items;
var pointers = this.hasFixedCapacity ? this.pointers : this.pointers.array;
if (length === 0)
return Iterator.empty();
var pointer = this.tails.array[index],
v;
return new Iterator(function() {
if (i === length)
return {done: true};
i++;
v = items[pointer];
pointer = pointers[pointer];
return {done: false, value: v};
});
}
length = this.size;
return new Iterator(function() {
if (i >= length)
return {done: true};
return {done: false, value: items[i++]};
});
};
/**
* Method used to iterate over the structure's entries.
*
* @return {Iterator}
*/
MultiArray.prototype.entries = function() {
if (this.size === 0)
return Iterator.empty();
var inContainer = false,
pointer,
length,
i = 0,
j = 0,
l = this.dimension,
v;
var pointers = this.hasFixedCapacity ? this.pointers : this.pointers.array,
items = this.items,
tails = this.tails.array,
lengths = this.lengths.array;
var iterator = new Iterator(function next() {
if (!inContainer) {
if (i >= l)
return {done: true};
length = lengths[i];
pointer = tails[i];
i++;
if (length === 0)
return next();
j = 0;
inContainer = true;
}
if (j === length) {
inContainer = false;
return next();
}
v = items[pointer];
// TODO: guard for out-of-bounds
pointer = pointers[pointer];
j++;
return {
done: false,
value: [i - 1, v]
};
});
return iterator;
};
/**
* Method used to iterate over the structure's keys.
*
* @return {Iterator}
*/
MultiArray.prototype.keys = function() {
var i = 0,
l = this.dimension;
return new Iterator(function() {
if (i >= l)
return {done: true};
return {done: false, value: i++};
});
};
/**
* Convenience known methods.
*/
MultiArray.prototype.inspect = function() {
var proxy = new Array(this.dimension),
i,
l;
for (i = 0, l = this.dimension; i < l; i++)
proxy[i] = Array.from(this.get(i));
if (this.hasFixedCapacity) {
proxy.type = this.Container.name;
proxy.capacity = this.capacity;
}
proxy.size = this.size;
proxy.dimension = this.dimension;
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: MultiArray,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
MultiArray.prototype[Symbol.for('nodejs.util.inspect.custom')] = MultiArray.prototype.inspect;
// TODO: .from
/**
* Exporting.
*/
module.exports = MultiArray;

View File

@@ -1,47 +0,0 @@
/**
* Mnemonist MultiMap Typings
* ===========================
*/
interface MultiMap<K, V, C extends V[] | Set<V> = V[]> extends Iterable<[K, V]> {
// Members
dimension: number;
size: number;
// Methods
clear(): void;
set(key: K, value: V): this;
delete(key: K): boolean;
remove(key: K, value: V): boolean;
has(key: K): boolean;
get(key: K): C | undefined;
multiplicity(key: K): number;
forEach(callback: (value: V, key: K, map: this) => void, scope?: any): void;
forEachAssociation(callback: (value: C, key: K, map: this) => void, scope?: any): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
containers(): IterableIterator<C>;
associations(): IterableIterator<[K, C]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
inspect(): any;
toJSON(): any;
}
interface MultiMapConstructor {
new <K, V>(container: SetConstructor): MultiMap<K, V, Set<V>>;
new <K, V>(container?: ArrayConstructor): MultiMap<K, V, V[]>;
from<K, V>(
iterable: Iterable<[K, V]> | {[key: string]: V},
Container: SetConstructor
): MultiMap<K, V, Set<V>>;
from<K, V>(
iterable: Iterable<[K, V]> | {[key: string]: V},
Container?: ArrayConstructor
): MultiMap<K, V, V[]>;
}
declare const MultiMap: MultiMapConstructor;
export default MultiMap;

View File

@@ -1,408 +0,0 @@
/**
* Mnemonist MultiMap
* ===================
*
* Implementation of a MultiMap with custom container.
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach');
/**
* MultiMap.
*
* @constructor
*/
function MultiMap(Container) {
this.Container = Container || Array;
this.items = new Map();
this.clear();
Object.defineProperty(this.items, 'constructor', {
value: MultiMap,
enumerable: false
});
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
MultiMap.prototype.clear = function() {
// Properties
this.size = 0;
this.dimension = 0;
this.items.clear();
};
/**
* Method used to set a value.
*
* @param {any} key - Key.
* @param {any} value - Value to add.
* @return {MultiMap}
*/
MultiMap.prototype.set = function(key, value) {
var container = this.items.get(key),
sizeBefore;
if (!container) {
this.dimension++;
container = new this.Container();
this.items.set(key, container);
}
if (this.Container === Set) {
sizeBefore = container.size;
container.add(value);
if (sizeBefore < container.size)
this.size++;
}
else {
container.push(value);
this.size++;
}
return this;
};
/**
* Method used to delete the given key.
*
* @param {any} key - Key to delete.
* @return {boolean}
*/
MultiMap.prototype.delete = function(key) {
var container = this.items.get(key);
if (!container)
return false;
this.size -= (this.Container === Set ? container.size : container.length);
this.dimension--;
this.items.delete(key);
return true;
};
/**
* Method used to delete the remove an item in the container stored at the
* given key.
*
* @param {any} key - Key to delete.
* @return {boolean}
*/
MultiMap.prototype.remove = function(key, value) {
var container = this.items.get(key),
wasDeleted,
index;
if (!container)
return false;
if (this.Container === Set) {
wasDeleted = container.delete(value);
if (wasDeleted)
this.size--;
if (container.size === 0) {
this.items.delete(key);
this.dimension--;
}
return wasDeleted;
}
else {
index = container.indexOf(value);
if (index === -1)
return false;
this.size--;
if (container.length === 1) {
this.items.delete(key);
this.dimension--;
return true;
}
container.splice(index, 1);
return true;
}
};
/**
* Method used to return whether the given keys exists in the map.
*
* @param {any} key - Key to check.
* @return {boolean}
*/
MultiMap.prototype.has = function(key) {
return this.items.has(key);
};
/**
* Method used to return the container stored at the given key or `undefined`.
*
* @param {any} key - Key to get.
* @return {boolean}
*/
MultiMap.prototype.get = function(key) {
return this.items.get(key);
};
/**
* Method used to return the multiplicity of the given key, meaning the number
* of times it is set, or, more trivially, the size of the attached container.
*
* @param {any} key - Key to check.
* @return {number}
*/
MultiMap.prototype.multiplicity = function(key) {
var container = this.items.get(key);
if (typeof container === 'undefined')
return 0;
return this.Container === Set ? container.size : container.length;
};
MultiMap.prototype.count = MultiMap.prototype.multiplicity;
/**
* Method used to iterate over each of the key/value pairs.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
MultiMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
// Inner iteration function is created here to avoid creating it in the loop
var key;
function inner(value) {
callback.call(scope, value, key);
}
this.items.forEach(function(container, k) {
key = k;
container.forEach(inner);
});
};
/**
* Method used to iterate over each of the associations.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
MultiMap.prototype.forEachAssociation = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
this.items.forEach(callback, scope);
};
/**
* Method returning an iterator over the map's keys.
*
* @return {Iterator}
*/
MultiMap.prototype.keys = function() {
return this.items.keys();
};
/**
* Method returning an iterator over the map's keys.
*
* @return {Iterator}
*/
MultiMap.prototype.values = function() {
var iterator = this.items.values(),
inContainer = false,
countainer,
step,
i,
l;
if (this.Container === Set)
return new Iterator(function next() {
if (!inContainer) {
step = iterator.next();
if (step.done)
return {done: true};
inContainer = true;
countainer = step.value.values();
}
step = countainer.next();
if (step.done) {
inContainer = false;
return next();
}
return {
done: false,
value: step.value
};
});
return new Iterator(function next() {
if (!inContainer) {
step = iterator.next();
if (step.done)
return {done: true};
inContainer = true;
countainer = step.value;
i = 0;
l = countainer.length;
}
if (i >= l) {
inContainer = false;
return next();
}
return {
done: false,
value: countainer[i++]
};
});
};
/**
* Method returning an iterator over the map's entries.
*
* @return {Iterator}
*/
MultiMap.prototype.entries = function() {
var iterator = this.items.entries(),
inContainer = false,
countainer,
step,
key,
i,
l;
if (this.Container === Set)
return new Iterator(function next() {
if (!inContainer) {
step = iterator.next();
if (step.done)
return {done: true};
inContainer = true;
key = step.value[0];
countainer = step.value[1].values();
}
step = countainer.next();
if (step.done) {
inContainer = false;
return next();
}
return {
done: false,
value: [key, step.value]
};
});
return new Iterator(function next() {
if (!inContainer) {
step = iterator.next();
if (step.done)
return {done: true};
inContainer = true;
key = step.value[0];
countainer = step.value[1];
i = 0;
l = countainer.length;
}
if (i >= l) {
inContainer = false;
return next();
}
return {
done: false,
value: [key, countainer[i++]]
};
});
};
/**
* Method returning an iterator over the map's containers.
*
* @return {Iterator}
*/
MultiMap.prototype.containers = function() {
return this.items.values();
};
/**
* Method returning an iterator over the map's associations.
*
* @return {Iterator}
*/
MultiMap.prototype.associations = function() {
return this.items.entries();
};
/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
MultiMap.prototype[Symbol.iterator] = MultiMap.prototype.entries;
/**
* Convenience known methods.
*/
MultiMap.prototype.inspect = function() {
return this.items;
};
if (typeof Symbol !== 'undefined')
MultiMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = MultiMap.prototype.inspect;
MultiMap.prototype.toJSON = function() {
return this.items;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @param {Class} Container - Container.
* @return {MultiMap}
*/
MultiMap.from = function(iterable, Container) {
var map = new MultiMap(Container);
forEach(iterable, function(value, key) {
map.set(key, value);
});
return map;
};
/**
* Exporting.
*/
module.exports = MultiMap;

View File

@@ -1,40 +0,0 @@
/**
* Mnemonist MultiSet Typings
* ===========================
*/
export default class MultiSet<K> implements Iterable<K> {
// Members
dimension: number;
size: number;
// Constructor
constructor();
// Methods
clear(): void;
add(key: K, count?: number): this;
set(key: K, count: number): this;
has(key: K): boolean;
delete(key: K): boolean;
remove(key: K, count?: number): void;
edit(a: K, b: K): this;
multiplicity(key: K): number;
count(key: K): number;
get(key: K): number;
frequency(key: K): number;
top(n: number): Array<[K, number]>;
forEach(callback: (value: K, key: K, set: this) => void, scope?: any): void;
forEachMultiplicity(callback: (value: number, key: K, set: this) => void, scope?: any): void;
keys(): IterableIterator<K>;
values(): IterableIterator<K>;
multiplicities(): IterableIterator<[K, number]>;
[Symbol.iterator](): IterableIterator<K>;
inspect(): any;
toJSON(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}): MultiSet<I>;
static isSubset<T>(a: MultiSet<T>, b: MultiSet<T>): boolean;
static isSuperset<T>(a: MultiSet<T>, b: MultiSet<T>): boolean;
}

View File

@@ -1,445 +0,0 @@
/**
* Mnemonist MultiSet
* ====================
*
* JavaScript implementation of a MultiSet.
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach'),
FixedReverseHeap = require('./fixed-reverse-heap.js');
/**
* Helpers.
*/
var MULTISET_ITEM_COMPARATOR = function(a, b) {
if (a[1] > b[1])
return -1;
if (a[1] < b[1])
return 1;
return 0;
};
// TODO: helper functions: union, intersection, sum, difference, subtract
/**
* MultiSet.
*
* @constructor
*/
function MultiSet() {
this.items = new Map();
Object.defineProperty(this.items, 'constructor', {
value: MultiSet,
enumerable: false
});
this.clear();
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
MultiSet.prototype.clear = function() {
// Properties
this.size = 0;
this.dimension = 0;
this.items.clear();
};
/**
* Method used to add an item to the set.
*
* @param {any} item - Item to add.
* @param {number} count - Optional count.
* @return {MultiSet}
*/
MultiSet.prototype.add = function(item, count) {
if (count === 0)
return this;
if (count < 0)
return this.remove(item, -count);
count = count || 1;
if (typeof count !== 'number')
throw new Error('mnemonist/multi-set.add: given count should be a number.');
this.size += count;
const currentCount = this.items.get(item);
if (currentCount === undefined)
this.dimension++;
else
count += currentCount;
this.items.set(item, count);
return this;
};
/**
* Method used to set the multiplicity of an item in the set.
*
* @param {any} item - Target item.
* @param {number} count - Desired multiplicity.
* @return {MultiSet}
*/
MultiSet.prototype.set = function(item, count) {
var currentCount;
if (typeof count !== 'number')
throw new Error('mnemonist/multi-set.set: given count should be a number.');
// Setting an item to 0 or to a negative number means deleting it from the set
if (count <= 0) {
currentCount = this.items.get(item);
if (typeof currentCount !== 'undefined') {
this.size -= currentCount;
this.dimension--;
}
this.items.delete(item);
return this;
}
count = count || 1;
currentCount = this.items.get(item);
if (typeof currentCount === 'number') {
this.items.set(item, currentCount + count);
}
else {
this.dimension++;
this.items.set(item, count);
}
this.size += count;
return this;
};
/**
* Method used to return whether the item exists in the set.
*
* @param {any} item - Item to check.
* @return {boolan}
*/
MultiSet.prototype.has = function(item) {
return this.items.has(item);
};
/**
* Method used to delete an item from the set.
*
* @param {any} item - Item to delete.
* @return {boolan}
*/
MultiSet.prototype.delete = function(item) {
var count = this.items.get(item);
if (count === 0)
return false;
this.size -= count;
this.dimension--;
this.items.delete(item);
return true;
};
/**
* Method used to remove an item from the set.
*
* @param {any} item - Item to delete.
* @param {number} count - Optional count.
* @return {undefined}
*/
MultiSet.prototype.remove = function(item, count) {
if (count === 0)
return;
if (count < 0)
return this.add(item, -count);
count = count || 1;
if (typeof count !== 'number')
throw new Error('mnemonist/multi-set.remove: given count should be a number.');
var currentCount = this.items.get(item);
if (typeof currentCount === 'undefined') return;
var newCount = Math.max(0, currentCount - count);
if (newCount === 0) {
this.items.delete(item);
this.size -= currentCount;
this.dimension--;
}
else {
this.items.set(item, newCount);
this.size -= count;
}
return;
};
/**
* Method used to change a key into another one, merging counts if the target
* key already exists.
*
* @param {any} a - From key.
* @param {any} b - To key.
* @return {MultiSet}
*/
MultiSet.prototype.edit = function(a, b) {
var am = this.multiplicity(a);
// If a does not exist in the set, we can stop right there
if (am === 0)
return;
var bm = this.multiplicity(b);
this.items.set(b, am + bm);
this.items.delete(a);
return this;
};
/**
* Method used to return the multiplicity of the given item.
*
* @param {any} item - Item to get.
* @return {number}
*/
MultiSet.prototype.multiplicity = function(item) {
var count = this.items.get(item);
if (typeof count === 'undefined')
return 0;
return count;
};
MultiSet.prototype.get = MultiSet.prototype.multiplicity;
MultiSet.prototype.count = MultiSet.prototype.multiplicity;
/**
* Method used to return the frequency of the given item in the set.
*
* @param {any} item - Item to get.
* @return {number}
*/
MultiSet.prototype.frequency = function(item) {
if (this.size === 0)
return 0;
var count = this.multiplicity(item);
return count / this.size;
};
/**
* Method used to return the n most common items from the set.
*
* @param {number} n - Number of items to retrieve.
* @return {array}
*/
MultiSet.prototype.top = function(n) {
if (typeof n !== 'number' || n <= 0)
throw new Error('mnemonist/multi-set.top: n must be a number > 0.');
var heap = new FixedReverseHeap(Array, MULTISET_ITEM_COMPARATOR, n);
var iterator = this.items.entries(),
step;
while ((step = iterator.next(), !step.done))
heap.push(step.value);
return heap.consume();
};
/**
* Method used to iterate over the set's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
MultiSet.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
var i;
this.items.forEach(function(multiplicity, value) {
for (i = 0; i < multiplicity; i++)
callback.call(scope, value, value);
});
};
/**
* Method used to iterate over the set's multiplicities.
*
* @param {function} callback - Function to call for each multiplicity.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
MultiSet.prototype.forEachMultiplicity = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
this.items.forEach(callback, scope);
};
/**
* Method returning an iterator over the set's keys. I.e. its unique values,
* in a sense.
*
* @return {Iterator}
*/
MultiSet.prototype.keys = function() {
return this.items.keys();
};
/**
* Method returning an iterator over the set's values.
*
* @return {Iterator}
*/
MultiSet.prototype.values = function() {
var iterator = this.items.entries(),
inContainer = false,
step,
value,
multiplicity,
i;
return new Iterator(function next() {
if (!inContainer) {
step = iterator.next();
if (step.done)
return {done: true};
inContainer = true;
value = step.value[0];
multiplicity = step.value[1];
i = 0;
}
if (i >= multiplicity) {
inContainer = false;
return next();
}
i++;
return {
done: false,
value: value
};
});
};
/**
* Method returning an iterator over the set's multiplicities.
*
* @return {Iterator}
*/
MultiSet.prototype.multiplicities = function() {
return this.items.entries();
};
/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
MultiSet.prototype[Symbol.iterator] = MultiSet.prototype.values;
/**
* Convenience known methods.
*/
MultiSet.prototype.inspect = function() {
return this.items;
};
if (typeof Symbol !== 'undefined')
MultiSet.prototype[Symbol.for('nodejs.util.inspect.custom')] = MultiSet.prototype.inspect;
MultiSet.prototype.toJSON = function() {
return this.items;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @return {MultiSet}
*/
MultiSet.from = function(iterable) {
var set = new MultiSet();
forEach(iterable, function(value) {
set.add(value);
});
return set;
};
/**
* Function returning whether the multiset A is a subset of the multiset B.
*
* @param {MultiSet} A - First set.
* @param {MultiSet} B - Second set.
* @return {boolean}
*/
MultiSet.isSubset = function(A, B) {
var iterator = A.multiplicities(),
step,
key,
mA;
// Shortcuts
if (A === B)
return true;
if (A.dimension > B.dimension)
return false;
while ((step = iterator.next(), !step.done)) {
key = step.value[0];
mA = step.value[1];
if (B.multiplicity(key) < mA)
return false;
}
return true;
};
/**
* Function returning whether the multiset A is a superset of the multiset B.
*
* @param {MultiSet} A - First set.
* @param {MultiSet} B - Second set.
* @return {boolean}
*/
MultiSet.isSuperset = function(A, B) {
return MultiSet.isSubset(B, A);
};
/**
* Exporting.
*/
module.exports = MultiSet;

View File

@@ -1,122 +0,0 @@
{
"name": "mnemonist",
"version": "0.40.3",
"description": "Curated collection of data structures for the JavaScript/TypeScript.",
"scripts": {
"lint": "eslint --cache --ext .js,.mjs ./*.js ./*.mjs ./utils ./test",
"lint:fix": "eslint --cache --fix --ext .js,.mjs ./*.js ./*.mjs ./utils ./test",
"prepublishOnly": "npm run lint && npm test && npm run test:exports",
"test": "mocha",
"test:exports": "cd test/exports && npm i && npm run test"
},
"main": "./index.js",
"module": "./index.mjs",
"types": "./index.d.ts",
"exports": {
".": {
"types": "./index.d.ts",
"require": "./index.js",
"import": "./index.mjs"
},
"./*": {
"types": "./*.d.ts",
"require": "./*.js"
},
"./*.js": {
"types": "./*.d.ts",
"require": "./*.js"
}
},
"sideEffects": false,
"files": [
"sort",
"utils",
"*.d.ts",
"*.js",
"*.mjs"
],
"repository": {
"type": "git",
"url": "git+https://github.com/yomguithereal/mnemonist.git"
},
"keywords": [
"bag",
"bimap",
"bit array",
"bit set",
"bit vector",
"bitset",
"bk tree",
"burkhard-keller tree",
"cache",
"circular buffer",
"counter",
"data structures",
"default map",
"deque",
"disjoint set",
"fibonacci heap",
"fuzzy map",
"hashed array tree",
"heap",
"interval tree",
"inverted index",
"kd tree",
"linked list",
"lru",
"lru cache",
"multimap",
"multiset",
"passjoin",
"queue",
"sparse map",
"sparse set",
"stack",
"structures",
"suffix tree",
"symspell",
"trie",
"union find",
"vantage point tree",
"vector",
"vp tree"
],
"author": {
"name": "Guillaume Plique",
"url": "http://github.com/Yomguithereal"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/yomguithereal/mnemonist/issues"
},
"homepage": "https://github.com/yomguithereal/mnemonist#readme",
"dependencies": {
"obliterator": "^2.0.4"
},
"devDependencies": {
"@yomguithereal/eslint-config": "^4.4.0",
"asciitree": "^1.0.2",
"damerau-levenshtein": "^1.0.8",
"eslint": "^8.2.0",
"leven": "^3.1.0",
"lodash": "^4.17.21",
"matcha": "^0.7.0",
"mocha": "^9.1.3",
"pandemonium": "^2.0.0",
"seedrandom": "^3.0.5",
"static-kdtree": "^1.0.2"
},
"eslintConfig": {
"extends": "@yomguithereal/eslint-config",
"parserOptions": {
"ecmaVersion": 6,
"ecmaFeatures": {
"forOf": true
},
"sourceType": "module"
},
"rules": {
"no-new": 0
}
}
}

View File

@@ -1,54 +0,0 @@
/**
* Mnemonist PassjoinIndex Typings
* ================================
*/
type LevenshteinDistanceFunction<T> = (a: T, b: T) => number;
export default class PassjoinIndex<T> implements Iterable<T> {
// Members
size: number;
// Constructor
constructor(levenshtein: LevenshteinDistanceFunction<T>, k: number);
// Methods
add(value: T): this;
search(query: T): Set<T>;
clear(): void;
forEach(callback: (value: T, index: number, self: this) => void, scope?: any): void;
values(): IterableIterator<T>;
[Symbol.iterator](): IterableIterator<T>;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
levenshtein: LevenshteinDistanceFunction<I>,
k: number
): PassjoinIndex<I>;
}
export function countKeys(k: number, s: number): number;
export function comparator<T>(a: T, b: T): number;
export function partition(k: number, l: number): Array<[number, number]>;
export function segments<T>(k: number, string: T): Array<T>;
export function segmentPos<T>(k: number, i: number, string: T): number;
export function multiMatchAwareInterval(
k: number,
delta: number,
i: number,
s: number,
pi: number,
li: number
): [number, number];
export function multiMatchAwareSubstrings<T>(
k: number,
string: T,
l: number,
i: number,
pi: number,
li: number
): Array<T>;

View File

@@ -1,518 +0,0 @@
/**
* Mnemonist PassjoinIndex
* ========================
*
* The PassjoinIndex is an index leveraging the "passjoin" algorithm as a mean
* to index strings for Levenshtein distance queries. It features a complexity
* related to the Levenshtein query threshold k rather than the number of
* strings to test (roughly O(k^3)).
*
* [References]:
* Jiang, Yu, Dong Deng, Jiannan Wang, Guoliang Li, et Jianhua Feng.
* « Efficient Parallel Partition-Based Algorithms for Similarity Search and Join
* with Edit Distance Constraints ». In Proceedings of the Joint EDBT/ICDT 2013
* Workshops on - EDBT 13, 341. Genoa, Italy: ACM Press, 2013.
* https://doi.org/10.1145/2457317.2457382.
*
* Li, Guoliang, Dong Deng, et Jianhua Feng. « A Partition-Based Method for
* String Similarity Joins with Edit-Distance Constraints ». ACM Transactions on
* Database Systems 38, no 2 (1 juin 2013): 133.
* https://doi.org/10.1145/2487259.2487261.
*
* [Urls]:
* http://people.csail.mit.edu/dongdeng/projects/passjoin/index.html
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach');
// TODO: leveraging BagDistance as an upper bound of Levenshtein
// TODO: leverage n-grams recursive indexing
// TODO: try the MultiArray as a memory backend
// TODO: what about damerau levenshtein
/**
* Helpers.
*/
/**
* Function returning the number of substrings that will be selected by the
* multi-match-aware selection scheme for theshold `k`, for a string of length
* `s` to match strings of length `l`.
*
* @param {number} k - Levenshtein distance threshold.
* @param {number} s - Length of target strings.
* @param {number} l - Length of strings to match.
* @returns {number} - The number of selected substrings.
*/
function countSubstringsL(k, s, l) {
return (((Math.pow(k, 2) - Math.pow(Math.abs(s - l), 2)) / 2) | 0) + k + 1;
}
/**
* Function returning the minimum number of substrings that will be selected by
* the multi-match-aware selection scheme for theshold `k`, for a string of
* length `s` to match any string of relevant length.
*
* @param {number} k - Levenshtein distance threshold.
* @param {number} s - Length of target strings.
* @returns {number} - The number of selected substrings.
*/
function countKeys(k, s) {
var c = 0;
for (var l = 0, m = s + 1; l < m; l++)
c += countSubstringsL(k, s, l);
return c;
}
/**
* Function used to compare two keys in order to sort them first by decreasing
* length and then alphabetically as per the "4.2 Effective Indexing Strategy"
* point of the paper.
*
* @param {number} k - Levenshtein distance threshold.
* @param {number} s - Length of target strings.
* @returns {number} - The number of selected substrings.
*/
function comparator(a, b) {
if (a.length > b.length)
return -1;
if (a.length < b.length)
return 1;
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
/**
* Function partitioning a string into k + 1 uneven segments, the shorter
* ones, then the longer ones.
*
* @param {number} k - Levenshtein distance threshold.
* @param {number} l - Length of the string.
* @returns {Array} - The partition tuples (start, length).
*/
function partition(k, l) {
var m = k + 1,
a = (l / m) | 0,
b = a + 1,
i,
j;
var largeSegments = l - a * m,
smallSegments = m - largeSegments;
var tuples = new Array(k + 1);
for (i = 0; i < smallSegments; i++)
tuples[i] = [i * a, a];
var offset = (i - 1) * a + a;
for (j = 0; j < largeSegments; j++)
tuples[i + j] = [offset + j * b, b];
return tuples;
}
/**
* Function yielding a string's k + 1 passjoin segments to index.
*
* @param {number} k - Levenshtein distance threshold.
* @param {string} string - Target string.
* @returns {Array} - The string's segments.
*/
function segments(k, string) {
var l = string.length,
m = k + 1,
a = (l / m) | 0,
b = a + 1,
o,
i,
j;
var largeSegments = l - a * m,
smallSegments = m - largeSegments;
var S = new Array(k + 1);
for (i = 0; i < smallSegments; i++) {
o = i * a;
S[i] = string.slice(o, o + a);
}
var offset = (i - 1) * a + a;
for (j = 0; j < largeSegments; j++) {
o = offset + j * b;
S[i + j] = string.slice(o, o + b);
}
return S;
}
// TODO: jsdocs
function segmentPos(k, i, string) {
if (i === 0)
return 0;
var l = string.length;
var m = k + 1,
a = (l / m) | 0,
b = a + 1;
var largeSegments = l - a * m,
smallSegments = m - largeSegments;
if (i <= smallSegments - 1)
return i * a;
var offset = i - smallSegments;
return smallSegments * a + offset * b;
}
/**
* Function returning the interval of relevant substrings to lookup using the
* multi-match-aware substring selection scheme described in the paper.
*
* @param {number} k - Levenshtein distance threshold.
* @param {number} delta - Signed length difference between both considered strings.
* @param {number} i - k + 1 segment index.
* @param {number} s - String's length.
* @param {number} pi - k + 1 segment position in target string.
* @param {number} li - k + 1 segment length.
* @returns {Array} - The interval (start, stop).
*/
function multiMatchAwareInterval(k, delta, i, s, pi, li) {
var start1 = pi - i,
end1 = pi + i;
var o = k - i;
var start2 = pi + delta - o,
end2 = pi + delta + o;
var end3 = s - li;
return [Math.max(0, start1, start2), Math.min(end1, end2, end3)];
}
/**
* Function yielding relevant substrings to lookup using the multi-match-aware
* substring selection scheme described in the paper.
*
* @param {number} k - Levenshtein distance threshold.
* @param {string} string - Target string.
* @param {number} l - Length of strings to match.
* @param {number} i - k + 1 segment index.
* @param {number} pi - k + 1 segment position in target string.
* @param {number} li - k + 1 segment length.
* @returns {Array} - The contiguous substrings.
*/
function multiMatchAwareSubstrings(k, string, l, i, pi, li) {
var s = string.length;
// Note that we need to keep the non-absolute delta for this function
// to work in both directions, up & down
var delta = s - l;
var interval = multiMatchAwareInterval(k, delta, i, s, pi, li);
var start = interval[0],
stop = interval[1];
var currentSubstring = '';
var substrings = [];
var substring, j, m;
for (j = start, m = stop + 1; j < m; j++) {
substring = string.slice(j, j + li);
// We skip identical consecutive substrings (to avoid repetition in case
// of contiguous letter duplication)
if (substring === currentSubstring)
continue;
substrings.push(substring);
currentSubstring = substring;
}
return substrings;
}
/**
* PassjoinIndex.
*
* @note I tried to apply the paper's optimizations regarding Levenshtein
* distance computations but it did not provide a performance boost, quite
* the contrary. This is because since we are mostly using the index for small k
* here, most of the strings we work on are quite small and the bookkeeping
* induced by Ukkonen's method and the paper's one are slowing us down more than
* they actually help us go faster.
*
* @note This implementation does not try to ensure that you add the same string
* more than once.
*
* @constructor
* @param {function} levenshtein - Levenshtein distance function.
* @param {number} k - Levenshtein distance threshold.
*/
function PassjoinIndex(levenshtein, k) {
if (typeof levenshtein !== 'function')
throw new Error('mnemonist/passjoin-index: `levenshtein` should be a function returning edit distance between two strings.');
if (typeof k !== 'number' || k < 1)
throw new Error('mnemonist/passjoin-index: `k` should be a number > 0');
this.levenshtein = levenshtein;
this.k = k;
this.clear();
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
PassjoinIndex.prototype.clear = function() {
// Properties
this.size = 0;
this.strings = [];
this.invertedIndices = {};
};
/**
* Method used to add a new value to the index.
*
* @param {string|Array} value - Value to add.
* @return {PassjoinIndex}
*/
PassjoinIndex.prototype.add = function(value) {
var l = value.length;
var stringIndex = this.size;
this.strings.push(value);
this.size++;
var S = segments(this.k, value);
var Ll = this.invertedIndices[l];
if (typeof Ll === 'undefined') {
Ll = {};
this.invertedIndices[l] = Ll;
}
var segment,
matches,
key,
i,
m;
for (i = 0, m = S.length; i < m; i++) {
segment = S[i];
key = segment + i;
matches = Ll[key];
if (typeof matches === 'undefined') {
matches = [stringIndex];
Ll[key] = matches;
}
else {
matches.push(stringIndex);
}
}
return this;
};
/**
* Method used to search for string matching the given query.
*
* @param {string|Array} query - Query string.
* @return {Array}
*/
PassjoinIndex.prototype.search = function(query) {
var s = query.length,
k = this.k;
var M = new Set();
var candidates,
candidate,
queryPos,
querySegmentLength,
key,
S,
P,
l,
m,
i,
n1,
j,
n2,
y,
n3;
for (l = Math.max(0, s - k), m = s + k + 1; l < m; l++) {
var Ll = this.invertedIndices[l];
if (typeof Ll === 'undefined')
continue;
P = partition(k, l);
for (i = 0, n1 = P.length; i < n1; i++) {
queryPos = P[i][0];
querySegmentLength = P[i][1];
S = multiMatchAwareSubstrings(
k,
query,
l,
i,
queryPos,
querySegmentLength
);
// Empty string edge case
if (!S.length)
S = [''];
for (j = 0, n2 = S.length; j < n2; j++) {
key = S[j] + i;
candidates = Ll[key];
if (typeof candidates === 'undefined')
continue;
for (y = 0, n3 = candidates.length; y < n3; y++) {
candidate = this.strings[candidates[y]];
// NOTE: first condition is here not to compute Levenshtein
// distance for tiny strings
// NOTE: maintaining a Set of rejected candidate is not really useful
// because it consumes more memory and because non-matches are
// less likely to be candidates agains
if (
s <= k && l <= k ||
(
!M.has(candidate) &&
this.levenshtein(query, candidate) <= k
)
)
M.add(candidate);
}
}
}
}
return M;
};
/**
* Method used to iterate over the index.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
PassjoinIndex.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
for (var i = 0, l = this.strings.length; i < l; i++)
callback.call(scope, this.strings[i], i, this);
};
/**
* Method used to create an iterator over a index's values.
*
* @return {Iterator}
*/
PassjoinIndex.prototype.values = function() {
var strings = this.strings,
l = strings.length,
i = 0;
return new Iterator(function() {
if (i >= l)
return {
done: true
};
var value = strings[i];
i++;
return {
value: value,
done: false
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
PassjoinIndex.prototype[Symbol.iterator] = PassjoinIndex.prototype.values;
/**
* Convenience known methods.
*/
PassjoinIndex.prototype.inspect = function() {
var array = this.strings.slice();
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: PassjoinIndex,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
PassjoinIndex.prototype[Symbol.for('nodejs.util.inspect.custom')] = PassjoinIndex.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @return {PassjoinIndex}
*/
PassjoinIndex.from = function(iterable, levenshtein, k) {
var index = new PassjoinIndex(levenshtein, k);
forEach(iterable, function(string) {
index.add(string);
});
return index;
};
/**
* Exporting.
*/
PassjoinIndex.countKeys = countKeys;
PassjoinIndex.comparator = comparator;
PassjoinIndex.partition = partition;
PassjoinIndex.segments = segments;
PassjoinIndex.segmentPos = segmentPos;
PassjoinIndex.multiMatchAwareInterval = multiMatchAwareInterval;
PassjoinIndex.multiMatchAwareSubstrings = multiMatchAwareSubstrings;
module.exports = PassjoinIndex;

View File

@@ -1,30 +0,0 @@
/**
* Mnemonist Queue Typings
* ========================
*/
export default class Queue<T> implements Iterable<T> {
// Members
size: number;
// Constructor
constructor();
// Methods
clear(): void;
enqueue(item: T): number;
dequeue(): T | undefined;
peek(): T | undefined;
forEach(callback: (item: T, index: number, queue: this) => void, scope?: any): void;
toArray(): Array<T>;
values(): IterableIterator<T>;
entries(): IterableIterator<[number, T]>;
[Symbol.iterator](): IterableIterator<T>;
toString(): string;
toJSON(): Array<T>;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}): Queue<I>;
static of<I>(...items: Array<I>): Queue<I>;
}

View File

@@ -1,215 +0,0 @@
/**
* Mnemonist Queue
* ================
*
* Queue implementation based on the ideas of Queue.js that seems to beat
* a LinkedList one in performance.
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach');
/**
* Queue
*
* @constructor
*/
function Queue() {
this.clear();
}
/**
* Method used to clear the queue.
*
* @return {undefined}
*/
Queue.prototype.clear = function() {
// Properties
this.items = [];
this.offset = 0;
this.size = 0;
};
/**
* Method used to add an item to the queue.
*
* @param {any} item - Item to enqueue.
* @return {number}
*/
Queue.prototype.enqueue = function(item) {
this.items.push(item);
return ++this.size;
};
/**
* Method used to retrieve & remove the first item of the queue.
*
* @return {any}
*/
Queue.prototype.dequeue = function() {
if (!this.size)
return;
var item = this.items[this.offset];
if (++this.offset * 2 >= this.items.length) {
this.items = this.items.slice(this.offset);
this.offset = 0;
}
this.size--;
return item;
};
/**
* Method used to retrieve the first item of the queue.
*
* @return {any}
*/
Queue.prototype.peek = function() {
if (!this.size)
return;
return this.items[this.offset];
};
/**
* Method used to iterate over the queue.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
Queue.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
for (var i = this.offset, j = 0, l = this.items.length; i < l; i++, j++)
callback.call(scope, this.items[i], j, this);
};
/*
* Method used to convert the queue to a JavaScript array.
*
* @return {array}
*/
Queue.prototype.toArray = function() {
return this.items.slice(this.offset);
};
/**
* Method used to create an iterator over a queue's values.
*
* @return {Iterator}
*/
Queue.prototype.values = function() {
var items = this.items,
i = this.offset;
return new Iterator(function() {
if (i >= items.length)
return {
done: true
};
var value = items[i];
i++;
return {
value: value,
done: false
};
});
};
/**
* Method used to create an iterator over a queue's entries.
*
* @return {Iterator}
*/
Queue.prototype.entries = function() {
var items = this.items,
i = this.offset,
j = 0;
return new Iterator(function() {
if (i >= items.length)
return {
done: true
};
var value = items[i];
i++;
return {
value: [j++, value],
done: false
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
Queue.prototype[Symbol.iterator] = Queue.prototype.values;
/**
* Convenience known methods.
*/
Queue.prototype.toString = function() {
return this.toArray().join(',');
};
Queue.prototype.toJSON = function() {
return this.toArray();
};
Queue.prototype.inspect = function() {
var array = this.toArray();
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: Queue,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
Queue.prototype[Symbol.for('nodejs.util.inspect.custom')] = Queue.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a queue.
*
* @param {Iterable} iterable - Target iterable.
* @return {Queue}
*/
Queue.from = function(iterable) {
var queue = new Queue();
forEach(iterable, function(value) {
queue.enqueue(value);
});
return queue;
};
/**
* Static @.of function taking an arbitrary number of arguments & converting it
* into a queue.
*
* @param {...any} args
* @return {Queue}
*/
Queue.of = function() {
return Queue.from(arguments);
};
/**
* Exporting.
*/
module.exports = Queue;

View File

@@ -1,251 +0,0 @@
/* eslint no-constant-condition: 0 */
/**
* Mnemonist SemiDynamicTrie
* ==========================
*
* Lowlevel Trie working at character level, storing information in typed
* array and organizing its children in linked lists.
*
* This implementation also uses a "fat node" strategy to boost access to some
* bloated node's children when the number of children rises above a certain
* threshold.
*/
var Vector = require('./vector.js');
// TODO: rename => ternary search tree
/**
* Constants.
*/
const MAX_LINKED = 7;
/**
* SemiDynamicTrie.
*
* @constructor
*/
function SemiDynamicTrie() {
// Properties
// TODO: make it 16 bits
this.characters = new Vector.Uint8Vector(256);
this.nextPointers = new Vector.Int32Vector(256);
this.childPointers = new Vector.Uint32Vector(256);
this.maps = new Vector.Uint32Vector(256);
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
SemiDynamicTrie.prototype.clear = function() {
// Properties
};
SemiDynamicTrie.prototype.ensureSibling = function(block, character) {
var nextCharacter,
nextBlock,
newBlock;
// Do we have a root?
if (this.characters.length === 0) {
this.nextPointers.push(0);
this.childPointers.push(0);
this.characters.push(character);
return block;
}
// Are we traversing a fat node?
var fatNode = this.nextPointers.array[block];
if (fatNode < 0) {
var mapIndex = -fatNode + character;
nextBlock = this.maps.array[mapIndex];
if (nextBlock !== 0)
return nextBlock;
newBlock = this.characters.length;
this.nextPointers.push(0);
this.childPointers.push(0);
this.characters.push(character);
this.maps.set(mapIndex, newBlock);
return newBlock;
}
var listLength = 1,
startingBlock = block;
while (true) {
nextCharacter = this.characters.array[block];
if (nextCharacter === character)
return block;
nextBlock = this.nextPointers.array[block];
if (nextBlock === 0)
break;
listLength++;
block = nextBlock;
}
// If the list is too long, we create a fat node
if (listLength > MAX_LINKED) {
block = startingBlock;
var offset = this.maps.length;
this.maps.resize(offset + 255);
this.maps.set(offset + 255, 0);
while (true) {
nextBlock = this.nextPointers.array[block];
if (nextBlock === 0)
break;
nextCharacter = this.characters.array[nextBlock];
this.maps.set(offset + nextCharacter, nextBlock);
block = nextBlock;
}
this.nextPointers.set(startingBlock, -offset);
newBlock = this.characters.length;
this.nextPointers.push(0);
this.childPointers.push(0);
this.characters.push(character);
this.maps.set(offset + character, newBlock);
return newBlock;
}
// Else, we append the character to the list
newBlock = this.characters.length;
this.nextPointers.push(0);
this.childPointers.push(0);
this.nextPointers.set(block, newBlock);
this.characters.push(character);
return newBlock;
};
SemiDynamicTrie.prototype.findSibling = function(block, character) {
var nextCharacter;
// Do we have a fat node?
var fatNode = this.nextPointers.array[block];
if (fatNode < 0) {
var mapIndex = -fatNode + character;
var nextBlock = this.maps.array[mapIndex];
if (nextBlock === 0)
return -1;
return nextBlock;
}
while (true) {
nextCharacter = this.characters.array[block];
if (nextCharacter === character)
return block;
block = this.nextPointers.array[block];
if (block === 0)
return -1;
}
};
SemiDynamicTrie.prototype.add = function(key) {
var keyCharacter,
childBlock,
block = 0;
var i = 0, l = key.length;
// Going as far as possible
while (i < l) {
keyCharacter = key.charCodeAt(i);
// Ensuring a correct sibling exists
block = this.ensureSibling(block, keyCharacter);
i++;
if (i < l) {
// Descending
childBlock = this.childPointers.array[block];
if (childBlock === 0)
break;
block = childBlock;
}
}
// Adding as many blocks as necessary
while (i < l) {
childBlock = this.characters.length;
this.characters.push(key.charCodeAt(i));
this.childPointers.push(0);
this.nextPointers.push(0);
this.childPointers.set(block, childBlock);
block = childBlock;
i++;
}
};
SemiDynamicTrie.prototype.has = function(key) {
var i, l;
var block = 0,
siblingBlock;
for (i = 0, l = key.length; i < l; i++) {
siblingBlock = this.findSibling(block, key.charCodeAt(i));
if (siblingBlock === -1)
return false;
// TODO: be sure
if (i === l - 1)
return true;
block = this.childPointers.array[siblingBlock];
if (block === 0)
return false;
}
// TODO: fix, should have a leaf pointer somehow
return true;
};
/**
* Exporting.
*/
module.exports = SemiDynamicTrie;

View File

@@ -1,18 +0,0 @@
/**
* Mnemonist Set Typings
* ======================
*/
export function intersection<T>(a: ReadonlySet<T>, b: ReadonlySet<T>, ...rest: Array<ReadonlySet<T>>): Set<T>;
export function union<T>(a: ReadonlySet<T>, b: ReadonlySet<T>, ...rest: Array<ReadonlySet<T>>): Set<T>;
export function difference<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): Set<T>;
export function symmetricDifference<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): Set<T>;
export function isSubset<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): boolean;
export function isSuperset<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): boolean;
export function add<T>(a: Set<T>, b: ReadonlySet<T>): void;
export function subtract<T>(a: Set<T>, b: ReadonlySet<T>): void;
export function intersect<T>(a: Set<T>, b: ReadonlySet<T>): void;
export function disjunct<T>(a: Set<T>, b: ReadonlySet<T>): void;
export function intersectionSize<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): number;
export function unionSize<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): number;
export function jaccard<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): number;
export function overlap<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): number;

View File

@@ -1,356 +0,0 @@
/**
* Mnemonist Set
* ==============
*
* Useful function related to sets such as union, intersection and so on...
*/
// TODO: optimize versions for less variadicities
/**
* Variadic function computing the intersection of multiple sets.
*
* @param {...Set} sets - Sets to intersect.
* @return {Set} - The intesection.
*/
exports.intersection = function() {
if (arguments.length < 2)
throw new Error('mnemonist/Set.intersection: needs at least two arguments.');
var I = new Set();
// First we need to find the smallest set
var smallestSize = Infinity,
smallestSet = null;
var s, i, l = arguments.length;
for (i = 0; i < l; i++) {
s = arguments[i];
// If one of the set has no items, we can stop right there
if (s.size === 0)
return I;
if (s.size < smallestSize) {
smallestSize = s.size;
smallestSet = s;
}
}
// Now we need to intersect this set with the others
var iterator = smallestSet.values(),
step,
item,
add,
set;
// TODO: we can optimize by iterating each next time over the current intersection
// but this probably means more RAM to consume since we'll create n-1 sets rather than
// only the one.
while ((step = iterator.next(), !step.done)) {
item = step.value;
add = true;
for (i = 0; i < l; i++) {
set = arguments[i];
if (set === smallestSet)
continue;
if (!set.has(item)) {
add = false;
break;
}
}
if (add)
I.add(item);
}
return I;
};
/**
* Variadic function computing the union of multiple sets.
*
* @param {...Set} sets - Sets to unite.
* @return {Set} - The union.
*/
exports.union = function() {
if (arguments.length < 2)
throw new Error('mnemonist/Set.union: needs at least two arguments.');
var U = new Set();
var i, l = arguments.length;
var iterator,
step;
for (i = 0; i < l; i++) {
iterator = arguments[i].values();
while ((step = iterator.next(), !step.done))
U.add(step.value);
}
return U;
};
/**
* Function computing the difference between two sets.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {Set} - The difference.
*/
exports.difference = function(A, B) {
// If first set is empty
if (!A.size)
return new Set();
if (!B.size)
return new Set(A);
var D = new Set();
var iterator = A.values(),
step;
while ((step = iterator.next(), !step.done)) {
if (!B.has(step.value))
D.add(step.value);
}
return D;
};
/**
* Function computing the symmetric difference between two sets.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {Set} - The symmetric difference.
*/
exports.symmetricDifference = function(A, B) {
var S = new Set();
var iterator = A.values(),
step;
while ((step = iterator.next(), !step.done)) {
if (!B.has(step.value))
S.add(step.value);
}
iterator = B.values();
while ((step = iterator.next(), !step.done)) {
if (!A.has(step.value))
S.add(step.value);
}
return S;
};
/**
* Function returning whether A is a subset of B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {boolean}
*/
exports.isSubset = function(A, B) {
var iterator = A.values(),
step;
// Shortcuts
if (A === B)
return true;
if (A.size > B.size)
return false;
while ((step = iterator.next(), !step.done)) {
if (!B.has(step.value))
return false;
}
return true;
};
/**
* Function returning whether A is a superset of B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {boolean}
*/
exports.isSuperset = function(A, B) {
return exports.isSubset(B, A);
};
/**
* Function adding the items of set B to the set A.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
*/
exports.add = function(A, B) {
var iterator = B.values(),
step;
while ((step = iterator.next(), !step.done))
A.add(step.value);
return;
};
/**
* Function subtracting the items of set B from the set A.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
*/
exports.subtract = function(A, B) {
var iterator = B.values(),
step;
while ((step = iterator.next(), !step.done))
A.delete(step.value);
return;
};
/**
* Function intersecting the items of A & B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
*/
exports.intersect = function(A, B) {
var iterator = A.values(),
step;
while ((step = iterator.next(), !step.done)) {
if (!B.has(step.value))
A.delete(step.value);
}
return;
};
/**
* Function disjuncting the items of A & B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
*/
exports.disjunct = function(A, B) {
var iterator = A.values(),
step;
var toRemove = [];
while ((step = iterator.next(), !step.done)) {
if (B.has(step.value))
toRemove.push(step.value);
}
iterator = B.values();
while ((step = iterator.next(), !step.done)) {
if (!A.has(step.value))
A.add(step.value);
}
for (var i = 0, l = toRemove.length; i < l; i++)
A.delete(toRemove[i]);
return;
};
/**
* Function returning the size of the intersection of A & B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {number}
*/
exports.intersectionSize = function(A, B) {
var tmp;
// We need to know the smallest set
if (A.size > B.size) {
tmp = A;
A = B;
B = tmp;
}
if (A.size === 0)
return 0;
if (A === B)
return A.size;
var iterator = A.values(),
step;
var I = 0;
while ((step = iterator.next(), !step.done)) {
if (B.has(step.value))
I++;
}
return I;
};
/**
* Function returning the size of the union of A & B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {number}
*/
exports.unionSize = function(A, B) {
var I = exports.intersectionSize(A, B);
return A.size + B.size - I;
};
/**
* Function returning the Jaccard similarity between A & B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {number}
*/
exports.jaccard = function(A, B) {
var I = exports.intersectionSize(A, B);
if (I === 0)
return 0;
var U = A.size + B.size - I;
return I / U;
};
/**
* Function returning the overlap coefficient between A & B.
*
* @param {Set} A - First set.
* @param {Set} B - Second set.
* @return {number}
*/
exports.overlap = function(A, B) {
var I = exports.intersectionSize(A, B);
if (I === 0)
return 0;
return I / Math.min(A.size, B.size);
};

View File

@@ -1,4 +0,0 @@
import {IArrayLike} from '../utils/types';
export function inplaceInsertionSort(array: IArrayLike, lo: number, hi: number): IArrayLike;
export function inplaceInsertionSortIndices(array: IArrayLike, indices: IArrayLike, lo: number, hi: number): IArrayLike;

View File

@@ -1,50 +0,0 @@
/**
* Mnemonist Insertion Sort
* =========================
*
* Insertion sort related functions.
*/
function inplaceInsertionSort(array, lo, hi) {
i = lo + 1;
var j, k;
for (; i < hi; i++) {
k = array[i];
j = i - 1;
while (j >= lo && array[j] > k) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = k;
}
return array;
}
exports.inplaceInsertionSort = inplaceInsertionSort;
function inplaceInsertionSortIndices(array, indices, lo, hi) {
i = lo + 1;
var j, k, t;
for (; i < hi; i++) {
t = indices[i];
k = array[t];
j = i - 1;
while (j >= lo && array[indices[j]] > k) {
indices[j + 1] = indices[j];
j--;
}
indices[j + 1] = t;
}
return indices;
}
exports.inplaceInsertionSortIndices = inplaceInsertionSortIndices;

View File

@@ -1,4 +0,0 @@
import {IArrayLike} from '../utils/types';
export function inplaceQuickSort(array: IArrayLike, lo: number, hi: number): IArrayLike;
export function inplaceQuickSortIndices(array: IArrayLike, indices: IArrayLike, lo: number, hi: number): IArrayLike;

View File

@@ -1,116 +0,0 @@
/**
* Mnemonist Quick Sort
* =====================
*
* Quick sort related functions.
* Adapted from: https://alienryderflex.com/quicksort/
*/
var LOS = new Float64Array(64),
HIS = new Float64Array(64);
function inplaceQuickSort(array, lo, hi) {
var p, i, l, r, swap;
LOS[0] = lo;
HIS[0] = hi;
i = 0;
while (i >= 0) {
l = LOS[i];
r = HIS[i] - 1;
if (l < r) {
p = array[l];
while (l < r) {
while (array[r] >= p && l < r)
r--;
if (l < r)
array[l++] = array[r];
while (array[l] <= p && l < r)
l++;
if (l < r)
array[r--] = array[l];
}
array[l] = p;
LOS[i + 1] = l + 1;
HIS[i + 1] = HIS[i];
HIS[i++] = l;
if (HIS[i] - LOS[i] > HIS[i - 1] - LOS[i - 1]) {
swap = LOS[i];
LOS[i] = LOS[i - 1];
LOS[i - 1] = swap;
swap = HIS[i];
HIS[i] = HIS[i - 1];
HIS[i - 1] = swap;
}
}
else {
i--;
}
}
return array;
}
exports.inplaceQuickSort = inplaceQuickSort;
function inplaceQuickSortIndices(array, indices, lo, hi) {
var p, i, l, r, t, swap;
LOS[0] = lo;
HIS[0] = hi;
i = 0;
while (i >= 0) {
l = LOS[i];
r = HIS[i] - 1;
if (l < r) {
t = indices[l];
p = array[t];
while (l < r) {
while (array[indices[r]] >= p && l < r)
r--;
if (l < r)
indices[l++] = indices[r];
while (array[indices[l]] <= p && l < r)
l++;
if (l < r)
indices[r--] = indices[l];
}
indices[l] = t;
LOS[i + 1] = l + 1;
HIS[i + 1] = HIS[i];
HIS[i++] = l;
if (HIS[i] - LOS[i] > HIS[i - 1] - LOS[i - 1]) {
swap = LOS[i];
LOS[i] = LOS[i - 1];
LOS[i - 1] = swap;
swap = HIS[i];
HIS[i] = HIS[i - 1];
HIS[i - 1] = swap;
}
}
else {
i--;
}
}
return indices;
}
exports.inplaceQuickSortIndices = inplaceQuickSortIndices;

View File

@@ -1,29 +0,0 @@
/**
* Mnemonist SparseMap Typings
* ============================
*/
import {IArrayLikeConstructor} from './utils/types';
export default class SparseMap<V> implements Iterable<[number, V]> {
// Members
length: number;
size: number;
// Constructor
constructor(length: number);
constructor(values: IArrayLikeConstructor, length: number);
// Methods
clear(): void;
has(key: number): boolean;
get(key: number): V | undefined;
set(key: number, value: V): this;
delete(key: number): boolean;
forEach(callback: (value: V, key: number, set: this) => void, scope?: any): void;
keys(): IterableIterator<number>;
values(): IterableIterator<V>;
entries(): IterableIterator<[number, V]>;
[Symbol.iterator](): IterableIterator<[number, V]>;
inspect(): any;
}

View File

@@ -1,243 +0,0 @@
/**
* Mnemonist SparseMap
* ====================
*
* JavaScript sparse map implemented on top of byte arrays.
*
* [Reference]: https://research.swtch.com/sparse
*/
var Iterator = require('obliterator/iterator'),
getPointerArray = require('./utils/typed-arrays.js').getPointerArray;
/**
* SparseMap.
*
* @constructor
*/
function SparseMap(Values, length) {
if (arguments.length < 2) {
length = Values;
Values = Array;
}
var ByteArray = getPointerArray(length);
// Properties
this.size = 0;
this.length = length;
this.dense = new ByteArray(length);
this.sparse = new ByteArray(length);
this.vals = new Values(length);
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
SparseMap.prototype.clear = function() {
this.size = 0;
};
/**
* Method used to check the existence of a member in the set.
*
* @param {number} member - Member to test.
* @return {SparseMap}
*/
SparseMap.prototype.has = function(member) {
var index = this.sparse[member];
return (
index < this.size &&
this.dense[index] === member
);
};
/**
* Method used to get the value associated to a member in the set.
*
* @param {number} member - Member to test.
* @return {any}
*/
SparseMap.prototype.get = function(member) {
var index = this.sparse[member];
if (index < this.size && this.dense[index] === member)
return this.vals[index];
return;
};
/**
* Method used to set a value into the map.
*
* @param {number} member - Member to set.
* @param {any} value - Associated value.
* @return {SparseMap}
*/
SparseMap.prototype.set = function(member, value) {
var index = this.sparse[member];
if (index < this.size && this.dense[index] === member) {
this.vals[index] = value;
return this;
}
this.dense[this.size] = member;
this.sparse[member] = this.size;
this.vals[this.size] = value;
this.size++;
return this;
};
/**
* Method used to remove a member from the set.
*
* @param {number} member - Member to delete.
* @return {boolean}
*/
SparseMap.prototype.delete = function(member) {
var index = this.sparse[member];
if (index >= this.size || this.dense[index] !== member)
return false;
index = this.dense[this.size - 1];
this.dense[this.sparse[member]] = index;
this.sparse[index] = this.sparse[member];
this.size--;
return true;
};
/**
* Method used to iterate over the set's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
SparseMap.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
for (var i = 0; i < this.size; i++)
callback.call(scope, this.vals[i], this.dense[i]);
};
/**
* Method used to create an iterator over a set's members.
*
* @return {Iterator}
*/
SparseMap.prototype.keys = function() {
var size = this.size,
dense = this.dense,
i = 0;
return new Iterator(function() {
if (i < size) {
var item = dense[i];
i++;
return {
value: item
};
}
return {
done: true
};
});
};
/**
* Method used to create an iterator over a set's values.
*
* @return {Iterator}
*/
SparseMap.prototype.values = function() {
var size = this.size,
values = this.vals,
i = 0;
return new Iterator(function() {
if (i < size) {
var item = values[i];
i++;
return {
value: item
};
}
return {
done: true
};
});
};
/**
* Method used to create an iterator over a set's entries.
*
* @return {Iterator}
*/
SparseMap.prototype.entries = function() {
var size = this.size,
dense = this.dense,
values = this.vals,
i = 0;
return new Iterator(function() {
if (i < size) {
var item = [dense[i], values[i]];
i++;
return {
value: item
};
}
return {
done: true
};
});
};
/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
SparseMap.prototype[Symbol.iterator] = SparseMap.prototype.entries;
/**
* Convenience known methods.
*/
SparseMap.prototype.inspect = function() {
var proxy = new Map();
for (var i = 0; i < this.size; i++)
proxy.set(this.dense[i], this.vals[i]);
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: SparseMap,
enumerable: false
});
proxy.length = this.length;
if (this.vals.constructor !== Array)
proxy.type = this.vals.constructor.name;
return proxy;
};
if (typeof Symbol !== 'undefined')
SparseMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = SparseMap.prototype.inspect;
/**
* Exporting.
*/
module.exports = SparseMap;

View File

@@ -1,24 +0,0 @@
/**
* Mnemonist SparseQueueSet Typings
* =================================
*/
export default class SparseQueueSet implements Iterable<number> {
// Members
capacity: number;
start: number;
size: number;
// Constructor
constructor(length: number);
// Methods
clear(): void;
has(value: number): boolean;
enqueue(value: number): this;
dequeue(): number | undefined;
forEach(callback: (value: number, key: number, set: this) => void, scope?: any): void;
values(): IterableIterator<number>;
[Symbol.iterator](): IterableIterator<number>;
inspect(): any;
}

View File

@@ -1,218 +0,0 @@
/**
* Mnemonist SparseQueueSet
* =========================
*
* JavaScript sparse queue set implemented on top of byte arrays.
*
* [Reference]: https://research.swtch.com/sparse
*/
var Iterator = require('obliterator/iterator'),
getPointerArray = require('./utils/typed-arrays.js').getPointerArray;
/**
* SparseQueueSet.
*
* @constructor
*/
function SparseQueueSet(capacity) {
var ByteArray = getPointerArray(capacity);
// Properties
this.start = 0;
this.size = 0;
this.capacity = capacity;
this.dense = new ByteArray(capacity);
this.sparse = new ByteArray(capacity);
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
SparseQueueSet.prototype.clear = function() {
this.start = 0;
this.size = 0;
};
/**
* Method used to check the existence of a member in the queue.
*
* @param {number} member - Member to test.
* @return {SparseQueueSet}
*/
SparseQueueSet.prototype.has = function(member) {
if (this.size === 0)
return false;
var index = this.sparse[member];
var inBounds = (
index < this.capacity &&
(
index >= this.start &&
index < this.start + this.size
) ||
(
index < ((this.start + this.size) % this.capacity)
)
);
return (
inBounds &&
this.dense[index] === member
);
};
/**
* Method used to add a member to the queue.
*
* @param {number} member - Member to add.
* @return {SparseQueueSet}
*/
SparseQueueSet.prototype.enqueue = function(member) {
var index = this.sparse[member];
if (this.size !== 0) {
var inBounds = (
index < this.capacity &&
(
index >= this.start &&
index < this.start + this.size
) ||
(
index < ((this.start + this.size) % this.capacity)
)
);
if (inBounds && this.dense[index] === member)
return this;
}
index = (this.start + this.size) % this.capacity;
this.dense[index] = member;
this.sparse[member] = index;
this.size++;
return this;
};
/**
* Method used to remove the next member from the queue.
*
* @param {number} member - Member to delete.
* @return {boolean}
*/
SparseQueueSet.prototype.dequeue = function() {
if (this.size === 0)
return;
var index = this.start;
this.size--;
this.start++;
if (this.start === this.capacity)
this.start = 0;
var member = this.dense[index];
this.sparse[member] = this.capacity;
return member;
};
/**
* Method used to iterate over the queue's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
SparseQueueSet.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
var c = this.capacity,
l = this.size,
i = this.start,
j = 0;
while (j < l) {
callback.call(scope, this.dense[i], j, this);
i++;
j++;
if (i === c)
i = 0;
}
};
/**
* Method used to create an iterator over a set's values.
*
* @return {Iterator}
*/
SparseQueueSet.prototype.values = function() {
var dense = this.dense,
c = this.capacity,
l = this.size,
i = this.start,
j = 0;
return new Iterator(function() {
if (j >= l)
return {
done: true
};
var value = dense[i];
i++;
j++;
if (i === c)
i = 0;
return {
value: value,
done: false
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
SparseQueueSet.prototype[Symbol.iterator] = SparseQueueSet.prototype.values;
/**
* Convenience known methods.
*/
SparseQueueSet.prototype.inspect = function() {
var proxy = [];
this.forEach(function(member) {
proxy.push(member);
});
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: SparseQueueSet,
enumerable: false
});
proxy.capacity = this.capacity;
return proxy;
};
if (typeof Symbol !== 'undefined')
SparseQueueSet.prototype[Symbol.for('nodejs.util.inspect.custom')] = SparseQueueSet.prototype.inspect;
/**
* Exporting.
*/
module.exports = SparseQueueSet;

View File

@@ -1,23 +0,0 @@
/**
* Mnemonist SparseSet Typings
* ============================
*/
export default class SparseSet implements Iterable<number> {
// Members
length: number;
size: number;
// Constructor
constructor(length: number);
// Methods
clear(): void;
has(value: number): boolean;
add(value: number): this;
delete(value: number): boolean;
forEach(callback: (value: number, key: number, set: this) => void, scope?: any): void;
values(): IterableIterator<number>;
[Symbol.iterator](): IterableIterator<number>;
inspect(): any;
}

View File

@@ -1,168 +0,0 @@
/**
* Mnemonist SparseSet
* ====================
*
* JavaScript sparse set implemented on top of byte arrays.
*
* [Reference]: https://research.swtch.com/sparse
*/
var Iterator = require('obliterator/iterator'),
getPointerArray = require('./utils/typed-arrays.js').getPointerArray;
/**
* SparseSet.
*
* @constructor
*/
function SparseSet(length) {
var ByteArray = getPointerArray(length);
// Properties
this.size = 0;
this.length = length;
this.dense = new ByteArray(length);
this.sparse = new ByteArray(length);
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
SparseSet.prototype.clear = function() {
this.size = 0;
};
/**
* Method used to check the existence of a member in the set.
*
* @param {number} member - Member to test.
* @return {SparseSet}
*/
SparseSet.prototype.has = function(member) {
var index = this.sparse[member];
return (
index < this.size &&
this.dense[index] === member
);
};
/**
* Method used to add a member to the set.
*
* @param {number} member - Member to add.
* @return {SparseSet}
*/
SparseSet.prototype.add = function(member) {
var index = this.sparse[member];
if (index < this.size && this.dense[index] === member)
return this;
this.dense[this.size] = member;
this.sparse[member] = this.size;
this.size++;
return this;
};
/**
* Method used to remove a member from the set.
*
* @param {number} member - Member to delete.
* @return {boolean}
*/
SparseSet.prototype.delete = function(member) {
var index = this.sparse[member];
if (index >= this.size || this.dense[index] !== member)
return false;
index = this.dense[this.size - 1];
this.dense[this.sparse[member]] = index;
this.sparse[index] = this.sparse[member];
this.size--;
return true;
};
/**
* Method used to iterate over the set's values.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
SparseSet.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
var item;
for (var i = 0; i < this.size; i++) {
item = this.dense[i];
callback.call(scope, item, item);
}
};
/**
* Method used to create an iterator over a set's values.
*
* @return {Iterator}
*/
SparseSet.prototype.values = function() {
var size = this.size,
dense = this.dense,
i = 0;
return new Iterator(function() {
if (i < size) {
var item = dense[i];
i++;
return {
value: item
};
}
return {
done: true
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
SparseSet.prototype[Symbol.iterator] = SparseSet.prototype.values;
/**
* Convenience known methods.
*/
SparseSet.prototype.inspect = function() {
var proxy = new Set();
for (var i = 0; i < this.size; i++)
proxy.add(this.dense[i]);
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: SparseSet,
enumerable: false
});
proxy.length = this.length;
return proxy;
};
if (typeof Symbol !== 'undefined')
SparseSet.prototype[Symbol.for('nodejs.util.inspect.custom')] = SparseSet.prototype.inspect;
/**
* Exporting.
*/
module.exports = SparseSet;

View File

@@ -1,30 +0,0 @@
/**
* Mnemonist Stack Typings
* ========================
*/
export default class Stack<T> implements Iterable<T> {
// Members
size: number;
// Constructor
constructor();
// Methods
clear(): void;
push(item: T): number;
pop(): T | undefined;
peek(): T | undefined;
forEach(callback: (item: T, index: number, stack: this) => void, scope?: any): void;
toArray(): Array<T>;
values(): IterableIterator<T>;
entries(): IterableIterator<[number, T]>;
[Symbol.iterator](): IterableIterator<T>;
toString(): string;
toJSON(): Array<T>;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}): Stack<I>;
static of<I>(...items: Array<I>): Stack<I>;
}

View File

@@ -1,210 +0,0 @@
/**
* Mnemonist Stack
* ================
*
* Stack implementation relying on JavaScript arrays, which are fast enough &
* correctly optimized for this kind of work.
*/
var Iterator = require('obliterator/iterator'),
forEach = require('obliterator/foreach');
/**
* Stack
*
* @constructor
*/
function Stack() {
this.clear();
}
/**
* Method used to clear the stack.
*
* @return {undefined}
*/
Stack.prototype.clear = function() {
// Properties
this.items = [];
this.size = 0;
};
/**
* Method used to add an item to the stack.
*
* @param {any} item - Item to add.
* @return {number}
*/
Stack.prototype.push = function(item) {
this.items.push(item);
return ++this.size;
};
/**
* Method used to retrieve & remove the last item of the stack.
*
* @return {any}
*/
Stack.prototype.pop = function() {
if (this.size === 0)
return;
this.size--;
return this.items.pop();
};
/**
* Method used to get the last item of the stack.
*
* @return {any}
*/
Stack.prototype.peek = function() {
return this.items[this.size - 1];
};
/**
* Method used to iterate over the stack.
*
* @param {function} callback - Function to call for each item.
* @param {object} scope - Optional scope.
* @return {undefined}
*/
Stack.prototype.forEach = function(callback, scope) {
scope = arguments.length > 1 ? scope : this;
for (var i = 0, l = this.items.length; i < l; i++)
callback.call(scope, this.items[l - i - 1], i, this);
};
/**
* Method used to convert the stack to a JavaScript array.
*
* @return {array}
*/
Stack.prototype.toArray = function() {
var array = new Array(this.size),
l = this.size - 1,
i = this.size;
while (i--)
array[i] = this.items[l - i];
return array;
};
/**
* Method used to create an iterator over a stack's values.
*
* @return {Iterator}
*/
Stack.prototype.values = function() {
var items = this.items,
l = items.length,
i = 0;
return new Iterator(function() {
if (i >= l)
return {
done: true
};
var value = items[l - i - 1];
i++;
return {
value: value,
done: false
};
});
};
/**
* Method used to create an iterator over a stack's entries.
*
* @return {Iterator}
*/
Stack.prototype.entries = function() {
var items = this.items,
l = items.length,
i = 0;
return new Iterator(function() {
if (i >= l)
return {
done: true
};
var value = items[l - i - 1];
return {
value: [i++, value],
done: false
};
});
};
/**
* Attaching the #.values method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
Stack.prototype[Symbol.iterator] = Stack.prototype.values;
/**
* Convenience known methods.
*/
Stack.prototype.toString = function() {
return this.toArray().join(',');
};
Stack.prototype.toJSON = function() {
return this.toArray();
};
Stack.prototype.inspect = function() {
var array = this.toArray();
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: Stack,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
Stack.prototype[Symbol.for('nodejs.util.inspect.custom')] = Stack.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a stack.
*
* @param {Iterable} iterable - Target iterable.
* @return {Stack}
*/
Stack.from = function(iterable) {
var stack = new Stack();
forEach(iterable, function(value) {
stack.push(value);
});
return stack;
};
/**
* Static @.of function taking an arbitrary number of arguments & converting it
* into a stack.
*
* @param {...any} args
* @return {Stack}
*/
Stack.of = function() {
return Stack.from(arguments);
};
/**
* Exporting.
*/
module.exports = Stack;

View File

@@ -1,23 +0,0 @@
/**
* Mnemonist StaticDisjointSet Typings
* ====================================
*/
import {ArrayLike} from './utils/types';
export default class StaticDisjointSet {
// Members
dimension: number;
size: number;
// Constructor
constructor(size: number);
// Methods
find(x: number): number;
union(x: number, y: number): this;
connected(x: number, y: number): boolean;
mapping(): ArrayLike;
compile(): Array<Array<number>>;
inspect(): any;
}

View File

@@ -1,195 +0,0 @@
/* eslint no-constant-condition: 0 */
/**
* Mnemonist StaticDisjointSet
* ============================
*
* JavaScript implementation of a static disjoint set (union-find).
*
* Note that to remain performant, this implementation needs to know a size
* beforehand.
*/
var helpers = require('./utils/typed-arrays.js');
/**
* StaticDisjointSet.
*
* @constructor
*/
function StaticDisjointSet(size) {
// Optimizing the typed array types
var ParentsTypedArray = helpers.getPointerArray(size),
RanksTypedArray = helpers.getPointerArray(Math.log2(size));
// Properties
this.size = size;
this.dimension = size;
this.parents = new ParentsTypedArray(size);
this.ranks = new RanksTypedArray(size);
// Initializing parents
for (var i = 0; i < size; i++)
this.parents[i] = i;
}
/**
* Method used to find the root of the given item.
*
* @param {number} x - Target item.
* @return {number}
*/
StaticDisjointSet.prototype.find = function(x) {
var y = x;
var c, p;
while (true) {
c = this.parents[y];
if (y === c)
break;
y = c;
}
// Path compression
while (true) {
p = this.parents[x];
if (p === y)
break;
this.parents[x] = y;
x = p;
}
return y;
};
/**
* Method used to perform the union of two items.
*
* @param {number} x - First item.
* @param {number} y - Second item.
* @return {StaticDisjointSet}
*/
StaticDisjointSet.prototype.union = function(x, y) {
var xRoot = this.find(x),
yRoot = this.find(y);
// x and y are already in the same set
if (xRoot === yRoot)
return this;
this.dimension--;
// x and y are not in the same set, we merge them
var xRank = this.ranks[x],
yRank = this.ranks[y];
if (xRank < yRank) {
this.parents[xRoot] = yRoot;
}
else if (xRank > yRank) {
this.parents[yRoot] = xRoot;
}
else {
this.parents[yRoot] = xRoot;
this.ranks[xRoot]++;
}
return this;
};
/**
* Method returning whether two items are connected.
*
* @param {number} x - First item.
* @param {number} y - Second item.
* @return {boolean}
*/
StaticDisjointSet.prototype.connected = function(x, y) {
var xRoot = this.find(x);
return xRoot === this.find(y);
};
/**
* Method returning the set mapping.
*
* @return {TypedArray}
*/
StaticDisjointSet.prototype.mapping = function() {
var MappingClass = helpers.getPointerArray(this.dimension);
var ids = {},
mapping = new MappingClass(this.size),
c = 0;
var r;
for (var i = 0, l = this.parents.length; i < l; i++) {
r = this.find(i);
if (typeof ids[r] === 'undefined') {
mapping[i] = c;
ids[r] = c++;
}
else {
mapping[i] = ids[r];
}
}
return mapping;
};
/**
* Method used to compile the disjoint set into an array of arrays.
*
* @return {array}
*/
StaticDisjointSet.prototype.compile = function() {
var ids = {},
result = new Array(this.dimension),
c = 0;
var r;
for (var i = 0, l = this.parents.length; i < l; i++) {
r = this.find(i);
if (typeof ids[r] === 'undefined') {
result[c] = [i];
ids[r] = c++;
}
else {
result[ids[r]].push(i);
}
}
return result;
};
/**
* Convenience known methods.
*/
StaticDisjointSet.prototype.inspect = function() {
var array = this.compile();
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: StaticDisjointSet,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
StaticDisjointSet.prototype[Symbol.for('nodejs.util.inspect.custom')] = StaticDisjointSet.prototype.inspect;
/**
* Exporting.
*/
module.exports = StaticDisjointSet;

View File

@@ -1,27 +0,0 @@
/**
* Mnemonist StaticIntervalTree Typings
* =====================================
*/
type StaticIntervalTreeGetter<T> = (item: T) => number;
type StaticIntervalTreeGettersTuple<T> = [StaticIntervalTreeGetter<T>, StaticIntervalTreeGetter<T>];
export default class StaticIntervalTree<T> {
// Members
height: number;
size: number;
// Constructor
constructor(intervals: Array<T>, getters?: StaticIntervalTreeGettersTuple<T>);
// Methods
intervalsContainingPoint(point: number): Array<T>;
intervalsOverlappingInterval(interval: T): Array<T>;
inspect(): any;
// Statics
static from<I>(
iterable: Iterable<I> | {[key: string]: I},
getters?: StaticIntervalTreeGettersTuple<I>
): StaticIntervalTree<I>;
}

View File

@@ -1,387 +0,0 @@
/*
* Mnemonist StaticIntervalTree
* =============================
*
* JavaScript implementation of a static interval tree. This tree is static in
* that you are required to know all its items beforehand and to built it
* from an iterable.
*
* This implementation represents the interval tree as an augmented balanced
* binary search tree. It works by sorting the intervals by startpoint first
* then proceeds building the augmented balanced BST bottom-up from the
* sorted list.
*
* Note that this implementation considers every given intervals as closed for
* simplicity's sake.
*
* For more information: https://en.wikipedia.org/wiki/Interval_tree
*/
var iterables = require('./utils/iterables.js'),
typed = require('./utils/typed-arrays.js');
var FixedStack = require('./fixed-stack.js');
// TODO: pass index to getters
// TODO: custom comparison
// TODO: possibility to pass offset buffer
// TODO: intervals() => Symbol.iterator
// TODO: dfs()
/**
* Helpers.
*/
/**
* Recursive function building the BST from the sorted list of interval
* indices.
*
* @param {array} intervals - Array of intervals to index.
* @param {function} endGetter - Getter function for end of intervals.
* @param {array} sortedIndices - Sorted indices of the intervals.
* @param {array} tree - BST memory.
* @param {array} augmentations - Array of node augmentations.
* @param {number} i - BST index of current node.
* @param {number} low - Dichotomy low index.
* @param {number} high - Dichotomy high index.
* @return {number} - Created node augmentation value.
*/
function buildBST(
intervals,
endGetter,
sortedIndices,
tree,
augmentations,
i,
low,
high
) {
var mid = (low + (high - low) / 2) | 0,
midMinusOne = ~-mid,
midPlusOne = -~mid;
var current = sortedIndices[mid];
tree[i] = current + 1;
var end = endGetter ? endGetter(intervals[current]) : intervals[current][1];
var left = i * 2 + 1,
right = i * 2 + 2;
var leftEnd = -Infinity,
rightEnd = -Infinity;
if (low <= midMinusOne) {
leftEnd = buildBST(
intervals,
endGetter,
sortedIndices,
tree,
augmentations,
left,
low,
midMinusOne
);
}
if (midPlusOne <= high) {
rightEnd = buildBST(
intervals,
endGetter,
sortedIndices,
tree,
augmentations,
right,
midPlusOne,
high
);
}
var augmentation = Math.max(end, leftEnd, rightEnd);
var augmentationPointer = current;
if (augmentation === leftEnd)
augmentationPointer = augmentations[tree[left] - 1];
else if (augmentation === rightEnd)
augmentationPointer = augmentations[tree[right] - 1];
augmentations[current] = augmentationPointer;
return augmentation;
}
/**
* StaticIntervalTree.
*
* @constructor
* @param {array} intervals - Array of intervals to index.
* @param {array<function>} getters - Optional getters.
*/
function StaticIntervalTree(intervals, getters) {
// Properties
this.size = intervals.length;
this.intervals = intervals;
var startGetter = null,
endGetter = null;
if (Array.isArray(getters)) {
startGetter = getters[0];
endGetter = getters[1];
}
// Building the indices array
var length = intervals.length;
var IndicesArray = typed.getPointerArray(length + 1);
var indices = new IndicesArray(length);
var i;
for (i = 1; i < length; i++)
indices[i] = i;
// Sorting indices array
// TODO: check if some version of radix sort can outperform this part
indices.sort(function(a, b) {
a = intervals[a];
b = intervals[b];
if (startGetter) {
a = startGetter(a);
b = startGetter(b);
}
else {
a = a[0];
b = b[0];
}
if (a < b)
return -1;
if (a > b)
return 1;
// TODO: use getters
// TODO: this ordering has the following invariant: if query interval
// contains [nodeStart, max], then whole right subtree can be collected
// a = a[1];
// b = b[1];
// if (a < b)
// return 1;
// if (a > b)
// return -1;
return 0;
});
// Building the binary tree
var height = Math.ceil(Math.log2(length + 1)),
treeSize = Math.pow(2, height) - 1;
var tree = new IndicesArray(treeSize);
var augmentations = new IndicesArray(length);
buildBST(
intervals,
endGetter,
indices,
tree,
augmentations,
0,
0,
length - 1
);
// Dropping indices
indices = null;
// Storing necessary information
this.height = height;
this.tree = tree;
this.augmentations = augmentations;
this.startGetter = startGetter;
this.endGetter = endGetter;
// Initializing DFS stack
this.stack = new FixedStack(IndicesArray, this.height);
}
/**
* Method returning a list of intervals containing the given point.
*
* @param {any} point - Target point.
* @return {array}
*/
StaticIntervalTree.prototype.intervalsContainingPoint = function(point) {
var matches = [];
var stack = this.stack;
stack.clear();
stack.push(0);
var l = this.tree.length;
var bstIndex,
intervalIndex,
interval,
maxInterval,
start,
end,
max,
left,
right;
while (stack.size) {
bstIndex = stack.pop();
intervalIndex = this.tree[bstIndex] - 1;
interval = this.intervals[intervalIndex];
maxInterval = this.intervals[this.augmentations[intervalIndex]];
max = this.endGetter ? this.endGetter(maxInterval) : maxInterval[1];
// No possible match, point is farther right than the max end value
if (point > max)
continue;
// Searching left
left = bstIndex * 2 + 1;
if (left < l && this.tree[left] !== 0)
stack.push(left);
start = this.startGetter ? this.startGetter(interval) : interval[0];
end = this.endGetter ? this.endGetter(interval) : interval[1];
// Checking current node
if (point >= start && point <= end)
matches.push(interval);
// If the point is to the left of the start of the current interval,
// then it cannot be in the right child
if (point < start)
continue;
// Searching right
right = bstIndex * 2 + 2;
if (right < l && this.tree[right] !== 0)
stack.push(right);
}
return matches;
};
/**
* Method returning a list of intervals overlapping the given interval.
*
* @param {any} interval - Target interval.
* @return {array}
*/
StaticIntervalTree.prototype.intervalsOverlappingInterval = function(interval) {
var intervalStart = this.startGetter ? this.startGetter(interval) : interval[0],
intervalEnd = this.endGetter ? this.endGetter(interval) : interval[1];
var matches = [];
var stack = this.stack;
stack.clear();
stack.push(0);
var l = this.tree.length;
var bstIndex,
intervalIndex,
currentInterval,
maxInterval,
start,
end,
max,
left,
right;
while (stack.size) {
bstIndex = stack.pop();
intervalIndex = this.tree[bstIndex] - 1;
currentInterval = this.intervals[intervalIndex];
maxInterval = this.intervals[this.augmentations[intervalIndex]];
max = this.endGetter ? this.endGetter(maxInterval) : maxInterval[1];
// No possible match, start is farther right than the max end value
if (intervalStart > max)
continue;
// Searching left
left = bstIndex * 2 + 1;
if (left < l && this.tree[left] !== 0)
stack.push(left);
start = this.startGetter ? this.startGetter(currentInterval) : currentInterval[0];
end = this.endGetter ? this.endGetter(currentInterval) : currentInterval[1];
// Checking current node
if (intervalEnd >= start && intervalStart <= end)
matches.push(currentInterval);
// If the end is to the left of the start of the current interval,
// then it cannot be in the right child
if (intervalEnd < start)
continue;
// Searching right
right = bstIndex * 2 + 2;
if (right < l && this.tree[right] !== 0)
stack.push(right);
}
return matches;
};
/**
* Convenience known methods.
*/
StaticIntervalTree.prototype.inspect = function() {
var proxy = this.intervals.slice();
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: StaticIntervalTree,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
StaticIntervalTree.prototype[Symbol.for('nodejs.util.inspect.custom')] = StaticIntervalTree.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @return {StaticIntervalTree}
*/
StaticIntervalTree.from = function(iterable, getters) {
if (iterables.isArrayLike(iterable))
return new StaticIntervalTree(iterable, getters);
return new StaticIntervalTree(Array.from(iterable), getters);
};
/**
* Exporting.
*/
module.exports = StaticIntervalTree;

View File

@@ -1,37 +0,0 @@
/**
* Mnemonist SuffixArray Typings
* ==============================
*/
export default class SuffixArray {
// Members
array: Array<number>;
length: number;
string: string | Array<string>;
// Constructor
constructor(string: string | Array<string>);
// Methods
toString(): string;
toJSON(): Array<number>;
inspect(): any;
}
export class GeneralizedSuffixArray {
// Members
array: Array<number>;
length: number;
size: number;
text: string | Array<string>;
// Constructor
constructor(strings: Array<string> | Array<Array<string>>);
// Methods
longestCommonSubsequence(): string | Array<string>;
toString(): string;
toJSON(): Array<number>;
inspect(): any;
}

View File

@@ -1,353 +0,0 @@
/**
* Mnemonist Suffix Array
* =======================
*
* Linear time implementation of a suffix array using the recursive
* method by Karkkainen and Sanders.
*
* [References]:
* https://www.cs.helsinki.fi/u/tpkarkka/publications/jacm05-revised.pdf
* http://people.mpi-inf.mpg.de/~sanders/programs/suffix/
* http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.184.442&rep=rep1&type=pdf
*
* [Article]:
* "Simple Linear Work Suffix Array Construction", Karkkainen and Sanders.
*
* [Note]:
* A paper by Simon J. Puglisi, William F. Smyth & Andrew Turpin named
* "The Performance of Linear Time Suffix Sorting Algorithms" seems to
* prove that supralinear algorithm are in fact better faring for
* "real" world use cases. It would be nice to check this out in JavaScript
* because the high level of the language could change a lot to the fact.
*
* The current code is largely inspired by the following:
* https://github.com/tixxit/suffixarray/blob/master/suffixarray.js
*/
/**
* Constants.
*/
var SEPARATOR = '\u0001';
/**
* Function used to sort the triples.
*
* @param {string|array} string - Padded sequence.
* @param {array} array - Array to sort (will be mutated).
* @param {number} offset - Index offset.
*/
function sort(string, array, offset) {
var l = array.length,
buckets = [],
i = l,
j = -1,
b,
d = 0,
bits;
while (i--)
j = Math.max(string[array[i] + offset], j);
bits = j >> 24 && 32 || j >> 16 && 24 || j >> 8 && 16 || 8;
for (; d < bits; d += 4) {
for (i = 16; i--;)
buckets[i] = [];
for (i = l; i--;)
buckets[((string[array[i] + offset]) >> d) & 15].push(array[i]);
for (b = 0; b < 16; b++) {
for (j = buckets[b].length; j--;)
array[++i] = buckets[b][j];
}
}
}
/**
* Comparison helper.
*/
function compare(string, lookup, m, n) {
return (
(string[m] - string[n]) ||
(m % 3 === 2 ?
(string[m + 1] - string[n + 1]) || (lookup[m + 2] - lookup[n + 2]) :
(lookup[m + 1] - lookup[n + 1]))
);
}
/**
* Recursive function used to build the suffix tree in linear time.
*
* @param {string|array} string - Padded sequence.
* @param {number} l - True length of sequence (unpadded).
* @return {array}
*/
function build(string, l) {
var a = [],
b = [],
al = (2 * l / 3) | 0,
bl = l - al,
r = (al + 1) >> 1,
i = al,
j = 0,
k,
lookup = [],
result = [];
if (l === 1)
return [0];
while (i--)
a[i] = ((i * 3) >> 1) + 1;
for (i = 3; i--;)
sort(string, a, i);
j = b[((a[0] / 3) | 0) + (a[0] % 3 === 1 ? 0 : r)] = 1;
for (i = 1; i < al; i++) {
if (string[a[i]] !== string[a[i - 1]] ||
string[a[i] + 1] !== string[a[i - 1] + 1] ||
string[a[i] + 2] !== string[a[i - 1] + 2])
j++;
b[((a[i] / 3) | 0) + (a[i] % 3 === 1 ? 0 : r)] = j;
}
if (j < al) {
b = build(b, al);
for (i = al; i--;)
a[i] = b[i] < r ? b[i] * 3 + 1 : ((b[i] - r) * 3 + 2);
}
for (i = al; i--;)
lookup[a[i]] = i;
lookup[l] = -1;
lookup[l + 1] = -2;
b = l % 3 === 1 ? [l - 1] : [];
for (i = 0; i < al; i++) {
if (a[i] % 3 === 1)
b.push(a[i] - 1);
}
sort(string, b, 0);
for (i = 0, j = 0, k = 0; i < al && j < bl;)
result[k++] = (
compare(string, lookup, a[i], b[j]) < 0 ?
a[i++] :
b[j++]
);
while (i < al)
result[k++] = a[i++];
while (j < bl)
result[k++] = b[j++];
return result;
}
/**
* Function used to create the array we are going to work on.
*
* @param {string|array} target - Target sequence.
* @return {array}
*/
function convert(target) {
// Creating the alphabet array
var length = target.length,
paddingOffset = length % 3,
array = new Array(length + paddingOffset),
l,
i;
// If we have an arbitrary sequence, we need to transform it
if (typeof target !== 'string') {
var uniqueTokens = Object.create(null);
for (i = 0; i < length; i++) {
if (!uniqueTokens[target[i]])
uniqueTokens[target[i]] = true;
}
var alphabet = Object.create(null),
sortedUniqueTokens = Object.keys(uniqueTokens).sort();
for (i = 0, l = sortedUniqueTokens.length; i < l; i++)
alphabet[sortedUniqueTokens[i]] = i + 1;
for (i = 0; i < length; i++) {
array[i] = alphabet[target[i]];
}
}
else {
for (i = 0; i < length; i++)
array[i] = target.charCodeAt(i);
}
// Padding the array
for (i = length; i < length + paddingOffset; i++)
array[i] = 0;
return array;
}
/**
* Suffix Array.
*
* @constructor
* @param {string|array} string - Sequence for which to build the suffix array.
*/
function SuffixArray(string) {
// Properties
this.hasArbitrarySequence = typeof string !== 'string';
this.string = string;
this.length = string.length;
// Building the array
this.array = build(convert(string), this.length);
}
/**
* Convenience known methods.
*/
SuffixArray.prototype.toString = function() {
return this.array.join(',');
};
SuffixArray.prototype.toJSON = function() {
return this.array;
};
SuffixArray.prototype.inspect = function() {
var array = new Array(this.length);
for (var i = 0; i < this.length; i++)
array[i] = this.string.slice(this.array[i]);
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: SuffixArray,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
SuffixArray.prototype[Symbol.for('nodejs.util.inspect.custom')] = SuffixArray.prototype.inspect;
/**
* Generalized Suffix Array.
*
* @constructor
*/
function GeneralizedSuffixArray(strings) {
// Properties
this.hasArbitrarySequence = typeof strings[0] !== 'string';
this.size = strings.length;
if (this.hasArbitrarySequence) {
this.text = [];
for (var i = 0, l = this.size; i < l; i++) {
this.text.push.apply(this.text, strings[i]);
if (i < l - 1)
this.text.push(SEPARATOR);
}
}
else {
this.text = strings.join(SEPARATOR);
}
this.firstLength = strings[0].length;
this.length = this.text.length;
// Building the array
this.array = build(convert(this.text), this.length);
}
/**
* Method used to retrieve the longest common subsequence of the generalized
* suffix array.
*
* @return {string|array}
*/
GeneralizedSuffixArray.prototype.longestCommonSubsequence = function() {
var lcs = this.hasArbitrarySequence ? [] : '',
lcp,
i,
j,
s,
t;
for (i = 1; i < this.length; i++) {
s = this.array[i];
t = this.array[i - 1];
if (s < this.firstLength &&
t < this.firstLength)
continue;
if (s > this.firstLength &&
t > this.firstLength)
continue;
lcp = Math.min(this.length - s, this.length - t);
for (j = 0; j < lcp; j++) {
if (this.text[s + j] !== this.text[t + j]) {
lcp = j;
break;
}
}
if (lcp > lcs.length)
lcs = this.text.slice(s, s + lcp);
}
return lcs;
};
/**
* Convenience known methods.
*/
GeneralizedSuffixArray.prototype.toString = function() {
return this.array.join(',');
};
GeneralizedSuffixArray.prototype.toJSON = function() {
return this.array;
};
GeneralizedSuffixArray.prototype.inspect = function() {
var array = new Array(this.length);
for (var i = 0; i < this.length; i++)
array[i] = this.text.slice(this.array[i]);
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: GeneralizedSuffixArray,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
GeneralizedSuffixArray.prototype[Symbol.for('nodejs.util.inspect.custom')] = GeneralizedSuffixArray.prototype.inspect;
/**
* Exporting.
*/
SuffixArray.GeneralizedSuffixArray = GeneralizedSuffixArray;
module.exports = SuffixArray;

View File

@@ -1,33 +0,0 @@
/**
* Mnemonist SymSpell Typings
* ===========================
*/
type SymSpellVerbosity = 0 | 1 | 2;
type SymSpellOptions = {
maxDistance?: number;
verbosity?: SymSpellVerbosity
};
type SymSpellMatch = {
term: string;
distance: number;
count: number;
}
export default class SymSpell {
// Members
size: number;
// Constructor
constructor(options?: SymSpellOptions);
// Methods
clear(): void;
add(string: string): this;
search(query: string): Array<SymSpellMatch>;
// Statics
static from(strings: Iterable<string> | {[key: string]: string}, options?: SymSpellOptions): SymSpell;
}

View File

@@ -1,547 +0,0 @@
/* eslint no-loop-func: 0 */
/**
* Mnemonist SymSpell
* ===================
*
* JavaScript implementation of the Symmetric Delete Spelling dictionary to
* efficiently index & query expression based on edit distance.
* Note that the current implementation target the v3.0 of the algorithm.
*
* [Reference]:
* http://blog.faroo.com/2012/06/07/improved-edit-distance-based-spelling-correction/
* https://github.com/wolfgarbe/symspell
*
* [Author]:
* Wolf Garbe
*/
var forEach = require('obliterator/foreach');
/**
* Constants.
*/
var DEFAULT_MAX_DISTANCE = 2,
DEFAULT_VERBOSITY = 2;
var VERBOSITY = new Set([
// Returns only the top suggestion
0,
// Returns suggestions with the smallest edit distance
1,
// Returns every suggestion (no early termination)
2
]);
var VERBOSITY_EXPLANATIONS = {
0: 'Returns only the top suggestion',
1: 'Returns suggestions with the smallest edit distance',
2: 'Returns every suggestion (no early termination)'
};
/**
* Functions.
*/
/**
* Function creating a dictionary item.
*
* @param {number} [value] - An optional suggestion.
* @return {object} - The created item.
*/
function createDictionaryItem(value) {
var suggestions = new Set();
if (typeof value === 'number')
suggestions.add(value);
return {
suggestions,
count: 0
};
}
/**
* Function creating a suggestion item.
*
* @return {object} - The created item.
*/
function createSuggestionItem(term, distance, count) {
return {
term: term || '',
distance: distance || 0,
count: count || 0
};
}
/**
* Simplified edit function.
*
* @param {string} word - Target word.
* @param {number} distance - Distance.
* @param {number} max - Max distance.
* @param {Set} [deletes] - Set mutated to store deletes.
*/
function edits(word, distance, max, deletes) {
deletes = deletes || new Set();
distance++;
var deletedItem,
l = word.length,
i;
if (l > 1) {
for (i = 0; i < l; i++) {
deletedItem = word.substring(0, i) + word.substring(i + 1);
if (!deletes.has(deletedItem)) {
deletes.add(deletedItem);
if (distance < max)
edits(deletedItem, distance, max, deletes);
}
}
}
return deletes;
}
/**
* Function used to conditionally add suggestions.
*
* @param {array} words - Words list.
* @param {number} verbosity - Verbosity level.
* @param {object} item - The target item.
* @param {string} suggestion - The target suggestion.
* @param {number} int - Integer key of the word.
* @param {object} deletedItem - Considered deleted item.
* @param {SymSpell}
*/
function addLowestDistance(words, verbosity, item, suggestion, int, deletedItem) {
var first = item.suggestions.values().next().value;
if (verbosity < 2 &&
item.suggestions.size > 0 &&
words[first].length - deletedItem.length > suggestion.length - deletedItem.length) {
item.suggestions = new Set();
item.count = 0;
}
if (verbosity === 2 ||
!item.suggestions.size ||
words[first].length - deletedItem.length >= suggestion.length - deletedItem.length) {
item.suggestions.add(int);
}
}
/**
* Custom Damerau-Levenshtein used by the algorithm.
*
* @param {string} source - First string.
* @param {string} target - Second string.
* @return {number} - The distance.
*/
function damerauLevenshtein(source, target) {
var m = source.length,
n = target.length,
H = [[]],
INF = m + n,
sd = new Map(),
i,
l,
j;
H[0][0] = INF;
for (i = 0; i <= m; i++) {
if (!H[i + 1])
H[i + 1] = [];
H[i + 1][1] = i;
H[i + 1][0] = INF;
}
for (j = 0; j <= n; j++) {
H[1][j + 1] = j;
H[0][j + 1] = INF;
}
var st = source + target,
letter;
for (i = 0, l = st.length; i < l; i++) {
letter = st[i];
if (!sd.has(letter))
sd.set(letter, 0);
}
// Iterating
for (i = 1; i <= m; i++) {
var DB = 0;
for (j = 1; j <= n; j++) {
var i1 = sd.get(target[j - 1]),
j1 = DB;
if (source[i - 1] === target[j - 1]) {
H[i + 1][j + 1] = H[i][j];
DB = j;
}
else {
H[i + 1][j + 1] = Math.min(
H[i][j],
H[i + 1][j],
H[i][j + 1]
) + 1;
}
H[i + 1][j + 1] = Math.min(
H[i + 1][j + 1],
H[i1][j1] + (i - i1 - 1) + 1 + (j - j1 - 1)
);
}
sd.set(source[i - 1], i);
}
return H[m + 1][n + 1];
}
/**
* Lookup function.
*
* @param {object} dictionary - A SymSpell dictionary.
* @param {array} words - Unique words list.
* @param {number} verbosity - Verbosity level.
* @param {number} maxDistance - Maximum distance.
* @param {number} maxLength - Maximum word length in the dictionary.
* @param {string} input - Input string.
* @return {array} - The list of suggestions.
*/
function lookup(dictionary, words, verbosity, maxDistance, maxLength, input) {
var length = input.length;
if (length - maxDistance > maxLength)
return [];
var candidates = [input],
candidateSet = new Set(),
suggestionSet = new Set();
var suggestions = [],
candidate,
item;
// Exhausting every candidates
while (candidates.length > 0) {
candidate = candidates.shift();
// Early termination
if (
verbosity < 2 &&
suggestions.length > 0 &&
length - candidate.length > suggestions[0].distance
)
break;
item = dictionary[candidate];
if (item !== undefined) {
if (typeof item === 'number')
item = createDictionaryItem(item);
if (item.count > 0 && !suggestionSet.has(candidate)) {
suggestionSet.add(candidate);
var suggestItem = createSuggestionItem(
candidate,
length - candidate.length,
item.count
);
suggestions.push(suggestItem);
// Another early termination
if (verbosity < 2 && length - candidate.length === 0)
break;
}
// Iterating over the item's suggestions
item.suggestions.forEach(index => {
var suggestion = words[index];
// Do we already have this suggestion?
if (suggestionSet.has(suggestion))
return;
suggestionSet.add(suggestion);
// Computing distance between candidate & suggestion
var distance = 0;
if (input !== suggestion) {
if (suggestion.length === candidate.length) {
distance = length - candidate.length;
}
else if (length === candidate.length) {
distance = suggestion.length - candidate.length;
}
else {
var ii = 0,
jj = 0;
var l = suggestion.length;
while (
ii < l &&
ii < length &&
suggestion[ii] === input[ii]
) {
ii++;
}
while (
jj < l - ii &&
jj < length &&
suggestion[l - jj - 1] === input[length - jj - 1]
) {
jj++;
}
if (ii > 0 || jj > 0) {
distance = damerauLevenshtein(
suggestion.substr(ii, l - ii - jj),
input.substr(ii, length - ii - jj)
);
}
else {
distance = damerauLevenshtein(suggestion, input);
}
}
}
// Removing suggestions of higher distance
if (verbosity < 2 &&
suggestions.length > 0 &&
suggestions[0].distance > distance) {
suggestions = [];
}
if (verbosity < 2 &&
suggestions.length > 0 &&
distance > suggestions[0].distance) {
return;
}
if (distance <= maxDistance) {
var target = dictionary[suggestion];
if (target !== undefined) {
suggestions.push(createSuggestionItem(
suggestion,
distance,
target.count
));
}
}
});
}
// Adding edits
if (length - candidate.length < maxDistance) {
if (verbosity < 2 &&
suggestions.length > 0 &&
length - candidate.length >= suggestions[0].distance)
continue;
for (var i = 0, l = candidate.length; i < l; i++) {
var deletedItem = (
candidate.substring(0, i) +
candidate.substring(i + 1)
);
if (!candidateSet.has(deletedItem)) {
candidateSet.add(deletedItem);
candidates.push(deletedItem);
}
}
}
}
if (verbosity === 0)
return suggestions.slice(0, 1);
return suggestions;
}
/**
* SymSpell.
*
* @constructor
*/
function SymSpell(options) {
options = options || {};
this.clear();
// Properties
this.maxDistance = typeof options.maxDistance === 'number' ?
options.maxDistance :
DEFAULT_MAX_DISTANCE;
this.verbosity = typeof options.verbosity === 'number' ?
options.verbosity :
DEFAULT_VERBOSITY;
// Sanity checks
if (typeof this.maxDistance !== 'number' || this.maxDistance <= 0)
throw Error('mnemonist/SymSpell.constructor: invalid `maxDistance` option. Should be a integer greater than 0.');
if (!VERBOSITY.has(this.verbosity))
throw Error('mnemonist/SymSpell.constructor: invalid `verbosity` option. Should be either 0, 1 or 2.');
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
SymSpell.prototype.clear = function() {
// Properties
this.size = 0;
this.dictionary = Object.create(null);
this.maxLength = 0;
this.words = [];
};
/**
* Method used to add a word to the index.
*
* @param {string} word - Word to add.
* @param {SymSpell}
*/
SymSpell.prototype.add = function(word) {
var item = this.dictionary[word];
if (item !== undefined) {
if (typeof item === 'number') {
item = createDictionaryItem(item);
this.dictionary[word] = item;
}
item.count++;
}
else {
item = createDictionaryItem();
item.count++;
this.dictionary[word] = item;
if (word.length > this.maxLength)
this.maxLength = word.length;
}
if (item.count === 1) {
var number = this.words.length;
this.words.push(word);
var deletes = edits(word, 0, this.maxDistance);
deletes.forEach(deletedItem => {
var target = this.dictionary[deletedItem];
if (target !== undefined) {
if (typeof target === 'number') {
target = createDictionaryItem(target);
this.dictionary[deletedItem] = target;
}
if (!target.suggestions.has(number)) {
addLowestDistance(
this.words,
this.verbosity,
target,
word,
number,
deletedItem
);
}
}
else {
this.dictionary[deletedItem] = number;
}
});
}
this.size++;
return this;
};
/**
* Method used to search the index.
*
* @param {string} input - Input query.
* @return {array} - The found suggestions.
*/
SymSpell.prototype.search = function(input) {
return lookup(
this.dictionary,
this.words,
this.verbosity,
this.maxDistance,
this.maxLength,
input
);
};
/**
* Convenience known methods.
*/
SymSpell.prototype.inspect = function() {
var array = [];
array.size = this.size;
array.maxDistance = this.maxDistance;
array.verbosity = this.verbosity;
array.behavior = VERBOSITY_EXPLANATIONS[this.verbosity];
for (var k in this.dictionary) {
if (typeof this.dictionary[k] === 'object' && this.dictionary[k].count)
array.push([k, this.dictionary[k].count]);
}
// Trick so that node displays the name of the constructor
Object.defineProperty(array, 'constructor', {
value: SymSpell,
enumerable: false
});
return array;
};
if (typeof Symbol !== 'undefined')
SymSpell.prototype[Symbol.for('nodejs.util.inspect.custom')] = SymSpell.prototype.inspect;
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a structure.
*
* @param {Iterable} iterable - Target iterable.
* @return {SymSpell}
*/
SymSpell.from = function(iterable, options) {
var index = new SymSpell(options);
forEach(iterable, function(value) {
index.add(value);
});
return index;
};
/**
* Exporting.
*/
module.exports = SymSpell;

View File

@@ -1,30 +0,0 @@
/**
* Mnemonist TrieMap Typings
* ==========================
*/
export default class TrieMap<K, V> implements Iterable<[K, V]> {
// Members
size: number;
// Constructor
constructor(Token?: new () => K);
// Methods
clear(): void;
set(prefix: K, value: V): this;
update(prefix: K, updateFunction: (oldValue: V | undefined) => V): this
get(prefix: K): V;
delete(prefix: K): boolean;
has(prefix: K): boolean;
find(prefix: K): Array<[K, V]>;
values(): IterableIterator<V>;
prefixes(): IterableIterator<K>;
keys(): IterableIterator<K>;
entries(): IterableIterator<[K, V]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
inspect(): any;
// Statics
static from<I, J>(iterable: Iterable<[I, J]> | {[key: string]: J}): TrieMap<I, J>;
}

View File

@@ -1,477 +0,0 @@
/**
* Mnemonist TrieMap
* ==================
*
* JavaScript TrieMap implementation based upon plain objects. As such this
* structure is more a convenience building upon the trie's advantages than
* a real performant alternative to already existing structures.
*
* Note that the Trie is based upon the TrieMap since the underlying machine
* is the very same. The Trie just does not let you set values and only
* considers the existence of the given prefixes.
*/
var forEach = require('obliterator/foreach'),
Iterator = require('obliterator/iterator');
/**
* Constants.
*/
var SENTINEL = String.fromCharCode(0);
/**
* TrieMap.
*
* @constructor
*/
function TrieMap(Token) {
this.mode = Token === Array ? 'array' : 'string';
this.clear();
}
/**
* Method used to clear the trie.
*
* @return {undefined}
*/
TrieMap.prototype.clear = function() {
// Properties
this.root = {};
this.size = 0;
};
/**
* Method used to set the value of the given prefix in the trie.
*
* @param {string|array} prefix - Prefix to follow.
* @param {any} value - Value for the prefix.
* @return {TrieMap}
*/
TrieMap.prototype.set = function(prefix, value) {
var node = this.root,
token;
for (var i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token] || (node[token] = {});
}
// Do we need to increase size?
if (!(SENTINEL in node))
this.size++;
node[SENTINEL] = value;
return this;
};
/**
* Method used to update the value of the given prefix in the trie.
*
* @param {string|array} prefix - Prefix to follow.
* @param {(oldValue: any | undefined) => any} updateFunction - Update value visitor callback.
* @return {TrieMap}
*/
TrieMap.prototype.update = function(prefix, updateFunction) {
var node = this.root,
token;
for (var i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token] || (node[token] = {});
}
// Do we need to increase size?
if (!(SENTINEL in node))
this.size++;
node[SENTINEL] = updateFunction(node[SENTINEL]);
return this;
};
/**
* Method used to return the value sitting at the end of the given prefix or
* undefined if none exist.
*
* @param {string|array} prefix - Prefix to follow.
* @return {any|undefined}
*/
TrieMap.prototype.get = function(prefix) {
var node = this.root,
token,
i,
l;
for (i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token];
// Prefix does not exist
if (typeof node === 'undefined')
return;
}
if (!(SENTINEL in node))
return;
return node[SENTINEL];
};
/**
* Method used to delete a prefix from the trie.
*
* @param {string|array} prefix - Prefix to delete.
* @return {boolean}
*/
TrieMap.prototype.delete = function(prefix) {
var node = this.root,
toPrune = null,
tokenToPrune = null,
parent,
token,
i,
l;
for (i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
parent = node;
node = node[token];
// Prefix does not exist
if (typeof node === 'undefined')
return false;
// Keeping track of a potential branch to prune
if (toPrune !== null) {
if (Object.keys(node).length > 1) {
toPrune = null;
tokenToPrune = null;
}
}
else {
if (Object.keys(node).length < 2) {
toPrune = parent;
tokenToPrune = token;
}
}
}
if (!(SENTINEL in node))
return false;
this.size--;
if (toPrune)
delete toPrune[tokenToPrune];
else
delete node[SENTINEL];
return true;
};
// TODO: add #.prune?
/**
* Method used to assert whether the given prefix exists in the TrieMap.
*
* @param {string|array} prefix - Prefix to check.
* @return {boolean}
*/
TrieMap.prototype.has = function(prefix) {
var node = this.root,
token;
for (var i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token];
if (typeof node === 'undefined')
return false;
}
return SENTINEL in node;
};
/**
* Method used to retrieve every item in the trie with the given prefix.
*
* @param {string|array} prefix - Prefix to query.
* @return {array}
*/
TrieMap.prototype.find = function(prefix) {
var isString = typeof prefix === 'string';
var node = this.root,
matches = [],
token,
i,
l;
for (i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token];
if (typeof node === 'undefined')
return matches;
}
// Performing DFS from prefix
var nodeStack = [node],
prefixStack = [prefix],
k;
while (nodeStack.length) {
prefix = prefixStack.pop();
node = nodeStack.pop();
for (k in node) {
if (k === SENTINEL) {
matches.push([prefix, node[SENTINEL]]);
continue;
}
nodeStack.push(node[k]);
prefixStack.push(isString ? prefix + k : prefix.concat(k));
}
}
return matches;
};
/**
* Method returning an iterator over the trie's values.
*
* @param {string|array} [prefix] - Optional starting prefix.
* @return {Iterator}
*/
TrieMap.prototype.values = function(prefix) {
var node = this.root,
nodeStack = [],
token,
i,
l;
// Resolving initial prefix
if (prefix) {
for (i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token];
// If the prefix does not exist, we return an empty iterator
if (typeof node === 'undefined')
return Iterator.empty();
}
}
nodeStack.push(node);
return new Iterator(function() {
var currentNode,
hasValue = false,
k;
while (nodeStack.length) {
currentNode = nodeStack.pop();
for (k in currentNode) {
if (k === SENTINEL) {
hasValue = true;
continue;
}
nodeStack.push(currentNode[k]);
}
if (hasValue)
return {done: false, value: currentNode[SENTINEL]};
}
return {done: true};
});
};
/**
* Method returning an iterator over the trie's prefixes.
*
* @param {string|array} [prefix] - Optional starting prefix.
* @return {Iterator}
*/
TrieMap.prototype.prefixes = function(prefix) {
var node = this.root,
nodeStack = [],
prefixStack = [],
token,
i,
l;
var isString = this.mode === 'string';
// Resolving initial prefix
if (prefix) {
for (i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token];
// If the prefix does not exist, we return an empty iterator
if (typeof node === 'undefined')
return Iterator.empty();
}
}
else {
prefix = isString ? '' : [];
}
nodeStack.push(node);
prefixStack.push(prefix);
return new Iterator(function() {
var currentNode,
currentPrefix,
hasValue = false,
k;
while (nodeStack.length) {
currentNode = nodeStack.pop();
currentPrefix = prefixStack.pop();
for (k in currentNode) {
if (k === SENTINEL) {
hasValue = true;
continue;
}
nodeStack.push(currentNode[k]);
prefixStack.push(isString ? currentPrefix + k : currentPrefix.concat(k));
}
if (hasValue)
return {done: false, value: currentPrefix};
}
return {done: true};
});
};
TrieMap.prototype.keys = TrieMap.prototype.prefixes;
/**
* Method returning an iterator over the trie's entries.
*
* @param {string|array} [prefix] - Optional starting prefix.
* @return {Iterator}
*/
TrieMap.prototype.entries = function(prefix) {
var node = this.root,
nodeStack = [],
prefixStack = [],
token,
i,
l;
var isString = this.mode === 'string';
// Resolving initial prefix
if (prefix) {
for (i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token];
// If the prefix does not exist, we return an empty iterator
if (typeof node === 'undefined')
return Iterator.empty();
}
}
else {
prefix = isString ? '' : [];
}
nodeStack.push(node);
prefixStack.push(prefix);
return new Iterator(function() {
var currentNode,
currentPrefix,
hasValue = false,
k;
while (nodeStack.length) {
currentNode = nodeStack.pop();
currentPrefix = prefixStack.pop();
for (k in currentNode) {
if (k === SENTINEL) {
hasValue = true;
continue;
}
nodeStack.push(currentNode[k]);
prefixStack.push(isString ? currentPrefix + k : currentPrefix.concat(k));
}
if (hasValue)
return {done: false, value: [currentPrefix, currentNode[SENTINEL]]};
}
return {done: true};
});
};
/**
* Attaching the #.entries method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
TrieMap.prototype[Symbol.iterator] = TrieMap.prototype.entries;
/**
* Convenience known methods.
*/
TrieMap.prototype.inspect = function() {
var proxy = new Array(this.size);
var iterator = this.entries(),
step,
i = 0;
while ((step = iterator.next(), !step.done))
proxy[i++] = step.value;
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: TrieMap,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
TrieMap.prototype[Symbol.for('nodejs.util.inspect.custom')] = TrieMap.prototype.inspect;
TrieMap.prototype.toJSON = function() {
return this.root;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a trie.
*
* @param {Iterable} iterable - Target iterable.
* @return {TrieMap}
*/
TrieMap.from = function(iterable) {
var trie = new TrieMap();
forEach(iterable, function(value, key) {
trie.set(key, value);
});
return trie;
};
/**
* Exporting.
*/
TrieMap.SENTINEL = SENTINEL;
module.exports = TrieMap;

View File

@@ -1,26 +0,0 @@
/**
* Mnemonist Trie Typings
* =======================
*/
export default class Trie<T> implements Iterable<T> {
// Members
size: number;
// Constructor
constructor(Token?: new () => T);
// Methods
clear(): void;
add(prefix: T): this;
delete(prefix: T): boolean;
has(prefix: T): boolean;
find(prefix: T): Array<T>;
prefixes(): IterableIterator<T>;
keys(): IterableIterator<T>;
[Symbol.iterator](): IterableIterator<T>;
inspect(): any;
// Statics
static from<I>(iterable: Iterable<I> | {[key: string]: I}): Trie<I>;
}

View File

@@ -1,167 +0,0 @@
/**
* Mnemonist Trie
* ===============
*
* JavaScript Trie implementation based upon plain objects. As such this
* structure is more a convenience building upon the trie's advantages than
* a real performant alternative to already existing structures.
*
* Note that the Trie is based upon the TrieMap since the underlying machine
* is the very same. The Trie just does not let you set values and only
* considers the existence of the given prefixes.
*/
var forEach = require('obliterator/foreach'),
TrieMap = require('./trie-map.js');
/**
* Constants.
*/
var SENTINEL = String.fromCharCode(0);
/**
* Trie.
*
* @constructor
*/
function Trie(Token) {
this.mode = Token === Array ? 'array' : 'string';
this.clear();
}
// Re-using TrieMap's prototype
for (var methodName in TrieMap.prototype)
Trie.prototype[methodName] = TrieMap.prototype[methodName];
// Dropping irrelevant methods
delete Trie.prototype.set;
delete Trie.prototype.get;
delete Trie.prototype.values;
delete Trie.prototype.entries;
/**
* Method used to add the given prefix to the trie.
*
* @param {string|array} prefix - Prefix to follow.
* @return {TrieMap}
*/
Trie.prototype.add = function(prefix) {
var node = this.root,
token;
for (var i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token] || (node[token] = {});
}
// Do we need to increase size?
if (!(SENTINEL in node))
this.size++;
node[SENTINEL] = true;
return this;
};
/**
* Method used to retrieve every item in the trie with the given prefix.
*
* @param {string|array} prefix - Prefix to query.
* @return {array}
*/
Trie.prototype.find = function(prefix) {
var isString = typeof prefix === 'string';
var node = this.root,
matches = [],
token,
i,
l;
for (i = 0, l = prefix.length; i < l; i++) {
token = prefix[i];
node = node[token];
if (typeof node === 'undefined')
return matches;
}
// Performing DFS from prefix
var nodeStack = [node],
prefixStack = [prefix],
k;
while (nodeStack.length) {
prefix = prefixStack.pop();
node = nodeStack.pop();
for (k in node) {
if (k === SENTINEL) {
matches.push(prefix);
continue;
}
nodeStack.push(node[k]);
prefixStack.push(isString ? prefix + k : prefix.concat(k));
}
}
return matches;
};
/**
* Attaching the #.keys method to Symbol.iterator if possible.
*/
if (typeof Symbol !== 'undefined')
Trie.prototype[Symbol.iterator] = Trie.prototype.keys;
/**
* Convenience known methods.
*/
Trie.prototype.inspect = function() {
var proxy = new Set();
var iterator = this.keys(),
step;
while ((step = iterator.next(), !step.done))
proxy.add(step.value);
// Trick so that node displays the name of the constructor
Object.defineProperty(proxy, 'constructor', {
value: Trie,
enumerable: false
});
return proxy;
};
if (typeof Symbol !== 'undefined')
Trie.prototype[Symbol.for('nodejs.util.inspect.custom')] = Trie.prototype.inspect;
Trie.prototype.toJSON = function() {
return this.root;
};
/**
* Static @.from function taking an arbitrary iterable & converting it into
* a trie.
*
* @param {Iterable} iterable - Target iterable.
* @return {Trie}
*/
Trie.from = function(iterable) {
var trie = new Trie();
forEach(iterable, function(value) {
trie.add(value);
});
return trie;
};
/**
* Exporting.
*/
Trie.SENTINEL = SENTINEL;
module.exports = Trie;

View File

@@ -1,216 +0,0 @@
/**
* Mnemonist Binary Search Helpers
* ================================
*
* Typical binary search functions.
*/
/**
* Function returning the index of the search value in the array or `-1` if
* not found.
*
* @param {array} array - Haystack.
* @param {any} value - Needle.
* @return {number}
*/
exports.search = function(array, value, lo, hi) {
var mid = 0;
lo = typeof lo !== 'undefined' ? lo : 0;
hi = typeof hi !== 'undefined' ? hi : array.length;
hi--;
var current;
while (lo <= hi) {
mid = (lo + hi) >>> 1;
current = array[mid];
if (current > value) {
hi = ~-mid;
}
else if (current < value) {
lo = -~mid;
}
else {
return mid;
}
}
return -1;
};
/**
* Same as above, but can use a custom comparator function.
*
* @param {function} comparator - Custom comparator function.
* @param {array} array - Haystack.
* @param {any} value - Needle.
* @return {number}
*/
exports.searchWithComparator = function(comparator, array, value) {
var mid = 0,
lo = 0,
hi = ~-array.length,
comparison;
while (lo <= hi) {
mid = (lo + hi) >>> 1;
comparison = comparator(array[mid], value);
if (comparison > 0) {
hi = ~-mid;
}
else if (comparison < 0) {
lo = -~mid;
}
else {
return mid;
}
}
return -1;
};
/**
* Function returning the lower bound of the given value in the array.
*
* @param {array} array - Haystack.
* @param {any} value - Needle.
* @param {number} [lo] - Start index.
* @param {numner} [hi] - End index.
* @return {number}
*/
exports.lowerBound = function(array, value, lo, hi) {
var mid = 0;
lo = typeof lo !== 'undefined' ? lo : 0;
hi = typeof hi !== 'undefined' ? hi : array.length;
while (lo < hi) {
mid = (lo + hi) >>> 1;
if (value <= array[mid]) {
hi = mid;
}
else {
lo = -~mid;
}
}
return lo;
};
/**
* Same as above, but can use a custom comparator function.
*
* @param {function} comparator - Custom comparator function.
* @param {array} array - Haystack.
* @param {any} value - Needle.
* @return {number}
*/
exports.lowerBoundWithComparator = function(comparator, array, value) {
var mid = 0,
lo = 0,
hi = array.length;
while (lo < hi) {
mid = (lo + hi) >>> 1;
if (comparator(value, array[mid]) <= 0) {
hi = mid;
}
else {
lo = -~mid;
}
}
return lo;
};
/**
* Same as above, but can work on sorted indices.
*
* @param {array} array - Haystack.
* @param {array} array - Indices.
* @param {any} value - Needle.
* @return {number}
*/
exports.lowerBoundIndices = function(array, indices, value, lo, hi) {
var mid = 0;
lo = typeof lo !== 'undefined' ? lo : 0;
hi = typeof hi !== 'undefined' ? hi : array.length;
while (lo < hi) {
mid = (lo + hi) >>> 1;
if (value <= array[indices[mid]]) {
hi = mid;
}
else {
lo = -~mid;
}
}
return lo;
};
/**
* Function returning the upper bound of the given value in the array.
*
* @param {array} array - Haystack.
* @param {any} value - Needle.
* @param {number} [lo] - Start index.
* @param {numner} [hi] - End index.
* @return {number}
*/
exports.upperBound = function(array, value, lo, hi) {
var mid = 0;
lo = typeof lo !== 'undefined' ? lo : 0;
hi = typeof hi !== 'undefined' ? hi : array.length;
while (lo < hi) {
mid = (lo + hi) >>> 1;
if (value >= array[mid]) {
lo = -~mid;
}
else {
hi = mid;
}
}
return lo;
};
/**
* Same as above, but can use a custom comparator function.
*
* @param {function} comparator - Custom comparator function.
* @param {array} array - Haystack.
* @param {any} value - Needle.
* @return {number}
*/
exports.upperBoundWithComparator = function(comparator, array, value) {
var mid = 0,
lo = 0,
hi = array.length;
while (lo < hi) {
mid = (lo + hi) >>> 1;
if (comparator(value, array[mid]) >= 0) {
lo = -~mid;
}
else {
hi = mid;
}
}
return lo;
};

View File

@@ -1,109 +0,0 @@
/**
* Mnemonist Bitwise Helpers
* ==========================
*
* Miscellaneous helpers helping with bitwise operations.
*/
/**
* Takes a 32 bits integer and returns its MSB using SWAR strategy.
*
* @param {number} x - Target number.
* @return {number}
*/
function msb32(x) {
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return (x & ~(x >> 1));
}
exports.msb32 = msb32;
/**
* Takes a byte and returns its MSB using SWAR strategy.
*
* @param {number} x - Target number.
* @return {number}
*/
function msb8(x) {
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
return (x & ~(x >> 1));
}
exports.msb8 = msb8;
/**
* Takes a number and return bit at position.
*
* @param {number} x - Target number.
* @param {number} pos - Position.
* @return {number}
*/
exports.test = function(x, pos) {
return (x >> pos) & 1;
};
/**
* Compare two bytes and return their critical bit.
*
* @param {number} a - First byte.
* @param {number} b - Second byte.
* @return {number}
*/
exports.criticalBit8 = function(a, b) {
return msb8(a ^ b);
};
exports.criticalBit8Mask = function(a, b) {
return (~msb8(a ^ b) >>> 0) & 0xff;
};
exports.testCriticalBit8 = function(x, mask) {
return (1 + (x | mask)) >> 8;
};
exports.criticalBit32Mask = function(a, b) {
return (~msb32(a ^ b) >>> 0) & 0xffffffff;
};
/**
* Takes a 32 bits integer and returns its population count (number of 1 of
* the binary representation).
*
* @param {number} x - Target number.
* @return {number}
*/
exports.popcount = function(x) {
x -= x >> 1 & 0x55555555;
x = (x & 0x33333333) + (x >> 2 & 0x33333333);
x = x + (x >> 4) & 0x0f0f0f0f;
x += x >> 8;
x += x >> 16;
return x & 0x7f;
};
/**
* Slightly faster popcount function based on a precomputed table of 8bits
* words.
*
* @param {number} x - Target number.
* @return {number}
*/
var TABLE8 = new Uint8Array(Math.pow(2, 8));
for (var i = 0, l = TABLE8.length; i < l; i++)
TABLE8[i] = exports.popcount(i);
exports.table8Popcount = function(x) {
return (
TABLE8[x & 0xff] +
TABLE8[(x >> 8) & 0xff] +
TABLE8[(x >> 16) & 0xff] +
TABLE8[(x >> 24) & 0xff]
);
};

View File

@@ -1,79 +0,0 @@
/**
* Mnemonist Heap Comparators
* ===========================
*
* Default comparators & functions dealing with comparators reversing etc.
*/
var DEFAULT_COMPARATOR = function(a, b) {
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
};
var DEFAULT_REVERSE_COMPARATOR = function(a, b) {
if (a < b)
return 1;
if (a > b)
return -1;
return 0;
};
/**
* Function used to reverse a comparator.
*/
function reverseComparator(comparator) {
return function(a, b) {
return comparator(b, a);
};
}
/**
* Function returning a tuple comparator.
*/
function createTupleComparator(size) {
if (size === 2) {
return function(a, b) {
if (a[0] < b[0])
return -1;
if (a[0] > b[0])
return 1;
if (a[1] < b[1])
return -1;
if (a[1] > b[1])
return 1;
return 0;
};
}
return function(a, b) {
var i = 0;
while (i < size) {
if (a[i] < b[i])
return -1;
if (a[i] > b[i])
return 1;
i++;
}
return 0;
};
}
/**
* Exporting.
*/
exports.DEFAULT_COMPARATOR = DEFAULT_COMPARATOR;
exports.DEFAULT_REVERSE_COMPARATOR = DEFAULT_REVERSE_COMPARATOR;
exports.reverseComparator = reverseComparator;
exports.createTupleComparator = createTupleComparator;

View File

@@ -1,107 +0,0 @@
/* eslint no-constant-condition: 0 */
/**
* Mnemonist Hashtable Helpers
* ============================
*
* Miscellaneous helpers helper function dealing with hashtables.
*/
function jenkinsInt32(a) {
a = (a + 0x7ed55d16) + (a << 12);
a = (a ^ 0xc761c23c) ^ (a >> 19);
a = (a + 0x165667b1) + (a << 5);
a = (a + 0xd3a2646c) ^ (a << 9);
a = (a + 0xfd7046c5) + (a << 3);
a = (a ^ 0xb55a4f09) ^ (a >> 16);
return a;
}
function linearProbingGet(hash, keys, values, key) {
var n = keys.length,
j = hash(key) & (n - 1),
i = j;
var c;
while (true) {
c = keys[i];
if (c === key)
return values[i];
else if (c === 0)
return;
// Handling wrapping around
i += 1;
i %= n;
// Full turn
if (i === j)
return;
}
}
function linearProbingHas(hash, keys, key) {
var n = keys.length,
j = hash(key) & (n - 1),
i = j;
var c;
while (true) {
c = keys[i];
if (c === key)
return true;
else if (c === 0)
return false;
// Handling wrapping around
i += 1;
i %= n;
// Full turn
if (i === j)
return false;
}
}
function linearProbingSet(hash, keys, values, key, value) {
var n = keys.length,
j = hash(key) & (n - 1),
i = j;
var c;
while (true) {
c = keys[i];
if (c === 0 || c === key)
break;
// Handling wrapping around
i += 1;
i %= n;
// Full turn
if (i === j)
throw new Error('mnemonist/utils/hash-tables.linearProbingSet: table is full.');
}
keys[i] = key;
values[i] = value;
}
module.exports = {
hashes: {
jenkinsInt32: jenkinsInt32
},
linearProbing: {
get: linearProbingGet,
has: linearProbingHas,
set: linearProbingSet
}
};

View File

@@ -1,93 +0,0 @@
/**
* Mnemonist Iterable Function
* ============================
*
* Harmonized iteration helpers over mixed iterable targets.
*/
var forEach = require('obliterator/foreach');
var typed = require('./typed-arrays.js');
/**
* Function used to determine whether the given object supports array-like
* random access.
*
* @param {any} target - Target object.
* @return {boolean}
*/
function isArrayLike(target) {
return Array.isArray(target) || typed.isTypedArray(target);
}
/**
* Function used to guess the length of the structure over which we are going
* to iterate.
*
* @param {any} target - Target object.
* @return {number|undefined}
*/
function guessLength(target) {
if (typeof target.length === 'number')
return target.length;
if (typeof target.size === 'number')
return target.size;
return;
}
/**
* Function used to convert an iterable to an array.
*
* @param {any} target - Iteration target.
* @return {array}
*/
function toArray(target) {
var l = guessLength(target);
var array = typeof l === 'number' ? new Array(l) : [];
var i = 0;
// TODO: we could optimize when given target is array like
forEach(target, function(value) {
array[i++] = value;
});
return array;
}
/**
* Same as above but returns a supplementary indices array.
*
* @param {any} target - Iteration target.
* @return {array}
*/
function toArrayWithIndices(target) {
var l = guessLength(target);
var IndexArray = typeof l === 'number' ?
typed.getPointerArray(l) :
Array;
var array = typeof l === 'number' ? new Array(l) : [];
var indices = typeof l === 'number' ? new IndexArray(l) : [];
var i = 0;
// TODO: we could optimize when given target is array like
forEach(target, function(value) {
array[i] = value;
indices[i] = i++;
});
return [array, indices];
}
/**
* Exporting.
*/
exports.isArrayLike = isArrayLike;
exports.guessLength = guessLength;
exports.toArray = toArray;
exports.toArrayWithIndices = toArrayWithIndices;

View File

@@ -1,563 +0,0 @@
/* eslint no-constant-condition: 0 */
/**
* Mnemonist Merge Helpers
* ========================
*
* Various merge algorithms used to handle sorted lists. Note that the given
* functions are optimized and won't accept mixed arguments.
*
* Note: maybe this piece of code belong to sortilege, along with binary-search.
*/
var typed = require('./typed-arrays.js'),
isArrayLike = require('./iterables.js').isArrayLike,
binarySearch = require('./binary-search.js'),
FibonacciHeap = require('../fibonacci-heap.js');
// TODO: update to use exponential search
// TODO: when not knowing final length => should use plain arrays rather than
// same type as input
/**
* Merge two sorted array-like structures into one.
*
* @param {array} a - First array.
* @param {array} b - Second array.
* @return {array}
*/
function mergeArrays(a, b) {
// One of the arrays is empty
if (a.length === 0)
return b.slice();
if (b.length === 0)
return a.slice();
// Finding min array
var tmp;
if (a[0] > b[0]) {
tmp = a;
a = b;
b = tmp;
}
// If array have non overlapping ranges, we can just concatenate them
var aEnd = a[a.length - 1],
bStart = b[0];
if (aEnd <= bStart) {
if (typed.isTypedArray(a))
return typed.concat(a, b);
return a.concat(b);
}
// Initializing target
var array = new a.constructor(a.length + b.length);
// Iterating until we overlap
var i, l, v;
for (i = 0, l = a.length; i < l; i++) {
v = a[i];
if (v <= bStart)
array[i] = v;
else
break;
}
// Handling overlap
var aPointer = i,
aLength = a.length,
bPointer = 0,
bLength = b.length,
aHead,
bHead;
while (aPointer < aLength && bPointer < bLength) {
aHead = a[aPointer];
bHead = b[bPointer];
if (aHead <= bHead) {
array[i++] = aHead;
aPointer++;
}
else {
array[i++] = bHead;
bPointer++;
}
}
// Filling
while (aPointer < aLength)
array[i++] = a[aPointer++];
while (bPointer < bLength)
array[i++] = b[bPointer++];
return array;
}
/**
* Perform the union of two already unique sorted array-like structures into one.
*
* @param {array} a - First array.
* @param {array} b - Second array.
* @return {array}
*/
function unionUniqueArrays(a, b) {
// One of the arrays is empty
if (a.length === 0)
return b.slice();
if (b.length === 0)
return a.slice();
// Finding min array
var tmp;
if (a[0] > b[0]) {
tmp = a;
a = b;
b = tmp;
}
// If array have non overlapping ranges, we can just concatenate them
var aEnd = a[a.length - 1],
bStart = b[0];
if (aEnd < bStart) {
if (typed.isTypedArray(a))
return typed.concat(a, b);
return a.concat(b);
}
// Initializing target
var array = new a.constructor();
// Iterating until we overlap
var i, l, v;
for (i = 0, l = a.length; i < l; i++) {
v = a[i];
if (v < bStart)
array.push(v);
else
break;
}
// Handling overlap
var aPointer = i,
aLength = a.length,
bPointer = 0,
bLength = b.length,
aHead,
bHead;
while (aPointer < aLength && bPointer < bLength) {
aHead = a[aPointer];
bHead = b[bPointer];
if (aHead <= bHead) {
if (array.length === 0 || array[array.length - 1] !== aHead)
array.push(aHead);
aPointer++;
}
else {
if (array.length === 0 || array[array.length - 1] !== bHead)
array.push(bHead);
bPointer++;
}
}
// Filling
// TODO: it's possible to optimize a bit here, since the condition is only
// relevant the first time
while (aPointer < aLength) {
aHead = a[aPointer++];
if (array.length === 0 || array[array.length - 1] !== aHead)
array.push(aHead);
}
while (bPointer < bLength) {
bHead = b[bPointer++];
if (array.length === 0 || array[array.length - 1] !== bHead)
array.push(bHead);
}
return array;
}
/**
* Perform the intersection of two already unique sorted array-like structures into one.
*
* @param {array} a - First array.
* @param {array} b - Second array.
* @return {array}
*/
exports.intersectionUniqueArrays = function(a, b) {
// One of the arrays is empty
if (a.length === 0 || b.length === 0)
return new a.constructor(0);
// Finding min array
var tmp;
if (a[0] > b[0]) {
tmp = a;
a = b;
b = tmp;
}
// If array have non overlapping ranges, there is no intersection
var aEnd = a[a.length - 1],
bStart = b[0];
if (aEnd < bStart)
return new a.constructor(0);
// Initializing target
var array = new a.constructor();
// Handling overlap
var aPointer = binarySearch.lowerBound(a, bStart),
aLength = a.length,
bPointer = 0,
bLength = binarySearch.upperBound(b, aEnd),
aHead,
bHead;
while (aPointer < aLength && bPointer < bLength) {
aHead = a[aPointer];
bHead = b[bPointer];
if (aHead < bHead) {
aPointer = binarySearch.lowerBound(a, bHead, aPointer + 1);
}
else if (aHead > bHead) {
bPointer = binarySearch.lowerBound(b, aHead, bPointer + 1);
}
else {
array.push(aHead);
aPointer++;
bPointer++;
}
}
return array;
};
/**
* Merge k sorted array-like structures into one.
*
* @param {array<array>} arrays - Arrays to merge.
* @return {array}
*/
function kWayMergeArrays(arrays) {
var length = 0,
max = -Infinity,
al,
i,
l;
var filtered = [];
for (i = 0, l = arrays.length; i < l; i++) {
al = arrays[i].length;
if (al === 0)
continue;
filtered.push(arrays[i]);
length += al;
if (al > max)
max = al;
}
if (filtered.length === 0)
return new arrays[0].constructor(0);
if (filtered.length === 1)
return filtered[0].slice();
if (filtered.length === 2)
return mergeArrays(filtered[0], filtered[1]);
arrays = filtered;
var array = new arrays[0].constructor(length);
var PointerArray = typed.getPointerArray(max);
var pointers = new PointerArray(arrays.length);
// TODO: benchmark vs. a binomial heap
var heap = new FibonacciHeap(function(a, b) {
a = arrays[a][pointers[a]];
b = arrays[b][pointers[b]];
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
});
for (i = 0; i < l; i++)
heap.push(i);
i = 0;
var p,
v;
while (heap.size) {
p = heap.pop();
v = arrays[p][pointers[p]++];
array[i++] = v;
if (pointers[p] < arrays[p].length)
heap.push(p);
}
return array;
}
/**
* Perform the union of k sorted unique array-like structures into one.
*
* @param {array<array>} arrays - Arrays to merge.
* @return {array}
*/
function kWayUnionUniqueArrays(arrays) {
var max = -Infinity,
al,
i,
l;
var filtered = [];
for (i = 0, l = arrays.length; i < l; i++) {
al = arrays[i].length;
if (al === 0)
continue;
filtered.push(arrays[i]);
if (al > max)
max = al;
}
if (filtered.length === 0)
return new arrays[0].constructor(0);
if (filtered.length === 1)
return filtered[0].slice();
if (filtered.length === 2)
return unionUniqueArrays(filtered[0], filtered[1]);
arrays = filtered;
var array = new arrays[0].constructor();
var PointerArray = typed.getPointerArray(max);
var pointers = new PointerArray(arrays.length);
// TODO: benchmark vs. a binomial heap
var heap = new FibonacciHeap(function(a, b) {
a = arrays[a][pointers[a]];
b = arrays[b][pointers[b]];
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
});
for (i = 0; i < l; i++)
heap.push(i);
var p,
v;
while (heap.size) {
p = heap.pop();
v = arrays[p][pointers[p]++];
if (array.length === 0 || array[array.length - 1] !== v)
array.push(v);
if (pointers[p] < arrays[p].length)
heap.push(p);
}
return array;
}
/**
* Perform the intersection of k sorted array-like structures into one.
*
* @param {array<array>} arrays - Arrays to merge.
* @return {array}
*/
exports.kWayIntersectionUniqueArrays = function(arrays) {
var max = -Infinity,
maxStart = -Infinity,
minEnd = Infinity,
first,
last,
al,
i,
l;
for (i = 0, l = arrays.length; i < l; i++) {
al = arrays[i].length;
// If one of the arrays is empty, so is the intersection
if (al === 0)
return [];
if (al > max)
max = al;
first = arrays[i][0];
last = arrays[i][al - 1];
if (first > maxStart)
maxStart = first;
if (last < minEnd)
minEnd = last;
}
// Full overlap is impossible
if (maxStart > minEnd)
return [];
// Only one value
if (maxStart === minEnd)
return [maxStart];
// NOTE: trying to outsmart I(D,I(C,I(A,B))) is pointless unfortunately...
// NOTE: I tried to be very clever about bounds but it does not seem
// to improve the performance of the algorithm.
var a, b,
array = arrays[0],
aPointer,
bPointer,
aLimit,
bLimit,
aHead,
bHead,
start = maxStart;
for (i = 1; i < l; i++) {
a = array;
b = arrays[i];
// Change that to `[]` and observe some perf drops on V8...
array = new Array();
aPointer = 0;
bPointer = binarySearch.lowerBound(b, start);
aLimit = a.length;
bLimit = b.length;
while (aPointer < aLimit && bPointer < bLimit) {
aHead = a[aPointer];
bHead = b[bPointer];
if (aHead < bHead) {
aPointer = binarySearch.lowerBound(a, bHead, aPointer + 1);
}
else if (aHead > bHead) {
bPointer = binarySearch.lowerBound(b, aHead, bPointer + 1);
}
else {
array.push(aHead);
aPointer++;
bPointer++;
}
}
if (array.length === 0)
return array;
start = array[0];
}
return array;
};
/**
* Variadic merging all of the given arrays.
*
* @param {...array}
* @return {array}
*/
exports.merge = function() {
if (arguments.length === 2) {
if (isArrayLike(arguments[0]))
return mergeArrays(arguments[0], arguments[1]);
}
else {
if (isArrayLike(arguments[0]))
return kWayMergeArrays(arguments);
}
return null;
};
/**
* Variadic function performing the union of all the given unique arrays.
*
* @param {...array}
* @return {array}
*/
exports.unionUnique = function() {
if (arguments.length === 2) {
if (isArrayLike(arguments[0]))
return unionUniqueArrays(arguments[0], arguments[1]);
}
else {
if (isArrayLike(arguments[0]))
return kWayUnionUniqueArrays(arguments);
}
return null;
};
/**
* Variadic function performing the intersection of all the given unique arrays.
*
* @param {...array}
* @return {array}
*/
exports.intersectionUnique = function() {
if (arguments.length === 2) {
if (isArrayLike(arguments[0]))
return exports.intersectionUniqueArrays(arguments[0], arguments[1]);
}
else {
if (isArrayLike(arguments[0]))
return exports.kWayIntersectionUniqueArrays(arguments);
}
return null;
};

View File

@@ -1,87 +0,0 @@
/* eslint no-fallthrough: 0 */
/**
* Mnemonist MurmurHash 3
* =======================
*
* Straightforward implementation of the third version of MurmurHash.
*
* Note: this piece of code belong to haschisch.
*/
/**
* Various helpers.
*/
function mul32(a, b) {
return (a & 0xffff) * b + (((a >>> 16) * b & 0xffff) << 16) & 0xffffffff;
}
function sum32(a, b) {
return (a & 0xffff) + (b >>> 16) + (((a >>> 16) + b & 0xffff) << 16) & 0xffffffff;
}
function rotl32(a, b) {
return (a << b) | (a >>> (32 - b));
}
/**
* MumurHash3 function.
*
* @param {number} seed - Seed.
* @param {ByteArray} data - Data.
*/
module.exports = function murmurhash3(seed, data) {
var c1 = 0xcc9e2d51,
c2 = 0x1b873593,
r1 = 15,
r2 = 13,
m = 5,
n = 0x6b64e654;
var hash = seed,
k1,
i,
l;
for (i = 0, l = data.length - 4; i <= l; i += 4) {
k1 = (
data[i] |
(data[i + 1] << 8) |
(data[i + 2] << 16) |
(data[i + 3] << 24)
);
k1 = mul32(k1, c1);
k1 = rotl32(k1, r1);
k1 = mul32(k1, c2);
hash ^= k1;
hash = rotl32(hash, r2);
hash = mul32(hash, m);
hash = sum32(hash, n);
}
k1 = 0;
switch (data.length & 3) {
case 3:
k1 ^= data[i + 2] << 16;
case 2:
k1 ^= data[i + 1] << 8;
case 1:
k1 ^= data[i];
k1 = mul32(k1, c1);
k1 = rotl32(k1, r1);
k1 = mul32(k1, c2);
hash ^= k1;
default:
}
hash ^= data.length;
hash ^= hash >>> 16;
hash = mul32(hash, 0x85ebca6b);
hash ^= hash >>> 13;
hash = mul32(hash, 0xc2b2ae35);
hash ^= hash >>> 16;
return hash >>> 0;
};

View File

@@ -1,10 +0,0 @@
export type PointerArray = Uint8Array | Uint16Array | Uint32Array | Float64Array;
export type SignedPointerArray = Int8Array | Int16Array | Int32Array | Float64Array;
export type PointerArrayConstructor = Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | Float64ArrayConstructor;
export type SignedPointerArrayConstructor = Int8ArrayConstructor | Int16ArrayConstructor | Int32ArrayConstructor | Float64ArrayConstructor;
export function getPointerArray(size: number): PointerArrayConstructor;
export function getSignedPointerArray(size: number): SignedPointerArrayConstructor;
export function getNumberType(value: number): PointerArrayConstructor | SignedPointerArrayConstructor;
export function isTypedArray(value: unknown): boolean;
export function indices(length: number): PointerArray;

View File

@@ -1,187 +0,0 @@
/**
* Mnemonist Typed Array Helpers
* ==============================
*
* Miscellaneous helpers related to typed arrays.
*/
/**
* When using an unsigned integer array to store pointers, one might want to
* choose the optimal word size in regards to the actual numbers of pointers
* to store.
*
* This helpers does just that.
*
* @param {number} size - Expected size of the array to map.
* @return {TypedArray}
*/
var MAX_8BIT_INTEGER = Math.pow(2, 8) - 1,
MAX_16BIT_INTEGER = Math.pow(2, 16) - 1,
MAX_32BIT_INTEGER = Math.pow(2, 32) - 1;
var MAX_SIGNED_8BIT_INTEGER = Math.pow(2, 7) - 1,
MAX_SIGNED_16BIT_INTEGER = Math.pow(2, 15) - 1,
MAX_SIGNED_32BIT_INTEGER = Math.pow(2, 31) - 1;
exports.getPointerArray = function(size) {
var maxIndex = size - 1;
if (maxIndex <= MAX_8BIT_INTEGER)
return Uint8Array;
if (maxIndex <= MAX_16BIT_INTEGER)
return Uint16Array;
if (maxIndex <= MAX_32BIT_INTEGER)
return Uint32Array;
throw new Error('mnemonist: Pointer Array of size > 4294967295 is not supported.');
};
exports.getSignedPointerArray = function(size) {
var maxIndex = size - 1;
if (maxIndex <= MAX_SIGNED_8BIT_INTEGER)
return Int8Array;
if (maxIndex <= MAX_SIGNED_16BIT_INTEGER)
return Int16Array;
if (maxIndex <= MAX_SIGNED_32BIT_INTEGER)
return Int32Array;
return Float64Array;
};
/**
* Function returning the minimal type able to represent the given number.
*
* @param {number} value - Value to test.
* @return {TypedArrayClass}
*/
exports.getNumberType = function(value) {
// <= 32 bits itnteger?
if (value === (value | 0)) {
// Negative
if (Math.sign(value) === -1) {
if (value <= 127 && value >= -128)
return Int8Array;
if (value <= 32767 && value >= -32768)
return Int16Array;
return Int32Array;
}
else {
if (value <= 255)
return Uint8Array;
if (value <= 65535)
return Uint16Array;
return Uint32Array;
}
}
// 53 bits integer & floats
// NOTE: it's kinda hard to tell whether we could use 32bits or not...
return Float64Array;
};
/**
* Function returning the minimal type able to represent the given array
* of JavaScript numbers.
*
* @param {array} array - Array to represent.
* @param {function} getter - Optional getter.
* @return {TypedArrayClass}
*/
var TYPE_PRIORITY = {
Uint8Array: 1,
Int8Array: 2,
Uint16Array: 3,
Int16Array: 4,
Uint32Array: 5,
Int32Array: 6,
Float32Array: 7,
Float64Array: 8
};
// TODO: make this a one-shot for one value
exports.getMinimalRepresentation = function(array, getter) {
var maxType = null,
maxPriority = 0,
p,
t,
v,
i,
l;
for (i = 0, l = array.length; i < l; i++) {
v = getter ? getter(array[i]) : array[i];
t = exports.getNumberType(v);
p = TYPE_PRIORITY[t.name];
if (p > maxPriority) {
maxPriority = p;
maxType = t;
}
}
return maxType;
};
/**
* Function returning whether the given value is a typed array.
*
* @param {any} value - Value to test.
* @return {boolean}
*/
exports.isTypedArray = function(value) {
return typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView(value);
};
/**
* Function used to concat byte arrays.
*
* @param {...ByteArray}
* @return {ByteArray}
*/
exports.concat = function() {
var length = 0,
i,
o,
l;
for (i = 0, l = arguments.length; i < l; i++)
length += arguments[i].length;
var array = new (arguments[0].constructor)(length);
for (i = 0, o = 0; i < l; i++) {
array.set(arguments[i], o);
o += arguments[i].length;
}
return array;
};
/**
* Function used to initialize a byte array of indices.
*
* @param {number} length - Length of target.
* @return {ByteArray}
*/
exports.indices = function(length) {
var PointerArray = exports.getPointerArray(length);
var array = new PointerArray(length);
for (var i = 0; i < length; i++)
array[i] = i;
return array;
};

Some files were not shown because too many files have changed in this diff Show More