GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
dim-vector.h
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2003-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // or <https://octave.org/copyright/>.
7 //
8 // Copyirght (C) 2009, 2010 VZLU Prague
9 //
10 // This file is part of Octave.
11 //
12 // Octave is free software: you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by
14 // the Free Software Foundation, either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // Octave is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with Octave; see the file COPYING. If not, see
24 // <https://www.gnu.org/licenses/>.
25 //
26 ////////////////////////////////////////////////////////////////////////
27 
28 #if ! defined (octave_dim_vector_h)
29 #define octave_dim_vector_h 1
30 
31 #include "octave-config.h"
32 
33 #include <cassert>
34 
35 #include <algorithm>
36 #include <initializer_list>
37 #include <string>
38 
39 #include "Array-fwd.h"
40 #include "oct-atomic.h"
41 #include "oct-refcount.h"
42 
43 //! Vector representing the dimensions (size) of an Array.
44 //!
45 //! A dim_vector is used to represent dimensions of an Array. It is used
46 //! on its constructor to specify its size, or when reshaping it.
47 //!
48 //! @code{.cc}
49 //! // Matrix with 10 rows and 20 columns.
50 //! Matrix m Matrix (dim_vector (10, 20));
51 //!
52 //! // Change its size to 5 rows and 40 columns.
53 //! Matrix m2 = m.reshape (dim_vector (5, 40));
54 //!
55 //! // Five dimensional Array of length 10, 20, 3, 8, 7 on each dimension.
56 //! NDArray a (dim_vector (10, 20, 3, 8, 7));
57 //!
58 //! // Uninitialized array of same size as other.
59 //! NDArray b (a.dims ());
60 //! @endcode
61 //!
62 //! The main thing to understand about this class, is that methods such as
63 //! ndims() and numel(), return the value for an Array of these dimensions,
64 //! not the actual number of elements in the dim_vector.
65 //!
66 //! @code{.cc}
67 //! dim_vector d (10, 5, 3);
68 //! octave_idx_type n = d.numel (); // returns 150
69 //! octave_idx_type nd = d.ndims (); // returns 3
70 //! @endcode
71 //!
72 //! ## Implementation details ##
73 //!
74 //! This implementation is more tricky than Array, but the big plus is that
75 //! dim_vector requires only one allocation instead of two. It is (slightly)
76 //! patterned after GCC's basic_string implementation. rep is a pointer to an
77 //! array of memory, comprising count, length, and the data:
78 //!
79 //! @verbatim
80 //! <count>
81 //! <ndims>
82 //! rep --> <dims[0]>
83 //! <dims[1]>
84 //! ...
85 //! @endverbatim
86 //!
87 //! The inlines count(), ndims() recover this data from the rep. Note
88 //! that rep points to the beginning of dims to grant faster access
89 //! (reinterpret_cast is assumed to be an inexpensive operation).
90 
91 class
94 {
95 private:
96 
97  octave_idx_type m_num_dims;
98 
99  octave_idx_type *m_dims;
100 
101 public:
102 
103  //! Construct dim_vector for a N dimensional array.
104  //!
105  //! Each argument to constructor defines the length of an additional
106  //! dimension. A dim_vector always represents a minimum of 2 dimensions
107  //! (just like an Array has at least 2 dimensions) and there is no
108  //! upper limit on the number of dimensions.
109  //!
110  //! @code{.cc}
111  //! dim_vector dv (7, 5);
112  //! Matrix mat (dv);
113  //! @endcode
114  //!
115  //! The constructed dim_vector @c dv will have two elements, @f$[7, 5]@f$,
116  //! one for each dimension. It can then be used to construct a Matrix
117  //! with such dimensions, i.e., 7 rows and 5 columns.
118  //!
119  //! @code{.cc}
120  //! NDArray x (dim_vector (7, 5, 10));
121  //! @endcode
122  //!
123  //! This will construct a 3 dimensional NDArray of lengths 7, 5, and 10,
124  //! on the first, second, and third dimension (rows, columns, and pages)
125  //! respectively.
126  //!
127  //! Note that that there is no constructor that accepts only one
128  //! dimension length to avoid confusion. The source for such confusion
129  //! is that constructor could mean:
130  //! - a column vector, i.e., assume @f$[N, 1]@f$;
131  //! - a square matrix, i.e., as is common in Octave interpreter;
132  //! - support for a 1 dimensional Array (does not exist);
133  //!
134  //! Using r, c, and lengths... as arguments, allow us to check at compile
135  //! time that there's at least 2 dimensions specified, while maintaining
136  //! type safety.
137 
138  template <typename... Ints>
140  Ints... lengths)
141  : m_num_dims (2 + sizeof... (Ints)), m_dims (new octave_idx_type [m_num_dims])
142  {
143  std::initializer_list<octave_idx_type> all_lengths = {r, c, lengths...};
144  octave_idx_type *ptr = m_dims;
145  for (const octave_idx_type l: all_lengths)
146  *ptr++ = l;
147  }
148 
149  // Fast access with absolutely no checking
150 
151  octave_idx_type& xelem (int i) { return m_dims[i]; }
152 
153  octave_idx_type xelem (int i) const { return m_dims[i]; }
154 
155  // Safe access to to elements
156 
158  {
159  return xelem (i);
160  }
161 
162  octave_idx_type elem (int i) const { return xelem (i); }
163 
165  {
166  while (m_num_dims > 2 && xelem(m_num_dims-1) == 1)
167  m_num_dims--;
168  }
169 
170  OCTAVE_API void chop_all_singletons ();
171 
172 private:
173 
174  explicit dim_vector (octave_idx_type ndims)
175  : m_num_dims (ndims < 2 ? 2 : ndims), m_dims (new octave_idx_type [m_num_dims])
176  {
177  std::fill_n (m_dims, m_num_dims, 0);
178  }
179 
180 public:
181 
182  static OCTAVE_API octave_idx_type dim_max ();
183 
184  explicit dim_vector ()
185  : m_num_dims (2), m_dims (new octave_idx_type [m_num_dims])
186  {
187  std::fill_n (m_dims, m_num_dims, 0);
188  }
189 
190  dim_vector (const dim_vector& dv)
191  : m_num_dims (dv.m_num_dims), m_dims (new octave_idx_type [m_num_dims])
192  {
193  std::copy_n (dv.m_dims, m_num_dims, m_dims);
194  }
195 
197  : m_num_dims (0), m_dims (nullptr)
198  {
199  *this = std::move (dv);
200  }
201 
202  static dim_vector alloc (int n)
203  {
204  return dim_vector (n);
205  }
206 
207  dim_vector& operator = (const dim_vector& dv)
208  {
209  if (&dv != this)
210  {
211  delete [] m_dims;
212 
213  m_num_dims = dv.m_num_dims;
214  m_dims = new octave_idx_type [m_num_dims];
215 
216  std::copy_n (dv.m_dims, m_num_dims, m_dims);
217  }
218 
219  return *this;
220  }
221 
222  dim_vector& operator = (dim_vector&& dv)
223  {
224  if (&dv != this)
225  {
226  // Because we define a move constructor and a move assignment
227  // operator, m_dims may be a nullptr here. We should only need to
228  // protect the destructor in a similar way.
229 
230  delete [] m_dims;
231 
232  m_num_dims = dv.m_num_dims;
233  m_dims = dv.m_dims;
234 
235  dv.m_num_dims = 0;
236  dv.m_dims = nullptr;
237  }
238 
239  return *this;
240  }
241 
243  {
244  // Because we define a move constructor and a move assignment
245  // operator, m_dims may be a nullptr here. We should only need to
246  // protect the move assignment operator in a similar way.
247 
248  delete [] m_dims;
249  }
250 
251  //! Number of dimensions.
252  //!
253  //! Returns the number of dimensions of the dim_vector. This is number of
254  //! elements in the dim_vector including trailing singletons. It is also
255  //! the number of dimensions an Array with this dim_vector would have.
256 
257  octave_idx_type ndims () const { return m_num_dims; }
258 
259  //! Number of dimensions.
260  //! Synonymous with ndims().
261  //!
262  //! While this method is not officially deprecated, consider using ndims()
263  //! instead to avoid confusion. Array does not have length because of its
264  //! odd definition as length of the longest dimension.
265 
266  int length () const { return ndims (); }
267 
268  octave_idx_type& operator () (int i) { return elem (i); }
269 
270  octave_idx_type operator () (int i) const { return elem (i); }
271 
272  void resize (int n, int fill_value = 0)
273  {
274  if (n < 2)
275  n = 2;
276 
277  if (n == m_num_dims)
278  return;
279 
280  if (n < m_num_dims)
281  {
282  m_num_dims = n;
283  return;
284  }
285 
286  octave_idx_type *new_rep = new octave_idx_type [n];
287 
288  std::copy_n (m_dims, m_num_dims, new_rep);
289  std::fill_n (new_rep + m_num_dims, n - m_num_dims, fill_value);
290 
291  delete [] m_dims;
292 
293  m_dims = new_rep;
294 
295  m_num_dims = n;
296  }
297 
298  OCTAVE_API std::string str (char sep = 'x') const;
299 
300  bool all_zero () const
301  {
302  return std::all_of (m_dims, m_dims + ndims (),
303  [] (octave_idx_type dim) { return dim == 0; });
304  }
305 
306  bool empty_2d () const
307  {
308  return ndims () == 2 && (xelem (0) == 0 || xelem (1) == 0);
309  }
310 
311  bool zero_by_zero () const
312  {
313  return ndims () == 2 && xelem (0) == 0 && xelem (1) == 0;
314  }
315 
316  bool any_zero () const
317  {
318  return std::any_of (m_dims, m_dims + ndims (),
319  [] (octave_idx_type dim) { return dim == 0; });
320  }
321 
322  OCTAVE_API int num_ones () const;
323 
324  bool all_ones () const
325  {
326  return (num_ones () == ndims ());
327  }
328 
329  //! Number of elements that a matrix with this dimensions would have.
330  //!
331  //! Return the number of elements that a matrix with this dimension
332  //! vector would have, NOT the number of dimensions (elements in the
333  //! dimension vector).
334 
335  octave_idx_type numel (int n = 0) const
336  {
337  int n_dims = ndims ();
338 
339  octave_idx_type retval = 1;
340 
341  for (int i = n; i < n_dims; i++)
342  retval *= elem (i);
343 
344  return retval;
345  }
346 
347  //! The following function will throw a std::bad_alloc ()
348  //! exception if the requested size is larger than can be indexed by
349  //! octave_idx_type. This may be smaller than the actual amount of
350  //! memory that can be safely allocated on a system. However, if we
351  //! don't fail here, we can end up with a mysterious crash inside a
352  //! function that is iterating over an array using octave_idx_type
353  //! indices.
354 
355  OCTAVE_API octave_idx_type safe_numel () const;
356 
357  bool any_neg () const
358  {
359  return std::any_of (m_dims, m_dims + ndims (),
360  [] (octave_idx_type dim) { return dim < 0; });
361  }
362 
363  OCTAVE_API dim_vector squeeze () const;
364 
365  //! This corresponds to cat().
366  OCTAVE_API bool concat (const dim_vector& dvb, int dim);
367 
368  //! This corresponds to [,] (horzcat, dim = 0) and [;] (vertcat, dim = 1).
369  // The rules are more relaxed here.
370  OCTAVE_API bool hvcat (const dim_vector& dvb, int dim);
371 
372  //! Force certain dimensionality, preserving numel (). Missing
373  //! dimensions are set to 1, redundant are folded into the trailing
374  //! one. If n = 1, the result is 2d and the second dim is 1
375  //! (dim_vectors are always at least 2D).
376 
377  OCTAVE_API dim_vector redim (int n) const;
378 
380  {
381  if (ndims () == 2 && xelem (1) == 1)
382  return *this;
383  else
384  return dim_vector (numel (), 1);
385  }
386 
388  {
389  if (ndims () == 2 && xelem (0) == 1)
390  return *this;
391  else
392  return dim_vector (1, numel ());
393  }
394 
395  bool isvector () const
396  {
397  return (ndims () == 2 && (xelem (0) == 1 || xelem (1) == 1));
398  }
399 
400  bool is_nd_vector () const
401  {
402  int num_non_one = 0;
403 
404  for (int i = 0; i < ndims (); i++)
405  {
406  if (xelem (i) != 1)
407  {
408  num_non_one++;
409 
410  if (num_non_one > 1)
411  break;
412  }
413  }
414 
415  return num_non_one == 1;
416  }
417 
418  // Create a vector with length N. If this object is a vector,
419  // preserve the orientation, otherwise, create a column vector.
420 
422  {
423  dim_vector orig_dims;
424 
425  if (is_nd_vector ())
426  {
427  orig_dims = *this;
428 
429  for (int i = 0; i < orig_dims.ndims (); i++)
430  {
431  if (orig_dims(i) != 1)
432  {
433  orig_dims(i) = n;
434  break;
435  }
436  }
437  }
438  else
439  orig_dims = dim_vector (n, 1);
440 
441  return orig_dims;
442  }
443 
444  int first_non_singleton (int def = 0) const
445  {
446  for (int i = 0; i < ndims (); i++)
447  {
448  if (xelem (i) != 1)
449  return i;
450  }
451 
452  return def;
453  }
454 
455  //! Linear index from an index tuple.
457  { return compute_index (idx, ndims ()); }
458 
459  //! Linear index from an incomplete index tuple (nidx < length ()).
460  octave_idx_type compute_index (const octave_idx_type *idx, int nidx) const
461  {
462  octave_idx_type k = 0;
463  for (int i = nidx - 1; i >= 0; i--)
464  k = xelem(i) * k + idx[i];
465 
466  return k;
467  }
468 
469  //! Increment a multi-dimensional index tuple, optionally starting
470  //! from an offset position and return the index of the last index
471  //! position that was changed, or length () if just cycled over.
472 
473  int increment_index (octave_idx_type *idx, int start = 0) const
474  {
475  int i;
476  for (i = start; i < ndims (); i++)
477  {
478  if (++(*idx) == xelem(i))
479  *idx++ = 0;
480  else
481  break;
482  }
483  return i;
484  }
485 
486  //! Return cumulative dimensions.
487 
489  {
490  int nd = ndims ();
491  dim_vector retval = alloc (nd);
492 
493  octave_idx_type k = 1;
494  for (int i = 0; i < nd; i++)
495  retval.xelem(i) = (k *= xelem(i));
496 
497  return retval;
498  }
499 
500  //! Compute a linear index from an index tuple. Dimensions are
501  //! required to be cumulative.
502 
504  {
505  octave_idx_type k = idx[0];
506 
507  for (int i = 1; i < ndims (); i++)
508  k += xelem(i-1) * idx[i];
509 
510  return k;
511  }
512 
513  friend OCTAVE_API bool
514  operator == (const dim_vector& a, const dim_vector& b);
515 
516  OCTAVE_API Array<octave_idx_type> as_array () const;
517 };
518 
519 inline bool
520 operator == (const dim_vector& a, const dim_vector& b)
521 {
522  // Fast case.
523  if (a.m_dims == b.m_dims)
524  return true;
525 
526  int a_len = a.ndims ();
527  int b_len = b.ndims ();
528 
529  if (a_len != b_len)
530  return false;
531 
532  return std::equal (a.m_dims, a.m_dims + a_len, b.m_dims);
533 }
534 
535 inline bool
536 operator != (const dim_vector& a, const dim_vector& b)
537 {
538  return ! operator == (a, b);
539 }
540 
541 #endif
octave_idx_type compute_index(octave_idx_type n, const dim_vector &dims)
Definition: Array-util.cc:177
octave_idx_type num_ones(const Array< octave_idx_type > &ra_idx)
Definition: Array-util.cc:102
ComplexNDArray concat(NDArray &ra, ComplexNDArray &rb, const Array< octave_idx_type > &ra_idx)
Definition: CNDArray.cc:418
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
octave_idx_type compute_index(const octave_idx_type *idx) const
Linear index from an index tuple.
Definition: dim-vector.h:456
bool empty_2d() const
Definition: dim-vector.h:306
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:335
void chop_trailing_singletons()
Definition: dim-vector.h:164
void resize(int n, int fill_value=0)
Definition: dim-vector.h:272
dim_vector(const octave_idx_type r, const octave_idx_type c, Ints... lengths)
Construct dim_vector for a N dimensional array.
Definition: dim-vector.h:139
static dim_vector alloc(int n)
Definition: dim-vector.h:202
int increment_index(octave_idx_type *idx, int start=0) const
Increment a multi-dimensional index tuple, optionally starting from an offset position and return the...
Definition: dim-vector.h:473
bool is_nd_vector() const
Definition: dim-vector.h:400
bool any_neg() const
Definition: dim-vector.h:357
octave_idx_type ndims() const
Number of dimensions.
Definition: dim-vector.h:257
bool all_zero() const
Definition: dim-vector.h:300
bool isvector() const
Definition: dim-vector.h:395
bool zero_by_zero() const
Definition: dim-vector.h:311
octave_idx_type elem(int i) const
Definition: dim-vector.h:162
dim_vector cumulative() const
Return cumulative dimensions.
Definition: dim-vector.h:488
dim_vector(const dim_vector &dv)
Definition: dim-vector.h:190
bool any_zero() const
Definition: dim-vector.h:316
octave_idx_type cum_compute_index(const octave_idx_type *idx) const
Compute a linear index from an index tuple.
Definition: dim-vector.h:503
octave_idx_type & xelem(int i)
Definition: dim-vector.h:151
int first_non_singleton(int def=0) const
Definition: dim-vector.h:444
octave_idx_type compute_index(const octave_idx_type *idx, int nidx) const
Linear index from an incomplete index tuple (nidx < length ()).
Definition: dim-vector.h:460
octave_idx_type xelem(int i) const
Definition: dim-vector.h:153
dim_vector(dim_vector &&dv)
Definition: dim-vector.h:196
bool all_ones() const
Definition: dim-vector.h:324
dim_vector as_column() const
Definition: dim-vector.h:379
octave_idx_type & elem(int i)
Definition: dim-vector.h:157
dim_vector as_row() const
Definition: dim-vector.h:387
int length() const
Number of dimensions.
Definition: dim-vector.h:266
dim_vector make_nd_vector(octave_idx_type n) const
Definition: dim-vector.h:421
bool operator!=(const dim_vector &a, const dim_vector &b)
Definition: dim-vector.h:536
bool operator==(const dim_vector &a, const dim_vector &b)
Definition: dim-vector.h:520
#define OCTAVE_API
Definition: main.cc:55
octave_idx_type n
Definition: mx-inlines.cc:761
T * r
Definition: mx-inlines.cc:781
T::size_type numel(const T &str)
Definition: oct-string.cc:74