time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

main.js (15020B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 /* eslint-disable no-restricted-syntax, no-invalid-this */
     20 
     21 'use strict';
     22 
     23 // MODULES //
     24 
     25 var setReadOnly = require( './../../define-nonenumerable-read-only-property' );
     26 var setReadOnlyAccessor = require( './../../define-nonenumerable-read-only-accessor' );
     27 var iteratorSymbol = require( '@stdlib/symbol/iterator' );
     28 var Node = require( './node.js' ); // eslint-disable-line stdlib/no-redeclare
     29 
     30 
     31 // MAIN //
     32 
     33 /**
     34 * Linked list constructor.
     35 *
     36 * @constructor
     37 * @returns {LinkedList} linked list instance
     38 *
     39 * @example
     40 * var list = new LinkedList();
     41 *
     42 * // Add values to the list:
     43 * list.push( 'foo' ).push( 'bar' );
     44 *
     45 * // Remove the last value:
     46 * var v = list.pop();
     47 * // returns 'bar'
     48 *
     49 * // Add a new value to the list:
     50 * list.push( 'beep' );
     51 *
     52 * // Remove the first value:
     53 * v = list.shift();
     54 * // returns 'foo'
     55 */
     56 function LinkedList() {
     57 	if ( !(this instanceof LinkedList) ) {
     58 		return new LinkedList();
     59 	}
     60 	this._length = 0;
     61 	this._first = null;
     62 	this._last = null;
     63 	return this;
     64 }
     65 
     66 /**
     67 * Clears the list.
     68 *
     69 * @name clear
     70 * @memberof LinkedList.prototype
     71 * @type {Function}
     72 * @returns {LinkedList} list instance
     73 *
     74 * @example
     75 * var list = new LinkedList();
     76 *
     77 * // Add values to the list:
     78 * list.push( 'foo' ).push( 'bar' );
     79 *
     80 * // Peek at the first value:
     81 * var v = list.first().value;
     82 * // returns 'foo'
     83 *
     84 * // Examine the list length:
     85 * var len = list.length;
     86 * // returns 2
     87 *
     88 * // Clear all list items:
     89 * list.clear();
     90 *
     91 * // Peek at the first value:
     92 * v = list.first();
     93 * // returns undefined
     94 *
     95 * // Examine the list length:
     96 * len = list.length;
     97 * // returns 0
     98 */
     99 setReadOnly( LinkedList.prototype, 'clear', function clear() {
    100 	this._length = 0;
    101 	this._first = null;
    102 	this._last = null;
    103 	return this;
    104 });
    105 
    106 /**
    107 * Returns the first list node.
    108 *
    109 * @name first
    110 * @memberof LinkedList.prototype
    111 * @type {Function}
    112 * @returns {(Node|void)} list node
    113 *
    114 * @example
    115 * var list = new LinkedList();
    116 *
    117 * // Add values to the list:
    118 * list.push( 'foo' ).push( 'bar' );
    119 *
    120 * // Peek at the first value:
    121 * var v = list.first().value;
    122 * // returns 'foo'
    123 */
    124 setReadOnly( LinkedList.prototype, 'first', function first() {
    125 	if ( this._length ) {
    126 		return this._first;
    127 	}
    128 });
    129 
    130 /**
    131 * Inserts a value into the list **after** a provided list node.
    132 *
    133 * @name insert
    134 * @memberof LinkedList.prototype
    135 * @type {Function}
    136 * @param {Node} node - node after which to insert the value
    137 * @param {*} value - value to insert
    138 * @throws {Error} must provide a node belonging to the list
    139 * @returns {LinkedList} list instance
    140 *
    141 * @example
    142 * var list = new LinkedList();
    143 *
    144 * // Add values to the list:
    145 * list.push( 'foo' ).push( 'bar' ).push( 'beep' );
    146 *
    147 * // Determine the list length:
    148 * var len = list.length;
    149 * // returns 3
    150 *
    151 * // Get the second node:
    152 * var node = list.first().next;
    153 *
    154 * // Insert a value after the second node:
    155 * list.insert( node, 'boop' );
    156 *
    157 * // Determine the list length:
    158 * len = list.length;
    159 * // returns 4
    160 */
    161 setReadOnly( LinkedList.prototype, 'insert', function insert( node, value ) {
    162 	/* eslint-disable no-underscore-dangle */
    163 	var n;
    164 
    165 	// Case: last node (equivalent to `push()`)
    166 	if ( node === this._last ) {
    167 		return this.push( value );
    168 	}
    169 	// Unfortunately, we need to check whether we have been provided a node belonging to our list by walking the list. If we don't, we could erroneously increment the list length. This means our runtime goes from the theoretical O(1) to O(N).
    170 	n = this._first;
    171 	while ( n !== this._last && n !== node ) {
    172 		n = n._next;
    173 	}
    174 	// Check if we iterated through the entire list:
    175 	if ( n === this._last ) {
    176 		throw new Error( 'invalid argument. The list does not contain the provided list node.' );
    177 	}
    178 	// Create a new list node:
    179 	n = new Node( value );
    180 
    181 	// Update pointers:
    182 	node._next._prev = n;
    183 	n._next = node._next;
    184 
    185 	node._next = n;
    186 	n._prev = node;
    187 
    188 	// Increment the list length:
    189 	this._length += 1;
    190 
    191 	return this;
    192 
    193 	/* eslint-enable no-underscore-dangle */
    194 });
    195 
    196 /**
    197 * Returns an iterator for iterating over a list.
    198 *
    199 * ## Notes
    200 *
    201 * -   In order to prevent confusion arising from list mutation during iteration, a returned iterator **always** iterates over a list "snapshot", which is defined as the list of elements at the time of this method's invocation.
    202 *
    203 * @name iterator
    204 * @memberof LinkedList.prototype
    205 * @type {Function}
    206 * @returns {Iterator} iterator
    207 *
    208 * @example
    209 * var list = new LinkedList();
    210 *
    211 * // Add values to the list:
    212 * list.push( 'foo' ).push( 'bar' );
    213 *
    214 * // Create an iterator:
    215 * var it = list.iterator();
    216 *
    217 * // Iterate over the list...
    218 * var v = it.next().value;
    219 * // returns 'foo'
    220 *
    221 * v = it.next().value;
    222 * // returns 'bar'
    223 *
    224 * var bool = it.next().done;
    225 * // returns true
    226 */
    227 setReadOnly( LinkedList.prototype, 'iterator', function iterator() {
    228 	var values;
    229 	var iter;
    230 	var self;
    231 	var FLG;
    232 	var i;
    233 
    234 	self = this;
    235 
    236 	// Initialize the iteration index:
    237 	i = -1;
    238 
    239 	// Create a copy of list values (necessary in order to "snapshot" the list; otherwise, values could come and go between calls to `next`):
    240 	values = this.toArray();
    241 
    242 	// Create an iterator protocol-compliant object:
    243 	iter = {};
    244 	setReadOnly( iter, 'next', next );
    245 	setReadOnly( iter, 'return', end );
    246 	if ( iteratorSymbol ) {
    247 		setReadOnly( iter, iteratorSymbol, factory );
    248 	}
    249 	return iter;
    250 
    251 	/**
    252 	* Returns an iterator protocol-compliant object containing the next iterated value.
    253 	*
    254 	* @private
    255 	* @returns {Object} iterator protocol-compliant object
    256 	*/
    257 	function next() {
    258 		i += 1;
    259 		if ( FLG || i >= values.length ) {
    260 			return {
    261 				'done': true
    262 			};
    263 		}
    264 		return {
    265 			'value': values[ i ],
    266 			'done': false
    267 		};
    268 	}
    269 
    270 	/**
    271 	* Finishes an iterator.
    272 	*
    273 	* @private
    274 	* @param {*} [value] - value to return
    275 	* @returns {Object} iterator protocol-compliant object
    276 	*/
    277 	function end( value ) {
    278 		FLG = true;
    279 		if ( arguments.length ) {
    280 			return {
    281 				'value': value,
    282 				'done': true
    283 			};
    284 		}
    285 		return {
    286 			'done': true
    287 		};
    288 	}
    289 
    290 	/**
    291 	* Returns a new iterator.
    292 	*
    293 	* @private
    294 	* @returns {Iterator} iterator
    295 	*/
    296 	function factory() {
    297 		return self.iterator();
    298 	}
    299 });
    300 
    301 /**
    302 * Returns the last node.
    303 *
    304 * @name last
    305 * @memberof LinkedList.prototype
    306 * @type {Function}
    307 * @returns {(Node|void)} list node
    308 *
    309 * @example
    310 * var list = new LinkedList();
    311 *
    312 * // Add values to the list:
    313 * list.push( 'foo' ).push( 'bar' );
    314 *
    315 * // Peek at the last value:
    316 * var v = list.last().value;
    317 * // returns 'bar'
    318 */
    319 setReadOnly( LinkedList.prototype, 'last', function last() {
    320 	if ( this._length ) {
    321 		return this._last;
    322 	}
    323 });
    324 
    325 /**
    326 * List length.
    327 *
    328 * @name length
    329 * @memberof LinkedList.prototype
    330 * @type {NonNegativeInteger}
    331 *
    332 * @example
    333 * var list = new LinkedList();
    334 *
    335 * // Examine the initial list length:
    336 * var len = list.length;
    337 * // returns 0
    338 *
    339 * // Add values to the list:
    340 * list.push( 'foo' ).push( 'bar' );
    341 *
    342 * // Retrieve the current list length:
    343 * len = list.length;
    344 * // returns 2
    345 */
    346 setReadOnlyAccessor( LinkedList.prototype, 'length', function get() {
    347 	return this._length;
    348 });
    349 
    350 /**
    351 * Removes a value from the end of the list.
    352 *
    353 * @name pop
    354 * @memberof LinkedList.prototype
    355 * @type {Function}
    356 * @returns {(*|void)} removed value
    357 *
    358 * @example
    359 * var list = new LinkedList();
    360 *
    361 * // Add values to the list:
    362 * list.push( 'foo' ).push( 'bar' );
    363 *
    364 * // Remove the last value:
    365 * var v = list.pop();
    366 * // returns 'bar'
    367 *
    368 * // Add a new value to the list:
    369 * list.push( 'beep' );
    370 *
    371 * // Remove the last value:
    372 * v = list.pop();
    373 * // returns 'beep'
    374 */
    375 setReadOnly( LinkedList.prototype, 'pop', function pop() {
    376 	/* eslint-disable no-underscore-dangle */
    377 	var value;
    378 	if ( this._length ) {
    379 		// Retrieve the last value:
    380 		value = this._last.value;
    381 
    382 		// Check whether we have a new "tail" or whether we have emptied the list...
    383 		if ( this._last._prev ) {
    384 			this._last = this._last._prev;
    385 			this._last._next = null;
    386 		} else {
    387 			// List is empty:
    388 			this._first = null;
    389 			this._last = null;
    390 		}
    391 		// Decrement the list length:
    392 		this._length -= 1;
    393 	}
    394 	return value;
    395 
    396 	/* eslint-enable no-underscore-dangle */
    397 });
    398 
    399 /**
    400 * Adds a value to the end of the list.
    401 *
    402 * @name push
    403 * @memberof LinkedList.prototype
    404 * @type {Function}
    405 * @returns {LinkedList} list instance
    406 *
    407 * @example
    408 * var list = new LinkedList();
    409 *
    410 * // Add values to the list:
    411 * list.push( 'foo' ).push( 'bar' );
    412 *
    413 * // Remove the last value:
    414 * var v = list.pop();
    415 * // returns 'bar'
    416 *
    417 * // Add a new value to the list:
    418 * list.push( 'beep' );
    419 *
    420 * // Remove the last value:
    421 * v = list.pop();
    422 * // returns 'beep'
    423 */
    424 setReadOnly( LinkedList.prototype, 'push', function push( value ) {
    425 	var node;
    426 
    427 	// Create a new list node:
    428 	node = new Node( value );
    429 
    430 	// Check whether the list is currently empty...
    431 	if ( this._length === 0 ) {
    432 		// This is the only list node, making it both the first and last node:
    433 		this._first = node;
    434 		this._last = node;
    435 	} else {
    436 		// Link the new node to the previous last node:
    437 		node._prev = this._last; // eslint-disable-line no-underscore-dangle
    438 
    439 		// Link the previous last node to the new node:
    440 		this._last._next = node; // eslint-disable-line no-underscore-dangle
    441 
    442 		// Update the pointer for the last node:
    443 		this._last = node;
    444 	}
    445 	// Increment the list length:
    446 	this._length += 1;
    447 
    448 	return this;
    449 });
    450 
    451 /**
    452 * Removes a list node from the list.
    453 *
    454 * @name remove
    455 * @memberof LinkedList.prototype
    456 * @type {Function}
    457 * @param {Node} node - node to remove
    458 * @throws {Error} must provide a node belonging to the list
    459 * @returns {(*|void)} removed value
    460 *
    461 * @example
    462 * var list = new LinkedList();
    463 *
    464 * // Add values to the list:
    465 * list.push( 'foo' ).push( 'bar' ).push( 'beep' );
    466 *
    467 * // Determine the list length:
    468 * var len = list.length;
    469 * // returns 3
    470 *
    471 * // Get the second node:
    472 * var node = list.first().next;
    473 *
    474 * // Remove the second node:
    475 * var v = list.remove( node );
    476 * // returns 'bar'
    477 *
    478 * // Determine the list length:
    479 * len = list.length;
    480 * // returns 2
    481 */
    482 setReadOnly( LinkedList.prototype, 'remove', function remove( node ) {
    483 	/* eslint-disable no-underscore-dangle */
    484 	var value;
    485 	var n;
    486 
    487 	// Case: first node (equivalent to `shift()`)
    488 	if ( node === this._first ) {
    489 		return this.shift();
    490 	}
    491 	// Case: last node (equivalent to `pop()`)
    492 	if ( node === this._last ) {
    493 		return this.pop();
    494 	}
    495 	// Retrieve the node value:
    496 	value = node.value;
    497 
    498 	// Unfortunately, we need to check whether we have been provided a node belonging to our list by walking the list. If we don't, we could erroneously decrement the list length. This means our runtime goes from the theoretical O(1) to O(N).
    499 	n = this._first;
    500 	while ( n !== this._last && n !== node ) {
    501 		n = n._next;
    502 	}
    503 	// Check if we iterated through the entire list:
    504 	if ( n === this._last ) {
    505 		throw new Error( 'invalid argument. The list does not contain the provided list node.' );
    506 	}
    507 	// Update pointers:
    508 	node._prev._next = node._next;
    509 	node._next._prev = node._prev;
    510 
    511 	// Decrement the list length:
    512 	this._length -= 1;
    513 
    514 	return value;
    515 
    516 	/* eslint-enable no-underscore-dangle */
    517 });
    518 
    519 /**
    520 * Removes a value from the beginning of the list.
    521 *
    522 * @name shift
    523 * @memberof LinkedList.prototype
    524 * @type {Function}
    525 * @returns {(*|void)} removed value
    526 *
    527 * @example
    528 * var list = new LinkedList();
    529 *
    530 * // Add values to the list:
    531 * list.push( 'foo' ).push( 'bar' );
    532 *
    533 * // Remove the first value:
    534 * var v = list.shift();
    535 * // returns 'foo'
    536 *
    537 * // Add a new value to the list:
    538 * list.push( 'beep' );
    539 *
    540 * // Remove the first value:
    541 * v = list.shift();
    542 * // returns 'bar'
    543 */
    544 setReadOnly( LinkedList.prototype, 'shift', function shift() {
    545 	/* eslint-disable no-underscore-dangle */
    546 	var value;
    547 	if ( this._length ) {
    548 		// Retrieve the first value:
    549 		value = this._first.value;
    550 
    551 		// Check whether we have a new "head" or whether we have emptied the list...
    552 		if ( this._first._next ) {
    553 			this._first = this._first._next;
    554 			this._first._prev = null;
    555 		} else {
    556 			// List is empty:
    557 			this._first = null;
    558 			this._last = null;
    559 		}
    560 		// Decrement the list length:
    561 		this._length -= 1;
    562 	}
    563 	return value;
    564 
    565 	/* eslint-enable no-underscore-dangle */
    566 });
    567 
    568 /**
    569 * Returns an array of list values.
    570 *
    571 * @name toArray
    572 * @memberof LinkedList.prototype
    573 * @type {Function}
    574 * @returns {Array} list values
    575 *
    576 * @example
    577 * var list = new LinkedList();
    578 *
    579 * // Add values to the list:
    580 * list.push( 'foo' ).push( 'bar' );
    581 *
    582 * // Get an array of list values:
    583 * var vals = list.toArray();
    584 * // returns [ 'foo', 'bar' ]
    585 */
    586 setReadOnly( LinkedList.prototype, 'toArray', function toArray() {
    587 	var node;
    588 	var out;
    589 	var i;
    590 
    591 	out = [];
    592 	node = this._first;
    593 	for ( i = 0; i < this._length; i++ ) {
    594 		out.push( node.value );
    595 		node = node.next;
    596 	}
    597 	return out;
    598 });
    599 
    600 /**
    601 * Serializes a list as JSON.
    602 *
    603 * ## Notes
    604 *
    605 * -   `JSON.stringify()` implicitly calls this method when stringifying a `LinkedList` instance.
    606 *
    607 * @name toJSON
    608 * @memberof LinkedList.prototype
    609 * @type {Function}
    610 * @returns {Object} serialized list
    611 *
    612 * @example
    613 * var list = new LinkedList();
    614 *
    615 * // Add values to the list:
    616 * list.push( 'foo' ).push( 'bar' );
    617 *
    618 * // Serialize to JSON:
    619 * var o = list.toJSON();
    620 * // returns { 'type': 'linked-list', 'data': [ 'foo', 'bar' ] }
    621 */
    622 setReadOnly( LinkedList.prototype, 'toJSON', function toJSON() {
    623 	var out = {};
    624 	out.type = 'linked-list';
    625 	out.data = this.toArray();
    626 	return out;
    627 });
    628 
    629 /**
    630 * Adds a value to the beginning of the list.
    631 *
    632 * @name unshift
    633 * @memberof LinkedList.prototype
    634 * @type {Function}
    635 * @returns {LinkedList} list instance
    636 *
    637 * @example
    638 * var list = new LinkedList();
    639 *
    640 * // Add values to the beginning of the list:
    641 * list.unshift( 'foo' ).unshift( 'bar' );
    642 *
    643 * // Remove the last value:
    644 * var v = list.pop();
    645 * // returns 'foo'
    646 *
    647 * // Add a new value to the beginning of the list:
    648 * list.unshift( 'beep' );
    649 *
    650 * // Remove the last value:
    651 * v = list.pop();
    652 * // returns 'bar'
    653 */
    654 setReadOnly( LinkedList.prototype, 'unshift', function unshift( value ) {
    655 	var node;
    656 
    657 	// Create a new list node:
    658 	node = new Node( value );
    659 
    660 	// Check whether the list is currently empty...
    661 	if ( this._length === 0 ) {
    662 		// This is the only list node, making it both the first and last node:
    663 		this._first = node;
    664 		this._last = node;
    665 	} else {
    666 		// Link the new node to the previous first node:
    667 		node._next = this._first; // eslint-disable-line no-underscore-dangle
    668 
    669 		// Link the previous first node to the new node:
    670 		this._first._prev = node; // eslint-disable-line no-underscore-dangle
    671 
    672 		// Update the pointer for the first node:
    673 		this._first = node;
    674 	}
    675 	// Increment the list length:
    676 	this._length += 1;
    677 
    678 	return this;
    679 });
    680 
    681 
    682 // EXPORTS //
    683 
    684 module.exports = LinkedList;