main.js (7687B)
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 'use strict'; 20 21 // MODULES // 22 23 var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive; 24 var isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ); 25 var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive; 26 var isFunctionArray = require( '@stdlib/assert/is-function-array' ); 27 var isFunction = require( '@stdlib/assert/is-function' ); 28 var isStringArray = require( '@stdlib/assert/is-string-array' ).primitives; 29 var isCollection = require( '@stdlib/assert/is-collection' ); 30 var isndarrayLike = require( '@stdlib/assert/is-ndarray-like' ); 31 var indexOfTypes = require( './index_of_types.js' ); 32 33 34 // MAIN // 35 36 /** 37 * Returns an ndarray function interface which performs multiple dispatch. 38 * 39 * @param {(FunctionArray|Function)} fcns - list of ndarray functions 40 * @param {StringArray} types - one-dimensional list of ndarray argument data types 41 * @param {(Collection|null)} data - ndarray function data (e.g., callbacks) 42 * @param {PositiveInteger} nargs - total number of ndarray function interface arguments 43 * @param {NonNegativeInteger} nin - number of input ndarrays 44 * @param {NonNegativeInteger} nout - number of output ndarrays 45 * @throws {TypeError} first argument must be either a function or an array of functions 46 * @throws {TypeError} second argument must be an array of strings 47 * @throws {TypeError} third argument must be an array-like object or `null` 48 * @throws {Error} third and first arguments must have the same number of elements 49 * @throws {TypeError} fourth argument must be a positive integer 50 * @throws {TypeError} fifth argument must be a nonnegative integer 51 * @throws {TypeError} sixth argument must be a nonnegative integer 52 * @throws {Error} fourth argument must equal the specified number of input and output arrays 53 * @throws {Error} number of types must match the number of functions times the total number of array arguments for each function 54 * @throws {Error} interface must accept at least one input and/or output ndarray 55 * @returns {Function} ndarray function interface 56 * 57 * @example 58 * var unary = require( '@stdlib/ndarray/base/unary' ); 59 * var abs = require( '@stdlib/math/base/special/abs' ); 60 * var Float64Array = require( '@stdlib/array/float64' ); 61 * var ndarray = require( '@stdlib/ndarray/ctor' ); 62 * 63 * var types = [ 64 * 'float64', 'float64' 65 * ]; 66 * 67 * var data = [ 68 * abs 69 * ]; 70 * 71 * var absolute = dispatch( unary, types, data, 2, 1, 1 ); 72 * 73 * // ... 74 * 75 * var xbuf = new Float64Array( [ -1.0, -2.0, -3.0, -4.0, -5.0 ] ); 76 * var ybuf = new Float64Array( [ 0.0, 0.0, 0.0, 0.0, 0.0 ] ); 77 * 78 * var x = ndarray( 'float64', xbuf, [ 5 ], [ 1 ], 0, 'row-major' ); 79 * var y = ndarray( 'float64', ybuf, [ 5 ], [ 1 ], 0, 'row-major' ); 80 * 81 * absolute( x, y ); 82 * // ybuf => <Float64Array>[ 1.0, 2.0, 3.0, 4.0, 5.0 ] 83 */ 84 function dispatch( fcns, types, data, nargs, nin, nout ) { 85 var narrays; 86 var nfcns; 87 var fcn; 88 89 if ( isFunction( fcns ) ) { 90 fcn = fcns; 91 } else if ( !isFunctionArray( fcns ) ) { 92 throw new TypeError( 'invalid argument. First argument must be either a function or an array of functions. Value: `' + fcns + '`.' ); 93 } 94 if ( !isStringArray( types ) ) { 95 throw new TypeError( 'invalid argument. Second argument must be an array of strings. Value: `' + types + '`.' ); 96 } 97 if ( !isCollection( data ) && data !== null ) { 98 throw new TypeError( 'invalid argument. Third argument must be an array-like object or `null`. Value: `' + data + '`.' ); 99 } 100 if ( !isPositiveInteger( nargs ) ) { 101 throw new TypeError( 'invalid argument. Fourth argument must be a positive integer. Value: `' + nargs + '`.' ); 102 } 103 if ( !isNonNegativeInteger( nin ) ) { 104 throw new TypeError( 'invalid argument. Fifth argument must be a nonnegative integer. Value: `' + nin + '`.' ); 105 } 106 if ( !isNonNegativeInteger( nout ) ) { 107 throw new TypeError( 'invalid argument. Sixth argument must be a nonnegative integer. Value: `' + nout + '`.' ); 108 } 109 narrays = nin + nout; 110 if ( narrays === 0 ) { 111 throw new Error( 'invalid arguments. Interface must accept at least one input and/or output ndarray. Based on the provided arguments, `nin+nout` equals `0`.' ); 112 } 113 if ( nargs !== narrays ) { 114 throw new Error( 'invalid arguments. Fourth argument does not equal the number of input and output ndarrays.' ); 115 } 116 if ( fcn ) { 117 nfcns = types.length / narrays; 118 if ( !isInteger( nfcns ) ) { 119 throw new Error( 'invalid argument. Unexpected number of types. A type must be specified for each input and output ndarray for each provided ndarray function.' ); 120 } 121 } else { 122 nfcns = fcns.length; 123 if ( types.length !== nfcns*narrays ) { 124 throw new Error( 'invalid argument. Unexpected number of types. A type must be specified for each input and output ndarray for each provided ndarray function.' ); 125 } 126 } 127 if ( data && data.length !== nfcns ) { 128 throw new Error( 'invalid argument. The third argument must have the same number of elements as the first argument.' ); 129 } 130 return dispatcher; 131 132 /** 133 * ndarray function interface which performs multiple dispatch. 134 * 135 * @private 136 * @param {ndarrayLike} x - ndarray 137 * @param {...ndarrayLike} args - ndarray arguments 138 * @throws {Error} insufficient arguments 139 * @throws {Error} too many arguments 140 * @throws {TypeError} input array arguments must be ndarray-like objects 141 * @throws {TypeError} output array arguments must be ndarray-like objects 142 * @throws {TypeError} unable to resolve an ndarray function supporting the provided ndarray argument data types 143 * @returns {(ndarrayLike|Array<ndarrayLike>|void)} destination array(s) 144 */ 145 function dispatcher() { 146 var arrays; 147 var dtypes; 148 var argc; 149 var idx; 150 var v; 151 var f; 152 var i; 153 154 argc = arguments.length; 155 if ( argc !== nargs ) { 156 if ( argc < nargs ) { 157 throw new Error( 'invalid invocation. Insufficient arguments.' ); 158 } 159 throw new Error( 'invalid invocation. Too many arguments.' ); 160 } 161 arrays = []; 162 dtypes = []; 163 for ( i = 0; i < nargs; i++ ) { 164 v = arguments[ i ]; 165 if ( !isndarrayLike( v ) ) { 166 if ( i < nin ) { 167 throw new TypeError( 'invalid argument. Input array argument must be an ndarray-like object.' ); 168 } else { 169 throw new TypeError( 'invalid argument. Output array argument must be an ndarray-like object.' ); 170 } 171 } 172 arrays.push( v ); 173 dtypes.push( v.dtype ); 174 } 175 // Resolve the ndarray function satisfying the input array types: 176 idx = indexOfTypes( nfcns, narrays, types, narrays, 1, 0, dtypes, 1, 0 ); // eslint-disable-line max-len 177 178 // Check whether we were able to successfully resolve an ndarray function: 179 if ( idx < 0 ) { 180 throw new TypeError( 'invalid arguments. Unable to resolve an ndarray function supporting the provided array argument data types.' ); 181 } 182 // Retrieve the ndarray function: 183 if ( fcn ) { 184 f = fcn; 185 } else { 186 f = fcns[ idx ]; 187 } 188 // Evaluate the ndarray function: 189 if ( data ) { 190 f( arrays, data[ idx ] ); 191 } else { 192 f( arrays ); 193 } 194 if ( nout === 1 ) { 195 return arrays[ narrays-1 ]; 196 } 197 if ( nout === 0 ) { 198 return; 199 } 200 return arrays.slice( nin ); 201 } 202 } 203 204 205 // EXPORTS // 206 207 module.exports = dispatch;