changeset 28646:e26201931ea3

new template class for octave_range objects * ov-range.h, ov-range.cc (class octave_range): Convert to template. For now, use specializations to preserve existing behavior for double-precision ranges. Change all uses. * ov.h, ov.cc: Update range constructors to create range objects for integer and float ranges. Provide value extractor functions for range<T> types that forward to virtual functions in the octave_base_value class. * ov-base.cc: Provide virtual value extractor functions for range<T> types. * ov-range-traits.h: New file. * libinterp/octave-value/module.mk: Update. * Range.h, Range.cc (Range::Range): Deprecate public constructors. * pt-eval.h, pt-eval.cc (tree_evaluator::execute_range_loop): New template function. (tree_evaluator::visit_simple_for_command): Use it to handle looping when the loop variable expression is a range.
author John W. Eaton <jwe@octave.org>
date Wed, 12 Aug 2020 12:14:17 -0400
parents 175eedccc085
children f0414ee0fefe
files libinterp/octave-value/module.mk libinterp/octave-value/ov-base.cc libinterp/octave-value/ov-base.h libinterp/octave-value/ov-range-traits.h libinterp/octave-value/ov-range.cc libinterp/octave-value/ov-range.h libinterp/octave-value/ov.cc libinterp/octave-value/ov.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h liboctave/array/Range.h
diffstat 11 files changed, 1213 insertions(+), 442 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/module.mk	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/octave-value/module.mk	Wed Aug 12 12:14:17 2020 -0400
@@ -57,6 +57,7 @@
   %reldir%/ov-null-mat.h \
   %reldir%/ov-oncleanup.h \
   %reldir%/ov-perm.h \
+  %reldir%/ov-range-traits.h \
   %reldir%/ov-range.h \
   %reldir%/ov-re-diag.h \
   %reldir%/ov-re-mat.h \
--- a/libinterp/octave-value/ov-base.cc	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/octave-value/ov-base.cc	Wed Aug 12 12:14:17 2020 -0400
@@ -811,12 +811,66 @@
   err_wrong_type_arg ("octave_base_value::cellstr_value()", type_name ());
 }
 
+octave::range<float>
+octave_base_value::float_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::float_range_value()", type_name ());
+}
+
 octave::range<double>
 octave_base_value::range_value (void) const
 {
   err_wrong_type_arg ("octave_base_value::range_value()", type_name ());
 }
 
+octave::range<octave_int8>
+octave_base_value::int8_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::int8_range_value()", type_name ());
+}
+
+octave::range<octave_int16>
+octave_base_value::int16_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::int16_range_value()", type_name ());
+}
+
+octave::range<octave_int32>
+octave_base_value::int32_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::int32_range_value()", type_name ());
+}
+
+octave::range<octave_int64>
+octave_base_value::int64_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::int64_range_value()", type_name ());
+}
+
+octave::range<octave_uint8>
+octave_base_value::uint8_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::uint8_range_value()", type_name ());
+}
+
+octave::range<octave_uint16>
+octave_base_value::uint16_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::uint16_range_value()", type_name ());
+}
+
+octave::range<octave_uint32>
+octave_base_value::uint32_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::uint32_range_value()", type_name ());
+}
+
+octave::range<octave_uint64>
+octave_base_value::uint64_range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::uint64_range_value()", type_name ());
+}
+
 octave_map
 octave_base_value::map_value (void) const
 {
--- a/libinterp/octave-value/ov-base.h	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/octave-value/ov-base.h	Wed Aug 12 12:14:17 2020 -0400
@@ -622,8 +622,26 @@
 
   virtual Array<std::string> cellstr_value (void) const;
 
+  virtual octave::range<float> float_range_value (void) const;
+
   virtual octave::range<double> range_value (void) const;
 
+  virtual octave::range<octave_int8> int8_range_value (void) const;
+
+  virtual octave::range<octave_int16> int16_range_value (void) const;
+
+  virtual octave::range<octave_int32> int32_range_value (void) const;
+
+  virtual octave::range<octave_int64> int64_range_value (void) const;
+
+  virtual octave::range<octave_uint8> uint8_range_value (void) const;
+
+  virtual octave::range<octave_uint16> uint16_range_value (void) const;
+
+  virtual octave::range<octave_uint32> uint32_range_value (void) const;
+
+  virtual octave::range<octave_uint64> uint64_range_value (void) const;
+
   virtual octave_map map_value (void) const;
 
   virtual octave_scalar_map scalar_map_value (void) const;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-range-traits.h	Wed Aug 12 12:14:17 2020 -0400
@@ -0,0 +1,154 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2020 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_ov_range_traits_h)
+#define octave_ov_range_traits_h 1
+
+#include "octave-config.h"
+
+#include "ov-bool-mat.h"
+#include "ov-bool.h"
+#include "ov-float.h"
+#include "ov-flt-re-mat.h"
+#include "ov-int16.h"
+#include "ov-int32.h"
+#include "ov-int64.h"
+#include "ov-int8.h"
+#include "ov-re-mat.h"
+#include "ov-scalar.h"
+#include "ov-uint16.h"
+#include "ov-uint32.h"
+#include "ov-uint64.h"
+#include "ov-uint8.h"
+
+template <typename T>
+class
+octave_value_range_traits
+{
+public:
+  typedef T scalar_type;
+  typedef T matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<bool>
+{
+public:
+  typedef octave_bool scalar_type;
+  typedef octave_bool_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<float>
+{
+public:
+  typedef octave_float_scalar scalar_type;
+  typedef octave_float_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<double>
+{
+public:
+  typedef octave_scalar scalar_type;
+  typedef octave_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_int8>
+{
+public:
+  typedef octave_int8_scalar scalar_type;
+  typedef octave_int8_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_int16>
+{
+public:
+  typedef octave_int16_scalar scalar_type;
+  typedef octave_int16_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_int32>
+{
+public:
+  typedef octave_int32_scalar scalar_type;
+  typedef octave_int32_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_int64>
+{
+public:
+  typedef octave_int64_scalar scalar_type;
+  typedef octave_int64_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_uint8>
+{
+public:
+  typedef octave_uint8_scalar scalar_type;
+  typedef octave_uint8_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_uint16>
+{
+public:
+  typedef octave_uint16_scalar scalar_type;
+  typedef octave_uint16_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_uint32>
+{
+public:
+  typedef octave_uint32_scalar scalar_type;
+  typedef octave_uint32_matrix matrix_type;
+};
+
+template <>
+class
+octave_value_range_traits<octave_uint64>
+{
+public:
+  typedef octave_uint64_scalar scalar_type;
+  typedef octave_uint64_matrix matrix_type;
+};
+
+#endif
--- a/libinterp/octave-value/ov-range.cc	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/octave-value/ov-range.cc	Wed Aug 12 12:14:17 2020 -0400
@@ -23,9 +23,9 @@
 //
 ////////////////////////////////////////////////////////////////////////
 
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
+// This file should not include config.h.  It is only included in other
+// C++ source files that should have included config.h before including
+// this file.
 
 #include <istream>
 #include <ostream>
@@ -49,9 +49,11 @@
 #include "variables.h"
 #include "errwarn.h"
 #include "mxarray.h"
+#include "mx-type-traits.h"
 #include "ops.h"
 #include "ovl.h"
 #include "oct-hdf5.h"
+#include "ov-range-traits.h"
 #include "ov-range.h"
 #include "ov-re-mat.h"
 #include "ov-scalar.h"
@@ -62,41 +64,148 @@
 #include "ls-hdf5.h"
 #include "ls-utils.h"
 
+#if defined (HAVE_HDF5)
 
-DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_range, "range", "double");
+template <>
+octave_hdf5_id ov_range<float>::hdf5_save_type = H5T_NATIVE_FLOAT;
+
+template <>
+octave_hdf5_id ov_range<double>::hdf5_save_type = H5T_NATIVE_DOUBLE;
+
+template <>
+octave_hdf5_id ov_range<octave_int8>::hdf5_save_type = H5T_NATIVE_INT8;
+
+template <>
+octave_hdf5_id ov_range<octave_int16>::hdf5_save_type = H5T_NATIVE_INT16;
+
+template <>
+octave_hdf5_id ov_range<octave_int32>::hdf5_save_type = H5T_NATIVE_INT32;
+
+template <>
+octave_hdf5_id ov_range<octave_int64>::hdf5_save_type = H5T_NATIVE_INT64;
+
+template <>
+octave_hdf5_id ov_range<octave_uint8>::hdf5_save_type = H5T_NATIVE_UINT8;
+
+template <>
+octave_hdf5_id ov_range<octave_uint16>::hdf5_save_type = H5T_NATIVE_UINT16;
+
+template <>
+octave_hdf5_id ov_range<octave_uint32>::hdf5_save_type = H5T_NATIVE_UINT32;
+
+template <>
+octave_hdf5_id ov_range<octave_uint64>::hdf5_save_type = H5T_NATIVE_UINT64;
+
+#else
+
+template <>
+octave_hdf5_id ov_range<float>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<double>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<octave_int8>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<octave_int16>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<octave_int32>::hdf5_save_type = 0;
 
+template <>
+octave_hdf5_id ov_range<octave_int64>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<octave_uint8>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<octave_uint16>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<octave_uint32>::hdf5_save_type = 0;
+
+template <>
+octave_hdf5_id ov_range<octave_uint64>::hdf5_save_type = 0;
+
+#endif
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<float>,
+                                              "float_range", "single");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<double>,
+                                              "range", "double");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_int8>,
+                                              "int8_range", "int8");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_int16>,
+                                              "int16_range", "int16");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_int32>,
+                                              "int32_range", "int32");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_int64>,
+                                              "int64_range", "int64");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_uint8>,
+                                              "uint8_range", "uint8");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_uint16>,
+                                              "uint16_range", "uint16");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_uint32>,
+                                              "uint32_range", "uint32");
+
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_uint64>,
+                                              "uint64_range", "uint64");
+
+template <typename T>
 static octave_base_value *
 default_numeric_conversion_function (const octave_base_value& a)
 {
-  const octave_range& v = dynamic_cast<const octave_range&> (a);
+  typedef typename octave_value_range_traits<T>::matrix_type ov_mx_type;
 
-  return new octave_matrix (v.matrix_value ());
+  const ov_range<T>& v = dynamic_cast<const ov_range<T>&> (a);
+
+  return new ov_mx_type (v.raw_array_value ());
 }
 
