GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
Range.h
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 (octave_Range_h)
27 #define octave_Range_h 1
28 
29 #include "octave-config.h"
30 
31 #include <iosfwd>
32 #include <type_traits>
33 
34 #include "Array-fwd.h"
35 #include "dMatrix.h"
36 #include "dim-vector.h"
37 #include "lo-error.h"
38 #include "oct-sort.h"
39 #include "range-fwd.h"
40 
42 
43 // For now, only define for floating point types. However, we only
44 // need range<float> as a temporary local variable in make_float_range
45 // in ov.cc.
46 
47 template <typename T>
48 class
49 range<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
50 {
51 public:
52 
53  range (void)
54  : m_base (0), m_increment (0), m_limit (0), m_final (0), m_numel (0),
55  m_reverse (false)
56  { }
57 
58  // LIMIT is an upper limit and may be outside the range of actual
59  // values. For floating point ranges, we perform a tolerant check
60  // to attempt to capture limit in the set of values if it is "close"
61  // to the value of base + a multiple of the increment.
62 
63  range (const T& base, const T& increment, const T& limit,
64  bool reverse = false)
65  : m_base (base), m_increment (increment), m_limit (limit),
66  m_final (), m_numel (), m_reverse (reverse)
67  {
68  init ();
69  }
70 
71  range (const T& base, const T& limit)
72  : m_base (base), m_increment (1), m_limit (limit), m_final (), m_numel (),
73  m_reverse (false)
74  {
75  init ();
76  }
77 
78  // Allow conversion from (presumably) properly constructed Range
79  // objects. The values of base, limit, increment, and numel must be
80  // consistent.
81 
82  // FIXME: Actually check that base, limit, increment, and numel are
83  // consistent?
84 
85  range (const T& base, const T& increment, const T& limit,
86  octave_idx_type numel, bool reverse = false)
87  : m_base (base), m_increment (increment), m_limit (limit),
88  m_final (limit), m_numel (numel), m_reverse (reverse)
89  { }
90 
91  // We don't use a constructor for this because it will conflict with
92  // range<T> (base, limit, increment) when T is octave_idx_type.
93 
94  static range<T> make_n_element_range (const T& base, const T& increment,
96  bool reverse = false)
97  {
98  // We could just make this constructor public, but it allows
99  // inconsistent ranges to be constructed. And it is probably much
100  // clearer to see "make_n_element_range" instead of puzzling over the
101  // purpose of this strange constructor form.
102 
103  T final_val = (reverse ? base - (numel - 1) * increment
104  : base + (numel - 1) * increment);
105 
106  return range<T> (base, increment, final_val, numel, reverse);
107  }
108 
109  range (const range<T>& r)
110  : m_base (r.m_base), m_increment (r.m_increment),
111  m_limit (r.m_limit), m_final (r.m_final),
112  m_numel (r.m_numel), m_reverse (r.m_reverse)
113  { }
114 
115  range<T>& operator = (const range<T>& r)
116  {
117  if (this != &r)
118  {
119  m_base = r.m_base;
120  m_increment = r.m_increment;
121  m_limit = r.m_limit;
122  m_final = r.m_final;
123  m_numel = r.m_numel;
124  m_reverse = r.m_reverse;
125  }
126 
127  return *this;
128  }
129 
130  ~range (void) = default;
131 
132  T base (void) const { return m_base; }
133  T increment (void) const { return m_increment; }
134  T limit (void) const { return m_limit; }
135  bool reverse (void) const { return m_reverse; }
136 
137  T final_value (void) const { return m_final; }
138 
139  T min (void) const
140  {
141  return (m_numel > 0
142  ? ((m_reverse ? m_increment > T (0)
143  : m_increment > T (0)) ? base () : final_value ())
144  : T (0));
145  }
146 
147  T max (void) const
148  {
149  return (m_numel > 0
150  ? ((m_reverse ? m_increment < T (0)
151  : m_increment > T (0)) ? final_value () : base ())
152  : T (0));
153  }
154 
155  octave_idx_type numel (void) const { return m_numel; }
156 
157  // To support things like "for i = 1:Inf; ...; end" that are
158  // required for Matlab compatibility, creation of a range object
159  // like 1:Inf is allowed with m_numel set to
160  // numeric_limits<octave_idx_type>::max(). However, it is not
161  // possible to store these ranges. The following function allows
162  // us to easily distinguish ranges with an infinite number of
163  // elements. There are specializations for double and float.
164 
165  bool is_storable (void) const { return true; }
166 
167  dim_vector dims (void) const { return dim_vector (1, m_numel); }
168 
169  octave_idx_type rows (void) const { return 1; }
170 
171  octave_idx_type cols (void) const { return numel (); }
172  octave_idx_type columns (void) const { return numel (); }
173 
174  bool isempty (void) const { return numel () == 0; }
175 
176  bool all_elements_are_ints (void) const { return true; }
177 
179  {
180  if (m_numel > 1 && (m_reverse ? m_increment < T (0)
181  : m_increment > T (0)))
182  mode = ((mode == DESCENDING) ? UNSORTED : ASCENDING);
183  else if (m_numel > 1 && (m_reverse ? m_increment > T (0)
184  : m_increment < T (0)))
185  mode = ((mode == ASCENDING) ? UNSORTED : DESCENDING);
186  else
187  mode = ((mode == UNSORTED) ? ASCENDING : mode);
188 
189  return mode;
190  }
191 
193 
194  // Support for single-index subscripting, without generating matrix cache.
195 
197  {
198  if (i < 0 || i >= m_numel)
199  err_index_out_of_range (2, 2, i+1, m_numel, dims ());
200 
201  if (i == 0)
202  // Required for proper NaN handling.
203  return (m_numel == 1 ? final_value () : m_base);
204  else if (i < m_numel - 1)
205  return (m_reverse ? m_base + T (i) * m_increment
206  : m_base + T (i) * m_increment);
207  else
208  return final_value ();
209  }
210 
212  {
213  // Ranges are *always* row vectors.
214  if (i != 0)
215  err_index_out_of_range (1, 1, i+1, m_numel, dims ());
216 
217  return checkelem (j);
218  }
219 
220  T elem (octave_idx_type i) const
221  {
222  if (i == 0)
223  // Required for proper NaN handling.
224  return (m_numel == 1 ? final_value () : m_base);
225  else if (i < m_numel - 1)
226  return (m_reverse ? m_base - T (i) * m_increment
227  : m_base + T (i) * m_increment);
228  else
229  return final_value ();
230  }
231 
232  T elem (octave_idx_type /* i */, octave_idx_type j) const
233  {
234  return elem (j);
235  }
236 
237  T operator () (octave_idx_type i) const
238  {
239  return elem (i);
240  }
241 
242  T operator () (octave_idx_type i, octave_idx_type j) const
243  {
244  return elem (i, j);
245  }
246 
247  Array<T> index (const idx_vector& idx) const
248  {
249  Array<T> retval;
250 
251  octave_idx_type n = m_numel;
252 
253  if (idx.is_colon ())
254  {
255  retval = array_value ().reshape (dim_vector (m_numel, 1));
256  }
257  else
258  {
259  if (idx.extent (n) != n)
260  err_index_out_of_range (1, 1, idx.extent (n), n, dims ());
261 
262  dim_vector idx_dims = idx.orig_dimensions ();
263  octave_idx_type idx_len = idx.length (n);
264 
265  // taken from Array.cc.
266  if (n != 1 && idx_dims.isvector ())
267  idx_dims = dim_vector (1, idx_len);
268 
269  retval.clear (idx_dims);
270 
271  // Loop over all values in IDX, executing the lambda
272  // expression for each index value.
273 
274  T *array = retval.fortran_vec ();
275 
276  idx.loop (n, [=, &array] (octave_idx_type i)
277  {
278  if (i == 0)
279  // Required for proper NaN handling.
280  *array++ = (m_numel == 0 ? m_final : m_base);
281  else if (i < m_numel - 1)
282  *array++ = (m_reverse ? m_base - T (i) * m_increment
283  : m_base + T (i) * m_increment);
284  else
285  *array++ = m_final;
286  });
287  }
288 
289  return retval;
290  }
291 
293  {
294  return array_value ().diag (k);
295  }
296 
297  Array<T> array_value (void) const
298  {
299  octave_idx_type nel = numel ();
300 
301  Array<T> retval (dim_vector (1, nel));
302 
303  if (nel == 1)
304  // Required for proper NaN handling.
305  retval(0) = final_value ();
306  else if (nel > 1)
307  {
308  // The first element must always be *exactly* the base.
309  // E.g, -0 would otherwise become +0 in the loop (-0 + 0*increment).
310  retval(0) = m_base;
311 
312  if (m_reverse)
313  for (octave_idx_type i = 1; i < nel - 1; i++)
314  retval.xelem (i) = m_base - i * m_increment;
315  else
316  for (octave_idx_type i = 1; i < nel - 1; i++)
317  retval.xelem (i) = m_base + i * m_increment;
318 
319  retval.xelem (nel - 1) = final_value ();
320  }
321 
322  return retval;
323  }
324 
325 private:
326 
332  bool m_reverse;
333 
334  // Setting the number of elements to zero when the increment is zero
335  // is intentional and matches the behavior of Matlab's colon
336  // operator.
337 
338  // These calculations are appropriate for integer ranges. There are
339  // specializations for double and float.
340 
341  void init (void)
342  {
343  if (m_reverse)
344  {
345  m_numel = ((m_increment == T (0)
346  || (m_limit > m_base && m_increment > T (0))
347  || (m_limit < m_base && m_increment < T (0)))
348  ? T (0)
349  : (m_base - m_limit - m_increment) / m_increment);
350 
351  m_final = m_base - (m_numel - 1) * m_increment;
352  }
353  else
354  {
355  m_numel = ((m_increment == T (0)
356  || (m_limit > m_base && m_increment < T (0))
357  || (m_limit < m_base && m_increment > T (0)))
358  ? T (0)
359  : (m_limit - m_base + m_increment) / m_increment);
360 
361  m_final = m_base + (m_numel - 1) * m_increment;
362  }
363  }
364 };
365 
366 // Specializations defined externally.
367 
368 template <> OCTAVE_API bool range<double>::all_elements_are_ints (void) const;
369 template <> OCTAVE_API bool range<float>::all_elements_are_ints (void) const;
370 
371 template <> OCTAVE_API void range<double>::init (void);
372 template <> OCTAVE_API void range<float>::init (void);
373 
374 // For now, only define for floating point types. However, we only
375 // need range<float> as a temporary local variable in make_float_range
376 // in ov.cc.
377 
378 #if 0
379 
380 template <> OCTAVE_API void range<octave_int8>::init (void);
381 template <> OCTAVE_API void range<octave_int16>::init (void);
382 template <> OCTAVE_API void range<octave_int32>::init (void);
383 template <> OCTAVE_API void range<octave_int64>::init (void);
384 template <> OCTAVE_API void range<octave_uint8>::init (void);
385 template <> OCTAVE_API void range<octave_uint16>::init (void);
386 template <> OCTAVE_API void range<octave_uint32>::init (void);
387 template <> OCTAVE_API void range<octave_uint64>::init (void);
388 
389 #endif
390 
391 template <> OCTAVE_API bool range<double>::is_storable (void) const;
392 template <> OCTAVE_API bool range<float>::is_storable (void) const;
393 
394 template <> OCTAVE_API octave_idx_type range<double>::nnz (void) const;
395 template <> OCTAVE_API octave_idx_type range<float>::nnz (void) const;
396 
398 
399 class
400 Range
401 {
402 public:
403 
404  OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
405  Range (void)
406  : m_base (0), m_limit (0), m_inc (0), m_numel (0)
407  { }
408 
409  // Assume range is already properly constructed, so just copy internal
410  // values. However, we set LIMIT to the computed final value because
411  // that mimics the behavior of the other Range class constructors that
412  // reset limit to the computed final value.
413 
414  OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
415  Range (const octave::range<double>& r)
416  : m_base (r.base ()), m_limit (r.final_value ()), m_inc (r.increment ()),
417  m_numel (r.numel ())
418  { }
419 
420  Range (const Range& r) = default;
421 
422  Range& operator = (const Range& r) = default;
423 
424  ~Range (void) = default;
425 
426  OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
427  Range (double b, double l)
428  : m_base (b), m_limit (l), m_inc (1), m_numel (numel_internal ())
429  {
430  if (! octave::math::isinf (m_limit))
431  m_limit = limit_internal ();
432  }
433 
434  OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
435  Range (double b, double l, double i)
436  : m_base (b), m_limit (l), m_inc (i), m_numel (numel_internal ())
437  {
438  if (! octave::math::isinf (m_limit))
439  m_limit = limit_internal ();
440  }
441 
442  // NOTE: The following constructor may be deprecated and removed after
443  // the arithmetic operators are removed.
444 
445  // For operators' usage (to preserve element count) and to create
446  // constant row vectors (obsolete usage).
447 
448  OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
449  Range (double b, double i, octave_idx_type n)
450  : m_base (b), m_limit (b + (n-1) * i), m_inc (i), m_numel (n)
451  {
452  if (! octave::math::isinf (m_limit))
453  m_limit = limit_internal ();
454  }
455 
456  // The range has a finite number of elements.
457  bool ok (void) const
458  {
459  return (octave::math::isfinite (m_limit)
460  && (m_numel >= 0 || m_numel == -2));
461  }
462 
463  double base (void) const { return m_base; }
464  double limit (void) const { return m_limit; }
465  double inc (void) const { return m_inc; }
466  double increment (void) const { return m_inc; }
467 
468  // We adjust the limit to be the final value, so return that. We
469  // could introduce a new variable to store the final value separately,
470  // but it seems like that would just add confusion. If we changed
471  // the meaning of the limit function, we would change the behavior of
472  // programs that expect limit to be the final value instead of the
473  // value of the limit when the range was created. This problem will
474  // be fixed with the new template range class.
475  double final_value (void) const { return m_limit; }
476 
477  octave_idx_type numel (void) const { return m_numel; }
478 
479  dim_vector dims (void) const { return dim_vector (1, m_numel); }
480 
481  octave_idx_type rows (void) const { return 1; }
482 
483  octave_idx_type cols (void) const { return numel (); }
484  octave_idx_type columns (void) const { return numel (); }
485 
486  bool isempty (void) const { return numel () == 0; }
487 
488  OCTAVE_API bool all_elements_are_ints (void) const;
489 
490  OCTAVE_API Matrix matrix_value (void) const;
491 
492  OCTAVE_API double min (void) const;
493  OCTAVE_API double max (void) const;
494 
495  OCTAVE_API void sort_internal (bool ascending = true);
496  OCTAVE_API void sort_internal (Array<octave_idx_type>& sidx, bool ascending = true);
497 
498  OCTAVE_API Matrix diag (octave_idx_type k = 0) const;
499 
500  OCTAVE_API Range sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const;
502  sortmode mode = ASCENDING) const;
503 
504  OCTAVE_API sortmode issorted (sortmode mode = ASCENDING) const;
505 
506  OCTAVE_API octave_idx_type nnz (void) const;
507 
508  // Support for single-index subscripting, without generating matrix cache.
509 
510  OCTAVE_API double checkelem (octave_idx_type i) const;
511  OCTAVE_API double checkelem (octave_idx_type i, octave_idx_type j) const;
512 
513  OCTAVE_API double elem (octave_idx_type i) const;
514  double elem (octave_idx_type /* i */, octave_idx_type j) const
515  { return elem (j); }
516 
517  double operator () (octave_idx_type i) const { return elem (i); }
518  double operator () (octave_idx_type i, octave_idx_type j) const
519  { return elem (i, j); }
520 
521  OCTAVE_API Array<double> index (const octave::idx_vector& i) const;
522 
523  OCTAVE_API void set_base (double b);
524 
525  OCTAVE_API void set_limit (double l);
526 
527  OCTAVE_API void set_inc (double i);
528 
529  friend OCTAVE_API std::ostream& operator << (std::ostream& os,
530  const Range& r);
531  friend OCTAVE_API std::istream& operator >> (std::istream& is, Range& r);
532 
533  friend OCTAVE_API Range operator - (const Range& r);
534  friend OCTAVE_API Range operator + (double x, const Range& r);
535  friend OCTAVE_API Range operator + (const Range& r, double x);
536  friend OCTAVE_API Range operator - (double x, const Range& r);
537  friend OCTAVE_API Range operator - (const Range& r, double x);
538  friend OCTAVE_API Range operator * (double x, const Range& r);
539  friend OCTAVE_API Range operator * (const Range& r, double x);
540 
541 private:
542 
543  double m_base;
544  double m_limit;
545  double m_inc;
546 
548 
549  OCTAVE_API octave_idx_type numel_internal (void) const;
550 
551  OCTAVE_API double limit_internal (void) const;
552 
553  OCTAVE_API void init (void);
554 
555 protected:
556 
557  // NOTE: The following constructor may be removed when the arithmetic
558  // operators are removed.
559 
560  // For operators' usage (to allow all values to be set directly).
561  Range (double b, double l, double i, octave_idx_type n)
562  : m_base (b), m_limit (l), m_inc (i), m_numel (n)
563  { }
564 };
565 
566 #if defined (OCTAVE_PROVIDE_DEPRECATED_SYMBOLS)
567 OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
568 extern OCTAVE_API Range operator - (const Range& r);
569 
570 OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
571 extern OCTAVE_API Range operator + (double x, const Range& r);
572 
573 OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
574 extern OCTAVE_API Range operator + (const Range& r, double x);
575 
576 OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
577 extern OCTAVE_API Range operator - (double x, const Range& r);
578 
579 OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
580 extern OCTAVE_API Range operator - (const Range& r, double x);
581 
582 OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
583 extern OCTAVE_API Range operator * (double x, const Range& r);
584 
585 OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
586 extern OCTAVE_API Range operator * (const Range& r, double x);
587 #endif
588 
589 #endif
template OCTAVE_API std::ostream & operator<<(std::ostream &, const Array< bool > &)
OCTAVE_END_NAMESPACE(octave)
ComplexColumnVector operator*(const ComplexMatrix &m, const ColumnVector &a)
Definition: CColVector.cc:293
ComplexColumnVector operator-(const ComplexColumnVector &x)
Definition: CColVector.h:156
ComplexColumnVector operator+(const ComplexColumnVector &x)
Definition: CColVector.h:156
static int elem
Definition: __contourc__.cc:54
std::istream & operator>>(std::istream &is, SparseBoolMatrix &a)
Definition: boolSparse.cc:279
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
OCTARRAY_API void clear(void)
Definition: Array-base.cc:109
OCTARRAY_OVERRIDABLE_FUNC_API Array< T, Alloc > reshape(octave_idx_type nr, octave_idx_type nc) const
Size of the specified dimension.
Definition: Array.h:635
OCTARRAY_API T * fortran_vec(void)
Size of the specified dimension.
Definition: Array-base.cc:1766
OCTARRAY_API Array< T, Alloc > diag(octave_idx_type k=0) const
Get the kth super or subdiagonal.
Definition: Array-base.cc:2543
OCTARRAY_OVERRIDABLE_FUNC_API T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:524
Definition: dMatrix.h:42
Definition: Range.h:401
double increment(void) const
Definition: Range.h:466
bool isempty(void) const
Definition: Range.h:486
double m_inc
Definition: Range.h:545
bool ok(void) const
Definition: Range.h:457
octave_idx_type rows(void) const
Definition: Range.h:481
octave_idx_type m_numel
Definition: Range.h:547
dim_vector dims(void) const
Definition: Range.h:479
double final_value(void) const
Definition: Range.h:475
Range(const Range &r)=default
octave_idx_type cols(void) const
Definition: Range.h:483
double inc(void) const
Definition: Range.h:465
double base(void) const
Definition: Range.h:463
~Range(void)=default
double m_limit
Definition: Range.h:544
Range(double b, double l, double i, octave_idx_type n)
Definition: Range.h:561
double m_base
Definition: Range.h:543
double limit(void) const
Definition: Range.h:464
octave_idx_type columns(void) const
Definition: Range.h:484
octave_idx_type numel(void) const
Definition: Range.h:477
double elem(octave_idx_type, octave_idx_type j) const
Definition: Range.h:514
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
bool isvector(void) const
Definition: dim-vector.h:395
bool is_colon(void) const
Definition: idx-vector.h:557
void loop(octave_idx_type n, Functor body) const
Definition: idx-vector.h:819
octave_idx_type length(octave_idx_type n=0) const
Definition: idx-vector.h:538
dim_vector orig_dimensions(void) const
Definition: idx-vector.h:575
octave_idx_type extent(octave_idx_type n) const
Definition: idx-vector.h:541
range(const T &base, const T &increment, const T &limit, bool reverse=false)
Definition: Range.h:63
static range< T > make_n_element_range(const T &base, const T &increment, octave_idx_type numel, bool reverse=false)
Definition: Range.h:94
range(const T &base, const T &increment, const T &limit, octave_idx_type numel, bool reverse=false)
Definition: Range.h:85
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
bool isfinite(double x)
Definition: lo-mappers.h:192
bool isinf(double x)
Definition: lo-mappers.h:203
F77_RET_T const F77_DBLE * x
#define OCTAVE_API
Definition: main.in.cc:55
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
T::size_type numel(const T &str)
Definition: oct-string.cc:71
class OCTAVE_API range
Definition: range-fwd.h:33