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