changeset 30491:2be7ac9389cb

maint: Merge stable to default.
author John W. Eaton <jwe@octave.org>
date Wed, 15 Dec 2021 20:26:12 -0500
parents 458b014d8829 (current diff) 34b4d993abb5 (diff)
children 6fc9847bd302
files configure.ac libinterp/corefcn/__isprimelarge__.cc libinterp/corefcn/mex.cc
diffstat 29 files changed, 770 insertions(+), 449 deletions(-) [+]
line wrap: on
line diff
--- a/build-aux/mk-octave-config-h.sh	Thu Dec 09 21:21:37 2021 -0800
+++ b/build-aux/mk-octave-config-h.sh	Wed Dec 15 20:26:12 2021 -0500
@@ -279,6 +279,7 @@
 $SED -n 's/#\(\(undef\|define\) OCTAVE_ENABLE_OPENMP.*$\)/#  \1/p' $config_h_file
 $SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_LONG_LONG_INT.*$\)/#  \1/p' $config_h_file
 $SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_UNSIGNED_LONG_LONG_INT.*$\)/#  \1/p' $config_h_file
+$SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR.*$\)/#  \1/p' $config_h_file
 $SED -n 's/#\(\(undef\|define\) OCTAVE_HAVE_OVERLOAD_CHAR_INT8_TYPES.*$\)/#  \1/p' $config_h_file
 $SED -n 's/#\(\(undef\|define\) OCTAVE_SIZEOF_F77_INT_TYPE.*$\)/#  \1/p' $config_h_file
 $SED -n 's/#\(\(undef\|define\) OCTAVE_SIZEOF_IDX_TYPE.*$\)/#  \1/p' $config_h_file
--- a/configure.ac	Thu Dec 09 21:21:37 2021 -0800
+++ b/configure.ac	Wed Dec 15 20:26:12 2021 -0500
@@ -400,6 +400,8 @@
   OCTAVE_CONFIGURE_WARNING([warn_stl_algo_h])
 fi
 
+OCTAVE_CHECK_STD_PMR_POLYMORPHIC_ALLOCATOR
+
 ### Check version number when using gcc.
 dnl It might be different from the g++ version number.
 
--- a/libinterp/corefcn/__isprimelarge__.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/libinterp/corefcn/__isprimelarge__.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -23,6 +23,10 @@
 //
 ////////////////////////////////////////////////////////////////////////
 
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
 #include "defun.h"
 #include "error.h"
 #include "ovl.h"
--- a/libinterp/corefcn/mex.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/libinterp/corefcn/mex.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -27,6 +27,12 @@
 #  include "config.h"
 #endif
 
+#define DEBUG 1
+
+#if defined (DEBUG)
+#  include <iostream>
+#endif
+
 #include <cstdarg>
 #include <cstdlib>
 #include <cstring>
@@ -34,9 +40,12 @@
 
 #include <limits>
 #include <map>
+#include <memory_resource>
 #include <set>
 #include <string>
 
+// Needed to instantiate Array objects with custom allocator.
+#include "Array.cc"
 #include "f77-fcn.h"
 #include "lo-ieee.h"
 #include "oct-locbuf.h"
@@ -253,18 +262,12 @@
   extern OCTINTERP_API void mxSetImagData (mxArray *ptr, void *pi);
 }
 
