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