Code coverage tests

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

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

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

LCOV - code coverage report
Current view: top level - basemath - alglin1.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.10.0 lcov report (development 21343-6216058) Lines: 3339 3565 93.7 %
Date: 2017-11-19 06:21:17 Functions: 312 331 94.3 %
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        1149 :   for (u=t+1; u<=m; u++)
      39             :   {
      40        1083 :     A = (pari_sp)coeff(x,u,k);
      41        1083 :     if (A < av && A >= bot) coeff(x,u,k) += dec;
      42             :   }
      43        1896 :   for (i=k+1; i<=n; i++)
      44       74730 :     for (u=1; u<=m; u++)
      45             :     {
      46       72900 :       A = (pari_sp)coeff(x,u,i);
      47       72900 :       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        1896 :   for (i=k+1; i<=n; i++)
      60        1830 :     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          34 : gerepile_gauss(GEN x,long k,long t,pari_sp av, long j, GEN c)
      85             : {
      86          34 :   pari_sp tetpil = avma, A, bot;
      87          34 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      88             :   size_t dec;
      89             : 
      90          34 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot. k=%ld, n=%ld",k,n);
      91         573 :   for (u=t+1; u<=m; u++)
      92         539 :     if (u==j || !c[u]) COPY(gcoeff(x,u,k));
      93        1262 :   for (u=1; u<=m; u++)
      94        1228 :     if (u==j || !c[u])
      95        1044 :       for (i=k+1; i<=n; i++) COPY(gcoeff(x,u,i));
      96             : 
      97          34 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      98          34 :   bot = pari_mainstack->bot;
      99         573 :   for (u=t+1; u<=m; u++)
     100         539 :     if (u==j || !c[u])
     101             :     {
     102         485 :       A=(pari_sp)coeff(x,u,k);
     103         485 :       if (A<av && A>=bot) coeff(x,u,k)+=dec;
     104             :     }
     105        1262 :   for (u=1; u<=m; u++)
     106        1228 :     if (u==j || !c[u])
     107       52860 :       for (i=k+1; i<=n; i++)
     108             :       {
     109       51816 :         A=(pari_sp)coeff(x,u,i);
     110       51816 :         if (A<av && A>=bot) coeff(x,u,i)+=dec;
     111             :       }
     112          34 : }
     113             : 
     114             : /*******************************************************************/
     115             : /*                                                                 */
     116             : /*                         GENERIC                                 */
     117             : /*                                                                 */
     118             : /*******************************************************************/
     119             : GEN
     120        2822 : gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff)
     121             : {
     122        2822 :   pari_sp av0 = avma, av, tetpil;
     123             :   GEN y, c, d;
     124             :   long i, j, k, r, t, n, m;
     125             : 
     126        2822 :   n=lg(x)-1; if (!n) return cgetg(1,t_MAT);
     127        2822 :   m=nbrows(x); r=0;
     128        2822 :   x = RgM_shallowcopy(x);
     129        2822 :   c = zero_zv(m);
     130        2822 :   d=new_chunk(n+1);
     131        2822 :   av=avma;
     132       25209 :   for (k=1; k<=n; k++)
     133             :   {
     134      345495 :     for (j=1; j<=m; j++)
     135      337623 :       if (!c[j])
     136             :       {
     137      108199 :         gcoeff(x,j,k) = ff->red(E, gcoeff(x,j,k));
     138      108199 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     139             :       }
     140       22436 :     if (j>m)
     141             :     {
     142        7872 :       if (deplin)
     143             :       {
     144          49 :         GEN c = cgetg(n+1, t_COL), g0 = ff->s(E,0), g1=ff->s(E,1);
     145          49 :         for (i=1; i<k; i++) gel(c,i) = ff->red(E, gcoeff(x,d[i],k));
     146          49 :         gel(c,k) = g1; for (i=k+1; i<=n; i++) gel(c,i) = g0;
     147          49 :         return gerepileupto(av0, c);
     148             :       }
     149        7823 :       r++; d[k]=0;
     150       78384 :       for(j=1; j<k; j++)
     151       70561 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
     152             :     }
     153             :     else
     154             :     {
     155       14564 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     156       14564 :       c[j] = k; d[k] = j;
     157       14564 :       gcoeff(x,j,k) = ff->s(E,-1);
     158       14564 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     159      474491 :       for (t=1; t<=m; t++)
     160             :       {
     161      459927 :         if (t==j) continue;
     162             : 
     163      445363 :         piv = ff->red(E,gcoeff(x,t,k));
     164      445363 :         if (ff->equal0(piv)) continue;
     165             : 
     166      102089 :         gcoeff(x,t,k) = ff->s(E,0);
     167     1425380 :         for (i=k+1; i<=n; i++)
     168     1323291 :            gcoeff(x,t,i) = ff->add(E, gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     169      102089 :         if (gc_needed(av,1))
     170          66 :           gen_gerepile_gauss_ker(x,k,t,av,E,ff->red);
     171             :       }
     172             :     }
     173             :   }
     174        2773 :   if (deplin) { avma = av0; return NULL; }
     175             : 
     176        2731 :   tetpil=avma; y=cgetg(r+1,t_MAT);
     177       10554 :   for (j=k=1; j<=r; j++,k++)
     178             :   {
     179        7823 :     GEN C = cgetg(n+1,t_COL);
     180        7823 :     GEN g0 = ff->s(E,0), g1 = ff->s(E,1);
     181        7823 :     gel(y,j) = C; while (d[k]) k++;
     182       78384 :     for (i=1; i<k; i++)
     183       70561 :       if (d[i])
     184             :       {
     185       36244 :         GEN p1=gcoeff(x,d[i],k);
     186       36244 :         gel(C,i) = ff->red(E,p1); gunclone(p1);
     187             :       }
     188             :       else
     189       34317 :         gel(C,i) = g0;
     190        7823 :     gel(C,k) = g1; for (i=k+1; i<=n; i++) gel(C,i) = g0;
     191             :   }
     192        2731 :   return gerepile(av0,tetpil,y);
     193             : }
     194             : 
     195             : GEN
     196        1466 : gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff)
     197             : {
     198             :   pari_sp av;
     199             :   GEN c, d;
     200        1466 :   long i, j, k, r, t, m, n = lg(x)-1;
     201             : 
     202        1466 :   if (!n) { *rr = 0; return NULL; }
     203             : 
     204        1466 :   m=nbrows(x); r=0;
     205        1466 :   d = cgetg(n+1, t_VECSMALL);
     206        1466 :   x = RgM_shallowcopy(x);
     207        1466 :   c = zero_zv(m);
     208        1466 :   av=avma;
     209        7974 :   for (k=1; k<=n; k++)
     210             :   {
     211       67267 :     for (j=1; j<=m; j++)
     212       66440 :       if (!c[j])
     213             :       {
     214       48976 :         gcoeff(x,j,k) = ff->red(E,gcoeff(x,j,k));
     215       48976 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     216             :       }
     217        6508 :     if (j>m) { r++; d[k]=0; }
     218             :     else
     219             :     {
     220        5681 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     221        5681 :       GEN g0 = ff->s(E,0);
     222        5681 :       c[j] = k; d[k] = j;
     223        5681 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     224       56264 :       for (t=1; t<=m; t++)
     225             :       {
     226       50583 :         if (c[t]) continue; /* already a pivot on that line */
     227             : 
     228       31087 :         piv = ff->red(E,gcoeff(x,t,k));
     229       31087 :         if (ff->equal0(piv)) continue;
     230       14854 :         gcoeff(x,t,k) = g0;
     231      189665 :         for (i=k+1; i<=n; i++)
     232      174811 :           gcoeff(x,t,i) = ff->add(E,gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     233       14854 :         if (gc_needed(av,1))
     234          10 :           gerepile_gauss(x,k,t,av,j,c);
     235             :       }
     236        5681 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = g0; /* dummy */
     237             :     }
     238             :   }
     239        1466 :   *rr = r; avma = (pari_sp)d; return d;
     240             : }
     241             : 
     242             : GEN
     243         140 : gen_det(GEN a, void *E, const struct bb_field *ff)
     244             : {
     245         140 :   pari_sp av = avma;
     246         140 :   long i,j,k, s = 1, nbco = lg(a)-1;
     247         140 :   GEN q, x = ff->s(E,1);
     248         140 :   if (!nbco) return x;
     249         133 :   a = RgM_shallowcopy(a);
     250        1631 :   for (i=1; i<nbco; i++)
     251             :   {
     252        1505 :     for(k=i; k<=nbco; k++)
     253             :     {
     254        1505 :       gcoeff(a,k,i) = ff->red(E,gcoeff(a,k,i));
     255        1505 :       if (!ff->equal0(gcoeff(a,k,i))) break;
     256             :     }
     257        1498 :     if (k > nbco) return gerepileupto(av, gcoeff(a,i,i));
     258        1498 :     if (k != i)
     259             :     { /* exchange the lines s.t. k = i */
     260           7 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     261           7 :       s = -s;
     262             :     }
     263        1498 :     q = gcoeff(a,i,i);
     264             : 
     265        1498 :     x = ff->red(E,ff->mul(E,x,q));
     266        1498 :     q = ff->inv(E,q);
     267       15526 :     for (k=i+1; k<=nbco; k++)
     268             :     {
     269       14028 :       GEN m = ff->red(E,gcoeff(a,i,k));
     270       14028 :       if (ff->equal0(m)) continue;
     271             : 
     272       13902 :       m = ff->neg(E, ff->mul(E,m, q));
     273      203630 :       for (j=i+1; j<=nbco; j++)
     274             :       {
     275      189728 :         gcoeff(a,j,k) = ff->add(E, gcoeff(a,j,k), ff->mul(E,m,gcoeff(a,j,i)));
     276      189728 :         if (gc_needed(av,1))
     277             :         {
     278         968 :           if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
     279         968 :           gerepileall(av,4, &a,&x,&q,&m);
     280             :         }
     281             :       }
     282             :     }
     283             :   }
     284         133 :   if (s < 0) x = ff->neg(E,x);
     285         133 :   return gerepileupto(av, ff->red(E,ff->mul(E, x, gcoeff(a,nbco,nbco))));
     286             : }
     287             : 
     288             : INLINE void
     289      824715 : _gen_addmul(GEN b, long k, long i, GEN m, void *E, const struct bb_field *ff)
     290             : {
     291      824715 :   gel(b,i) = ff->red(E,gel(b,i));
     292      824715 :   gel(b,k) = ff->add(E,gel(b,k), ff->mul(E,m, gel(b,i)));
     293      824715 : }
     294             : 
     295             : static GEN
     296       51358 : _gen_get_col(GEN a, GEN b, long li, void *E, const struct bb_field *ff)
     297             : {
     298       51358 :   GEN u = cgetg(li+1,t_COL);
     299       51358 :   pari_sp av = avma;
     300             :   long i, j;
     301             : 
     302       51358 :   gel(u,li) = gerepileupto(av, ff->red(E,ff->mul(E,gel(b,li), gcoeff(a,li,li))));
     303      289869 :   for (i=li-1; i>0; i--)
     304             :   {
     305      238511 :     pari_sp av = avma;
     306      238511 :     GEN m = gel(b,i);
     307      238511 :     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      238511 :     m = ff->red(E, m);
     309      238511 :     gel(u,i) = gerepileupto(av, ff->red(E,ff->mul(E,m, gcoeff(a,i,i))));
     310             :   }
     311       51358 :   return u;
     312             : }
     313             : 
     314             : GEN
     315       11440 : gen_Gauss(GEN a, GEN b, void *E, const struct bb_field *ff)
     316             : {
     317             :   long i, j, k, li, bco, aco;
     318       11440 :   GEN u, g0 = ff->s(E,0);
     319       11440 :   pari_sp av = avma;
     320       11440 :   a = RgM_shallowcopy(a);
     321       11440 :   b = RgM_shallowcopy(b);
     322       11440 :   aco = lg(a)-1; bco = lg(b)-1; li = nbrows(a);
     323       51743 :   for (i=1; i<=aco; i++)
     324             :   {
     325             :     GEN invpiv;
     326       68451 :     for (k = i; k <= li; k++)
     327             :     {
     328       68353 :       GEN piv = ff->red(E,gcoeff(a,k,i));
     329       68353 :       if (!ff->equal0(piv)) { gcoeff(a,k,i) = ff->inv(E,piv); break; }
     330       16708 :       gcoeff(a,k,i) = g0;
     331             :     }
     332             :     /* found a pivot on line k */
     333       51743 :     if (k > li) return NULL;
     334       51645 :     if (k != i)
     335             :     { /* swap lines so that k = i */
     336        8986 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     337        8986 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
     338             :     }
     339       51645 :     if (i == aco) break;
     340             : 
     341       40303 :     invpiv = gcoeff(a,i,i); /* 1/piv mod p */
     342      169019 :     for (k=i+1; k<=li; k++)
     343             :     {
     344      128716 :       GEN m = ff->red(E,gcoeff(a,k,i)); gcoeff(a,k,i) = g0;
     345      128716 :       if (ff->equal0(m)) continue;
     346             : 
     347       38086 :       m = ff->red(E,ff->neg(E,ff->mul(E,m, invpiv)));
     348       38086 :       for (j=i+1; j<=aco; j++) _gen_addmul(gel(a,j),k,i,m,E,ff);
     349       38086 :       for (j=1  ; j<=bco; j++) _gen_addmul(gel(b,j),k,i,m,E,ff);
     350             :     }
     351       40303 :     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       11342 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
     359       11342 :   u = cgetg(bco+1,t_MAT);
     360       11342 :   for (j=1; j<=bco; j++) gel(u,j) = _gen_get_col(a, gel(b,j), aco, E, ff);
     361       11342 :   return u;
     362             : }
     363             : 
     364             : /* compatible t_MAT * t_COL, lgA = lg(A) = lg(B) > 1, l = lgcols(A) */
     365             : static GEN
     366      303674 : gen_matcolmul_i(GEN A, GEN B, ulong lgA, ulong l,
     367             :                 void *E, const struct bb_field *ff)
     368             : {
     369      303674 :   GEN C = cgetg(l, t_COL);
     370             :   ulong i;
     371     2066134 :   for (i = 1; i < l; i++) {
     372     1762460 :     pari_sp av = avma;
     373     1762460 :     GEN e = ff->mul(E, gcoeff(A, i, 1), gel(B, 1));
     374             :     ulong k;
     375    10427368 :     for(k = 2; k < lgA; k++)
     376     8664908 :       e = ff->add(E, e, ff->mul(E, gcoeff(A, i, k), gel(B, k)));
     377     1762460 :     gel(C, i) = gerepileupto(av, ff->red(E, e));
     378             :   }
     379      303674 :   return C;
     380             : }
     381             : 
     382             : GEN
     383      161035 : gen_matcolmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     384             : {
     385      161035 :   ulong lgA = lg(A);
     386      161035 :   if (lgA != (ulong)lg(B))
     387           0 :     pari_err_OP("operation 'gen_matcolmul'", A, B);
     388      161035 :   if (lgA == 1)
     389           0 :     return cgetg(1, t_COL);
     390      161035 :   return gen_matcolmul_i(A, B, lgA, lgcols(A), E, ff);
     391             : }
     392             : 
     393             : GEN
     394       10178 : gen_matmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     395             : {
     396       10178 :   ulong j, l, lgA, lgB = lg(B);
     397             :   GEN C;
     398       10178 :   if (lgB == 1)
     399           0 :     return cgetg(1, t_MAT);
     400       10178 :   lgA = lg(A);
     401       10178 :   if (lgA != (ulong)lgcols(B))
     402           0 :     pari_err_OP("operation 'gen_matmul'", A, B);
     403       10178 :   if (lgA == 1)
     404           0 :     return zeromat(0, lgB - 1);
     405       10178 :   l = lgcols(A);
     406       10178 :   C = cgetg(lgB, t_MAT);
     407      152817 :   for(j = 1; j < lgB; j++)
     408      142639 :     gel(C, j) = gen_matcolmul_i(A, gel(B, j), lgA, l, E, ff);
     409       10178 :   return C;
     410             : }
     411             : 
     412             : static GEN
     413         217 : gen_colneg(GEN A, void *E, const struct bb_field *ff)
     414             : {
     415             :   long i, l;
     416         217 :   GEN B = cgetg_copy(A, &l);
     417        3794 :   for (i = 1; i < l; i++)
     418        3577 :     gel(B, i) = ff->neg(E, gel(A, i));
     419         217 :   return B;
     420             : }
     421             : 
     422             : static GEN
     423          28 : gen_matneg(GEN A, void *E, const struct bb_field *ff)
     424             : {
     425             :   long i, l;
     426          28 :   GEN B = cgetg_copy(A, &l);
     427         245 :   for (i = 1; i < l; i++)
     428         217 :     gel(B, i) = gen_colneg(gel(A, i), E, ff);
     429          28 :   return B;
     430             : }
     431             : 
     432             : /* assume dim A >= 1, A invertible + upper triangular  */
     433             : static GEN
     434          63 : gen_matinv_upper_ind(GEN A, long index, void *E, const struct bb_field *ff)
     435             : {
     436          63 :   long n = lg(A) - 1, i, j;
     437          63 :   GEN u = cgetg(n + 1, t_COL);
     438         126 :   for (i = n; i > index; i--)
     439          63 :     gel(u, i) = ff->s(E, 0);
     440          63 :   gel(u, i) = ff->inv(E, gcoeff(A, i, i));
     441         126 :   for (i--; i > 0; i--) {
     442          63 :     pari_sp av = avma;
     443          63 :     GEN m = ff->neg(E, ff->mul(E, gcoeff(A, i, i + 1), gel(u, i + 1)));
     444         105 :     for (j = i + 2; j <= n; j++)
     445          42 :       m = ff->add(E, m, ff->neg(E, ff->mul(E, gcoeff(A, i, j), gel(u, j))));
     446          63 :     gel(u, i) = gerepileupto(av, ff->red(E, ff->mul(E, m, ff->inv(E, gcoeff(A, i, i)))));
     447             :   }
     448          63 :   return u;
     449             : }
     450             : 
     451             : static GEN
     452          21 : gen_matinv_upper(GEN A, void *E, const struct bb_field *ff)
     453             : {
     454             :   long i, l;
     455          21 :   GEN B = cgetg_copy(A, &l);
     456          84 :   for (i = 1; i < l; i++)
     457          63 :     gel(B,i) = gen_matinv_upper_ind(A, i, E, ff);
     458          21 :   return B;
     459             : }
     460             : 
     461             : /* find z such that A z = y. Return NULL if no solution */
     462             : GEN
     463          63 : gen_matcolinvimage(GEN A, GEN y, void *E, const struct bb_field *ff)
     464             : {
     465          63 :   pari_sp av = avma;
     466          63 :   long i, l = lg(A);
     467             :   GEN M, x, t;
     468             : 
     469          63 :   M = gen_ker(shallowconcat(A, y), 0, E, ff);
     470          63 :   i = lg(M) - 1;
     471          63 :   if (!i) { avma = av; return NULL; }
     472             : 
     473          63 :   x = gel(M, i);
     474          63 :   t = gel(x, l);
     475          63 :   if (ff->equal0(t)) { avma = av; return NULL; }
     476             : 
     477          42 :   t = ff->neg(E, ff->inv(E, t));
     478          42 :   setlg(x, l);
     479         147 :   for (i = 1; i < l; i++)
     480         105 :     gel(x, i) = ff->red(E, ff->mul(E, t, gel(x, i)));
     481          42 :   return gerepilecopy(av, x);
     482             : }
     483             : 
     484             : /* find Z such that A Z = B. Return NULL if no solution */
     485             : GEN
     486          28 : gen_matinvimage(GEN A, GEN B, void *E, const struct bb_field *ff)
     487             : {
     488          28 :   pari_sp av = avma;
     489             :   GEN d, x, X, Y;
     490             :   long i, j, nY, nA, nB;
     491          28 :   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          28 :   nY = lg(x) - 1;
     496          28 :   nB = lg(B) - 1;
     497          28 :   if (nY < nB) { avma = av; return NULL; }
     498          28 :   nA = lg(A) - 1;
     499          28 :   Y = rowslice(x, nA + 1, nA + nB); /* nB rows */
     500          28 :   d = cgetg(nB + 1, t_VECSMALL);
     501         175 :   for (i = nB, j = nY; i >= 1; i--, j--) {
     502         224 :     for (; j >= 1; j--)
     503         217 :       if (!ff->equal0(gcoeff(Y, i, j))) { d[i] = j; break; }
     504         154 :     if (!j) { avma = av; return NULL; }
     505             :   }
     506             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
     507          21 :   Y = vecpermute(Y, d);
     508          21 :   x = vecpermute(x, d);
     509          21 :   X = rowslice(x, 1, nA);
     510          21 :   return gerepileupto(av, gen_matmul(X, gen_matinv_upper(Y, E, ff), E, ff));
     511             : }
     512             : 
     513             : static GEN
     514       79237 : image_from_pivot(GEN x, GEN d, long r)
     515             : {
     516             :   GEN y;
     517             :   long j, k;
     518             : 
     519       79237 :   if (!d) return gcopy(x);
     520             :   /* d left on stack for efficiency */
     521       77865 :   r = lg(x)-1 - r; /* = dim Im(x) */
     522       77865 :   y = cgetg(r+1,t_MAT);
     523      696739 :   for (j=k=1; j<=r; k++)
     524      618874 :     if (d[k]) gel(y,j++) = gcopy(gel(x,k));
     525       77865 :   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      489548 : indexcompl(GEN v, long n)
     553             : {
     554      489548 :   long i, j, k, m = lg(v) - 1;
     555      489548 :   GEN w = cgetg(n - m + 1, t_VECSMALL);
     556     8666747 :   for (i = j = k = 1; i <= n; i++)
     557     8177199 :     if (j <= m && v[j] == i) j++; else w[k++] = i;
     558      489548 :   return w;
     559             : }
     560             : 
     561             : static GEN
     562      263038 : Flm_rsolve_upper_1(GEN U, GEN B, ulong p)
     563      263038 : { return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p); }
     564             : 
     565             : static GEN
     566      759119 : Flm_rsolve_upper_2(GEN U, GEN B, ulong p)
     567             : {
     568      759119 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     569      759119 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     570      759173 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     571      759162 :   GEN B1 = rowslice(B, 1, 1);
     572      759096 :   GEN B2 = rowslice(B, 2, 2);
     573      759108 :   GEN X2 = Flm_Fl_mul(B2, dinv, p);
     574      759113 :   GEN X1 = Flm_Fl_mul(Flm_sub(B1, Flm_Fl_mul(X2, b, p), p),
     575             :                       ainv, p);
     576      759128 :   return vconcat(X1, X2);
     577             : }
     578             : 
     579             : /* solve U*X = B,  U upper triangular and invertible */
     580             : static GEN
     581     1873761 : Flm_rsolve_upper(GEN U, GEN B, ulong p)
     582             : {
     583     1873761 :   long n = lg(U) - 1, n1;
     584             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     585     1873761 :   pari_sp av = avma;
     586             : 
     587     1873761 :   if (n == 0) return B;
     588     1873761 :   if (n == 1) return Flm_rsolve_upper_1(U, B, p);
     589     1610724 :   if (n == 2) return Flm_rsolve_upper_2(U, B, p);
     590      851605 :   n1 = (n + 1)/2;
     591      851605 :   U2 = vecslice(U, n1 + 1, n);
     592      851565 :   U22 = rowslice(U2, n1 + 1, n);
     593      851528 :   B2 = rowslice(B, n1 + 1, n);
     594      851562 :   X2 = Flm_rsolve_upper(U22, B2, p);
     595      851604 :   U12 = rowslice(U2, 1, n1);
     596      851541 :   B1 = rowslice(B, 1, n1);
     597      851554 :   B1 = Flm_sub(B1, Flm_mul(U12, X2, p), p);
     598      851546 :   if (gc_needed(av, 1)) gerepileall(av, 2, &B1, &X2);
     599      851546 :   U11 = matslice(U, 1,n1, 1,n1);
     600      851520 :   X1 = Flm_rsolve_upper(U11, B1, p);
     601      851629 :   X = vconcat(X1, X2);
     602      851638 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     603      851644 :   return X;
     604             : }
     605             : 
     606             : static GEN
     607      295512 : Flm_lsolve_upper_1(GEN U, GEN B, ulong p)
     608      295512 : { return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p); }
     609             : 
     610             : static GEN
     611      878026 : Flm_lsolve_upper_2(GEN U, GEN B, ulong p)
     612             : {
     613      878026 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     614      878026 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     615      878095 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     616      878089 :   GEN B1 = vecslice(B, 1, 1);
     617      878018 :   GEN B2 = vecslice(B, 2, 2);
     618      878013 :   GEN X1 = Flm_Fl_mul(B1, ainv, p);
     619      878036 :   GEN X2 = Flm_Fl_mul(Flm_sub(B2, Flm_Fl_mul(X1, b, p), p),
     620             :                       dinv, p);
     621      878043 :   return shallowconcat(X1, X2);
     622             : }
     623             : 
     624             : /* solve X*U = B,  U upper triangular and invertible */
     625             : static GEN
     626     2014423 : Flm_lsolve_upper(GEN U, GEN B, ulong p)
     627             : {
     628     2014423 :   long n = lg(U) - 1, n1;
     629             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     630     2014423 :   pari_sp av = avma;
     631             : 
     632     2014423 :   if (n == 0) return B;
     633     2014423 :   if (n == 1) return Flm_lsolve_upper_1(U, B, p);
     634     1718911 :   if (n == 2) return Flm_lsolve_upper_2(U, B, p);
     635      840863 :   n1 = (n + 1)/2;
     636      840863 :   U2 = vecslice(U, n1 + 1, n);
     637      840825 :   U11 = matslice(U, 1,n1, 1,n1);
     638      840828 :   U12 = rowslice(U2, 1, n1);
     639      840831 :   U22 = rowslice(U2, n1 + 1, n);
     640      840822 :   B1 = vecslice(B, 1, n1);
     641      840840 :   B2 = vecslice(B, n1 + 1, n);
     642      840839 :   X1 = Flm_lsolve_upper(U11, B1, p);
     643      840809 :   B2 = Flm_sub(B2, Flm_mul(X1, U12, p), p);
     644      840824 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B2, &U22, &X1);
     645      840824 :   X2 = Flm_lsolve_upper(U22, B2, p);
     646      840857 :   X = shallowconcat(X1, X2);
     647      840881 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     648      840882 :   return X;
     649             : }
     650             : 
     651             : static GEN
     652     1471272 : Flm_rsolve_lower_unit_2(GEN L, GEN A, ulong p)
     653             : {
     654     1471272 :   GEN X1 = rowslice(A, 1, 1);
     655     1471254 :   GEN X2 = Flm_sub(rowslice(A, 2, 2),
     656     1471254 :                    Flm_Fl_mul(X1, ucoeff(L, 2, 1), p), p);
     657     1471241 :   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     3459466 : Flm_rsolve_lower_unit(GEN L, GEN A, ulong p)
     664             : {
     665     3459466 :   long m = lg(L) - 1, m1, n;
     666             :   GEN L1, L11, L21, L22, A1, A2, X1, X2, X;
     667     3459466 :   pari_sp av = avma;
     668             : 
     669     3459466 :   if (m == 0) return zero_Flm(0, lg(A) - 1);
     670     3459466 :   if (m == 1) return rowslice(A, 1, 1);
     671     2970356 :   if (m == 2) return Flm_rsolve_lower_unit_2(L, A, p);
     672     1499089 :   m1 = (m + 1)/2;
     673     1499089 :   n = nbrows(L);
     674     1499109 :   L1 = vecslice(L, 1, m1);
     675     1499016 :   L11 = rowslice(L1, 1, m1);
     676     1499034 :   L21 = rowslice(L1, m1 + 1, n);
     677     1499009 :   A1 = rowslice(A, 1, m1);
     678     1499046 :   X1 = Flm_rsolve_lower_unit(L11, A1, p);
     679     1499107 :   A2 = rowslice(A, m1 + 1, n);
     680     1499011 :   A2 = Flm_sub(A2, Flm_mul(L21, X1, p), p);
     681     1499067 :   if (gc_needed(av, 1)) gerepileall(av, 2, &A2, &X1);
     682     1499067 :   L22 = matslice(L, m1+1,n, m1+1,m);
     683     1498987 :   X2 = Flm_rsolve_lower_unit(L22, A2, p);
     684     1499067 :   X = vconcat(X1, X2);
     685     1499148 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     686     1499152 :   return X;
     687             : }
     688             : 
     689             : static GEN
     690      858417 : Flm_lsolve_lower_unit_2(GEN L, GEN A, ulong p)
     691             : {
     692      858417 :   GEN X2 = vecslice(A, 2, 2);
     693      858417 :   GEN X1 = Flm_sub(vecslice(A, 1, 1),
     694      858417 :                    Flm_Fl_mul(X2, ucoeff(L, 2, 1), p), p);
     695      858417 :   return shallowconcat(X1, X2);
     696             : }
     697             : 
     698             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
     699             : static GEN
     700     2200023 : Flm_lsolve_lower_unit(GEN L, GEN A, ulong p)
     701             : {
     702     2200023 :   long m = lg(L) - 1, m1;
     703             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
     704     2200023 :   pari_sp av = avma;
     705             : 
     706     2200023 :   if (m <= 1) return A;
     707     1862330 :   if (m == 2) return Flm_lsolve_lower_unit_2(L, A, p);
     708     1003913 :   m1 = (m + 1)/2;
     709     1003913 :   L2 = vecslice(L, m1 + 1, m);
     710     1003913 :   L22 = rowslice(L2, m1 + 1, m);
     711     1003913 :   A2 = vecslice(A, m1 + 1, m);
     712     1003913 :   X2 = Flm_lsolve_lower_unit(L22, A2, p);
     713     1003913 :   if (gc_needed(av, 1)) X2 = gerepilecopy(av, X2);
     714     1003913 :   L1 = vecslice(L, 1, m1);
     715     1003913 :   L21 = rowslice(L1, m1 + 1, m);
     716     1003913 :   A1 = vecslice(A, 1, m1);
     717     1003913 :   A1 = Flm_sub(A1, Flm_mul(X2, L21, p), p);
     718     1003913 :   L11 = rowslice(L1, 1, m1);
     719     1003913 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
     720     1003913 :   X1 = Flm_lsolve_lower_unit(L11, A1, p);
     721     1003913 :   X = shallowconcat(X1, X2);
     722     1003913 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     723     1003913 :   return X;
     724             : }
     725             : 
     726             : /* destroy A */
     727             : static long
     728     1122760 : Flm_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     729             : {
     730     1122760 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc, u, v;
     731             : 
     732     1122764 :   if (P) *P = identity_perm(n);
     733     1122748 :   *R = cgetg(m + 1, t_VECSMALL);
     734     5984990 :   for (j = 1, pr = 0; j <= n; j++)
     735             :   {
     736     7721060 :     for (pr++, pc = 0; pr <= m; pr++)
     737             :     {
     738     7182859 :       for (k = j; k <= n; k++) { v = ucoeff(A, pr, k); if (!pc && v) pc = k; }
     739     7182859 :       if (pc) break;
     740             :     }
     741     5400249 :     if (!pc) break;
     742     4862101 :     (*R)[j] = pr;
     743     4862101 :     if (pc != j)
     744             :     {
     745     1075729 :       swap(gel(A, j), gel(A, pc));
     746     1075729 :       if (P) lswap((*P)[j], (*P)[pc]);
     747             :     }
     748     4862101 :     u = Fl_inv(ucoeff(A, pr, j), p);
     749    31834694 :     for (i = pr + 1; i <= m; i++)
     750             :     {
     751    26972428 :       v = Fl_mul(ucoeff(A, i, j), u, p);
     752    26976034 :       ucoeff(A, i, j) = v;
     753   141024013 :       for (k = j + 1; k <= n; k++)
     754   228104406 :         ucoeff(A, i, k) = Fl_sub(ucoeff(A, i, k),
     755   114052203 :                                  Fl_mul(ucoeff(A, pr, k), v, p), p);
     756             :     }
     757             :   }
     758     1122889 :   setlg(*R, j);
     759     1122772 :   *C = vecslice(A, 1, j - 1);
     760     1122755 :   if (U) *U = rowpermute(A, *R);
     761     1122760 :   return j - 1;
     762             : }
     763             : 
     764             : static const long Flm_CUP_LIMIT = 8;
     765             : 
     766             : static ulong
     767      941843 : Flm_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     768             : {
     769      941843 :   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      941849 :   pari_sp av = avma;
     773             : 
     774      941849 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     775             :     /* destroy A; not called at the outermost recursion level */
     776      604620 :     return Flm_CUP_gauss(A, R, C, U, P, p);
     777      337229 :   m1 = (minss(m, n) + 1)/2;
     778      337226 :   A1 = rowslice(A, 1, m1);
     779      337211 :   A2 = rowslice(A, m1 + 1, m);
     780      337233 :   r1 = Flm_CUP(A1, &R1, &C1, &U1, &P1, p);
     781      337217 :   if (r1 == 0)
     782             :   {
     783        4480 :     r2 = Flm_CUP(A2, &R2, &C2, &U2, &P2, p);
     784        4480 :     *R = cgetg(r2 + 1, t_VECSMALL);
     785        4480 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
     786        4480 :     *C = vconcat(zero_Flm(m1, r2), C2);
     787        4480 :     *U = U2;
     788        4480 :     *P = P2;
     789        4480 :     r = r2;
     790             :   }
     791             :   else
     792             :   {
     793      332737 :     U11 = vecslice(U1, 1, r1);
     794      332745 :     U12 = vecslice(U1, r1 + 1, n);
     795      332747 :     T21 = vecslicepermute(A2, P1, 1, r1);
     796      332740 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
     797      332749 :     C21 = Flm_lsolve_upper(U11, T21, p);
     798      332733 :     if (gc_needed(av, 1))
     799           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
     800      332733 :     B2 = Flm_sub(T22, Flm_mul(C21, U12, p), p);
     801      332738 :     r2 = Flm_CUP(B2, &R2, &C2, &U2, &P2, p);
     802      332734 :     r = r1 + r2;
     803      332734 :     *R = cgetg(r + 1, t_VECSMALL);
     804      332753 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
     805      332753 :     for (;      i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
     806      332753 :     *C = shallowconcat(vconcat(C1, C21),
     807             :                        vconcat(zero_Flm(m1, r2), C2));
     808      332751 :     *U = shallowconcat(vconcat(U11, zero_Flm(r2, r1)),
     809             :                        vconcat(vecpermute(U12, P2), U2));
     810      332757 :     *P = cgetg(n + 1, t_VECSMALL);
     811      332749 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
     812      332749 :     for (; i <= n; i++)       (*P)[i] = P1[P2[i - r1] + r1];
     813             :   }
     814      337229 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
     815      337230 :   return r;
     816             : }
     817             : 
     818             : static ulong
     819      518120 : Flm_echelon_gauss(GEN A, GEN *R, GEN *C, ulong p)
     820      518120 : { return Flm_CUP_gauss(A, R, C, NULL, NULL, p); }
     821             : 
     822             : /* column echelon form */
     823             : static ulong
     824      845796 : Flm_echelon(GEN A, GEN *R, GEN *C, ulong p)
     825             : {
     826      845796 :   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      845796 :   pari_sp av = avma;
     830             : 
     831      845796 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     832      518120 :     return Flm_echelon_gauss(Flm_copy(A), R, C, p);
     833             : 
     834      327676 :   n1 = (n + 1)/2;
     835      327676 :   A1 = vecslice(A, 1, n1);
     836      327676 :   A2 = vecslice(A, n1 + 1, n);
     837      327676 :   r1 = Flm_echelon(A1, &R1, &C1, p);
     838      327676 :   if (!r1) return Flm_echelon(A2, R, C, p);
     839      292538 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
     840             : 
     841      290750 :   R1c = indexcompl(R1, m);
     842      290750 :   C11 = rowpermute(C1, R1);
     843      290750 :   C21 = rowpermute(C1, R1c);
     844      290750 :   A12 = rowpermute(A2, R1);
     845      290750 :   A22 = rowpermute(A2, R1c);
     846      290750 :   M12 = Flm_rsolve_lower_unit(C11, A12, p);
     847      290750 :   B2 = Flm_sub(A22, Flm_mul(C21, M12, p), p);
     848      290750 :   r2 = Flm_echelon(B2, &R2, &C2, p);
     849      290750 :   if (!r2) { *R = R1; *C = C1; r = r1; }
     850             :   else
     851             :   {
     852      266008 :     R2 = perm_mul(R1c, R2);
     853      266008 :     C2 = rowpermute(vconcat(zero_Flm(r1, r2), C2),
     854             :                     perm_inv(vecsmall_concat(R1, R1c)));
     855      266008 :     r = r1 + r2;
     856      266008 :     *R = cgetg(r + 1, t_VECSMALL);
     857      266008 :     *C = cgetg(r + 1, t_MAT);
     858     3119090 :     for (j = j1 = j2 = 1; j <= r; j++)
     859     2853082 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
     860             :       {
     861     1513412 :         gel(*C, j) = gel(C1, j1);
     862     1513412 :         (*R)[j] = R1[j1++];
     863             :       }
     864             :       else
     865             :       {
     866     1339670 :         gel(*C, j) = gel(C2, j2);
     867     1339670 :         (*R)[j] = R2[j2++];
     868             :       }
     869             :   }
     870      290750 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
     871      290750 :   return r;
     872             : }
     873             : 
     874             : static GEN
     875         112 : FlxqM_rsolve_upper_1(GEN U, GEN B, GEN T, ulong p)
     876         112 : { return FlxqM_Flxq_mul(B, Flxq_inv(gcoeff(U, 1, 1), T, p), T, p);
     877             : }
     878             : 
     879             : static GEN
     880         140 : FlxqM_rsolve_upper_2(GEN U, GEN B, GEN T, ulong p)
     881             : {
     882         140 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     883         140 :   GEN D = Flxq_mul(a, d, T, p), Dinv = Flxq_inv(D, T, p);
     884         140 :   GEN ainv = Flxq_mul(d, Dinv, T, p), dinv = Flxq_mul(a, Dinv, T, p);
     885         140 :   GEN B1 = rowslice(B, 1, 1);
     886         140 :   GEN B2 = rowslice(B, 2, 2);
     887         140 :   GEN X2 = FlxqM_Flxq_mul(B2, dinv, T, p);
     888         140 :   GEN X1 = FlxqM_Flxq_mul(FlxM_sub(B1, FlxqM_Flxq_mul(X2, b, T, p), p),
     889             :                           ainv, T, p);
     890         140 :   return vconcat(X1, X2);
     891             : }
     892             : 
     893             : /* solve U*X = B,  U upper triangular and invertible */
     894             : static GEN
     895         483 : FlxqM_rsolve_upper(GEN U, GEN B, GEN T, ulong p)
     896             : {
     897         483 :   long n = lg(U) - 1, n1;
     898             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     899         483 :   pari_sp av = avma;
     900             : 
     901         483 :   if (n == 0) return B;
     902         483 :   if (n == 1) return FlxqM_rsolve_upper_1(U, B, T, p);
     903         371 :   if (n == 2) return FlxqM_rsolve_upper_2(U, B, T, p);
     904         231 :   n1 = (n + 1)/2;
     905         231 :   U2 = vecslice(U, n1 + 1, n);
     906         231 :   U11 = matslice(U, 1,n1, 1,n1);
     907         231 :   U12 = rowslice(U2, 1, n1);
     908         231 :   U22 = rowslice(U2, n1 + 1, n);
     909         231 :   B1 = rowslice(B, 1, n1);
     910         231 :   B2 = rowslice(B, n1 + 1, n);
     911         231 :   X2 = FlxqM_rsolve_upper(U22, B2, T, p);
     912         231 :   B1 = FlxM_sub(B1, FlxqM_mul(U12, X2, T, p), p);
     913         231 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B1, &U11, &X2);
     914         231 :   X1 = FlxqM_rsolve_upper(U11, B1, T, p);
     915         231 :   X = vconcat(X1, X2);
     916         231 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     917         231 :   return X;
     918             : }
     919             : 
     920             : static GEN
     921         364 : FlxqM_lsolve_upper_1(GEN U, GEN B, GEN T, ulong p)
     922         364 : { return FlxqM_Flxq_mul(B, Flxq_inv(gcoeff(U, 1, 1), T, p), T, p); }
     923             : 
     924             : static GEN
     925         588 : FlxqM_lsolve_upper_2(GEN U, GEN B, GEN T, ulong p)
     926             : {
     927         588 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     928         588 :   GEN D = Flxq_mul(a, d, T, p), Dinv = Flxq_inv(D, T, p);
     929         588 :   GEN ainv = Flxq_mul(d, Dinv, T, p), dinv = Flxq_mul(a, Dinv, T, p);
     930         588 :   GEN B1 = vecslice(B, 1, 1);
     931         588 :   GEN B2 = vecslice(B, 2, 2);
     932         588 :   GEN X1 = FlxqM_Flxq_mul(B1, ainv, T, p);
     933         588 :   GEN X2 = FlxqM_Flxq_mul(FlxM_sub(B2, FlxqM_Flxq_mul(X1, b, T, p), p),
     934             :                           dinv, T, p);
     935         588 :   return shallowconcat(X1, X2);
     936             : }
     937             : 
     938             : /* solve X*U = B,  U upper triangular and invertible */
     939             : static GEN
     940        1484 : FlxqM_lsolve_upper(GEN U, GEN B, GEN T, ulong p)
     941             : {
     942        1484 :   long n = lg(U) - 1, n1;
     943             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     944        1484 :   pari_sp av = avma;
     945             : 
     946        1484 :   if (n == 0) return B;
     947        1484 :   if (n == 1) return FlxqM_lsolve_upper_1(U, B, T, p);
     948        1120 :   if (n == 2) return FlxqM_lsolve_upper_2(U, B, T, p);
     949         532 :   n1 = (n + 1)/2;
     950         532 :   U2 = vecslice(U, n1 + 1, n);
     951         532 :   U11 = matslice(U, 1,n1, 1,n1);
     952         532 :   U12 = rowslice(U2, 1, n1);
     953         532 :   U22 = rowslice(U2, n1 + 1, n);
     954         532 :   B1 = vecslice(B, 1, n1);
     955         532 :   B2 = vecslice(B, n1 + 1, n);
     956         532 :   X1 = FlxqM_lsolve_upper(U11, B1, T, p);
     957         532 :   B2 = FlxM_sub(B2, FlxqM_mul(X1, U12, T, p), p);
     958         532 :   if (gc_needed(av, 1)) gerepileall(av, 3, &B2, &U22, &X1);
     959         532 :   X2 = FlxqM_lsolve_upper(U22, B2, T, p);
     960         532 :   X = shallowconcat(X1, X2);
     961         532 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     962         532 :   return X;
     963             : }
     964             : 
     965             : static GEN
     966        5999 : FlxqM_rsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
     967             : {
     968        5999 :   GEN X1 = rowslice(A, 1, 1);
     969        5999 :   GEN X2 = FlxM_sub(rowslice(A, 2, 2),
     970        5999 :                     FlxqM_Flxq_mul(X1, gcoeff(L, 2, 1), T, p), p);
     971        5999 :   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       13601 : FlxqM_rsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
     978             : {
     979       13601 :   long m = lg(L) - 1, m1, n;
     980             :   GEN L1, L11, L21, L22, A1, A2, X1, X2, X;
     981       13601 :   pari_sp av = avma;
     982             : 
     983       13601 :   if (m == 0) return zeromat(0, lg(A) - 1);
     984       13601 :   if (m == 1) return rowslice(A, 1, 1);
     985       10402 :   if (m == 2) return FlxqM_rsolve_lower_unit_2(L, A, T, p);
     986        4403 :   m1 = (m + 1)/2;
     987        4403 :   n = nbrows(L);
     988        4403 :   L1 = vecslice(L, 1, m1);
     989        4403 :   L11 = rowslice(L1, 1, m1);
     990        4403 :   L21 = rowslice(L1, m1 + 1, n);
     991        4403 :   A1 = rowslice(A, 1, m1);
     992        4403 :   A2 = rowslice(A, m1 + 1, n);
     993        4403 :   X1 = FlxqM_rsolve_lower_unit(L11, A1, T, p);
     994        4403 :   A2 = FlxM_sub(A2, FlxqM_mul(L21, X1, T, p), p);
     995        4403 :   if (gc_needed(av, 1)) gerepileall(av, 2, &A2, &X1);
     996        4403 :   L22 = matslice(L, m1+1,n, m1+1,m);
     997        4403 :   X2 = FlxqM_rsolve_lower_unit(L22, A2, T, p);
     998        4403 :   X = vconcat(X1, X2);
     999        4403 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1000        4403 :   return X;
    1001             : }
    1002             : 
    1003             : static GEN
    1004        2450 : FlxqM_lsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
    1005             : {
    1006        2450 :   GEN X2 = vecslice(A, 2, 2);
    1007        2450 :   GEN X1 = FlxM_sub(vecslice(A, 1, 1),
    1008        2450 :                     FlxqM_Flxq_mul(X2, gcoeff(L, 2, 1), T, p), p);
    1009        2450 :   return shallowconcat(X1, X2);
    1010             : }
    1011             : 
    1012             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
    1013             : static GEN
    1014        6265 : FlxqM_lsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
    1015             : {
    1016        6265 :   long m = lg(L) - 1, m1;
    1017             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
    1018        6265 :   pari_sp av = avma;
    1019             : 
    1020        6265 :   if (m <= 1) return A;
    1021        4676 :   if (m == 2) return FlxqM_lsolve_lower_unit_2(L, A, T, p);
    1022        2226 :   m1 = (m + 1)/2;
    1023        2226 :   L1 = vecslice(L, 1, m1);
    1024        2226 :   L2 = vecslice(L, m1 + 1, m);
    1025        2226 :   L11 = rowslice(L1, 1, m1);
    1026        2226 :   L21 = rowslice(L1, m1 + 1, m);
    1027        2226 :   L22 = rowslice(L2, m1 + 1, m);
    1028        2226 :   A1 = vecslice(A, 1, m1);
    1029        2226 :   A2 = vecslice(A, m1 + 1, m);
    1030        2226 :   X2 = FlxqM_lsolve_lower_unit(L22, A2, T, p);
    1031        2226 :   A1 = FlxM_sub(A1, FlxqM_mul(X2, L21, T, p), p);
    1032        2226 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
    1033        2226 :   X1 = FlxqM_lsolve_lower_unit(L11, A1, T, p);
    1034        2226 :   X = shallowconcat(X1, X2);
    1035        2226 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1036        2226 :   return X;
    1037             : }
    1038             : 
    1039             : /* destroy A */
    1040             : static long
    1041        7630 : FlxqM_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, GEN T, ulong p)
    1042             : {
    1043        7630 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc;
    1044             :   pari_sp av;
    1045             :   GEN u, v;
    1046             : 
    1047        7630 :   if (P) *P = identity_perm(n);
    1048        7630 :   *R = cgetg(m + 1, t_VECSMALL);
    1049        7630 :   av = avma;
    1050       15561 :   for (j = 1, pr = 0; j <= n; j++)
    1051             :   {
    1052       41027 :     for (pr++, pc = 0; pr <= m; pr++)
    1053             :     {
    1054      161532 :       for (k = j; k <= n; k++)
    1055             :       {
    1056      126945 :         v = Flx_rem(gcoeff(A, pr, k), T, p);
    1057      126945 :         gcoeff(A, pr, k) = v;
    1058      126945 :         if (!pc && lgpol(v) > 0) pc = k;
    1059             :       }
    1060       34587 :       if (pc) break;
    1061             :     }
    1062       14371 :     if (!pc) break;
    1063        7931 :     (*R)[j] = pr;
    1064        7931 :     if (pc != j)
    1065             :     {
    1066         987 :       swap(gel(A, j), gel(A, pc));
    1067         987 :       if (P) lswap((*P)[j], (*P)[pc]);
    1068             :     }
    1069        7931 :     u = Flxq_inv(gcoeff(A, pr, j), T, p);
    1070       49924 :     for (i = pr + 1; i <= m; i++)
    1071             :     {
    1072       41993 :       v = Flxq_mul(gcoeff(A, i, j), u, T, p);
    1073       41993 :       gcoeff(A, i, j) = v;
    1074      111944 :       for (k = j + 1; k <= n; k++)
    1075      139902 :         gcoeff(A, i, k) = Flx_sub(gcoeff(A, i, k),
    1076       69951 :                                   Flx_mul(gcoeff(A, pr, k), v, p), p);
    1077             :     }
    1078        7931 :     if (gc_needed(av, 2)) A = gerepilecopy(av, A);
    1079             :   }
    1080        7630 :   setlg(*R, j);
    1081        7630 :   *C = vecslice(A, 1, j - 1);
    1082        7630 :   if (U) *U = rowpermute(A, *R);
    1083        7630 :   return j - 1;
    1084             : }
    1085             : 
    1086             : static const long FlxqM_CUP_LIMIT = 5;
    1087             : 
    1088             : static ulong
    1089         994 : FlxqM_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, GEN T, ulong p)
    1090             : {
    1091         994 :   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         994 :   pari_sp av = avma;
    1095             : 
    1096         994 :   if (m < FlxqM_CUP_LIMIT || n < FlxqM_CUP_LIMIT)
    1097             :     /* destroy A; not called at the outermost recursion level */
    1098         532 :     return FlxqM_CUP_gauss(A, R, C, U, P, T, p);
    1099         462 :   sv = get_Flx_var(T);
    1100         462 :   m1 = (minss(m, n) + 1)/2;
    1101         462 :   A1 = rowslice(A, 1, m1);
    1102         462 :   A2 = rowslice(A, m1 + 1, m);
    1103         462 :   r1 = FlxqM_CUP(A1, &R1, &C1, &U1, &P1, T, p);
    1104         462 :   if (r1 == 0)
    1105             :   {
    1106          42 :     r2 = FlxqM_CUP(A2, &R2, &C2, &U2, &P2, T, p);
    1107          42 :     *R = cgetg(r2 + 1, t_VECSMALL);
    1108          42 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
    1109          42 :     *C = vconcat(zero_FlxM(m1, r2, sv), C2);
    1110          42 :     *U = U2;
    1111          42 :     *P = P2;
    1112          42 :     r = r2;
    1113             :   }
    1114             :   else
    1115             :   {
    1116         420 :     U11 = vecslice(U1, 1, r1);
    1117         420 :     U12 = vecslice(U1, r1 + 1, n);
    1118         420 :     T21 = vecslicepermute(A2, P1, 1, r1);
    1119         420 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
    1120         420 :     C21 = FlxqM_lsolve_upper(U11, T21, T, p);
    1121         420 :     if (gc_needed(av, 1))
    1122           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
    1123         420 :     B2 = FlxM_sub(T22, FlxqM_mul(C21, U12, T, p), p);
    1124         420 :     r2 = FlxqM_CUP(B2, &R2, &C2, &U2, &P2, T, p);
    1125         420 :     r = r1 + r2;
    1126         420 :     *R = cgetg(r + 1, t_VECSMALL);
    1127         420 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
    1128         420 :     for (     ; i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
    1129         420 :     *C = shallowmatconcat(mkmat2(mkcol2(C1, C21),
    1130             :                                  mkcol2(zero_FlxM(m1, r2, sv), C2)));
    1131         420 :     *U = shallowmatconcat(mkmat2(mkcol2(U11, zero_FlxM(r2, r1, sv)),
    1132             :                                  mkcol2(vecpermute(U12, P2), U2)));
    1133         420 :     *P = cgetg(n + 1, t_VECSMALL);
    1134         420 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
    1135         420 :     for (     ; i <= n; i++)  (*P)[i] = P1[P2[i - r1] + r1];
    1136             :   }
    1137         462 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
    1138         462 :   return r;
    1139             : }
    1140             : 
    1141             : static ulong
    1142        7098 : FlxqM_echelon_gauss(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1143        7098 : { return FlxqM_CUP_gauss(A, R, C, NULL, NULL, T, p); }
    1144             : 
    1145             : /* column echelon form */
    1146             : static ulong
    1147       12453 : FlxqM_echelon(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1148             : {
    1149       12453 :   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       12453 :   pari_sp av = avma;
    1153             : 
    1154       12453 :   if (m < FlxqM_CUP_LIMIT || n < FlxqM_CUP_LIMIT)
    1155        7098 :     return FlxqM_echelon_gauss(shallowcopy(A), R, C, T, p);
    1156             : 
    1157        5355 :   n1 = (n + 1)/2;
    1158        5355 :   A1 = vecslice(A, 1, n1);
    1159        5355 :   A2 = vecslice(A, n1 + 1, n);
    1160        5355 :   r1 = FlxqM_echelon(A1, &R1, &C1, T, p);
    1161        5355 :   if (!r1) return FlxqM_echelon(A2, R, C, T, p);
    1162        4865 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
    1163        4767 :   R1c = indexcompl(R1, m);
    1164        4767 :   C11 = rowpermute(C1, R1);
    1165        4767 :   C21 = rowpermute(C1, R1c);
    1166        4767 :   A12 = rowpermute(A2, R1);
    1167        4767 :   A22 = rowpermute(A2, R1c);
    1168        4767 :   M12 = FlxqM_rsolve_lower_unit(C11, A12, T, p);
    1169        4767 :   B2 = FlxM_sub(A22, FlxqM_mul(C21, M12, T, p), p);
    1170        4767 :   r2 = FlxqM_echelon(B2, &R2, &C2, T, p);
    1171        4767 :   if (!r2) { *R = R1; *C = C1; r = r1; }
    1172             :   else
    1173             :   {
    1174        1876 :     R2 = perm_mul(R1c, R2);
    1175        1876 :     C2 = rowpermute(vconcat(zero_FlxM(r1, r2, get_Flx_var(T)), C2),
    1176             :                     perm_inv(vecsmall_concat(R1, R1c)));
    1177        1876 :     r = r1 + r2;
    1178        1876 :     *R = cgetg(r + 1, t_VECSMALL);
    1179        1876 :     *C = cgetg(r + 1, t_MAT);
    1180       11921 :     for (j = j1 = j2 = 1; j <= r; j++)
    1181             :     {
    1182       10045 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
    1183             :       {
    1184        6167 :         gel(*C, j) = gel(C1, j1);
    1185        6167 :         (*R)[j] = R1[j1++];
    1186             :       }
    1187             :       else
    1188             :       {
    1189        3878 :         gel(*C, j) = gel(C2, j2);
    1190        3878 :         (*R)[j] = R2[j2++];
    1191             :       }
    1192             :     }
    1193             :   }
    1194        4767 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
    1195        4767 :   return r;
    1196             : }
    1197             : 
    1198             : /*******************************************************************/
    1199             : /*                                                                 */
    1200             : /*                    LINEAR ALGEBRA MODULO P                      */
    1201             : /*                                                                 */
    1202             : /*******************************************************************/
    1203             : 
    1204             : static long
    1205     2208864 : F2v_find_nonzero(GEN x0, GEN mask0, long l, long m)
    1206             : {
    1207     2208864 :   ulong *x = (ulong *)x0+2, *mask = (ulong *)mask0+2, e;
    1208             :   long i, j;
    1209     4762107 :   for (i = 0; i < l; i++)
    1210             :   {
    1211     4760391 :     e = *x++ & *mask++;
    1212     4760391 :     if (e)
    1213     2207148 :       for (j = 1; ; j++, e >>= 1) if (e & 1uL) return i*BITS_IN_LONG+j;
    1214     9955231 :   }
    1215        1716 :   return m+1;
    1216             : }
    1217             : 
    1218             : /* in place, destroy x */
    1219             : GEN
    1220      304217 : F2m_ker_sp(GEN x, long deplin)
    1221             : {
    1222             :   GEN y, c, d;
    1223             :   long i, j, k, l, r, m, n;
    1224             : 
    1225      304217 :   n = lg(x)-1;
    1226      304217 :   m = mael(x,1,1); r=0;
    1227             : 
    1228      304217 :   d = cgetg(n+1, t_VECSMALL);
    1229      304217 :   c = const_F2v(m); l = lg(c)-1;
    1230     2262439 :   for (k=1; k<=n; k++)
    1231             :   {
    1232     1982491 :     GEN xk = gel(x,k);
    1233     1982491 :     j = F2v_find_nonzero(xk, c, l, m);
    1234     1982491 :     if (j>m)
    1235             :     {
    1236      442648 :       if (deplin) {
    1237       24269 :         GEN c = zero_F2v(n);
    1238      104914 :         for (i=1; i<k; i++)
    1239       80645 :           if (F2v_coeff(xk, d[i]))
    1240       40474 :             F2v_set(c, i);
    1241       24269 :         F2v_set(c, k);
    1242       24269 :         return c;
    1243             :       }
    1244      418379 :       r++; d[k] = 0;
    1245             :     }
    1246             :     else
    1247             :     {
    1248     1539843 :       F2v_clear(c,j); d[k] = j;
    1249     1539843 :       F2v_clear(xk, j);
    1250   134519317 :       for (i=k+1; i<=n; i++)
    1251             :       {
    1252   132979474 :         GEN xi = gel(x,i);
    1253   132979474 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1254             :       }
    1255     1539843 :       F2v_set(xk, j);
    1256             :     }
    1257             :   }
    1258      279948 :   if (deplin) return NULL;
    1259             : 
    1260      279913 :   y = zero_F2m_copy(n,r);
    1261      698292 :   for (j=k=1; j<=r; j++,k++)
    1262             :   {
    1263      418379 :     GEN C = gel(y,j); while (d[k]) k++;
    1264    19810678 :     for (i=1; i<k; i++)
    1265    19392299 :       if (d[i] && F2m_coeff(x,d[i],k))
    1266     7354526 :         F2v_set(C,i);
    1267      418379 :     F2v_set(C, k);
    1268             :   }
    1269      279913 :   return y;
    1270             : }
    1271             : 
    1272             : static void /* assume m < p */
    1273     6598706 : _Fl_addmul(GEN b, long k, long i, ulong m, ulong p, ulong pi)
    1274             : {
    1275     6598706 :   uel(b,k) = Fl_addmul_pre(uel(b, k), m, uel(b, i), p, pi);
    1276     6598706 : }
    1277             : static void /* same m = 1 */
    1278      179053 : _Fl_add(GEN b, long k, long i, ulong p)
    1279             : {
    1280      179053 :   uel(b,k) = Fl_add(uel(b,k), uel(b,i), p);
    1281      179053 : }
    1282             : static void /* assume m < p && SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1283    12916850 : _Fl_addmul_OK(GEN b, long k, long i, ulong m, ulong p)
    1284             : {
    1285    12916850 :   uel(b,k) += m * uel(b,i);
    1286    12916850 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1287    12916850 : }
    1288             : static void /* assume SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1289     2690121 : _Fl_add_OK(GEN b, long k, long i, ulong p)
    1290             : {
    1291     2690121 :   uel(b,k) += uel(b,i);
    1292     2690121 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1293     2690121 : }
    1294             : 
    1295             : static GEN
    1296      341034 : Flm_ker_gauss_OK(GEN x, ulong p, long deplin)
    1297             : {
    1298             :   GEN y, c, d;
    1299             :   long i, j, k, r, t, m, n;
    1300             :   ulong a;
    1301             : 
    1302      341034 :   n = lg(x)-1;
    1303      341034 :   m=nbrows(x); r=0;
    1304             : 
    1305      341034 :   c = zero_zv(m);
    1306      341034 :   d = cgetg(n+1, t_VECSMALL);
    1307      341034 :   a = 0; /* for gcc -Wall */
    1308     1508298 :   for (k=1; k<=n; k++)
    1309             :   {
    1310     4241128 :     for (j=1; j<=m; j++)
    1311     3598253 :       if (!c[j])
    1312             :       {
    1313     2709376 :         a = ucoeff(x,j,k) % p;
    1314     2709376 :         if (a) break;
    1315             :       }
    1316     1185971 :     if (j > m)
    1317             :     {
    1318      642875 :       if (deplin==1) {
    1319       18707 :         c = cgetg(n+1, t_VECSMALL);
    1320       18707 :         for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k) % p;
    1321       18707 :         c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
    1322       18707 :         return c;
    1323             :       }
    1324      624168 :       r++; d[k] = 0;
    1325             :     }
    1326             :     else
    1327             :     {
    1328      543096 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1329      543096 :       c[j] = k; d[k] = j;
    1330      543096 :       ucoeff(x,j,k) = p-1;
    1331      543096 :       if (piv != 1)
    1332      415747 :         for (i=k+1; i<=n; i++) ucoeff(x,j,i) = (piv * ucoeff(x,j,i)) % p;
    1333     2928216 :       for (t=1; t<=m; t++)
    1334             :       {
    1335     2385120 :         if (t == j) continue;
    1336             : 
    1337     1842024 :         piv = ( ucoeff(x,t,k) %= p );
    1338     1842024 :         if (!piv) continue;
    1339     1094973 :         if (piv == 1)
    1340      181176 :           for (i=k+1; i<=n; i++) _Fl_add_OK(gel(x,i),t,j, p);
    1341             :         else
    1342      913797 :           for (i=k+1; i<=n; i++) _Fl_addmul_OK(gel(x,i),t,j,piv, p);
    1343             :       }
    1344             :     }
    1345             :   }
    1346      322327 :   if (deplin==1) return NULL;
    1347             : 
    1348      322320 :   y = cgetg(r+1, t_MAT);
    1349      946488 :   for (j=k=1; j<=r; j++,k++)
    1350             :   {
    1351      624168 :     GEN C = cgetg(n+1, t_VECSMALL);
    1352             : 
    1353      624168 :     gel(y,j) = C; while (d[k]) k++;
    1354     1525783 :     for (i=1; i<k; i++)
    1355      901615 :       if (d[i])
    1356      417573 :         uel(C,i) = ucoeff(x,d[i],k) % p;
    1357             :       else
    1358      484042 :         uel(C,i) = 0UL;
    1359      624168 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1360             :   }
    1361      322320 :   if (deplin == 2) {
    1362           0 :     GEN pc = cgetg(n - r + 1, t_VECSMALL);  /* indices of pivot columns */
    1363           0 :     for (i = j = 1; j <= n; j++)
    1364           0 :       if (d[j]) pc[i++] = j;
    1365           0 :     return mkvec2(y, pc);
    1366             :   }
    1367      322320 :   return y;
    1368             : }
    1369             : 
    1370             : /* in place, destroy x */
    1371             : static GEN
    1372      460526 : Flm_ker_gauss(GEN x, ulong p, long deplin)
    1373             : {
    1374             :   GEN y, c, d;
    1375             :   long i, j, k, r, t, m, n;
    1376             :   ulong a, pi;
    1377      460526 :   n = lg(x)-1;
    1378      460526 :   if (!n) return cgetg(1,t_MAT);
    1379      460526 :   if (SMALL_ULONG(p)) return Flm_ker_gauss_OK(x, p, deplin);
    1380      119492 :   pi = get_Fl_red(p);
    1381             : 
    1382      119492 :   m=nbrows(x); r=0;
    1383             : 
    1384      119492 :   c = zero_zv(m);
    1385      119492 :   d = cgetg(n+1, t_VECSMALL);
    1386      119492 :   a = 0; /* for gcc -Wall */
    1387      389965 :   for (k=1; k<=n; k++)
    1388             :   {
    1389      756029 :     for (j=1; j<=m; j++)
    1390      652542 :       if (!c[j])
    1391             :       {
    1392      504512 :         a = ucoeff(x,j,k);
    1393      504512 :         if (a) break;
    1394             :       }
    1395      270480 :     if (j > m)
    1396             :     {
    1397      103487 :       if (deplin==1) {
    1398           7 :         c = cgetg(n+1, t_VECSMALL);
    1399           7 :         for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k);
    1400           7 :         c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
    1401           7 :         return c;
    1402             :       }
    1403      103480 :       r++; d[k] = 0;
    1404             :     }
    1405             :     else
    1406             :     {
    1407      166993 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1408      166993 :       c[j] = k; d[k] = j;
    1409      166993 :       ucoeff(x,j,k) = p-1;
    1410      166993 :       if (piv != 1)
    1411      320428 :         for (i=k+1; i<=n; i++)
    1412      155718 :           ucoeff(x,j,i) = Fl_mul_pre(piv, ucoeff(x,j,i), p, pi);
    1413      688499 :       for (t=1; t<=m; t++)
    1414             :       {
    1415      521506 :         if (t == j) continue;
    1416             : 
    1417      354513 :         piv = ucoeff(x,t,k);
    1418      354513 :         if (!piv) continue;
    1419      194647 :         if (piv == 1)
    1420        8578 :           for (i=k+1; i<=n; i++) _Fl_add(gel(x,i),t,j,p);
    1421             :         else
    1422      186069 :           for (i=k+1; i<=n; i++) _Fl_addmul(gel(x,i),t,j,piv,p, pi);
    1423             :       }
    1424             :     }
    1425             :   }
    1426      119485 :   if (deplin==1) return NULL;
    1427             : 
    1428      119478 :   y = cgetg(r+1, t_MAT);
    1429      222958 :   for (j=k=1; j<=r; j++,k++)
    1430             :   {
    1431      103480 :     GEN C = cgetg(n+1, t_VECSMALL);
    1432             : 
    1433      103480 :     gel(y,j) = C; while (d[k]) k++;
    1434      219381 :     for (i=1; i<k; i++)
    1435      115901 :       if (d[i])
    1436       76849 :         uel(C,i) = ucoeff(x,d[i],k);
    1437             :       else
    1438       39052 :         uel(C,i) = 0UL;
    1439      103480 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1440             :   }
    1441      119478 :   if (deplin == 2) {
    1442      115601 :     GEN pc = cgetg(n - r + 1, t_VECSMALL);  /* indices of pivot columns */
    1443      361661 :     for (i = j = 1; j <= n; j++)
    1444      246060 :       if (d[j]) pc[i++] = j;
    1445      115601 :     return mkvec2(y, pc);
    1446             :   }
    1447        3877 :   return y;
    1448             : }
    1449             : 
    1450             : GEN
    1451       72879 : FpM_intersect(GEN x, GEN y, GEN p)
    1452             : {
    1453       72879 :   pari_sp av = avma;
    1454       72879 :   long j, lx = lg(x);
    1455             :   GEN z;
    1456             : 
    1457       72879 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    1458       72879 :   z = FpM_ker(shallowconcat(x,y), p);
    1459       72879 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    1460       72879 :   return gerepileupto(av, FpM_mul(x,z,p));
    1461             : }
    1462             : GEN
    1463           0 : Flm_intersect(GEN x, GEN y, ulong p)
    1464             : {
    1465           0 :   pari_sp av = avma;
    1466           0 :   long j, lx = lg(x);
    1467             :   GEN z;
    1468             : 
    1469           0 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    1470           0 :   z = Flm_ker(shallowconcat(x,y), p);
    1471           0 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    1472           0 :   return gerepileupto(av, Flm_mul(x,z,p));
    1473             : }
    1474             : 
    1475             : /* not memory clean */
    1476             : GEN
    1477         173 : F2m_ker(GEN x) { return F2m_ker_sp(F2m_copy(x), 0); }
    1478             : GEN
    1479           0 : F2m_deplin(GEN x) { return F2m_ker_sp(F2m_copy(x), 1); }
    1480             : 
    1481             : static GEN
    1482      168480 : Flm_ker_echelon(GEN x, ulong p, long pivots) {
    1483      168480 :   pari_sp av = avma;
    1484             :   GEN R, Rc, C, C1, C2, S, K;
    1485      168480 :   long n = lg(x) - 1, r;
    1486      168480 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1487      168480 :   Rc = indexcompl(R, n);
    1488      168480 :   C1 = rowpermute(C, R);
    1489      168480 :   C2 = rowpermute(C, Rc);
    1490      168480 :   S = Flm_lsolve_lower_unit(C1, C2, p);
    1491      168480 :   K = vecpermute(shallowconcat(Flm_neg(S, p), matid_Flm(n - r)),
    1492             :                  perm_inv(vecsmall_concat(R, Rc)));
    1493      168480 :   K = Flm_transpose(K);
    1494      168480 :   if (pivots)
    1495        3187 :     return gerepilecopy(av, mkvec2(K, R));
    1496      165293 :   return gerepileupto(av, K);
    1497             : }
    1498             : 
    1499             : static GEN
    1500       23717 : Flm_deplin_echelon(GEN x, ulong p) {
    1501       23717 :   pari_sp av = avma;
    1502             :   GEN R, Rc, C, C1, C2, s, v;
    1503       23717 :   long i, n = lg(x) - 1, r;
    1504       23717 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1505       23717 :   if (r == n) { avma = av; return NULL; }
    1506       23710 :   Rc = indexcompl(R, n);
    1507       23710 :   i = Rc[1];
    1508       23710 :   C1 = rowpermute(C, R);
    1509       23710 :   C2 = rowslice(C, i, i);
    1510       23710 :   s = Flm_row(Flm_lsolve_lower_unit(C1, C2, p), 1);
    1511       23710 :   v = vecpermute(vecsmall_concat(Flv_neg(s, p), vecsmall_ei(n - r, 1)),
    1512             :                  perm_inv(vecsmall_concat(R, Rc)));
    1513       23710 :   return gerepileuptoleaf(av, v);
    1514             : }
    1515             : 
    1516             : static GEN
    1517      652723 : Flm_ker_i(GEN x, ulong p, long deplin, long inplace) {
    1518      652723 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1519      192197 :     switch(deplin) {
    1520      165293 :     case 0: return Flm_ker_echelon(x, p, 0);
    1521       23717 :     case 1: return Flm_deplin_echelon(x, p);
    1522        3187 :     case 2: return Flm_ker_echelon(x, p, 1);
    1523             :     }
    1524      460526 :   return Flm_ker_gauss(inplace? x: Flm_copy(x), p, deplin);
    1525             : }
    1526             : 
    1527             : GEN
    1528      594549 : Flm_ker_sp(GEN x, ulong p, long deplin) {
    1529      594549 :   return Flm_ker_i(x, p, deplin, 1);
    1530             : }
    1531             : 
    1532             : GEN
    1533       58174 : Flm_ker(GEN x, ulong p) {
    1534       58174 :   return Flm_ker_i(x, p, 0, 0);
    1535             : }
    1536             : 
    1537             : GEN
    1538           0 : Flm_deplin(GEN x, ulong p) {
    1539           0 :   return Flm_ker_i(x, p, 1, 0);
    1540             : }
    1541             : 
    1542             : ulong
    1543          28 : F2m_det_sp(GEN x) { return !F2m_ker_sp(x, 1); }
    1544             : 
    1545             : ulong
    1546          14 : F2m_det(GEN x)
    1547             : {
    1548          14 :   pari_sp av = avma;
    1549          14 :   ulong d = F2m_det_sp(F2m_copy(x));
    1550          14 :   avma = av; return d;
    1551             : }
    1552             : 
    1553             : /* in place, destroy a, SMALL_ULONG(p) is TRUE */
    1554             : static ulong
    1555          14 : Flm_det_gauss_OK(GEN a, long nbco, ulong p)
    1556             : {
    1557          14 :   long i,j,k, s = 1;
    1558          14 :   ulong q, x = 1;
    1559             : 
    1560          42 :   for (i=1; i<nbco; i++)
    1561             :   {
    1562          42 :     for(k=i; k<=nbco; k++)
    1563             :     {
    1564          42 :       ulong c = ucoeff(a,k,i) % p;
    1565          42 :       ucoeff(a,k,i) = c;
    1566          42 :       if (c) break;
    1567             :     }
    1568          28 :     for(j=k+1; j<=nbco; j++) ucoeff(a,j,i) %= p;
    1569          28 :     if (k > nbco) return ucoeff(a,i,i);
    1570          28 :     if (k != i)
    1571             :     { /* exchange the lines s.t. k = i */
    1572          14 :       for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
    1573          14 :       s = -s;
    1574             :     }
    1575          28 :     q = ucoeff(a,i,i);
    1576             : 
    1577          28 :     if (x & HIGHMASK) x %= p;
    1578          28 :     x *= q;
    1579          28 :     q = Fl_inv(q,p);
    1580          70 :     for (k=i+1; k<=nbco; k++)
    1581             :     {
    1582          42 :       ulong m = ucoeff(a,i,k) % p;
    1583          42 :       if (!m) continue;
    1584             : 
    1585          35 :       m = p - ((m*q)%p);
    1586          91 :       for (j=i+1; j<=nbco; j++)
    1587             :       {
    1588          56 :         ulong c = ucoeff(a,j,k);
    1589          56 :         if (c & HIGHMASK) c %= p;
    1590          56 :         ucoeff(a,j,k) = c  + m*ucoeff(a,j,i);
    1591             :       }
    1592             :     }
    1593             :   }
    1594          14 :   if (x & HIGHMASK) x %= p;
    1595          14 :   q = ucoeff(a,nbco,nbco);
    1596          14 :   if (q & HIGHMASK) q %= p;
    1597          14 :   x = (x*q) % p;
    1598          14 :   if (s < 0 && x) x = p - x;
    1599          14 :   return x;
    1600             : }
    1601             : 
    1602             : /* in place, destroy a */
    1603             : static ulong
    1604       53382 : Flm_det_gauss(GEN a, ulong p)
    1605             : {
    1606       53382 :   long i,j,k, s = 1, nbco = lg(a)-1;
    1607       53382 :   ulong pi, q, x = 1;
    1608             : 
    1609       53382 :   if (SMALL_ULONG(p)) return Flm_det_gauss_OK(a, nbco, p);
    1610       53368 :   pi = get_Fl_red(p);
    1611      339114 :   for (i=1; i<nbco; i++)
    1612             :   {
    1613      285829 :     for(k=i; k<=nbco; k++)
    1614      285822 :       if (ucoeff(a,k,i)) break;
    1615      285747 :     if (k > nbco) return ucoeff(a,i,i);
    1616      285747 :     if (k != i)
    1617             :     { /* exchange the lines s.t. k = i */
    1618          67 :       for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
    1619          67 :       s = -s;
    1620             :     }
    1621      285747 :     q = ucoeff(a,i,i);
    1622             : 
    1623      285747 :     x = Fl_mul_pre(x, q, p, pi);
    1624      285750 :     q = Fl_inv(q,p);
    1625     1220856 :     for (k=i+1; k<=nbco; k++)
    1626             :     {
    1627      935110 :       ulong m = ucoeff(a,i,k);
    1628      935110 :       if (!m) continue;
    1629             : 
    1630      892257 :       m = Fl_mul_pre(m, q, p, pi);
    1631     4550084 :       for (j=i+1; j<=nbco; j++)
    1632     3657909 :         ucoeff(a,j,k) = Fl_sub(ucoeff(a,j,k), Fl_mul_pre(m,ucoeff(a,j,i), p, pi), p);
    1633             :     }
    1634             :   }
    1635       53367 :   if (s < 0) x = Fl_neg(x, p);
    1636       53367 :   return Fl_mul(x, ucoeff(a,nbco,nbco), p);
    1637             : }
    1638             : 
    1639             : static ulong
    1640       34969 : Flm_det_CUP(GEN a, ulong p) {
    1641             :   GEN R, C, U, P;
    1642       34969 :   long d, i, n = lg(a) - 1, r;
    1643       34969 :   r = Flm_CUP(a, &R, &C, &U, &P, p);
    1644       34976 :   if (r < n)
    1645          53 :     d = 0;
    1646             :   else {
    1647       34923 :     d = perm_sign(P) == 1? 1: p-1;
    1648      351146 :     for (i = 1; i <= n; i++)
    1649      316229 :       d = Fl_mul(d, ucoeff(U, i, i), p);
    1650             :   }
    1651       34970 :   return d;
    1652             : }
    1653             : 
    1654             : static ulong
    1655       88350 : Flm_det_i(GEN x, ulong p, long inplace) {
    1656       88350 :   pari_sp av = avma;
    1657             :   ulong d;
    1658       88350 :   if (lg(x) - 1 >= Flm_CUP_LIMIT)
    1659       34968 :     d = Flm_det_CUP(x, p);
    1660             :   else
    1661       53382 :     d = Flm_det_gauss(inplace? x: Flm_copy(x), p);
    1662       88346 :   avma = av; return d;
    1663             : }
    1664             : 
    1665             : ulong
    1666       88349 : Flm_det_sp(GEN x, ulong p) {
    1667       88349 :   return Flm_det_i(x, p, 1);
    1668             : }
    1669             : 
    1670             : ulong
    1671           0 : Flm_det(GEN x, ulong p) {
    1672           0 :   return Flm_det_i(x, p, 0);
    1673             : }
    1674             : 
    1675             : static GEN
    1676      443746 : FpM_init(GEN a, GEN p, ulong *pp)
    1677             : {
    1678      443746 :   if (lgefint(p) == 3)
    1679             :   {
    1680      438903 :     *pp = uel(p,2);
    1681      438903 :     return (*pp==2)? ZM_to_F2m(a): ZM_to_Flm(a, *pp);
    1682             :   }
    1683        4843 :   *pp = 0; return a;
    1684             : }
    1685             : GEN
    1686        2282 : RgM_Fp_init(GEN a, GEN p, ulong *pp)
    1687             : {
    1688        2282 :   if (lgefint(p) == 3)
    1689             :   {
    1690        1960 :     *pp = uel(p,2);
    1691        1960 :     return (*pp==2)? RgM_to_F2m(a): RgM_to_Flm(a, *pp);
    1692             :   }
    1693         322 :   *pp = 0; return RgM_to_FpM(a,p);
    1694             : }
    1695             : 
    1696             : static GEN
    1697          63 : FpM_det_gen(GEN a, GEN p)
    1698             : {
    1699             :   void *E;
    1700          63 :   const struct bb_field *S = get_Fp_field(&E,p);
    1701          63 :   return gen_det(a, E, S);
    1702             : }
    1703             : GEN
    1704          84 : FpM_det(GEN a, GEN p)
    1705             : {
    1706          84 :   pari_sp av = avma;
    1707             :   ulong pp, d;
    1708          84 :   a = FpM_init(a, p, &pp);
    1709          84 :   switch(pp)
    1710             :   {
    1711          63 :   case 0: return FpM_det_gen(a, p);
    1712          14 :   case 2: d = F2m_det_sp(a); break;
    1713           7 :   default:d = Flm_det_sp(a,pp); break;
    1714             :   }
    1715          21 :   avma = av; return utoi(d);
    1716             : }
    1717             : 
    1718             : /* Destroy x */
    1719             : static GEN
    1720       31652 : F2m_gauss_pivot(GEN x, long *rr)
    1721             : {
    1722             :   GEN c, d;
    1723             :   long i, j, k, l, r, m, n;
    1724             : 
    1725       31652 :   n = lg(x)-1; if (!n) { *rr=0; return NULL; }
    1726       31652 :   m = mael(x,1,1); r=0;
    1727             : 
    1728       31652 :   d = cgetg(n+1, t_VECSMALL);
    1729       31652 :   c = const_F2v(m); l = lg(c)-1;
    1730      258025 :   for (k=1; k<=n; k++)
    1731             :   {
    1732      226373 :     GEN xk = gel(x,k);
    1733      226373 :     j = F2v_find_nonzero(xk, c, l, m);
    1734      226373 :     if (j>m) { r++; d[k] = 0; }
    1735             :     else
    1736             :     {
    1737      146388 :       F2v_clear(c,j); d[k] = j;
    1738     2104836 :       for (i=k+1; i<=n; i++)
    1739             :       {
    1740     1958448 :         GEN xi = gel(x,i);
    1741     1958448 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1742             :       }
    1743             :     }
    1744             :   }
    1745             : 
    1746       31652 :   *rr = r; avma = (pari_sp)d; return d;
    1747             : }
    1748             : 
    1749             : /* Destroy x */
    1750             : static GEN
    1751      156030 : Flm_gauss_pivot(GEN x, ulong p, long *rr)
    1752             : {
    1753             :   GEN c,d;
    1754             :   long i,j,k,r,t,n,m;
    1755             : 
    1756      156030 :   n=lg(x)-1; if (!n) { *rr=0; return NULL; }
    1757             : 
    1758      156030 :   m=nbrows(x); r=0;
    1759      156030 :   d=cgetg(n+1,t_VECSMALL);
    1760      156030 :   c = zero_zv(m);
    1761      658841 :   for (k=1; k<=n; k++)
    1762             :   {
    1763     1632487 :     for (j=1; j<=m; j++)
    1764     1541488 :       if (!c[j])
    1765             :       {
    1766      844740 :         ucoeff(x,j,k) %= p;
    1767      844740 :         if (ucoeff(x,j,k)) break;
    1768             :       }
    1769      502811 :     if (j>m) { r++; d[k]=0; }
    1770             :     else
    1771             :     {
    1772      411812 :       ulong piv = p - Fl_inv(ucoeff(x,j,k), p);
    1773      411812 :       c[j]=k; d[k]=j;
    1774     1153459 :       for (i=k+1; i<=n; i++)
    1775      741647 :         ucoeff(x,j,i) = Fl_mul(piv, ucoeff(x,j,i), p);
    1776     3076578 :       for (t=1; t<=m; t++)
    1777     2664766 :         if (!c[t]) /* no pivot on that line yet */
    1778             :         {
    1779     1721168 :           piv = ucoeff(x,t,k);
    1780     1721168 :           if (piv)
    1781             :           {
    1782      959247 :             ucoeff(x,t,k) = 0;
    1783     3047625 :             for (i=k+1; i<=n; i++)
    1784     4176756 :               ucoeff(x,t,i) = Fl_add(ucoeff(x,t,i),
    1785     2088378 :                                      Fl_mul(piv,ucoeff(x,j,i),p),p);
    1786             :           }
    1787             :         }
    1788      411812 :       for (i=k; i<=n; i++) ucoeff(x,j,i) = 0; /* dummy */
    1789             :     }
    1790             :   }
    1791      156030 :   *rr = r; avma = (pari_sp)d; return d;
    1792             : }
    1793             : 
    1794             : static GEN
    1795       61847 : Flm_pivots_CUP(GEN x, ulong p, long *rr) {
    1796             :   pari_sp av;
    1797       61847 :   long i, n = lg(x) - 1, r;
    1798       61847 :   GEN R, C, U, P, d = zero_zv(n);
    1799       61847 :   av = avma;
    1800       61847 :   r = Flm_CUP(x, &R, &C, &U, &P, p);
    1801      777310 :   for(i = 1; i <= r; i++)
    1802      715463 :     d[P[i]] = R[i];
    1803       61847 :   avma = av;
    1804       61847 :   *rr = n - r;
    1805       61847 :   return d;
    1806             : }
    1807             : 
    1808             : static GEN
    1809      217877 : Flm_pivots(GEN x, ulong p, long *rr, long inplace) {
    1810      217877 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1811       61847 :     return Flm_pivots_CUP(x, p, rr);
    1812      156030 :   return Flm_gauss_pivot(inplace? x: Flm_copy(x), p, rr);
    1813             : }
    1814             : 
    1815             : static GEN
    1816         150 : FpM_gauss_pivot_gen(GEN x, GEN p, long *rr)
    1817             : {
    1818             :   void *E;
    1819         150 :   const struct bb_field *S = get_Fp_field(&E,p);
    1820         150 :   return gen_Gauss_pivot(x, rr, E, S);
    1821             : }
    1822             : static GEN
    1823      129584 : FpM_gauss_pivot(GEN x, GEN p, long *rr)
    1824             : {
    1825             :   ulong pp;
    1826      129584 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1827      128247 :   x = FpM_init(x, p, &pp);
    1828      128247 :   switch(pp)
    1829             :   {
    1830         150 :   case 0: return FpM_gauss_pivot_gen(x, p, rr);
    1831       31568 :   case 2: return F2m_gauss_pivot(x, rr);
    1832       96529 :   default:return Flm_pivots(x, pp, rr, 1);
    1833             :   }
    1834             : }
    1835             : 
    1836             : GEN
    1837       77669 : FpM_image(GEN x, GEN p)
    1838             : {
    1839             :   long r;
    1840       77669 :   GEN d = FpM_gauss_pivot(x,p,&r); /* d left on stack for efficiency */
    1841       77669 :   return image_from_pivot(x,d,r);
    1842             : }
    1843             : 
    1844             : GEN
    1845          63 : Flm_image(GEN x, ulong p)
    1846             : {
    1847             :   long r;
    1848          63 :   GEN d = Flm_pivots(x, p, &r, 0); /* d left on stack for efficiency */
    1849          63 :   return image_from_pivot(x,d,r);
    1850             : }
    1851             : 
    1852             : GEN
    1853           7 : F2m_image(GEN x)
    1854             : {
    1855             :   long r;
    1856           7 :   GEN d = F2m_gauss_pivot(F2m_copy(x),&r); /* d left on stack for efficiency */
    1857           7 :   return image_from_pivot(x,d,r);
    1858             : }
    1859             : 
    1860             : long
    1861          28 : FpM_rank(GEN x, GEN p)
    1862             : {
    1863          28 :   pari_sp av = avma;
    1864             :   long r;
    1865          28 :   (void)FpM_gauss_pivot(x,p,&r);
    1866          28 :   avma = av; return lg(x)-1 - r;
    1867             : }
    1868             : 
    1869             : long
    1870        2207 : Flm_rank(GEN x, ulong p)
    1871             : {
    1872        2207 :   pari_sp av = avma;
    1873             :   long r;
    1874        2207 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT) {
    1875             :     GEN R, C;
    1876          35 :     r = Flm_echelon(x, &R, &C, p);
    1877          35 :     avma = av; return r;
    1878             :   }
    1879        2172 :   (void) Flm_pivots(x, p, &r, 0);
    1880        2172 :   avma = av; return lg(x)-1 - r;
    1881             : }
    1882             : 
    1883             : long
    1884          63 : F2m_rank(GEN x)
    1885             : {
    1886          63 :   pari_sp av = avma;
    1887             :   long r;
    1888          63 :   (void)F2m_gauss_pivot(F2m_copy(x),&r);
    1889          63 :   avma = av; return lg(x)-1 - r;
    1890             : }
    1891             : 
    1892             : static GEN
    1893        1225 : FlxqM_gauss_pivot(GEN x, GEN T, ulong p, long *rr)
    1894             : {
    1895             :   void *E;
    1896        1225 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1897        1225 :   return gen_Gauss_pivot(x, rr, E, S);
    1898             : }
    1899             : 
    1900             : static GEN
    1901          14 : FlxqM_pivots_CUP(GEN x, GEN T, ulong p, long *rr) {
    1902             :   pari_sp av;
    1903          14 :   long i, n = lg(x) - 1, r;
    1904          14 :   GEN R, C, U, P, d = zero_zv(n);
    1905          14 :   av = avma;
    1906          14 :   r = FlxqM_CUP(x, &R, &C, &U, &P, T, p);
    1907         182 :   for(i = 1; i <= r; i++)
    1908         168 :     d[P[i]] = R[i];
    1909          14 :   avma = av;
    1910          14 :   *rr = n - r;
    1911          14 :   return d;
    1912             : }
    1913             : 
    1914             : static GEN
    1915          28 : FlxqM_pivots(GEN x, GEN T, ulong p, long *rr) {
    1916          28 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    1917          14 :     return FlxqM_pivots_CUP(x, T, p, rr);
    1918          14 :   return FlxqM_gauss_pivot(x, T, p, rr);
    1919             : }
    1920             : 
    1921             : GEN
    1922          21 : FlxqM_image(GEN x, GEN T, ulong p)
    1923             : {
    1924             :   long r;
    1925          21 :   GEN d = FlxqM_pivots(x, T, p, &r); /* d left on stack for efficiency */
    1926          21 :   return image_from_pivot(x,d,r);
    1927             : }
    1928             : 
    1929             : long
    1930          28 : FlxqM_rank(GEN x, GEN T, ulong p)
    1931             : {
    1932          28 :   pari_sp av = avma;
    1933             :   long r;
    1934          28 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT) {
    1935             :     GEN R, C;
    1936          21 :     r = FlxqM_echelon(x, &R, &C, T, p);
    1937          21 :     avma = av; return r;
    1938             :   }
    1939           7 :   (void) FlxqM_pivots(x, T, p, &r);
    1940           7 :   avma = av; return lg(x)-1 - r;
    1941             : }
    1942             : 
    1943             : static GEN
    1944           7 : FlxqM_det_gen(GEN a, GEN T, ulong p)
    1945             : {
    1946             :   void *E;
    1947           7 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1948           7 :   return gen_det(a, E, S);
    1949             : }
    1950             : 
    1951             : static GEN
    1952          14 : FlxqM_det_CUP(GEN a, GEN T, ulong p) {
    1953          14 :   pari_sp av = avma;
    1954             :   GEN R, C, U, P, d;
    1955          14 :   long i, n = lg(a) - 1, r, sv = get_Flx_var(T);
    1956          14 :   r = FlxqM_CUP(a, &R, &C, &U, &P, T, p);
    1957          14 :   if (r < n)
    1958           0 :     d = pol0_Flx(sv);
    1959             :   else {
    1960          14 :     d = mkvecsmall2(sv, perm_sign(P) == 1? 1: p - 1);
    1961         322 :     for (i = 1; i <= n; i++)
    1962         308 :       d = Flxq_mul(d, gcoeff(U, i, i), T, p);
    1963             :   }
    1964          14 :   return gerepileuptoleaf(av, d);
    1965             : }
    1966             : 
    1967             : GEN
    1968          21 : FlxqM_det(GEN a, GEN T, ulong p) {
    1969          21 :   if (lg(a) - 1 >= FlxqM_CUP_LIMIT)
    1970          14 :     return FlxqM_det_CUP(a, T, p);
    1971             :   else
    1972           7 :     return FlxqM_det_gen(a, T, p);
    1973             : }
    1974             : 
    1975             : GEN
    1976          21 : FlxqM_FlxqC_invimage(GEN A, GEN B, GEN T, ulong p) {
    1977             :   void *E;
    1978          21 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1979          21 :   return gen_matcolinvimage(A, B, E, ff);
    1980             : }
    1981             : 
    1982             : GEN
    1983          28 : FlxqM_FlxqC_mul(GEN A, GEN B, GEN T, ulong p) {
    1984             :   void *E;
    1985          28 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1986          28 :   return gen_matcolmul(A, B, E, ff);
    1987             : }
    1988             : 
    1989             : GEN
    1990       12770 : FlxqM_mul(GEN A, GEN B, GEN T, ulong p) {
    1991             :   void *E;
    1992             :   const struct bb_field *ff;
    1993       12770 :   long n = lg(A) - 1;
    1994             : 
    1995       12770 :   if (n == 0)
    1996           0 :     return cgetg(1, t_MAT);
    1997       12770 :   if (n > 1)
    1998       10656 :     return FlxqM_mul_Kronecker(A, B, T, p);
    1999        2114 :   ff = get_Flxq_field(&E, T, p);
    2000        2114 :   return gen_matmul(A, B, E, ff);
    2001             : }
    2002             : 
    2003             : static GEN
    2004           7 : FlxqM_invimage_gen(GEN A, GEN B, GEN T, ulong p) {
    2005             :   void *E;
    2006           7 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    2007           7 :   return gen_matinvimage(A, B, E, ff);
    2008             : }
    2009             : 
    2010             : static GEN
    2011           7 : FlxqM_invimage_CUP(GEN A, GEN B, GEN T, ulong p) {
    2012           7 :   pari_sp av = avma;
    2013             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    2014           7 :   long r, sv = get_Flx_var(T);
    2015           7 :   r = FlxqM_CUP(A, &R, &C, &U, &P, T, p);
    2016           7 :   Rc = indexcompl(R, nbrows(B));
    2017           7 :   C1 = rowpermute(C, R);
    2018           7 :   C2 = rowpermute(C, Rc);
    2019           7 :   B1 = rowpermute(B, R);
    2020           7 :   B2 = rowpermute(B, Rc);
    2021           7 :   Z = FlxqM_rsolve_lower_unit(C1, B1, T, p);
    2022           7 :   if (!gequal(FlxqM_mul(C2, Z, T, p), B2))
    2023           7 :     return NULL;
    2024           0 :   Y = vconcat(FlxqM_rsolve_upper(vecslice(U, 1, r), Z, T, p),
    2025           0 :               zero_FlxM(lg(A) - 1 - r, lg(B) - 1, sv));
    2026           0 :   X = rowpermute(Y, perm_inv(P));
    2027           0 :   return gerepilecopy(av, X);
    2028             : }
    2029             : 
    2030             : GEN
    2031          14 : FlxqM_invimage(GEN A, GEN B, GEN T, ulong p) {
    2032          14 :   long nA = lg(A)-1, nB = lg(B)-1;
    2033             : 
    2034          14 :   if (!nB) return cgetg(1, t_MAT);
    2035          14 :   if (nA + nB >= FlxqM_CUP_LIMIT && nbrows(B) >= FlxqM_CUP_LIMIT)
    2036           7 :     return FlxqM_invimage_CUP(A, B, T, p);
    2037           7 :   return FlxqM_invimage_gen(A, B, T, p);
    2038             : }
    2039             : 
    2040             : GEN
    2041           7 : F2xqM_det(GEN a, GEN T)
    2042             : {
    2043             :   void *E;
    2044           7 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2045           7 :   return gen_det(a, E, S);
    2046             : }
    2047             : 
    2048             : static GEN
    2049          77 : F2xqM_gauss_gen(GEN a, GEN b, GEN T)
    2050             : {
    2051             :   void *E;
    2052          77 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2053          77 :   return gen_Gauss(a, b, E, S);
    2054             : }
    2055             : 
    2056             : GEN
    2057          14 : F2xqM_gauss(GEN a, GEN b, GEN T)
    2058             : {
    2059          14 :   pari_sp av = avma;
    2060          14 :   long n = lg(a)-1;
    2061             :   GEN u;
    2062          14 :   if (!n || lg(b)==1) { avma = av; return cgetg(1, t_MAT); }
    2063          14 :   u = F2xqM_gauss_gen(a, b, T);
    2064          14 :   if (!u) { avma = av; return NULL; }
    2065           7 :   return gerepilecopy(av, u);
    2066             : }
    2067             : 
    2068             : GEN
    2069          35 : F2xqM_inv(GEN a, GEN T)
    2070             : {
    2071          35 :   pari_sp av = avma;
    2072             :   GEN u;
    2073          35 :   if (lg(a) == 1) { avma = av; return cgetg(1, t_MAT); }
    2074          35 :   u = F2xqM_gauss_gen(a, matid_F2xqM(nbrows(a),T), T);
    2075          35 :   if (!u) { avma = av; return NULL; }
    2076          28 :   return gerepilecopy(av, u);
    2077             : }
    2078             : 
    2079             : GEN
    2080          28 : F2xqM_F2xqC_gauss(GEN a, GEN b, GEN T)
    2081             : {
    2082          28 :   pari_sp av = avma;
    2083             :   GEN u;
    2084          28 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2085          28 :   u = F2xqM_gauss_gen(a, mkmat(b), T);
    2086          28 :   if (!u) { avma = av; return NULL; }
    2087          14 :   return gerepilecopy(av, gel(u,1));
    2088             : }
    2089             : 
    2090             : GEN
    2091          21 : F2xqM_F2xqC_invimage(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_matcolinvimage(A, B, E, ff);
    2095             : }
    2096             : 
    2097             : GEN
    2098          21 : F2xqM_F2xqC_mul(GEN A, GEN B, GEN T) {
    2099             :   void *E;
    2100          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2101          21 :   return gen_matcolmul(A, B, E, ff);
    2102             : }
    2103             : 
    2104             : GEN
    2105           7 : F2xqM_mul(GEN A, GEN B, GEN T) {
    2106             :   void *E;
    2107           7 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2108           7 :   return gen_matmul(A, B, E, ff);
    2109             : }
    2110             : 
    2111             : GEN
    2112           7 : F2xqM_invimage(GEN A, GEN B, GEN T) {
    2113             :   void *E;
    2114           7 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    2115           7 :   return gen_matinvimage(A, B, E, ff);
    2116             : }
    2117             : 
    2118             : static GEN
    2119          63 : FqM_gauss_pivot_gen(GEN x, GEN T, GEN p, long *rr)
    2120             : {
    2121             :   void *E;
    2122          63 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2123          63 :   return gen_Gauss_pivot(x, rr, E, S);
    2124             : }
    2125             : static GEN
    2126        1260 : FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr)
    2127             : {
    2128        1260 :   if (lg(x)==1) { *rr = 0; return NULL; }
    2129        1260 :   if (!T) return FpM_gauss_pivot(x, p, rr);
    2130        1260 :   if (lgefint(p) == 3)
    2131             :   {
    2132        1197 :     pari_sp av = avma;
    2133        1197 :     ulong pp = uel(p,2);
    2134        1197 :     GEN Tp = ZXT_to_FlxT(T, pp);
    2135        1197 :     GEN d = FlxqM_gauss_pivot(FqM_to_FlxM(x, T, p), Tp, pp, rr);
    2136        1197 :     return d ? gerepileuptoleaf(av, d): d;
    2137             :   }
    2138          63 :   return FqM_gauss_pivot_gen(x, T, p, rr);
    2139             : }
    2140             : 
    2141             : GEN
    2142          21 : FqM_image(GEN x, GEN T, GEN p)
    2143             : {
    2144             :   long r;
    2145          21 :   GEN d = FqM_gauss_pivot(x,T,p,&r); /* d left on stack for efficiency */
    2146          21 :   return image_from_pivot(x,d,r);
    2147             : }
    2148             : 
    2149             : long
    2150          28 : FqM_rank(GEN x, GEN T, GEN p)
    2151             : {
    2152          28 :   pari_sp av = avma;
    2153             :   long r;
    2154          28 :   (void)FqM_gauss_pivot(x,T,p,&r);
    2155          28 :   avma = av; return lg(x)-1 - r;
    2156             : }
    2157             : 
    2158             : GEN
    2159          49 : FqM_det(GEN x, GEN T, GEN p)
    2160             : {
    2161             :   void *E;
    2162          49 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2163          49 :   return gen_det(x, E, S);
    2164             : }
    2165             : 
    2166             : GEN
    2167          21 : FqM_FqC_invimage(GEN A, GEN B, GEN T, GEN p) {
    2168             :   void *E;
    2169          21 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2170          21 :   return gen_matcolinvimage(A, B, E, ff);
    2171             : }
    2172             : 
    2173             : GEN
    2174          28 : FqM_FqC_mul(GEN A, GEN B, GEN T, GEN p) {
    2175             :   void *E;
    2176          28 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2177          28 :   return gen_matcolmul(A, B, E, ff);
    2178             : }
    2179             : 
    2180             : GEN
    2181         446 : FqM_mul(GEN A, GEN B, GEN T, GEN p) {
    2182             :   void *E;
    2183         446 :   long n = lg(A) - 1;
    2184             :   const struct bb_field *ff;
    2185         446 :   if (n == 0)
    2186           0 :     return cgetg(1, t_MAT);
    2187         446 :   if (n > 1)
    2188         446 :     return FqM_mul_Kronecker(A, B, T, p);
    2189           0 :   ff = get_Fq_field(&E, T, p);
    2190           0 :   return gen_matmul(A, B, E, ff);
    2191             : }
    2192             : 
    2193             : GEN
    2194          14 : FqM_invimage(GEN A, GEN B, GEN T, GEN p) {
    2195             :   void *E;
    2196          14 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    2197          14 :   return gen_matinvimage(A, B, E, ff);
    2198             : }
    2199             : 
    2200             : static GEN
    2201        1758 : FpM_ker_gen(GEN x, GEN p, long deplin)
    2202             : {
    2203             :   void *E;
    2204        1758 :   const struct bb_field *S = get_Fp_field(&E,p);
    2205        1758 :   return gen_ker(x, deplin, E, S);
    2206             : }
    2207             : static GEN
    2208      267783 : FpM_ker_i(GEN x, GEN p, long deplin)
    2209             : {
    2210      267783 :   pari_sp av = avma;
    2211             :   ulong pp;
    2212             :   GEN y;
    2213             : 
    2214      267783 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2215      267678 :   x = FpM_init(x, p, &pp);
    2216      267678 :   switch(pp)
    2217             :   {
    2218        1688 :   case 0: return FpM_ker_gen(x,p,deplin);
    2219             :   case 2:
    2220       71540 :     y = F2m_ker_sp(x, deplin);
    2221       71540 :     if (!y) return y;
    2222       71540 :     y = deplin? F2c_to_ZC(y): F2m_to_ZM(y);
    2223       71540 :     return gerepileupto(av, y);
    2224             :   default:
    2225      194450 :     y = Flm_ker_sp(x, pp, deplin);
    2226      194450 :     if (!y) return y;
    2227      194450 :     y = deplin? Flc_to_ZC(y): Flm_to_ZM(y);
    2228      194450 :     return gerepileupto(av, y);
    2229             :   }
    2230             : }
    2231             : 
    2232             : GEN
    2233      195392 : FpM_ker(GEN x, GEN p) { return FpM_ker_i(x,p,0); }
    2234             : 
    2235             : GEN
    2236       66651 : FpM_deplin(GEN x, GEN p) { return FpM_ker_i(x,p,1); }
    2237             : 
    2238             : static GEN
    2239         126 : FqM_ker_gen(GEN x, GEN T, GEN p, long deplin)
    2240             : {
    2241             :   void *E;
    2242         126 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2243         126 :   return gen_ker(x,deplin,E,S);
    2244             : }
    2245             : static GEN
    2246        8400 : FqM_ker_i(GEN x, GEN T, GEN p, long deplin)
    2247             : {
    2248        8400 :   if (!T) return FpM_ker_i(x,p,deplin);
    2249        2660 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2250             : 
    2251        2660 :   if (lgefint(p)==3)
    2252             :   {
    2253        2534 :     pari_sp ltop=avma;
    2254        2534 :     ulong l= p[2];
    2255        2534 :     GEN Ml = FqM_to_FlxM(x, T, p);
    2256        2534 :     GEN Tl = ZXT_to_FlxT(T,l);
    2257        2534 :     GEN p1 = FlxM_to_ZXM(FlxqM_ker(Ml,Tl,l));
    2258        2534 :     return gerepileupto(ltop,p1);
    2259             :   }
    2260         126 :   return FqM_ker_gen(x, T, p, deplin);
    2261             : }
    2262             : 
    2263             : GEN
    2264        8372 : FqM_ker(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,0); }
    2265             : 
    2266             : GEN
    2267          28 : FqM_deplin(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,1); }
    2268             : 
    2269             : static GEN
    2270         798 : FlxqM_ker_gen(GEN x, GEN T, ulong p, long deplin)
    2271             : {
    2272             :   const struct bb_field *ff;
    2273             :   void *E;
    2274             : 
    2275         798 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2276         798 :   ff=get_Flxq_field(&E,T,p);
    2277         798 :   return gen_ker(x,deplin, E, ff);
    2278             : }
    2279             : 
    2280             : static GEN
    2281        1806 : FlxqM_ker_echelon(GEN x, GEN T, ulong p) {
    2282        1806 :   pari_sp av = avma;
    2283             :   GEN R, Rc, C, C1, C2, S, K;
    2284        1806 :   long n = lg(x) - 1, r;
    2285        1806 :   r = FlxqM_echelon(shallowtrans(x), &R, &C, T, p);
    2286        1806 :   Rc = indexcompl(R, n);
    2287        1806 :   C1 = rowpermute(C, R);
    2288        1806 :   C2 = rowpermute(C, Rc);
    2289        1806 :   S = FlxqM_lsolve_lower_unit(C1, C2, T, p);
    2290        1806 :   K = vecpermute(shallowconcat(FlxM_neg(S, p), matid_FlxqM(n - r, T, p)),
    2291             :                  perm_inv(vecsmall_concat(R, Rc)));
    2292        1806 :   K = shallowtrans(K);
    2293        1806 :   return gerepilecopy(av, K);
    2294             : }
    2295             : 
    2296             : static GEN
    2297           7 : col_ei_FlxC(long n, long i, long sv) {
    2298           7 :   GEN v = zero_FlxC(n, sv);
    2299           7 :   gel(v, i) = pol1_Flx(sv);
    2300           7 :   return v;
    2301             : }
    2302             : 
    2303             : static GEN
    2304          14 : FlxqM_deplin_echelon(GEN x, GEN T, ulong p) {
    2305          14 :   pari_sp av = avma;
    2306             :   GEN R, Rc, C, C1, C2, s, v;
    2307          14 :   long i, n = lg(x) - 1, r, sv = get_Flx_var(T);
    2308          14 :   r = FlxqM_echelon(shallowtrans(x), &R, &C, T, p);
    2309          14 :   if (r == n) { avma = av; return NULL; }
    2310           7 :   Rc = indexcompl(R, n);
    2311           7 :   i = Rc[1];
    2312           7 :   C1 = rowpermute(C, R);
    2313           7 :   C2 = rowslice(C, i, i);
    2314           7 :   s = row(FlxqM_lsolve_lower_unit(C1, C2, T, p), 1);
    2315           7 :   settyp(s, t_COL);
    2316           7 :   v = vecpermute(shallowconcat(FlxC_neg(s, p), col_ei_FlxC(n - r, 1, sv)),
    2317             :                  perm_inv(vecsmall_concat(R, Rc)));
    2318           7 :   return gerepilecopy(av, v);
    2319             : }
    2320             : 
    2321             : static GEN
    2322        2618 : FlxqM_ker_i(GEN x, GEN T, ulong p, long deplin) {
    2323        2618 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    2324        1820 :     return deplin? FlxqM_deplin_echelon(x, T, p): FlxqM_ker_echelon(x, T, p);
    2325         798 :   return FlxqM_ker_gen(x, T, p, deplin);
    2326             : }
    2327             : 
    2328             : GEN
    2329        2590 : FlxqM_ker(GEN x, GEN T, ulong p)
    2330             : {
    2331        2590 :   return FlxqM_ker_i(x, T, p, 0);
    2332             : }
    2333             : 
    2334             : GEN
    2335          28 : FlxqM_deplin(GEN x, GEN T, ulong p)
    2336             : {
    2337          28 :   return FlxqM_ker_i(x, T, p, 1);
    2338             : }
    2339             : 
    2340             : static GEN
    2341          49 : F2xqM_ker_i(GEN x, GEN T, long deplin)
    2342             : {
    2343             :   const struct bb_field *ff;
    2344             :   void *E;
    2345             : 
    2346          49 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2347          49 :   ff = get_F2xq_field(&E,T);
    2348          49 :   return gen_ker(x,deplin, E, ff);
    2349             : }
    2350             : 
    2351             : GEN
    2352          35 : F2xqM_ker(GEN x, GEN T)
    2353             : {
    2354          35 :   return F2xqM_ker_i(x, T, 0);
    2355             : }
    2356             : 
    2357             : GEN
    2358          14 : F2xqM_deplin(GEN x, GEN T)
    2359             : {
    2360          14 :   return F2xqM_ker_i(x, T, 1);
    2361             : }
    2362             : 
    2363             : static GEN
    2364          28 : F2xqM_gauss_pivot(GEN x, GEN T, long *rr)
    2365             : {
    2366             :   void *E;
    2367          28 :   const struct bb_field *S = get_F2xq_field(&E,T);
    2368          28 :   return gen_Gauss_pivot(x, rr, E, S);
    2369             : }
    2370             : GEN
    2371           7 : F2xqM_image(GEN x, GEN T)
    2372             : {
    2373             :   long r;
    2374           7 :   GEN d = F2xqM_gauss_pivot(x,T,&r); /* d left on stack for efficiency */
    2375           7 :   return image_from_pivot(x,d,r);
    2376             : }
    2377             : long
    2378           7 : F2xqM_rank(GEN x, GEN T)
    2379             : {
    2380           7 :   pari_sp av = avma;
    2381             :   long r;
    2382           7 :   (void)F2xqM_gauss_pivot(x,T,&r);
    2383           7 :   avma = av; return lg(x)-1 - r;
    2384             : }
    2385             : /*******************************************************************/
    2386             : /*                                                                 */
    2387             : /*                       Solve A*X=B (Gauss pivot)                 */
    2388             : /*                                                                 */
    2389             : /*******************************************************************/
    2390             : /* x ~ 0 compared to reference y */
    2391             : int
    2392      572828 : approx_0(GEN x, GEN y)
    2393             : {
    2394      572828 :   long tx = typ(x);
    2395      572828 :   if (tx == t_COMPLEX)
    2396          42 :     return approx_0(gel(x,1), y) && approx_0(gel(x,2), y);
    2397      572933 :   return gequal0(x) ||
    2398      395989 :          (tx == t_REAL && gexpo(y) - gexpo(x) > bit_prec(x));
    2399             : }
    2400             : /* x a column, x0 same column in the original input matrix (for reference),
    2401             :  * c list of pivots so far */
    2402             : static long
    2403      585533 : gauss_get_pivot_max(GEN X, GEN X0, long ix, GEN c)
    2404             : {
    2405      585533 :   GEN p, r, x = gel(X,ix), x0 = gel(X0,ix);
    2406      585533 :   long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
    2407      585533 :   if (c)
    2408             :   {
    2409       80990 :     for (i=1; i<lx; i++)
    2410       53025 :       if (!c[i])
    2411             :       {
    2412       27244 :         long e = gexpo(gel(x,i));
    2413       27244 :         if (e > ex) { ex = e; k = i; }
    2414             :       }
    2415             :   }
    2416             :   else
    2417             :   {
    2418     1912791 :     for (i=ix; i<lx; i++)
    2419             :     {
    2420     1355223 :       long e = gexpo(gel(x,i));
    2421     1355223 :       if (e > ex) { ex = e; k = i; }
    2422             :     }
    2423             :   }
    2424      585533 :   if (!k) return lx;
    2425      572779 :   p = gel(x,k);
    2426      572779 :   r = gel(x0,k); if (isrationalzero(r)) r = x0;
    2427      572779 :   return approx_0(p, r)? lx: k;
    2428             : }
    2429             : static long
    2430       63091 : gauss_get_pivot_padic(GEN X, GEN p, long ix, GEN c)
    2431             : {
    2432       63091 :   GEN x = gel(X, ix);
    2433       63091 :   long i, k = 0, ex = (long)HIGHVALPBIT, lx = lg(x);
    2434       63091 :   if (c)
    2435             :   {
    2436         504 :     for (i=1; i<lx; i++)
    2437         378 :       if (!c[i] && !gequal0(gel(x,i)))
    2438             :       {
    2439         245 :         long e = gvaluation(gel(x,i), p);
    2440         245 :         if (e < ex) { ex = e; k = i; }
    2441             :       }
    2442             :   }
    2443             :   else
    2444             :   {
    2445      443905 :     for (i=ix; i<lx; i++)
    2446      380940 :       if (!gequal0(gel(x,i)))
    2447             :       {
    2448      182779 :         long e = gvaluation(gel(x,i), p);
    2449      182779 :         if (e < ex) { ex = e; k = i; }
    2450             :       }
    2451             :   }
    2452       63091 :   return k? k: lx;
    2453             : }
    2454             : static long
    2455      245834 : gauss_get_pivot_NZ(GEN X, GEN x0/*unused*/, long ix, GEN c)
    2456             : {
    2457      245834 :   GEN x = gel(X, ix);
    2458      245834 :   long i, lx = lg(x);
    2459             :   (void)x0;
    2460      245834 :   if (c)
    2461             :   {
    2462      120687 :     for (i=1; i<lx; i++)
    2463      114527 :       if (!c[i] && !gequal0(gel(x,i))) return i;
    2464             :   }
    2465             :   else
    2466             :   {
    2467      275914 :     for (i=ix; i<lx; i++)
    2468      275893 :       if (!gequal0(gel(x,i))) return i;
    2469             :   }
    2470        6181 :   return lx;
    2471             : }
    2472             : 
    2473             : /* Return pivot seeking function appropriate for the domain of the RgM x
    2474             :  * (first non zero pivot, maximal pivot...)
    2475             :  * x0 is a reference point used when guessing whether x[i,j] ~ 0
    2476             :  * (iff x[i,j] << x0[i,j]); typical case: mateigen, Gauss pivot on x - vp.Id,
    2477             :  * but use original x when deciding whether a prospective pivot is non-0 */
    2478             : static pivot_fun
    2479      247250 : get_pivot_fun(GEN x, GEN x0, GEN *data)
    2480             : {
    2481      247250 :   long i, j, hx, lx = lg(x);
    2482      247250 :   int res = t_INT;
    2483      247250 :   GEN p = NULL;
    2484             : 
    2485      247250 :   *data = NULL;
    2486      247250 :   if (lx == 1) return &gauss_get_pivot_NZ;
    2487      247110 :   hx = lgcols(x);
    2488     1158568 :   for (j=1; j<lx; j++)
    2489             :   {
    2490      911486 :     GEN xj = gel(x,j);
    2491     5741700 :     for (i=1; i<hx; i++)
    2492             :     {
    2493     4830242 :       GEN c = gel(xj,i);
    2494     4830242 :       switch(typ(c))
    2495             :       {
    2496             :         case t_REAL:
    2497     1560254 :           res = t_REAL;
    2498     1560254 :           break;
    2499             :         case t_COMPLEX:
    2500         266 :           if (typ(gel(c,1)) == t_REAL || typ(gel(c,2)) == t_REAL) res = t_REAL;
    2501         266 :           break;
    2502             :         case t_INT: case t_INTMOD: case t_FRAC: case t_FFELT: case t_QUAD:
    2503             :         case t_POLMOD: /* exact types */
    2504     3110255 :           break;
    2505             :         case t_PADIC:
    2506      159439 :           p = gel(c,2);
    2507      159439 :           res = t_PADIC;
    2508      159439 :           break;
    2509          28 :         default: return &gauss_get_pivot_NZ;
    2510             :       }
    2511             :     }
    2512             :   }
    2513      247082 :   switch(res)
    2514             :   {
    2515      181138 :     case t_REAL: *data = x0; return &gauss_get_pivot_max;
    2516        8225 :     case t_PADIC: *data = p; return &gauss_get_pivot_padic;
    2517       57719 :     default: return &gauss_get_pivot_NZ;
    2518             :   }
    2519             : }
    2520             : 
    2521             : static GEN
    2522      454372 : get_col(GEN a, GEN b, GEN p, long li)
    2523             : {
    2524      454372 :   GEN u = cgetg(li+1,t_COL);
    2525             :   long i, j;
    2526             : 
    2527      454372 :   gel(u,li) = gdiv(gel(b,li), p);
    2528     2437571 :   for (i=li-1; i>0; i--)
    2529             :   {
    2530     1983199 :     pari_sp av = avma;
    2531     1983199 :     GEN m = gel(b,i);
    2532     1983199 :     for (j=i+1; j<=li; j++) m = gsub(m, gmul(gcoeff(a,i,j), gel(u,j)));
    2533     1983199 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(a,i,i)));
    2534             :   }
    2535      454372 :   return u;
    2536             : }
    2537             : /* assume 0 <= a[i,j] < p */
    2538             : static GEN
    2539      288310 : Fl_get_col_OK(GEN a, GEN b, long li, ulong p)
    2540             : {
    2541      288310 :   GEN u = cgetg(li+1,t_VECSMALL);
    2542      288310 :   ulong m = uel(b,li) % p;
    2543             :   long i,j;
    2544             : 
    2545      288310 :   uel(u,li) = (m * ucoeff(a,li,li)) % p;
    2546     3497885 :   for (i = li-1; i > 0; i--)
    2547             :   {
    2548     3209575 :     m = p - uel(b,i)%p;
    2549    29312901 :     for (j = i+1; j <= li; j++) {
    2550    26103326 :       if (m & HIGHBIT) m %= p;
    2551    26103326 :       m += ucoeff(a,i,j) * uel(u,j); /* 0 <= u[j] < p */
    2552             :     }
    2553     3209575 :     m %= p;
    2554     3209575 :     if (m) m = ((p-m) * ucoeff(a,i,i)) % p;
    2555     3209575 :     uel(u,i) = m;
    2556             :   }
    2557      288310 :   return u;
    2558             : }
    2559             : static GEN
    2560     1376828 : Fl_get_col(GEN a, GEN b, long li, ulong p)
    2561             : {
    2562     1376828 :   GEN u = cgetg(li+1,t_VECSMALL);
    2563     1376828 :   ulong m = uel(b,li) % p;
    2564             :   long i,j;
    2565             : 
    2566     1376828 :   uel(u,li) = Fl_mul(m, ucoeff(a,li,li), p);
    2567     4137442 :   for (i=li-1; i>0; i--)
    2568             :   {
    2569     2760614 :     m = b[i]%p;
    2570     7555871 :     for (j = i+1; j <= li; j++)
    2571     4795257 :       m = Fl_sub(m, Fl_mul(ucoeff(a,i,j), uel(u,j), p), p);
    2572     2760614 :     if (m) m = Fl_mul(m, ucoeff(a,i,i), p);
    2573     2760614 :     uel(u,i) = m;
    2574             :   }
    2575     1376828 :   return u;
    2576             : }
    2577             : 
    2578             : /* bk -= m * bi */
    2579             : static void
    2580     2937878 : _submul(GEN b, long k, long i, GEN m)
    2581             : {
    2582     2937878 :   gel(b,k) = gsub(gel(b,k), gmul(m, gel(b,i)));
    2583     2937878 : }
    2584             : static int
    2585      733418 : init_gauss(GEN a, GEN *b, long *aco, long *li, int *iscol)
    2586             : {
    2587      733418 :   *iscol = *b ? (typ(*b) == t_COL): 0;
    2588      733418 :   *aco = lg(a) - 1;
    2589      733418 :   if (!*aco) /* a empty */
    2590             :   {
    2591          70 :     if (*b && lg(*b) != 1) pari_err_DIM("gauss");
    2592          70 :     *li = 0; return 0;
    2593             :   }
    2594      733348 :   *li = nbrows(a);
    2595      733348 :   if (*li < *aco) pari_err_INV("gauss [no left inverse]", a);
    2596      733348 :   if (*b)
    2597             :   {
    2598      720753 :     if (*li != *aco) pari_err_DIM("gauss");
    2599      720753 :     switch(typ(*b))
    2600             :     {
    2601             :       case t_MAT:
    2602       53115 :         if (lg(*b) == 1) return 0;
    2603       53115 :         *b = RgM_shallowcopy(*b);
    2604       53115 :         break;
    2605             :       case t_COL:
    2606      667638 :         *b = mkmat( leafcopy(*b) );
    2607      667638 :         break;
    2608           0 :       default: pari_err_TYPE("gauss",*b);
    2609             :     }
    2610      720753 :     if (nbrows(*b) != *li) pari_err_DIM("gauss");
    2611             :   }
    2612             :   else
    2613       12595 :     *b = matid(*li);
    2614      733348 :   return 1;
    2615             : }
    2616             : 
    2617             : static GEN Flm_inv_sp(GEN a, ulong *detp, ulong p);
    2618             : static GEN
    2619       20671 : RgM_inv_QM(GEN M)
    2620             : {
    2621       20671 :   pari_sp av = avma;
    2622       20671 :   GEN den, cM, pM = Q_primitive_part(M, &cM);
    2623       20671 :   GEN b = ZM_inv(pM, &den);
    2624       20664 :   if (!b) { avma = av; return NULL; }
    2625       20657 :   if (cM) den = gmul(den, cM);
    2626       20657 :   if (!gequal1(den)) b = ZM_Q_mul(b, ginv(den));
    2627       20657 :   return gerepileupto(av, b);
    2628             : }
    2629             : 
    2630             : static GEN
    2631         112 : RgM_inv_FpM(GEN a, GEN p)
    2632             : {
    2633             :   ulong pp;
    2634         112 :   a = RgM_Fp_init(a, p, &pp);
    2635         112 :   switch(pp)
    2636             :   {
    2637             :   case 0:
    2638          35 :     a = FpM_inv(a,p);
    2639          35 :     if (a) a = FpM_to_mod(a, p);
    2640          35 :     break;
    2641             :   case 2:
    2642          35 :     a = F2m_inv(a);
    2643          35 :     if (a) a = F2m_to_mod(a);
    2644          35 :     break;
    2645             :   default:
    2646          42 :     a = Flm_inv_sp(a, NULL, pp);
    2647          42 :     if (a) a = Flm_to_mod(a, pp);
    2648             :   }
    2649         112 :   return a;
    2650             : }
    2651             : 
    2652             : static GEN
    2653          42 : RgM_inv_FqM(GEN x, GEN pol, GEN p)
    2654             : {
    2655          42 :   pari_sp av = avma;
    2656          42 :   GEN T = RgX_to_FpX(pol, p);
    2657          42 :   GEN b = FqM_inv(RgM_to_FqM(x, T, p), T, p);
    2658          42 :   if (!b) { avma = av; return NULL; }
    2659          28 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    2660             : }
    2661             : 
    2662             : #define code(t1,t2) ((t1 << 6) | t2)
    2663             : static GEN
    2664       36527 : RgM_inv_fast(GEN x)
    2665             : {
    2666             :   GEN p, pol;
    2667             :   long pa;
    2668       36527 :   long t = RgM_type(x, &p,&pol,&pa);
    2669       36527 :   switch(t)
    2670             :   {
    2671             :     case t_INT:    /* Fall back */
    2672       20671 :     case t_FRAC:   return RgM_inv_QM(x);
    2673         147 :     case t_FFELT:  return FFM_inv(x, pol);
    2674         112 :     case t_INTMOD: return RgM_inv_FpM(x, p);
    2675             :     case code(t_POLMOD, t_INTMOD):
    2676          42 :                    return RgM_inv_FqM(x, pol, p);
    2677       15555 :     default:       return gen_0;
    2678             :   }
    2679             : }
    2680             : #undef code
    2681             : 
    2682             : GEN
    2683       36527 : RgM_inv(GEN a)
    2684             : {
    2685       36527 :   GEN b = RgM_inv_fast(a);
    2686       36520 :   return b==gen_0? RgM_solve(a, NULL): b;
    2687             : }
    2688             : 
    2689             : static int
    2690      359233 : is_modular_solve(GEN a, GEN b, GEN *u)
    2691             : {
    2692      359233 :   GEN p = NULL;
    2693             :   ulong pp;
    2694      359233 :   if (!RgM_is_FpM(a, &p) || !p) return 0;
    2695         126 :   if (!b) { *u = RgM_inv_FpM(a, p); return 1; }
    2696         126 :   switch(typ(b))
    2697             :   {
    2698             :     case t_COL:
    2699          49 :       if (!RgV_is_FpV(b, &p)) return 0;
    2700          49 :       a = RgM_Fp_init(a, p, &pp);
    2701          49 :       switch(pp)
    2702             :       {
    2703             :       case 0:
    2704          14 :         b = RgC_to_FpC(b, p);
    2705          14 :         a = FpM_FpC_gauss(a,b,p);
    2706          14 :         if (a) a = FpC_to_mod(a, p);
    2707          14 :         break;
    2708             :       case 2:
    2709          14 :         b = RgV_to_F2v(b);
    2710          14 :         a = F2m_F2c_gauss(a,b);
    2711          14 :         if (a) a = F2c_to_mod(a);
    2712          14 :         break;
    2713             :       default:
    2714          21 :         b = RgV_to_Flv(b, pp);
    2715          21 :         a = Flm_Flc_gauss(a,b,pp);
    2716          21 :         if (a) a = Flc_to_mod(a, pp);
    2717          21 :         break;
    2718             :       }
    2719          49 :       break;
    2720             :     case t_MAT:
    2721          77 :       if (!RgM_is_FpM(b, &p)) return 0;
    2722          77 :       a = RgM_Fp_init(a, p, &pp);
    2723          77 :       switch(pp)
    2724             :       {
    2725             :       case 0:
    2726          28 :         b = RgM_to_FpM(b, p);
    2727          28 :         a = FpM_gauss(a,b,p);
    2728          28 :         if (a) a = FpM_to_mod(a, p);
    2729          28 :         break;
    2730             :       case 2:
    2731          14 :         b = RgM_to_F2m(b);
    2732          14 :         a = F2m_gauss(a,b);
    2733          14 :         if (a) a = F2m_to_mod(a);
    2734          14 :         break;
    2735             :       default:
    2736          35 :         b = RgM_to_Flm(b, pp);
    2737          35 :         a = Flm_gauss(a,b,pp);
    2738          35 :         if (a) a = Flm_to_mod(a, pp);
    2739          35 :         break;
    2740             :       }
    2741          77 :       break;
    2742           0 :     default: return 0;
    2743             :   }
    2744         126 :   *u = a; return 1;
    2745             : }
    2746             : /* Gaussan Elimination. If a is square, return a^(-1)*b;
    2747             :  * if a has more rows than columns and b is NULL, return c such that c a = Id.
    2748             :  * a is a (not necessarily square) matrix
    2749             :  * b is a matrix or column vector, NULL meaning: take the identity matrix,
    2750             :  *   effectively returning the inverse of a
    2751             :  * If a and b are empty, the result is the empty matrix.
    2752             :  *
    2753             :  * li: number of rows of a and b
    2754             :  * aco: number of columns of a
    2755             :  * bco: number of columns of b (if matrix)
    2756             :  */
    2757             : GEN
    2758      359233 : RgM_solve(GEN a, GEN b)
    2759             : {
    2760      359233 :   pari_sp av = avma;
    2761             :   long i, j, k, li, bco, aco;
    2762             :   int iscol;
    2763             :   pivot_fun pivot;
    2764      359233 :   GEN p, u, data, ff = NULL;
    2765             : 
    2766      359233 :   if (is_modular_solve(a,b,&u)) return gerepileupto(av, u);
    2767      359107 :   if (RgM_is_FFM(a, &ff)) {
    2768         112 :     if (!b)
    2769           0 :       return FFM_inv(a, ff);
    2770         112 :     if (typ(b) == t_COL && RgC_is_FFC(b, &ff))
    2771          56 :       return FFM_FFC_gauss(a, b, ff);
    2772          56 :     if (typ(b) == t_MAT && RgM_is_FFM(b, &ff))
    2773          56 :       return FFM_gauss(a, b, ff);
    2774             :   }
    2775      358995 :   avma = av;
    2776             : 
    2777      358995 :   if (lg(a)-1 == 2 && nbrows(a) == 2) {
    2778             :     /* 2x2 matrix, start by inverting a */
    2779      138314 :     GEN u = gcoeff(a,1,1), v = gcoeff(a,1,2);
    2780      138314 :     GEN w = gcoeff(a,2,1), x = gcoeff(a,2,2);
    2781      138314 :     GEN D = gsub(gmul(u,x), gmul(v,w)), ainv;
    2782      138314 :     if (gequal0(D)) return NULL;
    2783      138314 :     ainv = mkmat2(mkcol2(x, gneg(w)), mkcol2(gneg(v), u));
    2784      138314 :     ainv = gmul(ainv, ginv(D));
    2785      138314 :     if (b) ainv = gmul(ainv, b);
    2786      138314 :     return gerepileupto(av, ainv);
    2787             :   }
    2788             : 
    2789      220681 :   if (!init_gauss(a, &b, &aco, &li, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    2790      220681 :   pivot = get_pivot_fun(a, a, &data);
    2791      220681 :   a = RgM_shallowcopy(a);
    2792      220681 :   bco = lg(b)-1;
    2793      220681 :   if(DEBUGLEVEL>4) err_printf("Entering gauss\n");
    2794             : 
    2795      220681 :   p = NULL; /* gcc -Wall */
    2796      783492 :   for (i=1; i<=aco; i++)
    2797             :   {
    2798             :     /* k is the line where we find the pivot */
    2799      783492 :     k = pivot(a, data, i, NULL);
    2800      783492 :     if (k > li) return NULL;
    2801      783471 :     if (k != i)
    2802             :     { /* exchange the lines s.t. k = i */
    2803      129485 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    2804      129485 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    2805             :     }
    2806      783471 :     p = gcoeff(a,i,i);
    2807      783471 :     if (i == aco) break;
    2808             : 
    2809     2103578 :     for (k=i+1; k<=li; k++)
    2810             :     {
    2811     1540767 :       GEN m = gcoeff(a,k,i);
    2812     1540767 :       if (!gequal0(m))
    2813             :       {
    2814      638130 :         m = gdiv(m,p);
    2815      638130 :         for (j=i+1; j<=aco; j++) _submul(gel(a,j),k,i,m);
    2816      638130 :         for (j=1;   j<=bco; j++) _submul(gel(b,j),k,i,m);
    2817             :       }
    2818             :     }
    2819      562811 :     if (gc_needed(av,1))
    2820             :     {
    2821          56 :       if(DEBUGMEM>1) pari_warn(warnmem,"gauss. i=%ld",i);
    2822          56 :       gerepileall(av,2, &a,&b);
    2823             :     }
    2824             :   }
    2825             : 
    2826      220660 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
    2827      220660 :   u = cgetg(bco+1,t_MAT);
    2828      220660 :   for (j=1; j<=bco; j++) gel(u,j) = get_col(a,gel(b,j),p,aco);
    2829      220660 :   return gerepilecopy(av, iscol? gel(u,1): u);
    2830             : }
    2831             : 
    2832             : /* assume dim A >= 1, A invertible + upper triangular  */
    2833             : static GEN
    2834      329239 : RgM_inv_upper_ind(GEN A, long index)
    2835             : {
    2836      329239 :   long n = lg(A)-1, i = index, j;
    2837      329239 :   GEN u = zerocol(n);
    2838      329239 :   gel(u,i) = ginv(gcoeff(A,i,i));
    2839     1363653 :   for (i--; i>0; i--)
    2840             :   {
    2841     1034414 :     pari_sp av = avma;
    2842     1034414 :     GEN m = gneg(gmul(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2843     1034414 :     for (j=i+2; j<=n; j++) m = gsub(m, gmul(gcoeff(A,i,j),gel(u,j)));
    2844     1034414 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(A,i,i)));
    2845             :   }
    2846      329239 :   return u;
    2847             : }
    2848             : GEN
    2849       58485 : RgM_inv_upper(GEN A)
    2850             : {
    2851             :   long i, l;
    2852       58485 :   GEN B = cgetg_copy(A, &l);
    2853       58485 :   for (i = 1; i < l; i++) gel(B,i) = RgM_inv_upper_ind(A, i);
    2854       58485 :   return B;
    2855             : }
    2856             : 
    2857             : /* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal  */
    2858             : static GEN
    2859          98 : FpM_inv_upper_1_ind(GEN A, long index, GEN p)
    2860             : {
    2861          98 :   long n = lg(A)-1, i = index, j;
    2862          98 :   GEN u = zerocol(n);
    2863          98 :   gel(u,i) = gen_1;
    2864         567 :   for (i--; i>0; i--)
    2865             :   {
    2866         469 :     pari_sp av = avma;
    2867         469 :     GEN m = negi(mulii(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2868         469 :     for (j=i+2; j<=n; j++) m = subii(m, mulii(gcoeff(A,i,j),gel(u,j)));
    2869         469 :     gel(u,i) = gerepileuptoint(av, modii(m,p));
    2870             :   }
    2871          98 :   return u;
    2872             : }
    2873             : static GEN
    2874          14 : FpM_inv_upper_1(GEN A, GEN p)
    2875             : {
    2876             :   long i, l;
    2877          14 :   GEN B = cgetg_copy(A, &l);
    2878          14 :   for (i = 1; i < l; i++) gel(B,i) = FpM_inv_upper_1_ind(A, i, p);
    2879          14 :   return B;
    2880             : }
    2881             : /* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal,
    2882             :  * reduced mod p */
    2883             : static GEN
    2884          28 : Flm_inv_upper_1_ind(GEN A, long index, ulong p)
    2885             : {
    2886          28 :   long n = lg(A)-1, i = index, j;
    2887          28 :   GEN u = const_vecsmall(n, 0);
    2888          28 :   u[i] = 1;
    2889          28 :   if (SMALL_ULONG(p))
    2890          21 :     for (i--; i>0; i--)
    2891             :     {
    2892           7 :       ulong m = ucoeff(A,i,i+1) * uel(u,i+1); /* j = i+1 */
    2893           7 :       for (j=i+2; j<=n; j++)
    2894             :       {
    2895           0 :         if (m & HIGHMASK) m %= p;
    2896           0 :         m += ucoeff(A,i,j) * uel(u,j);
    2897             :       }
    2898           7 :       u[i] = Fl_neg(m % p, p);
    2899             :     }
    2900             :   else
    2901          21 :     for (i--; i>0; i--)
    2902             :     {
    2903           7 :       ulong m = Fl_mul(ucoeff(A,i,i+1),uel(u,i+1), p); /* j = i+1 */
    2904           7 :       for (j=i+2; j<=n; j++) m = Fl_add(m, Fl_mul(ucoeff(A,i,j),uel(u,j),p), p);
    2905           7 :       u[i] = Fl_neg(m, p);
    2906             :     }
    2907          28 :   return u;
    2908             : }
    2909             : static GEN
    2910          14 : F2m_inv_upper_1_ind(GEN A, long index)
    2911             : {
    2912          14 :   pari_sp av = avma;
    2913          14 :   long n = lg(A)-1, i = index, j;
    2914          14 :   GEN u = const_vecsmall(n, 0);
    2915          14 :   u[i] = 1;
    2916          21 :   for (i--; i>0; i--)
    2917             :   {
    2918           7 :     ulong m = F2m_coeff(A,i,i+1) & uel(u,i+1); /* j = i+1 */
    2919           7 :     for (j=i+2; j<=n; j++) m ^= F2m_coeff(A,i,j) & uel(u,j);
    2920           7 :     u[i] = m & 1;
    2921             :   }
    2922          14 :   return gerepileuptoleaf(av, Flv_to_F2v(u));
    2923             : }
    2924             : static GEN
    2925          14 : Flm_inv_upper_1(GEN A, ulong p)
    2926             : {
    2927             :   long i, l;
    2928          14 :   GEN B = cgetg_copy(A, &l);
    2929          14 :   for (i = 1; i < l; i++) gel(B,i) = Flm_inv_upper_1_ind(A, i, p);
    2930          14 :   return B;
    2931             : }
    2932             : static GEN
    2933           7 : F2m_inv_upper_1(GEN A)
    2934             : {
    2935             :   long i, l;
    2936           7 :   GEN B = cgetg_copy(A, &l);
    2937           7 :   for (i = 1; i < l; i++) gel(B,i) = F2m_inv_upper_1_ind(A, i);
    2938           7 :   return B;
    2939             : }
    2940             : 
    2941             : static GEN
    2942      966260 : split_realimag_col(GEN z, long r1, long r2)
    2943             : {
    2944      966260 :   long i, ru = r1+r2;
    2945      966260 :   GEN x = cgetg(ru+r2+1,t_COL), y = x + r2;
    2946     2906608 :   for (i=1; i<=r1; i++) {
    2947     1940348 :     GEN a = gel(z,i);
    2948     1940348 :     if (typ(a) == t_COMPLEX) a = gel(a,1); /* paranoia: a should be real */
    2949     1940348 :     gel(x,i) = a;
    2950             :   }
    2951     1585168 :   for (   ; i<=ru; i++) {
    2952      618908 :     GEN b, a = gel(z,i);
    2953      618908 :     if (typ(a) == t_COMPLEX) { b = gel(a,2); a = gel(a,1); } else b = gen_0;
    2954      618908 :     gel(x,i) = a;
    2955      618908 :     gel(y,i) = b;
    2956             :   }
    2957      966260 :   return x;
    2958             : }
    2959             : GEN
    2960      507162 : split_realimag(GEN x, long r1, long r2)
    2961             : {
    2962             :   long i,l; GEN y;
    2963      507162 :   if (typ(x) == t_COL) return split_realimag_col(x,r1,r2);
    2964      250695 :   y = cgetg_copy(x, &l);
    2965      250695 :   for (i=1; i<l; i++) gel(y,i) = split_realimag_col(gel(x,i), r1, r2);
    2966      250695 :   return y;
    2967             : }
    2968             : 
    2969             : /* assume M = (r1+r2) x (r1+2r2) matrix and y compatible vector or matrix
    2970             :  * r1 first lines of M,y are real. Solve the system obtained by splitting
    2971             :  * real and imaginary parts. */
    2972             : GEN
    2973      247337 : RgM_solve_realimag(GEN M, GEN y)
    2974             : {
    2975      247337 :   long l = lg(M), r2 = l - lgcols(M), r1 = l-1 - 2*r2;
    2976      247337 :   return RgM_solve(split_realimag(M, r1,r2),
    2977             :                    split_realimag(y, r1,r2));
    2978             : }
    2979             : 
    2980             : GEN
    2981         378 : gauss(GEN a, GEN b)
    2982             : {
    2983             :   GEN z;
    2984         378 :   if (typ(a)!=t_MAT) pari_err_TYPE("gauss",a);
    2985         462 :   if (RgM_is_ZM(a) && b &&
    2986         133 :       ((typ(b) == t_COL && RgV_is_ZV(b)) || (typ(b) == t_MAT && RgM_is_ZM(b))))
    2987          84 :     z = ZM_gauss(a,b);
    2988             :   else
    2989         294 :     z = RgM_solve(a,b);
    2990         378 :   if (!z) pari_err_INV("gauss",a);
    2991         273 :   return z;
    2992             : }
    2993             : 
    2994             : static GEN
    2995       83808 : F2_get_col(GEN b, GEN d, long li, long aco)
    2996             : {
    2997       83808 :   long i, l = nbits2lg(aco);
    2998       83808 :   GEN u = cgetg(l, t_VECSMALL);
    2999       83808 :   u[1] = aco;
    3000     1366618 :   for (i = 1; i <= li; i++)
    3001     1282810 :     if (d[i]) /* d[i] can still be 0 if li > aco */
    3002             :     {
    3003     1282775 :       if (F2v_coeff(b, i))
    3004      426991 :         F2v_set(u, d[i]);
    3005             :       else
    3006      855784 :         F2v_clear(u, d[i]);
    3007             :     }
    3008       83808 :   return u;
    3009             : }
    3010             : 
    3011             : /* destroy a, b */
    3012             : static GEN
    3013       12894 : F2m_gauss_sp(GEN a, GEN b)
    3014             : {
    3015       12894 :   long i, j, k, l, li, bco, aco = lg(a)-1;
    3016             :   GEN u, d;
    3017             : 
    3018       12894 :   if (!aco) return cgetg(1,t_MAT);
    3019       12894 :   li = gel(a,1)[1];
    3020       12894 :   d = zero_Flv(li);
    3021       12894 :   bco = lg(b)-1;
    3022       96716 :   for (i=1; i<=aco; i++)
    3023             :   {
    3024       83836 :     GEN ai = vecsmall_copy(gel(a,i));
    3025       83836 :     if (!d[i] && F2v_coeff(ai, i))
    3026       40844 :       k = i;
    3027             :     else
    3028       42992 :       for (k = 1; k <= li; k++) if (!d[k] && F2v_coeff(ai,k)) break;
    3029             :     /* found a pivot on row k */
    3030       83836 :     if (k > li) return NULL;
    3031       83822 :     d[k] = i;
    3032             : 
    3033             :     /* Clear k-th row but column-wise instead of line-wise */
    3034             :     /*  a_ij -= a_i1*a1j/a_11
    3035             :        line-wise grouping:  L_j -= a_1j/a_11*L_1
    3036             :        column-wise:         C_i -= a_i1/a_11*C_1
    3037             :     */
    3038       83822 :     F2v_clear(ai,k);
    3039     1366639 :     for (l=1; l<=aco; l++)
    3040             :     {
    3041     1282817 :       GEN al = gel(a,l);
    3042     1282817 :       if (F2v_coeff(al,k)) F2v_add_inplace(al,ai);
    3043             :     }
    3044     1366604 :     for (l=1; l<=bco; l++)
    3045             :     {
    3046     1282782 :       GEN bl = gel(b,l);
    3047     1282782 :       if (F2v_coeff(bl,k)) F2v_add_inplace(bl,ai);
    3048             :     }
    3049             :   }
    3050       12880 :   u = cgetg(bco+1,t_MAT);
    3051       12880 :   for (j = 1; j <= bco; j++) gel(u,j) = F2_get_col(gel(b,j), d, li, aco);
    3052       12880 :   return u;
    3053             : }
    3054             : 
    3055             : GEN
    3056          28 : F2m_gauss(GEN a, GEN b)
    3057             : {
    3058          28 :   pari_sp av = avma;
    3059          28 :   if (lg(a) == 1) return cgetg(1,t_MAT);
    3060          28 :   return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), F2m_copy(b)));
    3061             : }
    3062             : GEN
    3063          14 : F2m_F2c_gauss(GEN a, GEN b)
    3064             : {
    3065          14 :   pari_sp av = avma;
    3066          14 :   GEN z = F2m_gauss(a, mkmat(b));
    3067          14 :   if (!z) { avma = av; return NULL; }
    3068           7 :   if (lg(z) == 1) { avma = av; return cgetg(1,t_VECSMALL); }
    3069           7 :   return gerepileuptoleaf(av, gel(z,1));
    3070             : }
    3071             : 
    3072             : GEN
    3073          35 : F2m_inv(GEN a)
    3074             : {
    3075          35 :   pari_sp av = avma;
    3076          35 :   if (lg(a) == 1) return cgetg(1,t_MAT);
    3077          35 :   return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), matid_F2m(gel(a,1)[1])));
    3078             : }
    3079             : 
    3080             : /* destroy a, b */
    3081             : static GEN
    3082       37296 : Flm_gauss_sp_OK(GEN a, GEN b, ulong *detp, ulong p)
    3083             : {
    3084       37296 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3085       37296 :   ulong det = 1;
    3086             :   GEN u;
    3087             : 
    3088       37296 :   li = nbrows(a);
    3089       37296 :   bco = lg(b)-1;
    3090      288331 :   for (i=1; i<=aco; i++)
    3091             :   {
    3092             :     ulong invpiv;
    3093             :     /* Fl_get_col wants 0 <= a[i,j] < p for all i,j */
    3094      288331 :     for (k = 1; k < i; k++) ucoeff(a,k,i) %= p;
    3095      520096 :     for (k = i; k <= li; k++)
    3096             :     {
    3097      520089 :       ulong piv = ( ucoeff(a,k,i) %= p );
    3098      520089 :       if (piv)
    3099             :       {
    3100      288324 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3101      288324 :         if (detp)
    3102             :         {
    3103           0 :           if (det & HIGHMASK) det %= p;
    3104           0 :           det *= piv;
    3105             :         }
    3106      288324 :         break;
    3107             :       }
    3108             :     }
    3109             :     /* found a pivot on line k */
    3110      288331 :     if (k > li) return NULL;
    3111      288324 :     if (k != i)
    3112             :     { /* swap lines so that k = i */
    3113       91167 :       s = -s;
    3114       91167 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3115       91167 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3116             :     }
    3117      288324 :     if (i == aco) break;
    3118             : 
    3119      251035 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3120     1855854 :     for (k=i+1; k<=li; k++)
    3121             :     {
    3122     1604819 :       ulong m = ( ucoeff(a,k,i) %= p );
    3123     1604819 :       if (!m) continue;
    3124             : 
    3125      504755 :       m = Fl_mul(m, invpiv, p);
    3126      504755 :       if (m == 1) {
    3127       91014 :         for (j=i+1; j<=aco; j++) _Fl_add_OK(gel(a,j),k,i, p);
    3128       91014 :         for (j=1;   j<=bco; j++) _Fl_add_OK(gel(b,j),k,i, p);
    3129             :       } else {
    3130      413741 :         for (j=i+1; j<=aco; j++) _Fl_addmul_OK(gel(a,j),k,i,m, p);
    3131      413741 :         for (j=1;   j<=bco; j++) _Fl_addmul_OK(gel(b,j),k,i,m, p);
    3132             :       }
    3133             :     }
    3134             :   }
    3135       37289 :   if (detp)
    3136             :   {
    3137           0 :     det %=  p;
    3138           0 :     if (s < 0 && det) det = p - det;
    3139           0 :     *detp = det;
    3140             :   }
    3141       37289 :   u = cgetg(bco+1,t_MAT);
    3142       37289 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col_OK(a,gel(b,j), aco,p);
    3143       37289 :   return u;
    3144             : }
    3145             : 
    3146             : /* destroy a, b */
    3147             : static GEN
    3148      540316 : Flm_gauss_sp(GEN a, GEN b, ulong *detp, ulong p)
    3149             : {
    3150      540316 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3151      540316 :   ulong det = 1;
    3152             :   GEN u;
    3153             :   ulong pi;
    3154      540316 :   if (!aco) { if (detp) *detp = 1; return cgetg(1,t_MAT); }
    3155      540316 :   if (SMALL_ULONG(p)) return Flm_gauss_sp_OK(a, b, detp, p);
    3156      503020 :   pi = get_Fl_red(p);
    3157      503020 :   li = nbrows(a);
    3158      503020 :   bco = lg(b)-1;
    3159     1376842 :   for (i=1; i<=aco; i++)
    3160             :   {
    3161             :     ulong invpiv;
    3162             :     /* Fl_get_col wants 0 <= a[i,j] < p for all i,j */
    3163     1429846 :     for (k = i; k <= li; k++)
    3164             :     {
    3165     1429846 :       ulong piv = ucoeff(a,k,i);
    3166     1429846 :       if (piv)
    3167             :       {
    3168     1376842 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3169     1376842 :         if (detp) det = Fl_mul_pre(det, piv, p, pi);
    3170     1376842 :         break;
    3171             :       }
    3172             :     }
    3173             :     /* found a pivot on line k */
    3174     1376842 :     if (k > li) return NULL;
    3175     1376842 :     if (k != i)
    3176             :     { /* swap lines so that k = i */
    3177       46173 :       s = -s;
    3178       46173 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3179       46173 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3180             :     }
    3181     1376842 :     if (i == aco) break;
    3182             : 
    3183      873822 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3184     2254143 :     for (k=i+1; k<=li; k++)
    3185             :     {
    3186     1380321 :       ulong m = ucoeff(a,k,i);
    3187     1380321 :       if (!m) continue;
    3188             : 
    3189     1220162 :       m = Fl_mul_pre(m, invpiv, p, pi);
    3190     1220162 :       if (m == 1) {
    3191       27178 :         for (j=i+1; j<=aco; j++) _Fl_add(gel(a,j),k,i, p);
    3192       27178 :         for (j=1;   j<=bco; j++) _Fl_add(gel(b,j),k,i, p);
    3193             :       } else {
    3194     1192984 :         for (j=i+1; j<=aco; j++) _Fl_addmul(gel(a,j),k,i,m, p, pi);
    3195     1192984 :         for (j=1;   j<=bco; j++) _Fl_addmul(gel(b,j),k,i,m, p, pi);
    3196             :       }
    3197             :     }
    3198             :   }
    3199      503020 :   if (detp)
    3200             :   {
    3201           0 :     if (s < 0 && det) det = p - det;
    3202           0 :     *detp = det;
    3203             :   }
    3204      503020 :   u = cgetg(bco+1,t_MAT);
    3205      503020 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col(a,gel(b,j), aco,p);
    3206      503020 :   return u;
    3207             : }
    3208             : 
    3209             : static GEN
    3210      170579 : Flm_gauss_from_CUP(GEN b, GEN R, GEN C, GEN U, GEN P, ulong p, ulong *detp)
    3211             : {
    3212      170579 :   GEN Y = Flm_rsolve_lower_unit(rowpermute(C, R), rowpermute(b, R), p);
    3213      170578 :   GEN X = rowpermute(Flm_rsolve_upper(U, Y, p), perm_inv(P));
    3214      170573 :   if (detp)
    3215             :   {
    3216      147376 :     ulong d = perm_sign(P) == 1? 1: p-1;
    3217      147375 :     long i, r = lg(R);
    3218     1625631 :     for (i = 1; i < r; i++)
    3219     1478251 :       d = Fl_mul(d, ucoeff(U, i, i), p);
    3220      147380 :     *detp = d;
    3221             :   }
    3222      170577 :   return X;
    3223             : }
    3224             : 
    3225             : static GEN
    3226       23211 : Flm_gauss_CUP(GEN a, GEN b, ulong *detp, ulong p) {
    3227             :   GEN R, C, U, P;
    3228       23211 :   long n = lg(a) - 1, r;
    3229       23211 :   if (nbrows(a) < n || (r = Flm_CUP(a, &R, &C, &U, &P, p)) < n)
    3230          14 :     return NULL;
    3231       23197 :   return Flm_gauss_from_CUP(b, R, C, U, P, p, detp);
    3232             : }
    3233             : 
    3234             : GEN
    3235          56 : Flm_gauss(GEN a, GEN b, ulong p) {
    3236          56 :   pari_sp av = avma;
    3237             :   GEN x;
    3238          56 :   if (lg(a) - 1 >= Flm_CUP_LIMIT)
    3239          14 :     x = Flm_gauss_CUP(a, b, NULL, p);
    3240             :   else {
    3241          42 :     a = RgM_shallowcopy(a);
    3242          42 :     b = RgM_shallowcopy(b);
    3243          42 :     x = Flm_gauss_sp(a, b, NULL, p);
    3244             :   }
    3245          56 :   if (!x) { avma = av; return NULL; }
    3246          42 :   return gerepileupto(av, x);
    3247             : }
    3248             : 
    3249             : static GEN
    3250      531507 : Flm_inv_i(GEN a, ulong *detp, ulong p, long inplace) {
    3251      531507 :   pari_sp av = avma;
    3252      531507 :   long n = lg(a) - 1;
    3253             :   GEN b, x;
    3254      531507 :   if (n == 0) return cgetg(1, t_MAT);
    3255      531507 :   b = matid_Flm(nbrows(a));
    3256      531507 :   if (n >= Flm_CUP_LIMIT)
    3257       23197 :     x = Flm_gauss_CUP(a, b, detp, p);
    3258             :   else {
    3259      508310 :     if (!inplace)
    3260        5313 :       a = RgM_shallowcopy(a);
    3261      508310 :     x = Flm_gauss_sp(a, b, detp, p);
    3262             :   }
    3263      531507 :   if (!x) { avma = av; return NULL; }
    3264      531500 :   return gerepileupto(av, x);
    3265             : }
    3266             : 
    3267             : static GEN
    3268      517395 : Flm_inv_sp(GEN a, ulong *detp, ulong p) {
    3269      517395 :   return Flm_inv_i(a, detp, p, 1);
    3270             : }
    3271             : 
    3272             : GEN
    3273       14112 : Flm_inv(GEN a, ulong p) {
    3274       14112 :   return Flm_inv_i(a, NULL, p, 0);
    3275             : }
    3276             : 
    3277             : GEN
    3278          21 : Flm_Flc_gauss(GEN a, GEN b, ulong p) {
    3279          21 :   pari_sp av = avma;
    3280          21 :   GEN z = Flm_gauss(a, mkmat(b), p);
    3281          21 :   if (!z) { avma = av; return NULL; }
    3282          14 :   if (lg(z) == 1) { avma = av; return cgetg(1,t_VECSMALL); }
    3283          14 :   return gerepileuptoleaf(av, gel(z,1));
    3284             : }
    3285             : 
    3286             : GEN
    3287      147377 : Flm_adjoint(GEN A, ulong p)
    3288             : {
    3289      147377 :   pari_sp av = avma;
    3290             :   GEN R, C, U, P, C1, U1, v, c, d;
    3291      147377 :   long r, i, q, n = lg(A)-1, m;
    3292             :   ulong D;
    3293      147377 :   if (n == 0) return cgetg(1, t_MAT);
    3294      147377 :   r = Flm_CUP(A, &R, &C, &U, &P, p);
    3295      147380 :   m = nbrows(A);
    3296      147382 :   if (r == n)
    3297             :   {
    3298      147375 :     GEN X = Flm_gauss_from_CUP(matid_Flm(m), R, C, U, P, p, &D);
    3299      147375 :     return gerepileupto(av, Flm_Fl_mul(X, D, p));
    3300             :   }
    3301           7 :   if (r < n-1) return zeromat(m, m);
    3302          21 :   for (q = m, i = 1; i < m; i++)
    3303          14 :    if (R[i] != i) { q = i; break; }
    3304           7 :   C1 = matslice(C, 1, q-1, 1, q-1);
    3305           7 :   c = vecslice(Flm_row(C, q), 1, q-1);
    3306           7 :   c = Flm_lsolve_lower_unit(C1, Flm_transpose(mkmat(c)), p);
    3307           7 :   d = cgetg(n+1, t_VECSMALL);
    3308           7 :   for (i=1; i<q; i++)    uel(d,i) = ucoeff(c,1,i);
    3309           7 :   uel(d,q) = p-1;
    3310           7 :   for (i=q+1; i<=n; i++) uel(d,i) = 0;
    3311           7 :   U1 = vecslice(U, 1, n-1);
    3312           7 :   v = gel(Flm_rsolve_upper(U1, mkmat(gel(U,n)), p),1);
    3313           7 :   v = vecsmall_append(v, p-1);
    3314           7 :   D = perm_sign(P) != (odd(q+n)?-1:1) ? p-1 : 1;
    3315          21 :   for (i = 1; i <= n-1; i++)
    3316          14 :     D = Fl_mul(D, ucoeff(U1, i, i), p);
    3317           7 :   d = Flv_Fl_mul(d, D, p);
    3318           7 :   return rowpermute(Flc_Flv_mul(v, d, p),perm_inv(P));
    3319             : }
    3320             : 
    3321             : static GEN
    3322        2942 : FpM_gauss_gen(GEN a, GEN b, GEN p)
    3323             : {
    3324             :   void *E;
    3325        2942 :   const struct bb_field *S = get_Fp_field(&E,p);
    3326        2942 :   return gen_Gauss(a,b, E, S);
    3327             : }
    3328             : /* a an FpM, lg(a)>1; b an FpM or NULL (replace by identity) */
    3329             : static GEN
    3330       47737 : FpM_gauss_i(GEN a, GEN b, GEN p, ulong *pp)
    3331             : {
    3332       47737 :   long n = nbrows(a);
    3333       47737 :   a = FpM_init(a,p,pp);
    3334       47737 :   switch(*pp)
    3335             :   {
    3336             :   case 0:
    3337        2942 :     if (!b) b = matid(n);
    3338        2942 :     return FpM_gauss_gen(a,b,p);
    3339             :   case 2:
    3340       12831 :     if (b) b = ZM_to_F2m(b); else b = matid_F2m(n);
    3341       12831 :     return F2m_gauss_sp(a,b);
    3342             :   default:
    3343       31964 :     if (b) b = ZM_to_Flm(b, *pp); else b = matid_Flm(n);
    3344       31964 :     return Flm_gauss_sp(a,b, NULL, *pp);
    3345             :   }
    3346             : }
    3347             : GEN
    3348          28 : FpM_gauss(GEN a, GEN b, GEN p)
    3349             : {
    3350          28 :   pari_sp av = avma;
    3351             :   ulong pp;
    3352             :   GEN u;
    3353          28 :   if (lg(a) == 1 || lg(b)==1) return cgetg(1, t_MAT);
    3354          28 :   u = FpM_gauss_i(a, b, p, &pp);
    3355          28 :   if (!u) { avma = av; return NULL; }
    3356          21 :   switch(pp)
    3357             :   {
    3358          21 :   case 0: return gerepilecopy(av, u);
    3359           0 :   case 2:  u = F2m_to_ZM(u); break;
    3360           0 :   default: u = Flm_to_ZM(u); break;
    3361             :   }
    3362           0 :   return gerepileupto(av, u);
    3363             : }
    3364             : GEN
    3365       47695 : FpM_inv(GEN a, GEN p)
    3366             : {
    3367       47695 :   pari_sp av = avma;
    3368             :   ulong pp;
    3369             :   GEN u;
    3370       47695 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    3371       47695 :   u = FpM_gauss_i(a, NULL, p, &pp);
    3372       47695 :   if (!u) { avma = av; return NULL; }
    3373       47688 :   switch(pp)
    3374             :   {
    3375        2893 :   case 0: return gerepilecopy(av, u);
    3376       12831 :   case 2:  u = F2m_to_ZM(u); break;
    3377       31964 :   default: u = Flm_to_ZM(u); break;
    3378             :   }
    3379       44795 :   return gerepileupto(av, u);
    3380             : }
    3381             : 
    3382             : GEN
    3383          14 : FpM_FpC_gauss(GEN a, GEN b, GEN p)
    3384             : {
    3385          14 :   pari_sp av = avma;
    3386             :   ulong pp;
    3387             :   GEN u;
    3388          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3389          14 :   u = FpM_gauss_i(a, mkmat(b), p, &pp);
    3390          14 :   if (!u) { avma = av; return NULL; }
    3391          14 :   switch(pp)
    3392             :   {
    3393          14 :   case 0: return gerepilecopy(av, gel(u,1));
    3394           0 :   case 2:  u = F2c_to_ZC(gel(u,1)); break;
    3395           0 :   default: u = Flc_to_ZC(gel(u,1)); break;
    3396             :   }
    3397           0 :   return gerepileupto(av, u);
    3398             : }
    3399             : 
    3400             : static GEN
    3401          56 : FlxqM_gauss_gen(GEN a, GEN b, GEN T, ulong p)
    3402             : {
    3403             :   void *E;
    3404          56 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    3405          56 :   return gen_Gauss(a, b, E, S);
    3406             : }
    3407             : 
    3408             : static GEN
    3409          35 : FlxqM_gauss_CUP(GEN a, GEN b, GEN T, ulong p) {
    3410             :   GEN R, C, U, P, Y;
    3411          35 :   long n = lg(a) - 1, r;
    3412          35 :   if (nbrows(a) < n || (r = FlxqM_CUP(a, &R, &C, &U, &P, T, p)) < n)
    3413          14 :     return NULL;
    3414          21 :   Y = FlxqM_rsolve_lower_unit(rowpermute(C, R),
    3415             :                               rowpermute(b, R), T, p);
    3416          21 :   return rowpermute(FlxqM_rsolve_upper(U, Y, T, p),
    3417             :                     perm_inv(P));
    3418             : }
    3419             : 
    3420             : static GEN
    3421          91 : FlxqM_gauss_i(GEN a, GEN b, GEN T, ulong p) {
    3422          91 :   if (lg(a) - 1 >= FlxqM_CUP_LIMIT)
    3423          35 :     return FlxqM_gauss_CUP(a, b, T, p);
    3424          56 :   return FlxqM_gauss_gen(a, b, T, p);
    3425             : }
    3426             : 
    3427             : GEN
    3428          21 : FlxqM_gauss(GEN a, GEN b, GEN T, ulong p)
    3429             : {
    3430          21 :   pari_sp av = avma;
    3431          21 :   long n = lg(a)-1;
    3432             :   GEN u;
    3433          21 :   if (!n || lg(b)==1) { avma = av; return cgetg(1, t_MAT); }
    3434          21 :   u = FlxqM_gauss_i(a, b, T, p);
    3435          21 :   if (!u) { avma = av; return NULL; }
    3436          14 :   return gerepilecopy(av, u);
    3437             : }
    3438             : GEN
    3439          56 : FlxqM_inv(GEN a, GEN T, ulong p)
    3440             : {
    3441          56 :   pari_sp av = avma;
    3442             :   GEN u;
    3443          56 :   if (lg(a) == 1) { avma = av; return cgetg(1, t_MAT); }
    3444          56 :   u = FlxqM_gauss_i(a, matid_FlxqM(nbrows(a),T,p), T,p);
    3445          56 :   if (!u) { avma = av; return NULL; }
    3446          42 :   return gerepilecopy(av, u);
    3447             : }
    3448             : GEN
    3449          14 : FlxqM_FlxqC_gauss(GEN a, GEN b, GEN T, ulong p)
    3450             : {
    3451          14 :   pari_sp av = avma;
    3452             :   GEN u;
    3453          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3454          14 :   u = FlxqM_gauss_i(a, mkmat(b), T, p);
    3455          14 :   if (!u) { avma = av; return NULL; }
    3456           7 :   return gerepilecopy(av, gel(u,1));
    3457             : }
    3458             : 
    3459             : static GEN
    3460         133 : FqM_gauss_gen(GEN a, GEN b, GEN T, GEN p)
    3461             : {
    3462             :   void *E;
    3463         133 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    3464         133 :   return gen_Gauss(a,b,E,S);
    3465             : }
    3466             : GEN
    3467          21 : FqM_gauss(GEN a, GEN b, GEN T, GEN p)
    3468             : {
    3469          21 :   pari_sp av = avma;
    3470             :   GEN u;
    3471             :   long n;
    3472          21 :   if (!T) return FpM_gauss(a,b,p);
    3473          21 :   n = lg(a)-1; if (!n || lg(b)==1) return cgetg(1, t_MAT);
    3474          21 :   u = FqM_gauss_gen(a,b,T,p);
    3475          21 :   if (!u) { avma = av; return NULL; }
    3476          14 :   return gerepilecopy(av, u);
    3477             : }
    3478             : GEN
    3479          98 : FqM_inv(GEN a, GEN T, GEN p)
    3480             : {
    3481          98 :   pari_sp av = avma;
    3482             :   GEN u;
    3483          98 :   if (!T) return FpM_inv(a,p);
    3484          98 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    3485          98 :   u = FqM_gauss_gen(a,matid(nbrows(a)),T,p);
    3486          98 :   if (!u) { avma = av; return NULL; }
    3487          70 :   return gerepilecopy(av, u);
    3488             : }
    3489             : GEN
    3490          14 : FqM_FqC_gauss(GEN a, GEN b, GEN T, GEN p)
    3491             : {
    3492          14 :   pari_sp av = avma;
    3493             :   GEN u;
    3494          14 :   if (!T) return FpM_FpC_gauss(a,b,p);
    3495          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    3496          14 :   u = FqM_gauss_gen(a,mkmat(b),T,p);
    3497          14 :   if (!u) { avma = av; return NULL; }
    3498           7 :   return gerepilecopy(av, gel(u,1));
    3499             : }
    3500             : 
    3501             : /* Dixon p-adic lifting algorithm.
    3502             :  * Numer. Math. 40, 137-141 (1982), DOI: 10.1007/BF01459082 */
    3503             : GEN
    3504      512737 : ZM_gauss(GEN a, GEN b0)
    3505             : {
    3506      512737 :   pari_sp av = avma, av2;
    3507             :   int iscol;
    3508             :   long n, ncol, i, m, elim;
    3509             :   ulong p;
    3510      512737 :   GEN N, C, delta, xb, nb, nmin, res, b = b0;
    3511             :   forprime_t S;
    3512             : 
    3513      512737 :   if (!init_gauss(a, &b, &n, &ncol, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    3514      512667 :   nb = gen_0; ncol = lg(b);
    3515     1037079 :   for (i = 1; i < ncol; i++)
    3516             :   {
    3517      524412 :     GEN ni = gnorml2(gel(b, i));
    3518      524412 :     if (cmpii(nb, ni) < 0) nb = ni;
    3519             :   }
    3520      512667 :   if (!signe(nb)) { avma = av; return gcopy(b0); }
    3521      512667 :   delta = gen_1; nmin = nb;
    3522     2023657 :   for (i = 1; i <= n; i++)
    3523             :   {
    3524     1510990 :     GEN ni = gnorml2(gel(a, i));
    3525     1510990 :     if (cmpii(ni, nmin) < 0)
    3526             :     {
    3527        2496 :       delta = mulii(delta, nmin); nmin = ni;
    3528             :     }
    3529             :     else
    3530     1508494 :       delta = mulii(delta, ni);
    3531             :   }
    3532      512667 :   if (!signe(nmin)) return NULL;
    3533      512653 :   elim = expi(delta)+1;
    3534      512653 :   av2 = avma;
    3535      512653 :   init_modular_big(&S);
    3536             :   for(;;)
    3537             :   {
    3538      512653 :     p = u_forprime_next(&S);
    3539      512653 :     C = Flm_inv_sp(ZM_to_Flm(a, p), NULL, p);
    3540      512653 :     if (C) break;
    3541           0 :     elim -= expu(p);
    3542           0 :     if (elim < 0) return NULL;
    3543           0 :     avma = av2;
    3544           0 :   }
    3545             :   /* N.B. Our delta/lambda are SQUARES of those in the paper
    3546             :    * log(delta lambda) / log p, where lambda is 3+sqrt(5) / 2,
    3547             :    * whose log is < 1, hence + 1 (to cater for rounding errors) */
    3548     1025306 :   m = (long)ceil((rtodbl(logr_abs(itor(delta,LOWDEFAULTPREC))) + 1)
    3549      512653 :                  / log((double)p));
    3550      512653 :   xb = ZlM_gauss(a, b, p, m, C);
    3551      512653 :   N = powuu(p, m);
    3552      512653 :   delta = sqrti(delta);
    3553      512653 :   if (iscol)
    3554      507525 :     res = FpC_ratlift(gel(xb,1), N, delta,delta, NULL);
    3555             :   else
    3556        5128 :     res = FpM_ratlift(xb, N, delta,delta, NULL);
    3557      512653 :   return gerepileupto(av, res);
    3558             : }
    3559             : 
    3560             : static GEN
    3561      128594 : ZM_inv_slice(GEN A, GEN P, GEN *mod)
    3562             : {
    3563      128594 :   pari_sp av = avma;
    3564      128594 :   long i, n = lg(P)-1;
    3565             :   GEN H, T;
    3566      128594 :   if (n == 1)
    3567             :   {
    3568      124439 :     ulong p = uel(P,1);
    3569      124439 :     GEN Hp, a = ZM_to_Flm(A, p);
    3570      124446 :     Hp = Flm_adjoint(a, p);
    3571      124442 :     Hp = gerepileupto(av, Flm_to_ZM(Hp));
    3572      124449 :     *mod = utoi(p); return Hp;
    3573             :   }
    3574        4155 :   T = ZV_producttree(P);
    3575        4155 :   A = ZM_nv_mod_tree(A, P, T);
    3576        4154 :   H = cgetg(n+1, t_VEC);
    3577       22435 :   for(i=1; i <= n; i++)
    3578       18281 :     gel(H,i) = Flm_adjoint(gel(A, i), uel(P,i));
    3579        4154 :   H = nmV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    3580        4154 :   *mod = gmael(T, lg(T)-1, 1);
    3581        4154 :   gerepileall(av, 2, &H, mod);
    3582        4155 :   return H;
    3583             : }
    3584             : 
    3585             : static GEN
    3586       98820 : RgM_true_Hadamard(GEN a)
    3587             : {
    3588       98820 :   pari_sp av = avma;
    3589       98820 :   long n = lg(a)-1, i;
    3590             :   GEN B;
    3591       98820 :   if (n == 0) return gen_1;
    3592       98820 :   if (n == 1) return gcoeff(a,1,1);
    3593       98785 :   B = gen_1; a = RgM_gtofp(a, LOWDEFAULTPREC);
    3594       98785 :   for (i = 1; i <= n; i++) B = gmul(B, gnorml2(gel(a,i)));
    3595       98785 :   return gerepileuptoint(av, ceil_safe(sqrtr_abs(B)));
    3596             : }
    3597             : 
    3598             : GEN
    3599      128600 : ZM_inv_worker(GEN P, GEN A)
    3600             : {
    3601      128600 :   GEN V = cgetg(3, t_VEC);
    3602      128597 :   gel(V,1) = ZM_inv_slice(A, P, &gel(V,2));
    3603      128604 :   return V;
    3604             : }
    3605             : 
    3606             : static GEN
    3607        4354 : ZM_inv0(GEN A, GEN *pden)
    3608             : {
    3609        4354 :   if (pden) *pden = gen_1;
    3610        4354 :   (void)A; return cgetg(1, t_MAT);
    3611             : }
    3612             : static GEN
    3613       16384 : ZM_inv1(GEN A, GEN *pden)
    3614             : {
    3615       16384 :   GEN a = gcoeff(A,1,1);
    3616       16384 :   long s = signe(a);
    3617       16384 :   if (!s) return NULL;
    3618       16384 :   if (pden) *pden = absi(a);
    3619       16384 :   retmkmat(mkcol(s == 1? gen_1: gen_m1));
    3620             : }
    3621             : static GEN
    3622       33705 : ZM_inv2(GEN A, GEN *pden)
    3623             : {
    3624             :   GEN a, b, c, d, D, cA;
    3625             :   long s;
    3626       33705 :   A = Q_primitive_part(A, &cA);
    3627       33705 :   a = gcoeff(A,1,1); b = gcoeff(A,1,2);
    3628       33705 :   c = gcoeff(A,2,1); d = gcoeff(A,2,2);
    3629       33705 :   D = subii(mulii(a,d), mulii(b,c)); /* left on stack */
    3630       33705 :   s = signe(D);
    3631       33705 :   if (!s) return NULL;
    3632       33705 :   if (s < 0) D = negi(D);
    3633       33705 :   if (pden) *pden = mul_denom(D, cA);
    3634       33705 :   if (s > 0)
    3635       15071 :     retmkmat2(mkcol2(icopy(d), negi(c)), mkcol2(negi(b), icopy(a)));
    3636             :   else
    3637       18634 :     retmkmat2(mkcol2(negi(d), icopy(c)), mkcol2(icopy(b), negi(a)));
    3638             : }
    3639             : 
    3640             : GEN
    3641      150134 : ZM_inv(GEN A, GEN *pden)
    3642             : {
    3643      150134 :   pari_sp av = avma;
    3644      150134 :   long B, m = lg(A)-1;
    3645             :   GEN d, H, D, mod, worker;
    3646             : 
    3647      150134 :   if (m == 0) return ZM_inv0(A,pden);
    3648      146473 :   if (pden) *pden = gen_1;
    3649      146473 :   if (nbrows(A) < m) return NULL;
    3650      146466 :   if (m == 1 && nbrows(A)==1) return ZM_inv1(A,pden);
    3651      131181 :   if (m == 2 && nbrows(A)==2) return ZM_inv2(A,pden);
    3652             : 
    3653       98820 :   B = expi(RgM_true_Hadamard(A));
    3654       98820 :   worker = strtoclosure("_ZM_inv_worker", 1, A);
    3655       98820 :   H = gen_crt("ZM_inv", worker, NULL, B, m, &mod, nmV_chinese_center, FpM_center);
    3656       98820 :   D = ZMrow_ZC_mul(A, gel(H,1), 1); /* det(A) */
    3657       98820 :   if (signe(D)==0) pari_err_INV("ZM_inv", A);
    3658       98813 :   d = Z_content(mkvec2(H, D));
    3659       98813 :   if (!d) d = gen_1;
    3660       98813 :   if (signe(D) < 0) d = negi(d);
    3661       98813 :   if (!equali1(d))
    3662             :   {
    3663       54081 :     H = ZM_Z_divexact(H, d);
    3664       54081 :     D = diviiexact(D, d);
    3665             :   }
    3666       98813 :   if (pden)
    3667             :   {
    3668        8281 :     gerepileall(av, 2, &H, &D);
    3669        8281 :     *pden = D; return H;
    3670             :   }
    3671       90532 :   return gerepilecopy(av, H);
    3672             : }
    3673             : 
    3674             : /* to be used when denom(M^(-1)) << det(M) and a sharp multiple is
    3675             :  * not available. Return H primitive such that M*H = den*Id */
    3676             : GEN
    3677        7224 : ZM_inv_ratlift(GEN M, GEN *pden)
    3678             : {
    3679        7224 :   pari_sp av2, av = avma;
    3680             :   GEN Hp, q, H;
    3681             :   ulong p;
    3682        7224 :   long m = lg(M)-1;
    3683             :   forprime_t S;
    3684             :   pari_timer ti;
    3685             : 
    3686        7224 :   if (m == 0) return ZM_inv0(M,pden);
    3687        6531 :   if (m == 1 && nbrows(M)==1) return ZM_inv1(M,pden);
    3688        5432 :   if (m == 2 && nbrows(M)==2) return ZM_inv2(M,pden);
    3689             : 
    3690        4088 :   if (DEBUGLEVEL>5) timer_start(&ti);
    3691        4088 :   init_modular_big(&S);
    3692        4088 :   av2 = avma;
    3693        4088 :   H = NULL;
    3694        8788 :   while ((p = u_forprime_next(&S)))
    3695             :   {
    3696             :     GEN Mp, B, Hr;
    3697        4700 :     Mp = ZM_to_Flm(M,p);
    3698        4700 :     Hp = Flm_inv_sp(Mp, NULL, p);
    3699        4700 :     if (!Hp) continue;
    3700        4700 :     if (!H)
    3701             :     {
    3702        4088 :       H = ZM_init_CRT(Hp, p);
    3703        4088 :       q = utoipos(p);
    3704             :     }
    3705             :     else
    3706         612 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3707        4700 :     B = sqrti(shifti(q,-1));
    3708        4700 :     Hr = FpM_ratlift(H,q,B,B,NULL);
    3709        4700 :     if (DEBUGLEVEL>5)
    3710           0 :       timer_printf(&ti,"ZM_inv mod %lu (ratlift=%ld)", p,!!Hr);
    3711        4700 :     if (Hr) {/* DONE ? */
    3712        4105 :       GEN Hl = Q_remove_denom(Hr, pden);
    3713        4105 :       if (ZM_isscalar(ZM_mul(M, Hl), *pden)) { H = Hl; break; }
    3714             :     }
    3715             : 
    3716         612 :     if (gc_needed(av,2))
    3717             :     {
    3718           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv_ratlift");
    3719           0 :       gerepileall(av2, 2, &H, &q);
    3720             :     }
    3721             :   }
    3722        4088 :   if (!*pden) *pden = gen_1;
    3723        4088 :   gerepileall(av, 2, &H, pden);
    3724        4088 :   return H;
    3725             : }
    3726             : 
    3727             : /* same as above, M rational */
    3728             : GEN
    3729         833 : QM_inv(GEN M)
    3730             : {
    3731         833 :   pari_sp av = avma;
    3732             :   GEN den, cM, K;
    3733         833 :   M = Q_primitive_part(M, &cM);
    3734         833 :   K = ZM_inv(M, &den);
    3735         833 :   if (!K) { avma = av; return NULL; }
    3736         833 :   cM = inv_content(mul_content(cM, den));
    3737         833 :   if (cM) K = RgM_Rg_div(K, cM);
    3738         833 :   return gerepileupto(av, K);
    3739             : }
    3740             : 
    3741             : static GEN
    3742       55042 : ZM_ker_i(GEN M, long fl)
    3743             : {
    3744       55042 :   pari_sp av2, av = avma;
    3745             :   GEN q, H, D;
    3746             :   forprime_t S;
    3747       55042 :   av2 = avma;
    3748       55042 :   H = NULL; D = NULL;
    3749       55042 :   if (lg(M)==1) return cgetg(1, t_MAT);
    3750       55035 :   init_modular_big(&S);
    3751             :   for(;;)
    3752             :   {
    3753             :     GEN Kp, Hp, Dp, Mp, Hr, B;
    3754      116900 :     ulong p = u_forprime_next(&S);
    3755      116900 :     Mp = ZM_to_Flm(M, p);
    3756      116900 :     Kp = Flm_ker_sp(Mp, p, 2);
    3757      116900 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    3758      116900 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    3759      108992 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    3760             :     {
    3761       92715 :       H = ZM_init_CRT(Hp, p); D = Dp;
    3762       92715 :       q = utoipos(p);
    3763             :     }
    3764             :     else
    3765       16277 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3766      108992 :     B = sqrti(shifti(q,-1));
    3767      108992 :     Hr = FpM_ratlift(H, q, B, B, NULL);
    3768      108992 :     if (DEBUGLEVEL>5) err_printf("ZM_ker mod %lu (ratlift=%ld)\n", p,!!Hr);
    3769      108992 :     if (Hr) {/* DONE ? */
    3770      103498 :       GEN MH = QM_mul(M, Hr);
    3771      103498 :       if (gequal0(MH)) { H = fl ? vec_Q_primpart(Hr): Hr;  break; }
    3772             :     }
    3773       53957 :     if (gc_needed(av,2))
    3774             :     {
    3775           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_ker");
    3776           0 :       gerepileall(av2, 3, &H, &D, &q);
    3777             :     }
    3778       61865 :   }
    3779       55035 :   return gerepilecopy(av, H);
    3780             : }
    3781             : 
    3782             : GEN
    3783       48518 : ZM_ker(GEN M)
    3784       48518 : { return ZM_ker_i(M, 1); }
    3785             : 
    3786             : GEN
    3787        7735 : QM_ker(GEN M)
    3788             : {
    3789        7735 :   pari_sp av = avma;
    3790        7735 :   long l = lg(M)-1;
    3791        7735 :   if (l==0) return cgetg(1, t_MAT);
    3792        7700 :   if (lgcols(M)==1) return matid(l);
    3793        6524 :   M = shallowtrans(vec_Q_primpart(shallowtrans(M)));
    3794        6524 :   return gerepileupto(av, ZM_ker_i(M, 0));
    3795             : }
    3796             : 
    3797             : /* x a ZM. Return a multiple of the determinant of the lattice generated by
    3798             :  * the columns of x. From Algorithm 2.2.6 in GTM138 */
    3799             : GEN
    3800       47521 : detint(GEN A)
    3801             : {
    3802       47521 :   if (typ(A) != t_MAT) pari_err_TYPE("detint",A);
    3803       47521 :   RgM_check_ZM(A, "detint");
    3804       47521 :   return ZM_detmult(A);
    3805             : }
    3806             : GEN
    3807       77649 : ZM_detmult(GEN A)
    3808             : {
    3809       77649 :   pari_sp av1, av = avma;
    3810             :   GEN B, c, v, piv;
    3811       77649 :   long rg, i, j, k, m, n = lg(A) - 1;
    3812             : 
    3813       77649 :   if (!n) return gen_1;
    3814       77649 :   m = nbrows(A);
    3815       77649 :   if (n < m) return gen_0;
    3816       77628 :   c = zero_zv(m);
    3817       77628 :   av1 = avma;
    3818       77628 :   B = zeromatcopy(m,m);
    3819       77628 :   v = cgetg(m+1, t_COL);
    3820       77628 :   piv = gen_1; rg = 0;
    3821      478224 :   for (k=1; k<=n; k++)
    3822             :   {
    3823      478210 :     GEN pivprec = piv;
    3824      478210 :     long t = 0;
    3825     4017849 :     for (i=1; i<=m; i++)
    3826             :     {
    3827     3539639 :       pari_sp av2 = avma;
    3828             :       GEN vi;
    3829     3539639 :       if (c[i]) continue;
    3830             : 
    3831     2009173 :       vi = mulii(piv, gcoeff(A,i,k));
    3832    17734923 :       for (j=1; j<=m; j++)
    3833    15725750 :         if (c[j]) vi = addii(vi, mulii(gcoeff(B,j,i),gcoeff(A,j,k)));
    3834     2009173 :       if (!t && signe(vi)) t = i;
    3835     2009173 :       gel(v,i) = gerepileuptoint(av2, vi);
    3836             :     }
    3837      478210 :     if (!t) continue;
    3838             :     /* at this point c[t] = 0 */
    3839             : 
    3840      478126 :     if (++rg >= m) { /* full rank; mostly done */
    3841       77614 :       GEN det = gel(v,t); /* last on stack */
    3842       77614 :       if (++k > n)
    3843       77538 :         det = absi(det);
    3844             :       else
    3845             :       {
    3846             :         /* improve further; at this point c[i] is set for all i != t */
    3847          76 :         gcoeff(B,t,t) = piv; v = centermod(gel(B,t), det);
    3848         320 :         for ( ; k<=n; k++)
    3849         244 :           det = gcdii(det, ZV_dotproduct(v, gel(A,k)));
    3850             :       }
    3851       77614 :       return gerepileuptoint(av, det);
    3852             :     }
    3853             : 
    3854      400512 :     piv = gel(v,t);
    3855     3461542 :     for (i=1; i<=m; i++)
    3856             :     {
    3857             :       GEN mvi;
    3858     3061030 :       if (c[i] || i == t) continue;
    3859             : 
    3860     1530515 :       gcoeff(B,t,i) = mvi = negi(gel(v,i));
    3861    13713084 :       for (j=1; j<=m; j++)
    3862    12182569 :         if (c[j]) /* implies j != t */
    3863             :         {
    3864     3040513 :           pari_sp av2 = avma;
    3865     3040513 :           GEN z = addii(mulii(gcoeff(B,j,i), piv), mulii(gcoeff(B,j,t), mvi));
    3866     3040513 :           if (rg > 1) z = diviiexact(z, pivprec);
    3867     3040513 :           gcoeff(B,j,i) = gerepileuptoint(av2, z);
    3868             :         }
    3869             :     }
    3870      400512 :     c[t] = k;
    3871      400512 :     if (gc_needed(av,1))
    3872             :     {
    3873           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"detint. k=%ld",k);
    3874           0 :       gerepileall(av1, 2, &piv,&B); v = zerovec(m);
    3875             :     }
    3876             :   }
    3877          14 :   avma = av; return gen_0;
    3878             : }
    3879             : 
    3880             : /* Reduce x modulo (invertible) y */
    3881             : GEN
    3882       12971 : closemodinvertible(GEN x, GEN y)
    3883             : {
    3884       12971 :   return gmul(y, ground(RgM_solve(y,x)));
    3885             : }
    3886             : GEN
    3887           7 : reducemodinvertible(GEN x, GEN y)
    3888             : {
    3889           7 :   return gsub(x, closemodinvertible(x,y));
    3890             : }
    3891             : GEN
    3892           0 : reducemodlll(GEN x,GEN y)
    3893             : {
    3894           0 :   return reducemodinvertible(x, ZM_lll(y, 0.75, LLL_INPLACE));
    3895             : }
    3896             : 
    3897             : /*******************************************************************/
    3898             : /*                                                                 */
    3899             : /*                    KERNEL of an m x n matrix                    */
    3900             : /*          return n - rk(x) linearly independent vectors          */
    3901             : /*                                                                 */
    3902             : /*******************************************************************/
    3903             : static GEN
    3904         182 : deplin_aux(GEN x0)
    3905             : {
    3906         182 :   pari_sp av = avma;
    3907         182 :   long i, j, k, nl, nc = lg(x0)-1;
    3908             :   GEN D, x, y, c, l, d, ck;
    3909             : 
    3910         182 :   if (!nc) { avma=av; return cgetg(1,t_COL); }
    3911         147 :   x = RgM_shallowcopy(x0);
    3912         147 :   nl = nbrows(x);
    3913         147 :   d = const_vec(nl, gen_1); /* pivot list */
    3914         147 :   c = zero_zv(nl);
    3915         147 :   l = cgetg(nc+1, t_VECSMALL); /* not initialized */
    3916         147 :   ck = NULL; /* gcc -Wall */
    3917         791 :   for (k=1; k<=nc; k++)
    3918             :   {
    3919         756 :     ck = gel(x,k);
    3920        3570 :     for (j=1; j<k; j++)
    3921             :     {
    3922        2814 :       GEN cj = gel(x,j), piv = gel(d,j), q = gel(ck,l[j]);
    3923       56196 :       for (i=1; i<=nl; i++)
    3924       53382 :         if (i!=l[j]) gel(ck,i) = gsub(gmul(piv, gel(ck,i)), gmul(q, gel(cj,i)));
    3925             :     }
    3926             : 
    3927         756 :     i = gauss_get_pivot_NZ(x, NULL, k, c);
    3928         756 :     if (i > nl) break;
    3929             : 
    3930         644 :     gel(d,k) = gel(ck,i);
    3931         644 :     c[i] = k; l[k] = i; /* pivot d[k] in x[i,k] */
    3932             :   }
    3933         147 :   if (k > nc) { avma = av; return cgetg(1,t_COL); }
    3934         112 :   if (k == 1) { avma = av; return scalarcol_shallow(gen_1,nc); }
    3935          84 :   y = cgetg(nc+1,t_COL);
    3936          84 :   gel(y,1) = gcopy(gel(ck, l[1]));
    3937         371 :   for (D=gel(d,1),j=2; j<k; j++)
    3938             :   {
    3939         287 :     gel(y,j) = gmul(gel(ck, l[j]), D);
    3940         287 :     D = gmul(D, gel(d,j));
    3941             :   }
    3942          84 :   gel(y,j) = gneg(D);
    3943          84 :   for (j++; j<=nc; j++) gel(y,j) = gen_0;
    3944          84 :   y = primitive_part(y, &c);
    3945          84 :   return c? gerepileupto(av, y): gerepilecopy(av, y);
    3946             : }
    3947             : static GEN
    3948           0 : RgV_deplin(GEN v)
    3949             : {
    3950           0 :   pari_sp av = avma;
    3951           0 :   long n = lg(v)-1;
    3952           0 :   GEN y, p = NULL;
    3953           0 :   if (n <= 1)
    3954             :   {
    3955           0 :     if (n == 1 && gequal0(gel(v,1))) return mkcol(gen_1);
    3956           0 :     return cgetg(1, t_COL);
    3957             :   }
    3958           0 :   if (gequal0(gel(v,1))) return scalarcol_shallow(gen_1, n);
    3959           0 :   v = primpart(mkvec2(gel(v,1),gel(v,2)));
    3960           0 :   if (RgV_is_FpV(v, &p) && p) v = centerlift(v);
    3961           0 :   y = zerocol(n);
    3962           0 :   gel(y,1) = gneg(gel(v,2));
    3963           0 :   gel(y,2) = gcopy(gel(v,1));
    3964           0 :   return gerepileupto(av, y);
    3965             : 
    3966             : }
    3967             : GEN
    3968         357 : deplin(GEN x)
    3969             : {
    3970         357 :   GEN p = NULL, ff = NULL;
    3971         357 :   switch(typ(x))
    3972             :   {
    3973         357 :     case t_MAT: break;
    3974           0 :     case t_VEC: return RgV_deplin(x);
    3975           0 :     default: pari_err_TYPE("deplin",x);
    3976             :   }
    3977         357 :   if (RgM_is_FpM(x, &p) && p)
    3978             :   {
    3979         105 :     pari_sp av = avma;
    3980             :     ulong pp;
    3981         105 :     x = RgM_Fp_init(x, p, &pp);
    3982         105 :     switch(pp)
    3983             :     {
    3984             :     case 0:
    3985          35 :       x = FpM_ker_gen(x,p,1);
    3986          35 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3987          21 :       x = FpC_center(x,p,shifti(p,-1));
    3988          21 :       break;
    3989             :     case 2:
    3990          14 :       x = F2m_ker_sp(x,1);
    3991          14 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3992           7 :       x = F2c_to_ZC(x); break;
    3993             :     default:
    3994          56 :       x = Flm_ker_sp(x,pp,1);
    3995          56 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3996          35 :       x = Flv_center(x, pp, pp>>1);
    3997          35 :       x = zc_to_ZC(x);
    3998          35 :       break;
    3999             :     }
    4000          63 :     return gerepileupto(av, x);
    4001             :   }
    4002         252 :   if (RgM_is_FFM(x, &ff))
    4003             :   {
    4004          70 :     x = FFM_deplin(x, ff);
    4005          70 :     if (!x) return cgetg(1, t_COL);
    4006          35 :     return x;
    4007             :   }
    4008         182 :   return deplin_aux(x);
    4009             : }
    4010             : 
    4011             : /*******************************************************************/
    4012             : /*                                                                 */
    4013             : /*         GAUSS REDUCTION OF MATRICES  (m lines x n cols)         */
    4014             : /*           (kernel, image, complementary image, rank)            */
    4015             : /*                                                                 */
    4016             : /*******************************************************************/
    4017             : /* return the transform of x under a standard Gauss pivot.
    4018             :  * x0 is a reference point when guessing whether x[i,j] ~ 0
    4019             :  * (iff x[i,j] << x0[i,j])
    4020             :  * Set r = dim ker(x). d[k] contains the index of the first non-zero pivot
    4021             :  * in column k */
    4022             : static GEN
    4023         854 : gauss_pivot_ker(GEN x, GEN x0, GEN *dd, long *rr)
    4024             : {
    4025             :   GEN c, d, p, data;
    4026             :   pari_sp av;
    4027             :   long i, j, k, r, t, n, m;
    4028             :   pivot_fun pivot;
    4029             : 
    4030         854 :   n=lg(x)-1; if (!n) { *dd=NULL; *rr=0; return cgetg(1,t_MAT); }
    4031         854 :   m=nbrows(x); r=0;
    4032         854 :   pivot = get_pivot_fun(x, x0, &data);
    4033         854 :   x = RgM_shallowcopy(x);
    4034         854 :   c = zero_zv(m);
    4035         854 :   d = cgetg(n+1,t_VECSMALL);
    4036         854 :   av=avma;
    4037        4977 :   for (k=1; k<=n; k++)
    4038             :   {
    4039        4123 :     j = pivot(x, data, k, c);
    4040        4123 :     if (j > m)
    4041             :     {
    4042         903 :       r++; d[k]=0;
    4043        4081 :       for(j=1; j<k; j++)
    4044        3178 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
    4045             :     }
    4046             :     else
    4047             :     { /* pivot for column k on row j */
    4048        3220 :       c[j]=k; d[k]=j; p = gdiv(gen_m1,gcoeff(x,j,k));
    4049        3220 :       gcoeff(x,j,k) = gen_m1;
    4050             :       /* x[j,] /= - x[j,k] */
    4051        3220 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    4052       32998 :       for (t=1; t<=m; t++)
    4053       29778 :         if (t!=j)
    4054             :         { /* x[t,] -= 1 / x[j,k] x[j,] */
    4055       26558 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    4056      223706 :           for (i=k+1; i<=n; i++)
    4057      197148 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i),gmul(p,gcoeff(x,j,i)));
    4058       26558 :           if (gc_needed(av,1)) gerepile_gauss_ker(x,k,t,av);
    4059             :         }
    4060             :     }
    4061             :   }
    4062         854 :   *dd=d; *rr=r; return x;
    4063             : }
    4064             : 
    4065             : static GEN FpM_gauss_pivot(GEN x, GEN p, long *rr);
    4066             : static GEN FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr);
    4067             : static GEN F2m_gauss_pivot(GEN x, long *rr);
    4068             : 
    4069             : /* r = dim ker(x).
    4070             :  * Returns d:
    4071             :  *   d[k] != 0 contains the index of a non-zero pivot in column k
    4072             :  *   d[k] == 0 if column k is a linear combination of the (k-1) first ones */
    4073             : GEN
    4074       25894 : RgM_pivots(GEN x0, GEN data, long *rr, pivot_fun pivot)
    4075             : {
    4076             :   GEN x, c, d, p;
    4077       25894 :   long i, j, k, r, t, m, n = lg(x0)-1;
    4078             :   pari_sp av;
    4079             : 
    4080       25894 :   if (RgM_is_ZM(x0)) return ZM_pivots(x0, rr);
    4081       23871 :   if (!n) { *rr = 0; return NULL; }
    4082             : 
    4083       23871 :   d = cgetg(n+1, t_VECSMALL);
    4084       23871 :   x = RgM_shallowcopy(x0);
    4085       23871 :   m = nbrows(x); r = 0;
    4086       23871 :   c = zero_zv(m);
    4087       23871 :   av = avma;
    4088      914452 :   for (k=1; k<=n; k++)
    4089             :   {
    4090      890581 :     j = pivot(x, data, k, c);
    4091      890581 :     if (j > m) { r++; d[k] = 0; }
    4092             :     else
    4093             :     {
    4094       44424 :       c[j] = k; d[k] = j; p = gdiv(gen_m1, gcoeff(x,j,k));
    4095       44424 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    4096             : 
    4097      241074 :       for (t=1; t<=m; t++)
    4098      196650 :         if (!c[t]) /* no pivot on that line yet */
    4099             :         {
    4100       97588 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    4101     5933628 :           for (i=k+1; i<=n; i++)
    4102     5836040 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i), gmul(p, gcoeff(x,j,i)));
    4103       97588 :           if (gc_needed(av,1)) gerepile_gauss(x,k,t,av,j,c);
    4104             :         }
    4105       44424 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = gen_0; /* dummy */
    4106             :     }
    4107             :   }
    4108       23871 :   *rr = r; avma = (pari_sp)d; return d;
    4109             : }
    4110             : 
    4111             : static long
    4112      100258 : ZM_count_0_cols(GEN M)
    4113             : {
    4114      100258 :   long i, l = lg(M), n = 0;
    4115      528931 :   for (i = 1; i < l; i++)
    4116      428673 :     if (ZV_equal0(gel(M,i))) n++;
    4117      100258 :   return n;
    4118             : }
    4119             : 
    4120             : static void indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol);
    4121             : /* As RgM_pivots, integer entries. Set *rr = dim Ker M0 */
    4122             : GEN
    4123      104001 : ZM_pivots(GEN M0, long *rr)
    4124             : {
    4125      104001 :   GEN d, dbest = NULL;
    4126             :   long m, n, i, imax, rmin, rbest, zc;
    4127      104001 :   int beenthere = 0;
    4128      104001 :   pari_sp av, av0 = avma;
    4129             :   forprime_t S;
    4130             : 
    4131      104001 :   rbest = n = lg(M0)-1;
    4132      104001 :   if (n == 0) { *rr = 0; return NULL; }
    4133      100258 :   zc = ZM_count_0_cols(M0);
    4134      100258 :   if (n == zc) { *rr = zc; return zero_zv(n); }
    4135             : 
    4136       99915 :   m = nbrows(M0);
    4137       99915 :   rmin = maxss(zc, n-m);
    4138       99915 :   init_modular(&S);
    4139       99915 :   imax = (n < (1<<4))? 1: (n>>3); /* heuristic */
    4140             : 
    4141             :   for(;;)
    4142             :   {
    4143             :     GEN row, col, M, KM, IM, RHS, X, cX;
    4144             :     long rk;
    4145      106254 :     for (av = avma, i = 0;; avma = av, i++)
    4146             :     {
    4147      106254 :       ulong p = u_forprime_next(&S);
    4148             :       long rp;
    4149      106254 :       if (!p) pari_err_OVERFLOW("ZM_pivots [ran out of primes]");
    4150      106254 :       d = Flm_pivots(ZM_to_Flm(M0, p), p, &rp, 1);
    4151      106254 :       if (rp == rmin) { rbest = rp; goto END; } /* maximal rank, return */
    4152       11467 :       if (rp < rbest) { /* save best r so far */
    4153        5135 :         rbest = rp;
    4154        5135 :         if (dbest) gunclone(dbest);
    4155        5135 :         dbest = gclone(d);
    4156       10263 :         if (beenthere) break;
    4157             :       }
    4158       11467 :       if (!beenthere && i >= imax) break;
    4159        6339 :     }
    4160        5128 :     beenthere = 1;
    4161             :     /* Dubious case: there is (probably) a non trivial kernel */
    4162        5128 :     indexrank_all(m,n, rbest, dbest, &row, &col);
    4163        5128 :     M = rowpermute(vecpermute(M0, col), row);
    4164        5128 :     rk = n - rbest; /* (probable) dimension of image */
    4165        5128 :     IM = vecslice(M,1,rk);
    4166        5128 :     KM = vecslice(M,rk+1, n);
    4167        5128 :     M = rowslice(IM, 1,rk); /* square maximal rank */
    4168        5128 :     X = ZM_gauss(M, rowslice(KM, 1,rk));
    4169        5128 :     X = Q_remove_denom(X, &cX);
    4170        5128 :     RHS = rowslice(KM,rk+1,m);
    4171        5128 :     if (cX) RHS = ZM_Z_mul(RHS, cX);
    4172        5128 :     if (ZM_equal(ZM_mul(rowslice(IM,rk+1,m), X), RHS))
    4173             :     {
    4174        5128 :       d = vecsmall_copy(dbest);
    4175        5128 :       goto END;
    4176             :     }
    4177           0 :     avma = av;
    4178           0 :   }
    4179             : END:
    4180       99915 :   *rr = rbest; if (dbest) gunclone(dbest);
    4181       99915 :   return gerepileuptoleaf(av0, d);
    4182             : }
    4183             : 
    4184             : /* set *pr = dim Ker x */
    4185             : static GEN
    4186       15876 : gauss_pivot(GEN x, long *pr) {
    4187             :   GEN data;
    4188       15876 :   pivot_fun pivot = get_pivot_fun(x, x, &data);
    4189       15876 :   return RgM_pivots(x, data, pr, pivot);
    4190             : }
    4191             : 
    4192             : /* compute ker(x), x0 is a reference point when guessing whether x[i,j] ~ 0
    4193             :  * (iff x[i,j] << x0[i,j]) */
    4194             : static GEN
    4195         854 : ker_aux(GEN x, GEN x0)
    4196             : {
    4197         854 :   pari_sp av = avma;
    4198             :   GEN d,y;
    4199             :   long i,j,k,r,n;
    4200             : 
    4201         854 :   x = gauss_pivot_ker(x,x0,&d,&r);
    4202         854 :   if (!r) { avma=av; return cgetg(1,t_MAT); }
    4203         847 :   n = lg(x)-1; y=cgetg(r+1,t_MAT);
    4204        1750 :   for (j=k=1; j<=r; j++,k++)
    4205             :   {
    4206         903 :     GEN p = cgetg(n+1,t_COL);
    4207             : 
    4208         903 :     gel(y,j) = p; while (d[k]) k++;
    4209        4081 :     for (i=1; i<k; i++)
    4210        3178 :       if (d[i])
    4211             :       {
    4212        3080 :         GEN p1=gcoeff(x,d[i],k);
    4213        3080 :         gel(p,i) = gcopy(p1); gunclone(p1);
    4214             :       }
    4215             :       else
    4216          98 :         gel(p,i) = gen_0;
    4217         903 :     gel(p,k) = gen_1; for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
    4218             :   }
    4219         847 :   return gerepileupto(av,y);
    4220             : }
    4221             : 
    4222             : static GEN
    4223          77 : RgM_ker_FpM(GEN x, GEN p)
    4224             : {
    4225          77 :   pari_sp av = avma;
    4226             :   ulong pp;
    4227          77 :   x = RgM_Fp_init(x, p, &pp);
    4228          77 :   switch(pp)
    4229             :   {
    4230          35 :     case 0: x = FpM_to_mod(FpM_ker_gen(x,p,0),p); break;
    4231           7 :     case 2: x = F2m_to_mod(F2m_ker_sp(x,0)); break;
    4232          35 :     default:x = Flm_to_mod(Flm_ker_sp(x,pp,0), pp); break;
    4233             :   }
    4234          77 :   return gerepileupto(av, x);
    4235             : }
    4236             : 
    4237             : static GEN
    4238          84 : RgM_ker_FqM(GEN x, GEN pol, GEN p)
    4239             : {
    4240          84 :   pari_sp av = avma;
    4241          84 :   GEN T = RgX_to_FpX(pol, p);
    4242          84 :   GEN b = FqM_ker(RgM_to_FqM(x, T, p), T, p);
    4243          84 :   return gerepileupto(av, FqM_to_mod(b, T, p));
    4244             : }
    4245             : 
    4246             : #define code(t1,t2) ((t1 << 6) | t2)
    4247             : static GEN
    4248        8743 : RgM_ker_fast(GEN x)
    4249             : {
    4250             :   GEN p, pol;
    4251             :   long pa;
    4252        8743 :   long t = RgM_type(x, &p,&pol,&pa);
    4253        8743 :   switch(t)
    4254             :   {
    4255             :     case t_INT:    /* fall through */
    4256        7735 :     case t_FRAC:   return QM_ker(x);
    4257         147 :     case t_FFELT:  return FFM_ker(x, pol);
    4258          77 :     case t_INTMOD: return RgM_ker_FpM(x, p);
    4259             :     case code(t_POLMOD, t_INTMOD):
    4260          84 :                    return RgM_ker_FqM(x, pol, p);
    4261         700 :     default:       return NULL;
    4262             :   }
    4263             : }
    4264             : #undef code
    4265             : 
    4266             : GEN
    4267        8743 : ker(GEN x)
    4268             : {
    4269        8743 :   GEN b = RgM_ker_fast(x);
    4270        8743 :   if (b) return b;
    4271         700 :   return ker_aux(x,x);
    4272             : }
    4273             : 
    4274             : GEN
    4275       46207 : matker0(GEN x,long flag)
    4276             : {
    4277       46207 :   if (typ(x)!=t_MAT) pari_err_TYPE("matker",x);
    4278       46207 :   if (!flag) return ker(x);
    4279       45934 :   RgM_check_ZM(x, "matker");
    4280       45934 :   return ZM_ker(x);
    4281             : }
    4282             : 
    4283             : GEN
    4284        1561 : image(GEN x)
    4285             : {
    4286        1561 :   pari_sp av = avma;
    4287        1561 :   GEN d, ff = NULL, p = NULL;
    4288             :   long r;
    4289             : 
    4290        1561 :   if (typ(x)!=t_MAT) pari_err_TYPE("matimage",x);
    4291        1561 :   if (RgM_is_FpM(x, &p) && p)
    4292             :   {
    4293             :     ulong pp;
    4294          63 :     x = RgM_Fp_init(x, p, &pp);
    4295          63 :     switch(pp)
    4296             :     {
    4297          28 :     case 0: x = FpM_to_mod(FpM_image(x,p), p); break;
    4298           7 :     case 2: x = F2m_to_mod(F2m_image(x)); break;
    4299          28 :     default:x = Flm_to_mod(Flm_image(x,pp), pp);
    4300             :     }
    4301          63 :     return gerepileupto(av, x);
    4302             :   }
    4303        1498 :   if (RgM_is_FFM(x, &ff)) return FFM_image(x, ff);
    4304        1449 :   d = gauss_pivot(x,&r); /* d left on stack for efficiency */
    4305        1449 :   return image_from_pivot(x,d,r);
    4306             : }
    4307             : 
    4308             : static GEN
    4309          84 : imagecompl_aux(GEN x, GEN(*PIVOT)(GEN,long*))
    4310             : {
    4311          84 :   pari_sp av = avma;
    4312             :   GEN d,y;
    4313             :   long j,i,r;
    4314             : 
    4315          84 :   if (typ(x)!=t_MAT) pari_err_TYPE("imagecompl",x);
    4316          84 :   (void)new_chunk(lg(x) * 4 + 1); /* HACK */
    4317          84 :   d = PIVOT(x,&r); /* if (!d) then r = 0 */
    4318          84 :   avma = av; y = cgetg(r+1,t_VECSMALL);
    4319         126 :   for (i=j=1; j<=r; i++)
    4320          42 :     if (!d[i]) y[j++] = i;
    4321          84 :   return y;
    4322             : }
    4323             : GEN
    4324          84 : imagecompl(GEN x) { return imagecompl_aux(x, &gauss_pivot); }
    4325             : GEN
    4326           0 : ZM_imagecompl(GEN x) { return imagecompl_aux(x, &ZM_pivots); }
    4327             : 
    4328             : GEN
    4329        1967 : RgM_RgC_invimage(GEN A, GEN y)
    4330             : {
    4331        1967 :   pari_sp av = avma;
    4332        1967 :   long i, l = lg(A);
    4333        1967 :   GEN M, x, t, p = NULL;
    4334             : 
    4335        1967 :   if (RgM_is_FpM(A, &p) && RgV_is_FpV(y, &p) && p)
    4336             :   {
    4337             :     ulong pp;
    4338          28 :     A = RgM_Fp_init(A,p,&pp);
    4339          28 :     switch(pp)
    4340             :     {
    4341             :     case 0:
    4342           7 :       y = RgC_to_FpC(y,p);
    4343           7 :       x = FpM_FpC_invimage(A, y, p);
    4344           7 :       if (x) x = FpC_to_mod(x,p);
    4345           7 :       break;
    4346             :     case 2:
    4347           7 :       y = RgV_to_F2v(y);
    4348           7 :       x = F2m_F2c_invimage(A, y);
    4349           7 :       if (x) x = F2c_to_mod(x);
    4350           7 :       break;
    4351             :     default:
    4352          14 :       y = RgV_to_Flv(y,pp);
    4353          14 :       x = Flm_Flc_invimage(A, y, pp);
    4354          14 :       if (x) x = Flc_to_mod(x,pp);
    4355             :     }
    4356          28 :     if (!x) { avma = av; return NULL; }
    4357          28 :     return gerepileupto(av, x);
    4358             :   }
    4359             : 
    4360        1939 :   if (l==1) return NULL;
    4361        1743 :   if (lg(y) != lgcols(A)) pari_err_DIM("inverseimage");
    4362             :   {
    4363        1743 :     GEN ff = NULL;
    4364        1743 :     if (RgM_is_FFM(A, &ff) && RgC_is_FFC(y, &ff))
    4365          63 :       return FFM_FFC_invimage(A, y, ff);
    4366             :   }
    4367        1680 :   M = ker(shallowconcat(A, y));
    4368        1680 :   i = lg(M)-1;
    4369        1680 :   if (!i) { avma = av; return NULL; }
    4370             : 
    4371        1421 :   x = gel(M,i); t = gel(x,l);
    4372        1421 :   if (gequal0(t)) { avma = av; return NULL; }
    4373             : 
    4374        1393 :   t = gneg_i(t); setlg(x,l);
    4375        1393 :   return gerepileupto(av, RgC_Rg_div(x, t));
    4376             : }
    4377             : GEN
    4378       41834 : FpM_FpC_invimage(GEN A, GEN y, GEN p)
    4379             : {
    4380       41834 :   pari_sp av = avma;
    4381       41834 :   long i, l = lg(A);
    4382             :   GEN M, x, t;
    4383             : 
    4384       41834 :   if (lgefint(p) == 3)
    4385             :   {
    4386       41827 :     ulong pp = p[2];
    4387       41827 :     A = ZM_to_Flm(A, pp);
    4388       41827 :     y = ZV_to_Flv(y, pp);
    4389       41827 :     x = Flm_Flc_invimage(A,y,pp);
    4390       41827 :     if (!x) { avma = av; return NULL; }
    4391       41827 :     return gerepileupto(av, Flc_to_ZC(x));
    4392             :   }
    4393           7 :   if (l==1) return NULL;
    4394           7 :   if (lg(y) != lgcols(A)) pari_err_DIM("FpM_FpC_invimage");
    4395           7 :   M = FpM_ker(shallowconcat(A,y),p);
    4396           7 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4397             : 
    4398           7 :   x = gel(M,i); t = gel(x,l);
    4399           7 :   if (!signe(t)) { avma = av; return NULL; }
    4400             : 
    4401           7 :   setlg(x,l); t = Fp_inv(negi(t),p);
    4402           7 :   if (is_pm1(t)) return gerepilecopy(av, x);
    4403           7 :   return gerepileupto(av, FpC_Fp_mul(x, t, p));
    4404             : }
    4405             : GEN
    4406       48029 : Flm_Flc_invimage(GEN A, GEN y, ulong p)
    4407             : {
    4408       48029 :   pari_sp av = avma;
    4409       48029 :   long i, l = lg(A);
    4410             :   GEN M, x;
    4411             :   ulong t;
    4412             : 
    4413       48029 :   if (l==1) return NULL;
    4414       48029 :   if (lg(y) != lgcols(A)) pari_err_DIM("Flm_Flc_invimage");
    4415       48029 :   M = cgetg(l+1,t_MAT);
    4416       48029 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4417       48029 :   gel(M,l) = y; M = Flm_ker(M,p);
    4418       48029 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4419             : 
    4420       48029 :   x = gel(M,i); t = x[l];
    4421       48029 :   if (!t) { avma = av; return NULL; }
    4422             : 
    4423       48029 :   setlg(x,l); t = Fl_inv(Fl_neg(t,p),p);
    4424       48029 :   if (t!=1) x = Flv_Fl_mul(x, t, p);
    4425       48029 :   return gerepileuptoleaf(av, x);
    4426             : }
    4427             : GEN
    4428          21 : F2m_F2c_invimage(GEN A, GEN y)
    4429             : {
    4430          21 :   pari_sp av = avma;
    4431          21 :   long i, l = lg(A);
    4432             :   GEN M, x;
    4433             : 
    4434          21 :   if (l==1) return NULL;
    4435          21 :   if (lg(y) != lgcols(A)) pari_err_DIM("F2m_F2c_invimage");
    4436          21 :   M = cgetg(l+1,t_MAT);
    4437          21 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4438          21 :   gel(M,l) = y; M = F2m_ker(M);
    4439          21 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4440             : 
    4441          21 :   x = gel(M,i);
    4442          21 :   if (!F2v_coeff(x,l)) { avma = av; return NULL; }
    4443          21 :   F2v_clear(x, x[1]); x[1]--; /* remove last coord */
    4444          21 :   return gerepileuptoleaf(av, x);
    4445             : }
    4446             : 
    4447             : /* Return X such that m X = v (t_COL or t_MAT), resp. an empty t_COL / t_MAT
    4448             :  * if no solution exist */
    4449             : GEN
    4450        2184 : inverseimage(GEN m, GEN v)
    4451             : {
    4452             :   GEN y;
    4453        2184 :   if (typ(m)!=t_MAT) pari_err_TYPE("inverseimage",m);
    4454        2184 :   switch(typ(v))
    4455             :   {
    4456             :     case t_COL:
    4457        1946 :       y = RgM_RgC_invimage(m,v);
    4458        1946 :       return y? y: cgetg(1,t_COL);
    4459             :     case t_MAT:
    4460         238 :       y = RgM_invimage(m, v);
    4461         238 :       return y? y: cgetg(1,t_MAT);
    4462             :   }
    4463           0 :   pari_err_TYPE("inverseimage",v);
    4464             :   return NULL;/*LCOV_EXCL_LINE*/
    4465             : }
    4466             : 
    4467             : static GEN
    4468          21 : Flm_invimage_CUP(GEN A, GEN B, ulong p) {
    4469          21 :   pari_sp av = avma;
    4470             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    4471             :   long r;
    4472          21 :   r = Flm_CUP(A, &R, &C, &U, &P, p);
    4473          21 :   Rc = indexcompl(R, nbrows(B));
    4474          21 :   C1 = rowpermute(C, R);
    4475          21 :   C2 = rowpermute(C, Rc);
    4476          21 :   B1 = rowpermute(B, R);
    4477          21 :   B2 = rowpermute(B, Rc);
    4478          21 :   Z = Flm_rsolve_lower_unit(C1, B1, p);
    4479          21 :   if (!gequal(Flm_mul(C2, Z, p), B2))
    4480          14 :     return NULL;
    4481          14 :   Y = vconcat(Flm_rsolve_upper(vecslice(U, 1, r), Z, p),
    4482          14 :               zero_Flm(lg(A) - 1 - r, lg(B) - 1));
    4483           7 :   X = rowpermute(Y, perm_inv(P));
    4484           7 :   return gerepileupto(av, X);
    4485             : }
    4486             : 
    4487             : static GEN
    4488          42 : Flm_invimage_i(GEN A, GEN B, ulong p)
    4489             : {
    4490             :   GEN d, x, X, Y;
    4491          42 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4492             : 
    4493          42 :   if (!nB) return cgetg(1, t_MAT);
    4494          42 :   if (nA + nB >= Flm_CUP_LIMIT && nbrows(B) >= Flm_CUP_LIMIT)
    4495          21 :     return Flm_invimage_CUP(A, B, p);
    4496             : 
    4497          21 :   x = Flm_ker_sp(shallowconcat(Flm_neg(A,p), B), p, 0);
    4498             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4499             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4500             :    * Y has at least nB columns and full rank */
    4501          21 :   nY = lg(x)-1;
    4502          21 :   if (nY < nB) return NULL;
    4503          21 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4504          21 :   d = cgetg(nB+1, t_VECSMALL);
    4505          56 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4506             :   {
    4507          49 :     for (; j>=1; j--)
    4508          42 :       if (coeff(Y,i,j)) { d[i] = j; break; }
    4509          42 :     if (!j) return NULL;
    4510             :   }
    4511             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4512          14 :   Y = vecpermute(Y, d);
    4513          14 :   x = vecpermute(x, d);
    4514          14 :   X = rowslice(x, 1, nA);
    4515          14 :   return Flm_mul(X, Flm_inv_upper_1(Y,p), p);
    4516             : }
    4517             : 
    4518             : static GEN
    4519           7 : F2m_invimage_i(GEN A, GEN B)
    4520             : {
    4521             :   GEN d, x, X, Y;
    4522           7 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4523           7 :   x = F2m_ker_sp(shallowconcat(A, B), 0);
    4524             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4525             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4526             :    * Y has at least nB columns and full rank */
    4527           7 :   nY = lg(x)-1;
    4528           7 :   if (nY < nB) return NULL;
    4529             : 
    4530             :   /* implicitly: Y = rowslice(x, nA+1, nA+nB), nB rows */
    4531           7 :   d = cgetg(nB+1, t_VECSMALL);
    4532          21 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4533             :   {
    4534          14 :     for (; j>=1; j--)
    4535          14 :       if (F2m_coeff(x,nA+i,j)) { d[i] = j; break; } /* Y[i,j] */
    4536          14 :     if (!j) return NULL;
    4537             :   }
    4538           7 :   x = vecpermute(x, d);
    4539             : 
    4540           7 :   X = F2m_rowslice(x, 1, nA);
    4541           7 :   Y = F2m_rowslice(x, nA+1, nA+nB);
    4542           7 :   return F2m_mul(X, F2m_inv_upper_1(Y));
    4543             : }
    4544             : GEN
    4545           0 : Flm_invimage(GEN A, GEN B, ulong p)
    4546             : {
    4547           0 :   pari_sp av = avma;
    4548           0 :   GEN X = Flm_invimage_i(A,B,p);
    4549           0 :   if (!X) { avma = av; return NULL; }
    4550           0 :   return gerepileupto(av, X);
    4551             : }
    4552             : GEN
    4553           0 : F2m_invimage(GEN A, GEN B)
    4554             : {
    4555           0 :   pari_sp av = avma;
    4556           0 :   GEN X = F2m_invimage_i(A,B);
    4557           0 :   if (!X) { avma = av; return NULL; }
    4558           0 :   return gerepileupto(av, X);
    4559             : }
    4560             : static GEN
    4561          35 : FpM_invimage_i(GEN A, GEN B, GEN p)
    4562             : {
    4563             :   GEN d, x, X, Y;
    4564          35 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4565          35 :   if (lgefint(p) == 3)
    4566             :   {
    4567           0 :     ulong pp = p[2];
    4568           0 :     A = ZM_to_Flm(A, pp);
    4569           0 :     B = ZM_to_Flm(B, pp);
    4570           0 :     x = Flm_invimage_i(A, B, pp);
    4571           0 :     return x? Flm_to_ZM(x): NULL;
    4572             :   }
    4573          35 :   x = FpM_ker(shallowconcat(ZM_neg(A), B), p);
    4574             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4575             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4576             :    * Y has at least nB columns and full rank */
    4577          35 :   nY = lg(x)-1;
    4578          35 :   if (nY < nB) return NULL;
    4579          28 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4580          28 :   d = cgetg(nB+1, t_VECSMALL);
    4581         217 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4582             :   {
    4583         280 :     for (; j>=1; j--)
    4584         266 :       if (signe(gcoeff(Y,i,j))) { d[i] = j; break; }
    4585         203 :     if (!j) return NULL;
    4586             :   }
    4587             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4588          14 :   Y = vecpermute(Y, d);
    4589          14 :   x = vecpermute(x, d);
    4590          14 :   X = rowslice(x, 1, nA);
    4591          14 :   return FpM_mul(X, FpM_inv_upper_1(Y,p), p);
    4592             : }
    4593             : GEN
    4594           0 : FpM_invimage(GEN A, GEN B, GEN p)
    4595             : {
    4596           0 :   pari_sp av = avma;
    4597           0 :   GEN X = FpM_invimage_i(A,B,p);
    4598           0 :   if (!X) { avma = av; return NULL; }
    4599           0 :   return gerepileupto(av, X);
    4600             : }
    4601             : 
    4602             : /* find Z such that A Z = B. Return NULL if no solution */
    4603             : GEN
    4604         252 : RgM_invimage(GEN A, GEN B)
    4605             : {
    4606         252 :   pari_sp av = avma;
    4607             :   GEN d, x, X, Y;
    4608         252 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4609         252 :   GEN p = NULL;
    4610         252 :   if (RgM_is_FpM(A, &p) && RgM_is_FpM(B, &p) && p)
    4611             :   {
    4612             :     ulong pp;
    4613          84 :     A = RgM_Fp_init(A,p,&pp);
    4614          84 :     switch(pp)
    4615             :     {
    4616             :     case 0:
    4617          35 :       B = RgM_to_FpM(B,p);
    4618          35 :       x = FpM_invimage_i(A, B, p);
    4619          35 :       if (x) x = FpM_to_mod(x, p);
    4620          35 :     break;
    4621             :     case 2:
    4622           7 :       B = RgM_to_F2m(B);
    4623           7 :       x = F2m_invimage_i(A, B);
    4624           7 :       if (x) x = F2m_to_mod(x);
    4625           7 :       break;
    4626             :     default:
    4627          42 :       B = RgM_to_Flm(B,pp);
    4628          42 :       x = Flm_invimage_i(A, B, pp);
    4629          42 :       if (x) x = Flm_to_mod(x,pp);
    4630          42 :       break;
    4631             :     }
    4632          84 :     if (!x) { avma = av; return NULL; }
    4633          42 :     return gerepileupto(av, x);
    4634             :   }
    4635             :   {
    4636         168 :     GEN ff = NULL;
    4637         168 :     if (RgM_is_FFM(A, &ff) && RgM_is_FFM(B, &ff))
    4638          35 :       return FFM_invimage(A, B, ff);
    4639             :   }
    4640         133 :   x = ker(shallowconcat(RgM_neg(A), B));
    4641             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4642             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4643             :    * Y has at least nB columns and full rank */
    4644         133 :   nY = lg(x)-1;
    4645         133 :   if (nY < nB) { avma = av; return NULL; }
    4646         105 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4647         105 :   d = cgetg(nB+1, t_VECSMALL);
    4648         686 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4649             :   {
    4650         840 :     for (; j>=1; j--)
    4651         784 :       if (!gequal0(gcoeff(Y,i,j))) { d[i] = j; break; }
    4652         637 :     if (!j) { avma = av; return NULL; }
    4653             :   }
    4654             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4655          49 :   Y = vecpermute(Y, d);
    4656          49 :   x = vecpermute(x, d);
    4657          49 :   X = rowslice(x, 1, nA);
    4658          49 :   return gerepileupto(av, RgM_mul(X, RgM_inv_upper(Y)));
    4659             : }
    4660             : 
    4661             : /* r = dim Ker x, n = nbrows(x) */
    4662             : static GEN
    4663       39875 : get_suppl(GEN x, GEN d, long n, long r, GEN(*ei)(long,long))
    4664             : {
    4665             :   pari_sp av;
    4666             :   GEN y, c;
    4667       39875 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
    4668             : 
    4669       39875 :   if (rx == n && r == 0) return gcopy(x);
    4670       31981 :   y = cgetg(n+1, t_MAT);
    4671       31981 :   av = avma; c = zero_zv(n);
    4672             :   /* c = lines containing pivots (could get it from gauss_pivot, but cheap)
    4673             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
    4674      246449 :   for (k = j = 1; j<=rx; j++)
    4675      214468 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gel(x,j); }
    4676      329901 :   for (j=1; j<=n; j++)
    4677      297920 :     if (!c[j]) gel(y,k++) = (GEN)j; /* HACK */
    4678       31981 :   avma = av;
    4679             : 
    4680       31981 :   rx -= r;
    4681       31981 :   for (j=1; j<=rx; j++) gel(y,j) = gcopy(gel(y,j));
    4682       31981 :   for (   ; j<=n; j++)  gel(y,j) = ei(n, y[j]);
    4683       31981 :   return y;
    4684             : }
    4685             : 
    4686             : static void
    4687       39875 : init_suppl(GEN x)
    4688             : {
    4689       39875 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    4690             :   /* HACK: avoid overwriting d from gauss_pivot() after avma=av */
    4691       39875 :   (void)new_chunk(lgcols(x) * 2);
    4692       39875 : }
    4693             : 
    4694             : /* x is an n x k matrix, rank(x) = k <= n. Return an invertible n x n matrix
    4695             :  * whose first k columns are given by x. If rank(x) < k, undefined result. */
    4696             : GEN
    4697          91 : suppl(GEN x)
    4698             : {
    4699          91 :   pari_sp av = avma;
    4700          91 :   GEN d, X = x, p = NULL, ff = NULL;
    4701             :   long r;
    4702             : 
    4703          91 :   if (typ(x)!=t_MAT) pari_err_TYPE("suppl",x);
    4704          91 :   if (RgM_is_FpM(x, &p) && p)
    4705             :   {
    4706             :     ulong pp;
    4707          28 :     x = RgM_Fp_init(x, p, &pp);
    4708          28 :     switch(pp)
    4709             :     {
    4710           7 :     case 0: x = FpM_to_mod(FpM_suppl(x,p), p); break;
    4711           7 :     case 2: x = F2m_to_mod(F2m_suppl(x)); break;
    4712          14 :     default:x = Flm_to_mod(Flm_suppl(x,pp), pp); break;
    4713             :     }
    4714          28 :     return gerepileupto(av, x);
    4715             :   }
    4716          63 :   if (RgM_is_FFM(x, &ff)) return FFM_suppl(x, ff);
    4717          42 :   avma = av; init_suppl(x);
    4718          42 :   d = gauss_pivot(X,&r);
    4719          42 :   avma = av; return get_suppl(X,d,nbrows(X),r,&col_ei);
    4720             : }
    4721             : GEN
    4722       38566 : FpM_suppl(GEN x, GEN p)
    4723             : {
    4724       38566 :   pari_sp av = avma;
    4725             :   GEN d;
    4726             :   long r;
    4727       38566 :   init_suppl(x); d = FpM_gauss_pivot(x,p, &r);
    4728       38566 :   avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
    4729             : }
    4730             : 
    4731             : GEN
    4732          42 : Flm_suppl(GEN x, ulong p)
    4733             : {
    4734          42 :   pari_sp av = avma;
    4735             :   GEN d;
    4736             :   long r;
    4737          42 :   init_suppl(x); d = Flm_pivots(x, p, &r, 0);
    4738          42 :   avma = av; return get_suppl(x,d,nbrows(x),r,&vecsmall_ei);
    4739             : }
    4740             : 
    4741             : GEN
    4742           7 : F2m_suppl(GEN x)
    4743             : {
    4744           7 :   pari_sp av = avma;
    4745             :   GEN d;
    4746             :   long r;
    4747           7 :   init_suppl(x); d = F2m_gauss_pivot(F2m_copy(x), &r);
    4748           7 :   avma = av; return get_suppl(x,d,mael(x,1,1),r,&F2v_ei);
    4749             : }
    4750             : 
    4751             : /* variable number to be filled in later */
    4752             : static GEN
    4753          14 : _FlxC_ei(long n, long i)
    4754             : {
    4755          14 :   GEN x = cgetg(n + 1, t_COL);
    4756             :   long j;
    4757          42 :   for (j = 1; j <= n; j++)
    4758          28 :     gel(x, j) = (j == i)? pol1_Flx(0): pol0_Flx(0);
    4759          14 :   return x;
    4760             : }
    4761             : 
    4762             : GEN
    4763           7 : F2xqM_suppl(GEN x, GEN T)
    4764             : {
    4765           7 :   pari_sp av = avma;
    4766             :   GEN d, y;
    4767           7 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    4768             : 
    4769           7 :   init_suppl(x);
    4770           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    4771           7 :   avma = av;
    4772           7 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    4773           7 :   if (sv) {
    4774             :     long i, j;
    4775          21 :     for (j = r + 1; j <= n; j++) {
    4776          42 :       for (i = 1; i <= n; i++)
    4777          28 :         gcoeff(y, i, j)[1] = sv;
    4778             :     }
    4779             :   }
    4780           7 :   return y;
    4781             : }
    4782             : 
    4783             : GEN
    4784           7 : FlxqM_suppl(GEN x, GEN T, ulong p)
    4785             : {
    4786           7 :   pari_sp av = avma;
    4787             :   GEN d, y;
    4788           7 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    4789             : 
    4790           7 :   init_suppl(x);
    4791           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    4792           7 :   avma = av;
    4793           7 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    4794           7 :   if (sv) {
    4795             :     long i, j;
    4796          21 :     for (j = r + 1; j <= n; j++) {
    4797          42 :       for (i = 1; i <= n; i++)
    4798          28 :         gcoeff(y, i, j)[1] = sv;
    4799             :     }
    4800             :   }
    4801           7 :   return y;
    4802             : }
    4803             : 
    4804             : GEN
    4805        4025 : FqM_suppl(GEN x, GEN T, GEN p)
    4806             : {
    4807        4025 :   pari_sp av = avma;
    4808             :   GEN d;
    4809             :   long r;
    4810             : 
    4811        4025 :   if (!T) return FpM_suppl(x,p);
    4812        1204 :   init_suppl(x);
    4813        1204 :   d = FqM_gauss_pivot(x,T,p,&r);
    4814        1204 :   avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
    4815             : }
    4816             : 
    4817             : GEN
    4818           7 : image2(GEN x)
    4819             : {
    4820           7 :   pari_sp av = avma;
    4821             :   long k, n, i;
    4822             :   GEN A, B;
    4823             : 
    4824           7 :   if (typ(x)!=t_MAT) pari_err_TYPE("image2",x);
    4825           7 :   if (lg(x) == 1) return cgetg(1,t_MAT);
    4826           7 :   A = ker(x); k = lg(A)-1;
    4827           7 :   if (!k) { avma = av; return gcopy(x); }
    4828           7 :   A = suppl(A); n = lg(A)-1;
    4829           7 :   B = cgetg(n-k+1, t_MAT);
    4830           7 :   for (i = k+1; i <= n; i++) gel(B,i-k) = RgM_RgC_mul(x, gel(A,i));
    4831           7 :   return gerepileupto(av, B);
    4832             : }
    4833             : 
    4834             : GEN
    4835         203 : matimage0(GEN x,long flag)
    4836             : {
    4837         203 :   switch(flag)
    4838             :   {
    4839         196 :     case 0: return image(x);
    4840           7 :     case 1: return image2(x);
    4841           0 :     default: pari_err_FLAG("matimage");
    4842             :   }
    4843             :   return NULL; /* LCOV_EXCL_LINE */
    4844             : }
    4845             : 
    4846             : long
    4847         280 : rank(GEN x)
    4848             : {
    4849         280 :   pari_sp av = avma;
    4850             :   long r;
    4851         280 :   GEN ff = NULL, p = NULL;
    4852             : 
    4853         280 :   if (typ(x)!=t_MAT) pari_err_TYPE("rank",x);
    4854         280 :   if (RgM_is_FpM(x, &p) && p)
    4855             :   {
    4856             :     ulong pp;
    4857         126 :     x = RgM_Fp_init(x,p,&pp);
    4858         126 :     switch(pp)
    4859             :     {
    4860          28 :     case 0: r = FpM_rank(x,p); break;
    4861          63 :     case 2: r = F2m_rank(x); break;
    4862          35 :     default:r = Flm_rank(x,pp); break;
    4863             :     }
    4864         126 :     avma = av; return r;
    4865             :   }
    4866         154 :   if (RgM_is_FFM(x, &ff)) return FFM_rank(x, ff);
    4867          91 :   (void)gauss_pivot(x, &r);
    4868          91 :   avma = av; return lg(x)-1 - r;
    4869             : }
    4870             : 
    4871             : /* d a t_VECSMALL of integers in 1..n. Return the vector of the d[i]
    4872             :  * followed by the missing indices */
    4873             : static GEN
    4874       10256 : perm_complete(GEN d, long n)
    4875             : {
    4876       10256 :   GEN y = cgetg(n+1, t_VECSMALL);
    4877       10256 :   long i, j = 1, k = n, l = lg(d);
    4878       10256 :   pari_sp av = avma;
    4879       10256 :   char *T = stack_calloc(n+1);
    4880       10256 :   for (i = 1; i < l; i++) T[d[i]] = 1;
    4881      106650 :   for (i = 1; i <= n; i++)
    4882       96394 :     if (T[i]) y[j++] = i; else y[k--] = i;
    4883       10256 :   avma = av; return y;
    4884             : }
    4885             : 
    4886             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    4887             : static GEN
    4888       61380 : indexrank0(long n, long r, GEN d)
    4889             : {
    4890       61380 :   GEN p1, p2, res = cgetg(3,t_VEC);
    4891             :   long i, j;
    4892             : 
    4893       61380 :   r = n - r; /* now r = dim Im(x) */
    4894       61380 :   p1 = cgetg(r+1,t_VECSMALL); gel(res,1) = p1;
    4895       61380 :   p2 = cgetg(r+1,t_VECSMALL); gel(res,2) = p2;
    4896       61380 :   if (d)
    4897             :   {
    4898      324412 :     for (i=0,j=1; j<=n; j++)
    4899      263774 :       if (d[j]) { i++; p1[i] = d[j]; p2[i] = j; }
    4900       60638 :     vecsmall_sort(p1);
    4901             :   }
    4902       61380 :   return res;
    4903             : }
    4904             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    4905             : static GEN
    4906         840 : indeximage0(long n, long r, GEN d)
    4907             : {
    4908             :   long i, j;
    4909             :   GEN v;
    4910             : 
    4911         840 :   r = n - r; /* now r = dim Im(x) */
    4912         840 :   v = cgetg(r+1,t_VECSMALL);
    4913        7651 :   if (d) for (i=j=1; j<=n; j++)
    4914        6811 :     if (d[j]) v[i++] = j;
    4915         840 :   return v;
    4916             : }
    4917             : /* x an m x n t_MAT, n > 0, r = dim Ker(x), d from gauss_pivot */
    4918             : static void
    4919        5128 : indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol)
    4920             : {
    4921        5128 :   GEN IR = indexrank0(n, r, d);
    4922        5128 :   *prow = perm_complete(gel(IR,1), m);
    4923        5128 :   *pcol = perm_complete(gel(IR,2), n);
    4924        5128 : }
    4925             : static void
    4926       57092 : init_indexrank(GEN x) {
    4927       57092 :   (void)new_chunk(3 + 2*lg(x)); /* HACK */
    4928       57092 : }
    4929             : 
    4930             : GEN
    4931       14259 : indexrank(GEN x) {
    4932             :   pari_sp av;
    4933             :   long r;
    4934       14259 :   GEN d, p = NULL, ff = NULL;
    4935       14259 :   if (typ(x)!=t_MAT) pari_err_TYPE("indexrank",x);
    4936       14259 :   if (RgM_is_FpM(x, &p) && p)
    4937             :   {
    4938             :     ulong pp;
    4939          28 :     x = RgM_Fp_init(x,p,&pp);
    4940          28 :     switch(pp)
    4941             :     {
    4942           7 :     case 0:  return FpM_indexrank(x, p);
    4943           7 :     case 2:  return F2m_indexrank(x);
    4944          14 :     default: return Flm_indexrank(x, pp);
    4945             :     }
    4946             :   }
    4947       14231 :   if (RgM_is_FFM(x, &ff)) return FFM_indexrank(x, ff);
    4948       14210 :   av = avma;
    4949       14210 :   init_indexrank(x);
    4950       14210 :   d = gauss_pivot(x, &r);
    4951       14210 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4952             : }
    4953             : 
    4954             : GEN
    4955       13321 : FpM_indexrank(GEN x, GEN p) {
    4956       13321 :   pari_sp av = avma;
    4957             :   long r;
    4958             :   GEN d;
    4959       13321 :   init_indexrank(x);
    4960       13321 :   d = FpM_gauss_pivot(x,p,&r);
    4961       13321 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4962             : }
    4963             : 
    4964             : GEN
    4965       12817 : Flm_indexrank(GEN x, ulong p) {
    4966       12817 :   pari_sp av = avma;
    4967             :   long r;
    4968             :   GEN d;
    4969       12817 :   init_indexrank(x);
    4970       12817 :   d = Flm_pivots(x, p, &r, 0);
    4971       12817 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4972             : }
    4973             : 
    4974             : GEN
    4975           7 : F2m_indexrank(GEN x) {
    4976           7 :   pari_sp av = avma;
    4977             :   long r;
    4978             :   GEN d;
    4979           7 :   init_indexrank(x);
    4980           7 :   d = F2m_gauss_pivot(F2m_copy(x),&r);
    4981           7 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4982             : }
    4983             : 
    4984             : GEN
    4985           7 : F2xqM_indexrank(GEN x, GEN T) {
    4986           7 :   pari_sp av = avma;
    4987             :   long r;
    4988             :   GEN d;
    4989           7 :   init_indexrank(x);
    4990           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    4991           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    4992             : }
    4993             : 
    4994             : GEN
    4995           7 : FlxqM_indexrank(GEN x, GEN T, ulong p) {
    4996           7 :   pari_sp av = avma;
    4997             :   long r;
    4998             :   GEN d;
    4999           7 :   init_indexrank(x);
    5000           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    5001           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    5002             : }
    5003             : 
    5004             : GEN
    5005           7 : FqM_indexrank(GEN x, GEN T, GEN p) {
    5006           7 :   pari_sp av = avma;
    5007             :   long r;
    5008             :   GEN d;
    5009           7 :   init_indexrank(x);
    5010           7 :   d = FqM_gauss_pivot(x, T, p, &r);
    5011           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    5012             : }
    5013             : 
    5014             : GEN
    5015         840 : ZM_indeximage(GEN x) {
    5016         840 :   pari_sp av = avma;
    5017             :   long r;
    5018             :   GEN d;
    5019         840 :   init_indexrank(x);
    5020         840 :   d = ZM_pivots(x,&r);
    5021         840 :   avma = av; return indeximage0(lg(x)-1, r, d);
    5022             : }
    5023             : long
    5024       42271 : ZM_rank(GEN x) {
    5025       42271 :   pari_sp av = avma;
    5026             :   long r;
    5027       42271 :   (void)ZM_pivots(x,&r);
    5028       42271 :   avma = av; return lg(x)-1-r;
    5029             : }
    5030             : GEN
    5031       15876 : ZM_indexrank(GEN x) {
    5032       15876 :   pari_sp av = avma;
    5033             :   long r;
    5034             :   GEN d;
    5035       15876 :   init_indexrank(x);
    5036       15876 :   d = ZM_pivots(x,&r);
    5037       15876 :   avma = av; return indexrank0(lg(x)-1, r, d);
    5038             : }
    5039             : 
    5040             : /*******************************************************************/
    5041             : /*                                                                 */
    5042             : /*                             ZabM                                */
    5043             : /*                                                                 */
    5044             : /*******************************************************************/
    5045             : 
    5046             : static GEN
    5047         923 : FpXM_ratlift(GEN a, GEN q)
    5048             : {
    5049             :   GEN B, y;
    5050         923 :   long i, j, l = lg(a), n;
    5051         923 :   B = sqrti(shifti(q,-1));
    5052         923 :   y = cgetg(l, t_MAT);
    5053         923 :   if (l==1) return y;
    5054         923 :   n = lgcols(a);
    5055        2970 :   for (i=1; i<l; i++)
    5056             :   {
    5057        2373 :     GEN yi = cgetg(n, t_COL);
    5058       31661 :     for (j=1; j<n; j++)
    5059             :     {
    5060       29614 :       GEN v = FpX_ratlift(gmael(a,i,j), q, B, B, NULL);
    5061       29614 :       if (!v) return NULL;
    5062       29288 :       gel(yi, j) = RgX_renormalize(v);
    5063             :     }
    5064        2047 :     gel(y,i) = yi;
    5065             :   }
    5066         597 :   return y;
    5067             : }
    5068             : 
    5069             : static GEN
    5070        2426 : FlmV_recover_pre(GEN a, GEN M, ulong p, ulong pi, long sv)
    5071             : {
    5072        2426 :   GEN a1 = gel(a,1);
    5073        2426 :   long i, j, k, l = lg(a1), n, lM = lg(M);
    5074        2426 :   GEN v = cgetg(lM, t_VECSMALL);
    5075        2426 :   GEN y = cgetg(l, t_MAT);
    5076        2426 :   if (l==1) return y;
    5077        2426 :   n = lgcols(a1);
    5078       17275 :   for (i=1; i<l; i++)
    5079             :   {
    5080       14849 :     GEN yi = cgetg(n, t_COL);
    5081      320641 :     for (j=1; j<n; j++)
    5082             :     {
    5083      305792 :       for (k=1; k<lM; k++) uel(v,k) = umael(gel(a,k),i,j);
    5084      305792 :       gel(yi, j) = Flm_Flc_mul_pre_Flx(M, v, p, pi, sv);
    5085             :     }
    5086       14849 :     gel(y,i) = yi;
    5087             :   }
    5088        2426 :   return y;
    5089             : }
    5090             : 
    5091             : static GEN
    5092           0 : FlkM_inv(GEN M, GEN P, ulong p)
    5093             : {
    5094           0 :   ulong pi = get_Fl_red(p);
    5095           0 :   GEN R = Flx_roots(P, p);
    5096           0 :   long l = lg(R), i;
    5097           0 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    5098           0 :   GEN V = cgetg(l, t_VEC);
    5099           0 :   for(i=1; i<l; i++)
    5100             :   {
    5101           0 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, pi);
    5102           0 :     GEN H = Flm_inv_sp(FlxM_eval_powers_pre(M, pows, p, pi), NULL, p);
    5103           0 :     if (!H) return NULL;
    5104           0 :     gel(V, i) = H;
    5105             :   }
    5106           0 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    5107             : }
    5108             : 
    5109             : static GEN
    5110        1503 : FlkM_adjoint(GEN M, GEN P, ulong p)
    5111             : {
    5112        1503 :   ulong pi = get_Fl_red(p);
    5113        1503 :   GEN R = Flx_roots(P, p);
    5114        1503 :   long l = lg(R), i;
    5115        1503 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    5116        1503 :   GEN V = cgetg(l, t_VEC);
    5117        6155 :   for(i=1; i<l; i++)
    5118             :   {
    5119        4652 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, pi);
    5120        4652 :     gel(V, i) = Flm_adjoint(FlxM_eval_powers_pre(M, pows, p, pi), p);
    5121             :   }
    5122        1503 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    5123             : }
    5124             : 
    5125             : 
    5126             : static GEN
    5127        1433 : ZabM_inv_slice(GEN A, GEN Q, GEN P, GEN *mod)
    5128             : {
    5129        1433 :   pari_sp av = avma;
    5130        1433 :   long i, n = lg(P)-1, w = varn(Q);
    5131             :   GEN H, T;
    5132        1433 :   if (n == 1)
    5133             :   {
    5134        1383 :     ulong p = uel(P,1);
    5135        1383 :     GEN Ap = FqM_to_FlxM(A, Q, utoi(p));
    5136        1383 :     GEN Qp = ZX_to_Flx(Q, p);
    5137        1383 :     GEN Hp = FlkM_adjoint(Ap, Qp, p);
    5138        1383 :     Hp = gerepileupto(av, FlxM_to_ZXM(Hp));
    5139        1383 :     *mod = utoi(p); return Hp;
    5140             :   }
    5141          50 :   T = ZV_producttree(P);
    5142          50 :   A = ZXM_nv_mod_tree(A, P, T, w);
    5143          50 :   Q = ZX_nv_mod_tree(Q, P, T);
    5144          50 :   H = cgetg(n+1, t_VEC);
    5145         170 :   for(i=1; i <= n; i++)
    5146             :   {
    5147         120 :     ulong p = P[i];
    5148         120 :     GEN a = gel(A,i), q = gel(Q, i);
    5149         120 :     gel(H,i) = FlkM_adjoint(a, q, p);
    5150             :   }
    5151          50 :   H = nxMV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    5152          50 :   *mod = gmael(T, lg(T)-1, 1);
    5153          50 :   gerepileall(av, 2, &H, mod);
    5154          50 :   return H;
    5155             : }
    5156             : 
    5157             : GEN
    5158        1433 : ZabM_inv_worker(GEN P, GEN A, GEN Q)
    5159             : {
    5160        1433 :   GEN V = cgetg(3, t_VEC);
    5161        1433 :   gel(V,1) = ZabM_inv_slice(A, Q, P, &gel(V,2));
    5162        1433 :   return V;
    5163             : }
    5164             : 
    5165             : static GEN
    5166        3836 : vecnorml1(GEN a)
    5167             : {
    5168             :   long i, l;
    5169        3836 :   GEN g = cgetg_copy(a, &l);
    5170       62090 :   for (i=1; i<l; i++)
    5171       58254 :     gel(g, i) = gnorml1_fake(gel(a,i));
    5172        3836 :   return g;
    5173             : }
    5174             : 
    5175             : static GEN
    5176        1043 : ZabM_true_Hadamard(GEN a)
    5177             : {
    5178        1043 :   pari_sp av = avma;
    5179        1043 :   long n = lg(a)-1, i;
    5180             :   GEN B;
    5181        1043 :   if (n == 0) return gen_1;
    5182        1043 :   if (n == 1) return gnorml1_fake(gcoeff(a,1,1));
    5183         567 :   B = gen_1;
    5184         567 :   for (i = 1; i <= n; i++) B = gmul(B, gnorml2(RgC_gtofp(vecnorml1(gel(a,i)),DEFAULTPREC)));
    5185         567 :   return gerepileuptoint(av, ceil_safe(sqrtr_abs(B)));
    5186             : }
    5187             : 
    5188             : GEN
    5189        1043 : ZabM_inv(GEN A, GEN Q, long n, GEN *pt_den)
    5190             : {
    5191        1043 :   pari_sp av = avma;
    5192        1043 :   long m = lg(A)-1;
    5193             :   GEN bnd, H, D, d, mod, worker;
    5194        1043 :   if (m == 0)
    5195             :   {
    5196           0 :     if (pt_den) *pt_den = gen_1;
    5197           0 :     return cgetg(1, t_MAT);
    5198             :   }
    5199        1043 :   bnd = ZabM_true_Hadamard(A);
    5200        1043 :   worker = strtoclosure("_ZabM_inv_worker", 2, A, Q);
    5201        1043 :   H = gen_crt("ZabM_inv", worker, mkvecsmall(n), expi(bnd), m, &mod,
    5202             :               nxMV_chinese_center, FpXM_center);
    5203        1043 :   D = RgMrow_RgC_mul(A, gel(H,1), 1);
    5204        1043 :   D = ZX_rem(D, Q);
    5205        1043 :   d = Z_content(mkvec2(H, D));
    5206        1043 :   if (d)
    5207             :   {
    5208         315 :     D = ZX_Z_divexact(D, d);
    5209         315 :     H = Q_div_to_int(H, d);
    5210             :   }
    5211        1043 :   if (pt_den)
    5212             :   {
    5213        1043 :     gerepileall(av, 2, &H, &D);
    5214        1043 :     *pt_den = D; return H;
    5215             :   }
    5216           0 :   return gerepileupto(av, H);
    5217             : }
    5218             : 
    5219             : GEN
    5220           0 : ZabM_inv_ratlift(GEN M, GEN P, long n, GEN *pden)
    5221             : {
    5222           0 :   pari_sp av2, av = avma;
    5223             :   GEN q, H;
    5224           0 :   ulong m = LONG_MAX>>1;
    5225           0 :   ulong p= 1 + m - (m % n);
    5226           0 :   long lM = lg(M);
    5227           0 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    5228             : 
    5229           0 :   av2 = avma;
    5230           0 :   H = NULL;
    5231             :   for(;;)
    5232             :   {
    5233             :     GEN Hp, Pp, Mp, Hr;
    5234           0 :     do p += n; while(!uisprime(p));
    5235           0 :     Pp = ZX_to_Flx(P, p);
    5236           0 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    5237           0 :     Hp = FlkM_inv(Mp, Pp, p);
    5238           0 :     if (!Hp) continue;
    5239           0 :     if (!H)
    5240             :     {
    5241           0 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p);
    5242           0 :       q = utoipos(p);
    5243             :     }
    5244             :     else
    5245           0 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    5246           0 :     Hr = FpXM_ratlift(H, q);
    5247           0 :     if (DEBUGLEVEL>5) err_printf("ZabM_inv mod %ld (ratlift=%ld)\n", p,!!Hr);
    5248           0 :     if (Hr) {/* DONE ? */
    5249           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    5250           0 :       GEN MH = ZXQM_mul(M, Hl,P);
    5251           0 :       if (*pden)
    5252           0 :       { if (RgM_isscalar(MH, *pden)) { H = Hl; break; }}
    5253             :       else
    5254           0 :       { if (RgM_isidentity(MH)) { H = Hl; *pden = gen_1; break; } }
    5255             :     }
    5256             : 
    5257           0 :     if (gc_needed(av,2))
    5258             :     {
    5259           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_inv");
    5260           0 :       gerepileall(av2, 2, &H, &q);
    5261             :     }
    5262           0 :   }
    5263           0 :   gerepileall(av, 2, &H, pden);
    5264           0 :   return H;
    5265             : }
    5266             : 
    5267             : static GEN
    5268         923 : FlkM_ker(GEN M, GEN P, ulong p)
    5269             : {
    5270         923 :   ulong pi = get_Fl_red(p);
    5271         923 :   GEN R = Flx_roots(P, p);
    5272         923 :   long l = lg(R), i, dP = degpol(P), r;
    5273             :   GEN M1, K, D;
    5274         923 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    5275         923 :   GEN V = cgetg(l, t_VEC);
    5276         923 :   M1 = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,1), dP, p, pi), p, pi);
    5277         923 :   K = Flm_ker_sp(M1, p, 2);
    5278         923 :   r = lg(gel(K,1)); D = gel(K,2);
    5279         923 :   gel(V, 1) = gel(K,1);
    5280        1888 :   for(i=2; i<l; i++)
    5281             :   {
    5282         965 :     GEN Mi = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), dP, p, pi), p, pi);
    5283         965 :     GEN K = Flm_ker_sp(Mi, p, 2);
    5284         965 :     if (lg(gel(K,1)) != r || !zv_equal(D, gel(K,2))) return NULL;
    5285         965 :     gel(V, i) = gel(K,1);
    5286             :   }
    5287         923 :   return mkvec2(FlmV_recover_pre(V, W, p, pi, P[1]), D);
    5288             : }
    5289             : 
    5290             : GEN
    5291         588 : ZabM_ker(GEN M, GEN P, long n)
    5292             : {
    5293         588 :   pari_sp av2, av = avma;
    5294             :   GEN q, H, D;
    5295         588 :   ulong m = LONG_MAX>>1;
    5296         588 :   ulong p= 1 + m - (m % n);
    5297         588 :   av2 = avma;
    5298         588 :   H = NULL; D = NULL;
    5299             :   for(;;)
    5300             :   {
    5301             :     GEN Kp, Hp, Dp, Pp, Mp, Hr;
    5302       25707 :     do p += n; while(!uisprime(p));
    5303         923 :     Pp = ZX_to_Flx(P, p);
    5304         923 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    5305         923 :     Kp = FlkM_ker(Mp, Pp, p);
    5306         923 :     if (!Kp) continue;
    5307         923 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    5308         923 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    5309         923 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    5310             :     {
    5311         588 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p); D = Dp;
    5312         588 :       q = utoipos(p);
    5313             :     }
    5314             :     else
    5315         335 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    5316         923 :     Hr = FpXM_ratlift(H, q);
    5317         923 :     if (DEBUGLEVEL>5) err_printf("ZabM_ker mod %ld (ratlift=%ld)\n", p,!!Hr);
    5318         923 :     if (Hr) {/* DONE ? */
    5319         597 :       GEN Hl = Q_remove_denom(Hr, NULL);
    5320         597 :       GEN MH = ZXQM_mul(M, Hl,P);
    5321         597 :       if (gequal0(MH)) { H = Hl;  break; }
    5322             :     }
    5323             : 
    5324         335 :     if (gc_needed(av,2))
    5325             :     {
    5326           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_ker");
    5327           0 :       gerepileall(av2, 3, &H, &D, &q);
    5328             :     }
    5329         335 :   }
    5330         588 :   return gerepilecopy(av, H);
    5331             : }
    5332             : 
    5333             : GEN
    5334        1813 : ZabM_indexrank(GEN M, GEN P, long n)
    5335             : {
    5336        1813 :   pari_sp av = avma;
    5337        1813 :   ulong m = LONG_MAX>>1;
    5338        1813 :   ulong p = 1+m-(m%n), D = degpol(P);
    5339        1813 :   long lM = lg(M), lmax = 0, c = 0;
    5340             :   GEN v;
    5341             :   for(;;)
    5342             :   {
    5343             :     GEN R, Mp, K;
    5344             :     ulong pi;
    5345             :     long l;
    5346       73507 :     do p += n; while (!uisprime(p));
    5347        2422 :     pi = get_Fl_red(p);
    5348        2422 :     R = Flx_roots(ZX_to_Flx(P, p), p);
    5349        2422 :     Mp = FqM_to_FlxM(M, P, utoipos(p));
    5350        2422 :     K = FlxM_eval_powers_pre(Mp, Fl_powers_pre(uel(R,1), D,p,pi), p,pi);
    5351        2422 :     v = Flm_indexrank(K, p);
    5352        2422 :     l = lg(gel(v,2));
    5353        2422 :     if (l == lM) break;
    5354         812 :     if (lmax >= 0 && l > lmax) { lmax = l; c = 0; } else c++;
    5355         812 :     if (c > 2)
    5356             :     { /* probably not maximal rank, expensive check */
    5357         203 :       lM -= lg(ZabM_ker(M, P, n))-1; /* actual rank (+1) */
    5358         203 :       if (lmax == lM) break;
    5359           0 :       lmax = -1; /* disable check */
    5360             :     }
    5361         609 :   }
    5362        1813 :   return gerepileupto(av, v);
    5363             : }
    5364             : 
    5365             : #if 0
    5366             : GEN
    5367             : ZabM_gauss(GEN M, GEN P, long n, GEN *den)
    5368             : {
    5369             :   pari_sp av = avma;
    5370             :   GEN v, S, W;
    5371             :   v = ZabM_indexrank(M, P, n);
    5372             :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    5373             :   W = ZabM_inv(S, P, n, den);
    5374             :   gerepileall(av,2,&W,den);
    5375             :   return W;
    5376             : }
    5377             : #endif
    5378             : 
    5379             : GEN
    5380         798 : ZabM_pseudoinv(GEN M, GEN P, long n, GEN *pv, GEN *den)
    5381             : {
    5382         798 :   GEN v = ZabM_indexrank(M, P, n);
    5383         798 :   if (pv) *pv = v;
    5384         798 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    5385         798 :   return ZabM_inv(M, P, n, den);
    5386             : }
    5387             : GEN
    5388           0 : ZM_pseudoinv(GEN M, GEN *pv, GEN *den)
    5389             : {
    5390           0 :   GEN v = ZM_indexrank(M);
    5391           0 :   if (pv) *pv = v;
    5392           0 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    5393           0 :   return ZM_inv(M, den);
    5394             : }
    5395             : 
    5396             : /*******************************************************************/
    5397             : /*                                                                 */
    5398             : /*                   Structured Elimination                        */
    5399             : /*                                                                 */
    5400             : /*******************************************************************/
    5401             : 
    5402             : static void
    5403       99658 : rem_col(GEN c, long i, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5404             : {
    5405       99658 :   long lc = lg(c), k;
    5406       99658 :   iscol[i] = 0; (*rcol)--;
    5407      883000 :   for (k = 1; k < lc; ++k)
    5408             :   {
    5409      783342 :     Wrow[c[k]]--;
    5410      783342 :     if (Wrow[c[k]]==0) (*rrow)--;
    5411             :   }
    5412       99658 : }
    5413             : 
    5414             : static void
    5415        5967 : rem_singleton(GEN M, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5416             : {
    5417             :   long i, j;
    5418        5967 :   long nbcol = lg(iscol)-1, last;
    5419             :   do
    5420             :   {
    5421        7817 :     last = 0;
    5422    18931270 :     for (i = 1; i <= nbcol; ++i)
    5423    18923453 :       if (iscol[i])
    5424             :       {
    5425     9730655 :         GEN c = gmael(M, i, 1);
    5426     9730655 :         long lc = lg(c);
    5427    90887386 :         for (j = 1; j < lc; ++j)
    5428    81168792 :           if (Wrow[c[j]] == 1)
    5429             :           {
    5430       12061 :             rem_col(c, i, iscol, Wrow, rcol, rrow);
    5431       12061 :             last=1; break;
    5432             :           }
    5433             :       }
    5434        7817 :   } while (last);
    5435        5967 : }
    5436             : 
    5437             : static GEN
    5438        5848 : fill_wcol(GEN M, GEN iscol, GEN Wrow, long *w, GEN wcol)
    5439             : {
    5440        5848 :   long nbcol = lg(iscol)-1;
    5441             :   long i, j, m, last;
    5442             :   GEN per;
    5443       14559 :   for (m = 2, last=0; !last ; m++)
    5444             :   {
    5445    22245628 :     for (i = 1; i <= nbcol; ++i)
    5446             :     {
    5447    22236917 :       wcol[i] = 0;
    5448    22236917 :       if (iscol[i])
    5449             :       {
    5450    11415860 :         GEN c = gmael(M, i, 1);
    5451    11415860 :         long lc = lg(c);
    5452   106639332 :         for (j = 1; j < lc; ++j)
    5453    95223472 :           if (Wrow[c[j]] == m) {  wcol[i]++; last = 1; }
    5454             :       }
    5455             :     }
    5456             :   }
    5457        5848 :   per = vecsmall_indexsort(wcol);
    5458        5848 :   *w = wcol[per[nbcol]];
    5459        5848 :   return per;
    5460             : }
    5461             : 
    5462             : /* M is a RgMs with nbrow rows, A a list of row indices.
    5463             :    Eliminate rows of M with a single entry that do not belong to A,
    5464             :    and the corresponding columns. Also eliminate columns until #colums=#rows.
    5465             :    Return pcol and prow:
    5466             :    pcol is a map from the new columns indices to the old one.
    5467             :    prow is a map from the old rows indices to the new one (0 if removed).
    5468             : */
    5469             : 
    5470             : void
    5471         119 : RgMs_structelim_col(GEN M, long nbcol, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5472             : {
    5473             :   long i,j,k;
    5474         119 :   long lA = lg(A);
    5475         119 :   GEN prow = cgetg(nbrow+1, t_VECSMALL);
    5476         119 :   GEN pcol = zero_zv(nbcol);
    5477         119 :   pari_sp av = avma;
    5478         119 :   long rcol = nbcol, rrow = 0, imin = nbcol - usqrt(nbcol);
    5479         119 :   GEN iscol = const_vecsmall(nbcol, 1);
    5480         119 :   GEN Wrow  = zero_zv(nbrow);
    5481         119 :   GEN wcol = cgetg(nbcol+1, t_VECSMALL);
    5482         119 :   pari_sp av2=avma;
    5483      124999 :   for (i = 1; i <= nbcol; ++i)
    5484             :   {
    5485      124880 :     GEN F = gmael(M, i, 1);
    5486      124880 :     long l = lg(F)-1;
    5487     1104904 :     for (j = 1; j <= l; ++j)
    5488      980024 :       Wrow[F[j]]++;
    5489             :   }
    5490         119 :   for (j = 1; j < lA; ++j)
    5491             :   {
    5492         119 :     if (Wrow[A[j]] == 0) { *p_col=NULL; return; }
    5493           0 :     Wrow[A[j]] = -1;
    5494             :   }
    5495      221956 :   for (i = 1; i <= nbrow; ++i)
    5496      221837 :     if (Wrow[i])
    5497       66340 :       rrow++;
    5498         119 :   rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5499         119 :   if (rcol<rrow) pari_err_BUG("RgMs_structelim, rcol<rrow");
    5500        6086 :   for (; rcol>rrow;)
    5501             :   {
    5502             :     long w;
    5503        5848 :     GEN per = fill_wcol(M, iscol, Wrow, &w, wcol);
    5504       93445 :     for (i = nbcol; i>=imin && wcol[per[i]]>=w && rcol>rrow; i--)
    5505       87597 :       rem_col(gmael(M, per[i], 1), per[i], iscol, Wrow, &rcol, &rrow);
    5506        5848 :     rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5507        5848 :     avma = av2;
    5508             :   }
    5509      124999 :   for (j = 1, i = 1; i <= nbcol; ++i)
    5510      124880 :     if (iscol[i])
    5511       25222 :       pcol[j++] = i;
    5512         119 :   setlg(pcol,j);
    5513      221956 :   for (k = 1, i = 1; i <= nbrow; ++i)
    5514      221837 :     prow[i] = Wrow[i] ? k++: 0;
    5515         119 :   avma = av;
    5516         119 :   *p_col = pcol; *p_row = prow;
    5517             : }
    5518             : 
    5519             : void
    5520           0 : RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5521             : {
    5522           0 :   RgMs_structelim_col(M, lg(M)-1, nbrow, A, p_col, p_row);
    5523           0 : }
    5524             : 
    5525             : /*******************************************************************/
    5526             : /*                                                                 */
    5527             : /*                        EIGENVECTORS                             */
    5528             : /*   (independent eigenvectors, sorted by increasing eigenvalue)   */
    5529             : /*                                                                 */
    5530             : /*******************************************************************/
    5531             : 
    5532             : GEN
    5533          63 : mateigen(GEN x, long flag, long prec)
    5534             : {
    5535             :   GEN y, R, T;
    5536          63 :   long k, l, ex, n = lg(x);
    5537          63 :   pari_sp av = avma;
    5538             : 
    5539          63 :   if (typ(x)!=t_MAT) pari_err_TYPE("eigen",x);
    5540          63 :   if (n != 1 && n != lgcols(x)) pari_err_DIM("eigen");
    5541          63 :   if (flag < 0 || flag > 1) pari_err_FLAG("mateigen");
    5542          63 :   if (n == 1)
    5543             :   {
    5544          14 :     if (flag) retmkvec2(cgetg(1,t_VEC), cgetg(1,t_MAT));
    5545           7 :     return cgetg(1,t_VEC);
    5546             :   }
    5547          49 :   if (n == 2)
    5548             :   {
    5549          14 :     if (flag) retmkvec2(mkveccopy(gcoeff(x,1,1)), matid(1));
    5550           7 :     return matid(1);
    5551             :   }
    5552             : 
    5553          35 :   ex = 16 - prec2nbits(prec);
    5554          35 :   T = charpoly(x,0);
    5555          35 :   if (RgX_is_QX(T))
    5556             :   {
    5557          28 :     T = ZX_radical( Q_primpart(T) );
    5558          28 :     R = nfrootsQ(T);
    5559          28 :     if (lg(R)-1 < degpol(T))
    5560             :     { /* add missing complex roots */
    5561          14 :       GEN r = cleanroots(RgX_div(T, roots_to_pol(R, 0)), prec);
    5562          14 :       settyp(r, t_VEC);
    5563          14 :       R = shallowconcat(R, r);
    5564             :     }
    5565             :   }
    5566             :   else
    5567             :   {
    5568           7 :     GEN r1, v = vectrunc_init(lg(T));
    5569             :     long e;
    5570           7 :     R = cleanroots(T,prec);
    5571           7 :     r1 = NULL;
    5572          21 :     for (k = 1; k < lg(R); k++)
    5573             :     {
    5574          14 :       GEN r2 = gel(R,k), r = grndtoi(r2, &e);
    5575          14 :       if (e < ex) r2 = r;
    5576          14 :       if (r1)
    5577             :       {
    5578           7 :         r = gsub(r1,r2);
    5579           7 :         if (gequal0(r) || gexpo(r) < ex) continue;
    5580             :       }
    5581          14 :       vectrunc_append(v, r2);
    5582          14 :       r1 = r2;
    5583             :     }
    5584           7 :     R = v;
    5585             :   }
    5586             :   /* R = distinct complex roots of charpoly(x) */
    5587          35 :   l = lg(R); y = cgetg(l, t_VEC);
    5588         189 :   for (k = 1; k < l; k++)
    5589             :   {
    5590         154 :     GEN F = ker_aux(RgM_Rg_sub_shallow(x, gel(R,k)), x);
    5591         154 :     long d = lg(F)-1;
    5592         154 :     if (!d) pari_err_PREC("mateigen");
    5593         154 :     gel(y,k) = F;
    5594         154 :     if (flag) gel(R,k) = const_vec(d, gel(R,k));
    5595             :   }
    5596          35 :   y = shallowconcat1(y);
    5597          35 :   if (lg(y) > n) pari_err_PREC("mateigen");
    5598             :   /* lg(y) < n if x is not diagonalizable */
    5599          35 :   if (flag) y = mkvec2(shallowconcat1(R), y);
    5600          35 :   return gerepilecopy(av,y);
    5601             : }
    5602             : GEN
    5603           0 : eigen(GEN x, long prec) { return mateigen(x, 0, prec); }
    5604             : 
    5605             : /*******************************************************************/
    5606             : /*                                                                 */
    5607             : /*                           DETERMINANT                           */
    5608             : /*                                                                 */
    5609             : /*******************************************************************/
    5610             : 
    5611             : GEN
    5612        1743 : det0(GEN a,long flag)
    5613             : {
    5614        1743 :   switch(flag)
    5615             :   {
    5616        1729 :     case 0: return det(a);
    5617          14 :     case 1: return det2(a);
    5618           0 :     default: pari_err_FLAG("matdet");
    5619             :   }
    5620             :   return NULL; /* LCOV_EXCL_LINE */
    5621             : }
    5622             : 
    5623             : /* M a 2x2 matrix, returns det(M) */
    5624             : static GEN
    5625        3572 : RgM_det2(GEN M)
    5626             : {
    5627        3572 :   pari_sp av = avma;
    5628        3572 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5629        3572 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5630        3572 :   return gerepileupto(av, gsub(gmul(a,d), gmul(b,c)));
    5631             : }
    5632             : /* M a 2x2 ZM, returns det(M) */
    5633             : static GEN
    5634        9177 : ZM_det2(GEN M)
    5635             : {
    5636        9177 :   pari_sp av = avma;
    5637        9177 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5638        9177 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5639        9177 :   return gerepileuptoint(av, subii(mulii(a,d), mulii(b, c)));
    5640             : }
    5641             : /* M a 3x3 ZM, return det(M) */
    5642             : static GEN
    5643        2933 : ZM_det3(GEN M)
    5644             : {
    5645        2933 :   pari_sp av = avma;
    5646        2933 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2), c = gcoeff(M,1,3);
    5647        2933 :   GEN d = gcoeff(M,2,1), e = gcoeff(M,2,2), f = gcoeff(M,2,3);
    5648        2933 :   GEN g = gcoeff(M,3,1), h = gcoeff(M,3,2), i = gcoeff(M,3,3);
    5649        2933 :   GEN t, D = signe(i)? mulii(subii(mulii(a,e), mulii(b,d)), i): gen_0;
    5650        2933 :   if (signe(g))
    5651             :   {
    5652        2730 :     t = mulii(subii(mulii(b,f), mulii(c,e)), g);
    5653        2730 :     D = addii(D, t);
    5654             :   }
    5655        2933 :   if (signe(h))
    5656             :   {
    5657        2751 :     t = mulii(subii(mulii(c,d), mulii(a,f)), h);
    5658        2751 :     D = addii(D, t);
    5659             :   }
    5660        2933 :   return gerepileuptoint(av, D);
    5661             : }
    5662             : 
    5663             : static GEN
    5664        9762 : det_simple_gauss(GEN a, GEN data, pivot_fun pivot)
    5665             : {
    5666        9762 :   pari_sp av = avma;
    5667        9762 :   long i,j,k, s = 1, nbco = lg(a)-1;
    5668        9762 :   GEN p, x = gen_1;
    5669             : 
    5670        9762 :   a = RgM_shallowcopy(a);
    5671       78056 :   for (i=1; i<nbco; i++)
    5672             :   {
    5673       68301 :     k = pivot(a, data, i, NULL);
    5674       68301 :     if (k > nbco) return gerepilecopy(av, gcoeff(a,i,i));
    5675       68294 :     if (k != i)
    5676             :     { /* exchange the lines s.t. k = i */
    5677        4223 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    5678        4223 :       s = -s;
    5679             :     }
    5680       68294 :     p = gcoeff(a,i,i);
    5681             : 
    5682       68294 :     x = gmul(x,p);
    5683      400280 :     for (k=i+1; k<=nbco; k++)
    5684             :     {
    5685      331986 :       GEN m = gcoeff(a,i,k);
    5686      331986 :       if (gequal0(m)) continue;
    5687             : 
    5688      116242 :       m = gdiv(m,p);
    5689      774738 :       for (j=i+1; j<=nbco; j++)
    5690      658496 :         gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i)));
    5691             :     }
    5692       68294 :     if (gc_needed(av,2))
    5693             :     {
    5694           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5695           0 :       gerepileall(av,2, &a,&x);
    5696             :     }
    5697             :   }
    5698        9755 :   if (s < 0) x = gneg_i(x);
    5699        9755 :   return gerepileupto(av, gmul(x, gcoeff(a,nbco,nbco)));
    5700             : }
    5701             : 
    5702             : GEN
    5703        4307 : det2(GEN a)
    5704             : {
    5705             :   GEN data;
    5706             :   pivot_fun pivot;
    5707        4307 :   long n = lg(a)-1;
    5708        4307 :   if (typ(a)!=t_MAT) pari_err_TYPE("det2",a);
    5709        4307 :   if (!n) return gen_1;
    5710        4307 :   if (n != nbrows(a)) pari_err_DIM("det2");
    5711        4307 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5712        4307 :   if (n == 2) return RgM_det2(a);
    5713        1589 :   pivot = get_pivot_fun(a, a, &data);
    5714        1589 :   return det_simple_gauss(a, data, pivot);
    5715             : }
    5716             : 
    5717             : static GEN
    5718         287 : mydiv(GEN x, GEN y)
    5719             : {
    5720         287 :   long tx = typ(x), ty = typ(y);
    5721         287 :   if (tx == ty && tx == t_POL && varn(x) == varn(y)) return RgX_div(x,y);
    5722         287 :   return gdiv(x,y);
    5723             : }
    5724             : 
    5725             : /* Assumes a a square t_MAT of dimension n > 0. Returns det(a) using
    5726             :  * Gauss-Bareiss. */
    5727             : static GEN
    5728          63 : det_bareiss(GEN a)
    5729             : {
    5730          63 :   pari_sp av = avma;
    5731          63 :   long nbco = lg(a)-1,i,j,k,s = 1;
    5732             :   GEN p, pprec;
    5733             : 
    5734          63 :   a = RgM_shallowcopy(a);
    5735         245 :   for (pprec=gen_1,i=1; i<nbco; i++,pprec=p)
    5736             :   {
    5737             :     GEN ci;
    5738         182 :     int diveuc = (gequal1(pprec)==0);
    5739             : 
    5740         182 :     p = gcoeff(a,i,i);
    5741         182 :     if (gequal0(p))
    5742             :     {
    5743           0 :       k=i+1; while (k<=nbco && gequal0(gcoeff(a,i,k))) k++;
    5744           0 :       if (k>nbco) return gerepilecopy(av, p);
    5745           0 :       swap(gel(a,k), gel(a,i)); s = -s;
    5746           0 :       p = gcoeff(a,i,i);
    5747             :     }
    5748         182 :     ci = gel(a,i);
    5749         539 :     for (k=i+1; k<=nbco; k++)
    5750             :     {
    5751         357 :       GEN ck = gel(a,k), m = gel(ck,i);
    5752         357 :       if (gequal0(m))
    5753             :       {
    5754           0 :         if (gequal1(p))
    5755             :         {
    5756           0 :           if (diveuc)
    5757           0 :             gel(a,k) = mydiv(gel(a,k), pprec);
    5758             :         }
    5759             :         else
    5760           0 :           for (j=i+1; j<=nbco; j++)
    5761             :           {
    5762           0 :             GEN p1 = gmul(p, gel(ck,j));
    5763           0 :             if (diveuc) p1 = mydiv(p1,pprec);
    5764           0 :             gel(ck,j) = p1;
    5765             :           }
    5766             :       }
    5767             :       else
    5768        1176 :         for (j=i+1; j<=nbco; j++)
    5769             :         {
    5770         819 :           pari_sp av2 = avma;
    5771         819 :           GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j)));
    5772         819 :           if (diveuc) p1 = mydiv(p1,pprec);
    5773         819 :           gel(ck,j) = gerepileupto(av2, p1);
    5774             :         }
    5775         357 :       if (gc_needed(av,2))
    5776             :       {
    5777           0 :         if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5778           0 :         gerepileall(av,2, &a,&pprec);
    5779           0 :         ci = gel(a,i);
    5780           0 :         p = gcoeff(a,i,i);
    5781             :       }
    5782             :     }
    5783             :   }
    5784          63 :   p = gcoeff(a,nbco,nbco);
    5785          63 :   p = (s < 0)? gneg(p): gcopy(p);
    5786          63 :   return gerepileupto(av, p);
    5787             : }
    5788             : 
    5789             : /* count non-zero entries in col j, at most 'max' of them.
    5790             :  * Return their indices */
    5791             : static GEN
    5792         259 : col_count_non_zero(GEN a, long j, long max)
    5793             : {
    5794         259 :   GEN v = cgetg(max+1, t_VECSMALL);
    5795         259 :   GEN c = gel(a,j);
    5796         259 :   long i, l = lg(a), k = 1;
    5797        1015 :   for (i = 1; i < l; i++)
    5798        1001 :     if (!gequal0(gel(c,i)))
    5799             :     {
    5800         973 :       if (k > max) return NULL; /* fail */
    5801         728 :       v[k++] = i;
    5802             :     }
    5803          14 :   setlg(v, k); return v;
    5804             : }
    5805             : /* count non-zero entries in row i, at most 'max' of them.
    5806             :  * Return their indices */
    5807             : static GEN
    5808         245 : row_count_non_zero(GEN a, long i, long max)
    5809             : {
    5810         245 :   GEN v = cgetg(max+1, t_VECSMALL);
    5811         245 :   long j, l = lg(a), k = 1;
    5812         959 :   for (j = 1; j < l; j++)
    5813         959 :     if (!gequal0(gcoeff(a,i,j)))
    5814             :     {
    5815         959 :       if (k > max) return NULL; /* fail */
    5816         714 :       v[k++] = j;
    5817             :     }
    5818           0 :   setlg(v, k); return v;
    5819             : }
    5820             : 
    5821             : static GEN det_develop(GEN a, long max, double bound);
    5822             : /* (-1)^(i+j) a[i,j] * det RgM_minor(a,i,j) */
    5823             : static GEN
    5824          14 : coeff_det(GEN a, long i, long j, long max, double bound)
    5825             : {
    5826          14 :   GEN c = gcoeff(a, i, j);
    5827          14 :   c = gmul(c, det_develop(RgM_minor(a, i,j), max, bound));
    5828          14 :   if (odd(i+j)) c = gneg(c);
    5829          14 :   return c;
    5830             : }
    5831             : /* a square t_MAT, 'bound' a rough upper bound for the number of
    5832             :  * multiplications we are willing to pay while developing rows/columns before
    5833             :  * switching to Gaussian elimination */
    5834             : static GEN
    5835          91 : det_develop(GEN M, long max, double bound)
    5836             : {
    5837          91 :   pari_sp av = avma;
    5838          91 :   long i,j, n = lg(M)-1, lbest = max+2, best_col = 0, best_row = 0;
    5839          91 :   GEN best = NULL;
    5840             : 
    5841          91 :   if (bound < 1.) return det_bareiss(M); /* too costly now */
    5842             : 
    5843          91 :   switch(n)
    5844             :   {
    5845           0 :     case 0: return gen_1;
    5846           0 :     case 1: return gcopy(gcoeff(M,1,1));
    5847          14 :     case 2: return RgM_det2(M);
    5848             :   }
    5849          77 :   if (max > ((n+2)>>1)) max = (n+2)>>1;
    5850         322 :   for (j = 1; j <= n; j++)
    5851             :   {
    5852         259 :     pari_sp av2 = avma;
    5853         259 :     GEN v = col_count_non_zero(M, j, max);
    5854             :     long lv;
    5855         259 :     if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
    5856          14 :     if (lv == 1) { avma = av; return gen_0; }
    5857          14 :     if (lv == 2) {
    5858          14 :       avma = av;
    5859          14 :       return gerepileupto(av, coeff_det(M,v[1],j,max,bound));
    5860             :     }
    5861           0 :     best = v; lbest = lv; best_col = j;
    5862             :   }
    5863         308 :   for (i = 1; i <= n; i++)
    5864             :   {
    5865         245 :     pari_sp av2 = avma;
    5866         245 :     GEN v = row_count_non_zero(M, i, max);
    5867             :     long lv;
    5868         245 :     if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
    5869           0 :     if (lv == 1) { avma = av; return gen_0; }
    5870           0 :     if (lv == 2) {
    5871           0 :       avma = av;
    5872           0 :       return gerepileupto(av, coeff_det(M,i,v[1],max,bound));
    5873             :     }
    5874           0 :     best = v; lbest = lv; best_row = i;
    5875             :   }
    5876          63 :   if (best_row)
    5877             :   {
    5878           0 :     double d = lbest-1;
    5879           0 :     GEN s = NULL;
    5880             :     long k;
    5881           0 :     bound /= d*d*d;
    5882           0 :     for (k = 1; k < lbest; k++)
    5883             :     {
    5884           0 :       GEN c = coeff_det(M, best_row, best[k], max, bound);
    5885           0 :       s = s? gadd(s, c): c;
    5886             :     }
    5887           0 :     return gerepileupto(av, s);
    5888             :   }
    5889          63 :   if (best_col)
    5890             :   {
    5891           0 :     double d = lbest-1;
    5892           0 :     GEN s = NULL;
    5893             :     long k;
    5894           0 :     bound /= d*d*d;
    5895           0 :     for (k = 1; k < lbest; k++)
    5896             :     {
    5897           0 :       GEN c = coeff_det(M, best[k], best_col, max, bound);
    5898           0 :       s = s? gadd(s, c): c;
    5899             :     }
    5900           0 :     return gerepileupto(av, s);
    5901             :   }
    5902          63 :   return det_bareiss(M);
    5903             : }
    5904             : 
    5905             : /* area of parallelogram bounded by (v1,v2) */
    5906             : static GEN
    5907       51744 : parallelogramarea(GEN v1, GEN v2)
    5908       51744 : { return gsub(gmul(gnorml2(v1), gnorml2(v2)), gsqr(RgV_dotproduct(v1, v2))); }
    5909             : 
    5910             : /* Square of Hadamard bound for det(a), a square matrix.
    5911             :  * Slightly improvement: instead of using the column norms, use the area of
    5912             :  * the parallelogram formed by pairs of consecutive vectors */
    5913             : GEN
    5914       16793 : RgM_Hadamard(GEN a)
    5915             : {
    5916       16793 :   pari_sp av = avma;
    5917       16793 :   long n = lg(a)-1, i;
    5918             :   GEN B;
    5919       16793 :   if (n == 0) return gen_1;
    5920       16793 :   if (n == 1) return gsqr(gcoeff(a,1,1));
    5921       16793 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    5922       16793 :   B = gen_1;
    5923       68537 :   for (i = 1; i <= n/2; i++)
    5924       51744 :     B = gmul(B, parallelogramarea(gel(a,2*i-1), gel(a,2*i)));
    5925       16793 :   if (odd(n)) B = gmul(B, gnorml2(gel(a, n)));
    5926       16793 :   return gerepileuptoint(av, ceil_safe(B));
    5927             : }
    5928             : 
    5929             : /* If B=NULL, assume B=A' */
    5930             : static GEN
    5931       52780 : ZM_det_slice(GEN A, GEN P, GEN *mod)
    5932             : {
    5933       52780 :   pari_sp av = avma;
    5934       52780 :   long i, n = lg(P)-1;
    5935             :   GEN H, T;
    5936       52780 :   if (n == 1)
    5937             :   {
    5938       41520 :     ulong Hp, p = uel(P,1);
    5939       41520 :     GEN a = ZM_to_Flm(A, p);
    5940       41518 :     Hp = Flm_det_sp(a, p);
    5941       41521 :     avma = av;
    5942       41521 :     *mod = utoi(p); return utoi(Hp);
    5943             :   }
    5944       11260 :   T = ZV_producttree(P);
    5945       11260 :   A = ZM_nv_mod_tree(A, P, T);
    5946       11259 :   H = cgetg(n+1, t_VECSMALL);
    5947       39860 :   for(i=1; i <= n; i++)
    5948             :   {
    5949       28600 :     ulong p = P[i];
    5950       28600 :     GEN a = gel(A,i);
    5951       28600 :     H[i] = Flm_det_sp(a, p);
    5952             :   }
    5953       11260 :   H = ZV_chinese_tree(H, P, T, ZV_chinesetree(P,T));
    5954       11258 :   *mod = gmael(T, lg(T)-1, 1);
    5955       11258 :   gerepileall(av, 2, &H, mod);
    5956       11260 :   return H;
    5957             : }
    5958             : 
    5959             : GEN
    5960       52782 : ZM_det_worker(GEN P, GEN A)
    5961             : {
    5962       52782 :   GEN V = cgetg(3, t_VEC);
    5963       52777 :   gel(V,1) = ZM_det_slice(A, P, &gel(V,2));
    5964       52777 :   return V;
    5965             : }
    5966             : 
    5967             : /* assume dim(a) = n > 0 */
    5968             : static GEN
    5969       29736 : ZM_det_i(GEN M, long n)
    5970             : {
    5971       29736 :   const long DIXON_THRESHOLD = 40;
    5972       29736 :   pari_sp av = avma, av2;
    5973             :   long i;
    5974       29736 :   ulong p, Dp = 1;
    5975             :   forprime_t S;
    5976             :   pari_timer ti;
    5977             :   GEN H, D, mod, h, q, v, worker;
    5978       29736 :   if (n == 1) return icopy(gcoeff(M,1,1));
    5979       28903 :   if (n == 2) return ZM_det2(M);
    5980       19726 :   if (n == 3) return ZM_det3(M);
    5981       16793 :   if (DEBUGLEVEL >=4) timer_start(&ti);
    5982       16793 :   h = RgM_Hadamard(M);
    5983       16793 :   if (!signe(h)) { avma = av; return gen_0; }
    5984       16793 :   h = sqrti(h); q = gen_1;
    5985       16793 :   init_modular_big(&S);
    5986       16793 :   p = 0; /* -Wall */
    5987       33586 :   while( cmpii(q, h) <= 0 && (p = u_forprime_next(&S)) )
    5988             :   {
    5989       16793 :     av2 = avma; Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    5990       16793 :     avma = av2;
    5991       16793 :     if (Dp) break;
    5992           0 :     q = muliu(q, p);
    5993             :   }
    5994       16793 :   if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    5995       16793 :   if (!Dp) { avma = av; return gen_0; }
    5996       16793 :   if (n <= DIXON_THRESHOLD)
    5997       16793 :     D = q;
    5998             :   else
    5999             :   {
    6000           0 :     av2 = avma;
    6001           0 :     v = cgetg(n+1, t_COL);
    6002           0 :     gel(v, 1) = gen_1; /* ensure content(v) = 1 */
    6003           0 :     for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    6004           0 :     D = Q_denom(ZM_gauss(M, v));
    6005           0 :     if (expi(D) < expi(h) >> 1)
    6006             :     { /* First try unlucky, try once more */
    6007           0 :       for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    6008           0 :       D = lcmii(D, Q_denom(ZM_gauss(M, v)));
    6009             :     }
    6010           0 :     D = gerepileuptoint(av2, D);
    6011           0 :     if (q != gen_1) D = lcmii(D, q);
    6012             :   }
    6013             :   /* determinant is a multiple of D */
    6014       16793 :   if (DEBUGLEVEL >=4)
    6015           0 :     timer_printf(&ti,"ZM_det: Dixon %ld/%ld bits",expi(D),expi(h));
    6016       16793 :   h = divii(h, D);
    6017       16793 :   worker = strtoclosure("_ZM_det_worker", 1, M);
    6018       16793 :   H = gen_crt("ZM_det", worker, D, expi(h)+1, lg(M)-1, &mod, ZV_chinese, NULL);
    6019       16793 :   if (D) H = Fp_div(H, D, mod);
    6020       16793 :   H = Fp_center(H, mod, shifti(mod,-1));
    6021       16793 :   if (D) H = mulii(H, D);
    6022       16793 :   return gerepileuptoint(av, H);
    6023             : }
    6024             : 
    6025             : static GEN
    6026        1505 : RgM_det_FpM(GEN a, GEN p)
    6027             : {
    6028        1505 :   pari_sp av = avma;
    6029             :   ulong pp, d;
    6030        1505 :   a = RgM_Fp_init(a,p,&pp);
    6031        1505 :   switch(pp)
    6032             :   {
    6033          63 :   case 0: return gerepileupto(av, Fp_to_mod(FpM_det(a,p),p)); break;
    6034          14 :   case 2: d = F2m_det(a); break;
    6035        1428 :   default:d = Flm_det_sp(a, pp); break;
    6036             :   }
    6037        1442 :   avma = av; return mkintmodu(d, pp);
    6038             : }
    6039             : 
    6040             : static GEN
    6041          28 : RgM_det_FqM(GEN x, GEN pol, GEN p)
    6042             : {
    6043          28 :   pari_sp av = avma;
    6044          28 :   GEN T = RgX_to_FpX(pol, p);
    6045          28 :   GEN b = FqM_det(RgM_to_FqM(x, T, p), T, p);
    6046          28 :   if (!b) { avma = av; return NULL; }
    6047          28 :   return gerepilecopy(av, mkpolmod(FpX_to_mod(b, p), FpX_to_mod(T, p)));
    6048             : }
    6049             : 
    6050             : #define code(t1,t2) ((t1 << 6) | t2)
    6051             : static GEN
    6052       10126 : RgM_det_fast(GEN x)
    6053             : {
    6054             :   GEN p, pol;
    6055             :   long pa;
    6056       10126 :   long t = RgM_type(x, &p,&pol,&pa);
    6057       10126 :   switch(t)
    6058             :   {
    6059         273 :     case t_INT:    return ZM_det(x);
    6060          21 :     case t_FRAC:   return QM_det(x);
    6061          49 :     case t_FFELT:  return FFM_det(x, pol);
    6062        1505 :     case t_INTMOD: return RgM_det_FpM(x, p);
    6063             :     case code(t_POLMOD, t_INTMOD):
    6064          28 :                    return RgM_det_FqM(x, pol, p);
    6065        8250 :     default:       return NULL;
    6066             :   }
    6067             : }
    6068             : #undef code
    6069             : 
    6070             : static long
    6071          77 : det_init_max(long n)
    6072             : {
    6073          77 :   if (n > 100) return 0;
    6074          77 :   if (n > 50) return 1;
    6075          77 :   if (n > 30) return 4;
    6076          77 :   return 7;
    6077             : }
    6078             : 
    6079             : GEN
    6080       11209 : det(GEN a)
    6081             : {
    6082       11209 :   long n = lg(a)-1;
    6083             :   double B;
    6084             :   GEN data, b;
    6085             :   pivot_fun pivot;
    6086             : 
    6087       11209 :   if (typ(a)!=t_MAT) pari_err_TYPE("det",a);
    6088       11209 :   if (!n) return gen_1;
    6089       11167 :   if (n != nbrows(a)) pari_err_DIM("det");
    6090       11160 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    6091       10966 :   if (n == 2) return RgM_det2(a);
    6092       10126 :   b = RgM_det_fast(a);
    6093       10126 :   if (b) return b;
    6094        8250 :   pivot = get_pivot_fun(a, a, &data);
    6095        8250 :   if (pivot != gauss_get_pivot_NZ) return det_simple_gauss(a, data, pivot);
    6096          77 :   B = (double)n;
    6097          77 :   return det_develop(a, det_init_max(n), B*B*B);
    6098             : }
    6099             : 
    6100             : GEN
    6101       29743 : ZM_det(GEN a)
    6102             : {
    6103       29743 :   long n = lg(a)-1;
    6104       29743 :   if (!n) return gen_1;
    6105       29736 :   return ZM_det_i(a, n);
    6106             : }
    6107             : 
    6108             : GEN
    6109          21 : QM_det(GEN M)
    6110             : {
    6111          21 :   pari_sp av = avma;
    6112          21 :   GEN cM, pM = Q_primitive_part(M, &cM);
    6113          21 :   GEN b = ZM_det(pM);
    6114          21 :   if (cM) b = gmul(b, gpowgs(cM, lg(M)-1));
    6115          21 :   return gerepileupto(av, b);
    6116             : }
    6117             : /* return a solution of congruence system sum M_{ i,j } X_j = Y_i mod D_i
    6118             :  * If ptu1 != NULL, put in *ptu1 a Z-basis of the homogeneous system */
    6119             : static GEN
    6120       43799 : gaussmoduloall(GEN M, GEN D, GEN Y, GEN *ptu1)
    6121             : {
    6122       43799 :   pari_sp av = avma;
    6123             :   long n, m, j, l, lM;
    6124             :   GEN delta, H, U, u1, u2, x;
    6125             : 
    6126       43799 :   if (typ(M)!=t_MAT) pari_err_TYPE("gaussmodulo",M);
    6127       43799 :   lM = lg(M);
    6128       43799 :   if (lM == 1)
    6129             :   {
    6130          28 :     switch(typ(Y))
    6131             :     {
    6132           7 :       case t_INT: break;
    6133          14 :       case t_COL: if (lg(Y) != 1) pari_err_DIM("gaussmodulo");
    6134          14 :                   break;
    6135           7 :       default: pari_err_TYPE("gaussmodulo",Y);
    6136             :     }
    6137          21 :     switch(typ(D))
    6138             :     {
    6139           7 :       case t_INT: break;
    6140           7 :       case t_COL: if (lg(D) != 1) pari_err_DIM("gaussmodulo");
    6141           7 :                   break;
    6142           7 :       default: pari_err_TYPE("gaussmodulo",D);
    6143             :     }
    6144          14 :     if (ptu1) *ptu1 = cgetg(1, t_MAT);
    6145          14 :     return gen_0;
    6146             :   }
    6147       43771 :   n = nbrows(M);
    6148       43771 :   switch(typ(D))
    6149             :   {
    6150             :     case t_COL:
    6151          14 :       if (lg(D)-1!=n) pari_err_DIM("gaussmodulo");
    6152          14 :       delta = diagonal_shallow(D); break;
    6153       43757 :     case t_INT: delta = scalarmat_shallow(D,n); break;
    6154           0 :     default: pari_err_TYPE("gaussmodulo",D);
    6155             :       return NULL; /* LCOV_EXCL_LINE */
    6156             :   }
    6157       43771 :   switch(typ(Y))
    6158             :   {
    6159       43757 :     case t_INT: Y = const_col(n, Y); break;
    6160             :     case t_COL:
    6161          14 :       if (lg(Y)-1!=n) pari_err_DIM("gaussmodulo");
    6162          14 :       break;
    6163           0 :     default: pari_err_TYPE("gaussmodulo",Y);
    6164             :       return NULL; /* LCOV_EXCL_LINE */
    6165             :   }
    6166       43771 :   H = ZM_hnfall_i(shallowconcat(M,delta), &U, 1);
    6167       43771 :   Y = hnf_solve(H,Y); if (!Y) return gen_0;
    6168       43771 :   l = lg(H); /* may be smaller than lM if some moduli are 0 */
    6169       43771 :   n = l-1;
    6170       43771 :   m = lg(U)-1 - n;
    6171       43771 :   u1 = cgetg(m+1,t_MAT);
    6172       43771 :   u2 = cgetg(n+1,t_MAT);
    6173       43771 :   for (j=1; j<=m; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u1,j) = c; }
    6174       43771 :   U += m;
    6175       43771 :   for (j=1; j<=n; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u2,j) = c; }
    6176             :   /*       (u1 u2)
    6177             :    * (M D) (*  * ) = (0 H) */
    6178       43771 :   u1 = ZM_lll(u1, 0.75, LLL_INPLACE);
    6179       43771 :   Y = ZM_ZC_mul(u2,Y);
    6180       43771 :   x = ZC_reducemodmatrix(Y, u1);
    6181       43771 :   if (!ptu1) x = gerepileupto(av, x);
    6182             :   else
    6183             :   {
    6184       43764 :     gerepileall(av, 2, &x, &u1);
    6185       43764 :     *ptu1 = u1;
    6186             :   }
    6187       43771 :   return x;
    6188             : }
    6189             : 
    6190             : GEN
    6191       43799 : matsolvemod0(GEN M, GEN D, GEN Y, long flag)
    6192             : {
    6193             :   pari_sp av;
    6194             :   GEN p1,y;
    6195             : 
    6196       43799 :   if (!flag) return gaussmoduloall(M,D,Y,NULL);
    6197             : 
    6198       43792 :   av=avma; y = cgetg(3,t_VEC);
    6199       43792 :   p1 = gaussmoduloall(M,D,Y, (GEN*)y+2);
    6200       43778 :   if (p1==gen_0) { avma=av; return gen_0; }
    6201       43764 :   gel(y,1) = p1; return y;
    6202             : }
    6203             : 
    6204             : GEN
    6205           0 : gaussmodulo2(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,1); }
    6206             : 
    6207             : GEN
    6208           0 : gaussmodulo(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,0); }

Generated by: LCOV version 1.11