GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
sub2ind.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2009-2025 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
43static dim_vector
44get_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
67DEFUN (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})
71Convert subscripts to linear indices.
72
73The input @var{dims} is a dimension vector where each element is the size of
74the array in the respective dimension (@pxref{XREFsize,,@code{size}}). The
75remaining inputs are scalars or vectors of subscripts to be converted.
76
77The output vector @var{ind} contains the converted linear indices.
78
79Background: Array elements can be specified either by a linear index which
80starts at 1 and runs through the number of elements in the array, or they may
81be specified with subscripts for the row, column, page, etc. The functions
82@code{ind2sub} and @code{sub2ind} interconvert between the two forms.
83
84The linear index traverses dimension 1 (rows), then dimension 2 (columns), then
85dimension 3 (pages), etc.@: until it has numbered all of the elements.
86Consider 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
97The left matrix contains the subscript tuples for each matrix element. The
98right matrix shows the linear indices for the same matrix.
99
100The 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
102single call to @code{sub2ind}.
103
104@example
105@group
106s1 = [2, 2];
107s2 = [1, 3];
108ind = 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 const 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
193DEFUN (ind2sub, args, nargout,
194 doc: /* -*- texinfo -*-
195@deftypefn {} {[@var{s1}, @var{s2}, @dots{}, @var{sN}] =} ind2sub (@var{dims}, @var{ind})
196Convert linear indices to subscripts.
197
198The input @var{dims} is a dimension vector where each element is the size of
199the array in the respective dimension (@pxref{XREFsize,,@code{size}}). The
200second input @var{ind} contains linear indices to be converted.
201
202The outputs @var{s1}, @dots{}, @var{sN} contain the converted subscripts.
203
204Background: Array elements can be specified either by a linear index which
205starts at 1 and runs through the number of elements in the array, or they may
206be specified with subscripts for the row, column, page, etc. The functions
207@code{ind2sub} and @code{sub2ind} interconvert between the two forms.
208
209The linear index traverses dimension 1 (rows), then dimension 2 (columns), then
210dimension 3 (pages), etc.@: until it has numbered all of the elements.
211Consider 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
222The left matrix contains the linear indices for each matrix element. The right
223matrix shows the subscript tuples for the same matrix.
224
225The 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
230ind = [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
237If the number of output subscripts exceeds the number of dimensions, the
238exceeded dimensions are set to @code{1}. On the other hand, if fewer
239subscripts than dimensions are provided, the exceeding dimensions are merged
240into the final requested dimension. For clarity, consider the following
241examples:
242
243@example
244@group
245ind = [2, 8];
246dims = [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]
253r = 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
338OCTAVE_END_NAMESPACE(octave)
octave::idx_vector sub2ind(const dim_vector &dv, const Array< octave::idx_vector > &idxa)
Array< octave::idx_vector > ind2sub(const dim_vector &dv, const octave::idx_vector &idx)
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:418
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
void resize(int n, int fill_value=0)
Definition dim-vector.h:268
dim_vector redim(int n) const
Force certain dimensionality, preserving numel ().
int length() const
Number of dimensions.
Definition dim-vector.h:262
void set_pos_if_unset(octave_idx_type nd_arg, octave_idx_type dim_arg)
void set_var(const std::string &var_arg="")
virtual const char * err_id() const =0
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()
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:1048
void error(const char *fmt,...)
Definition error.cc:1003
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217