26#include "absl/algorithm/container.h"
27#include "absl/container/btree_set.h"
28#include "absl/log/check.h"
29#include "absl/numeric/bits.h"
30#include "absl/numeric/int128.h"
31#include "absl/random/bit_gen_ref.h"
32#include "absl/random/distributions.h"
33#include "absl/strings/str_cat.h"
34#include "absl/types/span.h"
35#include "google/protobuf/descriptor.h"
48inline std::string LeftAlign(std::string s,
int size = 16) {
49 if (s.size() >= size)
return s;
54inline std::string RightAlign(std::string s,
int size = 16) {
55 if (s.size() >= size)
return s;
56 return absl::StrCat(std::string(size - s.size(),
' '), s);
61std::string
FormatTable(std::vector<std::vector<std::string>>& table,
63 if (table.size() > 1) {
65 std::sort(table.begin() + 1, table.end());
68 std::vector<int> widths;
69 for (
const std::vector<std::string>& line : table) {
70 if (line.size() > widths.size()) widths.resize(line.size(), spacing);
71 for (
int j = 0; j < line.size(); ++j) {
72 widths[j] = std::max<int>(widths[j], line[j].size() + spacing);
76 for (
int i = 0;
i < table.size(); ++
i) {
77 for (
int j = 0; j < table[
i].size(); ++j) {
78 if (
i == 0 && j == 0) {
80 absl::StrAppend(&output, LeftAlign(table[
i][j], widths[j]));
82 absl::StrAppend(&output, RightAlign(table[
i][j], widths[j]));
85 absl::StrAppend(&output,
"\n");
92#if !defined(__PORTABLE_PLATFORM__)
94 const google::protobuf::EnumDescriptor* order_d =
98 order_d->value(absl::Uniform(random, 0, order_d->value_count()))
102 const google::protobuf::EnumDescriptor* polarity_d =
105 polarity_d->value(absl::Uniform(random, 0, polarity_d->value_count()))
123void QuotientAndRemainder(int64_t a, int64_t
b, int64_t& q, int64_t& r) {
137 int64_t r[2] = {m, x};
138 int64_t t[2] = {0, 1};
151 for (; r[
i ^ 1] != 0;
i ^= 1) {
152 QuotientAndRemainder(r[
i], r[
i ^ 1], q, r[
i]);
153 t[
i] -= t[
i ^ 1] * q;
157 if (r[
i] != 1)
return 0;
161 if (t[
i] < 0) t[
i] += m;
167 const int64_t r = x % m;
168 return r < 0 ? r + m : r;
176 if (rhs == 0 || mod == 1)
return 0;
177 DCHECK_EQ(std::gcd(std::abs(coeff), mod), 1);
186 CHECK_NE(inverse, 0);
189 const absl::int128 p = absl::int128{inverse} * absl::int128{rhs};
190 return static_cast<int64_t
>(p % absl::int128{mod});
194 int64_t& x0, int64_t& y0) {
197 CHECK_NE(a, std::numeric_limits<int64_t>::min());
198 CHECK_NE(
b, std::numeric_limits<int64_t>::min());
200 const int64_t gcd = std::gcd(std::abs(a), std::abs(
b));
201 if (cte % gcd != 0)
return false;
217 if (cte < 0 && x0 != 0) x0 -= std::abs(
b);
223 const absl::int128 t = absl::int128{cte} - absl::int128{a} * absl::int128{x0};
224 DCHECK_EQ(t % absl::int128{
b}, absl::int128{0});
229 const absl::int128 r = t / absl::int128{
b};
230 DCHECK_LE(r, absl::int128{std::numeric_limits<int64_t>::max()});
231 DCHECK_GE(r, absl::int128{std::numeric_limits<int64_t>::min()});
233 y0 =
static_cast<int64_t
>(r);
240 if (x.IsEmpty() || y.
IsEmpty())
return false;
241 if (a == 0 &&
b == 0) {
245 const int64_t div = cte /
b;
246 if (
b * div != cte)
return false;
250 const int64_t div = cte / a;
251 if (a * div != cte)
return false;
252 return x.Contains(div);
256 const int64_t div = cte / a;
257 if (a * div != cte) {
272 x.AdditionWith(
Domain(-x0))
273 .InverseMultiplicationBy(
b)
277 const Domain z_restricted_d1 =
278 x.AdditionWith(
Domain(-x0)).InverseMultiplicationBy(
b);
279 const Domain z_restricted_d2 =
281 const Domain z_restricted_domain =
283 return !z_restricted_domain.
IsEmpty();
291 static_cast<int64_t
>(std::floor(std::sqrt(
static_cast<double>(a))));
292 while (
CapProd(result, result) > a) --result;
293 while (
CapProd(result + 1, result + 1) <= a) ++result;
300 static_cast<int64_t
>(std::ceil(std::sqrt(
static_cast<double>(a))));
301 while (
CapProd(result, result) < a) ++result;
302 while ((result - 1) * (result - 1) >= a) --result;
308 int64_t result = value /
base *
base;
309 if (value - result >
base / 2) result +=
base;
314 int64_t
base, absl::Span<const int64_t> coeffs,
315 absl::Span<const int64_t> lbs, absl::Span<const int64_t> ubs, int64_t rhs,
318 int64_t max_activity = 0;
320 int64_t min_error = 0;
321 const int num_terms = coeffs.size();
322 if (num_terms == 0)
return false;
323 for (
int i = 0;
i < num_terms; ++
i) {
324 const int64_t coeff = coeffs[
i];
327 max_activity += coeff * ubs[
i];
328 max_x += closest /
base * ubs[
i];
330 const int64_t error = coeff - closest;
332 min_error += error * lbs[
i];
334 min_error += error * ubs[
i];
338 if (max_activity <= rhs) {
345 int64_t max_error_if_invalid = 0;
346 const int64_t slack = max_activity - rhs - 1;
347 for (
int i = 0;
i < num_terms; ++
i) {
348 const int64_t coeff = coeffs[
i];
350 const int64_t error = coeff - closest;
352 max_error_if_invalid += error * ubs[
i];
354 const int64_t lb = std::max(lbs[
i], ubs[
i] - slack / coeff);
355 max_error_if_invalid += error * lb;
370 const int64_t infeasibility_bound =
374 return *new_rhs < infeasibility_bound;
378 const absl::btree_set<LiteralIndex>& processed,
int relevant_prefix_size,
379 std::vector<Literal>* literals) {
380 if (literals->empty())
return -1;
381 if (!processed.contains(literals->back().Index())) {
382 return std::min<int>(relevant_prefix_size, literals->size());
392 int num_processed = 0;
393 int num_not_processed = 0;
394 int target_prefix_size = literals->size() - 1;
395 for (
int i = literals->size() - 1;
i >= 0;
i--) {
396 if (processed.contains((*literals)[
i].Index())) {
400 target_prefix_size =
i;
402 if (num_not_processed >= num_processed)
break;
404 if (num_not_processed == 0)
return -1;
405 target_prefix_size = std::min(target_prefix_size, relevant_prefix_size);
409 std::stable_partition(
410 literals->begin() + target_prefix_size, literals->end(),
411 [&processed](
Literal l) { return processed.contains(l.Index()); });
412 return target_prefix_size;
416 DCHECK(!
input.empty());
418 double total_weight = 0;
419 for (
const double w :
input) {
423 const double weight_point = absl::Uniform(random, 0.0f, total_weight);
424 double total_weight2 = 0;
425 for (
int i = 0;
i <
input.size(); ++
i) {
426 total_weight2 +=
input[
i];
427 if (total_weight2 > weight_point) {
431 return input.size() - 1;
436 average_ = reset_value;
441 average_ += (new_record - average_) / num_records_;
446 average_ = (num_records_ == 1)
448 : (new_record + decaying_factor_ * (average_ - new_record));
452 records_.push_front(record);
453 if (records_.size() > record_limit_) {
459 CHECK_GT(records_.size(), 0);
460 CHECK_LE(percent, 100.0);
461 CHECK_GE(percent, 0.0);
463 const int num_records = records_.size();
464 const double percentile_rank =
465 static_cast<double>(num_records) * percent / 100.0 - 0.5;
466 if (percentile_rank <= 0) {
467 return *absl::c_min_element(records_);
468 }
else if (percentile_rank >= num_records - 1) {
469 return *absl::c_max_element(records_);
471 std::vector<double> sorted_records;
472 sorted_records.assign(records_.begin(), records_.end());
474 DCHECK_GE(num_records, 2);
475 DCHECK_LT(percentile_rank, num_records - 1);
476 const int lower_rank =
static_cast<int>(std::floor(percentile_rank));
477 DCHECK_LT(lower_rank, num_records - 1);
478 auto upper_it = sorted_records.begin() + lower_rank + 1;
482 absl::c_nth_element(sorted_records, upper_it);
483 auto lower_it = std::max_element(sorted_records.begin(), upper_it);
484 return *lower_it + (percentile_rank - lower_rank) * (*upper_it - *lower_it);
491 expanded_sums_.clear();
497 if (value == 0)
return;
498 if (value > bound_)
return;
499 gcd_ = std::gcd(gcd_, value);
500 AddChoicesInternal({value});
505 for (
const int64_t c : choices) {
511 if (current_max_ == bound_)
return;
514 filtered_values_.clear();
515 for (
const int64_t c : choices) {
516 if (c == 0 || c > bound_)
continue;
517 filtered_values_.push_back(c);
518 gcd_ = std::gcd(gcd_, c);
520 if (filtered_values_.empty())
return;
523 std::sort(filtered_values_.begin(), filtered_values_.end());
524 AddChoicesInternal(filtered_values_);
529 DCHECK_GE(max_value, 0);
531 if (coeff == 0 || max_value == 0)
return;
532 if (coeff > bound_)
return;
533 if (current_max_ == bound_)
return;
534 gcd_ = std::gcd(gcd_, coeff);
536 const int64_t num_values =
538 if (num_values > 10) {
541 expanded_sums_.clear();
546 filtered_values_.clear();
547 for (
int multiple = 1; multiple <= num_values; ++multiple) {
548 const int64_t v = multiple * coeff;
550 current_max_ = bound_;
553 filtered_values_.push_back(v);
555 AddChoicesInternal(filtered_values_);
558void MaxBoundedSubsetSum::AddChoicesInternal(absl::Span<const int64_t> values) {
560 if (!sums_.empty() && sums_.size() <= max_complexity_per_add_) {
561 const int old_size = sums_.size();
562 for (
int i = 0;
i < old_size; ++
i) {
563 for (
const int64_t value : values) {
564 const int64_t s = sums_[
i] + value;
565 if (s > bound_)
break;
568 current_max_ = std::max(current_max_, s);
569 if (current_max_ == bound_)
return;
576 if (bound_ <= max_complexity_per_add_) {
577 if (!sums_.empty()) {
578 expanded_sums_.assign(bound_ + 1,
false);
579 for (
const int64_t s : sums_) {
580 expanded_sums_[s] =
true;
586 if (!expanded_sums_.empty()) {
587 for (int64_t
i = bound_ - 1;
i >= 0; --
i) {
588 if (!expanded_sums_[
i])
continue;
589 for (
const int64_t value : values) {
590 if (
i + value > bound_)
break;
592 expanded_sums_[
i + value] =
true;
593 current_max_ = std::max(current_max_,
i + value);
594 if (current_max_ == bound_)
return;
604 current_max_ = bound_;
611 absl::Span<const Domain> domains, absl::Span<const int64_t> coeffs,
612 absl::Span<const int64_t> costs,
const Domain& rhs) {
613 const int num_vars = domains.size();
614 if (num_vars == 0)
return {};
616 int64_t min_activity = 0;
617 int64_t max_domain_size = 0;
618 for (
int i = 0;
i < num_vars; ++
i) {
619 max_domain_size = std::max(max_domain_size, domains[
i].Size());
621 min_activity += coeffs[
i] * domains[
i].Min();
623 min_activity += coeffs[
i] * domains[
i].Max();
632 const int64_t num_values = rhs.
Max() - min_activity + 1;
633 if (num_values < 0) {
642 const int64_t max_work_per_variable = std::min(num_values, max_domain_size);
643 if (rhs.
Max() - min_activity > 1e6)
return {};
644 if (num_vars * num_values * max_work_per_variable > 1e8)
return {};
650 for (
int i = 0;
i < num_vars; ++
i) {
652 domains_.push_back(domains[
i].AdditionWith(
Domain(-domains[
i].Min())));
653 coeffs_.push_back(coeffs[
i]);
654 costs_.push_back(costs[
i]);
657 domains[
i].Negation().AdditionWith(
Domain(domains[
i].Max())));
658 coeffs_.push_back(-coeffs[
i]);
659 costs_.push_back(-costs[
i]);
667 for (
int i = 0;
i < num_vars; ++
i) {
679 int64_t num_values,
const Domain& rhs) {
680 const int num_vars = domains_.size();
683 var_activity_states_.assign(num_vars, std::vector<State>(num_values));
686 for (
const int64_t v : domains_[0].Values()) {
687 const int64_t value = v * coeffs_[0];
689 if (value >= num_values)
break;
690 var_activity_states_[0][value].cost = v * costs_[0];
691 var_activity_states_[0][value].value = v;
695 for (
int i = 1;
i < num_vars; ++
i) {
696 const std::vector<State>& prev = var_activity_states_[
i - 1];
697 std::vector<State>& current = var_activity_states_[
i];
698 for (
int prev_value = 0; prev_value < num_values; ++prev_value) {
699 if (prev[prev_value].cost == std::numeric_limits<int64_t>::max()) {
702 for (
const int64_t v : domains_[
i].Values()) {
703 const int64_t value = prev_value + v * coeffs_[
i];
705 if (value >= num_values)
break;
706 const int64_t new_cost = prev[prev_value].cost + v * costs_[
i];
707 if (new_cost < current[value].cost) {
708 current[value].cost = new_cost;
709 current[value].value = v;
718 int64_t best_cost = std::numeric_limits<int64_t>::max();
719 int64_t best_activity;
720 for (
int v = 0; v < num_values; ++v) {
723 if (var_activity_states_.back()[v].cost < best_cost) {
724 best_cost = var_activity_states_.back()[v].cost;
729 if (best_cost == std::numeric_limits<int64_t>::max()) {
730 result.infeasible =
true;
735 result.solution.resize(num_vars);
736 int64_t current_activity = best_activity;
737 for (
int i = num_vars - 1;
i >= 0; --
i) {
738 const int64_t var_value = var_activity_states_[
i][current_activity].value;
739 result.solution[
i] = var_value;
740 current_activity -= coeffs_[
i] * var_value;
748class CliqueDecomposition {
750 CliqueDecomposition(
const std::vector<std::vector<int>>& graph,
751 absl::BitGenRef random, std::vector<int>* buffer)
752 : graph_(graph), random_(random), buffer_(buffer) {
753 const int n = graph.size();
754 permutation_.resize(n);
758 for (
int i = 0;
i < n; ++
i) permutation_[
i] =
i;
765 void DecomposeGreedily() {
766 decomposition_.clear();
769 const int n = graph_.size();
770 taken_.assign(n,
false);
771 temp_.assign(n,
false);
773 int buffer_index = 0;
774 for (
const int i : permutation_) {
775 if (taken_[
i])
continue;
778 const int start = buffer_index;
780 (*buffer_)[buffer_index++] =
i;
783 for (
const int c : graph_[
i]) {
784 if (!taken_[c]) candidates_.push_back(c);
786 while (!candidates_.empty()) {
788 int next = candidates_.front();
789 for (
const int n : candidates_) {
790 if (permutation_[n] < permutation_[next]) next = n;
795 (*buffer_)[buffer_index++] = next;
799 for (
const int head : graph_[next]) temp_[head] =
true;
801 for (
const int c : candidates_) {
802 if (taken_[c])
continue;
803 if (!temp_[c])
continue;
804 candidates_[new_size++] =
c;
806 candidates_.resize(new_size);
807 for (
const int head : graph_[next]) temp_[head] =
false;
811 decomposition_.push_back(
812 absl::MakeSpan(buffer_->data() + start, buffer_index - start));
814 DCHECK_EQ(buffer_index, n);
824 if (absl::Bernoulli(random_, 0.5)) {
825 std::reverse(decomposition_.begin(), decomposition_.end());
827 std::shuffle(decomposition_.begin(), decomposition_.end(), random_);
831 for (
const absl::Span<const int> clique : decomposition_) {
832 for (
const int i : clique) {
833 permutation_[out_index++] =
i;
838 const std::vector<absl::Span<int>>& decomposition()
const {
839 return decomposition_;
843 const std::vector<std::vector<int>>& graph_;
844 absl::BitGenRef random_;
845 std::vector<int>* buffer_;
847 std::vector<absl::Span<int>> decomposition_;
848 std::vector<int> candidates_;
849 std::vector<int> permutation_;
850 std::vector<bool> taken_;
851 std::vector<bool> temp_;
857 const std::vector<std::vector<int>>& graph, absl::BitGenRef random,
858 std::vector<int>* buffer) {
859 CliqueDecomposition decomposer(graph, random, buffer);
860 for (
int pass = 0; pass < 10; ++pass) {
861 decomposer.DecomposeGreedily();
862 if (decomposer.decomposition().size() == 1)
break;
863 decomposer.ChangeOrder();
865 return decomposer.decomposition();
869 absl::Span<const int64_t> elements, int64_t maximum_sum,
870 bool abort_if_maximum_reached) {
873 for (
const int64_t e : elements) {
875 if (e == 0 || e > maximum_sum)
continue;
879 if (sums_.size() == maximum_sum + 1)
return sums_;
882 if (abort_if_maximum_reached && sums_.back() == maximum_sum)
return sums_;
888 const int size = sums_.size();
889 const int64_t*
const data = sums_.data();
890 int64_t last_pushed = -1;
893 const int64_t a = data[
i];
894 const int64_t
b = data[j] + e;
904 if (to_push == last_pushed)
continue;
905 if (to_push > maximum_sum) {
909 last_pushed = to_push;
910 new_sums_.push_back(to_push);
918 for (; j < size; ++j) {
919 const int64_t to_push = data[j] + e;
920 if (to_push == last_pushed)
continue;
921 if (to_push > maximum_sum)
break;
922 last_pushed = to_push;
923 new_sums_.push_back(to_push);
925 std::swap(sums_, new_sums_);
932 double estimate = std::numeric_limits<double>::infinity();
933 if (num_elements < 100) {
934 estimate = 2 * pow(2.0, num_elements / 2);
936 return std::min(estimate,
static_cast<double>(bin_size) *
937 static_cast<double>(num_elements));
941 absl::Span<const int64_t> elements, int64_t bin_size) {
943 if (elements.empty())
return 0;
944 if (elements.size() == 1) {
945 if (elements[0] > bin_size)
return 0;
950 const int middle = elements.size() / 2;
951 const auto span_a = sums_a_.Compute(elements.subspan(0, middle), bin_size,
953 if (span_a.back() == bin_size)
return bin_size;
955 const auto span_b = sums_b_.Compute(elements.subspan(middle), bin_size,
957 if (span_b.back() == bin_size)
return bin_size;
962 CHECK(!span_a.empty());
963 CHECK(!span_b.empty());
964 int j = span_b.size() - 1;
965 for (
int i = 0;
i < span_a.size(); ++
i) {
966 while (j >= 0 && span_a[
i] + span_b[j] > bin_size) --j;
967 result = std::max(result, span_a[
i] + span_b[j]);
968 if (result == bin_size)
return bin_size;
974 absl::Span<const int64_t> distances,
975 std::vector<int64_t>& buffer,
976 int always_pick_mask) {
978 std::vector<int> result;
982 for (
int i = 0;
i < n; ++
i) result.push_back(
i);
988 int64_t worse = std::numeric_limits<int64_t>::max();
990 for (
int i = 0;
i < n; ++
i) {
991 if ((always_pick_mask >>
i) & 1)
continue;
994 for (
int j = 0; j < n; ++j) {
995 if (
i != j) score += distances[
i * n + j];
1003 CHECK_NE(to_exclude, -1);
1004 for (
int i = 0;
i < n; ++
i) {
1005 if (
i != to_exclude) result.push_back(
i);
1011 const int limit = 1 << n;
1012 buffer.assign(limit, 0);
1014 int best_value = -1;
1015 for (
unsigned int mask = 1; mask < limit; ++mask) {
1016 const int hamming_weight = absl::popcount(mask);
1020 if (hamming_weight > k)
continue;
1023 for (
int i = 0;
i < n; ++
i) {
1024 if ((mask >>
i) & 1) {
1025 if (low_bit == -1) {
1028 sum += distances[low_bit * n +
i];
1032 buffer[mask] = buffer[mask ^ (1 << low_bit)] + sum;
1033 if (hamming_weight == k && buffer[mask] > best_value) {
1034 if ((mask & always_pick_mask) != always_pick_mask)
continue;
1035 best_value = buffer[mask];
1040 for (
int i = 0;
i < n; ++
i) {
1041 if ((best_mask >>
i) & 1) {
1042 result.push_back(
i);
1049 absl::Span<const int64_t> coeffs) {
1050 std::vector<std::pair<int, int>> result;
1051 if (coeffs.empty())
return result;
1054 const auto append_splits = [&result](
int offset,
int size,
int num_parts) {
1055 int previous_start = 0;
1056 for (int64_t
b = 0;
b < num_parts; ++
b) {
1057 const int next_start =
static_cast<int64_t
>(
b + 1) * size / num_parts;
1058 result.push_back({offset + previous_start, next_start - previous_start});
1059 previous_start = next_start;
1063 const int num_terms = coeffs.size();
1064 const int num_buckets =
static_cast<int>(std::round(std::sqrt(num_terms)));
1066 int num_differents = 1;
1067 for (
int i = 1;
i < num_terms; ++
i) {
1068 if (coeffs[
i - 1] != coeffs[
i]) ++num_differents;
1074 if (num_differents < 20) {
1075 const int expected_part_size = num_terms / num_buckets;
1077 for (
int i = 0;
i < num_terms;) {
1079 for (; j < num_terms; ++j) {
1080 if (coeffs[j] != coeffs[
i])
break;
1083 const int local_size = j -
i;
1084 const int num_local_buckets =
1086 append_splits(
i, local_size, num_local_buckets);
1095 append_splits(0, num_terms, num_buckets);
Domain IntersectionWith(const Domain &domain) const
bool Contains(int64_t value) const
Domain AdditionWith(const Domain &domain) const
Domain InverseMultiplicationBy(int64_t coeff) const
static IntegralType CeilOfRatio(IntegralType numerator, IntegralType denominator)
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
Result Solve(absl::Span< const Domain > domains, absl::Span< const int64_t > coeffs, absl::Span< const int64_t > costs, const Domain &rhs)
void AddData(double new_record)
void Reset(double reset_value)
void AddData(double new_record)
double ComplexityEstimate(int num_elements, int64_t bin_size)
int64_t MaxSubsetSum(absl::Span< const int64_t > elements, int64_t bin_size)
void AddChoices(absl::Span< const int64_t > choices)
void Reset(int64_t bound)
void AddMultiples(int64_t coeff, int64_t max_value)
void AddRecord(double record)
double GetPercentile(double percent)
static const ::google::protobuf::EnumDescriptor *PROTOBUF_NONNULL Polarity_descriptor()
void set_random_branches_ratio(double value)
void set_preferred_variable_order(::operations_research::sat::SatParameters_VariableOrder value)
void set_initial_polarity(::operations_research::sat::SatParameters_Polarity value)
void set_use_phase_saving(bool value)
SatParameters_VariableOrder VariableOrder
void set_random_polarity_ratio(double value)
SatParameters_Polarity Polarity
static const ::google::protobuf::EnumDescriptor *PROTOBUF_NONNULL VariableOrder_descriptor()
absl::Span< const int64_t > Compute(absl::Span< const int64_t > elements, int64_t maximum_sum, bool abort_if_maximum_reached=false)
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
bool SolveDiophantineEquationOfSizeTwo(int64_t &a, int64_t &b, int64_t &cte, int64_t &x0, int64_t &y0)
int64_t FloorSquareRoot(int64_t a)
int64_t CeilSquareRoot(int64_t a)
int64_t ModularInverse(int64_t x, int64_t m)
int WeightedPick(absl::Span< const double > input, absl::BitGenRef random)
std::vector< std::pair< int, int > > HeuristicallySplitLongLinear(absl::Span< const int64_t > coeffs)
bool DiophantineEquationOfSizeTwoHasSolutionInDomain(const Domain &x, int64_t a, const Domain &y, int64_t b, int64_t cte)
std::vector< int > FindMostDiverseSubset(int k, int n, absl::Span< const int64_t > distances, std::vector< int64_t > &buffer, int always_pick_mask)
int64_t ClosestMultiple(int64_t value, int64_t base)
int64_t PositiveMod(int64_t x, int64_t m)
bool LinearInequalityCanBeReducedWithClosestMultiple(int64_t base, absl::Span< const int64_t > coeffs, absl::Span< const int64_t > lbs, absl::Span< const int64_t > ubs, int64_t rhs, int64_t *new_rhs)
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
std::vector< absl::Span< int > > AtMostOneDecomposition(const std::vector< std::vector< int > > &graph, absl::BitGenRef random, std::vector< int > *buffer)
std::string FormatTable(std::vector< std::vector< std::string > > &table, int spacing)
int64_t CapProd(int64_t x, int64_t y)
trees with all degrees equal w the current value of w
static int input(yyscan_t yyscanner)
std::vector< int64_t > solution