# HG changeset patch # User Markus Mützel # Date 1638292760 -3600 # Node ID 4736bc8e98046734fa438cdba44ae64616586d6b # Parent 391b35ef8b24fb811533ea360900cf1b6e2a27d0 Allow descending ranges of all integer types (bug #61132). * liboctave/array/Range.h (range): Add new property "m_reverse" for reverse ranges. Add optional argument "reverse" to constructors. Add support for reverse ranges to member functions. * liboctave/array/Range.cc (xinit): Add option for reverse ranges. * libinterp/octave-value/ov.cc (make_range): Create reverse ranges for negative increments. * libinterp/octave-value/ov-range.cc (save_ascii, load_ascii, save_binary, load_binary, save_hdf5, load_hdf5): Include config.h to allow saving as HDF5. Add support for saving and loading reverse ranges. Add tests. * test/range.tst: Add tests for descending ranges with unsigned integers. diff -r 391b35ef8b24 -r 4736bc8e9804 libinterp/octave-value/ov-range.cc --- a/libinterp/octave-value/ov-range.cc Thu Dec 02 08:03:39 2021 -0500 +++ b/libinterp/octave-value/ov-range.cc Tue Nov 30 18:19:20 2021 +0100 @@ -23,9 +23,9 @@ // //////////////////////////////////////////////////////////////////////// -// 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. +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif #include #include @@ -648,18 +648,24 @@ template bool -ov_range::save_ascii (std::ostream& os) +xsave_ascii (std::ostream& os, const octave::range& r, + const bool with_reverse) { - octave::range r = m_range; T base = r.base (); T limit = r.limit (); T inc = r.increment (); + bool rev = r.reverse (); octave_idx_type len = r.numel (); if (inc != T (0)) - os << "# base, limit, increment\n"; + os << "# base, limit, increment"; else - os << "# base, length, increment\n"; + os << "# base, length, increment"; + + if (with_reverse) + os << ", reverse\n"; + else + os << "\n"; octave::write_value (os, base); os << ' '; @@ -669,6 +675,8 @@ os << len; os << ' '; octave::write_value (os, inc); + if (with_reverse) + os << ' ' << rev; os << "\n"; return true; @@ -676,37 +684,141 @@ template bool +ov_range::save_ascii (std::ostream& os) +{ + return xsave_ascii (os, m_range, false); +} + +// specialize for saving with "reverse" flag + +template <> +bool +ov_range::save_ascii (std::ostream& os) +{ + return xsave_ascii (os, m_range, true); +} + +template <> +bool +ov_range::save_ascii (std::ostream& os) +{ + return xsave_ascii (os, m_range, true); +} + +template <> +bool +ov_range::save_ascii (std::ostream& os) +{ + return xsave_ascii (os, m_range, true); +} + +template <> +bool +ov_range::save_ascii (std::ostream& os) +{ + return xsave_ascii (os, m_range, true); +} + +template +bool +xload_ascii (std::istream& is, octave::range& r, const bool with_reverse) +{ + // # base, limit, range comment added by save (). + skip_comments (is); + + T base, limit, inc; + bool rev = false; + is >> base >> limit >> inc; + + if (with_reverse) + is >> rev; + + if (! is) + error ("load: failed to load range constant"); + + if (inc != T (0)) + r = octave::range (base, inc, limit, rev); + else + { + octave_idx_type numel = static_cast (limit); + r = octave::range::make_constant (base, numel, rev); + } + + return true; +} + +template +bool ov_range::load_ascii (std::istream& is) { - // # base, limit, range comment added by save (). - skip_comments (is); + return xload_ascii (is, m_range, false); +} + +// specialize for loading with "reverse" flag + +template <> +bool +ov_range::load_ascii (std::istream& is) +{ + return xload_ascii (is, m_range, true); +} - T base, limit, inc; - is >> base >> limit >> inc; +template <> +bool +ov_range::load_ascii (std::istream& is) +{ + return xload_ascii (is, m_range, true); +} - if (! is) - error ("load: failed to load range constant"); +template <> +bool +ov_range::load_ascii (std::istream& is) +{ + return xload_ascii (is, m_range, true); +} - if (inc != T (0)) - m_range = octave::range (base, limit, inc); - else - { - octave_idx_type numel = static_cast (limit); - m_range = octave::range::make_constant (base, numel); - } +template <> +bool +ov_range::load_ascii (std::istream& is) +{ + return xload_ascii (is, m_range, true); +} - return true; -} +/* +%!test +%! a = b = 1:4; +%! sv_file = [tempname(), ".sav"]; +%! unwind_protect +%! save (sv_file, "a", "-text"); +%! clear a; +%! load (sv_file); +%! assert (a, b); +%! unwind_protect_cleanup +%! unlink (sv_file); +%! end_unwind_protect + +%!test +%! a = b = uint8(5):-1:0; +%! sv_file = [tempname(), ".sav"]; +%! unwind_protect +%! save (sv_file, "a", "-text"); +%! clear a; +%! load (sv_file); +%! assert (a, b); +%! unwind_protect_cleanup +%! unlink (sv_file); +%! end_unwind_protect +*/ template bool -ov_range::save_binary (std::ostream& os, bool /* save_as_floats */) +xsave_binary (std::ostream& os, bool /* save_as_floats */, + const octave::range& r, const bool with_reverse) { // FIXME: Not always double! char tmp = LS_DOUBLE; os.write (reinterpret_cast (&tmp), 1); - octave::range r = m_range; T bas = r.base (); T lim = r.limit (); T inc = r.increment (); @@ -716,14 +828,55 @@ os.write (reinterpret_cast (&bas), sizeof (T)); os.write (reinterpret_cast (&lim), sizeof (T)); os.write (reinterpret_cast (&inc), sizeof (T)); + if (with_reverse) + { + bool rev = r.reverse (); + os.write (reinterpret_cast (&rev), sizeof (bool)); + } return true; } template bool -ov_range::load_binary (std::istream& is, bool swap, - octave::mach_info::float_format /* fmt */) +ov_range::save_binary (std::ostream& os, bool save_as_floats) +{ + return xsave_binary (os, save_as_floats, m_range, false); +} + +template <> +bool +ov_range::save_binary (std::ostream& os, bool save_as_floats) +{ + return xsave_binary (os, save_as_floats, m_range, true); +} + +template <> +bool +ov_range::save_binary (std::ostream& os, bool save_as_floats) +{ + return xsave_binary (os, save_as_floats, m_range, true); +} + +template <> +bool +ov_range::save_binary (std::ostream& os, bool save_as_floats) +{ + return xsave_binary (os, save_as_floats, m_range, true); +} + +template <> +bool +ov_range::save_binary (std::ostream& os, bool save_as_floats) +{ + return xsave_binary (os, save_as_floats, m_range, true); +} + +template +bool +xload_binary (std::istream& is, bool swap, + octave::mach_info::float_format /* fmt */, + octave::range& r, const bool with_reverse) { // FIXME: Not always double! @@ -743,17 +896,91 @@ return false; if (swap) swap_bytes (&inc); + bool rev = false; + if (with_reverse) + { + if (! is.read (reinterpret_cast (&rev), sizeof (bool))) + return false; + if (swap) + swap_bytes (&rev); + } if (inc != T (0)) - m_range = octave::range (bas, inc, lim); + r = octave::range (bas, inc, lim, rev); else { octave_idx_type numel = static_cast (lim); - m_range = octave::range::make_constant (bas, numel); + r = octave::range::make_constant (bas, numel, rev); } return true; } +template +bool +ov_range::load_binary (std::istream& is, bool swap, + octave::mach_info::float_format fmt) +{ + return xload_binary (is, swap, fmt, m_range, false); +} + +template <> +bool +ov_range::load_binary (std::istream& is, bool swap, + octave::mach_info::float_format fmt) +{ + return xload_binary (is, swap, fmt, m_range, true); +} + +template <> +bool +ov_range::load_binary (std::istream& is, bool swap, + octave::mach_info::float_format fmt) +{ + return xload_binary (is, swap, fmt, m_range, true); +} + +template <> +bool +ov_range::load_binary (std::istream& is, bool swap, + octave::mach_info::float_format fmt) +{ + return xload_binary (is, swap, fmt, m_range, true); +} + +template <> +bool +ov_range::load_binary (std::istream& is, bool swap, + octave::mach_info::float_format fmt) +{ + return xload_binary (is, swap, fmt, m_range, true); +} + +/* +%!test +%! a = b = 1:4; +%! sv_file = [tempname(), ".dat"]; +%! unwind_protect +%! save (sv_file, "a", "-binary"); +%! clear a; +%! load (sv_file); +%! assert (a, b); +%! unwind_protect_cleanup +%! unlink (sv_file); +%! end_unwind_protect + +%!test +%! a = b = uint8(5):-1:0; +%! sv_file = [tempname(), ".dat"]; +%! unwind_protect +%! save (sv_file, "a", "-binary"); +%! clear a; +%! load (sv_file); +%! assert (a, b); +%! unwind_protect_cleanup +%! unlink (sv_file); +%! end_unwind_protect +*/ + #if defined (HAVE_HDF5) // The following subroutines creates an HDF5 representation of the way @@ -775,36 +1002,50 @@ return type_id; } -#endif +template +static hid_t +hdf5_make_range_rev_type (hid_t num_type) +{ + hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (T) * 4); + + 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); + // FIXME: Storing "reverse" with the same width is inefficient. + H5Tinsert (type_id, "reverse", 3 * sizeof (T), num_type); + + return type_id; +} template bool -ov_range::save_hdf5 (octave_hdf5_id loc_id, const char *name, - bool /* save_as_floats */) +xsave_hdf5 (octave_hdf5_id loc_id, const char *name, + bool /* save_as_floats */, const octave::range& r, + const octave_hdf5_id h5_save_type, const bool with_reverse) { bool retval = false; -#if defined (HAVE_HDF5) - - hsize_t dimens[3]; + hsize_t dimens[3] = {0}; hid_t space_hid, type_hid, data_hid; space_hid = type_hid = data_hid = -1; space_hid = H5Screate_simple (0, dimens, nullptr); if (space_hid < 0) return false; - type_hid = hdf5_make_range_type (hdf5_save_type); + type_hid = with_reverse + ? hdf5_make_range_rev_type (h5_save_type) + : hdf5_make_range_type (h5_save_type); if (type_hid < 0) { H5Sclose (space_hid); return false; } -#if defined (HAVE_HDF5_18) +# if defined (HAVE_HDF5_18) data_hid = H5Dcreate (loc_id, name, type_hid, space_hid, octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else +# else data_hid = H5Dcreate (loc_id, name, type_hid, space_hid, octave_H5P_DEFAULT); -#endif +# endif if (data_hid < 0) { H5Sclose (space_hid); @@ -812,11 +1053,14 @@ return false; } - octave::range r = m_range; - T range_vals[3]; + T range_vals[4]; range_vals[0] = r.base (); - range_vals[1] = (r.increment () != T (0) ? r.limit () : r.numel ()); + if (r.increment () != T (0)) + range_vals[1] = r.limit (); + else + range_vals[1] = r.numel (); range_vals[2] = r.increment (); + range_vals[3] = r.reverse (); if (H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, octave_H5P_DEFAULT, range_vals) @@ -833,32 +1077,124 @@ H5Tclose (type_hid); H5Sclose (space_hid); + return retval; +} + +#endif + +template +bool +ov_range::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool save_as_floats) +{ +#if defined (HAVE_HDF5) + return xsave_hdf5 (loc_id, name, save_as_floats, m_range, hdf5_save_type, + false); #else octave_unused_parameter (loc_id); octave_unused_parameter (name); warn_save ("hdf5"); + + return false; #endif +} + +template <> +bool +ov_range::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool save_as_floats) +{ +#if defined (HAVE_HDF5) + return xsave_hdf5 (loc_id, name, save_as_floats, m_range, hdf5_save_type, + true); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + octave_unused_parameter (save_as_floats); + + warn_save ("hdf5"); + + return false; +#endif +} + +template <> +bool +ov_range::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool save_as_floats) +{ +#if defined (HAVE_HDF5) + return xsave_hdf5 (loc_id, name, save_as_floats, m_range, hdf5_save_type, + true); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + octave_unused_parameter (save_as_floats); + + warn_save ("hdf5"); - return retval; + return false; +#endif +} + +template <> +bool +ov_range::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool save_as_floats) +{ +#if defined (HAVE_HDF5) + return xsave_hdf5 (loc_id, name, save_as_floats, m_range, hdf5_save_type, + true); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + octave_unused_parameter (save_as_floats); + + warn_save ("hdf5"); + + return false; +#endif } +template <> +bool +ov_range::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool save_as_floats) +{ +#if defined (HAVE_HDF5) + return xsave_hdf5 (loc_id, name, save_as_floats, m_range, hdf5_save_type, + true); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + octave_unused_parameter (save_as_floats); + + warn_save ("hdf5"); + + return false; +#endif +} + +#if defined (HAVE_HDF5) + template bool -ov_range::load_hdf5 (octave_hdf5_id loc_id, const char *name) +xload_hdf5 (octave_hdf5_id loc_id, const char *name, octave::range& r, + const octave_hdf5_id h5_save_type, const bool with_reverse) { bool retval = false; -#if defined (HAVE_HDF5) - -#if defined (HAVE_HDF5_18) +# if defined (HAVE_HDF5_18) hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT); -#else +# else hid_t data_hid = H5Dopen (loc_id, name); -#endif +# endif hid_t type_hid = H5Dget_type (data_hid); - hid_t range_type = hdf5_make_range_type (hdf5_save_type); + hid_t range_type = with_reverse + ? hdf5_make_range_rev_type (h5_save_type) + : hdf5_make_range_type (h5_save_type); if (! hdf5_types_compatible (type_hid, range_type)) { @@ -878,7 +1214,7 @@ return false; } - T rangevals[3]; + T rangevals[4]; if (H5Dread (data_hid, range_type, octave_H5S_ALL, octave_H5S_ALL, octave_H5P_DEFAULT, rangevals) >= 0) @@ -887,12 +1223,14 @@ // Don't use OCTAVE_RANGE_NELEM attribute, just reconstruct the range. + bool rev = with_reverse ? static_cast (rangevals[3]) : false; + if (rangevals[2] != T (0)) - m_range = octave::range (rangevals[0], rangevals[1], rangevals[2]); + r = octave::range (rangevals[0], rangevals[2], rangevals[1], rev); else { octave_idx_type numel = static_cast (rangevals[1]); - m_range = octave::range::make_constant (rangevals[0], numel); + r = octave::range::make_constant (rangevals[0], numel, rev); } } @@ -900,16 +1238,117 @@ H5Sclose (space_hid); H5Dclose (data_hid); + return retval; +} + +#endif + +template +bool +ov_range::load_hdf5 (octave_hdf5_id loc_id, const char *name) +{ +#if defined (HAVE_HDF5) + return xload_hdf5 (loc_id, name, m_range, hdf5_save_type, false); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + warn_load ("hdf5"); + + return false; +#endif +} + +template <> +bool +ov_range::load_hdf5 (octave_hdf5_id loc_id, const char *name) +{ +#if defined (HAVE_HDF5) + return xload_hdf5 (loc_id, name, m_range, hdf5_save_type, true); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + warn_load ("hdf5"); + + return false; +#endif +} + +template <> +bool +ov_range::load_hdf5 (octave_hdf5_id loc_id, const char *name) +{ +#if defined (HAVE_HDF5) + return xload_hdf5 (loc_id, name, m_range, hdf5_save_type, true); #else octave_unused_parameter (loc_id); octave_unused_parameter (name); warn_load ("hdf5"); + + return false; #endif +} + +template <> +bool +ov_range::load_hdf5 (octave_hdf5_id loc_id, const char *name) +{ +#if defined (HAVE_HDF5) + return xload_hdf5 (loc_id, name, m_range, hdf5_save_type, true); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + warn_load ("hdf5"); + + return false; +#endif +} + +template <> +bool +ov_range::load_hdf5 (octave_hdf5_id loc_id, const char *name) +{ +#if defined (HAVE_HDF5) + return xload_hdf5 (loc_id, name, m_range, hdf5_save_type, true); +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); - return retval; + warn_load ("hdf5"); + + return false; +#endif } +/* +%!testif HAVE_HDF5 +%! a = b = 1:4; +%! sv_file = [tempname(), ".h5"]; +%! unwind_protect +%! save (sv_file, "a", "-hdf5"); +%! clear a; +%! load (sv_file); +%! assert (a, b); +%! unwind_protect_cleanup +%! unlink (sv_file); +%! end_unwind_protect + +%!testif HAVE_HDF5 +%! a = b = uint8(5):-1:0; +%! sv_file = [tempname(), ".h5"]; +%! unwind_protect +%! save (sv_file, "a", "-hdf5"); +%! clear a; +%! load (sv_file); +%! assert (a, b); +%! unwind_protect_cleanup +%! unlink (sv_file); +%! end_unwind_protect +*/ + template mxArray * ov_range::as_mxArray (bool interleaved) const diff -r 391b35ef8b24 -r 4736bc8e9804 libinterp/octave-value/ov.cc --- a/libinterp/octave-value/ov.cc Thu Dec 02 08:03:39 2021 -0500 +++ b/libinterp/octave-value/ov.cc Tue Nov 30 18:19:20 2021 +0100 @@ -3031,17 +3031,24 @@ if (base.isempty () || increment.isempty () || limit.isempty ()) return octave_value (range (), for_cmd_expr); + bool reverse = (base.is_uint8_type () || base.is_uint16_type () + || base.is_uint32_type () || base.is_uint64_type () + || limit.is_uint8_type () || limit.is_uint16_type () + || limit.is_uint32_type () || limit.is_uint64_type ()) + && increment.scalar_value () < 0; + check_colon_operand (base, "lower bound"); - check_colon_operand (increment, "increment"); + check_colon_operand ((reverse ? -increment : increment), "increment"); check_colon_operand (limit, "upper bound"); T base_val = octave_value_extract (base); - T increment_val = octave_value_extract (increment); + T increment_val = octave_value_extract (reverse ? -increment + : increment); T limit_val = octave_value_extract (limit); - range r (base_val, increment_val, limit_val); + range r (base_val, increment_val, limit_val, reverse); return octave_value (r, for_cmd_expr); } diff -r 391b35ef8b24 -r 4736bc8e9804 liboctave/array/Range.cc --- a/liboctave/array/Range.cc Thu Dec 02 08:03:39 2021 -0500 +++ b/liboctave/array/Range.cc Tue Nov 30 18:19:20 2021 +0100 @@ -192,7 +192,8 @@ template void - xinit (T base, T limit, T inc, T& final_val, octave_idx_type& nel) + xinit (T base, T limit, T inc, const bool reverse, T& final_val, + octave_idx_type& nel) { // Catch obvious NaN ranges. if (math::isnan (base) || math::isnan (limit) || math::isnan (inc)) @@ -202,6 +203,10 @@ return; } + // Floating point numbers are always signed + if (reverse) + inc = -inc; + // Catch empty ranges. if (inc == 0 || (limit < base && inc > 0) @@ -254,9 +259,9 @@ template void - xinit (const octave_int& base, const octave_int& limit, - const octave_int& inc, octave_int& final_val, - octave_idx_type& nel) + xinit (const octave_int base, const octave_int limit, + const octave_int inc, const bool reverse, + octave_int& final_val, octave_idx_type& nel) { // We need an integer division that is truncating decimals instead // of rounding. So, use underlying C++ types instead of @@ -264,14 +269,28 @@ // FIXME: The numerator might underflow or overflow. Add checks for // that. + if (reverse) + { + nel = ((inc == octave_int (0) + || (limit > base && inc > octave_int (0)) + || (limit < base && inc < octave_int (0))) + ? 0 + : (base.value () - limit.value () + inc.value ()) + / inc.value ()); - nel = ((inc == octave_int (0) - || (limit > base && inc < octave_int (0)) - || (limit < base && inc > octave_int (0))) - ? 0 - : (limit.value () - base.value () + inc.value ()) / inc.value ()); + final_val = base - (nel - 1) * inc; + } + else + { + nel = ((inc == octave_int (0) + || (limit > base && inc < octave_int (0)) + || (limit < base && inc > octave_int (0))) + ? 0 + : (limit.value () - base.value () + inc.value ()) + / inc.value ()); - final_val = base + (nel - 1) * inc; + final_val = base + (nel - 1) * inc; + } } template @@ -299,70 +318,70 @@ void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> void range::init (void) { - xinit (m_base, m_limit, m_increment, m_final, m_numel); + xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel); } template <> diff -r 391b35ef8b24 -r 4736bc8e9804 liboctave/array/Range.h --- a/liboctave/array/Range.h Thu Dec 02 08:03:39 2021 -0500 +++ b/liboctave/array/Range.h Tue Nov 30 18:19:20 2021 +0100 @@ -47,7 +47,8 @@ public: range (void) - : m_base (0), m_increment (0), m_limit (0), m_final (0), m_numel (0) + : m_base (0), m_increment (0), m_limit (0), m_final (0), m_numel (0), + m_reverse (false) { } // LIMIT is an upper limit and may be outside the range of actual @@ -55,15 +56,17 @@ // to attempt to capture limit in the set of values if it is "close" // to the value of base + a multiple of the increment. - range (const T& base, const T& increment, const T& limit) + range (const T& base, const T& increment, const T& limit, + const bool& reverse = false) : m_base (base), m_increment (increment), m_limit (limit), - m_final (), m_numel () + m_final (), m_numel (), m_reverse (reverse) { init (); } range (const T& base, const T& limit) - : m_base (base), m_increment (1), m_limit (limit), m_final (), m_numel () + : m_base (base), m_increment (1), m_limit (limit), m_final (), m_numel (), + m_reverse (false) { init (); } @@ -79,44 +82,47 @@ // FIXME: Is there a way to limit this to T == double? range (const T& base, const T& increment, const T& limit, - octave_idx_type numel) + octave_idx_type numel, const bool& reverse = false) : m_base (base), m_increment (increment), m_limit (limit), - m_final (limit), m_numel (numel) + m_final (limit), m_numel (numel), m_reverse (reverse) { } range (const T& base, const T& increment, const T& limit, - const T& final, octave_idx_type numel) + const T& final, octave_idx_type numel, const bool& reverse = false) : m_base (base), m_increment (increment), m_limit (limit), - m_final (final), m_numel (numel) + m_final (final), m_numel (numel), m_reverse (reverse) { } // We don't use a constructor for this because it will conflict with // range (base, limit) when T is octave_idx_type. - static range make_constant (const T& base, octave_idx_type numel) + static range make_constant (const T& base, octave_idx_type numel, + const bool& reverse = false) { // We could just make this constructor public, but it allows // inconsistent ranges to be constructed. And it is probably much // clearer to see "make_constant" instead of puzzling over the // purpose of this strange constructor form. - return range (base, T (), base, numel); + return range (base, T (), base, numel, reverse); } // We don't use a constructor for this because it will conflict with // range (base, limit, increment) when T is octave_idx_type. static range make_n_element_range (const T& base, const T& increment, - octave_idx_type numel) + octave_idx_type numel, + bool reverse = false) { // We could just make this constructor public, but it allows // inconsistent ranges to be constructed. And it is probably much // clearer to see "make_constant" instead of puzzling over the // purpose of this strange constructor form. - T final_val = base + (numel - 1) * increment; + T final_val = (reverse ? base - (numel - 1) * increment + : base + (numel - 1) * increment); - return range (base, increment, final_val, numel); + return range (base, increment, final_val, numel, reverse); } range (const range&) = default; @@ -128,20 +134,23 @@ T base (void) const { return m_base; } T increment (void) const { return m_increment; } T limit (void) const { return m_limit; } + bool reverse (void) const { return m_reverse; } T final_value (void) const { return m_final; } T min (void) const { return (m_numel > 0 - ? m_increment > T (0) ? base () : final_value () + ? ((m_reverse ? m_increment > T (0) + : m_increment > T (0)) ? base () : final_value ()) : T (0)); } T max (void) const { return (m_numel > 0 - ? m_increment > T (0) ? final_value () : base () + ? ((m_reverse ? m_increment < T (0) + : m_increment > T (0)) ? final_value () : base ()) : T (0)); } @@ -170,12 +179,14 @@ sortmode issorted (sortmode mode = ASCENDING) const { - if (m_numel > 1 && m_increment > T (0)) - mode = (mode == DESCENDING) ? UNSORTED : ASCENDING; - else if (m_numel > 1 && m_increment < T (0)) - mode = (mode == ASCENDING) ? UNSORTED : DESCENDING; + if (m_numel > 1 && (m_reverse ? m_increment < T (0) + : m_increment > T (0))) + mode = ((mode == DESCENDING) ? UNSORTED : ASCENDING); + else if (m_numel > 1 && (m_reverse ? m_increment > T (0) + : m_increment < T (0))) + mode = ((mode == ASCENDING) ? UNSORTED : DESCENDING); else - mode = (mode == UNSORTED) ? ASCENDING : mode; + mode = ((mode == UNSORTED) ? ASCENDING : mode); return mode; } @@ -191,9 +202,10 @@ if (i == 0) // Required for proper NaN handling. - return m_numel == 1 ? final_value () : m_base; + return (m_numel == 1 ? final_value () : m_base); else if (i < m_numel - 1) - return m_base + T (i) * m_increment; + return (m_reverse ? m_base + T (i) * m_increment + : m_base + T (i) * m_increment); else return final_value (); } @@ -211,9 +223,10 @@ { if (i == 0) // Required for proper NaN handling. - return m_numel == 1 ? final_value () : m_base; + return (m_numel == 1 ? final_value () : m_base); else if (i < m_numel - 1) - return m_base + T (i) * m_increment; + return (m_reverse ? m_base - T (i) * m_increment + : m_base + T (i) * m_increment); else return final_value (); } @@ -266,9 +279,10 @@ { if (i == 0) // Required for proper NaN handling. - *array++ = m_numel == 0 ? m_final : m_base; + *array++ = (m_numel == 0 ? m_final : m_base); else if (i < m_numel - 1) - *array++ = m_base + T (i) * m_increment; + *array++ = (m_reverse ? m_base - T (i) * m_increment + : m_base + T (i) * m_increment); else *array++ = m_final; }); @@ -297,8 +311,12 @@ // E.g, -0 would otherwise become +0 in the loop (-0 + 0*increment). retval(0) = m_base; - for (octave_idx_type i = 1; i < nel - 1; i++) - retval.xelem (i) = m_base + i * m_increment; + if (m_reverse) + for (octave_idx_type i = 1; i < nel - 1; i++) + retval.xelem (i) = m_base - i * m_increment; + else + for (octave_idx_type i = 1; i < nel - 1; i++) + retval.xelem (i) = m_base + i * m_increment; retval.xelem (nel - 1) = final_value (); } @@ -313,6 +331,7 @@ T m_limit; T m_final; octave_idx_type m_numel; + bool m_reverse; // Setting the number of elements to zero when the increment is zero // is intentional and matches the behavior of Matlab's colon @@ -323,13 +342,26 @@ void init (void) { - m_numel = ((m_increment == T (0) - || (m_limit > m_base && m_increment < T (0)) - || (m_limit < m_base && m_increment > T (0))) - ? T (0) - : (m_limit - m_base + m_increment) / m_increment); + if (m_reverse) + { + m_numel = ((m_increment == T (0) + || (m_limit > m_base && m_increment > T (0)) + || (m_limit < m_base && m_increment < T (0))) + ? T (0) + : (m_base - m_limit - m_increment) / m_increment); - m_final = m_base + (m_numel - 1) * m_increment; + m_final = m_base - (m_numel - 1) * m_increment; + } + else + { + m_numel = ((m_increment == T (0) + || (m_limit > m_base && m_increment < T (0)) + || (m_limit < m_base && m_increment > T (0))) + ? T (0) + : (m_limit - m_base + m_increment) / m_increment); + + m_final = m_base + (m_numel - 1) * m_increment; + } } }; diff -r 391b35ef8b24 -r 4736bc8e9804 test/range.tst --- a/test/range.tst Thu Dec 02 08:03:39 2021 -0500 +++ b/test/range.tst Tue Nov 30 18:19:20 2021 +0100 @@ -491,9 +491,10 @@ %!error (1.5:uint8(1):5) %!error (-1:uint8(1):5) %!error (uint8(1):1.5:5) -%!error (uint8(1):-1:5) +%!error (uint8(1):-256:5) %!error (uint8(1):1:5.5) %!error (uint8(1):1:256) +%!error (uint8(1):-1:-6) ## Extreme integer values. %!test <61132> @@ -549,3 +550,15 @@ %! assert ((int_types{i_type} (95) : -6 : 0)([1,end]), ... %! [int_types{i_type}(95), int_types{i_type}(5)]); %! endfor + +## Descending ranges, unsigned +%!test <*61132> +%! int_types = {@uint8, @uint16, @uint32, @uint64}; +%! for i_type = 1:numel (int_types) +%! for i_start = 0:4 +%! assert ((int_types{i_type} (100-i_start) : -6 : 0)([1,end]), ... +%! [int_types{i_type}(100-i_start), int_types{i_type}(4-i_start)]); +%! endfor +%! assert ((int_types{i_type} (95) : -6 : 0)([1,end]), ... +%! [int_types{i_type}(95), int_types{i_type}(5)]); +%! endfor