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.
		
		
		
		
		
			
		
			
				
					448 lines
				
				10 KiB
			
		
		
			
		
	
	
					448 lines
				
				10 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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;
							 |