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

Generated by: LCOV version 1.11