1 /* Copyright (C) 2008-2011 Xavier Pujol.
2     (C) 2015 Michael Walter.
3     Copyright (C) 2019 Koen de Boer & Wessel van Woerden
4 
5    This file is part of fplll. fplll is free software: you
6    can redistribute it and/or modify it under the terms of the GNU Lesser
7    General Public License as published by the Free Software Foundation,
8    either version 2.1 of the License, or (at your option) any later version.
9 
10    fplll is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    GNU Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public License
16    along with fplll. If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "svpcvp.h"
19 #include "enum/enumerate.h"
20 #include "enum/topenum.h"
21 
22 FPLLL_BEGIN_NAMESPACE
23 
24 /* Shortest vector problem
25    ======================= */
26 
27 /* Returns i such that the shortest vector of L(b) belongs to
28    L(b_0,...,b_(i-1)), assuming that the error on rdiag's is less than 100%.
29    If b is LLL-reduced, then for any reasonnable dimension,
30    max(rdiag[0],...,rdiag[i-1]) / min(rdiag[0],...,rdiag[i-1])
31    is much smaller than numeric_limits<double>::max */
last_useful_index(const Matrix<FP_NR<mpfr_t>> & r)32 static int last_useful_index(const Matrix<FP_NR<mpfr_t>> &r)
33 {
34   int i;
35   FP_NR<mpfr_t> rdiag_min_value;
36   rdiag_min_value.mul_2si(r(0, 0), 1);
37   for (i = r.get_rows() - 1; i > 0; i--)
38   {
39     if (r(i, i) <= rdiag_min_value)
40       break;
41   }
42   return i + 1;
43 }
44 
45 /* Finds the shortest vector of the basis b and returns its squared norm in
46    basisMin */
get_basis_min(Z_NR<mpz_t> & basis_min,const ZZ_mat<mpz_t> & b,int first,int last)47 static void get_basis_min(Z_NR<mpz_t> &basis_min, const ZZ_mat<mpz_t> &b, int first, int last)
48 {
49   Z_NR<mpz_t> sq_norm;
50   int n = b.get_cols();
51   b[first].dot_product(basis_min, b[first], n);
52 
53   for (int i = first + 1; i < last; i++)
54   {
55     b[i].dot_product(sq_norm, b[i], n);
56     if (sq_norm < basis_min)
57       basis_min = sq_norm;
58   }
59 }
60 
get_basis_min(Z_NR<mpz_t> & basis_min,MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,int first,int last)61 static void get_basis_min(Z_NR<mpz_t> &basis_min, MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso,
62                           int first, int last)
63 {
64   Z_NR<mpz_t> sq_norm;
65   gso.get_int_gram(basis_min, first, first);
66   for (int i = first + 1; i < last; i++)
67   {
68     gso.get_int_gram(sq_norm, i, i);
69     if (sq_norm < basis_min)
70       basis_min = sq_norm;
71   }
72 }
73 
enumerate_svp(int d,MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,FP_NR<mpfr_t> & max_dist,ErrorBoundedEvaluator & evaluator,const vector<enumf> & pruning,int flags)74 static bool enumerate_svp(int d, MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso,
75                           FP_NR<mpfr_t> &max_dist, ErrorBoundedEvaluator &evaluator,
76                           const vector<enumf> &pruning, int flags)
77 {
78   Enumeration<Z_NR<mpz_t>, FP_NR<mpfr_t>> enumobj(gso, evaluator);
79   bool dual = (flags & SVP_DUAL);
80   enumobj.enumerate(0, d, max_dist, 0, vector<FP_NR<mpfr_t>>(), vector<enumxt>(), pruning, dual);
81   return !evaluator.empty();
82 }
83 
shortest_vector_ex(ZZ_mat<mpz_t> & b,vector<Z_NR<mpz_t>> & sol_coord,SVPMethod method,const vector<double> & pruning,int flags,EvaluatorMode eval_mode,long long & sol_count,vector<vector<Z_NR<mpz_t>>> * subsol_coord=nullptr,vector<enumf> * subsol_dist=nullptr,vector<vector<Z_NR<mpz_t>>> * auxsol_coord=nullptr,vector<enumf> * auxsol_dist=nullptr,int max_aux_sols=0)84 static int shortest_vector_ex(ZZ_mat<mpz_t> &b, vector<Z_NR<mpz_t>> &sol_coord, SVPMethod method,
85                               const vector<double> &pruning, int flags, EvaluatorMode eval_mode,
86                               long long &sol_count,
87                               vector<vector<Z_NR<mpz_t>>> *subsol_coord = nullptr,
88                               vector<enumf> *subsol_dist                = nullptr,
89                               vector<vector<Z_NR<mpz_t>>> *auxsol_coord = nullptr,
90                               vector<enumf> *auxsol_dist = nullptr, int max_aux_sols = 0)
91 {
92   bool findsubsols = (subsol_coord != nullptr) && (subsol_dist != nullptr);
93   bool findauxsols = (auxsol_coord != nullptr) && (auxsol_dist != nullptr) && (max_aux_sols != 0);
94 
95   // d = lattice dimension (note that it might decrease during preprocessing)
96   int d = b.get_rows();
97   // n = dimension of the space
98   int n = b.get_cols();
99 
100   FPLLL_CHECK(d > 0 && n > 0, "shortestVector: empty matrix");
101   FPLLL_CHECK(d <= n, "shortestVector: number of vectors > size of the vectors");
102 
103   // Sets the floating-point precision
104   // Error bounds on GSO are valid if prec >= minprec
105   double rho;
106   int min_prec = gso_min_prec(rho, d, LLL_DEF_DELTA, LLL_DEF_ETA);
107   int prec     = max(53, min_prec + 10);
108   int old_prec = FP_NR<mpfr_t>::set_prec(prec);
109 
110   // Allocates space for vectors and matrices in constructors
111   ZZ_mat<mpz_t> empty_mat;
112   MatGSO<Z_NR<mpz_t>, FP_NR<mpfr_t>> gso(b, empty_mat, empty_mat, GSO_INT_GRAM);
113   FP_NR<mpfr_t> max_dist;
114   Z_NR<mpz_t> int_max_dist;
115   Z_NR<mpz_t> itmp1;
116 
117   // Computes the Gram-Schmidt orthogonalization in floating-point
118   gso.update_gso();
119   gen_zero_vect(sol_coord, d);
120 
121   // If the last b_i* are too large, removes them to avoid an underflow
122   int new_d = last_useful_index(gso.get_r_matrix());
123   if (new_d < d)
124   {
125     // FPLLL_TRACE("Ignoring the last " << d - new_d << " vector(s)");
126     d = new_d;
127   }
128 
129   if (flags & SVP_DUAL)
130   {
131     max_dist = 1.0 / gso.get_r_exp(d - 1, d - 1);
132     if (flags & SVP_VERBOSE)
133     {
134       cout << "max_dist = " << max_dist << endl;
135     }
136   }
137   else
138   {
139     /* Computes a bound for the enumeration. This bound would work for an
140        exact algorithm, but we will increase it later to ensure that the fp
141        algorithm finds a solution */
142     get_basis_min(int_max_dist, b, 0, d);
143     max_dist.set_z(int_max_dist, GMP_RNDU);
144   }
145 
146   // Initializes the evaluator of solutions
147   ErrorBoundedEvaluator *evaluator;
148   if (method == SVPM_FAST)
149   {
150     evaluator =
151         new FastErrorBoundedEvaluator(d, gso.get_mu_matrix(), gso.get_r_matrix(), eval_mode,
152                                       max_aux_sols + 1, EVALSTRATEGY_BEST_N_SOLUTIONS, findsubsols);
153   }
154   else if (method == SVPM_PROVED)
155   {
156     ExactErrorBoundedEvaluator *p = new ExactErrorBoundedEvaluator(
157         // d, b, gso.get_mu_matrix(), gso.get_r_matrix()
158         d, gso, eval_mode, max_aux_sols + 1, EVALSTRATEGY_BEST_N_SOLUTIONS, findsubsols);
159     p->int_max_dist = int_max_dist;
160     evaluator       = p;
161   }
162   else
163   {
164     FPLLL_ABORT("shortestVector: invalid evaluator type");
165   }
166   evaluator->init_delta_def(prec, rho, true);
167 
168   if (!(flags & SVP_OVERRIDE_BND) && (eval_mode == EVALMODE_SV || method == SVPM_PROVED))
169   {
170     FP_NR<mpfr_t> ftmp1;
171     bool result = evaluator->get_max_error_aux(max_dist, true, ftmp1);
172     FPLLL_CHECK(result, "shortestVector: cannot compute an initial bound");
173     max_dist.add(max_dist, ftmp1, GMP_RNDU);
174   }
175 
176   // Main loop of the enumeration
177   enumerate_svp(d, gso, max_dist, *evaluator, pruning, flags);
178 
179   int result = RED_ENUM_FAILURE;
180   if (eval_mode != EVALMODE_SV)
181   {
182     result    = RED_SUCCESS;
183     sol_count = evaluator->sol_count * 2;
184   }
185   else if (!evaluator->empty())
186   {
187     /*FP_NR<mpfr_t> fMaxError;
188     validMaxError = evaluator->get_max_error(fMaxError);
189     max_error = fMaxError.get_d(GMP_RNDU);*/
190     for (int i = 0; i < d; i++)
191     {
192       itmp1.set_f(evaluator->begin()->second[i]);
193       sol_coord[i].add(sol_coord[i], itmp1);
194     }
195     result = RED_SUCCESS;
196   }
197 
198   if (findsubsols)
199   {
200     subsol_coord->clear();
201     subsol_dist->clear();
202     subsol_dist->resize(evaluator->sub_solutions.size());
203     for (size_t i = 0; i < evaluator->sub_solutions.size(); ++i)
204     {
205       (*subsol_dist)[i] = evaluator->sub_solutions[i].first.get_d();
206 
207       vector<Z_NR<mpz_t>> ss_c;
208       for (size_t j = 0; j < evaluator->sub_solutions[i].second.size(); ++j)
209       {
210         itmp1.set_f(evaluator->sub_solutions[i].second[j]);
211         ss_c.emplace_back(itmp1);
212       }
213       subsol_coord->emplace_back(std::move(ss_c));
214     }
215   }
216   if (findauxsols)
217   {
218     auxsol_coord->clear();
219     auxsol_dist->clear();
220     // iterators over all solutions
221     auto it = evaluator->begin(), itend = evaluator->end();
222     // skip shortest solution
223     ++it;
224     for (; it != itend; ++it)
225     {
226       auxsol_dist->push_back(it->first.get_d());
227 
228       vector<Z_NR<mpz_t>> as_c;
229       for (size_t j = 0; j < it->second.size(); ++j)
230       {
231         itmp1.set_f(it->second[j]);
232         as_c.emplace_back(itmp1);
233       }
234       auxsol_coord->emplace_back(std::move(as_c));
235     }
236   }
237 
238   delete evaluator;
239   FP_NR<mpfr_t>::set_prec(old_prec);
240   return result;
241 }
242 
shortest_vector(ZZ_mat<mpz_t> & b,vector<Z_NR<mpz_t>> & sol_coord,SVPMethod method,int flags)243 int shortest_vector(ZZ_mat<mpz_t> &b, vector<Z_NR<mpz_t>> &sol_coord, SVPMethod method, int flags)
244 {
245   long long tmp;
246   return shortest_vector_ex(b, sol_coord, method, vector<double>(), flags, EVALMODE_SV, tmp);
247 }
248 
shortest_vector_pruning(ZZ_mat<mpz_t> & b,vector<Z_NR<mpz_t>> & sol_coord,const vector<double> & pruning,int flags)249 int shortest_vector_pruning(ZZ_mat<mpz_t> &b, vector<Z_NR<mpz_t>> &sol_coord,
250                             const vector<double> &pruning, int flags)
251 {
252   long long tmp;
253   return shortest_vector_ex(b, sol_coord, SVPM_FAST, pruning, flags, EVALMODE_SV, tmp);
254 }
255 
shortest_vector_pruning(ZZ_mat<mpz_t> & b,vector<Z_NR<mpz_t>> & sol_coord,vector<vector<Z_NR<mpz_t>>> & subsol_coord,vector<enumf> & subsol_dist,const vector<double> & pruning,int flags)256 int shortest_vector_pruning(ZZ_mat<mpz_t> &b, vector<Z_NR<mpz_t>> &sol_coord,
257                             vector<vector<Z_NR<mpz_t>>> &subsol_coord, vector<enumf> &subsol_dist,
258                             const vector<double> &pruning, int flags)
259 {
260   long long tmp;
261   return shortest_vector_ex(b, sol_coord, SVPM_FAST, pruning, flags, EVALMODE_SV, tmp,
262                             &subsol_coord, &subsol_dist);
263 }
264 
shortest_vector_pruning(ZZ_mat<mpz_t> & b,vector<Z_NR<mpz_t>> & sol_coord,vector<vector<Z_NR<mpz_t>>> & auxsol_coord,vector<enumf> & auxsol_dist,const int max_aux_sols,const vector<double> & pruning,int flags)265 int shortest_vector_pruning(ZZ_mat<mpz_t> &b, vector<Z_NR<mpz_t>> &sol_coord,
266                             vector<vector<Z_NR<mpz_t>>> &auxsol_coord, vector<enumf> &auxsol_dist,
267                             const int max_aux_sols, const vector<double> &pruning, int flags)
268 {
269   long long tmp;
270   return shortest_vector_ex(b, sol_coord, SVPM_FAST, pruning, flags, EVALMODE_SV, tmp, nullptr,
271                             nullptr, &auxsol_coord, &auxsol_dist, max_aux_sols);
272 }
273 
274 //////////////////////////////////////////
275 ////// SVP FOR GSO OBJECT  ///////////////
276 //////////////////////////////////////////
277 
shortest_vector_ex(MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,vector<Z_NR<mpz_t>> & sol_coord,SVPMethod method,const vector<double> & pruning,int flags,EvaluatorMode eval_mode,long long & sol_count,vector<vector<Z_NR<mpz_t>>> * subsol_coord=nullptr,vector<enumf> * subsol_dist=nullptr,vector<vector<Z_NR<mpz_t>>> * auxsol_coord=nullptr,vector<enumf> * auxsol_dist=nullptr,int max_aux_sols=0,bool merge_sol_in_aux=false)278 static int shortest_vector_ex(
279     MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso, vector<Z_NR<mpz_t>> &sol_coord,
280     SVPMethod method, const vector<double> &pruning, int flags, EvaluatorMode eval_mode,
281     long long &sol_count, vector<vector<Z_NR<mpz_t>>> *subsol_coord = nullptr,
282     vector<enumf> *subsol_dist = nullptr, vector<vector<Z_NR<mpz_t>>> *auxsol_coord = nullptr,
283     vector<enumf> *auxsol_dist = nullptr, int max_aux_sols = 0, bool merge_sol_in_aux = false)
284 {
285   bool findsubsols = (subsol_coord != nullptr) && (subsol_dist != nullptr);
286   bool findauxsols = (auxsol_coord != nullptr) && (auxsol_dist != nullptr) && (max_aux_sols != 0);
287 
288   // d = lattice dimension (note that it might decrease during preprocessing)
289   // int d = b.get_rows();
290   int d = gso.d;  // Number of rows of b in the GSO
291 
292   // n = dimension of the space
293   // int n = b.get_cols();
294   int n = gso.get_cols_of_b();  // number of columns of b in the GSO
295 
296   FPLLL_CHECK(d > 0 && n > 0, "shortestVector: empty matrix");
297   FPLLL_CHECK(d <= n, "shortestVector: number of vectors > size of the vectors");
298 
299   // Sets the floating-point precision
300   // Error bounds on GSO are valid if prec >= minprec
301   double rho;
302   int min_prec = gso_min_prec(rho, d, LLL_DEF_DELTA, LLL_DEF_ETA);
303   int prec     = max(53, min_prec + 10);
304   int old_prec = FP_NR<mpfr_t>::set_prec(prec);
305 
306   // Allocates space for vectors and matrices in constructors
307   FP_NR<mpfr_t> max_dist;
308   Z_NR<mpz_t> int_max_dist;
309   Z_NR<mpz_t> itmp1;
310 
311   // Computes the Gram-Schmidt orthogonalization in floating-point
312   gso.update_gso();
313   gen_zero_vect(sol_coord, d);
314 
315   // If the last b_i* are too large, removes them to avoid an underflow
316   int new_d = last_useful_index(gso.get_r_matrix());
317   if (new_d < d)
318   {
319     // FPLLL_TRACE("Ignoring the last " << d - new_d << " vector(s)");
320     d = new_d;
321   }
322 
323   if (flags & SVP_DUAL)
324   {
325     max_dist = 1.0 / gso.get_r_exp(d - 1, d - 1);
326     if (flags & SVP_VERBOSE)
327     {
328       cout << "max_dist = " << max_dist << endl;
329     }
330   }
331   else
332   {
333     // Computes a bound for the enumeration. This bound would work for an
334     //   exact algorithm, but we will increase it later to ensure that the fp
335     //   algorithm finds a solution
336 
337     // Use the GSO version of get_basis_min
338     get_basis_min(int_max_dist, gso, 0, d);
339     max_dist.set_z(int_max_dist, GMP_RNDU);
340   }
341 
342   // Initializes the evaluator of solutions
343   ErrorBoundedEvaluator *evaluator;
344   if (method == SVPM_FAST)
345   {
346     evaluator =
347         new FastErrorBoundedEvaluator(d, gso.get_mu_matrix(), gso.get_r_matrix(), eval_mode,
348                                       max_aux_sols + 1, EVALSTRATEGY_BEST_N_SOLUTIONS, findsubsols);
349   }
350   else if (method == SVPM_PROVED)
351   {
352     ExactErrorBoundedEvaluator *p = new ExactErrorBoundedEvaluator(
353         d, gso, eval_mode, max_aux_sols + 1, EVALSTRATEGY_BEST_N_SOLUTIONS, findsubsols);
354     p->int_max_dist = int_max_dist;
355     evaluator       = p;
356   }
357   else
358   {
359     FPLLL_ABORT("shortestVector: invalid evaluator type");
360   }
361   evaluator->init_delta_def(prec, rho, true);
362 
363   if (!(flags & SVP_OVERRIDE_BND) && (eval_mode == EVALMODE_SV || method == SVPM_PROVED))
364   {
365     FP_NR<mpfr_t> ftmp1;
366     bool result = evaluator->get_max_error_aux(max_dist, true, ftmp1);
367     FPLLL_CHECK(result, "shortestVector: cannot compute an initial bound");
368     max_dist.add(max_dist, ftmp1, GMP_RNDU);
369   }
370 
371   // Main loop of the enumeration
372   enumerate_svp(d, gso, max_dist, *evaluator, pruning, flags);  // Only uses r and mu
373 
374   int result = RED_ENUM_FAILURE;
375   if (eval_mode != EVALMODE_SV)
376   {
377     result    = RED_SUCCESS;
378     sol_count = evaluator->sol_count * 2;
379   }
380   else if (!evaluator->empty())
381   {
382     // FP_NR<mpfr_t> fMaxError;
383     // validMaxError = evaluator->get_max_error(fMaxError);
384     // max_error = fMaxError.get_d(GMP_RNDU);
385     for (int i = 0; i < d; i++)
386     {
387       itmp1.set_f(evaluator->begin()->second[i]);
388       sol_coord[i].add(sol_coord[i], itmp1);
389     }
390     result = RED_SUCCESS;
391   }
392 
393   if (findsubsols)
394   {
395     subsol_coord->clear();
396     subsol_dist->clear();
397     subsol_dist->resize(evaluator->sub_solutions.size());
398     for (size_t i = 0; i < evaluator->sub_solutions.size(); ++i)
399     {
400       (*subsol_dist)[i] = evaluator->sub_solutions[i].first.get_d();
401 
402       vector<Z_NR<mpz_t>> ss_c;
403       for (size_t j = 0; j < evaluator->sub_solutions[i].second.size(); ++j)
404       {
405         itmp1.set_f(evaluator->sub_solutions[i].second[j]);
406         ss_c.emplace_back(itmp1);
407       }
408       subsol_coord->emplace_back(std::move(ss_c));
409     }
410   }
411   if (findauxsols)
412   {
413     auxsol_coord->clear();
414     auxsol_dist->clear();
415     // iterators over all solutions
416     auto it = evaluator->begin(), itend = evaluator->end();
417     // skip shortest solution
418     if (!merge_sol_in_aux)
419       ++it;
420     for (; it != itend; ++it)
421     {
422       auxsol_dist->push_back(it->first.get_d());
423 
424       vector<Z_NR<mpz_t>> as_c;
425       for (size_t j = 0; j < it->second.size(); ++j)
426       {
427         itmp1.set_f(it->second[j]);
428         as_c.emplace_back(itmp1);
429       }
430       auxsol_coord->emplace_back(std::move(as_c));
431     }
432   }
433 
434   delete evaluator;
435   FP_NR<mpfr_t>::set_prec(old_prec);
436   return result;
437 }
438 
shortest_vector(MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,vector<Z_NR<mpz_t>> & sol_coord,SVPMethod method,int flags)439 int shortest_vector(MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso,
440                     vector<Z_NR<mpz_t>> &sol_coord, SVPMethod method, int flags)
441 {
442   long long tmp;
443   return shortest_vector_ex(gso, sol_coord, method, vector<double>(), flags, EVALMODE_SV, tmp);
444 }
445 
shortest_vectors(MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,vector<vector<Z_NR<mpz_t>>> & sol_coord,vector<enumf> & sol_dist,const int max_sols,SVPMethod method,int flags)446 int shortest_vectors(MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso,
447                      vector<vector<Z_NR<mpz_t>>> &sol_coord, vector<enumf> &sol_dist,
448                      const int max_sols, SVPMethod method, int flags)
449 {
450   long long tmp;
451   vector<Z_NR<mpz_t>> sol_coord_tmp;
452   return shortest_vector_ex(gso, sol_coord_tmp, method, vector<double>(), flags, EVALMODE_SV, tmp,
453                             nullptr, nullptr, &sol_coord, &sol_dist, max_sols - 1, true);
454 }
455 
shortest_vector_pruning(MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,vector<Z_NR<mpz_t>> & sol_coord,const vector<double> & pruning,int flags)456 int shortest_vector_pruning(MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso,
457                             vector<Z_NR<mpz_t>> &sol_coord, const vector<double> &pruning,
458                             int flags)
459 {
460   long long tmp;
461   return shortest_vector_ex(gso, sol_coord, SVPM_FAST, pruning, flags, EVALMODE_SV, tmp);
462 }
463 
shortest_vector_pruning(MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,vector<Z_NR<mpz_t>> & sol_coord,vector<vector<Z_NR<mpz_t>>> & subsol_coord,vector<enumf> & subsol_dist,const vector<double> & pruning,int flags)464 int shortest_vector_pruning(MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso,
465                             vector<Z_NR<mpz_t>> &sol_coord,
466                             vector<vector<Z_NR<mpz_t>>> &subsol_coord, vector<enumf> &subsol_dist,
467                             const vector<double> &pruning, int flags)
468 {
469   long long tmp;
470   return shortest_vector_ex(gso, sol_coord, SVPM_FAST, pruning, flags, EVALMODE_SV, tmp,
471                             &subsol_coord, &subsol_dist);
472 }
473 
shortest_vector_pruning(MatGSOInterface<Z_NR<mpz_t>,FP_NR<mpfr_t>> & gso,vector<Z_NR<mpz_t>> & sol_coord,vector<vector<Z_NR<mpz_t>>> & auxsol_coord,vector<enumf> & auxsol_dist,const int max_aux_sols,const vector<double> & pruning,int flags)474 int shortest_vector_pruning(MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso,
475                             vector<Z_NR<mpz_t>> &sol_coord,
476                             vector<vector<Z_NR<mpz_t>>> &auxsol_coord, vector<enumf> &auxsol_dist,
477                             const int max_aux_sols, const vector<double> &pruning, int flags)
478 {
479   long long tmp;
480   return shortest_vector_ex(gso, sol_coord, SVPM_FAST, pruning, flags, EVALMODE_SV, tmp, nullptr,
481                             nullptr, &auxsol_coord, &auxsol_dist, max_aux_sols);
482 }
483 
484 ///////////////////////////////////
485 //////END SVP FOR GSO OBJECT //////
486 ///////////////////////////////////
487 
488 /* Closest vector problem
489    ====================== */
490 
get_gscoords(const Matrix<FP_NR<mpfr_t>> & matrix,const Matrix<FP_NR<mpfr_t>> & mu,const Matrix<FP_NR<mpfr_t>> & r,const vector<FP_NR<mpfr_t>> & v,vector<FP_NR<mpfr_t>> & vcoord)491 static void get_gscoords(const Matrix<FP_NR<mpfr_t>> &matrix, const Matrix<FP_NR<mpfr_t>> &mu,
492                          const Matrix<FP_NR<mpfr_t>> &r, const vector<FP_NR<mpfr_t>> &v,
493                          vector<FP_NR<mpfr_t>> &vcoord)
494 {
495 
496   int n = matrix.get_rows(), m = matrix.get_cols();
497 
498   if (static_cast<int>(vcoord.size()) != n)
499     vcoord.resize(n);
500   FPLLL_DEBUG_CHECK(mu.get_rows() == n && mu.get_cols() == n && r.get_rows() == n &&
501                     r.get_cols() == n && static_cast<int>(v.size()) == m);
502 
503   for (int i = 0; i < n; i++)
504   {
505     vcoord[i] = 0.0;
506     for (int j = 0; j < m; j++)
507       vcoord[i].addmul(v[j], matrix(i, j));
508     for (int j = 0; j < i; j++)
509       vcoord[i].submul(mu(i, j), vcoord[j]);
510   }
511   for (int i = 0; i < n; i++)
512   {
513     vcoord[i].div(vcoord[i], r(i, i));
514   }
515 }
516 
babai(const FP_mat<mpfr_t> & matrix,const Matrix<FP_NR<mpfr_t>> & mu,const Matrix<FP_NR<mpfr_t>> & r,const vector<FP_NR<mpfr_t>> & target,vector<FP_NR<mpfr_t>> & target_coord)517 static void babai(const FP_mat<mpfr_t> &matrix, const Matrix<FP_NR<mpfr_t>> &mu,
518                   const Matrix<FP_NR<mpfr_t>> &r, const vector<FP_NR<mpfr_t>> &target,
519                   vector<FP_NR<mpfr_t>> &target_coord)
520 {
521 
522   int d = matrix.get_rows();
523   get_gscoords(matrix, mu, r, target, target_coord);
524   for (int i = d - 1; i >= 0; i--)
525   {
526     target_coord[i].rnd(target_coord[i]);
527     for (int j = 0; j < i; j++)
528       target_coord[j].submul(mu(i, j), target_coord[i]);
529   }
530 }
531 
closest_vector(ZZ_mat<mpz_t> & b,const vector<Z_NR<mpz_t>> & int_target,vector<Z_NR<mpz_t>> & sol_coord,int method,int flags)532 int closest_vector(ZZ_mat<mpz_t> &b, const vector<Z_NR<mpz_t>> &int_target,
533                    vector<Z_NR<mpz_t>> &sol_coord, int method, int flags)
534 {
535   // d = lattice dimension (note that it might decrease during preprocessing)
536   int d = b.get_rows();
537   // n = dimension of the space
538   int n = b.get_cols();
539 
540   FPLLL_CHECK(d > 0 && n > 0, "closestVector: empty matrix");
541   FPLLL_CHECK(d <= n, "closestVector: number of vectors > size of the vectors");
542 
543   // Sets the floating-point precision
544   // Error bounds on GSO are valid if prec >= minprec
545   double rho;
546   int min_prec = gso_min_prec(rho, d, LLL_DEF_DELTA, LLL_DEF_ETA);
547   int prec     = max(53, min_prec + 10);
548   int old_prec = FP_NR<mpfr_t>::set_prec(prec);
549 
550   // Allocates space for vectors and matrices in constructors
551   ZZ_mat<mpz_t> empty_mat;
552   MatGSO<Z_NR<mpz_t>, FP_NR<mpfr_t>> gso(b, empty_mat, empty_mat, GSO_INT_GRAM);
553   vector<FP_NR<mpfr_t>> target_coord;
554   FP_NR<mpfr_t> max_dist;
555   Z_NR<mpz_t> itmp1;
556 
557   // Computes the Gram-Schmidt orthogonalization in floating-point
558   gso.update_gso();
559   gen_zero_vect(sol_coord, d);
560 
561   /* Applies Babai's algorithm. Because we use fp, it might be necessary to
562       do it several times (if ||target|| >> ||b_i||) */
563   FP_mat<mpfr_t> float_matrix(d, n);
564   vector<FP_NR<mpfr_t>> target(n), babai_sol;
565   vector<Z_NR<mpz_t>> int_new_target = int_target;
566 
567   for (int i = 0; i < d; i++)
568     for (int j = 0; j < n; j++)
569       float_matrix(i, j).set_z(b(i, j));
570 
571   for (int loop_idx = 0;; loop_idx++)
572   {
573     if (loop_idx >= 0x100 && ((loop_idx & (loop_idx - 1)) == 0))
574       FPLLL_INFO("warning: possible infinite loop in Babai's algorithm");
575 
576     for (int i = 0; i < n; i++)
577     {
578       target[i].set_z(int_new_target[i]);
579     }
580     babai(float_matrix, gso.get_mu_matrix(), gso.get_r_matrix(), target, babai_sol);
581     int idx;
582     for (idx = 0; idx < d && babai_sol[idx] >= -1 && babai_sol[idx] <= 1; idx++)
583     {
584     }
585     if (idx == d)
586       break;
587 
588     for (int i = 0; i < d; i++)
589     {
590       itmp1.set_f(babai_sol[i]);
591       sol_coord[i].add(sol_coord[i], itmp1);
592       for (int j = 0; j < n; j++)
593         int_new_target[j].submul(itmp1, b(i, j));
594     }
595   }
596   // FPLLL_TRACE("BabaiSol=" << sol_coord);
597   get_gscoords(float_matrix, gso.get_mu_matrix(), gso.get_r_matrix(), target, target_coord);
598 
599   /* Computes a very large bound to make the algorithm work
600       until the first solution is found */
601   max_dist = 0.0;
602   for (int i = 1; i < d; i++)
603   {
604     // get_r_exp(i, i) = r(i, i) because gso is initialized without GSO_ROW_EXPO
605     max_dist.add(max_dist, gso.get_r_exp(i, i));
606   }
607 
608   vector<int> max_indices;
609   if (method & CVPM_PROVED)
610   {
611     // For Exact CVP, we need to reset enum below depth with maximal r_i
612     max_indices = vector<int>(d);
613     int cur, max_index, previous_max_index;
614     previous_max_index = max_index = d - 1;
615     FP_NR<mpfr_t> max_val;
616 
617     while (max_index > 0)
618     {
619       max_val = gso.get_r_exp(max_index, max_index);
620       for (cur = previous_max_index - 1; cur >= 0; --cur)
621       {
622         if (max_val <= gso.get_r_exp(cur, cur))
623         {
624           max_val   = gso.get_r_exp(cur, cur);
625           max_index = cur;
626         }
627       }
628       for (cur = max_index; cur < previous_max_index; ++cur)
629         max_indices[cur] = max_index;
630       max_indices[previous_max_index] = previous_max_index;
631       previous_max_index              = max_index;
632       --max_index;
633     }
634   }
635   FPLLL_TRACE("max_indices " << max_indices);
636 
637   FastErrorBoundedEvaluator evaluator(n, gso.get_mu_matrix(), gso.get_r_matrix(), EVALMODE_CV);
638 
639   // Main loop of the enumeration
640   Enumeration<Z_NR<mpz_t>, FP_NR<mpfr_t>> enumobj(gso, evaluator, max_indices);
641   enumobj.enumerate(0, d, max_dist, 0, target_coord);
642 
643   int result = RED_ENUM_FAILURE;
644   if (!evaluator.empty())
645   {
646     FPLLL_TRACE("evaluator.bestsol_coord=" << evaluator.begin()->second);
647     if (flags & CVP_VERBOSE)
648       FPLLL_INFO("max_dist=" << max_dist);
649     for (int i = 0; i < d; i++)
650     {
651       itmp1.set_f(evaluator.begin()->second[i]);
652       sol_coord[i].add(sol_coord[i], itmp1);
653     }
654     result = RED_SUCCESS;
655   }
656 
657   FP_NR<mpfr_t>::set_prec(old_prec);
658   return result;
659 }
660 
661 FPLLL_END_NAMESPACE
662