GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
Range.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1993-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 <cmath>
31
32#include <istream>
33#include <limits>
34#include <ostream>
35
36#include "Array-util.h"
37#include "Range.h"
38#include "lo-error.h"
39#include "lo-mappers.h"
40#include "lo-utils.h"
41
43
44template <typename T>
45T xtfloor (T x, T ct)
46{
47 // C---------FLOOR(X) is the largest integer algebraically less than
48 // C or equal to X; that is, the unfuzzy FLOOR function.
49
50 // DINT (X) = X - DMOD (X, 1.0);
51 // FLOOR (X) = DINT (X) - DMOD (2.0 + DSIGN (1.0, X), 3.0);
52
53 // C---------Hagerty's FL5 function follows...
54
55 T q = 1;
56
57 if (x < 0)
58 q = 1 - ct;
59
60 T rmax = q / (2 - ct);
61
62 T t1 = 1 + std::floor (x);
63 t1 = (ct / q) * (t1 < 0 ? -t1 : t1);
64 t1 = (rmax < t1 ? rmax : t1);
65 t1 = (ct > t1 ? ct : t1);
66 t1 = std::floor (x + t1);
67
68 if (x <= 0 || (t1 - x) < rmax)
69 return t1;
70 else
71 return t1 - 1;
72}
73
74template <typename T>
75bool
76xteq (T u, T v, T ct = 3 * std::numeric_limits<T>::epsilon ())
77{
78 T tu = std::abs (u);
79 T tv = std::abs (v);
80
81 return std::abs (u - v) < ((tu > tv ? tu : tv) * ct);
82}
83
84template <typename T>
86xnumel_internal (T base, T limit, T inc)
87{
88 octave_idx_type retval = -1;
89 if (! math::isfinite (base) || ! math::isfinite (inc)
90 || math::isnan (limit))
91 retval = -2;
92 else if (math::isinf (limit)
93 && ((inc > 0 && limit > 0)
94 || (inc < 0 && limit < 0)))
95 retval = std::numeric_limits<octave_idx_type>::max () - 1;
96 else if (inc == 0
97 || (limit > base && inc < 0)
98 || (limit < base && inc > 0))
99 {
100 retval = 0;
101 }
102 else
103 {
104 T ct = 3 * std::numeric_limits<T>::epsilon ();
105
106 T tmp = xtfloor ((limit - base + inc) / inc, ct);
107
108 octave_idx_type n_elt
109 = (tmp > 0 ? static_cast<octave_idx_type> (tmp) : 0);
110
111 // If the final element that we would compute for the range is
112 // equal to the limit of the range, or is an adjacent floating
113 // point number, accept it. Otherwise, try a range with one
114 // fewer element. If that fails, try again with one more
115 // element.
116 //
117 // I'm not sure this is very good, but it seems to work better
118 // than just using tfloor as above. For example, without it,
119 // the expression 1.8:0.05:1.9 fails to produce the expected
120 // result of [1.8, 1.85, 1.9].
121
122 if (! xteq (base + (n_elt - 1) * inc, limit))
123 {
124 if (xteq (base + (n_elt - 2) * inc, limit))
125 n_elt--;
126 else if (xteq (base + n_elt * inc, limit))
127 n_elt++;
128 }
129
130 retval = (n_elt < std::numeric_limits<octave_idx_type>::max () - 1
131 ? n_elt : -1);
132 }
133
134 return retval;
135}
136
137template <typename T>
138bool
139xall_elements_are_ints (T base, T inc, T final_val, octave_idx_type nel)
140{
141 // If the range is empty or NaN then there are no elements so there
142 // can be no int elements.
143 if (nel == 0 || math::isnan (final_val))
144 return false;
145
146 // If the base and increment are ints, all elements will be
147 // integers.
148 if (math::nint_big (base) == base && math::nint_big (inc) == inc)
149 return true;
150
151 // If the range has only one element, then the base needs to be an
152 // integer.
153 if (nel == 1 && math::nint_big (base))
154 return true;
155
156 return false;
157}
158
159template <typename T>
160T
161xfinal_value (T base, T limit, T inc, octave_idx_type nel)
162{
163 T retval = T (0);
164
165 if (nel <= 1)
166 return base;
167
168 // If increment is 0, then numel should also be zero.
169
170 retval = base + (nel - 1) * inc;
171
172 // On some machines (x86 with extended precision floating point
173 // arithmetic, for example) it is possible that we can overshoot
174 // the limit by approximately the machine precision even though
175 // we were very careful in our calculation of the number of
176 // elements. Therefore, we clip the result to the limit if it
177 // overshoots.
178
179 // NOTE: The test also includes equality (>= limit) to have
180 // expressions such as -5:1:-0 result in a -0 endpoint.
181
182 if ((inc > T (0) && retval >= limit) || (inc < T (0) && retval <= limit))
183 retval = limit;
184
185 // If all elements are integers, then ensure the final value is.
186 // Note that we pass the preliminary computed final value to
187 // xall_elements_are_ints, but it only checks whether that value is
188 // NaN.
189
190 if (xall_elements_are_ints (base, inc, retval, nel))
191 retval = std::round (retval);
192
193 return retval;
194}
195
196template <typename T>
197void
198xinit (T base, T limit, T inc, bool reverse, T& final_val,
199 octave_idx_type& nel)
200{
201 // Catch obvious NaN ranges.
202 if (math::isnan (base) || math::isnan (limit) || math::isnan (inc))
203 {
204 final_val = numeric_limits<T>::NaN ();
205 nel = 1;
206 return;
207 }
208
209 // Floating point numbers are always signed
210 if (reverse)
211 inc = -inc;
212
213 // Catch empty ranges.
214 if (inc == 0
215 || (limit < base && inc > 0)
216 || (limit > base && inc < 0))
217 {
218 nel = 0;
219 return;
220 }
221
222 // The following case also catches Inf values for increment when
223 // there will be only one element.
224
225 if ((limit <= base && base + inc < limit)
226 || (limit >= base && base + inc > limit))
227 {
228 final_val = base;
229 nel = 1;
230 return;
231 }
232
233 // Any other calculations with Inf will give us either a NaN range
234 // or an infinite nember of elements.
235
236 T dnel = (limit - base) / inc;
237 if (math::isnan (dnel))
238 {
239 nel = 1;
240 final_val = numeric_limits<T>::NaN ();
241 return;
242 }
243
244 if (dnel > 0 && math::isinf (dnel))
245 {
246 // FIXME: Should this be an immediate error?
247 nel = std::numeric_limits<octave_idx_type>::max ();
248
249 // FIXME: Will this do the right thing in all cases?
250 final_val = xfinal_value (base, limit, inc, nel);
251
252 return;
253 }
254
255 // Now that we have handled all the special cases, we can compute
256 // the number of elements and the final value in a way that attempts
257 // to avoid rounding errors as much as possible.
258
259 nel = xnumel_internal (base, limit, inc);
260 final_val = xfinal_value (base, limit, inc, nel);
261}
262
263template <typename T>
264void
265xinit (const octave_int<T>& base, const octave_int<T>& limit,
266 const octave_int<T>& inc, bool reverse,
267 octave_int<T>& final_val, octave_idx_type& nel)
268{
269 // We need an integer division that is truncating decimals instead
270 // of rounding. So, use underlying C++ types instead of
271 // octave_int<T>.
272
273 // FIXME: The numerator might underflow or overflow. Add checks for
274 // that.
275 if (reverse)
276 {
277 nel = ((inc == octave_int<T> (0)
278 || (limit > base && inc > octave_int<T> (0))
279 || (limit < base && inc < octave_int<T> (0)))
280 ? 0
281 : (base.value () - limit.value () + inc.value ())
282 / inc.value ());
283
284 final_val = base - (nel - 1) * inc;
285 }
286 else
287 {
288 nel = ((inc == octave_int<T> (0)
289 || (limit > base && inc < octave_int<T> (0))
290 || (limit < base && inc > octave_int<T> (0)))
291 ? 0
292 : (limit.value () - base.value () + inc.value ())
293 / inc.value ());
294
295 final_val = base + (nel - 1) * inc;
296 }
297}
298
299template <typename T>
300bool
301xis_storable (T base, T limit, octave_idx_type nel)
302{
303 return ! (nel > 1 && (math::isinf (base) || math::isinf (limit)));
304}
305
306template <>
307bool
309{
310 return xall_elements_are_ints (m_base, m_increment, m_final, m_numel);
311}
312
313template <>
314bool
316{
317 return xall_elements_are_ints (m_base, m_increment, m_final, m_numel);
318}
319
320template <>
321void
323{
324 xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
325}
326
327template <>
328void
330{
331 xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
332}
333
334// For now, only define for float and double.
335
336template <>
337bool
339{
340 return xis_storable (m_base, m_limit, m_numel);
341}
342
343template <>
344bool
346{
347 return xis_storable (m_base, m_limit, m_numel);
348}
349
350template <typename T>
352xnnz (T base, T limit, T inc, T final_val, octave_idx_type nel)
353{
354 // Note that the order of the following checks matters.
355
356 // If there are no elements, there can be no nonzero elements.
357 if (nel == 0)
358 return 0;
359
360 // All elements have the same sign, hence there are no zeros.
361 if ((base > 0 && limit > 0) || (base < 0 && limit < 0))
362 return nel;
363
364 // All elements are equal (inc = 0) but we know from the previous
365 // condition that they are not positive or negative, therefore all
366 // elements are zero.
367 if (inc == 0)
368 return 0;
369
370 // Exactly one zero at beginning or end of range.
371 if (base == 0 || final_val == 0)
372 return nel - 1;
373
374 // Range crosses negative/positive without hitting zero.
375 // FIXME: Is this test sufficiently tolerant or do we need to be
376 // more careful?
377 if (math::mod (-base, inc) != 0)
378 return nel;
379
380 // Range crosses negative/positive and hits zero.
381 return nel - 1;
382}
383
384template <>
387{
388 return xnnz (m_base, m_limit, m_increment, m_final, m_numel);
389}
390
391template <>
394{
395 return xnnz (m_base, m_limit, m_increment, m_final, m_numel);
396}
397
398OCTAVE_END_NAMESPACE(octave)
octave_idx_type xnnz(T base, T limit, T inc, T final_val, octave_idx_type nel)
Definition Range.cc:352
bool xteq(T u, T v, T ct=3 *std::numeric_limits< T >::epsilon())
Definition Range.cc:76
bool xis_storable(T base, T limit, octave_idx_type nel)
Definition Range.cc:301
void xinit(T base, T limit, T inc, bool reverse, T &final_val, octave_idx_type &nel)
Definition Range.cc:198
octave_idx_type xnumel_internal(T base, T limit, T inc)
Definition Range.cc:86
T xtfloor(T x, T ct)
Definition Range.cc:45
T xfinal_value(T base, T limit, T inc, octave_idx_type nel)
Definition Range.cc:161
bool xall_elements_are_ints(T base, T inc, T final_val, octave_idx_type nel)
Definition Range.cc:139
T value() const
bool is_storable() const
Definition Range.cc:338
octave_idx_type nnz() const
Definition Range.cc:386
void init()
Definition Range.cc:322
bool all_elements_are_ints() const
Definition Range.cc:308
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
F77_RET_T const F77_DBLE * x