GNU Octave 7.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-2022 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
91class
94{
95private:
96
98
100
101public:
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 (void);
171
172private:
173
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
180public:
181
182 static OCTAVE_API octave_idx_type dim_max (void);
183
184 explicit dim_vector (void)
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
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 (void) 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 (void) 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 (void) 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 (void) const
307 {
308 return ndims () == 2 && (xelem (0) == 0 || xelem (1) == 0);
309 }
310
311 bool zero_by_zero (void) const
312 {
313 return ndims () == 2 && xelem (0) == 0 && xelem (1) == 0;
314 }
315
316 bool any_zero (void) 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 (void) const;
323
324 bool all_ones (void) 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 (void) const;
356
357 bool any_neg (void) 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 (void) 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
387 dim_vector as_row (void) const
388 {
389 if (ndims () == 2 && xelem (0) == 1)
390 return *this;
391 else
392 return dim_vector (1, numel ());
393 }
394
395 bool isvector (void) const
396 {
397 return (ndims () == 2 && (xelem (0) == 1 || xelem (1) == 1));
398 }
399
400 bool is_nd_vector (void) 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 (void) const;
517};
518
519inline bool
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
535inline bool
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
static int elem
Definition: __contourc__.cc:54
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
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:335
octave_idx_type m_num_dims
Definition: dim-vector.h:97
bool zero_by_zero(void) const
Definition: dim-vector.h:311
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 all_ones(void) const
Definition: dim-vector.h:324
bool isvector(void) const
Definition: dim-vector.h:395
dim_vector as_column(void) const
Definition: dim-vector.h:379
bool all_zero(void) const
Definition: dim-vector.h:300
dim_vector cumulative(void) const
Return cumulative dimensions.
Definition: dim-vector.h:488
octave_idx_type * m_dims
Definition: dim-vector.h:99
octave_idx_type & xelem(int i)
Definition: dim-vector.h:151
void chop_trailing_singletons(void)
Definition: dim-vector.h:164
bool any_neg(void) const
Definition: dim-vector.h:357
octave_idx_type elem(int i) const
Definition: dim-vector.h:162
dim_vector(const dim_vector &dv)
Definition: dim-vector.h:190
dim_vector(void)
Definition: dim-vector.h:184
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
int first_non_singleton(int def=0) const
Definition: dim-vector.h:444
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:257
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
bool empty_2d(void) const
Definition: dim-vector.h:306
dim_vector(octave_idx_type ndims)
Definition: dim-vector.h:174
int length(void) const
Number of dimensions.
Definition: dim-vector.h:266
octave_idx_type & elem(int i)
Definition: dim-vector.h:157
bool is_nd_vector(void) const
Definition: dim-vector.h:400
octave_idx_type xelem(int i) const
Definition: dim-vector.h:153
dim_vector(dim_vector &&dv)
Definition: dim-vector.h:196
bool any_zero(void) const
Definition: dim-vector.h:316
dim_vector as_row(void) const
Definition: dim-vector.h:387
~dim_vector(void)
Definition: dim-vector.h:242
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.in.cc:55
T::size_type numel(const T &str)
Definition: oct-string.cc:71