23 #include "absl/container/btree_set.h"
24 #include "absl/strings/str_format.h"
35 int FindSegmentIndex(
const std::vector<PiecewiseSegment>& segments, int64_t x) {
36 if (segments.empty() || segments.front().start_x() > x) {
44 if (position == segments.end()) {
45 return segments.size() - 1;
47 position -= position->start_x() > x ? 1 : 0;
49 return position - segments.begin();
52 inline bool IsAtBounds(int64_t
value) {
56 inline bool PointInsideRange(int64_t point, int64_t range_start,
58 return range_start <= point && range_end >= point;
63 inline bool FormConvexPair(
const PiecewiseSegment& left,
64 const PiecewiseSegment& right) {
65 return right.slope() >= left.slope() && right.start_x() == left.end_x() &&
66 right.start_y() == left.end_y();
69 uint64_t UnsignedCapAdd(uint64_t left, uint64_t right) {
73 uint64_t UnsignedCapProd(uint64_t left, uint64_t right) {
74 if (right == 0)
return 0;
81 int64_t slope, int64_t other_point_x)
82 : slope_(slope), reference_x_(point_x), reference_y_(point_y) {
83 start_x_ =
std::min(point_x, other_point_x);
84 end_x_ =
std::max(point_x, other_point_x);
86 reference_x_ < 0 ? SafeValuePostReference(0) : SafeValuePreReference(0);
90 CHECK_GE(x, start_x_);
93 const int64_t span_x =
CapSub(x, reference_x_);
96 return SafeValuePostReference(x);
99 return SafeValuePreReference(x);
102 const int64_t span_y =
CapProd(slope_, span_x);
103 if (IsAtBounds(span_y)) {
105 return SafeValuePostReference(x);
107 return SafeValuePreReference(x);
111 const int64_t
value =
CapAdd(reference_y_, span_y);
112 if (IsAtBounds(
value)) {
114 return SafeValuePostReference(x);
116 return SafeValuePreReference(x);
123 int64_t PiecewiseSegment::SafeValuePostReference(int64_t x)
const {
124 DCHECK_GE(x, reference_x_);
125 const uint64_t span_x =
static_cast<uint64_t
>(x) - reference_x_;
132 }
else if (slope_ > 0) {
134 const uint64_t span_y = UnsignedCapProd(span_x, slope_);
135 if (reference_y_ == 0) {
137 }
else if (reference_y_ > 0) {
138 const uint64_t unsigned_sum = UnsignedCapAdd(reference_y_, span_y);
140 :
static_cast<int64_t
>(unsigned_sum);
142 const uint64_t opp_reference_y = -
static_cast<uint64_t
>(reference_y_);
143 if (span_y >= opp_reference_y) {
144 return span_y - opp_reference_y >
kint64max
146 :
static_cast<int64_t
>(span_y - opp_reference_y);
148 return opp_reference_y - span_y >
static_cast<uint64_t
>(
kint64max) + 1
150 : -
static_cast<int64_t
>(opp_reference_y - span_y);
155 const uint64_t span_y = UnsignedCapProd(span_x, -slope_);
156 if (reference_y_ == 0) {
158 }
else if (reference_y_ < 0) {
159 const uint64_t opp_reference_y = -
static_cast<uint64_t
>(reference_y_);
160 const uint64_t opp_unsigned_sum = UnsignedCapAdd(opp_reference_y, span_y);
163 : -
static_cast<int64_t
>(opp_unsigned_sum);
165 if (reference_y_ >= span_y) {
168 :
static_cast<int64_t
>(reference_y_ - span_y);
170 return span_y - reference_y_ >
static_cast<uint64_t
>(
kint64max) + 1
172 : -
static_cast<int64_t
>(span_y - reference_y_);
178 int64_t PiecewiseSegment::SafeValuePreReference(int64_t x)
const {
179 DCHECK_LE(x, reference_x_);
180 const uint64_t span_x =
static_cast<uint64_t
>(reference_x_) - x;
184 }
else if (slope_ > 0) {
186 const uint64_t span_y = UnsignedCapProd(span_x, slope_);
187 if (reference_y_ == 0) {
189 }
else if (reference_y_ > 0) {
190 if (reference_y_ >= span_y) {
193 :
static_cast<int64_t
>(reference_y_ - span_y);
195 return span_y - reference_y_ >
static_cast<uint64_t
>(
kint64max) + 1
197 : -
static_cast<uint64_t
>(span_y - reference_y_);
200 const uint64_t opp_reference_y = -
static_cast<uint64_t
>(reference_y_);
201 const uint64_t opp_unsigned_sum = UnsignedCapAdd(opp_reference_y, span_y);
204 : -
static_cast<uint64_t
>(opp_unsigned_sum);
208 const uint64_t span_y = UnsignedCapProd(span_x, -slope_);
209 if (reference_y_ == 0) {
211 }
else if (reference_y_ < 0) {
212 const uint64_t opp_reference_y = -
static_cast<uint64_t
>(reference_y_);
213 if (span_y >= opp_reference_y) {
214 return span_y - opp_reference_y >
kint64max
216 :
static_cast<int64_t
>(span_y - opp_reference_y);
218 return opp_reference_y - span_y >
static_cast<uint64_t
>(
kint64max) + 1
220 : -
static_cast<uint64_t
>(opp_reference_y - span_y);
223 const uint64_t unsigned_sum = UnsignedCapAdd(reference_y_, span_y);
225 :
static_cast<int64_t
>(unsigned_sum);
232 return segment1.start_x_ < segment2.start_x_;
245 if (IsAtBounds(
CapAdd(reference_x_, constant))) {
246 LOG(ERROR) <<
"Segment Overflow: " <<
DebugString();
249 start_x_ =
CapAdd(start_x_, constant);
250 end_x_ =
CapAdd(end_x_, constant);
251 reference_x_ =
CapAdd(reference_x_, constant);
255 if (IsAtBounds(
CapAdd(reference_y_, constant))) {
256 LOG(ERROR) <<
"Segment Overflow: " <<
DebugString();
259 reference_y_ =
CapAdd(reference_y_, constant);
263 std::string result = absl::StrFormat(
264 "PiecewiseSegment(<start: (%d, %d), end: (%d, %d), "
265 "reference: (%d, %d), slope = %d>)",
266 start_x_,
Value(start_x_), end_x_,
Value(end_x_), reference_x_,
267 reference_y_, slope_);
273 PiecewiseLinearFunction::PiecewiseLinearFunction(
274 std::vector<PiecewiseSegment> segments)
275 : is_modified_(true),
277 is_non_decreasing_(false),
278 is_non_increasing_(false) {
282 for (
int i = 0; i < segments.size() - 1; ++i) {
284 LOG(FATAL) <<
"Overlapping segments: " << segments[i].DebugString()
285 <<
" & " << segments[i + 1].DebugString();
289 for (
const auto& segment : segments) {
290 InsertSegment(segment);
295 std::vector<int64_t> points_x, std::vector<int64_t> points_y,
296 std::vector<int64_t> slopes, std::vector<int64_t> other_points_x) {
297 CHECK_EQ(points_x.size(), points_y.size());
298 CHECK_EQ(points_x.size(), other_points_x.size());
299 CHECK_EQ(points_x.size(), slopes.size());
300 CHECK_GT(points_x.size(), 0);
302 std::vector<PiecewiseSegment>
segments;
303 for (
int i = 0; i < points_x.size(); ++i) {
312 std::vector<int64_t> points_x, std::vector<int64_t> points_y,
313 std::vector<int64_t> other_points_x) {
314 CHECK_EQ(points_x.size(), points_y.size());
315 CHECK_EQ(points_x.size(), other_points_x.size());
316 CHECK_GT(points_x.size(), 0);
318 std::vector<PiecewiseSegment>
segments;
319 for (
int i = 0; i < points_x.size(); ++i) {
328 int64_t initial_level, std::vector<int64_t> points_x,
329 std::vector<int64_t> slopes) {
330 CHECK_EQ(points_x.size(), slopes.size() - 1);
331 CHECK_GT(points_x.size(), 0);
333 int64_t level = initial_level;
334 std::vector<PiecewiseSegment>
segments;
338 level = segment.
Value(points_x[0]);
339 for (
int i = 1; i < points_x.size(); ++i) {
343 level = segment.
Value(points_x[i]);
352 int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x) {
355 std::vector<PiecewiseSegment>
segments = {
361 int64_t point_x, int64_t point_y, int64_t slope) {
362 std::vector<PiecewiseSegment>
segments = {
368 int64_t point_x, int64_t point_y, int64_t slope) {
369 std::vector<PiecewiseSegment>
segments = {
375 int64_t slope, int64_t
value) {
376 std::vector<PiecewiseSegment>
segments = {
385 int64_t reference, int64_t earliness_slope, int64_t tardiness_slope) {
386 std::vector<PiecewiseSegment>
segments = {
389 CHECK_GE(earliness_slope, 0);
390 CHECK_GE(tardiness_slope, 0);
396 int64_t early_slack, int64_t late_slack, int64_t earliness_slope,
397 int64_t tardiness_slope) {
398 std::vector<PiecewiseSegment>
segments = {
403 CHECK_GE(earliness_slope, 0);
404 CHECK_GE(tardiness_slope, 0);
409 int index = FindSegmentIndex(segments_, x);
413 if (segments_[
index].end_x() < x) {
426 return is_non_decreasing_;
431 return is_non_increasing_;
440 const int index = FindSegmentIndex(segments_, x);
441 return segments_[
index].Value(x);
445 int64_t range_end)
const {
447 return Value(range_end);
449 return Value(range_start);
451 int start_segment = -1;
452 int end_segment = -1;
453 if (!FindSegmentIndicesFromRange(range_start, range_end, &start_segment,
457 CHECK_GE(end_segment, start_segment);
467 for (
int i =
std::max(0, start_segment); i <= end_segment; ++i) {
468 if (PointInsideRange(segments_[i].start_x(), range_start, range_end)) {
469 range_maximum =
std::max(range_maximum, segments_[i].start_y());
471 if (PointInsideRange(segments_[i].end_x(), range_start, range_end)) {
472 range_maximum =
std::max(range_maximum, segments_[i].end_y());
475 return range_maximum;
479 int64_t range_end)
const {
481 return Value(range_start);
483 return Value(range_end);
485 int start_segment = -1;
486 int end_segment = -1;
487 if (!FindSegmentIndicesFromRange(range_start, range_end, &start_segment,
491 CHECK_GE(end_segment, start_segment);
501 for (
int i =
std::max(0, start_segment); i <= end_segment; ++i) {
502 if (PointInsideRange(segments_[i].start_x(), range_start, range_end)) {
503 range_minimum =
std::min(range_minimum, segments_[i].start_y());
505 if (PointInsideRange(segments_[i].end_x(), range_start, range_end)) {
506 range_minimum =
std::min(range_minimum, segments_[i].end_y());
509 return range_minimum;
513 return GetMaximum(segments_.front().start_x(), segments_.back().end_x());
517 return GetMinimum(segments_.front().start_x(), segments_.back().end_x());
520 std::pair<int64_t, int64_t>
523 int64_t
value)
const {
527 std::pair<int64_t, int64_t>
530 int64_t
value)
const {
535 std::pair<int64_t, int64_t> ComputeXFromY(int64_t start_x, int64_t start_y,
536 int64_t slope, int64_t y) {
538 const int64_t delta_y =
CapSub(y, start_y);
539 const int64_t delta_x = delta_y / slope;
540 if ((delta_y >= 0 && slope >= 0) || (delta_y <= 0 && slope <= 0)) {
541 const int64_t delta_x_down = delta_x;
542 const int64_t delta_x_up = delta_y % slope == 0 ? delta_x : delta_x + 1;
543 return {delta_x_down + start_x, delta_x_up + start_x};
545 const int64_t delta_x_down = delta_y % slope == 0 ? delta_x : delta_x - 1;
546 const int64_t delta_x_up = -(-delta_y / slope);
547 return {delta_x_down + start_x, delta_x_up + start_x};
551 std::pair<int64_t, int64_t> GetRangeInValueRange(int64_t start_x, int64_t end_x,
552 int64_t start_y, int64_t end_y,
556 if ((start_y > value_max && end_y > value_max) ||
557 (start_y < value_min && end_y < value_min)) {
561 if (start_y <= value_max && end_y <= value_max) {
562 x_range_max = {start_x, end_x};
563 }
else if (start_y <= value_max || end_y <= value_max) {
565 ? ComputeXFromY(end_x, end_y, slope, value_max)
566 : ComputeXFromY(start_x, start_y, slope, value_max);
567 if (end_y <= value_max) {
568 x_range_max = {x.second, end_x};
570 x_range_max = {start_x, x.first};
574 if (start_y >= value_min && end_y >= value_min) {
575 x_range_min = {start_x, end_x};
576 }
else if (start_y >= value_min || end_y >= value_min) {
578 ? ComputeXFromY(end_x, end_y, slope, value_min)
579 : ComputeXFromY(start_x, start_y, slope, value_min);
580 if (end_y >= value_min) {
581 x_range_min = {x.second, end_x};
583 x_range_min = {start_x, x.first};
586 if (x_range_min.first > x_range_max.second ||
587 x_range_max.first > x_range_min.second) {
590 return {
std::max(x_range_min.first, x_range_max.first),
591 std::min(x_range_min.second, x_range_max.second)};
595 std::pair<int64_t, int64_t>
599 int64_t value_max)
const {
602 int start_segment = -1;
603 int end_segment = -1;
604 if (!FindSegmentIndicesFromRange(range_start, range_end, &start_segment,
606 return {reduced_range_start, reduced_range_end};
608 for (
int i =
std::max(0, start_segment); i <= end_segment; ++i) {
609 const auto& segment = segments_[i];
610 const int64_t start_x =
std::max(range_start, segment.start_x());
611 const int64_t end_x =
std::min(range_end, segment.end_x());
612 const int64_t start_y = segment.Value(start_x);
613 const int64_t end_y = segment.Value(end_x);
614 const std::pair<int64_t, int64_t>
range = GetRangeInValueRange(
615 start_x, end_x, start_y, end_y, segment.slope(), value_min, value_max);
616 reduced_range_start =
std::min(reduced_range_start,
range.first);
617 reduced_range_end =
std::max(reduced_range_end,
range.second);
619 return {reduced_range_start, reduced_range_end};
624 for (
int i = 0; i < segments_.size(); ++i) {
625 segments_[i].AddConstantToX(constant);
631 for (
int i = 0; i < segments_.size(); ++i) {
632 segments_[i].AddConstantToY(constant);
637 Operation(other, [](int64_t
a, int64_t
b) {
return CapAdd(
a,
b); });
641 Operation(other, [](int64_t
a, int64_t
b) {
return CapSub(
a,
b); });
644 std::vector<PiecewiseLinearFunction*>
646 CHECK_GE(segments_.size(), 1);
651 std::vector<PiecewiseLinearFunction*> convex_functions;
652 std::vector<PiecewiseSegment> convex_segments;
655 if (convex_segments.empty()) {
656 convex_segments.push_back(segment);
661 if (FormConvexPair(last, segment)) {
663 convex_segments.push_back(segment);
666 convex_segments.clear();
667 convex_segments.push_back(segment);
671 if (!convex_segments.empty()) {
672 convex_functions.push_back(
675 return convex_functions;
679 std::string result =
"PiecewiseLinearFunction(";
680 for (
int i = 0; i < segments_.size(); ++i) {
687 void PiecewiseLinearFunction::InsertSegment(
const PiecewiseSegment& segment) {
690 if (segments_.empty() || segments_.back().end_x() < segment.
start_x()) {
691 segments_.push_back(segment);
696 if (segments_.back().end_x() == segment.
start_x()) {
697 if (segments_.back().end_y() == segment.
start_y() &&
698 segments_.back().slope() == segment.
slope()) {
699 segments_.back().ExpandEnd(segment.
end_x());
702 segments_.push_back(segment);
706 void PiecewiseLinearFunction::Operation(
707 const PiecewiseLinearFunction& other,
708 const std::function<int64_t(int64_t, int64_t)>& operation) {
710 std::vector<PiecewiseSegment> own_segments;
711 const std::vector<PiecewiseSegment>& other_segments = other.segments();
712 own_segments.swap(segments_);
714 absl::btree_set<int64_t> start_x_points;
715 for (
int i = 0; i < own_segments.size(); ++i) {
716 start_x_points.insert(own_segments[i].start_x());
718 for (
int i = 0; i < other_segments.size(); ++i) {
719 start_x_points.insert(other_segments[i].start_x());
722 for (int64_t start_x : start_x_points) {
723 const int own_index = FindSegmentIndex(own_segments, start_x);
724 const int other_index = FindSegmentIndex(other_segments, start_x);
725 if (own_index >= 0 && other_index >= 0) {
726 const PiecewiseSegment& own_segment = own_segments[own_index];
727 const PiecewiseSegment& other_segment = other_segments[other_index];
729 const int64_t end_x =
730 std::min(own_segment.end_x(), other_segment.end_x());
731 const int64_t start_y =
732 operation(own_segment.Value(start_x), other_segment.Value(start_x));
733 const int64_t end_y =
734 operation(own_segment.Value(end_x), other_segment.Value(end_x));
735 const int64_t slope =
736 operation(own_segment.slope(), other_segment.slope());
738 int64_t point_x, point_y, other_point_x;
739 if (IsAtBounds(start_y)) {
742 other_point_x = start_x;
746 other_point_x = end_x;
748 InsertSegment(PiecewiseSegment(point_x, point_y, slope, other_point_x));
753 bool PiecewiseLinearFunction::FindSegmentIndicesFromRange(
754 int64_t range_start, int64_t range_end,
int* start_segment,
755 int* end_segment)
const {
756 *start_segment = FindSegmentIndex(segments_, range_start);
757 *end_segment = FindSegmentIndex(segments_, range_end);
758 if (*start_segment == *end_segment) {
759 if (*start_segment < 0) {
763 if (segments_[*start_segment].end_x() < range_start) {
771 bool PiecewiseLinearFunction::IsConvexInternal()
const {
772 for (
int i = 1; i < segments_.size(); ++i) {
773 if (!FormConvexPair(segments_[i - 1], segments_[i])) {
780 bool PiecewiseLinearFunction::IsNonDecreasingInternal()
const {
782 for (
const auto& segment : segments_) {
783 const int64_t start_y = segment.
start_y();
784 const int64_t end_y = segment.
end_y();
785 if (end_y < start_y || start_y <
value)
return false;
791 bool PiecewiseLinearFunction::IsNonIncreasingInternal()
const {
793 for (
const auto& segment : segments_) {
794 const int64_t start_y = segment.
start_y();
795 const int64_t end_y = segment.
end_y();
796 if (end_y > start_y || start_y >
value)
return false;
int64_t GetMaximum() const
const std::vector< PiecewiseSegment > & segments() const
static PiecewiseLinearFunction * CreateRightRayFunction(int64_t point_x, int64_t point_y, int64_t slope)
void AddConstantToX(int64_t constant)
static PiecewiseLinearFunction * CreateLeftRayFunction(int64_t point_x, int64_t point_y, int64_t slope)
static PiecewiseLinearFunction * CreateStepFunction(std::vector< int64_t > points_x, std::vector< int64_t > points_y, std::vector< int64_t > other_points_x)
std::pair< int64_t, int64_t > GetSmallestRangeInValueRange(int64_t range_start, int64_t range_end, int64_t value_min, int64_t value_max) const
static const int kNotFound
int64_t Value(int64_t x) const
std::vector< PiecewiseLinearFunction * > DecomposeToConvexFunctions() const
std::string DebugString() const
void Add(const PiecewiseLinearFunction &other)
bool IsNonDecreasing() const
static PiecewiseLinearFunction * CreateOneSegmentFunction(int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x)
int64_t GetMinimum() const
void AddConstantToY(int64_t constant)
bool IsNonIncreasing() const
std::pair< int64_t, int64_t > GetSmallestRangeGreaterThanValue(int64_t range_start, int64_t range_end, int64_t value) const
bool InDomain(int64_t x) const
void Subtract(const PiecewiseLinearFunction &other)
std::pair< int64_t, int64_t > GetSmallestRangeLessThanValue(int64_t range_start, int64_t range_end, int64_t value) const
static PiecewiseLinearFunction * CreatePiecewiseLinearFunction(std::vector< int64_t > points_x, std::vector< int64_t > points_y, std::vector< int64_t > slopes, std::vector< int64_t > other_points_x)
static PiecewiseLinearFunction * CreateEarlyTardyFunctionWithSlack(int64_t early_slack, int64_t late_slack, int64_t earliness_slope, int64_t tardiness_slope)
static PiecewiseLinearFunction * CreateFullDomainFunction(int64_t initial_level, std::vector< int64_t > points_x, std::vector< int64_t > slopes)
static PiecewiseLinearFunction * CreateEarlyTardyFunction(int64_t reference, int64_t earliness_slope, int64_t tardiness_slope)
static PiecewiseLinearFunction * CreateFixedChargeFunction(int64_t slope, int64_t value)
void AddConstantToX(int64_t constant)
PiecewiseSegment(int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x)
int64_t Value(int64_t x) const
std::string DebugString() const
static bool FindComparator(int64_t point, const PiecewiseSegment &segment)
void ExpandEnd(int64_t end_x)
void AddConstantToY(int64_t constant)
static bool SortComparator(const PiecewiseSegment &segment1, const PiecewiseSegment &segment2)
static const uint64_t kuint64max
static const int64_t kint64max
static const int64_t kint64min
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
const std::optional< Range > & range