[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

counting_iterator.hxx
1/************************************************************************/
2/* */
3/* Copyright 2015 by Thorsten Beier */
4/* */
5/* This file is part of the VIGRA computer vision library. */
6/* The VIGRA Website is */
7/* http://hci.iwr.uni-heidelberg.de/vigra/ */
8/* Please direct questions, bug reports, and contributions to */
9/* ullrich.koethe@iwr.uni-heidelberg.de or */
10/* vigra@informatik.uni-hamburg.de */
11/* */
12/* Permission is hereby granted, free of charge, to any person */
13/* obtaining a copy of this software and associated documentation */
14/* files (the "Software"), to deal in the Software without */
15/* restriction, including without limitation the rights to use, */
16/* copy, modify, merge, publish, distribute, sublicense, and/or */
17/* sell copies of the Software, and to permit persons to whom the */
18/* Software is furnished to do so, subject to the following */
19/* conditions: */
20/* */
21/* The above copyright notice and this permission notice shall be */
22/* included in all copies or substantial portions of the */
23/* Software. */
24/* */
25/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32/* OTHER DEALINGS IN THE SOFTWARE. */
33/* */
34/************************************************************************/
35
36#ifndef VIGRA_COUNTING_ITERATOR_HXX
37#define VIGRA_COUNTING_ITERATOR_HXX
38
39
40#include <cmath>
41#include <iterator>
42#include <limits>
43#include <type_traits>
44#include "error.hxx"
45#include "tinyvector.hxx"
46
47namespace vigra {
48
49namespace detail {
50
51template <class T, bool is_float=false>
52struct CountingIteratorCompare
53{
54 // use exact comparison for integer counting
55 static bool equal(T left, T right, T /* step */)
56 {
57 return left == right;
58 }
59 static bool not_equal(T left, T right, T /* step */)
60 {
61 return left != right;
62 }
63 static bool less(T left, T right, T step)
64 {
65 // NOTE: the more efficient '(right - left)*step > 0'
66 // fails for unsigned arguments
67 return step > 0
68 ? left < right
69 : left > right;
70 }
71 static bool less_equal(T left, T right, T step)
72 {
73 return step > 0
74 ? left <= right
75 : left >= right;
76 }
77 static bool greater(T left, T right, T step)
78 {
79 return step > 0
80 ? left > right
81 : left < right;
82 }
83 static bool greater_equal(T left, T right, T step)
84 {
85 return step > 0
86 ? left >= right
87 : left <= right;
88 }
89 // integer counting: if the raw distance is not divisible by step,
90 // we must round upwards
91 static std::ptrdiff_t distance(T from, T to, T step)
92 {
93 const double diff = (double(to) - double(from)) / double(step);
94 return diff > 0.0
95 ? (std::ptrdiff_t)std::ceil(diff)
96 : (std::ptrdiff_t)std::floor(diff);
97 }
98};
99
100template <class T>
101struct CountingIteratorCompare<T, true>
102{
103 typedef std::numeric_limits<T> limit;
104
105 // use comparison with tolerance for floating-point counting
106 // (the natural epsilon is 0.5*step)
107 static bool equal(T left, T right, T step)
108 {
109 return std::fabs(right-left) <= 0.5*std::fabs(step);
110 }
111 static bool not_equal(T left, T right, T step)
112 {
113 return std::fabs(right-left) > 0.5*std::fabs(step);
114 }
115 static bool less(T left, T right, T step)
116 {
117 return step > 0.0
118 ? right - left > 0.5*step
119 : right - left < 0.5*step;
120 }
121 static bool less_equal(T left, T right, T step)
122 {
123 return step > 0.0
124 ? left - right < 0.5*step
125 : left - right > 0.5*step;
126 }
127 static bool greater(T left, T right, T step)
128 {
129 return step > 0.0
130 ? left - right > 0.5*step
131 : left - right < 0.5*step;
132 }
133 static bool greater_equal(T left, T right, T step)
134 {
135 return step > 0.0
136 ? right - left < 0.5*step
137 : right - left > 0.5*step;
138 }
139 // floating-point counting: if the raw distance is not divisible by step,
140 // we round to nearest if the difference is small, otherwise upwards
141 static std::ptrdiff_t distance(T from, T to, T step)
142 {
143 const double diff = (double(to) - double(from)) / double(step);
144 return diff > 0.0
145 ? (std::ptrdiff_t)std::ceil(diff*(1.0-2.0*limit::epsilon()))
146 : (std::ptrdiff_t)std::floor(diff*(1.0-2.0*limit::epsilon()));
147 }
148};
149
150} // namespace detail
151
152/** \addtogroup RangesAndPoints
153*/
154//@{
155
156 /** \brief Iterator that counts upwards or downwards with a given step size.
157
158 This iterator replicates the functionality of Python's
159 well-known range-function. It is especially convenient in
160 range-based for-loops. <tt>CountingIterator</tt> also works for
161 floating-point counting.
162
163 <b>Usage:</b>
164
165 <b>\#include</b> <vigra/counting_iterator.hxx><br>
166 Namespace: vigra
167
168 You will normally construct instances of this iterator with
169 one of the <tt>range()</tt> factory functions. There are three versions
170 of this function <tt>range(end)</tt>, <tt>range(begin, end)</tt>, and
171 <tt>range(begin, end, step)</tt>.
172 \code
173 // count upwards from 0 to 4
174 for(int i: range(5))
175 std::cout << i << " "; // prints '0 1 2 3 4'
176
177 // count upwards from 4 to 7
178 for(int i: range(4, 8))
179 std::cout << i << " "; // prints '4 5 6 7'
180
181 // count upwards from 0 to 9 with step 3
182 for(int i: range(0, 9, 3))
183 std::cout << i << " "; // prints '0 3 6'
184
185 // likewise (note upper bound)
186 for(int i: range(0, 7, 3))
187 std::cout << i << " "; // prints '0 3 6'
188
189 // count downwards from 4 to 1 with step -1
190 for(int i: range(4, 0))
191 std::cout << i << " "; // prints '4 3 2 1'
192
193 // count downwards from 8 to 2 with step -2
194 for(int i: range(8, 0, -2))
195 std::cout << i << " "; // prints '8 6 4 2'
196 \endcode
197
198 Alternatively, you can create a traditional random-access iterator pair.
199 The end iterator can be conveniently constructed by the begin iterator's
200 <tt>end()</tt> function:
201 \code
202 auto iter = range(5),
203 end = iter.end();
204 std::cout << std::accumulate(iter, end, 0) << std::endl; // prints '10'
205 \endcode
206
207 <tt>range()</tt> and <tt>CountingIterator</tt> also work for floating-point
208 arguments. As in the integer case, the upper bound is excluded from the range
209 if it can be reached by an integer multiple of the step (within machine
210 epsilon):
211 \code
212 for(auto i: range(1.0, 1.6, 0.1)) // 1.6 is excluded
213 std::cout << i << " "; // prints '1 1.1 1.2 1.3 1.4 1.5'
214
215 for(auto i: range(1.0, 1.61, 0.1)) // 1.6 is included
216 std::cout << i << " "; // prints '1 1.1 1.2 1.3 1.4 1.5 1.6'
217 \endcode
218
219 If you use an iterator pair, you can make clear which behavior you want
220 by using either <tt>iter < end</tt> or <tt>iter <= end</tt> to terminate
221 the loop:
222 \code
223 auto iter = range(1.0, 1.6, 0.1),
224 end = iter.end();
225 for(; iter < end; ++iter) // exclude upper bound
226 std::cout << *iter << " "; // prints '1 1.1 1.2 1.3 1.4 1.5'
227
228 iter = range(1.0, 1.6, 0.1);
229 for(; iter <= end; ++iter) // include upper bound
230 std::cout << *iter << " "; // prints '1 1.1 1.2 1.3 1.4 1.5 1.6'
231 \endcode
232
233 Note that the termination condition is still <tt>iter <= end</tt>, even
234 when the iterator counts downwards:
235 \code
236 auto iter = range(1.6, 1.0, -0.1),
237 end = iter.end();
238 for(; iter <= end; ++iter)
239 std::cout << *iter << " "; // prints '1.6 1.5 1.4 1.3 1.2 1.1 1'
240 \endcode
241 */
242template<class T = std::ptrdiff_t>
244: public std::iterator<std::random_access_iterator_tag,
245 T, std::ptrdiff_t, T const *, T>
246{
247 public:
249 : begin_(0)
250 , end_(0)
251 , step_(1)
252 {}
253
254 CountingIterator(T begin, T end)
255 : begin_(begin)
256 , end_(end)
257 , step_(1)
258 {
259 vigra_precondition(begin <= end,
260 "CountingIterator(): begin must be less or equal to end.");
261 }
262
263 CountingIterator(T begin, T end, T step)
264 : begin_(begin)
265 , end_(end)
266 , step_(step)
267 {
268 vigra_precondition(step != 0,
269 "CountingIterator(): step must be non-zero.");
270 vigra_precondition((step > 0 && begin <= end) || (step < 0 && begin >= end),
271 "CountingIterator(): sign mismatch between step and (end-begin).");
272 }
273
274 CountingIterator(CountingIterator const & other, ReverseCopyTag)
275 : begin_(other.end_)
276 , end_(other.begin_)
277 , step_(-other.step_)
278 {}
279
280 public:
281
282 CountingIterator begin() const
283 {
284 return *this;
285 }
286
287 CountingIterator end() const
288 {
289 // since the range-based for-loop checks "iter != end",
290 // (end - begin) must be a multiple of step to avoid infinite loops
291 T end = begin_ + step_*Compare::distance(begin_, end_, step_);
292 return CountingIterator(end, end, step_);
293 }
294
295 bool empty() const
296 {
297 return Compare::greater_equal(begin_, end_, step_);
298 }
299
300 CountingIterator& operator++() {begin_ += step_; return *this;} // prefix++
301 CountingIterator operator++(int) {CountingIterator tmp(*this); ++(*this); return tmp;} // postfix++
302 CountingIterator& operator--() {begin_ -= step_; return *this;} // prefix--
303 CountingIterator operator--(int) {CountingIterator tmp(*this); --(*this); return tmp;} // postfix--
304
305 CountingIterator& operator+=(std::ptrdiff_t n)
306 {
307 begin_ += n*step_;
308 return *this;
309 }
310
311 CountingIterator operator+(std::ptrdiff_t n) const
312 {
313 return CountingIterator(*this) += n;
314 }
315
316 CountingIterator& operator-=(std::ptrdiff_t n)
317 {
318 begin_ -= n*step_;
319 return *this;
320 }
321
322 CountingIterator operator-(std::ptrdiff_t n) const
323 {
324 return CountingIterator(*this) -= n;
325 }
326
327 std::ptrdiff_t operator-(const CountingIterator& other) const
328 {
329 return Compare::distance(other.begin_, begin_, step_);
330 }
331
332 bool operator<(CountingIterator const & other) const
333 {
334 return Compare::less(begin_, other.begin_, step_);
335 }
336
337 bool operator<=(CountingIterator const & other) const
338 {
339 return Compare::less_equal(begin_, other.begin_, step_);
340 }
341
342 bool operator>(CountingIterator const & other) const
343 {
344 return Compare::greater(begin_, other.begin_, step_);
345 }
346
347 bool operator>=(CountingIterator const & other) const
348 {
349 return Compare::greater_equal(begin_, other.begin_, step_);
350 }
351
352 bool operator==(const CountingIterator& other) const
353 {
354 return Compare::equal(begin_, other.begin_, step_);
355 }
356
357 bool operator!=(const CountingIterator& other) const
358 {
359 return Compare::not_equal(begin_, other.begin_, step_);
360 }
361
362 T operator[](std::ptrdiff_t n) const {
363 return begin_ + n*step_;
364 }
365
366 T operator*() const {
367 return begin_;
368 }
369
370 T const * operator->() const{
371 return &begin_;
372 }
373
374 private:
375
376 typedef detail::CountingIteratorCompare<T, std::is_floating_point<T>::value> Compare;
377
378 T begin_, end_, step_;
379};
380
381
382template <class T1, class T2, class T3>
384range(T1 begin, T2 end, T3 step)
385{
386 return CountingIterator<T1>(begin, end, step);
387}
388
389template <class T1, class T2>
390inline CountingIterator<T1>
391range(T1 begin, T2 end)
392{
393 return CountingIterator<T1>(begin, end, 1);
394}
395
396template <class T>
397inline CountingIterator<T>
398range(T end)
399{
400 return CountingIterator<T>(0, end, 1);
401}
402
403//@}
404
405} // namespace vigra
406
407#endif
Iterator that counts upwards or downwards with a given step size.
Definition counting_iterator.hxx:246
Class for a single RGB value.
Definition rgbvalue.hxx:128
int floor(FixedPoint< IntBits, FracBits > v)
rounding down.
Definition fixedpoint.hxx:667

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.12.1