main.js (27201B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 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 */ 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 isArrayBuffer = require( '@stdlib/assert/is-arraybuffer' ); 29 var isObject = require( '@stdlib/assert/is-object' ); 30 var isArray = require( '@stdlib/assert/is-array' ); 31 var isFunction = require( '@stdlib/assert/is-function' ); 32 var isComplexLike = require( '@stdlib/assert/is-complex-like' ); 33 var isEven = require( '@stdlib/math/base/assert/is-even' ); 34 var isInteger = require( '@stdlib/math/base/assert/is-integer' ); 35 var hasIteratorSymbolSupport = require( '@stdlib/assert/has-iterator-symbol-support' ); 36 var ITERATOR_SYMBOL = require( '@stdlib/symbol/iterator' ); 37 var defineProperty = require( '@stdlib/utils/define-property' ); 38 var Float64Array = require( './../../float64' ); 39 var Complex128 = require( '@stdlib/complex/float64' ); 40 var real = require( '@stdlib/complex/real' ); 41 var imag = require( '@stdlib/complex/imag' ); 42 var fromIterator = require( './from_iterator.js' ); 43 var fromIteratorMap = require( './from_iterator_map.js' ); 44 var fromArray = require( './from_array.js' ); 45 46 47 // VARIABLES // 48 49 var BYTES_PER_ELEMENT = Float64Array.BYTES_PER_ELEMENT * 2; 50 var HAS_ITERATOR_SYMBOL = hasIteratorSymbolSupport(); 51 52 53 // FUNCTIONS // 54 55 /** 56 * Returns a boolean indicating if a value is a complex typed array. 57 * 58 * @private 59 * @param {*} value - value to test 60 * @returns {boolean} boolean indicating if a value is a complex typed array 61 */ 62 function isComplexArray( value ) { 63 return ( 64 value instanceof Complex128Array || 65 ( 66 typeof value === 'object' && 67 value !== null && 68 ( 69 value.constructor.name === 'Complex64Array' || 70 value.constructor.name === 'Complex128Array' 71 ) && 72 typeof value._length === 'number' && // eslint-disable-line no-underscore-dangle 73 74 // NOTE: we don't perform a more rigorous test here for a typed array for performance reasons, as robustly checking for a typed array instance could require walking the prototype tree and performing relatively expensive constructor checks... 75 typeof value._buffer === 'object' // eslint-disable-line no-underscore-dangle 76 ) 77 ); 78 } 79 80 /** 81 * Returns a boolean indicating if a value is a complex typed array constructor. 82 * 83 * @private 84 * @param {*} value - value to test 85 * @returns {boolean} boolean indicating if a value is a complex typed array constructor 86 */ 87 function isComplexArrayConstructor( value ) { 88 return ( 89 value === Complex128Array || 90 91 // NOTE: weaker test in order to avoid a circular dependency with Complex64Array... 92 value.name === 'Complex64Array' 93 ); 94 } 95 96 97 // MAIN // 98 99 /** 100 * 128-bit complex number array constructor. 101 * 102 * @constructor 103 * @param {(NonNegativeInteger|TypedArray|ArrayLikeObject|ArrayBuffer|Iterable)} [arg] - length, typed array, array-like object, or buffer 104 * @param {NonNegativeInteger} [byteOffset=0] - byte offset 105 * @param {NonNegativeInteger} [length] - view length 106 * @throws {RangeError} ArrayBuffer byte length must be a multiple of `16` 107 * @throws {RangeError} array-like object and typed array input arguments must have a length which is a multiple of two 108 * @throws {TypeError} if provided only a single argument, must provide a valid argument 109 * @throws {TypeError} byte offset must be a nonnegative integer 110 * @throws {RangeError} byte offset must be a multiple of `16` 111 * @throws {TypeError} view length must be a positive multiple of `16` 112 * @throws {RangeError} must provide sufficient memory to accommodate byte offset and view length requirements 113 * @throws {TypeError} an iterator must return either a two element array containing real and imaginary components or a complex number 114 * @returns {Complex128Array} complex number array 115 * 116 * @example 117 * var arr = new Complex128Array(); 118 * // returns <Complex128Array> 119 * 120 * var len = arr.length; 121 * // returns 0 122 * 123 * @example 124 * var arr = new Complex128Array( 2 ); 125 * // returns <Complex128Array> 126 * 127 * var len = arr.length; 128 * // returns 2 129 * 130 * @example 131 * var arr = new Complex128Array( [ 1.0, -1.0 ] ); 132 * // returns <Complex128Array> 133 * 134 * var len = arr.length; 135 * // returns 1 136 * 137 * @example 138 * var ArrayBuffer = require( '@stdlib/array/buffer' ); 139 * 140 * var buf = new ArrayBuffer( 32 ); 141 * var arr = new Complex128Array( buf ); 142 * // returns <Complex128Array> 143 * 144 * var len = arr.length; 145 * // returns 2 146 * 147 * @example 148 * var ArrayBuffer = require( '@stdlib/array/buffer' ); 149 * 150 * var buf = new ArrayBuffer( 32 ); 151 * var arr = new Complex128Array( buf, 16 ); 152 * // returns <Complex128Array> 153 * 154 * var len = arr.length; 155 * // returns 1 156 * 157 * @example 158 * var ArrayBuffer = require( '@stdlib/array/buffer' ); 159 * 160 * var buf = new ArrayBuffer( 64 ); 161 * var arr = new Complex128Array( buf, 16, 2 ); 162 * // returns <Complex128Array> 163 * 164 * var len = arr.length; 165 * // returns 2 166 */ 167 function Complex128Array() { 168 var byteOffset; 169 var nargs; 170 var buf; 171 var len; 172 173 nargs = arguments.length; 174 if ( !(this instanceof Complex128Array) ) { 175 if ( nargs === 0 ) { 176 return new Complex128Array(); 177 } 178 if ( nargs === 1 ) { 179 return new Complex128Array( arguments[0] ); 180 } 181 if ( nargs === 2 ) { 182 return new Complex128Array( arguments[0], arguments[1] ); 183 } 184 return new Complex128Array( arguments[0], arguments[1], arguments[2] ); 185 } 186 // Create the underlying data buffer... 187 if ( nargs === 0 ) { 188 buf = new Float64Array( 0 ); // backward-compatibility 189 } else if ( nargs === 1 ) { 190 if ( isNonNegativeInteger( arguments[0] ) ) { 191 buf = new Float64Array( arguments[0]*2 ); 192 } else if ( isCollection( arguments[0] ) ) { 193 buf = arguments[ 0 ]; 194 len = buf.length; 195 196 // If provided a "generic" array, peak at the first value, and, if the value is a complex number, try to process as an array of complex numbers, falling back to "normal" typed array initialization if we fail and ensuring consistency if the first value had not been a complex number... 197 if ( len && isArray( buf ) && isComplexLike( buf[0] ) ) { 198 buf = fromArray( new Float64Array( len*2 ), buf ); 199 if ( buf === null ) { 200 // We failed and we are now forced to allocate a new array :-( 201 if ( !isEven( len ) ) { 202 throw new RangeError( 'invalid argument. Array-like object input arguments must have a length which is a multiple of two. Length: `'+len+'`.' ); 203 } 204 // We failed, so fall back to directly setting values... 205 buf = new Float64Array( arguments[0] ); 206 } 207 } else { 208 if ( !isEven( len ) ) { 209 throw new RangeError( 'invalid argument. Array-like object and typed array input arguments must have a length which is a multiple of two. Length: `'+len+'`.' ); 210 } 211 buf = new Float64Array( buf ); 212 } 213 } else if ( isArrayBuffer( arguments[0] ) ) { 214 buf = arguments[ 0 ]; 215 if ( !isInteger( buf.byteLength/BYTES_PER_ELEMENT ) ) { 216 throw new RangeError( 'invalid argument. ArrayBuffer byte length must be a multiple of '+BYTES_PER_ELEMENT+'. Byte length: `'+buf.byteLength+'`.' ); 217 } 218 buf = new Float64Array( buf ); 219 } else if ( isObject( arguments[0] ) ) { 220 buf = arguments[ 0 ]; 221 if ( HAS_ITERATOR_SYMBOL === false ) { 222 throw new TypeError( 'invalid argument. Environment lacks Symbol.iterator support. Must provide a length, ArrayBuffer, typed array, or array-like object. Value: `'+buf+'`.' ); 223 } 224 if ( !isFunction( buf[ ITERATOR_SYMBOL ] ) ) { 225 throw new TypeError( 'invalid argument. Must provide a length, ArrayBuffer, typed array, array-like object, or an iterable. Value: `'+buf+'`.' ); 226 } 227 buf = buf[ ITERATOR_SYMBOL ](); 228 if ( !isFunction( buf.next ) ) { 229 throw new TypeError( 'invalid argument. Must provide a length, ArrayBuffer, typed array, array-like object, or an iterable.' ); 230 } 231 buf = fromIterator( buf ); 232 if ( buf instanceof Error ) { 233 throw buf; 234 } 235 buf = new Float64Array( buf ); 236 } else { 237 throw new TypeError( 'invalid argument. Must provide a length, ArrayBuffer, typed array, array-like object, or an iterable. Value: `'+arguments[0]+'`.' ); 238 } 239 } else { 240 buf = arguments[ 0 ]; 241 if ( !isArrayBuffer( buf ) ) { 242 throw new TypeError( 'invalid argument. First argument must be an array buffer. Value: `'+buf+'`.' ); 243 } 244 byteOffset = arguments[ 1 ]; 245 if ( !isNonNegativeInteger( byteOffset ) ) { 246 throw new TypeError( 'invalid argument. Byte offset must be a nonnegative integer. Value: `'+byteOffset+'`.' ); 247 } 248 if ( !isInteger( byteOffset/BYTES_PER_ELEMENT ) ) { 249 throw new RangeError( 'invalid argument. Byte offset must be a multiple of '+BYTES_PER_ELEMENT+'. Value: `'+byteOffset+'`.' ); 250 } 251 if ( nargs === 2 ) { 252 len = buf.byteLength - byteOffset; 253 if ( !isInteger( len/BYTES_PER_ELEMENT ) ) { 254 throw new RangeError( 'invalid arguments. ArrayBuffer view byte length must be a multiple of '+BYTES_PER_ELEMENT+'. View byte length: `'+len+'`.' ); 255 } 256 buf = new Float64Array( buf, byteOffset ); 257 } else { 258 len = arguments[ 2 ]; 259 if ( !isNonNegativeInteger( len ) ) { 260 throw new TypeError( 'invalid argument. Length must be a nonnegative integer. Value: `'+len+'`.' ); 261 } 262 if ( (len*BYTES_PER_ELEMENT) > (buf.byteLength-byteOffset) ) { 263 throw new RangeError( 'invalid arguments. ArrayBuffer has insufficient capacity. Either decrease the array length or provide a bigger buffer. Minimum capacity: `'+(len*BYTES_PER_ELEMENT)+'`.' ); 264 } 265 buf = new Float64Array( buf, byteOffset, len*2 ); 266 } 267 } 268 defineProperty( this, '_buffer', { 269 'configurable': false, 270 'enumerable': false, 271 'writable': false, 272 'value': buf 273 }); 274 defineProperty( this, '_length', { 275 'configurable': false, 276 'enumerable': false, 277 'writable': false, 278 'value': buf.length / 2 279 }); 280 281 return this; 282 } 283 284 /** 285 * Size (in bytes) of each array element. 286 * 287 * @name BYTES_PER_ELEMENT 288 * @memberof Complex128Array 289 * @type {PositiveInteger} 290 * @default 16 291 * 292 * @example 293 * var nbytes = Complex128Array.BYTES_PER_ELEMENT; 294 * // returns 16 295 */ 296 defineProperty( Complex128Array, 'BYTES_PER_ELEMENT', { 297 'configurable': false, 298 'enumerable': false, 299 'writable': false, 300 'value': BYTES_PER_ELEMENT 301 }); 302 303 /** 304 * Constructor name. 305 * 306 * @name name 307 * @memberof Complex128Array 308 * @type {string} 309 * @default 'Complex128Array' 310 * 311 * @example 312 * var name = Complex128Array.name; 313 * // returns 'Complex128Array' 314 */ 315 defineProperty( Complex128Array, 'name', { 316 'configurable': false, 317 'enumerable': false, 318 'writable': false, 319 'value': 'Complex128Array' 320 }); 321 322 /** 323 * Creates a new 128-bit complex number array from an array-like object or an iterable. 324 * 325 * @name from 326 * @memberof Complex128Array 327 * @type {Function} 328 * @param {(ArrayLikeObject|Object)} src - array-like object or iterable 329 * @param {Function} [clbk] - callback to invoke for each source element 330 * @param {*} [thisArg] - context 331 * @throws {TypeError} `this` context must be a constructor 332 * @throws {TypeError} `this` must be a complex number array 333 * @throws {TypeError} first argument must be an array-like object or an iterable 334 * @throws {TypeError} second argument must be a function 335 * @throws {RangeError} array-like objects must have a length which is a multiple of two 336 * @throws {TypeError} an iterator must return either a two element array containing real and imaginary components or a complex number 337 * @throws {TypeError} when provided an iterator, a callback must return either a two element array containing real and imaginary components or a complex number 338 * @returns {Complex128Array} 128-bit complex number array 339 * 340 * @example 341 * var arr = Complex128Array.from( [ 1.0, -1.0 ] ); 342 * // returns <Complex128Array> 343 * 344 * var len = arr.length; 345 * // returns 1 346 * 347 * @example 348 * var Complex128 = require( '@stdlib/complex/float64' ); 349 * 350 * var arr = Complex128Array.from( [ new Complex128( 1.0, 1.0 ) ] ); 351 * // returns <Complex128Array> 352 * 353 * var len = arr.length; 354 * // returns 1 355 * 356 * @example 357 * var Complex128 = require( '@stdlib/complex/float64' ); 358 * var real = require( '@stdlib/complex/real' ); 359 * var imag = require( '@stdlib/complex/imag' ); 360 * 361 * function clbk( v ) { 362 * return new Complex128( real(v)*2.0, imag(v)*2.0 ); 363 * } 364 * 365 * var arr = Complex128Array.from( [ new Complex128( 1.0, 1.0 ) ], clbk ); 366 * // returns <Complex128Array> 367 * 368 * var len = arr.length; 369 * // returns 1 370 */ 371 defineProperty( Complex128Array, 'from', { 372 'configurable': false, 373 'enumerable': false, 374 'writable': false, 375 'value': function from( src ) { 376 var thisArg; 377 var nargs; 378 var clbk; 379 var out; 380 var buf; 381 var tmp; 382 var len; 383 var flg; 384 var v; 385 var i; 386 var j; 387 if ( !isFunction( this ) ) { 388 throw new TypeError( 'invalid invocation. `this` context must be a constructor.' ); 389 } 390 if ( !isComplexArrayConstructor( this ) ) { 391 throw new TypeError( 'invalid invocation. `this` is not a complex number array.' ); 392 } 393 nargs = arguments.length; 394 if ( nargs > 1 ) { 395 clbk = arguments[ 1 ]; 396 if ( !isFunction( clbk ) ) { 397 throw new TypeError( 'invalid argument. Second argument must be a function. Value: `'+clbk+'`.' ); 398 } 399 if ( nargs > 2 ) { 400 thisArg = arguments[ 2 ]; 401 } 402 } 403 if ( isCollection( src ) ) { 404 if ( clbk ) { 405 // Note: array contents affect how we iterate over a provided data source. If only complex numbers, we can extract real and imaginary components. Otherwise, we assume a strided array where real and imaginary components are interleaved. In the former case, we expect a callback to return real and imaginary components (possibly as a complex number). In the latter case, we expect a callback to return *either* a real or imaginary component. 406 407 // Detect whether we've been provided an array of complex numbers... 408 len = src.length; 409 for ( i = 0; i < len; i++ ) { 410 if ( !isComplexLike( src[ i ] ) ) { 411 flg = true; 412 break; 413 } 414 } 415 // If an array does not contain only complex numbers, then we assume interleaved real and imaginary components... 416 if ( flg ) { 417 if ( !isEven( len ) ) { 418 throw new RangeError( 'invalid argument. First argument must have a length which is a multiple of two. Length: `'+len+'`.' ); 419 } 420 out = new this( len/2 ); 421 buf = out._buffer; // eslint-disable-line no-underscore-dangle 422 for ( i = 0; i < len; i++ ) { 423 buf[ i ] = clbk.call( thisArg, src[ i ], i ); 424 } 425 } 426 // If an array contains only complex numbers, then we need to extract real and imaginary components... 427 else { 428 out = new this( len ); 429 buf = out._buffer; // eslint-disable-line no-underscore-dangle 430 j = 0; 431 for ( i = 0; i < len; i++ ) { 432 v = clbk.call( thisArg, src[ i ], i ); 433 if ( isComplexLike( v ) ) { 434 buf[ j ] = real( v ); 435 buf[ j+1 ] = imag( v ); 436 } else if ( isArrayLikeObject( v ) ) { 437 buf[ j ] = v[ 0 ]; 438 buf[ j+1 ] = v[ 1 ]; 439 } else { 440 throw new TypeError( 'invalid argument. Callback must return either a two-element array containing real and imaginary components or a complex number. Value: `'+v+'`.' ); 441 } 442 j += 2; // stride 443 } 444 } 445 } else { 446 out = new this( src ); 447 } 448 } else if ( isObject( src ) && HAS_ITERATOR_SYMBOL && isFunction( src[ ITERATOR_SYMBOL ] ) ) { // eslint-disable-line max-len 449 buf = src[ ITERATOR_SYMBOL ](); 450 if ( !isFunction( buf.next ) ) { 451 throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable.' ); 452 } 453 if ( clbk ) { 454 tmp = fromIteratorMap( buf, clbk, thisArg ); 455 } else { 456 tmp = fromIterator( buf ); 457 } 458 if ( tmp instanceof Error ) { 459 throw tmp; 460 } 461 len = tmp.length / 2; 462 out = new this( len ); 463 buf = out._buffer; // eslint-disable-line no-underscore-dangle 464 for ( i = 0; i < len; i++ ) { 465 buf[ i ] = tmp[ i ]; 466 } 467 } else { 468 throw new TypeError( 'invalid argument. First argument must be an array-like object or an iterable. Value: `'+src+'`.' ); 469 } 470 return out; 471 } 472 }); 473 474 /** 475 * Creates a new 128-bit complex number array from a variable number of arguments. 476 * 477 * @name of 478 * @memberof Complex128Array 479 * @type {Function} 480 * @param {...*} element - array elements 481 * @throws {TypeError} `this` context must be a constructor 482 * @throws {TypeError} `this` must be a complex number array 483 * @returns {Complex128Array} 128-bit complex number array 484 * 485 * @example 486 * var arr = Complex128Array.of( 1.0, 1.0, 1.0, 1.0 ); 487 * // returns <Complex128Array> 488 * 489 * var len = arr.length; 490 * // returns 2 491 */ 492 defineProperty( Complex128Array, 'of', { 493 'configurable': false, 494 'enumerable': false, 495 'writable': false, 496 'value': function of() { 497 var args; 498 var i; 499 if ( !isFunction( this ) ) { 500 throw new TypeError( 'invalid invocation. `this` context must be a constructor.' ); 501 } 502 if ( !isComplexArrayConstructor( this ) ) { 503 throw new TypeError( 'invalid invocation. `this` is not a complex number array.' ); 504 } 505 args = []; 506 for ( i = 0; i < arguments.length; i++ ) { 507 args.push( arguments[ i ] ); 508 } 509 return new this( args ); 510 } 511 }); 512 513 /** 514 * Pointer to the underlying data buffer. 515 * 516 * @name buffer 517 * @memberof Complex128Array.prototype 518 * @type {ArrayBuffer} 519 * 520 * @example 521 * var arr = new Complex128Array( 10 ); 522 * 523 * var buf = arr.buffer; 524 * // returns <ArrayBuffer> 525 */ 526 defineProperty( Complex128Array.prototype, 'buffer', { 527 'configurable': false, 528 'enumerable': false, 529 'get': function get() { 530 return this._buffer.buffer; 531 } 532 }); 533 534 /** 535 * Size (in bytes) of the array. 536 * 537 * @name byteLength 538 * @memberof Complex128Array.prototype 539 * @type {NonNegativeInteger} 540 * 541 * @example 542 * var arr = new Complex128Array( 10 ); 543 * 544 * var byteLength = arr.byteLength; 545 * // returns 160 546 */ 547 defineProperty( Complex128Array.prototype, 'byteLength', { 548 'configurable': false, 549 'enumerable': false, 550 'get': function get() { 551 return this._buffer.byteLength; 552 } 553 }); 554 555 /** 556 * Offset (in bytes) of the array from the start of its underlying `ArrayBuffer`. 557 * 558 * @name byteOffset 559 * @memberof Complex128Array.prototype 560 * @type {NonNegativeInteger} 561 * 562 * @example 563 * var arr = new Complex128Array( 10 ); 564 * 565 * var byteOffset = arr.byteOffset; 566 * // returns 0 567 */ 568 defineProperty( Complex128Array.prototype, 'byteOffset', { 569 'configurable': false, 570 'enumerable': false, 571 'get': function get() { 572 return this._buffer.byteOffset; 573 } 574 }); 575 576 /** 577 * Size (in bytes) of each array element. 578 * 579 * @name BYTES_PER_ELEMENT 580 * @memberof Complex128Array.prototype 581 * @type {PositiveInteger} 582 * @default 16 583 * 584 * @example 585 * var arr = new Complex128Array( 10 ); 586 * 587 * var nbytes = arr.BYTES_PER_ELEMENT; 588 * // returns 16 589 */ 590 defineProperty( Complex128Array.prototype, 'BYTES_PER_ELEMENT', { 591 'configurable': false, 592 'enumerable': false, 593 'writable': false, 594 'value': Complex128Array.BYTES_PER_ELEMENT 595 }); 596 597 /** 598 * Returns an array element. 599 * 600 * @name get 601 * @memberof Complex128Array.prototype 602 * @type {Function} 603 * @param {ArrayLikeObject} [out] - output array 604 * @param {NonNegativeInteger} i - element index 605 * @throws {TypeError} `this` must be a complex number array 606 * @throws {TypeError} index argument must be a nonnegative integer 607 * @throws {TypeError} output argument must be an array-like object 608 * @returns {(Complex128|ArrayLikeObject|void)} array element 609 * 610 * @example 611 * var arr = new Complex128Array( 10 ); 612 * 613 * var z = arr.get( 0 ); 614 * // returns <Complex128> 615 * 616 * arr.set( [ 1.0, -1.0 ], 0 ); 617 * 618 * z = arr.get( [ 0.0, 0.0 ], 0 ); 619 * // returns [ 1.0, -1.0 ] 620 * 621 * z = arr.get( 100 ); 622 * // returns undefined 623 */ 624 defineProperty( Complex128Array.prototype, 'get', { 625 'configurable': false, 626 'enumerable': false, 627 'writable': false, 628 'value': function get( i ) { 629 var idx; 630 var out; 631 var buf; 632 633 if ( !isComplexArray( this ) ) { 634 throw new TypeError( 'invalid invocation. `this` is not a complex number array.' ); 635 } 636 buf = this._buffer; 637 if ( arguments.length > 1 ) { 638 idx = arguments[ 1 ]; 639 out = i; 640 if ( !isArrayLikeObject( out ) || out.length < 2 ) { 641 throw new TypeError( 'invalid argument. Output argument must be an array-like object. Value: `'+out+'`.' ); 642 } 643 } else { 644 idx = i; 645 } 646 if ( !isNonNegativeInteger( idx ) ) { 647 throw new TypeError( 'invalid argument. Index argument must be a nonnegative integer. Value: `'+idx+'`.' ); 648 } 649 if ( idx >= this._length ) { 650 return; 651 } 652 idx *= 2; 653 if ( out ) { 654 out[ 0 ] = buf[ idx ]; 655 out[ 1 ] = buf[ idx+1 ]; 656 return out; 657 } 658 return new Complex128( buf[ idx ], buf[ idx+1 ] ); 659 } 660 }); 661 662 /** 663 * Number of array elements. 664 * 665 * @name length 666 * @memberof Complex128Array.prototype 667 * @type {NonNegativeInteger} 668 * 669 * @example 670 * var arr = new Complex128Array( 10 ); 671 * 672 * var len = arr.length; 673 * // returns 10 674 */ 675 defineProperty( Complex128Array.prototype, 'length', { 676 'configurable': false, 677 'enumerable': false, 678 'get': function get() { 679 return this._length; 680 } 681 }); 682 683 /** 684 * Sets an array element. 685 * 686 * ## Notes 687 * 688 * - When provided a typed array, real or complex, we must check whether the source array shares the same buffer as the target array and whether the underlying memory overlaps. In particular, we are concerned with the following scenario: 689 * 690 * ```text 691 * buf: --------------------- 692 * src: --------------------- 693 * ``` 694 * 695 * In the above, as we copy values from `src`, we will overwrite values in the `src` view, resulting in duplicated values copied into the end of `buf`, which is not intended. Hence, to avoid overwriting source values, we must **copy** source values to a temporary array. 696 * 697 * In the other overlapping scenario, 698 * 699 * ```text 700 * buf: --------------------- 701 * src: --------------------- 702 * ``` 703 * 704 * by the time we begin copying into the overlapping region, we are copying from the end of `src`, a non-overlapping region, which means we don't run the risk of copying copied values, rather than the original `src` values as intended. 705 * 706 * 707 * @name set 708 * @memberof Complex128Array.prototype 709 * @type {Function} 710 * @param {(Collection|Complex|ComplexArray)} value - value(s) 711 * @param {NonNegativeInteger} [i=0] - element index at which to start writing values 712 * @throws {TypeError} `this` must be a complex number array 713 * @throws {TypeError} first argument must be either a complex number, an array-like object, or a complex number array 714 * @throws {TypeError} index argument must be a nonnegative integer 715 * @throws {RangeError} array-like objects must have a length which is a multiple of two 716 * @throws {RangeError} index argument is out-of-bounds 717 * @throws {RangeError} target array lacks sufficient storage to accommodate source values 718 * @returns {void} 719 * 720 * @example 721 * var real = require( '@stdlib/complex/real' ); 722 * var imag = require( '@stdlib/complex/imag' ); 723 * 724 * var arr = new Complex128Array( 10 ); 725 * 726 * var z = arr.get( 0 ); 727 * // returns <Complex128> 728 * 729 * var re = real( z ); 730 * // returns 0.0 731 * 732 * var im = imag( z ); 733 * // returns 0.0 734 * 735 * arr.set( [ 1.0, -1.0 ], 0 ); 736 * 737 * z = arr.get( 0 ); 738 * // returns <Complex128> 739 * 740 * re = real( z ); 741 * // returns 1.0 742 * 743 * im = imag( z ); 744 * // returns -1.0 745 */ 746 defineProperty( Complex128Array.prototype, 'set', { 747 'configurable': false, 748 'enumerable': false, 749 'writable': false, 750 'value': function set( value ) { 751 /* eslint-disable no-underscore-dangle */ 752 var dbuf; 753 var idx; 754 var buf; 755 var tmp; 756 var flg; 757 var N; 758 var v; 759 var i; 760 var j; 761 if ( !isComplexArray( this ) ) { 762 throw new TypeError( 'invalid invocation. `this` is not a complex number array.' ); 763 } 764 buf = this._buffer; 765 if ( arguments.length > 1 ) { 766 idx = arguments[ 1 ]; 767 if ( !isNonNegativeInteger( idx ) ) { 768 throw new TypeError( 'invalid argument. Index argument must be a nonnegative integer. Value: `'+idx+'`.' ); 769 } 770 } else { 771 idx = 0; 772 } 773 if ( isComplexLike( value ) ) { 774 if ( idx >= this._length ) { 775 throw new RangeError( 'invalid argument. Index argument is out-of-bounds. Value: `'+idx+'`.' ); 776 } 777 idx *= 2; 778 buf[ idx ] = real( value ); 779 buf[ idx+1 ] = imag( value ); 780 return; 781 } 782 if ( isComplexArray( value ) ) { 783 N = value._length; 784 if ( idx+N > this._length ) { 785 throw new RangeError( 'invalid arguments. Target array lacks sufficient storage to accommodate source values.' ); 786 } 787 dbuf = value._buffer; 788 789 // Check for overlapping memory... 790 j = buf.byteOffset + (idx*BYTES_PER_ELEMENT); 791 if ( 792 dbuf.buffer === buf.buffer && 793 ( 794 dbuf.byteOffset < j && 795 dbuf.byteOffset+dbuf.byteLength > j 796 ) 797 ) { 798 // We need to copy source values... 799 tmp = new Float64Array( dbuf.length ); 800 for ( i = 0; i < dbuf.length; i++ ) { 801 tmp[ i ] = dbuf[ i ]; 802 } 803 dbuf = tmp; 804 } 805 idx *= 2; 806 j = 0; 807 for ( i = 0; i < N; i++ ) { 808 buf[ idx ] = dbuf[ j ]; 809 buf[ idx+1 ] = dbuf[ j+1 ]; 810 idx += 2; // stride 811 j += 2; // stride 812 } 813 return; 814 } 815 if ( isCollection( value ) ) { 816 // Detect whether we've been provided an array of complex numbers... 817 N = value.length; 818 for ( i = 0; i < N; i++ ) { 819 if ( !isComplexLike( value[ i ] ) ) { 820 flg = true; 821 break; 822 } 823 } 824 // If an array does not contain only complex numbers, then we assume interleaved real and imaginary components... 825 if ( flg ) { 826 if ( !isEven( N ) ) { 827 throw new RangeError( 'invalid argument. Array-like object arguments must have a length which is a multiple of two. Length: `'+N+'`.' ); 828 } 829 if ( idx+(N/2) > this._length ) { 830 throw new RangeError( 'invalid arguments. Target array lacks sufficient storage to accommodate source values.' ); 831 } 832 dbuf = value; 833 834 // Check for overlapping memory... 835 j = buf.byteOffset + (idx*BYTES_PER_ELEMENT); 836 if ( 837 dbuf.buffer === buf.buffer && 838 ( 839 dbuf.byteOffset < j && 840 dbuf.byteOffset+dbuf.byteLength > j 841 ) 842 ) { 843 // We need to copy source values... 844 tmp = new Float64Array( N ); 845 for ( i = 0; i < N; i++ ) { 846 tmp[ i ] = dbuf[ i ]; 847 } 848 dbuf = tmp; 849 } 850 idx *= 2; 851 N /= 2; 852 j = 0; 853 for ( i = 0; i < N; i++ ) { 854 buf[ idx ] = dbuf[ j ]; 855 buf[ idx+1 ] = dbuf[ j+1 ]; 856 idx += 2; // stride 857 j += 2; // stride 858 } 859 return; 860 } 861 // If an array contains only complex numbers, then we need to extract real and imaginary components... 862 if ( idx+N > this._length ) { 863 throw new RangeError( 'invalid arguments. Target array lacks sufficient storage to accommodate source values.' ); 864 } 865 idx *= 2; 866 for ( i = 0; i < N; i++ ) { 867 v = value[ i ]; 868 buf[ idx ] = real( v ); 869 buf[ idx+1 ] = imag( v ); 870 idx += 2; // stride 871 } 872 return; 873 } 874 throw new TypeError( 'invalid argument. First argument must be either a complex number, an array-like object, or a complex number array. Value: `'+value+'`.' ); 875 876 /* eslint-enable no-underscore-dangle */ 877 } 878 }); 879 880 881 // EXPORTS // 882 883 module.exports = Complex128Array;