You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							551 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							551 lines
						
					
					
						
							11 KiB
						
					
					
				| /**
 | |
|  * 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;
 |