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