Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sorted_interval_list.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef ORTOOLS_UTIL_SORTED_INTERVAL_LIST_H_
15#define ORTOOLS_UTIL_SORTED_INTERVAL_LIST_H_
16
17#include <cstddef>
18#include <cstdint>
19#include <iterator>
20#include <ostream>
21#include <set>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/container/inlined_vector.h"
27#include "absl/types/span.h"
29
30namespace operations_research {
31
36#if !defined(SWIG)
52 class Iterator;
53#endif // !defined(SWIG)
54
56 explicit ClosedInterval(int64_t v) : start(v), end(v) {}
57 ClosedInterval(int64_t s, int64_t e) : start(s), end(e) {
58 DLOG_IF(DFATAL, s > e) << "Invalid ClosedInterval(" << s << ", " << e
59 << ")";
60 }
61
62 std::string DebugString() const;
63 constexpr bool operator==(const ClosedInterval& other) const {
64 return start == other.start && end == other.end;
65 }
66
67 // Because we mainly manipulate vector of disjoint intervals, we only need to
68 // sort by the start. We do not care about the order in which interval with
69 // the same start appear since they will always be merged into one interval.
70 constexpr bool operator<(const ClosedInterval& other) const {
71 return start < other.start;
72 }
73
74 template <typename H>
75 friend H AbslHashValue(H h, const ClosedInterval& interval) {
76 return H::combine(std::move(h), interval.start, interval.end);
77 }
78
79 int64_t start = 0; // Inclusive.
80 int64_t end = 0; // Inclusive.
81};
82
83#if !defined(SWIG)
86#endif // !defined(SWIG)
87
88std::ostream& operator<<(std::ostream& out, const ClosedInterval& interval);
89std::ostream& operator<<(std::ostream& out,
90 const std::vector<ClosedInterval>& intervals);
91
101 absl::Span<const ClosedInterval> intervals);
102
113class Domain {
114 public:
117
118#if !defined(SWIG)
120 Domain(const Domain& other) : intervals_(other.intervals_) {}
121
123 Domain& operator=(const Domain& other) {
124 intervals_ = other.intervals_;
125 return *this;
126 }
127
129 Domain(Domain&& other) noexcept : intervals_(std::move(other.intervals_)) {}
130
132 Domain& operator=(Domain&& other) noexcept {
133 intervals_ = std::move(other.intervals_);
134 return *this;
135 }
136#endif // !defined(SWIG)
137
139 explicit Domain(int64_t value);
140
145 Domain(int64_t left, int64_t right);
146
150 static Domain AllValues();
151
153 static Domain LowerOrEqual(int64_t value);
154
156 static Domain GreaterOrEqual(int64_t value);
157
162 static Domain FromValues(std::vector<int64_t> values);
163
170 static Domain FromIntervals(absl::Span<const ClosedInterval> intervals);
171
180 absl::Span<const int64_t> flat_intervals);
181
194 const std::vector<std::vector<int64_t> >& intervals);
195
204 static Domain FromFlatIntervals(const std::vector<int64_t>& flat_intervals);
205
213 std::vector<int64_t> FlattenedIntervals() const;
214
215#if !defined(SWIG)
224 public:
226 const absl::InlinedVector<ClosedInterval, 1>& intervals)
227 : value_(intervals.empty() ? int64_t{0} : intervals.front().start),
228 it_(intervals.begin()),
229 end_(intervals.end()) {}
230
231 int64_t operator*() const { return value_; }
232
233 void operator++() {
234 if (value_ == it_->end) {
235 ++it_;
236 if (it_ != end_) value_ = it_->start;
237 } else {
238 ++value_;
239 }
240 }
241
243 absl::InlinedVector<ClosedInterval, 1>::const_iterator end) const {
244 return it_ != end;
245 }
246
247 private:
248 int64_t value_;
249 absl::InlinedVector<ClosedInterval, 1>::const_iterator it_;
250 absl::InlinedVector<ClosedInterval, 1>::const_iterator end_;
251 };
254 absl::InlinedVector<ClosedInterval, 1>::const_iterator end() const {
255 return intervals.end();
256 }
257 const absl::InlinedVector<ClosedInterval, 1>& intervals;
258 };
261 absl::InlinedVector<ClosedInterval, 1>::const_iterator end() const {
262 return intervals.end();
263 }
264 absl::InlinedVector<ClosedInterval, 1> intervals;
265 };
266 DomainIteratorBeginEnd Values() const& { return {this->intervals_}; }
268 return {std::move(this->intervals_)};
269 }
270#endif // !defined(SWIG)
271
275 bool IsEmpty() const;
276
280 int64_t Size() const;
281
286 bool HasTwoValues() const {
287 if (intervals_.size() == 1) {
288 return intervals_[0].end == intervals_[0].start + 1;
289 } else if (intervals_.size() == 2) {
290 return intervals_[0].end == intervals_[0].start &&
291 intervals_[1].end == intervals_[1].start;
292 }
293 return false;
294 }
295
300 int64_t Min() const;
301
306 int64_t Max() const;
307
311 int64_t SmallestValue() const;
312
317 int64_t ClosestValue(int64_t wanted) const;
318
323 int64_t ValueAtOrBefore(int64_t input) const;
324 int64_t ValueAtOrAfter(int64_t input) const;
325
330 Domain PartAroundZero() const;
331
336 bool IsFixed() const;
337
343 int64_t FixedValue() const;
344
348 bool Contains(int64_t value) const;
349
353 int64_t Distance(int64_t value) const;
354
358 bool IsIncludedIn(const Domain& domain) const;
359
364 bool OverlapsWith(const Domain& domain) const;
365
369 Domain Complement() const;
370
377 Domain Negation() const;
378
382 Domain IntersectionWith(const Domain& domain) const;
383
387 Domain UnionWith(const Domain& domain) const;
388
392 Domain AdditionWith(const Domain& domain) const;
393
405 Domain MultiplicationBy(int64_t coeff, bool* exact = nullptr) const;
406
411
424 Domain ContinuousMultiplicationBy(int64_t coeff) const;
425
438 Domain ContinuousMultiplicationBy(const Domain& domain) const;
439
445 Domain DivisionBy(int64_t coeff) const;
446
452 Domain InverseMultiplicationBy(int64_t coeff) const;
453
462 Domain PositiveModuloBySuperset(const Domain& modulo) const;
463
470 Domain PositiveDivisionBySuperset(const Domain& divisor) const;
471
475 Domain SquareSuperset() const;
476
480 Domain QuadraticSuperset(int64_t a, int64_t b, int64_t c, int64_t d) const;
481
502 Domain SimplifyUsingImpliedDomain(const Domain& implied_domain) const;
503
507 std::string ToString() const;
508
512 bool operator<(const Domain& other) const;
513
514 bool operator==(const Domain& other) const {
515 return intervals_ == other.intervals_;
516 }
517
518 bool operator!=(const Domain& other) const {
519 return intervals_ != other.intervals_;
520 }
521
522 template <typename H>
523 friend H AbslHashValue(H h, const Domain& domain) {
524 return H::combine(std::move(h), domain.intervals_);
525 }
526
532 int NumIntervals() const { return intervals_.size(); }
533 ClosedInterval front() const { return intervals_.front(); }
534 ClosedInterval back() const { return intervals_.back(); }
535 ClosedInterval operator[](int i) const { return intervals_[i]; }
536 absl::InlinedVector<ClosedInterval, 1>::const_iterator begin() const {
537 return intervals_.begin();
538 }
539 absl::InlinedVector<ClosedInterval, 1>::const_iterator end() const {
540 return intervals_.end();
541 }
542
543 private:
544 // Same as Negation() but modify the current domain.
545 void NegateInPlace();
546
547 // Some functions relax the domain when its "complexity" (i.e NumIntervals())
548 // become too large.
549 static constexpr int kDomainComplexityLimit = 100;
550
551 // Invariant: will always satisfy IntervalsAreSortedAndNonAdjacent().
552 //
553 // Note that we use InlinedVector for the common case of single internal
554 // interval. This provide a good performance boost when working with a
555 // std::vector<Domain>.
556 absl::InlinedVector<ClosedInterval, 1> intervals_;
557};
558
559std::ostream& operator<<(std::ostream& out, const Domain& domain);
560
561// Returns the sum of smallest k values in the domain.
562int64_t SumOfKMinValueInDomain(const Domain& domain, int k);
563
564// Returns the sum of largest k values in the domain.
565int64_t SumOfKMaxValueInDomain(const Domain& domain, int k);
566
574// TODO(user): Templatize the class on the type of the bounds.
576 public:
578 bool operator()(const ClosedInterval& a, const ClosedInterval& b) const {
579 return a.start != b.start ? a.start < b.start : a.end < b.end;
580 }
581 };
582 typedef std::set<ClosedInterval, IntervalComparator> IntervalSet;
583 typedef IntervalSet::iterator Iterator;
584 typedef IntervalSet::const_iterator ConstIterator;
585
588 const std::vector<ClosedInterval>& intervals);
589
595 // TODO(user): Explain why we favored this API to the more natural
596 // input std::vector<ClosedInterval> or std::vector<std::pair<int, int>>.
597 SortedDisjointIntervalList(const std::vector<int64_t>& starts,
598 const std::vector<int64_t>& ends);
599 SortedDisjointIntervalList(const std::vector<int>& starts,
600 const std::vector<int>& ends);
601
606 int64_t end);
607
617 Iterator InsertInterval(int64_t start, int64_t end);
618
625 void InsertIntervals(const std::vector<int64_t>& starts,
626 const std::vector<int64_t>& ends);
627 void InsertIntervals(const std::vector<int>& starts,
628 const std::vector<int>& ends);
629
633 int NumIntervals() const { return intervals_.size(); }
634
643 Iterator FirstIntervalGreaterOrEqual(int64_t value) const;
644 Iterator LastIntervalLessOrEqual(int64_t value) const;
645
646 std::string DebugString() const;
647
658 ConstIterator begin() const { return intervals_.begin(); }
659 ConstIterator end() const { return intervals_.end(); }
660
664 const ClosedInterval& last() const { return *intervals_.rbegin(); }
665
666 void clear() { intervals_.clear(); }
667 void swap(SortedDisjointIntervalList& other) noexcept {
668 intervals_.swap(other.intervals_);
669 }
670
671 private:
672 template <class T>
673 void InsertAll(const std::vector<T>& starts, const std::vector<T>& ends);
674
675 IntervalSet intervals_;
676};
677
678// Implementation details.
679
680#if !defined(SWIG)
682 public:
683 using value_type = int64_t;
684 using difference_type = std::ptrdiff_t;
685
686 Iterator(const Iterator&) = default;
687
688 int64_t operator*() const { return static_cast<int64_t>(current_); }
689
691 ++current_;
692 return *this;
693 }
694 void operator++(int) { ++current_; }
695
696 bool operator==(Iterator other) const { return current_ == other.current_; }
697 bool operator!=(Iterator other) const { return current_ != other.current_; }
698
699 Iterator& operator=(const Iterator&) = default;
700
701 static Iterator Begin(ClosedInterval interval) {
702 AssertNotFullInt64Range(interval);
703 return Iterator(static_cast<uint64_t>(interval.start));
704 }
705 static Iterator End(ClosedInterval interval) {
706 AssertNotFullInt64Range(interval);
707 return Iterator(static_cast<uint64_t>(interval.end) + 1);
708 }
709
710 private:
711 explicit Iterator(uint64_t current) : current_(current) {}
712
713 // Triggers a DCHECK-failure when `interval` represents the full int64_t
714 // range.
715 static void AssertNotFullInt64Range(ClosedInterval interval) {
716 DCHECK_NE(static_cast<uint64_t>(interval.start),
717 static_cast<uint64_t>(interval.end) + 1)
718 << "Iteration over the full int64_t range is not supported.";
719 }
720
721 // Implementation note: In C++, integer overflow is well-defined only for
722 // unsigned integers. To avoid any compilation issues or UBSan failures, the
723 // iterator uses uint64_t internally and relies on the fact that since C++20
724 // unsigned->signed conversion is well-defined for all values using modulo
725 // arithmetic.
726 uint64_t current_;
727};
728#if __cplusplus >= 202002L
729static_assert(std::input_iterator<ClosedInterval::Iterator>);
730#endif
731// begin()/end() are required for iteration over ClosedInterval in a range for
732// loop.
737 return ClosedInterval::Iterator::End(interval);
738}
739#endif // !defined(SWIG)
740
741} // namespace operations_research
742
743#endif // ORTOOLS_UTIL_SORTED_INTERVAL_LIST_H_
static Iterator Begin(ClosedInterval interval)
static Iterator End(ClosedInterval interval)
Iterator & operator=(const Iterator &)=default
DomainIterator(const absl::InlinedVector< ClosedInterval, 1 > &intervals)
bool operator!=(absl::InlinedVector< ClosedInterval, 1 >::const_iterator end) const
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) const
static Domain FromVectorIntervals(const std::vector< std::vector< int64_t > > &intervals)
bool OverlapsWith(const Domain &domain) const
DomainIteratorBeginEndWithOwnership Values() const &&
Domain(Domain &&other) noexcept
Move constructor.
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
static Domain FromValues(std::vector< int64_t > values)
bool operator<(const Domain &other) const
friend H AbslHashValue(H h, const Domain &domain)
int64_t ValueAtOrAfter(int64_t input) const
Domain(const Domain &other)
Copy constructor (mandatory as we define the move constructor).
std::vector< int64_t > FlattenedIntervals() const
Domain IntersectionWith(const Domain &domain) const
static Domain LowerOrEqual(int64_t value)
Domain QuadraticSuperset(int64_t a, int64_t b, int64_t c, int64_t d) const
static Domain FromIntervals(absl::Span< const ClosedInterval > intervals)
Domain ContinuousMultiplicationBy(int64_t coeff) const
bool Contains(int64_t value) const
DomainIteratorBeginEnd Values() const &
Domain PositiveModuloBySuperset(const Domain &modulo) const
Domain AdditionWith(const Domain &domain) const
bool IsIncludedIn(const Domain &domain) const
static Domain FromFlatIntervals(const std::vector< int64_t > &flat_intervals)
Domain()
By default, Domain will be empty.
Domain DivisionBy(int64_t coeff) const
int64_t Distance(int64_t value) const
Domain & operator=(const Domain &other)
Copy operator (mandatory as we define the move operator).
static Domain FromFlatSpanOfIntervals(absl::Span< const int64_t > flat_intervals)
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
Domain PositiveDivisionBySuperset(const Domain &divisor) const
absl::InlinedVector< ClosedInterval, 1 >::const_iterator begin() const
static Domain GreaterOrEqual(int64_t value)
Domain UnionWith(const Domain &domain) const
bool operator==(const Domain &other) const
Domain & operator=(Domain &&other) noexcept
Move operator.
ClosedInterval operator[](int i) const
Domain InverseMultiplicationBy(int64_t coeff) const
int64_t ValueAtOrBefore(int64_t input) const
bool operator!=(const Domain &other) const
int64_t ClosestValue(int64_t wanted) const
void InsertIntervals(const std::vector< int64_t > &starts, const std::vector< int64_t > &ends)
void swap(SortedDisjointIntervalList &other) noexcept
Iterator InsertInterval(int64_t start, int64_t end)
SortedDisjointIntervalList BuildComplementOnInterval(int64_t start, int64_t end)
std::set< ClosedInterval, IntervalComparator > IntervalSet
OR-Tools root namespace.
ClosedInterval::Iterator end(ClosedInterval interval)
int64_t SumOfKMinValueInDomain(const Domain &domain, int k)
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
bool IntervalsAreSortedAndNonAdjacent(absl::Span< const ClosedInterval > intervals)
ClosedInterval::Iterator begin(ClosedInterval interval)
int64_t SumOfKMaxValueInDomain(const Domain &domain, int k)
static int input(yyscan_t yyscanner)
Definition model.h:50
constexpr bool operator==(const ClosedInterval &other) const
constexpr bool operator<(const ClosedInterval &other) const
friend H AbslHashValue(H h, const ClosedInterval &interval)
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
const absl::InlinedVector< ClosedInterval, 1 > & intervals
bool operator()(const ClosedInterval &a, const ClosedInterval &b) const