main.js (5031B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 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 // MAIN // 22 23 /** 24 * Converts subscripts to a linear index. 25 * 26 * ## Notes 27 * 28 * - The function accepts the following "modes": 29 * 30 * - `throw`: throws an error when a subscript exceeds array dimensions. 31 * - `wrap`: wrap around subscripts exceeding array dimensions using modulo arithmetic. 32 * - `clamp`: set subscripts exceeding array dimensions to either `0` (minimum index) or the maximum index along a particular dimension. 33 * 34 * - When provided fewer modes than dimensions, the function recycles modes using modulo arithmetic. 35 * 36 * - When provided a stride array containing negative strides, if an `offset` is greater than `0`, the function treats subscripts as mapping to a linear index in an underlying data buffer for the array, thus returning a linear index from the perspective of that buffer. If an `offset` is equal to `0`, the function treats subscripts as mapping to a linear index in an array view, thus returning a linear index from the perspective of that view. 37 * 38 * ```text 39 * Dims: 2x2 40 * Buffer: [ 1, 2, 3, 4 ] 41 * 42 * View = [ a00, a01, 43 * a10, a11 ] 44 * 45 * Strides: 2,1 46 * Offset: 0 47 * 48 * View = [ 1, 2, 49 * 3, 4 ] 50 * 51 * Strides: 2,-1 52 * Offset: 1 53 * 54 * View = [ 2, 1, 55 * 4, 3 ] 56 * 57 * Strides: -2,1 58 * Offset: 2 59 * 60 * View = [ 3, 4, 61 * 1, 2 ] 62 * 63 * Strides: -2,-1 64 * Offset: 3 65 * 66 * View = [ 4, 3, 67 * 2, 1 ] 68 * ``` 69 * 70 * ```javascript 71 * var shape = [ 2, 2 ]; 72 * var strides = [ -2, 1 ]; 73 * var offset = 2; 74 * var mode = [ 'throw' ]; 75 * 76 * // From the perspective of a view... 77 * var idx = sub2ind( shape, strides, 0, 0, 0, mode ); 78 * // returns 0 79 * 80 * idx = sub2ind( shape, strides, 0, 0, 1, mode ); 81 * // returns 1 82 * 83 * idx = sub2ind( shape, strides, 0, 1, 0, mode ); 84 * // returns 2 85 * 86 * idx = sub2ind( shape, strides, 0, 1, 1, mode ); 87 * // returns 3 88 * 89 * // From the perspective of an underlying buffer... 90 * idx = sub2ind( shape, strides, offset, 0, 0, mode ); 91 * // returns 2 92 * 93 * idx = sub2ind( shape, strides, offset, 0, 1, mode ); 94 * // returns 3 95 * 96 * idx = sub2ind( shape, strides, offset, 1, 0, mode ); 97 * // returns 0 98 * 99 * idx = sub2ind( shape, strides, offset, 1, 1, mode ); 100 * // returns 1 101 * ``` 102 * 103 * In short, from the perspective of a view, view data is always ordered. 104 * 105 * 106 * @param {NonNegativeIntegerArray} shape - array shape 107 * @param {IntegerArray} strides - stride array 108 * @param {NonNegativeInteger} offset - location of the first indexed value **based** on the stride array 109 * @param {...integer} i - subscripts 110 * @param {StringArray} mode - specifies how to handle subscripts which exceed array dimensions 111 * @throws {RangeError} must provide subscripts which do not exceed array dimensions 112 * @returns {NonNegativeInteger} linear index 113 * 114 * @example 115 * var shape = [ 3, 3, 3 ]; 116 * var strides = [ 9, 3, 1 ]; 117 * var offset = 0; 118 * var mode = [ 'throw' ] 119 * 120 * var idx = sub2ind( shape, strides, offset, 1, 2, 2, mode ); 121 * // returns 17 122 */ 123 function sub2ind() { 124 var strides; 125 var offset; 126 var nmodes; 127 var shape; 128 var ndims; 129 var modes; 130 var mode; 131 var idx; 132 var m; 133 var s; 134 var j; 135 var i; 136 137 shape = arguments[ 0 ]; 138 strides = arguments[ 1 ]; 139 offset = arguments[ 2 ]; 140 ndims = shape.length; 141 modes = arguments[ 3+ndims ]; // last argument 142 nmodes = modes.length; 143 idx = offset; 144 for ( i = 0; i < ndims; i++ ) { 145 m = shape[ i ]; 146 j = arguments[ i+3 ]; 147 mode = modes[ i%nmodes ]; 148 if ( mode === 'clamp' ) { 149 if ( j < 0 ) { 150 j = 0; 151 } else if ( j >= m ) { 152 j = m - 1; 153 } 154 } else if ( mode === 'wrap' ) { 155 if ( j < 0 ) { 156 j += m; // slight optimization to avoid modulo arithmetic when |j| <= m 157 if ( j < 0 ) { 158 j %= m; 159 if ( j !== 0 ) { 160 j += m; 161 } 162 } 163 } else if ( j >= m ) { 164 j -= m; // slight optimization to avoid modulo arithmetic when m < j <= 2m 165 if ( j >= m ) { 166 j %= m; 167 } 168 } 169 } else if ( j < 0 || j >= m ) { // mode === 'throw' 170 throw new RangeError( 'invalid argument. Subscripts must not exceed array dimensions. Subscript: ' + i + '. Value: `' + j + '`.' ); 171 } 172 s = strides[ i ]; 173 174 // Check if array view... 175 if ( s < 0 && offset === 0 ) { 176 idx -= j * s; // increments idx 177 } else { 178 idx += j * s; // may increment or decrement idx 179 } 180 } 181 return idx; 182 } 183 184 185 // EXPORTS // 186 187 module.exports = sub2ind;