time-to-botec/squiggle/node_modules/@stdlib/math/tools/unary/lib/main.js

248 lines
8.3 KiB
JavaScript
Raw Normal View History

/**
* @license Apache-2.0
*
* Copyright (c) 2021 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
// MODULES //
var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' );
var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive;
var isComplexLike = require( '@stdlib/assert/is-complex-like' );
var isndarrayLike = require( '@stdlib/assert/is-ndarray-like' );
var isCollection = require( '@stdlib/assert/is-collection' );
var dtype = require( '@stdlib/ndarray/base/buffer-dtype' );
var buffer = require( '@stdlib/ndarray/base/buffer' );
var broadcast = require( '@stdlib/ndarray/base/broadcast-array' );
var ndarrayfcn = require( './ndarray.js' );
var odtype = require( './resolve_output_dtype.js' );
var defaults = require( './defaults.json' );
var validateTable = require( './validate_table.js' );
var validateOptions = require( './validate_options.js' );
var validate = require( './validate.js' );
// MAIN //
/**
* Returns a function which dispatches to specified functions based on input argument types.
*
* @param {Object} table - resolution table object
* @param {(Function|null)} [table.number] - function to invoke upon receiving a number
* @param {(Function|null)} [table.complex] - function to invoke upon receiving a complex number
* @param {(Function|null)} [table.array] - function to invoke upon receiving an array-like object
* @param {(Function|null)} [table.ndarray] - function to invoke upon receiving an ndarray-like object
* @param {Options} [options] - options
* @param {string} [options.output_dtype_policy='float'] - policy for determining the output array data type
* @throws {TypeError} first argument must be an object
* @throws {TypeError} first argument must have valid table fields
* @throws {Error} each table field value must be either a function or `null`
* @throws {TypeError} options argument must be an object
* @throws {TypeError} must provide valid options
* @returns {Function} dispatch function
*
* @example
* var base = require( '@stdlib/math/base/special/abs' );
* var strided = require( '@stdlib/math/strided/special/abs' );
* var dispatcher = require( '@stdlib/ndarray/dispatch' );
* var unary = require( '@stdlib/ndarray/base/unary' );
* var Float64Array = require( '@stdlib/array/float64' );
*
* var types = [
* 'float64', 'float64',
* 'float32', 'float32',
* 'generic', 'generic'
* ];
* var data = [
* base,
* base,
* base
* ];
* var nd = dispatcher( unary, types, data, 2, 1, 1 );
*
* var table = {
* 'number': base,
* 'complex': null,
* 'array': strided,
* 'ndarray': nd
* };
*
* var abs = dispatch( table, {
* 'output_dtype_policy': 'same'
* });
*
* var x = new Float64Array( [ -1.0, -2.0, -3.0 ] );
*
* var y = abs( x );
* // returns <Float64Array>[ 1.0, 2.0, 3.0 ]
*/
function dispatch( table, options ) {
var OPTS;
var err;
var fcn;
var t;
t = {
'number': null,
'complex': null,
'array': null,
'ndarray': null
};
err = validateTable( t, table );
if ( err ) {
throw err;
}
OPTS = {
'policy': defaults.output_dtype_policy
};
if ( arguments.length > 1 ) {
err = validateOptions( OPTS, options );
if ( err ) {
throw err;
}
}
fcn = dispatcher;
setReadOnly( fcn, 'assign', assign );
return fcn;
/**
* Function interface which performs dispatch.
*
* @private
* @param {(ndarray|Collection|number|Complex)} x - input value
* @param {Options} [options] - options
* @param {string} [options.dtype] - output array data type
* @param {string} [options.order] - output array order (row-major or column-major)
* @throws {TypeError} first argument must be a supported data type
* @throws {TypeError} options argument must be an object
* @throws {TypeError} must provide valid options
* @returns {(ndarray|Collection|number|Complex)} results
*/
function dispatcher( x, options ) {
var xdtype;
var ydtype;
var opts;
var err;
var y;
if ( isNumber( x ) ) {
if ( t.number ) {
return t.number( x );
}
throw new TypeError( 'invalid argument. Providing a number is not supported.' );
}
if ( isComplexLike( x ) ) {
if ( t.complex ) {
return t.complex( x );
}
throw new TypeError( 'invalid argument. Providing a complex number is not supported.' );
}
opts = {};
if ( arguments.length > 1 ) {
err = validate( opts, options );
if ( err ) {
throw err;
}
}
if ( isndarrayLike( x ) ) {
if ( t.ndarray === null ) {
throw new TypeError( 'invalid argument. Providing an ndarray is not supported.' );
}
ydtype = opts.dtype || odtype( x.dtype, OPTS.policy );
return ndarrayfcn( t.ndarray, x, ydtype, opts.order || x.order );
}
if ( isCollection( x ) ) {
if ( t.array === null ) {
throw new TypeError( 'invalid argument. Providing an array-like object is not supported.' );
}
xdtype = dtype( x ) || 'generic';
ydtype = opts.dtype || odtype( xdtype, OPTS.policy );
y = buffer( ydtype, x.length );
// FIXME: need to supply dtype enum argument for each array argument...
t.array( x.length, x, 1, y, 1 );
return y;
}
throw new TypeError( 'invalid argument. Must provide an argument having a supported data type. Value: `' + x + '`.' );
}
/**
* Function interface which performs dispatch and assigns results to a provided output array.
*
* @private
* @param {(ndarray|Collection)} x - input array
* @param {(ndarray|Collection)} y - output array
* @throws {TypeError} first argument must be a supported data type
* @throws {TypeError} second argument must be a supported data type
* @throws {TypeError} first and second argument must be the same "kind" (i.e., either both ndarrays or both collections)
* @throws {RangeError} output array must have sufficient elements
* @throws {Error} unable to broadcast the input array against the output array
* @returns {(ndarray|Collection)} output array
*/
function assign( x, y ) {
var xsh;
var ysh;
var i;
if ( isndarrayLike( x ) ) {
if ( isndarrayLike( y ) ) {
xsh = x.shape;
ysh = y.shape;
// Check whether we need to broadcast `x`...
if ( xsh.length === ysh.length ) {
for ( i = 0; i < xsh.length; i++ ) {
// Check whether dimensions match...
if ( xsh[ i ] !== ysh[ i ] ) {
// We found a mismatched dimension; delegate to `broadcast` to ensure that `x` is broadcast compatible with the output array shape...
x = broadcast( x, ysh );
break;
}
}
} else {
// 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...
x = broadcast( x, ysh );
}
t.ndarray( x, y );
return y;
}
throw new TypeError( 'invalid argument. If the first argument is an ndarray, the second argument must be an ndarray.' );
}
if ( isCollection( x ) ) {
if ( isCollection( y ) ) {
if ( y.length !== x.length ) {
throw new RangeError( 'invalid argument. Output array must have the same number of elements (i.e., length) as the input array.' );
}
// FIXME: need to supply dtype enum argument for each array argument...
t.array( x.length, x, 1, y, 1 );
return y;
}
throw new TypeError( 'invalid argument. If the first argument is an array-like object, the second argument must be an array-like object.' );
}
if ( isNumber( x ) ) {
throw new TypeError( 'invalid argument. Providing a number is not supported. Consider providing a zero-dimensional ndarray containing the numeric value.' );
}
if ( isComplexLike( x ) ) {
throw new TypeError( 'invalid argument. Providing a complex number is not supported. Consider providing a zero-dimensional ndarray containing the complex number value.' );
}
throw new TypeError( 'invalid argument. Must provide an argument having a supported data type. Value: `' + x + '`.' );
}
}
// EXPORTS //
module.exports = dispatch;