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 to exceed 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.12.1 lcov report (development 24782-f7724578b4) Lines: 2661 3078 86.5 %
Date: 2019-12-06 05:56:26 Functions: 288 310 92.9 %
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           0 : gerepile_mat(pari_sp av, pari_sp tetpil, GEN x, long k, long m, long n, long t)
      31             : {
      32           0 :   pari_sp A, bot = pari_mainstack->bot;
      33             :   long u, i;
      34             :   size_t dec;
      35             : 
      36           0 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      37             : 
      38           0 :   for (u=t+1; u<=m; u++)
      39             :   {
      40           0 :     A = (pari_sp)coeff(x,u,k);
      41           0 :     if (A < av && A >= bot) coeff(x,u,k) += dec;
      42             :   }
      43           0 :   for (i=k+1; i<=n; i++)
      44           0 :     for (u=1; u<=m; u++)
      45             :     {
      46           0 :       A = (pari_sp)coeff(x,u,i);
      47           0 :       if (A < av && A >= bot) coeff(x,u,i) += dec;
      48             :     }
      49           0 : }
      50             : 
      51             : static void
      52           0 : gen_gerepile_gauss_ker(GEN x, long k, long t, pari_sp av, void *E, GEN (*copy)(void*, GEN))
      53             : {
      54           0 :   pari_sp tetpil = avma;
      55           0 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      56             : 
      57           0 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot_ker. k=%ld, n=%ld",k,n);
      58           0 :   for (u=t+1; u<=m; u++) gcoeff(x,u,k) = copy(E,gcoeff(x,u,k));
      59           0 :   for (i=k+1; i<=n; i++)
      60           0 :     for (u=1; u<=m; u++) gcoeff(x,u,i) = copy(E,gcoeff(x,u,i));
      61           0 :   gerepile_mat(av,tetpil,x,k,m,n,t);
      62           0 : }
      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           0 : _copy(void *E, GEN x)
      72             : {
      73           0 :   (void) E; COPY(x);
      74           0 :   return x;
      75             : }
      76             : 
      77             : static void
      78           0 : gerepile_gauss_ker(GEN x, long k, long t, pari_sp av)
      79             : {
      80           0 :   gen_gerepile_gauss_ker(x, k, t, av, NULL, &_copy);
      81           0 : }
      82             : 
      83             : static void
      84           0 : gerepile_gauss(GEN x,long k,long t,pari_sp av, long j, GEN c)
      85             : {
      86           0 :   pari_sp tetpil = avma, A, bot;
      87           0 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      88             :   size_t dec;
      89             : 
      90           0 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot. k=%ld, n=%ld",k,n);
      91           0 :   for (u=t+1; u<=m; u++)
      92           0 :     if (u==j || !c[u]) COPY(gcoeff(x,u,k));
      93           0 :   for (u=1; u<=m; u++)
      94           0 :     if (u==j || !c[u])
      95           0 :       for (i=k+1; i<=n; i++) COPY(gcoeff(x,u,i));
      96             : 
      97           0 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      98           0 :   bot = pari_mainstack->bot;
      99           0 :   for (u=t+1; u<=m; u++)
     100           0 :     if (u==j || !c[u])
     101             :     {
     102           0 :       A=(pari_sp)coeff(x,u,k);
     103           0 :       if (A<av && A>=bot) coeff(x,u,k)+=dec;
     104             :     }
     105           0 :   for (u=1; u<=m; u++)
     106           0 :     if (u==j || !c[u])
     107           0 :       for (i=k+1; i<=n; i++)
     108             :       {
     109           0 :         A=(pari_sp)coeff(x,u,i);
     110           0 :         if (A<av && A>=bot) coeff(x,u,i)+=dec;
     111             :       }
     112           0 : }
     113             : 
     114             : /*******************************************************************/
     115             : /*                                                                 */
     116             : /*                         GENERIC                                 */
     117             : /*                                                                 */
     118             : /*******************************************************************/
     119             : GEN
     120        1465 : gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff)
     121             : {
     122        1465 :   pari_sp av0 = avma, av, tetpil;
     123             :   GEN y, c, d;
     124             :   long i, j, k, r, t, n, m;
     125             : 
     126        1465 :   n=lg(x)-1; if (!n) return cgetg(1,t_MAT);
     127        1465 :   m=nbrows(x); r=0;
     128        1465 :   x = RgM_shallowcopy(x);
     129        1465 :   c = zero_zv(m);
     130        1465 :   d=new_chunk(n+1);
     131        1465 :   av=avma;
     132        5271 :   for (k=1; k<=n; k++)
     133             :   {
     134       11055 :     for (j=1; j<=m; j++)
     135        9417 :       if (!c[j])
     136             :       {
     137        6513 :         gcoeff(x,j,k) = ff->red(E, gcoeff(x,j,k));
     138        6513 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     139             :       }
     140        3834 :     if (j>m)
     141             :     {
     142        1638 :       if (deplin)
     143             :       {
     144          28 :         GEN c = cgetg(n+1, t_COL), g0 = ff->s(E,0), g1=ff->s(E,1);
     145          28 :         for (i=1; i<k; i++) gel(c,i) = ff->red(E, gcoeff(x,d[i],k));
     146          28 :         gel(c,k) = g1; for (i=k+1; i<=n; i++) gel(c,i) = g0;
     147          28 :         return gerepileupto(av0, c);
     148             :       }
     149        1610 :       r++; d[k]=0;
     150        3989 :       for(j=1; j<k; j++)
     151        2379 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
     152             :     }
     153             :     else
     154             :     {
     155        2196 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     156        2196 :       c[j] = k; d[k] = j;
     157        2196 :       gcoeff(x,j,k) = ff->s(E,-1);
     158        2196 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     159       11088 :       for (t=1; t<=m; t++)
     160             :       {
     161        8892 :         if (t==j) continue;
     162             : 
     163        6696 :         piv = ff->red(E,gcoeff(x,t,k));
     164        6696 :         if (ff->equal0(piv)) continue;
     165             : 
     166        1754 :         gcoeff(x,t,k) = ff->s(E,0);
     167        4454 :         for (i=k+1; i<=n; i++)
     168        5400 :            gcoeff(x,t,i) = ff->red(E, ff->add(E, gcoeff(x,t,i),
     169        2700 :                                       ff->mul(E,piv,gcoeff(x,j,i))));
     170        1754 :         if (gc_needed(av,1))
     171           0 :           gen_gerepile_gauss_ker(x,k,t,av,E,ff->red);
     172             :       }
     173             :     }
     174             :   }
     175        1437 :   if (deplin) return gc_NULL(av0);
     176             : 
     177        1409 :   tetpil=avma; y=cgetg(r+1,t_MAT);
     178        3019 :   for (j=k=1; j<=r; j++,k++)
     179             :   {
     180        1610 :     GEN C = cgetg(n+1,t_COL);
     181        1610 :     GEN g0 = ff->s(E,0), g1 = ff->s(E,1);
     182        1610 :     gel(y,j) = C; while (d[k]) k++;
     183        3989 :     for (i=1; i<k; i++)
     184        2379 :       if (d[i])
     185             :       {
     186        1985 :         GEN p1=gcoeff(x,d[i],k);
     187        1985 :         gel(C,i) = ff->red(E,p1); gunclone(p1);
     188             :       }
     189             :       else
     190         394 :         gel(C,i) = g0;
     191        1610 :     gel(C,k) = g1; for (i=k+1; i<=n; i++) gel(C,i) = g0;
     192             :   }
     193        1409 :   return gerepile(av0,tetpil,y);
     194             : }
     195             : 
     196             : GEN
     197        1500 : gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff)
     198             : {
     199             :   pari_sp av;
     200             :   GEN c, d;
     201        1500 :   long i, j, k, r, t, m, n = lg(x)-1;
     202             : 
     203        1500 :   if (!n) { *rr = 0; return NULL; }
     204             : 
     205        1500 :   m=nbrows(x); r=0;
     206        1500 :   d = cgetg(n+1, t_VECSMALL);
     207        1500 :   x = RgM_shallowcopy(x);
     208        1500 :   c = zero_zv(m);
     209        1500 :   av=avma;
     210        5572 :   for (k=1; k<=n; k++)
     211             :   {
     212       10611 :     for (j=1; j<=m; j++)
     213       10318 :       if (!c[j])
     214             :       {
     215        6969 :         gcoeff(x,j,k) = ff->red(E,gcoeff(x,j,k));
     216        6969 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     217             :       }
     218        4072 :     if (j>m) { r++; d[k]=0; }
     219             :     else
     220             :     {
     221        3779 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     222        3779 :       GEN g0 = ff->s(E,0);
     223        3779 :       c[j] = k; d[k] = j;
     224        3779 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     225       23599 :       for (t=1; t<=m; t++)
     226             :       {
     227       19820 :         if (c[t]) continue; /* already a pivot on that line */
     228             : 
     229       12262 :         piv = ff->red(E,gcoeff(x,t,k));
     230       12262 :         if (ff->equal0(piv)) continue;
     231        4873 :         gcoeff(x,t,k) = g0;
     232        8870 :         for (i=k+1; i<=n; i++)
     233        3997 :           gcoeff(x,t,i) = ff->red(E, ff->add(E,gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i))));
     234        4873 :         if (gc_needed(av,1))
     235           0 :           gerepile_gauss(x,k,t,av,j,c);
     236             :       }
     237        3779 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = g0; /* dummy */
     238             :     }
     239             :   }
     240        1500 :   *rr = r; set_avma((pari_sp)d); return d;
     241             : }
     242             : 
     243             : GEN
     244         294 : gen_det(GEN a, void *E, const struct bb_field *ff)
     245             : {
     246         294 :   pari_sp av = avma;
     247         294 :   long i,j,k, s = 1, nbco = lg(a)-1;
     248         294 :   GEN x = ff->s(E,1);
     249         294 :   if (!nbco) return x;
     250         287 :   a = RgM_shallowcopy(a);
     251        1064 :   for (i=1; i<nbco; i++)
     252             :   {
     253             :     GEN q;
     254        1029 :     for(k=i; k<=nbco; k++)
     255             :     {
     256         994 :       gcoeff(a,k,i) = ff->red(E,gcoeff(a,k,i));
     257         994 :       if (!ff->equal0(gcoeff(a,k,i))) break;
     258             :     }
     259         812 :     if (k > nbco) return gerepileupto(av, gcoeff(a,i,i));
     260         777 :     if (k != i)
     261             :     { /* exchange the lines s.t. k = i */
     262         105 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     263         105 :       s = -s;
     264             :     }
     265         777 :     q = gcoeff(a,i,i);
     266         777 :     x = ff->red(E,ff->mul(E,x,q));
     267         777 :     q = ff->inv(E,q);
     268        2324 :     for (k=i+1; k<=nbco; k++)
     269             :     {
     270        1547 :       GEN m = ff->red(E,gcoeff(a,i,k));
     271        1547 :       if (ff->equal0(m)) continue;
     272        1092 :       m = ff->neg(E, ff->red(E,ff->mul(E,m, q)));
     273        3528 :       for (j=i+1; j<=nbco; j++)
     274        4872 :         gcoeff(a,j,k) = ff->red(E, ff->add(E, gcoeff(a,j,k),
     275        2436 :                                    ff->mul(E, m, gcoeff(a,j,i))));
     276             :     }
     277         777 :     if (gc_needed(av,2))
     278             :     {
     279           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
     280           0 :       gerepileall(av,2, &a,&x);
     281             :     }
     282             :   }
     283         252 :   if (s < 0) x = ff->neg(E,x);
     284         252 :   return gerepileupto(av, ff->red(E,ff->mul(E, x, gcoeff(a,nbco,nbco))));
     285             : }
     286             : 
     287             : INLINE void
     288      147486 : _gen_addmul(GEN b, long k, long i, GEN m, void *E, const struct bb_field *ff)
     289             : {
     290      147486 :   gel(b,i) = ff->red(E,gel(b,i));
     291      147486 :   gel(b,k) = ff->add(E,gel(b,k), ff->mul(E,m, gel(b,i)));
     292      147486 : }
     293             : 
     294             : static GEN
     295       54005 : _gen_get_col(GEN a, GEN b, long li, void *E, const struct bb_field *ff)
     296             : {
     297       54005 :   GEN u = cgetg(li+1,t_COL);
     298       54005 :   pari_sp av = avma;
     299             :   long i, j;
     300             : 
     301       54005 :   gel(u,li) = gerepileupto(av, ff->red(E,ff->mul(E,gel(b,li), gcoeff(a,li,li))));
     302      274426 :   for (i=li-1; i>0; i--)
     303             :   {
     304      220421 :     pari_sp av = avma;
     305      220421 :     GEN m = gel(b,i);
     306      220421 :     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))));
     307      220421 :     m = ff->red(E, m);
     308      220421 :     gel(u,i) = gerepileupto(av, ff->red(E,ff->mul(E,m, gcoeff(a,i,i))));
     309             :   }
     310       54005 :   return u;
     311             : }
     312             : 
     313             : GEN
     314       12245 : gen_Gauss(GEN a, GEN b, void *E, const struct bb_field *ff)
     315             : {
     316             :   long i, j, k, li, bco, aco;
     317       12245 :   GEN u, g0 = ff->s(E,0);
     318       12245 :   pari_sp av = avma;
     319       12245 :   a = RgM_shallowcopy(a);
     320       12245 :   b = RgM_shallowcopy(b);
     321       12245 :   aco = lg(a)-1; bco = lg(b)-1; li = nbrows(a);
     322       52864 :   for (i=1; i<=aco; i++)
     323             :   {
     324             :     GEN invpiv;
     325       63189 :     for (k = i; k <= li; k++)
     326             :     {
     327       63133 :       GEN piv = ff->red(E,gcoeff(a,k,i));
     328       63133 :       if (!ff->equal0(piv)) { gcoeff(a,k,i) = ff->inv(E,piv); break; }
     329       10325 :       gcoeff(a,k,i) = g0;
     330             :     }
     331             :     /* found a pivot on line k */
     332       52864 :     if (k > li) return NULL;
     333       52808 :     if (k != i)
     334             :     { /* swap lines so that k = i */
     335        8337 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     336        8337 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
     337             :     }
     338       52808 :     if (i == aco) break;
     339             : 
     340       40619 :     invpiv = gcoeff(a,i,i); /* 1/piv mod p */
     341      151582 :     for (k=i+1; k<=li; k++)
     342             :     {
     343      110963 :       GEN m = ff->red(E,gcoeff(a,k,i)); gcoeff(a,k,i) = g0;
     344      110963 :       if (ff->equal0(m)) continue;
     345             : 
     346       18464 :       m = ff->red(E,ff->neg(E,ff->mul(E,m, invpiv)));
     347       18464 :       for (j=i+1; j<=aco; j++) _gen_addmul(gel(a,j),k,i,m,E,ff);
     348       18464 :       for (j=1  ; j<=bco; j++) _gen_addmul(gel(b,j),k,i,m,E,ff);
     349             :     }
     350       40619 :     if (gc_needed(av,1))
     351             :     {
     352           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"gen_Gauss. i=%ld",i);
     353           0 :       gerepileall(av,2, &a,&b);
     354             :     }
     355             :   }
     356             : 
     357       12189 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
     358       12189 :   u = cgetg(bco+1,t_MAT);
     359       12189 :   for (j=1; j<=bco; j++) gel(u,j) = _gen_get_col(a, gel(b,j), aco, E, ff);
     360       12189 :   return u;
     361             : }
     362             : 
     363             : /* compatible t_MAT * t_COL, lgA = lg(A) = lg(B) > 1, l = lgcols(A) */
     364             : static GEN
     365      557559 : gen_matcolmul_i(GEN A, GEN B, ulong lgA, ulong l,
     366             :                 void *E, const struct bb_field *ff)
     367             : {
     368      557559 :   GEN C = cgetg(l, t_COL);
     369             :   ulong i;
     370     3682063 :   for (i = 1; i < l; i++) {
     371     3124504 :     pari_sp av = avma;
     372     3124504 :     GEN e = ff->mul(E, gcoeff(A, i, 1), gel(B, 1));
     373             :     ulong k;
     374    12791070 :     for(k = 2; k < lgA; k++)
     375     9666566 :       e = ff->add(E, e, ff->mul(E, gcoeff(A, i, k), gel(B, k)));
     376     3124504 :     gel(C, i) = gerepileupto(av, ff->red(E, e));
     377             :   }
     378      557559 :   return C;
     379             : }
     380             : 
     381             : GEN
     382      170324 : gen_matcolmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     383             : {
     384      170324 :   ulong lgA = lg(A);
     385      170324 :   if (lgA != (ulong)lg(B))
     386           0 :     pari_err_OP("operation 'gen_matcolmul'", A, B);
     387      170324 :   if (lgA == 1)
     388           0 :     return cgetg(1, t_COL);
     389      170324 :   return gen_matcolmul_i(A, B, lgA, lgcols(A), E, ff);
     390             : }
     391             : 
     392             : static GEN
     393       75410 : gen_matmul_classical(GEN A, GEN B, long l, long la, long lb,
     394             :                      void *E, const struct bb_field *ff)
     395             : {
     396             :   long j;
     397       75410 :   GEN C = cgetg(lb, t_MAT);
     398      462645 :   for(j = 1; j < lb; j++)
     399      387235 :     gel(C, j) = gen_matcolmul_i(A, gel(B, j), la, l, E, ff);
     400       75410 :   return C;
     401             : }
     402             : 
     403             : /* Strassen-Winograd algorithm */
     404             : 
     405             : /*
     406             :   Return A[ma+1..ma+da, na+1..na+ea] - B[mb+1..mb+db, nb+1..nb+eb]
     407             :   as an (m x n)-matrix, padding the input with zeroes as necessary.
     408             : */
     409             : static GEN
     410           0 : add_slices(long m, long n,
     411             :            GEN A, long ma, long da, long na, long ea,
     412             :            GEN B, long mb, long db, long nb, long eb,
     413             :            void *E, const struct bb_field *ff)
     414             : {
     415           0 :   long min_d = minss(da, db), min_e = minss(ea, eb), i, j;
     416           0 :   GEN M = cgetg(n + 1, t_MAT), C;
     417             : 
     418           0 :   for (j = 1; j <= min_e; j++) {
     419           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     420           0 :     for (i = 1; i <= min_d; i++)
     421           0 :       gel(C, i) = ff->add(E, gcoeff(A, ma + i, na + j),
     422           0 :                           gcoeff(B, mb + i, nb + j));
     423           0 :     for (; i <= da; i++)
     424           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     425           0 :     for (; i <= db; i++)
     426           0 :       gel(C, i) = gcoeff(B, mb + i, nb + j);
     427           0 :     for (; i <= m; i++)
     428           0 :       gel(C, i) = ff->s(E, 0);
     429             :   }
     430           0 :   for (; j <= ea; j++) {
     431           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     432           0 :     for (i = 1; i <= da; i++)
     433           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     434           0 :     for (; i <= m; i++)
     435           0 :       gel(C, i) = ff->s(E, 0);
     436             :   }
     437           0 :   for (; j <= eb; j++) {
     438           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     439           0 :     for (i = 1; i <= db; i++)
     440           0 :       gel(C, i) = gcoeff(B, mb + i, nb + j);
     441           0 :     for (; i <= m; i++)
     442           0 :       gel(C, i) = ff->s(E, 0);
     443             :   }
     444           0 :   for (; j <= n; j++) {
     445           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     446           0 :     for (i = 1; i <= m; i++)
     447           0 :       gel(C, i) = ff->s(E, 0);
     448             :   }
     449           0 :   return M;
     450             : }
     451             : 
     452             : /*
     453             :   Return A[ma+1..ma+da, na+1..na+ea] - B[mb+1..mb+db, nb+1..nb+eb]
     454             :   as an (m x n)-matrix, padding the input with zeroes as necessary.
     455             : */
     456             : static GEN
     457           0 : subtract_slices(long m, long n,
     458             :                 GEN A, long ma, long da, long na, long ea,
     459             :                 GEN B, long mb, long db, long nb, long eb,
     460             :                 void *E, const struct bb_field *ff)
     461             : {
     462           0 :   long min_d = minss(da, db), min_e = minss(ea, eb), i, j;
     463           0 :   GEN M = cgetg(n + 1, t_MAT), C;
     464             : 
     465           0 :   for (j = 1; j <= min_e; j++) {
     466           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     467           0 :     for (i = 1; i <= min_d; i++)
     468           0 :       gel(C, i) = ff->add(E, gcoeff(A, ma + i, na + j),
     469           0 :                           ff->neg(E, gcoeff(B, mb + i, nb + j)));
     470           0 :     for (; i <= da; i++)
     471           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     472           0 :     for (; i <= db; i++)
     473           0 :       gel(C, i) = ff->neg(E, gcoeff(B, mb + i, nb + j));
     474           0 :     for (; i <= m; i++)
     475           0 :       gel(C, i) = ff->s(E, 0);
     476             :   }
     477           0 :   for (; j <= ea; j++) {
     478           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     479           0 :     for (i = 1; i <= da; i++)
     480           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     481           0 :     for (; i <= m; i++)
     482           0 :       gel(C, i) = ff->s(E, 0);
     483             :   }
     484           0 :   for (; j <= eb; j++) {
     485           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     486           0 :     for (i = 1; i <= db; i++)
     487           0 :       gel(C, i) = ff->neg(E, gcoeff(B, mb + i, nb + j));
     488           0 :     for (; i <= m; i++)
     489           0 :       gel(C, i) = ff->s(E, 0);
     490             :   }
     491           0 :   for (; j <= n; j++) {
     492           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     493           0 :     for (i = 1; i <= m; i++)
     494           0 :       gel(C, i) = ff->s(E, 0);
     495             :   }
     496           0 :   return M;
     497             : }
     498             : 
     499             : static GEN gen_matmul_i(GEN A, GEN B, long l, long la, long lb,
     500             :                         void *E, const struct bb_field *ff);
     501             : 
     502             : static GEN
     503           0 : gen_matmul_sw(GEN A, GEN B, long m, long n, long p,
     504             :               void *E, const struct bb_field *ff)
     505             : {
     506           0 :   pari_sp av = avma;
     507           0 :   long m1 = (m + 1)/2, m2 = m/2,
     508           0 :     n1 = (n + 1)/2, n2 = n/2,
     509           0 :     p1 = (p + 1)/2, p2 = p/2;
     510             :   GEN A11, A12, A22, B11, B21, B22,
     511             :     S1, S2, S3, S4, T1, T2, T3, T4,
     512             :     M1, M2, M3, M4, M5, M6, M7,
     513             :     V1, V2, V3, C11, C12, C21, C22, C;
     514             : 
     515           0 :   T2 = subtract_slices(n1, p2, B, 0, n1, p1, p2, B, n1, n2, p1, p2, E, ff);
     516           0 :   S1 = subtract_slices(m2, n1, A, m1, m2, 0, n1, A, 0, m2, 0, n1, E, ff);
     517           0 :   M2 = gen_matmul_i(S1, T2, m2 + 1, n1 + 1, p2 + 1, E, ff);
     518           0 :   if (gc_needed(av, 1))
     519           0 :     gerepileall(av, 2, &T2, &M2);  /* destroy S1 */
     520           0 :   T3 = subtract_slices(n1, p1, T2, 0, n1, 0, p2, B, 0, n1, 0, p1, E, ff);
     521           0 :   if (gc_needed(av, 1))
     522           0 :     gerepileall(av, 2, &M2, &T3);  /* destroy T2 */
     523           0 :   S2 = add_slices(m2, n1, A, m1, m2, 0, n1, A, m1, m2, n1, n2, E, ff);
     524           0 :   T1 = subtract_slices(n1, p1, B, 0, n1, p1, p2, B, 0, n1, 0, p2, E, ff);
     525           0 :   M3 = gen_matmul_i(S2, T1, m2 + 1, n1 + 1, p2 + 1, E, ff);
     526           0 :   if (gc_needed(av, 1))
     527           0 :     gerepileall(av, 4, &M2, &T3, &S2, &M3);  /* destroy T1 */
     528           0 :   S3 = subtract_slices(m1, n1, S2, 0, m2, 0, n1, A, 0, m1, 0, n1, E, ff);
     529           0 :   if (gc_needed(av, 1))
     530           0 :     gerepileall(av, 4, &M2, &T3, &M3, &S3);  /* destroy S2 */
     531           0 :   A11 = matslice(A, 1, m1, 1, n1);
     532           0 :   B11 = matslice(B, 1, n1, 1, p1);
     533           0 :   M1 = gen_matmul_i(A11, B11, m1 + 1, n1 + 1, p1 + 1, E, ff);
     534           0 :   if (gc_needed(av, 1))
     535           0 :     gerepileall(av, 5, &M2, &T3, &M3, &S3, &M1);  /* destroy A11, B11 */
     536           0 :   A12 = matslice(A, 1, m1, n1 + 1, n);
     537           0 :   B21 = matslice(B, n1 + 1, n, 1, p1);
     538           0 :   M4 = gen_matmul_i(A12, B21, m1 + 1, n2 + 1, p1 + 1, E, ff);
     539           0 :   if (gc_needed(av, 1))
     540           0 :     gerepileall(av, 6, &M2, &T3, &M3, &S3, &M1, &M4);  /* destroy A12, B21 */
     541           0 :   C11 = add_slices(m1, p1, M1, 0, m1, 0, p1, M4, 0, m1, 0, p1, E, ff);
     542           0 :   if (gc_needed(av, 1))
     543           0 :     gerepileall(av, 6, &M2, &T3, &M3, &S3, &M1, &C11);  /* destroy M4 */
     544           0 :   M5 = gen_matmul_i(S3, T3, m1 + 1, n1 + 1, p1 + 1, E, ff);
     545           0 :   S4 = subtract_slices(m1, n2, A, 0, m1, n1, n2, S3, 0, m1, 0, n2, E, ff);
     546           0 :   if (gc_needed(av, 1))
     547           0 :     gerepileall(av, 7, &M2, &T3, &M3, &M1, &C11, &M5, &S4);  /* destroy S3 */
     548           0 :   T4 = add_slices(n2, p1, B, n1, n2, 0, p1, T3, 0, n2, 0, p1, E, ff);
     549           0 :   if (gc_needed(av, 1))
     550           0 :     gerepileall(av, 7, &M2, &M3, &M1, &C11, &M5, &S4, &T4);  /* destroy T3 */
     551           0 :   V1 = subtract_slices(m1, p1, M1, 0, m1, 0, p1, M5, 0, m1, 0, p1, E, ff);
     552           0 :   if (gc_needed(av, 1))
     553           0 :     gerepileall(av, 6, &M2, &M3, &S4, &T4, &C11, &V1);  /* destroy M1, M5 */
     554           0 :   B22 = matslice(B, n1 + 1, n, p1 + 1, p);
     555           0 :   M6 = gen_matmul_i(S4, B22, m1 + 1, n2 + 1, p2 + 1, E, ff);
     556           0 :   if (gc_needed(av, 1))
     557           0 :     gerepileall(av, 6, &M2, &M3, &T4, &C11, &V1, &M6);  /* destroy S4, B22 */
     558           0 :   A22 = matslice(A, m1 + 1, m, n1 + 1, n);
     559           0 :   M7 = gen_matmul_i(A22, T4, m2 + 1, n2 + 1, p1 + 1, E, ff);
     560           0 :   if (gc_needed(av, 1))
     561           0 :     gerepileall(av, 6, &M2, &M3, &C11, &V1, &M6, &M7);  /* destroy A22, T4 */
     562           0 :   V3 = add_slices(m1, p2, V1, 0, m1, 0, p2, M3, 0, m2, 0, p2, E, ff);
     563           0 :   C12 = add_slices(m1, p2, V3, 0, m1, 0, p2, M6, 0, m1, 0, p2, E, ff);
     564           0 :   if (gc_needed(av, 1))
     565           0 :     gerepileall(av, 6, &M2, &M3, &C11, &V1, &M7, &C12);  /* destroy V3, M6 */
     566           0 :   V2 = add_slices(m2, p1, V1, 0, m2, 0, p1, M2, 0, m2, 0, p2, E, ff);
     567           0 :   if (gc_needed(av, 1))
     568           0 :     gerepileall(av, 5, &M3, &C11, &M7, &C12, &V2);  /* destroy V1, M2 */
     569           0 :   C21 = add_slices(m2, p1, V2, 0, m2, 0, p1, M7, 0, m2, 0, p1, E, ff);
     570           0 :   if (gc_needed(av, 1))
     571           0 :     gerepileall(av, 5, &M3, &C11, &C12, &V2, &C21);  /* destroy M7 */
     572           0 :   C22 = add_slices(m2, p2, V2, 0, m2, 0, p2, M3, 0, m2, 0, p2, E, ff);
     573           0 :   if (gc_needed(av, 1))
     574           0 :     gerepileall(av, 4, &C11, &C12, &C21, &C22);  /* destroy V2, M3 */
     575           0 :   C = mkmat2(mkcol2(C11, C21), mkcol2(C12, C22));
     576           0 :   return gerepileupto(av, matconcat(C));
     577             : }
     578             : 
     579             : /* Strassen-Winograd used for dim >= gen_matmul_sw_bound */
     580             : static const long gen_matmul_sw_bound = 24;
     581             : 
     582             : static GEN
     583       75410 : gen_matmul_i(GEN A, GEN B, long l, long la, long lb,
     584             :              void *E, const struct bb_field *ff)
     585             : {
     586       75410 :   if (l <= gen_matmul_sw_bound
     587           7 :       || la <= gen_matmul_sw_bound
     588           0 :       || lb <= gen_matmul_sw_bound)
     589       75410 :     return gen_matmul_classical(A, B, l, la, lb, E, ff);
     590             :   else
     591           0 :     return gen_matmul_sw(A, B, l - 1, la - 1, lb - 1, E, ff);
     592             : }
     593             : 
     594             : GEN
     595       75410 : gen_matmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     596             : {
     597       75410 :   ulong lgA, lgB = lg(B);
     598       75410 :   if (lgB == 1)
     599           0 :     return cgetg(1, t_MAT);
     600       75410 :   lgA = lg(A);
     601       75410 :   if (lgA != (ulong)lgcols(B))
     602           0 :     pari_err_OP("operation 'gen_matmul'", A, B);
     603       75410 :   if (lgA == 1)
     604           0 :     return zeromat(0, lgB - 1);
     605       75410 :   return gen_matmul_i(A, B, lgcols(A), lgA, lgB, E, ff);
     606             : }
     607             : 
     608             : static GEN
     609       17813 : gen_colneg(GEN A, void *E, const struct bb_field *ff)
     610             : {
     611             :   long i, l;
     612       17813 :   GEN B = cgetg_copy(A, &l);
     613       70066 :   for (i = 1; i < l; i++)
     614       52253 :     gel(B, i) = ff->neg(E, gel(A, i));
     615       17813 :   return B;
     616             : }
     617             : 
     618             : static GEN
     619        3761 : gen_matneg(GEN A, void *E, const struct bb_field *ff)
     620             : {
     621             :   long i, l;
     622        3761 :   GEN B = cgetg_copy(A, &l);
     623       21518 :   for (i = 1; i < l; i++)
     624       17757 :     gel(B, i) = gen_colneg(gel(A, i), E, ff);
     625        3761 :   return B;
     626             : }
     627             : 
     628             : static GEN
     629      273628 : gen_colscalmul(GEN A, GEN b, void *E, const struct bb_field *ff)
     630             : {
     631             :   long i, l;
     632      273628 :   GEN B = cgetg_copy(A, &l);
     633      656051 :   for (i = 1; i < l; i++)
     634      382423 :     gel(B, i) = ff->red(E, ff->mul(E, gel(A, i), b));
     635      273628 :   return B;
     636             : }
     637             : 
     638             : static GEN
     639       48902 : gen_matscalmul(GEN A, GEN b, void *E, const struct bb_field *ff)
     640             : {
     641             :   long i, l;
     642       48902 :   GEN B = cgetg_copy(A, &l);
     643      322530 :   for (i = 1; i < l; i++)
     644      273628 :     gel(B, i) = gen_colscalmul(gel(A, i), b, E, ff);
     645       48902 :   return B;
     646             : }
     647             : 
     648             : static GEN
     649      515335 : gen_colsub(GEN A, GEN C, void *E, const struct bb_field *ff)
     650             : {
     651             :   long i, l;
     652      515335 :   GEN B = cgetg_copy(A, &l);
     653     1920025 :   for (i = 1; i < l; i++)
     654     1404690 :     gel(B, i) = ff->add(E, gel(A, i), ff->neg(E, gel(C, i)));
     655      515335 :   return B;
     656             : }
     657             : 
     658             : static GEN
     659       69792 : gen_matsub(GEN A, GEN C, void *E, const struct bb_field *ff)
     660             : {
     661             :   long i, l;
     662       69792 :   GEN B = cgetg_copy(A, &l);
     663      585127 :   for (i = 1; i < l; i++)
     664      515335 :     gel(B, i) = gen_colsub(gel(A, i), gel(C, i), E, ff);
     665       69792 :   return B;
     666             : }
     667             : 
     668             : static GEN
     669       43096 : gen_zerocol(long n, void* data, const struct bb_field *R)
     670             : {
     671       43096 :   GEN C = cgetg(n+1,t_COL), zero = R->s(data, 0);
     672             :   long i;
     673       43096 :   for (i=1; i<=n; i++) gel(C,i) = zero;
     674       43096 :   return C;
     675             : }
     676             : 
     677             : static GEN
     678       13805 : gen_zeromat(long m, long n, void* data, const struct bb_field *R)
     679             : {
     680       13805 :   GEN M = cgetg(n+1,t_MAT);
     681             :   long i;
     682       13805 :   for (i=1; i<=n; i++) gel(M,i) = gen_zerocol(m, data, R);
     683       13805 :   return M;
     684             : }
     685             : 
     686             : static GEN
     687         140 : gen_colei(long n, long i, void *E, const struct bb_field *S)
     688             : {
     689         140 :   GEN y = cgetg(n+1,t_COL), _0, _1;
     690             :   long j;
     691         140 :   if (n < 0) pari_err_DOMAIN("gen_colei", "dimension","<",gen_0,stoi(n));
     692         140 :   _0 = S->s(E,0);
     693         140 :   _1 = S->s(E,1);
     694        2268 :   for (j=1; j<=n; j++)
     695        2128 :     gel(y, j) = i==j ? _1: _0;
     696         140 :   return y;
     697             : }
     698             : 
     699             : /* assume dim A >= 1, A invertible + upper triangular  */
     700             : static GEN
     701          77 : gen_matinv_upper_ind(GEN A, long index, void *E, const struct bb_field *ff)
     702             : {
     703          77 :   long n = lg(A) - 1, i, j;
     704          77 :   GEN u = cgetg(n + 1, t_COL);
     705         147 :   for (i = n; i > index; i--)
     706          70 :     gel(u, i) = ff->s(E, 0);
     707          77 :   gel(u, i) = ff->inv(E, gcoeff(A, i, i));
     708         147 :   for (i--; i > 0; i--) {
     709          70 :     pari_sp av = avma;
     710          70 :     GEN m = ff->neg(E, ff->mul(E, gcoeff(A, i, i + 1), gel(u, i + 1)));
     711         112 :     for (j = i + 2; j <= n; j++)
     712          42 :       m = ff->add(E, m, ff->neg(E, ff->mul(E, gcoeff(A, i, j), gel(u, j))));
     713          70 :     gel(u, i) = gerepileupto(av, ff->red(E, ff->mul(E, m, ff->inv(E, gcoeff(A, i, i)))));
     714             :   }
     715          77 :   return u;
     716             : }
     717             : 
     718             : static GEN
     719          28 : gen_matinv_upper(GEN A, void *E, const struct bb_field *ff)
     720             : {
     721             :   long i, l;
     722          28 :   GEN B = cgetg_copy(A, &l);
     723         105 :   for (i = 1; i < l; i++)
     724          77 :     gel(B,i) = gen_matinv_upper_ind(A, i, E, ff);
     725          28 :   return B;
     726             : }
     727             : 
     728             : /* find z such that A z = y. Return NULL if no solution */
     729             : GEN
     730           0 : gen_matcolinvimage(GEN A, GEN y, void *E, const struct bb_field *ff)
     731             : {
     732           0 :   pari_sp av = avma;
     733           0 :   long i, l = lg(A);
     734             :   GEN M, x, t;
     735             : 
     736           0 :   M = gen_ker(shallowconcat(A, y), 0, E, ff);
     737           0 :   i = lg(M) - 1;
     738           0 :   if (!i) return gc_NULL(av);
     739             : 
     740           0 :   x = gel(M, i);
     741           0 :   t = gel(x, l);
     742           0 :   if (ff->equal0(t)) return gc_NULL(av);
     743             : 
     744           0 :   t = ff->neg(E, ff->inv(E, t));
     745           0 :   setlg(x, l);
     746           0 :   for (i = 1; i < l; i++)
     747           0 :     gel(x, i) = ff->red(E, ff->mul(E, t, gel(x, i)));
     748           0 :   return gerepilecopy(av, x);
     749             : }
     750             : 
     751             : /* find Z such that A Z = B. Return NULL if no solution */
     752             : GEN
     753          77 : gen_matinvimage(GEN A, GEN B, void *E, const struct bb_field *ff)
     754             : {
     755          77 :   pari_sp av = avma;
     756             :   GEN d, x, X, Y;
     757             :   long i, j, nY, nA, nB;
     758          77 :   x = gen_ker(shallowconcat(gen_matneg(A, E, ff), B), 0, E, ff);
     759             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
     760             :    * We must find T such that Y T = Id_nB then X T = Z. This exists
     761             :    * iff Y has at least nB columns and full rank. */
     762          77 :   nY = lg(x) - 1;
     763          77 :   nB = lg(B) - 1;
     764          77 :   if (nY < nB) return gc_NULL(av);
     765          77 :   nA = lg(A) - 1;
     766          77 :   Y = rowslice(x, nA + 1, nA + nB); /* nB rows */
     767          77 :   d = cgetg(nB + 1, t_VECSMALL);
     768         182 :   for (i = nB, j = nY; i >= 1; i--, j--) {
     769         224 :     for (; j >= 1; j--)
     770         175 :       if (!ff->equal0(gcoeff(Y, i, j))) { d[i] = j; break; }
     771         154 :     if (!j) return gc_NULL(av);
     772             :   }
     773             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
     774          28 :   Y = vecpermute(Y, d);
     775          28 :   x = vecpermute(x, d);
     776          28 :   X = rowslice(x, 1, nA);
     777          28 :   return gerepileupto(av, gen_matmul(X, gen_matinv_upper(Y, E, ff), E, ff));
     778             : }
     779             : 
     780             : static GEN
     781       89770 : image_from_pivot(GEN x, GEN d, long r)
     782             : {
     783             :   GEN y;
     784             :   long j, k;
     785             : 
     786       89770 :   if (!d) return gcopy(x);
     787             :   /* d left on stack for efficiency */
     788       87831 :   r = lg(x)-1 - r; /* = dim Im(x) */
     789       87831 :   y = cgetg(r+1,t_MAT);
     790      723450 :   for (j=k=1; j<=r; k++)
     791      635619 :     if (d[k]) gel(y,j++) = gcopy(gel(x,k));
     792       87831 :   return y;
     793             : }
     794             : 
     795             : /* r = dim Ker x, n = nbrows(x) */
     796             : static GEN
     797       43061 : get_suppl(GEN x, GEN d, long n, long r, GEN(*ei)(long,long))
     798             : {
     799             :   pari_sp av;
     800             :   GEN y, c;
     801       43061 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
     802             : 
     803       43061 :   if (rx == n && r == 0) return gcopy(x);
     804       39197 :   y = cgetg(n+1, t_MAT);
     805       39197 :   av = avma; c = zero_zv(n);
     806             :   /* c = lines containing pivots (could get it from gauss_pivot, but cheap)
     807             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
     808      277968 :   for (k = j = 1; j<=rx; j++)
     809      238771 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gel(x,j); }
     810      377182 :   for (j=1; j<=n; j++)
     811      337985 :     if (!c[j]) gel(y,k++) = (GEN)j; /* HACK */
     812       39197 :   set_avma(av);
     813             : 
     814       39197 :   rx -= r;
     815       39197 :   for (j=1; j<=rx; j++) gel(y,j) = gcopy(gel(y,j));
     816       39197 :   for (   ; j<=n; j++)  gel(y,j) = ei(n, y[j]);
     817       39197 :   return y;
     818             : }
     819             : 
     820             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
     821             : static GEN
     822      105837 : indexrank0(long n, long r, GEN d)
     823             : {
     824      105837 :   GEN p1, p2, res = cgetg(3,t_VEC);
     825             :   long i, j;
     826             : 
     827      105837 :   r = n - r; /* now r = dim Im(x) */
     828      105837 :   p1 = cgetg(r+1,t_VECSMALL); gel(res,1) = p1;
     829      105837 :   p2 = cgetg(r+1,t_VECSMALL); gel(res,2) = p2;
     830      105837 :   if (d)
     831             :   {
     832      593317 :     for (i=0,j=1; j<=n; j++)
     833      488712 :       if (d[j]) { i++; p1[i] = d[j]; p2[i] = j; }
     834      104605 :     vecsmall_sort(p1);
     835             :   }
     836      105837 :   return res;
     837             : }
     838             : 
     839             : /*******************************************************************/
     840             : /*                                                                 */
     841             : /*                Echelon form and CUP decomposition               */
     842             : /*                                                                 */
     843             : /*******************************************************************/
     844             : 
     845             : /* By Peter Bruin, based on
     846             :   C.-P. Jeannerod, C. Pernet and A. Storjohann, Rank-profile revealing
     847             :   Gaussian elimination and the CUP matrix decomposition.  J. Symbolic
     848             :   Comput. 56 (2013), 46-68.
     849             : 
     850             :   Decompose an m x n-matrix A of rank r as C*U*P, with
     851             :   - C: m x r-matrix in column echelon form (not necessarily reduced)
     852             :        with all pivots equal to 1
     853             :   - U: upper-triangular r x n-matrix
     854             :   - P: permutation matrix
     855             :   The pivots of C and the known zeroes in C and U are not necessarily
     856             :   filled in; instead, we also return the vector R of pivot rows.
     857             :   Instead of the matrix P, we return the permutation p of [1..n]
     858             :   (t_VECSMALL) such that P[i,j] = 1 if and only if j = p[i].
     859             : */
     860             : 
     861             : /* complement of a strictly increasing subsequence of (1, 2, ..., n) */
     862             : static GEN
     863       16216 : indexcompl(GEN v, long n)
     864             : {
     865       16216 :   long i, j, k, m = lg(v) - 1;
     866       16216 :   GEN w = cgetg(n - m + 1, t_VECSMALL);
     867      149969 :   for (i = j = k = 1; i <= n; i++)
     868      133753 :     if (j <= m && v[j] == i) j++; else w[k++] = i;
     869       16216 :   return w;
     870             : }
     871             : 
     872             : static GEN
     873        3836 : gen_solve_upper_1(GEN U, GEN B, void *E, const struct bb_field *ff)
     874        3836 : { return gen_matscalmul(B, ff->inv(E, gcoeff(U, 1, 1)), E, ff); }
     875             : 
     876             : static GEN
     877        2054 : gen_rsolve_upper_2(GEN U, GEN B, void *E, const struct bb_field *ff)
     878             : {
     879        2054 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     880        2054 :   GEN D = ff->red(E, ff->mul(E, a, d)), Dinv = ff->inv(E, D);
     881        2054 :   GEN ainv = ff->red(E, ff->mul(E, d, Dinv));
     882        2054 :   GEN dinv = ff->red(E, ff->mul(E, a, Dinv));
     883        2054 :   GEN B1 = rowslice(B, 1, 1);
     884        2054 :   GEN B2 = rowslice(B, 2, 2);
     885        2054 :   GEN X2 = gen_matscalmul(B2, dinv, E, ff);
     886        2054 :   GEN X1 = gen_matscalmul(gen_matsub(B1, gen_matscalmul(X2, b, E, ff), E, ff),
     887             :                           ainv, E, ff);
     888        2054 :   return vconcat(X1, X2);
     889             : }
     890             : 
     891             : /* solve U*X = B,  U upper triangular and invertible */
     892             : static GEN
     893        5385 : gen_rsolve_upper(GEN U, GEN B, void *E, const struct bb_field *ff,
     894             :                  GEN (*mul)(void *E, GEN a, GEN))
     895             : {
     896        5385 :   long n = lg(U) - 1, n1;
     897             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     898        5385 :   pari_sp av = avma;
     899             : 
     900        5385 :   if (n == 0) return B;
     901        5385 :   if (n == 1) return gen_solve_upper_1(U, B, E, ff);
     902        4503 :   if (n == 2) return gen_rsolve_upper_2(U, B, E, ff);
     903        2449 :   n1 = (n + 1)/2;
     904        2449 :   U2 = vecslice(U, n1 + 1, n);
     905        2449 :   U11 = matslice(U, 1,n1, 1,n1);
     906        2449 :   U12 = rowslice(U2, 1, n1);
     907        2449 :   U22 = rowslice(U2, n1 + 1, n);
     908        2449 :   B1 = rowslice(B, 1, n1);
     909        2449 :   B2 = rowslice(B, n1 + 1, n);
     910        2449 :   X2 = gen_rsolve_upper(U22, B2, E, ff, mul);
     911        2449 :   B1 = gen_matsub(B1, mul(E, U12, X2), E, ff);
     912        2449 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B1, &U11, &X2);
     913        2449 :   X1 = gen_rsolve_upper(U11, B1, E, ff, mul);
     914        2449 :   X = vconcat(X1, X2);
     915        2449 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     916        2449 :   return X;
     917             : }
     918             : 
     919             : static GEN
     920        5668 : gen_lsolve_upper_2(GEN U, GEN B, void *E, const struct bb_field *ff)
     921             : {
     922        5668 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     923        5668 :   GEN D = ff->red(E, ff->mul(E, a, d)), Dinv = ff->inv(E, D);
     924        5668 :   GEN ainv = ff->red(E, ff->mul(E, d, Dinv)), dinv = ff->red(E, ff->mul(E, a, Dinv));
     925        5668 :   GEN B1 = vecslice(B, 1, 1);
     926        5668 :   GEN B2 = vecslice(B, 2, 2);
     927        5668 :   GEN X1 = gen_matscalmul(B1, ainv, E, ff);
     928        5668 :   GEN X2 = gen_matscalmul(gen_matsub(B2, gen_matscalmul(X1, b, E, ff), E, ff), dinv, E, ff);
     929        5668 :   return shallowconcat(X1, X2);
     930             : }
     931             : 
     932             : /* solve X*U = B,  U upper triangular and invertible */
     933             : static GEN
     934       13130 : gen_lsolve_upper(GEN U, GEN B, void *E, const struct bb_field *ff,
     935             :                  GEN (*mul)(void *E, GEN a, GEN))
     936             : {
     937       13130 :   long n = lg(U) - 1, n1;
     938             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     939       13130 :   pari_sp av = avma;
     940             : 
     941       13130 :   if (n == 0) return B;
     942       13130 :   if (n == 1) return gen_solve_upper_1(U, B, E, ff);
     943       10176 :   if (n == 2) return gen_lsolve_upper_2(U, B, E, ff);
     944        4508 :   n1 = (n + 1)/2;
     945        4508 :   U2 = vecslice(U, n1 + 1, n);
     946        4508 :   U11 = matslice(U, 1,n1, 1,n1);
     947        4508 :   U12 = rowslice(U2, 1, n1);
     948        4508 :   U22 = rowslice(U2, n1 + 1, n);
     949        4508 :   B1 = vecslice(B, 1, n1);
     950        4508 :   B2 = vecslice(B, n1 + 1, n);
     951        4508 :   X1 = gen_lsolve_upper(U11, B1, E, ff, mul);
     952        4508 :   B2 = gen_matsub(B2, mul(E, X1, U12), E, ff);
     953        4508 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B2, &U22, &X1);
     954        4508 :   X2 = gen_lsolve_upper(U22, B2, E, ff, mul);
     955        4508 :   X = shallowconcat(X1, X2);
     956        4508 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     957        4508 :   return X;
     958             : }
     959             : 
     960             : static GEN
     961       14781 : gen_rsolve_lower_unit_2(GEN L, GEN A, void *E, const struct bb_field *ff)
     962             : {
     963       14781 :   GEN X1 = rowslice(A, 1, 1);
     964       14781 :   GEN X2 = gen_matsub(rowslice(A, 2, 2), gen_matscalmul(X1, gcoeff(L, 2, 1), E, ff), E, ff);
     965       14781 :   return vconcat(X1, X2);
     966             : }
     967             : 
     968             : /* solve L*X = A,  L lower triangular with ones on the diagonal
     969             :  * (at least as many rows as columns) */
     970             : static GEN
     971       34542 : gen_rsolve_lower_unit(GEN L, GEN A, void *E, const struct bb_field *ff,
     972             :                       GEN (*mul)(void *E, GEN a, GEN))
     973             : {
     974       34542 :   long m = lg(L) - 1, m1, n;
     975             :   GEN L1, L11, L21, L22, A1, A2, X1, X2, X;
     976       34542 :   pari_sp av = avma;
     977             : 
     978       34542 :   if (m == 0) return zeromat(0, lg(A) - 1);
     979       34542 :   if (m == 1) return rowslice(A, 1, 1);
     980       27083 :   if (m == 2) return gen_rsolve_lower_unit_2(L, A, E, ff);
     981       12302 :   m1 = (m + 1)/2;
     982       12302 :   n = nbrows(L);
     983       12302 :   L1 = vecslice(L, 1, m1);
     984       12302 :   L11 = rowslice(L1, 1, m1);
     985       12302 :   L21 = rowslice(L1, m1 + 1, n);
     986       12302 :   A1 = rowslice(A, 1, m1);
     987       12302 :   X1 = gen_rsolve_lower_unit(L11, A1, E, ff, mul);
     988       12302 :   A2 = rowslice(A, m1 + 1, n);
     989       12302 :   A2 = gen_matsub(A2, mul(E, L21, X1), E, ff);
     990       12302 :   if (gc_needed(av, 1)) gerepileall(av, 2, &A2, &X1);
     991       12302 :   L22 = matslice(L, m1+1,n, m1+1,m);
     992       12302 :   X2 = gen_rsolve_lower_unit(L22, A2, E, ff, mul);
     993       12302 :   X = vconcat(X1, X2);
     994       12302 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     995       12302 :   return X;
     996             : }
     997             : 
     998             : static GEN
     999        7119 : gen_lsolve_lower_unit_2(GEN L, GEN A, void *E, const struct bb_field *ff)
    1000             : {
    1001        7119 :   GEN X2 = vecslice(A, 2, 2);
    1002        7119 :   GEN X1 = gen_matsub(vecslice(A, 1, 1),
    1003        7119 :                     gen_matscalmul(X2, gcoeff(L, 2, 1), E, ff), E, ff);
    1004        7119 :   return shallowconcat(X1, X2);
    1005             : }
    1006             : 
    1007             : /* solve L*X = A,  L lower triangular with ones on the diagonal
    1008             :  * (at least as many rows as columns) */
    1009             : static GEN
    1010       18516 : gen_lsolve_lower_unit(GEN L, GEN A, void *E, const struct bb_field *ff,
    1011             :                       GEN (*mul)(void *E, GEN a, GEN))
    1012             : {
    1013       18516 :   long m = lg(L) - 1, m1;
    1014             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
    1015       18516 :   pari_sp av = avma;
    1016             : 
    1017       18516 :   if (m <= 1) return A;
    1018       14507 :   if (m == 2) return gen_lsolve_lower_unit_2(L, A, E, ff);
    1019        7388 :   m1 = (m + 1)/2;
    1020        7388 :   L2 = vecslice(L, m1 + 1, m);
    1021        7388 :   L22 = rowslice(L2, m1 + 1, m);
    1022        7388 :   A2 = vecslice(A, m1 + 1, m);
    1023        7388 :   X2 = gen_lsolve_lower_unit(L22, A2, E, ff, mul);
    1024        7388 :   if (gc_needed(av, 1)) X2 = gerepilecopy(av, X2);
    1025        7388 :   L1 = vecslice(L, 1, m1);
    1026        7388 :   L21 = rowslice(L1, m1 + 1, m);
    1027        7388 :   A1 = vecslice(A, 1, m1);
    1028        7388 :   A1 = gen_matsub(A1, mul(E, X2, L21), E, ff);
    1029        7388 :   L11 = rowslice(L1, 1, m1);
    1030        7388 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
    1031        7388 :   X1 = gen_lsolve_lower_unit(L11, A1, E, ff, mul);
    1032        7388 :   X = shallowconcat(X1, X2);
    1033        7388 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1034        7388 :   return X;
    1035             : }
    1036             : 
    1037             : /* destroy A */
    1038             : static long
    1039       20361 : gen_CUP_basecase(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, void *E, const struct bb_field *ff)
    1040             : {
    1041       20361 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc;
    1042             :   pari_sp av;
    1043             :   GEN u, v;
    1044             : 
    1045       20361 :   if (P) *P = identity_perm(n);
    1046       20361 :   *R = cgetg(m + 1, t_VECSMALL);
    1047       20361 :   av = avma;
    1048       53248 :   for (j = 1, pr = 0; j <= n; j++)
    1049             :   {
    1050      121464 :     for (pr++, pc = 0; pr <= m; pr++)
    1051             :     {
    1052      585418 :       for (k = j; k <= n; k++)
    1053             :       {
    1054      479856 :         v = ff->red(E, gcoeff(A, pr, k));
    1055      479856 :         gcoeff(A, pr, k) = v;
    1056      479856 :         if (!pc && !ff->equal0(v)) pc = k;
    1057             :       }
    1058      105562 :       if (pc) break;
    1059             :     }
    1060       48789 :     if (!pc) break;
    1061       32887 :     (*R)[j] = pr;
    1062       32887 :     if (pc != j)
    1063             :     {
    1064        4325 :       swap(gel(A, j), gel(A, pc));
    1065        4325 :       if (P) lswap((*P)[j], (*P)[pc]);
    1066             :     }
    1067       32887 :     u = ff->inv(E, gcoeff(A, pr, j));
    1068      164319 :     for (i = pr + 1; i <= m; i++)
    1069             :     {
    1070      131432 :       v = ff->red(E, ff->mul(E, gcoeff(A, i, j), u));
    1071      131432 :       gcoeff(A, i, j) = v;
    1072      131432 :       v = ff->neg(E, v);
    1073      425926 :       for (k = j + 1; k <= n; k++)
    1074      588988 :         gcoeff(A, i, k) = ff->add(E, gcoeff(A, i, k),
    1075      294494 :                                   ff->red(E, ff->mul(E, gcoeff(A, pr, k), v)));
    1076             :     }
    1077       32887 :     if (gc_needed(av, 2)) A = gerepilecopy(av, A);
    1078             :   }
    1079       20361 :   setlg(*R, j);
    1080       20361 :   *C = vecslice(A, 1, j - 1);
    1081       20361 :   if (U) *U = rowpermute(A, *R);
    1082       20361 :   return j - 1;
    1083             : }
    1084             : 
    1085             : static const long gen_CUP_LIMIT = 5;
    1086             : 
    1087             : static long
    1088       10672 : gen_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, void *E, const struct bb_field *ff,
    1089             :         GEN (*mul)(void *E, GEN a, GEN))
    1090             : {
    1091       10672 :   long m = nbrows(A), m1, n = lg(A) - 1, i, r1, r2, r;
    1092             :   GEN R1, C1, U1, P1, R2, C2, U2, P2;
    1093             :   GEN A1, A2, B2, C21, U11, U12, T21, T22;
    1094       10672 :   pari_sp av = avma;
    1095             : 
    1096       10672 :   if (m < gen_CUP_LIMIT || n < gen_CUP_LIMIT)
    1097             :     /* destroy A; not called at the outermost recursion level */
    1098        6091 :     return gen_CUP_basecase(A, R, C, U, P, E, ff);
    1099        4581 :   m1 = (minss(m, n) + 1)/2;
    1100        4581 :   A1 = rowslice(A, 1, m1);
    1101        4581 :   A2 = rowslice(A, m1 + 1, m);
    1102        4581 :   r1 = gen_CUP(A1, &R1, &C1, &U1, &P1, E, ff, mul);
    1103        4581 :   if (r1 == 0)
    1104             :   {
    1105         467 :     r2 = gen_CUP(A2, &R2, &C2, &U2, &P2, E, ff, mul);
    1106         467 :     *R = cgetg(r2 + 1, t_VECSMALL);
    1107         467 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
    1108         467 :     *C = vconcat(gen_zeromat(m1, r2, E, ff), C2);
    1109         467 :     *U = U2;
    1110         467 :     *P = P2;
    1111         467 :     r = r2;
    1112             :   }
    1113             :   else
    1114             :   {
    1115        4114 :     U11 = vecslice(U1, 1, r1);
    1116        4114 :     U12 = vecslice(U1, r1 + 1, n);
    1117        4114 :     T21 = vecslicepermute(A2, P1, 1, r1);
    1118        4114 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
    1119        4114 :     C21 = gen_lsolve_upper(U11, T21, E, ff, mul);
    1120        4114 :     if (gc_needed(av, 1))
    1121           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
    1122        4114 :     B2 = gen_matsub(T22, mul(E, C21, U12), E, ff);
    1123        4114 :     r2 = gen_CUP(B2, &R2, &C2, &U2, &P2, E, ff, mul);
    1124        4114 :     r = r1 + r2;
    1125        4114 :     *R = cgetg(r + 1, t_VECSMALL);
    1126        4114 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
    1127        4114 :     for (     ; i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
    1128        4114 :     *C = shallowconcat(vconcat(C1, C21),
    1129             :                        vconcat(gen_zeromat(m1, r2, E, ff), C2));
    1130        4114 :     *U = shallowconcat(vconcat(U11, gen_zeromat(r2, r1, E, ff)),
    1131             :                        vconcat(vecpermute(U12, P2), U2));
    1132             : 
    1133        4114 :     *P = cgetg(n + 1, t_VECSMALL);
    1134        4114 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
    1135        4114 :     for (     ; i <= n; i++)  (*P)[i] = P1[P2[i - r1] + r1];
    1136             :   }
    1137        4581 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
    1138        4581 :   return r;
    1139             : }
    1140             : 
    1141             : /* column echelon form */
    1142             : static long
    1143       24911 : gen_echelon(GEN A, GEN *R, GEN *C, void *E, const struct bb_field *ff,
    1144             :             GEN (*mul)(void*, GEN, GEN))
    1145             : {
    1146       24911 :   long j, j1, j2, m = nbrows(A), n = lg(A) - 1, n1, r, r1, r2;
    1147             :   GEN A1, A2, R1, R1c, C1, R2, C2;
    1148             :   GEN A12, A22, B2, C11, C21, M12;
    1149       24911 :   pari_sp av = avma;
    1150             : 
    1151       24911 :   if (m < gen_CUP_LIMIT || n < gen_CUP_LIMIT)
    1152       14270 :     return gen_CUP_basecase(shallowcopy(A), R, C, NULL, NULL, E, ff);
    1153             : 
    1154       10641 :   n1 = (n + 1)/2;
    1155       10641 :   A1 = vecslice(A, 1, n1);
    1156       10641 :   A2 = vecslice(A, n1 + 1, n);
    1157       10641 :   r1 = gen_echelon(A1, &R1, &C1, E, ff, mul);
    1158       10641 :   if (!r1) return gen_echelon(A2, R, C, E, ff, mul);
    1159        9576 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
    1160        9409 :   R1c = indexcompl(R1, m);
    1161        9409 :   C11 = rowpermute(C1, R1);
    1162        9409 :   C21 = rowpermute(C1, R1c);
    1163        9409 :   A12 = rowpermute(A2, R1);
    1164        9409 :   A22 = rowpermute(A2, R1c);
    1165        9409 :   M12 = gen_rsolve_lower_unit(C11, A12, E, ff, mul);
    1166        9409 :   B2 = gen_matsub(A22, mul(E, C21, M12), E, ff);
    1167        9409 :   r2 = gen_echelon(B2, &R2, &C2, E, ff, mul);
    1168        9409 :   if (!r2) { *R = R1; *C = C1; r = r1; }
    1169             :   else
    1170             :   {
    1171        5089 :     R2 = perm_mul(R1c, R2);
    1172        5089 :     C2 = rowpermute(vconcat(gen_zeromat(r1, r2, E, ff), C2),
    1173             :                     perm_inv(vecsmall_concat(R1, R1c)));
    1174        5089 :     r = r1 + r2;
    1175        5089 :     *R = cgetg(r + 1, t_VECSMALL);
    1176        5089 :     *C = cgetg(r + 1, t_MAT);
    1177       35705 :     for (j = j1 = j2 = 1; j <= r; j++)
    1178       30616 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
    1179             :       {
    1180       17911 :         gel(*C, j) = gel(C1, j1);
    1181       17911 :         (*R)[j] = R1[j1++];
    1182             :       }
    1183             :       else
    1184             :       {
    1185       12705 :         gel(*C, j) = gel(C2, j2);
    1186       12705 :         (*R)[j] = R2[j2++];
    1187             :       }
    1188             :   }
    1189        9409 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
    1190        9409 :   return r;
    1191             : }
    1192             : 
    1193             : static GEN
    1194         785 : gen_pivots_CUP(GEN x, long *rr, void *E, const struct bb_field *ff,
    1195             :                GEN (*mul)(void*, GEN, GEN))
    1196             : {
    1197             :   pari_sp av;
    1198         785 :   long i, n = lg(x) - 1, r;
    1199         785 :   GEN R, C, U, P, d = zero_zv(n);
    1200         785 :   av = avma;
    1201         785 :   r = gen_CUP(x, &R, &C, &U, &P, E, ff, mul);
    1202        5885 :   for(i = 1; i <= r; i++)
    1203        5100 :     d[P[i]] = R[i];
    1204         785 :   set_avma(av);
    1205         785 :   *rr = n - r;
    1206         785 :   return d;
    1207             : }
    1208             : 
    1209             : static GEN
    1210         140 : gen_det_CUP(GEN a, void *E, const struct bb_field *ff,
    1211             :             GEN (*mul)(void*, GEN, GEN))
    1212             : {
    1213         140 :   pari_sp av = avma;
    1214             :   GEN R, C, U, P, d;
    1215         140 :   long i, n = lg(a) - 1, r;
    1216         140 :   r = gen_CUP(a, &R, &C, &U, &P, E, ff, mul);
    1217         140 :   if (r < n)
    1218           0 :     d = ff->s(E, 0);
    1219             :   else {
    1220         140 :     d = ff->s(E, perm_sign(P) == 1 ? 1: - 1);
    1221        2730 :     for (i = 1; i <= n; i++)
    1222        2590 :       d = ff->red(E, ff->mul(E, d, gcoeff(U, i, i)));
    1223             :   }
    1224         140 :   return gerepileupto(av, d);
    1225             : }
    1226             : 
    1227             : static long
    1228          35 : gen_matrank(GEN x, void *E, const struct bb_field *ff,
    1229             :             GEN (*mul)(void*, GEN, GEN))
    1230             : {
    1231          35 :   pari_sp av = avma;
    1232             :   long r;
    1233          35 :   if (lg(x) - 1 >= gen_CUP_LIMIT && nbrows(x) >= gen_CUP_LIMIT)
    1234             :   {
    1235             :     GEN R, C;
    1236          28 :     return gc_long(av, gen_echelon(x, &R, &C, E, ff, mul));
    1237             :   }
    1238           7 :   (void) gen_Gauss_pivot(x, &r, E, ff);
    1239           7 :   return gc_long(av, lg(x)-1 - r);
    1240             : }
    1241             : 
    1242             : static GEN
    1243          63 : gen_invimage_CUP(GEN A, GEN B, void *E, const struct bb_field *ff,
    1244             :                  GEN (*mul)(void*, GEN, GEN))
    1245             : {
    1246          63 :   pari_sp av = avma;
    1247             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    1248          63 :   long r = gen_CUP(A, &R, &C, &U, &P, E, ff, mul);
    1249          63 :   Rc = indexcompl(R, nbrows(B));
    1250          63 :   C1 = rowpermute(C, R);
    1251          63 :   C2 = rowpermute(C, Rc);
    1252          63 :   B1 = rowpermute(B, R);
    1253          63 :   B2 = rowpermute(B, Rc);
    1254          63 :   Z = gen_rsolve_lower_unit(C1, B1, E, ff, mul);
    1255          63 :   if (!gequal(mul(E, C2, Z), B2))
    1256          42 :     return NULL;
    1257          42 :   Y = vconcat(gen_rsolve_upper(vecslice(U, 1, r), Z, E, ff, mul),
    1258          42 :               gen_zeromat(lg(A) - 1 - r, lg(B) - 1, E, ff));
    1259          21 :   X = rowpermute(Y, perm_inv(P));
    1260          21 :   return gerepilecopy(av, X);
    1261             : }
    1262             : 
    1263             : static GEN
    1264        3684 : gen_ker_echelon(GEN x, void *E, const struct bb_field *ff,
    1265             :                 GEN (*mul)(void*, GEN, GEN))
    1266             : {
    1267        3684 :   pari_sp av = avma;
    1268             :   GEN R, Rc, C, C1, C2, S, K;
    1269        3684 :   long n = lg(x) - 1, r;
    1270        3684 :   r = gen_echelon(shallowtrans(x), &R, &C, E, ff, mul);
    1271        3684 :   Rc = indexcompl(R, n);
    1272        3684 :   C1 = rowpermute(C, R);
    1273        3684 :   C2 = rowpermute(C, Rc);
    1274        3684 :   S = gen_lsolve_lower_unit(C1, C2, E, ff, mul);
    1275        3684 :   K = vecpermute(shallowconcat(gen_matneg(S, E, ff), gen_matid(n - r, E, ff)),
    1276             :                  perm_inv(vecsmall_concat(R, Rc)));
    1277        3684 :   K = shallowtrans(K);
    1278        3684 :   return gerepilecopy(av, K);
    1279             : }
    1280             : 
    1281             : static GEN
    1282          84 : gen_deplin_echelon(GEN x, void *E, const struct bb_field *ff,
    1283             :                    GEN (*mul)(void*, GEN, GEN))
    1284             : {
    1285          84 :   pari_sp av = avma;
    1286             :   GEN R, Rc, C, C1, C2, s, v;
    1287          84 :   long i, n = lg(x) - 1, r;
    1288          84 :   r = gen_echelon(shallowtrans(x), &R, &C, E, ff, mul);
    1289          84 :   if (r == n) return gc_NULL(av);
    1290          56 :   Rc = indexcompl(R, n);
    1291          56 :   i = Rc[1];
    1292          56 :   C1 = rowpermute(C, R);
    1293          56 :   C2 = rowslice(C, i, i);
    1294          56 :   s = row(gen_lsolve_lower_unit(C1, C2, E, ff, mul), 1);
    1295          56 :   settyp(s, t_COL);
    1296          56 :   v = vecpermute(shallowconcat(gen_colneg(s, E, ff), gen_colei(n - r, 1, E, ff)),
    1297             :                  perm_inv(vecsmall_concat(R, Rc)));
    1298          56 :   return gerepilecopy(av, v);
    1299             : }
    1300             : 
    1301             : static GEN
    1302         522 : gen_gauss_CUP(GEN a, GEN b, void *E, const struct bb_field *ff,
    1303             :               GEN (*mul)(void*, GEN, GEN))
    1304             : {
    1305             :   GEN R, C, U, P, Y;
    1306         522 :   long n = lg(a) - 1, r;
    1307         522 :   if (nbrows(a) < n || (r = gen_CUP(a, &R, &C, &U, &P, E, ff, mul)) < n)
    1308          56 :     return NULL;
    1309         466 :   Y = gen_rsolve_lower_unit(rowpermute(C, R), rowpermute(b, R), E, ff, mul);
    1310         466 :   return rowpermute(gen_rsolve_upper(U, Y, E, ff, mul), perm_inv(P));
    1311             : }
    1312             : 
    1313             : static GEN
    1314        4297 : gen_gauss(GEN a, GEN b, void *E, const struct bb_field *ff,
    1315             :           GEN (*mul)(void*, GEN, GEN))
    1316             : {
    1317        4297 :   if (lg(a) - 1 >= gen_CUP_LIMIT)
    1318         522 :     return gen_gauss_CUP(a, b, E, ff, mul);
    1319        3775 :   return gen_Gauss(a, b, E, ff);
    1320             : }
    1321             : 
    1322             : static GEN
    1323        5156 : gen_ker_i(GEN x, long deplin, void *E, const struct bb_field *ff,
    1324             :           GEN (*mul)(void*, GEN, GEN)) {
    1325        5156 :   if (lg(x) - 1 >= gen_CUP_LIMIT && nbrows(x) >= gen_CUP_LIMIT)
    1326        3768 :     return deplin? gen_deplin_echelon(x, E, ff, mul): gen_ker_echelon(x, E, ff, mul);
    1327        1388 :   return gen_ker(x, deplin, E, ff);
    1328             : }
    1329             : 
    1330             : static GEN
    1331         140 : gen_invimage(GEN A, GEN B, void *E, const struct bb_field *ff,
    1332             :              GEN (*mul)(void*, GEN, GEN))
    1333             : {
    1334         140 :   long nA = lg(A)-1, nB = lg(B)-1;
    1335             : 
    1336         140 :   if (!nB) return cgetg(1, t_MAT);
    1337         140 :   if (nA + nB >= gen_CUP_LIMIT && nbrows(B) >= gen_CUP_LIMIT)
    1338          63 :     return gen_invimage_CUP(A, B, E, ff, mul);
    1339          77 :   return gen_matinvimage(A, B, E, ff);
    1340             : }
    1341             : 
    1342             : /* find z such that A z = y. Return NULL if no solution */
    1343             : static GEN
    1344          70 : gen_matcolinvimage_i(GEN A, GEN y, void *E, const struct bb_field *ff,
    1345             :                      GEN (*mul)(void*, GEN, GEN))
    1346             : {
    1347          70 :   pari_sp av = avma;
    1348          70 :   long i, l = lg(A);
    1349             :   GEN M, x, t;
    1350             : 
    1351          70 :   M = gen_ker_i(shallowconcat(A, y), 0, E, ff, mul);
    1352          70 :   i = lg(M) - 1;
    1353          70 :   if (!i) return gc_NULL(av);
    1354             : 
    1355          70 :   x = gel(M, i);
    1356          70 :   t = gel(x, l);
    1357          70 :   if (ff->equal0(t)) return gc_NULL(av);
    1358             : 
    1359          49 :   t = ff->neg(E, ff->inv(E, t));
    1360          49 :   setlg(x, l);
    1361         175 :   for (i = 1; i < l; i++)
    1362         126 :     gel(x, i) = ff->red(E, ff->mul(E, t, gel(x, i)));
    1363          49 :   return gerepilecopy(av, x);
    1364             : }
    1365             : 
    1366             : static GEN
    1367         420 : gen_det_i(GEN a, void *E, const struct bb_field *ff,
    1368             :           GEN (*mul)(void*, GEN, GEN))
    1369             : {
    1370         420 :   if (lg(a) - 1 >= gen_CUP_LIMIT)
    1371         140 :     return gen_det_CUP(a, E, ff, mul);
    1372             :   else
    1373         280 :     return gen_det(a, E, ff);
    1374             : }
    1375             : 
    1376             : static GEN
    1377        2278 : gen_pivots(GEN x, long *rr, void *E, const struct bb_field *ff,
    1378             :            GEN (*mul)(void*, GEN, GEN))
    1379             : {
    1380        2278 :   if (lg(x) - 1 >= gen_CUP_LIMIT && nbrows(x) >= gen_CUP_LIMIT)
    1381         785 :     return gen_pivots_CUP(x, rr, E, ff, mul);
    1382        1493 :   return gen_Gauss_pivot(x, rr, E, ff);
    1383             : }
    1384             : 
    1385             : /* r = dim Ker x, n = nbrows(x) */
    1386             : static GEN
    1387          21 : gen_get_suppl(GEN x, GEN d, long n, long r, void *E, const struct bb_field *ff)
    1388             : {
    1389             :   GEN y, c;
    1390          21 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
    1391             : 
    1392          21 :   if (rx == n && r == 0) return gcopy(x);
    1393          21 :   c = zero_zv(n);
    1394          21 :   y = cgetg(n+1, t_MAT);
    1395             :   /* c = lines containing pivots (could get it from gauss_pivot, but cheap)
    1396             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
    1397         119 :   for (k = j = 1; j<=rx; j++)
    1398          98 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gcopy(gel(x,j)); }
    1399         203 :   for (j=1; j<=n; j++)
    1400         182 :     if (!c[j]) gel(y,k++) = gen_colei(n, j, E, ff);
    1401          21 :   return y;
    1402             : }
    1403             : 
    1404             : static GEN
    1405          21 : gen_suppl(GEN x, void *E, const struct bb_field *ff,
    1406             :           GEN (*mul)(void*, GEN, GEN))
    1407             : {
    1408             :   GEN d;
    1409          21 :   long n = nbrows(x), r;
    1410             : 
    1411          21 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    1412          21 :   d = gen_pivots(x, &r, E, ff, mul);
    1413          21 :   return gen_get_suppl(x, d, n, r, E, ff);
    1414             : }
    1415             : 
    1416             : /*******************************************************************/
    1417             : /*                                                                 */
    1418             : /*                MATRIX MULTIPLICATION MODULO P                   */
    1419             : /*                                                                 */
    1420             : /*******************************************************************/
    1421             : 
    1422             : GEN
    1423          21 : F2xqM_F2xqC_mul(GEN A, GEN B, GEN T) {
    1424             :   void *E;
    1425          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1426          21 :   return gen_matcolmul(A, B, E, ff);
    1427             : }
    1428             : 
    1429             : GEN
    1430          35 : FlxqM_FlxqC_mul(GEN A, GEN B, GEN T, ulong p) {
    1431             :   void *E;
    1432          35 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1433          35 :   return gen_matcolmul(A, B, E, ff);
    1434             : }
    1435             : 
    1436             : GEN
    1437          49 : FqM_FqC_mul(GEN A, GEN B, GEN T, GEN p) {
    1438             :   void *E;
    1439          49 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    1440          49 :   return gen_matcolmul(A, B, E, ff);
    1441             : }
    1442             : 
    1443             : GEN
    1444        1407 : F2xqM_mul(GEN A, GEN B, GEN T) {
    1445             :   void *E;
    1446        1407 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1447        1407 :   return gen_matmul(A, B, E, ff);
    1448             : }
    1449             : 
    1450             : GEN
    1451      138852 : FlxqM_mul(GEN A, GEN B, GEN T, ulong p) {
    1452             :   void *E;
    1453             :   const struct bb_field *ff;
    1454      138852 :   long n = lg(A) - 1;
    1455             : 
    1456      138852 :   if (n == 0)
    1457           0 :     return cgetg(1, t_MAT);
    1458      138852 :   if (n > 1)
    1459       76714 :     return FlxqM_mul_Kronecker(A, B, T, p);
    1460       62138 :   ff = get_Flxq_field(&E, T, p);
    1461       62138 :   return gen_matmul(A, B, E, ff);
    1462             : }
    1463             : 
    1464             : GEN
    1465       66416 : FqM_mul(GEN A, GEN B, GEN T, GEN p) {
    1466             :   void *E;
    1467       66416 :   long n = lg(A) - 1;
    1468             :   const struct bb_field *ff;
    1469       66416 :   if (n == 0)
    1470           0 :     return cgetg(1, t_MAT);
    1471       66416 :   if (n > 1)
    1472       62797 :     return FqM_mul_Kronecker(A, B, T, p);
    1473        3619 :   ff = get_Fq_field(&E, T, p);
    1474        3619 :   return gen_matmul(A, B, E, ff);
    1475             : }
    1476             : 
    1477             : /*******************************************************************/
    1478             : /*                                                                 */
    1479             : /*                    LINEAR ALGEBRA MODULO P                      */
    1480             : /*                                                                 */
    1481             : /*******************************************************************/
    1482             : 
    1483             : static GEN
    1484           0 : _F2xqM_mul(void *E, GEN A, GEN B)
    1485           0 : { return F2xqM_mul(A, B, (GEN) E); }
    1486             : 
    1487             : struct _Flxq {
    1488             :   GEN aut;
    1489             :   GEN T;
    1490             :   ulong p;
    1491             : };
    1492             : 
    1493             : static GEN
    1494       14392 : _FlxqM_mul(void *E, GEN A, GEN B)
    1495             : {
    1496       14392 :   struct _Flxq *D = (struct _Flxq*)E;
    1497       14392 :   return FlxqM_mul(A, B, D->T, D->p);
    1498             : }
    1499             : 
    1500             : static GEN
    1501       19492 : _FpM_mul(void *E, GEN A, GEN B)
    1502       19492 : { return FpM_mul(A, B, (GEN) E); }
    1503             : 
    1504             : struct _Fq_field
    1505             : {
    1506             :   GEN T, p;
    1507             : };
    1508             : 
    1509             : static GEN
    1510        6349 : _FqM_mul(void *E, GEN A, GEN B)
    1511             : {
    1512        6349 :   struct _Fq_field *D = (struct _Fq_field*) E;
    1513        6349 :   return FqM_mul(A, B, D->T, D->p);
    1514             : }
    1515             : 
    1516             : 
    1517             : static GEN
    1518      604571 : FpM_init(GEN a, GEN p, ulong *pp)
    1519             : {
    1520      604571 :   if (lgefint(p) == 3)
    1521             :   {
    1522      597208 :     *pp = uel(p,2);
    1523      597208 :     return (*pp==2)? ZM_to_F2m(a): ZM_to_Flm(a, *pp);
    1524             :   }
    1525        7363 :   *pp = 0; return a;
    1526             : }
    1527             : GEN
    1528        2359 : RgM_Fp_init(GEN a, GEN p, ulong *pp)
    1529             : {
    1530        2359 :   if (lgefint(p) == 3)
    1531             :   {
    1532        2009 :     *pp = uel(p,2);
    1533        2009 :     return (*pp==2)? RgM_to_F2m(a): RgM_to_Flm(a, *pp);
    1534             :   }
    1535         350 :   *pp = 0; return RgM_to_FpM(a,p);
    1536             : }
    1537             : 
    1538             : static GEN
    1539         315 : FpM_det_gen(GEN a, GEN p)
    1540             : {
    1541             :   void *E;
    1542         315 :   const struct bb_field *S = get_Fp_field(&E,p);
    1543         315 :   return gen_det_i(a, E, S, _FpM_mul);
    1544             : }
    1545             : GEN
    1546        3948 : FpM_det(GEN a, GEN p)
    1547             : {
    1548        3948 :   pari_sp av = avma;
    1549             :   ulong pp, d;
    1550        3948 :   a = FpM_init(a, p, &pp);
    1551        3948 :   switch(pp)
    1552             :   {
    1553         315 :   case 0: return FpM_det_gen(a, p);
    1554        1617 :   case 2: d = F2m_det_sp(a); break;
    1555        2016 :   default:d = Flm_det_sp(a,pp); break;
    1556             :   }
    1557        3633 :   set_avma(av); return utoi(d);
    1558             : }
    1559             : 
    1560             : GEN
    1561           7 : F2xqM_det(GEN a, GEN T)
    1562             : {
    1563             :   void *E;
    1564           7 :   const struct bb_field *S = get_F2xq_field(&E, T);
    1565           7 :   return gen_det_i(a, E, S, _F2xqM_mul);
    1566             : }
    1567             : 
    1568             : GEN
    1569          28 : FlxqM_det(GEN a, GEN T, ulong p) {
    1570             :   void *E;
    1571          28 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1572          28 :   return gen_det_i(a, E, S, _FlxqM_mul);
    1573             : }
    1574             : 
    1575             : GEN
    1576          70 : FqM_det(GEN x, GEN T, GEN p)
    1577             : {
    1578             :   void *E;
    1579          70 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    1580          70 :   return gen_det_i(x, E, S, _FqM_mul);
    1581             : }
    1582             : 
    1583             : static GEN
    1584         857 : FpM_gauss_pivot_gen(GEN x, GEN p, long *rr)
    1585             : {
    1586             :   void *E;
    1587         857 :   const struct bb_field *S = get_Fp_field(&E,p);
    1588         857 :   return gen_pivots(x, rr, E, S, _FpM_mul);
    1589             : }
    1590             : 
    1591             : static GEN
    1592      158787 : FpM_gauss_pivot(GEN x, GEN p, long *rr)
    1593             : {
    1594             :   ulong pp;
    1595      158787 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1596      156960 :   x = FpM_init(x, p, &pp);
    1597      156960 :   switch(pp)
    1598             :   {
    1599         857 :   case 0: return FpM_gauss_pivot_gen(x, p, rr);
    1600       49282 :   case 2: return F2m_gauss_pivot(x, rr);
    1601      106821 :   default:return Flm_pivots(x, pp, rr, 1);
    1602             :   }
    1603             : }
    1604             : 
    1605             : static GEN
    1606          21 : F2xqM_gauss_pivot(GEN x, GEN T, long *rr)
    1607             : {
    1608             :   void *E;
    1609          21 :   const struct bb_field *S = get_F2xq_field(&E,T);
    1610          21 :   return gen_pivots(x, rr, E, S, _F2xqM_mul);
    1611             : }
    1612             : 
    1613             : static GEN
    1614        1274 : FlxqM_gauss_pivot(GEN x, GEN T, ulong p, long *rr) {
    1615             :   void *E;
    1616        1274 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1617        1274 :   return gen_pivots(x, rr, E, S, _FlxqM_mul);
    1618             : }
    1619             : 
    1620             : static GEN
    1621         105 : FqM_gauss_pivot_gen(GEN x, GEN T, GEN p, long *rr)
    1622             : {
    1623             :   void *E;
    1624         105 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    1625         105 :   return gen_pivots(x, rr, E, S, _FqM_mul);
    1626             : }
    1627             : static GEN
    1628        1351 : FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr)
    1629             : {
    1630        1351 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1631        1351 :   if (!T) return FpM_gauss_pivot(x, p, rr);
    1632        1351 :   if (lgefint(p) == 3)
    1633             :   {
    1634        1246 :     pari_sp av = avma;
    1635        1246 :     ulong pp = uel(p,2);
    1636        1246 :     GEN Tp = ZXT_to_FlxT(T, pp);
    1637        1246 :     GEN d = FlxqM_gauss_pivot(FqM_to_FlxM(x, T, p), Tp, pp, rr);
    1638        1246 :     return d ? gerepileuptoleaf(av, d): d;
    1639             :   }
    1640         105 :   return FqM_gauss_pivot_gen(x, T, p, rr);
    1641             : }
    1642             : 
    1643             : GEN
    1644       88615 : FpM_image(GEN x, GEN p)
    1645             : {
    1646             :   long r;
    1647       88615 :   GEN d = FpM_gauss_pivot(x,p,&r); /* d left on stack for efficiency */
    1648       88615 :   return image_from_pivot(x,d,r);
    1649             : }
    1650             : 
    1651             : GEN
    1652        1057 : Flm_image(GEN x, ulong p)
    1653             : {
    1654             :   long r;
    1655        1057 :   GEN d = Flm_pivots(x, p, &r, 0); /* d left on stack for efficiency */
    1656        1057 :   return image_from_pivot(x,d,r);
    1657             : }
    1658             : 
    1659             : GEN
    1660           7 : F2m_image(GEN x)
    1661             : {
    1662             :   long r;
    1663           7 :   GEN d = F2m_gauss_pivot(F2m_copy(x),&r); /* d left on stack for efficiency */
    1664           7 :   return image_from_pivot(x,d,r);
    1665             : }
    1666             : 
    1667             : GEN
    1668           7 : F2xqM_image(GEN x, GEN T)
    1669             : {
    1670             :   long r;
    1671           7 :   GEN d = F2xqM_gauss_pivot(x,T,&r); /* d left on stack for efficiency */
    1672           7 :   return image_from_pivot(x,d,r);
    1673             : }
    1674             : 
    1675             : GEN
    1676          21 : FlxqM_image(GEN x, GEN T, ulong p)
    1677             : {
    1678             :   long r;
    1679          21 :   GEN d = FlxqM_gauss_pivot(x, T, p, &r); /* d left on stack for efficiency */
    1680          21 :   return image_from_pivot(x,d,r);
    1681             : }
    1682             : 
    1683             : GEN
    1684          49 : FqM_image(GEN x, GEN T, GEN p)
    1685             : {
    1686             :   long r;
    1687          49 :   GEN d = FqM_gauss_pivot(x,T,p,&r); /* d left on stack for efficiency */
    1688          49 :   return image_from_pivot(x,d,r);
    1689             : }
    1690             : 
    1691             : long
    1692          28 : FpM_rank(GEN x, GEN p)
    1693             : {
    1694          28 :   pari_sp av = avma;
    1695             :   long r;
    1696          28 :   (void)FpM_gauss_pivot(x,p,&r);
    1697          28 :   return gc_long(av, lg(x)-1 - r);
    1698             : }
    1699             : 
    1700             : long
    1701           7 : F2xqM_rank(GEN x, GEN T)
    1702             : {
    1703           7 :   pari_sp av = avma;
    1704             :   long r;
    1705           7 :   (void)F2xqM_gauss_pivot(x,T,&r);
    1706           7 :   return gc_long(av, lg(x)-1 - r);
    1707             : }
    1708             : 
    1709             : long
    1710          35 : FlxqM_rank(GEN x, GEN T, ulong p)
    1711             : {
    1712             :   void *E;
    1713          35 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1714          35 :   return gen_matrank(x, E, S, _FlxqM_mul);
    1715             : }
    1716             : 
    1717             : long
    1718          70 : FqM_rank(GEN x, GEN T, GEN p)
    1719             : {
    1720          70 :   pari_sp av = avma;
    1721             :   long r;
    1722          70 :   (void)FqM_gauss_pivot(x,T,p,&r);
    1723          70 :   return gc_long(av, lg(x)-1 - r);
    1724             : }
    1725             : 
    1726             : static GEN
    1727          35 : FpM_invimage_gen(GEN A, GEN B, GEN p)
    1728             : {
    1729             :   void *E;
    1730          35 :   const struct bb_field *ff = get_Fp_field(&E, p);
    1731          35 :   return gen_invimage(A, B, E, ff, _FpM_mul);
    1732             : }
    1733             : 
    1734             : GEN
    1735           0 : FpM_invimage(GEN A, GEN B, GEN p)
    1736             : {
    1737           0 :   pari_sp av = avma;
    1738             :   ulong pp;
    1739             :   GEN y;
    1740             : 
    1741           0 :   A = FpM_init(A, p, &pp);
    1742           0 :   switch(pp)
    1743             :   {
    1744           0 :   case 0: return FpM_invimage_gen(A, B, p);
    1745             :   case 2:
    1746           0 :     y = F2m_invimage(A, ZM_to_F2m(B));
    1747           0 :     if (!y) return gc_NULL(av);
    1748           0 :     y = F2m_to_ZM(y);
    1749           0 :     return gerepileupto(av, y);
    1750             :   default:
    1751           0 :     y = Flm_invimage(A, ZM_to_Flm(B, pp), pp);
    1752           0 :     if (!y) return gc_NULL(av);
    1753           0 :     y = Flm_to_ZM(y);
    1754           0 :     return gerepileupto(av, y);
    1755             :   }
    1756             : }
    1757             : 
    1758             : GEN
    1759          21 : F2xqM_invimage(GEN A, GEN B, GEN T) {
    1760             :   void *E;
    1761          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1762          21 :   return gen_invimage(A, B, E, ff, _F2xqM_mul);
    1763             : }
    1764             : 
    1765             : GEN
    1766          42 : FlxqM_invimage(GEN A, GEN B, GEN T, ulong p) {
    1767             :   void *E;
    1768          42 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1769          42 :   return gen_invimage(A, B, E, ff, _FlxqM_mul);
    1770             : }
    1771             : 
    1772             : GEN
    1773          42 : FqM_invimage(GEN A, GEN B, GEN T, GEN p) {
    1774             :   void *E;
    1775          42 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    1776          42 :   return gen_invimage(A, B, E, ff, _FqM_mul);
    1777             : }
    1778             : 
    1779             : static GEN
    1780           7 : FpM_FpC_invimage_gen(GEN A, GEN y, GEN p)
    1781             : {
    1782             :   void *E;
    1783           7 :   const struct bb_field *ff = get_Fp_field(&E, p);
    1784           7 :   return gen_matcolinvimage_i(A, y, E, ff, _FpM_mul);
    1785             : }
    1786             : 
    1787             : GEN
    1788       52473 : FpM_FpC_invimage(GEN A, GEN x, GEN p)
    1789             : {
    1790       52473 :   pari_sp av = avma;
    1791             :   ulong pp;
    1792             :   GEN y;
    1793             : 
    1794       52473 :   A = FpM_init(A, p, &pp);
    1795       52473 :   switch(pp)
    1796             :   {
    1797           7 :   case 0: return FpM_FpC_invimage_gen(A, x, p);
    1798             :   case 2:
    1799       21433 :     y = F2m_F2c_invimage(A, ZV_to_F2v(x));
    1800       21433 :     if (!y) return y;
    1801       21433 :     y = F2c_to_ZC(y);
    1802       21433 :     return gerepileupto(av, y);
    1803             :   default:
    1804       31033 :     y = Flm_Flc_invimage(A, ZV_to_Flv(x, pp), pp);
    1805       31033 :     if (!y) return y;
    1806       31033 :     y = Flc_to_ZC(y);
    1807       31033 :     return gerepileupto(av, y);
    1808             :   }
    1809             : }
    1810             : 
    1811             : GEN
    1812          21 : F2xqM_F2xqC_invimage(GEN A, GEN B, GEN T) {
    1813             :   void *E;
    1814          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1815          21 :   return gen_matcolinvimage_i(A, B, E, ff, _F2xqM_mul);
    1816             : }
    1817             : 
    1818             : GEN
    1819          21 : FlxqM_FlxqC_invimage(GEN A, GEN B, GEN T, ulong p) {
    1820             :   void *E;
    1821          21 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1822          21 :   return gen_matcolinvimage_i(A, B, E, ff, _FlxqM_mul);
    1823             : }
    1824             : 
    1825             : GEN
    1826          21 : FqM_FqC_invimage(GEN A, GEN B, GEN T, GEN p) {
    1827             :   void *E;
    1828          21 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    1829          21 :   return gen_matcolinvimage_i(A, B, E, ff, _FqM_mul);
    1830             : }
    1831             : 
    1832             : static GEN
    1833        2265 : FpM_ker_gen(GEN x, GEN p, long deplin)
    1834             : {
    1835             :   void *E;
    1836        2265 :   const struct bb_field *S = get_Fp_field(&E,p);
    1837        2265 :   return gen_ker_i(x, deplin, E, S, _FpM_mul);
    1838             : }
    1839             : static GEN
    1840      318171 : FpM_ker_i(GEN x, GEN p, long deplin)
    1841             : {
    1842      318171 :   pari_sp av = avma;
    1843             :   ulong pp;
    1844             :   GEN y;
    1845             : 
    1846      318171 :   if (lg(x)==1) return cgetg(1,t_MAT);
    1847      318171 :   x = FpM_init(x, p, &pp);
    1848      318171 :   switch(pp)
    1849             :   {
    1850        2195 :   case 0: return FpM_ker_gen(x,p,deplin);
    1851             :   case 2:
    1852       96802 :     y = F2m_ker_sp(x, deplin);
    1853       96802 :     if (!y) return gc_NULL(av);
    1854       96802 :     y = deplin? F2c_to_ZC(y): F2m_to_ZM(y);
    1855       96802 :     return gerepileupto(av, y);
    1856             :   default:
    1857      219174 :     y = Flm_ker_sp(x, pp, deplin);
    1858      219174 :     if (!y) return gc_NULL(av);
    1859      219174 :     y = deplin? Flc_to_ZC(y): Flm_to_ZM(y);
    1860      219174 :     return gerepileupto(av, y);
    1861             :   }
    1862             : }
    1863             : 
    1864             : GEN
    1865      230386 : FpM_ker(GEN x, GEN p) { return FpM_ker_i(x,p,0); }
    1866             : 
    1867             : static GEN
    1868          35 : F2xqM_ker_i(GEN x, GEN T, long deplin)
    1869             : {
    1870             :   const struct bb_field *ff;
    1871             :   void *E;
    1872             : 
    1873          35 :   if (lg(x)==1) return cgetg(1,t_MAT);
    1874          35 :   ff = get_F2xq_field(&E,T);
    1875          35 :   return gen_ker_i(x,deplin, E, ff, _F2xqM_mul);
    1876             : }
    1877             : 
    1878             : GEN
    1879          21 : F2xqM_ker(GEN x, GEN T)
    1880             : {
    1881          21 :   return F2xqM_ker_i(x, T, 0);
    1882             : }
    1883             : 
    1884             : static GEN
    1885        2660 : FlxqM_ker_i(GEN x, GEN T, ulong p, long deplin) {
    1886             :   void *E;
    1887        2660 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1888        2660 :   return gen_ker_i(x, deplin, E, S, _FlxqM_mul);
    1889             : }
    1890             : 
    1891             : GEN
    1892        2625 : FlxqM_ker(GEN x, GEN T, ulong p)
    1893             : {
    1894        2625 :   return FlxqM_ker_i(x, T, p, 0);
    1895             : }
    1896             : 
    1897             : static GEN
    1898         126 : FqM_ker_gen(GEN x, GEN T, GEN p, long deplin)
    1899             : {
    1900             :   void *E;
    1901         126 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    1902         126 :   return gen_ker_i(x,deplin,E,S,_FqM_mul);
    1903             : }
    1904             : static GEN
    1905        8659 : FqM_ker_i(GEN x, GEN T, GEN p, long deplin)
    1906             : {
    1907        8659 :   if (!T) return FpM_ker_i(x,p,deplin);
    1908        2723 :   if (lg(x)==1) return cgetg(1,t_MAT);
    1909             : 
    1910        2723 :   if (lgefint(p)==3)
    1911             :   {
    1912        2597 :     pari_sp ltop=avma;
    1913        2597 :     ulong l= p[2];
    1914        2597 :     GEN Ml = FqM_to_FlxM(x, T, p);
    1915        2597 :     GEN Tl = ZXT_to_FlxT(T,l);
    1916        2597 :     GEN p1 = FlxM_to_ZXM(FlxqM_ker(Ml,Tl,l));
    1917        2597 :     return gerepileupto(ltop,p1);
    1918             :   }
    1919         126 :   return FqM_ker_gen(x, T, p, deplin);
    1920             : }
    1921             : 
    1922             : GEN
    1923        8582 : FqM_ker(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,0); }
    1924             : 
    1925             : GEN
    1926       81849 : FpM_deplin(GEN x, GEN p) { return FpM_ker_i(x,p,1); }
    1927             : 
    1928             : GEN
    1929          14 : F2xqM_deplin(GEN x, GEN T)
    1930             : {
    1931          14 :   return F2xqM_ker_i(x, T, 1);
    1932             : }
    1933             : 
    1934             : GEN
    1935          35 : FlxqM_deplin(GEN x, GEN T, ulong p)
    1936             : {
    1937          35 :   return FlxqM_ker_i(x, T, p, 1);
    1938             : }
    1939             : 
    1940             : GEN
    1941          77 : FqM_deplin(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,1); }
    1942             : 
    1943             : static GEN
    1944        3989 : FpM_gauss_gen(GEN a, GEN b, GEN p)
    1945             : {
    1946             :   void *E;
    1947        3989 :   const struct bb_field *S = get_Fp_field(&E,p);
    1948        3989 :   return gen_gauss(a,b, E, S, _FpM_mul);
    1949             : }
    1950             : /* a an FpM, lg(a)>1; b an FpM or NULL (replace by identity) */
    1951             : static GEN
    1952       73019 : FpM_gauss_i(GEN a, GEN b, GEN p, ulong *pp)
    1953             : {
    1954       73019 :   long n = nbrows(a);
    1955       73019 :   a = FpM_init(a,p,pp);
    1956       73019 :   switch(*pp)
    1957             :   {
    1958             :   case 0:
    1959        3989 :     if (!b) b = matid(n);
    1960        3989 :     return FpM_gauss_gen(a,b,p);
    1961             :   case 2:
    1962       24335 :     if (b) b = ZM_to_F2m(b); else b = matid_F2m(n);
    1963       24335 :     return F2m_gauss_sp(a,b);
    1964             :   default:
    1965       44695 :     if (b) b = ZM_to_Flm(b, *pp); else b = matid_Flm(n);
    1966       44695 :     return Flm_gauss_sp(a,b, NULL, *pp);
    1967             :   }
    1968             : }
    1969             : GEN
    1970          35 : FpM_gauss(GEN a, GEN b, GEN p)
    1971             : {
    1972          35 :   pari_sp av = avma;
    1973             :   ulong pp;
    1974             :   GEN u;
    1975          35 :   if (lg(a) == 1 || lg(b)==1) return cgetg(1, t_MAT);
    1976          35 :   u = FpM_gauss_i(a, b, p, &pp);
    1977          35 :   if (!u) return gc_NULL(av);
    1978          28 :   switch(pp)
    1979             :   {
    1980          28 :   case 0: return gerepilecopy(av, u);
    1981           0 :   case 2:  u = F2m_to_ZM(u); break;
    1982           0 :   default: u = Flm_to_ZM(u); break;
    1983             :   }
    1984           0 :   return gerepileupto(av, u);
    1985             : }
    1986             : 
    1987             : static GEN
    1988          84 : F2xqM_gauss_gen(GEN a, GEN b, GEN T)
    1989             : {
    1990             :   void *E;
    1991          84 :   const struct bb_field *S = get_F2xq_field(&E, T);
    1992          84 :   return gen_gauss(a, b, E, S, _F2xqM_mul);
    1993             : }
    1994             : 
    1995             : GEN
    1996          21 : F2xqM_gauss(GEN a, GEN b, GEN T)
    1997             : {
    1998          21 :   pari_sp av = avma;
    1999          21 :   long n = lg(a)-1;
    2000             :   GEN u;
    2001          21 :   if (!n || lg(b)==1) { set_avma(av); return cgetg(1, t_MAT); }
    2002          21 :   u = F2xqM_gauss_gen(a, b, T);
    2003          21 :   if (!u) return gc_NULL(av);
    2004          14 :   return gerepilecopy(av, u);
    2005             : }
    2006             : 
    2007             : static GEN
    2008          91 : FlxqM_gauss_i(GEN a, GEN b, GEN T, ulong p) {
    2009             :   void *E;
    2010          91 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    2011          91 :   return gen_gauss(a, b, E, S, _FlxqM_mul);
    2012             : }
    2013             : 
    2014             : GEN
    2015          21 : FlxqM_gauss(GEN a, GEN b, GEN T, ulong p)
    2016             : {
    2017          21 :   pari_sp av = avma;
    2018          21 :   long n = lg(a)-1;
    2019             :   GEN u;
    2020          21 :   if (!n || lg(b)==1) { set_avma(av); return cgetg(1, t_MAT); }
    2021          21 :   u = FlxqM_gauss_i(a, b, T, p);
    2022          21 :   if (!u) return gc_NULL(av);
    2023          14 :   return gerepilecopy(av, u);
    2024             : }
    2025             : 
    2026             : static GEN
    2027         133 : FqM_gauss_gen(GEN a, GEN b, GEN T, GEN p)
    2028             : {
    2029             :   void *E;
    2030         133 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2031         133 :   return gen_gauss(a,b,E,S,_FqM_mul);
    2032             : }
    2033             : GEN
    2034          21 : FqM_gauss(GEN a, GEN b, GEN T, GEN p)
    2035             : {
    2036          21 :   pari_sp av = avma;
    2037             :   GEN u;
    2038             :   long n;
    2039          21 :   if (!T) return FpM_gauss(a,b,p);
    2040          21 :   n = lg(a)-1; if (!n || lg(b)==1) return cgetg(1, t_MAT);
    2041          21 :   u = FqM_gauss_gen(a,b,T,p);
    2042          21 :   if (!u) return gc_NULL(av);
    2043          14 :   return gerepilecopy(av, u);
    2044             : }
    2045             : 
    2046             : GEN
    2047          14 : FpM_FpC_gauss(GEN a, GEN b, GEN p)
    2048             : {
    2049          14 :   pari_sp av = avma;
    2050             :   ulong pp;
    2051             :   GEN u;
    2052          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2053          14 :   u = FpM_gauss_i(a, mkmat(b), p, &pp);
    2054          14 :   if (!u) return gc_NULL(av);
    2055          14 :   switch(pp)
    2056             :   {
    2057          14 :   case 0: return gerepilecopy(av, gel(u,1));
    2058           0 :   case 2:  u = F2c_to_ZC(gel(u,1)); break;
    2059           0 :   default: u = Flc_to_ZC(gel(u,1)); break;
    2060             :   }
    2061           0 :   return gerepileupto(av, u);
    2062             : }
    2063             : 
    2064             : GEN
    2065          28 : F2xqM_F2xqC_gauss(GEN a, GEN b, GEN T)
    2066             : {
    2067          28 :   pari_sp av = avma;
    2068             :   GEN u;
    2069          28 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2070          28 :   u = F2xqM_gauss_gen(a, mkmat(b), T);
    2071          28 :   if (!u) return gc_NULL(av);
    2072          14 :   return gerepilecopy(av, gel(u,1));
    2073             : }
    2074             : 
    2075             : GEN
    2076          14 : FlxqM_FlxqC_gauss(GEN a, GEN b, GEN T, ulong p)
    2077             : {
    2078          14 :   pari_sp av = avma;
    2079             :   GEN u;
    2080          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2081          14 :   u = FlxqM_gauss_i(a, mkmat(b), T, p);
    2082          14 :   if (!u) return gc_NULL(av);
    2083           7 :   return gerepilecopy(av, gel(u,1));
    2084             : }
    2085             : 
    2086             : GEN
    2087          14 : FqM_FqC_gauss(GEN a, GEN b, GEN T, GEN p)
    2088             : {
    2089          14 :   pari_sp av = avma;
    2090             :   GEN u;
    2091          14 :   if (!T) return FpM_FpC_gauss(a,b,p);
    2092          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2093          14 :   u = FqM_gauss_gen(a,mkmat(b),T,p);
    2094          14 :   if (!u) return gc_NULL(av);
    2095           7 :   return gerepilecopy(av, gel(u,1));
    2096             : }
    2097             : 
    2098             : GEN
    2099       72970 : FpM_inv(GEN a, GEN p)
    2100             : {
    2101       72970 :   pari_sp av = avma;
    2102             :   ulong pp;
    2103             :   GEN u;
    2104       72970 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    2105       72970 :   u = FpM_gauss_i(a, NULL, p, &pp);
    2106       72970 :   if (!u) return gc_NULL(av);
    2107       72956 :   switch(pp)
    2108             :   {
    2109        3933 :   case 0: return gerepilecopy(av, u);
    2110       24328 :   case 2:  u = F2m_to_ZM(u); break;
    2111       44695 :   default: u = Flm_to_ZM(u); break;
    2112             :   }
    2113       69023 :   return gerepileupto(av, u);
    2114             : }
    2115             : 
    2116             : GEN
    2117          35 : F2xqM_inv(GEN a, GEN T)
    2118             : {
    2119          35 :   pari_sp av = avma;
    2120             :   GEN u;
    2121          35 :   if (lg(a) == 1) { set_avma(av); return cgetg(1, t_MAT); }
    2122          35 :   u = F2xqM_gauss_gen(a, matid_F2xqM(nbrows(a),T), T);
    2123          35 :   if (!u) return gc_NULL(av);
    2124          28 :   return gerepilecopy(av, u);
    2125             : }
    2126             : 
    2127             : GEN
    2128          56 : FlxqM_inv(GEN a, GEN T, ulong p)
    2129             : {
    2130          56 :   pari_sp av = avma;
    2131             :   GEN u;
    2132          56 :   if (lg(a) == 1) { set_avma(av); return cgetg(1, t_MAT); }
    2133          56 :   u = FlxqM_gauss_i(a, matid_FlxqM(nbrows(a),T,p), T,p);
    2134          56 :   if (!u) return gc_NULL(av);
    2135          42 :   return gerepilecopy(av, u);
    2136             : }
    2137             : 
    2138             : GEN
    2139          98 : FqM_inv(GEN a, GEN T, GEN p)
    2140             : {
    2141          98 :   pari_sp av = avma;
    2142             :   GEN u;
    2143          98 :   if (!T) return FpM_inv(a,p);
    2144          98 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    2145          98 :   u = FqM_gauss_gen(a,matid(nbrows(a)),T,p);
    2146          98 :   if (!u) return gc_NULL(av);
    2147          70 :   return gerepilecopy(av, u);
    2148             : }
    2149             : 
    2150             : GEN
    2151       94377 : FpM_intersect(GEN x, GEN y, GEN p)
    2152             : {
    2153       94377 :   pari_sp av = avma;
    2154       94377 :   long j, lx = lg(x);
    2155             :   GEN z;
    2156             : 
    2157       94377 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    2158       94377 :   z = FpM_ker(shallowconcat(x,y), p);
    2159       94377 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    2160       94377 :   return gerepileupto(av, FpM_mul(x,z,p));
    2161             : }
    2162             : 
    2163             : static void
    2164       43061 : init_suppl(GEN x)
    2165             : {
    2166       43061 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    2167             :   /* HACK: avoid overwriting d from gauss_pivot() after set_avma(av) */
    2168       43061 :   (void)new_chunk(lgcols(x) * 2);
    2169       43061 : }
    2170             : 
    2171             : GEN
    2172       41689 : FpM_suppl(GEN x, GEN p)
    2173             : {
    2174             :   GEN d;
    2175             :   long r;
    2176       41689 :   init_suppl(x); d = FpM_gauss_pivot(x,p, &r);
    2177       41689 :   return get_suppl(x,d,nbrows(x),r,&col_ei);
    2178             : }
    2179             : 
    2180             : GEN
    2181          14 : F2m_suppl(GEN x)
    2182             : {
    2183             :   GEN d;
    2184             :   long r;
    2185          14 :   init_suppl(x); d = F2m_gauss_pivot(F2m_copy(x), &r);
    2186          14 :   return get_suppl(x,d,mael(x,1,1),r,&F2v_ei);
    2187             : }
    2188             : 
    2189             : GEN
    2190          63 : Flm_suppl(GEN x, ulong p)
    2191             : {
    2192             :   GEN d;
    2193             :   long r;
    2194          63 :   init_suppl(x); d = Flm_pivots(x, p, &r, 0);
    2195          63 :   return get_suppl(x,d,nbrows(x),r,&vecsmall_ei);
    2196             : }
    2197             : 
    2198             : GEN
    2199           7 : F2xqM_suppl(GEN x, GEN T)
    2200             : {
    2201             :   void *E;
    2202           7 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2203           7 :   return gen_suppl(x, E, S, _F2xqM_mul);
    2204             : }
    2205             : 
    2206             : GEN
    2207          14 : FlxqM_suppl(GEN x, GEN T, ulong p)
    2208             : {
    2209             :   void *E;
    2210          14 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    2211          14 :   return gen_suppl(x, E, S, _FlxqM_mul);
    2212             : }
    2213             : 
    2214             : GEN
    2215        4123 : FqM_suppl(GEN x, GEN T, GEN p)
    2216             : {
    2217        4123 :   pari_sp av = avma;
    2218             :   GEN d;
    2219             :   long r;
    2220             : 
    2221        4123 :   if (!T) return FpM_suppl(x,p);
    2222        1225 :   init_suppl(x);
    2223        1225 :   d = FqM_gauss_pivot(x,T,p,&r);
    2224        1225 :   set_avma(av); return get_suppl(x,d,nbrows(x),r,&col_ei);
    2225             : }
    2226             : 
    2227             : static void
    2228       99356 : init_indexrank(GEN x) {
    2229       99356 :   (void)new_chunk(3 + 2*lg(x)); /* HACK */
    2230       99356 : }
    2231             : 
    2232             : GEN
    2233       28455 : FpM_indexrank(GEN x, GEN p) {
    2234       28455 :   pari_sp av = avma;
    2235             :   long r;
    2236             :   GEN d;
    2237       28455 :   init_indexrank(x);
    2238       28455 :   d = FpM_gauss_pivot(x,p,&r);
    2239       28455 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    2240             : }
    2241             : 
    2242             : GEN
    2243       19730 : Flm_indexrank(GEN x, ulong p) {
    2244       19730 :   pari_sp av = avma;
    2245             :   long r;
    2246             :   GEN d;
    2247       19730 :   init_indexrank(x);
    2248       19730 :   d = Flm_pivots(x, p, &r, 0);
    2249       19730 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    2250             : }
    2251             : 
    2252             : GEN
    2253           7 : F2m_indexrank(GEN x) {
    2254           7 :   pari_sp av = avma;
    2255             :   long r;
    2256             :   GEN d;
    2257           7 :   init_indexrank(x);
    2258           7 :   d = F2m_gauss_pivot(F2m_copy(x),&r);
    2259           7 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    2260             : }
    2261             : 
    2262             : GEN
    2263           7 : F2xqM_indexrank(GEN x, GEN T) {
    2264           7 :   pari_sp av = avma;
    2265             :   long r;
    2266             :   GEN d;
    2267           7 :   init_indexrank(x);
    2268           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    2269           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    2270             : }
    2271             : 
    2272             : GEN
    2273           7 : FlxqM_indexrank(GEN x, GEN T, ulong p) {
    2274           7 :   pari_sp av = avma;
    2275             :   long r;
    2276             :   GEN d;
    2277           7 :   init_indexrank(x);
    2278           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    2279           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    2280             : }
    2281             : 
    2282             : GEN
    2283           7 : FqM_indexrank(GEN x, GEN T, GEN p) {
    2284           7 :   pari_sp av = avma;
    2285             :   long r;
    2286             :   GEN d;
    2287           7 :   init_indexrank(x);
    2288           7 :   d = FqM_gauss_pivot(x, T, p, &r);
    2289           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    2290             : }
    2291             : 
    2292             : /*******************************************************************/
    2293             : /*                                                                 */
    2294             : /*                       Solve A*X=B (Gauss pivot)                 */
    2295             : /*                                                                 */
    2296             : /*******************************************************************/
    2297             : /* x ~ 0 compared to reference y */
    2298             : int
    2299      609481 : approx_0(GEN x, GEN y)
    2300             : {
    2301      609481 :   long tx = typ(x);
    2302      609481 :   if (tx == t_COMPLEX)
    2303         140 :     return approx_0(gel(x,1), y) && approx_0(gel(x,2), y);
    2304      609649 :   return gequal0(x) ||
    2305      418540 :          (tx == t_REAL && gexpo(y) - gexpo(x) > bit_prec(x));
    2306             : }
    2307             : /* x a column, x0 same column in the original input matrix (for reference),
    2308             :  * c list of pivots so far */
    2309             : static long
    2310      630859 : gauss_get_pivot_max(GEN X, GEN X0, long ix, GEN c)
    2311             : {
    2312      630859 :   GEN p, r, x = gel(X,ix), x0 = gel(X0,ix);
    2313      630859 :   long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
    2314      630859 :   if (c)
    2315             :   {
    2316      122473 :     for (i=1; i<lx; i++)
    2317       76004 :       if (!c[i])
    2318             :       {
    2319       38973 :         long e = gexpo(gel(x,i));
    2320       38973 :         if (e > ex) { ex = e; k = i; }
    2321             :       }
    2322             :   }
    2323             :   else
    2324             :   {
    2325     2056140 :     for (i=ix; i<lx; i++)
    2326             :     {
    2327     1471750 :       long e = gexpo(gel(x,i));
    2328     1471750 :       if (e > ex) { ex = e; k = i; }
    2329             :     }
    2330             :   }
    2331      630859 :   if (!k) return lx;
    2332      609306 :   p = gel(x,k);
    2333      609306 :   r = gel(x0,k); if (isrationalzero(r)) r = x0;
    2334      609306 :   return approx_0(p, r)? lx: k;
    2335             : }
    2336             : static long
    2337       51863 : gauss_get_pivot_padic(GEN X, GEN p, long ix, GEN c)
    2338             : {
    2339       51863 :   GEN x = gel(X, ix);
    2340       51863 :   long i, k = 0, ex = (long)HIGHVALPBIT, lx = lg(x);
    2341       51863 :   if (c)
    2342             :   {
    2343         504 :     for (i=1; i<lx; i++)
    2344         378 :       if (!c[i] && !gequal0(gel(x,i)))
    2345             :       {
    2346         245 :         long e = gvaluation(gel(x,i), p);
    2347         245 :         if (e < ex) { ex = e; k = i; }
    2348             :       }
    2349             :   }
    2350             :   else
    2351             :   {
    2352      421182 :     for (i=ix; i<lx; i++)
    2353      369445 :       if (!gequal0(gel(x,i)))
    2354             :       {
    2355      272251 :         long e = gvaluation(gel(x,i), p);
    2356      272251 :         if (e < ex) { ex = e; k = i; }
    2357             :       }
    2358             :   }
    2359       51863 :   return k? k: lx;
    2360             : }
    2361             : static long
    2362        3815 : gauss_get_pivot_NZ(GEN X, GEN x0/*unused*/, long ix, GEN c)
    2363             : {
    2364        3815 :   GEN x = gel(X, ix);
    2365        3815 :   long i, lx = lg(x);
    2366             :   (void)x0;
    2367        3815 :   if (c)
    2368             :   {
    2369       11634 :     for (i=1; i<lx; i++)
    2370       10780 :       if (!c[i] && !gequal0(gel(x,i))) return i;
    2371             :   }
    2372             :   else
    2373             :   {
    2374        2002 :     for (i=ix; i<lx; i++)
    2375        1988 :       if (!gequal0(gel(x,i))) return i;
    2376             :   }
    2377         868 :   return lx;
    2378             : }
    2379             : 
    2380             : /* Return pivot seeking function appropriate for the domain of the RgM x
    2381             :  * (first non zero pivot, maximal pivot...)
    2382             :  * x0 is a reference point used when guessing whether x[i,j] ~ 0
    2383             :  * (iff x[i,j] << x0[i,j]); typical case: mateigen, Gauss pivot on x - vp.Id,
    2384             :  * but use original x when deciding whether a prospective pivot is non-0 */
    2385             : static pivot_fun
    2386      207987 : get_pivot_fun(GEN x, GEN x0, GEN *data)
    2387             : {
    2388      207987 :   long i, j, hx, lx = lg(x);
    2389      207987 :   int res = t_INT;
    2390      207987 :   GEN p = NULL;
    2391             : 
    2392      207987 :   *data = NULL;
    2393      207987 :   if (lx == 1) return &gauss_get_pivot_NZ;
    2394      207952 :   hx = lgcols(x);
    2395      906515 :   for (j=1; j<lx; j++)
    2396             :   {
    2397      698605 :     GEN xj = gel(x,j);
    2398     3863789 :     for (i=1; i<hx; i++)
    2399             :     {
    2400     3165226 :       GEN c = gel(xj,i);
    2401     3165226 :       switch(typ(c))
    2402             :       {
    2403             :         case t_REAL:
    2404     1723371 :           res = t_REAL;
    2405     1723371 :           break;
    2406             :         case t_COMPLEX:
    2407         364 :           if (typ(gel(c,1)) == t_REAL || typ(gel(c,2)) == t_REAL) res = t_REAL;
    2408         364 :           break;
    2409             :         case t_INT: case t_INTMOD: case t_FRAC: case t_FFELT: case t_QUAD:
    2410             :         case t_POLMOD: /* exact types */
    2411     1167884 :           break;
    2412             :         case t_PADIC:
    2413      273565 :           p = gel(c,2);
    2414      273565 :           res = t_PADIC;
    2415      273565 :           break;
    2416          42 :         default: return &gauss_get_pivot_NZ;
    2417             :       }
    2418             :     }
    2419             :   }
    2420      207910 :   switch(res)
    2421             :   {
    2422      197960 :     case t_REAL: *data = x0; return &gauss_get_pivot_max;
    2423        8809 :     case t_PADIC: *data = p; return &gauss_get_pivot_padic;
    2424        1141 :     default: return &gauss_get_pivot_NZ;
    2425             :   }
    2426             : }
    2427             : 
    2428             : static GEN
    2429      203927 : get_col(GEN a, GEN b, GEN p, long li)
    2430             : {
    2431      203927 :   GEN u = cgetg(li+1,t_COL);
    2432             :   long i, j;
    2433             : 
    2434      203927 :   gel(u,li) = gdiv(gel(b,li), p);
    2435      906228 :   for (i=li-1; i>0; i--)
    2436             :   {
    2437      702301 :     pari_sp av = avma;
    2438      702301 :     GEN m = gel(b,i);
    2439      702301 :     for (j=i+1; j<=li; j++) m = gsub(m, gmul(gcoeff(a,i,j), gel(u,j)));
    2440      702301 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(a,i,i)));
    2441             :   }
    2442      203927 :   return u;
    2443             : }
    2444             : 
    2445             : /* bk -= m * bi */
    2446             : static void
    2447     3347852 : _submul(GEN b, long k, long i, GEN m)
    2448             : {
    2449     3347852 :   gel(b,k) = gsub(gel(b,k), gmul(m, gel(b,i)));
    2450     3347852 : }
    2451             : static int
    2452      730287 : init_gauss(GEN a, GEN *b, long *aco, long *li, int *iscol)
    2453             : {
    2454      730287 :   *iscol = *b ? (typ(*b) == t_COL): 0;
    2455      730287 :   *aco = lg(a) - 1;
    2456      730287 :   if (!*aco) /* a empty */
    2457             :   {
    2458          70 :     if (*b && lg(*b) != 1) pari_err_DIM("gauss");
    2459          70 :     *li = 0; return 0;
    2460             :   }
    2461      730217 :   *li = nbrows(a);
    2462      730217 :   if (*li < *aco) pari_err_INV("gauss [no left inverse]", a);
    2463      730217 :   if (*b)
    2464             :   {
    2465      715534 :     switch(typ(*b))
    2466             :     {
    2467             :       case t_MAT:
    2468       22882 :         if (lg(*b) == 1) return 0;
    2469       22882 :         *b = RgM_shallowcopy(*b);
    2470       22882 :         break;
    2471             :       case t_COL:
    2472      692652 :         *b = mkmat( leafcopy(*b) );
    2473      692652 :         break;
    2474           0 :       default: pari_err_TYPE("gauss",*b);
    2475             :     }
    2476      715534 :     if (nbrows(*b) != *li) pari_err_DIM("gauss");
    2477             :   }
    2478             :   else
    2479       14683 :     *b = matid(*li);
    2480      730217 :   return 1;
    2481             : }
    2482             : 
    2483             : static GEN
    2484         112 : RgM_inv_FpM(GEN a, GEN p)
    2485             : {
    2486             :   ulong pp;
    2487         112 :   a = RgM_Fp_init(a, p, &pp);
    2488         112 :   switch(pp)
    2489             :   {
    2490             :   case 0:
    2491          35 :     a = FpM_inv(a,p);
    2492          35 :     if (a) a = FpM_to_mod(a, p);
    2493          35 :     break;
    2494             :   case 2:
    2495          35 :     a = F2m_inv(a);
    2496          35 :     if (a) a = F2m_to_mod(a);
    2497          35 :     break;
    2498             :   default:
    2499          42 :     a = Flm_inv_sp(a, NULL, pp);
    2500          42 :     if (a) a = Flm_to_mod(a, pp);
    2501             :   }
    2502         112 :   return a;
    2503             : }
    2504             : 
    2505             : static GEN
    2506          42 : RgM_inv_FqM(GEN x, GEN pol, GEN p)
    2507             : {
    2508          42 :   pari_sp av = avma;
    2509          42 :   GEN b, T = RgX_to_FpX(pol, p);
    2510          42 :   if (signe(T) == 0) pari_err_OP("^",x,gen_m1);
    2511          42 :   b = FqM_inv(RgM_to_FqM(x, T, p), T, p);
    2512          42 :   if (!b) return gc_NULL(av);
    2513          28 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    2514             : }
    2515             : 
    2516             : #define code(t1,t2) ((t1 << 6) | t2)
    2517             : static GEN
    2518       74189 : RgM_inv_fast(GEN x)
    2519             : {
    2520             :   GEN p, pol;
    2521             :   long pa;
    2522       74189 :   long t = RgM_type(x, &p,&pol,&pa);
    2523       74189 :   switch(t)
    2524             :   {
    2525             :     case t_INT:    /* Fall back */
    2526       49172 :     case t_FRAC:   return QM_inv(x);
    2527         147 :     case t_FFELT:  return FFM_inv(x, pol);
    2528         112 :     case t_INTMOD: return RgM_inv_FpM(x, p);
    2529             :     case code(t_POLMOD, t_INTMOD):
    2530          42 :                    return RgM_inv_FqM(x, pol, p);
    2531       24716 :     default:       return gen_0;
    2532             :   }
    2533             : }
    2534             : #undef code
    2535             : 
    2536             : static GEN
    2537          49 : RgM_RgC_solve_FpC(GEN a, GEN b, GEN p)
    2538             : {
    2539          49 :   pari_sp av = avma;
    2540             :   ulong pp;
    2541          49 :   a = RgM_Fp_init(a, p, &pp);
    2542          49 :   switch(pp)
    2543             :   {
    2544             :   case 0:
    2545          14 :     b = RgC_to_FpC(b, p);
    2546          14 :     a = FpM_FpC_gauss(a,b,p);
    2547          14 :     return a ? gerepileupto(av, FpC_to_mod(a, p)): NULL;
    2548             :   case 2:
    2549          14 :     b = RgV_to_F2v(b);
    2550          14 :     a = F2m_F2c_gauss(a,b);
    2551          14 :     return a ? gerepileupto(av, F2c_to_mod(a)): NULL;
    2552             :   default:
    2553          21 :     b = RgV_to_Flv(b, pp);
    2554          21 :     a = Flm_Flc_gauss(a, b, pp);
    2555          21 :     return a ? gerepileupto(av, Flc_to_mod(a, pp)): NULL;
    2556             :   }
    2557             : }
    2558             : 
    2559             : static GEN
    2560          98 : RgM_solve_FpM(GEN a, GEN b, GEN p)
    2561             : {
    2562          98 :   pari_sp av = avma;
    2563             :   ulong pp;
    2564          98 :   a = RgM_Fp_init(a, p, &pp);
    2565          98 :   switch(pp)
    2566             :   {
    2567             :   case 0:
    2568          35 :     b = RgM_to_FpM(b, p);
    2569          35 :     a = FpM_gauss(a,b,p);
    2570          35 :     return a ? gerepileupto(av, FpM_to_mod(a, p)): NULL;
    2571             :   case 2:
    2572          21 :     b = RgM_to_F2m(b);
    2573          21 :     a = F2m_gauss(a,b);
    2574          21 :     return a ? gerepileupto(av, F2m_to_mod(a)): NULL;
    2575             :   default:
    2576          42 :     b = RgM_to_Flm(b, pp);
    2577          42 :     a = Flm_gauss(a,b,pp);
    2578          42 :     return a ? gerepileupto(av, Flm_to_mod(a, pp)): NULL;
    2579             :   }
    2580             : }
    2581             : 
    2582             : /* Gaussan Elimination. If a is square, return a^(-1)*b;
    2583             :  * if a has more rows than columns and b is NULL, return c such that c a = Id.
    2584             :  * a is a (not necessarily square) matrix
    2585             :  * b is a matrix or column vector, NULL meaning: take the identity matrix,
    2586             :  *   effectively returning the inverse of a
    2587             :  * If a and b are empty, the result is the empty matrix.
    2588             :  *
    2589             :  * li: number of rows of a and b
    2590             :  * aco: number of columns of a
    2591             :  * bco: number of columns of b (if matrix)
    2592             :  */
    2593             : static GEN
    2594      279416 : RgM_solve_basecase(GEN a, GEN b)
    2595             : {
    2596      279416 :   pari_sp av = avma;
    2597             :   long i, j, k, li, bco, aco;
    2598             :   int iscol;
    2599             :   pivot_fun pivot;
    2600             :   GEN p, u, data;
    2601             : 
    2602      279416 :   set_avma(av);
    2603             : 
    2604      279416 :   if (lg(a)-1 == 2 && nbrows(a) == 2) {
    2605             :     /* 2x2 matrix, start by inverting a */
    2606      105035 :     GEN u = gcoeff(a,1,1), v = gcoeff(a,1,2);
    2607      105035 :     GEN w = gcoeff(a,2,1), x = gcoeff(a,2,2);
    2608      105035 :     GEN D = gsub(gmul(u,x), gmul(v,w)), ainv;
    2609      105035 :     if (gequal0(D)) return NULL;
    2610      105035 :     ainv = mkmat2(mkcol2(x, gneg(w)), mkcol2(gneg(v), u));
    2611      105035 :     ainv = gmul(ainv, ginv(D));
    2612      105035 :     if (b) ainv = gmul(ainv, b);
    2613      105035 :     return gerepileupto(av, ainv);
    2614             :   }
    2615             : 
    2616      174381 :   if (!init_gauss(a, &b, &aco, &li, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    2617      174381 :   pivot = get_pivot_fun(a, a, &data);
    2618      174381 :   a = RgM_shallowcopy(a);
    2619      174381 :   bco = lg(b)-1;
    2620      174381 :   if(DEBUGLEVEL>4) err_printf("Entering gauss\n");
    2621             : 
    2622      174381 :   p = NULL; /* gcc -Wall */
    2623      576600 :   for (i=1; i<=aco; i++)
    2624             :   {
    2625             :     /* k is the line where we find the pivot */
    2626      576600 :     k = pivot(a, data, i, NULL);
    2627      576600 :     if (k > li) return NULL;
    2628      576586 :     if (k != i)
    2629             :     { /* exchange the lines s.t. k = i */
    2630      125132 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    2631      125132 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    2632             :     }
    2633      576586 :     p = gcoeff(a,i,i);
    2634      576586 :     if (i == aco) break;
    2635             : 
    2636     1269518 :     for (k=i+1; k<=li; k++)
    2637             :     {
    2638      867299 :       GEN m = gcoeff(a,k,i);
    2639      867299 :       if (!gequal0(m))
    2640             :       {
    2641      664769 :         m = gdiv(m,p);
    2642      664769 :         for (j=i+1; j<=aco; j++) _submul(gel(a,j),k,i,m);
    2643      664769 :         for (j=1;   j<=bco; j++) _submul(gel(b,j),k,i,m);
    2644             :       }
    2645             :     }
    2646      402219 :     if (gc_needed(av,1))
    2647             :     {
    2648          12 :       if(DEBUGMEM>1) pari_warn(warnmem,"gauss. i=%ld",i);
    2649          12 :       gerepileall(av,2, &a,&b);
    2650             :     }
    2651             :   }
    2652             : 
    2653      174367 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
    2654      174367 :   u = cgetg(bco+1,t_MAT);
    2655      174367 :   for (j=1; j<=bco; j++) gel(u,j) = get_col(a,gel(b,j),p,aco);
    2656      174367 :   return gerepilecopy(av, iscol? gel(u,1): u);
    2657             : }
    2658             : 
    2659             : static GEN
    2660      265263 : RgM_RgC_solve_fast(GEN x, GEN y)
    2661             : {
    2662             :   GEN p, pol;
    2663             :   long pa;
    2664      265263 :   long t = RgM_RgC_type(x, y, &p,&pol,&pa);
    2665      265263 :   switch(t)
    2666             :   {
    2667       14546 :     case t_INT:    return ZM_gauss(x, y);
    2668          42 :     case t_FRAC:   return QM_gauss(x, y);
    2669          49 :     case t_INTMOD: return RgM_RgC_solve_FpC(x, y, p);
    2670          56 :     case t_FFELT:  return FFM_FFC_gauss(x, y, pol);
    2671      250570 :     default:       return gen_0;
    2672             :   }
    2673             : }
    2674             : 
    2675             : static GEN
    2676        4340 : RgM_solve_fast(GEN x, GEN y)
    2677             : {
    2678             :   GEN p, pol;
    2679             :   long pa;
    2680        4340 :   long t = RgM_type2(x, y, &p,&pol,&pa);
    2681        4340 :   switch(t)
    2682             :   {
    2683          42 :     case t_INT:    return ZM_gauss(x, y);
    2684           7 :     case t_FRAC:   return QM_gauss(x, y);
    2685          98 :     case t_INTMOD: return RgM_solve_FpM(x, y, p);
    2686          63 :     case t_FFELT:  return FFM_gauss(x, y, pol);
    2687        4130 :     default:       return gen_0;
    2688             :   }
    2689             : }
    2690             : 
    2691             : GEN
    2692      269603 : RgM_solve(GEN a, GEN b)
    2693             : {
    2694      269603 :   pari_sp av = avma;
    2695             :   GEN u;
    2696      269603 :   if (!b) return RgM_inv(a);
    2697      269603 :   u = typ(b)==t_MAT ? RgM_solve_fast(a, b): RgM_RgC_solve_fast(a, b);
    2698      269603 :   if (!u) { set_avma(av); return u; }
    2699      269505 :   if (u != gen_0) return u;
    2700      254700 :   return RgM_solve_basecase(a, b);
    2701             : }
    2702             : 
    2703             : GEN
    2704       74189 : RgM_inv(GEN a)
    2705             : {
    2706       74189 :   GEN b = RgM_inv_fast(a);
    2707       74175 :   return b==gen_0? RgM_solve_basecase(a, NULL): b;
    2708             : }
    2709             : 
    2710             : /* assume dim A >= 1, A invertible + upper triangular  */
    2711             : static GEN
    2712      363483 : RgM_inv_upper_ind(GEN A, long index)
    2713             : {
    2714      363483 :   long n = lg(A)-1, i = index, j;
    2715      363483 :   GEN u = zerocol(n);
    2716      363483 :   gel(u,i) = ginv(gcoeff(A,i,i));
    2717     1429040 :   for (i--; i>0; i--)
    2718             :   {
    2719     1065557 :     pari_sp av = avma;
    2720     1065557 :     GEN m = gneg(gmul(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2721     1065557 :     for (j=i+2; j<=n; j++) m = gsub(m, gmul(gcoeff(A,i,j),gel(u,j)));
    2722     1065557 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(A,i,i)));
    2723             :   }
    2724      363483 :   return u;
    2725             : }
    2726             : GEN
    2727       75558 : RgM_inv_upper(GEN A)
    2728             : {
    2729             :   long i, l;
    2730       75558 :   GEN B = cgetg_copy(A, &l);
    2731       75558 :   for (i = 1; i < l; i++) gel(B,i) = RgM_inv_upper_ind(A, i);
    2732       75558 :   return B;
    2733             : }
    2734             : 
    2735             : static GEN
    2736     1001302 : split_realimag_col(GEN z, long r1, long r2)
    2737             : {
    2738     1001302 :   long i, ru = r1+r2;
    2739     1001302 :   GEN x = cgetg(ru+r2+1,t_COL), y = x + r2;
    2740     3004725 :   for (i=1; i<=r1; i++) {
    2741     2003423 :     GEN a = gel(z,i);
    2742     2003423 :     if (typ(a) == t_COMPLEX) a = gel(a,1); /* paranoia: a should be real */
    2743     2003423 :     gel(x,i) = a;
    2744             :   }
    2745     1680736 :   for (   ; i<=ru; i++) {
    2746      679434 :     GEN b, a = gel(z,i);
    2747      679434 :     if (typ(a) == t_COMPLEX) { b = gel(a,2); a = gel(a,1); } else b = gen_0;
    2748      679434 :     gel(x,i) = a;
    2749      679434 :     gel(y,i) = b;
    2750             :   }
    2751     1001302 :   return x;
    2752             : }
    2753             : GEN
    2754      526123 : split_realimag(GEN x, long r1, long r2)
    2755             : {
    2756             :   long i,l; GEN y;
    2757      526123 :   if (typ(x) == t_COL) return split_realimag_col(x,r1,r2);
    2758      258767 :   y = cgetg_copy(x, &l);
    2759      258767 :   for (i=1; i<l; i++) gel(y,i) = split_realimag_col(gel(x,i), r1, r2);
    2760      258767 :   return y;
    2761             : }
    2762             : 
    2763             : /* assume M = (r1+r2) x (r1+2r2) matrix and y compatible vector or matrix
    2764             :  * r1 first lines of M,y are real. Solve the system obtained by splitting
    2765             :  * real and imaginary parts. */
    2766             : GEN
    2767      253706 : RgM_solve_realimag(GEN M, GEN y)
    2768             : {
    2769      253706 :   long l = lg(M), r2 = l - lgcols(M), r1 = l-1 - 2*r2;
    2770      253706 :   return RgM_solve(split_realimag(M, r1,r2),
    2771             :                    split_realimag(y, r1,r2));
    2772             : }
    2773             : 
    2774             : GEN
    2775         420 : gauss(GEN a, GEN b)
    2776             : {
    2777             :   GEN z;
    2778         420 :   long t = typ(b);
    2779         420 :   if (typ(a)!=t_MAT) pari_err_TYPE("gauss",a);
    2780         420 :   if (t!=t_COL && t!=t_MAT) pari_err_TYPE("gauss",b);
    2781         420 :   z = RgM_solve(a,b);
    2782         420 :   if (!z) pari_err_INV("gauss",a);
    2783         315 :   return z;
    2784             : }
    2785             : 
    2786             : static GEN
    2787      555214 : ZlM_gauss_ratlift(GEN a, GEN b, ulong p, long e, GEN C)
    2788             : {
    2789      555214 :   pari_sp av = avma, av2;
    2790             :   GEN bb, xi, xb, pi, q, B, r;
    2791             :   long i, f, k;
    2792             :   ulong mask;
    2793      555214 :   if (!C) {
    2794           0 :     C = Flm_inv(ZM_to_Flm(a, p), p);
    2795           0 :     if (!C) pari_err_INV("ZlM_gauss", a);
    2796             :   }
    2797      555214 :   k = f = ZM_max_lg(a)-1;
    2798      555214 :   mask = quadratic_prec_mask((e+f-1)/f);
    2799      555214 :   pi = q = powuu(p, f);
    2800      555214 :   bb = b;
    2801      555214 :   C = ZpM_invlift(FpM_red(a, q), Flm_to_ZM(C), utoi(p), f);
    2802      555214 :   av2 = avma;
    2803      555214 :   xb = xi = FpM_mul(C, b, q);
    2804      665733 :   for (i = f; i <= e; i+=f)
    2805             :   {
    2806      241168 :     if (i==k)
    2807             :     {
    2808      227321 :       k = (mask&1UL) ? 2*k-f: 2*k;
    2809      227321 :       mask >>= 1;
    2810      227321 :       B = sqrti(shifti(pi,-1));
    2811      227321 :       r = FpM_ratlift(xb, pi, B, B, NULL);
    2812      227321 :       if (r)
    2813             :       {
    2814      157747 :         GEN dr, nr = Q_remove_denom(r,&dr);
    2815      157747 :         if (ZM_equal(ZM_mul(a,nr), dr? ZM_Z_mul(b,dr): b))
    2816             :         {
    2817      130649 :           if (DEBUGLEVEL>=4)
    2818           0 :             err_printf("ZlM_gauss: early solution: %ld/%ld\n",i,e);
    2819      130649 :           return gerepilecopy(av, r);
    2820             :         }
    2821             :       }
    2822             :     }
    2823      110519 :     bb = ZM_Z_divexact(ZM_sub(bb, ZM_mul(a, xi)), q);
    2824      110519 :     if (gc_needed(av,2))
    2825             :     {
    2826           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"ZlM_gauss. i=%ld/%ld",i,e);
    2827           0 :       gerepileall(av2,3, &pi,&bb,&xb);
    2828             :     }
    2829      110519 :     xi = FpM_mul(C, bb, q);
    2830      110519 :     xb = ZM_add(xb, ZM_Z_mul(xi, pi));
    2831      110519 :     pi = mulii(pi, q);
    2832             :   }
    2833      424565 :   B = sqrti(shifti(pi,-1));
    2834      424565 :   return gerepileupto(av, FpM_ratlift(xb, pi, B, B, NULL));
    2835             : }
    2836             : 
    2837             : /* Dixon p-adic lifting algorithm.
    2838             :  * Numer. Math. 40, 137-141 (1982), DOI: 10.1007/BF01459082 */
    2839             : GEN
    2840      555906 : ZM_gauss(GEN a, GEN b0)
    2841             : {
    2842      555906 :   pari_sp av = avma, av2;
    2843             :   int iscol;
    2844             :   long n, ncol, i, m, elim;
    2845             :   ulong p;
    2846      555906 :   GEN C, delta, nb, nmin, res, b = b0;
    2847             :   forprime_t S;
    2848             : 
    2849      555906 :   if (!init_gauss(a, &b, &n, &ncol, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    2850      555836 :   nb = gen_0; ncol = lg(b);
    2851     1177359 :   for (i = 1; i < ncol; i++)
    2852             :   {
    2853      621523 :     GEN ni = gnorml2(gel(b, i));
    2854      621523 :     if (cmpii(nb, ni) < 0) nb = ni;
    2855             :   }
    2856      555836 :   if (!signe(nb)) {set_avma(av); return iscol? zerocol(n): zeromat(n,lg(b)-1);}
    2857      555235 :   delta = gen_1; nmin = nb;
    2858     2253752 :   for (i = 1; i <= n; i++)
    2859             :   {
    2860     1698517 :     GEN ni = gnorml2(gel(a, i));
    2861     1698517 :     if (cmpii(ni, nmin) < 0)
    2862             :     {
    2863       30141 :       delta = mulii(delta, nmin); nmin = ni;
    2864             :     }
    2865             :     else
    2866     1668376 :       delta = mulii(delta, ni);
    2867             :   }
    2868      555235 :   if (!signe(nmin)) return NULL;
    2869      555221 :   elim = expi(delta)+1;
    2870      555221 :   av2 = avma;
    2871      555221 :   init_modular_big(&S);
    2872             :   for(;;)
    2873             :   {
    2874      555221 :     p = u_forprime_next(&S);
    2875      555221 :     C = Flm_inv_sp(ZM_to_Flm(a, p), NULL, p);
    2876      555221 :     if (C) break;
    2877           7 :     elim -= expu(p);
    2878           7 :     if (elim < 0) return NULL;
    2879           0 :     set_avma(av2);
    2880             :   }
    2881             :   /* N.B. Our delta/lambda are SQUARES of those in the paper
    2882             :    * log(delta lambda) / log p, where lambda is 3+sqrt(5) / 2,
    2883             :    * whose log is < 1, hence + 1 (to cater for rounding errors) */
    2884      555214 :   m = (long)ceil((dbllog2(delta)*M_LN2 + 1) / log((double)p));
    2885      555214 :   res = ZlM_gauss_ratlift(a, b, p, m, C);
    2886      555214 :   if (iscol) return gerepilecopy(av, gel(res, 1));
    2887       20629 :   return gerepileupto(av, res);
    2888             : }
    2889             : 
    2890             : static GEN
    2891           0 : RgC_inflate(GEN K, GEN v, long n)
    2892             : {
    2893           0 :   GEN c = zerocol(n);
    2894           0 :   long j, l = lg(K);
    2895           0 :   for (j = 1; j < l; j++) gel(c, v[j]) = gel(K, j);
    2896           0 :   return c;
    2897             : }
    2898             : /* same as above, M rational; if flag = 1, call indexrank and return 1 sol */
    2899             : GEN
    2900        1708 : QM_gauss_i(GEN M, GEN B, long flag)
    2901             : {
    2902        1708 :   pari_sp av = avma;
    2903        1708 :   long i, l = lg(M);
    2904        1708 :   GEN K, cB, N = cgetg_copy(M, &l), v = cgetg(l, t_VEC), z2 = NULL;
    2905        9450 :   for (i = 1; i < l; i++)
    2906        7742 :     gel(N,i) = Q_primitive_part(gel(M,i), &gel(v,i));
    2907        1708 :   if (flag)
    2908             :   {
    2909         301 :     GEN z = ZM_indexrank(N), z1 = gel(z,1);
    2910         301 :     z2 = gel(z,2);
    2911         301 :     N = shallowmatextract(N, z1, z2);
    2912         301 :     B = typ(B) == t_MAT? rowpermute(B,z1): vecpermute(B,z1);
    2913         301 :     if (lg(z2) == l) z2 = NULL; else { v = vecpermute(v, z2); l = lg(v); }
    2914             :   }
    2915        1708 :   B = Q_primitive_part(B, &cB);
    2916        1708 :   K = ZM_gauss(N, B); if (!K) { set_avma(av); return NULL; }
    2917        9450 :   for (i = 1; i < l; i++)
    2918             :   {
    2919        7742 :     GEN c, k = gel(K,i), d = gel(v,i);
    2920        7742 :     if (d)
    2921             :     {
    2922        5644 :       if (isintzero(d))
    2923             :       {
    2924           0 :         if (gequal0(k)) continue;
    2925           0 :         return NULL;
    2926             :       }
    2927        5644 :       d = inv_content(d);
    2928             :     }
    2929        7742 :     c = mul_content(cB, d);
    2930        7742 :     if (c) gel(K,i) = gmul(gel(K,i), c);
    2931             :   }
    2932        1708 :   if (z2)
    2933             :   {
    2934           0 :     long n = lg(M)-1;
    2935           0 :     if (typ(B) == t_COL) K = RgC_inflate(K, z2, n);
    2936             :     else
    2937             :     {
    2938           0 :       l = lg(B);
    2939           0 :       for (i = 1; i < l; i++) gel(B,i) = RgC_inflate(gel(B,i), z2, n);
    2940             :     }
    2941             :   }
    2942        1708 :   return gerepilecopy(av, K);
    2943             : }
    2944             : GEN
    2945        1407 : QM_gauss(GEN M, GEN B) { return QM_gauss_i(M, B, 0); }
    2946             : 
    2947             : static GEN
    2948      146070 : ZM_inv_slice(GEN A, GEN P, GEN *mod)
    2949             : {
    2950      146070 :   pari_sp av = avma;
    2951      146070 :   long i, n = lg(P)-1;
    2952             :   GEN H, T;
    2953      146070 :   if (n == 1)
    2954             :   {
    2955      143791 :     ulong p = uel(P,1);
    2956      143791 :     GEN Hp, a = ZM_to_Flm(A, p);
    2957      143791 :     Hp = Flm_adjoint(a, p);
    2958      143790 :     Hp = gerepileupto(av, Flm_to_ZM(Hp));
    2959      143789 :     *mod = utoi(p); return Hp;
    2960             :   }
    2961        2279 :   T = ZV_producttree(P);
    2962        2279 :   A = ZM_nv_mod_tree(A, P, T);
    2963        2279 :   H = cgetg(n+1, t_VEC);
    2964        7933 :   for(i=1; i <= n; i++)
    2965        5654 :     gel(H,i) = Flm_adjoint(gel(A, i), uel(P,i));
    2966        2279 :   H = nmV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    2967        2279 :   *mod = gmael(T, lg(T)-1, 1);
    2968        2279 :   gerepileall(av, 2, &H, mod);
    2969        2279 :   return H;
    2970             : }
    2971             : 
    2972             : static GEN
    2973      116379 : RgM_true_Hadamard(GEN a)
    2974             : {
    2975      116379 :   pari_sp av = avma;
    2976      116379 :   long n = lg(a)-1, i;
    2977             :   GEN B;
    2978      116379 :   if (n == 0) return gen_1;
    2979      116379 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    2980      116379 :   B = gnorml2(gel(a,1));
    2981      116379 :   for (i = 2; i <= n; i++) B = gmul(B, gnorml2(gel(a,i)));
    2982      116379 :   return gerepileuptoint(av, ceil_safe(sqrtr(B)));
    2983             : }
    2984             : 
    2985             : GEN
    2986      146070 : ZM_inv_worker(GEN P, GEN A)
    2987             : {
    2988      146070 :   GEN V = cgetg(3, t_VEC);
    2989      146070 :   gel(V,1) = ZM_inv_slice(A, P, &gel(V,2));
    2990      146068 :   return V;
    2991             : }
    2992             : 
    2993             : static GEN
    2994        5516 : ZM_inv0(GEN A, GEN *pden)
    2995             : {
    2996        5516 :   if (pden) *pden = gen_1;
    2997        5516 :   (void)A; return cgetg(1, t_MAT);
    2998             : }
    2999             : static GEN
    3000       30555 : ZM_inv1(GEN A, GEN *pden)
    3001             : {
    3002       30555 :   GEN a = gcoeff(A,1,1);
    3003       30555 :   long s = signe(a);
    3004       30555 :   if (!s) return NULL;
    3005       30555 :   if (pden) *pden = absi(a);
    3006       30555 :   retmkmat(mkcol(s == 1? gen_1: gen_m1));
    3007             : }
    3008             : static GEN
    3009       66272 : ZM_inv2(GEN A, GEN *pden)
    3010             : {
    3011             :   GEN a, b, c, d, D, cA;
    3012             :   long s;
    3013       66272 :   A = Q_primitive_part(A, &cA);
    3014       66272 :   a = gcoeff(A,1,1); b = gcoeff(A,1,2);
    3015       66272 :   c = gcoeff(A,2,1); d = gcoeff(A,2,2);
    3016       66272 :   D = subii(mulii(a,d), mulii(b,c)); /* left on stack */
    3017       66272 :   s = signe(D);
    3018       66272 :   if (!s) return NULL;
    3019       66272 :   if (s < 0) D = negi(D);
    3020       66272 :   if (pden) *pden = mul_denom(D, cA);
    3021       66272 :   if (s > 0)
    3022       43702 :     retmkmat2(mkcol2(icopy(d), negi(c)), mkcol2(negi(b), icopy(a)));
    3023             :   else
    3024       22570 :     retmkmat2(mkcol2(negi(d), icopy(c)), mkcol2(icopy(b), negi(a)));
    3025             : }
    3026             : 
    3027             : /* to be used when denom(M^(-1)) << det(M) and a sharp multiple is
    3028             :  * not available. Return H primitive such that M*H = den*Id */
    3029             : GEN
    3030           0 : ZM_inv_ratlift(GEN M, GEN *pden)
    3031             : {
    3032           0 :   pari_sp av2, av = avma;
    3033             :   GEN Hp, q, H;
    3034             :   ulong p;
    3035           0 :   long m = lg(M)-1;
    3036             :   forprime_t S;
    3037             :   pari_timer ti;
    3038             : 
    3039           0 :   if (m == 0) return ZM_inv0(M,pden);
    3040           0 :   if (m == 1 && nbrows(M)==1) return ZM_inv1(M,pden);
    3041           0 :   if (m == 2 && nbrows(M)==2) return ZM_inv2(M,pden);
    3042             : 
    3043           0 :   if (DEBUGLEVEL>5) timer_start(&ti);
    3044           0 :   init_modular_big(&S);
    3045           0 :   av2 = avma;
    3046           0 :   H = NULL;
    3047           0 :   while ((p = u_forprime_next(&S)))
    3048             :   {
    3049             :     GEN Mp, B, Hr;
    3050           0 :     Mp = ZM_to_Flm(M,p);
    3051           0 :     Hp = Flm_inv_sp(Mp, NULL, p);
    3052           0 :     if (!Hp) continue;
    3053           0 :     if (!H)
    3054             :     {
    3055           0 :       H = ZM_init_CRT(Hp, p);
    3056           0 :       q = utoipos(p);
    3057             :     }
    3058             :     else
    3059           0 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3060           0 :     B = sqrti(shifti(q,-1));
    3061           0 :     Hr = FpM_ratlift(H,q,B,B,NULL);
    3062           0 :     if (DEBUGLEVEL>5)
    3063           0 :       timer_printf(&ti,"ZM_inv mod %lu (ratlift=%ld)", p,!!Hr);
    3064           0 :     if (Hr) {/* DONE ? */
    3065           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    3066           0 :       if (ZM_isscalar(ZM_mul(Hl, M), *pden)) { H = Hl; break; }
    3067             :     }
    3068             : 
    3069           0 :     if (gc_needed(av,2))
    3070             :     {
    3071           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv_ratlift");
    3072           0 :       gerepileall(av2, 2, &H, &q);
    3073             :     }
    3074             :   }
    3075           0 :   if (!*pden) *pden = gen_1;
    3076           0 :   gerepileall(av, 2, &H, pden);
    3077           0 :   return H;
    3078             : }
    3079             : 
    3080             : GEN
    3081       68481 : FpM_ratlift_worker(GEN A, GEN mod, GEN B)
    3082             : {
    3083             :   long l, i;
    3084       68481 :   GEN H = cgetg_copy(A, &l);
    3085      138935 :   for (i = 1; i < l; i++)
    3086             :   {
    3087       70452 :      GEN c = FpC_ratlift(gel(A,i), mod, B, B, NULL);
    3088       70454 :      gel(H,i) = c? c: gen_0;
    3089             :   }
    3090       68483 :   return H;
    3091             : }
    3092             : static int
    3093      132207 : can_ratlift(GEN x, GEN mod, GEN B)
    3094             : {
    3095      132207 :   pari_sp av = avma;
    3096             :   GEN a, b;
    3097      132207 :   return gc_bool(av, Fp_ratlift(x, mod, B, B, &a,&b));
    3098             : }
    3099             : static GEN
    3100      198404 : FpM_ratlift_parallel(GEN A, GEN mod, GEN B)
    3101             : {
    3102      198404 :   pari_sp av = avma;
    3103             :   GEN worker;
    3104      198404 :   long i, l = lg(A), m = pari_mt_nbthreads;
    3105      198404 :   int test = !!B;
    3106             : 
    3107      198404 :   if (l == 1 || lgcols(A) == 1) return gcopy(A);
    3108      198404 :   if (!B) B = sqrti(shifti(mod,-1));
    3109      198404 :   if (m == 1 || l == 2 || lgcols(A) < 10)
    3110             :   {
    3111      191872 :     A = FpM_ratlift(A, mod, B, B, NULL);
    3112      191872 :     return A? A: gc_NULL(av);
    3113             :   }
    3114             :   /* test one coefficient first */
    3115        6532 :   if (test && !can_ratlift(gcoeff(A,1,1), mod, B)) return gc_NULL(av);
    3116        6454 :   worker = snm_closure(is_entry("_FpM_ratlift_worker"), mkvec2(mod,B));
    3117        6454 :   A = gen_parapply_slice(worker, A, m);
    3118        6454 :   for (i = 1; i < l; i++) if (typ(gel(A,i)) != t_COL) return gc_NULL(av);
    3119        5661 :   return A;
    3120             : }
    3121             : 
    3122             : static GEN
    3123      125830 : ZM_adj_ratlift(GEN A, GEN H, GEN mod, GEN T)
    3124             : {
    3125      125830 :   pari_sp av = avma;
    3126             :   GEN B, D, g;
    3127      125830 :   D = ZMrow_ZC_mul(H, gel(A,1), 1);
    3128      125830 :   if (T) D = mulii(T, D);
    3129      125830 :   g = gcdii(D, mod);
    3130      125830 :   if (!equali1(g))
    3131             :   {
    3132          14 :     mod = diviiexact(mod, g);
    3133          14 :     H = FpM_red(H, mod);
    3134             :   }
    3135      125830 :   D = Fp_inv(Fp_red(D, mod), mod);
    3136             :   /* test 1 coeff first */
    3137      125830 :   B = sqrti(shifti(mod,-1));
    3138      125830 :   if (!can_ratlift(Fp_mul(D, gcoeff(A,1,1), mod), mod, B)) return gc_NULL(av);
    3139      120924 :   H = FpM_Fp_mul(H, D, mod);
    3140      120924 :   H = FpM_ratlift_parallel(H, mod, B);
    3141      120924 :   return H? H: gc_NULL(av);
    3142             : }
    3143             : 
    3144             : /* if (T) return T A^(-1) in Mn(Q), else B in Mn(Z) such that A B = den*Id */
    3145             : static GEN
    3146      218729 : ZM_inv_i(GEN A, GEN *pden, GEN T)
    3147             : {
    3148      218729 :   pari_sp av = avma;
    3149      218729 :   long m = lg(A)-1, n, k1 = 1, k2;
    3150      218729 :   GEN H = NULL, D, H1 = NULL, mod1 = NULL, worker;
    3151             :   ulong bnd, mask;
    3152             :   forprime_t S;
    3153             :   pari_timer ti;
    3154             : 
    3155      218729 :   if (m == 0) return ZM_inv0(A,pden);
    3156      213213 :   if (pden) *pden = gen_1;
    3157      213213 :   if (nbrows(A) < m) return NULL;
    3158      213206 :   if (m == 1 && nbrows(A)==1 && !T) return ZM_inv1(A,pden);
    3159      182651 :   if (m == 2 && nbrows(A)==2 && !T) return ZM_inv2(A,pden);
    3160             : 
    3161      116379 :   if (DEBUGLEVEL>=5) timer_start(&ti);
    3162      116379 :   init_modular_big(&S);
    3163      116379 :   bnd = expi(RgM_true_Hadamard(A));
    3164      116379 :   worker = snm_closure(is_entry("_ZM_inv_worker"), mkvec(A));
    3165      116379 :   gen_inccrt("ZM_inv_r", worker, NULL, k1, m, &S, &H1, &mod1, nmV_chinese_center, FpM_center);
    3166      116379 :   n = (bnd+1)/expu(S.p)+1;
    3167      116379 :   if (DEBUGLEVEL>=5) timer_printf(&ti,"inv (%ld/%ld primes)", k1, n);
    3168      116379 :   mask = quadratic_prec_mask(n);
    3169      116379 :   for (k2 = 0;;)
    3170       20543 :   {
    3171             :     GEN Hr;
    3172      136922 :     if (k2 > 0)
    3173             :     {
    3174       17204 :       gen_inccrt("ZM_inv_r", worker, NULL, k2, m, &S, &H1, &mod1,nmV_chinese_center,FpM_center);
    3175       17204 :       k1 += k2;
    3176       17204 :       if (DEBUGLEVEL>=5) timer_printf(&ti,"CRT (%ld/%ld primes)", k1, n);
    3177             :     }
    3178      136922 :     if (mask == 1) break;
    3179      125830 :     k2 = (mask&1UL) ? k1-1: k1;
    3180      125830 :     mask >>= 1;
    3181             : 
    3182      125830 :     Hr = ZM_adj_ratlift(A, H1, mod1, T);
    3183      125830 :     if (DEBUGLEVEL>=5) timer_printf(&ti,"ratlift (%ld/%ld primes)", k1, n);
    3184      125830 :     if (Hr) {/* DONE ? */
    3185      106948 :       GEN Hl = Q_primpart(Hr), R = ZM_mul(Hl, A), d = gcoeff(R,1,1);
    3186      106948 :       if (gsigne(d) < 0) { d = gneg(d); Hl = ZM_neg(Hl); }
    3187      106948 :       if (DEBUGLEVEL>=5) timer_printf(&ti,"mult (%ld/%ld primes)", k1, n);
    3188      106948 :       if (equali1(d))
    3189             :       {
    3190       85662 :         if (ZM_isidentity(R)) { H = Hl; break; }
    3191             :       }
    3192       21286 :       else if (ZM_isscalar(R, d))
    3193             :       {
    3194       19625 :         if (T) T = gdiv(T,d);
    3195       17025 :         else if (pden) *pden = d;
    3196       19625 :         H = Hl; break;
    3197             :       }
    3198             :     }
    3199             :   }
    3200      116379 :   if (!H)
    3201             :   {
    3202             :     GEN d;
    3203       11092 :     H = H1;
    3204       11092 :     D = ZMrow_ZC_mul(H, gel(A,1), 1);
    3205       11092 :     if (signe(D)==0) pari_err_INV("ZM_inv", A);
    3206       11092 :     if (T) T = gdiv(T, D);
    3207             :     else
    3208             :     {
    3209       10766 :       d = gcdii(Q_content_safe(H), D);
    3210       10766 :       if (signe(D) < 0) d = negi(d);
    3211       10766 :       if (!equali1(d))
    3212             :       {
    3213        6717 :         H = ZM_Z_divexact(H, d);
    3214        6717 :         D = diviiexact(D, d);
    3215             :       }
    3216       10766 :       if (pden) *pden = D;
    3217             :     }
    3218             :   }
    3219      116379 :   if (T && !isint1(T)) H = ZM_Q_mul(H, T);
    3220      116379 :   gerepileall(av, pden? 2: 1, &H, pden);
    3221      116379 :   return H;
    3222             : }
    3223             : GEN
    3224      167870 : ZM_inv(GEN A, GEN *pden) { return ZM_inv_i(A, pden, NULL); }
    3225             : 
    3226             : /* same as above, M rational */
    3227             : GEN
    3228       50859 : QM_inv(GEN M)
    3229             : {
    3230       50859 :   pari_sp av = avma;
    3231             :   GEN den, dM, K;
    3232       50859 :   M = Q_remove_denom(M, &dM);
    3233       50859 :   K = ZM_inv_i(M, &den, dM);
    3234       50859 :   if (!K) return gc_NULL(av);
    3235       50852 :   if (den && !equali1(den)) K = ZM_Q_mul(K, ginv(den));
    3236       50838 :   return gerepileupto(av, K);
    3237             : }
    3238             : 
    3239             : static GEN
    3240       96771 : ZM_ker_filter(GEN A, GEN P)
    3241             : {
    3242       96771 :   long i, j, l = lg(A), n = 1, d = lg(gmael(A,1,1));
    3243       96771 :   GEN B, Q, D = gmael(A,1,2);
    3244      201486 :   for (i=2; i<l; i++)
    3245             :   {
    3246      104715 :     GEN Di = gmael(A,i,2);
    3247      104715 :     long di = lg(gmael(A,i,1));
    3248      104715 :     int c = vecsmall_lexcmp(D, Di);
    3249      104715 :     if (di==d && c==0) n++;
    3250       45588 :     else if (d > di || (di==d && c>0))
    3251       37680 :     { n = 1; d = di; D = Di; }
    3252             :   }
    3253       96771 :   B = cgetg(n+1, t_VEC);
    3254       96771 :   Q = cgetg(n+1, typ(P));
    3255      298257 :   for (i=1, j=1; i<l; i++)
    3256             :   {
    3257      201486 :     if (lg(gmael(A,i,1))==d &&  vecsmall_lexcmp(D, gmael(A,i,2))==0)
    3258             :     {
    3259      155898 :       gel(B,j) = gmael(A,i,1);
    3260      155898 :       Q[j] = P[i];
    3261      155898 :       j++;
    3262             :     }
    3263             :   }
    3264       96771 :   return mkvec3(B,Q,D);
    3265             : }
    3266             : 
    3267             : static GEN
    3268       94053 : ZM_ker_chinese(GEN A, GEN P, GEN *mod)
    3269             : {
    3270       94053 :   GEN BQD = ZM_ker_filter(A, P);
    3271       94053 :   return mkvec2(nmV_chinese_center(gel(BQD,1), gel(BQD,2), mod), gel(BQD,3));
    3272             : }
    3273             : 
    3274             : static GEN
    3275      159187 : ZM_ker_slice(GEN A, GEN P, GEN *mod)
    3276             : {
    3277      159187 :   pari_sp av = avma;
    3278      159187 :   long i, n = lg(P)-1;
    3279             :   GEN BQD, D, H, T, Q;
    3280      159187 :   if (n == 1)
    3281             :   {
    3282      156469 :     ulong p = uel(P,1);
    3283      156469 :     GEN K = Flm_ker_sp(ZM_to_Flm(A, p), p, 2);
    3284      156469 :     *mod = utoi(p);
    3285      156468 :     return mkvec2(Flm_to_ZM(gel(K,1)), gel(K,2));
    3286             :   }
    3287        2718 :   T = ZV_producttree(P);
    3288        2718 :   A = ZM_nv_mod_tree(A, P, T);
    3289        2718 :   H = cgetg(n+1, t_VEC);
    3290        8831 :   for(i=1 ; i <= n; i++)
    3291        6113 :     gel(H,i) = Flm_ker_sp(gel(A, i), P[i], 2);
    3292        2718 :   BQD = ZM_ker_filter(H, P); Q = gel(BQD,2);
    3293        2718 :   if (lg(Q) != lg(P)) T = ZV_producttree(Q);
    3294        2718 :   H = nmV_chinese_center_tree_seq(gel(BQD,1), Q, T, ZV_chinesetree(Q,T));
    3295        2718 :   *mod = gmael(T, lg(T)-1, 1);
    3296        2718 :   D = gel(BQD, 3);
    3297        2718 :   gerepileall(av, 3, &H, &D, mod);
    3298        2718 :   return mkvec2(H,D);
    3299             : }
    3300             : 
    3301             : GEN
    3302      159187 : ZM_ker_worker(GEN P, GEN A)
    3303             : {
    3304      159187 :   GEN V = cgetg(3, t_VEC);
    3305      159187 :   gel(V,1) = ZM_ker_slice(A, P, &gel(V,2));
    3306      159187 :   return V;
    3307             : }
    3308             : 
    3309             : /* assume lg(A) > 1 */
    3310             : static GEN
    3311       60874 : ZM_ker_i(GEN A)
    3312             : {
    3313             :   pari_sp av;
    3314       60874 :   long k, m = lg(A)-1;
    3315       60874 :   GEN HD = NULL, mod = gen_1, worker;
    3316             :   forprime_t S;
    3317             : 
    3318       60874 :   if (m >= 2*nbrows(A))
    3319             :   {
    3320        3004 :     GEN v = ZM_indexrank(A), y = gel(v,2), z = indexcompl(y, m);
    3321             :     GEN B, A1, A1i, d;
    3322        3004 :     A = rowpermute(A, gel(v,1)); /* same kernel */
    3323        3004 :     A1 = vecpermute(A, y); /* maximal rank submatrix */
    3324        3004 :     B = vecpermute(A, z);
    3325        3004 :     A1i = ZM_inv(A1, &d);
    3326        3004 :     if (!d) d = gen_1;
    3327        3004 :     B = vconcat(ZM_mul(ZM_neg(A1i), B), scalarmat_shallow(d, lg(B)-1));
    3328        3004 :     if (!gequal(y, identity_perm(lg(y)-1)))
    3329         665 :       B = rowpermute(B, perm_inv(shallowconcat(y,z)));
    3330        3004 :     return vec_Q_primpart(B);
    3331             :   }
    3332       57870 :   init_modular_big(&S);
    3333       57870 :   worker = snm_closure(is_entry("_ZM_ker_worker"), mkvec(A));
    3334       57870 :   av = avma;
    3335      121734 :   for (k = 1;; k <<= 1)
    3336       63864 :   {
    3337             :     pari_timer ti;
    3338             :     GEN H, Hr;
    3339      121734 :     gen_inccrt_i("ZM_ker", worker, NULL, (k+1)>>1 , m,
    3340             :                  &S, &HD, &mod, ZM_ker_chinese, NULL);
    3341      121734 :     gerepileall(av, 2, &HD, &mod);
    3342      179604 :     H = gel(HD, 1); if (lg(H) == 1) return H;
    3343       77480 :     if (DEBUGLEVEL >= 4) timer_start(&ti);
    3344       77480 :     Hr = FpM_ratlift_parallel(H, mod, NULL);
    3345       77480 :     if (DEBUGLEVEL >= 4) timer_printf(&ti,"ZM_ker: ratlift (%ld)",!!Hr);
    3346       77480 :     if (Hr)
    3347             :     {
    3348             :       GEN MH;
    3349       68207 :       Hr = vec_Q_primpart(Hr);
    3350       68207 :       MH = ZM_mul(A, Hr);
    3351       68207 :       if (DEBUGLEVEL >= 4) timer_printf(&ti,"ZM_ker: QM_mul");
    3352       68207 :       if (ZM_equal0(MH)) return Hr;
    3353             :     }
    3354             :   }
    3355             : }
    3356             : 
    3357             : GEN
    3358       47783 : ZM_ker(GEN M)
    3359             : {
    3360       47783 :   pari_sp av = avma;
    3361       47783 :   long l = lg(M)-1;
    3362       47783 :   if (l==0) return cgetg(1, t_MAT);
    3363       47783 :   if (lgcols(M)==1) return matid(l);
    3364       47783 :   return gerepilecopy(av, ZM_ker_i(M));
    3365             : }
    3366             : 
    3367             : static GEN
    3368       13091 : row_Q_primpart(GEN M)
    3369       13091 : { return shallowtrans(vec_Q_primpart(shallowtrans(M))); }
    3370             : 
    3371             : GEN
    3372       13931 : QM_ker(GEN M)
    3373             : {
    3374       13931 :   pari_sp av = avma;
    3375       13931 :   long l = lg(M)-1;
    3376       13931 :   if (l==0) return cgetg(1, t_MAT);
    3377       13889 :   if (lgcols(M)==1) return matid(l);
    3378       13028 :   return gerepilecopy(av, ZM_ker_i(row_Q_primpart(M)));
    3379             : }
    3380             : 
    3381             : /* x a ZM. Return a multiple of the determinant of the lattice generated by
    3382             :  * the columns of x. From Algorithm 2.2.6 in GTM138 */
    3383             : GEN
    3384       47549 : detint(GEN A)
    3385             : {
    3386       47549 :   if (typ(A) != t_MAT) pari_err_TYPE("detint",A);
    3387       47549 :   RgM_check_ZM(A, "detint");
    3388       47549 :   return ZM_detmult(A);
    3389             : }
    3390             : GEN
    3391       97767 : ZM_detmult(GEN A)
    3392             : {
    3393       97767 :   pari_sp av1, av = avma;
    3394             :   GEN B, c, v, piv;
    3395       97767 :   long rg, i, j, k, m, n = lg(A) - 1;
    3396             : 
    3397       97767 :   if (!n) return gen_1;
    3398       97767 :   m = nbrows(A);
    3399       97767 :   if (n < m) return gen_0;
    3400       97746 :   c = zero_zv(m);
    3401       97746 :   av1 = avma;
    3402       97746 :   B = zeromatcopy(m,m);
    3403       97746 :   v = cgetg(m+1, t_COL);
    3404       97746 :   piv = gen_1; rg = 0;
    3405      521463 :   for (k=1; k<=n; k++)
    3406             :   {
    3407      521449 :     GEN pivprec = piv;
    3408      521449 :     long t = 0;
    3409     4171009 :     for (i=1; i<=m; i++)
    3410             :     {
    3411     3649560 :       pari_sp av2 = avma;
    3412             :       GEN vi;
    3413     3649560 :       if (c[i]) continue;
    3414             : 
    3415     2085753 :       vi = mulii(piv, gcoeff(A,i,k));
    3416    18081913 :       for (j=1; j<=m; j++)
    3417    15996160 :         if (c[j]) vi = addii(vi, mulii(gcoeff(B,j,i),gcoeff(A,j,k)));
    3418     2085753 :       if (!t && signe(vi)) t = i;
    3419     2085753 :       gel(v,i) = gerepileuptoint(av2, vi);
    3420             :     }
    3421      521449 :     if (!t) continue;
    3422             :     /* at this point c[t] = 0 */
    3423             : 
    3424      521365 :     if (++rg >= m) { /* full rank; mostly done */
    3425       97732 :       GEN det = gel(v,t); /* last on stack */
    3426       97732 :       if (++k > n)
    3427       97649 :         det = absi(det);
    3428             :       else
    3429             :       {
    3430             :         /* improve further; at this point c[i] is set for all i != t */
    3431          83 :         gcoeff(B,t,t) = piv; v = centermod(gel(B,t), det);
    3432         334 :         for ( ; k<=n; k++)
    3433         251 :           det = gcdii(det, ZV_dotproduct(v, gel(A,k)));
    3434             :       }
    3435       97732 :       return gerepileuptoint(av, det);
    3436             :     }
    3437             : 
    3438      423633 :     piv = gel(v,t);
    3439     3551345 :     for (i=1; i<=m; i++)
    3440             :     {
    3441             :       GEN mvi;
    3442     3127712 :       if (c[i] || i == t) continue;
    3443             : 
    3444     1563856 :       gcoeff(B,t,i) = mvi = negi(gel(v,i));
    3445    13906914 :       for (j=1; j<=m; j++)
    3446    12343058 :         if (c[j]) /* implies j != t */
    3447             :         {
    3448     3071782 :           pari_sp av2 = avma;
    3449     3071782 :           GEN z = addii(mulii(gcoeff(B,j,i), piv), mulii(gcoeff(B,j,t), mvi));
    3450     3071782 :           if (rg > 1) z = diviiexact(z, pivprec);
    3451     3071782 :           gcoeff(B,j,i) = gerepileuptoint(av2, z);
    3452             :         }
    3453             :     }
    3454      423633 :     c[t] = k;
    3455      423633 :     if (gc_needed(av,1))
    3456             :     {
    3457           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"detint. k=%ld",k);
    3458           0 :       gerepileall(av1, 2, &piv,&B); v = zerovec(m);
    3459             :     }
    3460             :   }
    3461          14 :   set_avma(av); return gen_0;
    3462             : }
    3463             : 
    3464             : /* Reduce x modulo (invertible) y */
    3465             : GEN
    3466       14441 : closemodinvertible(GEN x, GEN y)
    3467             : {
    3468       14441 :   return gmul(y, ground(RgM_solve(y,x)));
    3469             : }
    3470             : GEN
    3471           7 : reducemodinvertible(GEN x, GEN y)
    3472             : {
    3473           7 :   return gsub(x, closemodinvertible(x,y));
    3474             : }
    3475             : GEN
    3476           0 : reducemodlll(GEN x,GEN y)
    3477             : {
    3478           0 :   return reducemodinvertible(x, ZM_lll(y, 0.75, LLL_INPLACE));
    3479             : }
    3480             : 
    3481             : /*******************************************************************/
    3482             : /*                                                                 */
    3483             : /*                    KERNEL of an m x n matrix                    */
    3484             : /*          return n - rk(x) linearly independent vectors          */
    3485             : /*                                                                 */
    3486             : /*******************************************************************/
    3487             : static GEN
    3488          28 : RgM_deplin_i(GEN x0)
    3489             : {
    3490          28 :   pari_sp av = avma, av2;
    3491          28 :   long i, j, k, nl, nc = lg(x0)-1;
    3492             :   GEN D, x, y, c, l, d, ck;
    3493             : 
    3494          28 :   if (!nc) return NULL;
    3495          28 :   nl = nbrows(x0);
    3496          28 :   c = zero_zv(nl);
    3497          28 :   l = cgetg(nc+1, t_VECSMALL); /* not initialized */
    3498          28 :   av2 = avma;
    3499          28 :   x = RgM_shallowcopy(x0);
    3500          28 :   d = const_vec(nl, gen_1); /* pivot list */
    3501          28 :   ck = NULL; /* gcc -Wall */
    3502          98 :   for (k=1; k<=nc; k++)
    3503             :   {
    3504          91 :     ck = gel(x,k);
    3505         196 :     for (j=1; j<k; j++)
    3506             :     {
    3507         105 :       GEN cj = gel(x,j), piv = gel(d,j), q = gel(ck,l[j]);
    3508         420 :       for (i=1; i<=nl; i++)
    3509         315 :         if (i!=l[j]) gel(ck,i) = gsub(gmul(piv, gel(ck,i)), gmul(q, gel(cj,i)));
    3510             :     }
    3511             : 
    3512          91 :     i = gauss_get_pivot_NZ(x, NULL, k, c);
    3513          91 :     if (i > nl) break;
    3514          70 :     if (gc_needed(av,1))
    3515             :     {
    3516           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"deplin k = %ld/%ld",k,nc);
    3517           0 :       gerepileall(av2, 2, &x, &d);
    3518           0 :       ck = gel(x,k);
    3519             :     }
    3520          70 :     gel(d,k) = gel(ck,i);
    3521          70 :     c[i] = k; l[k] = i; /* pivot d[k] in x[i,k] */
    3522             :   }
    3523          28 :   if (k > nc) return gc_NULL(av);
    3524          21 :   if (k == 1) { set_avma(av); return scalarcol_shallow(gen_1,nc); }
    3525          21 :   y = cgetg(nc+1,t_COL);
    3526          21 :   gel(y,1) = gcopy(gel(ck, l[1]));
    3527          49 :   for (D=gel(d,1),j=2; j<k; j++)
    3528             :   {
    3529          28 :     gel(y,j) = gmul(gel(ck, l[j]), D);
    3530          28 :     D = gmul(D, gel(d,j));
    3531             :   }
    3532          21 :   gel(y,j) = gneg(D);
    3533          21 :   for (j++; j<=nc; j++) gel(y,j) = gen_0;
    3534          21 :   y = primitive_part(y, &c);
    3535          21 :   return c? gerepileupto(av, y): gerepilecopy(av, y);
    3536             : }
    3537             : static GEN
    3538           0 : RgV_deplin(GEN v)
    3539             : {
    3540           0 :   pari_sp av = avma;
    3541           0 :   long n = lg(v)-1;
    3542           0 :   GEN y, p = NULL;
    3543           0 :   if (n <= 1)
    3544             :   {
    3545           0 :     if (n == 1 && gequal0(gel(v,1))) return mkcol(gen_1);
    3546           0 :     return cgetg(1, t_COL);
    3547             :   }
    3548           0 :   if (gequal0(gel(v,1))) return scalarcol_shallow(gen_1, n);
    3549           0 :   v = primpart(mkvec2(gel(v,1),gel(v,2)));
    3550           0 :   if (RgV_is_FpV(v, &p) && p) v = centerlift(v);
    3551           0 :   y = zerocol(n);
    3552           0 :   gel(y,1) = gneg(gel(v,2));
    3553           0 :   gel(y,2) = gcopy(gel(v,1));
    3554           0 :   return gerepileupto(av, y);
    3555             : 
    3556             : }
    3557             : 
    3558             : static GEN
    3559         105 : RgM_deplin_FpM(GEN x, GEN p)
    3560             : {
    3561         105 :   pari_sp av = avma;
    3562             :   ulong pp;
    3563         105 :   x = RgM_Fp_init(x, p, &pp);
    3564         105 :   switch(pp)
    3565             :   {
    3566             :   case 0:
    3567          35 :     x = FpM_ker_gen(x,p,1);
    3568          35 :     if (!x) return gc_NULL(av);
    3569          21 :     x = FpC_center(x,p,shifti(p,-1));
    3570          21 :     break;
    3571             :   case 2:
    3572          14 :     x = F2m_ker_sp(x,1);
    3573          14 :     if (!x) return gc_NULL(av);
    3574           7 :     x = F2c_to_ZC(x); break;
    3575             :   default:
    3576          56 :     x = Flm_ker_sp(x,pp,1);
    3577          56 :     if (!x) return gc_NULL(av);
    3578          35 :     x = Flv_center(x, pp, pp>>1);
    3579          35 :     x = zc_to_ZC(x);
    3580          35 :     break;
    3581             :   }
    3582          63 :   return gerepileupto(av, x);
    3583             : }
    3584             : 
    3585             : /* FIXME: implement direct modular ZM_deplin ? */
    3586             : static GEN
    3587          98 : QM_deplin(GEN M)
    3588             : {
    3589          98 :   pari_sp av = avma;
    3590          98 :   long l = lg(M)-1;
    3591             :   GEN k;
    3592          98 :   if (l==0) return NULL;
    3593          63 :   if (lgcols(M)==1) return col_ei(l, 1);
    3594          63 :   k = ZM_ker_i(row_Q_primpart(M));
    3595          63 :   if (lg(k)== 1) return gc_NULL(av);
    3596          49 :   return gerepilecopy(av, gel(k,1));
    3597             : }
    3598             : 
    3599             : static GEN
    3600          42 : RgM_deplin_FqM(GEN x, GEN pol, GEN p)
    3601             : {
    3602          42 :   pari_sp av = avma;
    3603          42 :   GEN b, T = RgX_to_FpX(pol, p);
    3604          42 :   if (signe(T) == 0) pari_err_OP("deplin",x,pol);
    3605          42 :   b = FqM_deplin(RgM_to_FqM(x, T, p), T, p);
    3606          42 :   return gerepileupto(av, b);
    3607             : }
    3608             : 
    3609             : #define code(t1,t2) ((t1 << 6) | t2)
    3610             : static GEN
    3611         357 : RgM_deplin_fast(GEN x)
    3612             : {
    3613             :   GEN p, pol;
    3614             :   long pa;
    3615         357 :   long t = RgM_type(x, &p,&pol,&pa);
    3616         357 :   switch(t)
    3617             :   {
    3618             :     case t_INT:    /* fall through */
    3619          98 :     case t_FRAC:   return QM_deplin(x);
    3620          84 :     case t_FFELT:  return FFM_deplin(x, pol);
    3621         105 :     case t_INTMOD: return RgM_deplin_FpM(x, p);
    3622             :     case code(t_POLMOD, t_INTMOD):
    3623          42 :                    return RgM_deplin_FqM(x, pol, p);
    3624          28 :     default:       return gen_0;
    3625             :   }
    3626             : }
    3627             : #undef code
    3628             : 
    3629             : static GEN
    3630         357 : RgM_deplin(GEN x)
    3631             : {
    3632         357 :   GEN z = RgM_deplin_fast(x);
    3633         357 :   if (z!= gen_0) return z;
    3634          28 :   return RgM_deplin_i(x);
    3635             : }
    3636             : 
    3637             : GEN
    3638         357 : deplin(GEN x)
    3639             : {
    3640         357 :   switch(typ(x))
    3641             :   {
    3642             :     case t_MAT:
    3643             :     {
    3644         357 :       GEN z = RgM_deplin(x);
    3645         357 :       if (z) return z;
    3646         140 :       return cgetg(1, t_COL);
    3647             :     }
    3648           0 :     case t_VEC: return RgV_deplin(x);
    3649           0 :     default: pari_err_TYPE("deplin",x);
    3650             :   }
    3651             :   return NULL;/*LCOV_EXCL_LINE*/
    3652             : }
    3653             : 
    3654             : /*******************************************************************/
    3655             : /*                                                                 */
    3656             : /*         GAUSS REDUCTION OF MATRICES  (m lines x n cols)         */
    3657             : /*           (kernel, image, complementary image, rank)            */
    3658             : /*                                                                 */
    3659             : /*******************************************************************/
    3660             : /* return the transform of x under a standard Gauss pivot.
    3661             :  * x0 is a reference point when guessing whether x[i,j] ~ 0
    3662             :  * (iff x[i,j] << x0[i,j])
    3663             :  * Set r = dim ker(x). d[k] contains the index of the first non-zero pivot
    3664             :  * in column k */
    3665             : static GEN
    3666         993 : gauss_pivot_ker(GEN x, GEN x0, GEN *dd, long *rr)
    3667             : {
    3668             :   GEN c, d, p, data;
    3669             :   pari_sp av;
    3670             :   long i, j, k, r, t, n, m;
    3671             :   pivot_fun pivot;
    3672             : 
    3673         993 :   n=lg(x)-1; if (!n) { *dd=NULL; *rr=0; return cgetg(1,t_MAT); }
    3674         993 :   m=nbrows(x); r=0;
    3675         993 :   pivot = get_pivot_fun(x, x0, &data);
    3676         993 :   x = RgM_shallowcopy(x);
    3677         993 :   c = zero_zv(m);
    3678         993 :   d = cgetg(n+1,t_VECSMALL);
    3679         993 :   av=avma;
    3680        5763 :   for (k=1; k<=n; k++)
    3681             :   {
    3682        4770 :     j = pivot(x, data, k, c);
    3683        4770 :     if (j > m)
    3684             :     {
    3685        1057 :       r++; d[k]=0;
    3686        4690 :       for(j=1; j<k; j++)
    3687        3633 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
    3688             :     }
    3689             :     else
    3690             :     { /* pivot for column k on row j */
    3691        3713 :       c[j]=k; d[k]=j; p = gdiv(gen_m1,gcoeff(x,j,k));
    3692        3713 :       gcoeff(x,j,k) = gen_m1;
    3693             :       /* x[j,] /= - x[j,k] */
    3694        3713 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3695       35680 :       for (t=1; t<=m; t++)
    3696       31967 :         if (t!=j)
    3697             :         { /* x[t,] -= 1 / x[j,k] x[j,] */
    3698       28254 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3699       28254 :           if (gequal0(p)) continue;
    3700       75759 :           for (i=k+1; i<=n; i++)
    3701       61386 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i),gmul(p,gcoeff(x,j,i)));
    3702       14373 :           if (gc_needed(av,1)) gerepile_gauss_ker(x,k,t,av);
    3703             :         }
    3704             :     }
    3705             :   }
    3706         993 :   *dd=d; *rr=r; return x;
    3707             : }
    3708             : 
    3709             : /* r = dim ker(x).
    3710             :  * Returns d:
    3711             :  *   d[k] != 0 contains the index of a non-zero pivot in column k
    3712             :  *   d[k] == 0 if column k is a linear combination of the (k-1) first ones */
    3713             : GEN
    3714       34445 : RgM_pivots(GEN x0, GEN data, long *rr, pivot_fun pivot)
    3715             : {
    3716             :   GEN x, c, d, p;
    3717       34445 :   long i, j, k, r, t, m, n = lg(x0)-1;
    3718             :   pari_sp av;
    3719             : 
    3720       34445 :   if (RgM_is_ZM(x0)) return ZM_pivots(x0, rr);
    3721       30512 :   if (!n) { *rr = 0; return NULL; }
    3722             : 
    3723       30512 :   d = cgetg(n+1, t_VECSMALL);
    3724       30512 :   x = RgM_shallowcopy(x0);
    3725       30512 :   m = nbrows(x); r = 0;
    3726       30512 :   c = zero_zv(m);
    3727       30512 :   av = avma;
    3728      213574 :   for (k=1; k<=n; k++)
    3729             :   {
    3730      183062 :     j = pivot(x, data, k, c);
    3731      183062 :     if (j > m) { r++; d[k] = 0; }
    3732             :     else
    3733             :     {
    3734       52123 :       c[j] = k; d[k] = j; p = gdiv(gen_m1, gcoeff(x,j,k));
    3735       52123 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3736             : 
    3737      224764 :       for (t=1; t<=m; t++)
    3738      172641 :         if (!c[t]) /* no pivot on that line yet */
    3739             :         {
    3740       68369 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3741     1745422 :           for (i=k+1; i<=n; i++)
    3742     1677053 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i), gmul(p, gcoeff(x,j,i)));
    3743       68369 :           if (gc_needed(av,1)) gerepile_gauss(x,k,t,av,j,c);
    3744             :         }
    3745       52123 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = gen_0; /* dummy */
    3746             :     }
    3747             :   }
    3748       30512 :   *rr = r; set_avma((pari_sp)d); return d;
    3749             : }
    3750             : 
    3751             : static long
    3752      135929 : ZM_count_0_cols(GEN M)
    3753             : {
    3754      135929 :   long i, l = lg(M), n = 0;
    3755      783479 :   for (i = 1; i < l; i++)
    3756      647550 :     if (ZV_equal0(gel(M,i))) n++;
    3757      135929 :   return n;
    3758             : }
    3759             : 
    3760             : static void indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol);
    3761             : /* As RgM_pivots, integer entries. Set *rr = dim Ker M0 */
    3762             : GEN
    3763      139152 : ZM_pivots(GEN M0, long *rr)
    3764             : {
    3765      139152 :   GEN d, dbest = NULL;
    3766             :   long m, mm, n, nn, i, imax, rmin, rbest, zc;
    3767      139152 :   int beenthere = 0;
    3768      139152 :   pari_sp av, av0 = avma;
    3769             :   forprime_t S;
    3770             : 
    3771      139152 :   rbest = n = lg(M0)-1;
    3772      139152 :   if (n == 0) { *rr = 0; return NULL; }
    3773      135929 :   zc = ZM_count_0_cols(M0);
    3774      135929 :   if (n == zc) { *rr = zc; return zero_zv(n); }
    3775             : 
    3776      135775 :   m = nbrows(M0);
    3777      135775 :   rmin = maxss(zc, n-m);
    3778      135775 :   init_modular_small(&S);
    3779      135775 :   if (n <= m) { nn = n; mm = m; } else { nn = m; mm = n; }
    3780      135775 :   imax = (nn < 16)? 1: (nn < 64)? 2: 3; /* heuristic */
    3781             : 
    3782             :   for(;;)
    3783           0 :   {
    3784             :     GEN row, col, M, KM, IM, RHS, X, cX;
    3785             :     long rk;
    3786      149153 :     for (av = avma, i = 0;; set_avma(av), i++)
    3787       13378 :     {
    3788      149153 :       ulong p = u_forprime_next(&S);
    3789             :       long rp;
    3790      149153 :       if (!p) pari_err_OVERFLOW("ZM_pivots [ran out of primes]");
    3791      149153 :       d = Flm_pivots(ZM_to_Flm(M0, p), p, &rp, 1);
    3792      149153 :       if (rp == rmin) { rbest = rp; goto END; } /* maximal rank, return */
    3793       25340 :       if (rp < rbest) { /* save best r so far */
    3794       11964 :         rbest = rp;
    3795       11964 :         guncloneNULL(dbest);
    3796       11964 :         dbest = gclone(d);
    3797       23926 :         if (beenthere) break;
    3798             :       }
    3799       25340 :       if (!beenthere && i >= imax) break;
    3800             :     }
    3801       11962 :     beenthere = 1;
    3802             :     /* Dubious case: there is (probably) a non trivial kernel */
    3803       11962 :     indexrank_all(m,n, rbest, dbest, &row, &col);
    3804       11962 :     M = rowpermute(vecpermute(M0, col), row);
    3805       11962 :     rk = n - rbest; /* (probable) dimension of image */
    3806       11962 :     if (n > m) M = shallowtrans(M);
    3807       11962 :     IM = vecslice(M,1,rk);
    3808       11962 :     KM = vecslice(M,rk+1, nn);
    3809       11962 :     M = rowslice(IM, 1,rk); /* square maximal rank */
    3810       11962 :     X = ZM_gauss(M, rowslice(KM, 1,rk));
    3811       11962 :     RHS = rowslice(KM,rk+1,mm);
    3812       11962 :     M = rowslice(IM,rk+1,mm);
    3813       11962 :     X = Q_remove_denom(X, &cX);
    3814       11962 :     if (cX) RHS = ZM_Z_mul(RHS, cX);
    3815       11962 :     if (ZM_equal(ZM_mul(M, X), RHS)) { d = vecsmall_copy(dbest); goto END; }
    3816           0 :     set_avma(av);
    3817             :   }
    3818             : END:
    3819      135775 :   *rr = rbest; guncloneNULL(dbest);
    3820      135775 :   return gerepileuptoleaf(av0, d);
    3821             : }
    3822             : 
    3823             : /* set *pr = dim Ker x */
    3824             : static GEN
    3825       21343 : gauss_pivot(GEN x, long *pr) {
    3826             :   GEN data;
    3827       21343 :   pivot_fun pivot = get_pivot_fun(x, x, &data);
    3828       21343 :   return RgM_pivots(x, data, pr, pivot);
    3829             : }
    3830             : 
    3831             : /* compute ker(x), x0 is a reference point when guessing whether x[i,j] ~ 0
    3832             :  * (iff x[i,j] << x0[i,j]) */
    3833             : static GEN
    3834         993 : ker_aux(GEN x, GEN x0)
    3835             : {
    3836         993 :   pari_sp av = avma;
    3837             :   GEN d,y;
    3838             :   long i,j,k,r,n;
    3839             : 
    3840         993 :   x = gauss_pivot_ker(x,x0,&d,&r);
    3841         993 :   if (!r) { set_avma(av); return cgetg(1,t_MAT); }
    3842         959 :   n = lg(x)-1; y=cgetg(r+1,t_MAT);
    3843        2016 :   for (j=k=1; j<=r; j++,k++)
    3844             :   {
    3845        1057 :     GEN p = cgetg(n+1,t_COL);
    3846             : 
    3847        1057 :     gel(y,j) = p; while (d[k]) k++;
    3848        4690 :     for (i=1; i<k; i++)
    3849        3633 :       if (d[i])
    3850             :       {
    3851        3451 :         GEN p1=gcoeff(x,d[i],k);
    3852        3451 :         gel(p,i) = gcopy(p1); gunclone(p1);
    3853             :       }
    3854             :       else
    3855         182 :         gel(p,i) = gen_0;
    3856        1057 :     gel(p,k) = gen_1; for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
    3857             :   }
    3858         959 :   return gerepileupto(av,y);
    3859             : }
    3860             : 
    3861             : static GEN
    3862          77 : RgM_ker_FpM(GEN x, GEN p)
    3863             : {
    3864          77 :   pari_sp av = avma;
    3865             :   ulong pp;
    3866          77 :   x = RgM_Fp_init(x, p, &pp);
    3867          77 :   switch(pp)
    3868             :   {
    3869          35 :     case 0: x = FpM_to_mod(FpM_ker_gen(x,p,0),p); break;
    3870           7 :     case 2: x = F2m_to_mod(F2m_ker_sp(x,0)); break;
    3871          35 :     default:x = Flm_to_mod(Flm_ker_sp(x,pp,0), pp); break;
    3872             :   }
    3873          77 :   return gerepileupto(av, x);
    3874             : }
    3875             : 
    3876             : static GEN
    3877          91 : RgM_ker_FqM(GEN x, GEN pol, GEN p)
    3878             : {
    3879          91 :   pari_sp av = avma;
    3880          91 :   GEN b, T = RgX_to_FpX(pol, p);
    3881          91 :   if (signe(T) == 0) pari_err_OP("ker",x,pol);
    3882          84 :   b = FqM_ker(RgM_to_FqM(x, T, p), T, p);
    3883          84 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    3884             : }
    3885             : 
    3886             : #define code(t1,t2) ((t1 << 6) | t2)
    3887             : static GEN
    3888       11887 : RgM_ker_fast(GEN x)
    3889             : {
    3890             :   GEN p, pol;
    3891             :   long pa;
    3892       11887 :   long t = RgM_type(x, &p,&pol,&pa);
    3893       11887 :   switch(t)
    3894             :   {
    3895             :     case t_INT:    /* fall through */
    3896       10886 :     case t_FRAC:   return QM_ker(x);
    3897          77 :     case t_FFELT:  return FFM_ker(x, pol);
    3898          77 :     case t_INTMOD: return RgM_ker_FpM(x, p);
    3899             :     case code(t_POLMOD, t_INTMOD):
    3900          91 :                    return RgM_ker_FqM(x, pol, p);
    3901         756 :     default:       return NULL;
    3902             :   }
    3903             : }
    3904             : #undef code
    3905             : 
    3906             : GEN
    3907       11887 : ker(GEN x)
    3908             : {
    3909       11887 :   GEN b = RgM_ker_fast(x);
    3910       11880 :   if (b) return b;
    3911         756 :   return ker_aux(x,x);
    3912             : }
    3913             : 
    3914             : GEN
    3915       46214 : matker0(GEN x,long flag)
    3916             : {
    3917       46214 :   if (typ(x)!=t_MAT) pari_err_TYPE("matker",x);
    3918       46214 :   if (!flag) return ker(x);
    3919       45934 :   RgM_check_ZM(x, "matker");
    3920       45934 :   return ZM_ker(x);
    3921             : }
    3922             : 
    3923             : static GEN
    3924          63 : RgM_image_FpM(GEN x, GEN p)
    3925             : {
    3926          63 :   pari_sp av = avma;
    3927             :   ulong pp;
    3928          63 :   x = RgM_Fp_init(x, p, &pp);
    3929          63 :   switch(pp)
    3930             :   {
    3931          28 :     case 0: x = FpM_to_mod(FpM_image(x,p),p); break;
    3932           7 :     case 2: x = F2m_to_mod(F2m_image(x)); break;
    3933          28 :     default:x = Flm_to_mod(Flm_image(x,pp), pp); break;
    3934             :   }
    3935          63 :   return gerepileupto(av, x);
    3936             : }
    3937             : 
    3938             : static GEN
    3939          35 : RgM_image_FqM(GEN x, GEN pol, GEN p)
    3940             : {
    3941          35 :   pari_sp av = avma;
    3942          35 :   GEN b, T = RgX_to_FpX(pol, p);
    3943          35 :   if (signe(T) == 0) pari_err_OP("image",x,pol);
    3944          28 :   b = FqM_image(RgM_to_FqM(x, T, p), T, p);
    3945          28 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    3946             : }
    3947             : 
    3948             : GEN
    3949        5481 : QM_image_shallow(GEN A)
    3950             : {
    3951        5481 :   A = vec_Q_primpart(A);
    3952        5481 :   return vecpermute(A, ZM_indeximage(A));
    3953             : }
    3954             : GEN
    3955        4711 : QM_image(GEN A)
    3956             : {
    3957        4711 :   pari_sp av = avma;
    3958        4711 :   return gerepilecopy(av, QM_image_shallow(A));
    3959             : }
    3960             : 
    3961             : #define code(t1,t2) ((t1 << 6) | t2)
    3962             : static GEN
    3963        4872 : RgM_image_fast(GEN x)
    3964             : {
    3965             :   GEN p, pol;
    3966             :   long pa;
    3967        4872 :   long t = RgM_type(x, &p,&pol,&pa);
    3968        4872 :   switch(t)
    3969             :   {
    3970             :     case t_INT:    /* fall through */
    3971        4711 :     case t_FRAC:   return QM_image(x);
    3972          49 :     case t_FFELT:  return FFM_image(x, pol);
    3973          63 :     case t_INTMOD: return RgM_image_FpM(x, p);
    3974             :     case code(t_POLMOD, t_INTMOD):
    3975          35 :                    return RgM_image_FqM(x, pol, p);
    3976          14 :     default:       return NULL;
    3977             :   }
    3978             : }
    3979             : #undef code
    3980             : 
    3981             : GEN
    3982        4872 : image(GEN x)
    3983             : {
    3984             :   GEN d, M;
    3985             :   long r;
    3986             : 
    3987        4872 :   if (typ(x)!=t_MAT) pari_err_TYPE("matimage",x);
    3988        4872 :   M = RgM_image_fast(x);
    3989        4865 :   if (M) return M;
    3990          14 :   d = gauss_pivot(x,&r); /* d left on stack for efficiency */
    3991          14 :   return image_from_pivot(x,d,r);
    3992             : }
    3993             : 
    3994             : static GEN
    3995          84 : imagecompl_aux(GEN x, GEN(*PIVOT)(GEN,long*))
    3996             : {
    3997          84 :   pari_sp av = avma;
    3998             :   GEN d,y;
    3999             :   long j,i,r;
    4000             : 
    4001          84 :   if (typ(x)!=t_MAT) pari_err_TYPE("imagecompl",x);
    4002          84 :   (void)new_chunk(lg(x) * 4 + 1); /* HACK */
    4003          84 :   d = PIVOT(x,&r); /* if (!d) then r = 0 */
    4004          84 :   set_avma(av); y = cgetg(r+1,t_VECSMALL);
    4005         126 :   for (i=j=1; j<=r; i++)
    4006          42 :     if (!d[i]) y[j++] = i;
    4007          84 :   return y;
    4008             : }
    4009             : GEN
    4010          84 : imagecompl(GEN x) { return imagecompl_aux(x, &gauss_pivot); }
    4011             : GEN
    4012           0 : ZM_imagecompl(GEN x) { return imagecompl_aux(x, &ZM_pivots); }
    4013             : 
    4014             : static GEN
    4015          28 : RgM_RgC_invimage_FpC(GEN A, GEN y, GEN p)
    4016             : {
    4017          28 :   pari_sp av = avma;
    4018             :   ulong pp;
    4019             :   GEN x;
    4020          28 :   A = RgM_Fp_init(A,p,&pp);
    4021          28 :   switch(pp)
    4022             :   {
    4023             :   case 0:
    4024           7 :     y = RgC_to_FpC(y,p);
    4025           7 :     x = FpM_FpC_invimage(A, y, p);
    4026           7 :     return x ? gerepileupto(av, FpC_to_mod(x,p)): NULL;
    4027             :   case 2:
    4028           7 :     y = RgV_to_F2v(y);
    4029           7 :     x = F2m_F2c_invimage(A, y);
    4030           7 :     return x ? gerepileupto(av, F2c_to_mod(x)): NULL;
    4031             :   default:
    4032          14 :     y = RgV_to_Flv(y,pp);
    4033          14 :     x = Flm_Flc_invimage(A, y, pp);
    4034          14 :     return x ? gerepileupto(av, Flc_to_mod(x,pp)): NULL;
    4035             :   }
    4036             : }
    4037             : 
    4038             : static GEN
    4039        2044 : RgM_RgC_invimage_fast(GEN x, GEN y)
    4040             : {
    4041             :   GEN p, pol;
    4042             :   long pa;
    4043        2044 :   long t = RgM_RgC_type(x, y, &p,&pol,&pa);
    4044        2044 :   switch(t)
    4045             :   {
    4046          28 :     case t_INTMOD: return RgM_RgC_invimage_FpC(x, y, p);
    4047          63 :     case t_FFELT:  return FFM_FFC_invimage(x, y, pol);
    4048        1953 :     default:       return gen_0;
    4049             :   }
    4050             : }
    4051             : 
    4052             : GEN
    4053        2149 : RgM_RgC_invimage(GEN A, GEN y)
    4054             : {
    4055        2149 :   pari_sp av = avma;
    4056        2149 :   long i, l = lg(A);
    4057             :   GEN M, x, t;
    4058        2149 :   if (l==1) return NULL;
    4059        2044 :   if (lg(y) != lgcols(A)) pari_err_DIM("inverseimage");
    4060        2044 :   M = RgM_RgC_invimage_fast(A, y);
    4061        2044 :   if (!M) return gc_NULL(av);
    4062        2023 :   if (M != gen_0) return M;
    4063        1953 :   M = ker(shallowconcat(A, y));
    4064        1953 :   i = lg(M)-1;
    4065        1953 :   if (!i) return gc_NULL(av);
    4066             : 
    4067        1694 :   x = gel(M,i); t = gel(x,l);
    4068        1694 :   if (gequal0(t)) return gc_NULL(av);
    4069             : 
    4070        1659 :   t = gneg_i(t); setlg(x,l);
    4071        1659 :   return gerepileupto(av, RgC_Rg_div(x, t));
    4072             : }
    4073             : 
    4074             : /* Return X such that m X = v (t_COL or t_MAT), resp. an empty t_COL / t_MAT
    4075             :  * if no solution exist */
    4076             : GEN
    4077        2359 : inverseimage(GEN m, GEN v)
    4078             : {
    4079             :   GEN y;
    4080        2359 :   if (typ(m)!=t_MAT) pari_err_TYPE("inverseimage",m);
    4081        2359 :   switch(typ(v))
    4082             :   {
    4083             :     case t_COL:
    4084        2121 :       y = RgM_RgC_invimage(m,v);
    4085        2121 :       return y? y: cgetg(1,t_COL);
    4086             :     case t_MAT:
    4087         238 :       y = RgM_invimage(m, v);
    4088         238 :       return y? y: cgetg(1,t_MAT);
    4089             :   }
    4090           0 :   pari_err_TYPE("inverseimage",v);
    4091             :   return NULL;/*LCOV_EXCL_LINE*/
    4092             : }
    4093             : 
    4094             : static GEN
    4095          84 : RgM_invimage_FpM(GEN A, GEN B, GEN p)
    4096             : {
    4097          84 :   pari_sp av = avma;
    4098             :   ulong pp;
    4099             :   GEN x;
    4100          84 :   A = RgM_Fp_init(A,p,&pp);
    4101          84 :   switch(pp)
    4102             :   {
    4103             :   case 0:
    4104          35 :     B = RgM_to_FpM(B,p);
    4105          35 :     x = FpM_invimage_gen(A, B, p);
    4106          35 :     return x ? gerepileupto(av, FpM_to_mod(x, p)): x;
    4107             :   case 2:
    4108           7 :     B = RgM_to_F2m(B);
    4109           7 :     x = F2m_invimage_i(A, B);
    4110           7 :     return x ? gerepileupto(av, F2m_to_mod(x)): x;
    4111             :   default:
    4112          42 :     B = RgM_to_Flm(B,pp);
    4113          42 :     x = Flm_invimage_i(A, B, pp);
    4114          42 :     return x ? gerepileupto(av, Flm_to_mod(x, pp)): x;
    4115             :   }
    4116             : }
    4117             : 
    4118             : static GEN
    4119         252 : RgM_invimage_fast(GEN x, GEN y)
    4120             : {
    4121             :   GEN p, pol;
    4122             :   long pa;
    4123         252 :   long t = RgM_type2(x, y, &p,&pol,&pa);
    4124         252 :   switch(t)
    4125             :   {
    4126          84 :     case t_INTMOD: return RgM_invimage_FpM(x, y, p);
    4127         105 :     case t_FFELT:  return FFM_invimage(x, y, pol);
    4128          63 :     default:       return gen_0;
    4129             :   }
    4130             : }
    4131             : 
    4132             : /* find Z such that A Z = B. Return NULL if no solution */
    4133             : GEN
    4134         252 : RgM_invimage(GEN A, GEN B)
    4135             : {
    4136         252 :   pari_sp av = avma;
    4137             :   GEN d, x, X, Y;
    4138         252 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4139         252 :   X = RgM_invimage_fast(A, B);
    4140         252 :   if (!X) return gc_NULL(av);
    4141         140 :   if (X != gen_0) return X;
    4142          63 :   x = ker(shallowconcat(RgM_neg(A), B));
    4143             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4144             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4145             :    * Y has at least nB columns and full rank */
    4146          63 :   nY = lg(x)-1;
    4147          63 :   if (nY < nB) return gc_NULL(av);
    4148          49 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4149          49 :   d = cgetg(nB+1, t_VECSMALL);
    4150         441 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4151             :   {
    4152         546 :     for (; j>=1; j--)
    4153         532 :       if (!gequal0(gcoeff(Y,i,j))) { d[i] = j; break; }
    4154         406 :     if (!j) return gc_NULL(av);
    4155             :   }
    4156             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4157          35 :   Y = vecpermute(Y, d);
    4158          35 :   x = vecpermute(x, d);
    4159          35 :   X = rowslice(x, 1, nA);
    4160          35 :   return gerepileupto(av, RgM_mul(X, RgM_inv_upper(Y)));
    4161             : }
    4162             : 
    4163             : static GEN
    4164          70 : RgM_suppl_FpM(GEN x, GEN p)
    4165             : {
    4166          70 :   pari_sp av = avma;
    4167             :   ulong pp;
    4168          70 :   x = RgM_Fp_init(x, p, &pp);
    4169          70 :   switch(pp)
    4170             :   {
    4171          21 :   case 0: x = FpM_to_mod(FpM_suppl(x,p), p); break;
    4172          14 :   case 2: x = F2m_to_mod(F2m_suppl(x)); break;
    4173          35 :   default:x = Flm_to_mod(Flm_suppl(x,pp), pp); break;
    4174             :   }
    4175          70 :   return gerepileupto(av, x);
    4176             : }
    4177             : 
    4178             : static GEN
    4179         175 : RgM_suppl_fast(GEN x)
    4180             : {
    4181             :   GEN p, pol;
    4182             :   long pa;
    4183         175 :   long t = RgM_type(x,&p,&pol,&pa);
    4184         175 :   switch(t)
    4185             :   {
    4186          70 :     case t_INTMOD: return RgM_suppl_FpM(x, p);
    4187          35 :     case t_FFELT:  return FFM_suppl(x, pol);
    4188          70 :     default:       return NULL;
    4189             :   }
    4190             : }
    4191             : 
    4192             : /* x is an n x k matrix, rank(x) = k <= n. Return an invertible n x n matrix
    4193             :  * whose first k columns are given by x. If rank(x) < k, undefined result. */
    4194             : GEN
    4195         175 : suppl(GEN x)
    4196             : {
    4197         175 :   pari_sp av = avma;
    4198             :   GEN d, M;
    4199             :   long r;
    4200         175 :   if (typ(x)!=t_MAT) pari_err_TYPE("suppl",x);
    4201         175 :   M = RgM_suppl_fast(x);
    4202         175 :   if (M) return M;
    4203          70 :   init_suppl(x);
    4204          70 :   d = gauss_pivot(x,&r);
    4205          70 :   set_avma(av); return get_suppl(x,d,nbrows(x),r,&col_ei);
    4206             : }
    4207             : 
    4208             : GEN
    4209           7 : image2(GEN x)
    4210             : {
    4211           7 :   pari_sp av = avma;
    4212             :   long k, n, i;
    4213             :   GEN A, B;
    4214             : 
    4215           7 :   if (typ(x)!=t_MAT) pari_err_TYPE("image2",x);
    4216           7 :   if (lg(x) == 1) return cgetg(1,t_MAT);
    4217           7 :   A = ker(x); k = lg(A)-1;
    4218           7 :   if (!k) { set_avma(av); return gcopy(x); }
    4219           7 :   A = suppl(A); n = lg(A)-1;
    4220           7 :   B = cgetg(n-k+1, t_MAT);
    4221           7 :   for (i = k+1; i <= n; i++) gel(B,i-k) = RgM_RgC_mul(x, gel(A,i));
    4222           7 :   return gerepileupto(av, B);
    4223             : }
    4224             : 
    4225             : GEN
    4226         210 : matimage0(GEN x,long flag)
    4227             : {
    4228         210 :   switch(flag)
    4229             :   {
    4230         203 :     case 0: return image(x);
    4231           7 :     case 1: return image2(x);
    4232           0 :     default: pari_err_FLAG("matimage");
    4233             :   }
    4234             :   return NULL; /* LCOV_EXCL_LINE */
    4235             : }
    4236             : 
    4237             : static long
    4238         126 : RgM_rank_FpM(GEN x, GEN p)
    4239             : {
    4240         126 :   pari_sp av = avma;
    4241             :   ulong pp;
    4242             :   long r;
    4243         126 :   x = RgM_Fp_init(x,p,&pp);
    4244         126 :   switch(pp)
    4245             :   {
    4246          28 :   case 0: r = FpM_rank(x,p); break;
    4247          63 :   case 2: r = F2m_rank(x); break;
    4248          35 :   default:r = Flm_rank(x,pp); break;
    4249             :   }
    4250         126 :   return gc_long(av, r);
    4251             : }
    4252             : 
    4253             : static long
    4254          49 : RgM_rank_FqM(GEN x, GEN pol, GEN p)
    4255             : {
    4256          49 :   pari_sp av = avma;
    4257             :   long r;
    4258          49 :   GEN T = RgX_to_FpX(pol, p);
    4259          49 :   if (signe(T) == 0) pari_err_OP("rank",x,pol);
    4260          42 :   r = FqM_rank(RgM_to_FqM(x, T, p), T, p);
    4261          42 :   return gc_long(av,r);
    4262             : }
    4263             : 
    4264             : #define code(t1,t2) ((t1 << 6) | t2)
    4265             : static long
    4266         294 : RgM_rank_fast(GEN x)
    4267             : {
    4268             :   GEN p, pol;
    4269             :   long pa;
    4270         294 :   long t = RgM_type(x,&p,&pol,&pa);
    4271         294 :   switch(t)
    4272             :   {
    4273          42 :     case t_INT:    return ZM_rank(x);
    4274           0 :     case t_FRAC:   return QM_rank(x);
    4275         126 :     case t_INTMOD: return RgM_rank_FpM(x, p);
    4276          70 :     case t_FFELT:  return FFM_rank(x, pol);
    4277             :     case code(t_POLMOD, t_INTMOD):
    4278          49 :                    return RgM_rank_FqM(x, pol, p);
    4279           7 :     default:       return -1;
    4280             :   }
    4281             : }
    4282             : #undef code
    4283             : 
    4284             : long
    4285         294 : rank(GEN x)
    4286             : {
    4287         294 :   pari_sp av = avma;
    4288             :   long r;
    4289             : 
    4290         294 :   if (typ(x)!=t_MAT) pari_err_TYPE("rank",x);
    4291         294 :   r = RgM_rank_fast(x);
    4292         287 :   if (r >= 0) return r;
    4293           7 :   (void)gauss_pivot(x, &r);
    4294           7 :   return gc_long(av, lg(x)-1 - r);
    4295             : }
    4296             : 
    4297             : /* d a t_VECSMALL of integers in 1..n. Return the vector of the d[i]
    4298             :  * followed by the missing indices */
    4299             : static GEN
    4300       23924 : perm_complete(GEN d, long n)
    4301             : {
    4302       23924 :   GEN y = cgetg(n+1, t_VECSMALL);
    4303       23924 :   long i, j = 1, k = n, l = lg(d);
    4304       23924 :   pari_sp av = avma;
    4305       23924 :   char *T = stack_calloc(n+1);
    4306       23924 :   for (i = 1; i < l; i++) T[d[i]] = 1;
    4307      276120 :   for (i = 1; i <= n; i++)
    4308      252196 :     if (T[i]) y[j++] = i; else y[k--] = i;
    4309       23924 :   set_avma(av); return y;
    4310             : }
    4311             : 
    4312             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    4313             : static GEN
    4314        5481 : indeximage0(long n, long r, GEN d)
    4315             : {
    4316             :   long i, j;
    4317             :   GEN v;
    4318             : 
    4319        5481 :   r = n - r; /* now r = dim Im(x) */
    4320        5481 :   v = cgetg(r+1,t_VECSMALL);
    4321       30793 :   if (d) for (i=j=1; j<=n; j++)
    4322       25312 :     if (d[j]) v[i++] = j;
    4323        5481 :   return v;
    4324             : }
    4325             : /* x an m x n t_MAT, n > 0, r = dim Ker(x), d from gauss_pivot */
    4326             : static void
    4327       11962 : indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol)
    4328             : {
    4329       11962 :   GEN IR = indexrank0(n, r, d);
    4330       11962 :   *prow = perm_complete(gel(IR,1), m);
    4331       11962 :   *pcol = perm_complete(gel(IR,2), n);
    4332       11962 : }
    4333             : 
    4334             : static GEN
    4335          28 : RgM_indexrank_FpM(GEN x, GEN p)
    4336             : {
    4337          28 :   pari_sp av = avma;
    4338             :   ulong pp;
    4339             :   GEN r;
    4340          28 :   x = RgM_Fp_init(x,p,&pp);
    4341          28 :   switch(pp)
    4342             :   {
    4343           7 :   case 0:  r = FpM_indexrank(x,p); break;
    4344           7 :   case 2:  r = F2m_indexrank(x); break;
    4345          14 :   default: r = Flm_indexrank(x,pp); break;
    4346             :   }
    4347          28 :   return gerepileupto(av, r);
    4348             : }
    4349             : 
    4350             : static GEN
    4351           0 : RgM_indexrank_FqM(GEN x, GEN pol, GEN p)
    4352             : {
    4353           0 :   pari_sp av = avma;
    4354           0 :   GEN r, T = RgX_to_FpX(pol, p);
    4355           0 :   if (signe(T) == 0) pari_err_OP("indexrank",x,pol);
    4356           0 :   r = FqM_indexrank(RgM_to_FqM(x, T, p), T, p);
    4357           0 :   return gerepileupto(av, r);
    4358             : }
    4359             : 
    4360             : #define code(t1,t2) ((t1 << 6) | t2)
    4361             : static GEN
    4362       22757 : RgM_indexrank_fast(GEN x)
    4363             : {
    4364             :   GEN p, pol;
    4365             :   long pa;
    4366       22757 :   long t = RgM_type(x,&p,&pol,&pa);
    4367       22757 :   switch(t)
    4368             :   {
    4369         392 :     case t_INT:    return ZM_indexrank(x);
    4370        1148 :     case t_FRAC:   return QM_indexrank(x);
    4371          28 :     case t_INTMOD: return RgM_indexrank_FpM(x, p);
    4372          21 :     case t_FFELT:  return FFM_indexrank(x, pol);
    4373             :     case code(t_POLMOD, t_INTMOD):
    4374           0 :                    return RgM_indexrank_FqM(x, pol, p);
    4375       21168 :     default:       return NULL;
    4376             :   }
    4377             : }
    4378             : #undef code
    4379             : 
    4380             : GEN
    4381       22757 : indexrank(GEN x)
    4382             : {
    4383             :   pari_sp av;
    4384             :   long r;
    4385             :   GEN d;
    4386       22757 :   if (typ(x)!=t_MAT) pari_err_TYPE("indexrank",x);
    4387       22757 :   d = RgM_indexrank_fast(x);
    4388       22757 :   if (d) return d;
    4389       21168 :   av = avma;
    4390       21168 :   init_indexrank(x);
    4391       21168 :   d = gauss_pivot(x, &r);
    4392       21168 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    4393             : }
    4394             : 
    4395             : GEN
    4396        5481 : ZM_indeximage(GEN x) {
    4397        5481 :   pari_sp av = avma;
    4398             :   long r;
    4399             :   GEN d;
    4400        5481 :   init_indexrank(x);
    4401        5481 :   d = ZM_pivots(x,&r);
    4402        5481 :   set_avma(av); return indeximage0(lg(x)-1, r, d);
    4403             : }
    4404             : long
    4405       53229 : ZM_rank(GEN x) {
    4406       53229 :   pari_sp av = avma;
    4407             :   long r;
    4408       53229 :   (void)ZM_pivots(x,&r);
    4409       53229 :   return gc_long(av, lg(x)-1-r);
    4410             : }
    4411             : GEN
    4412       24494 : ZM_indexrank(GEN x) {
    4413       24494 :   pari_sp av = avma;
    4414             :   long r;
    4415             :   GEN d;
    4416       24494 :   init_indexrank(x);
    4417       24494 :   d = ZM_pivots(x,&r);
    4418       24494 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    4419             : }
    4420             : 
    4421             : long
    4422           0 : QM_rank(GEN x)
    4423             : {
    4424           0 :   pari_sp av = avma;
    4425           0 :   long r = ZM_rank(Q_primpart(x));
    4426           0 :   set_avma(av);
    4427           0 :   return r;
    4428             : }
    4429             : 
    4430             : GEN
    4431        1148 : QM_indexrank(GEN x)
    4432             : {
    4433        1148 :   pari_sp av = avma;
    4434        1148 :   GEN r = ZM_indexrank(Q_primpart(x));
    4435        1148 :   return gerepileupto(av, r);
    4436             : }
    4437             : 
    4438             : /*******************************************************************/
    4439             : /*                                                                 */
    4440             : /*                             ZabM                                */
    4441             : /*                                                                 */
    4442             : /*******************************************************************/
    4443             : 
    4444             : static GEN
    4445        1866 : FpXM_ratlift(GEN a, GEN q)
    4446             : {
    4447             :   GEN B, y;
    4448        1866 :   long i, j, l = lg(a), n;
    4449        1866 :   B = sqrti(shifti(q,-1));
    4450        1866 :   y = cgetg(l, t_MAT);
    4451        1866 :   if (l==1) return y;
    4452        1866 :   n = lgcols(a);
    4453        5351 :   for (i=1; i<l; i++)
    4454             :   {
    4455        4351 :     GEN yi = cgetg(n, t_COL);
    4456       56840 :     for (j=1; j<n; j++)
    4457             :     {
    4458       53355 :       GEN v = FpX_ratlift(gmael(a,i,j), q, B, B, NULL);
    4459       53355 :       if (!v) return NULL;
    4460       52489 :       gel(yi, j) = RgX_renormalize(v);
    4461             :     }
    4462        3485 :     gel(y,i) = yi;
    4463             :   }
    4464        1000 :   return y;
    4465             : }
    4466             : 
    4467             : static GEN
    4468        4679 : FlmV_recover_pre(GEN a, GEN M, ulong p, ulong pi, long sv)
    4469             : {
    4470        4679 :   GEN a1 = gel(a,1);
    4471        4679 :   long i, j, k, l = lg(a1), n, lM = lg(M);
    4472        4679 :   GEN v = cgetg(lM, t_VECSMALL);
    4473        4679 :   GEN y = cgetg(l, t_MAT);
    4474        4679 :   if (l==1) return y;
    4475        4679 :   n = lgcols(a1);
    4476       35690 :   for (i=1; i<l; i++)
    4477             :   {
    4478       31011 :     GEN yi = cgetg(n, t_COL);
    4479      679347 :     for (j=1; j<n; j++)
    4480             :     {
    4481      648336 :       for (k=1; k<lM; k++) uel(v,k) = umael(gel(a,k),i,j);
    4482      648336 :       gel(yi, j) = Flm_Flc_mul_pre_Flx(M, v, p, pi, sv);
    4483             :     }
    4484       31011 :     gel(y,i) = yi;
    4485             :   }
    4486        4679 :   return y;
    4487             : }
    4488             : 
    4489             : static GEN
    4490           0 : FlkM_inv(GEN M, GEN P, ulong p)
    4491             : {
    4492           0 :   ulong pi = get_Fl_red(p);
    4493           0 :   GEN R = Flx_roots(P, p);
    4494           0 :   long l = lg(R), i;
    4495           0 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4496           0 :   GEN V = cgetg(l, t_VEC);
    4497           0 :   for(i=1; i<l; i++)
    4498             :   {
    4499           0 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, pi);
    4500           0 :     GEN H = Flm_inv_sp(FlxM_eval_powers_pre(M, pows, p, pi), NULL, p);
    4501           0 :     if (!H) return NULL;
    4502           0 :     gel(V, i) = H;
    4503             :   }
    4504           0 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    4505             : }
    4506             : 
    4507             : static GEN
    4508        2813 : FlkM_adjoint(GEN M, GEN P, ulong p)
    4509             : {
    4510        2813 :   ulong pi = get_Fl_red(p);
    4511        2813 :   GEN R = Flx_roots(P, p);
    4512        2813 :   long l = lg(R), i;
    4513        2813 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4514        2813 :   GEN V = cgetg(l, t_VEC);
    4515       14063 :   for(i=1; i<l; i++)
    4516             :   {
    4517       11250 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, pi);
    4518       11250 :     gel(V, i) = Flm_adjoint(FlxM_eval_powers_pre(M, pows, p, pi), p);
    4519             :   }
    4520        2813 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    4521             : }
    4522             : 
    4523             : 
    4524             : static GEN
    4525        2604 : ZabM_inv_slice(GEN A, GEN Q, GEN P, GEN *mod)
    4526             : {
    4527        2604 :   pari_sp av = avma;
    4528        2604 :   long i, n = lg(P)-1, w = varn(Q);
    4529             :   GEN H, T;
    4530        2604 :   if (n == 1)
    4531             :   {
    4532        2474 :     ulong p = uel(P,1);
    4533        2474 :     GEN Ap = FqM_to_FlxM(A, Q, utoi(p));
    4534        2474 :     GEN Qp = ZX_to_Flx(Q, p);
    4535        2474 :     GEN Hp = FlkM_adjoint(Ap, Qp, p);
    4536        2474 :     Hp = gerepileupto(av, FlxM_to_ZXM(Hp));
    4537        2474 :     *mod = utoi(p); return Hp;
    4538             :   }
    4539         130 :   T = ZV_producttree(P);
    4540         130 :   A = ZXM_nv_mod_tree(A, P, T, w);
    4541         130 :   Q = ZX_nv_mod_tree(Q, P, T);
    4542         130 :   H = cgetg(n+1, t_VEC);
    4543         469 :   for(i=1; i <= n; i++)
    4544             :   {
    4545         339 :     ulong p = P[i];
    4546         339 :     GEN a = gel(A,i), q = gel(Q, i);
    4547         339 :     gel(H,i) = FlkM_adjoint(a, q, p);
    4548             :   }
    4549         130 :   H = nxMV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    4550         130 :   *mod = gmael(T, lg(T)-1, 1);
    4551         130 :   gerepileall(av, 2, &H, mod);
    4552         130 :   return H;
    4553             : }
    4554             : 
    4555             : GEN
    4556        2604 : ZabM_inv_worker(GEN P, GEN A, GEN Q)
    4557             : {
    4558        2604 :   GEN V = cgetg(3, t_VEC);
    4559        2604 :   gel(V,1) = ZabM_inv_slice(A, Q, P, &gel(V,2));
    4560        2604 :   return V;
    4561             : }
    4562             : 
    4563             : static GEN
    4564        8281 : vecnorml1(GEN a)
    4565             : {
    4566             :   long i, l;
    4567        8281 :   GEN g = cgetg_copy(a, &l);
    4568      133056 :   for (i=1; i<l; i++)
    4569      124775 :     gel(g, i) = gnorml1_fake(gel(a,i));
    4570        8281 :   return g;
    4571             : }
    4572             : 
    4573             : static GEN
    4574        1813 : ZabM_true_Hadamard(GEN a)
    4575             : {
    4576        1813 :   pari_sp av = avma;
    4577        1813 :   long n = lg(a)-1, i;
    4578             :   GEN B;
    4579        1813 :   if (n == 0) return gen_1;
    4580        1813 :   if (n == 1) return gnorml1_fake(gcoeff(a,1,1));
    4581        1239 :   B = gen_1;
    4582        1239 :   for (i = 1; i <= n; i++) B = gmul(B, gnorml2(RgC_gtofp(vecnorml1(gel(a,i)),DEFAULTPREC)));
    4583        1239 :   return gerepileuptoint(av, ceil_safe(sqrtr_abs(B)));
    4584             : }
    4585             : 
    4586             : GEN
    4587        1813 : ZabM_inv(GEN A, GEN Q, long n, GEN *pt_den)
    4588             : {
    4589        1813 :   pari_sp av = avma;
    4590             :   forprime_t S;
    4591        1813 :   long m = lg(A)-1;
    4592             :   GEN bnd, H, D, d, mod, worker;
    4593        1813 :   if (m == 0)
    4594             :   {
    4595           0 :     if (pt_den) *pt_den = gen_1;
    4596           0 :     return cgetg(1, t_MAT);
    4597             :   }
    4598        1813 :   bnd = ZabM_true_Hadamard(A);
    4599        1813 :   worker = snm_closure(is_entry("_ZabM_inv_worker"), mkvec2(A, Q));
    4600        1813 :   u_forprime_arith_init(&S, HIGHBIT+1, ULONG_MAX, 1, n);
    4601        1813 :   H = gen_crt("ZabM_inv", worker, &S, NULL, expi(bnd), m, &mod,
    4602             :               nxMV_chinese_center, FpXM_center);
    4603        1813 :   D = RgMrow_RgC_mul(H, gel(A,1), 1);
    4604        1813 :   D = ZX_rem(D, Q);
    4605        1813 :   d = Z_content(mkvec2(H, D));
    4606        1813 :   if (d)
    4607             :   {
    4608         658 :     D = ZX_Z_divexact(D, d);
    4609         658 :     H = Q_div_to_int(H, d);
    4610             :   }
    4611        1813 :   if (pt_den)
    4612             :   {
    4613        1813 :     gerepileall(av, 2, &H, &D);
    4614        1813 :     *pt_den = D; return H;
    4615             :   }
    4616           0 :   return gerepileupto(av, H);
    4617             : }
    4618             : 
    4619             : GEN
    4620           0 : ZabM_inv_ratlift(GEN M, GEN P, long n, GEN *pden)
    4621             : {
    4622           0 :   pari_sp av2, av = avma;
    4623             :   GEN q, H;
    4624           0 :   ulong m = LONG_MAX>>1;
    4625           0 :   ulong p= 1 + m - (m % n);
    4626           0 :   long lM = lg(M);
    4627           0 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    4628             : 
    4629           0 :   av2 = avma;
    4630           0 :   H = NULL;
    4631             :   for(;;)
    4632           0 :   {
    4633             :     GEN Hp, Pp, Mp, Hr;
    4634           0 :     do p += n; while(!uisprime(p));
    4635           0 :     Pp = ZX_to_Flx(P, p);
    4636           0 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    4637           0 :     Hp = FlkM_inv(Mp, Pp, p);
    4638           0 :     if (!Hp) continue;
    4639           0 :     if (!H)
    4640             :     {
    4641           0 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p);
    4642           0 :       q = utoipos(p);
    4643             :     }
    4644             :     else
    4645           0 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    4646           0 :     Hr = FpXM_ratlift(H, q);
    4647           0 :     if (DEBUGLEVEL>5) err_printf("ZabM_inv mod %ld (ratlift=%ld)\n", p,!!Hr);
    4648           0 :     if (Hr) {/* DONE ? */
    4649           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    4650           0 :       GEN MH = ZXQM_mul(Hl, M, P);
    4651           0 :       if (*pden)
    4652           0 :       { if (RgM_isscalar(MH, *pden)) { H = Hl; break; }}
    4653             :       else
    4654           0 :       { if (RgM_isidentity(MH)) { H = Hl; *pden = gen_1; break; } }
    4655             :     }
    4656             : 
    4657           0 :     if (gc_needed(av,2))
    4658             :     {
    4659           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_inv");
    4660           0 :       gerepileall(av2, 2, &H, &q);
    4661             :     }
    4662             :   }
    4663           0 :   gerepileall(av, 2, &H, pden);
    4664           0 :   return H;
    4665             : }
    4666             : 
    4667             : static GEN
    4668        1866 : FlkM_ker(GEN M, GEN P, ulong p)
    4669             : {
    4670        1866 :   ulong pi = get_Fl_red(p);
    4671        1866 :   GEN R = Flx_roots(P, p);
    4672        1866 :   long l = lg(R), i, dP = degpol(P), r;
    4673             :   GEN M1, K, D;
    4674        1866 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4675        1866 :   GEN V = cgetg(l, t_VEC);
    4676        1866 :   M1 = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,1), dP, p, pi), p, pi);
    4677        1866 :   K = Flm_ker_sp(M1, p, 2);
    4678        1866 :   r = lg(gel(K,1)); D = gel(K,2);
    4679        1866 :   gel(V, 1) = gel(K,1);
    4680        3818 :   for(i=2; i<l; i++)
    4681             :   {
    4682        1952 :     GEN Mi = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), dP, p, pi), p, pi);
    4683        1952 :     GEN K = Flm_ker_sp(Mi, p, 2);
    4684        1952 :     if (lg(gel(K,1)) != r || !zv_equal(D, gel(K,2))) return NULL;
    4685        1952 :     gel(V, i) = gel(K,1);
    4686             :   }
    4687        1866 :   return mkvec2(FlmV_recover_pre(V, W, p, pi, P[1]), D);
    4688             : }
    4689             : 
    4690             : GEN
    4691         966 : ZabM_ker(GEN M, GEN P, long n)
    4692             : {
    4693         966 :   pari_sp av2, av = avma;
    4694             :   GEN q, H, D;
    4695         966 :   ulong m = LONG_MAX>>1;
    4696         966 :   ulong p= 1 + m - (m % n);
    4697         966 :   av2 = avma;
    4698         966 :   H = NULL; D = NULL;
    4699             :   for(;;)
    4700         900 :   {
    4701             :     GEN Kp, Hp, Dp, Pp, Mp, Hr;
    4702       30269 :     do p += n; while(!uisprime(p));
    4703        1866 :     Pp = ZX_to_Flx(P, p);
    4704        1866 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    4705        1866 :     Kp = FlkM_ker(Mp, Pp, p);
    4706        1866 :     if (!Kp) continue;
    4707        1866 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    4708        1866 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    4709        1866 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    4710             :     {
    4711         966 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p); D = Dp;
    4712         966 :       q = utoipos(p);
    4713             :     }
    4714             :     else
    4715         900 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    4716        1866 :     Hr = FpXM_ratlift(H, q);
    4717        1866 :     if (DEBUGLEVEL>5) err_printf("ZabM_ker mod %ld (ratlift=%ld)\n", p,!!Hr);
    4718        1866 :     if (Hr) {/* DONE ? */
    4719        1000 :       GEN Hl = vec_Q_primpart(Hr);
    4720        1000 :       GEN MH = ZXQM_mul(M, Hl,P);
    4721        1000 :       if (gequal0(MH)) { H = Hl;  break; }
    4722             :     }
    4723             : 
    4724         900 :     if (gc_needed(av,2))
    4725             :     {
    4726           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_ker");
    4727           0 :       gerepileall(av2, 3, &H, &D, &q);
    4728             :     }
    4729             :   }
    4730         966 :   return gerepilecopy(av, H);
    4731             : }
    4732             : 
    4733             : GEN
    4734        2366 : ZabM_indexrank(GEN M, GEN P, long n)
    4735             : {
    4736        2366 :   pari_sp av = avma;
    4737        2366 :   ulong m = LONG_MAX>>1;
    4738        2366 :   ulong p = 1+m-(m%n), D = degpol(P);
    4739        2366 :   long lM = lg(M), lmax = 0, c = 0;
    4740             :   GEN v;
    4741             :   for(;;)
    4742         672 :   {
    4743             :     GEN R, Mp, K;
    4744             :     ulong pi;
    4745             :     long l;
    4746       61347 :     do p += n; while (!uisprime(p));
    4747        3038 :     pi = get_Fl_red(p);
    4748        3038 :     R = Flx_roots(ZX_to_Flx(P, p), p);
    4749        3038 :     Mp = FqM_to_FlxM(M, P, utoipos(p));
    4750        3038 :     K = FlxM_eval_powers_pre(Mp, Fl_powers_pre(uel(R,1), D,p,pi), p,pi);
    4751        3038 :     v = Flm_indexrank(K, p);
    4752        3038 :     l = lg(gel(v,2));
    4753        3038 :     if (l == lM) break;
    4754         896 :     if (lmax >= 0 && l > lmax) { lmax = l; c = 0; } else c++;
    4755         896 :     if (c > 2)
    4756             :     { /* probably not maximal rank, expensive check */
    4757         224 :       lM -= lg(ZabM_ker(M, P, n))-1; /* actual rank (+1) */
    4758         224 :       if (lmax == lM) break;
    4759           0 :       lmax = -1; /* disable check */
    4760             :     }
    4761             :   }
    4762        2366 :   return gerepileupto(av, v);
    4763             : }
    4764             : 
    4765             : #if 0
    4766             : GEN
    4767             : ZabM_gauss(GEN M, GEN P, long n, GEN *den)
    4768             : {
    4769             :   pari_sp av = avma;
    4770             :   GEN v, S, W;
    4771             :   v = ZabM_indexrank(M, P, n);
    4772             :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    4773             :   W = ZabM_inv(S, P, n, den);
    4774             :   gerepileall(av,2,&W,den);
    4775             :   return W;
    4776             : }
    4777             : #endif
    4778             : 
    4779             : GEN
    4780         287 : ZabM_pseudoinv(GEN M, GEN P, long n, GEN *pv, GEN *den)
    4781             : {
    4782         287 :   GEN v = ZabM_indexrank(M, P, n);
    4783         287 :   if (pv) *pv = v;
    4784         287 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    4785         287 :   return ZabM_inv(M, P, n, den);
    4786             : }
    4787             : GEN
    4788        4515 : ZM_pseudoinv(GEN M, GEN *pv, GEN *den)
    4789             : {
    4790        4515 :   GEN v = ZM_indexrank(M);
    4791        4515 :   if (pv) *pv = v;
    4792        4515 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    4793        4515 :   return ZM_inv(M, den);
    4794             : }
    4795             : 
    4796             : /*******************************************************************/
    4797             : /*                                                                 */
    4798             : /*                   Structured Elimination                        */
    4799             : /*                                                                 */
    4800             : /*******************************************************************/
    4801             : 
    4802             : static void
    4803      100903 : rem_col(GEN c, long i, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    4804             : {
    4805      100903 :   long lc = lg(c), k;
    4806      100903 :   iscol[i] = 0; (*rcol)--;
    4807      892550 :   for (k = 1; k < lc; ++k)
    4808             :   {
    4809      791647 :     Wrow[c[k]]--;
    4810      791647 :     if (Wrow[c[k]]==0) (*rrow)--;
    4811             :   }
    4812      100903 : }
    4813             : 
    4814             : static void
    4815        6058 : rem_singleton(GEN M, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    4816             : {
    4817             :   long i, j;
    4818        6058 :   long nbcol = lg(iscol)-1, last;
    4819             :   do
    4820             :   {
    4821        8004 :     last = 0;
    4822    18978457 :     for (i = 1; i <= nbcol; ++i)
    4823    18970453 :       if (iscol[i])
    4824             :       {
    4825     9754886 :         GEN c = gmael(M, i, 1);
    4826     9754886 :         long lc = lg(c);
    4827    91077012 :         for (j = 1; j < lc; ++j)
    4828    81334899 :           if (Wrow[c[j]] == 1)
    4829             :           {
    4830       12773 :             rem_col(c, i, iscol, Wrow, rcol, rrow);
    4831       12773 :             last=1; break;
    4832             :           }
    4833             :       }
    4834        8004 :   } while (last);
    4835        6058 : }
    4836             : 
    4837             : static GEN
    4838        5932 : fill_wcol(GEN M, GEN iscol, GEN Wrow, long *w, GEN wcol)
    4839             : {
    4840        5932 :   long nbcol = lg(iscol)-1;
    4841             :   long i, j, m, last;
    4842             :   GEN per;
    4843       14728 :   for (m = 2, last=0; !last ; m++)
    4844             :   {
    4845    22267183 :     for (i = 1; i <= nbcol; ++i)
    4846             :     {
    4847    22258387 :       wcol[i] = 0;
    4848    22258387 :       if (iscol[i])
    4849             :       {
    4850    11426422 :         GEN c = gmael(M, i, 1);
    4851    11426422 :         long lc = lg(c);
    4852   106722507 :         for (j = 1; j < lc; ++j)
    4853    95296085 :           if (Wrow[c[j]] == m) {  wcol[i]++; last = 1; }
    4854             :       }
    4855             :     }
    4856             :   }
    4857        5932 :   per = vecsmall_indexsort(wcol);
    4858        5932 :   *w = wcol[per[nbcol]];
    4859        5932 :   return per;
    4860             : }
    4861             : 
    4862             : /* M is a RgMs with nbrow rows, A a list of row indices.
    4863             :    Eliminate rows of M with a single entry that do not belong to A,
    4864             :    and the corresponding columns. Also eliminate columns until #colums=#rows.
    4865             :    Return pcol and prow:
    4866             :    pcol is a map from the new columns indices to the old one.
    4867             :    prow is a map from the old rows indices to the new one (0 if removed).
    4868             : */
    4869             : 
    4870             : void
    4871         126 : RgMs_structelim_col(GEN M, long nbcol, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    4872             : {
    4873             :   long i,j,k;
    4874         126 :   long lA = lg(A);
    4875         126 :   GEN prow = cgetg(nbrow+1, t_VECSMALL);
    4876         126 :   GEN pcol = zero_zv(nbcol);
    4877         126 :   pari_sp av = avma;
    4878         126 :   long rcol = nbcol, rrow = 0, imin = nbcol - usqrt(nbcol);
    4879         126 :   GEN iscol = const_vecsmall(nbcol, 1);
    4880         126 :   GEN Wrow  = zero_zv(nbrow);
    4881         126 :   GEN wcol = cgetg(nbcol+1, t_VECSMALL);
    4882         126 :   pari_sp av2=avma;
    4883      126721 :   for (i = 1; i <= nbcol; ++i)
    4884             :   {
    4885      126595 :     GEN F = gmael(M, i, 1);
    4886      126595 :     long l = lg(F)-1;
    4887     1118175 :     for (j = 1; j <= l; ++j)
    4888      991580 :       Wrow[F[j]]++;
    4889             :   }
    4890         126 :   for (j = 1; j < lA; ++j)
    4891             :   {
    4892           0 :     if (Wrow[A[j]] == 0) { *p_col=NULL; return; }
    4893           0 :     Wrow[A[j]] = -1;
    4894             :   }
    4895      237272 :   for (i = 1; i <= nbrow; ++i)
    4896      237146 :     if (Wrow[i])
    4897       67633 :       rrow++;
    4898         126 :   rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    4899         126 :   if (rcol<rrow) pari_err_BUG("RgMs_structelim, rcol<rrow");
    4900        6184 :   for (; rcol>rrow;)
    4901             :   {
    4902             :     long w;
    4903        5932 :     GEN per = fill_wcol(M, iscol, Wrow, &w, wcol);
    4904       94062 :     for (i = nbcol; i>=imin && wcol[per[i]]>=w && rcol>rrow; i--)
    4905       88130 :       rem_col(gmael(M, per[i], 1), per[i], iscol, Wrow, &rcol, &rrow);
    4906        5932 :     rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    4907        5932 :     set_avma(av2);
    4908             :   }
    4909      126721 :   for (j = 1, i = 1; i <= nbcol; ++i)
    4910      126595 :     if (iscol[i])
    4911       25692 :       pcol[j++] = i;
    4912         126 :   setlg(pcol,j);
    4913      237272 :   for (k = 1, i = 1; i <= nbrow; ++i)
    4914      237146 :     prow[i] = Wrow[i] ? k++: 0;
    4915         126 :   set_avma(av);
    4916         126 :   *p_col = pcol; *p_row = prow;
    4917             : }
    4918             : 
    4919             : void
    4920           0 : RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    4921             : {
    4922           0 :   RgMs_structelim_col(M, lg(M)-1, nbrow, A, p_col, p_row);
    4923           0 : }
    4924             : 
    4925             : /*******************************************************************/
    4926             : /*                                                                 */
    4927             : /*                        EIGENVECTORS                             */
    4928             : /*   (independent eigenvectors, sorted by increasing eigenvalue)   */
    4929             : /*                                                                 */
    4930             : /*******************************************************************/
    4931             : /* assume x is square of dimension > 0 */
    4932             : static int
    4933          27 : RgM_is_symmetric_cx(GEN x, long bit)
    4934             : {
    4935          27 :   pari_sp av = avma;
    4936          27 :   long i, j, l = lg(x);
    4937         138 :   for (i = 1; i < l; i++)
    4938         418 :     for (j = 1; j < i; j++)
    4939             :     {
    4940         307 :       GEN a = gcoeff(x,i,j), b = gcoeff(x,j,i), c = gsub(a,b);
    4941         307 :       if (!gequal0(c) && gexpo(c) - gexpo(a) > -bit) return gc_long(av,0);
    4942             :     }
    4943          14 :   return gc_long(av,1);
    4944             : }
    4945             : static GEN
    4946          27 : eigen_err(int exact, GEN x, long flag, long prec)
    4947             : {
    4948          27 :   pari_sp av = avma;
    4949          27 :   if (RgM_is_symmetric_cx(x, prec2nbits(prec) - 10))
    4950             :   { /* approximately symmetric: recover */
    4951          14 :     x = jacobi(x, prec); if (flag) return x;
    4952           7 :     return gerepilecopy(av, gel(x,1));
    4953             :   }
    4954          13 :   if (exact)
    4955             :   {
    4956           6 :     GEN y = mateigen(x, flag, precdbl(prec));
    4957           6 :     return gerepilecopy(av, gprec_wtrunc(y, prec));
    4958             :   }
    4959           7 :   pari_err_PREC("mateigen");
    4960             :   return NULL; /* LCOV_EXCL_LINE */
    4961             : }
    4962             : GEN
    4963          97 : mateigen(GEN x, long flag, long prec)
    4964             : {
    4965             :   GEN y, R, T;
    4966          97 :   long k, l, ex, n = lg(x);
    4967             :   int exact;
    4968          97 :   pari_sp av = avma;
    4969             : 
    4970          97 :   if (typ(x)!=t_MAT) pari_err_TYPE("eigen",x);
    4971          97 :   if (n != 1 && n != lgcols(x)) pari_err_DIM("eigen");
    4972          97 :   if (flag < 0 || flag > 1) pari_err_FLAG("mateigen");
    4973          97 :   if (n == 1)
    4974             :   {
    4975          14 :     if (flag) retmkvec2(cgetg(1,t_VEC), cgetg(1,t_MAT));
    4976           7 :     return cgetg(1,t_VEC);
    4977             :   }
    4978          83 :   if (n == 2)
    4979             :   {
    4980          14 :     if (flag) retmkvec2(mkveccopy(gcoeff(x,1,1)), matid(1));
    4981           7 :     return matid(1);
    4982             :   }
    4983             : 
    4984          69 :   ex = 16 - prec2nbits(prec);
    4985          69 :   T = charpoly(x,0);
    4986          69 :   exact = RgX_is_QX(T);
    4987          69 :   if (exact)
    4988             :   {
    4989          41 :     T = ZX_radical( Q_primpart(T) );
    4990          41 :     R = nfrootsQ(T);
    4991          41 :     if (lg(R)-1 < degpol(T))
    4992             :     { /* add missing complex roots */
    4993          27 :       GEN r = cleanroots(RgX_div(T, roots_to_pol(R, 0)), prec);
    4994          27 :       settyp(r, t_VEC);
    4995          27 :       R = shallowconcat(R, r);
    4996             :     }
    4997             :   }
    4998             :   else
    4999             :   {
    5000          28 :     GEN r1, v = vectrunc_init(lg(T));
    5001             :     long e;
    5002          28 :     R = cleanroots(T,prec);
    5003          28 :     r1 = NULL;
    5004         168 :     for (k = 1; k < lg(R); k++)
    5005             :     {
    5006         140 :       GEN r2 = gel(R,k), r = grndtoi(r2, &e);
    5007         140 :       if (e < ex) r2 = r;
    5008         140 :       if (r1)
    5009             :       {
    5010         112 :         r = gsub(r1,r2);
    5011         112 :         if (gequal0(r) || gexpo(r) < ex) continue;
    5012             :       }
    5013          98 :       vectrunc_append(v, r2);
    5014          98 :       r1 = r2;
    5015             :     }
    5016          28 :     R = v;
    5017             :   }
    5018             :   /* R = distinct complex roots of charpoly(x) */
    5019          69 :   l = lg(R); y = cgetg(l, t_VEC);
    5020         279 :   for (k = 1; k < l; k++)
    5021             :   {
    5022         237 :     GEN F = ker_aux(RgM_Rg_sub_shallow(x, gel(R,k)), x);
    5023         237 :     long d = lg(F)-1;
    5024         237 :     if (!d) { set_avma(av); return eigen_err(exact, x, flag, prec); }
    5025         210 :     gel(y,k) = F;
    5026         210 :     if (flag) gel(R,k) = const_vec(d, gel(R,k));
    5027             :   }
    5028          42 :   y = shallowconcat1(y);
    5029          42 :   if (lg(y) > n) { set_avma(av); return eigen_err(exact, x, flag, prec); }
    5030             :   /* lg(y) < n if x is not diagonalizable */
    5031          42 :   if (flag) y = mkvec2(shallowconcat1(R), y);
    5032          42 :   return gerepilecopy(av,y);
    5033             : }
    5034             : GEN
    5035           0 : eigen(GEN x, long prec) { return mateigen(x, 0, prec); }
    5036             : 
    5037             : /*******************************************************************/
    5038             : /*                                                                 */
    5039             : /*                           DETERMINANT                           */
    5040             : /*                                                                 */
    5041             : /*******************************************************************/
    5042             : 
    5043             : GEN
    5044        4060 : det0(GEN a,long flag)
    5045             : {
    5046        4060 :   switch(flag)
    5047             :   {
    5048        4046 :     case 0: return det(a);
    5049          14 :     case 1: return det2(a);
    5050           0 :     default: pari_err_FLAG("matdet");
    5051             :   }
    5052             :   return NULL; /* LCOV_EXCL_LINE */
    5053             : }
    5054             : 
    5055             : /* M a 2x2 matrix, returns det(M) */
    5056             : static GEN
    5057        7580 : RgM_det2(GEN M)
    5058             : {
    5059        7580 :   pari_sp av = avma;
    5060        7580 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5061        7580 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5062        7580 :   return gerepileupto(av, gsub(gmul(a,d), gmul(b,c)));
    5063             : }
    5064             : /* M a 2x2 ZM, returns det(M) */
    5065             : static GEN
    5066        9700 : ZM_det2(GEN M)
    5067             : {
    5068        9700 :   pari_sp av = avma;
    5069        9700 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5070        9700 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5071        9700 :   return gerepileuptoint(av, subii(mulii(a,d), mulii(b, c)));
    5072             : }
    5073             : /* M a 3x3 ZM, return det(M) */
    5074             : static GEN
    5075        3136 : ZM_det3(GEN M)
    5076             : {
    5077        3136 :   pari_sp av = avma;
    5078        3136 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2), c = gcoeff(M,1,3);
    5079        3136 :   GEN d = gcoeff(M,2,1), e = gcoeff(M,2,2), f = gcoeff(M,2,3);
    5080        3136 :   GEN g = gcoeff(M,3,1), h = gcoeff(M,3,2), i = gcoeff(M,3,3);
    5081        3136 :   GEN t, D = signe(i)? mulii(subii(mulii(a,e), mulii(b,d)), i): gen_0;
    5082        3136 :   if (signe(g))
    5083             :   {
    5084        2751 :     t = mulii(subii(mulii(b,f), mulii(c,e)), g);
    5085        2751 :     D = addii(D, t);
    5086             :   }
    5087        3136 :   if (signe(h))
    5088             :   {
    5089        2835 :     t = mulii(subii(mulii(c,d), mulii(a,f)), h);
    5090        2835 :     D = addii(D, t);
    5091             :   }
    5092        3136 :   return gerepileuptoint(av, D);
    5093             : }
    5094             : 
    5095             : static GEN
    5096       11032 : det_simple_gauss(GEN a, GEN data, pivot_fun pivot)
    5097             : {
    5098       11032 :   pari_sp av = avma;
    5099       11032 :   long i,j,k, s = 1, nbco = lg(a)-1;
    5100       11032 :   GEN p, x = gen_1;
    5101             : 
    5102       11032 :   a = RgM_shallowcopy(a);
    5103       71154 :   for (i=1; i<nbco; i++)
    5104             :   {
    5105       60129 :     k = pivot(a, data, i, NULL);
    5106       60129 :     if (k > nbco) return gerepilecopy(av, gcoeff(a,i,i));
    5107       60122 :     if (k != i)
    5108             :     { /* exchange the lines s.t. k = i */
    5109       23654 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    5110       23654 :       s = -s;
    5111             :     }
    5112       60122 :     p = gcoeff(a,i,i);
    5113             : 
    5114       60122 :     x = gmul(x,p);
    5115      403120 :     for (k=i+1; k<=nbco; k++)
    5116             :     {
    5117      342998 :       GEN m = gcoeff(a,i,k);
    5118      342998 :       if (gequal0(m)) continue;
    5119             : 
    5120      225941 :       m = gdiv(m,p);
    5121     2172786 :       for (j=i+1; j<=nbco; j++)
    5122     1946845 :         gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i)));
    5123             :     }
    5124       60122 :     if (gc_needed(av,2))
    5125             :     {
    5126           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5127           0 :       gerepileall(av,2, &a,&x);
    5128             :     }
    5129             :   }
    5130       11025 :   if (s < 0) x = gneg_i(x);
    5131       11025 :   return gerepileupto(av, gmul(x, gcoeff(a,nbco,nbco)));
    5132             : }
    5133             : 
    5134             : GEN
    5135        6716 : det2(GEN a)
    5136             : {
    5137             :   GEN data;
    5138             :   pivot_fun pivot;
    5139        6716 :   long n = lg(a)-1;
    5140        6716 :   if (typ(a)!=t_MAT) pari_err_TYPE("det2",a);
    5141        6716 :   if (!n) return gen_1;
    5142        6716 :   if (n != nbrows(a)) pari_err_DIM("det2");
    5143        6716 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5144        6716 :   if (n == 2) return RgM_det2(a);
    5145        2279 :   pivot = get_pivot_fun(a, a, &data);
    5146        2279 :   return det_simple_gauss(a, data, pivot);
    5147             : }
    5148             : 
    5149             : /* Assumes a a square t_MAT of dimension n > 0. Returns det(a) using
    5150             :  * Gauss-Bareiss. */
    5151             : static GEN
    5152         448 : det_bareiss(GEN a)
    5153             : {
    5154         448 :   pari_sp av = avma;
    5155         448 :   long nbco = lg(a)-1,i,j,k,s = 1;
    5156             :   GEN p, pprec;
    5157             : 
    5158         448 :   a = RgM_shallowcopy(a);
    5159        1274 :   for (pprec=gen_1,i=1; i<nbco; i++,pprec=p)
    5160             :   {
    5161         826 :     int diveuc = (gequal1(pprec)==0);
    5162             :     GEN ci;
    5163             : 
    5164         826 :     p = gcoeff(a,i,i);
    5165         826 :     if (gequal0(p))
    5166             :     {
    5167           0 :       k=i+1; while (k<=nbco && gequal0(gcoeff(a,i,k))) k++;
    5168           0 :       if (k>nbco) return gerepilecopy(av, p);
    5169           0 :       swap(gel(a,k), gel(a,i)); s = -s;
    5170           0 :       p = gcoeff(a,i,i);
    5171             :     }
    5172         826 :     ci = gel(a,i);
    5173        2184 :     for (k=i+1; k<=nbco; k++)
    5174             :     {
    5175        1358 :       GEN ck = gel(a,k), m = gel(ck,i);
    5176        1358 :       if (gequal0(m))
    5177             :       {
    5178           0 :         if (gequal1(p))
    5179             :         {
    5180           0 :           if (diveuc)
    5181           0 :             gel(a,k) = gdiv(gel(a,k), pprec);
    5182             :         }
    5183             :         else
    5184           0 :           for (j=i+1; j<=nbco; j++)
    5185             :           {
    5186           0 :             GEN p1 = gmul(p, gel(ck,j));
    5187           0 :             if (diveuc) p1 = gdiv(p1,pprec);
    5188           0 :             gel(ck,j) = p1;
    5189             :           }
    5190             :       }
    5191             :       else
    5192        4088 :         for (j=i+1; j<=nbco; j++)
    5193             :         {
    5194        2730 :           pari_sp av2 = avma;
    5195        2730 :           GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j)));
    5196        2730 :           if (diveuc) p1 = gdiv(p1,pprec);
    5197        2730 :           gel(ck,j) = gerepileupto(av2, p1);
    5198             :         }
    5199        1358 :       if (gc_needed(av,2))
    5200             :       {
    5201           0 :         if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5202           0 :         gerepileall(av,2, &a,&pprec);
    5203           0 :         ci = gel(a,i);
    5204           0 :         p = gcoeff(a,i,i);
    5205             :       }
    5206             :     }
    5207             :   }
    5208         448 :   p = gcoeff(a,nbco,nbco);
    5209         448 :   p = (s < 0)? gneg(p): gcopy(p);
    5210         448 :   return gerepileupto(av, p);
    5211             : }
    5212             : 
    5213             : /* count non-zero entries in col j, at most 'max' of them.
    5214             :  * Return their indices */
    5215             : static GEN
    5216        1400 : col_count_non_zero(GEN a, long j, long max)
    5217             : {
    5218        1400 :   GEN v = cgetg(max+1, t_VECSMALL);
    5219        1400 :   GEN c = gel(a,j);
    5220        1400 :   long i, l = lg(a), k = 1;
    5221        5278 :   for (i = 1; i < l; i++)
    5222        5040 :     if (!gequal0(gel(c,i)))
    5223             :     {
    5224        4788 :       if (k > max) return NULL; /* fail */
    5225        3626 :       v[k++] = i;
    5226             :     }
    5227         238 :   setlg(v, k); return v;
    5228             : }
    5229             : /* count non-zero entries in row i, at most 'max' of them.
    5230             :  * Return their indices */
    5231             : static GEN
    5232        1386 : row_count_non_zero(GEN a, long i, long max)
    5233             : {
    5234        1386 :   GEN v = cgetg(max+1, t_VECSMALL);
    5235        1386 :   long j, l = lg(a), k = 1;
    5236        5222 :   for (j = 1; j < l; j++)
    5237        4998 :     if (!gequal0(gcoeff(a,i,j)))
    5238             :     {
    5239        4774 :       if (k > max) return NULL; /* fail */
    5240        3612 :       v[k++] = j;
    5241             :     }
    5242         224 :   setlg(v, k); return v;
    5243             : }
    5244             : 
    5245             : static GEN det_develop(GEN a, long max, double bound);
    5246             : /* (-1)^(i+j) a[i,j] * det RgM_minor(a,i,j) */
    5247             : static GEN
    5248         406 : coeff_det(GEN a, long i, long j, long max, double bound)
    5249             : {
    5250         406 :   GEN c = gcoeff(a, i, j);
    5251         406 :   c = gmul(c, det_develop(RgM_minor(a, i,j), max, bound));
    5252         406 :   if (odd(i+j)) c = gneg(c);
    5253         406 :   return c;
    5254             : }
    5255             : /* a square t_MAT, 'bound' a rough upper bound for the number of
    5256             :  * multiplications we are willing to pay while developing rows/columns before
    5257             :  * switching to Gaussian elimination */
    5258             : static GEN
    5259         644 : det_develop(GEN M, long max, double bound)
    5260             : {
    5261         644 :   pari_sp av = avma;
    5262         644 :   long i,j, n = lg(M)-1, lbest = max+2, best_col = 0, best_row = 0;
    5263         644 :   GEN best = NULL;
    5264             : 
    5265         644 :   if (bound < 1.) return det_bareiss(M); /* too costly now */
    5266             : 
    5267         420 :   switch(n)
    5268             :   {
    5269           0 :     case 0: return gen_1;
    5270           0 :     case 1: return gcopy(gcoeff(M,1,1));
    5271          14 :     case 2: return RgM_det2(M);
    5272             :   }
    5273         406 :   if (max > ((n+2)>>1)) max = (n+2)>>1;
    5274        1792 :   for (j = 1; j <= n; j++)
    5275             :   {
    5276        1400 :     pari_sp av2 = avma;
    5277        1400 :     GEN v = col_count_non_zero(M, j, max);
    5278             :     long lv;
    5279        1400 :     if (!v || (lv = lg(v)) >= lbest) { set_avma(av2); continue; }
    5280         182 :     if (lv == 1) { set_avma(av); return gen_0; }
    5281         182 :     if (lv == 2) {
    5282          14 :       set_avma(av);
    5283          14 :       return gerepileupto(av, coeff_det(M,v[1],j,max,bound));
    5284             :     }
    5285         168 :     best = v; lbest = lv; best_col = j;
    5286             :   }
    5287        1778 :   for (i = 1; i <= n; i++)
    5288             :   {
    5289        1386 :     pari_sp av2 = avma;
    5290        1386 :     GEN v = row_count_non_zero(M, i, max);
    5291             :     long lv;
    5292        1386 :     if (!v || (lv = lg(v)) >= lbest) { set_avma(av2); continue; }
    5293           0 :     if (lv == 1) { set_avma(av); return gen_0; }
    5294           0 :     if (lv == 2) {
    5295           0 :       set_avma(av);
    5296           0 :       return gerepileupto(av, coeff_det(M,i,v[1],max,bound));
    5297             :     }
    5298           0 :     best = v; lbest = lv; best_row = i;
    5299             :   }
    5300         392 :   if (best_row)
    5301             :   {
    5302           0 :     double d = lbest-1;
    5303           0 :     GEN s = NULL;
    5304             :     long k;
    5305           0 :     bound /= d*d*d;
    5306           0 :     for (k = 1; k < lbest; k++)
    5307             :     {
    5308           0 :       GEN c = coeff_det(M, best_row, best[k], max, bound);
    5309           0 :       s = s? gadd(s, c): c;
    5310             :     }
    5311           0 :     return gerepileupto(av, s);
    5312             :   }
    5313         392 :   if (best_col)
    5314             :   {
    5315         168 :     double d = lbest-1;
    5316         168 :     GEN s = NULL;
    5317             :     long k;
    5318         168 :     bound /= d*d*d;
    5319         560 :     for (k = 1; k < lbest; k++)
    5320             :     {
    5321         392 :       GEN c = coeff_det(M, best[k], best_col, max, bound);
    5322         392 :       s = s? gadd(s, c): c;
    5323             :     }
    5324         168 :     return gerepileupto(av, s);
    5325             :   }
    5326         224 :   return det_bareiss(M);
    5327             : }
    5328             : 
    5329             : /* area of parallelogram bounded by (v1,v2) */
    5330             : static GEN
    5331       56459 : parallelogramarea(GEN v1, GEN v2)
    5332       56459 : { return gsub(gmul(gnorml2(v1), gnorml2(v2)), gsqr(RgV_dotproduct(v1, v2))); }
    5333             : 
    5334             : /* Square of Hadamard bound for det(a), a square matrix.
    5335             :  * Slight improvement: instead of using the column norms, use the area of
    5336             :  * the parallelogram formed by pairs of consecutive vectors */
    5337             : GEN
    5338       17655 : RgM_Hadamard(GEN a)
    5339             : {
    5340       17655 :   pari_sp av = avma;
    5341       17655 :   long n = lg(a)-1, i;
    5342             :   GEN B;
    5343       17655 :   if (n == 0) return gen_1;
    5344       17655 :   if (n == 1) return gsqr(gcoeff(a,1,1));
    5345       17655 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    5346       17655 :   B = gen_1;
    5347       74114 :   for (i = 1; i <= n/2; i++)
    5348       56459 :     B = gmul(B, parallelogramarea(gel(a,2*i-1), gel(a,2*i)));
    5349       17655 :   if (odd(n)) B = gmul(B, gnorml2(gel(a, n)));
    5350       17655 :   return gerepileuptoint(av, ceil_safe(B));
    5351             : }
    5352             : 
    5353             : /* If B=NULL, assume B=A' */
    5354             : static GEN
    5355       48518 : ZM_det_slice(GEN A, GEN P, GEN *mod)
    5356             : {
    5357       48518 :   pari_sp av = avma;
    5358       48518 :   long i, n = lg(P)-1;
    5359             :   GEN H, T;
    5360       48518 :   if (n == 1)
    5361             :   {
    5362       37624 :     ulong Hp, p = uel(P,1);
    5363       37624 :     GEN a = ZM_to_Flm(A, p);
    5364       37624 :     Hp = Flm_det_sp(a, p);
    5365       37624 :     set_avma(av);
    5366       37624 :     *mod = utoi(p); return utoi(Hp);
    5367             :   }
    5368       10894 :   T = ZV_producttree(P);
    5369       10894 :   A = ZM_nv_mod_tree(A, P, T);
    5370       10894 :   H = cgetg(n+1, t_VECSMALL);
    5371       38386 :   for(i=1; i <= n; i++)
    5372             :   {
    5373       27492 :     ulong p = P[i];
    5374       27492 :     GEN a = gel(A,i);
    5375       27492 :     H[i] = Flm_det_sp(a, p);
    5376             :   }
    5377       10894 :   H = ZV_chinese_tree(H, P, T, ZV_chinesetree(P,T));
    5378       10894 :   *mod = gmael(T, lg(T)-1, 1);
    5379       10894 :   gerepileall(av, 2, &H, mod);
    5380       10894 :   return H;
    5381             : }
    5382             : 
    5383             : GEN
    5384       48518 : ZM_det_worker(GEN P, GEN A)
    5385             : {
    5386       48518 :   GEN V = cgetg(3, t_VEC);
    5387       48518 :   gel(V,1) = ZM_det_slice(A, P, &gel(V,2));
    5388       48518 :   return V;
    5389             : }
    5390             : 
    5391             : GEN
    5392       31345 : ZM_det(GEN M)
    5393             : {
    5394       31345 :   const long DIXON_THRESHOLD = 40;
    5395             :   pari_sp av, av2;
    5396       31345 :   long i, n = lg(M)-1;
    5397             :   ulong p, Dp;
    5398             :   forprime_t S;
    5399             :   pari_timer ti;
    5400             :   GEN H, D, mod, h, q, v, worker;
    5401             : #ifdef LONG_IS_64BIT
    5402       26868 :   const ulong PMAX = 18446744073709551557UL;
    5403             : #else
    5404        4477 :   const ulong PMAX = 4294967291UL;
    5405             : #endif
    5406             : 
    5407       31345 :   switch(n)
    5408             :   {
    5409           7 :     case 0: return gen_1;
    5410         847 :     case 1: return icopy(gcoeff(M,1,1));
    5411        9700 :     case 2: return ZM_det2(M);
    5412        3136 :     case 3: return ZM_det3(M);
    5413             :   }
    5414       17655 :   if (DEBUGLEVEL>=4) timer_start(&ti);
    5415       17655 :   av = avma; h = RgM_Hadamard(M); /* |D| <= sqrt(h) */
    5416       17655 :   if (!signe(h)) { set_avma(av); return gen_0; }
    5417       17655 :   h = sqrti(h);
    5418       17655 :   if (lgefint(h) == 3 && (ulong)h[2] <= (PMAX >> 1))
    5419             :   { /* h < p/2 => direct result */
    5420        5018 :     p = PMAX;
    5421        5018 :     Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    5422        5018 :     set_avma(av);
    5423        5018 :     if (!Dp) return gen_0;
    5424        5018 :     return (Dp <= (p>>1))? utoipos(Dp): utoineg(p - Dp);
    5425             :   }
    5426       12637 :   q = gen_1; Dp = 1;
    5427       12637 :   init_modular_big(&S);
    5428       12637 :   p = 0; /* -Wall */
    5429       25274 :   while (cmpii(q, h) <= 0 && (p = u_forprime_next(&S)))
    5430             :   {
    5431       12637 :     av2 = avma; Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    5432       12637 :     set_avma(av2);
    5433       12637 :     if (Dp) break;
    5434           0 :     q = muliu(q, p);
    5435             :   }
    5436       12637 :   if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    5437       12637 :   if (!Dp) { set_avma(av); return gen_0; }
    5438       12637 :   if (pari_mt_nbthreads > 1 || n <= DIXON_THRESHOLD)
    5439       12637 :     D = q; /* never competitive when bound is sharp even with 2 threads */
    5440             :   else
    5441             :   {
    5442           0 :     av2 = avma;
    5443           0 :     v = cgetg(n+1, t_COL);
    5444           0 :     gel(v, 1) = gen_1; /* ensure content(v) = 1 */
    5445           0 :     for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    5446           0 :     D = Q_denom(ZM_gauss(M, v));
    5447           0 :     if (expi(D) < expi(h) >> 1)
    5448             :     { /* First try unlucky, try once more */
    5449           0 :       for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    5450           0 :       D = lcmii(D, Q_denom(ZM_gauss(M, v)));
    5451             :     }
    5452           0 :     D = gerepileuptoint(av2, D);
    5453           0 :     if (q != gen_1) D = lcmii(D, q);
    5454             :   }
    5455       12637 :   if (DEBUGLEVEL >=4)
    5456           0 :     timer_printf(&ti,"ZM_det: Dixon %ld/%ld bits",expi(D),expi(h));
    5457             :   /* determinant is a multiple of D */
    5458       12637 :   if (is_pm1(D)) D = NULL;
    5459       12637 :   if (D) h = diviiexact(h, D);
    5460       12637 :   worker = snm_closure(is_entry("_ZM_det_worker"), mkvec(M));
    5461       12637 :   H = gen_crt("ZM_det", worker, &S, D, expi(h)+1, lg(M)-1, &mod,
    5462             :               ZV_chinese, NULL);
    5463       12637 :   if (D) H = Fp_div(H, D, mod);
    5464       12637 :   H = Fp_center(H, mod, shifti(mod,-1));
    5465       12637 :   if (D) H = mulii(H, D);
    5466       12637 :   return gerepileuptoint(av, H);
    5467             : }
    5468             : 
    5469             : static GEN
    5470        1519 : RgM_det_FpM(GEN a, GEN p)
    5471             : {
    5472        1519 :   pari_sp av = avma;
    5473             :   ulong pp, d;
    5474        1519 :   a = RgM_Fp_init(a,p,&pp);
    5475        1519 :   switch(pp)
    5476             :   {
    5477          70 :   case 0: return gerepileupto(av, Fp_to_mod(FpM_det(a,p),p)); break;
    5478          14 :   case 2: d = F2m_det_sp(a); break;
    5479        1435 :   default:d = Flm_det_sp(a, pp); break;
    5480             :   }
    5481        1449 :   set_avma(av); return mkintmodu(d, pp);
    5482             : }
    5483             : 
    5484             : static GEN
    5485          42 : RgM_det_FqM(GEN x, GEN pol, GEN p)
    5486             : {
    5487          42 :   pari_sp av = avma;
    5488          42 :   GEN b, T = RgX_to_FpX(pol, p);
    5489          42 :   if (signe(T) == 0) pari_err_OP("%",x,pol);
    5490          42 :   b = FqM_det(RgM_to_FqM(x, T, p), T, p);
    5491          42 :   if (!b) return gc_NULL(av);
    5492          42 :   return gerepilecopy(av, mkpolmod(FpX_to_mod(b, p), FpX_to_mod(T, p)));
    5493             : }
    5494             : 
    5495             : #define code(t1,t2) ((t1 << 6) | t2)
    5496             : static GEN
    5497       11147 : RgM_det_fast(GEN x)
    5498             : {
    5499             :   GEN p, pol;
    5500             :   long pa;
    5501       11147 :   long t = RgM_type(x, &p,&pol,&pa);
    5502       11147 :   switch(t)
    5503             :   {
    5504         336 :     case t_INT:    return ZM_det(x);
    5505         196 :     case t_FRAC:   return QM_det(x);
    5506          63 :     case t_FFELT:  return FFM_det(x, pol);
    5507        1519 :     case t_INTMOD: return RgM_det_FpM(x, p);
    5508             :     case code(t_POLMOD, t_INTMOD):
    5509          42 :                    return RgM_det_FqM(x, pol, p);
    5510        8991 :     default:       return NULL;
    5511             :   }
    5512             : }
    5513             : #undef code
    5514             : 
    5515             : static long
    5516         238 : det_init_max(long n)
    5517             : {
    5518         238 :   if (n > 100) return 0;
    5519         238 :   if (n > 50) return 1;
    5520         238 :   if (n > 30) return 4;
    5521         238 :   return 7;
    5522             : }
    5523             : 
    5524             : GEN
    5525      152943 : det(GEN a)
    5526             : {
    5527      152943 :   long n = lg(a)-1;
    5528             :   double B;
    5529             :   GEN data, b;
    5530             :   pivot_fun pivot;
    5531             : 
    5532      152943 :   if (typ(a)!=t_MAT) pari_err_TYPE("det",a);
    5533      152943 :   if (!n) return gen_1;
    5534      152901 :   if (n != nbrows(a)) pari_err_DIM("det");
    5535      152894 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5536       14276 :   if (n == 2) return RgM_det2(a);
    5537       11147 :   b = RgM_det_fast(a);
    5538       11147 :   if (b) return b;
    5539        8991 :   pivot = get_pivot_fun(a, a, &data);
    5540        8991 :   if (pivot != gauss_get_pivot_NZ) return det_simple_gauss(a, data, pivot);
    5541         238 :   B = (double)n;
    5542         238 :   return det_develop(a, det_init_max(n), B*B*B);
    5543             : }
    5544             : 
    5545             : GEN
    5546         196 : QM_det(GEN M)
    5547             : {
    5548         196 :   pari_sp av = avma;
    5549         196 :   GEN cM, pM = Q_primitive_part(M, &cM);
    5550         196 :   GEN b = ZM_det(pM);
    5551         196 :   if (cM) b = gmul(b, gpowgs(cM, lg(M)-1));
    5552         196 :   return gerepileupto(av, b);
    5553             : }

Generated by: LCOV version 1.13