25 #include "absl/container/inlined_vector.h"
26 #include "absl/strings/str_format.h"
27 #include "absl/types/span.h"
36 return absl::StrFormat(
"[%d,%d]",
start,
end);
40 absl::Span<const ClosedInterval> intervals) {
41 for (
int i = 1; i < intervals.size(); ++i) {
42 if (intervals[i - 1].
start > intervals[i - 1].
end)
return false;
44 if (intervals[i - 1].
end >= intervals[i].
start ||
45 intervals[i - 1].
end + 1 >= intervals[i].
start) {
49 return intervals.empty() ? true
50 : intervals.back().start <= intervals.back().end;
55 template <
class Intervals>
56 std::string IntervalsAsString(
const Intervals& intervals) {
58 for (ClosedInterval
interval : intervals) {
61 if (result.empty()) result =
"[]";
67 void UnionOfSortedIntervals(absl::InlinedVector<ClosedInterval, 1>* intervals) {
68 DCHECK(std::is_sorted(intervals->begin(), intervals->end()));
69 const int size = intervals->size();
70 if (size == 0)
return;
73 for (
int i = 1; i < size; ++i) {
74 const ClosedInterval& current = (*intervals)[i];
75 const int64_t
end = (*intervals)[new_size - 1].end;
77 current.start <=
end + 1) {
78 (*intervals)[new_size - 1].end =
std::max(current.end,
end);
81 (*intervals)[new_size++] = current;
83 intervals->resize(new_size);
87 intervals->shrink_to_fit();
95 DCHECK_GT(positive_coeff, 0);
96 const int64_t result =
value / positive_coeff;
97 const int64_t adjust =
static_cast<int64_t
>(result * positive_coeff <
value);
98 return result + adjust;
102 DCHECK_GT(positive_coeff, 0);
103 const int64_t result =
value / positive_coeff;
104 const int64_t adjust =
static_cast<int64_t
>(result * positive_coeff >
value);
105 return result - adjust;
113 const std::vector<ClosedInterval>& intervals) {
114 return out << IntervalsAsString(intervals);
118 return out << IntervalsAsString(domain);
130 inline ClosedInterval UncheckedClosedInterval(int64_t s, int64_t e) {
139 : intervals_({UncheckedClosedInterval(left, right)}) {
140 if (left > right) intervals_.clear();
146 std::sort(values.begin(), values.end());
148 for (
const int64_t v : values) {
149 if (result.intervals_.empty() || v > result.intervals_.back().end + 1) {
150 result.intervals_.push_back({v, v});
152 result.intervals_.back().end = v;
161 std::sort(result.intervals_.begin(), result.intervals_.end());
162 UnionOfSortedIntervals(&result.intervals_);
167 absl::Span<const int64_t> flat_intervals) {
168 DCHECK(flat_intervals.size() % 2 == 0) << flat_intervals.size();
170 result.intervals_.reserve(flat_intervals.size() / 2);
171 for (
int i = 0; i < flat_intervals.size(); i += 2) {
172 result.intervals_.push_back({flat_intervals[i], flat_intervals[i + 1]});
174 std::sort(result.intervals_.begin(), result.intervals_.end());
175 UnionOfSortedIntervals(&result.intervals_);
184 const std::vector<std::vector<int64_t>>& intervals) {
194 std::sort(result.intervals_.begin(), result.intervals_.end());
195 UnionOfSortedIntervals(&result.intervals_);
217 return intervals_.front().start;
222 return intervals_.back().end;
227 int64_t result =
Min();
231 if (
b > 0 &&
b <= std::abs(result)) result =
b;
232 if (
b < 0 && -
b < std::abs(result)) result =
b;
244 if (it == intervals_.begin())
return input;
255 if (it == intervals_.end())
return input;
256 const int64_t candidate = it->start;
257 if (it == intervals_.begin())
return candidate;
264 return intervals_.front().start;
273 if (it == intervals_.begin())
return false;
275 return value <= it->end;
280 const auto& others = domain.intervals_;
283 for (; i < others.size() &&
interval.end > others[i].end; ++i) {
285 if (i == others.size())
return false;
286 if (
interval.start < others[i].start)
return false;
294 result.intervals_.reserve(intervals_.size() + 1);
297 result.intervals_.push_back({next_start,
interval.start - 1});
302 result.intervals_.push_back({next_start,
kint64max});
309 result.NegateInPlace();
313 void Domain::NegateInPlace() {
314 if (intervals_.empty())
return;
315 std::reverse(intervals_.begin(), intervals_.end());
316 if (intervals_.back().end ==
kint64min) {
318 intervals_.pop_back();
320 for (ClosedInterval& ref : intervals_) {
330 const auto&
a = intervals_;
331 const auto&
b = domain.intervals_;
332 for (
int i = 0, j = 0; i <
a.size() && j <
b.size();) {
341 result.intervals_.push_back({
b[j].start,
a[i].end});
344 result.intervals_.push_back({
b[j].start,
b[j].end});
354 result.intervals_.push_back({
a[i].start,
b[j].end});
357 result.intervals_.push_back({
a[i].start,
a[i].end});
369 const auto&
a = intervals_;
370 const auto&
b = domain.intervals_;
371 result.intervals_.resize(
a.size() +
b.size());
372 std::merge(
a.begin(),
a.end(),
b.begin(),
b.end(), result.intervals_.begin());
373 UnionOfSortedIntervals(&result.intervals_);
381 const auto&
a = intervals_;
382 const auto&
b = domain.intervals_;
383 result.intervals_.reserve(
a.size() *
b.size());
386 result.intervals_.push_back(
392 if (
a.size() > 1 &&
b.size() > 1) {
393 std::sort(result.intervals_.begin(), result.intervals_.end());
395 UnionOfSortedIntervals(&result.intervals_);
408 if (exact !=
nullptr) *exact =
true;
409 if (intervals_.empty())
return {};
410 if (coeff == 0)
return Domain(0);
412 const int64_t abs_coeff = std::abs(coeff);
413 const int64_t size_if_non_trivial = abs_coeff > 1 ?
Size() : 0;
414 if (size_if_non_trivial > kDomainComplexityLimit) {
415 if (exact !=
nullptr) *exact =
false;
421 const int64_t max_value =
kint64max / abs_coeff;
422 const int64_t min_value =
kint64min / abs_coeff;
423 result.intervals_.reserve(size_if_non_trivial);
425 for (int64_t v = i.start;; ++v) {
427 if (v >= min_value && v <= max_value) {
429 const int64_t new_value = v * abs_coeff;
430 result.intervals_.push_back({new_value, new_value});
434 if (v == i.end)
break;
440 if (coeff < 0) result.NegateInPlace();
446 const int64_t abs_coeff = std::abs(coeff);
451 UnionOfSortedIntervals(&result.intervals_);
452 if (coeff < 0) result.NegateInPlace();
467 result.intervals_.push_back(new_interval);
470 std::sort(result.intervals_.begin(), result.intervals_.end());
471 UnionOfSortedIntervals(&result.intervals_);
478 const int64_t abs_coeff = std::abs(coeff);
481 i.
end = i.
end / abs_coeff;
483 UnionOfSortedIntervals(&result.intervals_);
484 if (coeff < 0) result.NegateInPlace();
494 const int64_t abs_coeff = std::abs(coeff);
499 if (new_size > 0 &&
start == result.intervals_[new_size - 1].end + 1) {
500 result.intervals_[new_size - 1].end =
end;
502 result.intervals_[new_size++] = {
start,
end};
505 result.intervals_.resize(new_size);
506 result.intervals_.shrink_to_fit();
508 if (coeff < 0) result.NegateInPlace();
515 DCHECK_GT(modulo.
Min(), 0);
516 const int64_t max_mod = modulo.
Max() - 1;
520 if (modulo.
Min() == modulo.
Max()) {
521 const int64_t size =
max -
min;
522 const int64_t v1 =
min % modulo.
Max();
523 if (v1 + size > max_mod)
return Domain(0, max_mod);
524 return Domain(v1, v1 + size);
534 CHECK_GT(modulo.
Min(), 0);
535 const int64_t max_mod = modulo.
Max() - 1;
536 if (
Max() >= 0 &&
Min() <= 0) {
540 return ModuloHelper(
Min(),
Max(), modulo);
543 return ModuloHelper(-
Max(), -
Min(), modulo).Negation();
548 CHECK_GT(divisor.
Min(), 0);
559 if (abs_domain.
Size() >= kDomainComplexityLimit) {
563 result.intervals_.push_back(
567 UnionOfSortedIntervals(&result.intervals_);
570 std::vector<int64_t> values;
571 values.reserve(abs_domain.
Size());
585 if (implied_domain.
IsEmpty())
return result;
590 bool started =
false;
596 if (started && implied_domain.intervals_[i].start <
interval.start) {
597 result.intervals_.push_back({min_point, max_point});
604 for (; i < implied_domain.intervals_.size(); ++i) {
612 max_point = inter_max;
616 DCHECK_GE(inter_max, max_point);
617 max_point = inter_max;
622 if (i == implied_domain.intervals_.size())
break;
625 result.intervals_.push_back({min_point, max_point});
632 std::vector<int64_t> result;
641 const auto& d1 = intervals_;
642 const auto& d2 = other.intervals_;
643 const int common_size =
std::min(d1.size(), d2.size());
644 for (
int i = 0; i < common_size; ++i) {
649 if (i1.
end < i2.
end)
return true;
650 if (i1.
end > i2.
end)
return false;
652 return d1.size() < d2.size();
658 int64_t current_sum = 0.0;
659 int current_index = 0;
661 if (current_index >= k)
break;
663 if (current_index >= k)
break;
678 const std::vector<int64_t>& starts,
const std::vector<int64_t>& ends) {
683 const std::vector<int>& starts,
const std::vector<int>& ends) {
688 const std::vector<ClosedInterval>& intervals) {
698 int64_t next_start =
start;
702 if (next_end >
end)
break;
703 if (next_start <= next_end) {
708 if (next_start <=
end) {
711 return interval_list;
720 return intervals_.end();
723 auto result = intervals_.insert({
start,
end});
724 if (!result.second)
return result.first;
735 auto it1 = result.first;
737 it1 = intervals_.begin();
739 const int64_t before_start =
start - 1;
740 while (it1 != intervals_.begin()) {
743 if (prev_it->end < before_start)
break;
750 auto it2 = result.first;
752 it2 = intervals_.end();
754 const int64_t after_end =
end + 1;
757 }
while (it2 != intervals_.end() && it2->start <= after_end);
765 if (it1 == it3)
return it3;
768 auto it = intervals_.erase(it1, it3);
779 int64_t
value, int64_t* newly_covered) {
788 *newly_covered =
value;
789 if (it ==
end() || it->start !=
value + 1) {
797 DCHECK_EQ(it->start,
value + 1);
806 CHECK_NE(
kint64max, it_prev->end) <<
"Cannot grow right by one: the interval "
807 "that would grow already ends at "
809 *newly_covered = it_prev->end + 1;
810 if (it !=
end() && it_prev->end + 2 == it->start) {
813 intervals_.erase(it);
821 void SortedDisjointIntervalList::InsertAll(
const std::vector<T>& starts,
822 const std::vector<T>& ends) {
823 CHECK_EQ(starts.size(), ends.size());
824 for (
int i = 0; i < starts.size(); ++i)
InsertInterval(starts[i], ends[i]);
828 const std::vector<int64_t>& starts,
const std::vector<int64_t>& ends) {
829 InsertAll(starts, ends);
833 const std::vector<int>& ends) {
835 InsertAll(starts, ends);
841 if (it ==
begin())
return it;
844 DCHECK_LE(it_prev->start,
value);
845 return it_prev->end >=
value ? it_prev : it;
We call domain any subset of Int64 = [kint64min, kint64max].
static Domain AllValues()
Returns the full domain Int64.
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
std::string ToString() const
Returns a compact string of a vector of intervals like "[1,4][6][10,20]".
Domain Negation() const
Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.
Domain Complement() const
Returns the set Int64 ∖ D.
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
Domain ContinuousMultiplicationBy(int64_t coeff) const
Returns a superset of MultiplicationBy() to avoid the explosion in the representation size.
static Domain FromFlatSpanOfIntervals(absl::Span< const int64_t > flat_intervals)
Same as FromIntervals() for a flattened representation (start, end, start, end, .....
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
int NumIntervals() const
Basic read-only std::vector<> wrapping to view a Domain as a sorted list of non-adjacent intervals.
int64_t FixedValue() const
Returns the value of a fixed domain.
bool operator<(const Domain &other) const
Lexicographic order on the intervals() representation.
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
int64_t Size() const
Returns the number of elements in the domain.
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
static Domain FromFlatIntervals(const std::vector< int64_t > &flat_intervals)
This method is available in Python, Java and .NET.
std::vector< int64_t > FlattenedIntervals() const
This method returns the flattened list of interval bounds of the domain.
bool IsFixed() const
Returns true iff the domain is reduced to a single value.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
static Domain FromVectorIntervals(const std::vector< std::vector< int64_t > > &intervals)
This method is available in Python, Java and .NET.
int64_t Min() const
Returns the min value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
Domain PositiveDivisionBySuperset(const Domain &divisor) const
Returns a superset of {x ∈ Int64, ∃ e ∈ D, ∃ d ∈ divisor, x = e / d }.
std::vector< ClosedInterval > intervals() const
static Domain FromIntervals(absl::Span< const ClosedInterval > intervals)
Creates a domain from the union of an unsorted list of intervals.
Domain()
By default, Domain will be empty.
int64_t SmallestValue() const
Returns the value closest to zero.
int64_t Max() const
Returns the max value of the domain.
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
static Domain FromValues(std::vector< int64_t > values)
Creates a domain from the union of an unsorted list of integer values.
Domain SquareSuperset() const
Returns a superset of {x ∈ Int64, ∃ y ∈ D, x = y * y }.
Domain DivisionBy(int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e / coeff}.
DomainIteratorBeginEnd Values() const &
int64_t ValueAtOrAfter(int64_t input) const
Domain PositiveModuloBySuperset(const Domain &modulo) const
Returns a superset of {x ∈ Int64, ∃ e ∈ D, ∃ m ∈ modulo, x = e % m }.
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) const
Advanced usage.
int64_t ValueAtOrBefore(int64_t input) const
Returns the closest value in the domain that is <= (resp.
std::string DebugString() const override
This class represents a sorted list of disjoint, closed intervals.
Iterator GrowRightByOne(int64_t value, int64_t *newly_covered)
If value is in an interval, increase its end by one, otherwise insert the interval [value,...
void InsertIntervals(const std::vector< int64_t > &starts, const std::vector< int64_t > &ends)
Adds all intervals [starts[i]..ends[i]].
Iterator InsertInterval(int64_t start, int64_t end)
Adds the interval [start..end] to the list, and merges overlapping or immediately adjacent intervals ...
Iterator LastIntervalLessOrEqual(int64_t value) const
ConstIterator end() const
std::string DebugString() const
Iterator FirstIntervalGreaterOrEqual(int64_t value) const
Returns an iterator to either:
IntervalSet::iterator Iterator
ConstIterator begin() const
Const iterators for SortedDisjoinIntervalList.
SortedDisjointIntervalList BuildComplementOnInterval(int64_t start, int64_t end)
Builds the complement of the interval list on the interval [start, end].
SortedDisjointIntervalList()
static const int64_t kint64max
static const int64_t kint64min
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Collection of objects used to extend the Constraint Solver library.
int64_t SumOfKMinValueInDomain(const Domain &domain, int k)
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)
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
int64_t CeilRatio(int64_t value, int64_t positive_coeff)
int64_t SumOfKMaxValueInDomain(const Domain &domain, int k)
int64_t CapProd(int64_t x, int64_t y)
bool IntervalsAreSortedAndNonAdjacent(absl::Span< const ClosedInterval > intervals)
Returns true iff we have:
static int input(yyscan_t yyscanner)
std::optional< int64_t > end
Represents a closed interval [start, end].
std::string DebugString() const