GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
dim-vector.h
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2003-2026 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 <algorithm>
34#include <initializer_list>
35#include <limits>
36#include <string>
37
38#include "Array-fwd.h"
39#include "lo-utils.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
92{
93private:
94
95 octave_idx_type m_num_dims;
96
97 octave_idx_type *m_dims;
98
99public:
100
101 //! Construct dim_vector for a N dimensional array.
102 //!
103 //! Each argument to constructor defines the length of an additional
104 //! dimension. A dim_vector always represents a minimum of 2 dimensions
105 //! (just like an Array has at least 2 dimensions) and there is no
106 //! upper limit on the number of dimensions.
107 //!
108 //! @code{.cc}
109 //! dim_vector dv (7, 5);
110 //! Matrix mat (dv);
111 //! @endcode
112 //!
113 //! The constructed dim_vector @c dv will have two elements, @f$[7, 5]@f$,
114 //! one for each dimension. It can then be used to construct a Matrix
115 //! with such dimensions, i.e., 7 rows and 5 columns.
116 //!
117 //! @code{.cc}
118 //! NDArray x (dim_vector (7, 5, 10));
119 //! @endcode
120 //!
121 //! This will construct a 3 dimensional NDArray of lengths 7, 5, and 10,
122 //! on the first, second, and third dimension (rows, columns, and pages)
123 //! respectively.
124 //!
125 //! Note that that there is no constructor that accepts only one
126 //! dimension length to avoid confusion. The source for such confusion
127 //! is that constructor could mean:
128 //! - a column vector, i.e., assume @f$[N, 1]@f$;
129 //! - a square matrix, i.e., as is common in Octave interpreter;
130 //! - support for a 1 dimensional Array (does not exist);
131 //!
132 //! Using r, c, and lengths... as arguments, allow us to check at compile
133 //! time that there's at least 2 dimensions specified, while maintaining
134 //! type safety.
135
136 template <typename... Ints>
138 Ints... lengths)
139 : m_num_dims (2 + sizeof... (Ints)), m_dims (new octave_idx_type [m_num_dims])
140 {
141 std::initializer_list<octave_idx_type> all_lengths = {r, c, lengths...};
142 octave_idx_type *ptr = m_dims;
143 for (const octave_idx_type l: all_lengths)
144 *ptr++ = l;
145 }
146
147 // Fast access with absolutely no checking
148
149 octave_idx_type& xelem (int i) { return m_dims[i]; }
150
151 octave_idx_type xelem (int i) const { return m_dims[i]; }
152
153 // Safe access to to elements
154
156 {
157 return xelem (i);
158 }
159
160 octave_idx_type elem (int i) const { return xelem (i); }
161
163 {
164 while (m_num_dims > 2 && xelem(m_num_dims-1) == 1)
165 m_num_dims--;
166 }
167
168 OCTAVE_API void chop_all_singletons ();
169
170private:
171
172 explicit dim_vector (octave_idx_type ndims)
173 : m_num_dims (ndims < 2 ? 2 : ndims), m_dims (new octave_idx_type [m_num_dims])
174 {
175 std::fill_n (m_dims, m_num_dims, 0);
176 }
177
178public:
179
180 // The maximum allowed value for a dimension extent. This will
181 // normally be a tiny bit off the maximum value of octave_idx_type.
182 // Currently, 1 is subtracted to allow safe conversion of any 2-D Array
183 // into Sparse but this offset may change in the future.
184
185 static constexpr octave_idx_type dim_max ()
186 {
187 return std::numeric_limits<octave_idx_type>::max () - 1;
188 }
189
190 explicit dim_vector ()
191 : m_num_dims (2), m_dims (new octave_idx_type [m_num_dims])
192 {
193 std::fill_n (m_dims, m_num_dims, 0);
194 }
195
197 : m_num_dims (dv.m_num_dims), m_dims (new octave_idx_type [m_num_dims])
198 {
199 std::copy_n (dv.m_dims, m_num_dims, m_dims);
200 }
201
203 : m_num_dims (0), m_dims (nullptr)
204 {
205 *this = std::move (dv);
206 }
207
208 static dim_vector alloc (int n)
209 {
210 return dim_vector (n);
211 }
212
213 dim_vector& operator = (const dim_vector& dv)
214 {
215 if (&dv != this)
216 {
217 delete [] m_dims;
218
219 m_num_dims = dv.m_num_dims;
220 m_dims = new octave_idx_type [m_num_dims];
221
222 std::copy_n (dv.m_dims, m_num_dims, m_dims);
223 }
224
225 return *this;
226 }
227
228 dim_vector& operator = (dim_vector&& dv)
229 {
230 if (&dv != this)
231 {
232 // Because we define a move constructor and a move assignment
233 // operator, m_dims may be a nullptr here. We should only need to
234 // protect the destructor in a similar way.
235
236 delete [] m_dims;
237
238 m_num_dims = dv.m_num_dims;
239 m_dims = dv.m_dims;
240
241 dv.m_num_dims = 0;
242 dv.m_dims = nullptr;
243 }
244
245 return *this;
246 }
247
249 {
250 // Because we define a move constructor and a move assignment
251 // operator, m_dims may be a nullptr here. We should only need to
252 // protect the move assignment operator in a similar way.
253
254 delete [] m_dims;
255 }
256
257 //! Number of dimensions.
258 //!
259 //! Returns the number of dimensions of the dim_vector. This is number of
260 //! elements in the dim_vector including trailing singletons. It is also
261 //! the number of dimensions an Array with this dim_vector would have.
262
263 octave_idx_type ndims () const { return m_num_dims; }
264
265 //! Number of dimensions.
266 //! Synonymous with ndims().
267 //!
268 //! While this method is not officially deprecated, consider using ndims()
269 //! instead to avoid confusion. Array does not have length because of its
270 //! odd definition as length of the longest dimension.
271
272 int length () const { return ndims (); }
273
274 octave_idx_type& operator () (int i) { return elem (i); }
275
276 octave_idx_type operator () (int i) const { return elem (i); }
277
278 void resize (int n, int fill_value = 0)
279 {
280 if (n < 2)
281 n = 2;
282
283 if (n == m_num_dims)
284 return;
285
286 if (n < m_num_dims)
287 {
288 m_num_dims = n;
289 return;
290 }
291
292 octave_idx_type *new_rep = new octave_idx_type [n];
293
294 std::copy_n (m_dims, m_num_dims, new_rep);
295 std::fill_n (new_rep + m_num_dims, n - m_num_dims, fill_value);
296
297 delete [] m_dims;
298
299 m_dims = new_rep;
300
301 m_num_dims = n;
302 }
303
304 OCTAVE_API std::string str (char sep = 'x') const;
305
306 bool all_zero () const
307 {
308 return std::all_of (m_dims, m_dims + ndims (),
309 [] (octave_idx_type dim) { return dim == 0; });
310 }
311
312 bool empty_2d () const
313 {
314 return ndims () == 2 && (xelem (0) == 0 || xelem (1) == 0);
315 }
316
317 bool zero_by_zero () const
318 {
319 return ndims () == 2 && xelem (0) == 0 && xelem (1) == 0;
320 }
321
322 bool any_zero () const
323 {
324 return std::any_of (m_dims, m_dims + ndims (),
325 [] (octave_idx_type dim) { return dim == 0; });
326 }
327
328 OCTAVE_API int num_ones () const;
329
330 bool all_ones () const
331 {
332 return (num_ones () == ndims ());
333 }
334
335 //! Number of elements that a matrix with this dimensions would have.
336 //!
337 //! Return the number of elements that a matrix with this dimension
338 //! vector would have, NOT the number of dimensions (elements in the
339 //! dimension vector).
340
341 octave_idx_type numel (int n = 0) const
342 {
343 int n_dims = ndims ();
344
345 octave_idx_type retval = 1;
346
347 for (int i = n; i < n_dims; i++)
348 retval *= elem (i);
349
350 return retval;
351 }
352
353 //! The following function will throw a std::bad_alloc ()
354 //! exception if the requested size is larger than can be indexed by
355 //! octave_idx_type. This may be smaller than the actual amount of
356 //! memory that can be safely allocated on a system. However, if we
357 //! don't fail here, we can end up with a mysterious crash inside a
358 //! function that is iterating over an array using octave_idx_type
359 //! indices.
360
361 // IF we ever require C++26 we can use ckd_mul in the following
362 // function and not have to check for or explicitly call
363 // __builtin_mul_overflow.
365 {
366 octave_idx_type n = xelem(0);
367
368 if (n > dim_max ())
369 throw std::bad_alloc ();
370
371 if (n == 0)
372 return 0;
373
374 for (int i = 1; i < ndims (); i++)
375 {
376#if defined (__has_builtin) && __has_builtin (__builtin_mul_overflow)
377 bool overflow = __builtin_mul_overflow (n, xelem(i), &n);
378#else
379 // Function call overhead, much less efficient than using the
380 // built-in function or ckd_mul (C23 / C++26)
381 bool overflow = octave::math::int_multiply_overflow (n, xelem(i), &n);
382#endif
383
384 if (overflow || n > dim_max ())
385 throw std::bad_alloc ();
386
387 if (n == 0)
388 return 0;
389 }
390
391 return n;
392 }
393
394 bool any_neg () const
395 {
396 return std::any_of (m_dims, m_dims + ndims (),
397 [] (octave_idx_type dim) { return dim < 0; });
398 }
399
400 OCTAVE_API dim_vector squeeze () const;
401
402 //! This corresponds to cat().
403 OCTAVE_API bool concat (const dim_vector& dvb, int dim);
404
405 //! This corresponds to [,] (horzcat, dim = 0) and [;] (vertcat, dim = 1).
406 // The rules are more relaxed here.
407 OCTAVE_API bool hvcat (const dim_vector& dvb, int dim);
408
409 //! Force certain dimensionality, preserving numel (). Missing
410 //! dimensions are set to 1, redundant are folded into the trailing
411 //! one. If n = 1, the result is 2-D and the second dim is 1
412 //! (dim_vectors are always at least 2-D).
413
414 OCTAVE_API dim_vector redim (int n) const;
415
417 {
418 if (ndims () == 2 && xelem (1) == 1)
419 return *this;
420 else
421 return dim_vector (numel (), 1);
422 }
423
425 {
426 if (ndims () == 2 && xelem (0) == 1)
427 return *this;
428 else
429 return dim_vector (1, numel ());
430 }
431
432 bool isvector () const
433 {
434 return (ndims () == 2 && (xelem (0) == 1 || xelem (1) == 1));
435 }
436
437 bool is_nd_vector () const
438 {
439 int num_non_one = 0;
440
441 for (int i = 0; i < ndims (); i++)
442 {
443 if (xelem (i) != 1)
444 {
445 num_non_one++;
446
447 if (num_non_one > 1)
448 break;
449 }
450 }
451
452 return num_non_one == 1;
453 }
454
455 // Create a vector with length N. If this object is a vector,
456 // preserve the orientation, otherwise, create a column vector.
457
459 {
460 dim_vector orig_dims;
461
462 if (is_nd_vector ())
463 {
464 orig_dims = *this;
465
466 for (int i = 0; i < orig_dims.ndims (); i++)
467 {
468 if (orig_dims(i) != 1)
469 {
470 orig_dims(i) = n;
471 break;
472 }
473 }
474 }
475 else
476 orig_dims = dim_vector (n, 1);
477
478 return orig_dims;
479 }
480
481 int first_non_singleton (int def = 0) const
482 {
483 for (int i = 0; i < ndims (); i++)
484 {
485 if (xelem (i) != 1)
486 return i;
487 }
488
489 return def;
490 }
491
492 //! Linear index from an index tuple.
494 { return compute_index (idx, ndims ()); }
495
496 //! Linear index from an incomplete index tuple (nidx < length ()).
497 octave_idx_type compute_index (const octave_idx_type *idx, int nidx) const
498 {
499 octave_idx_type k = 0;
500 for (int i = nidx - 1; i >= 0; i--)
501 k = xelem(i) * k + idx[i];
502
503 return k;
504 }
505
506 //! Increment a multi-dimensional index tuple, optionally starting
507 //! from an offset position and return the index of the last index
508 //! position that was changed, or length () if just cycled over.
509
510 int increment_index (octave_idx_type *idx, int start = 0) const
511 {
512 int i;
513 for (i = start; i < ndims (); i++)
514 {
515 if (++(*idx) == xelem(i))
516 *idx++ = 0;
517 else
518 break;
519 }
520 return i;
521 }
522
523 //! Return cumulative dimensions.
524
526 {
527 int nd = ndims ();
528 dim_vector retval = alloc (nd);
529
530 octave_idx_type k = 1;
531 for (int i = 0; i < nd; i++)
532 retval.xelem(i) = (k *= xelem(i));
533
534 return retval;
535 }
536
537 //! Compute a linear index from an index tuple. Dimensions are
538 //! required to be cumulative.
539
541 {
542 octave_idx_type k = idx[0];
543
544 for (int i = 1; i < ndims (); i++)
545 k += xelem(i-1) * idx[i];
546
547 return k;
548 }
549
550 friend OCTAVE_API bool
551 operator == (const dim_vector& a, const dim_vector& b);
552
553 OCTAVE_API Array<octave_idx_type> as_array () const;
554};
555
556inline bool
558{
559 // Fast case.
560 if (a.m_dims == b.m_dims)
561 return true;
562
563 int a_len = a.ndims ();
564 int b_len = b.ndims ();
565
566 if (a_len != b_len)
567 return false;
568
569 return std::equal (a.m_dims, a.m_dims + a_len, b.m_dims);
570}
571
572inline bool
574{
575 return ! operator == (a, b);
576}
577
578#endif
octave_idx_type compute_index(octave_idx_type n, const dim_vector &dims)
octave_idx_type num_ones(const Array< octave_idx_type > &ra_idx)
ComplexNDArray concat(NDArray &ra, ComplexNDArray &rb, const Array< octave_idx_type > &ra_idx)
Definition CNDArray.cc:424
N Dimensional Array with copy-on-write semantics.
Definition Array-base.h:130
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:92
octave_idx_type compute_index(const octave_idx_type *idx) const
Linear index from an index tuple.
Definition dim-vector.h:493
octave_idx_type safe_numel() const
The following function will throw a std::bad_alloc () exception if the requested size is larger than ...
Definition dim-vector.h:364
bool empty_2d() const
Definition dim-vector.h:312
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition dim-vector.h:341
void chop_trailing_singletons()
Definition dim-vector.h:162
void resize(int n, int fill_value=0)
Definition dim-vector.h:278
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:137
static dim_vector alloc(int n)
Definition dim-vector.h:208
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:510
static constexpr octave_idx_type dim_max()
Definition dim-vector.h:185
bool is_nd_vector() const
Definition dim-vector.h:437
bool any_neg() const
Definition dim-vector.h:394
octave_idx_type ndims() const
Number of dimensions.
Definition dim-vector.h:263
bool all_zero() const
Definition dim-vector.h:306
bool isvector() const
Definition dim-vector.h:432
bool zero_by_zero() const
Definition dim-vector.h:317
octave_idx_type & xelem(int i)
Definition dim-vector.h:149
octave_idx_type elem(int i) const
Definition dim-vector.h:160
dim_vector cumulative() const
Return cumulative dimensions.
Definition dim-vector.h:525
dim_vector(const dim_vector &dv)
Definition dim-vector.h:196
bool any_zero() const
Definition dim-vector.h:322
octave_idx_type cum_compute_index(const octave_idx_type *idx) const
Compute a linear index from an index tuple.
Definition dim-vector.h:540
int first_non_singleton(int def=0) const
Definition dim-vector.h:481
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:497
octave_idx_type & elem(int i)
Definition dim-vector.h:155
octave_idx_type xelem(int i) const
Definition dim-vector.h:151
dim_vector(dim_vector &&dv)
Definition dim-vector.h:202
bool all_ones() const
Definition dim-vector.h:330
dim_vector as_column() const
Definition dim-vector.h:416
dim_vector as_row() const
Definition dim-vector.h:424
int length() const
Number of dimensions.
Definition dim-vector.h:272
dim_vector make_nd_vector(octave_idx_type n) const
Definition dim-vector.h:458
bool operator!=(const dim_vector &a, const dim_vector &b)
Definition dim-vector.h:573
bool operator==(const dim_vector &a, const dim_vector &b)
Definition dim-vector.h:557
#define OCTAVE_API
Definition main.in.cc:55
T::size_type numel(const T &str)
Definition oct-string.cc:81