@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/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;