+template <typename T>
 octave_base_value::type_conv_info
-octave_range::numeric_conversion_function (void) const
+ov_range<T>::numeric_conversion_function (void) const
 {
-  return octave_base_value::type_conv_info (default_numeric_conversion_function,
-                                            octave_matrix::static_type_id ());
+  typedef typename octave_value_range_traits<T>::matrix_type ov_mx_type;
+
+  return octave_base_value::type_conv_info
+    (default_numeric_conversion_function<T>, ov_mx_type::static_type_id ());
 }
 
+template <typename T>
 octave_base_value *
-octave_range::try_narrowing_conversion (void)
+ov_range<T>::try_narrowing_conversion (void)
 {
   octave_base_value *retval = nullptr;
 
   switch (numel ())
     {
     case 1:
-      retval = new octave_scalar (m_range.base ());
+      retval = new typename octave_value_range_traits<T>::scalar_type (m_range.elem (0));
       break;
 
     case 0:
-      retval = new octave_matrix (Matrix (1, 0));
+      {
+        typedef typename octave_value_range_traits<T>::matrix_type ov_mx_type;
+        typename ov_mx_type::object_type m (dim_vector (1, 0));
+        retval = new ov_mx_type (m);
+      }
       break;
 
     case -2:
-      retval = new octave_matrix (matrix_value ());
+      // FIXME: is this case possible now?  It would have to be due to
+      // conversion from Range to range<double>, but even in that case,
+      // is the invalid numel value preserved?
+      retval = new typename octave_value_range_traits<T>::matrix_type (raw_array_value ());
       break;
 
     default:
@@ -106,9 +215,10 @@
   return retval;
 }
 
+template <typename T>
 octave_value
-octave_range::subsref (const std::string& type,
-                       const std::list<octave_value_list>& idx)
+ov_range<T>::subsref (const std::string& type,
+                      const std::list<octave_value_list>& idx)
 {
   octave_value retval;
 
@@ -133,8 +243,10 @@
   return retval.next_subsref (type, idx);
 }
 
+template <typename T>
 octave_value
-octave_range::do_index_op (const octave_value_list& idx, bool resize_ok)
+ov_range<T>::do_index_op (const octave_value_list& idx,
+                                   bool resize_ok)
 {
   if (idx.length () == 1 && ! resize_ok)
     {
@@ -163,47 +275,23 @@
     }
   else
     {
-      octave_value tmp (new octave_matrix (matrix_value ()));
+      octave_value tmp (new typename octave_value_range_traits<T>::matrix_type (raw_array_value ()));
 
       return tmp.index_op (idx, resize_ok);
     }
 }
 
+template <typename T>
 idx_vector
-octave_range::index_vector (bool require_integers) const
+ov_range<T>::index_vector (bool require_integers) const
 {
-  if (m_idx_cache)
-    return *m_idx_cache;
-  else
-    {
-      if (require_integers || m_range.all_elements_are_ints ())
-        return set_idx_cache (idx_vector (m_range));
-      else
-        {
-          warning_with_id ("Octave:noninteger-range-as-index",
-                           "non-integer range used as index");
-
-          return octave_value (matrix_value ()).round ().index_vector ();
-        }
-    }
+  octave_value tmp (raw_array_value ());
+  return tmp.index_vector (require_integers);
 }
 
+template <typename T>
 double
-octave_range::double_value (bool) const
-{
-  octave_idx_type nel = numel ();
-
-  if (nel == 0)
-    err_invalid_conversion ("range", "real scalar");
-
-  warn_implicit_conversion ("Octave:array-to-scalar",
-                            "range", "real scalar");
-
-  return m_range.base ();
-}
-
-float
-octave_range::float_value (bool) const
+ov_range<T>::double_value (bool) const
 {
   octave_idx_type nel = numel ();
 
@@ -216,10 +304,26 @@
   return m_range.base ();
 }
 
+template <typename T>
+float
+ov_range<T>::float_value (bool) const
+{
+  octave_idx_type nel = numel ();
+
+  if (nel == 0)
+    err_invalid_conversion ("range", "real scalar");
+
+  warn_implicit_conversion ("Octave:array-to-scalar",
+                            "range", "real scalar");
+
+  return m_range.base ();
+}
+
+template <typename T>
 charNDArray
-octave_range::char_array_value (bool) const
+ov_range<T>::char_array_value (bool) const
 {
-  const Matrix matrix = matrix_value ();
+  const Array<T> matrix = raw_array_value ();
   charNDArray retval (dims ());
 
   octave_idx_type nel = numel ();
@@ -230,83 +334,9 @@
   return retval;
 }
 
-octave_value
-octave_range::all (int dim) const
-{
-  // FIXME: this is a potential waste of memory.
-
-  Matrix m = matrix_value ();
-
-  return m.all (dim);
-}
-
-octave_value
-octave_range::any (int dim) const
-{
-  // FIXME: this is a potential waste of memory.
-
-  Matrix m = matrix_value ();
-
-  return m.any (dim);
-}
-
-octave_value
-octave_range::diag (octave_idx_type k) const
-{
-  return
-    (k == 0
-       ? octave_value (DiagMatrix (DiagArray2<double> (matrix_value ())))
-       : octave_value (m_range.diag (k)));
-}
-
-octave_value
-octave_range::diag (octave_idx_type m, octave_idx_type n) const
-{
-  Matrix mat = matrix_value ();
-
-  return mat.diag (m, n);
-}
-
-// Return true if this range has all true elements (non-zero, not NaN/NA).
-// A range cannot have NaN/NA.
-bool
-octave_range::is_true (void) const
-{
-  bool retval = false;
-
-  if (! m_range.isempty ())
-    {
-      if (dims ().numel () > 1)
-        warn_array_as_logical (dims ());
-
-      octave::range<double> r = range_value ();
-      double base = r.base ();
-      double limit = r.limit ();
-
-      // Can't be zero if we start and finish on the same size of 0
-      if (((base > 0 && limit > 0) || (base < 0 && limit < 0)) && numel () > 0)
-        retval = true;
-      else
-        {
-          /*
-          // This tells us whether one element is 0, if arithmetic is exact.
-          double steps_to_zero = base / r.increment ();
-
-          retval = (steps_to_zero != floor (steps_to_zero));
-          */
-
-          // FIXME: this is a waste of memory.
-          Matrix m ((matrix_value ().all ()).all ());
-
-          retval = ! m.isempty () && m(0, 0) != 0.0;
-        }
-    }
-
-  return retval;
-}
-
+template <typename T>
 Complex
-octave_range::complex_value (bool) const
+ov_range<T>::complex_value (bool) const
 {
   octave_idx_type nel = numel ();
 
@@ -319,8 +349,9 @@
   return Complex (m_range.base (), 0);
 }
 
+template <typename T>
 FloatComplex
-octave_range::float_complex_value (bool) const
+ov_range<T>::float_complex_value (bool) const
 {
   float tmp = lo_ieee_float_nan_value ();
 
@@ -339,23 +370,23 @@
   return retval;
 }
 
+template <typename T>
 boolNDArray
-octave_range::bool_array_value (bool warn) const
+ov_range<T>::bool_array_value (bool warn) const
 {
-  Matrix m = matrix_value ();
+  Array<T> matrix = raw_array_value ();
 
-  if (m.any_element_is_nan ())
-    octave::err_nan_to_logical_conversion ();
-  if (warn && m.any_element_not_one_or_zero ())
+  if (warn && ! matrix.test_all (xis_one_or_zero<T>))
     warn_logical_conversion ();
 
-  return boolNDArray (m);
+  return boolNDArray (matrix);
 }
 
+template <typename T>
 octave_value
-octave_range::resize (const dim_vector& dv, bool fill) const
+ov_range<T>::resize (const dim_vector& dv, bool fill) const
 {
-  NDArray retval = array_value ();
+  Array<T> retval = raw_array_value ();
   if (fill)
     retval.resize (dv, 0);
   else
@@ -363,89 +394,180 @@
   return retval;
 }
 
+template <typename T>
+octave::range<float>
+ov_range<T>::float_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::float_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<double>
+ov_range<T>::range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::range_value()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_int8>
+ov_range<T>::int8_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::int8_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_int16>
+ov_range<T>::int16_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::int16_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_int32>
+ov_range<T>::int32_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::int32_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_int64>
+ov_range<T>::int64_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::int64_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_uint8>
+ov_range<T>::uint8_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::uint8_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_uint16>
+ov_range<T>::uint16_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::uint16_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_uint32>
+ov_range<T>::uint32_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::uint32_range_value ()", type_name ());
+}
+
+template <typename T>
+octave::range<octave_uint64>
+ov_range<T>::uint64_range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::uint64_range_value ()", type_name ());
+}
+
+template <typename T>
 octave_value
