All files / binary-input-casting-dtype/lib main.js

61.76% Statements 63/102
100% Branches 1/1
0% Functions 0/1
61.76% Lines 63/102

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 1031x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                               1x 1x 1x 1x 1x  
/**
* @license Apache-2.0
*
* Copyright (c) 2025 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.
*/
 
/* eslint-disable id-length, max-len */
 
'use strict';
 
// MODULES //
 
var isFloatingPointDataType = require( '@stdlib/ndarray/base/assert/is-floating-point-data-type' );
var isSignedIntegerDataType = require( '@stdlib/ndarray/base/assert/is-signed-integer-data-type' );
var isUnsignedIntegerDataType = require( '@stdlib/ndarray/base/assert/is-unsigned-integer-data-type' );
var isDataType = require( '@stdlib/ndarray/base/assert/is-data-type' );
var promoteDataTypes = require( '@stdlib/ndarray/base/promote-dtypes' );
var defaults = require( '@stdlib/ndarray/defaults' );
var join = require( '@stdlib/array/base/join' );
var format = require( '@stdlib/string/format' );
 
 
// VARIABLES //
 
var DEFAULT_SIGNED_INTEGER_DTYPE = defaults.get( 'dtypes.signed_integer' );
var DEFAULT_UNSIGNED_INTEGER_DTYPE = defaults.get( 'dtypes.unsigned_integer' );
var DEFAULT_REAL_FLOATING_POINT_DTYPE = defaults.get( 'dtypes.real_floating_point' );
 
 
// MAIN //
 
/**
* Resolves the casting data type for an input ndarray provided to a binary function.
*
* @param {string} idtype1 - input ndarray data type
* @param {string} idtype2 - additional input ndarray data type
* @param {string} odtype - output ndarray data type
* @param {string} policy - input ndarray data type casting policy
* @throws {TypeError} fourth argument must be a recognized data type policy
* @throws {Error} unexpected error
* @returns {string} data type
*
* @example
* var dt = resolve( 'float32', 'float32', 'float64', 'none' );
* // returns <string>
*/
function resolve( idtype1, idtype2, odtype, policy ) {
	var dt;
	if ( policy === 'none' ) {
		// When the policy is 'none', casting behavior is implementation-defined, so we just return the input ndarray data type as is...
		return idtype1;
	}
	if ( policy === 'output' ) {
		return odtype;
	}
	if ( policy === 'promoted' ) {
		dt = promoteDataTypes( [ idtype1, idtype2, odtype ] );
		if ( dt === null ) {
			throw new Error( format( 'invalid operation. Unable to promote the input and output data types. Input data types: [%s]. Output data type: %s.', join( [ idtype1, idtype2 ], ', ' ), odtype ) );
		}
		return dt;
	}
	if ( policy === 'accumulation' ) {
		// When the casting policy is 'accumulation', we consider the input ndarray data type in isolation, irrespective of the output data type or the data types of additional input ndarrays...

		// If an input data type is floating-point, allow accumulation in that data type as overflow/underflow is handled naturally as a built-in feature of that data type...
		if ( isFloatingPointDataType( idtype1 ) || idtype1 === 'generic' ) { // NOTE: we may want to revisit this in the future for float16/complex32, where the value range is much more limited
			return idtype1;
		}
		// Unless an input data type value range is larger than the default un/signed integer data type, accumulate in the default un/signed integer data type, as accumulating in smaller range integer data types (e.g., `int8`) are at high risk for overflow, especially for ndarrays containing many elements...
		if ( isUnsignedIntegerDataType( idtype1 ) ) {
			return promoteDataTypes( [ idtype1, DEFAULT_UNSIGNED_INTEGER_DTYPE ] );
		}
		if ( isSignedIntegerDataType( idtype1 ) ) {
			return promoteDataTypes( [ idtype1, DEFAULT_SIGNED_INTEGER_DTYPE ] );
		}
		// For all other input data types, accumulate in the default real-valued floating-point data type...
		return DEFAULT_REAL_FLOATING_POINT_DTYPE;
	}
	// Check for an explicit data type...
	if ( isDataType( policy ) ) {
		return policy;
	}
	throw new TypeError( format( 'invalid argument. Fourth argument must be a supported casting policy. Value: `%s`.', policy ) );
}
 
 
// EXPORTS //
 
module.exports = resolve;