changeset 30791:b1301358b040 stable

allocators for Sparse<T> * Sparse-fwd.h: Update forward declarations to include allocator template parameter. Use std::pmr::polymorphic_allocator if it is available. * Sparse.h, Sparse.cc (Sparse<T>): New allocator template parameter. Update all declarations and member function definitions as needed. (Sparse<T>::SparseRep): Derive from Sparse<T> allocator template parameter. (Alloc_traits, T_Alloc_traits, T_pointer, idx_type_Alloc_traits, idx_type_pointer): New typedefs in SparseRep class. (m_data, m_ridx, m_cidx): Declare with typedef types. (SparseRep::T_allocate, SparseRep::T_deallocate, SparseRep::idx_type_allocate, SparseRep::idx_type_deallocate): New allocator functions. (SparseRep::SparseRep): Update to use allocator functions instead of new. (SparseRep:~SparseRep): Use allocator functions instead of delete[].
author John W. Eaton <jwe@octave.org>
date Mon, 28 Feb 2022 13:56:18 -0500
parents 290e7e3f859f
children 4fc0b7269803
files liboctave/array/Sparse-fwd.h liboctave/array/Sparse.cc liboctave/array/Sparse.h
diffstat 3 files changed, 356 insertions(+), 272 deletions(-) [+]
line wrap: on
line diff
--- a/liboctave/array/Sparse-fwd.h	Sun Feb 20 20:21:12 2022 +0100
+++ b/liboctave/array/Sparse-fwd.h	Mon Feb 28 13:56:18 2022 -0500
@@ -28,6 +28,17 @@
 
 #include "octave-config.h"
 
-template <typename T> class OCTAVE_API Sparse;
+#if defined (OCTAVE_HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+#  include <memory_resource>
+
+template <typename T, typename Alloc = std::pmr::polymorphic_allocator<T>>
+class OCTAVE_API Sparse;
+
+#else
+
+template <typename T, typename Alloc = std::allocator<T>>
+class OCTARRAY_API Sparse;
 
 #endif
+
+#endif
--- a/liboctave/array/Sparse.cc	Sun Feb 20 20:21:12 2022 +0100
+++ b/liboctave/array/Sparse.cc	Mon Feb 28 13:56:18 2022 -0500
@@ -52,18 +52,18 @@
 
 #include "PermMatrix.h"
 
