Testing latest pari + WASM + node.js... and it works?! Wow.
License: GPL3
ubuntu2004
/* Copyright (C) 2000-2019 The PARI group.12This file is part of the PARI/GP package.34PARI/GP is free software; you can redistribute it and/or modify it under the5terms of the GNU General Public License as published by the Free Software6Foundation; either version 2 of the License, or (at your option) any later7version. It is distributed in the hope that it will be useful, but WITHOUT8ANY WARRANTY WHATSOEVER.910Check the License for details. You should have received a copy of it, along11with the package; see the file 'COPYING'. If not, write to the Free Software12Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */1314#include "pari.h"15#include "paripriv.h"1617GEN18Flv_to_ZV(GEN x)19{ pari_APPLY_type(t_VEC, utoi(x[i])) }2021GEN22Flc_to_ZC(GEN x)23{ pari_APPLY_type(t_COL, utoi(x[i])) }2425GEN26Flm_to_ZM(GEN x)27{ pari_APPLY_type(t_MAT, Flc_to_ZC(gel(x,i))) }2829GEN30Flc_to_ZC_inplace(GEN z)31{32long i, l = lg(z);33for (i=1; i<l; i++) gel(z,i) = utoi(z[i]);34settyp(z, t_COL);35return z;36}3738GEN39Flm_to_ZM_inplace(GEN z)40{41long i, l = lg(z);42for (i=1; i<l; i++) Flc_to_ZC_inplace(gel(z, i));43return z;44}4546static GEN47Flm_solve_upper_1(GEN U, GEN B, ulong p, ulong pi)48{ return Flm_Fl_mul_pre(B, Fl_inv(ucoeff(U, 1, 1), p), p, pi); }4950static GEN51Flm_rsolve_upper_2(GEN U, GEN B, ulong p, ulong pi)52{53ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);54ulong D = Fl_mul_pre(a, d, p, pi), Dinv = Fl_inv(D, p);55ulong ainv = Fl_mul_pre(d, Dinv, p, pi);56ulong dinv = Fl_mul_pre(a, Dinv, p, pi);57GEN B1 = rowslice(B, 1, 1);58GEN B2 = rowslice(B, 2, 2);59GEN X2 = Flm_Fl_mul_pre(B2, dinv, p, pi);60GEN X1 = Flm_Fl_mul_pre(Flm_sub(B1, Flm_Fl_mul_pre(X2, b, p, pi), p),61ainv, p, pi);62return vconcat(X1, X2);63}6465/* solve U*X = B, U upper triangular and invertible */66static GEN67Flm_rsolve_upper_pre(GEN U, GEN B, ulong p, ulong pi)68{69long n = lg(U) - 1, n1;70GEN U2, U11, U12, U22, B1, B2, X1, X2, X;71pari_sp av = avma;7273if (n == 0) return B;74if (n == 1) return Flm_solve_upper_1(U, B, p, pi);75if (n == 2) return Flm_rsolve_upper_2(U, B, p, pi);76n1 = (n + 1)/2;77U2 = vecslice(U, n1 + 1, n);78U22 = rowslice(U2, n1 + 1, n);79B2 = rowslice(B, n1 + 1, n);80X2 = Flm_rsolve_upper_pre(U22, B2, p, pi);81U12 = rowslice(U2, 1, n1);82B1 = rowslice(B, 1, n1);83B1 = Flm_sub(B1, Flm_mul_pre(U12, X2, p, pi), p);84if (gc_needed(av, 1)) gerepileall(av, 2, &B1, &X2);85U11 = matslice(U, 1,n1, 1,n1);86X1 = Flm_rsolve_upper_pre(U11, B1, p, pi);87X = vconcat(X1, X2);88if (gc_needed(av, 1)) X = gerepilecopy(av, X);89return X;90}9192static GEN93Flm_lsolve_upper_2(GEN U, GEN B, ulong p, ulong pi)94{95ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);96ulong D = Fl_mul_pre(a, d, p, pi), Dinv = Fl_inv(D, p);97ulong ainv = Fl_mul_pre(d, Dinv, p, pi);98ulong dinv = Fl_mul_pre(a, Dinv, p, pi);99GEN B1 = vecslice(B, 1, 1);100GEN B2 = vecslice(B, 2, 2);101GEN X1 = Flm_Fl_mul_pre(B1, ainv, p, pi);102GEN X2 = Flm_Fl_mul_pre(Flm_sub(B2, Flm_Fl_mul_pre(X1, b, p, pi), p),103dinv, p, pi);104return shallowconcat(X1, X2);105}106107/* solve X*U = B, U upper triangular and invertible */108static GEN109Flm_lsolve_upper_pre(GEN U, GEN B, ulong p, ulong pi)110{111long n = lg(U) - 1, n1;112GEN U2, U11, U12, U22, B1, B2, X1, X2, X;113pari_sp av = avma;114115if (n == 0) return B;116if (n == 1) return Flm_solve_upper_1(U, B, p, pi);117if (n == 2) return Flm_lsolve_upper_2(U, B, p, pi);118n1 = (n + 1)/2;119U2 = vecslice(U, n1 + 1, n);120U11 = matslice(U, 1,n1, 1,n1);121U12 = rowslice(U2, 1, n1);122U22 = rowslice(U2, n1 + 1, n);123B1 = vecslice(B, 1, n1);124B2 = vecslice(B, n1 + 1, n);125X1 = Flm_lsolve_upper_pre(U11, B1, p, pi);126B2 = Flm_sub(B2, Flm_mul_pre(X1, U12, p, pi), p);127if (gc_needed(av, 1)) gerepileall(av, 3, &B2, &U22, &X1);128X2 = Flm_lsolve_upper_pre(U22, B2, p, pi);129X = shallowconcat(X1, X2);130if (gc_needed(av, 1)) X = gerepilecopy(av, X);131return X;132}133134static GEN135Flm_rsolve_lower_unit_2(GEN L, GEN A, ulong p, ulong pi)136{137GEN X1 = rowslice(A, 1, 1);138GEN X2 = Flm_sub(rowslice(A, 2, 2),139Flm_Fl_mul_pre(X1, ucoeff(L, 2, 1), p, pi), p);140return vconcat(X1, X2);141}142143/* solve L*X = A, L lower triangular with ones on the diagonal144* (at least as many rows as columns) */145static GEN146Flm_rsolve_lower_unit_pre(GEN L, GEN A, ulong p, ulong pi)147{148long m = lg(L) - 1, m1, n;149GEN L1, L11, L21, L22, A1, A2, X1, X2, X;150pari_sp av = avma;151152if (m == 0) return zero_Flm(0, lg(A) - 1);153if (m == 1) return rowslice(A, 1, 1);154if (m == 2) return Flm_rsolve_lower_unit_2(L, A, p, pi);155m1 = (m + 1)/2;156n = nbrows(L);157L1 = vecslice(L, 1, m1);158L11 = rowslice(L1, 1, m1);159L21 = rowslice(L1, m1 + 1, n);160A1 = rowslice(A, 1, m1);161X1 = Flm_rsolve_lower_unit_pre(L11, A1, p, pi);162A2 = rowslice(A, m1 + 1, n);163A2 = Flm_sub(A2, Flm_mul_pre(L21, X1, p, pi), p);164if (gc_needed(av, 1)) gerepileall(av, 2, &A2, &X1);165L22 = matslice(L, m1+1,n, m1+1,m);166X2 = Flm_rsolve_lower_unit_pre(L22, A2, p, pi);167X = vconcat(X1, X2);168if (gc_needed(av, 1)) X = gerepilecopy(av, X);169return X;170}171172static GEN173Flm_lsolve_lower_unit_2(GEN L, GEN A, ulong p, ulong pi)174{175GEN X2 = vecslice(A, 2, 2);176GEN X1 = Flm_sub(vecslice(A, 1, 1),177Flm_Fl_mul_pre(X2, ucoeff(L, 2, 1), p, pi), p);178return shallowconcat(X1, X2);179}180181/* solve L*X = A, L square lower triangular with ones on the diagonal */182static GEN183Flm_lsolve_lower_unit_pre(GEN L, GEN A, ulong p, ulong pi)184{185long m = lg(L) - 1, m1;186GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;187pari_sp av = avma;188189if (m <= 1) return A;190if (m == 2) return Flm_lsolve_lower_unit_2(L, A, p, pi);191m1 = (m + 1)/2;192L2 = vecslice(L, m1 + 1, m);193L22 = rowslice(L2, m1 + 1, m);194A2 = vecslice(A, m1 + 1, m);195X2 = Flm_lsolve_lower_unit_pre(L22, A2, p, pi);196if (gc_needed(av, 1)) X2 = gerepilecopy(av, X2);197L1 = vecslice(L, 1, m1);198L21 = rowslice(L1, m1 + 1, m);199A1 = vecslice(A, 1, m1);200A1 = Flm_sub(A1, Flm_mul_pre(X2, L21, p, pi), p);201L11 = rowslice(L1, 1, m1);202if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);203X1 = Flm_lsolve_lower_unit_pre(L11, A1, p, pi);204X = shallowconcat(X1, X2);205if (gc_needed(av, 1)) X = gerepilecopy(av, X);206return X;207}208209/* destroy A */210static long211Flm_CUP_basecase(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p, ulong pi)212{213long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc;214ulong u, v;215216if (P) *P = identity_perm(n);217*R = cgetg(m + 1, t_VECSMALL);218for (j = 1, pr = 0; j <= n; j++)219{220for (pr++, pc = 0; pr <= m; pr++)221{222for (k = j; k <= n; k++) { v = ucoeff(A, pr, k); if (!pc && v) pc = k; }223if (pc) break;224}225if (!pc) break;226(*R)[j] = pr;227if (pc != j)228{229swap(gel(A, j), gel(A, pc));230if (P) lswap((*P)[j], (*P)[pc]);231}232u = Fl_inv(ucoeff(A, pr, j), p);233for (i = pr + 1; i <= m; i++)234{235v = Fl_mul_pre(ucoeff(A, i, j), u, p, pi);236ucoeff(A, i, j) = v;237v = Fl_neg(v, p);238for (k = j + 1; k <= n; k++)239ucoeff(A, i, k) = Fl_addmul_pre(ucoeff(A, i, k),240ucoeff(A, pr, k), v, p, pi);241}242}243setlg(*R, j);244*C = vecslice(A, 1, j - 1);245if (U) *U = rowpermute(A, *R);246return j - 1;247}248249static const long Flm_CUP_LIMIT = 8;250251static long252Flm_CUP_pre(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p, ulong pi)253{254long m = nbrows(A), m1, n = lg(A) - 1, i, r1, r2, r;255GEN R1, C1, U1, P1, R2, C2, U2, P2;256GEN A1, A2, B2, C21, U11, U12, T21, T22;257pari_sp av = avma;258259if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)260/* destroy A; not called at the outermost recursion level */261return Flm_CUP_basecase(A, R, C, U, P, p, pi);262m1 = (minss(m, n) + 1)/2;263A1 = rowslice(A, 1, m1);264A2 = rowslice(A, m1 + 1, m);265r1 = Flm_CUP_pre(A1, &R1, &C1, &U1, &P1, p, pi);266if (r1 == 0)267{268r2 = Flm_CUP_pre(A2, &R2, &C2, &U2, &P2, p, pi);269*R = cgetg(r2 + 1, t_VECSMALL);270for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;271*C = vconcat(zero_Flm(m1, r2), C2);272*U = U2;273*P = P2;274r = r2;275}276else277{278U11 = vecslice(U1, 1, r1);279U12 = vecslice(U1, r1 + 1, n);280T21 = vecslicepermute(A2, P1, 1, r1);281T22 = vecslicepermute(A2, P1, r1 + 1, n);282C21 = Flm_lsolve_upper_pre(U11, T21, p, pi);283if (gc_needed(av, 1))284gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);285B2 = Flm_sub(T22, Flm_mul_pre(C21, U12, p, pi), p);286r2 = Flm_CUP_pre(B2, &R2, &C2, &U2, &P2, p, pi);287r = r1 + r2;288*R = cgetg(r + 1, t_VECSMALL);289for (i = 1; i <= r1; i++) (*R)[i] = R1[i];290for (; i <= r; i++) (*R)[i] = R2[i - r1] + m1;291*C = shallowconcat(vconcat(C1, C21),292vconcat(zero_Flm(m1, r2), C2));293*U = shallowconcat(vconcat(U11, zero_Flm(r2, r1)),294vconcat(vecpermute(U12, P2), U2));295*P = cgetg(n + 1, t_VECSMALL);296for (i = 1; i <= r1; i++) (*P)[i] = P1[i];297for (; i <= n; i++) (*P)[i] = P1[P2[i - r1] + r1];298}299if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);300return r;301}302303static long304Flm_echelon_gauss(GEN A, GEN *R, GEN *C, ulong p, ulong pi)305{ return Flm_CUP_basecase(A, R, C, NULL, NULL, p, pi); }306307/* complement of a strictly increasing subsequence of (1, 2, ..., n) */308static GEN309indexcompl(GEN v, long n)310{311long i, j, k, m = lg(v) - 1;312GEN w = cgetg(n - m + 1, t_VECSMALL);313for (i = j = k = 1; i <= n; i++)314if (j <= m && v[j] == i) j++; else w[k++] = i;315return w;316}317318/* column echelon form */319static long320Flm_echelon_pre(GEN A, GEN *R, GEN *C, ulong p, ulong pi)321{322long j, j1, j2, m = nbrows(A), n = lg(A) - 1, n1, r, r1, r2;323GEN A1, A2, R1, R1c, C1, R2, C2;324GEN A12, A22, B2, C11, C21, M12;325pari_sp av = avma;326327if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)328return Flm_echelon_gauss(Flm_copy(A), R, C, p, pi);329330n1 = (n + 1)/2;331A1 = vecslice(A, 1, n1);332A2 = vecslice(A, n1 + 1, n);333r1 = Flm_echelon_pre(A1, &R1, &C1, p, pi);334if (!r1) return Flm_echelon_pre(A2, R, C, p, pi);335if (r1 == m) { *R = R1; *C = C1; return r1; }336337R1c = indexcompl(R1, m);338C11 = rowpermute(C1, R1);339C21 = rowpermute(C1, R1c);340A12 = rowpermute(A2, R1);341A22 = rowpermute(A2, R1c);342M12 = Flm_rsolve_lower_unit_pre(C11, A12, p, pi);343B2 = Flm_sub(A22, Flm_mul_pre(C21, M12, p, pi), p);344r2 = Flm_echelon_pre(B2, &R2, &C2, p, pi);345if (!r2) { *R = R1; *C = C1; r = r1; }346else347{348R2 = perm_mul(R1c, R2);349C2 = rowpermute(vconcat(zero_Flm(r1, r2), C2),350perm_inv(vecsmall_concat(R1, R1c)));351r = r1 + r2;352*R = cgetg(r + 1, t_VECSMALL);353*C = cgetg(r + 1, t_MAT);354for (j = j1 = j2 = 1; j <= r; j++)355if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))356{357gel(*C, j) = gel(C1, j1);358(*R)[j] = R1[j1++];359}360else361{362gel(*C, j) = gel(C2, j2);363(*R)[j] = R2[j2++];364}365}366if (gc_needed(av, 1)) gerepileall(av, 2, R, C);367return r;368}369370static void /* assume m < p */371_Fl_addmul(GEN b, long k, long i, ulong m, ulong p, ulong pi)372{373uel(b,k) = Fl_addmul_pre(uel(b, k), m, uel(b, i), p, pi);374}375static void /* same m = 1 */376_Fl_add(GEN b, long k, long i, ulong p)377{378uel(b,k) = Fl_add(uel(b,k), uel(b,i), p);379}380static void /* assume m < p && SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */381_Fl_addmul_OK(GEN b, long k, long i, ulong m, ulong p)382{383uel(b,k) += m * uel(b,i);384if (uel(b,k) & HIGHMASK) uel(b,k) %= p;385}386static void /* assume SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */387_Fl_add_OK(GEN b, long k, long i, ulong p)388{389uel(b,k) += uel(b,i);390if (uel(b,k) & HIGHMASK) uel(b,k) %= p;391}392393/* assume 0 <= a[i,j] < p */394static GEN395Fl_get_col_OK(GEN a, GEN b, long li, ulong p)396{397GEN u = cgetg(li+1,t_VECSMALL);398ulong m = uel(b,li) % p;399long i,j;400401uel(u,li) = (m * ucoeff(a,li,li)) % p;402for (i = li-1; i > 0; i--)403{404m = p - uel(b,i)%p;405for (j = i+1; j <= li; j++) {406if (m & HIGHBIT) m %= p;407m += ucoeff(a,i,j) * uel(u,j); /* 0 <= u[j] < p */408}409m %= p;410if (m) m = ((p-m) * ucoeff(a,i,i)) % p;411uel(u,i) = m;412}413return u;414}415static GEN416Fl_get_col(GEN a, GEN b, long li, ulong p)417{418GEN u = cgetg(li+1,t_VECSMALL);419ulong m = uel(b,li) % p;420long i,j;421422uel(u,li) = Fl_mul(m, ucoeff(a,li,li), p);423for (i=li-1; i>0; i--)424{425m = b[i]%p;426for (j = i+1; j <= li; j++)427m = Fl_sub(m, Fl_mul(ucoeff(a,i,j), uel(u,j), p), p);428if (m) m = Fl_mul(m, ucoeff(a,i,i), p);429uel(u,i) = m;430}431return u;432}433434static GEN435Flm_ker_gauss_OK(GEN x, ulong p, long deplin)436{437GEN y, c, d;438long i, j, k, r, t, m, n;439ulong a;440441n = lg(x)-1;442m=nbrows(x); r=0;443444c = zero_zv(m);445d = cgetg(n+1, t_VECSMALL);446a = 0; /* for gcc -Wall */447for (k=1; k<=n; k++)448{449for (j=1; j<=m; j++)450if (!c[j])451{452a = ucoeff(x,j,k) % p;453if (a) break;454}455if (j > m)456{457if (deplin==1) {458c = cgetg(n+1, t_VECSMALL);459for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k) % p;460c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;461return c;462}463r++; d[k] = 0;464}465else466{467ulong piv = p - Fl_inv(a, p); /* -1/a */468c[j] = k; d[k] = j;469ucoeff(x,j,k) = p-1;470if (piv != 1)471for (i=k+1; i<=n; i++) ucoeff(x,j,i) = (piv * ucoeff(x,j,i)) % p;472for (t=1; t<=m; t++)473{474if (t == j) continue;475476piv = ( ucoeff(x,t,k) %= p );477if (!piv) continue;478if (piv == 1)479for (i=k+1; i<=n; i++) _Fl_add_OK(gel(x,i),t,j, p);480else481for (i=k+1; i<=n; i++) _Fl_addmul_OK(gel(x,i),t,j,piv, p);482}483}484}485if (deplin==1) return NULL;486487y = cgetg(r+1, t_MAT);488for (j=k=1; j<=r; j++,k++)489{490GEN C = cgetg(n+1, t_VECSMALL);491492gel(y,j) = C; while (d[k]) k++;493for (i=1; i<k; i++)494if (d[i])495uel(C,i) = ucoeff(x,d[i],k) % p;496else497uel(C,i) = 0UL;498uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;499}500if (deplin == 2) {501GEN pc = cgetg(n - r + 1, t_VECSMALL); /* indices of pivot columns */502for (i = j = 1; j <= n; j++)503if (d[j]) pc[i++] = j;504return mkvec2(y, pc);505}506return y;507}508509/* in place, destroy x */510static GEN511Flm_ker_gauss(GEN x, ulong p, long deplin)512{513GEN y, c, d;514long i, j, k, r, t, m, n;515ulong a, pi;516n = lg(x)-1;517if (!n) return cgetg(1,t_MAT);518if (SMALL_ULONG(p)) return Flm_ker_gauss_OK(x, p, deplin);519pi = get_Fl_red(p);520521m=nbrows(x); r=0;522523c = zero_zv(m);524d = cgetg(n+1, t_VECSMALL);525a = 0; /* for gcc -Wall */526for (k=1; k<=n; k++)527{528for (j=1; j<=m; j++)529if (!c[j])530{531a = ucoeff(x,j,k);532if (a) break;533}534if (j > m)535{536if (deplin==1) {537c = cgetg(n+1, t_VECSMALL);538for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k);539c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;540return c;541}542r++; d[k] = 0;543}544else545{546ulong piv = p - Fl_inv(a, p); /* -1/a */547c[j] = k; d[k] = j;548ucoeff(x,j,k) = p-1;549if (piv != 1)550for (i=k+1; i<=n; i++)551ucoeff(x,j,i) = Fl_mul_pre(piv, ucoeff(x,j,i), p, pi);552for (t=1; t<=m; t++)553{554if (t == j) continue;555556piv = ucoeff(x,t,k);557if (!piv) continue;558if (piv == 1)559for (i=k+1; i<=n; i++) _Fl_add(gel(x,i),t,j,p);560else561for (i=k+1; i<=n; i++) _Fl_addmul(gel(x,i),t,j,piv,p, pi);562}563}564}565if (deplin==1) return NULL;566567y = cgetg(r+1, t_MAT);568for (j=k=1; j<=r; j++,k++)569{570GEN C = cgetg(n+1, t_VECSMALL);571572gel(y,j) = C; while (d[k]) k++;573for (i=1; i<k; i++)574if (d[i])575uel(C,i) = ucoeff(x,d[i],k);576else577uel(C,i) = 0UL;578uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;579}580if (deplin == 2) {581GEN pc = cgetg(n - r + 1, t_VECSMALL); /* indices of pivot columns */582for (i = j = 1; j <= n; j++)583if (d[j]) pc[i++] = j;584return mkvec2(y, pc);585}586return y;587}588589GEN590Flm_intersect_i(GEN x, GEN y, ulong p)591{592long j, lx = lg(x);593GEN z;594595if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);596z = Flm_ker(shallowconcat(x,y), p);597for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);598return Flm_mul(x,z,p);599}600GEN601Flm_intersect(GEN x, GEN y, ulong p)602{603pari_sp av = avma;604return gerepileupto(av, Flm_image(Flm_intersect_i(x, y, p), p));605}606607static GEN608Flm_ker_echelon(GEN x, ulong p, long pivots) {609pari_sp av = avma;610GEN R, Rc, C, C1, C2, S, K;611long n = lg(x) - 1, r;612ulong pi = get_Fl_red(p);613r = Flm_echelon_pre(Flm_transpose(x), &R, &C, p, pi);614Rc = indexcompl(R, n);615C1 = rowpermute(C, R);616C2 = rowpermute(C, Rc);617S = Flm_lsolve_lower_unit_pre(C1, C2, p, pi);618K = vecpermute(shallowconcat(Flm_neg(S, p), matid_Flm(n - r)),619perm_inv(vecsmall_concat(R, Rc)));620K = Flm_transpose(K);621if (pivots)622return gerepilecopy(av, mkvec2(K, R));623return gerepileupto(av, K);624}625626static GEN627Flm_deplin_echelon(GEN x, ulong p) {628pari_sp av = avma;629GEN R, Rc, C, C1, C2, s, v;630long i, n = lg(x) - 1, r;631ulong pi = get_Fl_red(p);632r = Flm_echelon_pre(Flm_transpose(x), &R, &C, p, pi);633if (r == n) return gc_NULL(av);634Rc = indexcompl(R, n);635i = Rc[1];636C1 = rowpermute(C, R);637C2 = rowslice(C, i, i);638s = Flm_row(Flm_lsolve_lower_unit_pre(C1, C2, p, pi), 1);639v = vecsmallpermute(vecsmall_concat(Flv_neg(s, p), vecsmall_ei(n - r, 1)),640perm_inv(vecsmall_concat(R, Rc)));641return gerepileuptoleaf(av, v);642}643644static GEN645Flm_ker_i(GEN x, ulong p, long deplin, long inplace) {646if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)647switch(deplin) {648case 0: return Flm_ker_echelon(x, p, 0);649case 1: return Flm_deplin_echelon(x, p);650case 2: return Flm_ker_echelon(x, p, 1);651}652return Flm_ker_gauss(inplace? x: Flm_copy(x), p, deplin);653}654655GEN656Flm_ker_sp(GEN x, ulong p, long deplin) {657return Flm_ker_i(x, p, deplin, 1);658}659660GEN661Flm_ker(GEN x, ulong p) {662return Flm_ker_i(x, p, 0, 0);663}664665GEN666Flm_deplin(GEN x, ulong p) {667return Flm_ker_i(x, p, 1, 0);668}669670/* in place, destroy a, SMALL_ULONG(p) is TRUE */671static ulong672Flm_det_gauss_OK(GEN a, long nbco, ulong p)673{674long i,j,k, s = 1;675ulong q, x = 1;676677for (i=1; i<nbco; i++)678{679for(k=i; k<=nbco; k++)680{681ulong c = ucoeff(a,k,i) % p;682ucoeff(a,k,i) = c;683if (c) break;684}685for(j=k+1; j<=nbco; j++) ucoeff(a,j,i) %= p;686if (k > nbco) return ucoeff(a,i,i);687if (k != i)688{ /* exchange the lines s.t. k = i */689for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));690s = -s;691}692q = ucoeff(a,i,i);693694if (x & HIGHMASK) x %= p;695x *= q;696q = Fl_inv(q,p);697for (k=i+1; k<=nbco; k++)698{699ulong m = ucoeff(a,i,k) % p;700if (!m) continue;701702m = p - ((m*q)%p);703for (j=i+1; j<=nbco; j++)704{705ulong c = ucoeff(a,j,k);706if (c & HIGHMASK) c %= p;707ucoeff(a,j,k) = c + m*ucoeff(a,j,i);708}709}710}711if (x & HIGHMASK) x %= p;712q = ucoeff(a,nbco,nbco);713if (q & HIGHMASK) q %= p;714x = (x*q) % p;715if (s < 0 && x) x = p - x;716return x;717}718719/* in place, destroy a */720static ulong721Flm_det_gauss(GEN a, ulong p)722{723long i,j,k, s = 1, nbco = lg(a)-1;724ulong pi, q, x = 1;725726if (SMALL_ULONG(p)) return Flm_det_gauss_OK(a, nbco, p);727pi = get_Fl_red(p);728for (i=1; i<nbco; i++)729{730for(k=i; k<=nbco; k++)731if (ucoeff(a,k,i)) break;732if (k > nbco) return ucoeff(a,i,i);733if (k != i)734{ /* exchange the lines s.t. k = i */735for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));736s = -s;737}738q = ucoeff(a,i,i);739740x = Fl_mul_pre(x, q, p, pi);741q = Fl_inv(q,p);742for (k=i+1; k<=nbco; k++)743{744ulong m = ucoeff(a,i,k);745if (!m) continue;746747m = Fl_mul_pre(m, q, p, pi);748for (j=i+1; j<=nbco; j++)749ucoeff(a,j,k) = Fl_sub(ucoeff(a,j,k), Fl_mul_pre(m,ucoeff(a,j,i), p, pi), p);750}751}752if (s < 0) x = Fl_neg(x, p);753return Fl_mul(x, ucoeff(a,nbco,nbco), p);754}755756static ulong757Flm_det_CUP(GEN a, ulong p) {758GEN R, C, U, P;759long i, n = lg(a) - 1, r;760ulong d;761ulong pi = get_Fl_red(p);762r = Flm_CUP_pre(a, &R, &C, &U, &P, p, pi);763if (r < n)764d = 0;765else {766d = perm_sign(P) == 1? 1: p-1;767for (i = 1; i <= n; i++)768d = Fl_mul_pre(d, ucoeff(U, i, i), p, pi);769}770return d;771}772773static ulong774Flm_det_i(GEN x, ulong p, long inplace) {775pari_sp av = avma;776ulong d;777if (lg(x) - 1 >= Flm_CUP_LIMIT)778d = Flm_det_CUP(x, p);779else780d = Flm_det_gauss(inplace? x: Flm_copy(x), p);781return gc_ulong(av, d);782}783784ulong785Flm_det_sp(GEN x, ulong p) { return Flm_det_i(x, p, 1); }786ulong787Flm_det(GEN x, ulong p) { return Flm_det_i(x, p, 0); }788789/* Destroy x */790static GEN791Flm_gauss_pivot(GEN x, ulong p, long *rr)792{793GEN c,d;794long i,j,k,r,t,n,m;795796n=lg(x)-1; if (!n) { *rr=0; return NULL; }797798m=nbrows(x); r=0;799d=cgetg(n+1,t_VECSMALL);800c = zero_zv(m);801for (k=1; k<=n; k++)802{803for (j=1; j<=m; j++)804if (!c[j])805{806ucoeff(x,j,k) %= p;807if (ucoeff(x,j,k)) break;808}809if (j>m) { r++; d[k]=0; }810else811{812ulong piv = p - Fl_inv(ucoeff(x,j,k), p);813c[j]=k; d[k]=j;814for (i=k+1; i<=n; i++)815ucoeff(x,j,i) = Fl_mul(piv, ucoeff(x,j,i), p);816for (t=1; t<=m; t++)817if (!c[t]) /* no pivot on that line yet */818{819piv = ucoeff(x,t,k);820if (piv)821{822ucoeff(x,t,k) = 0;823for (i=k+1; i<=n; i++)824ucoeff(x,t,i) = Fl_add(ucoeff(x,t,i),825Fl_mul(piv,ucoeff(x,j,i),p),p);826}827}828for (i=k; i<=n; i++) ucoeff(x,j,i) = 0; /* dummy */829}830}831*rr = r; return gc_const((pari_sp)d, d);832}833834static GEN835Flm_pivots_CUP(GEN x, ulong p, long *rr)836{837long i, n = lg(x) - 1, r;838GEN R, C, U, P, d = zero_zv(n);839ulong pi = get_Fl_red(p);840r = Flm_CUP_pre(x, &R, &C, &U, &P, p, pi);841for(i = 1; i <= r; i++)842d[P[i]] = R[i];843*rr = n - r; return gc_const((pari_sp)d, d);844}845846GEN847Flm_pivots(GEN x, ulong p, long *rr, long inplace)848{849if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)850return Flm_pivots_CUP(x, p, rr);851return Flm_gauss_pivot(inplace? x: Flm_copy(x), p, rr);852}853854long855Flm_rank(GEN x, ulong p)856{857pari_sp av = avma;858long r;859if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT) {860GEN R, C;861ulong pi = get_Fl_red(p);862return gc_long(av, Flm_echelon_pre(x, &R, &C, p, pi));863}864(void) Flm_pivots(x, p, &r, 0);865return gc_long(av, lg(x)-1 - r);866}867868/* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal,869* reduced mod p */870static GEN871Flm_inv_upper_1_ind(GEN A, long index, ulong p)872{873long n = lg(A)-1, i = index, j;874GEN u = const_vecsmall(n, 0);875u[i] = 1;876if (SMALL_ULONG(p))877for (i--; i>0; i--)878{879ulong m = ucoeff(A,i,i+1) * uel(u,i+1); /* j = i+1 */880for (j=i+2; j<=n; j++)881{882if (m & HIGHMASK) m %= p;883m += ucoeff(A,i,j) * uel(u,j);884}885u[i] = Fl_neg(m % p, p);886}887else888for (i--; i>0; i--)889{890ulong m = Fl_mul(ucoeff(A,i,i+1),uel(u,i+1), p); /* j = i+1 */891for (j=i+2; j<=n; j++) m = Fl_add(m, Fl_mul(ucoeff(A,i,j),uel(u,j),p), p);892u[i] = Fl_neg(m, p);893}894return u;895}896static GEN897Flm_inv_upper_1(GEN A, ulong p)898{899long i, l;900GEN B = cgetg_copy(A, &l);901for (i = 1; i < l; i++) gel(B,i) = Flm_inv_upper_1_ind(A, i, p);902return B;903}904/* destroy a, b */905static GEN906Flm_gauss_sp_OK(GEN a, GEN b, ulong *detp, ulong p)907{908long i, j, k, li, bco, aco = lg(a)-1, s = 1;909ulong det = 1;910GEN u;911912li = nbrows(a);913bco = lg(b)-1;914for (i=1; i<=aco; i++)915{916ulong invpiv;917/* Fl_get_col wants 0 <= a[i,j] < p for all i,j */918for (k = 1; k < i; k++) ucoeff(a,k,i) %= p;919for (k = i; k <= li; k++)920{921ulong piv = ( ucoeff(a,k,i) %= p );922if (piv)923{924ucoeff(a,k,i) = Fl_inv(piv, p);925if (detp)926{927if (det & HIGHMASK) det %= p;928det *= piv;929}930break;931}932}933/* found a pivot on line k */934if (k > li) return NULL;935if (k != i)936{ /* swap lines so that k = i */937s = -s;938for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));939for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));940}941if (i == aco) break;942943invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */944for (k=i+1; k<=li; k++)945{946ulong m = ( ucoeff(a,k,i) %= p );947if (!m) continue;948949m = Fl_mul(m, invpiv, p);950if (m == 1) {951for (j=i+1; j<=aco; j++) _Fl_add_OK(gel(a,j),k,i, p);952for (j=1; j<=bco; j++) _Fl_add_OK(gel(b,j),k,i, p);953} else {954for (j=i+1; j<=aco; j++) _Fl_addmul_OK(gel(a,j),k,i,m, p);955for (j=1; j<=bco; j++) _Fl_addmul_OK(gel(b,j),k,i,m, p);956}957}958}959if (detp)960{961det %= p;962if (s < 0 && det) det = p - det;963*detp = det;964}965u = cgetg(bco+1,t_MAT);966for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col_OK(a,gel(b,j), aco,p);967return u;968}969970/* destroy a, b */971static GEN972Flm_gauss_sp_i(GEN a, GEN b, ulong *detp, ulong p)973{974long i, j, k, li, bco, aco = lg(a)-1, s = 1;975ulong det = 1;976GEN u;977ulong pi;978if (!aco) { if (detp) *detp = 1; return cgetg(1,t_MAT); }979if (SMALL_ULONG(p)) return Flm_gauss_sp_OK(a, b, detp, p);980pi = get_Fl_red(p);981li = nbrows(a);982bco = lg(b)-1;983for (i=1; i<=aco; i++)984{985ulong invpiv;986/* Fl_get_col wants 0 <= a[i,j] < p for all i,j */987for (k = i; k <= li; k++)988{989ulong piv = ucoeff(a,k,i);990if (piv)991{992ucoeff(a,k,i) = Fl_inv(piv, p);993if (detp) det = Fl_mul_pre(det, piv, p, pi);994break;995}996}997/* found a pivot on line k */998if (k > li) return NULL;999if (k != i)1000{ /* swap lines so that k = i */1001s = -s;1002for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));1003for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));1004}1005if (i == aco) break;10061007invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */1008for (k=i+1; k<=li; k++)1009{1010ulong m = ucoeff(a,k,i);1011if (!m) continue;10121013m = Fl_mul_pre(m, invpiv, p, pi);1014if (m == 1) {1015for (j=i+1; j<=aco; j++) _Fl_add(gel(a,j),k,i, p);1016for (j=1; j<=bco; j++) _Fl_add(gel(b,j),k,i, p);1017} else {1018for (j=i+1; j<=aco; j++) _Fl_addmul(gel(a,j),k,i,m, p, pi);1019for (j=1; j<=bco; j++) _Fl_addmul(gel(b,j),k,i,m, p, pi);1020}1021}1022}1023if (detp)1024{1025if (s < 0 && det) det = p - det;1026*detp = det;1027}1028u = cgetg(bco+1,t_MAT);1029for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col(a,gel(b,j), aco,p);1030return u;1031}10321033static GEN1034Flm_gauss_from_CUP(GEN b, GEN R, GEN C, GEN U, GEN P, ulong p, ulong pi, ulong *detp)1035{1036GEN Y = Flm_rsolve_lower_unit_pre(rowpermute(C, R), rowpermute(b, R), p, pi);1037GEN X = rowpermute(Flm_rsolve_upper_pre(U, Y, p, pi), perm_inv(P));1038if (detp)1039{1040ulong d = perm_sign(P) == 1? 1: p-1;1041long i, r = lg(R);1042for (i = 1; i < r; i++)1043d = Fl_mul_pre(d, ucoeff(U, i, i), p, pi);1044*detp = d;1045}1046return X;1047}10481049static GEN1050Flm_gauss_CUP(GEN a, GEN b, ulong *detp, ulong p) {1051GEN R, C, U, P;1052long n = lg(a) - 1, r;1053ulong pi = get_Fl_red(p);1054if (nbrows(a) < n || (r = Flm_CUP_pre(a, &R, &C, &U, &P, p, pi)) < n)1055return NULL;1056return Flm_gauss_from_CUP(b, R, C, U, P, p, pi, detp);1057}10581059GEN1060Flm_gauss_sp(GEN a, GEN b, ulong *detp, ulong p) {1061pari_sp av = avma;1062GEN x;1063if (lg(a) - 1 >= Flm_CUP_LIMIT)1064x = Flm_gauss_CUP(a, b, detp, p);1065else1066x = Flm_gauss_sp_i(a, b, detp, p);1067if (!x) return gc_NULL(av);1068return gerepileupto(av, x);1069}10701071GEN1072Flm_gauss(GEN a, GEN b, ulong p) {1073pari_sp av = avma;1074GEN x;1075if (lg(a) - 1 >= Flm_CUP_LIMIT)1076x = Flm_gauss_CUP(a, b, NULL, p);1077else {1078a = RgM_shallowcopy(a);1079b = RgM_shallowcopy(b);1080x = Flm_gauss_sp(a, b, NULL, p);1081}1082if (!x) return gc_NULL(av);1083return gerepileupto(av, x);1084}10851086static GEN1087Flm_inv_i(GEN a, ulong *detp, ulong p, long inplace) {1088pari_sp av = avma;1089long n = lg(a) - 1;1090GEN b, x;1091if (n == 0) return cgetg(1, t_MAT);1092b = matid_Flm(nbrows(a));1093if (n >= Flm_CUP_LIMIT)1094x = Flm_gauss_CUP(a, b, detp, p);1095else {1096if (!inplace)1097a = RgM_shallowcopy(a);1098x = Flm_gauss_sp(a, b, detp, p);1099}1100if (!x) return gc_NULL(av);1101return gerepileupto(av, x);1102}11031104GEN1105Flm_inv_sp(GEN a, ulong *detp, ulong p) {1106return Flm_inv_i(a, detp, p, 1);1107}11081109GEN1110Flm_inv(GEN a, ulong p) {1111return Flm_inv_i(a, NULL, p, 0);1112}11131114GEN1115Flm_Flc_gauss(GEN a, GEN b, ulong p) {1116pari_sp av = avma;1117GEN z = Flm_gauss(a, mkmat(b), p);1118if (!z) return gc_NULL(av);1119if (lg(z) == 1) { set_avma(av); return cgetg(1,t_VECSMALL); }1120return gerepileuptoleaf(av, gel(z,1));1121}11221123GEN1124Flm_adjoint(GEN A, ulong p)1125{1126pari_sp av = avma;1127GEN R, C, U, P, C1, U1, v, c, d;1128long r, i, q, n = lg(A)-1, m;1129ulong D;1130ulong pi = get_Fl_red(p);1131if (n == 0) return cgetg(1, t_MAT);1132r = Flm_CUP_pre(A, &R, &C, &U, &P, p, pi);1133m = nbrows(A);1134if (r == n)1135{1136GEN X = Flm_gauss_from_CUP(matid_Flm(m), R, C, U, P, p, pi, &D);1137return gerepileupto(av, Flm_Fl_mul_pre(X, D, p, pi));1138}1139if (r < n-1) return zero_Flm(n, m);1140for (q = n, i = 1; i < n; i++)1141if (R[i] != i) { q = i; break; }1142C1 = matslice(C, 1, q-1, 1, q-1);1143c = vecslice(Flm_row(C, q), 1, q-1);1144c = Flm_lsolve_lower_unit_pre(C1, Flm_transpose(mkmat(c)), p, pi);1145d = cgetg(m+1, t_VECSMALL);1146for (i=1; i<q; i++) uel(d,i) = ucoeff(c,1,i);1147uel(d,q) = p-1;1148for (i=q+1; i<=m; i++) uel(d,i) = 0;1149U1 = vecslice(U, 1, n-1);1150v = gel(Flm_rsolve_upper_pre(U1, mkmat(gel(U,n)), p, pi),1);1151v = vecsmall_append(v, p-1);1152D = perm_sign(P) != (odd(q+n)?-1:1) ? p-1 : 1;1153for (i = 1; i <= n-1; i++)1154D = Fl_mul_pre(D, ucoeff(U1, i, i), p, pi);1155d = Flv_Fl_mul(d, D, p);1156return rowpermute(Flc_Flv_mul(v, d, p),perm_inv(P));1157}11581159static GEN1160Flm_invimage_CUP(GEN A, GEN B, ulong p) {1161pari_sp av = avma;1162GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;1163long r;1164ulong pi = get_Fl_red(p);1165r = Flm_CUP_pre(A, &R, &C, &U, &P, p, pi);1166Rc = indexcompl(R, nbrows(B));1167C1 = rowpermute(C, R);1168C2 = rowpermute(C, Rc);1169B1 = rowpermute(B, R);1170B2 = rowpermute(B, Rc);1171Z = Flm_rsolve_lower_unit_pre(C1, B1, p, pi);1172if (!gequal(Flm_mul_pre(C2, Z, p, pi), B2))1173return NULL;1174Y = vconcat(Flm_rsolve_upper_pre(vecslice(U, 1, r), Z, p, pi),1175zero_Flm(lg(A) - 1 - r, lg(B) - 1));1176X = rowpermute(Y, perm_inv(P));1177return gerepileupto(av, X);1178}11791180GEN1181Flm_invimage_i(GEN A, GEN B, ulong p)1182{1183GEN d, x, X, Y;1184long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;11851186if (!nB) return cgetg(1, t_MAT);1187if (maxss(nA, nB) >= Flm_CUP_LIMIT && nbrows(B) >= Flm_CUP_LIMIT)1188return Flm_invimage_CUP(A, B, p);11891190x = Flm_ker(shallowconcat(Flm_neg(A,p), B), p);1191/* AX = BY, Y in strict upper echelon form with pivots = 1.1192* We must find T such that Y T = Id_nB then X T = Z. This exists iff1193* Y has at least nB columns and full rank */1194nY = lg(x)-1;1195if (nY < nB) return NULL;1196Y = rowslice(x, nA+1, nA+nB); /* nB rows */1197d = cgetg(nB+1, t_VECSMALL);1198for (i = nB, j = nY; i >= 1; i--, j--)1199{1200for (; j>=1; j--)1201if (coeff(Y,i,j)) { d[i] = j; break; }1202if (!j) return NULL;1203}1204/* reduce to the case Y square, upper triangular with 1s on diagonal */1205Y = vecpermute(Y, d);1206x = vecpermute(x, d);1207X = rowslice(x, 1, nA);1208return Flm_mul(X, Flm_inv_upper_1(Y,p), p);1209}1210GEN1211Flm_invimage(GEN A, GEN B, ulong p)1212{1213pari_sp av = avma;1214GEN X = Flm_invimage_i(A,B,p);1215if (!X) return gc_NULL(av);1216return gerepileupto(av, X);1217}12181219GEN1220Flm_Flc_invimage(GEN A, GEN y, ulong p)1221{1222pari_sp av = avma;1223long i, l = lg(A);1224GEN M, x;1225ulong t;12261227if (l==1) return NULL;1228if (lg(y) != lgcols(A)) pari_err_DIM("Flm_Flc_invimage");1229M = cgetg(l+1,t_MAT);1230for (i=1; i<l; i++) gel(M,i) = gel(A,i);1231gel(M,l) = y; M = Flm_ker(M,p);1232i = lg(M)-1; if (!i) return gc_NULL(av);12331234x = gel(M,i); t = x[l];1235if (!t) return gc_NULL(av);12361237setlg(x,l); t = Fl_inv(Fl_neg(t,p),p);1238if (t!=1) x = Flv_Fl_mul(x, t, p);1239return gerepileuptoleaf(av, x);1240}124112421243