Mercurial > octave
changeset 31396:7e60506a5428
Prevent implicit instantiations of the Array class template (bug #61711).
* liboctave/array/Array-base.cc: Rename from liboctave/array/Array.cc.
* liboctave/array/Array-C.cc, liboctave/array/Array-b.cc,
liboctave/array/Array-ch.cc, liboctave/array/Array-d.cc,
liboctave/array/Array-f.cc, liboctave/array/Array-fC.cc,
liboctave/array/Array-i.cc, liboctave/array/Array-idx-vec.cc,
liboctave/array/Array-s.cc, liboctave/array/Array-str.cc,
liboctave/array/Array-voidp.cc: Include renamed file.
* liboctave/array/Array-oct.cc: Add new file that includes "Array-base.cc" and
contains extern template declarations for Array<T> classes that are exported by
liboctave.
* libinterp/template-inst/Array-tc.cc: Include "Array-oct.cc". Remove extern
template declarations that are no longer needed.
* libinterp/template-inst/Array.cc: Add new file that includes "Array-oct.cc"
and contains extern template declarations for Array<T> classes that are exported
by liboctinterp.
* liboctave/array/module.mk: Use renamed file and add new file to list of
installed template files.
* libinterp/template-inst/module.mk: Add new file to build system.
* libinterp/module.mk: Install new template file.
author | Markus Mützel <markus.muetzel@gmx.de> |
---|---|
date | Sat, 05 Nov 2022 13:48:24 +0100 |
parents | 068342cc93b8 |
children | 0bd5ea4bd31c |
files | libinterp/module.mk libinterp/template-inst/Array-tc.cc libinterp/template-inst/Array.cc libinterp/template-inst/module.mk liboctave/array/Array-C.cc liboctave/array/Array-b.cc liboctave/array/Array-base.cc liboctave/array/Array-ch.cc liboctave/array/Array-d.cc liboctave/array/Array-f.cc liboctave/array/Array-fC.cc liboctave/array/Array-i.cc liboctave/array/Array-idx-vec.cc liboctave/array/Array-oct.cc liboctave/array/Array-s.cc liboctave/array/Array-str.cc liboctave/array/Array-voidp.cc liboctave/array/Array.cc liboctave/array/module.mk |
diffstat | 19 files changed, 3042 insertions(+), 2933 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/module.mk Sat Nov 05 00:10:11 2022 -0400 +++ b/libinterp/module.mk Sat Nov 05 13:48:24 2022 +0100 @@ -89,7 +89,8 @@ $(LIBINTERP_OPERATORS_INC) \ $(OCTAVE_VALUE_INC) \ $(PARSE_TREE_INC) \ - $(PARSER_INC) + $(PARSER_INC) \ + $(TEMPLATE_INST_INC) noinst_HEADERS += \ %reldir%/options.h \
--- a/libinterp/template-inst/Array-tc.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/libinterp/template-inst/Array-tc.cc Sat Nov 05 13:48:24 2022 +0100 @@ -30,7 +30,7 @@ #endif #include "Array.h" -#include "Array.cc" +#include "Array-oct.cc" #include "ov.h" #include "cdef-class.h" @@ -38,19 +38,6 @@ #include "oct-sort.cc" -// Prevent implicit instantiations on some systems (Windows, others?) -// that can lead to duplicate definitions of static data members. - -extern template class Array<Complex>; -extern template class Array<FloatComplex>; -extern template class Array<bool>; -extern template class Array<char>; -extern template class Array<double>; -extern template class Array<float>; -extern template class Array<octave::idx_vector>; -extern template class Array<octave_idx_type>; -extern template class Array<std::string>; - #if defined (HAVE_PRAGMA_GCC_VISIBILITY) // Visibility attributes are ignored on template instantiation. // As a work-around, set visibility to default overriding compiler options.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/template-inst/Array.cc Sat Nov 05 13:48:24 2022 +0100 @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2022 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// 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 +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + + +// Include this file when instantiating the Array class with new types in +// projects linking to "liboctinterp". + +#include "Array-oct.cc" + +// "Protect" Array<T> instantiations that are exported by liboctinterp from +// being implicitly instantiated in compilation units including this file. + +// instantiated in Array-tc.cc +extern template class Array<octave_value>; +extern template class Array<octave_value *>; +extern template class Array<octave::cdef_object>;
--- a/libinterp/template-inst/module.mk Sat Nov 05 00:10:11 2022 -0400 +++ b/libinterp/template-inst/module.mk Sat Nov 05 13:48:24 2022 +0100 @@ -1,2 +1,5 @@ TEMPLATE_INST_SRC = \ %reldir%/Array-tc.cc + +TEMPLATE_INST_INC = \ + %reldir%/Array.cc
--- a/liboctave/array/Array-C.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-C.cc Sat Nov 05 13:48:24 2022 +0100 @@ -33,7 +33,7 @@ #include "lo-mappers.h" #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #include "oct-sort.cc" // Prevent implicit instantiations on some systems (Windows, others?)
--- a/liboctave/array/Array-b.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-b.cc Sat Nov 05 13:48:24 2022 +0100 @@ -30,7 +30,7 @@ // Instantiate Arrays of bool values. #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #define INLINE_ASCENDING_SORT 1 #define INLINE_DESCENDING_SORT 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/liboctave/array/Array-base.cc Sat Nov 05 13:48:24 2022 +0100 @@ -0,0 +1,2910 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 1993-2022 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// 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 +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +// Do not include this file directly to instantiate the Array<T> template +// class in code outside liboctave. Include "Array-oct.cc" or "Array.cc" +// instead. + +// This file should not include config.h. It is only included in other +// C++ source files that should have included config.h before including +// this file. + +#include <ostream> + +#include "Array-util.h" +#include "Array.h" +#include "lo-mappers.h" +#include "oct-locbuf.h" + +// One dimensional array class. Handles the reference counting for +// all the derived classes. + +template <typename T, typename Alloc> +typename Array<T, Alloc>::ArrayRep * +Array<T, Alloc>::nil_rep (void) +{ + static ArrayRep nr; + return &nr; +} + +// This is similar to the template for containers but specialized for Array. +// Note we can't specialize a member without also specializing the class. +template <typename T, typename Alloc> +Array<T, Alloc>::Array (const Array<T, Alloc>& a, const dim_vector& dv) + : m_dimensions (dv), m_rep (a.m_rep), + m_slice_data (a.m_slice_data), m_slice_len (a.m_slice_len) +{ + bool invalid_size = false; + + octave_idx_type new_numel = 0; + + try + { + // Safe numel may throw a bad_alloc error because the new + // dimensions are too large to allocate. Trap that here so + // we can issue a more helpful diagnostic that is consistent + // with other size mismatch failures. + + new_numel = m_dimensions.safe_numel (); + } + catch (const std::bad_alloc&) + { + invalid_size = true; + } + + if (invalid_size || new_numel != a.numel ()) + { + std::string dimensions_str = a.m_dimensions.str (); + std::string new_dims_str = m_dimensions.str (); + + (*current_liboctave_error_handler) + ("reshape: can't reshape %s array to %s array", + dimensions_str.c_str (), new_dims_str.c_str ()); + } + + // This goes here because if an exception is thrown by the above, + // destructor will be never called. + m_rep->m_count++; + m_dimensions.chop_trailing_singletons (); +} + +// We need to dllexport this constructor from explicit template +// instantiations with specializations. One way to do this is to mark the +// declaration with the corresponding attributes and define the +// constructor separately here. +template <typename T, typename Alloc> +Array<T, Alloc>::Array (T *ptr, const dim_vector& dv, const Alloc& xallocator) + : m_dimensions (dv), + m_rep (new typename Array<T, Alloc>::ArrayRep (ptr, dv, xallocator)), + m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) +{ + m_dimensions.chop_trailing_singletons (); +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::fill (const T& val) +{ + if (m_rep->m_count > 1) + { + --m_rep->m_count; + m_rep = new ArrayRep (numel (), val); + m_slice_data = m_rep->m_data; + } + else + std::fill_n (m_slice_data, m_slice_len, val); +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::clear (void) +{ + if (--m_rep->m_count == 0) + delete m_rep; + + m_rep = nil_rep (); + m_rep->m_count++; + m_slice_data = m_rep->m_data; + m_slice_len = m_rep->m_len; + + m_dimensions = dim_vector (); +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::clear (const dim_vector& dv) +{ + if (--m_rep->m_count == 0) + delete m_rep; + + m_rep = new ArrayRep (dv.safe_numel ()); + m_slice_data = m_rep->m_data; + m_slice_len = m_rep->m_len; + + m_dimensions = dv; + m_dimensions.chop_trailing_singletons (); +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::squeeze (void) const +{ + Array<T, Alloc> retval = *this; + + if (ndims () > 2) + { + bool dims_changed = false; + + dim_vector new_dimensions = m_dimensions; + + int k = 0; + + for (int i = 0; i < ndims (); i++) + { + if (m_dimensions(i) == 1) + dims_changed = true; + else + new_dimensions(k++) = m_dimensions(i); + } + + if (dims_changed) + { + switch (k) + { + case 0: + new_dimensions = dim_vector (1, 1); + break; + + case 1: + { + octave_idx_type tmp = new_dimensions(0); + + new_dimensions.resize (2); + + new_dimensions(0) = tmp; + new_dimensions(1) = 1; + } + break; + + default: + new_dimensions.resize (k); + break; + } + } + + retval = Array<T, Alloc> (*this, new_dimensions); + } + + return retval; +} + +template <typename T, typename Alloc> +octave_idx_type +Array<T, Alloc>::compute_index (octave_idx_type i, octave_idx_type j) const +{ + return ::compute_index (i, j, m_dimensions); +} + +template <typename T, typename Alloc> +octave_idx_type +Array<T, Alloc>::compute_index (octave_idx_type i, octave_idx_type j, + octave_idx_type k) const +{ + return ::compute_index (i, j, k, m_dimensions); +} + +template <typename T, typename Alloc> +octave_idx_type +Array<T, Alloc>::compute_index (const Array<octave_idx_type>& ra_idx) const +{ + return ::compute_index (ra_idx, m_dimensions); +} + +template <typename T, typename Alloc> +T& +Array<T, Alloc>::checkelem (octave_idx_type n) +{ + // Do checks directly to avoid recomputing m_slice_len. + if (n < 0) + octave::err_invalid_index (n); + if (n >= m_slice_len) + octave::err_index_out_of_range (1, 1, n+1, m_slice_len, m_dimensions); + + return elem (n); +} + +template <typename T, typename Alloc> +T& +Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j) +{ + return elem (compute_index (i, j)); +} + +template <typename T, typename Alloc> +T& +Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j, octave_idx_type k) +{ + return elem (compute_index (i, j, k)); +} + +template <typename T, typename Alloc> +T& +Array<T, Alloc>::checkelem (const Array<octave_idx_type>& ra_idx) +{ + return elem (compute_index (ra_idx)); +} + +template <typename T, typename Alloc> +typename Array<T, Alloc>::crefT +Array<T, Alloc>::checkelem (octave_idx_type n) const +{ + // Do checks directly to avoid recomputing m_slice_len. + if (n < 0) + octave::err_invalid_index (n); + if (n >= m_slice_len) + octave::err_index_out_of_range (1, 1, n+1, m_slice_len, m_dimensions); + + return elem (n); +} + +template <typename T, typename Alloc> +typename Array<T, Alloc>::crefT +Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j) const +{ + return elem (compute_index (i, j)); +} + +template <typename T, typename Alloc> +typename Array<T, Alloc>::crefT +Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j, + octave_idx_type k) const +{ + return elem (compute_index (i, j, k)); +} + +template <typename T, typename Alloc> +typename Array<T, Alloc>::crefT +Array<T, Alloc>::checkelem (const Array<octave_idx_type>& ra_idx) const +{ + return elem (compute_index (ra_idx)); +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::column (octave_idx_type k) const +{ + octave_idx_type r = m_dimensions(0); + + return Array<T, Alloc> (*this, dim_vector (r, 1), k*r, k*r + r); +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::page (octave_idx_type k) const +{ + octave_idx_type r = m_dimensions(0); + octave_idx_type c = m_dimensions(1); + octave_idx_type p = r*c; + + return Array<T, Alloc> (*this, dim_vector (r, c), k*p, k*p + p); +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::linear_slice (octave_idx_type lo, octave_idx_type up) const +{ + if (up < lo) + up = lo; + + return Array<T, Alloc> (*this, dim_vector (up - lo, 1), lo, up); +} + +// Helper class for multi-d dimension permuting (generalized transpose). +class rec_permute_helper +{ +public: + rec_permute_helper (const dim_vector& dv, const Array<octave_idx_type>& perm) + + : m_n (dv.ndims ()), m_top (0), m_dim (new octave_idx_type [2*m_n]), + m_stride (m_dim + m_n), m_use_blk (false) + { + assert (m_n == perm.numel ()); + + // Get cumulative dimensions. + OCTAVE_LOCAL_BUFFER (octave_idx_type, cdim, m_n+1); + cdim[0] = 1; + for (int i = 1; i < m_n+1; i++) cdim[i] = cdim[i-1] * dv(i-1); + + // Setup the permuted strides. + for (int k = 0; k < m_n; k++) + { + int kk = perm(k); + m_dim[k] = dv(kk); + m_stride[k] = cdim[kk]; + } + + // Reduce contiguous runs. + for (int k = 1; k < m_n; k++) + { + if (m_stride[k] == m_stride[m_top]*m_dim[m_top]) + m_dim[m_top] *= m_dim[k]; + else + { + m_top++; + m_dim[m_top] = m_dim[k]; + m_stride[m_top] = m_stride[k]; + } + } + + // Determine whether we can use block transposes. + m_use_blk = m_top >= 1 && m_stride[1] == 1 && m_stride[0] == m_dim[1]; + + } + + // No copying! + + rec_permute_helper (const rec_permute_helper&) = delete; + + rec_permute_helper& operator = (const rec_permute_helper&) = delete; + + ~rec_permute_helper (void) { delete [] m_dim; } + + template <typename T> + void permute (const T *src, T *dest) const { do_permute (src, dest, m_top); } + + // Helper method for fast blocked transpose. + template <typename T> + static T * + blk_trans (const T *src, T *dest, octave_idx_type nr, octave_idx_type nc) + { + static const octave_idx_type m = 8; + OCTAVE_LOCAL_BUFFER (T, blk, m*m); + for (octave_idx_type kr = 0; kr < nr; kr += m) + for (octave_idx_type kc = 0; kc < nc; kc += m) + { + octave_idx_type lr = std::min (m, nr - kr); + octave_idx_type lc = std::min (m, nc - kc); + if (lr == m && lc == m) + { + const T *ss = src + kc * nr + kr; + for (octave_idx_type j = 0; j < m; j++) + for (octave_idx_type i = 0; i < m; i++) + blk[j*m+i] = ss[j*nr + i]; + T *dd = dest + kr * nc + kc; + for (octave_idx_type j = 0; j < m; j++) + for (octave_idx_type i = 0; i < m; i++) + dd[j*nc+i] = blk[i*m+j]; + } + else + { + const T *ss = src + kc * nr + kr; + for (octave_idx_type j = 0; j < lc; j++) + for (octave_idx_type i = 0; i < lr; i++) + blk[j*m+i] = ss[j*nr + i]; + T *dd = dest + kr * nc + kc; + for (octave_idx_type j = 0; j < lr; j++) + for (octave_idx_type i = 0; i < lc; i++) + dd[j*nc+i] = blk[i*m+j]; + } + } + + return dest + nr*nc; + } + +private: + + // Recursive N-D generalized transpose + template <typename T> + T * do_permute (const T *src, T *dest, int lev) const + { + if (lev == 0) + { + octave_idx_type step = m_stride[0]; + octave_idx_type len = m_dim[0]; + if (step == 1) + { + std::copy_n (src, len, dest); + dest += len; + } + else + { + for (octave_idx_type i = 0, j = 0; i < len; i++, j += step) + dest[i] = src[j]; + + dest += len; + } + } + else if (m_use_blk && lev == 1) + dest = blk_trans (src, dest, m_dim[1], m_dim[0]); + else + { + octave_idx_type step = m_stride[lev]; + octave_idx_type len = m_dim[lev]; + for (octave_idx_type i = 0, j = 0; i < len; i++, j+= step) + dest = do_permute (src + i * step, dest, lev-1); + } + + return dest; + } + + //-------- + + // STRIDE occupies the last half of the space allocated for dim to + // avoid a double allocation. + + int m_n; + int m_top; + octave_idx_type *m_dim; + octave_idx_type *m_stride; + bool m_use_blk; +}; + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::permute (const Array<octave_idx_type>& perm_vec_arg, bool inv) const +{ + Array<T, Alloc> retval; + + Array<octave_idx_type> perm_vec = perm_vec_arg; + + dim_vector dv = dims (); + + int perm_vec_len = perm_vec_arg.numel (); + + if (perm_vec_len < dv.ndims ()) + (*current_liboctave_error_handler) + ("%s: invalid permutation vector", inv ? "ipermute" : "permute"); + + dim_vector dv_new = dim_vector::alloc (perm_vec_len); + + // Append singleton dimensions as needed. + dv.resize (perm_vec_len, 1); + + // Need this array to check for identical elements in permutation array. + OCTAVE_LOCAL_BUFFER_INIT (bool, checked, perm_vec_len, false); + + bool identity = true; + + // Find dimension vector of permuted array. + for (int i = 0; i < perm_vec_len; i++) + { + octave_idx_type perm_elt = perm_vec.elem (i); + if (perm_elt >= perm_vec_len || perm_elt < 0) + (*current_liboctave_error_handler) + ("%s: permutation vector contains an invalid element", + inv ? "ipermute" : "permute"); + + if (checked[perm_elt]) + (*current_liboctave_error_handler) + ("%s: permutation vector cannot contain identical elements", + inv ? "ipermute" : "permute"); + else + { + checked[perm_elt] = true; + identity = identity && perm_elt == i; + } + } + + if (identity) + return *this; + + if (inv) + { + for (int i = 0; i < perm_vec_len; i++) + perm_vec(perm_vec_arg(i)) = i; + } + + for (int i = 0; i < perm_vec_len; i++) + dv_new(i) = dv(perm_vec(i)); + + retval = Array<T, Alloc> (dv_new); + + if (numel () > 0) + { + rec_permute_helper rh (dv, perm_vec); + rh.permute (data (), retval.fortran_vec ()); + } + + return retval; +} + +// Helper class for multi-d index reduction and recursive +// indexing/indexed assignment. Rationale: we could avoid recursion +// using a state machine instead. However, using recursion is much +// more amenable to possible parallelization in the future. +// Also, the recursion solution is cleaner and more understandable. + +class rec_index_helper +{ +public: + rec_index_helper (const dim_vector& dv, const Array<octave::idx_vector>& ia) + : m_n (ia.numel ()), m_top (0), m_dim (new octave_idx_type [2*m_n]), + m_cdim (m_dim + m_n), m_idx (new octave::idx_vector [m_n]) + { + assert (m_n > 0 && (dv.ndims () == std::max (m_n, 2))); + + m_dim[0] = dv(0); + m_cdim[0] = 1; + m_idx[0] = ia(0); + + for (int i = 1; i < m_n; i++) + { + // Try reduction... + if (m_idx[m_top].maybe_reduce (m_dim[m_top], ia(i), dv(i))) + { + // Reduction successful, fold dimensions. + m_dim[m_top] *= dv(i); + } + else + { + // Unsuccessful, store index & cumulative m_dim. + m_top++; + m_idx[m_top] = ia(i); + m_dim[m_top] = dv(i); + m_cdim[m_top] = m_cdim[m_top-1] * m_dim[m_top-1]; + } + } + } + + // No copying! + + rec_index_helper (const rec_index_helper&) = delete; + + rec_index_helper& operator = (const rec_index_helper&) = delete; + + ~rec_index_helper (void) { delete [] m_idx; delete [] m_dim; } + + template <typename T> + void index (const T *src, T *dest) const { do_index (src, dest, m_top); } + + template <typename T> + void assign (const T *src, T *dest) const { do_assign (src, dest, m_top); } + + template <typename T> + void fill (const T& val, T *dest) const { do_fill (val, dest, m_top); } + + bool is_cont_range (octave_idx_type& l, octave_idx_type& u) const + { + return m_top == 0 && m_idx[0].is_cont_range (m_dim[0], l, u); + } + +private: + + // Recursive N-D indexing + template <typename T> + T * do_index (const T *src, T *dest, int lev) const + { + if (lev == 0) + dest += m_idx[0].index (src, m_dim[0], dest); + else + { + octave_idx_type nn = m_idx[lev].length (m_dim[lev]); + octave_idx_type d = m_cdim[lev]; + for (octave_idx_type i = 0; i < nn; i++) + dest = do_index (src + d*m_idx[lev].xelem (i), dest, lev-1); + } + + return dest; + } + + // Recursive N-D indexed assignment + template <typename T> + const T * do_assign (const T *src, T *dest, int lev) const + { + if (lev == 0) + src += m_idx[0].assign (src, m_dim[0], dest); + else + { + octave_idx_type nn = m_idx[lev].length (m_dim[lev]); + octave_idx_type d = m_cdim[lev]; + for (octave_idx_type i = 0; i < nn; i++) + src = do_assign (src, dest + d*m_idx[lev].xelem (i), lev-1); + } + + return src; + } + + // Recursive N-D indexed assignment + template <typename T> + void do_fill (const T& val, T *dest, int lev) const + { + if (lev == 0) + m_idx[0].fill (val, m_dim[0], dest); + else + { + octave_idx_type nn = m_idx[lev].length (m_dim[lev]); + octave_idx_type d = m_cdim[lev]; + for (octave_idx_type i = 0; i < nn; i++) + do_fill (val, dest + d*m_idx[lev].xelem (i), lev-1); + } + } + + //-------- + + // CDIM occupies the last half of the space allocated for dim to + // avoid a double allocation. + + int m_n; + int m_top; + octave_idx_type *m_dim; + octave_idx_type *m_cdim; + octave::idx_vector *m_idx; +}; + +// Helper class for multi-d recursive resizing +// This handles resize () in an efficient manner, touching memory only +// once (apart from reinitialization) +class rec_resize_helper +{ +public: + rec_resize_helper (const dim_vector& ndv, const dim_vector& odv) + : m_cext (nullptr), m_sext (nullptr), m_dext (nullptr), m_n (0) + { + int l = ndv.ndims (); + assert (odv.ndims () == l); + octave_idx_type ld = 1; + int i = 0; + for (; i < l-1 && ndv(i) == odv(i); i++) ld *= ndv(i); + m_n = l - i; + m_cext = new octave_idx_type [3*m_n]; + // Trick to avoid three allocations + m_sext = m_cext + m_n; + m_dext = m_sext + m_n; + + octave_idx_type sld = ld; + octave_idx_type dld = ld; + for (int j = 0; j < m_n; j++) + { + m_cext[j] = std::min (ndv(i+j), odv(i+j)); + m_sext[j] = sld *= odv(i+j); + m_dext[j] = dld *= ndv(i+j); + } + m_cext[0] *= ld; + } + + // No copying! + + rec_resize_helper (const rec_resize_helper&) = delete; + + rec_resize_helper& operator = (const rec_resize_helper&) = delete; + + ~rec_resize_helper (void) { delete [] m_cext; } + + template <typename T> + void resize_fill (const T *src, T *dest, const T& rfv) const + { do_resize_fill (src, dest, rfv, m_n-1); } + +private: + + // recursive resizing + template <typename T> + void do_resize_fill (const T *src, T *dest, const T& rfv, int lev) const + { + if (lev == 0) + { + std::copy_n (src, m_cext[0], dest); + std::fill_n (dest + m_cext[0], m_dext[0] - m_cext[0], rfv); + } + else + { + octave_idx_type sd, dd, k; + sd = m_sext[lev-1]; + dd = m_dext[lev-1]; + for (k = 0; k < m_cext[lev]; k++) + do_resize_fill (src + k * sd, dest + k * dd, rfv, lev - 1); + + std::fill_n (dest + k * dd, m_dext[lev] - k * dd, rfv); + } + } + + //-------- + + octave_idx_type *m_cext; + octave_idx_type *m_sext; + octave_idx_type *m_dext; + int m_n; +}; + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::index (const octave::idx_vector& i) const +{ + // Colon: + // + // object | index | result orientation + // ---------+----------+------------------- + // anything | colon | column vector + // + // Numeric array or logical mask: + // + // Note that logical mask indices are always transformed to vectors + // before we see them. + // + // object | index | result orientation + // ---------+----------+------------------- + // vector | vector | indexed object + // | other | same size as index + // ---------+----------+------------------- + // array | anything | same size as index + + octave_idx_type n = numel (); + Array<T, Alloc> retval; + + if (i.is_colon ()) + { + // A(:) produces a shallow copy as a column vector. + retval = Array<T, Alloc> (*this, dim_vector (n, 1)); + } + else + { + if (i.extent (n) != n) + octave::err_index_out_of_range (1, 1, i.extent (n), n, m_dimensions); + + dim_vector result_dims = i.orig_dimensions (); + octave_idx_type idx_len = i.length (); + + if (n != 1 && is_nd_vector () && idx_len != 1 + && result_dims.is_nd_vector ()) + { + // Indexed object and index are both vectors. Set result size + // and orientation as above. + + dim_vector dv = dims (); + + result_dims = dv.make_nd_vector (idx_len); + } + + octave_idx_type l, u; + if (idx_len != 0 && i.is_cont_range (n, l, u)) + // If suitable, produce a shallow slice. + retval = Array<T, Alloc> (*this, result_dims, l, u); + else + { + // Don't use resize here to avoid useless initialization for POD + // types. + retval = Array<T, Alloc> (result_dims); + + if (idx_len != 0) + i.index (data (), n, retval.fortran_vec ()); + } + } + + return retval; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::index (const octave::idx_vector& i, const octave::idx_vector& j) const +{ + // Get dimensions, allowing Fortran indexing in the 2nd dim. + dim_vector dv = m_dimensions.redim (2); + octave_idx_type r = dv(0); + octave_idx_type c = dv(1); + Array<T, Alloc> retval; + + if (i.is_colon () && j.is_colon ()) + { + // A(:,:) produces a shallow copy. + retval = Array<T, Alloc> (*this, dv); + } + else + { + if (i.extent (r) != r) + octave::err_index_out_of_range (2, 1, i.extent (r), r, m_dimensions); // throws + if (j.extent (c) != c) + octave::err_index_out_of_range (2, 2, j.extent (c), c, m_dimensions); // throws + + octave_idx_type n = numel (); + octave_idx_type il = i.length (r); + octave_idx_type jl = j.length (c); + + octave::idx_vector ii (i); + + if (ii.maybe_reduce (r, j, c)) + { + octave_idx_type l, u; + if (ii.length () > 0 && ii.is_cont_range (n, l, u)) + // If suitable, produce a shallow slice. + retval = Array<T, Alloc> (*this, dim_vector (il, jl), l, u); + else + { + // Don't use resize to avoid useless initialization for POD types. + retval = Array<T, Alloc> (dim_vector (il, jl)); + + ii.index (data (), n, retval.fortran_vec ()); + } + } + else + { + // Don't use resize to avoid useless initialization for POD types. + retval = Array<T, Alloc> (dim_vector (il, jl)); + + const T *src = data (); + T *dest = retval.fortran_vec (); + + for (octave_idx_type k = 0; k < jl; k++) + dest += i.index (src + r * j.xelem (k), r, dest); + } + } + + return retval; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::index (const Array<octave::idx_vector>& ia) const +{ + int ial = ia.numel (); + Array<T, Alloc> retval; + + // FIXME: is this dispatching necessary? + if (ial == 1) + retval = index (ia(0)); + else if (ial == 2) + retval = index (ia(0), ia(1)); + else if (ial > 0) + { + // Get dimensions, allowing Fortran indexing in the last dim. + dim_vector dv = m_dimensions.redim (ial); + + // Check for out of bounds conditions. + bool all_colons = true; + for (int i = 0; i < ial; i++) + { + if (ia(i).extent (dv(i)) != dv(i)) + octave::err_index_out_of_range (ial, i+1, ia(i).extent (dv(i)), dv(i), + m_dimensions); // throws + + all_colons = all_colons && ia(i).is_colon (); + } + + if (all_colons) + { + // A(:,:,...,:) produces a shallow copy. + dv.chop_trailing_singletons (); + retval = Array<T, Alloc> (*this, dv); + } + else + { + // Form result dimensions. + dim_vector rdv = dim_vector::alloc (ial); + for (int i = 0; i < ial; i++) rdv(i) = ia(i).length (dv(i)); + rdv.chop_trailing_singletons (); + + // Prepare for recursive indexing + rec_index_helper rh (dv, ia); + + octave_idx_type l, u; + if (rh.is_cont_range (l, u)) + // If suitable, produce a shallow slice. + retval = Array<T, Alloc> (*this, rdv, l, u); + else + { + // Don't use resize to avoid useless initialization for POD types. + retval = Array<T, Alloc> (rdv); + + // Do it. + rh.index (data (), retval.fortran_vec ()); + } + } + } + + return retval; +} + +// The default fill value. Override if you want a different one. + +template <typename T, typename Alloc> +T +Array<T, Alloc>::resize_fill_value (void) const +{ + static T zero = T (); + return zero; +} + +// Yes, we could do resize using index & assign. However, that would +// possibly involve a lot more memory traffic than we actually need. + +template <typename T, typename Alloc> +void +Array<T, Alloc>::resize1 (octave_idx_type n, const T& rfv) +{ + if (n < 0 || ndims () != 2) + octave::err_invalid_resize (); + + dim_vector dv; + // This is driven by Matlab's behavior of giving a *row* vector + // on some out-of-bounds assignments. Specifically, Matlab + // allows a(i) with out-of-bouds i when a is either of 0x0, 1x0, + // 1x1, 0xN, and gives a row vector in all cases (yes, even the + // last one, search me why). Giving a column vector would make + // much more sense (given the way trailing singleton dims are + // treated). + bool invalid = false; + if (rows () == 0 || rows () == 1) + dv = dim_vector (1, n); + else if (columns () == 1) + dv = dim_vector (n, 1); + else + invalid = true; + + if (invalid) + octave::err_invalid_resize (); + + octave_idx_type nx = numel (); + if (n == nx - 1 && n > 0) + { + // Stack "pop" operation. + if (m_rep->m_count == 1) + m_slice_data[m_slice_len-1] = T (); + m_slice_len--; + m_dimensions = dv; + } + else if (n == nx + 1 && nx > 0) + { + // Stack "push" operation. + if (m_rep->m_count == 1 + && m_slice_data + m_slice_len < m_rep->m_data + m_rep->m_len) + { + m_slice_data[m_slice_len++] = rfv; + m_dimensions = dv; + } + else + { + static const octave_idx_type max_stack_chunk = 1024; + octave_idx_type nn = n + std::min (nx, max_stack_chunk); + Array<T, Alloc> tmp (Array<T, Alloc> (dim_vector (nn, 1)), dv, 0, n); + T *dest = tmp.fortran_vec (); + + std::copy_n (data (), nx, dest); + dest[nx] = rfv; + + *this = tmp; + } + } + else if (n != nx) + { + Array<T, Alloc> tmp = Array<T, Alloc> (dv); + T *dest = tmp.fortran_vec (); + + octave_idx_type n0 = std::min (n, nx); + octave_idx_type n1 = n - n0; + std::copy_n (data (), n0, dest); + std::fill_n (dest + n0, n1, rfv); + + *this = tmp; + } +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::resize2 (octave_idx_type r, octave_idx_type c, const T& rfv) +{ + if (r < 0 || c < 0 || ndims () != 2) + octave::err_invalid_resize (); + + octave_idx_type rx = rows (); + octave_idx_type cx = columns (); + if (r != rx || c != cx) + { + Array<T, Alloc> tmp = Array<T, Alloc> (dim_vector (r, c)); + T *dest = tmp.fortran_vec (); + + octave_idx_type r0 = std::min (r, rx); + octave_idx_type r1 = r - r0; + octave_idx_type c0 = std::min (c, cx); + octave_idx_type c1 = c - c0; + const T *src = data (); + if (r == rx) + { + std::copy_n (src, r * c0, dest); + dest += r * c0; + } + else + { + for (octave_idx_type k = 0; k < c0; k++) + { + std::copy_n (src, r0, dest); + src += rx; + dest += r0; + std::fill_n (dest, r1, rfv); + dest += r1; + } + } + + std::fill_n (dest, r * c1, rfv); + + *this = tmp; + } +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::resize (const dim_vector& dv, const T& rfv) +{ + int dvl = dv.ndims (); + if (dvl == 2) + resize2 (dv(0), dv(1), rfv); + else if (m_dimensions != dv) + { + if (m_dimensions.ndims () > dvl || dv.any_neg ()) + octave::err_invalid_resize (); + + Array<T, Alloc> tmp (dv); + // Prepare for recursive resizing. + rec_resize_helper rh (dv, m_dimensions.redim (dvl)); + + // Do it. + rh.resize_fill (data (), tmp.fortran_vec (), rfv); + *this = tmp; + } +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const +{ + Array<T, Alloc> tmp = *this; + if (resize_ok) + { + octave_idx_type n = numel (); + octave_idx_type nx = i.extent (n); + if (n != nx) + { + if (i.is_scalar ()) + return Array<T, Alloc> (dim_vector (1, 1), rfv); + else + tmp.resize1 (nx, rfv); + } + + if (tmp.numel () != nx) + return Array<T, Alloc> (); + } + + return tmp.index (i); +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::index (const octave::idx_vector& i, const octave::idx_vector& j, + bool resize_ok, const T& rfv) const +{ + Array<T, Alloc> tmp = *this; + if (resize_ok) + { + dim_vector dv = m_dimensions.redim (2); + octave_idx_type r = dv(0); + octave_idx_type c = dv(1); + octave_idx_type rx = i.extent (r); + octave_idx_type cx = j.extent (c); + if (r != rx || c != cx) + { + if (i.is_scalar () && j.is_scalar ()) + return Array<T, Alloc> (dim_vector (1, 1), rfv); + else + tmp.resize2 (rx, cx, rfv); + } + + if (tmp.rows () != rx || tmp.columns () != cx) + return Array<T, Alloc> (); + } + + return tmp.index (i, j); +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::index (const Array<octave::idx_vector>& ia, + bool resize_ok, const T& rfv) const +{ + Array<T, Alloc> tmp = *this; + if (resize_ok) + { + int ial = ia.numel (); + dim_vector dv = m_dimensions.redim (ial); + dim_vector dvx = dim_vector::alloc (ial); + for (int i = 0; i < ial; i++) + dvx(i) = ia(i).extent (dv(i)); + if (! (dvx == dv)) + { + bool all_scalars = true; + for (int i = 0; i < ial; i++) + all_scalars = all_scalars && ia(i).is_scalar (); + if (all_scalars) + return Array<T, Alloc> (dim_vector (1, 1), rfv); + else + tmp.resize (dvx, rfv); + + if (tmp.m_dimensions != dvx) + return Array<T, Alloc> (); + } + } + + return tmp.index (ia); +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::assign (const octave::idx_vector& i, const Array<T, Alloc>& rhs, const T& rfv) +{ + octave_idx_type n = numel (); + octave_idx_type rhl = rhs.numel (); + + if (rhl != 1 && i.length (n) != rhl) + octave::err_nonconformant ("=", dim_vector(i.length(n), 1), rhs.dims()); + + octave_idx_type nx = i.extent (n); + bool colon = i.is_colon_equiv (nx); + // Try to resize first if necessary. + if (nx != n) + { + // Optimize case A = []; A(1:n) = X with A empty. + if (m_dimensions.zero_by_zero () && colon) + { + if (rhl == 1) + *this = Array<T, Alloc> (dim_vector (1, nx), rhs(0)); + else + *this = Array<T, Alloc> (rhs, dim_vector (1, nx)); + return; + } + + resize1 (nx, rfv); + n = numel (); + } + + if (colon) + { + // A(:) = X makes a full fill or a shallow copy. + if (rhl == 1) + fill (rhs(0)); + else + *this = rhs.reshape (m_dimensions); + } + else + { + if (rhl == 1) + i.fill (rhs(0), n, fortran_vec ()); + else + i.assign (rhs.data (), n, fortran_vec ()); + } +} + +// Assignment to a 2-dimensional array +template <typename T, typename Alloc> +void +Array<T, Alloc>::assign (const octave::idx_vector& i, const octave::idx_vector& j, + const Array<T, Alloc>& rhs, const T& rfv) +{ + bool initial_dims_all_zero = m_dimensions.all_zero (); + + // Get RHS extents, discarding singletons. + dim_vector rhdv = rhs.dims (); + + // Get LHS extents, allowing Fortran indexing in the second dim. + dim_vector dv = m_dimensions.redim (2); + + // Check for out-of-bounds and form resizing m_dimensions. + dim_vector rdv; + + // In the special when all dimensions are zero, colons are allowed + // to inquire the shape of RHS. The rules are more obscure, so we + // solve that elsewhere. + if (initial_dims_all_zero) + rdv = zero_dims_inquire (i, j, rhdv); + else + { + rdv(0) = i.extent (dv(0)); + rdv(1) = j.extent (dv(1)); + } + + bool isfill = rhs.numel () == 1; + octave_idx_type il = i.length (rdv(0)); + octave_idx_type jl = j.length (rdv(1)); + rhdv.chop_all_singletons (); + bool match = (isfill + || (rhdv.ndims () == 2 && il == rhdv(0) && jl == rhdv(1))); + match = match || (il == 1 && jl == rhdv(0) && rhdv(1) == 1); + + if (match) + { + bool all_colons = (i.is_colon_equiv (rdv(0)) + && j.is_colon_equiv (rdv(1))); + // Resize if requested. + if (rdv != dv) + { + // Optimize case A = []; A(1:m, 1:n) = X + if (dv.zero_by_zero () && all_colons) + { + if (isfill) + *this = Array<T, Alloc> (rdv, rhs(0)); + else + *this = Array<T, Alloc> (rhs, rdv); + return; + } + + resize (rdv, rfv); + dv = m_dimensions; + } + + if (all_colons) + { + // A(:,:) = X makes a full fill or a shallow copy + if (isfill) + fill (rhs(0)); + else + *this = rhs.reshape (m_dimensions); + } + else + { + // The actual work. + octave_idx_type n = numel (); + octave_idx_type r = dv(0); + octave_idx_type c = dv(1); + octave::idx_vector ii (i); + + const T *src = rhs.data (); + T *dest = fortran_vec (); + + // Try reduction first. + if (ii.maybe_reduce (r, j, c)) + { + if (isfill) + ii.fill (*src, n, dest); + else + ii.assign (src, n, dest); + } + else + { + if (isfill) + { + for (octave_idx_type k = 0; k < jl; k++) + i.fill (*src, r, dest + r * j.xelem (k)); + } + else + { + for (octave_idx_type k = 0; k < jl; k++) + src += i.assign (src, r, dest + r * j.xelem (k)); + } + } + } + } + // any empty RHS can be assigned to an empty LHS + else if ((il != 0 && jl != 0) || (rhdv(0) != 0 && rhdv(1) != 0)) + octave::err_nonconformant ("=", il, jl, rhs.dim1 (), rhs.dim2 ()); +} + +// Assignment to a multi-dimensional array +template <typename T, typename Alloc> +void +Array<T, Alloc>::assign (const Array<octave::idx_vector>& ia, + const Array<T, Alloc>& rhs, const T& rfv) +{ + int ial = ia.numel (); + + // FIXME: is this dispatching necessary / desirable? + if (ial == 1) + assign (ia(0), rhs, rfv); + else if (ial == 2) + assign (ia(0), ia(1), rhs, rfv); + else if (ial > 0) + { + bool initial_dims_all_zero = m_dimensions.all_zero (); + + // Get RHS extents, discarding singletons. + dim_vector rhdv = rhs.dims (); + + // Get LHS extents, allowing Fortran indexing in the second dim. + dim_vector dv = m_dimensions.redim (ial); + + // Get the extents forced by indexing. + dim_vector rdv; + + // In the special when all dimensions are zero, colons are + // allowed to inquire the shape of RHS. The rules are more + // obscure, so we solve that elsewhere. + if (initial_dims_all_zero) + rdv = zero_dims_inquire (ia, rhdv); + else + { + rdv = dim_vector::alloc (ial); + for (int i = 0; i < ial; i++) + rdv(i) = ia(i).extent (dv(i)); + } + + // Check whether LHS and RHS match, up to singleton dims. + bool match = true; + bool all_colons = true; + bool isfill = rhs.numel () == 1; + + rhdv.chop_all_singletons (); + int j = 0; + int rhdvl = rhdv.ndims (); + for (int i = 0; i < ial; i++) + { + all_colons = all_colons && ia(i).is_colon_equiv (rdv(i)); + octave_idx_type l = ia(i).length (rdv(i)); + if (l == 1) continue; + match = match && j < rhdvl && l == rhdv(j++); + } + + match = match && (j == rhdvl || rhdv(j) == 1); + match = match || isfill; + + if (match) + { + // Resize first if necessary. + if (rdv != dv) + { + // Optimize case A = []; A(1:m, 1:n) = X + if (dv.zero_by_zero () && all_colons) + { + rdv.chop_trailing_singletons (); + if (isfill) + *this = Array<T, Alloc> (rdv, rhs(0)); + else + *this = Array<T, Alloc> (rhs, rdv); + return; + } + + resize (rdv, rfv); + dv = rdv; + } + + if (all_colons) + { + // A(:,:,...,:) = X makes a full fill or a shallow copy. + if (isfill) + fill (rhs(0)); + else + *this = rhs.reshape (m_dimensions); + } + else + { + // Do the actual work. + + // Prepare for recursive indexing + rec_index_helper rh (dv, ia); + + // Do it. + if (isfill) + rh.fill (rhs(0), fortran_vec ()); + else + rh.assign (rhs.data (), fortran_vec ()); + } + } + else + { + // dimension mismatch, unless LHS and RHS both empty + bool lhsempty, rhsempty; + lhsempty = rhsempty = false; + dim_vector lhs_dv = dim_vector::alloc (ial); + for (int i = 0; i < ial; i++) + { + octave_idx_type l = ia(i).length (rdv(i)); + lhs_dv(i) = l; + lhsempty = lhsempty || (l == 0); + rhsempty = rhsempty || (rhdv(j++) == 0); + } + if (! lhsempty || ! rhsempty) + { + lhs_dv.chop_trailing_singletons (); + octave::err_nonconformant ("=", lhs_dv, rhdv); + } + } + } +} + +/* +%!shared a +%! a = [1 2; 3 4]; +%!error <op1 is 1x2, op2 is 1x3> a(1,:) = [1 2 3] +%!error <op1 is 2x1, op2 is 1x3> a(:,1) = [1 2 3] +%!error <op1 is 2x2, op2 is 2x1> a(1:2,2:3) = [1;2] +*/ + +template <typename T, typename Alloc> +void +Array<T, Alloc>::delete_elements (const octave::idx_vector& i) +{ + octave_idx_type n = numel (); + if (i.is_colon ()) + { + *this = Array<T, Alloc> (); + } + else if (i.length (n) != 0) + { + if (i.extent (n) != n) + octave::err_del_index_out_of_range (true, i.extent (n), n); + + octave_idx_type l, u; + bool col_vec = ndims () == 2 && columns () == 1 && rows () != 1; + if (i.is_scalar () && i(0) == n-1 && m_dimensions.isvector ()) + { + // Stack "pop" operation. + resize1 (n-1); + } + else if (i.is_cont_range (n, l, u)) + { + // Special case deleting a contiguous range. + octave_idx_type m = n + l - u; + Array<T, Alloc> tmp (dim_vector (col_vec ? m : 1, ! col_vec ? m : 1)); + const T *src = data (); + T *dest = tmp.fortran_vec (); + std::copy_n (src, l, dest); + std::copy (src + u, src + n, dest + l); + *this = tmp; + } + else + { + // Use index. + *this = index (i.complement (n)); + } + } +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::delete_elements (int dim, const octave::idx_vector& i) +{ + if (dim < 0 || dim >= ndims ()) + (*current_liboctave_error_handler) ("invalid dimension in delete_elements"); + + octave_idx_type n = m_dimensions(dim); + if (i.is_colon ()) + { + *this = Array<T, Alloc> (); + } + else if (i.length (n) != 0) + { + if (i.extent (n) != n) + octave::err_del_index_out_of_range (false, i.extent (n), n); + + octave_idx_type l, u; + + if (i.is_cont_range (n, l, u)) + { + // Special case deleting a contiguous range. + octave_idx_type nd = n + l - u; + octave_idx_type dl = 1; + octave_idx_type du = 1; + dim_vector rdv = m_dimensions; + rdv(dim) = nd; + for (int k = 0; k < dim; k++) dl *= m_dimensions(k); + for (int k = dim + 1; k < ndims (); k++) du *= m_dimensions(k); + + // Special case deleting a contiguous range. + Array<T, Alloc> tmp = Array<T, Alloc> (rdv); + const T *src = data (); + T *dest = tmp.fortran_vec (); + l *= dl; u *= dl; n *= dl; + for (octave_idx_type k = 0; k < du; k++) + { + std::copy_n (src, l, dest); + dest += l; + std::copy (src + u, src + n, dest); + dest += n - u; + src += n; + } + + *this = tmp; + } + else + { + // Use index. + Array<octave::idx_vector> ia (dim_vector (ndims (), 1), octave::idx_vector::colon); + ia (dim) = i.complement (n); + *this = index (ia); + } + } +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::delete_elements (const Array<octave::idx_vector>& ia) +{ + int ial = ia.numel (); + + if (ial == 1) + delete_elements (ia(0)); + else + { + int k, dim = -1; + for (k = 0; k < ial; k++) + { + if (! ia(k).is_colon ()) + { + if (dim < 0) + dim = k; + else + break; + } + } + if (dim < 0) + { + dim_vector dv = m_dimensions; + dv(0) = 0; + *this = Array<T, Alloc> (dv); + } + else if (k == ial) + { + delete_elements (dim, ia(dim)); + } + else + { + // Allow the null assignment to succeed if it won't change + // anything because the indices reference an empty slice, + // provided that there is at most one non-colon (or + // equivalent) index. So, we still have the requirement of + // deleting a slice, but it is OK if the slice is empty. + + // For compatibility with Matlab, stop checking once we see + // more than one non-colon index or an empty index. Matlab + // considers "[]" to be an empty index but not "false". We + // accept both. + + bool empty_assignment = false; + + int num_non_colon_indices = 0; + + int nd = ndims (); + + for (int i = 0; i < ial; i++) + { + octave_idx_type dim_len = (i >= nd ? 1 : m_dimensions(i)); + + if (ia(i).length (dim_len) == 0) + { + empty_assignment = true; + break; + } + + if (! ia(i).is_colon_equiv (dim_len)) + { + num_non_colon_indices++; + + if (num_non_colon_indices == 2) + break; + } + } + + if (! empty_assignment) + (*current_liboctave_error_handler) + ("a null assignment can only have one non-colon index"); + } + } + +} + +template <typename T, typename Alloc> +Array<T, Alloc>& +Array<T, Alloc>::insert (const Array<T, Alloc>& a, octave_idx_type r, octave_idx_type c) +{ + octave::idx_vector i (r, r + a.rows ()); + octave::idx_vector j (c, c + a.columns ()); + if (ndims () == 2 && a.ndims () == 2) + assign (i, j, a); + else + { + Array<octave::idx_vector> idx (dim_vector (a.ndims (), 1)); + idx(0) = i; + idx(1) = j; + for (int k = 2; k < a.ndims (); k++) + idx(k) = octave::idx_vector (0, a.m_dimensions(k)); + assign (idx, a); + } + + return *this; +} + +template <typename T, typename Alloc> +Array<T, Alloc>& +Array<T, Alloc>::insert (const Array<T, Alloc>& a, const Array<octave_idx_type>& ra_idx) +{ + octave_idx_type n = ra_idx.numel (); + Array<octave::idx_vector> idx (dim_vector (n, 1)); + const dim_vector dva = a.dims ().redim (n); + for (octave_idx_type k = 0; k < n; k++) + idx(k) = octave::idx_vector (ra_idx(k), ra_idx(k) + dva(k)); + + assign (idx, a); + + return *this; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::transpose (void) const +{ + assert (ndims () == 2); + + octave_idx_type nr = dim1 (); + octave_idx_type nc = dim2 (); + + if (nr >= 8 && nc >= 8) + { + Array<T, Alloc> result (dim_vector (nc, nr)); + + // Reuse the implementation used for permuting. + + rec_permute_helper::blk_trans (data (), result.fortran_vec (), nr, nc); + + return result; + } + else if (nr > 1 && nc > 1) + { + Array<T, Alloc> result (dim_vector (nc, nr)); + + for (octave_idx_type j = 0; j < nc; j++) + for (octave_idx_type i = 0; i < nr; i++) + result.xelem (j, i) = xelem (i, j); + + return result; + } + else + { + // Fast transpose for vectors and empty matrices. + return Array<T, Alloc> (*this, dim_vector (nc, nr)); + } +} + +template <typename T> +static T +no_op_fcn (const T& x) +{ + return x; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::hermitian (T (*fcn) (const T&)) const +{ + assert (ndims () == 2); + + if (! fcn) + fcn = no_op_fcn<T>; + + octave_idx_type nr = dim1 (); + octave_idx_type nc = dim2 (); + + if (nr >= 8 && nc >= 8) + { + Array<T, Alloc> result (dim_vector (nc, nr)); + + // Blocked transpose to attempt to avoid cache misses. + + T buf[64]; + + octave_idx_type jj; + for (jj = 0; jj < (nc - 8 + 1); jj += 8) + { + octave_idx_type ii; + for (ii = 0; ii < (nr - 8 + 1); ii += 8) + { + // Copy to buffer + for (octave_idx_type j = jj, k = 0, idxj = jj * nr; + j < jj + 8; j++, idxj += nr) + for (octave_idx_type i = ii; i < ii + 8; i++) + buf[k++] = xelem (i + idxj); + + // Copy from buffer + for (octave_idx_type i = ii, idxi = ii * nc; i < ii + 8; + i++, idxi += nc) + for (octave_idx_type j = jj, k = i - ii; j < jj + 8; + j++, k+=8) + result.xelem (j + idxi) = fcn (buf[k]); + } + + if (ii < nr) + for (octave_idx_type j = jj; j < jj + 8; j++) + for (octave_idx_type i = ii; i < nr; i++) + result.xelem (j, i) = fcn (xelem (i, j)); + } + + for (octave_idx_type j = jj; j < nc; j++) + for (octave_idx_type i = 0; i < nr; i++) + result.xelem (j, i) = fcn (xelem (i, j)); + + return result; + } + else + { + Array<T, Alloc> result (dim_vector (nc, nr)); + + for (octave_idx_type j = 0; j < nc; j++) + for (octave_idx_type i = 0; i < nr; i++) + result.xelem (j, i) = fcn (xelem (i, j)); + + return result; + } +} + +/* + +## Transpose tests for matrices of the tile size and plus or minus a row +## and with four tiles. + +%!shared m7, mt7, m8, mt8, m9, mt9 +%! m7 = reshape (1 : 7*8, 8, 7); +%! mt7 = [1:8; 9:16; 17:24; 25:32; 33:40; 41:48; 49:56]; +%! m8 = reshape (1 : 8*8, 8, 8); +%! mt8 = mt8 = [mt7; 57:64]; +%! m9 = reshape (1 : 9*8, 8, 9); +%! mt9 = [mt8; 65:72]; + +%!assert (m7', mt7) +%!assert ((1i*m7).', 1i * mt7) +%!assert ((1i*m7)', conj (1i * mt7)) +%!assert (m8', mt8) +%!assert ((1i*m8).', 1i * mt8) +%!assert ((1i*m8)', conj (1i * mt8)) +%!assert (m9', mt9) +%!assert ((1i*m9).', 1i * mt9) +%!assert ((1i*m9)', conj (1i * mt9)) +%!assert ([m7, m8; m7, m8]', [mt7, mt7; mt8, mt8]) +%!assert ((1i*[m7, m8; m7, m8]).', 1i * [mt7, mt7; mt8, mt8]) +%!assert ((1i*[m7, m8; m7, m8])', conj (1i * [mt7, mt7; mt8, mt8])) +%!assert ([m8, m8; m8, m8]', [mt8, mt8; mt8, mt8]) +%!assert ((1i*[m8, m8; m8, m8]).', 1i * [mt8, mt8; mt8, mt8]) +%!assert ((1i*[m8, m8; m8, m8])', conj (1i * [mt8, mt8; mt8, mt8])) +%!assert ([m9, m8; m9, m8]', [mt9, mt9; mt8, mt8]) +%!assert ((1i*[m9, m8; m9, m8]).', 1i * [mt9, mt9; mt8, mt8]) +%!assert ((1i*[m9, m8; m9, m8])', conj (1i * [mt9, mt9; mt8, mt8])) + +*/ + +template <typename T, typename Alloc> +T * +Array<T, Alloc>::fortran_vec (void) +{ + make_unique (); + + return m_slice_data; +} + +// Non-real types don't have NaNs. +template <typename T> +inline bool +sort_isnan (typename ref_param<T>::type) +{ + return false; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::sort (int dim, sortmode mode) const +{ + if (dim < 0) + (*current_liboctave_error_handler) ("sort: invalid dimension"); + + Array<T, Alloc> m (dims ()); + + dim_vector dv = m.dims (); + + if (m.numel () < 1) + return m; + + if (dim >= dv.ndims ()) + dv.resize (dim+1, 1); + + octave_idx_type ns = dv(dim); + octave_idx_type iter = dv.numel () / ns; + octave_idx_type stride = 1; + + for (int i = 0; i < dim; i++) + stride *= dv(i); + + T *v = m.fortran_vec (); + const T *ov = data (); + + octave_sort<T> lsort; + + if (mode != UNSORTED) + lsort.set_compare (mode); + else + return m; + + if (stride == 1) + { + // Special case along first dimension avoids gather/scatter AND directly + // sorts into destination buffer for an 11% performance boost. + for (octave_idx_type j = 0; j < iter; j++) + { + // Copy and partition out NaNs. + // No need to special case integer types <T> from floating point + // types <T> to avoid sort_isnan() test as it makes no discernible + // performance impact. + octave_idx_type kl = 0; + octave_idx_type ku = ns; + for (octave_idx_type i = 0; i < ns; i++) + { + T tmp = ov[i]; + if (sort_isnan<T> (tmp)) + v[--ku] = tmp; + else + v[kl++] = tmp; + } + + // sort. + lsort.sort (v, kl); + + if (ku < ns) + { + // NaNs are in reverse order + std::reverse (v + ku, v + ns); + if (mode == DESCENDING) + std::rotate (v, v + ku, v + ns); + } + + v += ns; + ov += ns; + } + } + else + { + OCTAVE_LOCAL_BUFFER (T, buf, ns); + + for (octave_idx_type j = 0; j < iter; j++) + { + octave_idx_type offset = j; + octave_idx_type n_strides = j / stride; + offset += n_strides * stride * (ns - 1); + + // gather and partition out NaNs. + octave_idx_type kl = 0; + octave_idx_type ku = ns; + for (octave_idx_type i = 0; i < ns; i++) + { + T tmp = ov[i*stride + offset]; + if (sort_isnan<T> (tmp)) + buf[--ku] = tmp; + else + buf[kl++] = tmp; + } + + // sort. + lsort.sort (buf, kl); + + if (ku < ns) + { + // NaNs are in reverse order + std::reverse (buf + ku, buf + ns); + if (mode == DESCENDING) + std::rotate (buf, buf + ku, buf + ns); + } + + // scatter. + for (octave_idx_type i = 0; i < ns; i++) + v[i*stride + offset] = buf[i]; + } + } + + return m; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::sort (Array<octave_idx_type>& sidx, int dim, + sortmode mode) const +{ + if (dim < 0 || dim >= ndims ()) + (*current_liboctave_error_handler) ("sort: invalid dimension"); + + Array<T, Alloc> m (dims ()); + + dim_vector dv = m.dims (); + + if (m.numel () < 1) + { + sidx = Array<octave_idx_type> (dv); + return m; + } + + octave_idx_type ns = dv(dim); + octave_idx_type iter = dv.numel () / ns; + octave_idx_type stride = 1; + + for (int i = 0; i < dim; i++) + stride *= dv(i); + + T *v = m.fortran_vec (); + const T *ov = data (); + + octave_sort<T> lsort; + + sidx = Array<octave_idx_type> (dv); + octave_idx_type *vi = sidx.fortran_vec (); + + if (mode != UNSORTED) + lsort.set_compare (mode); + else + return m; + + if (stride == 1) + { + // Special case for dim 1 avoids gather/scatter for performance boost. + // See comments in Array::sort (dim, mode). + for (octave_idx_type j = 0; j < iter; j++) + { + // copy and partition out NaNs. + octave_idx_type kl = 0; + octave_idx_type ku = ns; + for (octave_idx_type i = 0; i < ns; i++) + { + T tmp = ov[i]; + if (sort_isnan<T> (tmp)) + { + --ku; + v[ku] = tmp; + vi[ku] = i; + } + else + { + v[kl] = tmp; + vi[kl] = i; + kl++; + } + } + + // sort. + lsort.sort (v, vi, kl); + + if (ku < ns) + { + // NaNs are in reverse order + std::reverse (v + ku, v + ns); + std::reverse (vi + ku, vi + ns); + if (mode == DESCENDING) + { + std::rotate (v, v + ku, v + ns); + std::rotate (vi, vi + ku, vi + ns); + } + } + + v += ns; + vi += ns; + ov += ns; + } + } + else + { + OCTAVE_LOCAL_BUFFER (T, buf, ns); + OCTAVE_LOCAL_BUFFER (octave_idx_type, bufi, ns); + + for (octave_idx_type j = 0; j < iter; j++) + { + octave_idx_type offset = j; + octave_idx_type n_strides = j / stride; + offset += n_strides * stride * (ns - 1); + + // gather and partition out NaNs. + octave_idx_type kl = 0; + octave_idx_type ku = ns; + for (octave_idx_type i = 0; i < ns; i++) + { + T tmp = ov[i*stride + offset]; + if (sort_isnan<T> (tmp)) + { + --ku; + buf[ku] = tmp; + bufi[ku] = i; + } + else + { + buf[kl] = tmp; + bufi[kl] = i; + kl++; + } + } + + // sort. + lsort.sort (buf, bufi, kl); + + if (ku < ns) + { + // NaNs are in reverse order + std::reverse (buf + ku, buf + ns); + std::reverse (bufi + ku, bufi + ns); + if (mode == DESCENDING) + { + std::rotate (buf, buf + ku, buf + ns); + std::rotate (bufi, bufi + ku, bufi + ns); + } + } + + // scatter. + for (octave_idx_type i = 0; i < ns; i++) + v[i*stride + offset] = buf[i]; + for (octave_idx_type i = 0; i < ns; i++) + vi[i*stride + offset] = bufi[i]; + } + } + + return m; +} + +template <typename T, typename Alloc> +typename Array<T, Alloc>::compare_fcn_type +safe_comparator (sortmode mode, const Array<T, Alloc>& /* a */, + bool /* allow_chk */) +{ + if (mode == ASCENDING) + return octave_sort<T>::ascending_compare; + else if (mode == DESCENDING) + return octave_sort<T>::descending_compare; + else + return nullptr; +} + +template <typename T, typename Alloc> +sortmode +Array<T, Alloc>::issorted (sortmode mode) const +{ + octave_sort<T> lsort; + + octave_idx_type n = numel (); + + if (n <= 1) + return (mode == UNSORTED) ? ASCENDING : mode; + + if (mode == UNSORTED) + { + // Auto-detect mode. + compare_fcn_type compare + = safe_comparator (ASCENDING, *this, false); + + if (compare (elem (n-1), elem (0))) + mode = DESCENDING; + else + mode = ASCENDING; + } + + lsort.set_compare (safe_comparator (mode, *this, false)); + + if (! lsort.issorted (data (), n)) + mode = UNSORTED; + + return mode; + +} + +template <typename T, typename Alloc> +Array<octave_idx_type> +Array<T, Alloc>::sort_rows_idx (sortmode mode) const +{ + Array<octave_idx_type> idx; + + octave_sort<T> lsort (safe_comparator (mode, *this, true)); + + octave_idx_type r = rows (); + octave_idx_type c = cols (); + + idx = Array<octave_idx_type> (dim_vector (r, 1)); + + lsort.sort_rows (data (), idx.fortran_vec (), r, c); + + return idx; +} + +template <typename T, typename Alloc> +sortmode +Array<T, Alloc>::is_sorted_rows (sortmode mode) const +{ + octave_sort<T> lsort; + + octave_idx_type r = rows (); + octave_idx_type c = cols (); + + if (r <= 1 || c == 0) + return (mode == UNSORTED) ? ASCENDING : mode; + + if (mode == UNSORTED) + { + // Auto-detect mode. + compare_fcn_type compare + = safe_comparator (ASCENDING, *this, false); + + octave_idx_type i; + for (i = 0; i < cols (); i++) + { + T l = elem (0, i); + T u = elem (rows () - 1, i); + if (compare (l, u)) + { + if (mode == DESCENDING) + { + mode = UNSORTED; + break; + } + else + mode = ASCENDING; + } + else if (compare (u, l)) + { + if (mode == ASCENDING) + { + mode = UNSORTED; + break; + } + else + mode = DESCENDING; + } + } + if (mode == UNSORTED && i == cols ()) + mode = ASCENDING; + } + + if (mode != UNSORTED) + { + lsort.set_compare (safe_comparator (mode, *this, false)); + + if (! lsort.is_sorted_rows (data (), r, c)) + mode = UNSORTED; + } + + return mode; + +} + +// Do a binary lookup in a sorted array. +template <typename T, typename Alloc> +octave_idx_type +Array<T, Alloc>::lookup (const T& value, sortmode mode) const +{ + octave_idx_type n = numel (); + octave_sort<T> lsort; + + if (mode == UNSORTED) + { + // auto-detect mode + if (n > 1 && lsort.descending_compare (elem (0), elem (n-1))) + mode = DESCENDING; + else + mode = ASCENDING; + } + + lsort.set_compare (mode); + + return lsort.lookup (data (), n, value); +} + +template <typename T, typename Alloc> +Array<octave_idx_type> +Array<T, Alloc>::lookup (const Array<T, Alloc>& values, sortmode mode) const +{ + octave_idx_type n = numel (); + octave_idx_type nval = values.numel (); + octave_sort<T> lsort; + Array<octave_idx_type> idx (values.dims ()); + + if (mode == UNSORTED) + { + // auto-detect mode + if (n > 1 && lsort.descending_compare (elem (0), elem (n-1))) + mode = DESCENDING; + else + mode = ASCENDING; + } + + lsort.set_compare (mode); + + // This determines the split ratio between the O(M*log2(N)) and O(M+N) + // algorithms. + static const double ratio = 1.0; + sortmode vmode = UNSORTED; + + // Attempt the O(M+N) algorithm if M is large enough. + if (nval > ratio * n / octave::math::log2 (n + 1.0)) + { + vmode = values.issorted (); + // The table must not contain a NaN. + if ((vmode == ASCENDING && sort_isnan<T> (values(nval-1))) + || (vmode == DESCENDING && sort_isnan<T> (values(0)))) + vmode = UNSORTED; + } + + if (vmode != UNSORTED) + lsort.lookup_sorted (data (), n, values.data (), nval, + idx.fortran_vec (), vmode != mode); + else + lsort.lookup (data (), n, values.data (), nval, idx.fortran_vec ()); + + return idx; +} + +template <typename T, typename Alloc> +octave_idx_type +Array<T, Alloc>::nnz (void) const +{ + const T *src = data (); + octave_idx_type nel = numel (); + octave_idx_type retval = 0; + const T zero = T (); + for (octave_idx_type i = 0; i < nel; i++) + if (src[i] != zero) + retval++; + + return retval; +} + +template <typename T, typename Alloc> +Array<octave_idx_type> +Array<T, Alloc>::find (octave_idx_type n, bool backward) const +{ + Array<octave_idx_type> retval; + const T *src = data (); + octave_idx_type nel = numel (); + const T zero = T (); + if (n < 0 || n >= nel) + { + // We want all elements, which means we'll almost surely need + // to resize. So count first, then allocate array of exact size. + octave_idx_type cnt = 0; + for (octave_idx_type i = 0; i < nel; i++) + cnt += src[i] != zero; + + retval.clear (cnt, 1); + octave_idx_type *dest = retval.fortran_vec (); + for (octave_idx_type i = 0; i < nel; i++) + if (src[i] != zero) *dest++ = i; + } + else + { + // We want a fixed max number of elements, usually small. So be + // optimistic, alloc the array in advance, and then resize if + // needed. + retval.clear (n, 1); + if (backward) + { + // Do the search as a series of successive single-element searches. + octave_idx_type k = 0; + octave_idx_type l = nel - 1; + for (; k < n; k++) + { + for (; l >= 0 && src[l] == zero; l--) ; + if (l >= 0) + retval(k) = l--; + else + break; + } + if (k < n) + retval.resize2 (k, 1); + octave_idx_type *rdata = retval.fortran_vec (); + std::reverse (rdata, rdata + k); + } + else + { + // Do the search as a series of successive single-element searches. + octave_idx_type k = 0; + octave_idx_type l = 0; + for (; k < n; k++) + { + for (; l != nel && src[l] == zero; l++) ; + if (l != nel) + retval(k) = l++; + else + break; + } + if (k < n) + retval.resize2 (k, 1); + } + } + + // Fixup return dimensions, for Matlab compatibility. + // find (zeros (0,0)) -> zeros (0,0) + // find (zeros (1,0)) -> zeros (1,0) + // find (zeros (0,1)) -> zeros (0,1) + // find (zeros (0,X)) -> zeros (0,1) + // find (zeros (1,1)) -> zeros (0,0) !!!! WHY? + // find (zeros (0,1,0)) -> zeros (0,0) + // find (zeros (0,1,0,1)) -> zeros (0,0) etc + + if ((numel () == 1 && retval.isempty ()) + || (rows () == 0 && dims ().numel (1) == 0)) + retval.m_dimensions = dim_vector (); + else if (rows () == 1 && ndims () == 2) + retval.m_dimensions = dim_vector (1, retval.numel ()); + + return retval; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::nth_element (const octave::idx_vector& n, int dim) const +{ + if (dim < 0) + (*current_liboctave_error_handler) ("nth_element: invalid dimension"); + + dim_vector dv = dims (); + if (dim >= dv.ndims ()) + dv.resize (dim+1, 1); + + octave_idx_type ns = dv(dim); + + octave_idx_type nn = n.length (ns); + + dv(dim) = std::min (nn, ns); + dv.chop_trailing_singletons (); + dim = std::min (dv.ndims (), static_cast<octave_idx_type> (dim)); + + Array<T, Alloc> m (dv); + + if (m.isempty ()) + return m; + + sortmode mode = UNSORTED; + octave_idx_type lo = 0; + + switch (n.idx_class ()) + { + case octave::idx_vector::class_scalar: + mode = ASCENDING; + lo = n(0); + break; + case octave::idx_vector::class_range: + { + octave_idx_type inc = n.increment (); + if (inc == 1) + { + mode = ASCENDING; + lo = n(0); + } + else if (inc == -1) + { + mode = DESCENDING; + lo = ns - 1 - n(0); + } + } + break; + case octave::idx_vector::class_vector: + // This case resolves bug #51329, a fallback to allow the given index + // to be a sequential vector instead of the typical scalar or range + if (n(1) - n(0) == 1) + { + mode = ASCENDING; + lo = n(0); + } + else if (n(1) - n(0) == -1) + { + mode = DESCENDING; + lo = ns - 1 - n(0); + } + // Ensure that the vector is actually an arithmetic contiguous sequence + for (octave_idx_type i = 2; i < n.length () && mode != UNSORTED; i++) + if ((mode == ASCENDING && n(i) - n(i-1) != 1) + || (mode == DESCENDING && n(i) - n(i-1) != -1)) + mode = UNSORTED; + break; + default: + break; + } + + if (mode == UNSORTED) + (*current_liboctave_error_handler) + ("nth_element: n must be a scalar or a contiguous range"); + + octave_idx_type up = lo + nn; + + if (lo < 0 || up > ns) + (*current_liboctave_error_handler) ("nth_element: invalid element index"); + + octave_idx_type iter = numel () / ns; + octave_idx_type stride = 1; + + for (int i = 0; i < dim; i++) + stride *= dv(i); + + T *v = m.fortran_vec (); + const T *ov = data (); + + OCTAVE_LOCAL_BUFFER (T, buf, ns); + + octave_sort<T> lsort; + lsort.set_compare (mode); + + for (octave_idx_type j = 0; j < iter; j++) + { + octave_idx_type kl = 0; + octave_idx_type ku = ns; + + if (stride == 1) + { + // copy without NaNs. + for (octave_idx_type i = 0; i < ns; i++) + { + T tmp = ov[i]; + if (sort_isnan<T> (tmp)) + buf[--ku] = tmp; + else + buf[kl++] = tmp; + } + + ov += ns; + } + else + { + octave_idx_type offset = j % stride; + // copy without NaNs. + for (octave_idx_type i = 0; i < ns; i++) + { + T tmp = ov[offset + i*stride]; + if (sort_isnan<T> (tmp)) + buf[--ku] = tmp; + else + buf[kl++] = tmp; + } + + if (offset == stride-1) + ov += ns*stride; + } + + if (ku == ns) + lsort.nth_element (buf, ns, lo, up); + else if (mode == ASCENDING) + lsort.nth_element (buf, ku, lo, std::min (ku, up)); + else + { + octave_idx_type nnan = ns - ku; + octave_idx_type zero = 0; + lsort.nth_element (buf, ku, std::max (lo - nnan, zero), + std::max (up - nnan, zero)); + std::rotate (buf, buf + ku, buf + ns); + } + + if (stride == 1) + { + for (octave_idx_type i = 0; i < nn; i++) + v[i] = buf[lo + i]; + + v += nn; + } + else + { + octave_idx_type offset = j % stride; + for (octave_idx_type i = 0; i < nn; i++) + v[offset + stride * i] = buf[lo + i]; + if (offset == stride-1) + v += nn*stride; + } + } + + return m; +} + +#define NO_INSTANTIATE_ARRAY_SORT_API(T, API) \ + template <> API Array<T> \ + Array<T>::sort (int, sortmode) const \ + { \ + return *this; \ + } \ + template <> API Array<T> \ + Array<T>::sort (Array<octave_idx_type> &sidx, int, sortmode) const \ + { \ + sidx = Array<octave_idx_type> (); \ + return *this; \ + } \ + template <> API sortmode \ + Array<T>::issorted (sortmode) const \ + { \ + return UNSORTED; \ + } \ + API Array<T>::compare_fcn_type \ + safe_comparator (sortmode, const Array<T>&, bool) \ + { \ + return nullptr; \ + } \ + template <> API Array<octave_idx_type> \ + Array<T>::sort_rows_idx (sortmode) const \ + { \ + return Array<octave_idx_type> (); \ + } \ + template <> API sortmode \ + Array<T>::is_sorted_rows (sortmode) const \ + { \ + return UNSORTED; \ + } \ + template <> API octave_idx_type \ + Array<T>::lookup (T const &, sortmode) const \ + { \ + return 0; \ + } \ + template <> API Array<octave_idx_type> \ + Array<T>::lookup (const Array<T>&, sortmode) const \ + { \ + return Array<octave_idx_type> (); \ + } \ + template <> API octave_idx_type \ + Array<T>::nnz (void) const \ + { \ + return 0; \ + } \ + template <> API Array<octave_idx_type> \ + Array<T>::find (octave_idx_type, bool) const \ + { \ + return Array<octave_idx_type> (); \ + } \ + template <> API Array<T> \ + Array<T>::nth_element (const octave::idx_vector&, int) const { \ + return Array<T> (); \ + } + +#define NO_INSTANTIATE_ARRAY_SORT(T) NO_INSTANTIATE_ARRAY_SORT_API (T,) + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::diag (octave_idx_type k) const +{ + dim_vector dv = dims (); + octave_idx_type nd = dv.ndims (); + Array<T, Alloc> d; + + if (nd > 2) + (*current_liboctave_error_handler) ("Matrix must be 2-dimensional"); + + octave_idx_type nnr = dv(0); + octave_idx_type nnc = dv(1); + + if (nnr == 0 && nnc == 0) + ; // do nothing for empty matrix + else if (nnr != 1 && nnc != 1) + { + // Extract diag from matrix + if (k > 0) + nnc -= k; + else if (k < 0) + nnr += k; + + if (nnr > 0 && nnc > 0) + { + octave_idx_type ndiag = (nnr < nnc) ? nnr : nnc; + + d.resize (dim_vector (ndiag, 1)); + + if (k > 0) + { + for (octave_idx_type i = 0; i < ndiag; i++) + d.xelem (i) = elem (i, i+k); + } + else if (k < 0) + { + for (octave_idx_type i = 0; i < ndiag; i++) + d.xelem (i) = elem (i-k, i); + } + else + { + for (octave_idx_type i = 0; i < ndiag; i++) + d.xelem (i) = elem (i, i); + } + } + else // Matlab returns [] 0x1 for out-of-range diagonal + d.resize (dim_vector (0, 1)); + } + else + { + // Create diag matrix from vector + octave_idx_type roff = 0; + octave_idx_type coff = 0; + if (k > 0) + { + roff = 0; + coff = k; + } + else if (k < 0) + { + roff = -k; + coff = 0; + } + + if (nnr == 1) + { + octave_idx_type n = nnc + std::abs (k); + d = Array<T, Alloc> (dim_vector (n, n), resize_fill_value ()); + + for (octave_idx_type i = 0; i < nnc; i++) + d.xelem (i+roff, i+coff) = elem (0, i); + } + else + { + octave_idx_type n = nnr + std::abs (k); + d = Array<T, Alloc> (dim_vector (n, n), resize_fill_value ()); + + for (octave_idx_type i = 0; i < nnr; i++) + d.xelem (i+roff, i+coff) = elem (i, 0); + } + } + + return d; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::diag (octave_idx_type m, octave_idx_type n) const +{ + if (ndims () != 2 || (rows () != 1 && cols () != 1)) + (*current_liboctave_error_handler) ("cat: invalid dimension"); + + Array<T, Alloc> retval (dim_vector (m, n), resize_fill_value ()); + + octave_idx_type nel = std::min (numel (), std::min (m, n)); + for (octave_idx_type i = 0; i < nel; i++) + retval.xelem (i, i) = xelem (i); + + return retval; +} + +template <typename T, typename Alloc> +Array<T, Alloc> +Array<T, Alloc>::cat (int dim, octave_idx_type n, const Array<T, Alloc> *array_list) +{ + // Default concatenation. + bool (dim_vector::*concat_rule) (const dim_vector&, int) = &dim_vector::concat; + + if (dim == -1 || dim == -2) + { + concat_rule = &dim_vector::hvcat; + dim = -dim - 1; + } + else if (dim < 0) + (*current_liboctave_error_handler) ("cat: invalid dimension"); + + if (n == 1) + return array_list[0]; + else if (n == 0) + return Array<T, Alloc> (); + + // Special case: + // + // cat (dim, [], ..., [], A, ...) + // + // with dim > 2, A not 0x0, and at least three arguments to + // concatenate is equivalent to + // + // cat (dim, A, ...) + // + // Note that this check must be performed here because for full-on + // braindead Matlab compatibility, we need to have things like + // + // cat (3, [], [], A) + // + // succeed, but to have things like + // + // cat (3, cat (3, [], []), A) + // cat (3, zeros (0, 0, 2), A) + // + // fail. See also bug report #31615. + + octave_idx_type istart = 0; + + if (n > 2 && dim > 1) + { + for (octave_idx_type i = 0; i < n; i++) + { + dim_vector dv = array_list[i].dims (); + + if (dv.zero_by_zero ()) + istart++; + else + break; + } + + // Don't skip any initial arguments if they are all empty. + if (istart >= n) + istart = 0; + } + + dim_vector dv = array_list[istart++].dims (); + + for (octave_idx_type i = istart; i < n; i++) + if (! (dv.*concat_rule) (array_list[i].dims (), dim)) + (*current_liboctave_error_handler) ("cat: dimension mismatch"); + + Array<T, Alloc> retval (dv); + + if (retval.isempty ()) + return retval; + + int nidx = std::max (dv.ndims (), static_cast<octave_idx_type> (dim + 1)); + Array<octave::idx_vector> idxa (dim_vector (nidx, 1), octave::idx_vector::colon); + octave_idx_type l = 0; + + for (octave_idx_type i = 0; i < n; i++) + { + // NOTE: This takes some thinking, but no matter what the above rules + // are, an empty array can always be skipped at this point, because + // the result dimensions are already determined, and there is no way + // an empty array may contribute a nonzero piece along the dimension + // at this point, unless an empty array can be promoted to a non-empty + // one (which makes no sense). I repeat, *no way*, think about it. + if (array_list[i].isempty ()) + continue; + + octave_quit (); + + octave_idx_type u; + if (dim < array_list[i].ndims ()) + u = l + array_list[i].dims ()(dim); + else + u = l + 1; + + idxa(dim) = octave::idx_vector (l, u); + + retval.assign (idxa, array_list[i]); + + l = u; + } + + return retval; +} + +template <typename T, typename Alloc> +void +Array<T, Alloc>::print_info (std::ostream& os, const std::string& prefix) const +{ + os << prefix << "m_rep address: " << m_rep << '\n' + << prefix << "m_rep->m_len: " << m_rep->m_len << '\n' + << prefix << "m_rep->m_data: " << static_cast<void *> (m_rep->m_data) << '\n' + << prefix << "m_rep->m_count: " << m_rep->m_count << '\n' + << prefix << "m_slice_data: " << static_cast<void *> (m_slice_data) << '\n' + << prefix << "m_slice_len: " << m_slice_len << '\n'; + + // 2D info: + // + // << pefix << "rows: " << rows () << "\n" + // << prefix << "cols: " << cols () << "\n"; +} + +template <typename T, typename Alloc> +bool Array<T, Alloc>::optimize_dimensions (const dim_vector& dv) +{ + bool retval = m_dimensions == dv; + if (retval) + m_dimensions = dv; + + return retval; +} + +template <typename T, typename Alloc> +void Array<T, Alloc>::instantiation_guard () +{ + // This guards against accidental implicit instantiations. + // Array<T, Alloc> instances should always be explicit and use INSTANTIATE_ARRAY. + T::__xXxXx__ (); +} + +#if defined (__clang__) +# define INSTANTIATE_ARRAY(T, API) \ + template <> API void \ + Array<T>::instantiation_guard () { } \ + \ + template class API Array<T> +#else +# define INSTANTIATE_ARRAY(T, API) \ + template <> API void \ + Array<T>::instantiation_guard () { } \ + \ + template class Array<T> +#endif + +// FIXME: is this used? + +template <typename T, typename Alloc> +std::ostream& +operator << (std::ostream& os, const Array<T, Alloc>& a) +{ + dim_vector a_dims = a.dims (); + + int n_dims = a_dims.ndims (); + + os << n_dims << "-dimensional array"; + + if (n_dims) + os << " (" << a_dims.str () << ')'; + + os <<"\n\n"; + + if (n_dims) + { + os << "data:"; + + Array<octave_idx_type> ra_idx (dim_vector (n_dims, 1), 0); + + // Number of times the first 2d-array is to be displayed. + + octave_idx_type m = 1; + for (int i = 2; i < n_dims; i++) + m *= a_dims(i); + + if (m == 1) + { + octave_idx_type rows = 0; + octave_idx_type cols = 0; + + switch (n_dims) + { + case 2: + rows = a_dims(0); + cols = a_dims(1); + + for (octave_idx_type j = 0; j < rows; j++) + { + ra_idx(0) = j; + for (octave_idx_type k = 0; k < cols; k++) + { + ra_idx(1) = k; + os << ' ' << a.elem (ra_idx); + } + os << "\n"; + } + break; + + default: + rows = a_dims(0); + + for (octave_idx_type k = 0; k < rows; k++) + { + ra_idx(0) = k; + os << ' ' << a.elem (ra_idx); + } + break; + } + + os << "\n"; + } + else + { + octave_idx_type rows = a_dims(0); + octave_idx_type cols = a_dims(1); + + for (int i = 0; i < m; i++) + { + os << "\n(:,:,"; + + for (int j = 2; j < n_dims - 1; j++) + os << ra_idx(j) + 1 << ','; + + os << ra_idx(n_dims - 1) + 1 << ") = \n"; + + for (octave_idx_type j = 0; j < rows; j++) + { + ra_idx(0) = j; + + for (octave_idx_type k = 0; k < cols; k++) + { + ra_idx(1) = k; + os << ' ' << a.elem (ra_idx); + } + + os << "\n"; + } + + os << "\n"; + + if (i != m - 1) + increment_index (ra_idx, a_dims, 2); + } + } + } + + return os; +}
--- a/liboctave/array/Array-ch.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-ch.cc Sat Nov 05 13:48:24 2022 +0100 @@ -30,7 +30,7 @@ // Instantiate Arrays of char values. #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #define INLINE_ASCENDING_SORT 1 #define INLINE_DESCENDING_SORT 1
--- a/liboctave/array/Array-d.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-d.cc Sat Nov 05 13:48:24 2022 +0100 @@ -31,7 +31,7 @@ #include "lo-mappers.h" #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #include "oct-locbuf.h" #define INLINE_ASCENDING_SORT 1
--- a/liboctave/array/Array-f.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-f.cc Sat Nov 05 13:48:24 2022 +0100 @@ -31,7 +31,7 @@ #include "lo-mappers.h" #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #include "oct-locbuf.h" #define INLINE_ASCENDING_SORT 1
--- a/liboctave/array/Array-fC.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-fC.cc Sat Nov 05 13:48:24 2022 +0100 @@ -33,7 +33,7 @@ #include "lo-mappers.h" #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #include "oct-sort.cc" // Prevent implicit instantiations on some systems (Windows, others?)
--- a/liboctave/array/Array-i.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-i.cc Sat Nov 05 13:48:24 2022 +0100 @@ -32,7 +32,7 @@ // Instantiate Arrays of integer values. #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #define INLINE_ASCENDING_SORT 1 #define INLINE_DESCENDING_SORT 1
--- a/liboctave/array/Array-idx-vec.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-idx-vec.cc Sat Nov 05 13:48:24 2022 +0100 @@ -32,7 +32,7 @@ #include "idx-vector.h" #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" // Prevent implicit instantiations on some systems (Windows, others?) // that can lead to duplicate definitions of static data members.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/liboctave/array/Array-oct.cc Sat Nov 05 13:48:24 2022 +0100 @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2022 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// 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 +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + + +// Include this file when instantiating the Array class with new types in +// projects linking to "liboctave" but not to "liboctinterp". + +#include "Array-base.cc" + +// "Protect" Array<T> instantiations that are exported by liboctave from being +// implicitly instantiated in compilation units including this file. + +// instantiated in Array-C.cc +extern template class Array<Complex>; +// instantiated in Array-b.cc +extern template class Array<bool>; +// instantiated in Array-ch.cc +extern template class Array<char>; +// instantiated in Array-d.cc +extern template class Array<double>; +// instantiated in Array-f.cc +extern template class Array<float>; +// instantiated in Array-fC.cc +extern template class Array<FloatComplex>; +// instantiated in Array-i.cc +extern template class Array<signed char>; +// extern template class Array<short>; +extern template class Array<int>; +extern template class Array<long>; +#if defined (OCTAVE_HAVE_LONG_LONG_INT) +extern template class Array<long long>; +#endif +extern template class Array<unsigned char>; +extern template class Array<unsigned short>; +extern template class Array<unsigned int>; +extern template class Array<unsigned long>; +#if defined (OCTAVE_HAVE_UNSIGNED_LONG_LONG_INT) +extern template class Array<unsigned long long>; +#endif +extern template class Array<octave_int8>; +extern template class Array<octave_int16>; +extern template class Array<octave_int32>; +extern template class Array<octave_int64>; +extern template class Array<octave_uint8>; +extern template class Array<octave_uint16>; +extern template class Array<octave_uint32>; +extern template class Array<octave_uint64>; +// instantiated in Array-idx-vec.cc +extern template class Array<octave::idx_vector>; +// instantiated in Array-s.cc +extern template class Array<short>; +// instantiated in Array-str.cc +extern template class Array<std::string>;
--- a/liboctave/array/Array-s.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-s.cc Sat Nov 05 13:48:24 2022 +0100 @@ -30,7 +30,7 @@ // Instantiate Arrays of short int values. #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #define INLINE_ASCENDING_SORT 1 #define INLINE_DESCENDING_SORT 1
--- a/liboctave/array/Array-str.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-str.cc Sat Nov 05 13:48:24 2022 +0100 @@ -32,7 +32,7 @@ // Instantiate Arrays of strings. #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" #include "oct-sort.cc"
--- a/liboctave/array/Array-voidp.cc Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/Array-voidp.cc Sat Nov 05 13:48:24 2022 +0100 @@ -32,7 +32,7 @@ // Instantiate Arrays of void *. #include "Array.h" -#include "Array.cc" +#include "Array-base.cc" // Prevent implicit instantiations on some systems (Windows, others?) // that can lead to duplicate definitions of static data members.
--- a/liboctave/array/Array.cc Sat Nov 05 00:10:11 2022 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2906 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 1993-2022 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// 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 -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -// This file should not include config.h. It is only included in other -// C++ source files that should have included config.h before including -// this file. - -#include <ostream> - -#include "Array-util.h" -#include "Array.h" -#include "lo-mappers.h" -#include "oct-locbuf.h" - -// One dimensional array class. Handles the reference counting for -// all the derived classes. - -template <typename T, typename Alloc> -typename Array<T, Alloc>::ArrayRep * -Array<T, Alloc>::nil_rep (void) -{ - static ArrayRep nr; - return &nr; -} - -// This is similar to the template for containers but specialized for Array. -// Note we can't specialize a member without also specializing the class. -template <typename T, typename Alloc> -Array<T, Alloc>::Array (const Array<T, Alloc>& a, const dim_vector& dv) - : m_dimensions (dv), m_rep (a.m_rep), - m_slice_data (a.m_slice_data), m_slice_len (a.m_slice_len) -{ - bool invalid_size = false; - - octave_idx_type new_numel = 0; - - try - { - // Safe numel may throw a bad_alloc error because the new - // dimensions are too large to allocate. Trap that here so - // we can issue a more helpful diagnostic that is consistent - // with other size mismatch failures. - - new_numel = m_dimensions.safe_numel (); - } - catch (const std::bad_alloc&) - { - invalid_size = true; - } - - if (invalid_size || new_numel != a.numel ()) - { - std::string dimensions_str = a.m_dimensions.str (); - std::string new_dims_str = m_dimensions.str (); - - (*current_liboctave_error_handler) - ("reshape: can't reshape %s array to %s array", - dimensions_str.c_str (), new_dims_str.c_str ()); - } - - // This goes here because if an exception is thrown by the above, - // destructor will be never called. - m_rep->m_count++; - m_dimensions.chop_trailing_singletons (); -} - -// We need to dllexport this constructor from explicit template -// instantiations with specializations. One way to do this is to mark the -// declaration with the corresponding attributes and define the -// constructor separately here. -template <typename T, typename Alloc> -Array<T, Alloc>::Array (T *ptr, const dim_vector& dv, const Alloc& xallocator) - : m_dimensions (dv), - m_rep (new typename Array<T, Alloc>::ArrayRep (ptr, dv, xallocator)), - m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) -{ - m_dimensions.chop_trailing_singletons (); -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::fill (const T& val) -{ - if (m_rep->m_count > 1) - { - --m_rep->m_count; - m_rep = new ArrayRep (numel (), val); - m_slice_data = m_rep->m_data; - } - else - std::fill_n (m_slice_data, m_slice_len, val); -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::clear (void) -{ - if (--m_rep->m_count == 0) - delete m_rep; - - m_rep = nil_rep (); - m_rep->m_count++; - m_slice_data = m_rep->m_data; - m_slice_len = m_rep->m_len; - - m_dimensions = dim_vector (); -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::clear (const dim_vector& dv) -{ - if (--m_rep->m_count == 0) - delete m_rep; - - m_rep = new ArrayRep (dv.safe_numel ()); - m_slice_data = m_rep->m_data; - m_slice_len = m_rep->m_len; - - m_dimensions = dv; - m_dimensions.chop_trailing_singletons (); -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::squeeze (void) const -{ - Array<T, Alloc> retval = *this; - - if (ndims () > 2) - { - bool dims_changed = false; - - dim_vector new_dimensions = m_dimensions; - - int k = 0; - - for (int i = 0; i < ndims (); i++) - { - if (m_dimensions(i) == 1) - dims_changed = true; - else - new_dimensions(k++) = m_dimensions(i); - } - - if (dims_changed) - { - switch (k) - { - case 0: - new_dimensions = dim_vector (1, 1); - break; - - case 1: - { - octave_idx_type tmp = new_dimensions(0); - - new_dimensions.resize (2); - - new_dimensions(0) = tmp; - new_dimensions(1) = 1; - } - break; - - default: - new_dimensions.resize (k); - break; - } - } - - retval = Array<T, Alloc> (*this, new_dimensions); - } - - return retval; -} - -template <typename T, typename Alloc> -octave_idx_type -Array<T, Alloc>::compute_index (octave_idx_type i, octave_idx_type j) const -{ - return ::compute_index (i, j, m_dimensions); -} - -template <typename T, typename Alloc> -octave_idx_type -Array<T, Alloc>::compute_index (octave_idx_type i, octave_idx_type j, - octave_idx_type k) const -{ - return ::compute_index (i, j, k, m_dimensions); -} - -template <typename T, typename Alloc> -octave_idx_type -Array<T, Alloc>::compute_index (const Array<octave_idx_type>& ra_idx) const -{ - return ::compute_index (ra_idx, m_dimensions); -} - -template <typename T, typename Alloc> -T& -Array<T, Alloc>::checkelem (octave_idx_type n) -{ - // Do checks directly to avoid recomputing m_slice_len. - if (n < 0) - octave::err_invalid_index (n); - if (n >= m_slice_len) - octave::err_index_out_of_range (1, 1, n+1, m_slice_len, m_dimensions); - - return elem (n); -} - -template <typename T, typename Alloc> -T& -Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j) -{ - return elem (compute_index (i, j)); -} - -template <typename T, typename Alloc> -T& -Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j, octave_idx_type k) -{ - return elem (compute_index (i, j, k)); -} - -template <typename T, typename Alloc> -T& -Array<T, Alloc>::checkelem (const Array<octave_idx_type>& ra_idx) -{ - return elem (compute_index (ra_idx)); -} - -template <typename T, typename Alloc> -typename Array<T, Alloc>::crefT -Array<T, Alloc>::checkelem (octave_idx_type n) const -{ - // Do checks directly to avoid recomputing m_slice_len. - if (n < 0) - octave::err_invalid_index (n); - if (n >= m_slice_len) - octave::err_index_out_of_range (1, 1, n+1, m_slice_len, m_dimensions); - - return elem (n); -} - -template <typename T, typename Alloc> -typename Array<T, Alloc>::crefT -Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j) const -{ - return elem (compute_index (i, j)); -} - -template <typename T, typename Alloc> -typename Array<T, Alloc>::crefT -Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j, - octave_idx_type k) const -{ - return elem (compute_index (i, j, k)); -} - -template <typename T, typename Alloc> -typename Array<T, Alloc>::crefT -Array<T, Alloc>::checkelem (const Array<octave_idx_type>& ra_idx) const -{ - return elem (compute_index (ra_idx)); -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::column (octave_idx_type k) const -{ - octave_idx_type r = m_dimensions(0); - - return Array<T, Alloc> (*this, dim_vector (r, 1), k*r, k*r + r); -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::page (octave_idx_type k) const -{ - octave_idx_type r = m_dimensions(0); - octave_idx_type c = m_dimensions(1); - octave_idx_type p = r*c; - - return Array<T, Alloc> (*this, dim_vector (r, c), k*p, k*p + p); -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::linear_slice (octave_idx_type lo, octave_idx_type up) const -{ - if (up < lo) - up = lo; - - return Array<T, Alloc> (*this, dim_vector (up - lo, 1), lo, up); -} - -// Helper class for multi-d dimension permuting (generalized transpose). -class rec_permute_helper -{ -public: - rec_permute_helper (const dim_vector& dv, const Array<octave_idx_type>& perm) - - : m_n (dv.ndims ()), m_top (0), m_dim (new octave_idx_type [2*m_n]), - m_stride (m_dim + m_n), m_use_blk (false) - { - assert (m_n == perm.numel ()); - - // Get cumulative dimensions. - OCTAVE_LOCAL_BUFFER (octave_idx_type, cdim, m_n+1); - cdim[0] = 1; - for (int i = 1; i < m_n+1; i++) cdim[i] = cdim[i-1] * dv(i-1); - - // Setup the permuted strides. - for (int k = 0; k < m_n; k++) - { - int kk = perm(k); - m_dim[k] = dv(kk); - m_stride[k] = cdim[kk]; - } - - // Reduce contiguous runs. - for (int k = 1; k < m_n; k++) - { - if (m_stride[k] == m_stride[m_top]*m_dim[m_top]) - m_dim[m_top] *= m_dim[k]; - else - { - m_top++; - m_dim[m_top] = m_dim[k]; - m_stride[m_top] = m_stride[k]; - } - } - - // Determine whether we can use block transposes. - m_use_blk = m_top >= 1 && m_stride[1] == 1 && m_stride[0] == m_dim[1]; - - } - - // No copying! - - rec_permute_helper (const rec_permute_helper&) = delete; - - rec_permute_helper& operator = (const rec_permute_helper&) = delete; - - ~rec_permute_helper (void) { delete [] m_dim; } - - template <typename T> - void permute (const T *src, T *dest) const { do_permute (src, dest, m_top); } - - // Helper method for fast blocked transpose. - template <typename T> - static T * - blk_trans (const T *src, T *dest, octave_idx_type nr, octave_idx_type nc) - { - static const octave_idx_type m = 8; - OCTAVE_LOCAL_BUFFER (T, blk, m*m); - for (octave_idx_type kr = 0; kr < nr; kr += m) - for (octave_idx_type kc = 0; kc < nc; kc += m) - { - octave_idx_type lr = std::min (m, nr - kr); - octave_idx_type lc = std::min (m, nc - kc); - if (lr == m && lc == m) - { - const T *ss = src + kc * nr + kr; - for (octave_idx_type j = 0; j < m; j++) - for (octave_idx_type i = 0; i < m; i++) - blk[j*m+i] = ss[j*nr + i]; - T *dd = dest + kr * nc + kc; - for (octave_idx_type j = 0; j < m; j++) - for (octave_idx_type i = 0; i < m; i++) - dd[j*nc+i] = blk[i*m+j]; - } - else - { - const T *ss = src + kc * nr + kr; - for (octave_idx_type j = 0; j < lc; j++) - for (octave_idx_type i = 0; i < lr; i++) - blk[j*m+i] = ss[j*nr + i]; - T *dd = dest + kr * nc + kc; - for (octave_idx_type j = 0; j < lr; j++) - for (octave_idx_type i = 0; i < lc; i++) - dd[j*nc+i] = blk[i*m+j]; - } - } - - return dest + nr*nc; - } - -private: - - // Recursive N-D generalized transpose - template <typename T> - T * do_permute (const T *src, T *dest, int lev) const - { - if (lev == 0) - { - octave_idx_type step = m_stride[0]; - octave_idx_type len = m_dim[0]; - if (step == 1) - { - std::copy_n (src, len, dest); - dest += len; - } - else - { - for (octave_idx_type i = 0, j = 0; i < len; i++, j += step) - dest[i] = src[j]; - - dest += len; - } - } - else if (m_use_blk && lev == 1) - dest = blk_trans (src, dest, m_dim[1], m_dim[0]); - else - { - octave_idx_type step = m_stride[lev]; - octave_idx_type len = m_dim[lev]; - for (octave_idx_type i = 0, j = 0; i < len; i++, j+= step) - dest = do_permute (src + i * step, dest, lev-1); - } - - return dest; - } - - //-------- - - // STRIDE occupies the last half of the space allocated for dim to - // avoid a double allocation. - - int m_n; - int m_top; - octave_idx_type *m_dim; - octave_idx_type *m_stride; - bool m_use_blk; -}; - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::permute (const Array<octave_idx_type>& perm_vec_arg, bool inv) const -{ - Array<T, Alloc> retval; - - Array<octave_idx_type> perm_vec = perm_vec_arg; - - dim_vector dv = dims (); - - int perm_vec_len = perm_vec_arg.numel (); - - if (perm_vec_len < dv.ndims ()) - (*current_liboctave_error_handler) - ("%s: invalid permutation vector", inv ? "ipermute" : "permute"); - - dim_vector dv_new = dim_vector::alloc (perm_vec_len); - - // Append singleton dimensions as needed. - dv.resize (perm_vec_len, 1); - - // Need this array to check for identical elements in permutation array. - OCTAVE_LOCAL_BUFFER_INIT (bool, checked, perm_vec_len, false); - - bool identity = true; - - // Find dimension vector of permuted array. - for (int i = 0; i < perm_vec_len; i++) - { - octave_idx_type perm_elt = perm_vec.elem (i); - if (perm_elt >= perm_vec_len || perm_elt < 0) - (*current_liboctave_error_handler) - ("%s: permutation vector contains an invalid element", - inv ? "ipermute" : "permute"); - - if (checked[perm_elt]) - (*current_liboctave_error_handler) - ("%s: permutation vector cannot contain identical elements", - inv ? "ipermute" : "permute"); - else - { - checked[perm_elt] = true; - identity = identity && perm_elt == i; - } - } - - if (identity) - return *this; - - if (inv) - { - for (int i = 0; i < perm_vec_len; i++) - perm_vec(perm_vec_arg(i)) = i; - } - - for (int i = 0; i < perm_vec_len; i++) - dv_new(i) = dv(perm_vec(i)); - - retval = Array<T, Alloc> (dv_new); - - if (numel () > 0) - { - rec_permute_helper rh (dv, perm_vec); - rh.permute (data (), retval.fortran_vec ()); - } - - return retval; -} - -// Helper class for multi-d index reduction and recursive -// indexing/indexed assignment. Rationale: we could avoid recursion -// using a state machine instead. However, using recursion is much -// more amenable to possible parallelization in the future. -// Also, the recursion solution is cleaner and more understandable. - -class rec_index_helper -{ -public: - rec_index_helper (const dim_vector& dv, const Array<octave::idx_vector>& ia) - : m_n (ia.numel ()), m_top (0), m_dim (new octave_idx_type [2*m_n]), - m_cdim (m_dim + m_n), m_idx (new octave::idx_vector [m_n]) - { - assert (m_n > 0 && (dv.ndims () == std::max (m_n, 2))); - - m_dim[0] = dv(0); - m_cdim[0] = 1; - m_idx[0] = ia(0); - - for (int i = 1; i < m_n; i++) - { - // Try reduction... - if (m_idx[m_top].maybe_reduce (m_dim[m_top], ia(i), dv(i))) - { - // Reduction successful, fold dimensions. - m_dim[m_top] *= dv(i); - } - else - { - // Unsuccessful, store index & cumulative m_dim. - m_top++; - m_idx[m_top] = ia(i); - m_dim[m_top] = dv(i); - m_cdim[m_top] = m_cdim[m_top-1] * m_dim[m_top-1]; - } - } - } - - // No copying! - - rec_index_helper (const rec_index_helper&) = delete; - - rec_index_helper& operator = (const rec_index_helper&) = delete; - - ~rec_index_helper (void) { delete [] m_idx; delete [] m_dim; } - - template <typename T> - void index (const T *src, T *dest) const { do_index (src, dest, m_top); } - - template <typename T> - void assign (const T *src, T *dest) const { do_assign (src, dest, m_top); } - - template <typename T> - void fill (const T& val, T *dest) const { do_fill (val, dest, m_top); } - - bool is_cont_range (octave_idx_type& l, octave_idx_type& u) const - { - return m_top == 0 && m_idx[0].is_cont_range (m_dim[0], l, u); - } - -private: - - // Recursive N-D indexing - template <typename T> - T * do_index (const T *src, T *dest, int lev) const - { - if (lev == 0) - dest += m_idx[0].index (src, m_dim[0], dest); - else - { - octave_idx_type nn = m_idx[lev].length (m_dim[lev]); - octave_idx_type d = m_cdim[lev]; - for (octave_idx_type i = 0; i < nn; i++) - dest = do_index (src + d*m_idx[lev].xelem (i), dest, lev-1); - } - - return dest; - } - - // Recursive N-D indexed assignment - template <typename T> - const T * do_assign (const T *src, T *dest, int lev) const - { - if (lev == 0) - src += m_idx[0].assign (src, m_dim[0], dest); - else - { - octave_idx_type nn = m_idx[lev].length (m_dim[lev]); - octave_idx_type d = m_cdim[lev]; - for (octave_idx_type i = 0; i < nn; i++) - src = do_assign (src, dest + d*m_idx[lev].xelem (i), lev-1); - } - - return src; - } - - // Recursive N-D indexed assignment - template <typename T> - void do_fill (const T& val, T *dest, int lev) const - { - if (lev == 0) - m_idx[0].fill (val, m_dim[0], dest); - else - { - octave_idx_type nn = m_idx[lev].length (m_dim[lev]); - octave_idx_type d = m_cdim[lev]; - for (octave_idx_type i = 0; i < nn; i++) - do_fill (val, dest + d*m_idx[lev].xelem (i), lev-1); - } - } - - //-------- - - // CDIM occupies the last half of the space allocated for dim to - // avoid a double allocation. - - int m_n; - int m_top; - octave_idx_type *m_dim; - octave_idx_type *m_cdim; - octave::idx_vector *m_idx; -}; - -// Helper class for multi-d recursive resizing -// This handles resize () in an efficient manner, touching memory only -// once (apart from reinitialization) -class rec_resize_helper -{ -public: - rec_resize_helper (const dim_vector& ndv, const dim_vector& odv) - : m_cext (nullptr), m_sext (nullptr), m_dext (nullptr), m_n (0) - { - int l = ndv.ndims (); - assert (odv.ndims () == l); - octave_idx_type ld = 1; - int i = 0; - for (; i < l-1 && ndv(i) == odv(i); i++) ld *= ndv(i); - m_n = l - i; - m_cext = new octave_idx_type [3*m_n]; - // Trick to avoid three allocations - m_sext = m_cext + m_n; - m_dext = m_sext + m_n; - - octave_idx_type sld = ld; - octave_idx_type dld = ld; - for (int j = 0; j < m_n; j++) - { - m_cext[j] = std::min (ndv(i+j), odv(i+j)); - m_sext[j] = sld *= odv(i+j); - m_dext[j] = dld *= ndv(i+j); - } - m_cext[0] *= ld; - } - - // No copying! - - rec_resize_helper (const rec_resize_helper&) = delete; - - rec_resize_helper& operator = (const rec_resize_helper&) = delete; - - ~rec_resize_helper (void) { delete [] m_cext; } - - template <typename T> - void resize_fill (const T *src, T *dest, const T& rfv) const - { do_resize_fill (src, dest, rfv, m_n-1); } - -private: - - // recursive resizing - template <typename T> - void do_resize_fill (const T *src, T *dest, const T& rfv, int lev) const - { - if (lev == 0) - { - std::copy_n (src, m_cext[0], dest); - std::fill_n (dest + m_cext[0], m_dext[0] - m_cext[0], rfv); - } - else - { - octave_idx_type sd, dd, k; - sd = m_sext[lev-1]; - dd = m_dext[lev-1]; - for (k = 0; k < m_cext[lev]; k++) - do_resize_fill (src + k * sd, dest + k * dd, rfv, lev - 1); - - std::fill_n (dest + k * dd, m_dext[lev] - k * dd, rfv); - } - } - - //-------- - - octave_idx_type *m_cext; - octave_idx_type *m_sext; - octave_idx_type *m_dext; - int m_n; -}; - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::index (const octave::idx_vector& i) const -{ - // Colon: - // - // object | index | result orientation - // ---------+----------+------------------- - // anything | colon | column vector - // - // Numeric array or logical mask: - // - // Note that logical mask indices are always transformed to vectors - // before we see them. - // - // object | index | result orientation - // ---------+----------+------------------- - // vector | vector | indexed object - // | other | same size as index - // ---------+----------+------------------- - // array | anything | same size as index - - octave_idx_type n = numel (); - Array<T, Alloc> retval; - - if (i.is_colon ()) - { - // A(:) produces a shallow copy as a column vector. - retval = Array<T, Alloc> (*this, dim_vector (n, 1)); - } - else - { - if (i.extent (n) != n) - octave::err_index_out_of_range (1, 1, i.extent (n), n, m_dimensions); - - dim_vector result_dims = i.orig_dimensions (); - octave_idx_type idx_len = i.length (); - - if (n != 1 && is_nd_vector () && idx_len != 1 - && result_dims.is_nd_vector ()) - { - // Indexed object and index are both vectors. Set result size - // and orientation as above. - - dim_vector dv = dims (); - - result_dims = dv.make_nd_vector (idx_len); - } - - octave_idx_type l, u; - if (idx_len != 0 && i.is_cont_range (n, l, u)) - // If suitable, produce a shallow slice. - retval = Array<T, Alloc> (*this, result_dims, l, u); - else - { - // Don't use resize here to avoid useless initialization for POD - // types. - retval = Array<T, Alloc> (result_dims); - - if (idx_len != 0) - i.index (data (), n, retval.fortran_vec ()); - } - } - - return retval; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::index (const octave::idx_vector& i, const octave::idx_vector& j) const -{ - // Get dimensions, allowing Fortran indexing in the 2nd dim. - dim_vector dv = m_dimensions.redim (2); - octave_idx_type r = dv(0); - octave_idx_type c = dv(1); - Array<T, Alloc> retval; - - if (i.is_colon () && j.is_colon ()) - { - // A(:,:) produces a shallow copy. - retval = Array<T, Alloc> (*this, dv); - } - else - { - if (i.extent (r) != r) - octave::err_index_out_of_range (2, 1, i.extent (r), r, m_dimensions); // throws - if (j.extent (c) != c) - octave::err_index_out_of_range (2, 2, j.extent (c), c, m_dimensions); // throws - - octave_idx_type n = numel (); - octave_idx_type il = i.length (r); - octave_idx_type jl = j.length (c); - - octave::idx_vector ii (i); - - if (ii.maybe_reduce (r, j, c)) - { - octave_idx_type l, u; - if (ii.length () > 0 && ii.is_cont_range (n, l, u)) - // If suitable, produce a shallow slice. - retval = Array<T, Alloc> (*this, dim_vector (il, jl), l, u); - else - { - // Don't use resize to avoid useless initialization for POD types. - retval = Array<T, Alloc> (dim_vector (il, jl)); - - ii.index (data (), n, retval.fortran_vec ()); - } - } - else - { - // Don't use resize to avoid useless initialization for POD types. - retval = Array<T, Alloc> (dim_vector (il, jl)); - - const T *src = data (); - T *dest = retval.fortran_vec (); - - for (octave_idx_type k = 0; k < jl; k++) - dest += i.index (src + r * j.xelem (k), r, dest); - } - } - - return retval; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::index (const Array<octave::idx_vector>& ia) const -{ - int ial = ia.numel (); - Array<T, Alloc> retval; - - // FIXME: is this dispatching necessary? - if (ial == 1) - retval = index (ia(0)); - else if (ial == 2) - retval = index (ia(0), ia(1)); - else if (ial > 0) - { - // Get dimensions, allowing Fortran indexing in the last dim. - dim_vector dv = m_dimensions.redim (ial); - - // Check for out of bounds conditions. - bool all_colons = true; - for (int i = 0; i < ial; i++) - { - if (ia(i).extent (dv(i)) != dv(i)) - octave::err_index_out_of_range (ial, i+1, ia(i).extent (dv(i)), dv(i), - m_dimensions); // throws - - all_colons = all_colons && ia(i).is_colon (); - } - - if (all_colons) - { - // A(:,:,...,:) produces a shallow copy. - dv.chop_trailing_singletons (); - retval = Array<T, Alloc> (*this, dv); - } - else - { - // Form result dimensions. - dim_vector rdv = dim_vector::alloc (ial); - for (int i = 0; i < ial; i++) rdv(i) = ia(i).length (dv(i)); - rdv.chop_trailing_singletons (); - - // Prepare for recursive indexing - rec_index_helper rh (dv, ia); - - octave_idx_type l, u; - if (rh.is_cont_range (l, u)) - // If suitable, produce a shallow slice. - retval = Array<T, Alloc> (*this, rdv, l, u); - else - { - // Don't use resize to avoid useless initialization for POD types. - retval = Array<T, Alloc> (rdv); - - // Do it. - rh.index (data (), retval.fortran_vec ()); - } - } - } - - return retval; -} - -// The default fill value. Override if you want a different one. - -template <typename T, typename Alloc> -T -Array<T, Alloc>::resize_fill_value (void) const -{ - static T zero = T (); - return zero; -} - -// Yes, we could do resize using index & assign. However, that would -// possibly involve a lot more memory traffic than we actually need. - -template <typename T, typename Alloc> -void -Array<T, Alloc>::resize1 (octave_idx_type n, const T& rfv) -{ - if (n < 0 || ndims () != 2) - octave::err_invalid_resize (); - - dim_vector dv; - // This is driven by Matlab's behavior of giving a *row* vector - // on some out-of-bounds assignments. Specifically, Matlab - // allows a(i) with out-of-bouds i when a is either of 0x0, 1x0, - // 1x1, 0xN, and gives a row vector in all cases (yes, even the - // last one, search me why). Giving a column vector would make - // much more sense (given the way trailing singleton dims are - // treated). - bool invalid = false; - if (rows () == 0 || rows () == 1) - dv = dim_vector (1, n); - else if (columns () == 1) - dv = dim_vector (n, 1); - else - invalid = true; - - if (invalid) - octave::err_invalid_resize (); - - octave_idx_type nx = numel (); - if (n == nx - 1 && n > 0) - { - // Stack "pop" operation. - if (m_rep->m_count == 1) - m_slice_data[m_slice_len-1] = T (); - m_slice_len--; - m_dimensions = dv; - } - else if (n == nx + 1 && nx > 0) - { - // Stack "push" operation. - if (m_rep->m_count == 1 - && m_slice_data + m_slice_len < m_rep->m_data + m_rep->m_len) - { - m_slice_data[m_slice_len++] = rfv; - m_dimensions = dv; - } - else - { - static const octave_idx_type max_stack_chunk = 1024; - octave_idx_type nn = n + std::min (nx, max_stack_chunk); - Array<T, Alloc> tmp (Array<T, Alloc> (dim_vector (nn, 1)), dv, 0, n); - T *dest = tmp.fortran_vec (); - - std::copy_n (data (), nx, dest); - dest[nx] = rfv; - - *this = tmp; - } - } - else if (n != nx) - { - Array<T, Alloc> tmp = Array<T, Alloc> (dv); - T *dest = tmp.fortran_vec (); - - octave_idx_type n0 = std::min (n, nx); - octave_idx_type n1 = n - n0; - std::copy_n (data (), n0, dest); - std::fill_n (dest + n0, n1, rfv); - - *this = tmp; - } -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::resize2 (octave_idx_type r, octave_idx_type c, const T& rfv) -{ - if (r < 0 || c < 0 || ndims () != 2) - octave::err_invalid_resize (); - - octave_idx_type rx = rows (); - octave_idx_type cx = columns (); - if (r != rx || c != cx) - { - Array<T, Alloc> tmp = Array<T, Alloc> (dim_vector (r, c)); - T *dest = tmp.fortran_vec (); - - octave_idx_type r0 = std::min (r, rx); - octave_idx_type r1 = r - r0; - octave_idx_type c0 = std::min (c, cx); - octave_idx_type c1 = c - c0; - const T *src = data (); - if (r == rx) - { - std::copy_n (src, r * c0, dest); - dest += r * c0; - } - else - { - for (octave_idx_type k = 0; k < c0; k++) - { - std::copy_n (src, r0, dest); - src += rx; - dest += r0; - std::fill_n (dest, r1, rfv); - dest += r1; - } - } - - std::fill_n (dest, r * c1, rfv); - - *this = tmp; - } -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::resize (const dim_vector& dv, const T& rfv) -{ - int dvl = dv.ndims (); - if (dvl == 2) - resize2 (dv(0), dv(1), rfv); - else if (m_dimensions != dv) - { - if (m_dimensions.ndims () > dvl || dv.any_neg ()) - octave::err_invalid_resize (); - - Array<T, Alloc> tmp (dv); - // Prepare for recursive resizing. - rec_resize_helper rh (dv, m_dimensions.redim (dvl)); - - // Do it. - rh.resize_fill (data (), tmp.fortran_vec (), rfv); - *this = tmp; - } -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const -{ - Array<T, Alloc> tmp = *this; - if (resize_ok) - { - octave_idx_type n = numel (); - octave_idx_type nx = i.extent (n); - if (n != nx) - { - if (i.is_scalar ()) - return Array<T, Alloc> (dim_vector (1, 1), rfv); - else - tmp.resize1 (nx, rfv); - } - - if (tmp.numel () != nx) - return Array<T, Alloc> (); - } - - return tmp.index (i); -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::index (const octave::idx_vector& i, const octave::idx_vector& j, - bool resize_ok, const T& rfv) const -{ - Array<T, Alloc> tmp = *this; - if (resize_ok) - { - dim_vector dv = m_dimensions.redim (2); - octave_idx_type r = dv(0); - octave_idx_type c = dv(1); - octave_idx_type rx = i.extent (r); - octave_idx_type cx = j.extent (c); - if (r != rx || c != cx) - { - if (i.is_scalar () && j.is_scalar ()) - return Array<T, Alloc> (dim_vector (1, 1), rfv); - else - tmp.resize2 (rx, cx, rfv); - } - - if (tmp.rows () != rx || tmp.columns () != cx) - return Array<T, Alloc> (); - } - - return tmp.index (i, j); -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::index (const Array<octave::idx_vector>& ia, - bool resize_ok, const T& rfv) const -{ - Array<T, Alloc> tmp = *this; - if (resize_ok) - { - int ial = ia.numel (); - dim_vector dv = m_dimensions.redim (ial); - dim_vector dvx = dim_vector::alloc (ial); - for (int i = 0; i < ial; i++) - dvx(i) = ia(i).extent (dv(i)); - if (! (dvx == dv)) - { - bool all_scalars = true; - for (int i = 0; i < ial; i++) - all_scalars = all_scalars && ia(i).is_scalar (); - if (all_scalars) - return Array<T, Alloc> (dim_vector (1, 1), rfv); - else - tmp.resize (dvx, rfv); - - if (tmp.m_dimensions != dvx) - return Array<T, Alloc> (); - } - } - - return tmp.index (ia); -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::assign (const octave::idx_vector& i, const Array<T, Alloc>& rhs, const T& rfv) -{ - octave_idx_type n = numel (); - octave_idx_type rhl = rhs.numel (); - - if (rhl != 1 && i.length (n) != rhl) - octave::err_nonconformant ("=", dim_vector(i.length(n), 1), rhs.dims()); - - octave_idx_type nx = i.extent (n); - bool colon = i.is_colon_equiv (nx); - // Try to resize first if necessary. - if (nx != n) - { - // Optimize case A = []; A(1:n) = X with A empty. - if (m_dimensions.zero_by_zero () && colon) - { - if (rhl == 1) - *this = Array<T, Alloc> (dim_vector (1, nx), rhs(0)); - else - *this = Array<T, Alloc> (rhs, dim_vector (1, nx)); - return; - } - - resize1 (nx, rfv); - n = numel (); - } - - if (colon) - { - // A(:) = X makes a full fill or a shallow copy. - if (rhl == 1) - fill (rhs(0)); - else - *this = rhs.reshape (m_dimensions); - } - else - { - if (rhl == 1) - i.fill (rhs(0), n, fortran_vec ()); - else - i.assign (rhs.data (), n, fortran_vec ()); - } -} - -// Assignment to a 2-dimensional array -template <typename T, typename Alloc> -void -Array<T, Alloc>::assign (const octave::idx_vector& i, const octave::idx_vector& j, - const Array<T, Alloc>& rhs, const T& rfv) -{ - bool initial_dims_all_zero = m_dimensions.all_zero (); - - // Get RHS extents, discarding singletons. - dim_vector rhdv = rhs.dims (); - - // Get LHS extents, allowing Fortran indexing in the second dim. - dim_vector dv = m_dimensions.redim (2); - - // Check for out-of-bounds and form resizing m_dimensions. - dim_vector rdv; - - // In the special when all dimensions are zero, colons are allowed - // to inquire the shape of RHS. The rules are more obscure, so we - // solve that elsewhere. - if (initial_dims_all_zero) - rdv = zero_dims_inquire (i, j, rhdv); - else - { - rdv(0) = i.extent (dv(0)); - rdv(1) = j.extent (dv(1)); - } - - bool isfill = rhs.numel () == 1; - octave_idx_type il = i.length (rdv(0)); - octave_idx_type jl = j.length (rdv(1)); - rhdv.chop_all_singletons (); - bool match = (isfill - || (rhdv.ndims () == 2 && il == rhdv(0) && jl == rhdv(1))); - match = match || (il == 1 && jl == rhdv(0) && rhdv(1) == 1); - - if (match) - { - bool all_colons = (i.is_colon_equiv (rdv(0)) - && j.is_colon_equiv (rdv(1))); - // Resize if requested. - if (rdv != dv) - { - // Optimize case A = []; A(1:m, 1:n) = X - if (dv.zero_by_zero () && all_colons) - { - if (isfill) - *this = Array<T, Alloc> (rdv, rhs(0)); - else - *this = Array<T, Alloc> (rhs, rdv); - return; - } - - resize (rdv, rfv); - dv = m_dimensions; - } - - if (all_colons) - { - // A(:,:) = X makes a full fill or a shallow copy - if (isfill) - fill (rhs(0)); - else - *this = rhs.reshape (m_dimensions); - } - else - { - // The actual work. - octave_idx_type n = numel (); - octave_idx_type r = dv(0); - octave_idx_type c = dv(1); - octave::idx_vector ii (i); - - const T *src = rhs.data (); - T *dest = fortran_vec (); - - // Try reduction first. - if (ii.maybe_reduce (r, j, c)) - { - if (isfill) - ii.fill (*src, n, dest); - else - ii.assign (src, n, dest); - } - else - { - if (isfill) - { - for (octave_idx_type k = 0; k < jl; k++) - i.fill (*src, r, dest + r * j.xelem (k)); - } - else - { - for (octave_idx_type k = 0; k < jl; k++) - src += i.assign (src, r, dest + r * j.xelem (k)); - } - } - } - } - // any empty RHS can be assigned to an empty LHS - else if ((il != 0 && jl != 0) || (rhdv(0) != 0 && rhdv(1) != 0)) - octave::err_nonconformant ("=", il, jl, rhs.dim1 (), rhs.dim2 ()); -} - -// Assignment to a multi-dimensional array -template <typename T, typename Alloc> -void -Array<T, Alloc>::assign (const Array<octave::idx_vector>& ia, - const Array<T, Alloc>& rhs, const T& rfv) -{ - int ial = ia.numel (); - - // FIXME: is this dispatching necessary / desirable? - if (ial == 1) - assign (ia(0), rhs, rfv); - else if (ial == 2) - assign (ia(0), ia(1), rhs, rfv); - else if (ial > 0) - { - bool initial_dims_all_zero = m_dimensions.all_zero (); - - // Get RHS extents, discarding singletons. - dim_vector rhdv = rhs.dims (); - - // Get LHS extents, allowing Fortran indexing in the second dim. - dim_vector dv = m_dimensions.redim (ial); - - // Get the extents forced by indexing. - dim_vector rdv; - - // In the special when all dimensions are zero, colons are - // allowed to inquire the shape of RHS. The rules are more - // obscure, so we solve that elsewhere. - if (initial_dims_all_zero) - rdv = zero_dims_inquire (ia, rhdv); - else - { - rdv = dim_vector::alloc (ial); - for (int i = 0; i < ial; i++) - rdv(i) = ia(i).extent (dv(i)); - } - - // Check whether LHS and RHS match, up to singleton dims. - bool match = true; - bool all_colons = true; - bool isfill = rhs.numel () == 1; - - rhdv.chop_all_singletons (); - int j = 0; - int rhdvl = rhdv.ndims (); - for (int i = 0; i < ial; i++) - { - all_colons = all_colons && ia(i).is_colon_equiv (rdv(i)); - octave_idx_type l = ia(i).length (rdv(i)); - if (l == 1) continue; - match = match && j < rhdvl && l == rhdv(j++); - } - - match = match && (j == rhdvl || rhdv(j) == 1); - match = match || isfill; - - if (match) - { - // Resize first if necessary. - if (rdv != dv) - { - // Optimize case A = []; A(1:m, 1:n) = X - if (dv.zero_by_zero () && all_colons) - { - rdv.chop_trailing_singletons (); - if (isfill) - *this = Array<T, Alloc> (rdv, rhs(0)); - else - *this = Array<T, Alloc> (rhs, rdv); - return; - } - - resize (rdv, rfv); - dv = rdv; - } - - if (all_colons) - { - // A(:,:,...,:) = X makes a full fill or a shallow copy. - if (isfill) - fill (rhs(0)); - else - *this = rhs.reshape (m_dimensions); - } - else - { - // Do the actual work. - - // Prepare for recursive indexing - rec_index_helper rh (dv, ia); - - // Do it. - if (isfill) - rh.fill (rhs(0), fortran_vec ()); - else - rh.assign (rhs.data (), fortran_vec ()); - } - } - else - { - // dimension mismatch, unless LHS and RHS both empty - bool lhsempty, rhsempty; - lhsempty = rhsempty = false; - dim_vector lhs_dv = dim_vector::alloc (ial); - for (int i = 0; i < ial; i++) - { - octave_idx_type l = ia(i).length (rdv(i)); - lhs_dv(i) = l; - lhsempty = lhsempty || (l == 0); - rhsempty = rhsempty || (rhdv(j++) == 0); - } - if (! lhsempty || ! rhsempty) - { - lhs_dv.chop_trailing_singletons (); - octave::err_nonconformant ("=", lhs_dv, rhdv); - } - } - } -} - -/* -%!shared a -%! a = [1 2; 3 4]; -%!error <op1 is 1x2, op2 is 1x3> a(1,:) = [1 2 3] -%!error <op1 is 2x1, op2 is 1x3> a(:,1) = [1 2 3] -%!error <op1 is 2x2, op2 is 2x1> a(1:2,2:3) = [1;2] -*/ - -template <typename T, typename Alloc> -void -Array<T, Alloc>::delete_elements (const octave::idx_vector& i) -{ - octave_idx_type n = numel (); - if (i.is_colon ()) - { - *this = Array<T, Alloc> (); - } - else if (i.length (n) != 0) - { - if (i.extent (n) != n) - octave::err_del_index_out_of_range (true, i.extent (n), n); - - octave_idx_type l, u; - bool col_vec = ndims () == 2 && columns () == 1 && rows () != 1; - if (i.is_scalar () && i(0) == n-1 && m_dimensions.isvector ()) - { - // Stack "pop" operation. - resize1 (n-1); - } - else if (i.is_cont_range (n, l, u)) - { - // Special case deleting a contiguous range. - octave_idx_type m = n + l - u; - Array<T, Alloc> tmp (dim_vector (col_vec ? m : 1, ! col_vec ? m : 1)); - const T *src = data (); - T *dest = tmp.fortran_vec (); - std::copy_n (src, l, dest); - std::copy (src + u, src + n, dest + l); - *this = tmp; - } - else - { - // Use index. - *this = index (i.complement (n)); - } - } -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::delete_elements (int dim, const octave::idx_vector& i) -{ - if (dim < 0 || dim >= ndims ()) - (*current_liboctave_error_handler) ("invalid dimension in delete_elements"); - - octave_idx_type n = m_dimensions(dim); - if (i.is_colon ()) - { - *this = Array<T, Alloc> (); - } - else if (i.length (n) != 0) - { - if (i.extent (n) != n) - octave::err_del_index_out_of_range (false, i.extent (n), n); - - octave_idx_type l, u; - - if (i.is_cont_range (n, l, u)) - { - // Special case deleting a contiguous range. - octave_idx_type nd = n + l - u; - octave_idx_type dl = 1; - octave_idx_type du = 1; - dim_vector rdv = m_dimensions; - rdv(dim) = nd; - for (int k = 0; k < dim; k++) dl *= m_dimensions(k); - for (int k = dim + 1; k < ndims (); k++) du *= m_dimensions(k); - - // Special case deleting a contiguous range. - Array<T, Alloc> tmp = Array<T, Alloc> (rdv); - const T *src = data (); - T *dest = tmp.fortran_vec (); - l *= dl; u *= dl; n *= dl; - for (octave_idx_type k = 0; k < du; k++) - { - std::copy_n (src, l, dest); - dest += l; - std::copy (src + u, src + n, dest); - dest += n - u; - src += n; - } - - *this = tmp; - } - else - { - // Use index. - Array<octave::idx_vector> ia (dim_vector (ndims (), 1), octave::idx_vector::colon); - ia (dim) = i.complement (n); - *this = index (ia); - } - } -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::delete_elements (const Array<octave::idx_vector>& ia) -{ - int ial = ia.numel (); - - if (ial == 1) - delete_elements (ia(0)); - else - { - int k, dim = -1; - for (k = 0; k < ial; k++) - { - if (! ia(k).is_colon ()) - { - if (dim < 0) - dim = k; - else - break; - } - } - if (dim < 0) - { - dim_vector dv = m_dimensions; - dv(0) = 0; - *this = Array<T, Alloc> (dv); - } - else if (k == ial) - { - delete_elements (dim, ia(dim)); - } - else - { - // Allow the null assignment to succeed if it won't change - // anything because the indices reference an empty slice, - // provided that there is at most one non-colon (or - // equivalent) index. So, we still have the requirement of - // deleting a slice, but it is OK if the slice is empty. - - // For compatibility with Matlab, stop checking once we see - // more than one non-colon index or an empty index. Matlab - // considers "[]" to be an empty index but not "false". We - // accept both. - - bool empty_assignment = false; - - int num_non_colon_indices = 0; - - int nd = ndims (); - - for (int i = 0; i < ial; i++) - { - octave_idx_type dim_len = (i >= nd ? 1 : m_dimensions(i)); - - if (ia(i).length (dim_len) == 0) - { - empty_assignment = true; - break; - } - - if (! ia(i).is_colon_equiv (dim_len)) - { - num_non_colon_indices++; - - if (num_non_colon_indices == 2) - break; - } - } - - if (! empty_assignment) - (*current_liboctave_error_handler) - ("a null assignment can only have one non-colon index"); - } - } - -} - -template <typename T, typename Alloc> -Array<T, Alloc>& -Array<T, Alloc>::insert (const Array<T, Alloc>& a, octave_idx_type r, octave_idx_type c) -{ - octave::idx_vector i (r, r + a.rows ()); - octave::idx_vector j (c, c + a.columns ()); - if (ndims () == 2 && a.ndims () == 2) - assign (i, j, a); - else - { - Array<octave::idx_vector> idx (dim_vector (a.ndims (), 1)); - idx(0) = i; - idx(1) = j; - for (int k = 2; k < a.ndims (); k++) - idx(k) = octave::idx_vector (0, a.m_dimensions(k)); - assign (idx, a); - } - - return *this; -} - -template <typename T, typename Alloc> -Array<T, Alloc>& -Array<T, Alloc>::insert (const Array<T, Alloc>& a, const Array<octave_idx_type>& ra_idx) -{ - octave_idx_type n = ra_idx.numel (); - Array<octave::idx_vector> idx (dim_vector (n, 1)); - const dim_vector dva = a.dims ().redim (n); - for (octave_idx_type k = 0; k < n; k++) - idx(k) = octave::idx_vector (ra_idx(k), ra_idx(k) + dva(k)); - - assign (idx, a); - - return *this; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::transpose (void) const -{ - assert (ndims () == 2); - - octave_idx_type nr = dim1 (); - octave_idx_type nc = dim2 (); - - if (nr >= 8 && nc >= 8) - { - Array<T, Alloc> result (dim_vector (nc, nr)); - - // Reuse the implementation used for permuting. - - rec_permute_helper::blk_trans (data (), result.fortran_vec (), nr, nc); - - return result; - } - else if (nr > 1 && nc > 1) - { - Array<T, Alloc> result (dim_vector (nc, nr)); - - for (octave_idx_type j = 0; j < nc; j++) - for (octave_idx_type i = 0; i < nr; i++) - result.xelem (j, i) = xelem (i, j); - - return result; - } - else - { - // Fast transpose for vectors and empty matrices. - return Array<T, Alloc> (*this, dim_vector (nc, nr)); - } -} - -template <typename T> -static T -no_op_fcn (const T& x) -{ - return x; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::hermitian (T (*fcn) (const T&)) const -{ - assert (ndims () == 2); - - if (! fcn) - fcn = no_op_fcn<T>; - - octave_idx_type nr = dim1 (); - octave_idx_type nc = dim2 (); - - if (nr >= 8 && nc >= 8) - { - Array<T, Alloc> result (dim_vector (nc, nr)); - - // Blocked transpose to attempt to avoid cache misses. - - T buf[64]; - - octave_idx_type jj; - for (jj = 0; jj < (nc - 8 + 1); jj += 8) - { - octave_idx_type ii; - for (ii = 0; ii < (nr - 8 + 1); ii += 8) - { - // Copy to buffer - for (octave_idx_type j = jj, k = 0, idxj = jj * nr; - j < jj + 8; j++, idxj += nr) - for (octave_idx_type i = ii; i < ii + 8; i++) - buf[k++] = xelem (i + idxj); - - // Copy from buffer - for (octave_idx_type i = ii, idxi = ii * nc; i < ii + 8; - i++, idxi += nc) - for (octave_idx_type j = jj, k = i - ii; j < jj + 8; - j++, k+=8) - result.xelem (j + idxi) = fcn (buf[k]); - } - - if (ii < nr) - for (octave_idx_type j = jj; j < jj + 8; j++) - for (octave_idx_type i = ii; i < nr; i++) - result.xelem (j, i) = fcn (xelem (i, j)); - } - - for (octave_idx_type j = jj; j < nc; j++) - for (octave_idx_type i = 0; i < nr; i++) - result.xelem (j, i) = fcn (xelem (i, j)); - - return result; - } - else - { - Array<T, Alloc> result (dim_vector (nc, nr)); - - for (octave_idx_type j = 0; j < nc; j++) - for (octave_idx_type i = 0; i < nr; i++) - result.xelem (j, i) = fcn (xelem (i, j)); - - return result; - } -} - -/* - -## Transpose tests for matrices of the tile size and plus or minus a row -## and with four tiles. - -%!shared m7, mt7, m8, mt8, m9, mt9 -%! m7 = reshape (1 : 7*8, 8, 7); -%! mt7 = [1:8; 9:16; 17:24; 25:32; 33:40; 41:48; 49:56]; -%! m8 = reshape (1 : 8*8, 8, 8); -%! mt8 = mt8 = [mt7; 57:64]; -%! m9 = reshape (1 : 9*8, 8, 9); -%! mt9 = [mt8; 65:72]; - -%!assert (m7', mt7) -%!assert ((1i*m7).', 1i * mt7) -%!assert ((1i*m7)', conj (1i * mt7)) -%!assert (m8', mt8) -%!assert ((1i*m8).', 1i * mt8) -%!assert ((1i*m8)', conj (1i * mt8)) -%!assert (m9', mt9) -%!assert ((1i*m9).', 1i * mt9) -%!assert ((1i*m9)', conj (1i * mt9)) -%!assert ([m7, m8; m7, m8]', [mt7, mt7; mt8, mt8]) -%!assert ((1i*[m7, m8; m7, m8]).', 1i * [mt7, mt7; mt8, mt8]) -%!assert ((1i*[m7, m8; m7, m8])', conj (1i * [mt7, mt7; mt8, mt8])) -%!assert ([m8, m8; m8, m8]', [mt8, mt8; mt8, mt8]) -%!assert ((1i*[m8, m8; m8, m8]).', 1i * [mt8, mt8; mt8, mt8]) -%!assert ((1i*[m8, m8; m8, m8])', conj (1i * [mt8, mt8; mt8, mt8])) -%!assert ([m9, m8; m9, m8]', [mt9, mt9; mt8, mt8]) -%!assert ((1i*[m9, m8; m9, m8]).', 1i * [mt9, mt9; mt8, mt8]) -%!assert ((1i*[m9, m8; m9, m8])', conj (1i * [mt9, mt9; mt8, mt8])) - -*/ - -template <typename T, typename Alloc> -T * -Array<T, Alloc>::fortran_vec (void) -{ - make_unique (); - - return m_slice_data; -} - -// Non-real types don't have NaNs. -template <typename T> -inline bool -sort_isnan (typename ref_param<T>::type) -{ - return false; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::sort (int dim, sortmode mode) const -{ - if (dim < 0) - (*current_liboctave_error_handler) ("sort: invalid dimension"); - - Array<T, Alloc> m (dims ()); - - dim_vector dv = m.dims (); - - if (m.numel () < 1) - return m; - - if (dim >= dv.ndims ()) - dv.resize (dim+1, 1); - - octave_idx_type ns = dv(dim); - octave_idx_type iter = dv.numel () / ns; - octave_idx_type stride = 1; - - for (int i = 0; i < dim; i++) - stride *= dv(i); - - T *v = m.fortran_vec (); - const T *ov = data (); - - octave_sort<T> lsort; - - if (mode != UNSORTED) - lsort.set_compare (mode); - else - return m; - - if (stride == 1) - { - // Special case along first dimension avoids gather/scatter AND directly - // sorts into destination buffer for an 11% performance boost. - for (octave_idx_type j = 0; j < iter; j++) - { - // Copy and partition out NaNs. - // No need to special case integer types <T> from floating point - // types <T> to avoid sort_isnan() test as it makes no discernible - // performance impact. - octave_idx_type kl = 0; - octave_idx_type ku = ns; - for (octave_idx_type i = 0; i < ns; i++) - { - T tmp = ov[i]; - if (sort_isnan<T> (tmp)) - v[--ku] = tmp; - else - v[kl++] = tmp; - } - - // sort. - lsort.sort (v, kl); - - if (ku < ns) - { - // NaNs are in reverse order - std::reverse (v + ku, v + ns); - if (mode == DESCENDING) - std::rotate (v, v + ku, v + ns); - } - - v += ns; - ov += ns; - } - } - else - { - OCTAVE_LOCAL_BUFFER (T, buf, ns); - - for (octave_idx_type j = 0; j < iter; j++) - { - octave_idx_type offset = j; - octave_idx_type n_strides = j / stride; - offset += n_strides * stride * (ns - 1); - - // gather and partition out NaNs. - octave_idx_type kl = 0; - octave_idx_type ku = ns; - for (octave_idx_type i = 0; i < ns; i++) - { - T tmp = ov[i*stride + offset]; - if (sort_isnan<T> (tmp)) - buf[--ku] = tmp; - else - buf[kl++] = tmp; - } - - // sort. - lsort.sort (buf, kl); - - if (ku < ns) - { - // NaNs are in reverse order - std::reverse (buf + ku, buf + ns); - if (mode == DESCENDING) - std::rotate (buf, buf + ku, buf + ns); - } - - // scatter. - for (octave_idx_type i = 0; i < ns; i++) - v[i*stride + offset] = buf[i]; - } - } - - return m; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::sort (Array<octave_idx_type>& sidx, int dim, - sortmode mode) const -{ - if (dim < 0 || dim >= ndims ()) - (*current_liboctave_error_handler) ("sort: invalid dimension"); - - Array<T, Alloc> m (dims ()); - - dim_vector dv = m.dims (); - - if (m.numel () < 1) - { - sidx = Array<octave_idx_type> (dv); - return m; - } - - octave_idx_type ns = dv(dim); - octave_idx_type iter = dv.numel () / ns; - octave_idx_type stride = 1; - - for (int i = 0; i < dim; i++) - stride *= dv(i); - - T *v = m.fortran_vec (); - const T *ov = data (); - - octave_sort<T> lsort; - - sidx = Array<octave_idx_type> (dv); - octave_idx_type *vi = sidx.fortran_vec (); - - if (mode != UNSORTED) - lsort.set_compare (mode); - else - return m; - - if (stride == 1) - { - // Special case for dim 1 avoids gather/scatter for performance boost. - // See comments in Array::sort (dim, mode). - for (octave_idx_type j = 0; j < iter; j++) - { - // copy and partition out NaNs. - octave_idx_type kl = 0; - octave_idx_type ku = ns; - for (octave_idx_type i = 0; i < ns; i++) - { - T tmp = ov[i]; - if (sort_isnan<T> (tmp)) - { - --ku; - v[ku] = tmp; - vi[ku] = i; - } - else - { - v[kl] = tmp; - vi[kl] = i; - kl++; - } - } - - // sort. - lsort.sort (v, vi, kl); - - if (ku < ns) - { - // NaNs are in reverse order - std::reverse (v + ku, v + ns); - std::reverse (vi + ku, vi + ns); - if (mode == DESCENDING) - { - std::rotate (v, v + ku, v + ns); - std::rotate (vi, vi + ku, vi + ns); - } - } - - v += ns; - vi += ns; - ov += ns; - } - } - else - { - OCTAVE_LOCAL_BUFFER (T, buf, ns); - OCTAVE_LOCAL_BUFFER (octave_idx_type, bufi, ns); - - for (octave_idx_type j = 0; j < iter; j++) - { - octave_idx_type offset = j; - octave_idx_type n_strides = j / stride; - offset += n_strides * stride * (ns - 1); - - // gather and partition out NaNs. - octave_idx_type kl = 0; - octave_idx_type ku = ns; - for (octave_idx_type i = 0; i < ns; i++) - { - T tmp = ov[i*stride + offset]; - if (sort_isnan<T> (tmp)) - { - --ku; - buf[ku] = tmp; - bufi[ku] = i; - } - else - { - buf[kl] = tmp; - bufi[kl] = i; - kl++; - } - } - - // sort. - lsort.sort (buf, bufi, kl); - - if (ku < ns) - { - // NaNs are in reverse order - std::reverse (buf + ku, buf + ns); - std::reverse (bufi + ku, bufi + ns); - if (mode == DESCENDING) - { - std::rotate (buf, buf + ku, buf + ns); - std::rotate (bufi, bufi + ku, bufi + ns); - } - } - - // scatter. - for (octave_idx_type i = 0; i < ns; i++) - v[i*stride + offset] = buf[i]; - for (octave_idx_type i = 0; i < ns; i++) - vi[i*stride + offset] = bufi[i]; - } - } - - return m; -} - -template <typename T, typename Alloc> -typename Array<T, Alloc>::compare_fcn_type -safe_comparator (sortmode mode, const Array<T, Alloc>& /* a */, - bool /* allow_chk */) -{ - if (mode == ASCENDING) - return octave_sort<T>::ascending_compare; - else if (mode == DESCENDING) - return octave_sort<T>::descending_compare; - else - return nullptr; -} - -template <typename T, typename Alloc> -sortmode -Array<T, Alloc>::issorted (sortmode mode) const -{ - octave_sort<T> lsort; - - octave_idx_type n = numel (); - - if (n <= 1) - return (mode == UNSORTED) ? ASCENDING : mode; - - if (mode == UNSORTED) - { - // Auto-detect mode. - compare_fcn_type compare - = safe_comparator (ASCENDING, *this, false); - - if (compare (elem (n-1), elem (0))) - mode = DESCENDING; - else - mode = ASCENDING; - } - - lsort.set_compare (safe_comparator (mode, *this, false)); - - if (! lsort.issorted (data (), n)) - mode = UNSORTED; - - return mode; - -} - -template <typename T, typename Alloc> -Array<octave_idx_type> -Array<T, Alloc>::sort_rows_idx (sortmode mode) const -{ - Array<octave_idx_type> idx; - - octave_sort<T> lsort (safe_comparator (mode, *this, true)); - - octave_idx_type r = rows (); - octave_idx_type c = cols (); - - idx = Array<octave_idx_type> (dim_vector (r, 1)); - - lsort.sort_rows (data (), idx.fortran_vec (), r, c); - - return idx; -} - -template <typename T, typename Alloc> -sortmode -Array<T, Alloc>::is_sorted_rows (sortmode mode) const -{ - octave_sort<T> lsort; - - octave_idx_type r = rows (); - octave_idx_type c = cols (); - - if (r <= 1 || c == 0) - return (mode == UNSORTED) ? ASCENDING : mode; - - if (mode == UNSORTED) - { - // Auto-detect mode. - compare_fcn_type compare - = safe_comparator (ASCENDING, *this, false); - - octave_idx_type i; - for (i = 0; i < cols (); i++) - { - T l = elem (0, i); - T u = elem (rows () - 1, i); - if (compare (l, u)) - { - if (mode == DESCENDING) - { - mode = UNSORTED; - break; - } - else - mode = ASCENDING; - } - else if (compare (u, l)) - { - if (mode == ASCENDING) - { - mode = UNSORTED; - break; - } - else - mode = DESCENDING; - } - } - if (mode == UNSORTED && i == cols ()) - mode = ASCENDING; - } - - if (mode != UNSORTED) - { - lsort.set_compare (safe_comparator (mode, *this, false)); - - if (! lsort.is_sorted_rows (data (), r, c)) - mode = UNSORTED; - } - - return mode; - -} - -// Do a binary lookup in a sorted array. -template <typename T, typename Alloc> -octave_idx_type -Array<T, Alloc>::lookup (const T& value, sortmode mode) const -{ - octave_idx_type n = numel (); - octave_sort<T> lsort; - - if (mode == UNSORTED) - { - // auto-detect mode - if (n > 1 && lsort.descending_compare (elem (0), elem (n-1))) - mode = DESCENDING; - else - mode = ASCENDING; - } - - lsort.set_compare (mode); - - return lsort.lookup (data (), n, value); -} - -template <typename T, typename Alloc> -Array<octave_idx_type> -Array<T, Alloc>::lookup (const Array<T, Alloc>& values, sortmode mode) const -{ - octave_idx_type n = numel (); - octave_idx_type nval = values.numel (); - octave_sort<T> lsort; - Array<octave_idx_type> idx (values.dims ()); - - if (mode == UNSORTED) - { - // auto-detect mode - if (n > 1 && lsort.descending_compare (elem (0), elem (n-1))) - mode = DESCENDING; - else - mode = ASCENDING; - } - - lsort.set_compare (mode); - - // This determines the split ratio between the O(M*log2(N)) and O(M+N) - // algorithms. - static const double ratio = 1.0; - sortmode vmode = UNSORTED; - - // Attempt the O(M+N) algorithm if M is large enough. - if (nval > ratio * n / octave::math::log2 (n + 1.0)) - { - vmode = values.issorted (); - // The table must not contain a NaN. - if ((vmode == ASCENDING && sort_isnan<T> (values(nval-1))) - || (vmode == DESCENDING && sort_isnan<T> (values(0)))) - vmode = UNSORTED; - } - - if (vmode != UNSORTED) - lsort.lookup_sorted (data (), n, values.data (), nval, - idx.fortran_vec (), vmode != mode); - else - lsort.lookup (data (), n, values.data (), nval, idx.fortran_vec ()); - - return idx; -} - -template <typename T, typename Alloc> -octave_idx_type -Array<T, Alloc>::nnz (void) const -{ - const T *src = data (); - octave_idx_type nel = numel (); - octave_idx_type retval = 0; - const T zero = T (); - for (octave_idx_type i = 0; i < nel; i++) - if (src[i] != zero) - retval++; - - return retval; -} - -template <typename T, typename Alloc> -Array<octave_idx_type> -Array<T, Alloc>::find (octave_idx_type n, bool backward) const -{ - Array<octave_idx_type> retval; - const T *src = data (); - octave_idx_type nel = numel (); - const T zero = T (); - if (n < 0 || n >= nel) - { - // We want all elements, which means we'll almost surely need - // to resize. So count first, then allocate array of exact size. - octave_idx_type cnt = 0; - for (octave_idx_type i = 0; i < nel; i++) - cnt += src[i] != zero; - - retval.clear (cnt, 1); - octave_idx_type *dest = retval.fortran_vec (); - for (octave_idx_type i = 0; i < nel; i++) - if (src[i] != zero) *dest++ = i; - } - else - { - // We want a fixed max number of elements, usually small. So be - // optimistic, alloc the array in advance, and then resize if - // needed. - retval.clear (n, 1); - if (backward) - { - // Do the search as a series of successive single-element searches. - octave_idx_type k = 0; - octave_idx_type l = nel - 1; - for (; k < n; k++) - { - for (; l >= 0 && src[l] == zero; l--) ; - if (l >= 0) - retval(k) = l--; - else - break; - } - if (k < n) - retval.resize2 (k, 1); - octave_idx_type *rdata = retval.fortran_vec (); - std::reverse (rdata, rdata + k); - } - else - { - // Do the search as a series of successive single-element searches. - octave_idx_type k = 0; - octave_idx_type l = 0; - for (; k < n; k++) - { - for (; l != nel && src[l] == zero; l++) ; - if (l != nel) - retval(k) = l++; - else - break; - } - if (k < n) - retval.resize2 (k, 1); - } - } - - // Fixup return dimensions, for Matlab compatibility. - // find (zeros (0,0)) -> zeros (0,0) - // find (zeros (1,0)) -> zeros (1,0) - // find (zeros (0,1)) -> zeros (0,1) - // find (zeros (0,X)) -> zeros (0,1) - // find (zeros (1,1)) -> zeros (0,0) !!!! WHY? - // find (zeros (0,1,0)) -> zeros (0,0) - // find (zeros (0,1,0,1)) -> zeros (0,0) etc - - if ((numel () == 1 && retval.isempty ()) - || (rows () == 0 && dims ().numel (1) == 0)) - retval.m_dimensions = dim_vector (); - else if (rows () == 1 && ndims () == 2) - retval.m_dimensions = dim_vector (1, retval.numel ()); - - return retval; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::nth_element (const octave::idx_vector& n, int dim) const -{ - if (dim < 0) - (*current_liboctave_error_handler) ("nth_element: invalid dimension"); - - dim_vector dv = dims (); - if (dim >= dv.ndims ()) - dv.resize (dim+1, 1); - - octave_idx_type ns = dv(dim); - - octave_idx_type nn = n.length (ns); - - dv(dim) = std::min (nn, ns); - dv.chop_trailing_singletons (); - dim = std::min (dv.ndims (), static_cast<octave_idx_type> (dim)); - - Array<T, Alloc> m (dv); - - if (m.isempty ()) - return m; - - sortmode mode = UNSORTED; - octave_idx_type lo = 0; - - switch (n.idx_class ()) - { - case octave::idx_vector::class_scalar: - mode = ASCENDING; - lo = n(0); - break; - case octave::idx_vector::class_range: - { - octave_idx_type inc = n.increment (); - if (inc == 1) - { - mode = ASCENDING; - lo = n(0); - } - else if (inc == -1) - { - mode = DESCENDING; - lo = ns - 1 - n(0); - } - } - break; - case octave::idx_vector::class_vector: - // This case resolves bug #51329, a fallback to allow the given index - // to be a sequential vector instead of the typical scalar or range - if (n(1) - n(0) == 1) - { - mode = ASCENDING; - lo = n(0); - } - else if (n(1) - n(0) == -1) - { - mode = DESCENDING; - lo = ns - 1 - n(0); - } - // Ensure that the vector is actually an arithmetic contiguous sequence - for (octave_idx_type i = 2; i < n.length () && mode != UNSORTED; i++) - if ((mode == ASCENDING && n(i) - n(i-1) != 1) - || (mode == DESCENDING && n(i) - n(i-1) != -1)) - mode = UNSORTED; - break; - default: - break; - } - - if (mode == UNSORTED) - (*current_liboctave_error_handler) - ("nth_element: n must be a scalar or a contiguous range"); - - octave_idx_type up = lo + nn; - - if (lo < 0 || up > ns) - (*current_liboctave_error_handler) ("nth_element: invalid element index"); - - octave_idx_type iter = numel () / ns; - octave_idx_type stride = 1; - - for (int i = 0; i < dim; i++) - stride *= dv(i); - - T *v = m.fortran_vec (); - const T *ov = data (); - - OCTAVE_LOCAL_BUFFER (T, buf, ns); - - octave_sort<T> lsort; - lsort.set_compare (mode); - - for (octave_idx_type j = 0; j < iter; j++) - { - octave_idx_type kl = 0; - octave_idx_type ku = ns; - - if (stride == 1) - { - // copy without NaNs. - for (octave_idx_type i = 0; i < ns; i++) - { - T tmp = ov[i]; - if (sort_isnan<T> (tmp)) - buf[--ku] = tmp; - else - buf[kl++] = tmp; - } - - ov += ns; - } - else - { - octave_idx_type offset = j % stride; - // copy without NaNs. - for (octave_idx_type i = 0; i < ns; i++) - { - T tmp = ov[offset + i*stride]; - if (sort_isnan<T> (tmp)) - buf[--ku] = tmp; - else - buf[kl++] = tmp; - } - - if (offset == stride-1) - ov += ns*stride; - } - - if (ku == ns) - lsort.nth_element (buf, ns, lo, up); - else if (mode == ASCENDING) - lsort.nth_element (buf, ku, lo, std::min (ku, up)); - else - { - octave_idx_type nnan = ns - ku; - octave_idx_type zero = 0; - lsort.nth_element (buf, ku, std::max (lo - nnan, zero), - std::max (up - nnan, zero)); - std::rotate (buf, buf + ku, buf + ns); - } - - if (stride == 1) - { - for (octave_idx_type i = 0; i < nn; i++) - v[i] = buf[lo + i]; - - v += nn; - } - else - { - octave_idx_type offset = j % stride; - for (octave_idx_type i = 0; i < nn; i++) - v[offset + stride * i] = buf[lo + i]; - if (offset == stride-1) - v += nn*stride; - } - } - - return m; -} - -#define NO_INSTANTIATE_ARRAY_SORT_API(T, API) \ - template <> API Array<T> \ - Array<T>::sort (int, sortmode) const \ - { \ - return *this; \ - } \ - template <> API Array<T> \ - Array<T>::sort (Array<octave_idx_type> &sidx, int, sortmode) const \ - { \ - sidx = Array<octave_idx_type> (); \ - return *this; \ - } \ - template <> API sortmode \ - Array<T>::issorted (sortmode) const \ - { \ - return UNSORTED; \ - } \ - API Array<T>::compare_fcn_type \ - safe_comparator (sortmode, const Array<T>&, bool) \ - { \ - return nullptr; \ - } \ - template <> API Array<octave_idx_type> \ - Array<T>::sort_rows_idx (sortmode) const \ - { \ - return Array<octave_idx_type> (); \ - } \ - template <> API sortmode \ - Array<T>::is_sorted_rows (sortmode) const \ - { \ - return UNSORTED; \ - } \ - template <> API octave_idx_type \ - Array<T>::lookup (T const &, sortmode) const \ - { \ - return 0; \ - } \ - template <> API Array<octave_idx_type> \ - Array<T>::lookup (const Array<T>&, sortmode) const \ - { \ - return Array<octave_idx_type> (); \ - } \ - template <> API octave_idx_type \ - Array<T>::nnz (void) const \ - { \ - return 0; \ - } \ - template <> API Array<octave_idx_type> \ - Array<T>::find (octave_idx_type, bool) const \ - { \ - return Array<octave_idx_type> (); \ - } \ - template <> API Array<T> \ - Array<T>::nth_element (const octave::idx_vector&, int) const { \ - return Array<T> (); \ - } - -#define NO_INSTANTIATE_ARRAY_SORT(T) NO_INSTANTIATE_ARRAY_SORT_API (T,) - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::diag (octave_idx_type k) const -{ - dim_vector dv = dims (); - octave_idx_type nd = dv.ndims (); - Array<T, Alloc> d; - - if (nd > 2) - (*current_liboctave_error_handler) ("Matrix must be 2-dimensional"); - - octave_idx_type nnr = dv(0); - octave_idx_type nnc = dv(1); - - if (nnr == 0 && nnc == 0) - ; // do nothing for empty matrix - else if (nnr != 1 && nnc != 1) - { - // Extract diag from matrix - if (k > 0) - nnc -= k; - else if (k < 0) - nnr += k; - - if (nnr > 0 && nnc > 0) - { - octave_idx_type ndiag = (nnr < nnc) ? nnr : nnc; - - d.resize (dim_vector (ndiag, 1)); - - if (k > 0) - { - for (octave_idx_type i = 0; i < ndiag; i++) - d.xelem (i) = elem (i, i+k); - } - else if (k < 0) - { - for (octave_idx_type i = 0; i < ndiag; i++) - d.xelem (i) = elem (i-k, i); - } - else - { - for (octave_idx_type i = 0; i < ndiag; i++) - d.xelem (i) = elem (i, i); - } - } - else // Matlab returns [] 0x1 for out-of-range diagonal - d.resize (dim_vector (0, 1)); - } - else - { - // Create diag matrix from vector - octave_idx_type roff = 0; - octave_idx_type coff = 0; - if (k > 0) - { - roff = 0; - coff = k; - } - else if (k < 0) - { - roff = -k; - coff = 0; - } - - if (nnr == 1) - { - octave_idx_type n = nnc + std::abs (k); - d = Array<T, Alloc> (dim_vector (n, n), resize_fill_value ()); - - for (octave_idx_type i = 0; i < nnc; i++) - d.xelem (i+roff, i+coff) = elem (0, i); - } - else - { - octave_idx_type n = nnr + std::abs (k); - d = Array<T, Alloc> (dim_vector (n, n), resize_fill_value ()); - - for (octave_idx_type i = 0; i < nnr; i++) - d.xelem (i+roff, i+coff) = elem (i, 0); - } - } - - return d; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::diag (octave_idx_type m, octave_idx_type n) const -{ - if (ndims () != 2 || (rows () != 1 && cols () != 1)) - (*current_liboctave_error_handler) ("cat: invalid dimension"); - - Array<T, Alloc> retval (dim_vector (m, n), resize_fill_value ()); - - octave_idx_type nel = std::min (numel (), std::min (m, n)); - for (octave_idx_type i = 0; i < nel; i++) - retval.xelem (i, i) = xelem (i); - - return retval; -} - -template <typename T, typename Alloc> -Array<T, Alloc> -Array<T, Alloc>::cat (int dim, octave_idx_type n, const Array<T, Alloc> *array_list) -{ - // Default concatenation. - bool (dim_vector::*concat_rule) (const dim_vector&, int) = &dim_vector::concat; - - if (dim == -1 || dim == -2) - { - concat_rule = &dim_vector::hvcat; - dim = -dim - 1; - } - else if (dim < 0) - (*current_liboctave_error_handler) ("cat: invalid dimension"); - - if (n == 1) - return array_list[0]; - else if (n == 0) - return Array<T, Alloc> (); - - // Special case: - // - // cat (dim, [], ..., [], A, ...) - // - // with dim > 2, A not 0x0, and at least three arguments to - // concatenate is equivalent to - // - // cat (dim, A, ...) - // - // Note that this check must be performed here because for full-on - // braindead Matlab compatibility, we need to have things like - // - // cat (3, [], [], A) - // - // succeed, but to have things like - // - // cat (3, cat (3, [], []), A) - // cat (3, zeros (0, 0, 2), A) - // - // fail. See also bug report #31615. - - octave_idx_type istart = 0; - - if (n > 2 && dim > 1) - { - for (octave_idx_type i = 0; i < n; i++) - { - dim_vector dv = array_list[i].dims (); - - if (dv.zero_by_zero ()) - istart++; - else - break; - } - - // Don't skip any initial arguments if they are all empty. - if (istart >= n) - istart = 0; - } - - dim_vector dv = array_list[istart++].dims (); - - for (octave_idx_type i = istart; i < n; i++) - if (! (dv.*concat_rule) (array_list[i].dims (), dim)) - (*current_liboctave_error_handler) ("cat: dimension mismatch"); - - Array<T, Alloc> retval (dv); - - if (retval.isempty ()) - return retval; - - int nidx = std::max (dv.ndims (), static_cast<octave_idx_type> (dim + 1)); - Array<octave::idx_vector> idxa (dim_vector (nidx, 1), octave::idx_vector::colon); - octave_idx_type l = 0; - - for (octave_idx_type i = 0; i < n; i++) - { - // NOTE: This takes some thinking, but no matter what the above rules - // are, an empty array can always be skipped at this point, because - // the result dimensions are already determined, and there is no way - // an empty array may contribute a nonzero piece along the dimension - // at this point, unless an empty array can be promoted to a non-empty - // one (which makes no sense). I repeat, *no way*, think about it. - if (array_list[i].isempty ()) - continue; - - octave_quit (); - - octave_idx_type u; - if (dim < array_list[i].ndims ()) - u = l + array_list[i].dims ()(dim); - else - u = l + 1; - - idxa(dim) = octave::idx_vector (l, u); - - retval.assign (idxa, array_list[i]); - - l = u; - } - - return retval; -} - -template <typename T, typename Alloc> -void -Array<T, Alloc>::print_info (std::ostream& os, const std::string& prefix) const -{ - os << prefix << "m_rep address: " << m_rep << '\n' - << prefix << "m_rep->m_len: " << m_rep->m_len << '\n' - << prefix << "m_rep->m_data: " << static_cast<void *> (m_rep->m_data) << '\n' - << prefix << "m_rep->m_count: " << m_rep->m_count << '\n' - << prefix << "m_slice_data: " << static_cast<void *> (m_slice_data) << '\n' - << prefix << "m_slice_len: " << m_slice_len << '\n'; - - // 2D info: - // - // << pefix << "rows: " << rows () << "\n" - // << prefix << "cols: " << cols () << "\n"; -} - -template <typename T, typename Alloc> -bool Array<T, Alloc>::optimize_dimensions (const dim_vector& dv) -{ - bool retval = m_dimensions == dv; - if (retval) - m_dimensions = dv; - - return retval; -} - -template <typename T, typename Alloc> -void Array<T, Alloc>::instantiation_guard () -{ - // This guards against accidental implicit instantiations. - // Array<T, Alloc> instances should always be explicit and use INSTANTIATE_ARRAY. - T::__xXxXx__ (); -} - -#if defined (__clang__) -# define INSTANTIATE_ARRAY(T, API) \ - template <> API void \ - Array<T>::instantiation_guard () { } \ - \ - template class API Array<T> -#else -# define INSTANTIATE_ARRAY(T, API) \ - template <> API void \ - Array<T>::instantiation_guard () { } \ - \ - template class Array<T> -#endif - -// FIXME: is this used? - -template <typename T, typename Alloc> -std::ostream& -operator << (std::ostream& os, const Array<T, Alloc>& a) -{ - dim_vector a_dims = a.dims (); - - int n_dims = a_dims.ndims (); - - os << n_dims << "-dimensional array"; - - if (n_dims) - os << " (" << a_dims.str () << ')'; - - os <<"\n\n"; - - if (n_dims) - { - os << "data:"; - - Array<octave_idx_type> ra_idx (dim_vector (n_dims, 1), 0); - - // Number of times the first 2d-array is to be displayed. - - octave_idx_type m = 1; - for (int i = 2; i < n_dims; i++) - m *= a_dims(i); - - if (m == 1) - { - octave_idx_type rows = 0; - octave_idx_type cols = 0; - - switch (n_dims) - { - case 2: - rows = a_dims(0); - cols = a_dims(1); - - for (octave_idx_type j = 0; j < rows; j++) - { - ra_idx(0) = j; - for (octave_idx_type k = 0; k < cols; k++) - { - ra_idx(1) = k; - os << ' ' << a.elem (ra_idx); - } - os << "\n"; - } - break; - - default: - rows = a_dims(0); - - for (octave_idx_type k = 0; k < rows; k++) - { - ra_idx(0) = k; - os << ' ' << a.elem (ra_idx); - } - break; - } - - os << "\n"; - } - else - { - octave_idx_type rows = a_dims(0); - octave_idx_type cols = a_dims(1); - - for (int i = 0; i < m; i++) - { - os << "\n(:,:,"; - - for (int j = 2; j < n_dims - 1; j++) - os << ra_idx(j) + 1 << ','; - - os << ra_idx(n_dims - 1) + 1 << ") = \n"; - - for (octave_idx_type j = 0; j < rows; j++) - { - ra_idx(0) = j; - - for (octave_idx_type k = 0; k < cols; k++) - { - ra_idx(1) = k; - os << ' ' << a.elem (ra_idx); - } - - os << "\n"; - } - - os << "\n"; - - if (i != m - 1) - increment_index (ra_idx, a_dims, 2); - } - } - } - - return os; -}
--- a/liboctave/array/module.mk Sat Nov 05 00:10:11 2022 -0400 +++ b/liboctave/array/module.mk Sat Nov 05 13:48:24 2022 +0100 @@ -121,7 +121,8 @@ %reldir%/uint8NDArray.cc LIBOCTAVE_TEMPLATE_SRC += \ - %reldir%/Array.cc \ + %reldir%/Array-base.cc \ + %reldir%/Array-oct.cc \ %reldir%/DiagArray2.cc \ %reldir%/MArray.cc \ %reldir%/MDiagArray2.cc \