-octave_range::convert_to_str_internal (bool pad, bool force, char type) const
+ov_range<T>::convert_to_str_internal (bool pad, bool force, char type) const
 {
-  octave_value tmp (matrix_value ());
+  octave_value tmp (raw_array_value ());
   return tmp.convert_to_str (pad, force, type);
 }
 
+// FIXME: could most of these fucntions preserve range type now?
+
+template <typename T>
 octave_value
-octave_range::as_double (void) const
+ov_range<T>::as_double (void) const
 {
-  return m_range;
+  return NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_single (void) const
+ov_range<T>::as_single (void) const
 {
-  return FloatMatrix (matrix_value ());
+  return FloatMatrix (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_int8 (void) const
+ov_range<T>::as_int8 (void) const
 {
-  return int8NDArray (matrix_value ());
+  return int8NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_int16 (void) const
+ov_range<T>::as_int16 (void) const
 {
-  return int16NDArray (matrix_value ());
+  return int16NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_int32 (void) const
+ov_range<T>::as_int32 (void) const
 {
-  return int32NDArray (matrix_value ());
+  return int32NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_int64 (void) const
+ov_range<T>::as_int64 (void) const
 {
-  return int64NDArray (matrix_value ());
+  return int64NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_uint8 (void) const
+ov_range<T>::as_uint8 (void) const
 {
-  return uint8NDArray (matrix_value ());
+  return uint8NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_uint16 (void) const
+ov_range<T>::as_uint16 (void) const
 {
-  return uint16NDArray (matrix_value ());
+  return uint16NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_uint32 (void) const
+ov_range<T>::as_uint32 (void) const
 {
-  return uint32NDArray (matrix_value ());
+  return uint32NDArray (raw_array_value ());
 }
 
+template <typename T>
 octave_value
-octave_range::as_uint64 (void) const
+ov_range<T>::as_uint64 (void) const
 {
-  return uint64NDArray (matrix_value ());
+  return uint64NDArray (raw_array_value ());
 }
 
+template <typename T>
 void
-octave_range::print (std::ostream& os, bool pr_as_read_syntax)
+ov_range<T>::print (std::ostream& os, bool pr_as_read_syntax)
 {
   print_raw (os, pr_as_read_syntax);
   newline (os);
 }
 
+template <typename T>
 void
-octave_range::print_raw (std::ostream& os, bool pr_as_read_syntax) const
+ov_range<T>::print_raw (std::ostream& os, bool pr_as_read_syntax) const
 {
-  octave_print_internal (os, m_range, pr_as_read_syntax,
+  // FIXME: this is a potential waste of memory.
+
+  typedef typename octave_value_range_traits<T>::matrix_type ov_mx_type;
+  typename ov_mx_type::object_type tmp (raw_array_value ());
+
+  octave_print_internal (os, tmp, pr_as_read_syntax,
                          current_print_indent_level ());
 }
 
+template <typename T>
 bool
-octave_range::print_name_tag (std::ostream& os, const std::string& name) const
+ov_range<T>::print_name_tag (std::ostream& os, const std::string& name) const
 {
   bool retval = false;
 
@@ -468,8 +590,9 @@
   return retval;
 }
 
+template <typename T>
 void
-octave_range::short_disp (std::ostream& os) const
+ov_range<T>::short_disp (std::ostream& os) const
 {
   octave_idx_type len = numel ();
 
@@ -481,7 +604,7 @@
 
       if (len > 1)
         {
-          if (m_range.increment () != 1)
+          if (m_range.increment () != T (1))
             os << m_range.increment () << ':';
 
           os << m_range.limit ();
@@ -506,116 +629,126 @@
   skip_until_newline (is, false);
 }
 
+template <typename T>
 float_display_format
-octave_range::get_edit_display_format (void) const
+ov_range<T>::get_edit_display_format (void) const
 {
-  return make_format (range_value ());
+  return make_format (m_range);
 }
 
+template <typename T>
 std::string
-octave_range::edit_display (const float_display_format& fmt,
-                            octave_idx_type, octave_idx_type j) const
+ov_range<T>::edit_display (const float_display_format& fmt,
+                           octave_idx_type, octave_idx_type j) const
 {
   std::ostringstream buf;
   octave_print_internal (buf, fmt, m_range.elem (j));
   return buf.str ();
 }
 
+template <typename T>
 bool
-octave_range::save_ascii (std::ostream& os)
+ov_range<T>::save_ascii (std::ostream& os)
 {
-  octave::range<double> r = range_value ();
-  double base = r.base ();
-  double limit = r.limit ();
-  double inc = r.increment ();
+  octave::range<T> r = m_range;
+  T base = r.base ();
+  T limit = r.limit ();
+  T inc = r.increment ();
   octave_idx_type len = r.numel ();
 
-  if (inc != 0)
+  if (inc != T (0))
     os << "# base, limit, increment\n";
   else
     os << "# base, length, increment\n";
 
-  octave::write_value<double> (os, base);
+  octave::write_value<T> (os, base);
   os << ' ';
-  if (inc != 0)
-    octave::write_value<double> (os, limit);
+  if (inc != T (0))
+    octave::write_value<T> (os, limit);
   else
     os << len;
   os << ' ';
-  octave::write_value<double> (os, inc);
+  octave::write_value<T> (os, inc);
   os << "\n";
 
   return true;
 }
 
+template <typename T>
 bool
-octave_range::load_ascii (std::istream& is)
+ov_range<T>::load_ascii (std::istream& is)
 {
   // # base, limit, range comment added by save ().
   skip_comments (is);
 
-  double base, limit, inc;
+  T base, limit, inc;
   is >> base >> limit >> inc;
 
   if (! is)
     error ("load: failed to load range constant");
 
-  if (inc != 0)
-    m_range = octave::range<double> (base, limit, inc);
+  if (inc != T (0))
+    m_range = octave::range<T> (base, limit, inc);
   else
     {
       octave_idx_type numel = static_cast<octave_idx_type> (limit);
-      m_range = octave::range<double>::make_constant (base, numel);
+      m_range = octave::range<T>::make_constant (base, numel);
     }
 
   return true;
 }
 
+template <typename T>
 bool
-octave_range::save_binary (std::ostream& os, bool /* save_as_floats */)
+ov_range<T>::save_binary (std::ostream& os, bool /* save_as_floats */)
 {
+  // FIXME: Not always double!
+
   char tmp = LS_DOUBLE;
   os.write (reinterpret_cast<char *> (&tmp), 1);
-  octave::range<double> r = range_value ();
-  double bas = r.base ();
-  double lim = r.limit ();
-  double inc = r.increment ();
-  if (inc == 0)
+  octave::range<T> r = m_range;
+  T bas = r.base ();
+  T lim = r.limit ();
+  T inc = r.increment ();
+  if (inc == T (0))
     lim = r.numel ();
 
-  os.write (reinterpret_cast<char *> (&bas), 8);
-  os.write (reinterpret_cast<char *> (&lim), 8);
-  os.write (reinterpret_cast<char *> (&inc), 8);
+  os.write (reinterpret_cast<char *> (&bas), sizeof (T));
+  os.write (reinterpret_cast<char *> (&lim), sizeof (T));
+  os.write (reinterpret_cast<char *> (&inc), sizeof (T));
 
   return true;
 }
 
+template <typename T>
 bool
-octave_range::load_binary (std::istream& is, bool swap,
-                           octave::mach_info::float_format /* fmt */)
+ov_range<T>::load_binary (std::istream& is, bool swap,
+                                   octave::mach_info::float_format /* fmt */)
 {
+  // FIXME: Not always double!
+
   char tmp;
   if (! is.read (reinterpret_cast<char *> (&tmp), 1))
     return false;
-  double bas, lim, inc;
-  if (! is.read (reinterpret_cast<char *> (&bas), 8))
+  T bas, lim, inc;
+  if (! is.read (reinterpret_cast<char *> (&bas), sizeof (T)))
     return false;
   if (swap)
-    swap_bytes<8> (&bas);
-  if (! is.read (reinterpret_cast<char *> (&lim), 8))
+    swap_bytes<sizeof (T)> (&bas);
+  if (! is.read (reinterpret_cast<char *> (&lim), sizeof (T)))
     return false;
   if (swap)
-    swap_bytes<8> (&lim);
-  if (! is.read (reinterpret_cast<char *> (&inc), 8))
+    swap_bytes<sizeof (T)> (&lim);
+  if (! is.read (reinterpret_cast<char *> (&inc), sizeof (T)))
     return false;
   if (swap)
-    swap_bytes<8> (&inc);
-  if (inc != 0)
-    m_range = octave::range<double> (bas, lim, inc);
+    swap_bytes<sizeof (T)> (&inc);
+  if (inc != T (0))
+    m_range = octave::range<T> (bas, lim, inc);
   else
     {
       octave_idx_type numel = static_cast<octave_idx_type> (lim);
-      m_range = octave::range<double>::make_constant (bas, numel);
+      m_range = octave::range<T>::make_constant (bas, numel);
     }
 
   return true;
@@ -629,23 +762,25 @@
 // H5T_NATIVE_DOUBLE to save as 'double').  Note that any necessary
 // conversions are handled automatically by HDF5.
 
+template <typename T>
 static hid_t
 hdf5_make_range_type (hid_t num_type)
 {
-  hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
+  hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (T) * 3);
 
-  H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
-  H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
-  H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
+  H5Tinsert (type_id, "base", 0 * sizeof (T), num_type);
+  H5Tinsert (type_id, "limit", 1 * sizeof (T), num_type);
+  H5Tinsert (type_id, "increment", 2 * sizeof (T), num_type);
 
   return type_id;
 }
 
 #endif
 
+template <typename T>
 bool
-octave_range::save_hdf5 (octave_hdf5_id loc_id, const char *name,
-                         bool /* save_as_floats */)
+ov_range<T>::save_hdf5 (octave_hdf5_id loc_id, const char *name,
+                        bool /* save_as_floats */)
 {
   bool retval = false;
 
@@ -658,7 +793,7 @@
   space_hid = H5Screate_simple (0, dimens, nullptr);
   if (space_hid < 0) return false;
 
-  type_hid = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
+  type_hid = hdf5_make_range_type<T> (hdf5_save_type);
   if (type_hid < 0)
     {
       H5Sclose (space_hid);
@@ -677,10 +812,10 @@
       return false;
     }
 
-  octave::range<double> r = range_value ();
-  double range_vals[3];
+  octave::range<T> r = m_range;
+  T range_vals[3];
   range_vals[0] = r.base ();
-  range_vals[1] = (r.increment () != 0 ? r.limit () : r.numel ());
+  range_vals[1] = (r.increment () != T (0) ? r.limit () : r.numel ());
   range_vals[2] = r.increment ();
 
   if (H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
@@ -708,8 +843,9 @@
   return retval;
 }
 
+template <typename T>
 bool
-octave_range::load_hdf5 (octave_hdf5_id loc_id, const char *name)
+ov_range<T>::load_hdf5 (octave_hdf5_id loc_id, const char *name)
 {
   bool retval = false;
 
@@ -722,7 +858,7 @@
 #endif
   hid_t type_hid = H5Dget_type (data_hid);
 
-  hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
+  hid_t range_type = hdf5_make_range_type<T> (hdf5_save_type);
 
   if (! hdf5_types_compatible (type_hid, range_type))
     {
@@ -742,7 +878,7 @@
       return false;
     }
 
-  double rangevals[3];
+  T rangevals[3];
   if (H5Dread (data_hid, range_type, octave_H5S_ALL, octave_H5S_ALL,
                octave_H5P_DEFAULT, rangevals)
       >= 0)
@@ -751,13 +887,12 @@
 
       // Don't use OCTAVE_RANGE_NELEM attribute, just reconstruct the range.
 
-      if (rangevals[2] != 0)
-        m_range = octave::range<double> (rangevals[0], rangevals[1],
-                                         rangevals[2]);
+      if (rangevals[2] != T (0))
+        m_range = octave::range<T> (rangevals[0], rangevals[1], rangevals[2]);
       else
         {
           octave_idx_type numel = static_cast<octave_idx_type> (rangevals[1]);
-          m_range = octave::range<double>::make_constant (rangevals[0], numel);
+          m_range = octave::range<T>::make_constant (rangevals[0], numel);
         }
     }
 
@@ -775,18 +910,22 @@
   return retval;
 }
 
+template <typename T>
 mxArray *
-octave_range::as_mxArray (bool interleaved) const
+ov_range<T>::as_mxArray (bool interleaved) const
 {
-  mxArray *retval = new mxArray (interleaved, mxDOUBLE_CLASS, dims (), mxREAL);
+  mxClassID mx_class = mx_type_traits<T>::mx_class;
 
-  mxDouble *pd = static_cast<mxDouble *> (retval->get_data ());
+  mxArray *retval = new mxArray (interleaved, mx_class, dims (), mxREAL);
+
+  typedef typename mx_type_traits<T>::mx_type mx_type;
+  mx_type *pd = static_cast<mx_type *> (retval->get_data ());
 
   mwSize nel = numel ();
 
-  Matrix m = matrix_value ();
+  Array<T> matrix = raw_array_value ();
 
-  const double *pdata = m.data ();
+  const T *pdata = matrix.data ();
 
   for (mwSize i = 0; i < nel; i++)
     pd[i] = pdata[i];
@@ -794,8 +933,138 @@
   return retval;
 }
 
+template <typename T>
 octave_value
-octave_range::fast_elem_extract (octave_idx_type n) const
+ov_range<T>::fast_elem_extract (octave_idx_type n) const
 {
   return (n < numel () ? octave_value (m_range.elem (n)) : octave_value ());
 }
+
+// Specializations.
+
+template <>
+octave::range<float>
+ov_range<float>::float_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<double>
+ov_range<double>::range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_int8>
+ov_range<octave_int8>::int8_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_int16>
+ov_range<octave_int16>::int16_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_int32>
+ov_range<octave_int32>::int32_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_int64>
+ov_range<octave_int64>::int64_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_uint8>
+ov_range<octave_uint8>::uint8_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_uint16>
+ov_range<octave_uint16>::uint16_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_uint32>
+ov_range<octave_uint32>::uint32_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+octave::range<octave_uint64>
+ov_range<octave_uint64>::uint64_range_value (void) const
+{
+  return m_range;
+}
+
+template <>
+idx_vector
+ov_range<double>::index_vector (bool require_integers) const
+{
+  if (m_idx_cache)
+    return *m_idx_cache;
+
+  if (require_integers || m_range.all_elements_are_ints ())
+    return set_idx_cache (idx_vector (m_range));
+
+  warning_with_id ("Octave:noninteger-range-as-index",
+                   "non-integer range used as index");
+
+  return octave_value (matrix_value ()).round ().index_vector ();
+}
+
+template <>
+octave_idx_type
+ov_range<double>::nnz (void) const
+{
+  return m_range.nnz ();
+}
+
+// The following specialization is also historical baggage.  For double
+// ranges, we can produce special double-valued diagnoal matrix objects
+// but Octave currently provides only double and Complex diagonal matrix
+// objects.
+
+template <>
+octave_value
+ov_range<double>::diag (octave_idx_type k) const
+{
+  // FIXME: this is a potential waste of memory.
+
+  return
+    (k == 0
+     ? octave_value (DiagMatrix (DiagArray2<double> (matrix_value ())))
+     : octave_value (m_range.diag (k)));
+}
+
+template <>
+octave_value
+ov_range<double>::diag (octave_idx_type nr, octave_idx_type nc) const
+{
+  Matrix mat = matrix_value ();
+
+  return mat.diag (nr, nc);
+}
+
+template <>
+void
+ov_range<double>::print_raw (std::ostream& os, bool pr_as_read_syntax) const
+{
+  octave_print_internal (os, m_range, pr_as_read_syntax,
+                         current_print_indent_level ());
+}
--- a/libinterp/octave-value/ov-range.h	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/octave-value/ov-range.h	Wed Aug 12 12:14:17 2020 -0400
@@ -43,6 +43,7 @@
 #include "error.h"
 #include "oct-stream.h"
 #include "ov-base.h"
+#include "ov-range-traits.h"
 #include "ov-re-mat.h"
 #include "ov-typeinfo.h"
 
@@ -51,136 +52,57 @@
 // Range values.
 
 template <typename T>
-octave::range<double>
-make_double_range (const octave::range<T>& r)
-{
-  return octave::range<double> (static_cast<double> (r.base ()),
-                                static_cast<double> (r.increment ()),
-                                static_cast<double> (r.limit ()),
-                                static_cast<double> (r.final_value ()),
-                                r.numel ());
-}
-
 class
-octave_range : public octave_base_value
+ov_range : public octave_base_value
 {
 public:
 
-  octave_range (void)
+  ov_range (void)
     : octave_base_value (), m_range (), m_idx_cache () { }
 
-  octave_range (const octave::range<float>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<double>& r)
+  ov_range (const octave::range<T>& r)
     : octave_base_value (), m_range (r), m_idx_cache ()
   {
     if (numel () < 0 && numel () != -2)
       error ("invalid range");
   }
 
-  octave_range (const octave::range<octave_int8>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<octave_int16>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<octave_int32>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<octave_int64>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<octave_uint8>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<octave_uint16>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<octave_uint32>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave::range<octave_uint64>& r)
-    : octave_base_value (), m_range (make_double_range (r)), m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (double base, double limit, double increment)
-    : octave_base_value (), m_range (base, limit, increment), m_idx_cache ()
-  {
-    if (numel () < 0)
-      error ("invalid range");
-  }
-
-  octave_range (const Range& r)
-    : octave_base_value (),
-      m_range (r.base (), r.increment (), r.limit (), r.numel ()),
-      m_idx_cache ()
-  {
-    if (numel () < 0 && numel () != -2)
-      error ("invalid range");
-  }
-
-  octave_range (const octave_range& r)
+  ov_range (const ov_range<T>& r)
     : octave_base_value (), m_range (r.m_range),
       m_idx_cache (r.m_idx_cache ? new idx_vector (*r.m_idx_cache) : nullptr)
   { }
 
-  octave_range (const Range& r, const idx_vector& cache)
-    : octave_base_value (),
-      m_range (r.base (), r.increment (), r.limit (), r.numel ()),
-      m_idx_cache ()
+  ov_range (const octave::range<T>& r, const idx_vector& cache)
+    : octave_base_value (), m_range (r), m_idx_cache ()
   {
     set_idx_cache (cache);
   }
 
-  ~octave_range (void) { clear_cached_info (); }
+  // No assignment.
+  ov_range& operator = (const ov_range&) = delete;
+
+  ~ov_range (void) { clear_cached_info (); }
 
-  octave_base_value * clone (void) const { return new octave_range (*this); }
+  octave_base_value * clone (void) const
+  {
+    return new ov_range (*this);
+  }
 
   // A range is really just a special kind of real matrix object.  In
   // the places where we need to call empty_clone, it makes more sense
   // to create an empty matrix (0x0) instead of an empty range (1x0).
-  octave_base_value * empty_clone (void) const { return new octave_matrix (); }
+
+  octave_base_value * empty_clone (void) const
+  {
+    return new typename octave_value_range_traits<T>::matrix_type ();
+  }
 
   type_conv_info numeric_conversion_function (void) const;
 
   octave_base_value * try_narrowing_conversion (void);
 
+  builtin_type_t builtin_type (void) const { return class_to_btyp<T>::btyp; }
+
   // We don't need to override all three forms of subsref.  The using
   // declaration will avoid warnings about partially-overloaded virtual
   // functions.
@@ -206,21 +128,31 @@
 
   octave_idx_type numel (void) const { return m_range.numel (); }
 
-  octave_idx_type nnz (void) const { return m_range.nnz (); }
+  octave_idx_type nnz (void) const
+  {
+    // FIXME: this is a potential waste of memory.
+
+    octave_value tmp (raw_array_value ());
+    return tmp.nnz ();
+  }
 
   octave_value resize (const dim_vector& dv, bool fill = false) const;
 
-  size_t byte_size (void) const { return 3 * sizeof (double); }
+  size_t byte_size (void) const { return 3 * sizeof (T); }
 
   octave_value reshape (const dim_vector& new_dims) const
-  { return NDArray (array_value ().reshape (new_dims)); }
+  {
+    return raw_array_value ().reshape (new_dims);
+  }
 
   octave_value permute (const Array<int>& vec, bool inv = false) const
-  { return NDArray (array_value ().permute (vec, inv)); }
+  {
+    return raw_array_value ().permute (vec, inv);
+  }
 
   octave_value squeeze (void) const { return m_range; }
 
-  octave_value full_value (void) const { return matrix_value (); }
+  octave_value full_value (void) const { return raw_array_value (); }
 
   bool is_defined (void) const { return true; }
 
@@ -228,69 +160,142 @@
 
   bool is_range (void) const { return true; }
 
-  octave_value all (int dim = 0) const;
+  bool is_double_type (void) const { return builtin_type () == btyp_double; }
+
+  bool is_single_type (void) const { return builtin_type () == btyp_float; }
+
+  bool isfloat (void) const { return btyp_isfloat (builtin_type ()); }
+
+  bool is_int8_type (void) const { return builtin_type () == btyp_int8; }
+
+  bool is_int16_type (void) const { return builtin_type () == btyp_int16; }
+
+  bool is_int32_type (void) const { return builtin_type () == btyp_int32; }
+
+  bool is_int64_type (void) const { return builtin_type () == btyp_int64; }
+
+  bool is_uint8_type (void) const { return builtin_type () == btyp_uint8; }
+
+  bool is_uint16_type (void) const { return builtin_type () == btyp_uint16; }
 
-  octave_value any (int dim = 0) const;
+  bool is_uint32_type (void) const { return builtin_type () == btyp_uint32; }
+
+  bool is_uint64_type (void) const { return builtin_type () == btyp_uint64; }
+
+  bool isinteger (void) const
+  {
+    return btyp_isinteger (builtin_type ());
+  }
+
+  bool isreal (void) const { return isfloat (); }
+
+  bool isnumeric (void) const
+  {
+    return btyp_isnumeric (builtin_type ());
+  }
+
+  bool is_true (void) const { return nnz () == numel (); }
 
-  octave_value diag (octave_idx_type k = 0) const;
+  octave_value all (int dim = 0) const
+  {
+    // FIXME: this is a potential waste of memory.
+
+    typedef typename octave_value_range_traits<T>::matrix_type ov_mx_type;
+    typename ov_mx_type::object_type m (raw_array_value ());
+
+    return m.all (dim);
+  }
+
+  octave_value any (int dim = 0) const
+  {
+    // FIXME: this is a potential waste of memory.
+
+    typedef typename octave_value_range_traits<T>::matrix_type ov_mx_type;
+    typename ov_mx_type::object_type m (raw_array_value ());
 
-  octave_value diag (octave_idx_type m, octave_idx_type n) const;
+    return m.any (dim);
+  }
+
+  octave_value diag (octave_idx_type k = 0) const
+  {
+    // FIXME: this is a potential waste of memory.
+
+    return m_range.diag (k);
+  }
+
+  octave_value diag (octave_idx_type nr, octave_idx_type nc) const
+  {
+    // FIXME: this is a potential waste of memory.
+
+    typedef typename octave_value_range_traits<T>::matrix_type ov_mx_type;
+    typename ov_mx_type::object_type m (raw_array_value ());
+
+    return m.diag (nr, nc);
+  }
 
   octave_value sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const
   {
-    Matrix tmp = matrix_value ();
+    Array<T> tmp = raw_array_value ();
     return tmp.sort (dim, mode);
   }
 
   octave_value sort (Array<octave_idx_type>& sidx, octave_idx_type dim = 0,
                      sortmode mode = ASCENDING) const
   {
-    Matrix tmp = matrix_value ();
+    Array<T> tmp = raw_array_value ();
     return tmp.sort (sidx, dim, mode);
   }
 
   sortmode issorted (sortmode mode = UNSORTED) const
-  { return m_range.issorted (mode); }
+  {
+    return m_range.issorted (mode);
+  }
 
   Array<octave_idx_type> sort_rows_idx (sortmode) const
-  { return Array<octave_idx_type> (dim_vector (1, 0)); }
+  {
+    return Array<octave_idx_type> (dim_vector (1, 0));
+  }
 
   sortmode is_sorted_rows (sortmode mode = UNSORTED) const
-  { return (mode == UNSORTED) ? ASCENDING : mode; }
-
-  builtin_type_t builtin_type (void) const { return btyp_double; }
-
-  bool isreal (void) const { return true; }
+  {
+    return (mode == UNSORTED) ? ASCENDING : mode;
+  }
 
-  bool is_double_type (void) const { return true; }
-
-  bool isfloat (void) const { return true; }
-
-  bool isnumeric (void) const { return true; }
-
-  bool is_true (void) const;
+  Array<T> raw_array_value (void) const { return m_range.array_value (); }
 
   double double_value (bool = false) const;
 
   float float_value (bool = false) const;
 
   double scalar_value (bool frc_str_conv = false) const
-  { return double_value (frc_str_conv); }
+  {
+    return double_value (frc_str_conv);
+  }
 
   float float_scalar_value (bool frc_str_conv = false) const
-  { return float_value (frc_str_conv); }
+  {
+    return float_value (frc_str_conv);
+  }
 
   Matrix matrix_value (bool = false) const
-  { return m_range.array_value (); }
+  {
+    return raw_array_value ();
+  }
 
   FloatMatrix float_matrix_value (bool = false) const
-  { return matrix_value (); }
+  {
+    return raw_array_value ();
+  }
 
   NDArray array_value (bool = false) const
-  { return matrix_value (); }
+  {
+    return raw_array_value ();
+  }
 
   FloatNDArray float_array_value (bool = false) const
-  { return FloatMatrix (matrix_value ()); }
+  {
+    return raw_array_value ();
+  }
 
   charNDArray char_array_value (bool = false) const;
 
@@ -298,35 +303,55 @@
   // functions to avoid the intermediate conversion to a matrix
   // object.
 
-  int8NDArray
-  int8_array_value (void) const { return int8NDArray (array_value ()); }
+  int8NDArray int8_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
-  int16NDArray
-  int16_array_value (void) const { return int16NDArray (array_value ()); }
+  int16NDArray int16_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
-  int32NDArray
-  int32_array_value (void) const { return int32NDArray (array_value ()); }
+  int32NDArray int32_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
-  int64NDArray
-  int64_array_value (void) const { return int64NDArray (array_value ()); }
+  int64NDArray int64_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
-  uint8NDArray
-  uint8_array_value (void) const { return uint8NDArray (array_value ()); }
+  uint8NDArray uint8_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
-  uint16NDArray
-  uint16_array_value (void) const { return uint16NDArray (array_value ()); }
+  uint16NDArray uint16_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
-  uint32NDArray
-  uint32_array_value (void) const { return uint32NDArray (array_value ()); }
+  uint32NDArray uint32_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
-  uint64NDArray
-  uint64_array_value (void) const { return uint64NDArray (array_value ()); }
+  uint64NDArray uint64_array_value (void) const
+  {
+    return raw_array_value ();
+  }
 
   SparseMatrix sparse_matrix_value (bool = false) const
-  { return SparseMatrix (matrix_value ()); }
+  {
+    return SparseMatrix (matrix_value ());
+  }
 
   SparseComplexMatrix sparse_complex_matrix_value (bool = false) const
-  { return SparseComplexMatrix (sparse_matrix_value ()); }
+  {
+    return SparseComplexMatrix (complex_matrix_value ());
+  }
 
   Complex complex_value (bool = false) const;
 
@@ -335,18 +360,44 @@
   boolNDArray bool_array_value (bool warn = false) const;
 
   ComplexMatrix complex_matrix_value (bool = false) const
-  { return ComplexMatrix (matrix_value ()); }
+  {
+    return raw_array_value ();
+  }
 
   FloatComplexMatrix float_complex_matrix_value (bool = false) const
-  { return FloatComplexMatrix (matrix_value ()); }
+  {
+    return raw_array_value ();
+  }
 
   ComplexNDArray complex_array_value (bool = false) const
-  { return ComplexMatrix (matrix_value ()); }
+  {
+    return raw_array_value ();
+  }
 
   FloatComplexNDArray float_complex_array_value (bool = false) const
-  { return FloatComplexMatrix (matrix_value ()); }
+  {
+    return raw_array_value ();
+  }
+
+  octave::range<float> float_range_value (void) const;
+
+  octave::range<double> range_value (void) const;
+
+  octave::range<octave_int8> int8_range_value (void) const;
+
+  octave::range<octave_int16> int16_range_value (void) const;
 
-  octave::range<double> range_value (void) const { return m_range; }
+  octave::range<octave_int32> int32_range_value (void) const;
+
+  octave::range<octave_int64> int64_range_value (void) const;
+
+  octave::range<octave_uint8> uint8_range_value (void) const;
+
+  octave::range<octave_uint16> uint16_range_value (void) const;
+
+  octave::range<octave_uint32> uint32_range_value (void) const;
+
+  octave::range<octave_uint64> uint64_range_value (void) const;
 
   octave_value convert_to_str_internal (bool pad, bool force, char type) const;
 
@@ -385,7 +436,7 @@
   bool load_binary (std::istream& is, bool swap,
                     octave::mach_info::float_format fmt);
 
-  bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
+  bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool flag);
 
   bool load_hdf5 (octave_hdf5_id loc_id, const char *name);
 
@@ -403,15 +454,15 @@
 
   octave_value map (unary_mapper_t umap) const
   {
-    octave_matrix m (matrix_value ());
-    return m.map (umap);
+    octave_value tmp (raw_array_value ());
+    return tmp.map (umap);
   }
 
   octave_value fast_elem_extract (octave_idx_type n) const;
 
-private:
+protected:
 
-  octave::range<double> m_range;
+  octave::range<T> m_range;
 
   idx_vector set_idx_cache (const idx_vector& idx) const
   {
@@ -427,25 +478,106 @@
 
   mutable idx_vector *m_idx_cache;
 
-  // No assignment.
-
-  octave_range& operator = (const octave_range&);
+  static octave_hdf5_id hdf5_save_type;
 
   DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
 };
 
-typedef octave_range octave_float_range;
-typedef octave_range octave_double_range;
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, float)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, double)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_int8)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_int16)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_int32)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_int64)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_uint8)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_uint16)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_uint32)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_uint64)
+
+// Specializations.
+
+template <>
+octave::range<float>
+ov_range<float>::float_range_value (void) const;
+
+template <>
+octave::range<double>
+ov_range<double>::range_value (void) const;
+
+template <>
+octave::range<octave_int8>
+ov_range<octave_int8>::int8_range_value (void) const;
+
+template <>
+octave::range<octave_int16>
+ov_range<octave_int16>::int16_range_value (void) const;
+
+template <>
+octave::range<octave_int32>
+ov_range<octave_int32>::int32_range_value (void) const;
+
+template <>
+octave::range<octave_int64>
+ov_range<octave_int64>::int64_range_value (void) const;
 
-typedef octave_range octave_int8_range;
-typedef octave_range octave_int16_range;
-typedef octave_range octave_int32_range;
-typedef octave_range octave_int64_range;
+template <>
+octave::range<octave_uint8>
+ov_range<octave_uint8>::uint8_range_value (void) const;
+
+template <>
+octave::range<octave_uint16>
+ov_range<octave_uint16>::uint16_range_value (void) const;
+
+template <>
+octave::range<octave_uint32>
+ov_range<octave_uint32>::uint32_range_value (void) const;
+
+template <>
+octave::range<octave_uint64>
+ov_range<octave_uint64>::uint64_range_value (void) const;
+
+// The following specializations are here to preserve previous Range
+// performance until solutions can be generalized for other types.
 
-typedef octave_range octave_uint8_range;
-typedef octave_range octave_uint16_range;
-typedef octave_range octave_uint32_range;
-typedef octave_range octave_uint64_range;
+template <>
+idx_vector
+ov_range<double>::index_vector (bool require_integers) const;
+
+template <>
+octave_idx_type
+ov_range<double>::nnz (void) const;
+
+// The following specialization is also historical baggage.  For double
+// ranges, we can produce special double-valued diagnoal matrix objects
+// but Octave currently provides only double and Complex diagonal matrix
+// objects.
+
+template <>
+octave_value
+ov_range<double>::diag (octave_idx_type k) const;
+
+template <>
+octave_value
+ov_range<double>::diag (octave_idx_type nr, octave_idx_type nc) const;
+
+template <>
+void
+ov_range<double>::print_raw (std::ostream& os, bool pr_as_read_syntax) const;
 
 
+typedef ov_range<float> octave_float_range;
+typedef ov_range<double> octave_double_range;
+
+typedef ov_range<octave_int8> octave_int8_range;
+typedef ov_range<octave_int16> octave_int16_range;
+typedef ov_range<octave_int32> octave_int32_range;
+typedef ov_range<octave_int64> octave_int64_range;
+
+typedef ov_range<octave_uint8> octave_uint8_range;
+typedef ov_range<octave_uint16> octave_uint16_range;
+typedef ov_range<octave_uint32> octave_uint32_range;
+typedef ov_range<octave_uint64> octave_uint64_range;
+
+typedef octave_double_range octave_range;
+
 #endif
--- a/libinterp/octave-value/ov.cc	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/octave-value/ov.cc	Wed Aug 12 12:14:17 2020 -0400
@@ -1069,7 +1069,7 @@
 }
 
 octave_value::octave_value (double base, double limit, double inc)
