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