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