time-to-botec/js/node_modules/@stdlib/utils/compact-adjacency-matrix/lib/main.js
NunoSempere b6addc7f05 feat: add the node modules
Necessary in order to clearly see the squiggle hotwiring.
2022-12-03 12:44:49 +00:00

977 lines
28 KiB
JavaScript

/**
* @license Apache-2.0
*
* Copyright (c) 2021 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-restricted-syntax, no-invalid-this */
'use strict';
// MODULES //
var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive;
var isArrayLikeObject = require( '@stdlib/assert/is-array-like-object' );
var isCollection = require( '@stdlib/assert/is-collection' );
var isFunction = require( '@stdlib/assert/is-function' );
var isObject = require( '@stdlib/assert/is-object' );
var hasIteratorSymbolSupport = require( '@stdlib/assert/has-iterator-symbol-support' );
var ITERATOR_SYMBOL = require( '@stdlib/symbol/iterator' );
var setReadOnly = require( './../../define-nonenumerable-read-only-property' );
var setReadOnlyAccessor = require( './../../define-nonenumerable-read-only-accessor' );
var Int32Array = require( '@stdlib/array/int32' );
var Int8Array = require( '@stdlib/array/int8' );
var ceil = require( '@stdlib/math/base/special/ceil' );
var floor = require( '@stdlib/math/base/special/floor' );
var grev = require( '@stdlib/blas/ext/base/grev' );
var fromIteratorAdjList = require( './from_adjacency_list_iterator.js' );
var fromIteratorAdjListMap = require( './from_adjacency_list_iterator_map.js' );
var fromIteratorEdges = require( './from_edges_iterator.js' );
var fromIteratorEdgesMap = require( './from_edges_iterator_map.js' );
var setBit = require( './set_bit.js' );
var clearBit = require( './clear_bit.js' );
var isSet = require( './is_set.js' );
var bitValue = require( './bit_value.js' );
// VARIABLES //
var HAS_ITERATOR_SYMBOL = hasIteratorSymbolSupport();
var NBITS = Int32Array.BYTES_PER_ELEMENT * 8; // 8 bits per byte
// MAIN //
/**
* Compact adjacency matrix constructor.
*
* @constructor
* @param {NonNegativeInteger} N - number of vertices
* @throws {TypeError} must provide a nonnegative integer
* @returns {CompactAdjacencyMatrix} adjacency matrix instance
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*/
function CompactAdjacencyMatrix( N ) {
if ( !( this instanceof CompactAdjacencyMatrix ) ) {
return new CompactAdjacencyMatrix( N );
}
if ( !isNonNegativeInteger( N ) ) {
throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + N + '`.' );
}
this._N = N; // number of vertices
this._M = 0; // number of edges
this._buffer = new Int32Array( ceil( N*N/NBITS ) ); // square matrix
return this;
}
/**
* Creates a compact adjacency matrix from an adjacency list.
*
* @name fromAdjacencyList
* @memberof CompactAdjacencyMatrix
* @type {Function}
* @param {(ArrayLikeObject|Iterable)} list - adjacency list
* @param {Function} [clbk] - callback to invoke for each list element
* @param {*} [thisArg] - context
* @throws {TypeError} `this` context must be a constructor
* @throws {TypeError} `this` must be a compact adjacency matrix
* @throws {TypeError} first argument must be an array-like object or an iterable
* @throws {TypeError} second argument must be a function
* @throws {TypeError} each element of a provided adjacency list must be an array-like object
* @throws {TypeError} an iterator must return an array-like object containing vertices
* @throws {TypeError} when provided an iterator, a callback must return an array-like object containing vertices
* @returns {CompactAdjacencyMatrix} adjacency matrix instance
*
* @example
* var list = [ [ 1, 2 ], [ 2 ], [ 3 ], [] ];
*
* var adj = CompactAdjacencyMatrix.fromAdjacencyList( list );
* // returns <CompactAdjacencyMatrix>
*
* var bool = adj.hasEdge( 0, 1 );
* // returns true
*
* bool = adj.hasEdge( 0, 2 );
* // returns true
*
* bool = adj.hasEdge( 1, 2 );
* // returns true
*
* bool = adj.hasEdge( 2, 3 );
* // returns true
*/
setReadOnly( CompactAdjacencyMatrix, 'fromAdjacencyList', function fromAdjacencyList( list ) {
var thisArg;
var nargs;
var edges;
var clbk;
var adj;
var tmp;
var len;
var N;
var i;
var j;
if ( !isFunction( this ) ) {
throw new TypeError( 'invalid invocation. `this` context must be a constructor.' );
}
if ( this !== CompactAdjacencyMatrix ) {
throw new TypeError( 'invalid invocation. `this` is not a compact adjacency matrix.' );
}
nargs = arguments.length;
if ( nargs > 1 ) {
clbk = arguments[ 1 ];
if ( !isFunction( clbk ) ) {
throw new TypeError( 'invalid argument. Second argument must be a function. Value: `'+clbk+'`.' );
}
if ( nargs > 2 ) {
thisArg = arguments[ 2 ];
}
}
if ( isArrayLikeObject( list ) ) {
N = list.length;
adj = new this( N );
if ( clbk ) {
for ( i = 0; i < N; i++ ) {
edges = clbk.call( thisArg, list[ i ], i );
if ( !isCollection( edges ) ) {
throw new TypeError( 'invalid argument. Callback must return an array-like object. Value: `' + edges + '`.' );
}
for ( j = 0; j < edges.length; j++ ) {
adj.addEdge( i, edges[ j ] );
}
}
return adj;
}
for ( i = 0; i < N; i++ ) {
edges = list[ i ];
if ( !isCollection( edges ) ) {
throw new TypeError( 'invalid argument. Each element of the adjacency list must be an array-like object. Value: `' + list + '`.' );
}
for ( j = 0; j < edges.length; j++ ) {
adj.addEdge( i, edges[ j ] );
}
}
return adj;
}
if ( isObject( list ) && HAS_ITERATOR_SYMBOL && isFunction( list[ ITERATOR_SYMBOL ] ) ) { // eslint-disable-line max-len
tmp = list[ ITERATOR_SYMBOL ]();
if ( !isFunction( tmp.next ) ) {
throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable.' );
}
if ( clbk ) {
tmp = fromIteratorAdjListMap( tmp, clbk, thisArg );
} else {
tmp = fromIteratorAdjList( tmp );
}
if ( tmp instanceof Error ) {
throw tmp;
}
len = tmp.length;
adj = new this( len );
for ( i = 0; i < len; i++ ) {
edges = tmp[ i ];
for ( j = 0; j < edges.length; j++ ) {
adj.addEdge( i, edges[ j ] );
}
}
return adj;
}
throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable. Value: `' + list + '`.' );
});
/**
* Creates a compact adjacency matrix from a list of edges.
*
* @name fromEdges
* @memberof CompactAdjacencyMatrix
* @type {Function}
* @param {NonNegativeInteger} N - number of vertices
* @param {(ArrayLikeObject|Iterable)} edges - list of edges
* @param {Function} [clbk] - callback to invoke for each list element
* @param {*} [thisArg] - context
* @throws {TypeError} `this` context must be a constructor
* @throws {TypeError} `this` must be a compact adjacency matrix
* @throws {TypeError} first argument must be a nonnegative integer
* @throws {TypeError} second argument must be an array-like object
* @throws {TypeError} third argument must be a function
* @throws {TypeError} each element of a provided list of edges must be a two-element array-like object containing vertices
* @throws {TypeError} an iterator must return a two-element array-like object containing vertices
* @throws {TypeError} when provided an iterator, a callback must return a two-element array-like object containing vertices
* @returns {CompactAdjacencyMatrix} adjacency matrix instance
*
* @example
* var edges = [ [ 0, 1 ], [ 0, 2 ], [ 1, 2 ], [ 2, 3 ] ];
*
* var adj = CompactAdjacencyMatrix.fromEdges( 4, edges );
* // returns <CompactAdjacencyMatrix>
*
* var bool = adj.hasEdge( 0, 1 );
* // returns true
*
* bool = adj.hasEdge( 0, 2 );
* // returns true
*
* bool = adj.hasEdge( 1, 2 );
* // returns true
*
* bool = adj.hasEdge( 2, 3 );
* // returns true
*/
setReadOnly( CompactAdjacencyMatrix, 'fromEdges', function fromEdges( N, edges ) {
var thisArg;
var nargs;
var clbk;
var edge;
var adj;
var tmp;
var len;
var i;
if ( !isFunction( this ) ) {
throw new TypeError( 'invalid invocation. `this` context must be a constructor.' );
}
if ( this !== CompactAdjacencyMatrix ) {
throw new TypeError( 'invalid invocation. `this` is not a compact adjacency matrix.' );
}
nargs = arguments.length;
if ( nargs > 2 ) {
clbk = arguments[ 2 ];
if ( !isFunction( clbk ) ) {
throw new TypeError( 'invalid argument. Third argument must be a function. Value: `'+clbk+'`.' );
}
if ( nargs > 3 ) {
thisArg = arguments[ 3 ];
}
}
if ( !isNonNegativeInteger( N ) ) {
throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + N + '`.' );
}
if ( isArrayLikeObject( edges ) ) {
if ( clbk ) {
adj = new this( N );
for ( i = 0; i < edges.length; i++ ) {
edge = clbk.call( thisArg, edges[ i ], i );
if ( !isArrayLikeObject( edge ) ) {
throw new TypeError( 'invalid argument. Callback must return an array-like object. Value: `' + edge + '`.' );
}
adj.addEdge( edge[ 0 ], edge[ 1 ] );
}
return adj;
}
adj = new this( N );
for ( i = 0; i < edges.length; i++ ) {
edge = edges[ i ];
if ( !isArrayLikeObject( edge ) ) {
throw new TypeError( 'invalid argument. Each element of the edge list must be an array-like object. Value: `' + edge + '`.' );
}
adj.addEdge( edge[ 0 ], edge[ 1 ] );
}
return adj;
}
if ( isObject( edges ) && HAS_ITERATOR_SYMBOL && isFunction( edges[ ITERATOR_SYMBOL ] ) ) { // eslint-disable-line max-len
tmp = edges[ ITERATOR_SYMBOL ]();
if ( !isFunction( tmp.next ) ) {
throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable.' );
}
if ( clbk ) {
tmp = fromIteratorEdgesMap( tmp, clbk, thisArg );
} else {
tmp = fromIteratorEdges( tmp );
}
if ( tmp instanceof Error ) {
throw tmp;
}
len = tmp.length;
adj = new this( len/2 );
for ( i = 0; i < len; i += 2 ) {
adj.addEdge( tmp[ i ], tmp[ i+1 ] );
}
return adj;
}
throw new TypeError( 'invalid argument. Second argument must be an array-like object or an iterable. Value: `' + edges + '`.' );
});
/**
* Returns indices ("bucket" and bit offset) for an `(i,j)` vertex pair.
*
* @private
* @name _loc
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} i - starting vertex
* @param {NonNegativeInteger} j - ending vertex
* @param {Array} out - output array
* @throws {TypeError} first argument must be a nonnegative integer
* @throws {TypeError} second argument must be a nonnegative integer
* @throws {RangeError} first argument must not exceed matrix dimensions
* @throws {RangeError} second argument must not exceed matrix dimensions
* @returns {Array} output array
*/
setReadOnly( CompactAdjacencyMatrix.prototype, '_loc', function loc( i, j, out ) {
var bucket;
var bit;
var idx;
// Compute a strided index for the desired bit:
idx = ( i*this._N ) + j;
// Compute the index of the buffer element (bucket) containing the bit:
bucket = floor( idx / NBITS );
// Compute the bit offset:
bit = idx - ( bucket*NBITS );
// Set the output values:
out[ 0 ] = bucket;
out[ 1 ] = bit;
return out;
});
/**
* Adds a directed edge between two vertices.
*
* @name addEdge
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} i - starting vertex
* @param {NonNegativeInteger} j - ending vertex
* @throws {TypeError} first argument must be a nonnegative integer
* @throws {TypeError} second argument must be a nonnegative integer
* @throws {RangeError} first argument must not exceed matrix dimensions
* @throws {RangeError} second argument must not exceed matrix dimensions
* @returns {CompactAdjacencyMatrix} adjacency matrix instance
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'addEdge', function addEdge( i, j ) {
var idx;
if ( !isNonNegativeInteger( i ) ) {
throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + i + '`.' );
}
if ( !isNonNegativeInteger( j ) ) {
throw new TypeError( 'invalid argument. Second argument must be a nonnegative integer. Value: `' + j + '`.' );
}
if ( i >= this._N ) {
throw new RangeError( 'invalid argument. First argument exceeds matrix dimensions. Value: `' + i + '`.' );
}
if ( j >= this._N ) {
throw new RangeError( 'invalid argument. Second argument exceeds matrix dimensions. Value: `' + j + '`.' );
}
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, [ 0, 0 ] );
// Set the bit for the edge:
if ( isSet( this._buffer[ idx[0] ], idx[1] ) === false ) {
this._buffer[ idx[0] ] = setBit( this._buffer[ idx[0] ], idx[1] );
this._M += 1;
}
return this;
});
/**
* Returns the list of all edges.
*
* @name edges
* @memberof CompactAdjacencyMatrix.prototype
* @type {Array}
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* var edges = adj.edges;
* // returns [ [ 0, 1 ], [ 0, 2 ], [ 1, 2 ], [ 2, 3 ] ]
*/
setReadOnlyAccessor( CompactAdjacencyMatrix.prototype, 'edges', function edges() {
var edges;
var idx;
var i;
var j;
edges = [];
idx = [ 0, 0 ];
for ( i = 0; i < this._N; i++ ) {
for ( j = 0; j < this._N; j++ ) {
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, idx );
// Check for an edge:
if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
edges.push( [ i, j ] );
}
}
}
return edges;
});
/**
* Checks whether a directed edge exists between two vertices.
*
* @name hasEdge
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} i - starting vertex
* @param {NonNegativeInteger} j - ending vertex
* @throws {TypeError} first argument must be a nonnegative integer
* @throws {TypeError} second argument must be a nonnegative integer
* @throws {RangeError} first argument must not exceed matrix dimensions
* @throws {RangeError} second argument must not exceed matrix dimensions
* @returns {boolean} boolean indicating if an edge exists
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* // ...
*
* var bool = adj.hasEdge( 0, 1 );
* // returns true
*
* bool = adj.hasEdge( 0, 2 );
* // returns true
*
* bool = adj.hasEdge( 1, 2 );
* // returns true
*
* bool = adj.hasEdge( 2, 3 );
* // returns true
*
* bool = adj.hasEdge( 1, 3 );
* // returns false
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'hasEdge', function hasEdge( i, j ) {
var idx;
if ( !isNonNegativeInteger( i ) ) {
throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + i + '`.' );
}
if ( !isNonNegativeInteger( j ) ) {
throw new TypeError( 'invalid argument. Second argument must be a nonnegative integer. Value: `' + j + '`.' );
}
if ( i >= this._N ) {
throw new RangeError( 'invalid argument. First argument exceeds matrix dimensions. Value: `' + i + '`.' );
}
if ( j >= this._N ) {
throw new RangeError( 'invalid argument. Second argument exceeds matrix dimensions. Value: `' + j + '`.' );
}
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, [ 0, 0 ] );
// Check for an edge:
return isSet( this._buffer[ idx[0] ], idx[1] );
});
/**
* Returns the indegree of a vertex (i.e., number of edges ending at a vertex).
*
* @name inDegree
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} j - vertex
* @throws {TypeError} must provide a nonnegative integer
* @throws {RangeError} must not exceed matrix dimensions
* @returns {NonNegativeInteger} indegree
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* var d = adj.inDegree( 2 );
* // returns 2
*
* d = adj.inDegree( 3 );
* // returns 1
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'inDegree', function inDegree( j ) {
var deg;
var idx;
var i;
if ( !isNonNegativeInteger( j ) ) {
throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + j + '`.' );
}
if ( j >= this._N ) {
throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + j + '`.' );
}
// Iterate over the rows and add up the number of edges...
deg = 0;
idx = [ 0, 0 ];
for ( i = 0; i < this._N; i++ ) {
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, idx );
// Check for an edge:
deg += bitValue( this._buffer[ idx[0] ], idx[1] );
}
return deg;
});
/**
* Returns a list of vertices having edges ending at a specified vertex.
*
* @name inEdges
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} j - vertex
* @throws {TypeError} must provide a nonnegative integer
* @throws {RangeError} must not exceed matrix dimensions
* @returns {Array} list of vertices
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* var e = adj.inEdges( 2 );
* // returns [ 0, 1 ]
*
* e = adj.inEdges( 3 );
* // returns [ 2 ]
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'inEdges', function inEdges( j ) {
var edges;
var idx;
var i;
if ( !isNonNegativeInteger( j ) ) {
throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + j + '`.' );
}
if ( j >= this._N ) {
throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + j + '`.' );
}
// Iterate over the rows and retrieve edges...
edges = [];
idx = [ 0, 0 ];
for ( i = 0; i < this._N; i++ ) {
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, idx );
// Check for an edge:
if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
edges.push( i );
}
}
return edges;
});
/**
* Returns the total number of edges.
*
* @name nedges
* @memberof CompactAdjacencyMatrix.prototype
* @readonly
* @type {NonNegativeInteger}
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* // ...
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
*
* // ...
*
* var M = adj.nedges;
* // returns 3
*/
setReadOnlyAccessor( CompactAdjacencyMatrix.prototype, 'nedges', function nedges() {
return this._M;
});
/**
* Returns the number of vertices.
*
* @name nvertices
* @memberof CompactAdjacencyMatrix.prototype
* @readonly
* @type {NonNegativeInteger}
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* // ...
*
* var N = adj.nvertices;
* // returns 4
*/
setReadOnlyAccessor( CompactAdjacencyMatrix.prototype, 'nvertices', function nvertices() {
return this._N;
});
/**
* Returns the outdegree of a vertex (i.e., number of edges starting from a vertex).
*
* @name outDegree
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} i - vertex
* @throws {TypeError} must provide a nonnegative integer
* @throws {RangeError} must not exceed matrix dimensions
* @returns {NonNegativeInteger} outdegree
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* var d = adj.outDegree( 2 );
* // returns 1
*
* d = adj.outDegree( 0 );
* // returns 2
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'outDegree', function outDegree( i ) {
var deg;
var idx;
var j;
if ( !isNonNegativeInteger( i ) ) {
throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + i + '`.' );
}
if ( i >= this._N ) {
throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + i + '`.' );
}
// Iterate over the columns and add up the number of edges...
deg = 0;
idx = [ 0, 0 ];
for ( j = 0; j < this._N; j++ ) {
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, idx );
// Check for an edge:
deg += bitValue( this._buffer[ idx[0] ], idx[1] );
}
return deg;
});
/**
* Returns a list of vertices having edges starting at a specified vertex.
*
* @name outEdges
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} i - vertex
* @throws {TypeError} must provide a nonnegative integer
* @throws {RangeError} must not exceed matrix dimensions
* @returns {Array} list of vertices
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* var e = adj.outEdges( 2 );
* // returns [ 3 ]
*
* e = adj.outEdges( 0 );
* // returns [ 1, 2 ]
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'outEdges', function outEdges( i ) {
var edges;
var idx;
var j;
if ( !isNonNegativeInteger( i ) ) {
throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + i + '`.' );
}
if ( i >= this._N ) {
throw new RangeError( 'invalid argument. Input argument cannot exceed matrix dimensions. Value: `' + i + '`.' );
}
// Iterate over the rows and retrieve edges...
edges = [];
idx = [ 0, 0 ];
for ( j = 0; j < this._N; j++ ) {
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, idx );
// Check for an edge:
if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
edges.push( j );
}
}
return edges;
});
/**
* Removes a directed edge between two vertices.
*
* @name removeEdge
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @param {NonNegativeInteger} i - starting vertex
* @param {NonNegativeInteger} j - ending vertex
* @throws {TypeError} first argument must be a nonnegative integer
* @throws {TypeError} second argument must be a nonnegative integer
* @throws {RangeError} first argument must not exceed matrix dimensions
* @throws {RangeError} second argument must not exceed matrix dimensions
* @returns {CompactAdjacencyMatrix} adjacency matrix instance
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* // ...
*
* adj.removeEdge( 0, 1 );
* adj.removeEdge( 0, 2 );
* adj.removeEdge( 1, 2 );
* adj.removeEdge( 2, 3 );
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'removeEdge', function removeEdge( i, j ) {
var idx;
if ( !isNonNegativeInteger( i ) ) {
throw new TypeError( 'invalid argument. First argument must be a nonnegative integer. Value: `' + i + '`.' );
}
if ( !isNonNegativeInteger( j ) ) {
throw new TypeError( 'invalid argument. Second argument must be a nonnegative integer. Value: `' + j + '`.' );
}
if ( i >= this._N ) {
throw new RangeError( 'invalid argument. First argument exceeds matrix dimensions. Value: `' + i + '`.' );
}
if ( j >= this._N ) {
throw new RangeError( 'invalid argument. Second argument exceeds matrix dimensions. Value: `' + j + '`.' );
}
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, [ 0, 0 ] );
// Clear the bit for the edge:
if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
this._buffer[ idx[0] ] = clearBit( this._buffer[ idx[0] ], idx[1] );
this._M -= 1;
}
return this;
});
/**
* Returns an adjacency list representation.
*
* @name toAdjacencyList
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @returns {Array} adjacency list representation
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 0, 1 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 2, 3 );
*
* var list = adj.toAdjacencyList();
* // returns [ [ 1, 2 ], [ 2 ], [ 3 ], [] ]
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'toAdjacencyList', function toAdjacencyList() {
var list;
var idx;
var tmp;
var i;
var j;
list = [];
idx = [ 0, 0 ];
for ( i = 0; i < this._N; i++ ) {
tmp = [];
for ( j = 0; j < this._N; j++ ) {
// Resolve the `(i,j)` pair:
idx = this._loc( i, j, idx );
// Check for an edge:
if ( isSet( this._buffer[ idx[0] ], idx[1] ) ) {
tmp.push( j );
}
}
list.push( tmp );
}
return list;
});
/**
* Returns a topological ordering of the directed graph.
*
* ## Notes
*
* - The function returns a two-element array.
* - If the function is able to compute a topological ordering, the first array element is the topological ordering and the second element is `null`.
* - 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.
*
* @name toposort
* @memberof CompactAdjacencyMatrix.prototype
* @type {Function}
* @returns {Array} topological ordering
*
* @example
* var adj = new CompactAdjacencyMatrix( 4 );
* // returns <CompactAdjacencyMatrix>
*
* adj.addEdge( 1, 0 );
* adj.addEdge( 1, 2 );
* adj.addEdge( 0, 2 );
* adj.addEdge( 2, 3 );
*
* var results = adj.toposort();
* // returns <Array>
*
* var order = results[ 0 ];
* // returns [ 1, 0, 2, 3 ]
*
* var cycle = results[ 1 ];
* // returns null
*/
setReadOnly( CompactAdjacencyMatrix.prototype, 'toposort', function toposort() {
var marks;
var self;
var out;
var idx;
var err;
var N;
var s;
var i;
self = this;
N = this._N;
// Initialize an empty list that will contain the sorted vertices:
out = [];
// If the graph is empty, nothing to sort...
if ( this._N === 0 ) {
return [ out, null ];
}
// Initialize an array for keeping track of whether a vertex has been "visited":
marks = new Int8Array( N );
// Initialize a stack for keeping track of cycles:
s = [];
// Process vertices using depth-first-search...
idx = [ 0, 0 ];
for ( i = 0; i < N; i++ ) {
if ( marks[ i ] === 0 ) {
err = visit( i );
if ( err !== 0 ) {
// Found a cycle...
s.push( i );
return [ null, s ];
}
}
}
// Reverse the output array as the leaves were added first, followed the by the roots, via depth-first-search:
grev( out.length, out, 1 );
return [ out, null ];
/**
* Visits a graph vertex and follows edges until finding a leaf vertex (if one exists).
*
* ## Notes
*
* - 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.
*
* @private
* @param {NonNegativeInteger} i - vertex
* @returns {integer} error code
*/
function visit( i ) {
var err;
var j;
// Check if we've already processed/visited this vertex...
if ( marks[ i ] === 2 ) {
return 0;
}
// Check if we've seen this vertex before and the vertex is still being processed...
if ( marks[ i ] === 1 ) {
// We've found a cycle...
return -1;
}
// Mark the current vertex as currently being processed:
marks[ i ] = 1;
// Follow all edges from the current vertex...
for ( j = 0; j < N; j++ ) {
idx = self._loc( i, j, idx ); // eslint-disable-line no-underscore-dangle
if ( isSet( self._buffer[ idx[0] ], idx[1] ) ) { // eslint-disable-line no-underscore-dangle
err = visit( j );
if ( err !== 0 ) {
// This vertex is part of a cycle, so add to cycle stack...
s.push( j );
return err;
}
}
}
// Mark the current vertex as processed:
marks[ i ] = 2;
// 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:
out.push( i );
return 0;
}
});
// EXPORTS //
module.exports = CompactAdjacencyMatrix;