Array.cc

Go to the documentation of this file.
00001 // Template array classes
00002 /*
00003 
00004 Copyright (C) 1993-2012 John W. Eaton
00005 Copyright (C) 2008-2009 Jaroslav Hajek
00006 Copyright (C) 2009 VZLU Prague
00007 
00008 This file is part of Octave.
00009 
00010 Octave is free software; you can redistribute it and/or modify it
00011 under the terms of the GNU General Public License as published by the
00012 Free Software Foundation; either version 3 of the License, or (at your
00013 option) any later version.
00014 
00015 Octave is distributed in the hope that it will be useful, but WITHOUT
00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00017 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00018 for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with Octave; see the file COPYING.  If not, see
00022 <http://www.gnu.org/licenses/>.
00023 
00024 */
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #include <config.h>
00028 #endif
00029 
00030 #include <cassert>
00031 #include <climits>
00032 
00033 #include <iostream>
00034 #include <sstream>
00035 #include <vector>
00036 #include <algorithm>
00037 #include <new>
00038 
00039 #include "Array.h"
00040 #include "Array-util.h"
00041 #include "idx-vector.h"
00042 #include "lo-error.h"
00043 #include "oct-locbuf.h"
00044 
00045 // One dimensional array class.  Handles the reference counting for
00046 // all the derived classes.
00047 
00048 template <class T>
00049 Array<T>::Array (const Array<T>& a, const dim_vector& dv)
00050   : dimensions (dv), rep (a.rep),
00051     slice_data (a.slice_data), slice_len (a.slice_len)
00052 {
00053   if (dimensions.safe_numel () != a.numel ())
00054     {
00055       std::string dimensions_str = a.dimensions.str ();
00056       std::string new_dims_str = dimensions.str ();
00057 
00058       (*current_liboctave_error_handler)
00059         ("reshape: can't reshape %s array to %s array",
00060          dimensions_str.c_str (), new_dims_str.c_str ());
00061     }
00062 
00063   // This goes here because if an exception is thrown by the above,
00064   // destructor will be never called.
00065   rep->count++;
00066   dimensions.chop_trailing_singletons ();
00067 }
00068 
00069 template <class T>
00070 void
00071 Array<T>::fill (const T& val)
00072 {
00073   if (rep->count > 1)
00074     {
00075       --rep->count;
00076       rep = new ArrayRep (length (), val);
00077       slice_data = rep->data;
00078     }
00079   else
00080     fill_or_memset (slice_len, val, slice_data);
00081 }
00082 
00083 template <class T>
00084 void
00085 Array<T>::clear (void)
00086 {
00087   if (--rep->count == 0)
00088     delete rep;
00089 
00090   rep = nil_rep ();
00091   rep->count++;
00092   slice_data = rep->data;
00093   slice_len = rep->len;
00094 
00095   dimensions = dim_vector ();
00096 }
00097 
00098 template <class T>
00099 void
00100 Array<T>::clear (const dim_vector& dv)
00101 {
00102   if (--rep->count == 0)
00103     delete rep;
00104 
00105   rep = new ArrayRep (dv.safe_numel ());
00106   slice_data = rep->data;
00107   slice_len = rep->len;
00108 
00109   dimensions = dv;
00110   dimensions.chop_trailing_singletons ();
00111 }
00112 
00113 template <class T>
00114 Array<T>
00115 Array<T>::squeeze (void) const
00116 {
00117   Array<T> retval = *this;
00118 
00119   if (ndims () > 2)
00120     {
00121       bool dims_changed = false;
00122 
00123       dim_vector new_dimensions = dimensions;
00124 
00125       int k = 0;
00126 
00127       for (int i = 0; i < ndims (); i++)
00128         {
00129           if (dimensions(i) == 1)
00130             dims_changed = true;
00131           else
00132             new_dimensions(k++) = dimensions(i);
00133         }
00134 
00135       if (dims_changed)
00136         {
00137           switch (k)
00138             {
00139             case 0:
00140               new_dimensions = dim_vector (1, 1);
00141               break;
00142 
00143             case 1:
00144               {
00145                 octave_idx_type tmp = new_dimensions(0);
00146 
00147                 new_dimensions.resize (2);
00148 
00149                 new_dimensions(0) = tmp;
00150                 new_dimensions(1) = 1;
00151               }
00152               break;
00153 
00154             default:
00155               new_dimensions.resize (k);
00156               break;
00157             }
00158         }
00159 
00160       retval = Array<T> (*this, new_dimensions);
00161     }
00162 
00163   return retval;
00164 }
00165 
00166 template <class T>
00167 octave_idx_type
00168 Array<T>::compute_index (octave_idx_type i, octave_idx_type j) const
00169 {
00170   return ::compute_index (i, j, dimensions);
00171 }
00172 
00173 template <class T>
00174 octave_idx_type
00175 Array<T>::compute_index (octave_idx_type i, octave_idx_type j, octave_idx_type k) const
00176 {
00177   return ::compute_index (i, j, k, dimensions);
00178 }
00179 
00180 template <class T>
00181 octave_idx_type
00182 Array<T>::compute_index (const Array<octave_idx_type>& ra_idx) const
00183 {
00184   return ::compute_index (ra_idx, dimensions);
00185 }
00186 
00187 template <class T>
00188 T&
00189 Array<T>::checkelem (octave_idx_type n)
00190 {
00191   // Do checks directly to avoid recomputing slice_len.
00192   if (n < 0)
00193     gripe_invalid_index ();
00194   if (n >= slice_len)
00195     gripe_index_out_of_range (1, 1, n+1, slice_len);
00196 
00197   return elem (n);
00198 }
00199 
00200 template <class T>
00201 T&
00202 Array<T>::checkelem (octave_idx_type i, octave_idx_type j)
00203 {
00204   return elem (compute_index (i, j));
00205 }
00206 
00207 template <class T>
00208 T&
00209 Array<T>::checkelem (octave_idx_type i, octave_idx_type j, octave_idx_type k)
00210 {
00211   return elem (compute_index (i, j, k));
00212 }
00213 
00214 template <class T>
00215 T&
00216 Array<T>::checkelem (const Array<octave_idx_type>& ra_idx)
00217 {
00218   return elem (compute_index (ra_idx));
00219 }
00220 
00221 template <class T>
00222 typename Array<T>::crefT
00223 Array<T>::checkelem (octave_idx_type n) const
00224 {
00225   // Do checks directly to avoid recomputing slice_len.
00226   if (n < 0)
00227     gripe_invalid_index ();
00228   if (n >= slice_len)
00229     gripe_index_out_of_range (1, 1, n+1, slice_len);
00230 
00231   return elem (n);
00232 }
00233 
00234 template <class T>
00235 typename Array<T>::crefT
00236 Array<T>::checkelem (octave_idx_type i, octave_idx_type j) const
00237 {
00238   return elem (compute_index (i, j));
00239 }
00240 
00241 template <class T>
00242 typename Array<T>::crefT
00243 Array<T>::checkelem (octave_idx_type i, octave_idx_type j, octave_idx_type k) const
00244 {
00245   return elem (compute_index (i, j, k));
00246 }
00247 
00248 template <class T>
00249 typename Array<T>::crefT
00250 Array<T>::checkelem (const Array<octave_idx_type>& ra_idx) const
00251 {
00252   return elem (compute_index (ra_idx));
00253 }
00254 
00255 template <class T>
00256 Array<T>
00257 Array<T>::column (octave_idx_type k) const
00258 {
00259   octave_idx_type r = dimensions(0);
00260 #ifdef BOUNDS_CHECKING
00261   if (k < 0 || k > dimensions.numel (1))
00262     gripe_index_out_of_range (2, 2, k+1, dimensions.numel (1));
00263 #endif
00264 
00265   return Array<T> (*this, dim_vector (r, 1), k*r, k*r + r);
00266 }
00267 
00268 template <class T>
00269 Array<T>
00270 Array<T>::page (octave_idx_type k) const
00271 {
00272   octave_idx_type r = dimensions(0), c = dimensions (1), p = r*c;
00273 #ifdef BOUNDS_CHECKING
00274   if (k < 0 || k > dimensions.numel (2))
00275     gripe_index_out_of_range (3, 3, k+1, dimensions.numel (2));
00276 #endif
00277 
00278   return Array<T> (*this, dim_vector (r, c), k*p, k*p + p);
00279 }
00280 
00281 template <class T>
00282 Array<T>
00283 Array<T>::linear_slice (octave_idx_type lo, octave_idx_type up) const
00284 {
00285 #ifdef BOUNDS_CHECKING
00286   if (lo < 0)
00287     gripe_index_out_of_range (1, 1, lo+1, numel ());
00288   if (up > numel ())
00289     gripe_index_out_of_range (1, 1, up, numel ());
00290 #endif
00291   if (up < lo) up = lo;
00292   return Array<T> (*this, dim_vector (up - lo, 1), lo, up);
00293 }
00294 
00295 // Helper class for multi-d dimension permuting (generalized transpose).
00296 class rec_permute_helper
00297 {
00298   // STRIDE occupies the last half of the space allocated for dim to
00299   // avoid a double allocation.
00300 
00301   int n;
00302   int top;
00303   octave_idx_type *dim;
00304   octave_idx_type *stride;
00305   bool use_blk;
00306 
00307 public:
00308   rec_permute_helper (const dim_vector& dv, const Array<octave_idx_type>& perm)
00309 
00310     : n (dv.length ()), top (0), dim (new octave_idx_type [2*n]),
00311       stride (dim + n), use_blk (false)
00312     {
00313       assert (n == perm.length ());
00314 
00315       // Get cumulative dimensions.
00316       OCTAVE_LOCAL_BUFFER (octave_idx_type, cdim, n+1);
00317       cdim[0] = 1;
00318       for (int i = 1; i < n+1; i++) cdim[i] = cdim[i-1] * dv(i-1);
00319 
00320       // Setup the permuted strides.
00321       for (int k = 0; k < n; k++)
00322         {
00323           int kk = perm(k);
00324           dim[k] = dv(kk);
00325           stride[k] = cdim[kk];
00326         }
00327 
00328       // Reduce contiguous runs.
00329       for (int k = 1; k < n; k++)
00330         {
00331           if (stride[k] == stride[top]*dim[top])
00332             dim[top] *= dim[k];
00333           else
00334             {
00335               top++;
00336               dim[top] = dim[k];
00337               stride[top] = stride[k];
00338             }
00339         }
00340 
00341       // Determine whether we can use block transposes.
00342       use_blk = top >= 1 && stride[1] == 1 && stride[0] == dim[1];
00343 
00344     }
00345 
00346   ~rec_permute_helper (void) { delete [] dim; }
00347 
00348   // Helper method for fast blocked transpose.
00349   template <class T>
00350   static T *
00351   blk_trans (const T *src, T *dest, octave_idx_type nr, octave_idx_type nc)
00352     {
00353       static const octave_idx_type m = 8;
00354       OCTAVE_LOCAL_BUFFER (T, blk, m*m);
00355       for (octave_idx_type kr = 0; kr < nr; kr += m)
00356         for (octave_idx_type kc = 0; kc < nc; kc += m)
00357           {
00358             octave_idx_type lr = std::min (m, nr - kr);
00359             octave_idx_type lc = std::min (m, nc - kc);
00360             if (lr == m && lc == m)
00361               {
00362                 const T *ss = src + kc * nr + kr;
00363                 for (octave_idx_type j = 0; j < m; j++)
00364                   for (octave_idx_type i = 0; i < m; i++)
00365                     blk[j*m+i] = ss[j*nr + i];
00366                 T *dd = dest + kr * nc + kc;
00367                 for (octave_idx_type j = 0; j < m; j++)
00368                   for (octave_idx_type i = 0; i < m; i++)
00369                     dd[j*nc+i] = blk[i*m+j];
00370               }
00371             else
00372               {
00373                 const T *ss = src + kc * nr + kr;
00374                 for (octave_idx_type j = 0; j < lc; j++)
00375                   for (octave_idx_type i = 0; i < lr; i++)
00376                     blk[j*m+i] = ss[j*nr + i];
00377                 T *dd = dest + kr * nc + kc;
00378                 for (octave_idx_type j = 0; j < lr; j++)
00379                   for (octave_idx_type i = 0; i < lc; i++)
00380                     dd[j*nc+i] = blk[i*m+j];
00381               }
00382           }
00383 
00384       return dest + nr*nc;
00385     }
00386 
00387 private:
00388 
00389   // Recursive N-d generalized transpose
00390   template <class T>
00391   T *do_permute (const T *src, T *dest, int lev) const
00392     {
00393       if (lev == 0)
00394         {
00395           octave_idx_type step = stride[0], len = dim[0];
00396           if (step == 1)
00397             {
00398               copy_or_memcpy (len, src, dest);
00399               dest += len;
00400             }
00401           else
00402             {
00403               for (octave_idx_type i = 0, j = 0; i < len; i++, j += step)
00404                 dest[i] = src[j];
00405 
00406               dest += len;
00407             }
00408         }
00409       else if (use_blk && lev == 1)
00410         dest = blk_trans (src, dest, dim[1], dim[0]);
00411       else
00412         {
00413           octave_idx_type step = stride[lev], len = dim[lev];
00414           for (octave_idx_type i = 0, j = 0; i < len; i++, j+= step)
00415            dest = do_permute (src + i * step, dest, lev-1);
00416         }
00417 
00418       return dest;
00419     }
00420 
00421   // No copying!
00422 
00423   rec_permute_helper (const rec_permute_helper&);
00424 
00425   rec_permute_helper& operator = (const rec_permute_helper&);
00426 
00427 public:
00428 
00429   template <class T>
00430   void permute (const T *src, T *dest) const { do_permute (src, dest, top); }
00431 };
00432 
00433 
00434 template <class T>
00435 Array<T>
00436 Array<T>::permute (const Array<octave_idx_type>& perm_vec_arg, bool inv) const
00437 {
00438   Array<T> retval;
00439 
00440   Array<octave_idx_type> perm_vec = perm_vec_arg;
00441 
00442   dim_vector dv = dims ();
00443 
00444   int perm_vec_len = perm_vec_arg.length ();
00445 
00446   if (perm_vec_len < dv.length ())
00447     (*current_liboctave_error_handler)
00448       ("%s: invalid permutation vector", inv ? "ipermute" : "permute");
00449 
00450   dim_vector dv_new = dim_vector::alloc (perm_vec_len);
00451 
00452   // Append singleton dimensions as needed.
00453   dv.resize (perm_vec_len, 1);
00454 
00455   // Need this array to check for identical elements in permutation array.
00456   OCTAVE_LOCAL_BUFFER_INIT (bool, checked, perm_vec_len, false);
00457 
00458   bool identity = true;
00459 
00460   // Find dimension vector of permuted array.
00461   for (int i = 0; i < perm_vec_len; i++)
00462     {
00463       octave_idx_type perm_elt = perm_vec.elem (i);
00464       if (perm_elt >= perm_vec_len || perm_elt < 0)
00465         {
00466           (*current_liboctave_error_handler)
00467             ("%s: permutation vector contains an invalid element",
00468              inv ? "ipermute" : "permute");
00469 
00470           return retval;
00471         }
00472 
00473       if (checked[perm_elt])
00474         {
00475           (*current_liboctave_error_handler)
00476             ("%s: permutation vector cannot contain identical elements",
00477              inv ? "ipermute" : "permute");
00478 
00479           return retval;
00480         }
00481       else
00482         {
00483           checked[perm_elt] = true;
00484           identity = identity && perm_elt == i;
00485         }
00486     }
00487 
00488   if (identity)
00489     return *this;
00490 
00491   if (inv)
00492     {
00493       for (int i = 0; i < perm_vec_len; i++)
00494         perm_vec(perm_vec_arg(i)) = i;
00495     }
00496 
00497   for (int i = 0; i < perm_vec_len; i++)
00498     dv_new(i) = dv(perm_vec(i));
00499 
00500   retval = Array<T> (dv_new);
00501 
00502   if (numel () > 0)
00503     {
00504       rec_permute_helper rh (dv, perm_vec);
00505       rh.permute (data (), retval.fortran_vec ());
00506     }
00507 
00508   return retval;
00509 }
00510 
00511 // Helper class for multi-d index reduction and recursive
00512 // indexing/indexed assignment.  Rationale: we could avoid recursion
00513 // using a state machine instead.  However, using recursion is much
00514 // more amenable to possible parallelization in the future.
00515 // Also, the recursion solution is cleaner and more understandable.
00516 
00517 class rec_index_helper
00518 {
00519   // CDIM occupies the last half of the space allocated for dim to
00520   // avoid a double allocation.
00521 
00522   int n;
00523   int top;
00524   octave_idx_type *dim;
00525   octave_idx_type *cdim;
00526   idx_vector *idx;
00527 
00528 public:
00529   rec_index_helper (const dim_vector& dv, const Array<idx_vector>& ia)
00530     : n (ia.length ()), top (0), dim (new octave_idx_type [2*n]),
00531       cdim (dim + n), idx (new idx_vector [n])
00532     {
00533       assert (n > 0 && (dv.length () == std::max (n, 2)));
00534 
00535       dim[0] = dv(0);
00536       cdim[0] = 1;
00537       idx[0] = ia(0);
00538 
00539       for (int i = 1; i < n; i++)
00540         {
00541           // Try reduction...
00542           if (idx[top].maybe_reduce (dim[top], ia(i), dv(i)))
00543             {
00544               // Reduction successful, fold dimensions.
00545               dim[top] *= dv(i);
00546             }
00547           else
00548             {
00549               // Unsuccessful, store index & cumulative dim.
00550               top++;
00551               idx[top] = ia(i);
00552               dim[top] = dv(i);
00553               cdim[top] = cdim[top-1] * dim[top-1];
00554             }
00555         }
00556     }
00557 
00558   ~rec_index_helper (void) { delete [] idx; delete [] dim; }
00559 
00560 private:
00561 
00562   // Recursive N-d indexing
00563   template <class T>
00564   T *do_index (const T *src, T *dest, int lev) const
00565     {
00566       if (lev == 0)
00567         dest += idx[0].index (src, dim[0], dest);
00568       else
00569         {
00570           octave_idx_type nn = idx[lev].length (dim[lev]), d = cdim[lev];
00571           for (octave_idx_type i = 0; i < nn; i++)
00572             dest = do_index (src + d*idx[lev].xelem (i), dest, lev-1);
00573         }
00574 
00575       return dest;
00576     }
00577 
00578   // Recursive N-d indexed assignment
00579   template <class T>
00580   const T *do_assign (const T *src, T *dest, int lev) const
00581     {
00582       if (lev == 0)
00583         src += idx[0].assign (src, dim[0], dest);
00584       else
00585         {
00586           octave_idx_type nn = idx[lev].length (dim[lev]), d = cdim[lev];
00587           for (octave_idx_type i = 0; i < nn; i++)
00588             src = do_assign (src, dest + d*idx[lev].xelem (i), lev-1);
00589         }
00590 
00591       return src;
00592     }
00593 
00594   // Recursive N-d indexed assignment
00595   template <class T>
00596   void do_fill (const T& val, T *dest, int lev) const
00597     {
00598       if (lev == 0)
00599         idx[0].fill (val, dim[0], dest);
00600       else
00601         {
00602           octave_idx_type nn = idx[lev].length (dim[lev]), d = cdim[lev];
00603           for (octave_idx_type i = 0; i < nn; i++)
00604             do_fill (val, dest + d*idx[lev].xelem (i), lev-1);
00605         }
00606     }
00607 
00608   // No copying!
00609 
00610   rec_index_helper (const rec_index_helper&);
00611 
00612   rec_index_helper& operator = (const rec_index_helper&);
00613 
00614 public:
00615 
00616   template <class T>
00617   void index (const T *src, T *dest) const { do_index (src, dest, top); }
00618 
00619   template <class T>
00620   void assign (const T *src, T *dest) const { do_assign (src, dest, top); }
00621 
00622   template <class T>
00623   void fill (const T& val, T *dest) const { do_fill (val, dest, top); }
00624 
00625   bool is_cont_range (octave_idx_type& l,
00626                             octave_idx_type& u) const
00627     {
00628       return top == 0 && idx[0].is_cont_range (dim[0], l, u);
00629     }
00630 };
00631 
00632 // Helper class for multi-d recursive resizing
00633 // This handles resize () in an efficient manner, touching memory only
00634 // once (apart from reinitialization)
00635 class rec_resize_helper
00636 {
00637   octave_idx_type *cext;
00638   octave_idx_type *sext;
00639   octave_idx_type *dext;
00640   int n;
00641 
00642 public:
00643   rec_resize_helper (const dim_vector& ndv, const dim_vector& odv)
00644     : cext (0), sext (0), dext (0), n (0)
00645     {
00646       int l = ndv.length ();
00647       assert (odv.length () == l);
00648       octave_idx_type ld = 1;
00649       int i = 0;
00650       for (; i < l-1 && ndv(i) == odv(i); i++) ld *= ndv(i);
00651       n = l - i;
00652       cext = new octave_idx_type[3*n];
00653       // Trick to avoid three allocations
00654       sext = cext + n;
00655       dext = sext + n;
00656 
00657       octave_idx_type sld = ld, dld = ld;
00658       for (int j = 0; j < n; j++)
00659         {
00660           cext[j] = std::min (ndv(i+j), odv(i+j));
00661           sext[j] = sld *= odv(i+j);
00662           dext[j] = dld *= ndv(i+j);
00663         }
00664       cext[0] *= ld;
00665     }
00666 
00667   ~rec_resize_helper (void) { delete [] cext; }
00668 
00669 private:
00670 
00671   // recursive resizing
00672   template <class T>
00673   void do_resize_fill (const T* src, T *dest, const T& rfv, int lev) const
00674     {
00675       if (lev == 0)
00676         {
00677           copy_or_memcpy (cext[0], src, dest);
00678           fill_or_memset (dext[0] - cext[0], rfv, dest + cext[0]);
00679         }
00680       else
00681         {
00682           octave_idx_type sd = sext[lev-1], dd = dext[lev-1], k;
00683           for (k = 0; k < cext[lev]; k++)
00684             do_resize_fill (src + k * sd, dest + k * dd, rfv, lev - 1);
00685 
00686           fill_or_memset (dext[lev] - k * dd, rfv, dest + k * dd);
00687         }
00688     }
00689 
00690   // No copying!
00691 
00692   rec_resize_helper (const rec_resize_helper&);
00693 
00694   rec_resize_helper& operator = (const rec_resize_helper&);
00695 
00696 public:
00697 
00698   template <class T>
00699   void resize_fill (const T* src, T *dest, const T& rfv) const
00700     { do_resize_fill (src, dest, rfv, n-1); }
00701 };
00702 
00703 template <class T>
00704 Array<T>
00705 Array<T>::index (const idx_vector& i) const
00706 {
00707   octave_idx_type n = numel ();
00708   Array<T> retval;
00709 
00710   if (i.is_colon ())
00711     {
00712       // A(:) produces a shallow copy as a column vector.
00713       retval = Array<T> (*this, dim_vector (n, 1));
00714     }
00715   else
00716     {
00717       if (i.extent (n) != n)
00718         gripe_index_out_of_range (1, 1, i.extent (n), n); // throws
00719 
00720       // FIXME -- this is the only place where orig_dimensions are used.
00721       dim_vector rd = i.orig_dimensions ();
00722       octave_idx_type il = i.length (n);
00723 
00724       // FIXME -- this is for Matlab compatibility.  Matlab 2007 given
00725       //
00726       //   b = ones(3,1)
00727       //
00728       // yields the following:
00729       //
00730       //   b(zeros(0,0)) gives []
00731       //   b(zeros(1,0)) gives zeros(0,1)
00732       //   b(zeros(0,1)) gives zeros(0,1)
00733       //   b(zeros(0,m)) gives zeros(0,m)
00734       //   b(zeros(m,0)) gives zeros(m,0)
00735       //   b(1:2) gives ones(2,1)
00736       //   b(ones(2)) gives ones(2) etc.
00737       //
00738       // As you can see, the behaviour is weird, but the tests end up pretty
00739       // simple.  Nah, I don't want to suggest that this is ad hoc :)
00740 
00741       if (ndims () == 2 && n != 1 && rd.is_vector ())
00742         {
00743           if (columns () == 1)
00744             rd = dim_vector (il, 1);
00745           else if (rows () == 1)
00746             rd = dim_vector (1, il);
00747         }
00748 
00749       octave_idx_type l, u;
00750       if (il != 0 && i.is_cont_range (n, l, u))
00751         // If suitable, produce a shallow slice.
00752         retval = Array<T> (*this, rd, l, u);
00753       else
00754         {
00755           // Don't use resize here to avoid useless initialization for POD
00756           // types.
00757           retval = Array<T> (rd);
00758 
00759           if (il != 0)
00760             i.index (data (), n, retval.fortran_vec ());
00761         }
00762     }
00763 
00764   return retval;
00765 }
00766 
00767 template <class T>
00768 Array<T>
00769 Array<T>::index (const idx_vector& i, const idx_vector& j) const
00770 {
00771   // Get dimensions, allowing Fortran indexing in the 2nd dim.
00772   dim_vector dv = dimensions.redim (2);
00773   octave_idx_type r = dv(0), c = dv(1);
00774   Array<T> retval;
00775 
00776   if (i.is_colon () && j.is_colon ())
00777     {
00778       // A(:,:) produces a shallow copy.
00779       retval = Array<T> (*this, dv);
00780     }
00781   else
00782     {
00783       if (i.extent (r) != r)
00784         gripe_index_out_of_range (2, 1, i.extent (r), r); // throws
00785       if (j.extent (c) != c)
00786         gripe_index_out_of_range (2, 2, j.extent (c), c); // throws
00787 
00788       octave_idx_type n = numel (), il = i.length (r), jl = j.length (c);
00789 
00790       idx_vector ii (i);
00791 
00792       if (ii.maybe_reduce (r, j, c))
00793         {
00794           octave_idx_type l, u;
00795           if (ii.length () > 0 && ii.is_cont_range (n, l, u))
00796             // If suitable, produce a shallow slice.
00797             retval = Array<T> (*this, dim_vector (il, jl), l, u);
00798           else
00799             {
00800               // Don't use resize here to avoid useless initialization for POD types.
00801               retval = Array<T> (dim_vector (il, jl));
00802 
00803               ii.index (data (), n, retval.fortran_vec ());
00804             }
00805         }
00806       else
00807         {
00808           // Don't use resize here to avoid useless initialization for POD types.
00809           retval = Array<T> (dim_vector (il, jl));
00810 
00811           const T* src = data ();
00812           T *dest = retval.fortran_vec ();
00813 
00814           for (octave_idx_type k = 0; k < jl; k++)
00815             dest += i.index (src + r * j.xelem (k), r, dest);
00816         }
00817     }
00818 
00819   return retval;
00820 }
00821 
00822 template <class T>
00823 Array<T>
00824 Array<T>::index (const Array<idx_vector>& ia) const
00825 {
00826   int ial = ia.length ();
00827   Array<T> retval;
00828 
00829   // FIXME: is this dispatching necessary?
00830   if (ial == 1)
00831     retval = index (ia(0));
00832   else if (ial == 2)
00833     retval = index (ia(0), ia(1));
00834   else if (ial > 0)
00835     {
00836       // Get dimensions, allowing Fortran indexing in the last dim.
00837       dim_vector dv = dimensions.redim (ial);
00838 
00839       // Check for out of bounds conditions.
00840       bool all_colons = true;
00841       for (int i = 0; i < ial; i++)
00842         {
00843           if (ia(i).extent (dv(i)) != dv(i))
00844             gripe_index_out_of_range (ial, i+1, ia(i).extent (dv(i)), dv(i)); // throws
00845 
00846           all_colons = all_colons && ia(i).is_colon ();
00847         }
00848 
00849 
00850       if (all_colons)
00851         {
00852           // A(:,:,...,:) produces a shallow copy.
00853           dv.chop_trailing_singletons ();
00854           retval = Array<T> (*this, dv);
00855         }
00856       else
00857         {
00858           // Form result dimensions.
00859           dim_vector rdv = dim_vector::alloc (ial);
00860           for (int i = 0; i < ial; i++) rdv(i) = ia(i).length (dv(i));
00861           rdv.chop_trailing_singletons ();
00862 
00863           // Prepare for recursive indexing
00864           rec_index_helper rh (dv, ia);
00865 
00866           octave_idx_type l, u;
00867           if (rh.is_cont_range (l, u))
00868             // If suitable, produce a shallow slice.
00869             retval = Array<T> (*this, rdv, l, u);
00870           else
00871             {
00872               // Don't use resize here to avoid useless initialization for POD types.
00873               retval = Array<T> (rdv);
00874 
00875               // Do it.
00876               rh.index (data (), retval.fortran_vec ());
00877             }
00878         }
00879     }
00880 
00881   return retval;
00882 }
00883 
00884 // The default fill value.  Override if you want a different one.
00885 
00886 template <class T>
00887 const T& Array<T>::resize_fill_value ()
00888 {
00889   static T zero = T ();
00890   return zero;
00891 }
00892 
00893 // Yes, we could do resize using index & assign.  However, that would
00894 // possibly involve a lot more memory traffic than we actually need.
00895 
00896 template <class T>
00897 void
00898 Array<T>::resize1 (octave_idx_type n, const T& rfv)
00899 {
00900   if (n >= 0 && ndims () == 2)
00901     {
00902       dim_vector dv;
00903       // This is driven by Matlab's behaviour of giving a *row* vector
00904       // on some out-of-bounds assignments.  Specifically, Matlab
00905       // allows a(i) with out-of-bouds i when a is either of 0x0, 1x0,
00906       // 1x1, 0xN, and gives a row vector in all cases (yes, even the
00907       // last one, search me why).  Giving a column vector would make
00908       // much more sense (given the way trailing singleton dims are
00909       // treated).
00910       bool invalid = false;
00911       if (rows () == 0 || rows () == 1)
00912         dv = dim_vector (1, n);
00913       else if (columns () == 1)
00914         dv = dim_vector (n, 1);
00915       else
00916          invalid = true;
00917 
00918       if (invalid)
00919         gripe_invalid_resize ();
00920       else
00921         {
00922           octave_idx_type nx = numel ();
00923           if (n == nx - 1 && n > 0)
00924             {
00925               // Stack "pop" operation.
00926               if (rep->count == 1)
00927                 slice_data[slice_len-1] = T ();
00928               slice_len--;
00929               dimensions = dv;
00930             }
00931           else if (n == nx + 1 && nx > 0)
00932             {
00933               // Stack "push" operation.
00934               if (rep->count == 1 && slice_data + slice_len < rep->data + rep->len)
00935                 {
00936                   slice_data[slice_len++] = rfv;
00937                   dimensions = dv;
00938                 }
00939               else
00940                 {
00941                   static const octave_idx_type max_stack_chunk = 1024;
00942                   octave_idx_type nn = n + std::min (nx, max_stack_chunk);
00943                   Array<T> tmp (Array<T> (dim_vector (nn, 1)), dv, 0, n);
00944                   T *dest = tmp.fortran_vec ();
00945 
00946                   copy_or_memcpy (nx, data (), dest);
00947                   dest[nx] = rfv;
00948 
00949                   *this = tmp;
00950                 }
00951             }
00952           else if (n != nx)
00953             {
00954               Array<T> tmp = Array<T> (dv);
00955               T *dest = tmp.fortran_vec ();
00956 
00957               octave_idx_type n0 = std::min (n, nx), n1 = n - n0;
00958               copy_or_memcpy (n0, data (), dest);
00959               fill_or_memset (n1, rfv, dest + n0);
00960 
00961               *this = tmp;
00962             }
00963         }
00964     }
00965   else
00966     gripe_invalid_resize ();
00967 }
00968 
00969 template <class T>
00970 void
00971 Array<T>::resize2 (octave_idx_type r, octave_idx_type c, const T& rfv)
00972 {
00973   if (r >= 0 && c >= 0 && ndims () == 2)
00974     {
00975       octave_idx_type rx = rows (), cx = columns ();
00976       if (r != rx || c != cx)
00977         {
00978           Array<T> tmp = Array<T> (dim_vector (r, c));
00979           T *dest = tmp.fortran_vec ();
00980 
00981           octave_idx_type r0 = std::min (r, rx), r1 = r - r0;
00982           octave_idx_type c0 = std::min (c, cx), c1 = c - c0;
00983           const T *src = data ();
00984           if (r == rx)
00985             {
00986               copy_or_memcpy (r * c0, src, dest);
00987               dest += r * c0;
00988             }
00989           else
00990             {
00991               for (octave_idx_type k = 0; k < c0; k++)
00992                 {
00993                   copy_or_memcpy (r0, src, dest);
00994                   src += rx;
00995                   dest += r0;
00996                   fill_or_memset (r1, rfv, dest);
00997                   dest += r1;
00998                 }
00999             }
01000 
01001           fill_or_memset (r * c1, rfv, dest);
01002 
01003           *this = tmp;
01004         }
01005     }
01006   else
01007     gripe_invalid_resize ();
01008 
01009 }
01010 
01011 template<class T>
01012 void
01013 Array<T>::resize (const dim_vector& dv, const T& rfv)
01014 {
01015   int dvl = dv.length ();
01016   if (dvl == 2)
01017     resize2 (dv(0), dv(1), rfv);
01018   else if (dimensions != dv)
01019     {
01020       if (dimensions.length () <= dvl && ! dv.any_neg ())
01021         {
01022           Array<T> tmp (dv);
01023           // Prepare for recursive resizing.
01024           rec_resize_helper rh (dv, dimensions.redim (dvl));
01025 
01026           // Do it.
01027           rh.resize_fill (data (), tmp.fortran_vec (), rfv);
01028           *this = tmp;
01029         }
01030       else
01031         gripe_invalid_resize ();
01032     }
01033 }
01034 
01035 template <class T>
01036 Array<T>
01037 Array<T>::index (const idx_vector& i, bool resize_ok, const T& rfv) const
01038 {
01039   Array<T> tmp = *this;
01040   if (resize_ok)
01041     {
01042       octave_idx_type n = numel (), nx = i.extent (n);
01043       if (n != nx)
01044         {
01045           if (i.is_scalar ())
01046             return Array<T> (dim_vector (1, 1), rfv);
01047           else
01048             tmp.resize1 (nx, rfv);
01049         }
01050 
01051       if (tmp.numel () != nx)
01052         return Array<T> ();
01053     }
01054 
01055   return tmp.index (i);
01056 }
01057 
01058 template <class T>
01059 Array<T>
01060 Array<T>::index (const idx_vector& i, const idx_vector& j,
01061                  bool resize_ok, const T& rfv) const
01062 {
01063   Array<T> tmp = *this;
01064   if (resize_ok)
01065     {
01066       dim_vector dv = dimensions.redim (2);
01067       octave_idx_type r = dv(0), c = dv(1);
01068       octave_idx_type rx = i.extent (r), cx = j.extent (c);
01069       if (r != rx || c != cx)
01070         {
01071           if (i.is_scalar () && j.is_scalar ())
01072             return Array<T> (dim_vector (1, 1), rfv);
01073           else
01074             tmp.resize2 (rx, cx, rfv);
01075         }
01076 
01077       if (tmp.rows () != rx || tmp.columns () != cx)
01078         return Array<T> ();
01079     }
01080 
01081   return tmp.index (i, j);
01082 }
01083 
01084 template <class T>
01085 Array<T>
01086 Array<T>::index (const Array<idx_vector>& ia,
01087                  bool resize_ok, const T& rfv) const
01088 {
01089   Array<T> tmp = *this;
01090   if (resize_ok)
01091     {
01092       int ial = ia.length ();
01093       dim_vector dv = dimensions.redim (ial);
01094       dim_vector dvx = dim_vector::alloc (ial);
01095       for (int i = 0; i < ial; i++) dvx(i) = ia(i).extent (dv (i));
01096       if (! (dvx == dv))
01097         {
01098           bool all_scalars = true;
01099           for (int i = 0; i < ial; i++)
01100             all_scalars = all_scalars && ia(i).is_scalar ();
01101           if (all_scalars)
01102             return Array<T> (dim_vector (1, 1), rfv);
01103           else
01104             tmp.resize (dvx, rfv);
01105         }
01106 
01107       if (tmp.dimensions != dvx)
01108         return Array<T> ();
01109     }
01110 
01111   return tmp.index (ia);
01112 }
01113 
01114 
01115 template <class T>
01116 void
01117 Array<T>::assign (const idx_vector& i, const Array<T>& rhs, const T& rfv)
01118 {
01119   octave_idx_type n = numel (), rhl = rhs.numel ();
01120 
01121   if (rhl == 1 || i.length (n) == rhl)
01122     {
01123       octave_idx_type nx = i.extent (n);
01124       bool colon = i.is_colon_equiv (nx);
01125       // Try to resize first if necessary.
01126       if (nx != n)
01127         {
01128           // Optimize case A = []; A(1:n) = X with A empty.
01129           if (dimensions.zero_by_zero () && colon)
01130             {
01131               if (rhl == 1)
01132                 *this = Array<T> (dim_vector (1, nx), rhs(0));
01133               else
01134                 *this = Array<T> (rhs, dim_vector (1, nx));
01135               return;
01136             }
01137 
01138           resize1 (nx, rfv);
01139           n = numel ();
01140         }
01141 
01142       if (colon)
01143         {
01144           // A(:) = X makes a full fill or a shallow copy.
01145           if (rhl == 1)
01146             fill (rhs(0));
01147           else
01148             *this = rhs.reshape (dimensions);
01149         }
01150       else
01151         {
01152           if (rhl == 1)
01153             i.fill (rhs(0), n, fortran_vec ());
01154           else
01155             i.assign (rhs.data (), n, fortran_vec ());
01156         }
01157     }
01158   else
01159     gripe_invalid_assignment_size ();
01160 }
01161 
01162 template <class T>
01163 void
01164 Array<T>::assign (const idx_vector& i, const idx_vector& j,
01165                   const Array<T>& rhs, const T& rfv)
01166 {
01167   bool initial_dims_all_zero = dimensions.all_zero ();
01168 
01169   // Get RHS extents, discarding singletons.
01170   dim_vector rhdv = rhs.dims ();
01171 
01172   // Get LHS extents, allowing Fortran indexing in the second dim.
01173   dim_vector dv = dimensions.redim (2);
01174 
01175   // Check for out-of-bounds and form resizing dimensions.
01176   dim_vector rdv;
01177 
01178   // In the special when all dimensions are zero, colons are allowed
01179   // to inquire the shape of RHS.  The rules are more obscure, so we
01180   // solve that elsewhere.
01181   if (initial_dims_all_zero)
01182     rdv = zero_dims_inquire (i, j, rhdv);
01183   else
01184     {
01185       rdv(0) = i.extent (dv(0));
01186       rdv(1) = j.extent (dv(1));
01187     }
01188 
01189   bool isfill = rhs.numel () == 1;
01190   octave_idx_type il = i.length (rdv(0)), jl = j.length (rdv(1));
01191   rhdv.chop_all_singletons ();
01192   bool match = (isfill
01193                 || (rhdv.length () == 2 && il == rhdv(0) && jl == rhdv(1)));
01194   match = match || (il == 1 && jl == rhdv(0) && rhdv(1) == 1);
01195 
01196   if (match)
01197     {
01198       bool all_colons = (i.is_colon_equiv (rdv(0))
01199                          && j.is_colon_equiv (rdv(1)));
01200       // Resize if requested.
01201       if (rdv != dv)
01202         {
01203           // Optimize case A = []; A(1:m, 1:n) = X
01204           if (dv.zero_by_zero () && all_colons)
01205             {
01206               if (isfill)
01207                 *this = Array<T> (rdv, rhs(0));
01208               else
01209                 *this = Array<T> (rhs, rdv);
01210               return;
01211             }
01212 
01213           resize (rdv, rfv);
01214           dv = dimensions;
01215         }
01216 
01217       if (all_colons)
01218         {
01219           // A(:,:) = X makes a full fill or a shallow copy
01220           if (isfill)
01221             fill (rhs(0));
01222           else
01223             *this = rhs.reshape (dimensions);
01224         }
01225       else
01226         {
01227           // The actual work.
01228           octave_idx_type n = numel (), r = dv (0), c = dv (1);
01229           idx_vector ii (i);
01230 
01231           const T* src = rhs.data ();
01232           T *dest = fortran_vec ();
01233 
01234           // Try reduction first.
01235           if (ii.maybe_reduce (r, j, c))
01236             {
01237               if (isfill)
01238                 ii.fill (*src, n, dest);
01239               else
01240                 ii.assign (src, n, dest);
01241             }
01242           else
01243             {
01244               if (isfill)
01245                 {
01246                   for (octave_idx_type k = 0; k < jl; k++)
01247                     i.fill (*src, r, dest + r * j.xelem (k));
01248                 }
01249               else
01250                 {
01251                   for (octave_idx_type k = 0; k < jl; k++)
01252                     src += i.assign (src, r, dest + r * j.xelem (k));
01253                 }
01254             }
01255         }
01256     }
01257   else
01258     gripe_assignment_dimension_mismatch ();
01259 }
01260 
01261 template <class T>
01262 void
01263 Array<T>::assign (const Array<idx_vector>& ia,
01264                   const Array<T>& rhs, const T& rfv)
01265 {
01266   int ial = ia.length ();
01267 
01268   // FIXME: is this dispatching necessary / desirable?
01269   if (ial == 1)
01270     assign (ia(0), rhs, rfv);
01271   else if (ial == 2)
01272     assign (ia(0), ia(1), rhs, rfv);
01273   else if (ial > 0)
01274     {
01275       bool initial_dims_all_zero = dimensions.all_zero ();
01276 
01277       // Get RHS extents, discarding singletons.
01278       dim_vector rhdv = rhs.dims ();
01279 
01280       // Get LHS extents, allowing Fortran indexing in the second dim.
01281       dim_vector dv = dimensions.redim (ial);
01282 
01283       // Get the extents forced by indexing.
01284       dim_vector rdv;
01285 
01286       // In the special when all dimensions are zero, colons are
01287       // allowed to inquire the shape of RHS.  The rules are more
01288       // obscure, so we solve that elsewhere.
01289       if (initial_dims_all_zero)
01290         rdv = zero_dims_inquire (ia, rhdv);
01291       else
01292         {
01293           rdv = dim_vector::alloc (ial);
01294           for (int i = 0; i < ial; i++)
01295             rdv(i) = ia(i).extent (dv(i));
01296         }
01297 
01298       // Check whether LHS and RHS match, up to singleton dims.
01299       bool match = true, all_colons = true, isfill = rhs.numel () == 1;
01300 
01301       rhdv.chop_all_singletons ();
01302       int j = 0, rhdvl = rhdv.length ();
01303       for (int i = 0; i < ial; i++)
01304         {
01305           all_colons = all_colons && ia(i).is_colon_equiv (rdv(i));
01306           octave_idx_type l = ia(i).length (rdv(i));
01307           if (l == 1) continue;
01308           match = match && j < rhdvl && l == rhdv(j++);
01309         }
01310 
01311       match = match && (j == rhdvl || rhdv(j) == 1);
01312       match = match || isfill;
01313 
01314       if (match)
01315         {
01316           // Resize first if necessary.
01317           if (rdv != dv)
01318             {
01319               // Optimize case A = []; A(1:m, 1:n) = X
01320               if (dv.zero_by_zero () && all_colons)
01321                 {
01322                   rdv.chop_trailing_singletons ();
01323                   if (isfill)
01324                     *this = Array<T> (rdv, rhs(0));
01325                   else
01326                     *this = Array<T> (rhs, rdv);
01327                   return;
01328                 }
01329 
01330               resize (rdv, rfv);
01331               dv = rdv;
01332             }
01333 
01334           if (all_colons)
01335             {
01336               // A(:,:,...,:) = X makes a full fill or a shallow copy.
01337               if (isfill)
01338                 fill (rhs(0));
01339               else
01340                 *this = rhs.reshape (dimensions);
01341             }
01342           else
01343             {
01344               // Do the actual work.
01345 
01346               // Prepare for recursive indexing
01347               rec_index_helper rh (dv, ia);
01348 
01349               // Do it.
01350               if (isfill)
01351                 rh.fill (rhs(0), fortran_vec ());
01352               else
01353                 rh.assign (rhs.data (), fortran_vec ());
01354             }
01355         }
01356       else
01357         gripe_assignment_dimension_mismatch ();
01358     }
01359 }
01360 
01361 template <class T>
01362 void
01363 Array<T>::delete_elements (const idx_vector& i)
01364 {
01365   octave_idx_type n = numel ();
01366   if (i.is_colon ())
01367     {
01368       *this = Array<T> ();
01369     }
01370   else if (i.length (n) != 0)
01371     {
01372       if (i.extent (n) != n)
01373         gripe_del_index_out_of_range (true, i.extent (n), n);
01374 
01375       octave_idx_type l, u;
01376       bool col_vec = ndims () == 2 && columns () == 1 && rows () != 1;
01377       if (i.is_scalar () && i(0) == n-1 && dimensions.is_vector ())
01378         {
01379           // Stack "pop" operation.
01380           resize1 (n-1);
01381         }
01382       else if (i.is_cont_range (n, l, u))
01383         {
01384           // Special case deleting a contiguous range.
01385           octave_idx_type m = n + l - u;
01386           Array<T> tmp (dim_vector (col_vec ? m : 1, !col_vec ? m : 1));
01387           const T *src = data ();
01388           T *dest = tmp.fortran_vec ();
01389           copy_or_memcpy (l, src, dest);
01390           copy_or_memcpy (n - u, src + u, dest + l);
01391           *this = tmp;
01392         }
01393       else
01394         {
01395           // Use index.
01396           *this = index (i.complement (n));
01397         }
01398     }
01399 }
01400 
01401 template <class T>
01402 void
01403 Array<T>::delete_elements (int dim, const idx_vector& i)
01404 {
01405   if (dim < 0 || dim >= ndims ())
01406     {
01407       (*current_liboctave_error_handler)
01408         ("invalid dimension in delete_elements");
01409       return;
01410     }
01411 
01412   octave_idx_type n = dimensions (dim);
01413   if (i.is_colon ())
01414     {
01415       *this = Array<T> ();
01416     }
01417   else if (i.length (n) != 0)
01418     {
01419       if (i.extent (n) != n)
01420         gripe_del_index_out_of_range (false, i.extent (n), n);
01421 
01422       octave_idx_type l, u;
01423 
01424       if (i.is_cont_range (n, l, u))
01425         {
01426           // Special case deleting a contiguous range.
01427           octave_idx_type nd = n + l - u, dl = 1, du = 1;
01428           dim_vector rdv = dimensions;
01429           rdv(dim) = nd;
01430           for (int k = 0; k < dim; k++) dl *= dimensions(k);
01431           for (int k = dim + 1; k < ndims (); k++) du *= dimensions(k);
01432 
01433           // Special case deleting a contiguous range.
01434           Array<T> tmp = Array<T> (rdv);
01435           const T *src = data ();
01436           T *dest = tmp.fortran_vec ();
01437           l *= dl; u *= dl; n *= dl;
01438           for (octave_idx_type k = 0; k < du; k++)
01439             {
01440               copy_or_memcpy (l, src, dest);
01441               dest += l;
01442               copy_or_memcpy (n - u, src + u, dest);
01443               dest += n - u;
01444               src += n;
01445             }
01446 
01447           *this = tmp;
01448         }
01449       else
01450         {
01451           // Use index.
01452           Array<idx_vector> ia (dim_vector (ndims (), 1), idx_vector::colon);
01453           ia (dim) = i.complement (n);
01454           *this = index (ia);
01455         }
01456     }
01457 }
01458 
01459 template <class T>
01460 void
01461 Array<T>::delete_elements (const Array<idx_vector>& ia)
01462 {
01463   if (ia.length () == 1)
01464     delete_elements (ia(0));
01465   else
01466     {
01467       int len = ia.length (), k, dim = -1;
01468       for (k = 0; k < len; k++)
01469         {
01470           if (! ia(k).is_colon ())
01471             {
01472               if (dim < 0)
01473                 dim = k;
01474               else
01475                 break;
01476             }
01477         }
01478       if (dim < 0)
01479         {
01480           dim_vector dv = dimensions;
01481           dv(0) = 0;
01482           *this = Array<T> (dv);
01483         }
01484       else if (k == len)
01485         {
01486           delete_elements (dim, ia(dim));
01487         }
01488       else
01489         {
01490           (*current_liboctave_error_handler)
01491             ("a null assignment can only have one non-colon index");
01492         }
01493     }
01494 
01495 }
01496 
01497 template <class T>
01498 Array<T>&
01499 Array<T>::insert (const Array<T>& a, octave_idx_type r, octave_idx_type c)
01500 {
01501   idx_vector i (r, r + a.rows ());
01502   idx_vector j (c, c + a.columns ());
01503   if (ndims () == 2 && a.ndims () == 2)
01504     assign (i, j, a);
01505   else
01506     {
01507       Array<idx_vector> idx (dim_vector (a.ndims (), 1));
01508       idx(0) = i;
01509       idx(1) = j;
01510       for (int k = 2; k < a.ndims (); k++)
01511         idx(k) = idx_vector (0, a.dimensions(k));
01512       assign (idx, a);
01513     }
01514 
01515   return *this;
01516 }
01517 
01518 template <class T>
01519 Array<T>&
01520 Array<T>::insert (const Array<T>& a, const Array<octave_idx_type>& ra_idx)
01521 {
01522   octave_idx_type n = ra_idx.length ();
01523   Array<idx_vector> idx (dim_vector (n, 1));
01524   const dim_vector dva = a.dims ().redim (n);
01525   for (octave_idx_type k = 0; k < n; k++)
01526     idx(k) = idx_vector (ra_idx (k), ra_idx (k) + dva(k));
01527 
01528   assign (idx, a);
01529 
01530   return *this;
01531 }
01532 
01533 
01534 template <class T>
01535 Array<T>
01536 Array<T>::transpose (void) const
01537 {
01538   assert (ndims () == 2);
01539 
01540   octave_idx_type nr = dim1 ();
01541   octave_idx_type nc = dim2 ();
01542 
01543   if (nr >= 8 && nc >= 8)
01544     {
01545       Array<T> result (dim_vector (nc, nr));
01546 
01547       // Reuse the implementation used for permuting.
01548 
01549       rec_permute_helper::blk_trans (data (), result.fortran_vec (), nr, nc);
01550 
01551       return result;
01552     }
01553   else if (nr > 1 && nc > 1)
01554     {
01555       Array<T> result (dim_vector (nc, nr));
01556 
01557       for (octave_idx_type j = 0; j < nc; j++)
01558         for (octave_idx_type i = 0; i < nr; i++)
01559           result.xelem (j, i) = xelem (i, j);
01560 
01561       return result;
01562     }
01563   else
01564     {
01565       // Fast transpose for vectors and empty matrices.
01566       return Array<T> (*this, dim_vector (nc, nr));
01567     }
01568 }
01569 
01570 template <class T>
01571 static T
01572 no_op_fcn (const T& x)
01573 {
01574   return x;
01575 }
01576 
01577 template <class T>
01578 Array<T>
01579 Array<T>::hermitian (T (*fcn) (const T&)) const
01580 {
01581   assert (ndims () == 2);
01582 
01583   if (! fcn)
01584     fcn = no_op_fcn<T>;
01585 
01586   octave_idx_type nr = dim1 ();
01587   octave_idx_type nc = dim2 ();
01588 
01589   if (nr >= 8 && nc >= 8)
01590     {
01591       Array<T> result (dim_vector (nc, nr));
01592 
01593       // Blocked transpose to attempt to avoid cache misses.
01594 
01595       // Don't use OCTAVE_LOCAL_BUFFER here as it doesn't work with bool
01596       // on some compilers.
01597       T buf[64];
01598 
01599       octave_idx_type ii = 0, jj;
01600       for (jj = 0; jj < (nc - 8 + 1); jj += 8)
01601         {
01602           for (ii = 0; ii < (nr - 8 + 1); ii += 8)
01603             {
01604               // Copy to buffer
01605               for (octave_idx_type j = jj, k = 0, idxj = jj * nr;
01606                    j < jj + 8; j++, idxj += nr)
01607                 for (octave_idx_type i = ii; i < ii + 8; i++)
01608                   buf[k++] = xelem (i + idxj);
01609 
01610               // Copy from buffer
01611               for (octave_idx_type i = ii, idxi = ii * nc; i < ii + 8;
01612                    i++, idxi += nc)
01613                 for (octave_idx_type j = jj, k = i - ii; j < jj + 8;
01614                      j++, k+=8)
01615                   result.xelem (j + idxi) = fcn (buf[k]);
01616             }
01617 
01618           if (ii < nr)
01619             for (octave_idx_type j = jj; j < jj + 8; j++)
01620               for (octave_idx_type i = ii; i < nr; i++)
01621                 result.xelem (j, i) = fcn (xelem (i, j));
01622         }
01623 
01624       for (octave_idx_type j = jj; j < nc; j++)
01625         for (octave_idx_type i = 0; i < nr; i++)
01626           result.xelem (j, i) = fcn (xelem (i, j));
01627 
01628       return result;
01629     }
01630   else
01631     {
01632       Array<T> result (dim_vector (nc, nr));
01633 
01634       for (octave_idx_type j = 0; j < nc; j++)
01635         for (octave_idx_type i = 0; i < nr; i++)
01636           result.xelem (j, i) = fcn (xelem (i, j));
01637 
01638       return result;
01639     }
01640 }
01641 
01642 /*
01643 
01644 %% Tranpose tests for matrices of the tile size and plus or minus a row
01645 %% and with four tiles.
01646 
01647 %!shared m7, mt7, m8, mt8, m9, mt9
01648 %! m7 = reshape (1 : 7*8, 8, 7);
01649 %! mt7 = [1:8; 9:16; 17:24; 25:32; 33:40; 41:48; 49:56];
01650 %! m8 = reshape (1 : 8*8, 8, 8);
01651 %! mt8 = mt8 = [mt7; 57:64];
01652 %! m9 = reshape (1 : 9*8, 8, 9);
01653 %! mt9 = [mt8; 65:72];
01654 
01655 %!assert(m7', mt7)
01656 %!assert((1i*m7).', 1i * mt7)
01657 %!assert((1i*m7)', conj (1i * mt7))
01658 %!assert(m8', mt8)
01659 %!assert((1i*m8).', 1i * mt8)
01660 %!assert((1i*m8)', conj (1i * mt8))
01661 %!assert(m9', mt9)
01662 %!assert((1i*m9).', 1i * mt9)
01663 %!assert((1i*m9)', conj (1i * mt9))
01664 %!assert([m7, m8; m7, m8]', [mt7, mt7; mt8, mt8])
01665 %!assert((1i*[m7, m8; m7, m8]).', 1i * [mt7, mt7; mt8, mt8])
01666 %!assert((1i*[m7, m8; m7, m8])', conj (1i * [mt7, mt7; mt8, mt8]))
01667 %!assert([m8, m8; m8, m8]', [mt8, mt8; mt8, mt8])
01668 %!assert((1i*[m8, m8; m8, m8]).', 1i * [mt8, mt8; mt8, mt8])
01669 %!assert((1i*[m8, m8; m8, m8])', conj (1i * [mt8, mt8; mt8, mt8]))
01670 %!assert([m9, m8; m9, m8]', [mt9, mt9; mt8, mt8])
01671 %!assert((1i*[m9, m8; m9, m8]).', 1i * [mt9, mt9; mt8, mt8])
01672 %!assert((1i*[m9, m8; m9, m8])', conj (1i * [mt9, mt9; mt8, mt8]))
01673 
01674 */
01675 
01676 template <class T>
01677 T *
01678 Array<T>::fortran_vec (void)
01679 {
01680   make_unique ();
01681 
01682   return slice_data;
01683 }
01684 
01685 // Non-real types don't have NaNs.
01686 template <class T>
01687 inline bool
01688 sort_isnan (typename ref_param<T>::type)
01689 {
01690   return false;
01691 }
01692 
01693 template <class T>
01694 Array<T>
01695 Array<T>::sort (int dim, sortmode mode) const
01696 {
01697   if (dim < 0)
01698     {
01699       (*current_liboctave_error_handler)
01700         ("sort: invalid dimension");
01701       return Array<T> ();
01702     }
01703 
01704   Array<T> m (dims ());
01705 
01706   dim_vector dv = m.dims ();
01707 
01708   if (m.length () < 1)
01709     return m;
01710 
01711   if (dim >= dv.length ())
01712     dv.resize (dim+1, 1);
01713 
01714   octave_idx_type ns = dv(dim);
01715   octave_idx_type iter = dv.numel () / ns;
01716   octave_idx_type stride = 1;
01717 
01718   for (int i = 0; i < dim; i++)
01719     stride *= dv(i);
01720 
01721   T *v = m.fortran_vec ();
01722   const T *ov = data ();
01723 
01724   octave_sort<T> lsort;
01725 
01726   if (mode != UNSORTED)
01727     lsort.set_compare (mode);
01728   else
01729     return m;
01730 
01731   if (stride == 1)
01732     {
01733       for (octave_idx_type j = 0; j < iter; j++)
01734         {
01735           // copy and partition out NaNs.
01736           // FIXME: impact on integer types noticeable?
01737           octave_idx_type kl = 0, ku = ns;
01738           for (octave_idx_type i = 0; i < ns; i++)
01739             {
01740               T tmp = ov[i];
01741               if (sort_isnan<T> (tmp))
01742                 v[--ku] = tmp;
01743               else
01744                 v[kl++] = tmp;
01745             }
01746 
01747           // sort.
01748           lsort.sort (v, kl);
01749 
01750           if (ku < ns)
01751             {
01752               // NaNs are in reverse order
01753               std::reverse (v + ku, v + ns);
01754               if (mode == DESCENDING)
01755                 std::rotate (v, v + ku, v + ns);
01756             }
01757 
01758           v += ns;
01759           ov += ns;
01760         }
01761     }
01762   else
01763     {
01764       OCTAVE_LOCAL_BUFFER (T, buf, ns);
01765 
01766       for (octave_idx_type j = 0; j < iter; j++)
01767         {
01768           octave_idx_type offset = j;
01769           octave_idx_type offset2 = 0;
01770 
01771           while (offset >= stride)
01772             {
01773               offset -= stride;
01774               offset2++;
01775             }
01776 
01777           offset += offset2 * stride * ns;
01778 
01779           // gather and partition out NaNs.
01780           // FIXME: impact on integer types noticeable?
01781           octave_idx_type kl = 0, ku = ns;
01782           for (octave_idx_type i = 0; i < ns; i++)
01783             {
01784               T tmp = ov[i*stride + offset];
01785               if (sort_isnan<T> (tmp))
01786                 buf[--ku] = tmp;
01787               else
01788                 buf[kl++] = tmp;
01789             }
01790 
01791           // sort.
01792           lsort.sort (buf, kl);
01793 
01794           if (ku < ns)
01795             {
01796               // NaNs are in reverse order
01797               std::reverse (buf + ku, buf + ns);
01798               if (mode == DESCENDING)
01799                 std::rotate (buf, buf + ku, buf + ns);
01800             }
01801 
01802           // scatter.
01803           for (octave_idx_type i = 0; i < ns; i++)
01804             v[i*stride + offset] = buf[i];
01805         }
01806     }
01807 
01808   return m;
01809 }
01810 
01811 template <class T>
01812 Array<T>
01813 Array<T>::sort (Array<octave_idx_type> &sidx, int dim,
01814                 sortmode mode) const
01815 {
01816   if (dim < 0 || dim >= ndims ())
01817     {
01818       (*current_liboctave_error_handler)
01819         ("sort: invalid dimension");
01820       return Array<T> ();
01821     }
01822 
01823   Array<T> m (dims ());
01824 
01825   dim_vector dv = m.dims ();
01826 
01827   if (m.length () < 1)
01828     {
01829       sidx = Array<octave_idx_type> (dv);
01830       return m;
01831     }
01832 
01833   octave_idx_type ns = dv(dim);
01834   octave_idx_type iter = dv.numel () / ns;
01835   octave_idx_type stride = 1;
01836 
01837   for (int i = 0; i < dim; i++)
01838     stride *= dv(i);
01839 
01840   T *v = m.fortran_vec ();
01841   const T *ov = data ();
01842 
01843   octave_sort<T> lsort;
01844 
01845   sidx = Array<octave_idx_type> (dv);
01846   octave_idx_type *vi = sidx.fortran_vec ();
01847 
01848   if (mode != UNSORTED)
01849     lsort.set_compare (mode);
01850   else
01851     return m;
01852 
01853   if (stride == 1)
01854     {
01855       for (octave_idx_type j = 0; j < iter; j++)
01856         {
01857           // copy and partition out NaNs.
01858           // FIXME: impact on integer types noticeable?
01859           octave_idx_type kl = 0, ku = ns;
01860           for (octave_idx_type i = 0; i < ns; i++)
01861             {
01862               T tmp = ov[i];
01863               if (sort_isnan<T> (tmp))
01864                 {
01865                   --ku;
01866                   v[ku] = tmp;
01867                   vi[ku] = i;
01868                 }
01869               else
01870                 {
01871                   v[kl] = tmp;
01872                   vi[kl] = i;
01873                   kl++;
01874                 }
01875             }
01876 
01877           // sort.
01878           lsort.sort (v, vi, kl);
01879 
01880           if (ku < ns)
01881             {
01882               // NaNs are in reverse order
01883               std::reverse (v + ku, v + ns);
01884               std::reverse (vi + ku, vi + ns);
01885               if (mode == DESCENDING)
01886                 {
01887                   std::rotate (v, v + ku, v + ns);
01888                   std::rotate (vi, vi + ku, vi + ns);
01889                 }
01890             }
01891 
01892           v += ns;
01893           vi += ns;
01894           ov += ns;
01895         }
01896     }
01897   else
01898     {
01899       OCTAVE_LOCAL_BUFFER (T, buf, ns);
01900       OCTAVE_LOCAL_BUFFER (octave_idx_type, bufi, ns);
01901 
01902       for (octave_idx_type j = 0; j < iter; j++)
01903         {
01904           octave_idx_type offset = j;
01905           octave_idx_type offset2 = 0;
01906 
01907           while (offset >= stride)
01908             {
01909               offset -= stride;
01910               offset2++;
01911             }
01912 
01913           offset += offset2 * stride * ns;
01914 
01915           // gather and partition out NaNs.
01916           // FIXME: impact on integer types noticeable?
01917           octave_idx_type kl = 0, ku = ns;
01918           for (octave_idx_type i = 0; i < ns; i++)
01919             {
01920               T tmp = ov[i*stride + offset];
01921               if (sort_isnan<T> (tmp))
01922                 {
01923                   --ku;
01924                   buf[ku] = tmp;
01925                   bufi[ku] = i;
01926                 }
01927               else
01928                 {
01929                   buf[kl] = tmp;
01930                   bufi[kl] = i;
01931                   kl++;
01932                 }
01933             }
01934 
01935           // sort.
01936           lsort.sort (buf, bufi, kl);
01937 
01938           if (ku < ns)
01939             {
01940               // NaNs are in reverse order
01941               std::reverse (buf + ku, buf + ns);
01942               std::reverse (bufi + ku, bufi + ns);
01943               if (mode == DESCENDING)
01944                 {
01945                   std::rotate (buf, buf + ku, buf + ns);
01946                   std::rotate (bufi, bufi + ku, bufi + ns);
01947                 }
01948             }
01949 
01950           // scatter.
01951           for (octave_idx_type i = 0; i < ns; i++)
01952             v[i*stride + offset] = buf[i];
01953           for (octave_idx_type i = 0; i < ns; i++)
01954             vi[i*stride + offset] = bufi[i];
01955         }
01956     }
01957 
01958   return m;
01959 }
01960 
01961 template <class T>
01962 typename Array<T>::compare_fcn_type
01963 safe_comparator (sortmode mode, const Array<T>& /* a */,
01964                  bool /* allow_chk */)
01965 {
01966   if (mode == ASCENDING)
01967     return octave_sort<T>::ascending_compare;
01968   else if (mode == DESCENDING)
01969     return octave_sort<T>::descending_compare;
01970   else
01971     return 0;
01972 }
01973 
01974 template <class T>
01975 sortmode
01976 Array<T>::is_sorted (sortmode mode) const
01977 {
01978   octave_sort<T> lsort;
01979 
01980   octave_idx_type n = numel ();
01981 
01982   if (n <= 1)
01983     return mode ? mode : ASCENDING;
01984 
01985   if (mode == UNSORTED)
01986     {
01987       // Auto-detect mode.
01988       compare_fcn_type compare
01989         = safe_comparator (ASCENDING, *this, false);
01990 
01991       if (compare (elem (n-1), elem (0)))
01992         mode = DESCENDING;
01993       else
01994         mode = ASCENDING;
01995     }
01996 
01997   if (mode != UNSORTED)
01998     {
01999       lsort.set_compare (safe_comparator (mode, *this, false));
02000 
02001       if (! lsort.is_sorted (data (), n))
02002         mode = UNSORTED;
02003     }
02004 
02005   return mode;
02006 
02007 }
02008 
02009 template <class T>
02010 Array<octave_idx_type>
02011 Array<T>::sort_rows_idx (sortmode mode) const
02012 {
02013   Array<octave_idx_type> idx;
02014 
02015   octave_sort<T> lsort (safe_comparator (mode, *this, true));
02016 
02017   octave_idx_type r = rows (), c = cols ();
02018 
02019   idx = Array<octave_idx_type> (dim_vector (r, 1));
02020 
02021   lsort.sort_rows (data (), idx.fortran_vec (), r, c);
02022 
02023   return idx;
02024 }
02025 
02026 
02027 template <class T>
02028 sortmode
02029 Array<T>::is_sorted_rows (sortmode mode) const
02030 {
02031   octave_sort<T> lsort;
02032 
02033   octave_idx_type r = rows (), c = cols ();
02034 
02035   if (r <= 1 || c == 0)
02036     return mode ? mode : ASCENDING;
02037 
02038   if (mode == UNSORTED)
02039     {
02040       // Auto-detect mode.
02041       compare_fcn_type compare
02042         = safe_comparator (ASCENDING, *this, false);
02043 
02044       octave_idx_type i;
02045       for (i = 0; i < cols (); i++)
02046         {
02047           T l = elem (0, i), u = elem (rows () - 1, i);
02048           if (compare (l, u))
02049             {
02050               if (mode == DESCENDING)
02051                 {
02052                   mode = UNSORTED;
02053                   break;
02054                 }
02055               else
02056                 mode = ASCENDING;
02057             }
02058           else if (compare (u, l))
02059             {
02060               if (mode == ASCENDING)
02061                 {
02062                   mode = UNSORTED;
02063                   break;
02064                 }
02065               else
02066                 mode = DESCENDING;
02067             }
02068         }
02069       if (mode == UNSORTED && i == cols ())
02070         mode = ASCENDING;
02071     }
02072 
02073   if (mode != UNSORTED)
02074     {
02075       lsort.set_compare (safe_comparator (mode, *this, false));
02076 
02077       if (! lsort.is_sorted_rows (data (), r, c))
02078         mode = UNSORTED;
02079     }
02080 
02081   return mode;
02082 
02083 }
02084 
02085 // Do a binary lookup in a sorted array.
02086 template <class T>
02087 octave_idx_type
02088 Array<T>::lookup (const T& value, sortmode mode) const
02089 {
02090   octave_idx_type n = numel ();
02091   octave_sort<T> lsort;
02092 
02093   if (mode == UNSORTED)
02094     {
02095       // auto-detect mode
02096       if (n > 1 && lsort.descending_compare (elem (0), elem (n-1)))
02097         mode = DESCENDING;
02098       else
02099         mode = ASCENDING;
02100     }
02101 
02102   lsort.set_compare (mode);
02103 
02104   return lsort.lookup (data (), n, value);
02105 }
02106 
02107 template <class T>
02108 Array<octave_idx_type>
02109 Array<T>::lookup (const Array<T>& values, sortmode mode) const
02110 {
02111   octave_idx_type n = numel (), nval = values.numel ();
02112   octave_sort<T> lsort;
02113   Array<octave_idx_type> idx (values.dims ());
02114 
02115   if (mode == UNSORTED)
02116     {
02117       // auto-detect mode
02118       if (n > 1 && lsort.descending_compare (elem (0), elem (n-1)))
02119         mode = DESCENDING;
02120       else
02121         mode = ASCENDING;
02122     }
02123 
02124   lsort.set_compare (mode);
02125 
02126   // This determines the split ratio between the O(M*log2(N)) and O(M+N) algorithms.
02127   static const double ratio = 1.0;
02128   sortmode vmode = UNSORTED;
02129 
02130   // Attempt the O(M+N) algorithm if M is large enough.
02131   if (nval > ratio * n / xlog2 (n + 1.0))
02132     {
02133       vmode = values.is_sorted ();
02134       // The table must not contain a NaN.
02135       if ((vmode == ASCENDING && sort_isnan<T> (values(nval-1)))
02136           || (vmode == DESCENDING && sort_isnan<T> (values(0))))
02137         vmode = UNSORTED;
02138     }
02139 
02140   if (vmode != UNSORTED)
02141     lsort.lookup_sorted (data (), n, values.data (), nval,
02142                          idx.fortran_vec (), vmode != mode);
02143   else
02144     lsort.lookup (data (), n, values.data (), nval, idx.fortran_vec ());
02145 
02146   return idx;
02147 }
02148 
02149 template <class T>
02150 octave_idx_type
02151 Array<T>::nnz (void) const
02152 {
02153   const T *src = data ();
02154   octave_idx_type nel = nelem (), retval = 0;
02155   const T zero = T ();
02156   for (octave_idx_type i = 0; i < nel; i++)
02157     if (src[i] != zero)
02158       retval++;
02159 
02160   return retval;
02161 }
02162 
02163 template <class T>
02164 Array<octave_idx_type>
02165 Array<T>::find (octave_idx_type n, bool backward) const
02166 {
02167   Array<octave_idx_type> retval;
02168   const T *src = data ();
02169   octave_idx_type nel = nelem ();
02170   const T zero = T ();
02171   if (n < 0 || n >= nel)
02172     {
02173       // We want all elements, which means we'll almost surely need
02174       // to resize. So count first, then allocate array of exact size.
02175       octave_idx_type cnt = 0;
02176       for (octave_idx_type i = 0; i < nel; i++)
02177         cnt += src[i] != zero;
02178 
02179       retval.clear (cnt, 1);
02180       octave_idx_type *dest = retval.fortran_vec ();
02181       for (octave_idx_type i = 0; i < nel; i++)
02182         if (src[i] != zero) *dest++ = i;
02183     }
02184   else
02185     {
02186       // We want a fixed max number of elements, usually small. So be
02187       // optimistic, alloc the array in advance, and then resize if
02188       // needed.
02189       retval.clear (n, 1);
02190       if (backward)
02191         {
02192           // Do the search as a series of successive single-element searches.
02193           octave_idx_type k = 0, l = nel - 1;
02194           for (; k < n; k++)
02195             {
02196               for (;l >= 0 && src[l] == zero; l--) ;
02197               if (l >= 0)
02198                 retval(k) = l--;
02199               else
02200                 break;
02201             }
02202           if (k < n)
02203             retval.resize2 (k, 1);
02204           octave_idx_type *rdata = retval.fortran_vec ();
02205           std::reverse (rdata, rdata + k);
02206         }
02207       else
02208         {
02209           // Do the search as a series of successive single-element searches.
02210           octave_idx_type k = 0, l = 0;
02211           for (; k < n; k++)
02212             {
02213               for (;l != nel && src[l] == zero; l++) ;
02214               if (l != nel)
02215                 retval(k) = l++;
02216               else
02217                 break;
02218             }
02219           if (k < n)
02220             retval.resize2 (k, 1);
02221         }
02222     }
02223 
02224   // Fixup return dimensions, for Matlab compatibility.
02225   // find(zeros(0,0)) -> zeros(0,0)
02226   // find(zeros(1,0)) -> zeros(1,0)
02227   // find(zeros(0,1)) -> zeros(0,1)
02228   // find(zeros(0,X)) -> zeros(0,1)
02229   // find(zeros(1,1)) -> zeros(0,0) !!!! WHY?
02230   // find(zeros(0,1,0)) -> zeros(0,0)
02231   // find(zeros(0,1,0,1)) -> zeros(0,0) etc
02232 
02233   if ((numel () == 1 && retval.is_empty ())
02234       || (rows () == 0 && dims ().numel (1) == 0))
02235     retval.dimensions = dim_vector ();
02236   else if (rows () == 1 && ndims () == 2)
02237     retval.dimensions = dim_vector (1, retval.length ());
02238 
02239   return retval;
02240 }
02241 
02242 template <class T>
02243 Array<T>
02244 Array<T>::nth_element (const idx_vector& n, int dim) const
02245 {
02246   if (dim < 0)
02247     {
02248       (*current_liboctave_error_handler)
02249         ("nth_element: invalid dimension");
02250       return Array<T> ();
02251     }
02252 
02253   dim_vector dv = dims ();
02254   if (dim >= dv.length ())
02255     dv.resize (dim+1, 1);
02256 
02257   octave_idx_type ns = dv(dim);
02258 
02259   octave_idx_type nn = n.length (ns);
02260 
02261   dv(dim) = std::min (nn, ns);
02262   dv.chop_trailing_singletons ();
02263   dim = std::min (dv.length (), dim);
02264 
02265   Array<T> m (dv);
02266 
02267   if (m.numel () == 0)
02268     return m;
02269 
02270   sortmode mode = UNSORTED;
02271   octave_idx_type lo = 0;
02272 
02273   switch (n.idx_class ())
02274     {
02275     case idx_vector::class_scalar:
02276       mode = ASCENDING;
02277       lo = n(0);
02278       break;
02279     case idx_vector::class_range:
02280       {
02281         octave_idx_type inc = n.increment ();
02282         if (inc == 1)
02283           {
02284             mode = ASCENDING;
02285             lo = n(0);
02286           }
02287         else if (inc == -1)
02288           {
02289             mode = DESCENDING;
02290             lo = ns - 1 - n(0);
02291           }
02292       }
02293     default:
02294       break;
02295     }
02296 
02297   if (mode == UNSORTED)
02298     {
02299       (*current_liboctave_error_handler)
02300         ("nth_element: n must be a scalar or a contiguous range");
02301       return Array<T> ();
02302     }
02303 
02304   octave_idx_type up = lo + nn;
02305 
02306   if (lo < 0 || up > ns)
02307     {
02308       (*current_liboctave_error_handler)
02309         ("nth_element: invalid element index");
02310       return Array<T> ();
02311     }
02312 
02313   octave_idx_type iter = numel () / ns;
02314   octave_idx_type stride = 1;
02315 
02316   for (int i = 0; i < dim; i++)
02317     stride *= dv(i);
02318 
02319   T *v = m.fortran_vec ();
02320   const T *ov = data ();
02321 
02322   OCTAVE_LOCAL_BUFFER (T, buf, ns);
02323 
02324   octave_sort<T> lsort;
02325   lsort.set_compare (mode);
02326 
02327   for (octave_idx_type j = 0; j < iter; j++)
02328     {
02329       octave_idx_type kl = 0, ku = ns;
02330 
02331       if (stride == 1)
02332         {
02333           // copy without NaNs.
02334           // FIXME: impact on integer types noticeable?
02335           for (octave_idx_type i = 0; i < ns; i++)
02336             {
02337               T tmp = ov[i];
02338               if (sort_isnan<T> (tmp))
02339                 buf[--ku] = tmp;
02340               else
02341                 buf[kl++] = tmp;
02342             }
02343 
02344           ov += ns;
02345         }
02346       else
02347         {
02348           octave_idx_type offset = j % stride;
02349           // copy without NaNs.
02350           // FIXME: impact on integer types noticeable?
02351           for (octave_idx_type i = 0; i < ns; i++)
02352             {
02353               T tmp = ov[offset + i*stride];
02354               if (sort_isnan<T> (tmp))
02355                 buf[--ku] = tmp;
02356               else
02357                 buf[kl++] = tmp;
02358             }
02359 
02360           if (offset == stride-1)
02361             ov += ns*stride;
02362         }
02363 
02364       if (ku == ns)
02365           lsort.nth_element (buf, ns, lo, up);
02366       else if (mode == ASCENDING)
02367         lsort.nth_element (buf, ku, lo, std::min (ku, up));
02368       else
02369         {
02370           octave_idx_type nnan = ns - ku;
02371           octave_idx_type zero = 0;
02372           lsort.nth_element (buf, ku, std::max (lo - nnan, zero),
02373                              std::max (up - nnan, zero));
02374           std::rotate (buf, buf + ku, buf + ns);
02375         }
02376 
02377       if (stride == 1)
02378         {
02379           for (octave_idx_type i = 0; i < nn; i++)
02380             v[i] = buf[lo + i];
02381 
02382           v += nn;
02383         }
02384       else
02385         {
02386           octave_idx_type offset = j % stride;
02387           for (octave_idx_type i = 0; i < nn; i++)
02388             v[offset + stride * i] = buf[lo + i];
02389           if (offset == stride-1)
02390             v += nn*stride;
02391         }
02392     }
02393 
02394   return m;
02395 }
02396 
02397 
02398 #define INSTANTIATE_ARRAY_SORT(T) template class OCTAVE_API octave_sort<T>;
02399 
02400 #define NO_INSTANTIATE_ARRAY_SORT(T) \
02401  \
02402 template <> Array<T>  \
02403 Array<T>::sort (int, sortmode) const { return *this; } \
02404  \
02405 template <> Array<T>  \
02406 Array<T>::sort (Array<octave_idx_type> &sidx, int, sortmode) const \
02407 { sidx = Array<octave_idx_type> (); return *this; } \
02408  \
02409 template <> sortmode  \
02410 Array<T>::is_sorted (sortmode) const  \
02411 { return UNSORTED; } \
02412  \
02413 Array<T>::compare_fcn_type \
02414 safe_comparator (sortmode, const Array<T>&, bool) \
02415 { return 0; } \
02416  \
02417 template <> Array<octave_idx_type>  \
02418 Array<T>::sort_rows_idx (sortmode) const  \
02419 { return Array<octave_idx_type> (); } \
02420  \
02421 template <> sortmode  \
02422 Array<T>::is_sorted_rows (sortmode) const \
02423 { return UNSORTED; } \
02424  \
02425 template <> octave_idx_type  \
02426 Array<T>::lookup (T const &, sortmode) const \
02427 { return 0; } \
02428 template <> Array<octave_idx_type>  \
02429 Array<T>::lookup (const Array<T>&, sortmode) const \
02430 { return Array<octave_idx_type> (); } \
02431  \
02432 template <> octave_idx_type \
02433 Array<T>::nnz (void) const\
02434 { return 0; } \
02435 template <> Array<octave_idx_type> \
02436 Array<T>::find (octave_idx_type, bool) const\
02437 { return Array<octave_idx_type> (); } \
02438  \
02439 template <> Array<T>  \
02440 Array<T>::nth_element (const idx_vector&, int) const { return Array<T> (); } \
02441 
02442 
02443 template <class T>
02444 Array<T>
02445 Array<T>::diag (octave_idx_type k) const
02446 {
02447   dim_vector dv = dims ();
02448   octave_idx_type nd = dv.length ();
02449   Array<T> d;
02450 
02451   if (nd > 2)
02452     (*current_liboctave_error_handler) ("Matrix must be 2-dimensional");
02453   else
02454     {
02455       octave_idx_type nnr = dv (0);
02456       octave_idx_type nnc = dv (1);
02457 
02458       if (nnr == 0 && nnc == 0)
02459         ; // do nothing for empty matrix
02460       else if (nnr != 1 && nnc != 1)
02461         {
02462           // Extract diag from matrix
02463           if (k > 0)
02464             nnc -= k;
02465           else if (k < 0)
02466             nnr += k;
02467 
02468           if (nnr > 0 && nnc > 0)
02469             {
02470               octave_idx_type ndiag = (nnr < nnc) ? nnr : nnc;
02471 
02472               d.resize (dim_vector (ndiag, 1));
02473 
02474               if (k > 0)
02475                 {
02476                   for (octave_idx_type i = 0; i < ndiag; i++)
02477                     d.xelem (i) = elem (i, i+k);
02478                 }
02479               else if (k < 0)
02480                 {
02481                   for (octave_idx_type i = 0; i < ndiag; i++)
02482                     d.xelem (i) = elem (i-k, i);
02483                 }
02484               else
02485                 {
02486                   for (octave_idx_type i = 0; i < ndiag; i++)
02487                     d.xelem (i) = elem (i, i);
02488                 }
02489             }
02490           else
02491             (*current_liboctave_error_handler)
02492               ("diag: requested diagonal out of range");
02493         }
02494       else
02495         {
02496           // Create diag matrix from vector
02497           octave_idx_type roff = 0;
02498           octave_idx_type coff = 0;
02499           if (k > 0)
02500             {
02501               roff = 0;
02502               coff = k;
02503             }
02504           else if (k < 0)
02505             {
02506               roff = -k;
02507               coff = 0;
02508             }
02509 
02510           if (nnr == 1)
02511             {
02512               octave_idx_type n = nnc + std::abs (k);
02513               d = Array<T> (dim_vector (n, n), resize_fill_value ());
02514 
02515               for (octave_idx_type i = 0; i < nnc; i++)
02516                 d.xelem (i+roff, i+coff) = elem (0, i);
02517             }
02518           else
02519             {
02520               octave_idx_type n = nnr + std::abs (k);
02521               d = Array<T> (dim_vector (n, n), resize_fill_value ());
02522 
02523               for (octave_idx_type i = 0; i < nnr; i++)
02524                 d.xelem (i+roff, i+coff) = elem (i, 0);
02525             }
02526         }
02527     }
02528 
02529   return d;
02530 }
02531 
02532 template <class T>
02533 Array<T>
02534 Array<T>::cat (int dim, octave_idx_type n, const Array<T> *array_list)
02535 {
02536   // Default concatenation.
02537   bool (dim_vector::*concat_rule) (const dim_vector&, int) = &dim_vector::concat;
02538 
02539   if (dim == -1 || dim == -2)
02540     {
02541       concat_rule = &dim_vector::hvcat;
02542       dim = -dim - 1;
02543     }
02544   else if (dim < 0)
02545     (*current_liboctave_error_handler)
02546       ("cat: invalid dimension");
02547 
02548   if (n == 1)
02549     return array_list[0];
02550   else if (n == 0)
02551     return Array<T> ();
02552 
02553   // Special case:
02554   //
02555   //   cat (dim, [], ..., [], A, ...)
02556   //
02557   // with dim > 2, A not 0x0, and at least three arguments to
02558   // concatenate is equivalent to
02559   //
02560   //   cat (dim, A, ...)
02561   //
02562   // Note that this check must be performed here because for full-on
02563   // braindead Matlab compatibility, we need to have things like
02564   //
02565   //   cat (3, [], [], A)
02566   //
02567   // succeed, but to have things like
02568   //
02569   //   cat (3, cat (3, [], []), A)
02570   //   cat (3, zeros (0, 0, 2), A)
02571   //
02572   // fail.  See also bug report #31615.
02573 
02574   octave_idx_type istart = 0;
02575 
02576   if (n > 2 && dim > 1)
02577     {
02578       for (octave_idx_type i = 0; i < n; i++)
02579         {
02580           dim_vector dv = array_list[i].dims ();
02581 
02582           if (dv.zero_by_zero ())
02583             istart++;
02584           else
02585             break;
02586         }
02587 
02588       // Don't skip any initial aguments if they are all empty.
02589       if (istart >= n)
02590         istart = 0;
02591     }
02592 
02593   dim_vector dv = array_list[istart++].dims ();
02594 
02595   for (octave_idx_type i = istart; i < n; i++)
02596     if (! (dv.*concat_rule) (array_list[i].dims (), dim))
02597       (*current_liboctave_error_handler)
02598         ("cat: dimension mismatch");
02599 
02600   Array<T> retval (dv);
02601 
02602   if (retval.is_empty ())
02603     return retval;
02604 
02605   int nidx = std::max (dv.length (), dim + 1);
02606   Array<idx_vector> idxa (dim_vector (nidx, 1), idx_vector::colon);
02607   octave_idx_type l = 0;
02608 
02609   for (octave_idx_type i = 0; i < n; i++)
02610     {
02611       // NOTE: This takes some thinking, but no matter what the above rules
02612       // are, an empty array can always be skipped at this point, because
02613       // the result dimensions are already determined, and there is no way
02614       // an empty array may contribute a nonzero piece along the dimension
02615       // at this point, unless an empty array can be promoted to a non-empty
02616       // one (which makes no sense). I repeat, *no way*, think about it.
02617       if (array_list[i].is_empty ())
02618         continue;
02619 
02620       octave_quit ();
02621 
02622       octave_idx_type u;
02623       if (dim < array_list[i].ndims ())
02624         u = l + array_list[i].dims ()(dim);
02625       else
02626         u = l + 1;
02627 
02628       idxa(dim) = idx_vector (l, u);
02629 
02630       retval.assign (idxa, array_list[i]);
02631 
02632       l = u;
02633     }
02634 
02635   return retval;
02636 }
02637 
02638 template <class T>
02639 void
02640 Array<T>::print_info (std::ostream& os, const std::string& prefix) const
02641 {
02642   os << prefix << "rep address: " << rep << '\n'
02643      << prefix << "rep->len:    " << rep->len << '\n'
02644      << prefix << "rep->data:   " << static_cast<void *> (rep->data) << '\n'
02645      << prefix << "rep->count:  " << rep->count << '\n'
02646      << prefix << "slice_data:  " << static_cast<void *> (slice_data) << '\n'
02647      << prefix << "slice_len:   " << slice_len << '\n';
02648 
02649   // 2D info:
02650   //
02651   //     << pefix << "rows: " << rows () << "\n"
02652   //     << prefix << "cols: " << cols () << "\n";
02653 }
02654 
02655 template <class T>
02656 bool Array<T>::optimize_dimensions (const dim_vector& dv)
02657 {
02658   bool retval = dimensions == dv;
02659   if (retval)
02660     dimensions = dv;
02661 
02662   return retval;
02663 }
02664 
02665 template <class T>
02666 void Array<T>::instantiation_guard ()
02667 {
02668   // This guards against accidental implicit instantiations.
02669   // Array<T> instances should always be explicit and use INSTANTIATE_ARRAY.
02670   T::__xXxXx__();
02671 }
02672 
02673 #define INSTANTIATE_ARRAY(T, API) \
02674   template <> void Array<T>::instantiation_guard () { } \
02675   template class API Array<T>
02676 
02677 // FIXME: is this used?
02678 
02679 template <class T>
02680 std::ostream&
02681 operator << (std::ostream& os, const Array<T>& a)
02682 {
02683   dim_vector a_dims = a.dims ();
02684 
02685   int n_dims = a_dims.length ();
02686 
02687   os << n_dims << "-dimensional array";
02688 
02689   if (n_dims)
02690     os << " (" << a_dims.str () << ")";
02691 
02692   os <<"\n\n";
02693 
02694   if (n_dims)
02695     {
02696       os << "data:";
02697 
02698       Array<octave_idx_type> ra_idx (dim_vector (n_dims, 1), 0);
02699 
02700       // Number of times the first 2d-array is to be displayed.
02701 
02702       octave_idx_type m = 1;
02703       for (int i = 2; i < n_dims; i++)
02704         m *= a_dims(i);
02705 
02706       if (m == 1)
02707         {
02708           octave_idx_type rows = 0;
02709           octave_idx_type cols = 0;
02710 
02711           switch (n_dims)
02712             {
02713             case 2:
02714               rows = a_dims(0);
02715               cols = a_dims(1);
02716 
02717               for (octave_idx_type j = 0; j < rows; j++)
02718                 {
02719                   ra_idx(0) = j;
02720                   for (octave_idx_type k = 0; k < cols; k++)
02721                     {
02722                       ra_idx(1) = k;
02723                       os << " " << a.elem(ra_idx);
02724                     }
02725                   os << "\n";
02726                 }
02727               break;
02728 
02729             default:
02730               rows = a_dims(0);
02731 
02732               for (octave_idx_type k = 0; k < rows; k++)
02733                 {
02734                   ra_idx(0) = k;
02735                   os << " " << a.elem(ra_idx);
02736                 }
02737               break;
02738             }
02739 
02740           os << "\n";
02741         }
02742       else
02743         {
02744           octave_idx_type rows = a_dims(0);
02745           octave_idx_type cols = a_dims(1);
02746 
02747           for (int i = 0; i < m; i++)
02748             {
02749               os << "\n(:,:,";
02750 
02751               for (int j = 2; j < n_dims - 1; j++)
02752                 os << ra_idx(j) + 1 << ",";
02753 
02754               os << ra_idx(n_dims - 1) + 1 << ") = \n";
02755 
02756               for (octave_idx_type j = 0; j < rows; j++)
02757                 {
02758                   ra_idx(0) = j;
02759 
02760                   for (octave_idx_type k = 0; k < cols; k++)
02761                     {
02762                       ra_idx(1) = k;
02763                       os << " " << a.elem(ra_idx);
02764                     }
02765 
02766                   os << "\n";
02767                 }
02768 
02769               os << "\n";
02770 
02771               if (i != m - 1)
02772                 increment_index (ra_idx, a_dims, 2);
02773             }
02774         }
02775     }
02776 
02777   return os;
02778 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines