chain-lightning
Advanced tools
+1220
| /* | ||
| Chain Lightning | ||
| Copyright (c) 2018 Cédric Ronvel | ||
| The MIT License (MIT) | ||
| 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. | ||
| */ | ||
| "use strict" ; | ||
| /* | ||
| For balancing, it uses the AVL algorithm. | ||
| See: https://en.wikipedia.org/wiki/AVL_tree | ||
| Visualization: https://www.cs.usfca.edu/~galles/visualization/AVLtree.html | ||
| */ | ||
| function BinaryTree( options , ... elements ) { | ||
| var autoIndex = 0 ; | ||
| this.trunc = null ; | ||
| this.length = 0 ; | ||
| this.keyFn = options && options.keyFn ; | ||
| this.uniqueKeys = !! ( options && options.uniqueKeys ) ; | ||
| if ( ! this.keyFn ) { | ||
| this.keyFn = ( element , hint ) => hint !== undefined ? hint : autoIndex ++ ; | ||
| } | ||
| if ( elements.length ) { | ||
| this.insert( ... elements ) ; | ||
| } | ||
| } | ||
| module.exports = BinaryTree ; | ||
| BinaryTree.from = ( iterable , options ) => { | ||
| var element , | ||
| tree = new BinaryTree( options ) ; | ||
| for ( element of iterable ) { | ||
| tree.insert( element ) ; | ||
| } | ||
| return tree ; | ||
| } ; | ||
| function Node( tree , key , element , parent = null ) { | ||
| this.tree = tree ; | ||
| this.key = key ; | ||
| this.element = element ; | ||
| this.parent = parent ; | ||
| this.left = null ; | ||
| this.right = null ; | ||
| this.depth = 1 ; | ||
| } | ||
| BinaryTree.Node = Node ; | ||
| // /!\ DEPRECATED? .truncate*() does not call it /!\ | ||
| // Used when a node is deleted | ||
| BinaryTree.prototype.destroyNode = function( node ) { | ||
| node.tree = null ; | ||
| node.key = null ; | ||
| node.element = null ; | ||
| node.parent = null ; | ||
| node.left = null ; | ||
| node.right = null ; | ||
| node.depth = 0 ; | ||
| } ; | ||
| // Implementation dependant, this works for key as finite number, for things like key as string, those methods must be overloaded | ||
| BinaryTree.prototype.isOrdered = ( key1 , key2 ) => key1 <= key2 ; | ||
| BinaryTree.prototype.defaultKey = 0 ; | ||
| BinaryTree.prototype.increment = key => key + 1 ; | ||
| BinaryTree.prototype.decrement = key => key - 1 ; | ||
| BinaryTree.prototype.values = function *() { | ||
| var current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| yield current.element ; | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| } ; | ||
| BinaryTree.prototype[Symbol.iterator] = BinaryTree.prototype.values ; | ||
| BinaryTree.prototype.keys = function() { | ||
| var keys = new Array( this.length ) , | ||
| index = 0 , | ||
| current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| keys[ index ++ ] = current.key ; | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return keys ; | ||
| } ; | ||
| BinaryTree.prototype.getHeadNode = | ||
| BinaryTree.prototype.getMinNode = function( node = this.trunc ) { | ||
| if ( ! node ) { return null ; } | ||
| while ( node.left ) { node = node.left ; } | ||
| return node ; | ||
| } ; | ||
| BinaryTree.prototype.getTailNode = | ||
| BinaryTree.prototype.getMaxNode = function( node = this.trunc ) { | ||
| if ( ! node ) { return null ; } | ||
| while ( node.right ) { node = node.right ; } | ||
| return node ; | ||
| } ; | ||
| BinaryTree.prototype.nextNode = function( node ) { | ||
| if ( node.right ) { | ||
| node = node.right ; | ||
| while ( node.left ) { | ||
| node = node.left ; | ||
| } | ||
| return node ; | ||
| } | ||
| while ( node.parent ) { | ||
| if ( node === node.parent.left ) { | ||
| return node.parent ; | ||
| } | ||
| node = node.parent ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| BinaryTree.prototype.previousNode = function( node ) { | ||
| if ( node.left ) { | ||
| node = node.left ; | ||
| while ( node.right ) { | ||
| node = node.right ; | ||
| } | ||
| return node ; | ||
| } | ||
| while ( node.parent ) { | ||
| if ( node === node.parent.right ) { | ||
| return node.parent ; | ||
| } | ||
| node = node.parent ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| BinaryTree.prototype.get = function( key ) { | ||
| var node = this.getNode( key ) ; | ||
| if ( ! node ) { return undefined ; } | ||
| return node.element ; | ||
| } ; | ||
| BinaryTree.prototype.getNode = function( key ) { | ||
| if ( ! this.trunc ) { return null ; } | ||
| var node = this.trunc ; | ||
| for ( ;; ) { | ||
| if ( node.key === key ) { | ||
| return node ; | ||
| } | ||
| if ( this.isOrdered( node.key , key ) ) { | ||
| if ( ! node.right ) { return null ; } | ||
| node = node.right ; | ||
| } | ||
| else { | ||
| if ( ! node.left ) { return null ; } | ||
| node = node.left ; | ||
| } | ||
| } | ||
| } ; | ||
| BinaryTree.prototype.set = function( key , element , noOverwrite ) { | ||
| if ( ! this.trunc ) { | ||
| this.trunc = new Node( this , key , element ) ; | ||
| this.length ++ ; | ||
| return ; | ||
| } | ||
| var node = this.trunc ; | ||
| for ( ;; ) { | ||
| if ( key === node.key ) { | ||
| // Overwrite this key | ||
| if ( noOverwrite ) { | ||
| throw new Error( "Duplicated key '" + key + "'" ) ; | ||
| } | ||
| if ( this.uniqueKeys ) { | ||
| node.element = element ; | ||
| return ; | ||
| } | ||
| // No uniqueKey? So insert it after | ||
| if ( ! node.right ) { | ||
| node.right = new Node( this , key , element , node ) ; | ||
| this.length ++ ; | ||
| this.rebalance( node , true ) ; | ||
| return ; | ||
| } | ||
| node = node.right ; | ||
| } | ||
| if ( this.isOrdered( node.key , key ) ) { | ||
| if ( ! node.right ) { | ||
| node.right = new Node( this , key , element , node ) ; | ||
| this.length ++ ; | ||
| this.rebalance( node , true ) ; | ||
| return ; | ||
| } | ||
| node = node.right ; | ||
| } | ||
| else { | ||
| if ( ! node.left ) { | ||
| node.left = new Node( this , key , element , node ) ; | ||
| this.length ++ ; | ||
| this.rebalance( node , true ) ; | ||
| return ; | ||
| } | ||
| node = node.left ; | ||
| } | ||
| } | ||
| } ; | ||
| BinaryTree.prototype.insert = function( ... elements ) { | ||
| elements.forEach( element => this.set( this.keyFn( element ) , element , true ) ) ; | ||
| } ; | ||
| // Divide and conquer algo, to avoid rotation when all elements are in the correct order | ||
| BinaryTree.prototype.insertOrdered = function( ... elements ) { | ||
| var start , end , middle , diff , listIndex , | ||
| list = [ 0 , elements.length - 1 ] ; | ||
| for ( listIndex = 0 ; listIndex < list.length ; listIndex += 2 ) { | ||
| start = list[ listIndex ] ; | ||
| end = list[ listIndex + 1 ] ; | ||
| diff = end - start ; | ||
| switch ( diff ) { | ||
| case 0 : | ||
| //console.log( "===" , start ) ; | ||
| this.set( this.keyFn( elements[ start ] , start ) , elements[ start ] , true ) ; | ||
| break ; | ||
| case 1 : | ||
| //console.log( "start end" , start , end ) ; | ||
| this.set( this.keyFn( elements[ start ] , start ) , elements[ start ] , true ) ; | ||
| list.push( end , end ) ; | ||
| //this.set( this.keyFn( elements[ end ] , end ) , elements[ end ] , true ) ; | ||
| break ; | ||
| case 2 : | ||
| //console.log( "middle start end" , start + 1 , start , end ) ; | ||
| this.set( this.keyFn( elements[ start + 1 ] , start + 1 ) , elements[ start + 1 ] , true ) ; | ||
| list.push( start , start ) ; | ||
| //this.set( this.keyFn( elements[ start ] , start ) , elements[ start ] , true ) ; | ||
| list.push( end , end ) ; | ||
| //this.set( this.keyFn( elements[ end ] , end ) , elements[ end ] , true ) ; | ||
| break ; | ||
| default : | ||
| middle = Math.round( start + diff / 2 ) ; | ||
| //console.log( "split" , middle , start , end ) ; | ||
| this.set( this.keyFn( elements[ middle ] , middle ) , elements[ middle ] , true ) ; | ||
| list.push( start , middle - 1 ) ; | ||
| list.push( middle + 1 , end ) ; | ||
| break ; | ||
| } | ||
| } | ||
| } ; | ||
| BinaryTree.prototype.delete = function( key ) { | ||
| var node = this.getNode( key ) ; | ||
| if ( ! node ) { return false ; } | ||
| this.deleteNode( node ) ; | ||
| } ; | ||
| BinaryTree.prototype.deleteNode = function( node ) { | ||
| var parent , referer , refererProp , replacerNode ; | ||
| parent = node.parent ; | ||
| if ( parent ) { | ||
| referer = parent ; | ||
| refererProp = node === parent.left ? 'left' : 'right' ; | ||
| } | ||
| else { | ||
| referer = this ; | ||
| refererProp = 'trunc' ; | ||
| } | ||
| if ( node.left ) { | ||
| if ( node.right ) { | ||
| // Ok, both node exist... it's the complicated case, | ||
| // Find a replacerNode, copy its key and value in the current node, | ||
| // and delete the replacer instead of this one. | ||
| // Note that the replacer has only one child, since it's either the min or the max of the subtree. | ||
| // Choose the heavier child | ||
| if ( node.left.depth >= node.right.depth ) { | ||
| // We will use the left subtree, the replacer is the maximum of this sub-tree | ||
| replacerNode = this.getMaxNode( node.left ) ; | ||
| } | ||
| else { | ||
| // We will use the right subtree, the replacer is the minimum of this sub-tree | ||
| replacerNode = this.getMinNode( node.right ) ; | ||
| } | ||
| // Copy key/value... | ||
| node.key = replacerNode.key ; | ||
| node.element = replacerNode.element ; | ||
| // ... and just delete the replacer using .deleteNode() | ||
| this.deleteNode( replacerNode ) ; | ||
| } | ||
| else { | ||
| // Only a left child, attach its subtree to the parent | ||
| referer[ refererProp ] = node.left ; | ||
| node.left.parent = parent ; | ||
| this.length -- ; | ||
| this.rebalance( node , true ) ; | ||
| this.destroyNode( node ) ; | ||
| } | ||
| } | ||
| else if ( node.right ) { | ||
| // Only a right child, attach its subtree to the parent | ||
| referer[ refererProp ] = node.right ; | ||
| node.right.parent = parent ; | ||
| this.length -- ; | ||
| this.rebalance( node , true ) ; | ||
| this.destroyNode( node ) ; | ||
| } | ||
| else { | ||
| // No child, simply unlink it from the parent | ||
| referer[ refererProp ] = null ; | ||
| this.length -- ; | ||
| this.rebalance( node , true ) ; | ||
| this.destroyNode( node ) ; | ||
| } | ||
| } ; | ||
| BinaryTree.prototype.truncateBefore = function( key , including ) { | ||
| var lastTruncatedNode = this.getClosestNode( key , ! including , -1 ) ; | ||
| //var firstKeptNode = this.nextNode( lastTruncatedNode ) ; | ||
| if ( ! lastTruncatedNode ) { return 0 ; } | ||
| var count = 0 , | ||
| truncatedSubTree = lastTruncatedNode , | ||
| current = lastTruncatedNode , | ||
| remainderNode = lastTruncatedNode.right ; | ||
| // length reduction | ||
| while ( current ) { | ||
| count ++ ; | ||
| current = this.previousNode( current ) ; | ||
| } | ||
| this.length -= count ; | ||
| //console.log( "lastTruncatedNode:" , lastTruncatedNode.key ) ; | ||
| //this.debug() ; | ||
| //console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.right ) { | ||
| truncatedSubTree = truncatedSubTree.parent ; | ||
| //console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| } | ||
| for ( ;; ) { | ||
| if ( truncatedSubTree.parent ) { | ||
| truncatedSubTree.parent.left = remainderNode ; | ||
| if ( remainderNode ) { | ||
| remainderNode.parent = truncatedSubTree.parent ; | ||
| } | ||
| // Loop conditions | ||
| //console.log( ">>> rebalancing" , truncatedSubTree.parent.key ) ; | ||
| remainderNode = this.rebalance( truncatedSubTree.parent ) ; | ||
| truncatedSubTree = remainderNode ; | ||
| //console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.left ) { | ||
| truncatedSubTree = truncatedSubTree.parent ; | ||
| //console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| //console.log( ">>> rebalancing" , truncatedSubTree.key ) ; | ||
| truncatedSubTree = this.rebalance( truncatedSubTree ) ; | ||
| //console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| } | ||
| if ( ! truncatedSubTree.parent ) { break ; } | ||
| //console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.right ) { | ||
| truncatedSubTree = truncatedSubTree.parent ; | ||
| //console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| //console.log( ">>> rebalancing" , truncatedSubTree.key ) ; | ||
| truncatedSubTree = this.rebalance( truncatedSubTree ) ; | ||
| //console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| } | ||
| if ( truncatedSubTree === remainderNode ) { break ; } | ||
| } | ||
| else { | ||
| //console.log( "trunc!" ) ; | ||
| this.trunc = remainderNode ; | ||
| if ( remainderNode ) { | ||
| remainderNode.parent = null ; | ||
| //console.log( ">>> rebalancing" , remainderNode.key ) ; | ||
| this.rebalance( remainderNode ) ; | ||
| } | ||
| break ; | ||
| } | ||
| } | ||
| return count ; | ||
| } ; | ||
| BinaryTree.prototype.truncateAfter = function( key , including ) { | ||
| var firstTruncatedNode = this.getClosestNode( key , ! including , 1 ) ; | ||
| //var firstKeptNode = this.nextNode( firstTruncatedNode ) ; | ||
| if ( ! firstTruncatedNode ) { return 0 ; } | ||
| var count = 0 , | ||
| truncatedSubTree = firstTruncatedNode , | ||
| current = firstTruncatedNode , | ||
| remainderNode = firstTruncatedNode.left ; | ||
| // length reduction | ||
| while ( current ) { | ||
| count ++ ; | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| this.length -= count ; | ||
| //console.log( "firstTruncatedNode:" , firstTruncatedNode.key ) ; | ||
| //this.debug() ; | ||
| //console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.left ) { | ||
| truncatedSubTree = truncatedSubTree.parent ; | ||
| //console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| } | ||
| for ( ;; ) { | ||
| if ( truncatedSubTree.parent ) { | ||
| truncatedSubTree.parent.right = remainderNode ; | ||
| if ( remainderNode ) { | ||
| remainderNode.parent = truncatedSubTree.parent ; | ||
| } | ||
| // Loop conditions | ||
| //console.log( ">>> rebalancing" , truncatedSubTree.parent.key ) ; | ||
| remainderNode = this.rebalance( truncatedSubTree.parent ) ; | ||
| truncatedSubTree = remainderNode ; | ||
| //console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.right ) { | ||
| truncatedSubTree = truncatedSubTree.parent ; | ||
| //console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| //console.log( ">>> rebalancing" , truncatedSubTree.key ) ; | ||
| truncatedSubTree = this.rebalance( truncatedSubTree ) ; | ||
| //console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| } | ||
| if ( ! truncatedSubTree.parent ) { break ; } | ||
| //console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.left ) { | ||
| truncatedSubTree = truncatedSubTree.parent ; | ||
| //console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| //console.log( ">>> rebalancing" , truncatedSubTree.key ) ; | ||
| truncatedSubTree = this.rebalance( truncatedSubTree ) ; | ||
| //console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ; | ||
| } | ||
| if ( truncatedSubTree === remainderNode ) { break ; } | ||
| } | ||
| else { | ||
| //console.log( "trunc!" ) ; | ||
| this.trunc = remainderNode ; | ||
| if ( remainderNode ) { | ||
| remainderNode.parent = null ; | ||
| //console.log( ">>> rebalancing" , remainderNode.key ) ; | ||
| this.rebalance( remainderNode ) ; | ||
| } | ||
| break ; | ||
| } | ||
| } | ||
| return count ; | ||
| } ; | ||
| /* | ||
| Advanced Array-like features | ||
| */ | ||
| // Array.prototype.includes() uses this for value equality | ||
| // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes | ||
| function isIdenticalTo( x , y ) { | ||
| return x === y || ( Number.isNaN( x ) && Number.isNaN( y ) ) ; | ||
| } | ||
| BinaryTree.prototype.keyOf = function( element , fromKey ) { | ||
| var current = fromKey ? this.getNode( fromKey ) : this.getHeadNode() ; | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return current.key ; } | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return ; | ||
| } ; | ||
| BinaryTree.prototype.lastKeyOf = function( element , fromKey ) { | ||
| var current = fromKey ? this.getNode( fromKey ) : this.getTailNode() ; | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return current.key ; } | ||
| current = this.previousNode( current ) ; | ||
| } | ||
| return ; | ||
| } ; | ||
| BinaryTree.prototype.includes = function( element ) { | ||
| if ( ! this.trunc ) { return false ; } | ||
| return this._recursiveIncludes( element , this.trunc ) ; | ||
| } ; | ||
| BinaryTree.prototype._recursiveIncludes = function( element , current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return true ; } | ||
| if ( current.left && this._recursiveIncludes( element , current.left ) ) { return true ; } | ||
| if ( current.right && this._recursiveIncludes( element , current.right ) ) { return true ; } | ||
| return false ; | ||
| } ; | ||
| BinaryTree.prototype.forEach = function( fn , thisArg ) { | ||
| var current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| fn.call( thisArg , current.element , current.key , this ) ; | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| } ; | ||
| BinaryTree.prototype.some = function( fn , thisArg ) { | ||
| var current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current.key , this ) ) { return true ; } | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return false ; | ||
| } ; | ||
| BinaryTree.prototype.every = function( fn , thisArg ) { | ||
| var current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| if ( ! fn.call( thisArg , current.element , current.key , this ) ) { return false ; } | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return true ; | ||
| } ; | ||
| BinaryTree.prototype.find = function( fn , thisArg ) { | ||
| var current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current.key , this ) ) { | ||
| return current.element ; | ||
| } | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return ; | ||
| } ; | ||
| BinaryTree.prototype.findKey = function( fn , thisArg ) { | ||
| var current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current.key , this ) ) { | ||
| return current.key ; | ||
| } | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return ; | ||
| } ; | ||
| // Map to an array | ||
| BinaryTree.prototype.arrayMap = | ||
| BinaryTree.prototype.map = function( fn , thisArg ) { | ||
| var array = new Array( this.length ) , | ||
| index = 0 , | ||
| current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| array[ index ++ ] = fn.call( thisArg , current.element , current.key , this ) ; | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return array ; | ||
| } ; | ||
| BinaryTree.prototype.reduce = function( fn , accumulator ) { | ||
| var current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| accumulator = fn( accumulator , current.element , current.key , this ) ; | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return accumulator ; | ||
| } ; | ||
| BinaryTree.prototype.reduceRight = function( fn , accumulator ) { | ||
| var current = this.getTailNode() ; | ||
| while ( current ) { | ||
| accumulator = fn( accumulator , current.element , current.key , this ) ; | ||
| current = this.previousNode( current ) ; | ||
| } | ||
| return accumulator ; | ||
| } ; | ||
| // Filter to an array | ||
| BinaryTree.prototype.arrayFilter = | ||
| BinaryTree.prototype.filter = function( fn , thisArg ) { | ||
| var array = [] , | ||
| index = 0 , | ||
| current = this.getHeadNode() ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current.key , this ) ) { | ||
| array[ index ++ ] = current.element ; | ||
| } | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| return array ; | ||
| } ; | ||
| /* | ||
| Advanced custom features | ||
| */ | ||
| // Delete all occurences of a value | ||
| BinaryTree.prototype.deleteValue = | ||
| BinaryTree.prototype.removeValue = function( value ) { | ||
| this.inPlaceFilter( e => ! isIdenticalTo( e , value ) ) ; | ||
| } ; | ||
| BinaryTree.prototype.inPlaceFilter = function( fn , thisArg ) { | ||
| var current = this.getHeadNode() , | ||
| next ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current.key , this ) ) { | ||
| current = this.nextNode( current ) ; | ||
| } | ||
| else { | ||
| next = this.nextNode( current ) ; | ||
| this.deleteNode( current ) ; | ||
| current = next ; | ||
| } | ||
| } | ||
| return this ; | ||
| } ; | ||
| /* | ||
| Internal, low-level | ||
| */ | ||
| // Propagate depth upward and balance the tree if necessary | ||
| BinaryTree.prototype.rebalance = function( node , upwardRecusive ) { | ||
| var balance = this.updateDepth( node ) ; | ||
| // After things like .truncate*(), it is possible that one node needs multiple rotations | ||
| while ( balance > 1 || balance < -1 ) { | ||
| if ( balance > 1 ) { | ||
| if ( this.nodeBalance( node.right ) < 0 ) { | ||
| //console.log( "right-left heavy" ) ; | ||
| //console.log( "right-left rotations needed, keys: " + node.right.key + ' ; ' + node.key ) ; | ||
| //console.log( "\nbefore:" ) ; | ||
| //this.debug() ; | ||
| // First rotate the child | ||
| this.rotateNodeRight( node.right ) ; | ||
| //console.log( "\nafter the child rotation:" ) ; | ||
| //this.debug() ; | ||
| node = this.rotateNodeLeft( node ) ; | ||
| //console.log( "\nafter both rotations:" ) ; | ||
| //this.debug() ; | ||
| //console.log( "Node sanity check, key: " + node.key ) ; | ||
| this.nodeSanityCheck( node , node.parent ) ; | ||
| } | ||
| else { | ||
| //console.log( "right heavy" ) ; | ||
| //console.log( "simple left rotation needed, key: " + node.key ) ; | ||
| //console.log( "\nbefore:" ) ; | ||
| //this.debug() ; | ||
| node = this.rotateNodeLeft( node ) ; | ||
| //console.log( "\nafter:" ) ; | ||
| //this.debug() ; | ||
| //console.log( "Node sanity check, key: " + node.key ) ; | ||
| this.nodeSanityCheck( node , node.parent ) ; | ||
| } | ||
| } | ||
| else if ( balance < -1 ) { | ||
| if ( this.nodeBalance( node.left ) > 0 ) { | ||
| //console.log( "left-right heavy" ) ; | ||
| //console.log( "left-right rotations needed, keys: " + node.left.key + ' ; ' + node.key ) ; | ||
| //console.log( "\nbefore:" ) ; | ||
| //this.debug() ; | ||
| // First rotate the child | ||
| this.rotateNodeLeft( node.left ) ; | ||
| //console.log( "\nafter the child rotation:" ) ; | ||
| //this.debug() ; | ||
| node = this.rotateNodeRight( node ) ; | ||
| //console.log( "\nafter both rotations:" ) ; | ||
| //this.debug() ; | ||
| //console.log( "Node sanity check, key: " + node.key ) ; | ||
| this.nodeSanityCheck( node , node.parent ) ; | ||
| } | ||
| else { | ||
| //console.log( "left heavy" ) ; | ||
| //console.log( "simple right rotation needed, key: " + node.key ) ; | ||
| //console.log( "\nbefore:" ) ; | ||
| //this.debug() ; | ||
| node = this.rotateNodeRight( node ) ; | ||
| //console.log( "\nafter:" ) ; | ||
| //this.debug() ; | ||
| //console.log( "Node sanity check, key: " + node.key ) ; | ||
| this.nodeSanityCheck( node , node.parent ) ; | ||
| } | ||
| } | ||
| balance = this.nodeBalance( node ) ; | ||
| } | ||
| if ( upwardRecusive && node.parent ) { | ||
| this.rebalance( node.parent , true ) ; | ||
| } | ||
| return node ; | ||
| } ; | ||
| // Update the depth of the node, return its balance | ||
| BinaryTree.prototype.updateDepth = function( node ) { | ||
| if ( node.left ) { | ||
| if ( node.right ) { | ||
| // There are 2 children | ||
| node.depth = 1 + Math.max( node.left.depth , node.right.depth ) ; | ||
| return node.right.depth - node.left.depth ; | ||
| } | ||
| // Only a left child | ||
| node.depth = 1 + node.left.depth ; | ||
| return -node.left.depth ; | ||
| } | ||
| if ( node.right ) { | ||
| // Only a right child | ||
| node.depth = 1 + node.right.depth ; | ||
| return node.right.depth ; | ||
| } | ||
| // No child | ||
| node.depth = 1 ; | ||
| return 0 ; | ||
| } ; | ||
| // Just return the node balance without recomputing depth | ||
| BinaryTree.prototype.nodeBalance = function( node ) { | ||
| if ( node.left ) { | ||
| if ( node.right ) { | ||
| // There are 2 children | ||
| return node.right.depth - node.left.depth ; | ||
| } | ||
| // Only a left child | ||
| return -node.left.depth ; | ||
| } | ||
| if ( node.right ) { | ||
| // Only a right child | ||
| return node.right.depth ; | ||
| } | ||
| // No child | ||
| return 0 ; | ||
| } ; | ||
| // Assume node.right existance | ||
| BinaryTree.prototype.rotateNodeLeft = function( node ) { | ||
| var parent = node.parent ; | ||
| var replacerNode = node.right ; | ||
| // Attach the left of the right child to the current node's right | ||
| node.right = replacerNode.left ; | ||
| if ( node.right ) { node.right.parent = node ; } | ||
| // Attach the current node as the replacer's left | ||
| replacerNode.left = node ; | ||
| node.parent = replacerNode ; | ||
| // Attach the replacer to the parent node | ||
| replacerNode.parent = parent ; | ||
| if ( parent ) { | ||
| if ( parent.left === node ) { | ||
| parent.left = replacerNode ; | ||
| } | ||
| else { | ||
| parent.right = replacerNode ; | ||
| } | ||
| } | ||
| else { | ||
| this.trunc = replacerNode ; | ||
| } | ||
| // Now update the depth in the correct order | ||
| this.updateDepth( node ) ; | ||
| this.updateDepth( replacerNode ) ; | ||
| return replacerNode ; | ||
| } ; | ||
| // Same algo than rotateNodeRight, just swap all left/right | ||
| BinaryTree.prototype.rotateNodeRight = function( node ) { | ||
| var parent = node.parent ; | ||
| var replacerNode = node.left ; | ||
| // Attach the right of the left child to the current node's left | ||
| node.left = replacerNode.right ; | ||
| if ( node.left ) { node.left.parent = node ; } | ||
| // Attach the current node as the replacer's right | ||
| replacerNode.right = node ; | ||
| node.parent = replacerNode ; | ||
| // Attach the replacer to the parent node | ||
| replacerNode.parent = parent ; | ||
| if ( parent ) { | ||
| if ( parent.right === node ) { | ||
| parent.right = replacerNode ; | ||
| } | ||
| else { | ||
| parent.left = replacerNode ; | ||
| } | ||
| } | ||
| else { | ||
| this.trunc = replacerNode ; | ||
| } | ||
| // Now update the depth in the correct order | ||
| this.updateDepth( node ) ; | ||
| this.updateDepth( replacerNode ) ; | ||
| return replacerNode ; | ||
| } ; | ||
| /* | ||
| excluding: exclude the current key | ||
| direction: how to choose a node for a non uniqueKeys tree or when excluding the key: | ||
| * unset: don't care | ||
| * 1: forward direction, choose the left-most node | ||
| * -1: backward direction, choose the right-most node | ||
| */ | ||
| BinaryTree.prototype.getClosestNode = function( key , excluding , direction ) { | ||
| if ( ! this.trunc ) { return null ; } | ||
| var lastNode = null , | ||
| lastDirection = 0 , | ||
| lastLeftAncestor = null , | ||
| lastRightAncestor = null , | ||
| node = this.trunc ; | ||
| for ( ;; ) { | ||
| if ( node.key === key ) { | ||
| if ( excluding ) { | ||
| if ( this.uniqueKeys ) { | ||
| if ( direction > 0 ) { return this.nextNode( node ) ; } | ||
| return this.previousNode( node ) ; | ||
| } | ||
| if ( direction > 0 ) { | ||
| do { | ||
| node = this.nextNode( node ) ; | ||
| } while ( node && node.key === key ) ; | ||
| return node ; | ||
| } | ||
| do { | ||
| node = this.previousNode( node ) ; | ||
| } while ( node && node.key === key ) ; | ||
| return node ; | ||
| } | ||
| else if ( this.uniqueKeys ) { | ||
| return node ; | ||
| } | ||
| else if ( direction > 0 ) { | ||
| if ( ! node.left || node.left.key !== key ) { return node ; } | ||
| lastRightAncestor = lastNode = node ; | ||
| lastDirection = -1 ; | ||
| node = node.left ; | ||
| } | ||
| else { // if ( direction < 0 ) { | ||
| if ( ! node.right || node.right.key !== key ) { return node ; } | ||
| lastLeftAncestor = lastNode = node ; | ||
| lastDirection = 1 ; | ||
| node = node.right ; | ||
| } | ||
| } | ||
| else if ( this.isOrdered( node.key , key ) ) { | ||
| if ( ! node.right ) { | ||
| if ( direction < 0 ) { return node ; } | ||
| if ( lastDirection < 0 ) { return lastNode ; } | ||
| return lastRightAncestor ; | ||
| } | ||
| lastLeftAncestor = lastNode = node ; | ||
| lastDirection = 1 ; | ||
| node = node.right ; | ||
| } | ||
| else { | ||
| if ( ! node.left ) { | ||
| if ( direction > 0 ) { return node ; } | ||
| if ( lastDirection > 0 ) { return lastNode ; } | ||
| return lastLeftAncestor ; | ||
| } | ||
| lastRightAncestor = lastNode = node ; | ||
| lastDirection = -1 ; | ||
| node = node.left ; | ||
| } | ||
| } | ||
| } ; | ||
| // DEPRECATED? | ||
| // Delete subtree without re-balancing | ||
| BinaryTree.prototype.deleteSubTree = function( node , isRecursiveCall ) { | ||
| if ( node.left ) { this.deleteSubTree( node.left , true ) ; } | ||
| if ( node.right ) { this.deleteSubTree( node.right , true ) ; } | ||
| if ( ! isRecursiveCall ) { | ||
| if ( node.parent ) { | ||
| if ( node.parent.left === node ) { | ||
| node.parent.left = null ; | ||
| } | ||
| else { | ||
| node.parent.right = null ; | ||
| } | ||
| } | ||
| else { | ||
| this.trunc = null ; | ||
| } | ||
| } | ||
| this.length -- ; | ||
| this.destroyNode( node ) ; | ||
| } ; | ||
| /* | ||
| Debug stuffs | ||
| */ | ||
| BinaryTree.prototype.debug = function( node = this.trunc , spaces = 0 , prefix = '━' ) { | ||
| if ( ! node ) { | ||
| console.log( '(empty)' ) ; | ||
| return ; | ||
| } | ||
| var str = ' '.repeat( spaces ) + prefix + node.key ; | ||
| //var nextSpaces = str.length ? str.length - 1 : 0 ; | ||
| var nextSpaces = spaces + 2 ; | ||
| //str += ' (' + node.depth + ' | ' + this.nodeBalance( node ) + ')' ; | ||
| str += ' (' + this.nodeBalance( node ) + ')' ; | ||
| if ( node.right ) { | ||
| this.debug( node.right , nextSpaces , '┏' ) ; | ||
| } | ||
| console.log( str ) ; | ||
| if ( node.left ) { | ||
| this.debug( node.left , nextSpaces , '┗' ) ; | ||
| } | ||
| } ; | ||
| const assert = require( 'assert' ).strict ; | ||
| BinaryTree.prototype.sanityCheck = function( onlyStructure ) { | ||
| var runtime = { | ||
| length: 0 , | ||
| onlyStructure: !! onlyStructure | ||
| } ; | ||
| if ( this.trunc ) { | ||
| this.nodeSanityCheck( this.trunc , null , runtime ) ; | ||
| } | ||
| if ( ! runtime.onlyStructure ) { | ||
| assert.strictEqual( this.length , runtime.length , "Length mismatch" ) ; | ||
| } | ||
| } ; | ||
| BinaryTree.prototype.nodeSanityCheck = function( node , parentNode = null , runtime = null ) { | ||
| if ( runtime ) { | ||
| if ( ++ runtime.length > this.length && ! runtime.onlyStructure ) { | ||
| assert.fail( "Length mismatch: exceeding, key: " + node.key ) ; | ||
| } | ||
| } | ||
| assert.strictEqual( node.tree , this , "Current node doesn't belong to the current tree, key: " + node.key ) ; | ||
| assert.strictEqual( node.parent , parentNode , "Parent mismatch, key: " + node.key ) ; | ||
| if ( node.left ) { | ||
| if ( node.right ) { | ||
| // There are 2 children | ||
| if ( runtime && ! runtime.onlyStructure ) { | ||
| assert.strictEqual( node.depth , 1 + Math.max( node.left.depth , node.right.depth ) , "Depth mismatch (RL), key: " + node.key ) ; | ||
| } | ||
| this.nodeSanityCheck( node.left , node , runtime ) ; | ||
| this.nodeSanityCheck( node.right , node , runtime ) ; | ||
| } | ||
| else { | ||
| // Only a left child | ||
| if ( runtime && ! runtime.onlyStructure ) { | ||
| assert.strictEqual( node.depth , 1 + node.left.depth , "Depth mismatch (L), key: " + node.key ) ; | ||
| } | ||
| this.nodeSanityCheck( node.left , node , runtime ) ; | ||
| } | ||
| } | ||
| else if ( node.right ) { | ||
| // Only a right child | ||
| if ( runtime && ! runtime.onlyStructure ) { | ||
| assert.strictEqual( node.depth , 1 + node.right.depth , "Depth mismatch (R), key: " + node.key ) ; | ||
| } | ||
| this.nodeSanityCheck( node.right , node , runtime ) ; | ||
| } | ||
| else if ( runtime && ! runtime.onlyStructure ) { | ||
| // No child | ||
| assert.strictEqual( node.depth , 1 , "Depth mismatch (), key: " + node.key ) ; | ||
| } | ||
| if ( runtime && ! runtime.onlyStructure ) { | ||
| // Should be done after the recursive call, to avoid throwing the wrong error | ||
| var balance = this.nodeBalance( node ) ; | ||
| if ( Math.abs( balance ) > 1 ) { | ||
| assert.fail( "Unbalanced node (" + balance + "), key: " + node.key ) ; | ||
| } | ||
| } | ||
| } ; | ||
| /* | ||
| Chain Lightning | ||
| Copyright (c) 2018 Cédric Ronvel | ||
| The MIT License (MIT) | ||
| 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. | ||
| */ | ||
| "use strict" ; | ||
| // This is a “Doubly” Linked List | ||
| function LinkedList( ... elements ) { | ||
| this.head = null ; | ||
| this.tail = null ; | ||
| this.length = 0 ; | ||
| if ( elements.length ) { | ||
| this.push( ... elements ) ; | ||
| } | ||
| } | ||
| module.exports = LinkedList ; | ||
| function Node( list , previous , next , element ) { | ||
| this.list = list ; | ||
| this.previous = previous ; | ||
| this.next = next ; | ||
| this.element = element ; | ||
| } | ||
| LinkedList.Node = Node ; | ||
| // Array.prototype.includes() uses this for value equality | ||
| // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes | ||
| function isIdenticalTo( x , y ) { | ||
| return x === y || ( Number.isNaN( x ) && Number.isNaN( y ) ) ; | ||
| } | ||
| LinkedList.prototype.values = function *() { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| yield current.element ; | ||
| current = current.next ; | ||
| } | ||
| } ; | ||
| LinkedList.prototype[Symbol.iterator] = LinkedList.prototype.values ; | ||
| LinkedList.from = function( iterable ) { | ||
| var node , element , | ||
| list = new LinkedList() ; | ||
| for ( element of iterable ) { | ||
| node = new Node( list , list.tail , null , element ) ; | ||
| if ( ! list.head ) { | ||
| // This is the first element | ||
| list.head = list.tail = node ; | ||
| } | ||
| else { | ||
| list.tail.next = node ; | ||
| list.tail = node ; | ||
| } | ||
| list.length ++ ; | ||
| } | ||
| return list ; | ||
| } ; | ||
| // Similar to .keys() | ||
| LinkedList.prototype.nodes = function() { | ||
| var nodes = new Array( this.length ) , | ||
| index = 0 , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| nodes[ index ++ ] = current ; | ||
| current = current.next ; | ||
| } | ||
| return nodes ; | ||
| } ; | ||
| LinkedList.prototype.push = | ||
| LinkedList.prototype.append = function( ... elements ) { | ||
| var index , node ; | ||
| if ( ! elements.length ) { return ; } | ||
| this.length += elements.length ; | ||
| node = new Node( this , this.tail , null , elements[ 0 ] ) ; | ||
| if ( this.tail ) { | ||
| this.tail.next = node ; | ||
| this.tail = node ; | ||
| } | ||
| else { | ||
| this.head = this.tail = node ; | ||
| } | ||
| for ( index = 1 ; index < elements.length ; index ++ ) { | ||
| node = new Node( this , this.tail , null , elements[ index ] ) ; | ||
| this.tail.next = node ; | ||
| this.tail = node ; | ||
| } | ||
| } ; | ||
| LinkedList.prototype.unshift = | ||
| LinkedList.prototype.prepend = function( ... elements ) { | ||
| var index , node ; | ||
| if ( ! elements.length ) { return ; } | ||
| this.length += elements.length ; | ||
| index = elements.length - 1 ; | ||
| node = new Node( this , null , this.head , elements[ index ] ) ; | ||
| if ( this.head ) { | ||
| this.head.previous = node ; | ||
| this.head = node ; | ||
| } | ||
| else { | ||
| this.head = this.tail = node ; | ||
| } | ||
| while ( index -- ) { | ||
| node = new Node( this , null , this.head , elements[ index ] ) ; | ||
| this.head.previous = node ; | ||
| this.head = node ; | ||
| } | ||
| } ; | ||
| LinkedList.prototype.pop = function() { | ||
| if ( ! this.tail ) { return ; } | ||
| var node = this.tail ; | ||
| this.tail = node.previous ; | ||
| if ( this.tail ) { | ||
| this.tail.next = null ; | ||
| } | ||
| else { | ||
| // That was the last element | ||
| this.head = null ; | ||
| } | ||
| this.length -- ; | ||
| return node.element ; | ||
| } ; | ||
| LinkedList.prototype.shift = function() { | ||
| if ( ! this.head ) { return ; } | ||
| var node = this.head ; | ||
| this.head = node.next ; | ||
| if ( this.head ) { | ||
| this.head.previous = null ; | ||
| } | ||
| else { | ||
| // That was the last element | ||
| this.tail = null ; | ||
| } | ||
| this.length -- ; | ||
| return node.element ; | ||
| } ; | ||
| /* | ||
| Advanced Array-like features | ||
| */ | ||
| LinkedList.prototype.nodeOf = function( element , fromNode ) { | ||
| var current ; | ||
| if ( fromNode ) { | ||
| if ( fromNode.list !== this ) { return ; } | ||
| current = fromNode ; | ||
| } | ||
| else { | ||
| current = this.head ; | ||
| } | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return current ; } | ||
| current = current.next ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| LinkedList.prototype.lastNodeOf = function( element , fromNode ) { | ||
| var current ; | ||
| if ( fromNode ) { | ||
| if ( fromNode.list !== this ) { return ; } | ||
| current = fromNode ; | ||
| } | ||
| else { | ||
| current = this.tail ; | ||
| } | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return current ; } | ||
| current = current.previous ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| // Almost like .indexOf() | ||
| LinkedList.prototype.includes = function( element , fromNode ) { | ||
| var current ; | ||
| if ( fromNode ) { | ||
| if ( fromNode.list !== this ) { return false ; } | ||
| current = fromNode ; | ||
| } | ||
| else { | ||
| current = this.head ; | ||
| } | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return true ; } | ||
| current = current.next ; | ||
| } | ||
| return false ; | ||
| } ; | ||
| LinkedList.prototype.forEach = function( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| fn.call( thisArg , current.element , current , this ) ; | ||
| current = current.next ; | ||
| } | ||
| } ; | ||
| LinkedList.prototype.some = function( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { return true ; } | ||
| current = current.next ; | ||
| } | ||
| return false ; | ||
| } ; | ||
| LinkedList.prototype.every = function( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( ! fn.call( thisArg , current.element , current , this ) ) { return false ; } | ||
| current = current.next ; | ||
| } | ||
| return true ; | ||
| } ; | ||
| LinkedList.prototype.find = function( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { | ||
| return current.element ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| return ; | ||
| } ; | ||
| LinkedList.prototype.findNode = function( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { | ||
| return current ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| // Create a new LinkedList | ||
| LinkedList.prototype.map = function( fn , thisArg ) { | ||
| var newList = new LinkedList() , | ||
| newElement , | ||
| node , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| newElement = fn.call( thisArg , current.element , current , this ) ; | ||
| node = new Node( newList , newList.tail , null , newElement ) ; | ||
| if ( newList.tail ) { | ||
| newList.tail.next = node ; | ||
| newList.tail = node ; | ||
| } | ||
| else { | ||
| newList.head = newList.tail = node ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| newList.length = this.length ; | ||
| return newList ; | ||
| } ; | ||
| LinkedList.prototype.reduce = function( fn , accumulator ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| accumulator = fn( accumulator , current.element , current , this ) ; | ||
| current = current.next ; | ||
| } | ||
| return accumulator ; | ||
| } ; | ||
| LinkedList.prototype.reduceRight = function( fn , accumulator ) { | ||
| var current = this.tail ; | ||
| while ( current ) { | ||
| accumulator = fn( accumulator , current.element , current , this ) ; | ||
| current = current.previous ; | ||
| } | ||
| return accumulator ; | ||
| } ; | ||
| LinkedList.prototype.filter = function( fn , thisArg ) { | ||
| var newList = new LinkedList() , | ||
| node , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { | ||
| node = new Node( newList , newList.tail , null , current.element ) ; | ||
| if ( newList.tail ) { | ||
| newList.tail.next = node ; | ||
| newList.tail = node ; | ||
| } | ||
| else { | ||
| newList.head = newList.tail = node ; | ||
| } | ||
| newList.length ++ ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| return newList ; | ||
| } ; | ||
| LinkedList.prototype.reverse = function() { | ||
| var tmp , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| tmp = current.next ; | ||
| current.next = current.previous ; | ||
| current.previous = tmp ; | ||
| // Note: it's already inverted, so use previous instead of next | ||
| current = current.previous ; | ||
| } | ||
| tmp = this.head ; | ||
| this.head = this.tail ; | ||
| this.tail = tmp ; | ||
| return this ; | ||
| } ; | ||
| /* | ||
| Advanced custom features | ||
| */ | ||
| LinkedList.prototype.get = function( node ) { | ||
| if ( ! node || node.list !== this ) { return ; } | ||
| return node.element ; | ||
| } ; | ||
| LinkedList.prototype.set = function( node , element ) { | ||
| if ( ! node || node.list !== this ) { return ; } | ||
| node.element = element ; | ||
| } ; | ||
| LinkedList.prototype.deleteNode = | ||
| LinkedList.prototype.removeNode = function( node ) { | ||
| if ( ! node || node.list !== this ) { return false ; } | ||
| if ( node.previous ) { node.previous.next = node.next ; } | ||
| if ( node.next ) { node.next.previous = node.previous ; } | ||
| if ( this.head === node ) { this.head = node.next ; } | ||
| if ( this.tail === node ) { this.tail = node.previous ; } | ||
| node.list = node.previous = node.next = null ; | ||
| this.length -- ; | ||
| return true ; | ||
| } ; | ||
| // Delete all occurences of a value | ||
| LinkedList.prototype.delete = | ||
| LinkedList.prototype.remove = function( value ) { | ||
| this.inPlaceFilter( e => ! isIdenticalTo( e , value ) ) ; | ||
| } ; | ||
| LinkedList.prototype.moveAfter = function( node , afterNode ) { | ||
| if ( | ||
| ! node || node.list !== this || ! afterNode || afterNode.list !== this || | ||
| node === afterNode || afterNode === node.previous | ||
| ) { | ||
| return false ; | ||
| } | ||
| var beforeNode = afterNode.next ; | ||
| if ( this.head === node ) { this.head = node.next ; } | ||
| if ( this.tail === node ) { this.tail = node.previous ; } | ||
| else if ( this.tail === afterNode ) { this.tail = node ; } | ||
| if ( node.previous ) { node.previous.next = node.next ; } | ||
| if ( node.next ) { node.next.previous = node.previous ; } | ||
| node.previous = afterNode ; | ||
| afterNode.next = node ; | ||
| node.next = beforeNode ; | ||
| if ( beforeNode ) { beforeNode.previous = node ; } | ||
| return true ; | ||
| } ; | ||
| LinkedList.prototype.moveBefore = function( node , beforeNode ) { | ||
| if ( | ||
| ! node || node.list !== this || ! beforeNode || beforeNode.list !== this || | ||
| node === beforeNode || beforeNode === node.next | ||
| ) { | ||
| return false ; | ||
| } | ||
| var afterNode = beforeNode.previous ; | ||
| if ( this.head === node ) { this.head = node.next ; } | ||
| else if ( this.head === beforeNode ) { this.head = node ; } | ||
| if ( this.tail === node ) { this.tail = node.previous ; } | ||
| if ( node.previous ) { node.previous.next = node.next ; } | ||
| if ( node.next ) { node.next.previous = node.previous ; } | ||
| node.next = beforeNode ; | ||
| beforeNode.previous = node ; | ||
| node.previous = afterNode ; | ||
| if ( afterNode ) { afterNode.next = node ; } | ||
| return true ; | ||
| } ; | ||
| LinkedList.prototype.moveToTail = function( node ) { return this.moveAfter( node , this.tail ) ; } ; | ||
| LinkedList.prototype.moveToHead = function( node ) { return this.moveBefore( node , this.head ) ; } ; | ||
| LinkedList.prototype.insertAfter = function( afterNode , ... elements ) { | ||
| if ( afterNode.list !== this || ! elements.length ) { return ; } | ||
| var node , index , | ||
| lastNode = afterNode , | ||
| beforeNode = afterNode.next ; | ||
| for ( index = 0 ; index < elements.length ; index ++ ) { | ||
| node = new Node( this , lastNode , null , elements[ index ] ) ; | ||
| lastNode.next = node ; | ||
| lastNode = node ; | ||
| } | ||
| this.length += elements.length ; | ||
| if ( beforeNode ) { | ||
| lastNode.next = beforeNode ; | ||
| beforeNode.previous = lastNode ; | ||
| } | ||
| else { | ||
| this.tail = lastNode ; | ||
| } | ||
| } ; | ||
| LinkedList.prototype.insertBefore = function( beforeNode , ... elements ) { | ||
| if ( beforeNode.list !== this || ! elements.length ) { return ; } | ||
| var node , | ||
| index = elements.length , | ||
| lastNode = beforeNode , | ||
| afterNode = beforeNode.previous ; | ||
| while ( index -- ) { | ||
| node = new Node( this , null , lastNode , elements[ index ] ) ; | ||
| lastNode.previous = node ; | ||
| lastNode = node ; | ||
| } | ||
| this.length += elements.length ; | ||
| if ( afterNode ) { | ||
| lastNode.previous = afterNode ; | ||
| afterNode.next = lastNode ; | ||
| } | ||
| else { | ||
| this.head = lastNode ; | ||
| } | ||
| } ; | ||
| LinkedList.prototype.inPlaceFilter = function( fn , thisArg ) { | ||
| var lastNode = null , | ||
| nextNode , | ||
| node = this.head ; | ||
| this.head = null ; | ||
| while ( node ) { | ||
| node.previous = lastNode ; | ||
| nextNode = node.next ; // backup that | ||
| if ( fn.call( thisArg , node.element , node , this ) ) { | ||
| if ( ! this.head ) { this.head = node ; } | ||
| lastNode = node ; | ||
| } | ||
| else { | ||
| if ( lastNode ) { lastNode.next = node.next ; } | ||
| this.length -- ; | ||
| node.list = node.previous = node.next = null ; | ||
| } | ||
| node = nextNode ; | ||
| } | ||
| this.tail = lastNode ; | ||
| return this ; | ||
| } ; | ||
| /* | ||
| Debug stuffs | ||
| */ | ||
| const assert = require( 'assert' ).strict ; | ||
| LinkedList.prototype.sanityCheck = function() { | ||
| var length = 0 , | ||
| lastNode = null , | ||
| node = this.head ; | ||
| while ( node && ++ length <= this.length ) { | ||
| assert.strictEqual( node.list , this , "Current node doesn't belong to the current list" ) ; | ||
| assert.strictEqual( node.previous , lastNode , "Current node's previous is the real previous node" ) ; | ||
| // Useless because we precisely come from that lastNode | ||
| //if ( lastNode ) { assert( lastNode.next , node ) ; } | ||
| lastNode = node ; | ||
| node = node.next ; | ||
| } | ||
| assert.strictEqual( this.length , length , "Length mismatch" ) ; | ||
| assert.strictEqual( this.tail , lastNode , "The last node is not the tail" ) ; | ||
| } ; | ||
| /* | ||
| Chain Lightning | ||
| Copyright (c) 2018 Cédric Ronvel | ||
| The MIT License (MIT) | ||
| 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. | ||
| */ | ||
| "use strict" ; | ||
| /* global describe, it, before, after */ | ||
| const lib = require( '..' ) ; | ||
| const BinaryTree = lib.BinaryTree ; | ||
| /* Tests */ | ||
| describe( "Binary Tree" , () => { | ||
| describe( "Basic features" , () => { | ||
| it( "constructor arguments should be added as elements" , () => { | ||
| var tree ; | ||
| tree = new BinaryTree() ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , 'jack' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ] ) ; | ||
| tree.sanityCheck() ; | ||
| } ) ; | ||
| it( "BinaryTree.from() should create a tree from any iterable" , () => { | ||
| var tree ; | ||
| tree = BinaryTree.from( new Set() ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = BinaryTree.from( new Set( [ 'jack' ] ) ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = BinaryTree.from( new Set( [ 'jack' , 'jean' , 'steve' ] ) ) ; | ||
| tree.sanityCheck() ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| } ) ; | ||
| it( ".values(), .keys() and iterator" , () => { | ||
| var tree ; | ||
| tree = new BinaryTree() ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.set( 5 , 'steve' ) ; | ||
| tree.set( 2.5 , 'john' ) ; | ||
| tree.set( 2.7 , 'robert' ) ; | ||
| tree.set( 2.8 , 'johnson' ) ; | ||
| tree.set( 2.75 , 'boris' ) ; | ||
| tree.set( 6 , 'bobby' ) ; | ||
| tree.set( 2.85 , 'carl' ) ; | ||
| tree.set( 2.72 , 'tom' ) ; | ||
| tree.set( 2.76 , 'roger' ) ; | ||
| tree.set( 2.77 , 'vlad' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| expect( tree.keys() ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| expect( [ ... tree.values() ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| } ) ; | ||
| it( "set and get elements" , () => { | ||
| var tree ; | ||
| tree = new BinaryTree() ; | ||
| // Try getting unexisting keys | ||
| expect( tree.get( 0 ) ).to.be( undefined ) ; | ||
| expect( tree.get( 9 ) ).to.be( undefined ) ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'jack' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 5 , 'steve' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'jack' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 2.5 , 'john' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'jack' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 2.7 , 'robert' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'jack' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| // Force a left-right heavy | ||
| tree.set( 2.8 , 'johnson' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'johnson' , 'jack' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 2.75 , 'boris' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'boris' , 'johnson' , 'jack' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 6 , 'bobby' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'boris' , 'johnson' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 2.85 , 'carl' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'boris' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| tree.sanityCheck() ; | ||
| // Force a right-left heavy | ||
| tree.set( 2.72 , 'tom' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| tree.sanityCheck() ; | ||
| //tree.debug() ; | ||
| expect( tree.get( 3 ) ).to.be( 'jack' ) ; | ||
| expect( tree.get( 2 ) ).to.be( 'jean' ) ; | ||
| expect( tree.get( 5 ) ).to.be( 'steve' ) ; | ||
| expect( tree.get( 2.5 ) ).to.be( 'john' ) ; | ||
| expect( tree.get( 2.7 ) ).to.be( 'robert' ) ; | ||
| expect( tree.get( 2.8 ) ).to.be( 'johnson' ) ; | ||
| expect( tree.get( 2.75 ) ).to.be( 'boris' ) ; | ||
| expect( tree.get( 6 ) ).to.be( 'bobby' ) ; | ||
| expect( tree.get( 2.85 ) ).to.be( 'carl' ) ; | ||
| expect( tree.get( 2.72 ) ).to.be( 'tom' ) ; | ||
| // Try getting unexisting keys | ||
| expect( tree.get( 0 ) ).to.be( undefined ) ; | ||
| expect( tree.get( 9 ) ).to.be( undefined ) ; | ||
| } ) ; | ||
| it( "duplicated keys and the 'uniqueKeys' option" , () => { | ||
| var tree ; | ||
| tree = new BinaryTree() ; | ||
| // Without uniqueKeys | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'jack' ] ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.set( 2 , 'bob' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'bob' , 'jack' ] ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2 , 3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| // With uniqueKeys | ||
| tree = new BinaryTree( { uniqueKeys: true } ) ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.set( 2 , 'bob' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'bob' , 'jack' ] ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| } ) ; | ||
| it( ".insert()" , () => { | ||
| var tree ; | ||
| tree = new BinaryTree() ; | ||
| expect( tree ).to.have.length( 0 ) ; | ||
| tree.insert( 'bob' ) ; | ||
| tree.insert( 'bill' ) ; | ||
| tree.insert( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'bob' , 'bill' , 'jack' , 'jean' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree() ; | ||
| tree.insert( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| tree.sanityCheck() ; | ||
| } ) ; | ||
| it( "delete by key" , () => { | ||
| var tree ; | ||
| tree = new BinaryTree() ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.delete( 3 ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree() ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.delete( 3 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree.delete( 2 ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree() ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.set( 5 , 'steve' ) ; | ||
| tree.set( 2.5 , 'john' ) ; | ||
| tree.set( 2.7 , 'robert' ) ; | ||
| tree.set( 2.8 , 'johnson' ) ; | ||
| tree.set( 2.75 , 'boris' ) ; | ||
| tree.set( 6 , 'bobby' ) ; | ||
| tree.set( 2.85 , 'carl' ) ; | ||
| tree.set( 2.72 , 'tom' ) ; | ||
| tree.set( 2.76 , 'roger' ) ; | ||
| tree.set( 2.77 , 'vlad' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( '\n\nTree:' ) ; tree.debug() ; | ||
| // Delete a leaf and force a left-right heavy | ||
| tree.delete( 2.85 ) ; | ||
| tree.sanityCheck() ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| expect( tree.get( 2.85 ) ).to.be( undefined ) ; | ||
| // Delete a node with only one child | ||
| tree.delete( 2.5 ) ; | ||
| tree.sanityCheck() ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| expect( tree.get( 2.5 ) ).to.be( undefined ) ; | ||
| // Delete a node with only two children that are not leaves | ||
| tree.delete( 2.8 ) ; | ||
| tree.sanityCheck() ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| expect( tree.get( 2.8 ) ).to.be( undefined ) ; | ||
| // Delete the trunc node, the tree should replace the trunc node, it also cause a right heavy | ||
| tree.delete( 2.75 ) ; | ||
| tree.sanityCheck() ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jean' , 'robert' , 'tom' , 'roger' , 'vlad' , 'jack' , 'steve' , 'bobby' ] ) ; | ||
| expect( tree.get( 2.75 ) ).to.be( undefined ) ; | ||
| //tree.debug() ; | ||
| } ) ; | ||
| } ) ; | ||
| describe( "Advanced Array-like features" , () => { | ||
| it( ".keyOf()/.lastKeyOf()" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| tree = new BinaryTree( { uniqueKeys: true } , e1 , e2 , e3 ) ; | ||
| expect( tree.keyOf( e2 ) ).to.be( 1 ) ; | ||
| expect( tree.keyOf( e4 ) ).to.be( undefined ) ; | ||
| tree.insert( e2 , e2 , e2 ) ; | ||
| tree.set( tree.keyOf( e2 ) , e4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bob' } ] ) ; | ||
| tree.set( tree.lastKeyOf( e2 ) , e4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bobby' } ] ) ; | ||
| } ) ; | ||
| it( ".includes()" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| tree = new BinaryTree() ; | ||
| expect( tree.includes( e2 ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e1 ) ; | ||
| expect( tree.includes( e2 ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e1 , e3 ) ; | ||
| expect( tree.includes( e2 ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e2 ) ; | ||
| expect( tree.includes( e2 ) ).to.be.true() ; | ||
| tree = new BinaryTree( null , e2 , e2 ) ; | ||
| expect( tree.includes( e2 ) ).to.be.true() ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ) ; | ||
| expect( tree.includes( e2 ) ).to.be.true() ; | ||
| tree = new BinaryTree( null , e1 , e3 , e2 ) ; | ||
| expect( tree.includes( e2 ) ).to.be.true() ; | ||
| tree = new BinaryTree( null , e2 , e1 , e3 ) ; | ||
| expect( tree.includes( e2 ) ).to.be.true() ; | ||
| } ) ; | ||
| it( ".forEach()" , () => { | ||
| var tree , | ||
| accumulator = [] , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| tree = new BinaryTree() ; | ||
| tree.forEach( element => accumulator.push( element.v ) ) ; | ||
| expect( accumulator ).to.equal( [] ) ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ) ; | ||
| tree.forEach( element => accumulator.push( element.v ) ) ; | ||
| expect( accumulator ).to.equal( [ 'jack' , 'bob' , 'steve' ] ) ; | ||
| } ) ; | ||
| it( ".some()/.every()" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| tree = new BinaryTree() ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.false() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| tree = new BinaryTree( null , e1 ) ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.false() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e2 ) ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| tree = new BinaryTree( null , e1 , e2 ) ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e2 , e1 ) ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ) ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e1 , e2 , e2 , e3 ) ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| tree = new BinaryTree( null , e2 , e2 , e2 ) ; | ||
| expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( tree.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| } ) ; | ||
| it( ".find()" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bob' } ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ) ; | ||
| expect( tree.find( element => element.v === 'bob' ) ).to.be( e2 ) ; | ||
| expect( tree.find( element => element.v === 'bobby' ) ).to.be( undefined ) ; | ||
| tree.insert( e4 ) ; | ||
| expect( tree.find( element => element.v === 'bob' ) ).to.be( e2 ) ; | ||
| tree.delete( 1 ) ; | ||
| expect( tree.find( element => element.v === 'bob' ) ).to.be( e4 ) ; | ||
| } ) ; | ||
| it( ".findKey()" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bob' } ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ) ; | ||
| expect( tree.findKey( element => element.v === 'bob' ) ).to.be( 1 ) ; | ||
| expect( tree.findKey( element => element.v === 'bobby' ) ).to.be( undefined ) ; | ||
| expect( tree.findKey( element => element.v === 'bobby' ) ).to.be( undefined ) ; | ||
| tree.insert( e4 ) ; | ||
| expect( tree.findKey( element => element.v === 'bob' ) ).to.be( 1 ) ; | ||
| tree.delete( 1 ) ; | ||
| expect( tree.findKey( element => element.v === 'bob' ) ).to.be( 3 ) ; | ||
| } ) ; | ||
| it( ".arrayMap()" , () => { | ||
| var array , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| array = new BinaryTree( null ).arrayMap( element => element.v + element.v ) ; | ||
| expect( array ).to.equal( [] ) ; | ||
| array = new BinaryTree( null , e1 ).arrayMap( element => element.v + element.v ) ; | ||
| expect( array ).to.equal( [ 'jackjack' ] ) ; | ||
| array = new BinaryTree( null , e1 , e2 , e3 ).arrayMap( element => element.v + element.v ) ; | ||
| expect( array ).to.equal( [ 'jackjack' , 'bobbob' , 'stevesteve' ] ) ; | ||
| } ) ; | ||
| it( ".reduce()/.reduceRight" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| tree = new BinaryTree() ; | ||
| expect( tree.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ; | ||
| expect( tree.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ; | ||
| tree = new BinaryTree( null , e1 ) ; | ||
| expect( tree.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ; | ||
| expect( tree.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ) ; | ||
| expect( tree.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jackbobsteve' ) ; | ||
| expect( tree.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'stevebobjack' ) ; | ||
| } ) ; | ||
| it( ".arrayFilter()" , () => { | ||
| var array , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| array = new BinaryTree().filter( element => element.v.length >= 4 ) ; | ||
| expect( array ).to.equal( [] ) ; | ||
| array = new BinaryTree( null , e1 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( array ).to.equal( [ e1 ] ) ; | ||
| array = new BinaryTree( null , e1 ).filter( element => element.v.length < 4 ) ; | ||
| expect( array ).to.equal( [] ) ; | ||
| array = new BinaryTree( null , e1 , e2 , e3 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( array ).to.equal( [ e1 , e3 ] ) ; | ||
| array = new BinaryTree( null , e1 , e3 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( array ).to.equal( [ e1 , e3 ] ) ; | ||
| array = new BinaryTree( null , e2 , e2 , e2 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( array ).to.equal( [] ) ; | ||
| array = new BinaryTree( null , e1 , e2 , e3 ).filter( element => element.v.length < 4 ) ; | ||
| expect( array ).to.equal( [ e2 ] ) ; | ||
| } ) ; | ||
| } ) ; | ||
| describe( "Advanced custom features" , () => { | ||
| it( ".deleteValue()" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ) ; | ||
| tree.deleteValue( e2 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree() ; | ||
| tree.deleteValue( e2 ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e2 ) ; | ||
| tree.deleteValue( e2 ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e2 , e1 , e3 ) ; | ||
| tree.deleteValue( e2 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e1 , e3 , e2 ) ; | ||
| tree.deleteValue( e2 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| // Remove all occurences | ||
| tree = new BinaryTree( null , e2 , e2 , e2 , e1 , e2 , e3 , e2 ) ; | ||
| tree.deleteValue( e2 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| // NaN test | ||
| tree = new BinaryTree( null , NaN , NaN , NaN , e1 , NaN , e3 , NaN ) ; | ||
| tree.deleteValue( NaN ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| } ) ; | ||
| it( ".inPlaceFilter()" , () => { | ||
| var tree , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| tree = new BinaryTree( null , e2 , e2 , e2 ) ; | ||
| expect( tree.inPlaceFilter( () => true ) ).to.be( tree ) ; | ||
| tree = new BinaryTree().inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e1 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( true , e1 ).inPlaceFilter( element => element.v.length < 4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e1 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e2 , e2 , e2 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| tree = new BinaryTree( null , e1 , e2 , e3 ).inPlaceFilter( element => element.v.length < 4 ) ; | ||
| expect( [ ... tree ] ).to.equal( [ e2 ] ) ; | ||
| tree.sanityCheck() ; | ||
| } ) ; | ||
| it( ".truncateBefore()" , () => { | ||
| var tree ; | ||
| var reset = () => { | ||
| tree = new BinaryTree() ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.set( 5 , 'steve' ) ; | ||
| tree.set( 2.5 , 'john' ) ; | ||
| tree.set( 2.7 , 'robert' ) ; | ||
| tree.set( 2.8 , 'johnson' ) ; | ||
| tree.set( 2.75 , 'boris' ) ; | ||
| tree.set( 6 , 'bobby' ) ; | ||
| tree.set( 2.85 , 'carl' ) ; | ||
| tree.set( 2.72 , 'tom' ) ; | ||
| tree.set( 2.76 , 'roger' ) ; | ||
| tree.set( 2.77 , 'vlad' ) ; | ||
| } ; | ||
| reset() ; | ||
| tree.truncateBefore( 1 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 1 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.72 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.72 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.73 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.73 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.8 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.8 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.85 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.85 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.9 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 2.9 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 4 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 4 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 5 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 5 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 6 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 6 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 7 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateBefore( 7 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| } ) ; | ||
| it( ".truncateAfter()" , () => { | ||
| var tree ; | ||
| var reset = () => { | ||
| tree = new BinaryTree() ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.set( 5 , 'steve' ) ; | ||
| tree.set( 2.5 , 'john' ) ; | ||
| tree.set( 2.7 , 'robert' ) ; | ||
| tree.set( 2.8 , 'johnson' ) ; | ||
| tree.set( 2.75 , 'boris' ) ; | ||
| tree.set( 6 , 'bobby' ) ; | ||
| tree.set( 2.85 , 'carl' ) ; | ||
| tree.set( 2.72 , 'tom' ) ; | ||
| tree.set( 2.76 , 'roger' ) ; | ||
| tree.set( 2.77 , 'vlad' ) ; | ||
| } ; | ||
| reset() ; | ||
| tree.truncateAfter( 1 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 1 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 ] ) ; | ||
| tree.sanityCheck() ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.72 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.72 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.73 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.73 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.8 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.8 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.85 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.85 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.9 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 2.9 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 4 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 4 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 5 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 5 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 6 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 6 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 7 , true ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| reset() ; | ||
| tree.truncateAfter( 7 ) ; | ||
| expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ; | ||
| tree.sanityCheck( true ) ; | ||
| //console.log( "Tree after truncate:" ) ; | ||
| //tree.debug() ; | ||
| } ) ; | ||
| } ) ; | ||
| describe( "Internal methods" , () => { | ||
| it( ".insertOrdered()" , () => { | ||
| var tree ; | ||
| tree = new BinaryTree() ; | ||
| tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ] ) ; | ||
| tree.sanityCheck() ; | ||
| //tree.debug() ; | ||
| tree = new BinaryTree() ; | ||
| tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' ] ) ; | ||
| tree.sanityCheck() ; | ||
| //tree.debug() ; | ||
| tree = new BinaryTree() ; | ||
| tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' ] ) ; | ||
| tree.sanityCheck() ; | ||
| //tree.debug() ; | ||
| tree = new BinaryTree() ; | ||
| tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' , 'jörgl' , 'edmund' , 'karl' , 'roberto' , 'juan' , 'augustus' , 'edward' , 'leon' , 'sergio' ) ; | ||
| expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' , 'jörgl' , 'edmund' , 'karl' , 'roberto' , 'juan' , 'augustus' , 'edward' , 'leon' , 'sergio' ] ) ; | ||
| tree.sanityCheck() ; | ||
| //tree.debug() ; | ||
| } ) ; | ||
| it( ".getClosestNode()" , () => { | ||
| var tree = new BinaryTree() ; | ||
| tree.set( 3 , 'jack' ) ; | ||
| tree.set( 2 , 'jean' ) ; | ||
| tree.set( 5 , 'steve' ) ; | ||
| tree.set( 2.5 , 'john' ) ; | ||
| tree.set( 2.7 , 'robert' ) ; | ||
| tree.set( 2.8 , 'johnson' ) ; | ||
| tree.set( 2.75 , 'boris' ) ; | ||
| tree.set( 6 , 'bobby' ) ; | ||
| tree.set( 2.85 , 'carl' ) ; | ||
| tree.set( 2.72 , 'tom' ) ; | ||
| tree.set( 2.76 , 'roger' ) ; | ||
| tree.set( 2.77 , 'vlad' ) ; | ||
| //tree.debug() ; | ||
| // Including existing | ||
| expect( tree.getClosestNode( 2 , false , -1 ).key ).to.be( 2 ) ; | ||
| expect( tree.getClosestNode( 2 , false , 1 ).key ).to.be( 2 ) ; | ||
| expect( tree.getClosestNode( 2.72 , false , -1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.72 , false , 1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.8 , false , -1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.8 , false , 1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.85 , false , -1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 2.85 , false , 1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 6 , false , -1 ).key ).to.be( 6 ) ; | ||
| expect( tree.getClosestNode( 6 , false , 1 ).key ).to.be( 6 ) ; | ||
| // Excluding existing | ||
| expect( tree.getClosestNode( 2 , true , -1 ) ).to.be( null ) ; | ||
| expect( tree.getClosestNode( 2 , true , 1 ).key ).to.be( 2.5 ) ; | ||
| expect( tree.getClosestNode( 2.5 , true , -1 ).key ).to.be( 2 ) ; | ||
| expect( tree.getClosestNode( 2.5 , true , 1 ).key ).to.be( 2.7 ) ; | ||
| expect( tree.getClosestNode( 2.7 , true , -1 ).key ).to.be( 2.5 ) ; | ||
| expect( tree.getClosestNode( 2.7 , true , 1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.72 , true , -1 ).key ).to.be( 2.7 ) ; | ||
| expect( tree.getClosestNode( 2.72 , true , 1 ).key ).to.be( 2.75 ) ; | ||
| expect( tree.getClosestNode( 2.75 , true , -1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.75 , true , 1 ).key ).to.be( 2.76 ) ; | ||
| expect( tree.getClosestNode( 2.76 , true , -1 ).key ).to.be( 2.75 ) ; | ||
| expect( tree.getClosestNode( 2.76 , true , 1 ).key ).to.be( 2.77 ) ; | ||
| expect( tree.getClosestNode( 2.77 , true , -1 ).key ).to.be( 2.76 ) ; | ||
| expect( tree.getClosestNode( 2.77 , true , 1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.8 , true , -1 ).key ).to.be( 2.77 ) ; | ||
| expect( tree.getClosestNode( 2.8 , true , 1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 2.85 , true , -1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.85 , true , 1 ).key ).to.be( 3 ) ; | ||
| expect( tree.getClosestNode( 3 , true , -1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 3 , true , 1 ).key ).to.be( 5 ) ; | ||
| expect( tree.getClosestNode( 6 , true , -1 ).key ).to.be( 5 ) ; | ||
| expect( tree.getClosestNode( 6 , true , 1 ) ).to.be( null ) ; | ||
| // Including unexisting | ||
| expect( tree.getClosestNode( 1 , false , -1 ) ).to.be( null ) ; | ||
| expect( tree.getClosestNode( 1 , false , 1 ).key ).to.be( 2 ) ; | ||
| expect( tree.getClosestNode( 2.1 , false , -1 ).key ).to.be( 2 ) ; | ||
| expect( tree.getClosestNode( 2.1 , false , 1 ).key ).to.be( 2.5 ) ; | ||
| expect( tree.getClosestNode( 2.6 , false , -1 ).key ).to.be( 2.5 ) ; | ||
| expect( tree.getClosestNode( 2.6 , false , 1 ).key ).to.be( 2.7 ) ; | ||
| expect( tree.getClosestNode( 2.71 , false , -1 ).key ).to.be( 2.7 ) ; | ||
| expect( tree.getClosestNode( 2.71 , false , 1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.73 , false , -1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.73 , false , 1 ).key ).to.be( 2.75 ) ; | ||
| expect( tree.getClosestNode( 2.755 , false , -1 ).key ).to.be( 2.75 ) ; | ||
| expect( tree.getClosestNode( 2.755 , false , 1 ).key ).to.be( 2.76 ) ; | ||
| expect( tree.getClosestNode( 2.765 , false , -1 ).key ).to.be( 2.76 ) ; | ||
| expect( tree.getClosestNode( 2.765 , false , 1 ).key ).to.be( 2.77 ) ; | ||
| expect( tree.getClosestNode( 2.78 , false , -1 ).key ).to.be( 2.77 ) ; | ||
| expect( tree.getClosestNode( 2.78 , false , 1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.84 , false , -1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.84 , false , 1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 2.9 , false , -1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 2.9 , false , 1 ).key ).to.be( 3 ) ; | ||
| expect( tree.getClosestNode( 4 , false , -1 ).key ).to.be( 3 ) ; | ||
| expect( tree.getClosestNode( 4 , false , 1 ).key ).to.be( 5 ) ; | ||
| expect( tree.getClosestNode( 7 , false , -1 ).key ).to.be( 6 ) ; | ||
| expect( tree.getClosestNode( 7 , false , 1 ) ).to.be( null ) ; | ||
| // Excluding unexisting -- should be identical to 'including unexisting' | ||
| expect( tree.getClosestNode( 1 , true , -1 ) ).to.be( null ) ; | ||
| expect( tree.getClosestNode( 1 , true , 1 ).key ).to.be( 2 ) ; | ||
| expect( tree.getClosestNode( 2.1 , true , -1 ).key ).to.be( 2 ) ; | ||
| expect( tree.getClosestNode( 2.1 , true , 1 ).key ).to.be( 2.5 ) ; | ||
| expect( tree.getClosestNode( 2.6 , true , -1 ).key ).to.be( 2.5 ) ; | ||
| expect( tree.getClosestNode( 2.6 , true , 1 ).key ).to.be( 2.7 ) ; | ||
| expect( tree.getClosestNode( 2.71 , true , -1 ).key ).to.be( 2.7 ) ; | ||
| expect( tree.getClosestNode( 2.71 , true , 1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.73 , true , -1 ).key ).to.be( 2.72 ) ; | ||
| expect( tree.getClosestNode( 2.73 , true , 1 ).key ).to.be( 2.75 ) ; | ||
| expect( tree.getClosestNode( 2.755 , true , -1 ).key ).to.be( 2.75 ) ; | ||
| expect( tree.getClosestNode( 2.755 , true , 1 ).key ).to.be( 2.76 ) ; | ||
| expect( tree.getClosestNode( 2.765 , true , -1 ).key ).to.be( 2.76 ) ; | ||
| expect( tree.getClosestNode( 2.765 , true , 1 ).key ).to.be( 2.77 ) ; | ||
| expect( tree.getClosestNode( 2.78 , true , -1 ).key ).to.be( 2.77 ) ; | ||
| expect( tree.getClosestNode( 2.78 , true , 1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.84 , true , -1 ).key ).to.be( 2.8 ) ; | ||
| expect( tree.getClosestNode( 2.84 , true , 1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 2.9 , true , -1 ).key ).to.be( 2.85 ) ; | ||
| expect( tree.getClosestNode( 2.9 , true , 1 ).key ).to.be( 3 ) ; | ||
| expect( tree.getClosestNode( 4 , true , -1 ).key ).to.be( 3 ) ; | ||
| expect( tree.getClosestNode( 4 , true , 1 ).key ).to.be( 5 ) ; | ||
| expect( tree.getClosestNode( 7 , true , -1 ).key ).to.be( 6 ) ; | ||
| expect( tree.getClosestNode( 7 , true , 1 ) ).to.be( null ) ; | ||
| } ) ; | ||
| } ) ; | ||
| } ) ; | ||
| /* | ||
| Chain Lightning | ||
| Copyright (c) 2018 Cédric Ronvel | ||
| The MIT License (MIT) | ||
| 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. | ||
| */ | ||
| "use strict" ; | ||
| /* global describe, it, before, after */ | ||
| const lib = require( '..' ) ; | ||
| const LinkedList = lib.LinkedList ; | ||
| /* Tests */ | ||
| describe( "Linked List" , () => { | ||
| describe( "Basic features" , () => { | ||
| it( "constructor arguments should be added as elements" , () => { | ||
| var list ; | ||
| list = new LinkedList() ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( 'jack' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( "LinkedList.from() should create a list from any iterable" , () => { | ||
| var list ; | ||
| list = LinkedList.from( new Set() ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = LinkedList.from( new Set( [ 'jack' ] ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' ] ) ; | ||
| list.sanityCheck() ; | ||
| list = LinkedList.from( new Set( [ 'jack' , 'jean' , 'steve' ] ) ) ; | ||
| list.sanityCheck() ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| } ) ; | ||
| it( ".push()/.append()" , () => { | ||
| var list ; | ||
| list = new LinkedList() ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| list.push( 'bob' ) ; | ||
| list.append( 'bill' ) ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'bob' , 'bill' , 'jack' , 'jean' , 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList() ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".unshift()/.prepend()" , () => { | ||
| var list ; | ||
| list = new LinkedList() ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| list.unshift( 'bob' ) ; | ||
| list.prepend( 'bill' ) ; | ||
| list.unshift( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bill' , 'bob' ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList() ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".pop()" , () => { | ||
| var list ; | ||
| list = new LinkedList() ; | ||
| expect( list.pop() ).to.be( undefined ) ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| list.sanityCheck() ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.pop() ).to.be( 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' ] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.pop() ).to.be( 'jean' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' ] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.pop() ).to.be( 'jack' ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.pop() ).to.be( undefined ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".shift()" , () => { | ||
| var list ; | ||
| list = new LinkedList() ; | ||
| expect( list.shift() ).to.be( undefined ) ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| list.sanityCheck() ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.shift() ).to.be( 'jack' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jean' , 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.shift() ).to.be( 'jean' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'steve' ] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.shift() ).to.be( 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| expect( list.shift() ).to.be( undefined ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| } ) ; | ||
| describe( "Advanced Array-like features" , () => { | ||
| it( ".nodes()" , () => { | ||
| var nodes ; | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| nodes = list.nodes() ; | ||
| expect( nodes ).to.be.an( Array ) ; | ||
| expect( nodes ).to.have.length( 3 ) ; | ||
| expect( nodes.map( e => e.element ) ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list = new LinkedList() ; | ||
| nodes = list.nodes() ; | ||
| expect( nodes ).to.be.an( Array ) ; | ||
| expect( nodes ).to.have.length( 0 ) ; | ||
| expect( nodes.map( e => e.element ) ).to.equal( [] ) ; | ||
| list = new LinkedList( e1 , e2 , e2 , e2 , e3 ) ; | ||
| nodes = list.nodes() ; | ||
| expect( nodes ).to.be.an( Array ) ; | ||
| expect( nodes ).to.have.length( 5 ) ; | ||
| expect( nodes.map( e => e.element ) ).to.equal( [ e1 , e2 , e2 , e2 , e3 ] ) ; | ||
| } ) ; | ||
| it( ".nodeOf()/.lastNodeOf()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.nodeOf( e2 ).element ).to.be( e2 ) ; | ||
| expect( list.nodeOf( e4 ) ).to.be( null ) ; | ||
| list.push( e2 , e2 , e2 ) ; | ||
| list.set( list.nodeOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bob' } ] ) ; | ||
| list.set( list.lastNodeOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bobby' } ] ) ; | ||
| } ) ; | ||
| it( ".includes()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList() ; | ||
| expect( list.includes( e2 ) ).to.be.false() ; | ||
| list = new LinkedList( e1 ) ; | ||
| expect( list.includes( e2 ) ).to.be.false() ; | ||
| list = new LinkedList( e1 , e3 ) ; | ||
| expect( list.includes( e2 ) ).to.be.false() ; | ||
| list = new LinkedList( e2 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new LinkedList( e2 , e2 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new LinkedList( e1 , e3 , e2 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new LinkedList( e2 , e1 , e3 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| } ) ; | ||
| it( ".forEach()" , () => { | ||
| var list , | ||
| accumulator = [] , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList() ; | ||
| list.forEach( element => accumulator.push( element.v ) ) ; | ||
| expect( accumulator ).to.equal( [] ) ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| list.forEach( element => accumulator.push( element.v ) ) ; | ||
| expect( accumulator ).to.equal( [ 'jack' , 'bob' , 'steve' ] ) ; | ||
| } ) ; | ||
| it( ".some()/.every()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList() ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.false() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| list = new LinkedList( e1 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.false() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new LinkedList( e2 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| list = new LinkedList( e1 , e2 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new LinkedList( e2 , e1 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new LinkedList( e1 , e2 , e2 , e3 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new LinkedList( e2 , e2 , e2 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| } ) ; | ||
| it( ".find()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bob' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ; | ||
| expect( list.find( element => element.v === 'bobby' ) ).to.be( undefined ) ; | ||
| list.push( e4 ) ; | ||
| expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ; | ||
| list.unshift( e4 ) ; | ||
| expect( list.find( element => element.v === 'bob' ) ).to.be( e4 ) ; | ||
| } ) ; | ||
| it( ".findNode()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bob' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.get( list.findNode( element => element.v === 'bob' ) ) ).to.be( e2 ) ; | ||
| expect( list.findNode( element => element.v === 'bobby' ) ).to.be( null ) ; | ||
| expect( list.get( list.findNode( element => element.v === 'bobby' ) ) ).to.be( undefined ) ; | ||
| list.push( e4 ) ; | ||
| expect( list.get( list.findNode( element => element.v === 'bob' ) ) ).to.be( e2 ) ; | ||
| list.unshift( e4 ) ; | ||
| expect( list.get( list.findNode( element => element.v === 'bob' ) ) ).to.be( e4 ) ; | ||
| } ) ; | ||
| it( ".map()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList().map( element => element.v + element.v ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 ).map( element => element.v + element.v ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jackjack' ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ).map( element => element.v + element.v ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jackjack' , 'bobbob' , 'stevesteve' ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".reduce()/.reduceRight" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList() ; | ||
| expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ; | ||
| expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ; | ||
| list = new LinkedList( e1 ) ; | ||
| expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ; | ||
| expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jackbobsteve' ) ; | ||
| expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'stevebobjack' ) ; | ||
| } ) ; | ||
| it( ".filter()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList( e2 , e2 , e2 ) ; | ||
| expect( list.filter( () => true ) ).not.to.be( list ) ; | ||
| list = new LinkedList().filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 ).filter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e3 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e2 , e2 , e2 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ).filter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".reverse()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList().reverse() ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 ).reverse() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ).reverse() ; | ||
| expect( [ ... list ] ).to.equal( [ e3 , e2 , e1 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( "missing .concat()" ) ; | ||
| it( "missing .copyWithin()" ) ; | ||
| it( "missing .slice()" ) ; | ||
| it( "missing .splice()" ) ; | ||
| it( "missing .sort()" ) ; | ||
| } ) ; | ||
| describe( "Advanced custom features" , () => { | ||
| it( ".removeNode()/.deleteNode()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| list.removeNode( list.nodeOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList() ; | ||
| list.removeNode( list.nodeOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e2 ) ; | ||
| list.removeNode( list.nodeOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e2 , e1 , e3 ) ; | ||
| list.removeNode( list.nodeOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e3 , e2 ) ; | ||
| list.removeNode( list.nodeOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".remove()/.delete()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList() ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e2 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e2 , e1 , e3 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e3 , e2 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| // Remove all occurences | ||
| list = new LinkedList( e2 , e2 , e2 , e1 , e2 , e3 , e2 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| // NaN test | ||
| list = new LinkedList( NaN , NaN , NaN , e1 , NaN , e3 , NaN ) ; | ||
| list.remove( NaN ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".moveAfter()/.moveToTail()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e1 ) , list.nodeOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e1 ) , list.nodeOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e1 ) , list.nodeOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveToTail( list.nodeOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e2 ) , list.nodeOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e2 ) , list.nodeOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e2 ) , list.nodeOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveToTail( list.nodeOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e3 ) , list.nodeOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e3 ) , list.nodeOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.nodeOf( e3 ) , list.nodeOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveToTail( list.nodeOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".moveBefore()/.moveToHead()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e1 ) , list.nodeOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveToHead( list.nodeOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e1 ) , list.nodeOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e1 ) , list.nodeOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e2 ) , list.nodeOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveToHead( list.nodeOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e2 ) , list.nodeOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e2 ) , list.nodeOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e3 ) , list.nodeOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveToHead( list.nodeOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e3 ) , list.nodeOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.nodeOf( e3 ) , list.nodeOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".insertAfter()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| list.insertAfter( list.nodeOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list.insertAfter( list.nodeOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e4 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list.insertAfter( list.nodeOf( e1 ) , e4 , e4 , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list.insertAfter( list.nodeOf( e3 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 , e4 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".insertBefore()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new LinkedList( e1 , e2 , e3 ) ; | ||
| list.insertBefore( list.nodeOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list.insertBefore( list.nodeOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e4 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list.insertBefore( list.nodeOf( e1 ) , e4 , e4 , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list.insertBefore( list.nodeOf( e3 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e4 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| it( ".inPlaceFilter()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new LinkedList( e2 , e2 , e2 ) ; | ||
| expect( list.inPlaceFilter( () => true ) ).to.be( list ) ; | ||
| list = new LinkedList().inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 ).inPlaceFilter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e2 , e2 , e2 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| list.sanityCheck() ; | ||
| list = new LinkedList( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e2 ] ) ; | ||
| list.sanityCheck() ; | ||
| } ) ; | ||
| } ) ; | ||
| } ) ; |
+2
-617
@@ -29,619 +29,4 @@ /* | ||
| exports.LinkedList = require( './LinkedList.js' ) ; | ||
| exports.BinaryTree = require( './BinaryTree.js' ) ; | ||
| function List( ... elements ) { | ||
| this.head = null ; | ||
| this.tail = null ; | ||
| this.length = 0 ; | ||
| if ( elements.length ) { | ||
| this.push( ... elements ) ; | ||
| } | ||
| } | ||
| module.exports = List ; | ||
| function Slot( list , previous , next , element ) { | ||
| this.list = list ; | ||
| this.previous = previous ; | ||
| this.next = next ; | ||
| this.element = element ; | ||
| } | ||
| List.Slot = Slot ; | ||
| // Array.prototype.includes() uses this for value equality | ||
| // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes | ||
| function isIdenticalTo( x , y ) { | ||
| return x === y || ( Number.isNaN( x ) && Number.isNaN( y ) ) ; | ||
| } | ||
| List.prototype.values = function *() { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| yield current.element ; | ||
| current = current.next ; | ||
| } | ||
| } ; | ||
| List.prototype[Symbol.iterator] = List.prototype.values ; | ||
| List.from = function from( iterable ) { | ||
| var index , slot , element , | ||
| list = new List() ; | ||
| for ( element of iterable ) { | ||
| slot = new Slot( list , list.tail , null , element ) ; | ||
| if ( ! list.head ) { | ||
| // This is the first element | ||
| list.head = list.tail = slot ; | ||
| } | ||
| else { | ||
| list.tail.next = slot ; | ||
| list.tail = slot ; | ||
| } | ||
| list.length ++ ; | ||
| } | ||
| return list ; | ||
| } ; | ||
| // Similar to .keys() | ||
| List.prototype.slots = function slots() { | ||
| var slots = new Array( this.length ) , | ||
| index = 0 , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| slots[ index ++ ] = current ; | ||
| current = current.next ; | ||
| } | ||
| return slots ; | ||
| } ; | ||
| List.prototype.push = | ||
| List.prototype.append = function append( ... elements ) { | ||
| var index , slot ; | ||
| if ( ! elements.length ) { return ; } | ||
| this.length += elements.length ; | ||
| slot = new Slot( this , this.tail , null , elements[ 0 ] ) ; | ||
| if ( this.tail ) { | ||
| this.tail.next = slot ; | ||
| this.tail = slot ; | ||
| } | ||
| else { | ||
| this.head = this.tail = slot ; | ||
| } | ||
| for ( index = 1 ; index < elements.length ; index ++ ) { | ||
| slot = new Slot( this , this.tail , null , elements[ index ] ) ; | ||
| this.tail.next = slot ; | ||
| this.tail = slot ; | ||
| } | ||
| } ; | ||
| List.prototype.unshift = | ||
| List.prototype.prepend = function prepend( ... elements ) { | ||
| var index , slot ; | ||
| if ( ! elements.length ) { return ; } | ||
| this.length += elements.length ; | ||
| index = elements.length - 1 ; | ||
| slot = new Slot( this , null , this.head , elements[ index ] ) ; | ||
| if ( this.head ) { | ||
| this.head.previous = slot ; | ||
| this.head = slot ; | ||
| } | ||
| else { | ||
| this.head = this.tail = slot ; | ||
| } | ||
| while ( index -- ) { | ||
| slot = new Slot( this , null , this.head , elements[ index ] ) ; | ||
| this.head.previous = slot ; | ||
| this.head = slot ; | ||
| } | ||
| } ; | ||
| List.prototype.pop = function pop() { | ||
| if ( ! this.tail ) { return ; } | ||
| var slot = this.tail ; | ||
| this.tail = slot.previous ; | ||
| if ( this.tail ) { | ||
| this.tail.next = null ; | ||
| } | ||
| else { | ||
| // That was the last element | ||
| this.head = null ; | ||
| } | ||
| this.length -- ; | ||
| return slot.element ; | ||
| } ; | ||
| List.prototype.shift = function shift() { | ||
| if ( ! this.head ) { return ; } | ||
| var slot = this.head ; | ||
| this.head = slot.next ; | ||
| if ( this.head ) { | ||
| this.head.previous = null ; | ||
| } | ||
| else { | ||
| // That was the last element | ||
| this.tail = null ; | ||
| } | ||
| this.length -- ; | ||
| return slot.element ; | ||
| } ; | ||
| /* | ||
| Advanced Array-like features | ||
| */ | ||
| List.prototype.slotOf = function slotOf( element , fromSlot ) { | ||
| var current ; | ||
| if ( fromSlot ) { | ||
| if ( fromSlot.list !== this ) { return ; } | ||
| current = fromSlot ; | ||
| } | ||
| else { | ||
| current = this.head ; | ||
| } | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return current ; } | ||
| current = current.next ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| List.prototype.lastSlotOf = function lastSlotOf( element , fromSlot ) { | ||
| var current ; | ||
| if ( fromSlot ) { | ||
| if ( fromSlot.list !== this ) { return ; } | ||
| current = fromSlot ; | ||
| } | ||
| else { | ||
| current = this.tail ; | ||
| } | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return current ; } | ||
| current = current.previous ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| // Almost like .indexOf() | ||
| List.prototype.includes = function includes( element , fromSlot ) { | ||
| var current ; | ||
| if ( fromSlot ) { | ||
| if ( fromSlot.list !== this ) { return false ; } | ||
| current = fromSlot ; | ||
| } | ||
| else { | ||
| current = this.head ; | ||
| } | ||
| while ( current ) { | ||
| if ( isIdenticalTo( current.element , element ) ) { return true ; } | ||
| current = current.next ; | ||
| } | ||
| return false ; | ||
| } ; | ||
| List.prototype.forEach = function forEach( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| fn.call( thisArg , current.element , current , this ) ; | ||
| current = current.next ; | ||
| } | ||
| } ; | ||
| List.prototype.some = function some( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { return true ; } | ||
| current = current.next ; | ||
| } | ||
| return false ; | ||
| } ; | ||
| List.prototype.every = function every( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( ! fn.call( thisArg , current.element , current , this ) ) { return false ; } | ||
| current = current.next ; | ||
| } | ||
| return true ; | ||
| } ; | ||
| List.prototype.find = function find( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { | ||
| return current.element ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| return ; | ||
| } ; | ||
| List.prototype.findSlot = function findSlot( fn , thisArg ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { | ||
| return current ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| return null ; | ||
| } ; | ||
| List.prototype.map = function map( fn , thisArg ) { | ||
| var newList = new List() , | ||
| newElement , | ||
| slot , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| newElement = fn.call( thisArg , current.element , current , this ) ; | ||
| slot = new Slot( newList , newList.tail , null , newElement ) ; | ||
| if ( newList.tail ) { | ||
| newList.tail.next = slot ; | ||
| newList.tail = slot ; | ||
| } | ||
| else { | ||
| newList.head = newList.tail = slot ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| newList.length = this.length ; | ||
| return newList ; | ||
| } ; | ||
| List.prototype.reduce = function reduce( fn , accumulator ) { | ||
| var current = this.head ; | ||
| while ( current ) { | ||
| accumulator = fn( accumulator , current.element , current , this ) ; | ||
| current = current.next ; | ||
| } | ||
| return accumulator ; | ||
| } ; | ||
| List.prototype.reduceRight = function reduceRight( fn , accumulator ) { | ||
| var current = this.tail ; | ||
| while ( current ) { | ||
| accumulator = fn( accumulator , current.element , current , this ) ; | ||
| current = current.previous ; | ||
| } | ||
| return accumulator ; | ||
| } ; | ||
| List.prototype.filter = function filter( fn , thisArg ) { | ||
| var newList = new List() , | ||
| slot , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| if ( fn.call( thisArg , current.element , current , this ) ) { | ||
| slot = new Slot( newList , newList.tail , null , current.element ) ; | ||
| if ( newList.tail ) { | ||
| newList.tail.next = slot ; | ||
| newList.tail = slot ; | ||
| } | ||
| else { | ||
| newList.head = newList.tail = slot ; | ||
| } | ||
| newList.length ++ ; | ||
| } | ||
| current = current.next ; | ||
| } | ||
| return newList ; | ||
| } ; | ||
| List.prototype.reverse = function reverse() { | ||
| var tmp , | ||
| current = this.head ; | ||
| while ( current ) { | ||
| tmp = current.next ; | ||
| current.next = current.previous ; | ||
| current.previous = tmp ; | ||
| // Note: it's already inverted, so use previous instead of next | ||
| current = current.previous ; | ||
| } | ||
| tmp = this.head ; | ||
| this.head = this.tail ; | ||
| this.tail = tmp ; | ||
| return this ; | ||
| } ; | ||
| /* | ||
| Advanced custom features | ||
| */ | ||
| List.prototype.get = function get( slot ) { | ||
| if ( ! slot || slot.list !== this ) { return ; } | ||
| return slot.element ; | ||
| } ; | ||
| List.prototype.set = function set( slot , element ) { | ||
| if ( ! slot || slot.list !== this ) { return ; } | ||
| slot.element = element ; | ||
| } ; | ||
| List.prototype.deleteSlot = | ||
| List.prototype.removeSlot = function removeSlot( slot ) { | ||
| if ( ! slot || slot.list !== this ) { return false ; } | ||
| if ( slot.previous ) { slot.previous.next = slot.next ; } | ||
| if ( slot.next ) { slot.next.previous = slot.previous ; } | ||
| if ( this.head === slot ) { this.head = slot.next ; } | ||
| if ( this.tail === slot ) { this.tail = slot.previous ; } | ||
| slot.list = slot.previous = slot.next = null ; | ||
| this.length -- ; | ||
| return true ; | ||
| } ; | ||
| // Delete all occurences of a value | ||
| List.prototype.delete = | ||
| List.prototype.remove = function remove( value ) { | ||
| this.inPlaceFilter( e => ! isIdenticalTo( e , value ) ) ; | ||
| } ; | ||
| List.prototype.moveAfter = function moveAfter( slot , afterSlot ) { | ||
| if ( | ||
| ! slot || slot.list !== this || ! afterSlot || afterSlot.list !== this || | ||
| slot === afterSlot || afterSlot === slot.previous | ||
| ) { | ||
| return false ; | ||
| } | ||
| var beforeSlot = afterSlot.next ; | ||
| if ( this.head === slot ) { this.head = slot.next ; } | ||
| if ( this.tail === slot ) { this.tail = slot.previous ; } | ||
| else if ( this.tail === afterSlot ) { this.tail = slot ; } | ||
| if ( slot.previous ) { slot.previous.next = slot.next ; } | ||
| if ( slot.next ) { slot.next.previous = slot.previous ; } | ||
| slot.previous = afterSlot ; | ||
| afterSlot.next = slot ; | ||
| slot.next = beforeSlot ; | ||
| if ( beforeSlot ) { beforeSlot.previous = slot ; } | ||
| return true ; | ||
| } ; | ||
| List.prototype.moveBefore = function moveBefore( slot , beforeSlot ) { | ||
| if ( | ||
| ! slot || slot.list !== this || ! beforeSlot || beforeSlot.list !== this || | ||
| slot === beforeSlot || beforeSlot === slot.next | ||
| ) { | ||
| return false ; | ||
| } | ||
| var afterSlot = beforeSlot.previous ; | ||
| if ( this.head === slot ) { this.head = slot.next ; } | ||
| else if ( this.head === beforeSlot ) { this.head = slot ; } | ||
| if ( this.tail === slot ) { this.tail = slot.previous ; } | ||
| if ( slot.previous ) { slot.previous.next = slot.next ; } | ||
| if ( slot.next ) { slot.next.previous = slot.previous ; } | ||
| slot.next = beforeSlot ; | ||
| beforeSlot.previous = slot ; | ||
| slot.previous = afterSlot ; | ||
| if ( afterSlot ) { afterSlot.next = slot ; } | ||
| return true ; | ||
| } ; | ||
| List.prototype.moveToTail = function moveToTail( slot ) { return this.moveAfter( slot , this.tail ) ; } ; | ||
| List.prototype.moveToHead = function moveToHead( slot ) { return this.moveBefore( slot , this.head ) ; } ; | ||
| List.prototype.insertAfter = function insertAfter( afterSlot , ... elements ) { | ||
| if ( afterSlot.list !== this || ! elements.length ) { return ; } | ||
| var slot , index , | ||
| lastSlot = afterSlot , | ||
| beforeSlot = afterSlot.next ; | ||
| for ( index = 0 ; index < elements.length ; index ++ ) { | ||
| slot = new Slot( this , lastSlot , null , elements[ index ] ) ; | ||
| lastSlot.next = slot ; | ||
| lastSlot = slot ; | ||
| } | ||
| this.length += elements.length ; | ||
| if ( beforeSlot ) { | ||
| lastSlot.next = beforeSlot ; | ||
| beforeSlot.previous = lastSlot ; | ||
| } | ||
| else { | ||
| this.tail = lastSlot ; | ||
| } | ||
| } ; | ||
| List.prototype.insertBefore = function insertBefore( beforeSlot , ... elements ) { | ||
| if ( beforeSlot.list !== this || ! elements.length ) { return ; } | ||
| var slot , | ||
| index = elements.length , | ||
| lastSlot = beforeSlot , | ||
| afterSlot = beforeSlot.previous ; | ||
| while ( index -- ) { | ||
| slot = new Slot( this , null , lastSlot , elements[ index ] ) ; | ||
| lastSlot.previous = slot ; | ||
| lastSlot = slot ; | ||
| } | ||
| this.length += elements.length ; | ||
| if ( afterSlot ) { | ||
| lastSlot.previous = afterSlot ; | ||
| afterSlot.next = lastSlot ; | ||
| } | ||
| else { | ||
| this.head = lastSlot ; | ||
| } | ||
| } ; | ||
| List.prototype.inPlaceFilter = function inPlaceFilter( fn , thisArg ) { | ||
| var lastSlot = null , | ||
| nextSlot , | ||
| slot = this.head ; | ||
| this.head = null ; | ||
| while ( slot ) { | ||
| slot.previous = lastSlot ; | ||
| nextSlot = slot.next ; // backup that | ||
| if ( fn.call( thisArg , slot.element , slot , this ) ) { | ||
| if ( ! this.head ) { this.head = slot ; } | ||
| lastSlot = slot ; | ||
| } | ||
| else { | ||
| if ( lastSlot ) { lastSlot.next = slot.next ; } | ||
| this.length -- ; | ||
| slot.list = slot.previous = slot.next = null ; | ||
| } | ||
| slot = nextSlot ; | ||
| } | ||
| this.tail = lastSlot ; | ||
| return this ; | ||
| } ; | ||
+1
-1
| { | ||
| "name": "chain-lightning", | ||
| "version": "0.2.1", | ||
| "version": "0.3.0", | ||
| "description": "Linked list", | ||
@@ -5,0 +5,0 @@ "main": "lib/chainlightning.js", |
| /* | ||
| Chain Lightning | ||
| Copyright (c) 2018 Cédric Ronvel | ||
| The MIT License (MIT) | ||
| 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. | ||
| */ | ||
| "use strict" ; | ||
| /* global describe, it, before, after */ | ||
| var List = require( '..' ) ; | ||
| function sanityCheck( list ) { | ||
| var length = 0 , | ||
| lastSlot = null , | ||
| slot = list.head ; | ||
| while ( slot ) { | ||
| expect( slot.list ).to.be( list ) ; | ||
| expect( slot.previous ).to.be( lastSlot ) ; | ||
| // Useless because we precisely come from that lastSlot | ||
| //if ( lastSlot ) { expect( lastSlot.next ).to.be( slot ) ; } | ||
| length ++ ; | ||
| lastSlot = slot ; | ||
| slot = slot.next ; | ||
| } | ||
| expect( list.tail ).to.be( lastSlot ) ; | ||
| expect( list.length ).to.be( length ) ; | ||
| } | ||
| /* Tests */ | ||
| describe( "Basic features" , () => { | ||
| it( "constructor arguments should be added as elements" , () => { | ||
| var list ; | ||
| list = new List() ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( 'jack' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( "List.from() should create a list from any iterable" , () => { | ||
| var list ; | ||
| list = List.from( new Set() ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = List.from( new Set( [ 'jack' ] ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = List.from( new Set( [ 'jack' , 'jean' , 'steve' ] ) ) ; | ||
| sanityCheck( list ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| } ) ; | ||
| it( ".push()/.append()" , () => { | ||
| var list ; | ||
| list = new List() ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| list.push( 'bob' ) ; | ||
| list.append( 'bill' ) ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'bob' , 'bill' , 'jack' , 'jean' , 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List() ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".unshift()/.prepend()" , () => { | ||
| var list ; | ||
| list = new List() ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| list.unshift( 'bob' ) ; | ||
| list.prepend( 'bill' ) ; | ||
| list.unshift( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bill' , 'bob' ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List() ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".pop()" , () => { | ||
| var list ; | ||
| list = new List() ; | ||
| expect( list.pop() ).to.be( undefined ) ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| sanityCheck( list ) ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.pop() ).to.be( 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' ] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.pop() ).to.be( 'jean' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' ] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.pop() ).to.be( 'jack' ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.pop() ).to.be( undefined ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".shift()" , () => { | ||
| var list ; | ||
| list = new List() ; | ||
| expect( list.shift() ).to.be( undefined ) ; | ||
| expect( list ).to.have.length( 0 ) ; | ||
| sanityCheck( list ) ; | ||
| list.push( 'jack' , 'jean' , 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.shift() ).to.be( 'jack' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jean' , 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.shift() ).to.be( 'jean' ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'steve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.shift() ).to.be( 'steve' ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| expect( list.shift() ).to.be( undefined ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| } ) ; | ||
| describe( "Advanced Array-like features" , () => { | ||
| it( ".slots()" , () => { | ||
| var slots ; | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| slots = list.slots() ; | ||
| expect( slots ).to.be.an( Array ) ; | ||
| expect( slots ).to.have.length( 3 ) ; | ||
| expect( slots.map( e => e.element ) ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| list = new List() ; | ||
| slots = list.slots() ; | ||
| expect( slots ).to.be.an( Array ) ; | ||
| expect( slots ).to.have.length( 0 ) ; | ||
| expect( slots.map( e => e.element ) ).to.equal( [] ) ; | ||
| list = new List( e1 , e2 , e2 , e2 , e3 ) ; | ||
| slots = list.slots() ; | ||
| expect( slots ).to.be.an( Array ) ; | ||
| expect( slots ).to.have.length( 5 ) ; | ||
| expect( slots.map( e => e.element ) ).to.equal( [ e1 , e2 , e2 , e2 , e3 ] ) ; | ||
| } ) ; | ||
| it( ".slotOf()/.lastSlotOf()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.slotOf( e2 ).element ).to.be( e2 ) ; | ||
| expect( list.slotOf( e4 ) ).to.be( null ) ; | ||
| list.push( e2 , e2 , e2 ) ; | ||
| list.set( list.slotOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bob' } ] ) ; | ||
| list.set( list.lastSlotOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bobby' } ] ) ; | ||
| } ) ; | ||
| it( ".includes()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List() ; | ||
| expect( list.includes( e2 ) ).to.be.false() ; | ||
| list = new List( e1 ) ; | ||
| expect( list.includes( e2 ) ).to.be.false() ; | ||
| list = new List( e1 , e3 ) ; | ||
| expect( list.includes( e2 ) ).to.be.false() ; | ||
| list = new List( e2 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new List( e2 , e2 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new List( e1 , e3 , e2 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| list = new List( e2 , e1 , e3 ) ; | ||
| expect( list.includes( e2 ) ).to.be.true() ; | ||
| } ) ; | ||
| it( ".forEach()" , () => { | ||
| var list , | ||
| accumulator = [] , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List() ; | ||
| list.forEach( element => accumulator.push( element.v ) ) ; | ||
| expect( accumulator ).to.equal( [] ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| list.forEach( element => accumulator.push( element.v ) ) ; | ||
| expect( accumulator ).to.equal( [ 'jack' , 'bob' , 'steve' ] ) ; | ||
| } ) ; | ||
| it( ".some()/.every()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List() ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.false() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| list = new List( e1 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.false() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new List( e2 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| list = new List( e1 , e2 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new List( e2 , e1 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new List( e1 , e2 , e2 , e3 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.false() ; | ||
| list = new List( e2 , e2 , e2 ) ; | ||
| expect( list.some( element => element.v === 'bob' ) ).to.be.true() ; | ||
| expect( list.every( element => element.v === 'bob' ) ).to.be.true() ; | ||
| } ) ; | ||
| it( ".find()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bob' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ; | ||
| expect( list.find( element => element.v === 'bobby' ) ).to.be( undefined ) ; | ||
| list.push( e4 ) ; | ||
| expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ; | ||
| list.unshift( e4 ) ; | ||
| expect( list.find( element => element.v === 'bob' ) ).to.be( e4 ) ; | ||
| } ) ; | ||
| it( ".findSlot()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bob' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.get( list.findSlot( element => element.v === 'bob' ) ) ).to.be( e2 ) ; | ||
| expect( list.findSlot( element => element.v === 'bobby' ) ).to.be( null ) ; | ||
| expect( list.get( list.findSlot( element => element.v === 'bobby' ) ) ).to.be( undefined ) ; | ||
| list.push( e4 ) ; | ||
| expect( list.get( list.findSlot( element => element.v === 'bob' ) ) ).to.be( e2 ) ; | ||
| list.unshift( e4 ) ; | ||
| expect( list.get( list.findSlot( element => element.v === 'bob' ) ) ).to.be( e4 ) ; | ||
| } ) ; | ||
| it( ".map()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List().map( element => element.v + element.v ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 ).map( element => element.v + element.v ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jackjack' ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ).map( element => element.v + element.v ) ; | ||
| expect( [ ... list ] ).to.equal( [ 'jackjack' , 'bobbob' , 'stevesteve' ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".reduce()/.reduceRight" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List() ; | ||
| expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ; | ||
| expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ; | ||
| list = new List( e1 ) ; | ||
| expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ; | ||
| expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jackbobsteve' ) ; | ||
| expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'stevebobjack' ) ; | ||
| } ) ; | ||
| it( ".filter()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List( e2 , e2 , e2 ) ; | ||
| expect( list.filter( () => true ) ).not.to.be( list ) ; | ||
| list = new List().filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 ).filter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e3 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e2 , e2 , e2 ).filter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ).filter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".reverse()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List().reverse() ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 ).reverse() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ).reverse() ; | ||
| expect( [ ... list ] ).to.equal( [ e3 , e2 , e1 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( "missing .concat()" ) ; | ||
| it( "missing .copyWithin()" ) ; | ||
| it( "missing .slice()" ) ; | ||
| it( "missing .splice()" ) ; | ||
| it( "missing .sort()" ) ; | ||
| } ) ; | ||
| describe( "Advanced custom features" , () => { | ||
| it( ".removeSlot()/.deleteSlot()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| list.removeSlot( list.slotOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List() ; | ||
| list.removeSlot( list.slotOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e2 ) ; | ||
| list.removeSlot( list.slotOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e2 , e1 , e3 ) ; | ||
| list.removeSlot( list.slotOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e3 , e2 ) ; | ||
| list.removeSlot( list.slotOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".remove()/.delete()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List() ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e2 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e2 , e1 , e3 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e3 , e2 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| // Remove all occurences | ||
| list = new List( e2 , e2 , e2 , e1 , e2 , e3 , e2 ) ; | ||
| list.remove( e2 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| // NaN test | ||
| list = new List( NaN , NaN , NaN , e1 , NaN , e3 , NaN ) ; | ||
| list.remove( NaN ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".moveAfter()/.moveToTail()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e1 ) , list.slotOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e1 ) , list.slotOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e1 ) , list.slotOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveToTail( list.slotOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e2 ) , list.slotOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e2 ) , list.slotOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e2 ) , list.slotOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveToTail( list.slotOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e3 ) , list.slotOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e3 ) , list.slotOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveAfter( list.slotOf( e3 ) , list.slotOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveToTail( list.slotOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".moveBefore()/.moveToHead()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e1 ) , list.slotOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveToHead( list.slotOf( e1 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e1 ) , list.slotOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e1 ) , list.slotOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e2 ) , list.slotOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveToHead( list.slotOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e2 ) , list.slotOf( e2 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e2 ) , list.slotOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e3 ) , list.slotOf( e1 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveToHead( list.slotOf( e3 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e3 ) , list.slotOf( e2 ) ) ).to.be.true() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| expect( list.moveBefore( list.slotOf( e3 ) , list.slotOf( e3 ) ) ).to.be.false() ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".insertAfter()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| list.insertAfter( list.slotOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list.insertAfter( list.slotOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e4 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list.insertAfter( list.slotOf( e1 ) , e4 , e4 , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list.insertAfter( list.slotOf( e3 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 , e4 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".insertBefore()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } , | ||
| e4 = { v: 'bobby' } ; | ||
| list = new List( e1 , e2 , e3 ) ; | ||
| list.insertBefore( list.slotOf( e2 ) ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list.insertBefore( list.slotOf( e2 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e4 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list.insertBefore( list.slotOf( e1 ) , e4 , e4 , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list.insertBefore( list.slotOf( e3 ) , e4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e4 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| it( ".inPlaceFilter()" , () => { | ||
| var list , | ||
| e1 = { v: 'jack' } , | ||
| e2 = { v: 'bob' } , | ||
| e3 = { v: 'steve' } ; | ||
| list = new List( e2 , e2 , e2 ) ; | ||
| expect( list.inPlaceFilter( () => true ) ).to.be( list ) ; | ||
| list = new List().inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 ).inPlaceFilter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e2 , e2 , e2 ).inPlaceFilter( element => element.v.length >= 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [] ) ; | ||
| sanityCheck( list ) ; | ||
| list = new List( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length < 4 ) ; | ||
| expect( [ ... list ] ).to.equal( [ e2 ] ) ; | ||
| sanityCheck( list ) ; | ||
| } ) ; | ||
| } ) ; | ||
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
117283
155.93%11
37.5%2968
155.86%2
Infinity%1
Infinity%