GNU Octave  8.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-2023 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 xteq (T u, T v, T ct = 3 * std::numeric_limits<T>::epsilon ())
76 {
77  T tu = std::abs (u);
78  T tv = std::abs (v);
79 
80  return std::abs (u - v) < ((tu > tv ? tu : tv) * ct);
81 }
82 
83 template <typename T>
84 octave_idx_type xnumel_internal (T base, T limit, T inc)
85 {
86  octave_idx_type retval = -1;
87  if (! math::isfinite (base) || ! math::isfinite (inc)
88  || math::isnan (limit))
89  retval = -2;
90  else if (math::isinf (limit)
91  && ((inc > 0 && limit > 0)
92  || (inc < 0 && limit < 0)))
94  else if (inc == 0
95  || (limit > base && inc < 0)
96  || (limit < base && inc > 0))
97  {
98  retval = 0;
99  }
100  else
101  {
102  T ct = 3 * std::numeric_limits<T>::epsilon ();
103 
104  T tmp = xtfloor ((limit - base + inc) / inc, ct);
105 
106  octave_idx_type n_elt
107  = (tmp > 0 ? static_cast<octave_idx_type> (tmp) : 0);
108 
109  // If the final element that we would compute for the range is
110  // equal to the limit of the range, or is an adjacent floating
111  // point number, accept it. Otherwise, try a range with one
112  // fewer element. If that fails, try again with one more
113  // element.
114  //
115  // I'm not sure this is very good, but it seems to work better
116  // than just using tfloor as above. For example, without it,
117  // the expression 1.8:0.05:1.9 fails to produce the expected
118  // result of [1.8, 1.85, 1.9].
119 
120  if (! xteq (base + (n_elt - 1) * inc, limit))
121  {
122  if (xteq (base + (n_elt - 2) * inc, limit))
123  n_elt--;
124  else if (xteq (base + n_elt * inc, limit))
125  n_elt++;
126  }
127 
129  ? n_elt : -1);
130  }
131 
132  return retval;
133 }
134 
135 template <typename T>
136 bool xall_elements_are_ints (T base, T inc, T final_val, octave_idx_type nel)
137 {
138  // If the range is empty or NaN then there are no elements so there
139  // can be no int elements.
140  if (nel == 0 || math::isnan (final_val))
141  return false;
142 
143  // If the base and increment are ints, all elements will be
144  // integers.
145  if (math::nint_big (base) == base && math::nint_big (inc) == inc)
146  return true;
147 
148  // If the range has only one element, then the base needs to be an
149  // integer.
150  if (nel == 1 && math::nint_big (base))
151  return true;
152 
153  return false;
154 }
155 
156 template <typename T>
157 T
158 xfinal_value (T base, T limit, T inc, octave_idx_type nel)
159 {
160  T retval = T (0);
161 
162  if (nel <= 1)
163  return base;
164 
165  // If increment is 0, then numel should also be zero.
166 
167  retval = base + (nel - 1) * inc;
168 
169  // On some machines (x86 with extended precision floating point
170  // arithmetic, for example) it is possible that we can overshoot
171  // the limit by approximately the machine precision even though
172  // we were very careful in our calculation of the number of
173  // elements. Therefore, we clip the result to the limit if it
174  // overshoots.
175 
176  // NOTE: The test also includes equality (>= limit) to have
177  // expressions such as -5:1:-0 result in a -0 endpoint.
178 
179  if ((inc > T (0) && retval >= limit) || (inc < T (0) && retval <= limit))
180  retval = limit;
181 
182  // If all elements are integers, then ensure the final value is.
183  // Note that we pass the preliminary computed final value to
184  // xall_elements_are_ints, but it only checks whether that value is
185  // NaN.
186 
187  if (xall_elements_are_ints (base, inc, retval, nel))
188  retval = std::round (retval);
189 
190  return retval;
191 }
192 
193 template <typename T>
194 void
195 xinit (T base, T limit, T inc, bool reverse, T& final_val,
196  octave_idx_type& nel)
197 {
198  // Catch obvious NaN ranges.
199  if (math::isnan (base) || math::isnan (limit) || math::isnan (inc))
200  {
201  final_val = numeric_limits<T>::NaN ();
202  nel = 1;
203  return;
204  }
205 
206  // Floating point numbers are always signed
207  if (reverse)
208  inc = -inc;
209 
210  // Catch empty ranges.
211  if (inc == 0
212  || (limit < base && inc > 0)
213  || (limit > base && inc < 0))
214  {
215  nel = 0;
216  return;
217  }
218 
219  // The following case also catches Inf values for increment when
220  // there will be only one element.
221 
222  if ((limit <= base && base + inc < limit)
223  || (limit >= base && base + inc > limit))
224  {
225  final_val = base;
226  nel = 1;
227  return;
228  }
229 
230  // Any other calculations with Inf will give us either a NaN range
231  // or an infinite nember of elements.
232 
233  T dnel = (limit - base) / inc;
234  if (math::isnan (dnel))
235  {
236  nel = 1;
237  final_val = numeric_limits<T>::NaN ();
238  return;
239  }
240 
241  if (dnel > 0 && math::isinf (dnel))
242  {
243  // FIXME: Should this be an immediate error?
245 
246  // FIXME: Will this do the right thing in all cases?
247  final_val = xfinal_value (base, limit, inc, nel);
248 
249  return;
250  }
251 
252  // Now that we have handled all the special cases, we can compute
253  // the number of elements and the final value in a way that attempts
254  // to avoid rounding errors as much as possible.
255 
256  nel = xnumel_internal (base, limit, inc);
257  final_val = xfinal_value (base, limit, inc, nel);
258 }
259 
260 template <typename T>
261 void
262 xinit (const octave_int<T>& base, const octave_int<T>& limit,
263  const octave_int<T>& inc, bool reverse,
264  octave_int<T>& final_val, octave_idx_type& nel)
265 {
266  // We need an integer division that is truncating decimals instead
267  // of rounding. So, use underlying C++ types instead of
268  // octave_int<T>.
269 
270  // FIXME: The numerator might underflow or overflow. Add checks for
271  // that.
272  if (reverse)
273  {
274  nel = ((inc == octave_int<T> (0)
275  || (limit > base && inc > octave_int<T> (0))
276  || (limit < base && inc < octave_int<T> (0)))
277  ? 0
278  : (base.value () - limit.value () + inc.value ())
279  / inc.value ());
280 
281  final_val = base - (nel - 1) * inc;
282  }
283  else
284  {
285  nel = ((inc == octave_int<T> (0)
286  || (limit > base && inc < octave_int<T> (0))
287  || (limit < base && inc > octave_int<T> (0)))
288  ? 0
289  : (limit.value () - base.value () + inc.value ())
290  / inc.value ());
291 
292  final_val = base + (nel - 1) * inc;
293  }
294 }
295 
296 template <typename T>
297 bool
298 xis_storable (T base, T limit, octave_idx_type nel)
299 {
300  return ! (nel > 1 && (math::isinf (base) || math::isinf (limit)));
301 }
302 
303 template <>
304 bool
305 range<double>::all_elements_are_ints (void) const
306 {
307  return xall_elements_are_ints (m_base, m_increment, m_final, m_numel);
308 }
309 
310 template <>
311 bool
312 range<float>::all_elements_are_ints (void) const
313 {
314  return xall_elements_are_ints (m_base, m_increment, m_final, m_numel);
315 }
316 
317 template <>
318 void
319 range<double>::init (void)
320 {
321  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
322 }
323 
324 template <>
325 void
326 range<float>::init (void)
327 {
328  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
329 }
330 
331 // For now, only define for float and double.
332 
333 #if 0
334 
335 template <>
336 void
337 range<octave_int8>::init (void)
338 {
339  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
340 }
341 
342 template <>
343 void
344 range<octave_int16>::init (void)
345 {
346  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
347 }
348 
349 template <>
350 void
351 range<octave_int32>::init (void)
352 {
353  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
354 }
355 
356 template <>
357 void
358 range<octave_int64>::init (void)
359 {
360  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
361 }
362 
363 template <>
364 void
365 range<octave_uint8>::init (void)
366 {
367  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
368 }
369 
370 template <>
371 void
372 range<octave_uint16>::init (void)
373 {
374  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
375 }
376 
377 template <>
378 void
379 range<octave_uint32>::init (void)
380 {
381  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
382 }
383 
384 template <>
385 void
386 range<octave_uint64>::init (void)
387 {
388  xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
389 }
390 
391 #endif
392 
393 template <>
394 bool
395 range<double>::is_storable (void) const
396 {
397  return xis_storable (m_base, m_limit, m_numel);
398 }
399 
400 template <>
401 bool
402 range<float>::is_storable (void) const
403 {
404  return xis_storable (m_base, m_limit, m_numel);
405 }
406 
407 template <typename T>
409 xnnz (T base, T limit, T inc, T final_val, octave_idx_type nel)
410 {
411  // Note that the order of the following checks matters.
412 
413  // If there are no elements, there can be no non-zero elements.
414  if (nel == 0)
415  return 0;
416 
417  // All elements have the same sign, hence there are no zeros.
418  if ((base > 0 && limit > 0) || (base < 0 && limit < 0))
419  return nel;
420 
421  // All elements are equal (inc = 0) but we know from the previous
422  // condition that they are not positive or negative, therefore all
423  // elements are zero.
424  if (inc == 0)
425  return 0;
426 
427  // Exactly one zero at beginning or end of range.
428  if (base == 0 || final_val == 0)
429  return nel - 1;
430 
431  // Range crosses negative/positive without hitting zero.
432  // FIXME: Is this test sufficiently tolerant or do we need to be
433  // more careful?
434  if (math::mod (-base, inc) != 0)
435  return nel;
436 
437  // Range crosses negative/positive and hits zero.
438  return nel - 1;
439 }
440 
441 template <>
443 range<double>::nnz (void) const
444 {
445  return xnnz (m_base, m_limit, m_increment, m_final, m_numel);
446 }
447 
448 template <>
450 range<float>::nnz (void) const
451 {
452  return xnnz (m_base, m_limit, m_increment, m_final, m_numel);
453 }
454 
456 
457 bool
459 {
460  // If the base and increment are ints, the final value in the range will also
461  // be an integer, even if the limit is not. If there is one or fewer
462  // elements only the base needs to be an integer.
463 
466  && (octave::math::nint_big (m_inc) == m_inc || m_numel <= 1));
467 }
468 
470 Range::nnz (void) const
471 {
472  octave_idx_type retval = 0;
473 
474  if (! isempty ())
475  {
476  if ((m_base > 0.0 && m_limit > 0.0) || (m_base < 0.0 && m_limit < 0.0))
477  {
478  // All elements have the same sign, hence there are no zeros.
479  retval = m_numel;
480  }
481  else if (m_inc != 0.0)
482  {
483  if (m_base == 0.0 || m_limit == 0.0)
484  // Exactly one zero at beginning or end of range.
485  retval = m_numel - 1;
486  else if ((m_base / m_inc) != std::floor (m_base / m_inc))
487  // Range crosses negative/positive without hitting zero.
488  retval = m_numel;
489  else
490  // Range crosses negative/positive and hits zero.
491  retval = m_numel - 1;
492  }
493  else
494  {
495  // All elements are equal (m_inc = 0) but not positive or negative,
496  // therefore all elements are zero.
497  retval = 0;
498  }
499  }
500 
501  return retval;
502 }
503 
504 Matrix
506 {
507  Matrix retval (1, m_numel);
508 
509  if (m_numel > 0)
510  {
511  // The first element must always be *exactly* the base.
512  // E.g, -0 would otherwise become +0 in the loop (-0 + 0*increment).
513  retval(0) = m_base;
514 
515  double b = m_base;
516  double increment = m_inc;
517  for (octave_idx_type i = 1; i < m_numel - 1; i++)
518  retval.xelem (i) = b + i * increment;
519 
520  retval.xelem (m_numel - 1) = m_limit;
521  }
522 
523  return retval;
524 }
525 
526 double
528 {
529  if (i < 0 || i >= m_numel)
531 
532  if (i == 0)
533  return m_base;
534  else if (i < m_numel - 1)
535  return m_base + i * m_inc;
536  else
537  return m_limit;
538 }
539 
540 double
542 {
543  // Ranges are *always* row vectors.
544  if (i != 0)
546 
547  return checkelem (j);
548 }
549 
550 double
552 {
553  if (i == 0)
554  return m_base;
555  else if (i < m_numel - 1)
556  return m_base + i * m_inc;
557  else
558  return m_limit;
559 }
560 
563 {
564  Array<double> retval;
565 
567 
568  if (idx.is_colon ())
569  {
570  retval = matrix_value ().reshape (dim_vector (m_numel, 1));
571  }
572  else
573  {
574  if (idx.extent (n) != n)
575  octave::err_index_out_of_range (1, 1, idx.extent (n), n, dims ()); // throws
576 
577  dim_vector idx_dims = idx.orig_dimensions ();
578  octave_idx_type idx_len = idx.length (n);
579 
580  // taken from Array.cc.
581  if (n != 1 && idx_dims.isvector ())
582  idx_dims = dim_vector (1, idx_len);
583 
584  retval.clear (idx_dims);
585 
586  // Loop over all values in IDX, executing the lambda expression
587  // for each index value.
588 
589  double *array = retval.fortran_vec ();
590 
591  idx.loop (n, [=, &array] (idx_vector i)
592  {
593  if (i == 0)
594  *array++ = m_base;
595  else if (i < m_numel - 1)
596  *array++ = m_base + i * m_inc;
597  else
598  *array++ = m_limit;
599  });
600  }
601 
602  return retval;
603 }
604 
605 // NOTE: max and min only return useful values if numel > 0.
606 // do_minmax_body() in max.cc avoids calling Range::min/max if numel == 0.
607 
608 double
609 Range::min (void) const
610 {
611  double retval = 0.0;
612  if (m_numel > 0)
613  {
614  if (m_inc > 0)
615  retval = m_base;
616  else
617  {
618  retval = m_base + (m_numel - 1) * m_inc;
619 
620  // Require '<=' test. See note in max ().
621  if (retval <= m_limit)
622  retval = m_limit;
623  }
624 
625  }
626  return retval;
627 }
628 
629 double
630 Range::max (void) const
631 {
632  double retval = 0.0;
633  if (m_numel > 0)
634  {
635  if (m_inc > 0)
636  {
637  retval = m_base + (m_numel - 1) * m_inc;
638 
639  // On some machines (x86 with extended precision floating point
640  // arithmetic, for example) it is possible that we can overshoot the
641  // limit by approximately the machine precision even though we were
642  // very careful in our calculation of the number of elements.
643  // Therefore, we clip the result to the limit if it overshoots.
644  // The test also includes equality (>= m_limit) to have expressions
645  // such as -5:1:-0 result in a -0 endpoint.
646  if (retval >= m_limit)
647  retval = m_limit;
648  }
649  else
650  retval = m_base;
651  }
652  return retval;
653 }
654 
655 void
656 Range::sort_internal (bool ascending)
657 {
658  if ((ascending && m_base > m_limit && m_inc < 0.0)
659  || (! ascending && m_base < m_limit && m_inc > 0.0))
660  {
661  std::swap (m_base, m_limit);
662  m_inc = -m_inc;
663  }
664 }
665 
666 void
668 {
669  octave_idx_type nel = numel ();
670 
671  sidx.resize (dim_vector (1, nel));
672 
673  octave_idx_type *psidx = sidx.fortran_vec ();
674 
675  bool reverse = false;
676 
677  if ((ascending && m_base > m_limit && m_inc < 0.0)
678  || (! ascending && m_base < m_limit && m_inc > 0.0))
679  {
680  std::swap (m_base, m_limit);
681  m_inc = -m_inc;
682  reverse = true;
683  }
684 
685  octave_idx_type tmp = (reverse ? nel - 1 : 0);
686  octave_idx_type stp = (reverse ? -1 : 1);
687 
688  for (octave_idx_type i = 0; i < nel; i++, tmp += stp)
689  psidx[i] = tmp;
690 }
691 
692 Matrix
694 {
695  return matrix_value ().diag (k);
696 }
697 
698 Range
700 {
701  Range retval = *this;
702 
703  if (dim == 1)
704  {
705  if (mode == ASCENDING)
706  retval.sort_internal (true);
707  else if (mode == DESCENDING)
708  retval.sort_internal (false);
709  }
710  else if (dim != 0)
711  (*current_liboctave_error_handler) ("Range::sort: invalid dimension");
712 
713  return retval;
714 }
715 
716 Range
718  sortmode mode) const
719 {
720  Range retval = *this;
721 
722  if (dim == 1)
723  {
724  if (mode == ASCENDING)
725  retval.sort_internal (sidx, true);
726  else if (mode == DESCENDING)
727  retval.sort_internal (sidx, false);
728  }
729  else if (dim != 0)
730  (*current_liboctave_error_handler) ("Range::sort: invalid dimension");
731 
732  return retval;
733 }
734 
735 sortmode
737 {
738  if (m_numel > 1 && m_inc > 0)
739  mode = (mode == DESCENDING) ? UNSORTED : ASCENDING;
740  else if (m_numel > 1 && m_inc < 0)
741  mode = (mode == ASCENDING) ? UNSORTED : DESCENDING;
742  else
743  mode = (mode == UNSORTED) ? ASCENDING : mode;
744 
745  return mode;
746 }
747 
748 void
749 Range::set_base (double b)
750 {
751  if (m_base != b)
752  {
753  m_base = b;
754 
755  init ();
756  }
757 }
758 
759 void
761 {
762  if (m_limit != l)
763  {
764  m_limit = l;
765 
766  init ();
767  }
768 }
769 
770 void
771 Range::set_inc (double i)
772 {
773  if (m_inc != i)
774  {
775  m_inc = i;
776 
777  init ();
778  }
779 }
780 
781 std::ostream&
782 operator << (std::ostream& os, const Range& a)
783 {
784  double b = a.base ();
785  double increment = a.increment ();
786  octave_idx_type nel = a.numel ();
787 
788  if (nel > 1)
789  {
790  // First element must be the base *exactly* (e.g., -0).
791  os << b << ' ';
792  for (octave_idx_type i = 1; i < nel-1; i++)
793  os << b + i * increment << ' ';
794  }
795 
796  // Print out the last element exactly, rather than a calculated last element.
797  os << a.m_limit << "\n";
798 
799  return os;
800 }
801 
802 std::istream&
803 operator >> (std::istream& is, Range& a)
804 {
805  is >> a.m_base;
806  if (is)
807  {
808  double tmp_limit;
809  is >> tmp_limit;
810 
811  if (is)
812  is >> a.m_inc;
813 
814  // Clip the m_limit to the true limit, rebuild numel, clear cache
815  a.set_limit (tmp_limit);
816  }
817 
818  return is;
819 }
820 
821 // DEPRECATED in Octave 7.
823 {
824  return Range (-r.base (), -r.limit (), -r.increment (), r.numel ());
825 }
826 
827 // DEPRECATED in Octave 7.
828 Range operator + (double x, const Range& r)
829 {
830  return Range (x + r.base (), x + r.limit (), r.increment (), r.numel ());
831 }
832 
833 // DEPRECATED in Octave 7.
834 Range operator + (const Range& r, double x)
835 {
836  return Range (r.base () + x, r.limit () + x, r.increment (), r.numel ());
837 }
838 
839 // DEPRECATED in Octave 7.
840 Range operator - (double x, const Range& r)
841 {
842  return Range (x - r.base (), x - r.limit (), -r.increment (), r.numel ());
843 }
844 
845 // DEPRECATED in Octave 7.
846 Range operator - (const Range& r, double x)
847 {
848  return Range (r.base () - x, r.limit () - x, r.increment (), r.numel ());
849 }
850 
851 // DEPRECATED in Octave 7.
852 Range operator * (double x, const Range& r)
853 {
854  return Range (x * r.base (), x * r.limit (), x * r.increment (), r.numel ());
855 }
856 
857 // DEPRECATED in Octave 7.
858 Range operator * (const Range& r, double x)
859 {
860  return Range (r.base () * x, r.limit () * x, r.increment () * x, r.numel ());
861 }
862 
863 // C See Knuth, Art Of Computer Programming, Vol. 1, Problem 1.2.4-5.
864 // C
865 // C===Tolerant FLOOR function.
866 // C
867 // C X - is given as a Double Precision argument to be operated on.
868 // C It is assumed that X is represented with M mantissa bits.
869 // C CT - is given as a Comparison Tolerance such that
870 // C 0.LT.CT.LE.3-SQRT(5)/2. If the relative difference between
871 // C X and A whole number is less than CT, then TFLOOR is
872 // C returned as this whole number. By treating the
873 // C floating-point numbers as a finite ordered set note that
874 // C the heuristic EPS=2.**(-(M-1)) and CT=3*EPS causes
875 // C arguments of TFLOOR/TCEIL to be treated as whole numbers
876 // C if they are exactly whole numbers or are immediately
877 // C adjacent to whole number representations. Since EPS, the
878 // C "distance" between floating-point numbers on the unit
879 // C interval, and M, the number of bits in X'S mantissa, exist
880 // C on every floating-point computer, TFLOOR/TCEIL are
881 // C consistently definable on every floating-point computer.
882 // C
883 // C For more information see the following references:
884 // C (1) P. E. Hagerty, "More On Fuzzy Floor And Ceiling," APL QUOTE
885 // C QUAD 8(4):20-24, June 1978. Note that TFLOOR=FL5.
886 // C (2) L. M. Breed, "Definitions For Fuzzy Floor And Ceiling", APL
887 // C QUOTE QUAD 8(3):16-23, March 1978. This paper cites FL1 through
888 // C FL5, the history of five years of evolutionary development of
889 // C FL5 - the seven lines of code below - by open collaboration
890 // C and corroboration of the mathematical-computing community.
891 // C
892 // C Penn State University Center for Academic Computing
893 // C H. D. Knoble - August, 1978.
894 
895 static inline double
896 tfloor (double x, double ct)
897 {
898 // C---------FLOOR(X) is the largest integer algebraically less than
899 // C or equal to X; that is, the unfuzzy FLOOR function.
900 
901 // DINT (X) = X - DMOD (X, 1.0);
902 // FLOOR (X) = DINT (X) - DMOD (2.0 + DSIGN (1.0, X), 3.0);
903 
904 // C---------Hagerty's FL5 function follows...
905 
906  double q = 1.0;
907 
908  if (x < 0.0)
909  q = 1.0 - ct;
910 
911  double rmax = q / (2.0 - ct);
912 
913  double t1 = 1.0 + std::floor (x);
914  t1 = (ct / q) * (t1 < 0.0 ? -t1 : t1);
915  t1 = (rmax < t1 ? rmax : t1);
916  t1 = (ct > t1 ? ct : t1);
917  t1 = std::floor (x + t1);
918 
919  if (x <= 0.0 || (t1 - x) < rmax)
920  return t1;
921  else
922  return t1 - 1.0;
923 }
924 
925 static inline bool
926 teq (double u, double v,
927  double ct = 3.0 * std::numeric_limits<double>::epsilon ())
928 {
929  double tu = std::abs (u);
930  double tv = std::abs (v);
931 
932  return std::abs (u - v) < ((tu > tv ? tu : tv) * ct);
933 }
934 
937 {
938  octave_idx_type retval = -1;
939 
942  retval = -2;
943  else if (octave::math::isinf (m_limit)
944  && ((m_inc > 0 && m_limit > 0)
945  || (m_inc < 0 && m_limit < 0)))
947  else if (m_inc == 0
948  || (m_limit > m_base && m_inc < 0)
949  || (m_limit < m_base && m_inc > 0))
950  {
951  retval = 0;
952  }
953  else
954  {
955  double ct = 3.0 * std::numeric_limits<double>::epsilon ();
956 
957  double tmp = tfloor ((m_limit - m_base + m_inc) / m_inc, ct);
958 
959  octave_idx_type n_elt = (tmp > 0.0
960  ? static_cast<octave_idx_type> (tmp) : 0);
961 
962  // If the final element that we would compute for the range is equal to
963  // the limit of the range, or is an adjacent floating point number,
964  // accept it. Otherwise, try a range with one fewer element. If that
965  // fails, try again with one more element.
966  //
967  // I'm not sure this is very good, but it seems to work better than just
968  // using tfloor as above. For example, without it, the expression
969  // 1.8:0.05:1.9 fails to produce the expected result of [1.8, 1.85, 1.9].
970 
971  if (! teq (m_base + (n_elt - 1) * m_inc, m_limit))
972  {
973  if (teq (m_base + (n_elt - 2) * m_inc, m_limit))
974  n_elt--;
975  else if (teq (m_base + n_elt * m_inc, m_limit))
976  n_elt++;
977  }
978 
980  ? n_elt : -1);
981  }
982 
983  return retval;
984 }
985 
986 double
988 {
989  double new_limit = m_inc > 0 ? max () : min ();
990 
991  // If result must be an integer then force the new_limit to be one.
992  if (all_elements_are_ints ())
993  new_limit = std::round (new_limit);
994 
995  return new_limit;
996 }
997 
998 void
1000 {
1001  m_numel = numel_internal ();
1002 
1003  if (! octave::math::isinf (m_limit))
1004  m_limit = limit_internal ();
1005 }
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:409
bool xteq(T u, T v, T ct=3 *std::numeric_limits< T >::epsilon())
Definition: Range.cc:75
bool xis_storable(T base, T limit, octave_idx_type nel)
Definition: Range.cc:298
void xinit(T base, T limit, T inc, bool reverse, T &final_val, octave_idx_type &nel)
Definition: Range.cc:195
Range operator*(double x, const Range &r)
Definition: Range.cc:852
octave_idx_type xnumel_internal(T base, T limit, T inc)
Definition: Range.cc:84
std::ostream & operator<<(std::ostream &os, const Range &a)
Definition: Range.cc:782
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:158
Range operator-(const Range &r)
Definition: Range.cc:822
Range operator+(double x, const Range &r)
Definition: Range.cc:828
std::istream & operator>>(std::istream &is, Range &a)
Definition: Range.cc:803
static bool teq(double u, double v, double ct=3.0 *std::numeric_limits< double >::epsilon())
Definition: Range.cc:926
static double tfloor(double x, double ct)
Definition: Range.cc:896
bool xall_elements_are_ints(T base, T inc, T final_val, octave_idx_type nel)
Definition: Range.cc:136
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
OCTARRAY_API void clear(void)
Definition: Array-base.cc:109
OCTARRAY_API void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array-base.cc:1032
OCTARRAY_API T * fortran_vec(void)
Size of the specified dimension.
Definition: Array-base.cc:1766
OCTARRAY_OVERRIDABLE_FUNC_API T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:524
MArray< T > reshape(const dim_vector &new_dims) const
Definition: MArray.h:87
Definition: dMatrix.h:42
OCTAVE_API Matrix diag(octave_idx_type k=0) const
Definition: dMatrix.cc:2407
Definition: Range.h:401
double increment(void) const
Definition: Range.h:466
bool isempty(void) const
Definition: Range.h:486
OCTAVE_API void set_inc(double i)
Definition: Range.cc:771
OCTAVE_API octave_idx_type numel_internal(void) const
Definition: Range.cc:936
OCTAVE_API Array< double > index(const octave::idx_vector &i) const
Definition: Range.cc:562
double m_inc
Definition: Range.h:545
octave_idx_type m_numel
Definition: Range.h:547
dim_vector dims(void) const
Definition: Range.h:479
OCTAVE_API sortmode issorted(sortmode mode=ASCENDING) const
Definition: Range.cc:736
OCTAVE_API double elem(octave_idx_type i) const
Definition: Range.cc:551
OCTAVE_API Range sort(octave_idx_type dim=0, sortmode mode=ASCENDING) const
Definition: Range.cc:699
OCTAVE_API Matrix diag(octave_idx_type k=0) const
Definition: Range.cc:693
double base(void) const
Definition: Range.h:463
OCTAVE_API octave_idx_type nnz(void) const
Definition: Range.cc:470
OCTAVE_API double limit_internal(void) const
Definition: Range.cc:987
OCTAVE_API void init(void)
Definition: Range.cc:999
double m_limit
Definition: Range.h:544
double m_base
Definition: Range.h:543
OCTAVE_API Matrix matrix_value(void) const
Definition: Range.cc:505
OCTAVE_API void set_base(double b)
Definition: Range.cc:749
OCTAVE_API void sort_internal(bool ascending=true)
Definition: Range.cc:656
OCTAVE_API bool all_elements_are_ints(void) const
Definition: Range.cc:458
octave_idx_type numel(void) const
Definition: Range.h:477
OCTAVE_API double max(void) const
Definition: Range.cc:630
OCTAVE_API double min(void) const
Definition: Range.cc:609
OCTAVE_API double checkelem(octave_idx_type i) const
Definition: Range.cc:527
OCTAVE_API void set_limit(double l)
Definition: Range.cc:760
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
bool isvector(void) const
Definition: dim-vector.h:395
T value(void) const
Definition: oct-inttypes.h:832
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
static OCTAVE_NORETURN void err_index_out_of_range(void)
Definition: idx-vector.cc:52
octave::idx_vector idx_vector
Definition: idx-vector.h:1039
octave_idx_type nint_big(double x)
Definition: lo-mappers.cc:184
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
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
sortmode
Definition: oct-sort.h:97
@ UNSORTED
Definition: oct-sort.h:97
@ ASCENDING
Definition: oct-sort.h:97
@ DESCENDING
Definition: oct-sort.h:97
static T abs(T x)
Definition: pr-output.cc:1678