From 3f9f311d30b290e3b673713980c1318c7630c899 Mon Sep 17 00:00:00 2001 From: stdlib-bot Date: Sat, 25 Nov 2023 07:27:42 +0000 Subject: [PATCH] Auto-generated commit --- .editorconfig | 5 - .github/.keepalive | 1 - .github/workflows/publish.yml | 4 +- CONTRIBUTORS | 3 +- README.md | 9 +- benchmark/benchmark.js | 130 ++++++++++++++++ benchmark/c/benchmark.c | 194 ++++++++++++++++++++++-- dist/index.js | 2 +- dist/index.js.map | 4 +- docs/repl.txt | 12 +- docs/types/index.d.ts | 2 +- include/stdlib/ndarray/base/vind2bind.h | 2 +- lib/main.js | 9 +- package.json | 4 +- src/main.c | 55 ++++--- test/test.js | 137 +++++++++++++++++ 16 files changed, 507 insertions(+), 66 deletions(-) delete mode 100644 .github/.keepalive diff --git a/.editorconfig b/.editorconfig index 13e9c39..60d743f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -148,11 +148,6 @@ indent_size = 2 indent_style = space indent_size = 2 -# Set properties for `tslint.json` files: -[tslint.json] -indent_style = space -indent_size = 2 - # Set properties for `tsconfig.json` files: [tsconfig.json] indent_style = space diff --git a/.github/.keepalive b/.github/.keepalive deleted file mode 100644 index ed84bd7..0000000 --- a/.github/.keepalive +++ /dev/null @@ -1 +0,0 @@ -2023-11-01T01:21:58.090Z diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0037bdb..8b8977f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -119,7 +119,7 @@ jobs: # For all dependencies, check in all *.js files if they are still used; if not, remove them: jq -r '.dependencies | keys[]' ./package.json | while read -r dep; do dep=$(echo "$dep" | xargs) - if ! grep -q "$dep" lib/** && ! grep -q -s "$dep" manifest.json && ! grep -q -s "$dep" include.gypi; then + if ! find lib -name "*.js" -exec grep -q "$dep" {} + && ! grep -q -s "$dep" manifest.json && ! grep -q -s "$dep" include.gypi; then jq --indent 2 "del(.dependencies[\"$dep\"])" ./package.json > ./package.json.tmp mv ./package.json.tmp ./package.json fi @@ -129,7 +129,7 @@ jobs: continue fi dep=$(echo "$dep" | xargs) - if ! grep -q "$dep" lib/** && ! grep -q -s "$dep" manifest.json && ! grep -q -s "$dep" include.gypi; then + if ! find lib -name "*.js" -exec grep -q "$dep" {} + && ! grep -q -s "$dep" manifest.json && ! grep -q -s "$dep" include.gypi; then jq --indent 2 "del(.devDependencies[\"$dep\"])" ./package.json > ./package.json.tmp mv ./package.json.tmp ./package.json fi diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0dae4fe..188cda8 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -9,6 +9,7 @@ Brendan Graetz Bruno Fenzl Christopher Dambamuromo Dan Rose +Daniel Killenberger Dominik Moritz Dorrin Sotoudeh Frank Kovacs @@ -29,6 +30,7 @@ Ognjen Jevremović Philipp Burckhardt Pranav Goswami Ricky Reusser +Robert Gislason Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Ryan Seal Seyyed Parsa Neshaei @@ -37,4 +39,3 @@ Stephannie Jiménez Gacha Yernar Yergaziyev orimiles5 <97595296+orimiles5@users.noreply.github.com> rei2hu -Robert Gislason diff --git a/README.md b/README.md index 5358123..5175dee 100644 --- a/README.md +++ b/README.md @@ -85,11 +85,12 @@ var idx = vind2bind( shape, strides, offset, order, 1, 'throw' ); // returns 7 ``` -The function supports the following `modes`: +The function supports the following modes: -- `throw`: specifies that the function should throw an error when a linear index exceeds array dimensions. -- `wrap`: specifies that the function should wrap around a linear index exceeding array dimensions using modulo arithmetic. -- `clamp`: specifies that the function should set a linear index exceeding array dimensions to either `0` (minimum linear index) or the maximum linear index. +- **throw**: specifies that the function should throw an error when a linear index exceeds array dimensions. +- **normalize**: specifies that the function should normalize negative linear indices and throw an error when a linear index exceeds array dimensions. +- **wrap**: specifies that the function should wrap around a linear index exceeding array dimensions using modulo arithmetic. +- **clamp**: specifies that the function should set a linear index exceeding array dimensions to either `0` (minimum linear index) or the maximum linear index. ```javascript var shape = [ 2, 2 ]; diff --git a/benchmark/benchmark.js b/benchmark/benchmark.js index aa638c5..102f467 100644 --- a/benchmark/benchmark.js +++ b/benchmark/benchmark.js @@ -422,3 +422,133 @@ bench( pkg+':mode=clamp,offset=0,order=column-major', function benchmark( b ) { b.pass( 'benchmark finished' ); b.end(); }); + +bench( pkg+':mode=normalize,order=row-major', function benchmark( b ) { + var strides; + var offset; + var shape; + var order; + var out; + var len; + var idx; + var i; + + shape = [ 10, 10, 10 ]; + order = 'row-major'; + strides = shape2strides( shape, order ); + strides[ 1 ] *= -1; + offset = strides2offset( shape, strides ); + len = numel( shape ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + idx = floor( randu()*len*2.0 ) - len; + out = vind2bind( shape, strides, offset, order, idx, 'normalize' ); + if ( out !== out ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( !isInteger( out ) ) { + b.fail( 'should return an integer' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':mode=normalize,order=column-major', function benchmark( b ) { + var strides; + var offset; + var shape; + var order; + var out; + var len; + var idx; + var i; + + shape = [ 10, 10, 10 ]; + order = 'column-major'; + strides = shape2strides( shape, order ); + strides[ 1 ] *= -1; + offset = strides2offset( shape, strides ); + len = numel( shape ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + idx = floor( randu()*len*2.0 ) - len; + out = vind2bind( shape, strides, offset, order, idx, 'normalize' ); + if ( out !== out ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( !isInteger( out ) ) { + b.fail( 'should return an integer' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':mode=normalize,offset=0,order=row-major', function benchmark( b ) { + var strides; + var offset; + var shape; + var order; + var out; + var len; + var idx; + var i; + + shape = [ 10, 10, 10 ]; + order = 'row-major'; + strides = shape2strides( shape, order ); + offset = strides2offset( shape, strides ); + len = numel( shape ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + idx = floor( randu()*len*2.0 ) - len; + out = vind2bind( shape, strides, offset, order, idx, 'normalize' ); + if ( out !== out ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( !isInteger( out ) ) { + b.fail( 'should return an integer' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':mode=normalize,offset=0,order=column-major', function benchmark( b ) { + var strides; + var offset; + var shape; + var order; + var out; + var len; + var idx; + var i; + + shape = [ 10, 10, 10 ]; + order = 'column-major'; + strides = shape2strides( shape, order ); + offset = strides2offset( shape, strides ); + len = numel( shape ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + idx = floor( randu()*len*2.0 ) - len; + out = vind2bind( shape, strides, offset, order, idx, 'normalize' ); + if ( out !== out ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( !isInteger( out ) ) { + b.fail( 'should return an integer' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/benchmark/c/benchmark.c b/benchmark/c/benchmark.c index 8be7ec5..da24c2e 100644 --- a/benchmark/c/benchmark.c +++ b/benchmark/c/benchmark.c @@ -36,7 +36,7 @@ /** * Prints the TAP version. */ -void print_version() { +void print_version( void ) { printf( "TAP version 13\n" ); } @@ -74,7 +74,7 @@ void print_results( double elapsed ) { * * @return clock time */ -double tic() { +double tic( void ) { struct timeval now; gettimeofday( &now, NULL ); return (double)now.tv_sec + (double)now.tv_usec/1.0e6; @@ -85,7 +85,7 @@ double tic() { * * @return random double */ -double rand_double() { +double rand_double( void ) { int r = rand(); return (double)r / ( (double)RAND_MAX + 1.0 ); } @@ -95,7 +95,7 @@ double rand_double() { * * @return elapsed time in seconds */ -double benchmark1() { +double benchmark1( void ) { double elapsed; int64_t idx; int64_t ind; @@ -129,7 +129,7 @@ double benchmark1() { * * @return elapsed time in seconds */ -double benchmark2() { +double benchmark2( void ) { double elapsed; int64_t idx; int64_t ind; @@ -163,7 +163,7 @@ double benchmark2() { * * @return elapsed time in seconds */ -double benchmark3() { +double benchmark3( void ) { double elapsed; int64_t idx; int64_t ind; @@ -197,7 +197,7 @@ double benchmark3() { * * @return elapsed time in seconds */ -double benchmark4() { +double benchmark4( void ) { double elapsed; int64_t idx; int64_t ind; @@ -231,7 +231,7 @@ double benchmark4() { * * @return elapsed time in seconds */ -double benchmark5() { +double benchmark5( void ) { double elapsed; int64_t idx; int64_t ind; @@ -265,7 +265,7 @@ double benchmark5() { * * @return elapsed time in seconds */ -double benchmark6() { +double benchmark6( void ) { double elapsed; int64_t idx; int64_t ind; @@ -299,7 +299,7 @@ double benchmark6() { * * @return elapsed time in seconds */ -double benchmark7() { +double benchmark7( void ) { double elapsed; int64_t idx; int64_t ind; @@ -333,7 +333,7 @@ double benchmark7() { * * @return elapsed time in seconds */ -double benchmark8() { +double benchmark8( void ) { double elapsed; int64_t idx; int64_t ind; @@ -367,7 +367,7 @@ double benchmark8() { * * @return elapsed time in seconds */ -double benchmark9() { +double benchmark9( void ) { double elapsed; int64_t idx; int64_t ind; @@ -401,7 +401,7 @@ double benchmark9() { * * @return elapsed time in seconds */ -double benchmark10() { +double benchmark10( void ) { double elapsed; int64_t idx; int64_t ind; @@ -435,7 +435,7 @@ double benchmark10() { * * @return elapsed time in seconds */ -double benchmark11() { +double benchmark11( void ) { double elapsed; int64_t idx; int64_t ind; @@ -469,7 +469,7 @@ double benchmark11() { * * @return elapsed time in seconds */ -double benchmark12() { +double benchmark12( void ) { double elapsed; int64_t idx; int64_t ind; @@ -498,6 +498,142 @@ double benchmark12() { return elapsed; } +/** +* Runs a benchmark. +* +* @return elapsed time in seconds +*/ +double benchmark13( void ) { + double elapsed; + int64_t idx; + int64_t ind; + double t; + int i; + + int64_t ndims = 3; + int64_t shape[] = { 10, 10, 10 }; + int64_t strides[] = { 100, -10, 1 }; + int64_t offset = 90; + int64_t len = 1000; + + t = tic(); + for ( i = 0; i < ITERATIONS; i++ ) { + idx = (int64_t)( (rand_double()*len*2.0)-len ); + ind = stdlib_ndarray_vind2bind( ndims, shape, strides, offset, STDLIB_NDARRAY_ROW_MAJOR, idx, STDLIB_NDARRAY_INDEX_NORMALIZE ); + if ( ind < -1 ) { + printf( "unexpected result\n" ); + break; + } + } + elapsed = tic() - t; + if ( ind < -1 ) { + printf( "unexpected result\n" ); + } + return elapsed; +} + +/** +* Runs a benchmark. +* +* @return elapsed time in seconds +*/ +double benchmark14( void ) { + double elapsed; + int64_t idx; + int64_t ind; + double t; + int i; + + int64_t ndims = 3; + int64_t shape[] = { 10, 10, 10 }; + int64_t strides[] = { 1, -10, 100 }; + int64_t offset = 90; + int64_t len = 1000; + + t = tic(); + for ( i = 0; i < ITERATIONS; i++ ) { + idx = (int64_t)( (rand_double()*len*2.0)-len ); + ind = stdlib_ndarray_vind2bind( ndims, shape, strides, offset, STDLIB_NDARRAY_COLUMN_MAJOR, idx, STDLIB_NDARRAY_INDEX_NORMALIZE ); + if ( ind < -1 ) { + printf( "unexpected result\n" ); + break; + } + } + elapsed = tic() - t; + if ( ind < -1 ) { + printf( "unexpected result\n" ); + } + return elapsed; +} + +/** +* Runs a benchmark. +* +* @return elapsed time in seconds +*/ +double benchmark15( void ) { + double elapsed; + int64_t idx; + int64_t ind; + double t; + int i; + + int64_t ndims = 3; + int64_t shape[] = { 10, 10, 10 }; + int64_t strides[] = { 100, 10, 1 }; + int64_t offset = 0; + int64_t len = 1000; + + t = tic(); + for ( i = 0; i < ITERATIONS; i++ ) { + idx = (int64_t)( (rand_double()*len*2.0)-len ); + ind = stdlib_ndarray_vind2bind( ndims, shape, strides, offset, STDLIB_NDARRAY_ROW_MAJOR, idx, STDLIB_NDARRAY_INDEX_NORMALIZE ); + if ( ind < -1 ) { + printf( "unexpected result\n" ); + break; + } + } + elapsed = tic() - t; + if ( ind < -1 ) { + printf( "unexpected result\n" ); + } + return elapsed; +} + +/** +* Runs a benchmark. +* +* @return elapsed time in seconds +*/ +double benchmark16( void ) { + double elapsed; + int64_t idx; + int64_t ind; + double t; + int i; + + int64_t ndims = 3; + int64_t shape[] = { 10, 10, 10 }; + int64_t strides[] = { 1, 10, 100 }; + int64_t offset = 0; + int64_t len = 1000; + + t = tic(); + for ( i = 0; i < ITERATIONS; i++ ) { + idx = (int64_t)( (rand_double()*len*2.0)-len ); + ind = stdlib_ndarray_vind2bind( ndims, shape, strides, offset, STDLIB_NDARRAY_COLUMN_MAJOR, idx, STDLIB_NDARRAY_INDEX_NORMALIZE ); + if ( ind < -1 ) { + printf( "unexpected result\n" ); + break; + } + } + elapsed = tic() - t; + if ( ind < -1 ) { + printf( "unexpected result\n" ); + } + return elapsed; +} + /** * Main execution sequence. */ @@ -596,5 +732,33 @@ int main( void ) { print_results( elapsed ); printf( "ok %d benchmark finished\n", count ); } + for ( i = 0; i < REPEATS; i++ ) { + count += 1; + printf( "# c::native::%s:mode=normalize,order=row-major\n", NAME ); + elapsed = benchmark13(); + print_results( elapsed ); + printf( "ok %d benchmark finished\n", count ); + } + for ( i = 0; i < REPEATS; i++ ) { + count += 1; + printf( "# c::native::%s:mode=normalize,order=column-major\n", NAME ); + elapsed = benchmark14(); + print_results( elapsed ); + printf( "ok %d benchmark finished\n", count ); + } + for ( i = 0; i < REPEATS; i++ ) { + count += 1; + printf( "# c::native::%s:mode=normalize,order=row-major,offset=0\n", NAME ); + elapsed = benchmark15(); + print_results( elapsed ); + printf( "ok %d benchmark finished\n", count ); + } + for ( i = 0; i < REPEATS; i++ ) { + count += 1; + printf( "# c::native::%s:mode=normalize,order=column-major,offset=0\n", NAME ); + elapsed = benchmark16(); + print_results( elapsed ); + printf( "ok %d benchmark finished\n", count ); + } print_summary( count, count ); } diff --git a/dist/index.js b/dist/index.js index daa63ba..cbf968e 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,5 +1,5 @@ "use strict";var i=function(f,a){return function(){return a||f((a={exports:{}}).exports,a),a.exports}};var m=i(function(y,v){ -var b=require('@stdlib/error-tools-fmtprodmsg/dist');function g(f,a,s,c,r,l){var u,n,o,t,e;for(u=f.length,n=1,e=0;e=n&&(r=n-1);else if(l==="wrap")r<0?(r+=n,r<0&&(r%=n,r!==0&&(r+=n))):r>=n&&(r-=n,r>=n&&(r%=n));else if(r<0||r>=n)throw new RangeError(b('0jf5E',n,r));if(o=s,c==="column-major"){for(e=0;e=0;e--)t=r%f[e],r-=t,r/=f[e],o+=t*a[e];return o}v.exports=g +var b=require('@stdlib/error-tools-fmtprodmsg/dist');function g(f,a,s,c,r,l){var u,n,o,t,e;for(u=f.length,n=1,e=0;e=n&&(r=n-1);else if(l==="wrap")r<0?(r+=n,r<0&&(r%=n,r!==0&&(r+=n))):r>=n&&(r-=n,r>=n&&(r%=n));else if(l==="normalize"&&r<0&&(r+=n),r<0||r>=n)throw new RangeError(b('0jf5E',n,r));if(o=s,c==="column-major"){for(e=0;e=0;e--)t=r%f[e],r-=t,r/=f[e],o+=t*a[e];return o}v.exports=g });var w=m();module.exports=w; /** @license Apache-2.0 */ //# sourceMappingURL=index.js.map diff --git a/dist/index.js.map b/dist/index.js.map index 66cffc2..ac930ca 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../lib/main.js", "../lib/index.js"], - "sourcesContent": ["/**\n* @license Apache-2.0\n*\n* Copyright (c) 2018 The Stdlib Authors.\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n'use strict';\n\n// MODULES //\n\nvar format = require( '@stdlib/string-format' );\n\n\n// MAIN //\n\n/**\n* Converts a linear index in an array view to a linear index in an underlying data buffer.\n*\n* @param {NonNegativeIntegerArray} shape - array shape\n* @param {IntegerArray} strides - stride array\n* @param {NonNegativeInteger} offset - location of the first indexed value **based** on the stride array\n* @param {string} order - specifies whether an array is row-major (C-style) or column-major (Fortran-style)\n* @param {integer} idx - linear index in an array view\n* @param {string} mode - specifies how to handle a linear index which exceeds array dimensions\n* @throws {RangeError} linear index must not exceed array dimensions\n* @returns {NonNegativeInteger} linear index in an underlying data buffer\n*\n* @example\n* var shape = [ 3, 3 ];\n* var strides = [ -3, 1 ];\n* var offset = 6;\n* var order = 'row-major';\n* var mode = 'throw';\n*\n* var ind = vind2bind( shape, strides, offset, order, 1, mode );\n* // returns 7\n*/\nfunction vind2bind( shape, strides, offset, order, idx, mode ) {\n\tvar ndims;\n\tvar len;\n\tvar ind;\n\tvar s;\n\tvar i;\n\n\tndims = shape.length;\n\tlen = 1;\n\tfor ( i = 0; i < ndims; i++ ) {\n\t\tlen *= shape[ i ];\n\t}\n\tif ( mode === 'clamp' ) {\n\t\tif ( idx < 0 ) {\n\t\t\tidx = 0;\n\t\t} else if ( idx >= len ) {\n\t\t\tidx = len - 1;\n\t\t}\n\t} else if ( mode === 'wrap' ) {\n\t\tif ( idx < 0 ) {\n\t\t\tidx += len; // slight optimization to avoid modulo arithmetic when |idx| <= len\n\t\t\tif ( idx < 0 ) {\n\t\t\t\tidx %= len;\n\t\t\t\tif ( idx !== 0 ) {\n\t\t\t\t\tidx += len;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ( idx >= len ) {\n\t\t\tidx -= len; // slight optimization to avoid modulo arithmetic when len < idx <= 2*len\n\t\t\tif ( idx >= len ) {\n\t\t\t\tidx %= len;\n\t\t\t}\n\t\t}\n\t} else if ( idx < 0 || idx >= len ) {\n\t\tthrow new RangeError( format( 'invalid argument. Linear index must not exceed array dimensions. Number of array elements: `%u`. Value: `%d`.', len, idx ) );\n\t}\n\t// The approach which follows is to resolve a view index to its subscripts and then plug the subscripts into the standard formula for computing the linear index in the underlying data buffer...\n\tind = offset;\n\tif ( order === 'column-major' ) {\n\t\tfor ( i = 0; i < ndims; i++ ) {\n\t\t\ts = idx % shape[ i ];\n\t\t\tidx -= s;\n\t\t\tidx /= shape[ i ];\n\t\t\tind += s * strides[ i ];\n\t\t}\n\t\treturn ind;\n\t}\n\t// Case: row-major\n\tfor ( i = ndims-1; i >= 0; i-- ) {\n\t\ts = idx % shape[ i ];\n\t\tidx -= s;\n\t\tidx /= shape[ i ];\n\t\tind += s * strides[ i ];\n\t}\n\treturn ind;\n}\n\n\n// EXPORTS //\n\nmodule.exports = vind2bind;\n", "/**\n* @license Apache-2.0\n*\n* Copyright (c) 2018 The Stdlib Authors.\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n'use strict';\n\n/**\n* Convert a linear index in an array view to a linear index in an underlying data buffer.\n*\n* @module @stdlib/ndarray-base-vind2bind\n*\n* @example\n* var vind2bind = require( '@stdlib/ndarray-base-vind2bind' );\n*\n* var shape = [ 3, 3 ];\n* var strides = [ -3, 1 ];\n* var offset = 6;\n* var order = 'row-major';\n* var mode = 'throw';\n*\n* var ind = vind2bind( shape, strides, offset, order, 1, mode );\n* // returns 7\n*/\n\n// MODULES //\n\nvar vind2bind = require( './main.js' );\n\n\n// EXPORTS //\n\nmodule.exports = vind2bind;\n"], - "mappings": "uGAAA,IAAAA,EAAAC,EAAA,SAAAC,EAAAC,EAAA,cAsBA,IAAIC,EAAS,QAAS,uBAAwB,EA2B9C,SAASC,EAAWC,EAAOC,EAASC,EAAQC,EAAOC,EAAKC,EAAO,CAC9D,IAAIC,EACAC,EACAC,EACAC,EACAC,EAIJ,IAFAJ,EAAQN,EAAM,OACdO,EAAM,EACAG,EAAI,EAAGA,EAAIJ,EAAOI,IACvBH,GAAOP,EAAOU,CAAE,EAEjB,GAAKL,IAAS,QACRD,EAAM,EACVA,EAAM,EACKA,GAAOG,IAClBH,EAAMG,EAAM,WAEFF,IAAS,OACfD,EAAM,GACVA,GAAOG,EACFH,EAAM,IACVA,GAAOG,EACFH,IAAQ,IACZA,GAAOG,KAGEH,GAAOG,IAClBH,GAAOG,EACFH,GAAOG,IACXH,GAAOG,YAGEH,EAAM,GAAKA,GAAOG,EAC7B,MAAM,IAAI,WAAYT,EAAQ,gHAAiHS,EAAKH,CAAI,CAAE,EAI3J,GADAI,EAAMN,EACDC,IAAU,eAAiB,CAC/B,IAAMO,EAAI,EAAGA,EAAIJ,EAAOI,IACvBD,EAAIL,EAAMJ,EAAOU,CAAE,EACnBN,GAAOK,EACPL,GAAOJ,EAAOU,CAAE,EAChBF,GAAOC,EAAIR,EAASS,CAAE,EAEvB,OAAOF,CACR,CAEA,IAAME,EAAIJ,EAAM,EAAGI,GAAK,EAAGA,IAC1BD,EAAIL,EAAMJ,EAAOU,CAAE,EACnBN,GAAOK,EACPL,GAAOJ,EAAOU,CAAE,EAChBF,GAAOC,EAAIR,EAASS,CAAE,EAEvB,OAAOF,CACR,CAKAX,EAAO,QAAUE,ICrEjB,IAAIY,EAAY,IAKhB,OAAO,QAAUA", + "sourcesContent": ["/**\n* @license Apache-2.0\n*\n* Copyright (c) 2018 The Stdlib Authors.\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n'use strict';\n\n// MODULES //\n\nvar format = require( '@stdlib/string-format' );\n\n\n// MAIN //\n\n/**\n* Converts a linear index in an array view to a linear index in an underlying data buffer.\n*\n* @param {NonNegativeIntegerArray} shape - array shape\n* @param {IntegerArray} strides - stride array\n* @param {NonNegativeInteger} offset - location of the first indexed value **based** on the stride array\n* @param {string} order - specifies whether an array is row-major (C-style) or column-major (Fortran-style)\n* @param {integer} idx - linear index in an array view\n* @param {string} mode - specifies how to handle a linear index which exceeds array dimensions\n* @throws {RangeError} linear index must not exceed array dimensions\n* @returns {NonNegativeInteger} linear index in an underlying data buffer\n*\n* @example\n* var shape = [ 3, 3 ];\n* var strides = [ -3, 1 ];\n* var offset = 6;\n* var order = 'row-major';\n* var mode = 'throw';\n*\n* var ind = vind2bind( shape, strides, offset, order, 1, mode );\n* // returns 7\n*/\nfunction vind2bind( shape, strides, offset, order, idx, mode ) {\n\tvar ndims;\n\tvar len;\n\tvar ind;\n\tvar s;\n\tvar i;\n\n\tndims = shape.length;\n\tlen = 1;\n\tfor ( i = 0; i < ndims; i++ ) {\n\t\tlen *= shape[ i ];\n\t}\n\tif ( mode === 'clamp' ) {\n\t\tif ( idx < 0 ) {\n\t\t\tidx = 0;\n\t\t} else if ( idx >= len ) {\n\t\t\tidx = len - 1;\n\t\t}\n\t} else if ( mode === 'wrap' ) {\n\t\tif ( idx < 0 ) {\n\t\t\tidx += len; // slight optimization to avoid modulo arithmetic when |idx| <= len\n\t\t\tif ( idx < 0 ) {\n\t\t\t\tidx %= len;\n\t\t\t\tif ( idx !== 0 ) {\n\t\t\t\t\tidx += len;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ( idx >= len ) {\n\t\t\tidx -= len; // slight optimization to avoid modulo arithmetic when len < idx <= 2*len\n\t\t\tif ( idx >= len ) {\n\t\t\t\tidx %= len;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif ( mode === 'normalize' && idx < 0 ) {\n\t\t\tidx += len;\n\t\t}\n\t\tif ( idx < 0 || idx >= len ) {\n\t\t\tthrow new RangeError( format( 'invalid argument. Linear index must not exceed array dimensions. Number of array elements: `%u`. Value: `%d`.', len, idx ) );\n\t\t}\n\t}\n\t// The approach which follows is to resolve a view index to its subscripts and then plug the subscripts into the standard formula for computing the linear index in the underlying data buffer...\n\tind = offset;\n\tif ( order === 'column-major' ) {\n\t\tfor ( i = 0; i < ndims; i++ ) {\n\t\t\ts = idx % shape[ i ];\n\t\t\tidx -= s;\n\t\t\tidx /= shape[ i ];\n\t\t\tind += s * strides[ i ];\n\t\t}\n\t\treturn ind;\n\t}\n\t// Case: row-major\n\tfor ( i = ndims-1; i >= 0; i-- ) {\n\t\ts = idx % shape[ i ];\n\t\tidx -= s;\n\t\tidx /= shape[ i ];\n\t\tind += s * strides[ i ];\n\t}\n\treturn ind;\n}\n\n\n// EXPORTS //\n\nmodule.exports = vind2bind;\n", "/**\n* @license Apache-2.0\n*\n* Copyright (c) 2018 The Stdlib Authors.\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n'use strict';\n\n/**\n* Convert a linear index in an array view to a linear index in an underlying data buffer.\n*\n* @module @stdlib/ndarray-base-vind2bind\n*\n* @example\n* var vind2bind = require( '@stdlib/ndarray-base-vind2bind' );\n*\n* var shape = [ 3, 3 ];\n* var strides = [ -3, 1 ];\n* var offset = 6;\n* var order = 'row-major';\n* var mode = 'throw';\n*\n* var ind = vind2bind( shape, strides, offset, order, 1, mode );\n* // returns 7\n*/\n\n// MODULES //\n\nvar vind2bind = require( './main.js' );\n\n\n// EXPORTS //\n\nmodule.exports = vind2bind;\n"], + "mappings": "uGAAA,IAAAA,EAAAC,EAAA,SAAAC,EAAAC,EAAA,cAsBA,IAAIC,EAAS,QAAS,uBAAwB,EA2B9C,SAASC,EAAWC,EAAOC,EAASC,EAAQC,EAAOC,EAAKC,EAAO,CAC9D,IAAIC,EACAC,EACAC,EACAC,EACAC,EAIJ,IAFAJ,EAAQN,EAAM,OACdO,EAAM,EACAG,EAAI,EAAGA,EAAIJ,EAAOI,IACvBH,GAAOP,EAAOU,CAAE,EAEjB,GAAKL,IAAS,QACRD,EAAM,EACVA,EAAM,EACKA,GAAOG,IAClBH,EAAMG,EAAM,WAEFF,IAAS,OACfD,EAAM,GACVA,GAAOG,EACFH,EAAM,IACVA,GAAOG,EACFH,IAAQ,IACZA,GAAOG,KAGEH,GAAOG,IAClBH,GAAOG,EACFH,GAAOG,IACXH,GAAOG,YAIJF,IAAS,aAAeD,EAAM,IAClCA,GAAOG,GAEHH,EAAM,GAAKA,GAAOG,EACtB,MAAM,IAAI,WAAYT,EAAQ,gHAAiHS,EAAKH,CAAI,CAAE,EAK5J,GADAI,EAAMN,EACDC,IAAU,eAAiB,CAC/B,IAAMO,EAAI,EAAGA,EAAIJ,EAAOI,IACvBD,EAAIL,EAAMJ,EAAOU,CAAE,EACnBN,GAAOK,EACPL,GAAOJ,EAAOU,CAAE,EAChBF,GAAOC,EAAIR,EAASS,CAAE,EAEvB,OAAOF,CACR,CAEA,IAAME,EAAIJ,EAAM,EAAGI,GAAK,EAAGA,IAC1BD,EAAIL,EAAMJ,EAAOU,CAAE,EACnBN,GAAOK,EACPL,GAAOJ,EAAOU,CAAE,EAChBF,GAAOC,EAAIR,EAASS,CAAE,EAEvB,OAAOF,CACR,CAKAX,EAAO,QAAUE,IC1EjB,IAAIY,EAAY,IAKhB,OAAO,QAAUA", "names": ["require_main", "__commonJSMin", "exports", "module", "format", "vind2bind", "shape", "strides", "offset", "order", "idx", "mode", "ndims", "len", "ind", "s", "i", "vind2bind"] } diff --git a/docs/repl.txt b/docs/repl.txt index 3b466e6..b770901 100644 --- a/docs/repl.txt +++ b/docs/repl.txt @@ -24,11 +24,13 @@ mode: string Specifies how to handle a linear index which exceeds array dimensions. If equal to 'throw', the function throws an error when a linear index - exceeds array dimensions. If equal to 'wrap', the function wraps around - a linear index exceeding array dimensions using modulo arithmetic. If - equal to 'clamp', the function sets a linear index exceeding array - dimensions to either `0` (minimum linear index) or the maximum linear - index. Default: 'throw'. + exceeds array dimensions. If equal to 'normalize', the function + normalizes negative linear indices and throws an error when a linear + index exceeds array dimensions. If equal to 'wrap', the function wraps + around a linear index exceeding array dimensions using modulo + arithmetic. If equal to 'clamp', the function sets a linear index + exceeding array dimensions to either `0` (minimum linear index) or the + maximum linear index. Default: 'throw'. Returns ------- diff --git a/docs/types/index.d.ts b/docs/types/index.d.ts index 702bc73..e7b5423 100644 --- a/docs/types/index.d.ts +++ b/docs/types/index.d.ts @@ -45,7 +45,7 @@ import { Mode, Order } from '@stdlib/types/ndarray'; * var ind = vind2bind( shape, strides, offset, order, 1, mode ); * // returns 7 */ -declare function vind2bind( shape: ArrayLike, strides: ArrayLike, offset: number, order: Order, idx: number, mode: Mode ): number; // tslint-disable-line max-line-length +declare function vind2bind( shape: ArrayLike, strides: ArrayLike, offset: number, order: Order, idx: number, mode: Mode ): number; // EXPORTS // diff --git a/include/stdlib/ndarray/base/vind2bind.h b/include/stdlib/ndarray/base/vind2bind.h index ef21bf8..ef09218 100644 --- a/include/stdlib/ndarray/base/vind2bind.h +++ b/include/stdlib/ndarray/base/vind2bind.h @@ -33,7 +33,7 @@ extern "C" { /** * Converts a linear index in an array view to a linear index in an underlying data buffer. */ -int64_t stdlib_ndarray_vind2bind( int64_t ndims, int64_t *shape, int64_t *strides, int64_t offset, enum STDLIB_NDARRAY_ORDER order, int64_t idx, enum STDLIB_NDARRAY_INDEX_MODE mode ); +int64_t stdlib_ndarray_vind2bind( const int64_t ndims, const int64_t *shape, const int64_t *strides, const int64_t offset, const enum STDLIB_NDARRAY_ORDER order, const int64_t idx, const enum STDLIB_NDARRAY_INDEX_MODE mode ); #ifdef __cplusplus } diff --git a/lib/main.js b/lib/main.js index a117229..4177905 100644 --- a/lib/main.js +++ b/lib/main.js @@ -80,8 +80,13 @@ function vind2bind( shape, strides, offset, order, idx, mode ) { idx %= len; } } - } else if ( idx < 0 || idx >= len ) { - throw new RangeError( format( 'invalid argument. Linear index must not exceed array dimensions. Number of array elements: `%u`. Value: `%d`.', len, idx ) ); + } else { + if ( mode === 'normalize' && idx < 0 ) { + idx += len; + } + if ( idx < 0 || idx >= len ) { + throw new RangeError( format( 'invalid argument. Linear index must not exceed array dimensions. Number of array elements: `%u`. Value: `%d`.', len, idx ) ); + } } // The approach which follows is to resolve a view index to its subscripts and then plug the subscripts into the standard formula for computing the linear index in the underlying data buffer... ind = offset; diff --git a/package.json b/package.json index 8792cc4..0f015d7 100644 --- a/package.json +++ b/package.json @@ -42,12 +42,12 @@ "@stdlib/ndarray-index-modes": "^0.1.1", "@stdlib/ndarray-orders": "^0.1.1", "@stdlib/string-format": "^0.1.1", - "@stdlib/types": "^0.1.0", + "@stdlib/types": "^0.2.0", "@stdlib/utils-library-manifest": "^0.1.1" }, "devDependencies": { "@stdlib/assert-is-integer": "^0.1.0", - "@stdlib/bench": "^0.1.0", + "@stdlib/bench": "^0.2.1", "@stdlib/math-base-special-floor": "^0.1.1", "@stdlib/ndarray-base-numel": "^0.1.1", "@stdlib/ndarray-base-shape2strides": "^0.1.1", diff --git a/src/main.c b/src/main.c index d7d1ab9..186a4e9 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ * * ## Notes * -* - In "error" mode, the function returns `-1` if an index is out-of-bounds. +* - In "error" and "normalize" modes, the function returns `-1` if an index is out-of-bounds. * * @param ndims number of dimensions * @param shape array shape (dimensions) @@ -50,56 +50,63 @@ * int64_t idx = stdlib_ndarray_vind2bind( ndims, shape, strides, offset, STDLIB_NDARRAY_ROW_MAJOR, 1, STDLIB_NDARRAY_INDEX_ERROR ); * // returns 7 */ -int64_t stdlib_ndarray_vind2bind( int64_t ndims, int64_t *shape, int64_t *strides, int64_t offset, enum STDLIB_NDARRAY_ORDER order, int64_t idx, enum STDLIB_NDARRAY_INDEX_MODE mode ) { +int64_t stdlib_ndarray_vind2bind( const int64_t ndims, const int64_t *shape, const int64_t *strides, const int64_t offset, const enum STDLIB_NDARRAY_ORDER order, const int64_t idx, const enum STDLIB_NDARRAY_INDEX_MODE mode ) { + int64_t index; int64_t len; int64_t ind; int64_t s; int64_t i; + index = idx; len = 1; for ( i = 0; i < ndims; i++ ) { len *= shape[ i ]; } if ( mode == STDLIB_NDARRAY_INDEX_CLAMP ) { - if ( idx < 0 ) { - idx = 0; - } else if ( idx >= len ) { - idx = len - 1; + if ( index < 0 ) { + index = 0; + } else if ( index >= len ) { + index = len - 1; } } else if ( mode == STDLIB_NDARRAY_INDEX_WRAP ) { - if ( idx < 0 ) { - idx += len; // slight optimization to avoid modulo arithmetic when |idx| <= len - if ( idx < 0 ) { - idx -= len*( (int64_t)( idx/len ) ); // this is equivalent to `idx mod len`, where the result has same sign as dividend (i.e., `idx`); cannot use `%` as the sign of the result is implementation defined in C - if ( idx != 0 ) { - idx += len; + if ( index < 0 ) { + index += len; // slight optimization to avoid modulo arithmetic when |index| <= len + if ( index < 0 ) { + index -= len*( (int64_t)( index/len ) ); // this is equivalent to `index mod len`, where the result has same sign as dividend (i.e., `index`); cannot use `%` as the sign of the result is implementation defined in C + if ( index != 0 ) { + index += len; } } - } else if ( idx >= len ) { - idx -= len; // slight optimization to avoid modulo arithmetic when len < idx <= 2*len - if ( idx >= len ) { - idx %= len; + } else if ( index >= len ) { + index -= len; // slight optimization to avoid modulo arithmetic when len < index <= 2*len + if ( index >= len ) { + index %= len; } } - } else if ( idx < 0 || idx >= len ) { - return -1; + } else { + if ( mode == STDLIB_NDARRAY_INDEX_NORMALIZE && index < 0 ) { + index += len; + } + if ( index < 0 || index >= len ) { + return -1; + } } // The approach which follows is to resolve a view index to its subscripts and then plug the subscripts into the standard formula for computing the linear index in the underlying data buffer... ind = offset; if ( order == STDLIB_NDARRAY_COLUMN_MAJOR ) { for ( i = 0; i < ndims; i++ ) { - s = idx % shape[ i ]; // assume nonnegative "shape" - idx -= s; - idx /= shape[ i ]; + s = index % shape[ i ]; // assume nonnegative "shape" + index -= s; + index /= shape[ i ]; ind += s * strides[ i ]; } return ind; } // Case: row-major for ( i = ndims-1; i >= 0; i-- ) { - s = idx % shape[ i ]; // assume nonnegative "shape" - idx -= s; - idx /= shape[ i ]; + s = index % shape[ i ]; // assume nonnegative "shape" + index -= s; + index /= shape[ i ]; ind += s * strides[ i ]; } return ind; diff --git a/test/test.js b/test/test.js index abd362b..a4d2c62 100644 --- a/test/test.js +++ b/test/test.js @@ -288,6 +288,56 @@ tape( 'if the `mode` is `throw`, the function throws if provided a linear index } }); +tape( 'if the `mode` is `normalize`, the function throws if provided a linear index which exceeds array dimensions (order=row-major)', function test( t ) { + var strides; + var offset; + var order; + var shape; + + shape = [ 2, 2 ]; + order = 'row-major'; + strides = [ 2, 1 ]; + offset = 0; + + t.throws( foo, RangeError, 'throws a range error' ); + t.throws( bar, RangeError, 'throws a range error' ); + + t.end(); + + function foo() { + vind2bind( shape, strides, offset, order, 999999, 'normalize' ); + } + + function bar() { + vind2bind( shape, strides, offset, order, -999999, 'normalize' ); + } +}); + +tape( 'if the `mode` is `normalize`, the function throws if provided a linear index which exceeds array dimensions (order=column-major)', function test( t ) { + var strides; + var offset; + var order; + var shape; + + shape = [ 2, 2 ]; + order = 'column-major'; + strides = [ 1, 2 ]; + offset = 0; + + t.throws( foo, RangeError, 'throws a range error' ); + t.throws( bar, RangeError, 'throws a range error' ); + + t.end(); + + function foo() { + vind2bind( shape, strides, offset, order, 999999, 'normalize' ); + } + + function bar() { + vind2bind( shape, strides, offset, order, -999999, 'normalize' ); + } +}); + tape( 'if the `mode` is `wrap`, the function wraps a linear index which exceeds array dimensions (offset=0,order=row-major)', function test( t ) { var strides; var offset; @@ -575,3 +625,90 @@ tape( 'if the `mode` is `clamp`, the function clamps a linear index which exceed t.end(); }); + +tape( 'if the `mode` is `normalize`, the function normalizes negative linear indices (order=row-major)', function test( t ) { + var strides; + var offset; + var order; + var shape; + var idx; + + shape = [ 2, 2 ]; + order = 'row-major'; + strides = [ -2, 1 ]; + offset = 2; + + // Iteration order => [ 2, 3, 0, 1 ] + + idx = vind2bind( shape, strides, offset, order, 3, 'normalize' ); + t.strictEqual( idx, 1, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, 0, 'normalize' ); + t.strictEqual( idx, 2, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, -4, 'normalize' ); + t.strictEqual( idx, 2, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, -1, 'normalize' ); + t.strictEqual( idx, 1, 'returns expected value' ); + + t.end(); +}); + +tape( 'if the `mode` is `normalize`, the function normalizes negative linear indices (offset=0,order=column-major)', function test( t ) { + var strides; + var offset; + var order; + var shape; + var idx; + + shape = [ 2, 2 ]; + order = 'column-major'; + strides = [ 1, 2 ]; + offset = 0; + + // Iteration order => [ 0, 1, 2, 3 ] + + idx = vind2bind( shape, strides, offset, order, 3, 'normalize' ); + t.strictEqual( idx, 3, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, 0, 'normalize' ); + t.strictEqual( idx, 0, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, -4, 'normalize' ); + t.strictEqual( idx, 0, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, -3, 'normalize' ); + t.strictEqual( idx, 1, 'returns expected value' ); + + t.end(); +}); + +tape( 'if the `mode` is `normalize`, the function normalizes negative linear indices (order=column-major)', function test( t ) { + var strides; + var offset; + var order; + var shape; + var idx; + + shape = [ 2, 2 ]; + order = 'column-major'; + strides = [ 1, -2 ]; + offset = 2; + + // Iteration order => [ 2, 3, 0, 1 ] + + idx = vind2bind( shape, strides, offset, order, 3, 'normalize' ); + t.strictEqual( idx, 1, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, 0, 'normalize' ); + t.strictEqual( idx, 2, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, -4, 'normalize' ); + t.strictEqual( idx, 2, 'returns expected value' ); + + idx = vind2bind( shape, strides, offset, order, -1, 'normalize' ); + t.strictEqual( idx, 1, 'returns expected value' ); + + t.end(); +});