time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

main.js (8498B)


      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 setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' );
     24 var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive;
     25 var isComplexLike = require( '@stdlib/assert/is-complex-like' );
     26 var isndarrayLike = require( '@stdlib/assert/is-ndarray-like' );
     27 var isCollection = require( '@stdlib/assert/is-collection' );
     28 var dtype = require( '@stdlib/ndarray/base/buffer-dtype' );
     29 var buffer = require( '@stdlib/ndarray/base/buffer' );
     30 var broadcast = require( '@stdlib/ndarray/base/broadcast-array' );
     31 var ndarrayfcn = require( './ndarray.js' );
     32 var odtype = require( './resolve_output_dtype.js' );
     33 var defaults = require( './defaults.json' );
     34 var validateTable = require( './validate_table.js' );
     35 var validateOptions = require( './validate_options.js' );
     36 var validate = require( './validate.js' );
     37 
     38 
     39 // MAIN //
     40 
     41 /**
     42 * Returns a function which dispatches to specified functions based on input argument types.
     43 *
     44 * @param {Object} table - resolution table object
     45 * @param {(Function|null)} [table.number] - function to invoke upon receiving a number
     46 * @param {(Function|null)} [table.complex] - function to invoke upon receiving a complex number
     47 * @param {(Function|null)} [table.array] - function to invoke upon receiving an array-like object
     48 * @param {(Function|null)} [table.ndarray] - function to invoke upon receiving an ndarray-like object
     49 * @param {Options} [options] - options
     50 * @param {string} [options.output_dtype_policy='float'] - policy for determining the output array data type
     51 * @throws {TypeError} first argument must be an object
     52 * @throws {TypeError} first argument must have valid table fields
     53 * @throws {Error} each table field value must be either a function or `null`
     54 * @throws {TypeError} options argument must be an object
     55 * @throws {TypeError} must provide valid options
     56 * @returns {Function} dispatch function
     57 *
     58 * @example
     59 * var base = require( '@stdlib/math/base/special/abs' );
     60 * var strided = require( '@stdlib/math/strided/special/abs' );
     61 * var dispatcher = require( '@stdlib/ndarray/dispatch' );
     62 * var unary = require( '@stdlib/ndarray/base/unary' );
     63 * var Float64Array = require( '@stdlib/array/float64' );
     64 *
     65 * var types = [
     66 *     'float64', 'float64',
     67 *     'float32', 'float32',
     68 *     'generic', 'generic'
     69 * ];
     70 * var data = [
     71 *     base,
     72 *     base,
     73 *     base
     74 * ];
     75 * var nd = dispatcher( unary, types, data, 2, 1, 1 );
     76 *
     77 * var table = {
     78 *     'number': base,
     79 *     'complex': null,
     80 *     'array': strided,
     81 *     'ndarray': nd
     82 * };
     83 *
     84 * var abs = dispatch( table, {
     85 *     'output_dtype_policy': 'same'
     86 * });
     87 *
     88 * var x = new Float64Array( [ -1.0, -2.0, -3.0 ] );
     89 *
     90 * var y = abs( x );
     91 * // returns <Float64Array>[ 1.0, 2.0, 3.0 ]
     92 */
     93 function dispatch( table, options ) {
     94 	var OPTS;
     95 	var err;
     96 	var fcn;
     97 	var t;
     98 
     99 	t = {
    100 		'number': null,
    101 		'complex': null,
    102 		'array': null,
    103 		'ndarray': null
    104 	};
    105 	err = validateTable( t, table );
    106 	if ( err ) {
    107 		throw err;
    108 	}
    109 	OPTS = {
    110 		'policy': defaults.output_dtype_policy
    111 	};
    112 	if ( arguments.length > 1 ) {
    113 		err = validateOptions( OPTS, options );
    114 		if ( err ) {
    115 			throw err;
    116 		}
    117 	}
    118 	fcn = dispatcher;
    119 	setReadOnly( fcn, 'assign', assign );
    120 	return fcn;
    121 
    122 	/**
    123 	* Function interface which performs dispatch.
    124 	*
    125 	* @private
    126 	* @param {(ndarray|Collection|number|Complex)} x - input value
    127 	* @param {Options} [options] - options
    128 	* @param {string} [options.dtype] - output array data type
    129 	* @param {string} [options.order] - output array order (row-major or column-major)
    130 	* @throws {TypeError} first argument must be a supported data type
    131 	* @throws {TypeError} options argument must be an object
    132 	* @throws {TypeError} must provide valid options
    133 	* @returns {(ndarray|Collection|number|Complex)} results
    134 	*/
    135 	function dispatcher( x, options ) {
    136 		var xdtype;
    137 		var ydtype;
    138 		var opts;
    139 		var err;
    140 		var y;
    141 		if ( isNumber( x ) ) {
    142 			if ( t.number ) {
    143 				return t.number( x );
    144 			}
    145 			throw new TypeError( 'invalid argument. Providing a number is not supported.' );
    146 		}
    147 		if ( isComplexLike( x ) ) {
    148 			if ( t.complex ) {
    149 				return t.complex( x );
    150 			}
    151 			throw new TypeError( 'invalid argument. Providing a complex number is not supported.' );
    152 		}
    153 		opts = {};
    154 		if ( arguments.length > 1 ) {
    155 			err = validate( opts, options );
    156 			if ( err ) {
    157 				throw err;
    158 			}
    159 		}
    160 		if ( isndarrayLike( x ) ) {
    161 			if ( t.ndarray === null ) {
    162 				throw new TypeError( 'invalid argument. Providing an ndarray is not supported.' );
    163 			}
    164 			ydtype = opts.dtype || odtype( x.dtype, OPTS.policy );
    165 			return ndarrayfcn( t.ndarray, x, ydtype, opts.order || x.order );
    166 		}
    167 		if ( isCollection( x ) ) {
    168 			if ( t.array === null ) {
    169 				throw new TypeError( 'invalid argument. Providing an array-like object is not supported.' );
    170 			}
    171 			xdtype = dtype( x ) || 'generic';
    172 			ydtype = opts.dtype || odtype( xdtype, OPTS.policy );
    173 			y = buffer( ydtype, x.length );
    174 
    175 			// FIXME: need to supply dtype enum argument for each array argument...
    176 			t.array( x.length, x, 1, y, 1 );
    177 			return y;
    178 		}
    179 		throw new TypeError( 'invalid argument. Must provide an argument having a supported data type. Value: `' + x + '`.' );
    180 	}
    181 
    182 	/**
    183 	* Function interface which performs dispatch and assigns results to a provided output array.
    184 	*
    185 	* @private
    186 	* @param {(ndarray|Collection)} x - input array
    187 	* @param {(ndarray|Collection)} y - output array
    188 	* @throws {TypeError} first argument must be a supported data type
    189 	* @throws {TypeError} second argument must be a supported data type
    190 	* @throws {TypeError} first and second argument must be the same "kind" (i.e., either both ndarrays or both collections)
    191 	* @throws {RangeError} output array must have sufficient elements
    192 	* @throws {Error} unable to broadcast the input array against the output array
    193 	* @returns {(ndarray|Collection)} output array
    194 	*/
    195 	function assign( x, y ) {
    196 		var xsh;
    197 		var ysh;
    198 		var i;
    199 		if ( isndarrayLike( x ) ) {
    200 			if ( isndarrayLike( y ) ) {
    201 				xsh = x.shape;
    202 				ysh = y.shape;
    203 
    204 				// Check whether we need to broadcast `x`...
    205 				if ( xsh.length === ysh.length ) {
    206 					for ( i = 0; i < xsh.length; i++ ) {
    207 						// Check whether dimensions match...
    208 						if ( xsh[ i ] !== ysh[ i ] ) {
    209 							// We found a mismatched dimension; delegate to `broadcast` to ensure that `x` is broadcast compatible with the output array shape...
    210 							x = broadcast( x, ysh );
    211 							break;
    212 						}
    213 					}
    214 				} else {
    215 					// If we are provided arrays with different ranks (i.e., number of dimensions), assume we need to broadcast, delegating to `broadcast` to ensure that `x` is broadcast compatible with the output array shape...
    216 					x = broadcast( x, ysh );
    217 				}
    218 				t.ndarray( x, y );
    219 				return y;
    220 			}
    221 			throw new TypeError( 'invalid argument. If the first argument is an ndarray, the second argument must be an ndarray.' );
    222 		}
    223 		if ( isCollection( x ) ) {
    224 			if ( isCollection( y ) ) {
    225 				if ( y.length !== x.length ) {
    226 					throw new RangeError( 'invalid argument. Output array must have the same number of elements (i.e., length) as the input array.' );
    227 				}
    228 				// FIXME: need to supply dtype enum argument for each array argument...
    229 				t.array( x.length, x, 1, y, 1 );
    230 				return y;
    231 			}
    232 			throw new TypeError( 'invalid argument. If the first argument is an array-like object, the second argument must be an array-like object.' );
    233 		}
    234 		if ( isNumber( x ) ) {
    235 			throw new TypeError( 'invalid argument. Providing a number is not supported. Consider providing a zero-dimensional ndarray containing the numeric value.' );
    236 		}
    237 		if ( isComplexLike( x ) ) {
    238 			throw new TypeError( 'invalid argument. Providing a complex number is not supported. Consider providing a zero-dimensional ndarray containing the complex number value.' );
    239 		}
    240 		throw new TypeError( 'invalid argument. Must provide an argument having a supported data type. Value: `' + x + '`.' );
    241 	}
    242 }
    243 
    244 
    245 // EXPORTS //
    246 
    247 module.exports = dispatch;