time-to-botec

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

main.js (28230B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2021 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 /* eslint-disable no-restricted-syntax, no-invalid-this */
     20 
     21 'use strict';
     22 
     23 // MODULES //
     24 
     25 var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive;
     26 var isArrayLikeObject = require( '@stdlib/assert/is-array-like-object' );
     27 var isCollection = require( '@stdlib/assert/is-collection' );
     28 var isFunction = require( '@stdlib/assert/is-function' );
     29 var isObject = require( '@stdlib/assert/is-object' );
     30 var hasIteratorSymbolSupport = require( '@stdlib/assert/has-iterator-symbol-support' );
     31 var ITERATOR_SYMBOL = require( '@stdlib/symbol/iterator' );
     32 var setReadOnly = require( './../../define-nonenumerable-read-only-property' );
     33 var setReadOnlyAccessor = require( './../../define-nonenumerable-read-only-accessor' );
     34 var Int32Array = require( '@stdlib/array/int32' );
     35 var Int8Array = require( '@stdlib/array/int8' );
     36 var ceil = require( '@stdlib/math/base/special/ceil' );
     37 var floor = require( '@stdlib/math/base/special/floor' );
     38 var grev = require( '@stdlib/blas/ext/base/grev' );
     39 var fromIteratorAdjList = require( './from_adjacency_list_iterator.js' );
     40 var fromIteratorAdjListMap = require( './from_adjacency_list_iterator_map.js' );
     41 var fromIteratorEdges = require( './from_edges_iterator.js' );
     42 var fromIteratorEdgesMap = require( './from_edges_iterator_map.js' );
     43 var setBit = require( './set_bit.js' );
     44 var clearBit = require( './clear_bit.js' );
     45 var isSet = require( './is_set.js' );
     46 var bitValue = require( './bit_value.js' );
     47 
     48 
     49 // VARIABLES //
     50 
     51 var HAS_ITERATOR_SYMBOL = hasIteratorSymbolSupport();
     52 var NBITS = Int32Array.BYTES_PER_ELEMENT * 8; // 8 bits per byte
     53 
     54 
     55 // MAIN //
     56 
     57 /**
     58 * Compact adjacency matrix constructor.
     59 *
     60 * @constructor
     61 * @param {NonNegativeInteger} N - number of vertices
     62 * @throws {TypeError} must provide a nonnegative integer
     63 * @returns {CompactAdjacencyMatrix} adjacency matrix instance
     64 *
     65 * @example
     66 * var adj = new CompactAdjacencyMatrix( 4 );
     67 * // returns <CompactAdjacencyMatrix>
     68 *
     69 * adj.addEdge( 0, 1 );
     70 * adj.addEdge( 0, 2 );
     71 * adj.addEdge( 1, 2 );
     72 * adj.addEdge( 2, 3 );
     73 */
     74 function CompactAdjacencyMatrix( N ) {
     75 	if ( !( this instanceof CompactAdjacencyMatrix ) ) {
     76 		return new CompactAdjacencyMatrix( N );
     77 	}
     78 	if ( !isNonNegativeInteger( N ) ) {
     79 		throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + N + '`.' );
     80 	}
     81 	this._N = N; // number of vertices
     82 	this._M = 0; // number of edges
     83 	this._buffer = new Int32Array( ceil( N*N/NBITS ) ); // square matrix
     84 	return this;
     85 }
     86 
     87 /**
     88 * Creates a compact adjacency matrix from an adjacency list.
     89 *
     90 * @name fromAdjacencyList
     91 * @memberof CompactAdjacencyMatrix
     92 * @type {Function}
     93 * @param {(ArrayLikeObject|Iterable)} list - adjacency list
     94 * @param {Function} [clbk] - callback to invoke for each list element
     95 * @param {*} [thisArg] - context
     96 * @throws {TypeError} `this` context must be a constructor
     97 * @throws {TypeError} `this` must be a compact adjacency matrix
     98 * @throws {TypeError} first argument must be an array-like object or an iterable
     99 * @throws {TypeError} second argument must be a function
    100 * @throws {TypeError} each element of a provided adjacency list must be an array-like object
    101 * @throws {TypeError} an iterator must return an array-like object containing vertices
    102 * @throws {TypeError} when provided an iterator, a callback must return an array-like object containing vertices
    103 * @returns {CompactAdjacencyMatrix} adjacency matrix instance
    104 *
    105 * @example
    106 * var list = [ [ 1, 2 ], [ 2 ], [ 3 ], [] ];
    107 *
    108 * var adj = CompactAdjacencyMatrix.fromAdjacencyList( list );
    109 * // returns <CompactAdjacencyMatrix>
    110 *
    111 * var bool = adj.hasEdge( 0, 1 );
    112 * // returns true
    113 *
    114 * bool = adj.hasEdge( 0, 2 );
    115 * // returns true
    116 *
    117 * bool = adj.hasEdge( 1, 2 );
    118 * // returns true
    119 *
    120 * bool = adj.hasEdge( 2, 3 );
    121 * // returns true
    122 */
    123 setReadOnly( CompactAdjacencyMatrix, 'fromAdjacencyList', function fromAdjacencyList( list ) {
    124 	var thisArg;
    125 	var nargs;
    126 	var edges;
    127 	var clbk;
    128 	var adj;
    129 	var tmp;
    130 	var len;
    131 	var N;
    132 	var i;
    133 	var j;
    134 	if ( !isFunction( this ) ) {
    135 		throw new TypeError( 'invalid invocation. `this` context must be a constructor.' );
    136 	}
    137 	if ( this !== CompactAdjacencyMatrix ) {
    138 		throw new TypeError( 'invalid invocation. `this` is not a compact adjacency matrix.' );
    139 	}
    140 	nargs = arguments.length;
    141 	if ( nargs > 1 ) {
    142 		clbk = arguments[ 1 ];
    143 		if ( !isFunction( clbk ) ) {
    144 			throw new TypeError( 'invalid argument. Second argument must be a function. Value: `'+clbk+'`.' );
    145 		}
    146 		if ( nargs > 2 ) {
    147 			thisArg = arguments[ 2 ];
    148 		}
    149 	}
    150 	if ( isArrayLikeObject( list ) ) {
    151 		N = list.length;
    152 		adj = new this( N );
    153 		if ( clbk ) {
    154 			for ( i = 0; i < N; i++ ) {
    155 				edges = clbk.call( thisArg, list[ i ], i );
    156 				if ( !isCollection( edges ) ) {
    157 					throw new TypeError( 'invalid argument. Callback must return an array-like object. Value: `' + edges + '`.' );
    158 				}
    159 				for ( j = 0; j < edges.length; j++ ) {
    160 					adj.addEdge( i, edges[ j ] );
    161 				}
    162 			}
    163 			return adj;
    164 		}
    165 		for ( i = 0; i < N; i++ ) {
    166 			edges = list[ i ];
    167 			if ( !isCollection( edges ) ) {
    168 				throw new TypeError( 'invalid argument. Each element of the adjacency list must be an array-like object. Value: `' + list + '`.' );
    169 			}
    170 			for ( j = 0; j < edges.length; j++ ) {
    171 				adj.addEdge( i, edges[ j ] );
    172 			}
    173 		}
    174 		return adj;
    175 	}
    176 	if ( isObject( list ) && HAS_ITERATOR_SYMBOL && isFunction( list[ ITERATOR_SYMBOL ] ) ) { // eslint-disable-line max-len
    177 		tmp = list[ ITERATOR_SYMBOL ]();
    178 		if ( !isFunction( tmp.next ) ) {
    179 			throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable.' );
    180 		}
    181 		if ( clbk ) {
    182 			tmp = fromIteratorAdjListMap( tmp, clbk, thisArg );
    183 		} else {
    184 			tmp = fromIteratorAdjList( tmp );
    185 		}
    186 		if ( tmp instanceof Error ) {
    187 			throw tmp;
    188 		}
    189 		len = tmp.length;
    190 		adj = new this( len );
    191 		for ( i = 0; i < len; i++ ) {
    192 			edges = tmp[ i ];
    193 			for ( j = 0; j < edges.length; j++ ) {
    194 				adj.addEdge( i, edges[ j ] );
    195 			}
    196 		}
    197 		return adj;
    198 	}
    199 	throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable. Value: `' + list + '`.' );
    200 });
    201 
    202 /**
    203 * Creates a compact adjacency matrix from a list of edges.
    204 *
    205 * @name fromEdges
    206 * @memberof CompactAdjacencyMatrix
    207 * @type {Function}
    208 * @param {NonNegativeInteger} N - number of vertices
    209 * @param {(ArrayLikeObject|Iterable)} edges - list of edges
    210 * @param {Function} [clbk] - callback to invoke for each list element
    211 * @param {*} [thisArg] - context
    212 * @throws {TypeError} `this` context must be a constructor
    213 * @throws {TypeError} `this` must be a compact adjacency matrix
    214 * @throws {TypeError} first argument must be a nonnegative integer
    215 * @throws {TypeError} second argument must be an array-like object
    216 * @throws {TypeError} third argument must be a function
    217 * @throws {TypeError} each element of a provided list of edges must be a two-element array-like object containing vertices
    218 * @throws {TypeError} an iterator must return a two-element array-like object containing vertices
    219 * @throws {TypeError} when provided an iterator, a callback must return a two-element array-like object containing vertices
    220 * @returns {CompactAdjacencyMatrix} adjacency matrix instance
    221 *
    222 * @example
    223 * var edges = [ [ 0, 1 ], [ 0, 2 ], [ 1, 2 ], [ 2, 3 ] ];
    224 *
    225 * var adj = CompactAdjacencyMatrix.fromEdges( 4, edges );
    226 * // returns <CompactAdjacencyMatrix>
    227 *
    228 * var bool = adj.hasEdge( 0, 1 );
    229 * // returns true
    230 *
    231 * bool = adj.hasEdge( 0, 2 );
    232 * // returns true
    233 *
    234 * bool = adj.hasEdge( 1, 2 );
    235 * // returns true
    236 *
    237 * bool = adj.hasEdge( 2, 3 );
    238 * // returns true
    239 */
    240 setReadOnly( CompactAdjacencyMatrix, 'fromEdges', function fromEdges( N, edges ) {
    241 	var thisArg;
    242 	var nargs;
    243 	var clbk;
    244 	var edge;
    245 	var adj;
    246 	var tmp;
    247 	var len;
    248 	var i;
    249 	if ( !isFunction( this ) ) {
    250 		throw new TypeError( 'invalid invocation. `this` context must be a constructor.' );
    251 	}
    252 	if ( this !== CompactAdjacencyMatrix ) {
    253 		throw new TypeError( 'invalid invocation. `this` is not a compact adjacency matrix.' );
    254 	}
    255 	nargs = arguments.length;
    256 	if ( nargs > 2 ) {
    257 		clbk = arguments[ 2 ];
    258 		if ( !isFunction( clbk ) ) {
    259 			throw new TypeError( 'invalid argument. Third argument must be a function. Value: `'+clbk+'`.' );
    260 		}
    261 		if ( nargs > 3 ) {
    262 			thisArg = arguments[ 3 ];
    263 		}
    264 	}
    265 	if ( !isNonNegativeInteger( N ) ) {
    266 		throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + N + '`.' );
    267 	}
    268 	if ( isArrayLikeObject( edges ) ) {
    269 		if ( clbk ) {
    270 			adj = new this( N );
    271 			for ( i = 0; i < edges.length; i++ ) {
    272 				edge = clbk.call( thisArg, edges[ i ], i );
    273 				if ( !isArrayLikeObject( edge ) ) {
    274 					throw new TypeError( 'invalid argument. Callback must return an array-like object. Value: `' + edge + '`.' );
    275 				}
    276 				adj.addEdge( edge[ 0 ], edge[ 1 ] );
    277 			}
    278 			return adj;
    279 		}
    280 		adj = new this( N );
    281 		for ( i = 0; i < edges.length; i++ ) {
    282 			edge = edges[ i ];
    283 			if ( !isArrayLikeObject( edge ) ) {
    284 				throw new TypeError( 'invalid argument. Each element of the edge list must be an array-like object. Value: `' + edge + '`.' );
    285 			}
    286 			adj.addEdge( edge[ 0 ], edge[ 1 ] );
    287 		}
    288 		return adj;
    289 	}
    290 
    291 	if ( isObject( edges ) && HAS_ITERATOR_SYMBOL && isFunction( edges[ ITERATOR_SYMBOL ] ) ) { // eslint-disable-line max-len
    292 		tmp = edges[ ITERATOR_SYMBOL ]();
    293 		if ( !isFunction( tmp.next ) ) {
    294 			throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable.' );
    295 		}
    296 		if ( clbk ) {
    297 			tmp = fromIteratorEdgesMap( tmp, clbk, thisArg );
    298 		} else {
    299 			tmp = fromIteratorEdges( tmp );
    300 		}
    301 		if ( tmp instanceof Error ) {
    302 			throw tmp;
    303 		}
    304 		len = tmp.length;
    305 		adj = new this( len/2 );
    306 		for ( i = 0; i < len; i += 2 ) {
    307 			adj.addEdge( tmp[ i ], tmp[ i+1 ] );
    308 		}
    309 		return adj;
    310 	}
    311 	throw new TypeError( 'invalid argument. Second argument must be an array-like object or an iterable. Value: `' + edges + '`.' );
    312 });
    313 
    314 /**
    315 * Returns indices ("bucket" and bit offset) for an `(i,j)` vertex pair.
    316 *
    317 * @private
    318 * @name _loc
    319 * @memberof CompactAdjacencyMatrix.prototype
    320 * @type {Function}
    321 * @param {NonNegativeInteger} i - starting vertex
    322 * @param {NonNegativeInteger} j - ending vertex
    323 * @param {Array} out - output array
    324 * @throws {TypeError} first argument must be a nonnegative integer
    325 * @throws {TypeError} second argument must be a nonnegative integer
    326 * @throws {RangeError} first argument must not exceed matrix dimensions
    327 * @throws {RangeError} second argument must not exceed matrix dimensions
    328 * @returns {Array} output array
    329 */
    330 setReadOnly( CompactAdjacencyMatrix.prototype, '_loc', function loc( i, j, out ) {
    331 	var bucket;
    332 	var bit;
    333 	var idx;
    334 
    335 	// Compute a strided index for the desired bit:
    336 	idx = ( i*this._N ) + j;
    337 
    338 	// Compute the index of the buffer element (bucket) containing the bit:
    339 	bucket = floor( idx / NBITS );
    340 
    341 	// Compute the bit offset:
    342 	bit = idx - ( bucket*NBITS );
    343 
    344 	// Set the output values:
    345 	out[ 0 ] = bucket;
    346 	out[ 1 ] = bit;
    347 
    348 	return out;
    349 });
    350 
    351 /**
    352 * Adds a directed edge between two vertices.
    353 *
    354 * @name addEdge
    355 * @memberof CompactAdjacencyMatrix.prototype
    356 * @type {Function}
    357 * @param {NonNegativeInteger} i - starting vertex
    358 * @param {NonNegativeInteger} j - ending vertex
    359 * @throws {TypeError} first argument must be a nonnegative integer
    360 * @throws {TypeError} second argument must be a nonnegative integer
    361 * @throws {RangeError} first argument must not exceed matrix dimensions
    362 * @throws {RangeError} second argument must not exceed matrix dimensions
    363 * @returns {CompactAdjacencyMatrix} adjacency matrix instance
    364 *
    365 * @example
    366 * var adj = new CompactAdjacencyMatrix( 4 );
    367 * // returns <CompactAdjacencyMatrix>
    368 *
    369 * adj.addEdge( 0, 1 );
    370 * adj.addEdge( 0, 2 );
    371 * adj.addEdge( 1, 2 );
    372 * adj.addEdge( 2, 3 );
    373 */
    374 setReadOnly( CompactAdjacencyMatrix.prototype, 'addEdge', function addEdge( i, j ) {
    375 	var idx;
    376 	if ( !isNonNegativeInteger( i ) ) {
    377 		throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + i + '`.' );
    378 	}
    379 	if ( !isNonNegativeInteger( j ) ) {
    380 		throw new TypeError( 'invalid argument. Second argument must be a nonnegative integer. Value: `' + j + '`.' );
    381 	}
    382 	if ( i >= this._N ) {
    383 		throw new RangeError( 'invalid argument. First argument exceeds matrix dimensions. Value: `' + i + '`.' );
    384 	}
    385 	if ( j >= this._N ) {
    386 		throw new RangeError( 'invalid argument. Second argument exceeds matrix dimensions. Value: `' + j + '`.' );
    387 	}
    388 	// Resolve the `(i,j)` pair:
    389 	idx = this._loc( i, j, [ 0, 0 ] );
    390 
    391 	// Set the bit for the edge:
    392 	if ( isSet( this._buffer[ idx[0] ], idx[1] ) === false ) {
    393 		this._buffer[ idx[0] ] = setBit( this._buffer[ idx[0] ], idx[1] );
    394 		this._M += 1;
    395 	}
    396 	return this;
    397 });
    398 
    399 /**
    400 * Returns the list of all edges.
    401 *
    402 * @name edges
    403 * @memberof CompactAdjacencyMatrix.prototype
    404 * @type {Array}
    405 *
    406 * @example
    407 * var adj = new CompactAdjacencyMatrix( 4 );
    408 * // returns <CompactAdjacencyMatrix>
    409 *
    410 * adj.addEdge( 0, 1 );
    411 * adj.addEdge( 0, 2 );
    412 * adj.addEdge( 1, 2 );
    413 * adj.addEdge( 2, 3 );
    414 *
    415 * var edges = adj.edges;
    416 * // returns [ [ 0, 1 ], [ 0, 2 ], [ 1, 2 ], [ 2, 3 ] ]
    417 */
    418 setReadOnlyAccessor( CompactAdjacencyMatrix.prototype, 'edges', function edges() {
    419 	var edges;
    420 	var idx;
    421 	var i;
    422 	var j;
    423 
    424 	edges = [];
    425 	idx = [ 0, 0 ];
    426 	for ( i = 0; i < this._N; i++ ) {
    427 		for ( j = 0; j < this._N; j++ ) {
    428 			// Resolve the `(i,j)` pair:
    429 			idx = this._loc( i, j, idx );
    430 
    431 			// Check for an edge:
    432 			if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
    433 				edges.push( [ i, j ] );
    434 			}
    435 		}
    436 	}
    437 	return edges;
    438 });
    439 
    440 /**
    441 * Checks whether a directed edge exists between two vertices.
    442 *
    443 * @name hasEdge
    444 * @memberof CompactAdjacencyMatrix.prototype
    445 * @type {Function}
    446 * @param {NonNegativeInteger} i - starting vertex
    447 * @param {NonNegativeInteger} j - ending vertex
    448 * @throws {TypeError} first argument must be a nonnegative integer
    449 * @throws {TypeError} second argument must be a nonnegative integer
    450 * @throws {RangeError} first argument must not exceed matrix dimensions
    451 * @throws {RangeError} second argument must not exceed matrix dimensions
    452 * @returns {boolean} boolean indicating if an edge exists
    453 *
    454 * @example
    455 * var adj = new CompactAdjacencyMatrix( 4 );
    456 * // returns <CompactAdjacencyMatrix>
    457 *
    458 * adj.addEdge( 0, 1 );
    459 * adj.addEdge( 0, 2 );
    460 * adj.addEdge( 1, 2 );
    461 * adj.addEdge( 2, 3 );
    462 *
    463 * // ...
    464 *
    465 * var bool = adj.hasEdge( 0, 1 );
    466 * // returns true
    467 *
    468 * bool = adj.hasEdge( 0, 2 );
    469 * // returns true
    470 *
    471 * bool = adj.hasEdge( 1, 2 );
    472 * // returns true
    473 *
    474 * bool = adj.hasEdge( 2, 3 );
    475 * // returns true
    476 *
    477 * bool = adj.hasEdge( 1, 3 );
    478 * // returns false
    479 */
    480 setReadOnly( CompactAdjacencyMatrix.prototype, 'hasEdge', function hasEdge( i, j ) {
    481 	var idx;
    482 	if ( !isNonNegativeInteger( i ) ) {
    483 		throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + i + '`.' );
    484 	}
    485 	if ( !isNonNegativeInteger( j ) ) {
    486 		throw new TypeError( 'invalid argument. Second argument must be a nonnegative integer. Value: `' + j + '`.' );
    487 	}
    488 	if ( i >= this._N ) {
    489 		throw new RangeError( 'invalid argument. First argument exceeds matrix dimensions. Value: `' + i + '`.' );
    490 	}
    491 	if ( j >= this._N ) {
    492 		throw new RangeError( 'invalid argument. Second argument exceeds matrix dimensions. Value: `' + j + '`.' );
    493 	}
    494 	// Resolve the `(i,j)` pair:
    495 	idx = this._loc( i, j, [ 0, 0 ] );
    496 
    497 	// Check for an edge:
    498 	return isSet( this._buffer[ idx[0] ], idx[1] );
    499 });
    500 
    501 /**
    502 * Returns the indegree of a vertex (i.e., number of edges ending at a vertex).
    503 *
    504 * @name inDegree
    505 * @memberof CompactAdjacencyMatrix.prototype
    506 * @type {Function}
    507 * @param {NonNegativeInteger} j - vertex
    508 * @throws {TypeError} must provide a nonnegative integer
    509 * @throws {RangeError} must not exceed matrix dimensions
    510 * @returns {NonNegativeInteger} indegree
    511 *
    512 * @example
    513 * var adj = new CompactAdjacencyMatrix( 4 );
    514 * // returns <CompactAdjacencyMatrix>
    515 *
    516 * adj.addEdge( 0, 1 );
    517 * adj.addEdge( 0, 2 );
    518 * adj.addEdge( 1, 2 );
    519 * adj.addEdge( 2, 3 );
    520 *
    521 * var d = adj.inDegree( 2 );
    522 * // returns 2
    523 *
    524 * d = adj.inDegree( 3 );
    525 * // returns 1
    526 */
    527 setReadOnly( CompactAdjacencyMatrix.prototype, 'inDegree', function inDegree( j ) {
    528 	var deg;
    529 	var idx;
    530 	var i;
    531 	if ( !isNonNegativeInteger( j ) ) {
    532 		throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + j + '`.' );
    533 	}
    534 	if ( j >= this._N ) {
    535 		throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + j + '`.' );
    536 	}
    537 	// Iterate over the rows and add up the number of edges...
    538 	deg = 0;
    539 	idx = [ 0, 0 ];
    540 	for ( i = 0; i < this._N; i++ ) {
    541 		// Resolve the `(i,j)` pair:
    542 		idx = this._loc( i, j, idx );
    543 
    544 		// Check for an edge:
    545 		deg += bitValue( this._buffer[ idx[0] ], idx[1] );
    546 	}
    547 	return deg;
    548 });
    549 
    550 /**
    551 * Returns a list of vertices having edges ending at a specified vertex.
    552 *
    553 * @name inEdges
    554 * @memberof CompactAdjacencyMatrix.prototype
    555 * @type {Function}
    556 * @param {NonNegativeInteger} j - vertex
    557 * @throws {TypeError} must provide a nonnegative integer
    558 * @throws {RangeError} must not exceed matrix dimensions
    559 * @returns {Array} list of vertices
    560 *
    561 * @example
    562 * var adj = new CompactAdjacencyMatrix( 4 );
    563 * // returns <CompactAdjacencyMatrix>
    564 *
    565 * adj.addEdge( 0, 1 );
    566 * adj.addEdge( 0, 2 );
    567 * adj.addEdge( 1, 2 );
    568 * adj.addEdge( 2, 3 );
    569 *
    570 * var e = adj.inEdges( 2 );
    571 * // returns [ 0, 1 ]
    572 *
    573 * e = adj.inEdges( 3 );
    574 * // returns [ 2 ]
    575 */
    576 setReadOnly( CompactAdjacencyMatrix.prototype, 'inEdges', function inEdges( j ) {
    577 	var edges;
    578 	var idx;
    579 	var i;
    580 	if ( !isNonNegativeInteger( j ) ) {
    581 		throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + j + '`.' );
    582 	}
    583 	if ( j >= this._N ) {
    584 		throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + j + '`.' );
    585 	}
    586 	// Iterate over the rows and retrieve edges...
    587 	edges = [];
    588 	idx = [ 0, 0 ];
    589 	for ( i = 0; i < this._N; i++ ) {
    590 		// Resolve the `(i,j)` pair:
    591 		idx = this._loc( i, j, idx );
    592 
    593 		// Check for an edge:
    594 		if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
    595 			edges.push( i );
    596 		}
    597 	}
    598 	return edges;
    599 });
    600 
    601 /**
    602 * Returns the total number of edges.
    603 *
    604 * @name nedges
    605 * @memberof CompactAdjacencyMatrix.prototype
    606 * @readonly
    607 * @type {NonNegativeInteger}
    608 *
    609 * @example
    610 * var adj = new CompactAdjacencyMatrix( 4 );
    611 * // returns <CompactAdjacencyMatrix>
    612 *
    613 * // ...
    614 *
    615 * adj.addEdge( 0, 1 );
    616 * adj.addEdge( 0, 2 );
    617 * adj.addEdge( 1, 2 );
    618 *
    619 * // ...
    620 *
    621 * var M = adj.nedges;
    622 * // returns 3
    623 */
    624 setReadOnlyAccessor( CompactAdjacencyMatrix.prototype, 'nedges', function nedges() {
    625 	return this._M;
    626 });
    627 
    628 /**
    629 * Returns the number of vertices.
    630 *
    631 * @name nvertices
    632 * @memberof CompactAdjacencyMatrix.prototype
    633 * @readonly
    634 * @type {NonNegativeInteger}
    635 *
    636 * @example
    637 * var adj = new CompactAdjacencyMatrix( 4 );
    638 * // returns <CompactAdjacencyMatrix>
    639 *
    640 * // ...
    641 *
    642 * var N = adj.nvertices;
    643 * // returns 4
    644 */
    645 setReadOnlyAccessor( CompactAdjacencyMatrix.prototype, 'nvertices', function nvertices() {
    646 	return this._N;
    647 });
    648 
    649 /**
    650 * Returns the outdegree of a vertex (i.e., number of edges starting from a vertex).
    651 *
    652 * @name outDegree
    653 * @memberof CompactAdjacencyMatrix.prototype
    654 * @type {Function}
    655 * @param {NonNegativeInteger} i - vertex
    656 * @throws {TypeError} must provide a nonnegative integer
    657 * @throws {RangeError} must not exceed matrix dimensions
    658 * @returns {NonNegativeInteger} outdegree
    659 *
    660 * @example
    661 * var adj = new CompactAdjacencyMatrix( 4 );
    662 * // returns <CompactAdjacencyMatrix>
    663 *
    664 * adj.addEdge( 0, 1 );
    665 * adj.addEdge( 0, 2 );
    666 * adj.addEdge( 1, 2 );
    667 * adj.addEdge( 2, 3 );
    668 *
    669 * var d = adj.outDegree( 2 );
    670 * // returns 1
    671 *
    672 * d = adj.outDegree( 0 );
    673 * // returns 2
    674 */
    675 setReadOnly( CompactAdjacencyMatrix.prototype, 'outDegree', function outDegree( i ) {
    676 	var deg;
    677 	var idx;
    678 	var j;
    679 	if ( !isNonNegativeInteger( i ) ) {
    680 		throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + i + '`.' );
    681 	}
    682 	if ( i >= this._N ) {
    683 		throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + i + '`.' );
    684 	}
    685 	// Iterate over the columns and add up the number of edges...
    686 	deg = 0;
    687 	idx = [ 0, 0 ];
    688 	for ( j = 0; j < this._N; j++ ) {
    689 		// Resolve the `(i,j)` pair:
    690 		idx = this._loc( i, j, idx );
    691 
    692 		// Check for an edge:
    693 		deg += bitValue( this._buffer[ idx[0] ], idx[1] );
    694 	}
    695 	return deg;
    696 });
    697 
    698 /**
    699 * Returns a list of vertices having edges starting at a specified vertex.
    700 *
    701 * @name outEdges
    702 * @memberof CompactAdjacencyMatrix.prototype
    703 * @type {Function}
    704 * @param {NonNegativeInteger} i - vertex
    705 * @throws {TypeError} must provide a nonnegative integer
    706 * @throws {RangeError} must not exceed matrix dimensions
    707 * @returns {Array} list of vertices
    708 *
    709 * @example
    710 * var adj = new CompactAdjacencyMatrix( 4 );
    711 * // returns <CompactAdjacencyMatrix>
    712 *
    713 * adj.addEdge( 0, 1 );
    714 * adj.addEdge( 0, 2 );
    715 * adj.addEdge( 1, 2 );
    716 * adj.addEdge( 2, 3 );
    717 *
    718 * var e = adj.outEdges( 2 );
    719 * // returns [ 3 ]
    720 *
    721 * e = adj.outEdges( 0 );
    722 * // returns [ 1, 2 ]
    723 */
    724 setReadOnly( CompactAdjacencyMatrix.prototype, 'outEdges', function outEdges( i ) {
    725 	var edges;
    726 	var idx;
    727 	var j;
    728 	if ( !isNonNegativeInteger( i ) ) {
    729 		throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + i + '`.' );
    730 	}
    731 	if ( i >= this._N ) {
    732 		throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + i + '`.' );
    733 	}
    734 	// Iterate over the rows and retrieve edges...
    735 	edges = [];
    736 	idx = [ 0, 0 ];
    737 	for ( j = 0; j < this._N; j++ ) {
    738 		// Resolve the `(i,j)` pair:
    739 		idx = this._loc( i, j, idx );
    740 
    741 		// Check for an edge:
    742 		if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
    743 			edges.push( j );
    744 		}
    745 	}
    746 	return edges;
    747 });
    748 
    749 /**
    750 * Removes a directed edge between two vertices.
    751 *
    752 * @name removeEdge
    753 * @memberof CompactAdjacencyMatrix.prototype
    754 * @type {Function}
    755 * @param {NonNegativeInteger} i - starting vertex
    756 * @param {NonNegativeInteger} j - ending vertex
    757 * @throws {TypeError} first argument must be a nonnegative integer
    758 * @throws {TypeError} second argument must be a nonnegative integer
    759 * @throws {RangeError} first argument must not exceed matrix dimensions
    760 * @throws {RangeError} second argument must not exceed matrix dimensions
    761 * @returns {CompactAdjacencyMatrix} adjacency matrix instance
    762 *
    763 * @example
    764 * var adj = new CompactAdjacencyMatrix( 4 );
    765 * // returns <CompactAdjacencyMatrix>
    766 *
    767 * adj.addEdge( 0, 1 );
    768 * adj.addEdge( 0, 2 );
    769 * adj.addEdge( 1, 2 );
    770 * adj.addEdge( 2, 3 );
    771 *
    772 * // ...
    773 *
    774 * adj.removeEdge( 0, 1 );
    775 * adj.removeEdge( 0, 2 );
    776 * adj.removeEdge( 1, 2 );
    777 * adj.removeEdge( 2, 3 );
    778 */
    779 setReadOnly( CompactAdjacencyMatrix.prototype, 'removeEdge', function removeEdge( i, j ) {
    780 	var idx;
    781 	if ( !isNonNegativeInteger( i ) ) {
    782 		throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + i + '`.' );
    783 	}
    784 	if ( !isNonNegativeInteger( j ) ) {
    785 		throw new TypeError( 'invalid argument. Second argument must be a nonnegative integer. Value: `' + j + '`.' );
    786 	}
    787 	if ( i >= this._N ) {
    788 		throw new RangeError( 'invalid argument. First argument exceeds matrix dimensions. Value: `' + i + '`.' );
    789 	}
    790 	if ( j >= this._N ) {
    791 		throw new RangeError( 'invalid argument. Second argument exceeds matrix dimensions. Value: `' + j + '`.' );
    792 	}
    793 	// Resolve the `(i,j)` pair:
    794 	idx = this._loc( i, j, [ 0, 0 ] );
    795 
    796 	// Clear the bit for the edge:
    797 	if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
    798 		this._buffer[ idx[0] ] = clearBit( this._buffer[ idx[0] ], idx[1] );
    799 		this._M -= 1;
    800 	}
    801 	return this;
    802 });
    803 
    804 /**
    805 * Returns an adjacency list representation.
    806 *
    807 * @name toAdjacencyList
    808 * @memberof CompactAdjacencyMatrix.prototype
    809 * @type {Function}
    810 * @returns {Array} adjacency list representation
    811 *
    812 * @example
    813 * var adj = new CompactAdjacencyMatrix( 4 );
    814 * // returns <CompactAdjacencyMatrix>
    815 *
    816 * adj.addEdge( 0, 1 );
    817 * adj.addEdge( 0, 2 );
    818 * adj.addEdge( 1, 2 );
    819 * adj.addEdge( 2, 3 );
    820 *
    821 * var list = adj.toAdjacencyList();
    822 * // returns [ [ 1, 2 ], [ 2 ], [ 3 ], [] ]
    823 */
    824 setReadOnly( CompactAdjacencyMatrix.prototype, 'toAdjacencyList', function toAdjacencyList() {
    825 	var list;
    826 	var idx;
    827 	var tmp;
    828 	var i;
    829 	var j;
    830 
    831 	list = [];
    832 	idx = [ 0, 0 ];
    833 	for ( i = 0; i < this._N; i++ ) {
    834 		tmp = [];
    835 		for ( j = 0; j < this._N; j++ ) {
    836 			// Resolve the `(i,j)` pair:
    837 			idx = this._loc( i, j, idx );
    838 
    839 			// Check for an edge:
    840 			if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
    841 				tmp.push( j );
    842 			}
    843 		}
    844 		list.push( tmp );
    845 	}
    846 	return list;
    847 });
    848 
    849 /**
    850 * Returns a topological ordering of the directed graph.
    851 *
    852 * ## Notes
    853 *
    854 * -   The function returns a two-element array.
    855 * -   If the function is able to compute a topological ordering, the first array element is the topological ordering and the second element is `null`.
    856 * -   If a topological ordering cannot be achieved (e.g., due to the graph not being a directed acyclic graph (DAG)), the first array element is `null` and the second element is the first encountered cycle.
    857 *
    858 * @name toposort
    859 * @memberof CompactAdjacencyMatrix.prototype
    860 * @type {Function}
    861 * @returns {Array} topological ordering
    862 *
    863 * @example
    864 * var adj = new CompactAdjacencyMatrix( 4 );
    865 * // returns <CompactAdjacencyMatrix>
    866 *
    867 * adj.addEdge( 1, 0 );
    868 * adj.addEdge( 1, 2 );
    869 * adj.addEdge( 0, 2 );
    870 * adj.addEdge( 2, 3 );
    871 *
    872 * var results = adj.toposort();
    873 * // returns <Array>
    874 *
    875 * var order = results[ 0 ];
    876 * // returns [ 1, 0, 2, 3 ]
    877 *
    878 * var cycle = results[ 1 ];
    879 * // returns null
    880 */
    881 setReadOnly( CompactAdjacencyMatrix.prototype, 'toposort', function toposort() {
    882 	var marks;
    883 	var self;
    884 	var out;
    885 	var idx;
    886 	var err;
    887 	var N;
    888 	var s;
    889 	var i;
    890 
    891 	self = this;
    892 	N = this._N;
    893 
    894 	// Initialize an empty list that will contain the sorted vertices:
    895 	out = [];
    896 
    897 	// If the graph is empty, nothing to sort...
    898 	if ( this._N === 0 ) {
    899 		return [ out, null ];
    900 	}
    901 	// Initialize an array for keeping track of whether a vertex has been "visited":
    902 	marks = new Int8Array( N );
    903 
    904 	// Initialize a stack for keeping track of cycles:
    905 	s = [];
    906 
    907 	// Process vertices using depth-first-search...
    908 	idx = [ 0, 0 ];
    909 	for ( i = 0; i < N; i++ ) {
    910 		if ( marks[ i ] === 0 ) {
    911 			err = visit( i );
    912 			if ( err !== 0 ) {
    913 				// Found a cycle...
    914 				s.push( i );
    915 				return [ null, s ];
    916 			}
    917 		}
    918 	}
    919 	// Reverse the output array as the leaves were added first, followed the by the roots, via depth-first-search:
    920 	grev( out.length, out, 1 );
    921 
    922 	return [ out, null ];
    923 
    924 	/**
    925 	* Visits a graph vertex and follows edges until finding a leaf vertex (if one exists).
    926 	*
    927 	* ## Notes
    928 	*
    929 	* -   If the function is able to successfully perform a depth-first-search, the functions returns `0`; otherwise, the function returns `-1` in the event of a cycle.
    930 	*
    931 	* @private
    932 	* @param {NonNegativeInteger} i - vertex
    933 	* @returns {integer} error code
    934 	*/
    935 	function visit( i ) {
    936 		var err;
    937 		var j;
    938 
    939 		// Check if we've already processed/visited this vertex...
    940 		if ( marks[ i ] === 2 ) {
    941 			return 0;
    942 		}
    943 		// Check if we've seen this vertex before and the vertex is still being processed...
    944 		if ( marks[ i ] === 1 ) {
    945 			// We've found a cycle...
    946 			return -1;
    947 		}
    948 		// Mark the current vertex as currently being processed:
    949 		marks[ i ] = 1;
    950 
    951 		// Follow all edges from the current vertex...
    952 		for ( j = 0; j < N; j++ ) {
    953 			idx = self._loc( i, j, idx ); // eslint-disable-line no-underscore-dangle
    954 			if ( isSet( self._buffer[ idx[0] ], idx[1] ) ) { // eslint-disable-line no-underscore-dangle
    955 				err = visit( j );
    956 				if ( err !== 0 ) {
    957 					// This vertex is part of a cycle, so add to cycle stack...
    958 					s.push( j );
    959 					return err;
    960 				}
    961 			}
    962 		}
    963 		// Mark the current vertex as processed:
    964 		marks[ i ] = 2;
    965 
    966 		// Add to the output array now that all subsequent vertices (relative to this vertex) in the graph have already been added to the output array:
    967 		out.push( i );
    968 
    969 		return 0;
    970 	}
    971 });
    972 
    973 
    974 // EXPORTS //
    975 
    976 module.exports = CompactAdjacencyMatrix;