@stdlib/dstructs-named-typed-tuple 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,1444 @@
1
+ /* eslint-disable max-len, max-lines */
2
+
3
+ /**
4
+ * @license Apache-2.0
5
+ *
6
+ * Copyright (c) 2018 The Stdlib Authors.
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */
20
+
21
+ 'use strict';
22
+
23
+ // MODULES //
24
+
25
+ var isStringArray = require( '@stdlib/assert-is-string-array' ).primitives;
26
+ var isEmptyArrayLikeObject = require( '@stdlib/assert-is-empty-array-like-object' );
27
+ var isString = require( '@stdlib/assert-is-string' ).isPrimitive;
28
+ var isArrayBuffer = require( '@stdlib/assert-is-arraybuffer' );
29
+ var isFunction = require( '@stdlib/assert-is-function' );
30
+ var isInteger = require( '@stdlib/assert-is-integer' ).isPrimitive;
31
+ var isObject = require( '@stdlib/assert-is-object' );
32
+ var isCollection = require( '@stdlib/assert-is-collection' );
33
+ var hasOwnProp = require( '@stdlib/assert-has-own-property' );
34
+ var hasIteratorSymbolSupport = require( '@stdlib/assert-has-iterator-symbol-support' );
35
+ var propertiesIn = require( '@stdlib/utils-properties-in' );
36
+ var typedarray = require( '@stdlib/array-typed' );
37
+ var Int8Array = require( '@stdlib/array-int8' );
38
+ var getDtype = require( '@stdlib/array-dtype' );
39
+ var defineProperty = require( '@stdlib/utils-define-property' );
40
+ var setNonEnumerableProperty = require( '@stdlib/utils-define-nonenumerable-property' );
41
+ var setNonEnumerableReadOnlyAccessor = require( '@stdlib/utils-define-nonenumerable-read-only-accessor' ); // eslint-disable-line id-length
42
+ var setNonEnumerableReadWriteAccessor = require( '@stdlib/utils-define-nonenumerable-read-write-accessor' ); // eslint-disable-line id-length
43
+ var floor = require( '@stdlib/math-base-special-floor' );
44
+ var ITERATOR_SYMBOL = require( '@stdlib/symbol-iterator' );
45
+ var format = require( '@stdlib/string-format' );
46
+ var contains = require( './contains.js' );
47
+ var hasDistinctElements = require( './has_distinct_elements.js' );
48
+ var validate = require( './validate.js' );
49
+ var ascending = require( './ascending.js' );
50
+ var fromIterator = require( './from_iterator.js' );
51
+ var fromIteratorMap = require( './from_iterator_map.js' );
52
+
53
+
54
+ // VARIABLES //
55
+
56
+ var RESERVED_PROPS = propertiesIn( new Int8Array( 0 ) );
57
+ var HAS_ITERATOR_SYMBOL = hasIteratorSymbolSupport();
58
+
59
+
60
+ // MAIN //
61
+
62
+ /**
63
+ * Returns a named typed tuple factory.
64
+ *
65
+ * @param {StringArray} names - field (property) names
66
+ * @param {Options} [options] - options
67
+ * @param {string} [options.dtype="float64"] - default data type
68
+ * @param {string} [options.name="tuple"] - tuple name
69
+ * @throws {TypeError} must provide an array of strings
70
+ * @throws {TypeError} must provide distinct field names
71
+ * @throws {Error} cannot provide a reserved field (property) name
72
+ * @throws {TypeError} must provide valid options
73
+ * @throws {Error} must provide a recognized data type
74
+ * @returns {Function} factory function
75
+ *
76
+ * @example
77
+ * var point = factory( [ 'x', 'y' ] );
78
+ *
79
+ * var p = point( [ 1.0, -1.0 ] );
80
+ *
81
+ * var x = p[ 0 ];
82
+ * // returns 1.0
83
+ *
84
+ * x = p.x;
85
+ * // returns 1.0
86
+ *
87
+ * var y = p[ 1 ];
88
+ * // returns -1.0
89
+ *
90
+ * y = p.y;
91
+ * // returns -1.0
92
+ */
93
+ function factory( names, options ) { // eslint-disable-line max-lines-per-function, stdlib/jsdoc-require-throws-tags
94
+ var nfields;
95
+ var fields;
96
+ var opts;
97
+ var err;
98
+ var i;
99
+ if ( !isStringArray( names ) && !isEmptyArrayLikeObject( names ) ) {
100
+ throw new TypeError( format( 'invalid argument. Must provide an array of strings. Value: `%s`.', names ) );
101
+ }
102
+ if ( !hasDistinctElements( names ) ) {
103
+ throw new TypeError( format( 'invalid argument. Field names must be distinct. Value: `%s`.', names ) );
104
+ }
105
+ fields = names.slice();
106
+ nfields = fields.length;
107
+ for ( i = 0; i < nfields; i++ ) {
108
+ if ( contains( RESERVED_PROPS, fields[ i ] ) ) {
109
+ throw new Error( format( 'invalid argument. Provided field name is reserved. Name: `%s`.', fields[ i ] ) );
110
+ }
111
+ }
112
+ opts = {
113
+ 'dtype': 'float64',
114
+ 'name': 'tuple'
115
+ };
116
+ if ( arguments.length > 1 ) {
117
+ err = validate( opts, options );
118
+ if ( err ) {
119
+ throw err;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Returns a named typed tuple.
125
+ *
126
+ * @private
127
+ * @param {(TypedArray|ArrayLikeObject|ArrayBuffer|Iterable)} [arg] - a typed array, array-like object, buffer, or an iterable
128
+ * @param {NonNegativeInteger} [byteOffset=0] - byte offset
129
+ * @param {string} [dtype] - data type
130
+ * @throws {TypeError} must provide a recognized data type
131
+ * @throws {RangeError} arguments must be compatible with tuple length
132
+ * @returns {TypedArray} named typed tuple
133
+ */
134
+ function namedtypedtuple() { // eslint-disable-line max-lines-per-function
135
+ var indices;
136
+ var dtype;
137
+ var nargs;
138
+ var tuple;
139
+ var i;
140
+
141
+ nargs = arguments.length;
142
+ if ( nargs <= 0 ) {
143
+ tuple = typedarray( nfields, opts.dtype );
144
+ } else if ( nargs === 1 ) {
145
+ if ( isString( arguments[ 0 ] ) ) {
146
+ // Arguments: [ dtype ]
147
+ tuple = typedarray( nfields, arguments[ 0 ] );
148
+ } else if ( isArrayBuffer( arguments[ 0 ] ) ) {
149
+ // Arguments: [ ArrayBuffer ]
150
+ tuple = typedarray( arguments[ 0 ], 0, nfields, opts.dtype );
151
+ } else {
152
+ // Arguments: [ TypedArray|ArrayLikeObject|Iterable ]
153
+ tuple = typedarray( arguments[ 0 ], opts.dtype );
154
+ }
155
+ } else if ( nargs === 2 ) {
156
+ if ( isArrayBuffer( arguments[ 0 ] ) ) {
157
+ if ( isString( arguments[ 1 ] ) ) {
158
+ // Arguments: [ ArrayBuffer, dtype ]
159
+ tuple = typedarray( arguments[ 0 ], 0, nfields, arguments[ 1 ] );
160
+ } else {
161
+ // Arguments: [ ArrayBuffer, byteOffset ]
162
+ tuple = typedarray( arguments[ 0 ], arguments[ 1 ], nfields, opts.dtype );
163
+ }
164
+ } else {
165
+ // Arguments: [ TypedArray|ArrayLikeObject|Iterable, dtype ]
166
+ tuple = typedarray( arguments[ 0 ], arguments[ 1 ] );
167
+ }
168
+ } else {
169
+ // Arguments: [ ArrayBuffer, byteOffset, dtype ]
170
+ tuple = typedarray( arguments[ 0 ], arguments[ 1 ], nfields, arguments[ 2 ] );
171
+ }
172
+ if ( tuple.length !== nfields ) {
173
+ throw new RangeError( format( 'invalid arguments. Arguments are incompatible with the number of tuple fields. Number of fields: `%u`. Number of data elements: `%u`.', nfields, tuple.length ) );
174
+ }
175
+ dtype = getDtype( tuple );
176
+
177
+ indices = []; // indirect index look-up table
178
+ for ( i = 0; i < nfields; i++ ) {
179
+ indices.push( i );
180
+ setNonEnumerableReadWriteAccessor( tuple, fields[ i ], getter( i ), setter( i ) );
181
+ }
182
+ setNonEnumerableProperty( tuple, 'name', opts.name );
183
+ setNonEnumerableReadOnlyAccessor( tuple, 'fields', getFields );
184
+ setNonEnumerableReadOnlyAccessor( tuple, 'orderedFields', orderedFields );
185
+
186
+ // Note: keep in alphabetical order
187
+ setNonEnumerableProperty( tuple, 'entries', entries );
188
+ setNonEnumerableProperty( tuple, 'every', every );
189
+ setNonEnumerableProperty( tuple, 'fieldOf', fieldOf );
190
+ setNonEnumerableProperty( tuple, 'filter', filter );
191
+ setNonEnumerableProperty( tuple, 'find', find );
192
+ setNonEnumerableProperty( tuple, 'findIndex', findIndex );
193
+ setNonEnumerableProperty( tuple, 'findField', findField );
194
+ setNonEnumerableProperty( tuple, 'forEach', forEach );
195
+ setNonEnumerableProperty( tuple, 'ind2key', ind2key );
196
+ setNonEnumerableProperty( tuple, 'key2ind', key2ind );
197
+ setNonEnumerableProperty( tuple, 'keys', keys );
198
+ setNonEnumerableProperty( tuple, 'lastFieldOf', lastFieldOf );
199
+ setNonEnumerableProperty( tuple, 'map', map );
200
+ setNonEnumerableProperty( tuple, 'reduce', reduce );
201
+ setNonEnumerableProperty( tuple, 'reduceRight', reduceRight );
202
+ setNonEnumerableProperty( tuple, 'reverse', reverse );
203
+ setNonEnumerableProperty( tuple, 'slice', slice );
204
+ setNonEnumerableProperty( tuple, 'some', some );
205
+ setNonEnumerableProperty( tuple, 'sort', sort );
206
+ setNonEnumerableProperty( tuple, 'subtuple', subtuple );
207
+ setNonEnumerableProperty( tuple, 'toJSON', toJSON );
208
+ setNonEnumerableProperty( tuple, 'toLocaleString', toLocaleString );
209
+ setNonEnumerableProperty( tuple, 'toString', toString );
210
+
211
+ return tuple;
212
+
213
+ /**
214
+ * Returns an accessor to retrieve a tuple value.
215
+ *
216
+ * @private
217
+ * @param {NonNegativeInteger} i - tuple index
218
+ * @returns {Function} accessor
219
+ */
220
+ function getter( i ) {
221
+ return get;
222
+
223
+ /**
224
+ * Returns a tuple value.
225
+ *
226
+ * @private
227
+ * @returns {number} tuple value
228
+ */
229
+ function get() {
230
+ return tuple[ indices[ i ] ];
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Returns an accessor to set a tuple value.
236
+ *
237
+ * @private
238
+ * @param {NonNegativeInteger} i - tuple index
239
+ * @returns {Function} accessor
240
+ */
241
+ function setter( i ) {
242
+ return set;
243
+
244
+ /**
245
+ * Sets a tuple value.
246
+ *
247
+ * @private
248
+ * @param {number} v - value to set
249
+ */
250
+ function set( v ) {
251
+ tuple[ indices[ i ] ] = v;
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Returns the list of tuple fields in index order.
257
+ *
258
+ * @private
259
+ * @memberof tuple
260
+ * @returns {StringArray} tuple fields
261
+ */
262
+ function orderedFields() {
263
+ var out;
264
+ var i;
265
+ out = fields.slice();
266
+ for ( i = 0; i < nfields; i++ ) {
267
+ out[ i ] = fields[ indices[i] ];
268
+ }
269
+ return out;
270
+ }
271
+
272
+ // Note: keep functions which follow in alphabetical order
273
+
274
+ /**
275
+ * Returns an iterator for iterating over tuple key-value pairs.
276
+ *
277
+ * @private
278
+ * @memberof tuple
279
+ * @throws {TypeError} `this` must be the host tuple
280
+ * @returns {Iterator} iterator
281
+ */
282
+ function entries() {
283
+ var self;
284
+ var iter;
285
+ var FLG;
286
+ var i;
287
+
288
+ self = this; // eslint-disable-line no-invalid-this
289
+ if ( self !== tuple ) {
290
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
291
+ }
292
+
293
+ // Initialize the iteration index:
294
+ i = -1;
295
+
296
+ // Create an iterator protocol-compliant object:
297
+ iter = {};
298
+ defineProperty( iter, 'next', {
299
+ 'configurable': false,
300
+ 'enumerable': false,
301
+ 'writable': false,
302
+ 'value': next
303
+ });
304
+ defineProperty( iter, 'return', {
305
+ 'configurable': false,
306
+ 'enumerable': false,
307
+ 'writable': false,
308
+ 'value': end
309
+ });
310
+ if ( HAS_ITERATOR_SYMBOL ) {
311
+ defineProperty( iter, ITERATOR_SYMBOL, {
312
+ 'configurable': false,
313
+ 'enumerable': false,
314
+ 'writable': false,
315
+ 'value': factory
316
+ });
317
+ }
318
+ return iter;
319
+
320
+ /**
321
+ * Returns an iterator protocol-compliant object containing the next iterated value.
322
+ *
323
+ * @private
324
+ * @returns {Object} iterator protocol-compliant object
325
+ */
326
+ function next() {
327
+ i += 1;
328
+ if ( FLG || i >= nfields ) {
329
+ return {
330
+ 'done': true
331
+ };
332
+ }
333
+ return {
334
+ 'value': [ i, fields[ indices[ i ] ], tuple[ i ] ],
335
+ 'done': false
336
+ };
337
+ }
338
+
339
+ /**
340
+ * Finishes an iterator.
341
+ *
342
+ * @private
343
+ * @param {*} [value] - value to return
344
+ * @returns {Object} iterator protocol-compliant object
345
+ */
346
+ function end( value ) {
347
+ FLG = true;
348
+ if ( arguments.length ) {
349
+ return {
350
+ 'value': value,
351
+ 'done': true
352
+ };
353
+ }
354
+ return {
355
+ 'done': true
356
+ };
357
+ }
358
+
359
+ /**
360
+ * Returns a new iterator.
361
+ *
362
+ * @private
363
+ * @returns {Iterator} iterator
364
+ */
365
+ function factory() {
366
+ return self.entries();
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Tests whether all tuple elements pass a test implemented by a predicate function.
372
+ *
373
+ * @private
374
+ * @memberof tuple
375
+ * @param {Function} predicate - predicate function
376
+ * @param {*} [thisArg] - execution context
377
+ * @throws {TypeError} `this` must be the host tuple
378
+ * @throws {TypeError} first argument must be a function
379
+ * @returns {boolean} boolean indicating if all elements pass
380
+ */
381
+ function every( predicate, thisArg ) {
382
+ var bool;
383
+ var i;
384
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
385
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
386
+ }
387
+ if ( !isFunction( predicate ) ) {
388
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', predicate ) );
389
+ }
390
+ for ( i = 0; i < nfields; i++ ) {
391
+ bool = predicate.call( thisArg, tuple[ i ], i, fields[ indices[i] ], tuple );
392
+ if ( !bool ) {
393
+ return false;
394
+ }
395
+ }
396
+ return true;
397
+ }
398
+
399
+ /**
400
+ * Returns the field of the first tuple element strictly equal to a search element.
401
+ *
402
+ * ## Notes
403
+ *
404
+ * - The function does not distinguish between signed and unsigned zero.
405
+ * - If unable to locate a search element, the function returns `undefined`.
406
+ *
407
+ * @private
408
+ * @memberof tuple
409
+ * @param {*} searchElement - search element
410
+ * @param {integer} [fromIndex=0] - tuple index from which to begin searching
411
+ * @throws {TypeError} `this` must be the host tuple
412
+ * @throws {TypeError} second argument must be an integer
413
+ * @returns {(string|void)} tuple field name or `undefined`
414
+ */
415
+ function fieldOf( searchElement ) {
416
+ var i;
417
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
418
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
419
+ }
420
+ if ( arguments.length > 1 ) {
421
+ i = arguments[ 0 ];
422
+ if ( !isInteger( i ) ) {
423
+ throw new TypeError( format( 'invalid argument. Second argument must be an integer. Value: `%s`.', i ) );
424
+ }
425
+ if ( i >= nfields ) {
426
+ return;
427
+ }
428
+ if ( i < 0 ) {
429
+ i = nfields + i;
430
+ if ( i < 0 ) {
431
+ i = 0;
432
+ }
433
+ }
434
+ } else {
435
+ i = 0;
436
+ }
437
+ for ( ; i < nfields; i++ ) {
438
+ if ( tuple[ i ] === searchElement ) {
439
+ return fields[ indices[ i ] ];
440
+ }
441
+ }
442
+ }
443
+
444
+ /**
445
+ * Creates a new tuple which includes those elements for which a predicate function returns a truthy value.
446
+ *
447
+ * ## Notes
448
+ *
449
+ * - The returned tuple has the same data type as the host tuple.
450
+ * - If a predicate function does not return a truthy value for any tuple element, the function returns `null`.
451
+ *
452
+ * @private
453
+ * @memberof tuple
454
+ * @param {Function} predicate - filter (predicate) function
455
+ * @param {*} [thisArg] - execution context
456
+ * @throws {TypeError} `this` must be the host tuple
457
+ * @throws {TypeError} first argument must be a function
458
+ * @returns {(TypedArray|null)} new tuple
459
+ */
460
+ function filter( predicate, thisArg ) {
461
+ var bool;
462
+ var tmp;
463
+ var f;
464
+ var i;
465
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
466
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
467
+ }
468
+ if ( !isFunction( predicate ) ) {
469
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', predicate ) );
470
+ }
471
+ tmp = [];
472
+ f = [];
473
+ for ( i = 0; i < nfields; i++ ) {
474
+ bool = predicate.call( thisArg, tuple[ i ], i, fields[ indices[i] ], tuple );
475
+ if ( bool ) {
476
+ f.push( fields[ indices[i] ] );
477
+ tmp.push( tuple[ i ] );
478
+ }
479
+ }
480
+ if ( f.length === nfields ) {
481
+ return namedtypedtuple( tmp, dtype );
482
+ }
483
+ if ( f.length ) {
484
+ return factory( f, opts )( tmp );
485
+ }
486
+ return null;
487
+ }
488
+
489
+ /**
490
+ * Returns the first tuple element for which a provided predicate function returns a truthy value.
491
+ *
492
+ * @private
493
+ * @memberof tuple
494
+ * @param {Function} predicate - predicate function
495
+ * @param {*} [thisArg] - execution context
496
+ * @throws {TypeError} `this` must be the host tuple
497
+ * @throws {TypeError} first argument must be a function
498
+ * @returns {(number|void)} tuple element
499
+ */
500
+ function find( predicate, thisArg ) {
501
+ var bool;
502
+ var i;
503
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
504
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
505
+ }
506
+ if ( !isFunction( predicate ) ) {
507
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', predicate ) );
508
+ }
509
+ for ( i = 0; i < nfields; i++ ) {
510
+ bool = predicate.call( thisArg, tuple[ i ], i, fields[ indices[i] ], tuple );
511
+ if ( bool ) {
512
+ return tuple[ i ];
513
+ }
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Returns the field of the first tuple element for which a provided predicate function returns a truthy value.
519
+ *
520
+ * ## Notes
521
+ *
522
+ * - If the predicate function never returns a truthy value, the function returns `undefined`.
523
+ *
524
+ * @private
525
+ * @memberof tuple
526
+ * @param {Function} predicate - predicate function
527
+ * @param {*} [thisArg] - execution context
528
+ * @throws {TypeError} `this` must be the host tuple
529
+ * @throws {TypeError} first argument must be a function
530
+ * @returns {(string|void)} tuple field name or `undefined`
531
+ */
532
+ function findField( predicate, thisArg ) {
533
+ var bool;
534
+ var f;
535
+ var i;
536
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
537
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
538
+ }
539
+ if ( !isFunction( predicate ) ) {
540
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', predicate ) );
541
+ }
542
+ for ( i = 0; i < nfields; i++ ) {
543
+ f = fields[ indices[ i ] ];
544
+ bool = predicate.call( thisArg, tuple[ i ], i, f, tuple );
545
+ if ( bool ) {
546
+ return f;
547
+ }
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Returns the index of the first tuple element for which a provided predicate function returns a truthy value.
553
+ *
554
+ * ## Notes
555
+ *
556
+ * - If the predicate function never returns a truthy value, the function returns `-1`.
557
+ *
558
+ * @private
559
+ * @memberof tuple
560
+ * @param {Function} predicate - predicate function
561
+ * @param {*} [thisArg] - execution context
562
+ * @throws {TypeError} `this` must be the host tuple
563
+ * @throws {TypeError} first argument must be a function
564
+ * @returns {integer} tuple index or `-1`
565
+ */
566
+ function findIndex( predicate, thisArg ) {
567
+ var bool;
568
+ var i;
569
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
570
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
571
+ }
572
+ if ( !isFunction( predicate ) ) {
573
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', predicate ) );
574
+ }
575
+ for ( i = 0; i < nfields; i++ ) {
576
+ bool = predicate.call( thisArg, tuple[ i ], i, fields[ indices[i] ], tuple );
577
+ if ( bool ) {
578
+ return i;
579
+ }
580
+ }
581
+ return -1;
582
+ }
583
+
584
+ /**
585
+ * Invokes a callback for each tuple element.
586
+ *
587
+ * @private
588
+ * @memberof tuple
589
+ * @param {Function} fcn - function to invoke
590
+ * @param {*} [thisArg] - execution context
591
+ * @throws {TypeError} `this` must be the host tuple
592
+ * @throws {TypeError} first argument must be a function
593
+ */
594
+ function forEach( fcn, thisArg ) {
595
+ var i;
596
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
597
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
598
+ }
599
+ if ( !isFunction( fcn ) ) {
600
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', fcn ) );
601
+ }
602
+ for ( i = 0; i < nfields; i++ ) {
603
+ fcn.call( thisArg, tuple[ i ], i, fields[ indices[i] ], tuple );
604
+ }
605
+ }
606
+
607
+ /**
608
+ * Converts a tuple index to a field name.
609
+ *
610
+ * ## Notes
611
+ *
612
+ * - If provided an out-of-bounds index, the function returns `undefined`.
613
+ * - If provided a negative tuple index, the function resolves the index relative to the last tuple element.
614
+ *
615
+ * @private
616
+ * @memberof tuple
617
+ * @param {integer} ind - tuple index
618
+ * @throws {TypeError} `this` must be the host tuple
619
+ * @throws {TypeError} must provide an integer
620
+ * @returns {(string|void)} field name or undefined
621
+ */
622
+ function ind2key( ind ) {
623
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
624
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
625
+ }
626
+ if ( !isInteger( ind ) ) {
627
+ throw new TypeError( format( 'invalid argument. Must provide an integer. Value: `%s`.', ind ) );
628
+ }
629
+ if ( ind < 0 ) {
630
+ ind = nfields + ind;
631
+ }
632
+ if ( ind < 0 || ind >= nfields ) {
633
+ return;
634
+ }
635
+ return fields[ indices[ ind ] ];
636
+ }
637
+
638
+ /**
639
+ * Converts a field name to a tuple index.
640
+ *
641
+ * ## Notes
642
+ *
643
+ * - If provided an unknown field name, the function returns `-1`.
644
+ *
645
+ * @private
646
+ * @memberof tuple
647
+ * @param {string} key - field name
648
+ * @throws {TypeError} `this` must be the host tuple
649
+ * @throws {TypeError} first argument must be a string
650
+ * @returns {integer} tuple index
651
+ */
652
+ function key2ind( key ) {
653
+ var i;
654
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
655
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
656
+ }
657
+ if ( !isString( key ) ) {
658
+ throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', key ) );
659
+ }
660
+ for ( i = 0; i < nfields; i++ ) {
661
+ if ( fields[ indices[i] ] === key ) {
662
+ return i;
663
+ }
664
+ }
665
+ return -1;
666
+ }
667
+
668
+ /**
669
+ * Returns an iterator for iterating over tuple keys.
670
+ *
671
+ * @private
672
+ * @memberof tuple
673
+ * @throws {TypeError} `this` must be the host tuple
674
+ * @returns {Iterator} iterator
675
+ */
676
+ function keys() {
677
+ var self;
678
+ var iter;
679
+ var FLG;
680
+ var i;
681
+
682
+ self = this; // eslint-disable-line no-invalid-this
683
+ if ( self !== tuple ) {
684
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
685
+ }
686
+
687
+ // Initialize the iteration index:
688
+ i = -1;
689
+
690
+ // Create an iterator protocol-compliant object:
691
+ iter = {};
692
+ defineProperty( iter, 'next', {
693
+ 'configurable': false,
694
+ 'enumerable': false,
695
+ 'writable': false,
696
+ 'value': next
697
+ });
698
+ defineProperty( iter, 'return', {
699
+ 'configurable': false,
700
+ 'enumerable': false,
701
+ 'writable': false,
702
+ 'value': end
703
+ });
704
+ if ( HAS_ITERATOR_SYMBOL ) {
705
+ defineProperty( iter, ITERATOR_SYMBOL, {
706
+ 'configurable': false,
707
+ 'enumerable': false,
708
+ 'writable': false,
709
+ 'value': factory
710
+ });
711
+ }
712
+ return iter;
713
+
714
+ /**
715
+ * Returns an iterator protocol-compliant object containing the next iterated value.
716
+ *
717
+ * @private
718
+ * @returns {Object} iterator protocol-compliant object
719
+ */
720
+ function next() {
721
+ i += 1;
722
+ if ( FLG || i >= nfields ) {
723
+ return {
724
+ 'done': true
725
+ };
726
+ }
727
+ return {
728
+ 'value': [ i, fields[ indices[ i ] ] ],
729
+ 'done': false
730
+ };
731
+ }
732
+
733
+ /**
734
+ * Finishes an iterator.
735
+ *
736
+ * @private
737
+ * @param {*} [value] - value to return
738
+ * @returns {Object} iterator protocol-compliant object
739
+ */
740
+ function end( value ) {
741
+ FLG = true;
742
+ if ( arguments.length ) {
743
+ return {
744
+ 'value': value,
745
+ 'done': true
746
+ };
747
+ }
748
+ return {
749
+ 'done': true
750
+ };
751
+ }
752
+
753
+ /**
754
+ * Returns a new iterator.
755
+ *
756
+ * @private
757
+ * @returns {Iterator} iterator
758
+ */
759
+ function factory() {
760
+ return self.keys();
761
+ }
762
+ }
763
+
764
+ /**
765
+ * Returns the field of the last tuple element strictly equal to a search element, iterating from right to left.
766
+ *
767
+ * ## Notes
768
+ *
769
+ * - The function does not distinguish between signed and unsigned zero.
770
+ * - If unable to locate a search element, the function returns `undefined`.
771
+ *
772
+ * @private
773
+ * @memberof tuple
774
+ * @param {*} searchElement - search element
775
+ * @param {integer} [fromIndex=-1] - tuple index from which to begin searching
776
+ * @throws {TypeError} `this` must be the host tuple
777
+ * @throws {TypeError} second argument must be an integer
778
+ * @returns {(string|void)} tuple field name or `undefined`
779
+ */
780
+ function lastFieldOf( searchElement ) {
781
+ var i;
782
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
783
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
784
+ }
785
+ if ( arguments.length > 1 ) {
786
+ i = arguments[ 1 ];
787
+ if ( !isInteger( i ) ) {
788
+ throw new TypeError( format( 'invalid argument. Second argument must be an integer. Value: `%s`.', i ) );
789
+ }
790
+ if ( i >= nfields ) {
791
+ i = nfields - 1;
792
+ } else if ( i < 0 ) {
793
+ i = nfields + i;
794
+ if ( i < 0 ) {
795
+ return;
796
+ }
797
+ }
798
+ } else {
799
+ i = nfields - 1;
800
+ }
801
+ for ( ; i >= 0; i-- ) {
802
+ if ( tuple[ i ] === searchElement ) {
803
+ return fields[ indices[ i ] ];
804
+ }
805
+ }
806
+ }
807
+
808
+ /**
809
+ * Maps each tuple element to an element in a new tuple.
810
+ *
811
+ * ## Notes
812
+ *
813
+ * - The returned tuple has the same data type as the host tuple.
814
+ *
815
+ * @private
816
+ * @memberof tuple
817
+ * @param {Function} fcn - map function
818
+ * @param {*} [thisArg] - execution context
819
+ * @throws {TypeError} `this` must be the host tuple
820
+ * @throws {TypeError} first argument must be a function
821
+ * @returns {TypedArray} new tuple
822
+ */
823
+ function map( fcn, thisArg ) {
824
+ var out;
825
+ var i;
826
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
827
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
828
+ }
829
+ if ( !isFunction( fcn ) ) {
830
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', fcn ) );
831
+ }
832
+ out = namedtypedtuple( dtype );
833
+ for ( i = 0; i < nfields; i++ ) {
834
+ out[ i ] = fcn.call( thisArg, tuple[ i ], i, fields[ indices[i] ], tuple );
835
+ }
836
+ return out;
837
+ }
838
+
839
+ /**
840
+ * Applies a function against an accumulator and each element in a tuple and returns the accumulated result.
841
+ *
842
+ * @private
843
+ * @memberof tuple
844
+ * @param {Function} fcn - reduction function
845
+ * @param {*} [initial] - initial value
846
+ * @throws {TypeError} `this` must be the host tuple
847
+ * @throws {TypeError} first argument must be a function
848
+ * @returns {*} accumulated result
849
+ */
850
+ function reduce( fcn ) {
851
+ var acc;
852
+ var i;
853
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
854
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
855
+ }
856
+ if ( !isFunction( fcn ) ) {
857
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', fcn ) );
858
+ }
859
+ if ( arguments.length > 1 ) {
860
+ acc = arguments[ 1 ];
861
+ i = 0;
862
+ } else {
863
+ acc = tuple[ 0 ];
864
+ i = 1;
865
+ }
866
+ for ( ; i < nfields; i++ ) {
867
+ acc = fcn( acc, tuple[ i ], i, fields[ indices[i] ], tuple );
868
+ }
869
+ return acc;
870
+ }
871
+
872
+ /**
873
+ * Applies a function against an accumulator and each element in a tuple and returns the accumulated result, iterating from right to left.
874
+ *
875
+ * @private
876
+ * @memberof tuple
877
+ * @param {Function} fcn - reduction function
878
+ * @param {*} [initial] - initial value
879
+ * @throws {TypeError} `this` must be the host tuple
880
+ * @throws {TypeError} first argument must be a function
881
+ * @returns {*} accumulated result
882
+ */
883
+ function reduceRight( fcn ) {
884
+ var acc;
885
+ var i;
886
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
887
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
888
+ }
889
+ if ( !isFunction( fcn ) ) {
890
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', fcn ) );
891
+ }
892
+ if ( arguments.length > 1 ) {
893
+ acc = arguments[ 1 ];
894
+ i = nfields - 1;
895
+ } else {
896
+ acc = tuple[ nfields-1 ];
897
+ i = nfields - 2;
898
+ }
899
+ for ( ; i >= 0; i-- ) {
900
+ acc = fcn( acc, tuple[ i ], i, fields[ indices[i] ], tuple );
901
+ }
902
+ return acc;
903
+ }
904
+
905
+ /**
906
+ * Reverses a tuple **in-place**.
907
+ *
908
+ * @private
909
+ * @memberof tuple
910
+ * @throws {TypeError} `this` must be the host tuple
911
+ * @returns {TypedArray} reversed tuple
912
+ */
913
+ function reverse() {
914
+ var tmp;
915
+ var i;
916
+ var j;
917
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
918
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
919
+ }
920
+ for ( i = 0; i < floor( nfields/2 ); i++ ) {
921
+ j = nfields - i - 1;
922
+ tmp = tuple[ i ];
923
+ tuple[ i ] = tuple[ j ];
924
+ tuple[ j ] = tmp;
925
+ }
926
+ // Because the indices are bounded [0,nfields), we can use simple arithmetic to "reverse" index values in-place...
927
+ for ( i = 0; i < nfields; i++ ) {
928
+ indices[ i ] = nfields - indices[ i ] - 1;
929
+ }
930
+ return tuple;
931
+ }
932
+
933
+ /**
934
+ * Copies elements to a new tuple with the same underlying data type as the host tuple.
935
+ *
936
+ * @private
937
+ * @memberof tuple
938
+ * @param {integer} [begin=0] - start element index (inclusive)
939
+ * @param {integer} [end=tuple.length] - end element index (exclusive)
940
+ * @throws {TypeError} `this` must be the host tuple
941
+ * @throws {TypeError} first argument must be an integer
942
+ * @throws {TypeError} second argument must be an integer
943
+ * @returns {TypedArray} new tuple
944
+ */
945
+ function slice( begin, end ) {
946
+ var tmp;
947
+ var f;
948
+ var i;
949
+ var j;
950
+
951
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
952
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
953
+ }
954
+ if ( arguments.length === 0 ) {
955
+ return namedtypedtuple( tuple, dtype );
956
+ }
957
+ i = begin;
958
+ if ( !isInteger( i ) ) {
959
+ throw new TypeError( format( 'invalid argument. First argument must be an integer. Value: `%s`.', begin ) );
960
+ }
961
+ if ( i < 0 ) {
962
+ i = nfields + i;
963
+ if ( i < 0 ) {
964
+ i = 0;
965
+ }
966
+ }
967
+ if ( arguments.length === 1 ) {
968
+ j = nfields;
969
+ } else {
970
+ j = end;
971
+ if ( !isInteger( j ) ) {
972
+ throw new TypeError( format( 'invalid argument. Second argument must be an integer. Value: `%s`.', end ) );
973
+ }
974
+ if ( j < 0 ) {
975
+ j = nfields + j;
976
+ if ( j < 0 ) {
977
+ j = 0;
978
+ }
979
+ } else if ( j > nfields ) {
980
+ j = nfields;
981
+ }
982
+ }
983
+ f = [];
984
+ tmp = [];
985
+ for ( ; i < j; i++ ) {
986
+ f.push( fields[ indices[i] ] );
987
+ tmp.push( tuple[ i ] );
988
+ }
989
+ return factory( f, opts )( tmp, dtype );
990
+ }
991
+
992
+ /**
993
+ * Tests whether at least one tuple element passes a test implemented by a predicate function.
994
+ *
995
+ * @private
996
+ * @memberof tuple
997
+ * @param {Function} predicate - predicate function
998
+ * @param {*} [thisArg] - execution context
999
+ * @throws {TypeError} `this` must be the host tuple
1000
+ * @throws {TypeError} first argument must be a function
1001
+ * @returns {boolean} boolean indicating if at least one element passes
1002
+ */
1003
+ function some( predicate, thisArg ) {
1004
+ var bool;
1005
+ var i;
1006
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
1007
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
1008
+ }
1009
+ if ( !isFunction( predicate ) ) {
1010
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', predicate ) );
1011
+ }
1012
+ for ( i = 0; i < nfields; i++ ) {
1013
+ bool = predicate.call( thisArg, tuple[ i ], i, fields[ indices[i] ], tuple );
1014
+ if ( bool ) {
1015
+ return true;
1016
+ }
1017
+ }
1018
+ return false;
1019
+ }
1020
+
1021
+ /**
1022
+ * Sorts a tuple in-place.
1023
+ *
1024
+ * ## Notes
1025
+ *
1026
+ * - The comparison function is provided two tuple elements, `a` and `b`, per invocation, and its return value determines the sort order as follows:
1027
+ *
1028
+ * - If the comparison function returns a value **less** than zero, then the function sorts `a` to an index lower than `b` (i.e., `a` should come **before** `b`).
1029
+ * - If the comparison function returns a value **greater** than zero, then the function sorts `a` to an index higher than `b` (i.e., `b` should come **before** `a`).
1030
+ * - If the comparison function returns **zero**, then the relative order of `a` and `b` _should_ remain unchanged.
1031
+ *
1032
+ * - Invoking this method does **not** affect tuple field assignments.
1033
+ *
1034
+ * @private
1035
+ * @memberof tuple
1036
+ * @param {Function} [compareFunction] - function which specifies the sort order
1037
+ * @throws {TypeError} `this` must be the host tuple
1038
+ * @throws {TypeError} first argument must be a function
1039
+ * @returns {TypedArray} sorted tuple
1040
+ */
1041
+ function sort( compareFunction ) {
1042
+ var clbk;
1043
+ var tmp;
1044
+ var i;
1045
+ var j;
1046
+ var k;
1047
+ var v;
1048
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
1049
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
1050
+ }
1051
+ if ( arguments.length ) {
1052
+ if ( !isFunction( compareFunction ) ) {
1053
+ throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', compareFunction ) );
1054
+ }
1055
+ clbk = compareFunction;
1056
+ } else {
1057
+ clbk = ascending;
1058
+ }
1059
+ indices.sort( wrapper );
1060
+
1061
+ // Create a temporary indices array which we'll reorder as we rearrange the tuple elements:
1062
+ tmp = indices.slice();
1063
+
1064
+ // Rearrange tuple elements according to the rearranged indices (note: every "move" moves a tuple element to its desired position with runtime complexity O(N))...
1065
+ for ( i = 0; i < nfields; i++ ) {
1066
+ // Check if we need to move a tuple element:
1067
+ if ( tmp[ i ] !== i ) {
1068
+ v = tuple[ i ];
1069
+ j = i;
1070
+ k = tmp[ j ];
1071
+
1072
+ // Follow "cycles", stopping once we are back at index `i`...
1073
+ while ( k !== i ) {
1074
+ tuple[ j ] = tuple[ k ];
1075
+ tmp[ j ] = j;
1076
+ j = k;
1077
+ k = tmp[ j ];
1078
+ }
1079
+ tuple[ j ] = v;
1080
+ tmp[ j ] = j;
1081
+ }
1082
+ }
1083
+ return tuple;
1084
+
1085
+ /**
1086
+ * Wraps a comparison function to allow sorting the internal indices array rather than the tuple directly.
1087
+ *
1088
+ * @private
1089
+ * @param {NonNegativeInteger} ia - first index
1090
+ * @param {NonNegativeInteger} ib - second index
1091
+ * @returns {*} value specifying the sort order
1092
+ */
1093
+ function wrapper( ia, ib ) {
1094
+ var a = tuple[ indices[ ia ] ];
1095
+ var b = tuple[ indices[ ib ] ];
1096
+ return clbk( a, b );
1097
+ }
1098
+ }
1099
+
1100
+ /**
1101
+ * Creates a new tuple over the same underlying `ArrayBuffer` and with the same underlying data type as the host tuple.
1102
+ *
1103
+ * @private
1104
+ * @memberof tuple
1105
+ * @param {integer} [begin=0] - start element index (inclusive)
1106
+ * @param {integer} [end=tuple.length] - end element index (exclusive)
1107
+ * @throws {TypeError} `this` must be the host tuple
1108
+ * @throws {TypeError} first argument must be an integer
1109
+ * @throws {TypeError} second argument must be an integer
1110
+ * @returns {TypedArray} new tuple
1111
+ */
1112
+ function subtuple( begin, end ) {
1113
+ var f;
1114
+ var i;
1115
+ var j;
1116
+ var k;
1117
+
1118
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
1119
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
1120
+ }
1121
+ if ( arguments.length === 0 ) {
1122
+ return namedtypedtuple( tuple.buffer, tuple.byteOffset, dtype );
1123
+ }
1124
+ i = begin;
1125
+ if ( !isInteger( i ) ) {
1126
+ throw new TypeError( format( 'invalid argument. First argument must be an integer. Value: `%s`.', begin ) );
1127
+ }
1128
+ if ( i < 0 ) {
1129
+ i = nfields + i;
1130
+ if ( i < 0 ) {
1131
+ i = 0;
1132
+ }
1133
+ }
1134
+ if ( arguments.length === 1 ) {
1135
+ j = nfields;
1136
+ } else {
1137
+ j = end;
1138
+ if ( !isInteger( j ) ) {
1139
+ throw new TypeError( format( 'invalid argument. Second argument must be an integer. Value: `%s`.', end ) );
1140
+ }
1141
+ if ( j < 0 ) {
1142
+ j = nfields + j;
1143
+ if ( j < 0 ) {
1144
+ j = 0;
1145
+ }
1146
+ } else if ( j > nfields ) {
1147
+ j = nfields;
1148
+ }
1149
+ }
1150
+ if ( j <= i ) {
1151
+ return factory( [], opts )( tuple.buffer, tuple.byteOffset, dtype );
1152
+ }
1153
+ f = [];
1154
+ for ( k = i; k < j; k++ ) {
1155
+ f.push( fields[ indices[k] ] );
1156
+ }
1157
+ return factory( f, opts )( tuple.buffer, tuple.byteOffset+(i*tuple.BYTES_PER_ELEMENT), dtype );
1158
+ }
1159
+
1160
+ /**
1161
+ * Serializes a tuple as JSON.
1162
+ *
1163
+ * @private
1164
+ * @memberof tuple
1165
+ * @throws {TypeError} `this` must be the host tuple
1166
+ * @returns {JSON} tuple JSON representation
1167
+ */
1168
+ function toJSON() {
1169
+ var out;
1170
+ var i;
1171
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
1172
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
1173
+ }
1174
+ out = {};
1175
+ for ( i = 0; i < nfields; i++ ) {
1176
+ out[ fields[i] ] = tuple[ indices[i] ];
1177
+ }
1178
+ return out;
1179
+ }
1180
+
1181
+ /**
1182
+ * Serializes a tuple as a locale-specific string.
1183
+ *
1184
+ * @private
1185
+ * @memberof tuple
1186
+ * @param {(string|Array<string>)} [locales] - locale identifier(s)
1187
+ * @param {Object} [options] - configuration options
1188
+ * @throws {TypeError} `this` must be the host tuple
1189
+ * @throws {TypeError} first argument must be a string or an array of strings
1190
+ * @throws {TypeError} options argument must be an object
1191
+ * @returns {string} string representation
1192
+ */
1193
+ function toLocaleString( locales, options ) {
1194
+ var loc;
1195
+ var out;
1196
+ var o;
1197
+ var i;
1198
+
1199
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
1200
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
1201
+ }
1202
+ if ( arguments.length === 0 ) {
1203
+ loc = [];
1204
+ } else if ( isString( locales ) || isStringArray( locales ) ) {
1205
+ loc = locales;
1206
+ } else {
1207
+ throw new TypeError( format( 'invalid argument. First argument must be a string or an array of strings. Value: `%s`.', locales ) );
1208
+ }
1209
+ if ( arguments.length < 2 ) {
1210
+ o = {};
1211
+ } else if ( isObject( options ) ) {
1212
+ o = options;
1213
+ } else {
1214
+ throw new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) );
1215
+ }
1216
+ out = opts.name.toLocaleString( loc, o ) + '(';
1217
+ for ( i = 0; i < nfields; i++ ) {
1218
+ out += fields[ i ].toLocaleString( loc, o );
1219
+ out += '=';
1220
+ out += tuple[ indices[ i ] ].toLocaleString( loc, o );
1221
+ if ( i < nfields-1 ) {
1222
+ out += ', ';
1223
+ }
1224
+ }
1225
+ out += ')';
1226
+ return out;
1227
+ }
1228
+
1229
+ /**
1230
+ * Serializes a tuple as a string.
1231
+ *
1232
+ * @private
1233
+ * @memberof tuple
1234
+ * @throws {TypeError} `this` must be the host tuple
1235
+ * @returns {string} tuple string representation
1236
+ */
1237
+ function toString() {
1238
+ var out;
1239
+ var i;
1240
+ if ( this !== tuple ) { // eslint-disable-line no-invalid-this
1241
+ throw new TypeError( 'invalid invocation. `this` is not host tuple.' );
1242
+ }
1243
+ out = opts.name + '(';
1244
+ for ( i = 0; i < nfields; i++ ) {
1245
+ out += fields[ i ];
1246
+ out += '=';
1247
+ out += tuple[ indices[ i ] ];
1248
+ if ( i < nfields-1 ) {
1249
+ out += ', ';
1250
+ }
1251
+ }
1252
+ out += ')';
1253
+ return out;
1254
+ }
1255
+ }
1256
+
1257
+ /**
1258
+ * Returns the list of tuple fields.
1259
+ *
1260
+ * @private
1261
+ * @memberof tuple
1262
+ * @returns {StringArray} tuple fields
1263
+ */
1264
+ function getFields() {
1265
+ return fields.slice();
1266
+ }
1267
+
1268
+ // Note: keep the following methods in alphabetical order...
1269
+
1270
+ /**
1271
+ * Creates a new tuple from an array-like object or an iterable.
1272
+ *
1273
+ * @private
1274
+ * @name from
1275
+ * @memberof namedtypedtuple
1276
+ * @type {Function}
1277
+ * @param {(ArrayLikeObject|Iterable)} src - array-like object or iterable
1278
+ * @param {Function} [clbk] - callback to invoke for each source element
1279
+ * @param {*} [thisArg] - callback execution context
1280
+ * @throws {TypeError} `this` must be the host tuple factory
1281
+ * @throws {TypeError} first argument must be an array-like object or an iterable
1282
+ * @throws {RangeError} source must be compatible with tuple length
1283
+ * @throws {TypeError} second argument must be a function
1284
+ * @returns {TypedArray} new tuple
1285
+ */
1286
+ defineProperty( namedtypedtuple, 'from', {
1287
+ 'configurable': false,
1288
+ 'enumerable': false,
1289
+ 'writable': false,
1290
+ 'value': function from( src ) { // eslint-disable-line no-restricted-syntax
1291
+ var thisArg;
1292
+ var nargs;
1293
+ var tuple;
1294
+ var clbk;
1295
+ var tmp;
1296
+ var it;
1297
+ var i;
1298
+ if ( this !== namedtypedtuple ) {
1299
+ throw new TypeError( 'invalid invocation. `this` is not the host tuple factory.' );
1300
+ }
1301
+ nargs = arguments.length;
1302
+ if ( nargs > 1 ) {
1303
+ clbk = arguments[ 1 ];
1304
+ if ( !isFunction( clbk ) ) {
1305
+ throw new TypeError( format( 'invalid argument. Second argument must be a function. Value: `%s`.', clbk ) );
1306
+ }
1307
+ if ( nargs > 2 ) {
1308
+ thisArg = arguments[ 2 ];
1309
+ }
1310
+ }
1311
+ if ( isCollection( src ) ) {
1312
+ if ( src.length !== nfields ) {
1313
+ throw new RangeError( format( 'invalid argument. Source is incompatible with the number of tuple fields. Number of fields: `%u`. Source length: `%u`.', nfields, src.length ) );
1314
+ }
1315
+ tuple = namedtypedtuple( nfields, opts.dtype );
1316
+ if ( clbk ) {
1317
+ for ( i = 0; i < nfields; i++ ) {
1318
+ tuple[ i ] = clbk.call( thisArg, src[ i ], i, fields[ i ] );
1319
+ }
1320
+ } else {
1321
+ for ( i = 0; i < nfields; i++ ) {
1322
+ tuple[ i ] = src[ i ];
1323
+ }
1324
+ }
1325
+ } else if ( isObject( src ) && HAS_ITERATOR_SYMBOL && isFunction( src[ ITERATOR_SYMBOL ] ) ) {
1326
+ it = src[ ITERATOR_SYMBOL ]();
1327
+ if ( !isFunction( it.next ) ) {
1328
+ throw new TypeError( format( 'invalid argument. First argument must be an array-like object or an iterable. Value: `%s`.', src ) );
1329
+ }
1330
+ if ( clbk ) {
1331
+ tmp = fromIteratorMap( fields, it, clbk, thisArg );
1332
+ } else {
1333
+ tmp = fromIterator( it );
1334
+ }
1335
+ tuple = namedtypedtuple( tmp, opts.dtype );
1336
+ } else {
1337
+ throw new TypeError( format( 'invalid argument. First argument must be an array-like object or an iterable. Value: `%s`.', src ) );
1338
+ }
1339
+ return tuple;
1340
+ }
1341
+ });
1342
+
1343
+ /**
1344
+ * Creates a new tuple from an object containing tuple fields.
1345
+ *
1346
+ * @private
1347
+ * @name fromObject
1348
+ * @memberof namedtypedtuple
1349
+ * @type {Function}
1350
+ * @param {Object} obj - source object
1351
+ * @param {Function} [clbk] - callback to invoke for each source object tuple field
1352
+ * @param {*} [thisArg] - callback execution context
1353
+ * @throws {TypeError} `this` must be the host tuple factory
1354
+ * @throws {TypeError} first argument must be an object
1355
+ * @throws {TypeError} second argument must be a function
1356
+ * @returns {TypedArray} new tuple
1357
+ */
1358
+ defineProperty( namedtypedtuple, 'fromObject', {
1359
+ 'configurable': false,
1360
+ 'enumerable': false,
1361
+ 'writable': false,
1362
+ 'value': function fromObject( obj ) { // eslint-disable-line no-restricted-syntax
1363
+ var thisArg;
1364
+ var nargs;
1365
+ var tuple;
1366
+ var clbk;
1367
+ var f;
1368
+ var i;
1369
+ if ( this !== namedtypedtuple ) {
1370
+ throw new TypeError( 'invalid invocation. `this` is not the host tuple factory.' );
1371
+ }
1372
+ if ( obj === null || typeof obj !== 'object' ) {
1373
+ throw new TypeError( format( 'invalid argument. First argument must be an object. Value: `%s`.', obj ) );
1374
+ }
1375
+ nargs = arguments.length;
1376
+ if ( nargs > 1 ) {
1377
+ clbk = arguments[ 1 ];
1378
+ if ( !isFunction( clbk ) ) {
1379
+ throw new TypeError( format( 'invalid argument. Second argument must be a function. Value: `%s`.', clbk ) );
1380
+ }
1381
+ if ( nargs > 2 ) {
1382
+ thisArg = arguments[ 2 ];
1383
+ }
1384
+ }
1385
+ tuple = namedtypedtuple( nfields, opts.dtype );
1386
+ if ( clbk ) {
1387
+ for ( i = 0; i < nfields; i++ ) {
1388
+ f = fields[ i ];
1389
+ if ( hasOwnProp( obj, f ) ) {
1390
+ tuple[ i ] = clbk.call( thisArg, obj[ f ], f );
1391
+ }
1392
+ }
1393
+ } else {
1394
+ for ( i = 0; i < nfields; i++ ) {
1395
+ f = fields[ i ];
1396
+ if ( hasOwnProp( obj, f ) ) {
1397
+ tuple[ i ] = obj[ f ];
1398
+ }
1399
+ }
1400
+ }
1401
+ return tuple;
1402
+ }
1403
+ });
1404
+
1405
+ /**
1406
+ * Creates a new tuple from a variable number of arguments.
1407
+ *
1408
+ * @private
1409
+ * @name of
1410
+ * @memberof namedtypedtuple
1411
+ * @type {Function}
1412
+ * @param {...number} element - tuple elements
1413
+ * @throws {TypeError} `this` must be the host tuple factory
1414
+ * @throws {RangeError} incompatible number of arguments
1415
+ * @returns {TypedArray} new tuple
1416
+ */
1417
+ defineProperty( namedtypedtuple, 'of', {
1418
+ 'configurable': false,
1419
+ 'enumerable': false,
1420
+ 'writable': false,
1421
+ 'value': function of() { // eslint-disable-line no-restricted-syntax
1422
+ var args;
1423
+ var i;
1424
+ if ( this !== namedtypedtuple ) {
1425
+ throw new TypeError( 'invalid invocation. `this` is not the host tuple factory.' );
1426
+ }
1427
+ if ( arguments.length !== nfields ) {
1428
+ throw new RangeError( format( 'invalid invocation. Number of arguments is incompatible with the number of tuple fields. Number of fields: `%u`. Number of arguments: `%u`.', nfields, arguments.length ) );
1429
+ }
1430
+ args = [];
1431
+ for ( i = 0; i < arguments.length; i++ ) {
1432
+ args.push( arguments[ i ] );
1433
+ }
1434
+ return namedtypedtuple( args );
1435
+ }
1436
+ });
1437
+
1438
+ return namedtypedtuple;
1439
+ }
1440
+
1441
+
1442
+ // EXPORTS //
1443
+
1444
+ module.exports = factory;