-  : rep (new octave_range (base, limit, inc))
+  : rep (new ov_range<double> (octave::range<double> (base, inc, limit)))
 {
   maybe_mutate ();
 }
@@ -1081,7 +1081,7 @@
     error ("invalid range");
 
   if (force_range || ! Vdisable_range)
-    rep = dynamic_cast<octave_base_value *> (new octave_range (r));
+    rep = dynamic_cast<octave_base_value *> (new ov_range<double> (octave::range<double> (r.base (), r.increment (), r.limit ())));
   else
     rep = dynamic_cast<octave_base_value *> (new octave_matrix (r.matrix_value ()));
 
@@ -1107,7 +1107,7 @@
 
 octave_value::octave_value (const octave::range<float>& r, bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_float_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<float> (r))
          : dynamic_cast<octave_base_value *> (new octave_float_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1115,7 +1115,7 @@
 
 octave_value::octave_value (const octave::range<double>& r, bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_double_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<double> (r))
          : dynamic_cast<octave_base_value *> (new octave_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1124,7 +1124,7 @@
 octave_value::octave_value (const octave::range<octave_int8>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_int8_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_int8> (r))
          : dynamic_cast<octave_base_value *> (new octave_int8_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1133,7 +1133,7 @@
 octave_value::octave_value (const octave::range<octave_int16>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_int16_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_int16> (r))
          : dynamic_cast<octave_base_value *> (new octave_int16_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1142,7 +1142,7 @@
 octave_value::octave_value (const octave::range<octave_int32>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_int32_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_int32> (r))
          : dynamic_cast<octave_base_value *> (new octave_int32_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1151,7 +1151,7 @@
 octave_value::octave_value (const octave::range<octave_int64>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_int64_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_int64> (r))
          : dynamic_cast<octave_base_value *> (new octave_int64_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1160,7 +1160,7 @@
 octave_value::octave_value (const octave::range<octave_uint8>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_uint8_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_uint8> (r))
          : dynamic_cast<octave_base_value *> (new octave_uint8_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1169,7 +1169,7 @@
 octave_value::octave_value (const octave::range<octave_uint16>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_uint16_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_uint16> (r))
          : dynamic_cast<octave_base_value *> (new octave_uint16_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1178,7 +1178,7 @@
 octave_value::octave_value (const octave::range<octave_uint32>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_uint32_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_uint32> (r))
          : dynamic_cast<octave_base_value *> (new octave_uint32_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -1187,7 +1187,7 @@
 octave_value::octave_value (const octave::range<octave_uint64>& r,
                             bool force_range)
   : rep (force_range || ! Vdisable_range
-         ? dynamic_cast<octave_base_value *> (new octave_uint64_range (r))
+         ? dynamic_cast<octave_base_value *> (new ov_range<octave_uint64> (r))
          : dynamic_cast<octave_base_value *> (new octave_uint64_matrix (r.array_value ())))
 {
   maybe_mutate ();
@@ -2193,7 +2193,16 @@
 XVALUE_EXTRACTOR (Cell, xcell_value, cell_value)
 XVALUE_EXTRACTOR (Array<std::string>, xcellstr_value, cellstr_value)
 
+XVALUE_EXTRACTOR (octave::range<float>, xfloat_range_value, float_range_value)
 XVALUE_EXTRACTOR (octave::range<double>, xrange_value, range_value)
+XVALUE_EXTRACTOR (octave::range<octave_int8>, xint8_range_value, int8_range_value)
+XVALUE_EXTRACTOR (octave::range<octave_int16>, xint16_range_value, int16_range_value)
+XVALUE_EXTRACTOR (octave::range<octave_int32>, xint32_range_value, int32_range_value)
+XVALUE_EXTRACTOR (octave::range<octave_int64>, xint64_range_value, int64_range_value)
+XVALUE_EXTRACTOR (octave::range<octave_uint8>, xuint8_range_value, uint8_range_value)
+XVALUE_EXTRACTOR (octave::range<octave_uint16>, xuint16_range_value, uint16_range_value)
+XVALUE_EXTRACTOR (octave::range<octave_uint32>, xuint32_range_value, uint32_range_value)
+XVALUE_EXTRACTOR (octave::range<octave_uint64>, xuint64_range_value, uint64_range_value)
 
 XVALUE_EXTRACTOR (octave_map, xmap_value, map_value)
 XVALUE_EXTRACTOR (octave_scalar_map, xscalar_map_value, scalar_map_value)
@@ -3174,7 +3183,16 @@
   octave_diag_matrix::register_type (ti);
   octave_complex_matrix::register_type (ti);
   octave_complex_diag_matrix::register_type (ti);
-  octave_range::register_type (ti);
+  ov_range<float>::register_type (ti);
+  ov_range<double>::register_type (ti);
+  ov_range<octave_int8>::register_type (ti);
+  ov_range<octave_int16>::register_type (ti);
+  ov_range<octave_int32>::register_type (ti);
+  ov_range<octave_int64>::register_type (ti);
+  ov_range<octave_uint8>::register_type (ti);
+  ov_range<octave_uint16>::register_type (ti);
+  ov_range<octave_uint32>::register_type (ti);
+  ov_range<octave_uint64>::register_type (ti);
   octave_bool::register_type (ti);
   octave_bool_matrix::register_type (ti);
   octave_char_matrix_str::register_type (ti);
--- a/libinterp/octave-value/ov.h	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/octave-value/ov.h	Wed Aug 12 12:14:17 2020 -0400
@@ -950,9 +950,36 @@
   Array<std::string> cellstr_value (void) const
   { return rep->cellstr_value (); }
 
+  octave::range<float> float_range_value (void) const
+  { return rep->float_range_value (); }
+
   octave::range<double> range_value (void) const
   { return rep->range_value (); }
 
+  octave::range<octave_int8> int8_range_value (void) const
+  { return rep->int8_range_value (); }
+
+  octave::range<octave_int16> int16_range_value (void) const
+  { return rep->int16_range_value (); }
+
+  octave::range<octave_int32> int32_range_value (void) const
+  { return rep->int32_range_value (); }
+
+  octave::range<octave_int64> int64_range_value (void) const
+  { return rep->int64_range_value (); }
+
+  octave::range<octave_uint8> uint8_range_value (void) const
+  { return rep->uint8_range_value (); }
+
+  octave::range<octave_uint16> uint16_range_value (void) const
+  { return rep->uint16_range_value (); }
+
+  octave::range<octave_uint32> uint32_range_value (void) const
+  { return rep->uint32_range_value (); }
+
+  octave::range<octave_uint64> uint64_range_value (void) const
+  { return rep->uint64_range_value (); }
+
   octave_map map_value (void) const;
 
   octave_scalar_map scalar_map_value (void) const;
@@ -1162,8 +1189,26 @@
 
   Array<std::string> xcellstr_value (const char *fmt, ...) const;
 
+  octave::range<float> xfloat_range_value (const char *fmt, ...) const;
+
   octave::range<double> xrange_value (const char *fmt, ...) const;
 
+  octave::range<octave_int8> xint8_range_value (const char *fmt, ...) const;
+
+  octave::range<octave_int16> xint16_range_value (const char *fmt, ...) const;
+
+  octave::range<octave_int32> xint32_range_value (const char *fmt, ...) const;
+
+  octave::range<octave_int64> xint64_range_value (const char *fmt, ...) const;
+
+  octave::range<octave_uint8> xuint8_range_value (const char *fmt, ...) const;
+
+  octave::range<octave_uint16> xuint16_range_value (const char *fmt, ...) const;
+
+  octave::range<octave_uint32> xuint32_range_value (const char *fmt, ...) const;
+
+  octave::range<octave_uint64> xuint64_range_value (const char *fmt, ...) const;
+
   octave_map xmap_value (const char *fmt, ...) const;
 
   octave_scalar_map xscalar_map_value (const char *fmt, ...) const;
--- a/libinterp/parse-tree/pt-eval.cc	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Wed Aug 12 12:14:17 2020 -0400
@@ -2490,6 +2490,36 @@
       }
   }
 
+  template <typename T>
+  void
+  tree_evaluator::execute_range_loop (const range<T>& rng, size_t line,
+                                      octave_lvalue& ult,
+                                      tree_statement_list *loop_body)
+  {
+    octave_idx_type steps = rng.numel ();
+
+    if (octave::math::isinf (rng.limit ()))
+      warning_with_id ("Octave:infinite-loop",
+                       "FOR loop limit is infinite, will stop after %"
+                       OCTAVE_IDX_TYPE_FORMAT " steps", steps);
+
+    for (octave_idx_type i = 0; i < steps; i++)
+      {
+        if (m_echo_state)
+          m_echo_file_pos = line;
+
+        octave_value val (rng.elem (i));
+
+        ult.assign (octave_value::op_asn_eq, val);
+
+        if (loop_body)
+          loop_body->accept (*this);
+
+        if (quit_loop_now ())
+          break;
+      }
+  }
+
   void
   tree_evaluator::visit_simple_for_command (tree_simple_for_command& cmd)
   {
@@ -2529,33 +2559,70 @@
 
     if (rhs.is_range ())
       {
-        octave::range<double> rng = rhs.range_value ();
-
-        octave_idx_type steps = rng.numel ();
-
-        if (octave::math::isinf (rng.limit ()))
-          warning_with_id ("Octave:infinite-loop",
-                           "FOR loop limit is infinite, will stop after %"
-                           OCTAVE_IDX_TYPE_FORMAT " steps",
-                           steps);
-
-        for (octave_idx_type i = 0; i < steps; i++)
+        // FIXME: is there a better way to dispatch here?
+
+        if (rhs.is_double_type ())
+          {
+            execute_range_loop (rhs.range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_int64_type ())
+          {
+            execute_range_loop (rhs.int64_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_uint64_type ())
+          {
+            execute_range_loop (rhs.uint64_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_int32_type ())
+          {
+            execute_range_loop (rhs.int32_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_uint32_type ())
           {
-            if (m_echo_state)
-              m_echo_file_pos = line;
-
-            octave_value val (rng.elem (i));
-
-            ult.assign (octave_value::op_asn_eq, val);
-
-            if (loop_body)
-              loop_body->accept (*this);
-
-            if (quit_loop_now ())
-              break;
+            execute_range_loop (rhs.uint32_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_int16_type ())
+          {
+            execute_range_loop (rhs.int16_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_uint16_type ())
+          {
+            execute_range_loop (rhs.uint16_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_int8_type ())
+          {
+            execute_range_loop (rhs.int8_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_uint8_type ())
+          {
+            execute_range_loop (rhs.uint8_range_value (), line, ult, loop_body);
+            return;
+          }
+
+        if (rhs.is_single_type ())
+          {
+            execute_range_loop (rhs.float_range_value (), line, ult, loop_body);
+            return;
           }
       }
-    else if (rhs.is_scalar_type ())
+
+    if (rhs.is_scalar_type ())
       {
         if (m_echo_state)
           m_echo_file_pos = line;
@@ -2567,9 +2634,15 @@
 
         // Maybe decrement break and continue states.
         quit_loop_now ();
+
+        return;
       }
-    else if (rhs.is_matrix_type () || rhs.iscell () || rhs.is_string ()
-             || rhs.isstruct ())
+
+    // Also handle any range types not explicitly handled above, though
+    // not as efficiently as the specialized code above.
+
+    if (rhs.is_range () || rhs.is_matrix_type () || rhs.iscell ()
+        || rhs.is_string () || rhs.isstruct ())
       {
         // A matrix or cell is reshaped to 2 dimensions and iterated by
         // columns.
@@ -2624,10 +2697,12 @@
             // Handle empty cases, while still assigning to loop var.
             ult.assign (octave_value::op_asn_eq, arg);
           }
+
+        return;
       }
-    else
-      error ("invalid type in for loop expression near line %d, column %d",
-             cmd.line (), cmd.column ());
+
+    error ("invalid type in for loop expression near line %d, column %d",
+           cmd.line (), cmd.column ());
   }
 
   void
--- a/libinterp/parse-tree/pt-eval.h	Thu Aug 20 17:42:26 2020 -0400
+++ b/libinterp/parse-tree/pt-eval.h	Wed Aug 12 12:14:17 2020 -0400
@@ -717,6 +717,11 @@
 
   private:
 
+    template <typename T>
+    void execute_range_loop (const range<T>& rng, size_t line,
+                             octave_lvalue& ult,
+                             tree_statement_list *loop_body);
+
     void set_echo_state (int type, const std::string& file_name, size_t pos);
 
     void maybe_set_echo_state (void);
--- a/liboctave/array/Range.h	Thu Aug 20 17:42:26 2020 -0400
+++ b/liboctave/array/Range.h	Wed Aug 12 12:14:17 2020 -0400
@@ -139,7 +139,7 @@
       // clearer to see "make_constant" instead of puzzling over the
       // purpose of this strange constructor form.
 
-      T final_val = base + double (numel - 1) * increment;
+      T final_val = base + (numel - 1) * increment;
 
       return range<T> (base, increment, final_val, numel);
     }
@@ -298,7 +298,7 @@
           retval(0) = m_base;
 
           for (octave_idx_type i = 1; i < nel - 1; i++)
-            retval.xelem (i) = m_base + double (i) * m_increment;
+            retval.xelem (i) = m_base + i * m_increment;
 
           retval.xelem (nel - 1) = final_value ();
         }
@@ -337,7 +337,7 @@
 
     T get_final_value (void) const
     {
-      return m_base + double (m_numel - 1) * m_increment;
+      return m_base + (m_numel - 1) * m_increment;
     }
   };