Mercurial > hg > octave-nkf
diff liboctave/Array.cc @ 8290:7cbe01c21986
improve dense array indexing
author | Jaroslav Hajek <highegg@gmail.com> |
---|---|
date | Mon, 20 Oct 2008 16:54:28 +0200 |
parents | 6c08e3921d3e |
children | f2e050b62199 |
line wrap: on
line diff
--- a/liboctave/Array.cc +++ b/liboctave/Array.cc @@ -3,6 +3,7 @@ Copyright (C) 1993, 1994, 1995, 1996, 1997, 2000, 2002, 2003, 2004, 2005, 2006, 2007 John W. Eaton +Copyright (C) 2008 Jaroslav Hajek <highegg@gmail.com> This file is part of Octave. @@ -32,6 +33,7 @@ #include <iostream> #include <sstream> #include <vector> +#include <algorithm> #include <new> #include "Array.h" @@ -44,7 +46,7 @@ template <class T> Array<T>::Array (const Array<T>& a, const dim_vector& dv) - : rep (a.rep), dimensions (dv), idx (0), idx_count (0) + : rep (a.rep), dimensions (dv) { rep->count++; @@ -58,8 +60,6 @@ { if (--rep->count <= 0) delete rep; - - delete [] idx; } template <class T> @@ -75,10 +75,6 @@ rep->count++; dimensions = a.dimensions; - - delete [] idx; - idx_count = 0; - idx = 0; } return *this; @@ -559,458 +555,896 @@ return retval; } -template <class T> -void -Array<T>::resize_no_fill (octave_idx_type n) +// 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 { - if (n < 0) + octave_idx_type *dim, *cdim; + idx_vector *idx; + int top; + +public: + rec_index_helper (const dim_vector& dv, const Array<idx_vector>& ia) { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; + int n = ia.length (); + assert (n > 0 && (dv.length () == std::max (n, 2))); + + dim = new octave_idx_type [2*n]; + // A hack to avoid double allocation + cdim = dim + n; + idx = new idx_vector [n]; + top = 0; + + dim[0] = dv(0); + cdim[0] = 1; + idx[0] = ia(0); + + for (int i = 1; i < n; i++) + { + // Try reduction... + if (idx[top].maybe_reduce (dim[top], ia(i), dv(i))) + { + // Reduction successful, fold dimensions. + dim[top] *= dv(i); + } + else + { + // Unsuccessful, store index & cumulative dim. + top++; + idx[top] = ia(i); + dim[top] = dv(i); + cdim[top] = cdim[top-1] * dim[top-1]; + } + } + } + + ~rec_index_helper (void) { delete [] idx; delete [] dim; } + +private: + + // Recursive N-d indexing + template <class T> + T *do_index (const T *src, T *dest, int lev) const + { + if (lev == 0) + dest += idx[0].index (src, dim[0], dest); + else + { + octave_idx_type n = idx[lev].length (dim[lev]), d = cdim[lev]; + for (octave_idx_type i = 0; i < n; i++) + dest = do_index (src + d*idx[lev].xelem (i), dest, lev-1); + } + + return dest; + } + + // Recursive N-d indexed assignment + template <class T> + const T *do_assign (const T *src, T *dest, int lev) const + { + if (lev == 0) + src += idx[0].assign (src, dim[0], dest); + else + { + octave_idx_type n = idx[lev].length (dim[lev]), d = cdim[lev]; + for (octave_idx_type i = 0; i < n; i++) + src = do_assign (src, dest + d*idx[lev].xelem (i), lev-1); + } + + return src; } - if (n == length ()) - return; - - typename Array<T>::ArrayRep *old_rep = rep; - const T *old_data = data (); - octave_idx_type old_len = length (); - - rep = new typename Array<T>::ArrayRep (n); - - dimensions = dim_vector (n); - - if (n > 0 && old_data && old_len > 0) + // Recursive N-d indexed assignment + template <class T> + void do_fill (const T& val, T *dest, int lev) const { - octave_idx_type min_len = old_len < n ? old_len : n; - - for (octave_idx_type i = 0; i < min_len; i++) - xelem (i) = old_data[i]; + if (lev == 0) + idx[0].fill (val, dim[0], dest); + else + { + octave_idx_type n = idx[lev].length (dim[lev]), d = cdim[lev]; + for (octave_idx_type i = 0; i < n; i++) + do_fill (val, dest + d*idx[lev].xelem (i), lev-1); + } } - if (--old_rep->count <= 0) - delete old_rep; +public: + + template <class T> + void index (const T *src, T *dest) const { do_index (src, dest, top); } + + template <class T> + void assign (const T *src, T *dest) const { do_assign (src, dest, top); } + + template <class T> + void fill (const T& val, T *dest) const { do_fill (val, dest, top); } + +}; + +// 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 +{ + octave_idx_type *cext, *sext, *dext; + int n; + +public: + rec_resize_helper (const dim_vector& ndv, const dim_vector& odv) + { + int l = ndv.length (); + assert (odv.length () == l); + octave_idx_type ld = 1; + int i = 0; + for (; i < l-1 && ndv(i) == odv(i); i++) ld *= ndv(i); + n = l - i; + cext = new octave_idx_type[3*n]; + // Trick to avoid three allocations + sext = cext + n; + dext = sext + n; + + octave_idx_type sld = ld, dld = ld; + for (int j = 0; j < n; j++) + { + cext[j] = std::min (ndv(i+j), odv(i+j)); + sext[j] = sld *= odv(i+j); + dext[j] = dld *= ndv(i+j); + } + cext[0] *= ld; + } + + ~rec_resize_helper (void) { delete [] cext; } + +private: + // recursive resizing + template <class T> + void do_resize_fill (const T* src, T *dest, const T& rfv, int lev) const + { + if (lev == 0) + { + T* destc = std::copy (src, src + cext[0], dest); + std::fill (destc, dest + dext[0], rfv); + } + else + { + octave_idx_type sd = sext[lev-1], dd = dext[lev-1], k; + for (k = 0; k < cext[lev]; k++) + do_resize_fill (src + k * sd, dest + k * dd, rfv, lev - 1); + + std::fill (dest + k * dd, dest + dext[lev], rfv); + } + } +public: + template <class T> + void resize_fill (const T* src, T *dest, const T& rfv) const + { do_resize_fill (src, dest, rfv, n-1); } + +}; + +static void gripe_index_out_of_range (void) +{ + (*current_liboctave_error_handler) + ("A(I): Index exceeds matrix dimension."); } template <class T> -void -Array<T>::resize_no_fill (const dim_vector& dv) +Array<T> +Array<T>::index (const idx_vector& i) const { - octave_idx_type n = dv.length (); + octave_idx_type n = numel (); + Array<T> retval; - for (octave_idx_type i = 0; i < n; i++) + if (i.is_colon ()) { - if (dv(i) < 0) - { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; - } + // A(:) produces a shallow copy as a column vector. + retval.dimensions = dim_vector (n, 1); + rep->count++; + retval.rep = rep; } - - bool same_size = true; - - if (dimensions.length () != n) + else if (i.extent (n) != n) { - same_size = false; + gripe_index_out_of_range (); } else { - for (octave_idx_type i = 0; i < n; i++) - { - if (dv(i) != dimensions(i)) - { - same_size = false; - break; - } - } + // FIXME: This is the only place where orig_dimensions are used. + dim_vector rd = i.orig_dimensions (); + octave_idx_type il = i.length (n); + + // FIXME: This is for Matlab compatibility. Matlab 2007 given `b = ones(3,1)' + // yields the following: + // b(zeros(0,0)) gives [] + // b(zeros(1,0)) gives zeros(0,1) + // b(zeros(0,1)) gives zeros(0,1) + // b(zeros(0,m)) gives zeros(0,m) + // b(zeros(m,0)) gives zeros(m,0) + // b(1:2) gives ones(2,1) + // b(ones(2)) gives ones(2) etc. + // As you can see, the behaviour is weird, but the tests end up pretty + // simple. Nah, I don't want to suggest that this is ad hoc :) Neither do + // I want to say that Matlab is a lousy piece of s...oftware. + if (ndims () == 2 && n != 1) + { + if (columns () == 1 && rd(0) == 1) + rd = dim_vector (il, 1); + else if (rows () == 1 && rd(1) == 1) + rd = dim_vector (1, il); + } + + // Don't use resize here to avoid useless initialization for POD types. + retval = Array<T> (rd); + + if (il != 0) + i.index (data (), n, retval.fortran_vec ()); + } + + return retval; +} + +template <class T> +Array<T> +Array<T>::index (const idx_vector& i, const idx_vector& j) const +{ + // Get dimensions, allowing Fortran indexing in the 2nd dim. + dim_vector dv = dimensions.redim (2); + octave_idx_type r = dv(0), c = dv(1); + Array<T> retval; + + if (i.is_colon () && j.is_colon ()) + { + // A(:,:) produces a shallow copy. + retval = Array<T> (*this, dv); + } + else if (i.extent (r) != r || j.extent (c) != c) + { + gripe_index_out_of_range (); + } + else + { + octave_idx_type n = numel (), il = i.length (r), jl = j.length (c); + + // Don't use resize here to avoid useless initialization for POD types. + retval = Array<T> (dim_vector (il, jl)); + + idx_vector ii (i); + + const T* src = data (); + T *dest = retval.fortran_vec (); + + if (ii.maybe_reduce (r, j, c)) + ii.index (src, n, dest); + else + { + for (octave_idx_type k = 0; k < jl; k++) + dest += i.index (src + r * j.xelem (k), r, dest); + } } - if (same_size) - return; - - typename Array<T>::ArrayRep *old_rep = rep; - const T *old_data = data (); - - octave_idx_type ts = get_size (dv); + return retval; +} - rep = new typename Array<T>::ArrayRep (ts); +template <class T> +Array<T> +Array<T>::index (const Array<idx_vector>& ia) const +{ + int ial = ia.length (); + Array<T> retval; - dim_vector dv_old = dimensions; - octave_idx_type dv_old_orig_len = dv_old.length (); - dimensions = dv; - octave_idx_type ts_old = get_size (dv_old); - - if (ts > 0 && ts_old > 0 && dv_old_orig_len > 0) + // 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) { - Array<octave_idx_type> ra_idx (dimensions.length (), 0); + // Get dimensions, allowing Fortran indexing in the last dim. + dim_vector dv = dimensions.redim (ial); - if (n > dv_old_orig_len) - { - dv_old.resize (n); + // Check for out of bounds conditions. + bool all_colons = true, mismatch = false; + for (int i = 0; i < ial; i++) + { + if (ia(i).extent (dv(i)) != dv(i)) + { + mismatch = true; + break; + } + else + all_colons = all_colons && ia(i).is_colon (); + } + - for (octave_idx_type i = dv_old_orig_len; i < n; i++) - dv_old.elem (i) = 1; - } + if (mismatch) + { + gripe_index_out_of_range (); + } + else if (all_colons) + { + // A(:,:,...,:) produces a shallow copy. + retval = Array<T> (*this, dv); + } + else + { + // Form result dimensions. + dim_vector rdv; + rdv.resize (ial); + for (int i = 0; i < ial; i++) rdv(i) = ia(i).length (dv(i)); + rdv.chop_trailing_singletons (); - for (octave_idx_type i = 0; i < ts; i++) - { - if (index_in_bounds (ra_idx, dv_old)) - rep->elem (i) = old_data[get_scalar_idx (ra_idx, dv_old)]; + // Don't use resize here to avoid useless initialization for POD types. + retval = Array<T> (rdv); - increment_index (ra_idx, dimensions); - } + // Prepare for recursive indexing + rec_index_helper rh (dv, ia); + + // Do it. + rh.index (data (), retval.fortran_vec ()); + } } - if (--old_rep->count <= 0) - delete old_rep; + return retval; } +// FIXME: the following is a common error message to resize, regardless of whether it's +// called from assign or elsewhere. It seems OK to me, but eventually the gripe can be +// specialized. Anyway, propagating various error messages into procedure is, IMHO, a +// nonsense. If anything, we should change error handling here (and throughout liboctave) +// to allow custom handling of errors +static void gripe_invalid_resize (void) +{ + (*current_liboctave_error_handler) + ("resize: Invalid resizing operation or ambiguous assignment to an out-of-bounds array element."); +} + +// The default fill value. Override if you want a different one. + +template <class T> +T Array<T>::resize_fill_value () +{ + return T (); +} + +// Yes, we could do resize using index & assign. However, that would possibly +// involve a lot more memory traffic than we actually need. + template <class T> void -Array<T>::resize_no_fill (octave_idx_type r, octave_idx_type c) +Array<T>::resize_fill (octave_idx_type n, const T& rfv) { - if (r < 0 || c < 0) + if (n >= 0 && ndims () == 2) { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; - } - - int n = ndims (); - - if (n == 0) - dimensions = dim_vector (0, 0); - - assert (ndims () == 2); - - if (r == dim1 () && c == dim2 ()) - return; - - typename Array<T>::ArrayRep *old_rep = Array<T>::rep; - const T *old_data = data (); - - octave_idx_type old_d1 = dim1 (); - octave_idx_type old_d2 = dim2 (); - octave_idx_type old_len = length (); - - octave_idx_type ts = get_size (r, c); - - rep = new typename Array<T>::ArrayRep (ts); - - dimensions = dim_vector (r, c); - - if (ts > 0 && old_data && old_len > 0) - { - octave_idx_type min_r = old_d1 < r ? old_d1 : r; - octave_idx_type min_c = old_d2 < c ? old_d2 : c; - - for (octave_idx_type j = 0; j < min_c; j++) - for (octave_idx_type i = 0; i < min_r; i++) - xelem (i, j) = old_data[old_d1*j+i]; - } - - if (--old_rep->count <= 0) - delete old_rep; -} + dim_vector dv; + // This is driven by Matlab's behaviour 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), but hey, Matlab is not here to + // make sense, it's here to make money ;) + 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) + gripe_invalid_resize (); + else + { + octave_idx_type nx = numel (); + if (n != nx) + { + Array<T> tmp = Array<T> (dv); + T *dest = tmp.fortran_vec (); -template <class T> -void -Array<T>::resize_no_fill (octave_idx_type r, octave_idx_type c, octave_idx_type p) -{ - if (r < 0 || c < 0 || p < 0) - { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; - } - - int n = ndims (); - - if (n == 0) - dimensions = dim_vector (0, 0, 0); - - assert (ndims () == 3); - - if (r == dim1 () && c == dim2 () && p == dim3 ()) - return; - - typename Array<T>::ArrayRep *old_rep = rep; - const T *old_data = data (); + octave_idx_type n0 = std::min (n, nx), n1 = n - n0; + dest = std::copy (data (), data () + n0, dest); + std::fill (dest, dest + n1, rfv); - octave_idx_type old_d1 = dim1 (); - octave_idx_type old_d2 = dim2 (); - octave_idx_type old_d3 = dim3 (); - octave_idx_type old_len = length (); - - octave_idx_type ts = get_size (get_size (r, c), p); - - rep = new typename Array<T>::ArrayRep (ts); - - dimensions = dim_vector (r, c, p); - - if (ts > 0 && old_data && old_len > 0) - { - octave_idx_type min_r = old_d1 < r ? old_d1 : r; - octave_idx_type min_c = old_d2 < c ? old_d2 : c; - octave_idx_type min_p = old_d3 < p ? old_d3 : p; - - for (octave_idx_type k = 0; k < min_p; k++) - for (octave_idx_type j = 0; j < min_c; j++) - for (octave_idx_type i = 0; i < min_r; i++) - xelem (i, j, k) = old_data[old_d1*(old_d2*k+j)+i]; + *this = tmp; + } + } } - - if (--old_rep->count <= 0) - delete old_rep; + else + gripe_invalid_resize (); } template <class T> void -Array<T>::resize_and_fill (octave_idx_type n, const T& val) +Array<T>::resize_fill (octave_idx_type r, octave_idx_type c, const T& rfv) { - if (n < 0) + if (r >= 0 && c >= 0 && ndims () == 2) { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; + octave_idx_type rx = rows (), cx = columns (); + if (r != rx || c != cx) + { + Array<T> tmp = Array<T> (dim_vector (r, c)); + T *dest = tmp.fortran_vec (); + + octave_idx_type r0 = std::min (r, rx), r1 = r - r0; + octave_idx_type c0 = std::min (c, cx), c1 = c - c0; + const T *src = data (); + if (r == rx) + dest = std::copy (src, src + r * c0, dest); + else + { + for (octave_idx_type k = 0; k < c0; k++) + { + dest = std::copy (src, src + r0, dest); + src += rx; + std::fill (dest, dest + r1, rfv); + dest += r1; + } + } + + std::fill (dest, dest + r * c1, rfv); + + *this = tmp; + } + } + else + gripe_invalid_resize (); + +} + +template<class T> +void +Array<T>::resize_fill (const dim_vector& dv, const T& rfv) +{ + int dvl = dv.length (); + if (dvl == 1) + resize (dv(0), rfv); + else if (dvl == 2) + resize (dv(0), dv(1), rfv); + else if (dimensions != dv) + { + if (dimensions.length () <= dvl) + { + Array<T> tmp (dv); + // Prepare for recursive resizing. + rec_resize_helper rh (dv, dimensions.redim (dvl)); + + // Do it. + rh.resize_fill (data (), tmp.fortran_vec (), rfv); + *this = tmp; + } + else + gripe_invalid_resize (); + } +} + +template <class T> +Array<T> +Array<T>::index (const idx_vector& i, bool resize_ok, const T& rfv) const +{ + Array<T> tmp = *this; + if (resize_ok) + { + octave_idx_type n = numel (), nx = i.extent (n); + if (n != nx) + tmp.resize_fill (nx, rfv); + + if (tmp.numel () != nx) + return Array<T> (); } - if (n == length ()) - return; - - typename Array<T>::ArrayRep *old_rep = rep; - const T *old_data = data (); - octave_idx_type old_len = length (); - - rep = new typename Array<T>::ArrayRep (n); - - dimensions = dim_vector (n); + return tmp.index (i); +} - if (n > 0) +template <class T> +Array<T> +Array<T>::index (const idx_vector& i, const idx_vector& j, + bool resize_ok, const T& rfv) const +{ + Array<T> tmp = *this; + if (resize_ok) { - octave_idx_type min_len = old_len < n ? old_len : n; + dim_vector dv = dimensions.redim (2); + octave_idx_type r = dv(0), c = dv(1); + octave_idx_type rx = i.extent (r), cx = j.extent (c); + if (r != rx || c != cx) + tmp.resize_fill (rx, cx, rfv); - if (old_data && old_len > 0) - { - for (octave_idx_type i = 0; i < min_len; i++) - xelem (i) = old_data[i]; - } - - for (octave_idx_type i = old_len; i < n; i++) - xelem (i) = val; + if (tmp.rows () != rx || tmp.columns () != cx) + return Array<T> (); } - if (--old_rep->count <= 0) - delete old_rep; + return tmp.index (i, j); +} + +template <class T> +Array<T> +Array<T>::index (const Array<idx_vector>& ia, + bool resize_ok, const T& rfv) const +{ + Array<T> tmp = *this; + if (resize_ok) + { + int ial = ia.length (); + dim_vector dv = dimensions.redim (ial); + dim_vector dvx; dvx.resize (ial); + for (int i = 0; i < ial; i++) dvx(i) = ia(i).extent (dv (i)); + if (! (dvx == dv)) + tmp.resize_fill (dvx, rfv); + + if (tmp.dimensions != dvx) + return Array<T> (); + } + + return tmp.index (ia); +} + + +static void +gripe_invalid_assignment_size (void) +{ + (*current_liboctave_error_handler) + ("A(I) = X: X must have the same size as I"); +} + +static void +gripe_assignment_dimension_mismatch (void) +{ + (*current_liboctave_error_handler) + ("A(I,J,...) = X: dimensions mismatch"); } template <class T> void -Array<T>::resize_and_fill (octave_idx_type r, octave_idx_type c, const T& val) +Array<T>::assign (const idx_vector& i, const Array<T>& rhs, const T& rfv) { - if (r < 0 || c < 0) - { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; - } - - if (ndims () == 0) - dimensions = dim_vector (0, 0); + octave_idx_type n = numel (), rhl = rhs.numel (); - assert (ndims () == 2); - - if (r == dim1 () && c == dim2 ()) - return; - - typename Array<T>::ArrayRep *old_rep = Array<T>::rep; - const T *old_data = data (); - - octave_idx_type old_d1 = dim1 (); - octave_idx_type old_d2 = dim2 (); - octave_idx_type old_len = length (); - - octave_idx_type ts = get_size (r, c); + if (rhl == 1 || i.length (n) == rhl) + { + octave_idx_type nx = i.extent (n); + // Try to resize first if necessary. + if (nx != n) + { + resize_fill (nx, rfv); + n = numel (); + } - rep = new typename Array<T>::ArrayRep (ts); - - dimensions = dim_vector (r, c); - - if (ts > 0) - { - octave_idx_type min_r = old_d1 < r ? old_d1 : r; - octave_idx_type min_c = old_d2 < c ? old_d2 : c; - - if (old_data && old_len > 0) - { - for (octave_idx_type j = 0; j < min_c; j++) - for (octave_idx_type i = 0; i < min_r; i++) - xelem (i, j) = old_data[old_d1*j+i]; - } - - for (octave_idx_type j = 0; j < min_c; j++) - for (octave_idx_type i = min_r; i < r; i++) - xelem (i, j) = val; - - for (octave_idx_type j = min_c; j < c; j++) - for (octave_idx_type i = 0; i < r; i++) - xelem (i, j) = val; + // If the resizing was ambiguous, resize has already griped. + if (nx == n) + { + if (i.is_colon ()) + { + // A(:) = X makes a full fill or a shallow copy. + if (rhl == 1) + fill (rhs(0)); + else + *this = rhs.reshape (dimensions); + } + else + { + if (rhl == 1) + i.fill (rhs(0), n, fortran_vec ()); + else + i.assign (rhs.data (), n, fortran_vec ()); + } + } } - - if (--old_rep->count <= 0) - delete old_rep; + else + gripe_invalid_assignment_size (); } template <class T> void -Array<T>::resize_and_fill (octave_idx_type r, octave_idx_type c, octave_idx_type p, const T& val) +Array<T>::assign (const idx_vector& i, const idx_vector& j, + const Array<T>& rhs, const T& rfv) { - if (r < 0 || c < 0 || p < 0) + // Get RHS extents, discarding singletons. + dim_vector rhdv = rhs.dims (); + // Get LHS extents, allowing Fortran indexing in the second dim. + dim_vector dv = dimensions.redim (2); + // Check for out-of-bounds and form resizing 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 (dv.all_zero ()) + rdv = zero_dims_inquire (i, j, rhdv); + else { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; + rdv(0) = i.extent (dv(0)); + rdv(1) = j.extent (dv(1)); } - if (ndims () == 0) - dimensions = dim_vector (0, 0, 0); - - assert (ndims () == 3); + bool isfill = rhs.numel () == 1; + octave_idx_type il = i.length (rdv(0)), jl = j.length (rdv(1)); + rhdv.chop_all_singletons (); + bool match = isfill || (rhdv.length () == 2 && il == rhdv(0) && jl == rhdv(1)); + match = match || (il == 1 && jl == rhdv(0) && rhdv(1) == 1); - if (r == dim1 () && c == dim2 () && p == dim3 ()) - return; - - typename Array<T>::ArrayRep *old_rep = rep; - const T *old_data = data (); + if (match) + { + // Resize if requested. + if (rdv != dv) + { + resize (rdv, rfv); + dv = dimensions; + } - octave_idx_type old_d1 = dim1 (); - octave_idx_type old_d2 = dim2 (); - octave_idx_type old_d3 = dim3 (); - - octave_idx_type old_len = length (); - - octave_idx_type ts = get_size (get_size (r, c), p); - - rep = new typename Array<T>::ArrayRep (ts); - - dimensions = dim_vector (r, c, p); - - if (ts > 0) - { - octave_idx_type min_r = old_d1 < r ? old_d1 : r; - octave_idx_type min_c = old_d2 < c ? old_d2 : c; - octave_idx_type min_p = old_d3 < p ? old_d3 : p; + // If the resizing was invalid, resize has already griped. + if (dv == rdv) + { + if (i.is_colon () && j.is_colon ()) + { + // A(:,:) = X makes a full fill or a shallow copy + if (isfill) + fill (rhs(0)); + else + *this = rhs.reshape (dimensions); + } + else + { + // The actual work. + octave_idx_type n = numel (), r = rows (), c = columns (); + idx_vector ii (i); - if (old_data && old_len > 0) - for (octave_idx_type k = 0; k < min_p; k++) - for (octave_idx_type j = 0; j < min_c; j++) - for (octave_idx_type i = 0; i < min_r; i++) - xelem (i, j, k) = old_data[old_d1*(old_d2*k+j)+i]; - - // FIXME -- if the copy constructor is expensive, this - // may win. Otherwise, it may make more sense to just copy the - // value everywhere when making the new ArrayRep. + const T* src = rhs.data (); + T *dest = fortran_vec (); - for (octave_idx_type k = 0; k < min_p; k++) - for (octave_idx_type j = min_c; j < c; j++) - for (octave_idx_type i = 0; i < min_r; i++) - xelem (i, j, k) = val; + // 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)); + } + } + } + + } - for (octave_idx_type k = 0; k < min_p; k++) - for (octave_idx_type j = 0; j < c; j++) - for (octave_idx_type i = min_r; i < r; i++) - xelem (i, j, k) = val; - - for (octave_idx_type k = min_p; k < p; k++) - for (octave_idx_type j = 0; j < c; j++) - for (octave_idx_type i = 0; i < r; i++) - xelem (i, j, k) = val; } - - if (--old_rep->count <= 0) - delete old_rep; + else + gripe_assignment_dimension_mismatch (); } template <class T> void -Array<T>::resize_and_fill (const dim_vector& dv, const T& val) +Array<T>::assign (const Array<idx_vector>& ia, + const Array<T>& rhs, const T& rfv) { - octave_idx_type n = dv.length (); + int ial = ia.length (); - for (octave_idx_type i = 0; i < n; i++) + // 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) { - if (dv(i) < 0) - { - (*current_liboctave_error_handler) - ("can't resize to negative dimension"); - return; - } - } + // Get RHS extents, discarding singletons. + dim_vector rhdv = rhs.dims (); + + // Get LHS extents, allowing Fortran indexing in the second dim. + dim_vector dv = 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 (dv.all_zero ()) + rdv = zero_dims_inquire (ia, rhdv); + else + { + rdv.resize (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, all_colons = true, isfill = rhs.numel () == 1; + + rhdv.chop_all_singletons (); + int j = 0, rhdvl = rhdv.length (); + for (int i = 0; i < ial; i++) + { + all_colons = all_colons && ia(i).is_colon (); + 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) + { + resize_fill (rdv, rfv); + dv = dimensions; + chop_trailing_singletons (); + } - bool same_size = true; + // If the resizing was invalid, resize has already griped. + if (dv == rdv) + { + if (all_colons) + { + // A(:,:,...,:) = X makes a full fill or a shallow copy. + if (isfill) + fill (rhs(0)); + else + *this = rhs.reshape (dimensions); + } + else + { + // Do the actual work. + + // Prepare for recursive indexing + rec_index_helper rh (dv, ia); - if (dimensions.length () != n) - { - same_size = false; + // Do it. + if (isfill) + rh.fill (rhs(0), fortran_vec ()); + else + rh.assign (rhs.data (), fortran_vec ()); + } + } + } + else + gripe_assignment_dimension_mismatch (); } - else +} + +template <class T> +void +Array<T>::delete_elements (const idx_vector& i) +{ + octave_idx_type n = numel (); + if (i.is_colon ()) + { + *this = Array<T> (); + } + else if (i.extent (n) != n) { - for (octave_idx_type i = 0; i < n; i++) - { - if (dv(i) != dimensions(i)) - { - same_size = false; - break; - } - } + gripe_index_out_of_range (); + } + else if (i.length (n) != 0) + { + octave_idx_type l, u; + + bool col_vec = ndims () == 2 && columns () == 1 && rows () != 1; + if (i.is_cont_range (n, l, u)) + { + // Special case deleting a contiguous range. + octave_idx_type m = n + l - u; + Array<T> tmp (dim_vector (col_vec ? m : 1, !col_vec ? m : 1)); + const T *src = data (); + T *dest = tmp.fortran_vec (); + dest = std::copy (src, src + l, dest); + dest = std::copy (src + u, src + n, dest); + *this = tmp; + } + else + { + // Use index. + *this = index (i.complement (n)); + } + } +} + +template <class T> +void +Array<T>::delete_elements (int dim, const idx_vector& i) +{ + if (dim > ndims ()) + { + (*current_liboctave_error_handler) + ("invalid dimension in delete_elements"); + return; } - if (same_size) - return; - - typename Array<T>::ArrayRep *old_rep = rep; - const T *old_data = data (); - - octave_idx_type len = get_size (dv); - - rep = new typename Array<T>::ArrayRep (len); + octave_idx_type n = dimensions (dim); + if (i.is_colon ()) + { + *this = Array<T> (); + } + else if (i.extent (n) != n) + { + gripe_index_out_of_range (); + } + else if (i.length (n) != 0) + { + octave_idx_type l, u; - dim_vector dv_old = dimensions; - octave_idx_type dv_old_orig_len = dv_old.length (); - dimensions = dv; + if (i.is_cont_range (n, l, u)) + { + // Special case deleting a contiguous range. + octave_idx_type nd = n + l - u, dl = 1, du = 1; + dim_vector rdv = dimensions; + rdv(dim) = nd; + for (int k = 0; k < dim; k++) dl *= dimensions(k); + for (int k = dim + 1; k < ndims (); k++) du *= dimensions(k); - if (len > 0 && dv_old_orig_len > 0) - { - Array<octave_idx_type> ra_idx (dimensions.length (), 0); - - if (n > dv_old_orig_len) - { - dv_old.resize (n); + // Special case deleting a contiguous range. + Array<T> tmp = Array<T> (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++) + { + dest = std::copy (src, src + l, dest); + dest = std::copy (src + u, src + n, dest); + src += n; + } - for (octave_idx_type i = dv_old_orig_len; i < n; i++) - dv_old.elem (i) = 1; - } + *this = tmp; + } + else + { + // Use index. + Array<idx_vector> ia (ndims (), idx_vector::colon); + ia (dim) = i.complement (n); + *this = index (ia); + } + } +} - for (octave_idx_type i = 0; i < len; i++) - { - if (index_in_bounds (ra_idx, dv_old)) - rep->elem (i) = old_data[get_scalar_idx (ra_idx, dv_old)]; - else - rep->elem (i) = val; - - increment_index (ra_idx, dimensions); - } +template <class T> +void +Array<T>::delete_elements (const Array<idx_vector>& ia) +{ + if (ia.length () == 1) + delete_elements (ia(0)); + else + { + int len = ia.length (), k, dim = -1; + for (k = 0; k < len; k++) + { + if (! ia(k).is_colon ()) + { + if (dim < 0) + dim = k; + else + break; + } + } + if (dim < 0) + { + dim_vector dv = dimensions; + dv(0) = 0; + *this = Array<T> (dv); + } + else if (k == len) + { + delete_elements (dim, ia(dim)); + } + else + { + (*current_liboctave_error_handler) + ("A null assignment can only have one non-colon index."); + } } - else - for (octave_idx_type i = 0; i < len; i++) - rep->elem (i) = val; - if (--old_rep->count <= 0) - delete old_rep; } +// FIXME: Remove these methods or implement them using assign. + template <class T> Array<T>& Array<T>::insert (const Array<T>& a, octave_idx_type r, octave_idx_type c) @@ -1194,6 +1628,7 @@ return *this; } + template <class T> Array<T> Array<T>::transpose (void) const @@ -1406,1178 +1841,6 @@ } template <class T> -void -Array<T>::clear_index (void) const -{ - delete [] idx; - idx = 0; - idx_count = 0; -} - -template <class T> -void -Array<T>::set_index (const idx_vector& idx_arg) const -{ - int nd = ndims (); - - if (! idx && nd > 0) - idx = new idx_vector [nd]; - - if (idx_count < nd) - { - idx[idx_count++] = idx_arg; - } - else - { - idx_vector *new_idx = new idx_vector [idx_count+1]; - - for (int i = 0; i < idx_count; i++) - new_idx[i] = idx[i]; - - new_idx[idx_count++] = idx_arg; - - delete [] idx; - - idx = new_idx; - } -} - -template <class T> -void -Array<T>::maybe_delete_elements (idx_vector& idx_arg) -{ - switch (ndims ()) - { - case 1: - maybe_delete_elements_1 (idx_arg); - break; - - case 2: - maybe_delete_elements_2 (idx_arg); - break; - - default: - (*current_liboctave_error_handler) - ("Array<T>::maybe_delete_elements: invalid operation"); - break; - } -} - -template <class T> -void -Array<T>::maybe_delete_elements_1 (idx_vector& idx_arg) -{ - octave_idx_type len = length (); - - if (len == 0) - return; - - if (idx_arg.is_colon_equiv (len, 1)) - resize_no_fill (0); - else - { - int num_to_delete = idx_arg.length (len); - - if (num_to_delete != 0) - { - octave_idx_type new_len = len; - - octave_idx_type iidx = 0; - - for (octave_idx_type i = 0; i < len; i++) - if (i == idx_arg.elem (iidx)) - { - iidx++; - new_len--; - - if (iidx == num_to_delete) - break; - } - - if (new_len > 0) - { - T *new_data = new T [new_len]; - - octave_idx_type ii = 0; - iidx = 0; - for (octave_idx_type i = 0; i < len; i++) - { - if (iidx < num_to_delete && i == idx_arg.elem (iidx)) - iidx++; - else - { - new_data[ii] = xelem (i); - ii++; - } - } - - if (--rep->count <= 0) - delete rep; - - rep = new typename Array<T>::ArrayRep (new_data, new_len); - - dimensions.resize (1); - dimensions(0) = new_len; - } - else - (*current_liboctave_error_handler) - ("A(idx) = []: index out of range"); - } - } -} - -template <class T> -void -Array<T>::maybe_delete_elements_2 (idx_vector& idx_arg) -{ - assert (ndims () == 2); - - octave_idx_type nr = dim1 (); - octave_idx_type nc = dim2 (); - - if (idx_arg.is_colon ()) - { - // A(:) = [] always gives 0-by-0 matrix, even if A was empty. - resize_no_fill (0, 0); - return; - } - - octave_idx_type n; - if (nr == 1) - n = nc; - else if (nc == 1) - n = nr; - else if (! idx_arg.orig_empty ()) - { - // Reshape to row vector for Matlab compatibility. - - n = nr * nc; - nr = 1; - nc = n; - } - else - return; - - idx_arg.sort (true); - - if (idx_arg.is_colon_equiv (n, 1)) - { - if (nr == 1) - resize_no_fill (1, 0); - else if (nc == 1) - resize_no_fill (0, 1); - return; - } - - octave_idx_type num_to_delete = idx_arg.length (n); - - if (num_to_delete != 0) - { - octave_idx_type new_n = n; - - octave_idx_type iidx = 0; - - for (octave_idx_type i = 0; i < n; i++) - if (i == idx_arg.elem (iidx)) - { - iidx++; - new_n--; - - if (iidx == num_to_delete) - break; - } - - if (new_n > 0) - { - T *new_data = new T [new_n]; - - octave_idx_type ii = 0; - iidx = 0; - for (octave_idx_type i = 0; i < n; i++) - { - if (iidx < num_to_delete && i == idx_arg.elem (iidx)) - iidx++; - else - { - new_data[ii] = xelem (i); - - ii++; - } - } - - if (--(Array<T>::rep)->count <= 0) - delete Array<T>::rep; - - Array<T>::rep = new typename Array<T>::ArrayRep (new_data, new_n); - - dimensions.resize (2); - - if (nr == 1) - { - dimensions(0) = 1; - dimensions(1) = new_n; - } - else - { - dimensions(0) = new_n; - dimensions(1) = 1; - } - } - else - (*current_liboctave_error_handler) - ("A(idx) = []: index out of range"); - } -} - -template <class T> -void -Array<T>::maybe_delete_elements (idx_vector& idx_i, idx_vector& idx_j) -{ - assert (ndims () == 2); - - octave_idx_type nr = dim1 (); - octave_idx_type nc = dim2 (); - - if (idx_i.is_colon () && idx_j.is_colon ()) - { - // A special case: A(:,:). Matlab gives 0-by-nc here, but perhaps we - // should not? - resize_no_fill (0, nc); - } - else if (idx_i.is_colon ()) - { - idx_j.sort (true); // sort in advance to speed-up the following check - - if (idx_j.is_colon_equiv (nc, 1)) - resize_no_fill (nr, 0); - else - { - octave_idx_type num_to_delete = idx_j.length (nc); - - if (num_to_delete != 0) - { - octave_idx_type new_nc = nc; - - octave_idx_type iidx = 0; - - for (octave_idx_type j = 0; j < nc; j++) - if (j == idx_j.elem (iidx)) - { - iidx++; - new_nc--; - - if (iidx == num_to_delete) - break; - } - - if (new_nc > 0) - { - T *new_data = new T [nr * new_nc]; - - octave_idx_type jj = 0; - iidx = 0; - for (octave_idx_type j = 0; j < nc; j++) - { - if (iidx < num_to_delete && j == idx_j.elem (iidx)) - iidx++; - else - { - for (octave_idx_type i = 0; i < nr; i++) - new_data[nr*jj+i] = xelem (i, j); - jj++; - } - } - - if (--(Array<T>::rep)->count <= 0) - delete Array<T>::rep; - - Array<T>::rep = new typename Array<T>::ArrayRep (new_data, nr * new_nc); - - dimensions.resize (2); - dimensions(1) = new_nc; - } - else - (*current_liboctave_error_handler) - ("A(idx) = []: index out of range"); - } - } - } - else if (idx_j.is_colon ()) - { - idx_i.sort (true); // sort in advance to speed-up the following check - - if (idx_i.is_colon_equiv (nr, 1)) - resize_no_fill (0, nc); - else - { - octave_idx_type num_to_delete = idx_i.length (nr); - - if (num_to_delete != 0) - { - octave_idx_type new_nr = nr; - - octave_idx_type iidx = 0; - - for (octave_idx_type i = 0; i < nr; i++) - if (i == idx_i.elem (iidx)) - { - iidx++; - new_nr--; - - if (iidx == num_to_delete) - break; - } - - if (new_nr > 0) - { - T *new_data = new T [new_nr * nc]; - - octave_idx_type ii = 0; - iidx = 0; - for (octave_idx_type i = 0; i < nr; i++) - { - if (iidx < num_to_delete && i == idx_i.elem (iidx)) - iidx++; - else - { - for (octave_idx_type j = 0; j < nc; j++) - new_data[new_nr*j+ii] = xelem (i, j); - ii++; - } - } - - if (--(Array<T>::rep)->count <= 0) - delete Array<T>::rep; - - Array<T>::rep = new typename Array<T>::ArrayRep (new_data, new_nr * nc); - - dimensions.resize (2); - dimensions(0) = new_nr; - } - else - (*current_liboctave_error_handler) - ("A(idx) = []: index out of range"); - } - } - } - else if (! (idx_i.orig_empty () || idx_j.orig_empty ())) - { - (*current_liboctave_error_handler) - ("a null assignment can have only one non-colon index"); - } -} - -template <class T> -void -Array<T>::maybe_delete_elements (idx_vector&, idx_vector&, idx_vector&) -{ - assert (0); -} - -template <class T> -void -Array<T>::maybe_delete_elements (Array<idx_vector>& ra_idx) -{ - octave_idx_type n_idx = ra_idx.length (); - - // Special case matrices - if (ndims () == 2) - { - if (n_idx == 1) - { - maybe_delete_elements (ra_idx (0)); - return; - } - else if (n_idx == 2) - { - maybe_delete_elements (ra_idx (0), ra_idx (1)); - return; - } - } - - dim_vector lhs_dims = dims (); - - int n_lhs_dims = lhs_dims.length (); - - if (lhs_dims.all_zero ()) - return; - - if (n_idx == 1 && ra_idx(0).is_colon ()) - { - resize (dim_vector (0, 0)); - return; - } - - if (n_idx > n_lhs_dims) - { - for (int i = n_idx; i < n_lhs_dims; i++) - { - // Ensure that extra indices are either colon or 1. - - if (! ra_idx(i).is_colon_equiv (1, 1)) - { - (*current_liboctave_error_handler) - ("index exceeds array dimensions"); - return; - } - } - - ra_idx.resize (n_lhs_dims); - - n_idx = n_lhs_dims; - } - - Array<int> idx_is_colon (n_idx, 0); - - Array<int> idx_is_colon_equiv (n_idx, 0); - - // Initialization of colon arrays. - - for (octave_idx_type i = 0; i < n_idx; i++) - { - if (ra_idx(i).orig_empty ()) return; - idx_is_colon_equiv(i) = ra_idx(i).is_colon_equiv (lhs_dims(i), 1); - - idx_is_colon(i) = ra_idx(i).is_colon (); - } - - bool idx_ok = true; - - // Check for index out of bounds. - - for (octave_idx_type i = 0 ; i < n_idx - 1; i++) - { - if (! (idx_is_colon(i) || idx_is_colon_equiv(i))) - { - ra_idx(i).sort (true); - - if (ra_idx(i).max () > lhs_dims(i)) - { - (*current_liboctave_error_handler) - ("index exceeds array dimensions"); - - idx_ok = false; - break; - } - else if (ra_idx(i).min () < 0) // I believe this is checked elsewhere - { - (*current_liboctave_error_handler) - ("index must be one or larger"); - - idx_ok = false; - break; - } - } - } - - if (n_idx <= n_lhs_dims) - { - octave_idx_type last_idx = ra_idx(n_idx-1).max (); - - octave_idx_type sum_el = lhs_dims(n_idx-1); - - for (octave_idx_type i = n_idx; i < n_lhs_dims; i++) - sum_el *= lhs_dims(i); - - if (last_idx > sum_el - 1) - { - (*current_liboctave_error_handler) - ("index exceeds array dimensions"); - - idx_ok = false; - } - } - - if (idx_ok) - { - if (n_idx > 1 - && (all_ones (idx_is_colon))) - { - // A(:,:,:) -- we are deleting elements in all dimensions, so - // the result is [](0x0x0). - - dim_vector newdim = dims (); - newdim(0) = 0; - - resize (newdim); - } - - else if (n_idx > 1 - && num_ones (idx_is_colon) == n_idx - 1 - && num_ones (idx_is_colon_equiv) == n_idx) - { - // A(:,:,j) -- we are deleting elements in one dimension by - // enumerating them. - // - // If we enumerate all of the elements, we should have zero - // elements in that dimension with the same number of elements - // in the other dimensions that we started with. - - dim_vector temp_dims; - temp_dims.resize (n_idx); - - for (octave_idx_type i = 0; i < n_idx; i++) - { - if (idx_is_colon (i)) - temp_dims(i) = lhs_dims(i); - else - temp_dims(i) = 0; - } - - resize (temp_dims); - } - else if (n_idx > 1 && num_ones (idx_is_colon) == n_idx - 1) - { - // We have colons in all indices except for one. - // This index tells us which slice to delete - - if (n_idx < n_lhs_dims) - { - // Collapse dimensions beyond last index. - - if (! (ra_idx(n_idx-1).is_colon ())) - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", - "fewer indices than dimensions for N-d array"); - - for (octave_idx_type i = n_idx; i < n_lhs_dims; i++) - lhs_dims(n_idx-1) *= lhs_dims(i); - - lhs_dims.resize (n_idx); - - // Reshape *this. - dimensions = lhs_dims; - } - - int non_col = 0; - - // Find the non-colon column. - - for (octave_idx_type i = 0; i < n_idx; i++) - { - if (! idx_is_colon(i)) - non_col = i; - } - - // The length of the non-colon dimension. - - octave_idx_type non_col_dim = lhs_dims (non_col); - - octave_idx_type num_to_delete = ra_idx(non_col).length (lhs_dims (non_col)); - - if (num_to_delete > 0) - { - int temp = lhs_dims.num_ones (); - - if (non_col_dim == 1) - temp--; - - if (temp == n_idx - 1 && num_to_delete == non_col_dim) - { - // We have A with (1x1x4), where A(1,:,1:4) - // Delete all (0x0x0) - - dim_vector zero_dims (n_idx, 0); - - resize (zero_dims); - } - else - { - // New length of non-colon dimension - // (calculated in the next for loop) - - octave_idx_type new_dim = non_col_dim; - - octave_idx_type iidx = 0; - - for (octave_idx_type j = 0; j < non_col_dim; j++) - if (j == ra_idx(non_col).elem (iidx)) - { - iidx++; - - new_dim--; - - if (iidx == num_to_delete) - break; - } - - // Creating the new nd array after deletions. - - if (new_dim > 0) - { - // Calculate number of elements in new array. - - octave_idx_type num_new_elem=1; - - for (int i = 0; i < n_idx; i++) - { - if (i == non_col) - num_new_elem *= new_dim; - - else - num_new_elem *= lhs_dims(i); - } - - T *new_data = new T [num_new_elem]; - - Array<octave_idx_type> result_idx (n_lhs_dims, 0); - - dim_vector new_lhs_dim = lhs_dims; - - new_lhs_dim(non_col) = new_dim; - - octave_idx_type num_elem = 1; - - octave_idx_type numidx = 0; - - octave_idx_type n = length (); - - for (int i = 0; i < n_lhs_dims; i++) - if (i != non_col) - num_elem *= lhs_dims(i); - - num_elem *= ra_idx(non_col).capacity (); - - for (octave_idx_type i = 0; i < n; i++) - { - if (numidx < num_elem - && is_in (result_idx(non_col), ra_idx(non_col))) - numidx++; - - else - { - Array<octave_idx_type> temp_result_idx = result_idx; - - octave_idx_type num_lgt = how_many_lgt (result_idx(non_col), - ra_idx(non_col)); - - temp_result_idx(non_col) -= num_lgt; - - octave_idx_type kidx - = ::compute_index (temp_result_idx, new_lhs_dim); - - new_data[kidx] = xelem (result_idx); - } - - increment_index (result_idx, lhs_dims); - } - - if (--rep->count <= 0) - delete rep; - - rep = new typename Array<T>::ArrayRep (new_data, - num_new_elem); - - dimensions = new_lhs_dim; - } - } - } - } - else if (n_idx == 1) - { - // This handle cases where we only have one index (not - // colon). The index denotes which elements we should - // delete in the array which can be of any dimension. We - // return a column vector, except for the case where we are - // operating on a row vector. The elements are numerated - // column by column. - // - // A(3,3,3)=2; - // A(3:5) = []; A(6)=[] - - octave_idx_type lhs_numel = numel (); - - idx_vector idx_vec = ra_idx(0); - - idx_vec.freeze (lhs_numel, 0, true); - - idx_vec.sort (true); - - octave_idx_type num_to_delete = idx_vec.length (lhs_numel); - - if (num_to_delete > 0) - { - octave_idx_type new_numel = lhs_numel - num_to_delete; - - T *new_data = new T[new_numel]; - - Array<octave_idx_type> lhs_ra_idx (ndims (), 0); - - octave_idx_type ii = 0; - octave_idx_type iidx = 0; - - for (octave_idx_type i = 0; i < lhs_numel; i++) - { - if (iidx < num_to_delete && i == idx_vec.elem (iidx)) - { - iidx++; - } - else - { - new_data[ii++] = xelem (lhs_ra_idx); - } - - increment_index (lhs_ra_idx, lhs_dims); - } - - if (--(Array<T>::rep)->count <= 0) - delete Array<T>::rep; - - Array<T>::rep = new typename Array<T>::ArrayRep (new_data, new_numel); - - dimensions.resize (2); - - if (lhs_dims.length () == 2 && lhs_dims(1) == 1) - { - dimensions(0) = new_numel; - dimensions(1) = 1; - } - else - { - dimensions(0) = 1; - dimensions(1) = new_numel; - } - } - } - else if (num_ones (idx_is_colon) < n_idx) - { - (*current_liboctave_error_handler) - ("a null assignment can have only one non-colon index"); - } - } -} - -template <class T> -Array<T> -Array<T>::value (void) const -{ - Array<T> retval; - - int n_idx = index_count (); - - if (n_idx == 2) - { - idx_vector *tmp = get_idx (); - - idx_vector idx_i = tmp[0]; - idx_vector idx_j = tmp[1]; - - retval = index (idx_i, idx_j); - } - else if (n_idx == 1) - { - retval = index (idx[0]); - } - else - { - clear_index (); - - (*current_liboctave_error_handler) - ("Array<T>::value: invalid number of indices specified"); - } - - clear_index (); - - return retval; -} - -template <class T> -Array<T> -Array<T>::index (idx_vector& idx_arg, int resize_ok, const T& rfv) const -{ - Array<T> retval; - - dim_vector dv = idx_arg.orig_dimensions (); - - if (dv.length () > 2 || ndims () > 2) - retval = indexN (idx_arg, resize_ok, rfv); - else - { - switch (ndims ()) - { - case 1: - retval = index1 (idx_arg, resize_ok, rfv); - break; - - case 2: - retval = index2 (idx_arg, resize_ok, rfv); - break; - - default: - (*current_liboctave_error_handler) - ("invalid array (internal error)"); - break; - } - } - - return retval; -} - -template <class T> -Array<T> -Array<T>::index1 (idx_vector& idx_arg, int resize_ok, const T& rfv) const -{ - Array<T> retval; - - octave_idx_type len = length (); - - octave_idx_type n = idx_arg.freeze (len, "vector", resize_ok); - - if (idx_arg) - { - if (idx_arg.is_colon_equiv (len)) - { - retval = *this; - } - else if (n == 0) - { - retval.resize_no_fill (0); - } - else - { - retval.resize_no_fill (n); - - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = idx_arg.elem (i); - if (ii >= len) - retval.elem (i) = rfv; - else - retval.elem (i) = elem (ii); - } - } - } - - // idx_vector::freeze() printed an error message for us. - - return retval; -} - -template <class T> -Array<T> -Array<T>::index2 (idx_vector& idx_arg, int resize_ok, const T& rfv) const -{ - Array<T> retval; - - assert (ndims () == 2); - - octave_idx_type nr = dim1 (); - octave_idx_type nc = dim2 (); - - octave_idx_type orig_len = nr * nc; - - dim_vector idx_orig_dims = idx_arg.orig_dimensions (); - - octave_idx_type idx_orig_rows = idx_arg.orig_rows (); - octave_idx_type idx_orig_columns = idx_arg.orig_columns (); - - if (idx_arg.is_colon ()) - { - // Fast magic colon processing. - - octave_idx_type result_nr = nr * nc; - octave_idx_type result_nc = 1; - - retval = Array<T> (*this, dim_vector (result_nr, result_nc)); - } - else if (nr == 1 && nc == 1) - { - Array<T> tmp = Array<T>::index1 (idx_arg, resize_ok); - - octave_idx_type len = tmp.length (); - - if (len >= idx_orig_dims.numel ()) - retval = Array<T> (tmp, idx_orig_dims); - } - else if (nr == 1 || nc == 1) - { - // If indexing a vector with a matrix, return value has same - // shape as the index. Otherwise, it has same orientation as - // indexed object. - - Array<T> tmp = Array<T>::index1 (idx_arg, resize_ok); - - octave_idx_type len = tmp.length (); - - if (idx_orig_rows == 1 || idx_orig_columns == 1) - { - if (nr == 1) - retval = Array<T> (tmp, dim_vector (1, len)); - else - retval = Array<T> (tmp, dim_vector (len, 1)); - } - else if (len >= idx_orig_dims.numel ()) - retval = Array<T> (tmp, idx_orig_dims); - } - else - { - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", "single index used for matrix"); - - // This code is only for indexing matrices. The vector - // cases are handled above. - - idx_arg.freeze (nr * nc, "matrix", resize_ok); - - if (idx_arg) - { - octave_idx_type result_nr = idx_orig_rows; - octave_idx_type result_nc = idx_orig_columns; - - retval.resize_no_fill (result_nr, result_nc); - - octave_idx_type k = 0; - for (octave_idx_type j = 0; j < result_nc; j++) - { - for (octave_idx_type i = 0; i < result_nr; i++) - { - octave_idx_type ii = idx_arg.elem (k++); - if (ii >= orig_len) - retval.elem (i, j) = rfv; - else - { - octave_idx_type fr = ii % nr; - octave_idx_type fc = (ii - fr) / nr; - retval.elem (i, j) = elem (fr, fc); - } - } - } - } - // idx_vector::freeze() printed an error message for us. - } - - return retval; -} - -template <class T> -Array<T> -Array<T>::indexN (idx_vector& ra_idx, int resize_ok, const T& rfv) const -{ - Array<T> retval; - - dim_vector dv = dims (); - - int n_dims = dv.length (); - - octave_idx_type orig_len = dv.numel (); - - dim_vector idx_orig_dims = ra_idx.orig_dimensions (); - - if (ra_idx.is_colon ()) - { - // Fast magic colon processing. - - retval = Array<T> (*this, dim_vector (orig_len, 1)); - } - else - { - bool vec_equiv = vector_equivalent (dv); - - if (! (vec_equiv || ra_idx.is_colon ())) - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", "single index used for N-d array"); - - octave_idx_type frozen_len - = ra_idx.freeze (orig_len, "nd-array", resize_ok); - - if (ra_idx) - { - dim_vector result_dims; - - if (vec_equiv && ! orig_len == 1) - { - result_dims = dv; - - for (int i = 0; i < n_dims; i++) - { - if (result_dims(i) != 1) - { - // All but this dim should be one. - result_dims(i) = frozen_len; - break; - } - } - } - else - result_dims = idx_orig_dims; - - result_dims.chop_trailing_singletons (); - - retval.resize (result_dims); - - octave_idx_type n = result_dims.numel (); - - octave_idx_type k = 0; - - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = ra_idx.elem (k++); - - if (ii >= orig_len) - retval.elem (i) = rfv; - else - retval.elem (i) = elem (ii); - } - } - } - - return retval; -} - -template <class T> -Array<T> -Array<T>::index (idx_vector& idx_i, idx_vector& idx_j, int resize_ok, - const T& rfv) const -{ - Array<T> retval; - - if (ndims () != 2) - { - Array<idx_vector> ra_idx (2); - ra_idx(0) = idx_i; - ra_idx(1) = idx_j; - return index (ra_idx, resize_ok, rfv); - } - - octave_idx_type nr = dim1 (); - octave_idx_type nc = dim2 (); - - octave_idx_type n = idx_i.freeze (nr, "row", resize_ok); - octave_idx_type m = idx_j.freeze (nc, "column", resize_ok); - - if (idx_i && idx_j) - { - if (idx_i.orig_empty () || idx_j.orig_empty () || n == 0 || m == 0) - { - retval.resize_no_fill (n, m); - } - else if (idx_i.is_colon_equiv (nr) && idx_j.is_colon_equiv (nc)) - { - retval = *this; - } - else - { - retval.resize_no_fill (n, m); - - for (octave_idx_type j = 0; j < m; j++) - { - octave_idx_type jj = idx_j.elem (j); - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = idx_i.elem (i); - if (ii >= nr || jj >= nc) - retval.elem (i, j) = rfv; - else - retval.elem (i, j) = elem (ii, jj); - } - } - } - } - - // idx_vector::freeze() printed an error message for us. - - return retval; -} - -template <class T> -Array<T> -Array<T>::index (Array<idx_vector>& ra_idx, int resize_ok, const T& rfv) const -{ - // This function handles all calls with more than one idx. - // For (3x3x3), the call can be A(2,5), A(2,:,:), A(3,2,3) etc. - - Array<T> retval; - - int n_dims = dimensions.length (); - - // Remove trailing singletons in ra_idx, but leave at least ndims - // elements. - - octave_idx_type ra_idx_len = ra_idx.length (); - - bool trim_trailing_singletons = true; - for (octave_idx_type j = ra_idx_len; j > n_dims; j--) - { - idx_vector iidx = ra_idx (ra_idx_len-1); - if (iidx.capacity () == 1 && trim_trailing_singletons) - ra_idx_len--; - else - trim_trailing_singletons = false; - - if (! resize_ok) - { - for (octave_idx_type i = 0; i < iidx.capacity (); i++) - if (iidx (i) != 0) - { - (*current_liboctave_error_handler) - ("index exceeds N-d array dimensions"); - - return retval; - } - } - } - - ra_idx.resize (ra_idx_len); - - dim_vector new_dims = dims (); - dim_vector frozen_lengths; - - if (!ra_idx (ra_idx_len - 1).orig_empty () && ra_idx_len < n_dims) - frozen_lengths = short_freeze (ra_idx, dimensions, resize_ok); - else - { - new_dims.resize (ra_idx_len, 1); - frozen_lengths = freeze (ra_idx, new_dims, resize_ok); - } - - if (all_ok (ra_idx)) - { - if (any_orig_empty (ra_idx) || frozen_lengths.any_zero ()) - { - frozen_lengths.chop_trailing_singletons (); - - retval.resize (frozen_lengths); - } - else if (frozen_lengths.length () == n_dims - && all_colon_equiv (ra_idx, dimensions)) - { - retval = *this; - } - else - { - dim_vector frozen_lengths_for_resize = frozen_lengths; - - frozen_lengths_for_resize.chop_trailing_singletons (); - - retval.resize (frozen_lengths_for_resize); - - octave_idx_type n = retval.length (); - - Array<octave_idx_type> result_idx (ra_idx.length (), 0); - - Array<octave_idx_type> elt_idx; - - for (octave_idx_type i = 0; i < n; i++) - { - elt_idx = get_elt_idx (ra_idx, result_idx); - - octave_idx_type numelem_elt = get_scalar_idx (elt_idx, new_dims); - - if (numelem_elt >= length () || numelem_elt < 0) - retval.elem (i) = rfv; - else - retval.elem (i) = elem (numelem_elt); - - increment_index (result_idx, frozen_lengths); - - } - } - } - - return retval; -} - -template <class T> bool ascending_compare (T a, T b) { @@ -2851,7 +2114,7 @@ if (nnr == 1) { octave_idx_type n = nnc + std::abs (k); - d = Array<T> (dim_vector (n, n), resize_fill_value (T ())); + d = Array<T> (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); @@ -2859,7 +2122,7 @@ else { octave_idx_type n = nnr + std::abs (k); - d = Array<T> (dim_vector (n, n), resize_fill_value (T ())); + d = Array<T> (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); @@ -2870,1011 +2133,6 @@ return d; } -// FIXME -- this is a mess. - -template <class LT, class RT> -int -assign (Array<LT>& lhs, const Array<RT>& rhs, const LT& rfv) -{ - int n_idx = lhs.index_count (); - - // kluge... - if (lhs.ndims () == 0) - lhs.resize_no_fill (0, 0); - - return (lhs.ndims () == 2 - && (n_idx == 1 - || (n_idx < 3 - && rhs.ndims () == 2 - && rhs.rows () == 0 && rhs.columns () == 0))) - ? assign2 (lhs, rhs, rfv) : assignN (lhs, rhs, rfv); -} - -template <class LT, class RT> -int -assign1 (Array<LT>& lhs, const Array<RT>& rhs, const LT& rfv) -{ - int retval = 1; - - idx_vector *tmp = lhs.get_idx (); - - idx_vector lhs_idx = tmp[0]; - - octave_idx_type lhs_len = lhs.length (); - octave_idx_type rhs_len = rhs.length (); - - octave_idx_type n = lhs_idx.freeze (lhs_len, "vector", true); - - if (n != 0) - { - dim_vector lhs_dims = lhs.dims (); - - if (lhs_len != 0 - || lhs_dims.all_zero () - || (lhs_dims.length () == 2 && lhs_dims(0) < 2)) - { - if (rhs_len == n || rhs_len == 1) - { - lhs.make_unique (); - - octave_idx_type max_idx = lhs_idx.max () + 1; - if (max_idx > lhs_len) - lhs.resize_and_fill (max_idx, rfv); - } - - if (rhs_len == n) - { - lhs.make_unique (); - - if (lhs_idx.is_colon ()) - { - for (octave_idx_type i = 0; i < n; i++) - lhs.xelem (i) = rhs.elem (i); - } - else - { - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = lhs_idx.elem (i); - lhs.xelem (ii) = rhs.elem (i); - } - } - } - else if (rhs_len == 1) - { - lhs.make_unique (); - - RT scalar = rhs.elem (0); - - if (lhs_idx.is_colon ()) - { - for (octave_idx_type i = 0; i < n; i++) - lhs.xelem (i) = scalar; - } - else - { - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = lhs_idx.elem (i); - lhs.xelem (ii) = scalar; - } - } - } - else - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(I) = X: X must be a scalar or a vector with same length as I"); - - retval = 0; - } - } - else - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(I) = X: unable to resize A"); - - retval = 0; - } - } - else if (lhs_idx.is_colon ()) - { - dim_vector lhs_dims = lhs.dims (); - - if (lhs_dims.all_zero ()) - { - lhs.make_unique (); - - lhs.resize_no_fill (rhs_len); - - for (octave_idx_type i = 0; i < rhs_len; i++) - lhs.xelem (i) = rhs.elem (i); - } - else if (rhs_len != lhs_len) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(:) = X: A must be the same size as X"); - } - } - else if (! (rhs_len == 1 || rhs_len == 0)) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A([]) = X: X must also be an empty matrix or a scalar"); - - retval = 0; - } - - lhs.clear_index (); - - return retval; -} - -#define MAYBE_RESIZE_LHS \ - do \ - { \ - octave_idx_type max_row_idx = idx_i_is_colon ? rhs_nr : idx_i.max () + 1; \ - octave_idx_type max_col_idx = idx_j_is_colon ? rhs_nc : idx_j.max () + 1; \ - \ - octave_idx_type new_nr = max_row_idx > lhs_nr ? max_row_idx : lhs_nr; \ - octave_idx_type new_nc = max_col_idx > lhs_nc ? max_col_idx : lhs_nc; \ - \ - lhs.resize_and_fill (new_nr, new_nc, rfv); \ - } \ - while (0) - -template <class LT, class RT> -int -assign2 (Array<LT>& lhs, const Array<RT>& rhs, const LT& rfv) -{ - int retval = 1; - - int n_idx = lhs.index_count (); - - octave_idx_type lhs_nr = lhs.rows (); - octave_idx_type lhs_nc = lhs.cols (); - - Array<RT> xrhs = rhs; - - octave_idx_type rhs_nr = xrhs.rows (); - octave_idx_type rhs_nc = xrhs.cols (); - - if (xrhs.ndims () > 2) - { - xrhs = xrhs.squeeze (); - - dim_vector dv_tmp = xrhs.dims (); - - switch (dv_tmp.length ()) - { - case 1: - // FIXME -- this case should be unnecessary, because - // squeeze should always return an object with 2 dimensions. - if (rhs_nr == 1) - rhs_nc = dv_tmp.elem (0); - break; - - case 2: - rhs_nr = dv_tmp.elem (0); - rhs_nc = dv_tmp.elem (1); - break; - - default: - lhs.clear_index (); - (*current_liboctave_error_handler) - ("Array<T>::assign2: Dimension mismatch"); - return 0; - } - } - - bool rhs_is_scalar = rhs_nr == 1 && rhs_nc == 1; - - idx_vector *tmp = lhs.get_idx (); - - idx_vector idx_i; - idx_vector idx_j; - - if (n_idx > 1) - idx_j = tmp[1]; - - if (n_idx > 0) - idx_i = tmp[0]; - - if (n_idx == 2) - { - octave_idx_type n = idx_i.freeze (lhs_nr, "row", true); - octave_idx_type m = idx_j.freeze (lhs_nc, "column", true); - - int idx_i_is_colon = idx_i.is_colon (); - int idx_j_is_colon = idx_j.is_colon (); - - if (lhs_nr == 0 && lhs_nc == 0) - { - if (idx_i_is_colon) - n = rhs_nr; - - if (idx_j_is_colon) - m = rhs_nc; - } - - if (idx_i && idx_j) - { - if (rhs_is_scalar && n >= 0 && m >= 0) - { - // No need to do anything if either of the indices - // are empty. - - if (n > 0 && m > 0) - { - lhs.make_unique (); - - MAYBE_RESIZE_LHS; - - RT scalar = xrhs.elem (0, 0); - - for (octave_idx_type j = 0; j < m; j++) - { - octave_idx_type jj = idx_j.elem (j); - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = idx_i.elem (i); - lhs.xelem (ii, jj) = scalar; - } - } - } - } - else if ((n == 1 || m == 1) - && (rhs_nr == 1 || rhs_nc == 1) - && n * m == rhs_nr * rhs_nc) - { - lhs.make_unique (); - - MAYBE_RESIZE_LHS; - - if (n > 0 && m > 0) - { - octave_idx_type k = 0; - - for (octave_idx_type j = 0; j < m; j++) - { - octave_idx_type jj = idx_j.elem (j); - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = idx_i.elem (i); - lhs.xelem (ii, jj) = xrhs.elem (k++); - } - } - } - } - else if (n == rhs_nr && m == rhs_nc) - { - lhs.make_unique (); - - MAYBE_RESIZE_LHS; - - if (n > 0 && m > 0) - { - for (octave_idx_type j = 0; j < m; j++) - { - octave_idx_type jj = idx_j.elem (j); - for (octave_idx_type i = 0; i < n; i++) - { - octave_idx_type ii = idx_i.elem (i); - lhs.xelem (ii, jj) = xrhs.elem (i, j); - } - } - } - } - else if (n == 0 && m == 0) - { - if (! (rhs_is_scalar || (rhs_nr == 0 || rhs_nc == 0))) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A([], []) = X: X must be an empty matrix or a scalar"); - - retval = 0; - } - } - else - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(I, J) = X: X must be a scalar or the number of elements in I must match the number of rows in X and the number of elements in J must match the number of columns in X"); - - retval = 0; - } - } - // idx_vector::freeze() printed an error message for us. - } - else if (n_idx == 1) - { - int lhs_is_empty = lhs_nr == 0 || lhs_nc == 0; - - if (lhs_is_empty || (lhs_nr == 1 && lhs_nc == 1)) - { - octave_idx_type lhs_len = lhs.length (); - - idx_i.freeze (lhs_len, 0, true); - - if (idx_i) - { - if (lhs_is_empty - && idx_i.is_colon () - && ! (rhs_nr == 1 || rhs_nc == 1)) - { - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", - "A(:) = X: X is not a vector or scalar"); - } - else - { - octave_idx_type idx_nr = idx_i.orig_rows (); - octave_idx_type idx_nc = idx_i.orig_columns (); - - if (! (rhs_nr == idx_nr && rhs_nc == idx_nc)) - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", - "A(I) = X: X does not have same shape as I"); - } - - if (assign1 (lhs, xrhs, rfv)) - { - octave_idx_type len = lhs.length (); - - if (len > 0) - { - // The following behavior is much simplified - // over previous versions of Octave. It - // seems to be compatible with Matlab. - - lhs.dimensions = dim_vector (1, lhs.length ()); - } - } - else - retval = 0; - } - // idx_vector::freeze() printed an error message for us. - } - else if (lhs_nr == 1) - { - idx_i.freeze (lhs_nc, "vector", true); - - if (idx_i) - { - if (assign1 (lhs, xrhs, rfv)) - lhs.dimensions = dim_vector (1, lhs.length ()); - else - retval = 0; - } - // idx_vector::freeze() printed an error message for us. - } - else if (lhs_nc == 1) - { - idx_i.freeze (lhs_nr, "vector", true); - - if (idx_i) - { - if (assign1 (lhs, xrhs, rfv)) - lhs.dimensions = dim_vector (lhs.length (), 1); - else - retval = 0; - } - // idx_vector::freeze() printed an error message for us. - } - else - { - if (! idx_i.is_colon ()) - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", "single index used for matrix"); - - octave_idx_type len = idx_i.freeze (lhs_nr * lhs_nc, "matrix"); - - if (idx_i) - { - if (len == 0) - { - if (! (rhs_is_scalar || (rhs_nr == 0 || rhs_nc == 0))) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A([]) = X: X must be an empty matrix or scalar"); - - retval = 0; - } - } - else if (len == rhs_nr * rhs_nc) - { - lhs.make_unique (); - - if (idx_i.is_colon ()) - { - for (octave_idx_type i = 0; i < len; i++) - lhs.xelem (i) = xrhs.elem (i); - } - else - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_idx_type ii = idx_i.elem (i); - lhs.xelem (ii) = xrhs.elem (i); - } - } - } - else if (rhs_is_scalar) - { - lhs.make_unique (); - - RT scalar = rhs.elem (0, 0); - - if (idx_i.is_colon ()) - { - for (octave_idx_type i = 0; i < len; i++) - lhs.xelem (i) = scalar; - } - else - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_idx_type ii = idx_i.elem (i); - lhs.xelem (ii) = scalar; - } - } - } - else - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(I) = X: X must be a scalar or a matrix with the same size as I"); - - retval = 0; - } - } - // idx_vector::freeze() printed an error message for us. - } - } - else - { - (*current_liboctave_error_handler) - ("invalid number of indices for matrix expression"); - - retval = 0; - } - - lhs.clear_index (); - - return retval; -} - -template <class LT, class RT> -int -assignN (Array<LT>& lhs, const Array<RT>& rhs, const LT& rfv) -{ - int retval = 1; - - dim_vector rhs_dims = rhs.dims (); - - octave_idx_type rhs_dims_len = rhs_dims.length (); - - bool rhs_is_scalar = is_scalar (rhs_dims); - - int n_idx = lhs.index_count (); - - idx_vector *idx_vex = lhs.get_idx (); - - Array<idx_vector> idx = conv_to_array (idx_vex, n_idx); - - if (n_idx == 0) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("invalid number of indices for matrix expression"); - - retval = 0; - } - else if (n_idx == 1) - { - idx_vector iidx = idx(0); - int iidx_is_colon = iidx.is_colon (); - - if (! iidx_is_colon) - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", "single index used for N-d array"); - - octave_idx_type lhs_len = lhs.length (); - - octave_idx_type len = iidx.freeze (lhs_len, "N-d arrray"); - - if (iidx) - { - if (len == 0) - { - if (! (rhs_dims.all_ones () || rhs_dims.any_zero ())) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A([]) = X: X must be an empty matrix or scalar"); - - retval = 0; - } - } - else if (len == rhs.length ()) - { - lhs.make_unique (); - - if (iidx_is_colon) - { - for (octave_idx_type i = 0; i < len; i++) - lhs.xelem (i) = rhs.elem (i); - } - else - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_idx_type ii = iidx.elem (i); - - lhs.xelem (ii) = rhs.elem (i); - } - } - } - else if (rhs_is_scalar) - { - RT scalar = rhs.elem (0); - - lhs.make_unique (); - - if (iidx_is_colon) - { - for (octave_idx_type i = 0; i < len; i++) - lhs.xelem (i) = scalar; - } - else - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_idx_type ii = iidx.elem (i); - - lhs.xelem (ii) = scalar; - } - } - } - else - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(I) = X: X must be a scalar or a matrix with the same size as I"); - - retval = 0; - } - - // idx_vector::freeze() printed an error message for us. - } - } - else - { - // Maybe expand to more dimensions. - - dim_vector lhs_dims = lhs.dims (); - - octave_idx_type lhs_dims_len = lhs_dims.length (); - - dim_vector final_lhs_dims = lhs_dims; - - dim_vector frozen_len; - - octave_idx_type orig_lhs_dims_len = lhs_dims_len; - - bool orig_empty = lhs_dims.all_zero (); - - if (n_idx < lhs_dims_len) - { - // Collapse dimensions beyond last index. Note that we - // delay resizing LHS until we know that the assignment will - // succeed. - - if (! (idx(n_idx-1).is_colon ())) - (*current_liboctave_warning_with_id_handler) - ("Octave:fortran-indexing", - "fewer indices than dimensions for N-d array"); - - for (int i = n_idx; i < lhs_dims_len; i++) - lhs_dims(n_idx-1) *= lhs_dims(i); - - lhs_dims.resize (n_idx); - - lhs_dims_len = lhs_dims.length (); - } - - // Resize. - - dim_vector new_dims; - new_dims.resize (n_idx); - - if (orig_empty) - { - if (rhs_is_scalar) - { - for (int i = 0; i < n_idx; i++) - { - if (idx(i).is_colon ()) - new_dims(i) = 1; - else - new_dims(i) = idx(i).orig_empty () ? 0 : idx(i).max () + 1; - } - } - else if (is_vector (rhs_dims)) - { - int ncolon = 0; - int fcolon = 0; - octave_idx_type new_dims_numel = 1; - int new_dims_vec = 0; - for (int i = 0; i < n_idx; i++) - if (idx(i).is_colon ()) - { - ncolon ++; - if (ncolon == 1) - fcolon = i; - } - else - { - octave_idx_type cap = idx(i).capacity (); - new_dims_numel *= cap; - if (cap != 1) - new_dims_vec ++; - } - - if (ncolon == n_idx) - { - new_dims = rhs_dims; - new_dims.resize (n_idx); - for (int i = rhs_dims_len; i < n_idx; i++) - new_dims (i) = 1; - } - else - { - octave_idx_type rhs_dims_numel = rhs_dims.numel (); - - for (int i = 0; i < n_idx; i++) - new_dims(i) = idx(i).orig_empty () ? 0 : idx(i).max () + 1; - - if (new_dims_numel != rhs_dims_numel && - ncolon > 0 && new_dims_numel == 1) - { - if (ncolon == rhs_dims_len) - { - int k = 0; - for (int i = 0; i < n_idx; i++) - if (idx(i).is_colon ()) - new_dims (i) = rhs_dims (k++); - } - else - new_dims (fcolon) = rhs_dims_numel; - } - else if (new_dims_numel != rhs_dims_numel || new_dims_vec > 1) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(IDX-LIST) = RHS: mismatched index and RHS dimension"); - return retval; - } - } - } - else - { - int k = 0; - for (int i = 0; i < n_idx; i++) - { - if (idx(i).is_colon ()) - { - if (k < rhs_dims_len) - new_dims(i) = rhs_dims(k++); - else - new_dims(i) = 1; - } - else - { - octave_idx_type nelem = idx(i).capacity (); - - if (nelem >= 1 - && (k < rhs_dims_len && nelem == rhs_dims(k))) - k++; - else if (nelem != 1) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(IDX-LIST) = RHS: mismatched index and RHS dimension"); - return retval; - } - new_dims(i) = idx(i).orig_empty () ? 0 : - idx(i).max () + 1; - } - } - } - } - else - { - for (int i = 0; i < n_idx; i++) - { - // We didn't start out with all zero dimensions, so if - // index is a colon, it refers to the current LHS - // dimension. Otherwise, it is OK to enlarge to a - // dimension given by the largest index, but if that - // index is a colon the new dimension is singleton. - - if (i < lhs_dims_len - && (idx(i).is_colon () - || idx(i).orig_empty () - || idx(i).max () < lhs_dims(i))) - new_dims(i) = lhs_dims(i); - else if (! idx(i).is_colon ()) - new_dims(i) = idx(i).max () + 1; - else - new_dims(i) = 1; - } - } - - if (retval != 0) - { - if (! orig_empty - && n_idx < orig_lhs_dims_len - && new_dims(n_idx-1) != lhs_dims(n_idx-1)) - { - // We reshaped and the last dimension changed. This has to - // be an error, because we don't know how to undo that - // later... - - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("array index %d (= %d) for assignment requires invalid resizing operation", - n_idx, new_dims(n_idx-1)); - - retval = 0; - } - else - { - // Determine final dimensions for LHS and reset the - // current size of the LHS. Note that we delay actually - // resizing LHS until we know that the assignment will - // succeed. - - if (n_idx < orig_lhs_dims_len) - { - for (int i = 0; i < n_idx-1; i++) - final_lhs_dims(i) = new_dims(i); - } - else - final_lhs_dims = new_dims; - - lhs_dims_len = new_dims.length (); - - frozen_len = freeze (idx, new_dims, true); - - for (int i = 0; i < idx.length (); i++) - { - if (! idx(i)) - { - retval = 0; - lhs.clear_index (); - return retval; - } - } - - if (rhs_is_scalar) - { - lhs.make_unique (); - - if (n_idx < orig_lhs_dims_len) - lhs = lhs.reshape (lhs_dims); - - lhs.resize_and_fill (new_dims, rfv); - - if (! final_lhs_dims.any_zero ()) - { - RT scalar = rhs.elem (0); - - if (n_idx == 1) - { - idx_vector iidx = idx(0); - - octave_idx_type len = frozen_len(0); - - if (iidx.is_colon ()) - { - for (octave_idx_type i = 0; i < len; i++) - lhs.xelem (i) = scalar; - } - else - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_idx_type ii = iidx.elem (i); - - lhs.xelem (ii) = scalar; - } - } - } - else if (lhs_dims_len == 2 && n_idx == 2) - { - idx_vector idx_i = idx(0); - idx_vector idx_j = idx(1); - - octave_idx_type i_len = frozen_len(0); - octave_idx_type j_len = frozen_len(1); - - if (idx_i.is_colon()) - { - for (octave_idx_type j = 0; j < j_len; j++) - { - octave_idx_type off = new_dims (0) * - idx_j.elem (j); - for (octave_idx_type i = 0; i < i_len; i++) - lhs.xelem (i + off) = scalar; - } - } - else - { - for (octave_idx_type j = 0; j < j_len; j++) - { - octave_idx_type off = new_dims (0) * - idx_j.elem (j); - for (octave_idx_type i = 0; i < i_len; i++) - { - octave_idx_type ii = idx_i.elem (i); - lhs.xelem (ii + off) = scalar; - } - } - } - } - else - { - octave_idx_type n = Array<LT>::get_size (frozen_len); - - Array<octave_idx_type> result_idx (lhs_dims_len, 0); - - for (octave_idx_type i = 0; i < n; i++) - { - Array<octave_idx_type> elt_idx = get_elt_idx (idx, result_idx); - - lhs.xelem (elt_idx) = scalar; - - increment_index (result_idx, frozen_len); - } - } - } - } - else - { - // RHS is matrix or higher dimension. - - octave_idx_type n = Array<LT>::get_size (frozen_len); - - if (n != rhs.numel ()) - { - lhs.clear_index (); - - (*current_liboctave_error_handler) - ("A(IDX-LIST) = X: X must be a scalar or size of X must equal number of elements indexed by IDX-LIST"); - - retval = 0; - } - else - { - lhs.make_unique (); - - if (n_idx < orig_lhs_dims_len) - lhs = lhs.reshape (lhs_dims); - - lhs.resize_and_fill (new_dims, rfv); - - if (! final_lhs_dims.any_zero ()) - { - if (n_idx == 1) - { - idx_vector iidx = idx(0); - - octave_idx_type len = frozen_len(0); - - if (iidx.is_colon ()) - { - for (octave_idx_type i = 0; i < len; i++) - lhs.xelem (i) = rhs.elem (i); - } - else - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_idx_type ii = iidx.elem (i); - - lhs.xelem (ii) = rhs.elem (i); - } - } - } - else if (lhs_dims_len == 2 && n_idx == 2) - { - idx_vector idx_i = idx(0); - idx_vector idx_j = idx(1); - - octave_idx_type i_len = frozen_len(0); - octave_idx_type j_len = frozen_len(1); - octave_idx_type k = 0; - - if (idx_i.is_colon()) - { - for (octave_idx_type j = 0; j < j_len; j++) - { - octave_idx_type off = new_dims (0) * - idx_j.elem (j); - for (octave_idx_type i = 0; - i < i_len; i++) - lhs.xelem (i + off) = rhs.elem (k++); - } - } - else - { - for (octave_idx_type j = 0; j < j_len; j++) - { - octave_idx_type off = new_dims (0) * - idx_j.elem (j); - for (octave_idx_type i = 0; i < i_len; i++) - { - octave_idx_type ii = idx_i.elem (i); - lhs.xelem (ii + off) = rhs.elem (k++); - } - } - } - - } - else - { - n = Array<LT>::get_size (frozen_len); - - Array<octave_idx_type> result_idx (lhs_dims_len, 0); - - for (octave_idx_type i = 0; i < n; i++) - { - Array<octave_idx_type> elt_idx = get_elt_idx (idx, result_idx); - - lhs.xelem (elt_idx) = rhs.elem (i); - - increment_index (result_idx, frozen_len); - } - } - } - } - } - } - } - - lhs.clear_index (); - - if (retval != 0) - lhs = lhs.reshape (final_lhs_dims); - } - - if (retval != 0) - lhs.chop_trailing_singletons (); - - lhs.clear_index (); - - return retval; -} - template <class T> void Array<T>::print_info (std::ostream& os, const std::string& prefix) const