Code coverage tests

This page documents the degree to which the PARI/GP source code is tested by our public test suite, distributed with the source distribution in directory src/test/. This is measured by the gcov utility; we then process gcov output using the lcov frond-end.

We test a few variants depending on Configure flags on the pari.math.u-bordeaux.fr machine (x86_64 architecture), and agregate them in the final report:

The target is 90% coverage for all mathematical modules (given that branches depending on DEBUGLEVEL or DEBUGMEM are not covered). This script is run to produce the results below.

LCOV - code coverage report
Current view: top level - basemath - alglin1.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.10.0 lcov report (development 20422-b487f4d) Lines: 3055 3359 90.9 %
Date: 2017-03-22 05:51:54 Functions: 278 301 92.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2000, 2012  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. It is distributed in the hope that it will be useful, but WITHOUT
       8             : ANY WARRANTY WHATSOEVER.
       9             : 
      10             : Check the License for details. You should have received a copy of it, along
      11             : with the package; see the file 'COPYING'. If not, write to the Free Software
      12             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      13             : 
      14             : /********************************************************************/
      15             : /**                                                                **/
      16             : /**                         LINEAR ALGEBRA                         **/
      17             : /**                          (first part)                          **/
      18             : /**                                                                **/
      19             : /********************************************************************/
      20             : #include "pari.h"
      21             : #include "paripriv.h"
      22             : 
      23             : /*******************************************************************/
      24             : /*                                                                 */
      25             : /*                         GEREPILE                                */
      26             : /*                                                                 */
      27             : /*******************************************************************/
      28             : 
      29             : static void
      30          16 : gerepile_mat(pari_sp av, pari_sp tetpil, GEN x, long k, long m, long n, long t)
      31             : {
      32          16 :   pari_sp A, bot = pari_mainstack->bot;
      33             :   long u, i;
      34             :   size_t dec;
      35             : 
      36          16 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      37             : 
      38         487 :   for (u=t+1; u<=m; u++)
      39             :   {
      40         471 :     A = (pari_sp)coeff(x,u,k);
      41         471 :     if (A < av && A >= bot) coeff(x,u,k) += dec;
      42             :   }
      43        1104 :   for (i=k+1; i<=n; i++)
      44       77248 :     for (u=1; u<=m; u++)
      45             :     {
      46       76160 :       A = (pari_sp)coeff(x,u,i);
      47       76160 :       if (A < av && A >= bot) coeff(x,u,i) += dec;
      48             :     }
      49          16 : }
      50             : 
      51             : static void
      52          16 : gen_gerepile_gauss_ker(GEN x, long k, long t, pari_sp av, void *E, GEN (*copy)(void*, GEN))
      53             : {
      54          16 :   pari_sp tetpil = avma;
      55          16 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      56             : 
      57          16 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot_ker. k=%ld, n=%ld",k,n);
      58          16 :   for (u=t+1; u<=m; u++) gcoeff(x,u,k) = copy(E,gcoeff(x,u,k));
      59        1104 :   for (i=k+1; i<=n; i++)
      60        1088 :     for (u=1; u<=m; u++) gcoeff(x,u,i) = copy(E,gcoeff(x,u,i));
      61          16 :   gerepile_mat(av,tetpil,x,k,m,n,t);
      62          16 : }
      63             : 
      64             : /* special gerepile for huge matrices */
      65             : 
      66             : #define COPY(x) {\
      67             :   GEN _t = (x); if (!is_universal_constant(_t)) x = gcopy(_t); \
      68             : }
      69             : 
      70             : INLINE GEN
      71       28662 : _copy(void *E, GEN x)
      72             : {
      73       28662 :   (void) E; COPY(x);
      74       28662 :   return x;
      75             : }
      76             : 
      77             : static void
      78           6 : gerepile_gauss_ker(GEN x, long k, long t, pari_sp av)
      79             : {
      80           6 :   gen_gerepile_gauss_ker(x, k, t, av, NULL, &_copy);
      81           6 : }
      82             : 
      83             : static void
      84          49 : gerepile_gauss(GEN x,long k,long t,pari_sp av, long j, GEN c)
      85             : {
      86          49 :   pari_sp tetpil = avma, A, bot;
      87          49 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      88             :   size_t dec;
      89             : 
      90          49 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot. k=%ld, n=%ld",k,n);
      91        1346 :   for (u=t+1; u<=m; u++)
      92        1297 :     if (u==j || !c[u]) COPY(gcoeff(x,u,k));
      93        2725 :   for (u=1; u<=m; u++)
      94        2676 :     if (u==j || !c[u])
      95        2041 :       for (i=k+1; i<=n; i++) COPY(gcoeff(x,u,i));
      96             : 
      97          49 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      98          49 :   bot = pari_mainstack->bot;
      99        1346 :   for (u=t+1; u<=m; u++)
     100        1297 :     if (u==j || !c[u])
     101             :     {
     102        1297 :       A=(pari_sp)coeff(x,u,k);
     103        1297 :       if (A<av && A>=bot) coeff(x,u,k)+=dec;
     104             :     }
     105        2725 :   for (u=1; u<=m; u++)
     106        2676 :     if (u==j || !c[u])
     107       98150 :       for (i=k+1; i<=n; i++)
     108             :       {
     109       96109 :         A=(pari_sp)coeff(x,u,i);
     110       96109 :         if (A<av && A>=bot) coeff(x,u,i)+=dec;
     111             :       }
     112          49 : }
     113             : 
     114             : /*******************************************************************/
     115             : /*                                                                 */
     116             : /*                         GENERIC                                 */
     117             : /*                                                                 */
     118             : /*******************************************************************/
     119             : GEN
     120        1885 : gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff)
     121             : {
     122        1885 :   pari_sp av0 = avma, av, tetpil;
     123             :   GEN y, c, d;
     124             :   long i, j, k, r, t, n, m;
     125             : 
     126        1885 :   n=lg(x)-1; if (!n) return cgetg(1,t_MAT);
     127        1885 :   m=nbrows(x); r=0;
     128        1885 :   x = RgM_shallowcopy(x);
     129        1885 :   c = zero_zv(m);
     130        1885 :   d=new_chunk(n+1);
     131        1885 :   av=avma;
     132       18218 :   for (k=1; k<=n; k++)
     133             :   {
     134      279737 :     for (j=1; j<=m; j++)
     135      273855 :       if (!c[j])
     136             :       {
     137       84065 :         gcoeff(x,j,k) = ff->red(E, gcoeff(x,j,k));
     138       84065 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     139             :       }
     140       16361 :     if (j>m)
     141             :     {
     142        5882 :       if (deplin)
     143             :       {
     144          28 :         GEN c = cgetg(n+1, t_COL), g0 = ff->s(E,0), g1=ff->s(E,1);
     145          28 :         for (i=1; i<k; i++) gel(c,i) = ff->red(E, gcoeff(x,d[i],k));
     146          28 :         gel(c,k) = g1; for (i=k+1; i<=n; i++) gel(c,i) = g0;
     147          28 :         return gerepileupto(av0, c);
     148             :       }
     149        5854 :       r++; d[k]=0;
     150       47761 :       for(j=1; j<k; j++)
     151       41907 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
     152             :     }
     153             :     else
     154             :     {
     155       10479 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     156       10479 :       c[j] = k; d[k] = j;
     157       10479 :       gcoeff(x,j,k) = ff->s(E,-1);
     158       10479 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     159      404953 :       for (t=1; t<=m; t++)
     160             :       {
     161      394474 :         if (t==j) continue;
     162             : 
     163      383995 :         piv = ff->red(E,gcoeff(x,t,k));
     164      383995 :         if (ff->equal0(piv)) continue;
     165             : 
     166       58412 :         gcoeff(x,t,k) = ff->s(E,0);
     167      750837 :         for (i=k+1; i<=n; i++)
     168      692425 :            gcoeff(x,t,i) = ff->add(E, gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     169       58412 :         if (gc_needed(av,1))
     170          10 :           gen_gerepile_gauss_ker(x,k,t,av,E,ff->red);
     171             :       }
     172             :     }
     173             :   }
     174        1857 :   if (deplin) { avma = av0; return NULL; }
     175             : 
     176        1829 :   tetpil=avma; y=cgetg(r+1,t_MAT);
     177        7683 :   for (j=k=1; j<=r; j++,k++)
     178             :   {
     179        5854 :     GEN C = cgetg(n+1,t_COL);
     180        5854 :     GEN g0 = ff->s(E,0), g1 = ff->s(E,1);
     181        5854 :     gel(y,j) = C; while (d[k]) k++;
     182       47761 :     for (i=1; i<k; i++)
     183       41907 :       if (d[i])
     184             :       {
     185       16167 :         GEN p1=gcoeff(x,d[i],k);
     186       16167 :         gel(C,i) = ff->red(E,p1); gunclone(p1);
     187             :       }
     188             :       else
     189       25740 :         gel(C,i) = g0;
     190        5854 :     gel(C,k) = g1; for (i=k+1; i<=n; i++) gel(C,i) = g0;
     191             :   }
     192        1829 :   return gerepile(av0,tetpil,y);
     193             : }
     194             : 
     195             : GEN
     196         305 : gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff)
     197             : {
     198             :   pari_sp av;
     199             :   GEN c, d;
     200         305 :   long i, j, k, r, t, m, n = lg(x)-1;
     201             : 
     202         305 :   if (!n) { *rr = 0; return NULL; }
     203             : 
     204         305 :   m=nbrows(x); r=0;
     205         305 :   d = cgetg(n+1, t_VECSMALL);
     206         305 :   x = RgM_shallowcopy(x);
     207         305 :   c = zero_zv(m);
     208         305 :   av=avma;
     209        3023 :   for (k=1; k<=n; k++)
     210             :   {
     211       61825 :     for (j=1; j<=m; j++)
     212       61095 :       if (!c[j])
     213             :       {
     214       36495 :         gcoeff(x,j,k) = ff->red(E,gcoeff(x,j,k));
     215       36495 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     216             :       }
     217        2718 :     if (j>m) { r++; d[k]=0; }
     218             :     else
     219             :     {
     220        1988 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     221        1988 :       GEN g0 = ff->s(E,0);
     222        1988 :       c[j] = k; d[k] = j;
     223        1988 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     224       51596 :       for (t=1; t<=m; t++)
     225             :       {
     226       49608 :         if (c[t]) continue; /* already a pivot on that line */
     227             : 
     228       26536 :         piv = ff->red(E,gcoeff(x,t,k));
     229       26536 :         if (ff->equal0(piv)) continue;
     230       25423 :         gcoeff(x,t,k) = g0;
     231      715659 :         for (i=k+1; i<=n; i++)
     232      690236 :           gcoeff(x,t,i) = ff->add(E,gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     233       25423 :         if (gc_needed(av,1))
     234          49 :           gerepile_gauss(x,k,t,av,j,c);
     235             :       }
     236        1988 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = g0; /* dummy */
     237             :     }
     238             :   }
     239         305 :   *rr = r; avma = (pari_sp)d; return d;
     240             : }
     241             : 
     242             : GEN
     243          84 : gen_det(GEN a, void *E, const struct bb_field *ff)
     244             : {
     245          84 :   pari_sp av = avma;
     246          84 :   long i,j,k, s = 1, nbco = lg(a)-1;
     247          84 :   GEN q, x = ff->s(E,1);
     248          84 :   if (!nbco) return x;
     249          77 :   a = RgM_shallowcopy(a);
     250         399 :   for (i=1; i<nbco; i++)
     251             :   {
     252         329 :     for(k=i; k<=nbco; k++)
     253             :     {
     254         329 :       gcoeff(a,k,i) = ff->red(E,gcoeff(a,k,i));
     255         329 :       if (!ff->equal0(gcoeff(a,k,i))) break;
     256             :     }
     257         322 :     if (k > nbco) return gerepileupto(av, gcoeff(a,i,i));
     258         322 :     if (k != i)
     259             :     { /* exchange the lines s.t. k = i */
     260           7 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     261           7 :       s = -s;
     262             :     }
     263         322 :     q = gcoeff(a,i,i);
     264             : 
     265         322 :     x = ff->red(E,ff->mul(E,x,q));
     266         322 :     q = ff->inv(E,q);
     267        1414 :     for (k=i+1; k<=nbco; k++)
     268             :     {
     269        1092 :       GEN m = ff->red(E,gcoeff(a,i,k));
     270        1092 :       if (ff->equal0(m)) continue;
     271             : 
     272         966 :       m = ff->neg(E, ff->mul(E,m, q));
     273        5278 :       for (j=i+1; j<=nbco; j++)
     274             :       {
     275        4312 :         gcoeff(a,j,k) = ff->add(E, gcoeff(a,j,k), ff->mul(E,m,gcoeff(a,j,i)));
     276        4312 :         if (gc_needed(av,1))
     277             :         {
     278           0 :           if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
     279           0 :           gerepileall(av,4, &a,&x,&q,&m);
     280             :         }
     281             :       }
     282             :     }
     283             :   }
     284          77 :   if (s < 0) x = ff->neg(E,x);
     285          77 :   return gerepileupto(av, ff->red(E,ff->mul(E, x, gcoeff(a,nbco,nbco))));
     286             : }
     287             : 
     288             : INLINE void
     289      140565 : _gen_addmul(GEN b, long k, long i, GEN m, void *E, const struct bb_field *ff)
     290             : {
     291      140565 :   gel(b,i) = ff->red(E,gel(b,i));
     292      140565 :   gel(b,k) = ff->add(E,gel(b,k), ff->mul(E,m, gel(b,i)));
     293      140565 : }
     294             : 
     295             : static GEN
     296       13492 : _gen_get_col(GEN a, GEN b, long li, void *E, const struct bb_field *ff)
     297             : {
     298       13492 :   GEN u = cgetg(li+1,t_COL);
     299       13492 :   pari_sp av = avma;
     300             :   long i, j;
     301             : 
     302       13492 :   gel(u,li) = gerepileupto(av, ff->red(E,ff->mul(E,gel(b,li), gcoeff(a,li,li))));
     303       60221 :   for (i=li-1; i>0; i--)
     304             :   {
     305       46729 :     pari_sp av = avma;
     306       46729 :     GEN m = gel(b,i);
     307       46729 :     for (j=i+1; j<=li; j++) m = ff->add(E,m, ff->neg(E,ff->mul(E,gcoeff(a,i,j), gel(u,j))));
     308       46729 :     m = ff->red(E, m);
     309       46729 :     gel(u,i) = gerepileupto(av, ff->red(E,ff->mul(E,m, gcoeff(a,i,i))));
     310             :   }
     311       13492 :   return u;
     312             : }
     313             : 
     314             : GEN
     315        4235 : gen_Gauss(GEN a, GEN b, void *E, const struct bb_field *ff)
     316             : {
     317             :   long i, j, k, li, bco, aco;
     318        4235 :   GEN u, g0 = ff->s(E,0);
     319        4235 :   pari_sp av = avma;
     320        4235 :   a = RgM_shallowcopy(a);
     321        4235 :   b = RgM_shallowcopy(b);
     322        4235 :   aco = lg(a)-1; bco = lg(b)-1; li = nbrows(a);
     323       13611 :   for (i=1; i<=aco; i++)
     324             :   {
     325             :     GEN invpiv;
     326       14534 :     for (k = i; k <= li; k++)
     327             :     {
     328       14478 :       GEN piv = ff->red(E,gcoeff(a,k,i));
     329       14478 :       if (!ff->equal0(piv)) { gcoeff(a,k,i) = ff->inv(E,piv); break; }
     330         923 :       gcoeff(a,k,i) = g0;
     331             :     }
     332             :     /* found a pivot on line k */
     333       13611 :     if (k > li) return NULL;
     334       13555 :     if (k != i)
     335             :     { /* swap lines so that k = i */
     336         740 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     337         740 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
     338             :     }
     339       13555 :     if (i == aco) break;
     340             : 
     341        9376 :     invpiv = gcoeff(a,i,i); /* 1/piv mod p */
     342       32849 :     for (k=i+1; k<=li; k++)
     343             :     {
     344       23473 :       GEN m = ff->red(E,gcoeff(a,k,i)); gcoeff(a,k,i) = g0;
     345       23473 :       if (ff->equal0(m)) continue;
     346             : 
     347       11985 :       m = ff->red(E,ff->neg(E,ff->mul(E,m, invpiv)));
     348       11985 :       for (j=i+1; j<=aco; j++) _gen_addmul(gel(a,j),k,i,m,E,ff);
     349       11985 :       for (j=1  ; j<=bco; j++) _gen_addmul(gel(b,j),k,i,m,E,ff);
     350             :     }
     351        9376 :     if (gc_needed(av,1))
     352             :     {
     353           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"gen_Gauss. i=%ld",i);
     354           0 :       gerepileall(av,2, &a,&b);
     355             :     }
     356             :   }
     357             : 
     358        4179 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
     359        4179 :   u = cgetg(bco+1,t_MAT);
     360        4179 :   for (j=1; j<=bco; j++) gel(u,j) = _gen_get_col(a, gel(b,j), aco, E, ff);
     361        4179 :   return u;
     362             : }
     363             : 
     364             : /* compatible t_MAT * t_COL, lgA = lg(A) = lg(B) > 1, l = lgcols(A) */
     365             : static GEN
     366       43773 : gen_matcolmul_i(GEN A, GEN B, ulong lgA, ulong l,
     367             :                 void *E, const struct bb_field *ff)
     368             : {
     369       43773 :   GEN C = cgetg(l, t_COL);
     370             :   ulong i;
     371      314008 :   for (i = 1; i < l; i++) {
     372      270235 :     pari_sp av = avma;
     373      270235 :     GEN e = ff->mul(E, gcoeff(A, i, 1), gel(B, 1));
     374             :     ulong k;
     375     1687573 :     for(k = 2; k < lgA; k++)
     376     1417338 :       e = ff->add(E, e, ff->mul(E, gcoeff(A, i, k), gel(B, k)));
     377      270235 :     gel(C, i) = gerepileupto(av, ff->red(E, e));
     378             :   }
     379       43773 :   return C;
     380             : }
     381             : 
     382             : GEN
     383       21000 : gen_matcolmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     384             : {
     385       21000 :   ulong lgA = lg(A);
     386       21000 :   if (lgA != (ulong)lg(B))
     387           0 :     pari_err_OP("operation 'gen_matcolmul'", A, B);
     388       21000 :   if (lgA == 1)
     389           0 :     return cgetg(1, t_COL);
     390       21000 :   return gen_matcolmul_i(A, B, lgA, lgcols(A), E, ff);
     391             : }
     392             : 
     393             : GEN
     394        2229 : gen_matmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     395             : {
     396        2229 :   ulong j, l, lgA, lgB = lg(B);
     397             :   GEN C;
     398        2229 :   if (lgB == 1)
     399           0 :     return cgetg(1, t_MAT);
     400        2229 :   lgA = lg(A);
     401        2229 :   if (lgA != (ulong)lgcols(B))
     402           0 :     pari_err_OP("operation 'gen_matmul'", A, B);
     403        2229 :   if (lgA == 1)
     404           0 :     return zeromat(0, lgB - 1);
     405        2229 :   l = lgcols(A);
     406        2229 :   C = cgetg(lgB, t_MAT);
     407       25002 :   for(j = 1; j < lgB; j++)
     408       22773 :     gel(C, j) = gen_matcolmul_i(A, gel(B, j), lgA, l, E, ff);
     409        2229 :   return C;
     410             : }
     411             : 
     412             : static GEN
     413          63 : gen_colneg(GEN A, void *E, const struct bb_field *ff)
     414             : {
     415             :   long i, l;
     416          63 :   GEN B = cgetg_copy(A, &l);
     417         252 :   for (i = 1; i < l; i++)
     418         189 :     gel(B, i) = ff->neg(E, gel(A, i));
     419          63 :   return B;
     420             : }
     421             : 
     422             : static GEN
     423          21 : gen_matneg(GEN A, void *E, const struct bb_field *ff)
     424             : {
     425             :   long i, l;
     426          21 :   GEN B = cgetg_copy(A, &l);
     427          84 :   for (i = 1; i < l; i++)
     428          63 :     gel(B, i) = gen_colneg(gel(A, i), E, ff);
     429          21 :   return B;
     430             : }
     431             : 
     432             : /* assume dim A >= 1, A invertible + upper triangular  */
     433             : static GEN
     434          63 : gen_matinv_upper_ind(GEN A, long index, void *E, const struct bb_field *ff)
     435             : {
     436          63 :   long n = lg(A) - 1, i, j;
     437          63 :   GEN u = cgetg(n + 1, t_COL);
     438         126 :   for (i = n; i > index; i--)
     439          63 :     gel(u, i) = ff->s(E, 0);
     440          63 :   gel(u, i) = ff->inv(E, gcoeff(A, i, i));
     441         126 :   for (i--; i > 0; i--) {
     442          63 :     pari_sp av = avma;
     443          63 :     GEN m = ff->neg(E, ff->mul(E, gcoeff(A, i, i + 1), gel(u, i + 1)));
     444         105 :     for (j = i + 2; j <= n; j++)
     445          42 :       m = ff->add(E, m, ff->neg(E, ff->mul(E, gcoeff(A, i, j), gel(u, j))));
     446          63 :     gel(u, i) = gerepileupto(av, ff->red(E, ff->mul(E, m, ff->inv(E, gcoeff(A, i, i)))));
     447             :   }
     448          63 :   return u;
     449             : }
     450             : 
     451             : static GEN
     452          21 : gen_matinv_upper(GEN A, void *E, const struct bb_field *ff)
     453             : {
     454             :   long i, l;
     455          21 :   GEN B = cgetg_copy(A, &l);
     456          84 :   for (i = 1; i < l; i++)
     457          63 :     gel(B,i) = gen_matinv_upper_ind(A, i, E, ff);
     458          21 :   return B;
     459             : }
     460             : 
     461             : /* find z such that A z = y. Return NULL if no solution */
     462             : GEN
     463          63 : gen_matcolinvimage(GEN A, GEN y, void *E, const struct bb_field *ff)
     464             : {
     465          63 :   pari_sp av = avma;
     466          63 :   long i, l = lg(A);
     467             :   GEN M, x, t;
     468             : 
     469          63 :   M = gen_ker(shallowconcat(A, y), 0, E, ff);
     470          63 :   i = lg(M) - 1;
     471          63 :   if (!i) { avma = av; return NULL; }
     472             : 
     473          63 :   x = gel(M, i);
     474          63 :   t = gel(x, l);
     475          63 :   if (ff->equal0(t)) { avma = av; return NULL; }
     476             : 
     477          42 :   t = ff->neg(E, ff->inv(E, t));
     478          42 :   setlg(x, l);
     479         147 :   for (i = 1; i < l; i++)
     480         105 :     gel(x, i) = ff->red(E, ff->mul(E, t, gel(x, i)));
     481          42 :   return gerepilecopy(av, x);
     482             : }
     483             : 
     484             : /* find Z such that A Z = B. Return NULL if no solution */
     485             : GEN
     486          21 : gen_matinvimage(GEN A, GEN B, void *E, const struct bb_field *ff)
     487             : {
     488          21 :   pari_sp av = avma;
     489             :   GEN d, x, X, Y;
     490             :   long i, j, nY, nA, nB;
     491          21 :   x = gen_ker(shallowconcat(gen_matneg(A, E, ff), B), 0, E, ff);
     492             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
     493             :    * We must find T such that Y T = Id_nB then X T = Z. This exists
     494             :    * iff Y has at least nB columns and full rank. */
     495          21 :   nY = lg(x) - 1;
     496          21 :   nB = lg(B) - 1;
     497          21 :   if (nY < nB) { avma = av; return NULL; }
     498          21 :   nA = lg(A) - 1;
     499          21 :   Y = rowslice(x, nA + 1, nA + nB); /* nB rows */
     500          21 :   d = cgetg(nB + 1, t_VECSMALL);
     501          84 :   for (i = nB, j = nY; i >= 1; i--, j--) {
     502          63 :     for (; j >= 1; j--)
     503          63 :       if (!ff->equal0(gcoeff(Y, i, j))) { d[i] = j; break; }
     504          63 :     if (!j) { avma = av; return NULL; }
     505             :   }
     506             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
     507          21 :   Y = vecpermute(Y, d);
     508          21 :   x = vecpermute(x, d);
     509          21 :   X = rowslice(x, 1, nA);
     510          21 :   return gerepileupto(av, gen_matmul(X, gen_matinv_upper(Y, E, ff), E, ff));
     511             : }
     512             : 
     513             : static GEN
     514       87808 : image_from_pivot(GEN x, GEN d, long r)
     515             : {
     516             :   GEN y;
     517             :   long j, k;
     518             : 
     519       87808 :   if (!d) return gcopy(x);
     520             :   /* d left on stack for efficiency */
     521       86380 :   r = lg(x)-1 - r; /* = dim Im(x) */
     522       86380 :   y = cgetg(r+1,t_MAT);
     523      720511 :   for (j=k=1; j<=r; k++)
     524      634131 :     if (d[k]) gel(y,j++) = gcopy(gel(x,k));
     525       86380 :   return y;
     526             : }
     527             : 
     528             : /*******************************************************************/
     529             : /*                                                                 */
     530             : /*                Echelon form and CUP decomposition               */
     531             : /*                                                                 */
     532             : /*******************************************************************/
     533             : 
     534             : /* By Peter Bruin, based on
     535             :   C.-P. Jeannerod, C. Pernet and A. Storjohann, Rank-profile revealing
     536             :   Gaussian elimination and the CUP matrix decomposition.  J. Symbolic
     537             :   Comput. 56 (2013), 46-68.
     538             : 
     539             :   Decompose an m x n-matrix A of rank r as C*U*P, with
     540             :   - C: m x r-matrix in column echelon form (not necessarily reduced)
     541             :        with all pivots equal to 1
     542             :   - U: upper-triangular r x n-matrix
     543             :   - P: permutation matrix
     544             :   The pivots of C and the known zeroes in C and U are not necessarily
     545             :   filled in; instead, we also return the vector R of pivot rows.
     546             :   Instead of the matrix P, we return the permutation p of [1..n]
     547             :   (t_VECSMALL) such that P[i,j] = 1 if and only if j = p[i].
     548             : */
     549             : 
     550             : /* complement of a strictly increasing subsequence of (1, 2, ..., n) */
     551             : static GEN
     552      381922 : indexcompl(GEN v, long n) {
     553      381922 :   long i, j, k, m = lg(v) - 1;
     554      381922 :   GEN w = cgetg(n - m + 1, t_VECSMALL);
     555     7303794 :   for (i = j = k = 1; i <= n; i++) {
     556     6921872 :     if (j <= m && v[j] == i)
     557     2989342 :       j++;
     558             :     else
     559     3932530 :       w[k++] = i;
     560             :   }
     561      381922 :   return w;
     562             : }
     563             : 
     564             : static GEN
     565      162055 : Flm_rsolve_upper_1(GEN U, GEN B, ulong p) {
     566      162055 :   return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p);
     567             : }
     568             : 
     569             : static GEN
     570      504529 : Flm_rsolve_upper_2(GEN U, GEN B, ulong p) {
     571      504529 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     572      504529 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     573      504529 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     574      504529 :   GEN B1 = rowslice(B, 1, 1);
     575      504529 :   GEN B2 = rowslice(B, 2, 2);
     576      504529 :   GEN X2 = Flm_Fl_mul(B2, dinv, p);
     577      504529 :   GEN X1 = Flm_Fl_mul(Flm_sub(B1, Flm_Fl_mul(X2, b, p), p),
     578             :                       ainv, p);
     579      504529 :   return vconcat(X1, X2);
     580             : }
     581             : 
     582             : /* solve U*X = B,  U upper triangular and invertible */
     583             : static GEN
     584     1225998 : Flm_rsolve_upper(GEN U, GEN B, ulong p) {
     585     1225998 :   long n = lg(U) - 1, n1;
     586             :   GEN U1, U2, U11, U12, U22;
     587             :   GEN B1, B2, X1, X2, X;
     588     1225998 :   pari_sp av = avma;
     589             : 
     590     1225998 :   if (n == 0)
     591           0 :     return B;
     592     1225998 :   if (n == 1)
     593      162055 :     return Flm_rsolve_upper_1(U, B, p);
     594     1063943 :   if (n == 2)
     595      504529 :     return Flm_rsolve_upper_2(U, B, p);
     596      559414 :   n1 = (n + 1)/2;
     597      559414 :   U1 = vecslice(U, 1, n1);
     598      559414 :   U2 = vecslice(U, n1 + 1, n);
     599      559414 :   U11 = rowslice(U1, 1, n1);
     600      559414 :   U12 = rowslice(U2, 1, n1);
     601      559414 :   U22 = rowslice(U2, n1 + 1, n);
     602      559414 :   B1 = rowslice(B, 1, n1);
     603      559414 :   B2 = rowslice(B, n1 + 1, n);
     604      559414 :   X2 = Flm_rsolve_upper(U22, B2, p);
     605      559414 :   if (gc_needed(av, 1))
     606           0 :     gerepileall(av, 4, &B1, &U11, &U12, &X2);
     607      559414 :   B1 = Flm_sub(B1, Flm_mul(U12, X2, p), p);
     608      559414 :   if (gc_needed(av, 1))
     609           0 :     gerepileall(av, 3, &B1, &U11, &X2);
     610      559414 :   X1 = Flm_rsolve_upper(U11, B1, p);
     611      559414 :   X = vconcat(X1, X2);
     612      559414 :   if (gc_needed(av, 1))
     613          13 :     X = gerepilecopy(av, X);
     614      559414 :   return X;
     615             : }
     616             : 
     617             : static GEN
     618      245570 : Flm_lsolve_upper_1(GEN U, GEN B, ulong p) {
     619      245570 :   return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p);
     620             : }
     621             : 
     622             : static GEN
     623      672246 : Flm_lsolve_upper_2(GEN U, GEN B, ulong p) {
     624      672246 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     625      672246 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     626      672246 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     627      672246 :   GEN B1 = vecslice(B, 1, 1);
     628      672246 :   GEN B2 = vecslice(B, 2, 2);
     629      672246 :   GEN X1 = Flm_Fl_mul(B1, ainv, p);
     630      672246 :   GEN X2 = Flm_Fl_mul(Flm_sub(B2, Flm_Fl_mul(X1, b, p), p),
     631             :                       dinv, p);
     632      672246 :   return shallowconcat(X1, X2);
     633             : }
     634             : 
     635             : /* solve X*U = B,  U upper triangular and invertible */
     636             : static GEN
     637     1554608 : Flm_lsolve_upper(GEN U, GEN B, ulong p) {
     638     1554608 :   long n = lg(U) - 1, n1;
     639             :   GEN U1, U2, U11, U12, U22;
     640             :   GEN B1, B2, X1, X2, X;
     641     1554608 :   pari_sp av = avma;
     642             : 
     643     1554608 :   if (n == 0)
     644           0 :     return B;
     645     1554608 :   if (n == 1)
     646      245570 :     return Flm_lsolve_upper_1(U, B, p);
     647     1309038 :   if (n == 2)
     648      672246 :     return Flm_lsolve_upper_2(U, B, p);
     649      636792 :   n1 = (n + 1)/2;
     650      636792 :   U1 = vecslice(U, 1, n1);
     651      636792 :   U2 = vecslice(U, n1 + 1, n);
     652      636792 :   U11 = rowslice(U1, 1, n1);
     653      636792 :   U12 = rowslice(U2, 1, n1);
     654      636792 :   U22 = rowslice(U2, n1 + 1, n);
     655      636792 :   B1 = vecslice(B, 1, n1);
     656      636792 :   B2 = vecslice(B, n1 + 1, n);
     657      636792 :   X1 = Flm_lsolve_upper(U11, B1, p);
     658      636792 :   if (gc_needed(av, 1))
     659           0 :     gerepileall(av, 4, &B2, &U12, &U22, &X1);
     660      636792 :   B2 = Flm_sub(B2, Flm_mul(X1, U12, p), p);
     661      636792 :   if (gc_needed(av, 1))
     662           0 :     gerepileall(av, 3, &B2, &U22, &X1);
     663      636792 :   X2 = Flm_lsolve_upper(U22, B2, p);
     664      636792 :   X = shallowconcat(X1, X2);
     665      636792 :   if (gc_needed(av, 1))
     666           0 :     X = gerepilecopy(av, X);
     667      636792 :   return X;
     668             : }
     669             : 
     670             : static GEN
     671     1116937 : Flm_rsolve_lower_unit_2(GEN L, GEN A, ulong p) {
     672     1116937 :   GEN X1 = rowslice(A, 1, 1);
     673     1116937 :   GEN X2 = Flm_sub(rowslice(A, 2, 2),
     674     1116937 :                    Flm_Fl_mul(X1, ucoeff(L, 2, 1), p), p);
     675     1116937 :   return vconcat(X1, X2);
     676             : }
     677             : 
     678             : /* solve L*X = A,  L lower triangular with ones on the diagonal
     679             : * (at least as many rows as columns) */
     680             : static GEN
     681     2521878 : Flm_rsolve_lower_unit(GEN L, GEN A, ulong p) {
     682     2521878 :   long m = lg(L) - 1, m1, n;
     683             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
     684     2521878 :   pari_sp av = avma;
     685             : 
     686     2521878 :   if (m == 0) return zero_Flm(0, lg(A) - 1);
     687     2521878 :   if (m == 1) return rowslice(A, 1, 1);
     688     2204128 :   if (m == 2) return Flm_rsolve_lower_unit_2(L, A, p);
     689     1087191 :   m1 = (m + 1)/2;
     690     1087191 :   n = nbrows(L);
     691     1087191 :   L1 = vecslice(L, 1, m1);
     692     1087191 :   L2 = vecslice(L, m1 + 1, m);
     693     1087191 :   L11 = rowslice(L1, 1, m1);
     694     1087191 :   L21 = rowslice(L1, m1 + 1, n);
     695     1087191 :   L22 = rowslice(L2, m1 + 1, n);
     696     1087191 :   A1 = rowslice(A, 1, m1);
     697     1087191 :   A2 = rowslice(A, m1 + 1, n);
     698     1087191 :   X1 = Flm_rsolve_lower_unit(L11, A1, p);
     699     1087191 :   if (gc_needed(av, 1))
     700           0 :     gerepileall(av, 4, &A2, &L21, &L22, &X1);
     701     1087191 :   A2 = Flm_sub(A2, Flm_mul(L21, X1, p), p);
     702     1087191 :   if (gc_needed(av, 1))
     703           6 :     gerepileall(av, 3, &A2, &L22, &X1);
     704     1087191 :   X2 = Flm_rsolve_lower_unit(L22, A2, p);
     705     1087191 :   X = vconcat(X1, X2);
     706     1087191 :   if (gc_needed(av, 1))
     707          57 :     X = gerepilecopy(av, X);
     708     1087191 :   return X;
     709             : }
     710             : 
     711             : static GEN
     712      682097 : Flm_lsolve_lower_unit_2(GEN L, GEN A, ulong p)
     713             : {
     714      682097 :   GEN X2 = vecslice(A, 2, 2);
     715      682097 :   GEN X1 = Flm_sub(vecslice(A, 1, 1),
     716      682097 :                    Flm_Fl_mul(X2, ucoeff(L, 2, 1), p), p);
     717      682097 :   return shallowconcat(X1, X2);
     718             : }
     719             : 
     720             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
     721             : static GEN
     722     1699057 : Flm_lsolve_lower_unit(GEN L, GEN A, ulong p)
     723             : {
     724     1699057 :   long m = lg(L) - 1, m1;
     725             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
     726     1699057 :   pari_sp av = avma;
     727             : 
     728     1699057 :   if (m <= 1) return A;
     729     1462048 :   if (m == 2) return Flm_lsolve_lower_unit_2(L, A, p);
     730      779951 :   m1 = (m + 1)/2;
     731      779951 :   L1 = vecslice(L, 1, m1);
     732      779951 :   L2 = vecslice(L, m1 + 1, m);
     733      779951 :   L11 = rowslice(L1, 1, m1);
     734      779951 :   L21 = rowslice(L1, m1 + 1, m);
     735      779951 :   L22 = rowslice(L2, m1 + 1, m);
     736      779951 :   A1 = vecslice(A, 1, m1);
     737      779951 :   A2 = vecslice(A, m1 + 1, m);
     738      779951 :   X2 = Flm_lsolve_lower_unit(L22, A2, p);
     739      779951 :   A1 = Flm_sub(A1, Flm_mul(X2, L21, p), p);
     740      779951 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
     741      779951 :   X1 = Flm_lsolve_lower_unit(L11, A1, p);
     742      779951 :   X = shallowconcat(X1, X2);
     743      779951 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     744      779951 :   return X;
     745             : }
     746             : 
     747             : /* destroy A */
     748             : static long
     749      905315 : Flm_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     750             : {
     751      905315 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc, u, v;
     752             : 
     753      905315 :   if (P) *P = identity_perm(n);
     754      905315 :   *R = cgetg(m + 1, t_VECSMALL);
     755     4608064 :   for (j = 1, pr = 0; j <= n; j++)
     756             :   {
     757     6400139 :     for (pr++, pc = 0; pr <= m; pr++)
     758             :     {
     759     5948432 :       for (k = j; k <= n; k++) { v = ucoeff(A, pr, k); if (!pc && v) pc = k; }
     760     5948432 :       if (pc) break;
     761             :     }
     762     4154456 :     if (!pc) break;
     763     3702749 :     (*R)[j] = pr;
     764     3702749 :     if (pc != j)
     765             :     {
     766      907041 :       swap(gel(A, j), gel(A, pc));
     767      907041 :       if (P) lswap((*P)[j], (*P)[pc]);
     768             :     }
     769     3702749 :     u = Fl_inv(ucoeff(A, pr, j), p);
     770    25536690 :     for (i = pr + 1; i <= m; i++)
     771             :     {
     772    21833941 :       v = Fl_mul(ucoeff(A, i, j), u, p);
     773    21833941 :       ucoeff(A, i, j) = v;
     774   107550425 :       for (k = j + 1; k <= n; k++)
     775   171432968 :         ucoeff(A, i, k) = Fl_sub(ucoeff(A, i, k),
     776    85716484 :                                  Fl_mul(ucoeff(A, pr, k), v, p), p);
     777             :     }
     778             :   }
     779      905315 :   setlg(*R, j);
     780      905315 :   *C = vecslice(A, 1, j - 1);
     781      905315 :   if (U) *U = rowpermute(A, *R);
     782      905315 :   return j - 1;
     783             : }
     784             : 
     785             : static const long Flm_CUP_LIMIT = 8;
     786             : 
     787             : static ulong
     788      765238 : Flm_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     789             : {
     790      765238 :   long m = nbrows(A), m1, n = lg(A) - 1, i, r1, r2, r;
     791             :   GEN R1, C1, U1, P1, R2, C2, U2, P2;
     792             :   GEN A1, A2, B2, C21, U11, U12, T21, T22;
     793      765238 :   pari_sp av = avma;
     794             : 
     795      765238 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     796             :     /* destroy A; not called at the outermost recursion level */
     797      479503 :     return Flm_CUP_gauss(A, R, C, U, P, p);
     798      285735 :   m1 = (minss(m, n) + 1)/2;
     799      285735 :   A1 = rowslice(A, 1, m1);
     800      285735 :   A2 = rowslice(A, m1 + 1, m);
     801      285735 :   r1 = Flm_CUP(A1, &R1, &C1, &U1, &P1, p);
     802      285735 :   if (r1 == 0)
     803             :   {
     804        4711 :     r2 = Flm_CUP(A2, &R2, &C2, &U2, &P2, p);
     805        4711 :     *R = cgetg(r2 + 1, t_VECSMALL);
     806        4711 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
     807        4711 :     *C = vconcat(zero_Flm(m1, r2), C2);
     808        4711 :     *U = U2;
     809        4711 :     *P = P2;
     810        4711 :     r = r2;
     811             :   }
     812             :   else
     813             :   {
     814      281024 :     U11 = vecslice(U1, 1, r1);
     815      281024 :     U12 = vecslice(U1, r1 + 1, n);
     816      281024 :     T21 = vecslicepermute(A2, P1, 1, r1);
     817      281024 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
     818      281024 :     C21 = Flm_lsolve_upper(U11, T21, p);
     819      281024 :     if (gc_needed(av, 1))
     820           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
     821      281024 :     B2 = Flm_sub(T22, Flm_mul(C21, U12, p), p);
     822      281024 :     r2 = Flm_CUP(B2, &R2, &C2, &U2, &P2, p);
     823      281024 :     r = r1 + r2;
     824      281024 :     *R = cgetg(r + 1, t_VECSMALL);
     825      281024 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
     826      281024 :     for (;      i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
     827      281024 :     *C = shallowconcat(vconcat(C1, C21),
     828             :                        vconcat(zero_Flm(m1, r2), C2));
     829      281024 :     *U = shallowconcat(vconcat(U11, zero_Flm(r2, r1)),
     830             :                        vconcat(vecpermute(U12, P2), U2));
     831      281024 :     *P = cgetg(n + 1, t_VECSMALL);
     832      281024 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
     833      281024 :     for (; i <= n; i++)       (*P)[i] = P1[P2[i - r1] + r1];
     834             :   }
     835      285735 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
     836      285735 :   return r;
     837             : }
     838             : 
     839             : static ulong
     840      425812 : Flm_echelon_gauss(GEN A, GEN *R, GEN *C, ulong p)
     841      425812 : { return Flm_CUP_gauss(A, R, C, NULL, NULL, p); }
     842             : 
     843             : /* column echelon form */
     844             : static ulong
     845      714887 : Flm_echelon(GEN A, GEN *R, GEN *C, ulong p)
     846             : {
     847      714887 :   long j, j1, j2, m = nbrows(A), n = lg(A) - 1, n1, r, r1, r2;
     848             :   GEN A1, A2, R1, R1c, C1, R2, C2;
     849             :   GEN A12, A22, B2, C11, C21, M12;
     850      714887 :   pari_sp av = avma;
     851             : 
     852      714887 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     853      425812 :     return Flm_echelon_gauss(Flm_copy(A), R, C, p);
     854             : 
     855      289075 :   n1 = (n + 1)/2;
     856      289075 :   A1 = vecslice(A, 1, n1);
     857      289075 :   A2 = vecslice(A, n1 + 1, n);
     858      289075 :   r1 = Flm_echelon(A1, &R1, &C1, p);
     859      289075 :   if (!r1) return Flm_echelon(A2, R, C, p);
     860      242772 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
     861             : 
     862      240312 :   R1c = indexcompl(R1, m);
     863      240312 :   C11 = rowpermute(C1, R1);
     864      240312 :   C21 = rowpermute(C1, R1c);
     865      240312 :   A12 = rowpermute(A2, R1);
     866      240312 :   A22 = rowpermute(A2, R1c);
     867      240312 :   M12 = Flm_rsolve_lower_unit(C11, A12, p);
     868      240312 :   B2 = Flm_sub(A22, Flm_mul(C21, M12, p), p);
     869      240312 :   r2 = Flm_echelon(B2, &R2, &C2, p);
     870      240312 :   if (!r2) { *R = R1; *C = C1; r = r1; }
     871             :   else
     872             :   {
     873      227623 :     R2 = perm_mul(R1c, R2);
     874      227623 :     C2 = rowpermute(vconcat(zero_Flm(r1, r2), C2),
     875             :                     perm_inv(vecsmall_concat(R1, R1c)));
     876      227623 :     r = r1 + r2;
     877      227623 :     *R = cgetg(r + 1, t_VECSMALL);
     878      227623 :     *C = cgetg(r + 1, t_MAT);
     879     2704520 :     for (j = j1 = j2 = 1; j <= r; j++)
     880     2476897 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
     881             :       {
     882     1291444 :         gel(*C, j) = gel(C1, j1);
     883     1291444 :         (*R)[j] = R1[j1++];
     884             :       }
     885             :       else
     886             :       {
     887     1185453 :         gel(*C, j) = gel(C2, j2);
     888     1185453 :         (*R)[j] = R2[j2++];
     889             :       }
     890             :   }
     891      240312 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
     892      240312 :   return r;
     893             : }
     894             : 
     895             : static GEN
     896         140 : FlxqM_rsolve_upper_1(GEN U, GEN B, GEN T, ulong p)
     897         140 : { return FlxqM_Flxq_mul(B, Flxq_inv(gcoeff(U, 1, 1), T, p), T, p);
     898             : }
     899             : 
     900             : static GEN
     901         168 : FlxqM_rsolve_upper_2(GEN U, GEN B, GEN T, ulong p)
     902             : {
     903         168 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     904         168 :   GEN D = Flxq_mul(a, d, T, p), Dinv = Flxq_inv(D, T, p);
     905         168 :   GEN ainv = Flxq_mul(d, Dinv, T, p), dinv = Flxq_mul(a, Dinv, T, p);
     906         168 :   GEN B1 = rowslice(B, 1, 1);
     907         168 :   GEN B2 = rowslice(B, 2, 2);
     908         168 :   GEN X2 = FlxqM_Flxq_mul(B2, dinv, T, p);
     909         168 :   GEN X1 = FlxqM_Flxq_mul(FlxM_sub(B1, FlxqM_Flxq_mul(X2, b, T, p), p),
     910             :                           ainv, T, p);
     911         168 :   return vconcat(X1, X2);
     912             : }
     913             : 
     914             : /* solve U*X = B,  U upper triangular and invertible */
     915             : static GEN
     916         588 : FlxqM_rsolve_upper(GEN U, GEN B, GEN T, ulong p)
     917             : {
     918         588 :   long n = lg(U) - 1, n1;
     919             :   GEN U1, U2, U11, U12, U22;
     920             :   GEN B1, B2, X1, X2, X;
     921         588 :   pari_sp av = avma;
     922             : 
     923         588 :   if (n == 0) return B;
     924         588 :   if (n == 1) return FlxqM_rsolve_upper_1(U, B, T, p);
     925         448 :   if (n == 2) return FlxqM_rsolve_upper_2(U, B, T, p);
     926         280 :   n1 = (n + 1)/2;
     927         280 :   U1 = vecslice(U, 1, n1);
     928         280 :   U2 = vecslice(U, n1 + 1, n);
     929         280 :   U11 = rowslice(U1, 1, n1);
     930         280 :   U12 = rowslice(U2, 1, n1);
     931         280 :   U22 = rowslice(U2, n1 + 1, n);
     932         280 :   B1 = rowslice(B, 1, n1);
     933         280 :   B2 = rowslice(B, n1 + 1, n);
     934         280 :   X2 = FlxqM_rsolve_upper(U22, B2, T, p);
     935         280 :   B1 = FlxM_sub(B1, FlxqM_mul(U12, X2, T, p), p);
     936         280 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B1, &U11, &X2);
     937         280 :   X1 = FlxqM_rsolve_upper(U11, B1, T, p);
     938         280 :   X = vconcat(X1, X2);
     939         280 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     940         280 :   return X;
     941             : }
     942             : 
     943             : static GEN
     944         392 : FlxqM_lsolve_upper_1(GEN U, GEN B, GEN T, ulong p)
     945         392 : { return FlxqM_Flxq_mul(B, Flxq_inv(gcoeff(U, 1, 1), T, p), T, p); }
     946             : 
     947             : static GEN
     948         644 : FlxqM_lsolve_upper_2(GEN U, GEN B, GEN T, ulong p)
     949             : {
     950         644 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     951         644 :   GEN D = Flxq_mul(a, d, T, p), Dinv = Flxq_inv(D, T, p);
     952         644 :   GEN ainv = Flxq_mul(d, Dinv, T, p), dinv = Flxq_mul(a, Dinv, T, p);
     953         644 :   GEN B1 = vecslice(B, 1, 1);
     954         644 :   GEN B2 = vecslice(B, 2, 2);
     955         644 :   GEN X1 = FlxqM_Flxq_mul(B1, ainv, T, p);
     956         644 :   GEN X2 = FlxqM_Flxq_mul(FlxM_sub(B2, FlxqM_Flxq_mul(X1, b, T, p), p),
     957             :                           dinv, T, p);
     958         644 :   return shallowconcat(X1, X2);
     959             : }
     960             : 
     961             : /* solve X*U = B,  U upper triangular and invertible */
     962             : static GEN
     963        1596 : FlxqM_lsolve_upper(GEN U, GEN B, GEN T, ulong p)
     964             : {
     965        1596 :   long n = lg(U) - 1, n1;
     966             :   GEN U1, U2, U11, U12, U22;
     967             :   GEN B1, B2, X1, X2, X;
     968        1596 :   pari_sp av = avma;
     969             : 
     970        1596 :   if (n == 0) return B;
     971        1596 :   if (n == 1) return FlxqM_lsolve_upper_1(U, B, T, p);
     972        1204 :   if (n == 2) return FlxqM_lsolve_upper_2(U, B, T, p);
     973         560 :   n1 = (n + 1)/2;
     974         560 :   U1 = vecslice(U, 1, n1);
     975         560 :   U2 = vecslice(U, n1 + 1, n);
     976         560 :   U11 = rowslice(U1, 1, n1);
     977         560 :   U12 = rowslice(U2, 1, n1);
     978         560 :   U22 = rowslice(U2, n1 + 1, n);
     979         560 :   B1 = vecslice(B, 1, n1);
     980         560 :   B2 = vecslice(B, n1 + 1, n);
     981         560 :   X1 = FlxqM_lsolve_upper(U11, B1, T, p);
     982         560 :   B2 = FlxM_sub(B2, FlxqM_mul(X1, U12, T, p), p);
     983         560 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B2, &U22, &X1);
     984         560 :   X2 = FlxqM_lsolve_upper(U22, B2, T, p);
     985         560 :   X = shallowconcat(X1, X2);
     986         560 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     987         560 :   return X;
     988             : }
     989             : 
     990             : static GEN
     991        4348 : FlxqM_rsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
     992             : {
     993        4348 :   GEN X1 = rowslice(A, 1, 1);
     994        4348 :   GEN X2 = FlxM_sub(rowslice(A, 2, 2),
     995        4348 :                     FlxqM_Flxq_mul(X1, gcoeff(L, 2, 1), T, p), p);
     996        4348 :   return vconcat(X1, X2);
     997             : }
     998             : 
     999             : /* solve L*X = A,  L lower triangular with ones on the diagonal
    1000             :  * (at least as many rows as columns) */
    1001             : static GEN
    1002        9457 : FlxqM_rsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
    1003             : {
    1004        9457 :   long m = lg(L) - 1, m1, n;
    1005             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
    1006        9457 :   pari_sp av = avma;
    1007             : 
    1008        9457 :   if (m == 0) return zeromat(0, lg(A) - 1);
    1009        9457 :   if (m == 1) return rowslice(A, 1, 1);
    1010        8090 :   if (m == 2) return FlxqM_rsolve_lower_unit_2(L, A, T, p);
    1011        3742 :   m1 = (m + 1)/2;
    1012        3742 :   n = nbrows(L);
    1013        3742 :   L1 = vecslice(L, 1, m1);
    1014        3742 :   L2 = vecslice(L, m1 + 1, m);
    1015        3742 :   L11 = rowslice(L1, 1, m1);
    1016        3742 :   L21 = rowslice(L1, m1 + 1, n);
    1017        3742 :   L22 = rowslice(L2, m1 + 1, n);
    1018        3742 :   A1 = rowslice(A, 1, m1);
    1019        3742 :   A2 = rowslice(A, m1 + 1, n);
    1020        3742 :   X1 = FlxqM_rsolve_lower_unit(L11, A1, T, p);
    1021        3742 :   A2 = FlxM_sub(A2, FlxqM_mul(L21, X1, T, p), p);
    1022        3742 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A2, &L22, &X1);
    1023        3742 :   X2 = FlxqM_rsolve_lower_unit(L22, A2, T, p);
    1024        3742 :   X = vconcat(X1, X2);
    1025        3742 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1026        3742 :   return X;
    1027             : }
    1028             : 
    1029             : static GEN
    1030        1245 : FlxqM_lsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
    1031             : {
    1032        1245 :   GEN X2 = vecslice(A, 2, 2);
    1033        1245 :   GEN X1 = FlxM_sub(vecslice(A, 1, 1),
    1034        1245 :                     FlxqM_Flxq_mul(X2, gcoeff(L, 2, 1), T, p), p);
    1035        1245 :   return shallowconcat(X1, X2);
    1036             : }
    1037             : 
    1038             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
    1039             : static GEN
    1040        3280 : FlxqM_lsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
    1041             : {
    1042        3280 :   long m = lg(L) - 1, m1;
    1043             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
    1044        3280 :   pari_sp av = avma;
    1045             : 
    1046        3280 :   if (m <= 1) return A;
    1047        2644 :   if (m == 2) return FlxqM_lsolve_lower_unit_2(L, A, T, p);
    1048        1399 :   m1 = (m + 1)/2;
    1049        1399 :   L1 = vecslice(L, 1, m1);
    1050        1399 :   L2 = vecslice(L, m1 + 1, m);
    1051        1399 :   L11 = rowslice(L1, 1, m1);
    1052        1399 :   L21 = rowslice(L1, m1 + 1, m);
    1053        1399 :   L22 = rowslice(L2, m1 + 1, m);
    1054        1399 :   A1 = vecslice(A, 1, m1);
    1055        1399 :   A2 = vecslice(A, m1 + 1, m);
    1056        1399 :   X2 = FlxqM_lsolve_lower_unit(L22, A2, T, p);
    1057        1399 :   A1 = FlxM_sub(A1, FlxqM_mul(X2, L21, T, p), p);
    1058        1399 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
    1059        1399 :   X1 = FlxqM_lsolve_lower_unit(L11, A1, T, p);
    1060        1399 :   X = shallowconcat(X1, X2);
    1061        1399 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1062        1399 :   return X;
    1063             : }
    1064             : 
    1065             : /* destroy A */
    1066             : static long
    1067        4925 : FlxqM_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, GEN T, ulong p)
    1068             : {
    1069        4925 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc;
    1070             :   pari_sp av;
    1071             :   GEN u, v;
    1072             : 
    1073        4925 :   if (P) *P = identity_perm(n);
    1074        4925 :   *R = cgetg(m + 1, t_VECSMALL);
    1075        4925 :   av = avma;
    1076       11436 :   for (j = 1, pr = 0; j <= n; j++)
    1077             :   {
    1078      121405 :     for (pr++, pc = 0; pr <= m; pr++)
    1079             :     {
    1080      548383 :       for (k = j; k <= n; k++)
    1081             :       {
    1082      430866 :         v = Flx_rem(gcoeff(A, pr, k), T, p);
    1083      430866 :         gcoeff(A, pr, k) = v;
    1084      430866 :         if (!pc && lgpol(v) > 0) pc = k;
    1085             :       }
    1086      117517 :       if (pc) break;
    1087             :     }
    1088       10399 :     if (!pc) break;
    1089        6511 :     (*R)[j] = pr;
    1090        6511 :     if (pc != j)
    1091             :     {
    1092        1773 :       swap(gel(A, j), gel(A, pc));
    1093        1773 :       if (P) lswap((*P)[j], (*P)[pc]);
    1094             :     }
    1095        6511 :     u = Flxq_inv(gcoeff(A, pr, j), T, p);
    1096      107331 :     for (i = pr + 1; i <= m; i++)
    1097             :     {
    1098      100820 :       v = Flxq_mul(gcoeff(A, i, j), u, T, p);
    1099      100820 :       gcoeff(A, i, j) = v;
    1100      283535 :       for (k = j + 1; k <= n; k++)
    1101      365430 :         gcoeff(A, i, k) = Flx_sub(gcoeff(A, i, k),
    1102      182715 :                                   Flx_mul(gcoeff(A, pr, k), v, p), p);
    1103             :     }
    1104        6511 :     if (gc_needed(av, 2)) A = gerepilecopy(av, A);
    1105             :   }
    1106        4925 :   setlg(*R, j);
    1107        4925 :   *C = vecslice(A, 1, j - 1);
    1108        4925 :   if (U) *U = rowpermute(A, *R);
    1109        4925 :   return j - 1;
    1110             : }
    1111             : 
    1112             : static const long FlxqM_CUP_LIMIT = 5;
    1113             : 
    1114             : static ulong
    1115        1148 : FlxqM_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, GEN T, ulong p)
    1116             : {
    1117        1148 :   long m = nbrows(A), m1, n = lg(A) - 1, i, r1, r2, r, sv;
    1118             :   GEN R1, C1, U1, P1, R2, C2, U2, P2;
    1119             :   GEN A1, A2, B2, C21, U11, U12, T21, T22;
    1120        1148 :   pari_sp av = avma;
    1121             : 
    1122        1148 :   if (m < FlxqM_CUP_LIMIT || n < FlxqM_CUP_LIMIT)
    1123             :     /* destroy A; not called at the outermost recursion level */
    1124         616 :     return FlxqM_CUP_gauss(A, R, C, U, P, T, p);
    1125         532 :   sv = get_Flx_var(T);
    1126         532 :   m1 = (minss(m, n) + 1)/2;
    1127         532 :   A1 = rowslice(A, 1, m1);
    1128         532 :   A2 = rowslice(A, m1 + 1, m);
    1129         532 :   r1 = FlxqM_CUP(A1, &R1, &C1, &U1, &P1, T, p);
    1130         532 :   if (r1 == 0)
    1131             :   {
    1132          56 :     r2 = FlxqM_CUP(A2, &R2, &C2, &U2, &P2, T, p);
    1133          56 :     *R = cgetg(r2 + 1, t_VECSMALL);
    1134          56 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
    1135          56 :     *C = vconcat(zero_FlxM(m1, r2, sv), C2);
    1136          56 :     *U = U2;
    1137          56 :     *P = P2;
    1138          56 :     r = r2;
    1139             :   }
    1140             :   else
    1141             :   {
    1142         476 :     U11 = vecslice(U1, 1, r1);
    1143         476 :     U12 = vecslice(U1, r1 + 1, n);
    1144         476 :     T21 = vecslicepermute(A2, P1, 1, r1);
    1145         476 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
    1146         476 :     C21 = FlxqM_lsolve_upper(U11, T21, T, p);
    1147         476 :     if (gc_needed(av, 1))
    1148           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
    1149         476 :     B2 = FlxM_sub(T22, FlxqM_mul(C21, U12, T, p), p);
    1150         476 :     r2 = FlxqM_CUP(B2, &R2, &C2, &U2, &P2, T, p);
    1151         476 :     r = r1 + r2;
    1152         476 :     *R = cgetg(r + 1, t_VECSMALL);
    1153         476 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
    1154         476 :     for (     ; i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
    1155         476 :     *C = shallowmatconcat(mkmat2(mkcol2(C1, C21),
    1156             :                                  mkcol2(zero_FlxM(m1, r2, sv), C2)));
    1157         476 :     *U = shallowmatconcat(mkmat2(mkcol2(U11, zero_FlxM(r2, r1, sv)),
    1158             :                                  mkcol2(vecpermute(U12, P2), U2)));
    1159         476 :     *P = cgetg(n + 1, t_VECSMALL);
    1160         476 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
    1161         476 :     for (     ; i <= n; i++)  (*P)[i] = P1[P2[i - r1] + r1];
    1162             :   }
    1163         532 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
    1164         532 :   return r;
    1165             : }
    1166             : 
    1167             : static ulong
    1168        4309 : FlxqM_echelon_gauss(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1169        4309 : { return FlxqM_CUP_gauss(A, R, C, NULL, NULL, T, p); }
    1170             : 
    1171             : /* column echelon form */
    1172             : static ulong
    1173        8004 : FlxqM_echelon(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1174             : {
    1175        8004 :   long j, j1, j2, m = nbrows(A), n = lg(A) - 1, n1, r, r1, r2;
    1176             :   GEN A1, A2, R1, R1c, C1, R2, C2;
    1177             :   GEN A12, A22, B2, C11, C21, M12;
    1178        8004 :   pari_sp av = avma;
    1179             : 
    1180        8004 :   if (m < FlxqM_CUP_LIMIT || n < FlxqM_CUP_LIMIT)
    1181        4309 :     return FlxqM_echelon_gauss(shallowcopy(A), R, C, T, p);
    1182             : 
    1183        3695 :   n1 = (n + 1)/2;
    1184        3695 :   A1 = vecslice(A, 1, n1);
    1185        3695 :   A2 = vecslice(A, n1 + 1, n);
    1186        3695 :   r1 = FlxqM_echelon(A1, &R1, &C1, T, p);
    1187        3695 :   if (!r1) return FlxqM_echelon(A2, R, C, T, p);
    1188        1945 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
    1189        1931 :   R1c = indexcompl(R1, m);
    1190        1931 :   C11 = rowpermute(C1, R1);
    1191        1931 :   C21 = rowpermute(C1, R1c);
    1192        1931 :   A12 = rowpermute(A2, R1);
    1193        1931 :   A22 = rowpermute(A2, R1c);
    1194        1931 :   M12 = FlxqM_rsolve_lower_unit(C11, A12, T, p);
    1195        1931 :   B2 = FlxM_sub(A22, FlxqM_mul(C21, M12, T, p), p);
    1196        1931 :   r2 = FlxqM_echelon(B2, &R2, &C2, T, p);
    1197        1931 :   if (!r2) { *R = R1; *C = C1; r = r1; }
    1198             :   else
    1199             :   {
    1200        1548 :     R2 = perm_mul(R1c, R2);
    1201        1548 :     C2 = rowpermute(vconcat(zero_FlxM(r1, r2, get_Flx_var(T)), C2),
    1202             :                     perm_inv(vecsmall_concat(R1, R1c)));
    1203        1548 :     r = r1 + r2;
    1204        1548 :     *R = cgetg(r + 1, t_VECSMALL);
    1205        1548 :     *C = cgetg(r + 1, t_MAT);
    1206       15764 :     for (j = j1 = j2 = 1; j <= r; j++)
    1207             :     {
    1208       14216 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
    1209             :       {
    1210        7638 :         gel(*C, j) = gel(C1, j1);
    1211        7638 :         (*R)[j] = R1[j1++];
    1212             :       }
    1213             :       else
    1214             :       {
    1215        6578 :         gel(*C, j) = gel(C2, j2);
    1216        6578 :         (*R)[j] = R2[j2++];
    1217             :       }
    1218             :     }
    1219             :   }
    1220        1931 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
    1221        1931 :   return r;
    1222             : }
    1223             : 
    1224             : /*******************************************************************/
    1225             : /*                                                                 */
    1226             : /*                    LINEAR ALGEBRA MODULO P                      */
    1227             : /*                                                                 */
    1228             : /*******************************************************************/
    1229             : 
    1230             : static long
    1231     1924620 : F2v_find_nonzero(GEN x0, GEN mask0, long l, long m)
    1232             : {
    1233     1924620 :   ulong *x = (ulong *)x0+2, *mask = (ulong *)mask0+2, e;
    1234             :   long i, j;
    1235     3505663 :   for (i = 0; i < l; i++)
    1236             :   {
    1237     3504323 :     e = *x++ & *mask++;
    1238     3504323 :     if (e)
    1239     1923280 :       for (j = 1; ; j++, e >>= 1) if (e & 1uL) return i*BITS_IN_LONG+j;
    1240     8111301 :   }
    1241        1340 :   return m+1;
    1242             : }
    1243             : 
    1244             : /* in place, destroy x */
    1245             : GEN
    1246      286057 : F2m_ker_sp(GEN x, long deplin)
    1247             : {
    1248             :   GEN y, c, d;
    1249             :   long i, j, k, l, r, m, n;
    1250             : 
    1251      286057 :   n = lg(x)-1;
    1252      286057 :   m = mael(x,1,1); r=0;
    1253             : 
    1254      286057 :   d = cgetg(n+1, t_VECSMALL);
    1255      286057 :   c = const_F2v(m); l = lg(c)-1;
    1256     2064776 :   for (k=1; k<=n; k++)
    1257             :   {
    1258     1797003 :     GEN xk = gel(x,k);
    1259     1797003 :     j = F2v_find_nonzero(xk, c, l, m);
    1260     1797003 :     if (j>m)
    1261             :     {
    1262      389745 :       if (deplin) {
    1263       18284 :         GEN c = zero_F2v(n);
    1264       64776 :         for (i=1; i<k; i++)
    1265       46492 :           if (F2v_coeff(xk, d[i]))
    1266       23534 :             F2v_set(c, i);
    1267       18284 :         F2v_set(c, k);
    1268       18284 :         return c;
    1269             :       }
    1270      371461 :       r++; d[k] = 0;
    1271             :     }
    1272             :     else
    1273             :     {
    1274     1407258 :       F2v_clear(c,j); d[k] = j;
    1275     1407258 :       F2v_clear(xk, j);
    1276    76537807 :       for (i=k+1; i<=n; i++)
    1277             :       {
    1278    75130549 :         GEN xi = gel(x,i);
    1279    75130549 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1280             :       }
    1281     1407258 :       F2v_set(xk, j);
    1282             :     }
    1283             :   }
    1284      267773 :   if (deplin) return NULL;
    1285             : 
    1286      267738 :   y = zero_F2m_copy(n,r);
    1287      639199 :   for (j=k=1; j<=r; j++,k++)
    1288             :   {
    1289      371461 :     GEN C = gel(y,j); while (d[k]) k++;
    1290    13802751 :     for (i=1; i<k; i++)
    1291    13431290 :       if (d[i] && F2m_coeff(x,d[i],k))
    1292     4955381 :         F2v_set(C,i);
    1293      371461 :     F2v_set(C, k);
    1294             :   }
    1295      267738 :   return y;
    1296             : }
    1297             : 
    1298             : static void /* assume m < p */
    1299     7238477 : _Fl_addmul(GEN b, long k, long i, ulong m, ulong p, ulong pi)
    1300             : {
    1301     7238477 :   uel(b,k) = Fl_addmul_pre(m, uel(b,i), uel(b,k), p, pi);
    1302     7238477 : }
    1303             : static void /* same m = 1 */
    1304      456457 : _Fl_add(GEN b, long k, long i, ulong p)
    1305             : {
    1306      456457 :   uel(b,k) = Fl_add(uel(b,k), uel(b,i), p);
    1307      456457 : }
    1308             : static void /* assume m < p && SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1309    16715083 : _Fl_addmul_OK(GEN b, long k, long i, ulong m, ulong p)
    1310             : {
    1311    16715083 :   uel(b,k) += m * uel(b,i);
    1312    16715083 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1313    16715083 : }
    1314             : static void /* assume SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1315     3486867 : _Fl_add_OK(GEN b, long k, long i, ulong p)
    1316             : {
    1317     3486867 :   uel(b,k) += uel(b,i);
    1318     3486867 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1319     3486867 : }
    1320             : 
    1321             : static GEN
    1322      345897 : Flm_ker_gauss_OK(GEN x, ulong p, long deplin)
    1323             : {
    1324             :   GEN y, c, d;
    1325             :   long i, j, k, r, t, m, n;
    1326             :   ulong a;
    1327             : 
    1328      345897 :   n = lg(x)-1;
    1329      345897 :   m=nbrows(x); r=0;
    1330             : 
    1331      345897 :   c = zero_zv(m);
    1332      345897 :   d = cgetg(n+1, t_VECSMALL);
    1333      345897 :   a = 0; /* for gcc -Wall */
    1334     1500013 :   for (k=1; k<=n; k++)
    1335             :   {
    1336     4082679 :     for (j=1; j<=m; j++)
    1337     3434427 :       if (!c[j])
    1338             :       {
    1339     2657348 :         a = ucoeff(x,j,k) % p;
    1340     2657348 :         if (a) break;
    1341             :       }
    1342     1170437 :     if (j > m)
    1343             :     {
    1344      648252 :       if (deplin==1) {
    1345       16321 :         c = cgetg(n+1, t_VECSMALL);
    1346       16321 :         for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k) % p;
    1347       16321 :         c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
    1348       16321 :         return c;
    1349             :       }
    1350      631931 :       r++; d[k] = 0;
    1351             :     }
    1352             :     else
    1353             :     {
    1354      522185 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1355      522185 :       c[j] = k; d[k] = j;
    1356      522185 :       ucoeff(x,j,k) = p-1;
    1357      522185 :       if (piv != 1)
    1358      408740 :         for (i=k+1; i<=n; i++) ucoeff(x,j,i) = (piv * ucoeff(x,j,i)) % p;
    1359     2653665 :       for (t=1; t<=m; t++)
    1360             :       {
    1361     2131480 :         if (t == j) continue;
    1362             : 
    1363     1609295 :         piv = ( ucoeff(x,t,k) %= p );
    1364     1609295 :         if (!piv) continue;
    1365     1007111 :         if (piv == 1)
    1366      138932 :           for (i=k+1; i<=n; i++) _Fl_add_OK(gel(x,i),t,j, p);
    1367             :         else
    1368      868179 :           for (i=k+1; i<=n; i++) _Fl_addmul_OK(gel(x,i),t,j,piv, p);
    1369             :       }
    1370             :     }
    1371             :   }
    1372      329576 :   if (deplin==1) return NULL;
    1373             : 
    1374      329569 :   y = cgetg(r+1, t_MAT);
    1375      961500 :   for (j=k=1; j<=r; j++,k++)
    1376             :   {
    1377      631931 :     GEN C = cgetg(n+1, t_VECSMALL);
    1378             : 
    1379      631931 :     gel(y,j) = C; while (d[k]) k++;
    1380     1473418 :     for (i=1; i<k; i++)
    1381      841487 :       if (d[i])
    1382      367686 :         uel(C,i) = ucoeff(x,d[i],k) % p;
    1383             :       else
    1384      473801 :         uel(C,i) = 0UL;
    1385      631931 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1386             :   }
    1387      329569 :   if (deplin == 2) {
    1388           0 :     GEN pc = cgetg(n - r + 1, t_VECSMALL);  /* indices of pivot columns */
    1389           0 :     for (i = j = 1; j <= n; j++)
    1390           0 :       if (d[j]) pc[i++] = j;
    1391           0 :     return mkvec2(y, pc);
    1392             :   }
    1393      329569 :   return y;
    1394             : }
    1395             : 
    1396             : /* in place, destroy x */
    1397             : static GEN
    1398      457168 : Flm_ker_gauss(GEN x, ulong p, long deplin)
    1399             : {
    1400             :   GEN y, c, d;
    1401             :   long i, j, k, r, t, m, n;
    1402             :   ulong a, pi;
    1403      457168 :   n = lg(x)-1;
    1404      457168 :   if (!n) return cgetg(1,t_MAT);
    1405      457168 :   if (SMALL_ULONG(p)) return Flm_ker_gauss_OK(x, p, deplin);
    1406      111271 :   pi = get_Fl_red(p);
    1407             : 
    1408      111271 :   m=nbrows(x); r=0;
    1409             : 
    1410      111271 :   c = zero_zv(m);
    1411      111271 :   d = cgetg(n+1, t_VECSMALL);
    1412      111271 :   a = 0; /* for gcc -Wall */
    1413      353057 :   for (k=1; k<=n; k++)
    1414             :   {
    1415      567880 :     for (j=1; j<=m; j++)
    1416      478557 :       if (!c[j])
    1417             :       {
    1418      358188 :         a = ucoeff(x,j,k);
    1419      358188 :         if (a) break;
    1420             :       }
    1421      241793 :     if (j > m)
    1422             :     {
    1423       89323 :       if (deplin==1) {
    1424           7 :         c = cgetg(n+1, t_VECSMALL);
    1425           7 :         for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k);
    1426           7 :         c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
    1427           7 :         return c;
    1428             :       }
    1429       89316 :       r++; d[k] = 0;
    1430             :     }
    1431             :     else
    1432             :     {
    1433      152470 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1434      152470 :       c[j] = k; d[k] = j;
    1435      152470 :       ucoeff(x,j,k) = p-1;
    1436      152470 :       if (piv != 1)
    1437      278514 :         for (i=k+1; i<=n; i++)
    1438      127240 :           ucoeff(x,j,i) = Fl_mul_pre(piv, ucoeff(x,j,i), p, pi);
    1439      529425 :       for (t=1; t<=m; t++)
    1440             :       {
    1441      376955 :         if (t == j) continue;
    1442             : 
    1443      224485 :         piv = ucoeff(x,t,k);
    1444      224485 :         if (!piv) continue;
    1445      142822 :         if (piv == 1)
    1446        6489 :           for (i=k+1; i<=n; i++) _Fl_add(gel(x,i),t,j,p);
    1447             :         else
    1448      136333 :           for (i=k+1; i<=n; i++) _Fl_addmul(gel(x,i),t,j,piv,p, pi);
    1449             :       }
    1450             :     }
    1451             :   }
    1452      111264 :   if (deplin==1) return NULL;
    1453             : 
    1454      111257 :   y = cgetg(r+1, t_MAT);
    1455      200573 :   for (j=k=1; j<=r; j++,k++)
    1456             :   {
    1457       89316 :     GEN C = cgetg(n+1, t_VECSMALL);
    1458             : 
    1459       89316 :     gel(y,j) = C; while (d[k]) k++;
    1460      177301 :     for (i=1; i<k; i++)
    1461       87985 :       if (d[i])
    1462       57550 :         uel(C,i) = ucoeff(x,d[i],k);
    1463             :       else
    1464       30435 :         uel(C,i) = 0UL;
    1465       89316 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1466             :   }
    1467      111257 :   if (deplin == 2) {
    1468      107407 :     GEN pc = cgetg(n - r + 1, t_VECSMALL);  /* indices of pivot columns */
    1469      324873 :     for (i = j = 1; j <= n; j++)
    1470      217466 :       if (d[j]) pc[i++] = j;
    1471      107407 :     return mkvec2(y, pc);
    1472             :   }
    1473        3850 :   return y;
    1474             : }
    1475             : 
    1476             : GEN
    1477       65565 : FpM_intersect(GEN x, GEN y, GEN p)
    1478             : {
    1479       65565 :   pari_sp av = avma;
    1480       65565 :   long j, lx = lg(x);
    1481             :   GEN z;
    1482             : 
    1483       65565 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    1484       65565 :   z = FpM_ker(shallowconcat(x,y), p);
    1485       65565 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    1486       65565 :   return gerepileupto(av, FpM_mul(x,z,p));
    1487             : }
    1488             : GEN
    1489           0 : Flm_intersect(GEN x, GEN y, ulong p)
    1490             : {
    1491           0 :   pari_sp av = avma;
    1492           0 :   long j, lx = lg(x);
    1493             :   GEN z;
    1494             : 
    1495           0 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    1496           0 :   z = Flm_ker(shallowconcat(x,y), p);
    1497           0 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    1498           0 :   return gerepileupto(av, Flm_mul(x,z,p));
    1499             : }
    1500             : 
    1501             : /* not memory clean */
    1502             : GEN
    1503         113 : F2m_ker(GEN x) { return F2m_ker_sp(F2m_copy(x), 0); }
    1504             : GEN
    1505           0 : F2m_deplin(GEN x) { return F2m_ker_sp(F2m_copy(x), 1); }
    1506             : 
    1507             : static GEN
    1508      117234 : Flm_ker_echelon(GEN x, ulong p, long pivots) {
    1509      117234 :   pari_sp av = avma;
    1510             :   GEN R, Rc, C, C1, C2, S, K;
    1511      117234 :   long n = lg(x) - 1, r;
    1512      117234 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1513      117234 :   Rc = indexcompl(R, n);
    1514      117234 :   C1 = rowpermute(C, R);
    1515      117234 :   C2 = rowpermute(C, Rc);
    1516      117234 :   S = Flm_lsolve_lower_unit(C1, C2, p);
    1517      117234 :   K = vecpermute(shallowconcat(Flm_neg(S, p), matid_Flm(n - r)),
    1518             :                  perm_inv(vecsmall_concat(R, Rc)));
    1519      117234 :   K = Flm_transpose(K);
    1520      117234 :   if (pivots)
    1521         935 :     return gerepilecopy(av, mkvec2(K, R));
    1522      116299 :   return gerepileupto(av, K);
    1523             : }
    1524             : 
    1525             : static GEN
    1526       21928 : Flm_deplin_echelon(GEN x, ulong p) {
    1527       21928 :   pari_sp av = avma;
    1528             :   GEN R, Rc, C, C1, C2, s, v;
    1529       21928 :   long i, n = lg(x) - 1, r;
    1530       21928 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1531       21928 :   if (r == n) { avma = av; return NULL; }
    1532       21921 :   Rc = indexcompl(R, n);
    1533       21921 :   i = Rc[1];
    1534       21921 :   C1 = rowpermute(C, R);
    1535       21921 :   C2 = rowslice(C, i, i);
    1536       21921 :   s = Flm_row(Flm_lsolve_lower_unit(C1, C2, p), 1);
    1537       21921 :   v = vecpermute(vecsmall_concat(Flv_neg(s, p), vecsmall_ei(n - r, 1)),
    1538             :                  perm_inv(vecsmall_concat(R, Rc)));
    1539       21921 :   return gerepileuptoleaf(av, v);
    1540             : }
    1541             : 
    1542             : static GEN
    1543      596330 : Flm_ker_i(GEN x, ulong p, long deplin, long inplace) {
    1544      596330 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1545      139162 :     switch(deplin) {
    1546      116299 :     case 0: return Flm_ker_echelon(x, p, 0);
    1547       21928 :     case 1: return Flm_deplin_echelon(x, p);
    1548         935 :     case 2: return Flm_ker_echelon(x, p, 1);
    1549             :     }
    1550      457168 :   return Flm_ker_gauss(inplace? x: Flm_copy(x), p, deplin);
    1551             : }
    1552             : 
    1553             : GEN
    1554      543896 : Flm_ker_sp(GEN x, ulong p, long deplin) {
    1555      543896 :   return Flm_ker_i(x, p, deplin, 1);
    1556             : }
    1557             : 
    1558             : GEN
    1559       52434 : Flm_ker(GEN x, ulong p) {
    1560       52434 :   return Flm_ker_i(x, p, 0, 0);
    1561             : }
    1562             : 
    1563             : GEN
    1564           0 : Flm_deplin(GEN x, ulong p) {
    1565           0 :   return Flm_ker_i(x, p, 1, 0);
    1566             : }
    1567             : 
    1568             : ulong
    1569          28 : F2m_det_sp(GEN x) { return !F2m_ker_sp(x, 1); }
    1570             : 
    1571             : ulong
    1572          14 : F2m_det(GEN x)
    1573             : {
    1574          14 :   pari_sp av = avma;
    1575          14 :   ulong d = F2m_det_sp(F2m_copy(x));
    1576          14 :   avma = av; return d;
    1577             : }
    1578             : 
    1579             : /* in place, destroy a, SMALL_ULONG(p) is TRUE */
    1580             : static ulong
    1581          14 : Flm_det_gauss_OK(GEN a, long nbco, ulong p)
    1582             : {
    1583          14 :   long i,j,k, s = 1;
    1584          14 :   ulong q, x = 1;
    1585             : 
    1586          42 :   for (i=1; i<nbco; i++)
    1587             :   {
    1588          42 :     for(k=i; k<=nbco; k++)
    1589             :     {
    1590          42 :       ulong c = ucoeff(a,k,i) % p;
    1591          42 :       ucoeff(a,k,i) = c;
    1592          42 :       if (c) break;
    1593             :     }
    1594          28 :     for(j=k+1; j<=nbco; j++) ucoeff(a,j,i) %= p;
    1595          28 :     if (k > nbco) return ucoeff(a,i,i);
    1596          28 :     if (k != i)
    1597             :     { /* exchange the lines s.t. k = i */
    1598          14 :       for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
    1599          14 :       s = -s;
    1600             :     }
    1601          28 :     q = ucoeff(a,i,i);
    1602             : 
    1603          28 :     if (x & HIGHMASK) x %= p;
    1604          28 :     x *= q;
    1605          28 :     q = Fl_inv(q,p);
    1606          70 :     for (k=i+1; k<=nbco; k++)
    1607             :     {
    1608          42 :       ulong m = ucoeff(a,i,k) % p;
    1609          42 :       if (!m) continue;
    1610             : 
    1611          35 :       m = p - ((m*q)%p);
    1612          91 :       for (j=i+1; j<=nbco; j++)
    1613             :       {
    1614          56 :         ulong c = ucoeff(a,j,k);
    1615          56 :         if (c & HIGHMASK) c %= p;
    1616          56 :         ucoeff(a,j,k) = c  + m*ucoeff(a,j,i);
    1617             :       }
    1618             :     }
    1619             :   }
    1620          14 :   if (x & HIGHMASK) x %= p;
    1621          14 :   q = ucoeff(a,nbco,nbco);
    1622          14 :   if (q & HIGHMASK) q %= p;
    1623          14 :   x = (x*q) % p;
    1624          14 :   if (s < 0 && x) x = p - x;
    1625          14 :   return x;
    1626             : }
    1627             : 
    1628             : /* in place, destroy a */
    1629             : static ulong
    1630       40518 : Flm_det_gauss(GEN a, ulong p)
    1631             : {
    1632       40518 :   long i,j,k, s = 1, nbco = lg(a)-1;
    1633       40518 :   ulong pi, q, x = 1;
    1634             : 
    1635       40518 :   if (SMALL_ULONG(p)) return Flm_det_gauss_OK(a, nbco, p);
    1636       40504 :   pi = get_Fl_red(p);
    1637      261577 :   for (i=1; i<nbco; i++)
    1638             :   {
    1639      221080 :     for(k=i; k<=nbco; k++)
    1640      221080 :       if (ucoeff(a,k,i)) break;
    1641      221073 :     if (k > nbco) return ucoeff(a,i,i);
    1642      221073 :     if (k != i)
    1643             :     { /* exchange the lines s.t. k = i */
    1644           7 :       for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
    1645           7 :       s = -s;
    1646             :     }
    1647      221073 :     q = ucoeff(a,i,i);
    1648             : 
    1649      221073 :     x = Fl_mul_pre(x, q, p, pi);
    1650      221073 :     q = Fl_inv(q,p);
    1651      953235 :     for (k=i+1; k<=nbco; k++)
    1652             :     {
    1653      732162 :       ulong m = ucoeff(a,i,k);
    1654      732162 :       if (!m) continue;
    1655             : 
    1656      703425 :       m = Fl_mul_pre(m, q, p, pi);
    1657     3622717 :       for (j=i+1; j<=nbco; j++)
    1658     2919292 :         ucoeff(a,j,k) = Fl_sub(ucoeff(a,j,k), Fl_mul_pre(m,ucoeff(a,j,i), p, pi), p);
    1659             :     }
    1660             :   }
    1661       40504 :   if (s < 0) x = Fl_neg(x, p);
    1662       40504 :   return Fl_mul(x, ucoeff(a,nbco,nbco), p);
    1663             : }
    1664             : 
    1665             : static ulong
    1666       29329 : Flm_det_CUP(GEN a, ulong p) {
    1667             :   GEN R, C, U, P;
    1668       29329 :   long d, i, n = lg(a) - 1, r;
    1669       29329 :   r = Flm_CUP(a, &R, &C, &U, &P, p);
    1670       29329 :   if (r < n)
    1671          42 :     d = 0;
    1672             :   else {
    1673       29287 :     d = perm_sign(P) == 1? 1: p-1;
    1674      293855 :     for (i = 1; i <= n; i++)
    1675      264568 :       d = Fl_mul(d, ucoeff(U, i, i), p);
    1676             :   }
    1677       29329 :   return d;
    1678             : }
    1679             : 
    1680             : static ulong
    1681       69847 : Flm_det_i(GEN x, ulong p, long inplace) {
    1682       69847 :   pari_sp av = avma;
    1683             :   ulong d;
    1684       69847 :   if (lg(x) - 1 >= Flm_CUP_LIMIT)
    1685       29329 :     d = Flm_det_CUP(x, p);
    1686             :   else
    1687       40518 :     d = Flm_det_gauss(inplace? x: Flm_copy(x), p);
    1688       69847 :   avma = av; return d;
    1689             : }
    1690             : 
    1691             : ulong
    1692       69847 : Flm_det_sp(GEN x, ulong p) {
    1693       69847 :   return Flm_det_i(x, p, 1);
    1694             : }
    1695             : 
    1696             : ulong
    1697           0 : Flm_det(GEN x, ulong p) {
    1698           0 :   return Flm_det_i(x, p, 0);
    1699             : }
    1700             : 
    1701             : static GEN
    1702      395923 : FpM_init(GEN a, GEN p, ulong *pp)
    1703             : {
    1704      395923 :   if (lgefint(p) == 3)
    1705             :   {
    1706      391418 :     *pp = uel(p,2);
    1707      391418 :     return (*pp==2)? ZM_to_F2m(a): ZM_to_Flm(a, *pp);
    1708             :   }
    1709        4505 :   *pp = 0; return a;
    1710             : }
    1711             : GEN
    1712        2310 : RgM_Fp_init(GEN a, GEN p, ulong *pp)
    1713             : {
    1714        2310 :   if (lgefint(p) == 3)
    1715             :   {
    1716        2105 :     *pp = uel(p,2);
    1717        2105 :     return (*pp==2)? RgM_to_F2m(a): RgM_to_Flm(a, *pp);
    1718             :   }
    1719         205 :   *pp = 0; return RgM_to_FpM(a,p);
    1720             : }
    1721             : 
    1722             : static GEN
    1723          49 : FpM_det_gen(GEN a, GEN p)
    1724             : {
    1725             :   void *E;
    1726          49 :   const struct bb_field *S = get_Fp_field(&E,p);
    1727          49 :   return gen_det(a, E, S);
    1728             : }
    1729             : GEN
    1730          70 : FpM_det(GEN a, GEN p)
    1731             : {
    1732          70 :   pari_sp av = avma;
    1733             :   ulong pp, d;
    1734          70 :   a = FpM_init(a, p, &pp);
    1735          70 :   switch(pp)
    1736             :   {
    1737          49 :   case 0: return FpM_det_gen(a, p);
    1738          14 :   case 2: d = F2m_det_sp(a); break;
    1739           7 :   default:d = Flm_det_sp(a,pp); break;
    1740             :   }
    1741          21 :   avma = av; return utoi(d);
    1742             : }
    1743             : 
    1744             : /* Destroy x */
    1745             : static GEN
    1746       23126 : F2m_gauss_pivot(GEN x, long *rr)
    1747             : {
    1748             :   GEN c, d;
    1749             :   long i, j, k, l, r, m, n;
    1750             : 
    1751       23126 :   n = lg(x)-1; if (!n) { *rr=0; return NULL; }
    1752       23126 :   m = mael(x,1,1); r=0;
    1753             : 
    1754       23126 :   d = cgetg(n+1, t_VECSMALL);
    1755       23126 :   c = const_F2v(m); l = lg(c)-1;
    1756      150743 :   for (k=1; k<=n; k++)
    1757             :   {
    1758      127617 :     GEN xk = gel(x,k);
    1759      127617 :     j = F2v_find_nonzero(xk, c, l, m);
    1760      127617 :     if (j>m) { r++; d[k] = 0; }
    1761             :     else
    1762             :     {
    1763       82387 :       F2v_clear(c,j); d[k] = j;
    1764     1402323 :       for (i=k+1; i<=n; i++)
    1765             :       {
    1766     1319936 :         GEN xi = gel(x,i);
    1767     1319936 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1768             :       }
    1769             :     }
    1770             :   }
    1771             : 
    1772       23126 :   *rr = r; avma = (pari_sp)d; return d;
    1773             : }
    1774             : 
    1775             : /* Destroy x */
    1776             : static GEN
    1777      150355 : Flm_gauss_pivot(GEN x, ulong p, long *rr)
    1778             : {
    1779             :   GEN c,d;
    1780             :   long i,j,k,r,t,n,m;
    1781             : 
    1782      150355 :   n=lg(x)-1; if (!n) { *rr=0; return NULL; }
    1783             : 
    1784      150355 :   m=nbrows(x); r=0;
    1785      150355 :   d=cgetg(n+1,t_VECSMALL);
    1786      150355 :   c = zero_zv(m);
    1787      629079 :   for (k=1; k<=n; k++)
    1788             :   {
    1789     1432299 :     for (j=1; j<=m; j++)
    1790     1351980 :       if (!c[j])
    1791             :       {
    1792      713342 :         ucoeff(x,j,k) %= p;
    1793      713342 :         if (ucoeff(x,j,k)) break;
    1794             :       }
    1795      478724 :     if (j>m) { r++; d[k]=0; }
    1796             :     else
    1797             :     {
    1798      398405 :       ulong piv = p - Fl_inv(ucoeff(x,j,k), p);
    1799      398405 :       c[j]=k; d[k]=j;
    1800     1080772 :       for (i=k+1; i<=n; i++)
    1801      682367 :         ucoeff(x,j,i) = Fl_mul(piv, ucoeff(x,j,i), p);
    1802     2869869 :       for (t=1; t<=m; t++)
    1803     2471464 :         if (!c[t]) /* no pivot on that line yet */
    1804             :         {
    1805     1576525 :           piv = ucoeff(x,t,k);
    1806     1576525 :           if (piv)
    1807             :           {
    1808     1027092 :             ucoeff(x,t,k) = 0;
    1809     2889183 :             for (i=k+1; i<=n; i++)
    1810     3724182 :               ucoeff(x,t,i) = Fl_add(ucoeff(x,t,i),
    1811     1862091 :                                      Fl_mul(piv,ucoeff(x,j,i),p),p);
    1812             :           }
    1813             :         }
    1814      398405 :       for (i=k; i<=n; i++) ucoeff(x,j,i) = 0; /* dummy */
    1815             :     }
    1816             :   }
    1817      150355 :   *rr = r; avma = (pari_sp)d; return d;
    1818             : }
    1819             : 
    1820             : static GEN
    1821       57241 : Flm_pivots_CUP(GEN x, ulong p, long *rr) {
    1822             :   pari_sp av;
    1823       57241 :   long i, n = lg(x) - 1, r;
    1824       57241 :   GEN R, C, U, P, d = zero_zv(n);
    1825       57241 :   av = avma;
    1826       57241 :   r = Flm_CUP(x, &R, &C, &U, &P, p);
    1827      727176 :   for(i = 1; i <= r; i++)
    1828      669935 :     d[P[i]] = R[i];
    1829       57241 :   avma = av;
    1830       57241 :   *rr = n - r;
    1831       57241 :   return d;
    1832             : }
    1833             : 
    1834             : static GEN
    1835      207596 : Flm_pivots(GEN x, ulong p, long *rr, long inplace) {
    1836      207596 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1837       57241 :     return Flm_pivots_CUP(x, p, rr);
    1838      150355 :   return Flm_gauss_pivot(inplace? x: Flm_copy(x), p, rr);
    1839             : }
    1840             : 
    1841             : static GEN
    1842          59 : FpM_gauss_pivot_gen(GEN x, GEN p, long *rr)
    1843             : {
    1844             :   void *E;
    1845          59 :   const struct bb_field *S = get_Fp_field(&E,p);
    1846          59 :   return gen_Gauss_pivot(x, rr, E, S);
    1847             : }
    1848             : static GEN
    1849      116758 : FpM_gauss_pivot(GEN x, GEN p, long *rr)
    1850             : {
    1851             :   ulong pp;
    1852      116758 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1853      115365 :   x = FpM_init(x, p, &pp);
    1854      115365 :   switch(pp)
    1855             :   {
    1856          59 :   case 0: return FpM_gauss_pivot_gen(x, p, rr);
    1857       23042 :   case 2: return F2m_gauss_pivot(x, rr);
    1858       92264 :   default:return Flm_pivots(x, pp, rr, 1);
    1859             :   }
    1860             : }
    1861             : 
    1862             : GEN
    1863       84833 : FpM_image(GEN x, GEN p)
    1864             : {
    1865             :   long r;
    1866       84833 :   GEN d = FpM_gauss_pivot(x,p,&r); /* d left on stack for efficiency */
    1867       84833 :   return image_from_pivot(x,d,r);
    1868             : }
    1869             : 
    1870             : GEN
    1871          63 : Flm_image(GEN x, ulong p)
    1872             : {
    1873             :   long r;
    1874          63 :   GEN d = Flm_pivots(x, p, &r, 0); /* d left on stack for efficiency */
    1875          63 :   return image_from_pivot(x,d,r);
    1876             : }
    1877             : 
    1878             : GEN
    1879           7 : F2m_image(GEN x)
    1880             : {
    1881             :   long r;
    1882           7 :   GEN d = F2m_gauss_pivot(F2m_copy(x),&r); /* d left on stack for efficiency */
    1883           7 :   return image_from_pivot(x,d,r);
    1884             : }
    1885             : 
    1886             : long
    1887           7 : FpM_rank(GEN x, GEN p)
    1888             : {
    1889           7 :   pari_sp av = avma;
    1890             :   long r;
    1891           7 :   (void)FpM_gauss_pivot(x,p,&r);
    1892           7 :   avma = av; return lg(x)-1 - r;
    1893             : }
    1894             : 
    1895             : long
    1896        2211 : Flm_rank(GEN x, ulong p)
    1897             : {
    1898        2211 :   pari_sp av = avma;
    1899             :   long r;
    1900        2211 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT) {
    1901             :     GEN R, C;
    1902          35 :     r = Flm_echelon(x, &R, &C, p);
    1903          35 :     avma = av; return r;
    1904             :   }
    1905        2176 :   (void) Flm_pivots(x, p, &r, 0);
    1906        2176 :   avma = av; return lg(x)-1 - r;
    1907             : }
    1908             : 
    1909             : long
    1910          63 : F2m_rank(GEN x)
    1911             : {
    1912          63 :   pari_sp av = avma;
    1913             :   long r;
    1914          63 :   (void)F2m_gauss_pivot(F2m_copy(x),&r);
    1915          63 :   avma = av; return lg(x)-1 - r;
    1916             : }
    1917             : 
    1918             : static GEN
    1919         140 : FlxqM_gauss_pivot(GEN x, GEN T, ulong p, long *rr)
    1920             : {
    1921             :   void *E;
    1922         140 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1923         140 :   return gen_Gauss_pivot(x, rr, E, S);
    1924             : }
    1925             : 
    1926             : static GEN
    1927          14 : FlxqM_pivots_CUP(GEN x, GEN T, ulong p, long *rr) {
    1928             :   pari_sp av;
    1929          14 :   long i, n = lg(x) - 1, r;
    1930          14 :   GEN R, C, U, P, d = zero_zv(n);
    1931          14 :   av = avma;
    1932          14 :   r = FlxqM_CUP(x, &R, &C, &U, &P, T, p);
    1933         182 :   for(i = 1; i <= r; i++)
    1934         168 :     d[P[i]] = R[i];
    1935          14 :   avma = av;
    1936          14 :   *rr = n - r;
    1937          14 :   return d;
    1938             : }
    1939             : 
    1940             : static GEN
    1941          42 : FlxqM_pivots(GEN x, GEN T, ulong p, long *rr) {
    1942          42 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    1943          14 :     return FlxqM_pivots_CUP(x, T, p, rr);
    1944          28 :   return FlxqM_gauss_pivot(x, T, p, rr);
    1945             : }
    1946             : 
    1947             : GEN
    1948          21 : FlxqM_image(GEN x, GEN T, ulong p)
    1949             : {
    1950             :   long r;
    1951          21 :   GEN d = FlxqM_pivots(x, T, p, &r); /* d left on stack for efficiency */
    1952          21 :   return image_from_pivot(x,d,r);
    1953             : }
    1954             : 
    1955             : long
    1956         160 : FlxqM_rank(GEN x, GEN T, ulong p)
    1957             : {
    1958         160 :   pari_sp av = avma;
    1959             :   long r;
    1960         160 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT) {
    1961             :     GEN R, C;
    1962         139 :     r = FlxqM_echelon(x, &R, &C, T, p);
    1963         139 :     avma = av; return r;
    1964             :   }
    1965          21 :   (void) FlxqM_pivots(x, T, p, &r);
    1966          21 :   avma = av; return lg(x)-1 - r;
    1967             : }
    1968             : 
    1969             : static GEN
    1970           7 : FlxqM_det_gen(GEN a, GEN T, ulong p)
    1971             : {
    1972             :   void *E;
    1973           7 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1974           7 :   return gen_det(a, E, S);
    1975             : }
    1976             : 
    1977             : static GEN
    1978          14 : FlxqM_det_CUP(GEN a, GEN T, ulong p) {
    1979          14 :   pari_sp av = avma;
    1980             :   GEN R, C, U, P, d;
    1981          14 :   long i, n = lg(a) - 1, r, sv = get_Flx_var(T);
    1982          14 :   r = FlxqM_CUP(a, &R, &C, &U, &P, T, p);
    1983          14 :   if (r < n)
    1984           0 :     d = pol0_Flx(sv);
    1985             :   else {
    1986          14 :     d = mkvecsmall2(sv, perm_sign(P) == 1? 1: p - 1);
    1987         322 :     for (i = 1; i <= n; i++)
    1988         308 :       d = Flxq_mul(d, gcoeff(U, i, i), T, p);
    1989             :   }
    1990          14 :   return gerepileuptoleaf(av, d);
    1991             : }
    1992             : 
    1993             : GEN
    1994          21 : FlxqM_det(GEN a, GEN T, ulong p) {
    1995          21 :   if (lg(a) - 1 >= FlxqM_CUP_LIMIT)
    1996          14 :     return FlxqM_det_CUP(a, T, p);
    1997             :   else
    1998           7 :     return FlxqM_det_gen(a, T, p);
    1999             : }
    2000             : 
    2001             : GEN
    2002          21 : FlxqM_FlxqC_invimage(GEN A, GEN B, GEN T, ulong p) {
    2003             :   void *E;
    2004          21 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    2005          21 :   return gen_matcolinvimage(A, B, E, ff);
    2006             : }
    2007             : 
    2008             : GEN
    2009          35 : FlxqM_FlxqC_mul(GEN A, GEN B, GEN T, ulong p) {
    2010             :   void *E;
    2011          35 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    2012          35 :   return gen_matcolmul(A, B, E, ff);
    2013             : }
    2014             : 
    2015             : GEN
    2016        8586 : FlxqM_mul(GEN A, GEN B, GEN T, ulong p) {
    2017             :   void *E;
    2018             :   const struct bb_field *ff;
    2019        8586 :   long n = lg(A) - 1;
    2020             : 
    2021        8586 :   if (n == 0)
    2022           0 :     return cgetg(1, t_MAT);
    2023        8586 :   if (n > 1)
    2024        7853 :     return FlxqM_mul_Kronecker(A, B, T, p);
    2025         733 :   ff = get_Flxq_field(&E, T, p);
    2026         733 :   return gen_matmul(A, B, E, ff);
    2027             : }
    2028             : 
    2029             : static GEN
    2030           7 : FlxqM_invimage_gen(GEN A, GEN B, GEN T, ulong p) {
    2031             :   void *E;
    2032           7 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    2033           7 :   return gen_matinvimage(A, B, E, ff);
    2034             : }
    2035             : 
    2036             : static GEN
    2037          21 : FlxqM_invimage_CUP(GEN A, GEN B, GEN T, ulong p) {
    2038          21 :   pari_sp av = avma;
    2039             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    2040          21 :   long r, sv = get_Flx_var(T);
    2041          21 :   r = FlxqM_CUP(A, &R, &C, &U, &P, T, p);
    2042          21 :   Rc = indexcompl(R, nbrows(B));
    2043          21 :   C1 = rowpermute(C, R);
    2044          21 :   C2 = rowpermute(C, Rc);
    2045          21 :   B1 = rowpermute(B, R);
    2046          21 :   B2 = rowpermute(B, Rc);
    2047          21 :   Z = FlxqM_rsolve_lower_unit(C1, B1, T, p);
    2048          21 :   if (!gequal(FlxqM_mul(C2, Z, T, p), B2))
    2049          14 :     return NULL;
    2050          14 :   Y = vconcat(FlxqM_rsolve_upper(vecslice(U, 1, r), Z, T, p),
    2051          14 :               zero_FlxM(lg(A) - 1 - r, lg(B) - 1, sv));
    2052           7 :   X = rowpermute(Y, perm_inv(P));
    2053           7 :   return gerepilecopy(av, X);
    2054             : }
    2055             : 
    2056             : GEN
    2057          28 : FlxqM_invimage(GEN A, GEN B, GEN T, ulong p) {
    2058          28 :   long nA = lg(A)-1, nB = lg(B)-1;
    2059             : 
    2060          28 :   if (!nB) return cgetg(1, t_MAT);
    2061          28 :   if (nA + nB >= FlxqM_CUP_LIMIT && nbrows(B) >= FlxqM_CUP_LIMIT)
    2062          21 :     return FlxqM_invimage_CUP(A, B, T, p);
    2063           7 :   return FlxqM_invimage_gen(A, B, T, p);
    2064             : }
    2065             : 
    2066             : GEN
    2067           7 : F2xqM_det(GEN a, GEN T)
    2068             : {
    2069             :   void *E;
    2070           7 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2071           7 :   return gen_det(a, E, S);
    2072             : }
    2073             : 
    2074             : static GEN
    2075          77 : F2xqM_gauss_gen(GEN a, GEN b, GEN T)
    2076             : {
    2077             :   void *E;
    2078          77 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2079          77 :   return gen_Gauss(a, b, E, S);
    2080             : }
    2081             : 
    2082             : GEN
    2083          14 : F2xqM_gauss(GEN a, GEN b, GEN T)
    2084             : {
    2085          14 :   pari_sp av = avma;
    2086          14 :   long n = lg(a)-1;
    2087             :   GEN u;
    2088          14 :   if (!n || lg(b)==1) { avma = av; return cgetg(1, t_MAT); }
    2089          14 :   u = F2xqM_gauss_gen(a, b, T);
    2090          14 :   if (!u) { avma = av; return NULL; }
    2091           7 :   return gerepilecopy(av, u);
    2092             : }
    2093             : 
    2094             : GEN
    2095          35 : F2xqM_inv(GEN a, GEN T)
    2096             : {
    2097          35 :   pari_sp av = avma;
    2098             :   GEN u;
    2099          35 :   if (lg(a) == 1) { avma = av; return cgetg(1, t_MAT); }
    2100          35 :   u = F2xqM_gauss_gen(a, matid_F2xqM(nbrows(a),T), T);
    2101          35 :   if (!u) { avma = av; return NULL; }
    2102          28 :   return gerepilecopy(av, u);
    2103             : }
    2104             : 
    2105             : GEN
    2106          28 : F2xqM_F2xqC_gauss(GEN a, GEN b, GEN T)
    2107             : {
    2108          28 :   pari_sp av = avma;
    2109             :   GEN u;
    2110          28 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2111          28 :   u = F2xqM_gauss_gen(a, mkmat(b), T);
    2112          28 :   if (!u) { avma = av; return NULL; }
    2113          14 :   return gerepilecopy(av, gel(u,1));
    2114             : }
    2115             : 
    2116             : GEN
    2117          21 : F2xqM_F2xqC_invimage(GEN A, GEN B, GEN T) {
    2118             :   void *E;
    2119          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2120          21 :   return gen_matcolinvimage(A, B, E, ff);
    2121             : }
    2122             : 
    2123             : GEN
    2124          21 : F2xqM_F2xqC_mul(GEN A, GEN B, GEN T) {
    2125             :   void *E;
    2126          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2127          21 :   return gen_matcolmul(A, B, E, ff);
    2128             : }
    2129             : 
    2130             : GEN
    2131           7 : F2xqM_mul(GEN A, GEN B, GEN T) {
    2132             :   void *E;
    2133           7 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2134           7 :   return gen_matmul(A, B, E, ff);
    2135             : }
    2136             : 
    2137             : GEN
    2138           7 : F2xqM_invimage(GEN A, GEN B, GEN T) {
    2139             :   void *E;
    2140           7 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2141           7 :   return gen_matinvimage(A, B, E, ff);
    2142             : }
    2143             : 
    2144             : static GEN
    2145          78 : FqM_gauss_pivot_gen(GEN x, GEN T, GEN p, long *rr)
    2146             : {
    2147             :   void *E;
    2148          78 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2149          78 :   return gen_Gauss_pivot(x, rr, E, S);
    2150             : }
    2151             : static GEN
    2152         176 : FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr)
    2153             : {
    2154         176 :   if (lg(x)==1) { *rr = 0; return NULL; }
    2155         176 :   if (!T) return FpM_gauss_pivot(x, p, rr);
    2156         176 :   if (lgefint(p) == 3)
    2157             :   {
    2158          98 :     pari_sp av = avma;
    2159          98 :     ulong pp = uel(p,2);
    2160          98 :     GEN Tp = ZXT_to_FlxT(T, pp);
    2161          98 :     GEN d = FlxqM_gauss_pivot(FqM_to_FlxM(x, T, p), Tp, pp, rr);
    2162          98 :     return d ? gerepileuptoleaf(av, d): d;
    2163             :   }
    2164          78 :   return FqM_gauss_pivot_gen(x, T, p, rr);
    2165             : }
    2166             : 
    2167             : GEN
    2168           7 : FqM_image(GEN x, GEN T, GEN p)
    2169             : {
    2170             :   long r;
    2171           7 :   GEN d = FqM_gauss_pivot(x,T,p,&r); /* d left on stack for efficiency */
    2172           7 :   return image_from_pivot(x,d,r);
    2173             : }
    2174             : 
    2175             : long
    2176          57 : FqM_rank(GEN x, GEN T, GEN p)
    2177             : {
    2178          57 :   pari_sp av = avma;
    2179             :   long r;
    2180          57 :   (void)FqM_gauss_pivot(x,T,p,&r);
    2181          57 :   avma = av; return lg(x)-1 - r;
    2182             : }
    2183             : 
    2184             : GEN
    2185           7 : FqM_det(GEN x, GEN T, GEN p)
    2186             : {
    2187             :   void *E;
    2188           7 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2189           7 :   return gen_det(x, E, S);
    2190             : }
    2191             : 
    2192             : GEN
    2193          21 : FqM_FqC_invimage(GEN A, GEN B, GEN T, GEN p) {
    2194             :   void *E;
    2195          21 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2196          21 :   return gen_matcolinvimage(A, B, E, ff);
    2197             : }
    2198             : 
    2199             : GEN
    2200          21 : FqM_FqC_mul(GEN A, GEN B, GEN T, GEN p) {
    2201             :   void *E;
    2202          21 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2203          21 :   return gen_matcolmul(A, B, E, ff);
    2204             : }
    2205             : 
    2206             : GEN
    2207         320 : FqM_mul(GEN A, GEN B, GEN T, GEN p) {
    2208             :   void *E;
    2209         320 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2210         320 :   return gen_matmul(A, B, E, ff);
    2211             : }
    2212             : 
    2213             : GEN
    2214           7 : FqM_invimage(GEN A, GEN B, GEN T, GEN p) {
    2215             :   void *E;
    2216           7 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2217           7 :   return gen_matinvimage(A, B, E, ff);
    2218             : }
    2219             : 
    2220             : static GEN
    2221        1618 : FpM_ker_gen(GEN x, GEN p, long deplin)
    2222             : {
    2223             :   void *E;
    2224        1618 :   const struct bb_field *S = get_Fp_field(&E,p);
    2225        1618 :   return gen_ker(x, deplin, E, S);
    2226             : }
    2227             : static GEN
    2228      238729 : FpM_ker_i(GEN x, GEN p, long deplin)
    2229             : {
    2230      238729 :   pari_sp av = avma;
    2231             :   ulong pp;
    2232             :   GEN y;
    2233             : 
    2234      238729 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2235      238624 :   x = FpM_init(x, p, &pp);
    2236      238624 :   switch(pp)
    2237             :   {
    2238        1590 :   case 0: return FpM_ker_gen(x,p,deplin);
    2239             :   case 2:
    2240       55713 :     y = F2m_ker_sp(x, deplin);
    2241       55713 :     if (!y) return y;
    2242       55713 :     y = deplin? F2c_to_ZC(y): F2m_to_ZM(y);
    2243       55713 :     return gerepileupto(av, y);
    2244             :   default:
    2245      181321 :     y = Flm_ker_sp(x, pp, deplin);
    2246      181321 :     if (!y) return y;
    2247      181321 :     y = deplin? Flc_to_ZC(y): Flm_to_ZM(y);
    2248      181321 :     return gerepileupto(av, y);
    2249             :   }
    2250             : }
    2251             : 
    2252             : GEN
    2253      181230 : FpM_ker(GEN x, GEN p) { return FpM_ker_i(x,p,0); }
    2254             : 
    2255             : GEN
    2256       56491 : FpM_deplin(GEN x, GEN p) { return FpM_ker_i(x,p,1); }
    2257             : 
    2258             : static GEN
    2259          29 : FqM_ker_gen(GEN x, GEN T, GEN p, long deplin)
    2260             : {
    2261             :   void *E;
    2262          29 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2263          29 :   return gen_ker(x,deplin,E,S);
    2264             : }
    2265             : static GEN
    2266        1268 : FqM_ker_i(GEN x, GEN T, GEN p, long deplin)
    2267             : {
    2268        1268 :   if (!T) return FpM_ker_i(x,p,deplin);
    2269         260 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2270             : 
    2271         260 :   if (lgefint(p)==3)
    2272             :   {
    2273         231 :     pari_sp ltop=avma;
    2274         231 :     ulong l= p[2];
    2275         231 :     GEN Ml = FqM_to_FlxM(x, T, p);
    2276         231 :     GEN Tl = ZXT_to_FlxT(T,l);
    2277         231 :     GEN p1 = FlxM_to_ZXM(FlxqM_ker(Ml,Tl,l));
    2278         231 :     return gerepileupto(ltop,p1);
    2279             :   }
    2280          29 :   return FqM_ker_gen(x, T, p, deplin);
    2281             : }
    2282             : 
    2283             : GEN
    2284        1254 : FqM_ker(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,0); }
    2285             : 
    2286             : GEN
    2287          14 : FqM_deplin(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,1); }
    2288             : 
    2289             : static GEN
    2290         119 : FlxqM_ker_gen(GEN x, GEN T, ulong p, long deplin)
    2291             : {
    2292             :   const struct bb_field *ff;
    2293             :   void *E;
    2294             : 
    2295         119 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2296         119 :   ff=get_Flxq_field(&E,T,p);
    2297         119 :   return gen_ker(x,deplin, E, ff);
    2298             : }
    2299             : 
    2300             : static GEN
    2301         468 : FlxqM_ker_echelon(GEN x, GEN T, ulong p) {
    2302         468 :   pari_sp av = avma;
    2303             :   GEN R, Rc, C, C1, C2, S, K;
    2304         468 :   long n = lg(x) - 1, r;
    2305         468 :   r = FlxqM_echelon(shallowtrans(x), &R, &C, T, p);
    2306         468 :   Rc = indexcompl(R, n);
    2307         468 :   C1 = rowpermute(C, R);
    2308         468 :   C2 = rowpermute(C, Rc);
    2309         468 :   S = FlxqM_lsolve_lower_unit(C1, C2, T, p);
    2310         468 :   K = vecpermute(shallowconcat(FlxM_neg(S, p), matid_FlxqM(n - r, T, p)),
    2311             :                  perm_inv(vecsmall_concat(R, Rc)));
    2312         468 :   K = shallowtrans(K);
    2313         468 :   return gerepilecopy(av, K);
    2314             : }
    2315             : 
    2316             : static GEN
    2317          14 : col_ei_FlxC(long n, long i, long sv) {
    2318          14 :   GEN v = zero_FlxC(n, sv);
    2319          14 :   gel(v, i) = pol1_Flx(sv);
    2320          14 :   return v;
    2321             : }
    2322             : 
    2323             : static GEN
    2324          21 : FlxqM_deplin_echelon(GEN x, GEN T, ulong p) {
    2325          21 :   pari_sp av = avma;
    2326             :   GEN R, Rc, C, C1, C2, s, v;
    2327          21 :   long i, n = lg(x) - 1, r, sv = get_Flx_var(T);
    2328          21 :   r = FlxqM_echelon(shallowtrans(x), &R, &C, T, p);
    2329          21 :   if (r == n) { avma = av; return NULL; }
    2330          14 :   Rc = indexcompl(R, n);
    2331          14 :   i = Rc[1];
    2332          14 :   C1 = rowpermute(C, R);
    2333          14 :   C2 = rowslice(C, i, i);
    2334          14 :   s = row(FlxqM_lsolve_lower_unit(C1, C2, T, p), 1);
    2335          14 :   settyp(s, t_COL);
    2336          14 :   v = vecpermute(shallowconcat(FlxC_neg(s, p), col_ei_FlxC(n - r, 1, sv)),
    2337             :                  perm_inv(vecsmall_concat(R, Rc)));
    2338          14 :   return gerepilecopy(av, v);
    2339             : }
    2340             : 
    2341             : static GEN
    2342         608 : FlxqM_ker_i(GEN x, GEN T, ulong p, long deplin) {
    2343         608 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    2344         489 :     return deplin? FlxqM_deplin_echelon(x, T, p): FlxqM_ker_echelon(x, T, p);
    2345         119 :   return FlxqM_ker_gen(x, T, p, deplin);
    2346             : }
    2347             : 
    2348             : GEN
    2349         573 : FlxqM_ker(GEN x, GEN T, ulong p)
    2350             : {
    2351         573 :   return FlxqM_ker_i(x, T, p, 0);
    2352             : }
    2353             : 
    2354             : GEN
    2355          35 : FlxqM_deplin(GEN x, GEN T, ulong p)
    2356             : {
    2357          35 :   return FlxqM_ker_i(x, T, p, 1);
    2358             : }
    2359             : 
    2360             : static GEN
    2361          35 : F2xqM_ker_i(GEN x, GEN T, long deplin)
    2362             : {
    2363             :   const struct bb_field *ff;
    2364             :   void *E;
    2365             : 
    2366          35 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2367          35 :   ff = get_F2xq_field(&E,T);
    2368          35 :   return gen_ker(x,deplin, E, ff);
    2369             : }
    2370             : 
    2371             : GEN
    2372          21 : F2xqM_ker(GEN x, GEN T)
    2373             : {
    2374          21 :   return F2xqM_ker_i(x, T, 0);
    2375             : }
    2376             : 
    2377             : GEN
    2378          14 : F2xqM_deplin(GEN x, GEN T)
    2379             : {
    2380          14 :   return F2xqM_ker_i(x, T, 1);
    2381             : }
    2382             : 
    2383             : static GEN
    2384          28 : F2xqM_gauss_pivot(GEN x, GEN T, long *rr)
    2385             : {
    2386             :   void *E;
    2387          28 :   const struct bb_field *S = get_F2xq_field(&E,T);
    2388          28 :   return gen_Gauss_pivot(x, rr, E, S);
    2389             : }
    2390             : GEN
    2391           7 : F2xqM_image(GEN x, GEN T)
    2392             : {
    2393             :   long r;
    2394           7 :   GEN d = F2xqM_gauss_pivot(x,T,&r); /* d left on stack for efficiency */
    2395           7 :   return image_from_pivot(x,d,r);
    2396             : }
    2397             : long
    2398           7 : F2xqM_rank(GEN x, GEN T)
    2399             : {
    2400           7 :   pari_sp av = avma;
    2401             :   long r;
    2402           7 :   (void)F2xqM_gauss_pivot(x,T,&r);
    2403           7 :   avma = av; return lg(x)-1 - r;
    2404             : }
    2405             : /*******************************************************************/
    2406             : /*                                                                 */
    2407             : /*                       Solve A*X=B (Gauss pivot)                 */
    2408             : /*                                                                 */
    2409             : /*******************************************************************/
    2410             : /* x ~ 0 compared to reference y */
    2411             : int
    2412      558068 : approx_0(GEN x, GEN y)
    2413             : {
    2414      558068 :   long tx = typ(x);
    2415      558068 :   if (tx == t_COMPLEX)
    2416           0 :     return approx_0(gel(x,1), y) && approx_0(gel(x,2), y);
    2417      558208 :   return gequal0(x) ||
    2418      385669 :          (tx == t_REAL && gexpo(y) - gexpo(x) > bit_prec(x));
    2419             : }
    2420             : /* x a column, x0 same column in the original input matrix (for reference),
    2421             :  * c list of pivots so far */
    2422             : static long
    2423      562212 : gauss_get_pivot_max(GEN X, GEN X0, long ix, GEN c)
    2424             : {
    2425      562212 :   GEN p, r, x = gel(X,ix), x0 = gel(X0,ix);
    2426      562212 :   long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
    2427      562212 :   if (c)
    2428             :   {
    2429       46550 :     for (i=1; i<lx; i++)
    2430       35805 :       if (!c[i])
    2431             :       {
    2432       18634 :         long e = gexpo(gel(x,i));
    2433       18634 :         if (e > ex) { ex = e; k = i; }
    2434             :       }
    2435             :   }
    2436             :   else
    2437             :   {
    2438     1894829 :     for (i=ix; i<lx; i++)
    2439             :     {
    2440     1343362 :       long e = gexpo(gel(x,i));
    2441     1343362 :       if (e > ex) { ex = e; k = i; }
    2442             :     }
    2443             :   }
    2444      562212 :   if (!k) return lx;
    2445      558068 :   p = gel(x,k);
    2446      558068 :   r = gel(x0,k); if (isrationalzero(r)) r = x0;
    2447      558068 :   return approx_0(p, r)? lx: k;
    2448             : }
    2449             : static long
    2450       63091 : gauss_get_pivot_padic(GEN X, GEN p, long ix, GEN c)
    2451             : {
    2452       63091 :   GEN x = gel(X, ix);
    2453       63091 :   long i, k = 0, ex = (long)HIGHVALPBIT, lx = lg(x);
    2454       63091 :   if (c)
    2455             :   {
    2456         504 :     for (i=1; i<lx; i++)
    2457         378 :       if (!c[i] && !gequal0(gel(x,i)))
    2458             :       {
    2459         245 :         long e = gvaluation(gel(x,i), p);
    2460         245 :         if (e < ex) { ex = e; k = i; }
    2461             :       }
    2462             :   }
    2463             :   else
    2464             :   {
    2465      443905 :     for (i=ix; i<lx; i++)
    2466      380940 :       if (!gequal0(gel(x,i)))
    2467             :       {
    2468      182434 :         long e = gvaluation(gel(x,i), p);
    2469      182434 :         if (e < ex) { ex = e; k = i; }
    2470             :       }
    2471             :   }
    2472       63091 :   return k? k: lx;
    2473             : }
    2474             : static long
    2475      290655 : gauss_get_pivot_NZ(GEN X, GEN x0/*unused*/, long ix, GEN c)
    2476             : {
    2477      290655 :   GEN x = gel(X, ix);
    2478      290655 :   long i, lx = lg(x);
    2479             :   (void)x0;
    2480      290655 :   if (c)
    2481             :   {
    2482     1512812 :     for (i=1; i<lx; i++)
    2483     1492736 :       if (!c[i] && !gequal0(gel(x,i))) return i;
    2484             :   }
    2485             :   else
    2486             :   {
    2487      288647 :     for (i=ix; i<lx; i++)
    2488      288640 :       if (!gequal0(gel(x,i))) return i;
    2489             :   }
    2490       20083 :   return lx;
    2491             : }
    2492             : 
    2493             : /* Return pivot seeking function appropriate for the domain of the RgM x
    2494             :  * (first non zero pivot, maximal pivot...)
    2495             :  * x0 is a reference point used when guessing whether x[i,j] ~ 0
    2496             :  * (iff x[i,j] << x0[i,j]); typical case: mateigen, Gauss pivot on x - vp.Id,
    2497             :  * but use original x when deciding whether a prospective pivot is non-0 */
    2498             : static pivot_fun
    2499      248265 : get_pivot_fun(GEN x, GEN x0, GEN *data)
    2500             : {
    2501      248265 :   long i, j, hx, lx = lg(x);
    2502      248265 :   int res = t_INT;
    2503      248265 :   GEN p = NULL;
    2504             : 
    2505      248265 :   *data = NULL;
    2506      248265 :   if (lx == 1) return &gauss_get_pivot_NZ;
    2507      248125 :   hx = lgcols(x);
    2508     1182007 :   for (j=1; j<lx; j++)
    2509             :   {
    2510      933910 :     GEN xj = gel(x,j);
    2511     8649505 :     for (i=1; i<hx; i++)
    2512             :     {
    2513     7715623 :       GEN c = gel(xj,i);
    2514     7715623 :       switch(typ(c))
    2515             :       {
    2516             :         case t_REAL:
    2517     1533019 :           res = t_REAL;
    2518     1533019 :           break;
    2519             :         case t_COMPLEX:
    2520          14 :           if (typ(gel(c,1)) == t_REAL || typ(gel(c,2)) == t_REAL) res = t_REAL;
    2521          14 :           break;
    2522             :         case t_INT: case t_INTMOD: case t_FRAC: case t_FFELT: case t_QUAD:
    2523             :         case t_POLMOD: /* exact types */
    2524     6023123 :           break;
    2525             :         case t_PADIC:
    2526      159439 :           p = gel(c,2);
    2527      159439 :           res = t_PADIC;
    2528      159439 :           break;
    2529          28 :         default: return &gauss_get_pivot_NZ;
    2530             :       }
    2531             :     }
    2532             :   }
    2533      248097 :   switch(res)
    2534             :   {
    2535      167683 :     case t_REAL: *data = x0; return &gauss_get_pivot_max;
    2536        8225 :     case t_PADIC: *data = p; return &gauss_get_pivot_padic;
    2537       72189 :     default: return &gauss_get_pivot_NZ;
    2538             :   }
    2539             : }
    2540             : 
    2541             : static GEN
    2542      462855 : get_col(GEN a, GEN b, GEN p, long li)
    2543             : {
    2544      462855 :   GEN u = cgetg(li+1,t_COL);
    2545             :   long i, j;
    2546             : 
    2547      462855 :   gel(u,li) = gdiv(gel(b,li), p);
    2548     2590227 :   for (i=li-1; i>0; i--)
    2549             :   {
    2550     2127372 :     pari_sp av = avma;
    2551     2127372 :     GEN m = gel(b,i);
    2552     2127372 :     for (j=i+1; j<=li; j++) m = gsub(m, gmul(gcoeff(a,i,j), gel(u,j)));
    2553     2127372 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(a,i,i)));
    2554             :   }
    2555      462855 :   return u;
    2556             : }
    2557             : /* assume 0 <= a[i,j] < p */
    2558             : static GEN
    2559      290720 : Fl_get_col_OK(GEN a, GEN b, long li, ulong p)
    2560             : {
    2561      290720 :   GEN u = cgetg(li+1,t_VECSMALL);
    2562      290720 :   ulong m = uel(b,li) % p;
    2563             :   long i,j;
    2564             : 
    2565      290720 :   uel(u,li) = (m * ucoeff(a,li,li)) % p;
    2566     4099747 :   for (i = li-1; i > 0; i--)
    2567             :   {
    2568     3809027 :     m = p - uel(b,i)%p;
    2569    44637518 :     for (j = i+1; j <= li; j++) {
    2570    40828491 :       if (m & HIGHBIT) m %= p;
    2571    40828491 :       m += ucoeff(a,i,j) * uel(u,j); /* 0 <= u[j] < p */
    2572             :     }
    2573     3809027 :     m %= p;
    2574     3809027 :     if (m) m = ((p-m) * ucoeff(a,i,i)) % p;
    2575     3809027 :     uel(u,i) = m;
    2576             :   }
    2577      290720 :   return u;
    2578             : }
    2579             : static GEN
    2580     1860372 : Fl_get_col(GEN a, GEN b, long li, ulong p)
    2581             : {
    2582     1860372 :   GEN u = cgetg(li+1,t_VECSMALL);
    2583     1860372 :   ulong m = uel(b,li) % p;
    2584             :   long i,j;
    2585             : 
    2586     1860372 :   uel(u,li) = Fl_mul(m, ucoeff(a,li,li), p);
    2587     7727232 :   for (i=li-1; i>0; i--)
    2588             :   {
    2589     5866860 :     m = b[i]%p;
    2590    21145561 :     for (j = i+1; j <= li; j++)
    2591    15278701 :       m = Fl_sub(m, Fl_mul(ucoeff(a,i,j), uel(u,j), p), p);
    2592     5866860 :     if (m) m = Fl_mul(m, ucoeff(a,i,i), p);
    2593     5866860 :     uel(u,i) = m;
    2594             :   }
    2595     1860372 :   return u;
    2596             : }
    2597             : 
    2598             : /* bk -= m * bi */
    2599             : static void
    2600     3957027 : _submul(GEN b, long k, long i, GEN m)
    2601             : {
    2602     3957027 :   gel(b,k) = gsub(gel(b,k), gmul(m, gel(b,i)));
    2603     3957027 : }
    2604             : static int
    2605      650092 : init_gauss(GEN a, GEN *b, long *aco, long *li, int *iscol)
    2606             : {
    2607      650092 :   *iscol = *b ? (typ(*b) == t_COL): 0;
    2608      650092 :   *aco = lg(a) - 1;
    2609      650092 :   if (!*aco) /* a empty */
    2610             :   {
    2611         322 :     if (*b && lg(*b) != 1) pari_err_DIM("gauss");
    2612         322 :     *li = 0; return 0;
    2613             :   }
    2614      649770 :   *li = nbrows(a);
    2615      649770 :   if (*li < *aco) pari_err_INV("gauss [no left inverse]", a);
    2616      649770 :   if (*b)
    2617             :   {
    2618      636944 :     if (*li != *aco) pari_err_DIM("gauss");
    2619      636944 :     switch(typ(*b))
    2620             :     {
    2621             :       case t_MAT:
    2622       53836 :         if (lg(*b) == 1) return 0;
    2623       53836 :         *b = RgM_shallowcopy(*b);
    2624       53836 :         break;
    2625             :       case t_COL:
    2626      583108 :         *b = mkmat( leafcopy(*b) );
    2627      583108 :         break;
    2628           0 :       default: pari_err_TYPE("gauss",*b);
    2629             :     }
    2630      636944 :     if (nbrows(*b) != *li) pari_err_DIM("gauss");
    2631             :   }
    2632             :   else
    2633       12826 :     *b = matid(*li);
    2634      649770 :   return 1;
    2635             : }
    2636             : 
    2637             : static GEN Flm_inv_sp(GEN a, ulong *detp, ulong p);
    2638             : 
    2639             : static int
    2640      313766 : is_modular_solve(GEN a, GEN b, GEN *u)
    2641             : {
    2642      313766 :   GEN p = NULL;
    2643             :   ulong pp;
    2644      313766 :   if (!RgM_is_FpM(a, &p) || !p) return 0;
    2645         203 :   if (!b)
    2646             :   {
    2647          91 :     a = RgM_Fp_init(a, p, &pp);
    2648          91 :     switch(pp)
    2649             :     {
    2650             :     case 0:
    2651          14 :       a = FpM_inv(a,p);
    2652          14 :       if (a) a = FpM_to_mod(a, p);
    2653          14 :       break;
    2654             :     case 2:
    2655          35 :       a = F2m_inv(a);
    2656          35 :       if (a) a = F2m_to_mod(a);
    2657          35 :       break;
    2658             :     default:
    2659          42 :       a = Flm_inv_sp(a, NULL, pp);
    2660          42 :       if (a) a = Flm_to_mod(a, pp);
    2661             :     }
    2662             :   }
    2663         112 :   else switch(typ(b))
    2664             :   {
    2665             :     case t_COL:
    2666          49 :       if (!RgV_is_FpV(b, &p)) return 0;
    2667          49 :       a = RgM_Fp_init(a, p, &pp);
    2668          49 :       switch(pp)
    2669             :       {
    2670             :       case 0:
    2671          14 :         b = RgC_to_FpC(b, p);
    2672          14 :         a = FpM_FpC_gauss(a,b,p);
    2673          14 :         if (a) a = FpC_to_mod(a, p);
    2674          14 :         break;
    2675             :       case 2:
    2676          14 :         b = RgV_to_F2v(b);
    2677          14 :         a = F2m_F2c_gauss(a,b);
    2678          14 :         if (a) a = F2c_to_mod(a);
    2679          14 :         break;
    2680             :       default:
    2681          21 :         b = RgV_to_Flv(b, pp);
    2682          21 :         a = Flm_Flc_gauss(a,b,pp);
    2683          21 :         if (a) a = Flc_to_mod(a, pp);
    2684          21 :         break;
    2685             :       }
    2686          49 :       break;
    2687             :     case t_MAT:
    2688          63 :       if (!RgM_is_FpM(b, &p)) return 0;
    2689          63 :       a = RgM_Fp_init(a, p, &pp);
    2690          63 :       switch(pp)
    2691             :       {
    2692             :       case 0:
    2693          14 :         b = RgM_to_FpM(b, p);
    2694          14 :         a = FpM_gauss(a,b,p);
    2695          14 :         if (a) a = FpM_to_mod(a, p);
    2696          14 :         break;
    2697             :       case 2:
    2698          14 :         b = RgM_to_F2m(b);
    2699          14 :         a = F2m_gauss(a,b);
    2700          14 :         if (a) a = F2m_to_mod(a);
    2701          14 :         break;
    2702             :       default:
    2703          35 :         b = RgM_to_Flm(b, pp);
    2704          35 :         a = Flm_gauss(a,b,pp);
    2705          35 :         if (a) a = Flm_to_mod(a, pp);
    2706          35 :         break;
    2707             :       }
    2708          63 :       break;
    2709           0 :     default: return 0;
    2710             :   }
    2711         203 :   *u = a; return 1;
    2712             : }
    2713             : /* Gaussan Elimination. If a is square, return a^(-1)*b;
    2714             :  * if a has more rows than columns and b is NULL, return c such that c a = Id.
    2715             :  * a is a (not necessarily square) matrix
    2716             :  * b is a matrix or column vector, NULL meaning: take the identity matrix,
    2717             :  *   effectively returning the inverse of a
    2718             :  * If a and b are empty, the result is the empty matrix.
    2719             :  *
    2720             :  * li: number of rows of a and b
    2721             :  * aco: number of columns of a
    2722             :  * bco: number of columns of b (if matrix)
    2723             :  */
    2724             : GEN
    2725      313766 : RgM_solve(GEN a, GEN b)
    2726             : {
    2727      313766 :   pari_sp av = avma;
    2728             :   long i, j, k, li, bco, aco;
    2729             :   int iscol;
    2730             :   pivot_fun pivot;
    2731      313766 :   GEN p, u, data, ff = NULL;
    2732             : 
    2733      313766 :   if (is_modular_solve(a,b,&u)) return gerepileupto(av, u);
    2734      313563 :   if (RgM_is_FFM(a, &ff)) {
    2735         224 :     if (!b)
    2736         126 :       return FFM_inv(a, ff);
    2737          98 :     if (typ(b) == t_COL && RgC_is_FFC(b, &ff))
    2738          56 :       return FFM_FFC_gauss(a, b, ff);
    2739          42 :     if (typ(b) == t_MAT && RgM_is_FFM(b, &ff))
    2740          42 :       return FFM_gauss(a, b, ff);
    2741             :   }
    2742      313339 :   avma = av;
    2743             : 
    2744      313339 :   if (lg(a)-1 == 2 && nbrows(a) == 2) {
    2745             :     /* 2x2 matrix, start by inverting a */
    2746       89921 :     GEN u = gcoeff(a,1,1), v = gcoeff(a,1,2);
    2747       89921 :     GEN w = gcoeff(a,2,1), x = gcoeff(a,2,2);
    2748       89921 :     GEN D = gsub(gmul(u,x), gmul(v,w)), ainv;
    2749       89921 :     if (gequal0(D)) return NULL;
    2750       89921 :     ainv = mkmat2(mkcol2(x, gneg(w)), mkcol2(gneg(v), u));
    2751       89921 :     ainv = gmul(ainv, ginv(D));
    2752       89921 :     if (b) ainv = gmul(ainv, b);
    2753       89921 :     return gerepileupto(av, ainv);
    2754             :   }
    2755             : 
    2756      223418 :   if (!init_gauss(a, &b, &aco, &li, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    2757      223166 :   pivot = get_pivot_fun(a, a, &data);
    2758      223166 :   a = RgM_shallowcopy(a);
    2759      223166 :   bco = lg(b)-1;
    2760      223166 :   if(DEBUGLEVEL>4) err_printf("Entering gauss\n");
    2761             : 
    2762      223166 :   p = NULL; /* gcc -Wall */
    2763      784370 :   for (i=1; i<=aco; i++)
    2764             :   {
    2765             :     /* k is the line where we find the pivot */
    2766      784370 :     k = pivot(a, data, i, NULL);
    2767      784370 :     if (k > li) return NULL;
    2768      784363 :     if (k != i)
    2769             :     { /* exchange the lines s.t. k = i */
    2770      131940 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    2771      131940 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    2772             :     }
    2773      784363 :     p = gcoeff(a,i,i);
    2774      784363 :     if (i == aco) break;
    2775             : 
    2776     2181830 :     for (k=i+1; k<=li; k++)
    2777             :     {
    2778     1620626 :       GEN m = gcoeff(a,k,i);
    2779     1620626 :       if (!gequal0(m))
    2780             :       {
    2781      666110 :         m = gdiv(m,p);
    2782      666110 :         for (j=i+1; j<=aco; j++) _submul(gel(a,j),k,i,m);
    2783      666110 :         for (j=1;   j<=bco; j++) _submul(gel(b,j),k,i,m);
    2784             :       }
    2785             :     }
    2786      561204 :     if (gc_needed(av,1))
    2787             :     {
    2788           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"gauss. i=%ld",i);
    2789           0 :       gerepileall(av,2, &a,&b);
    2790             :     }
    2791             :   }
    2792             : 
    2793      223159 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
    2794      223159 :   u = cgetg(bco+1,t_MAT);
    2795      223159 :   for (j=1; j<=bco; j++) gel(u,j) = get_col(a,gel(b,j),p,aco);
    2796      223159 :   return gerepilecopy(av, iscol? gel(u,1): u);
    2797             : }
    2798             : 
    2799             : /* assume dim A >= 1, A invertible + upper triangular  */
    2800             : static GEN
    2801       16409 : RgM_inv_upper_ind(GEN A, long index)
    2802             : {
    2803       16409 :   long n = lg(A)-1, i = index, j;
    2804       16409 :   GEN u = zerocol(n);
    2805       16409 :   gel(u,i) = ginv(gcoeff(A,i,i));
    2806       45812 :   for (i--; i>0; i--)
    2807             :   {
    2808       29403 :     pari_sp av = avma;
    2809       29403 :     GEN m = gneg(gmul(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2810       29403 :     for (j=i+2; j<=n; j++) m = gsub(m, gmul(gcoeff(A,i,j),gel(u,j)));
    2811       29403 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(A,i,i)));
    2812             :   }
    2813       16409 :   return u;
    2814             : }
    2815             : GEN
    2816        5922 : RgM_inv_upper(GEN A)
    2817             : {
    2818             :   long i, l;
    2819        5922 :   GEN B = cgetg_copy(A, &l);
    2820        5922 :   for (i = 1; i < l; i++) gel(B,i) = RgM_inv_upper_ind(A, i);
    2821        5922 :   return B;
    2822             : }
    2823             : 
    2824             : /* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal  */
    2825             : static GEN
    2826          14 : FpM_inv_upper_1_ind(GEN A, long index, GEN p)
    2827             : {
    2828          14 :   long n = lg(A)-1, i = index, j;
    2829          14 :   GEN u = zerocol(n);
    2830          14 :   gel(u,i) = gen_1;
    2831          21 :   for (i--; i>0; i--)
    2832             :   {
    2833           7 :     pari_sp av = avma;
    2834           7 :     GEN m = negi(mulii(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2835           7 :     for (j=i+2; j<=n; j++) m = subii(m, mulii(gcoeff(A,i,j),gel(u,j)));
    2836           7 :     gel(u,i) = gerepileuptoint(av, modii(m,p));
    2837             :   }
    2838          14 :   return u;
    2839             : }
    2840             : static GEN
    2841           7 : FpM_inv_upper_1(GEN A, GEN p)
    2842             : {
    2843             :   long i, l;
    2844           7 :   GEN B = cgetg_copy(A, &l);
    2845           7 :   for (i = 1; i < l; i++) gel(B,i) = FpM_inv_upper_1_ind(A, i, p);
    2846           7 :   return B;
    2847             : }
    2848             : /* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal,
    2849             :  * reduced mod p */
    2850             : static GEN
    2851          28 : Flm_inv_upper_1_ind(GEN A, long index, ulong p)
    2852             : {
    2853          28 :   long n = lg(A)-1, i = index, j;
    2854          28 :   GEN u = const_vecsmall(n, 0);
    2855          28 :   u[i] = 1;
    2856          28 :   if (SMALL_ULONG(p))
    2857          21 :     for (i--; i>0; i--)
    2858             :     {
    2859           7 :       ulong m = ucoeff(A,i,i+1) * uel(u,i+1); /* j = i+1 */
    2860           7 :       for (j=i+2; j<=n; j++)
    2861             :       {
    2862           0 :         if (m & HIGHMASK) m %= p;
    2863           0 :         m += ucoeff(A,i,j) * uel(u,j);
    2864             :       }
    2865           7 :       u[i] = Fl_neg(m % p, p);
    2866             :     }
    2867             :   else
    2868          21 :     for (i--; i>0; i--)
    2869             :     {
    2870           7 :       ulong m = Fl_mul(ucoeff(A,i,i+1),uel(u,i+1), p); /* j = i+1 */
    2871           7 :       for (j=i+2; j<=n; j++) m = Fl_add(m, Fl_mul(ucoeff(A,i,j),uel(u,j),p), p);
    2872           7 :       u[i] = Fl_neg(m, p);
    2873             :     }
    2874          28 :   return u;
    2875             : }
    2876             : static GEN
    2877          14 : F2m_inv_upper_1_ind(GEN A, long index)
    2878             : {
    2879          14 :   pari_sp av = avma;
    2880          14 :   long n = lg(A)-1, i = index, j;
    2881          14 :   GEN u = const_vecsmall(n, 0);
    2882          14 :   u[i] = 1;
    2883          21 :   for (i--; i>0; i--)
    2884             :   {
    2885           7 :     ulong m = F2m_coeff(A,i,i+1) & uel(u,i+1); /* j = i+1 */
    2886           7 :     for (j=i+2; j<=n; j++) m ^= F2m_coeff(A,i,j) & uel(u,j);
    2887           7 :     u[i] = m & 1;
    2888             :   }
    2889          14 :   return gerepileuptoleaf(av, Flv_to_F2v(u));
    2890             : }
    2891             : static GEN
    2892          14 : Flm_inv_upper_1(GEN A, ulong p)
    2893             : {
    2894             :   long i, l;
    2895          14 :   GEN B = cgetg_copy(A, &l);
    2896          14 :   for (i = 1; i < l; i++) gel(B,i) = Flm_inv_upper_1_ind(A, i, p);
    2897          14 :   return B;
    2898             : }
    2899             : static GEN
    2900           7 : F2m_inv_upper_1(GEN A)
    2901             : {
    2902             :   long i, l;
    2903           7 :   GEN B = cgetg_copy(A, &l);
    2904           7 :   for (i = 1; i < l; i++) gel(B,i) = F2m_inv_upper_1_ind(A, i);
    2905           7 :   return B;
    2906             : }
    2907             : 
    2908             : static GEN
    2909      864650 : split_realimag_col(GEN z, long r1, long r2)
    2910             : {
    2911      864650 :   long i, ru = r1+r2;
    2912      864650 :   GEN x = cgetg(ru+r2+1,t_COL), y = x + r2;
    2913     2774324 :   for (i=1; i<=r1; i++) {
    2914     1909674 :     GEN a = gel(z,i);
    2915     1909674 :     if (typ(a) == t_COMPLEX) a = gel(a,1); /* paranoia: a should be real */
    2916     1909674 :     gel(x,i) = a;
    2917             :   }
    2918     1391043 :   for (   ; i<=ru; i++) {
    2919      526393 :     GEN b, a = gel(z,i);
    2920      526393 :     if (typ(a) == t_COMPLEX) { b = gel(a,2); a = gel(a,1); } else b = gen_0;
    2921      526393 :     gel(x,i) = a;
    2922      526393 :     gel(y,i) = b;
    2923             :   }
    2924      864650 :   return x;
    2925             : }
    2926             : GEN
    2927      439449 : split_realimag(GEN x, long r1, long r2)
    2928             : {
    2929             :   long i,l; GEN y;
    2930      439449 :   if (typ(x) == t_COL) return split_realimag_col(x,r1,r2);
    2931      216015 :   y = cgetg_copy(x, &l);
    2932      216015 :   for (i=1; i<l; i++) gel(y,i) = split_realimag_col(gel(x,i), r1, r2);
    2933      216015 :   return y;
    2934             : }
    2935             : 
    2936             : /* assume M = (r1+r2) x (r1+2r2) matrix and y compatible vector or matrix
    2937             :  * r1 first lines of M,y are real. Solve the system obtained by splitting
    2938             :  * real and imaginary parts. */
    2939             : GEN
    2940      214044 : RgM_solve_realimag(GEN M, GEN y)
    2941             : {
    2942      214044 :   long l = lg(M), r2 = l - lgcols(M), r1 = l-1 - 2*r2;
    2943      214044 :   return RgM_solve(split_realimag(M, r1,r2),
    2944             :                    split_realimag(y, r1,r2));
    2945             : }
    2946             : 
    2947             : GEN
    2948         322 : gauss(GEN a, GEN b)
    2949             : {
    2950             :   GEN z;
    2951         322 :   if (typ(a)!=t_MAT) pari_err_TYPE("gauss",a);
    2952         406 :   if (RgM_is_ZM(a) && b &&
    2953         133 :       ((typ(b) == t_COL && RgV_is_ZV(b)) || (typ(b) == t_MAT && RgM_is_ZM(b))))
    2954          84 :     z = ZM_gauss(a,b);
    2955             :   else
    2956         238 :     z = RgM_solve(a,b);
    2957         322 :   if (!z) pari_err_INV("gauss",a);
    2958         245 :   return z;
    2959             : }
    2960             : 
    2961             : static GEN
    2962       51146 : F2_get_col(GEN b, GEN d, long li, long aco)
    2963             : {
    2964       51146 :   long i, l = nbits2lg(aco);
    2965       51146 :   GEN u = cgetg(l, t_VECSMALL);
    2966       51146 :   u[1] = aco;
    2967      969886 :   for (i = 1; i <= li; i++)
    2968      918740 :     if (d[i]) /* d[i] can still be 0 if li > aco */
    2969             :     {
    2970      918705 :       if (F2v_coeff(b, i))
    2971      323561 :         F2v_set(u, d[i]);
    2972             :       else
    2973      595144 :         F2v_clear(u, d[i]);
    2974             :     }
    2975       51146 :   return u;
    2976             : }
    2977             : 
    2978             : /* destroy a, b */
    2979             : static GEN
    2980        9275 : F2m_gauss_sp(GEN a, GEN b)
    2981             : {
    2982        9275 :   long i, j, k, l, li, bco, aco = lg(a)-1;
    2983             :   GEN u, d;
    2984             : 
    2985        9275 :   if (!aco) return cgetg(1,t_MAT);
    2986        9275 :   li = gel(a,1)[1];
    2987        9275 :   d = zero_Flv(li);
    2988        9275 :   bco = lg(b)-1;
    2989       60435 :   for (i=1; i<=aco; i++)
    2990             :   {
    2991       51174 :     GEN ai = vecsmall_copy(gel(a,i));
    2992       51174 :     if (!d[i] && F2v_coeff(ai, i))
    2993       27453 :       k = i;
    2994             :     else
    2995       23721 :       for (k = 1; k <= li; k++) if (!d[k] && F2v_coeff(ai,k)) break;
    2996             :     /* found a pivot on row k */
    2997       51174 :     if (k > li) return NULL;
    2998       51160 :     d[k] = i;
    2999             : 
    3000             :     /* Clear k-th row but column-wise instead of line-wise */
    3001             :     /*  a_ij -= a_i1*a1j/a_11
    3002             :        line-wise grouping:  L_j -= a_1j/a_11*L_1
    3003             :        column-wise:         C_i -= a_i1/a_11*C_1
    3004             :     */
    3005       51160 :     F2v_clear(ai,k);
    3006      969907 :     for (l=1; l<=aco; l++)
    3007             :     {
    3008      918747 :       GEN al = gel(a,l);
    3009      918747 :       if (F2v_coeff(al,k)) F2v_add_inplace(al,ai);
    3010             :     }
    3011      969872 :     for (l=1; l<=bco; l++)
    3012             :     {
    3013      918712 :       GEN bl = gel(b,l);
    3014      918712 :       if (F2v_coeff(bl,k)) F2v_add_inplace(bl,ai);
    3015             :     }
    3016             :   }
    3017        9261 :   u = cgetg(bco+1,t_MAT);
    3018        9261 :   for (j = 1; j <= bco; j++) gel(u,j) = F2_get_col(gel(b,j), d, li, aco);
    3019        9261 :   return u;
    3020             : }
    3021             : 
    3022             : GEN
    3023          28 : F2m_gauss(GEN a, GEN b)
    3024             : {
    3025          28 :   pari_sp av = avma;
    3026          28 :   if (lg(a) == 1) return cgetg(1,t_MAT);
    3027          28 :   return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), F2m_copy(b)));
    3028             : }
    3029             : GEN
    3030          14 : F2m_F2c_gauss(GEN a, GEN b)
    3031             : {
    3032          14 :   pari_sp av = avma;
    3033          14 :   GEN z = F2m_gauss(a, mkmat(b));
    3034          14 :   if (!z) { avma = av; return NULL; }
    3035           7 :   if (lg(z) == 1) { avma = av; return cgetg(1,t_VECSMALL); }
    3036           7 :   return gerepileuptoleaf(av, gel(z,1));
    3037             : }
    3038             : 
    3039             : GEN
    3040          35 : F2m_inv(GEN a)
    3041             : {
    3042          35 :   pari_sp av = avma;
    3043          35 :   if (lg(a) == 1) return cgetg(1,t_MAT);
    3044          35 :   return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), matid_F2m(gel(a,1)[1])));
    3045             : }
    3046             : 
    3047             : /* destroy a, b */
    3048             : static GEN
    3049       35065 : Flm_gauss_sp_OK(GEN a, GEN b, ulong *detp, ulong p)
    3050             : {
    3051       35065 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3052       35065 :   ulong det = 1;
    3053             :   GEN u;
    3054             : 
    3055       35065 :   li = nbrows(a);
    3056       35065 :   bco = lg(b)-1;
    3057      290741 :   for (i=1; i<=aco; i++)
    3058             :   {
    3059             :     ulong invpiv;
    3060             :     /* Fl_get_col wants 0 <= a[i,j] < p for all i,j */
    3061      290741 :     for (k = 1; k < i; k++) ucoeff(a,k,i) %= p;
    3062      578072 :     for (k = i; k <= li; k++)
    3063             :     {
    3064      578065 :       ulong piv = ( ucoeff(a,k,i) %= p );
    3065      578065 :       if (piv)
    3066             :       {
    3067      290734 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3068      290734 :         if (detp)
    3069             :         {
    3070           0 :           if (det & HIGHMASK) det %= p;
    3071           0 :           det *= piv;
    3072             :         }
    3073      290734 :         break;
    3074             :       }
    3075             :     }
    3076             :     /* found a pivot on line k */
    3077      290741 :     if (k > li) return NULL;
    3078      290734 :     if (k != i)
    3079             :     { /* swap lines so that k = i */
    3080       96567 :       s = -s;
    3081       96567 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3082       96567 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3083             :     }
    3084      290734 :     if (i == aco) break;
    3085             : 
    3086      255676 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3087     2160221 :     for (k=i+1; k<=li; k++)
    3088             :     {
    3089     1904545 :       ulong m = ( ucoeff(a,k,i) %= p );
    3090     1904545 :       if (!m) continue;
    3091             : 
    3092      537169 :       m = Fl_mul(m, invpiv, p);
    3093      537169 :       if (m == 1) {
    3094       93488 :         for (j=i+1; j<=aco; j++) _Fl_add_OK(gel(a,j),k,i, p);
    3095       93488 :         for (j=1;   j<=bco; j++) _Fl_add_OK(gel(b,j),k,i, p);
    3096             :       } else {
    3097      443681 :         for (j=i+1; j<=aco; j++) _Fl_addmul_OK(gel(a,j),k,i,m, p);
    3098      443681 :         for (j=1;   j<=bco; j++) _Fl_addmul_OK(gel(b,j),k,i,m, p);
    3099             :       }
    3100             :     }
    3101             :   }
    3102       35058 :   if (detp)
    3103             :   {
    3104           0 :     det %=  p;
    3105           0 :     if (s < 0 && det) det = p - det;
    3106           0 :     *detp = det;
    3107             :   }
    3108       35058 :   u = cgetg(bco+1,t_MAT);
    3109       35058 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col_OK(a,gel(b,j), aco,p);
    3110       35058 :   return u;
    3111             : }
    3112             : 
    3113             : /* destroy a, b */
    3114             : static GEN
    3115      580355 : Flm_gauss_sp(GEN a, GEN b, ulong *detp, ulong p)
    3116             : {
    3117      580355 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3118      580355 :   ulong det = 1;
    3119             :   GEN u;
    3120             :   ulong pi;
    3121      580355 :   if (!aco) { if (detp) *detp = 1; return cgetg(1,t_MAT); }
    3122      580355 :   if (SMALL_ULONG(p)) return Flm_gauss_sp_OK(a, b, detp, p);
    3123      545290 :   pi = get_Fl_red(p);
    3124      545290 :   li = nbrows(a);
    3125      545290 :   bco = lg(b)-1;
    3126     1860386 :   for (i=1; i<=aco; i++)
    3127             :   {
    3128             :     ulong invpiv;
    3129             :     /* Fl_get_col wants 0 <= a[i,j] < p for all i,j */
    3130     2228795 :     for (k = i; k <= li; k++)
    3131             :     {
    3132     2228795 :       ulong piv = ucoeff(a,k,i);
    3133     2228795 :       if (piv)
    3134             :       {
    3135     1860386 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3136     1860386 :         if (detp) det = Fl_mul_pre(det, piv, p, pi);
    3137     1860386 :         break;
    3138             :       }
    3139             :     }
    3140             :     /* found a pivot on line k */
    3141     1860386 :     if (k > li) return NULL;
    3142     1860386 :     if (k != i)
    3143             :     { /* swap lines so that k = i */
    3144      202193 :       s = -s;
    3145      202193 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3146      202193 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3147             :     }
    3148     1860386 :     if (i == aco) break;
    3149             : 
    3150     1315096 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3151     4248540 :     for (k=i+1; k<=li; k++)
    3152             :     {
    3153     2933444 :       ulong m = ucoeff(a,k,i);
    3154     2933444 :       if (!m) continue;
    3155             : 
    3156     1268003 :       m = Fl_mul_pre(m, invpiv, p, pi);
    3157     1268003 :       if (m == 1) {
    3158       49947 :         for (j=i+1; j<=aco; j++) _Fl_add(gel(a,j),k,i, p);
    3159       49947 :         for (j=1;   j<=bco; j++) _Fl_add(gel(b,j),k,i, p);
    3160             :       } else {
    3161     1218056 :         for (j=i+1; j<=aco; j++) _Fl_addmul(gel(a,j),k,i,m, p, pi);
    3162     1218056 :         for (j=1;   j<=bco; j++) _Fl_addmul(gel(b,j),k,i,m, p, pi);
    3163             :       }
    3164             :     }
    3165             :   }
    3166      545290 :   if (detp)
    3167             :   {
    3168           0 :     if (s < 0 && det) det = p - det;
    3169           0 :     *detp = det;
    3170             :   }
    3171      545290 :   u = cgetg(bco+1,t_MAT);
    3172      545290 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col(a,gel(b,j), aco,p);
    3173      545290 :   return u;
    3174             : }
    3175             : 
    3176             : static GEN
    3177      107177 : Flm_gauss_CUP(GEN a, GEN b, ulong *detp, ulong p) {
    3178             :   GEN R, C, U, P, X, Y;
    3179      107177 :   long i, n = lg(a) - 1, r;
    3180      107177 :   if (nbrows(a) < n || (r = Flm_CUP(a, &R, &C, &U, &P, p)) < n)
    3181          14 :     return NULL;
    3182      107163 :   Y = Flm_rsolve_lower_unit(rowpermute(C, R),
    3183             :                             rowpermute(b, R), p);
    3184      107163 :   X = rowpermute(Flm_rsolve_upper(U, Y, p),
    3185             :                  perm_inv(P));
    3186      107163 :   if (detp) {
    3187           0 :     ulong d = perm_sign(P) == 1? 1: p-1;
    3188           0 :     for (i = 1; i <= r; i++)
    3189           0 :       d = Fl_mul(d, ucoeff(U, i, i), p);
    3190           0 :     *detp = d;
    3191             :   }
    3192      107163 :   return X;
    3193             : }
    3194             : 
    3195             : GEN
    3196          56 : Flm_gauss(GEN a, GEN b, ulong p) {
    3197          56 :   pari_sp av = avma;
    3198             :   GEN x;
    3199          56 :   if (lg(a) - 1 >= Flm_CUP_LIMIT)
    3200          14 :     x = Flm_gauss_CUP(a, b, NULL, p);
    3201             :   else {
    3202          42 :     a = RgM_shallowcopy(a);
    3203          42 :     b = RgM_shallowcopy(b);
    3204          42 :     x = Flm_gauss_sp(a, b, NULL, p);
    3205             :   }
    3206          56 :   if (!x) { avma = av; return NULL; }
    3207          42 :   return gerepileupto(av, x);
    3208             : }
    3209             : 
    3210             : static GEN
    3211      657631 : Flm_inv_i(GEN a, ulong *detp, ulong p, long inplace) {
    3212      657631 :   pari_sp av = avma;
    3213      657631 :   long n = lg(a) - 1;
    3214             :   GEN b, x;
    3215      657631 :   if (n == 0) return cgetg(1, t_MAT);
    3216      657631 :   b = matid_Flm(nbrows(a));
    3217      657631 :   if (n >= Flm_CUP_LIMIT)
    3218      107163 :     x = Flm_gauss_CUP(a, b, detp, p);
    3219             :   else {
    3220      550468 :     if (!inplace)
    3221        5201 :       a = RgM_shallowcopy(a);
    3222      550468 :     x = Flm_gauss_sp(a, b, detp, p);
    3223             :   }
    3224      657631 :   if (!x) { avma = av; return NULL; }
    3225      657624 :   return gerepileupto(av, x);
    3226             : }
    3227             : 
    3228             : static GEN
    3229      643435 : Flm_inv_sp(GEN a, ulong *detp, ulong p) {
    3230      643435 :   return Flm_inv_i(a, detp, p, 1);
    3231             : }
    3232             : 
    3233             : GEN
    3234       14196 : Flm_inv(GEN a, ulong p) {
    3235       14196 :   return Flm_inv_i(a, NULL, p, 0);
    3236             : }
    3237             : 
    3238             : GEN
    3239          21 : Flm_Flc_gauss(GEN a, GEN b, ulong p) {
    3240          21 :   pari_sp av = avma;
    3241          21 :   GEN z = Flm_gauss(a, mkmat(b), p);
    3242          21 :   if (!z) { avma = av; return NULL; }
    3243          14 :   if (lg(z) == 1) { avma = av; return cgetg(1,t_VECSMALL); }
    3244          14 :   return gerepileuptoleaf(av, gel(z,1));
    3245             : }
    3246             : 
    3247             : static GEN
    3248        2807 : FpM_gauss_gen(GEN a, GEN b, GEN p)
    3249             : {
    3250             :   void *E;
    3251        2807 :   const struct bb_field *S = get_Fp_field(&E,p);
    3252        2807 :   return gen_Gauss(a,b, E, S);
    3253             : }
    3254             : /* a an FpM, lg(a)>1; b an FpM or NULL (replace by identity) */
    3255             : static GEN
    3256       41864 : FpM_gauss_i(GEN a, GEN b, GEN p, ulong *pp)
    3257             : {
    3258       41864 :   long n = nbrows(a);
    3259       41864 :   a = FpM_init(a,p,pp);
    3260       41864 :   switch(*pp)
    3261             :   {
    3262             :   case 0:
    3263        2807 :     if (!b) b = matid(n);
    3264        2807 :     return FpM_gauss_gen(a,b,p);
    3265             :   case 2:
    3266        9212 :     if (b) b = ZM_to_F2m(b); else b = matid_F2m(n);
    3267        9212 :     return F2m_gauss_sp(a,b);
    3268             :   default:
    3269       29845 :     if (b) b = ZM_to_Flm(b, *pp); else b = matid_Flm(n);
    3270       29845 :     return Flm_gauss_sp(a,b, NULL, *pp);
    3271             :   }
    3272             : }
    3273             : GEN
    3274          14 : FpM_gauss(GEN a, GEN b, GEN p)
    3275             : {
    3276          14 :   pari_sp av = avma;
    3277             :   ulong pp;
    3278             :   GEN u;
    3279          14 :   if (lg(a) == 1 || lg(b)==1) return cgetg(1, t_MAT);
    3280          14 :   u = FpM_gauss_i(a, b, p, &pp);
    3281          14 :   if (!u) { avma = av; return NULL; }
    3282          14 :   switch(pp)
    3283             :   {
    3284          14 :   case 0: return gerepilecopy(av, u);
    3285           0 :   case 2:  u = F2m_to_ZM(u); break;
    3286           0 :   default: u = Flm_to_ZM(u); break;
    3287             :   }
    3288           0 :   return gerepileupto(av, u);
    3289             : }
    3290             : GEN
    3291       41836 : FpM_inv(GEN a, GEN p)
    3292             : {
    3293       41836 :   pari_sp av = avma;
    3294             :   ulong pp;
    3295             :   GEN u;
    3296       41836 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    3297       41836 :   u = FpM_gauss_i(a, NULL, p, &pp);
    3298       41836 :   if (!u) { avma = av; return NULL; }
    3299       41836 :   switch(pp)
    3300             :   {
    3301        2779 :   case 0: return gerepilecopy(av, u);
    3302        9212 :   case 2:  u = F2m_to_ZM(u); break;
    3303       29845 :   default: u = Flm_to_ZM(u); break;
    3304             :   }
    3305       39057 :   return gerepileupto(av, u);
    3306             : }
    3307             : 
    3308             : GEN
    3309          14 : FpM_FpC_gauss(GEN a, GEN b, GEN p)
    3310             : {
    3311          14 :   pari_sp av = avma;
    3312             :   ulong pp;
    3313             :   GEN u;
    3314          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3315          14 :   u = FpM_gauss_i(a, mkmat(b), p, &pp);
    3316          14 :   if (!u) { avma = av; return NULL; }
    3317          14 :   switch(pp)
    3318             :   {
    3319          14 :   case 0: return gerepilecopy(av, gel(u,1));
    3320           0 :   case 2:  u = F2c_to_ZC(gel(u,1)); break;
    3321           0 :   default: u = Flc_to_ZC(gel(u,1)); break;
    3322             :   }
    3323           0 :   return gerepileupto(av, u);
    3324             : }
    3325             : 
    3326             : static GEN
    3327          56 : FlxqM_gauss_gen(GEN a, GEN b, GEN T, ulong p)
    3328             : {
    3329             :   void *E;
    3330          56 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    3331          56 :   return gen_Gauss(a, b, E, S);
    3332             : }
    3333             : 
    3334             : static GEN
    3335          35 : FlxqM_gauss_CUP(GEN a, GEN b, GEN T, ulong p) {
    3336             :   GEN R, C, U, P, Y;
    3337          35 :   long n = lg(a) - 1, r;
    3338          35 :   if (nbrows(a) < n || (r = FlxqM_CUP(a, &R, &C, &U, &P, T, p)) < n)
    3339          14 :     return NULL;
    3340          21 :   Y = FlxqM_rsolve_lower_unit(rowpermute(C, R),
    3341             :                               rowpermute(b, R), T, p);
    3342          21 :   return rowpermute(FlxqM_rsolve_upper(U, Y, T, p),
    3343             :                     perm_inv(P));
    3344             : }
    3345             : 
    3346             : static GEN
    3347          91 : FlxqM_gauss_i(GEN a, GEN b, GEN T, ulong p) {
    3348          91 :   if (lg(a) - 1 >= FlxqM_CUP_LIMIT)
    3349          35 :     return FlxqM_gauss_CUP(a, b, T, p);
    3350          56 :   return FlxqM_gauss_gen(a, b, T, p);
    3351             : }
    3352             : 
    3353             : GEN
    3354          21 : FlxqM_gauss(GEN a, GEN b, GEN T, ulong p)
    3355             : {
    3356          21 :   pari_sp av = avma;
    3357          21 :   long n = lg(a)-1;
    3358             :   GEN u;
    3359          21 :   if (!n || lg(b)==1) { avma = av; return cgetg(1, t_MAT); }
    3360          21 :   u = FlxqM_gauss_i(a, b, T, p);
    3361          21 :   if (!u) { avma = av; return NULL; }
    3362          14 :   return gerepilecopy(av, u);
    3363             : }
    3364             : GEN
    3365          56 : FlxqM_inv(GEN a, GEN T, ulong p)
    3366             : {
    3367          56 :   pari_sp av = avma;
    3368             :   GEN u;
    3369          56 :   if (lg(a) == 1) { avma = av; return cgetg(1, t_MAT); }
    3370          56 :   u = FlxqM_gauss_i(a, matid_FlxqM(nbrows(a),T,p), T,p);
    3371          56 :   if (!u) { avma = av; return NULL; }
    3372          42 :   return gerepilecopy(av, u);
    3373             : }
    3374             : GEN
    3375          14 : FlxqM_FlxqC_gauss(GEN a, GEN b, GEN T, ulong p)
    3376             : {
    3377          14 :   pari_sp av = avma;
    3378             :   GEN u;
    3379          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3380          14 :   u = FlxqM_gauss_i(a, mkmat(b), T, p);
    3381          14 :   if (!u) { avma = av; return NULL; }
    3382           7 :   return gerepilecopy(av, gel(u,1));
    3383             : }
    3384             : 
    3385             : static GEN
    3386          56 : FqM_gauss_gen(GEN a, GEN b, GEN T, GEN p)
    3387             : {
    3388             :   void *E;
    3389          56 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    3390          56 :   return gen_Gauss(a,b,E,S);
    3391             : }
    3392             : GEN
    3393           7 : FqM_gauss(GEN a, GEN b, GEN T, GEN p)
    3394             : {
    3395           7 :   pari_sp av = avma;
    3396             :   GEN u;
    3397             :   long n;
    3398           7 :   if (!T) return FpM_gauss(a,b,p);
    3399           7 :   n = lg(a)-1; if (!n || lg(b)==1) return cgetg(1, t_MAT);
    3400           7 :   u = FqM_gauss_gen(a,b,T,p);
    3401           7 :   if (!u) { avma = av; return NULL; }
    3402           7 :   return gerepilecopy(av, u);
    3403             : }
    3404             : GEN
    3405          35 : FqM_inv(GEN a, GEN T, GEN p)
    3406             : {
    3407          35 :   pari_sp av = avma;
    3408             :   GEN u;
    3409          35 :   if (!T) return FpM_inv(a,p);
    3410          35 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    3411          35 :   u = FqM_gauss_gen(a,matid(nbrows(a)),T,p);
    3412          35 :   if (!u) { avma = av; return NULL; }
    3413          28 :   return gerepilecopy(av, u);
    3414             : }
    3415             : GEN
    3416          14 : FqM_FqC_gauss(GEN a, GEN b, GEN T, GEN p)
    3417             : {
    3418          14 :   pari_sp av = avma;
    3419             :   GEN u;
    3420          14 :   if (!T) return FpM_FpC_gauss(a,b,p);
    3421          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3422          14 :   u = FqM_gauss_gen(a,mkmat(b),T,p);
    3423          14 :   if (!u) { avma = av; return NULL; }
    3424           7 :   return gerepilecopy(av, gel(u,1));
    3425             : }
    3426             : 
    3427             : /* Dixon p-adic lifting algorithm.
    3428             :  * Numer. Math. 40, 137-141 (1982), DOI: 10.1007/BF01459082 */
    3429             : GEN
    3430      426674 : ZM_gauss(GEN a, GEN b0)
    3431             : {
    3432      426674 :   pari_sp av = avma, av2;
    3433             :   int iscol;
    3434             :   long n, ncol, i, m, elim;
    3435             :   ulong p;
    3436      426674 :   GEN N, C, delta, xb, nb, nmin, res, b = b0;
    3437             :   forprime_t S;
    3438             : 
    3439      426674 :   if (!init_gauss(a, &b, &n, &ncol, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    3440      426604 :   nb = gen_0; ncol = lg(b);
    3441      863343 :   for (i = 1; i < ncol; i++)
    3442             :   {
    3443      436739 :     GEN ni = gnorml2(gel(b, i));
    3444      436739 :     if (cmpii(nb, ni) < 0) nb = ni;
    3445             :   }
    3446      426604 :   if (!signe(nb)) { avma = av; return gcopy(b0); }
    3447      426604 :   delta = gen_1; nmin = nb;
    3448     1729474 :   for (i = 1; i <= n; i++)
    3449             :   {
    3450     1302870 :     GEN ni = gnorml2(gel(a, i));
    3451     1302870 :     if (cmpii(ni, nmin) < 0)
    3452             :     {
    3453        1859 :       delta = mulii(delta, nmin); nmin = ni;
    3454             :     }
    3455             :     else
    3456     1301011 :       delta = mulii(delta, ni);
    3457             :   }
    3458      426604 :   if (!signe(nmin)) return NULL;
    3459      426590 :   elim = expi(delta)+1;
    3460      426590 :   av2 = avma;
    3461      426590 :   init_modular_big(&S);
    3462             :   for(;;)
    3463             :   {
    3464      426590 :     p = u_forprime_next(&S);
    3465      426590 :     C = Flm_inv_sp(ZM_to_Flm(a, p), NULL, p);
    3466      426590 :     if (C) break;
    3467           0 :     elim -= expu(p);
    3468           0 :     if (elim < 0) return NULL;
    3469           0 :     avma = av2;
    3470           0 :   }
    3471             :   /* N.B. Our delta/lambda are SQUARES of those in the paper
    3472             :    * log(delta lambda) / log p, where lambda is 3+sqrt(5) / 2,
    3473             :    * whose log is < 1, hence + 1 (to cater for rounding errors) */
    3474      853180 :   m = (long)ceil((rtodbl(logr_abs(itor(delta,LOWDEFAULTPREC))) + 1)
    3475      426590 :                  / log((double)p));
    3476      426590 :   xb = ZlM_gauss(a, b, p, m, C);
    3477      426590 :   N = powuu(p, m);
    3478      426590 :   delta = sqrti(delta);
    3479      426590 :   if (iscol)
    3480      423863 :     res = FpC_ratlift(gel(xb,1), N, delta,delta, NULL);
    3481             :   else
    3482        2727 :     res = FpM_ratlift(xb, N, delta,delta, NULL);
    3483      426590 :   return gerepileupto(av, res);
    3484             : }
    3485             : 
    3486             : /* M integral, dM such that M' = dM M^-1 is integral [e.g det(M)]. Return M' */
    3487             : GEN
    3488      102730 : ZM_inv(GEN M, GEN dM)
    3489             : {
    3490      102730 :   pari_sp av2, av = avma;
    3491             :   GEN Hp,q,H;
    3492             :   ulong p;
    3493      102730 :   long lM = lg(M), stable = 0;
    3494      102730 :   int negate = 0;
    3495             :   forprime_t S;
    3496             :   pari_timer ti;
    3497             : 
    3498      102730 :   if (lM == 1) return cgetg(1,t_MAT);
    3499             : 
    3500             :   /* HACK: include dM = -1 ! */
    3501      101750 :   if (dM && is_pm1(dM))
    3502             :   {
    3503             :     /* modular algorithm computes M^{-1}, NOT multiplied by det(M) = -1.
    3504             :      * We will correct (negate) at the end. */
    3505       90884 :     if (signe(dM) < 0) negate = 1;
    3506       90884 :     dM = gen_1;
    3507             :   }
    3508      101750 :   init_modular_big(&S);
    3509      101750 :   av2 = avma;
    3510      101750 :   H = NULL;
    3511      101750 :   if (DEBUGLEVEL>5) timer_start(&ti);
    3512      311769 :   while ((p = u_forprime_next(&S)))
    3513             :   {
    3514             :     ulong dMp;
    3515             :     GEN Mp;
    3516      210019 :     Mp = ZM_to_Flm(M,p);
    3517      210019 :     if (dM == gen_1)
    3518      184351 :       Hp = Flm_inv_sp(Mp, NULL, p);
    3519             :     else
    3520             :     {
    3521       25668 :       if (dM) {
    3522       25668 :         dMp = umodiu(dM,p); if (!dMp) continue;
    3523       25668 :         Hp = Flm_inv_sp(Mp, NULL, p);
    3524       25668 :         if (!Hp) pari_err_INV("ZM_inv", Mp);
    3525             :       } else {
    3526           0 :         Hp = Flm_inv_sp(Mp, &dMp, p);
    3527           0 :         if (!Hp) continue;
    3528             :       }
    3529       25668 :       if (dMp != 1) Flm_Fl_mul_inplace(Hp, dMp, p);
    3530             :     }
    3531             : 
    3532      210019 :     if (!H)
    3533             :     {
    3534      101750 :       H = ZM_init_CRT(Hp, p);
    3535      101750 :       q = utoipos(p);
    3536             :     }
    3537             :     else
    3538      108269 :       stable = ZM_incremental_CRT(&H, Hp, &q, p);
    3539      210019 :     if (DEBUGLEVEL>5) timer_printf(&ti, "ZM_inv mod %lu (stable=%ld)", p,stable);
    3540      210019 :     if (stable) {/* DONE ? */
    3541      101750 :       if (dM != gen_1)
    3542      112616 :       { if (ZM_isscalar(ZM_mul(M, H), dM)) break; }
    3543             :       else
    3544       90884 :       { if (ZM_isidentity(ZM_mul(M, H))) break; }
    3545             :     }
    3546             : 
    3547      108269 :     if (gc_needed(av,2))
    3548             :     {
    3549           5 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv");
    3550           5 :       gerepileall(av2, 2, &H, &q);
    3551             :     }
    3552             :   }
    3553      101750 :   if (!p) pari_err_OVERFLOW("ZM_inv [ran out of primes]");
    3554      101750 :   if (DEBUGLEVEL>5) err_printf("ZM_inv done\n");
    3555      101750 :   if (negate)
    3556           0 :     return gerepileupto(av, ZM_neg(H));
    3557             :   else
    3558      101750 :     return gerepilecopy(av, H);
    3559             : }
    3560             : 
    3561             : /* to be used when denom(M^(-1)) << det(M) and a sharp multiple is
    3562             :  * not available. Return H primitive such that M*H = den*Id */
    3563             : GEN
    3564        6713 : ZM_inv_ratlift(GEN M, GEN *pden)
    3565             : {
    3566        6713 :   pari_sp av2, av = avma;
    3567             :   GEN Hp, q, H;
    3568             :   ulong p;
    3569        6713 :   long lM = lg(M);
    3570             :   forprime_t S;
    3571        6713 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    3572             : 
    3573        6020 :   init_modular_big(&S);
    3574        6020 :   av2 = avma;
    3575        6020 :   H = NULL;
    3576       12804 :   while ((p = u_forprime_next(&S)))
    3577             :   {
    3578             :     GEN Mp, B, Hr;
    3579        6784 :     Mp = ZM_to_Flm(M,p);
    3580        6784 :     Hp = Flm_inv_sp(Mp, NULL, p);
    3581        6784 :     if (!Hp) continue;
    3582        6784 :     if (!H)
    3583             :     {
    3584        6020 :       H = ZM_init_CRT(Hp, p);
    3585        6020 :       q = utoipos(p);
    3586             :     }
    3587             :     else
    3588         764 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3589        6784 :     B = sqrti(shifti(q,-1));
    3590        6784 :     Hr = FpM_ratlift(H,q,B,B,NULL);
    3591        6784 :     if (DEBUGLEVEL>5) err_printf("ZM_inv mod %lu (ratlift=%ld)\n", p,!!Hr);
    3592        6784 :     if (Hr) {/* DONE ? */
    3593        6083 :       GEN Hl = Q_remove_denom(Hr, pden);
    3594        6083 :       if (*pden)
    3595        4676 :       { if (ZM_isscalar(ZM_mul(M, Hl), *pden)) { H = Hl; break; }}
    3596             :       else
    3597        1407 :       { if (ZM_isidentity(ZM_mul(M, Hl))) { H = Hl; *pden = gen_1; break; } }
    3598             :     }
    3599             : 
    3600         764 :     if (gc_needed(av,2))
    3601             :     {
    3602           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv_ratlift");
    3603           0 :       gerepileall(av2, 2, &H, &q);
    3604             :     }
    3605             :   }
    3606        6020 :   gerepileall(av, 2, &H, pden);
    3607        6020 :   return H;
    3608             : }
    3609             : 
    3610             : /* same as above, M rational */
    3611             : GEN
    3612         889 : QM_inv(GEN M, GEN dM)
    3613             : {
    3614         889 :   pari_sp av = avma;
    3615         889 :   GEN cM, pM = Q_primitive_part(M, &cM);
    3616         889 :   if (!cM) return ZM_inv(pM,dM);
    3617         889 :   return gerepileupto(av, ZM_inv(pM, gdiv(dM,cM)));
    3618             : }
    3619             : 
    3620             : GEN
    3621       47804 : ZM_ker(GEN M)
    3622             : {
    3623       47804 :   pari_sp av2, av = avma;
    3624             :   GEN q, H, D;
    3625             :   forprime_t S;
    3626       47804 :   av2 = avma;
    3627       47804 :   H = NULL; D = NULL;
    3628       47804 :   if (lg(M)==1) return cgetg(1, t_MAT);
    3629       47797 :   init_modular_big(&S);
    3630             :   for(;;)
    3631             :   {
    3632             :     GEN Kp, Hp, Dp, Mp, Hr, B;
    3633      108342 :     ulong p = u_forprime_next(&S);
    3634      108342 :     Mp = ZM_to_Flm(M, p);
    3635      108342 :     Kp = Flm_ker_sp(Mp, p, 2);
    3636      108342 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    3637      108342 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    3638      100434 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    3639             :     {
    3640       85477 :       H = ZM_init_CRT(Hp, p); D = Dp;
    3641       85477 :       q = utoipos(p);
    3642             :     }
    3643             :     else
    3644       14957 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3645      100434 :     B = sqrti(shifti(q,-1));
    3646      100434 :     Hr = FpM_ratlift(H, q, B, B, NULL);
    3647      100434 :     if (DEBUGLEVEL>5) err_printf("ZM_ker mod %lu (ratlift=%ld)\n", p,!!Hr);
    3648      100434 :     if (Hr) {/* DONE ? */
    3649       95900 :       GEN Hl = vec_Q_primpart(Q_remove_denom(Hr, NULL));
    3650       95900 :       GEN MH = ZM_mul(M, Hl);
    3651       95900 :       if (gequal0(MH)) { H = Hl;  break; }
    3652             :     }
    3653       52637 :     if (gc_needed(av,2))
    3654             :     {
    3655           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_ker");
    3656           0 :       gerepileall(av2, 3, &H, &D, &q);
    3657             :     }
    3658       60545 :   }
    3659       47797 :   return gerepilecopy(av, H);
    3660             : }
    3661             : 
    3662             : /* x a ZM. Return a multiple of the determinant of the lattice generated by
    3663             :  * the columns of x. From Algorithm 2.2.6 in GTM138 */
    3664             : GEN
    3665          42 : detint(GEN A)
    3666             : {
    3667          42 :   if (typ(A) != t_MAT) pari_err_TYPE("detint",A);
    3668          42 :   RgM_check_ZM(A, "detint");
    3669          42 :   return ZM_detmult(A);
    3670             : }
    3671             : GEN
    3672        9030 : ZM_detmult(GEN A)
    3673             : {
    3674        9030 :   pari_sp av1, av = avma;
    3675             :   GEN B, c, v, piv;
    3676        9030 :   long rg, i, j, k, m, n = lg(A) - 1;
    3677             : 
    3678        9030 :   if (!n) return gen_1;
    3679        9030 :   m = nbrows(A);
    3680        9030 :   if (n < m) return gen_0;
    3681        9009 :   c = zero_zv(m);
    3682        9009 :   av1 = avma;
    3683        9009 :   B = zeromatcopy(m,m);
    3684        9009 :   v = cgetg(m+1, t_COL);
    3685        9009 :   piv = gen_1; rg = 0;
    3686       56007 :   for (k=1; k<=n; k++)
    3687             :   {
    3688       56000 :     GEN pivprec = piv;
    3689       56000 :     long t = 0;
    3690      471933 :     for (i=1; i<=m; i++)
    3691             :     {
    3692      415933 :       pari_sp av2 = avma;
    3693             :       GEN vi;
    3694      415933 :       if (c[i]) continue;
    3695             : 
    3696      236019 :       vi = mulii(piv, gcoeff(A,i,k));
    3697     2158233 :       for (j=1; j<=m; j++)
    3698     1922214 :         if (c[j]) vi = addii(vi, mulii(gcoeff(B,j,i),gcoeff(A,j,k)));
    3699      236019 :       if (!t && signe(vi)) t = i;
    3700      236019 :       gel(v,i) = gerepileuptoint(av2, vi);
    3701             :     }
    3702       56000 :     if (!t) continue;
    3703             :     /* at this point c[t] = 0 */
    3704             : 
    3705       55972 :     if (++rg >= m) { /* full rank; mostly done */
    3706        9002 :       GEN det = gel(v,t); /* last on stack */
    3707        9002 :       if (++k > n)
    3708        8946 :         det = absi(det);
    3709             :       else
    3710             :       {
    3711             :         /* improve further; at this point c[i] is set for all i != t */
    3712          56 :         gcoeff(B,t,t) = piv; v = centermod(gel(B,t), det);
    3713         140 :         for ( ; k<=n; k++)
    3714          84 :           det = gcdii(det, ZV_dotproduct(v, gel(A,k)));
    3715             :       }
    3716        9002 :       return gerepileuptoint(av, det);
    3717             :     }
    3718             : 
    3719       46970 :     piv = gel(v,t);
    3720      406896 :     for (i=1; i<=m; i++)
    3721             :     {
    3722             :       GEN mvi;
    3723      359926 :       if (c[i] || i == t) continue;
    3724             : 
    3725      179963 :       gcoeff(B,t,i) = mvi = negi(gel(v,i));
    3726     1685838 :       for (j=1; j<=m; j++)
    3727     1505875 :         if (c[j]) /* implies j != t */
    3728             :         {
    3729      381983 :           pari_sp av2 = avma;
    3730      381983 :           GEN z = addii(mulii(gcoeff(B,j,i), piv), mulii(gcoeff(B,j,t), mvi));
    3731      381983 :           if (rg > 1) z = diviiexact(z, pivprec);
    3732      381983 :           gcoeff(B,j,i) = gerepileuptoint(av2, z);
    3733             :         }
    3734             :     }
    3735       46970 :     c[t] = k;
    3736       46970 :     if (gc_needed(av,1))
    3737             :     {
    3738           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"detint. k=%ld",k);
    3739           0 :       gerepileall(av1, 2, &piv,&B); v = zerovec(m);
    3740             :     }
    3741             :   }
    3742           7 :   avma = av; return gen_0;
    3743             : }
    3744             : 
    3745             : /* Reduce x modulo (invertible) y */
    3746             : GEN
    3747        6174 : closemodinvertible(GEN x, GEN y)
    3748             : {
    3749        6174 :   return gmul(y, ground(RgM_solve(y,x)));
    3750             : }
    3751             : GEN
    3752           7 : reducemodinvertible(GEN x, GEN y)
    3753             : {
    3754           7 :   return gsub(x, closemodinvertible(x,y));
    3755             : }
    3756             : GEN
    3757           0 : reducemodlll(GEN x,GEN y)
    3758             : {
    3759           0 :   return reducemodinvertible(x, ZM_lll(y, 0.75, LLL_INPLACE));
    3760             : }
    3761             : 
    3762             : /*******************************************************************/
    3763             : /*                                                                 */
    3764             : /*                    KERNEL of an m x n matrix                    */
    3765             : /*          return n - rk(x) linearly independent vectors          */
    3766             : /*                                                                 */
    3767             : /*******************************************************************/
    3768             : static GEN
    3769         126 : deplin_aux(GEN x0)
    3770             : {
    3771         126 :   pari_sp av = avma;
    3772         126 :   long i, j, k, nl, nc = lg(x0)-1;
    3773             :   GEN D, x, y, c, l, d, ck;
    3774             : 
    3775         126 :   if (!nc) { avma=av; return cgetg(1,t_COL); }
    3776          91 :   x = RgM_shallowcopy(x0);
    3777          91 :   nl = nbrows(x);
    3778          91 :   d = const_vec(nl, gen_1); /* pivot list */
    3779          91 :   c = zero_zv(nl);
    3780          91 :   l = cgetg(nc+1, t_VECSMALL); /* not initialized */
    3781          91 :   ck = NULL; /* gcc -Wall */
    3782         399 :   for (k=1; k<=nc; k++)
    3783             :   {
    3784         378 :     ck = gel(x,k);
    3785        1176 :     for (j=1; j<k; j++)
    3786             :     {
    3787         798 :       GEN cj = gel(x,j), piv = gel(d,j), q = gel(ck,l[j]);
    3788        9828 :       for (i=1; i<=nl; i++)
    3789        9030 :         if (i!=l[j]) gel(ck,i) = gsub(gmul(piv, gel(ck,i)), gmul(q, gel(cj,i)));
    3790             :     }
    3791             : 
    3792         378 :     i = gauss_get_pivot_NZ(x, NULL, k, c);
    3793         378 :     if (i > nl) break;
    3794             : 
    3795         308 :     gel(d,k) = gel(ck,i);
    3796         308 :     c[i] = k; l[k] = i; /* pivot d[k] in x[i,k] */
    3797             :   }
    3798          91 :   if (k > nc) { avma = av; return cgetg(1,t_COL); }
    3799          70 :   if (k == 1) { avma = av; return scalarcol_shallow(gen_1,nc); }
    3800          70 :   y = cgetg(nc+1,t_COL);
    3801          70 :   gel(y,1) = gcopy(gel(ck, l[1]));
    3802         203 :   for (D=gel(d,1),j=2; j<k; j++)
    3803             :   {
    3804         133 :     gel(y,j) = gmul(gel(ck, l[j]), D);
    3805         133 :     D = gmul(D, gel(d,j));
    3806             :   }
    3807          70 :   gel(y,j) = gneg(D);
    3808          70 :   for (j++; j<=nc; j++) gel(y,j) = gen_0;
    3809          70 :   y = primitive_part(y, &c);
    3810          70 :   return c? gerepileupto(av, y): gerepilecopy(av, y);
    3811             : }
    3812             : static GEN
    3813           0 : RgV_deplin(GEN v)
    3814             : {
    3815           0 :   pari_sp av = avma;
    3816           0 :   long n = lg(v)-1;
    3817           0 :   GEN y, p = NULL;
    3818           0 :   if (n <= 1)
    3819             :   {
    3820           0 :     if (n == 1 && gequal0(gel(v,1))) return mkcol(gen_1);
    3821           0 :     return cgetg(1, t_COL);
    3822             :   }
    3823           0 :   if (gequal0(gel(v,1))) return scalarcol_shallow(gen_1, n);
    3824           0 :   v = primpart(mkvec2(gel(v,1),gel(v,2)));
    3825           0 :   if (RgV_is_FpV(v, &p) && p) v = centerlift(v);
    3826           0 :   y = zerocol(n);
    3827           0 :   gel(y,1) = gneg(gel(v,2));
    3828           0 :   gel(y,2) = gcopy(gel(v,1));
    3829           0 :   return gerepileupto(av, y);
    3830             : 
    3831             : }
    3832             : GEN
    3833         273 : deplin(GEN x)
    3834             : {
    3835         273 :   GEN p = NULL, ff = NULL;
    3836         273 :   switch(typ(x))
    3837             :   {
    3838         273 :     case t_MAT: break;
    3839           0 :     case t_VEC: return RgV_deplin(x);
    3840           0 :     default: pari_err_TYPE("deplin",x);
    3841             :   }
    3842         273 :   if (RgM_is_FpM(x, &p) && p)
    3843             :   {
    3844          84 :     pari_sp av = avma;
    3845             :     ulong pp;
    3846          84 :     x = RgM_Fp_init(x, p, &pp);
    3847          84 :     switch(pp)
    3848             :     {
    3849             :     case 0:
    3850          14 :       x = FpM_ker_gen(x,p,1);
    3851          14 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3852           7 :       x = FpC_center(x,p,shifti(p,-1));
    3853           7 :       break;
    3854             :     case 2:
    3855          14 :       x = F2m_ker_sp(x,1);
    3856          14 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3857           7 :       x = F2c_to_ZC(x); break;
    3858             :     default:
    3859          56 :       x = Flm_ker_sp(x,pp,1);
    3860          56 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3861          35 :       x = Flv_center(x, pp, pp>>1);
    3862          35 :       x = zc_to_ZC(x);
    3863          35 :       break;
    3864             :     }
    3865          49 :     return gerepileupto(av, x);
    3866             :   }
    3867         189 :   if (RgM_is_FFM(x, &ff))
    3868             :   {
    3869          63 :     x = FFM_deplin(x, ff);
    3870          63 :     if (!x) return cgetg(1, t_COL);
    3871          35 :     return x;
    3872             :   }
    3873         126 :   return deplin_aux(x);
    3874             : }
    3875             : 
    3876             : /*******************************************************************/
    3877             : /*                                                                 */
    3878             : /*         GAUSS REDUCTION OF MATRICES  (m lines x n cols)         */
    3879             : /*           (kernel, image, complementary image, rank)            */
    3880             : /*                                                                 */
    3881             : /*******************************************************************/
    3882             : /* return the transform of x under a standard Gauss pivot.
    3883             :  * x0 is a reference point when guessing whether x[i,j] ~ 0
    3884             :  * (iff x[i,j] << x0[i,j])
    3885             :  * Set r = dim ker(x). d[k] contains the index of the first non-zero pivot
    3886             :  * in column k */
    3887             : static GEN
    3888        8050 : gauss_pivot_ker(GEN x, GEN x0, GEN *dd, long *rr)
    3889             : {
    3890             :   GEN c, d, p, data;
    3891             :   pari_sp av;
    3892             :   long i, j, k, r, t, n, m;
    3893             :   pivot_fun pivot;
    3894             : 
    3895        8050 :   n=lg(x)-1; if (!n) { *dd=NULL; *rr=0; return cgetg(1,t_MAT); }
    3896        8015 :   m=nbrows(x); r=0;
    3897        8015 :   pivot = get_pivot_fun(x, x0, &data);
    3898        8015 :   x = RgM_shallowcopy(x);
    3899        8015 :   c = zero_zv(m);
    3900        8015 :   d = cgetg(n+1,t_VECSMALL);
    3901        8015 :   av=avma;
    3902       48174 :   for (k=1; k<=n; k++)
    3903             :   {
    3904       40159 :     j = pivot(x, data, k, c);
    3905       40159 :     if (j > m)
    3906             :     {
    3907       13573 :       r++; d[k]=0;
    3908       74081 :       for(j=1; j<k; j++)
    3909       60508 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
    3910             :     }
    3911             :     else
    3912             :     { /* pivot for column k on row j */
    3913       26586 :       c[j]=k; d[k]=j; p = gdiv(gen_m1,gcoeff(x,j,k));
    3914       26586 :       gcoeff(x,j,k) = gen_m1;
    3915             :       /* x[j,] /= - x[j,k] */
    3916       26586 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3917     1816507 :       for (t=1; t<=m; t++)
    3918     1789921 :         if (t!=j)
    3919             :         { /* x[t,] -= 1 / x[j,k] x[j,] */
    3920     1763335 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3921    23484251 :           for (i=k+1; i<=n; i++)
    3922    21720916 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i),gmul(p,gcoeff(x,j,i)));
    3923     1763335 :           if (gc_needed(av,1)) gerepile_gauss_ker(x,k,t,av);
    3924             :         }
    3925             :     }
    3926             :   }
    3927        8015 :   *dd=d; *rr=r; return x;
    3928             : }
    3929             : 
    3930             : static GEN FpM_gauss_pivot(GEN x, GEN p, long *rr);
    3931             : static GEN FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr);
    3932             : static GEN F2m_gauss_pivot(GEN x, long *rr);
    3933             : 
    3934             : /* r = dim ker(x).
    3935             :  * Returns d:
    3936             :  *   d[k] != 0 contains the index of a non-zero pivot in column k
    3937             :  *   d[k] == 0 if column k is a linear combination of the (k-1) first ones */
    3938             : GEN
    3939       15255 : RgM_pivots(GEN x0, GEN data, long *rr, pivot_fun pivot)
    3940             : {
    3941             :   GEN x, c, d, p;
    3942       15255 :   long i, j, k, r, t, m, n = lg(x0)-1;
    3943             :   pari_sp av;
    3944             : 
    3945       15255 :   if (RgM_is_ZM(x0)) return ZM_pivots(x0, rr);
    3946       13267 :   if (!n) { *rr = 0; return NULL; }
    3947             : 
    3948       13267 :   d = cgetg(n+1, t_VECSMALL);
    3949       13267 :   x = RgM_shallowcopy(x0);
    3950       13267 :   m = nbrows(x); r = 0;
    3951       13267 :   c = zero_zv(m);
    3952       13267 :   av = avma;
    3953      869186 :   for (k=1; k<=n; k++)
    3954             :   {
    3955      855919 :     j = pivot(x, data, k, c);
    3956      855919 :     if (j > m) { r++; d[k] = 0; }
    3957             :     else
    3958             :     {
    3959       33543 :       c[j] = k; d[k] = j; p = gdiv(gen_m1, gcoeff(x,j,k));
    3960       33543 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3961             : 
    3962      258710 :       for (t=1; t<=m; t++)
    3963      225167 :         if (!c[t]) /* no pivot on that line yet */
    3964             :         {
    3965      119733 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3966     6555375 :           for (i=k+1; i<=n; i++)
    3967     6435642 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i), gmul(p, gcoeff(x,j,i)));
    3968      119733 :           if (gc_needed(av,1)) gerepile_gauss(x,k,t,av,j,c);
    3969             :         }
    3970       33543 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = gen_0; /* dummy */
    3971             :     }
    3972             :   }
    3973       13267 :   *rr = r; avma = (pari_sp)d; return d;
    3974             : }
    3975             : 
    3976             : static long
    3977       99481 : ZM_count_0_cols(GEN M)
    3978             : {
    3979       99481 :   long i, l = lg(M), n = 0;
    3980      503014 :   for (i = 1; i < l; i++)
    3981      403533 :     if (ZV_equal0(gel(M,i))) n++;
    3982       99481 :   return n;
    3983             : }
    3984             : 
    3985             : static void indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol);
    3986             : /* As RgM_pivots, integer entries. Set *rr = dim Ker M0 */
    3987             : GEN
    3988      102701 : ZM_pivots(GEN M0, long *rr)
    3989             : {
    3990      102701 :   GEN d, dbest = NULL;
    3991             :   long m, n, i, imax, rmin, rbest, zc;
    3992      102701 :   int beenthere = 0;
    3993      102701 :   pari_sp av, av0 = avma;
    3994             :   forprime_t S;
    3995             : 
    3996      102701 :   rbest = n = lg(M0)-1;
    3997      102701 :   if (n == 0) { *rr = 0; return NULL; }
    3998       99481 :   zc = ZM_count_0_cols(M0);
    3999       99481 :   if (n == zc) { *rr = zc; return zero_zv(n); }
    4000             : 
    4001       99180 :   m = nbrows(M0);
    4002       99180 :   rmin = maxss(zc, n-m);
    4003       99180 :   init_modular(&S);
    4004       99180 :   imax = (n < (1<<4))? 1: (n>>3); /* heuristic */
    4005             : 
    4006             :   for(;;)
    4007             :   {
    4008             :     GEN row, col, M, KM, IM, RHS, X, cX;
    4009             :     long rk;
    4010      103020 :     for (av = avma, i = 0;; avma = av, i++)
    4011             :     {
    4012      103020 :       ulong p = u_forprime_next(&S);
    4013             :       long rp;
    4014      103020 :       if (!p) pari_err_OVERFLOW("ZM_pivots [ran out of primes]");
    4015      103020 :       d = Flm_pivots(ZM_to_Flm(M0, p), p, &rp, 1);
    4016      103020 :       if (rp == rmin) { rbest = rp; goto END; } /* maximal rank, return */
    4017        6567 :       if (rp < rbest) { /* save best r so far */
    4018        2734 :         rbest = rp;
    4019        2734 :         if (dbest) gunclone(dbest);
    4020        2734 :         dbest = gclone(d);
    4021        5461 :         if (beenthere) break;
    4022             :       }
    4023        6567 :       if (!beenthere && i >= imax) break;
    4024        3840 :     }
    4025        2727 :     beenthere = 1;
    4026             :     /* Dubious case: there is (probably) a non trivial kernel */
    4027        2727 :     indexrank_all(m,n, rbest, dbest, &row, &col);
    4028        2727 :     M = rowpermute(vecpermute(M0, col), row);
    4029        2727 :     rk = n - rbest; /* (probable) dimension of image */
    4030        2727 :     IM = vecslice(M,1,rk);
    4031        2727 :     KM = vecslice(M,rk+1, n);
    4032        2727 :     M = rowslice(IM, 1,rk); /* square maximal rank */
    4033        2727 :     X = ZM_gauss(M, rowslice(KM, 1,rk));
    4034        2727 :     X = Q_remove_denom(X, &cX);
    4035        2727 :     RHS = rowslice(KM,rk+1,m);
    4036        2727 :     if (cX) RHS = ZM_Z_mul(RHS, cX);
    4037        2727 :     if (ZM_equal(ZM_mul(rowslice(IM,rk+1,m), X), RHS))
    4038             :     {
    4039        2727 :       d = vecsmall_copy(dbest);
    4040        2727 :       goto END;
    4041             :     }
    4042           0 :     avma = av;
    4043           0 :   }
    4044             : END:
    4045       99180 :   *rr = rbest; if (dbest) gunclone(dbest);
    4046       99180 :   return gerepileuptoleaf(av0, d);
    4047             : }
    4048             : 
    4049             : /* set *pr = dim Ker x */
    4050             : static GEN
    4051        7175 : gauss_pivot(GEN x, long *pr) {
    4052             :   GEN data;
    4053        7175 :   pivot_fun pivot = get_pivot_fun(x, x, &data);
    4054        7175 :   return RgM_pivots(x, data, pr, pivot);
    4055             : }
    4056             : 
    4057             : /* compute ker(x), x0 is a reference point when guessing whether x[i,j] ~ 0
    4058             :  * (iff x[i,j] << x0[i,j]) */
    4059             : static GEN
    4060        8050 : ker_aux(GEN x, GEN x0)
    4061             : {
    4062        8050 :   pari_sp av = avma;
    4063             :   GEN d,y;
    4064             :   long i,j,k,r,n;
    4065             : 
    4066        8050 :   x = gauss_pivot_ker(x,x0,&d,&r);
    4067        8050 :   if (!r) { avma=av; return cgetg(1,t_MAT); }
    4068        7693 :   n = lg(x)-1; y=cgetg(r+1,t_MAT);
    4069       21266 :   for (j=k=1; j<=r; j++,k++)
    4070             :   {
    4071       13573 :     GEN p = cgetg(n+1,t_COL);
    4072             : 
    4073       13573 :     gel(y,j) = p; while (d[k]) k++;
    4074       74081 :     for (i=1; i<k; i++)
    4075       60508 :       if (d[i])
    4076             :       {
    4077       31073 :         GEN p1=gcoeff(x,d[i],k);
    4078       31073 :         gel(p,i) = gcopy(p1); gunclone(p1);
    4079             :       }
    4080             :       else
    4081       29435 :         gel(p,i) = gen_0;
    4082       13573 :     gel(p,k) = gen_1; for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
    4083             :   }
    4084        7693 :   return gerepileupto(av,y);
    4085             : }
    4086             : GEN
    4087        8008 : ker(GEN x)
    4088             : {
    4089        8008 :   pari_sp av = avma;
    4090        8008 :   GEN p = NULL, ff = NULL;
    4091        8008 :   if (RgM_is_FpM(x, &p) && p)
    4092             :   {
    4093             :     ulong pp;
    4094          56 :     x = RgM_Fp_init(x, p, &pp);
    4095          56 :     switch(pp)
    4096             :     {
    4097          14 :     case 0: x = FpM_to_mod(FpM_ker_gen(x,p,0),p); break;
    4098           7 :     case 2: x = F2m_to_mod(F2m_ker_sp(x,0)); break;
    4099          35 :     default:x = Flm_to_mod(Flm_ker_sp(x,pp,0), pp); break;
    4100             :     }
    4101          56 :     return gerepileupto(av, x);
    4102             :   }
    4103        7952 :   if (RgM_is_FFM(x, &ff)) return FFM_ker(x, ff);
    4104        7896 :   return ker_aux(x,x);
    4105             : }
    4106             : GEN
    4107       46116 : matker0(GEN x,long flag)
    4108             : {
    4109       46116 :   if (typ(x)!=t_MAT) pari_err_TYPE("matker",x);
    4110       46116 :   if (!flag) return ker(x);
    4111       45934 :   RgM_check_ZM(x, "matker");
    4112       45934 :   return ZM_ker(x);
    4113             : }
    4114             : 
    4115             : GEN
    4116        2954 : image(GEN x)
    4117             : {
    4118        2954 :   pari_sp av = avma;
    4119        2954 :   GEN d, ff = NULL, p = NULL;
    4120             :   long r;
    4121             : 
    4122        2954 :   if (typ(x)!=t_MAT) pari_err_TYPE("matimage",x);
    4123        2954 :   if (RgM_is_FpM(x, &p) && p)
    4124             :   {
    4125             :     ulong pp;
    4126          49 :     x = RgM_Fp_init(x, p, &pp);
    4127          49 :     switch(pp)
    4128             :     {
    4129          14 :     case 0: x = FpM_to_mod(FpM_image(x,p), p); break;
    4130           7 :     case 2: x = F2m_to_mod(F2m_image(x)); break;
    4131          28 :     default:x = Flm_to_mod(Flm_image(x,pp), pp);
    4132             :     }
    4133          49 :     return gerepileupto(av, x);
    4134             :   }
    4135        2905 :   if (RgM_is_FFM(x, &ff)) return FFM_image(x, ff);
    4136        2870 :   d = gauss_pivot(x,&r); /* d left on stack for efficiency */
    4137        2870 :   return image_from_pivot(x,d,r);
    4138             : }
    4139             : 
    4140             : static GEN
    4141          84 : imagecompl_aux(GEN x, GEN(*PIVOT)(GEN,long*))
    4142             : {
    4143          84 :   pari_sp av = avma;
    4144             :   GEN d,y;
    4145             :   long j,i,r;
    4146             : 
    4147          84 :   if (typ(x)!=t_MAT) pari_err_TYPE("imagecompl",x);
    4148          84 :   (void)new_chunk(lg(x) * 4 + 1); /* HACK */
    4149          84 :   d = PIVOT(x,&r); /* if (!d) then r = 0 */
    4150          84 :   avma = av; y = cgetg(r+1,t_VECSMALL);
    4151         126 :   for (i=j=1; j<=r; i++)
    4152          42 :     if (!d[i]) y[j++] = i;
    4153          84 :   return y;
    4154             : }
    4155             : GEN
    4156          84 : imagecompl(GEN x) { return imagecompl_aux(x, &gauss_pivot); }
    4157             : GEN
    4158           0 : ZM_imagecompl(GEN x) { return imagecompl_aux(x, &ZM_pivots); }
    4159             : 
    4160             : GEN
    4161        1456 : RgM_RgC_invimage(GEN A, GEN y)
    4162             : {
    4163        1456 :   pari_sp av = avma;
    4164        1456 :   long i, l = lg(A);
    4165        1456 :   GEN M, x, t, p = NULL;
    4166             : 
    4167        1456 :   if (RgM_is_FpM(A, &p) && RgV_is_FpV(y, &p) && p)
    4168             :   {
    4169             :     ulong pp;
    4170          28 :     A = RgM_Fp_init(A,p,&pp);
    4171          28 :     switch(pp)
    4172             :     {
    4173             :     case 0:
    4174           7 :       y = RgC_to_FpC(y,p);
    4175           7 :       x = FpM_FpC_invimage(A, y, p);
    4176           7 :       if (x) x = FpC_to_mod(x,p);
    4177           7 :       break;
    4178             :     case 2:
    4179           7 :       y = RgV_to_F2v(y);
    4180           7 :       x = F2m_F2c_invimage(A, y);
    4181           7 :       if (x) x = F2c_to_mod(x);
    4182           7 :       break;
    4183             :     default:
    4184          14 :       y = RgV_to_Flv(y,pp);
    4185          14 :       x = Flm_Flc_invimage(A, y, pp);
    4186          14 :       if (x) x = Flc_to_mod(x,pp);
    4187             :     }
    4188          28 :     if (!x) { avma = av; return NULL; }
    4189          28 :     return gerepileupto(av, x);
    4190             :   }
    4191             : 
    4192        1428 :   if (l==1) return NULL;
    4193        1428 :   if (lg(y) != lgcols(A)) pari_err_DIM("inverseimage");
    4194             :   {
    4195        1428 :     GEN ff = NULL;
    4196        1428 :     if (RgM_is_FFM(A, &ff) && RgC_is_FFC(y, &ff))
    4197          63 :       return FFM_FFC_invimage(A, y, ff);
    4198             :   }
    4199        1365 :   M = ker(shallowconcat(A, y));
    4200        1365 :   i = lg(M)-1;
    4201        1365 :   if (!i) { avma = av; return NULL; }
    4202             : 
    4203        1365 :   x = gel(M,i); t = gel(x,l);
    4204        1365 :   if (gequal0(t)) { avma = av; return NULL; }
    4205             : 
    4206        1337 :   t = gneg_i(t); setlg(x,l);
    4207        1337 :   return gerepileupto(av, RgC_Rg_div(x, t));
    4208             : }
    4209             : GEN
    4210       36040 : FpM_FpC_invimage(GEN A, GEN y, GEN p)
    4211             : {
    4212       36040 :   pari_sp av = avma;
    4213       36040 :   long i, l = lg(A);
    4214             :   GEN M, x, t;
    4215             : 
    4216       36040 :   if (lgefint(p) == 3)
    4217             :   {
    4218       36033 :     ulong pp = p[2];
    4219       36033 :     A = ZM_to_Flm(A, pp);
    4220       36033 :     y = ZV_to_Flv(y, pp);
    4221       36033 :     x = Flm_Flc_invimage(A,y,pp);
    4222       36033 :     if (!x) { avma = av; return NULL; }
    4223       36033 :     return gerepileupto(av, Flc_to_ZC(x));
    4224             :   }
    4225           7 :   if (l==1) return NULL;
    4226           7 :   if (lg(y) != lgcols(A)) pari_err_DIM("FpM_FpC_invimage");
    4227           7 :   M = FpM_ker(shallowconcat(A,y),p);
    4228           7 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4229             : 
    4230           7 :   x = gel(M,i); t = gel(x,l);
    4231           7 :   if (!signe(t)) { avma = av; return NULL; }
    4232             : 
    4233           7 :   setlg(x,l); t = Fp_inv(negi(t),p);
    4234           7 :   if (is_pm1(t)) return gerepilecopy(av, x);
    4235           7 :   return gerepileupto(av, FpC_Fp_mul(x, t, p));
    4236             : }
    4237             : GEN
    4238       42207 : Flm_Flc_invimage(GEN A, GEN y, ulong p)
    4239             : {
    4240       42207 :   pari_sp av = avma;
    4241       42207 :   long i, l = lg(A);
    4242             :   GEN M, x;
    4243             :   ulong t;
    4244             : 
    4245       42207 :   if (l==1) return NULL;
    4246       42207 :   if (lg(y) != lgcols(A)) pari_err_DIM("Flm_Flc_invimage");
    4247       42207 :   M = cgetg(l+1,t_MAT);
    4248       42207 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4249       42207 :   gel(M,l) = y; M = Flm_ker(M,p);
    4250       42207 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4251             : 
    4252       42207 :   x = gel(M,i); t = x[l];
    4253       42207 :   if (!t) { avma = av; return NULL; }
    4254             : 
    4255       42207 :   setlg(x,l); t = Fl_inv(Fl_neg(t,p),p);
    4256       42207 :   if (t!=1) x = Flv_Fl_mul(x, t, p);
    4257       42207 :   return gerepileuptoleaf(av, x);
    4258             : }
    4259             : GEN
    4260          21 : F2m_F2c_invimage(GEN A, GEN y)
    4261             : {
    4262          21 :   pari_sp av = avma;
    4263          21 :   long i, l = lg(A);
    4264             :   GEN M, x;
    4265             : 
    4266          21 :   if (l==1) return NULL;
    4267          21 :   if (lg(y) != lgcols(A)) pari_err_DIM("F2m_F2c_invimage");
    4268          21 :   M = cgetg(l+1,t_MAT);
    4269          21 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4270          21 :   gel(M,l) = y; M = F2m_ker(M);
    4271          21 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4272             : 
    4273          21 :   x = gel(M,i);
    4274          21 :   if (!F2v_coeff(x,l)) { avma = av; return NULL; }
    4275          21 :   F2v_clear(x, x[1]); x[1]--; /* remove last coord */
    4276          21 :   return gerepileuptoleaf(av, x);
    4277             : }
    4278             : 
    4279             : /* Return X such that m X = v (t_COL or t_MAT), resp. an empty t_COL / t_MAT
    4280             :  * if no solution exist */
    4281             : GEN
    4282        1610 : inverseimage(GEN m, GEN v)
    4283             : {
    4284             :   GEN y;
    4285        1610 :   if (typ(m)!=t_MAT) pari_err_TYPE("inverseimage",m);
    4286        1610 :   switch(typ(v))
    4287             :   {
    4288             :     case t_COL:
    4289        1456 :       y = RgM_RgC_invimage(m,v);
    4290        1456 :       return y? y: cgetg(1,t_COL);
    4291             :     case t_MAT:
    4292         154 :       y = RgM_invimage(m, v);
    4293         154 :       return y? y: cgetg(1,t_MAT);
    4294             :   }
    4295           0 :   pari_err_TYPE("inverseimage",v);
    4296             :   return NULL;/*LCOV_EXCL_LINE*/
    4297             : }
    4298             : 
    4299             : static GEN
    4300          21 : Flm_invimage_CUP(GEN A, GEN B, ulong p) {
    4301          21 :   pari_sp av = avma;
    4302             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    4303             :   long r;
    4304          21 :   r = Flm_CUP(A, &R, &C, &U, &P, p);
    4305          21 :   Rc = indexcompl(R, nbrows(B));
    4306          21 :   C1 = rowpermute(C, R);
    4307          21 :   C2 = rowpermute(C, Rc);
    4308          21 :   B1 = rowpermute(B, R);
    4309          21 :   B2 = rowpermute(B, Rc);
    4310          21 :   Z = Flm_rsolve_lower_unit(C1, B1, p);
    4311          21 :   if (!gequal(Flm_mul(C2, Z, p), B2))
    4312          14 :     return NULL;
    4313          14 :   Y = vconcat(Flm_rsolve_upper(vecslice(U, 1, r), Z, p),
    4314          14 :               zero_Flm(lg(A) - 1 - r, lg(B) - 1));
    4315           7 :   X = rowpermute(Y, perm_inv(P));
    4316           7 :   return gerepileupto(av, X);
    4317             : }
    4318             : 
    4319             : static GEN
    4320          42 : Flm_invimage_i(GEN A, GEN B, ulong p)
    4321             : {
    4322             :   GEN d, x, X, Y;
    4323          42 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4324             : 
    4325          42 :   if (!nB) return cgetg(1, t_MAT);
    4326          42 :   if (nA + nB >= Flm_CUP_LIMIT && nbrows(B) >= Flm_CUP_LIMIT)
    4327          21 :     return Flm_invimage_CUP(A, B, p);
    4328             : 
    4329          21 :   x = Flm_ker_sp(shallowconcat(Flm_neg(A,p), B), p, 0);
    4330             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4331             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4332             :    * Y has at least nB columns and full rank */
    4333          21 :   nY = lg(x)-1;
    4334          21 :   if (nY < nB) return NULL;
    4335          21 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4336          21 :   d = cgetg(nB+1, t_VECSMALL);
    4337          56 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4338             :   {
    4339          49 :     for (; j>=1; j--)
    4340          42 :       if (coeff(Y,i,j)) { d[i] = j; break; }
    4341          42 :     if (!j) return NULL;
    4342             :   }
    4343             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4344          14 :   Y = vecpermute(Y, d);
    4345          14 :   x = vecpermute(x, d);
    4346          14 :   X = rowslice(x, 1, nA);
    4347          14 :   return Flm_mul(X, Flm_inv_upper_1(Y,p), p);
    4348             : }
    4349             : 
    4350             : static GEN
    4351           7 : F2m_invimage_i(GEN A, GEN B)
    4352             : {
    4353             :   GEN d, x, X, Y;
    4354           7 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4355           7 :   x = F2m_ker_sp(shallowconcat(A, B), 0);
    4356             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4357             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4358             :    * Y has at least nB columns and full rank */
    4359           7 :   nY = lg(x)-1;
    4360           7 :   if (nY < nB) return NULL;
    4361             : 
    4362             :   /* implicitly: Y = rowslice(x, nA+1, nA+nB), nB rows */
    4363           7 :   d = cgetg(nB+1, t_VECSMALL);
    4364          21 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4365             :   {
    4366          14 :     for (; j>=1; j--)
    4367          14 :       if (F2m_coeff(x,nA+i,j)) { d[i] = j; break; } /* Y[i,j] */
    4368          14 :     if (!j) return NULL;
    4369             :   }
    4370           7 :   x = vecpermute(x, d);
    4371             : 
    4372           7 :   X = F2m_rowslice(x, 1, nA);
    4373           7 :   Y = F2m_rowslice(x, nA+1, nA+nB);
    4374           7 :   return F2m_mul(X, F2m_inv_upper_1(Y));
    4375             : }
    4376             : GEN
    4377           0 : Flm_invimage(GEN A, GEN B, ulong p)
    4378             : {
    4379           0 :   pari_sp av = avma;
    4380           0 :   GEN X = Flm_invimage_i(A,B,p);
    4381           0 :   if (!X) { avma = av; return NULL; }
    4382           0 :   return gerepileupto(av, X);
    4383             : }
    4384             : GEN
    4385           0 : F2m_invimage(GEN A, GEN B)
    4386             : {
    4387           0 :   pari_sp av = avma;
    4388           0 :   GEN X = F2m_invimage_i(A,B);
    4389           0 :   if (!X) { avma = av; return NULL; }
    4390           0 :   return gerepileupto(av, X);
    4391             : }
    4392             : static GEN
    4393          14 : FpM_invimage_i(GEN A, GEN B, GEN p)
    4394             : {
    4395             :   GEN d, x, X, Y;
    4396          14 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4397          14 :   if (lgefint(p) == 3)
    4398             :   {
    4399           0 :     ulong pp = p[2];
    4400           0 :     A = ZM_to_Flm(A, pp);
    4401           0 :     B = ZM_to_Flm(B, pp);
    4402           0 :     x = Flm_invimage_i(A, B, pp);
    4403           0 :     return x? Flm_to_ZM(x): NULL;
    4404             :   }
    4405          14 :   x = FpM_ker(shallowconcat(ZM_neg(A), B), p);
    4406             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4407             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4408             :    * Y has at least nB columns and full rank */
    4409          14 :   nY = lg(x)-1;
    4410          14 :   if (nY < nB) return NULL;
    4411          14 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4412          14 :   d = cgetg(nB+1, t_VECSMALL);
    4413          35 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4414             :   {
    4415          35 :     for (; j>=1; j--)
    4416          28 :       if (signe(gcoeff(Y,i,j))) { d[i] = j; break; }
    4417          28 :     if (!j) return NULL;
    4418             :   }
    4419             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4420           7 :   Y = vecpermute(Y, d);
    4421           7 :   x = vecpermute(x, d);
    4422           7 :   X = rowslice(x, 1, nA);
    4423           7 :   return FpM_mul(X, FpM_inv_upper_1(Y,p), p);
    4424             : }
    4425             : GEN
    4426           0 : FpM_invimage(GEN A, GEN B, GEN p)
    4427             : {
    4428           0 :   pari_sp av = avma;
    4429           0 :   GEN X = FpM_invimage_i(A,B,p);
    4430           0 :   if (!X) { avma = av; return NULL; }
    4431           0 :   return gerepileupto(av, X);
    4432             : }
    4433             : 
    4434             : /* find Z such that A Z = B. Return NULL if no solution */
    4435             : GEN
    4436         168 : RgM_invimage(GEN A, GEN B)
    4437             : {
    4438         168 :   pari_sp av = avma;
    4439             :   GEN d, x, X, Y;
    4440         168 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4441         168 :   GEN p = NULL;
    4442         168 :   if (RgM_is_FpM(A, &p) && RgM_is_FpM(B, &p) && p)
    4443             :   {
    4444             :     ulong pp;
    4445          63 :     A = RgM_Fp_init(A,p,&pp);
    4446          63 :     switch(pp)
    4447             :     {
    4448             :     case 0:
    4449          14 :       B = RgM_to_FpM(B,p);
    4450          14 :       x = FpM_invimage_i(A, B, p);
    4451          14 :       if (x) x = FpM_to_mod(x, p);
    4452          14 :     break;
    4453             :     case 2:
    4454           7 :       B = RgM_to_F2m(B);
    4455           7 :       x = F2m_invimage_i(A, B);
    4456           7 :       if (x) x = F2m_to_mod(x);
    4457           7 :       break;
    4458             :     default:
    4459          42 :       B = RgM_to_Flm(B,pp);
    4460          42 :       x = Flm_invimage_i(A, B, pp);
    4461          42 :       if (x) x = Flm_to_mod(x,pp);
    4462          42 :       break;
    4463             :     }
    4464          63 :     if (!x) { avma = av; return NULL; }
    4465          35 :     return gerepileupto(av, x);
    4466             :   }
    4467             :   {
    4468         105 :     GEN ff = NULL;
    4469         105 :     if (RgM_is_FFM(A, &ff) && RgM_is_FFM(B, &ff))
    4470          42 :       return FFM_invimage(A, B, ff);
    4471             :   }
    4472          63 :   x = ker(shallowconcat(RgM_neg(A), B));
    4473             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4474             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4475             :    * Y has at least nB columns and full rank */
    4476          63 :   nY = lg(x)-1;
    4477          63 :   if (nY < nB) { avma = av; return NULL; }
    4478          63 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4479          63 :   d = cgetg(nB+1, t_VECSMALL);
    4480         140 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4481             :   {
    4482         182 :     for (; j>=1; j--)
    4483         140 :       if (!gequal0(gcoeff(Y,i,j))) { d[i] = j; break; }
    4484         119 :     if (!j) { avma = av; return NULL; }
    4485             :   }
    4486             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4487          21 :   Y = vecpermute(Y, d);
    4488          21 :   x = vecpermute(x, d);
    4489          21 :   X = rowslice(x, 1, nA);
    4490          21 :   return gerepileupto(av, RgM_mul(X, RgM_inv_upper(Y)));
    4491             : }
    4492             : 
    4493             : /* r = dim Ker x, n = nbrows(x) */
    4494             : static GEN
    4495       32100 : get_suppl(GEN x, GEN d, long n, long r, GEN(*ei)(long,long))
    4496             : {
    4497             :   pari_sp av;
    4498             :   GEN y, c;
    4499       32100 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
    4500             : 
    4501       32100 :   if (rx == n && r == 0) return gcopy(x);
    4502       25361 :   y = cgetg(n+1, t_MAT);
    4503       25361 :   av = avma; c = zero_zv(n);
    4504             :   /* c = lines containing pivots (could get it from gauss_pivot, but cheap)
    4505             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
    4506      202038 :   for (k = j = 1; j<=rx; j++)
    4507      176677 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gel(x,j); }
    4508      277643 :   for (j=1; j<=n; j++)
    4509      252282 :     if (!c[j]) gel(y,k++) = (GEN)j; /* HACK */
    4510       25361 :   avma = av;
    4511             : 
    4512       25361 :   rx -= r;
    4513       25361 :   for (j=1; j<=rx; j++) gel(y,j) = gcopy(gel(y,j));
    4514       25361 :   for (   ; j<=n; j++)  gel(y,j) = ei(n, y[j]);
    4515       25361 :   return y;
    4516             : }
    4517             : 
    4518             : static void
    4519       32100 : init_suppl(GEN x)
    4520             : {
    4521       32100 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    4522             :   /* HACK: avoid overwriting d from gauss_pivot() after avma=av */
    4523       32100 :   (void)new_chunk(lgcols(x) * 2);
    4524       32100 : }
    4525             : 
    4526             : /* x is an n x k matrix, rank(x) = k <= n. Return an invertible n x n matrix
    4527             :  * whose first k columns are given by x. If rank(x) < k, undefined result. */
    4528             : GEN
    4529          91 : suppl(GEN x)
    4530             : {
    4531          91 :   pari_sp av = avma;
    4532          91 :   GEN d, X = x, p = NULL, ff = NULL;
    4533             :   long r;
    4534             : 
    4535          91 :   if (typ(x)!=t_MAT) pari_err_TYPE("suppl",x);
    4536          91 :   if (RgM_is_FpM(x, &p) && p)
    4537             :   {
    4538             :     ulong pp;
    4539          28 :     x = RgM_Fp_init(x, p, &pp);
    4540          28 :     switch(pp)
    4541             :     {
    4542           7 :     case 0: x = FpM_to_mod(FpM_suppl(x,p), p); break;
    4543           7 :     case 2: x = F2m_to_mod(F2m_suppl(x)); break;
    4544          14 :     default:x = Flm_to_mod(Flm_suppl(x,pp), pp); break;
    4545             :     }
    4546          28 :     return gerepileupto(av, x);
    4547             :   }
    4548          63 :   if (RgM_is_FFM(x, &ff)) return FFM_suppl(x, ff);
    4549          42 :   avma = av; init_suppl(x);
    4550          42 :   d = gauss_pivot(X,&r);
    4551          42 :   avma = av; return get_suppl(X,d,nbrows(X),r,&col_ei);
    4552             : }
    4553             : GEN
    4554       31890 : FpM_suppl(GEN x, GEN p)
    4555             : {
    4556       31890 :   pari_sp av = avma;
    4557             :   GEN d;
    4558             :   long r;
    4559       31890 :   init_suppl(x); d = FpM_gauss_pivot(x,p, &r);
    4560       31890 :   avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
    4561             : }
    4562             : 
    4563             : GEN
    4564          42 : Flm_suppl(GEN x, ulong p)
    4565             : {
    4566          42 :   pari_sp av = avma;
    4567             :   GEN d;
    4568             :   long r;
    4569          42 :   init_suppl(x); d = Flm_pivots(x, p, &r, 0);
    4570          42 :   avma = av; return get_suppl(x,d,nbrows(x),r,&vecsmall_ei);
    4571             : }
    4572             : 
    4573             : GEN
    4574           7 : F2m_suppl(GEN x)
    4575             : {
    4576           7 :   pari_sp av = avma;
    4577             :   GEN d;
    4578             :   long r;
    4579           7 :   init_suppl(x); d = F2m_gauss_pivot(F2m_copy(x), &r);
    4580           7 :   avma = av; return get_suppl(x,d,mael(x,1,1),r,&F2v_ei);
    4581             : }
    4582             : 
    4583             : /* variable number to be filled in later */
    4584             : static GEN
    4585          14 : _FlxC_ei(long n, long i)
    4586             : {
    4587          14 :   GEN x = cgetg(n + 1, t_COL);
    4588             :   long j;
    4589          42 :   for (j = 1; j <= n; j++)
    4590          28 :     gel(x, j) = (j == i)? pol1_Flx(0): pol0_Flx(0);
    4591          14 :   return x;
    4592             : }
    4593             : 
    4594             : GEN
    4595           7 : F2xqM_suppl(GEN x, GEN T)
    4596             : {
    4597           7 :   pari_sp av = avma;
    4598             :   GEN d, y;
    4599           7 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    4600             : 
    4601           7 :   init_suppl(x);
    4602           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    4603           7 :   avma = av;
    4604           7 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    4605           7 :   if (sv) {
    4606             :     long i, j;
    4607          21 :     for (j = r + 1; j <= n; j++) {
    4608          42 :       for (i = 1; i <= n; i++)
    4609          28 :         gcoeff(y, i, j)[1] = sv;
    4610             :     }
    4611             :   }
    4612           7 :   return y;
    4613             : }
    4614             : 
    4615             : GEN
    4616           7 : FlxqM_suppl(GEN x, GEN T, ulong p)
    4617             : {
    4618           7 :   pari_sp av = avma;
    4619             :   GEN d, y;
    4620           7 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    4621             : 
    4622           7 :   init_suppl(x);
    4623           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    4624           7 :   avma = av;
    4625           7 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    4626           7 :   if (sv) {
    4627             :     long i, j;
    4628          21 :     for (j = r + 1; j <= n; j++) {
    4629          42 :       for (i = 1; i <= n; i++)
    4630          28 :         gcoeff(y, i, j)[1] = sv;
    4631             :     }
    4632             :   }
    4633           7 :   return y;
    4634             : }
    4635             : 
    4636             : GEN
    4637         581 : FqM_suppl(GEN x, GEN T, GEN p)
    4638             : {
    4639         581 :   pari_sp av = avma;
    4640             :   GEN d;
    4641             :   long r;
    4642             : 
    4643         581 :   if (!T) return FpM_suppl(x,p);
    4644         105 :   init_suppl(x);
    4645         105 :   d = FqM_gauss_pivot(x,T,p,&r);
    4646         105 :   avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
    4647             : }
    4648             : 
    4649             : GEN
    4650           7 : image2(GEN x)
    4651             : {
    4652           7 :   pari_sp av = avma;
    4653             :   long k, n, i;
    4654             :   GEN A, B;
    4655             : 
    4656           7 :   if (typ(x)!=t_MAT) pari_err_TYPE("image2",x);
    4657           7 :   if (lg(x) == 1) return cgetg(1,t_MAT);
    4658           7 :   A = ker(x); k = lg(A)-1;
    4659           7 :   if (!k) { avma = av; return gcopy(x); }
    4660           7 :   A = suppl(A); n = lg(A)-1;
    4661           7 :   B = cgetg(n-k+1, t_MAT);
    4662           7 :   for (i = k+1; i <= n; i++) gel(B,i-k) = RgM_RgC_mul(x, gel(A,i));
    4663           7 :   return gerepileupto(av, B);
    4664             : }
    4665             : 
    4666             : GEN
    4667         147 : matimage0(GEN x,long flag)
    4668             : {
    4669         147 :   switch(flag)
    4670             :   {
    4671         140 :     case 0: return image(x);
    4672           7 :     case 1: return image2(x);
    4673           0 :     default: pari_err_FLAG("matimage");
    4674             :   }
    4675             :   return NULL; /* LCOV_EXCL_LINE */
    4676             : }
    4677             : 
    4678             : long
    4679         196 : rank(GEN x)
    4680             : {
    4681         196 :   pari_sp av = avma;
    4682             :   long r;
    4683         196 :   GEN ff = NULL, p = NULL;
    4684             : 
    4685         196 :   if (typ(x)!=t_MAT) pari_err_TYPE("rank",x);
    4686         196 :   if (RgM_is_FpM(x, &p) && p)
    4687             :   {
    4688             :     ulong pp;
    4689         105 :     x = RgM_Fp_init(x,p,&pp);
    4690         105 :     switch(pp)
    4691             :     {
    4692           7 :     case 0: r = FpM_rank(x,p); break;
    4693          63 :     case 2: r = F2m_rank(x); break;
    4694          35 :     default:r = Flm_rank(x,pp); break;
    4695             :     }
    4696         105 :     avma = av; return r;
    4697             :   }
    4698          91 :   if (RgM_is_FFM(x, &ff)) return FFM_rank(x, ff);
    4699          49 :   (void)gauss_pivot(x, &r);
    4700          49 :   avma = av; return lg(x)-1 - r;
    4701             : }
    4702             : 
    4703             : /* d a t_VECSMALL of integers in 1..n. Return the vector of the d[i]
    4704             :  * followed by the missing indices */
    4705             : static GEN
    4706        5454 : perm_complete(GEN d, long n)
    4707             : {
    4708        5454 :   GEN y = cgetg(n+1, t_VECSMALL);
    4709        5454 :   long i, j = 1, k = n, l = lg(d);
    4710        5454 :   pari_sp av = avma;
    4711        5454 :   char *T = stack_calloc(n+1);
    4712        5454 :   for (i = 1; i < l; i++) T[d[i]] = 1;
    4713       67758 :   for (i = 1; i <= n; i++)
    4714       62304 :     if (T[i]) y[j++] = i; else y[k--] = i;
    4715        5454 :   avma = av; return y;
    4716             : }
    4717             : 
    4718             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    4719             : static GEN
    4720       20598 : indexrank0(long n, long r, GEN d)
    4721             : {
    4722       20598 :   GEN p1, p2, res = cgetg(3,t_VEC);
    4723             :   long i, j;
    4724             : 
    4725       20598 :   r = n - r; /* now r = dim Im(x) */
    4726       20598 :   p1 = cgetg(r+1,t_VECSMALL); gel(res,1) = p1;
    4727       20598 :   p2 = cgetg(r+1,t_VECSMALL); gel(res,2) = p2;
    4728       20598 :   if (d)
    4729             :   {
    4730      117429 :     for (i=0,j=1; j<=n; j++)
    4731       97559 :       if (d[j]) { i++; p1[i] = d[j]; p2[i] = j; }
    4732       19870 :     vecsmall_sort(p1);
    4733             :   }
    4734       20598 :   return res;
    4735             : }
    4736             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    4737             : static GEN
    4738         840 : indeximage0(long n, long r, GEN d)
    4739             : {
    4740             :   long i, j;
    4741             :   GEN v;
    4742             : 
    4743         840 :   r = n - r; /* now r = dim Im(x) */
    4744         840 :   v = cgetg(r+1,t_VECSMALL);
    4745        7651 :   if (d) for (i=j=1; j<=n; j++)
    4746        6811 :     if (d[j]) v[i++] = j;
    4747         840 :   return v;
    4748             : }
    4749             : /* x an m x n t_MAT, n > 0, r = dim Ker(x), d from gauss_pivot */
    4750             : static void
    4751        2727 : indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol)
    4752             : {
    4753        2727 :   GEN IR = indexrank0(n, r, d);
    4754        2727 :   *prow = perm_complete(gel(IR,1), m);
    4755        2727 :   *pcol = perm_complete(gel(IR,2), n);
    4756        2727 : }
    4757             : static void
    4758       18711 : init_indexrank(GEN x) {
    4759       18711 :   (void)new_chunk(3 + 2*lg(x)); /* HACK */
    4760       18711 : }
    4761             : 
    4762             : GEN
    4763        4179 : indexrank(GEN x) {
    4764             :   pari_sp av;
    4765             :   long r;
    4766        4179 :   GEN d, p = NULL, ff = NULL;
    4767        4179 :   if (typ(x)!=t_MAT) pari_err_TYPE("indexrank",x);
    4768        4179 :   if (RgM_is_FpM(x, &p) && p)
    4769             :   {
    4770             :     ulong pp;
    4771          28 :     x = RgM_Fp_init(x,p,&pp);
    4772          28 :     switch(pp)
    4773             :     {
    4774           7 :     case 0:  return FpM_indexrank(x, p);
    4775           7 :     case 2:  return F2m_indexrank(x);
    4776          14 :     default: return Flm_indexrank(x, pp);
    4777             :     }
    4778             :   }
    4779        4151 :   if (RgM_is_FFM(x, &ff)) return FFM_indexrank(x, ff);
    4780        4130 :   av = avma;
    4781        4130 :   init_indexrank(x);
    4782        4130 :   d = gauss_pivot(x, &r);
    4783        4130 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4784             : }
    4785             : 
    4786             : GEN
    4787          28 : FpM_indexrank(GEN x, GEN p) {
    4788          28 :   pari_sp av = avma;
    4789             :   long r;
    4790             :   GEN d;
    4791          28 :   init_indexrank(x);
    4792          28 :   d = FpM_gauss_pivot(x,p,&r);
    4793          28 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4794             : }
    4795             : 
    4796             : GEN
    4797       10031 : Flm_indexrank(GEN x, ulong p) {
    4798       10031 :   pari_sp av = avma;
    4799             :   long r;
    4800             :   GEN d;
    4801       10031 :   init_indexrank(x);
    4802       10031 :   d = Flm_pivots(x, p, &r, 0);
    4803       10031 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4804             : }
    4805             : 
    4806             : GEN
    4807           7 : F2m_indexrank(GEN x) {
    4808           7 :   pari_sp av = avma;
    4809             :   long r;
    4810             :   GEN d;
    4811           7 :   init_indexrank(x);
    4812           7 :   d = F2m_gauss_pivot(F2m_copy(x),&r);
    4813           7 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4814             : }
    4815             : 
    4816             : GEN
    4817           7 : F2xqM_indexrank(GEN x, GEN T) {
    4818           7 :   pari_sp av = avma;
    4819             :   long r;
    4820             :   GEN d;
    4821           7 :   init_indexrank(x);
    4822           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    4823           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    4824             : }
    4825             : 
    4826             : GEN
    4827           7 : FlxqM_indexrank(GEN x, GEN T, ulong p) {
    4828           7 :   pari_sp av = avma;
    4829             :   long r;
    4830             :   GEN d;
    4831           7 :   init_indexrank(x);
    4832           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    4833           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    4834             : }
    4835             : 
    4836             : GEN
    4837           7 : FqM_indexrank(GEN x, GEN T, GEN p) {
    4838           7 :   pari_sp av = avma;
    4839             :   long r;
    4840             :   GEN d;
    4841           7 :   init_indexrank(x);
    4842           7 :   d = FqM_gauss_pivot(x, T, p, &r);
    4843           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    4844             : }
    4845             : 
    4846             : GEN
    4847         840 : ZM_indeximage(GEN x) {
    4848         840 :   pari_sp av = avma;
    4849             :   long r;
    4850             :   GEN d;
    4851         840 :   init_indexrank(x);
    4852         840 :   d = ZM_pivots(x,&r);
    4853         840 :   avma = av; return indeximage0(lg(x)-1, r, d);
    4854             : }
    4855             : long
    4856       34025 : ZM_rank(GEN x) {
    4857       34025 :   pari_sp av = avma;
    4858             :   long r;
    4859       34025 :   (void)ZM_pivots(x,&r);
    4860       34025 :   avma = av; return lg(x)-1-r;
    4861             : }
    4862             : GEN
    4863        3654 : ZM_indexrank(GEN x) {
    4864        3654 :   pari_sp av = avma;
    4865             :   long r;
    4866             :   GEN d;
    4867        3654 :   init_indexrank(x);
    4868        3654 :   d = ZM_pivots(x,&r);
    4869        3654 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4870             : }
    4871             : 
    4872             : /*******************************************************************/
    4873             : /*                                                                 */
    4874             : /*                             ZabM                                */
    4875             : /*                                                                 */
    4876             : /*******************************************************************/
    4877             : 
    4878             : static GEN
    4879           0 : FpVM_ratlift(GEN a, GEN q, long vs)
    4880             : {
    4881             :   GEN B, y;
    4882           0 :   long i, j, l = lg(a), n;
    4883           0 :   B = sqrti(shifti(q,-1));
    4884           0 :   y = cgetg(l, t_MAT);
    4885           0 :   if (l==1) return y;
    4886           0 :   n = lgcols(a);
    4887           0 :   for (i=1; i<l; i++)
    4888             :   {
    4889           0 :     GEN yi = cgetg(n, t_COL);
    4890           0 :     for (j=1; j<n; j++)
    4891             :     {
    4892           0 :       GEN v = FpC_ratlift(gmael(a,i,j), q, B, B, NULL);
    4893           0 :       if (!v) return NULL;
    4894           0 :       gel(yi, j) = RgV_to_RgX(v, vs);
    4895             :     }
    4896           0 :     gel(y,i) = yi;
    4897             :   }
    4898           0 :   return y;
    4899             : }
    4900             : 
    4901             : static GEN
    4902           0 : FlmV_recover(GEN a, GEN M, ulong p)
    4903             : {
    4904           0 :   GEN a1 = gel(a,1);
    4905           0 :   long i, j, k, l = lg(a1), n, lM = lg(M);
    4906           0 :   GEN y = cgetg(l, t_MAT);
    4907           0 :   if (l==1) return y;
    4908           0 :   n = lgcols(a1);
    4909           0 :   for (i=1; i<l; i++)
    4910             :   {
    4911           0 :     GEN yi = cgetg(n, t_COL);
    4912           0 :     for (j=1; j<n; j++)
    4913             :     {
    4914           0 :       GEN v = cgetg(lM, t_COL);
    4915           0 :       for (k=1; k<lM; k++) gel(v,k) = gmael(gel(a,k),i,j);
    4916           0 :       gel(yi, j) = Flm_Flc_mul(M, v, p);
    4917             :     }
    4918           0 :     gel(y,i) = yi;
    4919             :   }
    4920           0 :   return y;
    4921             : }
    4922             : 
    4923             : static GEN
    4924           0 : FlkM_inv(GEN M, GEN P, ulong p)
    4925             : {
    4926           0 :   ulong pi = get_Fl_red(p);
    4927           0 :   GEN R = Flx_roots(P, p);
    4928           0 :   long l = lg(R), i;
    4929           0 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4930           0 :   GEN V = cgetg(l, t_VEC);
    4931           0 :   for(i=1; i<l; i++)
    4932             :   {
    4933           0 :     gel(V, i) = Flm_inv_sp(FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), degpol(P), p, pi), p, pi), NULL, p);
    4934           0 :     if (!gel(V, i)) return NULL;
    4935             :   }
    4936           0 :   return FlmV_recover(V,W,p);
    4937             : }
    4938             : 
    4939             : GEN
    4940           0 : ZabM_inv(GEN M, GEN P, long n, GEN *pden)
    4941             : {
    4942           0 :   pari_sp av2, av = avma;
    4943             :   GEN q, H;
    4944           0 :   ulong m = LONG_MAX>>1;
    4945           0 :   ulong p= 1 + m - (m % n);
    4946           0 :   long lM = lg(M);
    4947           0 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    4948             : 
    4949           0 :   av2 = avma;
    4950           0 :   H = NULL;
    4951             :   for(;;)
    4952             :   {
    4953             :     GEN Hp, Pp, Mp, Hr;
    4954           0 :     do p += n; while(!uisprime(p));
    4955           0 :     Pp = ZX_to_Flx(P, p);
    4956           0 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    4957           0 :     Hp = FlkM_inv(Mp, Pp, p);
    4958           0 :     if (!Hp) continue;
    4959           0 :     if (!H)
    4960             :     {
    4961           0 :       H = ZVM_init_CRT(Hp, p);
    4962           0 :       q = utoipos(p);
    4963             :     }
    4964             :     else
    4965           0 :       ZVM_incremental_CRT(&H, Hp, &q, p);
    4966           0 :     Hr = FpVM_ratlift(H, q, varn(P));
    4967           0 :     if (DEBUGLEVEL>5) err_printf("ZabM_inv mod %ld (ratlift=%ld)\n", p,!!Hr);
    4968           0 :     if (Hr) {/* DONE ? */
    4969           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    4970           0 :       GEN MH = RgXQM_mul(M, Hl,P);
    4971           0 :       if (*pden)
    4972           0 :       { if (RgM_isscalar(MH, *pden)) { H = Hl; break; }}
    4973             :       else
    4974           0 :       { if (RgM_isidentity(MH)) { H = Hl; *pden = gen_1; break; } }
    4975             :     }
    4976             : 
    4977           0 :     if (gc_needed(av,2))
    4978             :     {
    4979           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_inv");
    4980           0 :       gerepileall(av2, 2, &H, &q);
    4981             :     }
    4982           0 :   }
    4983           0 :   gerepileall(av, 2, &H, pden);
    4984           0 :   return H;
    4985             : }
    4986             : 
    4987             : static GEN
    4988           0 : FlkM_ker(GEN M, GEN P, ulong p)
    4989             : {
    4990           0 :   ulong pi = get_Fl_red(p);
    4991           0 :   GEN R = Flx_roots(P, p);
    4992           0 :   long l = lg(R), i, dP = degpol(P), r;
    4993             :   GEN M1, K, D;
    4994           0 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4995           0 :   GEN V = cgetg(l, t_VEC);
    4996           0 :   M1 = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,1), dP, p, pi), p, pi);
    4997           0 :   K = Flm_ker_sp(M1, p, 2);
    4998           0 :   r = lg(gel(K,1)); D = gel(K,2);
    4999           0 :   gel(V, 1) = gel(K,1);
    5000           0 :   for(i=2; i<l; i++)
    5001             :   {
    5002           0 :     GEN Mi = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), dP, p, pi), p, pi);
    5003           0 :     GEN K = Flm_ker_sp(Mi, p, 2);
    5004           0 :     if (lg(gel(K,1)) != r || !zv_equal(D, gel(K,2))) return NULL;
    5005           0 :     gel(V, i) = gel(K,1);
    5006             :   }
    5007           0 :   return mkvec2(FlmV_recover(V,W,p), D);
    5008             : }
    5009             : 
    5010             : GEN
    5011           0 : ZabM_ker(GEN M, GEN P, long n)
    5012             : {
    5013           0 :   pari_sp av2, av = avma;
    5014             :   GEN q, H, D;
    5015           0 :   ulong m = LONG_MAX>>1;
    5016           0 :   ulong p= 1 + m - (m % n);
    5017           0 :   av2 = avma;
    5018           0 :   H = NULL; D = NULL;
    5019             :   for(;;)
    5020             :   {
    5021             :     GEN Kp, Hp, Dp, Pp, Mp, Hr;
    5022           0 :     do p += n; while(!uisprime(p));
    5023           0 :     Pp = ZX_to_Flx(P, p);
    5024           0 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    5025           0 :     Kp = FlkM_ker(Mp, Pp, p);
    5026           0 :     if (!Kp) continue;
    5027           0 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    5028           0 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    5029           0 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    5030             :     {
    5031           0 :       H = ZVM_init_CRT(Hp, p); D = Dp;
    5032           0 :       q = utoipos(p);
    5033             :     }
    5034             :     else
    5035           0 :       ZVM_incremental_CRT(&H, Hp, &q, p);
    5036           0 :     Hr = FpVM_ratlift(H, q, varn(P));
    5037           0 :     if (DEBUGLEVEL>5) err_printf("ZabM_ker mod %ld (ratlift=%ld)\n", p,!!Hr);
    5038           0 :     if (Hr) {/* DONE ? */
    5039           0 :       GEN Hl = Q_remove_denom(Hr, NULL);
    5040           0 :       GEN MH = RgXQM_mul(M, Hl,P);
    5041           0 :       if (gequal0(MH)) { H = Hl;  break; }
    5042             :     }
    5043             : 
    5044           0 :     if (gc_needed(av,2))
    5045             :     {
    5046           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_ker");
    5047           0 :       gerepileall(av2, 3, &H, &D, &q);
    5048             :     }
    5049           0 :   }
    5050           0 :   return gerepilecopy(av, H);
    5051             : }
    5052             : 
    5053             : GEN
    5054           0 : ZabM_indexrank(GEN M, GEN P, long n)
    5055             : {
    5056           0 :   pari_sp av = avma;
    5057           0 :   ulong m = LONG_MAX>>1;
    5058           0 :   ulong p = 1+m-(m%n), D = degpol(P);
    5059             :   GEN v;
    5060             :   do
    5061             :   {
    5062             :     ulong pi;
    5063             :     GEN R, Mp, K;
    5064           0 :     do p += n; while (!uisprime(p));
    5065           0 :     pi = get_Fl_red(p);
    5066           0 :     R = Flx_roots(ZX_to_Flx(P, p), p);
    5067           0 :     Mp = FqM_to_FlxM(M, P, utoipos(p));
    5068           0 :     K = FlxM_eval_powers_pre(Mp, Fl_powers_pre(uel(R,1), D,p,pi), p,pi);
    5069           0 :     v = Flm_indexrank(K, p);
    5070           0 :   } while (lg(gel(v,2)) < lg(M));
    5071           0 :   return gerepileupto(av, v);
    5072             : }
    5073             : 
    5074             : #if 0
    5075             : GEN
    5076             : ZabM_gauss(GEN M, GEN P, long n, GEN *den)
    5077             : {
    5078             :   pari_sp av = avma;
    5079             :   GEN v, S, W;
    5080             :   v = ZabM_indexrank(M, P, n);
    5081             :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    5082             :   W = ZabM_inv(S, P, n, den);
    5083             :   gerepileall(av,2,&W,den);
    5084             :   return W;
    5085             : }
    5086             : #endif
    5087             : 
    5088             : GEN
    5089           0 : ZabM_pseudoinv(GEN M, GEN P, long n, GEN *den)
    5090             : {
    5091           0 :   pari_sp av = avma;
    5092             :   GEN v, S, W, z, v1;
    5093             :   long l, i;
    5094           0 :   v = ZabM_indexrank(M, P, n);
    5095           0 :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    5096           0 :   W = ZabM_inv(S, P, n, den);
    5097           0 :   z = zeromatcopy(lg(M)-1,lgcols(M)-1);
    5098           0 :   v1 = gel(v,1); l = lg(v1);
    5099           0 :   for(i=1; i<l; i++) gel(z, v1[i]) = gel(W, i);
    5100           0 :   gerepileall(av,2,&z,den);
    5101           0 :   return z;
    5102             : }
    5103             : 
    5104             : GEN
    5105           0 : ZM_pseudoinv(GEN M, GEN *den)
    5106             : {
    5107           0 :   pari_sp av = avma;
    5108             :   GEN v, S, W, z, v1;
    5109             :   long l, i;
    5110           0 :   v = ZM_indexrank(M);
    5111           0 :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    5112           0 :   W = ZM_inv_ratlift(S, den);
    5113           0 :   z = zeromatcopy(lg(M)-1,lgcols(M)-1);
    5114           0 :   v1 = gel(v,1); l = lg(v1);
    5115           0 :   for(i=1; i<l; i++) gel(z, v1[i]) = gel(W, i);
    5116           0 :   gerepileall(av,2,&z,den);
    5117           0 :   return z;
    5118             : }
    5119             : 
    5120             : /*******************************************************************/
    5121             : /*                                                                 */
    5122             : /*                   Structured Elimination                        */
    5123             : /*                                                                 */
    5124             : /*******************************************************************/
    5125             : 
    5126             : static void
    5127       93300 : rem_col(GEN c, long i, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5128             : {
    5129       93300 :   long lc = lg(c), k;
    5130       93300 :   iscol[i] = 0; (*rcol)--;
    5131      826898 :   for (k = 1; k < lc; ++k)
    5132             :   {
    5133      733598 :     Wrow[c[k]]--;
    5134      733598 :     if (Wrow[c[k]]==0) (*rrow)--;
    5135             :   }
    5136       93300 : }
    5137             : 
    5138             : static void
    5139        5511 : rem_singleton(GEN M, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5140             : {
    5141             :   long i, j;
    5142        5511 :   long nbcol = lg(iscol)-1, last;
    5143             :   do
    5144             :   {
    5145        7362 :     last = 0;
    5146    16273280 :     for (i = 1; i <= nbcol; ++i)
    5147    16265918 :       if (iscol[i])
    5148             :       {
    5149     8791549 :         GEN c = gmael(M, i, 1);
    5150     8791549 :         long lc = lg(c);
    5151    81794832 :         for (j = 1; j < lc; ++j)
    5152    73015726 :           if (Wrow[c[j]] == 1)
    5153             :           {
    5154       12443 :             rem_col(c, i, iscol, Wrow, rcol, rrow);
    5155       12443 :             last=1; break;
    5156             :           }
    5157             :       }
    5158        7362 :   } while (last);
    5159        5511 : }
    5160             : 
    5161             : static GEN
    5162        5395 : fill_wcol(GEN M, GEN iscol, GEN Wrow, long *w, GEN wcol)
    5163             : {
    5164        5395 :   long nbcol = lg(iscol)-1;
    5165             :   long i, j, m, last;
    5166             :   GEN per;
    5167       13083 :   for (m = 2, last=0; !last ; m++)
    5168             :   {
    5169    17501682 :     for (i = 1; i <= nbcol; ++i)
    5170             :     {
    5171    17493994 :       wcol[i] = 0;
    5172    17493994 :       if (iscol[i])
    5173             :       {
    5174     9397506 :         GEN c = gmael(M, i, 1);
    5175     9397506 :         long lc = lg(c);
    5176    87277809 :         for (j = 1; j < lc; ++j)
    5177    77880303 :           if (Wrow[c[j]] == m) {  wcol[i]++; last = 1; }
    5178             :       }
    5179             :     }
    5180             :   }
    5181        5395 :   per = vecsmall_indexsort(wcol);
    5182        5395 :   *w = wcol[per[nbcol]];
    5183        5395 :   return per;
    5184             : }
    5185             : 
    5186             : /* M is a RgMs with nbrow rows, A a list of row indices.
    5187             :    Eliminate rows of M with a single entry that do not belong to A,
    5188             :    and the corresponding columns. Also eliminate columns until #colums=#rows.
    5189             :    Return pcol and prow:
    5190             :    pcol is a map from the new columns indices to the old one.
    5191             :    prow is a map from the old rows indices to the new one (0 if removed).
    5192             : */
    5193             : 
    5194             : void
    5195         116 : RgMs_structelim_col(GEN M, long nbcol, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5196             : {
    5197             :   long i,j,k;
    5198         116 :   long lA = lg(A);
    5199         116 :   GEN prow = cgetg(nbrow+1, t_VECSMALL);
    5200         116 :   GEN pcol = zero_zv(nbcol);
    5201         116 :   pari_sp av = avma;
    5202         116 :   long rcol = nbcol, rrow = 0, imin = nbcol - usqrt(nbcol);
    5203         116 :   GEN iscol = const_vecsmall(nbcol, 1);
    5204         116 :   GEN Wrow  = zero_zv(nbrow);
    5205         116 :   GEN wcol = cgetg(nbcol+1, t_VECSMALL);
    5206         116 :   pari_sp av2=avma;
    5207      118801 :   for (i = 1; i <= nbcol; ++i)
    5208             :   {
    5209      118685 :     GEN F = gmael(M, i, 1);
    5210      118685 :     long l = lg(F)-1;
    5211     1050732 :     for (j = 1; j <= l; ++j)
    5212      932047 :       Wrow[F[j]]++;
    5213             :   }
    5214         116 :   for (j = 1; j < lA; ++j)
    5215             :   {
    5216         116 :     if (Wrow[A[j]] == 0) { *p_col=NULL; return; }
    5217           0 :     Wrow[A[j]] = -1;
    5218             :   }
    5219      220141 :   for (i = 1; i <= nbrow; ++i)
    5220      220025 :     if (Wrow[i])
    5221       64796 :       rrow++;
    5222         116 :   rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5223         116 :   if (rcol<rrow) pari_err_BUG("RgMs_structelim, rcol<rrow");
    5224        5627 :   for (; rcol>rrow;)
    5225             :   {
    5226             :     long w;
    5227        5395 :     GEN per = fill_wcol(M, iscol, Wrow, &w, wcol);
    5228       86252 :     for (i = nbcol; i>=imin && wcol[per[i]]>=w && rcol>rrow; i--)
    5229       80857 :       rem_col(gmael(M, per[i], 1), per[i], iscol, Wrow, &rcol, &rrow);
    5230        5395 :     rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5231        5395 :     avma = av2;
    5232             :   }
    5233      118801 :   for (j = 1, i = 1; i <= nbcol; ++i)
    5234      118685 :     if (iscol[i])
    5235       25385 :       pcol[j++] = i;
    5236         116 :   setlg(pcol,j);
    5237      220141 :   for (k = 1, i = 1; i <= nbrow; ++i)
    5238      220025 :     prow[i] = Wrow[i] ? k++: 0;
    5239         116 :   avma = av;
    5240         116 :   *p_col = pcol; *p_row = prow;
    5241             : }
    5242             : 
    5243             : void
    5244           0 : RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5245             : {
    5246           0 :   RgMs_structelim_col(M, lg(M)-1, nbrow, A, p_col, p_row);
    5247           0 : }
    5248             : 
    5249             : /*******************************************************************/
    5250             : /*                                                                 */
    5251             : /*                        EIGENVECTORS                             */
    5252             : /*   (independent eigenvectors, sorted by increasing eigenvalue)   */
    5253             : /*                                                                 */
    5254             : /*******************************************************************/
    5255             : 
    5256             : GEN
    5257          63 : mateigen(GEN x, long flag, long prec)
    5258             : {
    5259             :   GEN y, R, T;
    5260          63 :   long k, l, ex, n = lg(x);
    5261          63 :   pari_sp av = avma;
    5262             : 
    5263          63 :   if (typ(x)!=t_MAT) pari_err_TYPE("eigen",x);
    5264          63 :   if (n != 1 && n != lgcols(x)) pari_err_DIM("eigen");
    5265          63 :   if (flag < 0 || flag > 1) pari_err_FLAG("mateigen");
    5266          63 :   if (n == 1)
    5267             :   {
    5268          14 :     if (flag) retmkvec2(cgetg(1,t_VEC), cgetg(1,t_MAT));
    5269           7 :     return cgetg(1,t_VEC);
    5270             :   }
    5271          49 :   if (n == 2)
    5272             :   {
    5273          14 :     if (flag) retmkvec2(mkveccopy(gcoeff(x,1,1)), matid(1));
    5274           7 :     return matid(1);
    5275             :   }
    5276             : 
    5277          35 :   ex = 16 - prec2nbits(prec);
    5278          35 :   T = charpoly(x,0);
    5279          35 :   if (RgX_is_QX(T))
    5280             :   {
    5281          28 :     T = ZX_radical( Q_primpart(T) );
    5282          28 :     R = nfrootsQ(T);
    5283          28 :     if (lg(R)-1 < degpol(T))
    5284             :     { /* add missing complex roots */
    5285          14 :       GEN r = cleanroots(RgX_div(T, roots_to_pol(R, 0)), prec);
    5286          14 :       settyp(r, t_VEC);
    5287          14 :       R = shallowconcat(R, r);
    5288             :     }
    5289             :   }
    5290             :   else
    5291             :   {
    5292           7 :     GEN r1, v = vectrunc_init(lg(T));
    5293             :     long e;
    5294           7 :     R = cleanroots(T,prec);
    5295           7 :     r1 = NULL;
    5296          21 :     for (k = 1; k < lg(R); k++)
    5297             :     {
    5298          14 :       GEN r2 = gel(R,k), r = grndtoi(r2, &e);
    5299          14 :       if (e < ex) r2 = r;
    5300          14 :       if (r1)
    5301             :       {
    5302           7 :         r = gsub(r1,r2);
    5303           7 :         if (gequal0(r) || gexpo(r) < ex) continue;
    5304             :       }
    5305          14 :       vectrunc_append(v, r2);
    5306          14 :       r1 = r2;
    5307             :     }
    5308           7 :     R = v;
    5309             :   }
    5310             :   /* R = distinct complex roots of charpoly(x) */
    5311          35 :   l = lg(R); y = cgetg(l, t_VEC);
    5312         189 :   for (k = 1; k < l; k++)
    5313             :   {
    5314         154 :     GEN F = ker_aux(RgM_Rg_sub_shallow(x, gel(R,k)), x);
    5315         154 :     long d = lg(F)-1;
    5316         154 :     if (!d) pari_err_PREC("mateigen");
    5317         154 :     gel(y,k) = F;
    5318         154 :     if (flag) gel(R,k) = const_vec(d, gel(R,k));
    5319             :   }
    5320          35 :   y = shallowconcat1(y);
    5321          35 :   if (lg(y) > n) pari_err_PREC("mateigen");
    5322             :   /* lg(y) < n if x is not diagonalizable */
    5323          35 :   if (flag) y = mkvec2(shallowconcat1(R), y);
    5324          35 :   return gerepilecopy(av,y);
    5325             : }
    5326             : GEN
    5327           0 : eigen(GEN x, long prec) { return mateigen(x, 0, prec); }
    5328             : 
    5329             : /*******************************************************************/
    5330             : /*                                                                 */
    5331             : /*                           DETERMINANT                           */
    5332             : /*                                                                 */
    5333             : /*******************************************************************/
    5334             : 
    5335             : GEN
    5336        1645 : det0(GEN a,long flag)
    5337             : {
    5338        1645 :   switch(flag)
    5339             :   {
    5340        1631 :     case 0: return det(a);
    5341          14 :     case 1: return det2(a);
    5342           0 :     default: pari_err_FLAG("matdet");
    5343             :   }
    5344             :   return NULL; /* LCOV_EXCL_LINE */
    5345             : }
    5346             : 
    5347             : /* M a 2x2 matrix, returns det(M) */
    5348             : static GEN
    5349        2388 : RgM_det2(GEN M)
    5350             : {
    5351        2388 :   pari_sp av = avma;
    5352        2388 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5353        2388 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5354        2388 :   return gerepileupto(av, gsub(gmul(a,d), gmul(b,c)));
    5355             : }
    5356             : /* M a 2x2 ZM, returns det(M) */
    5357             : static GEN
    5358        7448 : ZM_det2(GEN M)
    5359             : {
    5360        7448 :   pari_sp av = avma;
    5361        7448 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5362        7448 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5363        7448 :   return gerepileuptoint(av, subii(mulii(a,d), mulii(b, c)));
    5364             : }
    5365             : /* M a 3x3 ZM, return det(M) */
    5366             : static GEN
    5367        2933 : ZM_det3(GEN M)
    5368             : {
    5369        2933 :   pari_sp av = avma;
    5370        2933 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2), c = gcoeff(M,1,3);
    5371        2933 :   GEN d = gcoeff(M,2,1), e = gcoeff(M,2,2), f = gcoeff(M,2,3);
    5372        2933 :   GEN g = gcoeff(M,3,1), h = gcoeff(M,3,2), i = gcoeff(M,3,3);
    5373        2933 :   GEN t, D = signe(i)? mulii(subii(mulii(a,e), mulii(b,d)), i): gen_0;
    5374        2933 :   if (signe(g))
    5375             :   {
    5376        2730 :     t = mulii(subii(mulii(b,f), mulii(c,e)), g);
    5377        2730 :     D = addii(D, t);
    5378             :   }
    5379        2933 :   if (signe(h))
    5380             :   {
    5381        2751 :     t = mulii(subii(mulii(c,d), mulii(a,f)), h);
    5382        2751 :     D = addii(D, t);
    5383             :   }
    5384        2933 :   return gerepileuptoint(av, D);
    5385             : }
    5386             : 
    5387             : static GEN
    5388        9811 : det_simple_gauss(GEN a, GEN data, pivot_fun pivot)
    5389             : {
    5390        9811 :   pari_sp av = avma;
    5391        9811 :   long i,j,k, s = 1, nbco = lg(a)-1;
    5392        9811 :   GEN p, x = gen_1;
    5393             : 
    5394        9811 :   a = RgM_shallowcopy(a);
    5395       78224 :   for (i=1; i<nbco; i++)
    5396             :   {
    5397       68420 :     k = pivot(a, data, i, NULL);
    5398       68420 :     if (k > nbco) return gerepilecopy(av, gcoeff(a,i,i));
    5399       68413 :     if (k != i)
    5400             :     { /* exchange the lines s.t. k = i */
    5401        4370 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    5402        4370 :       s = -s;
    5403             :     }
    5404       68413 :     p = gcoeff(a,i,i);
    5405             : 
    5406       68413 :     x = gmul(x,p);
    5407      400539 :     for (k=i+1; k<=nbco; k++)
    5408             :     {
    5409      332126 :       GEN m = gcoeff(a,i,k);
    5410      332126 :       if (gequal0(m)) continue;
    5411             : 
    5412      115329 :       m = gdiv(m,p);
    5413      767955 :       for (j=i+1; j<=nbco; j++)
    5414      652626 :         gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i)));
    5415             :     }
    5416       68413 :     if (gc_needed(av,2))
    5417             :     {
    5418           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5419           0 :       gerepileall(av,2, &a,&x);
    5420             :     }
    5421             :   }
    5422        9804 :   if (s < 0) x = gneg_i(x);
    5423        9804 :   return gerepileupto(av, gmul(x, gcoeff(a,nbco,nbco)));
    5424             : }
    5425             : 
    5426             : GEN
    5427        3130 : det2(GEN a)
    5428             : {
    5429             :   GEN data;
    5430             :   pivot_fun pivot;
    5431        3130 :   long n = lg(a)-1;
    5432        3130 :   if (typ(a)!=t_MAT) pari_err_TYPE("det2",a);
    5433        3130 :   if (!n) return gen_1;
    5434        3130 :   if (n != nbrows(a)) pari_err_DIM("det2");
    5435        3130 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5436        3130 :   if (n == 2) return RgM_det2(a);
    5437        1638 :   pivot = get_pivot_fun(a, a, &data);
    5438        1638 :   return det_simple_gauss(a, data, pivot);
    5439             : }
    5440             : 
    5441             : static GEN
    5442         672 : mydiv(GEN x, GEN y)
    5443             : {
    5444         672 :   long tx = typ(x), ty = typ(y);
    5445         672 :   if (tx == ty && tx == t_POL && varn(x) == varn(y)) return RgX_div(x,y);
    5446         672 :   return gdiv(x,y);
    5447             : }
    5448             : 
    5449             : /* Assumes a a square t_MAT of dimension n > 0. Returns det(a) using
    5450             :  * Gauss-Bareiss. */
    5451             : static GEN
    5452         196 : det_bareiss(GEN a)
    5453             : {
    5454         196 :   pari_sp av = avma;
    5455         196 :   long nbco = lg(a)-1,i,j,k,s = 1;
    5456             :   GEN p, pprec;
    5457             : 
    5458         196 :   a = RgM_shallowcopy(a);
    5459         770 :   for (pprec=gen_1,i=1; i<nbco; i++,pprec=p)
    5460             :   {
    5461             :     GEN ci;
    5462         574 :     int diveuc = (gequal1(pprec)==0);
    5463             : 
    5464         574 :     p = gcoeff(a,i,i);
    5465         574 :     if (gequal0(p))
    5466             :     {
    5467           0 :       k=i+1; while (k<=nbco && gequal0(gcoeff(a,i,k))) k++;
    5468           0 :       if (k>nbco) return gerepilecopy(av, p);
    5469           0 :       swap(gel(a,k), gel(a,i)); s = -s;
    5470           0 :       p = gcoeff(a,i,i);
    5471             :     }
    5472         574 :     ci = gel(a,i);
    5473        1708 :     for (k=i+1; k<=nbco; k++)
    5474             :     {
    5475        1134 :       GEN ck = gel(a,k), m = gel(ck,i);
    5476        1134 :       if (gequal0(m))
    5477             :       {
    5478           0 :         if (gequal1(p))
    5479             :         {
    5480           0 :           if (diveuc)
    5481           0 :             gel(a,k) = mydiv(gel(a,k), pprec);
    5482             :         }
    5483             :         else
    5484           0 :           for (j=i+1; j<=nbco; j++)
    5485             :           {
    5486           0 :             GEN p1 = gmul(p, gel(ck,j));
    5487           0 :             if (diveuc) p1 = mydiv(p1,pprec);
    5488           0 :             gel(ck,j) = p1;
    5489             :           }
    5490             :       }
    5491             :       else
    5492        3752 :         for (j=i+1; j<=nbco; j++)
    5493             :         {
    5494        2618 :           pari_sp av2 = avma;
    5495        2618 :           GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j)));
    5496        2618 :           if (diveuc) p1 = mydiv(p1,pprec);
    5497        2618 :           gel(ck,j) = gerepileupto(av2, p1);
    5498             :         }
    5499        1134 :       if (gc_needed(av,2))
    5500             :       {
    5501           0 :         if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5502           0 :         gerepileall(av,2, &a,&pprec);
    5503           0 :         ci = gel(a,i);
    5504           0 :         p = gcoeff(a,i,i);
    5505             :       }
    5506             :     }
    5507             :   }
    5508         196 :   p = gcoeff(a,nbco,nbco);
    5509         196 :   p = (s < 0)? gneg(p): gcopy(p);
    5510         196 :   return gerepileupto(av, p);
    5511             : }
    5512             : 
    5513             : /* count non-zero entries in col j, at most 'max' of them.
    5514             :  * Return their indices */
    5515             : static GEN
    5516         609 : col_count_non_zero(GEN a, long j, long max)
    5517             : {
    5518         609 :   GEN v = cgetg(max+1, t_VECSMALL);
    5519         609 :   GEN c = gel(a,j);
    5520         609 :   long i, l = lg(a), k = 1;
    5521        2695 :   for (i = 1; i < l; i++)
    5522        2632 :     if (!gequal0(gel(c,i)))
    5523             :     {
    5524        2324 :       if (k > max) return NULL; /* fail */
    5525        1778 :       v[k++] = i;
    5526             :     }
    5527          63 :   setlg(v, k); return v;
    5528             : }
    5529             : /* count non-zero entries in row i, at most 'max' of them.
    5530             :  * Return their indices */
    5531             : static GEN
    5532         560 : row_count_non_zero(GEN a, long i, long max)
    5533             : {
    5534         560 :   GEN v = cgetg(max+1, t_VECSMALL);
    5535         560 :   long j, l = lg(a), k = 1;
    5536        2422 :   for (j = 1; j < l; j++)
    5537        2366 :     if (!gequal0(gcoeff(a,i,j)))
    5538             :     {
    5539        2212 :       if (k > max) return NULL; /* fail */
    5540        1708 :       v[k++] = j;
    5541             :     }
    5542          56 :   setlg(v, k); return v;
    5543             : }
    5544             : 
    5545             : static GEN det_develop(GEN a, long max, double bound);
    5546             : /* (-1)^(i+j) a[i,j] * det RgM_minor(a,i,j) */
    5547             : static GEN
    5548         203 : coeff_det(GEN a, long i, long j, long max, double bound)
    5549             : {
    5550         203 :   GEN c = gcoeff(a, i, j);
    5551         203 :   c = gmul(c, det_develop(RgM_minor(a, i,j), max, bound));
    5552         203 :   if (odd(i+j)) c = gneg(c);
    5553         203 :   return c;
    5554             : }
    5555             : /* a square t_MAT, 'bound' a rough upper bound for the number of
    5556             :  * multiplications we are willing to pay while developing rows/columns before
    5557             :  * switching to Gaussian elimination */
    5558             : static GEN
    5559         301 : det_develop(GEN M, long max, double bound)
    5560             : {
    5561         301 :   pari_sp av = avma;
    5562         301 :   long i,j, n = lg(M)-1, lbest = max+2, best_col = 0, best_row = 0;
    5563         301 :   GEN best = NULL;
    5564             : 
    5565         301 :   if (bound < 1.) return det_bareiss(M); /* too costly now */
    5566             : 
    5567         175 :   switch(n)
    5568             :   {
    5569           0 :     case 0: return gen_1;
    5570           0 :     case 1: return gcopy(gcoeff(M,1,1));
    5571          14 :     case 2: return RgM_det2(M);
    5572             :   }
    5573         161 :   if (max > ((n+2)>>1)) max = (n+2)>>1;
    5574         735 :   for (j = 1; j <= n; j++)
    5575             :   {
    5576         609 :     pari_sp av2 = avma;
    5577         609 :     GEN v = col_count_non_zero(M, j, max);
    5578             :     long lv;
    5579         609 :     if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
    5580          63 :     if (lv == 1) { avma = av; return gen_0; }
    5581          63 :     if (lv == 2) {
    5582          35 :       avma = av;
    5583          35 :       return gerepileupto(av, coeff_det(M,v[1],j,max,bound));
    5584             :     }
    5585          28 :     best = v; lbest = lv; best_col = j;
    5586             :   }
    5587         686 :   for (i = 1; i <= n; i++)
    5588             :   {
    5589         560 :     pari_sp av2 = avma;
    5590         560 :     GEN v = row_count_non_zero(M, i, max);
    5591             :     long lv;
    5592         560 :     if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
    5593          42 :     if (lv == 1) { avma = av; return gen_0; }
    5594          42 :     if (lv == 2) {
    5595           0 :       avma = av;
    5596           0 :       return gerepileupto(av, coeff_det(M,i,v[1],max,bound));
    5597             :     }
    5598          42 :     best = v; lbest = lv; best_row = i;
    5599             :   }
    5600         126 :   if (best_row)
    5601             :   {
    5602          42 :     double d = lbest-1;
    5603          42 :     GEN s = NULL;
    5604             :     long k;
    5605          42 :     bound /= d*d*d;
    5606         168 :     for (k = 1; k < lbest; k++)
    5607             :     {
    5608         126 :       GEN c = coeff_det(M, best_row, best[k], max, bound);
    5609         126 :       s = s? gadd(s, c): c;
    5610             :     }
    5611          42 :     return gerepileupto(av, s);
    5612             :   }
    5613          84 :   if (best_col)
    5614             :   {
    5615          14 :     double d = lbest-1;
    5616          14 :     GEN s = NULL;
    5617             :     long k;
    5618          14 :     bound /= d*d*d;
    5619          56 :     for (k = 1; k < lbest; k++)
    5620             :     {
    5621          42 :       GEN c = coeff_det(M, best[k], best_col, max, bound);
    5622          42 :       s = s? gadd(s, c): c;
    5623             :     }
    5624          14 :     return gerepileupto(av, s);
    5625             :   }
    5626          70 :   return det_bareiss(M);
    5627             : }
    5628             : 
    5629             : /* area of parallelogram bounded by (v1,v2) */
    5630             : static GEN
    5631       51464 : parallelogramarea(GEN v1, GEN v2)
    5632       51464 : { return gsub(gmul(gnorml2(v1), gnorml2(v2)), gsqr(RgV_dotproduct(v1, v2))); }
    5633             : 
    5634             : /* Square of Hadamard bound for det(a), a square matrix.
    5635             :  * Slightly improvement: instead of using the column norms, use the area of
    5636             :  * the parallelogram formed by pairs of consecutive vectors */
    5637             : GEN
    5638       16716 : RgM_Hadamard(GEN a)
    5639             : {
    5640       16716 :   pari_sp av = avma;
    5641       16716 :   long n = lg(a)-1, i;
    5642             :   GEN B;
    5643       16716 :   if (n == 0) return gen_1;
    5644       16716 :   if (n == 1) return gsqr(gcoeff(a,1,1));
    5645       16716 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    5646       16716 :   B = gen_1;
    5647       68180 :   for (i = 1; i <= n/2; i++)
    5648       51464 :     B = gmul(B, parallelogramarea(gel(a,2*i-1), gel(a,2*i)));
    5649       16716 :   if (odd(n)) B = gmul(B, gnorml2(gel(a, n)));
    5650       16716 :   return gerepileuptoint(av, ceil_safe(B));
    5651             : }
    5652             : 
    5653             : /* assume dim(a) = n > 0 */
    5654             : static GEN
    5655       27916 : ZM_det_i(GEN M, long n)
    5656             : {
    5657       27916 :   const long DIXON_THRESHOLD = 40;
    5658       27916 :   pari_sp av = avma, av2;
    5659             :   long i;
    5660       27916 :   ulong p, compp, Dp = 1;
    5661             :   forprime_t S;
    5662             :   GEN D, h, q, v, comp;
    5663       27916 :   if (n == 1) return icopy(gcoeff(M,1,1));
    5664       27097 :   if (n == 2) return ZM_det2(M);
    5665       19649 :   if (n == 3) return ZM_det3(M);
    5666       16716 :   h = RgM_Hadamard(M);
    5667       16716 :   if (!signe(h)) { avma = av; return gen_0; }
    5668       16716 :   h = sqrti(h); q = gen_1;
    5669       16716 :   init_modular_big(&S);
    5670       16716 :   p = 0; /* -Wall */
    5671       33432 :   while( cmpii(q, h) <= 0 && (p = u_forprime_next(&S)) )
    5672             :   {
    5673       16716 :     av2 = avma; Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    5674       16716 :     avma = av2;
    5675       16716 :     if (Dp) break;
    5676           0 :     q = muliu(q, p);
    5677             :   }
    5678       16716 :   if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    5679       16716 :   if (!Dp) { avma = av; return gen_0; }
    5680       16716 :   if (n <= DIXON_THRESHOLD)
    5681       16716 :     D = q;
    5682             :   else
    5683             :   {
    5684           0 :     av2 = avma;
    5685           0 :     v = cgetg(n+1, t_COL);
    5686           0 :     gel(v, 1) = gen_1; /* ensure content(v) = 1 */
    5687           0 :     for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    5688           0 :     D = Q_denom(ZM_gauss(M, v));
    5689           0 :     if (expi(D) < expi(h) >> 1)
    5690             :     { /* First try unlucky, try once more */
    5691           0 :       for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    5692           0 :       D = lcmii(D, Q_denom(ZM_gauss(M, v)));
    5693             :     }
    5694           0 :     D = gerepileuptoint(av2, D);
    5695           0 :     if (q != gen_1) D = lcmii(D, q);
    5696             :   }
    5697             :   /* determinant is a multiple of D */
    5698       16716 :   h = shifti(divii(h, D), 1);
    5699             : 
    5700       16716 :   compp = Fl_div(Dp, umodiu(D,p), p);
    5701       16716 :   comp = Z_init_CRT(compp, p);
    5702       16716 :   q = utoipos(p);
    5703       85128 :   while (cmpii(q, h) <= 0)
    5704             :   {
    5705       51696 :     p = u_forprime_next(&S);
    5706       51696 :     if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    5707       51696 :     Dp = umodiu(D, p);
    5708       51696 :     if (!Dp) continue;
    5709       51696 :     av2 = avma;
    5710       51696 :     compp = Fl_div(Flm_det_sp(ZM_to_Flm(M, p), p), Dp, p);
    5711       51696 :     avma = av2;
    5712       51696 :     (void) Z_incremental_CRT(&comp, compp, &q, p);
    5713             :   }
    5714       16716 :   return gerepileuptoint(av, mulii(comp, D));
    5715             : }
    5716             : 
    5717             : static long
    5718          98 : det_init_max(long n)
    5719             : {
    5720          98 :   if (n > 100) return 0;
    5721          98 :   if (n > 50) return 1;
    5722          98 :   if (n > 30) return 4;
    5723          98 :   return 7;
    5724             : }
    5725             : 
    5726             : GEN
    5727       11153 : det(GEN a)
    5728             : {
    5729       11153 :   long n = lg(a)-1;
    5730             :   double B;
    5731       11153 :   GEN data, ff = NULL, p = NULL;
    5732             :   pivot_fun pivot;
    5733             : 
    5734       11153 :   if (typ(a)!=t_MAT) pari_err_TYPE("det",a);
    5735       11153 :   if (!n) return gen_1;
    5736       11111 :   if (n != nbrows(a)) pari_err_DIM("det");
    5737       11104 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5738       10910 :   if (n == 2) return RgM_det2(a);
    5739       10028 :   if (RgM_is_FpM(a, &p))
    5740             :   {
    5741        1722 :     pari_sp av = avma;
    5742             :     ulong pp, d;
    5743        1722 :     if (!p) return ZM_det_i(a, n); /* ZM */
    5744             :     /* FpM */
    5745        1491 :     a = RgM_Fp_init(a,p,&pp);
    5746        1491 :     switch(pp)
    5747             :     {
    5748          49 :     case 0: return gerepileupto(av, Fp_to_mod(FpM_det(a,p),p)); break;
    5749          14 :     case 2: d = F2m_det(a); break;
    5750        1428 :     default:d = Flm_det_sp(a, pp); break;
    5751             :     }
    5752        1442 :     avma = av; return mkintmodu(d, pp);
    5753             :   }
    5754        8306 :   if (RgM_is_FFM(a, &ff)) return FFM_det(a, ff);
    5755        8271 :   pivot = get_pivot_fun(a, a, &data);
    5756        8271 :   if (pivot != gauss_get_pivot_NZ) return det_simple_gauss(a, data, pivot);
    5757          98 :   B = (double)n;
    5758          98 :   return det_develop(a, det_init_max(n), B*B*B);
    5759             : }
    5760             : 
    5761             : GEN
    5762       27685 : ZM_det(GEN a)
    5763             : {
    5764       27685 :   long n = lg(a)-1;
    5765       27685 :   if (!n) return gen_1;
    5766       27685 :   return ZM_det_i(a, n);
    5767             : }
    5768             : 
    5769             : /* return a solution of congruence system sum M_{ i,j } X_j = Y_i mod D_i
    5770             :  * If ptu1 != NULL, put in *ptu1 a Z-basis of the homogeneous system */
    5771             : static GEN
    5772          49 : gaussmoduloall(GEN M, GEN D, GEN Y, GEN *ptu1)
    5773             : {
    5774          49 :   pari_sp av = avma;
    5775             :   long n, m, j, l, lM;
    5776             :   GEN delta, H, U, u1, u2, x;
    5777             : 
    5778          49 :   if (typ(M)!=t_MAT) pari_err_TYPE("gaussmodulo",M);
    5779          49 :   lM = lg(M);
    5780          49 :   if (lM == 1)
    5781             :   {
    5782          28 :     switch(typ(Y))
    5783             :     {
    5784           7 :       case t_INT: break;
    5785          14 :       case t_COL: if (lg(Y) != 1) pari_err_DIM("gaussmodulo");
    5786          14 :                   break;
    5787           7 :       default: pari_err_TYPE("gaussmodulo",Y);
    5788             :     }
    5789          21 :     switch(typ(D))
    5790             :     {
    5791           7 :       case t_INT: break;
    5792           7 :       case t_COL: if (lg(D) != 1) pari_err_DIM("gaussmodulo");
    5793           7 :                   break;
    5794           7 :       default: pari_err_TYPE("gaussmodulo",D);
    5795             :     }
    5796          14 :     if (ptu1) *ptu1 = cgetg(1, t_MAT);
    5797          14 :     return gen_0;
    5798             :   }
    5799          21 :   n = nbrows(M);
    5800          21 :   switch(typ(D))
    5801             :   {
    5802             :     case t_COL:
    5803          14 :       if (lg(D)-1!=n) pari_err_DIM("gaussmodulo");
    5804          14 :       delta = diagonal_shallow(D); break;
    5805           7 :     case t_INT: delta = scalarmat_shallow(D,n); break;
    5806           0 :     default: pari_err_TYPE("gaussmodulo",D);
    5807             :       return NULL; /* LCOV_EXCL_LINE */
    5808             :   }
    5809          21 :   switch(typ(Y))
    5810             :   {
    5811           7 :     case t_INT: Y = const_col(n, Y); break;
    5812             :     case t_COL:
    5813          14 :       if (lg(Y)-1!=n) pari_err_DIM("gaussmodulo");
    5814          14 :       break;
    5815           0 :     default: pari_err_TYPE("gaussmodulo",Y);
    5816             :       return NULL; /* LCOV_EXCL_LINE */
    5817             :   }
    5818          21 :   H = ZM_hnfall_i(shallowconcat(M,delta), &U, 1);
    5819          21 :   Y = hnf_solve(H,Y); if (!Y) return gen_0;
    5820          21 :   l = lg(H); /* may be smaller than lM if some moduli are 0 */
    5821          21 :   n = l-1;
    5822          21 :   m = lg(U)-1 - n;
    5823          21 :   u1 = cgetg(m+1,t_MAT);
    5824          21 :   u2 = cgetg(n+1,t_MAT);
    5825          21 :   for (j=1; j<=m; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u1,j) = c; }
    5826          21 :   U += m;
    5827          21 :   for (j=1; j<=n; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u2,j) = c; }
    5828             :   /*       (u1 u2)
    5829             :    * (M D) (*  * ) = (0 H) */
    5830          21 :   u1 = ZM_lll(u1, 0.75, LLL_INPLACE);
    5831          21 :   Y = ZM_ZC_mul(u2,Y);
    5832          21 :   x = ZC_reducemodmatrix(Y, u1);
    5833          21 :   if (!ptu1) x = gerepileupto(av, x);
    5834             :   else
    5835             :   {
    5836          14 :     gerepileall(av, 2, &x, &u1);
    5837          14 :     *ptu1 = u1;
    5838             :   }
    5839          21 :   return x;
    5840             : }
    5841             : 
    5842             : GEN
    5843          49 : matsolvemod0(GEN M, GEN D, GEN Y, long flag)
    5844             : {
    5845             :   pari_sp av;
    5846             :   GEN p1,y;
    5847             : 
    5848          49 :   if (!flag) return gaussmoduloall(M,D,Y,NULL);
    5849             : 
    5850          42 :   av=avma; y = cgetg(3,t_VEC);
    5851          42 :   p1 = gaussmoduloall(M,D,Y, (GEN*)y+2);
    5852          28 :   if (p1==gen_0) { avma=av; return gen_0; }
    5853          14 :   gel(y,1) = p1; return y;
    5854             : }
    5855             : 
    5856             : GEN
    5857           0 : gaussmodulo2(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,1); }
    5858             : 
    5859             : GEN
    5860           0 : gaussmodulo(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,0); }

Generated by: LCOV version 1.11