-// #define DEBUG 1
-
-#if DEBUG
-#  include <iostream>
-#endif
-
 static void *
 xmalloc (size_t n)
 {
   void *ptr = std::malloc (n);
 
-#if DEBUG
+#if defined (DEBUG)
   std::cerr << "xmalloc (" << n << ") = " << ptr << std::endl;
 #endif
 
@@ -276,7 +279,7 @@
 {
   void *newptr = std::realloc (ptr, n);
 
-#if DEBUG
+#if defined (DEBUG)
   std::cerr << "xrealloc (" << ptr << ", " << n << ") = " << newptr
             << std::endl;
 #endif
@@ -287,7 +290,7 @@
 static void
 xfree (void *ptr)
 {
-#if DEBUG
+#if defined (DEBUG)
   std::cerr << "xfree (" << ptr << ")" << std::endl;
 #endif
 
@@ -310,6 +313,63 @@
   return max_len;
 }
 
+// FIXME: Is there a better/standard way to do this job?
+
+template <typename T>
+class fp_type_traits
+{
+public:
+  static const bool is_complex = false;
+};
+
+template <>
+class fp_type_traits<Complex>
+{
+public:
+  static const bool is_complex = true;
+};
+
+template <>
+class fp_type_traits <FloatComplex>
+{
+public:
+  static const bool is_complex = true;
+};
+
+#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+#  include <memory_resource>
+
+class mx_memory_resource : public std::pmr::memory_resource
+{
+private:
+
+  void * do_allocate (std::size_t bytes, size_t /*alignment*/)
+  {
+    void *ptr = xmalloc (bytes);
+
+    if (! ptr)
+      throw std::bad_alloc ();
+
+    return ptr;
+  }
+
+  void do_deallocate (void* ptr, std::size_t /*bytes*/,
+                      std::size_t /*alignment*/)
+  {
+    xfree (ptr);
+  }
+
+  bool do_is_equal (const std::pmr::memory_resource& other) const noexcept
+  {
+    return this == dynamic_cast<const mx_memory_resource *> (&other);
+  }
+};
+
+// FIXME: Is it OK for the memory resource object to be defined this
+// way?
+static mx_memory_resource the_mx_memory_resource;
+#endif
+
 // ------------------------------------------------------------------
 
 mxArray_base::mxArray_base (bool interleaved)
@@ -2079,13 +2139,46 @@
   octave_value
   fp_to_ov (const dim_vector& dv) const
   {
+    octave_value retval;
+
     ELT_T *ppr = static_cast<ELT_T *> (m_pr);
 
-    Array<ELT_T> val (ppr, dv);
-
-    maybe_disown_ptr (m_pr);
-
-    return octave_value (val);
+#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+
+    octave::unwind_action act ([=] () { maybe_disown_ptr (m_pr); });
+
+    return octave_value (Array<ELT_T> (ppr, dv, &the_mx_memory_resource));
+
+#else
+
+    if (fp_type_traits<ELT_T>::is_complex)
+      {
+        // Mixing malloc and delete[] for arrays of Complex and
+        // FloatComplex objects is not possible.
+
+        Array<ELT_T> val (dv);
+
+        ELT_T *ptr = val.fortran_vec ();
+
+        mwSize nel = get_number_of_elements ();
+
+        for (mwIndex i = 0; i < nel; i++)
+          ptr[i] = ppr[i];
+
+        return octave_value (val);
+      }
+    else
+      {
+        // Although behavior is not specified by the standard, it should
+        // work to mix malloc and delete[] for arrays of float and
+        // double.
+
+        octave::unwind_action act ([=] () { maybe_disown_ptr (m_pr); });
+
+        return octave_value (Array<ELT_T> (ppr, dv));
+      }
+
+#endif
   }
 
   template <typename ELT_T, typename ARRAY_T, typename ARRAY_ELT_T>
@@ -2097,11 +2190,17 @@
 
     ELT_T *ppr = static_cast<ELT_T *> (m_pr);
 
-#if 0
-    ARRAY_T val (ppr, dv);
-
-    maybe_disown_ptr (m_pr);
+#if 0 && defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+
+    octave::unwind_action act ([=] () { maybe_disown_ptr (m_pr); });
+
+    return ARRAY_T (ppr, dv, &the_mx_memory_resource);
+
 #else
+
+    // All octave_int types are objects so we can't mix malloc and
+    // delete[] and we always have to copy.
+
     ARRAY_T val (dv);
 
     ARRAY_ELT_T *ptr = val.fortran_vec ();
@@ -2110,9 +2209,10 @@
 
     for (mwIndex i = 0; i < nel; i++)
       ptr[i] = ppr[i];
-#endif
 
     return octave_value (val);
+
+#endif
   }
 
 protected:
@@ -2363,6 +2463,9 @@
 
     T *ppr = static_cast<T *> (m_pr);
 
+    // We allocate in the Array<T> constructor and copy here, so we
+    // don't need the custom allocator for this object.
+
     Array<std::complex<T>> val (dv);
 
     std::complex<T> *ptr = val.fortran_vec ();
--- a/libinterp/corefcn/pr-output.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/libinterp/corefcn/pr-output.h	Wed Dec 15 20:26:12 2021 -0500
@@ -31,8 +31,10 @@
 #include <iosfwd>
 
 #include "Array-fwd.h"
+#include "intNDArray-fwd.h"
 #include "oct-cmplx.h"
 #include "oct-inttypes-fwd.h"
+#include "range-fwd.h"
 
 #include "pr-flt-fmt.h"
 
@@ -56,13 +58,6 @@
 class Cell;
 class octave_value;
 
-namespace octave
-{
-  template <typename T> class range;
-}
-
-template <typename T> class intNDArray;
-
 template <typename T>
 float_display_format
 make_format (const std::complex<T>&)
--- a/libinterp/octave-value/ov-range.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/libinterp/octave-value/ov-range.h	Wed Dec 15 20:26:12 2021 -0500
@@ -33,6 +33,7 @@
 #include <iosfwd>
 #include <string>
 
+#include "Array-fwd.h"
 #include "Range.h"
 
 #include "lo-mappers.h"
--- a/liboctave/array/Array-fwd.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/Array-fwd.h	Wed Dec 15 20:26:12 2021 -0500
@@ -28,6 +28,17 @@
 
 #include "octave-config.h"
 
-template <typename T> class OCTARRAY_API Array;
+#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+#  include <memory_resource>
+
+template <typename T, typename Alloc = std::pmr::polymorphic_allocator<T>>
+class OCTARRAY_API Array;
+
+#else
+
+template <typename T, typename Alloc = std::allocator<T>>
+class OCTARRAY_API Array;
 
 #endif
+
+#endif
--- a/liboctave/array/Array.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/Array.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -37,9 +37,9 @@
 // One dimensional array class.  Handles the reference counting for
 // all the derived classes.
 
-template <typename T>
-typename Array<T>::ArrayRep *
-Array<T>::nil_rep (void)
+template <typename T, typename Alloc>
+typename Array<T, Alloc>::ArrayRep *
+Array<T, Alloc>::nil_rep (void)
 {
   static ArrayRep nr;
   return &nr;
@@ -47,8 +47,8 @@
 
 // This is similar to the template for containers but specialized for Array.
 // Note we can't specialize a member without also specializing the class.
-template <typename T>
-Array<T>::Array (const Array<T>& a, const dim_vector& dv)
+template <typename T, typename Alloc>
+Array<T, Alloc>::Array (const Array<T, Alloc>& a, const dim_vector& dv)
   : m_dimensions (dv), m_rep (a.m_rep),
     m_slice_data (a.m_slice_data), m_slice_len (a.m_slice_len)
 {
@@ -68,9 +68,9 @@
   m_dimensions.chop_trailing_singletons ();
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::fill (const T& val)
+Array<T, Alloc>::fill (const T& val)
 {
   if (m_rep->m_count > 1)
     {
@@ -82,9 +82,9 @@
     std::fill_n (m_slice_data, m_slice_len, val);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::clear (void)
+Array<T, Alloc>::clear (void)
 {
   if (--m_rep->m_count == 0)
     delete m_rep;
@@ -97,9 +97,9 @@
   m_dimensions = dim_vector ();
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::clear (const dim_vector& dv)
+Array<T, Alloc>::clear (const dim_vector& dv)
 {
   if (--m_rep->m_count == 0)
     delete m_rep;
@@ -112,11 +112,11 @@
   m_dimensions.chop_trailing_singletons ();
 }
 
-template <typename T>
-Array<T>
-Array<T>::squeeze (void) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::squeeze (void) const
 {
-  Array<T> retval = *this;
+  Array<T, Alloc> retval = *this;
 
   if (ndims () > 2)
     {
@@ -159,37 +159,37 @@
             }
         }
 
-      retval = Array<T> (*this, new_dimensions);
+      retval = Array<T, Alloc> (*this, new_dimensions);
     }
 
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 octave_idx_type
-Array<T>::compute_index (octave_idx_type i, octave_idx_type j) const
+Array<T, Alloc>::compute_index (octave_idx_type i, octave_idx_type j) const
 {
   return ::compute_index (i, j, m_dimensions);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 octave_idx_type
-Array<T>::compute_index (octave_idx_type i, octave_idx_type j,
+Array<T, Alloc>::compute_index (octave_idx_type i, octave_idx_type j,
                          octave_idx_type k) const
 {
   return ::compute_index (i, j, k, m_dimensions);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 octave_idx_type
-Array<T>::compute_index (const Array<octave_idx_type>& ra_idx) const
+Array<T, Alloc>::compute_index (const Array<octave_idx_type>& ra_idx) const
 {
   return ::compute_index (ra_idx, m_dimensions);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 T&
-Array<T>::checkelem (octave_idx_type n)
+Array<T, Alloc>::checkelem (octave_idx_type n)
 {
   // Do checks directly to avoid recomputing m_slice_len.
   if (n < 0)
@@ -200,30 +200,30 @@
   return elem (n);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 T&
-Array<T>::checkelem (octave_idx_type i, octave_idx_type j)
+Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j)
 {
   return elem (compute_index (i, j));
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 T&
-Array<T>::checkelem (octave_idx_type i, octave_idx_type j, octave_idx_type k)
+Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j, octave_idx_type k)
 {
   return elem (compute_index (i, j, k));
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 T&
-Array<T>::checkelem (const Array<octave_idx_type>& ra_idx)
+Array<T, Alloc>::checkelem (const Array<octave_idx_type>& ra_idx)
 {
   return elem (compute_index (ra_idx));
 }
 
-template <typename T>
-typename Array<T>::crefT
-Array<T>::checkelem (octave_idx_type n) const
+template <typename T, typename Alloc>
+typename Array<T, Alloc>::crefT
+Array<T, Alloc>::checkelem (octave_idx_type n) const
 {
   // Do checks directly to avoid recomputing m_slice_len.
   if (n < 0)
@@ -234,56 +234,56 @@
   return elem (n);
 }
 
-template <typename T>
-typename Array<T>::crefT
-Array<T>::checkelem (octave_idx_type i, octave_idx_type j) const
+template <typename T, typename Alloc>
+typename Array<T, Alloc>::crefT
+Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j) const
 {
   return elem (compute_index (i, j));
 }
 
-template <typename T>
-typename Array<T>::crefT
-Array<T>::checkelem (octave_idx_type i, octave_idx_type j,
+template <typename T, typename Alloc>
+typename Array<T, Alloc>::crefT
+Array<T, Alloc>::checkelem (octave_idx_type i, octave_idx_type j,
                      octave_idx_type k) const
 {
   return elem (compute_index (i, j, k));
 }
 
-template <typename T>
-typename Array<T>::crefT
-Array<T>::checkelem (const Array<octave_idx_type>& ra_idx) const
+template <typename T, typename Alloc>
+typename Array<T, Alloc>::crefT
+Array<T, Alloc>::checkelem (const Array<octave_idx_type>& ra_idx) const
 {
   return elem (compute_index (ra_idx));
 }
 
-template <typename T>
-Array<T>
-Array<T>::column (octave_idx_type k) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::column (octave_idx_type k) const
 {
   octave_idx_type r = m_dimensions(0);
 
-  return Array<T> (*this, dim_vector (r, 1), k*r, k*r + r);
+  return Array<T, Alloc> (*this, dim_vector (r, 1), k*r, k*r + r);
 }
 
-template <typename T>
-Array<T>
-Array<T>::page (octave_idx_type k) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::page (octave_idx_type k) const
 {
   octave_idx_type r = m_dimensions(0);
   octave_idx_type c = m_dimensions(1);
   octave_idx_type p = r*c;
 
-  return Array<T> (*this, dim_vector (r, c), k*p, k*p + p);
+  return Array<T, Alloc> (*this, dim_vector (r, c), k*p, k*p + p);
 }
 
-template <typename T>
-Array<T>
-Array<T>::linear_slice (octave_idx_type lo, octave_idx_type up) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::linear_slice (octave_idx_type lo, octave_idx_type up) const
 {
   if (up < lo)
     up = lo;
 
-  return Array<T> (*this, dim_vector (up - lo, 1), lo, up);
+  return Array<T, Alloc> (*this, dim_vector (up - lo, 1), lo, up);
 }
 
 // Helper class for multi-d dimension permuting (generalized transpose).
@@ -426,11 +426,11 @@
   bool m_use_blk;
 };
 
-template <typename T>
-Array<T>
-Array<T>::permute (const Array<octave_idx_type>& perm_vec_arg, bool inv) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::permute (const Array<octave_idx_type>& perm_vec_arg, bool inv) const
 {
-  Array<T> retval;
+  Array<T, Alloc> retval;
 
   Array<octave_idx_type> perm_vec = perm_vec_arg;
 
@@ -484,7 +484,7 @@
   for (int i = 0; i < perm_vec_len; i++)
     dv_new(i) = dv(perm_vec(i));
 
-  retval = Array<T> (dv_new);
+  retval = Array<T, Alloc> (dv_new);
 
   if (numel () > 0)
     {
@@ -692,9 +692,9 @@
   int m_n;
 };
 
-template <typename T>
-Array<T>
-Array<T>::index (const octave::idx_vector& i) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::index (const octave::idx_vector& i) const
 {
   // Colon:
   //
@@ -715,12 +715,12 @@
   //   array    | anything | same size as index
 
   octave_idx_type n = numel ();
-  Array<T> retval;
+  Array<T, Alloc> retval;
 
   if (i.is_colon ())
     {
       // A(:) produces a shallow copy as a column vector.
-      retval = Array<T> (*this, dim_vector (n, 1));
+      retval = Array<T, Alloc> (*this, dim_vector (n, 1));
     }
   else
     {
@@ -744,12 +744,12 @@
       octave_idx_type l, u;
       if (idx_len != 0 && i.is_cont_range (n, l, u))
         // If suitable, produce a shallow slice.
-        retval = Array<T> (*this, result_dims, l, u);
+        retval = Array<T, Alloc> (*this, result_dims, l, u);
       else
         {
           // Don't use resize here to avoid useless initialization for POD
           // types.
-          retval = Array<T> (result_dims);
+          retval = Array<T, Alloc> (result_dims);
 
           if (idx_len != 0)
             i.index (data (), n, retval.fortran_vec ());
@@ -759,20 +759,20 @@
   return retval;
 }
 
-template <typename T>
-Array<T>
-Array<T>::index (const octave::idx_vector& i, const octave::idx_vector& j) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::index (const octave::idx_vector& i, const octave::idx_vector& j) const
 {
   // Get dimensions, allowing Fortran indexing in the 2nd dim.
   dim_vector dv = m_dimensions.redim (2);
   octave_idx_type r = dv(0);
   octave_idx_type c = dv(1);
-  Array<T> retval;
+  Array<T, Alloc> retval;
 
   if (i.is_colon () && j.is_colon ())
     {
       // A(:,:) produces a shallow copy.
-      retval = Array<T> (*this, dv);
+      retval = Array<T, Alloc> (*this, dv);
     }
   else
     {
@@ -792,11 +792,11 @@
           octave_idx_type l, u;
           if (ii.length () > 0 && ii.is_cont_range (n, l, u))
             // If suitable, produce a shallow slice.
-            retval = Array<T> (*this, dim_vector (il, jl), l, u);
+            retval = Array<T, Alloc> (*this, dim_vector (il, jl), l, u);
           else
             {
               // Don't use resize to avoid useless initialization for POD types.
-              retval = Array<T> (dim_vector (il, jl));
+              retval = Array<T, Alloc> (dim_vector (il, jl));
 
               ii.index (data (), n, retval.fortran_vec ());
             }
@@ -804,7 +804,7 @@
       else
         {
           // Don't use resize to avoid useless initialization for POD types.
-          retval = Array<T> (dim_vector (il, jl));
+          retval = Array<T, Alloc> (dim_vector (il, jl));
 
           const T *src = data ();
           T *dest = retval.fortran_vec ();
@@ -817,12 +817,12 @@
   return retval;
 }
 
-template <typename T>
-Array<T>
-Array<T>::index (const Array<octave::idx_vector>& ia) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::index (const Array<octave::idx_vector>& ia) const
 {
   int ial = ia.numel ();
-  Array<T> retval;
+  Array<T, Alloc> retval;
 
   // FIXME: is this dispatching necessary?
   if (ial == 1)
@@ -849,7 +849,7 @@
         {
           // A(:,:,...,:) produces a shallow copy.
           dv.chop_trailing_singletons ();
-          retval = Array<T> (*this, dv);
+          retval = Array<T, Alloc> (*this, dv);
         }
       else
         {
@@ -864,11 +864,11 @@
           octave_idx_type l, u;
           if (rh.is_cont_range (l, u))
             // If suitable, produce a shallow slice.
-            retval = Array<T> (*this, rdv, l, u);
+            retval = Array<T, Alloc> (*this, rdv, l, u);
           else
             {
               // Don't use resize to avoid useless initialization for POD types.
-              retval = Array<T> (rdv);
+              retval = Array<T, Alloc> (rdv);
 
               // Do it.
               rh.index (data (), retval.fortran_vec ());
@@ -881,9 +881,9 @@
 
 // The default fill value.  Override if you want a different one.
 
-template <typename T>
+template <typename T, typename Alloc>
 T
-Array<T>::resize_fill_value (void) const
+Array<T, Alloc>::resize_fill_value (void) const
 {
   static T zero = T ();
   return zero;
@@ -892,9 +892,9 @@
 // Yes, we could do resize using index & assign.  However, that would
 // possibly involve a lot more memory traffic than we actually need.
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::resize1 (octave_idx_type n, const T& rfv)
+Array<T, Alloc>::resize1 (octave_idx_type n, const T& rfv)
 {
   if (n < 0 || ndims () != 2)
     octave::err_invalid_resize ();
@@ -940,7 +940,7 @@
         {
           static const octave_idx_type max_stack_chunk = 1024;
           octave_idx_type nn = n + std::min (nx, max_stack_chunk);
-          Array<T> tmp (Array<T> (dim_vector (nn, 1)), dv, 0, n);
+          Array<T, Alloc> tmp (Array<T, Alloc> (dim_vector (nn, 1)), dv, 0, n);
           T *dest = tmp.fortran_vec ();
 
           std::copy_n (data (), nx, dest);
@@ -951,7 +951,7 @@
     }
   else if (n != nx)
     {
-      Array<T> tmp = Array<T> (dv);
+      Array<T, Alloc> tmp = Array<T, Alloc> (dv);
       T *dest = tmp.fortran_vec ();
 
       octave_idx_type n0 = std::min (n, nx);
@@ -963,9 +963,9 @@
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::resize2 (octave_idx_type r, octave_idx_type c, const T& rfv)
+Array<T, Alloc>::resize2 (octave_idx_type r, octave_idx_type c, const T& rfv)
 {
   if (r < 0 || c < 0 || ndims () != 2)
     octave::err_invalid_resize ();
@@ -974,7 +974,7 @@
   octave_idx_type cx = columns ();
   if (r != rx || c != cx)
     {
-      Array<T> tmp = Array<T> (dim_vector (r, c));
+      Array<T, Alloc> tmp = Array<T, Alloc> (dim_vector (r, c));
       T *dest = tmp.fortran_vec ();
 
       octave_idx_type r0 = std::min (r, rx);
@@ -1005,9 +1005,9 @@
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::resize (const dim_vector& dv, const T& rfv)
+Array<T, Alloc>::resize (const dim_vector& dv, const T& rfv)
 {
   int dvl = dv.ndims ();
   if (dvl == 2)
@@ -1017,7 +1017,7 @@
       if (m_dimensions.ndims () > dvl || dv.any_neg ())
         octave::err_invalid_resize ();
 
-      Array<T> tmp (dv);
+      Array<T, Alloc> tmp (dv);
       // Prepare for recursive resizing.
       rec_resize_helper rh (dv, m_dimensions.redim (dvl));
 
@@ -1027,11 +1027,11 @@
     }
 }
 
-template <typename T>
-Array<T>
-Array<T>::index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const
 {
-  Array<T> tmp = *this;
+  Array<T, Alloc> tmp = *this;
   if (resize_ok)
     {
       octave_idx_type n = numel ();
@@ -1039,24 +1039,24 @@
       if (n != nx)
         {
           if (i.is_scalar ())
-            return Array<T> (dim_vector (1, 1), rfv);
+            return Array<T, Alloc> (dim_vector (1, 1), rfv);
           else
             tmp.resize1 (nx, rfv);
         }
 
       if (tmp.numel () != nx)
-        return Array<T> ();
+        return Array<T, Alloc> ();
     }
 
   return tmp.index (i);
 }
 
-template <typename T>
-Array<T>
-Array<T>::index (const octave::idx_vector& i, const octave::idx_vector& j,
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::index (const octave::idx_vector& i, const octave::idx_vector& j,
                  bool resize_ok, const T& rfv) const
 {
-  Array<T> tmp = *this;
+  Array<T, Alloc> tmp = *this;
   if (resize_ok)
     {
       dim_vector dv = m_dimensions.redim (2);
@@ -1067,24 +1067,24 @@
       if (r != rx || c != cx)
         {
           if (i.is_scalar () && j.is_scalar ())
-            return Array<T> (dim_vector (1, 1), rfv);
+            return Array<T, Alloc> (dim_vector (1, 1), rfv);
           else
             tmp.resize2 (rx, cx, rfv);
         }
 
       if (tmp.rows () != rx || tmp.columns () != cx)
-        return Array<T> ();
+        return Array<T, Alloc> ();
     }
 
   return tmp.index (i, j);
 }
 
-template <typename T>
-Array<T>
-Array<T>::index (const Array<octave::idx_vector>& ia,
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::index (const Array<octave::idx_vector>& ia,
                  bool resize_ok, const T& rfv) const
 {
-  Array<T> tmp = *this;
+  Array<T, Alloc> tmp = *this;
   if (resize_ok)
     {
       int ial = ia.numel ();
@@ -1098,21 +1098,21 @@
           for (int i = 0; i < ial; i++)
             all_scalars = all_scalars && ia(i).is_scalar ();
           if (all_scalars)
-            return Array<T> (dim_vector (1, 1), rfv);
+            return Array<T, Alloc> (dim_vector (1, 1), rfv);
           else
             tmp.resize (dvx, rfv);
 
           if (tmp.m_dimensions != dvx)
-            return Array<T> ();
+            return Array<T, Alloc> ();
         }
     }
 
   return tmp.index (ia);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::assign (const octave::idx_vector& i, const Array<T>& rhs, const T& rfv)
+Array<T, Alloc>::assign (const octave::idx_vector& i, const Array<T, Alloc>& rhs, const T& rfv)
 {
   octave_idx_type n = numel ();
   octave_idx_type rhl = rhs.numel ();
@@ -1129,9 +1129,9 @@
       if (m_dimensions.zero_by_zero () && colon)
         {
           if (rhl == 1)
-            *this = Array<T> (dim_vector (1, nx), rhs(0));
+            *this = Array<T, Alloc> (dim_vector (1, nx), rhs(0));
           else
-            *this = Array<T> (rhs, dim_vector (1, nx));
+            *this = Array<T, Alloc> (rhs, dim_vector (1, nx));
           return;
         }
 
@@ -1157,10 +1157,10 @@
 }
 
 // Assignment to a 2-dimensional array
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::assign (const octave::idx_vector& i, const octave::idx_vector& j,
-                  const Array<T>& rhs, const T& rfv)
+Array<T, Alloc>::assign (const octave::idx_vector& i, const octave::idx_vector& j,
+                  const Array<T, Alloc>& rhs, const T& rfv)
 {
   bool initial_dims_all_zero = m_dimensions.all_zero ();
 
@@ -1203,9 +1203,9 @@
           if (dv.zero_by_zero () && all_colons)
             {
               if (isfill)
-                *this = Array<T> (rdv, rhs(0));
+                *this = Array<T, Alloc> (rdv, rhs(0));
               else
-                *this = Array<T> (rhs, rdv);
+                *this = Array<T, Alloc> (rhs, rdv);
               return;
             }
 
@@ -1261,10 +1261,10 @@
 }
 
 // Assignment to a multi-dimensional array
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::assign (const Array<octave::idx_vector>& ia,
-                  const Array<T>& rhs, const T& rfv)
+Array<T, Alloc>::assign (const Array<octave::idx_vector>& ia,
+                  const Array<T, Alloc>& rhs, const T& rfv)
 {
   int ial = ia.numel ();
 
@@ -1327,9 +1327,9 @@
                 {
                   rdv.chop_trailing_singletons ();
                   if (isfill)
-                    *this = Array<T> (rdv, rhs(0));
+                    *this = Array<T, Alloc> (rdv, rhs(0));
                   else
-                    *this = Array<T> (rhs, rdv);
+                    *this = Array<T, Alloc> (rhs, rdv);
                   return;
                 }
 
@@ -1389,14 +1389,14 @@
 %!error <op1 is 2x2, op2 is 2x1> a(1:2,2:3) = [1;2]
 */
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::delete_elements (const octave::idx_vector& i)
+Array<T, Alloc>::delete_elements (const octave::idx_vector& i)
 {
   octave_idx_type n = numel ();
   if (i.is_colon ())
     {
-      *this = Array<T> ();
+      *this = Array<T, Alloc> ();
     }
   else if (i.length (n) != 0)
     {
@@ -1414,7 +1414,7 @@
         {
           // Special case deleting a contiguous range.
           octave_idx_type m = n + l - u;
-          Array<T> tmp (dim_vector (col_vec ? m : 1, ! col_vec ? m : 1));
+          Array<T, Alloc> tmp (dim_vector (col_vec ? m : 1, ! col_vec ? m : 1));
           const T *src = data ();
           T *dest = tmp.fortran_vec ();
           std::copy_n (src, l, dest);
@@ -1429,9 +1429,9 @@
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::delete_elements (int dim, const octave::idx_vector& i)
+Array<T, Alloc>::delete_elements (int dim, const octave::idx_vector& i)
 {
   if (dim < 0 || dim >= ndims ())
     (*current_liboctave_error_handler) ("invalid dimension in delete_elements");
@@ -1439,7 +1439,7 @@
   octave_idx_type n = m_dimensions(dim);
   if (i.is_colon ())
     {
-      *this = Array<T> ();
+      *this = Array<T, Alloc> ();
     }
   else if (i.length (n) != 0)
     {
@@ -1460,7 +1460,7 @@
           for (int k = dim + 1; k < ndims (); k++) du *= m_dimensions(k);
 
           // Special case deleting a contiguous range.
-          Array<T> tmp = Array<T> (rdv);
+          Array<T, Alloc> tmp = Array<T, Alloc> (rdv);
           const T *src = data ();
           T *dest = tmp.fortran_vec ();
           l *= dl; u *= dl; n *= dl;
@@ -1485,9 +1485,9 @@
     }
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::delete_elements (const Array<octave::idx_vector>& ia)
+Array<T, Alloc>::delete_elements (const Array<octave::idx_vector>& ia)
 {
   int ial = ia.numel ();
 
@@ -1510,7 +1510,7 @@
         {
           dim_vector dv = m_dimensions;
           dv(0) = 0;
-          *this = Array<T> (dv);
+          *this = Array<T, Alloc> (dv);
         }
       else if (k == ial)
         {
@@ -1562,9 +1562,9 @@
 
 }
 
-template <typename T>
-Array<T>&
-Array<T>::insert (const Array<T>& a, octave_idx_type r, octave_idx_type c)
+template <typename T, typename Alloc>
+Array<T, Alloc>&
+Array<T, Alloc>::insert (const Array<T, Alloc>& a, octave_idx_type r, octave_idx_type c)
 {
   octave::idx_vector i (r, r + a.rows ());
   octave::idx_vector j (c, c + a.columns ());
@@ -1583,9 +1583,9 @@
   return *this;
 }
 
-template <typename T>
-Array<T>&
-Array<T>::insert (const Array<T>& a, const Array<octave_idx_type>& ra_idx)
+template <typename T, typename Alloc>
+Array<T, Alloc>&
+Array<T, Alloc>::insert (const Array<T, Alloc>& a, const Array<octave_idx_type>& ra_idx)
 {
   octave_idx_type n = ra_idx.numel ();
   Array<octave::idx_vector> idx (dim_vector (n, 1));
@@ -1598,9 +1598,9 @@
   return *this;
 }
 
-template <typename T>
-Array<T>
-Array<T>::transpose (void) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::transpose (void) const
 {
   assert (ndims () == 2);
 
@@ -1609,7 +1609,7 @@
 
   if (nr >= 8 && nc >= 8)
     {
-      Array<T> result (dim_vector (nc, nr));
+      Array<T, Alloc> result (dim_vector (nc, nr));
 
       // Reuse the implementation used for permuting.
 
@@ -1619,7 +1619,7 @@
     }
   else if (nr > 1 && nc > 1)
     {
-      Array<T> result (dim_vector (nc, nr));
+      Array<T, Alloc> result (dim_vector (nc, nr));
 
       for (octave_idx_type j = 0; j < nc; j++)
         for (octave_idx_type i = 0; i < nr; i++)
@@ -1630,7 +1630,7 @@
   else
     {
       // Fast transpose for vectors and empty matrices.
-      return Array<T> (*this, dim_vector (nc, nr));
+      return Array<T, Alloc> (*this, dim_vector (nc, nr));
     }
 }
 
@@ -1641,9 +1641,9 @@
   return x;
 }
 
-template <typename T>
-Array<T>
-Array<T>::hermitian (T (*fcn) (const T&)) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::hermitian (T (*fcn) (const T&)) const
 {
   assert (ndims () == 2);
 
@@ -1655,7 +1655,7 @@
 
   if (nr >= 8 && nc >= 8)
     {
-      Array<T> result (dim_vector (nc, nr));
+      Array<T, Alloc> result (dim_vector (nc, nr));
 
       // Blocked transpose to attempt to avoid cache misses.
 
@@ -1695,7 +1695,7 @@
     }
   else
     {
-      Array<T> result (dim_vector (nc, nr));
+      Array<T, Alloc> result (dim_vector (nc, nr));
 
       for (octave_idx_type j = 0; j < nc; j++)
         for (octave_idx_type i = 0; i < nr; i++)
@@ -1739,9 +1739,9 @@
 
 */
 
-template <typename T>
+template <typename T, typename Alloc>
 T *
-Array<T>::fortran_vec (void)
+Array<T, Alloc>::fortran_vec (void)
 {
   make_unique ();
 
@@ -1756,14 +1756,14 @@
   return false;
 }
 
-template <typename T>
-Array<T>
-Array<T>::sort (int dim, sortmode mode) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::sort (int dim, sortmode mode) const
 {
   if (dim < 0)
     (*current_liboctave_error_handler) ("sort: invalid dimension");
 
-  Array<T> m (dims ());
+  Array<T, Alloc> m (dims ());
 
   dim_vector dv = m.dims ();
 
@@ -1868,15 +1868,15 @@
   return m;
 }
 
-template <typename T>
-Array<T>
-Array<T>::sort (Array<octave_idx_type>& sidx, int dim,
-                sortmode mode) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::sort (Array<octave_idx_type>& sidx, int dim,
+                       sortmode mode) const
 {
   if (dim < 0 || dim >= ndims ())
     (*current_liboctave_error_handler) ("sort: invalid dimension");
 
-  Array<T> m (dims ());
+  Array<T, Alloc> m (dims ());
 
   dim_vector dv = m.dims ();
 
@@ -2009,9 +2009,9 @@
   return m;
 }
 
-template <typename T>
-typename Array<T>::compare_fcn_type
-safe_comparator (sortmode mode, const Array<T>& /* a */,
+template <typename T, typename Alloc>
+typename Array<T, Alloc>::compare_fcn_type
+safe_comparator (sortmode mode, const Array<T, Alloc>& /* a */,
                  bool /* allow_chk */)
 {
   if (mode == ASCENDING)
@@ -2022,9 +2022,9 @@
     return nullptr;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 sortmode
-Array<T>::issorted (sortmode mode) const
+Array<T, Alloc>::issorted (sortmode mode) const
 {
   octave_sort<T> lsort;
 
@@ -2054,9 +2054,9 @@
 
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 Array<octave_idx_type>
-Array<T>::sort_rows_idx (sortmode mode) const
+Array<T, Alloc>::sort_rows_idx (sortmode mode) const
 {
   Array<octave_idx_type> idx;
 
@@ -2072,9 +2072,9 @@
   return idx;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 sortmode
-Array<T>::is_sorted_rows (sortmode mode) const
+Array<T, Alloc>::is_sorted_rows (sortmode mode) const
 {
   octave_sort<T> lsort;
 
@@ -2133,9 +2133,9 @@
 }
 
 // Do a binary lookup in a sorted array.
-template <typename T>
+template <typename T, typename Alloc>
 octave_idx_type
-Array<T>::lookup (const T& value, sortmode mode) const
+Array<T, Alloc>::lookup (const T& value, sortmode mode) const
 {
   octave_idx_type n = numel ();
   octave_sort<T> lsort;
@@ -2154,9 +2154,9 @@
   return lsort.lookup (data (), n, value);
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 Array<octave_idx_type>
-Array<T>::lookup (const Array<T>& values, sortmode mode) const
+Array<T, Alloc>::lookup (const Array<T, Alloc>& values, sortmode mode) const
 {
   octave_idx_type n = numel ();
   octave_idx_type nval = values.numel ();
@@ -2198,9 +2198,9 @@
   return idx;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 octave_idx_type
-Array<T>::nnz (void) const
+Array<T, Alloc>::nnz (void) const
 {
   const T *src = data ();
   octave_idx_type nel = numel ();
@@ -2213,9 +2213,9 @@
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 Array<octave_idx_type>
-Array<T>::find (octave_idx_type n, bool backward) const
+Array<T, Alloc>::find (octave_idx_type n, bool backward) const
 {
   Array<octave_idx_type> retval;
   const T *src = data ();
@@ -2294,9 +2294,9 @@
   return retval;
 }
 
-template <typename T>
-Array<T>
-Array<T>::nth_element (const octave::idx_vector& n, int dim) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::nth_element (const octave::idx_vector& n, int dim) const
 {
   if (dim < 0)
     (*current_liboctave_error_handler) ("nth_element: invalid dimension");
@@ -2313,7 +2313,7 @@
   dv.chop_trailing_singletons ();
   dim = std::min (dv.ndims (), static_cast<octave_idx_type> (dim));
 
-  Array<T> m (dv);
+  Array<T, Alloc> m (dv);
 
   if (m.isempty ())
     return m;
@@ -2464,7 +2464,7 @@
     return *this;                                                       \
   }                                                                     \
   template <> API Array<T>                                              \
-  Array<T>::sort (Array<octave_idx_type> &sidx, int, sortmode) const    \
+  Array<T>::sort (Array<octave_idx_type> &sidx, int, sortmode) const  \
   {                                                                     \
     sidx = Array<octave_idx_type> ();                                   \
     return *this;                                                       \
@@ -2516,13 +2516,13 @@
 
 #define NO_INSTANTIATE_ARRAY_SORT(T) NO_INSTANTIATE_ARRAY_SORT_API (T,)
 
-template <typename T>
-Array<T>
-Array<T>::diag (octave_idx_type k) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::diag (octave_idx_type k) const
 {
   dim_vector dv = dims ();
   octave_idx_type nd = dv.ndims ();
-  Array<T> d;
+  Array<T, Alloc> d;
 
   if (nd > 2)
     (*current_liboctave_error_handler) ("Matrix must be 2-dimensional");
@@ -2584,7 +2584,7 @@
       if (nnr == 1)
         {
           octave_idx_type n = nnc + std::abs (k);
-          d = Array<T> (dim_vector (n, n), resize_fill_value ());
+          d = Array<T, Alloc> (dim_vector (n, n), resize_fill_value ());
 
           for (octave_idx_type i = 0; i < nnc; i++)
             d.xelem (i+roff, i+coff) = elem (0, i);
@@ -2592,7 +2592,7 @@
       else
         {
           octave_idx_type n = nnr + std::abs (k);
-          d = Array<T> (dim_vector (n, n), resize_fill_value ());
+          d = Array<T, Alloc> (dim_vector (n, n), resize_fill_value ());
 
           for (octave_idx_type i = 0; i < nnr; i++)
             d.xelem (i+roff, i+coff) = elem (i, 0);
@@ -2602,14 +2602,14 @@
   return d;
 }
 
-template <typename T>
-Array<T>
-Array<T>::diag (octave_idx_type m, octave_idx_type n) const
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::diag (octave_idx_type m, octave_idx_type n) const
 {
   if (ndims () != 2 || (rows () != 1 && cols () != 1))
     (*current_liboctave_error_handler) ("cat: invalid dimension");
 
-  Array<T> retval (dim_vector (m, n), resize_fill_value ());
+  Array<T, Alloc> retval (dim_vector (m, n), resize_fill_value ());
 
   octave_idx_type nel = std::min (numel (), std::min (m, n));
   for (octave_idx_type i = 0; i < nel; i++)
@@ -2618,9 +2618,9 @@
   return retval;
 }
 
-template <typename T>
-Array<T>
-Array<T>::cat (int dim, octave_idx_type n, const Array<T> *array_list)
+template <typename T, typename Alloc>
+Array<T, Alloc>
+Array<T, Alloc>::cat (int dim, octave_idx_type n, const Array<T, Alloc> *array_list)
 {
   // Default concatenation.
   bool (dim_vector::*concat_rule) (const dim_vector&, int) = &dim_vector::concat;
@@ -2636,7 +2636,7 @@
   if (n == 1)
     return array_list[0];
   else if (n == 0)
-    return Array<T> ();
+    return Array<T, Alloc> ();
 
   // Special case:
   //
@@ -2684,7 +2684,7 @@
     if (! (dv.*concat_rule) (array_list[i].dims (), dim))
       (*current_liboctave_error_handler) ("cat: dimension mismatch");
 
-  Array<T> retval (dv);
+  Array<T, Alloc> retval (dv);
 
   if (retval.isempty ())
     return retval;
@@ -2722,9 +2722,9 @@
   return retval;
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 void
-Array<T>::print_info (std::ostream& os, const std::string& prefix) const
+Array<T, Alloc>::print_info (std::ostream& os, const std::string& prefix) const
 {
   os << prefix << "m_rep address:   " << m_rep << '\n'
      << prefix << "m_rep->m_len:    " << m_rep->m_len << '\n'
@@ -2739,8 +2739,8 @@
   //     << prefix << "cols: " << cols () << "\n";
 }
 
-template <typename T>
-bool Array<T>::optimize_dimensions (const dim_vector& dv)
+template <typename T, typename Alloc>
+bool Array<T, Alloc>::optimize_dimensions (const dim_vector& dv)
 {
   bool retval = m_dimensions == dv;
   if (retval)
@@ -2749,29 +2749,33 @@
   return retval;
 }
 
-template <typename T>
-void Array<T>::instantiation_guard ()
+template <typename T, typename Alloc>
+void Array<T, Alloc>::instantiation_guard ()
 {
   // This guards against accidental implicit instantiations.
-  // Array<T> instances should always be explicit and use INSTANTIATE_ARRAY.
+  // Array<T, Alloc> instances should always be explicit and use INSTANTIATE_ARRAY.
   T::__xXxXx__ ();
 }
 
 #if defined (__clang__)
-#  define INSTANTIATE_ARRAY(T, API)                            \
-     template <> API void Array<T>::instantiation_guard () { } \
-     template class API Array<T>
+#  define INSTANTIATE_ARRAY(T, API)             \
+  template <> API void                          \
+  Array<T>::instantiation_guard () { }          \
+                                                \
+  template class API Array<T>
 #else
-#  define INSTANTIATE_ARRAY(T, API)                            \
-     template <> API void Array<T>::instantiation_guard () { } \
-     template class Array<T>
+#  define INSTANTIATE_ARRAY(T, API)             \
+  template <> API void                          \
+  Array<T>::instantiation_guard () { }          \
+                                                \
+  template class Array<T>
 #endif
 
 // FIXME: is this used?
 
-template <typename T>
+template <typename T, typename Alloc>
 std::ostream&
-operator << (std::ostream& os, const Array<T>& a)
+operator << (std::ostream& os, const Array<T, Alloc>& a)
 {
   dim_vector a_dims = a.dims ();
 
--- a/liboctave/array/Array.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/Array.h	Wed Dec 15 20:26:12 2021 -0500
@@ -123,65 +123,89 @@
 //!   - string_vector: Array<std::string> with 1 column
 //!   - Cell: Array<octave_value>, equivalent to an Octave cell.
 
-template <typename T>
+template <typename T, typename Alloc>
 class
 Array
 {
 protected:
 
   //! The real representation of all arrays.
-  class ArrayRep
+  class ArrayRep : public Alloc
   {
   public:
 
-    T *m_data;
+    typedef std::allocator_traits<Alloc> Alloc_traits;
+
+    typedef typename Alloc_traits::rebind_traits<T> T_Alloc_traits;
+    typedef typename T_Alloc_traits::pointer pointer;
+
+    pointer m_data;
     octave_idx_type m_len;
     octave::refcount<octave_idx_type> m_count;
 
-    ArrayRep (T *d, octave_idx_type l)
-      : m_data (new T [l]), m_len (l), m_count (1)
+    ArrayRep (pointer d, octave_idx_type len)
+      : Alloc (), m_data (allocate (len)), m_len (len), m_count (1)
     {
-      std::copy_n (d, l, m_data);
+      std::copy_n (d, len, m_data);
     }
 
     template <typename U>
-    ArrayRep (U *d, octave_idx_type l)
-      : m_data (new T [l]), m_len (l), m_count (1)
+    ArrayRep (U *d, octave_idx_type len)
+      : Alloc (), m_data (allocate (len)), m_len (len), m_count (1)
     {
-      std::copy_n (d, l, m_data);
+      std::copy_n (d, len, m_data);
     }
 
     // Use new instead of setting data to 0 so that fortran_vec and
     // data always return valid addresses, even for zero-size arrays.
 
-    ArrayRep (void) : m_data (new T [0]), m_len (0), m_count (1) { }
-
-    explicit ArrayRep (octave_idx_type n)
-      : m_data (new T [n]), m_len (n), m_count (1) { }
+    ArrayRep (void)
+      : Alloc (), m_data (allocate (0)), m_len (0), m_count (1) { }
 
-    explicit ArrayRep (octave_idx_type n, const T& val)
-      : m_data (new T [n]), m_len (n), m_count (1)
+    explicit ArrayRep (octave_idx_type len)
+      : Alloc (), m_data (allocate (len)), m_len (len), m_count (1) { }
+
+    explicit ArrayRep (octave_idx_type len, const T& val)
+      : Alloc (), m_data (allocate (len)), m_len (len), m_count (1)
     {
-      std::fill_n (m_data, n, val);
+      std::fill_n (m_data, len, val);
     }
 
-    explicit ArrayRep (T *ptr, const dim_vector& dv)
-      : m_data (ptr), m_len (dv.safe_numel ()), m_count (1)
+    explicit ArrayRep (pointer ptr, const dim_vector& dv,
+                       const Alloc& xallocator = Alloc ())
+      : Alloc (xallocator), m_data (ptr), m_len (dv.safe_numel ()), m_count (1)
     { }
 
+    // FIXME: Should the allocator be copied or created with the default?
     ArrayRep (const ArrayRep& a)
-      : m_data (new T [a.m_len]), m_len (a.m_len), m_count (1)
+      : Alloc (), m_data (allocate (a.m_len)), m_len (a.m_len),
+        m_count (1)
     {
       std::copy_n (a.m_data, a.m_len, m_data);
     }
 
-    ~ArrayRep (void) { delete [] m_data; }
+    ~ArrayRep (void) { deallocate (m_data, m_len); }
 
     octave_idx_type numel (void) const { return m_len; }
 
     // No assignment!
 
     ArrayRep& operator = (const ArrayRep&) = delete;
+
+    pointer allocate (size_t len)
+    {
+      pointer data = Alloc_traits::allocate (*this, len);
+      for (size_t i = 0; i < len; i++)
+        T_Alloc_traits::construct (*this, data+i);
+      return data;
+    }
+
+    void deallocate (pointer data, size_t len)
+    {
+      for (size_t i = 0; i < len; i++)
+        T_Alloc_traits::destroy (*this, data+i);
+      Alloc_traits::deallocate (*this, data, len);
+    }
   };
 
   //--------------------------------------------------------------------
@@ -219,7 +243,7 @@
 
   dim_vector m_dimensions;
 
-  typename Array<T>::ArrayRep *m_rep;
+  typename Array<T, Alloc>::ArrayRep *m_rep;
 
   // Rationale:
   // m_slice_data is a pointer to m_rep->m_data, denoting together with m_slice_len the
@@ -232,7 +256,7 @@
   octave_idx_type m_slice_len;
 
   //! slice constructor
-  Array (const Array<T>& a, const dim_vector& dv,
+  Array (const Array<T, Alloc>& a, const dim_vector& dv,
          octave_idx_type l, octave_idx_type u)
     : m_dimensions (dv), m_rep(a.m_rep), m_slice_data (a.m_slice_data+l), m_slice_len (u-l)
   {
@@ -242,7 +266,7 @@
 
 private:
 
-  static OCTARRAY_API typename Array<T>::ArrayRep *nil_rep (void);
+  static OCTARRAY_API typename Array<T, Alloc>::ArrayRep *nil_rep (void);
 
 public:
 
@@ -257,7 +281,7 @@
   //! nD uninitialized ctor.
   explicit Array (const dim_vector& dv)
     : m_dimensions (dv),
-      m_rep (new typename Array<T>::ArrayRep (dv.safe_numel ())),
+      m_rep (new typename Array<T, Alloc>::ArrayRep (dv.safe_numel ())),
       m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len)
   {
     m_dimensions.chop_trailing_singletons ();
@@ -266,7 +290,7 @@
   //! nD initialized ctor.
   explicit Array (const dim_vector& dv, const T& val)
     : m_dimensions (dv),
-      m_rep (new typename Array<T>::ArrayRep (dv.safe_numel ())),
+      m_rep (new typename Array<T, Alloc>::ArrayRep (dv.safe_numel ())),
       m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len)
   {
     fill (val);
@@ -279,38 +303,39 @@
   // object is deleted.  The dimension vector DV must be consistent with
   // the size of the allocated PTR array.
 
-  explicit Array (T *ptr, const dim_vector& dv)
+  explicit Array (T *ptr, const dim_vector& dv,
+                  const Alloc& xallocator = Alloc ())
     : m_dimensions (dv),
-      m_rep (new typename Array<T>::ArrayRep (ptr, dv)),
+      m_rep (new typename Array<T, Alloc>::ArrayRep (ptr, dv, xallocator)),
       m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len)
   {
     m_dimensions.chop_trailing_singletons ();
   }
 
   //! Reshape constructor.
-  OCTARRAY_API Array (const Array<T>& a, const dim_vector& dv);
+  OCTARRAY_API Array (const Array<T, Alloc>& a, const dim_vector& dv);
 
   //! Constructor from standard library sequence containers.
   template<template <typename...> class Container>
   Array (const Container<T>& a, const dim_vector& dv);
 
   //! Type conversion case.
-  template <typename U>
-  Array (const Array<U>& a)
+  template <typename U, typename A = Alloc>
+  Array (const Array<U, A>& a)
     : m_dimensions (a.dims ()),
-      m_rep (new typename Array<T>::ArrayRep (a.data (), a.numel ())),
+      m_rep (new typename Array<T, Alloc>::ArrayRep (a.data (), a.numel ())),
       m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len)
   { }
 
   //! No type conversion case.
-  Array (const Array<T>& a)
+  Array (const Array<T, Alloc>& a)
     : m_dimensions (a.m_dimensions), m_rep (a.m_rep), m_slice_data (a.m_slice_data),
       m_slice_len (a.m_slice_len)
   {
     m_rep->m_count++;
   }
 
-  Array (Array<T>&& a)
+  Array (Array<T, Alloc>&& a)
     : m_dimensions (std::move (a.m_dimensions)), m_rep (a.m_rep),
       m_slice_data (a.m_slice_data), m_slice_len (a.m_slice_len)
   {
@@ -331,7 +356,7 @@
       delete m_rep;
   }
 
-  Array<T>& operator = (const Array<T>& a)
+  Array<T, Alloc>& operator = (const Array<T, Alloc>& a)
   {
     if (this != &a)
       {
@@ -349,7 +374,7 @@
     return *this;
   }
 
-  Array<T>& operator = (Array<T>&& a)
+  Array<T, Alloc>& operator = (Array<T, Alloc>&& a)
   {
     if (this != &a)
       {
@@ -387,9 +412,9 @@
   //@}
 
   //! Return the array as a column vector.
-  Array<T> as_column (void) const
+  Array<T, Alloc> as_column (void) const
   {
-    Array<T> retval (*this);
+    Array<T, Alloc> retval (*this);
     if (m_dimensions.ndims () != 2 || m_dimensions(1) != 1)
       retval.m_dimensions = dim_vector (numel (), 1);
 
@@ -397,9 +422,9 @@
   }
 
   //! Return the array as a row vector.
-  Array<T> as_row (void) const
+  Array<T, Alloc> as_row (void) const
   {
-    Array<T> retval (*this);
+    Array<T, Alloc> retval (*this);
     if (m_dimensions.ndims () != 2 || m_dimensions(0) != 1)
       retval.m_dimensions = dim_vector (1, numel ());
 
@@ -407,9 +432,9 @@
   }
 
   //! Return the array as a matrix.
-  Array<T> as_matrix (void) const
+  Array<T, Alloc> as_matrix (void) const
   {
-    Array<T> retval (*this);
+    Array<T, Alloc> retval (*this);
     if (m_dimensions.ndims () != 2)
       retval.m_dimensions = m_dimensions.redim (2);
 
@@ -462,16 +487,17 @@
   const dim_vector& dims (void) const { return m_dimensions; }
 
   //! Chop off leading singleton dimensions
-  OCTARRAY_API Array<T> squeeze (void) const;
+  OCTARRAY_API Array<T, Alloc> squeeze (void) const;
 
   OCTARRAY_API octave_idx_type compute_index (octave_idx_type i, octave_idx_type j) const;
   OCTARRAY_API octave_idx_type compute_index (octave_idx_type i, octave_idx_type j,
                                  octave_idx_type k) const;
   OCTARRAY_API octave_idx_type compute_index (const Array<octave_idx_type>& ra_idx) const;
 
-  octave_idx_type compute_index_unchecked (const Array<octave_idx_type>& ra_idx)
-  const
-  { return m_dimensions.compute_index (ra_idx.data (), ra_idx.numel ()); }
+  octave_idx_type compute_index_unchecked (const Array<octave_idx_type>& ra_idx) const
+  {
+    return m_dimensions.compute_index (ra_idx.data (), ra_idx.numel ());
+  }
 
   // No checking, even for multiple references, ever.
 
@@ -517,7 +543,7 @@
   { return elem (i, dim2 ()*k+j); }
 
   T& elem (const Array<octave_idx_type>& ra_idx)
-  { return Array<T>::elem (compute_index_unchecked (ra_idx)); }
+  { return Array<T, Alloc>::elem (compute_index_unchecked (ra_idx)); }
 
   T& operator () (octave_idx_type n) { return elem (n); }
   T& operator () (octave_idx_type i, octave_idx_type j) { return elem (i, j); }
@@ -544,7 +570,7 @@
   { return xelem (i, j, k); }
 
   crefT elem (const Array<octave_idx_type>& ra_idx) const
-  { return Array<T>::xelem (compute_index_unchecked (ra_idx)); }
+  { return Array<T, Alloc>::xelem (compute_index_unchecked (ra_idx)); }
 
   crefT operator () (octave_idx_type n) const { return elem (n); }
   crefT operator () (octave_idx_type i, octave_idx_type j) const
@@ -558,22 +584,22 @@
   // Fast extractors.  All of these produce shallow copies.
 
   //! Extract column: A(:,k+1).
-  OCTARRAY_API Array<T> column (octave_idx_type k) const;
+  OCTARRAY_API Array<T, Alloc> column (octave_idx_type k) const;
   //! Extract page: A(:,:,k+1).
-  OCTARRAY_API Array<T> page (octave_idx_type k) const;
+  OCTARRAY_API Array<T, Alloc> page (octave_idx_type k) const;
 
   //! Extract a slice from this array as a column vector: A(:)(lo+1:up).
   //! Must be 0 <= lo && up <= numel.  May be up < lo.
-  OCTARRAY_API Array<T> linear_slice (octave_idx_type lo, octave_idx_type up) const;
+  OCTARRAY_API Array<T, Alloc> linear_slice (octave_idx_type lo, octave_idx_type up) const;
 
-  Array<T> reshape (octave_idx_type nr, octave_idx_type nc) const
-  { return Array<T> (*this, dim_vector (nr, nc)); }
+  Array<T, Alloc> reshape (octave_idx_type nr, octave_idx_type nc) const
+  { return Array<T, Alloc> (*this, dim_vector (nr, nc)); }
 
-  Array<T> reshape (const dim_vector& new_dims) const
-  { return Array<T> (*this, new_dims); }
+  Array<T, Alloc> reshape (const dim_vector& new_dims) const
+  { return Array<T, Alloc> (*this, new_dims); }
 
-  OCTARRAY_API Array<T> permute (const Array<octave_idx_type>& vec, bool inv = false) const;
-  Array<T> ipermute (const Array<octave_idx_type>& vec) const
+  OCTARRAY_API Array<T, Alloc> permute (const Array<octave_idx_type>& vec, bool inv = false) const;
+  Array<T, Alloc> ipermute (const Array<octave_idx_type>& vec) const
   { return permute (vec, true); }
 
   bool issquare (void) const { return (dim1 () == dim2 ()); }
@@ -584,8 +610,8 @@
 
   bool is_nd_vector (void) const { return m_dimensions.is_nd_vector (); }
 
-  OCTARRAY_API Array<T> transpose (void) const;
-  OCTARRAY_API Array<T> hermitian (T (*fcn) (const T&) = nullptr) const;
+  OCTARRAY_API Array<T, Alloc> transpose (void) const;
+  OCTARRAY_API Array<T, Alloc> hermitian (T (*fcn) (const T&) = nullptr) const;
 
   const T * data (void) const { return m_slice_data; }
 
@@ -602,11 +628,11 @@
 
   //@{
   //! Indexing without resizing.
-  OCTARRAY_API Array<T> index (const octave::idx_vector& i) const;
+  OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i) const;
 
-  OCTARRAY_API Array<T> index (const octave::idx_vector& i, const octave::idx_vector& j) const;
+  OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i, const octave::idx_vector& j) const;
 
-  OCTARRAY_API Array<T> index (const Array<octave::idx_vector>& ia) const;
+  OCTARRAY_API Array<T, Alloc> index (const Array<octave::idx_vector>& ia) const;
   //@}
 
   virtual OCTARRAY_API T resize_fill_value (void) const;
@@ -632,24 +658,24 @@
   // FIXME: this is really a corner case, that should better be
   // handled directly in liboctinterp.
 
-  OCTARRAY_API Array<T> index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const;
-  Array<T> index (const octave::idx_vector& i, bool resize_ok) const
+  OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const;
+  Array<T, Alloc> index (const octave::idx_vector& i, bool resize_ok) const
   {
     return index (i, resize_ok, resize_fill_value ());
   }
 
-  OCTARRAY_API Array<T> index (const octave::idx_vector& i, const octave::idx_vector& j,
+  OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i, const octave::idx_vector& j,
                                bool resize_ok,
                                const T& rfv) const;
-  Array<T> index (const octave::idx_vector& i, const octave::idx_vector& j,
+  Array<T, Alloc> index (const octave::idx_vector& i, const octave::idx_vector& j,
                   bool resize_ok) const
   {
     return index (i, j, resize_ok, resize_fill_value ());
   }
 
-  OCTARRAY_API Array<T> index (const Array<octave::idx_vector>& ia, bool resize_ok,
+  OCTARRAY_API Array<T, Alloc> index (const Array<octave::idx_vector>& ia, bool resize_ok,
                                const T& rfv) const;
-  Array<T> index (const Array<octave::idx_vector>& ia, bool resize_ok) const
+  Array<T, Alloc> index (const Array<octave::idx_vector>& ia, bool resize_ok) const
   {
     return index (ia, resize_ok, resize_fill_value ());
   }
@@ -657,22 +683,22 @@
 
   //@{
   //! Indexed assignment (always with resize & fill).
-  OCTARRAY_API void assign (const octave::idx_vector& i, const Array<T>& rhs, const T& rfv);
-  void assign (const octave::idx_vector& i, const Array<T>& rhs)
+  OCTARRAY_API void assign (const octave::idx_vector& i, const Array<T, Alloc>& rhs, const T& rfv);
+  void assign (const octave::idx_vector& i, const Array<T, Alloc>& rhs)
   {
     assign (i, rhs, resize_fill_value ());
   }
 
   OCTARRAY_API void assign (const octave::idx_vector& i, const octave::idx_vector& j,
-                            const Array<T>& rhs,
+                            const Array<T, Alloc>& rhs,
                             const T& rfv);
-  void assign (const octave::idx_vector& i, const octave::idx_vector& j, const Array<T>& rhs)
+  void assign (const octave::idx_vector& i, const octave::idx_vector& j, const Array<T, Alloc>& rhs)
   {
     assign (i, j, rhs, resize_fill_value ());
   }
 
-  OCTARRAY_API void assign (const Array<octave::idx_vector>& ia, const Array<T>& rhs, const T& rfv);
-  void assign (const Array<octave::idx_vector>& ia, const Array<T>& rhs)
+  OCTARRAY_API void assign (const Array<octave::idx_vector>& ia, const Array<T, Alloc>& rhs, const T& rfv);
+  void assign (const Array<octave::idx_vector>& ia, const Array<T, Alloc>& rhs)
   {
     assign (ia, rhs, resize_fill_value ());
   }
@@ -695,10 +721,10 @@
   //! size (a) is [d1 d2 ... dN] and idx is [i1 i2 ... iN], this
   //! method is equivalent to x(i1:i1+d1-1, i2:i2+d2-1, ... ,
   //! iN:iN+dN-1) = a.
-  OCTARRAY_API Array<T>& insert (const Array<T>& a, const Array<octave_idx_type>& idx);
+  OCTARRAY_API Array<T, Alloc>& insert (const Array<T, Alloc>& a, const Array<octave_idx_type>& idx);
 
   //! This is just a special case for idx = [r c 0 ...]
-  OCTARRAY_API Array<T>& insert (const Array<T>& a, octave_idx_type r, octave_idx_type c);
+  OCTARRAY_API Array<T, Alloc>& insert (const Array<T, Alloc>& a, octave_idx_type r, octave_idx_type c);
 
   void maybe_economize (void)
   {
@@ -713,9 +739,9 @@
 
   OCTARRAY_API void print_info (std::ostream& os, const std::string& prefix) const;
 
-  OCTARRAY_API Array<T> sort (int dim = 0, sortmode mode = ASCENDING) const;
-  OCTARRAY_API Array<T> sort (Array<octave_idx_type>& sidx, int dim = 0,
-                              sortmode mode = ASCENDING) const;
+  OCTARRAY_API Array<T, Alloc> sort (int dim = 0, sortmode mode = ASCENDING) const;
+  OCTARRAY_API Array<T, Alloc> sort (Array<octave_idx_type>& sidx, int dim = 0,
+                                     sortmode mode = ASCENDING) const;
 
   //! Ordering is auto-detected or can be specified.
   OCTARRAY_API sortmode issorted (sortmode mode = UNSORTED) const;
@@ -732,8 +758,8 @@
 
   //! Ditto, but for an array of values, specializing on the case when values
   //! are sorted.  NaNs get the value N.
-  OCTARRAY_API Array<octave_idx_type> lookup (const Array<T>& values,
-                                 sortmode mode = UNSORTED) const;
+  OCTARRAY_API Array<octave_idx_type> lookup (const Array<T, Alloc>& values,
+                                                sortmode mode = UNSORTED) const;
 
   //! Count nonzero elements.
   OCTARRAY_API octave_idx_type nnz (void) const;
@@ -741,37 +767,42 @@
   //! Find indices of (at most n) nonzero elements.  If n is specified,
   //! backward specifies search from backward.
   OCTARRAY_API Array<octave_idx_type> find (octave_idx_type n = -1,
-                               bool backward = false) const;
+                                              bool backward = false) const;
 
   //! Returns the n-th element in increasing order, using the same
   //! ordering as used for sort.  n can either be a scalar index or a
   //! contiguous range.
-  OCTARRAY_API Array<T> nth_element (const octave::idx_vector& n, int dim = 0) const;
+  OCTARRAY_API Array<T, Alloc> nth_element (const octave::idx_vector& n, int dim = 0) const;
 
   //! Get the kth super or subdiagonal.  The zeroth diagonal is the
   //! ordinary diagonal.
-  OCTARRAY_API Array<T> diag (octave_idx_type k = 0) const;
+  OCTARRAY_API Array<T, Alloc> diag (octave_idx_type k = 0) const;
 
-  OCTARRAY_API Array<T> diag (octave_idx_type m, octave_idx_type n) const;
+  OCTARRAY_API Array<T, Alloc> diag (octave_idx_type m, octave_idx_type n) const;
 
   //! Concatenation along a specified (0-based) dimension, equivalent
   //! to cat().  dim = -1 corresponds to dim = 0 and dim = -2
   //! corresponds to dim = 1, but apply the looser matching rules of
   //! vertcat/horzcat.
-  static OCTARRAY_API Array<T>
-  cat (int dim, octave_idx_type n, const Array<T> *array_list);
+  static OCTARRAY_API Array<T, Alloc>
+  cat (int dim, octave_idx_type n, const Array<T, Alloc> *array_list);
 
-  //! Apply function fcn to each element of the Array<T>.  This function
+  //! Apply function fcn to each element of the Array<T, Alloc>.  This function
   //! is optimized with a manually unrolled loop.
-  template <typename U, typename F>
-  Array<U>
+#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+  template <typename U, typename F,
+            typename A = std::pmr::polymorphic_allocator<U>>
+#else
+  template <typename U, typename F, typename A = std::allocator<U>>
+#endif
+  Array<U, A>
   map (F fcn) const
   {
     octave_idx_type len = numel ();
 
     const T *m = data ();
 
-    Array<U> result (dims ());
+    Array<U, A> result (dims ());
     U *p = result.fortran_vec ();
 
     octave_idx_type i;
@@ -795,15 +826,23 @@
 
   //@{
   //! Overloads for function references.
-  template <typename U>
-  Array<U>
+#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+  template <typename U, typename A = std::pmr::polymorphic_allocator<U>>
+#else
+  template <typename U, typename A = std::allocator<U>>
+#endif
+  Array<U, A>
   map (U (&fcn) (T)) const
-  { return map<U, U (&) (T)> (fcn); }
+  { return map<U, U (&) (T), A> (fcn); }
 
-  template <typename U>
-  Array<U>
+#if defined (HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR)
+  template <typename U, typename A = std::pmr::polymorphic_allocator<U>>
+#else
+  template <typename U, typename A = std::allocator<U>>
+#endif
+  Array<U, A>
   map (U (&fcn) (const T&)) const
-  { return map<U, U (&) (const T&)> (fcn); }
+  { return map<U, U (&) (const T&), A> (fcn); }
   //@}
 
   //! Generic any/all test functionality with arbitrary predicate.
@@ -839,7 +878,7 @@
   { return test<bool (&) (const T&), true> (fcn); }
   //@}
 
-  template <typename U> friend class Array;
+  template <typename U, typename A> friend class Array;
 
   //! Returns true if this->dims () == dv, and if so, replaces this->m_dimensions
   //! by a shallow copy of dv.  This is useful for maintaining several arrays
@@ -853,10 +892,10 @@
 // We use a variadic template for template template parameter so that
 // we don't have to specify all the template parameters and limit this
 // to Container<T>. http://stackoverflow.com/a/20499809/1609556
-template<typename T>
+template<typename T, typename Alloc>
 template<template <typename...> class Container>
-Array<T>::Array (const Container<T>& a, const dim_vector& dv)
-  : m_dimensions (dv), m_rep (new typename Array<T>::ArrayRep (dv.safe_numel ())),
+Array<T, Alloc>::Array (const Container<T>& a, const dim_vector& dv)
+  : m_dimensions (dv), m_rep (new typename Array<T, Alloc>::ArrayRep (dv.safe_numel ())),
     m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len)
 {
   if (m_dimensions.safe_numel () != octave_idx_type (a.size ()))
@@ -875,8 +914,8 @@
   m_dimensions.chop_trailing_singletons ();
 }
 
-template <typename T>
+template <typename T, typename Alloc>
 OCTARRAY_API std::ostream&
-operator << (std::ostream& os, const Array<T>& a);
+operator << (std::ostream& os, const Array<T, Alloc>& a);
 
 #endif
--- a/liboctave/array/MArray-C.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray-C.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -34,7 +34,7 @@
 #include "MArray.h"
 #include "MArray.cc"
 
-INSTANTIATE_MARRAY (Complex);
+INSTANTIATE_MARRAY (Complex, OCTAVE_API);
 
 INSTANTIATE_MARRAY_FRIENDS (Complex, OCTAVE_API)
 
--- a/liboctave/array/MArray-d.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray-d.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -32,7 +32,7 @@
 #include "MArray.h"
 #include "MArray.cc"
 
-INSTANTIATE_MARRAY (double);
+INSTANTIATE_MARRAY (double, OCTAVE_API);
 
 INSTANTIATE_MARRAY_FRIENDS (double, OCTAVE_API)
 
--- a/liboctave/array/MArray-f.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray-f.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -32,7 +32,7 @@
 #include "MArray.h"
 #include "MArray.cc"
 
-INSTANTIATE_MARRAY (float);
+INSTANTIATE_MARRAY (float, OCTAVE_API);
 
 INSTANTIATE_MARRAY_FRIENDS (float, OCTAVE_API)
 
--- a/liboctave/array/MArray-fC.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray-fC.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -34,7 +34,7 @@
 #include "MArray.h"
 #include "MArray.cc"
 
-INSTANTIATE_MARRAY (FloatComplex);
+INSTANTIATE_MARRAY (FloatComplex, OCTAVE_API);
 
 INSTANTIATE_MARRAY_FRIENDS (FloatComplex, OCTAVE_API)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/array/MArray-fwd.h	Wed Dec 15 20:26:12 2021 -0500
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2021 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_MArray_fwd_h)
+#define octave_MArray_fwd_h 1
+
+#include "octave-config.h"
+
+template <typename T> class OCTARRAY_API MArray;
+
+#endif
--- a/liboctave/array/MArray-i.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray-i.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -34,9 +34,9 @@
 #include "MArray.h"
 #include "MArray.cc"
 
-INSTANTIATE_MARRAY (int);
+INSTANTIATE_MARRAY (int, OCTAVE_API);
 #if defined (OCTAVE_ENABLE_64)
-INSTANTIATE_MARRAY (int64_t);
+INSTANTIATE_MARRAY (int64_t, OCTAVE_API);
 #endif
 
 INSTANTIATE_MARRAY_FRIENDS (int, OCTAVE_API)
@@ -44,20 +44,20 @@
 INSTANTIATE_MARRAY_FRIENDS (int64_t, OCTAVE_API)
 #endif
 
-INSTANTIATE_MARRAY (octave_int8);
-INSTANTIATE_MARRAY (octave_int16);
-INSTANTIATE_MARRAY (octave_int32);
-INSTANTIATE_MARRAY (octave_int64);
+INSTANTIATE_MARRAY (octave_int8, OCTAVE_API);
+INSTANTIATE_MARRAY (octave_int16, OCTAVE_API);
+INSTANTIATE_MARRAY (octave_int32, OCTAVE_API);
+INSTANTIATE_MARRAY (octave_int64, OCTAVE_API);
 
 INSTANTIATE_MARRAY_FRIENDS (octave_int8, OCTAVE_API)
 INSTANTIATE_MARRAY_FRIENDS (octave_int16, OCTAVE_API)
 INSTANTIATE_MARRAY_FRIENDS (octave_int32, OCTAVE_API)
 INSTANTIATE_MARRAY_FRIENDS (octave_int64, OCTAVE_API)
 
-INSTANTIATE_MARRAY (octave_uint8);
-INSTANTIATE_MARRAY (octave_uint16);
-INSTANTIATE_MARRAY (octave_uint32);
-INSTANTIATE_MARRAY (octave_uint64);
+INSTANTIATE_MARRAY (octave_uint8, OCTAVE_API);
+INSTANTIATE_MARRAY (octave_uint16, OCTAVE_API);
+INSTANTIATE_MARRAY (octave_uint32, OCTAVE_API);
+INSTANTIATE_MARRAY (octave_uint64, OCTAVE_API);
 
 INSTANTIATE_MARRAY_FRIENDS (octave_uint8, OCTAVE_API)
 INSTANTIATE_MARRAY_FRIENDS (octave_uint16, OCTAVE_API)
--- a/liboctave/array/MArray-s.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray-s.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -32,7 +32,7 @@
 #include "MArray.h"
 #include "MArray.cc"
 
-INSTANTIATE_MARRAY (short);
+INSTANTIATE_MARRAY (short, OCTAVE_API);
 
 INSTANTIATE_MARRAY_FRIENDS (short, OCTAVE_API)
 
--- a/liboctave/array/MArray.cc	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray.cc	Wed Dec 15 20:26:12 2021 -0500
@@ -372,8 +372,24 @@
   return do_mx_unary_op<T, T> (a, mx_inline_uminus);
 }
 
+template <typename T>
+void MArray<T>::instantiation_guard ()
+{
+  // This guards against accidental implicit instantiations.
+  // Array<T, Alloc> instances should always be explicit and use INSTANTIATE_ARRAY.
+  T::__xXxXx__ ();
+}
+
 #if defined (__clang__)
-#  define INSTANTIATE_MARRAY(T) template class OCTAVE_API MArray<T>
+#  define INSTANTIATE_MARRAY(T, API)            \
+  template <> API void                          \
+  MArray<T>::instantiation_guard () { }         \
+                                                \
+  template class API MArray<T>
 #else
-#  define INSTANTIATE_MARRAY(T) template class MArray<T>
+#  define INSTANTIATE_MARRAY(T, API)            \
+  template <> API void                          \
+  MArray<T>::instantiation_guard () { }         \
+                                                \
+  template class MArray<T>
 #endif
--- a/liboctave/array/MArray.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/MArray.h	Wed Dec 15 20:26:12 2021 -0500
@@ -29,38 +29,36 @@
 #include "octave-config.h"
 
 #include "Array.h"
+#include "MArray-fwd.h"
 #include "mx-inlines.cc"
 
-// forward declare template with visibility attribute
-template <typename T> class OCTAVE_API MArray;
-
-template <typename T> OCTAVE_API MArray<T>& operator += (MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T>& operator -= (MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T>& operator *= (MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T>& operator /= (MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T>& operator += (MArray<T>&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T>& operator -= (MArray<T>&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T>& product_eq (MArray<T>&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T>& quotient_eq (MArray<T>&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator + (const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator - (const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator + (const MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T> operator - (const MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T> operator * (const MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T> operator / (const MArray<T>&, const T&);
-template <typename T> OCTAVE_API MArray<T> operator + (const T&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator - (const T&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator * (const T&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator / (const T&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator + (const MArray<T>&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> operator - (const MArray<T>&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> quotient (const MArray<T>&, const MArray<T>&);
-template <typename T> OCTAVE_API MArray<T> product (const MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T>& operator += (MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T>& operator -= (MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T>& operator *= (MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T>& operator /= (MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T>& operator += (MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T>& operator -= (MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T>& product_eq (MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T>& quotient_eq (MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator + (const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator - (const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator + (const MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T> operator - (const MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T> operator * (const MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T> operator / (const MArray<T>&, const T&);
+template <typename T> OCTARRAY_API MArray<T> operator + (const T&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator - (const T&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator * (const T&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator / (const T&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator + (const MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> operator - (const MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> quotient (const MArray<T>&, const MArray<T>&);
+template <typename T> OCTARRAY_API MArray<T> product (const MArray<T>&, const MArray<T>&);
 
 //! Template for N-dimensional array classes with like-type math operators.
 template <typename T>
 class
-OCTAVE_API
+OCTARRAY_API
 MArray : public Array<T>
 {
 public:
@@ -106,22 +104,25 @@
 
   //! Performs indexed accumulative addition.
   //@{
-  OCTAVE_API void idx_add (const octave::idx_vector& idx, T val);
-  OCTAVE_API void
+  OCTARRAY_API void idx_add (const octave::idx_vector& idx, T val);
+  OCTARRAY_API void
   idx_add (const octave::idx_vector& idx, const MArray<T>& vals);
   //@}
 
-  OCTAVE_API void
+  OCTARRAY_API void
   idx_min (const octave::idx_vector& idx, const MArray<T>& vals);
 
-  OCTAVE_API void
+  OCTARRAY_API void
   idx_max (const octave::idx_vector& idx, const MArray<T>& vals);
 
-  OCTAVE_API void
+  OCTARRAY_API void
   idx_add_nd (const octave::idx_vector& idx, const MArray<T>& vals,
               int dim = -1);
 
-  OCTAVE_API void changesign (void);
+  OCTARRAY_API void changesign (void);
+
+private:
+  OCTARRAY_API static void instantiation_guard ();
 };
 
 // Define all the MArray forwarding functions for return type R and
--- a/liboctave/array/Range.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/Range.h	Wed Dec 15 20:26:12 2021 -0500
@@ -35,13 +35,12 @@
 #include "dim-vector.h"
 #include "lo-error.h"
 #include "oct-sort.h"
+#include "range-fwd.h"
 
 namespace octave
 {
   template <typename T>
-  class
-  OCTAVE_API
-  range
+  class range
   {
   public:
 
--- a/liboctave/array/dNDArray.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/dNDArray.h	Wed Dec 15 20:26:12 2021 -0500
@@ -28,13 +28,12 @@
 
 #include "octave-config.h"
 
+#include "intNDArray-fwd.h"
 #include "MArray.h"
 #include "bsxfun-decl.h"
 #include "mx-defs.h"
 #include "mx-op-decl.h"
 
-template <typename T> class intNDArray;
-
 class
 OCTAVE_API
 NDArray : public MArray<double>
--- a/liboctave/array/fNDArray.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/fNDArray.h	Wed Dec 15 20:26:12 2021 -0500
@@ -28,13 +28,12 @@
 
 #include "octave-config.h"
 
+#include "intNDArray-fwd.h"
 #include "MArray.h"
 #include "bsxfun-decl.h"
 #include "mx-defs.h"
 #include "mx-op-decl.h"
 
-template <typename T> class intNDArray;
-
 class
 OCTAVE_API
 FloatNDArray : public MArray<float>
--- a/liboctave/array/idx-vector.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/idx-vector.h	Wed Dec 15 20:26:12 2021 -0500
@@ -40,11 +40,10 @@
 #include "oct-inttypes.h"
 #include "oct-refcount.h"
 #include "Sparse-fwd.h"
+#include "range-fwd.h"
 
 namespace octave
 {
-  template <typename T> class range;
-
   // Design rationale:
   //
   // idx_vector is a reference-counting, polymorphic pointer, that can
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/array/intNDArray-fwd.h	Wed Dec 15 20:26:12 2021 -0500
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2021 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_intNDArray_fwd_h)
+#define octave_intNDArray_fwd_h 1
+
+#include "octave-config.h"
+
+template <typename T> class OCTAVE_API intNDArray;
+
+#endif
--- a/liboctave/array/intNDArray.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/intNDArray.h	Wed Dec 15 20:26:12 2021 -0500
@@ -28,6 +28,7 @@
 
 #include "octave-config.h"
 
+#include "intNDArray-fwd.h"
 #include "MArray.h"
 #include "boolNDArray.h"
 
@@ -35,7 +36,6 @@
 
 template <typename T>
 class
-OCTAVE_API
 intNDArray : public MArray<T>
 {
 public:
--- a/liboctave/array/module.mk	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/array/module.mk	Wed Dec 15 20:26:12 2021 -0500
@@ -2,31 +2,41 @@
   %reldir%/Array-fwd.h \
   %reldir%/Array-util.h \
   %reldir%/Array.h \
-  %reldir%/boolMatrix.h \
-  %reldir%/boolNDArray.h \
-  %reldir%/boolSparse.h \
   %reldir%/CColVector.h \
   %reldir%/CDiagMatrix.h \
-  %reldir%/chMatrix.h \
-  %reldir%/chNDArray.h \
   %reldir%/CMatrix.h \
   %reldir%/CNDArray.h \
   %reldir%/CRowVector.h \
   %reldir%/CSparse.h \
+  %reldir%/DiagArray2.h \
+  %reldir%/MArray-fwd.h \
+  %reldir%/MArray.h \
+  %reldir%/MDiagArray2.h \
+  %reldir%/MSparse.h \
+  %reldir%/Matrix.h \
+  %reldir%/MatrixType.h \
+  %reldir%/PermMatrix.h \
+  %reldir%/Range.h \
+  %reldir%/Sparse-fwd.h \
+  %reldir%/Sparse.h \
+  %reldir%/boolMatrix.h \
+  %reldir%/boolNDArray.h \
+  %reldir%/boolSparse.h \
+  %reldir%/chMatrix.h \
+  %reldir%/chNDArray.h \
   %reldir%/dColVector.h \
   %reldir%/dDiagMatrix.h \
-  %reldir%/DiagArray2.h \
-  %reldir%/dim-vector.h \
   %reldir%/dMatrix.h \
   %reldir%/dNDArray.h \
   %reldir%/dRowVector.h \
   %reldir%/dSparse.h \
+  %reldir%/dim-vector.h \
   %reldir%/fCColVector.h \
   %reldir%/fCDiagMatrix.h \
   %reldir%/fCMatrix.h \
   %reldir%/fCNDArray.h \
+  %reldir%/fCRowVector.h \
   %reldir%/fColVector.h \
-  %reldir%/fCRowVector.h \
   %reldir%/fDiagMatrix.h \
   %reldir%/fMatrix.h \
   %reldir%/fNDArray.h \
@@ -36,24 +46,17 @@
   %reldir%/int32NDArray.h \
   %reldir%/int64NDArray.h \
   %reldir%/int8NDArray.h \
+  %reldir%/intNDArray-fwd.h \
   %reldir%/intNDArray.h \
-  %reldir%/MArray.h \
-  %reldir%/Matrix.h \
-  %reldir%/MatrixType.h \
-  %reldir%/MDiagArray2.h \
-  %reldir%/MSparse.h \
-  %reldir%/PermMatrix.h \
-  %reldir%/Range.h \
-  %reldir%/Sparse-fwd.h \
-  %reldir%/Sparse.h \
+  %reldir%/range-fwd.h \
   %reldir%/uint16NDArray.h \
   %reldir%/uint32NDArray.h \
   %reldir%/uint64NDArray.h \
   %reldir%/uint8NDArray.h
 
 ARRAY_SRC = \
+  %reldir%/Array-C.cc \
   %reldir%/Array-b.cc \
-  %reldir%/Array-C.cc \
   %reldir%/Array-ch.cc \
   %reldir%/Array-d.cc \
   %reldir%/Array-f.cc \
@@ -64,30 +67,44 @@
   %reldir%/Array-str.cc \
   %reldir%/Array-util.cc \
   %reldir%/Array-voidp.cc \
-  %reldir%/boolMatrix.cc \
-  %reldir%/boolNDArray.cc \
-  %reldir%/boolSparse.cc \
   %reldir%/CColVector.cc \
   %reldir%/CDiagMatrix.cc \
-  %reldir%/chMatrix.cc \
-  %reldir%/chNDArray.cc \
   %reldir%/CMatrix.cc \
   %reldir%/CNDArray.cc \
   %reldir%/CRowVector.cc \
   %reldir%/CSparse.cc \
+  %reldir%/MArray-C.cc \
+  %reldir%/MArray-d.cc \
+  %reldir%/MArray-f.cc \
+  %reldir%/MArray-fC.cc \
+  %reldir%/MArray-i.cc \
+  %reldir%/MArray-s.cc \
+  %reldir%/MSparse-C.cc \
+  %reldir%/MSparse-d.cc \
+  %reldir%/MatrixType.cc \
+  %reldir%/PermMatrix.cc \
+  %reldir%/Range.cc \
+  %reldir%/Sparse-C.cc \
+  %reldir%/Sparse-b.cc \
+  %reldir%/Sparse-d.cc \
+  %reldir%/boolMatrix.cc \
+  %reldir%/boolNDArray.cc \
+  %reldir%/boolSparse.cc \
+  %reldir%/chMatrix.cc \
+  %reldir%/chNDArray.cc \
   %reldir%/dColVector.cc \
   %reldir%/dDiagMatrix.cc \
-  %reldir%/dim-vector.cc \
   %reldir%/dMatrix.cc \
   %reldir%/dNDArray.cc \
   %reldir%/dRowVector.cc \
   %reldir%/dSparse.cc \
+  %reldir%/dim-vector.cc \
   %reldir%/fCColVector.cc \
   %reldir%/fCDiagMatrix.cc \
   %reldir%/fCMatrix.cc \
   %reldir%/fCNDArray.cc \
+  %reldir%/fCRowVector.cc \
   %reldir%/fColVector.cc \
-  %reldir%/fCRowVector.cc \
   %reldir%/fDiagMatrix.cc \
   %reldir%/fMatrix.cc \
   %reldir%/fNDArray.cc \
@@ -97,20 +114,6 @@
   %reldir%/int32NDArray.cc \
   %reldir%/int64NDArray.cc \
   %reldir%/int8NDArray.cc \
-  %reldir%/MArray-C.cc \
-  %reldir%/MArray-d.cc \
-  %reldir%/MArray-f.cc \
-  %reldir%/MArray-fC.cc \
-  %reldir%/MArray-i.cc \
-  %reldir%/MArray-s.cc \
-  %reldir%/MatrixType.cc \
-  %reldir%/MSparse-C.cc \
-  %reldir%/MSparse-d.cc \
-  %reldir%/PermMatrix.cc \
-  %reldir%/Range.cc \
-  %reldir%/Sparse-b.cc \
-  %reldir%/Sparse-C.cc \
-  %reldir%/Sparse-d.cc \
   %reldir%/uint16NDArray.cc \
   %reldir%/uint32NDArray.cc \
   %reldir%/uint64NDArray.cc \
@@ -119,11 +122,11 @@
 LIBOCTAVE_TEMPLATE_SRC += \
   %reldir%/Array.cc \
   %reldir%/DiagArray2.cc \
-  %reldir%/intNDArray.cc \
   %reldir%/MArray.cc \
   %reldir%/MDiagArray2.cc \
   %reldir%/MSparse.cc \
-  %reldir%/Sparse.cc
+  %reldir%/Sparse.cc \
+  %reldir%/intNDArray.cc
 
 noinst_LTLIBRARIES += %reldir%/libarray.la
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/array/range-fwd.h	Wed Dec 15 20:26:12 2021 -0500
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2021 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_range_fwd_h)
+#define octave_range_fwd_h 1
+
+#include "octave-config.h"
+
+namespace octave
+{
+  template <typename T> class OCTAVE_API range;
+}
+
+#endif
--- a/liboctave/numeric/sparse-qr.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/numeric/sparse-qr.h	Wed Dec 15 20:26:12 2021 -0500
@@ -31,13 +31,13 @@
 #include <memory>
 
 #include "oct-cmplx.h"
+#include "MArray-fwd.h"
 
 class Matrix;
 class ComplexMatrix;
 class SparseMatrix;
 class SparseComplexMatrix;
 class ColumnVector;
-template <typename T> class MArray;
 
 namespace octave
 {
--- a/liboctave/util/oct-inttypes.h	Thu Dec 09 21:21:37 2021 -0800
+++ b/liboctave/util/oct-inttypes.h	Wed Dec 15 20:26:12 2021 -0500
@@ -779,9 +779,7 @@
 { };
 
 template <typename T>
-class
-OCTAVE_API
-octave_int : public octave_int_base<T>
+class octave_int : public octave_int_base<T>
 {
 public:
 
--- a/m4/acinclude.m4	Thu Dec 09 21:21:37 2021 -0800
+++ b/m4/acinclude.m4	Wed Dec 15 20:26:12 2021 -0500
@@ -255,6 +255,51 @@
   fi
 ])
 dnl
+dnl Check whether std::pmr::polymorphic_allocator is available.
+dnl
+AC_DEFUN([OCTAVE_CHECK_STD_PMR_POLYMORPHIC_ALLOCATOR], [
+  AC_CACHE_CHECK([whether std::pmr::polymorphic_allocator is avalable],
+    [octave_cv_std_pmr_polymorphic_allocator],
+    [AC_LANG_PUSH(C++)
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+      #include <cstdlib>
+      #include <memory_resource>
+      #include <vector>
+      class mx_memory_resource : public std::pmr::memory_resource
+      {
+      private:
+        void * do_allocate (std::size_t bytes, size_t /*alignment*/)
+        {
+          void *ptr = std::malloc (bytes);
+          if (! ptr)
+            throw std::bad_alloc ();
+            return ptr;
+        }
+        void do_deallocate (void* ptr, std::size_t /*bytes*/,
+                            std::size_t /*alignment*/)
+        {
+          std::free (ptr);
+        }
+        bool do_is_equal (const std::pmr::memory_resource& other) const noexcept
+        {
+          return this == dynamic_cast<const mx_memory_resource *> (&other);
+          return true;
+        }
+      };
+      mx_memory_resource the_mx_memory_resource;
+    ]], [[
+      std::pmr::vector<int> my_int_vec { &the_mx_memory_resource };
+    ]])],
+    octave_cv_std_pmr_polymorphic_allocator=yes,
+    octave_cv_std_pmr_polymorphic_allocator=no)
+    AC_LANG_POP(C++)
+  ])
+  if test $octave_cv_std_pmr_polymorphic_allocator = yes; then
+    AC_DEFINE(HAVE_STD_PMR_POLYMORPHIC_ALLOCATOR, 1,
+      [Define to 1 if std::pmr::polymorphic_allocator is available.])
+  fi
+])
+dnl
 dnl Check whether CXSparse is version 2.2 or later
 dnl FIXME: This test uses a version number.  It potentially could
 dnl        be re-written to actually call a function, but is it worth it?