Skip to content

Commit 8f43bdb

Browse files
committed
add iterators to sorted_interval_list
1 parent 98a3e8e commit 8f43bdb

1 file changed

Lines changed: 89 additions & 2 deletions

File tree

ortools/util/sorted_interval_list.h

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef OR_TOOLS_UTIL_SORTED_INTERVAL_LIST_H_
1515
#define OR_TOOLS_UTIL_SORTED_INTERVAL_LIST_H_
1616

17+
#include <cstddef>
1718
#include <cstdint>
1819
#include <iterator>
1920
#include <ostream>
@@ -32,21 +33,41 @@ namespace operations_research {
3233
* Represents a closed interval [start, end]. We must have start <= end.
3334
*/
3435
struct ClosedInterval {
36+
#if !defined(SWIG)
37+
/**
38+
* An iterator over the values of a ClosedInterval object.
39+
*
40+
* To iterate over the values, you can use either a range for loop:
41+
*
42+
* ClosedInterval interval = {0, 100};
43+
* for (const int64_t value : interval) { Work(value); }
44+
*
45+
* or a classical for loop:
46+
* for (auto it = begin(interval); it != end(interval); ++it) { Work(*it); }
47+
*
48+
* The iterator is designed to be very efficient, using just a single counter.
49+
* It works correctly for any combination of `start` and `end` except the full
50+
* int64_t range (start == INT64_MIN && end == INT64_MAX).
51+
*/
52+
class Iterator;
53+
#endif // !defined(SWIG)
54+
3555
ClosedInterval() {}
56+
explicit ClosedInterval(int64_t v) : start(v), end(v) {}
3657
ClosedInterval(int64_t s, int64_t e) : start(s), end(e) {
3758
DLOG_IF(DFATAL, s > e) << "Invalid ClosedInterval(" << s << ", " << e
3859
<< ")";
3960
}
4061

4162
std::string DebugString() const;
42-
bool operator==(const ClosedInterval& other) const {
63+
constexpr bool operator==(const ClosedInterval& other) const {
4364
return start == other.start && end == other.end;
4465
}
4566

4667
// Because we mainly manipulate vector of disjoint intervals, we only need to
4768
// sort by the start. We do not care about the order in which interval with
4869
// the same start appear since they will always be merged into one interval.
49-
bool operator<(const ClosedInterval& other) const {
70+
constexpr bool operator<(const ClosedInterval& other) const {
5071
return start < other.start;
5172
}
5273

@@ -59,6 +80,11 @@ struct ClosedInterval {
5980
int64_t end = 0; // Inclusive.
6081
};
6182

83+
#if !defined(SWIG)
84+
inline ClosedInterval::Iterator begin(ClosedInterval interval);
85+
inline ClosedInterval::Iterator end(ClosedInterval interval);
86+
#endif // !defined(SWIG)
87+
6288
std::ostream& operator<<(std::ostream& out, const ClosedInterval& interval);
6389
std::ostream& operator<<(std::ostream& out,
6490
const std::vector<ClosedInterval>& intervals);
@@ -648,6 +674,67 @@ class SortedDisjointIntervalList {
648674
IntervalSet intervals_;
649675
};
650676

677+
// Implementation details.
678+
679+
#if !defined(SWIG)
680+
class ClosedInterval::Iterator {
681+
public:
682+
using value_type = int64_t;
683+
using difference_type = std::ptrdiff_t;
684+
685+
Iterator(const Iterator&) = default;
686+
687+
int64_t operator*() const { return static_cast<int64_t>(current_); }
688+
689+
Iterator& operator++() {
690+
++current_;
691+
return *this;
692+
}
693+
void operator++(int) { ++current_; }
694+
695+
bool operator==(Iterator other) const { return current_ == other.current_; }
696+
bool operator!=(Iterator other) const { return current_ != other.current_; }
697+
698+
Iterator& operator=(const Iterator&) = default;
699+
700+
static Iterator Begin(ClosedInterval interval) {
701+
AssertNotFullInt64Range(interval);
702+
return Iterator(static_cast<uint64_t>(interval.start));
703+
}
704+
static Iterator End(ClosedInterval interval) {
705+
AssertNotFullInt64Range(interval);
706+
return Iterator(static_cast<uint64_t>(interval.end) + 1);
707+
}
708+
709+
private:
710+
explicit Iterator(uint64_t current) : current_(current) {}
711+
712+
// Triggers a DCHECK-failure when `interval` represents the full int64_t
713+
// range.
714+
static void AssertNotFullInt64Range(ClosedInterval interval) {
715+
DCHECK_NE(static_cast<uint64_t>(interval.start),
716+
static_cast<uint64_t>(interval.end) + 1)
717+
<< "Iteration over the full int64_t range is not supported.";
718+
}
719+
720+
// Implementation note: In C++, integer overflow is well-defined only for
721+
// unsigned integers. To avoid any compilation issues or UBSan failures, the
722+
// iterator uses uint64_t internally and relies on the fact that since C++20
723+
// unsigned->signed conversion is well-defined for all values using modulo
724+
// arithmetic.
725+
uint64_t current_;
726+
};
727+
728+
// begin()/end() are required for iteration over ClosedInterval in a range for
729+
// loop.
730+
inline ClosedInterval::Iterator begin(ClosedInterval interval) {
731+
return ClosedInterval::Iterator::Begin(interval);
732+
}
733+
inline ClosedInterval::Iterator end(ClosedInterval interval) {
734+
return ClosedInterval::Iterator::End(interval);
735+
}
736+
#endif // !defined(SWIG)
737+
651738
} // namespace operations_research
652739

653740
#endif // OR_TOOLS_UTIL_SORTED_INTERVAL_LIST_H_

0 commit comments

Comments
 (0)