1 /*
2  * Authors: Joshua Peignier and Emmanuel Thomé
3  */
4 #include "cado.h" // IWYU pragma: keep
5 // IWYU pragma: no_include <memory>
6 // iwyu wants it for allocator_traits<>::value_type, which seems weird
7 #include <ostream>
8 #include <iomanip>      // std::dec // IWYU pragma: keep
9 #include <sstream>
10 #include <utility>
11 
12 #include <cstddef> // size_t
13 #include <gmp.h>
14 
15 #include "gmp_aux.h"  // mpz_p_valuation
16 #include "mpz_mat.h"
17 #include "numbertheory.hpp"
18 #include "badideals.hpp"
19 #include "rootfinder.h"
20 #include "getprime.h"  // for getprime_mt, prime_info_clear, prime_info_init
21 #include "macros.h" // ASSERT_ALWAYS // IWYU pragma: keep
22 
23 using namespace std;
24 
25 
print_dot_badideals_file(std::ostream & o,int side) const26     std::ostream& badideal::print_dot_badideals_file(std::ostream & o, int side) const {/*{{{*/
27         o << p
28           << "," << r
29           << ":" << side
30           << ": " << nbad << std::endl;
31         return o;
32     }/*}}}*/
print_dot_badidealinfo_file(std::ostream & o,int side) const33     std::ostream& badideal::print_dot_badidealinfo_file(std::ostream& o, int side) const {/*{{{*/
34         o << comments;
35         for(unsigned int j = 0 ; j < branches.size() ; j++) {
36             badideal::branch const& br(branches[j]);
37             o << p << " " << br.k << " " << br.r << " " << side;
38             for(unsigned int k = 0 ; k < br.v.size() ; k++) {
39                 o << " " << br.v[k];
40             }
41             o << std::endl;
42         }
43         return o;
44     }/*}}}*/
45 
operator <<(std::ostream & os) const46 std::ostream& badideal::operator<<(std::ostream& os) const
47 {
48     std::ios_base::fmtflags ff = os.flags();
49     os << comments;
50     // p and r are printed according to the current format flags, but the
51     // number of ideals and the branches are always in decimal.
52     os << p
53         << " " << r
54         << dec
55         << "  " << nbad
56         << "  " << branches.size()
57         << std::endl;
58     for(unsigned int j = 0 ; j < branches.size() ; j++) {
59         badideal::branch const& br(branches[j]);
60         os << p << " " << br.k << " " << br.r;
61         ASSERT_ALWAYS(br.v.size() == (size_t) nbad);
62         for(unsigned int k = 0 ; k < br.v.size() ; k++) {
63             os << " " << br.v[k];
64         }
65         os << std::endl;
66     }
67     os.flags(ff);
68     return os;
69 }
70 
operator >>(std::istream & is)71 std::istream& badideal::operator>>(std::istream& is)
72 {
73     badideal c(is);
74     std::swap(*this, c);
75     return is;
76 }
77 
badideal(std::istream & is)78 badideal::badideal(std::istream& is)
79 {
80     for(std::string s; std::ws(is).peek() == '#' ; getline(is, s) ) ;
81     size_t nbranches;
82     // coverity[tainted_argument]
83     is >> p >> r >> nbad >> nbranches;
84     if (!is) return;
85     for(unsigned int j = 0 ; j < nbranches ; j++) {
86         badideal::branch br;
87         is >> p >> br.k >> br.r;
88         ASSERT_ALWAYS_OR_THROW(is, std::invalid_argument);
89         br.v.assign(nbad, 0);
90         for(unsigned int k = 0 ; k < br.v.size() ; k++) {
91             is >> br.v[k];
92             ASSERT_ALWAYS_OR_THROW(is, std::invalid_argument);
93         }
94         branches.emplace_back(std::move(br));
95     }
96     return;
97 }
98 
trial_division(cxx_mpz const & n0,unsigned long B,cxx_mpz & cofactor)99 vector<pair<cxx_mpz, int> > trial_division(cxx_mpz const& n0, unsigned long B, cxx_mpz & cofactor)/*{{{*/
100 {
101     vector<pair<cxx_mpz, int> > res;
102     prime_info pinf;
103 
104     prime_info_init (pinf);
105     cxx_mpz n = n0;
106 
107     for (unsigned long p = 2; p < B; p = getprime_mt (pinf)) {
108         if (!mpz_divisible_ui_p(n, p)) continue;
109         int k = 0;
110         for( ; mpz_divisible_ui_p(n, p) ; mpz_fdiv_q_ui(n, n, p), k++);
111         res.push_back(make_pair(cxx_mpz(p), k));
112     }
113     // cout << "remaining nriminant " << n << "\n";
114     cofactor = n;
115     prime_info_clear (pinf); /* free the tables */
116     return res;
117 }
118 /*}}}*/
119 
120 struct all_valuations_above_p {/*{{{*/
121     cxx_mpz_poly f;
122     cxx_mpz p;
123 private:
124     cxx_mpq_mat O;
125     cxx_mpz_mat M;
126     vector<pair<cxx_mpz_mat, int> > F;
127     vector<int> inertia;
128     pair<cxx_mpz_mat, cxx_mpz> jjinv;
129     vector<cxx_mpz_mat> helpers;
130     vector<int> val_base;
131 
132 public:
all_valuations_above_pall_valuations_above_p133     all_valuations_above_p(cxx_mpz_poly const& f, cxx_mpz const& p, gmp_randstate_t state) : f(f), p(p) {/*{{{*/
134         O = p_maximal_order(f, p);
135         M = multiplication_table_of_order(O, f);
136         F = factorization_of_prime(O, f, p, state);
137         for(unsigned int k = 0 ; k < F.size() ; k++) {
138             cxx_mpz_mat const& fkp(F[k].first);
139             inertia.push_back(prime_ideal_inertia_degree(fkp));
140             helpers.push_back(valuation_helper_for_ideal(M, fkp, p));
141         }
142         cxx_mpq_mat jjinv_gen(2, f->deg);
143         mpq_set_ui(mpq_mat_entry(jjinv_gen,0,0),1,1);
144         mpq_set_ui(mpq_mat_entry(jjinv_gen,1,1),1,1);
145         jjinv = ::generate_ideal(O,M,jjinv_gen);
146 
147         val_base.assign(f->deg, 0);
148         val_base = (*this)(jjinv);
149     }/*}}}*/
operator ()all_valuations_above_p150     vector<int> operator()(pair<cxx_mpz_mat, cxx_mpz> const& Id) const {/*{{{*/
151         int w = mpz_p_valuation(Id.second, p);
152         vector<int> res;
153         for(unsigned int k = 0 ; k < F.size() ; k++) {
154             cxx_mpz_mat const& a(helpers[k]);
155             int v = valuation_of_ideal_at_prime_ideal(M, Id.first, a, p);
156             int e = F[k].second;
157             res.push_back(v - w * e - val_base[k]);
158         }
159         return res;
160     }/*}}}*/
print_infoall_valuations_above_p161     void print_info(ostream& o, int k, cxx_mpz const& r, int side) const {/*{{{*/
162         cxx_mpz_mat const& fkp(F[k].first);
163         pair<cxx_mpz, cxx_mpz_mat> two = prime_ideal_two_element(O, f, M, fkp);
164         /* Write the uniformizer as a polynomial with respect to the
165          * polynomial basis defined by f */
166         cxx_mpq_mat theta_q;
167         {
168             mpq_mat_set_mpz_mat(theta_q, two.second);
169             mpq_mat_mul(theta_q, theta_q, O);
170         }
171 
172         /* That's only for debugging, so it's not terribly important.
173          * But we may have a preference towards giving ideal info in
174          * a more concise way, based on alpha_hat for instance */
175         ostringstream alpha;
176         alpha << "alpha" << side;
177         string uniformizer = write_element_as_polynomial(theta_q, alpha.str());
178 
179         int e = F[k].second;
180         o << "# I" << k
181             << ":=ideal<O" << side << "|" << two.first << "," << uniformizer << ">;"
182             << " // f=" << prime_ideal_inertia_degree(fkp)
183             << " e="<< e
184             << endl;
185         o << "# I_" << two.first << "_" << r << "_" << side << "_" << k
186             << " " << two.first
187             << " " << r
188             << " " << side
189             << " " << theta_q
190             << " // " << prime_ideal_inertia_degree(fkp)
191             << " " << e
192             << endl;
193     }/*}}}*/
generate_idealall_valuations_above_p194     pair<cxx_mpz_mat, cxx_mpz> generate_ideal(cxx_mpq_mat const& gens) const {/*{{{*/
195         return ::generate_ideal(O, M, gens);
196     }/*}}}*/
generate_idealall_valuations_above_p197     pair<cxx_mpz_mat, cxx_mpz> generate_ideal(cxx_mpz_mat const& gens) const {/*{{{*/
198         return ::generate_ideal(O, M, cxx_mpq_mat(gens));
199     }/*}}}*/
200     /* create ideal I=<p^k,p^k*alpha,v*alpha-u> and decompose I*J */
operator ()all_valuations_above_p201     vector<int> operator()(int k, cxx_mpz const& r) const {/*{{{*/
202         cxx_mpz pk;
203         mpz_pow_ui(pk, p, k);
204         cxx_mpz_mat Igens(3, f->deg);
205         mpz_set(mpz_mat_entry(Igens,0,0),pk);
206         if (mpz_cmp(r, pk) < 0) {
207             mpz_neg(mpz_mat_entry(Igens,1,0),r);
208             mpz_set_ui(mpz_mat_entry(Igens,1,1),1);
209         } else {
210             mpz_set_si(mpz_mat_entry(Igens,1,0),-1);
211             mpz_sub(mpz_mat_entry(Igens,1,1), r, pk);
212         }
213         /* hell, do I _really_ need p*alpha here ??? */
214         mpz_set(mpz_mat_entry(Igens,2,1),pk);
215         pair<cxx_mpz_mat, cxx_mpz> I = generate_ideal(Igens);
216         return (*this)(I);
217     }/*}}}*/
multiply_inertiaall_valuations_above_p218     vector<int> multiply_inertia(vector<int> const& v) const {/*{{{*/
219         ASSERT_ALWAYS(v.size() == inertia.size());
220         vector<int> res(v.size(),0);
221         for(unsigned int i = 0 ; i < v.size() ; i++) {
222             res[i] = v[i] * inertia[i];
223         }
224         return res;
225     }/*}}}*/
226 };/*}}}*/
227 
lift_p1_elements(cxx_mpz const & p,int k,cxx_mpz const & x)228 vector<cxx_mpz> lift_p1_elements(cxx_mpz const& p, int k, cxx_mpz const& x)/*{{{*/
229 {
230     /* Given x which represents an element of P^1(Z/p^kZ), return all the p
231      * lifts of x in P^1(Z/p^(k+1)Z), all following the same representation
232      * convention (detailed somewhat above)
233      */
234     /* Note how we have complexity O(p) here ! */
235     ASSERT_ALWAYS(k >= 1);
236     vector<cxx_mpz> res;
237     cxx_mpz pk, pkp1;
238     mpz_pow_ui(pk, p, k);
239     mpz_mul(pkp1, pk, p);
240     cxx_mpz xx = x;
241     if (mpz_cmp(xx, pk) >= 0) {
242         mpz_sub(xx, xx, pk);
243         mpz_add(xx, xx, pkp1);
244     }
245     for(unsigned int w = 0 ; mpz_cmp_ui(p, w) > 0 ; w++) {
246         res.push_back(xx);
247         mpz_add(xx, xx, pk);
248     }
249     return res;
250 }/*}}}*/
251 
lift_root(all_valuations_above_p const & A,int k0,cxx_mpz const & Q,vector<int> v)252 vector<badideal::branch> lift_root(all_valuations_above_p const& A, int k0, cxx_mpz const& Q, vector<int> v)/*{{{*/
253 {
254     vector<badideal::branch> dead_branches_reports;
255     vector<pair<cxx_mpz, vector<int> > > live_branches;
256     vector<int> live_ideals;
257     cxx_mpz const& p(A.p);
258 
259     //int k = k0 + 1;
260 
261     vector<cxx_mpz> rootlifts = lift_p1_elements(p, k0, Q);
262 
263     for(unsigned int i = 0 ; i < rootlifts.size() ; i++) {
264         cxx_mpz const& nQ(rootlifts[i]);
265         vector<int> newvals = A(k0 + 1, nQ);
266         vector<int> alive;
267         /* All valuations are expected to be >= the base valuation. For
268          * some, it's going to lift higher. Find which ones.  */
269         for(unsigned int j = 0 ; j < v.size() ; j++) {
270             if (newvals[j] != v[j]) alive.push_back(j);
271         }
272         if (alive.empty()) {
273             badideal::branch br;
274             br.k = k0 + 1;
275             br.r = nQ;
276             br.v = A.multiply_inertia(v);
277             dead_branches_reports.push_back(br);
278         } else {
279             live_branches.push_back(make_pair(nQ, newvals));
280         }
281         live_ideals.insert(live_ideals.end(), alive.begin(), alive.end());
282     }
283     if (live_ideals.size() <= 1) {
284         /* Then the decision is basically taken */
285         vector<int> vv = A.multiply_inertia(v);
286         int sumvv = 0;
287         for(unsigned int j = 0 ; j < vv.size() ; sumvv+=vv[j++]);
288         for(unsigned int j = 0 ; j < live_ideals.size() ; j++) {
289             int jj = live_ideals[j];
290             vv[jj] = vv[jj] - sumvv;
291         }
292         badideal::branch br;
293         br.k = k0;
294         br.r = Q;
295         br.v = vv;
296         return vector<badideal::branch>(1, br);
297     }
298 
299     vector<badideal::branch> res = dead_branches_reports;
300 
301     for(unsigned int i = 0 ; i < live_branches.size() ; i++) {
302         cxx_mpz const& nQ(live_branches[i].first);
303         vector<int> const& nv(live_branches[i].second);
304 
305         vector<badideal::branch> add = lift_root(A, k0 + 1, nQ, nv);
306 
307         res.insert(res.end(), add.begin(), add.end());
308     }
309     return res;
310 }/*}}}*/
311 
projective_roots_modp(cxx_mpz_poly const & f,cxx_mpz const & p,gmp_randstate_ptr rstate)312 vector<cxx_mpz> projective_roots_modp(cxx_mpz_poly const& f, cxx_mpz const& p, gmp_randstate_ptr rstate)/*{{{*/
313 {
314     /* p must be prime */
315     vector<cxx_mpz> roots;
316     mpz_t * rr = new mpz_t[f->deg];
317     for(int i = 0 ; i < f->deg ; i++) mpz_init(rr[i]);
318 
319     int d = mpz_poly_roots(rr, f, p, rstate);
320     for(int i = 0 ; i < d ; i++) {
321         cxx_mpz a;
322         mpz_set(a, rr[i]);
323         roots.push_back(a);
324     }
325     if (mpz_divisible_p(f->coeff[f->deg], p)) {
326         roots.push_back(p);
327     }
328     for(int i = 0 ; i < f->deg ; i++) mpz_clear(rr[i]);
329     delete[] rr;
330     return roots;
331 }/*}}}*/
332 
badideals_above_p(cxx_mpz_poly const & f,int side,cxx_mpz const & p,gmp_randstate_t state)333 vector<badideal> badideals_above_p(cxx_mpz_poly const& f, int side, cxx_mpz const& p, gmp_randstate_t state)/*{{{*/
334 {
335     vector<badideal> badideals;
336 
337     all_valuations_above_p A(f, p, state);
338 
339     vector<cxx_mpz> roots = projective_roots_modp(f, p, state);
340 
341     for(unsigned int i = 0 ; i < roots.size() ; i++) {
342         /* first try to decompose <p,(v*alpha-u)>*J */
343         vector<int> vals = A(1, roots[i]);
344 
345         vector<int> nonzero;
346         for(unsigned int k = 0 ; k < vals.size() ; k++) {
347             if (vals[k]) nonzero.push_back(k);
348         }
349         if (nonzero.size() == 1)
350             continue;
351 
352         badideal b(p,roots[i], nonzero.size());
353 
354         vector<badideal::branch> lifts = lift_root(A, 1, roots[i], vals);
355 
356         ostringstream cmt;
357         cmt << "# p=" << p << ", r=" << roots[i] << " : " << nonzero.size() << " ideals among " << vals.size() << " are bad\n";
358         for(unsigned int j = 0 ; j < nonzero.size() ; j++) {
359             A.print_info(cmt, nonzero[j], roots[i], side);
360         }
361         cmt << "# " << lifts.size() << " branch"
362             << (lifts.size() == 1 ? "" : "es") << " found\n";
363         b.comments = cmt.str();
364 
365         /* compres all branches so that we keep only the valuations in
366          * the nonzero indirection table */
367         for(unsigned int j = 0 ; j < lifts.size() ; j++) {
368             badideal::branch & br(lifts[j]);
369             vector<int> w;
370             for(unsigned int k = 0 ; k < nonzero.size() ; k++) {
371                 w.push_back(br.v[nonzero[k]]);
372             }
373             swap(br.v, w);
374             b.branches.push_back(br);
375         }
376         badideals.push_back(b);
377     }
378     return badideals;
379 }/*}}}*/
380 
badideals_for_polynomial(cxx_mpz_poly const & f,int side,gmp_randstate_t state)381 vector<badideal> badideals_for_polynomial(cxx_mpz_poly const& f, int side, gmp_randstate_t state)/*{{{*/
382 {
383     vector<badideal> badideals;
384 
385     if (f->deg == 1) return badideals;
386 
387     ASSERT_ALWAYS(f->deg > 1);
388 
389     cxx_mpz disc;
390     mpz_poly_discriminant(disc, f);
391     mpz_mul(disc, disc, f->coeff[f->deg]);
392 
393     /* We're not urged to use ecm here */
394     vector<pair<cxx_mpz,int> > small_primes = trial_division(disc, 10000000, disc);
395 
396     typedef vector<pair<cxx_mpz,int> >::const_iterator vzci_t;
397 
398     for(vzci_t it = small_primes.begin() ; it != small_primes.end() ; it++) {
399         vector<badideal> tmp = badideals_above_p(f, side, it->first, state);
400         badideals.insert(badideals.end(), tmp.begin(), tmp.end());
401     }
402 
403     return badideals;
404 }/*}}}*/
405 
badideals_for_polynomial(cxx_mpz_poly const & f,int side)406 vector<badideal> badideals_for_polynomial(cxx_mpz_poly const& f, int side)
407 {
408     gmp_randstate_t state;
409     gmp_randinit_default(state);
410     auto x = badideals_for_polynomial(f, side, state);
411     gmp_randclear(state);
412     return x;
413 }
414 
badideals_above_p(cxx_mpz_poly const & f,int side,cxx_mpz const & p)415 vector<badideal> badideals_above_p(cxx_mpz_poly const& f, int side, cxx_mpz const & p)
416 {
417     gmp_randstate_t state;
418     gmp_randinit_default(state);
419     auto x = badideals_above_p(f, side, p, state);
420     gmp_randclear(state);
421     return x;
422 }
423 
r_from_rk(cxx_mpz const & p,int k,cxx_mpz const & rk)424 cxx_mpz badideal::r_from_rk(cxx_mpz const & p, int k, cxx_mpz const & rk)
425 {
426     cxx_mpz pk, r;
427     mpz_pow_ui(pk, p, k);
428     if (mpz_cmp(rk, pk) < 0) {
429         mpz_mod(r, rk, p);
430         return r;
431     } else {
432         /* then u=rk-p^k is divisible by p, and represents (1:u). Reduced
433          * mod p, this means (1:0), which is encoded by p */
434         return p;
435     }
436 }
437 
438