23 #include "absl/numeric/int128.h"
30 int64_t Gcd(
const absl::Span<const int64_t> coeffs) {
32 int64_t gcd = std::abs(coeffs[0]);
33 for (
int i = 1; i < coeffs.size(); ++i) {
35 const int64_t abs_coeff = std::abs(coeffs[i]);
36 gcd = std::gcd(gcd, abs_coeff);
44 const int elements_to_consider,
45 std::vector<absl::int128>& v) {
46 DCHECK(!basis.empty());
47 for (
int i = elements_to_consider - 1; i >= 0; --i) {
48 const int n =
static_cast<int>(basis[i].size()) - 1;
49 const absl::int128 leading_coeff = basis[i][n];
50 if (leading_coeff == 0)
continue;
52 const absl::int128 q =
60 for (
int j = 0; j <= n; ++j) v[j] -= q * basis[i][j];
65 const absl::Span<const int64_t> coeffs) {
66 std::vector<int> result;
68 int64_t min_abs_coeff = std::abs(coeffs[0]);
70 int64_t global_gcd = min_abs_coeff;
71 for (
int i = 1; i < coeffs.size(); ++i) {
73 const int64_t abs_coeff = std::abs(coeffs[i]);
74 global_gcd = std::gcd(global_gcd, abs_coeff);
75 if (abs_coeff < min_abs_coeff) {
76 min_abs_coeff = abs_coeff;
80 if (min_abs_coeff == global_gcd)
return result;
81 int64_t current_gcd = min_abs_coeff;
82 result.reserve(coeffs.size());
83 result.push_back(min_term);
84 while (current_gcd > global_gcd) {
88 int64_t new_gcd = std::gcd(current_gcd, std::abs(coeffs[0]));
90 for (
int i = 1; i < coeffs.size(); ++i) {
91 const int64_t gcd = std::gcd(current_gcd, std::abs(coeffs[i]));
97 result.push_back(term);
98 current_gcd = new_gcd;
100 const int initial_count =
static_cast<int>(result.size());
101 for (
int i = 0; i < coeffs.size(); ++i) {
104 for (
int j = 0; j < initial_count; ++j) {
105 if (result[j] == i) {
119 absl::Span<const int64_t> var_lbs,
120 absl::Span<const int64_t> var_ubs) {
121 const int64_t global_gcd = Gcd(coeffs);
123 if (rhs % global_gcd != 0)
return {.has_solutions =
false};
126 if (pivots.empty()) {
127 return {.no_reformulation_needed =
true, .has_solutions =
true};
129 int64_t current_gcd = std::abs(coeffs[pivots[0]]);
132 std::vector<absl::int128> special_solution = {current_gcd /
135 std::vector<std::vector<absl::int128>> kernel_basis;
136 kernel_basis.reserve(coeffs.size() - 1);
138 for (; i < pivots.size() && current_gcd > global_gcd; ++i) {
139 const int64_t coeff = coeffs[pivots[i]];
140 const int64_t new_gcd = std::gcd(current_gcd, std::abs(coeff));
141 kernel_basis.emplace_back(i + 1);
142 kernel_basis.back().back() = current_gcd / new_gcd;
143 for (
int i = 0; i < special_solution.size(); ++i) {
144 kernel_basis.back()[i] = -special_solution[i] * coeff / new_gcd;
147 kernel_basis.back());
150 int64_t
a = current_gcd;
155 for (
int i = 0; i < special_solution.size(); ++i) {
156 special_solution[i] *= u;
158 special_solution.push_back(v);
161 current_gcd = new_gcd;
163 const int replaced_variables_count = i;
164 for (; i < pivots.size(); ++i) {
165 const int64_t coeff = coeffs[pivots[i]];
166 kernel_basis.emplace_back(replaced_variables_count);
167 for (
int i = 0; i < special_solution.size(); ++i) {
168 kernel_basis.back()[i] = -special_solution[i] * coeff / global_gcd;
171 kernel_basis.back());
174 for (
int i = 0; i < special_solution.size(); ++i) {
175 special_solution[i] *= rhs / global_gcd;
191 std::vector<absl::int128> kernel_vars_lbs(replaced_variables_count - 1);
192 std::vector<absl::int128> kernel_vars_ubs(replaced_variables_count - 1);
193 for (
int i = replaced_variables_count - 1; i >= 0; --i) {
194 absl::int128 lb = var_lbs[pivots[i]] - special_solution[i];
195 absl::int128 ub = var_ubs[pivots[i]] - special_solution[i];
197 const int bounds_to_update = i > 0 ? i - 1 : 0;
198 for (
int j = bounds_to_update + 1; j < replaced_variables_count - 1; ++j) {
199 const absl::int128 coeff = kernel_basis[j][i];
200 lb -= coeff * (coeff < 0 ? kernel_vars_lbs[j] : kernel_vars_ubs[j]);
201 ub -= coeff * (coeff < 0 ? kernel_vars_ubs[j] : kernel_vars_lbs[j]);
203 for (
int j = replaced_variables_count - 1; j < pivots.size() - 1; ++j) {
204 const absl::int128 coeff = kernel_basis[j][i];
205 const int64_t lb_var = var_lbs[pivots[j + 1]];
206 const int64_t ub_var = var_ubs[pivots[j + 1]];
207 lb -= coeff * (coeff < 0 ? lb_var : ub_var);
208 ub -= coeff * (coeff < 0 ? ub_var : lb_var);
210 const absl::int128 coeff = kernel_basis[bounds_to_update][i];
211 const absl::int128 deduced_lb =
CeilOfRatio(coeff > 0 ? lb : ub, coeff);
212 const absl::int128 deduced_ub =
FloorOfRatio(coeff > 0 ? ub : lb, coeff);
214 kernel_vars_lbs[i - 1] = deduced_lb;
215 kernel_vars_ubs[i - 1] = deduced_ub;
217 kernel_vars_lbs[0] =
std::max(kernel_vars_lbs[0], deduced_lb);
218 kernel_vars_ubs[0] =
std::min(kernel_vars_ubs[0], deduced_ub);
221 for (
int i = 0; i < replaced_variables_count - 1; ++i) {
222 if (kernel_vars_lbs[i] > kernel_vars_ubs[i])
223 return {.has_solutions =
false};
225 return {.no_reformulation_needed =
false,
226 .has_solutions =
true,
227 .index_permutation = pivots,
228 .special_solution = special_solution,
229 .kernel_basis = kernel_basis,
230 .kernel_vars_lbs = kernel_vars_lbs,
231 .kernel_vars_ubs = kernel_vars_ubs};
DiophantineSolution SolveDiophantine(absl::Span< const int64_t > coeffs, int64_t rhs, absl::Span< const int64_t > var_lbs, absl::Span< const int64_t > var_ubs)
IntType CeilOfRatio(IntType numerator, IntType denominator)
std::vector< int > GreedyFastDecreasingGcd(const absl::Span< const int64_t > coeffs)
IntType FloorOfRatio(IntType numerator, IntType denominator)
bool SolveDiophantineEquationOfSizeTwo(int64_t &a, int64_t &b, int64_t &cte, int64_t &x0, int64_t &y0)
void ReduceModuloBasis(const std::vector< std::vector< absl::int128 >> &basis, const int elements_to_consider, std::vector< absl::int128 > &v)