GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
Range.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1993-2024 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 
44 template <typename T>
45 T 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 
74 template <typename T>
75 bool
76 xteq (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 
84 template <typename T>
86 xnumel_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)))
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 
131  ? n_elt : -1);
132  }
133 
134  return retval;
135 }
136 
137 template <typename T>
138 bool
139 xall_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 
159 template <typename T>
160 T
161 xfinal_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 
196 template <typename T>
197 void
198 xinit (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?
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 
263 template <typename T>
264 void
265 xinit (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 
299 template <typename T>
300 bool
301 xis_storable (T base, T limit, octave_idx_type nel)
302 {
303  return ! (nel > 1 && (math::isinf (base) || math::isinf (limit)));
304 }
305 
306 template <>
307 bool
309 {
310  return xall_elements_are_ints (m_base, m_increment, m_final, m_numel);
311 }
312 
313 template <>
314 bool
316 {
317  return xall_elements_are_ints (m_base, m_increment, m_final, m_numel);
318 }
319 
320 template <>
321 void
323 {
324  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
325 }
326 
327 template <>
328 void
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 
336 template <>
337 bool
339 {
340  return xis_storable (m_base, m_limit, m_numel);
341 }
342 
343 template <>
344 bool
346 {
347  return xis_storable (m_base, m_limit, m_numel);
348 }
349 
350 template <typename T>
352 xnnz (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 
384 template <>
387 {
388  return xnnz (m_base, m_limit, m_increment, m_final, m_numel);
389 }
390 
391 template <>
394 {
395  return xnnz (m_base, m_limit, m_increment, m_final, m_numel);
396 }
397 
398 OCTAVE_END_NAMESPACE(octave)
#define NaN
Definition: Faddeeva.cc:261
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
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
T value() const
Definition: oct-inttypes.h:832
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
octave_idx_type nint_big(double x)
Definition: lo-mappers.cc:188
bool isfinite(double x)
Definition: lo-mappers.h:192
bool isinf(double x)
Definition: lo-mappers.h:203
T mod(T x, T y)
Definition: lo-mappers.h:294
double round(double x)
Definition: lo-mappers.h:136
bool isnan(bool)
Definition: lo-mappers.h:178
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
F77_RET_T const F77_DBLE * x