GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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