GNU Octave  6.2.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-2021 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 
42 bool
44 {
45  // If the base and increment are ints, the final value in the range will also
46  // be an integer, even if the limit is not. If there is one or fewer
47  // elements only the base needs to be an integer.
48 
52 }
53 
55 Range::nnz (void) const
56 {
58 
59  if (! isempty ())
60  {
61  if ((rng_base > 0.0 && rng_limit > 0.0)
62  || (rng_base < 0.0 && rng_limit < 0.0))
63  {
64  // All elements have the same sign, hence there are no zeros.
65  retval = rng_numel;
66  }
67  else if (rng_inc != 0.0)
68  {
69  if (rng_base == 0.0 || rng_limit == 0.0)
70  // Exactly one zero at beginning or end of range.
71  retval = rng_numel - 1;
72  else if ((rng_base / rng_inc) != std::floor (rng_base / rng_inc))
73  // Range crosses negative/positive without hitting zero.
74  retval = rng_numel;
75  else
76  // Range crosses negative/positive and hits zero.
77  retval = rng_numel - 1;
78  }
79  else
80  {
81  // All elements are equal (rng_inc = 0) but not positive or negative,
82  // therefore all elements are zero.
83  retval = 0;
84  }
85  }
86 
87  return retval;
88 }
89 
90 Matrix
91 Range::matrix_value (void) const
92 {
93  if (rng_numel > 0 && cache.isempty ())
94  {
95  cache.resize (1, rng_numel);
96 
97  // The first element must always be *exactly* the base.
98  // E.g, -0 would otherwise become +0 in the loop (-0 + 0*increment).
99  cache(0) = rng_base;
100 
101  double b = rng_base;
102  double increment = rng_inc;
103  for (octave_idx_type i = 1; i < rng_numel - 1; i++)
104  cache.xelem (i) = b + i * increment;
105 
106  cache.xelem (rng_numel - 1) = rng_limit;
107  }
108 
109  return cache;
110 }
111 
112 double
114 {
115  if (i < 0 || i >= rng_numel)
117 
118  if (i == 0)
119  return rng_base;
120  else if (i < rng_numel - 1)
121  return rng_base + i * rng_inc;
122  else
123  return rng_limit;
124 }
125 
126 double
128 {
129  // Ranges are *always* row vectors.
130  if (i != 0)
132 
133  return checkelem (j);
134 }
135 
136 double
138 {
139  if (i == 0)
140  return rng_base;
141  else if (i < rng_numel - 1)
142  return rng_base + i * rng_inc;
143  else
144  return rng_limit;
145 }
146 
147 // Helper class used solely for idx_vector.loop () function call
149 {
150 public:
151  __rangeidx_helper (double *a, double b, double i, double l, octave_idx_type n)
152  : array (a), base (b), inc (i), limit (l), nmax (n-1) { }
153 
155  {
156  if (i == 0)
157  *array++ = base;
158  else if (i < nmax)
159  *array++ = base + i * inc;
160  else
161  *array++ = limit;
162  }
163 
164 private:
165 
166  double *array, base, inc, limit;
168 
169 };
170 
172 Range::index (const idx_vector& i) const
173 {
175 
177 
178  if (i.is_colon ())
179  {
181  }
182  else
183  {
184  if (i.extent (n) != n)
185  octave::err_index_out_of_range (1, 1, i.extent (n), n, dims ()); // throws
186 
187  dim_vector rd = i.orig_dimensions ();
188  octave_idx_type il = i.length (n);
189 
190  // taken from Array.cc.
191  if (n != 1 && rd.isvector ())
192  rd = dim_vector (1, il);
193 
194  retval.clear (rd);
195 
196  // idx_vector loop across all values in i,
197  // executing __rangeidx_helper (i) for each i
200  }
201 
202  return retval;
203 }
204 
205 // NOTE: max and min only return useful values if numel > 0.
206 // do_minmax_body() in max.cc avoids calling Range::min/max if numel == 0.
207 
208 double
209 Range::min (void) const
210 {
211  double retval = 0.0;
212  if (rng_numel > 0)
213  {
214  if (rng_inc > 0)
215  retval = rng_base;
216  else
217  {
218  retval = rng_base + (rng_numel - 1) * rng_inc;
219 
220  // Require '<=' test. See note in max ().
221  if (retval <= rng_limit)
222  retval = rng_limit;
223  }
224 
225  }
226  return retval;
227 }
228 
229 double
230 Range::max (void) const
231 {
232  double retval = 0.0;
233  if (rng_numel > 0)
234  {
235  if (rng_inc > 0)
236  {
237  retval = rng_base + (rng_numel - 1) * rng_inc;
238 
239  // On some machines (x86 with extended precision floating point
240  // arithmetic, for example) it is possible that we can overshoot the
241  // limit by approximately the machine precision even though we were
242  // very careful in our calculation of the number of elements.
243  // Therefore, we clip the result to the limit if it overshoots.
244  // The test also includes equality (>= rng_limit) to have expressions
245  // such as -5:1:-0 result in a -0 endpoint.
246  if (retval >= rng_limit)
247  retval = rng_limit;
248  }
249  else
250  retval = rng_base;
251  }
252  return retval;
253 }
254 
255 void
256 Range::sort_internal (bool ascending)
257 {
258  if ((ascending && rng_base > rng_limit && rng_inc < 0.0)
259  || (! ascending && rng_base < rng_limit && rng_inc > 0.0))
260  {
261  std::swap (rng_base, rng_limit);
262  rng_inc = -rng_inc;
263  clear_cache ();
264  }
265 }
266 
267 void
269 {
270  octave_idx_type nel = numel ();
271 
272  sidx.resize (dim_vector (1, nel));
273 
274  octave_idx_type *psidx = sidx.fortran_vec ();
275 
276  bool reverse = false;
277 
278  if ((ascending && rng_base > rng_limit && rng_inc < 0.0)
279  || (! ascending && rng_base < rng_limit && rng_inc > 0.0))
280  {
281  std::swap (rng_base, rng_limit);
282  rng_inc = -rng_inc;
283  clear_cache ();
284  reverse = true;
285  }
286 
287  octave_idx_type tmp = (reverse ? nel - 1 : 0);
288  octave_idx_type stp = (reverse ? -1 : 1);
289 
290  for (octave_idx_type i = 0; i < nel; i++, tmp += stp)
291  psidx[i] = tmp;
292 }
293 
294 Matrix
296 {
297  return matrix_value ().diag (k);
298 }
299 
300 Range
302 {
303  Range retval = *this;
304 
305  if (dim == 1)
306  {
307  if (mode == ASCENDING)
308  retval.sort_internal (true);
309  else if (mode == DESCENDING)
310  retval.sort_internal (false);
311  }
312  else if (dim != 0)
313  (*current_liboctave_error_handler) ("Range::sort: invalid dimension");
314 
315  return retval;
316 }
317 
318 Range
320  sortmode mode) const
321 {
322  Range retval = *this;
323 
324  if (dim == 1)
325  {
326  if (mode == ASCENDING)
327  retval.sort_internal (sidx, true);
328  else if (mode == DESCENDING)
329  retval.sort_internal (sidx, false);
330  }
331  else if (dim != 0)
332  (*current_liboctave_error_handler) ("Range::sort: invalid dimension");
333 
334  return retval;
335 }
336 
337 sortmode
339 {
340  if (rng_numel > 1 && rng_inc > 0)
341  mode = (mode == DESCENDING) ? UNSORTED : ASCENDING;
342  else if (rng_numel > 1 && rng_inc < 0)
343  mode = (mode == ASCENDING) ? UNSORTED : DESCENDING;
344  else
345  mode = (mode == UNSORTED) ? ASCENDING : mode;
346 
347  return mode;
348 }
349 
350 void
351 Range::set_base (double b)
352 {
353  if (rng_base != b)
354  {
355  rng_base = b;
356 
357  init ();
358  }
359 }
360 
361 void
363 {
364  if (rng_limit != l)
365  {
366  rng_limit = l;
367 
368  init ();
369  }
370 }
371 
372 void
373 Range::set_inc (double i)
374 {
375  if (rng_inc != i)
376  {
377  rng_inc = i;
378 
379  init ();
380  }
381 }
382 
383 std::ostream&
384 operator << (std::ostream& os, const Range& a)
385 {
386  double b = a.base ();
387  double increment = a.inc ();
388  octave_idx_type nel = a.numel ();
389 
390  if (nel > 1)
391  {
392  // First element must be the base *exactly* (e.g., -0).
393  os << b << ' ';
394  for (octave_idx_type i = 1; i < nel-1; i++)
395  os << b + i * increment << ' ';
396  }
397 
398  // Print out the last element exactly, rather than a calculated last element.
399  os << a.rng_limit << "\n";
400 
401  return os;
402 }
403 
404 std::istream&
405 operator >> (std::istream& is, Range& a)
406 {
407  is >> a.rng_base;
408  if (is)
409  {
410  double tmp_rng_limit;
411  is >> tmp_rng_limit;
412 
413  if (is)
414  is >> a.rng_inc;
415 
416  // Clip the rng_limit to the true limit, rebuild numel, clear cache
417  a.set_limit (tmp_rng_limit);
418  }
419 
420  return is;
421 }
422 
423 Range
425 {
426  return Range (-r.base (), -r.limit (), -r.inc (), r.numel ());
427 }
428 
429 Range operator + (double x, const Range& r)
430 {
431  Range result (x + r.base (), x + r.limit (), r.inc (), r.numel ());
432  // Check whether new range was constructed properly. A non-finite
433  // value (Inf or NaN) requires that the output be of the same size
434  // as the original range with all values set to the non-finite value.
435  if (result.rng_numel < 0)
436  result.cache = x + r.matrix_value ();
437 
438  return result;
439 }
440 
441 Range operator + (const Range& r, double x)
442 {
443  Range result (r.base () + x, r.limit () + x, r.inc (), r.numel ());
444  if (result.rng_numel < 0)
445  result.cache = r.matrix_value () + x;
446 
447  return result;
448 }
449 
450 Range operator - (double x, const Range& r)
451 {
452  Range result (x - r.base (), x - r.limit (), -r.inc (), r.numel ());
453  if (result.rng_numel < 0)
454  result.cache = x - r.matrix_value ();
455 
456  return result;
457 }
458 
459 Range operator - (const Range& r, double x)
460 {
461  Range result (r.base () - x, r.limit () - x, r.inc (), r.numel ());
462  if (result.rng_numel < 0)
463  result.cache = r.matrix_value () - x;
464 
465  return result;
466 }
467 
468 Range operator * (double x, const Range& r)
469 {
470  Range result (x * r.base (), x * r.limit (), x * r.inc (), r.numel ());
471  if (result.rng_numel < 0)
472  result.cache = x * r.matrix_value ();
473 
474  return result;
475 }
476 
477 Range operator * (const Range& r, double x)
478 {
479  Range result (r.base () * x, r.limit () * x, r.inc () * x, r.numel ());
480  if (result.rng_numel < 0)
481  result.cache = r.matrix_value () * x;
482 
483  return result;
484 }
485 
486 // C See Knuth, Art Of Computer Programming, Vol. 1, Problem 1.2.4-5.
487 // C
488 // C===Tolerant FLOOR function.
489 // C
490 // C X - is given as a Double Precision argument to be operated on.
491 // C It is assumed that X is represented with M mantissa bits.
492 // C CT - is given as a Comparison Tolerance such that
493 // C 0.LT.CT.LE.3-SQRT(5)/2. If the relative difference between
494 // C X and A whole number is less than CT, then TFLOOR is
495 // C returned as this whole number. By treating the
496 // C floating-point numbers as a finite ordered set note that
497 // C the heuristic EPS=2.**(-(M-1)) and CT=3*EPS causes
498 // C arguments of TFLOOR/TCEIL to be treated as whole numbers
499 // C if they are exactly whole numbers or are immediately
500 // C adjacent to whole number representations. Since EPS, the
501 // C "distance" between floating-point numbers on the unit
502 // C interval, and M, the number of bits in X'S mantissa, exist
503 // C on every floating-point computer, TFLOOR/TCEIL are
504 // C consistently definable on every floating-point computer.
505 // C
506 // C For more information see the following references:
507 // C (1) P. E. Hagerty, "More On Fuzzy Floor And Ceiling," APL QUOTE
508 // C QUAD 8(4):20-24, June 1978. Note that TFLOOR=FL5.
509 // C (2) L. M. Breed, "Definitions For Fuzzy Floor And Ceiling", APL
510 // C QUOTE QUAD 8(3):16-23, March 1978. This paper cites FL1 through
511 // C FL5, the history of five years of evolutionary development of
512 // C FL5 - the seven lines of code below - by open collaboration
513 // C and corroboration of the mathematical-computing community.
514 // C
515 // C Penn State University Center for Academic Computing
516 // C H. D. Knoble - August, 1978.
517 
518 static inline double
519 tfloor (double x, double ct)
520 {
521 // C---------FLOOR(X) is the largest integer algebraically less than
522 // C or equal to X; that is, the unfuzzy FLOOR function.
523 
524 // DINT (X) = X - DMOD (X, 1.0);
525 // FLOOR (X) = DINT (X) - DMOD (2.0 + DSIGN (1.0, X), 3.0);
526 
527 // C---------Hagerty's FL5 function follows...
528 
529  double q = 1.0;
530 
531  if (x < 0.0)
532  q = 1.0 - ct;
533 
534  double rmax = q / (2.0 - ct);
535 
536  double t1 = 1.0 + std::floor (x);
537  t1 = (ct / q) * (t1 < 0.0 ? -t1 : t1);
538  t1 = (rmax < t1 ? rmax : t1);
539  t1 = (ct > t1 ? ct : t1);
540  t1 = std::floor (x + t1);
541 
542  if (x <= 0.0 || (t1 - x) < rmax)
543  return t1;
544  else
545  return t1 - 1.0;
546 }
547 
548 static inline bool
549 teq (double u, double v,
550  double ct = 3.0 * std::numeric_limits<double>::epsilon ())
551 {
552  double tu = std::abs (u);
553  double tv = std::abs (v);
554 
555  return std::abs (u - v) < ((tu > tv ? tu : tv) * ct);
556 }
557 
560 {
561  octave_idx_type retval = -1;
562 
563  if (rng_inc == 0
564  || (rng_limit > rng_base && rng_inc < 0)
565  || (rng_limit < rng_base && rng_inc > 0))
566  {
567  retval = 0;
568  }
569  else
570  {
571  double ct = 3.0 * std::numeric_limits<double>::epsilon ();
572 
573  double tmp = tfloor ((rng_limit - rng_base + rng_inc) / rng_inc, ct);
574 
575  octave_idx_type n_elt = (tmp > 0.0 ? static_cast<octave_idx_type> (tmp)
576  : 0);
577 
578  // If the final element that we would compute for the range is equal to
579  // the limit of the range, or is an adjacent floating point number,
580  // accept it. Otherwise, try a range with one fewer element. If that
581  // fails, try again with one more element.
582  //
583  // I'm not sure this is very good, but it seems to work better than just
584  // using tfloor as above. For example, without it, the expression
585  // 1.8:0.05:1.9 fails to produce the expected result of [1.8, 1.85, 1.9].
586 
587  if (! teq (rng_base + (n_elt - 1) * rng_inc, rng_limit))
588  {
589  if (teq (rng_base + (n_elt - 2) * rng_inc, rng_limit))
590  n_elt--;
591  else if (teq (rng_base + n_elt * rng_inc, rng_limit))
592  n_elt++;
593  }
594 
596  ? n_elt : -1;
597  }
598 
599  return retval;
600 }
601 
602 double
604 {
605  double new_limit;
606 
607  if (rng_inc > 0)
608  new_limit = max ();
609  else
610  new_limit = min ();
611 
612  // If result must be an integer then force the new_limit to be one.
613  if (all_elements_are_ints ())
614  new_limit = std::round (new_limit);
615 
616  return new_limit;
617 }
618 
619 void
621 {
624 
625  clear_cache ();
626 }
Range operator*(double x, const Range &r)
Definition: Range.cc:468
std::ostream & operator<<(std::ostream &os, const Range &a)
Definition: Range.cc:384
Range operator-(const Range &r)
Definition: Range.cc:424
Range operator+(double x, const Range &r)
Definition: Range.cc:429
std::istream & operator>>(std::istream &is, Range &a)
Definition: Range.cc:405
static bool teq(double u, double v, double ct=3.0 *std::numeric_limits< double >::epsilon())
Definition: Range.cc:549
static double tfloor(double x, double ct)
Definition: Range.cc:519
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:469
void clear(void)
Definition: Array.cc:87
const T * fortran_vec(void) const
Size of the specified dimension.
Definition: Array.h:583
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:572
MArray< T > reshape(const dim_vector &new_dims) const
Definition: MArray.h:93
Definition: dMatrix.h:42
Matrix diag(octave_idx_type k=0) const
Definition: dMatrix.cc:2391
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:151
Definition: Range.h:40
bool isempty(void) const
Definition: Range.h:96
void set_inc(double i)
Definition: Range.cc:373
octave_idx_type numel_internal(void) const
Definition: Range.cc:559
void clear_cache(void) const
Definition: Range.h:167
double rng_base
Definition: Range.h:153
dim_vector dims(void) const
Definition: Range.h:89
sortmode issorted(sortmode mode=ASCENDING) const
Definition: Range.cc:338
double elem(octave_idx_type i) const
Definition: Range.cc:137
Range sort(octave_idx_type dim=0, sortmode mode=ASCENDING) const
Definition: Range.cc:301
Matrix diag(octave_idx_type k=0) const
Definition: Range.cc:295
double inc(void) const
Definition: Range.h:85
double base(void) const
Definition: Range.h:83
octave_idx_type nnz(void) const
Definition: Range.cc:55
double rng_limit
Definition: Range.h:154
double limit_internal(void) const
Definition: Range.cc:603
void init(void)
Definition: Range.cc:620
octave_idx_type rng_numel
Definition: Range.h:157
Matrix matrix_value(void) const
Definition: Range.cc:91
void set_base(double b)
Definition: Range.cc:351
Matrix cache
Definition: Range.h:159
void sort_internal(bool ascending=true)
Definition: Range.cc:256
bool all_elements_are_ints(void) const
Definition: Range.cc:43
octave_idx_type numel(void) const
Definition: Range.h:87
Array< double > index(const idx_vector &i) const
Definition: Range.cc:172
double rng_inc
Definition: Range.h:155
double max(void) const
Definition: Range.cc:230
double min(void) const
Definition: Range.cc:209
double checkelem(octave_idx_type i) const
Definition: Range.cc:113
void set_limit(double l)
Definition: Range.cc:362
octave_idx_type nmax
Definition: Range.cc:167
double * array
Definition: Range.cc:166
double limit
Definition: Range.cc:166
__rangeidx_helper(double *a, double b, double i, double l, octave_idx_type n)
Definition: Range.cc:151
void operator()(octave_idx_type i)
Definition: Range.cc:154
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
bool isvector(void) const
Definition: dim-vector.h:461
bool is_colon(void) const
Definition: idx-vector.h:576
void loop(octave_idx_type n, Functor body) const
Definition: idx-vector.h:838
octave_idx_type length(octave_idx_type n=0) const
Definition: idx-vector.h:558
dim_vector orig_dimensions(void) const
Definition: idx-vector.h:594
octave_idx_type extent(octave_idx_type n) const
Definition: idx-vector.h:561
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
bool isnan(bool)
Definition: lo-mappers.h:178
octave_idx_type nint_big(double x)
Definition: lo-mappers.cc:184
double round(double x)
Definition: lo-mappers.h:136
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
void err_index_out_of_range(int nd, int dim, octave_idx_type idx, octave_idx_type ext, const dim_vector &dv)
sortmode
Definition: oct-sort.h:95
@ UNSORTED
Definition: oct-sort.h:95
@ ASCENDING
Definition: oct-sort.h:95
@ DESCENDING
Definition: oct-sort.h:95
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
static T abs(T x)
Definition: pr-output.cc:1678