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.0 lcov report (development 23339-b1c33c51a) Lines: 3442 3687 93.4 %
Date: 2018-12-11 05:41:34 Functions: 340 359 94.7 %
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          66 : gerepile_mat(pari_sp av, pari_sp tetpil, GEN x, long k, long m, long n, long t)
      31             : {
      32          66 :   pari_sp A, bot = pari_mainstack->bot;
      33             :   long u, i;
      34             :   size_t dec;
      35             : 
      36          66 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      37             : 
      38         949 :   for (u=t+1; u<=m; u++)
      39             :   {
      40         883 :     A = (pari_sp)coeff(x,u,k);
      41         883 :     if (A < av && A >= bot) coeff(x,u,k) += dec;
      42             :   }
      43        1920 :   for (i=k+1; i<=n; i++)
      44       75282 :     for (u=1; u<=m; u++)
      45             :     {
      46       73428 :       A = (pari_sp)coeff(x,u,i);
      47       73428 :       if (A < av && A >= bot) coeff(x,u,i) += dec;
      48             :     }
      49          66 : }
      50             : 
      51             : static void
      52          66 : gen_gerepile_gauss_ker(GEN x, long k, long t, pari_sp av, void *E, GEN (*copy)(void*, GEN))
      53             : {
      54          66 :   pari_sp tetpil = avma;
      55          66 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      56             : 
      57          66 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot_ker. k=%ld, n=%ld",k,n);
      58          66 :   for (u=t+1; u<=m; u++) gcoeff(x,u,k) = copy(E,gcoeff(x,u,k));
      59        1920 :   for (i=k+1; i<=n; i++)
      60        1854 :     for (u=1; u<=m; u++) gcoeff(x,u,i) = copy(E,gcoeff(x,u,i));
      61          66 :   gerepile_mat(av,tetpil,x,k,m,n,t);
      62          66 : }
      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          10 : gerepile_gauss(GEN x,long k,long t,pari_sp av, long j, GEN c)
      85             : {
      86          10 :   pari_sp tetpil = avma, A, bot;
      87          10 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      88             :   size_t dec;
      89             : 
      90          10 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot. k=%ld, n=%ld",k,n);
      91         375 :   for (u=t+1; u<=m; u++)
      92         365 :     if (u==j || !c[u]) COPY(gcoeff(x,u,k));
      93         710 :   for (u=1; u<=m; u++)
      94         700 :     if (u==j || !c[u])
      95         690 :       for (i=k+1; i<=n; i++) COPY(gcoeff(x,u,i));
      96             : 
      97          10 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      98          10 :   bot = pari_mainstack->bot;
      99         375 :   for (u=t+1; u<=m; u++)
     100         365 :     if (u==j || !c[u])
     101             :     {
     102         365 :       A=(pari_sp)coeff(x,u,k);
     103         365 :       if (A<av && A>=bot) coeff(x,u,k)+=dec;
     104             :     }
     105         710 :   for (u=1; u<=m; u++)
     106         700 :     if (u==j || !c[u])
     107       47610 :       for (i=k+1; i<=n; i++)
     108             :       {
     109       46920 :         A=(pari_sp)coeff(x,u,i);
     110       46920 :         if (A<av && A>=bot) coeff(x,u,i)+=dec;
     111             :       }
     112          10 : }
     113             : 
     114             : /*******************************************************************/
     115             : /*                                                                 */
     116             : /*                         GENERIC                                 */
     117             : /*                                                                 */
     118             : /*******************************************************************/
     119             : GEN
     120        3351 : gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff)
     121             : {
     122        3351 :   pari_sp av0 = avma, av, tetpil;
     123             :   GEN y, c, d;
     124             :   long i, j, k, r, t, n, m;
     125             : 
     126        3351 :   n=lg(x)-1; if (!n) return cgetg(1,t_MAT);
     127        3351 :   m=nbrows(x); r=0;
     128        3351 :   x = RgM_shallowcopy(x);
     129        3351 :   c = zero_zv(m);
     130        3351 :   d=new_chunk(n+1);
     131        3351 :   av=avma;
     132       25740 :   for (k=1; k<=n; k++)
     133             :   {
     134      214573 :     for (j=1; j<=m; j++)
     135      206022 :       if (!c[j])
     136             :       {
     137      129763 :         gcoeff(x,j,k) = ff->red(E, gcoeff(x,j,k));
     138      129763 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     139             :       }
     140       22459 :     if (j>m)
     141             :     {
     142        8551 :       if (deplin)
     143             :       {
     144          70 :         GEN c = cgetg(n+1, t_COL), g0 = ff->s(E,0), g1=ff->s(E,1);
     145          70 :         for (i=1; i<k; i++) gel(c,i) = ff->red(E, gcoeff(x,d[i],k));
     146          70 :         gel(c,k) = g1; for (i=k+1; i<=n; i++) gel(c,i) = g0;
     147          70 :         return gerepileupto(av0, c);
     148             :       }
     149        8481 :       r++; d[k]=0;
     150       79222 :       for(j=1; j<k; j++)
     151       70741 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
     152             :     }
     153             :     else
     154             :     {
     155       13908 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     156       13908 :       c[j] = k; d[k] = j;
     157       13908 :       gcoeff(x,j,k) = ff->s(E,-1);
     158       13908 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     159      225941 :       for (t=1; t<=m; t++)
     160             :       {
     161      212033 :         if (t==j) continue;
     162             : 
     163      198125 :         piv = ff->red(E,gcoeff(x,t,k));
     164      198125 :         if (ff->equal0(piv)) continue;
     165             : 
     166      147320 :         gcoeff(x,t,k) = ff->s(E,0);
     167     1245785 :         for (i=k+1; i<=n; i++)
     168     1098465 :            gcoeff(x,t,i) = ff->add(E, gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     169      147320 :         if (gc_needed(av,1))
     170          66 :           gen_gerepile_gauss_ker(x,k,t,av,E,ff->red);
     171             :       }
     172             :     }
     173             :   }
     174        3281 :   if (deplin) return gc_NULL(av0);
     175             : 
     176        3232 :   tetpil=avma; y=cgetg(r+1,t_MAT);
     177       11713 :   for (j=k=1; j<=r; j++,k++)
     178             :   {
     179        8481 :     GEN C = cgetg(n+1,t_COL);
     180        8481 :     GEN g0 = ff->s(E,0), g1 = ff->s(E,1);
     181        8481 :     gel(y,j) = C; while (d[k]) k++;
     182       79222 :     for (i=1; i<k; i++)
     183       70741 :       if (d[i])
     184             :       {
     185       36321 :         GEN p1=gcoeff(x,d[i],k);
     186       36321 :         gel(C,i) = ff->red(E,p1); gunclone(p1);
     187             :       }
     188             :       else
     189       34420 :         gel(C,i) = g0;
     190        8481 :     gel(C,k) = g1; for (i=k+1; i<=n; i++) gel(C,i) = g0;
     191             :   }
     192        3232 :   return gerepile(av0,tetpil,y);
     193             : }
     194             : 
     195             : GEN
     196        2215 : gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff)
     197             : {
     198             :   pari_sp av;
     199             :   GEN c, d;
     200        2215 :   long i, j, k, r, t, m, n = lg(x)-1;
     201             : 
     202        2215 :   if (!n) { *rr = 0; return NULL; }
     203             : 
     204        2215 :   m=nbrows(x); r=0;
     205        2215 :   d = cgetg(n+1, t_VECSMALL);
     206        2215 :   x = RgM_shallowcopy(x);
     207        2215 :   c = zero_zv(m);
     208        2215 :   av=avma;
     209       12867 :   for (k=1; k<=n; k++)
     210             :   {
     211      100580 :     for (j=1; j<=m; j++)
     212       98416 :       if (!c[j])
     213             :       {
     214       66714 :         gcoeff(x,j,k) = ff->red(E,gcoeff(x,j,k));
     215       66714 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     216             :       }
     217       10652 :     if (j>m) { r++; d[k]=0; }
     218             :     else
     219             :     {
     220        8488 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     221        8488 :       GEN g0 = ff->s(E,0);
     222        8488 :       c[j] = k; d[k] = j;
     223        8488 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     224       93651 :       for (t=1; t<=m; t++)
     225             :       {
     226       85163 :         if (c[t]) continue; /* already a pivot on that line */
     227             : 
     228       55405 :         piv = ff->red(E,gcoeff(x,t,k));
     229       55405 :         if (ff->equal0(piv)) continue;
     230       29393 :         gcoeff(x,t,k) = g0;
     231      320012 :         for (i=k+1; i<=n; i++)
     232      290619 :           gcoeff(x,t,i) = ff->add(E,gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     233       29393 :         if (gc_needed(av,1))
     234          10 :           gerepile_gauss(x,k,t,av,j,c);
     235             :       }
     236        8488 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = g0; /* dummy */
     237             :     }
     238             :   }
     239        2215 :   *rr = r; set_avma((pari_sp)d); return d;
     240             : }
     241             : 
     242             : GEN
     243         413 : gen_det(GEN a, void *E, const struct bb_field *ff)
     244             : {
     245         413 :   pari_sp av = avma;
     246         413 :   long i,j,k, s = 1, nbco = lg(a)-1;
     247         413 :   GEN q, x = ff->s(E,1);
     248         413 :   if (!nbco) return x;
     249         406 :   a = RgM_shallowcopy(a);
     250        3192 :   for (i=1; i<nbco; i++)
     251             :   {
     252        6034 :     for(k=i; k<=nbco; k++)
     253             :     {
     254        5999 :       gcoeff(a,k,i) = ff->red(E,gcoeff(a,k,i));
     255        5999 :       if (!ff->equal0(gcoeff(a,k,i))) break;
     256             :     }
     257        2821 :     if (k > nbco) return gerepileupto(av, gcoeff(a,i,i));
     258        2786 :     if (k != i)
     259             :     { /* exchange the lines s.t. k = i */
     260         665 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     261         665 :       s = -s;
     262             :     }
     263        2786 :     q = gcoeff(a,i,i);
     264             : 
     265        2786 :     x = ff->red(E,ff->mul(E,x,q));
     266        2786 :     q = ff->inv(E,q);
     267       24717 :     for (k=i+1; k<=nbco; k++)
     268             :     {
     269       21931 :       GEN m = ff->red(E,gcoeff(a,i,k));
     270       21931 :       if (ff->equal0(m)) continue;
     271             : 
     272       14903 :       m = ff->neg(E, ff->mul(E,m, q));
     273      206920 :       for (j=i+1; j<=nbco; j++)
     274             :       {
     275      192017 :         gcoeff(a,j,k) = ff->add(E, gcoeff(a,j,k), ff->mul(E,m,gcoeff(a,j,i)));
     276      192017 :         if (gc_needed(av,1))
     277             :         {
     278         887 :           if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
     279         887 :           gerepileall(av,4, &a,&x,&q,&m);
     280             :         }
     281             :       }
     282             :     }
     283             :   }
     284         371 :   if (s < 0) x = ff->neg(E,x);
     285         371 :   return gerepileupto(av, ff->red(E,ff->mul(E, x, gcoeff(a,nbco,nbco))));
     286             : }
     287             : 
     288             : INLINE void
     289      878509 : _gen_addmul(GEN b, long k, long i, GEN m, void *E, const struct bb_field *ff)
     290             : {
     291      878509 :   gel(b,i) = ff->red(E,gel(b,i));
     292      878509 :   gel(b,k) = ff->add(E,gel(b,k), ff->mul(E,m, gel(b,i)));
     293      878509 : }
     294             : 
     295             : static GEN
     296       56845 : _gen_get_col(GEN a, GEN b, long li, void *E, const struct bb_field *ff)
     297             : {
     298       56845 :   GEN u = cgetg(li+1,t_COL);
     299       56845 :   pari_sp av = avma;
     300             :   long i, j;
     301             : 
     302       56845 :   gel(u,li) = gerepileupto(av, ff->red(E,ff->mul(E,gel(b,li), gcoeff(a,li,li))));
     303      313998 :   for (i=li-1; i>0; i--)
     304             :   {
     305      257153 :     pari_sp av = avma;
     306      257153 :     GEN m = gel(b,i);
     307      257153 :     for (j=i+1; j<=li; j++) m = ff->add(E,m, ff->neg(E,ff->mul(E,gcoeff(a,i,j), gel(u,j))));
     308      257153 :     m = ff->red(E, m);
     309      257153 :     gel(u,i) = gerepileupto(av, ff->red(E,ff->mul(E,m, gcoeff(a,i,i))));
     310             :   }
     311       56845 :   return u;
     312             : }
     313             : 
     314             : GEN
     315       12515 : gen_Gauss(GEN a, GEN b, void *E, const struct bb_field *ff)
     316             : {
     317             :   long i, j, k, li, bco, aco;
     318       12515 :   GEN u, g0 = ff->s(E,0);
     319       12515 :   pari_sp av = avma;
     320       12515 :   a = RgM_shallowcopy(a);
     321       12515 :   b = RgM_shallowcopy(b);
     322       12515 :   aco = lg(a)-1; bco = lg(b)-1; li = nbrows(a);
     323       55970 :   for (i=1; i<=aco; i++)
     324             :   {
     325             :     GEN invpiv;
     326       72979 :     for (k = i; k <= li; k++)
     327             :     {
     328       72881 :       GEN piv = ff->red(E,gcoeff(a,k,i));
     329       72881 :       if (!ff->equal0(piv)) { gcoeff(a,k,i) = ff->inv(E,piv); break; }
     330       17009 :       gcoeff(a,k,i) = g0;
     331             :     }
     332             :     /* found a pivot on line k */
     333       55970 :     if (k > li) return NULL;
     334       55872 :     if (k != i)
     335             :     { /* swap lines so that k = i */
     336        9154 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     337        9154 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
     338             :     }
     339       55872 :     if (i == aco) break;
     340             : 
     341       43455 :     invpiv = gcoeff(a,i,i); /* 1/piv mod p */
     342      182136 :     for (k=i+1; k<=li; k++)
     343             :     {
     344      138681 :       GEN m = ff->red(E,gcoeff(a,k,i)); gcoeff(a,k,i) = g0;
     345      138681 :       if (ff->equal0(m)) continue;
     346             : 
     347       43074 :       m = ff->red(E,ff->neg(E,ff->mul(E,m, invpiv)));
     348       43074 :       for (j=i+1; j<=aco; j++) _gen_addmul(gel(a,j),k,i,m,E,ff);
     349       43074 :       for (j=1  ; j<=bco; j++) _gen_addmul(gel(b,j),k,i,m,E,ff);
     350             :     }
     351       43455 :     if (gc_needed(av,1))
     352             :     {
     353          38 :       if(DEBUGMEM>1) pari_warn(warnmem,"gen_Gauss. i=%ld",i);
     354          38 :       gerepileall(av,2, &a,&b);
     355             :     }
     356             :   }
     357             : 
     358       12417 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
     359       12417 :   u = cgetg(bco+1,t_MAT);
     360       12417 :   for (j=1; j<=bco; j++) gel(u,j) = _gen_get_col(a, gel(b,j), aco, E, ff);
     361       12417 :   return u;
     362             : }
     363             : 
     364             : /* compatible t_MAT * t_COL, lgA = lg(A) = lg(B) > 1, l = lgcols(A) */
     365             : static GEN
     366      311115 : gen_matcolmul_i(GEN A, GEN B, ulong lgA, ulong l,
     367             :                 void *E, const struct bb_field *ff)
     368             : {
     369      311115 :   GEN C = cgetg(l, t_COL);
     370             :   ulong i;
     371     2089073 :   for (i = 1; i < l; i++) {
     372     1777958 :     pari_sp av = avma;
     373     1777958 :     GEN e = ff->mul(E, gcoeff(A, i, 1), gel(B, 1));
     374             :     ulong k;
     375    10469088 :     for(k = 2; k < lgA; k++)
     376     8691130 :       e = ff->add(E, e, ff->mul(E, gcoeff(A, i, k), gel(B, k)));
     377     1777958 :     gel(C, i) = gerepileupto(av, ff->red(E, e));
     378             :   }
     379      311115 :   return C;
     380             : }
     381             : 
     382             : GEN
     383      161336 : gen_matcolmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     384             : {
     385      161336 :   ulong lgA = lg(A);
     386      161336 :   if (lgA != (ulong)lg(B))
     387           0 :     pari_err_OP("operation 'gen_matcolmul'", A, B);
     388      161336 :   if (lgA == 1)
     389           0 :     return cgetg(1, t_COL);
     390      161336 :   return gen_matcolmul_i(A, B, lgA, lgcols(A), E, ff);
     391             : }
     392             : 
     393             : GEN
     394       15155 : gen_matmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     395             : {
     396       15155 :   ulong j, l, lgA, lgB = lg(B);
     397             :   GEN C;
     398       15155 :   if (lgB == 1)
     399           0 :     return cgetg(1, t_MAT);
     400       15155 :   lgA = lg(A);
     401       15155 :   if (lgA != (ulong)lgcols(B))
     402           0 :     pari_err_OP("operation 'gen_matmul'", A, B);
     403       15155 :   if (lgA == 1)
     404           0 :     return zeromat(0, lgB - 1);
     405       15155 :   l = lgcols(A);
     406       15155 :   C = cgetg(lgB, t_MAT);
     407      164934 :   for(j = 1; j < lgB; j++)
     408      149779 :     gel(C, j) = gen_matcolmul_i(A, gel(B, j), lgA, l, E, ff);
     409       15155 :   return C;
     410             : }
     411             : 
     412             : static GEN
     413         469 : gen_colneg(GEN A, void *E, const struct bb_field *ff)
     414             : {
     415             :   long i, l;
     416         469 :   GEN B = cgetg_copy(A, &l);
     417        7910 :   for (i = 1; i < l; i++)
     418        7441 :     gel(B, i) = ff->neg(E, gel(A, i));
     419         469 :   return B;
     420             : }
     421             : 
     422             : static GEN
     423          84 : gen_matneg(GEN A, void *E, const struct bb_field *ff)
     424             : {
     425             :   long i, l;
     426          84 :   GEN B = cgetg_copy(A, &l);
     427         553 :   for (i = 1; i < l; i++)
     428         469 :     gel(B, i) = gen_colneg(gel(A, i), E, ff);
     429          84 :   return B;
     430             : }
     431             : 
     432             : /* assume dim A >= 1, A invertible + upper triangular  */
     433             : static GEN
     434         147 : gen_matinv_upper_ind(GEN A, long index, void *E, const struct bb_field *ff)
     435             : {
     436         147 :   long n = lg(A) - 1, i, j;
     437         147 :   GEN u = cgetg(n + 1, t_COL);
     438         672 :   for (i = n; i > index; i--)
     439         525 :     gel(u, i) = ff->s(E, 0);
     440         147 :   gel(u, i) = ff->inv(E, gcoeff(A, i, i));
     441         672 :   for (i--; i > 0; i--) {
     442         525 :     pari_sp av = avma;
     443         525 :     GEN m = ff->neg(E, ff->mul(E, gcoeff(A, i, i + 1), gel(u, i + 1)));
     444        3647 :     for (j = i + 2; j <= n; j++)
     445        3122 :       m = ff->add(E, m, ff->neg(E, ff->mul(E, gcoeff(A, i, j), gel(u, j))));
     446         525 :     gel(u, i) = gerepileupto(av, ff->red(E, ff->mul(E, m, ff->inv(E, gcoeff(A, i, i)))));
     447             :   }
     448         147 :   return u;
     449             : }
     450             : 
     451             : static GEN
     452          28 : gen_matinv_upper(GEN A, void *E, const struct bb_field *ff)
     453             : {
     454             :   long i, l;
     455          28 :   GEN B = cgetg_copy(A, &l);
     456         175 :   for (i = 1; i < l; i++)
     457         147 :     gel(B,i) = gen_matinv_upper_ind(A, i, E, ff);
     458          28 :   return B;
     459             : }
     460             : 
     461             : /* find z such that A z = y. Return NULL if no solution */
     462             : GEN
     463          63 : gen_matcolinvimage(GEN A, GEN y, void *E, const struct bb_field *ff)
     464             : {
     465          63 :   pari_sp av = avma;
     466          63 :   long i, l = lg(A);
     467             :   GEN M, x, t;
     468             : 
     469          63 :   M = gen_ker(shallowconcat(A, y), 0, E, ff);
     470          63 :   i = lg(M) - 1;
     471          63 :   if (!i) return gc_NULL(av);
     472             : 
     473          63 :   x = gel(M, i);
     474          63 :   t = gel(x, l);
     475          63 :   if (ff->equal0(t)) return gc_NULL(av);
     476             : 
     477          42 :   t = ff->neg(E, ff->inv(E, t));
     478          42 :   setlg(x, l);
     479         147 :   for (i = 1; i < l; i++)
     480         105 :     gel(x, i) = ff->red(E, ff->mul(E, t, gel(x, i)));
     481          42 :   return gerepilecopy(av, x);
     482             : }
     483             : 
     484             : /* find Z such that A Z = B. Return NULL if no solution */
     485             : GEN
     486          84 : gen_matinvimage(GEN A, GEN B, void *E, const struct bb_field *ff)
     487             : {
     488          84 :   pari_sp av = avma;
     489             :   GEN d, x, X, Y;
     490             :   long i, j, nY, nA, nB;
     491          84 :   x = gen_ker(shallowconcat(gen_matneg(A, E, ff), B), 0, E, ff);
     492             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
     493             :    * We must find T such that Y T = Id_nB then X T = Z. This exists
     494             :    * iff Y has at least nB columns and full rank. */
     495          84 :   nY = lg(x) - 1;
     496          84 :   nB = lg(B) - 1;
     497          84 :   if (nY < nB) return gc_NULL(av);
     498          77 :   nA = lg(A) - 1;
     499          77 :   Y = rowslice(x, nA + 1, nA + nB); /* nB rows */
     500          77 :   d = cgetg(nB + 1, t_VECSMALL);
     501         329 :   for (i = nB, j = nY; i >= 1; i--, j--) {
     502         434 :     for (; j >= 1; j--)
     503         385 :       if (!ff->equal0(gcoeff(Y, i, j))) { d[i] = j; break; }
     504         301 :     if (!j) return gc_NULL(av);
     505             :   }
     506             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
     507          28 :   Y = vecpermute(Y, d);
     508          28 :   x = vecpermute(x, d);
     509          28 :   X = rowslice(x, 1, nA);
     510          28 :   return gerepileupto(av, gen_matmul(X, gen_matinv_upper(Y, E, ff), E, ff));
     511             : }
     512             : 
     513             : static GEN
     514       78321 : image_from_pivot(GEN x, GEN d, long r)
     515             : {
     516             :   GEN y;
     517             :   long j, k;
     518             : 
     519       78321 :   if (!d) return gcopy(x);
     520             :   /* d left on stack for efficiency */
     521       76298 :   r = lg(x)-1 - r; /* = dim Im(x) */
     522       76298 :   y = cgetg(r+1,t_MAT);
     523      689490 :   for (j=k=1; j<=r; k++)
     524      613192 :     if (d[k]) gel(y,j++) = gcopy(gel(x,k));
     525       76298 :   return y;
     526             : }
     527             : 
     528             : /*******************************************************************/
     529             : /*                                                                 */
     530             : /*                Echelon form and CUP decomposition               */
     531             : /*                                                                 */
     532             : /*******************************************************************/
     533             : 
     534             : /* By Peter Bruin, based on
     535             :   C.-P. Jeannerod, C. Pernet and A. Storjohann, Rank-profile revealing
     536             :   Gaussian elimination and the CUP matrix decomposition.  J. Symbolic
     537             :   Comput. 56 (2013), 46-68.
     538             : 
     539             :   Decompose an m x n-matrix A of rank r as C*U*P, with
     540             :   - C: m x r-matrix in column echelon form (not necessarily reduced)
     541             :        with all pivots equal to 1
     542             :   - U: upper-triangular r x n-matrix
     543             :   - P: permutation matrix
     544             :   The pivots of C and the known zeroes in C and U are not necessarily
     545             :   filled in; instead, we also return the vector R of pivot rows.
     546             :   Instead of the matrix P, we return the permutation p of [1..n]
     547             :   (t_VECSMALL) such that P[i,j] = 1 if and only if j = p[i].
     548             : */
     549             : 
     550             : /* complement of a strictly increasing subsequence of (1, 2, ..., n) */
     551             : static GEN
     552      411777 : indexcompl(GEN v, long n)
     553             : {
     554      411777 :   long i, j, k, m = lg(v) - 1;
     555      411777 :   GEN w = cgetg(n - m + 1, t_VECSMALL);
     556     7856454 :   for (i = j = k = 1; i <= n; i++)
     557     7444677 :     if (j <= m && v[j] == i) j++; else w[k++] = i;
     558      411777 :   return w;
     559             : }
     560             : 
     561             : static GEN
     562      244712 : Flm_rsolve_upper_1(GEN U, GEN B, ulong p)
     563      244712 : { return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p); }
     564             : 
     565             : static GEN
     566      741020 : Flm_rsolve_upper_2(GEN U, GEN B, ulong p)
     567             : {
     568      741020 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     569      741020 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     570      741076 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     571      741064 :   GEN B1 = rowslice(B, 1, 1);
     572      741014 :   GEN B2 = rowslice(B, 2, 2);
     573      741011 :   GEN X2 = Flm_Fl_mul(B2, dinv, p);
     574      741001 :   GEN X1 = Flm_Fl_mul(Flm_sub(B1, Flm_Fl_mul(X2, b, p), p),
     575             :                       ainv, p);
     576      741012 :   return vconcat(X1, X2);
     577             : }
     578             : 
     579             : /* solve U*X = B,  U upper triangular and invertible */
     580             : static GEN
     581     1783200 : Flm_rsolve_upper(GEN U, GEN B, ulong p)
     582             : {
     583     1783200 :   long n = lg(U) - 1, n1;
     584             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     585     1783200 :   pari_sp av = avma;
     586             : 
     587     1783200 :   if (n == 0) return B;
     588     1783193 :   if (n == 1) return Flm_rsolve_upper_1(U, B, p);
     589     1538479 :   if (n == 2) return Flm_rsolve_upper_2(U, B, p);
     590      797464 :   n1 = (n + 1)/2;
     591      797464 :   U2 = vecslice(U, n1 + 1, n);
     592      797459 :   U22 = rowslice(U2, n1 + 1, n);
     593      797444 :   B2 = rowslice(B, n1 + 1, n);
     594      797436 :   X2 = Flm_rsolve_upper(U22, B2, p);
     595      797485 :   U12 = rowslice(U2, 1, n1);
     596      797465 :   B1 = rowslice(B, 1, n1);
     597      797475 :   B1 = Flm_sub(B1, Flm_mul(U12, X2, p), p);
     598      797433 :   if (gc_needed(av, 1)) gerepileall(av, 2, &B1, &X2);
     599      797433 :   U11 = matslice(U, 1,n1, 1,n1);
     600      797438 :   X1 = Flm_rsolve_upper(U11, B1, p);
     601      797502 :   X = vconcat(X1, X2);
     602      797517 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     603      797517 :   return X;
     604             : }
     605             : 
     606             : static GEN
     607      280754 : Flm_lsolve_upper_1(GEN U, GEN B, ulong p)
     608      280754 : { return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p); }
     609             : 
     610             : static GEN
     611      819632 : Flm_lsolve_upper_2(GEN U, GEN B, ulong p)
     612             : {
     613      819632 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     614      819632 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     615      819691 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     616      819674 :   GEN B1 = vecslice(B, 1, 1);
     617      819622 :   GEN B2 = vecslice(B, 2, 2);
     618      819590 :   GEN X1 = Flm_Fl_mul(B1, ainv, p);
     619      819642 :   GEN X2 = Flm_Fl_mul(Flm_sub(B2, Flm_Fl_mul(X1, b, p), p),
     620             :                       dinv, p);
     621      819627 :   return shallowconcat(X1, X2);
     622             : }
     623             : 
     624             : /* solve X*U = B,  U upper triangular and invertible */
     625             : static GEN
     626     1865481 : Flm_lsolve_upper(GEN U, GEN B, ulong p)
     627             : {
     628     1865481 :   long n = lg(U) - 1, n1;
     629             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     630     1865481 :   pari_sp av = avma;
     631             : 
     632     1865481 :   if (n == 0) return B;
     633     1865481 :   if (n == 1) return Flm_lsolve_upper_1(U, B, p);
     634     1584724 :   if (n == 2) return Flm_lsolve_upper_2(U, B, p);
     635      765088 :   n1 = (n + 1)/2;
     636      765088 :   U2 = vecslice(U, n1 + 1, n);
     637      765058 :   U11 = matslice(U, 1,n1, 1,n1);
     638      765053 :   U12 = rowslice(U2, 1, n1);
     639      765046 :   U22 = rowslice(U2, n1 + 1, n);
     640      765046 :   B1 = vecslice(B, 1, n1);
     641      765037 :   B2 = vecslice(B, n1 + 1, n);
     642      765037 :   X1 = Flm_lsolve_upper(U11, B1, p);
     643      765068 :   B2 = Flm_sub(B2, Flm_mul(X1, U12, p), p);
     644      765060 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B2, &U22, &X1);
     645      765060 :   X2 = Flm_lsolve_upper(U22, B2, p);
     646      765084 :   X = shallowconcat(X1, X2);
     647      765080 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     648      765081 :   return X;
     649             : }
     650             : 
     651             : static GEN
     652     1409642 : Flm_rsolve_lower_unit_2(GEN L, GEN A, ulong p)
     653             : {
     654     1409642 :   GEN X1 = rowslice(A, 1, 1);
     655     1409633 :   GEN X2 = Flm_sub(rowslice(A, 2, 2),
     656     1409633 :                    Flm_Fl_mul(X1, ucoeff(L, 2, 1), p), p);
     657     1409641 :   return vconcat(X1, X2);
     658             : }
     659             : 
     660             : /* solve L*X = A,  L lower triangular with ones on the diagonal
     661             : * (at least as many rows as columns) */
     662             : static GEN
     663     3245547 : Flm_rsolve_lower_unit(GEN L, GEN A, ulong p)
     664             : {
     665     3245547 :   long m = lg(L) - 1, m1, n;
     666             :   GEN L1, L11, L21, L22, A1, A2, X1, X2, X;
     667     3245547 :   pari_sp av = avma;
     668             : 
     669     3245547 :   if (m == 0) return zero_Flm(0, lg(A) - 1);
     670     3245547 :   if (m == 1) return rowslice(A, 1, 1);
     671     2808418 :   if (m == 2) return Flm_rsolve_lower_unit_2(L, A, p);
     672     1398779 :   m1 = (m + 1)/2;
     673     1398779 :   n = nbrows(L);
     674     1398787 :   L1 = vecslice(L, 1, m1);
     675     1398768 :   L11 = rowslice(L1, 1, m1);
     676     1398727 :   L21 = rowslice(L1, m1 + 1, n);
     677     1398699 :   A1 = rowslice(A, 1, m1);
     678     1398736 :   X1 = Flm_rsolve_lower_unit(L11, A1, p);
     679     1398801 :   A2 = rowslice(A, m1 + 1, n);
     680     1398714 :   A2 = Flm_sub(A2, Flm_mul(L21, X1, p), p);
     681     1398748 :   if (gc_needed(av, 1)) gerepileall(av, 2, &A2, &X1);
     682     1398748 :   L22 = matslice(L, m1+1,n, m1+1,m);
     683     1398692 :   X2 = Flm_rsolve_lower_unit(L22, A2, p);
     684     1398762 :   X = vconcat(X1, X2);
     685     1398825 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     686     1398823 :   return X;
     687             : }
     688             : 
     689             : static GEN
     690      740662 : Flm_lsolve_lower_unit_2(GEN L, GEN A, ulong p)
     691             : {
     692      740662 :   GEN X2 = vecslice(A, 2, 2);
     693      740662 :   GEN X1 = Flm_sub(vecslice(A, 1, 1),
     694      740662 :                    Flm_Fl_mul(X2, ucoeff(L, 2, 1), p), p);
     695      740662 :   return shallowconcat(X1, X2);
     696             : }
     697             : 
     698             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
     699             : static GEN
     700     1894287 : Flm_lsolve_lower_unit(GEN L, GEN A, ulong p)
     701             : {
     702     1894287 :   long m = lg(L) - 1, m1;
     703             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
     704     1894287 :   pari_sp av = avma;
     705             : 
     706     1894287 :   if (m <= 1) return A;
     707     1615125 :   if (m == 2) return Flm_lsolve_lower_unit_2(L, A, p);
     708      874463 :   m1 = (m + 1)/2;
     709      874463 :   L2 = vecslice(L, m1 + 1, m);
     710      874463 :   L22 = rowslice(L2, m1 + 1, m);
     711      874463 :   A2 = vecslice(A, m1 + 1, m);
     712      874463 :   X2 = Flm_lsolve_lower_unit(L22, A2, p);
     713      874463 :   if (gc_needed(av, 1)) X2 = gerepilecopy(av, X2);
     714      874463 :   L1 = vecslice(L, 1, m1);
     715      874463 :   L21 = rowslice(L1, m1 + 1, m);
     716      874463 :   A1 = vecslice(A, 1, m1);
     717      874463 :   A1 = Flm_sub(A1, Flm_mul(X2, L21, p), p);
     718      874463 :   L11 = rowslice(L1, 1, m1);
     719      874463 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
     720      874463 :   X1 = Flm_lsolve_lower_unit(L11, A1, p);
     721      874463 :   X = shallowconcat(X1, X2);
     722      874463 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     723      874463 :   return X;
     724             : }
     725             : 
     726             : /* destroy A */
     727             : static long
     728     1077694 : Flm_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     729             : {
     730     1077694 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc, u, v;
     731             : 
     732     1077692 :   if (P) *P = identity_perm(n);
     733     1077685 :   *R = cgetg(m + 1, t_VECSMALL);
     734     5658923 :   for (j = 1, pr = 0; j <= n; j++)
     735             :   {
     736     7344933 :     for (pr++, pc = 0; pr <= m; pr++)
     737             :     {
     738     6839096 :       for (k = j; k <= n; k++) { v = ucoeff(A, pr, k); if (!pc && v) pc = k; }
     739     6839096 :       if (pc) break;
     740             :     }
     741     5087007 :     if (!pc) break;
     742     4581075 :     (*R)[j] = pr;
     743     4581075 :     if (pc != j)
     744             :     {
     745     1052159 :       swap(gel(A, j), gel(A, pc));
     746     1052159 :       if (P) lswap((*P)[j], (*P)[pc]);
     747             :     }
     748     4581075 :     u = Fl_inv(ucoeff(A, pr, j), p);
     749    30504555 :     for (i = pr + 1; i <= m; i++)
     750             :     {
     751    25923306 :       v = Fl_mul(ucoeff(A, i, j), u, p);
     752    25923576 :       ucoeff(A, i, j) = v;
     753   131225754 :       for (k = j + 1; k <= n; k++)
     754   210606266 :         ucoeff(A, i, k) = Fl_sub(ucoeff(A, i, k),
     755   105303133 :                                  Fl_mul(ucoeff(A, pr, k), v, p), p);
     756             :     }
     757             :   }
     758     1077848 :   setlg(*R, j);
     759     1077698 :   *C = vecslice(A, 1, j - 1);
     760     1077676 :   if (U) *U = rowpermute(A, *R);
     761     1077680 :   return j - 1;
     762             : }
     763             : 
     764             : static const long Flm_CUP_LIMIT = 8;
     765             : 
     766             : static ulong
     767      975849 : Flm_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     768             : {
     769      975849 :   long m = nbrows(A), m1, n = lg(A) - 1, i, r1, r2, r;
     770             :   GEN R1, C1, U1, P1, R2, C2, U2, P2;
     771             :   GEN A1, A2, B2, C21, U11, U12, T21, T22;
     772      975844 :   pari_sp av = avma;
     773             : 
     774      975844 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     775             :     /* destroy A; not called at the outermost recursion level */
     776      633844 :     return Flm_CUP_gauss(A, R, C, U, P, p);
     777      342000 :   m1 = (minss(m, n) + 1)/2;
     778      342002 :   A1 = rowslice(A, 1, m1);
     779      341999 :   A2 = rowslice(A, m1 + 1, m);
     780      341995 :   r1 = Flm_CUP(A1, &R1, &C1, &U1, &P1, p);
     781      342007 :   if (r1 == 0)
     782             :   {
     783        6670 :     r2 = Flm_CUP(A2, &R2, &C2, &U2, &P2, p);
     784        6670 :     *R = cgetg(r2 + 1, t_VECSMALL);
     785        6670 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
     786        6670 :     *C = vconcat(zero_Flm(m1, r2), C2);
     787        6670 :     *U = U2;
     788        6670 :     *P = P2;
     789        6670 :     r = r2;
     790             :   }
     791             :   else
     792             :   {
     793      335337 :     U11 = vecslice(U1, 1, r1);
     794      335336 :     U12 = vecslice(U1, r1 + 1, n);
     795      335334 :     T21 = vecslicepermute(A2, P1, 1, r1);
     796      335331 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
     797      335333 :     C21 = Flm_lsolve_upper(U11, T21, p);
     798      335327 :     if (gc_needed(av, 1))
     799           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
     800      335327 :     B2 = Flm_sub(T22, Flm_mul(C21, U12, p), p);
     801      335333 :     r2 = Flm_CUP(B2, &R2, &C2, &U2, &P2, p);
     802      335324 :     r = r1 + r2;
     803      335324 :     *R = cgetg(r + 1, t_VECSMALL);
     804      335327 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
     805      335327 :     for (;      i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
     806      335327 :     *C = shallowconcat(vconcat(C1, C21),
     807             :                        vconcat(zero_Flm(m1, r2), C2));
     808      335343 :     *U = shallowconcat(vconcat(U11, zero_Flm(r2, r1)),
     809             :                        vconcat(vecpermute(U12, P2), U2));
     810      335340 :     *P = cgetg(n + 1, t_VECSMALL);
     811      335332 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
     812      335332 :     for (; i <= n; i++)       (*P)[i] = P1[P2[i - r1] + r1];
     813             :   }
     814      342002 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
     815      342008 :   return r;
     816             : }
     817             : 
     818             : static ulong
     819      443822 : Flm_echelon_gauss(GEN A, GEN *R, GEN *C, ulong p)
     820      443822 : { return Flm_CUP_gauss(A, R, C, NULL, NULL, p); }
     821             : 
     822             : /* column echelon form */
     823             : static ulong
     824      746444 : Flm_echelon(GEN A, GEN *R, GEN *C, ulong p)
     825             : {
     826      746444 :   long j, j1, j2, m = nbrows(A), n = lg(A) - 1, n1, r, r1, r2;
     827             :   GEN A1, A2, R1, R1c, C1, R2, C2;
     828             :   GEN A12, A22, B2, C11, C21, M12;
     829      746444 :   pari_sp av = avma;
     830             : 
     831      746444 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     832      443822 :     return Flm_echelon_gauss(Flm_copy(A), R, C, p);
     833             : 
     834      302622 :   n1 = (n + 1)/2;
     835      302622 :   A1 = vecslice(A, 1, n1);
     836      302622 :   A2 = vecslice(A, n1 + 1, n);
     837      302622 :   r1 = Flm_echelon(A1, &R1, &C1, p);
     838      302622 :   if (!r1) return Flm_echelon(A2, R, C, p);
     839      263955 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
     840             : 
     841      259766 :   R1c = indexcompl(R1, m);
     842      259766 :   C11 = rowpermute(C1, R1);
     843      259766 :   C21 = rowpermute(C1, R1c);
     844      259766 :   A12 = rowpermute(A2, R1);
     845      259766 :   A22 = rowpermute(A2, R1c);
     846      259766 :   M12 = Flm_rsolve_lower_unit(C11, A12, p);
     847      259766 :   B2 = Flm_sub(A22, Flm_mul(C21, M12, p), p);
     848      259766 :   r2 = Flm_echelon(B2, &R2, &C2, p);
     849      259766 :   if (!r2) { *R = R1; *C = C1; r = r1; }
     850             :   else
     851             :   {
     852      245078 :     R2 = perm_mul(R1c, R2);
     853      245078 :     C2 = rowpermute(vconcat(zero_Flm(r1, r2), C2),
     854             :                     perm_inv(vecsmall_concat(R1, R1c)));
     855      245078 :     r = r1 + r2;
     856      245078 :     *R = cgetg(r + 1, t_VECSMALL);
     857      245078 :     *C = cgetg(r + 1, t_MAT);
     858     2979298 :     for (j = j1 = j2 = 1; j <= r; j++)
     859     2734220 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
     860             :       {
     861     1423516 :         gel(*C, j) = gel(C1, j1);
     862     1423516 :         (*R)[j] = R1[j1++];
     863             :       }
     864             :       else
     865             :       {
     866     1310704 :         gel(*C, j) = gel(C2, j2);
     867     1310704 :         (*R)[j] = R2[j2++];
     868             :       }
     869             :   }
     870      259766 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
     871      259766 :   return r;
     872             : }
     873             : 
     874             : static GEN
     875         140 : FlxqM_rsolve_upper_1(GEN U, GEN B, GEN T, ulong p)
     876         140 : { return FlxqM_Flxq_mul(B, Flxq_inv(gcoeff(U, 1, 1), T, p), T, p);
     877             : }
     878             : 
     879             : static GEN
     880         168 : FlxqM_rsolve_upper_2(GEN U, GEN B, GEN T, ulong p)
     881             : {
     882         168 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     883         168 :   GEN D = Flxq_mul(a, d, T, p), Dinv = Flxq_inv(D, T, p);
     884         168 :   GEN ainv = Flxq_mul(d, Dinv, T, p), dinv = Flxq_mul(a, Dinv, T, p);
     885         168 :   GEN B1 = rowslice(B, 1, 1);
     886         168 :   GEN B2 = rowslice(B, 2, 2);
     887         168 :   GEN X2 = FlxqM_Flxq_mul(B2, dinv, T, p);
     888         168 :   GEN X1 = FlxqM_Flxq_mul(FlxM_sub(B1, FlxqM_Flxq_mul(X2, b, T, p), p),
     889             :                           ainv, T, p);
     890         168 :   return vconcat(X1, X2);
     891             : }
     892             : 
     893             : /* solve U*X = B,  U upper triangular and invertible */
     894             : static GEN
     895         588 : FlxqM_rsolve_upper(GEN U, GEN B, GEN T, ulong p)
     896             : {
     897         588 :   long n = lg(U) - 1, n1;
     898             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     899         588 :   pari_sp av = avma;
     900             : 
     901         588 :   if (n == 0) return B;
     902         588 :   if (n == 1) return FlxqM_rsolve_upper_1(U, B, T, p);
     903         448 :   if (n == 2) return FlxqM_rsolve_upper_2(U, B, T, p);
     904         280 :   n1 = (n + 1)/2;
     905         280 :   U2 = vecslice(U, n1 + 1, n);
     906         280 :   U11 = matslice(U, 1,n1, 1,n1);
     907         280 :   U12 = rowslice(U2, 1, n1);
     908         280 :   U22 = rowslice(U2, n1 + 1, n);
     909         280 :   B1 = rowslice(B, 1, n1);
     910         280 :   B2 = rowslice(B, n1 + 1, n);
     911         280 :   X2 = FlxqM_rsolve_upper(U22, B2, T, p);
     912         280 :   B1 = FlxM_sub(B1, FlxqM_mul(U12, X2, T, p), p);
     913         280 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B1, &U11, &X2);
     914         280 :   X1 = FlxqM_rsolve_upper(U11, B1, T, p);
     915         280 :   X = vconcat(X1, X2);
     916         280 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     917         280 :   return X;
     918             : }
     919             : 
     920             : static GEN
     921         469 : FlxqM_lsolve_upper_1(GEN U, GEN B, GEN T, ulong p)
     922         469 : { return FlxqM_Flxq_mul(B, Flxq_inv(gcoeff(U, 1, 1), T, p), T, p); }
     923             : 
     924             : static GEN
     925         728 : FlxqM_lsolve_upper_2(GEN U, GEN B, GEN T, ulong p)
     926             : {
     927         728 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     928         728 :   GEN D = Flxq_mul(a, d, T, p), Dinv = Flxq_inv(D, T, p);
     929         728 :   GEN ainv = Flxq_mul(d, Dinv, T, p), dinv = Flxq_mul(a, Dinv, T, p);
     930         728 :   GEN B1 = vecslice(B, 1, 1);
     931         728 :   GEN B2 = vecslice(B, 2, 2);
     932         728 :   GEN X1 = FlxqM_Flxq_mul(B1, ainv, T, p);
     933         728 :   GEN X2 = FlxqM_Flxq_mul(FlxM_sub(B2, FlxqM_Flxq_mul(X1, b, T, p), p),
     934             :                           dinv, T, p);
     935         728 :   return shallowconcat(X1, X2);
     936             : }
     937             : 
     938             : /* solve X*U = B,  U upper triangular and invertible */
     939             : static GEN
     940        1869 : FlxqM_lsolve_upper(GEN U, GEN B, GEN T, ulong p)
     941             : {
     942        1869 :   long n = lg(U) - 1, n1;
     943             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     944        1869 :   pari_sp av = avma;
     945             : 
     946        1869 :   if (n == 0) return B;
     947        1869 :   if (n == 1) return FlxqM_lsolve_upper_1(U, B, T, p);
     948        1400 :   if (n == 2) return FlxqM_lsolve_upper_2(U, B, T, p);
     949         672 :   n1 = (n + 1)/2;
     950         672 :   U2 = vecslice(U, n1 + 1, n);
     951         672 :   U11 = matslice(U, 1,n1, 1,n1);
     952         672 :   U12 = rowslice(U2, 1, n1);
     953         672 :   U22 = rowslice(U2, n1 + 1, n);
     954         672 :   B1 = vecslice(B, 1, n1);
     955         672 :   B2 = vecslice(B, n1 + 1, n);
     956         672 :   X1 = FlxqM_lsolve_upper(U11, B1, T, p);
     957         672 :   B2 = FlxM_sub(B2, FlxqM_mul(X1, U12, T, p), p);
     958         672 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B2, &U22, &X1);
     959         672 :   X2 = FlxqM_lsolve_upper(U22, B2, T, p);
     960         672 :   X = shallowconcat(X1, X2);
     961         672 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     962         672 :   return X;
     963             : }
     964             : 
     965             : static GEN
     966        6069 : FlxqM_rsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
     967             : {
     968        6069 :   GEN X1 = rowslice(A, 1, 1);
     969        6069 :   GEN X2 = FlxM_sub(rowslice(A, 2, 2),
     970        6069 :                     FlxqM_Flxq_mul(X1, gcoeff(L, 2, 1), T, p), p);
     971        6069 :   return vconcat(X1, X2);
     972             : }
     973             : 
     974             : /* solve L*X = A,  L lower triangular with ones on the diagonal
     975             :  * (at least as many rows as columns) */
     976             : static GEN
     977       13783 : FlxqM_rsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
     978             : {
     979       13783 :   long m = lg(L) - 1, m1, n;
     980             :   GEN L1, L11, L21, L22, A1, A2, X1, X2, X;
     981       13783 :   pari_sp av = avma;
     982             : 
     983       13783 :   if (m == 0) return zeromat(0, lg(A) - 1);
     984       13783 :   if (m == 1) return rowslice(A, 1, 1);
     985       10542 :   if (m == 2) return FlxqM_rsolve_lower_unit_2(L, A, T, p);
     986        4473 :   m1 = (m + 1)/2;
     987        4473 :   n = nbrows(L);
     988        4473 :   L1 = vecslice(L, 1, m1);
     989        4473 :   L11 = rowslice(L1, 1, m1);
     990        4473 :   L21 = rowslice(L1, m1 + 1, n);
     991        4473 :   A1 = rowslice(A, 1, m1);
     992        4473 :   A2 = rowslice(A, m1 + 1, n);
     993        4473 :   X1 = FlxqM_rsolve_lower_unit(L11, A1, T, p);
     994        4473 :   A2 = FlxM_sub(A2, FlxqM_mul(L21, X1, T, p), p);
     995        4473 :   if (gc_needed(av, 1)) gerepileall(av, 2, &A2, &X1);
     996        4473 :   L22 = matslice(L, m1+1,n, m1+1,m);
     997        4473 :   X2 = FlxqM_rsolve_lower_unit(L22, A2, T, p);
     998        4473 :   X = vconcat(X1, X2);
     999        4473 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1000        4473 :   return X;
    1001             : }
    1002             : 
    1003             : static GEN
    1004        2478 : FlxqM_lsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
    1005             : {
    1006        2478 :   GEN X2 = vecslice(A, 2, 2);
    1007        2478 :   GEN X1 = FlxM_sub(vecslice(A, 1, 1),
    1008        2478 :                     FlxqM_Flxq_mul(X2, gcoeff(L, 2, 1), T, p), p);
    1009        2478 :   return shallowconcat(X1, X2);
    1010             : }
    1011             : 
    1012             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
    1013             : static GEN
    1014        6391 : FlxqM_lsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
    1015             : {
    1016        6391 :   long m = lg(L) - 1, m1;
    1017             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
    1018        6391 :   pari_sp av = avma;
    1019             : 
    1020        6391 :   if (m <= 1) return A;
    1021        4760 :   if (m == 2) return FlxqM_lsolve_lower_unit_2(L, A, T, p);
    1022        2282 :   m1 = (m + 1)/2;
    1023        2282 :   L1 = vecslice(L, 1, m1);
    1024        2282 :   L2 = vecslice(L, m1 + 1, m);
    1025        2282 :   L11 = rowslice(L1, 1, m1);
    1026        2282 :   L21 = rowslice(L1, m1 + 1, m);
    1027        2282 :   L22 = rowslice(L2, m1 + 1, m);
    1028        2282 :   A1 = vecslice(A, 1, m1);
    1029        2282 :   A2 = vecslice(A, m1 + 1, m);
    1030        2282 :   X2 = FlxqM_lsolve_lower_unit(L22, A2, T, p);
    1031        2282 :   A1 = FlxM_sub(A1, FlxqM_mul(X2, L21, T, p), p);
    1032        2282 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
    1033        2282 :   X1 = FlxqM_lsolve_lower_unit(L11, A1, T, p);
    1034        2282 :   X = shallowconcat(X1, X2);
    1035        2282 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1036        2282 :   return X;
    1037             : }
    1038             : 
    1039             : /* destroy A */
    1040             : static long
    1041        7819 : FlxqM_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, GEN T, ulong p)
    1042             : {
    1043        7819 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc;
    1044             :   pari_sp av;
    1045             :   GEN u, v;
    1046             : 
    1047        7819 :   if (P) *P = identity_perm(n);
    1048        7819 :   *R = cgetg(m + 1, t_VECSMALL);
    1049        7819 :   av = avma;
    1050       16170 :   for (j = 1, pr = 0; j <= n; j++)
    1051             :   {
    1052       42091 :     for (pr++, pc = 0; pr <= m; pr++)
    1053             :     {
    1054      167937 :       for (k = j; k <= n; k++)
    1055             :       {
    1056      132454 :         v = Flx_rem(gcoeff(A, pr, k), T, p);
    1057      132454 :         gcoeff(A, pr, k) = v;
    1058      132454 :         if (!pc && lgpol(v) > 0) pc = k;
    1059             :       }
    1060       35483 :       if (pc) break;
    1061             :     }
    1062       14959 :     if (!pc) break;
    1063        8351 :     (*R)[j] = pr;
    1064        8351 :     if (pc != j)
    1065             :     {
    1066        1141 :       swap(gel(A, j), gel(A, pc));
    1067        1141 :       if (P) lswap((*P)[j], (*P)[pc]);
    1068             :     }
    1069        8351 :     u = Flxq_inv(gcoeff(A, pr, j), T, p);
    1070       49490 :     for (i = pr + 1; i <= m; i++)
    1071             :     {
    1072       41139 :       v = Flxq_mul(gcoeff(A, i, j), u, T, p);
    1073       41139 :       gcoeff(A, i, j) = v;
    1074      112910 :       for (k = j + 1; k <= n; k++)
    1075      143542 :         gcoeff(A, i, k) = Flx_sub(gcoeff(A, i, k),
    1076       71771 :                                   Flx_mul(gcoeff(A, pr, k), v, p), p);
    1077             :     }
    1078        8351 :     if (gc_needed(av, 2)) A = gerepilecopy(av, A);
    1079             :   }
    1080        7819 :   setlg(*R, j);
    1081        7819 :   *C = vecslice(A, 1, j - 1);
    1082        7819 :   if (U) *U = rowpermute(A, *R);
    1083        7819 :   return j - 1;
    1084             : }
    1085             : 
    1086             : static const long FlxqM_CUP_LIMIT = 5;
    1087             : 
    1088             : static ulong
    1089        1253 : FlxqM_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, GEN T, ulong p)
    1090             : {
    1091        1253 :   long m = nbrows(A), m1, n = lg(A) - 1, i, r1, r2, r, sv;
    1092             :   GEN R1, C1, U1, P1, R2, C2, U2, P2;
    1093             :   GEN A1, A2, B2, C21, U11, U12, T21, T22;
    1094        1253 :   pari_sp av = avma;
    1095             : 
    1096        1253 :   if (m < FlxqM_CUP_LIMIT || n < FlxqM_CUP_LIMIT)
    1097             :     /* destroy A; not called at the outermost recursion level */
    1098         672 :     return FlxqM_CUP_gauss(A, R, C, U, P, T, p);
    1099         581 :   sv = get_Flx_var(T);
    1100         581 :   m1 = (minss(m, n) + 1)/2;
    1101         581 :   A1 = rowslice(A, 1, m1);
    1102         581 :   A2 = rowslice(A, m1 + 1, m);
    1103         581 :   r1 = FlxqM_CUP(A1, &R1, &C1, &U1, &P1, T, p);
    1104         581 :   if (r1 == 0)
    1105             :   {
    1106          56 :     r2 = FlxqM_CUP(A2, &R2, &C2, &U2, &P2, T, p);
    1107          56 :     *R = cgetg(r2 + 1, t_VECSMALL);
    1108          56 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
    1109          56 :     *C = vconcat(zero_FlxM(m1, r2, sv), C2);
    1110          56 :     *U = U2;
    1111          56 :     *P = P2;
    1112          56 :     r = r2;
    1113             :   }
    1114             :   else
    1115             :   {
    1116         525 :     U11 = vecslice(U1, 1, r1);
    1117         525 :     U12 = vecslice(U1, r1 + 1, n);
    1118         525 :     T21 = vecslicepermute(A2, P1, 1, r1);
    1119         525 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
    1120         525 :     C21 = FlxqM_lsolve_upper(U11, T21, T, p);
    1121         525 :     if (gc_needed(av, 1))
    1122           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
    1123         525 :     B2 = FlxM_sub(T22, FlxqM_mul(C21, U12, T, p), p);
    1124         525 :     r2 = FlxqM_CUP(B2, &R2, &C2, &U2, &P2, T, p);
    1125         525 :     r = r1 + r2;
    1126         525 :     *R = cgetg(r + 1, t_VECSMALL);
    1127         525 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
    1128         525 :     for (     ; i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
    1129         525 :     *C = shallowmatconcat(mkmat2(mkcol2(C1, C21),
    1130             :                                  mkcol2(zero_FlxM(m1, r2, sv), C2)));
    1131         525 :     *U = shallowmatconcat(mkmat2(mkcol2(U11, zero_FlxM(r2, r1, sv)),
    1132             :                                  mkcol2(vecpermute(U12, P2), U2)));
    1133         525 :     *P = cgetg(n + 1, t_VECSMALL);
    1134         525 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
    1135         525 :     for (     ; i <= n; i++)  (*P)[i] = P1[P2[i - r1] + r1];
    1136             :   }
    1137         581 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
    1138         581 :   return r;
    1139             : }
    1140             : 
    1141             : static ulong
    1142        7147 : FlxqM_echelon_gauss(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1143        7147 : { return FlxqM_CUP_gauss(A, R, C, NULL, NULL, T, p); }
    1144             : 
    1145             : /* column echelon form */
    1146             : static ulong
    1147       12537 : FlxqM_echelon(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1148             : {
    1149       12537 :   long j, j1, j2, m = nbrows(A), n = lg(A) - 1, n1, r, r1, r2;
    1150             :   GEN A1, A2, R1, R1c, C1, R2, C2;
    1151             :   GEN A12, A22, B2, C11, C21, M12;
    1152       12537 :   pari_sp av = avma;
    1153             : 
    1154       12537 :   if (m < FlxqM_CUP_LIMIT || n < FlxqM_CUP_LIMIT)
    1155        7147 :     return FlxqM_echelon_gauss(shallowcopy(A), R, C, T, p);
    1156             : 
    1157        5390 :   n1 = (n + 1)/2;
    1158        5390 :   A1 = vecslice(A, 1, n1);
    1159        5390 :   A2 = vecslice(A, n1 + 1, n);
    1160        5390 :   r1 = FlxqM_echelon(A1, &R1, &C1, T, p);
    1161        5390 :   if (!r1) return FlxqM_echelon(A2, R, C, T, p);
    1162        4893 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
    1163        4795 :   R1c = indexcompl(R1, m);
    1164        4795 :   C11 = rowpermute(C1, R1);
    1165        4795 :   C21 = rowpermute(C1, R1c);
    1166        4795 :   A12 = rowpermute(A2, R1);
    1167        4795 :   A22 = rowpermute(A2, R1c);
    1168        4795 :   M12 = FlxqM_rsolve_lower_unit(C11, A12, T, p);
    1169        4795 :   B2 = FlxM_sub(A22, FlxqM_mul(C21, M12, T, p), p);
    1170        4795 :   r2 = FlxqM_echelon(B2, &R2, &C2, T, p);
    1171        4795 :   if (!r2) { *R = R1; *C = C1; r = r1; }
    1172             :   else
    1173             :   {
    1174        1904 :     R2 = perm_mul(R1c, R2);
    1175        1904 :     C2 = rowpermute(vconcat(zero_FlxM(r1, r2, get_Flx_var(T)), C2),
    1176             :                     perm_inv(vecsmall_concat(R1, R1c)));
    1177        1904 :     r = r1 + r2;
    1178        1904 :     *R = cgetg(r + 1, t_VECSMALL);
    1179        1904 :     *C = cgetg(r + 1, t_MAT);
    1180       12040 :     for (j = j1 = j2 = 1; j <= r; j++)
    1181             :     {
    1182       10136 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
    1183             :       {
    1184        6181 :         gel(*C, j) = gel(C1, j1);
    1185        6181 :         (*R)[j] = R1[j1++];
    1186             :       }
    1187             :       else
    1188             :       {
    1189        3955 :         gel(*C, j) = gel(C2, j2);
    1190        3955 :         (*R)[j] = R2[j2++];
    1191             :       }
    1192             :     }
    1193             :   }
    1194        4795 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
    1195        4795 :   return r;
    1196             : }
    1197             : 
    1198             : /*******************************************************************/
    1199             : /*                                                                 */
    1200             : /*                    LINEAR ALGEBRA MODULO P                      */
    1201             : /*                                                                 */
    1202             : /*******************************************************************/
    1203             : 
    1204             : static long
    1205     2403775 : F2v_find_nonzero(GEN x0, GEN mask0, long m)
    1206             : {
    1207     2403775 :   ulong *x = (ulong *)x0+2, *mask = (ulong *)mask0+2, e;
    1208     2403775 :   long i, l = lg(x0)-2;
    1209     4007057 :   for (i = 0; i < l; i++)
    1210             :   {
    1211     3440843 :     e = *x++ & *mask++;
    1212     3440843 :     if (e) return i*BITS_IN_LONG+vals(e)+1;
    1213             :   }
    1214      566214 :   return m+1;
    1215             : }
    1216             : 
    1217             : /* in place, destroy x */
    1218             : GEN
    1219      304758 : F2m_ker_sp(GEN x, long deplin)
    1220             : {
    1221             :   GEN y, c, d;
    1222             :   long i, j, k, r, m, n;
    1223             : 
    1224      304758 :   n = lg(x)-1;
    1225      304758 :   m = mael(x,1,1); r=0;
    1226             : 
    1227      304758 :   d = cgetg(n+1, t_VECSMALL);
    1228      304758 :   c = const_F2v(m);
    1229     2392101 :   for (k=1; k<=n; k++)
    1230             :   {
    1231     2113356 :     GEN xk = gel(x,k);
    1232     2113356 :     j = F2v_find_nonzero(xk, c, m);
    1233     2113368 :     if (j>m)
    1234             :     {
    1235      457111 :       if (deplin) {
    1236       26013 :         GEN c = zero_F2v(n);
    1237      111573 :         for (i=1; i<k; i++)
    1238       85560 :           if (F2v_coeff(xk, d[i]))
    1239       43234 :             F2v_set(c, i);
    1240       26013 :         F2v_set(c, k);
    1241       26013 :         return c;
    1242             :       }
    1243      431098 :       r++; d[k] = 0;
    1244             :     }
    1245             :     else
    1246             :     {
    1247     1656257 :       F2v_clear(c,j); d[k] = j;
    1248     1656256 :       F2v_clear(xk, j);
    1249    73607137 :       for (i=k+1; i<=n; i++)
    1250             :       {
    1251    71950880 :         GEN xi = gel(x,i);
    1252    71950880 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1253             :       }
    1254     1656257 :       F2v_set(xk, j);
    1255             :     }
    1256             :   }
    1257      278745 :   if (deplin) return NULL;
    1258             : 
    1259      277310 :   y = zero_F2m_copy(n,r);
    1260      708409 :   for (j=k=1; j<=r; j++,k++)
    1261             :   {
    1262      431099 :     GEN C = gel(y,j); while (d[k]) k++;
    1263    12614971 :     for (i=1; i<k; i++)
    1264    12183872 :       if (d[i] && F2m_coeff(x,d[i],k))
    1265     4462751 :         F2v_set(C,i);
    1266      431099 :     F2v_set(C, k);
    1267             :   }
    1268      277310 :   return y;
    1269             : }
    1270             : 
    1271             : static void /* assume m < p */
    1272     6524576 : _Fl_addmul(GEN b, long k, long i, ulong m, ulong p, ulong pi)
    1273             : {
    1274     6524576 :   uel(b,k) = Fl_addmul_pre(uel(b, k), m, uel(b, i), p, pi);
    1275     6524576 : }
    1276             : static void /* same m = 1 */
    1277      208659 : _Fl_add(GEN b, long k, long i, ulong p)
    1278             : {
    1279      208659 :   uel(b,k) = Fl_add(uel(b,k), uel(b,i), p);
    1280      208659 : }
    1281             : static void /* assume m < p && SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1282    14868771 : _Fl_addmul_OK(GEN b, long k, long i, ulong m, ulong p)
    1283             : {
    1284    14868771 :   uel(b,k) += m * uel(b,i);
    1285    14868771 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1286    14868771 : }
    1287             : static void /* assume SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1288     2811391 : _Fl_add_OK(GEN b, long k, long i, ulong p)
    1289             : {
    1290     2811391 :   uel(b,k) += uel(b,i);
    1291     2811391 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1292     2811391 : }
    1293             : 
    1294             : static GEN
    1295      116439 : Flm_ker_gauss_OK(GEN x, ulong p, long deplin)
    1296             : {
    1297             :   GEN y, c, d;
    1298             :   long i, j, k, r, t, m, n;
    1299             :   ulong a;
    1300             : 
    1301      116439 :   n = lg(x)-1;
    1302      116439 :   m=nbrows(x); r=0;
    1303             : 
    1304      116439 :   c = zero_zv(m);
    1305      116439 :   d = cgetg(n+1, t_VECSMALL);
    1306      116439 :   a = 0; /* for gcc -Wall */
    1307      549975 :   for (k=1; k<=n; k++)
    1308             :   {
    1309     1849333 :     for (j=1; j<=m; j++)
    1310     1642738 :       if (!c[j])
    1311             :       {
    1312     1036690 :         a = ucoeff(x,j,k) % p;
    1313     1036690 :         if (a) break;
    1314             :       }
    1315      452716 :     if (j > m)
    1316             :     {
    1317      206595 :       if (deplin==1) {
    1318       19180 :         c = cgetg(n+1, t_VECSMALL);
    1319       19180 :         for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k) % p;
    1320       19180 :         c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
    1321       19180 :         return c;
    1322             :       }
    1323      187415 :       r++; d[k] = 0;
    1324             :     }
    1325             :     else
    1326             :     {
    1327      246121 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1328      246121 :       c[j] = k; d[k] = j;
    1329      246121 :       ucoeff(x,j,k) = p-1;
    1330      246121 :       if (piv != 1)
    1331      163575 :         for (i=k+1; i<=n; i++) ucoeff(x,j,i) = (piv * ucoeff(x,j,i)) % p;
    1332     1559314 :       for (t=1; t<=m; t++)
    1333             :       {
    1334     1313193 :         if (t == j) continue;
    1335             : 
    1336     1067072 :         piv = ( ucoeff(x,t,k) %= p );
    1337     1067072 :         if (!piv) continue;
    1338      489331 :         if (piv == 1)
    1339      130217 :           for (i=k+1; i<=n; i++) _Fl_add_OK(gel(x,i),t,j, p);
    1340             :         else
    1341      359114 :           for (i=k+1; i<=n; i++) _Fl_addmul_OK(gel(x,i),t,j,piv, p);
    1342             :       }
    1343             :     }
    1344             :   }
    1345       97259 :   if (deplin==1) return NULL;
    1346             : 
    1347       97252 :   y = cgetg(r+1, t_MAT);
    1348      284667 :   for (j=k=1; j<=r; j++,k++)
    1349             :   {
    1350      187415 :     GEN C = cgetg(n+1, t_VECSMALL);
    1351             : 
    1352      187415 :     gel(y,j) = C; while (d[k]) k++;
    1353      655439 :     for (i=1; i<k; i++)
    1354      468024 :       if (d[i])
    1355      285856 :         uel(C,i) = ucoeff(x,d[i],k) % p;
    1356             :       else
    1357      182168 :         uel(C,i) = 0UL;
    1358      187415 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1359             :   }
    1360       97252 :   if (deplin == 2) {
    1361           0 :     GEN pc = cgetg(n - r + 1, t_VECSMALL);  /* indices of pivot columns */
    1362           0 :     for (i = j = 1; j <= n; j++)
    1363           0 :       if (d[j]) pc[i++] = j;
    1364           0 :     return mkvec2(y, pc);
    1365             :   }
    1366       97252 :   return y;
    1367             : }
    1368             : 
    1369             : /* in place, destroy x */
    1370             : static GEN
    1371      236727 : Flm_ker_gauss(GEN x, ulong p, long deplin)
    1372             : {
    1373             :   GEN y, c, d;
    1374             :   long i, j, k, r, t, m, n;
    1375             :   ulong a, pi;
    1376      236727 :   n = lg(x)-1;
    1377      236727 :   if (!n) return cgetg(1,t_MAT);
    1378      236559 :   if (SMALL_ULONG(p)) return Flm_ker_gauss_OK(x, p, deplin);
    1379      120120 :   pi = get_Fl_red(p);
    1380             : 
    1381      120120 :   m=nbrows(x); r=0;
    1382             : 
    1383      120120 :   c = zero_zv(m);
    1384      120120 :   d = cgetg(n+1, t_VECSMALL);
    1385      120120 :   a = 0; /* for gcc -Wall */
    1386      393382 :   for (k=1; k<=n; k++)
    1387             :   {
    1388      818782 :     for (j=1; j<=m; j++)
    1389      714799 :       if (!c[j])
    1390             :       {
    1391      561486 :         a = ucoeff(x,j,k);
    1392      561486 :         if (a) break;
    1393             :       }
    1394      273269 :     if (j > m)
    1395             :     {
    1396      103983 :       if (deplin==1) {
    1397           7 :         c = cgetg(n+1, t_VECSMALL);
    1398           7 :         for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k);
    1399           7 :         c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
    1400           7 :         return c;
    1401             :       }
    1402      103976 :       r++; d[k] = 0;
    1403             :     }
    1404             :     else
    1405             :     {
    1406      169286 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1407      169286 :       c[j] = k; d[k] = j;
    1408      169286 :       ucoeff(x,j,k) = p-1;
    1409      169286 :       if (piv != 1)
    1410      329998 :         for (i=k+1; i<=n; i++)
    1411      162635 :           ucoeff(x,j,i) = Fl_mul_pre(piv, ucoeff(x,j,i), p, pi);
    1412      744052 :       for (t=1; t<=m; t++)
    1413             :       {
    1414      574766 :         if (t == j) continue;
    1415             : 
    1416      405480 :         piv = ucoeff(x,t,k);
    1417      405480 :         if (!piv) continue;
    1418      218908 :         if (piv == 1)
    1419        8976 :           for (i=k+1; i<=n; i++) _Fl_add(gel(x,i),t,j,p);
    1420             :         else
    1421      209932 :           for (i=k+1; i<=n; i++) _Fl_addmul(gel(x,i),t,j,piv,p, pi);
    1422             :       }
    1423             :     }
    1424             :   }
    1425      120113 :   if (deplin==1) return NULL;
    1426             : 
    1427      120106 :   y = cgetg(r+1, t_MAT);
    1428      224082 :   for (j=k=1; j<=r; j++,k++)
    1429             :   {
    1430      103976 :     GEN C = cgetg(n+1, t_VECSMALL);
    1431             : 
    1432      103976 :     gel(y,j) = C; while (d[k]) k++;
    1433      222731 :     for (i=1; i<k; i++)
    1434      118755 :       if (d[i])
    1435       80056 :         uel(C,i) = ucoeff(x,d[i],k);
    1436             :       else
    1437       38699 :         uel(C,i) = 0UL;
    1438      103976 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1439             :   }
    1440      120106 :   if (deplin == 2) {
    1441      116534 :     GEN pc = cgetg(n - r + 1, t_VECSMALL);  /* indices of pivot columns */
    1442      366972 :     for (i = j = 1; j <= n; j++)
    1443      250438 :       if (d[j]) pc[i++] = j;
    1444      116534 :     return mkvec2(y, pc);
    1445             :   }
    1446        3572 :   return y;
    1447             : }
    1448             : 
    1449             : GEN
    1450       74415 : FpM_intersect(GEN x, GEN y, GEN p)
    1451             : {
    1452       74415 :   pari_sp av = avma;
    1453       74415 :   long j, lx = lg(x);
    1454             :   GEN z;
    1455             : 
    1456       74415 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    1457       74415 :   z = FpM_ker(shallowconcat(x,y), p);
    1458       74415 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    1459       74415 :   return gerepileupto(av, FpM_mul(x,z,p));
    1460             : }
    1461             : GEN
    1462           0 : Flm_intersect(GEN x, GEN y, ulong p)
    1463             : {
    1464           0 :   pari_sp av = avma;
    1465           0 :   long j, lx = lg(x);
    1466             :   GEN z;
    1467             : 
    1468           0 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    1469           0 :   z = Flm_ker(shallowconcat(x,y), p);
    1470           0 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    1471           0 :   return gerepileupto(av, Flm_mul(x,z,p));
    1472             : }
    1473             : 
    1474             : /* not memory clean */
    1475             : GEN
    1476          92 : F2m_ker(GEN x) { return F2m_ker_sp(F2m_copy(x), 0); }
    1477             : GEN
    1478           0 : F2m_deplin(GEN x) { return F2m_ker_sp(F2m_copy(x), 1); }
    1479             : 
    1480             : static GEN
    1481      121802 : Flm_ker_echelon(GEN x, ulong p, long pivots) {
    1482      121802 :   pari_sp av = avma;
    1483             :   GEN R, Rc, C, C1, C2, S, K;
    1484      121802 :   long n = lg(x) - 1, r;
    1485      121802 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1486      121802 :   Rc = indexcompl(R, n);
    1487      121802 :   C1 = rowpermute(C, R);
    1488      121802 :   C2 = rowpermute(C, Rc);
    1489      121802 :   S = Flm_lsolve_lower_unit(C1, C2, p);
    1490      121802 :   K = vecpermute(shallowconcat(Flm_neg(S, p), matid_Flm(n - r)),
    1491             :                  perm_inv(vecsmall_concat(R, Rc)));
    1492      121802 :   K = Flm_transpose(K);
    1493      121802 :   if (pivots)
    1494        4973 :     return gerepilecopy(av, mkvec2(K, R));
    1495      116829 :   return gerepileupto(av, K);
    1496             : }
    1497             : 
    1498             : static GEN
    1499       23552 : Flm_deplin_echelon(GEN x, ulong p) {
    1500       23552 :   pari_sp av = avma;
    1501             :   GEN R, Rc, C, C1, C2, s, v;
    1502       23552 :   long i, n = lg(x) - 1, r;
    1503       23552 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1504       23552 :   if (r == n) return gc_NULL(av);
    1505       23545 :   Rc = indexcompl(R, n);
    1506       23545 :   i = Rc[1];
    1507       23545 :   C1 = rowpermute(C, R);
    1508       23545 :   C2 = rowslice(C, i, i);
    1509       23545 :   s = Flm_row(Flm_lsolve_lower_unit(C1, C2, p), 1);
    1510       23545 :   v = vecsmallpermute(vecsmall_concat(Flv_neg(s, p), vecsmall_ei(n - r, 1)),
    1511             :                  perm_inv(vecsmall_concat(R, Rc)));
    1512       23545 :   return gerepileuptoleaf(av, v);
    1513             : }
    1514             : 
    1515             : static GEN
    1516      382081 : Flm_ker_i(GEN x, ulong p, long deplin, long inplace) {
    1517      382081 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1518      145354 :     switch(deplin) {
    1519      116829 :     case 0: return Flm_ker_echelon(x, p, 0);
    1520       23552 :     case 1: return Flm_deplin_echelon(x, p);
    1521        4973 :     case 2: return Flm_ker_echelon(x, p, 1);
    1522             :     }
    1523      236727 :   return Flm_ker_gauss(inplace? x: Flm_copy(x), p, deplin);
    1524             : }
    1525             : 
    1526             : GEN
    1527      313083 : Flm_ker_sp(GEN x, ulong p, long deplin) {
    1528      313083 :   return Flm_ker_i(x, p, deplin, 1);
    1529             : }
    1530             : 
    1531             : GEN
    1532       68998 : Flm_ker(GEN x, ulong p) {
    1533       68998 :   return Flm_ker_i(x, p, 0, 0);
    1534             : }
    1535             : 
    1536             : GEN
    1537           0 : Flm_deplin(GEN x, ulong p) {
    1538           0 :   return Flm_ker_i(x, p, 1, 0);
    1539             : }
    1540             : 
    1541             : ulong
    1542        1631 : F2m_det_sp(GEN x) { return !F2m_ker_sp(x, 1); }
    1543             : 
    1544             : ulong
    1545          14 : F2m_det(GEN x)
    1546             : {
    1547          14 :   pari_sp av = avma;
    1548          14 :   ulong d = F2m_det_sp(F2m_copy(x));
    1549          14 :   return gc_ulong(av, d);
    1550             : }
    1551             : 
    1552             : /* in place, destroy a, SMALL_ULONG(p) is TRUE */
    1553             : static ulong
    1554        1743 : Flm_det_gauss_OK(GEN a, long nbco, ulong p)
    1555             : {
    1556        1743 :   long i,j,k, s = 1;
    1557        1743 :   ulong q, x = 1;
    1558             : 
    1559        7266 :   for (i=1; i<nbco; i++)
    1560             :   {
    1561        7140 :     for(k=i; k<=nbco; k++)
    1562             :     {
    1563        6958 :       ulong c = ucoeff(a,k,i) % p;
    1564        6958 :       ucoeff(a,k,i) = c;
    1565        6958 :       if (c) break;
    1566             :     }
    1567        5705 :     for(j=k+1; j<=nbco; j++) ucoeff(a,j,i) %= p;
    1568        5705 :     if (k > nbco) return ucoeff(a,i,i);
    1569        5523 :     if (k != i)
    1570             :     { /* exchange the lines s.t. k = i */
    1571         784 :       for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
    1572         784 :       s = -s;
    1573             :     }
    1574        5523 :     q = ucoeff(a,i,i);
    1575             : 
    1576        5523 :     if (x & HIGHMASK) x %= p;
    1577        5523 :     x *= q;
    1578        5523 :     q = Fl_inv(q,p);
    1579       18417 :     for (k=i+1; k<=nbco; k++)
    1580             :     {
    1581       12894 :       ulong m = ucoeff(a,i,k) % p;
    1582       12894 :       if (!m) continue;
    1583             : 
    1584        8708 :       m = p - ((m*q)%p);
    1585       34489 :       for (j=i+1; j<=nbco; j++)
    1586             :       {
    1587       25781 :         ulong c = ucoeff(a,j,k);
    1588       25781 :         if (c & HIGHMASK) c %= p;
    1589       25781 :         ucoeff(a,j,k) = c  + m*ucoeff(a,j,i);
    1590             :       }
    1591             :     }
    1592             :   }
    1593        1561 :   if (x & HIGHMASK) x %= p;
    1594        1561 :   q = ucoeff(a,nbco,nbco);
    1595        1561 :   if (q & HIGHMASK) q %= p;
    1596        1561 :   x = (x*q) % p;
    1597        1561 :   if (s < 0 && x) x = p - x;
    1598        1561 :   return x;
    1599             : }
    1600             : 
    1601             : /* in place, destroy a */
    1602             : static ulong
    1603       55110 : Flm_det_gauss(GEN a, ulong p)
    1604             : {
    1605       55110 :   long i,j,k, s = 1, nbco = lg(a)-1;
    1606       55110 :   ulong pi, q, x = 1;
    1607             : 
    1608       55110 :   if (SMALL_ULONG(p)) return Flm_det_gauss_OK(a, nbco, p);
    1609       53367 :   pi = get_Fl_red(p);
    1610      339114 :   for (i=1; i<nbco; i++)
    1611             :   {
    1612      285828 :     for(k=i; k<=nbco; k++)
    1613      285829 :       if (ucoeff(a,k,i)) break;
    1614      285746 :     if (k > nbco) return ucoeff(a,i,i);
    1615      285746 :     if (k != i)
    1616             :     { /* exchange the lines s.t. k = i */
    1617          67 :       for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
    1618          67 :       s = -s;
    1619             :     }
    1620      285746 :     q = ucoeff(a,i,i);
    1621             : 
    1622      285746 :     x = Fl_mul_pre(x, q, p, pi);
    1623      285754 :     q = Fl_inv(q,p);
    1624     1220905 :     for (k=i+1; k<=nbco; k++)
    1625             :     {
    1626      935153 :       ulong m = ucoeff(a,i,k);
    1627      935153 :       if (!m) continue;
    1628             : 
    1629      892470 :       m = Fl_mul_pre(m, q, p, pi);
    1630     4553053 :       for (j=i+1; j<=nbco; j++)
    1631     3660660 :         ucoeff(a,j,k) = Fl_sub(ucoeff(a,j,k), Fl_mul_pre(m,ucoeff(a,j,i), p, pi), p);
    1632             :     }
    1633             :   }
    1634       53368 :   if (s < 0) x = Fl_neg(x, p);
    1635       53368 :   return Fl_mul(x, ucoeff(a,nbco,nbco), p);
    1636             : }
    1637             : 
    1638             : static ulong
    1639       36281 : Flm_det_CUP(GEN a, ulong p) {
    1640             :   GEN R, C, U, P;
    1641       36281 :   long d, i, n = lg(a) - 1, r;
    1642       36281 :   r = Flm_CUP(a, &R, &C, &U, &P, p);
    1643       36287 :   if (r < n)
    1644          55 :     d = 0;
    1645             :   else {
    1646       36232 :     d = perm_sign(P) == 1? 1: p-1;
    1647      380279 :     for (i = 1; i <= n; i++)
    1648      344056 :       d = Fl_mul(d, ucoeff(U, i, i), p);
    1649             :   }
    1650       36278 :   return d;
    1651             : }
    1652             : 
    1653             : static ulong
    1654       91392 : Flm_det_i(GEN x, ulong p, long inplace) {
    1655       91392 :   pari_sp av = avma;
    1656             :   ulong d;
    1657       91392 :   if (lg(x) - 1 >= Flm_CUP_LIMIT)
    1658       36281 :     d = Flm_det_CUP(x, p);
    1659             :   else
    1660       55111 :     d = Flm_det_gauss(inplace? x: Flm_copy(x), p);
    1661       91388 :   return gc_ulong(av, d);
    1662             : }
    1663             : 
    1664             : ulong
    1665       91392 : Flm_det_sp(GEN x, ulong p) { return Flm_det_i(x, p, 1); }
    1666             : ulong
    1667           0 : Flm_det(GEN x, ulong p) { return Flm_det_i(x, p, 0); }
    1668             : 
    1669             : static GEN
    1670      483446 : FpM_init(GEN a, GEN p, ulong *pp)
    1671             : {
    1672      483446 :   if (lgefint(p) == 3)
    1673             :   {
    1674      476145 :     *pp = uel(p,2);
    1675      476145 :     return (*pp==2)? ZM_to_F2m(a): ZM_to_Flm(a, *pp);
    1676             :   }
    1677        7301 :   *pp = 0; return a;
    1678             : }
    1679             : GEN
    1680        2359 : RgM_Fp_init(GEN a, GEN p, ulong *pp)
    1681             : {
    1682        2359 :   if (lgefint(p) == 3)
    1683             :   {
    1684        2009 :     *pp = uel(p,2);
    1685        2009 :     return (*pp==2)? RgM_to_F2m(a): RgM_to_Flm(a, *pp);
    1686             :   }
    1687         350 :   *pp = 0; return RgM_to_FpM(a,p);
    1688             : }
    1689             : 
    1690             : static GEN
    1691         315 : FpM_det_gen(GEN a, GEN p)
    1692             : {
    1693             :   void *E;
    1694         315 :   const struct bb_field *S = get_Fp_field(&E,p);
    1695         315 :   return gen_det(a, E, S);
    1696             : }
    1697             : GEN
    1698        3948 : FpM_det(GEN a, GEN p)
    1699             : {
    1700        3948 :   pari_sp av = avma;
    1701             :   ulong pp, d;
    1702        3948 :   a = FpM_init(a, p, &pp);
    1703        3948 :   switch(pp)
    1704             :   {
    1705         315 :   case 0: return FpM_det_gen(a, p);
    1706        1617 :   case 2: d = F2m_det_sp(a); break;
    1707        2016 :   default:d = Flm_det_sp(a,pp); break;
    1708             :   }
    1709        3633 :   set_avma(av); return utoi(d);
    1710             : }
    1711             : 
    1712             : /* Destroy x */
    1713             : static GEN
    1714       40116 : F2m_gauss_pivot(GEN x, long *rr)
    1715             : {
    1716             :   GEN c, d;
    1717             :   long i, j, k, r, m, n;
    1718             : 
    1719       40116 :   n = lg(x)-1; if (!n) { *rr=0; return NULL; }
    1720       40116 :   m = mael(x,1,1); r=0;
    1721             : 
    1722       40116 :   d = cgetg(n+1, t_VECSMALL);
    1723       40116 :   c = const_F2v(m);
    1724      330533 :   for (k=1; k<=n; k++)
    1725             :   {
    1726      290417 :     GEN xk = gel(x,k);
    1727      290417 :     j = F2v_find_nonzero(xk, c, m);
    1728      290417 :     if (j>m) { r++; d[k] = 0; }
    1729             :     else
    1730             :     {
    1731      181314 :       F2v_clear(c,j); d[k] = j;
    1732     2632957 :       for (i=k+1; i<=n; i++)
    1733             :       {
    1734     2451643 :         GEN xi = gel(x,i);
    1735     2451643 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1736             :       }
    1737             :     }
    1738             :   }
    1739             : 
    1740       40116 :   *rr = r; set_avma((pari_sp)d); return d;
    1741             : }
    1742             : 
    1743             : /* Destroy x */
    1744             : static GEN
    1745      178273 : Flm_gauss_pivot(GEN x, ulong p, long *rr)
    1746             : {
    1747             :   GEN c,d;
    1748             :   long i,j,k,r,t,n,m;
    1749             : 
    1750      178273 :   n=lg(x)-1; if (!n) { *rr=0; return NULL; }
    1751             : 
    1752      178161 :   m=nbrows(x); r=0;
    1753      178161 :   d=cgetg(n+1,t_VECSMALL);
    1754      178161 :   c = zero_zv(m);
    1755      808439 :   for (k=1; k<=n; k++)
    1756             :   {
    1757     2139500 :     for (j=1; j<=m; j++)
    1758     1973030 :       if (!c[j])
    1759             :       {
    1760     1100766 :         ucoeff(x,j,k) %= p;
    1761     1100766 :         if (ucoeff(x,j,k)) break;
    1762             :       }
    1763      630278 :     if (j>m) { r++; d[k]=0; }
    1764             :     else
    1765             :     {
    1766      463808 :       ulong piv = p - Fl_inv(ucoeff(x,j,k), p);
    1767      463808 :       c[j]=k; d[k]=j;
    1768     1388054 :       for (i=k+1; i<=n; i++)
    1769      924246 :         ucoeff(x,j,i) = Fl_mul(piv, ucoeff(x,j,i), p);
    1770     3480376 :       for (t=1; t<=m; t++)
    1771     3016568 :         if (!c[t]) /* no pivot on that line yet */
    1772             :         {
    1773     1967536 :           piv = ucoeff(x,t,k);
    1774     1967536 :           if (piv)
    1775             :           {
    1776     1089225 :             ucoeff(x,t,k) = 0;
    1777     3482231 :             for (i=k+1; i<=n; i++)
    1778     4786012 :               ucoeff(x,t,i) = Fl_add(ucoeff(x,t,i),
    1779     2393006 :                                      Fl_mul(piv,ucoeff(x,j,i),p),p);
    1780             :           }
    1781             :         }
    1782      463808 :       for (i=k; i<=n; i++) ucoeff(x,j,i) = 0; /* dummy */
    1783             :     }
    1784             :   }
    1785      178161 :   *rr = r; set_avma((pari_sp)d); return d;
    1786             : }
    1787             : 
    1788             : static GEN
    1789       67301 : Flm_pivots_CUP(GEN x, ulong p, long *rr) {
    1790             :   pari_sp av;
    1791       67301 :   long i, n = lg(x) - 1, r;
    1792       67301 :   GEN R, C, U, P, d = zero_zv(n);
    1793       67301 :   av = avma;
    1794       67301 :   r = Flm_CUP(x, &R, &C, &U, &P, p);
    1795      819426 :   for(i = 1; i <= r; i++)
    1796      752125 :     d[P[i]] = R[i];
    1797       67301 :   set_avma(av);
    1798       67301 :   *rr = n - r;
    1799       67301 :   return d;
    1800             : }
    1801             : 
    1802             : static GEN
    1803      245574 : Flm_pivots(GEN x, ulong p, long *rr, long inplace) {
    1804      245574 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1805       67301 :     return Flm_pivots_CUP(x, p, rr);
    1806      178273 :   return Flm_gauss_pivot(inplace? x: Flm_copy(x), p, rr);
    1807             : }
    1808             : 
    1809             : static GEN
    1810         815 : FpM_gauss_pivot_gen(GEN x, GEN p, long *rr)
    1811             : {
    1812             :   void *E;
    1813         815 :   const struct bb_field *S = get_Fp_field(&E,p);
    1814         815 :   return gen_Gauss_pivot(x, rr, E, S);
    1815             : }
    1816             : static GEN
    1817      141811 : FpM_gauss_pivot(GEN x, GEN p, long *rr)
    1818             : {
    1819             :   ulong pp;
    1820      141811 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1821      139900 :   x = FpM_init(x, p, &pp);
    1822      139900 :   switch(pp)
    1823             :   {
    1824         815 :   case 0: return FpM_gauss_pivot_gen(x, p, rr);
    1825       40025 :   case 2: return F2m_gauss_pivot(x, rr);
    1826       99060 :   default:return Flm_pivots(x, pp, rr, 1);
    1827             :   }
    1828             : }
    1829             : 
    1830             : GEN
    1831       77586 : FpM_image(GEN x, GEN p)
    1832             : {
    1833             :   long r;
    1834       77586 :   GEN d = FpM_gauss_pivot(x,p,&r); /* d left on stack for efficiency */
    1835       77586 :   return image_from_pivot(x,d,r);
    1836             : }
    1837             : 
    1838             : GEN
    1839         637 : Flm_image(GEN x, ulong p)
    1840             : {
    1841             :   long r;
    1842         637 :   GEN d = Flm_pivots(x, p, &r, 0); /* d left on stack for efficiency */
    1843         637 :   return image_from_pivot(x,d,r);
    1844             : }
    1845             : 
    1846             : GEN
    1847           7 : F2m_image(GEN x)
    1848             : {
    1849             :   long r;
    1850           7 :   GEN d = F2m_gauss_pivot(F2m_copy(x),&r); /* d left on stack for efficiency */
    1851           7 :   return image_from_pivot(x,d,r);
    1852             : }
    1853             : 
    1854             : long
    1855          28 : FpM_rank(GEN x, GEN p)
    1856             : {
    1857          28 :   pari_sp av = avma;
    1858             :   long r;
    1859          28 :   (void)FpM_gauss_pivot(x,p,&r);
    1860          28 :   return gc_long(av, lg(x)-1 - r);
    1861             : }
    1862             : 
    1863             : long
    1864        2480 : Flm_rank(GEN x, ulong p)
    1865             : {
    1866        2480 :   pari_sp av = avma;
    1867             :   long r;
    1868        2480 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT) {
    1869             :     GEN R, C;
    1870          35 :     return gc_long(av, Flm_echelon(x, &R, &C, p));
    1871             :   }
    1872        2445 :   (void) Flm_pivots(x, p, &r, 0);
    1873        2445 :   return gc_long(av, lg(x)-1 - r);
    1874             : }
    1875             : 
    1876             : long
    1877          63 : F2m_rank(GEN x)
    1878             : {
    1879          63 :   pari_sp av = avma;
    1880             :   long r;
    1881          63 :   (void)F2m_gauss_pivot(F2m_copy(x),&r);
    1882          63 :   return gc_long(av, lg(x)-1 - r);
    1883             : }
    1884             : 
    1885             : static GEN
    1886        1267 : FlxqM_gauss_pivot(GEN x, GEN T, ulong p, long *rr)
    1887             : {
    1888             :   void *E;
    1889        1267 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1890        1267 :   return gen_Gauss_pivot(x, rr, E, S);
    1891             : }
    1892             : 
    1893             : static GEN
    1894          14 : FlxqM_pivots_CUP(GEN x, GEN T, ulong p, long *rr) {
    1895             :   pari_sp av;
    1896          14 :   long i, n = lg(x) - 1, r;
    1897          14 :   GEN R, C, U, P, d = zero_zv(n);
    1898          14 :   av = avma;
    1899          14 :   r = FlxqM_CUP(x, &R, &C, &U, &P, T, p);
    1900         182 :   for(i = 1; i <= r; i++)
    1901         168 :     d[P[i]] = R[i];
    1902          14 :   set_avma(av);
    1903          14 :   *rr = n - r;
    1904          14 :   return d;
    1905             : }
    1906             : 
    1907             : static GEN
    1908          28 : FlxqM_pivots(GEN x, GEN T, ulong p, long *rr) {
    1909          28 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    1910          14 :     return FlxqM_pivots_CUP(x, T, p, rr);
    1911          14 :   return FlxqM_gauss_pivot(x, T, p, rr);
    1912             : }
    1913             : 
    1914             : GEN
    1915          21 : FlxqM_image(GEN x, GEN T, ulong p)
    1916             : {
    1917             :   long r;
    1918          21 :   GEN d = FlxqM_pivots(x, T, p, &r); /* d left on stack for efficiency */
    1919          21 :   return image_from_pivot(x,d,r);
    1920             : }
    1921             : 
    1922             : long
    1923          28 : FlxqM_rank(GEN x, GEN T, ulong p)
    1924             : {
    1925          28 :   pari_sp av = avma;
    1926             :   long r;
    1927          28 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    1928             :   {
    1929             :     GEN R, C;
    1930          21 :     return gc_long(av, FlxqM_echelon(x, &R, &C, T, p));
    1931             :   }
    1932           7 :   (void) FlxqM_pivots(x, T, p, &r);
    1933           7 :   return gc_long(av, lg(x)-1 - r);
    1934             : }
    1935             : 
    1936             : static GEN
    1937           7 : FlxqM_det_gen(GEN a, GEN T, ulong p)
    1938             : {
    1939             :   void *E;
    1940           7 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1941           7 :   return gen_det(a, E, S);
    1942             : }
    1943             : 
    1944             : static GEN
    1945          21 : FlxqM_det_CUP(GEN a, GEN T, ulong p) {
    1946          21 :   pari_sp av = avma;
    1947             :   GEN R, C, U, P, d;
    1948          21 :   long i, n = lg(a) - 1, r, sv = get_Flx_var(T);
    1949          21 :   r = FlxqM_CUP(a, &R, &C, &U, &P, T, p);
    1950          21 :   if (r < n)
    1951           0 :     d = pol0_Flx(sv);
    1952             :   else {
    1953          21 :     d = mkvecsmall2(sv, perm_sign(P) == 1? 1: p - 1);
    1954         483 :     for (i = 1; i <= n; i++)
    1955         462 :       d = Flxq_mul(d, gcoeff(U, i, i), T, p);
    1956             :   }
    1957          21 :   return gerepileuptoleaf(av, d);
    1958             : }
    1959             : 
    1960             : GEN
    1961          28 : FlxqM_det(GEN a, GEN T, ulong p) {
    1962          28 :   if (lg(a) - 1 >= FlxqM_CUP_LIMIT)
    1963          21 :     return FlxqM_det_CUP(a, T, p);
    1964             :   else
    1965           7 :     return FlxqM_det_gen(a, T, p);
    1966             : }
    1967             : 
    1968             : GEN
    1969          21 : FlxqM_FlxqC_invimage(GEN A, GEN B, GEN T, ulong p) {
    1970             :   void *E;
    1971          21 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1972          21 :   return gen_matcolinvimage(A, B, E, ff);
    1973             : }
    1974             : 
    1975             : GEN
    1976          28 : FlxqM_FlxqC_mul(GEN A, GEN B, GEN T, ulong p) {
    1977             :   void *E;
    1978          28 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1979          28 :   return gen_matcolmul(A, B, E, ff);
    1980             : }
    1981             : 
    1982             : GEN
    1983       15332 : FlxqM_mul(GEN A, GEN B, GEN T, ulong p) {
    1984             :   void *E;
    1985             :   const struct bb_field *ff;
    1986       15332 :   long n = lg(A) - 1;
    1987             : 
    1988       15332 :   if (n == 0)
    1989           0 :     return cgetg(1, t_MAT);
    1990       15332 :   if (n > 1)
    1991       12427 :     return FlxqM_mul_Kronecker(A, B, T, p);
    1992        2905 :   ff = get_Flxq_field(&E, T, p);
    1993        2905 :   return gen_matmul(A, B, E, ff);
    1994             : }
    1995             : 
    1996             : static GEN
    1997          21 : FlxqM_invimage_gen(GEN A, GEN B, GEN T, ulong p) {
    1998             :   void *E;
    1999          21 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    2000          21 :   return gen_matinvimage(A, B, E, ff);
    2001             : }
    2002             : 
    2003             : static GEN
    2004          21 : FlxqM_invimage_CUP(GEN A, GEN B, GEN T, ulong p) {
    2005          21 :   pari_sp av = avma;
    2006             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    2007          21 :   long r, sv = get_Flx_var(T);
    2008          21 :   r = FlxqM_CUP(A, &R, &C, &U, &P, T, p);
    2009          21 :   Rc = indexcompl(R, nbrows(B));
    2010          21 :   C1 = rowpermute(C, R);
    2011          21 :   C2 = rowpermute(C, Rc);
    2012          21 :   B1 = rowpermute(B, R);
    2013          21 :   B2 = rowpermute(B, Rc);
    2014          21 :   Z = FlxqM_rsolve_lower_unit(C1, B1, T, p);
    2015          21 :   if (!gequal(FlxqM_mul(C2, Z, T, p), B2))
    2016          14 :     return NULL;
    2017          14 :   Y = vconcat(FlxqM_rsolve_upper(vecslice(U, 1, r), Z, T, p),
    2018          14 :               zero_FlxM(lg(A) - 1 - r, lg(B) - 1, sv));
    2019           7 :   X = rowpermute(Y, perm_inv(P));
    2020           7 :   return gerepilecopy(av, X);
    2021             : }
    2022             : 
    2023             : GEN
    2024          42 : FlxqM_invimage(GEN A, GEN B, GEN T, ulong p) {
    2025          42 :   long nA = lg(A)-1, nB = lg(B)-1;
    2026             : 
    2027          42 :   if (!nB) return cgetg(1, t_MAT);
    2028          42 :   if (nA + nB >= FlxqM_CUP_LIMIT && nbrows(B) >= FlxqM_CUP_LIMIT)
    2029          21 :     return FlxqM_invimage_CUP(A, B, T, p);
    2030          21 :   return FlxqM_invimage_gen(A, B, T, p);
    2031             : }
    2032             : 
    2033             : GEN
    2034           7 : F2xqM_det(GEN a, GEN T)
    2035             : {
    2036             :   void *E;
    2037           7 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2038           7 :   return gen_det(a, E, S);
    2039             : }
    2040             : 
    2041             : static GEN
    2042          84 : F2xqM_gauss_gen(GEN a, GEN b, GEN T)
    2043             : {
    2044             :   void *E;
    2045          84 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2046          84 :   return gen_Gauss(a, b, E, S);
    2047             : }
    2048             : 
    2049             : GEN
    2050          21 : F2xqM_gauss(GEN a, GEN b, GEN T)
    2051             : {
    2052          21 :   pari_sp av = avma;
    2053          21 :   long n = lg(a)-1;
    2054             :   GEN u;
    2055          21 :   if (!n || lg(b)==1) { set_avma(av); return cgetg(1, t_MAT); }
    2056          21 :   u = F2xqM_gauss_gen(a, b, T);
    2057          21 :   if (!u) return gc_NULL(av);
    2058          14 :   return gerepilecopy(av, u);
    2059             : }
    2060             : 
    2061             : GEN
    2062          35 : F2xqM_inv(GEN a, GEN T)
    2063             : {
    2064          35 :   pari_sp av = avma;
    2065             :   GEN u;
    2066          35 :   if (lg(a) == 1) { set_avma(av); return cgetg(1, t_MAT); }
    2067          35 :   u = F2xqM_gauss_gen(a, matid_F2xqM(nbrows(a),T), T);
    2068          35 :   if (!u) return gc_NULL(av);
    2069          28 :   return gerepilecopy(av, u);
    2070             : }
    2071             : 
    2072             : GEN
    2073          28 : F2xqM_F2xqC_gauss(GEN a, GEN b, GEN T)
    2074             : {
    2075          28 :   pari_sp av = avma;
    2076             :   GEN u;
    2077          28 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2078          28 :   u = F2xqM_gauss_gen(a, mkmat(b), T);
    2079          28 :   if (!u) return gc_NULL(av);
    2080          14 :   return gerepilecopy(av, gel(u,1));
    2081             : }
    2082             : 
    2083             : GEN
    2084          21 : F2xqM_F2xqC_invimage(GEN A, GEN B, GEN T) {
    2085             :   void *E;
    2086          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2087          21 :   return gen_matcolinvimage(A, B, E, ff);
    2088             : }
    2089             : 
    2090             : GEN
    2091          21 : F2xqM_F2xqC_mul(GEN A, GEN B, GEN T) {
    2092             :   void *E;
    2093          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2094          21 :   return gen_matcolmul(A, B, E, ff);
    2095             : }
    2096             : 
    2097             : GEN
    2098        1407 : F2xqM_mul(GEN A, GEN B, GEN T) {
    2099             :   void *E;
    2100        1407 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2101        1407 :   return gen_matmul(A, B, E, ff);
    2102             : }
    2103             : 
    2104             : GEN
    2105          21 : F2xqM_invimage(GEN A, GEN B, GEN T) {
    2106             :   void *E;
    2107          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2108          21 :   return gen_matinvimage(A, B, E, ff);
    2109             : }
    2110             : 
    2111             : static GEN
    2112         105 : FqM_gauss_pivot_gen(GEN x, GEN T, GEN p, long *rr)
    2113             : {
    2114             :   void *E;
    2115         105 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2116         105 :   return gen_Gauss_pivot(x, rr, E, S);
    2117             : }
    2118             : static GEN
    2119        1337 : FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr)
    2120             : {
    2121        1337 :   if (lg(x)==1) { *rr = 0; return NULL; }
    2122        1337 :   if (!T) return FpM_gauss_pivot(x, p, rr);
    2123        1337 :   if (lgefint(p) == 3)
    2124             :   {
    2125        1232 :     pari_sp av = avma;
    2126        1232 :     ulong pp = uel(p,2);
    2127        1232 :     GEN Tp = ZXT_to_FlxT(T, pp);
    2128        1232 :     GEN d = FlxqM_gauss_pivot(FqM_to_FlxM(x, T, p), Tp, pp, rr);
    2129        1232 :     return d ? gerepileuptoleaf(av, d): d;
    2130             :   }
    2131         105 :   return FqM_gauss_pivot_gen(x, T, p, rr);
    2132             : }
    2133             : 
    2134             : GEN
    2135          49 : FqM_image(GEN x, GEN T, GEN p)
    2136             : {
    2137             :   long r;
    2138          49 :   GEN d = FqM_gauss_pivot(x,T,p,&r); /* d left on stack for efficiency */
    2139          49 :   return image_from_pivot(x,d,r);
    2140             : }
    2141             : 
    2142             : long
    2143          70 : FqM_rank(GEN x, GEN T, GEN p)
    2144             : {
    2145          70 :   pari_sp av = avma;
    2146             :   long r;
    2147          70 :   (void)FqM_gauss_pivot(x,T,p,&r);
    2148          70 :   return gc_long(av, lg(x)-1 - r);
    2149             : }
    2150             : 
    2151             : GEN
    2152          70 : FqM_det(GEN x, GEN T, GEN p)
    2153             : {
    2154             :   void *E;
    2155          70 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2156          70 :   return gen_det(x, E, S);
    2157             : }
    2158             : 
    2159             : GEN
    2160          21 : FqM_FqC_invimage(GEN A, GEN B, GEN T, GEN p) {
    2161             :   void *E;
    2162          21 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2163          21 :   return gen_matcolinvimage(A, B, E, ff);
    2164             : }
    2165             : 
    2166             : GEN
    2167          28 : FqM_FqC_mul(GEN A, GEN B, GEN T, GEN p) {
    2168             :   void *E;
    2169          28 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2170          28 :   return gen_matcolmul(A, B, E, ff);
    2171             : }
    2172             : 
    2173             : GEN
    2174       59960 : FqM_mul(GEN A, GEN B, GEN T, GEN p) {
    2175             :   void *E;
    2176       59960 :   long n = lg(A) - 1;
    2177             :   const struct bb_field *ff;
    2178       59960 :   if (n == 0)
    2179           0 :     return cgetg(1, t_MAT);
    2180       59960 :   if (n > 1)
    2181       57195 :     return FqM_mul_Kronecker(A, B, T, p);
    2182        2765 :   ff = get_Fq_field(&E, T, p);
    2183        2765 :   return gen_matmul(A, B, E, ff);
    2184             : }
    2185             : 
    2186             : GEN
    2187          42 : FqM_invimage(GEN A, GEN B, GEN T, GEN p) {
    2188             :   void *E;
    2189          42 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2190          42 :   return gen_matinvimage(A, B, E, ff);
    2191             : }
    2192             : 
    2193             : static GEN
    2194        2259 : FpM_ker_gen(GEN x, GEN p, long deplin)
    2195             : {
    2196             :   void *E;
    2197        2259 :   const struct bb_field *S = get_Fp_field(&E,p);
    2198        2259 :   return gen_ker(x, deplin, E, S);
    2199             : }
    2200             : static GEN
    2201      273184 : FpM_ker_i(GEN x, GEN p, long deplin)
    2202             : {
    2203      273184 :   pari_sp av = avma;
    2204             :   ulong pp;
    2205             :   GEN y;
    2206             : 
    2207      273184 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2208      273184 :   x = FpM_init(x, p, &pp);
    2209      273184 :   switch(pp)
    2210             :   {
    2211        2189 :   case 0: return FpM_ker_gen(x,p,deplin);
    2212             :   case 2:
    2213       79531 :     y = F2m_ker_sp(x, deplin);
    2214       79531 :     if (!y) return y;
    2215       79531 :     y = deplin? F2c_to_ZC(y): F2m_to_ZM(y);
    2216       79531 :     return gerepileupto(av, y);
    2217             :   default:
    2218      191464 :     y = Flm_ker_sp(x, pp, deplin);
    2219      191464 :     if (!y) return y;
    2220      191464 :     y = deplin? Flc_to_ZC(y): Flm_to_ZM(y);
    2221      191464 :     return gerepileupto(av, y);
    2222             :   }
    2223             : }
    2224             : 
    2225             : GEN
    2226      198916 : FpM_ker(GEN x, GEN p) { return FpM_ker_i(x,p,0); }
    2227             : 
    2228             : GEN
    2229       68500 : FpM_deplin(GEN x, GEN p) { return FpM_ker_i(x,p,1); }
    2230             : 
    2231             : static GEN
    2232         126 : FqM_ker_gen(GEN x, GEN T, GEN p, long deplin)
    2233             : {
    2234             :   void *E;
    2235         126 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2236         126 :   return gen_ker(x,deplin,E,S);
    2237             : }
    2238             : static GEN
    2239        8449 : FqM_ker_i(GEN x, GEN T, GEN p, long deplin)
    2240             : {
    2241        8449 :   if (!T) return FpM_ker_i(x,p,deplin);
    2242        2681 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2243             : 
    2244        2681 :   if (lgefint(p)==3)
    2245             :   {
    2246        2555 :     pari_sp ltop=avma;
    2247        2555 :     ulong l= p[2];
    2248        2555 :     GEN Ml = FqM_to_FlxM(x, T, p);
    2249        2555 :     GEN Tl = ZXT_to_FlxT(T,l);
    2250        2555 :     GEN p1 = FlxM_to_ZXM(FlxqM_ker(Ml,Tl,l));
    2251        2555 :     return gerepileupto(ltop,p1);
    2252             :   }
    2253         126 :   return FqM_ker_gen(x, T, p, deplin);
    2254             : }
    2255             : 
    2256             : GEN
    2257        8372 : FqM_ker(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,0); }
    2258             : 
    2259             : GEN
    2260          77 : FqM_deplin(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,1); }
    2261             : 
    2262             : static GEN
    2263         784 : FlxqM_ker_gen(GEN x, GEN T, ulong p, long deplin)
    2264             : {
    2265             :   const struct bb_field *ff;
    2266             :   void *E;
    2267             : 
    2268         784 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2269         784 :   ff=get_Flxq_field(&E,T,p);
    2270         784 :   return gen_ker(x,deplin, E, ff);
    2271             : }
    2272             : 
    2273             : static GEN
    2274        1813 : FlxqM_ker_echelon(GEN x, GEN T, ulong p) {
    2275        1813 :   pari_sp av = avma;
    2276             :   GEN R, Rc, C, C1, C2, S, K;
    2277        1813 :   long n = lg(x) - 1, r;
    2278        1813 :   r = FlxqM_echelon(shallowtrans(x), &R, &C, T, p);
    2279        1813 :   Rc = indexcompl(R, n);
    2280        1813 :   C1 = rowpermute(C, R);
    2281        1813 :   C2 = rowpermute(C, Rc);
    2282        1813 :   S = FlxqM_lsolve_lower_unit(C1, C2, T, p);
    2283        1813 :   K = vecpermute(shallowconcat(FlxM_neg(S, p), matid_FlxqM(n - r, T, p)),
    2284             :                  perm_inv(vecsmall_concat(R, Rc)));
    2285        1813 :   K = shallowtrans(K);
    2286        1813 :   return gerepilecopy(av, K);
    2287             : }
    2288             : 
    2289             : static GEN
    2290          14 : col_ei_FlxC(long n, long i, long sv) {
    2291          14 :   GEN v = zero_FlxC(n, sv);
    2292          14 :   gel(v, i) = pol1_Flx(sv);
    2293          14 :   return v;
    2294             : }
    2295             : 
    2296             : static GEN
    2297          21 : FlxqM_deplin_echelon(GEN x, GEN T, ulong p) {
    2298          21 :   pari_sp av = avma;
    2299             :   GEN R, Rc, C, C1, C2, s, v;
    2300          21 :   long i, n = lg(x) - 1, r, sv = get_Flx_var(T);
    2301          21 :   r = FlxqM_echelon(shallowtrans(x), &R, &C, T, p);
    2302          21 :   if (r == n) return gc_NULL(av);
    2303          14 :   Rc = indexcompl(R, n);
    2304          14 :   i = Rc[1];
    2305          14 :   C1 = rowpermute(C, R);
    2306          14 :   C2 = rowslice(C, i, i);
    2307          14 :   s = row(FlxqM_lsolve_lower_unit(C1, C2, T, p), 1);
    2308          14 :   settyp(s, t_COL);
    2309          14 :   v = vecpermute(shallowconcat(FlxC_neg(s, p), col_ei_FlxC(n - r, 1, sv)),
    2310             :                  perm_inv(vecsmall_concat(R, Rc)));
    2311          14 :   return gerepilecopy(av, v);
    2312             : }
    2313             : 
    2314             : static GEN
    2315        2618 : FlxqM_ker_i(GEN x, GEN T, ulong p, long deplin) {
    2316        2618 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    2317        1834 :     return deplin? FlxqM_deplin_echelon(x, T, p): FlxqM_ker_echelon(x, T, p);
    2318         784 :   return FlxqM_ker_gen(x, T, p, deplin);
    2319             : }
    2320             : 
    2321             : GEN
    2322        2583 : FlxqM_ker(GEN x, GEN T, ulong p)
    2323             : {
    2324        2583 :   return FlxqM_ker_i(x, T, p, 0);
    2325             : }
    2326             : 
    2327             : GEN
    2328          35 : FlxqM_deplin(GEN x, GEN T, ulong p)
    2329             : {
    2330          35 :   return FlxqM_ker_i(x, T, p, 1);
    2331             : }
    2332             : 
    2333             : static GEN
    2334          35 : F2xqM_ker_i(GEN x, GEN T, long deplin)
    2335             : {
    2336             :   const struct bb_field *ff;
    2337             :   void *E;
    2338             : 
    2339          35 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2340          35 :   ff = get_F2xq_field(&E,T);
    2341          35 :   return gen_ker(x,deplin, E, ff);
    2342             : }
    2343             : 
    2344             : GEN
    2345          21 : F2xqM_ker(GEN x, GEN T)
    2346             : {
    2347          21 :   return F2xqM_ker_i(x, T, 0);
    2348             : }
    2349             : 
    2350             : GEN
    2351          14 : F2xqM_deplin(GEN x, GEN T)
    2352             : {
    2353          14 :   return F2xqM_ker_i(x, T, 1);
    2354             : }
    2355             : 
    2356             : static GEN
    2357          28 : F2xqM_gauss_pivot(GEN x, GEN T, long *rr)
    2358             : {
    2359             :   void *E;
    2360          28 :   const struct bb_field *S = get_F2xq_field(&E,T);
    2361          28 :   return gen_Gauss_pivot(x, rr, E, S);
    2362             : }
    2363             : GEN
    2364           7 : F2xqM_image(GEN x, GEN T)
    2365             : {
    2366             :   long r;
    2367           7 :   GEN d = F2xqM_gauss_pivot(x,T,&r); /* d left on stack for efficiency */
    2368           7 :   return image_from_pivot(x,d,r);
    2369             : }
    2370             : long
    2371           7 : F2xqM_rank(GEN x, GEN T)
    2372             : {
    2373           7 :   pari_sp av = avma;
    2374             :   long r;
    2375           7 :   (void)F2xqM_gauss_pivot(x,T,&r);
    2376           7 :   return gc_long(av, lg(x)-1 - r);
    2377             : }
    2378             : /*******************************************************************/
    2379             : /*                                                                 */
    2380             : /*                       Solve A*X=B (Gauss pivot)                 */
    2381             : /*                                                                 */
    2382             : /*******************************************************************/
    2383             : /* x ~ 0 compared to reference y */
    2384             : int
    2385      596313 : approx_0(GEN x, GEN y)
    2386             : {
    2387      596313 :   long tx = typ(x);
    2388      596313 :   if (tx == t_COMPLEX)
    2389         140 :     return approx_0(gel(x,1), y) && approx_0(gel(x,2), y);
    2390      596432 :   return gequal0(x) ||
    2391      413085 :          (tx == t_REAL && gexpo(y) - gexpo(x) > bit_prec(x));
    2392             : }
    2393             : /* x a column, x0 same column in the original input matrix (for reference),
    2394             :  * c list of pivots so far */
    2395             : static long
    2396      617334 : gauss_get_pivot_max(GEN X, GEN X0, long ix, GEN c)
    2397             : {
    2398      617334 :   GEN p, r, x = gel(X,ix), x0 = gel(X0,ix);
    2399      617334 :   long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
    2400      617334 :   if (c)
    2401             :   {
    2402      118888 :     for (i=1; i<lx; i++)
    2403       73563 :       if (!c[i])
    2404             :       {
    2405       37688 :         long e = gexpo(gel(x,i));
    2406       37688 :         if (e > ex) { ex = e; k = i; }
    2407             :       }
    2408             :   }
    2409             :   else
    2410             :   {
    2411     1962918 :     for (i=ix; i<lx; i++)
    2412             :     {
    2413     1390909 :       long e = gexpo(gel(x,i));
    2414     1390909 :       if (e > ex) { ex = e; k = i; }
    2415             :     }
    2416             :   }
    2417      617334 :   if (!k) return lx;
    2418      596138 :   p = gel(x,k);
    2419      596138 :   r = gel(x0,k); if (isrationalzero(r)) r = x0;
    2420      596138 :   return approx_0(p, r)? lx: k;
    2421             : }
    2422             : static long
    2423       63938 : gauss_get_pivot_padic(GEN X, GEN p, long ix, GEN c)
    2424             : {
    2425       63938 :   GEN x = gel(X, ix);
    2426       63938 :   long i, k = 0, ex = (long)HIGHVALPBIT, lx = lg(x);
    2427       63938 :   if (c)
    2428             :   {
    2429         504 :     for (i=1; i<lx; i++)
    2430         378 :       if (!c[i] && !gequal0(gel(x,i)))
    2431             :       {
    2432         245 :         long e = gvaluation(gel(x,i), p);
    2433         245 :         if (e < ex) { ex = e; k = i; }
    2434             :       }
    2435             :   }
    2436             :   else
    2437             :   {
    2438      450723 :     for (i=ix; i<lx; i++)
    2439      386911 :       if (!gequal0(gel(x,i)))
    2440             :       {
    2441      183847 :         long e = gvaluation(gel(x,i), p);
    2442      183847 :         if (e < ex) { ex = e; k = i; }
    2443             :       }
    2444             :   }
    2445       63938 :   return k? k: lx;
    2446             : }
    2447             : static long
    2448        4529 : gauss_get_pivot_NZ(GEN X, GEN x0/*unused*/, long ix, GEN c)
    2449             : {
    2450        4529 :   GEN x = gel(X, ix);
    2451        4529 :   long i, lx = lg(x);
    2452             :   (void)x0;
    2453        4529 :   if (c)
    2454             :   {
    2455       11634 :     for (i=1; i<lx; i++)
    2456       10780 :       if (!c[i] && !gequal0(gel(x,i))) return i;
    2457             :   }
    2458             :   else
    2459             :   {
    2460        2716 :     for (i=ix; i<lx; i++)
    2461        2702 :       if (!gequal0(gel(x,i))) return i;
    2462             :   }
    2463         868 :   return lx;
    2464             : }
    2465             : 
    2466             : /* Return pivot seeking function appropriate for the domain of the RgM x
    2467             :  * (first non zero pivot, maximal pivot...)
    2468             :  * x0 is a reference point used when guessing whether x[i,j] ~ 0
    2469             :  * (iff x[i,j] << x0[i,j]); typical case: mateigen, Gauss pivot on x - vp.Id,
    2470             :  * but use original x when deciding whether a prospective pivot is non-0 */
    2471             : static pivot_fun
    2472      206228 : get_pivot_fun(GEN x, GEN x0, GEN *data)
    2473             : {
    2474      206228 :   long i, j, hx, lx = lg(x);
    2475      206228 :   int res = t_INT;
    2476      206228 :   GEN p = NULL;
    2477             : 
    2478      206228 :   *data = NULL;
    2479      206228 :   if (lx == 1) return &gauss_get_pivot_NZ;
    2480      206193 :   hx = lgcols(x);
    2481      902961 :   for (j=1; j<lx; j++)
    2482             :   {
    2483      696803 :     GEN xj = gel(x,j);
    2484     3734528 :     for (i=1; i<hx; i++)
    2485             :     {
    2486     3037760 :       GEN c = gel(xj,i);
    2487     3037760 :       switch(typ(c))
    2488             :       {
    2489             :         case t_REAL:
    2490     1622828 :           res = t_REAL;
    2491     1622828 :           break;
    2492             :         case t_COMPLEX:
    2493         364 :           if (typ(gel(c,1)) == t_REAL || typ(gel(c,2)) == t_REAL) res = t_REAL;
    2494         364 :           break;
    2495             :         case t_INT: case t_INTMOD: case t_FRAC: case t_FFELT: case t_QUAD:
    2496             :         case t_POLMOD: /* exact types */
    2497     1253330 :           break;
    2498             :         case t_PADIC:
    2499      161203 :           p = gel(c,2);
    2500      161203 :           res = t_PADIC;
    2501      161203 :           break;
    2502          35 :         default: return &gauss_get_pivot_NZ;
    2503             :       }
    2504             :     }
    2505             :   }
    2506      206158 :   switch(res)
    2507             :   {
    2508      196043 :     case t_REAL: *data = x0; return &gauss_get_pivot_max;
    2509        8260 :     case t_PADIC: *data = p; return &gauss_get_pivot_padic;
    2510        1855 :     default: return &gauss_get_pivot_NZ;
    2511             :   }
    2512             : }
    2513             : 
    2514             : static GEN
    2515      193471 : get_col(GEN a, GEN b, GEN p, long li)
    2516             : {
    2517      193471 :   GEN u = cgetg(li+1,t_COL);
    2518             :   long i, j;
    2519             : 
    2520      193471 :   gel(u,li) = gdiv(gel(b,li), p);
    2521      780330 :   for (i=li-1; i>0; i--)
    2522             :   {
    2523      586859 :     pari_sp av = avma;
    2524      586859 :     GEN m = gel(b,i);
    2525      586859 :     for (j=i+1; j<=li; j++) m = gsub(m, gmul(gcoeff(a,i,j), gel(u,j)));
    2526      586859 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(a,i,i)));
    2527             :   }
    2528      193471 :   return u;
    2529             : }
    2530             : /* assume 0 <= a[i,j] < p */
    2531             : static GEN
    2532      369227 : Fl_get_col_OK(GEN a, GEN b, long li, ulong p)
    2533             : {
    2534      369227 :   GEN u = cgetg(li+1,t_VECSMALL);
    2535      369227 :   ulong m = uel(b,li) % p;
    2536             :   long i,j;
    2537             : 
    2538      369227 :   uel(u,li) = (m * ucoeff(a,li,li)) % p;
    2539     4068123 :   for (i = li-1; i > 0; i--)
    2540             :   {
    2541     3698896 :     m = p - uel(b,i)%p;
    2542    33596570 :     for (j = i+1; j <= li; j++) {
    2543    29897674 :       if (m & HIGHBIT) m %= p;
    2544    29897674 :       m += ucoeff(a,i,j) * uel(u,j); /* 0 <= u[j] < p */
    2545             :     }
    2546     3698896 :     m %= p;
    2547     3698896 :     if (m) m = ((p-m) * ucoeff(a,i,i)) % p;
    2548     3698896 :     uel(u,i) = m;
    2549             :   }
    2550      369227 :   return u;
    2551             : }
    2552             : static GEN
    2553     1566897 : Fl_get_col(GEN a, GEN b, long li, ulong p)
    2554             : {
    2555     1566897 :   GEN u = cgetg(li+1,t_VECSMALL);
    2556     1566897 :   ulong m = uel(b,li) % p;
    2557             :   long i,j;
    2558             : 
    2559     1566897 :   uel(u,li) = Fl_mul(m, ucoeff(a,li,li), p);
    2560     4577426 :   for (i=li-1; i>0; i--)
    2561             :   {
    2562     3010529 :     m = b[i]%p;
    2563     8359979 :     for (j = i+1; j <= li; j++)
    2564     5349450 :       m = Fl_sub(m, Fl_mul(ucoeff(a,i,j), uel(u,j), p), p);
    2565     3010529 :     if (m) m = Fl_mul(m, ucoeff(a,i,i), p);
    2566     3010529 :     uel(u,i) = m;
    2567             :   }
    2568     1566897 :   return u;
    2569             : }
    2570             : 
    2571             : /* bk -= m * bi */
    2572             : static void
    2573     2891831 : _submul(GEN b, long k, long i, GEN m)
    2574             : {
    2575     2891831 :   gel(b,k) = gsub(gel(b,k), gmul(m, gel(b,i)));
    2576     2891831 : }
    2577             : static int
    2578      806287 : init_gauss(GEN a, GEN *b, long *aco, long *li, int *iscol)
    2579             : {
    2580      806287 :   *iscol = *b ? (typ(*b) == t_COL): 0;
    2581      806287 :   *aco = lg(a) - 1;
    2582      806287 :   if (!*aco) /* a empty */
    2583             :   {
    2584          70 :     if (*b && lg(*b) != 1) pari_err_DIM("gauss");
    2585          70 :     *li = 0; return 0;
    2586             :   }
    2587      806217 :   *li = nbrows(a);
    2588      806217 :   if (*li < *aco) pari_err_INV("gauss [no left inverse]", a);
    2589      806217 :   if (*b)
    2590             :   {
    2591      793040 :     switch(typ(*b))
    2592             :     {
    2593             :       case t_MAT:
    2594      113274 :         if (lg(*b) == 1) return 0;
    2595      113274 :         *b = RgM_shallowcopy(*b);
    2596      113274 :         break;
    2597             :       case t_COL:
    2598      679766 :         *b = mkmat( leafcopy(*b) );
    2599      679766 :         break;
    2600           0 :       default: pari_err_TYPE("gauss",*b);
    2601             :     }
    2602      793040 :     if (nbrows(*b) != *li) pari_err_DIM("gauss");
    2603             :   }
    2604             :   else
    2605       13177 :     *b = matid(*li);
    2606      806217 :   return 1;
    2607             : }
    2608             : 
    2609             : static GEN Flm_inv_sp(GEN a, ulong *detp, ulong p);
    2610             : static GEN
    2611       32130 : RgM_inv_QM(GEN M)
    2612             : {
    2613       32130 :   pari_sp av = avma;
    2614       32130 :   GEN den, cM, pM = Q_primitive_part(M, &cM);
    2615       32130 :   GEN b = ZM_inv(pM, &den);
    2616       32116 :   if (!b) return gc_NULL(av);
    2617       32109 :   if (cM) den = gmul(den, cM);
    2618       32109 :   if (!gequal1(den)) b = ZM_Q_mul(b, ginv(den));
    2619       32109 :   return gerepileupto(av, b);
    2620             : }
    2621             : 
    2622             : static GEN
    2623         112 : RgM_inv_FpM(GEN a, GEN p)
    2624             : {
    2625             :   ulong pp;
    2626         112 :   a = RgM_Fp_init(a, p, &pp);
    2627         112 :   switch(pp)
    2628             :   {
    2629             :   case 0:
    2630          35 :     a = FpM_inv(a,p);
    2631          35 :     if (a) a = FpM_to_mod(a, p);
    2632          35 :     break;
    2633             :   case 2:
    2634          35 :     a = F2m_inv(a);
    2635          35 :     if (a) a = F2m_to_mod(a);
    2636          35 :     break;
    2637             :   default:
    2638          42 :     a = Flm_inv_sp(a, NULL, pp);
    2639          42 :     if (a) a = Flm_to_mod(a, pp);
    2640             :   }
    2641         112 :   return a;
    2642             : }
    2643             : 
    2644             : static GEN
    2645          42 : RgM_inv_FqM(GEN x, GEN pol, GEN p)
    2646             : {
    2647          42 :   pari_sp av = avma;
    2648          42 :   GEN b, T = RgX_to_FpX(pol, p);
    2649          42 :   if (signe(T) == 0) pari_err_OP("^",x,gen_m1);
    2650          42 :   b = FqM_inv(RgM_to_FqM(x, T, p), T, p);
    2651          42 :   if (!b) return gc_NULL(av);
    2652          28 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    2653             : }
    2654             : 
    2655             : #define code(t1,t2) ((t1 << 6) | t2)
    2656             : static GEN
    2657       54995 : RgM_inv_fast(GEN x)
    2658             : {
    2659             :   GEN p, pol;
    2660             :   long pa;
    2661       54995 :   long t = RgM_type(x, &p,&pol,&pa);
    2662       54995 :   switch(t)
    2663             :   {
    2664             :     case t_INT:    /* Fall back */
    2665       32130 :     case t_FRAC:   return RgM_inv_QM(x);
    2666         147 :     case t_FFELT:  return FFM_inv(x, pol);
    2667         112 :     case t_INTMOD: return RgM_inv_FpM(x, p);
    2668             :     case code(t_POLMOD, t_INTMOD):
    2669          42 :                    return RgM_inv_FqM(x, pol, p);
    2670       22564 :     default:       return gen_0;
    2671             :   }
    2672             : }
    2673             : #undef code
    2674             : 
    2675             : static GEN
    2676          49 : RgM_RgC_solve_FpC(GEN a, GEN b, GEN p)
    2677             : {
    2678          49 :   pari_sp av = avma;
    2679             :   ulong pp;
    2680          49 :   a = RgM_Fp_init(a, p, &pp);
    2681          49 :   switch(pp)
    2682             :   {
    2683             :   case 0:
    2684          14 :     b = RgC_to_FpC(b, p);
    2685          14 :     a = FpM_FpC_gauss(a,b,p);
    2686          14 :     return a ? gerepileupto(av, FpC_to_mod(a, p)): NULL;
    2687             :   case 2:
    2688          14 :     b = RgV_to_F2v(b);
    2689          14 :     a = F2m_F2c_gauss(a,b);
    2690          14 :     return a ? gerepileupto(av, F2c_to_mod(a)): NULL;
    2691             :   default:
    2692          21 :     b = RgV_to_Flv(b, pp);
    2693          21 :     a = Flm_Flc_gauss(a, b, pp);
    2694          21 :     return a ? gerepileupto(av, Flc_to_mod(a, pp)): NULL;
    2695             :   }
    2696             : }
    2697             : 
    2698             : static GEN
    2699          98 : RgM_solve_FpM(GEN a, GEN b, GEN p)
    2700             : {
    2701          98 :   pari_sp av = avma;
    2702             :   ulong pp;
    2703          98 :   a = RgM_Fp_init(a, p, &pp);
    2704          98 :   switch(pp)
    2705             :   {
    2706             :   case 0:
    2707          35 :     b = RgM_to_FpM(b, p);
    2708          35 :     a = FpM_gauss(a,b,p);
    2709          35 :     return a ? gerepileupto(av, FpM_to_mod(a, p)): NULL;
    2710             :   case 2:
    2711          21 :     b = RgM_to_F2m(b);
    2712          21 :     a = F2m_gauss(a,b);
    2713          21 :     return a ? gerepileupto(av, F2m_to_mod(a)): NULL;
    2714             :   default:
    2715          42 :     b = RgM_to_Flm(b, pp);
    2716          42 :     a = Flm_gauss(a,b,pp);
    2717          42 :     return a ? gerepileupto(av, Flm_to_mod(a, pp)): NULL;
    2718             :   }
    2719             : }
    2720             : 
    2721             : /* Gaussan Elimination. If a is square, return a^(-1)*b;
    2722             :  * if a has more rows than columns and b is NULL, return c such that c a = Id.
    2723             :  * a is a (not necessarily square) matrix
    2724             :  * b is a matrix or column vector, NULL meaning: take the identity matrix,
    2725             :  *   effectively returning the inverse of a
    2726             :  * If a and b are empty, the result is the empty matrix.
    2727             :  *
    2728             :  * li: number of rows of a and b
    2729             :  * aco: number of columns of a
    2730             :  * bco: number of columns of b (if matrix)
    2731             :  */
    2732             : static GEN
    2733      277531 : RgM_solve_basecase(GEN a, GEN b)
    2734             : {
    2735      277531 :   pari_sp av = avma;
    2736             :   long i, j, k, li, bco, aco;
    2737             :   int iscol;
    2738             :   pivot_fun pivot;
    2739             :   GEN p, u, data;
    2740             : 
    2741      277531 :   set_avma(av);
    2742             : 
    2743      277531 :   if (lg(a)-1 == 2 && nbrows(a) == 2) {
    2744             :     /* 2x2 matrix, start by inverting a */
    2745      103445 :     GEN u = gcoeff(a,1,1), v = gcoeff(a,1,2);
    2746      103445 :     GEN w = gcoeff(a,2,1), x = gcoeff(a,2,2);
    2747      103445 :     GEN D = gsub(gmul(u,x), gmul(v,w)), ainv;
    2748      103445 :     if (gequal0(D)) return NULL;
    2749      103445 :     ainv = mkmat2(mkcol2(x, gneg(w)), mkcol2(gneg(v), u));
    2750      103445 :     ainv = gmul(ainv, ginv(D));
    2751      103445 :     if (b) ainv = gmul(ainv, b);
    2752      103445 :     return gerepileupto(av, ainv);
    2753             :   }
    2754             : 
    2755      174086 :   if (!init_gauss(a, &b, &aco, &li, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    2756      174086 :   pivot = get_pivot_fun(a, a, &data);
    2757      174086 :   a = RgM_shallowcopy(a);
    2758      174086 :   bco = lg(b)-1;
    2759      174086 :   if(DEBUGLEVEL>4) err_printf("Entering gauss\n");
    2760             : 
    2761      174086 :   p = NULL; /* gcc -Wall */
    2762      567505 :   for (i=1; i<=aco; i++)
    2763             :   {
    2764             :     /* k is the line where we find the pivot */
    2765      567505 :     k = pivot(a, data, i, NULL);
    2766      567505 :     if (k > li) return NULL;
    2767      567491 :     if (k != i)
    2768             :     { /* exchange the lines s.t. k = i */
    2769      124588 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    2770      124588 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    2771             :     }
    2772      567491 :     p = gcoeff(a,i,i);
    2773      567491 :     if (i == aco) break;
    2774             : 
    2775     1203446 :     for (k=i+1; k<=li; k++)
    2776             :     {
    2777      810027 :       GEN m = gcoeff(a,k,i);
    2778      810027 :       if (!gequal0(m))
    2779             :       {
    2780      642903 :         m = gdiv(m,p);
    2781      642903 :         for (j=i+1; j<=aco; j++) _submul(gel(a,j),k,i,m);
    2782      642903 :         for (j=1;   j<=bco; j++) _submul(gel(b,j),k,i,m);
    2783             :       }
    2784             :     }
    2785      393419 :     if (gc_needed(av,1))
    2786             :     {
    2787          62 :       if(DEBUGMEM>1) pari_warn(warnmem,"gauss. i=%ld",i);
    2788          62 :       gerepileall(av,2, &a,&b);
    2789             :     }
    2790             :   }
    2791             : 
    2792      174072 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
    2793      174072 :   u = cgetg(bco+1,t_MAT);
    2794      174072 :   for (j=1; j<=bco; j++) gel(u,j) = get_col(a,gel(b,j),p,aco);
    2795      174072 :   return gerepilecopy(av, iscol? gel(u,1): u);
    2796             : }
    2797             : 
    2798             : static GEN
    2799      264796 : RgM_RgC_solve_fast(GEN x, GEN y)
    2800             : {
    2801             :   GEN p, pol;
    2802             :   long pa;
    2803      264796 :   long t = RgM_RgC_type(x, y, &p,&pol,&pa);
    2804      264796 :   switch(t)
    2805             :   {
    2806       13398 :     case t_INT:    return ZM_gauss(x, y);
    2807          42 :     case t_FRAC:   return QM_gauss(x, y);
    2808          49 :     case t_INTMOD: return RgM_RgC_solve_FpC(x, y, p);
    2809          56 :     case t_FFELT:  return FFM_FFC_gauss(x, y, pol);
    2810      251251 :     default:       return gen_0;
    2811             :   }
    2812             : }
    2813             : 
    2814             : static GEN
    2815      108550 : RgM_solve_fast(GEN x, GEN y)
    2816             : {
    2817             :   GEN p, pol;
    2818             :   long pa;
    2819      108550 :   long t = RgM_type2(x, y, &p,&pol,&pa);
    2820      108550 :   switch(t)
    2821             :   {
    2822      104666 :     case t_INT:    return ZM_gauss(x, y);
    2823           7 :     case t_FRAC:   return QM_gauss(x, y);
    2824          98 :     case t_INTMOD: return RgM_solve_FpM(x, y, p);
    2825          63 :     case t_FFELT:  return FFM_gauss(x, y, pol);
    2826        3716 :     default:       return gen_0;
    2827             :   }
    2828             : }
    2829             : 
    2830             : GEN
    2831      373346 : RgM_solve(GEN a, GEN b)
    2832             : {
    2833      373346 :   pari_sp av = avma;
    2834             :   GEN u;
    2835      373346 :   if (!b) return RgM_inv(a);
    2836      373346 :   u = typ(b)==t_MAT ? RgM_solve_fast(a, b): RgM_RgC_solve_fast(a, b);
    2837      373346 :   if (!u) { set_avma(av); return u; }
    2838      373248 :   if (u != gen_0) return u;
    2839      254967 :   return RgM_solve_basecase(a, b);
    2840             : }
    2841             : 
    2842             : GEN
    2843       54995 : RgM_inv(GEN a)
    2844             : {
    2845       54995 :   GEN b = RgM_inv_fast(a);
    2846       54981 :   return b==gen_0? RgM_solve_basecase(a, NULL): b;
    2847             : }
    2848             : 
    2849             : /* assume dim A >= 1, A invertible + upper triangular  */
    2850             : static GEN
    2851      359395 : RgM_inv_upper_ind(GEN A, long index)
    2852             : {
    2853      359395 :   long n = lg(A)-1, i = index, j;
    2854      359395 :   GEN u = zerocol(n);
    2855      359395 :   gel(u,i) = ginv(gcoeff(A,i,i));
    2856     1415985 :   for (i--; i>0; i--)
    2857             :   {
    2858     1056590 :     pari_sp av = avma;
    2859     1056590 :     GEN m = gneg(gmul(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2860     1056590 :     for (j=i+2; j<=n; j++) m = gsub(m, gmul(gcoeff(A,i,j),gel(u,j)));
    2861     1056590 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(A,i,i)));
    2862             :   }
    2863      359395 :   return u;
    2864             : }
    2865             : GEN
    2866       74326 : RgM_inv_upper(GEN A)
    2867             : {
    2868             :   long i, l;
    2869       74326 :   GEN B = cgetg_copy(A, &l);
    2870       74326 :   for (i = 1; i < l; i++) gel(B,i) = RgM_inv_upper_ind(A, i);
    2871       74326 :   return B;
    2872             : }
    2873             : 
    2874             : /* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal  */
    2875             : static GEN
    2876          98 : FpM_inv_upper_1_ind(GEN A, long index, GEN p)
    2877             : {
    2878          98 :   long n = lg(A)-1, i = index, j;
    2879          98 :   GEN u = zerocol(n);
    2880          98 :   gel(u,i) = gen_1;
    2881         567 :   for (i--; i>0; i--)
    2882             :   {
    2883         469 :     pari_sp av = avma;
    2884         469 :     GEN m = negi(mulii(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2885         469 :     for (j=i+2; j<=n; j++) m = subii(m, mulii(gcoeff(A,i,j),gel(u,j)));
    2886         469 :     gel(u,i) = gerepileuptoint(av, modii(m,p));
    2887             :   }
    2888          98 :   return u;
    2889             : }
    2890             : static GEN
    2891          14 : FpM_inv_upper_1(GEN A, GEN p)
    2892             : {
    2893             :   long i, l;
    2894          14 :   GEN B = cgetg_copy(A, &l);
    2895          14 :   for (i = 1; i < l; i++) gel(B,i) = FpM_inv_upper_1_ind(A, i, p);
    2896          14 :   return B;
    2897             : }
    2898             : /* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal,
    2899             :  * reduced mod p */
    2900             : static GEN
    2901          28 : Flm_inv_upper_1_ind(GEN A, long index, ulong p)
    2902             : {
    2903          28 :   long n = lg(A)-1, i = index, j;
    2904          28 :   GEN u = const_vecsmall(n, 0);
    2905          28 :   u[i] = 1;
    2906          28 :   if (SMALL_ULONG(p))
    2907          21 :     for (i--; i>0; i--)
    2908             :     {
    2909           7 :       ulong m = ucoeff(A,i,i+1) * uel(u,i+1); /* j = i+1 */
    2910           7 :       for (j=i+2; j<=n; j++)
    2911             :       {
    2912           0 :         if (m & HIGHMASK) m %= p;
    2913           0 :         m += ucoeff(A,i,j) * uel(u,j);
    2914             :       }
    2915           7 :       u[i] = Fl_neg(m % p, p);
    2916             :     }
    2917             :   else
    2918          21 :     for (i--; i>0; i--)
    2919             :     {
    2920           7 :       ulong m = Fl_mul(ucoeff(A,i,i+1),uel(u,i+1), p); /* j = i+1 */
    2921           7 :       for (j=i+2; j<=n; j++) m = Fl_add(m, Fl_mul(ucoeff(A,i,j),uel(u,j),p), p);
    2922           7 :       u[i] = Fl_neg(m, p);
    2923             :     }
    2924          28 :   return u;
    2925             : }
    2926             : static GEN
    2927          14 : F2m_inv_upper_1_ind(GEN A, long index)
    2928             : {
    2929          14 :   pari_sp av = avma;
    2930          14 :   long n = lg(A)-1, i = index, j;
    2931          14 :   GEN u = const_vecsmall(n, 0);
    2932          14 :   u[i] = 1;
    2933          21 :   for (i--; i>0; i--)
    2934             :   {
    2935           7 :     ulong m = F2m_coeff(A,i,i+1) & uel(u,i+1); /* j = i+1 */
    2936           7 :     for (j=i+2; j<=n; j++) m ^= F2m_coeff(A,i,j) & uel(u,j);
    2937           7 :     u[i] = m & 1;
    2938             :   }
    2939          14 :   return gerepileuptoleaf(av, Flv_to_F2v(u));
    2940             : }
    2941             : static GEN
    2942          14 : Flm_inv_upper_1(GEN A, ulong p)
    2943             : {
    2944             :   long i, l;
    2945          14 :   GEN B = cgetg_copy(A, &l);
    2946          14 :   for (i = 1; i < l; i++) gel(B,i) = Flm_inv_upper_1_ind(A, i, p);
    2947          14 :   return B;
    2948             : }
    2949             : static GEN
    2950           7 : F2m_inv_upper_1(GEN A)
    2951             : {
    2952             :   long i, l;
    2953           7 :   GEN B = cgetg_copy(A, &l);
    2954           7 :   for (i = 1; i < l; i++) gel(B,i) = F2m_inv_upper_1_ind(A, i);
    2955           7 :   return B;
    2956             : }
    2957             : 
    2958             : static GEN
    2959      990199 : split_realimag_col(GEN z, long r1, long r2)
    2960             : {
    2961      990199 :   long i, ru = r1+r2;
    2962      990199 :   GEN x = cgetg(ru+r2+1,t_COL), y = x + r2;
    2963     2972365 :   for (i=1; i<=r1; i++) {
    2964     1982166 :     GEN a = gel(z,i);
    2965     1982166 :     if (typ(a) == t_COMPLEX) a = gel(a,1); /* paranoia: a should be real */
    2966     1982166 :     gel(x,i) = a;
    2967             :   }
    2968     1640426 :   for (   ; i<=ru; i++) {
    2969      650227 :     GEN b, a = gel(z,i);
    2970      650227 :     if (typ(a) == t_COMPLEX) { b = gel(a,2); a = gel(a,1); } else b = gen_0;
    2971      650227 :     gel(x,i) = a;
    2972      650227 :     gel(y,i) = b;
    2973             :   }
    2974      990199 :   return x;
    2975             : }
    2976             : GEN
    2977      520210 : split_realimag(GEN x, long r1, long r2)
    2978             : {
    2979             :   long i,l; GEN y;
    2980      520210 :   if (typ(x) == t_COL) return split_realimag_col(x,r1,r2);
    2981      256975 :   y = cgetg_copy(x, &l);
    2982      256975 :   for (i=1; i<l; i++) gel(y,i) = split_realimag_col(gel(x,i), r1, r2);
    2983      256975 :   return y;
    2984             : }
    2985             : 
    2986             : /* assume M = (r1+r2) x (r1+2r2) matrix and y compatible vector or matrix
    2987             :  * r1 first lines of M,y are real. Solve the system obtained by splitting
    2988             :  * real and imaginary parts. */
    2989             : GEN
    2990      252531 : RgM_solve_realimag(GEN M, GEN y)
    2991             : {
    2992      252531 :   long l = lg(M), r2 = l - lgcols(M), r1 = l-1 - 2*r2;
    2993      252531 :   return RgM_solve(split_realimag(M, r1,r2),
    2994             :                    split_realimag(y, r1,r2));
    2995             : }
    2996             : 
    2997             : GEN
    2998         420 : gauss(GEN a, GEN b)
    2999             : {
    3000             :   GEN z;
    3001         420 :   long t = typ(b);
    3002         420 :   if (typ(a)!=t_MAT) pari_err_TYPE("gauss",a);
    3003         420 :   if (t!=t_COL && t!=t_MAT) pari_err_TYPE("gauss",b);
    3004         420 :   z = RgM_solve(a,b);
    3005         420 :   if (!z) pari_err_INV("gauss",a);
    3006         315 :   return z;
    3007             : }
    3008             : 
    3009             : static GEN
    3010      133618 : F2_get_col(GEN b, GEN d, long li, long aco)
    3011             : {
    3012      133618 :   long i, l = nbits2lg(aco);
    3013      133618 :   GEN u = cgetg(l, t_VECSMALL);
    3014      133618 :   u[1] = aco;
    3015     1838330 :   for (i = 1; i <= li; i++)
    3016     1704712 :     if (d[i]) /* d[i] can still be 0 if li > aco */
    3017             :     {
    3018     1635874 :       if (F2v_coeff(b, i))
    3019      525668 :         F2v_set(u, d[i]);
    3020             :       else
    3021     1110206 :         F2v_clear(u, d[i]);
    3022             :     }
    3023      133618 :   return u;
    3024             : }
    3025             : 
    3026             : /* destroy a, b */
    3027             : static GEN
    3028       20851 : F2m_gauss_sp(GEN a, GEN b)
    3029             : {
    3030       20851 :   long i, j, k, l, li, bco, aco = lg(a)-1;
    3031             :   GEN u, d;
    3032             : 
    3033       20851 :   if (!aco) return cgetg(1,t_MAT);
    3034       20851 :   li = gel(a,1)[1];
    3035       20851 :   d = zero_Flv(li);
    3036       20851 :   bco = lg(b)-1;
    3037      145817 :   for (i=1; i<=aco; i++)
    3038             :   {
    3039      124987 :     GEN ai = vecsmall_copy(gel(a,i));
    3040      124987 :     if (!d[i] && F2v_coeff(ai, i))
    3041       61639 :       k = i;
    3042             :     else
    3043       63348 :       for (k = 1; k <= li; k++) if (!d[k] && F2v_coeff(ai,k)) break;
    3044             :     /* found a pivot on row k */
    3045      124987 :     if (k > li) return NULL;
    3046      124966 :     d[k] = i;
    3047             : 
    3048             :     /* Clear k-th row but column-wise instead of line-wise */
    3049             :     /*  a_ij -= a_i1*a1j/a_11
    3050             :        line-wise grouping:  L_j -= a_1j/a_11*L_1
    3051             :        column-wise:         C_i -= a_i1/a_11*C_1
    3052             :     */
    3053      124966 :     F2v_clear(ai,k);
    3054     1743725 :     for (l=1; l<=aco; l++)
    3055             :     {
    3056     1618759 :       GEN al = gel(a,l);
    3057     1618759 :       if (F2v_coeff(al,k)) F2v_add_inplace(al,ai);
    3058             :     }
    3059     1761099 :     for (l=1; l<=bco; l++)
    3060             :     {
    3061     1636133 :       GEN bl = gel(b,l);
    3062     1636133 :       if (F2v_coeff(bl,k)) F2v_add_inplace(bl,ai);
    3063             :     }
    3064             :   }
    3065       20830 :   u = cgetg(bco+1,t_MAT);
    3066       20830 :   for (j = 1; j <= bco; j++) gel(u,j) = F2_get_col(gel(b,j), d, li, aco);
    3067       20830 :   return u;
    3068             : }
    3069             : 
    3070             : GEN
    3071          35 : F2m_gauss(GEN a, GEN b)
    3072             : {
    3073          35 :   pari_sp av = avma;
    3074          35 :   if (lg(a) == 1) return cgetg(1,t_MAT);
    3075          35 :   return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), F2m_copy(b)));
    3076             : }
    3077             : GEN
    3078          14 : F2m_F2c_gauss(GEN a, GEN b)
    3079             : {
    3080          14 :   pari_sp av = avma;
    3081          14 :   GEN z = F2m_gauss(a, mkmat(b));
    3082          14 :   if (!z) return gc_NULL(av);
    3083           7 :   if (lg(z) == 1) { set_avma(av); return cgetg(1,t_VECSMALL); }
    3084           7 :   return gerepileuptoleaf(av, gel(z,1));
    3085             : }
    3086             : 
    3087             : GEN
    3088          35 : F2m_inv(GEN a)
    3089             : {
    3090          35 :   pari_sp av = avma;
    3091          35 :   if (lg(a) == 1) return cgetg(1,t_MAT);
    3092          35 :   return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), matid_F2m(gel(a,1)[1])));
    3093             : }
    3094             : 
    3095             : /* destroy a, b */
    3096             : static GEN
    3097       51834 : Flm_gauss_sp_OK(GEN a, GEN b, ulong *detp, ulong p)
    3098             : {
    3099       51834 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3100       51834 :   ulong det = 1;
    3101             :   GEN u;
    3102             : 
    3103       51834 :   li = nbrows(a);
    3104       51834 :   bco = lg(b)-1;
    3105      355316 :   for (i=1; i<=aco; i++)
    3106             :   {
    3107             :     ulong invpiv;
    3108             :     /* Fl_get_col wants 0 <= a[i,j] < p for all i,j */
    3109      355316 :     for (k = 1; k < i; k++) ucoeff(a,k,i) %= p;
    3110      611567 :     for (k = i; k <= li; k++)
    3111             :     {
    3112      611560 :       ulong piv = ( ucoeff(a,k,i) %= p );
    3113      611560 :       if (piv)
    3114             :       {
    3115      355309 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3116      355309 :         if (detp)
    3117             :         {
    3118           0 :           if (det & HIGHMASK) det %= p;
    3119           0 :           det *= piv;
    3120             :         }
    3121      355309 :         break;
    3122             :       }
    3123             :     }
    3124             :     /* found a pivot on line k */
    3125      355316 :     if (k > li) return NULL;
    3126      355309 :     if (k != i)
    3127             :     { /* swap lines so that k = i */
    3128       99791 :       s = -s;
    3129       99791 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3130       99791 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3131             :     }
    3132      355309 :     if (i == aco) break;
    3133             : 
    3134      303482 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3135     2166569 :     for (k=i+1; k<=li; k++)
    3136             :     {
    3137     1863087 :       ulong m = ( ucoeff(a,k,i) %= p );
    3138     1863087 :       if (!m) continue;
    3139             : 
    3140      610245 :       m = Fl_mul(m, invpiv, p);
    3141      610245 :       if (m == 1) {
    3142      102467 :         for (j=i+1; j<=aco; j++) _Fl_add_OK(gel(a,j),k,i, p);
    3143      102467 :         for (j=1;   j<=bco; j++) _Fl_add_OK(gel(b,j),k,i, p);
    3144             :       } else {
    3145      507778 :         for (j=i+1; j<=aco; j++) _Fl_addmul_OK(gel(a,j),k,i,m, p);
    3146      507778 :         for (j=1;   j<=bco; j++) _Fl_addmul_OK(gel(b,j),k,i,m, p);
    3147             :       }
    3148             :     }
    3149             :   }
    3150       51827 :   if (detp)
    3151             :   {
    3152           0 :     det %=  p;
    3153           0 :     if (s < 0 && det) det = p - det;
    3154           0 :     *detp = det;
    3155             :   }
    3156       51827 :   u = cgetg(bco+1,t_MAT);
    3157       51827 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col_OK(a,gel(b,j), aco,p);
    3158       51827 :   return u;
    3159             : }
    3160             : 
    3161             : /* destroy a, b */
    3162             : static GEN
    3163      652430 : Flm_gauss_sp(GEN a, GEN b, ulong *detp, ulong p)
    3164             : {
    3165      652430 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3166      652430 :   ulong det = 1;
    3167             :   GEN u;
    3168             :   ulong pi;
    3169      652430 :   if (!aco) { if (detp) *detp = 1; return cgetg(1,t_MAT); }
    3170      652430 :   if (SMALL_ULONG(p)) return Flm_gauss_sp_OK(a, b, detp, p);
    3171      600596 :   pi = get_Fl_red(p);
    3172      600596 :   li = nbrows(a);
    3173      600596 :   bco = lg(b)-1;
    3174     1566906 :   for (i=1; i<=aco; i++)
    3175             :   {
    3176             :     ulong invpiv;
    3177             :     /* Fl_get_col wants 0 <= a[i,j] < p for all i,j */
    3178     1626059 :     for (k = i; k <= li; k++)
    3179             :     {
    3180     1626052 :       ulong piv = ucoeff(a,k,i);
    3181     1626052 :       if (piv)
    3182             :       {
    3183     1566899 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3184     1566899 :         if (detp) det = Fl_mul_pre(det, piv, p, pi);
    3185     1566899 :         break;
    3186             :       }
    3187             :     }
    3188             :     /* found a pivot on line k */
    3189     1566906 :     if (k > li) return NULL;
    3190     1566899 :     if (k != i)
    3191             :     { /* swap lines so that k = i */
    3192       51148 :       s = -s;
    3193       51148 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3194       51148 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3195             :     }
    3196     1566899 :     if (i == aco) break;
    3197             : 
    3198      966310 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3199     2471638 :     for (k=i+1; k<=li; k++)
    3200             :     {
    3201     1505328 :       ulong m = ucoeff(a,k,i);
    3202     1505328 :       if (!m) continue;
    3203             : 
    3204     1181672 :       m = Fl_mul_pre(m, invpiv, p, pi);
    3205     1181672 :       if (m == 1) {
    3206       31790 :         for (j=i+1; j<=aco; j++) _Fl_add(gel(a,j),k,i, p);
    3207       31790 :         for (j=1;   j<=bco; j++) _Fl_add(gel(b,j),k,i, p);
    3208             :       } else {
    3209     1149882 :         for (j=i+1; j<=aco; j++) _Fl_addmul(gel(a,j),k,i,m, p, pi);
    3210     1149882 :         for (j=1;   j<=bco; j++) _Fl_addmul(gel(b,j),k,i,m, p, pi);
    3211             :       }
    3212             :     }
    3213             :   }
    3214      600589 :   if (detp)
    3215             :   {
    3216           0 :     if (s < 0 && det) det = p - det;
    3217           0 :     *detp = det;
    3218             :   }
    3219      600589 :   u = cgetg(bco+1,t_MAT);
    3220      600589 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col(a,gel(b,j), aco,p);
    3221      600589 :   return u;
    3222             : }
    3223             : 
    3224             : static GEN
    3225      188255 : Flm_gauss_from_CUP(GEN b, GEN R, GEN C, GEN U, GEN P, ulong p, ulong *detp)
    3226             : {
    3227      188255 :   GEN Y = Flm_rsolve_lower_unit(rowpermute(C, R), rowpermute(b, R), p);
    3228      188254 :   GEN X = rowpermute(Flm_rsolve_upper(U, Y, p), perm_inv(P));
    3229      188254 :   if (detp)
    3230             :   {
    3231      145335 :     ulong d = perm_sign(P) == 1? 1: p-1;
    3232      145331 :     long i, r = lg(R);
    3233     1401606 :     for (i = 1; i < r; i++)
    3234     1256271 :       d = Fl_mul(d, ucoeff(U, i, i), p);
    3235      145335 :     *detp = d;
    3236             :   }
    3237      188254 :   return X;
    3238             : }
    3239             : 
    3240             : static GEN
    3241       42933 : Flm_gauss_CUP(GEN a, GEN b, ulong *detp, ulong p) {
    3242             :   GEN R, C, U, P;
    3243       42933 :   long n = lg(a) - 1, r;
    3244       42933 :   if (nbrows(a) < n || (r = Flm_CUP(a, &R, &C, &U, &P, p)) < n)
    3245          14 :     return NULL;
    3246       42919 :   return Flm_gauss_from_CUP(b, R, C, U, P, p, detp);
    3247             : }
    3248             : 
    3249             : GEN
    3250          63 : Flm_gauss(GEN a, GEN b, ulong p) {
    3251          63 :   pari_sp av = avma;
    3252             :   GEN x;
    3253          63 :   if (lg(a) - 1 >= Flm_CUP_LIMIT)
    3254          14 :     x = Flm_gauss_CUP(a, b, NULL, p);
    3255             :   else {
    3256          49 :     a = RgM_shallowcopy(a);
    3257          49 :     b = RgM_shallowcopy(b);
    3258          49 :     x = Flm_gauss_sp(a, b, NULL, p);
    3259             :   }
    3260          63 :   if (!x) return gc_NULL(av);
    3261          49 :   return gerepileupto(av, x);
    3262             : }
    3263             : 
    3264             : static GEN
    3265      653649 : Flm_inv_i(GEN a, ulong *detp, ulong p, long inplace) {
    3266      653649 :   pari_sp av = avma;
    3267      653649 :   long n = lg(a) - 1;
    3268             :   GEN b, x;
    3269      653649 :   if (n == 0) return cgetg(1, t_MAT);
    3270      653649 :   b = matid_Flm(nbrows(a));
    3271      653649 :   if (n >= Flm_CUP_LIMIT)
    3272       42919 :     x = Flm_gauss_CUP(a, b, detp, p);
    3273             :   else {
    3274      610730 :     if (!inplace)
    3275       10178 :       a = RgM_shallowcopy(a);
    3276      610730 :     x = Flm_gauss_sp(a, b, detp, p);
    3277             :   }
    3278      653649 :   if (!x) return gc_NULL(av);
    3279      653635 :   return gerepileupto(av, x);
    3280             : }
    3281             : 
    3282             : static GEN
    3283      632152 : Flm_inv_sp(GEN a, ulong *detp, ulong p) {
    3284      632152 :   return Flm_inv_i(a, detp, p, 1);
    3285             : }
    3286             : 
    3287             : GEN
    3288       21497 : Flm_inv(GEN a, ulong p) {
    3289       21497 :   return Flm_inv_i(a, NULL, p, 0);
    3290             : }
    3291             : 
    3292             : GEN
    3293          21 : Flm_Flc_gauss(GEN a, GEN b, ulong p) {
    3294          21 :   pari_sp av = avma;
    3295          21 :   GEN z = Flm_gauss(a, mkmat(b), p);
    3296          21 :   if (!z) return gc_NULL(av);
    3297          14 :   if (lg(z) == 1) { set_avma(av); return cgetg(1,t_VECSMALL); }
    3298          14 :   return gerepileuptoleaf(av, gel(z,1));
    3299             : }
    3300             : 
    3301             : GEN
    3302      145348 : Flm_adjoint(GEN A, ulong p)
    3303             : {
    3304      145348 :   pari_sp av = avma;
    3305             :   GEN R, C, U, P, C1, U1, v, c, d;
    3306      145348 :   long r, i, q, n = lg(A)-1, m;
    3307             :   ulong D;
    3308      145348 :   if (n == 0) return cgetg(1, t_MAT);
    3309      145348 :   r = Flm_CUP(A, &R, &C, &U, &P, p);
    3310      145350 :   m = nbrows(A);
    3311      145350 :   if (r == n)
    3312             :   {
    3313      145336 :     GEN X = Flm_gauss_from_CUP(matid_Flm(m), R, C, U, P, p, &D);
    3314      145332 :     return gerepileupto(av, Flm_Fl_mul(X, D, p));
    3315             :   }
    3316          14 :   if (r < n-1) return zero_Flm(n, m);
    3317          28 :   for (q = n, i = 1; i < n; i++)
    3318          14 :     if (R[i] != i) { q = i; break; }
    3319          14 :   C1 = matslice(C, 1, q-1, 1, q-1);
    3320          14 :   c = vecslice(Flm_row(C, q), 1, q-1);
    3321          14 :   c = Flm_lsolve_lower_unit(C1, Flm_transpose(mkmat(c)), p);
    3322          14 :   d = cgetg(m+1, t_VECSMALL);
    3323          14 :   for (i=1; i<q; i++)    uel(d,i) = ucoeff(c,1,i);
    3324          14 :   uel(d,q) = p-1;
    3325          14 :   for (i=q+1; i<=m; i++) uel(d,i) = 0;
    3326          14 :   U1 = vecslice(U, 1, n-1);
    3327          14 :   v = gel(Flm_rsolve_upper(U1, mkmat(gel(U,n)), p),1);
    3328          14 :   v = vecsmall_append(v, p-1);
    3329          14 :   D = perm_sign(P) != (odd(q+n)?-1:1) ? p-1 : 1;
    3330          28 :   for (i = 1; i <= n-1; i++)
    3331          14 :     D = Fl_mul(D, ucoeff(U1, i, i), p);
    3332          14 :   d = Flv_Fl_mul(d, D, p);
    3333          14 :   return rowpermute(Flc_Flv_mul(v, d, p),perm_inv(P));
    3334             : }
    3335             : 
    3336             : static GEN
    3337        3982 : FpM_gauss_gen(GEN a, GEN b, GEN p)
    3338             : {
    3339             :   void *E;
    3340        3982 :   const struct bb_field *S = get_Fp_field(&E,p);
    3341        3982 :   return gen_Gauss(a,b, E, S);
    3342             : }
    3343             : /* a an FpM, lg(a)>1; b an FpM or NULL (replace by identity) */
    3344             : static GEN
    3345       66414 : FpM_gauss_i(GEN a, GEN b, GEN p, ulong *pp)
    3346             : {
    3347       66414 :   long n = nbrows(a);
    3348       66414 :   a = FpM_init(a,p,pp);
    3349       66414 :   switch(*pp)
    3350             :   {
    3351             :   case 0:
    3352        3982 :     if (!b) b = matid(n);
    3353        3982 :     return FpM_gauss_gen(a,b,p);
    3354             :   case 2:
    3355       20781 :     if (b) b = ZM_to_F2m(b); else b = matid_F2m(n);
    3356       20781 :     return F2m_gauss_sp(a,b);
    3357             :   default:
    3358       41651 :     if (b) b = ZM_to_Flm(b, *pp); else b = matid_Flm(n);
    3359       41651 :     return Flm_gauss_sp(a,b, NULL, *pp);
    3360             :   }
    3361             : }
    3362             : GEN
    3363          35 : FpM_gauss(GEN a, GEN b, GEN p)
    3364             : {
    3365          35 :   pari_sp av = avma;
    3366             :   ulong pp;
    3367             :   GEN u;
    3368          35 :   if (lg(a) == 1 || lg(b)==1) return cgetg(1, t_MAT);
    3369          35 :   u = FpM_gauss_i(a, b, p, &pp);
    3370          35 :   if (!u) return gc_NULL(av);
    3371          28 :   switch(pp)
    3372             :   {
    3373          28 :   case 0: return gerepilecopy(av, u);
    3374           0 :   case 2:  u = F2m_to_ZM(u); break;
    3375           0 :   default: u = Flm_to_ZM(u); break;
    3376             :   }
    3377           0 :   return gerepileupto(av, u);
    3378             : }
    3379             : GEN
    3380       66365 : FpM_inv(GEN a, GEN p)
    3381             : {
    3382       66365 :   pari_sp av = avma;
    3383             :   ulong pp;
    3384             :   GEN u;
    3385       66365 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    3386       66365 :   u = FpM_gauss_i(a, NULL, p, &pp);
    3387       66365 :   if (!u) return gc_NULL(av);
    3388       66351 :   switch(pp)
    3389             :   {
    3390        3926 :   case 0: return gerepilecopy(av, u);
    3391       20774 :   case 2:  u = F2m_to_ZM(u); break;
    3392       41651 :   default: u = Flm_to_ZM(u); break;
    3393             :   }
    3394       62425 :   return gerepileupto(av, u);
    3395             : }
    3396             : 
    3397             : GEN
    3398          14 : FpM_FpC_gauss(GEN a, GEN b, GEN p)
    3399             : {
    3400          14 :   pari_sp av = avma;
    3401             :   ulong pp;
    3402             :   GEN u;
    3403          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3404          14 :   u = FpM_gauss_i(a, mkmat(b), p, &pp);
    3405          14 :   if (!u) return gc_NULL(av);
    3406          14 :   switch(pp)
    3407             :   {
    3408          14 :   case 0: return gerepilecopy(av, gel(u,1));
    3409           0 :   case 2:  u = F2c_to_ZC(gel(u,1)); break;
    3410           0 :   default: u = Flc_to_ZC(gel(u,1)); break;
    3411             :   }
    3412           0 :   return gerepileupto(av, u);
    3413             : }
    3414             : 
    3415             : static GEN
    3416          56 : FlxqM_gauss_gen(GEN a, GEN b, GEN T, ulong p)
    3417             : {
    3418             :   void *E;
    3419          56 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    3420          56 :   return gen_Gauss(a, b, E, S);
    3421             : }
    3422             : 
    3423             : static GEN
    3424          35 : FlxqM_gauss_CUP(GEN a, GEN b, GEN T, ulong p) {
    3425             :   GEN R, C, U, P, Y;
    3426          35 :   long n = lg(a) - 1, r;
    3427          35 :   if (nbrows(a) < n || (r = FlxqM_CUP(a, &R, &C, &U, &P, T, p)) < n)
    3428          14 :     return NULL;
    3429          21 :   Y = FlxqM_rsolve_lower_unit(rowpermute(C, R),
    3430             :                               rowpermute(b, R), T, p);
    3431          21 :   return rowpermute(FlxqM_rsolve_upper(U, Y, T, p),
    3432             :                     perm_inv(P));
    3433             : }
    3434             : 
    3435             : static GEN
    3436          91 : FlxqM_gauss_i(GEN a, GEN b, GEN T, ulong p) {
    3437          91 :   if (lg(a) - 1 >= FlxqM_CUP_LIMIT)
    3438          35 :     return FlxqM_gauss_CUP(a, b, T, p);
    3439          56 :   return FlxqM_gauss_gen(a, b, T, p);
    3440             : }
    3441             : 
    3442             : GEN
    3443          21 : FlxqM_gauss(GEN a, GEN b, GEN T, ulong p)
    3444             : {
    3445          21 :   pari_sp av = avma;
    3446          21 :   long n = lg(a)-1;
    3447             :   GEN u;
    3448          21 :   if (!n || lg(b)==1) { set_avma(av); return cgetg(1, t_MAT); }
    3449          21 :   u = FlxqM_gauss_i(a, b, T, p);
    3450          21 :   if (!u) return gc_NULL(av);
    3451          14 :   return gerepilecopy(av, u);
    3452             : }
    3453             : GEN
    3454          56 : FlxqM_inv(GEN a, GEN T, ulong p)
    3455             : {
    3456          56 :   pari_sp av = avma;
    3457             :   GEN u;
    3458          56 :   if (lg(a) == 1) { set_avma(av); return cgetg(1, t_MAT); }
    3459          56 :   u = FlxqM_gauss_i(a, matid_FlxqM(nbrows(a),T,p), T,p);
    3460          56 :   if (!u) return gc_NULL(av);
    3461          42 :   return gerepilecopy(av, u);
    3462             : }
    3463             : GEN
    3464          14 : FlxqM_FlxqC_gauss(GEN a, GEN b, GEN T, ulong p)
    3465             : {
    3466          14 :   pari_sp av = avma;
    3467             :   GEN u;
    3468          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3469          14 :   u = FlxqM_gauss_i(a, mkmat(b), T, p);
    3470          14 :   if (!u) return gc_NULL(av);
    3471           7 :   return gerepilecopy(av, gel(u,1));
    3472             : }
    3473             : 
    3474             : static GEN
    3475         133 : FqM_gauss_gen(GEN a, GEN b, GEN T, GEN p)
    3476             : {
    3477             :   void *E;
    3478         133 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    3479         133 :   return gen_Gauss(a,b,E,S);
    3480             : }
    3481             : GEN
    3482          21 : FqM_gauss(GEN a, GEN b, GEN T, GEN p)
    3483             : {
    3484          21 :   pari_sp av = avma;
    3485             :   GEN u;
    3486             :   long n;
    3487          21 :   if (!T) return FpM_gauss(a,b,p);
    3488          21 :   n = lg(a)-1; if (!n || lg(b)==1) return cgetg(1, t_MAT);
    3489          21 :   u = FqM_gauss_gen(a,b,T,p);
    3490          21 :   if (!u) return gc_NULL(av);
    3491          14 :   return gerepilecopy(av, u);
    3492             : }
    3493             : GEN
    3494          98 : FqM_inv(GEN a, GEN T, GEN p)
    3495             : {
    3496          98 :   pari_sp av = avma;
    3497             :   GEN u;
    3498          98 :   if (!T) return FpM_inv(a,p);
    3499          98 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    3500          98 :   u = FqM_gauss_gen(a,matid(nbrows(a)),T,p);
    3501          98 :   if (!u) return gc_NULL(av);
    3502          70 :   return gerepilecopy(av, u);
    3503             : }
    3504             : GEN
    3505          14 : FqM_FqC_gauss(GEN a, GEN b, GEN T, GEN p)
    3506             : {
    3507          14 :   pari_sp av = avma;
    3508             :   GEN u;
    3509          14 :   if (!T) return FpM_FpC_gauss(a,b,p);
    3510          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3511          14 :   u = FqM_gauss_gen(a,mkmat(b),T,p);
    3512          14 :   if (!u) return gc_NULL(av);
    3513           7 :   return gerepilecopy(av, gel(u,1));
    3514             : }
    3515             : 
    3516             : static GEN
    3517      632103 : ZlM_gauss_ratlift(GEN a, GEN b, ulong p, long e, GEN C)
    3518             : {
    3519      632103 :   pari_sp av = avma, av2;
    3520             :   GEN bb, xi, xb, pi, P, B, r;
    3521      632103 :   long i, k = 2;
    3522      632103 :   if (!C) {
    3523           0 :     C = Flm_inv(ZM_to_Flm(a, p), p);
    3524           0 :     if (!C) pari_err_INV("ZlM_gauss", a);
    3525             :   }
    3526      632103 :   pi = P = utoipos(p);
    3527      632103 :   av2 = avma;
    3528      632103 :   xi = Flm_mul(C, ZM_to_Flm(b, p), p);
    3529      632103 :   xb = Flm_to_ZM(xi);
    3530      632103 :   bb = b;
    3531     1245158 :   for (i = 2; i <= e; i++)
    3532             :   {
    3533      671839 :     bb = ZM_Z_divexact(ZM_sub(bb, ZM_nm_mul(a, xi)), P);
    3534      671839 :     if (gc_needed(av,2))
    3535             :     {
    3536          68 :       if(DEBUGMEM>1) pari_warn(warnmem,"ZlM_gauss. i=%ld/%ld",i,e);
    3537          68 :       gerepileall(av2,3, &pi,&bb,&xb);
    3538             :     }
    3539      671839 :     xi = Flm_mul(C, ZM_to_Flm(bb, p), p);
    3540      671839 :     xb = ZM_add(xb, nm_Z_mul(xi, pi));
    3541      671839 :     pi = muliu(pi, p); /* = p^(i-1) */
    3542      671839 :     if (i==k && i < e)
    3543             :     {
    3544      197663 :       k *= 2;
    3545      197663 :       B = sqrti(shifti(pi,-1));
    3546      197663 :       r = FpM_ratlift(xb, pi, B, B, NULL);
    3547      197663 :       if (r)
    3548             :       {
    3549       98752 :         GEN dr, nr = Q_remove_denom(r,&dr);
    3550       98752 :         if (ZM_equal(ZM_mul(a,nr), dr? ZM_Z_mul(b,dr): b))
    3551             :         {
    3552       58784 :           if (DEBUGLEVEL>=4)
    3553           0 :             err_printf("ZlM_gauss: early solution: %ld/%ld\n",i,e);
    3554       58784 :           return gerepilecopy(av, r);
    3555             :         }
    3556             :       }
    3557             :     }
    3558             :   }
    3559      573319 :   B = sqrti(shifti(pi,-1));
    3560      573319 :   return gerepileupto(av, FpM_ratlift(xb, pi, B, B, NULL));
    3561             : }
    3562             : 
    3563             : /* Dixon p-adic lifting algorithm.
    3564             :  * Numer. Math. 40, 137-141 (1982), DOI: 10.1007/BF01459082 */
    3565             : GEN
    3566      632201 : ZM_gauss(GEN a, GEN b0)
    3567             : {
    3568      632201 :   pari_sp av = avma, av2;
    3569             :   int iscol;
    3570             :   long n, ncol, i, m, elim;
    3571             :   ulong p;
    3572      632201 :   GEN C, delta, nb, nmin, res, b = b0;
    3573             :   forprime_t S;
    3574             : 
    3575      632201 :   if (!init_gauss(a, &b, &n, &ncol, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    3576      632131 :   nb = gen_0; ncol = lg(b);
    3577     1508721 :   for (i = 1; i < ncol; i++)
    3578             :   {
    3579      876590 :     GEN ni = gnorml2(gel(b, i));
    3580      876590 :     if (cmpii(nb, ni) < 0) nb = ni;
    3581             :   }
    3582      632131 :   if (!signe(nb)) { set_avma(av); return gcopy(b0); }
    3583      632131 :   delta = gen_1; nmin = nb;
    3584     2506869 :   for (i = 1; i <= n; i++)
    3585             :   {
    3586     1874738 :     GEN ni = gnorml2(gel(a, i));
    3587     1874738 :     if (cmpii(ni, nmin) < 0)
    3588             :     {
    3589       97898 :       delta = mulii(delta, nmin); nmin = ni;
    3590             :     }
    3591             :     else
    3592     1776840 :       delta = mulii(delta, ni);
    3593             :   }
    3594      632131 :   if (!signe(nmin)) return NULL;
    3595      632110 :   elim = expi(delta)+1;
    3596      632110 :   av2 = avma;
    3597      632110 :   init_modular_big(&S);
    3598             :   for(;;)
    3599             :   {
    3600      632110 :     p = u_forprime_next(&S);
    3601      632110 :     C = Flm_inv_sp(ZM_to_Flm(a, p), NULL, p);
    3602      632110 :     if (C) break;
    3603           7 :     elim -= expu(p);
    3604           7 :     if (elim < 0) return NULL;
    3605           0 :     set_avma(av2);
    3606             :   }
    3607             :   /* N.B. Our delta/lambda are SQUARES of those in the paper
    3608             :    * log(delta lambda) / log p, where lambda is 3+sqrt(5) / 2,
    3609             :    * whose log is < 1, hence + 1 (to cater for rounding errors) */
    3610     1264206 :   m = (long)ceil((rtodbl(logr_abs(itor(delta,LOWDEFAULTPREC))) + 1)
    3611      632103 :                  / log((double)p));
    3612      632103 :   res = ZlM_gauss_ratlift(a, b, p, m, C);
    3613      632103 :   if (iscol) return gerepilecopy(av, gel(res, 1));
    3614      112022 :   return gerepileupto(av, res);
    3615             : }
    3616             : 
    3617             : /* same as above, M rational */
    3618             : GEN
    3619        1106 : QM_gauss(GEN M, GEN B)
    3620             : {
    3621        1106 :   pari_sp av = avma;
    3622             :   GEN K, MB;
    3623        1106 :   MB = Q_primitive_part(mkvec2(M,B), NULL);
    3624        1106 :   K = ZM_gauss(gel(MB,1), gel(MB,2));
    3625        1106 :   return gerepileupto(av, K);
    3626             : }
    3627             : 
    3628             : static GEN
    3629      133501 : ZM_inv_slice(GEN A, GEN P, GEN *mod)
    3630             : {
    3631      133501 :   pari_sp av = avma;
    3632      133501 :   long i, n = lg(P)-1;
    3633             :   GEN H, T;
    3634      133501 :   if (n == 1)
    3635             :   {
    3636      132378 :     ulong p = uel(P,1);
    3637      132378 :     GEN Hp, a = ZM_to_Flm(A, p);
    3638      132377 :     Hp = Flm_adjoint(a, p);
    3639      132376 :     Hp = gerepileupto(av, Flm_to_ZM(Hp));
    3640      132376 :     *mod = utoi(p); return Hp;
    3641             :   }
    3642        1123 :   T = ZV_producttree(P);
    3643        1123 :   A = ZM_nv_mod_tree(A, P, T);
    3644        1123 :   H = cgetg(n+1, t_VEC);
    3645        4024 :   for(i=1; i <= n; i++)
    3646        2901 :     gel(H,i) = Flm_adjoint(gel(A, i), uel(P,i));
    3647        1123 :   H = nmV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    3648        1123 :   *mod = gmael(T, lg(T)-1, 1);
    3649        1123 :   gerepileall(av, 2, &H, mod);
    3650        1123 :   return H;
    3651             : }
    3652             : 
    3653             : static GEN
    3654      112890 : RgM_true_Hadamard(GEN a)
    3655             : {
    3656      112890 :   pari_sp av = avma;
    3657      112890 :   long n = lg(a)-1, i;
    3658             :   GEN B;
    3659      112890 :   if (n == 0) return gen_1;
    3660      112890 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    3661      112890 :   B = gnorml2(gel(a,1));
    3662      112890 :   for (i = 2; i <= n; i++) B = gmul(B, gnorml2(gel(a,i)));
    3663      112890 :   return gerepileuptoint(av, ceil_safe(sqrtr(B)));
    3664             : }
    3665             : 
    3666             : GEN
    3667      133501 : ZM_inv_worker(GEN P, GEN A)
    3668             : {
    3669      133501 :   GEN V = cgetg(3, t_VEC);
    3670      133501 :   gel(V,1) = ZM_inv_slice(A, P, &gel(V,2));
    3671      133497 :   return V;
    3672             : }
    3673             : 
    3674             : static GEN
    3675        4991 : ZM_inv0(GEN A, GEN *pden)
    3676             : {
    3677        4991 :   if (pden) *pden = gen_1;
    3678        4991 :   (void)A; return cgetg(1, t_MAT);
    3679             : }
    3680             : static GEN
    3681       22747 : ZM_inv1(GEN A, GEN *pden)
    3682             : {
    3683       22747 :   GEN a = gcoeff(A,1,1);
    3684       22747 :   long s = signe(a);
    3685       22747 :   if (!s) return NULL;
    3686       22747 :   if (pden) *pden = absi(a);
    3687       22747 :   retmkmat(mkcol(s == 1? gen_1: gen_m1));
    3688             : }
    3689             : static GEN
    3690       45433 : ZM_inv2(GEN A, GEN *pden)
    3691             : {
    3692             :   GEN a, b, c, d, D, cA;
    3693             :   long s;
    3694       45433 :   A = Q_primitive_part(A, &cA);
    3695       45433 :   a = gcoeff(A,1,1); b = gcoeff(A,1,2);
    3696       45433 :   c = gcoeff(A,2,1); d = gcoeff(A,2,2);
    3697       45433 :   D = subii(mulii(a,d), mulii(b,c)); /* left on stack */
    3698       45433 :   s = signe(D);
    3699       45433 :   if (!s) return NULL;
    3700       45433 :   if (s < 0) D = negi(D);
    3701       45433 :   if (pden) *pden = mul_denom(D, cA);
    3702       45433 :   if (s > 0)
    3703       23943 :     retmkmat2(mkcol2(icopy(d), negi(c)), mkcol2(negi(b), icopy(a)));
    3704             :   else
    3705       21490 :     retmkmat2(mkcol2(negi(d), icopy(c)), mkcol2(icopy(b), negi(a)));
    3706             : }
    3707             : 
    3708             : /* to be used when denom(M^(-1)) << det(M) and a sharp multiple is
    3709             :  * not available. Return H primitive such that M*H = den*Id */
    3710             : GEN
    3711           0 : ZM_inv_ratlift(GEN M, GEN *pden)
    3712             : {
    3713           0 :   pari_sp av2, av = avma;
    3714             :   GEN Hp, q, H;
    3715             :   ulong p;
    3716           0 :   long m = lg(M)-1;
    3717             :   forprime_t S;
    3718             :   pari_timer ti;
    3719             : 
    3720           0 :   if (m == 0) return ZM_inv0(M,pden);
    3721           0 :   if (m == 1 && nbrows(M)==1) return ZM_inv1(M,pden);
    3722           0 :   if (m == 2 && nbrows(M)==2) return ZM_inv2(M,pden);
    3723             : 
    3724           0 :   if (DEBUGLEVEL>5) timer_start(&ti);
    3725           0 :   init_modular_big(&S);
    3726           0 :   av2 = avma;
    3727           0 :   H = NULL;
    3728           0 :   while ((p = u_forprime_next(&S)))
    3729             :   {
    3730             :     GEN Mp, B, Hr;
    3731           0 :     Mp = ZM_to_Flm(M,p);
    3732           0 :     Hp = Flm_inv_sp(Mp, NULL, p);
    3733           0 :     if (!Hp) continue;
    3734           0 :     if (!H)
    3735             :     {
    3736           0 :       H = ZM_init_CRT(Hp, p);
    3737           0 :       q = utoipos(p);
    3738             :     }
    3739             :     else
    3740           0 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3741           0 :     B = sqrti(shifti(q,-1));
    3742           0 :     Hr = FpM_ratlift(H,q,B,B,NULL);
    3743           0 :     if (DEBUGLEVEL>5)
    3744           0 :       timer_printf(&ti,"ZM_inv mod %lu (ratlift=%ld)", p,!!Hr);
    3745           0 :     if (Hr) {/* DONE ? */
    3746           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    3747           0 :       if (ZM_isscalar(ZM_mul(Hl, M), *pden)) { H = Hl; break; }
    3748             :     }
    3749             : 
    3750           0 :     if (gc_needed(av,2))
    3751             :     {
    3752           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv_ratlift");
    3753           0 :       gerepileall(av2, 2, &H, &q);
    3754             :     }
    3755             :   }
    3756           0 :   if (!*pden) *pden = gen_1;
    3757           0 :   gerepileall(av, 2, &H, pden);
    3758           0 :   return H;
    3759             : }
    3760             : 
    3761             : static GEN
    3762      120492 : ZM_adj_ratlift(GEN A, GEN H, GEN mod)
    3763             : {
    3764             :   GEN B;
    3765      120492 :   GEN D = ZMrow_ZC_mul(H, gel(A,1), 1);
    3766      120492 :   GEN g = gcdii(D, mod);
    3767      120492 :   if (!equali1(g))
    3768             :   {
    3769          14 :     mod = diviiexact(mod, g);
    3770          14 :     H = FpM_red(H, mod);
    3771             :   }
    3772      120492 :   D = Fp_inv(Fp_red(D, mod), mod);
    3773      120492 :   H = FpM_Fp_mul(H, D, mod);
    3774      120492 :   B = sqrti(shifti(mod,-1));
    3775      120492 :   return FpM_ratlift(H, mod, B, B, NULL);
    3776             : }
    3777             : 
    3778             : GEN
    3779      186068 : ZM_inv(GEN A, GEN *pden)
    3780             : {
    3781      186068 :   pari_sp av = avma;
    3782      186068 :   long m = lg(A)-1, n, k1 = 1, k2;
    3783      186068 :   GEN H = NULL, D, H1 = NULL, mod1 = NULL, worker;
    3784      186068 :   ulong bnd, mask, p = 0;
    3785             :   pari_timer ti;
    3786             : 
    3787      186068 :   if (m == 0) return ZM_inv0(A,pden);
    3788      181077 :   if (pden) *pden = gen_1;
    3789      181077 :   if (nbrows(A) < m) return NULL;
    3790      181070 :   if (m == 1 && nbrows(A)==1) return ZM_inv1(A,pden);
    3791      158323 :   if (m == 2 && nbrows(A)==2) return ZM_inv2(A,pden);
    3792             : 
    3793      112890 :   if (DEBUGLEVEL>=5) timer_start(&ti);
    3794      112890 :   bnd = expi(RgM_true_Hadamard(A));
    3795      112890 :   worker = strtoclosure("_ZM_inv_worker", 1, A);
    3796      112890 :   gen_inccrt("ZM_inv_r", worker, NULL, k1, m, &p, &H1, &mod1, nmV_chinese_center, FpM_center);
    3797      112890 :   n = (bnd+1)/expu(p)+1;
    3798      112890 :   if (DEBUGLEVEL>=5) timer_printf(&ti,"inv (%ld/%ld primes)", k1, n);
    3799      112890 :   mask = quadratic_prec_mask(n);
    3800      112890 :   for (k2 = 0;;)
    3801       16604 :   {
    3802             :     GEN Hr;
    3803      129494 :     if (k2 > 0)
    3804             :     {
    3805       14187 :       gen_inccrt("ZM_inv_r", worker, NULL, k2, m, &p, &H1, &mod1,nmV_chinese_center,FpM_center);
    3806       14187 :       k1 += k2;
    3807       14187 :       if (DEBUGLEVEL>=5) timer_printf(&ti,"CRT (%ld/%ld primes)", k1, n);
    3808             :     }
    3809      129494 :     if (mask == 1) break;
    3810      120492 :     k2 = (mask&1UL) ? k1-1: k1;
    3811      120492 :     mask >>= 1;
    3812             : 
    3813      120492 :     Hr = ZM_adj_ratlift(A, H1, mod1);
    3814      120492 :     if (DEBUGLEVEL>=5) timer_printf(&ti,"ratlift (%ld/%ld primes)", k1, n);
    3815      120492 :     if (Hr) {/* DONE ? */
    3816             :       GEN den;
    3817      105630 :       GEN Hl = Q_remove_denom(Hr, &den);
    3818      105630 :       GEN R = ZM_mul(Hl, A);
    3819      105630 :       if (DEBUGLEVEL>=5) timer_printf(&ti,"mult (%ld/%ld primes)", k1, n);
    3820      105630 :       den = den ? den: gen_1;
    3821      105630 :       if (den)
    3822             :       {
    3823      105630 :         if (ZM_isscalar(R, den))
    3824             :         {
    3825      103888 :           H = Hl;
    3826      103888 :           if (pden) *pden = den;
    3827      207776 :           break;
    3828             :         }
    3829             :       }
    3830             :       else
    3831           0 :         if (ZM_isidentity(R)) { H=Hl; break; }
    3832             :     }
    3833             :   }
    3834      112890 :   if (!H)
    3835             :   {
    3836             :     GEN d;
    3837        9002 :     H = H1;
    3838        9002 :     D = ZMrow_ZC_mul(H, gel(A,1), 1);
    3839        9002 :     if (signe(D)==0) pari_err_INV("ZM_inv", A);
    3840        8988 :     d = gcdii(Q_content_safe(H), D);
    3841        8988 :     if (signe(D) < 0) d = negi(d);
    3842        8988 :     if (!equali1(d))
    3843             :     {
    3844        5358 :       H = ZM_Z_divexact(H, d);
    3845        5358 :       D = diviiexact(D, d);
    3846             :     }
    3847        8988 :     if (pden) *pden = D;
    3848             :   }
    3849      112876 :   gerepileall(av, pden? 2: 1, &H, pden);
    3850      112876 :   return H;
    3851             : }
    3852             : 
    3853             : /* same as above, M rational */
    3854             : GEN
    3855        1694 : QM_inv(GEN M)
    3856             : {
    3857        1694 :   pari_sp av = avma;
    3858             :   GEN den, cM, K;
    3859        1694 :   M = Q_primitive_part(M, &cM);
    3860        1694 :   K = ZM_inv(M, &den);
    3861        1694 :   if (!K) return gc_NULL(av);
    3862        1694 :   cM = inv_content(mul_content(cM, den));
    3863        1694 :   if (cM) K = RgM_Rg_div(K, cM);
    3864        1694 :   return gerepileupto(av, K);
    3865             : }
    3866             : 
    3867             : static GEN
    3868       55630 : ZM_ker_i(GEN M, long fl)
    3869             : {
    3870       55630 :   pari_sp av2, av = avma;
    3871             :   GEN q, H, D;
    3872             :   forprime_t S;
    3873       55630 :   av2 = avma;
    3874       55630 :   H = NULL; D = NULL;
    3875       55630 :   if (lg(M)==1) return cgetg(1, t_MAT);
    3876       55623 :   init_modular_big(&S);
    3877             :   for(;;)
    3878       62066 :   {
    3879             :     GEN Kp, Hp, Dp, Mp, Hr, B;
    3880      117689 :     ulong p = u_forprime_next(&S);
    3881      117689 :     Mp = ZM_to_Flm(M, p);
    3882      117689 :     Kp = Flm_ker_sp(Mp, p, 2);
    3883      117689 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    3884      117689 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    3885      109781 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    3886             :     {
    3887       93303 :       H = ZM_init_CRT(Hp, p); D = Dp;
    3888       93303 :       q = utoipos(p);
    3889             :     }
    3890             :     else
    3891       16478 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3892      109781 :     B = sqrti(shifti(q,-1));
    3893      109781 :     Hr = FpM_ratlift(H, q, B, B, NULL);
    3894      109781 :     if (DEBUGLEVEL>5) err_printf("ZM_ker mod %lu (ratlift=%ld)\n", p,!!Hr);
    3895      109781 :     if (Hr) {/* DONE ? */
    3896      104099 :       GEN MH = QM_mul(M, Hr);
    3897      104099 :       if (gequal0(MH)) { H = fl ? vec_Q_primpart(Hr): Hr;  break; }
    3898             :     }
    3899       54158 :     if (gc_needed(av,2))
    3900             :     {
    3901           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_ker");
    3902           0 :       gerepileall(av2, 3, &H, &D, &q);
    3903             :     }
    3904             :   }
    3905       55623 :   return gerepilecopy(av, H);
    3906             : }
    3907             : 
    3908             : GEN
    3909       48819 : ZM_ker(GEN M)
    3910       48819 : { return ZM_ker_i(M, 1); }
    3911             : 
    3912             : GEN
    3913        7644 : QM_ker(GEN M)
    3914             : {
    3915        7644 :   pari_sp av = avma;
    3916        7644 :   long l = lg(M)-1;
    3917        7644 :   if (l==0) return cgetg(1, t_MAT);
    3918        7609 :   if (lgcols(M)==1) return matid(l);
    3919        6748 :   M = shallowtrans(vec_Q_primpart(shallowtrans(M)));
    3920        6748 :   return gerepileupto(av, ZM_ker_i(M, 0));
    3921             : }
    3922             : 
    3923             : /* x a ZM. Return a multiple of the determinant of the lattice generated by
    3924             :  * the columns of x. From Algorithm 2.2.6 in GTM138 */
    3925             : GEN
    3926       47549 : detint(GEN A)
    3927             : {
    3928       47549 :   if (typ(A) != t_MAT) pari_err_TYPE("detint",A);
    3929       47549 :   RgM_check_ZM(A, "detint");
    3930       47549 :   return ZM_detmult(A);
    3931             : }
    3932             : GEN
    3933       97081 : ZM_detmult(GEN A)
    3934             : {
    3935       97081 :   pari_sp av1, av = avma;
    3936             :   GEN B, c, v, piv;
    3937       97081 :   long rg, i, j, k, m, n = lg(A) - 1;
    3938             : 
    3939       97081 :   if (!n) return gen_1;
    3940       97081 :   m = nbrows(A);
    3941       97081 :   if (n < m) return gen_0;
    3942       97060 :   c = zero_zv(m);
    3943       97060 :   av1 = avma;
    3944       97060 :   B = zeromatcopy(m,m);
    3945       97060 :   v = cgetg(m+1, t_COL);
    3946       97060 :   piv = gen_1; rg = 0;
    3947      517900 :   for (k=1; k<=n; k++)
    3948             :   {
    3949      517886 :     GEN pivprec = piv;
    3950      517886 :     long t = 0;
    3951     4141469 :     for (i=1; i<=m; i++)
    3952             :     {
    3953     3623583 :       pari_sp av2 = avma;
    3954             :       GEN vi;
    3955     3623583 :       if (c[i]) continue;
    3956             : 
    3957     2070983 :       vi = mulii(piv, gcoeff(A,i,k));
    3958    17936537 :       for (j=1; j<=m; j++)
    3959    15865554 :         if (c[j]) vi = addii(vi, mulii(gcoeff(B,j,i),gcoeff(A,j,k)));
    3960     2070983 :       if (!t && signe(vi)) t = i;
    3961     2070983 :       gel(v,i) = gerepileuptoint(av2, vi);
    3962             :     }
    3963      517886 :     if (!t) continue;
    3964             :     /* at this point c[t] = 0 */
    3965             : 
    3966      517802 :     if (++rg >= m) { /* full rank; mostly done */
    3967       97046 :       GEN det = gel(v,t); /* last on stack */
    3968       97046 :       if (++k > n)
    3969       96963 :         det = absi(det);
    3970             :       else
    3971             :       {
    3972             :         /* improve further; at this point c[i] is set for all i != t */
    3973          83 :         gcoeff(B,t,t) = piv; v = centermod(gel(B,t), det);
    3974         334 :         for ( ; k<=n; k++)
    3975         251 :           det = gcdii(det, ZV_dotproduct(v, gel(A,k)));
    3976             :       }
    3977       97046 :       return gerepileuptoint(av, det);
    3978             :     }
    3979             : 
    3980      420756 :     piv = gel(v,t);
    3981     3526054 :     for (i=1; i<=m; i++)
    3982             :     {
    3983             :       GEN mvi;
    3984     3105298 :       if (c[i] || i == t) continue;
    3985             : 
    3986     1552649 :       gcoeff(B,t,i) = mvi = negi(gel(v,i));
    3987    13791078 :       for (j=1; j<=m; j++)
    3988    12238429 :         if (c[j]) /* implies j != t */
    3989             :         {
    3990     3044377 :           pari_sp av2 = avma;
    3991     3044377 :           GEN z = addii(mulii(gcoeff(B,j,i), piv), mulii(gcoeff(B,j,t), mvi));
    3992     3044377 :           if (rg > 1) z = diviiexact(z, pivprec);
    3993     3044377 :           gcoeff(B,j,i) = gerepileuptoint(av2, z);
    3994             :         }
    3995             :     }
    3996      420756 :     c[t] = k;
    3997      420756 :     if (gc_needed(av,1))
    3998             :     {
    3999           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"detint. k=%ld",k);
    4000           0 :       gerepileall(av1, 2, &piv,&B); v = zerovec(m);
    4001             :     }
    4002             :   }
    4003          14 :   set_avma(av); return gen_0;
    4004             : }
    4005             : 
    4006             : /* Reduce x modulo (invertible) y */
    4007             : GEN
    4008       13293 : closemodinvertible(GEN x, GEN y)
    4009             : {
    4010       13293 :   return gmul(y, ground(RgM_solve(y,x)));
    4011             : }
    4012             : GEN
    4013           7 : reducemodinvertible(GEN x, GEN y)
    4014             : {
    4015           7 :   return gsub(x, closemodinvertible(x,y));
    4016             : }
    4017             : GEN
    4018           0 : reducemodlll(GEN x,GEN y)
    4019             : {
    4020           0 :   return reducemodinvertible(x, ZM_lll(y, 0.75, LLL_INPLACE));
    4021             : }
    4022             : 
    4023             : /*******************************************************************/
    4024             : /*                                                                 */
    4025             : /*                    KERNEL of an m x n matrix                    */
    4026             : /*          return n - rk(x) linearly independent vectors          */
    4027             : /*                                                                 */
    4028             : /*******************************************************************/
    4029             : static GEN
    4030          28 : RgM_deplin_i(GEN x0)
    4031             : {
    4032          28 :   pari_sp av = avma, av2;
    4033          28 :   long i, j, k, nl, nc = lg(x0)-1;
    4034             :   GEN D, x, y, c, l, d, ck;
    4035             : 
    4036          28 :   if (!nc) return NULL;
    4037          28 :   nl = nbrows(x0);
    4038          28 :   c = zero_zv(nl);
    4039          28 :   l = cgetg(nc+1, t_VECSMALL); /* not initialized */
    4040          28 :   av2 = avma;
    4041          28 :   x = RgM_shallowcopy(x0);
    4042          28 :   d = const_vec(nl, gen_1); /* pivot list */
    4043          28 :   ck = NULL; /* gcc -Wall */
    4044          98 :   for (k=1; k<=nc; k++)
    4045             :   {
    4046          91 :     ck = gel(x,k);
    4047         196 :     for (j=1; j<k; j++)
    4048             :     {
    4049         105 :       GEN cj = gel(x,j), piv = gel(d,j), q = gel(ck,l[j]);
    4050         420 :       for (i=1; i<=nl; i++)
    4051         315 :         if (i!=l[j]) gel(ck,i) = gsub(gmul(piv, gel(ck,i)), gmul(q, gel(cj,i)));
    4052             :     }
    4053             : 
    4054          91 :     i = gauss_get_pivot_NZ(x, NULL, k, c);
    4055          91 :     if (i > nl) break;
    4056          70 :     if (gc_needed(av,1))
    4057             :     {
    4058           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"deplin k = %ld/%ld",k,nc);
    4059           0 :       gerepileall(av2, 2, &x, &d);
    4060           0 :       ck = gel(x,k);
    4061             :     }
    4062          70 :     gel(d,k) = gel(ck,i);
    4063          70 :     c[i] = k; l[k] = i; /* pivot d[k] in x[i,k] */
    4064             :   }
    4065          28 :   if (k > nc) return gc_NULL(av);
    4066          21 :   if (k == 1) { set_avma(av); return scalarcol_shallow(gen_1,nc); }
    4067          21 :   y = cgetg(nc+1,t_COL);
    4068          21 :   gel(y,1) = gcopy(gel(ck, l[1]));
    4069          49 :   for (D=gel(d,1),j=2; j<k; j++)
    4070             :   {
    4071          28 :     gel(y,j) = gmul(gel(ck, l[j]), D);
    4072          28 :     D = gmul(D, gel(d,j));
    4073             :   }
    4074          21 :   gel(y,j) = gneg(D);
    4075          21 :   for (j++; j<=nc; j++) gel(y,j) = gen_0;
    4076          21 :   y = primitive_part(y, &c);
    4077          21 :   return c? gerepileupto(av, y): gerepilecopy(av, y);
    4078             : }
    4079             : static GEN
    4080           0 : RgV_deplin(GEN v)
    4081             : {
    4082           0 :   pari_sp av = avma;
    4083           0 :   long n = lg(v)-1;
    4084           0 :   GEN y, p = NULL;
    4085           0 :   if (n <= 1)
    4086             :   {
    4087           0 :     if (n == 1 && gequal0(gel(v,1))) return mkcol(gen_1);
    4088           0 :     return cgetg(1, t_COL);
    4089             :   }
    4090           0 :   if (gequal0(gel(v,1))) return scalarcol_shallow(gen_1, n);
    4091           0 :   v = primpart(mkvec2(gel(v,1),gel(v,2)));
    4092           0 :   if (RgV_is_FpV(v, &p) && p) v = centerlift(v);
    4093           0 :   y = zerocol(n);
    4094           0 :   gel(y,1) = gneg(gel(v,2));
    4095           0 :   gel(y,2) = gcopy(gel(v,1));
    4096           0 :   return gerepileupto(av, y);
    4097             : 
    4098             : }
    4099             : 
    4100             : static GEN
    4101         105 : RgM_deplin_FpM(GEN x, GEN p)
    4102             : {
    4103         105 :   pari_sp av = avma;
    4104             :   ulong pp;
    4105         105 :   x = RgM_Fp_init(x, p, &pp);
    4106         105 :   switch(pp)
    4107             :   {
    4108             :   case 0:
    4109          35 :     x = FpM_ker_gen(x,p,1);
    4110          35 :     if (!x) return gc_NULL(av);
    4111          21 :     x = FpC_center(x,p,shifti(p,-1));
    4112          21 :     break;
    4113             :   case 2:
    4114          14 :     x = F2m_ker_sp(x,1);
    4115          14 :     if (!x) return gc_NULL(av);
    4116           7 :     x = F2c_to_ZC(x); break;
    4117             :   default:
    4118          56 :     x = Flm_ker_sp(x,pp,1);
    4119          56 :     if (!x) return gc_NULL(av);
    4120          35 :     x = Flv_center(x, pp, pp>>1);
    4121          35 :     x = zc_to_ZC(x);
    4122          35 :     break;
    4123             :   }
    4124          63 :   return gerepileupto(av, x);
    4125             : }
    4126             : 
    4127             : /* FIXME: implement direct modular ZM_deplin ? */
    4128             : static GEN
    4129          98 : QM_deplin(GEN M)
    4130             : {
    4131          98 :   pari_sp av = avma;
    4132          98 :   long l = lg(M)-1;
    4133             :   GEN k;
    4134          98 :   if (l==0) return NULL;
    4135          63 :   if (lgcols(M)==1) return col_ei(l, 1);
    4136          63 :   M = shallowtrans(vec_Q_primpart(shallowtrans(M)));
    4137          63 :   k = ZM_ker_i(M, 1);
    4138          63 :   if (lg(k)== 1) return gc_NULL(av);
    4139          49 :   return gerepilecopy(av, gel(k,1));
    4140             : }
    4141             : 
    4142             : static GEN
    4143          42 : RgM_deplin_FqM(GEN x, GEN pol, GEN p)
    4144             : {
    4145          42 :   pari_sp av = avma;
    4146          42 :   GEN b, T = RgX_to_FpX(pol, p);
    4147          42 :   if (signe(T) == 0) pari_err_OP("deplin",x,pol);
    4148          42 :   b = FqM_deplin(RgM_to_FqM(x, T, p), T, p);
    4149          42 :   return gerepileupto(av, b);
    4150             : }
    4151             : 
    4152             : #define code(t1,t2) ((t1 << 6) | t2)
    4153             : static GEN
    4154         357 : RgM_deplin_fast(GEN x)
    4155             : {
    4156             :   GEN p, pol;
    4157             :   long pa;
    4158         357 :   long t = RgM_type(x, &p,&pol,&pa);
    4159         357 :   switch(t)
    4160             :   {
    4161             :     case t_INT:    /* fall through */
    4162          98 :     case t_FRAC:   return QM_deplin(x);
    4163          84 :     case t_FFELT:  return FFM_deplin(x, pol);
    4164         105 :     case t_INTMOD: return RgM_deplin_FpM(x, p);
    4165             :     case code(t_POLMOD, t_INTMOD):
    4166          42 :                    return RgM_deplin_FqM(x, pol, p);
    4167          28 :     default:       return gen_0;
    4168             :   }
    4169             : }
    4170             : #undef code
    4171             : 
    4172             : static GEN
    4173         357 : RgM_deplin(GEN x)
    4174             : {
    4175         357 :   GEN z = RgM_deplin_fast(x);
    4176         357 :   if (z!= gen_0) return z;
    4177          28 :   return RgM_deplin_i(x);
    4178             : }
    4179             : 
    4180             : GEN
    4181         357 : deplin(GEN x)
    4182             : {
    4183         357 :   switch(typ(x))
    4184             :   {
    4185             :     case t_MAT:
    4186             :     {
    4187         357 :       GEN z = RgM_deplin(x);
    4188         357 :       if (z) return z;
    4189         140 :       return cgetg(1, t_COL);
    4190             :     }
    4191           0 :     case t_VEC: return RgV_deplin(x);
    4192           0 :     default: pari_err_TYPE("deplin",x);
    4193             :   }
    4194             :   return NULL;/*LCOV_EXCL_LINE*/
    4195             : }
    4196             : 
    4197             : /*******************************************************************/
    4198             : /*                                                                 */
    4199             : /*         GAUSS REDUCTION OF MATRICES  (m lines x n cols)         */
    4200             : /*           (kernel, image, complementary image, rank)            */
    4201             : /*                                                                 */
    4202             : /*******************************************************************/
    4203             : /* return the transform of x under a standard Gauss pivot.
    4204             :  * x0 is a reference point when guessing whether x[i,j] ~ 0
    4205             :  * (iff x[i,j] << x0[i,j])
    4206             :  * Set r = dim ker(x). d[k] contains the index of the first non-zero pivot
    4207             :  * in column k */
    4208             : static GEN
    4209         952 : gauss_pivot_ker(GEN x, GEN x0, GEN *dd, long *rr)
    4210             : {
    4211             :   GEN c, d, p, data;
    4212             :   pari_sp av;
    4213             :   long i, j, k, r, t, n, m;
    4214             :   pivot_fun pivot;
    4215             : 
    4216         952 :   n=lg(x)-1; if (!n) { *dd=NULL; *rr=0; return cgetg(1,t_MAT); }
    4217         952 :   m=nbrows(x); r=0;
    4218         952 :   pivot = get_pivot_fun(x, x0, &data);
    4219         952 :   x = RgM_shallowcopy(x);
    4220         952 :   c = zero_zv(m);
    4221         952 :   d = cgetg(n+1,t_VECSMALL);
    4222         952 :   av=avma;
    4223        5558 :   for (k=1; k<=n; k++)
    4224             :   {
    4225        4606 :     j = pivot(x, data, k, c);
    4226        4606 :     if (j > m)
    4227             :     {
    4228        1029 :       r++; d[k]=0;
    4229        4578 :       for(j=1; j<k; j++)
    4230        3549 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
    4231             :     }
    4232             :     else
    4233             :     { /* pivot for column k on row j */
    4234        3577 :       c[j]=k; d[k]=j; p = gdiv(gen_m1,gcoeff(x,j,k));
    4235        3577 :       gcoeff(x,j,k) = gen_m1;
    4236             :       /* x[j,] /= - x[j,k] */
    4237        3577 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    4238       35000 :       for (t=1; t<=m; t++)
    4239       31423 :         if (t!=j)
    4240             :         { /* x[t,] -= 1 / x[j,k] x[j,] */
    4241       27846 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    4242       27846 :           if (gequal0(p)) continue;
    4243       74613 :           for (i=k+1; i<=n; i++)
    4244       60648 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i),gmul(p,gcoeff(x,j,i)));
    4245       13965 :           if (gc_needed(av,1)) gerepile_gauss_ker(x,k,t,av);
    4246             :         }
    4247             :     }
    4248             :   }
    4249         952 :   *dd=d; *rr=r; return x;
    4250             : }
    4251             : 
    4252             : static GEN FpM_gauss_pivot(GEN x, GEN p, long *rr);
    4253             : static GEN FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr);
    4254             : static GEN F2m_gauss_pivot(GEN x, long *rr);
    4255             : 
    4256             : /* r = dim ker(x).
    4257             :  * Returns d:
    4258             :  *   d[k] != 0 contains the index of a non-zero pivot in column k
    4259             :  *   d[k] == 0 if column k is a linear combination of the (k-1) first ones */
    4260             : GEN
    4261       33198 : RgM_pivots(GEN x0, GEN data, long *rr, pivot_fun pivot)
    4262             : {
    4263             :   GEN x, c, d, p;
    4264       33198 :   long i, j, k, r, t, m, n = lg(x0)-1;
    4265             :   pari_sp av;
    4266             : 
    4267       33198 :   if (RgM_is_ZM(x0)) return ZM_pivots(x0, rr);
    4268       33100 :   if (!n) { *rr = 0; return NULL; }
    4269             : 
    4270       33100 :   d = cgetg(n+1, t_VECSMALL);
    4271       33100 :   x = RgM_shallowcopy(x0);
    4272       33100 :   m = nbrows(x); r = 0;
    4273       33100 :   c = zero_zv(m);
    4274       33100 :   av = avma;
    4275      940178 :   for (k=1; k<=n; k++)
    4276             :   {
    4277      907078 :     j = pivot(x, data, k, c);
    4278      907078 :     if (j > m) { r++; d[k] = 0; }
    4279             :     else
    4280             :     {
    4281       51544 :       c[j] = k; d[k] = j; p = gdiv(gen_m1, gcoeff(x,j,k));
    4282       51544 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    4283             : 
    4284      193105 :       for (t=1; t<=m; t++)
    4285      141561 :         if (!c[t]) /* no pivot on that line yet */
    4286             :         {
    4287       54018 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    4288     5349882 :           for (i=k+1; i<=n; i++)
    4289     5295864 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i), gmul(p, gcoeff(x,j,i)));
    4290       54018 :           if (gc_needed(av,1)) gerepile_gauss(x,k,t,av,j,c);
    4291             :         }
    4292       51544 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = gen_0; /* dummy */
    4293             :     }
    4294             :   }
    4295       33100 :   *rr = r; set_avma((pari_sp)d); return d;
    4296             : }
    4297             : 
    4298             : static long
    4299      115290 : ZM_count_0_cols(GEN M)
    4300             : {
    4301      115290 :   long i, l = lg(M), n = 0;
    4302      646088 :   for (i = 1; i < l; i++)
    4303      530798 :     if (ZV_equal0(gel(M,i))) n++;
    4304      115290 :   return n;
    4305             : }
    4306             : 
    4307             : static void indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol);
    4308             : /* As RgM_pivots, integer entries. Set *rr = dim Ker M0 */
    4309             : GEN
    4310      119294 : ZM_pivots(GEN M0, long *rr)
    4311             : {
    4312      119294 :   GEN d, dbest = NULL;
    4313             :   long m, n, i, imax, rmin, rbest, zc;
    4314      119294 :   int beenthere = 0;
    4315      119294 :   pari_sp av, av0 = avma;
    4316             :   forprime_t S;
    4317             : 
    4318      119294 :   rbest = n = lg(M0)-1;
    4319      119294 :   if (n == 0) { *rr = 0; return NULL; }
    4320      115290 :   zc = ZM_count_0_cols(M0);
    4321      115290 :   if (n == zc) { *rr = zc; return zero_zv(n); }
    4322             : 
    4323      115185 :   m = nbrows(M0);
    4324      115185 :   rmin = maxss(zc, n-m);
    4325      115185 :   init_modular_small(&S);
    4326      115185 :   imax = (n < (1<<4))? 1: (n>>3); /* heuristic */
    4327             : 
    4328             :   for(;;)
    4329           0 :   {
    4330             :     GEN row, col, M, KM, IM, RHS, X, cX;
    4331             :     long rk;
    4332      124231 :     for (av = avma, i = 0;; set_avma(av), i++)
    4333        9046 :     {
    4334      124231 :       ulong p = u_forprime_next(&S);
    4335             :       long rp;
    4336      124231 :       if (!p) pari_err_OVERFLOW("ZM_pivots [ran out of primes]");
    4337      124231 :       d = Flm_pivots(ZM_to_Flm(M0, p), p, &rp, 1);
    4338      124231 :       if (rp == rmin) { rbest = rp; goto END; } /* maximal rank, return */
    4339       16430 :       if (rp < rbest) { /* save best r so far */
    4340        7386 :         rbest = rp;
    4341        7386 :         if (dbest) gunclone(dbest);
    4342        7386 :         dbest = gclone(d);
    4343       14770 :         if (beenthere) break;
    4344             :       }
    4345       16430 :       if (!beenthere && i >= imax) break;
    4346             :     }
    4347        7384 :     beenthere = 1;
    4348             :     /* Dubious case: there is (probably) a non trivial kernel */
    4349        7384 :     indexrank_all(m,n, rbest, dbest, &row, &col);
    4350        7384 :     M = rowpermute(vecpermute(M0, col), row);
    4351        7384 :     rk = n - rbest; /* (probable) dimension of image */
    4352        7384 :     IM = vecslice(M,1,rk);
    4353        7384 :     KM = vecslice(M,rk+1, n);
    4354        7384 :     M = rowslice(IM, 1,rk); /* square maximal rank */
    4355        7384 :     X = ZM_gauss(M, rowslice(KM, 1,rk));
    4356        7384 :     X = Q_remove_denom(X, &cX);
    4357        7384 :     RHS = rowslice(KM,rk+1,m);
    4358        7384 :     if (cX) RHS = ZM_Z_mul(RHS, cX);
    4359        7384 :     if (ZM_equal(ZM_mul(rowslice(IM,rk+1,m), X), RHS))
    4360             :     {
    4361        7384 :       d = vecsmall_copy(dbest);
    4362        7384 :       goto END;
    4363             :     }
    4364           0 :     set_avma(av);
    4365             :   }
    4366             : END:
    4367      115185 :   *rr = rbest; if (dbest) gunclone(dbest);
    4368      115185 :   return gerepileuptoleaf(av0, d);
    4369             : }
    4370             : 
    4371             : /* set *pr = dim Ker x */
    4372             : static GEN
    4373       20986 : gauss_pivot(GEN x, long *pr) {
    4374             :   GEN data;
    4375       20986 :   pivot_fun pivot = get_pivot_fun(x, x, &data);
    4376       20986 :   return RgM_pivots(x, data, pr, pivot);
    4377             : }
    4378             : 
    4379             : /* compute ker(x), x0 is a reference point when guessing whether x[i,j] ~ 0
    4380             :  * (iff x[i,j] << x0[i,j]) */
    4381             : static GEN
    4382         952 : ker_aux(GEN x, GEN x0)
    4383             : {
    4384         952 :   pari_sp av = avma;
    4385             :   GEN d,y;
    4386             :   long i,j,k,r,n;
    4387             : 
    4388         952 :   x = gauss_pivot_ker(x,x0,&d,&r);
    4389         952 :   if (!r) { set_avma(av); return cgetg(1,t_MAT); }
    4390         931 :   n = lg(x)-1; y=cgetg(r+1,t_MAT);
    4391        1960 :   for (j=k=1; j<=r; j++,k++)
    4392             :   {
    4393        1029 :     GEN p = cgetg(n+1,t_COL);
    4394             : 
    4395        1029 :     gel(y,j) = p; while (d[k]) k++;
    4396        4578 :     for (i=1; i<k; i++)
    4397        3549 :       if (d[i])
    4398             :       {
    4399        3367 :         GEN p1=gcoeff(x,d[i],k);
    4400        3367 :         gel(p,i) = gcopy(p1); gunclone(p1);
    4401             :       }
    4402             :       else
    4403         182 :         gel(p,i) = gen_0;
    4404        1029 :     gel(p,k) = gen_1; for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
    4405             :   }
    4406         931 :   return gerepileupto(av,y);
    4407             : }
    4408             : 
    4409             : static GEN
    4410          77 : RgM_ker_FpM(GEN x, GEN p)
    4411             : {
    4412          77 :   pari_sp av = avma;
    4413             :   ulong pp;
    4414          77 :   x = RgM_Fp_init(x, p, &pp);
    4415          77 :   switch(pp)
    4416             :   {
    4417          35 :     case 0: x = FpM_to_mod(FpM_ker_gen(x,p,0),p); break;
    4418           7 :     case 2: x = F2m_to_mod(F2m_ker_sp(x,0)); break;
    4419          35 :     default:x = Flm_to_mod(Flm_ker_sp(x,pp,0), pp); break;
    4420             :   }
    4421          77 :   return gerepileupto(av, x);
    4422             : }
    4423             : 
    4424             : static GEN
    4425          91 : RgM_ker_FqM(GEN x, GEN pol, GEN p)
    4426             : {
    4427          91 :   pari_sp av = avma;
    4428          91 :   GEN b, T = RgX_to_FpX(pol, p);
    4429          91 :   if (signe(T) == 0) pari_err_OP("ker",x,pol);
    4430          84 :   b = FqM_ker(RgM_to_FqM(x, T, p), T, p);
    4431          84 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    4432             : }
    4433             : 
    4434             : #define code(t1,t2) ((t1 << 6) | t2)
    4435             : static GEN
    4436        8645 : RgM_ker_fast(GEN x)
    4437             : {
    4438             :   GEN p, pol;
    4439             :   long pa;
    4440        8645 :   long t = RgM_type(x, &p,&pol,&pa);
    4441        8645 :   switch(t)
    4442             :   {
    4443             :     case t_INT:    /* fall through */
    4444        7644 :     case t_FRAC:   return QM_ker(x);
    4445          77 :     case t_FFELT:  return FFM_ker(x, pol);
    4446          77 :     case t_INTMOD: return RgM_ker_FpM(x, p);
    4447             :     case code(t_POLMOD, t_INTMOD):
    4448          91 :                    return RgM_ker_FqM(x, pol, p);
    4449         756 :     default:       return NULL;
    4450             :   }
    4451             : }
    4452             : #undef code
    4453             : 
    4454             : GEN
    4455        8645 : ker(GEN x)
    4456             : {
    4457        8645 :   GEN b = RgM_ker_fast(x);
    4458        8638 :   if (b) return b;
    4459         756 :   return ker_aux(x,x);
    4460             : }
    4461             : 
    4462             : GEN
    4463       46214 : matker0(GEN x,long flag)
    4464             : {
    4465       46214 :   if (typ(x)!=t_MAT) pari_err_TYPE("matker",x);
    4466       46214 :   if (!flag) return ker(x);
    4467       45934 :   RgM_check_ZM(x, "matker");
    4468       45934 :   return ZM_ker(x);
    4469             : }
    4470             : 
    4471             : static GEN
    4472          63 : RgM_image_FpM(GEN x, GEN p)
    4473             : {
    4474          63 :   pari_sp av = avma;
    4475             :   ulong pp;
    4476          63 :   x = RgM_Fp_init(x, p, &pp);
    4477          63 :   switch(pp)
    4478             :   {
    4479          28 :     case 0: x = FpM_to_mod(FpM_image(x,p),p); break;
    4480           7 :     case 2: x = F2m_to_mod(F2m_image(x)); break;
    4481          28 :     default:x = Flm_to_mod(Flm_image(x,pp), pp); break;
    4482             :   }
    4483          63 :   return gerepileupto(av, x);
    4484             : }
    4485             : 
    4486             : static GEN
    4487          35 : RgM_image_FqM(GEN x, GEN pol, GEN p)
    4488             : {
    4489          35 :   pari_sp av = avma;
    4490          35 :   GEN b, T = RgX_to_FpX(pol, p);
    4491          35 :   if (signe(T) == 0) pari_err_OP("image",x,pol);
    4492          28 :   b = FqM_image(RgM_to_FqM(x, T, p), T, p);
    4493          28 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    4494             : }
    4495             : 
    4496             : static GEN
    4497        1463 : QM_image(GEN A)
    4498             : {
    4499        1463 :   pari_sp av = avma;
    4500        1463 :   GEN M = vecpermute(A, ZM_indeximage(vec_Q_primpart(A)));
    4501        1463 :   return gerepilecopy(av, M);
    4502             : }
    4503             : 
    4504             : #define code(t1,t2) ((t1 << 6) | t2)
    4505             : static GEN
    4506        1624 : RgM_image_fast(GEN x)
    4507             : {
    4508             :   GEN p, pol;
    4509             :   long pa;
    4510        1624 :   long t = RgM_type(x, &p,&pol,&pa);
    4511        1624 :   switch(t)
    4512             :   {
    4513             :     case t_INT:    /* fall through */
    4514        1463 :     case t_FRAC:   return QM_image(x);
    4515          49 :     case t_FFELT:  return FFM_image(x, pol);
    4516          63 :     case t_INTMOD: return RgM_image_FpM(x, p);
    4517             :     case code(t_POLMOD, t_INTMOD):
    4518          35 :                    return RgM_image_FqM(x, pol, p);
    4519          14 :     default:       return NULL;
    4520             :   }
    4521             : }
    4522             : #undef code
    4523             : 
    4524             : GEN
    4525        1624 : image(GEN x)
    4526             : {
    4527             :   GEN d, M;
    4528             :   long r;
    4529             : 
    4530        1624 :   if (typ(x)!=t_MAT) pari_err_TYPE("matimage",x);
    4531        1624 :   M = RgM_image_fast(x);
    4532        1617 :   if (M) return M;
    4533          14 :   d = gauss_pivot(x,&r); /* d left on stack for efficiency */
    4534          14 :   return image_from_pivot(x,d,r);
    4535             : }
    4536             : 
    4537             : static GEN
    4538          84 : imagecompl_aux(GEN x, GEN(*PIVOT)(GEN,long*))
    4539             : {
    4540          84 :   pari_sp av = avma;
    4541             :   GEN d,y;
    4542             :   long j,i,r;
    4543             : 
    4544          84 :   if (typ(x)!=t_MAT) pari_err_TYPE("imagecompl",x);
    4545          84 :   (void)new_chunk(lg(x) * 4 + 1); /* HACK */
    4546          84 :   d = PIVOT(x,&r); /* if (!d) then r = 0 */
    4547          84 :   set_avma(av); y = cgetg(r+1,t_VECSMALL);
    4548         126 :   for (i=j=1; j<=r; i++)
    4549          42 :     if (!d[i]) y[j++] = i;
    4550          84 :   return y;
    4551             : }
    4552             : GEN
    4553          84 : imagecompl(GEN x) { return imagecompl_aux(x, &gauss_pivot); }
    4554             : GEN
    4555           0 : ZM_imagecompl(GEN x) { return imagecompl_aux(x, &ZM_pivots); }
    4556             : 
    4557             : GEN
    4558       43168 : FpM_FpC_invimage(GEN A, GEN y, GEN p)
    4559             : {
    4560       43168 :   pari_sp av = avma;
    4561       43168 :   long i, l = lg(A);
    4562             :   GEN M, x, t;
    4563             : 
    4564       43168 :   if (lgefint(p) == 3)
    4565             :   {
    4566       43161 :     ulong pp = p[2];
    4567       43161 :     A = ZM_to_Flm(A, pp);
    4568       43161 :     y = ZV_to_Flv(y, pp);
    4569       43161 :     x = Flm_Flc_invimage(A,y,pp);
    4570       43161 :     if (!x) return gc_NULL(av);
    4571       43161 :     return gerepileupto(av, Flc_to_ZC(x));
    4572             :   }
    4573           7 :   if (l==1) return NULL;
    4574           7 :   if (lg(y) != lgcols(A)) pari_err_DIM("FpM_FpC_invimage");
    4575           7 :   M = FpM_ker(shallowconcat(A,y),p);
    4576           7 :   i = lg(M)-1; if (!i) return gc_NULL(av);
    4577             : 
    4578           7 :   x = gel(M,i); t = gel(x,l);
    4579           7 :   if (!signe(t)) return gc_NULL(av);
    4580             : 
    4581           7 :   setlg(x,l); t = Fp_inv(negi(t),p);
    4582           7 :   if (is_pm1(t)) return gerepilecopy(av, x);
    4583           7 :   return gerepileupto(av, FpC_Fp_mul(x, t, p));
    4584             : }
    4585             : GEN
    4586       52352 : Flm_Flc_invimage(GEN A, GEN y, ulong p)
    4587             : {
    4588       52352 :   pari_sp av = avma;
    4589       52352 :   long i, l = lg(A);
    4590             :   GEN M, x;
    4591             :   ulong t;
    4592             : 
    4593       52352 :   if (l==1) return NULL;
    4594       52352 :   if (lg(y) != lgcols(A)) pari_err_DIM("Flm_Flc_invimage");
    4595       52352 :   M = cgetg(l+1,t_MAT);
    4596       52352 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4597       52352 :   gel(M,l) = y; M = Flm_ker(M,p);
    4598       52352 :   i = lg(M)-1; if (!i) return gc_NULL(av);
    4599             : 
    4600       52352 :   x = gel(M,i); t = x[l];
    4601       52352 :   if (!t) return gc_NULL(av);
    4602             : 
    4603       52352 :   setlg(x,l); t = Fl_inv(Fl_neg(t,p),p);
    4604       52352 :   if (t!=1) x = Flv_Fl_mul(x, t, p);
    4605       52352 :   return gerepileuptoleaf(av, x);
    4606             : }
    4607             : GEN
    4608          35 : F2m_F2c_invimage(GEN A, GEN y)
    4609             : {
    4610          35 :   pari_sp av = avma;
    4611          35 :   long i, l = lg(A);
    4612             :   GEN M, x;
    4613             : 
    4614          35 :   if (l==1) return NULL;
    4615          35 :   if (lg(y) != lgcols(A)) pari_err_DIM("F2m_F2c_invimage");
    4616          35 :   M = cgetg(l+1,t_MAT);
    4617          35 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4618          35 :   gel(M,l) = y; M = F2m_ker(M);
    4619          35 :   i = lg(M)-1; if (!i) return gc_NULL(av);
    4620             : 
    4621          35 :   x = gel(M,i);
    4622          35 :   if (!F2v_coeff(x,l)) return gc_NULL(av);
    4623          35 :   F2v_clear(x, x[1]); x[1]--; /* remove last coord */
    4624          35 :   return gerepileuptoleaf(av, x);
    4625             : }
    4626             : 
    4627             : static GEN
    4628          28 : RgM_RgC_invimage_FpC(GEN A, GEN y, GEN p)
    4629             : {
    4630          28 :   pari_sp av = avma;
    4631             :   ulong pp;
    4632             :   GEN x;
    4633          28 :   A = RgM_Fp_init(A,p,&pp);
    4634          28 :   switch(pp)
    4635             :   {
    4636             :   case 0:
    4637           7 :     y = RgC_to_FpC(y,p);
    4638           7 :     x = FpM_FpC_invimage(A, y, p);
    4639           7 :     return x ? gerepileupto(av, FpC_to_mod(x,p)): NULL;
    4640             :   case 2:
    4641           7 :     y = RgV_to_F2v(y);
    4642           7 :     x = F2m_F2c_invimage(A, y);
    4643           7 :     return x ? gerepileupto(av, F2c_to_mod(x)): NULL;
    4644             :   default:
    4645          14 :     y = RgV_to_Flv(y,pp);
    4646          14 :     x = Flm_Flc_invimage(A, y, pp);
    4647          14 :     return x ? gerepileupto(av, Flc_to_mod(x,pp)): NULL;
    4648             :   }
    4649             : }
    4650             : 
    4651             : static GEN
    4652        2051 : RgM_RgC_invimage_fast(GEN x, GEN y)
    4653             : {
    4654             :   GEN p, pol;
    4655             :   long pa;
    4656        2051 :   long t = RgM_RgC_type(x, y, &p,&pol,&pa);
    4657        2051 :   switch(t)
    4658             :   {
    4659          28 :     case t_INTMOD: return RgM_RgC_invimage_FpC(x, y, p);
    4660          63 :     case t_FFELT:  return FFM_FFC_invimage(x, y, pol);
    4661        1960 :     default:       return gen_0;
    4662             :   }
    4663             : }
    4664             : 
    4665             : GEN
    4666        2156 : RgM_RgC_invimage(GEN A, GEN y)
    4667             : {
    4668        2156 :   pari_sp av = avma;
    4669        2156 :   long i, l = lg(A);
    4670             :   GEN M, x, t;
    4671        2156 :   if (l==1) return NULL;
    4672        2051 :   if (lg(y) != lgcols(A)) pari_err_DIM("inverseimage");
    4673        2051 :   M = RgM_RgC_invimage_fast(A, y);
    4674        2051 :   if (!M) return gc_NULL(av);
    4675        2030 :   if (M != gen_0) return M;
    4676        1960 :   M = ker(shallowconcat(A, y));
    4677        1960 :   i = lg(M)-1;
    4678        1960 :   if (!i) return gc_NULL(av);
    4679             : 
    4680        1701 :   x = gel(M,i); t = gel(x,l);
    4681        1701 :   if (gequal0(t)) return gc_NULL(av);
    4682             : 
    4683        1666 :   t = gneg_i(t); setlg(x,l);
    4684        1666 :   return gerepileupto(av, RgC_Rg_div(x, t));
    4685             : }
    4686             : 
    4687             : /* Return X such that m X = v (t_COL or t_MAT), resp. an empty t_COL / t_MAT
    4688             :  * if no solution exist */
    4689             : GEN
    4690        2366 : inverseimage(GEN m, GEN v)
    4691             : {
    4692             :   GEN y;
    4693        2366 :   if (typ(m)!=t_MAT) pari_err_TYPE("inverseimage",m);
    4694        2366 :   switch(typ(v))
    4695             :   {
    4696             :     case t_COL:
    4697        2128 :       y = RgM_RgC_invimage(m,v);
    4698        2128 :       return y? y: cgetg(1,t_COL);
    4699             :     case t_MAT:
    4700         238 :       y = RgM_invimage(m, v);
    4701         238 :       return y? y: cgetg(1,t_MAT);
    4702             :   }
    4703           0 :   pari_err_TYPE("inverseimage",v);
    4704             :   return NULL;/*LCOV_EXCL_LINE*/
    4705             : }
    4706             : 
    4707             : static GEN
    4708          21 : Flm_invimage_CUP(GEN A, GEN B, ulong p) {
    4709          21 :   pari_sp av = avma;
    4710             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    4711             :   long r;
    4712          21 :   r = Flm_CUP(A, &R, &C, &U, &P, p);
    4713          21 :   Rc = indexcompl(R, nbrows(B));
    4714          21 :   C1 = rowpermute(C, R);
    4715          21 :   C2 = rowpermute(C, Rc);
    4716          21 :   B1 = rowpermute(B, R);
    4717          21 :   B2 = rowpermute(B, Rc);
    4718          21 :   Z = Flm_rsolve_lower_unit(C1, B1, p);
    4719          21 :   if (!gequal(Flm_mul(C2, Z, p), B2))
    4720          14 :     return NULL;
    4721          14 :   Y = vconcat(Flm_rsolve_upper(vecslice(U, 1, r), Z, p),
    4722          14 :               zero_Flm(lg(A) - 1 - r, lg(B) - 1));
    4723           7 :   X = rowpermute(Y, perm_inv(P));
    4724           7 :   return gerepileupto(av, X);
    4725             : }
    4726             : 
    4727             : static GEN
    4728          42 : Flm_invimage_i(GEN A, GEN B, ulong p)
    4729             : {
    4730             :   GEN d, x, X, Y;
    4731          42 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4732             : 
    4733          42 :   if (!nB) return cgetg(1, t_MAT);
    4734          42 :   if (nA + nB >= Flm_CUP_LIMIT && nbrows(B) >= Flm_CUP_LIMIT)
    4735          21 :     return Flm_invimage_CUP(A, B, p);
    4736             : 
    4737          21 :   x = Flm_ker_sp(shallowconcat(Flm_neg(A,p), B), p, 0);
    4738             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4739             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4740             :    * Y has at least nB columns and full rank */
    4741          21 :   nY = lg(x)-1;
    4742          21 :   if (nY < nB) return NULL;
    4743          21 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4744          21 :   d = cgetg(nB+1, t_VECSMALL);
    4745          56 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4746             :   {
    4747          49 :     for (; j>=1; j--)
    4748          42 :       if (coeff(Y,i,j)) { d[i] = j; break; }
    4749          42 :     if (!j) return NULL;
    4750             :   }
    4751             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4752          14 :   Y = vecpermute(Y, d);
    4753          14 :   x = vecpermute(x, d);
    4754          14 :   X = rowslice(x, 1, nA);
    4755          14 :   return Flm_mul(X, Flm_inv_upper_1(Y,p), p);
    4756             : }
    4757             : 
    4758             : static GEN
    4759           7 : F2m_invimage_i(GEN A, GEN B)
    4760             : {
    4761             :   GEN d, x, X, Y;
    4762           7 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4763           7 :   x = F2m_ker_sp(shallowconcat(A, B), 0);
    4764             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4765             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4766             :    * Y has at least nB columns and full rank */
    4767           7 :   nY = lg(x)-1;
    4768           7 :   if (nY < nB) return NULL;
    4769             : 
    4770             :   /* implicitly: Y = rowslice(x, nA+1, nA+nB), nB rows */
    4771           7 :   d = cgetg(nB+1, t_VECSMALL);
    4772          21 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4773             :   {
    4774          14 :     for (; j>=1; j--)
    4775          14 :       if (F2m_coeff(x,nA+i,j)) { d[i] = j; break; } /* Y[i,j] */
    4776          14 :     if (!j) return NULL;
    4777             :   }
    4778           7 :   x = vecpermute(x, d);
    4779             : 
    4780           7 :   X = F2m_rowslice(x, 1, nA);
    4781           7 :   Y = F2m_rowslice(x, nA+1, nA+nB);
    4782           7 :   return F2m_mul(X, F2m_inv_upper_1(Y));
    4783             : }
    4784             : GEN
    4785           0 : Flm_invimage(GEN A, GEN B, ulong p)
    4786             : {
    4787           0 :   pari_sp av = avma;
    4788           0 :   GEN X = Flm_invimage_i(A,B,p);
    4789           0 :   if (!X) return gc_NULL(av);
    4790           0 :   return gerepileupto(av, X);
    4791             : }
    4792             : GEN
    4793           0 : F2m_invimage(GEN A, GEN B)
    4794             : {
    4795           0 :   pari_sp av = avma;
    4796           0 :   GEN X = F2m_invimage_i(A,B);
    4797           0 :   if (!X) return gc_NULL(av);
    4798           0 :   return gerepileupto(av, X);
    4799             : }
    4800             : static GEN
    4801          35 : FpM_invimage_i(GEN A, GEN B, GEN p)
    4802             : {
    4803             :   GEN d, x, X, Y;
    4804          35 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4805          35 :   if (lgefint(p) == 3)
    4806             :   {
    4807           0 :     ulong pp = p[2];
    4808           0 :     A = ZM_to_Flm(A, pp);
    4809           0 :     B = ZM_to_Flm(B, pp);
    4810           0 :     x = Flm_invimage_i(A, B, pp);
    4811           0 :     return x? Flm_to_ZM(x): NULL;
    4812             :   }
    4813          35 :   x = FpM_ker(shallowconcat(ZM_neg(A), B), p);
    4814             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4815             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4816             :    * Y has at least nB columns and full rank */
    4817          35 :   nY = lg(x)-1;
    4818          35 :   if (nY < nB) return NULL;
    4819          28 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4820          28 :   d = cgetg(nB+1, t_VECSMALL);
    4821         217 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4822             :   {
    4823         280 :     for (; j>=1; j--)
    4824         266 :       if (signe(gcoeff(Y,i,j))) { d[i] = j; break; }
    4825         203 :     if (!j) return NULL;
    4826             :   }
    4827             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4828          14 :   Y = vecpermute(Y, d);
    4829          14 :   x = vecpermute(x, d);
    4830          14 :   X = rowslice(x, 1, nA);
    4831          14 :   return FpM_mul(X, FpM_inv_upper_1(Y,p), p);
    4832             : }
    4833             : GEN
    4834           0 : FpM_invimage(GEN A, GEN B, GEN p)
    4835             : {
    4836           0 :   pari_sp av = avma;
    4837           0 :   GEN X = FpM_invimage_i(A,B,p);
    4838           0 :   if (!X) return gc_NULL(av);
    4839           0 :   return gerepileupto(av, X);
    4840             : }
    4841             : 
    4842             : static GEN
    4843          84 : RgM_invimage_FpM(GEN A, GEN B, GEN p)
    4844             : {
    4845          84 :   pari_sp av = avma;
    4846             :   ulong pp;
    4847             :   GEN x;
    4848          84 :   A = RgM_Fp_init(A,p,&pp);
    4849          84 :   switch(pp)
    4850             :   {
    4851             :   case 0:
    4852          35 :     B = RgM_to_FpM(B,p);
    4853          35 :     x = FpM_invimage_i(A, B, p);
    4854          35 :     return x ? gerepileupto(av, FpM_to_mod(x, p)): x;
    4855             :   case 2:
    4856           7 :     B = RgM_to_F2m(B);
    4857           7 :     x = F2m_invimage_i(A, B);
    4858           7 :     return x ? gerepileupto(av, F2m_to_mod(x)): x;
    4859             :   default:
    4860          42 :     B = RgM_to_Flm(B,pp);
    4861          42 :     x = Flm_invimage_i(A, B, pp);
    4862          42 :     return x ? gerepileupto(av, Flm_to_mod(x, pp)): x;
    4863             :   }
    4864             : }
    4865             : 
    4866             : static GEN
    4867         252 : RgM_invimage_fast(GEN x, GEN y)
    4868             : {
    4869             :   GEN p, pol;
    4870             :   long pa;
    4871         252 :   long t = RgM_type2(x, y, &p,&pol,&pa);
    4872         252 :   switch(t)
    4873             :   {
    4874          84 :     case t_INTMOD: return RgM_invimage_FpM(x, y, p);
    4875         105 :     case t_FFELT:  return FFM_invimage(x, y, pol);
    4876          63 :     default:       return gen_0;
    4877             :   }
    4878             : }
    4879             : 
    4880             : /* find Z such that A Z = B. Return NULL if no solution */
    4881             : GEN
    4882         252 : RgM_invimage(GEN A, GEN B)
    4883             : {
    4884         252 :   pari_sp av = avma;
    4885             :   GEN d, x, X, Y;
    4886         252 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4887         252 :   X = RgM_invimage_fast(A, B);
    4888         252 :   if (!X) return gc_NULL(av);
    4889         140 :   if (X != gen_0) return X;
    4890          63 :   x = ker(shallowconcat(RgM_neg(A), B));
    4891             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4892             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4893             :    * Y has at least nB columns and full rank */
    4894          63 :   nY = lg(x)-1;
    4895          63 :   if (nY < nB) return gc_NULL(av);
    4896          49 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4897          49 :   d = cgetg(nB+1, t_VECSMALL);
    4898         441 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4899             :   {
    4900         546 :     for (; j>=1; j--)
    4901         532 :       if (!gequal0(gcoeff(Y,i,j))) { d[i] = j; break; }
    4902         406 :     if (!j) return gc_NULL(av);
    4903             :   }
    4904             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4905          35 :   Y = vecpermute(Y, d);
    4906          35 :   x = vecpermute(x, d);
    4907          35 :   X = rowslice(x, 1, nA);
    4908          35 :   return gerepileupto(av, RgM_mul(X, RgM_inv_upper(Y)));
    4909             : }
    4910             : 
    4911             : /* r = dim Ker x, n = nbrows(x) */
    4912             : static GEN
    4913       36463 : get_suppl(GEN x, GEN d, long n, long r, GEN(*ei)(long,long))
    4914             : {
    4915             :   pari_sp av;
    4916             :   GEN y, c;
    4917       36463 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
    4918             : 
    4919       36463 :   if (rx == n && r == 0) return gcopy(x);
    4920       33630 :   y = cgetg(n+1, t_MAT);
    4921       33630 :   av = avma; c = zero_zv(n);
    4922             :   /* c = lines containing pivots (could get it from gauss_pivot, but cheap)
    4923             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
    4924      258906 :   for (k = j = 1; j<=rx; j++)
    4925      225276 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gel(x,j); }
    4926      344734 :   for (j=1; j<=n; j++)
    4927      311104 :     if (!c[j]) gel(y,k++) = (GEN)j; /* HACK */
    4928       33630 :   set_avma(av);
    4929             : 
    4930       33630 :   rx -= r;
    4931       33630 :   for (j=1; j<=rx; j++) gel(y,j) = gcopy(gel(y,j));
    4932       33630 :   for (   ; j<=n; j++)  gel(y,j) = ei(n, y[j]);
    4933       33630 :   return y;
    4934             : }
    4935             : 
    4936             : static void
    4937       36463 : init_suppl(GEN x)
    4938             : {
    4939       36463 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    4940             :   /* HACK: avoid overwriting d from gauss_pivot() after set_avma(av) */
    4941       36463 :   (void)new_chunk(lgcols(x) * 2);
    4942       36463 : }
    4943             : 
    4944             : GEN
    4945       35084 : FpM_suppl(GEN x, GEN p)
    4946             : {
    4947             :   GEN d;
    4948             :   long r;
    4949       35084 :   init_suppl(x); d = FpM_gauss_pivot(x,p, &r);
    4950       35084 :   return get_suppl(x,d,nbrows(x),r,&col_ei);
    4951             : }
    4952             : 
    4953             : GEN
    4954          63 : Flm_suppl(GEN x, ulong p)
    4955             : {
    4956             :   GEN d;
    4957             :   long r;
    4958          63 :   init_suppl(x); d = Flm_pivots(x, p, &r, 0);
    4959          63 :   return get_suppl(x,d,nbrows(x),r,&vecsmall_ei);
    4960             : }
    4961             : 
    4962             : GEN
    4963          14 : F2m_suppl(GEN x)
    4964             : {
    4965             :   GEN d;
    4966             :   long r;
    4967          14 :   init_suppl(x); d = F2m_gauss_pivot(F2m_copy(x), &r);
    4968          14 :   return get_suppl(x,d,mael(x,1,1),r,&F2v_ei);
    4969             : }
    4970             : 
    4971             : static GEN
    4972          70 : RgM_suppl_FpM(GEN x, GEN p)
    4973             : {
    4974          70 :   pari_sp av = avma;
    4975             :   ulong pp;
    4976          70 :   x = RgM_Fp_init(x, p, &pp);
    4977          70 :   switch(pp)
    4978             :   {
    4979          21 :   case 0: x = FpM_to_mod(FpM_suppl(x,p), p); break;
    4980          14 :   case 2: x = F2m_to_mod(F2m_suppl(x)); break;
    4981          35 :   default:x = Flm_to_mod(Flm_suppl(x,pp), pp); break;
    4982             :   }
    4983          70 :   return gerepileupto(av, x);
    4984             : }
    4985             : 
    4986             : static GEN
    4987         175 : RgM_suppl_fast(GEN x)
    4988             : {
    4989             :   GEN p, pol;
    4990             :   long pa;
    4991         175 :   long t = RgM_type(x,&p,&pol,&pa);
    4992         175 :   switch(t)
    4993             :   {
    4994          70 :     case t_INTMOD: return RgM_suppl_FpM(x, p);
    4995          35 :     case t_FFELT:  return FFM_suppl(x, pol);
    4996          70 :     default:       return NULL;
    4997             :   }
    4998             : }
    4999             : 
    5000             : /* x is an n x k matrix, rank(x) = k <= n. Return an invertible n x n matrix
    5001             :  * whose first k columns are given by x. If rank(x) < k, undefined result. */
    5002             : GEN
    5003         175 : suppl(GEN x)
    5004             : {
    5005         175 :   pari_sp av = avma;
    5006             :   GEN d, M;
    5007             :   long r;
    5008         175 :   if (typ(x)!=t_MAT) pari_err_TYPE("suppl",x);
    5009         175 :   M = RgM_suppl_fast(x);
    5010         175 :   if (M) return M;
    5011          70 :   init_suppl(x);
    5012          70 :   d = gauss_pivot(x,&r);
    5013          70 :   set_avma(av); return get_suppl(x,d,nbrows(x),r,&col_ei);
    5014             : }
    5015             : /* variable number to be filled in later */
    5016             : static GEN
    5017          84 : _FlxC_ei(long n, long i)
    5018             : {
    5019          84 :   GEN x = cgetg(n + 1, t_COL);
    5020             :   long j;
    5021        1652 :   for (j = 1; j <= n; j++)
    5022        1568 :     gel(x, j) = (j == i)? pol1_Flx(0): pol0_Flx(0);
    5023          84 :   return x;
    5024             : }
    5025             : 
    5026             : GEN
    5027           7 : F2xqM_suppl(GEN x, GEN T)
    5028             : {
    5029           7 :   pari_sp av = avma;
    5030             :   GEN d, y;
    5031           7 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    5032             : 
    5033           7 :   init_suppl(x);
    5034           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    5035           7 :   set_avma(av);
    5036           7 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    5037           7 :   if (sv) {
    5038             :     long i, j;
    5039          21 :     for (j = r + 1; j <= n; j++) {
    5040          42 :       for (i = 1; i <= n; i++)
    5041          28 :         gcoeff(y, i, j)[1] = sv;
    5042             :     }
    5043             :   }
    5044           7 :   return y;
    5045             : }
    5046             : 
    5047             : GEN
    5048          14 : FlxqM_suppl(GEN x, GEN T, ulong p)
    5049             : {
    5050          14 :   pari_sp av = avma;
    5051             :   GEN d, y;
    5052          14 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    5053             : 
    5054          14 :   init_suppl(x);
    5055          14 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    5056          14 :   set_avma(av);
    5057          14 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    5058          14 :   if (sv) {
    5059             :     long i, j;
    5060          21 :     for (j = r + 1; j <= n; j++) {
    5061          42 :       for (i = 1; i <= n; i++)
    5062          28 :         gcoeff(y, i, j)[1] = sv;
    5063             :     }
    5064             :   }
    5065          14 :   return y;
    5066             : }
    5067             : 
    5068             : GEN
    5069        4039 : FqM_suppl(GEN x, GEN T, GEN p)
    5070             : {
    5071        4039 :   pari_sp av = avma;
    5072             :   GEN d;
    5073             :   long r;
    5074             : 
    5075        4039 :   if (!T) return FpM_suppl(x,p);
    5076        1211 :   init_suppl(x);
    5077        1211 :   d = FqM_gauss_pivot(x,T,p,&r);
    5078        1211 :   set_avma(av); return get_suppl(x,d,nbrows(x),r,&col_ei);
    5079             : }
    5080             : 
    5081             : GEN
    5082           7 : image2(GEN x)
    5083             : {
    5084           7 :   pari_sp av = avma;
    5085             :   long k, n, i;
    5086             :   GEN A, B;
    5087             : 
    5088           7 :   if (typ(x)!=t_MAT) pari_err_TYPE("image2",x);
    5089           7 :   if (lg(x) == 1) return cgetg(1,t_MAT);
    5090           7 :   A = ker(x); k = lg(A)-1;
    5091           7 :   if (!k) { set_avma(av); return gcopy(x); }
    5092           7 :   A = suppl(A); n = lg(A)-1;
    5093           7 :   B = cgetg(n-k+1, t_MAT);
    5094           7 :   for (i = k+1; i <= n; i++) gel(B,i-k) = RgM_RgC_mul(x, gel(A,i));
    5095           7 :   return gerepileupto(av, B);
    5096             : }
    5097             : 
    5098             : GEN
    5099         210 : matimage0(GEN x,long flag)
    5100             : {
    5101         210 :   switch(flag)
    5102             :   {
    5103         203 :     case 0: return image(x);
    5104           7 :     case 1: return image2(x);
    5105           0 :     default: pari_err_FLAG("matimage");
    5106             :   }
    5107             :   return NULL; /* LCOV_EXCL_LINE */
    5108             : }
    5109             : 
    5110             : static long
    5111         126 : RgM_rank_FpM(GEN x, GEN p)
    5112             : {
    5113         126 :   pari_sp av = avma;
    5114             :   ulong pp;
    5115             :   long r;
    5116         126 :   x = RgM_Fp_init(x,p,&pp);
    5117         126 :   switch(pp)
    5118             :   {
    5119          28 :   case 0: r = FpM_rank(x,p); break;
    5120          63 :   case 2: r = F2m_rank(x); break;
    5121          35 :   default:r = Flm_rank(x,pp); break;
    5122             :   }
    5123         126 :   return gc_long(av, r);
    5124             : }
    5125             : 
    5126             : static long
    5127          49 : RgM_rank_FqM(GEN x, GEN pol, GEN p)
    5128             : {
    5129          49 :   pari_sp av = avma;
    5130             :   long r;
    5131          49 :   GEN T = RgX_to_FpX(pol, p);
    5132          49 :   if (signe(T) == 0) pari_err_OP("rank",x,pol);
    5133          42 :   r = FqM_rank(RgM_to_FqM(x, T, p), T, p);
    5134          42 :   return gc_long(av,r);
    5135             : }
    5136             : 
    5137             : #define code(t1,t2) ((t1 << 6) | t2)
    5138             : static long
    5139         287 : RgM_rank_fast(GEN x)
    5140             : {
    5141             :   GEN p, pol;
    5142             :   long pa;
    5143         287 :   long t = RgM_type(x,&p,&pol,&pa);
    5144         287 :   switch(t)
    5145             :   {
    5146          42 :     case t_INT:    return ZM_rank(x);
    5147           0 :     case t_FRAC:   return QM_rank(x);
    5148         126 :     case t_INTMOD: return RgM_rank_FpM(x, p);
    5149          63 :     case t_FFELT:  return FFM_rank(x, pol);
    5150             :     case code(t_POLMOD, t_INTMOD):
    5151          49 :                    return RgM_rank_FqM(x, pol, p);
    5152           7 :     default:       return -1;
    5153             :   }
    5154             : }
    5155             : #undef code
    5156             : 
    5157             : long
    5158         287 : rank(GEN x)
    5159             : {
    5160         287 :   pari_sp av = avma;
    5161             :   long r;
    5162             : 
    5163         287 :   if (typ(x)!=t_MAT) pari_err_TYPE("rank",x);
    5164         287 :   r = RgM_rank_fast(x);
    5165         280 :   if (r >= 0) return r;
    5166           7 :   (void)gauss_pivot(x, &r);
    5167           7 :   return gc_long(av, lg(x)-1 - r);
    5168             : }
    5169             : 
    5170             : /* d a t_VECSMALL of integers in 1..n. Return the vector of the d[i]
    5171             :  * followed by the missing indices */
    5172             : static GEN
    5173       14768 : perm_complete(GEN d, long n)
    5174             : {
    5175       14768 :   GEN y = cgetg(n+1, t_VECSMALL);
    5176       14768 :   long i, j = 1, k = n, l = lg(d);
    5177       14768 :   pari_sp av = avma;
    5178       14768 :   char *T = stack_calloc(n+1);
    5179       14768 :   for (i = 1; i < l; i++) T[d[i]] = 1;
    5180      141832 :   for (i = 1; i <= n; i++)
    5181      127064 :     if (T[i]) y[j++] = i; else y[k--] = i;
    5182       14768 :   set_avma(av); return y;
    5183             : }
    5184             : 
    5185             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    5186             : static GEN
    5187       95822 : indexrank0(long n, long r, GEN d)
    5188             : {
    5189       95822 :   GEN p1, p2, res = cgetg(3,t_VEC);
    5190             :   long i, j;
    5191             : 
    5192       95822 :   r = n - r; /* now r = dim Im(x) */
    5193       95822 :   p1 = cgetg(r+1,t_VECSMALL); gel(res,1) = p1;
    5194       95822 :   p2 = cgetg(r+1,t_VECSMALL); gel(res,2) = p2;
    5195       95822 :   if (d)
    5196             :   {
    5197      535480 :     for (i=0,j=1; j<=n; j++)
    5198      440568 :       if (d[j]) { i++; p1[i] = d[j]; p2[i] = j; }
    5199       94912 :     vecsmall_sort(p1);
    5200             :   }
    5201       95822 :   return res;
    5202             : }
    5203             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    5204             : static GEN
    5205        2422 : indeximage0(long n, long r, GEN d)
    5206             : {
    5207             :   long i, j;
    5208             :   GEN v;
    5209             : 
    5210        2422 :   r = n - r; /* now r = dim Im(x) */
    5211        2422 :   v = cgetg(r+1,t_VECSMALL);
    5212       17108 :   if (d) for (i=j=1; j<=n; j++)
    5213       14686 :     if (d[j]) v[i++] = j;
    5214        2422 :   return v;
    5215             : }
    5216             : /* x an m x n t_MAT, n > 0, r = dim Ker(x), d from gauss_pivot */
    5217             : static void
    5218        7384 : indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol)
    5219             : {
    5220        7384 :   GEN IR = indexrank0(n, r, d);
    5221        7384 :   *prow = perm_complete(gel(IR,1), m);
    5222        7384 :   *pcol = perm_complete(gel(IR,2), n);
    5223        7384 : }
    5224             : static void
    5225       90860 : init_indexrank(GEN x) {
    5226       90860 :   (void)new_chunk(3 + 2*lg(x)); /* HACK */
    5227       90860 : }
    5228             : 
    5229             : static GEN
    5230          28 : RgM_indexrank_FpM(GEN x, GEN p)
    5231             : {
    5232          28 :   pari_sp av = avma;
    5233             :   ulong pp;
    5234             :   GEN r;
    5235          28 :   x = RgM_Fp_init(x,p,&pp);
    5236          28 :   switch(pp)
    5237             :   {
    5238           7 :   case 0:  r = FpM_indexrank(x,p); break;
    5239           7 :   case 2:  r = F2m_indexrank(x); break;
    5240          14 :   default: r = Flm_indexrank(x,pp); break;
    5241             :   }
    5242          28 :   return gerepileupto(av, r);
    5243             : }
    5244             : 
    5245             : static GEN
    5246           0 : RgM_indexrank_FqM(GEN x, GEN pol, GEN p)
    5247             : {
    5248           0 :   pari_sp av = avma;
    5249           0 :   GEN r, T = RgX_to_FpX(pol, p);
    5250           0 :   if (signe(T) == 0) pari_err_OP("indexrank",x,pol);
    5251           0 :   r = FqM_indexrank(RgM_to_FqM(x, T, p), T, p);
    5252           0 :   return gerepileupto(av, r);
    5253             : }
    5254             : 
    5255             : #define code(t1,t2) ((t1 << 6) | t2)
    5256             : static GEN
    5257       22400 : RgM_indexrank_fast(GEN x)
    5258             : {
    5259             :   GEN p, pol;
    5260             :   long pa;
    5261       22400 :   long t = RgM_type(x,&p,&pol,&pa);
    5262       22400 :   switch(t)
    5263             :   {
    5264         392 :     case t_INT:    return ZM_indexrank(x);
    5265        1148 :     case t_FRAC:   return QM_indexrank(x);
    5266          28 :     case t_INTMOD: return RgM_indexrank_FpM(x, p);
    5267          21 :     case t_FFELT:  return FFM_indexrank(x, pol);
    5268             :     case code(t_POLMOD, t_INTMOD):
    5269           0 :                    return RgM_indexrank_FqM(x, pol, p);
    5270       20811 :     default:       return NULL;
    5271             :   }
    5272             : }
    5273             : #undef code
    5274             : 
    5275             : GEN
    5276       22400 : indexrank(GEN x)
    5277             : {
    5278             :   pari_sp av;
    5279             :   long r;
    5280             :   GEN d;
    5281       22400 :   if (typ(x)!=t_MAT) pari_err_TYPE("indexrank",x);
    5282       22400 :   d = RgM_indexrank_fast(x);
    5283       22400 :   if (d) return d;
    5284       20811 :   av = avma;
    5285       20811 :   init_indexrank(x);
    5286       20811 :   d = gauss_pivot(x, &r);
    5287       20811 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    5288             : }
    5289             : 
    5290             : GEN
    5291       29113 : FpM_indexrank(GEN x, GEN p) {
    5292       29113 :   pari_sp av = avma;
    5293             :   long r;
    5294             :   GEN d;
    5295       29113 :   init_indexrank(x);
    5296       29113 :   d = FpM_gauss_pivot(x,p,&r);
    5297       29113 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    5298             : }
    5299             : 
    5300             : GEN
    5301       19138 : Flm_indexrank(GEN x, ulong p) {
    5302       19138 :   pari_sp av = avma;
    5303             :   long r;
    5304             :   GEN d;
    5305       19138 :   init_indexrank(x);
    5306       19138 :   d = Flm_pivots(x, p, &r, 0);
    5307       19138 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    5308             : }
    5309             : 
    5310             : GEN
    5311           7 : F2m_indexrank(GEN x) {
    5312           7 :   pari_sp av = avma;
    5313             :   long r;
    5314             :   GEN d;
    5315           7 :   init_indexrank(x);
    5316           7 :   d = F2m_gauss_pivot(F2m_copy(x),&r);
    5317           7 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    5318             : }
    5319             : 
    5320             : GEN
    5321           7 : F2xqM_indexrank(GEN x, GEN T) {
    5322           7 :   pari_sp av = avma;
    5323             :   long r;
    5324             :   GEN d;
    5325           7 :   init_indexrank(x);
    5326           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    5327           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    5328             : }
    5329             : 
    5330             : GEN
    5331           7 : FlxqM_indexrank(GEN x, GEN T, ulong p) {
    5332           7 :   pari_sp av = avma;
    5333             :   long r;
    5334             :   GEN d;
    5335           7 :   init_indexrank(x);
    5336           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    5337           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    5338             : }
    5339             : 
    5340             : GEN
    5341           7 : FqM_indexrank(GEN x, GEN T, GEN p) {
    5342           7 :   pari_sp av = avma;
    5343             :   long r;
    5344             :   GEN d;
    5345           7 :   init_indexrank(x);
    5346           7 :   d = FqM_gauss_pivot(x, T, p, &r);
    5347           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    5348             : }
    5349             : 
    5350             : GEN
    5351        2422 : ZM_indeximage(GEN x) {
    5352        2422 :   pari_sp av = avma;
    5353             :   long r;
    5354             :   GEN d;
    5355        2422 :   init_indexrank(x);
    5356        2422 :   d = ZM_pivots(x,&r);
    5357        2422 :   set_avma(av); return indeximage0(lg(x)-1, r, d);
    5358             : }
    5359             : long
    5360       47792 : ZM_rank(GEN x) {
    5361       47792 :   pari_sp av = avma;
    5362             :   long r;
    5363       47792 :   (void)ZM_pivots(x,&r);
    5364       47792 :   return gc_long(av, lg(x)-1-r);
    5365             : }
    5366             : GEN
    5367       19348 : ZM_indexrank(GEN x) {
    5368       19348 :   pari_sp av = avma;
    5369             :   long r;
    5370             :   GEN d;
    5371       19348 :   init_indexrank(x);
    5372       19348 :   d = ZM_pivots(x,&r);
    5373       19348 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    5374             : }
    5375             : 
    5376             : long
    5377           0 : QM_rank(GEN x)
    5378             : {
    5379           0 :   pari_sp av = avma;
    5380           0 :   long r = ZM_rank(Q_primpart(x));
    5381           0 :   set_avma(av);
    5382           0 :   return r;
    5383             : }
    5384             : 
    5385             : GEN
    5386        1148 : QM_indexrank(GEN x)
    5387             : {
    5388        1148 :   pari_sp av = avma;
    5389        1148 :   GEN r = ZM_indexrank(Q_primpart(x));
    5390        1148 :   return gerepileupto(av, r);
    5391             : }
    5392             : 
    5393             : /*******************************************************************/
    5394             : /*                                                                 */
    5395             : /*                             ZabM                                */
    5396             : /*                                                                 */
    5397             : /*******************************************************************/
    5398             : 
    5399             : static GEN
    5400        1866 : FpXM_ratlift(GEN a, GEN q)
    5401             : {
    5402             :   GEN B, y;
    5403        1866 :   long i, j, l = lg(a), n;
    5404        1866 :   B = sqrti(shifti(q,-1));
    5405        1866 :   y = cgetg(l, t_MAT);
    5406        1866 :   if (l==1) return y;
    5407        1866 :   n = lgcols(a);
    5408        5351 :   for (i=1; i<l; i++)
    5409             :   {
    5410        4351 :     GEN yi = cgetg(n, t_COL);
    5411       56840 :     for (j=1; j<n; j++)
    5412             :     {
    5413       53355 :       GEN v = FpX_ratlift(gmael(a,i,j), q, B, B, NULL);
    5414       53355 :       if (!v) return NULL;
    5415       52489 :       gel(yi, j) = RgX_renormalize(v);
    5416             :     }
    5417        3485 :     gel(y,i) = yi;
    5418             :   }
    5419        1000 :   return y;
    5420             : }
    5421             : 
    5422             : static GEN
    5423        4575 : FlmV_recover_pre(GEN a, GEN M, ulong p, ulong pi, long sv)
    5424             : {
    5425        4575 :   GEN a1 = gel(a,1);
    5426        4575 :   long i, j, k, l = lg(a1), n, lM = lg(M);
    5427        4575 :   GEN v = cgetg(lM, t_VECSMALL);
    5428        4575 :   GEN y = cgetg(l, t_MAT);
    5429        4575 :   if (l==1) return y;
    5430        4575 :   n = lgcols(a1);
    5431       34600 :   for (i=1; i<l; i++)
    5432             :   {
    5433       30025 :     GEN yi = cgetg(n, t_COL);
    5434      668588 :     for (j=1; j<n; j++)
    5435             :     {
    5436      638563 :       for (k=1; k<lM; k++) uel(v,k) = umael(gel(a,k),i,j);
    5437      638563 :       gel(yi, j) = Flm_Flc_mul_pre_Flx(M, v, p, pi, sv);
    5438             :     }
    5439       30025 :     gel(y,i) = yi;
    5440             :   }
    5441        4575 :   return y;
    5442             : }
    5443             : 
    5444             : static GEN
    5445           0 : FlkM_inv(GEN M, GEN P, ulong p)
    5446             : {
    5447           0 :   ulong pi = get_Fl_red(p);
    5448           0 :   GEN R = Flx_roots(P, p);
    5449           0 :   long l = lg(R), i;
    5450           0 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    5451           0 :   GEN V = cgetg(l, t_VEC);
    5452           0 :   for(i=1; i<l; i++)
    5453             :   {
    5454           0 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, pi);
    5455           0 :     GEN H = Flm_inv_sp(FlxM_eval_powers_pre(M, pows, p, pi), NULL, p);
    5456           0 :     if (!H) return NULL;
    5457           0 :     gel(V, i) = H;
    5458             :   }
    5459           0 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    5460             : }
    5461             : 
    5462             : static GEN
    5463        2709 : FlkM_adjoint(GEN M, GEN P, ulong p)
    5464             : {
    5465        2709 :   ulong pi = get_Fl_red(p);
    5466        2709 :   GEN R = Flx_roots(P, p);
    5467        2709 :   long l = lg(R), i;
    5468        2709 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    5469        2709 :   GEN V = cgetg(l, t_VEC);
    5470       12778 :   for(i=1; i<l; i++)
    5471             :   {
    5472       10069 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, pi);
    5473       10070 :     gel(V, i) = Flm_adjoint(FlxM_eval_powers_pre(M, pows, p, pi), p);
    5474             :   }
    5475        2709 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    5476             : }
    5477             : 
    5478             : 
    5479             : static GEN
    5480        2500 : ZabM_inv_slice(GEN A, GEN Q, GEN P, GEN *mod)
    5481             : {
    5482        2500 :   pari_sp av = avma;
    5483        2500 :   long i, n = lg(P)-1, w = varn(Q);
    5484             :   GEN H, T;
    5485        2500 :   if (n == 1)
    5486             :   {
    5487        2370 :     ulong p = uel(P,1);
    5488        2370 :     GEN Ap = FqM_to_FlxM(A, Q, utoi(p));
    5489        2370 :     GEN Qp = ZX_to_Flx(Q, p);
    5490        2370 :     GEN Hp = FlkM_adjoint(Ap, Qp, p);
    5491        2370 :     Hp = gerepileupto(av, FlxM_to_ZXM(Hp));
    5492        2370 :     *mod = utoi(p); return Hp;
    5493             :   }
    5494         130 :   T = ZV_producttree(P);
    5495         130 :   A = ZXM_nv_mod_tree(A, P, T, w);
    5496         130 :   Q = ZX_nv_mod_tree(Q, P, T);
    5497         130 :   H = cgetg(n+1, t_VEC);
    5498         469 :   for(i=1; i <= n; i++)
    5499             :   {
    5500         339 :     ulong p = P[i];
    5501         339 :     GEN a = gel(A,i), q = gel(Q, i);
    5502         339 :     gel(H,i) = FlkM_adjoint(a, q, p);
    5503             :   }
    5504         130 :   H = nxMV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    5505         130 :   *mod = gmael(T, lg(T)-1, 1);
    5506         130 :   gerepileall(av, 2, &H, mod);
    5507         130 :   return H;
    5508             : }
    5509             : 
    5510             : GEN
    5511        2500 : ZabM_inv_worker(GEN P, GEN A, GEN Q)
    5512             : {
    5513        2500 :   GEN V = cgetg(3, t_VEC);
    5514        2500 :   gel(V,1) = ZabM_inv_slice(A, Q, P, &gel(V,2));
    5515        2499 :   return V;
    5516             : }
    5517             : 
    5518             : static GEN
    5519        7854 : vecnorml1(GEN a)
    5520             : {
    5521             :   long i, l;
    5522        7854 :   GEN g = cgetg_copy(a, &l);
    5523      128058 :   for (i=1; i<l; i++)
    5524      120204 :     gel(g, i) = gnorml1_fake(gel(a,i));
    5525        7854 :   return g;
    5526             : }
    5527             : 
    5528             : static GEN
    5529        1757 : ZabM_true_Hadamard(GEN a)
    5530             : {
    5531        1757 :   pari_sp av = avma;
    5532        1757 :   long n = lg(a)-1, i;
    5533             :   GEN B;
    5534        1757 :   if (n == 0) return gen_1;
    5535        1757 :   if (n == 1) return gnorml1_fake(gcoeff(a,1,1));
    5536        1183 :   B = gen_1;
    5537        1183 :   for (i = 1; i <= n; i++) B = gmul(B, gnorml2(RgC_gtofp(vecnorml1(gel(a,i)),DEFAULTPREC)));
    5538        1183 :   return gerepileuptoint(av, ceil_safe(sqrtr_abs(B)));
    5539             : }
    5540             : 
    5541             : GEN
    5542        1757 : ZabM_inv(GEN A, GEN Q, long n, GEN *pt_den)
    5543             : {
    5544        1757 :   pari_sp av = avma;
    5545        1757 :   long m = lg(A)-1;
    5546             :   GEN bnd, H, D, d, mod, worker;
    5547        1757 :   if (m == 0)
    5548             :   {
    5549           0 :     if (pt_den) *pt_den = gen_1;
    5550           0 :     return cgetg(1, t_MAT);
    5551             :   }
    5552        1757 :   bnd = ZabM_true_Hadamard(A);
    5553        1757 :   worker = strtoclosure("_ZabM_inv_worker", 2, A, Q);
    5554        1757 :   H = gen_crt("ZabM_inv", worker, mkvecsmall(n), expi(bnd), m, &mod,
    5555             :               nxMV_chinese_center, FpXM_center);
    5556        1757 :   D = RgMrow_RgC_mul(H, gel(A,1), 1);
    5557        1757 :   D = ZX_rem(D, Q);
    5558        1757 :   d = Z_content(mkvec2(H, D));
    5559        1757 :   if (d)
    5560             :   {
    5561         623 :     D = ZX_Z_divexact(D, d);
    5562         623 :     H = Q_div_to_int(H, d);
    5563             :   }
    5564        1757 :   if (pt_den)
    5565             :   {
    5566        1757 :     gerepileall(av, 2, &H, &D);
    5567        1757 :     *pt_den = D; return H;
    5568             :   }
    5569           0 :   return gerepileupto(av, H);
    5570             : }
    5571             : 
    5572             : GEN
    5573           0 : ZabM_inv_ratlift(GEN M, GEN P, long n, GEN *pden)
    5574             : {
    5575           0 :   pari_sp av2, av = avma;
    5576             :   GEN q, H;
    5577           0 :   ulong m = LONG_MAX>>1;
    5578           0 :   ulong p= 1 + m - (m % n);
    5579           0 :   long lM = lg(M);
    5580           0 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    5581             : 
    5582           0 :   av2 = avma;
    5583           0 :   H = NULL;
    5584             :   for(;;)
    5585           0 :   {
    5586             :     GEN Hp, Pp, Mp, Hr;
    5587           0 :     do p += n; while(!uisprime(p));
    5588           0 :     Pp = ZX_to_Flx(P, p);
    5589           0 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    5590           0 :     Hp = FlkM_inv(Mp, Pp, p);
    5591           0 :     if (!Hp) continue;
    5592           0 :     if (!H)
    5593             :     {
    5594           0 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p);
    5595           0 :       q = utoipos(p);
    5596             :     }
    5597             :     else
    5598           0 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    5599           0 :     Hr = FpXM_ratlift(H, q);
    5600           0 :     if (DEBUGLEVEL>5) err_printf("ZabM_inv mod %ld (ratlift=%ld)\n", p,!!Hr);
    5601           0 :     if (Hr) {/* DONE ? */
    5602           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    5603           0 :       GEN MH = ZXQM_mul(Hl, M, P);
    5604           0 :       if (*pden)
    5605           0 :       { if (RgM_isscalar(MH, *pden)) { H = Hl; break; }}
    5606             :       else
    5607           0 :       { if (RgM_isidentity(MH)) { H = Hl; *pden = gen_1; break; } }
    5608             :     }
    5609             : 
    5610           0 :     if (gc_needed(av,2))
    5611             :     {
    5612           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_inv");
    5613           0 :       gerepileall(av2, 2, &H, &q);
    5614             :     }
    5615             :   }
    5616           0 :   gerepileall(av, 2, &H, pden);
    5617           0 :   return H;
    5618             : }
    5619             : 
    5620             : static GEN
    5621        1866 : FlkM_ker(GEN M, GEN P, ulong p)
    5622             : {
    5623        1866 :   ulong pi = get_Fl_red(p);
    5624        1866 :   GEN R = Flx_roots(P, p);
    5625        1866 :   long l = lg(R), i, dP = degpol(P), r;
    5626             :   GEN M1, K, D;
    5627        1866 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    5628        1866 :   GEN V = cgetg(l, t_VEC);
    5629        1866 :   M1 = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,1), dP, p, pi), p, pi);
    5630        1866 :   K = Flm_ker_sp(M1, p, 2);
    5631        1866 :   r = lg(gel(K,1)); D = gel(K,2);
    5632        1866 :   gel(V, 1) = gel(K,1);
    5633        3818 :   for(i=2; i<l; i++)
    5634             :   {
    5635        1952 :     GEN Mi = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), dP, p, pi), p, pi);
    5636        1952 :     GEN K = Flm_ker_sp(Mi, p, 2);
    5637        1952 :     if (lg(gel(K,1)) != r || !zv_equal(D, gel(K,2))) return NULL;
    5638        1952 :     gel(V, i) = gel(K,1);
    5639             :   }
    5640        1866 :   return mkvec2(FlmV_recover_pre(V, W, p, pi, P[1]), D);
    5641             : }
    5642             : 
    5643             : GEN
    5644         966 : ZabM_ker(GEN M, GEN P, long n)
    5645             : {
    5646         966 :   pari_sp av2, av = avma;
    5647             :   GEN q, H, D;
    5648         966 :   ulong m = LONG_MAX>>1;
    5649         966 :   ulong p= 1 + m - (m % n);
    5650         966 :   av2 = avma;
    5651         966 :   H = NULL; D = NULL;
    5652             :   for(;;)
    5653         900 :   {
    5654             :     GEN Kp, Hp, Dp, Pp, Mp, Hr;
    5655       30269 :     do p += n; while(!uisprime(p));
    5656        1866 :     Pp = ZX_to_Flx(P, p);
    5657        1866 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    5658        1866 :     Kp = FlkM_ker(Mp, Pp, p);
    5659        1866 :     if (!Kp) continue;
    5660        1866 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    5661        1866 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    5662        1866 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    5663             :     {
    5664         966 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p); D = Dp;
    5665         966 :       q = utoipos(p);
    5666             :     }
    5667             :     else
    5668         900 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    5669        1866 :     Hr = FpXM_ratlift(H, q);
    5670        1866 :     if (DEBUGLEVEL>5) err_printf("ZabM_ker mod %ld (ratlift=%ld)\n", p,!!Hr);
    5671        1866 :     if (Hr) {/* DONE ? */
    5672        1000 :       GEN Hl = vec_Q_primpart(Hr);
    5673        1000 :       GEN MH = ZXQM_mul(M, Hl,P);
    5674        1000 :       if (gequal0(MH)) { H = Hl;  break; }
    5675             :     }
    5676             : 
    5677         900 :     if (gc_needed(av,2))
    5678             :     {
    5679           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_ker");
    5680           0 :       gerepileall(av2, 3, &H, &D, &q);
    5681             :     }
    5682             :   }
    5683         966 :   return gerepilecopy(av, H);
    5684             : }
    5685             : 
    5686             : GEN
    5687        2352 : ZabM_indexrank(GEN M, GEN P, long n)
    5688             : {
    5689        2352 :   pari_sp av = avma;
    5690        2352 :   ulong m = LONG_MAX>>1;
    5691        2352 :   ulong p = 1+m-(m%n), D = degpol(P);
    5692        2352 :   long lM = lg(M), lmax = 0, c = 0;
    5693             :   GEN v;
    5694             :   for(;;)
    5695         672 :   {
    5696             :     GEN R, Mp, K;
    5697             :     ulong pi;
    5698             :     long l;
    5699       60911 :     do p += n; while (!uisprime(p));
    5700        3024 :     pi = get_Fl_red(p);
    5701        3024 :     R = Flx_roots(ZX_to_Flx(P, p), p);
    5702        3024 :     Mp = FqM_to_FlxM(M, P, utoipos(p));
    5703        3024 :     K = FlxM_eval_powers_pre(Mp, Fl_powers_pre(uel(R,1), D,p,pi), p,pi);
    5704        3024 :     v = Flm_indexrank(K, p);
    5705        3024 :     l = lg(gel(v,2));
    5706        3024 :     if (l == lM) break;
    5707         896 :     if (lmax >= 0 && l > lmax) { lmax = l; c = 0; } else c++;
    5708         896 :     if (c > 2)
    5709             :     { /* probably not maximal rank, expensive check */
    5710         224 :       lM -= lg(ZabM_ker(M, P, n))-1; /* actual rank (+1) */
    5711         224 :       if (lmax == lM) break;
    5712           0 :       lmax = -1; /* disable check */
    5713             :     }
    5714             :   }
    5715        2352 :   return gerepileupto(av, v);
    5716             : }
    5717             : 
    5718             : #if 0
    5719             : GEN
    5720             : ZabM_gauss(GEN M, GEN P, long n, GEN *den)
    5721             : {
    5722             :   pari_sp av = avma;
    5723             :   GEN v, S, W;
    5724             :   v = ZabM_indexrank(M, P, n);
    5725             :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    5726             :   W = ZabM_inv(S, P, n, den);
    5727             :   gerepileall(av,2,&W,den);
    5728             :   return W;
    5729             : }
    5730             : #endif
    5731             : 
    5732             : GEN
    5733         287 : ZabM_pseudoinv(GEN M, GEN P, long n, GEN *pv, GEN *den)
    5734             : {
    5735         287 :   GEN v = ZabM_indexrank(M, P, n);
    5736         287 :   if (pv) *pv = v;
    5737         287 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    5738         287 :   return ZabM_inv(M, P, n, den);
    5739             : }
    5740             : GEN
    5741        4431 : ZM_pseudoinv(GEN M, GEN *pv, GEN *den)
    5742             : {
    5743        4431 :   GEN v = ZM_indexrank(M);
    5744        4431 :   if (pv) *pv = v;
    5745        4431 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    5746        4431 :   return ZM_inv(M, den);
    5747             : }
    5748             : 
    5749             : /*******************************************************************/
    5750             : /*                                                                 */
    5751             : /*                   Structured Elimination                        */
    5752             : /*                                                                 */
    5753             : /*******************************************************************/
    5754             : 
    5755             : static void
    5756      100893 : rem_col(GEN c, long i, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5757             : {
    5758      100893 :   long lc = lg(c), k;
    5759      100893 :   iscol[i] = 0; (*rcol)--;
    5760      892455 :   for (k = 1; k < lc; ++k)
    5761             :   {
    5762      791562 :     Wrow[c[k]]--;
    5763      791562 :     if (Wrow[c[k]]==0) (*rrow)--;
    5764             :   }
    5765      100893 : }
    5766             : 
    5767             : static void
    5768        6051 : rem_singleton(GEN M, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5769             : {
    5770             :   long i, j;
    5771        6051 :   long nbcol = lg(iscol)-1, last;
    5772             :   do
    5773             :   {
    5774        7995 :     last = 0;
    5775    18975058 :     for (i = 1; i <= nbcol; ++i)
    5776    18967063 :       if (iscol[i])
    5777             :       {
    5778     9753455 :         GEN c = gmael(M, i, 1);
    5779     9753455 :         long lc = lg(c);
    5780    91065380 :         for (j = 1; j < lc; ++j)
    5781    81324677 :           if (Wrow[c[j]] == 1)
    5782             :           {
    5783       12752 :             rem_col(c, i, iscol, Wrow, rcol, rrow);
    5784       12752 :             last=1; break;
    5785             :           }
    5786             :       }
    5787        7995 :   } while (last);
    5788        6051 : }
    5789             : 
    5790             : static GEN
    5791        5925 : fill_wcol(GEN M, GEN iscol, GEN Wrow, long *w, GEN wcol)
    5792             : {
    5793        5925 :   long nbcol = lg(iscol)-1;
    5794             :   long i, j, m, last;
    5795             :   GEN per;
    5796       14714 :   for (m = 2, last=0; !last ; m++)
    5797             :   {
    5798    22264401 :     for (i = 1; i <= nbcol; ++i)
    5799             :     {
    5800    22255612 :       wcol[i] = 0;
    5801    22255612 :       if (iscol[i])
    5802             :       {
    5803    11425398 :         GEN c = gmael(M, i, 1);
    5804    11425398 :         long lc = lg(c);
    5805   106714069 :         for (j = 1; j < lc; ++j)
    5806    95288671 :           if (Wrow[c[j]] == m) {  wcol[i]++; last = 1; }
    5807             :       }
    5808             :     }
    5809             :   }
    5810        5925 :   per = vecsmall_indexsort(wcol);
    5811        5925 :   *w = wcol[per[nbcol]];
    5812        5925 :   return per;
    5813             : }
    5814             : 
    5815             : /* M is a RgMs with nbrow rows, A a list of row indices.
    5816             :    Eliminate rows of M with a single entry that do not belong to A,
    5817             :    and the corresponding columns. Also eliminate columns until #colums=#rows.
    5818             :    Return pcol and prow:
    5819             :    pcol is a map from the new columns indices to the old one.
    5820             :    prow is a map from the old rows indices to the new one (0 if removed).
    5821             : */
    5822             : 
    5823             : void
    5824         126 : RgMs_structelim_col(GEN M, long nbcol, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5825             : {
    5826             :   long i,j,k;
    5827         126 :   long lA = lg(A);
    5828         126 :   GEN prow = cgetg(nbrow+1, t_VECSMALL);
    5829         126 :   GEN pcol = zero_zv(nbcol);
    5830         126 :   pari_sp av = avma;
    5831         126 :   long rcol = nbcol, rrow = 0, imin = nbcol - usqrt(nbcol);
    5832         126 :   GEN iscol = const_vecsmall(nbcol, 1);
    5833         126 :   GEN Wrow  = zero_zv(nbrow);
    5834         126 :   GEN wcol = cgetg(nbcol+1, t_VECSMALL);
    5835         126 :   pari_sp av2=avma;
    5836      126721 :   for (i = 1; i <= nbcol; ++i)
    5837             :   {
    5838      126595 :     GEN F = gmael(M, i, 1);
    5839      126595 :     long l = lg(F)-1;
    5840     1118185 :     for (j = 1; j <= l; ++j)
    5841      991590 :       Wrow[F[j]]++;
    5842             :   }
    5843         126 :   for (j = 1; j < lA; ++j)
    5844             :   {
    5845           0 :     if (Wrow[A[j]] == 0) { *p_col=NULL; return; }
    5846           0 :     Wrow[A[j]] = -1;
    5847             :   }
    5848      237272 :   for (i = 1; i <= nbrow; ++i)
    5849      237146 :     if (Wrow[i])
    5850       67635 :       rrow++;
    5851         126 :   rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5852         126 :   if (rcol<rrow) pari_err_BUG("RgMs_structelim, rcol<rrow");
    5853        6177 :   for (; rcol>rrow;)
    5854             :   {
    5855             :     long w;
    5856        5925 :     GEN per = fill_wcol(M, iscol, Wrow, &w, wcol);
    5857       94066 :     for (i = nbcol; i>=imin && wcol[per[i]]>=w && rcol>rrow; i--)
    5858       88141 :       rem_col(gmael(M, per[i], 1), per[i], iscol, Wrow, &rcol, &rrow);
    5859        5925 :     rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5860        5925 :     set_avma(av2);
    5861             :   }
    5862      126721 :   for (j = 1, i = 1; i <= nbcol; ++i)
    5863      126595 :     if (iscol[i])
    5864       25702 :       pcol[j++] = i;
    5865         126 :   setlg(pcol,j);
    5866      237272 :   for (k = 1, i = 1; i <= nbrow; ++i)
    5867      237146 :     prow[i] = Wrow[i] ? k++: 0;
    5868         126 :   set_avma(av);
    5869         126 :   *p_col = pcol; *p_row = prow;
    5870             : }
    5871             : 
    5872             : void
    5873           0 : RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5874             : {
    5875           0 :   RgMs_structelim_col(M, lg(M)-1, nbrow, A, p_col, p_row);
    5876           0 : }
    5877             : 
    5878             : /*******************************************************************/
    5879             : /*                                                                 */
    5880             : /*                        EIGENVECTORS                             */
    5881             : /*   (independent eigenvectors, sorted by increasing eigenvalue)   */
    5882             : /*                                                                 */
    5883             : /*******************************************************************/
    5884             : /* assume x is square of dimension > 0 */
    5885             : static int
    5886          14 : RgM_is_symmetric_cx(GEN x, long bit)
    5887             : {
    5888          14 :   pari_sp av = avma;
    5889          14 :   long i, j, l = lg(x);
    5890         112 :   for (i = 1; i < l; i++)
    5891         392 :     for (j = 1; j < i; j++)
    5892             :     {
    5893         294 :       GEN a = gcoeff(x,i,j), b = gcoeff(x,j,i), c = gsub(a,b);
    5894         294 :       if (!gequal0(c) && gexpo(c) - gexpo(a) > -bit) return gc_long(av,0);
    5895             :     }
    5896          14 :   return gc_long(av,1);
    5897             : }
    5898             : static GEN
    5899          14 : eigen_err(GEN x, long prec, long flag)
    5900             : {
    5901          14 :   pari_sp av = avma;
    5902          14 :   if (!RgM_is_symmetric_cx(x, 10-prec2nbits(prec))) pari_err_PREC("mateigen");
    5903             :   /* approximately symmetric: recover */
    5904          14 :   x = jacobi(x, prec); if (flag) return x;
    5905           7 :   return gerepilecopy(av, gel(x,1));
    5906             : }
    5907             : GEN
    5908          77 : mateigen(GEN x, long flag, long prec)
    5909             : {
    5910             :   GEN y, R, T;
    5911          77 :   long k, l, ex, n = lg(x);
    5912          77 :   pari_sp av = avma;
    5913             : 
    5914          77 :   if (typ(x)!=t_MAT) pari_err_TYPE("eigen",x);
    5915          77 :   if (n != 1 && n != lgcols(x)) pari_err_DIM("eigen");
    5916          77 :   if (flag < 0 || flag > 1) pari_err_FLAG("mateigen");
    5917          77 :   if (n == 1)
    5918             :   {
    5919          14 :     if (flag) retmkvec2(cgetg(1,t_VEC), cgetg(1,t_MAT));
    5920           7 :     return cgetg(1,t_VEC);
    5921             :   }
    5922          63 :   if (n == 2)
    5923             :   {
    5924          14 :     if (flag) retmkvec2(mkveccopy(gcoeff(x,1,1)), matid(1));
    5925           7 :     return matid(1);
    5926             :   }
    5927             : 
    5928          49 :   ex = 16 - prec2nbits(prec);
    5929          49 :   T = charpoly(x,0);
    5930          49 :   if (RgX_is_QX(T))
    5931             :   {
    5932          28 :     T = ZX_radical( Q_primpart(T) );
    5933          28 :     R = nfrootsQ(T);
    5934          28 :     if (lg(R)-1 < degpol(T))
    5935             :     { /* add missing complex roots */
    5936          14 :       GEN r = cleanroots(RgX_div(T, roots_to_pol(R, 0)), prec);
    5937          14 :       settyp(r, t_VEC);
    5938          14 :       R = shallowconcat(R, r);
    5939             :     }
    5940             :   }
    5941             :   else
    5942             :   {
    5943          21 :     GEN r1, v = vectrunc_init(lg(T));
    5944             :     long e;
    5945          21 :     R = cleanroots(T,prec);
    5946          21 :     r1 = NULL;
    5947         133 :     for (k = 1; k < lg(R); k++)
    5948             :     {
    5949         112 :       GEN r2 = gel(R,k), r = grndtoi(r2, &e);
    5950         112 :       if (e < ex) r2 = r;
    5951         112 :       if (r1)
    5952             :       {
    5953          91 :         r = gsub(r1,r2);
    5954          91 :         if (gequal0(r) || gexpo(r) < ex) continue;
    5955             :       }
    5956          70 :       vectrunc_append(v, r2);
    5957          70 :       r1 = r2;
    5958             :     }
    5959          21 :     R = v;
    5960             :   }
    5961             :   /* R = distinct complex roots of charpoly(x) */
    5962          49 :   l = lg(R); y = cgetg(l, t_VEC);
    5963         231 :   for (k = 1; k < l; k++)
    5964             :   {
    5965         196 :     GEN F = ker_aux(RgM_Rg_sub_shallow(x, gel(R,k)), x);
    5966         196 :     long d = lg(F)-1;
    5967         196 :     if (!d) { set_avma(av); return eigen_err(x, prec, flag); }
    5968         182 :     gel(y,k) = F;
    5969         182 :     if (flag) gel(R,k) = const_vec(d, gel(R,k));
    5970             :   }
    5971          35 :   y = shallowconcat1(y);
    5972          35 :   if (lg(y) > n) { set_avma(av); return eigen_err(x, prec, flag); }
    5973             :   /* lg(y) < n if x is not diagonalizable */
    5974          35 :   if (flag) y = mkvec2(shallowconcat1(R), y);
    5975          35 :   return gerepilecopy(av,y);
    5976             : }
    5977             : GEN
    5978           0 : eigen(GEN x, long prec) { return mateigen(x, 0, prec); }
    5979             : 
    5980             : /*******************************************************************/
    5981             : /*                                                                 */
    5982             : /*                           DETERMINANT                           */
    5983             : /*                                                                 */
    5984             : /*******************************************************************/
    5985             : 
    5986             : GEN
    5987        4053 : det0(GEN a,long flag)
    5988             : {
    5989        4053 :   switch(flag)
    5990             :   {
    5991        4039 :     case 0: return det(a);
    5992          14 :     case 1: return det2(a);
    5993           0 :     default: pari_err_FLAG("matdet");
    5994             :   }
    5995             :   return NULL; /* LCOV_EXCL_LINE */
    5996             : }
    5997             : 
    5998             : /* M a 2x2 matrix, returns det(M) */
    5999             : static GEN
    6000        7396 : RgM_det2(GEN M)
    6001             : {
    6002        7396 :   pari_sp av = avma;
    6003        7396 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    6004        7396 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    6005        7396 :   return gerepileupto(av, gsub(gmul(a,d), gmul(b,c)));
    6006             : }
    6007             : /* M a 2x2 ZM, returns det(M) */
    6008             : static GEN
    6009        8946 : ZM_det2(GEN M)
    6010             : {
    6011        8946 :   pari_sp av = avma;
    6012        8946 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    6013        8946 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    6014        8946 :   return gerepileuptoint(av, subii(mulii(a,d), mulii(b, c)));
    6015             : }
    6016             : /* M a 3x3 ZM, return det(M) */
    6017             : static GEN
    6018        2940 : ZM_det3(GEN M)
    6019             : {
    6020        2940 :   pari_sp av = avma;
    6021        2940 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2), c = gcoeff(M,1,3);
    6022        2940 :   GEN d = gcoeff(M,2,1), e = gcoeff(M,2,2), f = gcoeff(M,2,3);
    6023        2940 :   GEN g = gcoeff(M,3,1), h = gcoeff(M,3,2), i = gcoeff(M,3,3);
    6024        2940 :   GEN t, D = signe(i)? mulii(subii(mulii(a,e), mulii(b,d)), i): gen_0;
    6025        2940 :   if (signe(g))
    6026             :   {
    6027        2737 :     t = mulii(subii(mulii(b,f), mulii(c,e)), g);
    6028        2737 :     D = addii(D, t);
    6029             :   }
    6030        2940 :   if (signe(h))
    6031             :   {
    6032        2758 :     t = mulii(subii(mulii(c,d), mulii(a,f)), h);
    6033        2758 :     D = addii(D, t);
    6034             :   }
    6035        2940 :   return gerepileuptoint(av, D);
    6036             : }
    6037             : 
    6038             : static GEN
    6039        9973 : det_simple_gauss(GEN a, GEN data, pivot_fun pivot)
    6040             : {
    6041        9973 :   pari_sp av = avma;
    6042        9973 :   long i,j,k, s = 1, nbco = lg(a)-1;
    6043        9973 :   GEN p, x = gen_1;
    6044             : 
    6045        9973 :   a = RgM_shallowcopy(a);
    6046       79598 :   for (i=1; i<nbco; i++)
    6047             :   {
    6048       69632 :     k = pivot(a, data, i, NULL);
    6049       69632 :     if (k > nbco) return gerepilecopy(av, gcoeff(a,i,i));
    6050       69625 :     if (k != i)
    6051             :     { /* exchange the lines s.t. k = i */
    6052        4338 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    6053        4338 :       s = -s;
    6054             :     }
    6055       69625 :     p = gcoeff(a,i,i);
    6056             : 
    6057       69625 :     x = gmul(x,p);
    6058      407687 :     for (k=i+1; k<=nbco; k++)
    6059             :     {
    6060      338062 :       GEN m = gcoeff(a,i,k);
    6061      338062 :       if (gequal0(m)) continue;
    6062             : 
    6063      117752 :       m = gdiv(m,p);
    6064      778076 :       for (j=i+1; j<=nbco; j++)
    6065      660324 :         gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i)));
    6066             :     }
    6067       69625 :     if (gc_needed(av,2))
    6068             :     {
    6069           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    6070           0 :       gerepileall(av,2, &a,&x);
    6071             :     }
    6072             :   }
    6073        9966 :   if (s < 0) x = gneg_i(x);
    6074        9966 :   return gerepileupto(av, gmul(x, gcoeff(a,nbco,nbco)));
    6075             : }
    6076             : 
    6077             : GEN
    6078        5772 : det2(GEN a)
    6079             : {
    6080             :   GEN data;
    6081             :   pivot_fun pivot;
    6082        5772 :   long n = lg(a)-1;
    6083        5772 :   if (typ(a)!=t_MAT) pari_err_TYPE("det2",a);
    6084        5772 :   if (!n) return gen_1;
    6085        5772 :   if (n != nbrows(a)) pari_err_DIM("det2");
    6086        5772 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    6087        5772 :   if (n == 2) return RgM_det2(a);
    6088        1764 :   pivot = get_pivot_fun(a, a, &data);
    6089        1764 :   return det_simple_gauss(a, data, pivot);
    6090             : }
    6091             : 
    6092             : static GEN
    6093         924 : mydiv(GEN x, GEN y)
    6094             : {
    6095         924 :   long tx = typ(x), ty = typ(y);
    6096         924 :   if (tx == ty && tx == t_POL && varn(x) == varn(y)) return RgX_div(x,y);
    6097         924 :   return gdiv(x,y);
    6098             : }
    6099             : 
    6100             : /* Assumes a a square t_MAT of dimension n > 0. Returns det(a) using
    6101             :  * Gauss-Bareiss. */
    6102             : static GEN
    6103         329 : det_bareiss(GEN a)
    6104             : {
    6105         329 :   pari_sp av = avma;
    6106         329 :   long nbco = lg(a)-1,i,j,k,s = 1;
    6107             :   GEN p, pprec;
    6108             : 
    6109         329 :   a = RgM_shallowcopy(a);
    6110        1057 :   for (pprec=gen_1,i=1; i<nbco; i++,pprec=p)
    6111             :   {
    6112             :     GEN ci;
    6113         728 :     int diveuc = (gequal1(pprec)==0);
    6114             : 
    6115         728 :     p = gcoeff(a,i,i);
    6116         728 :     if (gequal0(p))
    6117             :     {
    6118           0 :       k=i+1; while (k<=nbco && gequal0(gcoeff(a,i,k))) k++;
    6119           0 :       if (k>nbco) return gerepilecopy(av, p);
    6120           0 :       swap(gel(a,k), gel(a,i)); s = -s;
    6121           0 :       p = gcoeff(a,i,i);
    6122             :     }
    6123         728 :     ci = gel(a,i);
    6124        2037 :     for (k=i+1; k<=nbco; k++)
    6125             :     {
    6126        1309 :       GEN ck = gel(a,k), m = gel(ck,i);
    6127        1309 :       if (gequal0(m))
    6128             :       {
    6129           0 :         if (gequal1(p))
    6130             :         {
    6131           0 :           if (diveuc)
    6132           0 :             gel(a,k) = mydiv(gel(a,k), pprec);
    6133             :         }
    6134             :         else
    6135           0 :           for (j=i+1; j<=nbco; j++)
    6136             :           {
    6137           0 :             GEN p1 = gmul(p, gel(ck,j));
    6138           0 :             if (diveuc) p1 = mydiv(p1,pprec);
    6139           0 :             gel(ck,j) = p1;
    6140             :           }
    6141             :       }
    6142             :       else
    6143        4144 :         for (j=i+1; j<=nbco; j++)
    6144             :         {
    6145        2835 :           pari_sp av2 = avma;
    6146        2835 :           GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j)));
    6147        2835 :           if (diveuc) p1 = mydiv(p1,pprec);
    6148        2835 :           gel(ck,j) = gerepileupto(av2, p1);
    6149             :         }
    6150        1309 :       if (gc_needed(av,2))
    6151             :       {
    6152           0 :         if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    6153           0 :         gerepileall(av,2, &a,&pprec);
    6154           0 :         ci = gel(a,i);
    6155           0 :         p = gcoeff(a,i,i);
    6156             :       }
    6157             :     }
    6158             :   }
    6159         329 :   p = gcoeff(a,nbco,nbco);
    6160         329 :   p = (s < 0)? gneg(p): gcopy(p);
    6161         329 :   return gerepileupto(av, p);
    6162             : }
    6163             : 
    6164             : /* count non-zero entries in col j, at most 'max' of them.
    6165             :  * Return their indices */
    6166             : static GEN
    6167        1127 : col_count_non_zero(GEN a, long j, long max)
    6168             : {
    6169        1127 :   GEN v = cgetg(max+1, t_VECSMALL);
    6170        1127 :   GEN c = gel(a,j);
    6171        1127 :   long i, l = lg(a), k = 1;
    6172        4347 :   for (i = 1; i < l; i++)
    6173        4221 :     if (!gequal0(gel(c,i)))
    6174             :     {
    6175        4081 :       if (k > max) return NULL; /* fail */
    6176        3080 :       v[k++] = i;
    6177             :     }
    6178         126 :   setlg(v, k); return v;
    6179             : }
    6180             : /* count non-zero entries in row i, at most 'max' of them.
    6181             :  * Return their indices */
    6182             : static GEN
    6183        1113 : row_count_non_zero(GEN a, long i, long max)
    6184             : {
    6185        1113 :   GEN v = cgetg(max+1, t_VECSMALL);
    6186        1113 :   long j, l = lg(a), k = 1;
    6187        4291 :   for (j = 1; j < l; j++)
    6188        4179 :     if (!gequal0(gcoeff(a,i,j)))
    6189             :     {
    6190        4067 :       if (k > max) return NULL; /* fail */
    6191        3066 :       v[k++] = j;
    6192             :     }
    6193         112 :   setlg(v, k); return v;
    6194             : }
    6195             : 
    6196             : static GEN det_develop(GEN a, long max, double bound);
    6197             : /* (-1)^(i+j) a[i,j] * det RgM_minor(a,i,j) */
    6198             : static GEN
    6199         210 : coeff_det(GEN a, long i, long j, long max, double bound)
    6200             : {
    6201         210 :   GEN c = gcoeff(a, i, j);
    6202         210 :   c = gmul(c, det_develop(RgM_minor(a, i,j), max, bound));
    6203         210 :   if (odd(i+j)) c = gneg(c);
    6204         210 :   return c;
    6205             : }
    6206             : /* a square t_MAT, 'bound' a rough upper bound for the number of
    6207             :  * multiplications we are willing to pay while developing rows/columns before
    6208             :  * switching to Gaussian elimination */
    6209             : static GEN
    6210         441 : det_develop(GEN M, long max, double bound)
    6211             : {
    6212         441 :   pari_sp av = avma;
    6213         441 :   long i,j, n = lg(M)-1, lbest = max+2, best_col = 0, best_row = 0;
    6214         441 :   GEN best = NULL;
    6215             : 
    6216         441 :   if (bound < 1.) return det_bareiss(M); /* too costly now */
    6217             : 
    6218         329 :   switch(n)
    6219             :   {
    6220           0 :     case 0: return gen_1;
    6221           0 :     case 1: return gcopy(gcoeff(M,1,1));
    6222          14 :     case 2: return RgM_det2(M);
    6223             :   }
    6224         315 :   if (max > ((n+2)>>1)) max = (n+2)>>1;
    6225        1428 :   for (j = 1; j <= n; j++)
    6226             :   {
    6227        1127 :     pari_sp av2 = avma;
    6228        1127 :     GEN v = col_count_non_zero(M, j, max);
    6229             :     long lv;
    6230        1127 :     if (!v || (lv = lg(v)) >= lbest) { set_avma(av2); continue; }
    6231          98 :     if (lv == 1) { set_avma(av); return gen_0; }
    6232          98 :     if (lv == 2) {
    6233          14 :       set_avma(av);
    6234          14 :       return gerepileupto(av, coeff_det(M,v[1],j,max,bound));
    6235             :     }
    6236          84 :     best = v; lbest = lv; best_col = j;
    6237             :   }
    6238        1414 :   for (i = 1; i <= n; i++)
    6239             :   {
    6240        1113 :     pari_sp av2 = avma;
    6241        1113 :     GEN v = row_count_non_zero(M, i, max);
    6242             :     long lv;
    6243        1113 :     if (!v || (lv = lg(v)) >= lbest) { set_avma(av2); continue; }
    6244           0 :     if (lv == 1) { set_avma(av); return gen_0; }
    6245           0 :     if (lv == 2) {
    6246           0 :       set_avma(av);
    6247           0 :       return gerepileupto(av, coeff_det(M,i,v[1],max,bound));
    6248             :     }
    6249           0 :     best = v; lbest = lv; best_row = i;
    6250             :   }
    6251         301 :   if (best_row)
    6252             :   {
    6253           0 :     double d = lbest-1;
    6254           0 :     GEN s = NULL;
    6255             :     long k;
    6256           0 :     bound /= d*d*d;
    6257           0 :     for (k = 1; k < lbest; k++)
    6258             :     {
    6259           0 :       GEN c = coeff_det(M, best_row, best[k], max, bound);
    6260           0 :       s = s? gadd(s, c): c;
    6261             :     }
    6262           0 :     return gerepileupto(av, s);
    6263             :   }
    6264         301 :   if (best_col)
    6265             :   {
    6266          84 :     double d = lbest-1;
    6267          84 :     GEN s = NULL;
    6268             :     long k;
    6269          84 :     bound /= d*d*d;
    6270         280 :     for (k = 1; k < lbest; k++)
    6271             :     {
    6272         196 :       GEN c = coeff_det(M, best[k], best_col, max, bound);
    6273         196 :       s = s? gadd(s, c): c;
    6274             :     }
    6275          84 :     return gerepileupto(av, s);
    6276             :   }
    6277         217 :   return det_bareiss(M);
    6278             : }
    6279             : 
    6280             : /* area of parallelogram bounded by (v1,v2) */
    6281             : static GEN
    6282       53991 : parallelogramarea(GEN v1, GEN v2)
    6283       53991 : { return gsub(gmul(gnorml2(v1), gnorml2(v2)), gsqr(RgV_dotproduct(v1, v2))); }
    6284             : 
    6285             : /* Square of Hadamard bound for det(a), a square matrix.
    6286             :  * Slightly improvement: instead of using the column norms, use the area of
    6287             :  * the parallelogram formed by pairs of consecutive vectors */
    6288             : GEN
    6289       17038 : RgM_Hadamard(GEN a)
    6290             : {
    6291       17038 :   pari_sp av = avma;
    6292       17038 :   long n = lg(a)-1, i;
    6293             :   GEN B;
    6294       17038 :   if (n == 0) return gen_1;
    6295       17038 :   if (n == 1) return gsqr(gcoeff(a,1,1));
    6296       17038 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    6297       17038 :   B = gen_1;
    6298       71029 :   for (i = 1; i <= n/2; i++)
    6299       53991 :     B = gmul(B, parallelogramarea(gel(a,2*i-1), gel(a,2*i)));
    6300       17038 :   if (odd(n)) B = gmul(B, gnorml2(gel(a, n)));
    6301       17038 :   return gerepileuptoint(av, ceil_safe(B));
    6302             : }
    6303             : 
    6304             : /* If B=NULL, assume B=A' */
    6305             : static GEN
    6306       53568 : ZM_det_slice(GEN A, GEN P, GEN *mod)
    6307             : {
    6308       53568 :   pari_sp av = avma;
    6309       53568 :   long i, n = lg(P)-1;
    6310             :   GEN H, T;
    6311       53568 :   if (n == 1)
    6312             :   {
    6313       42308 :     ulong Hp, p = uel(P,1);
    6314       42308 :     GEN a = ZM_to_Flm(A, p);
    6315       42309 :     Hp = Flm_det_sp(a, p);
    6316       42305 :     set_avma(av);
    6317       42306 :     *mod = utoi(p); return utoi(Hp);
    6318             :   }
    6319       11260 :   T = ZV_producttree(P);
    6320       11260 :   A = ZM_nv_mod_tree(A, P, T);
    6321       11260 :   H = cgetg(n+1, t_VECSMALL);
    6322       39856 :   for(i=1; i <= n; i++)
    6323             :   {
    6324       28596 :     ulong p = P[i];
    6325       28596 :     GEN a = gel(A,i);
    6326       28596 :     H[i] = Flm_det_sp(a, p);
    6327             :   }
    6328       11260 :   H = ZV_chinese_tree(H, P, T, ZV_chinesetree(P,T));
    6329       11258 :   *mod = gmael(T, lg(T)-1, 1);
    6330       11258 :   gerepileall(av, 2, &H, mod);
    6331       11260 :   return H;
    6332             : }
    6333             : 
    6334             : GEN
    6335       53570 : ZM_det_worker(GEN P, GEN A)
    6336             : {
    6337       53570 :   GEN V = cgetg(3, t_VEC);
    6338       53568 :   gel(V,1) = ZM_det_slice(A, P, &gel(V,2));
    6339       53564 :   return V;
    6340             : }
    6341             : 
    6342             : /* assume dim(a) = n > 0 */
    6343             : static GEN
    6344       29764 : ZM_det_i(GEN M, long n)
    6345             : {
    6346       29764 :   const long DIXON_THRESHOLD = 40;
    6347       29764 :   pari_sp av = avma, av2;
    6348             :   long i;
    6349       29764 :   ulong p, Dp = 1;
    6350             :   forprime_t S;
    6351             :   pari_timer ti;
    6352             :   GEN H, D, mod, h, q, v, worker;
    6353       29764 :   if (n == 1) return icopy(gcoeff(M,1,1));
    6354       28924 :   if (n == 2) return ZM_det2(M);
    6355       19978 :   if (n == 3) return ZM_det3(M);
    6356       17038 :   if (DEBUGLEVEL >=4) timer_start(&ti);
    6357       17038 :   h = RgM_Hadamard(M);
    6358       17038 :   if (!signe(h)) { set_avma(av); return gen_0; }
    6359       17038 :   h = sqrti(h); q = gen_1;
    6360       17038 :   init_modular_big(&S);
    6361       17038 :   p = 0; /* -Wall */
    6362       34076 :   while( cmpii(q, h) <= 0 && (p = u_forprime_next(&S)) )
    6363             :   {
    6364       17038 :     av2 = avma; Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    6365       17038 :     set_avma(av2);
    6366       17038 :     if (Dp) break;
    6367           0 :     q = muliu(q, p);
    6368             :   }
    6369       17038 :   if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    6370       17038 :   if (!Dp) { set_avma(av); return gen_0; }
    6371       17038 :   if (n <= DIXON_THRESHOLD)
    6372       17038 :     D = q;
    6373             :   else
    6374             :   {
    6375           0 :     av2 = avma;
    6376           0 :     v = cgetg(n+1, t_COL);
    6377           0 :     gel(v, 1) = gen_1; /* ensure content(v) = 1 */
    6378           0 :     for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    6379           0 :     D = Q_denom(ZM_gauss(M, v));
    6380           0 :     if (expi(D) < expi(h) >> 1)
    6381             :     { /* First try unlucky, try once more */
    6382           0 :       for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    6383           0 :       D = lcmii(D, Q_denom(ZM_gauss(M, v)));
    6384             :     }
    6385           0 :     D = gerepileuptoint(av2, D);
    6386           0 :     if (q != gen_1) D = lcmii(D, q);
    6387             :   }
    6388             :   /* determinant is a multiple of D */
    6389       17038 :   if (DEBUGLEVEL >=4)
    6390           0 :     timer_printf(&ti,"ZM_det: Dixon %ld/%ld bits",expi(D),expi(h));
    6391       17038 :   h = divii(h, D);
    6392       17038 :   worker = strtoclosure("_ZM_det_worker", 1, M);
    6393       17038 :   H = gen_crt("ZM_det", worker, D, expi(h)+1, lg(M)-1, &mod, ZV_chinese, NULL);
    6394       17038 :   if (D) H = Fp_div(H, D, mod);
    6395       17038 :   H = Fp_center(H, mod, shifti(mod,-1));
    6396       17038 :   if (D) H = mulii(H, D);
    6397       17038 :   return gerepileuptoint(av, H);
    6398             : }
    6399             : 
    6400             : static GEN
    6401        1519 : RgM_det_FpM(GEN a, GEN p)
    6402             : {
    6403        1519 :   pari_sp av = avma;
    6404             :   ulong pp, d;
    6405        1519 :   a = RgM_Fp_init(a,p,&pp);
    6406        1519 :   switch(pp)
    6407             :   {
    6408          70 :   case 0: return gerepileupto(av, Fp_to_mod(FpM_det(a,p),p)); break;
    6409          14 :   case 2: d = F2m_det(a); break;
    6410        1435 :   default:d = Flm_det_sp(a, pp); break;
    6411             :   }
    6412        1449 :   set_avma(av); return mkintmodu(d, pp);
    6413             : }
    6414             : 
    6415             : static GEN
    6416          42 : RgM_det_FqM(GEN x, GEN pol, GEN p)
    6417             : {
    6418          42 :   pari_sp av = avma;
    6419          42 :   GEN b, T = RgX_to_FpX(pol, p);
    6420          42 :   if (signe(T) == 0) pari_err_OP("%",x,pol);
    6421          42 :   b = FqM_det(RgM_to_FqM(x, T, p), T, p);
    6422          42 :   if (!b) return gc_NULL(av);
    6423          42 :   return gerepilecopy(av, mkpolmod(FpX_to_mod(b, p), FpX_to_mod(T, p)));
    6424             : }
    6425             : 
    6426             : #define code(t1,t2) ((t1 << 6) | t2)
    6427             : static GEN
    6428       10575 : RgM_det_fast(GEN x)
    6429             : {
    6430             :   GEN p, pol;
    6431             :   long pa;
    6432       10575 :   long t = RgM_type(x, &p,&pol,&pa);
    6433       10575 :   switch(t)
    6434             :   {
    6435         315 :     case t_INT:    return ZM_det(x);
    6436         196 :     case t_FRAC:   return QM_det(x);
    6437          63 :     case t_FFELT:  return FFM_det(x, pol);
    6438        1519 :     case t_INTMOD: return RgM_det_FpM(x, p);
    6439             :     case code(t_POLMOD, t_INTMOD):
    6440          42 :                    return RgM_det_FqM(x, pol, p);
    6441        8440 :     default:       return NULL;
    6442             :   }
    6443             : }
    6444             : #undef code
    6445             : 
    6446             : static long
    6447         231 : det_init_max(long n)
    6448             : {
    6449         231 :   if (n > 100) return 0;
    6450         231 :   if (n > 50) return 1;
    6451         231 :   if (n > 30) return 4;
    6452         231 :   return 7;
    6453             : }
    6454             : 
    6455             : GEN
    6456       14395 : det(GEN a)
    6457             : {
    6458       14395 :   long n = lg(a)-1;
    6459             :   double B;
    6460             :   GEN data, b;
    6461             :   pivot_fun pivot;
    6462             : 
    6463       14395 :   if (typ(a)!=t_MAT) pari_err_TYPE("det",a);
    6464       14395 :   if (!n) return gen_1;
    6465       14353 :   if (n != nbrows(a)) pari_err_DIM("det");
    6466       14346 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    6467       13949 :   if (n == 2) return RgM_det2(a);
    6468       10575 :   b = RgM_det_fast(a);
    6469       10575 :   if (b) return b;
    6470        8440 :   pivot = get_pivot_fun(a, a, &data);
    6471        8440 :   if (pivot != gauss_get_pivot_NZ) return det_simple_gauss(a, data, pivot);
    6472         231 :   B = (double)n;
    6473         231 :   return det_develop(a, det_init_max(n), B*B*B);
    6474             : }
    6475             : 
    6476             : GEN
    6477       29771 : ZM_det(GEN a)
    6478             : {
    6479       29771 :   long n = lg(a)-1;
    6480       29771 :   if (!n) return gen_1;
    6481       29764 :   return ZM_det_i(a, n);
    6482             : }
    6483             : 
    6484             : GEN
    6485         196 : QM_det(GEN M)
    6486             : {
    6487         196 :   pari_sp av = avma;
    6488         196 :   GEN cM, pM = Q_primitive_part(M, &cM);
    6489         196 :   GEN b = ZM_det(pM);
    6490         196 :   if (cM) b = gmul(b, gpowgs(cM, lg(M)-1));
    6491         196 :   return gerepileupto(av, b);
    6492             : }

Generated by: LCOV version 1.13