//////////////////////////////////////////////////////////////////////// // // Copyright (C) 1993-2021 The Octave Project Developers // // See the file COPYRIGHT.md in the top-level directory of this // distribution or . // // This file is part of Octave. // // Octave is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Octave is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Octave; see the file COPYING. If not, see // . // //////////////////////////////////////////////////////////////////////// #if ! defined (octave_Array_h) #define octave_Array_h 1 #include "octave-config.h" #include #include #include #include #include #include "dim-vector.h" #include "idx-vector.h" #include "lo-error.h" #include "lo-traits.h" #include "lo-utils.h" #include "oct-refcount.h" #include "oct-sort.h" #include "quit.h" //! N Dimensional Array with copy-on-write semantics. //! //! The Array class is at the root of Octave. It provides a container //! with an arbitrary number of dimensions. The operator () provides //! access to individual elements via subscript and linear indexing. //! Indexing starts at 0. Arrays are column-major order as in Fortran. //! //! @code{.cc} //! // 3 D Array with 10 rows, 20 columns, and 5 pages, filled with 7.0 //! Array A Array A (dim_vector (10, 20, 4), 1); //! //! octave_idx_type n = A.numel (); // returns 800 (10x20x4) //! //! octave_idx_type nr = A.size (0); // returns 10 (number of rows/dimension 0) //! octave_idx_type nc = A.size (1); // returns 20 (number of columns) //! octave_idx_type nc = A.size (2); // returns 4 (size of dimension 3) //! octave_idx_type l6 = A.size (6); // returns 1 (implicit singleton dimension) //! //! // Alternatively, get a dim_vector which represents the dimensions. //! dim_vector dims = A.dims (); //! @endcode //! //! The methods size() and length() as they exist in the STL cause //! confusion in the context of a N dimensional array. //! //! The size() of an array is the length of all dimensions. In Octave, //! the size() function returns a row vector with the length of each //! dimension, or the size of a specific dimension. Only the latter is //! present in liboctave. //! //! Since there is more than 1 dimension, length() would not make sense //! without expliciting which dimension. If the function existed, which //! length should it return? Octave length() function returns the length //! of the longest dimension which is an odd definition, only useful for //! vectors and square matrices. The alternatives numel(), rows(), //! columns(), and size(d) are more explicit and recommended. //! //! ### size_type //! //! Array::size_type is 'octave_idx_type' which is a typedef for 'int' //! or 'long int', depending whether Octave was configured for 64-bit //! indexing. //! //! This is a signed integer which may cause problems when mixed with //! STL containers. The reason is that Octave interacts with Fortran //! routines, providing an interface many Fortran numeric libraries. //! //! ## Subclasses //! //! The following subclasses specializations, will be of most use: //! - Matrix: Array with only 2 dimensions //! - ComplexMatrix: Array> with only 2 dimensions //! - boolNDArray: N dimensional Array //! - ColumnVector: Array with 1 column //! - string_vector: Array with 1 column //! - Cell: Array, equivalent to an Octave cell. // forward declare template with visibility attribute template class OCTARRAY_API Array; using deleter_t = void (*)(void *); template void delete_array (void* p) { delete[] static_cast(p); }; template class Array { protected: //! The real representation of all arrays. class ArrayRep { public: T *m_data; octave_idx_type m_len; octave::refcount m_count; deleter_t m_deleter; ArrayRep (T *d, octave_idx_type l) : m_data (new T [l]), m_len (l), m_count (1), m_deleter (delete_array) { std::copy_n (d, l, m_data); } template ArrayRep (U *d, octave_idx_type l) : m_data (new T [l]), m_len (l), m_count (1), m_deleter (delete_array) { std::copy_n (d, l, m_data); } // Use new instead of setting data to 0 so that fortran_vec and // data always return valid addresses, even for zero-size arrays. ArrayRep (void) : m_data (new T [0]), m_len (0), m_count (1),m_deleter (delete_array) { } explicit ArrayRep (octave_idx_type n) : m_data (new T [n]), m_len (n), m_count (1), m_deleter (delete_array) { } explicit ArrayRep (octave_idx_type n, const T& val) : m_data (new T [n]), m_len (n), m_count (1), m_deleter (delete_array) { std::fill_n (m_data, n, val); } explicit ArrayRep (T *ptr, const dim_vector& dv, deleter_t del) : m_data (ptr), m_len (dv.safe_numel ()), m_count (1), m_deleter (del) { } ArrayRep (const ArrayRep& a) : m_data (new T [a.m_len]), m_len (a.m_len), m_count (1), m_deleter (delete_array) { std::copy_n (a.m_data, a.m_len, m_data); } ~ArrayRep (void) { m_deleter (m_data); } octave_idx_type numel (void) const { return m_len; } // No assignment! ArrayRep& operator = (const ArrayRep&) = delete; }; //-------------------------------------------------------------------- public: void make_unique (void) { if (m_rep->m_count > 1) { ArrayRep *r = new ArrayRep (m_slice_data, m_slice_len); if (--m_rep->m_count == 0) delete m_rep; m_rep = r; m_slice_data = m_rep->m_data; } } typedef T element_type; typedef T value_type; //! Used for operator(), and returned by numel() and size() //! (beware: signed integer) typedef octave_idx_type size_type; typedef typename ref_param::type crefT; typedef bool (*compare_fcn_type) (typename ref_param::type, typename ref_param::type); protected: dim_vector m_dimensions; typename Array::ArrayRep *m_rep; // Rationale: // m_slice_data is a pointer to m_rep->m_data, denoting together with m_slice_len the // actual portion of the data referenced by this Array object. This // allows to make shallow copies not only of a whole array, but also of // contiguous subranges. Every time m_rep is directly manipulated, m_slice_data // and m_slice_len need to be properly updated. T *m_slice_data; octave_idx_type m_slice_len; //! slice constructor Array (const Array& a, const dim_vector& dv, octave_idx_type l, octave_idx_type u) : m_dimensions (dv), m_rep(a.m_rep), m_slice_data (a.m_slice_data+l), m_slice_len (u-l) { m_rep->m_count++; m_dimensions.chop_trailing_singletons (); } private: static OCTARRAY_API typename Array::ArrayRep *nil_rep (void); public: //! Empty ctor (0 by 0). Array (void) : m_dimensions (), m_rep (nil_rep ()), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { m_rep->m_count++; } //! nD uninitialized ctor. explicit Array (const dim_vector& dv) : m_dimensions (dv), m_rep (new typename Array::ArrayRep (dv.safe_numel ())), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { m_dimensions.chop_trailing_singletons (); } //! nD initialized ctor. explicit Array (const dim_vector& dv, const T& val) : m_dimensions (dv), m_rep (new typename Array::ArrayRep (dv.safe_numel ())), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { fill (val); m_dimensions.chop_trailing_singletons (); } // Construct an Array from a pointer to an externally allocated array // of values. PTR must be allocated with operator new. The Array // object takes ownership of PTR and will delete it when the Array // object is deleted. The dimension vector DV must be consistent with // the size of the allocated PTR array. explicit Array (T *ptr, const dim_vector& dv, deleter_t del) : m_dimensions (dv), m_rep (new typename Array::ArrayRep (ptr, dv, del)), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { m_dimensions.chop_trailing_singletons (); } //! Reshape constructor. OCTARRAY_API Array (const Array& a, const dim_vector& dv); //! Constructor from standard library sequence containers. template