All files set_slice.js

45.66% Statements 79/173
100% Branches 1/1
0% Functions 0/2
45.66% Lines 79/173

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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 1741x 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 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                                                                                                                   1x 1x 1x 1x 1x  
/**
* @license Apache-2.0
*
* Copyright (c) 2024 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 sliceAssign = require( '@stdlib/ndarray/base/slice-assign' );
var isndarrayLike = require( '@stdlib/assert/is-ndarray-like' );
var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive;
var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive;
var isComplexLike = require( '@stdlib/assert/is-complex-like' );
var isComplexDataType = require( '@stdlib/ndarray/base/assert/is-complex-floating-point-data-type' );
var isFloatingDataType = require( '@stdlib/ndarray/base/assert/is-floating-point-data-type' );
var isUnsignedIntegerDataType = require( '@stdlib/ndarray/base/assert/is-unsigned-integer-data-type' );
var isSignedIntegerDataType = require( '@stdlib/ndarray/base/assert/is-signed-integer-data-type' );
var isSafeCast = require( '@stdlib/ndarray/base/assert/is-safe-data-type-cast' );
var INT8_MAX = require( '@stdlib/constants/int8/max' );
var INT16_MAX = require( '@stdlib/constants/int16/max' );
var INT32_MAX = require( '@stdlib/constants/int32/max' );
var minDataType = require( '@stdlib/ndarray/min-dtype' );
var complexDataType = require( '@stdlib/complex/dtype' );
var scalar2ndarray = require( '@stdlib/ndarray/from-scalar' );
var format = require( '@stdlib/string/format' );
var errMessage = require( './error_message.js' );
 
 
// FUNCTIONS //
 
/**
* Returns an options object for creating an ndarray from a scalar value.
*
* @private
* @param {string} dtype - output array data type
* @returns {Object} options
*/
function options( dtype ) {
	return {
		'dtype': dtype
	};
}
 
 
// MAIN //
 
/**
* Sets element values belonging to the ndarray view specified by an indexing expression.
*
* @private
* @param {Object} target - target object
* @param {string} property - indexing expression
* @param {*} value - new value
* @param {Object} receiver - the proxy object or an object inheriting from the proxy
* @param {Object} ctx - context object
* @param {string} ctx.dtype - ndarray data type
* @param {Function} ctx.prop2slice - function for converting an indexing expression to a slice
* @param {boolean} ctx.strict - boolean indicating whether to enforce strict bounds checking
* @throws {Error} invalid slice operation
* @throws {RangeError} number of slice dimensions must match the number of array dimensions
* @throws {Error} assigned value must be broadcast compatible with target array view
* @throws {TypeError} assigned value cannot be safely cast to the target array data type
* @throws {TypeError} target array must have a supported data type
* @returns {boolean} boolean indicating whether assignment succeeded
*/
function setSlice( target, property, value, receiver, ctx ) {
	var vdt;
	var dt;
	var s;

	s = ctx.prop2slice( target, property, ctx.strict );
	if ( s === null ) {
		// If unable to parse the property as an indexing expression, signal that we were unable to perform slice assignment:
		return false;
	}
	if ( !isndarrayLike( value ) ) {
		dt = ctx.dtype;

		// If the target array data type is "generic", we can just go ahead and "cast" to the target array data type...
		if ( dt === 'generic' ) {
			value = scalar2ndarray( value, options( dt ) );
		}
		// If the input value is a real-valued number, we need to inspect the value to determine whether we can safely cast the value to the target array data type...
		else if ( isNumber( value ) ) {
			// If the target array has a floating-point data type, we can just go ahead and cast the input scalar to the target array data type, as number literals are, by default, double-precision floating-point values and casting to lower-precision floating-point is allowed...
			if ( isFloatingDataType( dt ) ) {
				value = scalar2ndarray( value, options( dt ) );
			}
			// If the target array has an unsigned integer data type, then the assigned value must be a compatible nonnegative integer value...
			else if ( isUnsignedIntegerDataType( dt ) ) {
				vdt = minDataType( value ); // note: we rely on data type resolution to handle the case where `value` is a non-integer value. In that case, `vdt` will resolve to a floating-point data type and `isSafeCast` will evaluate to `false`
				if ( isSafeCast( vdt, dt ) ) {
					value = scalar2ndarray( value, options( dt ) );
				} else {
					throw new TypeError( format( 'invalid operation. Assigned value cannot be safely cast to the target array data type. Data types: [%s, %s].', vdt, dt ) );
				}
			}
			// If the target array has a signed integer data type, then the assigned value must be a compatible integer value...
			else if ( isSignedIntegerDataType( dt ) ) {
				if ( !isInteger( value ) ) {
					throw new TypeError( format( 'invalid operation. Assigned value cannot be safely cast to the target array data type. Data types: [%s, %s].', minDataType( value ), dt ) );
				}
				// Manually resolve the minimum data type of the closest "kind" necessary for storing a scalar value, as `minDataType()` defaults to unsigned integer data types when a scalar value is greater than or equal to zero...
				if ( value < 0 ) {
					vdt = minDataType( value );
				} else if ( value <= INT8_MAX ) { // TODO: consider moving this logic to `@stdlib/ndarray/base/min-signed-intger-dtype` where the interface can assume that `value` is integer-valued
					vdt = 'int8';
				} else if ( value <= INT16_MAX ) {
					vdt = 'int16';
				} else if ( value <= INT32_MAX ) {
					vdt = 'int32';
				} else {
					vdt = 'float64';
				}
				if ( isSafeCast( vdt, dt ) ) {
					value = scalar2ndarray( value, options( dt ) );
				} else {
					throw new TypeError( format( 'invalid operation. Assigned value cannot be safely cast to the target array data type. Data types: [%s, %s].', vdt, dt ) );
				}
			}
			// If the target array has "binary" data type, then the assigned value must be a compatible nonnegative integer value...
			else if ( dt === 'binary' ) {
				vdt = minDataType( value );
				if ( vdt === 'uint8' ) {
					value = scalar2ndarray( value, options( dt ) );
				} else {
					throw new TypeError( format( 'invalid operation. Assigned value cannot be safely cast to the target array data type. Data types: [%s, %s].', vdt, dt ) );
				}
			}
			// If we reach this point, we must be dealing with an unexpected target array data type...
			else {
				// Raise an exception in order to flag that, in order to perform assignment, we need to add explicit support for additional data types:
				throw new TypeError( format( 'invalid operation. Unsupported target array data type. Data type: `%s`.', dt ) );
			}
		}
		// If the target array is not "generic" and the input value is a complex number, then the target array data type must also have a complex number data type...
		else if ( isComplexLike( value ) ) {
			if ( !isComplexDataType( dt ) ) {
				throw new TypeError( format( 'invalid operation. Assigned value cannot be safely cast to the target array data type. Data types: [%s, %s].', complexDataType( value ), dt ) );
			}
			value = scalar2ndarray( value, options( dt ) );
		}
		// If the target array is not "generic" and the input value is neither a real- or complex-valued number, raise an exception in order to flag that, in order to perform assignment, we need to add explicit support for additional data types...
		else {
			throw new TypeError( format( 'invalid operation. Assigned value cannot be safely cast to the target array data type. Data types: [%s, %s].', typeof value, dt ) );
		}
	}
	try {
		sliceAssign( value, receiver, s, ctx.strict );
	} catch ( err ) {
		throw new err.constructor( errMessage( err.message ) );
	}
	return true;
}
 
 
// EXPORTS //
 
module.exports = setSlice;