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

Generated by: LCOV version 1.11