GNU Octave  6.2.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-2021 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 "oct-atomic.h"
40 #include "oct-refcount.h"
41 
42 template <typename T> class Array;
43 
44 //! Vector representing the dimensions (size) of an Array.
45 //!
46 //! A dim_vector is used to represent dimensions of an Array. It is used
47 //! on its constructor to specify its size, or when reshaping it.
48 //!
49 //! @code{.cc}
50 //! // Matrix with 10 rows and 20 columns.
51 //! Matrix m Matrix (dim_vector (10, 20));
52 //!
53 //! // Change its size to 5 rows and 40 columns.
54 //! Matrix m2 = m.reshape (dim_vector (5, 40));
55 //!
56 //! // Five dimensional Array of length 10, 20, 3, 8, 7 on each dimension.
57 //! NDArray a (dim_vector (10, 20, 3, 8, 7));
58 //!
59 //! // Uninitialized array of same size as other.
60 //! NDArray b (a.dims ());
61 //! @endcode
62 //!
63 //! The main thing to understand about this class, is that methods such as
64 //! ndims() and numel(), return the value for an Array of these dimensions,
65 //! not the actual number of elements in the dim_vector.
66 //!
67 //! @code{.cc}
68 //! dim_vector d (10, 5, 3);
69 //! octave_idx_type n = d.numel (); // returns 150
70 //! octave_idx_type nd = d.ndims (); // returns 3
71 //! @endcode
72 //!
73 //! ## Implementation details ##
74 //!
75 //! This implementation is more tricky than Array, but the big plus is that
76 //! dim_vector requires only one allocation instead of two. It is (slightly)
77 //! patterned after GCC's basic_string implementation. rep is a pointer to an
78 //! array of memory, comprising count, length, and the data:
79 //!
80 //! @verbatim
81 //! <count>
82 //! <ndims>
83 //! rep --> <dims[0]>
84 //! <dims[1]>
85 //! ...
86 //! @endverbatim
87 //!
88 //! The inlines count(), ndims() recover this data from the rep. Note
89 //! that rep points to the beginning of dims to grant faster access
90 //! (reinterpret_cast is assumed to be an inexpensive operation).
91 
92 class
93 OCTAVE_API
95 {
96 private:
97 
99 
100  octave_idx_type& count (void) const { return rep[-2]; }
101 
103  {
104  return octave_atomic_increment (&(count ()));
105  }
106 
108  {
109  return octave_atomic_decrement (&(count ()));
110  }
111 
112  //! Construct a new rep with count = 1 and ndims given.
113 
114  static octave_idx_type * newrep (int ndims)
115  {
116  octave_idx_type *r = new octave_idx_type [ndims + 2];
117 
118  *r++ = 1;
119  *r++ = ndims;
120 
121  return r;
122  }
123 
124  //! Clone this->rep.
125 
127  {
128  int nd = ndims ();
129 
130  octave_idx_type *r = newrep (nd);
131 
132  std::copy_n (rep, nd, r);
133 
134  return r;
135  }
136 
137  //! Clone and resize this->rep to length n, filling by given value.
138 
140  {
141  int nd = ndims ();
142 
143  if (n < 2)
144  n = 2;
145 
146  octave_idx_type *r = newrep (n);
147 
148  if (nd > n)
149  nd = n;
150 
151  std::copy_n (rep, nd, r);
152  std::fill_n (r + nd, n - nd, fill_value);
153 
154  return r;
155  }
156 
157  //! Free the rep.
158 
159  void freerep (void)
160  {
161  assert (count () == 0);
162  delete [] (rep - 2);
163  }
164 
165  void make_unique (void)
166  {
167  if (count () > 1)
168  {
169  octave_idx_type *new_rep = clonerep ();
170 
171  if (decrement_count () == 0)
172  freerep ();
173 
174  rep = new_rep;
175  }
176  }
177 
178 public:
179 
180  //! Construct dim_vector for a N dimensional array.
181  //!
182  //! Each argument to constructor defines the length of an additional
183  //! dimension. A dim_vector always represents a minimum of 2 dimensions
184  //! (just like an Array has at least 2 dimensions) and there is no
185  //! upper limit on the number of dimensions.
186  //!
187  //! @code{.cc}
188  //! dim_vector dv (7, 5);
189  //! Matrix mat (dv);
190  //! @endcode
191  //!
192  //! The constructed dim_vector @c dv will have two elements, @f$[7, 5]@f$,
193  //! one for each dimension. It can then be used to construct a Matrix
194  //! with such dimensions, i.e., 7 rows and 5 columns.
195  //!
196  //! @code{.cc}
197  //! NDArray x (dim_vector (7, 5, 10));
198  //! @endcode
199  //!
200  //! This will construct a 3 dimensional NDArray of lengths 7, 5, and 10,
201  //! on the first, second, and third dimension (rows, columns, and pages)
202  //! respectively.
203  //!
204  //! Note that that there is no constructor that accepts only one
205  //! dimension length to avoid confusion. The source for such confusion
206  //! is that constructor could mean:
207  //! - a column vector, i.e., assume @f$[N, 1]@f$;
208  //! - a square matrix, i.e., as is common in Octave interpreter;
209  //! - support for a 1 dimensional Array (does not exist);
210  //!
211  //! Using r, c, and lengths... as arguments, allow us to check at compile
212  //! time that there's at least 2 dimensions specified, while maintaining
213  //! type safety.
214 
215  template <typename... Ints>
217  Ints... lengths) : rep (newrep (2 + sizeof... (Ints)))
218  {
219  std::initializer_list<octave_idx_type> all_lengths = {r, c, lengths...};
220  for (const octave_idx_type l: all_lengths)
221  *rep++ = l;
222  rep -= all_lengths.size ();
223  }
224 
225  // Fast access with absolutely no checking
226 
227  octave_idx_type& xelem (int i) { return rep[i]; }
228 
229  octave_idx_type xelem (int i) const { return rep[i]; }
230 
231  // Safe access to to elements
232 
234  {
235  make_unique ();
236  return xelem (i);
237  }
238 
239  octave_idx_type elem (int i) const { return xelem (i); }
240 
242  {
243  int nd = ndims ();
244  if (nd > 2 && rep[nd-1] == 1)
245  {
246  make_unique ();
247  do
248  nd--;
249  while (nd > 2 && rep[nd-1] == 1);
250  rep[-1] = nd;
251  }
252  }
253 
254  void chop_all_singletons (void);
255 
256  // WARNING: Only call by jit
257  octave_idx_type * to_jit (void) const
258  {
259  return rep;
260  }
261 
262 private:
263 
264  static octave_idx_type *nil_rep (void);
265 
266 public:
267 
268  static octave_idx_type dim_max (void);
269 
270  explicit dim_vector (void) : rep (nil_rep ())
271  { increment_count (); }
272 
273  dim_vector (const dim_vector& dv) : rep (dv.rep)
274  { increment_count (); }
275 
276  dim_vector (dim_vector&& dv) : rep (dv.rep) { dv.rep = nullptr; }
277 
278 // FIXME: Should be private, but required by array constructor for jit
279  explicit dim_vector (octave_idx_type *r) : rep (r) { }
280 
281  static dim_vector alloc (int n)
282  {
283  return dim_vector (newrep (n < 2 ? 2 : n));
284  }
285 
287  {
288  if (&dv != this)
289  {
290  if (decrement_count () == 0)
291  freerep ();
292 
293  rep = dv.rep;
294  increment_count ();
295  }
296 
297  return *this;
298  }
299 
301  {
302  if (&dv != this)
303  {
304  // Because we define a move constructor and a move assignment
305  // operator, rep may be a nullptr here. We should only need to
306  // protect the destructor in a similar way.
307 
308  if (rep && decrement_count () == 0)
309  freerep ();
310 
311  rep = dv.rep;
312  dv.rep = nullptr;
313  }
314 
315  return *this;
316  }
317 
318  ~dim_vector (void)
319  {
320  // Because we define a move constructor and a move assignment
321  // operator, rep may be a nullptr here. We should only need to
322  // protect the move assignment operator in a similar way.
323 
324  if (rep && decrement_count () == 0)
325  freerep ();
326  }
327 
328  //! Number of dimensions.
329  //!
330  //! Returns the number of dimensions of the dim_vector. This is number of
331  //! elements in the dim_vector including trailing singletons. It is also
332  //! the number of dimensions an Array with this dim_vector would have.
333 
334  octave_idx_type ndims (void) const { return rep[-1]; }
335 
336  //! Number of dimensions.
337  //! Synonymous with ndims().
338  //!
339  //! While this method is not officially deprecated, consider using ndims()
340  //! instead to avoid confusion. Array does not have length because of its
341  //! odd definition as length of the longest dimension.
342 
343  int length (void) const { return ndims (); }
344 
345  octave_idx_type& operator () (int i) { return elem (i); }
346 
347  octave_idx_type operator () (int i) const { return elem (i); }
348 
349  void resize (int n, int fill_value = 0)
350  {
351  int len = ndims ();
352 
353  if (n != len)
354  {
355  octave_idx_type *r = resizerep (n, fill_value);
356 
357  if (decrement_count () == 0)
358  freerep ();
359 
360  rep = r;
361  }
362  }
363 
364  std::string str (char sep = 'x') const;
365 
366  bool all_zero (void) const
367  {
368  return std::all_of (rep, rep + ndims (),
369  [] (octave_idx_type dim) { return dim == 0; });
370  }
371 
372  bool empty_2d (void) const
373  {
374  return ndims () == 2 && (xelem (0) == 0 || xelem (1) == 0);
375  }
376 
377  bool zero_by_zero (void) const
378  {
379  return ndims () == 2 && xelem (0) == 0 && xelem (1) == 0;
380  }
381 
382  bool any_zero (void) const
383  {
384  return std::any_of (rep, rep + ndims (),
385  [] (octave_idx_type dim) { return dim == 0; });
386  }
387 
388  int num_ones (void) const;
389 
390  bool all_ones (void) const
391  {
392  return (num_ones () == ndims ());
393  }
394 
395  //! Number of elements that a matrix with this dimensions would have.
396  //!
397  //! Return the number of elements that a matrix with this dimension
398  //! vector would have, NOT the number of dimensions (elements in the
399  //! dimension vector).
400 
401  octave_idx_type numel (int n = 0) const
402  {
403  int n_dims = ndims ();
404 
406 
407  for (int i = n; i < n_dims; i++)
408  retval *= elem (i);
409 
410  return retval;
411  }
412 
413  //! The following function will throw a std::bad_alloc ()
414  //! exception if the requested size is larger than can be indexed by
415  //! octave_idx_type. This may be smaller than the actual amount of
416  //! memory that can be safely allocated on a system. However, if we
417  //! don't fail here, we can end up with a mysterious crash inside a
418  //! function that is iterating over an array using octave_idx_type
419  //! indices.
420 
421  octave_idx_type safe_numel (void) const;
422 
423  bool any_neg (void) const
424  {
425  return std::any_of (rep, rep + ndims (),
426  [] (octave_idx_type dim) { return dim < 0; });
427  }
428 
429  dim_vector squeeze (void) const;
430 
431  //! This corresponds to cat().
432  bool concat (const dim_vector& dvb, int dim);
433 
434  //! This corresponds to [,] (horzcat, dim = 0) and [;] (vertcat, dim = 1).
435  // The rules are more relaxed here.
436  bool hvcat (const dim_vector& dvb, int dim);
437 
438  //! Force certain dimensionality, preserving numel (). Missing
439  //! dimensions are set to 1, redundant are folded into the trailing
440  //! one. If n = 1, the result is 2d and the second dim is 1
441  //! (dim_vectors are always at least 2D).
442 
443  dim_vector redim (int n) const;
444 
445  dim_vector as_column (void) const
446  {
447  if (ndims () == 2 && xelem (1) == 1)
448  return *this;
449  else
450  return dim_vector (numel (), 1);
451  }
452 
453  dim_vector as_row (void) const
454  {
455  if (ndims () == 2 && xelem (0) == 1)
456  return *this;
457  else
458  return dim_vector (1, numel ());
459  }
460 
461  bool isvector (void) const
462  {
463  return (ndims () == 2 && (xelem (0) == 1 || xelem (1) == 1));
464  }
465 
466  bool is_nd_vector (void) const
467  {
468  int num_non_one = 0;
469 
470  for (int i = 0; i < ndims (); i++)
471  {
472  if (xelem (i) != 1)
473  {
474  num_non_one++;
475 
476  if (num_non_one > 1)
477  break;
478  }
479  }
480 
481  return num_non_one == 1;
482  }
483 
484  // Create a vector with length N. If this object is a vector,
485  // preserve the orientation, otherwise, create a column vector.
486 
488  {
489  dim_vector orig_dims;
490 
491  if (is_nd_vector ())
492  {
493  orig_dims = *this;
494 
495  for (int i = 0; i < orig_dims.ndims (); i++)
496  {
497  if (orig_dims(i) != 1)
498  {
499  orig_dims(i) = n;
500  break;
501  }
502  }
503  }
504  else
505  orig_dims = dim_vector (n, 1);
506 
507  return orig_dims;
508  }
509 
510  int first_non_singleton (int def = 0) const
511  {
512  for (int i = 0; i < ndims (); i++)
513  {
514  if (xelem (i) != 1)
515  return i;
516  }
517 
518  return def;
519  }
520 
521  //! Linear index from an index tuple.
523  { return compute_index (idx, ndims ()); }
524 
525  //! Linear index from an incomplete index tuple (nidx < length ()).
526  octave_idx_type compute_index (const octave_idx_type *idx, int nidx) const
527  {
528  octave_idx_type k = 0;
529  for (int i = nidx - 1; i >= 0; i--)
530  k = rep[i] * k + idx[i];
531 
532  return k;
533  }
534 
535  //! Increment a multi-dimensional index tuple, optionally starting
536  //! from an offset position and return the index of the last index
537  //! position that was changed, or length () if just cycled over.
538 
539  int increment_index (octave_idx_type *idx, int start = 0) const
540  {
541  int i;
542  for (i = start; i < ndims (); i++)
543  {
544  if (++(*idx) == rep[i])
545  *idx++ = 0;
546  else
547  break;
548  }
549  return i;
550  }
551 
552  //! Return cumulative dimensions.
553 
554  dim_vector cumulative (void) const
555  {
556  int nd = ndims ();
557  dim_vector retval = alloc (nd);
558 
559  octave_idx_type k = 1;
560  for (int i = 0; i < nd; i++)
561  retval.rep[i] = (k *= rep[i]);
562 
563  return retval;
564  }
565 
566  //! Compute a linear index from an index tuple. Dimensions are
567  //! required to be cumulative.
568 
570  {
571  octave_idx_type k = idx[0];
572 
573  for (int i = 1; i < ndims (); i++)
574  k += rep[i-1] * idx[i];
575 
576  return k;
577  }
578 
579  friend bool operator == (const dim_vector& a, const dim_vector& b);
580 
581  Array<octave_idx_type> as_array (void) const;
582 };
583 
584 inline bool
585 operator == (const dim_vector& a, const dim_vector& b)
586 {
587  // Fast case.
588  if (a.rep == b.rep)
589  return true;
590 
591  int a_len = a.ndims ();
592  int b_len = b.ndims ();
593 
594  if (a_len != b_len)
595  return false;
596 
597  return std::equal (a.rep, a.rep + a_len, b.rep);
598 }
599 
600 inline bool
601 operator != (const dim_vector& a, const dim_vector& b)
602 {
603  return ! operator == (a, b);
604 }
605 
606 #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
static int elem
Definition: __contourc__.cc:52
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:128
bool is_nd_vector(void) const
Size of the specified dimension.
Definition: Array.h:576
static Array< T >::ArrayRep * nil_rep(void)
Definition: Array.cc:42
Array< T >::ArrayRep * rep
Definition: Array.h:219
Array< T > squeeze(void) const
Chop off leading singleton dimensions.
Definition: Array.cc:117
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:469
Array< T > & operator=(const Array< T > &a)
Definition: Array.h:325
void make_unique(void)
Definition: Array.h:188
T & operator()(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:513
int ndims(void) const
Size of the specified dimension.
Definition: Array.h:589
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
octave_idx_type decrement_count(void)
Definition: dim-vector.h:107
octave_idx_type * clonerep(void)
Clone this->rep.
Definition: dim-vector.h:126
octave_idx_type compute_index(const octave_idx_type *idx) const
Linear index from an index tuple.
Definition: dim-vector.h:522
void freerep(void)
Free the rep.
Definition: dim-vector.h:159
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:401
bool zero_by_zero(void) const
Definition: dim-vector.h:377
void resize(int n, int fill_value=0)
Definition: dim-vector.h:349
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:216
static dim_vector alloc(int n)
Definition: dim-vector.h:281
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:539
bool all_ones(void) const
Definition: dim-vector.h:390
octave_idx_type & count(void) const
Definition: dim-vector.h:100
dim_vector(octave_idx_type *r)
Definition: dim-vector.h:279
octave_idx_type * resizerep(int n, octave_idx_type fill_value)
Clone and resize this->rep to length n, filling by given value.
Definition: dim-vector.h:139
bool isvector(void) const
Definition: dim-vector.h:461
dim_vector as_column(void) const
Definition: dim-vector.h:445
bool all_zero(void) const
Definition: dim-vector.h:366
dim_vector cumulative(void) const
Return cumulative dimensions.
Definition: dim-vector.h:554
void chop_trailing_singletons(void)
Definition: dim-vector.h:241
bool any_neg(void) const
Definition: dim-vector.h:423
octave_idx_type elem(int i) const
Definition: dim-vector.h:239
dim_vector(const dim_vector &dv)
Definition: dim-vector.h:273
dim_vector(void)
Definition: dim-vector.h:270
octave_idx_type cum_compute_index(const octave_idx_type *idx) const
Compute a linear index from an index tuple.
Definition: dim-vector.h:569
static octave_idx_type * newrep(int ndims)
Construct a new rep with count = 1 and ndims given.
Definition: dim-vector.h:114
octave_idx_type & xelem(int i)
Definition: dim-vector.h:227
int first_non_singleton(int def=0) const
Definition: dim-vector.h:510
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:334
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:526
bool empty_2d(void) const
Definition: dim-vector.h:372
int length(void) const
Number of dimensions.
Definition: dim-vector.h:343
bool is_nd_vector(void) const
Definition: dim-vector.h:466
octave_idx_type xelem(int i) const
Definition: dim-vector.h:229
dim_vector(dim_vector &&dv)
Definition: dim-vector.h:276
octave_idx_type * to_jit(void) const
Definition: dim-vector.h:257
bool any_zero(void) const
Definition: dim-vector.h:382
void make_unique(void)
Definition: dim-vector.h:165
dim_vector as_row(void) const
Definition: dim-vector.h:453
octave_idx_type increment_count(void)
Definition: dim-vector.h:102
~dim_vector(void)
Definition: dim-vector.h:318
octave_idx_type & elem(int i)
Definition: dim-vector.h:233
octave_idx_type * rep
Definition: dim-vector.h:98
dim_vector make_nd_vector(octave_idx_type n) const
Definition: dim-vector.h:487
bool operator!=(const dim_vector &a, const dim_vector &b)
Definition: dim-vector.h:601
bool operator==(const dim_vector &a, const dim_vector &b)
Definition: dim-vector.h:585
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
octave_idx_type octave_atomic_increment(octave_idx_type *x)
octave_idx_type octave_atomic_decrement(octave_idx_type *x)
T::size_type numel(const T &str)
Definition: oct-string.cc:71
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
F77_RET_T len
Definition: xerbla.cc:61