main.js (10877B)
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-invalid-this */ 20 21 'use strict'; 22 23 // MODULES // 24 25 var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); 26 var isCollection = require( '@stdlib/assert/is-collection' ); 27 var isNonNegativeIntegerArray = require( '@stdlib/assert/is-nonnegative-integer-array' ).primitives; 28 var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive; 29 var isIntegerArray = require( '@stdlib/assert/is-integer-array' ).primitives; 30 var isFunction = require( '@stdlib/assert/is-function' ); 31 var isOrder = require( './../../base/assert/is-order' ); 32 var isDataType = require( './../../base/assert/is-data-type' ); 33 var isBufferLengthCompatible = require( './../../base/assert/is-buffer-length-compatible' ); 34 var parent = require( './../../base/ctor' ); // eslint-disable-line stdlib/no-redeclare 35 var inherit = require( '@stdlib/utils/inherit' ); 36 var defaults = require( './defaults.json' ); 37 var iget = require( './iget.js' ); 38 var iset = require( './iset.js' ); 39 var get = require( './get.js' ); 40 var set = require( './set.js' ); 41 var copy = require( './copy_array.js' ); 42 var validate = require( './validate.js' ); 43 44 45 // VARIABLES // 46 47 /* 48 * See the following references: 49 * 50 * - https://stackoverflow.com/questions/22747068/is-there-a-max-number-of-arguments-javascript-functions-can-accept 51 * - https://bugs.webkit.org/show_bug.cgi?id=80797 52 * - https://github.com/numpy/numpy/issues/5744 53 * 54 * Note that the maximum number of function arguments can vary from engine to engine. Here, we choose something of a lowest common denominator which may **not** be valid everywhere. 55 */ 56 var MAX_DIMS = 32767|0; 57 58 59 // MAIN // 60 61 /** 62 * ndarray constructor. 63 * 64 * @constructor 65 * @param {string} dtype - data type 66 * @param {Collection} buffer - data buffer 67 * @param {NonNegativeIntegerArray} shape - array shape 68 * @param {IntegerArray} strides - array strides 69 * @param {NonNegativeInteger} offset - index offset 70 * @param {string} order - specifies whether an array is row-major (C-style) or column-major (Fortran-style) 71 * @param {Options} [options] - function options 72 * @param {string} [options.mode="throw"] - specifies how to handle indices which exceed array dimensions 73 * @param {StringArray} [options.submode=["throw"]] - specifies how to handle subscripts which exceed array dimensions on a per dimension basis 74 * @throws {TypeError} `dtype` argument must be a supported ndarray data type 75 * @throws {TypeError} `buffer` argument must be an array-like object, typed-array-like, or a Buffer 76 * @throws {TypeError} `buffer` argument `get` and `set` properties must be functions 77 * @throws {TypeError} `shape` argument must be an array-like object containing nonnegative integers 78 * @throws {Error} `shape` argument length must equal the number of dimensions 79 * @throws {TypeError} `strides` argument must be an array-like object containing integers 80 * @throws {Error} `strides` argument length must equal the number of dimensions (except for zero-dimensional arrays; in which case, the `strides` argument length must be equal to `1`) 81 * @throws {Error} for zero-dimensional ndarrays, the `strides` argument must contain a single element equal to `0` 82 * @throws {TypeError} `offset` argument must be a nonnegative integer 83 * @throws {TypeError} `order` argument must be a supported ndarray order 84 * @throws {Error} `buffer` argument must be compatible with specified meta data 85 * @throws {TypeError} options argument must be an object 86 * @throws {TypeError} must provide valid options 87 * @throws {RangeError} too many dimensions 88 * @returns {ndarray} ndarray instance 89 * 90 * @example 91 * var buffer = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ]; 92 * var shape = [ 3, 2 ]; 93 * var strides = [ 2, 1 ]; 94 * var offset = 0; 95 * 96 * var out = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); 97 */ 98 function ndarray( dtype, buffer, shape, strides, offset, order, options ) { 99 var ndims; 100 var opts; 101 var err; 102 var sh; 103 var st; 104 105 if ( !(this instanceof ndarray) ) { 106 if ( arguments.length < 7 ) { 107 return new ndarray( dtype, buffer, shape, strides, offset, order ); 108 } 109 return new ndarray( dtype, buffer, shape, strides, offset, order, options ); // eslint-disable-line max-len 110 } 111 if ( !isDataType( dtype ) ) { 112 throw new TypeError( 'invalid argument. `dtype` argument must be a supported ndarray data type. Value: `' + dtype + '`.' ); 113 } 114 if ( !isCollection( buffer ) ) { 115 throw new TypeError( 'invalid argument. `buffer` argument must be an array-like object, typed-array-like, or a Buffer. Value: `' + buffer + '`.' ); 116 } else if ( buffer.get && buffer.set && ( !isFunction( buffer.get ) || !isFunction( buffer.set ) ) ) { // eslint-disable-line max-len 117 throw new TypeError( 'invalid argument. `buffer` argument `get` and `set` properties must be functions. Value: `' + buffer + '`.' ); 118 } 119 if ( !isNonNegativeIntegerArray( shape ) ) { 120 if ( !isCollection( shape) || shape.length > 0 ) { 121 throw new TypeError( 'invalid argument. `shape` argument must be an array-like object containing nonnegative integers. Value: `' + shape + '`.' ); 122 } 123 } 124 ndims = shape.length; 125 if ( ndims > MAX_DIMS ) { 126 throw new RangeError( 'invalid argument. Number of dimensions must not exceed ' + MAX_DIMS + ' due to stack limits. Value: `' + ndims + '`.' ); 127 } 128 if ( !isIntegerArray( strides ) ) { 129 throw new TypeError( 'invalid argument. `strides` argument must be an array-like object containing integers. Value: `' + strides + '`.' ); 130 } 131 if ( ndims > 0 ) { 132 if ( strides.length !== ndims ) { 133 throw new RangeError( 'invalid argument. `strides` argument length must match the number of dimensions. Expected number of dimensions: ' + ndims + '. Strides length: ' + strides.length + '.' ); 134 } 135 } else if ( strides.length !== 1 ) { 136 throw new RangeError( 'invalid argument. `strides` length must be equal to 1 when creating a zero-dimensional ndarray.' ); 137 } else if ( strides[ 0 ] !== 0 ) { 138 throw new RangeError( 'invalid argument. `strides` argument must contain a single element equal to `0`. Value: `' + strides[ 0 ] + '`.' ); 139 } 140 if ( !isNonNegativeInteger( offset ) ) { 141 throw new TypeError( 'invalid argument. `offset` argument must be a nonnegative integer. Value: `' + offset + '`.' ); 142 } 143 if ( !isOrder( order ) ) { 144 throw new TypeError( 'invalid argument. `order` argument must be a supported order. Value: `' + order + '`.' ); 145 } 146 if ( !isBufferLengthCompatible( buffer.length, shape, strides, offset ) ) { 147 throw new Error( 'invalid arguments. The input buffer is incompatible with the specified meta data. Ensure that the offset is valid with regard to the strides array and that the buffer has enough elements to satisfy the desired array shape.' ); 148 } 149 opts = {}; 150 opts.mode = defaults.mode; 151 if ( arguments.length > 6 ) { 152 err = validate( opts, options ); 153 if ( err ) { 154 throw err; 155 } 156 } 157 this._mode = opts.mode; 158 if ( opts.submode === void 0 ) { 159 opts.submode = [ this._mode ]; 160 } 161 this._submode = opts.submode; 162 163 // Copy `shape` and `strides` to prevent external mutation: 164 sh = copy( shape, ndims ); 165 st = copy( strides, ndims || 1 ); 166 167 // Call the parent constructor: 168 parent.call( this, dtype, buffer, sh, st, offset, order ); 169 170 return this; 171 172 /* eslint-enable no-invalid-this */ 173 } 174 175 // Inherit from the parent constructor: 176 inherit( ndarray, parent ); 177 178 /** 179 * Constructor name. 180 * 181 * @name name 182 * @memberof ndarray 183 * @type {string} 184 * @default 'ndarray' 185 * 186 * @example 187 * var str = ndarray.name; 188 * // returns 'ndarray' 189 */ 190 setReadOnly( ndarray, 'name', 'ndarray' ); 191 192 /** 193 * Returns an array element. 194 * 195 * ## Notes 196 * 197 * - The number of indices should **equal** the number of dimensions. Accordingly, for zero-dimensional arrays, no indices should be provided. 198 * 199 * @name get 200 * @memberof ndarray.prototype 201 * @type {Function} 202 * @param {...integer} [idx] - indices 203 * @returns {*} array element 204 * 205 * @example 206 * var buffer = [ 1, 2, 3, 4, 5, 6 ]; 207 * var shape = [ 3, 2 ]; 208 * var strides = [ 2, 1 ]; 209 * var offset = 0; 210 * 211 * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); 212 * 213 * var v = x.get( 1, 1 ); 214 * // returns 4 215 */ 216 setReadOnly( ndarray.prototype, 'get', get ); 217 218 /** 219 * Returns an array element located at a specified linear index. 220 * 221 * ## Notes 222 * 223 * - For zero-dimensional arrays, the input argument is ignored and, for clarity, should not be provided. 224 * 225 * @name iget 226 * @memberof ndarray.prototype 227 * @type {Function} 228 * @param {integer} [idx] - linear index 229 * @returns {*} array element 230 * 231 * @example 232 * var buffer = [ 1, 2, 3, 4, 5, 6 ]; 233 * var shape = [ 3, 2 ]; 234 * var strides = [ 2, 1 ]; 235 * var offset = 0; 236 * 237 * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); 238 * 239 * var v = x.iget( 3 ); 240 * // returns 4 241 */ 242 setReadOnly( ndarray.prototype, 'iget', iget ); 243 244 /** 245 * Sets an array element. 246 * 247 * ## Notes 248 * 249 * - The number of indices should **equal** the number of dimensions. Accordingly, for zero-dimensional arrays, no indices should be provided. 250 * 251 * @name set 252 * @memberof ndarray.prototype 253 * @type {Function} 254 * @param {...integer} [idx] - indices 255 * @param {*} v - value to set 256 * @returns {ndarray} ndarray instance 257 * 258 * @example 259 * var buffer = [ 1, 2, 3, 4, 5, 6 ]; 260 * var shape = [ 3, 2 ]; 261 * var strides = [ 2, 1 ]; 262 * var offset = 0; 263 * 264 * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); 265 * 266 * var v = x.get( 1, 1 ); 267 * // returns 4 268 * 269 * x.set( 1, 1, 10 ); 270 * 271 * var b = x.data; 272 * // returns [ 1, 2, 3, 10, 5, 6 ] 273 * 274 * v = x.get( 1, 1 ); 275 * // returns 10 276 */ 277 setReadOnly( ndarray.prototype, 'set', set ); 278 279 /** 280 * Sets an array element located at a specified linear index. 281 * 282 * ## Notes 283 * 284 * - For zero-dimensional arrays, the first, and only, argument should be the value to set. 285 * 286 * @name iset 287 * @memberof ndarray.prototype 288 * @type {Function} 289 * @param {integer} [idx] - linear index 290 * @param {*} v - value to set 291 * @returns {ndarray} ndarray instance 292 * 293 * @example 294 * var buffer = [ 1, 2, 3, 4, 5, 6 ]; 295 * var shape = [ 3, 2 ]; 296 * var strides = [ 2, 1 ]; 297 * var offset = 0; 298 * 299 * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); 300 * 301 * var v = x.iget( 3 ); 302 * // returns 4 303 * 304 * x.iset( 3, 10 ); 305 * 306 * var b = x.data; 307 * // returns [ 1, 2, 3, 10, 5, 6 ] 308 * 309 * v = x.iget( 3 ); 310 * // returns 10 311 */ 312 setReadOnly( ndarray.prototype, 'iset', iset ); 313 314 315 // EXPORTS // 316 317 module.exports = ndarray;