20#include "absl/log/check.h" 
   35    : num_tasks_(helper->NumTasks()),
 
   41  mandatory_energy_before_end_max_.resize(num_tasks_);
 
   42  mandatory_energy_before_start_min_.resize(num_tasks_);
 
   45  size_free_.resize(num_tasks_);
 
   46  energy_free_.resize(num_tasks_);
 
 
   50  const int id = watcher->
Register(
this);
 
   52  helper_->WatchAllTasks(
id);
 
   53  for (
int t = 0; t < num_tasks_; t++) {
 
 
   61  if (!helper_->SynchronizeAndSetTimeDirection(
true)) 
return false;
 
   62  if (!TimeTableEdgeFindingPass()) 
return false;
 
   63  if (!helper_->SynchronizeAndSetTimeDirection(
false)) 
return false;
 
   64  if (!TimeTableEdgeFindingPass()) 
return false;
 
 
   68void TimeTableEdgeFinding::BuildTimeTable() {
 
   76    const IntegerValue start_max = -negated_smax;
 
   77    if (start_max < helper_->EndMin(t)) {
 
   78      scp_.push_back({t, start_max});
 
   83  for (
const auto task_time : helper_->TaskByIncreasingEndMin()) {
 
   84    const int t = task_time.task_index;
 
   85    if (!helper_->IsPresent(t)) 
continue;
 
   86    if (helper_->StartMax(t) < task_time.time) {
 
   87      ecp_.push_back(task_time);
 
   91  DCHECK_EQ(scp_.size(), ecp_.size());
 
   93  const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
 
   94  const auto by_start_min = helper_->TaskByIncreasingStartMin();
 
   96  IntegerValue height = IntegerValue(0);
 
   97  IntegerValue energy = IntegerValue(0);
 
  101  IntegerValue previous_time = IntegerValue(0);
 
  106  int index_emax = num_tasks_ - 1;  
 
  108  while (index_emax >= 0) {
 
  110    IntegerValue time = by_decreasing_end_max[index_emax].time;
 
  111    if (index_smin < num_tasks_) {
 
  112      time = std::min(time, by_start_min[index_smin].time);
 
  114    if (index_scp < scp_.size()) {
 
  115      time = std::min(time, scp_[index_scp].time);
 
  117    if (index_ecp < ecp_.size()) {
 
  118      time = std::min(time, ecp_[index_ecp].time);
 
  122    energy += (time - previous_time) * height;
 
  123    previous_time = time;
 
  126    while (index_smin < num_tasks_ && by_start_min[index_smin].time == time) {
 
  127      mandatory_energy_before_start_min_[by_start_min[index_smin].task_index] =
 
  133    while (index_emax >= 0 && by_decreasing_end_max[index_emax].time == time) {
 
  134      mandatory_energy_before_end_max_[by_decreasing_end_max[index_emax]
 
  135                                           .task_index] = energy;
 
  140    while (index_scp < scp_.size() && scp_[index_scp].time == time) {
 
  141      height += demands_->DemandMin(scp_[index_scp].task_index);
 
  146    while (index_ecp < ecp_.size() && ecp_[index_ecp].time == time) {
 
  147      height -= demands_->DemandMin(ecp_[index_ecp].task_index);
 
  153bool TimeTableEdgeFinding::TimeTableEdgeFindingPass() {
 
  154  if (!demands_->CacheAllEnergyValues()) 
return true;
 
  156  IntegerValue earliest_start_min = std::numeric_limits<IntegerValue>::max();
 
  157  IntegerValue latest_end_max = std::numeric_limits<IntegerValue>::min();
 
  158  IntegerValue maximum_demand_min = IntegerValue(0);
 
  161  for (
int t = 0; t < num_tasks_; ++t) {
 
  163    const IntegerValue start_max = helper_->StartMax(t);
 
  164    const IntegerValue end_min = helper_->EndMin(t);
 
  165    const IntegerValue demand_min = demands_->DemandMin(t);
 
  166    IntegerValue mandatory_energy(0);
 
  168    earliest_start_min = std::min(earliest_start_min, helper_->StartMin(t));
 
  169    latest_end_max = std::max(latest_end_max, helper_->EndMax(t));
 
  170    maximum_demand_min = std::max(maximum_demand_min, demand_min);
 
  172    if (start_max >= end_min) {
 
  173      size_free_[t] = helper_->SizeMin(t);
 
  175      const IntegerValue mandatory_size = end_min - start_max;
 
  176      size_free_[t] = helper_->SizeMin(t) - mandatory_size;
 
  177      mandatory_energy = mandatory_size * demand_min;
 
  180    const IntegerValue min_energy = demands_->EnergyMin(t);
 
  181    energy_free_[t] = min_energy - mandatory_energy;
 
  182    DCHECK_GE(energy_free_[t], 0);
 
  186                                maximum_demand_min))) {
 
  195  const auto& by_start_min = helper_->TaskByIncreasingStartMin();
 
  202  for (
const TaskTime end_task_time : helper_->TaskByDecreasingEndMax()) {
 
  203    const int end_task = end_task_time.task_index;
 
  206    if (!helper_->IsPresent(end_task)) 
continue;
 
  207    if (energy_free_[end_task] == 0) 
continue;
 
  210    if (end_task_time.time == previous_end) 
continue;
 
  211    previous_end = end_task_time.time;
 
  215    IntegerValue energy_free_parts = IntegerValue(0);
 
  216    reason_tasks_fully_included_in_window_.clear();
 
  217    reason_tasks_partially_included_in_window_.clear();
 
  223    IntegerValue free_energy_of_max_task_in_window(0);
 
  227    const IntegerValue window_max = end_task_time.time;
 
  229      const int begin_task = begin_task_time.task_index;
 
  233      const IntegerValue window_min = begin_task_time.time;
 
  236      if (window_max <= window_min) 
continue;
 
  239      if (!helper_->IsPresent(begin_task)) 
continue;
 
  240      if (energy_free_[begin_task] == 0) 
continue;
 
  258      const IntegerValue end_max = helper_->EndMax(begin_task);
 
  259      if (end_max <= window_max) {
 
  261        reason_tasks_fully_included_in_window_.push_back(begin_task);
 
  262        energy_free_parts += energy_free_[begin_task];
 
  264        const IntegerValue demand_min = demands_->DemandMin(begin_task);
 
  265        const IntegerValue extra_energy =
 
  266            std::min(size_free_[begin_task], (window_max - window_min)) *
 
  271        const IntegerValue free_energy_in_window =
 
  272            std::max(IntegerValue(0),
 
  273                     size_free_[begin_task] - (end_max - window_max)) *
 
  278        if (extra_energy > extra_energy_required_by_max_task) {
 
  279          if (max_task != -1 && free_energy_of_max_task_in_window > 0) {
 
  280            reason_tasks_partially_included_in_window_.push_back(max_task);
 
  283          max_task = begin_task;
 
  284          extra_energy_required_by_max_task = extra_energy;
 
  288          energy_free_parts += free_energy_of_max_task_in_window;
 
  289          free_energy_of_max_task_in_window = free_energy_in_window;
 
  290        } 
else if (free_energy_in_window > 0) {
 
  291          reason_tasks_partially_included_in_window_.push_back(begin_task);
 
  292          energy_free_parts += free_energy_in_window;
 
  305      if (max_task == -1 || demands_->DemandMin(max_task) == 0) 
continue;
 
  308      const IntegerValue window_energy =
 
  309          CapacityMax() * (window_max - window_min);
 
  310      const IntegerValue energy_mandatory =
 
  311          mandatory_energy_before_end_max_[end_task] -
 
  312          mandatory_energy_before_start_min_[begin_task];
 
  313      const IntegerValue available_energy =
 
  314          window_energy - energy_free_parts - energy_mandatory;
 
  320      if (extra_energy_required_by_max_task <= available_energy) {
 
  327        if (energy_free_[max_task] > available_energy &&
 
  328            helper_->EndMin(max_task) <= window_max) {
 
  329          FillEnergyInWindowReason(window_min, window_max, max_task);
 
  330          demands_->AddEnergyMinReason(max_task);
 
  331          helper_->AddStartMinReason(max_task, window_min);
 
  332          if (!helper_->IncreaseEndMin(max_task, window_max + 1)) 
return false;
 
  343      const IntegerValue mandatory_size_in_window =
 
  344          std::max(IntegerValue(0),
 
  345                   std::min(window_max, helper_->EndMin(max_task)) -
 
  346                       std::max(window_min, helper_->StartMax(max_task)));
 
  349      const IntegerValue max_free_size_that_fit =
 
  350          available_energy / demands_->DemandMin(max_task);
 
  351      const IntegerValue new_start =
 
  352          window_max - mandatory_size_in_window - max_free_size_that_fit;
 
  355      if (helper_->StartMin(max_task) < new_start) {
 
  356        FillEnergyInWindowReason(window_min, window_max, max_task);
 
  360        helper_->AddStartMinReason(max_task, window_min);
 
  361        demands_->AddDemandMinReason(max_task);
 
  363        if (!helper_->IncreaseStartMin(max_task, new_start)) 
return false;
 
  371void TimeTableEdgeFinding::FillEnergyInWindowReason(IntegerValue window_min,
 
  372                                                    IntegerValue window_max,
 
  374  helper_->ClearReason();
 
  378    helper_->MutableIntegerReason()->push_back(
 
  379        integer_trail_->UpperBoundAsLiteral(capacity_.var));
 
  383  for (
int t = 0; t < num_tasks_; ++t) {
 
  384    if (t == task_index) 
continue;
 
  385    if (!helper_->IsPresent(t)) 
continue;
 
  386    const IntegerValue smax = helper_->StartMax(t);
 
  387    const IntegerValue emin = helper_->EndMin(t);
 
  388    if (smax >= emin) 
continue;
 
  389    if (emin <= window_min) 
continue;
 
  390    if (smax >= window_max) 
continue;
 
  391    helper_->AddStartMaxReason(t, std::max(smax, window_min));
 
  392    helper_->AddEndMinReason(t, std::min(emin, window_max));
 
  393    helper_->AddPresenceReason(t);
 
  394    demands_->AddDemandMinReason(t);
 
  401  for (
const int t : reason_tasks_fully_included_in_window_) {
 
  402    DCHECK_NE(t, task_index);
 
  403    DCHECK(helper_->IsPresent(t));
 
  404    DCHECK_GT(helper_->EndMax(t), window_min);
 
  405    DCHECK_LT(helper_->StartMin(t), window_max);
 
  406    DCHECK_GE(helper_->StartMin(t), window_min);
 
  408    helper_->AddStartMinReason(t, helper_->StartMin(t));
 
  409    helper_->AddEndMaxReason(t, std::max(window_max, helper_->EndMax(t)));
 
  410    helper_->AddPresenceReason(t);
 
  411    demands_->AddEnergyMinReason(t);
 
  413  for (
const int t : reason_tasks_partially_included_in_window_) {
 
  414    DCHECK_NE(t, task_index);
 
  415    DCHECK(helper_->IsPresent(t));
 
  416    DCHECK_GT(helper_->EndMax(t), window_min);
 
  417    DCHECK_LT(helper_->StartMin(t), window_max);
 
  418    DCHECK_GE(helper_->StartMin(t), window_min);
 
  420    helper_->AddStartMinReason(t, helper_->StartMin(t));
 
  421    helper_->AddEndMaxReason(t, std::max(window_max, helper_->EndMax(t)));
 
  422    helper_->AddPresenceReason(t);
 
  424    helper_->AddSizeMinReason(t);
 
  425    demands_->AddDemandMinReason(t);
 
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
bool IsPresent(int t) const
ReverseView< Container > reversed_view(const Container &c)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerValue CapSubI(IntegerValue a, IntegerValue b)
bool AtMinOrMaxInt64I(IntegerValue t)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
Overflows and saturated arithmetic.
In SWIG mode, we don't want anything besides these top-level includes.