Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sorted_interval_list.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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
15
16#include <algorithm>
17#include <cmath>
18#include <cstdlib>
19#include <limits>
20#include <map>
21#include <ostream>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/container/inlined_vector.h"
27#include "absl/strings/str_format.h"
28#include "absl/types/span.h"
30#include "ortools/base/types.h"
32
33namespace operations_research {
34
35std::string ClosedInterval::DebugString() const {
36 if (start == end) return absl::StrFormat("[%d]", start);
37 return absl::StrFormat("[%d,%d]", start, end);
38}
39
41 absl::Span<const ClosedInterval> intervals) {
42 for (int i = 1; i < intervals.size(); ++i) {
43 if (intervals[i - 1].start > intervals[i - 1].end) return false;
44 // First test make sure that intervals[i - 1].end + 1 will not overflow.
45 if (intervals[i - 1].end >= intervals[i].start ||
46 intervals[i - 1].end + 1 >= intervals[i].start) {
47 return false;
48 }
49 }
50 return intervals.empty() ? true
51 : intervals.back().start <= intervals.back().end;
52}
53
54namespace {
55
56template <class Intervals>
57std::string IntervalsAsString(const Intervals& intervals) {
58 std::string result;
59 for (ClosedInterval interval : intervals) {
60 result += interval.DebugString();
61 }
62 if (result.empty()) result = "[]";
63 return result;
64}
65
66// Transforms a sorted list of intervals in a sorted DISJOINT list for which
67// IntervalsAreSortedAndNonAdjacent() would return true.
68void UnionOfSortedIntervals(absl::InlinedVector<ClosedInterval, 1>* intervals) {
69 DCHECK(std::is_sorted(intervals->begin(), intervals->end()));
70 const int size = intervals->size();
71 if (size == 0) return;
72
73 int new_size = 1;
74 for (int i = 1; i < size; ++i) {
75 const ClosedInterval& current = (*intervals)[i];
76 const int64_t end = (*intervals)[new_size - 1].end;
77 if (end == std::numeric_limits<int64_t>::max() ||
78 current.start <= end + 1) {
79 (*intervals)[new_size - 1].end = std::max(current.end, end);
80 continue;
81 }
82 (*intervals)[new_size++] = current;
83 }
84 intervals->resize(new_size);
85
86 // This is important for InlinedVector in the case the result is a single
87 // intervals.
88 intervals->shrink_to_fit();
89 DCHECK(IntervalsAreSortedAndNonAdjacent(*intervals));
90}
91
92} // namespace
93
94// TODO(user): Use MathUtil::CeilOfRatio / FloorOfRatio instead.
95int64_t CeilRatio(int64_t value, int64_t positive_coeff) {
96 DCHECK_GT(positive_coeff, 0);
97 const int64_t result = value / positive_coeff;
98 const int64_t adjust = static_cast<int64_t>(result * positive_coeff < value);
99 return result + adjust;
100}
101
102int64_t FloorRatio(int64_t value, int64_t positive_coeff) {
103 DCHECK_GT(positive_coeff, 0);
104 const int64_t result = value / positive_coeff;
105 const int64_t adjust = static_cast<int64_t>(result * positive_coeff > value);
106 return result - adjust;
107}
108
109std::ostream& operator<<(std::ostream& out, const ClosedInterval& interval) {
110 return out << interval.DebugString();
111}
112
113std::ostream& operator<<(std::ostream& out,
114 const std::vector<ClosedInterval>& intervals) {
115 return out << IntervalsAsString(intervals);
116}
117
118std::ostream& operator<<(std::ostream& out, const Domain& domain) {
119 return out << IntervalsAsString(domain);
120}
121
122Domain::Domain(int64_t value) : intervals_({{value, value}}) {}
123
124// HACK(user): We spare significant time if we use an initializer here, because
125// InlineVector<1> is able to recognize the fast path or "exactly one element".
126// I was unable to obtain the same performance with any other recipe, I always
127// had at least 1 more cycle. See BM_SingleIntervalDomainConstructor.
128// Since the constructor takes very few cycles (most likely less than 10),
129// that's quite significant.
130namespace {
131inline ClosedInterval UncheckedClosedInterval(int64_t s, int64_t e) {
132 ClosedInterval i;
133 i.start = s;
134 i.end = e;
135 return i;
136}
137} // namespace
138
139Domain::Domain(int64_t left, int64_t right)
140 : intervals_({UncheckedClosedInterval(left, right)}) {
141 if (left > right) intervals_.clear();
142}
143
145
146Domain Domain::FromValues(std::vector<int64_t> values) {
147 std::sort(values.begin(), values.end());
148 Domain result;
149 for (const int64_t v : values) {
150 if (result.intervals_.empty() || v > result.intervals_.back().end + 1) {
151 result.intervals_.push_back({v, v});
152 } else {
153 result.intervals_.back().end = v;
154 }
155 if (result.intervals_.back().end == std::numeric_limits<int64_t>::max()) {
156 break;
157 }
158 }
159 return result;
160}
161
162Domain Domain::FromIntervals(absl::Span<const ClosedInterval> intervals) {
163 Domain result;
164 result.intervals_.assign(intervals.begin(), intervals.end());
165 std::sort(result.intervals_.begin(), result.intervals_.end());
166 UnionOfSortedIntervals(&result.intervals_);
167 return result;
168}
169
171 absl::Span<const int64_t> flat_intervals) {
172 DCHECK(flat_intervals.size() % 2 == 0) << flat_intervals.size();
173 Domain result;
174 result.intervals_.reserve(flat_intervals.size() / 2);
175 for (int i = 0; i < flat_intervals.size(); i += 2) {
176 result.intervals_.push_back({flat_intervals[i], flat_intervals[i + 1]});
177 }
178 std::sort(result.intervals_.begin(), result.intervals_.end());
179 UnionOfSortedIntervals(&result.intervals_);
180 return result;
181}
182
183Domain Domain::FromFlatIntervals(const std::vector<int64_t>& flat_intervals) {
184 return FromFlatSpanOfIntervals(absl::MakeSpan(flat_intervals));
185}
186
188 const std::vector<std::vector<int64_t>>& intervals) {
189 Domain result;
190 for (const std::vector<int64_t>& interval : intervals) {
191 if (interval.size() == 1) {
192 result.intervals_.push_back({interval[0], interval[0]});
193 } else {
194 DCHECK_EQ(interval.size(), 2);
195 result.intervals_.push_back({interval[0], interval[1]});
196 }
197 }
198 std::sort(result.intervals_.begin(), result.intervals_.end());
199 UnionOfSortedIntervals(&result.intervals_);
200 return result;
201}
202
203bool Domain::IsEmpty() const { return intervals_.empty(); }
204
205bool Domain::IsFixed() const { return Min() == Max(); }
206
207int64_t Domain::Size() const {
208 int64_t size = 0;
209 for (const ClosedInterval interval : intervals_) {
212 }
213 // Because the intervals are closed on both side above, with miss 1 per
214 // interval.
215 size = operations_research::CapAdd(size, intervals_.size());
216 return size;
217}
218
219int64_t Domain::Min() const {
220 DCHECK(!IsEmpty());
221 return intervals_.front().start;
222}
223
224int64_t Domain::Max() const {
225 DCHECK(!IsEmpty());
226 return intervals_.back().end;
227}
228
229int64_t Domain::SmallestValue() const {
230 DCHECK(!IsEmpty());
231 int64_t result = Min();
232 for (const ClosedInterval interval : intervals_) {
233 if (interval.start <= 0 && interval.end >= 0) return 0;
234 for (const int64_t b : {interval.start, interval.end}) {
235 if (b > 0 && b <= std::abs(result)) result = b;
236 if (b < 0 && -b < std::abs(result)) result = b;
237 }
238 }
239 return result;
240}
241
243 for (const ClosedInterval interval : intervals_) {
244 if (interval.start <= 0 && interval.end >= 0) {
245 return Domain(interval.start, interval.end);
246 }
247 }
248 return Domain();
249}
250
251// TODO(user): Use std::upper_bound() like in ValueAtOrBefore() ?
252int64_t Domain::ClosestValue(int64_t wanted) const {
253 DCHECK(!IsEmpty());
254 if (wanted <= intervals_[0].start) {
255 return intervals_[0].start;
256 }
257 int64_t best_point;
258 int64_t best_distance;
259 for (const ClosedInterval interval : intervals_) {
260 if (interval.start <= wanted && wanted <= interval.end) {
261 return wanted;
262 } else if (interval.start >= wanted) {
263 return CapSub(interval.start, wanted) <= best_distance ? interval.start
264 : best_point;
265 } else {
266 best_point = interval.end;
267 best_distance = CapSub(wanted, interval.end);
268 }
269 }
270 return best_point;
271}
272
273int64_t Domain::ValueAtOrBefore(int64_t input) const {
274 // Because we only compare by start and there is no duplicate starts, this
275 // should be the next interval after the one that has a chance to contains
276 // value.
277 auto it = std::upper_bound(intervals_.begin(), intervals_.end(),
279 if (it == intervals_.begin()) return input;
280 --it;
281 return input <= it->end ? input : it->end;
282}
283
284int64_t Domain::ValueAtOrAfter(int64_t input) const {
285 // Because we only compare by start and there is no duplicate starts, this
286 // should be the next interval after the one that has a chance to contains
287 // value.
288 auto it = std::upper_bound(intervals_.begin(), intervals_.end(),
290 if (it == intervals_.end()) return input;
291 const int64_t candidate = it->start;
292 if (it == intervals_.begin()) return candidate;
293 --it;
294 return input <= it->end ? input : candidate;
295}
296
297int64_t Domain::FixedValue() const {
298 DCHECK(IsFixed());
299 return intervals_.front().start;
300}
301
302bool Domain::Contains(int64_t value) const {
303 // Because we only compare by start and there is no duplicate starts, this
304 // should be the next interval after the one that has a chance to contains
305 // value.
306 auto it = std::upper_bound(intervals_.begin(), intervals_.end(),
308 if (it == intervals_.begin()) return false;
309 --it;
310 return value <= it->end;
311}
312
313// TODO(user): Deal with overflow.
314int64_t Domain::Distance(int64_t value) const {
315 int64_t min_distance = std::numeric_limits<int64_t>::max();
316 for (const ClosedInterval interval : intervals_) {
317 if (value >= interval.start && value <= interval.end) return 0;
318 if (interval.start > value) {
319 min_distance = std::min(min_distance, interval.start - value);
320 break;
321 } else {
322 min_distance = value - interval.end;
323 }
324 }
325 return min_distance;
326}
327
328bool Domain::IsIncludedIn(const Domain& domain) const {
329 int i = 0;
330 const auto& others = domain.intervals_;
331 for (const ClosedInterval interval : intervals_) {
332 // Find the unique interval in others that contains interval if any.
333 for (; i < others.size() && interval.end > others[i].end; ++i) {
334 }
335 if (i == others.size()) return false;
336 if (interval.start < others[i].start) return false;
337 }
338 return true;
339}
340
342 Domain result;
343 int64_t next_start = kint64min;
344 result.intervals_.reserve(intervals_.size() + 1);
345 for (const ClosedInterval& interval : intervals_) {
346 if (interval.start != kint64min) {
347 result.intervals_.push_back({next_start, interval.start - 1});
348 }
349 if (interval.end == kint64max) return result;
350 next_start = interval.end + 1;
351 }
352 result.intervals_.push_back({next_start, kint64max});
353 DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_));
354 return result;
355}
356
358 Domain result = *this;
359 result.NegateInPlace();
360 return result;
361}
362
363void Domain::NegateInPlace() {
364 if (intervals_.empty()) return;
365 std::reverse(intervals_.begin(), intervals_.end());
366 if (intervals_.back().end == kint64min) {
367 // corner-case
368 intervals_.pop_back();
369 }
370 for (ClosedInterval& ref : intervals_) {
371 std::swap(ref.start, ref.end);
372 ref.start = ref.start == kint64min ? kint64max : -ref.start;
373 ref.end = ref.end == kint64min ? kint64max : -ref.end;
374 }
375 DCHECK(IntervalsAreSortedAndNonAdjacent(intervals_));
376}
377
379 Domain result;
380 const auto& a = intervals_;
381 const auto& b = domain.intervals_;
382 for (int i = 0, j = 0; i < a.size() && j < b.size();) {
383 if (a[i].start <= b[j].start) {
384 if (a[i].end < b[j].start) {
385 // Empty intersection. We advance past the first interval.
386 ++i;
387 } else { // a[i].end >= b[j].start
388 // Non-empty intersection: push back the intersection of these two, and
389 // advance past the first interval to finish.
390 if (a[i].end <= b[j].end) {
391 result.intervals_.push_back({b[j].start, a[i].end});
392 ++i;
393 } else { // a[i].end > b[j].end.
394 result.intervals_.push_back({b[j].start, b[j].end});
395 ++j;
396 }
397 }
398 } else { // a[i].start > b[i].start.
399 // We do the exact same thing as above, but swapping a and b.
400 if (b[j].end < a[i].start) {
401 ++j;
402 } else { // b[j].end >= a[i].start
403 if (b[j].end <= a[i].end) {
404 result.intervals_.push_back({a[i].start, b[j].end});
405 ++j;
406 } else { // a[i].end > b[j].end.
407 result.intervals_.push_back({a[i].start, a[i].end});
408 ++i;
409 }
410 }
411 }
412 }
413 DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_));
414 return result;
415}
416
417Domain Domain::UnionWith(const Domain& domain) const {
418 Domain result;
419 const auto& a = intervals_;
420 const auto& b = domain.intervals_;
421 result.intervals_.resize(a.size() + b.size());
422 std::merge(a.begin(), a.end(), b.begin(), b.end(), result.intervals_.begin());
423 UnionOfSortedIntervals(&result.intervals_);
424 return result;
425}
426
427// TODO(user): Use a better algorithm.
428Domain Domain::AdditionWith(const Domain& domain) const {
429 Domain result;
430
431 const auto& a = intervals_;
432 const auto& b = domain.intervals_;
433 result.intervals_.reserve(a.size() * b.size());
434 for (const ClosedInterval& i : a) {
435 for (const ClosedInterval& j : b) {
436 if (i.start > 0 && j.start > 0) {
437 if (AddOverflows(i.start, j.start)) continue; // empty.
438 result.intervals_.push_back({i.start + j.start, CapAdd(i.end, j.end)});
439 } else if (i.end < 0 && j.end < 0) {
440 if (AddOverflows(i.end, j.end)) continue; // empty.
441 result.intervals_.push_back({CapAdd(i.start, j.start), i.end + j.end});
442 } else {
443 result.intervals_.push_back(
444 {CapAdd(i.start, j.start), CapAdd(i.end, j.end)});
445 }
446 }
447 }
448
449 // The sort is not needed if one of the list is of size 1.
450 if (a.size() > 1 && b.size() > 1) {
451 std::sort(result.intervals_.begin(), result.intervals_.end());
452 }
453 UnionOfSortedIntervals(&result.intervals_);
454 return result;
455}
456
458 if (NumIntervals() > kDomainComplexityLimit) {
459 return Domain(Min(), Max());
460 } else {
461 return *this;
462 }
463}
464
465Domain Domain::MultiplicationBy(int64_t coeff, bool* exact) const {
466 if (exact != nullptr) *exact = true;
467 if (intervals_.empty()) return {};
468 if (coeff == 0) return Domain(0);
469
470 const int64_t abs_coeff = std::abs(coeff);
471 const int64_t size_if_non_trivial = abs_coeff > 1 ? Size() : 0;
472 if (size_if_non_trivial > kDomainComplexityLimit) {
473 if (exact != nullptr) *exact = false;
474 return ContinuousMultiplicationBy(coeff);
475 }
476
477 Domain result;
478 if (abs_coeff > 1) {
479 const int64_t max_value = kint64max / abs_coeff;
480 const int64_t min_value = kint64min / abs_coeff;
481 result.intervals_.reserve(size_if_non_trivial);
482 for (const ClosedInterval& i : intervals_) {
483 for (int64_t v = i.start;; ++v) {
484 // We ignore anything that overflow.
485 if (v >= min_value && v <= max_value) {
486 // Because abs_coeff > 1, all new values are disjoint.
487 const int64_t new_value = v * abs_coeff;
488 result.intervals_.push_back({new_value, new_value});
489 }
490
491 // This is to avoid doing ++v when v is kint64max!
492 if (v == i.end) break;
493 }
494 }
495 } else {
496 result = *this;
497 }
498 if (coeff < 0) result.NegateInPlace();
499 return result;
500}
501
503 Domain result = *this;
504 const int64_t abs_coeff = std::abs(coeff);
505 for (ClosedInterval& i : result.intervals_) {
506 i.start = CapProd(i.start, abs_coeff);
507 i.end = CapProd(i.end, abs_coeff);
508 }
509 UnionOfSortedIntervals(&result.intervals_);
510 if (coeff < 0) result.NegateInPlace();
511 return result;
512}
513
515 Domain result;
516 for (const ClosedInterval& i : this->intervals_) {
517 for (const ClosedInterval& j : domain.intervals_) {
518 ClosedInterval new_interval;
519 const int64_t a = CapProd(i.start, j.start);
520 const int64_t b = CapProd(i.end, j.end);
521 const int64_t c = CapProd(i.start, j.end);
522 const int64_t d = CapProd(i.end, j.start);
523 new_interval.start = std::min({a, b, c, d});
524 new_interval.end = std::max({a, b, c, d});
525 result.intervals_.push_back(new_interval);
526 }
527 }
528 std::sort(result.intervals_.begin(), result.intervals_.end());
529 UnionOfSortedIntervals(&result.intervals_);
530 return result;
531}
532
533Domain Domain::DivisionBy(int64_t coeff) const {
534 CHECK_NE(coeff, 0);
535 Domain result = *this;
536 const int64_t abs_coeff = std::abs(coeff);
537 for (ClosedInterval& i : result.intervals_) {
538 i.start = i.start / abs_coeff;
539 i.end = i.end / abs_coeff;
540 }
541 UnionOfSortedIntervals(&result.intervals_);
542 if (coeff < 0) result.NegateInPlace();
543 return result;
544}
545
546Domain Domain::InverseMultiplicationBy(const int64_t coeff) const {
547 if (coeff == 0) {
548 return Contains(0) ? Domain::AllValues() : Domain();
549 }
550 Domain result = *this;
551 int new_size = 0;
552 const int64_t abs_coeff = std::abs(coeff);
553 for (const ClosedInterval& i : result.intervals_) {
554 const int64_t start = CeilRatio(i.start, abs_coeff);
555 const int64_t end = FloorRatio(i.end, abs_coeff);
556 if (start > end) continue;
557 if (new_size > 0 && start == result.intervals_[new_size - 1].end + 1) {
558 result.intervals_[new_size - 1].end = end;
559 } else {
560 result.intervals_[new_size++] = {start, end};
561 }
562 }
563 result.intervals_.resize(new_size);
564 result.intervals_.shrink_to_fit();
565 DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_));
566 if (coeff < 0) result.NegateInPlace();
567 return result;
568}
569
570namespace {
571Domain ModuloHelper(int64_t min, int64_t max, const Domain& modulo) {
572 DCHECK_GT(min, 0);
573 DCHECK_GT(modulo.Min(), 0);
574 const int64_t max_mod = modulo.Max() - 1;
575
576 // The min/max are exact if the modulo is fixed. Note that we could return the
577 // exact domain with a potential hole but we currently don't.
578 if (modulo.Min() == modulo.Max()) {
579 const int64_t size = max - min;
580 const int64_t v1 = min % modulo.Max();
581 if (v1 + size > max_mod) return Domain(0, max_mod);
582 return Domain(v1, v1 + size);
583 }
584
585 // TODO(user): This is a superset.
586 return Domain(0, std::min(max, max_mod));
587}
588} // namespace
589
591 if (IsEmpty()) return Domain();
592 CHECK_GT(modulo.Min(), 0);
593 const int64_t max_mod = modulo.Max() - 1;
594 if (Max() >= 0 && Min() <= 0) {
595 return Domain(std::max(Min(), -max_mod), std::min(Max(), max_mod));
596 }
597 if (Min() > 0) {
598 return ModuloHelper(Min(), Max(), modulo);
599 }
600 DCHECK_LT(Max(), 0);
601 return ModuloHelper(-Max(), -Min(), modulo).Negation();
602}
603
605 if (IsEmpty()) return Domain();
606 CHECK_GT(divisor.Min(), 0);
607 return Domain(std::min(Min() / divisor.Max(), Min() / divisor.Min()),
608 std::max(Max() / divisor.Min(), Max() / divisor.Max()));
609}
610
612 if (IsEmpty()) return Domain();
613 const Domain abs_domain =
614 IntersectionWith({0, std::numeric_limits<int64_t>::max()})
616 {0, std::numeric_limits<int64_t>::max()}));
617 if (abs_domain.Size() >= kDomainComplexityLimit) {
618 Domain result;
619 result.intervals_.reserve(abs_domain.NumIntervals());
620 for (const auto& interval : abs_domain) {
621 result.intervals_.push_back(
623 CapProd(interval.end, interval.end)));
624 }
625 UnionOfSortedIntervals(&result.intervals_);
626 return result;
627 } else {
628 std::vector<int64_t> values;
629 values.reserve(abs_domain.Size());
630 for (const int64_t value : abs_domain.Values()) {
631 values.push_back(CapProd(value, value));
632 }
633 return Domain::FromValues(std::move(values));
634 }
635}
636
637// It is a bit difficult to see, but this code is doing the same thing as
638// for all interval in this.UnionWith(implied_domain.Complement())):
639// - Take the two extreme points (min and max) in interval \inter implied.
640// - Append to result [min, max] if these points exists.
642 Domain result;
643 if (implied_domain.IsEmpty()) return result;
644
645 int i = 0;
646 int64_t min_point;
647 int64_t max_point;
648 bool started = false;
649 for (const ClosedInterval interval : intervals_) {
650 // We only "close" the new result interval if it cannot be extended by
651 // implied_domain.Complement(). The only extension possible look like:
652 // interval_: ...] [....
653 // implied : ...] [... i ...]
654 if (started && implied_domain.intervals_[i].start < interval.start) {
655 result.intervals_.push_back({min_point, max_point});
656 started = false;
657 }
658
659 // Find the two extreme points in interval \inter implied_domain.
660 // Always stop the loop at the first interval with and end strictly greater
661 // that interval.end.
662 for (; i < implied_domain.intervals_.size(); ++i) {
663 const ClosedInterval current = implied_domain.intervals_[i];
664 if (current.end >= interval.start && current.start <= interval.end) {
665 // Current and interval have a non-empty intersection.
666 const int64_t inter_max = std::min(interval.end, current.end);
667 if (!started) {
668 started = true;
669 min_point = std::max(interval.start, current.start);
670 max_point = inter_max;
671 } else {
672 // No need to update the min_point here, and the new inter_max must
673 // necessarily be > old one.
674 DCHECK_GE(inter_max, max_point);
675 max_point = inter_max;
676 }
677 }
678 if (current.end > interval.end) break;
679 }
680 if (i == implied_domain.intervals_.size()) break;
681 }
682 if (started) {
683 result.intervals_.push_back({min_point, max_point});
684 }
685 DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_));
686 return result;
687}
688
689std::vector<int64_t> Domain::FlattenedIntervals() const {
690 std::vector<int64_t> result;
691 for (const ClosedInterval& interval : intervals_) {
692 result.push_back(interval.start);
693 result.push_back(interval.end);
694 }
695 return result;
696}
697
698bool Domain::operator<(const Domain& other) const {
699 const auto& d1 = intervals_;
700 const auto& d2 = other.intervals_;
701 const int common_size = std::min(d1.size(), d2.size());
702 for (int i = 0; i < common_size; ++i) {
703 const ClosedInterval& i1 = d1[i];
704 const ClosedInterval& i2 = d2[i];
705 if (i1.start < i2.start) return true;
706 if (i1.start > i2.start) return false;
707 if (i1.end < i2.end) return true;
708 if (i1.end > i2.end) return false;
709 }
710 return d1.size() < d2.size();
711}
712
713std::string Domain::ToString() const { return IntervalsAsString(intervals_); }
714
715int64_t SumOfKMinValueInDomain(const Domain& domain, int k) {
716 int64_t current_sum = 0.0;
717 int current_index = 0;
718 for (const ClosedInterval interval : domain) {
719 if (current_index >= k) break;
720 for (int v(interval.start); v <= interval.end; ++v) {
721 if (current_index >= k) break;
722 current_index++;
723 current_sum += v;
724 }
725 }
726 return current_sum;
727}
728
729int64_t SumOfKMaxValueInDomain(const Domain& domain, int k) {
730 return -SumOfKMinValueInDomain(domain.Negation(), k);
731}
732
734
736 const std::vector<int64_t>& starts, const std::vector<int64_t>& ends) {
737 InsertIntervals(starts, ends);
738}
739
741 const std::vector<int>& starts, const std::vector<int>& ends) {
742 InsertIntervals(starts, ends);
743}
744
746 const std::vector<ClosedInterval>& intervals) {
747 for (ClosedInterval interval : intervals) {
748 InsertInterval(interval.start, interval.end);
749 }
750}
751
754 int64_t end) {
755 SortedDisjointIntervalList interval_list;
756 int64_t next_start = start;
757 for (auto it = FirstIntervalGreaterOrEqual(start); it != this->end(); ++it) {
758 const ClosedInterval& interval = *it;
759 const int64_t next_end = CapSub(interval.start, 1);
760 if (next_end > end) break;
761 if (next_start <= next_end) {
762 interval_list.InsertInterval(next_start, next_end);
763 }
764 next_start = CapAdd(interval.end, 1);
765 }
766 if (next_start <= end) {
767 interval_list.InsertInterval(next_start, end);
768 }
769 return interval_list;
770}
771
773 int64_t start, int64_t end) {
774 // start > end could mean an empty interval, but we prefer to LOG(DFATAL)
775 // anyway. Really, the user should never give us that.
776 if (start > end) {
777 LOG(DFATAL) << "Invalid interval: " << ClosedInterval({start, end});
778 return intervals_.end();
779 }
780
781 auto result = intervals_.insert({start, end});
782 if (!result.second) return result.first; // Duplicate: exit immediately.
783
784 // TODO(user): tune the algorithm below if it proves to be a bottleneck.
785 // For example, one could try to avoid an insertion if it's not needed
786 // (when the interval merges with a single existing interval or is fully
787 // contained by one).
788
789 // Iterate over the previous iterators whose end is after (or almost at) our
790 // start. After this, "it1" will point to the first interval that needs to be
791 // merged with the current interval (possibly pointing to the current interval
792 // itself, if no "earlier" interval should be merged).
793 auto it1 = result.first;
794 if (start == kint64min) { // Catch underflows
795 it1 = intervals_.begin();
796 } else {
797 const int64_t before_start = start - 1;
798 while (it1 != intervals_.begin()) {
799 auto prev_it = it1;
800 --prev_it;
801 if (prev_it->end < before_start) break;
802 it1 = prev_it;
803 }
804 }
805
806 // Ditto, on the other side: "it2" will point to the interval *after* the last
807 // one that should be merged with the current interval.
808 auto it2 = result.first;
809 if (end == kint64max) {
810 it2 = intervals_.end();
811 } else {
812 const int64_t after_end = end + 1;
813 do {
814 ++it2;
815 } while (it2 != intervals_.end() && it2->start <= after_end);
816 }
817
818 // [it1..it2) is the range (inclusive on it1, exclusive on it2) of intervals
819 // that should be merged together. We'll set it3 = it2-1 and erase [it1..it3)
820 // and set *it3 to the merged interval.
821 auto it3 = it2;
822 it3--;
823 if (it1 == it3) return it3; // Nothing was merged.
824 const int64_t new_start = std::min(it1->start, start);
825 const int64_t new_end = std::max(it3->end, end);
826 auto it = intervals_.erase(it1, it3);
827 // HACK(user): set iterators point to *const* values. Which is expected,
828 // because if one alters a set element's value, then it collapses the set
829 // property! But in this very special case, we know that we can just overwrite
830 // it->start, so we do it.
831 const_cast<ClosedInterval*>(&(*it))->start = new_start;
832 const_cast<ClosedInterval*>(&(*it))->end = new_end;
833 return it;
834}
835
837 int64_t value, int64_t* newly_covered) {
838 auto it = intervals_.upper_bound({value, kint64max});
839 auto it_prev = it;
840
841 // No interval containing or adjacent to "value" on the left (i.e. below).
842 if (it != begin()) {
843 --it_prev;
844 }
845 if (it == begin() || ((value != kint64min) && it_prev->end < value - 1)) {
846 *newly_covered = value;
847 if (it == end() || it->start != value + 1) {
848 // No interval adjacent to "value" on the right: insert a singleton.
849 return intervals_.insert(it, {value, value});
850 } else {
851 // There is an interval adjacent to "value" on the right. Extend it by
852 // one. Note that we already know that there won't be a merge with another
853 // interval on the left, since there were no interval adjacent to "value"
854 // on the left.
855 DCHECK_EQ(it->start, value + 1);
856 const_cast<ClosedInterval*>(&(*it))->start = value;
857 return it;
858 }
859 }
860
861 // At this point, "it_prev" points to an interval containing or adjacent to
862 // "value" on the left: grow it by one, and if it now touches the next
863 // interval, merge with it.
864 CHECK_NE(kint64max, it_prev->end) << "Cannot grow right by one: the interval "
865 "that would grow already ends at "
866 "kint64max";
867 *newly_covered = it_prev->end + 1;
868 if (it != end() && it_prev->end + 2 == it->start) {
869 // We need to merge it_prev with 'it'.
870 const_cast<ClosedInterval*>(&(*it_prev))->end = it->end;
871 intervals_.erase(it);
872 } else {
873 const_cast<ClosedInterval*>(&(*it_prev))->end = it_prev->end + 1;
874 }
875 return it_prev;
876}
877
878template <class T>
879void SortedDisjointIntervalList::InsertAll(const std::vector<T>& starts,
880 const std::vector<T>& ends) {
881 CHECK_EQ(starts.size(), ends.size());
882 for (int i = 0; i < starts.size(); ++i) InsertInterval(starts[i], ends[i]);
883}
884
886 const std::vector<int64_t>& starts, const std::vector<int64_t>& ends) {
887 InsertAll(starts, ends);
888}
889
890void SortedDisjointIntervalList::InsertIntervals(const std::vector<int>& starts,
891 const std::vector<int>& ends) {
892 // TODO(user): treat kint32min and kint32max as their kint64 variants.
893 InsertAll(starts, ends);
894}
895
898 const auto it = intervals_.upper_bound({value, kint64max});
899 if (it == begin()) return it;
900 auto it_prev = it;
901 it_prev--;
902 DCHECK_LE(it_prev->start, value);
903 return it_prev->end >= value ? it_prev : it;
904}
905
908 const auto it = intervals_.upper_bound({value, kint64max});
909 if (it == begin()) return end();
910 auto it_prev = it;
911 it_prev--;
912 return it_prev;
913}
914
916 std::string str;
917 for (const ClosedInterval& interval : intervals_) {
918 str += interval.DebugString();
919 }
920 return str;
921}
922
923} // namespace operations_research
IntegerValue size
int64_t max
int64_t min
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) const
static Domain FromVectorIntervals(const std::vector< std::vector< int64_t > > &intervals)
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
static Domain FromValues(std::vector< int64_t > values)
bool operator<(const Domain &other) const
int64_t ValueAtOrAfter(int64_t input) const
std::vector< int64_t > FlattenedIntervals() const
Domain IntersectionWith(const Domain &domain) 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
static Domain FromFlatSpanOfIntervals(absl::Span< const int64_t > flat_intervals)
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
Domain PositiveDivisionBySuperset(const Domain &divisor) const
Domain UnionWith(const Domain &domain) const
Domain InverseMultiplicationBy(int64_t coeff) const
int64_t ValueAtOrBefore(int64_t input) const
int64_t ClosestValue(int64_t wanted) const
void InsertIntervals(const std::vector< int64_t > &starts, const std::vector< int64_t > &ends)
Iterator InsertInterval(int64_t start, int64_t end)
SortedDisjointIntervalList BuildComplementOnInterval(int64_t start, int64_t end)
Iterator GrowRightByOne(int64_t value, int64_t *newly_covered)
int64_t b
Definition table.cc:45
int64_t a
Definition table.cc:44
int64_t value
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
int64_t FloorRatio(int64_t value, int64_t positive_coeff)
int64_t CapSub(int64_t x, int64_t y)
int64_t CeilRatio(int64_t value, int64_t positive_coeff)
int64_t SumOfKMinValueInDomain(const Domain &domain, int k)
Returns the sum of smallest k values in the domain.
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
bool AddOverflows(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
bool IntervalsAreSortedAndNonAdjacent(absl::Span< const ClosedInterval > intervals)
int64_t SumOfKMaxValueInDomain(const Domain &domain, int k)
Returns the sum of largest k values in the domain.
static int input(yyscan_t yyscanner)
IntervalVar * interval
Definition resource.cc:101
std::optional< int64_t > end
int64_t start
static const int64_t kint64max
Definition types.h:32
static const int64_t kint64min
Definition types.h:31