/* Copyright (C) 2000 The PARI group. This file is part of the PARI/GP package. PARI/GP is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY WHATSOEVER. Check the License for details. You should have received a copy of it, along with the package; see the file 'COPYING'. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "pari.h" #include "paripriv.h" /********************************************************************/ /** **/ /** REDUCTION **/ /** **/ /********************************************************************/ /* z in Z^n, return lift(Col(z) * Mod(1,p)) */ GEN FpC_red(GEN x, GEN p) { pari_APPLY_type(t_COL, modii(gel(x,i), p)) } /* z in Z^n, return lift(Vec(z) * Mod(1,p)) */ GEN FpV_red(GEN x, GEN p) { pari_APPLY_type(t_VEC, modii(gel(x,i), p)) } GEN FpC_center(GEN x, GEN p, GEN pov2) { pari_APPLY_type(t_COL, Fp_center(gel(x,i), p, pov2)) } /* assume 0 <= u < p and ps2 = p>>1 */ INLINE void Fp_center_inplace(GEN u, GEN p, GEN ps2) { if (abscmpii(u,ps2) > 0) subiiz(u,p,u); } void FpC_center_inplace(GEN z, GEN p, GEN ps2) { long i,l = lg(z); for (i=1; i 1 and 0 < i < lgcols(x) */ static GEN ZMrow_ZC_mul_i(GEN x, GEN y, long lx, long i) { GEN c = mulii(gcoeff(x,i,1), gel(y,1)); long k; for (k = 2; k < lx; k++) { GEN t = mulii(gcoeff(x,i,k), gel(y,k)); if (signe(t)) c = addii(c, t); } return c; } static long zmrow_zc_mul(GEN x, GEN y, long lx, long i) { long k; long c = coeff(x,i,1) * y[1]; for (k = 2; k < lx; k++) c += coeff(x,i,k) * y[k]; return c; } GEN zm_zc_mul(GEN x, GEN y) { long lx = lg(x), l, i; GEN z; if (lx == 1) return cgetg(1, t_VECSMALL); l = lg(gel(x,1)); z = cgetg(l,t_VECSMALL); for (i=1; i= ZM_sw_bound */ static GEN Flm_mul_i(GEN x, GEN y, long l, long lx, long ly, ulong p, ulong pi) { ulong e = expu(p); #ifdef LONG_IS_64BIT long ZM_sw_bound = e <= 29 ? 140: e <=62 ? 40: 70; #else long ZM_sw_bound = e <= 12 ? 230: e <=14 ? 170 : e <=17 ? 110: 120; #endif if (l <= ZM_sw_bound || lx <= ZM_sw_bound || ly <= ZM_sw_bound) return Flm_mul_classical(x, y, l, lx, ly, p, pi); else return Flm_mul_sw(x, y, l - 1, lx - 1, ly - 1, p, pi); } GEN Flm_mul_pre(GEN x, GEN y, ulong p, ulong pi) { long lx=lg(x), ly=lg(y); if (ly==1) return cgetg(1,t_MAT); if (lx==1) return zero_Flm(0, ly-1); return Flm_mul_i(x, y, lgcols(x), lx, ly, p, pi); } GEN Flm_mul(GEN x, GEN y, ulong p) { long lx=lg(x), ly=lg(y); if (ly==1) return cgetg(1,t_MAT); if (lx==1) return zero_Flm(0, ly-1); return Flm_mul_i(x, y, lgcols(x), lx, ly, p, get_Fl_red(p)); } struct _Flm { ulong p; long n; }; static GEN _Flm_mul(void *E , GEN x, GEN y) { return Flm_mul(x,y,((struct _Flm*)E)->p); } static GEN _Flm_sqr(void *E, GEN x) { return Flm_mul(x,x,((struct _Flm*)E)->p); } static GEN _Flm_one(void *E) { return matid_Flm(((struct _Flm*)E)->n); } GEN Flm_powu(GEN x, ulong n, ulong p) { struct _Flm d; if (!n) return matid(lg(x)-1); d.p = p; return gen_powu(x, n, (void*)&d, &_Flm_sqr, &_Flm_mul); } GEN Flm_powers(GEN x, ulong n, ulong p) { struct _Flm d; d.p = p; d.n = lg(x)-1; return gen_powers(x, n, 1, (void*)&d, &_Flm_sqr, &_Flm_mul, &_Flm_one); } static GEN _FpM_mul(void *p , GEN x, GEN y) { return FpM_mul(x,y,(GEN)p); } static GEN _FpM_sqr(void *p, GEN x) { return FpM_mul(x,x,(GEN)p); } GEN FpM_powu(GEN x, ulong n, GEN p) { if (!n) return matid(lg(x)-1); if (lgefint(p) == 3) { pari_sp av = avma; ulong pp = uel(p,2); GEN z; if (pp == 2) z = F2m_to_ZM(F2m_powu(ZM_to_F2m(x),n)); else z = Flm_to_ZM(Flm_powu(ZM_to_Flm(x, pp), n, pp)); return gerepileupto(av, z); } return gen_powu(x, n, (void*)p, &_FpM_sqr, &_FpM_mul); } /*Multiple a column vector by a line vector to make a matrix*/ GEN Flc_Flv_mul(GEN x, GEN y, ulong p) { long i,j, lx=lg(x), ly=lg(y); GEN z; if (ly==1) return cgetg(1,t_MAT); z = cgetg(ly, t_MAT); for (j=1; j < ly; j++) { GEN zj = cgetg(lx,t_VECSMALL); for (i=1; is(E,0); _1 = S->s(E,1); for (i=1; i<=n; i++) { GEN z = const_col(n, _0); gel(z,i) = _1; gel(y, i) = z; } return y; } GEN matid_F2xqM(long n, GEN T) { void *E; const struct bb_field *S = get_F2xq_field(&E, T); return gen_matid(n, E, S); } GEN matid_FlxqM(long n, GEN T, ulong p) { void *E; const struct bb_field *S = get_Flxq_field(&E, T, p); return gen_matid(n, E, S); } GEN matid_Flm(long n) { GEN y = cgetg(n+1,t_MAT); long i; if (n < 0) pari_err_DOMAIN("matid_Flm", "dimension","<",gen_0,stoi(n)); for (i=1; i<=n; i++) { gel(y,i) = zero_zv(n); ucoeff(y, i,i) = 1; } return y; } GEN scalar_Flm(long s, long n) { long i; GEN y = cgetg(n+1,t_MAT); for (i=1; i<=n; i++) { gel(y,i) = zero_Flv(n); coeff(y, i,i) = s; } return y; } /********************************************************************/ /** **/ /** CONVERSIONS **/ /** **/ /********************************************************************/ GEN ZV_to_Flv(GEN x, ulong p) { pari_APPLY_ulong(umodiu(gel(x,i), p)) } GEN ZM_to_Flm(GEN x, ulong p) { pari_APPLY_same(ZV_to_Flv(gel(x,i), p)) } GEN ZMV_to_FlmV(GEN x, ulong m) { pari_APPLY_type(t_VEC,ZM_to_Flm(gel(x,i), m)) } /* TO INTMOD */ static GEN to_intmod(GEN x, GEN p) { retmkintmod(modii(x, p), p); } static GEN Fl_to_intmod(ulong x, GEN p) { retmkintmod(utoi(x), p); } GEN Fp_to_mod(GEN z, GEN p) { retmkintmod(modii(z, p), icopy(p)); } GEN FpX_to_mod_raw(GEN z, GEN p) { long i,l = lg(z); GEN x; if (l == 2) { x = cgetg(3,t_POL); x[1] = z[1]; gel(x,2) = mkintmod(gen_0,p); return x; } x = cgetg(l,t_POL); for (i=2; i2) p = icopy(p); for (i=2; i1) pari_warn(warnmem,"Wiedemann: first loop, %ld",i); W = gerepileupto(av, W); } } b = FpX_renormalize(b, m+2); if (lgpol(b)==0) {set_avma(btop); continue; } M = FpX_halfgcd(b, pol_xn(m, 0), p); Q = FpX_neg(FpX_normalize(gcoeff(M, 2, 1),p),p); W = B; lQ =lg(Q); if (DEBUGLEVEL) err_printf("Wiedemann: deg. minpoly: %ld\n",lQ-3); V = FpC_Fp_mul(W, gel(Q, lQ-2), p); av = avma; for (i = lQ-3; i > 1; i--) { W = f(E, W); V = ZC_lincomb(gen_1, gel(Q,i), V, W); if (gc_needed(av,1)) { if (DEBUGMEM>1) pari_warn(warnmem,"Wiedemann: second loop, %ld",i); gerepileall(av, 2, &V, &W); } } V = FpC_red(V, p); W = FpC_sub(f(E,V), B, p); if (ZV_equal0(W)) return gerepilecopy(ltop, V); av = avma; for (i = 1; i <= n; ++i) { V = W; W = f(E, V); if (ZV_equal0(W)) return gerepilecopy(ltop, shallowtrans(V)); gerepileall(av, 2, &V, &W); } set_avma(btop); } return NULL; } GEN zMs_ZC_mul(GEN M, GEN B) { long i, j; long n = lg(B)-1; GEN V = zerocol(n); for (i = 1; i <= n; ++i) if (signe(gel(B, i))) { GEN R = gel(M, i), C = gel(R, 1), E = gel(R, 2); long l = lg(C); for (j = 1; j < l; ++j) { long k = C[j]; switch(E[j]) { case 1: gel(V, k) = gel(V,k)==gen_0 ? gel(B,i) : addii(gel(V, k), gel(B,i)); break; case -1: gel(V, k) = gel(V,k)==gen_0 ? negi(gel(B,i)) : subii(gel(V, k), gel(B,i)); break; default: gel(V, k) = gel(V,k)==gen_0 ? mulis(gel(B, i), E[j]) : addii(gel(V, k), mulis(gel(B, i), E[j])); break; } } } return V; } GEN FpMs_FpC_mul(GEN M, GEN B, GEN p) { return FpC_red(zMs_ZC_mul(M, B), p); } GEN ZV_zMs_mul(GEN B, GEN M) { long i, j; long m = lg(M)-1; GEN V = cgetg(m+1,t_VEC); for (i = 1; i <= m; ++i) { GEN R = gel(M, i), C = gel(R, 1), E = gel(R, 2); long l = lg(C); GEN z; if (l == 1) { gel(V,i) = gen_0; continue; } z = mulis(gel(B, C[1]), E[1]); for (j = 2; j < l; ++j) { long k = C[j]; switch(E[j]) { case 1: z = addii(z, gel(B,k)); break; case -1: z = subii(z, gel(B,k)); break; default: z = addii(z, mulis(gel(B,k), E[j])); break; } } gel(V,i) = z; } return V; } GEN FpV_FpMs_mul(GEN B, GEN M, GEN p) { return FpV_red(ZV_zMs_mul(B, M), p); } GEN ZlM_gauss(GEN a, GEN b, ulong p, long e, GEN C) { pari_sp av = avma, av2; GEN xi, xb, pi = gen_1, P; long i; if (!C) { C = Flm_inv(ZM_to_Flm(a, p), p); if (!C) pari_err_INV("ZlM_gauss", a); } P = utoipos(p); av2 = avma; xi = Flm_mul(C, ZM_to_Flm(b, p), p); xb = Flm_to_ZM(xi); for (i = 2; i <= e; i++) { pi = muliu(pi, p); /* = p^(i-1) */ b = ZM_Z_divexact(ZM_sub(b, ZM_nm_mul(a, xi)), P); if (gc_needed(av,2)) { if(DEBUGMEM>1) pari_warn(warnmem,"ZlM_gauss. i=%ld/%ld",i,e); gerepileall(av2,3, &pi,&b,&xb); } xi = Flm_mul(C, ZM_to_Flm(b, p), p); xb = ZM_add(xb, nm_Z_mul(xi, pi)); } return gerepileupto(av, xb); } struct wrapper_modp_s { GEN (*f)(void*, GEN); void *E; GEN p; }; /* compute f(x) mod p */ static GEN wrap_relcomb_modp(void *E, GEN x) { struct wrapper_modp_s *W = (struct wrapper_modp_s*)E; return FpC_red(W->f(W->E, x), W->p); } static GEN wrap_relcomb(void*E, GEN x) { return zMs_ZC_mul((GEN)E, x); } static GEN wrap_relker(void*E, GEN x) { return ZV_zMs_mul(x, (GEN)E); } /* Solve f(X) = B (mod p^e); blackbox version of ZlM_gauss */ GEN gen_ZpM_Dixon_Wiedemann(void *E, GEN (*f)(void*, GEN), GEN B, GEN p, long e) { struct wrapper_modp_s W; pari_sp av = avma; GEN xb, xi, pi = gen_1; long i; W.E = E; W.f = f; W.p = p; xi = gen_FpM_Wiedemann((void*)&W, wrap_relcomb_modp, FpC_red(B, p), p); /* f^(-1) B */ if (!xi || e == 1 || typ(xi) == t_VEC) return xi; xb = xi; for (i = 2; i <= e; i++) { pi = mulii(pi, p); /* = p^(i-1) */ B = ZC_Z_divexact(ZC_sub(B, f(E, xi)), p); if (gc_needed(av,2)) { if(DEBUGMEM>1) pari_warn(warnmem,"gen_ZpM_Dixon_Wiedemann. i=%ld",i); gerepileall(av,3, &pi,&B,&xb); } xi = gen_FpM_Wiedemann((void*)&W, wrap_relcomb_modp, FpC_red(B, p), p); if (!xi) return NULL; if (typ(xi) == t_VEC) return gerepileupto(av, xi); xb = ZC_add(xb, ZC_Z_mul(xi, pi)); } return gerepileupto(av, xb); } static GEN vecprow(GEN A, GEN prow) { return mkvec2(vecsmallpermute(prow,gel(A,1)), gel(A,2)); } /* Solve the equation MX = A. Return either a solution as a t_COL, * or the index of a column which is linearly dependent from the others as a * t_VECSMALL with a single component. */ GEN ZpMs_ZpCs_solve(GEN M, GEN A, long nbrow, GEN p, long e) { pari_sp av = avma; GEN pcol, prow; long nbi=lg(M)-1, lR; long i, n; GEN Mp, Ap, Rp; pari_timer ti; if (DEBUGLEVEL) timer_start(&ti); RgMs_structelim(M, nbrow, gel(A, 1), &pcol, &prow); if (!pcol) return gc_NULL(av); if (DEBUGLEVEL) timer_printf(&ti,"structured elimination (%ld -> %ld)",nbi,lg(pcol)-1); n = lg(pcol)-1; Mp = cgetg(n+1, t_MAT); for(i=1; i<=n; i++) gel(Mp, i) = vecprow(gel(M,pcol[i]), prow); Ap = zCs_to_ZC(vecprow(A, prow), n); if (DEBUGLEVEL) timer_start(&ti); Rp = gen_ZpM_Dixon_Wiedemann((void*)Mp,wrap_relcomb, Ap, p, e); if (DEBUGLEVEL) timer_printf(&ti,"linear algebra"); if (!Rp) return gc_NULL(av); lR = lg(Rp)-1; if (typ(Rp) == t_COL) { GEN R = zerocol(nbi+1); for (i=1; i<=lR; i++) gel(R,pcol[i]) = gel(Rp,i); return gerepilecopy(av, R); } for (i = 1; i <= lR; ++i) if (signe(gel(Rp, i))) return gerepileuptoleaf(av, mkvecsmall(pcol[i])); return NULL; /* LCOV_EXCL_LINE */ } GEN FpMs_FpCs_solve(GEN M, GEN A, long nbrow, GEN p) { return ZpMs_ZpCs_solve(M, A, nbrow, p, 1); } GEN FpMs_FpCs_solve_safe(GEN M, GEN A, long nbrow, GEN p) { GEN res; pari_CATCH(e_INV) { GEN E = pari_err_last(); GEN x = err_get_compo(E,2); if (typ(x) != t_INTMOD) pari_err(0,E); if (DEBUGLEVEL) pari_warn(warner,"FpMs_FpCs_solve_safe, impossible inverse %Ps", x); res = NULL; } pari_TRY { res = ZpMs_ZpCs_solve(M, A, nbrow, p, 1); } pari_ENDCATCH return res; } static GEN FpMs_structelim_back(GEN M, GEN V, GEN prow, GEN p) { long i, j, oldf = 0, f = 0; long lrow = lg(prow), lM = lg(M); GEN W = const_vecsmall(lM-1,1); GEN R = cgetg(lrow, t_VEC); for (i=1; i=2) continue; if (c==1) { pari_sp av = avma; GEN s = gen_0; for(j=1; j %ld)",nbcol,lg(pcol)-1); n = lg(pcol)-1; Mp = cgetg(n+1, t_MAT); for(i=1; i<=n; i++) gel(Mp, i) = vecprow(gel(M,pcol[i]), prow); W.E = (void*) Mp; W.f = wrap_relker; W.p = p; av2 = avma; for(;;) { set_avma(av2); B = random_FpV(n, p); MB = FpV_FpMs_mul(B, Mp, p); if (DEBUGLEVEL) timer_start(&ti); pari_CATCH(e_INV) { GEN E = pari_err_last(); GEN x = err_get_compo(E,2); if (typ(x) != t_INTMOD) pari_err(0,E); if (DEBUGLEVEL) pari_warn(warner,"FpMs_leftkernel_elt, impossible inverse %Ps", x); Rp = NULL; } pari_TRY { Rp = gen_FpM_Wiedemann((void*)&W, wrap_relcomb_modp, MB, p); } pari_ENDCATCH if (!Rp) continue; if (typ(Rp)==t_COL) { Rp = FpC_sub(Rp,B,p); if (ZV_equal0(Rp)) continue; } R = FpMs_structelim_back(M, Rp, prow, p); if (DEBUGLEVEL) timer_printf(&ti,"Wiedemann left kernel"); return gerepilecopy(av, R); } } GEN FpMs_leftkernel_elt(GEN M, long nbrow, GEN p) { return FpMs_leftkernel_elt_col(M, lg(M)-1, nbrow, p); }