GNU Octave  6.2.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-2021 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 "Array-util.h"
31 #include "oct-locbuf.h"
32 #include "quit.h"
33 
34 #include "defun.h"
35 #include "error.h"
36 #include "errwarn.h"
37 #include "ovl.h"
38 
39 
40 static dim_vector
41 get_dim_vector (const octave_value& val, const char *name)
42 {
43  RowVector dimsv = val.row_vector_value (false, true);
44  dim_vector dv;
45  octave_idx_type n = dimsv.numel ();
46 
47  if (n < 1)
48  error ("%s: dimension vector DIMS must not be empty", name);
49 
50  dv.resize (std::max (n, static_cast<octave_idx_type> (2)));
51  dv(1) = 1;
52  for (octave_idx_type i = 0; i < n; i++)
53  {
54  octave_idx_type ii = dimsv(i);
55  if (ii == dimsv(i) && ii >= 0)
56  dv(i) = ii;
57  else
58  error ("%s: dimension vector DIMS must contain integers", name);
59  }
60 
61  return dv;
62 }
63 
64 DEFUN (sub2ind, args, ,
65  doc: /* -*- texinfo -*-
66 @deftypefn {} {@var{ind} =} sub2ind (@var{dims}, @var{i}, @var{j})
67 @deftypefnx {} {@var{ind} =} sub2ind (@var{dims}, @var{s1}, @var{s2}, @dots{}, @var{sN})
68 Convert subscripts to linear indices.
69 
70 The input @var{dims} is a dimension vector where each element is the size of
71 the array in the respective dimension (@pxref{XREFsize,,size}). The remaining
72 inputs are scalars or vectors of subscripts to be converted.
73 
74 The output vector @var{ind} contains the converted linear indices.
75 
76 Background: Array elements can be specified either by a linear index which
77 starts at 1 and runs through the number of elements in the array, or they may
78 be specified with subscripts for the row, column, page, etc. The functions
79 @code{ind2sub} and @code{sub2ind} interconvert between the two forms.
80 
81 The linear index traverses dimension 1 (rows), then dimension 2 (columns), then
82 dimension 3 (pages), etc.@: until it has numbered all of the elements.
83 Consider the following 3-by-3 matrices:
84 
85 @example
86 @group
87 [(1,1), (1,2), (1,3)] [1, 4, 7]
88 [(2,1), (2,2), (2,3)] ==> [2, 5, 8]
89 [(3,1), (3,2), (3,3)] [3, 6, 9]
90 @end group
91 @end example
92 
93 @noindent
94 The left matrix contains the subscript tuples for each matrix element. The
95 right matrix shows the linear indices for the same matrix.
96 
97 The following example shows how to convert the two-dimensional indices
98 @code{(2,1)} and @code{(2,3)} of a 3-by-3 matrix to linear indices with a
99 single call to @code{sub2ind}.
100 
101 @example
102 @group
103 s1 = [2, 2];
104 s2 = [1, 3];
105 ind = sub2ind ([3, 3], s1, s2)
106  @result{} ind = 2 8
107 @end group
108 @end example
109 @seealso{ind2sub, size}
110 @end deftypefn */)
111 {
112  int nargin = args.length ();
113 
114  if (nargin < 2)
115  print_usage ();
116 
117  dim_vector dv = get_dim_vector (args(0), "sub2ind");
118 
119  Array<idx_vector> idxa (dim_vector (nargin-1, 1));
120 
121  for (int j = 0; j < nargin - 1; j++)
122  {
123  if (! args(j+1).isnumeric ())
124  error ("sub2ind: subscripts must be numeric");
125 
126  try
127  {
128  idxa(j) = args(j+1).index_vector ();
129 
130  if (j > 0 && args(j+1).dims () != args(1).dims ())
131  error ("sub2ind: all subscripts must be of the same size");
132  }
133  catch (octave::index_exception& e)
134  {
135  e.set_pos_if_unset (nargin-1, j+1);
136  e.set_var ();
137  std::string msg = e.message ();
138  error_with_id (e.err_id (), "%s", msg.c_str ());
139  }
140  }
141 
142  return ovl (sub2ind (dv, idxa));
143 }
144 
145 /*
146 ## Test evaluation
147 %!test
148 %! s1 = [ 1 1 1 1 ; 2 2 2 2 ];
149 %! s2 = [ 1 1 2 2 ; 1 1 2 2 ];
150 %! s3 = [ 1 2 1 2 ; 1 2 1 2 ];
151 %! in = [ 1 101 11 111 ; 2 102 12 112 ];
152 %! assert (sub2ind ([10 10 10], s1, s2, s3), in);
153 
154 # Test low index
155 %!assert (sub2ind ([10 10 10], 1, 1, 1), 1)
156 %!error <index \‍(0,_,_\‍)> sub2ind ([10 10 10], 0, 1, 1)
157 %!error <index \‍(_,0,_\‍)> sub2ind ([10 10 10], 1, 0, 1)
158 %!error <index \‍(_,_,0\‍)> sub2ind ([10 10 10], 1, 1, 0)
159 
160 # Test high index
161 %!assert (sub2ind ([10 10 10], 10, 10, 10), 1000)
162 %!error <index \‍(11,_,_\‍): out of bound 10> sub2ind ([10 10 10], 11, 10, 10)
163 %!error <index \‍(_,11,_\‍): out of bound 10> sub2ind ([10 10 10], 10, 11, 10)
164 %!error <index \‍(_,_,11\‍): out of bound 10> sub2ind ([10 10 10], 10, 10, 11)
165 
166 # Test high index in the trailing dimensions
167 %!assert (sub2ind ([10, 1], 2, 1, 1), 2)
168 %!error <index \‍(_,2,_\‍): out of bound 1> sub2ind ([10, 1], 1, 2, 1)
169 %!error <index \‍(_,_,2\‍): out of bound 1> sub2ind ([10, 1], 1, 1, 2)
170 %!assert (sub2ind ([10 10], 2, 2, 1), 12)
171 %!error <index \‍(_,_,2\‍): out of bound 1> sub2ind ([10 10], 2, 1, 2)
172 %!error <index \‍(_,_,2\‍): out of bound 1> sub2ind ([10 10], 1, 2, 2)
173 
174 # Test handling of empty arguments
175 %!assert (sub2ind ([10 10], zeros (0,0), zeros (0,0)), zeros (0,0))
176 %!assert (sub2ind ([10 10], zeros (2,0), zeros (2,0)), zeros (2,0))
177 %!assert (sub2ind ([10 10], zeros (0,2), zeros (0,2)), zeros (0,2))
178 %!error <all subscripts .* same size> sub2ind ([10 10 10], zeros (0,2), zeros (2,0))
179 
180 # Test handling of arguments of different size
181 %!error <all subscripts .* same size> sub2ind ([10 10], ones (1,2), ones (1,3))
182 %!error <all subscripts .* same size> sub2ind ([10 10], ones (1,2), ones (2,1))
183 
184 ## Test input validation
185 %!error <dimension vector> sub2ind ([10 10.5], 1, 1)
186 %!error <index \‍(1.5,_\‍)> sub2ind ([10 10], 1.5, 1)
187 %!error <index \‍(_,1.5\‍)> sub2ind ([10 10], 1, 1.5)
188 */
189 
190 DEFUN (ind2sub, args, nargout,
191  doc: /* -*- texinfo -*-
192 @deftypefn {} {[@var{s1}, @var{s2}, @dots{}, @var{sN}] =} ind2sub (@var{dims}, @var{ind})
193 Convert linear indices to subscripts.
194 
195 The input @var{dims} is a dimension vector where each element is the size of
196 the array in the respective dimension (@pxref{XREFsize,,size}). The second
197 input @var{ind} contains linear indies to be converted.
198 
199 The outputs @var{s1}, @dots{}, @var{sN} contain the converted subscripts.
200 
201 Background: Array elements can be specified either by a linear index which
202 starts at 1 and runs through the number of elements in the array, or they may
203 be specified with subscripts for the row, column, page, etc. The functions
204 @code{ind2sub} and @code{sub2ind} interconvert between the two forms.
205 
206 The linear index traverses dimension 1 (rows), then dimension 2 (columns), then
207 dimension 3 (pages), etc.@: until it has numbered all of the elements.
208 Consider the following 3-by-3 matrices:
209 
210 @example
211 @group
212 [1, 4, 7] [(1,1), (1,2), (1,3)]
213 [2, 5, 8] ==> [(2,1), (2,2), (2,3)]
214 [3, 6, 9] [(3,1), (3,2), (3,3)]
215 @end group
216 @end example
217 
218 @noindent
219 The left matrix contains the linear indices for each matrix element. The right
220 matrix shows the subscript tuples for the same matrix.
221 
222 The following example shows how to convert the two-dimensional indices
223 @code{(2,1)} and @code{(2,3)} of a 3-by-3 matrix to linear indices with a
224 single call to @code{sub2ind}.
225 
226 The following example shows how to convert the linear indices @code{2} and
227 @code{8} in a 3-by-3 matrix into subscripts.
228 
229 @example
230 @group
231 ind = [2, 8];
232 [r, c] = ind2sub ([3, 3], ind)
233  @result{} r = 2 2
234  @result{} c = 1 3
235 @end group
236 @end example
237 
238 If the number of output subscripts exceeds the number of dimensions, the
239 exceeded dimensions are set to @code{1}. On the other hand, if fewer
240 subscripts than dimensions are provided, the exceeding dimensions are merged
241 into the final requested dimension. For clarity, consider the following
242 examples:
243 
244 @example
245 @group
246 ind = [2, 8];
247 dims = [3, 3];
248 ## same as dims = [3, 3, 1]
249 [r, c, s] = ind2sub (dims, ind)
250  @result{} r = 2 2
251  @result{} c = 1 3
252  @result{} s = 1 1
253 ## same as dims = [9]
254 r = ind2sub (dims, ind)
255  @result{} r = 2 8
256 @end group
257 @end example
258 @seealso{ind2sub, size}
259 @end deftypefn */)
260 {
261  if (args.length () != 2)
262  print_usage ();
263 
265 
266  // Redimension to provided number of subscripts.
267  dim_vector dv = get_dim_vector (args(0), "ind2sub").redim (nargout);
268 
269  try
270  {
271  retval = Array<octave_value> (ind2sub (dv, args(1).index_vector ()));
272  }
273  catch (const octave::index_exception& e)
274  {
275  error ("ind2sub: invalid index %s", e.what ());
276  }
277 
278  return retval;
279 }
280 
281 /*
282 ## Examples
283 %!test
284 %! [r, c] = ind2sub ([3, 3], [2, 8]);
285 %! assert (r, [2, 2]);
286 %! assert (c, [1, 3]);
287 
288 %!test
289 %! [r, c, s] = ind2sub ([3, 3], [2, 8]);
290 %! assert (r, [2, 2]);
291 %! assert (c, [1, 3]);
292 %! assert (s, [1, 1]);
293 %! [r, c, s] = ind2sub ([3, 3, 1], [2, 8]);
294 %! assert (r, [2, 2]);
295 %! assert (c, [1, 3]);
296 %! assert (s, [1, 1]);
297 
298 %!test
299 %! r = ind2sub ([3, 3], [2, 8]);
300 %! assert (r, [2, 8]);
301 %! r = ind2sub (9, [2, 8]);
302 %! assert (r, [2, 8]);
303 
304 ## 3-dimensional test
305 %!test
306 %! [r, c, s] = ind2sub ([2, 2, 2], 1:8);
307 %! assert (r, [1, 2, 1, 2, 1, 2, 1, 2]);
308 %! assert (c, [1, 1, 2, 2, 1, 1, 2, 2]);
309 %! assert (s, [1, 1, 1, 1, 2, 2, 2, 2]);
310 %! [r, c] = ind2sub ([2, 2, 2], 1:8);
311 %! assert (r, [1, 2, 1, 2, 1, 2, 1, 2]);
312 %! assert (c, [1, 1, 2, 2, 3, 3, 4, 4]);
313 %! r = ind2sub ([2, 2, 2], 1:8);
314 %! assert (r, 1:8);
315 
316 %!error <DIMS must contain integers> ind2sub ([2, -2], 3);
317 %!error <index out of range> ind2sub ([2, 2, 2], 1:9);
318 %!error <invalid index> ind2sub ([2, 2, 2], -1:8);
319 */
template class OCTINTERP_API Array< octave_value >
Definition: Array-tc.cc:55
Array< idx_vector > ind2sub(const dim_vector &dv, const idx_vector &idx)
Definition: Array-util.cc:620
idx_vector sub2ind(const dim_vector &dv, const Array< idx_vector > &idxa)
Definition: Array-util.cc:535
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:128
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
void resize(int n, int fill_value=0)
Definition: dim-vector.h:349
dim_vector redim(int n) const
Force certain dimensionality, preserving numel ().
Definition: dim-vector.cc:245
void set_pos_if_unset(octave_idx_type nd_arg, octave_idx_type dim_arg)
virtual const char * err_id(void) 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
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#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:1013
void error(const char *fmt,...)
Definition: error.cc:968
QString name
octave_idx_type n
Definition: mx-inlines.cc:753
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
static dim_vector get_dim_vector(const octave_value &val, const char *name)
Definition: sub2ind.cc:41