GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
sub2ind.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2009-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <utility>
31 
32 #include "Array-util.h"
33 #include "oct-locbuf.h"
34 #include "quit.h"
35 
36 #include "defun.h"
37 #include "error.h"
38 #include "errwarn.h"
39 #include "ovl.h"
40 
42 
43 static dim_vector
44 get_dim_vector (const octave_value& val, const char *name)
45 {
46  RowVector dimsv = val.row_vector_value (false, true);
47  dim_vector dv;
48  octave_idx_type n = dimsv.numel ();
49 
50  if (n < 1)
51  error ("%s: dimension vector DIMS must not be empty", name);
52 
53  dv.resize (std::max (n, static_cast<octave_idx_type> (2)));
54  dv(1) = 1;
55  for (octave_idx_type i = 0; i < n; i++)
56  {
57  octave_idx_type ii = dimsv(i);
58  if (ii == dimsv(i) && ii >= 0)
59  dv(i) = ii;
60  else
61  error ("%s: dimension vector DIMS must contain integers", name);
62  }
63 
64  return dv;
65 }
66 
67 DEFUN (sub2ind, args, ,
68  doc: /* -*- texinfo -*-
69 @deftypefn {} {@var{ind} =} sub2ind (@var{dims}, @var{i}, @var{j})
70 @deftypefnx {} {@var{ind} =} sub2ind (@var{dims}, @var{s1}, @var{s2}, @dots{}, @var{sN})
71 Convert subscripts to linear indices.
72 
73 The input @var{dims} is a dimension vector where each element is the size of
74 the array in the respective dimension (@pxref{XREFsize,,@code{size}}). The
75 remaining inputs are scalars or vectors of subscripts to be converted.
76 
77 The output vector @var{ind} contains the converted linear indices.
78 
79 Background: Array elements can be specified either by a linear index which
80 starts at 1 and runs through the number of elements in the array, or they may
81 be specified with subscripts for the row, column, page, etc. The functions
82 @code{ind2sub} and @code{sub2ind} interconvert between the two forms.
83 
84 The linear index traverses dimension 1 (rows), then dimension 2 (columns), then
85 dimension 3 (pages), etc.@: until it has numbered all of the elements.
86 Consider the following 3-by-3 matrices:
87 
88 @example
89 @group
90 [(1,1), (1,2), (1,3)] [1, 4, 7]
91 [(2,1), (2,2), (2,3)] ==> [2, 5, 8]
92 [(3,1), (3,2), (3,3)] [3, 6, 9]
93 @end group
94 @end example
95 
96 @noindent
97 The left matrix contains the subscript tuples for each matrix element. The
98 right matrix shows the linear indices for the same matrix.
99 
100 The following example shows how to convert the two-dimensional indices
101 @code{(2,1)} and @code{(2,3)} of a 3-by-3 matrix to linear indices with a
102 single call to @code{sub2ind}.
103 
104 @example
105 @group
106 s1 = [2, 2];
107 s2 = [1, 3];
108 ind = sub2ind ([3, 3], s1, s2)
109  @result{} ind = 2 8
110 @end group
111 @end example
112 @seealso{ind2sub, size}
113 @end deftypefn */)
114 {
115  int nargin = args.length ();
116 
117  if (nargin < 2)
118  print_usage ();
119 
120  dim_vector dv = get_dim_vector (args(0), "sub2ind");
121 
122  Array<idx_vector> idxa (dim_vector (nargin-1, 1));
123 
124  for (int j = 0; j < nargin - 1; j++)
125  {
126  if (! args(j+1).isnumeric ())
127  error ("sub2ind: subscripts must be numeric");
128 
129  try
130  {
131  idxa(j) = args(j+1).index_vector ();
132 
133  if (j > 0 && args(j+1).dims () != args(1).dims ())
134  error ("sub2ind: all subscripts must be of the same size");
135  }
136  catch (index_exception& ie)
137  {
138  ie.set_pos_if_unset (nargin-1, j+1);
139  ie.set_var ();
140  std::string msg = ie.message ();
141  error_with_id (ie.err_id (), "%s", msg.c_str ());
142  }
143  }
144 
145  return ovl (sub2ind (dv, idxa));
146 }
147 
148 /*
149 ## Test evaluation
150 %!test
151 %! s1 = [ 1 1 1 1 ; 2 2 2 2 ];
152 %! s2 = [ 1 1 2 2 ; 1 1 2 2 ];
153 %! s3 = [ 1 2 1 2 ; 1 2 1 2 ];
154 %! in = [ 1 101 11 111 ; 2 102 12 112 ];
155 %! assert (sub2ind ([10 10 10], s1, s2, s3), in);
156 
157 # Test low index
158 %!assert (sub2ind ([10 10 10], 1, 1, 1), 1)
159 %!error <index \‍(0,_,_\‍)> sub2ind ([10 10 10], 0, 1, 1)
160 %!error <index \‍(_,0,_\‍)> sub2ind ([10 10 10], 1, 0, 1)
161 %!error <index \‍(_,_,0\‍)> sub2ind ([10 10 10], 1, 1, 0)
162 
163 # Test high index
164 %!assert (sub2ind ([10 10 10], 10, 10, 10), 1000)
165 %!error <index \‍(11,_,_\‍): out of bound 10> sub2ind ([10 10 10], 11, 10, 10)
166 %!error <index \‍(_,11,_\‍): out of bound 10> sub2ind ([10 10 10], 10, 11, 10)
167 %!error <index \‍(_,_,11\‍): out of bound 10> sub2ind ([10 10 10], 10, 10, 11)
168 
169 # Test high index in the trailing dimensions
170 %!assert (sub2ind ([10, 1], 2, 1, 1), 2)
171 %!error <index \‍(_,2,_\‍): out of bound 1> sub2ind ([10, 1], 1, 2, 1)
172 %!error <index \‍(_,_,2\‍): out of bound 1> sub2ind ([10, 1], 1, 1, 2)
173 %!assert (sub2ind ([10 10], 2, 2, 1), 12)
174 %!error <index \‍(_,_,2\‍): out of bound 1> sub2ind ([10 10], 2, 1, 2)
175 %!error <index \‍(_,_,2\‍): out of bound 1> sub2ind ([10 10], 1, 2, 2)
176 
177 # Test handling of empty arguments
178 %!assert (sub2ind ([10 10], zeros (0,0), zeros (0,0)), zeros (0,0))
179 %!assert (sub2ind ([10 10], zeros (2,0), zeros (2,0)), zeros (2,0))
180 %!assert (sub2ind ([10 10], zeros (0,2), zeros (0,2)), zeros (0,2))
181 %!error <all subscripts .* same size> sub2ind ([10 10 10], zeros (0,2), zeros (2,0))
182 
183 # Test handling of arguments of different size
184 %!error <all subscripts .* same size> sub2ind ([10 10], ones (1,2), ones (1,3))
185 %!error <all subscripts .* same size> sub2ind ([10 10], ones (1,2), ones (2,1))
186 
187 ## Test input validation
188 %!error <dimension vector> sub2ind ([10 10.5], 1, 1)
189 %!error <index \‍(1.5,_\‍)> sub2ind ([10 10], 1.5, 1)
190 %!error <index \‍(_,1.5\‍)> sub2ind ([10 10], 1, 1.5)
191 */
192 
193 DEFUN (ind2sub, args, nargout,
194  doc: /* -*- texinfo -*-
195 @deftypefn {} {[@var{s1}, @var{s2}, @dots{}, @var{sN}] =} ind2sub (@var{dims}, @var{ind})
196 Convert linear indices to subscripts.
197 
198 The input @var{dims} is a dimension vector where each element is the size of
199 the array in the respective dimension (@pxref{XREFsize,,@code{size}}). The
200 second input @var{ind} contains linear indices to be converted.
201 
202 The outputs @var{s1}, @dots{}, @var{sN} contain the converted subscripts.
203 
204 Background: Array elements can be specified either by a linear index which
205 starts at 1 and runs through the number of elements in the array, or they may
206 be specified with subscripts for the row, column, page, etc. The functions
207 @code{ind2sub} and @code{sub2ind} interconvert between the two forms.
208 
209 The linear index traverses dimension 1 (rows), then dimension 2 (columns), then
210 dimension 3 (pages), etc.@: until it has numbered all of the elements.
211 Consider the following 3-by-3 matrices:
212 
213 @example
214 @group
215 [1, 4, 7] [(1,1), (1,2), (1,3)]
216 [2, 5, 8] ==> [(2,1), (2,2), (2,3)]
217 [3, 6, 9] [(3,1), (3,2), (3,3)]
218 @end group
219 @end example
220 
221 @noindent
222 The left matrix contains the linear indices for each matrix element. The right
223 matrix shows the subscript tuples for the same matrix.
224 
225 The following example shows how to convert the linear indices @code{2} and
226 @code{8} to appropriate subscripts of a 3-by-3 matrix.
227 
228 @example
229 @group
230 ind = [2, 8];
231 [r, c] = ind2sub ([3, 3], ind)
232  @result{} r = 2 2
233  @result{} c = 1 3
234 @end group
235 @end example
236 
237 If the number of output subscripts exceeds the number of dimensions, the
238 exceeded dimensions are set to @code{1}. On the other hand, if fewer
239 subscripts than dimensions are provided, the exceeding dimensions are merged
240 into the final requested dimension. For clarity, consider the following
241 examples:
242 
243 @example
244 @group
245 ind = [2, 8];
246 dims = [3, 3];
247 ## same as dims = [3, 3, 1]
248 [r, c, s] = ind2sub (dims, ind)
249  @result{} r = 2 2
250  @result{} c = 1 3
251  @result{} s = 1 1
252 ## same as dims = [9]
253 r = ind2sub (dims, ind)
254  @result{} r = 2 8
255 @end group
256 @end example
257 @seealso{sub2ind, size}
258 @end deftypefn */)
259 {
260  if (args.length () != 2)
261  print_usage ();
262 
263  octave_value_list retval;
264 
265  int nd = (nargout == 0) ? 1 : nargout;
266 
267  dim_vector dv = get_dim_vector (args(0), "ind2sub").redim (nargout);
268 
269  // Redim for 1 will give us a column vector but we want a row vector.
270  if (nd == 1)
271  std::swap (dv(0), dv(1));
272 
273  try
274  {
275  retval = Array<octave_value> (ind2sub (dv, args(1).index_vector ()));
276 
277  if (nd == 1)
278  retval(0) = retval(1);
279  }
280  catch (const index_exception& ie)
281  {
282  error ("ind2sub: invalid index %s", ie.what ());
283  }
284 
285  return retval;
286 }
287 
288 /*
289 ## Examples
290 %!test
291 %! [r, c] = ind2sub ([3, 3], [2, 8]);
292 %! assert (r, [2, 2]);
293 %! assert (c, [1, 3]);
294 
295 %!test
296 %! [r, c, s] = ind2sub ([3, 3], [2, 8]);
297 %! assert (r, [2, 2]);
298 %! assert (c, [1, 3]);
299 %! assert (s, [1, 1]);
300 %! [r, c, s] = ind2sub ([3, 3, 1], [2, 8]);
301 %! assert (r, [2, 2]);
302 %! assert (c, [1, 3]);
303 %! assert (s, [1, 1]);
304 
305 %!test
306 %! r = ind2sub ([3, 3], [2, 8]);
307 %! assert (r, [2, 8]);
308 %! r = ind2sub (9, [2, 8]);
309 %! assert (r, [2, 8]);
310 
311 ## 3-dimensional test
312 %!test
313 %! [r, c, s] = ind2sub ([2, 2, 2], 1:8);
314 %! assert (r, [1, 2, 1, 2, 1, 2, 1, 2]);
315 %! assert (c, [1, 1, 2, 2, 1, 1, 2, 2]);
316 %! assert (s, [1, 1, 1, 1, 2, 2, 2, 2]);
317 %! [r, c] = ind2sub ([2, 2, 2], 1:8);
318 %! assert (r, [1, 2, 1, 2, 1, 2, 1, 2]);
319 %! assert (c, [1, 1, 2, 2, 3, 3, 4, 4]);
320 %! r = ind2sub ([2, 2, 2], 1:8);
321 %! assert (r, 1:8);
322 
323 ## Indexing beyond specified size (bug #62184)
324 %!assert <*62184> (ind2sub (1, 2), 2)
325 %!assert <*62184> (ind2sub ([3,3], 10), 10)
326 %!test <*62184>
327 %! [r,c] = ind2sub ([3,3], 10);
328 %! assert ([r, c], [1, 4]);
329 %!test <*62184>
330 %! [r,c,p] = ind2sub ([3,3], 10);
331 %! assert ([r, c, p], [1, 1, 2]);
332 
333 ## Test input validation
334 %!error <DIMS must contain integers> ind2sub ([2, -2], 3)
335 %!error <invalid index> ind2sub ([2, 2, 2], -1:8)
336 */
337 
338 OCTAVE_END_NAMESPACE(octave)
octave::idx_vector sub2ind(const dim_vector &dv, const Array< octave::idx_vector > &idxa)
Definition: Array-util.cc:503
Array< octave::idx_vector > ind2sub(const dim_vector &dv, const octave::idx_vector &idx)
Definition: Array-util.cc:598
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:130
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
void resize(int n, int fill_value=0)
Definition: dim-vector.h:272
dim_vector redim(int n) const
Force certain dimensionality, preserving numel ().
Definition: dim-vector.cc:226
void set_pos_if_unset(octave_idx_type nd_arg, octave_idx_type dim_arg)
virtual const char * err_id() const =0
void set_var(const std::string &var_arg="")
RowVector row_vector_value(bool frc_str_conv=false, bool frc_vec_conv=false) const
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1033
void() error(const char *fmt,...)
Definition: error.cc:988
octave_idx_type n
Definition: mx-inlines.cc:761
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219