changeset 30902:972959edc3ff

Allow sub2ind() to accept indices outside the size of the input subscripts (bug #62184) * NEWS.8.md: Announce change in Matlab Compatibility section. * sub2ind.cc: Add '#include "utility"' for access to std::swap. * sub2ind.cc (Fsub2ind): Check nargout to figure out ndims of output. For special case of vector (1-dimension), put in code to guarantee a row vector output. Add BIST tests for bug #62184. Remove input validation BIST which no longer applies. * Array-util.cc (ind2sub): Remove input validation requiring index to be within range of subscript size. Adjust code to put all remaining elements in the final output dimension.
author John W. Eaton <jwe@octave.org>
date Tue, 05 Apr 2022 15:12:34 -0700
parents f6c74e01e294
children 062e00e903a5
files etc/NEWS.8.md libinterp/corefcn/sub2ind.cc liboctave/array/Array-util.cc
diffstat 3 files changed, 31 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/etc/NEWS.8.md	Tue Apr 05 13:54:18 2022 -0700
+++ b/etc/NEWS.8.md	Tue Apr 05 15:12:34 2022 -0700
@@ -17,6 +17,9 @@
 
 ### Matlab compatibility
 
+- `sub2ind` now supports index values outside of the size specified by
+  the subscripts.
+
 - `cylinder` now accepts a scalar for the radius argument.
 
 - `clock` now has an optional second output `ISDST` which indicates if
--- a/libinterp/corefcn/sub2ind.cc	Tue Apr 05 13:54:18 2022 -0700
+++ b/libinterp/corefcn/sub2ind.cc	Tue Apr 05 15:12:34 2022 -0700
@@ -27,6 +27,8 @@
 #  include "config.h"
 #endif
 
+#include <utility>
+
 #include "Array-util.h"
 #include "oct-locbuf.h"
 #include "quit.h"
@@ -260,12 +262,20 @@
 
   octave_value_list retval;
 
-  // Redimension to provided number of subscripts.
+  int nd = (nargout == 0) ? 1 : nargout;
+
   dim_vector dv = get_dim_vector (args(0), "ind2sub").redim (nargout);
 
+  // Redim for 1 will give us a column vector but we want a row vector.
+  if (nd == 1)
+    std::swap (dv(0), dv(1));
+
   try
     {
       retval = Array<octave_value> (ind2sub (dv, args(1).index_vector ()));
+
+      if (nd == 1)
+        retval(0) = retval(1);
     }
   catch (const index_exception& ie)
     {
@@ -310,8 +320,18 @@
 %! r = ind2sub ([2, 2, 2], 1:8);
 %! assert (r, 1:8);
 
+## Indexing beyond specified size (bug #62184)
+%!assert <*62184> (ind2sub (1, 2), 2)
+%!assert <*62184> (ind2sub ([3,3], 10), 10)
+%!test <*62184>
+%! [r,c] = ind2sub ([3,3], 10);
+%! assert ([r, c], [1, 4]);
+%!test <*62184>
+%! [r,c,p] = ind2sub ([3,3], 10);
+%! assert ([r, c, p], [1, 1, 2]);
+
+## Test input validation
 %!error <DIMS must contain integers> ind2sub ([2, -2], 3)
-%!error <index out of range> ind2sub ([2, 2, 2], 1:9)
 %!error <invalid index> ind2sub ([2, 2, 2], -1:8)
 */
 
--- a/liboctave/array/Array-util.cc	Tue Apr 05 13:54:18 2022 -0700
+++ b/liboctave/array/Array-util.cc	Tue Apr 05 15:12:34 2022 -0700
@@ -623,17 +623,16 @@
   Array<octave::idx_vector> retval (dim_vector (n, 1));
   octave_idx_type numel = dv.numel ();
 
-  if (idx.extent (numel) > numel)
-    (*current_liboctave_error_handler) ("ind2sub: index out of range");
-
   if (idx.is_scalar ())
     {
       octave_idx_type k = idx(0);
-      for (octave_idx_type j = 0; j < n; j++)
+      for (octave_idx_type j = 0; j < n-1; j++)
         {
           retval(j) = k % dv(j);
           k /= dv(j);
         }
+
+      retval(n-1) = idx(0) < numel ? k % dv(n-1) : k;
     }
   else
     {
@@ -646,11 +645,13 @@
       for (octave_idx_type i = 0; i < len; i++)
         {
           octave_idx_type k = idx(i);
-          for (octave_idx_type j = 0; j < n; j++)
+          for (octave_idx_type j = 0; j < n-1; j++)
             {
               rdata[j](i) = k % dv(j);
               k /= dv(j);
             }
+
+          rdata[n-1](i) = idx(i) < numel ? k % dv(n-1) : k;
         }
 
       for (octave_idx_type j = 0; j < n; j++)