GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
ov-legacy-range.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2025 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include <istream>
31#include <ostream>
32#include <sstream>
33
34#include "Range.h"
35#include "lo-ieee.h"
36#include "lo-utils.h"
37
38#include "variables.h"
39#include "error.h"
40#include "ovl.h"
41#include "oct-hdf5.h"
42#include "ov-legacy-range.h"
43#include "ov-range.h"
44#include "ov-re-mat.h"
45#include "ov-scalar.h"
46#include "pr-output.h"
47
48#include "byte-swap.h"
49#include "ls-ascii-helper.h"
50#include "ls-hdf5.h"
51#include "ls-utils.h"
52
53class Range
54{
55public:
56
57 Range ()
58 : m_base (0), m_limit (0), m_inc (0), m_numel (0)
59 { }
60
61 // Assume range is already properly constructed, so just copy internal
62 // values. However, we set LIMIT to the computed final value because
63 // that mimics the behavior of the other Range class constructors that
64 // reset limit to the computed final value.
65
66 Range (const octave::range<double>& r)
67 : m_base (r.base ()), m_limit (r.final_value ()), m_inc (r.increment ()),
68 m_numel (r.numel ())
69 { }
70
71 Range (const Range& r) = default;
72
73 Range& operator = (const Range& r) = default;
74
75 ~Range () = default;
76
77 Range (double b, double l)
78 : m_base (b), m_limit (l), m_inc (1), m_numel (numel_internal ())
79 {
80 if (! octave::math::isinf (m_limit))
81 m_limit = limit_internal ();
82 }
83
84 Range (double b, double l, double i)
85 : m_base (b), m_limit (l), m_inc (i), m_numel (numel_internal ())
86 {
87 if (! octave::math::isinf (m_limit))
88 m_limit = limit_internal ();
89 }
90
91 // The range has a finite number of elements.
92 bool ok () const
93 {
94 return (octave::math::isfinite (m_limit)
95 && (m_numel >= 0 || m_numel == -2));
96 }
97
98 double base () const { return m_base; }
99 double limit () const { return m_limit; }
100 double increment () const { return m_inc; }
101
102 octave_idx_type numel () const { return m_numel; }
103
104 bool all_elements_are_ints () const;
105
106 Matrix matrix_value () const;
107
108 double min () const;
109 double max () const;
110
111private:
112
113 double m_base;
114 double m_limit;
115 double m_inc;
116
117 octave_idx_type m_numel;
118
119 octave_idx_type numel_internal () const;
120
121 double limit_internal () const;
122
123 void init ();
124};
125
126bool
127Range::all_elements_are_ints () const
128{
129 // If the base and increment are ints, the final value in the range will also
130 // be an integer, even if the limit is not. If there is one or fewer
131 // elements only the base needs to be an integer.
132
133 return (! (octave::math::isnan (m_base) || octave::math::isnan (m_inc))
134 && (octave::math::nint_big (m_base) == m_base || m_numel < 1)
135 && (octave::math::nint_big (m_inc) == m_inc || m_numel <= 1));
136}
137
138Matrix
139Range::matrix_value () const
140{
141 Matrix retval (1, m_numel);
142
143 if (m_numel > 0)
144 {
145 // The first element must always be *exactly* the base.
146 // E.g, -0 would otherwise become +0 in the loop (-0 + 0*increment).
147 retval(0) = m_base;
148
149 double b = m_base;
150 double increment = m_inc;
151 for (octave_idx_type i = 1; i < m_numel - 1; i++)
152 retval.xelem (i) = b + i * increment;
153
154 retval.xelem (m_numel - 1) = m_limit;
155 }
156
157 return retval;
158}
159
160// NOTE: max and min only return useful values if numel > 0.
161// do_minmax_body() in max.cc avoids calling Range::min/max if numel == 0.
162
163double
164Range::min () const
165{
166 double retval = 0.0;
167 if (m_numel > 0)
168 {
169 if (m_inc > 0)
170 retval = m_base;
171 else
172 {
173 retval = m_base + (m_numel - 1) * m_inc;
174
175 // Require '<=' test. See note in max ().
176 if (retval <= m_limit)
177 retval = m_limit;
178 }
179
180 }
181 return retval;
182}
183
184double
185Range::max () const
186{
187 double retval = 0.0;
188 if (m_numel > 0)
189 {
190 if (m_inc > 0)
191 {
192 retval = m_base + (m_numel - 1) * m_inc;
193
194 // On some machines (x86 with extended precision floating point
195 // arithmetic, for example) it is possible that we can overshoot the
196 // limit by approximately the machine precision even though we were
197 // very careful in our calculation of the number of elements.
198 // Therefore, we clip the result to the limit if it overshoots.
199 // The test also includes equality (>= m_limit) to have expressions
200 // such as -5:1:-0 result in a -0 endpoint.
201 if (retval >= m_limit)
202 retval = m_limit;
203 }
204 else
205 retval = m_base;
206 }
207 return retval;
208}
209
210// C See Knuth, Art Of Computer Programming, Vol. 1, Problem 1.2.4-5.
211// C
212// C===Tolerant FLOOR function.
213// C
214// C X - is given as a Double Precision argument to be operated on.
215// C It is assumed that X is represented with M mantissa bits.
216// C CT - is given as a Comparison Tolerance such that
217// C 0.LT.CT.LE.3-SQRT(5)/2. If the relative difference between
218// C X and A whole number is less than CT, then TFLOOR is
219// C returned as this whole number. By treating the
220// C floating-point numbers as a finite ordered set note that
221// C the heuristic EPS=2.**(-(M-1)) and CT=3*EPS causes
222// C arguments of TFLOOR/TCEIL to be treated as whole numbers
223// C if they are exactly whole numbers or are immediately
224// C adjacent to whole number representations. Since EPS, the
225// C "distance" between floating-point numbers on the unit
226// C interval, and M, the number of bits in X'S mantissa, exist
227// C on every floating-point computer, TFLOOR/TCEIL are
228// C consistently definable on every floating-point computer.
229// C
230// C For more information see the following references:
231// C (1) P. E. Hagerty, "More On Fuzzy Floor And Ceiling," APL QUOTE
232// C QUAD 8(4):20-24, June 1978. Note that TFLOOR=FL5.
233// C (2) L. M. Breed, "Definitions For Fuzzy Floor And Ceiling", APL
234// C QUOTE QUAD 8(3):16-23, March 1978. This paper cites FL1 through
235// C FL5, the history of five years of evolutionary development of
236// C FL5 - the seven lines of code below - by open collaboration
237// C and corroboration of the mathematical-computing community.
238// C
239// C Penn State University Center for Academic Computing
240// C H. D. Knoble - August, 1978.
241
242static inline double
243tfloor (double x, double ct)
244{
245// C---------FLOOR(X) is the largest integer algebraically less than
246// C or equal to X; that is, the unfuzzy FLOOR function.
247
248// DINT (X) = X - DMOD (X, 1.0);
249// FLOOR (X) = DINT (X) - DMOD (2.0 + DSIGN (1.0, X), 3.0);
250
251// C---------Hagerty's FL5 function follows...
252
253 double q = 1.0;
254
255 if (x < 0.0)
256 q = 1.0 - ct;
257
258 double rmax = q / (2.0 - ct);
259
260 double t1 = 1.0 + std::floor (x);
261 t1 = (ct / q) * (t1 < 0.0 ? -t1 : t1);
262 t1 = (rmax < t1 ? rmax : t1);
263 t1 = (ct > t1 ? ct : t1);
264 t1 = std::floor (x + t1);
265
266 if (x <= 0.0 || (t1 - x) < rmax)
267 return t1;
268 else
269 return t1 - 1.0;
270}
271
272static inline bool
273teq (double u, double v,
274 double ct = 3.0 * std::numeric_limits<double>::epsilon ())
275{
276 double tu = std::abs (u);
277 double tv = std::abs (v);
278
279 return std::abs (u - v) < ((tu > tv ? tu : tv) * ct);
280}
281
283Range::numel_internal () const
284{
285 octave_idx_type retval = -1;
286
287 if (! octave::math::isfinite (m_base) || ! octave::math::isfinite (m_inc)
288 || octave::math::isnan (m_limit))
289 retval = -2;
290 else if (octave::math::isinf (m_limit)
291 && ((m_inc > 0 && m_limit > 0)
292 || (m_inc < 0 && m_limit < 0)))
293 retval = std::numeric_limits<octave_idx_type>::max () - 1;
294 else if (m_inc == 0
295 || (m_limit > m_base && m_inc < 0)
296 || (m_limit < m_base && m_inc > 0))
297 {
298 retval = 0;
299 }
300 else
301 {
302 double ct = 3.0 * std::numeric_limits<double>::epsilon ();
303
304 double tmp = tfloor ((m_limit - m_base + m_inc) / m_inc, ct);
305
306 octave_idx_type n_elt = (tmp > 0.0
307 ? static_cast<octave_idx_type> (tmp) : 0);
308
309 // If the final element that we would compute for the range is equal to
310 // the limit of the range, or is an adjacent floating point number,
311 // accept it. Otherwise, try a range with one fewer element. If that
312 // fails, try again with one more element.
313 //
314 // I'm not sure this is very good, but it seems to work better than just
315 // using tfloor as above. For example, without it, the expression
316 // 1.8:0.05:1.9 fails to produce the expected result of [1.8, 1.85, 1.9].
317
318 if (! teq (m_base + (n_elt - 1) * m_inc, m_limit))
319 {
320 if (teq (m_base + (n_elt - 2) * m_inc, m_limit))
321 n_elt--;
322 else if (teq (m_base + n_elt * m_inc, m_limit))
323 n_elt++;
324 }
325
326 retval = ((n_elt < std::numeric_limits<octave_idx_type>::max ())
327 ? n_elt : -1);
328 }
329
330 return retval;
331}
332
333double
334Range::limit_internal () const
335{
336 double new_limit = m_inc > 0 ? max () : min ();
337
338 // If result must be an integer then force the new_limit to be one.
339 if (all_elements_are_ints ())
340 new_limit = std::round (new_limit);
341
342 return new_limit;
343}
344
345void
346Range::init ()
347{
348 m_numel = numel_internal ();
349
350 if (! octave::math::isinf (m_limit))
351 m_limit = limit_internal ();
352}
353
355
357 : octave_base_value (), m_range (new Range ()) { }
358
360 : octave_base_value (), m_range (new Range (r))
361{
362 if (m_range->numel () < 0 && m_range->numel () != -2)
363 error ("invalid range");
364}
365
367 : octave_base_value (r), m_range ()
368{
369 m_range.reset (new Range (*(r.m_range)));
370}
371
372static octave_base_value *
373default_numeric_conversion_function (const octave_base_value& a)
374{
375 const octave_legacy_range& v = dynamic_cast<const octave_legacy_range&> (a);
376
377 return new octave_matrix (v.matrix_value ());
378}
379
382{
383 return octave_base_value::type_conv_info (default_numeric_conversion_function,
385}
386
389{
390 octave_base_value *retval = nullptr;
391
392 switch (m_range->numel ())
393 {
394 case 1:
395 retval = new octave_scalar (m_range->base ());
396 break;
397
398 case 0:
399 retval = new octave_matrix (Matrix (1, 0));
400 break;
401
402 case -2:
403 retval = new octave_matrix (m_range->matrix_value ());
404 break;
405
406 default:
407 {
408 if (m_range->increment () == 0)
409 retval = new octave_matrix (m_range->matrix_value ());
410 else
411 retval = new octave_range
412 (octave::range<double> (m_range->base (), m_range->increment (),
413 m_range->limit (), m_range->numel ()));
414 }
415 break;
416 }
417
418 return retval;
419}
420
421// Skip white space and comments on stream IS.
422
423static void
424skip_comments (std::istream& is)
425{
426 char c = '\0';
427 while (is.get (c))
428 {
429 if (c == ' ' || c == '\t' || c == '\n')
430 ; // Skip whitespace on way to beginning of next line.
431 else
432 break;
433 }
434
435 octave::skip_until_newline (is, false);
436}
437
438bool
440{
441 // # base, limit, range comment added by save ().
442 skip_comments (is);
443
444 double base, limit, inc;
445 is >> base >> limit >> inc;
446
447 if (! is)
448 error ("load: failed to load range constant");
449
450 if (inc != 0)
451 m_range.reset (new Range (base, limit, inc));
452 else
453 m_range.reset (new Range (base, inc, static_cast<octave_idx_type> (limit)));
454
455 return true;
456}
457
458bool
459octave_legacy_range::load_binary (std::istream& is, bool swap,
460 octave::mach_info::float_format /* fmt */)
461{
462 char tmp;
463 if (! is.read (reinterpret_cast<char *> (&tmp), 1))
464 return false;
465 double bas, lim, inc;
466 if (! is.read (reinterpret_cast<char *> (&bas), 8))
467 return false;
468 if (swap)
469 swap_bytes<8> (&bas);
470 if (! is.read (reinterpret_cast<char *> (&lim), 8))
471 return false;
472 if (swap)
473 swap_bytes<8> (&lim);
474 if (! is.read (reinterpret_cast<char *> (&inc), 8))
475 return false;
476 if (swap)
477 swap_bytes<8> (&inc);
478 if (inc != 0)
479 m_range.reset (new Range (bas, lim, inc));
480 else
481 m_range.reset (new Range (bas, inc, static_cast<octave_idx_type> (lim)));
482
483 return true;
484}
485
486#if defined (HAVE_HDF5)
487
488// The following subroutines creates an HDF5 representation of the way
489// we will store Octave range types (triplets of floating-point numbers).
490// NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
491// H5T_NATIVE_DOUBLE to save as 'double'). Note that any necessary
492// conversions are handled automatically by HDF5.
493
494static hid_t
495hdf5_make_range_type (hid_t num_type)
496{
497 hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
498
499 H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
500 H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
501 H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
502
503 return type_id;
504}
505
506#endif
507
508bool
510{
511 bool retval = false;
512
513#if defined (HAVE_HDF5)
514
515#if defined (HAVE_HDF5_18)
516 hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
517#else
518 hid_t data_hid = H5Dopen (loc_id, name);
519#endif
520 hid_t type_hid = H5Dget_type (data_hid);
521
522 hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
523
524 if (! hdf5_types_compatible (type_hid, range_type))
525 {
526 H5Tclose (range_type);
527 H5Dclose (data_hid);
528 return false;
529 }
530
531 hid_t space_hid = H5Dget_space (data_hid);
532 hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
533
534 if (rank != 0)
535 {
536 H5Tclose (range_type);
537 H5Sclose (space_hid);
538 H5Dclose (data_hid);
539 return false;
540 }
541
542 double rangevals[3];
543 if (H5Dread (data_hid, range_type, octave_H5S_ALL, octave_H5S_ALL,
544 octave_H5P_DEFAULT, rangevals)
545 >= 0)
546 {
547 retval = true;
548 octave_idx_type nel;
550 "OCTAVE_RANGE_NELEM", &nel))
551 m_range.reset (new Range (rangevals[0], rangevals[2], nel));
552 else
553 {
554 if (rangevals[2] != 0)
555 m_range.reset (new Range (rangevals[0], rangevals[1], rangevals[2]));
556 else
557 m_range.reset (new Range (rangevals[0], rangevals[2],
558 static_cast<octave_idx_type> (rangevals[1])));
559 }
560 }
561
562 H5Tclose (range_type);
563 H5Sclose (space_hid);
564 H5Dclose (data_hid);
565
566#else
567 octave_unused_parameter (loc_id);
568 octave_unused_parameter (name);
569
570 warn_load ("hdf5");
571#endif
572
573 return retval;
574}
void swap_bytes< 8 >(void *ptr)
Definition byte-swap.h:71
charNDArray max(char d, const charNDArray &m)
Definition chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition chNDArray.cc:207
void warn_load(const char *type) const
Definition ov-base.cc:1152
virtual Matrix matrix_value(bool=false) const
Definition ov-base.cc:597
bool load_ascii(std::istream &is)
bool load_binary(std::istream &is, bool swap, octave::mach_info::float_format fmt)
bool load_hdf5(octave_hdf5_id loc_id, const char *name)
octave_base_value * try_narrowing_conversion()
type_conv_info numeric_conversion_function() const
static int static_type_id()
Definition ov-re-mat.h:249
const octave_hdf5_id octave_H5P_DEFAULT
const octave_hdf5_id octave_H5S_ALL
void error(const char *fmt,...)
Definition error.cc:1003
F77_RET_T const F77_DBLE * x
bool hdf5_get_scalar_attr(octave_hdf5_id loc_id, octave_hdf5_id type_id, const char *attr_name, void *buf)
Definition ls-hdf5.cc:352
bool hdf5_types_compatible(octave_hdf5_id t1, octave_hdf5_id t2)
Definition ls-hdf5.cc:274
int64_t octave_hdf5_id
#define H5T_NATIVE_IDX
Definition oct-hdf5.h:42
T::size_type numel(const T &str)
Definition oct-string.cc:74
#define DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(t, n, c)
Definition ov-base.h:246
octave_double_range octave_range
Definition ov-range.h:572