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 21059-cbe0d6a) Lines: 3192 3358 95.1 %
Date: 2017-09-22 06:24:58 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          10 : gerepile_gauss(GEN x,long k,long t,pari_sp av, long j, GEN c)
      85             : {
      86          10 :   pari_sp tetpil = avma, A, bot;
      87          10 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      88             :   size_t dec;
      89             : 
      90          10 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot. k=%ld, n=%ld",k,n);
      91         375 :   for (u=t+1; u<=m; u++)
      92         365 :     if (u==j || !c[u]) COPY(gcoeff(x,u,k));
      93         710 :   for (u=1; u<=m; u++)
      94         700 :     if (u==j || !c[u])
      95         690 :       for (i=k+1; i<=n; i++) COPY(gcoeff(x,u,i));
      96             : 
      97          10 :   (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
      98          10 :   bot = pari_mainstack->bot;
      99         375 :   for (u=t+1; u<=m; u++)
     100         365 :     if (u==j || !c[u])
     101             :     {
     102         365 :       A=(pari_sp)coeff(x,u,k);
     103         365 :       if (A<av && A>=bot) coeff(x,u,k)+=dec;
     104             :     }
     105         710 :   for (u=1; u<=m; u++)
     106         700 :     if (u==j || !c[u])
     107       47610 :       for (i=k+1; i<=n; i++)
     108             :       {
     109       46920 :         A=(pari_sp)coeff(x,u,i);
     110       46920 :         if (A<av && A>=bot) coeff(x,u,i)+=dec;
     111             :       }
     112          10 : }
     113             : 
     114             : /*******************************************************************/
     115             : /*                                                                 */
     116             : /*                         GENERIC                                 */
     117             : /*                                                                 */
     118             : /*******************************************************************/
     119             : GEN
     120        2522 : gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff)
     121             : {
     122        2522 :   pari_sp av0 = avma, av, tetpil;
     123             :   GEN y, c, d;
     124             :   long i, j, k, r, t, n, m;
     125             : 
     126        2522 :   n=lg(x)-1; if (!n) return cgetg(1,t_MAT);
     127        2522 :   m=nbrows(x); r=0;
     128        2522 :   x = RgM_shallowcopy(x);
     129        2522 :   c = zero_zv(m);
     130        2522 :   d=new_chunk(n+1);
     131        2522 :   av=avma;
     132       20491 :   for (k=1; k<=n; k++)
     133             :   {
     134      284889 :     for (j=1; j<=m; j++)
     135      278376 :       if (!c[j])
     136             :       {
     137       87389 :         gcoeff(x,j,k) = ff->red(E, gcoeff(x,j,k));
     138       87389 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     139             :       }
     140       17997 :     if (j>m)
     141             :     {
     142        6513 :       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        6485 :       r++; d[k]=0;
     150       49201 :       for(j=1; j<k; j++)
     151       42716 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
     152             :     }
     153             :     else
     154             :     {
     155       11484 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     156       11484 :       c[j] = k; d[k] = j;
     157       11484 :       gcoeff(x,j,k) = ff->s(E,-1);
     158       11484 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     159      411169 :       for (t=1; t<=m; t++)
     160             :       {
     161      399685 :         if (t==j) continue;
     162             : 
     163      388201 :         piv = ff->red(E,gcoeff(x,t,k));
     164      388201 :         if (ff->equal0(piv)) continue;
     165             : 
     166       59067 :         gcoeff(x,t,k) = ff->s(E,0);
     167      751196 :         for (i=k+1; i<=n; i++)
     168      692129 :            gcoeff(x,t,i) = ff->add(E, gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     169       59067 :         if (gc_needed(av,1))
     170          10 :           gen_gerepile_gauss_ker(x,k,t,av,E,ff->red);
     171             :       }
     172             :     }
     173             :   }
     174        2494 :   if (deplin) { avma = av0; return NULL; }
     175             : 
     176        2466 :   tetpil=avma; y=cgetg(r+1,t_MAT);
     177        8951 :   for (j=k=1; j<=r; j++,k++)
     178             :   {
     179        6485 :     GEN C = cgetg(n+1,t_COL);
     180        6485 :     GEN g0 = ff->s(E,0), g1 = ff->s(E,1);
     181        6485 :     gel(y,j) = C; while (d[k]) k++;
     182       49201 :     for (i=1; i<k; i++)
     183       42716 :       if (d[i])
     184             :       {
     185       16994 :         GEN p1=gcoeff(x,d[i],k);
     186       16994 :         gel(C,i) = ff->red(E,p1); gunclone(p1);
     187             :       }
     188             :       else
     189       25722 :         gel(C,i) = g0;
     190        6485 :     gel(C,k) = g1; for (i=k+1; i<=n; i++) gel(C,i) = g0;
     191             :   }
     192        2466 :   return gerepile(av0,tetpil,y);
     193             : }
     194             : 
     195             : GEN
     196        1326 : gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff)
     197             : {
     198             :   pari_sp av;
     199             :   GEN c, d;
     200        1326 :   long i, j, k, r, t, m, n = lg(x)-1;
     201             : 
     202        1326 :   if (!n) { *rr = 0; return NULL; }
     203             : 
     204        1326 :   m=nbrows(x); r=0;
     205        1326 :   d = cgetg(n+1, t_VECSMALL);
     206        1326 :   x = RgM_shallowcopy(x);
     207        1326 :   c = zero_zv(m);
     208        1326 :   av=avma;
     209        6476 :   for (k=1; k<=n; k++)
     210             :   {
     211       49753 :     for (j=1; j<=m; j++)
     212       49206 :       if (!c[j])
     213             :       {
     214       40058 :         gcoeff(x,j,k) = ff->red(E,gcoeff(x,j,k));
     215       40058 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     216             :       }
     217        5150 :     if (j>m) { r++; d[k]=0; }
     218             :     else
     219             :     {
     220        4603 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     221        4603 :       GEN g0 = ff->s(E,0);
     222        4603 :       c[j] = k; d[k] = j;
     223        4603 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     224       34648 :       for (t=1; t<=m; t++)
     225             :       {
     226       30045 :         if (c[t]) continue; /* already a pivot on that line */
     227             : 
     228       16583 :         piv = ff->red(E,gcoeff(x,t,k));
     229       16583 :         if (ff->equal0(piv)) continue;
     230        6468 :         gcoeff(x,t,k) = g0;
     231      101388 :         for (i=k+1; i<=n; i++)
     232       94920 :           gcoeff(x,t,i) = ff->add(E,gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
     233        6468 :         if (gc_needed(av,1))
     234          10 :           gerepile_gauss(x,k,t,av,j,c);
     235             :       }
     236        4603 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = g0; /* dummy */
     237             :     }
     238             :   }
     239        1326 :   *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      303506 : gen_matcolmul_i(GEN A, GEN B, ulong lgA, ulong l,
     367             :                 void *E, const struct bb_field *ff)
     368             : {
     369      303506 :   GEN C = cgetg(l, t_COL);
     370             :   ulong i;
     371     2060821 :   for (i = 1; i < l; i++) {
     372     1757315 :     pari_sp av = avma;
     373     1757315 :     GEN e = ff->mul(E, gcoeff(A, i, 1), gel(B, 1));
     374             :     ulong k;
     375    10420599 :     for(k = 2; k < lgA; k++)
     376     8663284 :       e = ff->add(E, e, ff->mul(E, gcoeff(A, i, k), gel(B, k)));
     377     1757315 :     gel(C, i) = gerepileupto(av, ff->red(E, e));
     378             :   }
     379      303506 :   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        9947 : gen_matmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     395             : {
     396        9947 :   ulong j, l, lgA, lgB = lg(B);
     397             :   GEN C;
     398        9947 :   if (lgB == 1)
     399           0 :     return cgetg(1, t_MAT);
     400        9947 :   lgA = lg(A);
     401        9947 :   if (lgA != (ulong)lgcols(B))
     402           0 :     pari_err_OP("operation 'gen_matmul'", A, B);
     403        9947 :   if (lgA == 1)
     404           0 :     return zeromat(0, lgB - 1);
     405        9947 :   l = lgcols(A);
     406        9947 :   C = cgetg(lgB, t_MAT);
     407      152453 :   for(j = 1; j < lgB; j++)
     408      142506 :     gel(C, j) = gen_matcolmul_i(A, gel(B, j), lgA, l, E, ff);
     409        9947 :   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       84264 : image_from_pivot(GEN x, GEN d, long r)
     515             : {
     516             :   GEN y;
     517             :   long j, k;
     518             : 
     519       84264 :   if (!d) return gcopy(x);
     520             :   /* d left on stack for efficiency */
     521       82766 :   r = lg(x)-1 - r; /* = dim Im(x) */
     522       82766 :   y = cgetg(r+1,t_MAT);
     523      720693 :   for (j=k=1; j<=r; k++)
     524      637927 :     if (d[k]) gel(y,j++) = gcopy(gel(x,k));
     525       82766 :   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      505006 : indexcompl(GEN v, long n) {
     553      505006 :   long i, j, k, m = lg(v) - 1;
     554      505006 :   GEN w = cgetg(n - m + 1, t_VECSMALL);
     555     9222871 :   for (i = j = k = 1; i <= n; i++) {
     556     8717865 :     if (j <= m && v[j] == i)
     557     3960760 :       j++;
     558             :     else
     559     4757105 :       w[k++] = i;
     560             :   }
     561      505006 :   return w;
     562             : }
     563             : 
     564             : static GEN
     565      181164 : Flm_rsolve_upper_1(GEN U, GEN B, ulong p) {
     566      181164 :   return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p);
     567             : }
     568             : 
     569             : static GEN
     570      539879 : Flm_rsolve_upper_2(GEN U, GEN B, ulong p) {
     571      539879 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     572      539879 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     573      539879 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     574      539879 :   GEN B1 = rowslice(B, 1, 1);
     575      539879 :   GEN B2 = rowslice(B, 2, 2);
     576      539879 :   GEN X2 = Flm_Fl_mul(B2, dinv, p);
     577      539879 :   GEN X1 = Flm_Fl_mul(Flm_sub(B1, Flm_Fl_mul(X2, b, p), p),
     578             :                       ainv, p);
     579      539879 :   return vconcat(X1, X2);
     580             : }
     581             : 
     582             : /* solve U*X = B,  U upper triangular and invertible */
     583             : static GEN
     584     1328666 : Flm_rsolve_upper(GEN U, GEN B, ulong p) {
     585     1328666 :   long n = lg(U) - 1, n1;
     586             :   GEN U1, U2, U11, U12, U22;
     587             :   GEN B1, B2, X1, X2, X;
     588     1328666 :   pari_sp av = avma;
     589             : 
     590     1328666 :   if (n == 0)
     591           0 :     return B;
     592     1328666 :   if (n == 1)
     593      181164 :     return Flm_rsolve_upper_1(U, B, p);
     594     1147502 :   if (n == 2)
     595      539879 :     return Flm_rsolve_upper_2(U, B, p);
     596      607623 :   n1 = (n + 1)/2;
     597      607623 :   U1 = vecslice(U, 1, n1);
     598      607623 :   U2 = vecslice(U, n1 + 1, n);
     599      607623 :   U11 = rowslice(U1, 1, n1);
     600      607623 :   U12 = rowslice(U2, 1, n1);
     601      607623 :   U22 = rowslice(U2, n1 + 1, n);
     602      607623 :   B1 = rowslice(B, 1, n1);
     603      607623 :   B2 = rowslice(B, n1 + 1, n);
     604      607623 :   X2 = Flm_rsolve_upper(U22, B2, p);
     605      607623 :   if (gc_needed(av, 1))
     606           0 :     gerepileall(av, 4, &B1, &U11, &U12, &X2);
     607      607623 :   B1 = Flm_sub(B1, Flm_mul(U12, X2, p), p);
     608      607623 :   if (gc_needed(av, 1))
     609           0 :     gerepileall(av, 3, &B1, &U11, &X2);
     610      607623 :   X1 = Flm_rsolve_upper(U11, B1, p);
     611      607623 :   X = vconcat(X1, X2);
     612      607623 :   if (gc_needed(av, 1))
     613          13 :     X = gerepilecopy(av, X);
     614      607623 :   return X;
     615             : }
     616             : 
     617             : static GEN
     618      273972 : Flm_lsolve_upper_1(GEN U, GEN B, ulong p) {
     619      273972 :   return Flm_Fl_mul(B, Fl_inv(ucoeff(U, 1, 1), p), p);
     620             : }
     621             : 
     622             : static GEN
     623      721974 : Flm_lsolve_upper_2(GEN U, GEN B, ulong p) {
     624      721974 :   ulong a = ucoeff(U, 1, 1), b = ucoeff(U, 1, 2), d = ucoeff(U, 2, 2);
     625      721974 :   ulong D = Fl_mul(a, d, p), Dinv = Fl_inv(D, p);
     626      721974 :   ulong ainv = Fl_mul(d, Dinv, p), dinv = Fl_mul(a, Dinv, p);
     627      721974 :   GEN B1 = vecslice(B, 1, 1);
     628      721974 :   GEN B2 = vecslice(B, 2, 2);
     629      721974 :   GEN X1 = Flm_Fl_mul(B1, ainv, p);
     630      721974 :   GEN X2 = Flm_Fl_mul(Flm_sub(B2, Flm_Fl_mul(X1, b, p), p),
     631             :                       dinv, p);
     632      721974 :   return shallowconcat(X1, X2);
     633             : }
     634             : 
     635             : /* solve X*U = B,  U upper triangular and invertible */
     636             : static GEN
     637     1691985 : Flm_lsolve_upper(GEN U, GEN B, ulong p) {
     638     1691985 :   long n = lg(U) - 1, n1;
     639             :   GEN U1, U2, U11, U12, U22;
     640             :   GEN B1, B2, X1, X2, X;
     641     1691985 :   pari_sp av = avma;
     642             : 
     643     1691985 :   if (n == 0)
     644           0 :     return B;
     645     1691985 :   if (n == 1)
     646      273972 :     return Flm_lsolve_upper_1(U, B, p);
     647     1418013 :   if (n == 2)
     648      721974 :     return Flm_lsolve_upper_2(U, B, p);
     649      696039 :   n1 = (n + 1)/2;
     650      696039 :   U1 = vecslice(U, 1, n1);
     651      696039 :   U2 = vecslice(U, n1 + 1, n);
     652      696039 :   U11 = rowslice(U1, 1, n1);
     653      696039 :   U12 = rowslice(U2, 1, n1);
     654      696039 :   U22 = rowslice(U2, n1 + 1, n);
     655      696039 :   B1 = vecslice(B, 1, n1);
     656      696039 :   B2 = vecslice(B, n1 + 1, n);
     657      696039 :   X1 = Flm_lsolve_upper(U11, B1, p);
     658      696039 :   if (gc_needed(av, 1))
     659           0 :     gerepileall(av, 4, &B2, &U12, &U22, &X1);
     660      696039 :   B2 = Flm_sub(B2, Flm_mul(X1, U12, p), p);
     661      696039 :   if (gc_needed(av, 1))
     662           0 :     gerepileall(av, 3, &B2, &U22, &X1);
     663      696039 :   X2 = Flm_lsolve_upper(U22, B2, p);
     664      696039 :   X = shallowconcat(X1, X2);
     665      696039 :   if (gc_needed(av, 1))
     666           0 :     X = gerepilecopy(av, X);
     667      696039 :   return X;
     668             : }
     669             : 
     670             : static GEN
     671     1327233 : Flm_rsolve_lower_unit_2(GEN L, GEN A, ulong p) {
     672     1327233 :   GEN X1 = rowslice(A, 1, 1);
     673     1327233 :   GEN X2 = Flm_sub(rowslice(A, 2, 2),
     674     1327233 :                    Flm_Fl_mul(X1, ucoeff(L, 2, 1), p), p);
     675     1327233 :   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     3122479 : Flm_rsolve_lower_unit(GEN L, GEN A, ulong p) {
     682     3122479 :   long m = lg(L) - 1, m1, n;
     683             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
     684     3122479 :   pari_sp av = avma;
     685             : 
     686     3122479 :   if (m == 0) return zero_Flm(0, lg(A) - 1);
     687     3122479 :   if (m == 1) return rowslice(A, 1, 1);
     688     2679574 :   if (m == 2) return Flm_rsolve_lower_unit_2(L, A, p);
     689     1352341 :   m1 = (m + 1)/2;
     690     1352341 :   n = nbrows(L);
     691     1352341 :   L1 = vecslice(L, 1, m1);
     692     1352341 :   L2 = vecslice(L, m1 + 1, m);
     693     1352341 :   L11 = rowslice(L1, 1, m1);
     694     1352341 :   L21 = rowslice(L1, m1 + 1, n);
     695     1352341 :   L22 = rowslice(L2, m1 + 1, n);
     696     1352341 :   A1 = rowslice(A, 1, m1);
     697     1352341 :   A2 = rowslice(A, m1 + 1, n);
     698     1352341 :   X1 = Flm_rsolve_lower_unit(L11, A1, p);
     699     1352341 :   if (gc_needed(av, 1))
     700           0 :     gerepileall(av, 4, &A2, &L21, &L22, &X1);
     701     1352341 :   A2 = Flm_sub(A2, Flm_mul(L21, X1, p), p);
     702     1352341 :   if (gc_needed(av, 1))
     703           6 :     gerepileall(av, 3, &A2, &L22, &X1);
     704     1352341 :   X2 = Flm_rsolve_lower_unit(L22, A2, p);
     705     1352341 :   X = vconcat(X1, X2);
     706     1352341 :   if (gc_needed(av, 1))
     707          57 :     X = gerepilecopy(av, X);
     708     1352341 :   return X;
     709             : }
     710             : 
     711             : static GEN
     712      881321 : Flm_lsolve_lower_unit_2(GEN L, GEN A, ulong p)
     713             : {
     714      881321 :   GEN X2 = vecslice(A, 2, 2);
     715      881321 :   GEN X1 = Flm_sub(vecslice(A, 1, 1),
     716      881321 :                    Flm_Fl_mul(X2, ucoeff(L, 2, 1), p), p);
     717      881321 :   return shallowconcat(X1, X2);
     718             : }
     719             : 
     720             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
     721             : static GEN
     722     2266031 : Flm_lsolve_lower_unit(GEN L, GEN A, ulong p)
     723             : {
     724     2266031 :   long m = lg(L) - 1, m1;
     725             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
     726     2266031 :   pari_sp av = avma;
     727             : 
     728     2266031 :   if (m <= 1) return A;
     729     1917144 :   if (m == 2) return Flm_lsolve_lower_unit_2(L, A, p);
     730     1035823 :   m1 = (m + 1)/2;
     731     1035823 :   L1 = vecslice(L, 1, m1);
     732     1035823 :   L2 = vecslice(L, m1 + 1, m);
     733     1035823 :   L11 = rowslice(L1, 1, m1);
     734     1035823 :   L21 = rowslice(L1, m1 + 1, m);
     735     1035823 :   L22 = rowslice(L2, m1 + 1, m);
     736     1035823 :   A1 = vecslice(A, 1, m1);
     737     1035823 :   A2 = vecslice(A, m1 + 1, m);
     738     1035823 :   X2 = Flm_lsolve_lower_unit(L22, A2, p);
     739     1035823 :   A1 = Flm_sub(A1, Flm_mul(X2, L21, p), p);
     740     1035823 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
     741     1035823 :   X1 = Flm_lsolve_lower_unit(L11, A1, p);
     742     1035823 :   X = shallowconcat(X1, X2);
     743     1035823 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
     744     1035823 :   return X;
     745             : }
     746             : 
     747             : /* destroy A */
     748             : static long
     749     1068023 : Flm_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     750             : {
     751     1068023 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc, u, v;
     752             : 
     753     1068023 :   if (P) *P = identity_perm(n);
     754     1068023 :   *R = cgetg(m + 1, t_VECSMALL);
     755     5445597 :   for (j = 1, pr = 0; j <= n; j++)
     756             :   {
     757     7719912 :     for (pr++, pc = 0; pr <= m; pr++)
     758             :     {
     759     7181347 :       for (k = j; k <= n; k++) { v = ucoeff(A, pr, k); if (!pc && v) pc = k; }
     760     7181347 :       if (pc) break;
     761             :     }
     762     4916139 :     if (!pc) break;
     763     4377574 :     (*R)[j] = pr;
     764     4377574 :     if (pc != j)
     765             :     {
     766     1047428 :       swap(gel(A, j), gel(A, pc));
     767     1047428 :       if (P) lswap((*P)[j], (*P)[pc]);
     768             :     }
     769     4377574 :     u = Fl_inv(ucoeff(A, pr, j), p);
     770    31280915 :     for (i = pr + 1; i <= m; i++)
     771             :     {
     772    26903341 :       v = Fl_mul(ucoeff(A, i, j), u, p);
     773    26903341 :       ucoeff(A, i, j) = v;
     774   130503078 :       for (k = j + 1; k <= n; k++)
     775   207199474 :         ucoeff(A, i, k) = Fl_sub(ucoeff(A, i, k),
     776   103599737 :                                  Fl_mul(ucoeff(A, pr, k), v, p), p);
     777             :     }
     778             :   }
     779     1068023 :   setlg(*R, j);
     780     1068023 :   *C = vecslice(A, 1, j - 1);
     781     1068023 :   if (U) *U = rowpermute(A, *R);
     782     1068023 :   return j - 1;
     783             : }
     784             : 
     785             : static const long Flm_CUP_LIMIT = 8;
     786             : 
     787             : static ulong
     788      816373 : Flm_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, ulong p)
     789             : {
     790      816373 :   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      816373 :   pari_sp av = avma;
     794             : 
     795      816373 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     796             :     /* destroy A; not called at the outermost recursion level */
     797      511580 :     return Flm_CUP_gauss(A, R, C, U, P, p);
     798      304793 :   m1 = (minss(m, n) + 1)/2;
     799      304793 :   A1 = rowslice(A, 1, m1);
     800      304793 :   A2 = rowslice(A, m1 + 1, m);
     801      304793 :   r1 = Flm_CUP(A1, &R1, &C1, &U1, &P1, p);
     802      304793 :   if (r1 == 0)
     803             :   {
     804        4886 :     r2 = Flm_CUP(A2, &R2, &C2, &U2, &P2, p);
     805        4886 :     *R = cgetg(r2 + 1, t_VECSMALL);
     806        4886 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
     807        4886 :     *C = vconcat(zero_Flm(m1, r2), C2);
     808        4886 :     *U = U2;
     809        4886 :     *P = P2;
     810        4886 :     r = r2;
     811             :   }
     812             :   else
     813             :   {
     814      299907 :     U11 = vecslice(U1, 1, r1);
     815      299907 :     U12 = vecslice(U1, r1 + 1, n);
     816      299907 :     T21 = vecslicepermute(A2, P1, 1, r1);
     817      299907 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
     818      299907 :     C21 = Flm_lsolve_upper(U11, T21, p);
     819      299907 :     if (gc_needed(av, 1))
     820           0 :       gerepileall(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
     821      299907 :     B2 = Flm_sub(T22, Flm_mul(C21, U12, p), p);
     822      299907 :     r2 = Flm_CUP(B2, &R2, &C2, &U2, &P2, p);
     823      299907 :     r = r1 + r2;
     824      299907 :     *R = cgetg(r + 1, t_VECSMALL);
     825      299907 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
     826      299907 :     for (;      i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
     827      299907 :     *C = shallowconcat(vconcat(C1, C21),
     828             :                        vconcat(zero_Flm(m1, r2), C2));
     829      299907 :     *U = shallowconcat(vconcat(U11, zero_Flm(r2, r1)),
     830             :                        vconcat(vecpermute(U12, P2), U2));
     831      299907 :     *P = cgetg(n + 1, t_VECSMALL);
     832      299907 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
     833      299907 :     for (; i <= n; i++)       (*P)[i] = P1[P2[i - r1] + r1];
     834             :   }
     835      304793 :   if (gc_needed(av, 1)) gerepileall(av, 4, R, C, U, P);
     836      304793 :   return r;
     837             : }
     838             : 
     839             : static ulong
     840      556443 : Flm_echelon_gauss(GEN A, GEN *R, GEN *C, ulong p)
     841      556443 : { return Flm_CUP_gauss(A, R, C, NULL, NULL, p); }
     842             : 
     843             : /* column echelon form */
     844             : static ulong
     845      921332 : Flm_echelon(GEN A, GEN *R, GEN *C, ulong p)
     846             : {
     847      921332 :   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      921332 :   pari_sp av = avma;
     851             : 
     852      921332 :   if (m < Flm_CUP_LIMIT || n < Flm_CUP_LIMIT)
     853      556443 :     return Flm_echelon_gauss(Flm_copy(A), R, C, p);
     854             : 
     855      364889 :   n1 = (n + 1)/2;
     856      364889 :   A1 = vecslice(A, 1, n1);
     857      364889 :   A2 = vecslice(A, n1 + 1, n);
     858      364889 :   r1 = Flm_echelon(A1, &R1, &C1, p);
     859      364889 :   if (!r1) return Flm_echelon(A2, R, C, p);
     860      307236 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
     861             : 
     862      304363 :   R1c = indexcompl(R1, m);
     863      304363 :   C11 = rowpermute(C1, R1);
     864      304363 :   C21 = rowpermute(C1, R1c);
     865      304363 :   A12 = rowpermute(A2, R1);
     866      304363 :   A22 = rowpermute(A2, R1c);
     867      304363 :   M12 = Flm_rsolve_lower_unit(C11, A12, p);
     868      304363 :   B2 = Flm_sub(A22, Flm_mul(C21, M12, p), p);
     869      304363 :   r2 = Flm_echelon(B2, &R2, &C2, p);
     870      304363 :   if (!r2) { *R = R1; *C = C1; r = r1; }
     871             :   else
     872             :   {
     873      277118 :     R2 = perm_mul(R1c, R2);
     874      277118 :     C2 = rowpermute(vconcat(zero_Flm(r1, r2), C2),
     875             :                     perm_inv(vecsmall_concat(R1, R1c)));
     876      277118 :     r = r1 + r2;
     877      277118 :     *R = cgetg(r + 1, t_VECSMALL);
     878      277118 :     *C = cgetg(r + 1, t_MAT);
     879     3323897 :     for (j = j1 = j2 = 1; j <= r; j++)
     880     3046779 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
     881             :       {
     882     1630895 :         gel(*C, j) = gel(C1, j1);
     883     1630895 :         (*R)[j] = R1[j1++];
     884             :       }
     885             :       else
     886             :       {
     887     1415884 :         gel(*C, j) = gel(C2, j2);
     888     1415884 :         (*R)[j] = R2[j2++];
     889             :       }
     890             :   }
     891      304363 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
     892      304363 :   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        5579 : FlxqM_rsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
     992             : {
     993        5579 :   GEN X1 = rowslice(A, 1, 1);
     994        5579 :   GEN X2 = FlxM_sub(rowslice(A, 2, 2),
     995        5579 :                     FlxqM_Flxq_mul(X1, gcoeff(L, 2, 1), T, p), p);
     996        5579 :   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       12530 : FlxqM_rsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
    1003             : {
    1004       12530 :   long m = lg(L) - 1, m1, n;
    1005             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
    1006       12530 :   pari_sp av = avma;
    1007             : 
    1008       12530 :   if (m == 0) return zeromat(0, lg(A) - 1);
    1009       12530 :   if (m == 1) return rowslice(A, 1, 1);
    1010        9597 :   if (m == 2) return FlxqM_rsolve_lower_unit_2(L, A, T, p);
    1011        4018 :   m1 = (m + 1)/2;
    1012        4018 :   n = nbrows(L);
    1013        4018 :   L1 = vecslice(L, 1, m1);
    1014        4018 :   L2 = vecslice(L, m1 + 1, m);
    1015        4018 :   L11 = rowslice(L1, 1, m1);
    1016        4018 :   L21 = rowslice(L1, m1 + 1, n);
    1017        4018 :   L22 = rowslice(L2, m1 + 1, n);
    1018        4018 :   A1 = rowslice(A, 1, m1);
    1019        4018 :   A2 = rowslice(A, m1 + 1, n);
    1020        4018 :   X1 = FlxqM_rsolve_lower_unit(L11, A1, T, p);
    1021        4018 :   A2 = FlxM_sub(A2, FlxqM_mul(L21, X1, T, p), p);
    1022        4018 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A2, &L22, &X1);
    1023        4018 :   X2 = FlxqM_rsolve_lower_unit(L22, A2, T, p);
    1024        4018 :   X = vconcat(X1, X2);
    1025        4018 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1026        4018 :   return X;
    1027             : }
    1028             : 
    1029             : static GEN
    1030        2142 : FlxqM_lsolve_lower_unit_2(GEN L, GEN A, GEN T, ulong p)
    1031             : {
    1032        2142 :   GEN X2 = vecslice(A, 2, 2);
    1033        2142 :   GEN X1 = FlxM_sub(vecslice(A, 1, 1),
    1034        2142 :                     FlxqM_Flxq_mul(X2, gcoeff(L, 2, 1), T, p), p);
    1035        2142 :   return shallowconcat(X1, X2);
    1036             : }
    1037             : 
    1038             : /* solve L*X = A,  L square lower triangular with ones on the diagonal */
    1039             : static GEN
    1040        5222 : FlxqM_lsolve_lower_unit(GEN L, GEN A, GEN T, ulong p)
    1041             : {
    1042        5222 :   long m = lg(L) - 1, m1;
    1043             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
    1044        5222 :   pari_sp av = avma;
    1045             : 
    1046        5222 :   if (m <= 1) return A;
    1047        3871 :   if (m == 2) return FlxqM_lsolve_lower_unit_2(L, A, T, p);
    1048        1729 :   m1 = (m + 1)/2;
    1049        1729 :   L1 = vecslice(L, 1, m1);
    1050        1729 :   L2 = vecslice(L, m1 + 1, m);
    1051        1729 :   L11 = rowslice(L1, 1, m1);
    1052        1729 :   L21 = rowslice(L1, m1 + 1, m);
    1053        1729 :   L22 = rowslice(L2, m1 + 1, m);
    1054        1729 :   A1 = vecslice(A, 1, m1);
    1055        1729 :   A2 = vecslice(A, m1 + 1, m);
    1056        1729 :   X2 = FlxqM_lsolve_lower_unit(L22, A2, T, p);
    1057        1729 :   A1 = FlxM_sub(A1, FlxqM_mul(X2, L21, T, p), p);
    1058        1729 :   if (gc_needed(av, 1)) gerepileall(av, 3, &A1, &L11, &X2);
    1059        1729 :   X1 = FlxqM_lsolve_lower_unit(L11, A1, T, p);
    1060        1729 :   X = shallowconcat(X1, X2);
    1061        1729 :   if (gc_needed(av, 1)) X = gerepilecopy(av, X);
    1062        1729 :   return X;
    1063             : }
    1064             : 
    1065             : /* destroy A */
    1066             : static long
    1067        7329 : FlxqM_CUP_gauss(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, GEN T, ulong p)
    1068             : {
    1069        7329 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc;
    1070             :   pari_sp av;
    1071             :   GEN u, v;
    1072             : 
    1073        7329 :   if (P) *P = identity_perm(n);
    1074        7329 :   *R = cgetg(m + 1, t_VECSMALL);
    1075        7329 :   av = avma;
    1076       14574 :   for (j = 1, pr = 0; j <= n; j++)
    1077             :   {
    1078       36281 :     for (pr++, pc = 0; pr <= m; pr++)
    1079             :     {
    1080      151340 :       for (k = j; k <= n; k++)
    1081             :       {
    1082      121415 :         v = Flx_rem(gcoeff(A, pr, k), T, p);
    1083      121415 :         gcoeff(A, pr, k) = v;
    1084      121415 :         if (!pc && lgpol(v) > 0) pc = k;
    1085             :       }
    1086       29925 :       if (pc) break;
    1087             :     }
    1088       13601 :     if (!pc) break;
    1089        7245 :     (*R)[j] = pr;
    1090        7245 :     if (pc != j)
    1091             :     {
    1092         903 :       swap(gel(A, j), gel(A, pc));
    1093         903 :       if (P) lswap((*P)[j], (*P)[pc]);
    1094             :     }
    1095        7245 :     u = Flxq_inv(gcoeff(A, pr, j), T, p);
    1096       34552 :     for (i = pr + 1; i <= m; i++)
    1097             :     {
    1098       27307 :       v = Flxq_mul(gcoeff(A, i, j), u, T, p);
    1099       27307 :       gcoeff(A, i, j) = v;
    1100       81046 :       for (k = j + 1; k <= n; k++)
    1101      107478 :         gcoeff(A, i, k) = Flx_sub(gcoeff(A, i, k),
    1102       53739 :                                   Flx_mul(gcoeff(A, pr, k), v, p), p);
    1103             :     }
    1104        7245 :     if (gc_needed(av, 2)) A = gerepilecopy(av, A);
    1105             :   }
    1106        7329 :   setlg(*R, j);
    1107        7329 :   *C = vecslice(A, 1, j - 1);
    1108        7329 :   if (U) *U = rowpermute(A, *R);
    1109        7329 :   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        6713 : FlxqM_echelon_gauss(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1169        6713 : { return FlxqM_CUP_gauss(A, R, C, NULL, NULL, T, p); }
    1170             : 
    1171             : /* column echelon form */
    1172             : static ulong
    1173       11725 : FlxqM_echelon(GEN A, GEN *R, GEN *C, GEN T, ulong p)
    1174             : {
    1175       11725 :   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       11725 :   pari_sp av = avma;
    1179             : 
    1180       11725 :   if (m < FlxqM_CUP_LIMIT || n < FlxqM_CUP_LIMIT)
    1181        6713 :     return FlxqM_echelon_gauss(shallowcopy(A), R, C, T, p);
    1182             : 
    1183        5012 :   n1 = (n + 1)/2;
    1184        5012 :   A1 = vecslice(A, 1, n1);
    1185        5012 :   A2 = vecslice(A, n1 + 1, n);
    1186        5012 :   r1 = FlxqM_echelon(A1, &R1, &C1, T, p);
    1187        5012 :   if (!r1) return FlxqM_echelon(A2, R, C, T, p);
    1188        4543 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
    1189        4452 :   R1c = indexcompl(R1, m);
    1190        4452 :   C11 = rowpermute(C1, R1);
    1191        4452 :   C21 = rowpermute(C1, R1c);
    1192        4452 :   A12 = rowpermute(A2, R1);
    1193        4452 :   A22 = rowpermute(A2, R1c);
    1194        4452 :   M12 = FlxqM_rsolve_lower_unit(C11, A12, T, p);
    1195        4452 :   B2 = FlxM_sub(A22, FlxqM_mul(C21, M12, T, p), p);
    1196        4452 :   r2 = FlxqM_echelon(B2, &R2, &C2, T, p);
    1197        4452 :   if (!r2) { *R = R1; *C = C1; r = r1; }
    1198             :   else
    1199             :   {
    1200        1561 :     R2 = perm_mul(R1c, R2);
    1201        1561 :     C2 = rowpermute(vconcat(zero_FlxM(r1, r2, get_Flx_var(T)), C2),
    1202             :                     perm_inv(vecsmall_concat(R1, R1c)));
    1203        1561 :     r = r1 + r2;
    1204        1561 :     *R = cgetg(r + 1, t_VECSMALL);
    1205        1561 :     *C = cgetg(r + 1, t_MAT);
    1206        9079 :     for (j = j1 = j2 = 1; j <= r; j++)
    1207             :     {
    1208        7518 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
    1209             :       {
    1210        4893 :         gel(*C, j) = gel(C1, j1);
    1211        4893 :         (*R)[j] = R1[j1++];
    1212             :       }
    1213             :       else
    1214             :       {
    1215        2625 :         gel(*C, j) = gel(C2, j2);
    1216        2625 :         (*R)[j] = R2[j2++];
    1217             :       }
    1218             :     }
    1219             :   }
    1220        4452 :   if (gc_needed(av, 1)) gerepileall(av, 2, R, C);
    1221        4452 :   return r;
    1222             : }
    1223             : 
    1224             : /*******************************************************************/
    1225             : /*                                                                 */
    1226             : /*                    LINEAR ALGEBRA MODULO P                      */
    1227             : /*                                                                 */
    1228             : /*******************************************************************/
    1229             : 
    1230             : static long
    1231     2208906 : F2v_find_nonzero(GEN x0, GEN mask0, long l, long m)
    1232             : {
    1233     2208906 :   ulong *x = (ulong *)x0+2, *mask = (ulong *)mask0+2, e;
    1234             :   long i, j;
    1235     4762699 :   for (i = 0; i < l; i++)
    1236             :   {
    1237     4760983 :     e = *x++ & *mask++;
    1238     4760983 :     if (e)
    1239     2207190 :       for (j = 1; ; j++, e >>= 1) if (e & 1uL) return i*BITS_IN_LONG+j;
    1240     9970600 :   }
    1241        1716 :   return m+1;
    1242             : }
    1243             : 
    1244             : /* in place, destroy x */
    1245             : GEN
    1246      304147 : F2m_ker_sp(GEN x, long deplin)
    1247             : {
    1248             :   GEN y, c, d;
    1249             :   long i, j, k, l, r, m, n;
    1250             : 
    1251      304147 :   n = lg(x)-1;
    1252      304147 :   m = mael(x,1,1); r=0;
    1253             : 
    1254      304147 :   d = cgetg(n+1, t_VECSMALL);
    1255      304147 :   c = const_F2v(m); l = lg(c)-1;
    1256     2262124 :   for (k=1; k<=n; k++)
    1257             :   {
    1258     1982204 :     GEN xk = gel(x,k);
    1259     1982204 :     j = F2v_find_nonzero(xk, c, l, m);
    1260     1982204 :     if (j>m)
    1261             :     {
    1262      442991 :       if (deplin) {
    1263       24227 :         GEN c = zero_F2v(n);
    1264      104732 :         for (i=1; i<k; i++)
    1265       80505 :           if (F2v_coeff(xk, d[i]))
    1266       40390 :             F2v_set(c, i);
    1267       24227 :         F2v_set(c, k);
    1268       24227 :         return c;
    1269             :       }
    1270      418764 :       r++; d[k] = 0;
    1271             :     }
    1272             :     else
    1273             :     {
    1274     1539213 :       F2v_clear(c,j); d[k] = j;
    1275     1539213 :       F2v_clear(xk, j);
    1276   134519576 :       for (i=k+1; i<=n; i++)
    1277             :       {
    1278   132980363 :         GEN xi = gel(x,i);
    1279   132980363 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1280             :       }
    1281     1539213 :       F2v_set(xk, j);
    1282             :     }
    1283             :   }
    1284      279920 :   if (deplin) return NULL;
    1285             : 
    1286      279885 :   y = zero_F2m_copy(n,r);
    1287      698649 :   for (j=k=1; j<=r; j++,k++)
    1288             :   {
    1289      418764 :     GEN C = gel(y,j); while (d[k]) k++;
    1290    19819113 :     for (i=1; i<k; i++)
    1291    19400349 :       if (d[i] && F2m_coeff(x,d[i],k))
    1292     7354274 :         F2v_set(C,i);
    1293      418764 :     F2v_set(C, k);
    1294             :   }
    1295      279885 :   return y;
    1296             : }
    1297             : 
    1298             : static void /* assume m < p */
    1299     7886790 : _Fl_addmul(GEN b, long k, long i, ulong m, ulong p, ulong pi)
    1300             : {
    1301     7886790 :   uel(b,k) = Fl_addmul_pre(uel(b, k), m, uel(b, i), p, pi);
    1302     7886790 : }
    1303             : static void /* same m = 1 */
    1304      499243 : _Fl_add(GEN b, long k, long i, ulong p)
    1305             : {
    1306      499243 :   uel(b,k) = Fl_add(uel(b,k), uel(b,i), p);
    1307      499243 : }
    1308             : static void /* assume m < p && SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1309    17939686 : _Fl_addmul_OK(GEN b, long k, long i, ulong m, ulong p)
    1310             : {
    1311    17939686 :   uel(b,k) += m * uel(b,i);
    1312    17939686 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1313    17939686 : }
    1314             : static void /* assume SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
    1315     3923619 : _Fl_add_OK(GEN b, long k, long i, ulong p)
    1316             : {
    1317     3923619 :   uel(b,k) += uel(b,i);
    1318     3923619 :   if (uel(b,k) & HIGHMASK) uel(b,k) %= p;
    1319     3923619 : }
    1320             : 
    1321             : static GEN
    1322      347020 : 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      347020 :   n = lg(x)-1;
    1329      347020 :   m=nbrows(x); r=0;
    1330             : 
    1331      347020 :   c = zero_zv(m);
    1332      347020 :   d = cgetg(n+1, t_VECSMALL);
    1333      347020 :   a = 0; /* for gcc -Wall */
    1334     1523737 :   for (k=1; k<=n; k++)
    1335             :   {
    1336     4271295 :     for (j=1; j<=m; j++)
    1337     3620359 :       if (!c[j])
    1338             :       {
    1339     2730819 :         a = ucoeff(x,j,k) % p;
    1340     2730819 :         if (a) break;
    1341             :       }
    1342     1195415 :     if (j > m)
    1343             :     {
    1344      650936 :       if (deplin==1) {
    1345       18698 :         c = cgetg(n+1, t_VECSMALL);
    1346       18698 :         for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k) % p;
    1347       18698 :         c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
    1348       18698 :         return c;
    1349             :       }
    1350      632238 :       r++; d[k] = 0;
    1351             :     }
    1352             :     else
    1353             :     {
    1354      544479 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1355      544479 :       c[j] = k; d[k] = j;
    1356      544479 :       ucoeff(x,j,k) = p-1;
    1357      544479 :       if (piv != 1)
    1358      416955 :         for (i=k+1; i<=n; i++) ucoeff(x,j,i) = (piv * ucoeff(x,j,i)) % p;
    1359     2943401 :       for (t=1; t<=m; t++)
    1360             :       {
    1361     2398922 :         if (t == j) continue;
    1362             : 
    1363     1854443 :         piv = ( ucoeff(x,t,k) %= p );
    1364     1854443 :         if (!piv) continue;
    1365     1100776 :         if (piv == 1)
    1366      181407 :           for (i=k+1; i<=n; i++) _Fl_add_OK(gel(x,i),t,j, p);
    1367             :         else
    1368      919369 :           for (i=k+1; i<=n; i++) _Fl_addmul_OK(gel(x,i),t,j,piv, p);
    1369             :       }
    1370             :     }
    1371             :   }
    1372      328322 :   if (deplin==1) return NULL;
    1373             : 
    1374      328315 :   y = cgetg(r+1, t_MAT);
    1375      960553 :   for (j=k=1; j<=r; j++,k++)
    1376             :   {
    1377      632238 :     GEN C = cgetg(n+1, t_VECSMALL);
    1378             : 
    1379      632238 :     gel(y,j) = C; while (d[k]) k++;
    1380     1537484 :     for (i=1; i<k; i++)
    1381      905246 :       if (d[i])
    1382      417693 :         uel(C,i) = ucoeff(x,d[i],k) % p;
    1383             :       else
    1384      487553 :         uel(C,i) = 0UL;
    1385      632238 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1386             :   }
    1387      328315 :   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      328315 :   return y;
    1394             : }
    1395             : 
    1396             : /* in place, destroy x */
    1397             : static GEN
    1398      459454 : 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      459454 :   n = lg(x)-1;
    1404      459454 :   if (!n) return cgetg(1,t_MAT);
    1405      459454 :   if (SMALL_ULONG(p)) return Flm_ker_gauss_OK(x, p, deplin);
    1406      112434 :   pi = get_Fl_red(p);
    1407             : 
    1408      112434 :   m=nbrows(x); r=0;
    1409             : 
    1410      112434 :   c = zero_zv(m);
    1411      112434 :   d = cgetg(n+1, t_VECSMALL);
    1412      112434 :   a = 0; /* for gcc -Wall */
    1413      358793 :   for (k=1; k<=n; k++)
    1414             :   {
    1415      687718 :     for (j=1; j<=m; j++)
    1416      596320 :       if (!c[j])
    1417             :       {
    1418      470067 :         a = ucoeff(x,j,k);
    1419      470067 :         if (a) break;
    1420             :       }
    1421      246366 :     if (j > m)
    1422             :     {
    1423       91398 :       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       91391 :       r++; d[k] = 0;
    1430             :     }
    1431             :     else
    1432             :     {
    1433      154968 :       ulong piv = p - Fl_inv(a, p); /* -1/a */
    1434      154968 :       c[j] = k; d[k] = j;
    1435      154968 :       ucoeff(x,j,k) = p-1;
    1436      154968 :       if (piv != 1)
    1437      286976 :         for (i=k+1; i<=n; i++)
    1438      133302 :           ucoeff(x,j,i) = Fl_mul_pre(piv, ucoeff(x,j,i), p, pi);
    1439      638068 :       for (t=1; t<=m; t++)
    1440             :       {
    1441      483100 :         if (t == j) continue;
    1442             : 
    1443      328132 :         piv = ucoeff(x,t,k);
    1444      328132 :         if (!piv) continue;
    1445      182040 :         if (piv == 1)
    1446        7189 :           for (i=k+1; i<=n; i++) _Fl_add(gel(x,i),t,j,p);
    1447             :         else
    1448      174851 :           for (i=k+1; i<=n; i++) _Fl_addmul(gel(x,i),t,j,piv,p, pi);
    1449             :       }
    1450             :     }
    1451             :   }
    1452      112427 :   if (deplin==1) return NULL;
    1453             : 
    1454      112420 :   y = cgetg(r+1, t_MAT);
    1455      203811 :   for (j=k=1; j<=r; j++,k++)
    1456             :   {
    1457       91391 :     GEN C = cgetg(n+1, t_VECSMALL);
    1458             : 
    1459       91391 :     gel(y,j) = C; while (d[k]) k++;
    1460      184607 :     for (i=1; i<k; i++)
    1461       93216 :       if (d[i])
    1462       61005 :         uel(C,i) = ucoeff(x,d[i],k);
    1463             :       else
    1464       32211 :         uel(C,i) = 0UL;
    1465       91391 :     uel(C,k) = 1UL; for (i=k+1; i<=n; i++) uel(C,i) = 0UL;
    1466             :   }
    1467      112420 :   if (deplin == 2) {
    1468      108523 :     GEN pc = cgetg(n - r + 1, t_VECSMALL);  /* indices of pivot columns */
    1469      330387 :     for (i = j = 1; j <= n; j++)
    1470      221864 :       if (d[j]) pc[i++] = j;
    1471      108523 :     return mkvec2(y, pc);
    1472             :   }
    1473        3897 :   return y;
    1474             : }
    1475             : 
    1476             : GEN
    1477       72819 : FpM_intersect(GEN x, GEN y, GEN p)
    1478             : {
    1479       72819 :   pari_sp av = avma;
    1480       72819 :   long j, lx = lg(x);
    1481             :   GEN z;
    1482             : 
    1483       72819 :   if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
    1484       72819 :   z = FpM_ker(shallowconcat(x,y), p);
    1485       72819 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    1486       72819 :   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         173 : 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      170679 : Flm_ker_echelon(GEN x, ulong p, long pivots) {
    1509      170679 :   pari_sp av = avma;
    1510             :   GEN R, Rc, C, C1, C2, S, K;
    1511      170679 :   long n = lg(x) - 1, r;
    1512      170679 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1513      170679 :   Rc = indexcompl(R, n);
    1514      170679 :   C1 = rowpermute(C, R);
    1515      170679 :   C2 = rowpermute(C, Rc);
    1516      170679 :   S = Flm_lsolve_lower_unit(C1, C2, p);
    1517      170679 :   K = vecpermute(shallowconcat(Flm_neg(S, p), matid_Flm(n - r)),
    1518             :                  perm_inv(vecsmall_concat(R, Rc)));
    1519      170679 :   K = Flm_transpose(K);
    1520      170679 :   if (pivots)
    1521        2500 :     return gerepilecopy(av, mkvec2(K, R));
    1522      168179 :   return gerepileupto(av, K);
    1523             : }
    1524             : 
    1525             : static GEN
    1526       23713 : Flm_deplin_echelon(GEN x, ulong p) {
    1527       23713 :   pari_sp av = avma;
    1528             :   GEN R, Rc, C, C1, C2, s, v;
    1529       23713 :   long i, n = lg(x) - 1, r;
    1530       23713 :   r = Flm_echelon(Flm_transpose(x), &R, &C, p);
    1531       23713 :   if (r == n) { avma = av; return NULL; }
    1532       23706 :   Rc = indexcompl(R, n);
    1533       23706 :   i = Rc[1];
    1534       23706 :   C1 = rowpermute(C, R);
    1535       23706 :   C2 = rowslice(C, i, i);
    1536       23706 :   s = Flm_row(Flm_lsolve_lower_unit(C1, C2, p), 1);
    1537       23706 :   v = vecpermute(vecsmall_concat(Flv_neg(s, p), vecsmall_ei(n - r, 1)),
    1538             :                  perm_inv(vecsmall_concat(R, Rc)));
    1539       23706 :   return gerepileuptoleaf(av, v);
    1540             : }
    1541             : 
    1542             : static GEN
    1543      653846 : Flm_ker_i(GEN x, ulong p, long deplin, long inplace) {
    1544      653846 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1545      194392 :     switch(deplin) {
    1546      168179 :     case 0: return Flm_ker_echelon(x, p, 0);
    1547       23713 :     case 1: return Flm_deplin_echelon(x, p);
    1548        2500 :     case 2: return Flm_ker_echelon(x, p, 1);
    1549             :     }
    1550      459454 :   return Flm_ker_gauss(inplace? x: Flm_copy(x), p, deplin);
    1551             : }
    1552             : 
    1553             : GEN
    1554      593821 : Flm_ker_sp(GEN x, ulong p, long deplin) {
    1555      593821 :   return Flm_ker_i(x, p, deplin, 1);
    1556             : }
    1557             : 
    1558             : GEN
    1559       60025 : Flm_ker(GEN x, ulong p) {
    1560       60025 :   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       40525 : Flm_det_gauss(GEN a, ulong p)
    1631             : {
    1632       40525 :   long i,j,k, s = 1, nbco = lg(a)-1;
    1633       40525 :   ulong pi, q, x = 1;
    1634             : 
    1635       40525 :   if (SMALL_ULONG(p)) return Flm_det_gauss_OK(a, nbco, p);
    1636       40511 :   pi = get_Fl_red(p);
    1637      261605 :   for (i=1; i<nbco; i++)
    1638             :   {
    1639      221101 :     for(k=i; k<=nbco; k++)
    1640      221101 :       if (ucoeff(a,k,i)) break;
    1641      221094 :     if (k > nbco) return ucoeff(a,i,i);
    1642      221094 :     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      221094 :     q = ucoeff(a,i,i);
    1648             : 
    1649      221094 :     x = Fl_mul_pre(x, q, p, pi);
    1650      221094 :     q = Fl_inv(q,p);
    1651      953298 :     for (k=i+1; k<=nbco; k++)
    1652             :     {
    1653      732204 :       ulong m = ucoeff(a,i,k);
    1654      732204 :       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       40511 :   if (s < 0) x = Fl_neg(x, p);
    1662       40511 :   return Fl_mul(x, ucoeff(a,nbco,nbco), p);
    1663             : }
    1664             : 
    1665             : static ulong
    1666       29386 : Flm_det_CUP(GEN a, ulong p) {
    1667             :   GEN R, C, U, P;
    1668       29386 :   long d, i, n = lg(a) - 1, r;
    1669       29386 :   r = Flm_CUP(a, &R, &C, &U, &P, p);
    1670       29386 :   if (r < n)
    1671          42 :     d = 0;
    1672             :   else {
    1673       29344 :     d = perm_sign(P) == 1? 1: p-1;
    1674      294430 :     for (i = 1; i <= n; i++)
    1675      265086 :       d = Fl_mul(d, ucoeff(U, i, i), p);
    1676             :   }
    1677       29386 :   return d;
    1678             : }
    1679             : 
    1680             : static ulong
    1681       69911 : Flm_det_i(GEN x, ulong p, long inplace) {
    1682       69911 :   pari_sp av = avma;
    1683             :   ulong d;
    1684       69911 :   if (lg(x) - 1 >= Flm_CUP_LIMIT)
    1685       29386 :     d = Flm_det_CUP(x, p);
    1686             :   else
    1687       40525 :     d = Flm_det_gauss(inplace? x: Flm_copy(x), p);
    1688       69911 :   avma = av; return d;
    1689             : }
    1690             : 
    1691             : ulong
    1692       69911 : Flm_det_sp(GEN x, ulong p) {
    1693       69911 :   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      461881 : FpM_init(GEN a, GEN p, ulong *pp)
    1703             : {
    1704      461881 :   if (lgefint(p) == 3)
    1705             :   {
    1706      457324 :     *pp = uel(p,2);
    1707      457324 :     return (*pp==2)? ZM_to_F2m(a): ZM_to_Flm(a, *pp);
    1708             :   }
    1709        4557 :   *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       31652 : F2m_gauss_pivot(GEN x, long *rr)
    1747             : {
    1748             :   GEN c, d;
    1749             :   long i, j, k, l, r, m, n;
    1750             : 
    1751       31652 :   n = lg(x)-1; if (!n) { *rr=0; return NULL; }
    1752       31652 :   m = mael(x,1,1); r=0;
    1753             : 
    1754       31652 :   d = cgetg(n+1, t_VECSMALL);
    1755       31652 :   c = const_F2v(m); l = lg(c)-1;
    1756      258354 :   for (k=1; k<=n; k++)
    1757             :   {
    1758      226702 :     GEN xk = gel(x,k);
    1759      226702 :     j = F2v_find_nonzero(xk, c, l, m);
    1760      226702 :     if (j>m) { r++; d[k] = 0; }
    1761             :     else
    1762             :     {
    1763      146801 :       F2v_clear(c,j); d[k] = j;
    1764     2113726 :       for (i=k+1; i<=n; i++)
    1765             :       {
    1766     1966925 :         GEN xi = gel(x,i);
    1767     1966925 :         if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
    1768             :       }
    1769             :     }
    1770             :   }
    1771             : 
    1772       31652 :   *rr = r; avma = (pari_sp)d; return d;
    1773             : }
    1774             : 
    1775             : /* Destroy x */
    1776             : static GEN
    1777      163183 : 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      163183 :   n=lg(x)-1; if (!n) { *rr=0; return NULL; }
    1783             : 
    1784      163183 :   m=nbrows(x); r=0;
    1785      163183 :   d=cgetg(n+1,t_VECSMALL);
    1786      163183 :   c = zero_zv(m);
    1787      691282 :   for (k=1; k<=n; k++)
    1788             :   {
    1789     1738851 :     for (j=1; j<=m; j++)
    1790     1636289 :       if (!c[j])
    1791             :       {
    1792      909723 :         ucoeff(x,j,k) %= p;
    1793      909723 :         if (ucoeff(x,j,k)) break;
    1794             :       }
    1795      528099 :     if (j>m) { r++; d[k]=0; }
    1796             :     else
    1797             :     {
    1798      425537 :       ulong piv = p - Fl_inv(ucoeff(x,j,k), p);
    1799      425537 :       c[j]=k; d[k]=j;
    1800     1197472 :       for (i=k+1; i<=n; i++)
    1801      771935 :         ucoeff(x,j,i) = Fl_mul(piv, ucoeff(x,j,i), p);
    1802     3154628 :       for (t=1; t<=m; t++)
    1803     2729091 :         if (!c[t]) /* no pivot on that line yet */
    1804             :         {
    1805     1762381 :           piv = ucoeff(x,t,k);
    1806     1762381 :           if (piv)
    1807             :           {
    1808      947954 :             ucoeff(x,t,k) = 0;
    1809     2980363 :             for (i=k+1; i<=n; i++)
    1810     4064818 :               ucoeff(x,t,i) = Fl_add(ucoeff(x,t,i),
    1811     2032409 :                                      Fl_mul(piv,ucoeff(x,j,i),p),p);
    1812             :           }
    1813             :         }
    1814      425537 :       for (i=k; i<=n; i++) ucoeff(x,j,i) = 0; /* dummy */
    1815             :     }
    1816             :   }
    1817      163183 :   *rr = r; avma = (pari_sp)d; return d;
    1818             : }
    1819             : 
    1820             : static GEN
    1821       63953 : Flm_pivots_CUP(GEN x, ulong p, long *rr) {
    1822             :   pari_sp av;
    1823       63953 :   long i, n = lg(x) - 1, r;
    1824       63953 :   GEN R, C, U, P, d = zero_zv(n);
    1825       63953 :   av = avma;
    1826       63953 :   r = Flm_CUP(x, &R, &C, &U, &P, p);
    1827      809625 :   for(i = 1; i <= r; i++)
    1828      745672 :     d[P[i]] = R[i];
    1829       63953 :   avma = av;
    1830       63953 :   *rr = n - r;
    1831       63953 :   return d;
    1832             : }
    1833             : 
    1834             : static GEN
    1835      227136 : Flm_pivots(GEN x, ulong p, long *rr, long inplace) {
    1836      227136 :   if (lg(x) - 1 >= Flm_CUP_LIMIT && nbrows(x) >= Flm_CUP_LIMIT)
    1837       63953 :     return Flm_pivots_CUP(x, p, rr);
    1838      163183 :   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      138914 : FpM_gauss_pivot(GEN x, GEN p, long *rr)
    1850             : {
    1851             :   ulong pp;
    1852      138914 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1853      137451 :   x = FpM_init(x, p, &pp);
    1854      137451 :   switch(pp)
    1855             :   {
    1856          59 :   case 0: return FpM_gauss_pivot_gen(x, p, rr);
    1857       31568 :   case 2: return F2m_gauss_pivot(x, rr);
    1858      105824 :   default:return Flm_pivots(x, pp, rr, 1);
    1859             :   }
    1860             : }
    1861             : 
    1862             : GEN
    1863       82332 : FpM_image(GEN x, GEN p)
    1864             : {
    1865             :   long r;
    1866       82332 :   GEN d = FpM_gauss_pivot(x,p,&r); /* d left on stack for efficiency */
    1867       82332 :   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        2206 : Flm_rank(GEN x, ulong p)
    1897             : {
    1898        2206 :   pari_sp av = avma;
    1899             :   long r;
    1900        2206 :   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        2171 :   (void) Flm_pivots(x, p, &r, 0);
    1906        2171 :   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        1211 : FlxqM_gauss_pivot(GEN x, GEN T, ulong p, long *rr)
    1920             : {
    1921             :   void *E;
    1922        1211 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1923        1211 :   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          28 : FlxqM_pivots(GEN x, GEN T, ulong p, long *rr) {
    1942          28 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    1943          14 :     return FlxqM_pivots_CUP(x, T, p, rr);
    1944          14 :   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          28 : FlxqM_rank(GEN x, GEN T, ulong p)
    1957             : {
    1958          28 :   pari_sp av = avma;
    1959             :   long r;
    1960          28 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT) {
    1961             :     GEN R, C;
    1962          21 :     r = FlxqM_echelon(x, &R, &C, T, p);
    1963          21 :     avma = av; return r;
    1964             :   }
    1965           7 :   (void) FlxqM_pivots(x, T, p, &r);
    1966           7 :   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       11713 : FlxqM_mul(GEN A, GEN B, GEN T, ulong p) {
    2017             :   void *E;
    2018             :   const struct bb_field *ff;
    2019       11713 :   long n = lg(A) - 1;
    2020             : 
    2021       11713 :   if (n == 0)
    2022           0 :     return cgetg(1, t_MAT);
    2023       11713 :   if (n > 1)
    2024        9816 :     return FlxqM_mul_Kronecker(A, B, T, p);
    2025        1897 :   ff = get_Flxq_field(&E, T, p);
    2026        1897 :   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          28 : FqM_gauss_pivot_gen(GEN x, GEN T, GEN p, long *rr)
    2146             : {
    2147             :   void *E;
    2148          28 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2149          28 :   return gen_Gauss_pivot(x, rr, E, S);
    2150             : }
    2151             : static GEN
    2152        1211 : FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr)
    2153             : {
    2154        1211 :   if (lg(x)==1) { *rr = 0; return NULL; }
    2155        1211 :   if (!T) return FpM_gauss_pivot(x, p, rr);
    2156        1211 :   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          28 :   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           7 : FqM_rank(GEN x, GEN T, GEN p)
    2177             : {
    2178           7 :   pari_sp av = avma;
    2179             :   long r;
    2180           7 :   (void)FqM_gauss_pivot(x,T,p,&r);
    2181           7 :   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        1626 : FpM_ker_gen(GEN x, GEN p, long deplin)
    2228             : {
    2229             :   void *E;
    2230        1626 :   const struct bb_field *S = get_Fp_field(&E,p);
    2231        1626 :   return gen_ker(x, deplin, E, S);
    2232             : }
    2233             : static GEN
    2234      273699 : FpM_ker_i(GEN x, GEN p, long deplin)
    2235             : {
    2236      273699 :   pari_sp av = avma;
    2237             :   ulong pp;
    2238             :   GEN y;
    2239             : 
    2240      273699 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2241      273594 :   x = FpM_init(x, p, &pp);
    2242      273594 :   switch(pp)
    2243             :   {
    2244        1598 :   case 0: return FpM_ker_gen(x,p,deplin);
    2245             :   case 2:
    2246       71526 :     y = F2m_ker_sp(x, deplin);
    2247       71526 :     if (!y) return y;
    2248       71526 :     y = deplin? F2c_to_ZC(y): F2m_to_ZM(y);
    2249       71526 :     return gerepileupto(av, y);
    2250             :   default:
    2251      200470 :     y = Flm_ker_sp(x, pp, deplin);
    2252      200470 :     if (!y) return y;
    2253      200470 :     y = deplin? Flc_to_ZC(y): Flm_to_ZM(y);
    2254      200470 :     return gerepileupto(av, y);
    2255             :   }
    2256             : }
    2257             : 
    2258             : GEN
    2259      201349 : FpM_ker(GEN x, GEN p) { return FpM_ker_i(x,p,0); }
    2260             : 
    2261             : GEN
    2262       66596 : FpM_deplin(GEN x, GEN p) { return FpM_ker_i(x,p,1); }
    2263             : 
    2264             : static GEN
    2265          21 : FqM_ker_gen(GEN x, GEN T, GEN p, long deplin)
    2266             : {
    2267             :   void *E;
    2268          21 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    2269          21 :   return gen_ker(x,deplin,E,S);
    2270             : }
    2271             : static GEN
    2272        8239 : FqM_ker_i(GEN x, GEN T, GEN p, long deplin)
    2273             : {
    2274        8239 :   if (!T) return FpM_ker_i(x,p,deplin);
    2275        2485 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2276             : 
    2277        2485 :   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          21 :   return FqM_ker_gen(x, T, p, deplin);
    2287             : }
    2288             : 
    2289             : GEN
    2290        8225 : 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         756 : FlxqM_ker_gen(GEN x, GEN T, ulong p, long deplin)
    2297             : {
    2298             :   const struct bb_field *ff;
    2299             :   void *E;
    2300             : 
    2301         756 :   if (lg(x)==1) return cgetg(1,t_MAT);
    2302         756 :   ff=get_Flxq_field(&E,T,p);
    2303         756 :   return gen_ker(x,deplin, E, ff);
    2304             : }
    2305             : 
    2306             : static GEN
    2307        1750 : FlxqM_ker_echelon(GEN x, GEN T, ulong p) {
    2308        1750 :   pari_sp av = avma;
    2309             :   GEN R, Rc, C, C1, C2, S, K;
    2310        1750 :   long n = lg(x) - 1, r;
    2311        1750 :   r = FlxqM_echelon(shallowtrans(x), &R, &C, T, p);
    2312        1750 :   Rc = indexcompl(R, n);
    2313        1750 :   C1 = rowpermute(C, R);
    2314        1750 :   C2 = rowpermute(C, Rc);
    2315        1750 :   S = FlxqM_lsolve_lower_unit(C1, C2, T, p);
    2316        1750 :   K = vecpermute(shallowconcat(FlxM_neg(S, p), matid_FlxqM(n - r, T, p)),
    2317             :                  perm_inv(vecsmall_concat(R, Rc)));
    2318        1750 :   K = shallowtrans(K);
    2319        1750 :   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        2527 : FlxqM_ker_i(GEN x, GEN T, ulong p, long deplin) {
    2349        2527 :   if (lg(x) - 1 >= FlxqM_CUP_LIMIT && nbrows(x) >= FlxqM_CUP_LIMIT)
    2350        1771 :     return deplin? FlxqM_deplin_echelon(x, T, p): FlxqM_ker_echelon(x, T, p);
    2351         756 :   return FlxqM_ker_gen(x, T, p, deplin);
    2352             : }
    2353             : 
    2354             : GEN
    2355        2492 : FlxqM_ker(GEN x, GEN T, ulong p)
    2356             : {
    2357        2492 :   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      574888 : approx_0(GEN x, GEN y)
    2419             : {
    2420      574888 :   long tx = typ(x);
    2421      574888 :   if (tx == t_COMPLEX)
    2422           7 :     return approx_0(gel(x,1), y) && approx_0(gel(x,2), y);
    2423      575028 :   return gequal0(x) ||
    2424      396447 :          (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      587628 : gauss_get_pivot_max(GEN X, GEN X0, long ix, GEN c)
    2430             : {
    2431      587628 :   GEN p, r, x = gel(X,ix), x0 = gel(X0,ix);
    2432      587628 :   long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
    2433      587628 :   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     1927926 :     for (i=ix; i<lx; i++)
    2445             :     {
    2446     1368263 :       long e = gexpo(gel(x,i));
    2447     1368263 :       if (e > ex) { ex = e; k = i; }
    2448             :     }
    2449             :   }
    2450      587628 :   if (!k) return lx;
    2451      574874 :   p = gel(x,k);
    2452      574874 :   r = gel(x0,k); if (isrationalzero(r)) r = x0;
    2453      574874 :   return approx_0(p, r)? lx: k;
    2454             : }
    2455             : static long
    2456       63371 : gauss_get_pivot_padic(GEN X, GEN p, long ix, GEN c)
    2457             : {
    2458       63371 :   GEN x = gel(X, ix);
    2459       63371 :   long i, k = 0, ex = (long)HIGHVALPBIT, lx = lg(x);
    2460       63371 :   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      446005 :     for (i=ix; i<lx; i++)
    2472      382760 :       if (!gequal0(gel(x,i)))
    2473             :       {
    2474      183883 :         long e = gvaluation(gel(x,i), p);
    2475      183883 :         if (e < ex) { ex = e; k = i; }
    2476             :       }
    2477             :   }
    2478       63371 :   return k? k: lx;
    2479             : }
    2480             : static long
    2481      481279 : gauss_get_pivot_NZ(GEN X, GEN x0/*unused*/, long ix, GEN c)
    2482             : {
    2483      481279 :   GEN x = gel(X, ix);
    2484      481279 :   long i, lx = lg(x);
    2485             :   (void)x0;
    2486      481279 :   if (c)
    2487             :   {
    2488     5641510 :     for (i=1; i<lx; i++)
    2489     5561766 :       if (!c[i] && !gequal0(gel(x,i))) return i;
    2490             :   }
    2491             :   else
    2492             :   {
    2493      362000 :     for (i=ix; i<lx; i++)
    2494      361993 :       if (!gequal0(gel(x,i))) return i;
    2495             :   }
    2496       79751 :   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      279939 : get_pivot_fun(GEN x, GEN x0, GEN *data)
    2506             : {
    2507      279939 :   long i, j, hx, lx = lg(x);
    2508      279939 :   int res = t_INT;
    2509      279939 :   GEN p = NULL;
    2510             : 
    2511      279939 :   *data = NULL;
    2512      279939 :   if (lx == 1) return &gauss_get_pivot_NZ;
    2513      279799 :   hx = lgcols(x);
    2514     1432213 :   for (j=1; j<lx; j++)
    2515             :   {
    2516     1152442 :     GEN xj = gel(x,j);
    2517    16908630 :     for (i=1; i<hx; i++)
    2518             :     {
    2519    15756216 :       GEN c = gel(xj,i);
    2520    15756216 :       switch(typ(c))
    2521             :       {
    2522             :         case t_REAL:
    2523     1567920 :           res = t_REAL;
    2524     1567920 :           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    14027821 :           break;
    2531             :         case t_PADIC:
    2532      160419 :           p = gel(c,2);
    2533      160419 :           res = t_PADIC;
    2534      160419 :           break;
    2535          28 :         default: return &gauss_get_pivot_NZ;
    2536             :       }
    2537             :     }
    2538             :   }
    2539      279771 :   switch(res)
    2540             :   {
    2541      181298 :     case t_REAL: *data = x0; return &gauss_get_pivot_max;
    2542        8253 :     case t_PADIC: *data = p; return &gauss_get_pivot_padic;
    2543       90220 :     default: return &gauss_get_pivot_NZ;
    2544             :   }
    2545             : }
    2546             : 
    2547             : static GEN
    2548      542347 : get_col(GEN a, GEN b, GEN p, long li)
    2549             : {
    2550      542347 :   GEN u = cgetg(li+1,t_COL);
    2551             :   long i, j;
    2552             : 
    2553      542347 :   gel(u,li) = gdiv(gel(b,li), p);
    2554     3168058 :   for (i=li-1; i>0; i--)
    2555             :   {
    2556     2625711 :     pari_sp av = avma;
    2557     2625711 :     GEN m = gel(b,i);
    2558     2625711 :     for (j=i+1; j<=li; j++) m = gsub(m, gmul(gcoeff(a,i,j), gel(u,j)));
    2559     2625711 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(a,i,i)));
    2560             :   }
    2561      542347 :   return u;
    2562             : }
    2563             : /* assume 0 <= a[i,j] < p */
    2564             : static GEN
    2565      320080 : Fl_get_col_OK(GEN a, GEN b, long li, ulong p)
    2566             : {
    2567      320080 :   GEN u = cgetg(li+1,t_VECSMALL);
    2568      320080 :   ulong m = uel(b,li) % p;
    2569             :   long i,j;
    2570             : 
    2571      320080 :   uel(u,li) = (m * ucoeff(a,li,li)) % p;
    2572     4339725 :   for (i = li-1; i > 0; i--)
    2573             :   {
    2574     4019645 :     m = p - uel(b,i)%p;
    2575    45950879 :     for (j = i+1; j <= li; j++) {
    2576    41931234 :       if (m & HIGHBIT) m %= p;
    2577    41931234 :       m += ucoeff(a,i,j) * uel(u,j); /* 0 <= u[j] < p */
    2578             :     }
    2579     4019645 :     m %= p;
    2580     4019645 :     if (m) m = ((p-m) * ucoeff(a,i,i)) % p;
    2581     4019645 :     uel(u,i) = m;
    2582             :   }
    2583      320080 :   return u;
    2584             : }
    2585             : static GEN
    2586     2133306 : Fl_get_col(GEN a, GEN b, long li, ulong p)
    2587             : {
    2588     2133306 :   GEN u = cgetg(li+1,t_VECSMALL);
    2589     2133306 :   ulong m = uel(b,li) % p;
    2590             :   long i,j;
    2591             : 
    2592     2133306 :   uel(u,li) = Fl_mul(m, ucoeff(a,li,li), p);
    2593     8362594 :   for (i=li-1; i>0; i--)
    2594             :   {
    2595     6229288 :     m = b[i]%p;
    2596    22074411 :     for (j = i+1; j <= li; j++)
    2597    15845123 :       m = Fl_sub(m, Fl_mul(ucoeff(a,i,j), uel(u,j), p), p);
    2598     6229288 :     if (m) m = Fl_mul(m, ucoeff(a,i,i), p);
    2599     6229288 :     uel(u,i) = m;
    2600             :   }
    2601     2133306 :   return u;
    2602             : }
    2603             : 
    2604             : /* bk -= m * bi */
    2605             : static void
    2606     4461102 : _submul(GEN b, long k, long i, GEN m)
    2607             : {
    2608     4461102 :   gel(b,k) = gsub(gel(b,k), gmul(m, gel(b,i)));
    2609     4461102 : }
    2610             : static int
    2611      752485 : init_gauss(GEN a, GEN *b, long *aco, long *li, int *iscol)
    2612             : {
    2613      752485 :   *iscol = *b ? (typ(*b) == t_COL): 0;
    2614      752485 :   *aco = lg(a) - 1;
    2615      752485 :   if (!*aco) /* a empty */
    2616             :   {
    2617         364 :     if (*b && lg(*b) != 1) pari_err_DIM("gauss");
    2618         364 :     *li = 0; return 0;
    2619             :   }
    2620      752121 :   *li = nbrows(a);
    2621      752121 :   if (*li < *aco) pari_err_INV("gauss [no left inverse]", a);
    2622      752121 :   if (*b)
    2623             :   {
    2624      722803 :     if (*li != *aco) pari_err_DIM("gauss");
    2625      722803 :     switch(typ(*b))
    2626             :     {
    2627             :       case t_MAT:
    2628       53192 :         if (lg(*b) == 1) return 0;
    2629       53192 :         *b = RgM_shallowcopy(*b);
    2630       53192 :         break;
    2631             :       case t_COL:
    2632      669611 :         *b = mkmat( leafcopy(*b) );
    2633      669611 :         break;
    2634           0 :       default: pari_err_TYPE("gauss",*b);
    2635             :     }
    2636      722803 :     if (nbrows(*b) != *li) pari_err_DIM("gauss");
    2637             :   }
    2638             :   else
    2639       29318 :     *b = matid(*li);
    2640      752121 :   return 1;
    2641             : }
    2642             : 
    2643             : static GEN Flm_inv_sp(GEN a, ulong *detp, ulong p);
    2644             : 
    2645             : static int
    2646      389241 : is_modular_solve(GEN a, GEN b, GEN *u)
    2647             : {
    2648      389241 :   GEN p = NULL;
    2649             :   ulong pp;
    2650      389241 :   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      389241 : RgM_solve(GEN a, GEN b)
    2732             : {
    2733      389241 :   pari_sp av = avma;
    2734             :   long i, j, k, li, bco, aco;
    2735             :   int iscol;
    2736             :   pivot_fun pivot;
    2737      389241 :   GEN p, u, data, ff = NULL;
    2738             : 
    2739      389241 :   if (is_modular_solve(a,b,&u)) return gerepileupto(av, u);
    2740      389038 :   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      388814 :   avma = av;
    2749             : 
    2750      388814 :   if (lg(a)-1 == 2 && nbrows(a) == 2) {
    2751             :     /* 2x2 matrix, start by inverting a */
    2752      151033 :     GEN u = gcoeff(a,1,1), v = gcoeff(a,1,2);
    2753      151033 :     GEN w = gcoeff(a,2,1), x = gcoeff(a,2,2);
    2754      151033 :     GEN D = gsub(gmul(u,x), gmul(v,w)), ainv;
    2755      151033 :     if (gequal0(D)) return NULL;
    2756      151033 :     ainv = mkmat2(mkcol2(x, gneg(w)), mkcol2(gneg(v), u));
    2757      151033 :     ainv = gmul(ainv, ginv(D));
    2758      151033 :     if (b) ainv = gmul(ainv, b);
    2759      151033 :     return gerepileupto(av, ainv);
    2760             :   }
    2761             : 
    2762      237781 :   if (!init_gauss(a, &b, &aco, &li, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    2763      237487 :   pivot = get_pivot_fun(a, a, &data);
    2764      237487 :   a = RgM_shallowcopy(a);
    2765      237487 :   bco = lg(b)-1;
    2766      237487 :   if(DEBUGLEVEL>4) err_printf("Entering gauss\n");
    2767             : 
    2768      237487 :   p = NULL; /* gcc -Wall */
    2769      865555 :   for (i=1; i<=aco; i++)
    2770             :   {
    2771             :     /* k is the line where we find the pivot */
    2772      865555 :     k = pivot(a, data, i, NULL);
    2773      865555 :     if (k > li) return NULL;
    2774      865548 :     if (k != i)
    2775             :     { /* exchange the lines s.t. k = i */
    2776      132861 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    2777      132861 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    2778             :     }
    2779      865548 :     p = gcoeff(a,i,i);
    2780      865548 :     if (i == aco) break;
    2781             : 
    2782     2499519 :     for (k=i+1; k<=li; k++)
    2783             :     {
    2784     1871451 :       GEN m = gcoeff(a,k,i);
    2785     1871451 :       if (!gequal0(m))
    2786             :       {
    2787      687870 :         m = gdiv(m,p);
    2788      687870 :         for (j=i+1; j<=aco; j++) _submul(gel(a,j),k,i,m);
    2789      687870 :         for (j=1;   j<=bco; j++) _submul(gel(b,j),k,i,m);
    2790             :       }
    2791             :     }
    2792      628068 :     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      237480 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
    2800      237480 :   u = cgetg(bco+1,t_MAT);
    2801      237480 :   for (j=1; j<=bco; j++) gel(u,j) = get_col(a,gel(b,j),p,aco);
    2802      237480 :   return gerepilecopy(av, iscol? gel(u,1): u);
    2803             : }
    2804             : 
    2805             : /* assume dim A >= 1, A invertible + upper triangular  */
    2806             : static GEN
    2807      608784 : RgM_inv_upper_ind(GEN A, long index)
    2808             : {
    2809      608784 :   long n = lg(A)-1, i = index, j;
    2810      608784 :   GEN u = zerocol(n);
    2811      608784 :   gel(u,i) = ginv(gcoeff(A,i,i));
    2812     2621273 :   for (i--; i>0; i--)
    2813             :   {
    2814     2012489 :     pari_sp av = avma;
    2815     2012489 :     GEN m = gneg(gmul(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2816     2012489 :     for (j=i+2; j<=n; j++) m = gsub(m, gmul(gcoeff(A,i,j),gel(u,j)));
    2817     2012489 :     gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(A,i,i)));
    2818             :   }
    2819      608784 :   return u;
    2820             : }
    2821             : GEN
    2822       93394 : RgM_inv_upper(GEN A)
    2823             : {
    2824             :   long i, l;
    2825       93394 :   GEN B = cgetg_copy(A, &l);
    2826       93394 :   for (i = 1; i < l; i++) gel(B,i) = RgM_inv_upper_ind(A, i);
    2827       93394 :   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      970706 : split_realimag_col(GEN z, long r1, long r2)
    2916             : {
    2917      970706 :   long i, ru = r1+r2;
    2918      970706 :   GEN x = cgetg(ru+r2+1,t_COL), y = x + r2;
    2919     2910662 :   for (i=1; i<=r1; i++) {
    2920     1939956 :     GEN a = gel(z,i);
    2921     1939956 :     if (typ(a) == t_COMPLEX) a = gel(a,1); /* paranoia: a should be real */
    2922     1939956 :     gel(x,i) = a;
    2923             :   }
    2924     1593687 :   for (   ; i<=ru; i++) {
    2925      622981 :     GEN b, a = gel(z,i);
    2926      622981 :     if (typ(a) == t_COMPLEX) { b = gel(a,2); a = gel(a,1); } else b = gen_0;
    2927      622981 :     gel(x,i) = a;
    2928      622981 :     gel(y,i) = b;
    2929             :   }
    2930      970706 :   return x;
    2931             : }
    2932             : GEN
    2933      510170 : split_realimag(GEN x, long r1, long r2)
    2934             : {
    2935             :   long i,l; GEN y;
    2936      510170 :   if (typ(x) == t_COL) return split_realimag_col(x,r1,r2);
    2937      252220 :   y = cgetg_copy(x, &l);
    2938      252220 :   for (i=1; i<l; i++) gel(y,i) = split_realimag_col(gel(x,i), r1, r2);
    2939      252220 :   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      248918 : RgM_solve_realimag(GEN M, GEN y)
    2947             : {
    2948      248918 :   long l = lg(M), r2 = l - lgcols(M), r1 = l-1 - 2*r2;
    2949      248918 :   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       84032 : F2_get_col(GEN b, GEN d, long li, long aco)
    2969             : {
    2970       84032 :   long i, l = nbits2lg(aco);
    2971       84032 :   GEN u = cgetg(l, t_VECSMALL);
    2972       84032 :   u[1] = aco;
    2973     1382074 :   for (i = 1; i <= li; i++)
    2974     1298042 :     if (d[i]) /* d[i] can still be 0 if li > aco */
    2975             :     {
    2976     1298007 :       if (F2v_coeff(b, i))
    2977      427187 :         F2v_set(u, d[i]);
    2978             :       else
    2979      870820 :         F2v_clear(u, d[i]);
    2980             :     }
    2981       84032 :   return u;
    2982             : }
    2983             : 
    2984             : /* destroy a, b */
    2985             : static GEN
    2986       12887 : F2m_gauss_sp(GEN a, GEN b)
    2987             : {
    2988       12887 :   long i, j, k, l, li, bco, aco = lg(a)-1;
    2989             :   GEN u, d;
    2990             : 
    2991       12887 :   if (!aco) return cgetg(1,t_MAT);
    2992       12887 :   li = gel(a,1)[1];
    2993       12887 :   d = zero_Flv(li);
    2994       12887 :   bco = lg(b)-1;
    2995       96933 :   for (i=1; i<=aco; i++)
    2996             :   {
    2997       84060 :     GEN ai = vecsmall_copy(gel(a,i));
    2998       84060 :     if (!d[i] && F2v_coeff(ai, i))
    2999       40732 :       k = i;
    3000             :     else
    3001       43328 :       for (k = 1; k <= li; k++) if (!d[k] && F2v_coeff(ai,k)) break;
    3002             :     /* found a pivot on row k */
    3003       84060 :     if (k > li) return NULL;
    3004       84046 :     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       84046 :     F2v_clear(ai,k);
    3012     1382095 :     for (l=1; l<=aco; l++)
    3013             :     {
    3014     1298049 :       GEN al = gel(a,l);
    3015     1298049 :       if (F2v_coeff(al,k)) F2v_add_inplace(al,ai);
    3016             :     }
    3017     1382060 :     for (l=1; l<=bco; l++)
    3018             :     {
    3019     1298014 :       GEN bl = gel(b,l);
    3020     1298014 :       if (F2v_coeff(bl,k)) F2v_add_inplace(bl,ai);
    3021             :     }
    3022             :   }
    3023       12873 :   u = cgetg(bco+1,t_MAT);
    3024       12873 :   for (j = 1; j <= bco; j++) gel(u,j) = F2_get_col(gel(b,j), d, li, aco);
    3025       12873 :   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       40479 : Flm_gauss_sp_OK(GEN a, GEN b, ulong *detp, ulong p)
    3056             : {
    3057       40479 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3058       40479 :   ulong det = 1;
    3059             :   GEN u;
    3060             : 
    3061       40479 :   li = nbrows(a);
    3062       40479 :   bco = lg(b)-1;
    3063      320101 :   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      320101 :     for (k = 1; k < i; k++) ucoeff(a,k,i) %= p;
    3068      610547 :     for (k = i; k <= li; k++)
    3069             :     {
    3070      610540 :       ulong piv = ( ucoeff(a,k,i) %= p );
    3071      610540 :       if (piv)
    3072             :       {
    3073      320094 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3074      320094 :         if (detp)
    3075             :         {
    3076           0 :           if (det & HIGHMASK) det %= p;
    3077           0 :           det *= piv;
    3078             :         }
    3079      320094 :         break;
    3080             :       }
    3081             :     }
    3082             :     /* found a pivot on line k */
    3083      320101 :     if (k > li) return NULL;
    3084      320094 :     if (k != i)
    3085             :     { /* swap lines so that k = i */
    3086      100764 :       s = -s;
    3087      100764 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3088      100764 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3089             :     }
    3090      320094 :     if (i == aco) break;
    3091             : 
    3092      279622 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3093     2289476 :     for (k=i+1; k<=li; k++)
    3094             :     {
    3095     2009854 :       ulong m = ( ucoeff(a,k,i) %= p );
    3096     2009854 :       if (!m) continue;
    3097             : 
    3098      605804 :       m = Fl_mul(m, invpiv, p);
    3099      605804 :       if (m == 1) {
    3100      108703 :         for (j=i+1; j<=aco; j++) _Fl_add_OK(gel(a,j),k,i, p);
    3101      108703 :         for (j=1;   j<=bco; j++) _Fl_add_OK(gel(b,j),k,i, p);
    3102             :       } else {
    3103      497101 :         for (j=i+1; j<=aco; j++) _Fl_addmul_OK(gel(a,j),k,i,m, p);
    3104      497101 :         for (j=1;   j<=bco; j++) _Fl_addmul_OK(gel(b,j),k,i,m, p);
    3105             :       }
    3106             :     }
    3107             :   }
    3108       40472 :   if (detp)
    3109             :   {
    3110           0 :     det %=  p;
    3111           0 :     if (s < 0 && det) det = p - det;
    3112           0 :     *detp = det;
    3113             :   }
    3114       40472 :   u = cgetg(bco+1,t_MAT);
    3115       40472 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col_OK(a,gel(b,j), aco,p);
    3116       40472 :   return u;
    3117             : }
    3118             : 
    3119             : /* destroy a, b */
    3120             : static GEN
    3121      716686 : Flm_gauss_sp(GEN a, GEN b, ulong *detp, ulong p)
    3122             : {
    3123      716686 :   long i, j, k, li, bco, aco = lg(a)-1, s = 1;
    3124      716686 :   ulong det = 1;
    3125             :   GEN u;
    3126             :   ulong pi;
    3127      716686 :   if (!aco) { if (detp) *detp = 1; return cgetg(1,t_MAT); }
    3128      716686 :   if (SMALL_ULONG(p)) return Flm_gauss_sp_OK(a, b, detp, p);
    3129      676207 :   pi = get_Fl_red(p);
    3130      676207 :   li = nbrows(a);
    3131      676207 :   bco = lg(b)-1;
    3132     2133320 :   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     2516172 :     for (k = i; k <= li; k++)
    3137             :     {
    3138     2516172 :       ulong piv = ucoeff(a,k,i);
    3139     2516172 :       if (piv)
    3140             :       {
    3141     2133320 :         ucoeff(a,k,i) = Fl_inv(piv, p);
    3142     2133320 :         if (detp) det = Fl_mul_pre(det, piv, p, pi);
    3143     2133320 :         break;
    3144             :       }
    3145             :     }
    3146             :     /* found a pivot on line k */
    3147     2133320 :     if (k > li) return NULL;
    3148     2133320 :     if (k != i)
    3149             :     { /* swap lines so that k = i */
    3150      215110 :       s = -s;
    3151      215110 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    3152      215110 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    3153             :     }
    3154     2133320 :     if (i == aco) break;
    3155             : 
    3156     1457113 :     invpiv = p - ucoeff(a,i,i); /* -1/piv mod p */
    3157     4571771 :     for (k=i+1; k<=li; k++)
    3158             :     {
    3159     3114658 :       ulong m = ucoeff(a,k,i);
    3160     3114658 :       if (!m) continue;
    3161             : 
    3162     1393926 :       m = Fl_mul_pre(m, invpiv, p, pi);
    3163     1393926 :       if (m == 1) {
    3164       58970 :         for (j=i+1; j<=aco; j++) _Fl_add(gel(a,j),k,i, p);
    3165       58970 :         for (j=1;   j<=bco; j++) _Fl_add(gel(b,j),k,i, p);
    3166             :       } else {
    3167     1334956 :         for (j=i+1; j<=aco; j++) _Fl_addmul(gel(a,j),k,i,m, p, pi);
    3168     1334956 :         for (j=1;   j<=bco; j++) _Fl_addmul(gel(b,j),k,i,m, p, pi);
    3169             :       }
    3170             :     }
    3171             :   }
    3172      676207 :   if (detp)
    3173             :   {
    3174           0 :     if (s < 0 && det) det = p - det;
    3175           0 :     *detp = det;
    3176             :   }
    3177      676207 :   u = cgetg(bco+1,t_MAT);
    3178      676207 :   for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col(a,gel(b,j), aco,p);
    3179      676207 :   return u;
    3180             : }
    3181             : 
    3182             : static GEN
    3183      113427 : Flm_gauss_CUP(GEN a, GEN b, ulong *detp, ulong p) {
    3184             :   GEN R, C, U, P, X, Y;
    3185      113427 :   long i, n = lg(a) - 1, r;
    3186      113427 :   if (nbrows(a) < n || (r = Flm_CUP(a, &R, &C, &U, &P, p)) < n)
    3187          14 :     return NULL;
    3188      113413 :   Y = Flm_rsolve_lower_unit(rowpermute(C, R),
    3189             :                             rowpermute(b, R), p);
    3190      113413 :   X = rowpermute(Flm_rsolve_upper(U, Y, p),
    3191             :                  perm_inv(P));
    3192      113413 :   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      113413 :   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      794966 : Flm_inv_i(GEN a, ulong *detp, ulong p, long inplace) {
    3218      794966 :   pari_sp av = avma;
    3219      794966 :   long n = lg(a) - 1;
    3220             :   GEN b, x;
    3221      794966 :   if (n == 0) return cgetg(1, t_MAT);
    3222      794966 :   b = matid_Flm(nbrows(a));
    3223      794966 :   if (n >= Flm_CUP_LIMIT)
    3224      113413 :     x = Flm_gauss_CUP(a, b, detp, p);
    3225             :   else {
    3226      681553 :     if (!inplace)
    3227        5369 :       a = RgM_shallowcopy(a);
    3228      681553 :     x = Flm_gauss_sp(a, b, detp, p);
    3229             :   }
    3230      794966 :   if (!x) { avma = av; return NULL; }
    3231      794959 :   return gerepileupto(av, x);
    3232             : }
    3233             : 
    3234             : static GEN
    3235      780595 : Flm_inv_sp(GEN a, ulong *detp, ulong p) {
    3236      780595 :   return Flm_inv_i(a, detp, p, 1);
    3237             : }
    3238             : 
    3239             : GEN
    3240       14371 : Flm_inv(GEN a, ulong p) {
    3241       14371 :   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       50766 : FpM_gauss_i(GEN a, GEN b, GEN p, ulong *pp)
    3263             : {
    3264       50766 :   long n = nbrows(a);
    3265       50766 :   a = FpM_init(a,p,pp);
    3266       50766 :   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       12824 :     if (b) b = ZM_to_F2m(b); else b = matid_F2m(n);
    3273       12824 :     return F2m_gauss_sp(a,b);
    3274             :   default:
    3275       35091 :     if (b) b = ZM_to_Flm(b, *pp); else b = matid_Flm(n);
    3276       35091 :     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       50738 : FpM_inv(GEN a, GEN p)
    3298             : {
    3299       50738 :   pari_sp av = avma;
    3300             :   ulong pp;
    3301             :   GEN u;
    3302       50738 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    3303       50738 :   u = FpM_gauss_i(a, NULL, p, &pp);
    3304       50738 :   if (!u) { avma = av; return NULL; }
    3305       50738 :   switch(pp)
    3306             :   {
    3307        2823 :   case 0: return gerepilecopy(av, u);
    3308       12824 :   case 2:  u = F2m_to_ZM(u); break;
    3309       35091 :   default: u = Flm_to_ZM(u); break;
    3310             :   }
    3311       47915 :   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      514704 : ZM_gauss(GEN a, GEN b0)
    3437             : {
    3438      514704 :   pari_sp av = avma, av2;
    3439             :   int iscol;
    3440             :   long n, ncol, i, m, elim;
    3441             :   ulong p;
    3442      514704 :   GEN N, C, delta, xb, nb, nmin, res, b = b0;
    3443             :   forprime_t S;
    3444             : 
    3445      514704 :   if (!init_gauss(a, &b, &n, &ncol, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    3446      514634 :   nb = gen_0; ncol = lg(b);
    3447     1041419 :   for (i = 1; i < ncol; i++)
    3448             :   {
    3449      526785 :     GEN ni = gnorml2(gel(b, i));
    3450      526785 :     if (cmpii(nb, ni) < 0) nb = ni;
    3451             :   }
    3452      514634 :   if (!signe(nb)) { avma = av; return gcopy(b0); }
    3453      514634 :   delta = gen_1; nmin = nb;
    3454     2030727 :   for (i = 1; i <= n; i++)
    3455             :   {
    3456     1516093 :     GEN ni = gnorml2(gel(a, i));
    3457     1516093 :     if (cmpii(ni, nmin) < 0)
    3458             :     {
    3459        2615 :       delta = mulii(delta, nmin); nmin = ni;
    3460             :     }
    3461             :     else
    3462     1513478 :       delta = mulii(delta, ni);
    3463             :   }
    3464      514634 :   if (!signe(nmin)) return NULL;
    3465      514620 :   elim = expi(delta)+1;
    3466      514620 :   av2 = avma;
    3467      514620 :   init_modular_big(&S);
    3468             :   for(;;)
    3469             :   {
    3470      514620 :     p = u_forprime_next(&S);
    3471      514620 :     C = Flm_inv_sp(ZM_to_Flm(a, p), NULL, p);
    3472      514620 :     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     1029240 :   m = (long)ceil((rtodbl(logr_abs(itor(delta,LOWDEFAULTPREC))) + 1)
    3481      514620 :                  / log((double)p));
    3482      514620 :   xb = ZlM_gauss(a, b, p, m, C);
    3483      514620 :   N = powuu(p, m);
    3484      514620 :   delta = sqrti(delta);
    3485      514620 :   if (iscol)
    3486      509513 :     res = FpC_ratlift(gel(xb,1), N, delta,delta, NULL);
    3487             :   else
    3488        5107 :     res = FpM_ratlift(xb, N, delta,delta, NULL);
    3489      514620 :   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      125354 : ZM_inv(GEN M, GEN dM)
    3495             : {
    3496      125354 :   pari_sp av2, av = avma;
    3497             :   GEN Hp,q,H;
    3498             :   ulong p;
    3499      125354 :   long lM = lg(M), stable = 0;
    3500      125354 :   int negate = 0;
    3501             :   forprime_t S;
    3502             :   pari_timer ti;
    3503             : 
    3504      125354 :   if (lM == 1) return cgetg(1,t_MAT);
    3505             : 
    3506             :   /* HACK: include dM = -1 ! */
    3507      121980 :   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      101713 :     if (signe(dM) < 0) negate = 1;
    3512      101713 :     dM = gen_1;
    3513             :   }
    3514      121980 :   init_modular_big(&S);
    3515      121980 :   av2 = avma;
    3516      121980 :   H = NULL;
    3517      121980 :   if (DEBUGLEVEL>5) timer_start(&ti);
    3518      373356 :   while ((p = u_forprime_next(&S)))
    3519             :   {
    3520             :     ulong dMp;
    3521             :     GEN Mp;
    3522      251376 :     Mp = ZM_to_Flm(M,p);
    3523      251376 :     if (dM == gen_1)
    3524      206009 :       Hp = Flm_inv_sp(Mp, NULL, p);
    3525             :     else
    3526             :     {
    3527       45367 :       if (dM) {
    3528       45367 :         dMp = umodiu(dM,p); if (!dMp) continue;
    3529       45367 :         Hp = Flm_inv_sp(Mp, NULL, p);
    3530       45367 :         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       45367 :       if (dMp != 1) Flm_Fl_mul_inplace(Hp, dMp, p);
    3536             :     }
    3537             : 
    3538      251376 :     if (!H)
    3539             :     {
    3540      121980 :       H = ZM_init_CRT(Hp, p);
    3541      121980 :       q = utoipos(p);
    3542             :     }
    3543             :     else
    3544      129396 :       stable = ZM_incremental_CRT(&H, Hp, &q, p);
    3545      251376 :     if (DEBUGLEVEL>5) timer_printf(&ti, "ZM_inv mod %lu (stable=%ld)",p,stable);
    3546      251376 :     if (stable && ZM_isscalar(ZM_mul(M, H), dM)) break;
    3547      129396 :     if (gc_needed(av,2))
    3548             :     {
    3549          14 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv");
    3550          14 :       gerepileall(av2, 2, &H, &q);
    3551             :     }
    3552             :   }
    3553      121980 :   if (!p) pari_err_OVERFLOW("ZM_inv [ran out of primes]");
    3554      121980 :   if (DEBUGLEVEL>5) err_printf("ZM_inv done\n");
    3555      121980 :   if (negate)
    3556           0 :     return gerepileupto(av, ZM_neg(H));
    3557             :   else
    3558      121980 :     return gerepilecopy(av, H);
    3559             : }
    3560             : 
    3561             : /* to be used when denom(M^(-1)) << det(M) and a sharp multiple is
    3562             :  * not available. Return H primitive such that M*H = den*Id */
    3563             : GEN
    3564       11214 : ZM_inv_ratlift(GEN M, GEN *pden)
    3565             : {
    3566       11214 :   pari_sp av2, av = avma;
    3567             :   GEN Hp, q, H;
    3568             :   ulong p;
    3569       11214 :   long lM = lg(M);
    3570             :   forprime_t S;
    3571       11214 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    3572             : 
    3573       10507 :   init_modular_big(&S);
    3574       10507 :   av2 = avma;
    3575       10507 :   H = NULL;
    3576       22580 :   while ((p = u_forprime_next(&S)))
    3577             :   {
    3578             :     GEN Mp, B, Hr;
    3579       12073 :     Mp = ZM_to_Flm(M,p);
    3580       12073 :     Hp = Flm_inv_sp(Mp, NULL, p);
    3581       12073 :     if (!Hp) continue;
    3582       12073 :     if (!H)
    3583             :     {
    3584       10507 :       H = ZM_init_CRT(Hp, p);
    3585       10507 :       q = utoipos(p);
    3586             :     }
    3587             :     else
    3588        1566 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3589       12073 :     B = sqrti(shifti(q,-1));
    3590       12073 :     Hr = FpM_ratlift(H,q,B,B,NULL);
    3591       12073 :     if (DEBUGLEVEL>5) err_printf("ZM_inv mod %lu (ratlift=%ld)\n", p,!!Hr);
    3592       12073 :     if (Hr) {/* DONE ? */
    3593       10574 :       GEN Hl = Q_remove_denom(Hr, pden);
    3594       10574 :       if (ZM_isscalar(ZM_mul(M, Hl), *pden)) { H = Hl; break; }
    3595             :     }
    3596             : 
    3597        1566 :     if (gc_needed(av,2))
    3598             :     {
    3599           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv_ratlift");
    3600           0 :       gerepileall(av2, 2, &H, &q);
    3601             :     }
    3602             :   }
    3603       10507 :   if (!*pden) *pden = gen_1;
    3604       10507 :   gerepileall(av, 2, &H, pden);
    3605       10507 :   return H;
    3606             : }
    3607             : 
    3608             : /* same as above, M rational */
    3609             : GEN
    3610         924 : QM_inv(GEN M, GEN dM)
    3611             : {
    3612         924 :   pari_sp av = avma;
    3613         924 :   GEN cM, pM = Q_primitive_part(M, &cM);
    3614         924 :   if (!cM) return ZM_inv(pM,dM);
    3615         924 :   return gerepileupto(av, ZM_inv(pM, gdiv(dM,cM)));
    3616             : }
    3617             : 
    3618             : GEN
    3619       48245 : ZM_ker(GEN M)
    3620             : {
    3621       48245 :   pari_sp av2, av = avma;
    3622             :   GEN q, H, D;
    3623             :   forprime_t S;
    3624       48245 :   av2 = avma;
    3625       48245 :   H = NULL; D = NULL;
    3626       48245 :   if (lg(M)==1) return cgetg(1, t_MAT);
    3627       48238 :   init_modular_big(&S);
    3628             :   for(;;)
    3629             :   {
    3630             :     GEN Kp, Hp, Dp, Mp, Hr, B;
    3631      108883 :     ulong p = u_forprime_next(&S);
    3632      108883 :     Mp = ZM_to_Flm(M, p);
    3633      108883 :     Kp = Flm_ker_sp(Mp, p, 2);
    3634      108883 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    3635      108883 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    3636      100975 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    3637             :     {
    3638       85918 :       H = ZM_init_CRT(Hp, p); D = Dp;
    3639       85918 :       q = utoipos(p);
    3640             :     }
    3641             :     else
    3642       15057 :       ZM_incremental_CRT(&H, Hp, &q, p);
    3643      100975 :     B = sqrti(shifti(q,-1));
    3644      100975 :     Hr = FpM_ratlift(H, q, B, B, NULL);
    3645      100975 :     if (DEBUGLEVEL>5) err_printf("ZM_ker mod %lu (ratlift=%ld)\n", p,!!Hr);
    3646      100975 :     if (Hr) {/* DONE ? */
    3647       96342 :       GEN Hl = vec_Q_primpart(Q_remove_denom(Hr, NULL));
    3648       96342 :       GEN MH = ZM_mul(M, Hl);
    3649       96342 :       if (gequal0(MH)) { H = Hl;  break; }
    3650             :     }
    3651       52737 :     if (gc_needed(av,2))
    3652             :     {
    3653           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_ker");
    3654           0 :       gerepileall(av2, 3, &H, &D, &q);
    3655             :     }
    3656       60645 :   }
    3657       48238 :   return gerepilecopy(av, H);
    3658             : }
    3659             : 
    3660             : /* x a ZM. Return a multiple of the determinant of the lattice generated by
    3661             :  * the columns of x. From Algorithm 2.2.6 in GTM138 */
    3662             : GEN
    3663      101743 : detint(GEN A)
    3664             : {
    3665      101743 :   if (typ(A) != t_MAT) pari_err_TYPE("detint",A);
    3666      101743 :   RgM_check_ZM(A, "detint");
    3667      101743 :   return ZM_detmult(A);
    3668             : }
    3669             : GEN
    3670      131871 : ZM_detmult(GEN A)
    3671             : {
    3672      131871 :   pari_sp av1, av = avma;
    3673             :   GEN B, c, v, piv;
    3674      131871 :   long rg, i, j, k, m, n = lg(A) - 1;
    3675             : 
    3676      131871 :   if (!n) return gen_1;
    3677      131871 :   m = nbrows(A);
    3678      131871 :   if (n < m) return gen_0;
    3679      131850 :   c = zero_zv(m);
    3680      131850 :   av1 = avma;
    3681      131850 :   B = zeromatcopy(m,m);
    3682      131850 :   v = cgetg(m+1, t_COL);
    3683      131850 :   piv = gen_1; rg = 0;
    3684      912000 :   for (k=1; k<=n; k++)
    3685             :   {
    3686      911986 :     GEN pivprec = piv;
    3687      911986 :     long t = 0;
    3688     7921833 :     for (i=1; i<=m; i++)
    3689             :     {
    3690     7009847 :       pari_sp av2 = avma;
    3691             :       GEN vi;
    3692     7009847 :       if (c[i]) continue;
    3693             : 
    3694     3961165 :       vi = mulii(piv, gcoeff(A,i,k));
    3695    35302851 :       for (j=1; j<=m; j++)
    3696    31341686 :         if (c[j]) vi = addii(vi, mulii(gcoeff(B,j,i),gcoeff(A,j,k)));
    3697     3961165 :       if (!t && signe(vi)) t = i;
    3698     3961165 :       gel(v,i) = gerepileuptoint(av2, vi);
    3699             :     }
    3700      911986 :     if (!t) continue;
    3701             :     /* at this point c[t] = 0 */
    3702             : 
    3703      911902 :     if (++rg >= m) { /* full rank; mostly done */
    3704      131836 :       GEN det = gel(v,t); /* last on stack */
    3705      131836 :       if (++k > n)
    3706      117746 :         det = absi(det);
    3707             :       else
    3708             :       {
    3709             :         /* improve further; at this point c[i] is set for all i != t */
    3710       14090 :         gcoeff(B,t,t) = piv; v = centermod(gel(B,t), det);
    3711      799118 :         for ( ; k<=n; k++)
    3712      785028 :           det = gcdii(det, ZV_dotproduct(v, gel(A,k)));
    3713             :       }
    3714      131836 :       return gerepileuptoint(av, det);
    3715             :     }
    3716             : 
    3717      780066 :     piv = gel(v,t);
    3718     6877528 :     for (i=1; i<=m; i++)
    3719             :     {
    3720             :       GEN mvi;
    3721     6097462 :       if (c[i] || i == t) continue;
    3722             : 
    3723     3048731 :       gcoeff(B,t,i) = mvi = negi(gel(v,i));
    3724    27377028 :       for (j=1; j<=m; j++)
    3725    24328297 :         if (c[j]) /* implies j != t */
    3726             :         {
    3727     6076945 :           pari_sp av2 = avma;
    3728     6076945 :           GEN z = addii(mulii(gcoeff(B,j,i), piv), mulii(gcoeff(B,j,t), mvi));
    3729     6076945 :           if (rg > 1) z = diviiexact(z, pivprec);
    3730     6076945 :           gcoeff(B,j,i) = gerepileuptoint(av2, z);
    3731             :         }
    3732             :     }
    3733      780066 :     c[t] = k;
    3734      780066 :     if (gc_needed(av,1))
    3735             :     {
    3736           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"detint. k=%ld",k);
    3737           0 :       gerepileall(av1, 2, &piv,&B); v = zerovec(m);
    3738             :     }
    3739             :   }
    3740          14 :   avma = av; return gen_0;
    3741             : }
    3742             : 
    3743             : /* Reduce x modulo (invertible) y */
    3744             : GEN
    3745       12957 : closemodinvertible(GEN x, GEN y)
    3746             : {
    3747       12957 :   return gmul(y, ground(RgM_solve(y,x)));
    3748             : }
    3749             : GEN
    3750           7 : reducemodinvertible(GEN x, GEN y)
    3751             : {
    3752           7 :   return gsub(x, closemodinvertible(x,y));
    3753             : }
    3754             : GEN
    3755           0 : reducemodlll(GEN x,GEN y)
    3756             : {
    3757           0 :   return reducemodinvertible(x, ZM_lll(y, 0.75, LLL_INPLACE));
    3758             : }
    3759             : 
    3760             : /*******************************************************************/
    3761             : /*                                                                 */
    3762             : /*                    KERNEL of an m x n matrix                    */
    3763             : /*          return n - rk(x) linearly independent vectors          */
    3764             : /*                                                                 */
    3765             : /*******************************************************************/
    3766             : static GEN
    3767         126 : deplin_aux(GEN x0)
    3768             : {
    3769         126 :   pari_sp av = avma;
    3770         126 :   long i, j, k, nl, nc = lg(x0)-1;
    3771             :   GEN D, x, y, c, l, d, ck;
    3772             : 
    3773         126 :   if (!nc) { avma=av; return cgetg(1,t_COL); }
    3774          91 :   x = RgM_shallowcopy(x0);
    3775          91 :   nl = nbrows(x);
    3776          91 :   d = const_vec(nl, gen_1); /* pivot list */
    3777          91 :   c = zero_zv(nl);
    3778          91 :   l = cgetg(nc+1, t_VECSMALL); /* not initialized */
    3779          91 :   ck = NULL; /* gcc -Wall */
    3780         399 :   for (k=1; k<=nc; k++)
    3781             :   {
    3782         378 :     ck = gel(x,k);
    3783        1176 :     for (j=1; j<k; j++)
    3784             :     {
    3785         798 :       GEN cj = gel(x,j), piv = gel(d,j), q = gel(ck,l[j]);
    3786        9828 :       for (i=1; i<=nl; i++)
    3787        9030 :         if (i!=l[j]) gel(ck,i) = gsub(gmul(piv, gel(ck,i)), gmul(q, gel(cj,i)));
    3788             :     }
    3789             : 
    3790         378 :     i = gauss_get_pivot_NZ(x, NULL, k, c);
    3791         378 :     if (i > nl) break;
    3792             : 
    3793         308 :     gel(d,k) = gel(ck,i);
    3794         308 :     c[i] = k; l[k] = i; /* pivot d[k] in x[i,k] */
    3795             :   }
    3796          91 :   if (k > nc) { avma = av; return cgetg(1,t_COL); }
    3797          70 :   if (k == 1) { avma = av; return scalarcol_shallow(gen_1,nc); }
    3798          70 :   y = cgetg(nc+1,t_COL);
    3799          70 :   gel(y,1) = gcopy(gel(ck, l[1]));
    3800         203 :   for (D=gel(d,1),j=2; j<k; j++)
    3801             :   {
    3802         133 :     gel(y,j) = gmul(gel(ck, l[j]), D);
    3803         133 :     D = gmul(D, gel(d,j));
    3804             :   }
    3805          70 :   gel(y,j) = gneg(D);
    3806          70 :   for (j++; j<=nc; j++) gel(y,j) = gen_0;
    3807          70 :   y = primitive_part(y, &c);
    3808          70 :   return c? gerepileupto(av, y): gerepilecopy(av, y);
    3809             : }
    3810             : static GEN
    3811           0 : RgV_deplin(GEN v)
    3812             : {
    3813           0 :   pari_sp av = avma;
    3814           0 :   long n = lg(v)-1;
    3815           0 :   GEN y, p = NULL;
    3816           0 :   if (n <= 1)
    3817             :   {
    3818           0 :     if (n == 1 && gequal0(gel(v,1))) return mkcol(gen_1);
    3819           0 :     return cgetg(1, t_COL);
    3820             :   }
    3821           0 :   if (gequal0(gel(v,1))) return scalarcol_shallow(gen_1, n);
    3822           0 :   v = primpart(mkvec2(gel(v,1),gel(v,2)));
    3823           0 :   if (RgV_is_FpV(v, &p) && p) v = centerlift(v);
    3824           0 :   y = zerocol(n);
    3825           0 :   gel(y,1) = gneg(gel(v,2));
    3826           0 :   gel(y,2) = gcopy(gel(v,1));
    3827           0 :   return gerepileupto(av, y);
    3828             : 
    3829             : }
    3830             : GEN
    3831         273 : deplin(GEN x)
    3832             : {
    3833         273 :   GEN p = NULL, ff = NULL;
    3834         273 :   switch(typ(x))
    3835             :   {
    3836         273 :     case t_MAT: break;
    3837           0 :     case t_VEC: return RgV_deplin(x);
    3838           0 :     default: pari_err_TYPE("deplin",x);
    3839             :   }
    3840         273 :   if (RgM_is_FpM(x, &p) && p)
    3841             :   {
    3842          84 :     pari_sp av = avma;
    3843             :     ulong pp;
    3844          84 :     x = RgM_Fp_init(x, p, &pp);
    3845          84 :     switch(pp)
    3846             :     {
    3847             :     case 0:
    3848          14 :       x = FpM_ker_gen(x,p,1);
    3849          14 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3850           7 :       x = FpC_center(x,p,shifti(p,-1));
    3851           7 :       break;
    3852             :     case 2:
    3853          14 :       x = F2m_ker_sp(x,1);
    3854          14 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3855           7 :       x = F2c_to_ZC(x); break;
    3856             :     default:
    3857          56 :       x = Flm_ker_sp(x,pp,1);
    3858          56 :       if (!x) { avma = av; return cgetg(1,t_COL); }
    3859          35 :       x = Flv_center(x, pp, pp>>1);
    3860          35 :       x = zc_to_ZC(x);
    3861          35 :       break;
    3862             :     }
    3863          49 :     return gerepileupto(av, x);
    3864             :   }
    3865         189 :   if (RgM_is_FFM(x, &ff))
    3866             :   {
    3867          63 :     x = FFM_deplin(x, ff);
    3868          63 :     if (!x) return cgetg(1, t_COL);
    3869          35 :     return x;
    3870             :   }
    3871         126 :   return deplin_aux(x);
    3872             : }
    3873             : 
    3874             : /*******************************************************************/
    3875             : /*                                                                 */
    3876             : /*         GAUSS REDUCTION OF MATRICES  (m lines x n cols)         */
    3877             : /*           (kernel, image, complementary image, rank)            */
    3878             : /*                                                                 */
    3879             : /*******************************************************************/
    3880             : /* return the transform of x under a standard Gauss pivot.
    3881             :  * x0 is a reference point when guessing whether x[i,j] ~ 0
    3882             :  * (iff x[i,j] << x0[i,j])
    3883             :  * Set r = dim ker(x). d[k] contains the index of the first non-zero pivot
    3884             :  * in column k */
    3885             : static GEN
    3886       15946 : gauss_pivot_ker(GEN x, GEN x0, GEN *dd, long *rr)
    3887             : {
    3888             :   GEN c, d, p, data;
    3889             :   pari_sp av;
    3890             :   long i, j, k, r, t, n, m;
    3891             :   pivot_fun pivot;
    3892             : 
    3893       15946 :   n=lg(x)-1; if (!n) { *dd=NULL; *rr=0; return cgetg(1,t_MAT); }
    3894       15911 :   m=nbrows(x); r=0;
    3895       15911 :   pivot = get_pivot_fun(x, x0, &data);
    3896       15911 :   x = RgM_shallowcopy(x);
    3897       15911 :   c = zero_zv(m);
    3898       15911 :   d = cgetg(n+1,t_VECSMALL);
    3899       15911 :   av=avma;
    3900      170275 :   for (k=1; k<=n; k++)
    3901             :   {
    3902      154364 :     j = pivot(x, data, k, c);
    3903      154364 :     if (j > m)
    3904             :     {
    3905       71491 :       r++; d[k]=0;
    3906      783006 :       for(j=1; j<k; j++)
    3907      711515 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
    3908             :     }
    3909             :     else
    3910             :     { /* pivot for column k on row j */
    3911       82873 :       c[j]=k; d[k]=j; p = gdiv(gen_m1,gcoeff(x,j,k));
    3912       82873 :       gcoeff(x,j,k) = gen_m1;
    3913             :       /* x[j,] /= - x[j,k] */
    3914       82873 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3915     5471809 :       for (t=1; t<=m; t++)
    3916     5388936 :         if (t!=j)
    3917             :         { /* x[t,] -= 1 / x[j,k] x[j,] */
    3918     5306063 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3919    67762219 :           for (i=k+1; i<=n; i++)
    3920    62456156 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i),gmul(p,gcoeff(x,j,i)));
    3921     5306063 :           if (gc_needed(av,1)) gerepile_gauss_ker(x,k,t,av);
    3922             :         }
    3923             :     }
    3924             :   }
    3925       15911 :   *dd=d; *rr=r; return x;
    3926             : }
    3927             : 
    3928             : static GEN FpM_gauss_pivot(GEN x, GEN p, long *rr);
    3929             : static GEN FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr);
    3930             : static GEN F2m_gauss_pivot(GEN x, long *rr);
    3931             : 
    3932             : /* r = dim ker(x).
    3933             :  * Returns d:
    3934             :  *   d[k] != 0 contains the index of a non-zero pivot in column k
    3935             :  *   d[k] == 0 if column k is a linear combination of the (k-1) first ones */
    3936             : GEN
    3937       26601 : RgM_pivots(GEN x0, GEN data, long *rr, pivot_fun pivot)
    3938             : {
    3939             :   GEN x, c, d, p;
    3940       26601 :   long i, j, k, r, t, m, n = lg(x0)-1;
    3941             :   pari_sp av;
    3942             : 
    3943       26601 :   if (RgM_is_ZM(x0)) return ZM_pivots(x0, rr);
    3944       24165 :   if (!n) { *rr = 0; return NULL; }
    3945             : 
    3946       24165 :   d = cgetg(n+1, t_VECSMALL);
    3947       24165 :   x = RgM_shallowcopy(x0);
    3948       24165 :   m = nbrows(x); r = 0;
    3949       24165 :   c = zero_zv(m);
    3950       24165 :   av = avma;
    3951      917950 :   for (k=1; k<=n; k++)
    3952             :   {
    3953      893785 :     j = pivot(x, data, k, c);
    3954      893785 :     if (j > m) { r++; d[k] = 0; }
    3955             :     else
    3956             :     {
    3957       46916 :       c[j] = k; d[k] = j; p = gdiv(gen_m1, gcoeff(x,j,k));
    3958       46916 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3959             : 
    3960      309177 :       for (t=1; t<=m; t++)
    3961      262261 :         if (!c[t]) /* no pivot on that line yet */
    3962             :         {
    3963      135633 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3964     6776740 :           for (i=k+1; i<=n; i++)
    3965     6641107 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i), gmul(p, gcoeff(x,j,i)));
    3966      135633 :           if (gc_needed(av,1)) gerepile_gauss(x,k,t,av,j,c);
    3967             :         }
    3968       46916 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = gen_0; /* dummy */
    3969             :     }
    3970             :   }
    3971       24165 :   *rr = r; avma = (pari_sp)d; return d;
    3972             : }
    3973             : 
    3974             : static long
    3975      100538 : ZM_count_0_cols(GEN M)
    3976             : {
    3977      100538 :   long i, l = lg(M), n = 0;
    3978      534263 :   for (i = 1; i < l; i++)
    3979      433725 :     if (ZV_equal0(gel(M,i))) n++;
    3980      100538 :   return n;
    3981             : }
    3982             : 
    3983             : static void indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol);
    3984             : /* As RgM_pivots, integer entries. Set *rr = dim Ker M0 */
    3985             : GEN
    3986      104281 : ZM_pivots(GEN M0, long *rr)
    3987             : {
    3988      104281 :   GEN d, dbest = NULL;
    3989             :   long m, n, i, imax, rmin, rbest, zc;
    3990      104281 :   int beenthere = 0;
    3991      104281 :   pari_sp av, av0 = avma;
    3992             :   forprime_t S;
    3993             : 
    3994      104281 :   rbest = n = lg(M0)-1;
    3995      104281 :   if (n == 0) { *rr = 0; return NULL; }
    3996      100538 :   zc = ZM_count_0_cols(M0);
    3997      100538 :   if (n == zc) { *rr = zc; return zero_zv(n); }
    3998             : 
    3999      100202 :   m = nbrows(M0);
    4000      100202 :   rmin = maxss(zc, n-m);
    4001      100202 :   init_modular(&S);
    4002      100202 :   imax = (n < (1<<4))? 1: (n>>3); /* heuristic */
    4003             : 
    4004             :   for(;;)
    4005             :   {
    4006             :     GEN row, col, M, KM, IM, RHS, X, cX;
    4007             :     long rk;
    4008      106506 :     for (av = avma, i = 0;; avma = av, i++)
    4009             :     {
    4010      106506 :       ulong p = u_forprime_next(&S);
    4011             :       long rp;
    4012      106506 :       if (!p) pari_err_OVERFLOW("ZM_pivots [ran out of primes]");
    4013      106506 :       d = Flm_pivots(ZM_to_Flm(M0, p), p, &rp, 1);
    4014      106506 :       if (rp == rmin) { rbest = rp; goto END; } /* maximal rank, return */
    4015       11411 :       if (rp < rbest) { /* save best r so far */
    4016        5114 :         rbest = rp;
    4017        5114 :         if (dbest) gunclone(dbest);
    4018        5114 :         dbest = gclone(d);
    4019       10221 :         if (beenthere) break;
    4020             :       }
    4021       11411 :       if (!beenthere && i >= imax) break;
    4022        6304 :     }
    4023        5107 :     beenthere = 1;
    4024             :     /* Dubious case: there is (probably) a non trivial kernel */
    4025        5107 :     indexrank_all(m,n, rbest, dbest, &row, &col);
    4026        5107 :     M = rowpermute(vecpermute(M0, col), row);
    4027        5107 :     rk = n - rbest; /* (probable) dimension of image */
    4028        5107 :     IM = vecslice(M,1,rk);
    4029        5107 :     KM = vecslice(M,rk+1, n);
    4030        5107 :     M = rowslice(IM, 1,rk); /* square maximal rank */
    4031        5107 :     X = ZM_gauss(M, rowslice(KM, 1,rk));
    4032        5107 :     X = Q_remove_denom(X, &cX);
    4033        5107 :     RHS = rowslice(KM,rk+1,m);
    4034        5107 :     if (cX) RHS = ZM_Z_mul(RHS, cX);
    4035        5107 :     if (ZM_equal(ZM_mul(rowslice(IM,rk+1,m), X), RHS))
    4036             :     {
    4037        5107 :       d = vecsmall_copy(dbest);
    4038        5107 :       goto END;
    4039             :     }
    4040           0 :     avma = av;
    4041           0 :   }
    4042             : END:
    4043      100202 :   *rr = rbest; if (dbest) gunclone(dbest);
    4044      100202 :   return gerepileuptoleaf(av0, d);
    4045             : }
    4046             : 
    4047             : /* set *pr = dim Ker x */
    4048             : static GEN
    4049       16632 : gauss_pivot(GEN x, long *pr) {
    4050             :   GEN data;
    4051       16632 :   pivot_fun pivot = get_pivot_fun(x, x, &data);
    4052       16632 :   return RgM_pivots(x, data, pr, pivot);
    4053             : }
    4054             : 
    4055             : /* compute ker(x), x0 is a reference point when guessing whether x[i,j] ~ 0
    4056             :  * (iff x[i,j] << x0[i,j]) */
    4057             : static GEN
    4058       15946 : ker_aux(GEN x, GEN x0)
    4059             : {
    4060       15946 :   pari_sp av = avma;
    4061             :   GEN d,y;
    4062             :   long i,j,k,r,n;
    4063             : 
    4064       15946 :   x = gauss_pivot_ker(x,x0,&d,&r);
    4065       15946 :   if (!r) { avma=av; return cgetg(1,t_MAT); }
    4066       15589 :   n = lg(x)-1; y=cgetg(r+1,t_MAT);
    4067       87080 :   for (j=k=1; j<=r; j++,k++)
    4068             :   {
    4069       71491 :     GEN p = cgetg(n+1,t_COL);
    4070             : 
    4071       71491 :     gel(y,j) = p; while (d[k]) k++;
    4072      783006 :     for (i=1; i<k; i++)
    4073      711515 :       if (d[i])
    4074             :       {
    4075      480928 :         GEN p1=gcoeff(x,d[i],k);
    4076      480928 :         gel(p,i) = gcopy(p1); gunclone(p1);
    4077             :       }
    4078             :       else
    4079      230587 :         gel(p,i) = gen_0;
    4080       71491 :     gel(p,k) = gen_1; for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
    4081             :   }
    4082       15589 :   return gerepileupto(av,y);
    4083             : }
    4084             : GEN
    4085       15904 : ker(GEN x)
    4086             : {
    4087       15904 :   pari_sp av = avma;
    4088       15904 :   GEN p = NULL, ff = NULL;
    4089       15904 :   if (RgM_is_FpM(x, &p) && p)
    4090             :   {
    4091             :     ulong pp;
    4092          56 :     x = RgM_Fp_init(x, p, &pp);
    4093          56 :     switch(pp)
    4094             :     {
    4095          14 :     case 0: x = FpM_to_mod(FpM_ker_gen(x,p,0),p); break;
    4096           7 :     case 2: x = F2m_to_mod(F2m_ker_sp(x,0)); break;
    4097          35 :     default:x = Flm_to_mod(Flm_ker_sp(x,pp,0), pp); break;
    4098             :     }
    4099          56 :     return gerepileupto(av, x);
    4100             :   }
    4101       15848 :   if (RgM_is_FFM(x, &ff)) return FFM_ker(x, ff);
    4102       15792 :   return ker_aux(x,x);
    4103             : }
    4104             : GEN
    4105       46116 : matker0(GEN x,long flag)
    4106             : {
    4107       46116 :   if (typ(x)!=t_MAT) pari_err_TYPE("matker",x);
    4108       46116 :   if (!flag) return ker(x);
    4109       45934 :   RgM_check_ZM(x, "matker");
    4110       45934 :   return ZM_ker(x);
    4111             : }
    4112             : 
    4113             : GEN
    4114        1911 : image(GEN x)
    4115             : {
    4116        1911 :   pari_sp av = avma;
    4117        1911 :   GEN d, ff = NULL, p = NULL;
    4118             :   long r;
    4119             : 
    4120        1911 :   if (typ(x)!=t_MAT) pari_err_TYPE("matimage",x);
    4121        1911 :   if (RgM_is_FpM(x, &p) && p)
    4122             :   {
    4123             :     ulong pp;
    4124          49 :     x = RgM_Fp_init(x, p, &pp);
    4125          49 :     switch(pp)
    4126             :     {
    4127          14 :     case 0: x = FpM_to_mod(FpM_image(x,p), p); break;
    4128           7 :     case 2: x = F2m_to_mod(F2m_image(x)); break;
    4129          28 :     default:x = Flm_to_mod(Flm_image(x,pp), pp);
    4130             :     }
    4131          49 :     return gerepileupto(av, x);
    4132             :   }
    4133        1862 :   if (RgM_is_FFM(x, &ff)) return FFM_image(x, ff);
    4134        1827 :   d = gauss_pivot(x,&r); /* d left on stack for efficiency */
    4135        1827 :   return image_from_pivot(x,d,r);
    4136             : }
    4137             : 
    4138             : static GEN
    4139          84 : imagecompl_aux(GEN x, GEN(*PIVOT)(GEN,long*))
    4140             : {
    4141          84 :   pari_sp av = avma;
    4142             :   GEN d,y;
    4143             :   long j,i,r;
    4144             : 
    4145          84 :   if (typ(x)!=t_MAT) pari_err_TYPE("imagecompl",x);
    4146          84 :   (void)new_chunk(lg(x) * 4 + 1); /* HACK */
    4147          84 :   d = PIVOT(x,&r); /* if (!d) then r = 0 */
    4148          84 :   avma = av; y = cgetg(r+1,t_VECSMALL);
    4149         126 :   for (i=j=1; j<=r; i++)
    4150          42 :     if (!d[i]) y[j++] = i;
    4151          84 :   return y;
    4152             : }
    4153             : GEN
    4154          84 : imagecompl(GEN x) { return imagecompl_aux(x, &gauss_pivot); }
    4155             : GEN
    4156           0 : ZM_imagecompl(GEN x) { return imagecompl_aux(x, &ZM_pivots); }
    4157             : 
    4158             : GEN
    4159        1484 : RgM_RgC_invimage(GEN A, GEN y)
    4160             : {
    4161        1484 :   pari_sp av = avma;
    4162        1484 :   long i, l = lg(A);
    4163        1484 :   GEN M, x, t, p = NULL;
    4164             : 
    4165        1484 :   if (RgM_is_FpM(A, &p) && RgV_is_FpV(y, &p) && p)
    4166             :   {
    4167             :     ulong pp;
    4168          28 :     A = RgM_Fp_init(A,p,&pp);
    4169          28 :     switch(pp)
    4170             :     {
    4171             :     case 0:
    4172           7 :       y = RgC_to_FpC(y,p);
    4173           7 :       x = FpM_FpC_invimage(A, y, p);
    4174           7 :       if (x) x = FpC_to_mod(x,p);
    4175           7 :       break;
    4176             :     case 2:
    4177           7 :       y = RgV_to_F2v(y);
    4178           7 :       x = F2m_F2c_invimage(A, y);
    4179           7 :       if (x) x = F2c_to_mod(x);
    4180           7 :       break;
    4181             :     default:
    4182          14 :       y = RgV_to_Flv(y,pp);
    4183          14 :       x = Flm_Flc_invimage(A, y, pp);
    4184          14 :       if (x) x = Flc_to_mod(x,pp);
    4185             :     }
    4186          28 :     if (!x) { avma = av; return NULL; }
    4187          28 :     return gerepileupto(av, x);
    4188             :   }
    4189             : 
    4190        1456 :   if (l==1) return NULL;
    4191        1456 :   if (lg(y) != lgcols(A)) pari_err_DIM("inverseimage");
    4192             :   {
    4193        1456 :     GEN ff = NULL;
    4194        1456 :     if (RgM_is_FFM(A, &ff) && RgC_is_FFC(y, &ff))
    4195          63 :       return FFM_FFC_invimage(A, y, ff);
    4196             :   }
    4197        1393 :   M = ker(shallowconcat(A, y));
    4198        1393 :   i = lg(M)-1;
    4199        1393 :   if (!i) { avma = av; return NULL; }
    4200             : 
    4201        1393 :   x = gel(M,i); t = gel(x,l);
    4202        1393 :   if (gequal0(t)) { avma = av; return NULL; }
    4203             : 
    4204        1365 :   t = gneg_i(t); setlg(x,l);
    4205        1365 :   return gerepileupto(av, RgC_Rg_div(x, t));
    4206             : }
    4207             : GEN
    4208       41795 : FpM_FpC_invimage(GEN A, GEN y, GEN p)
    4209             : {
    4210       41795 :   pari_sp av = avma;
    4211       41795 :   long i, l = lg(A);
    4212             :   GEN M, x, t;
    4213             : 
    4214       41795 :   if (lgefint(p) == 3)
    4215             :   {
    4216       41788 :     ulong pp = p[2];
    4217       41788 :     A = ZM_to_Flm(A, pp);
    4218       41788 :     y = ZV_to_Flv(y, pp);
    4219       41788 :     x = Flm_Flc_invimage(A,y,pp);
    4220       41788 :     if (!x) { avma = av; return NULL; }
    4221       41788 :     return gerepileupto(av, Flc_to_ZC(x));
    4222             :   }
    4223           7 :   if (l==1) return NULL;
    4224           7 :   if (lg(y) != lgcols(A)) pari_err_DIM("FpM_FpC_invimage");
    4225           7 :   M = FpM_ker(shallowconcat(A,y),p);
    4226           7 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4227             : 
    4228           7 :   x = gel(M,i); t = gel(x,l);
    4229           7 :   if (!signe(t)) { avma = av; return NULL; }
    4230             : 
    4231           7 :   setlg(x,l); t = Fp_inv(negi(t),p);
    4232           7 :   if (is_pm1(t)) return gerepilecopy(av, x);
    4233           7 :   return gerepileupto(av, FpC_Fp_mul(x, t, p));
    4234             : }
    4235             : GEN
    4236       47990 : Flm_Flc_invimage(GEN A, GEN y, ulong p)
    4237             : {
    4238       47990 :   pari_sp av = avma;
    4239       47990 :   long i, l = lg(A);
    4240             :   GEN M, x;
    4241             :   ulong t;
    4242             : 
    4243       47990 :   if (l==1) return NULL;
    4244       47990 :   if (lg(y) != lgcols(A)) pari_err_DIM("Flm_Flc_invimage");
    4245       47990 :   M = cgetg(l+1,t_MAT);
    4246       47990 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4247       47990 :   gel(M,l) = y; M = Flm_ker(M,p);
    4248       47990 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4249             : 
    4250       47990 :   x = gel(M,i); t = x[l];
    4251       47990 :   if (!t) { avma = av; return NULL; }
    4252             : 
    4253       47990 :   setlg(x,l); t = Fl_inv(Fl_neg(t,p),p);
    4254       47990 :   if (t!=1) x = Flv_Fl_mul(x, t, p);
    4255       47990 :   return gerepileuptoleaf(av, x);
    4256             : }
    4257             : GEN
    4258          21 : F2m_F2c_invimage(GEN A, GEN y)
    4259             : {
    4260          21 :   pari_sp av = avma;
    4261          21 :   long i, l = lg(A);
    4262             :   GEN M, x;
    4263             : 
    4264          21 :   if (l==1) return NULL;
    4265          21 :   if (lg(y) != lgcols(A)) pari_err_DIM("F2m_F2c_invimage");
    4266          21 :   M = cgetg(l+1,t_MAT);
    4267          21 :   for (i=1; i<l; i++) gel(M,i) = gel(A,i);
    4268          21 :   gel(M,l) = y; M = F2m_ker(M);
    4269          21 :   i = lg(M)-1; if (!i) { avma = av; return NULL; }
    4270             : 
    4271          21 :   x = gel(M,i);
    4272          21 :   if (!F2v_coeff(x,l)) { avma = av; return NULL; }
    4273          21 :   F2v_clear(x, x[1]); x[1]--; /* remove last coord */
    4274          21 :   return gerepileuptoleaf(av, x);
    4275             : }
    4276             : 
    4277             : /* Return X such that m X = v (t_COL or t_MAT), resp. an empty t_COL / t_MAT
    4278             :  * if no solution exist */
    4279             : GEN
    4280        1617 : inverseimage(GEN m, GEN v)
    4281             : {
    4282             :   GEN y;
    4283        1617 :   if (typ(m)!=t_MAT) pari_err_TYPE("inverseimage",m);
    4284        1617 :   switch(typ(v))
    4285             :   {
    4286             :     case t_COL:
    4287        1463 :       y = RgM_RgC_invimage(m,v);
    4288        1463 :       return y? y: cgetg(1,t_COL);
    4289             :     case t_MAT:
    4290         154 :       y = RgM_invimage(m, v);
    4291         154 :       return y? y: cgetg(1,t_MAT);
    4292             :   }
    4293           0 :   pari_err_TYPE("inverseimage",v);
    4294             :   return NULL;/*LCOV_EXCL_LINE*/
    4295             : }
    4296             : 
    4297             : static GEN
    4298          21 : Flm_invimage_CUP(GEN A, GEN B, ulong p) {
    4299          21 :   pari_sp av = avma;
    4300             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    4301             :   long r;
    4302          21 :   r = Flm_CUP(A, &R, &C, &U, &P, p);
    4303          21 :   Rc = indexcompl(R, nbrows(B));
    4304          21 :   C1 = rowpermute(C, R);
    4305          21 :   C2 = rowpermute(C, Rc);
    4306          21 :   B1 = rowpermute(B, R);
    4307          21 :   B2 = rowpermute(B, Rc);
    4308          21 :   Z = Flm_rsolve_lower_unit(C1, B1, p);
    4309          21 :   if (!gequal(Flm_mul(C2, Z, p), B2))
    4310          14 :     return NULL;
    4311          14 :   Y = vconcat(Flm_rsolve_upper(vecslice(U, 1, r), Z, p),
    4312          14 :               zero_Flm(lg(A) - 1 - r, lg(B) - 1));
    4313           7 :   X = rowpermute(Y, perm_inv(P));
    4314           7 :   return gerepileupto(av, X);
    4315             : }
    4316             : 
    4317             : static GEN
    4318          42 : Flm_invimage_i(GEN A, GEN B, ulong p)
    4319             : {
    4320             :   GEN d, x, X, Y;
    4321          42 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4322             : 
    4323          42 :   if (!nB) return cgetg(1, t_MAT);
    4324          42 :   if (nA + nB >= Flm_CUP_LIMIT && nbrows(B) >= Flm_CUP_LIMIT)
    4325          21 :     return Flm_invimage_CUP(A, B, p);
    4326             : 
    4327          21 :   x = Flm_ker_sp(shallowconcat(Flm_neg(A,p), B), p, 0);
    4328             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4329             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4330             :    * Y has at least nB columns and full rank */
    4331          21 :   nY = lg(x)-1;
    4332          21 :   if (nY < nB) return NULL;
    4333          21 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4334          21 :   d = cgetg(nB+1, t_VECSMALL);
    4335          56 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4336             :   {
    4337          49 :     for (; j>=1; j--)
    4338          42 :       if (coeff(Y,i,j)) { d[i] = j; break; }
    4339          42 :     if (!j) return NULL;
    4340             :   }
    4341             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4342          14 :   Y = vecpermute(Y, d);
    4343          14 :   x = vecpermute(x, d);
    4344          14 :   X = rowslice(x, 1, nA);
    4345          14 :   return Flm_mul(X, Flm_inv_upper_1(Y,p), p);
    4346             : }
    4347             : 
    4348             : static GEN
    4349           7 : F2m_invimage_i(GEN A, GEN B)
    4350             : {
    4351             :   GEN d, x, X, Y;
    4352           7 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4353           7 :   x = F2m_ker_sp(shallowconcat(A, B), 0);
    4354             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4355             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4356             :    * Y has at least nB columns and full rank */
    4357           7 :   nY = lg(x)-1;
    4358           7 :   if (nY < nB) return NULL;
    4359             : 
    4360             :   /* implicitly: Y = rowslice(x, nA+1, nA+nB), nB rows */
    4361           7 :   d = cgetg(nB+1, t_VECSMALL);
    4362          21 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4363             :   {
    4364          14 :     for (; j>=1; j--)
    4365          14 :       if (F2m_coeff(x,nA+i,j)) { d[i] = j; break; } /* Y[i,j] */
    4366          14 :     if (!j) return NULL;
    4367             :   }
    4368           7 :   x = vecpermute(x, d);
    4369             : 
    4370           7 :   X = F2m_rowslice(x, 1, nA);
    4371           7 :   Y = F2m_rowslice(x, nA+1, nA+nB);
    4372           7 :   return F2m_mul(X, F2m_inv_upper_1(Y));
    4373             : }
    4374             : GEN
    4375           0 : Flm_invimage(GEN A, GEN B, ulong p)
    4376             : {
    4377           0 :   pari_sp av = avma;
    4378           0 :   GEN X = Flm_invimage_i(A,B,p);
    4379           0 :   if (!X) { avma = av; return NULL; }
    4380           0 :   return gerepileupto(av, X);
    4381             : }
    4382             : GEN
    4383           0 : F2m_invimage(GEN A, GEN B)
    4384             : {
    4385           0 :   pari_sp av = avma;
    4386           0 :   GEN X = F2m_invimage_i(A,B);
    4387           0 :   if (!X) { avma = av; return NULL; }
    4388           0 :   return gerepileupto(av, X);
    4389             : }
    4390             : static GEN
    4391          14 : FpM_invimage_i(GEN A, GEN B, GEN p)
    4392             : {
    4393             :   GEN d, x, X, Y;
    4394          14 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4395          14 :   if (lgefint(p) == 3)
    4396             :   {
    4397           0 :     ulong pp = p[2];
    4398           0 :     A = ZM_to_Flm(A, pp);
    4399           0 :     B = ZM_to_Flm(B, pp);
    4400           0 :     x = Flm_invimage_i(A, B, pp);
    4401           0 :     return x? Flm_to_ZM(x): NULL;
    4402             :   }
    4403          14 :   x = FpM_ker(shallowconcat(ZM_neg(A), B), p);
    4404             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4405             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4406             :    * Y has at least nB columns and full rank */
    4407          14 :   nY = lg(x)-1;
    4408          14 :   if (nY < nB) return NULL;
    4409          14 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4410          14 :   d = cgetg(nB+1, t_VECSMALL);
    4411          35 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4412             :   {
    4413          35 :     for (; j>=1; j--)
    4414          28 :       if (signe(gcoeff(Y,i,j))) { d[i] = j; break; }
    4415          28 :     if (!j) return NULL;
    4416             :   }
    4417             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4418           7 :   Y = vecpermute(Y, d);
    4419           7 :   x = vecpermute(x, d);
    4420           7 :   X = rowslice(x, 1, nA);
    4421           7 :   return FpM_mul(X, FpM_inv_upper_1(Y,p), p);
    4422             : }
    4423             : GEN
    4424           0 : FpM_invimage(GEN A, GEN B, GEN p)
    4425             : {
    4426           0 :   pari_sp av = avma;
    4427           0 :   GEN X = FpM_invimage_i(A,B,p);
    4428           0 :   if (!X) { avma = av; return NULL; }
    4429           0 :   return gerepileupto(av, X);
    4430             : }
    4431             : 
    4432             : /* find Z such that A Z = B. Return NULL if no solution */
    4433             : GEN
    4434        7196 : RgM_invimage(GEN A, GEN B)
    4435             : {
    4436        7196 :   pari_sp av = avma;
    4437             :   GEN d, x, X, Y;
    4438        7196 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4439        7196 :   GEN p = NULL;
    4440        7196 :   if (RgM_is_FpM(A, &p) && RgM_is_FpM(B, &p) && p)
    4441             :   {
    4442             :     ulong pp;
    4443          63 :     A = RgM_Fp_init(A,p,&pp);
    4444          63 :     switch(pp)
    4445             :     {
    4446             :     case 0:
    4447          14 :       B = RgM_to_FpM(B,p);
    4448          14 :       x = FpM_invimage_i(A, B, p);
    4449          14 :       if (x) x = FpM_to_mod(x, p);
    4450          14 :     break;
    4451             :     case 2:
    4452           7 :       B = RgM_to_F2m(B);
    4453           7 :       x = F2m_invimage_i(A, B);
    4454           7 :       if (x) x = F2m_to_mod(x);
    4455           7 :       break;
    4456             :     default:
    4457          42 :       B = RgM_to_Flm(B,pp);
    4458          42 :       x = Flm_invimage_i(A, B, pp);
    4459          42 :       if (x) x = Flm_to_mod(x,pp);
    4460          42 :       break;
    4461             :     }
    4462          63 :     if (!x) { avma = av; return NULL; }
    4463          35 :     return gerepileupto(av, x);
    4464             :   }
    4465             :   {
    4466        7133 :     GEN ff = NULL;
    4467        7133 :     if (RgM_is_FFM(A, &ff) && RgM_is_FFM(B, &ff))
    4468          42 :       return FFM_invimage(A, B, ff);
    4469             :   }
    4470        7091 :   x = ker(shallowconcat(RgM_neg(A), B));
    4471             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4472             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4473             :    * Y has at least nB columns and full rank */
    4474        7091 :   nY = lg(x)-1;
    4475        7091 :   if (nY < nB) { avma = av; return NULL; }
    4476        7091 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4477        7091 :   d = cgetg(nB+1, t_VECSMALL);
    4478       63392 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4479             :   {
    4480       56406 :     for (; j>=1; j--)
    4481       56364 :       if (!gequal0(gcoeff(Y,i,j))) { d[i] = j; break; }
    4482       56343 :     if (!j) { avma = av; return NULL; }
    4483             :   }
    4484             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4485        7049 :   Y = vecpermute(Y, d);
    4486        7049 :   x = vecpermute(x, d);
    4487        7049 :   X = rowslice(x, 1, nA);
    4488        7049 :   return gerepileupto(av, RgM_mul(X, RgM_inv_upper(Y)));
    4489             : }
    4490             : 
    4491             : /* r = dim Ker x, n = nbrows(x) */
    4492             : static GEN
    4493       41371 : get_suppl(GEN x, GEN d, long n, long r, GEN(*ei)(long,long))
    4494             : {
    4495             :   pari_sp av;
    4496             :   GEN y, c;
    4497       41371 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
    4498             : 
    4499       41371 :   if (rx == n && r == 0) return gcopy(x);
    4500       32490 :   y = cgetg(n+1, t_MAT);
    4501       32490 :   av = avma; c = zero_zv(n);
    4502             :   /* c = lines containing pivots (could get it from gauss_pivot, but cheap)
    4503             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
    4504      251080 :   for (k = j = 1; j<=rx; j++)
    4505      218590 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gel(x,j); }
    4506      341040 :   for (j=1; j<=n; j++)
    4507      308550 :     if (!c[j]) gel(y,k++) = (GEN)j; /* HACK */
    4508       32490 :   avma = av;
    4509             : 
    4510       32490 :   rx -= r;
    4511       32490 :   for (j=1; j<=rx; j++) gel(y,j) = gcopy(gel(y,j));
    4512       32490 :   for (   ; j<=n; j++)  gel(y,j) = ei(n, y[j]);
    4513       32490 :   return y;
    4514             : }
    4515             : 
    4516             : static void
    4517       41371 : init_suppl(GEN x)
    4518             : {
    4519       41371 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    4520             :   /* HACK: avoid overwriting d from gauss_pivot() after avma=av */
    4521       41371 :   (void)new_chunk(lgcols(x) * 2);
    4522       41371 : }
    4523             : 
    4524             : /* x is an n x k matrix, rank(x) = k <= n. Return an invertible n x n matrix
    4525             :  * whose first k columns are given by x. If rank(x) < k, undefined result. */
    4526             : GEN
    4527          91 : suppl(GEN x)
    4528             : {
    4529          91 :   pari_sp av = avma;
    4530          91 :   GEN d, X = x, p = NULL, ff = NULL;
    4531             :   long r;
    4532             : 
    4533          91 :   if (typ(x)!=t_MAT) pari_err_TYPE("suppl",x);
    4534          91 :   if (RgM_is_FpM(x, &p) && p)
    4535             :   {
    4536             :     ulong pp;
    4537          28 :     x = RgM_Fp_init(x, p, &pp);
    4538          28 :     switch(pp)
    4539             :     {
    4540           7 :     case 0: x = FpM_to_mod(FpM_suppl(x,p), p); break;
    4541           7 :     case 2: x = F2m_to_mod(F2m_suppl(x)); break;
    4542          14 :     default:x = Flm_to_mod(Flm_suppl(x,pp), pp); break;
    4543             :     }
    4544          28 :     return gerepileupto(av, x);
    4545             :   }
    4546          63 :   if (RgM_is_FFM(x, &ff)) return FFM_suppl(x, ff);
    4547          42 :   avma = av; init_suppl(x);
    4548          42 :   d = gauss_pivot(X,&r);
    4549          42 :   avma = av; return get_suppl(X,d,nbrows(X),r,&col_ei);
    4550             : }
    4551             : GEN
    4552       40076 : FpM_suppl(GEN x, GEN p)
    4553             : {
    4554       40076 :   pari_sp av = avma;
    4555             :   GEN d;
    4556             :   long r;
    4557       40076 :   init_suppl(x); d = FpM_gauss_pivot(x,p, &r);
    4558       40076 :   avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
    4559             : }
    4560             : 
    4561             : GEN
    4562          42 : Flm_suppl(GEN x, ulong p)
    4563             : {
    4564          42 :   pari_sp av = avma;
    4565             :   GEN d;
    4566             :   long r;
    4567          42 :   init_suppl(x); d = Flm_pivots(x, p, &r, 0);
    4568          42 :   avma = av; return get_suppl(x,d,nbrows(x),r,&vecsmall_ei);
    4569             : }
    4570             : 
    4571             : GEN
    4572           7 : F2m_suppl(GEN x)
    4573             : {
    4574           7 :   pari_sp av = avma;
    4575             :   GEN d;
    4576             :   long r;
    4577           7 :   init_suppl(x); d = F2m_gauss_pivot(F2m_copy(x), &r);
    4578           7 :   avma = av; return get_suppl(x,d,mael(x,1,1),r,&F2v_ei);
    4579             : }
    4580             : 
    4581             : /* variable number to be filled in later */
    4582             : static GEN
    4583          14 : _FlxC_ei(long n, long i)
    4584             : {
    4585          14 :   GEN x = cgetg(n + 1, t_COL);
    4586             :   long j;
    4587          42 :   for (j = 1; j <= n; j++)
    4588          28 :     gel(x, j) = (j == i)? pol1_Flx(0): pol0_Flx(0);
    4589          14 :   return x;
    4590             : }
    4591             : 
    4592             : GEN
    4593           7 : F2xqM_suppl(GEN x, GEN T)
    4594             : {
    4595           7 :   pari_sp av = avma;
    4596             :   GEN d, y;
    4597           7 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    4598             : 
    4599           7 :   init_suppl(x);
    4600           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    4601           7 :   avma = av;
    4602           7 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    4603           7 :   if (sv) {
    4604             :     long i, j;
    4605          21 :     for (j = r + 1; j <= n; j++) {
    4606          42 :       for (i = 1; i <= n; i++)
    4607          28 :         gcoeff(y, i, j)[1] = sv;
    4608             :     }
    4609             :   }
    4610           7 :   return y;
    4611             : }
    4612             : 
    4613             : GEN
    4614           7 : FlxqM_suppl(GEN x, GEN T, ulong p)
    4615             : {
    4616           7 :   pari_sp av = avma;
    4617             :   GEN d, y;
    4618           7 :   long n = nbrows(x), r, sv = get_Flx_var(T);
    4619             : 
    4620           7 :   init_suppl(x);
    4621           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    4622           7 :   avma = av;
    4623           7 :   y = get_suppl(x, d, n, r, &_FlxC_ei);
    4624           7 :   if (sv) {
    4625             :     long i, j;
    4626          21 :     for (j = r + 1; j <= n; j++) {
    4627          42 :       for (i = 1; i <= n; i++)
    4628          28 :         gcoeff(y, i, j)[1] = sv;
    4629             :     }
    4630             :   }
    4631           7 :   return y;
    4632             : }
    4633             : 
    4634             : GEN
    4635        4018 : FqM_suppl(GEN x, GEN T, GEN p)
    4636             : {
    4637        4018 :   pari_sp av = avma;
    4638             :   GEN d;
    4639             :   long r;
    4640             : 
    4641        4018 :   if (!T) return FpM_suppl(x,p);
    4642        1190 :   init_suppl(x);
    4643        1190 :   d = FqM_gauss_pivot(x,T,p,&r);
    4644        1190 :   avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
    4645             : }
    4646             : 
    4647             : GEN
    4648           7 : image2(GEN x)
    4649             : {
    4650           7 :   pari_sp av = avma;
    4651             :   long k, n, i;
    4652             :   GEN A, B;
    4653             : 
    4654           7 :   if (typ(x)!=t_MAT) pari_err_TYPE("image2",x);
    4655           7 :   if (lg(x) == 1) return cgetg(1,t_MAT);
    4656           7 :   A = ker(x); k = lg(A)-1;
    4657           7 :   if (!k) { avma = av; return gcopy(x); }
    4658           7 :   A = suppl(A); n = lg(A)-1;
    4659           7 :   B = cgetg(n-k+1, t_MAT);
    4660           7 :   for (i = k+1; i <= n; i++) gel(B,i-k) = RgM_RgC_mul(x, gel(A,i));
    4661           7 :   return gerepileupto(av, B);
    4662             : }
    4663             : 
    4664             : GEN
    4665         147 : matimage0(GEN x,long flag)
    4666             : {
    4667         147 :   switch(flag)
    4668             :   {
    4669         140 :     case 0: return image(x);
    4670           7 :     case 1: return image2(x);
    4671           0 :     default: pari_err_FLAG("matimage");
    4672             :   }
    4673             :   return NULL; /* LCOV_EXCL_LINE */
    4674             : }
    4675             : 
    4676             : long
    4677         196 : rank(GEN x)
    4678             : {
    4679         196 :   pari_sp av = avma;
    4680             :   long r;
    4681         196 :   GEN ff = NULL, p = NULL;
    4682             : 
    4683         196 :   if (typ(x)!=t_MAT) pari_err_TYPE("rank",x);
    4684         196 :   if (RgM_is_FpM(x, &p) && p)
    4685             :   {
    4686             :     ulong pp;
    4687         105 :     x = RgM_Fp_init(x,p,&pp);
    4688         105 :     switch(pp)
    4689             :     {
    4690           7 :     case 0: r = FpM_rank(x,p); break;
    4691          63 :     case 2: r = F2m_rank(x); break;
    4692          35 :     default:r = Flm_rank(x,pp); break;
    4693             :     }
    4694         105 :     avma = av; return r;
    4695             :   }
    4696          91 :   if (RgM_is_FFM(x, &ff)) return FFM_rank(x, ff);
    4697          49 :   (void)gauss_pivot(x, &r);
    4698          49 :   avma = av; return lg(x)-1 - r;
    4699             : }
    4700             : 
    4701             : /* d a t_VECSMALL of integers in 1..n. Return the vector of the d[i]
    4702             :  * followed by the missing indices */
    4703             : static GEN
    4704       10214 : perm_complete(GEN d, long n)
    4705             : {
    4706       10214 :   GEN y = cgetg(n+1, t_VECSMALL);
    4707       10214 :   long i, j = 1, k = n, l = lg(d);
    4708       10214 :   pari_sp av = avma;
    4709       10214 :   char *T = stack_calloc(n+1);
    4710       10214 :   for (i = 1; i < l; i++) T[d[i]] = 1;
    4711      104410 :   for (i = 1; i <= n; i++)
    4712       94196 :     if (T[i]) y[j++] = i; else y[k--] = i;
    4713       10214 :   avma = av; return y;
    4714             : }
    4715             : 
    4716             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    4717             : static GEN
    4718       64110 : indexrank0(long n, long r, GEN d)
    4719             : {
    4720       64110 :   GEN p1, p2, res = cgetg(3,t_VEC);
    4721             :   long i, j;
    4722             : 
    4723       64110 :   r = n - r; /* now r = dim Im(x) */
    4724       64110 :   p1 = cgetg(r+1,t_VECSMALL); gel(res,1) = p1;
    4725       64110 :   p2 = cgetg(r+1,t_VECSMALL); gel(res,2) = p2;
    4726       64110 :   if (d)
    4727             :   {
    4728      369121 :     for (i=0,j=1; j<=n; j++)
    4729      305746 :       if (d[j]) { i++; p1[i] = d[j]; p2[i] = j; }
    4730       63375 :     vecsmall_sort(p1);
    4731             :   }
    4732       64110 :   return res;
    4733             : }
    4734             : /* n = dim x, r = dim Ker(x), d from gauss_pivot */
    4735             : static GEN
    4736         840 : indeximage0(long n, long r, GEN d)
    4737             : {
    4738             :   long i, j;
    4739             :   GEN v;
    4740             : 
    4741         840 :   r = n - r; /* now r = dim Im(x) */
    4742         840 :   v = cgetg(r+1,t_VECSMALL);
    4743        7651 :   if (d) for (i=j=1; j<=n; j++)
    4744        6811 :     if (d[j]) v[i++] = j;
    4745         840 :   return v;
    4746             : }
    4747             : /* x an m x n t_MAT, n > 0, r = dim Ker(x), d from gauss_pivot */
    4748             : static void
    4749        5107 : indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol)
    4750             : {
    4751        5107 :   GEN IR = indexrank0(n, r, d);
    4752        5107 :   *prow = perm_complete(gel(IR,1), m);
    4753        5107 :   *pcol = perm_complete(gel(IR,2), n);
    4754        5107 : }
    4755             : static void
    4756       59843 : init_indexrank(GEN x) {
    4757       59843 :   (void)new_chunk(3 + 2*lg(x)); /* HACK */
    4758       59843 : }
    4759             : 
    4760             : GEN
    4761       14679 : indexrank(GEN x) {
    4762             :   pari_sp av;
    4763             :   long r;
    4764       14679 :   GEN d, p = NULL, ff = NULL;
    4765       14679 :   if (typ(x)!=t_MAT) pari_err_TYPE("indexrank",x);
    4766       14679 :   if (RgM_is_FpM(x, &p) && p)
    4767             :   {
    4768             :     ulong pp;
    4769          28 :     x = RgM_Fp_init(x,p,&pp);
    4770          28 :     switch(pp)
    4771             :     {
    4772           7 :     case 0:  return FpM_indexrank(x, p);
    4773           7 :     case 2:  return F2m_indexrank(x);
    4774          14 :     default: return Flm_indexrank(x, pp);
    4775             :     }
    4776             :   }
    4777       14651 :   if (RgM_is_FFM(x, &ff)) return FFM_indexrank(x, ff);
    4778       14630 :   av = avma;
    4779       14630 :   init_indexrank(x);
    4780       14630 :   d = gauss_pivot(x, &r);
    4781       14630 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4782             : }
    4783             : 
    4784             : GEN
    4785       16499 : FpM_indexrank(GEN x, GEN p) {
    4786       16499 :   pari_sp av = avma;
    4787             :   long r;
    4788             :   GEN d;
    4789       16499 :   init_indexrank(x);
    4790       16499 :   d = FpM_gauss_pivot(x,p,&r);
    4791       16499 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4792             : }
    4793             : 
    4794             : GEN
    4795       12530 : Flm_indexrank(GEN x, ulong p) {
    4796       12530 :   pari_sp av = avma;
    4797             :   long r;
    4798             :   GEN d;
    4799       12530 :   init_indexrank(x);
    4800       12530 :   d = Flm_pivots(x, p, &r, 0);
    4801       12530 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4802             : }
    4803             : 
    4804             : GEN
    4805           7 : F2m_indexrank(GEN x) {
    4806           7 :   pari_sp av = avma;
    4807             :   long r;
    4808             :   GEN d;
    4809           7 :   init_indexrank(x);
    4810           7 :   d = F2m_gauss_pivot(F2m_copy(x),&r);
    4811           7 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4812             : }
    4813             : 
    4814             : GEN
    4815           7 : F2xqM_indexrank(GEN x, GEN T) {
    4816           7 :   pari_sp av = avma;
    4817             :   long r;
    4818             :   GEN d;
    4819           7 :   init_indexrank(x);
    4820           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    4821           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    4822             : }
    4823             : 
    4824             : GEN
    4825           7 : FlxqM_indexrank(GEN x, GEN T, ulong p) {
    4826           7 :   pari_sp av = avma;
    4827             :   long r;
    4828             :   GEN d;
    4829           7 :   init_indexrank(x);
    4830           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    4831           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    4832             : }
    4833             : 
    4834             : GEN
    4835           7 : FqM_indexrank(GEN x, GEN T, GEN p) {
    4836           7 :   pari_sp av = avma;
    4837             :   long r;
    4838             :   GEN d;
    4839           7 :   init_indexrank(x);
    4840           7 :   d = FqM_gauss_pivot(x, T, p, &r);
    4841           7 :   avma = av; return indexrank0(lg(x) - 1, r, d);
    4842             : }
    4843             : 
    4844             : GEN
    4845         840 : ZM_indeximage(GEN x) {
    4846         840 :   pari_sp av = avma;
    4847             :   long r;
    4848             :   GEN d;
    4849         840 :   init_indexrank(x);
    4850         840 :   d = ZM_pivots(x,&r);
    4851         840 :   avma = av; return indeximage0(lg(x)-1, r, d);
    4852             : }
    4853             : long
    4854       42530 : ZM_rank(GEN x) {
    4855       42530 :   pari_sp av = avma;
    4856             :   long r;
    4857       42530 :   (void)ZM_pivots(x,&r);
    4858       42530 :   avma = av; return lg(x)-1-r;
    4859             : }
    4860             : GEN
    4861       15316 : ZM_indexrank(GEN x) {
    4862       15316 :   pari_sp av = avma;
    4863             :   long r;
    4864             :   GEN d;
    4865       15316 :   init_indexrank(x);
    4866       15316 :   d = ZM_pivots(x,&r);
    4867       15316 :   avma = av; return indexrank0(lg(x)-1, r, d);
    4868             : }
    4869             : 
    4870             : /*******************************************************************/
    4871             : /*                                                                 */
    4872             : /*                             ZabM                                */
    4873             : /*                                                                 */
    4874             : /*******************************************************************/
    4875             : 
    4876             : static GEN
    4877        2039 : FpVM_ratlift(GEN a, GEN q, long vs)
    4878             : {
    4879             :   GEN B, y;
    4880        2039 :   long i, j, l = lg(a), n;
    4881        2039 :   B = sqrti(shifti(q,-1));
    4882        2039 :   y = cgetg(l, t_MAT);
    4883        2039 :   if (l==1) return y;
    4884        2039 :   n = lgcols(a);
    4885        7182 :   for (i=1; i<l; i++)
    4886             :   {
    4887        5723 :     GEN yi = cgetg(n, t_COL);
    4888       80140 :     for (j=1; j<n; j++)
    4889             :     {
    4890       74997 :       GEN v = FpC_ratlift(gmael(a,i,j), q, B, B, NULL);
    4891       74997 :       if (!v) return NULL;
    4892       74417 :       gel(yi, j) = RgV_to_RgX(v, vs);
    4893             :     }
    4894        5143 :     gel(y,i) = yi;
    4895             :   }
    4896        1459 :   return y;
    4897             : }
    4898             : 
    4899             : static GEN
    4900        2039 : FlmV_recover(GEN a, GEN M, ulong p)
    4901             : {
    4902        2039 :   GEN a1 = gel(a,1);
    4903        2039 :   long i, j, k, l = lg(a1), n, lM = lg(M);
    4904        2039 :   GEN y = cgetg(l, t_MAT);
    4905        2039 :   if (l==1) return y;
    4906        2039 :   n = lgcols(a1);
    4907        9970 :   for (i=1; i<l; i++)
    4908             :   {
    4909        7931 :     GEN yi = cgetg(n, t_COL);
    4910      143890 :     for (j=1; j<n; j++)
    4911             :     {
    4912      135959 :       GEN v = cgetg(lM, t_COL);
    4913      135959 :       for (k=1; k<lM; k++) gel(v,k) = gmael(gel(a,k),i,j);
    4914      135959 :       gel(yi, j) = Flm_Flc_mul(M, v, p);
    4915             :     }
    4916        7931 :     gel(y,i) = yi;
    4917             :   }
    4918        2039 :   return y;
    4919             : }
    4920             : 
    4921             : static GEN
    4922        1004 : FlkM_inv(GEN M, GEN P, ulong p)
    4923             : {
    4924        1004 :   ulong pi = get_Fl_red(p);
    4925        1004 :   GEN R = Flx_roots(P, p);
    4926        1004 :   long l = lg(R), i;
    4927        1004 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4928        1004 :   GEN V = cgetg(l, t_VEC);
    4929        3488 :   for(i=1; i<l; i++)
    4930             :   {
    4931        2484 :     gel(V, i) = Flm_inv_sp(FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), degpol(P), p, pi), p, pi), NULL, p);
    4932        2484 :     if (!gel(V, i)) return NULL;
    4933             :   }
    4934        1004 :   return FlmV_recover(V,W,p);
    4935             : }
    4936             : 
    4937             : GEN
    4938         875 : ZabM_inv(GEN M, GEN P, long n, GEN *pden)
    4939             : {
    4940         875 :   pari_sp av2, av = avma;
    4941             :   GEN q, H;
    4942         875 :   ulong m = LONG_MAX>>1;
    4943         875 :   ulong p= 1 + m - (m % n);
    4944         875 :   long lM = lg(M);
    4945         875 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    4946             : 
    4947         875 :   av2 = avma;
    4948         875 :   H = NULL;
    4949             :   for(;;)
    4950             :   {
    4951             :     GEN Hp, Pp, Mp, Hr;
    4952       32812 :     do p += n; while(!uisprime(p));
    4953        1004 :     Pp = ZX_to_Flx(P, p);
    4954        1004 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    4955        1004 :     Hp = FlkM_inv(Mp, Pp, p);
    4956        1004 :     if (!Hp) continue;
    4957        1004 :     if (!H)
    4958             :     {
    4959         875 :       H = ZVM_init_CRT(Hp, p);
    4960         875 :       q = utoipos(p);
    4961             :     }
    4962             :     else
    4963         129 :       ZVM_incremental_CRT(&H, Hp, &q, p);
    4964        1004 :     Hr = FpVM_ratlift(H, q, varn(P));
    4965        1004 :     if (DEBUGLEVEL>5) err_printf("ZabM_inv mod %ld (ratlift=%ld)\n", p,!!Hr);
    4966        1004 :     if (Hr) {/* DONE ? */
    4967         876 :       GEN Hl = Q_remove_denom(Hr, pden);
    4968         876 :       GEN MH = ZXQM_mul(M, Hl,P);
    4969         876 :       if (*pden)
    4970         253 :       { if (RgM_isscalar(MH, *pden)) { H = Hl; break; }}
    4971             :       else
    4972         623 :       { if (RgM_isidentity(MH)) { H = Hl; *pden = gen_1; break; } }
    4973             :     }
    4974             : 
    4975         129 :     if (gc_needed(av,2))
    4976             :     {
    4977           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_inv");
    4978           0 :       gerepileall(av2, 2, &H, &q);
    4979             :     }
    4980         129 :   }
    4981         875 :   gerepileall(av, 2, &H, pden);
    4982         875 :   return H;
    4983             : }
    4984             : 
    4985             : static GEN
    4986        1035 : FlkM_ker(GEN M, GEN P, ulong p)
    4987             : {
    4988        1035 :   ulong pi = get_Fl_red(p);
    4989        1035 :   GEN R = Flx_roots(P, p);
    4990        1035 :   long l = lg(R), i, dP = degpol(P), r;
    4991             :   GEN M1, K, D;
    4992        1035 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4993        1035 :   GEN V = cgetg(l, t_VEC);
    4994        1035 :   M1 = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,1), dP, p, pi), p, pi);
    4995        1035 :   K = Flm_ker_sp(M1, p, 2);
    4996        1035 :   r = lg(gel(K,1)); D = gel(K,2);
    4997        1035 :   gel(V, 1) = gel(K,1);
    4998        2140 :   for(i=2; i<l; i++)
    4999             :   {
    5000        1105 :     GEN Mi = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), dP, p, pi), p, pi);
    5001        1105 :     GEN K = Flm_ker_sp(Mi, p, 2);
    5002        1105 :     if (lg(gel(K,1)) != r || !zv_equal(D, gel(K,2))) return NULL;
    5003        1105 :     gel(V, i) = gel(K,1);
    5004             :   }
    5005        1035 :   return mkvec2(FlmV_recover(V,W,p), D);
    5006             : }
    5007             : 
    5008             : GEN
    5009         574 : ZabM_ker(GEN M, GEN P, long n)
    5010             : {
    5011         574 :   pari_sp av2, av = avma;
    5012             :   GEN q, H, D;
    5013         574 :   ulong m = LONG_MAX>>1;
    5014         574 :   ulong p= 1 + m - (m % n);
    5015         574 :   av2 = avma;
    5016         574 :   H = NULL; D = NULL;
    5017             :   for(;;)
    5018             :   {
    5019             :     GEN Kp, Hp, Dp, Pp, Mp, Hr;
    5020       26259 :     do p += n; while(!uisprime(p));
    5021        1035 :     Pp = ZX_to_Flx(P, p);
    5022        1035 :     Mp = FqM_to_FlxM(M, P, utoi(p));
    5023        1035 :     Kp = FlkM_ker(Mp, Pp, p);
    5024        1035 :     if (!Kp) continue;
    5025        1035 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    5026        1035 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    5027        1035 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    5028             :     {
    5029         574 :       H = ZVM_init_CRT(Hp, p); D = Dp;
    5030         574 :       q = utoipos(p);
    5031             :     }
    5032             :     else
    5033         461 :       ZVM_incremental_CRT(&H, Hp, &q, p);
    5034        1035 :     Hr = FpVM_ratlift(H, q, varn(P));
    5035        1035 :     if (DEBUGLEVEL>5) err_printf("ZabM_ker mod %ld (ratlift=%ld)\n", p,!!Hr);
    5036        1035 :     if (Hr) {/* DONE ? */
    5037         583 :       GEN Hl = Q_remove_denom(Hr, NULL);
    5038         583 :       GEN MH = ZXQM_mul(M, Hl,P);
    5039         583 :       if (gequal0(MH)) { H = Hl;  break; }
    5040             :     }
    5041             : 
    5042         461 :     if (gc_needed(av,2))
    5043             :     {
    5044           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_ker");
    5045           0 :       gerepileall(av2, 3, &H, &D, &q);
    5046             :     }
    5047         461 :   }
    5048         574 :   return gerepilecopy(av, H);
    5049             : }
    5050             : 
    5051             : GEN
    5052        1736 : ZabM_indexrank(GEN M, GEN P, long n)
    5053             : {
    5054        1736 :   pari_sp av = avma;
    5055        1736 :   ulong m = LONG_MAX>>1;
    5056        1736 :   ulong p = 1+m-(m%n), D = degpol(P);
    5057        1736 :   long lM = lg(M), lmax = 0, c = 0;
    5058             :   GEN v;
    5059             :   for(;;)
    5060             :   {
    5061             :     GEN R, Mp, K;
    5062             :     ulong pi;
    5063             :     long l;
    5064       69890 :     do p += n; while (!uisprime(p));
    5065        2324 :     pi = get_Fl_red(p);
    5066        2324 :     R = Flx_roots(ZX_to_Flx(P, p), p);
    5067        2324 :     Mp = FqM_to_FlxM(M, P, utoipos(p));
    5068        2324 :     K = FlxM_eval_powers_pre(Mp, Fl_powers_pre(uel(R,1), D,p,pi), p,pi);
    5069        2324 :     v = Flm_indexrank(K, p);
    5070        2324 :     l = lg(gel(v,2));
    5071        2324 :     if (l == lM) break;
    5072         784 :     if (lmax >= 0 && l > lmax) { lmax = l; c = 0; } else c++;
    5073         784 :     if (c > 2)
    5074             :     { /* probably not maximal rank, expensive check */
    5075         196 :       lM -= lg(ZabM_ker(M, P, n))-1; /* actual rank (+1) */
    5076         196 :       if (lmax == lM) break;
    5077           0 :       lmax = -1; /* disable check */
    5078             :     }
    5079         588 :   }
    5080        1736 :   return gerepileupto(av, v);
    5081             : }
    5082             : 
    5083             : #if 0
    5084             : GEN
    5085             : ZabM_gauss(GEN M, GEN P, long n, GEN *den)
    5086             : {
    5087             :   pari_sp av = avma;
    5088             :   GEN v, S, W;
    5089             :   v = ZabM_indexrank(M, P, n);
    5090             :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    5091             :   W = ZabM_inv(S, P, n, den);
    5092             :   gerepileall(av,2,&W,den);
    5093             :   return W;
    5094             : }
    5095             : #endif
    5096             : 
    5097             : GEN
    5098         742 : ZabM_pseudoinv(GEN M, GEN P, long n, GEN *pv, GEN *den)
    5099             : {
    5100         742 :   GEN v = ZabM_indexrank(M, P, n);
    5101         742 :   if (pv) *pv = v;
    5102         742 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    5103         742 :   return ZabM_inv(M, P, n, den);
    5104             : }
    5105             : GEN
    5106        1547 : ZM_pseudoinv(GEN M, GEN *pv, GEN *den)
    5107             : {
    5108        1547 :   GEN v = ZM_indexrank(M);
    5109        1547 :   if (pv) *pv = v;
    5110        1547 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    5111        1547 :   return ZM_inv_ratlift(M, den);
    5112             : }
    5113             : 
    5114             : /*******************************************************************/
    5115             : /*                                                                 */
    5116             : /*                   Structured Elimination                        */
    5117             : /*                                                                 */
    5118             : /*******************************************************************/
    5119             : 
    5120             : static void
    5121       99654 : rem_col(GEN c, long i, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5122             : {
    5123       99654 :   long lc = lg(c), k;
    5124       99654 :   iscol[i] = 0; (*rcol)--;
    5125      882955 :   for (k = 1; k < lc; ++k)
    5126             :   {
    5127      783301 :     Wrow[c[k]]--;
    5128      783301 :     if (Wrow[c[k]]==0) (*rrow)--;
    5129             :   }
    5130       99654 : }
    5131             : 
    5132             : static void
    5133        5968 : rem_singleton(GEN M, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    5134             : {
    5135             :   long i, j;
    5136        5968 :   long nbcol = lg(iscol)-1, last;
    5137             :   do
    5138             :   {
    5139        7818 :     last = 0;
    5140    18930126 :     for (i = 1; i <= nbcol; ++i)
    5141    18922308 :       if (iscol[i])
    5142             :       {
    5143     9730205 :         GEN c = gmael(M, i, 1);
    5144     9730205 :         long lc = lg(c);
    5145    90883208 :         for (j = 1; j < lc; ++j)
    5146    81165068 :           if (Wrow[c[j]] == 1)
    5147             :           {
    5148       12065 :             rem_col(c, i, iscol, Wrow, rcol, rrow);
    5149       12065 :             last=1; break;
    5150             :           }
    5151             :       }
    5152        7818 :   } while (last);
    5153        5968 : }
    5154             : 
    5155             : static GEN
    5156        5849 : fill_wcol(GEN M, GEN iscol, GEN Wrow, long *w, GEN wcol)
    5157             : {
    5158        5849 :   long nbcol = lg(iscol)-1;
    5159             :   long i, j, m, last;
    5160             :   GEN per;
    5161       14561 :   for (m = 2, last=0; !last ; m++)
    5162             :   {
    5163    22245729 :     for (i = 1; i <= nbcol; ++i)
    5164             :     {
    5165    22237017 :       wcol[i] = 0;
    5166    22237017 :       if (iscol[i])
    5167             :       {
    5168    11415908 :         GEN c = gmael(M, i, 1);
    5169    11415908 :         long lc = lg(c);
    5170   106639624 :         for (j = 1; j < lc; ++j)
    5171    95223716 :           if (Wrow[c[j]] == m) {  wcol[i]++; last = 1; }
    5172             :       }
    5173             :     }
    5174             :   }
    5175        5849 :   per = vecsmall_indexsort(wcol);
    5176        5849 :   *w = wcol[per[nbcol]];
    5177        5849 :   return per;
    5178             : }
    5179             : 
    5180             : /* M is a RgMs with nbrow rows, A a list of row indices.
    5181             :    Eliminate rows of M with a single entry that do not belong to A,
    5182             :    and the corresponding columns. Also eliminate columns until #colums=#rows.
    5183             :    Return pcol and prow:
    5184             :    pcol is a map from the new columns indices to the old one.
    5185             :    prow is a map from the old rows indices to the new one (0 if removed).
    5186             : */
    5187             : 
    5188             : void
    5189         119 : RgMs_structelim_col(GEN M, long nbcol, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5190             : {
    5191             :   long i,j,k;
    5192         119 :   long lA = lg(A);
    5193         119 :   GEN prow = cgetg(nbrow+1, t_VECSMALL);
    5194         119 :   GEN pcol = zero_zv(nbcol);
    5195         119 :   pari_sp av = avma;
    5196         119 :   long rcol = nbcol, rrow = 0, imin = nbcol - usqrt(nbcol);
    5197         119 :   GEN iscol = const_vecsmall(nbcol, 1);
    5198         119 :   GEN Wrow  = zero_zv(nbrow);
    5199         119 :   GEN wcol = cgetg(nbcol+1, t_VECSMALL);
    5200         119 :   pari_sp av2=avma;
    5201      124999 :   for (i = 1; i <= nbcol; ++i)
    5202             :   {
    5203      124880 :     GEN F = gmael(M, i, 1);
    5204      124880 :     long l = lg(F)-1;
    5205     1104911 :     for (j = 1; j <= l; ++j)
    5206      980031 :       Wrow[F[j]]++;
    5207             :   }
    5208         119 :   for (j = 1; j < lA; ++j)
    5209             :   {
    5210         119 :     if (Wrow[A[j]] == 0) { *p_col=NULL; return; }
    5211           0 :     Wrow[A[j]] = -1;
    5212             :   }
    5213      221956 :   for (i = 1; i <= nbrow; ++i)
    5214      221837 :     if (Wrow[i])
    5215       66341 :       rrow++;
    5216         119 :   rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5217         119 :   if (rcol<rrow) pari_err_BUG("RgMs_structelim, rcol<rrow");
    5218        6087 :   for (; rcol>rrow;)
    5219             :   {
    5220             :     long w;
    5221        5849 :     GEN per = fill_wcol(M, iscol, Wrow, &w, wcol);
    5222       93438 :     for (i = nbcol; i>=imin && wcol[per[i]]>=w && rcol>rrow; i--)
    5223       87589 :       rem_col(gmael(M, per[i], 1), per[i], iscol, Wrow, &rcol, &rrow);
    5224        5849 :     rem_singleton(M, iscol, Wrow, &rcol, &rrow);
    5225        5849 :     avma = av2;
    5226             :   }
    5227      124999 :   for (j = 1, i = 1; i <= nbcol; ++i)
    5228      124880 :     if (iscol[i])
    5229       25226 :       pcol[j++] = i;
    5230         119 :   setlg(pcol,j);
    5231      221956 :   for (k = 1, i = 1; i <= nbrow; ++i)
    5232      221837 :     prow[i] = Wrow[i] ? k++: 0;
    5233         119 :   avma = av;
    5234         119 :   *p_col = pcol; *p_row = prow;
    5235             : }
    5236             : 
    5237             : void
    5238           0 : RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    5239             : {
    5240           0 :   RgMs_structelim_col(M, lg(M)-1, nbrow, A, p_col, p_row);
    5241           0 : }
    5242             : 
    5243             : /*******************************************************************/
    5244             : /*                                                                 */
    5245             : /*                        EIGENVECTORS                             */
    5246             : /*   (independent eigenvectors, sorted by increasing eigenvalue)   */
    5247             : /*                                                                 */
    5248             : /*******************************************************************/
    5249             : 
    5250             : GEN
    5251          63 : mateigen(GEN x, long flag, long prec)
    5252             : {
    5253             :   GEN y, R, T;
    5254          63 :   long k, l, ex, n = lg(x);
    5255          63 :   pari_sp av = avma;
    5256             : 
    5257          63 :   if (typ(x)!=t_MAT) pari_err_TYPE("eigen",x);
    5258          63 :   if (n != 1 && n != lgcols(x)) pari_err_DIM("eigen");
    5259          63 :   if (flag < 0 || flag > 1) pari_err_FLAG("mateigen");
    5260          63 :   if (n == 1)
    5261             :   {
    5262          14 :     if (flag) retmkvec2(cgetg(1,t_VEC), cgetg(1,t_MAT));
    5263           7 :     return cgetg(1,t_VEC);
    5264             :   }
    5265          49 :   if (n == 2)
    5266             :   {
    5267          14 :     if (flag) retmkvec2(mkveccopy(gcoeff(x,1,1)), matid(1));
    5268           7 :     return matid(1);
    5269             :   }
    5270             : 
    5271          35 :   ex = 16 - prec2nbits(prec);
    5272          35 :   T = charpoly(x,0);
    5273          35 :   if (RgX_is_QX(T))
    5274             :   {
    5275          28 :     T = ZX_radical( Q_primpart(T) );
    5276          28 :     R = nfrootsQ(T);
    5277          28 :     if (lg(R)-1 < degpol(T))
    5278             :     { /* add missing complex roots */
    5279          14 :       GEN r = cleanroots(RgX_div(T, roots_to_pol(R, 0)), prec);
    5280          14 :       settyp(r, t_VEC);
    5281          14 :       R = shallowconcat(R, r);
    5282             :     }
    5283             :   }
    5284             :   else
    5285             :   {
    5286           7 :     GEN r1, v = vectrunc_init(lg(T));
    5287             :     long e;
    5288           7 :     R = cleanroots(T,prec);
    5289           7 :     r1 = NULL;
    5290          21 :     for (k = 1; k < lg(R); k++)
    5291             :     {
    5292          14 :       GEN r2 = gel(R,k), r = grndtoi(r2, &e);
    5293          14 :       if (e < ex) r2 = r;
    5294          14 :       if (r1)
    5295             :       {
    5296           7 :         r = gsub(r1,r2);
    5297           7 :         if (gequal0(r) || gexpo(r) < ex) continue;
    5298             :       }
    5299          14 :       vectrunc_append(v, r2);
    5300          14 :       r1 = r2;
    5301             :     }
    5302           7 :     R = v;
    5303             :   }
    5304             :   /* R = distinct complex roots of charpoly(x) */
    5305          35 :   l = lg(R); y = cgetg(l, t_VEC);
    5306         189 :   for (k = 1; k < l; k++)
    5307             :   {
    5308         154 :     GEN F = ker_aux(RgM_Rg_sub_shallow(x, gel(R,k)), x);
    5309         154 :     long d = lg(F)-1;
    5310         154 :     if (!d) pari_err_PREC("mateigen");
    5311         154 :     gel(y,k) = F;
    5312         154 :     if (flag) gel(R,k) = const_vec(d, gel(R,k));
    5313             :   }
    5314          35 :   y = shallowconcat1(y);
    5315          35 :   if (lg(y) > n) pari_err_PREC("mateigen");
    5316             :   /* lg(y) < n if x is not diagonalizable */
    5317          35 :   if (flag) y = mkvec2(shallowconcat1(R), y);
    5318          35 :   return gerepilecopy(av,y);
    5319             : }
    5320             : GEN
    5321           0 : eigen(GEN x, long prec) { return mateigen(x, 0, prec); }
    5322             : 
    5323             : /*******************************************************************/
    5324             : /*                                                                 */
    5325             : /*                           DETERMINANT                           */
    5326             : /*                                                                 */
    5327             : /*******************************************************************/
    5328             : 
    5329             : GEN
    5330        1687 : det0(GEN a,long flag)
    5331             : {
    5332        1687 :   switch(flag)
    5333             :   {
    5334        1673 :     case 0: return det(a);
    5335          14 :     case 1: return det2(a);
    5336           0 :     default: pari_err_FLAG("matdet");
    5337             :   }
    5338             :   return NULL; /* LCOV_EXCL_LINE */
    5339             : }
    5340             : 
    5341             : /* M a 2x2 matrix, returns det(M) */
    5342             : static GEN
    5343        3607 : RgM_det2(GEN M)
    5344             : {
    5345        3607 :   pari_sp av = avma;
    5346        3607 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5347        3607 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5348        3607 :   return gerepileupto(av, gsub(gmul(a,d), gmul(b,c)));
    5349             : }
    5350             : /* M a 2x2 ZM, returns det(M) */
    5351             : static GEN
    5352        7462 : ZM_det2(GEN M)
    5353             : {
    5354        7462 :   pari_sp av = avma;
    5355        7462 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5356        7462 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5357        7462 :   return gerepileuptoint(av, subii(mulii(a,d), mulii(b, c)));
    5358             : }
    5359             : /* M a 3x3 ZM, return det(M) */
    5360             : static GEN
    5361        2933 : ZM_det3(GEN M)
    5362             : {
    5363        2933 :   pari_sp av = avma;
    5364        2933 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2), c = gcoeff(M,1,3);
    5365        2933 :   GEN d = gcoeff(M,2,1), e = gcoeff(M,2,2), f = gcoeff(M,2,3);
    5366        2933 :   GEN g = gcoeff(M,3,1), h = gcoeff(M,3,2), i = gcoeff(M,3,3);
    5367        2933 :   GEN t, D = signe(i)? mulii(subii(mulii(a,e), mulii(b,d)), i): gen_0;
    5368        2933 :   if (signe(g))
    5369             :   {
    5370        2730 :     t = mulii(subii(mulii(b,f), mulii(c,e)), g);
    5371        2730 :     D = addii(D, t);
    5372             :   }
    5373        2933 :   if (signe(h))
    5374             :   {
    5375        2751 :     t = mulii(subii(mulii(c,d), mulii(a,f)), h);
    5376        2751 :     D = addii(D, t);
    5377             :   }
    5378        2933 :   return gerepileuptoint(av, D);
    5379             : }
    5380             : 
    5381             : static GEN
    5382        9811 : det_simple_gauss(GEN a, GEN data, pivot_fun pivot)
    5383             : {
    5384        9811 :   pari_sp av = avma;
    5385        9811 :   long i,j,k, s = 1, nbco = lg(a)-1;
    5386        9811 :   GEN p, x = gen_1;
    5387             : 
    5388        9811 :   a = RgM_shallowcopy(a);
    5389       78448 :   for (i=1; i<nbco; i++)
    5390             :   {
    5391       68644 :     k = pivot(a, data, i, NULL);
    5392       68644 :     if (k > nbco) return gerepilecopy(av, gcoeff(a,i,i));
    5393       68637 :     if (k != i)
    5394             :     { /* exchange the lines s.t. k = i */
    5395        4141 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    5396        4141 :       s = -s;
    5397             :     }
    5398       68637 :     p = gcoeff(a,i,i);
    5399             : 
    5400       68637 :     x = gmul(x,p);
    5401      402289 :     for (k=i+1; k<=nbco; k++)
    5402             :     {
    5403      333652 :       GEN m = gcoeff(a,i,k);
    5404      333652 :       if (gequal0(m)) continue;
    5405             : 
    5406      116603 :       m = gdiv(m,p);
    5407      776663 :       for (j=i+1; j<=nbco; j++)
    5408      660060 :         gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i)));
    5409             :     }
    5410       68637 :     if (gc_needed(av,2))
    5411             :     {
    5412           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5413           0 :       gerepileall(av,2, &a,&x);
    5414             :     }
    5415             :   }
    5416        9804 :   if (s < 0) x = gneg_i(x);
    5417        9804 :   return gerepileupto(av, gmul(x, gcoeff(a,nbco,nbco)));
    5418             : }
    5419             : 
    5420             : GEN
    5421        4321 : det2(GEN a)
    5422             : {
    5423             :   GEN data;
    5424             :   pivot_fun pivot;
    5425        4321 :   long n = lg(a)-1;
    5426        4321 :   if (typ(a)!=t_MAT) pari_err_TYPE("det2",a);
    5427        4321 :   if (!n) return gen_1;
    5428        4321 :   if (n != nbrows(a)) pari_err_DIM("det2");
    5429        4321 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5430        4321 :   if (n == 2) return RgM_det2(a);
    5431        1610 :   pivot = get_pivot_fun(a, a, &data);
    5432        1610 :   return det_simple_gauss(a, data, pivot);
    5433             : }
    5434             : 
    5435             : static GEN
    5436         672 : mydiv(GEN x, GEN y)
    5437             : {
    5438         672 :   long tx = typ(x), ty = typ(y);
    5439         672 :   if (tx == ty && tx == t_POL && varn(x) == varn(y)) return RgX_div(x,y);
    5440         672 :   return gdiv(x,y);
    5441             : }
    5442             : 
    5443             : /* Assumes a a square t_MAT of dimension n > 0. Returns det(a) using
    5444             :  * Gauss-Bareiss. */
    5445             : static GEN
    5446         196 : det_bareiss(GEN a)
    5447             : {
    5448         196 :   pari_sp av = avma;
    5449         196 :   long nbco = lg(a)-1,i,j,k,s = 1;
    5450             :   GEN p, pprec;
    5451             : 
    5452         196 :   a = RgM_shallowcopy(a);
    5453         770 :   for (pprec=gen_1,i=1; i<nbco; i++,pprec=p)
    5454             :   {
    5455             :     GEN ci;
    5456         574 :     int diveuc = (gequal1(pprec)==0);
    5457             : 
    5458         574 :     p = gcoeff(a,i,i);
    5459         574 :     if (gequal0(p))
    5460             :     {
    5461           0 :       k=i+1; while (k<=nbco && gequal0(gcoeff(a,i,k))) k++;
    5462           0 :       if (k>nbco) return gerepilecopy(av, p);
    5463           0 :       swap(gel(a,k), gel(a,i)); s = -s;
    5464           0 :       p = gcoeff(a,i,i);
    5465             :     }
    5466         574 :     ci = gel(a,i);
    5467        1708 :     for (k=i+1; k<=nbco; k++)
    5468             :     {
    5469        1134 :       GEN ck = gel(a,k), m = gel(ck,i);
    5470        1134 :       if (gequal0(m))
    5471             :       {
    5472           0 :         if (gequal1(p))
    5473             :         {
    5474           0 :           if (diveuc)
    5475           0 :             gel(a,k) = mydiv(gel(a,k), pprec);
    5476             :         }
    5477             :         else
    5478           0 :           for (j=i+1; j<=nbco; j++)
    5479             :           {
    5480           0 :             GEN p1 = gmul(p, gel(ck,j));
    5481           0 :             if (diveuc) p1 = mydiv(p1,pprec);
    5482           0 :             gel(ck,j) = p1;
    5483             :           }
    5484             :       }
    5485             :       else
    5486        3752 :         for (j=i+1; j<=nbco; j++)
    5487             :         {
    5488        2618 :           pari_sp av2 = avma;
    5489        2618 :           GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j)));
    5490        2618 :           if (diveuc) p1 = mydiv(p1,pprec);
    5491        2618 :           gel(ck,j) = gerepileupto(av2, p1);
    5492             :         }
    5493        1134 :       if (gc_needed(av,2))
    5494             :       {
    5495           0 :         if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5496           0 :         gerepileall(av,2, &a,&pprec);
    5497           0 :         ci = gel(a,i);
    5498           0 :         p = gcoeff(a,i,i);
    5499             :       }
    5500             :     }
    5501             :   }
    5502         196 :   p = gcoeff(a,nbco,nbco);
    5503         196 :   p = (s < 0)? gneg(p): gcopy(p);
    5504         196 :   return gerepileupto(av, p);
    5505             : }
    5506             : 
    5507             : /* count non-zero entries in col j, at most 'max' of them.
    5508             :  * Return their indices */
    5509             : static GEN
    5510         609 : col_count_non_zero(GEN a, long j, long max)
    5511             : {
    5512         609 :   GEN v = cgetg(max+1, t_VECSMALL);
    5513         609 :   GEN c = gel(a,j);
    5514         609 :   long i, l = lg(a), k = 1;
    5515        2695 :   for (i = 1; i < l; i++)
    5516        2632 :     if (!gequal0(gel(c,i)))
    5517             :     {
    5518        2324 :       if (k > max) return NULL; /* fail */
    5519        1778 :       v[k++] = i;
    5520             :     }
    5521          63 :   setlg(v, k); return v;
    5522             : }
    5523             : /* count non-zero entries in row i, at most 'max' of them.
    5524             :  * Return their indices */
    5525             : static GEN
    5526         560 : row_count_non_zero(GEN a, long i, long max)
    5527             : {
    5528         560 :   GEN v = cgetg(max+1, t_VECSMALL);
    5529         560 :   long j, l = lg(a), k = 1;
    5530        2422 :   for (j = 1; j < l; j++)
    5531        2366 :     if (!gequal0(gcoeff(a,i,j)))
    5532             :     {
    5533        2212 :       if (k > max) return NULL; /* fail */
    5534        1708 :       v[k++] = j;
    5535             :     }
    5536          56 :   setlg(v, k); return v;
    5537             : }
    5538             : 
    5539             : static GEN det_develop(GEN a, long max, double bound);
    5540             : /* (-1)^(i+j) a[i,j] * det RgM_minor(a,i,j) */
    5541             : static GEN
    5542         203 : coeff_det(GEN a, long i, long j, long max, double bound)
    5543             : {
    5544         203 :   GEN c = gcoeff(a, i, j);
    5545         203 :   c = gmul(c, det_develop(RgM_minor(a, i,j), max, bound));
    5546         203 :   if (odd(i+j)) c = gneg(c);
    5547         203 :   return c;
    5548             : }
    5549             : /* a square t_MAT, 'bound' a rough upper bound for the number of
    5550             :  * multiplications we are willing to pay while developing rows/columns before
    5551             :  * switching to Gaussian elimination */
    5552             : static GEN
    5553         301 : det_develop(GEN M, long max, double bound)
    5554             : {
    5555         301 :   pari_sp av = avma;
    5556         301 :   long i,j, n = lg(M)-1, lbest = max+2, best_col = 0, best_row = 0;
    5557         301 :   GEN best = NULL;
    5558             : 
    5559         301 :   if (bound < 1.) return det_bareiss(M); /* too costly now */
    5560             : 
    5561         175 :   switch(n)
    5562             :   {
    5563           0 :     case 0: return gen_1;
    5564           0 :     case 1: return gcopy(gcoeff(M,1,1));
    5565          14 :     case 2: return RgM_det2(M);
    5566             :   }
    5567         161 :   if (max > ((n+2)>>1)) max = (n+2)>>1;
    5568         735 :   for (j = 1; j <= n; j++)
    5569             :   {
    5570         609 :     pari_sp av2 = avma;
    5571         609 :     GEN v = col_count_non_zero(M, j, max);
    5572             :     long lv;
    5573         609 :     if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
    5574          63 :     if (lv == 1) { avma = av; return gen_0; }
    5575          63 :     if (lv == 2) {
    5576          35 :       avma = av;
    5577          35 :       return gerepileupto(av, coeff_det(M,v[1],j,max,bound));
    5578             :     }
    5579          28 :     best = v; lbest = lv; best_col = j;
    5580             :   }
    5581         686 :   for (i = 1; i <= n; i++)
    5582             :   {
    5583         560 :     pari_sp av2 = avma;
    5584         560 :     GEN v = row_count_non_zero(M, i, max);
    5585             :     long lv;
    5586         560 :     if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
    5587          42 :     if (lv == 1) { avma = av; return gen_0; }
    5588          42 :     if (lv == 2) {
    5589           0 :       avma = av;
    5590           0 :       return gerepileupto(av, coeff_det(M,i,v[1],max,bound));
    5591             :     }
    5592          42 :     best = v; lbest = lv; best_row = i;
    5593             :   }
    5594         126 :   if (best_row)
    5595             :   {
    5596          42 :     double d = lbest-1;
    5597          42 :     GEN s = NULL;
    5598             :     long k;
    5599          42 :     bound /= d*d*d;
    5600         168 :     for (k = 1; k < lbest; k++)
    5601             :     {
    5602         126 :       GEN c = coeff_det(M, best_row, best[k], max, bound);
    5603         126 :       s = s? gadd(s, c): c;
    5604             :     }
    5605          42 :     return gerepileupto(av, s);
    5606             :   }
    5607          84 :   if (best_col)
    5608             :   {
    5609          14 :     double d = lbest-1;
    5610          14 :     GEN s = NULL;
    5611             :     long k;
    5612          14 :     bound /= d*d*d;
    5613          56 :     for (k = 1; k < lbest; k++)
    5614             :     {
    5615          42 :       GEN c = coeff_det(M, best[k], best_col, max, bound);
    5616          42 :       s = s? gadd(s, c): c;
    5617             :     }
    5618          14 :     return gerepileupto(av, s);
    5619             :   }
    5620          70 :   return det_bareiss(M);
    5621             : }
    5622             : 
    5623             : /* area of parallelogram bounded by (v1,v2) */
    5624             : static GEN
    5625       51681 : parallelogramarea(GEN v1, GEN v2)
    5626       51681 : { return gsub(gmul(gnorml2(v1), gnorml2(v2)), gsqr(RgV_dotproduct(v1, v2))); }
    5627             : 
    5628             : /* Square of Hadamard bound for det(a), a square matrix.
    5629             :  * Slightly improvement: instead of using the column norms, use the area of
    5630             :  * the parallelogram formed by pairs of consecutive vectors */
    5631             : GEN
    5632       16772 : RgM_Hadamard(GEN a)
    5633             : {
    5634       16772 :   pari_sp av = avma;
    5635       16772 :   long n = lg(a)-1, i;
    5636             :   GEN B;
    5637       16772 :   if (n == 0) return gen_1;
    5638       16772 :   if (n == 1) return gsqr(gcoeff(a,1,1));
    5639       16772 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    5640       16772 :   B = gen_1;
    5641       68453 :   for (i = 1; i <= n/2; i++)
    5642       51681 :     B = gmul(B, parallelogramarea(gel(a,2*i-1), gel(a,2*i)));
    5643       16772 :   if (odd(n)) B = gmul(B, gnorml2(gel(a, n)));
    5644       16772 :   return gerepileuptoint(av, ceil_safe(B));
    5645             : }
    5646             : 
    5647             : /* assume dim(a) = n > 0 */
    5648             : static GEN
    5649       28000 : ZM_det_i(GEN M, long n)
    5650             : {
    5651       28000 :   const long DIXON_THRESHOLD = 40;
    5652       28000 :   pari_sp av = avma, av2;
    5653             :   long i;
    5654       28000 :   ulong p, compp, Dp = 1;
    5655             :   forprime_t S;
    5656             :   GEN D, h, q, v, comp;
    5657       28000 :   if (n == 1) return icopy(gcoeff(M,1,1));
    5658       27167 :   if (n == 2) return ZM_det2(M);
    5659       19705 :   if (n == 3) return ZM_det3(M);
    5660       16772 :   h = RgM_Hadamard(M);
    5661       16772 :   if (!signe(h)) { avma = av; return gen_0; }
    5662       16772 :   h = sqrti(h); q = gen_1;
    5663       16772 :   init_modular_big(&S);
    5664       16772 :   p = 0; /* -Wall */
    5665       33544 :   while( cmpii(q, h) <= 0 && (p = u_forprime_next(&S)) )
    5666             :   {
    5667       16772 :     av2 = avma; Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    5668       16772 :     avma = av2;
    5669       16772 :     if (Dp) break;
    5670           0 :     q = muliu(q, p);
    5671             :   }
    5672       16772 :   if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    5673       16772 :   if (!Dp) { avma = av; return gen_0; }
    5674       16772 :   if (n <= DIXON_THRESHOLD)
    5675       16772 :     D = q;
    5676             :   else
    5677             :   {
    5678           0 :     av2 = avma;
    5679           0 :     v = cgetg(n+1, t_COL);
    5680           0 :     gel(v, 1) = gen_1; /* ensure content(v) = 1 */
    5681           0 :     for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    5682           0 :     D = Q_denom(ZM_gauss(M, v));
    5683           0 :     if (expi(D) < expi(h) >> 1)
    5684             :     { /* First try unlucky, try once more */
    5685           0 :       for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
    5686           0 :       D = lcmii(D, Q_denom(ZM_gauss(M, v)));
    5687             :     }
    5688           0 :     D = gerepileuptoint(av2, D);
    5689           0 :     if (q != gen_1) D = lcmii(D, q);
    5690             :   }
    5691             :   /* determinant is a multiple of D */
    5692       16772 :   h = shifti(divii(h, D), 1);
    5693             : 
    5694       16772 :   compp = Fl_div(Dp, umodiu(D,p), p);
    5695       16772 :   comp = Z_init_CRT(compp, p);
    5696       16772 :   q = utoipos(p);
    5697       85248 :   while (cmpii(q, h) <= 0)
    5698             :   {
    5699       51704 :     p = u_forprime_next(&S);
    5700       51704 :     if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    5701       51704 :     Dp = umodiu(D, p);
    5702       51704 :     if (!Dp) continue;
    5703       51704 :     av2 = avma;
    5704       51704 :     compp = Fl_div(Flm_det_sp(ZM_to_Flm(M, p), p), Dp, p);
    5705       51704 :     avma = av2;
    5706       51704 :     (void) Z_incremental_CRT(&comp, compp, &q, p);
    5707             :   }
    5708       16772 :   return gerepileuptoint(av, mulii(comp, D));
    5709             : }
    5710             : 
    5711             : static long
    5712          98 : det_init_max(long n)
    5713             : {
    5714          98 :   if (n > 100) return 0;
    5715          98 :   if (n > 50) return 1;
    5716          98 :   if (n > 30) return 4;
    5717          98 :   return 7;
    5718             : }
    5719             : 
    5720             : GEN
    5721       11223 : det(GEN a)
    5722             : {
    5723       11223 :   long n = lg(a)-1;
    5724             :   double B;
    5725       11223 :   GEN data, ff = NULL, p = NULL;
    5726             :   pivot_fun pivot;
    5727             : 
    5728       11223 :   if (typ(a)!=t_MAT) pari_err_TYPE("det",a);
    5729       11223 :   if (!n) return gen_1;
    5730       11181 :   if (n != nbrows(a)) pari_err_DIM("det");
    5731       11174 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5732       10980 :   if (n == 2) return RgM_det2(a);
    5733       10098 :   if (RgM_is_FpM(a, &p))
    5734             :   {
    5735        1764 :     pari_sp av = avma;
    5736             :     ulong pp, d;
    5737        1764 :     if (!p) return ZM_det_i(a, n); /* ZM */
    5738             :     /* FpM */
    5739        1491 :     a = RgM_Fp_init(a,p,&pp);
    5740        1491 :     switch(pp)
    5741             :     {
    5742          49 :     case 0: return gerepileupto(av, Fp_to_mod(FpM_det(a,p),p)); break;
    5743          14 :     case 2: d = F2m_det(a); break;
    5744        1428 :     default:d = Flm_det_sp(a, pp); break;
    5745             :     }
    5746        1442 :     avma = av; return mkintmodu(d, pp);
    5747             :   }
    5748        8334 :   if (RgM_is_FFM(a, &ff)) return FFM_det(a, ff);
    5749        8299 :   pivot = get_pivot_fun(a, a, &data);
    5750        8299 :   if (pivot != gauss_get_pivot_NZ) return det_simple_gauss(a, data, pivot);
    5751          98 :   B = (double)n;
    5752          98 :   return det_develop(a, det_init_max(n), B*B*B);
    5753             : }
    5754             : 
    5755             : GEN
    5756       27734 : ZM_det(GEN a)
    5757             : {
    5758       27734 :   long n = lg(a)-1;
    5759       27734 :   if (!n) return gen_1;
    5760       27727 :   return ZM_det_i(a, n);
    5761             : }
    5762             : 
    5763             : /* return a solution of congruence system sum M_{ i,j } X_j = Y_i mod D_i
    5764             :  * If ptu1 != NULL, put in *ptu1 a Z-basis of the homogeneous system */
    5765             : static GEN
    5766          49 : gaussmoduloall(GEN M, GEN D, GEN Y, GEN *ptu1)
    5767             : {
    5768          49 :   pari_sp av = avma;
    5769             :   long n, m, j, l, lM;
    5770             :   GEN delta, H, U, u1, u2, x;
    5771             : 
    5772          49 :   if (typ(M)!=t_MAT) pari_err_TYPE("gaussmodulo",M);
    5773          49 :   lM = lg(M);
    5774          49 :   if (lM == 1)
    5775             :   {
    5776          28 :     switch(typ(Y))
    5777             :     {
    5778           7 :       case t_INT: break;
    5779          14 :       case t_COL: if (lg(Y) != 1) pari_err_DIM("gaussmodulo");
    5780          14 :                   break;
    5781           7 :       default: pari_err_TYPE("gaussmodulo",Y);
    5782             :     }
    5783          21 :     switch(typ(D))
    5784             :     {
    5785           7 :       case t_INT: break;
    5786           7 :       case t_COL: if (lg(D) != 1) pari_err_DIM("gaussmodulo");
    5787           7 :                   break;
    5788           7 :       default: pari_err_TYPE("gaussmodulo",D);
    5789             :     }
    5790          14 :     if (ptu1) *ptu1 = cgetg(1, t_MAT);
    5791          14 :     return gen_0;
    5792             :   }
    5793          21 :   n = nbrows(M);
    5794          21 :   switch(typ(D))
    5795             :   {
    5796             :     case t_COL:
    5797          14 :       if (lg(D)-1!=n) pari_err_DIM("gaussmodulo");
    5798          14 :       delta = diagonal_shallow(D); break;
    5799           7 :     case t_INT: delta = scalarmat_shallow(D,n); break;
    5800           0 :     default: pari_err_TYPE("gaussmodulo",D);
    5801             :       return NULL; /* LCOV_EXCL_LINE */
    5802             :   }
    5803          21 :   switch(typ(Y))
    5804             :   {
    5805           7 :     case t_INT: Y = const_col(n, Y); break;
    5806             :     case t_COL:
    5807          14 :       if (lg(Y)-1!=n) pari_err_DIM("gaussmodulo");
    5808          14 :       break;
    5809           0 :     default: pari_err_TYPE("gaussmodulo",Y);
    5810             :       return NULL; /* LCOV_EXCL_LINE */
    5811             :   }
    5812          21 :   H = ZM_hnfall_i(shallowconcat(M,delta), &U, 1);
    5813          21 :   Y = hnf_solve(H,Y); if (!Y) return gen_0;
    5814          21 :   l = lg(H); /* may be smaller than lM if some moduli are 0 */
    5815          21 :   n = l-1;
    5816          21 :   m = lg(U)-1 - n;
    5817          21 :   u1 = cgetg(m+1,t_MAT);
    5818          21 :   u2 = cgetg(n+1,t_MAT);
    5819          21 :   for (j=1; j<=m; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u1,j) = c; }
    5820          21 :   U += m;
    5821          21 :   for (j=1; j<=n; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u2,j) = c; }
    5822             :   /*       (u1 u2)
    5823             :    * (M D) (*  * ) = (0 H) */
    5824          21 :   u1 = ZM_lll(u1, 0.75, LLL_INPLACE);
    5825          21 :   Y = ZM_ZC_mul(u2,Y);
    5826          21 :   x = ZC_reducemodmatrix(Y, u1);
    5827          21 :   if (!ptu1) x = gerepileupto(av, x);
    5828             :   else
    5829             :   {
    5830          14 :     gerepileall(av, 2, &x, &u1);
    5831          14 :     *ptu1 = u1;
    5832             :   }
    5833          21 :   return x;
    5834             : }
    5835             : 
    5836             : GEN
    5837          49 : matsolvemod0(GEN M, GEN D, GEN Y, long flag)
    5838             : {
    5839             :   pari_sp av;
    5840             :   GEN p1,y;
    5841             : 
    5842          49 :   if (!flag) return gaussmoduloall(M,D,Y,NULL);
    5843             : 
    5844          42 :   av=avma; y = cgetg(3,t_VEC);
    5845          42 :   p1 = gaussmoduloall(M,D,Y, (GEN*)y+2);
    5846          28 :   if (p1==gen_0) { avma=av; return gen_0; }
    5847          14 :   gel(y,1) = p1; return y;
    5848             : }
    5849             : 
    5850             : GEN
    5851           0 : gaussmodulo2(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,1); }
    5852             : 
    5853             : GEN
    5854           0 : gaussmodulo(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,0); }

Generated by: LCOV version 1.11