GNU Octave 7.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-2022 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
41namespace octave
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
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
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
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
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}
397
398class
399Range
400{
401public:
402
403 OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
404 Range (void)
405 : m_base (0), m_limit (0), m_inc (0), m_numel (0)
406 { }
407
408 // Assume range is already properly constructed, so just copy internal
409 // values. However, we set LIMIT to the computed final value because
410 // that mimics the behavior of the other Range class constructors that
411 // reset limit to the computed final value.
412
413 OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
414 Range (const octave::range<double>& r)
415 : m_base (r.base ()), m_limit (r.final_value ()), m_inc (r.increment ()),
416 m_numel (r.numel ())
417 { }
418
419 Range (const Range& r) = default;
420
421 Range& operator = (const Range& r) = default;
422
423 ~Range (void) = default;
424
425 OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
426 Range (double b, double l)
427 : m_base (b), m_limit (l), m_inc (1), m_numel (numel_internal ())
428 {
429 if (! octave::math::isinf (m_limit))
430 m_limit = limit_internal ();
431 }
432
433 OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
434 Range (double b, double l, double i)
435 : m_base (b), m_limit (l), m_inc (i), m_numel (numel_internal ())
436 {
437 if (! octave::math::isinf (m_limit))
438 m_limit = limit_internal ();
439 }
440
441 // NOTE: The following constructor may be deprecated and removed after
442 // the arithmetic operators are removed.
443
444 // For operators' usage (to preserve element count) and to create
445 // constant row vectors (obsolete usage).
446
447 OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
448 Range (double b, double i, octave_idx_type n)
449 : m_base (b), m_limit (b + (n-1) * i), m_inc (i), m_numel (n)
450 {
451 if (! octave::math::isinf (m_limit))
452 m_limit = limit_internal ();
453 }
454
455 // The range has a finite number of elements.
456 bool ok (void) const
457 {
458 return (octave::math::isfinite (m_limit)
459 && (m_numel >= 0 || m_numel == -2));
460 }
461
462 double base (void) const { return m_base; }
463 double limit (void) const { return m_limit; }
464 double inc (void) const { return m_inc; }
465 double increment (void) const { return m_inc; }
466
467 // We adjust the limit to be the final value, so return that. We
468 // could introduce a new variable to store the final value separately,
469 // but it seems like that would just add confusion. If we changed
470 // the meaning of the limit function, we would change the behavior of
471 // programs that expect limit to be the final value instead of the
472 // value of the limit when the range was created. This problem will
473 // be fixed with the new template range class.
474 double final_value (void) const { return m_limit; }
475
476 octave_idx_type numel (void) const { return m_numel; }
477
478 dim_vector dims (void) const { return dim_vector (1, m_numel); }
479
480 octave_idx_type rows (void) const { return 1; }
481
482 octave_idx_type cols (void) const { return numel (); }
483 octave_idx_type columns (void) const { return numel (); }
484
485 bool isempty (void) const { return numel () == 0; }
486
487 OCTAVE_API bool all_elements_are_ints (void) const;
488
489 OCTAVE_API Matrix matrix_value (void) const;
490
491 OCTAVE_API double min (void) const;
492 OCTAVE_API double max (void) const;
493
494 OCTAVE_API void sort_internal (bool ascending = true);
495 OCTAVE_API void sort_internal (Array<octave_idx_type>& sidx, bool ascending = true);
496
497 OCTAVE_API Matrix diag (octave_idx_type k = 0) const;
498
499 OCTAVE_API Range sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const;
501 sortmode mode = ASCENDING) const;
502
503 OCTAVE_API sortmode issorted (sortmode mode = ASCENDING) const;
504
505 OCTAVE_API octave_idx_type nnz (void) const;
506
507 // Support for single-index subscripting, without generating matrix cache.
508
509 OCTAVE_API double checkelem (octave_idx_type i) const;
510 OCTAVE_API double checkelem (octave_idx_type i, octave_idx_type j) const;
511
512 OCTAVE_API double elem (octave_idx_type i) const;
513 double elem (octave_idx_type /* i */, octave_idx_type j) const
514 { return elem (j); }
515
516 double operator () (octave_idx_type i) const { return elem (i); }
517 double operator () (octave_idx_type i, octave_idx_type j) const
518 { return elem (i, j); }
519
520 OCTAVE_API Array<double> index (const octave::idx_vector& i) const;
521
522 OCTAVE_API void set_base (double b);
523
524 OCTAVE_API void set_limit (double l);
525
526 OCTAVE_API void set_inc (double i);
527
528 friend OCTAVE_API std::ostream& operator << (std::ostream& os,
529 const Range& r);
530 friend OCTAVE_API std::istream& operator >> (std::istream& is, Range& r);
531
532 friend OCTAVE_API Range operator - (const Range& r);
533 friend OCTAVE_API Range operator + (double x, const Range& r);
534 friend OCTAVE_API Range operator + (const Range& r, double x);
535 friend OCTAVE_API Range operator - (double x, const Range& r);
536 friend OCTAVE_API Range operator - (const Range& r, double x);
537 friend OCTAVE_API Range operator * (double x, const Range& r);
538 friend OCTAVE_API Range operator * (const Range& r, double x);
539
540private:
541
542 double m_base;
543 double m_limit;
544 double m_inc;
545
547
548 OCTAVE_API octave_idx_type numel_internal (void) const;
549
550 OCTAVE_API double limit_internal (void) const;
551
552 OCTAVE_API void init (void);
553
554protected:
555
556 // NOTE: The following constructor may be removed when the arithmetic
557 // operators are removed.
558
559 // For operators' usage (to allow all values to be set directly).
560 Range (double b, double l, double i, octave_idx_type n)
561 : m_base (b), m_limit (l), m_inc (i), m_numel (n)
562 { }
563};
564
565#if defined (OCTAVE_PROVIDE_DEPRECATED_SYMBOLS)
566OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
567extern OCTAVE_API Range operator - (const Range& r);
568
569OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
570extern OCTAVE_API Range operator + (double x, const Range& r);
571
572OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
573extern OCTAVE_API Range operator + (const Range& r, double x);
574
575OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
576extern OCTAVE_API Range operator - (double x, const Range& r);
577
578OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
579extern OCTAVE_API Range operator - (const Range& r, double x);
580
581OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
582extern OCTAVE_API Range operator * (double x, const Range& r);
583
584OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable")
585extern OCTAVE_API Range operator * (const Range& r, double x);
586#endif
587
588#endif
template OCTAVE_API std::ostream & operator<<(std::ostream &, const Array< bool > &)
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
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:504
OCTARRAY_API void clear(void)
Definition: Array.cc:87
OCTARRAY_API T * fortran_vec(void)
Size of the specified dimension.
Definition: Array.cc:1744
Array< T, Alloc > reshape(octave_idx_type nr, octave_idx_type nc) const
Size of the specified dimension.
Definition: Array.h:595
OCTARRAY_API Array< T, Alloc > diag(octave_idx_type k=0) const
Get the kth super or subdiagonal.
Definition: Array.cc:2521
Definition: dMatrix.h:42
Definition: Range.h:400
double increment(void) const
Definition: Range.h:465
bool isempty(void) const
Definition: Range.h:485
double m_inc
Definition: Range.h:544
bool ok(void) const
Definition: Range.h:456
octave_idx_type rows(void) const
Definition: Range.h:480
octave_idx_type m_numel
Definition: Range.h:546
dim_vector dims(void) const
Definition: Range.h:478
double final_value(void) const
Definition: Range.h:474
Range(const Range &r)=default
octave_idx_type cols(void) const
Definition: Range.h:482
double inc(void) const
Definition: Range.h:464
double base(void) const
Definition: Range.h:462
~Range(void)=default
double m_limit
Definition: Range.h:543
Range(double b, double l, double i, octave_idx_type n)
Definition: Range.h:560
double m_base
Definition: Range.h:542
double limit(void) const
Definition: Range.h:463
octave_idx_type columns(void) const
Definition: Range.h:483
octave_idx_type numel(void) const
Definition: Range.h:476
double elem(octave_idx_type, octave_idx_type j) const
Definition: Range.h:513
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:556
void loop(octave_idx_type n, Functor body) const
Definition: idx-vector.h:818
octave_idx_type extent(octave_idx_type n) const
Definition: idx-vector.h:540
dim_vector orig_dimensions(void) const
Definition: idx-vector.h:574
octave_idx_type length(octave_idx_type n=0) const
Definition: idx-vector.h:537
range(const T &base, const T &increment, const T &limit, bool reverse=false)
Definition: Range.h:63
range(const T &base, const T &increment, const T &limit, octave_idx_type numel, bool reverse=false)
Definition: Range.h:85
static range< T > make_n_element_range(const T &base, const T &increment, octave_idx_type numel, bool reverse=false)
Definition: Range.h:94
F77_RET_T const F77_DBLE * x
#define OCTAVE_API
Definition: main.in.cc:55
bool isfinite(double x)
Definition: lo-mappers.h:192
bool isinf(double x)
Definition: lo-mappers.h:203
static OCTAVE_NORETURN void err_index_out_of_range(void)
Definition: idx-vector.cc:52
class OCTAVE_API range
Definition: range-fwd.h:33
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