Line data Source code
1 : /* Copyright (C) 2000 The PARI group.
2 :
3 : This file is part of the PARI/GP package.
4 :
5 : PARI/GP is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 2 of the License, or (at your option) any later
8 : version. It is distributed in the hope that it will be useful, but WITHOUT
9 : ANY WARRANTY WHATSOEVER.
10 :
11 : Check the License for details. You should have received a copy of it, along
12 : with the package; see the file 'COPYING'. If not, write to the Free Software
13 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
14 : #include "pari.h"
15 : #include "paripriv.h"
16 :
17 : #define DEBUGLEVEL DEBUGLEVEL_alg
18 :
19 : #define dbg_printf(lvl) if (DEBUGLEVEL >= (lvl) + 3) err_printf
20 :
21 : /********************************************************************/
22 : /** **/
23 : /** ASSOCIATIVE ALGEBRAS, CENTRAL SIMPLE ALGEBRAS **/
24 : /** contributed by Aurel Page (2014) **/
25 : /** **/
26 : /********************************************************************/
27 : static GEN alg_subalg(GEN al, GEN basis);
28 : static GEN alg_maximal_primes(GEN al, GEN P);
29 : static GEN algnatmultable(GEN al, long D);
30 : static GEN _tablemul_ej(GEN mt, GEN x, long j);
31 : static GEN _tablemul_ej_Fp(GEN mt, GEN x, long j, GEN p);
32 : static GEN _tablemul_ej_Fl(GEN mt, GEN x, long j, ulong p);
33 : static ulong algtracei(GEN mt, ulong p, ulong expo, ulong modu);
34 : static GEN alg_pmaximal(GEN al, GEN p);
35 : static GEN alg_maximal(GEN al);
36 : static GEN algtracematrix(GEN al);
37 : static GEN algtableinit_i(GEN mt0, GEN p);
38 : static GEN algbasisrightmultable(GEN al, GEN x);
39 : static GEN algbasismul(GEN al, GEN x, GEN y);
40 : static GEN algbasismultable(GEN al, GEN x);
41 : static GEN algbasismultable_Flm(GEN mt, GEN x, ulong m);
42 : static GEN algeltfromnf_i(GEN al, GEN x);
43 : static void computesplitting(GEN al, long d, long v, long flag);
44 : static GEN alg_change_overorder_shallow(GEN al, GEN ord);
45 :
46 : static GEN H_inv(GEN x);
47 : static GEN H_norm(GEN x, long abs);
48 : static GEN H_trace(GEN x, long abs);
49 :
50 : static int
51 1414323 : checkalg_i(GEN al)
52 : {
53 : GEN mt, rnf;
54 : long t;
55 1414323 : if (typ(al) != t_VEC || lg(al) != 12) return 0;
56 1413994 : mt = alg_get_multable(al);
57 1413994 : if (typ(mt) != t_VEC || lg(mt) == 1 || typ(gel(mt,1)) != t_MAT) return 0;
58 1413973 : rnf = alg_get_splittingfield(al);
59 1413973 : if (isintzero(rnf) || !gequal0(alg_get_char(al)))
60 545901 : return 1;
61 868072 : if (typ(gel(al,2)) != t_VEC || lg(gel(al,2)) == 1) return 0;
62 : /* not checkrnf_i: beware placeholder from alg_csa_table */
63 868065 : t = typ(rnf);
64 868065 : return t==t_COMPLEX || t==t_REAL || (t==t_VEC && lg(rnf)==13);
65 : }
66 : void
67 1541667 : checkalg(GEN al)
68 : {
69 1541667 : if (al && !checkalg_i(al))
70 203 : pari_err_TYPE("checkalg [please apply alginit()]",al);
71 1541464 : }
72 :
73 : static int
74 186858 : checklat_i(GEN al, GEN lat)
75 : {
76 : long N,i,j;
77 : GEN m,t,c;
78 186858 : if (typ(lat)!=t_VEC || lg(lat) != 3) return 0;
79 186858 : t = gel(lat,2);
80 186858 : if (typ(t) != t_INT && typ(t) != t_FRAC) return 0;
81 186858 : if (gsigne(t)<=0) return 0;
82 186858 : m = gel(lat,1);
83 186858 : if (typ(m) != t_MAT) return 0;
84 186858 : N = alg_get_absdim(al);
85 186858 : if (lg(m)-1 != N || lg(gel(m,1))-1 != N) return 0;
86 1686020 : for (i=1; i<=N; i++)
87 14154399 : for (j=1; j<=N; j++) {
88 12655237 : c = gcoeff(m,i,j);
89 12655237 : if (typ(c) != t_INT) return 0;
90 12655237 : if (j<i && signe(gcoeff(m,i,j))) return 0;
91 12655237 : if (i==j && !signe(gcoeff(m,i,j))) return 0;
92 : }
93 186851 : return 1;
94 : }
95 186858 : void checklat(GEN al, GEN lat)
96 186858 : { if (!checklat_i(al,lat)) pari_err_TYPE("checklat [please apply alglathnf()]", lat); }
97 :
98 : /** ACCESSORS **/
99 : long
100 10022814 : alg_type(GEN al)
101 : {
102 : long t;
103 10022814 : if (!al) return al_REAL;
104 9892621 : t = typ(alg_get_splittingfield(al));
105 9892621 : if (t==t_REAL || t==t_COMPLEX) return al_REAL;
106 9889100 : if (isintzero(alg_get_splittingfield(al)) || !gequal0(alg_get_char(al))) return al_TABLE;
107 7713481 : switch(typ(gmael(al,2,1))) {
108 1690759 : case t_MAT: return al_CSA;
109 6022687 : case t_INT:
110 : case t_FRAC:
111 : case t_POL:
112 6022687 : case t_POLMOD: return al_CYCLIC;
113 35 : default: return al_NULL;
114 : }
115 : return -1; /*LCOV_EXCL_LINE*/
116 : }
117 : long
118 2254 : algtype(GEN al)
119 2254 : { return checkalg_i(al)? alg_type(al): al_NULL; }
120 :
121 : static long /* is a square special case? */
122 1183 : alg_is_asq(GEN al) { return typ(gmael(al,6,1)) == t_VEC; }
123 :
124 : /* absdim == dim for al_TABLE. */
125 : static long
126 259 : algreal_dim(GEN al)
127 : {
128 259 : switch(lg(alg_get_multable(al))) {
129 161 : case 2: case 3: return 1;
130 91 : case 5: return 4;
131 7 : default: pari_err_TYPE("algreal_dim", al);
132 : }
133 : return -1; /*LCOV_EXCL_LINE*/
134 : }
135 : long
136 410688 : alg_get_dim(GEN al)
137 : {
138 : long d;
139 410688 : if (!al) return 4;
140 410688 : switch(alg_type(al)) {
141 19577 : case al_TABLE: return lg(alg_get_multable(al))-1;
142 390999 : case al_CSA: return lg(alg_get_relmultable(al))-1;
143 77 : case al_CYCLIC: d = alg_get_degree(al); return d*d;
144 28 : case al_REAL: return algreal_dim(al);
145 7 : default: pari_err_TYPE("alg_get_dim", al);
146 : }
147 : return -1; /*LCOV_EXCL_LINE*/
148 : }
149 :
150 : long
151 2566295 : alg_get_absdim(GEN al)
152 : {
153 2566295 : if (!al) return 4;
154 2519577 : switch(alg_type(al)) {
155 927115 : case al_TABLE: case al_REAL: return lg(alg_get_multable(al))-1;
156 198856 : case al_CSA: return alg_get_dim(al)*nf_get_degree(alg_get_center(al));
157 1393599 : case al_CYCLIC:
158 1393599 : return rnf_get_absdegree(alg_get_splittingfield(al))*alg_get_degree(al);
159 7 : default: pari_err_TYPE("alg_get_absdim", al);
160 : }
161 : return -1;/*LCOV_EXCL_LINE*/
162 : }
163 :
164 : long
165 6216 : algdim(GEN al, long abs)
166 : {
167 6216 : checkalg(al);
168 6195 : if (abs) return alg_get_absdim(al);
169 5551 : return alg_get_dim(al);
170 : }
171 :
172 : /* only cyclic */
173 : GEN
174 20937 : alg_get_auts(GEN al)
175 : {
176 20937 : long ta = alg_type(al);
177 20937 : if (ta != al_CYCLIC && ta != al_REAL)
178 0 : pari_err_TYPE("alg_get_auts [noncyclic algebra]", al);
179 20937 : return gel(al,2);
180 : }
181 : GEN
182 1169 : alg_get_aut(GEN al)
183 : {
184 1169 : long ta = alg_type(al);
185 1169 : if (ta != al_CYCLIC && ta != al_REAL)
186 28 : pari_err_TYPE("alg_get_aut [noncyclic algebra]", al);
187 1141 : return gel(alg_get_auts(al),1);
188 : }
189 : GEN
190 63 : algaut(GEN al) { checkalg(al); return alg_get_aut(al); }
191 : GEN
192 19992 : alg_get_b(GEN al)
193 : {
194 19992 : long ta = alg_type(al);
195 19992 : if (ta != al_CYCLIC && ta != al_REAL)
196 28 : pari_err_TYPE("alg_get_b [noncyclic algebra]", al);
197 19964 : return gel(al,3);
198 : }
199 : GEN
200 91 : algb(GEN al) { checkalg(al); return alg_get_b(al); }
201 :
202 : /* only CSA */
203 : GEN
204 393925 : alg_get_relmultable(GEN al)
205 : {
206 393925 : if (alg_type(al) != al_CSA)
207 14 : pari_err_TYPE("alg_get_relmultable [algebra not given via mult. table]", al);
208 393911 : return gel(al,2);
209 : }
210 : GEN
211 70 : algrelmultable(GEN al) { checkalg(al); return alg_get_relmultable(al); }
212 : GEN
213 84 : alg_get_splittingdata(GEN al)
214 : {
215 84 : if (alg_type(al) != al_CSA)
216 14 : pari_err_TYPE("alg_get_splittingdata [algebra not given via mult. table]",al);
217 70 : return gel(al,3);
218 : }
219 : GEN
220 84 : algsplittingdata(GEN al) { checkalg(al); return alg_get_splittingdata(al); }
221 : GEN
222 4347 : alg_get_splittingbasis(GEN al)
223 : {
224 4347 : if (alg_type(al) != al_CSA)
225 0 : pari_err_TYPE("alg_get_splittingbasis [algebra not given via mult. table]",al);
226 4347 : return gmael(al,3,2);
227 : }
228 : GEN
229 4347 : alg_get_splittingbasisinv(GEN al)
230 : {
231 4347 : if (alg_type(al) != al_CSA)
232 0 : pari_err_TYPE("alg_get_splittingbasisinv [algebra not given via mult. table]",al);
233 4347 : return gmael(al,3,3);
234 : }
235 :
236 : /* only cyclic and CSA */
237 : GEN
238 25865324 : alg_get_splittingfield(GEN al) { return gel(al,1); }
239 : GEN
240 161 : algsplittingfield(GEN al)
241 : {
242 : long ta;
243 161 : checkalg(al);
244 161 : ta = alg_type(al);
245 161 : if (ta != al_CYCLIC && ta != al_CSA && ta != al_REAL)
246 7 : pari_err_TYPE("alg_get_splittingfield [use alginit]",al);
247 154 : return alg_get_splittingfield(al);
248 : }
249 : long
250 2498604 : alg_get_degree(GEN al)
251 : {
252 : long ta;
253 2498604 : ta = alg_type(al);
254 2498604 : if (ta == al_REAL) return algreal_dim(al)==1? 1 : 2;
255 2498520 : if (ta != al_CYCLIC && ta != al_CSA)
256 21 : pari_err_TYPE("alg_get_degree [use alginit]",al);
257 2498499 : return rnf_get_degree(alg_get_splittingfield(al));
258 : }
259 : long
260 679 : algdegree(GEN al)
261 : {
262 679 : checkalg(al);
263 672 : return alg_get_degree(al);
264 : }
265 :
266 : GEN
267 543784 : alg_get_center(GEN al)
268 : {
269 : long ta;
270 543784 : ta = alg_type(al);
271 543784 : if (ta == al_REAL)
272 : {
273 28 : if (algreal_dim(al) != 4) return alg_get_splittingfield(al);
274 14 : return stor(1, LOWDEFAULTPREC);
275 : }
276 543756 : if (ta != al_CSA && ta != al_CYCLIC)
277 14 : pari_err_TYPE("alg_get_center [use alginit]",al);
278 543742 : return rnf_get_nf(alg_get_splittingfield(al));
279 : }
280 : GEN
281 315 : alg_get_splitpol(GEN al)
282 : {
283 315 : long ta = alg_type(al);
284 315 : if (ta != al_CYCLIC && ta != al_CSA)
285 0 : pari_err_TYPE("alg_get_splitpol [use alginit]",al);
286 315 : return rnf_get_pol(alg_get_splittingfield(al));
287 : }
288 : GEN
289 103644 : alg_get_abssplitting(GEN al)
290 : {
291 103644 : long ta = alg_type(al), prec;
292 103644 : if (ta != al_CYCLIC && ta != al_CSA)
293 0 : pari_err_TYPE("alg_get_abssplitting [use alginit]",al);
294 103644 : prec = nf_get_prec(alg_get_center(al));
295 103644 : return rnf_build_nfabs(alg_get_splittingfield(al), prec);
296 : }
297 : GEN
298 2198 : alg_get_hasse_i(GEN al)
299 : {
300 2198 : long ta = alg_type(al);
301 2198 : if (ta != al_CYCLIC && ta != al_CSA && ta != al_REAL)
302 7 : pari_err_TYPE("alg_get_hasse_i [use alginit]",al);
303 2191 : if (ta == al_CSA && !alg_is_asq(al))
304 21 : pari_err_IMPL("computation of Hasse invariants over table CSA");
305 2170 : return gel(al,4);
306 : }
307 : GEN
308 266 : alghassei(GEN al) { checkalg(al); return alg_get_hasse_i(al); }
309 : GEN
310 4550 : alg_get_hasse_f(GEN al)
311 : {
312 4550 : long ta = alg_type(al);
313 : GEN hf;
314 4550 : if (ta != al_CYCLIC && ta != al_CSA)
315 7 : pari_err_TYPE("alg_get_hasse_f [use alginit]",al);
316 4543 : if (ta == al_CSA && !alg_is_asq(al))
317 7 : pari_err_IMPL("computation of Hasse invariants over table CSA");
318 4536 : hf = gel(al,5);
319 4536 : if (typ(hf) == t_INT) /* could be computed on the fly */
320 35 : pari_err(e_MISC, "Hasse invariants were not computed for this algebra");
321 4501 : return hf;
322 : }
323 : GEN
324 364 : alghassef(GEN al) { checkalg(al); return alg_get_hasse_f(al); }
325 :
326 : /* all types */
327 : GEN
328 3297 : alg_get_basis(GEN al) { return gel(al,7); }
329 : GEN
330 154 : algbasis(GEN al) { checkalg(al); return alg_get_basis(al); }
331 : GEN
332 87411 : alg_get_invbasis(GEN al) { return gel(al,8); }
333 : GEN
334 84 : alginvbasis(GEN al) { checkalg(al); return alg_get_invbasis(al); }
335 : GEN
336 3769935 : alg_get_multable(GEN al) { return gel(al,9); }
337 : GEN
338 308 : algmultable(GEN al) { checkalg(al); return alg_get_multable(al); }
339 : GEN
340 11670330 : alg_get_char(GEN al) { if (!al) return gen_0; return gel(al,10); }
341 : GEN
342 112 : algchar(GEN al) { checkalg(al); return alg_get_char(al); }
343 : GEN
344 652222 : alg_get_tracebasis(GEN al) { return gel(al,11); }
345 : GEN
346 3862 : alg_get_invol(GEN al) { return gmael(al,6,2); }
347 :
348 : /* lattices */
349 : GEN
350 251188 : alglat_get_primbasis(GEN lat) { return gel(lat,1); }
351 : GEN
352 296779 : alglat_get_scalar(GEN lat) { return gel(lat,2); }
353 :
354 : /* algmodpr */
355 : GEN
356 3948 : algmodpr_get_pr(GEN data) { return gel(data,1); }
357 : long
358 5005 : algmodpr_get_k(GEN data) { return gel(data,2)[1]; } /* target M_k(F_p^m) */
359 : long
360 2219 : algmodpr_get_m(GEN data) { return gel(data,2)[2]; } /* target M_k(F_p^m) */
361 : GEN
362 1715 : algmodpr_get_ff(GEN data) { return gel(data,3); }
363 : GEN
364 1708 : algmodpr_get_proj(GEN data) { return gel(data,4); }
365 : GEN
366 3003 : algmodpr_get_lift(GEN data) { return gel(data,5); }
367 : GEN
368 1729 : algmodpr_get_tau(GEN data) { return gel(data,6); }
369 : GEN
370 3948 : algmodpr_get_p(GEN data) { return pr_get_p(algmodpr_get_pr(data)); }
371 : GEN
372 3801 : algmodpr_get_T(GEN data) { return gel(data,2)[2]==1 ? NULL : gel(data,7); }
373 :
374 : /** ADDITIONAL **/
375 :
376 : /* is N=smooth*prime? */
377 15444 : static int Z_easyfactor(GEN N, ulong lim)
378 : {
379 : GEN fa;
380 15444 : if (lgefint(N) <= 3) return 1;
381 13629 : fa = absZ_factor_limit(N, lim);
382 13629 : return BPSW_psp(veclast(gel(fa,1)));
383 : }
384 :
385 : /* no garbage collection */
386 : static GEN
387 1834 : backtrackfacto(GEN y0, long n, GEN red, GEN pl, GEN nf, GEN data, int (*test)(GEN,GEN), GEN* fa, GEN N, GEN I)
388 : {
389 : long b, i;
390 1834 : ulong lim = 1UL << 17;
391 1834 : long *v = new_chunk(n+1);
392 1834 : pari_sp av = avma;
393 1834 : for (b = 0;; b += (2*b)/(3*n) + 1)
394 343 : {
395 : GEN ny, y1, y2;
396 2177 : set_avma(av);
397 6423 : for (i = 1; i <= n; i++) v[i] = -b;
398 2177 : v[n]--;
399 : for(;;)
400 : {
401 15859 : i = n;
402 16521 : while (i > 0)
403 16178 : { if (v[i] == b) v[i--] = -b; else { v[i]++; break; } }
404 15859 : if (i==0) break;
405 :
406 15516 : y1 = y0;
407 34142 : for (i = 1; i <= n; i++) y1 = nfadd(nf, y1, ZC_z_mul(gel(red,i), v[i]));
408 15516 : if (!nfchecksigns(nf, y1, pl)) continue;
409 :
410 15444 : ny = absi_shallow(nfnorm(nf, y1));
411 15444 : if (!signe(ny)) continue;
412 15444 : ny = diviiexact(ny, gcdii(ny, N));
413 15444 : if (!Z_easyfactor(ny, lim)) continue;
414 :
415 2423 : y2 = idealdivexact(nf, y1, idealadd(nf,y1,I));
416 2423 : *fa = idealfactor(nf, y2);
417 2423 : if (!data || test(data,*fa)) return y1;
418 : }
419 : }
420 : }
421 :
422 : /* if data == NULL, the test is skipped */
423 : /* in the test, the factorization does not contain the known factors */
424 : static GEN
425 1834 : factoredextchinesetest(GEN nf, GEN x, GEN y, GEN pl, GEN* fa, GEN data, int (*test)(GEN,GEN))
426 : {
427 1834 : pari_sp av = avma;
428 : long n,i;
429 1834 : GEN x1, y0, y1, red, N, I, P = gel(x,1), E = gel(x,2);
430 1834 : n = nf_get_degree(nf);
431 1834 : x = idealchineseinit(nf, mkvec2(x,pl));
432 1834 : x1 = gel(x,1);
433 1834 : red = lg(x1) == 1? matid(n): gmael(x1,1,1);
434 1834 : y0 = idealchinese(nf, x, y);
435 :
436 1834 : E = shallowcopy(E);
437 1834 : if (!gequal0(y0))
438 7846 : for (i=1; i<lg(E); i++)
439 : {
440 6012 : long v = nfval(nf,y0,gel(P,i));
441 6012 : if (cmpsi(v, gel(E,i)) < 0) gel(E,i) = stoi(v);
442 : }
443 : /* N and I : known factors */
444 1834 : I = factorbackprime(nf, P, E);
445 1834 : N = idealnorm(nf,I);
446 :
447 1834 : y1 = backtrackfacto(y0, n, red, pl, nf, data, test, fa, N, I);
448 :
449 : /* restore known factors */
450 7846 : for (i=1; i<lg(E); i++) gel(E,i) = stoi(nfval(nf,y1,gel(P,i)));
451 1834 : *fa = famat_reduce(famat_mul_shallow(*fa, mkmat2(P, E)));
452 1834 : return gc_all(av, 2, &y1, fa);
453 : }
454 :
455 : static GEN
456 1421 : factoredextchinese(GEN nf, GEN x, GEN y, GEN pl, GEN* fa)
457 1421 : { return factoredextchinesetest(nf,x,y,pl,fa,NULL,NULL); }
458 :
459 : /** OPERATIONS ON ASSOCIATIVE ALGEBRAS algebras.c **/
460 :
461 : /*
462 : Convention:
463 : (K/F,sigma,b) = sum_{i=0..n-1} u^i*K
464 : t*u = u*sigma(t)
465 :
466 : Natural basis:
467 : 1<=i<=d*n^2
468 : b_i = u^((i-1)/(dn))*ZKabs.((i-1)%(dn)+1)
469 :
470 : Integral basis:
471 : Basis of some order.
472 :
473 : al structure:
474 : 1- rnf of the cyclic splitting field of degree n over the center nf of degree d
475 : 2- VEC of aut^i 1<=i<=n if n>1, or i=0 if n=1
476 : 3- b in nf
477 : 4- infinite hasse invariants (mod n) : VECSMALL of size r1, values only 0 or n/2 (if integral)
478 : 5- finite hasse invariants (mod n) : VEC[sorted VEC of primes, VECSMALL of hasse inv mod n]
479 : 6- VEC
480 : 6.1- 0, or [a,b,sa] where sa^2=a if al is quaternion algebra (a,b)
481 : 6.2- dn^2*dn^2 matrix of stored involution
482 : 7* dn^2*dn^2 matrix expressing the integral basis in terms of the natural basis
483 : 8* dn^2*dn^2 matrix expressing the natural basis in terms of the integral basis
484 : 9* VEC of dn^2 matrices giving the dn^2*dn^2 left multiplication tables of the integral basis
485 : 10* characteristic of the base field (used only for algebras given by a multiplication table)
486 : 11* trace of basis elements
487 :
488 : If al is given by a multiplication table (al_TABLE), only the * fields are present.
489 : The other ones are filled with gen_0 placeholders.
490 : */
491 :
492 : /* assumes same center and same variable */
493 : /* currently only works for coprime degrees */
494 : GEN
495 84 : algtensor(GEN al1, GEN al2, long flag) {
496 84 : pari_sp av = avma;
497 : long v, k, d1, d2;
498 : GEN nf, P1, P2, aut1, aut2, b1, b2, C, rnf, aut, b, x1, x2, al, rnfpol;
499 :
500 84 : checkalg(al1);
501 70 : checkalg(al2);
502 63 : if (alg_type(al1) != al_CYCLIC || alg_type(al2) != al_CYCLIC)
503 21 : pari_err_IMPL("tensor of noncyclic algebras"); /* TODO: do it. */
504 :
505 42 : nf = alg_get_center(al1);
506 42 : if (!gequal(alg_get_center(al2),nf))
507 7 : pari_err_OP("tensor product [not the same center]", al1, al2);
508 :
509 35 : P1=alg_get_splitpol(al1); aut1=alg_get_aut(al1); b1=alg_get_b(al1);
510 35 : P2=alg_get_splitpol(al2); aut2=alg_get_aut(al2); b2=alg_get_b(al2);
511 35 : v=varn(P1);
512 :
513 35 : d1=alg_get_degree(al1);
514 35 : d2=alg_get_degree(al2);
515 35 : if (ugcd(d1,d2) != 1)
516 7 : pari_err_IMPL("tensor of cyclic algebras of noncoprime degrees"); /* TODO */
517 :
518 28 : if (d1==1) return gcopy(al2);
519 21 : if (d2==1) return gcopy(al1);
520 :
521 14 : C = nfcompositum(nf, P1, P2, 3);
522 14 : rnfpol = gel(C,1);
523 14 : if (!(flag & al_FACTOR)) rnfpol = mkvec2(rnfpol, stoi(1<<20));
524 14 : rnf = rnfinit(nf, rnfpol);
525 : /* TODO use integral basis of P1 and P2 to get that of C */
526 14 : x1 = gel(C,2);
527 14 : x2 = gel(C,3);
528 14 : k = itos(gel(C,4));
529 14 : aut = gadd(gsubst(aut2,v,x2),gmulsg(k,gsubst(aut1,v,x1)));
530 14 : b = nfmul(nf,nfpow_u(nf,b1,d2),nfpow_u(nf,b2,d1));
531 14 : al = alg_cyclic(rnf, aut, b, flag);
532 14 : return gerepilecopy(av,al);
533 : }
534 :
535 : /* M an n x d Flm of rank d, n >= d. Initialize Mx = y solver */
536 : static GEN
537 9041 : Flm_invimage_init(GEN M, ulong p)
538 : {
539 9041 : GEN v = Flm_indexrank(M, p), perm = gel(v,1);
540 9041 : GEN MM = rowpermute(M, perm); /* square invertible */
541 9041 : return mkvec2(Flm_inv(MM,p), perm);
542 : }
543 : /* assume Mx = y has a solution, v = Flm_invimage_init(M,p); return x */
544 : static GEN
545 619723 : Flm_invimage_pre(GEN v, GEN y, ulong p)
546 : {
547 619723 : GEN inv = gel(v,1), perm = gel(v,2);
548 619723 : return Flm_Flc_mul(inv, vecsmallpermute(y, perm), p);
549 : }
550 :
551 : GEN
552 14956 : algradical(GEN al)
553 : {
554 14956 : pari_sp av = avma;
555 : GEN I, x, traces, K, MT, P, mt;
556 : long l,i,ni, n;
557 : ulong modu, expo, p;
558 14956 : checkalg(al);
559 14956 : if (alg_type(al) != al_TABLE) return gen_0;
560 14865 : P = alg_get_char(al);
561 14865 : mt = alg_get_multable(al);
562 14865 : n = alg_get_absdim(al);
563 14865 : dbg_printf(1)("algradical: char=%Ps, dim=%d\n", P, n);
564 14865 : traces = algtracematrix(al);
565 14865 : if (!signe(P))
566 : {
567 546 : dbg_printf(2)(" char 0, computing kernel...\n");
568 546 : K = ker(traces);
569 546 : dbg_printf(2)(" ...done.\n");
570 546 : ni = lg(K)-1; if (!ni) return gc_const(av, gen_0);
571 70 : return gerepileupto(av, K);
572 : }
573 14319 : dbg_printf(2)(" char>0, computing kernel...\n");
574 14319 : K = FpM_ker(traces, P);
575 14319 : dbg_printf(2)(" ...done.\n");
576 14319 : ni = lg(K)-1; if (!ni) return gc_const(av, gen_0);
577 7930 : if (abscmpiu(P,n)>0) return gerepileupto(av, K);
578 :
579 : /* tough case, p <= n. Ronyai's algorithm */
580 4982 : p = P[2]; l = 1;
581 4982 : expo = p; modu = p*p;
582 4982 : dbg_printf(2)(" char>0, hard case.\n");
583 9748 : while (modu<=(ulong)n) { l++; modu *= p; }
584 4982 : MT = ZMV_to_FlmV(mt, modu);
585 4982 : I = ZM_to_Flm(K,p); /* I_0 */
586 13463 : for (i=1; i<=l; i++) {/*compute I_i, expo = p^i, modu = p^(l+1) > n*/
587 : long j, lig,col;
588 9041 : GEN v = cgetg(ni+1, t_VECSMALL);
589 9041 : GEN invI = Flm_invimage_init(I, p);
590 9041 : dbg_printf(2)(" computing I_%d:\n", i);
591 9041 : traces = cgetg(ni+1,t_MAT);
592 61073 : for (j = 1; j <= ni; j++)
593 : {
594 52032 : GEN M = algbasismultable_Flm(MT, gel(I,j), modu);
595 52032 : uel(v,j) = algtracei(M, p,expo,modu);
596 : }
597 61073 : for (col=1; col<=ni; col++)
598 : {
599 52032 : GEN t = cgetg(n+1,t_VECSMALL); gel(traces,col) = t;
600 52032 : x = gel(I, col); /*col-th basis vector of I_{i-1}*/
601 671755 : for (lig=1; lig<=n; lig++)
602 : {
603 619723 : GEN y = _tablemul_ej_Fl(MT,x,lig,p);
604 619723 : GEN z = Flm_invimage_pre(invI, y, p);
605 619723 : uel(t,lig) = Flv_dotproduct(v, z, p);
606 : }
607 : }
608 9041 : dbg_printf(2)(" computing kernel...\n");
609 9041 : K = Flm_ker(traces, p);
610 9041 : dbg_printf(2)(" ...done.\n");
611 9041 : ni = lg(K)-1; if (!ni) return gc_const(av, gen_0);
612 8481 : I = Flm_mul(I,K,p);
613 8481 : expo *= p;
614 : }
615 4422 : return Flm_to_ZM(I);
616 : }
617 :
618 : /* compute the multiplication table of the element x, where mt is a
619 : * multiplication table in an arbitrary ring */
620 : static GEN
621 476 : Rgmultable(GEN mt, GEN x)
622 : {
623 476 : long i, l = lg(x);
624 476 : GEN z = NULL;
625 6188 : for (i = 1; i < l; i++)
626 : {
627 5712 : GEN c = gel(x,i);
628 5712 : if (!gequal0(c))
629 : {
630 714 : GEN M = RgM_Rg_mul(gel(mt,i),c);
631 714 : z = z? RgM_add(z, M): M;
632 : }
633 : }
634 476 : return z;
635 : }
636 :
637 : static GEN
638 56 : change_Rgmultable(GEN mt, GEN P, GEN Pi)
639 : {
640 : GEN mt2;
641 56 : long lmt = lg(mt), i;
642 56 : mt2 = cgetg(lmt,t_VEC);
643 532 : for (i=1;i<lmt;i++) {
644 476 : GEN mti = Rgmultable(mt,gel(P,i));
645 476 : gel(mt2,i) = RgM_mul(Pi, RgM_mul(mti,P));
646 : }
647 56 : return mt2;
648 : }
649 :
650 : /* S: lift (basis of quotient) ; Si: proj */
651 : static GEN
652 38475 : alg_quotient0(GEN al, GEN S, GEN Si, long nq, GEN p, long maps)
653 : {
654 38475 : GEN mt = cgetg(nq+1,t_VEC), P, Pi, d;
655 : long i;
656 38475 : dbg_printf(3)(" alg_quotient0: char=%Ps, dim=%d, dim I=%d\n", p, alg_get_absdim(al), lg(S)-1);
657 176124 : for (i=1; i<=nq; i++) {
658 137649 : GEN mti = algbasismultable(al,gel(S,i));
659 137649 : if (signe(p)) gel(mt,i) = FpM_mul(Si, FpM_mul(mti,S,p), p);
660 6076 : else gel(mt,i) = RgM_mul(Si, RgM_mul(mti,S));
661 : }
662 38475 : if (!signe(p) && !isint1(Q_denom(mt))) {
663 42 : dbg_printf(3)(" bad case: denominator=%Ps\n", Q_denom(mt));
664 42 : P = Q_remove_denom(Si,&d);
665 42 : P = ZM_hnf(P);
666 42 : P = RgM_Rg_div(P,d); /* P: new basis (Z-basis of image of order in al) */
667 42 : Pi = RgM_inv(P);
668 42 : mt = change_Rgmultable(mt,P,Pi);
669 42 : Si = RgM_mul(Pi,Si);
670 42 : S = RgM_mul(S,P);
671 : }
672 38475 : al = algtableinit_i(mt,p);
673 38475 : if (maps) al = mkvec3(al,Si,S); /* algebra, proj, lift */
674 38475 : return al;
675 : }
676 :
677 : /* quotient of an algebra by a nontrivial two-sided ideal */
678 : GEN
679 12357 : alg_quotient(GEN al, GEN I, long maps)
680 : {
681 12357 : pari_sp av = avma;
682 : GEN p, IS, ISi, S, Si;
683 : long n, ni;
684 :
685 12357 : checkalg(al);
686 12357 : if (alg_type(al) != al_TABLE) pari_err_TYPE("alg_quotient [not a table algebra]", al);
687 12350 : p = alg_get_char(al);
688 12350 : n = alg_get_absdim(al);
689 12350 : ni = lg(I)-1;
690 :
691 : /* force first vector of complement to be the identity */
692 12350 : IS = shallowconcat(I, gcoeff(alg_get_multable(al),1,1));
693 12350 : if (signe(p)) {
694 12322 : IS = FpM_suppl(IS,p);
695 12322 : ISi = FpM_inv(IS,p);
696 : }
697 : else {
698 28 : IS = suppl(IS);
699 28 : ISi = RgM_inv(IS);
700 : }
701 12350 : S = vecslice(IS, ni+1, n);
702 12350 : Si = rowslice(ISi, ni+1, n);
703 12350 : return gerepilecopy(av, alg_quotient0(al, S, Si, n-ni, p, maps));
704 : }
705 :
706 : static GEN
707 39501 : image_keep_first(GEN m, GEN p) /* assume first column is nonzero or m==0, no GC */
708 : {
709 : GEN ir, icol, irow, M, c, x;
710 : long i;
711 39501 : if (gequal0(gel(m,1))) return zeromat(nbrows(m),0);
712 :
713 39487 : if (signe(p)) ir = FpM_indexrank(m,p);
714 1708 : else ir = indexrank(m);
715 :
716 39487 : icol = gel(ir,2);
717 39487 : if (icol[1]==1) return extract0(m,icol,NULL);
718 :
719 14 : irow = gel(ir,1);
720 14 : M = extract0(m, irow, icol);
721 14 : c = extract0(gel(m,1), irow, NULL);
722 14 : if (signe(p)) x = FpM_FpC_invimage(M,c,p);
723 0 : else x = inverseimage(M,c); /* TODO modulo a small prime */
724 :
725 21 : for (i=1; i<lg(x); i++)
726 : {
727 21 : if (!gequal0(gel(x,i)))
728 : {
729 14 : icol[i] = 1;
730 14 : vecsmall_sort(icol);
731 14 : return extract0(m,icol,NULL);
732 : }
733 : }
734 :
735 : return NULL; /* LCOV_EXCL_LINE */
736 : }
737 :
738 : /* z[1],...z[nz] central elements such that z[1]A + z[2]A + ... + z[nz]A = A
739 : * is a direct sum. idempotents ==> first basis element is identity */
740 : GEN
741 12694 : alg_centralproj(GEN al, GEN z, long maps)
742 : {
743 12694 : pari_sp av = avma;
744 : GEN S, U, Ui, alq, p;
745 12694 : long i, iu, lz = lg(z), ta;
746 :
747 12694 : checkalg(al);
748 12694 : ta = alg_type(al);
749 12694 : if (ta != al_TABLE) pari_err_TYPE("algcentralproj [not a table algebra]", al);
750 12687 : if (typ(z) != t_VEC) pari_err_TYPE("alcentralproj",z);
751 12680 : p = alg_get_char(al);
752 12680 : dbg_printf(3)(" alg_centralproj: char=%Ps, dim=%d, #z=%d\n", p, alg_get_absdim(al), lz-1);
753 12680 : S = cgetg(lz,t_VEC); /* S[i] = Im(z_i) */
754 38819 : for (i=1; i<lz; i++)
755 : {
756 26139 : GEN mti = algbasismultable(al, gel(z,i));
757 26139 : gel(S,i) = image_keep_first(mti,p);
758 : }
759 12680 : U = shallowconcat1(S); /* U = [Im(z_1)|Im(z_2)|...|Im(z_nz)], n x n */
760 12680 : if (lg(U)-1 < alg_get_absdim(al)) pari_err_TYPE("alcentralproj [z[i]'s not surjective]",z);
761 12673 : if (signe(p)) Ui = FpM_inv(U,p);
762 854 : else Ui = RgM_inv(U);
763 : if (!Ui) pari_err_BUG("alcentralproj"); /*LCOV_EXCL_LINE*/
764 :
765 12673 : alq = cgetg(lz,t_VEC);
766 38798 : for (iu=0,i=1; i<lz; i++)
767 : {
768 26125 : long nq = lg(gel(S,i))-1, ju = iu + nq;
769 26125 : GEN Si = rowslice(Ui, iu+1, ju);
770 26125 : gel(alq, i) = alg_quotient0(al,gel(S,i),Si,nq,p,maps);
771 26125 : iu = ju;
772 : }
773 12673 : return gerepilecopy(av, alq);
774 : }
775 :
776 : /* al is an al_TABLE */
777 : static GEN
778 33245 : algtablecenter(GEN al)
779 : {
780 33245 : pari_sp av = avma;
781 : long n, i, j, k, ic;
782 : GEN C, cij, mt, p;
783 :
784 33245 : n = alg_get_absdim(al);
785 33245 : mt = alg_get_multable(al);
786 33245 : p = alg_get_char(al);
787 33245 : C = cgetg(n+1,t_MAT);
788 158605 : for (j=1; j<=n; j++)
789 : {
790 125360 : gel(C,j) = cgetg(n*n-n+1,t_COL);
791 125360 : ic = 1;
792 1124680 : for (i=2; i<=n; i++) {
793 999320 : if (signe(p)) cij = FpC_sub(gmael(mt,i,j),gmael(mt,j,i),p);
794 57694 : else cij = RgC_sub(gmael(mt,i,j),gmael(mt,j,i));
795 20001840 : for (k=1; k<=n; k++, ic++) gcoeff(C,ic,j) = gel(cij, k);
796 : }
797 : }
798 33245 : if (signe(p)) return gerepileupto(av, FpM_ker(C,p));
799 1785 : else return gerepileupto(av, ker(C));
800 : }
801 :
802 : GEN
803 11795 : algcenter(GEN al)
804 : {
805 11795 : checkalg(al);
806 11795 : if (alg_type(al)==al_TABLE) return algtablecenter(al);
807 2821 : return alg_get_center(al);
808 : }
809 :
810 : /* Only in positive characteristic. Assumes that al is semisimple. */
811 : GEN
812 9720 : algprimesubalg(GEN al)
813 : {
814 9720 : pari_sp av = avma;
815 : GEN p, Z, F, K;
816 : long nz, i;
817 9720 : checkalg(al);
818 9720 : p = alg_get_char(al);
819 9720 : if (!signe(p)) pari_err_DOMAIN("algprimesubalg","characteristic","=",gen_0,p);
820 :
821 9706 : Z = algtablecenter(al);
822 9706 : nz = lg(Z)-1;
823 9706 : if (nz==1) return Z;
824 :
825 6759 : F = cgetg(nz+1, t_MAT);
826 28340 : for (i=1; i<=nz; i++) {
827 21581 : GEN zi = gel(Z,i);
828 21581 : gel(F,i) = FpC_sub(algpow(al,zi,p),zi,p);
829 : }
830 6759 : K = FpM_ker(F,p);
831 6759 : return gerepileupto(av, FpM_mul(Z,K,p));
832 : }
833 :
834 : static GEN
835 19871 : out_decompose(GEN t, GEN Z, GEN P, GEN p)
836 : {
837 19871 : GEN ali = gel(t,1), projm = gel(t,2), liftm = gel(t,3), pZ;
838 19871 : if (signe(p)) pZ = FpM_image(FpM_mul(projm,Z,p),p);
839 1617 : else pZ = image(RgM_mul(projm,Z));
840 19871 : return mkvec5(ali, projm, liftm, pZ, P);
841 : }
842 : /* fa factorization of charpol(x) */
843 : static GEN
844 9974 : alg_decompose_from_facto(GEN al, GEN x, GEN fa, GEN Z, long mini)
845 : {
846 9974 : long k = lgcols(fa)-1, k2 = mini? 1: k/2;
847 9974 : GEN v1 = rowslice(fa,1,k2);
848 9974 : GEN v2 = rowslice(fa,k2+1,k);
849 9974 : GEN alq, P, Q, p = alg_get_char(al);
850 9974 : dbg_printf(3)(" alg_decompose_from_facto\n");
851 9974 : if (signe(p)) {
852 9148 : P = FpXV_factorback(gel(v1,1), gel(v1,2), p, 0);
853 9148 : Q = FpXV_factorback(gel(v2,1), gel(v2,2), p, 0);
854 9148 : P = FpX_mul(P, FpXQ_inv(P,Q,p), p);
855 : }
856 : else {
857 826 : P = factorback(v1);
858 826 : Q = factorback(v2);
859 826 : P = RgX_mul(P, RgXQ_inv(P,Q));
860 : }
861 9974 : P = algpoleval(al, P, x);
862 9974 : if (signe(p)) Q = FpC_sub(col_ei(lg(P)-1,1), P, p);
863 826 : else Q = gsub(gen_1, P);
864 9974 : if (gequal0(P) || gequal0(Q)) return NULL;
865 9974 : alq = alg_centralproj(al, mkvec2(P,Q), 1);
866 :
867 9974 : P = out_decompose(gel(alq,1), Z, P, p); if (mini) return P;
868 9897 : Q = out_decompose(gel(alq,2), Z, Q, p);
869 9897 : return mkvec2(P,Q);
870 : }
871 :
872 : static GEN
873 15231 : random_pm1(long n)
874 : {
875 15231 : GEN z = cgetg(n+1,t_VECSMALL);
876 : long i;
877 62885 : for (i = 1; i <= n; i++) z[i] = random_bits(5)%3 - 1;
878 15231 : return z;
879 : }
880 :
881 : static GEN alg_decompose(GEN al, GEN Z, long mini, GEN* pt_primelt);
882 : /* Try to split al using x's charpoly. Return gen_0 if simple, NULL if failure.
883 : * And a splitting otherwise
884 : * If pt_primelt!=NULL, compute a primitive element of the center when simple */
885 : static GEN
886 18291 : try_fact(GEN al, GEN x, GEN zx, GEN Z, GEN Zal, long mini, GEN* pt_primelt)
887 : {
888 18291 : GEN z, dec0, dec1, cp = algcharpoly(Zal,zx,0,1), fa, p = alg_get_char(al);
889 : long nfa, e;
890 18291 : dbg_printf(3)(" try_fact: zx=%Ps\n", zx);
891 18291 : if (signe(p)) fa = FpX_factor(cp,p);
892 1512 : else fa = factor(cp);
893 18291 : dbg_printf(3)(" charpoly=%Ps\n", fa);
894 18291 : nfa = nbrows(fa);
895 18291 : if (nfa == 1) {
896 8317 : if (signe(p)) e = gel(fa,2)[1];
897 686 : else e = itos(gcoeff(fa,1,2));
898 8317 : if (e == 1) {
899 4487 : if (pt_primelt != NULL) *pt_primelt = mkvec2(x, cp);
900 4487 : return gen_0;
901 : }
902 3830 : else return NULL;
903 : }
904 9974 : dec0 = alg_decompose_from_facto(al, x, fa, Z, mini);
905 9974 : if (!dec0) return NULL;
906 9974 : if (!mini) return dec0;
907 77 : dec1 = alg_decompose(gel(dec0,1), gel(dec0,4), 1, pt_primelt);
908 77 : z = gel(dec0,5);
909 77 : if (!isintzero(dec1)) {
910 7 : if (signe(p)) z = FpM_FpC_mul(gel(dec0,3),dec1,p);
911 7 : else z = RgM_RgC_mul(gel(dec0,3),dec1);
912 : }
913 77 : return z;
914 : }
915 : static GEN
916 7 : randcol(long n, GEN b)
917 : {
918 7 : GEN N = addiu(shifti(b,1), 1);
919 : long i;
920 7 : GEN res = cgetg(n+1,t_COL);
921 63 : for (i=1; i<=n; i++)
922 : {
923 56 : pari_sp av = avma;
924 56 : gel(res,i) = gerepileuptoint(av, subii(randomi(N),b));
925 : }
926 7 : return res;
927 : }
928 : /* Return gen_0 if already simple. mini: only returns a central idempotent
929 : * corresponding to one simple factor
930 : * if pt_primelt!=NULL, sets it to a primitive element of the center when simple */
931 : static GEN
932 28438 : alg_decompose(GEN al, GEN Z, long mini, GEN* pt_primelt)
933 : {
934 : pari_sp av;
935 : GEN Zal, x, zx, rand, dec0, B, p;
936 28438 : long i, nz = lg(Z)-1;
937 :
938 28438 : if (nz == 1) {
939 13977 : if (pt_primelt != 0) *pt_primelt = mkvec2(zerocol(alg_get_dim(al)), pol_x(0));
940 13977 : return gen_0;
941 : }
942 14461 : p = alg_get_char(al);
943 14461 : dbg_printf(2)(" alg_decompose: char=%Ps, dim=%d, dim Z=%d\n", p, alg_get_absdim(al), nz);
944 14461 : Zal = alg_subalg(al,Z);
945 14461 : Z = gel(Zal,2);
946 14461 : Zal = gel(Zal,1);
947 14461 : av = avma;
948 :
949 14461 : rand = random_pm1(nz);
950 14461 : zx = zc_to_ZC(rand);
951 14461 : if (signe(p)) {
952 13313 : zx = FpC_red(zx,p);
953 13313 : x = ZM_zc_mul(Z,rand);
954 13313 : x = FpC_red(x,p);
955 : }
956 1148 : else x = RgM_zc_mul(Z,rand);
957 14461 : dec0 = try_fact(al,x,zx,Z,Zal,mini,pt_primelt);
958 14461 : if (dec0) return dec0;
959 3760 : set_avma(av);
960 :
961 3830 : for (i=2; i<=nz; i++)
962 : {
963 3823 : dec0 = try_fact(al,gel(Z,i),col_ei(nz,i),Z,Zal,mini,pt_primelt);
964 3823 : if (dec0) return dec0;
965 70 : set_avma(av);
966 : }
967 7 : B = int2n(10);
968 : for (;;)
969 0 : {
970 7 : GEN x = randcol(nz,B), zx = ZM_ZC_mul(Z,x);
971 7 : dec0 = try_fact(al,x,zx,Z,Zal,mini,pt_primelt);
972 7 : if (dec0) return dec0;
973 0 : set_avma(av);
974 : }
975 : }
976 :
977 : static GEN
978 24098 : alg_decompose_total(GEN al, GEN Z, long maps)
979 : {
980 : GEN dec, sc, p;
981 : long i;
982 :
983 24098 : dec = alg_decompose(al, Z, 0, NULL);
984 24098 : if (isintzero(dec))
985 : {
986 14201 : if (maps) {
987 9021 : long n = alg_get_absdim(al);
988 9021 : al = mkvec3(al, matid(n), matid(n));
989 : }
990 14201 : return mkvec(al);
991 : }
992 9897 : p = alg_get_char(al); if (!signe(p)) p = NULL;
993 9897 : sc = cgetg(lg(dec), t_VEC);
994 29691 : for (i=1; i<lg(sc); i++) {
995 19794 : GEN D = gel(dec,i), a = gel(D,1), Za = gel(D,4);
996 19794 : GEN S = alg_decompose_total(a, Za, maps);
997 19794 : gel(sc,i) = S;
998 19794 : if (maps)
999 : {
1000 13186 : GEN projm = gel(D,2), liftm = gel(D,3);
1001 13186 : long j, lS = lg(S);
1002 34814 : for (j=1; j<lS; j++)
1003 : {
1004 21628 : GEN Sj = gel(S,j), p2 = gel(Sj,2), l2 = gel(Sj,3);
1005 21628 : if (p) p2 = FpM_mul(p2, projm, p);
1006 1449 : else p2 = RgM_mul(p2, projm);
1007 21628 : if (p) l2 = FpM_mul(liftm, l2, p);
1008 1449 : else l2 = RgM_mul(liftm, l2);
1009 21628 : gel(Sj,2) = p2;
1010 21628 : gel(Sj,3) = l2;
1011 : }
1012 : }
1013 : }
1014 9897 : return shallowconcat1(sc);
1015 : }
1016 :
1017 : static GEN
1018 14531 : alg_subalg(GEN al, GEN basis)
1019 : {
1020 14531 : GEN invbasis, mt, p = alg_get_char(al);
1021 : long i, j, n;
1022 :
1023 14531 : if (!signe(p)) p = NULL;
1024 14531 : basis = shallowmatconcat(mkvec2(col_ei(alg_get_absdim(al),1), basis));
1025 14531 : if (p)
1026 : {
1027 13362 : basis = image_keep_first(basis,p);
1028 13362 : invbasis = FpM_inv(basis,p);
1029 : }
1030 : else
1031 : { /* FIXME use an integral variant of image_keep_first */
1032 1169 : basis = QM_ImQ_hnf(basis);
1033 1169 : invbasis = RgM_inv(basis);
1034 : }
1035 14531 : n = lg(basis)-1;
1036 14531 : mt = cgetg(n+1,t_VEC);
1037 14531 : gel(mt,1) = matid(n);
1038 45015 : for (i = 2; i <= n; i++)
1039 : {
1040 30484 : GEN mtx = cgetg(n+1,t_MAT), x = gel(basis,i);
1041 30484 : gel(mtx,1) = col_ei(n,i);
1042 182326 : for (j = 2; j <= n; j++)
1043 : {
1044 151842 : GEN xy = algmul(al, x, gel(basis,j));
1045 151842 : if (p) gel(mtx,j) = FpM_FpC_mul(invbasis, xy, p);
1046 36253 : else gel(mtx,j) = RgM_RgC_mul(invbasis, xy);
1047 : }
1048 30484 : gel(mt,i) = mtx;
1049 : }
1050 14531 : return mkvec2(algtableinit_i(mt,p), basis);
1051 : }
1052 :
1053 : GEN
1054 84 : algsubalg(GEN al, GEN basis)
1055 : {
1056 84 : pari_sp av = avma;
1057 : GEN p;
1058 84 : checkalg(al);
1059 84 : if (alg_type(al) == al_REAL) pari_err_TYPE("algsubalg [real algebra]", al);
1060 77 : if (typ(basis) != t_MAT) pari_err_TYPE("algsubalg",basis);
1061 70 : p = alg_get_char(al);
1062 70 : if (signe(p)) basis = RgM_to_FpM(basis,p);
1063 70 : return gerepilecopy(av, alg_subalg(al,basis));
1064 : }
1065 :
1066 : static int
1067 14642 : cmp_algebra(GEN x, GEN y)
1068 : {
1069 : long d;
1070 14642 : d = gel(x,1)[1] - gel(y,1)[1]; if (d) return d < 0? -1: 1;
1071 12733 : d = gel(x,1)[2] - gel(y,1)[2]; if (d) return d < 0? -1: 1;
1072 12733 : return cmp_universal(gel(x,2), gel(y,2));
1073 : }
1074 :
1075 : GEN
1076 9825 : algsimpledec_ss(GEN al, long maps)
1077 : {
1078 9825 : pari_sp av = avma;
1079 : GEN Z, p, r, res, perm;
1080 : long i, l, n;
1081 9825 : checkalg(al);
1082 9825 : p = alg_get_char(al);
1083 9825 : dbg_printf(1)("algsimpledec_ss: char=%Ps, dim=%d\n", p, alg_get_absdim(al));
1084 9825 : if (signe(p)) Z = algprimesubalg(al);
1085 273 : else if (alg_type(al)!=al_TABLE) Z = gen_0;
1086 252 : else Z = algtablecenter(al);
1087 :
1088 9825 : if (lg(Z) == 2) {/* dim Z = 1 */
1089 5521 : n = alg_get_absdim(al);
1090 5521 : set_avma(av);
1091 5521 : if (!maps) return mkveccopy(al);
1092 4814 : retmkvec(mkvec3(gcopy(al), matid(n), matid(n)));
1093 : }
1094 4304 : res = alg_decompose_total(al, Z, maps);
1095 4304 : l = lg(res); r = cgetg(l, t_VEC);
1096 18505 : for (i = 1; i < l; i++)
1097 : {
1098 14201 : GEN A = maps? gmael(res,i,1): gel(res,i);
1099 14201 : gel(r,i) = mkvec2(mkvecsmall2(alg_get_dim(A), lg(algtablecenter(A))),
1100 : alg_get_multable(A));
1101 : }
1102 4304 : perm = gen_indexsort(r, (void*)cmp_algebra, &cmp_nodata);
1103 4304 : return gerepilecopy(av, vecpermute(res, perm));
1104 : }
1105 :
1106 : GEN
1107 2730 : algsimpledec(GEN al, long maps)
1108 : {
1109 2730 : pari_sp av = avma;
1110 : int ss;
1111 2730 : GEN rad, dec, res, proj=NULL, lift=NULL;
1112 2730 : rad = algradical(al);
1113 2730 : ss = gequal0(rad);
1114 2730 : if (!ss)
1115 : {
1116 1428 : al = alg_quotient(al, rad, maps);
1117 1428 : if (maps) {
1118 14 : proj = gel(al,2);
1119 14 : lift = gel(al,3);
1120 14 : al = gel(al,1);
1121 : }
1122 : }
1123 2730 : dec = algsimpledec_ss(al, maps);
1124 2730 : if (!ss && maps) /* update maps */
1125 : {
1126 14 : GEN p = alg_get_char(al);
1127 : long i;
1128 42 : for (i=1; i<lg(dec); i++)
1129 : {
1130 28 : if (signe(p))
1131 : {
1132 14 : gmael(dec,i,2) = FpM_mul(gmael(dec,i,2), proj, p);
1133 14 : gmael(dec,i,3) = FpM_mul(lift, gmael(dec,i,3), p);
1134 : }
1135 : else
1136 : {
1137 14 : gmael(dec,i,2) = RgM_mul(gmael(dec,i,2), proj);
1138 14 : gmael(dec,i,3) = RgM_mul(lift, gmael(dec,i,3));
1139 : }
1140 : }
1141 : }
1142 2730 : res = mkvec2(rad, dec);
1143 2730 : return gerepilecopy(av,res);
1144 : }
1145 :
1146 : static GEN alg_idempotent(GEN al, long n, long d);
1147 : static GEN
1148 13057 : try_split(GEN al, GEN x, long n, long d)
1149 : {
1150 13057 : GEN cp, p = alg_get_char(al), fa, e, pol, exp, P, Q, U, u, mx, mte, ire;
1151 13057 : long nfa, i, smalldim = alg_get_absdim(al)+1, dim, smalli = 0;
1152 13057 : cp = algcharpoly(al,x,0,1);
1153 13057 : fa = FpX_factor(cp,p);
1154 13057 : nfa = nbrows(fa);
1155 13057 : if (nfa == 1) return NULL;
1156 5061 : pol = gel(fa,1);
1157 5061 : exp = gel(fa,2);
1158 :
1159 : /* charpoly is always a d-th power */
1160 15696 : for (i=1; i<lg(exp); i++) {
1161 10642 : if (exp[i]%d) pari_err(e_MISC, "the algebra must be simple (try_split 1)");
1162 10635 : exp[i] /= d;
1163 : }
1164 5054 : cp = FpXV_factorback(gel(fa,1), gel(fa,2), p, 0);
1165 :
1166 : /* find smallest Fp-dimension of a characteristic space */
1167 15689 : for (i=1; i<lg(pol); i++) {
1168 10635 : dim = degree(gel(pol,i))*exp[i];
1169 10635 : if (dim < smalldim) {
1170 5126 : smalldim = dim;
1171 5126 : smalli = i;
1172 : }
1173 : }
1174 5054 : i = smalli;
1175 5054 : if (smalldim != n) return NULL;
1176 : /* We could also compute e*al*e and try again with this smaller algebra */
1177 : /* Fq-rank 1 = Fp-rank n idempotent: success */
1178 :
1179 : /* construct idempotent */
1180 5040 : mx = algbasismultable(al,x);
1181 5040 : P = gel(pol,i);
1182 5040 : P = FpX_powu(P, exp[i], p);
1183 5040 : Q = FpX_div(cp, P, p);
1184 5040 : e = algpoleval(al, Q, mkvec2(x,mx));
1185 5040 : U = FpXQ_inv(Q, P, p);
1186 5040 : u = algpoleval(al, U, mkvec2(x,mx));
1187 5040 : e = algbasismul(al, e, u);
1188 5040 : mte = algbasisrightmultable(al,e);
1189 5040 : ire = FpM_indexrank(mte,p);
1190 5040 : if (lg(gel(ire,1))-1 != smalldim*d) pari_err(e_MISC, "the algebra must be simple (try_split 2)");
1191 :
1192 5033 : return mkvec3(e,mte,ire);
1193 : }
1194 :
1195 : /*
1196 : * Given a simple algebra al of dimension d^2 over its center of degree n,
1197 : * find an idempotent e in al with rank n (which is minimal).
1198 : */
1199 : static GEN
1200 5047 : alg_idempotent(GEN al, long n, long d)
1201 : {
1202 5047 : pari_sp av = avma;
1203 5047 : long i, N = alg_get_absdim(al);
1204 5047 : GEN e, p = alg_get_char(al), x;
1205 12728 : for(i=2; i<=N; i++) {
1206 12385 : x = col_ei(N,i);
1207 12385 : e = try_split(al, x, n, d);
1208 12371 : if (e) return e;
1209 7681 : set_avma(av);
1210 : }
1211 : for(;;) {
1212 672 : x = random_FpC(N,p);
1213 672 : e = try_split(al, x, n, d);
1214 672 : if (e) return e;
1215 329 : set_avma(av);
1216 : }
1217 : }
1218 :
1219 : static GEN
1220 4585 : try_descend(GEN M, GEN B, GEN p, long m, long n, long d)
1221 : {
1222 4585 : GEN B2 = cgetg(m+1,t_MAT), b;
1223 4585 : long i, j, k=0;
1224 13321 : for (i=1; i<=d; i++)
1225 : {
1226 8736 : k++;
1227 8736 : b = gel(B,i);
1228 8736 : gel(B2,k) = b;
1229 20426 : for (j=1; j<n; j++)
1230 : {
1231 11690 : k++;
1232 11690 : b = FpM_FpC_mul(M,b,p);
1233 11690 : gel(B2,k) = b;
1234 : }
1235 : }
1236 4585 : if (!signe(FpM_det(B2,p))) return NULL;
1237 4165 : return FpM_inv(B2,p);
1238 : }
1239 :
1240 : /* Given an m*m matrix M with irreducible charpoly over F of degree n,
1241 : * let K = F(M), which is a field, and write m=d*n.
1242 : * Compute the d-dimensional K-vector space structure on V=F^m induced by M.
1243 : * Return [B,C] where:
1244 : * - B is m*d matrix over F giving a K-basis b_1,...,b_d of V
1245 : * - C is d*m matrix over F[x] expressing the canonical F-basis of V on the b_i
1246 : * Currently F = Fp TODO extend this. */
1247 : static GEN
1248 4165 : descend_i(GEN M, long n, GEN p)
1249 : {
1250 : GEN B, C;
1251 : long m,d,i;
1252 : pari_sp av;
1253 4165 : m = lg(M)-1;
1254 4165 : d = m/n;
1255 4165 : B = cgetg(d+1,t_MAT);
1256 4165 : av = avma;
1257 :
1258 : /* try a subset of the canonical basis */
1259 12061 : for (i=1; i<=d; i++)
1260 7896 : gel(B,i) = col_ei(m,n*(i-1)+1);
1261 4165 : C = try_descend(M,B,p,m,n,d);
1262 4165 : if (C) return mkvec2(B,C);
1263 385 : set_avma(av);
1264 :
1265 : /* try smallish elements */
1266 1155 : for (i=1; i<=d; i++)
1267 770 : gel(B,i) = FpC_red(zc_to_ZC(random_pm1(m)),p);
1268 385 : C = try_descend(M,B,p,m,n,d);
1269 385 : if (C) return mkvec2(B,C);
1270 35 : set_avma(av);
1271 :
1272 : /* try random elements */
1273 : for (;;)
1274 : {
1275 105 : for (i=1; i<=d; i++)
1276 70 : gel(B,i) = random_FpC(m,p);
1277 35 : C = try_descend(M,B,p,m,n,d);
1278 35 : if (C) return mkvec2(B,C);
1279 0 : set_avma(av);
1280 : }
1281 : }
1282 : static GEN
1283 18746 : RgC_contract(GEN C, long n, long v) /* n>1 */
1284 : {
1285 : GEN C2, P;
1286 : long m, d, i, j;
1287 18746 : m = lg(C)-1;
1288 18746 : d = m/n;
1289 18746 : C2 = cgetg(d+1,t_COL);
1290 55034 : for (i=1; i<=d; i++)
1291 : {
1292 36288 : P = pol_xn(n-1,v);
1293 131348 : for (j=1; j<=n; j++)
1294 95060 : gel(P,j+1) = gel(C,n*(i-1)+j);
1295 36288 : P = normalizepol(P);
1296 36288 : gel(C2,i) = P;
1297 : }
1298 18746 : return C2;
1299 : }
1300 : static GEN
1301 4165 : RgM_contract(GEN A, long n, long v) /* n>1 */
1302 : {
1303 4165 : GEN A2 = cgetg(lg(A),t_MAT);
1304 : long i;
1305 22911 : for (i=1; i<lg(A2); i++)
1306 18746 : gel(A2,i) = RgC_contract(gel(A,i),n,v);
1307 4165 : return A2;
1308 : }
1309 : static GEN
1310 4165 : descend(GEN M, long n, GEN p, long v)
1311 : {
1312 4165 : GEN res = descend_i(M,n,p);
1313 4165 : gel(res,2) = RgM_contract(gel(res,2),n,v);
1314 4165 : return res;
1315 : }
1316 :
1317 : /* isomorphism of Fp-vector spaces M_d(F_p^n) -> (F_p)^(d^2*n) */
1318 : static GEN
1319 49343 : RgM_mat2col(GEN M, long d, long n)
1320 : {
1321 49343 : long nd = d*n, N = d*nd, i, j, ni, nj;
1322 49343 : GEN C = cgetg(N+1, t_COL);
1323 168504 : for (i=1, ni = 0; i<=d; i++, ni += nd)
1324 459508 : for (j=1, nj = 0; j<=d; j++, nj += n)
1325 : {
1326 340347 : GEN P = gcoeff(M,i,j);
1327 340347 : long k, e = ni + nj + 1;
1328 340347 : if (typ(P)==t_POL)
1329 : {
1330 339731 : long dP = degpol(P);
1331 706043 : for (k = 0; k <= dP; k++)
1332 366312 : gel(C,e+k) = gel(P,k+2);
1333 : } else
1334 : {
1335 616 : gel(C,e) = P;
1336 616 : k = 1;
1337 : }
1338 555042 : for ( ; k < n; k++)
1339 214695 : gel(C,e+k) = gen_0;
1340 : }
1341 49343 : return C;
1342 : }
1343 : /* inverse isomorphism */
1344 : static GEN
1345 1708 : RgC_col2mat(GEN C, long d, long n, long v)
1346 : {
1347 : long i, j, start;
1348 1708 : GEN M = cgetg(d+1, t_MAT), cM;
1349 5432 : for (j=1; j<=d; j++)
1350 : {
1351 3724 : cM = cgetg(d+1, t_COL);
1352 14420 : for (i=1; i<=d; i++)
1353 : {
1354 10696 : start = n*(d*(i-1)+j-1)+1;
1355 10696 : if (n==1) gel(cM,i) = gel(C, start);
1356 4564 : else gel(cM,i) = RgV_to_RgX(vecslice(C, start, start+n-1), v);
1357 : }
1358 3724 : gel(M,j) = cM;
1359 : }
1360 1708 : return M;
1361 : }
1362 :
1363 : static GEN
1364 6510 : alg_finite_csa_split(GEN al, long v)
1365 : {
1366 : GEN Z, e, mte, ire, primelt, b, T, M, proje, lifte, extre, p, B, C, mt, mx, map, mapi, T2, ro;
1367 6510 : long n, d, N = alg_get_absdim(al), i;
1368 6510 : p = alg_get_char(al);
1369 : /* compute the center */
1370 6510 : Z = algcenter(al);
1371 : /* TODO option to give the center as input instead of computing it */
1372 6510 : n = lg(Z)-1;
1373 :
1374 : /* compute a minimal rank idempotent e */
1375 6510 : if (n==N) {
1376 1456 : d = 1;
1377 1456 : e = col_ei(N,1);
1378 1456 : mte = matid(N);
1379 1456 : ire = mkvec2(identity_perm(n),identity_perm(n));
1380 : }
1381 : else {
1382 5054 : d = usqrt(N/n);
1383 5054 : if (d*d*n != N) pari_err(e_MISC, "the algebra must be simple (alg_finite_csa_split 1)");
1384 5047 : e = alg_idempotent(al,n,d);
1385 5033 : mte = gel(e,2);
1386 5033 : ire = gel(e,3);
1387 5033 : e = gel(e,1);
1388 : }
1389 :
1390 : /* identify the center */
1391 6489 : if (n==1)
1392 : {
1393 2317 : T = pol_x(v);
1394 2317 : primelt = gen_0;
1395 : }
1396 : else
1397 : {
1398 4172 : b = alg_decompose(al, Z, 1, &primelt);
1399 4172 : if (!gequal0(b)) pari_err(e_MISC, "the algebra must be simple (alg_finite_csa_split 2)");
1400 4165 : T = gel(primelt,2);
1401 4165 : primelt = gel(primelt,1);
1402 4165 : setvarn(T,v);
1403 : }
1404 :
1405 : /* use the ffinit polynomial */
1406 6482 : if (n>1)
1407 : {
1408 4165 : T2 = init_Fq(p,n,v);
1409 4165 : setvarn(T,fetch_var_higher());
1410 4165 : ro = FpXQX_roots(T2,T,p);
1411 4165 : ro = gel(ro,1);
1412 4165 : primelt = algpoleval(al,ro,primelt);
1413 4165 : T = T2;
1414 4165 : delete_var();
1415 : }
1416 :
1417 : /* descend al*e to a vector space over the center */
1418 : /* lifte: al*e -> al ; proje: al*e -> al */
1419 6482 : lifte = shallowextract(mte,gel(ire,2));
1420 6482 : extre = shallowmatextract(mte,gel(ire,1),gel(ire,2));
1421 6482 : extre = FpM_inv(extre,p);
1422 6482 : proje = rowpermute(mte,gel(ire,1));
1423 6482 : proje = FpM_mul(extre,proje,p);
1424 6482 : if (n==1)
1425 : {
1426 2317 : B = lifte;
1427 2317 : C = proje;
1428 : }
1429 : else
1430 : {
1431 4165 : M = algbasismultable(al,primelt);
1432 4165 : M = FpM_mul(M,lifte,p);
1433 4165 : M = FpM_mul(proje,M,p);
1434 4165 : B = descend(M,n,p,v);
1435 4165 : C = gel(B,2);
1436 4165 : B = gel(B,1);
1437 4165 : B = FpM_mul(lifte,B,p);
1438 4165 : C = FqM_mul(C,proje,T,p);
1439 : }
1440 :
1441 : /* compute the isomorphism */
1442 6482 : mt = alg_get_multable(al);
1443 6482 : map = cgetg(N+1,t_VEC);
1444 6482 : M = cgetg(N+1,t_MAT);
1445 55321 : for (i=1; i<=N; i++)
1446 : {
1447 48839 : mx = gel(mt,i);
1448 48839 : mx = FpM_mul(mx,B,p);
1449 48839 : mx = FqM_mul(C,mx,T,p);
1450 48839 : gel(map,i) = mx;
1451 48839 : gel(M,i) = RgM_mat2col(mx,d,n);
1452 : }
1453 6482 : mapi = FpM_inv(M,p);
1454 6482 : if (!mapi) pari_err(e_MISC, "the algebra must be simple (alg_finite_csa_split 3)");
1455 6475 : return mkvec4(T,map,mapi,M);
1456 : }
1457 :
1458 : GEN
1459 3766 : algsplit(GEN al, long v)
1460 : {
1461 3766 : pari_sp av = avma;
1462 : GEN res, T, map, mapi, ff, p;
1463 : long i,j,k,li,lj;
1464 3766 : checkalg(al);
1465 3759 : p = alg_get_char(al);
1466 3759 : if (gequal0(p))
1467 7 : pari_err_IMPL("splitting a characteristic 0 algebra over its center");
1468 3752 : res = alg_finite_csa_split(al, v);
1469 3717 : T = gel(res,1);
1470 3717 : map = gel(res,2);
1471 3717 : mapi = gel(res,3);
1472 3717 : ff = Tp_to_FF(T,p);
1473 33593 : for (i=1; i<lg(map); i++)
1474 : {
1475 29876 : li = lg(gel(map,i));
1476 89908 : for (j=1; j<li; j++)
1477 : {
1478 60032 : lj = lg(gmael(map,i,j));
1479 190876 : for (k=1; k<lj; k++)
1480 130844 : gmael3(map,i,j,k) = Fq_to_FF(gmael3(map,i,j,k),ff);
1481 : }
1482 : }
1483 :
1484 3717 : return gerepilecopy(av, mkvec2(map,mapi));
1485 : }
1486 :
1487 : /* multiplication table sanity checks */
1488 : static GEN
1489 60916 : check_mt_noid(GEN mt, GEN p)
1490 : {
1491 : long i, l;
1492 60916 : GEN MT = cgetg_copy(mt, &l);
1493 60916 : if (typ(MT) != t_VEC || l == 1) return NULL;
1494 300553 : for (i = 1; i < l; i++)
1495 : {
1496 239686 : GEN M = gel(mt,i);
1497 239686 : if (typ(M) != t_MAT || lg(M) != l || lgcols(M) != l) return NULL;
1498 239658 : if (p) M = RgM_to_FpM(M,p);
1499 239658 : gel(MT,i) = M;
1500 : }
1501 60867 : return MT;
1502 : }
1503 : static GEN
1504 60391 : check_mt(GEN mt, GEN p)
1505 : {
1506 : long i;
1507 : GEN MT;
1508 60391 : MT = check_mt_noid(mt, p);
1509 60391 : if (!MT || !ZM_isidentity(gel(MT,1))) return NULL;
1510 236347 : for (i=2; i<lg(MT); i++)
1511 175984 : if (ZC_is_ei(gmael(MT,i,1)) != i) return NULL;
1512 60363 : return MT;
1513 : }
1514 :
1515 : static GEN
1516 294 : check_relmt(GEN nf, GEN mt)
1517 : {
1518 294 : long i, l = lg(mt), j, k;
1519 294 : GEN MT = gcopy(mt), a, b, d;
1520 294 : if (typ(MT) != t_VEC || l == 1) return NULL;
1521 1225 : for (i = 1; i < l; i++)
1522 : {
1523 952 : GEN M = gel(MT,i);
1524 952 : if (typ(M) != t_MAT || lg(M) != l || lgcols(M) != l) return NULL;
1525 4760 : for (k = 1; k < l; k++)
1526 21525 : for (j = 1; j < l; j++)
1527 : {
1528 17717 : a = gcoeff(M,j,k);
1529 17717 : if (typ(a)==t_INT) continue;
1530 2247 : b = algtobasis(nf,a);
1531 2247 : d = Q_denom(b);
1532 2247 : if (!isint1(d))
1533 14 : pari_err_DOMAIN("alg_csa_table", "denominator(mt)", "!=", gen_1, mt);
1534 2233 : gcoeff(M,j,k) = lift(basistoalg(nf,b));
1535 : }
1536 938 : if (i > 1 && RgC_is_ei(gel(M,1)) != i) return NULL; /* i = 1 checked at end */
1537 931 : gel(MT,i) = M;
1538 : }
1539 273 : if (!RgM_isidentity(gel(MT,1))) return NULL;
1540 273 : return MT;
1541 : }
1542 :
1543 : int
1544 532 : algisassociative(GEN mt0, GEN p)
1545 : {
1546 532 : pari_sp av = avma;
1547 : long i, j, k, n;
1548 : GEN M, mt;
1549 :
1550 532 : if (checkalg_i(mt0)) { p = alg_get_char(mt0); mt0 = alg_get_multable(mt0); }
1551 532 : if (!p) p = gen_0;
1552 532 : if (typ(p) != t_INT) pari_err_TYPE("algisassociative",p);
1553 525 : mt = check_mt_noid(mt0, isintzero(p)? NULL: p);
1554 525 : if (!mt) pari_err_TYPE("algisassociative (mult. table)", mt0);
1555 490 : if (!ZM_isidentity(gel(mt,1))) return gc_bool(av,0);
1556 476 : n = lg(mt)-1;
1557 476 : M = cgetg(n+1,t_MAT);
1558 3731 : for (j=1; j<=n; j++) gel(M,j) = cgetg(n+1,t_COL);
1559 3731 : for (i=1; i<=n; i++)
1560 : {
1561 3255 : GEN mi = gel(mt,i);
1562 36918 : for (j=1; j<=n; j++) gcoeff(M,i,j) = gel(mi,j); /* ei.ej */
1563 : }
1564 3241 : for (i=2; i<=n; i++) {
1565 2772 : GEN mi = gel(mt,i);
1566 30373 : for (j=2; j<=n; j++) {
1567 381451 : for (k=2; k<=n; k++) {
1568 : GEN x, y;
1569 353850 : if (signe(p)) {
1570 242039 : x = _tablemul_ej_Fp(mt,gcoeff(M,i,j),k,p);
1571 242039 : y = FpM_FpC_mul(mi,gcoeff(M,j,k),p);
1572 : }
1573 : else {
1574 111811 : x = _tablemul_ej(mt,gcoeff(M,i,j),k);
1575 111811 : y = RgM_RgC_mul(mi,gcoeff(M,j,k));
1576 : }
1577 : /* not cmp_universal: must not fail on 0 == Mod(0,2) for instance */
1578 353850 : if (!gequal(x,y)) return gc_bool(av,0);
1579 : }
1580 : }
1581 : }
1582 469 : return gc_bool(av,1);
1583 : }
1584 :
1585 : int
1586 392 : algiscommutative(GEN al) /* assumes e_1 = 1 */
1587 : {
1588 : long i,j,k,N,sp;
1589 : GEN mt,a,b,p;
1590 392 : checkalg(al);
1591 392 : if (alg_type(al) != al_TABLE) return alg_get_degree(al)==1;
1592 329 : N = alg_get_absdim(al);
1593 329 : mt = alg_get_multable(al);
1594 329 : p = alg_get_char(al);
1595 329 : sp = signe(p);
1596 1491 : for (i=2; i<=N; i++)
1597 9772 : for (j=2; j<=N; j++)
1598 89047 : for (k=1; k<=N; k++) {
1599 80514 : a = gcoeff(gel(mt,i),k,j);
1600 80514 : b = gcoeff(gel(mt,j),k,i);
1601 80514 : if (sp) {
1602 73423 : if (cmpii(Fp_red(a,p), Fp_red(b,p))) return 0;
1603 : }
1604 7091 : else if (gcmp(a,b)) return 0;
1605 : }
1606 252 : return 1;
1607 : }
1608 :
1609 : int
1610 392 : algissemisimple(GEN al)
1611 : {
1612 392 : pari_sp av = avma;
1613 : GEN rad;
1614 392 : checkalg(al);
1615 392 : if (alg_type(al) != al_TABLE) return 1;
1616 329 : rad = algradical(al);
1617 329 : set_avma(av);
1618 329 : return gequal0(rad);
1619 : }
1620 :
1621 : /* ss : known to be semisimple */
1622 : int
1623 301 : algissimple(GEN al, long ss)
1624 : {
1625 301 : pari_sp av = avma;
1626 : GEN Z, dec, p;
1627 301 : checkalg(al);
1628 301 : if (alg_type(al) != al_TABLE) return 1;
1629 245 : if (!ss && !algissemisimple(al)) return 0;
1630 :
1631 203 : p = alg_get_char(al);
1632 203 : if (signe(p)) Z = algprimesubalg(al);
1633 112 : else Z = algtablecenter(al);
1634 :
1635 203 : if (lg(Z) == 2) {/* dim Z = 1 */
1636 112 : set_avma(av);
1637 112 : return 1;
1638 : }
1639 91 : dec = alg_decompose(al, Z, 1, NULL);
1640 91 : set_avma(av);
1641 91 : return gequal0(dec);
1642 : }
1643 :
1644 : static long
1645 546 : is_place_emb(GEN nf, GEN pl)
1646 : {
1647 : long r, r1, r2;
1648 546 : if (typ(pl) != t_INT) pari_err_TYPE("is_place_emb", pl);
1649 525 : if (signe(pl)<=0) pari_err_DOMAIN("is_place_emb", "pl", "<=", gen_0, pl);
1650 518 : nf_get_sign(nf,&r1,&r2); r = r1+r2;
1651 518 : if (cmpiu(pl,r)>0) pari_err_DOMAIN("is_place_emb", "pl", ">", utoi(r), pl);
1652 497 : return itou(pl);
1653 : }
1654 :
1655 : static long
1656 497 : alghasse_emb(GEN al, long emb)
1657 : {
1658 497 : GEN nf = alg_get_center(al);
1659 497 : long r1 = nf_get_r1(nf);
1660 497 : return (emb <= r1)? alg_get_hasse_i(al)[emb]: 0;
1661 : }
1662 :
1663 : static long
1664 1799 : alghasse_pr(GEN al, GEN pr)
1665 : {
1666 1799 : GEN hf = alg_get_hasse_f(al);
1667 1792 : long i = tablesearch(gel(hf,1), pr, &cmp_prime_ideal);
1668 1792 : return i? gel(hf,2)[i]: 0;
1669 : }
1670 :
1671 : static long
1672 2380 : alghasse_0(GEN al, GEN pl)
1673 : {
1674 : long ta;
1675 : GEN pr, nf;
1676 2380 : ta = alg_type(al);
1677 2380 : if (ta == al_REAL) return algreal_dim(al)!=1;
1678 2359 : if (!pl)
1679 7 : pari_err(e_MISC, "must provide a place pl");
1680 2352 : if (ta == al_CSA && !alg_is_asq(al))
1681 7 : pari_err_IMPL("computation of Hasse invariants over table CSA");
1682 2345 : if ((pr = get_prid(pl))) return alghasse_pr(al, pr);
1683 546 : nf = alg_get_center(al);
1684 546 : return alghasse_emb(al, is_place_emb(nf, pl));
1685 : }
1686 : GEN
1687 567 : alghasse(GEN al, GEN pl)
1688 : {
1689 : long h;
1690 567 : checkalg(al);
1691 567 : if (alg_type(al) == al_TABLE) pari_err_TYPE("alghasse [use alginit]",al);
1692 560 : h = alghasse_0(al,pl);
1693 490 : return sstoQ(h, alg_get_degree(al));
1694 : }
1695 :
1696 : /* h >= 0, d >= 0 */
1697 : static long
1698 2219 : indexfromhasse(long h, long d) { return d/ugcd(h,d); }
1699 :
1700 : long
1701 2191 : algindex(GEN al, GEN pl)
1702 : {
1703 : long d, res, i, l, ta;
1704 : GEN hi, hf;
1705 :
1706 2191 : checkalg(al);
1707 2184 : ta = alg_type(al);
1708 2184 : if (ta == al_TABLE) pari_err_TYPE("algindex [use alginit]",al);
1709 2177 : if (ta == al_REAL) return algreal_dim(al)==1 ? 1 : 2;
1710 2093 : d = alg_get_degree(al);
1711 2093 : if (pl) return indexfromhasse(alghasse_0(al,pl), d);
1712 :
1713 : /* else : global index */
1714 273 : res = 1;
1715 273 : hi = alg_get_hasse_i(al); l = lg(hi);
1716 518 : for (i=1; i<l && res!=d; i++) res = ulcm(res, indexfromhasse(hi[i],d));
1717 273 : hf = gel(alg_get_hasse_f(al), 2); l = lg(hf);
1718 420 : for (i=1; i<l && res!=d; i++) res = ulcm(res, indexfromhasse(hf[i],d));
1719 266 : return res;
1720 : }
1721 :
1722 : int
1723 287 : algisdivision(GEN al, GEN pl)
1724 : {
1725 287 : checkalg(al);
1726 287 : if (alg_type(al) == al_TABLE) {
1727 21 : if (!algissimple(al,0)) return 0;
1728 14 : if (algiscommutative(al)) return 1;
1729 7 : pari_err_IMPL("algisdivision for table algebras");
1730 : }
1731 266 : return algindex(al,pl) == alg_get_degree(al);
1732 : }
1733 :
1734 : int
1735 1652 : algissplit(GEN al, GEN pl)
1736 : {
1737 1652 : checkalg(al);
1738 1652 : if (alg_type(al) == al_TABLE) pari_err_TYPE("algissplit [use alginit]", al);
1739 1638 : return algindex(al,pl) == 1;
1740 : }
1741 :
1742 : int
1743 1386 : algisramified(GEN al, GEN pl) { return !algissplit(al,pl); }
1744 :
1745 : /* sorted; infinite places first */
1746 : GEN
1747 364 : algramifiedplaces(GEN al)
1748 : {
1749 364 : pari_sp av = avma;
1750 : GEN ram, hf, hi, Lpr;
1751 : long r1, count, i, ta;
1752 364 : checkalg(al);
1753 364 : ta = alg_type(al);
1754 364 : if (ta != al_CSA && ta != al_CYCLIC)
1755 14 : pari_err_TYPE("algramifiedplaces [not a central simple algebra"
1756 : " over a number field]", al);
1757 350 : r1 = nf_get_r1(alg_get_center(al));
1758 350 : hi = alg_get_hasse_i(al);
1759 336 : hf = alg_get_hasse_f(al);
1760 322 : Lpr = gel(hf,1);
1761 322 : hf = gel(hf,2);
1762 322 : ram = cgetg(r1+lg(Lpr), t_VEC);
1763 322 : count = 0;
1764 889 : for (i=1; i<=r1; i++)
1765 567 : if (hi[i]) {
1766 224 : count++;
1767 224 : gel(ram,count) = stoi(i);
1768 : }
1769 1084 : for (i=1; i<lg(Lpr); i++)
1770 762 : if (hf[i]) {
1771 322 : count++;
1772 322 : gel(ram,count) = gel(Lpr,i);
1773 : }
1774 322 : setlg(ram, count+1);
1775 322 : return gerepilecopy(av, ram);
1776 : }
1777 :
1778 : /* no GC */
1779 : static int
1780 154 : algissimilar_i(GEN al, GEN al2, GEN pl)
1781 : {
1782 : GEN ram, ram2;
1783 : long i;
1784 154 : if (pl) return gequal(alghasse(al,pl), alghasse(al2,pl));
1785 84 : ram = algramifiedplaces(al);
1786 63 : ram2 = algramifiedplaces(al2);
1787 63 : if(!gequal(ram, ram2)) return 0;
1788 84 : for (i=1; i<lg(ram); i++)
1789 49 : if (!gequal(alghasse(al,gel(ram,i)), alghasse(al2,gel(ram,i))))
1790 7 : return 0;
1791 35 : return 1;
1792 : }
1793 :
1794 : int
1795 245 : algisisom(GEN al, GEN al2, GEN pl)
1796 : {
1797 245 : pari_sp av = avma;
1798 : long t, d;
1799 245 : checkalg(al);
1800 238 : checkalg(al2);
1801 231 : t = alg_type(al);
1802 231 : if (t != al_CYCLIC && t != al_CSA)
1803 14 : pari_err_TYPE("algisisom [al: apply alginit()]", al);
1804 217 : t = alg_type(al2);
1805 217 : if (t != al_CYCLIC && t != al_CSA)
1806 14 : pari_err_TYPE("algisisom [al2: apply alginit()]", al2);
1807 203 : if (!gequal(nf_get_pol(alg_get_center(al)), nf_get_pol(alg_get_center(al2))))
1808 7 : pari_err(e_MISC, "base fields must be identical in algisisom");
1809 196 : d = alg_get_degree(al);
1810 196 : if (d != alg_get_degree(al2)) return gc_int(av, 0);
1811 189 : if (d == 1) return gc_int(av, 1);
1812 154 : return gc_int(av, algissimilar_i(al,al2,pl));
1813 : }
1814 :
1815 : GEN
1816 84 : algnewprec_shallow(GEN al, long prec)
1817 : {
1818 : GEN al2;
1819 84 : long t = algtype(al);
1820 84 : if (t != al_CYCLIC && t != al_CSA) return al;
1821 56 : al2 = shallowcopy(al);
1822 56 : gel(al2,1) = rnfnewprec_shallow(gel(al2,1), prec);
1823 56 : return al2;
1824 : };
1825 :
1826 : GEN
1827 84 : algnewprec(GEN al, long prec)
1828 : {
1829 84 : pari_sp av = avma;
1830 84 : GEN al2 = algnewprec_shallow(al, prec);
1831 84 : return gerepilecopy(av, al2);
1832 : }
1833 :
1834 : /** OPERATIONS ON ELEMENTS operations.c **/
1835 :
1836 : static long
1837 1898306 : alg_model0(GEN al, GEN x)
1838 : {
1839 1898306 : long t, N = alg_get_absdim(al), lx = lg(x), d, n, D, i;
1840 1898306 : if (typ(x) == t_MAT) return al_MATRIX;
1841 1852183 : if (typ(x) != t_COL) return al_INVALID;
1842 1852106 : if (N == 1) {
1843 7679 : if (lx != 2) return al_INVALID;
1844 7658 : switch(typ(gel(x,1)))
1845 : {
1846 4928 : case t_INT: case t_FRAC: return al_TRIVIAL; /* cannot distinguish basis and alg from size */
1847 2723 : case t_POL: case t_POLMOD: return al_ALGEBRAIC;
1848 7 : default: return al_INVALID;
1849 : }
1850 : }
1851 :
1852 1844427 : switch(alg_type(al)) {
1853 735373 : case al_TABLE:
1854 735373 : if (lx != N+1) return al_INVALID;
1855 735352 : return al_BASIS;
1856 942741 : case al_CYCLIC:
1857 942741 : d = alg_get_degree(al);
1858 942741 : if (lx == N+1) return al_BASIS;
1859 113822 : if (lx == d+1) return al_ALGEBRAIC;
1860 49 : return al_INVALID;
1861 166313 : case al_CSA:
1862 166313 : D = alg_get_dim(al);
1863 166313 : n = nf_get_degree(alg_get_center(al));
1864 166313 : if (n == 1) {
1865 22652 : if (lx != D+1) return al_INVALID;
1866 104433 : for (i=1; i<=D; i++) {
1867 84007 : t = typ(gel(x,i));
1868 84007 : if (t == t_POL || t == t_POLMOD) return al_ALGEBRAIC;
1869 : /* TODO t_COL for coefficients in basis form ? */
1870 : }
1871 20426 : return al_BASIS;
1872 : }
1873 : else {
1874 143661 : if (lx == N+1) return al_BASIS;
1875 25795 : if (lx == D+1) return al_ALGEBRAIC;
1876 7 : return al_INVALID;
1877 : }
1878 : }
1879 : return al_INVALID; /* LCOV_EXCL_LINE */
1880 : }
1881 :
1882 : static void
1883 1898117 : checkalgx(GEN x, long model)
1884 : {
1885 : long t, i;
1886 1898117 : switch(model) {
1887 1702563 : case al_BASIS:
1888 22658105 : for (i=1; i<lg(x); i++) {
1889 20955549 : t = typ(gel(x,i));
1890 20955549 : if (t != t_INT && t != t_FRAC)
1891 7 : pari_err_TYPE("checkalgx", gel(x,i));
1892 : }
1893 1702556 : return;
1894 149431 : case al_TRIVIAL:
1895 : case al_ALGEBRAIC:
1896 505564 : for (i=1; i<lg(x); i++) {
1897 356140 : t = typ(gel(x,i));
1898 356140 : if (t != t_INT && t != t_FRAC && t != t_POL && t != t_POLMOD)
1899 : /* TODO t_COL ? */
1900 7 : pari_err_TYPE("checkalgx", gel(x,i));
1901 : }
1902 149424 : return;
1903 : }
1904 : }
1905 :
1906 : long
1907 1898306 : alg_model(GEN al, GEN x)
1908 : {
1909 1898306 : long res = alg_model0(al, x);
1910 1898306 : if (res == al_INVALID) pari_err_TYPE("alg_model", x);
1911 1898117 : checkalgx(x, res); return res;
1912 : }
1913 :
1914 : static long
1915 462910 : H_model0(GEN x)
1916 : {
1917 : long i;
1918 462910 : switch(typ(x))
1919 : {
1920 15274 : case t_INT:
1921 : case t_FRAC:
1922 : case t_REAL:
1923 : case t_COMPLEX:
1924 15274 : return H_SCALAR;
1925 10157 : case t_MAT:
1926 10157 : return H_MATRIX;
1927 437367 : case t_COL:
1928 437367 : if (lg(x)!=5) return H_INVALID;
1929 2186688 : for (i=1; i<=4; i++) if (!is_real_t(typ(gel(x,i)))) return H_INVALID;
1930 437332 : return H_QUATERNION;
1931 112 : default:
1932 112 : return al_INVALID;
1933 : }
1934 : }
1935 :
1936 : static long
1937 462910 : H_model(GEN x)
1938 : {
1939 462910 : long res = H_model0(x);
1940 462910 : if (res == H_INVALID) pari_err_TYPE("H_model", x);
1941 462763 : return res;
1942 : }
1943 :
1944 : static GEN
1945 756 : alC_add_i(GEN al, GEN x, GEN y, long lx)
1946 : {
1947 756 : GEN A = cgetg(lx, t_COL);
1948 : long i;
1949 2296 : for (i=1; i<lx; i++) gel(A,i) = algadd(al, gel(x,i), gel(y,i));
1950 749 : return A;
1951 : }
1952 : static GEN
1953 406 : alM_add(GEN al, GEN x, GEN y)
1954 : {
1955 406 : long lx = lg(x), l, j;
1956 : GEN z;
1957 406 : if (lg(y) != lx) pari_err_DIM("alM_add (rows)");
1958 392 : if (lx == 1) return cgetg(1, t_MAT);
1959 385 : z = cgetg(lx, t_MAT); l = lgcols(x);
1960 385 : if (lgcols(y) != l) pari_err_DIM("alM_add (columns)");
1961 1127 : for (j = 1; j < lx; j++) gel(z,j) = alC_add_i(al, gel(x,j), gel(y,j), l);
1962 371 : return z;
1963 : }
1964 : static GEN
1965 17745 : H_add(GEN x, GEN y)
1966 : {
1967 17745 : long tx = H_model(x), ty = H_model(y);
1968 17724 : if ((tx==H_MATRIX) ^ (ty==H_MATRIX)) pari_err_TYPE2("H_add", x, y);
1969 17710 : if (tx>ty) { swap(x,y); lswap(tx,ty); }
1970 17710 : switch (tx)
1971 : {
1972 105 : case H_MATRIX: /* both H_MATRIX */ return alM_add(NULL, x, y);
1973 16681 : case H_QUATERNION: /* both H_QUATERNION */ return gadd(x,y);
1974 924 : case H_SCALAR:
1975 924 : if (ty == H_SCALAR) return gadd(x,y);
1976 : else /* ty == H_QUATERNION */
1977 : {
1978 217 : pari_sp av = avma;
1979 217 : GEN res = gcopy(y), im;
1980 217 : gel(res,1) = gadd(gel(res,1), real_i(x));
1981 217 : im = imag_i(x);
1982 217 : if (im != gen_0) gel(res,2) = gadd(gel(res,2), im);
1983 217 : return gerepileupto(av, res);
1984 : }
1985 : }
1986 : return NULL; /*LCOV_EXCL_LINE*/
1987 : }
1988 : GEN
1989 54999 : algadd(GEN al, GEN x, GEN y)
1990 : {
1991 54999 : pari_sp av = avma;
1992 : long tx, ty;
1993 : GEN p;
1994 54999 : checkalg(al);
1995 54999 : if (alg_type(al)==al_REAL) return H_add(x,y);
1996 37254 : tx = alg_model(al,x);
1997 37247 : ty = alg_model(al,y);
1998 37247 : p = alg_get_char(al);
1999 37247 : if (signe(p)) return FpC_add(x,y,p);
2000 37114 : if (tx==ty) {
2001 36232 : if (tx!=al_MATRIX) return gadd(x,y);
2002 301 : return gerepilecopy(av, alM_add(al,x,y));
2003 : }
2004 882 : if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);
2005 882 : if (ty==al_ALGEBRAIC) y = algalgtobasis(al,y);
2006 882 : return gerepileupto(av, gadd(x,y));
2007 : }
2008 :
2009 : static GEN
2010 98 : H_neg(GEN x)
2011 : {
2012 98 : (void)H_model(x);
2013 70 : return gneg(x);
2014 : }
2015 :
2016 : GEN
2017 245 : algneg(GEN al, GEN x)
2018 : {
2019 245 : checkalg(al);
2020 245 : if (alg_type(al)==al_REAL) return H_neg(x);
2021 147 : (void)alg_model(al,x);
2022 140 : return gneg(x);
2023 : }
2024 :
2025 : static GEN
2026 210 : alC_sub_i(GEN al, GEN x, GEN y, long lx)
2027 : {
2028 : long i;
2029 210 : GEN A = cgetg(lx, t_COL);
2030 630 : for (i=1; i<lx; i++) gel(A,i) = algsub(al, gel(x,i), gel(y,i));
2031 210 : return A;
2032 : }
2033 : static GEN
2034 126 : alM_sub(GEN al, GEN x, GEN y)
2035 : {
2036 126 : long lx = lg(x), l, j;
2037 : GEN z;
2038 126 : if (lg(y) != lx) pari_err_DIM("alM_sub (rows)");
2039 119 : if (lx == 1) return cgetg(1, t_MAT);
2040 112 : z = cgetg(lx, t_MAT); l = lgcols(x);
2041 112 : if (lgcols(y) != l) pari_err_DIM("alM_sub (columns)");
2042 315 : for (j = 1; j < lx; j++) gel(z,j) = alC_sub_i(al, gel(x,j), gel(y,j), l);
2043 105 : return z;
2044 : }
2045 : GEN
2046 1127 : algsub(GEN al, GEN x, GEN y)
2047 : {
2048 : long tx, ty;
2049 1127 : pari_sp av = avma;
2050 : GEN p;
2051 1127 : checkalg(al);
2052 1127 : if (alg_type(al)==al_REAL) return gerepileupto(av, algadd(NULL,x,gneg(y)));
2053 973 : tx = alg_model(al,x);
2054 966 : ty = alg_model(al,y);
2055 966 : p = alg_get_char(al);
2056 966 : if (signe(p)) return FpC_sub(x,y,p);
2057 875 : if (tx==ty) {
2058 553 : if (tx != al_MATRIX) return gsub(x,y);
2059 126 : return gerepilecopy(av, alM_sub(al,x,y));
2060 : }
2061 322 : if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);
2062 322 : if (ty==al_ALGEBRAIC) y = algalgtobasis(al,y);
2063 322 : return gerepileupto(av, gsub(x,y));
2064 : }
2065 :
2066 : static GEN
2067 1659 : algalgmul_cyc(GEN al, GEN x, GEN y)
2068 : {
2069 1659 : pari_sp av = avma;
2070 1659 : long n = alg_get_degree(al), i, k;
2071 : GEN xalg, yalg, res, rnf, auts, sum, b, prod, autx;
2072 1659 : rnf = alg_get_splittingfield(al);
2073 1659 : auts = alg_get_auts(al);
2074 1659 : b = alg_get_b(al);
2075 :
2076 1659 : xalg = cgetg(n+1, t_COL);
2077 4935 : for (i=0; i<n; i++)
2078 3276 : gel(xalg,i+1) = lift_shallow(rnfbasistoalg(rnf,gel(x,i+1)));
2079 :
2080 1659 : yalg = cgetg(n+1, t_COL);
2081 4935 : for (i=0; i<n; i++) gel(yalg,i+1) = rnfbasistoalg(rnf,gel(y,i+1));
2082 :
2083 1659 : res = cgetg(n+1,t_COL);
2084 4935 : for (k=0; k<n; k++) {
2085 3276 : gel(res,k+1) = gmul(gel(xalg,k+1),gel(yalg,1));
2086 5166 : for (i=1; i<=k; i++) {
2087 1890 : autx = poleval(gel(xalg,k-i+1),gel(auts,i));
2088 1890 : prod = gmul(autx,gel(yalg,i+1));
2089 1890 : gel(res,k+1) = gadd(gel(res,k+1), prod);
2090 : }
2091 :
2092 3276 : sum = gen_0;
2093 5166 : for (; i<n; i++) {
2094 1890 : autx = poleval(gel(xalg,k+n-i+1),gel(auts,i));
2095 1890 : prod = gmul(autx,gel(yalg,i+1));
2096 1890 : sum = gadd(sum,prod);
2097 : }
2098 3276 : sum = gmul(b,sum);
2099 :
2100 3276 : gel(res,k+1) = gadd(gel(res,k+1),sum);
2101 : }
2102 :
2103 1659 : return gerepilecopy(av, res);
2104 : }
2105 :
2106 : static GEN
2107 521822 : _tablemul(GEN mt, GEN x, GEN y)
2108 : {
2109 521822 : pari_sp av = avma;
2110 521822 : long D = lg(mt)-1, i;
2111 521822 : GEN res = NULL;
2112 8016946 : for (i=1; i<=D; i++) {
2113 7495124 : GEN c = gel(x,i);
2114 7495124 : if (!gequal0(c)) {
2115 1755676 : GEN My = RgM_RgC_mul(gel(mt,i),y);
2116 1755676 : GEN t = RgC_Rg_mul(My,c);
2117 1755676 : res = res? RgC_add(res,t): t;
2118 : }
2119 : }
2120 521822 : if (!res) { set_avma(av); return zerocol(D); }
2121 520912 : return gerepileupto(av, res);
2122 : }
2123 :
2124 : static GEN
2125 300428 : _tablemul_Fp(GEN mt, GEN x, GEN y, GEN p)
2126 : {
2127 300428 : pari_sp av = avma;
2128 300428 : long D = lg(mt)-1, i;
2129 300428 : GEN res = NULL;
2130 2869101 : for (i=1; i<=D; i++) {
2131 2568673 : GEN c = gel(x,i);
2132 2568673 : if (signe(c)) {
2133 533353 : GEN My = FpM_FpC_mul(gel(mt,i),y,p);
2134 533353 : GEN t = FpC_Fp_mul(My,c,p);
2135 533353 : res = res? FpC_add(res,t,p): t;
2136 : }
2137 : }
2138 300428 : if (!res) { set_avma(av); return zerocol(D); }
2139 299889 : return gerepileupto(av, res);
2140 : }
2141 :
2142 : /* x*ej */
2143 : static GEN
2144 111811 : _tablemul_ej(GEN mt, GEN x, long j)
2145 : {
2146 111811 : pari_sp av = avma;
2147 111811 : long D = lg(mt)-1, i;
2148 111811 : GEN res = NULL;
2149 1707468 : for (i=1; i<=D; i++) {
2150 1595657 : GEN c = gel(x,i);
2151 1595657 : if (!gequal0(c)) {
2152 162302 : GEN My = gel(gel(mt,i),j);
2153 162302 : GEN t = RgC_Rg_mul(My,c);
2154 162302 : res = res? RgC_add(res,t): t;
2155 : }
2156 : }
2157 111811 : if (!res) { set_avma(av); return zerocol(D); }
2158 111629 : return gerepileupto(av, res);
2159 : }
2160 : static GEN
2161 242039 : _tablemul_ej_Fp(GEN mt, GEN x, long j, GEN p)
2162 : {
2163 242039 : pari_sp av = avma;
2164 242039 : long D = lg(mt)-1, i;
2165 242039 : GEN res = NULL;
2166 4364787 : for (i=1; i<=D; i++) {
2167 4122748 : GEN c = gel(x,i);
2168 4122748 : if (!gequal0(c)) {
2169 289954 : GEN My = gel(gel(mt,i),j);
2170 289954 : GEN t = FpC_Fp_mul(My,c,p);
2171 289954 : res = res? FpC_add(res,t,p): t;
2172 : }
2173 : }
2174 242039 : if (!res) { set_avma(av); return zerocol(D); }
2175 241927 : return gerepileupto(av, res);
2176 : }
2177 :
2178 : static GEN
2179 619723 : _tablemul_ej_Fl(GEN mt, GEN x, long j, ulong p)
2180 : {
2181 619723 : pari_sp av = avma;
2182 619723 : long D = lg(mt)-1, i;
2183 619723 : GEN res = NULL;
2184 13815540 : for (i=1; i<=D; i++) {
2185 13195817 : ulong c = x[i];
2186 13195817 : if (c) {
2187 1274519 : GEN My = gel(gel(mt,i),j);
2188 1274519 : GEN t = Flv_Fl_mul(My,c, p);
2189 1274519 : res = res? Flv_add(res,t, p): t;
2190 : }
2191 : }
2192 619723 : if (!res) { set_avma(av); return zero_Flv(D); }
2193 619723 : return gerepileupto(av, res);
2194 : }
2195 :
2196 : static GEN
2197 686 : algalgmul_csa(GEN al, GEN x, GEN y)
2198 : {
2199 686 : GEN z, nf = alg_get_center(al);
2200 : long i;
2201 686 : z = _tablemul(alg_get_relmultable(al), x, y);
2202 2485 : for (i=1; i<lg(z); i++)
2203 1799 : gel(z,i) = basistoalg(nf,gel(z,i));
2204 686 : return z;
2205 : }
2206 :
2207 : /* assumes x and y in algebraic form */
2208 : static GEN
2209 2345 : algalgmul(GEN al, GEN x, GEN y)
2210 : {
2211 2345 : switch(alg_type(al))
2212 : {
2213 1659 : case al_CYCLIC: return algalgmul_cyc(al, x, y);
2214 686 : case al_CSA: return algalgmul_csa(al, x, y);
2215 : }
2216 : return NULL; /*LCOV_EXCL_LINE*/
2217 : }
2218 :
2219 : static GEN
2220 821564 : algbasismul(GEN al, GEN x, GEN y)
2221 : {
2222 821564 : GEN mt = alg_get_multable(al), p = alg_get_char(al);
2223 821564 : if (signe(p)) return _tablemul_Fp(mt, x, y, p);
2224 521136 : return _tablemul(mt, x, y);
2225 : }
2226 :
2227 : /* x[i,]*y. Assume lg(x) > 1 and 0 < i < lgcols(x) */
2228 : static GEN
2229 119651 : alMrow_alC_mul_i(GEN al, GEN x, GEN y, long i, long lx)
2230 : {
2231 119651 : pari_sp av = avma;
2232 119651 : GEN c = algmul(al,gcoeff(x,i,1),gel(y,1)), ZERO;
2233 : long k;
2234 119651 : ZERO = zerocol(alg_get_absdim(al));
2235 273308 : for (k = 2; k < lx; k++)
2236 : {
2237 153657 : GEN t = algmul(al, gcoeff(x,i,k), gel(y,k));
2238 153657 : if (!gequal(t,ZERO)) c = algadd(al, c, t);
2239 : }
2240 119651 : return gerepilecopy(av, c);
2241 : }
2242 : /* return x * y, 1 < lx = lg(x), l = lgcols(x) */
2243 : static GEN
2244 54502 : alM_alC_mul_i(GEN al, GEN x, GEN y, long lx, long l)
2245 : {
2246 54502 : GEN z = cgetg(l,t_COL);
2247 : long i;
2248 174153 : for (i=1; i<l; i++) gel(z,i) = alMrow_alC_mul_i(al,x,y,i,lx);
2249 54502 : return z;
2250 : }
2251 : static GEN
2252 25627 : alM_mul(GEN al, GEN x, GEN y)
2253 : {
2254 25627 : long j, l, lx=lg(x), ly=lg(y);
2255 : GEN z;
2256 25627 : if (ly==1) return cgetg(1,t_MAT);
2257 25529 : if (lx != lgcols(y)) pari_err_DIM("alM_mul");
2258 25508 : if (lx==1) return zeromat(0, ly-1);
2259 25501 : l = lgcols(x); z = cgetg(ly,t_MAT);
2260 80003 : for (j=1; j<ly; j++) gel(z,j) = alM_alC_mul_i(al,x,gel(y,j),lx,l);
2261 25501 : return z;
2262 : }
2263 :
2264 : static void
2265 205639 : H_compo(GEN x, GEN* a, GEN* b, GEN* c, GEN* d)
2266 : {
2267 205639 : switch(H_model(x))
2268 : {
2269 5173 : case H_SCALAR:
2270 5173 : *a = real_i(x);
2271 5173 : *b = imag_i(x);
2272 5173 : *c = gen_0;
2273 5173 : *d = gen_0;
2274 5173 : return;
2275 200466 : case H_QUATERNION:
2276 200466 : *a = gel(x,1);
2277 200466 : *b = gel(x,2);
2278 200466 : *c = gel(x,3);
2279 200466 : *d = gel(x,4);
2280 200466 : return;
2281 : default: *a = *b = *c = *d = NULL; return; /*LCOV_EXCL_LINE*/
2282 : }
2283 : }
2284 : static GEN
2285 108129 : H_mul(GEN x, GEN y)
2286 : {
2287 108129 : pari_sp av = avma;
2288 : GEN a,b,c,d,u,v,w,z;
2289 108129 : long tx = H_model(x), ty = H_model(y);
2290 108115 : if ((tx==H_MATRIX) ^ (ty==H_MATRIX)) pari_err_TYPE2("H_mul", x, y);
2291 108108 : if (tx == H_MATRIX) /* both H_MATRIX */ return alM_mul(NULL, x, y);
2292 103817 : if (tx == H_SCALAR && ty == H_SCALAR) return gmul(x,y);
2293 102620 : H_compo(x,&a,&b,&c,&d);
2294 102620 : H_compo(y,&u,&v,&w,&z);
2295 102620 : return gerepilecopy(av,mkcol4(
2296 : gsub(gmul(a,u), gadd(gadd(gmul(b,v),gmul(c,w)),gmul(d,z))),
2297 : gsub(gadd(gmul(a,v),gadd(gmul(b,u),gmul(c,z))), gmul(d,w)),
2298 : gsub(gadd(gmul(a,w),gadd(gmul(c,u),gmul(d,v))), gmul(b,z)),
2299 : gsub(gadd(gmul(a,z),gadd(gmul(b,w),gmul(d,u))), gmul(c,v))
2300 : ));
2301 : }
2302 :
2303 : GEN
2304 821832 : algmul(GEN al, GEN x, GEN y)
2305 : {
2306 821832 : pari_sp av = avma;
2307 : long tx, ty;
2308 821832 : checkalg(al);
2309 821832 : if (alg_type(al)==al_REAL) return H_mul(x,y);
2310 713983 : tx = alg_model(al,x);
2311 713969 : ty = alg_model(al,y);
2312 713969 : if (tx==al_MATRIX) {
2313 20832 : if (ty==al_MATRIX) return alM_mul(al,x,y);
2314 7 : pari_err_TYPE("algmul", y);
2315 : }
2316 693137 : if (signe(alg_get_char(al))) return algbasismul(al,x,y);
2317 521150 : if (tx==al_TRIVIAL) retmkcol(gmul(gel(x,1),gel(y,1)));
2318 520450 : if (tx==al_ALGEBRAIC && ty==al_ALGEBRAIC) return algalgmul(al,x,y);
2319 518924 : if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);
2320 518924 : if (ty==al_ALGEBRAIC) y = algalgtobasis(al,y);
2321 518924 : return gerepileupto(av,algbasismul(al,x,y));
2322 : }
2323 :
2324 : static GEN
2325 329 : H_sqr(GEN x)
2326 : {
2327 329 : pari_sp av = avma;
2328 329 : long tx = H_model(x);
2329 : GEN a,b,c,d;
2330 308 : if (tx == H_SCALAR) return gsqr(x);
2331 224 : if (tx == H_MATRIX) return H_mul(x,x);
2332 119 : H_compo(x,&a,&b,&c,&d);
2333 119 : return gerepilecopy(av, mkcol4(
2334 : gsub(gsqr(a), gadd(gsqr(b),gadd(gsqr(c),gsqr(d)))),
2335 : gshift(gmul(a,b),1),
2336 : gshift(gmul(a,c),1),
2337 : gshift(gmul(a,d),1)
2338 : ));
2339 : }
2340 :
2341 : GEN
2342 127727 : algsqr(GEN al, GEN x)
2343 : {
2344 127727 : pari_sp av = avma;
2345 : long tx;
2346 127727 : checkalg(al);
2347 127692 : if (alg_type(al)==al_REAL) return H_sqr(x);
2348 127363 : tx = alg_model(al,x);
2349 127293 : if (tx==al_MATRIX) return gerepilecopy(av,alM_mul(al,x,x));
2350 126782 : if (signe(alg_get_char(al))) return algbasismul(al,x,x);
2351 3381 : if (tx==al_TRIVIAL) retmkcol(gsqr(gel(x,1)));
2352 3031 : if (tx==al_ALGEBRAIC) return algalgmul(al,x,x);
2353 2212 : return gerepileupto(av,algbasismul(al,x,x));
2354 : }
2355 :
2356 : static GEN
2357 14399 : algmtK2Z_cyc(GEN al, GEN m)
2358 : {
2359 14399 : pari_sp av = avma;
2360 14399 : GEN nf = alg_get_abssplitting(al), res, mt, rnf = alg_get_splittingfield(al), c, dc;
2361 14399 : long n = alg_get_degree(al), N = nf_get_degree(nf), Nn, i, j, i1, j1;
2362 14399 : Nn = N*n;
2363 14399 : res = zeromatcopy(Nn,Nn);
2364 60312 : for (i=0; i<n; i++)
2365 247380 : for (j=0; j<n; j++) {
2366 201467 : c = gcoeff(m,i+1,j+1);
2367 201467 : if (!gequal0(c)) {
2368 45913 : c = rnfeltreltoabs(rnf,c);
2369 45913 : c = algtobasis(nf,c);
2370 45913 : c = Q_remove_denom(c,&dc);
2371 45913 : mt = zk_multable(nf,c);
2372 45913 : if (dc) mt = ZM_Z_div(mt,dc);
2373 384860 : for (i1=1; i1<=N; i1++)
2374 3464636 : for (j1=1; j1<=N; j1++)
2375 3125689 : gcoeff(res,i*N+i1,j*N+j1) = gcoeff(mt,i1,j1);
2376 : }
2377 : }
2378 14399 : return gerepilecopy(av,res);
2379 : }
2380 :
2381 : static GEN
2382 1687 : algmtK2Z_csa(GEN al, GEN m)
2383 : {
2384 1687 : pari_sp av = avma;
2385 1687 : GEN nf = alg_get_center(al), res, mt, c, dc;
2386 1687 : long d2 = alg_get_dim(al), n = nf_get_degree(nf), D, i, j, i1, j1;
2387 1687 : D = d2*n;
2388 1687 : res = zeromatcopy(D,D);
2389 9086 : for (i=0; i<d2; i++)
2390 45206 : for (j=0; j<d2; j++) {
2391 37807 : c = gcoeff(m,i+1,j+1);
2392 37807 : if (!gequal0(c)) {
2393 7035 : c = algtobasis(nf,c);
2394 7035 : c = Q_remove_denom(c,&dc);
2395 7035 : mt = zk_multable(nf,c);
2396 7035 : if (dc) mt = ZM_Z_div(mt,dc);
2397 22064 : for (i1=1; i1<=n; i1++)
2398 50414 : for (j1=1; j1<=n; j1++)
2399 35385 : gcoeff(res,i*n+i1,j*n+j1) = gcoeff(mt,i1,j1);
2400 : }
2401 : }
2402 1687 : return gerepilecopy(av,res);
2403 : }
2404 :
2405 : /* assumes al is a CSA or CYCLIC */
2406 : static GEN
2407 16086 : algmtK2Z(GEN al, GEN m)
2408 : {
2409 16086 : switch(alg_type(al))
2410 : {
2411 14399 : case al_CYCLIC: return algmtK2Z_cyc(al, m);
2412 1687 : case al_CSA: return algmtK2Z_csa(al, m);
2413 : }
2414 : return NULL; /*LCOV_EXCL_LINE*/
2415 : }
2416 :
2417 : /* left multiplication table, as a vector space of dimension n over the splitting field (by right multiplication) */
2418 : static GEN
2419 17164 : algalgmultable_cyc(GEN al, GEN x)
2420 : {
2421 17164 : pari_sp av = avma;
2422 17164 : long n = alg_get_degree(al), i, j;
2423 : GEN res, rnf, auts, b, pol;
2424 17164 : rnf = alg_get_splittingfield(al);
2425 17164 : auts = alg_get_auts(al);
2426 17164 : b = alg_get_b(al);
2427 17164 : pol = rnf_get_pol(rnf);
2428 :
2429 17164 : res = zeromatcopy(n,n);
2430 68663 : for (i=0; i<n; i++)
2431 51499 : gcoeff(res,i+1,1) = lift_shallow(rnfbasistoalg(rnf,gel(x,i+1)));
2432 :
2433 68663 : for (i=0; i<n; i++) {
2434 132405 : for (j=1; j<=i; j++)
2435 80906 : gcoeff(res,i+1,j+1) = gmodulo(poleval(gcoeff(res,i-j+1,1),gel(auts,j)),pol);
2436 132405 : for (; j<n; j++)
2437 80906 : gcoeff(res,i+1,j+1) = gmodulo(gmul(b,poleval(gcoeff(res,n+i-j+1,1),gel(auts,j))), pol);
2438 : }
2439 :
2440 68663 : for (i=0; i<n; i++)
2441 51499 : gcoeff(res,i+1,1) = gmodulo(gcoeff(res,i+1,1),pol);
2442 :
2443 17164 : return gerepilecopy(av, res);
2444 : }
2445 :
2446 : static GEN
2447 2170 : elementmultable(GEN mt, GEN x)
2448 : {
2449 2170 : pari_sp av = avma;
2450 2170 : long D = lg(mt)-1, i;
2451 2170 : GEN z = NULL;
2452 11207 : for (i=1; i<=D; i++)
2453 : {
2454 9037 : GEN c = gel(x,i);
2455 9037 : if (!gequal0(c))
2456 : {
2457 2961 : GEN M = RgM_Rg_mul(gel(mt,i),c);
2458 2961 : z = z? RgM_add(z, M): M;
2459 : }
2460 : }
2461 2170 : if (!z) { set_avma(av); return zeromatcopy(D,D); }
2462 2170 : return gerepileupto(av, z);
2463 : }
2464 : /* mt a t_VEC of Flm modulo m */
2465 : static GEN
2466 52032 : algbasismultable_Flm(GEN mt, GEN x, ulong m)
2467 : {
2468 52032 : pari_sp av = avma;
2469 52032 : long D = lg(gel(mt,1))-1, i;
2470 52032 : GEN z = NULL;
2471 671755 : for (i=1; i<=D; i++)
2472 : {
2473 619723 : ulong c = x[i];
2474 619723 : if (c)
2475 : {
2476 81218 : GEN M = Flm_Fl_mul(gel(mt,i),c, m);
2477 81218 : z = z? Flm_add(z, M, m): M;
2478 : }
2479 : }
2480 52032 : if (!z) { set_avma(av); return zero_Flm(D,D); }
2481 52032 : return gerepileupto(av, z);
2482 : }
2483 : static GEN
2484 372436 : elementabsmultable_Z(GEN mt, GEN x)
2485 : {
2486 372436 : long i, l = lg(x);
2487 372436 : GEN z = NULL;
2488 4369472 : for (i = 1; i < l; i++)
2489 : {
2490 3997036 : GEN c = gel(x,i);
2491 3997036 : if (signe(c))
2492 : {
2493 1135355 : GEN M = ZM_Z_mul(gel(mt,i),c);
2494 1135355 : z = z? ZM_add(z, M): M;
2495 : }
2496 : }
2497 372436 : return z;
2498 : }
2499 : static GEN
2500 160781 : elementabsmultable(GEN mt, GEN x)
2501 : {
2502 160781 : GEN d, z = elementabsmultable_Z(mt, Q_remove_denom(x,&d));
2503 160781 : return (z && d)? ZM_Z_div(z, d): z;
2504 : }
2505 : static GEN
2506 211655 : elementabsmultable_Fp(GEN mt, GEN x, GEN p)
2507 : {
2508 211655 : GEN z = elementabsmultable_Z(mt, x);
2509 211655 : return z? FpM_red(z, p): z;
2510 : }
2511 : static GEN
2512 372436 : algbasismultable(GEN al, GEN x)
2513 : {
2514 372436 : pari_sp av = avma;
2515 372436 : GEN z, p = alg_get_char(al), mt = alg_get_multable(al);
2516 372436 : z = signe(p)? elementabsmultable_Fp(mt, x, p): elementabsmultable(mt, x);
2517 372436 : if (!z)
2518 : {
2519 4287 : long D = lg(mt)-1;
2520 4287 : set_avma(av); return zeromat(D,D);
2521 : }
2522 368149 : return gerepileupto(av, z);
2523 : }
2524 :
2525 : static GEN
2526 2170 : algalgmultable_csa(GEN al, GEN x)
2527 : {
2528 2170 : GEN nf = alg_get_center(al), m;
2529 : long i,j;
2530 2170 : m = elementmultable(alg_get_relmultable(al), x);
2531 11207 : for (i=1; i<lg(m); i++)
2532 53102 : for(j=1; j<lg(m); j++)
2533 44065 : gcoeff(m,i,j) = basistoalg(nf,gcoeff(m,i,j));
2534 2170 : return m;
2535 : }
2536 :
2537 : /* assumes x in algebraic form */
2538 : static GEN
2539 19005 : algalgmultable(GEN al, GEN x)
2540 : {
2541 19005 : switch(alg_type(al))
2542 : {
2543 17164 : case al_CYCLIC: return algalgmultable_cyc(al, x);
2544 1841 : case al_CSA: return algalgmultable_csa(al, x);
2545 : }
2546 : return NULL; /*LCOV_EXCL_LINE*/
2547 : }
2548 :
2549 : /* on the natural basis */
2550 : /* assumes x in algebraic form */
2551 : static GEN
2552 16086 : algZmultable(GEN al, GEN x) {
2553 16086 : pari_sp av = avma;
2554 16086 : return gerepileupto(av, algmtK2Z(al,algalgmultable(al,x)));
2555 : }
2556 :
2557 : /* x integral */
2558 : static GEN
2559 41265 : algbasisrightmultable(GEN al, GEN x)
2560 : {
2561 41265 : long N = alg_get_absdim(al), i,j,k;
2562 41265 : GEN res = zeromatcopy(N,N), c, mt = alg_get_multable(al), p = alg_get_char(al);
2563 41265 : if (gequal0(p)) p = NULL;
2564 374591 : for (i=1; i<=N; i++) {
2565 333326 : c = gel(x,i);
2566 333326 : if (!gequal0(c)) {
2567 1339560 : for (j=1; j<=N; j++)
2568 20693268 : for(k=1; k<=N; k++) {
2569 19474932 : if (p) gcoeff(res,k,j) = Fp_add(gcoeff(res,k,j), Fp_mul(c, gcoeff(gel(mt,j),k,i), p), p);
2570 14559732 : else gcoeff(res,k,j) = addii(gcoeff(res,k,j), mulii(c, gcoeff(gel(mt,j),k,i)));
2571 : }
2572 : }
2573 : }
2574 41265 : return res;
2575 : }
2576 :
2577 : /* central simple algebra al from alginit */
2578 : /* right multiplication table on integral basis; no checks no GC */
2579 : static GEN
2580 77 : algrightmultable(GEN al, GEN x)
2581 : {
2582 : GEN d, M;
2583 77 : x = algalgtobasis(al, x);
2584 70 : x = Q_remove_denom(x, &d);
2585 70 : M = algbasisrightmultable(al,x);
2586 70 : return d ? ZM_Z_div(M,d) : M;
2587 : }
2588 :
2589 : /* basis for matrices : 1, E_{i,j} for (i,j)!=(1,1) */
2590 : /* index : ijk = ((i-1)*N+j-1)*n + k */
2591 : /* square matrices only, coefficients in basis form, shallow function */
2592 : static GEN
2593 23961 : algmat2basis(GEN al, GEN M)
2594 : {
2595 23961 : long n = alg_get_absdim(al), N = lg(M)-1, i, j, k, ij, ijk;
2596 : GEN res, x;
2597 23961 : res = zerocol(N*N*n);
2598 75131 : for (i=1; i<=N; i++) {
2599 163310 : for (j=1, ij=(i-1)*N+1; j<=N; j++, ij++) {
2600 112140 : x = gcoeff(M,i,j);
2601 819532 : for (k=1, ijk=(ij-1)*n+1; k<=n; k++, ijk++) {
2602 707392 : gel(res, ijk) = gel(x, k);
2603 707392 : if (i>1 && i==j) gel(res, ijk) = gsub(gel(res,ijk), gel(res,k));
2604 : }
2605 : }
2606 : }
2607 :
2608 23961 : return res;
2609 : }
2610 :
2611 : static GEN
2612 294 : algbasis2mat(GEN al, GEN M, long N)
2613 : {
2614 294 : long n = alg_get_absdim(al), i, j, k, ij, ijk;
2615 : GEN res, x;
2616 294 : res = zeromatcopy(N,N);
2617 882 : for (i=1; i<=N; i++)
2618 1764 : for (j=1; j<=N; j++)
2619 1176 : gcoeff(res,i,j) = zerocol(n);
2620 :
2621 882 : for (i=1; i<=N; i++) {
2622 1764 : for (j=1, ij=(i-1)*N+1; j<=N; j++, ij++) {
2623 1176 : x = gcoeff(res,i,j);
2624 9240 : for (k=1, ijk=(ij-1)*n+1; k<=n; k++, ijk++) {
2625 8064 : gel(x,k) = gel(M,ijk);
2626 8064 : if (i>1 && i==j) gel(x,k) = gadd(gel(x,k), gel(M,k));
2627 : }
2628 : }
2629 : }
2630 :
2631 294 : return res;
2632 : }
2633 :
2634 : static GEN
2635 23884 : algmatbasis_ei(GEN al, long ijk, long N)
2636 : {
2637 23884 : long n = alg_get_absdim(al), i, j, k, ij;
2638 : GEN res;
2639 :
2640 23884 : res = zeromatcopy(N,N);
2641 74900 : for (i=1; i<=N; i++)
2642 162848 : for (j=1; j<=N; j++)
2643 111832 : gcoeff(res,i,j) = zerocol(n);
2644 :
2645 23884 : k = ijk%n;
2646 23884 : if (k==0) k=n;
2647 23884 : ij = (ijk-k)/n+1;
2648 :
2649 23884 : if (ij==1) {
2650 16947 : for (i=1; i<=N; i++)
2651 11410 : gcoeff(res,i,i) = col_ei(n,k);
2652 5537 : return res;
2653 : }
2654 :
2655 18347 : j = ij%N;
2656 18347 : if (j==0) j=N;
2657 18347 : i = (ij-j)/N+1;
2658 :
2659 18347 : gcoeff(res,i,j) = col_ei(n,k);
2660 18347 : return res;
2661 : }
2662 :
2663 : /* FIXME lazy implementation! */
2664 : static GEN
2665 910 : algleftmultable_mat(GEN al, GEN M)
2666 : {
2667 910 : long N = lg(M)-1, n = alg_get_absdim(al), D = N*N*n, j;
2668 : GEN res, x, Mx;
2669 910 : if (N == 0) return cgetg(1, t_MAT);
2670 903 : if (N != nbrows(M)) pari_err_DIM("algleftmultable_mat (nonsquare)");
2671 882 : res = cgetg(D+1, t_MAT);
2672 24766 : for (j=1; j<=D; j++) {
2673 23884 : x = algmatbasis_ei(al, j, N);
2674 23884 : Mx = algmul(al, M, x);
2675 23884 : gel(res, j) = algmat2basis(al, Mx);
2676 : }
2677 882 : return res;
2678 : }
2679 :
2680 : /* left multiplication table on integral basis */
2681 : static GEN
2682 23660 : algleftmultable(GEN al, GEN x)
2683 : {
2684 23660 : pari_sp av = avma;
2685 : long tx;
2686 : GEN res;
2687 :
2688 23660 : checkalg(al);
2689 23660 : tx = alg_model(al,x);
2690 23639 : switch(tx) {
2691 994 : case al_TRIVIAL : res = mkmatcopy(mkcol(gel(x,1))); break;
2692 280 : case al_ALGEBRAIC : x = algalgtobasis(al,x);
2693 22127 : case al_BASIS : res = algbasismultable(al,x); break;
2694 518 : case al_MATRIX : res = algleftmultable_mat(al,x); break;
2695 : default : return NULL; /* LCOV_EXCL_LINE */
2696 : }
2697 23632 : return gerepileupto(av,res);
2698 : }
2699 :
2700 : static GEN
2701 4347 : algbasissplittingmatrix_csa(GEN al, GEN x)
2702 : {
2703 4347 : long d = alg_get_degree(al), i, j;
2704 4347 : GEN rnf = alg_get_splittingfield(al), splba = alg_get_splittingbasis(al), splbainv = alg_get_splittingbasisinv(al), M;
2705 4347 : M = algbasismultable(al,x);
2706 4347 : M = RgM_mul(M, splba); /* TODO best order ? big matrix /Q vs small matrix /nf */
2707 4347 : M = RgM_mul(splbainv, M);
2708 12852 : for (i=1; i<=d; i++)
2709 25326 : for (j=1; j<=d; j++)
2710 16821 : gcoeff(M,i,j) = rnfeltabstorel(rnf, gcoeff(M,i,j));
2711 4347 : return M;
2712 : }
2713 :
2714 : static GEN
2715 728 : algmat_tomatrix(GEN al, GEN x) /* abs = 0 */
2716 : {
2717 : GEN res;
2718 : long i,j;
2719 728 : if (lg(x) == 1) return cgetg(1, t_MAT);
2720 700 : res = zeromatcopy(nbrows(x),lg(x)-1);
2721 2212 : for (j=1; j<lg(x); j++)
2722 4879 : for (i=1; i<lgcols(x); i++)
2723 3367 : gcoeff(res,i,j) = algtomatrix(al,gcoeff(x,i,j),0);
2724 700 : return shallowmatconcat(res);
2725 : }
2726 :
2727 : static GEN
2728 42 : R_tomatrix(GEN x)
2729 : {
2730 42 : long t = H_model(x);
2731 42 : if (t == H_QUATERNION) pari_err_TYPE("R_tomatrix", x);
2732 35 : if (t == H_MATRIX) return x;
2733 21 : return mkmat(mkcol(x));
2734 : }
2735 : static GEN
2736 84 : C_tomatrix(GEN z, long abs)
2737 : {
2738 : GEN x,y;
2739 84 : long t = H_model(z), nrows, ncols;
2740 84 : if (t == H_QUATERNION) pari_err_TYPE("C_tomatrix", z);
2741 77 : if (!abs)
2742 : {
2743 14 : if (t == H_MATRIX) return z;
2744 7 : return mkmat(mkcol(z));
2745 : }
2746 63 : if (t == H_MATRIX)
2747 : {
2748 : /* Warning: this is not the same choice of basis as for other algebras */
2749 : GEN res, a, b;
2750 : long i,j;
2751 56 : RgM_dimensions(z,&nrows,&ncols);
2752 56 : res = zeromatcopy(2*nrows,2*ncols);
2753 168 : for (i=1; i<=nrows; i++)
2754 336 : for (j=1; j<=ncols; j++)
2755 : {
2756 224 : a = real_i(gcoeff(z,i,j));
2757 224 : b = imag_i(gcoeff(z,i,j));
2758 224 : gcoeff(res,2*i-1,2*j-1) = a;
2759 224 : gcoeff(res,2*i,2*j) = a;
2760 224 : gcoeff(res,2*i-1,2*j) = gneg(b);
2761 224 : gcoeff(res,2*i,2*j-1) = b;
2762 : }
2763 56 : return res;
2764 : }
2765 7 : x = real_i(z);
2766 7 : y = imag_i(z);
2767 7 : return mkmat22(x,gneg(y),y,x);
2768 : }
2769 : static GEN
2770 2457 : H_tomatrix(GEN x, long abs)
2771 : {
2772 2457 : long tx = H_model(x);
2773 2450 : GEN a = NULL, b =NULL, c = NULL, d = NULL, md = NULL, M = NULL;
2774 2450 : if (abs) {
2775 413 : if (tx == H_MATRIX) return algleftmultable_mat(NULL,x);
2776 280 : switch(tx)
2777 : {
2778 77 : case H_SCALAR:
2779 77 : a = real_i(x);
2780 77 : b = imag_i(x);
2781 77 : c = gen_0;
2782 77 : d = gen_0;
2783 77 : break;
2784 203 : case H_QUATERNION:
2785 203 : a = gel(x,1);
2786 203 : b = gel(x,2);
2787 203 : c = gel(x,3);
2788 203 : d = gel(x,4);
2789 203 : break;
2790 : }
2791 280 : M = scalarmat(a,4);
2792 280 : gcoeff(M,2,1) = gcoeff(M,4,3) = b;
2793 280 : gcoeff(M,1,2) = gcoeff(M,3,4) = gneg(b);
2794 280 : gcoeff(M,3,1) = gcoeff(M,2,4) = c;
2795 280 : gcoeff(M,4,2) = gcoeff(M,1,3) = gneg(c);
2796 280 : gcoeff(M,4,1) = gcoeff(M,3,2) = d;
2797 280 : gcoeff(M,2,3) = gcoeff(M,1,4) = gneg(d);
2798 : }
2799 : else /* abs == 0 */
2800 : {
2801 2037 : if (tx == H_MATRIX) return algmat_tomatrix(NULL,x);
2802 1778 : switch(tx)
2803 : {
2804 273 : case H_SCALAR:
2805 273 : M = mkmat22(
2806 : x, gen_0,
2807 : gen_0, conj_i(x)
2808 : );
2809 273 : break;
2810 1505 : case H_QUATERNION:
2811 1505 : a = gel(x,1);
2812 1505 : b = gel(x,2);
2813 1505 : c = gel(x,3);
2814 1505 : md = gneg(gel(x,4));
2815 1505 : M = mkmat22(
2816 : mkcomplex(a,b), mkcomplex(gneg(c),md),
2817 : mkcomplex(c,md), mkcomplex(a,gneg(b))
2818 : );
2819 : }
2820 : }
2821 2058 : return M;
2822 : }
2823 :
2824 : GEN
2825 25109 : algtomatrix(GEN al, GEN x, long abs)
2826 : {
2827 25109 : pari_sp av = avma;
2828 25109 : GEN res = NULL;
2829 : long ta, tx;
2830 25109 : checkalg(al);
2831 25109 : ta = alg_type(al);
2832 25109 : if (ta==al_REAL)
2833 : {
2834 2268 : switch(alg_get_absdim(al)) {
2835 42 : case 1: res = R_tomatrix(x); break;
2836 84 : case 2: res = C_tomatrix(x,abs); break;
2837 2135 : case 4: res = H_tomatrix(x,abs); break;
2838 7 : default: pari_err_TYPE("algtomatrix [apply alginit]", al);
2839 : }
2840 2240 : return gerepilecopy(av, res);
2841 : }
2842 22841 : if (abs || ta==al_TABLE) return algleftmultable(al,x);
2843 7014 : tx = alg_model(al,x);
2844 7014 : if (tx == al_MATRIX) res = algmat_tomatrix(al,x);
2845 6545 : else switch (alg_type(al))
2846 : {
2847 2198 : case al_CYCLIC:
2848 2198 : if (tx==al_BASIS) x = algbasistoalg(al,x);
2849 2198 : res = algalgmultable(al,x);
2850 2198 : break;
2851 4347 : case al_CSA:
2852 4347 : if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);
2853 4347 : res = algbasissplittingmatrix_csa(al,x);
2854 4347 : break;
2855 : default: return NULL; /*LCOV_EXCL_LINE*/
2856 : }
2857 7014 : return gerepilecopy(av,res);
2858 : }
2859 :
2860 : /* x^(-1)*y, NULL if no solution */
2861 : static GEN
2862 112 : C_divl_i(GEN x, GEN y)
2863 : {
2864 112 : long tx = H_model(x), ty = H_model(y);
2865 112 : if (tx != ty) pari_err_TYPE2("C_divl", x, y);
2866 105 : switch (tx) {
2867 42 : case H_SCALAR:
2868 42 : if (gequal0(x)) return gequal0(y) ? gen_0 : NULL;
2869 14 : else return gdiv(y,x);
2870 56 : case H_MATRIX:
2871 56 : if ((lg(x)>1 && lg(x) != lgcols(x)) || (lg(y)>1 && lg(y) != lgcols(y)))
2872 7 : pari_err_DIM("C_divl (nonsquare)");
2873 49 : if (lg(x) != lg(y)) pari_err_DIM("C_divl");
2874 42 : if (lg(y) == 1) return cgetg(1, t_MAT);
2875 42 : return RgM_invimage(x, y);
2876 7 : default: pari_err_TYPE("C_divl", x); return NULL;
2877 : }
2878 : }
2879 : /* H^k -> C^2k */
2880 : static GEN
2881 140 : HC_to_CC(GEN v)
2882 : {
2883 140 : long l = lg(v), i;
2884 140 : GEN w = cgetg(2*l-1, t_COL), a, b, c, d;
2885 420 : for (i=1; i<l; i++)
2886 : {
2887 280 : H_compo(gel(v,i),&a,&b,&c,&d);
2888 280 : gel(w,2*i-1) = mkcomplex(a,b);
2889 280 : gel(w,2*i) = mkcomplex(c,gneg(d));
2890 : }
2891 140 : return w;
2892 : }
2893 : /* C^2k -> H^k */
2894 : static GEN
2895 98 : CC_to_HC(GEN w)
2896 : {
2897 98 : long l = lg(w), i, lv = (l+1)/2;
2898 98 : GEN v = cgetg(lv, t_COL), ab, cd;
2899 294 : for (i=1; i<lv; i++)
2900 : {
2901 196 : ab = gel(w,2*i-1);
2902 196 : cd = gel(w,2*i);
2903 196 : gel(v,i) = mkcol4(real_i(ab),imag_i(ab),real_i(cd),gneg(imag_i(cd)));
2904 : }
2905 98 : return v;
2906 : }
2907 : /* M_{k,n}(H) -> M_{2k,n}(C) */
2908 : static GEN
2909 210 : HM_to_CM(GEN x) pari_APPLY_same(HC_to_CC(gel(x,i)));
2910 : /* M_{2k,n}(C) -> M_{k,n}(H) */
2911 : static GEN
2912 147 : CM_to_HM(GEN x) pari_APPLY_same(CC_to_HC(gel(x,i)));
2913 : /* x^(-1)*y, NULL if no solution */
2914 : static GEN
2915 203 : H_divl_i(GEN x, GEN y)
2916 : {
2917 203 : pari_sp av = avma;
2918 203 : long tx = H_model(x), ty = H_model(y);
2919 189 : if ((tx==H_MATRIX) ^ (ty==H_MATRIX)) pari_err_TYPE2("H_divl", x, y);
2920 168 : if (tx==H_MATRIX)
2921 : {
2922 : GEN mx, my, mxdivy;
2923 98 : if ((lg(x)>1 && lg(x) != lgcols(x)) || (lg(y)>1 && lg(y) != lgcols(y)))
2924 14 : pari_err_DIM("H_divl (nonsquare)");
2925 84 : if (lg(x) != lg(y)) pari_err_DIM("H_divl");
2926 77 : if (lg(y) == 1) return cgetg(1, t_MAT);
2927 70 : mx = H_tomatrix(x,0);
2928 70 : my = HM_to_CM(y);
2929 70 : mxdivy = RgM_invimage(mx, my);
2930 70 : if (!mxdivy) return gc_NULL(av);
2931 49 : return gerepilecopy(av,CM_to_HM(mxdivy));
2932 : }
2933 70 : if (gequal0(y)) return gen_0;
2934 56 : if (gequal0(x)) return NULL;
2935 42 : return gerepilecopy(av,H_mul(H_inv(x),y));
2936 : }
2937 : /* x^(-1)*y, NULL if no solution */
2938 : static GEN
2939 3199 : algdivl_i(GEN al, GEN x, GEN y, long tx, long ty) {
2940 3199 : pari_sp av = avma;
2941 3199 : GEN res, p = alg_get_char(al), mtx;
2942 3199 : if (tx != ty) {
2943 343 : if (tx==al_ALGEBRAIC) { x = algalgtobasis(al,x); tx=al_BASIS; }
2944 343 : if (ty==al_ALGEBRAIC) { y = algalgtobasis(al,y); ty=al_BASIS; }
2945 : }
2946 3199 : if (ty == al_MATRIX)
2947 : {
2948 77 : if (alg_type(al) != al_TABLE) y = algalgtobasis(al,y);
2949 77 : y = algmat2basis(al,y);
2950 : }
2951 3199 : if (signe(p)) res = FpM_FpC_invimage(algbasismultable(al,x),y,p);
2952 : else
2953 : {
2954 3010 : if (ty==al_ALGEBRAIC) mtx = algalgmultable(al,x);
2955 2303 : else mtx = algleftmultable(al,x);
2956 3010 : res = inverseimage(mtx,y);
2957 : }
2958 3199 : if (!res || lg(res)==1) return gc_NULL(av);
2959 1764 : if (tx == al_MATRIX) {
2960 294 : res = algbasis2mat(al, res, lg(x)-1);
2961 294 : return gerepilecopy(av,res);
2962 : }
2963 1470 : return gerepileupto(av,res);
2964 : }
2965 : static GEN
2966 1015 : algdivl_i2(GEN al, GEN x, GEN y)
2967 : {
2968 : long tx, ty;
2969 1015 : checkalg(al);
2970 1015 : if (alg_type(al)==al_REAL) switch(alg_get_absdim(al)) {
2971 112 : case 1: case 2: return C_divl_i(x,y);
2972 147 : case 4: return H_divl_i(x,y);
2973 : }
2974 756 : tx = alg_model(al,x);
2975 749 : ty = alg_model(al,y);
2976 749 : if (tx == al_MATRIX) {
2977 140 : if (ty != al_MATRIX) pari_err_TYPE2("\\", x, y);
2978 133 : if ((lg(x)>1 && lg(x) != lgcols(x)) || (lg(y)>1 && lg(y) != lgcols(y)))
2979 28 : pari_err_DIM("algdivl (nonsquare)");
2980 105 : if (lg(x) != lg(y)) pari_err_DIM("algdivl");
2981 84 : if (lg(y) == 1) return cgetg(1, t_MAT);
2982 : }
2983 686 : return algdivl_i(al,x,y,tx,ty);
2984 : }
2985 :
2986 889 : GEN algdivl(GEN al, GEN x, GEN y)
2987 : {
2988 : GEN z;
2989 889 : z = algdivl_i2(al,x,y);
2990 742 : if (!z) pari_err_INV("algdivl", x);
2991 728 : return z;
2992 : }
2993 :
2994 : int
2995 126 : algisdivl(GEN al, GEN x, GEN y, GEN* ptz)
2996 : {
2997 126 : pari_sp av = avma;
2998 126 : GEN z = algdivl_i2(al,x,y);
2999 126 : if (!z) return gc_bool(av,0);
3000 84 : if (ptz != NULL) *ptz = z;
3001 84 : return 1;
3002 : }
3003 :
3004 : static GEN
3005 140 : C_inv(GEN x)
3006 : {
3007 140 : switch (H_model(x))
3008 : {
3009 63 : case H_SCALAR: return gequal0(x) ? NULL : ginv(x);
3010 70 : case H_MATRIX: return RgM_inv(x);
3011 7 : default: pari_err_TYPE("alginv_i", x);
3012 : }
3013 : return NULL; /*LCOV_EXCL_LINE*/
3014 : }
3015 : static GEN
3016 259 : H_inv(GEN x)
3017 : {
3018 259 : pari_sp av = avma;
3019 : GEN nm, xi;
3020 : long i;
3021 259 : switch (H_model(x))
3022 : {
3023 28 : case H_SCALAR:
3024 28 : if (gequal0(x)) return NULL;
3025 14 : return ginv(x);
3026 161 : case H_QUATERNION:
3027 161 : if (gequal0(x)) return NULL;
3028 154 : nm = H_norm(x, 0);
3029 154 : xi = gdiv(x,nm);
3030 616 : for(i=2; i<=4; i++) gel(xi,i) = gneg(gel(xi,i));
3031 154 : return gerepilecopy(av,xi);
3032 63 : case H_MATRIX:
3033 63 : if (lg(x)==1) return cgetg(1,t_MAT);
3034 56 : return H_divl_i(x, matid(lg(x)-1));
3035 : }
3036 : return NULL; /*LCOV_EXCL_LINE*/
3037 : }
3038 : static GEN
3039 2989 : alginv_i(GEN al, GEN x)
3040 : {
3041 2989 : pari_sp av = avma;
3042 2989 : GEN res = NULL, p = alg_get_char(al);
3043 : long tx, n, ta;
3044 2989 : ta = alg_type(al);
3045 2989 : if (ta==al_REAL) switch(alg_get_absdim(al)) {
3046 140 : case 1: case 2: return C_inv(x);
3047 217 : case 4: return H_inv(x);
3048 7 : default: pari_err_TYPE("alginv_i [apply alginit]", al);
3049 : }
3050 2625 : tx = alg_model(al,x);
3051 2604 : switch(tx) {
3052 70 : case al_TRIVIAL :
3053 70 : if (signe(p)) { res = mkcol(Fp_inv(gel(x,1),p)); break; }
3054 56 : else { res = mkcol(ginv(gel(x,1))); break; }
3055 455 : case al_ALGEBRAIC :
3056 : switch(ta) {
3057 350 : case al_CYCLIC: n = alg_get_degree(al); break;
3058 105 : case al_CSA: n = alg_get_dim(al); break;
3059 : default: return NULL; /* LCOV_EXCL_LINE */
3060 : }
3061 455 : res = algdivl_i(al, x, col_ei(n,1), tx, al_ALGEBRAIC); break;
3062 1841 : case al_BASIS : res = algdivl_i(al, x, col_ei(alg_get_absdim(al),1), tx,
3063 1841 : al_BASIS); break;
3064 238 : case al_MATRIX :
3065 238 : n = lg(x)-1;
3066 238 : if (n==0) return cgetg(1, t_MAT);
3067 224 : if (n != nbrows(x)) pari_err_DIM("alginv_i (nonsquare)");
3068 217 : res = algdivl_i(al, x, col_ei(n*n*alg_get_absdim(al),1), tx, al_BASIS);
3069 : /* cheat on type because wrong dimension */
3070 : }
3071 2583 : if (!res) return gc_NULL(av);
3072 1162 : return gerepilecopy(av,res);
3073 : }
3074 : GEN
3075 1330 : alginv(GEN al, GEN x)
3076 : {
3077 : GEN z;
3078 1330 : checkalg(al);
3079 1330 : z = alginv_i(al,x);
3080 1281 : if (!z) pari_err_INV("alginv", x);
3081 1246 : return z;
3082 : }
3083 :
3084 : int
3085 1659 : algisinv(GEN al, GEN x, GEN* ptix)
3086 : {
3087 1659 : pari_sp av = avma;
3088 : GEN ix;
3089 1659 : if (al) checkalg(al);
3090 1659 : ix = alginv_i(al,x);
3091 1659 : if (!ix) return gc_bool(av,0);
3092 196 : if (ptix != NULL) *ptix = ix;
3093 196 : return 1;
3094 : }
3095 :
3096 : /* x*y^(-1) */
3097 : GEN
3098 469 : algdivr(GEN al, GEN x, GEN y) { return algmul(al, x, alginv(al, y)); }
3099 :
3100 52170 : static GEN _mul(void* data, GEN x, GEN y) { return algmul((GEN)data,x,y); }
3101 125333 : static GEN _sqr(void* data, GEN x) { return algsqr((GEN)data,x); }
3102 :
3103 : static GEN
3104 21 : algmatid(GEN al, long N)
3105 : {
3106 21 : long n = alg_get_absdim(al), i, j;
3107 : GEN res, one, zero;
3108 :
3109 21 : res = zeromatcopy(N,N);
3110 21 : one = col_ei(n,1);
3111 21 : zero = zerocol(n);
3112 49 : for (i=1; i<=N; i++)
3113 84 : for (j=1; j<=N; j++)
3114 56 : gcoeff(res,i,j) = i==j ? one : zero;
3115 21 : return res;
3116 : }
3117 :
3118 : GEN
3119 22582 : algpow(GEN al, GEN x, GEN n)
3120 : {
3121 22582 : pari_sp av = avma;
3122 : GEN res;
3123 22582 : long s = signe(n);
3124 22582 : checkalg(al);
3125 22582 : if (!s && alg_type(al)==al_REAL)
3126 : {
3127 63 : if (H_model(x) == H_MATRIX) return matid(lg(x)-1);
3128 35 : else return gen_1;
3129 : }
3130 22519 : switch (s) {
3131 28 : case 0:
3132 28 : if (alg_model(al,x) == al_MATRIX)
3133 21 : res = algmatid(al,lg(x)-1);
3134 : else
3135 7 : res = col_ei(alg_get_absdim(al),1);
3136 28 : return res;
3137 22344 : case 1:
3138 22344 : res = gen_pow_i(x, n, (void*)al, _sqr, _mul); break;
3139 147 : default: /* -1 */
3140 147 : res = gen_pow_i(alginv(al,x), gneg(n), (void*)al, _sqr, _mul);
3141 : }
3142 22477 : return gerepilecopy(av,res);
3143 : }
3144 :
3145 : static GEN
3146 546 : algredcharpoly_i(GEN al, GEN x, long v)
3147 : {
3148 546 : GEN rnf = alg_get_splittingfield(al);
3149 546 : GEN cp = charpoly(algtomatrix(al,x,0),v);
3150 539 : long i, m = lg(cp);
3151 2184 : for (i=2; i<m; i++) gel(cp,i) = rnfeltdown(rnf, gel(cp,i));
3152 539 : return cp;
3153 : }
3154 :
3155 : /* assumes al is CSA or CYCLIC */
3156 : static GEN
3157 553 : algredcharpoly(GEN al, GEN x, long v)
3158 : {
3159 553 : pari_sp av = avma;
3160 553 : long w = gvar(rnf_get_pol(alg_get_center(al)));
3161 553 : if (varncmp(v,w)>=0) pari_err_PRIORITY("algredcharpoly",pol_x(v),">=",w);
3162 546 : switch(alg_type(al))
3163 : {
3164 546 : case al_CYCLIC:
3165 : case al_CSA:
3166 546 : return gerepileupto(av, algredcharpoly_i(al, x, v));
3167 : }
3168 : return NULL; /*LCOV_EXCL_LINE*/
3169 : }
3170 :
3171 : static GEN
3172 32300 : algbasischarpoly(GEN al, GEN x, long v)
3173 : {
3174 32300 : pari_sp av = avma;
3175 32300 : GEN p = alg_get_char(al), mx;
3176 32300 : if (alg_model(al,x) == al_MATRIX) mx = algleftmultable_mat(al,x);
3177 32209 : else mx = algbasismultable(al,x);
3178 32293 : if (signe(p)) {
3179 29955 : GEN res = FpM_charpoly(mx,p);
3180 29955 : setvarn(res,v);
3181 29955 : return gerepileupto(av, res);
3182 : }
3183 2338 : return gerepileupto(av, charpoly(mx,v));
3184 : }
3185 :
3186 : static GEN
3187 35 : R_charpoly(GEN x, long v, long abs)
3188 : {
3189 35 : pari_sp av = avma;
3190 35 : GEN res = NULL;
3191 35 : switch (H_model(x))
3192 : {
3193 14 : case H_SCALAR: res = mkpoln(2, gen_1, gneg(x)); break;
3194 14 : case H_MATRIX:
3195 14 : res = charpoly(x,v);
3196 14 : if (abs) res = gpowgs(res,nbrows(x));
3197 14 : break;
3198 7 : default: pari_err_TYPE("R_charpoly", x);
3199 : }
3200 28 : if (v) setvarn(res, v);
3201 28 : return gerepilecopy(av, res);
3202 : }
3203 : static GEN
3204 35 : C_charpoly(GEN x, long v, long abs)
3205 : {
3206 35 : pari_sp av = avma;
3207 35 : GEN res = NULL;
3208 35 : switch (H_model(x))
3209 : {
3210 14 : case H_SCALAR:
3211 14 : if (abs) res = mkpoln(3, gen_1, gneg(gshift(real_i(x),1)), cxnorm(x));
3212 7 : else res = mkpoln(2, gen_1, gneg(x));
3213 14 : break;
3214 14 : case H_MATRIX:
3215 14 : res = charpoly(x,v);
3216 14 : if (abs) res = gpowgs(real_i(gmul(res,gconj(res))),nbrows(x));
3217 14 : break;
3218 7 : default: pari_err_TYPE("C_charpoly", x);
3219 : }
3220 28 : if (v) setvarn(res, v);
3221 28 : return gerepilecopy(av, res);
3222 : }
3223 : static GEN
3224 98 : H_charpoly(GEN x, long v, long abs)
3225 : {
3226 98 : pari_sp av = avma;
3227 : GEN res;
3228 98 : if (H_model(x) == H_MATRIX) return greal(charpoly(H_tomatrix(x,abs),v));
3229 70 : res = mkpoln(3, gen_1, gneg(H_trace(x,0)), H_norm(x,0));
3230 70 : if (v) setvarn(res, v);
3231 70 : if (abs) res = gsqr(res);
3232 70 : return gerepilecopy(av, res);
3233 : }
3234 :
3235 : GEN
3236 32496 : algcharpoly(GEN al, GEN x, long v, long abs)
3237 : {
3238 : long ta;
3239 32496 : if (v<0) v=0;
3240 32496 : checkalg(al);
3241 32496 : ta = alg_type(al);
3242 32496 : if (ta == al_REAL) switch (alg_get_absdim(al)) {
3243 35 : case 1: return R_charpoly(x, v, abs);
3244 35 : case 2: return C_charpoly(x, v, abs);
3245 98 : case 4: return H_charpoly(x, v, abs);
3246 7 : default: pari_err_TYPE("algcharpoly [apply alginit]", al);
3247 : }
3248 :
3249 : /* gneg(x[1]) left on stack */
3250 32321 : if (alg_model(al,x) == al_TRIVIAL) {
3251 84 : GEN p = alg_get_char(al);
3252 84 : if (signe(p)) return deg1pol(gen_1,Fp_neg(gel(x,1),p),v);
3253 70 : return deg1pol(gen_1,gneg(gel(x,1)),v);
3254 : }
3255 :
3256 32230 : switch(ta) {
3257 665 : case al_CYCLIC: case al_CSA:
3258 665 : if (abs)
3259 : {
3260 112 : if (alg_model(al,x)==al_ALGEBRAIC) x = algalgtobasis(al,x);
3261 : }
3262 553 : else return algredcharpoly(al,x,v);
3263 31677 : case al_TABLE: return algbasischarpoly(al,x,v);
3264 : default : return NULL; /* LCOV_EXCL_LINE */
3265 : }
3266 : }
3267 :
3268 : /* assumes x in basis form */
3269 : static GEN
3270 652376 : algabstrace(GEN al, GEN x)
3271 : {
3272 652376 : pari_sp av = avma;
3273 652376 : GEN res = NULL, p = alg_get_char(al);
3274 652376 : if (signe(p)) return FpV_dotproduct(x, alg_get_tracebasis(al), p);
3275 49462 : switch(alg_model(al,x)) {
3276 154 : case al_TRIVIAL: return gcopy(gel(x,1)); break;
3277 49308 : case al_BASIS: res = RgV_dotproduct(x, alg_get_tracebasis(al)); break;
3278 : }
3279 49308 : return gerepileupto(av,res);
3280 : }
3281 :
3282 : static GEN
3283 1512 : algredtrace(GEN al, GEN x)
3284 : {
3285 1512 : pari_sp av = avma;
3286 1512 : GEN res = NULL;
3287 1512 : switch(alg_model(al,x)) {
3288 35 : case al_TRIVIAL: return gcopy(gel(x,1)); break;
3289 560 : case al_BASIS: return algredtrace(al, algbasistoalg(al,x));
3290 : /* TODO precompute too? */
3291 917 : case al_ALGEBRAIC:
3292 917 : switch(alg_type(al))
3293 : {
3294 588 : case al_CYCLIC:
3295 588 : res = rnfelttrace(alg_get_splittingfield(al),gel(x,1));
3296 588 : break;
3297 329 : case al_CSA:
3298 329 : res = gtrace(algalgmultable_csa(al,x));
3299 329 : res = gdiv(res, stoi(alg_get_degree(al)));
3300 329 : break;
3301 : default: return NULL; /* LCOV_EXCL_LINE */
3302 : }
3303 : }
3304 917 : return gerepileupto(av,res);
3305 : }
3306 :
3307 : static GEN
3308 469 : algtrace_mat(GEN al, GEN M, long abs) {
3309 469 : pari_sp av = avma;
3310 469 : long N = lg(M)-1, i;
3311 469 : GEN res, p = alg_get_char(al);
3312 469 : if (N == 0) return gen_0;
3313 448 : if (N != nbrows(M)) pari_err_DIM("algtrace_mat (nonsquare)");
3314 :
3315 434 : if (!signe(p)) p = NULL;
3316 434 : if (alg_type(al) == al_TABLE) abs = 1;
3317 434 : res = algtrace(al, gcoeff(M,1,1), abs);
3318 896 : for (i=2; i<=N; i++) {
3319 462 : if (p) res = Fp_add(res, algtrace(al,gcoeff(M,i,i),abs), p);
3320 455 : else res = gadd(res, algtrace(al,gcoeff(M,i,i),abs));
3321 : }
3322 434 : if (abs) res = gmulgu(res, N); /* absolute trace */
3323 434 : return gerepileupto(av, res);
3324 : }
3325 :
3326 : static GEN
3327 35 : R_trace(GEN x, long abs)
3328 : {
3329 35 : pari_sp av = avma;
3330 35 : GEN res = NULL;
3331 35 : switch (H_model(x))
3332 : {
3333 14 : case H_SCALAR: res = gcopy(x); break;
3334 14 : case H_MATRIX: res = abs? mulrs(gtrace(x),nbrows(x)) : gtrace(x); break;
3335 7 : default: pari_err_TYPE("R_trace", x);
3336 : }
3337 28 : return gerepilecopy(av, res);
3338 : }
3339 : static GEN
3340 35 : C_trace(GEN x, long abs)
3341 : {
3342 35 : pari_sp av = avma;
3343 35 : GEN res = NULL;
3344 35 : switch (H_model(x))
3345 : {
3346 14 : case H_SCALAR: res = abs ? gshift(real_i(x),1) : x; break;
3347 14 : case H_MATRIX:
3348 14 : res = abs ? mulrs(real_i(gtrace(x)),2*nbrows(x)) : gtrace(x); break;
3349 7 : default: pari_err_TYPE("C_trace", x);
3350 : }
3351 28 : return gerepilecopy(av, res);
3352 : }
3353 : static GEN
3354 567 : H_trace(GEN x, long abs)
3355 : {
3356 567 : long s = abs? 2 : 1;
3357 567 : switch (H_model(x))
3358 : {
3359 154 : case H_SCALAR: return gshift(real_i(x),s);
3360 329 : case H_QUATERNION: return gshift(gel(x,1),s);
3361 77 : case H_MATRIX:
3362 77 : return algtrace_mat(NULL, x, abs);
3363 : }
3364 : return NULL; /*LCOV_EXCL_LINE*/
3365 : }
3366 :
3367 : GEN
3368 2702 : algtrace(GEN al, GEN x, long abs)
3369 : {
3370 : long ta;
3371 2702 : checkalg(al);
3372 2702 : ta = alg_type(al);
3373 2702 : if (ta==al_REAL) switch (alg_get_absdim(al)) {
3374 35 : case 1: return R_trace(x,abs);
3375 35 : case 2: return C_trace(x,abs);
3376 497 : case 4: return H_trace(x,abs);
3377 7 : default: pari_err_TYPE("algtrace [apply alginit]", al);
3378 : }
3379 2128 : if (alg_model(al,x) == al_MATRIX) return algtrace_mat(al,x,abs);
3380 1736 : switch(ta) {
3381 1596 : case al_CYCLIC: case al_CSA:
3382 1596 : if (!abs) return algredtrace(al,x);
3383 644 : if (alg_model(al,x)==al_ALGEBRAIC) x = algalgtobasis(al,x);
3384 784 : case al_TABLE: return algabstrace(al,x);
3385 : default : return NULL; /* LCOV_EXCL_LINE */
3386 : }
3387 : }
3388 :
3389 : static GEN
3390 71294 : ZM_trace(GEN x)
3391 : {
3392 71294 : long i, lx = lg(x);
3393 : GEN t;
3394 71294 : if (lx < 3) return lx == 1? gen_0: gcopy(gcoeff(x,1,1));
3395 70391 : t = gcoeff(x,1,1);
3396 1190209 : for (i = 2; i < lx; i++) t = addii(t, gcoeff(x,i,i));
3397 70391 : return t;
3398 : }
3399 : static GEN
3400 221815 : FpM_trace(GEN x, GEN p)
3401 : {
3402 221815 : long i, lx = lg(x);
3403 : GEN t;
3404 221815 : if (lx < 3) return lx == 1? gen_0: gcopy(gcoeff(x,1,1));
3405 209524 : t = gcoeff(x,1,1);
3406 1730808 : for (i = 2; i < lx; i++) t = Fp_add(t, gcoeff(x,i,i), p);
3407 209524 : return t;
3408 : }
3409 :
3410 : static GEN
3411 65604 : algtracebasis(GEN al)
3412 : {
3413 65604 : pari_sp av = avma;
3414 65604 : GEN mt = alg_get_multable(al), p = alg_get_char(al);
3415 65604 : long i, l = lg(mt);
3416 65604 : GEN v = cgetg(l, t_VEC);
3417 287419 : if (signe(p)) for (i=1; i < l; i++) gel(v,i) = FpM_trace(gel(mt,i), p);
3418 79909 : else for (i=1; i < l; i++) gel(v,i) = ZM_trace(gel(mt,i));
3419 65604 : return gerepileupto(av,v);
3420 : }
3421 :
3422 : /* Assume: i > 0, expo := p^i <= absdim, x contained in I_{i-1} given by mult
3423 : * table modulo modu=p^(i+1). Return Tr(x^(p^i)) mod modu */
3424 : static ulong
3425 52032 : algtracei(GEN mt, ulong p, ulong expo, ulong modu)
3426 : {
3427 52032 : pari_sp av = avma;
3428 52032 : long j, l = lg(mt);
3429 52032 : ulong tr = 0;
3430 52032 : mt = Flm_powu(mt,expo,modu);
3431 671755 : for (j=1; j<l; j++) tr += ucoeff(mt,j,j);
3432 52032 : return gc_ulong(av, (tr/expo) % p);
3433 : }
3434 :
3435 : static GEN
3436 42 : R_norm(GEN x, long abs)
3437 : {
3438 42 : pari_sp av = avma;
3439 42 : GEN res = NULL;
3440 42 : switch (H_model(x))
3441 : {
3442 14 : case H_SCALAR: res = gcopy(x); break;
3443 21 : case H_MATRIX: res = abs ? powrs(det(x),nbrows(x)) : det(x); break;
3444 7 : default: pari_err_TYPE("R_norm", x);
3445 : }
3446 35 : return gerepilecopy(av,res);
3447 : }
3448 : static GEN
3449 42 : C_norm(GEN x, long abs)
3450 : {
3451 42 : pari_sp av = avma;
3452 42 : GEN res = NULL;
3453 42 : switch (H_model(x))
3454 : {
3455 14 : case H_SCALAR: res = abs ? cxnorm(x) : x; break;
3456 21 : case H_MATRIX: res = abs ? powrs(cxnorm(det(x)),nbrows(x)) : det(x); break;
3457 7 : default: pari_err_TYPE("C_norm", x);
3458 : }
3459 35 : return gerepilecopy(av,res);
3460 : }
3461 : static GEN
3462 434 : H_norm(GEN x, long abs)
3463 : {
3464 434 : pari_sp av = avma;
3465 434 : switch (H_model(x))
3466 : {
3467 42 : case H_SCALAR:
3468 42 : if (abs) return gerepilecopy(av,gsqr(gnorm(x)));
3469 35 : else return gnorm(x);
3470 322 : case H_QUATERNION:
3471 322 : if (abs) return gerepilecopy(av,gsqr(gnorml2(x)));
3472 294 : else return gnorml2(x);
3473 63 : case H_MATRIX:
3474 63 : return gerepilecopy(av,real_i(det(H_tomatrix(x,abs))));
3475 : }
3476 : return NULL; /*LCOV_EXCL_LINE*/
3477 : }
3478 :
3479 : GEN
3480 1309 : algnorm(GEN al, GEN x, long abs)
3481 : {
3482 1309 : pari_sp av = avma;
3483 : long tx, ta;
3484 : GEN p, rnf, res, mx;
3485 1309 : checkalg(al);
3486 1309 : ta = alg_type(al);
3487 1309 : if (ta==al_REAL) switch (alg_get_absdim(al)) {
3488 42 : case 1: return R_norm(x,abs);
3489 42 : case 2: return C_norm(x,abs);
3490 210 : case 4: return H_norm(x,abs);
3491 7 : default: pari_err_TYPE("algnorm [apply alginit]", al);
3492 : }
3493 1008 : p = alg_get_char(al);
3494 1008 : tx = alg_model(al,x);
3495 1008 : if (signe(p)) {
3496 21 : if (tx == al_MATRIX) mx = algleftmultable_mat(al,x);
3497 14 : else mx = algbasismultable(al,x);
3498 21 : return gerepileupto(av, FpM_det(mx,p));
3499 : }
3500 987 : if (tx == al_TRIVIAL) return gcopy(gel(x,1));
3501 :
3502 945 : switch(ta) {
3503 875 : case al_CYCLIC: case al_CSA:
3504 875 : if (abs)
3505 : {
3506 196 : if (alg_model(al,x)==al_ALGEBRAIC) x = algalgtobasis(al,x);
3507 : }
3508 : else
3509 : {
3510 679 : rnf = alg_get_splittingfield(al);
3511 679 : res = rnfeltdown(rnf, det(algtomatrix(al,x,0)));
3512 672 : break;
3513 : }
3514 : case al_TABLE:
3515 266 : if (tx == al_MATRIX) mx = algleftmultable_mat(al,x);
3516 105 : else mx = algbasismultable(al,x);
3517 259 : res = det(mx);
3518 259 : break;
3519 : default: return NULL; /* LCOV_EXCL_LINE */
3520 : }
3521 931 : return gerepileupto(av, res);
3522 : }
3523 :
3524 : static GEN
3525 71178 : algalgtonat_cyc(GEN al, GEN x)
3526 : {
3527 71178 : pari_sp av = avma;
3528 71178 : GEN nf = alg_get_abssplitting(al), rnf = alg_get_splittingfield(al), res, c;
3529 71178 : long n = alg_get_degree(al), N = nf_get_degree(nf), i, i1;
3530 71178 : res = zerocol(N*n);
3531 221154 : for (i=0; i<n; i++) {
3532 149976 : c = gel(x,i+1);
3533 149976 : c = rnfeltreltoabs(rnf,c);
3534 149976 : if (!gequal0(c)) {
3535 97302 : c = algtobasis(nf,c);
3536 510311 : for (i1=1; i1<=N; i1++) gel(res,i*N+i1) = gel(c,i1);
3537 : }
3538 : }
3539 71178 : return gerepilecopy(av, res);
3540 : }
3541 :
3542 : static GEN
3543 16149 : algalgtonat_csa(GEN al, GEN x)
3544 : {
3545 16149 : pari_sp av = avma;
3546 16149 : GEN nf = alg_get_center(al), res, c;
3547 16149 : long d2 = alg_get_dim(al), n = nf_get_degree(nf), i, i1;
3548 16149 : res = zerocol(d2*n);
3549 80080 : for (i=0; i<d2; i++) {
3550 63931 : c = gel(x,i+1);
3551 63931 : if (!gequal0(c)) {
3552 35966 : c = algtobasis(nf,c);
3553 107366 : for (i1=1; i1<=n; i1++) gel(res,i*n+i1) = gel(c,i1);
3554 : }
3555 : }
3556 16149 : return gerepilecopy(av, res);
3557 : }
3558 :
3559 : /* assumes al CSA or CYCLIC */
3560 : static GEN
3561 87327 : algalgtonat(GEN al, GEN x)
3562 : {
3563 87327 : switch(alg_type(al))
3564 : {
3565 71178 : case al_CYCLIC: return algalgtonat_cyc(al, x);
3566 16149 : case al_CSA: return algalgtonat_csa(al, x);
3567 : }
3568 : return NULL; /*LCOV_EXCL_LINE*/
3569 : }
3570 :
3571 : static GEN
3572 17094 : algnattoalg_cyc(GEN al, GEN x)
3573 : {
3574 17094 : pari_sp av = avma;
3575 17094 : GEN nf = alg_get_abssplitting(al), rnf = alg_get_splittingfield(al), res, c;
3576 17094 : long n = alg_get_degree(al), N = nf_get_degree(nf), i, i1;
3577 17094 : res = zerocol(n);
3578 17094 : c = zerocol(N);
3579 68390 : for (i=0; i<n; i++) {
3580 411523 : for (i1=1; i1<=N; i1++) gel(c,i1) = gel(x,i*N+i1);
3581 51296 : gel(res,i+1) = rnfeltabstorel(rnf,basistoalg(nf,c));
3582 : }
3583 17094 : return gerepilecopy(av, res);
3584 : }
3585 :
3586 : static GEN
3587 2135 : algnattoalg_csa(GEN al, GEN x)
3588 : {
3589 2135 : pari_sp av = avma;
3590 2135 : GEN nf = alg_get_center(al), res, c;
3591 2135 : long d2 = alg_get_dim(al), n = nf_get_degree(nf), i, i1;
3592 2135 : res = zerocol(d2);
3593 2135 : c = zerocol(n);
3594 11032 : for (i=0; i<d2; i++) {
3595 28728 : for (i1=1; i1<=n; i1++) gel(c,i1) = gel(x,i*n+i1);
3596 8897 : gel(res,i+1) = basistoalg(nf,c);
3597 : }
3598 2135 : return gerepilecopy(av, res);
3599 : }
3600 :
3601 : /* assumes al CSA or CYCLIC */
3602 : static GEN
3603 19229 : algnattoalg(GEN al, GEN x)
3604 : {
3605 19229 : switch(alg_type(al))
3606 : {
3607 17094 : case al_CYCLIC: return algnattoalg_cyc(al, x);
3608 2135 : case al_CSA: return algnattoalg_csa(al, x);
3609 : }
3610 : return NULL; /*LCOV_EXCL_LINE*/
3611 : }
3612 :
3613 : static GEN
3614 182 : algalgtobasis_mat(GEN al, GEN x) /* componentwise */
3615 : {
3616 182 : pari_sp av = avma;
3617 : long lx, lxj, i, j;
3618 : GEN res;
3619 182 : lx = lg(x);
3620 182 : res = cgetg(lx, t_MAT);
3621 546 : for (j=1; j<lx; j++) {
3622 364 : lxj = lg(gel(x,j));
3623 364 : gel(res,j) = cgetg(lxj, t_COL);
3624 1092 : for (i=1; i<lxj; i++)
3625 728 : gcoeff(res,i,j) = algalgtobasis(al,gcoeff(x,i,j));
3626 : }
3627 182 : return gerepilecopy(av,res);
3628 : }
3629 : GEN
3630 89329 : algalgtobasis(GEN al, GEN x)
3631 : {
3632 : pari_sp av;
3633 : long tx, ta;
3634 89329 : checkalg(al);
3635 89322 : ta = alg_type(al);
3636 89322 : if (ta != al_CYCLIC && ta != al_CSA) pari_err_TYPE("algalgtobasis [use alginit]", al);
3637 89287 : tx = alg_model(al,x);
3638 89266 : if (tx==al_BASIS) return gcopy(x);
3639 87425 : if (tx==al_MATRIX) return algalgtobasis_mat(al,x);
3640 87243 : av = avma;
3641 87243 : x = algalgtonat(al,x);
3642 87243 : x = RgM_RgC_mul(alg_get_invbasis(al),x);
3643 87243 : return gerepileupto(av, x);
3644 : }
3645 :
3646 : /*
3647 : Quaternion algebras special case:
3648 : al = (L/F, sigma, b) with L quadratic
3649 : > v^2-a: i = v
3650 : > v^2+A*v+B: i = 2*v+A: i^2 = a = A^2-4*B
3651 : al ~ (a,b)_F
3652 : */
3653 : /* We could improve efficiency, but these functions are just for convenience. */
3654 : GEN
3655 280 : algquattobasis(GEN al, GEN x)
3656 : {
3657 280 : pari_sp av = avma;
3658 : GEN L1, L2, pol, A, x2, nf;
3659 : long v, i, ta;
3660 280 : checkalg(al);
3661 273 : if (alg_is_asq(al))
3662 : {
3663 84 : x = algalgtonat(al,x);
3664 84 : x = RgM_RgC_mul(alg_get_invbasis(al),x);
3665 84 : return gerepileupto(av,x);
3666 : }
3667 189 : ta = alg_type(al);
3668 189 : if (ta != al_CYCLIC || alg_get_degree(al)!=2)
3669 28 : pari_err_TYPE("algquattobasis [not a quaternion algebra]", al);
3670 161 : if (typ(x)!=t_COL && typ(x)!=t_VEC) pari_err_TYPE("algquattobasis", x);
3671 154 : if (lg(x)!=5) pari_err_DIM("algquattobasis [quaternions have 4 components]");
3672 147 : nf = alg_get_center(al);
3673 147 : x2 = cgetg(5, t_COL);
3674 707 : for (i=1; i<=4; i++) gel(x2,i) = basistoalg(nf, gel(x,i));
3675 140 : gel(x2,4) = gneg(gel(x2,4));
3676 140 : pol = alg_get_splitpol(al);
3677 140 : v = varn(pol);
3678 140 : A = gel(pol,3); /* coeff of v^1 */
3679 140 : if (gequal0(A))
3680 : {
3681 : /* i = v */
3682 91 : L1 = deg1pol_shallow(gel(x2,2), gel(x2,1), v);
3683 91 : L2 = deg1pol_shallow(gel(x2,4), gel(x2,3), v);
3684 : }
3685 : else
3686 : {
3687 : /* i = 2*v+A */
3688 49 : L1 = deg1pol_shallow(gshift(gel(x2,2),1),
3689 49 : gadd(gel(x2,1),gmul(A,gel(x2,2))), v);
3690 49 : L2 = deg1pol_shallow(gshift(gel(x2,4),1),
3691 49 : gadd(gel(x2,3),gmul(A,gel(x2,4))), v);
3692 : }
3693 140 : return gerepileupto(av, algalgtobasis(al,mkcol2(L1,L2)));
3694 : }
3695 : GEN
3696 126 : algbasistoquat(GEN al, GEN x)
3697 : {
3698 126 : pari_sp av = avma;
3699 : GEN pol, A, x2, q;
3700 : long v, ta;
3701 126 : checkalg(al);
3702 119 : if (alg_is_asq(al))
3703 : {
3704 21 : x = RgM_RgC_mul(alg_get_basis(al),x);
3705 21 : x = algnattoalg(al,x);
3706 21 : return gerepileupto(av, x);
3707 : }
3708 98 : ta = alg_type(al);
3709 98 : if (ta != al_CYCLIC || alg_get_degree(al)!=2)
3710 28 : pari_err_TYPE("algbasistoquat [not a quaternion algebra]", al);
3711 70 : pol = alg_get_splitpol(al);
3712 70 : v = varn(pol);
3713 70 : A = gel(pol,3); /* coeff of v^1 */
3714 70 : x2 = algbasistoalg(al, x);
3715 56 : x2 = lift0(x2, v);
3716 56 : q = cgetg(5, t_COL);
3717 56 : if (gequal0(A))
3718 : {
3719 : /* v = i */
3720 42 : gel(q,1) = polcoef_i(gel(x2,1),0,v);
3721 42 : gel(q,2) = polcoef_i(gel(x2,1),1,v);
3722 42 : gel(q,3) = polcoef_i(gel(x2,2),0,v);
3723 42 : gel(q,4) = polcoef_i(gel(x2,2),1,v);
3724 42 : gel(q,4) = gneg(gel(q,4));
3725 : }
3726 : else
3727 : {
3728 : /* v = (i-A)/2 */
3729 14 : gel(q,2) = gshift(polcoef_i(gel(x2,1),1,v),-1);
3730 14 : gel(q,1) = gsub(polcoef_i(gel(x2,1),0,v), gmul(A,gel(q,2)));
3731 14 : gel(q,4) = gneg(gshift(polcoef_i(gel(x2,2),1,v),-1));
3732 14 : gel(q,3) = gadd(polcoef_i(gel(x2,2),0,v),gmul(A,gel(q,4)));
3733 : }
3734 56 : return gerepilecopy(av, q);
3735 : }
3736 : GEN
3737 98 : algisquatalg(GEN al)
3738 : {
3739 98 : pari_sp av = avma;
3740 : GEN pol, a;
3741 : long ta;
3742 98 : checkalg(al);
3743 91 : ta = alg_type(al);
3744 91 : if (ta == al_REAL && algreal_dim(al)==4)
3745 7 : return gerepilecopy(av, mkvec2(gen_m1,gen_m1));
3746 84 : if (alg_is_asq(al))
3747 21 : return gerepilecopy(av, mkvec2(gmael3(al,6,1,1),gmael3(al,6,1,2)));
3748 63 : if (ta != al_CYCLIC || alg_get_degree(al)!=2) return gc_const(av, gen_0);
3749 35 : pol = alg_get_splitpol(al);
3750 35 : if (gequal0(gel(pol,3))) a = gneg(gel(pol,2)); /* coeffs of v^1 and v^0 */
3751 7 : else a = RgX_disc(pol);
3752 35 : return gerepilecopy(av, mkvec2(a,lift_shallow(alg_get_b(al))));
3753 : }
3754 :
3755 : static GEN
3756 119 : algbasistoalg_mat(GEN al, GEN x) /* componentwise */
3757 : {
3758 119 : long j, lx = lg(x);
3759 119 : GEN res = cgetg(lx, t_MAT);
3760 357 : for (j=1; j<lx; j++) {
3761 238 : long i, lxj = lg(gel(x,j));
3762 238 : gel(res,j) = cgetg(lxj, t_COL);
3763 714 : for (i=1; i<lxj; i++) gcoeff(res,i,j) = algbasistoalg(al,gcoeff(x,i,j));
3764 : }
3765 119 : return res;
3766 : }
3767 : GEN
3768 3409 : algbasistoalg(GEN al, GEN x)
3769 : {
3770 : pari_sp av;
3771 : long tx, ta;
3772 3409 : checkalg(al);
3773 3409 : ta = alg_type(al);
3774 3409 : if (ta != al_CYCLIC && ta != al_CSA) pari_err_TYPE("algbasistoalg [use alginit]", al);
3775 3388 : tx = alg_model(al,x);
3776 3374 : if (tx==al_ALGEBRAIC) return gcopy(x);
3777 3241 : if (tx==al_MATRIX) return algbasistoalg_mat(al,x);
3778 3122 : av = avma;
3779 3122 : x = RgM_RgC_mul(alg_get_basis(al),x);
3780 3122 : x = algnattoalg(al,x);
3781 3122 : return gerepileupto(av, x);
3782 : }
3783 :
3784 : static GEN
3785 4466 : R_random(GEN b)
3786 : {
3787 4466 : pari_sp av = avma;
3788 4466 : long prec = realprec(b);
3789 4466 : GEN z = randomr(prec); shiftr_inplace(z, 1);
3790 4466 : return gerepileuptoleaf(av, mulrr(b,addsr(-1, z)));
3791 : }
3792 : static GEN
3793 182 : C_random(GEN b)
3794 : {
3795 182 : retmkcomplex(R_random(b), R_random(b));
3796 : }
3797 : static GEN
3798 980 : H_random(GEN b)
3799 : {
3800 980 : GEN res = cgetg(5, t_COL);
3801 : long i;
3802 4900 : for (i=1; i<=4; i++) gel(res,i) = R_random(b);
3803 980 : return res;
3804 : }
3805 : GEN
3806 20104 : algrandom(GEN al, GEN b)
3807 : {
3808 20104 : GEN res = NULL, p, N;
3809 : long i, n;
3810 20104 : checkalg(al);
3811 20090 : if (alg_type(al)==al_REAL)
3812 : {
3813 1365 : if (typ(b) != t_REAL) pari_err_TYPE("algrandom",b);
3814 1358 : if (signe(b) < 0) pari_err_DOMAIN("algrandom", "b", "<", gen_0, b);
3815 1351 : switch(alg_get_absdim(al))
3816 : {
3817 182 : case 1: res = R_random(b); break;
3818 182 : case 2: res = C_random(b); break;
3819 980 : case 4: res = H_random(b); break;
3820 7 : default: pari_err_TYPE("algrandom [apply alginit]", al);
3821 : }
3822 1344 : return res;
3823 : }
3824 18725 : if (typ(b) != t_INT) pari_err_TYPE("algrandom",b);
3825 18718 : if (signe(b) < 0) pari_err_DOMAIN("algrandom", "b", "<", gen_0, b);
3826 18711 : n = alg_get_absdim(al);
3827 18711 : N = addiu(shifti(b,1), 1); /* left on stack */
3828 18711 : p = alg_get_char(al); if (!signe(p)) p = NULL;
3829 18711 : res = cgetg(n+1,t_COL);
3830 168385 : for (i = 1; i <= n; i++)
3831 : {
3832 149674 : pari_sp av = avma;
3833 149674 : GEN t = subii(randomi(N),b);
3834 149674 : if (p) t = modii(t, p);
3835 149674 : gel(res,i) = gerepileuptoint(av, t);
3836 : }
3837 18711 : return res;
3838 : }
3839 :
3840 : static GEN
3841 84 : H_poleval(GEN pol, GEN x)
3842 : {
3843 84 : pari_sp av = avma;
3844 : GEN res;
3845 : long i;
3846 84 : switch (H_model(x))
3847 : {
3848 21 : case H_SCALAR: return RgX_cxeval(pol, x, NULL);
3849 49 : case H_QUATERNION: break;
3850 7 : default: pari_err_TYPE("H_poleval", x);
3851 : }
3852 :
3853 49 : res = zerocol(4);
3854 231 : for (i=lg(pol)-1; i>1; i--)
3855 : {
3856 182 : gel(res,1) = gadd(gel(res,1), gel(pol,i));
3857 182 : if (i>2) res = H_mul(x, res);
3858 : }
3859 :
3860 49 : return gerepilecopy(av,res);
3861 : }
3862 :
3863 : /* Assumes pol has coefficients in the same ring as the COL x; x either
3864 : * in basis or algebraic form or [x,mx] where mx is the mult. table of x.
3865 : TODO more general version: pol with coeffs in center and x in basis form */
3866 : GEN
3867 29434 : algpoleval(GEN al, GEN pol, GEN x)
3868 : {
3869 29434 : pari_sp av = avma;
3870 29434 : GEN p, mx = NULL, res, c;
3871 29434 : long i, xalg = 0;
3872 29434 : if (typ(pol) != t_POL) pari_err_TYPE("algpoleval", pol);
3873 29420 : checkalg(al);
3874 29420 : if (alg_type(al)==al_REAL) return H_poleval(pol,x);
3875 29336 : p = alg_get_char(al);
3876 29336 : if (typ(x) == t_VEC)
3877 : {
3878 10122 : if (lg(x) != 3) pari_err_TYPE("algpoleval [vector must be of length 2]", x);
3879 10115 : mx = gel(x,2);
3880 10115 : x = gel(x,1);
3881 10115 : if (typ(mx)!=t_MAT || !gequal(x,gel(mx,1)))
3882 21 : pari_err_TYPE("algpoleval [mx must be the multiplication table of x]", mx);
3883 : }
3884 : else
3885 : {
3886 19214 : switch(alg_model(al,x))
3887 : {
3888 14 : case al_ALGEBRAIC: mx = algalgmultable(al,x); xalg=1; break;
3889 19186 : case al_BASIS:
3890 19186 : case al_TRIVIAL: mx = algbasismultable(al,x); break;
3891 7 : default: pari_err_TYPE("algpoleval", x);
3892 : }
3893 : }
3894 29294 : res = zerocol(lg(mx)-1);
3895 29294 : if (signe(p)) {
3896 86405 : for (i=lg(pol)-1; i>1; i--)
3897 : {
3898 62879 : gel(res,1) = Fp_add(gel(res,1), gel(pol,i), p);
3899 62879 : if (i>2) res = FpM_FpC_mul(mx, res, p);
3900 : }
3901 : }
3902 : else {
3903 29323 : for (i=lg(pol)-1; i>1; i--)
3904 : {
3905 23555 : c = gel(pol,i);
3906 23555 : if (xalg || is_rational_t(typ(c))) gel(res,1) = gadd(gel(res,1), c);
3907 420 : else res = RgC_add(res, algeltfromnf_i(al,c));
3908 23555 : if (i>2) res = RgM_RgC_mul(mx, res);
3909 : }
3910 : }
3911 29294 : return gerepileupto(av, res);
3912 : }
3913 :
3914 : static GEN
3915 98 : H_invol(GEN x)
3916 : {
3917 98 : pari_sp av = avma;
3918 : long tx;
3919 : GEN cx;
3920 98 : if (!x) return gerepileupto(av,diagonal(mkvec4(gen_1,gen_m1,gen_m1,gen_m1)));
3921 35 : tx = H_model(x);
3922 28 : if (tx == H_SCALAR) return gconj(x);
3923 14 : cx = gneg(x);
3924 14 : gel(cx,1) = gcopy(gel(x,1));
3925 14 : return gerepileupto(av, cx);
3926 : }
3927 :
3928 : GEN
3929 308 : alginvol(GEN al, GEN x)
3930 : {
3931 308 : pari_sp av = avma;
3932 : GEN invol;
3933 308 : checkalg(al);
3934 294 : if (!x && al) return gerepileupto(av, alg_get_invol(al));
3935 175 : if (alg_type(al)==al_REAL) return H_invol(x);
3936 133 : x = algalgtobasis(al, x);
3937 126 : invol = alg_get_invol(al);
3938 126 : if (typ(invol)!=t_MAT)
3939 7 : pari_err_DOMAIN("alginvol [al does not contain an involution]", "invol", "=", gen_0, invol);
3940 119 : return gerepileupto(av, RgM_RgC_mul(invol,x));
3941 : }
3942 :
3943 : GEN
3944 112 : algskolemnoether(GEN al, GEN a, GEN fa)
3945 : {
3946 112 : pari_sp av = avma;
3947 112 : long c = 0, i, ta;
3948 : GEN M, K, b;
3949 112 : checkalg(al);
3950 105 : ta = alg_type(al);
3951 105 : if (ta!=al_CYCLIC && ta!=al_CSA) pari_err_TYPE("algskolemnoether"
3952 : " [al: apply alginit()]", al);
3953 91 : if (typ(a) != t_VEC) a = mkvec(a);
3954 91 : if (typ(fa) != t_VEC) fa = mkvec(fa);
3955 91 : if (lg(a) != lg(fa)) pari_err_DIM("algskolemnoether [lg(a) != lg(fa)]");
3956 84 : if (lg(a) == 1) return gerepileupto(av, col_ei(alg_get_absdim(al),1));
3957 :
3958 : /* compute space K of b s.t. b*a_i == fa_i*b for all i */
3959 77 : M = cgetg(lg(a),t_COL);
3960 154 : for (i=1; i<lg(a); i++) gel(M,i) = RgM_sub(algrightmultable(al,gel(a,i)),
3961 91 : algleftmultable(al,gel(fa,i)));
3962 56 : M = shallowmatconcat(M);
3963 56 : K = QM_ker(M);
3964 :
3965 : /* find invertible element in K */
3966 56 : if (lg(K)==1) pari_err(e_MISC, "no solution in algskolemnoether"
3967 : " [check simplicity and homomorphism assumptions]");
3968 49 : b = gel(K,1);
3969 1449 : while (!algisinv(al, b, NULL))
3970 : {
3971 1407 : b = gadd(b, gel(K,1+random_Fl(lg(K)-1)));
3972 1407 : c++;
3973 1407 : if (c > 200) pari_err(e_MISC, "probable infinite loop in algskolemnoether"
3974 : " (the subalgebra is probably not simple)");
3975 : }
3976 42 : return gerepileupto(av, b);
3977 : }
3978 :
3979 : /** GRUNWALD-WANG **/
3980 : /*
3981 : Song Wang's PhD thesis (pdf pages)
3982 : p.25 definition of chi_b. K^Ker(chi_b) = K(b^(1/m))
3983 : p.26 bound on the conductor (also Cohen adv. GTM 193 p.166)
3984 : p.21 & p.34 description special case, also on wikipedia:
3985 : http://en.wikipedia.org/wiki/Grunwald%E2%80%93Wang_theorem#Special_fields
3986 : p.77 Kummer case
3987 : */
3988 :
3989 : /* n > 0. Is n = 2^k ? */
3990 : static int
3991 385 : uispow2(ulong n) { return !(n &(n-1)); }
3992 :
3993 : static GEN
3994 441 : get_phi0(GEN bnr, GEN Lpr, GEN Ld, GEN pl, long *pr, long *pn)
3995 : {
3996 441 : const long NTRY = 10; /* FIXME: magic constant */
3997 441 : const long n = (lg(Ld)==1)? 2: vecsmall_max(Ld);
3998 441 : GEN S = bnr_get_cyc(bnr);
3999 : GEN Sst, G, globGmod, loc, X, Rglob, Rloc, H, U, Lconj;
4000 : long i, j, r, nbfrob, nbloc, nz, t;
4001 :
4002 441 : *pn = n;
4003 441 : *pr = r = lg(S)-1;
4004 441 : if (!r) return NULL;
4005 392 : Sst = cgetg(r+1, t_VECSMALL); /* Z/n-dual */
4006 1715 : for (i=1; i<=r; i++) Sst[i] = ugcdiu(gel(S,i), n);
4007 392 : if (Sst[1] != n) return NULL;
4008 385 : Lconj = NULL;
4009 385 : nbloc = nbfrob = lg(Lpr)-1;
4010 385 : if (uispow2(n))
4011 : {
4012 266 : long l = lg(pl), k = 0;
4013 266 : GEN real = cgetg(l, t_VECSMALL);
4014 994 : for (i = 1; i < l; i++)
4015 728 : if (pl[i] == -1) real[++k] = i;
4016 266 : if (k)
4017 : {
4018 266 : GEN nf = bnr_get_nf(bnr), I = bid_get_fact(bnr_get_bid(bnr));
4019 266 : GEN v, y, C = idealchineseinit(bnr, I);
4020 266 : long r1 = nf_get_r1(nf), n = nbrows(I);
4021 266 : nbloc += k;
4022 266 : Lconj = cgetg(k+1, t_VEC);
4023 266 : v = const_vecsmall(r1, 1);
4024 266 : y = const_vec(n, gen_1);
4025 728 : for (i = 1; i <= k; i++)
4026 : {
4027 462 : v[real[i]] = -1; gel(Lconj,i) = idealchinese(nf, mkvec2(C,v), y);
4028 462 : v[real[i]] = 1;
4029 : }
4030 : }
4031 : }
4032 385 : globGmod = cgetg(r+1,t_MAT);
4033 385 : G = cgetg(r+1,t_VECSMALL);
4034 1701 : for (i = 1; i <= r; i++)
4035 : {
4036 1316 : G[i] = n / Sst[i]; /* pairing between S and Sst */
4037 1316 : gel(globGmod,i) = cgetg(nbloc+1,t_VECSMALL);
4038 : }
4039 :
4040 : /* compute images of Frobenius elements (and complex conjugation) */
4041 385 : loc = cgetg(nbloc+1,t_VECSMALL);
4042 868 : for (i = 1; i <= nbloc; i++)
4043 : {
4044 : long L;
4045 651 : if (i <= nbfrob)
4046 : {
4047 322 : X = gel(Lpr, i);
4048 322 : L = Ld[i];
4049 : }
4050 : else
4051 : { /* X = 1 (mod f), sigma_i(x) < 0, positive at all other real places */
4052 329 : X = gel(Lconj, i-nbfrob);
4053 329 : L = 2;
4054 : }
4055 651 : X = ZV_to_Flv(isprincipalray(bnr,X), n);
4056 2499 : for (nz=0,j=1; j<=r; j++)
4057 : {
4058 1848 : ulong c = (X[j] * G[j]) % L;
4059 1848 : ucoeff(globGmod,i,j) = c;
4060 1848 : if (c) nz = 1;
4061 : }
4062 651 : if (!nz) return NULL;
4063 483 : loc[i] = L;
4064 : }
4065 :
4066 : /* try some random elements in the dual */
4067 217 : Rglob = cgetg(r+1,t_VECSMALL);
4068 461 : for (t=0; t<NTRY; t++) {
4069 1615 : for (j = 1; j <= r; j++) Rglob[j] = random_Fl(Sst[j]);
4070 454 : Rloc = zm_zc_mul(globGmod,Rglob);
4071 1119 : for (i = 1; i <= nbloc; i++)
4072 909 : if (Rloc[i] % loc[i] == 0) break;
4073 454 : if (i > nbloc) return zv_to_ZV(Rglob);
4074 : }
4075 :
4076 : /* try to realize some random elements of the product of the local duals */
4077 7 : H = ZM_hnfall_i(shallowconcat(zm_to_ZM(globGmod),
4078 : diagonal_shallow(zv_to_ZV(loc))), &U, 2);
4079 : /* H,U nbloc x nbloc */
4080 7 : Rloc = cgetg(nbloc+1,t_COL);
4081 77 : for (t = 0; t < NTRY; t++)
4082 : { /* nonzero random coordinate */ /* TODO add special case ? */
4083 560 : for (i = 1; i <= nbloc; i++) gel(Rloc,i) = stoi(1 + random_Fl(loc[i]-1));
4084 70 : Rglob = hnf_invimage(H, Rloc);
4085 70 : if (Rglob)
4086 : {
4087 0 : Rglob = ZM_ZC_mul(U,Rglob);
4088 0 : return vecslice(Rglob,1,r);
4089 : }
4090 : }
4091 7 : return NULL;
4092 : }
4093 :
4094 : static GEN
4095 441 : bnrgwsearch(GEN bnr, GEN Lpr, GEN Ld, GEN pl)
4096 : {
4097 441 : pari_sp av = avma;
4098 : long n, r;
4099 441 : GEN phi0 = get_phi0(bnr,Lpr,Ld,pl, &r,&n), gn, v, H,U;
4100 441 : if (!phi0) return gc_const(av, gen_0);
4101 210 : gn = stoi(n);
4102 : /* compute kernel of phi0 */
4103 210 : v = ZV_extgcd(vec_append(phi0, gn));
4104 210 : U = vecslice(gel(v,2), 1,r);
4105 210 : H = ZM_hnfmodid(rowslice(U, 1,r), gn);
4106 210 : return gerepileupto(av, H);
4107 : }
4108 :
4109 : GEN
4110 210 : bnfgwgeneric(GEN bnf, GEN Lpr, GEN Ld, GEN pl, long var)
4111 : {
4112 210 : pari_sp av = avma;
4113 210 : const long n = (lg(Ld)==1)? 2: vecsmall_max(Ld);
4114 : forprime_t S;
4115 210 : GEN bnr = NULL, ideal = gen_1, nf, dec, H = gen_0, finf, pol;
4116 : ulong ell, p;
4117 : long deg, i, degell;
4118 210 : (void)uisprimepower(n, &ell);
4119 210 : nf = bnf_get_nf(bnf);
4120 210 : deg = nf_get_degree(nf);
4121 210 : degell = ugcd(deg,ell-1);
4122 210 : finf = cgetg(lg(pl),t_VEC);
4123 546 : for (i=1; i<lg(pl); i++) gel(finf,i) = pl[i]==-1 ? gen_1 : gen_0;
4124 :
4125 210 : u_forprime_init(&S, 2, ULONG_MAX);
4126 903 : while ((p = u_forprime_next(&S))) {
4127 903 : if (Fl_powu(p % ell, degell, ell) != 1) continue; /* ell | p^deg-1 ? */
4128 434 : dec = idealprimedec(nf, utoipos(p));
4129 784 : for (i=1; i<lg(dec); i++) {
4130 560 : GEN pp = gel(dec,i);
4131 560 : if (RgV_isin(Lpr,pp)) continue;
4132 : /* TODO also accept the prime ideals at which there is a condition
4133 : * (use local Artin)? */
4134 497 : if (smodis(idealnorm(nf,pp),ell) != 1) continue; /* ell | N(pp)-1 ? */
4135 441 : ideal = idealmul(bnf,ideal,pp);
4136 : /* TODO: give factorization ? */
4137 441 : bnr = Buchray(bnf, mkvec2(ideal,finf), nf_INIT);
4138 441 : H = bnrgwsearch(bnr,Lpr,Ld,pl);
4139 441 : if (H != gen_0)
4140 : {
4141 210 : pol = rnfkummer(bnr,H,nf_get_prec(nf));
4142 210 : setvarn(pol, var);
4143 210 : return gerepileupto(av,pol);
4144 : }
4145 : }
4146 : }
4147 : pari_err_BUG("bnfgwgeneric (no suitable p)"); /*LCOV_EXCL_LINE*/
4148 : return NULL;/*LCOV_EXCL_LINE*/
4149 : }
4150 :
4151 : /* pr.p != ell */
4152 : static GEN
4153 1624 : localextdeg(GEN nf, GEN pr, long d, ulong ell, long n)
4154 : {
4155 : GEN modpr, T, p, gen, k;
4156 1624 : if (d == 1) return gen_1;
4157 1610 : k = powuu(ell, Z_lval(subiu(pr_norm(pr),1), ell));
4158 1610 : k = divis(k, n / d);
4159 1610 : modpr = nf_to_Fq_init(nf, &pr, &T, &p);
4160 1610 : (void)Fq_sqrtn(gen_1, k, T, p, &gen);
4161 1610 : return Fq_to_nf(gen, modpr);
4162 : }
4163 : /* pr.p = ell */
4164 : static GEN
4165 175 : localextdegell(GEN nf, GEN pr, GEN E, long d, long n)
4166 : {
4167 : GEN x;
4168 175 : if (d == 1) return gen_1;
4169 168 : x = nfadd(nf, gen_1, pr_get_gen(pr));
4170 168 : return nfpowmodideal(nf, x, stoi(n / d), idealpow(nf, pr, E));
4171 : }
4172 :
4173 : /* Ld[i] must be nontrivial powers of the same prime ell */
4174 : /* pl : -1 at real places at which the extension must ramify, 0 elsewhere */
4175 : GEN
4176 294 : nfgwkummer(GEN nf, GEN Lpr, GEN Ld, GEN pl, long var)
4177 : {
4178 294 : const long n = (lg(Ld)==1)? 2: vecsmall_max(Ld);
4179 : ulong ell;
4180 294 : long i, l = lg(Lpr), v = uisprimepower(n, &ell);
4181 294 : GEN E = cgetg(l, t_COL), y = cgetg(l, t_VEC), fa;
4182 :
4183 2093 : for (i = 1; i < l; i++)
4184 : {
4185 1799 : GEN pr = gel(Lpr,i), p = pr_get_p(pr);
4186 1799 : if (!absequalui(ell, p))
4187 : {
4188 1624 : gel(E, i) = gen_1;
4189 1624 : gel(y, i) = localextdeg(nf, pr, Ld[i], ell, n);
4190 : }
4191 : else
4192 : {
4193 175 : long e = pr_get_e(pr);
4194 175 : gel(E, i) = addui(1 + v*e, divsi(e, subiu(p,1)));
4195 175 : gel(y, i) = localextdegell(nf, pr, gel(E,i), Ld[i], n);
4196 : }
4197 : }
4198 294 : y = factoredextchinese(nf, mkmat2(shallowtrans(Lpr),E), y, pl, &fa);
4199 294 : return gsub(gpowgs(pol_x(var),n), basistoalg(nf, y));
4200 : }
4201 :
4202 : static GEN
4203 1113 : get_vecsmall(GEN v)
4204 : {
4205 1113 : switch(typ(v))
4206 : {
4207 987 : case t_VECSMALL: return v;
4208 119 : case t_VEC: if (RgV_is_ZV(v)) return ZV_to_zv(v);
4209 : }
4210 7 : pari_err_TYPE("nfgrunwaldwang",v);
4211 : return NULL;/*LCOV_EXCL_LINE*/
4212 : }
4213 : GEN
4214 602 : nfgrunwaldwang(GEN nf0, GEN Lpr, GEN Ld, GEN pl, long var)
4215 : {
4216 : ulong n, ell, ell2;
4217 602 : pari_sp av = avma;
4218 : GEN nf, bnf;
4219 : long t, w, i, vnf;
4220 :
4221 602 : if (var < 0) var = 0;
4222 602 : nf = get_nf(nf0,&t);
4223 602 : if (!nf) pari_err_TYPE("nfgrunwaldwang",nf0);
4224 602 : vnf = nf_get_varn(nf);
4225 602 : if (varncmp(var, vnf) >= 0)
4226 7 : pari_err_PRIORITY("nfgrunwaldwang", pol_x(var), ">=", vnf);
4227 595 : if (typ(Lpr) != t_VEC) pari_err_TYPE("nfgrunwaldwang",Lpr);
4228 581 : if (lg(Lpr) != lg(Ld)) pari_err_DIM("nfgrunwaldwang [#Lpr != #Ld]");
4229 574 : if (nf_get_degree(nf)==1) Lpr = shallowcopy(Lpr);
4230 2709 : for (i=1; i<lg(Lpr); i++) {
4231 2142 : GEN pr = gel(Lpr,i);
4232 2142 : if (nf_get_degree(nf)==1 && typ(pr)==t_INT)
4233 77 : gel(Lpr,i) = gel(idealprimedec(nf,pr), 1);
4234 2065 : else checkprid(pr);
4235 : }
4236 567 : if (lg(pl)-1 != nf_get_r1(nf))
4237 7 : pari_err_DOMAIN("nfgrunwaldwang [pl should have r1 components]", "#pl",
4238 7 : "!=", stoi(nf_get_r1(nf)), stoi(lg(pl)-1));
4239 :
4240 560 : Ld = get_vecsmall(Ld);
4241 553 : pl = get_vecsmall(pl);
4242 553 : bnf = get_bnf(nf0,&t);
4243 553 : n = (lg(Ld)==1)? 2: vecsmall_max(Ld);
4244 :
4245 553 : if (!uisprimepower(n, &ell))
4246 7 : pari_err_IMPL("nfgrunwaldwang for non prime-power local degrees (a)");
4247 2646 : for (i=1; i<lg(Ld); i++)
4248 2107 : if (Ld[i]!=1 && (!uisprimepower(Ld[i],&ell2) || ell2!=ell))
4249 7 : pari_err_IMPL("nfgrunwaldwang for non prime-power local degrees (b)");
4250 1393 : for (i=1; i<lg(pl); i++)
4251 861 : if (pl[i]==-1 && ell%2)
4252 7 : pari_err_IMPL("nfgrunwaldwang for non prime-power local degrees (c)");
4253 :
4254 532 : w = bnf? bnf_get_tuN(bnf): itos(gel(nfrootsof1(nf),1));
4255 :
4256 : /* TODO choice between kummer and generic ? Let user choose between speed
4257 : * and size */
4258 532 : if (w%n==0 && lg(Ld)>1)
4259 294 : return gerepileupto(av, nfgwkummer(nf,Lpr,Ld,pl,var));
4260 238 : if (ell==n)
4261 : {
4262 210 : if (!bnf) bnf = Buchall(nf, nf_FORCE, 0);
4263 210 : return gerepileupto(av, bnfgwgeneric(bnf,Lpr,Ld,pl,var));
4264 : }
4265 28 : pari_err_IMPL("nfgrunwaldwang for nonprime degree");
4266 : return NULL; /*LCOV_EXCL_LINE*/
4267 : }
4268 :
4269 : /** HASSE INVARIANTS **/
4270 :
4271 : /* TODO long -> ulong + uel */
4272 : static GEN
4273 1484 : hasseconvert(GEN H, long n)
4274 : {
4275 : GEN h, c;
4276 : long i, l;
4277 1484 : switch(typ(H)) {
4278 1323 : case t_VEC:
4279 1323 : l = lg(H); h = cgetg(l,t_VECSMALL);
4280 1323 : if (l == 1) return h;
4281 1197 : c = gel(H,1);
4282 1197 : if (typ(c) == t_VEC && l == 3)
4283 476 : return mkvec2(gel(H,1),hasseconvert(gel(H,2),n));
4284 3374 : for (i=1; i<l; i++)
4285 : {
4286 2681 : c = gel(H,i);
4287 2681 : switch(typ(c)) {
4288 910 : case t_INT: break;
4289 7 : case t_INTMOD:
4290 7 : c = gel(c,2); break;
4291 1743 : case t_FRAC :
4292 1743 : c = gmulgs(c,n);
4293 1743 : if (typ(c) == t_INT) break;
4294 7 : pari_err_DOMAIN("hasseconvert [degree should be a denominator of the invariant]", "denom(h)", "ndiv", stoi(n), Q_denom(gel(H,i)));
4295 21 : default : pari_err_TYPE("Hasse invariant", c);
4296 : }
4297 2653 : h[i] = smodis(c,n);
4298 : }
4299 693 : return h;
4300 154 : case t_VECSMALL: return H;
4301 : }
4302 7 : pari_err_TYPE("Hasse invariant", H);
4303 : return NULL;/*LCOV_EXCL_LINE*/
4304 : }
4305 :
4306 : /* assume f >= 2 */
4307 : static long
4308 546 : cyclicrelfrob0(GEN nf, GEN aut, GEN pr, GEN q, long f, long g)
4309 : {
4310 546 : GEN T, p, a, b, modpr = nf_to_Fq_init(nf,&pr,&T,&p);
4311 : long s;
4312 :
4313 546 : a = pol_x(nf_get_varn(nf));
4314 546 : b = galoisapply(nf, aut, modpr_genFq(modpr));
4315 546 : b = nf_to_Fq(nf, b, modpr);
4316 1575 : for (s = 0; !ZX_equal(a, b); s++) a = Fq_pow(a, q, T, p);
4317 546 : return g * Fl_inv(s, f); /* < n */
4318 : }
4319 :
4320 : static long
4321 3352 : cyclicrelfrob(GEN rnf, GEN auts, GEN pr)
4322 : {
4323 3352 : pari_sp av = avma;
4324 3352 : long f,g,frob, n = rnf_get_degree(rnf);
4325 3352 : GEN P = rnfidealprimedec(rnf, pr);
4326 :
4327 3352 : if (pr_get_e(gel(P,1)) > pr_get_e(pr))
4328 0 : pari_err_DOMAIN("cyclicrelfrob","e(PR/pr)",">",gen_1,pr);
4329 3352 : g = lg(P) - 1;
4330 3352 : f = n / g;
4331 :
4332 3352 : if (f <= 2) frob = g % n;
4333 : else {
4334 546 : GEN nf2, PR = gel(P,1);
4335 546 : GEN autabs = rnfeltreltoabs(rnf,gel(auts,g));
4336 546 : nf2 = obj_check(rnf,rnf_NFABS);
4337 546 : autabs = nfadd(nf2, autabs, gmul(rnf_get_k(rnf), rnf_get_alpha(rnf)));
4338 546 : frob = cyclicrelfrob0(nf2, autabs, PR, pr_norm(pr), f, g);
4339 : }
4340 3352 : return gc_long(av, frob);
4341 : }
4342 :
4343 : static long
4344 1127 : localhasse(GEN rnf, GEN cnd, GEN pl, GEN auts, GEN b, long k)
4345 : {
4346 1127 : pari_sp av = avma;
4347 : long v, m, h, lfa, frob, n, i;
4348 : GEN previous, y, pr, nf, q, fa;
4349 1127 : nf = rnf_get_nf(rnf);
4350 1127 : n = rnf_get_degree(rnf);
4351 1127 : pr = gcoeff(cnd,k,1);
4352 1127 : v = nfval(nf, b, pr);
4353 1127 : m = lg(cnd)>1 ? nbrows(cnd) : 0;
4354 :
4355 : /* add the valuation of b to the conductor... */
4356 1127 : previous = gcoeff(cnd,k,2);
4357 1127 : gcoeff(cnd,k,2) = addis(previous, v);
4358 :
4359 1127 : y = const_vec(m, gen_1);
4360 1127 : gel(y,k) = b;
4361 : /* find a factored element y congruent to b mod pr^(vpr(b)+vpr(cnd)) and to 1 mod the conductor. */
4362 1127 : y = factoredextchinese(nf, cnd, y, pl, &fa);
4363 1127 : h = 0;
4364 1127 : lfa = nbrows(fa);
4365 : /* sum of all Hasse invariants of (rnf/nf,aut,y) is 0, Hasse invariants at q!=pr are easy, Hasse invariant at pr is the same as for al=(rnf/nf,aut,b). */
4366 2211 : for (i=1; i<=lfa; i++) {
4367 1084 : q = gcoeff(fa,i,1);
4368 1084 : if (cmp_prime_ideal(pr,q)) {
4369 1007 : frob = cyclicrelfrob(rnf, auts, q);
4370 1007 : frob = Fl_mul(frob,umodiu(gcoeff(fa,i,2),n),n);
4371 1007 : h = Fl_add(h,frob,n);
4372 : }
4373 : }
4374 : /* ...then restore it. */
4375 1127 : gcoeff(cnd,k,2) = previous;
4376 1127 : return gc_long(av, Fl_neg(h,n));
4377 : }
4378 :
4379 : static GEN
4380 1386 : allauts(GEN rnf, GEN aut)
4381 : {
4382 1386 : long n = rnf_get_degree(rnf), i;
4383 1386 : GEN pol = rnf_get_pol(rnf), vaut;
4384 1386 : if (n==1) n=2;
4385 1386 : vaut = cgetg(n,t_VEC);
4386 1386 : aut = lift_shallow(rnfbasistoalg(rnf,aut));
4387 1386 : if (typ(aut) != t_POL || varn(pol) != varn(aut))
4388 0 : pari_err_TYPE("alg_cyclic", aut);
4389 1386 : gel(vaut,1) = aut;
4390 1841 : for (i=1; i<n-1; i++)
4391 455 : gel(vaut,i+1) = RgX_rem(poleval(gel(vaut,i), aut), pol);
4392 1386 : return vaut;
4393 : }
4394 :
4395 : static GEN
4396 413 : clean_factor(GEN fa)
4397 : {
4398 413 : GEN P2,E2, P = gel(fa,1), E = gel(fa,2);
4399 413 : long l = lg(P), i, j = 1;
4400 413 : P2 = cgetg(l, t_COL);
4401 413 : E2 = cgetg(l, t_COL);
4402 2849 : for (i = 1;i < l; i++)
4403 2436 : if (signe(gel(E,i))) {
4404 728 : gel(P2,j) = gel(P,i);
4405 728 : gel(E2,j) = gel(E,i); j++;
4406 : }
4407 413 : setlg(P2,j);
4408 413 : setlg(E2,j); return mkmat2(P2,E2);
4409 : }
4410 :
4411 : /* shallow concat x[1],...x[nx],y[1], ... y[ny], returning a t_COL. To be
4412 : * used when we do not know whether x,y are t_VEC or t_COL */
4413 : static GEN
4414 826 : colconcat(GEN x, GEN y)
4415 : {
4416 826 : long i, lx = lg(x), ly = lg(y);
4417 826 : GEN z=cgetg(lx+ly-1, t_COL);
4418 4396 : for (i=1; i<lx; i++) z[i] = x[i];
4419 2128 : for (i=1; i<ly; i++) z[lx+i-1]= y[i];
4420 826 : return z;
4421 : }
4422 :
4423 : /* return v(x) at all primes in listpr, replace x by cofactor */
4424 : static GEN
4425 1799 : nfmakecoprime(GEN nf, GEN *px, GEN listpr)
4426 : {
4427 1799 : long j, l = lg(listpr);
4428 1799 : GEN x1, x = *px, L = cgetg(l, t_COL);
4429 :
4430 1799 : if (typ(x) != t_MAT)
4431 : { /* scalar, divide at the end (fast valuation) */
4432 1547 : x1 = NULL;
4433 6293 : for (j=1; j<l; j++)
4434 : {
4435 4746 : GEN pr = gel(listpr,j), e;
4436 4746 : long v = nfval(nf, x, pr);
4437 4746 : e = stoi(v); gel(L,j) = e;
4438 6538 : if (v) x1 = x1? idealmulpowprime(nf, x1, pr, e)
4439 1792 : : idealpow(nf, pr, e);
4440 : }
4441 1547 : if (x1) x = idealdivexact(nf, idealhnf(nf,x), x1);
4442 : }
4443 : else
4444 : { /* HNF, divide as we proceed (reduce size) */
4445 525 : for (j=1; j<l; j++)
4446 : {
4447 273 : GEN pr = gel(listpr,j);
4448 273 : long v = idealval(nf, x, pr);
4449 273 : gel(L,j) = stoi(v);
4450 273 : if (v) x = idealmulpowprime(nf, x, pr, stoi(-v));
4451 : }
4452 : }
4453 1799 : *px = x; return L;
4454 : }
4455 :
4456 : /* Caveat: factorizations are not sorted wrt cmp_prime_ideal: Lpr comes first */
4457 : static GEN
4458 413 : computecnd(GEN rnf, GEN Lpr)
4459 : {
4460 : GEN id, nf, fa, Le, P,E;
4461 413 : long n = rnf_get_degree(rnf);
4462 :
4463 413 : nf = rnf_get_nf(rnf);
4464 413 : id = rnf_get_idealdisc(rnf);
4465 413 : Le = nfmakecoprime(nf, &id, Lpr);
4466 413 : fa = idealfactor(nf, id); /* part of D_{L/K} coprime with Lpr */
4467 413 : P = colconcat(Lpr,gel(fa,1));
4468 413 : E = colconcat(Le, gel(fa,2));
4469 413 : fa = mkmat2(P, gdiventgs(E, eulerphiu(n)));
4470 413 : return mkvec2(fa, clean_factor(fa));
4471 : }
4472 :
4473 : /* h >= 0 */
4474 : static void
4475 77 : nextgen(GEN gene, long h, GEN* gens, GEN* hgens, long* ngens, long* curgcd) {
4476 77 : long nextgcd = ugcd(h,*curgcd);
4477 77 : if (nextgcd == *curgcd) return;
4478 77 : (*ngens)++;
4479 77 : gel(*gens,*ngens) = gene;
4480 77 : gel(*hgens,*ngens) = utoi(h);
4481 77 : *curgcd = nextgcd;
4482 77 : return;
4483 : }
4484 :
4485 : static int
4486 140 : dividesmod(long d, long h, long n) { return !(h%cgcd(d,n)); }
4487 :
4488 : /* ramified prime with nontrivial Hasse invariant */
4489 : static GEN
4490 77 : localcomplete(GEN rnf, GEN pl, GEN cnd, GEN auts, long j, long n, long h, long* v)
4491 : {
4492 : GEN nf, gens, hgens, pr, modpr, T, p, sol, U, b, gene, randg, pu;
4493 : long ngens, i, d, np, d1, d2, hg, dnf, vcnd, curgcd;
4494 77 : nf = rnf_get_nf(rnf);
4495 77 : pr = gcoeff(cnd,j,1);
4496 77 : np = umodiu(pr_norm(pr), n);
4497 77 : dnf = nf_get_degree(nf);
4498 77 : vcnd = itos(gcoeff(cnd,j,2));
4499 77 : ngens = 13+dnf;
4500 77 : gens = zerovec(ngens);
4501 77 : hgens = zerovec(ngens);
4502 77 : *v = 0;
4503 77 : curgcd = 0;
4504 77 : ngens = 0;
4505 :
4506 77 : if (!uisprime(n)) {
4507 0 : gene = pr_get_gen(pr);
4508 0 : hg = localhasse(rnf, cnd, pl, auts, gene, j);
4509 0 : nextgen(gene, hg, &gens, &hgens, &ngens, &curgcd);
4510 : }
4511 :
4512 77 : if (ugcd(np,n) != 1) { /* GCD(Np,n) != 1 */
4513 77 : pu = idealprincipalunits(nf,pr,vcnd);
4514 77 : pu = abgrp_get_gen(pu);
4515 154 : for (i=1; i<lg(pu) && !dividesmod(curgcd,h,n); i++) {
4516 77 : gene = gel(pu,i);
4517 77 : hg = localhasse(rnf, cnd, pl, auts, gene, j);
4518 77 : nextgen(gene, hg, &gens, &hgens, &ngens, &curgcd);
4519 : }
4520 : }
4521 :
4522 77 : d = ugcd(np-1,n);
4523 77 : if (d != 1) { /* GCD(Np-1,n) != 1 */
4524 14 : modpr = nf_to_Fq_init(nf, &pr, &T, &p);
4525 14 : while (!dividesmod(curgcd,h,n)) { /* TODO gener_FpXQ_local */
4526 0 : if (T==NULL) randg = randomi(p);
4527 0 : else randg = random_FpX(degpol(T), varn(T),p);
4528 :
4529 0 : if (!gequal0(randg) && !gequal1(randg)) {
4530 0 : gene = Fq_to_nf(randg, modpr);
4531 0 : hg = localhasse(rnf, cnd, pl, auts, gene, j);
4532 0 : nextgen(gene, hg, &gens, &hgens, &ngens, &curgcd);
4533 : }
4534 : }
4535 : }
4536 :
4537 77 : setlg(gens,ngens+1);
4538 77 : setlg(hgens,ngens+1);
4539 :
4540 77 : sol = ZV_extgcd(hgens);
4541 77 : U = ZV_to_Flv(gmael(sol,2,ngens), n);
4542 77 : d = itou(gel(sol,1));
4543 77 : d1 = ugcd(d, n);
4544 77 : d2 = d / d1;
4545 77 : d = Fl_mul(h / d1, Fl_inv(d2,n), n);
4546 77 : if (d != 1) U = Flv_Fl_mul(U, d, n);
4547 154 : for (i = 1, b = gen_1; i <= ngens; i++)
4548 77 : if (U[i]) b = nfmul(nf, b, nfpow_u(nf, gel(gens,i), U[i]));
4549 77 : *v = U[1]; return b;
4550 : }
4551 :
4552 : static int
4553 1002 : testsplits(GEN data, GEN fa)
4554 : {
4555 1002 : GEN rnf = gel(data,1), forbid = gel(data,2), P = gel(fa,1), E = gel(fa,2);
4556 1002 : long i, n, l = lg(P);
4557 :
4558 1467 : for (i = 1; i < l; i++)
4559 : {
4560 956 : GEN pr = gel(P,i);
4561 956 : if (tablesearch(forbid, pr, &cmp_prime_ideal)) return 0;
4562 : }
4563 511 : n = rnf_get_degree(rnf);
4564 742 : for (i = 1; i < l; i++)
4565 : {
4566 329 : long e = itos(gel(E,i)) % n;
4567 329 : if (e)
4568 : {
4569 314 : GEN L = rnfidealprimedec(rnf, gel(P,i));
4570 314 : long g = lg(L) - 1;
4571 314 : if ((e * g) % n) return 0;
4572 : }
4573 : }
4574 413 : return 1;
4575 : }
4576 :
4577 : /* remove entries with Hasse invariant 0 */
4578 : static GEN
4579 854 : hassereduce(GEN hf)
4580 : {
4581 854 : GEN pr,h, PR = gel(hf,1), H = gel(hf,2);
4582 854 : long i, j, l = lg(PR);
4583 :
4584 854 : pr= cgetg(l, t_VEC);
4585 854 : h = cgetg(l, t_VECSMALL);
4586 4788 : for (i = j = 1; i < l; i++)
4587 3934 : if (H[i]) {
4588 3598 : gel(pr,j) = gel(PR,i);
4589 3598 : h[j] = H[i]; j++;
4590 : }
4591 854 : setlg(pr,j);
4592 854 : setlg(h,j); return mkvec2(pr,h);
4593 : }
4594 :
4595 : static void
4596 1036 : alg_insert_quatconj(GEN al)
4597 : {
4598 : GEN aut, nf, rnf, nfabs, gene, absaut;
4599 : long d;
4600 1036 : aut = alg_get_aut(al);
4601 1036 : d = alg_get_absdim(al) / 4;
4602 1036 : nf = alg_get_center(al);
4603 1036 : rnf = alg_get_splittingfield(al);
4604 1036 : nfabs = rnf_build_nfabs(rnf, nf_get_prec(nf));
4605 1036 : gene = lift_shallow(rnfeltabstorel(rnf,pol_x(nf_get_varn(nfabs))));
4606 1036 : absaut = rnfeltreltoabs(rnf,poleval(gene,aut));
4607 1036 : gmael(al,6,2) = shallowmatconcat(mkmat22(
4608 : nfgaloismatrix(nfabs,absaut),
4609 : gen_0,
4610 : gen_0,
4611 : gneg(matid(2*d))
4612 : ));
4613 1036 : }
4614 :
4615 : /* rnf complete */
4616 : static GEN
4617 413 : alg_complete0(GEN rnf, GEN aut, GEN hf, GEN hi, long flag)
4618 : {
4619 413 : pari_sp av = avma;
4620 : GEN nf, pl, pl2, cnd, prcnd, cnds, y, Lpr, auts, b, fa, data, hfe;
4621 : GEN forbid, al, ind, perm;
4622 : long D, n, d, i, j, l;
4623 413 : nf = rnf_get_nf(rnf);
4624 413 : n = rnf_get_degree(rnf);
4625 413 : d = nf_get_degree(nf);
4626 413 : D = d*n*n;
4627 413 : checkhasse(nf,hf,hi,n);
4628 413 : hf = hassereduce(hf);
4629 413 : Lpr = gel(hf,1);
4630 413 : hfe = gel(hf,2);
4631 :
4632 413 : auts = allauts(rnf,aut);
4633 :
4634 413 : pl = leafcopy(hi); /* conditions on the final b */
4635 413 : pl2 = leafcopy(hi); /* conditions for computing local Hasse invariants */
4636 413 : l = lg(pl); ind = cgetg(l, t_VECSMALL);
4637 1036 : for (i = j = 1; i < l; i++)
4638 623 : if (hi[i]) { pl[i] = -1; pl2[i] = 1; } else ind[j++] = i;
4639 413 : setlg(ind, j);
4640 413 : y = nfpolsturm(nf, rnf_get_pol(rnf), ind);
4641 777 : for (i = 1; i < j; i++)
4642 364 : if (!signe(gel(y,i))) { pl[ind[i]] = 1; pl2[ind[i]] = 1; }
4643 :
4644 413 : cnds = computecnd(rnf,Lpr);
4645 413 : prcnd = gel(cnds,1);
4646 413 : cnd = gel(cnds,2);
4647 413 : y = cgetg(lgcols(prcnd),t_VEC);
4648 413 : forbid = vectrunc_init(lg(Lpr));
4649 2198 : for (i=j=1; i<lg(Lpr); i++)
4650 : {
4651 1785 : GEN pr = gcoeff(prcnd,i,1), yi;
4652 1785 : long v, e = itou( gcoeff(prcnd,i,2) );
4653 1785 : if (!e) {
4654 1708 : long frob = cyclicrelfrob(rnf,auts,pr), f1 = ugcd(frob,n);
4655 1708 : vectrunc_append(forbid, pr);
4656 1708 : yi = gen_0;
4657 1708 : v = ((hfe[i]/f1) * Fl_inv(frob/f1,n)) % n;
4658 : }
4659 : else
4660 77 : yi = localcomplete(rnf, pl2, cnd, auts, j++, n, hfe[i], &v);
4661 1785 : gel(y,i) = yi;
4662 1785 : gcoeff(prcnd,i,2) = stoi(e + v);
4663 : }
4664 1064 : for (; i<lgcols(prcnd); i++) gel(y,i) = gen_1;
4665 413 : gen_sort_inplace(forbid, (void*)&cmp_prime_ideal, &cmp_nodata, NULL);
4666 413 : data = mkvec2(rnf,forbid);
4667 413 : b = factoredextchinesetest(nf,prcnd,y,pl,&fa,data,testsplits);
4668 :
4669 413 : al = cgetg(12, t_VEC);
4670 413 : gel(al,10)= gen_0; /* must be set first */
4671 413 : gel(al,1) = rnf;
4672 413 : gel(al,2) = auts;
4673 413 : gel(al,3) = basistoalg(nf,b);
4674 413 : gel(al,4) = hi;
4675 : /* add primes | disc or b with trivial Hasse invariant to hf */
4676 413 : Lpr = gel(prcnd,1); y = b;
4677 413 : (void)nfmakecoprime(nf, &y, Lpr);
4678 413 : Lpr = shallowconcat(Lpr, gel(idealfactor(nf,y), 1));
4679 413 : settyp(Lpr,t_VEC);
4680 413 : hf = shallowconcat(hfe, const_vecsmall(lg(Lpr)-lg(hfe), 0));
4681 413 : perm = gen_indexsort(Lpr, (void*)&cmp_prime_ideal, &cmp_nodata);
4682 413 : gel(al,5) = mkvec2(vecpermute(Lpr,perm), vecsmallpermute(hf,perm));
4683 413 : gel(al,6) = mkvec2(gen_0,gen_0);
4684 413 : gel(al,7) = matid(D);
4685 413 : gel(al,8) = matid(D); /* TODO modify 7, 8 et 9 once LLL added */
4686 413 : gel(al,9) = algnatmultable(al,D);
4687 413 : gel(al,11)= algtracebasis(al);
4688 :
4689 413 : if (n==2) alg_insert_quatconj(al);
4690 413 : if (flag & al_MAXORD) al = alg_maximal_primes(al, prV_primes(Lpr));
4691 413 : return gerepilecopy(av, al);
4692 : }
4693 :
4694 : GEN
4695 0 : alg_complete(GEN rnf, GEN aut, GEN hf, GEN hi, long flag)
4696 : {
4697 0 : long n = rnf_get_degree(rnf);
4698 0 : rnfcomplete(rnf);
4699 0 : return alg_complete0(rnf, aut, hasseconvert(hf,n), hasseconvert(hi,n), flag);
4700 : }
4701 :
4702 : void
4703 2254 : checkhasse(GEN nf, GEN hf, GEN hi, long n)
4704 : {
4705 : GEN Lpr, Lh;
4706 : long i, sum;
4707 2254 : if (typ(hf) != t_VEC || lg(hf) != 3) pari_err_TYPE("checkhasse [hf]", hf);
4708 2247 : Lpr = gel(hf,1);
4709 2247 : Lh = gel(hf,2);
4710 2247 : if (typ(Lpr) != t_VEC) pari_err_TYPE("checkhasse [Lpr]", Lpr);
4711 2247 : if (typ(Lh) != t_VECSMALL) pari_err_TYPE("checkhasse [Lh]", Lh);
4712 2247 : if (typ(hi) != t_VECSMALL) pari_err_TYPE("checkhasse [hi]", hi);
4713 2247 : if ((nf && lg(hi) != nf_get_r1(nf)+1))
4714 7 : pari_err_DOMAIN("checkhasse [hi should have r1 components]","#hi","!=",stoi(nf_get_r1(nf)),stoi(lg(hi)-1));
4715 2240 : if (lg(Lpr) != lg(Lh))
4716 7 : pari_err_DIM("checkhasse [Lpr and Lh should have same length]");
4717 9660 : for (i=1; i<lg(Lpr); i++) checkprid(gel(Lpr,i));
4718 2233 : if (lg(gen_sort_uniq(Lpr, (void*)cmp_prime_ideal, cmp_nodata)) < lg(Lpr))
4719 7 : pari_err(e_MISC, "error in checkhasse [duplicate prime ideal]");
4720 2226 : sum = 0;
4721 9639 : for (i=1; i<lg(Lh); i++) sum = (sum+Lh[i])%n;
4722 5278 : for (i=1; i<lg(hi); i++) {
4723 3066 : if (hi[i] && 2*hi[i] != n) pari_err_DOMAIN("checkhasse", "Hasse invariant at real place [must be 0 or 1/2]", "!=", n%2? gen_0 : stoi(n/2), stoi(hi[i]));
4724 3052 : sum = (sum+hi[i])%n;
4725 : }
4726 2212 : if (sum<0) sum = n+sum;
4727 2212 : if (sum != 0)
4728 7 : pari_err_DOMAIN("checkhasse","sum(Hasse invariants)","!=",gen_0,Lh);
4729 2205 : }
4730 :
4731 : static GEN
4732 511 : hassecoprime(GEN hf, GEN hi, long n)
4733 : {
4734 511 : pari_sp av = avma;
4735 : long l, i, j, lk, inv;
4736 : GEN fa, P,E, res, hil, hfl;
4737 511 : hi = hasseconvert(hi, n);
4738 497 : hf = hasseconvert(hf, n);
4739 476 : checkhasse(NULL,hf,hi,n);
4740 434 : fa = factoru(n);
4741 434 : P = gel(fa,1); l = lg(P);
4742 434 : E = gel(fa,2);
4743 434 : res = cgetg(l,t_VEC);
4744 875 : for (i=1; i<l; i++) {
4745 441 : lk = upowuu(P[i],E[i]);
4746 441 : inv = Fl_invsafe((n/lk)%lk, lk);
4747 441 : hil = gcopy(hi);
4748 441 : hfl = gcopy(hf);
4749 :
4750 441 : if (P[i] == 2)
4751 896 : for (j=1; j<lg(hil); j++) hil[j] = hi[j]==0 ? 0 : lk/2;
4752 : else
4753 203 : for (j=1; j<lg(hil); j++) hil[j] = 0;
4754 2590 : for (j=1; j<lgcols(hfl); j++) gel(hfl,2)[j] = (gel(hf,2)[j]*inv)%lk;
4755 441 : hfl = hassereduce(hfl);
4756 441 : gel(res,i) = mkvec3(hfl,hil,utoi(lk));
4757 : }
4758 :
4759 434 : return gerepilecopy(av, res);
4760 : }
4761 :
4762 : /* no garbage collection */
4763 : static GEN
4764 140 : genefrob(GEN nf, GEN gal, GEN r)
4765 : {
4766 : long i;
4767 140 : GEN g = identity_perm(nf_get_degree(nf)), fa = Z_factor(r), p, pr, frob;
4768 217 : for (i=1; i<lgcols(fa); i++) {
4769 77 : p = gcoeff(fa,i,1);
4770 77 : pr = idealprimedec(nf, p);
4771 77 : pr = gel(pr,1);
4772 77 : frob = idealfrobenius(nf, gal, pr);
4773 77 : g = perm_mul(g, perm_pow(frob, gcoeff(fa,i,2)));
4774 : }
4775 140 : return g;
4776 : }
4777 :
4778 : static GEN
4779 413 : rnfcycaut(GEN rnf)
4780 : {
4781 413 : GEN nf2 = obj_check(rnf, rnf_NFABS);
4782 : GEN L, alpha, pol, salpha, s, sj, polabs, k, X, pol0, nf;
4783 : long i, d, j;
4784 413 : d = rnf_get_degree(rnf);
4785 413 : L = galoisconj(nf2,NULL);
4786 413 : alpha = lift_shallow(rnf_get_alpha(rnf));
4787 413 : pol = rnf_get_pol(rnf);
4788 413 : k = rnf_get_k(rnf);
4789 413 : polabs = rnf_get_polabs(rnf);
4790 413 : nf = rnf_get_nf(rnf);
4791 413 : pol0 = nf_get_pol(nf);
4792 413 : X = RgX_rem(pol_x(varn(pol0)), pol0);
4793 :
4794 : /* TODO check mod prime of degree 1 */
4795 624 : for (i=1; i<lg(L); i++) {
4796 624 : s = gel(L,i);
4797 624 : salpha = RgX_RgXQ_eval(alpha,s,polabs);
4798 624 : if (!gequal(alpha,salpha)) continue;
4799 :
4800 553 : s = lift_shallow(rnfeltabstorel(rnf,s));
4801 553 : sj = s = gsub(s, gmul(k,X));
4802 1092 : for (j=1; !gequal0(gsub(sj,pol_x(varn(s)))); j++)
4803 539 : sj = RgX_RgXQ_eval(sj,s,pol);
4804 553 : if (j<d) continue;
4805 413 : return s;
4806 : }
4807 : return NULL; /*LCOV_EXCL_LINE*/
4808 : }
4809 :
4810 : /* returns the smallest prime not in P */
4811 : static GEN
4812 84 : extraprime(GEN P)
4813 : {
4814 : forprime_t T;
4815 : GEN p;
4816 84 : forprime_init(&T, gen_2, NULL);
4817 98 : while ((p = forprime_next(&T))) if (!ZV_search(P, p)) break;
4818 84 : return p;
4819 : }
4820 :
4821 : /* true nf */
4822 : GEN
4823 525 : alg_hasse(GEN nf, long n, GEN hf, GEN hi, long var, long flag)
4824 : {
4825 525 : pari_sp av = avma;
4826 525 : GEN primary, al = gen_0, al2, rnf, hil, hfl, Ld, pl, pol, Lpr, aut, Lpr2, Ld2;
4827 : long i, lk, j, maxdeg;
4828 525 : dbg_printf(1)("alg_hasse\n");
4829 525 : if (n<=1) pari_err_DOMAIN("alg_hasse", "degree", "<=", gen_1, stoi(n));
4830 511 : primary = hassecoprime(hf, hi, n);
4831 854 : for (i=1; i<lg(primary); i++) {
4832 441 : lk = itos(gmael(primary,i,3));
4833 441 : hfl = gmael(primary,i,1);
4834 441 : hil = gmael(primary,i,2);
4835 441 : checkhasse(nf, hfl, hil, lk);
4836 434 : dbg_printf(1)("alg_hasse: i=%d hf=%Ps hi=%Ps lk=%d\n", i, hfl, hil, lk);
4837 :
4838 434 : if (lg(gel(hfl,1))>1 || lk%2==0) {
4839 427 : maxdeg = 1;
4840 427 : Lpr = gel(hfl,1);
4841 427 : Ld = gcopy(gel(hfl,2));
4842 2226 : for (j=1; j<lg(Ld); j++)
4843 : {
4844 1799 : Ld[j] = lk/ugcd(lk,Ld[j]);
4845 1799 : maxdeg = maxss(Ld[j],maxdeg);
4846 : }
4847 427 : pl = leafcopy(hil);
4848 1064 : for (j=1; j<lg(pl); j++) if(pl[j])
4849 : {
4850 259 : pl[j] = -1;
4851 259 : maxdeg = maxss(maxdeg,2);
4852 : }
4853 :
4854 427 : Lpr2 = Lpr;
4855 427 : Ld2 = Ld;
4856 427 : if (maxdeg<lk)
4857 : {
4858 154 : if (maxdeg==1 && lk==2 && lg(pl)>1) pl[1] = -1;
4859 : else
4860 : {
4861 84 : GEN p = extraprime(prV_primes(Lpr));
4862 84 : Lpr2 = vec_append(Lpr2, idealprimedec_galois(nf, p));
4863 84 : Ld2 = vecsmall_append(Ld2, lk);
4864 : }
4865 : }
4866 :
4867 427 : dbg_printf(2)("alg_hasse: calling nfgrunwaldwang Lpr=%Ps Pd=%Ps pl=%Ps\n",
4868 : Lpr, Ld, pl);
4869 427 : pol = nfgrunwaldwang(nf, Lpr2, Ld2, pl, var);
4870 413 : dbg_printf(2)("alg_hasse: calling rnfinit(%Ps)\n", pol);
4871 413 : rnf = rnfinit0(nf,pol,1);
4872 413 : dbg_printf(2)("alg_hasse: computing automorphism\n");
4873 413 : aut = rnfcycaut(rnf);
4874 413 : dbg_printf(2)("alg_hasse: calling alg_complete\n");
4875 413 : al2 = alg_complete0(rnf, aut, hfl, hil, flag);
4876 : }
4877 7 : else al2 = alg_matrix(nf, lk, var, flag);
4878 :
4879 420 : if (i==1) al = al2;
4880 7 : else al = algtensor(al,al2,flag);
4881 : }
4882 413 : return gerepilecopy(av,al);
4883 : }
4884 :
4885 : /** CYCLIC ALGEBRA WITH GIVEN HASSE INVARIANTS **/
4886 :
4887 : /* no garbage collection */
4888 : static GEN
4889 140 : subcycloindep(GEN nf, long n, long v, GEN *pr)
4890 : {
4891 : pari_sp av;
4892 : forprime_t S;
4893 : ulong p;
4894 140 : u_forprime_arith_init(&S, 1, ULONG_MAX, 1, n);
4895 140 : av = avma;
4896 147 : while ((p = u_forprime_next(&S)))
4897 : {
4898 147 : ulong r = pgener_Fl(p);
4899 147 : GEN pol = galoissubcyclo(utoipos(p), utoipos(Fl_powu(r,n,p)), 0, v);
4900 147 : GEN fa = nffactor(nf, pol);
4901 147 : if (lgcols(fa) == 2) { *pr = utoipos(r); return pol; }
4902 7 : set_avma(av);
4903 : }
4904 : pari_err_BUG("subcycloindep (no suitable prime = 1(mod n))"); /*LCOV_EXCL_LINE*/
4905 : *pr = NULL; return NULL; /*LCOV_EXCL_LINE*/
4906 : }
4907 :
4908 : GEN
4909 147 : alg_matrix(GEN nf, long n, long v, long flag)
4910 : {
4911 147 : pari_sp av = avma;
4912 : GEN pol, gal, rnf, cyclo, g, r, aut;
4913 147 : dbg_printf(1)("alg_matrix\n");
4914 147 : if (n<=0) pari_err_DOMAIN("alg_matrix", "n", "<=", gen_0, stoi(n));
4915 140 : pol = subcycloindep(nf, n, v, &r);
4916 140 : rnf = rnfinit(nf, pol);
4917 140 : cyclo = nfinit(pol, nf_get_prec(nf));
4918 140 : gal = galoisinit(cyclo, NULL);
4919 140 : g = genefrob(cyclo,gal,r);
4920 140 : aut = galoispermtopol(gal,g);
4921 140 : return gerepileupto(av, alg_cyclic(rnf, aut, gen_1, flag));
4922 : }
4923 :
4924 : static GEN
4925 28 : alg_hilbert_asquare(GEN nf, GEN a, GEN sa, GEN b, long v, long flag)
4926 : {
4927 : GEN mt, al, ord, z1, z2, den, invol;
4928 28 : long d = nf_get_degree(nf), i;
4929 28 : mt = mkvec4(
4930 : matid(4),
4931 : mkmat4(
4932 : mkcol4(gen_0,gen_1,gen_0,gen_0),
4933 : mkcol4(a,gen_0,gen_0,gen_0),
4934 : mkcol4(gen_0,gen_0,gen_0,gen_1),
4935 : mkcol4(gen_0,gen_0,a,gen_0)
4936 : ),
4937 : mkmat4(
4938 : mkcol4(gen_0,gen_0,gen_1,gen_0),
4939 : mkcol4(gen_0,gen_0,gen_0,gen_m1),
4940 : mkcol4(b,gen_0,gen_0,gen_0),
4941 : mkcol4(gen_0,gneg(b),gen_0,gen_0)
4942 : ),
4943 : mkmat4(
4944 : mkcol4(gen_0,gen_0,gen_0,gen_1),
4945 : mkcol4(gen_0,gen_0,gneg(a),gen_0),
4946 : mkcol4(gen_0,b,gen_0,gen_0),
4947 : mkcol4(gneg(gmul(a,b)),gen_0,gen_0,gen_0)
4948 : )
4949 : );
4950 28 : al = alg_csa_table(nf, mt, v, al_NOSPLITTING);
4951 :
4952 : /* set trivial Hasse invariants */
4953 28 : gel(al,4) = zero_zv(nf_get_r1(nf));
4954 28 : gel(al,5) = mkvec2(cgetg(1,t_VEC),cgetg(1,t_VECSMALL));
4955 :
4956 : /* remember special case */
4957 28 : sa = basistoalg(nf,sa);
4958 28 : gmael(al,6,1) = mkvec3(a,b,sa);
4959 28 : invol = matid(4*d);
4960 196 : for (i=d+1; i<lg(invol); i++) gcoeff(invol,i,i) = gen_m1;
4961 28 : gmael(al,6,2) = invol;
4962 :
4963 28 : if (flag & al_MAXORD)
4964 : {
4965 28 : ord = cgetg(4,t_VEC);
4966 :
4967 28 : z1 = mkfracss(1,2); /* 1/2 */
4968 28 : z2 = gmul2n(ginv(sa),-1); /* 1/(2*sa) */
4969 : /* (1+i/sa)/2 */
4970 28 : gel(ord,1) = algleftmultable(al,mkcol4(z1,z2,gen_0,gen_0));
4971 : /* (j-ij/sa)/2 */
4972 28 : gel(ord,2) = algleftmultable(al,mkcol4(gen_0,gen_0,z1,gneg(z2)));
4973 28 : z1 = basistoalg(nf,nfdiv(nf,z1,b));
4974 28 : z2 = basistoalg(nf,nfdiv(nf,z2,b));
4975 : /* (j/b + ij/(b*sa))/2 */
4976 28 : gel(ord,3) = algleftmultable(al,mkcol4(gen_0,gen_0,z1,z2));
4977 :
4978 : /* multiply by nf.zk == d first vectors of natural basis */
4979 112 : for (i=1; i<=3; i++) gel(ord,i) = vecslice(gel(ord,i),1,d);
4980 :
4981 28 : ord = shallowmatconcat(ord);
4982 28 : ord = Q_remove_denom(ord, &den);
4983 28 : ord = hnfmodid(ord, den);
4984 28 : ord = ZM_Z_div(ord, den);
4985 28 : al = alg_change_overorder_shallow(al, ord);
4986 : }
4987 : /* could take splitting field == nf */
4988 28 : computesplitting(al, 2, v, flag);
4989 28 : return al;
4990 : }
4991 :
4992 : GEN
4993 658 : alg_hilbert(GEN nf, GEN a, GEN b, long v, long flag)
4994 : {
4995 658 : pari_sp av = avma;
4996 : GEN rnf, aut, rnfpol, sa;
4997 658 : dbg_printf(1)("alg_hilbert\n");
4998 658 : if (gequal0(a)) pari_err_DOMAIN("alg_hilbert", "a", "=", gen_0, a);
4999 651 : if (gequal0(b)) pari_err_DOMAIN("alg_hilbert", "b", "=", gen_0, b);
5000 644 : if (!isint1(Q_denom(algtobasis(nf,a))))
5001 7 : pari_err_DOMAIN("alg_hilbert", "denominator(a)", "!=", gen_1,a);
5002 637 : if (!isint1(Q_denom(algtobasis(nf,b))))
5003 7 : pari_err_DOMAIN("alg_hilbert", "denominator(b)", "!=", gen_1,b);
5004 630 : if (nfissquare(nf,a,&sa))
5005 28 : return gerepilecopy(av, alg_hilbert_asquare(nf,a,sa,b,v,flag));
5006 :
5007 602 : if (v < 0) v = 0;
5008 602 : rnfpol = deg2pol_shallow(gen_1, gen_0, gneg(basistoalg(nf,a)), v);
5009 602 : if (!(flag & al_FACTOR)) rnfpol = mkvec2(rnfpol, stoi(1<<20));
5010 602 : rnf = rnfinit(nf, rnfpol);
5011 602 : aut = gneg(pol_x(v));
5012 602 : return gerepileupto(av, alg_cyclic(rnf, aut, b, flag));
5013 : }
5014 :
5015 : /* shortcut for alg_hasse in quaternion case */
5016 : static GEN
5017 77 : alg_quatramif(GEN nf, GEN Lpr, GEN hi, long var, long flag)
5018 : {
5019 77 : pari_sp av = avma;
5020 77 : GEN hf = mkvec2(Lpr, const_vecsmall(lg(Lpr)-1,1));
5021 77 : return gerepileupto(av, alg_hasse(nf, 2, hf, hi, var, flag));
5022 : }
5023 :
5024 : /* return a structure representing the algebra of real numbers */
5025 : static GEN
5026 28 : mk_R()
5027 : {
5028 28 : pari_sp av = avma;
5029 : GEN al;
5030 28 : al = zerovec(11);
5031 28 : gel(al,1) = stor(1, LOWDEFAULTPREC);
5032 28 : gel(al,2) = mkvec(gel(al,1));
5033 28 : gel(al,3) = gen_1;
5034 28 : gel(al,4) = mkvecsmall(0);
5035 28 : gel(al,6) = mkvec2(gen_0,matid(1));
5036 28 : gel(al,8) = gel(al,7) = matid(1);
5037 28 : gel(al,9) = mkvec(matid(1));
5038 28 : return gerepilecopy(av,al);
5039 : }
5040 : /* return a structure representing the algebra of complex numbers */
5041 : static GEN
5042 21 : mk_C()
5043 : {
5044 21 : pari_sp av = avma;
5045 : GEN al, I;
5046 21 : al = zerovec(11);
5047 21 : I = gen_I();
5048 21 : gel(al,1) = I;
5049 21 : gel(al,2) = mkvec(I);
5050 21 : gel(al,3) = gen_1;
5051 21 : gel(al,4) = cgetg(1,t_VECSMALL);
5052 21 : gel(al,6) = mkvec2(gen_0,mkmat22(gen_1,gen_0,gen_0,gen_m1));
5053 21 : gel(al,8) = gel(al,7) = matid(2);
5054 21 : gel(al,9) = mkvec2(
5055 : matid(2),
5056 : mkmat22(gen_0,gen_m1,gen_1,gen_0)
5057 : );
5058 21 : return gerepilecopy(av,al);
5059 : }
5060 : /* return a structure representing the Hamilton quaternion algebra */
5061 : static GEN
5062 56 : mk_H()
5063 : {
5064 56 : pari_sp av = avma;
5065 : GEN al, I;
5066 56 : al = zerovec(11);
5067 56 : I = gen_I();
5068 56 : gel(al,1) = I;
5069 56 : gel(al,2) = mkvec(gconj(I));
5070 56 : gel(al,3) = gen_m1;
5071 56 : gel(al,4) = mkvecsmall(1);
5072 56 : gel(al,6) = mkvec2(gen_0, H_invol(NULL));
5073 56 : gel(al,8) = gel(al,7) = matid(4);
5074 56 : gel(al,9) = mkvec4(
5075 : matid(4),
5076 : H_tomatrix(I,1),
5077 : H_tomatrix(mkcol4(gen_0,gen_0,gen_1,gen_0),1),
5078 : H_tomatrix(mkcol4(gen_0,gen_0,gen_0,gen_1),1)
5079 : );
5080 56 : return gerepilecopy(av,al);
5081 : }
5082 :
5083 : GEN
5084 1995 : alginit(GEN A, GEN B, long v, long flag)
5085 : {
5086 : long w;
5087 1995 : if (typ(A) == t_COMPLEX) return mk_C();
5088 1974 : if (typ(A) == t_REAL)
5089 : {
5090 91 : if (is_scalar_t(typ(B)) && gequal0(B)) return mk_R();
5091 63 : if (typ(B) == t_FRAC && gequal(B, mkfrac(gen_1,gen_2))) return mk_H();
5092 7 : pari_err_DOMAIN("alginit", "real Hasse invariant [must be 0 or 1/2]", "", NULL, B);
5093 : }
5094 1883 : switch(nftyp(A))
5095 : {
5096 1624 : case typ_NF:
5097 1624 : if (v<0) v=0;
5098 1624 : w = gvar(nf_get_pol(A));
5099 1624 : if (varncmp(v,w)>=0) pari_err_PRIORITY("alginit", pol_x(v), ">=", w);
5100 1610 : switch(typ(B))
5101 : {
5102 : long nB;
5103 140 : case t_INT: return alg_matrix(A, itos(B), v, flag);
5104 1463 : case t_VEC:
5105 1463 : nB = lg(B)-1;
5106 1463 : if (nB && typ(gel(B,1)) == t_MAT) return alg_csa_table(A,B,v,flag);
5107 : switch(nB)
5108 : {
5109 735 : case 2:
5110 735 : if (typ(gel(B,1)) == t_VEC)
5111 77 : return alg_quatramif(A, gel(B,1), gel(B,2), v, flag);
5112 658 : return alg_hilbert(A, gel(B,1), gel(B,2), v, flag);
5113 455 : case 3:
5114 455 : if (typ(gel(B,1))!=t_INT)
5115 7 : pari_err_TYPE("alginit [degree should be an integer]", gel(B,1));
5116 448 : return alg_hasse(A, itos(gel(B,1)), gel(B,2), gel(B,3), v,
5117 : flag);
5118 : }
5119 : }
5120 14 : pari_err_TYPE("alginit", B); break;
5121 :
5122 245 : case typ_RNF:
5123 245 : if (typ(B) != t_VEC || lg(B) != 3) pari_err_TYPE("alginit", B);
5124 231 : return alg_cyclic(A, gel(B,1), gel(B,2), flag);
5125 : }
5126 14 : pari_err_TYPE("alginit", A);
5127 : return NULL;/*LCOV_EXCL_LINE*/
5128 : }
5129 :
5130 : /* assumes al CSA or CYCLIC */
5131 : static GEN
5132 1652 : algnatmultable(GEN al, long D)
5133 : {
5134 : GEN res, x;
5135 : long i;
5136 1652 : res = cgetg(D+1,t_VEC);
5137 17738 : for (i=1; i<=D; i++) {
5138 16086 : x = algnattoalg(al,col_ei(D,i));
5139 16086 : gel(res,i) = algZmultable(al,x);
5140 : }
5141 1652 : return res;
5142 : }
5143 :
5144 168 : static int normfact_is_partial(GEN nf, GEN x, GEN fax)
5145 : {
5146 : long i;
5147 : GEN nfx;
5148 168 : nfx = RgM_shallowcopy(fax);
5149 434 : for (i=1; i<lg(gel(nfx,1)); i++)
5150 266 : gcoeff(nfx,i,1) = idealnorm(nf, gcoeff(nfx,i,1));
5151 168 : nfx = factorback(nfx);
5152 168 : return !gequal(idealnorm(nf, x), nfx);
5153 : }
5154 : /* no garbage collection */
5155 : static void
5156 973 : algcomputehasse(GEN al, long flag)
5157 : {
5158 : int partialfact;
5159 : long r1, k, n, m, m1, m2, m3, i, m23, m123;
5160 : GEN rnf, nf, b, fab, disc2, cnd, fad, auts, pr, pl, perm, y, hi, PH, H, L;
5161 :
5162 973 : rnf = alg_get_splittingfield(al);
5163 973 : n = rnf_get_degree(rnf);
5164 973 : nf = rnf_get_nf(rnf);
5165 973 : b = alg_get_b(al);
5166 973 : r1 = nf_get_r1(nf);
5167 973 : auts = alg_get_auts(al);
5168 973 : (void)alg_get_abssplitting(al);
5169 :
5170 973 : y = nfpolsturm(nf, rnf_get_pol(rnf), NULL);
5171 973 : pl = cgetg(r1+1, t_VECSMALL);
5172 : /* real places where rnf/nf ramifies */
5173 2170 : for (k = 1; k <= r1; k++) pl[k] = !signe(gel(y,k));
5174 :
5175 : /* infinite Hasse invariants */
5176 973 : if (odd(n)) hi = const_vecsmall(r1, 0);
5177 : else
5178 : {
5179 805 : GEN s = nfsign(nf, b);
5180 805 : hi = cgetg(r1+1, t_VECSMALL);
5181 1834 : for (k = 1; k<=r1; k++) hi[k] = (s[k] && pl[k]) ? (n/2) : 0;
5182 : }
5183 973 : gel(al,4) = hi;
5184 :
5185 973 : partialfact = 0;
5186 973 : if (flag & al_FACTOR)
5187 868 : fab = idealfactor(nf, b);
5188 : else {
5189 105 : fab = idealfactor_limit(nf, b, 1<<20);
5190 : /* does not report whether factorisation was partial; check it */
5191 105 : partialfact = normfact_is_partial(nf, b, fab);
5192 : }
5193 :
5194 973 : disc2 = rnf_get_idealdisc(rnf);
5195 973 : L = nfmakecoprime(nf, &disc2, gel(fab,1));
5196 973 : m = lg(L)-1;
5197 : /* m1 = #{pr|b: pr \nmid disc}, m3 = #{pr|b: pr | disc} */
5198 973 : perm = cgetg(m+1, t_VECSMALL);
5199 1771 : for (i=1, m1=m, k=1; k<=m; k++)
5200 798 : if (signe(gel(L,k))) perm[m1--] = k; else perm[i++] = k;
5201 973 : m3 = m - m1;
5202 :
5203 : /* disc2 : factor of disc coprime to b */
5204 973 : if (flag & al_FACTOR)
5205 868 : fad = idealfactor(nf, disc2);
5206 : else {
5207 105 : fad = idealfactor_limit(nf, disc2, 1<<20);
5208 105 : partialfact = partialfact || normfact_is_partial(nf, disc2, fad);
5209 : }
5210 :
5211 : /* if factorisation is partial, do not compute Hasse invariants */
5212 : /* we could compute their sum at composite factors */
5213 973 : if (partialfact)
5214 : {
5215 49 : if (!(flag & al_MAXORD))
5216 : {
5217 42 : gel(al,5) = gen_0;
5218 49 : return;
5219 : }
5220 : /* but transmit list of factors found for computation of maximal order */
5221 7 : PH = prV_primes(shallowconcat(gel(fab,1), gel(fad,1)));
5222 7 : gel(al,5) = mkvec2(PH, gen_0);;
5223 7 : return;
5224 : }
5225 :
5226 : /* m2 : number of prime factors of disc not dividing b */
5227 924 : m2 = nbrows(fad);
5228 924 : m23 = m2+m3;
5229 924 : m123 = m1+m2+m3;
5230 :
5231 : /* initialize the possibly ramified primes (hasse) and the factored conductor of rnf/nf (cnd) */
5232 924 : cnd = zeromatcopy(m23,2);
5233 924 : PH = cgetg(m123+1, t_VEC); /* ramified primes */
5234 924 : H = cgetg(m123+1, t_VECSMALL); /* Hasse invariant */
5235 : /* compute Hasse invariant at primes that are unramified in rnf/nf */
5236 1561 : for (k=1; k<=m1; k++) {/* pr | b, pr \nmid disc */
5237 637 : long frob, e, j = perm[k];
5238 637 : pr = gcoeff(fab,j,1);
5239 637 : e = itos(gcoeff(fab,j,2));
5240 637 : frob = cyclicrelfrob(rnf, auts, pr);
5241 637 : gel(PH,k) = pr;
5242 637 : H[k] = Fl_mul(frob, e, n);
5243 : }
5244 : /* compute Hasse invariant at primes that are ramified in rnf/nf */
5245 1897 : for (k=1; k<=m2; k++) {/* pr \nmid b, pr | disc */
5246 973 : pr = gcoeff(fad,k,1);
5247 973 : gel(PH,k+m1) = pr;
5248 973 : gcoeff(cnd,k,1) = pr;
5249 973 : gcoeff(cnd,k,2) = gcoeff(fad,k,2);
5250 : }
5251 1001 : for (k=1; k<=m3; k++) { /* pr | (b, disc) */
5252 77 : long j = perm[k+m1];
5253 77 : pr = gcoeff(fab,j,1);
5254 77 : gel(PH,k+m1+m2) = pr;
5255 77 : gcoeff(cnd,k+m2,1) = pr;
5256 77 : gcoeff(cnd,k+m2,2) = gel(L,j);
5257 : }
5258 924 : gel(cnd,2) = gdiventgs(gel(cnd,2), eulerphiu(n));
5259 1974 : for (k=1; k<=m23; k++) H[k+m1] = localhasse(rnf, cnd, pl, auts, b, k);
5260 924 : perm = gen_indexsort(PH, (void*)&cmp_prime_ideal, &cmp_nodata);
5261 924 : gel(al,5) = mkvec2(vecpermute(PH,perm),vecsmallpermute(H,perm));
5262 924 : checkhasse(nf, alg_get_hasse_f(al), alg_get_hasse_i(al), n);
5263 : }
5264 :
5265 : static GEN
5266 1442 : alg_maximal_primes(GEN al, GEN P)
5267 : {
5268 1442 : pari_sp av = avma;
5269 1442 : long l = lg(P), i;
5270 4745 : for (i=1; i<l; i++)
5271 : {
5272 3303 : if (i != 1) al = gerepilecopy(av, al);
5273 3303 : al = alg_pmaximal(al,gel(P,i));
5274 : }
5275 1442 : return al;
5276 : }
5277 :
5278 : GEN
5279 987 : alg_cyclic(GEN rnf, GEN aut, GEN b, long flag)
5280 : {
5281 987 : pari_sp av = avma;
5282 : GEN al, nf;
5283 : long D, n, d;
5284 987 : dbg_printf(1)("alg_cyclic\n");
5285 987 : checkrnf(rnf); nf = rnf_get_nf(rnf);
5286 987 : b = nf_to_scalar_or_basis(nf, b);
5287 980 : if (typ(b) == t_FRAC || (typ(b) == t_COL && !RgV_is_ZV(b)))
5288 7 : pari_err_DOMAIN("alg_cyclic", "denominator(b)", "!=", gen_1,b);
5289 :
5290 973 : n = rnf_get_degree(rnf);
5291 973 : d = nf_get_degree(nf);
5292 973 : D = d*n*n;
5293 :
5294 973 : al = cgetg(12,t_VEC);
5295 973 : gel(al,10)= gen_0; /* must be set first */
5296 973 : gel(al,1) = rnf;
5297 973 : gel(al,2) = allauts(rnf, aut);
5298 973 : gel(al,3) = basistoalg(nf,b);
5299 973 : rnf_build_nfabs(rnf, nf_get_prec(nf));
5300 973 : gel(al,6) = mkvec2(gen_0,gen_0);
5301 973 : gel(al,7) = matid(D);
5302 973 : gel(al,8) = matid(D); /* TODO modify 7, 8 et 9 once LLL added */
5303 973 : gel(al,9) = algnatmultable(al,D);
5304 973 : gel(al,11)= algtracebasis(al);
5305 :
5306 973 : if (n==2) alg_insert_quatconj(al);
5307 :
5308 973 : algcomputehasse(al, flag);
5309 :
5310 973 : if (flag & al_MAXORD) {
5311 854 : GEN hf = alg_get_hasse_f(al), pr = gel(hf,1);
5312 854 : if (typ(gel(hf,2)) == t_INT) /* factorisation was partial */
5313 7 : gel(al,5) = gen_0;
5314 847 : else pr = prV_primes(pr);
5315 854 : al = alg_maximal_primes(al, pr);
5316 : }
5317 973 : return gerepilecopy(av, al);
5318 : }
5319 :
5320 : static int
5321 623 : ismaximalsubfield(GEN al, GEN x, GEN d, long v, GEN *pt_minpol)
5322 : {
5323 623 : GEN cp = algbasischarpoly(al, x, v), lead;
5324 623 : if (!ispower(cp, d, pt_minpol)) return 0;
5325 623 : lead = leading_coeff(*pt_minpol);
5326 623 : if (isintm1(lead)) *pt_minpol = gneg(*pt_minpol);
5327 623 : return ZX_is_irred(*pt_minpol);
5328 : }
5329 :
5330 : static GEN
5331 266 : findmaximalsubfield(GEN al, GEN d, long v)
5332 : {
5333 266 : long count, nb=2, i, N = alg_get_absdim(al), n = nf_get_degree(alg_get_center(al));
5334 266 : GEN x, minpol, maxc = gen_1;
5335 :
5336 385 : for (i=n+1; i<=N; i+=n) {
5337 595 : for (count=0; count<2 && i+count<=N; count++) {
5338 476 : x = col_ei(N,i+count);
5339 476 : if (ismaximalsubfield(al, x, d, v, &minpol)) return mkvec2(x,minpol);
5340 : }
5341 : }
5342 :
5343 : while(1) {
5344 147 : x = zerocol(N);
5345 588 : for (count=0; count<nb; count++)
5346 : {
5347 441 : i = random_Fl(N)+1;
5348 441 : gel(x,i) = addiu(randomi(maxc),1);
5349 441 : if (random_bits(1)) gel(x,i) = negi(gel(x,i));
5350 : }
5351 147 : if (ismaximalsubfield(al, x, d, v, &minpol)) return mkvec2(x,minpol);
5352 63 : if (!random_bits(3)) maxc = addiu(maxc,1);
5353 63 : if (nb<N) nb++;
5354 : }
5355 :
5356 : return NULL; /* LCOV_EXCL_LINE */
5357 : }
5358 :
5359 : static GEN
5360 266 : frobeniusform(GEN al, GEN x)
5361 : {
5362 : GEN M, FP, P, Pi;
5363 :
5364 : /* /!\ has to be the *right* multiplication table */
5365 266 : M = algbasisrightmultable(al, x);
5366 :
5367 266 : FP = matfrobenius(M,2,0); /* M = P^(-1)*F*P */
5368 266 : P = gel(FP,2);
5369 266 : Pi = RgM_inv(P);
5370 266 : return mkvec2(P, Pi);
5371 : }
5372 :
5373 : static void
5374 266 : computesplitting(GEN al, long d, long v, long flag)
5375 : {
5376 266 : GEN subf, x, pol, polabs, basis, P, Pi, nf = alg_get_center(al), rnf, Lbasis, Lbasisinv, Q, pows;
5377 266 : long i, n = nf_get_degree(nf), nd = n*d, N = alg_get_absdim(al), j, j2;
5378 :
5379 266 : subf = findmaximalsubfield(al, utoipos(d), v);
5380 266 : x = gel(subf, 1);
5381 266 : polabs = gel(subf, 2);
5382 :
5383 : /* Frobenius form to obtain L-vector space structure */
5384 266 : basis = frobeniusform(al, x);
5385 266 : P = gel(basis, 1);
5386 266 : Pi = gel(basis, 2);
5387 :
5388 : /* construct rnf of splitting field */
5389 266 : pol = gel(nffactor(nf,polabs),1);
5390 301 : for (i=1; i<lg(pol); i++)
5391 : /* select relative factor that vanishes on x */
5392 301 : if (gequal0(algpoleval(al, gel(pol,i), x)))
5393 : {
5394 266 : pol = gel(pol,i);
5395 266 : break;
5396 : }
5397 266 : if (typ(pol) != t_POL) pari_err_BUG("computesplitting (no valid factor)");
5398 266 : if (!(flag & al_FACTOR)) pol = mkvec2(pol, stoi(1<<20));
5399 266 : gel(al,1) = rnf = rnfinit(nf, pol);
5400 : /* since pol is irreducible over Q, we have k=0 in rnf. */
5401 266 : if (!gequal0(rnf_get_k(rnf)))
5402 : pari_err_BUG("computesplitting (k!=0)"); /*LCOV_EXCL_LINE*/
5403 266 : rnf_build_nfabs(rnf, nf_get_prec(nf));
5404 :
5405 : /* construct splitting data */
5406 266 : Lbasis = cgetg(d+1, t_MAT);
5407 735 : for (j=j2=1; j<=d; j++, j2+=nd)
5408 469 : gel(Lbasis,j) = gel(Pi,j2);
5409 :
5410 266 : Q = zeromatcopy(d,N);
5411 266 : pows = pol_x_powers(nd,v);
5412 735 : for (i=j=1; j<=N; j+=nd, i++)
5413 2156 : for (j2=0; j2<nd; j2++)
5414 1687 : gcoeff(Q,i,j+j2) = mkpolmod(gel(pows,j2+1),polabs);
5415 266 : Lbasisinv = RgM_mul(Q,P);
5416 :
5417 266 : gel(al,3) = mkvec3(x,Lbasis,Lbasisinv);
5418 266 : }
5419 :
5420 : /* assumes that mt defines a central simple algebra over nf */
5421 : GEN
5422 294 : alg_csa_table(GEN nf, GEN mt0, long v, long flag)
5423 : {
5424 294 : pari_sp av = avma;
5425 : GEN al, mt;
5426 294 : long n, D, d2 = lg(mt0)-1, d = usqrt(d2);
5427 294 : dbg_printf(1)("alg_csa_table\n");
5428 :
5429 294 : mt = check_relmt(nf,mt0);
5430 280 : if (!mt) pari_err_TYPE("alg_csa_table", mt0);
5431 273 : n = nf_get_degree(nf);
5432 273 : D = n*d2;
5433 273 : if (d*d != d2)
5434 7 : pari_err_DOMAIN("alg_csa_table","(nonsquare) dimension","!=",stoi(d*d),mt);
5435 :
5436 266 : al = cgetg(12, t_VEC);
5437 266 : gel(al,10) = gen_0; /* must be set first */
5438 266 : gel(al,1) = zerovec(12); gmael(al,1,10) = nf;
5439 266 : gmael(al,1,1) = gpowgs(pol_x(0), d); /* placeholder before splitting field */
5440 266 : gel(al,2) = mt;
5441 266 : gel(al,3) = gen_0; /* placeholder */
5442 266 : gel(al,4) = gel(al,5) = gen_0; /* TODO Hasse invariants if flag&al_FACTOR */
5443 266 : gel(al,6) = mkvec2(gen_0,gen_0);
5444 266 : gel(al,7) = matid(D);
5445 266 : gel(al,8) = matid(D);
5446 266 : gel(al,9) = algnatmultable(al,D);
5447 266 : gel(al,11)= algtracebasis(al);
5448 266 : if (flag & al_MAXORD) al = alg_maximal(al);
5449 266 : if (!(flag & al_NOSPLITTING)) computesplitting(al, d, v, flag);
5450 266 : return gerepilecopy(av, al);
5451 : }
5452 :
5453 : static GEN
5454 60349 : algtableinit_i(GEN mt0, GEN p)
5455 : {
5456 : GEN al, mt;
5457 : long i, n;
5458 :
5459 60349 : if (p && !signe(p)) p = NULL;
5460 60349 : mt = check_mt(mt0,p);
5461 60349 : if (!mt) pari_err_TYPE("algtableinit", mt0);
5462 60342 : if (!p && !isint1(Q_denom(mt0)))
5463 7 : pari_err_DOMAIN("algtableinit", "denominator(mt)", "!=", gen_1, mt0);
5464 60335 : n = lg(mt)-1;
5465 60335 : al = cgetg(12, t_VEC);
5466 362010 : for (i=1; i<=5; i++) gel(al,i) = gen_0;
5467 60335 : gel(al,6) = mkvec2(gen_0, gen_0);
5468 60335 : gel(al,7) = matid(n);
5469 60335 : gel(al,8) = matid(n);
5470 60335 : gel(al,9) = mt;
5471 60335 : gel(al,10) = p? p: gen_0;
5472 60335 : gel(al,11) = algtracebasis(al);
5473 60335 : return al;
5474 : }
5475 : GEN
5476 6223 : algtableinit(GEN mt0, GEN p)
5477 : {
5478 6223 : pari_sp av = avma;
5479 6223 : if (p)
5480 : {
5481 6041 : if (typ(p) != t_INT) pari_err_TYPE("algtableinit",p);
5482 6034 : if (signe(p) && !BPSW_psp(p)) pari_err_PRIME("algtableinit",p);
5483 : }
5484 6202 : return gerepilecopy(av, algtableinit_i(mt0, p));
5485 : }
5486 :
5487 : /** REPRESENTATIONS OF GROUPS **/
5488 :
5489 : static GEN
5490 294 : list_to_regular_rep(GEN elts, long n)
5491 : {
5492 : GEN reg, elts2, g;
5493 : long i,j;
5494 294 : elts = shallowcopy(elts);
5495 294 : gen_sort_inplace(elts, (void*)&vecsmall_lexcmp, &cmp_nodata, NULL);
5496 294 : reg = cgetg(n+1, t_VEC);
5497 294 : gel(reg,1) = identity_perm(n);
5498 3857 : for (i=2; i<=n; i++) {
5499 3563 : g = perm_inv(gel(elts,i));
5500 3563 : elts2 = cgetg(n+1, t_VEC);
5501 74543 : for (j=1; j<=n; j++) gel(elts2,j) = perm_mul(g,gel(elts,j));
5502 3563 : gen_sort_inplace(elts2, (void*)&vecsmall_lexcmp, &cmp_nodata, &gel(reg,i));
5503 : }
5504 294 : return reg;
5505 : }
5506 :
5507 : static GEN
5508 3857 : matrix_perm(GEN perm, long n)
5509 : {
5510 : GEN m;
5511 : long j;
5512 3857 : m = cgetg(n+1, t_MAT);
5513 78694 : for (j=1; j<=n; j++) {
5514 74837 : gel(m,j) = col_ei(n,perm[j]);
5515 : }
5516 3857 : return m;
5517 : }
5518 :
5519 : GEN
5520 847 : conjclasses_algcenter(GEN cc, GEN p)
5521 : {
5522 847 : GEN mt, elts = gel(cc,1), conjclass = gel(cc,2), rep = gel(cc,3), card;
5523 847 : long i, nbcl = lg(rep)-1, n = lg(elts)-1;
5524 : pari_sp av;
5525 :
5526 847 : card = zero_Flv(nbcl);
5527 14819 : for (i=1; i<=n; i++) card[conjclass[i]]++;
5528 :
5529 : /* multiplication table of the center of Z[G] (class functions) */
5530 847 : mt = cgetg(nbcl+1,t_VEC);
5531 7217 : for (i=1;i<=nbcl;i++) gel(mt,i) = zero_Flm_copy(nbcl,nbcl);
5532 847 : av = avma;
5533 7217 : for (i=1;i<=nbcl;i++)
5534 : {
5535 6370 : GEN xi = gel(elts,rep[i]), mi = gel(mt,i);
5536 : long j,k;
5537 132244 : for (j=1;j<=n;j++)
5538 : {
5539 125874 : GEN xj = gel(elts,j);
5540 125874 : k = vecsearch(elts, perm_mul(xi,xj), NULL);
5541 125874 : ucoeff(mi, conjclass[k], conjclass[j])++;
5542 : }
5543 70238 : for (k=1; k<=nbcl; k++)
5544 852362 : for (j=1; j<=nbcl; j++)
5545 : {
5546 788494 : ucoeff(mi,k,j) *= card[i];
5547 788494 : ucoeff(mi,k,j) /= card[k];
5548 : }
5549 6370 : set_avma(av);
5550 : }
5551 7217 : for (i=1;i<=nbcl;i++) gel(mt,i) = Flm_to_ZM(gel(mt,i));
5552 847 : return algtableinit_i(mt,p);
5553 : }
5554 :
5555 : GEN
5556 329 : alggroupcenter(GEN G, GEN p, GEN *pcc)
5557 : {
5558 329 : pari_sp av = avma;
5559 329 : GEN cc = group_to_cc(G), al = conjclasses_algcenter(cc, p);
5560 315 : if (!pcc) return gerepilecopy(av,al);
5561 7 : *pcc = cc; return gc_all(av, 2, &al, pcc);
5562 : }
5563 :
5564 : static GEN
5565 294 : groupelts_algebra(GEN elts, GEN p)
5566 : {
5567 294 : pari_sp av = avma;
5568 : GEN mt;
5569 294 : long i, n = lg(elts)-1;
5570 294 : elts = list_to_regular_rep(elts,n);
5571 294 : mt = cgetg(n+1, t_VEC);
5572 4151 : for (i=1; i<=n; i++) gel(mt,i) = matrix_perm(gel(elts,i),n);
5573 294 : return gerepilecopy(av, algtableinit_i(mt,p));
5574 : }
5575 :
5576 : GEN
5577 329 : alggroup(GEN gal, GEN p)
5578 : {
5579 329 : GEN elts = checkgroupelts(gal);
5580 294 : return groupelts_algebra(elts, p);
5581 : }
5582 :
5583 : /** MAXIMAL ORDER **/
5584 :
5585 : static GEN
5586 94935 : mattocol(GEN M, long n)
5587 : {
5588 94935 : GEN C = cgetg(n*n+1, t_COL);
5589 : long i,j,ic;
5590 94935 : ic = 1;
5591 1680858 : for (i=1; i<=n; i++)
5592 41492940 : for (j=1; j<=n; j++, ic++) gel(C,ic) = gcoeff(M,i,j);
5593 94935 : return C;
5594 : }
5595 :
5596 : /* Ip is a lift of a left O/pO-ideal where O is the integral basis of al */
5597 : static GEN
5598 8774 : algleftordermodp(GEN al, GEN Ip, GEN p)
5599 : {
5600 8774 : pari_sp av = avma;
5601 : GEN I, Ii, M, mt, K, imi, p2;
5602 : long n, i;
5603 8774 : n = alg_get_absdim(al);
5604 8774 : mt = alg_get_multable(al);
5605 8774 : p2 = sqri(p);
5606 :
5607 8774 : I = ZM_hnfmodid(Ip, p);
5608 8774 : Ii = ZM_inv(I,NULL);
5609 :
5610 8774 : M = cgetg(n+1, t_MAT);
5611 103709 : for (i=1; i<=n; i++) {
5612 94935 : imi = FpM_mul(Ii, FpM_mul(gel(mt,i), I, p2), p2);
5613 94935 : imi = ZM_Z_divexact(imi, p);
5614 94935 : gel(M,i) = mattocol(imi, n);
5615 : }
5616 8774 : K = FpM_ker(M, p);
5617 8774 : if (lg(K)==1) { set_avma(av); return matid(n); }
5618 3589 : K = ZM_hnfmodid(K,p);
5619 :
5620 3589 : return gerepileupto(av, ZM_Z_div(K,p));
5621 : }
5622 :
5623 : static GEN
5624 14597 : alg_ordermodp(GEN al, GEN p)
5625 : {
5626 : GEN alp;
5627 14597 : long i, N = alg_get_absdim(al);
5628 14597 : alp = cgetg(12, t_VEC);
5629 131373 : for (i=1; i<=8; i++) gel(alp,i) = gen_0;
5630 14597 : gel(alp,9) = cgetg(N+1, t_VEC);
5631 154598 : for (i=1; i<=N; i++) gmael(alp,9,i) = FpM_red(gmael(al,9,i), p);
5632 14597 : gel(alp,10) = p;
5633 14597 : gel(alp,11) = cgetg(N+1, t_VEC);
5634 154598 : for (i=1; i<=N; i++) gmael(alp,11,i) = Fp_red(gmael(al,11,i), p);
5635 :
5636 14597 : return alp;
5637 : }
5638 :
5639 : static GEN
5640 6892 : algpradical_i(GEN al, GEN p, GEN zprad, GEN projs)
5641 : {
5642 6892 : pari_sp av = avma;
5643 6892 : GEN alp = alg_ordermodp(al, p), liftrad, projrad, alq, alrad, res, Lalp, radq;
5644 : long i;
5645 6892 : if (lg(zprad)==1) {
5646 4754 : liftrad = NULL;
5647 4754 : projrad = NULL;
5648 : }
5649 : else {
5650 2138 : alq = alg_quotient(alp, zprad, 1);
5651 2138 : alp = gel(alq,1);
5652 2138 : projrad = gel(alq,2);
5653 2138 : liftrad = gel(alq,3);
5654 : }
5655 :
5656 6892 : if (projs) {
5657 1399 : if (projrad) {
5658 28 : projs = gcopy(projs);
5659 84 : for (i=1; i<lg(projs); i++)
5660 56 : gel(projs,i) = FpM_FpC_mul(projrad, gel(projs,i), p);
5661 : }
5662 1399 : Lalp = alg_centralproj(alp, projs, 1);
5663 :
5664 1399 : alrad = cgetg(lg(Lalp),t_VEC);
5665 4597 : for (i=1; i<lg(Lalp); i++) {
5666 3198 : alq = gel(Lalp,i);
5667 3198 : radq = algradical(gel(alq,1));
5668 3198 : if (gequal0(radq))
5669 1832 : gel(alrad,i) = cgetg(1,t_MAT);
5670 : else {
5671 1366 : radq = FpM_mul(gel(alq,3),radq,p);
5672 1366 : gel(alrad,i) = radq;
5673 : }
5674 : }
5675 1399 : alrad = shallowmatconcat(alrad);
5676 1399 : alrad = FpM_image(alrad,p);
5677 : }
5678 5493 : else alrad = algradical(alp);
5679 :
5680 6892 : if (!gequal0(alrad)) {
5681 5465 : if (liftrad) alrad = FpM_mul(liftrad, alrad, p);
5682 5465 : res = shallowmatconcat(mkvec2(alrad, zprad));
5683 5465 : res = FpM_image(res,p);
5684 : }
5685 1427 : else res = lg(zprad)==1 ? gen_0 : zprad;
5686 6892 : return gerepilecopy(av, res);
5687 : }
5688 :
5689 : static GEN
5690 4947 : algpdecompose0(GEN al, GEN prad, GEN p, GEN projs)
5691 : {
5692 4947 : pari_sp av = avma;
5693 4947 : GEN alp, quo, ss, liftm = NULL, projm = NULL, dec, res, I, Lss, deci;
5694 : long i, j;
5695 :
5696 4947 : alp = alg_ordermodp(al, p);
5697 4947 : if (!gequal0(prad)) {
5698 4003 : quo = alg_quotient(alp, prad, 1);
5699 4003 : ss = gel(quo,1);
5700 4003 : projm = gel(quo,2);
5701 4003 : liftm = gel(quo,3);
5702 : }
5703 944 : else ss = alp;
5704 :
5705 4947 : if (projs) {
5706 1237 : if (projm) {
5707 2810 : for (i=1; i<lg(projs); i++)
5708 1950 : gel(projs,i) = FpM_FpC_mul(projm, gel(projs,i), p);
5709 : }
5710 1237 : Lss = alg_centralproj(ss, projs, 1);
5711 :
5712 1237 : dec = cgetg(lg(Lss),t_VEC);
5713 4090 : for (i=1; i<lg(Lss); i++) {
5714 2853 : gel(dec,i) = algsimpledec_ss(gmael(Lss,i,1), 1);
5715 2853 : deci = gel(dec,i);
5716 6482 : for (j=1; j<lg(deci); j++)
5717 3629 : gmael(deci,j,3) = FpM_mul(gmael(Lss,i,3), gmael(deci,j,3), p);
5718 : }
5719 1237 : dec = shallowconcat1(dec);
5720 : }
5721 3710 : else dec = algsimpledec_ss(ss,1);
5722 :
5723 4947 : res = cgetg(lg(dec),t_VEC);
5724 13637 : for (i=1; i<lg(dec); i++) {
5725 8690 : I = gmael(dec,i,3);
5726 8690 : if (liftm) I = FpM_mul(liftm,I,p);
5727 8690 : I = shallowmatconcat(mkvec2(I,prad));
5728 8690 : gel(res,i) = I;
5729 : }
5730 :
5731 4947 : return gerepilecopy(av, res);
5732 : }
5733 :
5734 : /* finds a nontrivial ideal of O/prad or gen_0 if there is none. */
5735 : static GEN
5736 1644 : algpdecompose_i(GEN al, GEN p, GEN zprad, GEN projs)
5737 : {
5738 1644 : pari_sp av = avma;
5739 1644 : GEN prad = algpradical_i(al,p,zprad,projs);
5740 1644 : return gerepileupto(av, algpdecompose0(al, prad, p, projs));
5741 : }
5742 :
5743 : /* ord is assumed to be in hnf wrt the integral basis of al. */
5744 : /* assumes that alg_get_invbasis(al) is integral. */
5745 : static GEN
5746 3617 : alg_change_overorder_shallow(GEN al, GEN ord)
5747 : {
5748 : GEN al2, mt, iord, mtx, den, den2, div, invol;
5749 : long i, n;
5750 3617 : n = alg_get_absdim(al);
5751 :
5752 3617 : iord = QM_inv(ord);
5753 3617 : al2 = shallowcopy(al);
5754 :
5755 3617 : invol = alg_get_invol(al);
5756 3617 : if (typ(invol) == t_MAT) gmael(al2,6,2) = QM_mul(iord, QM_mul(invol,ord));
5757 :
5758 3617 : ord = Q_remove_denom(ord,&den);
5759 :
5760 3617 : gel(al2,7) = Q_remove_denom(gel(al,7), &den2);
5761 3617 : if (den2) div = mulii(den,den2);
5762 1253 : else div = den;
5763 3617 : gel(al2,7) = ZM_Z_div(ZM_mul(gel(al2,7), ord), div);
5764 :
5765 3617 : gel(al2,8) = ZM_mul(iord, gel(al,8));
5766 :
5767 3617 : mt = cgetg(n+1,t_VEC);
5768 3617 : gel(mt,1) = matid(n);
5769 3617 : div = sqri(den);
5770 40767 : for (i=2; i<=n; i++) {
5771 37150 : mtx = algbasismultable(al,gel(ord,i));
5772 37150 : gel(mt,i) = ZM_mul(iord, ZM_mul(mtx, ord));
5773 37150 : gel(mt,i) = ZM_Z_divexact(gel(mt,i), div);
5774 : }
5775 3617 : gel(al2,9) = mt;
5776 :
5777 3617 : gel(al2,11) = algtracebasis(al2);
5778 :
5779 3617 : return al2;
5780 : }
5781 :
5782 : static GEN
5783 37018 : algeltfromnf_i(GEN al, GEN x)
5784 : {
5785 37018 : GEN nf = alg_get_center(al);
5786 : long n;
5787 37018 : switch(alg_type(al)) {
5788 31334 : case al_CYCLIC:
5789 31334 : n = alg_get_degree(al);
5790 31334 : break;
5791 5684 : case al_CSA:
5792 5684 : n = alg_get_dim(al);
5793 5684 : break;
5794 : default: return NULL; /*LCOV_EXCL_LINE*/
5795 : }
5796 37018 : return algalgtobasis(al, scalarcol(basistoalg(nf, x), n));
5797 : }
5798 :
5799 : GEN
5800 5138 : algeltfromnf(GEN al, GEN x)
5801 : {
5802 5138 : pari_sp av = avma;
5803 5138 : checkalg(al);
5804 5131 : return gerepileupto(av, algeltfromnf_i(al,x));
5805 : }
5806 :
5807 : /* x is an ideal of the center in hnf form */
5808 : static GEN
5809 6892 : algeltfromnf_hnf(GEN al, GEN x)
5810 : {
5811 : GEN res;
5812 : long i;
5813 6892 : res = cgetg(lg(x), t_MAT);
5814 19938 : for (i=1; i<lg(x); i++) gel(res,i) = algeltfromnf_i(al, gel(x,i));
5815 6892 : return res;
5816 : }
5817 :
5818 : /* assumes al is CSA or CYCLIC */
5819 : static GEN
5820 3303 : algcenter_precompute(GEN al, GEN p)
5821 : {
5822 3303 : GEN fa, pdec, nfprad, projs, nf = alg_get_center(al);
5823 : long i, np;
5824 :
5825 3303 : pdec = idealprimedec(nf, p);
5826 3303 : settyp(pdec, t_COL);
5827 3303 : np = lg(pdec)-1;
5828 3303 : fa = mkmat2(pdec, const_col(np, gen_1));
5829 3303 : if (dvdii(nf_get_disc(nf), p))
5830 679 : nfprad = idealprodprime(nf, pdec);
5831 : else
5832 2624 : nfprad = scalarmat_shallow(p, nf_get_degree(nf));
5833 3303 : fa = idealchineseinit(nf, fa);
5834 3303 : projs = cgetg(np+1, t_VEC);
5835 7461 : for (i=1; i<=np; i++) gel(projs, i) = idealchinese(nf, fa, vec_ei(np,i));
5836 3303 : return mkvec2(nfprad, projs);
5837 : }
5838 :
5839 : static GEN
5840 6892 : algcenter_prad(GEN al, GEN p, GEN pre)
5841 : {
5842 : GEN nfprad, zprad, mtprad;
5843 : long i;
5844 6892 : nfprad = gel(pre,1);
5845 6892 : zprad = algeltfromnf_hnf(al, nfprad);
5846 6892 : zprad = FpM_image(zprad, p);
5847 6892 : mtprad = cgetg(lg(zprad), t_VEC);
5848 9577 : for (i=1; i<lg(zprad); i++) gel(mtprad, i) = algbasismultable(al, gel(zprad,i));
5849 6892 : mtprad = shallowmatconcat(mtprad);
5850 6892 : zprad = FpM_image(mtprad, p);
5851 6892 : return zprad;
5852 : }
5853 :
5854 : static GEN
5855 6892 : algcenter_p_projs(GEN al, GEN p, GEN pre)
5856 : {
5857 : GEN projs, zprojs;
5858 : long i;
5859 6892 : projs = gel(pre,2);
5860 6892 : zprojs = cgetg(lg(projs), t_VEC);
5861 15583 : for (i=1; i<lg(projs); i++) gel(zprojs,i) = FpC_red(algeltfromnf_i(al, gel(projs,i)),p);
5862 6892 : return zprojs;
5863 : }
5864 :
5865 : /* al is assumed to be simple */
5866 : static GEN
5867 3303 : alg_pmaximal(GEN al, GEN p)
5868 : {
5869 : pari_sp av;
5870 3303 : long n = alg_get_absdim(al);
5871 3303 : GEN id = matid(n), al2 = al, prad, lord = gen_0, dec, zprad, projs, pre;
5872 :
5873 3303 : dbg_printf(0)("Round 2 (noncommutative) at p=%Ps, dim=%d\n", p, n);
5874 3303 : pre = algcenter_precompute(al,p); av = avma;
5875 : while (1) {
5876 5248 : zprad = algcenter_prad(al2, p, pre);
5877 5248 : projs = algcenter_p_projs(al2, p, pre);
5878 5248 : if (lg(projs) == 2) projs = NULL;
5879 5248 : prad = algpradical_i(al2,p,zprad,projs);
5880 5248 : if (typ(prad) == t_INT) break;
5881 5206 : lord = algleftordermodp(al2,prad,p);
5882 5206 : if (!cmp_universal(lord,id)) break;
5883 1945 : al2 = gerepilecopy(av, alg_change_overorder_shallow(al2,lord));
5884 : }
5885 :
5886 3303 : dec = algpdecompose0(al2,prad,p,projs); av = avma;
5887 4947 : while (lg(dec) > 2) {
5888 : long i;
5889 4260 : for (i = 1; i < lg(dec); i++) {
5890 3568 : GEN I = gel(dec,i);
5891 3568 : lord = algleftordermodp(al2,I,p);
5892 3568 : if (cmp_universal(lord,id)) break;
5893 : }
5894 2336 : if (i==lg(dec)) break;
5895 1644 : al2 = gerepilecopy(av, alg_change_overorder_shallow(al2,lord));
5896 1644 : zprad = algcenter_prad(al2, p, pre);
5897 1644 : projs = algcenter_p_projs(al2, p, pre);
5898 1644 : if (lg(projs) == 2) projs = NULL;
5899 1644 : dec = algpdecompose_i(al2,p,zprad,projs);
5900 : }
5901 3303 : return al2;
5902 : }
5903 :
5904 : static GEN
5905 15432 : algtracematrix(GEN al)
5906 : {
5907 : GEN M, mt;
5908 : long n, i, j;
5909 15432 : n = alg_get_absdim(al);
5910 15432 : mt = alg_get_multable(al);
5911 15432 : M = cgetg(n+1, t_MAT);
5912 114958 : for (i=1; i<=n; i++)
5913 : {
5914 99526 : gel(M,i) = cgetg(n+1,t_MAT);
5915 751118 : for (j=1; j<=i; j++)
5916 651592 : gcoeff(M,j,i) = gcoeff(M,i,j) = algabstrace(al,gmael(mt,i,j));
5917 : }
5918 15432 : return M;
5919 : }
5920 : static GEN
5921 567 : algdisc_i(GEN al) { return ZM_det(algtracematrix(al)); }
5922 : GEN
5923 364 : algdisc(GEN al)
5924 : {
5925 364 : pari_sp av = avma;
5926 364 : checkalg(al);
5927 364 : if (alg_type(al) == al_REAL) pari_err_TYPE("algdisc [real algebra]", al);
5928 343 : return gerepileuptoint(av, algdisc_i(al));
5929 : }
5930 : static GEN
5931 224 : alg_maximal(GEN al)
5932 : {
5933 224 : GEN fa = absZ_factor(algdisc_i(al));
5934 224 : return alg_maximal_primes(al, gel(fa,1));
5935 : }
5936 :
5937 : /** LATTICES **/
5938 :
5939 : /*
5940 : Convention: lattice = [I,t] representing t*I, where
5941 : - I integral nonsingular upper-triangular matrix representing a lattice over
5942 : the integral basis of the algebra, and
5943 : - t>0 either an integer or a rational number.
5944 :
5945 : Recommended and returned by the functions below:
5946 : - I HNF and primitive
5947 : */
5948 :
5949 : /* TODO use hnfmodid whenever possible using a*O <= I <= O
5950 : * for instance a = ZM_det_triangular(I) */
5951 :
5952 : static GEN
5953 64351 : primlat(GEN lat)
5954 : {
5955 : GEN m, t, c;
5956 64351 : m = alglat_get_primbasis(lat);
5957 64351 : t = alglat_get_scalar(lat);
5958 64351 : m = Q_primitive_part(m,&c);
5959 64351 : if (c) return mkvec2(m,gmul(t,c));
5960 54817 : return lat;
5961 : }
5962 :
5963 : /* assumes the lattice contains d * integral basis, d=0 allowed */
5964 : GEN
5965 53487 : alglathnf(GEN al, GEN m, GEN d)
5966 : {
5967 53487 : pari_sp av = avma;
5968 : long N,i,j;
5969 : GEN m2, c;
5970 53487 : if (!d) d = gen_0;
5971 53487 : checkalg(al);
5972 53487 : if (alg_type(al) == al_REAL) pari_err_TYPE("alglathnf [real algebra]", al);
5973 53480 : N = alg_get_absdim(al);
5974 53480 : if (!d) d = gen_0;
5975 53480 : if (typ(m) == t_VEC) m = matconcat(m);
5976 53480 : if (typ(m) == t_COL) m = algleftmultable(al,m);
5977 53480 : if (typ(m) != t_MAT) pari_err_TYPE("alglathnf",m);
5978 53473 : if (typ(d) != t_FRAC && typ(d) != t_INT) pari_err_TYPE("alglathnf",d);
5979 53473 : if (lg(m)-1 < N || lg(gel(m,1))-1 != N) pari_err_DIM("alglathnf");
5980 480935 : for (i=1; i<=N; i++)
5981 7185192 : for (j=1; j<lg(m); j++)
5982 6757702 : if (typ(gcoeff(m,i,j)) != t_FRAC && typ(gcoeff(m,i,j)) != t_INT)
5983 7 : pari_err_TYPE("alglathnf", gcoeff(m,i,j));
5984 53438 : m2 = Q_primitive_part(m,&c);
5985 53438 : if (!c) c = gen_1;
5986 53438 : if (!signe(d)) d = detint(m2);
5987 45593 : else d = gdiv(d,c); /* should be an integer */
5988 53438 : if (!signe(d)) pari_err_INV("alglathnf [m does not have full rank]", m2);
5989 53424 : m2 = ZM_hnfmodid(m2,d);
5990 53424 : return gerepilecopy(av, mkvec2(m2,c));
5991 : }
5992 :
5993 : static GEN
5994 11683 : prepare_multipliers(GEN *a, GEN *b)
5995 : {
5996 : GEN na, nb, da, db, d;
5997 11683 : na = numer_i(*a); da = denom_i(*a);
5998 11683 : nb = numer_i(*b); db = denom_i(*b);
5999 11683 : na = mulii(na,db);
6000 11683 : nb = mulii(nb,da);
6001 11683 : d = gcdii(na,nb);
6002 11683 : *a = diviiexact(na,d);
6003 11683 : *b = diviiexact(nb,d);
6004 11683 : return gdiv(d, mulii(da,db));
6005 : }
6006 :
6007 : static GEN
6008 11683 : prepare_lat(GEN m1, GEN t1, GEN m2, GEN t2)
6009 : {
6010 11683 : GEN d = prepare_multipliers(&t1, &t2);
6011 11683 : m1 = ZM_Z_mul(m1,t1);
6012 11683 : m2 = ZM_Z_mul(m2,t2);
6013 11683 : return mkvec3(m1,m2,d);
6014 : }
6015 :
6016 : static GEN
6017 11697 : alglataddinter(GEN al, GEN lat1, GEN lat2, GEN *sum, GEN *inter)
6018 : {
6019 : GEN d, m1, m2, t1, t2, M, prep, d1, d2, ds, di, K;
6020 11697 : checkalg(al);
6021 11697 : if (alg_type(al) == al_REAL)
6022 14 : pari_err_TYPE("alglataddinter [real algebra]", al);
6023 11683 : checklat(al,lat1);
6024 11683 : checklat(al,lat2);
6025 :
6026 11683 : m1 = alglat_get_primbasis(lat1);
6027 11683 : t1 = alglat_get_scalar(lat1);
6028 11683 : m2 = alglat_get_primbasis(lat2);
6029 11683 : t2 = alglat_get_scalar(lat2);
6030 11683 : prep = prepare_lat(m1, t1, m2, t2);
6031 11683 : m1 = gel(prep,1);
6032 11683 : m2 = gel(prep,2);
6033 11683 : d = gel(prep,3);
6034 11683 : M = matconcat(mkvec2(m1,m2));
6035 11683 : d1 = ZM_det_triangular(m1);
6036 11683 : d2 = ZM_det_triangular(m2);
6037 11683 : ds = gcdii(d1,d2);
6038 11683 : if (inter)
6039 : {
6040 7616 : di = diviiexact(mulii(d1,d2),ds);
6041 7616 : if (equali1(di))
6042 : {
6043 140 : *inter = matid(lg(m1)-1);
6044 140 : if (sum) *sum = matid(lg(m1)-1);
6045 : }
6046 : else
6047 : {
6048 7476 : K = matkermod(M,di,sum);
6049 7476 : K = rowslice(K,1,lg(m1));
6050 7476 : *inter = hnfmodid(FpM_mul(m1,K,di),di);
6051 7476 : if (sum) *sum = hnfmodid(*sum,ds);
6052 : }
6053 : }
6054 4067 : else *sum = hnfmodid(M,ds);
6055 11683 : return d;
6056 : }
6057 :
6058 : GEN
6059 4109 : alglatinter(GEN al, GEN lat1, GEN lat2, GEN* psum)
6060 : {
6061 4109 : pari_sp av = avma;
6062 : GEN inter, d;
6063 4109 : d = alglataddinter(al, lat1, lat2, psum, &inter);
6064 4102 : inter = primlat(mkvec2(inter, d));
6065 4102 : if (!psum) return gerepilecopy(av, inter);
6066 28 : *psum = primlat(mkvec2(*psum,d));
6067 28 : return gc_all(av, 2, &inter, psum);
6068 : }
6069 :
6070 : GEN
6071 7588 : alglatadd(GEN al, GEN lat1, GEN lat2, GEN* pinter)
6072 : {
6073 7588 : pari_sp av = avma;
6074 : GEN sum, d;
6075 7588 : d = alglataddinter(al, lat1, lat2, &sum, pinter);
6076 7581 : sum = primlat(mkvec2(sum, d));
6077 7581 : if (!pinter) return gerepilecopy(av, sum);
6078 3514 : *pinter = primlat(mkvec2(*pinter,d));
6079 3514 : return gc_all(av, 2, &sum, pinter);
6080 : }
6081 :
6082 : /* TODO version that returns the quotient as abelian group? */
6083 : /* return matrices to convert coordinates from one to other? */
6084 : int
6085 33495 : alglatsubset(GEN al, GEN lat1, GEN lat2, GEN* pindex)
6086 : {
6087 33495 : pari_sp av = avma;
6088 : int res;
6089 : GEN m1, m2, m2i, m, t;
6090 33495 : checkalg(al);
6091 33495 : if (alg_type(al) == al_REAL) pari_err_TYPE("alglatsubset [real algebra]", al);
6092 33488 : checklat(al,lat1);
6093 33488 : checklat(al,lat2);
6094 33488 : m1 = alglat_get_primbasis(lat1);
6095 33488 : m2 = alglat_get_primbasis(lat2);
6096 33488 : m2i = RgM_inv_upper(m2);
6097 33488 : t = gdiv(alglat_get_scalar(lat1), alglat_get_scalar(lat2));
6098 33488 : m = RgM_Rg_mul(RgM_mul(m2i,m1), t);
6099 33488 : res = RgM_is_ZM(m);
6100 33488 : if (!res || !pindex) return gc_int(av, res);
6101 1757 : *pindex = gerepileuptoint(av, mpabs(ZM_det_triangular(m)));
6102 1757 : return 1;
6103 : }
6104 :
6105 : GEN
6106 5271 : alglatindex(GEN al, GEN lat1, GEN lat2)
6107 : {
6108 5271 : pari_sp av = avma;
6109 : long N;
6110 : GEN res;
6111 5271 : checkalg(al);
6112 5271 : if (alg_type(al) == al_REAL) pari_err_TYPE("alglatindex [real algebra]", al);
6113 5264 : checklat(al,lat1);
6114 5264 : checklat(al,lat2);
6115 5264 : N = alg_get_absdim(al);
6116 5264 : res = alglat_get_scalar(lat1);
6117 5264 : res = gdiv(res, alglat_get_scalar(lat2));
6118 5264 : res = gpowgs(res, N);
6119 5264 : res = gmul(res,RgM_det_triangular(alglat_get_primbasis(lat1)));
6120 5264 : res = gdiv(res, RgM_det_triangular(alglat_get_primbasis(lat2)));
6121 5264 : res = gabs(res,0);
6122 5264 : return gerepilecopy(av, res);
6123 : }
6124 :
6125 : GEN
6126 45612 : alglatmul(GEN al, GEN lat1, GEN lat2)
6127 : {
6128 45612 : pari_sp av = avma;
6129 : long N,i;
6130 : GEN m1, m2, m, V, lat, t, d, dp;
6131 45612 : checkalg(al);
6132 45612 : if (alg_type(al) == al_REAL) pari_err_TYPE("alglatmul [real algebra]", al);
6133 45605 : if (typ(lat1)==t_COL)
6134 : {
6135 19292 : if (typ(lat2)==t_COL)
6136 7 : pari_err_TYPE("alglatmul [one of lat1, lat2 has to be a lattice]", lat2);
6137 19285 : checklat(al,lat2);
6138 19285 : lat1 = Q_remove_denom(lat1,&d);
6139 19285 : m = algbasismultable(al,lat1);
6140 19285 : m2 = alglat_get_primbasis(lat2);
6141 19285 : dp = mulii(detint(m),ZM_det_triangular(m2));
6142 19285 : m = ZM_mul(m,m2);
6143 19285 : t = alglat_get_scalar(lat2);
6144 19285 : if (d) t = gdiv(t,d);
6145 : }
6146 : else /* typ(lat1)!=t_COL */
6147 : {
6148 26313 : checklat(al,lat1);
6149 26313 : if (typ(lat2)==t_COL)
6150 : {
6151 19285 : lat2 = Q_remove_denom(lat2,&d);
6152 19285 : m = algbasisrightmultable(al,lat2);
6153 19285 : m1 = alglat_get_primbasis(lat1);
6154 19285 : dp = mulii(detint(m),ZM_det_triangular(m1));
6155 19285 : m = ZM_mul(m,m1);
6156 19285 : t = alglat_get_scalar(lat1);
6157 19285 : if (d) t = gdiv(t,d);
6158 : }
6159 : else /* typ(lat2)!=t_COL */
6160 : {
6161 7028 : checklat(al,lat2);
6162 7021 : N = alg_get_absdim(al);
6163 7021 : m1 = alglat_get_primbasis(lat1);
6164 7021 : m2 = alglat_get_primbasis(lat2);
6165 7021 : dp = mulii(ZM_det_triangular(m1), ZM_det_triangular(m2));
6166 7021 : V = cgetg(N+1,t_VEC);
6167 63189 : for (i=1; i<=N; i++) {
6168 56168 : gel(V,i) = algbasismultable(al,gel(m1,i));
6169 56168 : gel(V,i) = ZM_mul(gel(V,i),m2);
6170 : }
6171 7021 : m = matconcat(V);
6172 7021 : t = gmul(alglat_get_scalar(lat1), alglat_get_scalar(lat2));
6173 : }
6174 : }
6175 :
6176 45591 : lat = alglathnf(al,m,dp);
6177 45591 : gel(lat,2) = gmul(alglat_get_scalar(lat), t);
6178 45591 : lat = primlat(lat);
6179 45591 : return gerepilecopy(av, lat);
6180 : }
6181 :
6182 : int
6183 17528 : alglatcontains(GEN al, GEN lat, GEN x, GEN *ptc)
6184 : {
6185 17528 : pari_sp av = avma;
6186 : GEN m, t, sol;
6187 17528 : checkalg(al);
6188 17528 : if (alg_type(al) == al_REAL)
6189 7 : pari_err_TYPE("alglatcontains [real algebra]", al);
6190 17521 : checklat(al,lat);
6191 17521 : m = alglat_get_primbasis(lat);
6192 17521 : t = alglat_get_scalar(lat);
6193 17521 : x = RgC_Rg_div(x,t);
6194 17521 : if (!RgV_is_ZV(x)) return gc_bool(av,0);
6195 17521 : sol = hnf_solve(m,x);
6196 17521 : if (!sol) return gc_bool(av,0);
6197 8771 : if (!ptc) return gc_bool(av,1);
6198 8764 : *ptc = gerepilecopy(av, sol); return 1;
6199 : }
6200 :
6201 : GEN
6202 8778 : alglatelement(GEN al, GEN lat, GEN c)
6203 : {
6204 8778 : pari_sp av = avma;
6205 : GEN res;
6206 8778 : checkalg(al);
6207 8778 : if (alg_type(al) == al_REAL)
6208 7 : pari_err_TYPE("alglatelement [real algebra]", al);
6209 8771 : checklat(al,lat);
6210 8771 : if (typ(c)!=t_COL) pari_err_TYPE("alglatelement", c);
6211 8764 : res = ZM_ZC_mul(alglat_get_primbasis(lat),c);
6212 8764 : res = RgC_Rg_mul(res, alglat_get_scalar(lat));
6213 8764 : return gerepilecopy(av,res);
6214 : }
6215 :
6216 : /* idem QM_invimZ, knowing result is contained in 1/c*Z^n */
6217 : static GEN
6218 3535 : QM_invimZ_mod(GEN m, GEN c)
6219 : {
6220 : GEN d, m0, K;
6221 3535 : m0 = Q_remove_denom(m, &d);
6222 3535 : if (d) d = mulii(d,c);
6223 35 : else d = c;
6224 3535 : K = matkermod(m0, d, NULL);
6225 3535 : if (lg(K)==1) K = scalarmat(d, lg(m)-1);
6226 3493 : else K = hnfmodid(K, d);
6227 3535 : return RgM_Rg_div(K,c);
6228 : }
6229 :
6230 : /* If m is injective, computes a Z-basis of the submodule of elements whose
6231 : * image under m is integral */
6232 : static GEN
6233 14 : QM_invimZ(GEN m)
6234 : {
6235 14 : return RgM_invimage(m, QM_ImQ_hnf(m));
6236 : }
6237 :
6238 : /* An isomorphism of R-modules M_{m,n}(R) -> R^{m*n} */
6239 : static GEN
6240 28322 : mat2col(GEN M, long m, long n)
6241 : {
6242 : long i,j,k,p;
6243 : GEN C;
6244 28322 : p = m*n;
6245 28322 : C = cgetg(p+1,t_COL);
6246 254702 : for (i=1,k=1;i<=m;i++)
6247 2036804 : for (j=1;j<=n;j++,k++)
6248 1810424 : gel(C,k) = gcoeff(M,i,j);
6249 28322 : return C;
6250 : }
6251 :
6252 : static GEN
6253 3535 : alglattransporter_i(GEN al, GEN lat1, GEN lat2, long right)
6254 : {
6255 : GEN m1, m2, m2i, M, MT, mt, t1, t2, T, c;
6256 : long N, i;
6257 3535 : N = alg_get_absdim(al);
6258 3535 : m1 = alglat_get_primbasis(lat1);
6259 3535 : m2 = alglat_get_primbasis(lat2);
6260 3535 : m2i = RgM_inv_upper(m2);
6261 3535 : c = detint(m1);
6262 3535 : t1 = alglat_get_scalar(lat1);
6263 3535 : m1 = RgM_Rg_mul(m1,t1);
6264 3535 : t2 = alglat_get_scalar(lat2);
6265 3535 : m2i = RgM_Rg_div(m2i,t2);
6266 :
6267 3535 : MT = right? NULL: alg_get_multable(al);
6268 3535 : M = cgetg(N+1, t_MAT);
6269 31815 : for (i=1; i<=N; i++) {
6270 28280 : if (right) mt = algbasisrightmultable(al, vec_ei(N,i));
6271 14168 : else mt = gel(MT,i);
6272 28280 : mt = RgM_mul(m2i,mt);
6273 28280 : mt = RgM_mul(mt,m1);
6274 28280 : gel(M,i) = mat2col(mt, N, N);
6275 : }
6276 :
6277 3535 : c = gdiv(t2,gmul(c,t1));
6278 3535 : c = denom_i(c);
6279 3535 : T = QM_invimZ_mod(M,c);
6280 3535 : return primlat(mkvec2(T,gen_1));
6281 : }
6282 :
6283 : /*
6284 : { x in al | x*lat1 subset lat2}
6285 : */
6286 : GEN
6287 1778 : alglatlefttransporter(GEN al, GEN lat1, GEN lat2)
6288 : {
6289 1778 : pari_sp av = avma;
6290 1778 : checkalg(al);
6291 1778 : if (alg_type(al) == al_REAL)
6292 7 : pari_err_TYPE("alglatlefttransporter [real algebra]", al);
6293 1771 : checklat(al,lat1);
6294 1771 : checklat(al,lat2);
6295 1771 : return gerepilecopy(av, alglattransporter_i(al,lat1,lat2,0));
6296 : }
6297 :
6298 : /*
6299 : { x in al | lat1*x subset lat2}
6300 : */
6301 : GEN
6302 1771 : alglatrighttransporter(GEN al, GEN lat1, GEN lat2)
6303 : {
6304 1771 : pari_sp av = avma;
6305 1771 : checkalg(al);
6306 1771 : if (alg_type(al) == al_REAL)
6307 7 : pari_err_TYPE("alglatrighttransporter [real algebra]", al);
6308 1764 : checklat(al,lat1);
6309 1764 : checklat(al,lat2);
6310 1764 : return gerepilecopy(av, alglattransporter_i(al,lat1,lat2,1));
6311 : }
6312 :
6313 : GEN
6314 42 : algmakeintegral(GEN mt0, long maps)
6315 : {
6316 42 : pari_sp av = avma;
6317 : long n,i;
6318 : GEN m,P,Pi,mt2,mt;
6319 42 : n = lg(mt0)-1;
6320 42 : mt = check_mt(mt0,NULL);
6321 42 : if (!mt) pari_err_TYPE("algmakeintegral", mt0);
6322 21 : if (isint1(Q_denom(mt0))) {
6323 7 : if (maps) mt = mkvec3(mt,matid(n),matid(n));
6324 7 : return gerepilecopy(av,mt);
6325 : }
6326 14 : dbg_printf(2)(" algmakeintegral: dim=%d, denom=%Ps\n", n, Q_denom(mt0));
6327 14 : m = cgetg(n+1,t_MAT);
6328 56 : for (i=1;i<=n;i++)
6329 42 : gel(m,i) = mat2col(gel(mt,i),n,n);
6330 14 : dbg_printf(2)(" computing order, dims m = %d x %d...\n", nbrows(m), lg(m)-1);
6331 14 : P = QM_invimZ(m);
6332 14 : dbg_printf(2)(" ...done.\n");
6333 14 : P = shallowmatconcat(mkvec2(col_ei(n,1),P));
6334 14 : P = hnf(P);
6335 14 : Pi = RgM_inv(P);
6336 14 : mt2 = change_Rgmultable(mt,P,Pi);
6337 14 : if (maps) mt2 = mkvec3(mt2,Pi,P); /* mt2, mt->mt2, mt2->mt */
6338 14 : return gerepilecopy(av,mt2);
6339 : }
6340 :
6341 : /** ORDERS **/
6342 :
6343 : /*
6344 : * algmodpr data:
6345 : * 1. pr
6346 : * 2. Vecsmall([k,m]) s.t. target is M_k(F_p^m). /!\ m can differ from pr.f
6347 : * 3. t_FFELT 1 representing the finite field F_q
6348 : * 4. proj: O -> M_k(F_q)
6349 : * 5. lift: M_k(F_q) -> O
6350 : * 6. tau: anti uniformizer (left multiplication matrix)
6351 : * 7. T s.t. F_q = F_p[x]/T
6352 : */
6353 : GEN
6354 2793 : algmodprinit(GEN al, GEN pr, long v)
6355 : {
6356 2793 : pari_sp av = avma;
6357 : GEN p, alp, g, Q, pro, lif, map, mapi, alpr, spl, data, nf, T, J, tau;
6358 : long tal, k, m;
6359 2793 : checkalg(al); checkprid(pr);
6360 2779 : tal = alg_type(al);
6361 2779 : if (tal!=al_CYCLIC && tal!=al_CSA)
6362 21 : pari_err_TYPE("algmodprinit [use alginit]", al);
6363 2758 : nf = alg_get_center(al);
6364 2758 : p = pr_get_p(pr);
6365 2758 : alp = alg_ordermodp(al, p);
6366 2758 : g = algeltfromnf_i(al, pr_get_gen(pr));
6367 2758 : g = algbasismultable(alp, g);
6368 2758 : g = FpM_image(g, p);
6369 2758 : alpr = alg_quotient(alp, g, 1);
6370 2758 : Q = gel(alpr, 1);
6371 2758 : pro = gel(alpr, 2);
6372 2758 : lif = gel(alpr, 3);
6373 2758 : J = algradical(Q); /* could skip if we knew the order is maximal at unramified pr */
6374 2758 : if (!gequal0(J))
6375 : {
6376 21 : Q = alg_quotient(Q, J, 1);
6377 21 : pro = ZM_mul(gel(Q,2), pro);
6378 21 : lif = ZM_mul(lif, gel(Q,3));
6379 21 : Q = gel(Q,1);
6380 : }
6381 2758 : spl = alg_finite_csa_split(Q, v);
6382 2758 : T = gel(spl, 1); /* t_POL, possibly of degree 1 */
6383 2758 : mapi = gel(spl, 3);
6384 2758 : map = gel(spl, 4);
6385 2758 : tau = pr_anti_uniformizer(nf, pr);
6386 2758 : m = degpol(T);
6387 2758 : k = lg(gmael(spl,2,1)) - 1;
6388 2758 : if (typ(tau) != t_INT) tau = algbasismultable(al,algeltfromnf_i(al,tau));
6389 2758 : data = mkvecn(7,
6390 : pr,
6391 : mkvecsmall2(k, m),
6392 : Tp_to_FF(T,p),
6393 : FpM_mul(map, pro, p),
6394 : FpM_mul(lif, mapi, p),
6395 : tau,
6396 : T
6397 : );
6398 2758 : return gerepilecopy(av, data);
6399 : }
6400 :
6401 : static int
6402 2135 : checkalgmodpr_i(GEN data)
6403 : {
6404 : GEN compo;
6405 2135 : if (typ(data)!=t_VEC || lg(data)!=8) return 0;
6406 2121 : checkprid(gel(data,1));
6407 2114 : compo = gel(data,2);
6408 2114 : if (typ(compo)!=t_VECSMALL || lg(compo)!=3) return 0;
6409 2107 : if (typ(gel(data,3))!=t_FFELT) return 0;
6410 2100 : if (typ(gel(data,4))!=t_MAT) return 0;
6411 2093 : if (typ(gel(data,5))!=t_MAT) return 0;
6412 2086 : compo = gel(data,6);
6413 2086 : if (typ(compo)!=t_MAT && (typ(compo)!=t_INT || !equali1(compo))) return 0;
6414 2079 : if (typ(gel(data,7))!=t_POL) return 0;
6415 2072 : return 1;
6416 : }
6417 : static void
6418 2135 : checkalgmodpr(GEN data)
6419 : {
6420 2135 : if(!checkalgmodpr_i(data))
6421 56 : pari_err_TYPE("checkalgmodpr [use algmodprinit()]", data);
6422 2072 : }
6423 :
6424 : /* x belongs to the stored order of al, no GC */
6425 : static GEN
6426 1708 : algmodpr_integral(GEN x, GEN data, long reduce)
6427 : {
6428 : GEN res, T, p;
6429 1708 : long k, m, v = -1;
6430 1708 : T = algmodpr_get_T(data);
6431 1708 : if (T) v = varn(T);
6432 1708 : p = algmodpr_get_p(data);
6433 1708 : k = algmodpr_get_k(data);
6434 1708 : m = algmodpr_get_m(data);
6435 1708 : res = ZM_ZC_mul(algmodpr_get_proj(data), x);
6436 1708 : res = RgC_col2mat(res, k, m, v);
6437 1708 : return reduce? FqM_red(res, T, p) : res;
6438 : }
6439 :
6440 : /* x in basis form */
6441 : static GEN
6442 1729 : algmodpr_i(GEN x, GEN data)
6443 : {
6444 : GEN T, p, res, den, tau;
6445 : long v, i, j;
6446 1729 : x = Q_remove_denom(x, &den);
6447 1729 : T = algmodpr_get_T(data);
6448 1729 : p = algmodpr_get_p(data);
6449 1729 : tau = algmodpr_get_tau(data);
6450 1729 : if (den)
6451 : {
6452 35 : v = Z_pvalrem(den, p, &den);
6453 35 : if (v && typ(tau)!=t_INT)
6454 : {
6455 : /* TODO not always better to exponentiate the matrix */
6456 21 : x = ZM_ZC_mul(ZM_powu(tau, v), x);
6457 21 : v -= ZV_pvalrem(x, p, &x);
6458 : }
6459 35 : if (v>0) pari_err_INV("algmodpr", mkintmod(gen_0,p));
6460 21 : if (v<0)
6461 : {
6462 7 : long k = algmodpr_get_k(data);
6463 7 : return zeromatcopy(k,k);
6464 : }
6465 14 : if (equali1(den)) den = NULL;
6466 : }
6467 1708 : res = algmodpr_integral(x, data, 0);
6468 1708 : if (den)
6469 : {
6470 7 : GEN d = Fp_inv(den, p);
6471 21 : for (j=1; j<lg(res); j++)
6472 42 : for (i=1; i<lg(res); i++)
6473 28 : gcoeff(res,i,j) = Fq_Fp_mul(gcoeff(res,i,j), d, T, p);
6474 : }
6475 1701 : else res = FqM_red(res, T, p);
6476 1708 : return res;
6477 : }
6478 :
6479 : static GEN
6480 28 : algmodpr_mat(GEN al, GEN x, GEN data)
6481 : {
6482 : GEN res, cx, c;
6483 : long i, j;
6484 28 : res = cgetg(lg(x),t_MAT);
6485 133 : for (j=1; j<lg(x); j++)
6486 : {
6487 105 : cx = gel(x,j);
6488 105 : c = cgetg(lg(cx), t_COL);
6489 525 : for (i=1; i<lg(cx); i++) gel(c,i) = algmodpr(al, gel(cx,i), data);
6490 105 : gel(res, j) = c;
6491 : }
6492 28 : return shallowmatconcat(res);
6493 : }
6494 :
6495 : GEN
6496 1841 : algmodpr(GEN al, GEN x, GEN data)
6497 : {
6498 1841 : pari_sp av = avma;
6499 : GEN res, ff;
6500 1841 : checkalgmodpr(data);
6501 1785 : if (typ(x) == t_MAT) return gerepilecopy(av, algmodpr_mat(al,x,data));
6502 1757 : x = algalgtobasis(al, x);
6503 1729 : res = algmodpr_i(x, data);
6504 1715 : ff = algmodpr_get_ff(data);
6505 1715 : return gerepilecopy(av, FqM_to_FFM(res,ff));
6506 : }
6507 :
6508 : static GEN
6509 511 : algmodprlift_i(GEN x, GEN data)
6510 : {
6511 511 : GEN lift, C, p, c, T = NULL;
6512 : long i, j, k, m;
6513 511 : lift = algmodpr_get_lift(data);
6514 511 : p = algmodpr_get_p(data);
6515 511 : k = algmodpr_get_k(data);
6516 511 : m = algmodpr_get_m(data); /* M_k(F_p^m) */
6517 511 : if (m > 1) T = algmodpr_get_T(data);
6518 511 : x = gcopy(x);
6519 1561 : for (i=1; i<=k; i++)
6520 3689 : for (j=1; j<=k; j++)
6521 : {
6522 2639 : c = gcoeff(x,i,j);
6523 2639 : if (typ(c) == t_FFELT) gcoeff(x,i,j) = FF_to_FpXQ(c);
6524 119 : else if (m == 1) gcoeff(x,i,j) = scalarpol(Rg_to_Fp(c,p), -1);
6525 91 : else gcoeff(x,i,j) = Rg_to_FpXQ(c, T, p);
6526 : }
6527 504 : C = RgM_mat2col(x, k, m);
6528 504 : return FpM_FpC_mul(lift, C, p);
6529 : }
6530 :
6531 : GEN
6532 301 : algmodprlift(GEN al, GEN x, GEN data)
6533 : {
6534 301 : pari_sp av = avma;
6535 : GEN res, blk;
6536 : long k, nc, nr, i, j;
6537 301 : checkalg(al);
6538 294 : checkalgmodpr(data);
6539 287 : k = algmodpr_get_k(data); /* M_k(F_p^m) */
6540 287 : if (typ(x) != t_MAT) pari_err_TYPE("algmodprlift [matrix x]",x);
6541 280 : if ((lg(x)-1)%k) pari_err_DIM("algmodprlift [matrix x, nb cols]");
6542 273 : nc = (lg(x)-1)/k;
6543 273 : if (!nc) return gerepileupto(av, zeromat(0,0));
6544 266 : if ((lgcols(x)-1)%k) pari_err_DIM("algmodprlift [matrix x, nb rows]");
6545 259 : nr = nbrows(x)/k;
6546 259 : if (nr==1 && nc==1) res = algmodprlift_i(x, data);
6547 : else
6548 : {
6549 28 : res = zeromatcopy(nr, nc);
6550 119 : for (i=1; i<=nr; i++)
6551 371 : for(j=1; j<=nc; j++)
6552 : {
6553 280 : blk = matslice(x, (i-1)*k+1, i*k, (j-1)*k+1, j*k);
6554 280 : gcoeff(res,i,j) = algmodprlift_i(blk, data);
6555 : }
6556 : }
6557 252 : return gerepilecopy(av, res);
6558 : }
6559 :
6560 : /* e in al such that e mod pr is a non-invertible idempotent of maximal rank */
6561 : static GEN
6562 2499 : eichleridempotent(GEN al, GEN pr)
6563 : {
6564 : long i, k, n, nk, j;
6565 : GEN data, mapi, e;
6566 2499 : data = algmodprinit(al, pr, -1);
6567 2492 : mapi = algmodpr_get_lift(data);
6568 2492 : k = algmodpr_get_k(data);
6569 2492 : n = pr_get_f(pr);
6570 2492 : nk = n*(k+1);
6571 2492 : if (k==1) return zerocol(alg_get_absdim(al));
6572 1820 : e = gel(mapi,1+nk);
6573 2681 : for (i = 2, j = 1+2*nk; i < k; i++, j += nk) e = ZC_add(e,gel(mapi,j));
6574 1820 : return e;
6575 : }
6576 :
6577 : static GEN
6578 2492 : mat_algeltfromnf(GEN al, GEN x)
6579 : {
6580 6244 : pari_APPLY_type(t_MAT, algeltfromnf_i(al, gel(x,i)));
6581 : }
6582 : static GEN
6583 2499 : eichlerprimepower_i(GEN al, GEN pr, long m, GEN prm)
6584 : {
6585 : GEN p, e, polidem, Me, Mzk, nf, Mprm;
6586 : long ep, i;
6587 : ulong mask;
6588 2499 : polidem = mkpoln(4, gen_m2, utoi(3), gen_0, gen_0);
6589 2499 : p = pr_get_p(pr); ep = pr_get_e(pr);
6590 2499 : e = eichleridempotent(al, pr); /* ZC */
6591 2492 : mask = quadratic_prec_mask(m);
6592 2492 : i = 1;
6593 6769 : while (mask > 1)
6594 : {
6595 4277 : i *=2;
6596 4277 : if (mask & 1UL) i--;
6597 4277 : mask >>= 1;
6598 4277 : e = algpoleval(al, polidem, e);
6599 4277 : e = FpC_red(e, powiu(p,(i+ep-1)/ep));
6600 : }
6601 2492 : Me = algbasisrightmultable(al, e);
6602 2492 : nf = algcenter(al);
6603 2492 : Mzk = mat_algeltfromnf(al, nf_get_zk(nf));
6604 2492 : prm = idealtwoelt(nf, prm);
6605 2492 : Mprm = algbasismultable(al, algeltfromnf_i(al,gel(prm,2)));
6606 2492 : return hnfmodid(shallowmatconcat(mkvec3(Me,Mzk,Mprm)), gel(prm,1));
6607 : }
6608 : static GEN
6609 546 : eichlerprimepower(GEN al, GEN pr, long m, GEN prm)
6610 : {
6611 546 : pari_sp av = avma;
6612 546 : return gerepileupto(av, eichlerprimepower_i(al, pr, m, prm));
6613 : }
6614 :
6615 : GEN
6616 2100 : algeichlerbasis(GEN al, GEN N)
6617 : {
6618 2100 : pari_sp av = avma;
6619 2100 : GEN nf, faN, LH = NULL, Cpr = NULL, Cm = NULL, Lpp, M, H, pp, LH2;
6620 : long k, n, ih, lh, np;
6621 :
6622 2100 : checkalg(al);
6623 2093 : nf = alg_get_center(al);
6624 2086 : if (checkprid_i(N)) return eichlerprimepower(al,N,1,N);
6625 2065 : if (is_nf_factor(N))
6626 : {
6627 2037 : faN = sort_factor(shallowcopy(N), (void*)&cmp_prime_ideal, &cmp_nodata);
6628 2037 : N = factorbackprime(nf, gel(faN,1), gel(faN,2));
6629 : }
6630 28 : else faN = idealfactor(nf, N);
6631 2051 : n = nbrows(faN);
6632 2051 : if (!n) { set_avma(av); return matid(alg_get_absdim(al)); }
6633 2044 : if (n==1)
6634 : {
6635 1953 : GEN pr = gcoeff(faN,1,1), mZ = gcoeff(faN,1,2);
6636 1953 : long m = itos(mZ);
6637 1953 : return gerepileupto(av, eichlerprimepower_i(al, pr, m, N));
6638 : }
6639 :
6640 : /* collect prime power Eichler orders */
6641 91 : Lpp = cgetg(n+1,t_VEC);
6642 91 : LH2 = cgetg(n+1, t_VEC);
6643 91 : np = 0;
6644 91 : ih = 1;
6645 91 : lh = 1;
6646 616 : for (k = 1; k <= n; k++)
6647 : {
6648 525 : GEN pr = gcoeff(faN,k,1), mZ = gcoeff(faN,k,2), prm;
6649 525 : long m = itos(mZ);
6650 :
6651 525 : if (ih == lh) /* done with previous p, prepare next */
6652 : {
6653 462 : GEN p = pr_get_p(pr);
6654 462 : long k2 = k + 1;
6655 462 : np++;
6656 462 : gel(Lpp,np) = gen_0;
6657 462 : lh = 2;
6658 : /* count the pr|p in faN */
6659 525 : while (k2<=n && equalii(p,pr_get_p(gcoeff(faN,k2,1)))) { lh++; k2++; }
6660 462 : LH = cgetg(lh, t_VEC);
6661 462 : Cpr = cgetg(lh, t_VEC);
6662 462 : Cm = cgetg(lh, t_VEC);
6663 462 : ih = 1;
6664 : }
6665 525 : prm = idealpow(nf, pr, mZ);
6666 525 : H = eichlerprimepower(al, pr, m, prm);
6667 525 : pp = gcoeff(prm,1,1);
6668 525 : if (cmpii(pp,gel(Lpp,np)) > 0) gel(Lpp,np) = pp;
6669 525 : gel(LH,ih) = H;
6670 525 : gel(Cpr,ih) = pr;
6671 525 : gel(Cm,ih) = mZ;
6672 525 : ih++;
6673 :
6674 525 : if (ih == lh) /* done with this p */
6675 : {
6676 462 : if (lh == 2) gel(LH2,np) = gel(LH,1);
6677 : else
6678 : { /* put together the pr|p */
6679 63 : GEN U = gmael(idealchineseinit(nf, mkmat2(Cpr,Cm)),1,2);
6680 : long i;
6681 189 : for (i = 1; i < lh; i++)
6682 : {
6683 126 : GEN e = algeltfromnf_i(al, gel(U,i));
6684 126 : e = algbasismultable(al, e);
6685 126 : gel(LH,i) = ZM_mul(e, gel(LH,i));
6686 : }
6687 63 : gel(LH2,np) = hnfmodid(shallowmatconcat(LH), gel(Lpp,np));
6688 : }
6689 : }
6690 : }
6691 91 : if (np == 1) return gerepilecopy(av, gel(LH2,1));
6692 : /* put together all p */
6693 84 : setlg(Lpp,np+1);
6694 84 : setlg(LH2,np+1);
6695 84 : H = nmV_chinese_center(LH2, Lpp, &M);
6696 84 : return gerepileupto(av, hnfmodid(H, M));
6697 : }
6698 :
6699 : /** IDEALS **/
|