-template <typename T>
-OCTAVE_API typename Sparse<T>::SparseRep *
-Sparse<T>::nil_rep (void)
+template <typename T, typename Alloc>
+OCTAVE_API typename Sparse<T, Alloc>::SparseRep *
+Sparse<T, Alloc>::nil_rep (void)
 {
-  static typename Sparse<T>::SparseRep nr;
+  static typename Sparse<T, Alloc>::SparseRep nr;
   return &nr;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T&
-Sparse<T>::SparseRep::elem (octave_idx_type r, octave_idx_type c)
+Sparse<T, Alloc>::SparseRep::elem (octave_idx_type r, octave_idx_type c)
 {
   octave_idx_type i;
 
@@ -102,10 +102,10 @@
   return m_data[i];
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T
-Sparse<T>::SparseRep::celem (octave_idx_type r, octave_idx_type c) const
+Sparse<T, Alloc>::SparseRep::celem (octave_idx_type r, octave_idx_type c) const
 {
   if (m_nzmax > 0)
     for (octave_idx_type i = m_cidx[c]; i < m_cidx[c + 1]; i++)
@@ -114,10 +114,10 @@
   return T ();
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::SparseRep::maybe_compress (bool remove_zeros)
+Sparse<T, Alloc>::SparseRep::maybe_compress (bool remove_zeros)
 {
   if (remove_zeros)
     {
@@ -139,10 +139,10 @@
   change_length (m_cidx[m_ncols]);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::SparseRep::change_length (octave_idx_type nz)
+Sparse<T, Alloc>::SparseRep::change_length (octave_idx_type nz)
 {
   for (octave_idx_type j = m_ncols; j > 0 && m_cidx[j] > nz; j--)
     m_cidx[j] = nz;
@@ -157,34 +157,34 @@
       // Reallocate.
       octave_idx_type min_nzmax = std::min (nz, m_nzmax);
 
-      octave_idx_type *new_ridx = new octave_idx_type [nz];
+      octave_idx_type *new_ridx = idx_type_allocate (nz);
       std::copy_n (m_ridx, min_nzmax, new_ridx);
 
-      delete [] m_ridx;
+      idx_type_deallocate (m_ridx, m_nzmax);
       m_ridx = new_ridx;
 
-      T *new_data = new T [nz];
+      T *new_data = T_allocate (nz);
       std::copy_n (m_data, min_nzmax, new_data);
 
-      delete [] m_data;
+      T_deallocate (m_data, m_nzmax);
       m_data = new_data;
 
       m_nzmax = nz;
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 bool
-Sparse<T>::SparseRep::indices_ok (void) const
+Sparse<T, Alloc>::SparseRep::indices_ok (void) const
 {
   return sparse_indices_ok (m_ridx, m_cidx, m_nrows, m_ncols, nnz ());
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 bool
-Sparse<T>::SparseRep::any_element_is_nan (void) const
+Sparse<T, Alloc>::SparseRep::any_element_is_nan (void) const
 {
   octave_idx_type nz = nnz ();
 
@@ -195,14 +195,15 @@
   return false;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>::Sparse (octave_idx_type nr, octave_idx_type nc, T val)
+Sparse<T, Alloc>::Sparse (octave_idx_type nr, octave_idx_type nc,
+                                        T val)
   : m_rep (nullptr), m_dimensions (dim_vector (nr, nc))
 {
   if (val != T ())
     {
-      m_rep = new typename Sparse<T>::SparseRep (nr, nc, m_dimensions.safe_numel ());
+      m_rep = new typename Sparse<T, Alloc>::SparseRep (nr, nc, m_dimensions.safe_numel ());
 
       octave_idx_type ii = 0;
       xcidx (0) = 0;
@@ -218,16 +219,16 @@
     }
   else
     {
-      m_rep = new typename Sparse<T>::SparseRep (nr, nc, 0);
+      m_rep = new typename Sparse<T, Alloc>::SparseRep (nr, nc, 0);
       for (octave_idx_type j = 0; j < nc+1; j++)
         xcidx (j) = 0;
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>::Sparse (const PermMatrix& a)
-  : m_rep (new typename Sparse<T>::SparseRep (a.rows (), a.cols (), a.rows ())),
+Sparse<T, Alloc>::Sparse (const PermMatrix& a)
+  : m_rep (new typename Sparse<T, Alloc>::SparseRep (a.rows (), a.cols (), a.rows ())),
     m_dimensions (dim_vector (a.rows (), a.cols ()))
 {
   octave_idx_type n = a.rows ();
@@ -243,21 +244,21 @@
     data (i) = 1.0;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>::Sparse (const dim_vector& dv)
+Sparse<T, Alloc>::Sparse (const dim_vector& dv)
   : m_rep (nullptr), m_dimensions (dv)
 {
   if (dv.ndims () != 2)
     (*current_liboctave_error_handler)
       ("Sparse::Sparse (const dim_vector&): dimension mismatch");
 
-  m_rep = new typename Sparse<T>::SparseRep (dv(0), dv(1), 0);
+  m_rep = new typename Sparse<T, Alloc>::SparseRep (dv(0), dv(1), 0);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>::Sparse (const Sparse<T>& a, const dim_vector& dv)
+Sparse<T, Alloc>::Sparse (const Sparse<T, Alloc>& a, const dim_vector& dv)
   : m_rep (nullptr), m_dimensions (dv)
 {
 
@@ -278,7 +279,7 @@
   octave_idx_type old_nr = old_dims(0);
   octave_idx_type old_nc = old_dims(1);
 
-  m_rep = new typename Sparse<T>::SparseRep (new_nr, new_nc, new_nzmax);
+  m_rep = new typename Sparse<T, Alloc>::SparseRep (new_nr, new_nc, new_nzmax);
 
   octave_idx_type kk = 0;
   xcidx (0) = 0;
@@ -298,12 +299,13 @@
     xcidx (k+1) = new_nzmax;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>::Sparse (const Array<T>& a, const octave::idx_vector& r,
-                   const octave::idx_vector& c, octave_idx_type nr,
-                   octave_idx_type nc, bool sum_terms,
-                   octave_idx_type nzm)
+Sparse<T, Alloc>::Sparse (const Array<T>& a,
+                          const octave::idx_vector& r,
+                          const octave::idx_vector& c,
+                          octave_idx_type nr, octave_idx_type nc,
+                          bool sum_terms, octave_idx_type nzm)
   : m_rep (nullptr), m_dimensions ()
 {
   if (nr < 0)
@@ -338,7 +340,7 @@
     (*current_liboctave_error_handler) ("sparse: dimension mismatch");
 
   // Only create m_rep after input validation to avoid memory leak.
-  m_rep = new typename Sparse<T>::SparseRep (nr, nc, (nzm > 0 ? nzm : 0));
+  m_rep = new typename Sparse<T, Alloc>::SparseRep (nr, nc, (nzm > 0 ? nzm : 0));
 
   if (rl <= 1 && cl <= 1)
     {
@@ -666,9 +668,9 @@
 %!assert <*51880> (sparse (1:2, 2, 1:2, 2, 3), sparse ([0, 1, 0; 0, 2, 0]))
 */
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>::Sparse (const Array<T>& a)
+Sparse<T, Alloc>::Sparse (const Array<T>& a)
   : m_rep (nullptr), m_dimensions (a.dims ())
 {
   if (m_dimensions.ndims () > 2)
@@ -685,7 +687,7 @@
     if (a(i) != T ())
       new_nzmax++;
 
-  m_rep = new typename Sparse<T>::SparseRep (nr, nc, new_nzmax);
+  m_rep = new typename Sparse<T, Alloc>::SparseRep (nr, nc, new_nzmax);
 
   octave_idx_type ii = 0;
   xcidx (0) = 0;
@@ -701,17 +703,17 @@
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>::~Sparse (void)
+Sparse<T, Alloc>::~Sparse (void)
 {
   if (--m_rep->m_count == 0)
     delete m_rep;
 }
 
-template <typename T>
-Sparse<T>&
-Sparse<T>::operator = (const Sparse<T>& a)
+template <typename T, typename Alloc>
+Sparse<T, Alloc>&
+Sparse<T, Alloc>::operator = (const Sparse<T, Alloc>& a)
 {
   if (this != &a)
     {
@@ -727,16 +729,16 @@
   return *this;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 octave_idx_type
-Sparse<T>::compute_index (const Array<octave_idx_type>& ra_idx) const
+Sparse<T, Alloc>::compute_index (const Array<octave_idx_type>& ra_idx) const
 {
   octave_idx_type n = m_dimensions.ndims ();
 
   if (n <= 0 || n != ra_idx.numel ())
     (*current_liboctave_error_handler)
-      ("Sparse<T>::compute_index: invalid ra_idxing operation");
+      ("Sparse<T, Alloc>::compute_index: invalid ra_idxing operation");
 
   octave_idx_type retval = -1;
 
@@ -751,50 +753,51 @@
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T
-Sparse<T>::range_error (const char *fcn, octave_idx_type n) const
+Sparse<T, Alloc>::range_error (const char *fcn, octave_idx_type n) const
 {
   (*current_liboctave_error_handler) ("%s (%" OCTAVE_IDX_TYPE_FORMAT "): "
                                       "range error", fcn, n);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T&
-Sparse<T>::range_error (const char *fcn, octave_idx_type n)
+Sparse<T, Alloc>::range_error (const char *fcn, octave_idx_type n)
 {
   (*current_liboctave_error_handler) ("%s (%" OCTAVE_IDX_TYPE_FORMAT "): "
                                       "range error", fcn, n);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T
-Sparse<T>::range_error (const char *fcn, octave_idx_type i,
-                        octave_idx_type j) const
+Sparse<T, Alloc>::range_error (const char *fcn, octave_idx_type i,
+                                             octave_idx_type j) const
 {
   (*current_liboctave_error_handler)
     ("%s (%" OCTAVE_IDX_TYPE_FORMAT ", %" OCTAVE_IDX_TYPE_FORMAT "): "
      "range error", fcn, i, j);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T&
-Sparse<T>::range_error (const char *fcn, octave_idx_type i, octave_idx_type j)
+Sparse<T, Alloc>::range_error (const char *fcn, octave_idx_type i,
+                                             octave_idx_type j)
 {
   (*current_liboctave_error_handler)
     ("%s (%" OCTAVE_IDX_TYPE_FORMAT ", %" OCTAVE_IDX_TYPE_FORMAT "): "
      "range error", fcn, i, j);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T
-Sparse<T>::range_error (const char *fcn,
-                        const Array<octave_idx_type>& ra_idx) const
+Sparse<T, Alloc>::range_error (const char *fcn,
+                                             const Array<octave_idx_type>& ra_idx) const
 {
   std::ostringstream buf;
 
@@ -815,10 +818,11 @@
   (*current_liboctave_error_handler) ("%s", buf_str.c_str ());
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 T&
-Sparse<T>::range_error (const char *fcn, const Array<octave_idx_type>& ra_idx)
+Sparse<T, Alloc>::range_error (const char *fcn,
+                               const Array<octave_idx_type>& ra_idx)
 {
   std::ostringstream buf;
 
@@ -839,12 +843,12 @@
   (*current_liboctave_error_handler) ("%s", buf_str.c_str ());
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::reshape (const dim_vector& new_dims) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::reshape (const dim_vector& new_dims) const
 {
-  Sparse<T> retval;
+  Sparse<T, Alloc> retval;
   dim_vector dims2 = new_dims;
 
   if (dims2.ndims () > 2)
@@ -868,7 +872,7 @@
           octave_idx_type new_nc = dims2 (1);
           octave_idx_type old_nr = rows ();
           octave_idx_type old_nc = cols ();
-          retval = Sparse<T> (new_nr, new_nc, new_nnz);
+          retval = Sparse<T, Alloc> (new_nr, new_nc, new_nnz);
 
           octave_idx_type kk = 0;
           retval.xcidx (0) = 0;
@@ -919,10 +923,10 @@
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::permute (const Array<octave_idx_type>& perm_vec, bool) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::permute (const Array<octave_idx_type>& perm_vec, bool) const
 {
   // The only valid permutations of a sparse array are [1, 2] and [2, 1].
 
@@ -948,10 +952,10 @@
   return trans ? this->transpose () : *this;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::resize1 (octave_idx_type n)
+Sparse<T, Alloc>::resize1 (octave_idx_type n)
 {
   octave_idx_type nr = rows ();
   octave_idx_type nc = cols ();
@@ -968,10 +972,10 @@
     octave::err_invalid_resize ();
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::resize (const dim_vector& dv)
+Sparse<T, Alloc>::resize (const dim_vector& dv)
 {
   octave_idx_type n = dv.ndims ();
 
@@ -981,10 +985,10 @@
   resize (dv(0), dv(1));
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::resize (octave_idx_type r, octave_idx_type c)
+Sparse<T, Alloc>::resize (octave_idx_type r, octave_idx_type c)
 {
   if (r < 0 || c < 0)
     (*current_liboctave_error_handler) ("can't resize to negative dimension");
@@ -1017,9 +1021,9 @@
 
   if (c != m_rep->m_ncols)
     {
-      octave_idx_type *new_cidx = new octave_idx_type [c+1];
+      octave_idx_type *new_cidx = m_rep->idx_type_allocate (c+1);
       std::copy_n (m_rep->m_cidx, std::min (c, m_rep->m_ncols) + 1, new_cidx);
-      delete [] m_rep->m_cidx;
+      m_rep->idx_type_deallocate (m_rep->m_cidx, m_rep->m_ncols);
       m_rep->m_cidx = new_cidx;
 
       if (c > m_rep->m_ncols)
@@ -1032,10 +1036,11 @@
   m_rep->change_length (m_rep->nnz ());
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>&
-Sparse<T>::insert (const Sparse<T>& a, octave_idx_type r, octave_idx_type c)
+Sparse<T, Alloc>&
+Sparse<T, Alloc>::insert (const Sparse<T, Alloc>& a,
+                          octave_idx_type r, octave_idx_type c)
 {
   octave_idx_type a_rows = a.rows ();
   octave_idx_type a_cols = a.cols ();
@@ -1056,9 +1061,9 @@
       if (ridx (j) < r || ridx (j) >= r + a_rows)
         nel++;
 
-  Sparse<T> tmp (*this);
+  Sparse<T, Alloc> tmp (*this);
   --m_rep->m_count;
-  m_rep = new typename Sparse<T>::SparseRep (nr, nc, nel);
+  m_rep = new typename Sparse<T, Alloc>::SparseRep (nr, nc, nel);
 
   for (octave_idx_type i = 0; i < tmp.cidx (c); i++)
     {
@@ -1114,10 +1119,11 @@
   return *this;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>&
-Sparse<T>::insert (const Sparse<T>& a, const Array<octave_idx_type>& ra_idx)
+Sparse<T, Alloc>&
+Sparse<T, Alloc>::insert (const Sparse<T, Alloc>& a,
+                          const Array<octave_idx_type>& ra_idx)
 {
 
   if (ra_idx.numel () != 2)
@@ -1126,17 +1132,17 @@
   return insert (a, ra_idx(0), ra_idx(1));
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::transpose (void) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::transpose (void) const
 {
   assert (ndims () == 2);
 
   octave_idx_type nr = rows ();
   octave_idx_type nc = cols ();
   octave_idx_type nz = nnz ();
-  Sparse<T> retval (nc, nr, nz);
+  Sparse<T, Alloc> retval (nc, nr, nz);
 
   for (octave_idx_type i = 0; i < nz; i++)
     retval.xcidx (ridx (i) + 1)++;
@@ -1184,12 +1190,12 @@
     return std::lower_bound (ridx, ridx + nr, ri) - ridx;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::delete_elements (const octave::idx_vector& idx)
+Sparse<T, Alloc>::delete_elements (const octave::idx_vector& idx)
 {
-  Sparse<T> retval;
+  Sparse<T, Alloc> retval;
 
   assert (ndims () == 2);
 
@@ -1207,7 +1213,7 @@
   if (nc == 1)
     {
       // Sparse column vector.
-      const Sparse<T> tmp = *this; // constant copy to prevent COW.
+      const Sparse<T, Alloc> tmp = *this; // constant copy to prevent COW.
 
       octave_idx_type lb, ub;
 
@@ -1219,7 +1225,7 @@
           octave_idx_type ui = lblookup (tmp.ridx (), nz, ub);
           // Copy data and adjust indices.
           octave_idx_type nz_new = nz - (ui - li);
-          *this = Sparse<T> (nr - (ub - lb), 1, nz_new);
+          *this = Sparse<T, Alloc> (nr - (ub - lb), 1, nz_new);
           std::copy_n (tmp.data (), li, data ());
           std::copy_n (tmp.ridx (), li, xridx ());
           std::copy (tmp.data () + ui, tmp.data () + nz, xdata () + li);
@@ -1246,7 +1252,7 @@
                 }
             }
 
-          *this = Sparse<T> (nr - sl, 1, nz_new);
+          *this = Sparse<T, Alloc> (nr - sl, 1, nz_new);
           std::copy_n (ridx_new, nz_new, ridx ());
           std::copy_n (data_new, nz_new, xdata ());
           xcidx (1) = nz_new;
@@ -1258,11 +1264,11 @@
       octave_idx_type lb, ub;
       if (idx.is_cont_range (nc, lb, ub))
         {
-          const Sparse<T> tmp = *this;
+          const Sparse<T, Alloc> tmp = *this;
           octave_idx_type lbi = tmp.cidx (lb);
           octave_idx_type ubi = tmp.cidx (ub);
           octave_idx_type new_nz = nz - (ubi - lbi);
-          *this = Sparse<T> (1, nc - (ub - lb), new_nz);
+          *this = Sparse<T, Alloc> (1, nc - (ub - lb), new_nz);
           std::copy_n (tmp.data (), lbi, data ());
           std::copy (tmp.data () + ubi, tmp.data () + nz, xdata () + lbi);
           std::fill_n (ridx (), new_nz, static_cast<octave_idx_type> (0));
@@ -1276,7 +1282,7 @@
   else if (idx.length (nel) != 0)
     {
       if (idx.is_colon_equiv (nel))
-        *this = Sparse<T> ();
+        *this = Sparse<T, Alloc> ();
       else
         {
           *this = index (octave::idx_vector::colon);
@@ -1286,10 +1292,11 @@
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::delete_elements (const octave::idx_vector& idx_i, const octave::idx_vector& idx_j)
+Sparse<T, Alloc>::delete_elements (const octave::idx_vector& idx_i,
+                                   const octave::idx_vector& idx_j)
 {
   assert (ndims () == 2);
 
@@ -1308,21 +1315,21 @@
           if (lb == 0 && ub == nc)
             {
               // Delete all rows and columns.
-              *this = Sparse<T> (nr, 0);
+              *this = Sparse<T, Alloc> (nr, 0);
             }
           else if (nz == 0)
             {
               // No elements to preserve; adjust dimensions.
-              *this = Sparse<T> (nr, nc - (ub - lb));
+              *this = Sparse<T, Alloc> (nr, nc - (ub - lb));
             }
           else
             {
-              const Sparse<T> tmp = *this;
+              const Sparse<T, Alloc> tmp = *this;
               octave_idx_type lbi = tmp.cidx (lb);
               octave_idx_type ubi = tmp.cidx (ub);
               octave_idx_type new_nz = nz - (ubi - lbi);
 
-              *this = Sparse<T> (nr, nc - (ub - lb), new_nz);
+              *this = Sparse<T, Alloc> (nr, nc - (ub - lb), new_nz);
               std::copy_n (tmp.data (), lbi, data ());
               std::copy_n (tmp.ridx (), lbi, ridx ());
               std::copy (tmp.data () + ubi, tmp.data () + nz, xdata () + lbi);
@@ -1346,20 +1353,20 @@
           if (lb == 0 && ub == nr)
             {
               // Delete all rows and columns.
-              *this = Sparse<T> (0, nc);
+              *this = Sparse<T, Alloc> (0, nc);
             }
           else if (nz == 0)
             {
               // No elements to preserve; adjust dimensions.
-              *this = Sparse<T> (nr - (ub - lb), nc);
+              *this = Sparse<T, Alloc> (nr - (ub - lb), nc);
             }
           else
             {
               // This is more memory-efficient than the approach below.
-              const Sparse<T> tmpl = index (octave::idx_vector (0, lb), idx_j);
-              const Sparse<T> tmpu = index (octave::idx_vector (ub, nr), idx_j);
-              *this = Sparse<T> (nr - (ub - lb), nc,
-                                 tmpl.nnz () + tmpu.nnz ());
+              const Sparse<T, Alloc> tmpl = index (octave::idx_vector (0, lb), idx_j);
+              const Sparse<T, Alloc> tmpu = index (octave::idx_vector (ub, nr), idx_j);
+              *this = Sparse<T, Alloc> (nr - (ub - lb), nc,
+                                        tmpl.nnz () + tmpu.nnz ());
               for (octave_idx_type j = 0, k = 0; j < nc; j++)
                 {
                   for (octave_idx_type i = tmpl.cidx (j); i < tmpl.cidx (j+1);
@@ -1383,7 +1390,7 @@
         {
           // This is done by transposing, deleting columns, then transposing
           // again.
-          Sparse<T> tmp = transpose ();
+          Sparse<T, Alloc> tmp = transpose ();
           tmp.delete_elements (idx_j, idx_i);
           *this = tmp.transpose ();
         }
@@ -1406,10 +1413,10 @@
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::delete_elements (int dim, const octave::idx_vector& idx)
+Sparse<T, Alloc>::delete_elements (int dim, const octave::idx_vector& idx)
 {
   if (dim == 0)
     delete_elements (idx, octave::idx_vector::colon);
@@ -1419,12 +1426,12 @@
     (*current_liboctave_error_handler) ("invalid dimension in delete_elements");
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::index (const octave::idx_vector& idx, bool resize_ok) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::index (const octave::idx_vector& idx, bool resize_ok) const
 {
-  Sparse<T> retval;
+  Sparse<T, Alloc> retval;
 
   assert (ndims () == 2);
 
@@ -1443,7 +1450,7 @@
       else
         {
           // Fast magic colon processing.
-          retval = Sparse<T> (nel, 1, nz);
+          retval = Sparse<T, Alloc> (nel, 1, nz);
 
           for (octave_idx_type i = 0; i < nc; i++)
             {
@@ -1465,7 +1472,7 @@
 
       // resize_ok is completely handled here.
       octave_idx_type ext = idx.extent (nel);
-      Sparse<T> tmp = *this;
+      Sparse<T, Alloc> tmp = *this;
       tmp.resize1 (ext);
       retval = tmp.index (idx);
     }
@@ -1475,7 +1482,7 @@
       // since you have a scalar stored as a sparse matrix, and
       // then want to make a dense matrix with sparse representation.
       // Ok, we'll do it, but you deserve what you get!!
-      retval = (Sparse<T> (idx_dims(0), idx_dims(1), nz ? data (0) : T ()));
+      retval = (Sparse<T, Alloc> (idx_dims(0), idx_dims(1), nz ? data (0) : T ()));
     }
   else if (nc == 1)
     {
@@ -1499,7 +1506,7 @@
           octave_idx_type ui = lblookup (ridx (), nz, ub);
           // Copy data and adjust indices.
           octave_idx_type nz_new = ui - li;
-          retval = Sparse<T> (ub - lb, 1, nz_new);
+          retval = Sparse<T, Alloc> (ub - lb, 1, nz_new);
           std::copy_n (data () + li, nz_new, retval.data ());
           mx_inline_sub (nz_new, retval.xridx (), ridx () + li, lb);
           retval.xcidx (1) = nz_new;
@@ -1508,7 +1515,7 @@
         {
           if (idx.is_range () && idx.increment () == -1)
             {
-              retval = Sparse<T> (nr, 1, nz);
+              retval = Sparse<T, Alloc> (nr, 1, nz);
 
               for (octave_idx_type j = 0; j < nz; j++)
                 retval.ridx (j) = nr - ridx (nz - j - 1) - 1;
@@ -1520,7 +1527,7 @@
             {
               Array<T> tmp = array_value ();
               tmp = tmp.index (idx);
-              retval = Sparse<T> (tmp);
+              retval = Sparse<T, Alloc> (tmp);
             }
         }
       else
@@ -1545,7 +1552,7 @@
             lidx.xelem (i) = lblookup (ridx (), nz, idxa(i));
 
           // Count matches.
-          retval = Sparse<T> (idxa.rows (), idxa.cols ());
+          retval = Sparse<T, Alloc> (idxa.rows (), idxa.cols ());
           for (octave_idx_type j = 0; j < new_nc; j++)
             {
               octave_idx_type nzj = 0;
@@ -1580,14 +1587,14 @@
     {
       octave_idx_type lb, ub;
       if (idx.is_scalar ())
-        retval = Sparse<T> (1, 1, elem (0, idx(0)));
+        retval = Sparse<T, Alloc> (1, 1, elem (0, idx(0)));
       else if (idx.is_cont_range (nel, lb, ub))
         {
           // Special-case a contiguous range.
           octave_idx_type lbi = cidx (lb);
           octave_idx_type ubi = cidx (ub);
           octave_idx_type new_nz = ubi - lbi;
-          retval = Sparse<T> (1, ub - lb, new_nz);
+          retval = Sparse<T, Alloc> (1, ub - lb, new_nz);
           std::copy_n (data () + lbi, new_nz, retval.data ());
           std::fill_n (retval.ridx (), new_nz, static_cast<octave_idx_type> (0));
           mx_inline_sub (ub - lb + 1, retval.cidx (), cidx () + lb, lbi);
@@ -1596,13 +1603,13 @@
         {
           // Sparse row vectors occupy O(nr) storage anyway, so let's just
           // convert the matrix to full, index, and sparsify the result.
-          retval = Sparse<T> (array_value ().index (idx));
+          retval = Sparse<T, Alloc> (array_value ().index (idx));
         }
     }
   else
     {
       if (nr != 0 && idx.is_scalar ())
-        retval = Sparse<T> (1, 1, elem (idx(0) % nr, idx(0) / nr));
+        retval = Sparse<T, Alloc> (1, 1, elem (idx(0) % nr, idx(0) / nr));
       else
         {
           // Indexing a non-vector sparse matrix by linear indexing.
@@ -1620,13 +1627,14 @@
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::index (const octave::idx_vector& idx_i, const octave::idx_vector& idx_j,
-                  bool resize_ok) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::index (const octave::idx_vector& idx_i,
+                         const octave::idx_vector& idx_j,
+                         bool resize_ok) const
 {
-  Sparse<T> retval;
+  Sparse<T, Alloc> retval;
 
   assert (ndims () == 2);
 
@@ -1645,7 +1653,7 @@
         {
           octave_idx_type ext_i = idx_i.extent (nr);
           octave_idx_type ext_j = idx_j.extent (nc);
-          Sparse<T> tmp = *this;
+          Sparse<T, Alloc> tmp = *this;
           tmp.resize (ext_i, ext_j);
           retval = tmp.index (idx_i, idx_j);
         }
@@ -1660,7 +1668,7 @@
       // a scalar, so let's just convert the matrix to full, index,
       // and sparsify the result.
 
-      retval = Sparse<T> (array_value ().index (idx_i, idx_j));
+      retval = Sparse<T, Alloc> (array_value ().index (idx_i, idx_j));
     }
   else if (idx_i.is_colon ())
     {
@@ -1674,7 +1682,7 @@
           octave_idx_type lbi = cidx (lb);
           octave_idx_type ubi = cidx (ub);
           octave_idx_type new_nz = ubi - lbi;
-          retval = Sparse<T> (nr, ub - lb, new_nz);
+          retval = Sparse<T, Alloc> (nr, ub - lb, new_nz);
           std::copy_n (data () + lbi, new_nz, retval.data ());
           std::copy_n (ridx () + lbi, new_nz, retval.ridx ());
           mx_inline_sub (ub - lb + 1, retval.cidx (), cidx () + lb, lbi);
@@ -1682,7 +1690,7 @@
       else
         {
           // Count new nonzero elements.
-          retval = Sparse<T> (nr, m);
+          retval = Sparse<T, Alloc> (nr, m);
           for (octave_idx_type j = 0; j < m; j++)
             {
               octave_idx_type jj = idx_j(j);
@@ -1715,7 +1723,7 @@
   else if (idx_i.is_scalar ())
     {
       octave_idx_type ii = idx_i(0);
-      retval = Sparse<T> (1, m);
+      retval = Sparse<T, Alloc> (1, m);
       OCTAVE_LOCAL_BUFFER (octave_idx_type, ij, m);
       for (octave_idx_type j = 0; j < m; j++)
         {
@@ -1750,7 +1758,7 @@
     }
   else if (idx_i.is_cont_range (nr, lb, ub))
     {
-      retval = Sparse<T> (n, m);
+      retval = Sparse<T, Alloc> (n, m);
       OCTAVE_LOCAL_BUFFER (octave_idx_type, li, m);
       OCTAVE_LOCAL_BUFFER (octave_idx_type, ui, m);
       for (octave_idx_type j = 0; j < m; j++)
@@ -1784,7 +1792,7 @@
     {
       // The columns preserve their length, just need to renumber and sort them.
       // Count new nonzero elements.
-      retval = Sparse<T> (nr, m);
+      retval = Sparse<T, Alloc> (nr, m);
       for (octave_idx_type j = 0; j < m; j++)
         {
           octave_idx_type jj = idx_j(j);
@@ -1865,12 +1873,13 @@
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::assign (const octave::idx_vector& idx, const Sparse<T>& rhs)
+Sparse<T, Alloc>::assign (const octave::idx_vector& idx,
+                          const Sparse<T, Alloc>& rhs)
 {
-  Sparse<T> retval;
+  Sparse<T, Alloc> retval;
 
   assert (ndims () == 2);
 
@@ -1937,8 +1946,8 @@
                 {
                   // Clearing elements or exceeding capacity, allocate afresh
                   // and paste pieces.
-                  const Sparse<T> tmp = *this;
-                  *this = Sparse<T> (nr, 1, new_nz);
+                  const Sparse<T, Alloc> tmp = *this;
+                  *this = Sparse<T, Alloc> (nr, 1, new_nz);
 
                   // Head ...
                   std::copy_n (tmp.data (), li, data ());
@@ -1982,7 +1991,7 @@
             }
           else
             {
-              const Sparse<T> tmp = *this;
+              const Sparse<T, Alloc> tmp = *this;
               octave_idx_type new_nz = nz + rhl;
               // Disassembly our matrix...
               Array<octave_idx_type> new_ri (dim_vector (new_nz, 1));
@@ -1993,7 +2002,7 @@
               idx.copy_data (new_ri.fortran_vec () + nz);
               new_data.assign (octave::idx_vector (nz, new_nz), rhs.array_value ());
               // ... reassembly.
-              *this = Sparse<T> (new_data, new_ri, 0, nr, nc, false);
+              *this = Sparse<T, Alloc> (new_data, new_ri, 0, nr, nc, false);
             }
         }
       else
@@ -2008,33 +2017,35 @@
     {
       rhl = idx.length (n);
       if (rhs.nnz () != 0)
-        assign (idx, Sparse<T> (rhl, 1, rhs.data (0)));
+        assign (idx, Sparse<T, Alloc> (rhl, 1, rhs.data (0)));
       else
-        assign (idx, Sparse<T> (rhl, 1));
+        assign (idx, Sparse<T, Alloc> (rhl, 1));
     }
   else
     octave::err_nonconformant ("=", dim_vector(idx.length (n), 1), rhs.dims());
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::assign (const octave::idx_vector& idx, const T& rhs)
+Sparse<T, Alloc>::assign (const octave::idx_vector& idx,
+                                        const T& rhs)
 {
   // FIXME: Converting the RHS and forwarding to the sparse matrix
   // assignment function is simpler, but it might be good to have a
   // specialization...
 
-  assign (idx, Sparse<T> (1, 1, rhs));
+  assign (idx, Sparse<T, Alloc> (1, 1, rhs));
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::assign (const octave::idx_vector& idx_i,
-                   const octave::idx_vector& idx_j, const Sparse<T>& rhs)
+Sparse<T, Alloc>::assign (const octave::idx_vector& idx_i,
+                          const octave::idx_vector& idx_j,
+                          const Sparse<T, Alloc>& rhs)
 {
-  Sparse<T> retval;
+  Sparse<T, Alloc> retval;
 
   assert (ndims () == 2);
 
@@ -2136,8 +2147,8 @@
                 {
                   // Clearing elements or exceeding capacity, allocate afresh
                   // and paste pieces.
-                  const Sparse<T> tmp = *this;
-                  *this = Sparse<T> (nr, nc, new_nz);
+                  const Sparse<T, Alloc> tmp = *this;
+                  *this = Sparse<T, Alloc> (nr, nc, new_nz);
 
                   // Head...
                   std::copy_n (tmp.data (), li, data ());
@@ -2173,8 +2184,8 @@
             }
           else
             {
-              const Sparse<T> tmp = *this;
-              *this = Sparse<T> (nr, nc);
+              const Sparse<T, Alloc> tmp = *this;
+              *this = Sparse<T, Alloc> (nr, nc);
               OCTAVE_LOCAL_BUFFER_INIT (octave_idx_type, jsav, nc, -1);
 
               // Assemble column lengths.
@@ -2243,7 +2254,7 @@
       else
         {
           // Split it into 2 assignments and one indexing.
-          Sparse<T> tmp = index (octave::idx_vector::colon, idx_j);
+          Sparse<T, Alloc> tmp = index (octave::idx_vector::colon, idx_j);
           tmp.assign (idx_i, octave::idx_vector::colon, rhs);
           assign (octave::idx_vector::colon, idx_j, tmp);
         }
@@ -2253,9 +2264,9 @@
       n = idx_i.length (nr);
       m = idx_j.length (nc);
       if (rhs.nnz () != 0)
-        assign (idx_i, idx_j, Sparse<T> (n, m, rhs.data (0)));
+        assign (idx_i, idx_j, Sparse<T, Alloc> (n, m, rhs.data (0)));
       else
-        assign (idx_i, idx_j, Sparse<T> (n, m));
+        assign (idx_i, idx_j, Sparse<T, Alloc> (n, m));
     }
   else if (idx_i.length (nr) == m && idx_j.length (nc) == n
            && (n == 1 || m == 1))
@@ -2266,17 +2277,18 @@
     octave::err_nonconformant  ("=", idx_i.length (nr), idx_j.length (nc), n, m);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::assign (const octave::idx_vector& idx_i,
-                   const octave::idx_vector& idx_j, const T& rhs)
+Sparse<T, Alloc>::assign (const octave::idx_vector& idx_i,
+                          const octave::idx_vector& idx_j,
+                          const T& rhs)
 {
   // FIXME: Converting the RHS and forwarding to the sparse matrix
   // assignment function is simpler, but it might be good to have a
   // specialization...
 
-  assign (idx_i, idx_j, Sparse<T> (1, 1, rhs));
+  assign (idx_i, idx_j, Sparse<T, Alloc> (1, 1, rhs));
 }
 
 // Can't use versions of these in Array.cc due to duplication of the
@@ -2299,12 +2311,12 @@
   return (a > b);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::sort (octave_idx_type dim, sortmode mode) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::sort (octave_idx_type dim, sortmode mode) const
 {
-  Sparse<T> m = *this;
+  Sparse<T, Alloc> m = *this;
 
   octave_idx_type nr = m.rows ();
   octave_idx_type nc = m.columns ();
@@ -2327,7 +2339,7 @@
     lsort.set_compare (sparse_descending_compare<T>);
   else
     (*current_liboctave_error_handler)
-      ("Sparse<T>::sort: invalid MODE");
+      ("Sparse<T, Alloc>::sort: invalid MODE");
 
   T *v = m.data ();
   octave_idx_type *mcidx = m.cidx ();
@@ -2366,13 +2378,13 @@
   return m;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::sort (Array<octave_idx_type>& sidx, octave_idx_type dim,
-                 sortmode mode) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::sort (Array<octave_idx_type>& sidx,
+                        octave_idx_type dim, sortmode mode) const
 {
-  Sparse<T> m = *this;
+  Sparse<T, Alloc> m = *this;
 
   octave_idx_type nr = m.rows ();
   octave_idx_type nc = m.columns ();
@@ -2398,7 +2410,7 @@
     indexed_sort.set_compare (sparse_descending_compare<T>);
   else
     (*current_liboctave_error_handler)
-      ("Sparse<T>::sort: invalid MODE");
+      ("Sparse<T, Alloc>::sort: invalid MODE");
 
   T *v = m.data ();
   octave_idx_type *mcidx = m.cidx ();
@@ -2474,14 +2486,14 @@
   return m;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::diag (octave_idx_type k) const
+Sparse<T, Alloc>
+Sparse<T, Alloc>::diag (octave_idx_type k) const
 {
   octave_idx_type nnr = rows ();
   octave_idx_type nnc = cols ();
-  Sparse<T> d;
+  Sparse<T, Alloc> d;
 
   if (nnr == 0 || nnc == 0)
     ; // do nothing
@@ -2517,7 +2529,7 @@
                   nel++;
             }
 
-          d = Sparse<T> (ndiag, 1, nel);
+          d = Sparse<T, Alloc> (ndiag, 1, nel);
           d.xcidx (0) = 0;
           d.xcidx (1) = nel;
 
@@ -2567,7 +2579,7 @@
           octave_idx_type nc = 1;
           octave_idx_type nz = 0;
 
-          d = Sparse<T> (nr, nc, nz);
+          d = Sparse<T, Alloc> (nr, nc, nz);
         }
     }
   else  // one of dimensions == 1 (vector)
@@ -2590,7 +2602,7 @@
           octave_idx_type n = nnc + std::abs (k);
           octave_idx_type nz = nnz ();
 
-          d = Sparse<T> (n, n, nz);
+          d = Sparse<T, Alloc> (n, n, nz);
 
           if (nnz () > 0)
             {
@@ -2616,7 +2628,7 @@
           octave_idx_type n = nnr + std::abs (k);
           octave_idx_type nz = nnz ();
 
-          d = Sparse<T> (n, n, nz);
+          d = Sparse<T, Alloc> (n, n, nz);
 
           if (nnz () > 0)
             {
@@ -2648,10 +2660,11 @@
   return d;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
-Sparse<T>
-Sparse<T>::cat (int dim, octave_idx_type n, const Sparse<T> *sparse_list)
+Sparse<T, Alloc>
+Sparse<T, Alloc>::cat (int dim, octave_idx_type n,
+                       const Sparse<T, Alloc> *sparse_list)
 {
   // Default concatenation.
   bool (dim_vector::*concat_rule) (const dim_vector&, int) = &dim_vector::concat;
@@ -2681,7 +2694,7 @@
       total_nz += sparse_list[i].nnz ();
     }
 
-  Sparse<T> retval (dv, total_nz);
+  Sparse<T, Alloc> retval (dv, total_nz);
 
   if (retval.isempty ())
     return retval;
@@ -2700,7 +2713,7 @@
             octave_idx_type rcum = 0;
             for (octave_idx_type i = 0; i < n; i++)
               {
-                const Sparse<T>& spi = sparse_list[i];
+                const Sparse<T, Alloc>& spi = sparse_list[i];
                 // Skipping empty matrices.  See the comment in Array.cc.
                 if (spi.isempty ())
                   continue;
@@ -2747,10 +2760,10 @@
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 Array<T>
-Sparse<T>::array_value () const
+Sparse<T, Alloc>::array_value () const
 {
   Array<T> retval (dims (), T ());
   if (rows () == 1)
@@ -3075,10 +3088,10 @@
 
 */
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTAVE_API
 void
-Sparse<T>::print_info (std::ostream& os, const std::string& prefix) const
+Sparse<T, Alloc>::print_info (std::ostream& os, const std::string& prefix) const
 {
   os << prefix << "m_rep address:  " << m_rep << "\n"
      << prefix << "m_rep->m_nzmax: " << m_rep->m_nzmax  << "\n"
@@ -3091,15 +3104,15 @@
 }
 
 #if defined (__clang__)
-#  define INSTANTIATE_SPARSE(T)                                         \
-     template class OCTAVE_API Sparse<T>;                               \
-     template OCTAVE_API std::istream&                                  \
-     read_sparse_matrix<T> (std::istream& is, Sparse<T>& a,             \
-                            T (*read_fcn) (std::istream&));
+#  define INSTANTIATE_SPARSE(T)                                 \
+  template class OCTAVE_API Sparse<T>;                          \
+  template OCTAVE_API std::istream&                             \
+  read_sparse_matrix<T> (std::istream& is, Sparse<T>& a,        \
+                         T (*read_fcn) (std::istream&));
 #else
-#  define INSTANTIATE_SPARSE(T)                                         \
-     template class Sparse<T>;                                          \
-     template OCTAVE_API std::istream&                                  \
-     read_sparse_matrix<T> (std::istream& is, Sparse<T>& a,             \
-                            T (*read_fcn) (std::istream&));
+#  define INSTANTIATE_SPARSE(T)                                 \
+  template class Sparse<T>;                                     \
+  template OCTAVE_API std::istream&                             \
+  read_sparse_matrix<T> (std::istream& is, Sparse<T>& a,        \
+                         T (*read_fcn) (std::istream&));
 #endif
--- a/liboctave/array/Sparse.h	Sun Feb 20 20:21:12 2022 +0100
+++ b/liboctave/array/Sparse.h	Mon Feb 28 13:56:18 2022 -0500
@@ -42,7 +42,7 @@
 // Two dimensional sparse class.  Handles the reference counting for
 // all the derived classes.
 
-template <typename T>
+template <typename T, typename Alloc>
 class
 OCTAVE_API
 Sparse
@@ -56,42 +56,50 @@
   // The real representation of all Sparse arrays.
   //--------------------------------------------------------------------
 
-  class SparseRep
+class SparseRep : public Alloc
   {
   public:
 
-    T *m_data;
-    octave_idx_type *m_ridx;
-    octave_idx_type *m_cidx;
+    typedef std::allocator_traits<Alloc> Alloc_traits;
+
+    typedef typename Alloc_traits::template rebind_traits<T> T_Alloc_traits;
+    typedef typename T_Alloc_traits::pointer T_pointer;
+
+    typedef typename Alloc_traits::template rebind_traits<octave_idx_type> idx_type_Alloc_traits;
+    typedef typename idx_type_Alloc_traits::pointer idx_type_pointer;
+
+    T_pointer m_data;
+    idx_type_pointer m_ridx;
+    idx_type_pointer m_cidx;
     octave_idx_type m_nzmax;
     octave_idx_type m_nrows;
     octave_idx_type m_ncols;
     octave::refcount<octave_idx_type> m_count;
 
     SparseRep (void)
-      : m_data (new T [1]), m_ridx (new octave_idx_type [1] {}),
-        m_cidx (new octave_idx_type [1] {}),
+      : Alloc (), m_data (T_allocate (1)), m_ridx (idx_type_allocate (1)),
+        m_cidx (idx_type_allocate (1)),
         m_nzmax (1), m_nrows (0), m_ncols (0), m_count (1)
     { }
 
     SparseRep (octave_idx_type n)
-      : m_data (new T [1]), m_ridx (new octave_idx_type [1] {}),
-        m_cidx (new octave_idx_type [n+1] {}),
+      : Alloc (), m_data (T_allocate (1)), m_ridx (idx_type_allocate (1)),
+        m_cidx (idx_type_allocate (n+1)),
         m_nzmax (1), m_nrows (n), m_ncols (n), m_count (1)
     { }
 
     SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz = 1)
-      : m_data (nz > 0 ? new T [nz] : new T [1]),
-        m_ridx (nz > 0 ? new octave_idx_type [nz] {} : new octave_idx_type [1] {}),
-        m_cidx (new octave_idx_type [nc+1] {}),
+      : Alloc (), m_data (T_allocate (nz > 0 ? nz : 1)),
+        m_ridx (idx_type_allocate (nz > 0 ? nz : 1)),
+        m_cidx (idx_type_allocate (nc+1)),
         m_nzmax (nz > 0 ? nz : 1), m_nrows (nr), m_ncols (nc), m_count (1)
     { }
 
     SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz,
                const T *d, const octave_idx_type *r, const octave_idx_type *c)
-      : m_data (new T [nz]),
-        m_ridx (new octave_idx_type [nz] {}),
-        m_cidx (new octave_idx_type [nc+1] {}),
+      : Alloc (), m_data (T_allocate (nz)),
+        m_ridx (idx_type_allocate (nz)),
+        m_cidx (idx_type_allocate (nc+1)),
         m_nzmax (nz), m_nrows (nr), m_ncols (nc), m_count (1)
     {
       std::copy_n (d, nz, m_data);
@@ -102,9 +110,9 @@
     template <typename U>
     SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz,
                const U *d, const octave_idx_type *r, const octave_idx_type *c)
-      : m_data (new T [nz]),
-        m_ridx (new octave_idx_type [nz] {}),
-        m_cidx (new octave_idx_type [nc+1] {}),
+      : Alloc (), m_data (T_allocate (nz)),
+        m_ridx (idx_type_allocate (nz)),
+        m_cidx (idx_type_allocate (nc+1)),
         m_nzmax (nz), m_nrows (nr), m_ncols (nc), m_count (1)
     {
       std::copy_n (d, nz, m_data);
@@ -114,15 +122,18 @@
 
     template <typename U>
     SparseRep (const dim_vector& dv, octave_idx_type nz,
-               U *d, octave_idx_type *r, octave_idx_type *c)
-      : m_data (d), m_ridx (r), m_cidx (c),
+               U *d, octave_idx_type *r, octave_idx_type *c,
+               const Alloc& xallocator = Alloc ())
+      : Alloc (xallocator), m_data (d), m_ridx (r), m_cidx (c),
         m_nzmax (nz), m_nrows (dv(0)), m_ncols (dv(1)), m_count (1)
     { }
 
     SparseRep (const SparseRep& a)
-      : m_data (new T [a.m_nzmax]), m_ridx (new octave_idx_type [a.m_nzmax]),
-        m_cidx (new octave_idx_type [a.m_ncols + 1]),
-        m_nzmax (a.m_nzmax), m_nrows (a.m_nrows), m_ncols (a.m_ncols), m_count (1)
+      : Alloc (), m_data (T_allocate (a.m_nzmax)),
+        m_ridx (idx_type_allocate (a.m_nzmax)),
+        m_cidx (idx_type_allocate (a.m_ncols + 1)),
+        m_nzmax (a.m_nzmax), m_nrows (a.m_nrows), m_ncols (a.m_ncols),
+        m_count (1)
     {
       octave_idx_type nz = a.nnz ();
       std::copy_n (a.m_data, nz, m_data);
@@ -130,7 +141,12 @@
       std::copy_n (a.m_cidx, m_ncols + 1, m_cidx);
     }
 
-    ~SparseRep (void) { delete [] m_data; delete [] m_ridx; delete [] m_cidx; }
+    ~SparseRep (void)
+    {
+      T_deallocate (m_data, m_nzmax);
+      idx_type_deallocate (m_ridx, m_nzmax);
+      idx_type_deallocate (m_cidx, m_ncols + 1);
+    }
 
     octave_idx_type nzmax (void) const { return m_nzmax; }
     octave_idx_type nnz (void) const { return m_cidx[m_ncols]; }
@@ -166,11 +182,51 @@
     // Prefer nzmax.
     octave_idx_type length (void) const { return m_nzmax; }
 
-    template <typename U> friend class Sparse;
+    template <typename U, typename A> friend class Sparse;
 
     // No assignment!
 
     SparseRep& operator = (const SparseRep&) = delete;
+
+    T_pointer T_allocate (size_t len)
+    {
+      typename T_Alloc_traits::allocator_type& alloc = *this;
+
+      T_pointer data = T_Alloc_traits::allocate (alloc, len);
+      for (size_t i = 0; i < len; i++)
+        T_Alloc_traits::construct (alloc, data+i);
+
+      return data;
+    }
+
+    void T_deallocate (T_pointer data, size_t len)
+    {
+      typename T_Alloc_traits::allocator_type& alloc = *this;
+
+      for (size_t i = 0; i < len; i++)
+        T_Alloc_traits::destroy (alloc, data+i);
+      T_Alloc_traits::deallocate (alloc, data, len);
+    }
+
+    idx_type_pointer idx_type_allocate (size_t len)
+    {
+      typename idx_type_Alloc_traits::allocator_type alloc = *this;
+
+      idx_type_pointer idx = idx_type_Alloc_traits::allocate (alloc, len);
+      for (size_t i = 0; i < len; i++)
+        idx_type_Alloc_traits::construct (alloc, idx+i);
+
+      return idx;
+    }
+
+    void idx_type_deallocate (idx_type_pointer idx, size_t len)
+    {
+      typename idx_type_Alloc_traits::allocator_type alloc = *this;
+
+      for (size_t i = 0; i < len; i++)
+        idx_type_Alloc_traits::destroy (alloc, idx+i);
+      idx_type_Alloc_traits::deallocate (alloc, idx, len);
+    }
   };
 
   //--------------------------------------------------------------------
@@ -190,13 +246,13 @@
 
 protected:
 
-  typename Sparse<T>::SparseRep *m_rep;
+  typename Sparse<T, Alloc>::SparseRep *m_rep;
 
   dim_vector m_dimensions;
 
 private:
 
-  static OCTAVE_API typename Sparse<T>::SparseRep * nil_rep (void);
+  static OCTAVE_API typename Sparse<T, Alloc>::SparseRep * nil_rep (void);
 
 public:
 
@@ -207,21 +263,21 @@
   }
 
   explicit Sparse (octave_idx_type n)
-    : m_rep (new typename Sparse<T>::SparseRep (n)),
+    : m_rep (new typename Sparse<T, Alloc>::SparseRep (n)),
       m_dimensions (dim_vector (n, n)) { }
 
   explicit Sparse (octave_idx_type nr, octave_idx_type nc)
-    : m_rep (new typename Sparse<T>::SparseRep (nr, nc)),
+    : m_rep (new typename Sparse<T, Alloc>::SparseRep (nr, nc)),
       m_dimensions (dim_vector (nr, nc)) { }
 
   explicit OCTAVE_API Sparse (octave_idx_type nr, octave_idx_type nc, T val);
 
   Sparse (const dim_vector& dv, octave_idx_type nz)
-    : m_rep (new typename Sparse<T>::SparseRep (dv(0), dv(1), nz)),
+    : m_rep (new typename Sparse<T, Alloc>::SparseRep (dv(0), dv(1), nz)),
       m_dimensions (dv) { }
 
   Sparse (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz)
-    : m_rep (new typename Sparse<T>::SparseRep (nr, nc, nz)),
+    : m_rep (new typename Sparse<T, Alloc>::SparseRep (nr, nc, nz)),
       m_dimensions (dim_vector (nr, nc)) { }
 
   // Construct a Sparse array from pointers to externally allocated
@@ -232,9 +288,10 @@
   // of the allocated PTR, CIDX, and RIDX arrays.
 
   Sparse (const dim_vector& dv, octave_idx_type nz,
-          T *ptr, octave_idx_type *ridx, octave_idx_type *cidx)
-    : m_rep (new typename Sparse<T>::SparseRep (dv, nz, ptr, ridx, cidx)),
-      m_dimensions (dv)
+          T *ptr, octave_idx_type *ridx, octave_idx_type *cidx,
+          const Alloc& xallocator = Alloc ())
+  : m_rep (new typename Sparse<T, Alloc>::SparseRep (dv, nz, ptr, ridx, cidx, xallocator)),
+    m_dimensions (dv)
   { }
 
   // Both SparseMatrix and SparseBoolMatrix need this ctor, and this
@@ -244,13 +301,13 @@
   // Type conversion case.  Preserves nzmax.
   template <typename U>
   Sparse (const Sparse<U>& a)
-    : m_rep (new typename Sparse<T>::SparseRep (a.rows (), a.cols (),
-                                                a.nzmax (), a.data (),
-                                                a.ridx (), a.cidx ())),
+    : m_rep (new typename Sparse<T, Alloc>::SparseRep (a.rows (), a.cols (),
+                                                       a.nzmax (), a.data (),
+                                                       a.ridx (), a.cidx ())),
       m_dimensions (a.dims ()) { }
 
   // No type conversion case.
-  Sparse (const Sparse<T>& a)
+  Sparse (const Sparse<T, Alloc>& a)
     : m_rep (a.m_rep), m_dimensions (a.m_dimensions)
   {
     m_rep->m_count++;
@@ -260,7 +317,7 @@
 
   OCTAVE_API Sparse (const dim_vector& dv);
 
-  OCTAVE_API Sparse (const Sparse<T>& a, const dim_vector& dv);
+  OCTAVE_API Sparse (const Sparse<T, Alloc>& a, const dim_vector& dv);
 
   OCTAVE_API
   Sparse (const Array<T>& a, const octave::idx_vector& r, const octave::idx_vector& c,
@@ -272,7 +329,7 @@
 
   virtual ~Sparse (void);
 
-  OCTAVE_API Sparse<T>& operator = (const Sparse<T>& a);
+  OCTAVE_API Sparse<T, Alloc>& operator = (const Sparse<T, Alloc>& a);
 
   //! Amount of storage for nonzero elements.
   //! This may differ from the actual number of elements, see nnz().
@@ -313,7 +370,7 @@
 
   dim_vector dims (void) const { return m_dimensions; }
 
-  Sparse<T> squeeze (void) const { return *this; }
+  Sparse<T, Alloc> squeeze (void) const { return *this; }
 
   OCTAVE_API octave_idx_type
   compute_index (const Array<octave_idx_type>& ra_idx) const;
@@ -409,7 +466,7 @@
   }
 
   T& elem (const Array<octave_idx_type>& ra_idx)
-  { return Sparse<T>::elem (compute_index (ra_idx)); }
+  { return Sparse<T, Alloc>::elem (compute_index (ra_idx)); }
 
   T& operator () (octave_idx_type n)
   {
@@ -449,7 +506,7 @@
     if (i < 0)
       range_error ("T Sparse<T>::checkelem", ra_idx);
     else
-      return Sparse<T>::elem (i);
+      return Sparse<T, Alloc>::elem (i);
   }
 
   T elem (octave_idx_type n) const { return xelem (n); }
@@ -457,7 +514,7 @@
   T elem (octave_idx_type i, octave_idx_type j) const { return xelem (i, j); }
 
   T elem (const Array<octave_idx_type>& ra_idx) const
-  { return Sparse<T>::elem (compute_index (ra_idx)); }
+  { return Sparse<T, Alloc>::elem (compute_index (ra_idx)); }
 
   T operator () (octave_idx_type n) const { return elem (n); }
 
@@ -471,7 +528,7 @@
     return elem (ra_idx);
   }
 
-  Sparse<T> maybe_compress (bool remove_zeros = false)
+  Sparse<T, Alloc> maybe_compress (bool remove_zeros = false)
   {
     if (remove_zeros)
       make_unique ();  // Need to unshare because elements are removed.
@@ -480,12 +537,12 @@
     return (*this);
   }
 
-  OCTAVE_API Sparse<T> reshape (const dim_vector& new_dims) const;
+  OCTAVE_API Sparse<T, Alloc> reshape (const dim_vector& new_dims) const;
 
-  OCTAVE_API Sparse<T>
+  OCTAVE_API Sparse<T, Alloc>
   permute (const Array<octave_idx_type>& vec, bool inv = false) const;
 
-  Sparse<T> ipermute (const Array<octave_idx_type>& vec) const
+  Sparse<T, Alloc> ipermute (const Array<octave_idx_type>& vec) const
   {
     return permute (vec, true);
   }
@@ -503,16 +560,16 @@
     m_rep->change_length (nz);
   }
 
-  OCTAVE_API Sparse<T>&
-  insert (const Sparse<T>& a, octave_idx_type r, octave_idx_type c);
-  OCTAVE_API Sparse<T>&
-  insert (const Sparse<T>& a, const Array<octave_idx_type>& idx);
+  OCTAVE_API Sparse<T, Alloc>&
+  insert (const Sparse<T, Alloc>& a, octave_idx_type r, octave_idx_type c);
+  OCTAVE_API Sparse<T, Alloc>&
+  insert (const Sparse<T, Alloc>& a, const Array<octave_idx_type>& idx);
 
   bool issquare (void) const { return (dim1 () == dim2 ()); }
 
   bool isempty (void) const { return (rows () < 1 || cols () < 1); }
 
-  OCTAVE_API Sparse<T> transpose (void) const;
+  OCTAVE_API Sparse<T, Alloc> transpose (void) const;
 
   T * data (void) { make_unique (); return m_rep->m_data; }
   T& data (octave_idx_type i) { make_unique (); return m_rep->data (i); }
@@ -557,37 +614,40 @@
 
   OCTAVE_API void delete_elements (const octave::idx_vector& i, const octave::idx_vector& j);
 
-  OCTAVE_API Sparse<T>
+  OCTAVE_API Sparse<T, Alloc>
   index (const octave::idx_vector& i, bool resize_ok = false) const;
 
-  OCTAVE_API Sparse<T>
+  OCTAVE_API Sparse<T, Alloc>
   index (const octave::idx_vector& i, const octave::idx_vector& j,
          bool resize_ok = false) const;
 
-  OCTAVE_API void assign (const octave::idx_vector& i, const Sparse<T>& rhs);
+  OCTAVE_API void assign (const octave::idx_vector& i,
+                          const Sparse<T, Alloc>& rhs);
 
   OCTAVE_API void assign (const octave::idx_vector& i, const T& rhs);
 
   OCTAVE_API void
-  assign (const octave::idx_vector& i, const octave::idx_vector& j, const Sparse<T>& rhs);
+  assign (const octave::idx_vector& i, const octave::idx_vector& j,
+          const Sparse<T, Alloc>& rhs);
 
   OCTAVE_API void
-  assign (const octave::idx_vector& i, const octave::idx_vector& j, const T& rhs);
+  assign (const octave::idx_vector& i, const octave::idx_vector& j,
+          const T& rhs);
 
   OCTAVE_API void
   print_info (std::ostream& os, const std::string& prefix) const;
 
-  OCTAVE_API Sparse<T>
+  OCTAVE_API Sparse<T, Alloc>
   sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const;
-  OCTAVE_API Sparse<T>
+  OCTAVE_API Sparse<T, Alloc>
   sort (Array<octave_idx_type>& sidx, octave_idx_type dim = 0,
         sortmode mode = ASCENDING) const;
 
-  OCTAVE_API Sparse<T> diag (octave_idx_type k = 0) const;
+  OCTAVE_API Sparse<T, Alloc> diag (octave_idx_type k = 0) const;
 
   // dim = -1 and dim = -2 are special; see Array<T>::cat description.
-  static OCTAVE_API Sparse<T>
-  cat (int dim, octave_idx_type n, const Sparse<T> *sparse_list);
+  static OCTAVE_API Sparse<T, Alloc>
+  cat (int dim, octave_idx_type n, const Sparse<T, Alloc> *sparse_list);
 
   OCTAVE_API Array<T> array_value (void) const;