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 21682-493a494) Lines: 3392 3606 94.1 %
Date: 2018-01-16 06:18:33 Functions: 323 341 94.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11