Code coverage tests

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

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

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

LCOV - code coverage report
Current view: top level - basemath - alglin1.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.18.1 lcov report (development 30213-f9b05da6c0) Lines: 2703 3076 87.9 %
Date: 2025-04-26 09:18:30 Functions: 296 316 93.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; either version 2 of the License, or (at your option) any later
       8             : version. It is distributed in the hope that it will be useful, but WITHOUT
       9             : ANY WARRANTY WHATSOEVER.
      10             : 
      11             : Check the License for details. You should have received a copy of it, along
      12             : with the package; see the file 'COPYING'. If not, write to the Free Software
      13             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      14             : 
      15             : /********************************************************************/
      16             : /**                                                                **/
      17             : /**                         LINEAR ALGEBRA                         **/
      18             : /**                          (first part)                          **/
      19             : /**                                                                **/
      20             : /********************************************************************/
      21             : #include "pari.h"
      22             : #include "paripriv.h"
      23             : 
      24             : #define DEBUGLEVEL DEBUGLEVEL_mat
      25             : 
      26             : /*******************************************************************/
      27             : /*                        GC FOR HUGE MATRICES                     */
      28             : /*******************************************************************/
      29             : /* update address if in stack range */
      30             : #define gc_dec(x, av0, av, dec) \
      31             : { pari_sp _A = (pari_sp)(x); if (_A < (av) && _A >= (av0)) (x) += (dec); }
      32             : 
      33             : /* Perform GC on t_MAT x updating only needed elements, on columns >= k.
      34             :  * On column k, we only need to consider rows > t */
      35             : static void
      36           0 : gen_gc_ker(pari_sp av, GEN x, long k, long t, void *E, GEN (*red)(void*, GEN))
      37             : {
      38           0 :   pari_sp tetpil = avma, bot = pari_mainstack->bot;
      39           0 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      40             :   size_t dec;
      41             : 
      42           0 :   if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot_ker. k=%ld, n=%ld",k,n);
      43           0 :   for (u=1; u<=m; u++)
      44             :   {
      45           0 :     if (u > t) gcoeff(x,u,k) = red(E,gcoeff(x,u,k));
      46           0 :     for (i=k+1; i<=n; i++) gcoeff(x,u,i) = red(E,gcoeff(x,u,i));
      47             :   }
      48           0 :   dec = gc_stack_update(av, tetpil);
      49           0 :   for (u=1; u<=m; u++)
      50             :   {
      51           0 :     if (u > t) gc_dec(coeff(x,u,k), bot, av, dec);
      52           0 :     for (i=k+1; i<=n; i++) gc_dec(coeff(x,u,i), bot, av, dec);
      53             :   }
      54           0 : }
      55             : 
      56             : #define COPY(x) \
      57             : { GEN _t = (x); if (!is_universal_constant(_t)) x = gcopy(_t); }
      58             : INLINE GEN
      59           0 : _copy(void *E, GEN x) { (void) E; COPY(x); return x; }
      60             : static void
      61           0 : gc_ker(pari_sp av, GEN x, long k, long t)
      62           0 : { gen_gc_ker(av, x, k, t, NULL, &_copy); }
      63             : 
      64             : /* Perform GC on t_MAT x updating only needed elements: on columns >= k
      65             :  * and row j or not containing a pivot yet (c[i] = 0). On column k,
      66             :  * we only need to consider rows > t */
      67             : static void
      68           0 : gc_gauss(pari_sp av, GEN x,long k,long t, long j, GEN c)
      69             : {
      70           0 :   pari_sp tetpil = avma, bot = pari_mainstack->bot;
      71           0 :   long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
      72             :   size_t dec;
      73             : 
      74           0 :   if (DEBUGMEM > 1) pari_warn(warnmem,"RgM_pivots. k=%ld, n=%ld",k,n);
      75           0 :   for (u=1; u<=m; u++) if (u==j || !c[u])
      76             :   {
      77           0 :     if (u > t) COPY(gcoeff(x,u,k));
      78           0 :     for (i=k+1; i<=n; i++) COPY(gcoeff(x,u,i));
      79             :   }
      80           0 :   dec = gc_stack_update(av, tetpil);
      81           0 :   for (u=1; u<=m; u++) if (u==j || !c[u])
      82             :   {
      83           0 :     if (u > t) gc_dec(coeff(x,u,k), bot, av, dec);
      84           0 :     for (i=k+1; i<=n; i++) gc_dec(coeff(x,u,i), bot, av, dec);
      85             :   }
      86           0 : }
      87             : 
      88             : /*******************************************************************/
      89             : /*                                                                 */
      90             : /*                         GENERIC                                 */
      91             : /*                                                                 */
      92             : /*******************************************************************/
      93             : GEN
      94        1271 : gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff)
      95             : {
      96        1271 :   pari_sp av0 = avma, av;
      97             :   GEN y, c, d;
      98             :   long i, j, k, r, t, n, m;
      99             : 
     100        1271 :   n=lg(x)-1; if (!n) return cgetg(1,t_MAT);
     101        1271 :   m=nbrows(x); r=0;
     102        1271 :   x = RgM_shallowcopy(x);
     103        1271 :   c = zero_zv(m);
     104        1271 :   d=new_chunk(n+1);
     105        1271 :   av=avma;
     106        4558 :   for (k=1; k<=n; k++)
     107             :   {
     108        9501 :     for (j=1; j<=m; j++)
     109        8047 :       if (!c[j])
     110             :       {
     111        5583 :         gcoeff(x,j,k) = ff->red(E, gcoeff(x,j,k));
     112        5583 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     113             :       }
     114        3322 :     if (j>m)
     115             :     {
     116        1454 :       if (deplin)
     117             :       {
     118          35 :         GEN c = cgetg(n+1, t_COL), g0 = ff->s(E,0), g1=ff->s(E,1);
     119          98 :         for (i=1; i<k; i++) gel(c,i) = ff->red(E, gcoeff(x,d[i],k));
     120          63 :         gel(c,k) = g1; for (i=k+1; i<=n; i++) gel(c,i) = g0;
     121          35 :         return gc_upto(av0, c);
     122             :       }
     123        1419 :       r++; d[k]=0;
     124        3313 :       for(j=1; j<k; j++)
     125        1894 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
     126             :     }
     127             :     else
     128             :     {
     129        1868 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     130        1868 :       c[j] = k; d[k] = j;
     131        1868 :       gcoeff(x,j,k) = ff->s(E,-1);
     132        4554 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     133        9916 :       for (t=1; t<=m; t++)
     134             :       {
     135        8048 :         if (t==j) continue;
     136             : 
     137        6180 :         piv = ff->red(E,gcoeff(x,t,k));
     138        6180 :         if (ff->equal0(piv)) continue;
     139             : 
     140        2240 :         gcoeff(x,t,k) = ff->s(E,0);
     141        5527 :         for (i=k+1; i<=n; i++)
     142        3287 :            gcoeff(x,t,i) = ff->red(E, ff->add(E, gcoeff(x,t,i),
     143        3287 :                                       ff->mul(E,piv,gcoeff(x,j,i))));
     144        2240 :         if (gc_needed(av,1)) gen_gc_ker(av, x,k,t,E,ff->red);
     145             :       }
     146             :     }
     147             :   }
     148        1236 :   if (deplin) return gc_NULL(av0);
     149             : 
     150        1208 :   y = cgetg(r+1, t_MAT);
     151        2627 :   for (j=k=1; j<=r; j++,k++)
     152             :   {
     153        1419 :     GEN C = cgetg(n+1,t_COL);
     154        1419 :     GEN g0 = ff->s(E,0), g1 = ff->s(E,1);
     155        2640 :     gel(y,j) = C; while (d[k]) k++;
     156        3313 :     for (i=1; i<k; i++)
     157        1894 :       if (d[i])
     158             :       {
     159        1512 :         GEN p1=gcoeff(x,d[i],k);
     160        1512 :         gel(C,i) = ff->red(E,p1); gunclone(p1);
     161             :       }
     162             :       else
     163         382 :         gel(C,i) = g0;
     164        2096 :     gel(C,k) = g1; for (i=k+1; i<=n; i++) gel(C,i) = g0;
     165             :   }
     166        1208 :   return gc_upto(av0, y);
     167             : }
     168             : 
     169             : GEN
     170        1119 : gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff)
     171             : {
     172             :   pari_sp av;
     173             :   GEN c, d;
     174        1119 :   long i, j, k, r, t, m, n = lg(x)-1;
     175             : 
     176        1119 :   if (!n) { *rr = 0; return NULL; }
     177             : 
     178        1119 :   m=nbrows(x); r=0;
     179        1119 :   d = cgetg(n+1, t_VECSMALL);
     180        1119 :   x = RgM_shallowcopy(x);
     181        1119 :   c = zero_zv(m);
     182        1119 :   av=avma;
     183        3816 :   for (k=1; k<=n; k++)
     184             :   {
     185        7828 :     for (j=1; j<=m; j++)
     186        7220 :       if (!c[j])
     187             :       {
     188        5367 :         gcoeff(x,j,k) = ff->red(E,gcoeff(x,j,k));
     189        5367 :         if (!ff->equal0(gcoeff(x,j,k))) break;
     190             :       }
     191        2697 :     if (j>m) { r++; d[k]=0; }
     192             :     else
     193             :     {
     194        2089 :       GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
     195        2089 :       GEN g0 = ff->s(E,0);
     196        2089 :       c[j] = k; d[k] = j;
     197        4012 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
     198       11858 :       for (t=1; t<=m; t++)
     199             :       {
     200        9769 :         if (c[t]) continue; /* already a pivot on that line */
     201             : 
     202        6344 :         piv = ff->red(E,gcoeff(x,t,k));
     203        6344 :         if (ff->equal0(piv)) continue;
     204        3021 :         gcoeff(x,t,k) = g0;
     205        4712 :         for (i=k+1; i<=n; i++)
     206        1691 :           gcoeff(x,t,i) = ff->red(E, ff->add(E,gcoeff(x,t,i),
     207        1691 :                                      ff->mul(E,piv,gcoeff(x,j,i))));
     208        3021 :         if (gc_needed(av,1)) gc_gauss(av, x,k,t,j,c);
     209             :       }
     210        6101 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = g0; /* dummy */
     211             :     }
     212             :   }
     213        1119 :   *rr = r; return gc_const((pari_sp)d, d);
     214             : }
     215             : 
     216             : GEN
     217         294 : gen_det(GEN a, void *E, const struct bb_field *ff)
     218             : {
     219         294 :   pari_sp av = avma;
     220         294 :   long i,j,k, s = 1, nbco = lg(a)-1;
     221         294 :   GEN x = ff->s(E,1);
     222         294 :   if (!nbco) return x;
     223         287 :   a = RgM_shallowcopy(a);
     224        1064 :   for (i=1; i<nbco; i++)
     225             :   {
     226             :     GEN q;
     227        1029 :     for(k=i; k<=nbco; k++)
     228             :     {
     229         994 :       gcoeff(a,k,i) = ff->red(E,gcoeff(a,k,i));
     230         994 :       if (!ff->equal0(gcoeff(a,k,i))) break;
     231             :     }
     232         812 :     if (k > nbco) return gc_upto(av, gcoeff(a,i,i));
     233         777 :     if (k != i)
     234             :     { /* exchange the lines s.t. k = i */
     235         413 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     236         105 :       s = -s;
     237             :     }
     238         777 :     q = gcoeff(a,i,i);
     239         777 :     x = ff->red(E,ff->mul(E,x,q));
     240         777 :     q = ff->inv(E,q);
     241        2324 :     for (k=i+1; k<=nbco; k++)
     242             :     {
     243        1547 :       GEN m = ff->red(E,gcoeff(a,i,k));
     244        1547 :       if (ff->equal0(m)) continue;
     245        1092 :       m = ff->neg(E, ff->red(E,ff->mul(E,m, q)));
     246        3528 :       for (j=i+1; j<=nbco; j++)
     247        2436 :         gcoeff(a,j,k) = ff->red(E, ff->add(E, gcoeff(a,j,k),
     248        2436 :                                    ff->mul(E, m, gcoeff(a,j,i))));
     249             :     }
     250         777 :     if (gc_needed(av,2))
     251             :     {
     252           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
     253           0 :       (void)gc_all(av,2, &a,&x);
     254             :     }
     255             :   }
     256         252 :   if (s < 0) x = ff->neg(E,x);
     257         252 :   return gc_upto(av, ff->red(E,ff->mul(E, x, gcoeff(a,nbco,nbco))));
     258             : }
     259             : 
     260             : INLINE void
     261       57335 : _gen_addmul(GEN b, long k, long i, GEN m, void *E, const struct bb_field *ff)
     262             : {
     263       57335 :   gel(b,i) = ff->red(E,gel(b,i));
     264       57335 :   gel(b,k) = ff->add(E,gel(b,k), ff->mul(E,m, gel(b,i)));
     265       57335 : }
     266             : 
     267             : static GEN
     268       21485 : _gen_get_col(GEN a, GEN b, long li, void *E, const struct bb_field *ff)
     269             : {
     270       21485 :   GEN u = cgetg(li+1,t_COL);
     271       21485 :   pari_sp av = avma;
     272             :   long i, j;
     273             : 
     274       21485 :   gel(u,li) = gc_upto(av, ff->red(E,ff->mul(E,gel(b,li), gcoeff(a,li,li))));
     275       94317 :   for (i=li-1; i>0; i--)
     276             :   {
     277       72832 :     pari_sp av = avma;
     278       72832 :     GEN m = gel(b,i);
     279      296314 :     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))));
     280       72832 :     m = ff->red(E, m);
     281       72832 :     gel(u,i) = gc_upto(av, ff->red(E,ff->mul(E,m, gcoeff(a,i,i))));
     282             :   }
     283       21485 :   return u;
     284             : }
     285             : 
     286             : GEN
     287        5816 : gen_Gauss(GEN a, GEN b, void *E, const struct bb_field *ff)
     288             : {
     289             :   long i, j, k, li, bco, aco;
     290        5816 :   GEN u, g0 = ff->s(E,0);
     291        5816 :   pari_sp av = avma;
     292        5816 :   a = RgM_shallowcopy(a);
     293        5816 :   b = RgM_shallowcopy(b);
     294        5816 :   aco = lg(a)-1; bco = lg(b)-1; li = nbrows(a);
     295       20204 :   for (i=1; i<=aco; i++)
     296             :   {
     297             :     GEN invpiv;
     298       22393 :     for (k = i; k <= li; k++)
     299             :     {
     300       22351 :       GEN piv = ff->red(E,gcoeff(a,k,i));
     301       22351 :       if (!ff->equal0(piv)) { gcoeff(a,k,i) = ff->inv(E,piv); break; }
     302        2189 :       gcoeff(a,k,i) = g0;
     303             :     }
     304             :     /* found a pivot on line k */
     305       20204 :     if (k > li) return NULL;
     306       20162 :     if (k != i)
     307             :     { /* swap lines so that k = i */
     308        9276 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
     309       12419 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
     310             :     }
     311       20162 :     if (i == aco) break;
     312             : 
     313       14388 :     invpiv = gcoeff(a,i,i); /* 1/piv mod p */
     314       51602 :     for (k=i+1; k<=li; k++)
     315             :     {
     316       37214 :       GEN m = ff->red(E,gcoeff(a,k,i)); gcoeff(a,k,i) = g0;
     317       37214 :       if (ff->equal0(m)) continue;
     318             : 
     319        7799 :       m = ff->red(E,ff->neg(E,ff->mul(E,m, invpiv)));
     320       29376 :       for (j=i+1; j<=aco; j++) _gen_addmul(gel(a,j),k,i,m,E,ff);
     321       43557 :       for (j=1  ; j<=bco; j++) _gen_addmul(gel(b,j),k,i,m,E,ff);
     322             :     }
     323       14388 :     if (gc_needed(av,1))
     324             :     {
     325           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"gen_Gauss. i=%ld",i);
     326           0 :       (void)gc_all(av,2, &a,&b);
     327             :     }
     328             :   }
     329             : 
     330        5774 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
     331        5774 :   u = cgetg(bco+1,t_MAT);
     332       27259 :   for (j=1; j<=bco; j++) gel(u,j) = _gen_get_col(a, gel(b,j), aco, E, ff);
     333        5774 :   return u;
     334             : }
     335             : 
     336             : /* compatible t_MAT * t_COL, lgA = lg(A) = lg(B) > 1, l = lgcols(A) */
     337             : static GEN
     338      350531 : gen_matcolmul_i(GEN A, GEN B, ulong lgA, ulong l,
     339             :                 void *E, const struct bb_field *ff)
     340             : {
     341      350531 :   GEN C = cgetg(l, t_COL);
     342             :   ulong i;
     343     2231912 :   for (i = 1; i < l; i++) {
     344     1881381 :     pari_sp av = avma;
     345     1881381 :     GEN e = ff->mul(E, gcoeff(A, i, 1), gel(B, 1));
     346             :     ulong k;
     347     5531603 :     for(k = 2; k < lgA; k++)
     348     3650222 :       e = ff->add(E, e, ff->mul(E, gcoeff(A, i, k), gel(B, k)));
     349     1881381 :     gel(C, i) = gc_upto(av, ff->red(E, e));
     350             :   }
     351      350531 :   return C;
     352             : }
     353             : 
     354             : GEN
     355       48648 : gen_matcolmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     356             : {
     357       48648 :   ulong lgA = lg(A);
     358       48648 :   if (lgA != (ulong)lg(B))
     359           0 :     pari_err_OP("operation 'gen_matcolmul'", A, B);
     360       48648 :   if (lgA == 1)
     361           0 :     return cgetg(1, t_COL);
     362       48648 :   return gen_matcolmul_i(A, B, lgA, lgcols(A), E, ff);
     363             : }
     364             : 
     365             : static GEN
     366       75983 : gen_matmul_classical(GEN A, GEN B, long l, long la, long lb,
     367             :                      void *E, const struct bb_field *ff)
     368             : {
     369             :   long j;
     370       75983 :   GEN C = cgetg(lb, t_MAT);
     371      377866 :   for(j = 1; j < lb; j++)
     372      301883 :     gel(C, j) = gen_matcolmul_i(A, gel(B, j), la, l, E, ff);
     373       75983 :   return C;
     374             : }
     375             : 
     376             : /* Strassen-Winograd algorithm */
     377             : 
     378             : /* Return A[ma+1..ma+da, na+1..na+ea] - B[mb+1..mb+db, nb+1..nb+eb]
     379             :  * as an (m x n)-matrix, padding the input with zeroes as necessary. */
     380             : static GEN
     381           0 : add_slices(long m, long n,
     382             :            GEN A, long ma, long da, long na, long ea,
     383             :            GEN B, long mb, long db, long nb, long eb,
     384             :            void *E, const struct bb_field *ff)
     385             : {
     386           0 :   long min_d = minss(da, db), min_e = minss(ea, eb), i, j;
     387           0 :   GEN M = cgetg(n + 1, t_MAT), C;
     388             : 
     389           0 :   for (j = 1; j <= min_e; j++) {
     390           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     391           0 :     for (i = 1; i <= min_d; i++)
     392           0 :       gel(C, i) = ff->add(E, gcoeff(A, ma + i, na + j),
     393           0 :                           gcoeff(B, mb + i, nb + j));
     394           0 :     for (; i <= da; i++)
     395           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     396           0 :     for (; i <= db; i++)
     397           0 :       gel(C, i) = gcoeff(B, mb + i, nb + j);
     398           0 :     for (; i <= m; i++)
     399           0 :       gel(C, i) = ff->s(E, 0);
     400             :   }
     401           0 :   for (; j <= ea; j++) {
     402           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     403           0 :     for (i = 1; i <= da; i++)
     404           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     405           0 :     for (; i <= m; i++)
     406           0 :       gel(C, i) = ff->s(E, 0);
     407             :   }
     408           0 :   for (; j <= eb; j++) {
     409           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     410           0 :     for (i = 1; i <= db; i++)
     411           0 :       gel(C, i) = gcoeff(B, mb + i, nb + j);
     412           0 :     for (; i <= m; i++)
     413           0 :       gel(C, i) = ff->s(E, 0);
     414             :   }
     415           0 :   for (; j <= n; j++) {
     416           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     417           0 :     for (i = 1; i <= m; i++)
     418           0 :       gel(C, i) = ff->s(E, 0);
     419             :   }
     420           0 :   return M;
     421             : }
     422             : 
     423             : /* Return A[ma+1..ma+da, na+1..na+ea] - B[mb+1..mb+db, nb+1..nb+eb]
     424             :  * as an (m x n)-matrix, padding the input with zeroes as necessary. */
     425             : static GEN
     426           0 : subtract_slices(long m, long n,
     427             :                 GEN A, long ma, long da, long na, long ea,
     428             :                 GEN B, long mb, long db, long nb, long eb,
     429             :                 void *E, const struct bb_field *ff)
     430             : {
     431           0 :   long min_d = minss(da, db), min_e = minss(ea, eb), i, j;
     432           0 :   GEN M = cgetg(n + 1, t_MAT), C;
     433             : 
     434           0 :   for (j = 1; j <= min_e; j++) {
     435           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     436           0 :     for (i = 1; i <= min_d; i++)
     437           0 :       gel(C, i) = ff->add(E, gcoeff(A, ma + i, na + j),
     438           0 :                           ff->neg(E, gcoeff(B, mb + i, nb + j)));
     439           0 :     for (; i <= da; i++)
     440           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     441           0 :     for (; i <= db; i++)
     442           0 :       gel(C, i) = ff->neg(E, gcoeff(B, mb + i, nb + j));
     443           0 :     for (; i <= m; i++)
     444           0 :       gel(C, i) = ff->s(E, 0);
     445             :   }
     446           0 :   for (; j <= ea; j++) {
     447           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     448           0 :     for (i = 1; i <= da; i++)
     449           0 :       gel(C, i) = gcoeff(A, ma + i, na + j);
     450           0 :     for (; i <= m; i++)
     451           0 :       gel(C, i) = ff->s(E, 0);
     452             :   }
     453           0 :   for (; j <= eb; j++) {
     454           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     455           0 :     for (i = 1; i <= db; i++)
     456           0 :       gel(C, i) = ff->neg(E, gcoeff(B, mb + i, nb + j));
     457           0 :     for (; i <= m; i++)
     458           0 :       gel(C, i) = ff->s(E, 0);
     459             :   }
     460           0 :   for (; j <= n; j++) {
     461           0 :     gel(M, j) = C = cgetg(m + 1, t_COL);
     462           0 :     for (i = 1; i <= m; i++)
     463           0 :       gel(C, i) = ff->s(E, 0);
     464             :   }
     465           0 :   return M;
     466             : }
     467             : 
     468             : static GEN gen_matmul_i(GEN A, GEN B, long l, long la, long lb,
     469             :                         void *E, const struct bb_field *ff);
     470             : 
     471             : static GEN
     472           0 : gen_matmul_sw(GEN A, GEN B, long m, long n, long p,
     473             :               void *E, const struct bb_field *ff)
     474             : {
     475           0 :   pari_sp av = avma;
     476           0 :   long m1 = (m + 1)/2, m2 = m/2,
     477           0 :     n1 = (n + 1)/2, n2 = n/2,
     478           0 :     p1 = (p + 1)/2, p2 = p/2;
     479             :   GEN A11, A12, A22, B11, B21, B22,
     480             :     S1, S2, S3, S4, T1, T2, T3, T4,
     481             :     M1, M2, M3, M4, M5, M6, M7,
     482             :     V1, V2, V3, C11, C12, C21, C22, C;
     483             : 
     484           0 :   T2 = subtract_slices(n1, p2, B, 0, n1, p1, p2, B, n1, n2, p1, p2, E, ff);
     485           0 :   S1 = subtract_slices(m2, n1, A, m1, m2, 0, n1, A, 0, m2, 0, n1, E, ff);
     486           0 :   M2 = gen_matmul_i(S1, T2, m2 + 1, n1 + 1, p2 + 1, E, ff);
     487           0 :   if (gc_needed(av, 1))
     488           0 :     (void)gc_all(av, 2, &T2, &M2);  /* destroy S1 */
     489           0 :   T3 = subtract_slices(n1, p1, T2, 0, n1, 0, p2, B, 0, n1, 0, p1, E, ff);
     490           0 :   if (gc_needed(av, 1))
     491           0 :     (void)gc_all(av, 2, &M2, &T3);  /* destroy T2 */
     492           0 :   S2 = add_slices(m2, n1, A, m1, m2, 0, n1, A, m1, m2, n1, n2, E, ff);
     493           0 :   T1 = subtract_slices(n1, p1, B, 0, n1, p1, p2, B, 0, n1, 0, p2, E, ff);
     494           0 :   M3 = gen_matmul_i(S2, T1, m2 + 1, n1 + 1, p2 + 1, E, ff);
     495           0 :   if (gc_needed(av, 1))
     496           0 :     (void)gc_all(av, 4, &M2, &T3, &S2, &M3);  /* destroy T1 */
     497           0 :   S3 = subtract_slices(m1, n1, S2, 0, m2, 0, n1, A, 0, m1, 0, n1, E, ff);
     498           0 :   if (gc_needed(av, 1))
     499           0 :     (void)gc_all(av, 4, &M2, &T3, &M3, &S3);  /* destroy S2 */
     500           0 :   A11 = matslice(A, 1, m1, 1, n1);
     501           0 :   B11 = matslice(B, 1, n1, 1, p1);
     502           0 :   M1 = gen_matmul_i(A11, B11, m1 + 1, n1 + 1, p1 + 1, E, ff);
     503           0 :   if (gc_needed(av, 1))
     504           0 :     (void)gc_all(av, 5, &M2, &T3, &M3, &S3, &M1);  /* destroy A11, B11 */
     505           0 :   A12 = matslice(A, 1, m1, n1 + 1, n);
     506           0 :   B21 = matslice(B, n1 + 1, n, 1, p1);
     507           0 :   M4 = gen_matmul_i(A12, B21, m1 + 1, n2 + 1, p1 + 1, E, ff);
     508           0 :   if (gc_needed(av, 1))
     509           0 :     (void)gc_all(av, 6, &M2, &T3, &M3, &S3, &M1, &M4);  /* destroy A12, B21 */
     510           0 :   C11 = add_slices(m1, p1, M1, 0, m1, 0, p1, M4, 0, m1, 0, p1, E, ff);
     511           0 :   if (gc_needed(av, 1))
     512           0 :     (void)gc_all(av, 6, &M2, &T3, &M3, &S3, &M1, &C11);  /* destroy M4 */
     513           0 :   M5 = gen_matmul_i(S3, T3, m1 + 1, n1 + 1, p1 + 1, E, ff);
     514           0 :   S4 = subtract_slices(m1, n2, A, 0, m1, n1, n2, S3, 0, m1, 0, n2, E, ff);
     515           0 :   if (gc_needed(av, 1))
     516           0 :     (void)gc_all(av, 7, &M2, &T3, &M3, &M1, &C11, &M5, &S4);  /* destroy S3 */
     517           0 :   T4 = add_slices(n2, p1, B, n1, n2, 0, p1, T3, 0, n2, 0, p1, E, ff);
     518           0 :   if (gc_needed(av, 1))
     519           0 :     (void)gc_all(av, 7, &M2, &M3, &M1, &C11, &M5, &S4, &T4);  /* destroy T3 */
     520           0 :   V1 = subtract_slices(m1, p1, M1, 0, m1, 0, p1, M5, 0, m1, 0, p1, E, ff);
     521           0 :   if (gc_needed(av, 1))
     522           0 :     (void)gc_all(av, 6, &M2, &M3, &S4, &T4, &C11, &V1);  /* destroy M1, M5 */
     523           0 :   B22 = matslice(B, n1 + 1, n, p1 + 1, p);
     524           0 :   M6 = gen_matmul_i(S4, B22, m1 + 1, n2 + 1, p2 + 1, E, ff);
     525           0 :   if (gc_needed(av, 1))
     526           0 :     (void)gc_all(av, 6, &M2, &M3, &T4, &C11, &V1, &M6);  /* destroy S4, B22 */
     527           0 :   A22 = matslice(A, m1 + 1, m, n1 + 1, n);
     528           0 :   M7 = gen_matmul_i(A22, T4, m2 + 1, n2 + 1, p1 + 1, E, ff);
     529           0 :   if (gc_needed(av, 1))
     530           0 :     (void)gc_all(av, 6, &M2, &M3, &C11, &V1, &M6, &M7);  /* destroy A22, T4 */
     531           0 :   V3 = add_slices(m1, p2, V1, 0, m1, 0, p2, M3, 0, m2, 0, p2, E, ff);
     532           0 :   C12 = add_slices(m1, p2, V3, 0, m1, 0, p2, M6, 0, m1, 0, p2, E, ff);
     533           0 :   if (gc_needed(av, 1))
     534           0 :     (void)gc_all(av, 6, &M2, &M3, &C11, &V1, &M7, &C12);  /* destroy V3, M6 */
     535           0 :   V2 = add_slices(m2, p1, V1, 0, m2, 0, p1, M2, 0, m2, 0, p2, E, ff);
     536           0 :   if (gc_needed(av, 1))
     537           0 :     (void)gc_all(av, 5, &M3, &C11, &M7, &C12, &V2);  /* destroy V1, M2 */
     538           0 :   C21 = add_slices(m2, p1, V2, 0, m2, 0, p1, M7, 0, m2, 0, p1, E, ff);
     539           0 :   if (gc_needed(av, 1))
     540           0 :     (void)gc_all(av, 5, &M3, &C11, &C12, &V2, &C21);  /* destroy M7 */
     541           0 :   C22 = add_slices(m2, p2, V2, 0, m2, 0, p2, M3, 0, m2, 0, p2, E, ff);
     542           0 :   if (gc_needed(av, 1))
     543           0 :     (void)gc_all(av, 4, &C11, &C12, &C21, &C22);  /* destroy V2, M3 */
     544           0 :   C = mkmat2(mkcol2(C11, C21), mkcol2(C12, C22));
     545           0 :   return gc_upto(av, matconcat(C));
     546             : }
     547             : 
     548             : /* Strassen-Winograd used for dim >= gen_matmul_sw_bound */
     549             : static const long gen_matmul_sw_bound = 24;
     550             : 
     551             : static GEN
     552       75983 : gen_matmul_i(GEN A, GEN B, long l, long la, long lb,
     553             :              void *E, const struct bb_field *ff)
     554             : {
     555       75983 :   if (l <= gen_matmul_sw_bound
     556           7 :       || la <= gen_matmul_sw_bound
     557           0 :       || lb <= gen_matmul_sw_bound)
     558       75983 :     return gen_matmul_classical(A, B, l, la, lb, E, ff);
     559             :   else
     560           0 :     return gen_matmul_sw(A, B, l - 1, la - 1, lb - 1, E, ff);
     561             : }
     562             : 
     563             : GEN
     564       75983 : gen_matmul(GEN A, GEN B, void *E, const struct bb_field *ff)
     565             : {
     566       75983 :   ulong lgA, lgB = lg(B);
     567       75983 :   if (lgB == 1)
     568           0 :     return cgetg(1, t_MAT);
     569       75983 :   lgA = lg(A);
     570       75983 :   if (lgA != (ulong)lgcols(B))
     571           0 :     pari_err_OP("operation 'gen_matmul'", A, B);
     572       75983 :   if (lgA == 1)
     573           0 :     return zeromat(0, lgB - 1);
     574       75983 :   return gen_matmul_i(A, B, lgcols(A), lgA, lgB, E, ff);
     575             : }
     576             : 
     577             : static GEN
     578       14704 : gen_colneg(GEN x, void *E, const struct bb_field *ff)
     579       59481 : { pari_APPLY_same(ff->neg(E, gel(x,i))); }
     580             : 
     581             : static GEN
     582        2450 : gen_matneg(GEN x, void *E, const struct bb_field *ff)
     583       17084 : { pari_APPLY_same(gen_colneg(gel(x,i), E, ff)); }
     584             : 
     585             : static GEN
     586      310391 : gen_colscalmul(GEN x, GEN b, void *E, const struct bb_field *ff)
     587      734209 : { pari_APPLY_same(ff->red(E, ff->mul(E, gel(x,i), b))); }
     588             : 
     589             : static GEN
     590       47140 : gen_matscalmul(GEN x, GEN b, void *E, const struct bb_field *ff)
     591      357531 : { pari_APPLY_same(gen_colscalmul(gel(x,i), b, E, ff)); }
     592             : 
     593             : static GEN
     594      574403 : gen_colsub(GEN x, GEN y, void *E, const struct bb_field *ff)
     595     2141596 : { pari_APPLY_same(ff->add(E, gel(x,i), ff->neg(E, gel(y,i)))); }
     596             : 
     597             : static GEN
     598       63502 : gen_matsub(GEN x, GEN y, void *E, const struct bb_field *ff)
     599      637905 : { pari_APPLY_same(gen_colsub(gel(x,i), gel(y,i), E, ff)); }
     600             : 
     601             : static GEN
     602       13108 : gen_zerocol(long n, void* data, const struct bb_field *R)
     603       13108 : { return const_col(n, R->s(data, 0)); }
     604             : 
     605             : static GEN
     606       13108 : gen_zeromat(long m, long n, void* data, const struct bb_field *R)
     607             : {
     608       13108 :   GEN M = const_vec(n, gen_zerocol(m, data, R));
     609       13108 :   settyp(M, t_MAT); return M;
     610             : }
     611             : 
     612             : static GEN
     613         154 : gen_colei(long n, long i, void *E, const struct bb_field *S)
     614             : {
     615         154 :   GEN y = cgetg(n+1,t_COL), _0, _1;
     616             :   long j;
     617         154 :   if (n < 0) pari_err_DOMAIN("gen_colei", "dimension","<",gen_0,stoi(n));
     618         154 :   _0 = S->s(E,0);
     619         154 :   _1 = S->s(E,1);
     620        2422 :   for (j=1; j<=n; j++)
     621        2268 :     gel(y, j) = i==j ? _1: _0;
     622         154 :   return y;
     623             : }
     624             : 
     625             : /* assume dim A >= 1, A invertible + upper triangular  */
     626             : static GEN
     627          77 : gen_matinv_upper_ind(GEN A, long index, void *E, const struct bb_field *ff)
     628             : {
     629          77 :   long n = lg(A) - 1, i, j;
     630          77 :   GEN u = cgetg(n + 1, t_COL);
     631         147 :   for (i = n; i > index; i--)
     632          70 :     gel(u, i) = ff->s(E, 0);
     633          77 :   gel(u, i) = ff->inv(E, gcoeff(A, i, i));
     634         147 :   for (i--; i > 0; i--) {
     635          70 :     pari_sp av = avma;
     636          70 :     GEN m = ff->neg(E, ff->mul(E, gcoeff(A, i, i + 1), gel(u, i + 1)));
     637         112 :     for (j = i + 2; j <= n; j++)
     638          42 :       m = ff->add(E, m, ff->neg(E, ff->mul(E, gcoeff(A, i, j), gel(u, j))));
     639          70 :     gel(u, i) = gc_upto(av, ff->red(E, ff->mul(E, m, ff->inv(E, gcoeff(A, i, i)))));
     640             :   }
     641          77 :   return u;
     642             : }
     643             : 
     644             : static GEN
     645          28 : gen_matinv_upper(GEN A, void *E, const struct bb_field *ff)
     646             : {
     647             :   long i, l;
     648          28 :   GEN B = cgetg_copy(A, &l);
     649         105 :   for (i = 1; i < l; i++)
     650          77 :     gel(B,i) = gen_matinv_upper_ind(A, i, E, ff);
     651          28 :   return B;
     652             : }
     653             : 
     654             : /* find z such that A z = y. Return NULL if no solution */
     655             : GEN
     656           0 : gen_matcolinvimage(GEN A, GEN y, void *E, const struct bb_field *ff)
     657             : {
     658           0 :   pari_sp av = avma;
     659           0 :   long i, l = lg(A);
     660             :   GEN M, x, t;
     661             : 
     662           0 :   M = gen_ker(shallowconcat(A, y), 0, E, ff);
     663           0 :   i = lg(M) - 1;
     664           0 :   if (!i) return gc_NULL(av);
     665             : 
     666           0 :   x = gel(M, i);
     667           0 :   t = gel(x, l);
     668           0 :   if (ff->equal0(t)) return gc_NULL(av);
     669             : 
     670           0 :   t = ff->neg(E, ff->inv(E, t));
     671           0 :   setlg(x, l);
     672           0 :   for (i = 1; i < l; i++)
     673           0 :     gel(x, i) = ff->red(E, ff->mul(E, t, gel(x, i)));
     674           0 :   return gc_GEN(av, x);
     675             : }
     676             : 
     677             : /* find Z such that A Z = B. Return NULL if no solution */
     678             : GEN
     679          77 : gen_matinvimage(GEN A, GEN B, void *E, const struct bb_field *ff)
     680             : {
     681          77 :   pari_sp av = avma;
     682             :   GEN d, x, X, Y;
     683             :   long i, j, nY, nA, nB;
     684          77 :   x = gen_ker(shallowconcat(gen_matneg(A, E, ff), B), 0, E, ff);
     685             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
     686             :    * We must find T such that Y T = Id_nB then X T = Z. This exists
     687             :    * iff Y has at least nB columns and full rank. */
     688          77 :   nY = lg(x) - 1;
     689          77 :   nB = lg(B) - 1;
     690          77 :   if (nY < nB) return gc_NULL(av);
     691          77 :   nA = lg(A) - 1;
     692          77 :   Y = rowslice(x, nA + 1, nA + nB); /* nB rows */
     693          77 :   d = cgetg(nB + 1, t_VECSMALL);
     694         182 :   for (i = nB, j = nY; i >= 1; i--, j--) {
     695         224 :     for (; j >= 1; j--)
     696         175 :       if (!ff->equal0(gcoeff(Y, i, j))) { d[i] = j; break; }
     697         154 :     if (!j) return gc_NULL(av);
     698             :   }
     699             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
     700          28 :   Y = vecpermute(Y, d);
     701          28 :   x = vecpermute(x, d);
     702          28 :   X = rowslice(x, 1, nA);
     703          28 :   return gc_upto(av, gen_matmul(X, gen_matinv_upper(Y, E, ff), E, ff));
     704             : }
     705             : 
     706             : static GEN
     707      371507 : image_from_pivot(GEN x, GEN d, long r)
     708             : {
     709             :   GEN y;
     710             :   long j, k;
     711             : 
     712      371507 :   if (!d) return gcopy(x);
     713             :   /* d left on stack for efficiency */
     714      365780 :   r = lg(x)-1 - r; /* = dim Im(x) */
     715      365780 :   y = cgetg(r+1,t_MAT);
     716     2098642 :   for (j=k=1; j<=r; k++)
     717     1732862 :     if (d[k]) gel(y,j++) = gcopy(gel(x,k));
     718      365780 :   return y;
     719             : }
     720             : 
     721             : /* r = dim Ker x, n = nbrows(x) */
     722             : static GEN
     723      267809 : get_suppl(GEN x, GEN d, long n, long r, GEN(*ei)(long,long))
     724             : {
     725             :   pari_sp av;
     726             :   GEN y, c;
     727      267809 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
     728             : 
     729      267809 :   if (rx == n && r == 0) return gcopy(x);
     730      197400 :   y = cgetg(n+1, t_MAT);
     731      197401 :   av = avma; c = zero_zv(n);
     732             :   /* c = lines containing pivots (could get it from RgM_pivots, but cheap)
     733             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
     734      836184 :   for (k = j = 1; j<=rx; j++)
     735      638783 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gel(x,j); }
     736     1198259 :   for (j=1; j<=n; j++)
     737     1000858 :     if (!c[j]) gel(y,k++) = (GEN)j; /* HACK */
     738      197401 :   set_avma(av);
     739             : 
     740      197400 :   rx -= r;
     741      836109 :   for (j=1; j<=rx; j++) gel(y,j) = gcopy(gel(y,j));
     742      559541 :   for (   ; j<=n; j++)  gel(y,j) = ei(n, y[j]);
     743      197400 :   return y;
     744             : }
     745             : 
     746             : /* n = dim x, r = dim Ker(x), d from RgM_pivots */
     747             : static GEN
     748     1941203 : indexrank0(long n, long r, GEN d)
     749             : {
     750     1941203 :   GEN p1, p2, res = cgetg(3,t_VEC);
     751             :   long i, j;
     752             : 
     753     1941203 :   r = n - r; /* now r = dim Im(x) */
     754     1941203 :   p1 = cgetg(r+1,t_VECSMALL); gel(res,1) = p1;
     755     1941203 :   p2 = cgetg(r+1,t_VECSMALL); gel(res,2) = p2;
     756     1941203 :   if (d)
     757             :   {
     758     7832956 :     for (i=0,j=1; j<=n; j++)
     759     5895260 :       if (d[j]) { i++; p1[i] = d[j]; p2[i] = j; }
     760     1937696 :     vecsmall_sort(p1);
     761             :   }
     762     1941210 :   return res;
     763             : }
     764             : 
     765             : /*******************************************************************/
     766             : /*                                                                 */
     767             : /*                Echelon form and CUP decomposition               */
     768             : /*                                                                 */
     769             : /*******************************************************************/
     770             : 
     771             : /* By Peter Bruin, based on
     772             :   C.-P. Jeannerod, C. Pernet and A. Storjohann, Rank-profile revealing
     773             :   Gaussian elimination and the CUP matrix decomposition.  J. Symbolic
     774             :   Comput. 56 (2013), 46-68.
     775             : 
     776             :   Decompose an m x n-matrix A of rank r as C*U*P, with
     777             :   - C: m x r-matrix in column echelon form (not necessarily reduced)
     778             :        with all pivots equal to 1
     779             :   - U: upper-triangular r x n-matrix
     780             :   - P: permutation matrix
     781             :   The pivots of C and the known zeroes in C and U are not necessarily
     782             :   filled in; instead, we also return the vector R of pivot rows.
     783             :   Instead of the matrix P, we return the permutation p of [1..n]
     784             :   (t_VECSMALL) such that P[i,j] = 1 if and only if j = p[i].
     785             : */
     786             : 
     787             : /* complement of a strictly increasing subsequence of (1, 2, ..., n) */
     788             : static GEN
     789       12199 : indexcompl(GEN v, long n)
     790             : {
     791       12199 :   long i, j, k, m = lg(v) - 1;
     792       12199 :   GEN w = cgetg(n - m + 1, t_VECSMALL);
     793      127236 :   for (i = j = k = 1; i <= n; i++)
     794      115037 :     if (j <= m && v[j] == i) j++; else w[k++] = i;
     795       12199 :   return w;
     796             : }
     797             : 
     798             : static GEN
     799        4035 : gen_solve_upper_1(GEN U, GEN B, void *E, const struct bb_field *ff)
     800        4035 : { return gen_matscalmul(B, ff->inv(E, gcoeff(U, 1, 1)), E, ff); }
     801             : 
     802             : static GEN
     803        2256 : gen_rsolve_upper_2(GEN U, GEN B, void *E, const struct bb_field *ff)
     804             : {
     805        2256 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     806        2256 :   GEN D = ff->red(E, ff->mul(E, a, d)), Dinv = ff->inv(E, D);
     807        2256 :   GEN ainv = ff->red(E, ff->mul(E, d, Dinv));
     808        2256 :   GEN dinv = ff->red(E, ff->mul(E, a, Dinv));
     809        2256 :   GEN B1 = rowslice(B, 1, 1);
     810        2256 :   GEN B2 = rowslice(B, 2, 2);
     811        2256 :   GEN X2 = gen_matscalmul(B2, dinv, E, ff);
     812        2256 :   GEN X1 = gen_matscalmul(gen_matsub(B1, gen_matscalmul(X2, b, E, ff), E, ff),
     813             :                           ainv, E, ff);
     814        2256 :   return vconcat(X1, X2);
     815             : }
     816             : 
     817             : /* solve U*X = B,  U upper triangular and invertible */
     818             : static GEN
     819        5840 : gen_rsolve_upper(GEN U, GEN B, void *E, const struct bb_field *ff,
     820             :                  GEN (*mul)(void *E, GEN a, GEN))
     821             : {
     822        5840 :   long n = lg(U) - 1, n1;
     823             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     824        5840 :   pari_sp av = avma;
     825             : 
     826        5840 :   if (n == 0) return B;
     827        5840 :   if (n == 1) return gen_solve_upper_1(U, B, E, ff);
     828        4914 :   if (n == 2) return gen_rsolve_upper_2(U, B, E, ff);
     829        2658 :   n1 = (n + 1)/2;
     830        2658 :   U2 = vecslice(U, n1 + 1, n);
     831        2658 :   U11 = matslice(U, 1,n1, 1,n1);
     832        2658 :   U12 = rowslice(U2, 1, n1);
     833        2658 :   U22 = rowslice(U2, n1 + 1, n);
     834        2658 :   B1 = rowslice(B, 1, n1);
     835        2658 :   B2 = rowslice(B, n1 + 1, n);
     836        2658 :   X2 = gen_rsolve_upper(U22, B2, E, ff, mul);
     837        2658 :   B1 = gen_matsub(B1, mul(E, U12, X2), E, ff);
     838        2658 :   if (gc_needed(av, 1)) (void)gc_all(av, 3, &B1, &U11, &X2);
     839        2658 :   X1 = gen_rsolve_upper(U11, B1, E, ff, mul);
     840        2658 :   X = vconcat(X1, X2);
     841        2658 :   if (gc_needed(av, 1)) X = gc_GEN(av, X);
     842        2658 :   return X;
     843             : }
     844             : 
     845             : static GEN
     846        5894 : gen_lsolve_upper_2(GEN U, GEN B, void *E, const struct bb_field *ff)
     847             : {
     848        5894 :   GEN a = gcoeff(U, 1, 1), b = gcoeff(U, 1, 2), d = gcoeff(U, 2, 2);
     849        5894 :   GEN D = ff->red(E, ff->mul(E, a, d)), Dinv = ff->inv(E, D);
     850        5894 :   GEN ainv = ff->red(E, ff->mul(E, d, Dinv)), dinv = ff->red(E, ff->mul(E, a, Dinv));
     851        5894 :   GEN B1 = vecslice(B, 1, 1);
     852        5894 :   GEN B2 = vecslice(B, 2, 2);
     853        5894 :   GEN X1 = gen_matscalmul(B1, ainv, E, ff);
     854        5894 :   GEN X2 = gen_matscalmul(gen_matsub(B2, gen_matscalmul(X1, b, E, ff), E, ff), dinv, E, ff);
     855        5894 :   return shallowconcat(X1, X2);
     856             : }
     857             : 
     858             : /* solve X*U = B,  U upper triangular and invertible */
     859             : static GEN
     860       13882 : gen_lsolve_upper(GEN U, GEN B, void *E, const struct bb_field *ff,
     861             :                  GEN (*mul)(void *E, GEN a, GEN))
     862             : {
     863       13882 :   long n = lg(U) - 1, n1;
     864             :   GEN U2, U11, U12, U22, B1, B2, X1, X2, X;
     865       13882 :   pari_sp av = avma;
     866             : 
     867       13882 :   if (n == 0) return B;
     868       13882 :   if (n == 1) return gen_solve_upper_1(U, B, E, ff);
     869       10773 :   if (n == 2) return gen_lsolve_upper_2(U, B, E, ff);
     870        4879 :   n1 = (n + 1)/2;
     871        4879 :   U2 = vecslice(U, n1 + 1, n);
     872        4879 :   U11 = matslice(U, 1,n1, 1,n1);
     873        4879 :   U12 = rowslice(U2, 1, n1);
     874        4879 :   U22 = rowslice(U2, n1 + 1, n);
     875        4879 :   B1 = vecslice(B, 1, n1);
     876        4879 :   B2 = vecslice(B, n1 + 1, n);
     877        4879 :   X1 = gen_lsolve_upper(U11, B1, E, ff, mul);
     878        4879 :   B2 = gen_matsub(B2, mul(E, X1, U12), E, ff);
     879        4879 :   if (gc_needed(av, 1)) (void)gc_all(av, 3, &B2, &U22, &X1);
     880        4879 :   X2 = gen_lsolve_upper(U22, B2, E, ff, mul);
     881        4879 :   X = shallowconcat(X1, X2);
     882        4879 :   if (gc_needed(av, 1)) X = gc_GEN(av, X);
     883        4879 :   return X;
     884             : }
     885             : 
     886             : static GEN
     887       12590 : gen_rsolve_lower_unit_2(GEN L, GEN A, void *E, const struct bb_field *ff)
     888             : {
     889       12590 :   GEN X1 = rowslice(A, 1, 1);
     890       12590 :   GEN X2 = gen_matsub(rowslice(A, 2, 2), gen_matscalmul(X1, gcoeff(L, 2, 1), E, ff), E, ff);
     891       12590 :   return vconcat(X1, X2);
     892             : }
     893             : 
     894             : /* solve L*X = A,  L lower triangular with ones on the diagonal
     895             :  * (at least as many rows as columns) */
     896             : static GEN
     897       30422 : gen_rsolve_lower_unit(GEN L, GEN A, void *E, const struct bb_field *ff,
     898             :                       GEN (*mul)(void *E, GEN a, GEN))
     899             : {
     900       30422 :   long m = lg(L) - 1, m1, n;
     901             :   GEN L1, L11, L21, L22, A1, A2, X1, X2, X;
     902       30422 :   pari_sp av = avma;
     903             : 
     904       30422 :   if (m == 0) return zeromat(0, lg(A) - 1);
     905       30422 :   if (m == 1) return rowslice(A, 1, 1);
     906       24201 :   if (m == 2) return gen_rsolve_lower_unit_2(L, A, E, ff);
     907       11611 :   m1 = (m + 1)/2;
     908       11611 :   n = nbrows(L);
     909       11611 :   L1 = vecslice(L, 1, m1);
     910       11611 :   L11 = rowslice(L1, 1, m1);
     911       11611 :   L21 = rowslice(L1, m1 + 1, n);
     912       11611 :   A1 = rowslice(A, 1, m1);
     913       11611 :   X1 = gen_rsolve_lower_unit(L11, A1, E, ff, mul);
     914       11611 :   A2 = rowslice(A, m1 + 1, n);
     915       11611 :   A2 = gen_matsub(A2, mul(E, L21, X1), E, ff);
     916       11611 :   if (gc_needed(av, 1)) (void)gc_all(av, 2, &A2, &X1);
     917       11611 :   L22 = matslice(L, m1+1,n, m1+1,m);
     918       11611 :   X2 = gen_rsolve_lower_unit(L22, A2, E, ff, mul);
     919       11611 :   X = vconcat(X1, X2);
     920       11611 :   if (gc_needed(av, 1)) X = gc_GEN(av, X);
     921       11611 :   return X;
     922             : }
     923             : 
     924             : static GEN
     925        6065 : gen_lsolve_lower_unit_2(GEN L, GEN A, void *E, const struct bb_field *ff)
     926             : {
     927        6065 :   GEN X2 = vecslice(A, 2, 2);
     928        6065 :   GEN X1 = gen_matsub(vecslice(A, 1, 1),
     929        6065 :                     gen_matscalmul(X2, gcoeff(L, 2, 1), E, ff), E, ff);
     930        6065 :   return shallowconcat(X1, X2);
     931             : }
     932             : 
     933             : /* solve L*X = A,  L lower triangular with ones on the diagonal
     934             :  * (at least as many rows as columns) */
     935             : static GEN
     936       16025 : gen_lsolve_lower_unit(GEN L, GEN A, void *E, const struct bb_field *ff,
     937             :                       GEN (*mul)(void *E, GEN a, GEN))
     938             : {
     939       16025 :   long m = lg(L) - 1, m1;
     940             :   GEN L1, L2, L11, L21, L22, A1, A2, X1, X2, X;
     941       16025 :   pari_sp av = avma;
     942             : 
     943       16025 :   if (m <= 1) return A;
     944       12856 :   if (m == 2) return gen_lsolve_lower_unit_2(L, A, E, ff);
     945        6791 :   m1 = (m + 1)/2;
     946        6791 :   L2 = vecslice(L, m1 + 1, m);
     947        6791 :   L22 = rowslice(L2, m1 + 1, m);
     948        6791 :   A2 = vecslice(A, m1 + 1, m);
     949        6791 :   X2 = gen_lsolve_lower_unit(L22, A2, E, ff, mul);
     950        6791 :   if (gc_needed(av, 1)) X2 = gc_GEN(av, X2);
     951        6791 :   L1 = vecslice(L, 1, m1);
     952        6791 :   L21 = rowslice(L1, m1 + 1, m);
     953        6791 :   A1 = vecslice(A, 1, m1);
     954        6791 :   A1 = gen_matsub(A1, mul(E, X2, L21), E, ff);
     955        6791 :   L11 = rowslice(L1, 1, m1);
     956        6791 :   if (gc_needed(av, 1)) (void)gc_all(av, 3, &A1, &L11, &X2);
     957        6791 :   X1 = gen_lsolve_lower_unit(L11, A1, E, ff, mul);
     958        6791 :   X = shallowconcat(X1, X2);
     959        6791 :   if (gc_needed(av, 1)) X = gc_GEN(av, X);
     960        6791 :   return X;
     961             : }
     962             : 
     963             : /* destroy A */
     964             : static long
     965       16007 : gen_CUP_basecase(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, void *E, const struct bb_field *ff)
     966             : {
     967       16007 :   long i, j, k, m = nbrows(A), n = lg(A) - 1, pr, pc;
     968             :   pari_sp av;
     969             :   GEN u, v;
     970             : 
     971       16007 :   if (P) *P = identity_perm(n);
     972       16007 :   *R = cgetg(m + 1, t_VECSMALL);
     973       16007 :   av = avma;
     974       45918 :   for (j = 1, pr = 0; j <= n; j++)
     975             :   {
     976      104382 :     for (pr++, pc = 0; pr <= m; pr++)
     977             :     {
     978      544100 :       for (k = j; k <= n; k++)
     979             :       {
     980      451722 :         v = ff->red(E, gcoeff(A, pr, k));
     981      451722 :         gcoeff(A, pr, k) = v;
     982      451722 :         if (!pc && !ff->equal0(v)) pc = k;
     983             :       }
     984       92378 :       if (pc) break;
     985             :     }
     986       41915 :     if (!pc) break;
     987       29911 :     (*R)[j] = pr;
     988       29911 :     if (pc != j)
     989             :     {
     990        4258 :       swap(gel(A, j), gel(A, pc));
     991        4258 :       if (P) lswap((*P)[j], (*P)[pc]);
     992             :     }
     993       29911 :     u = ff->inv(E, gcoeff(A, pr, j));
     994      154973 :     for (i = pr + 1; i <= m; i++)
     995             :     {
     996      125062 :       v = ff->red(E, ff->mul(E, gcoeff(A, i, j), u));
     997      125062 :       gcoeff(A, i, j) = v;
     998      125062 :       v = ff->neg(E, v);
     999      413234 :       for (k = j + 1; k <= n; k++)
    1000      288172 :         gcoeff(A, i, k) = ff->add(E, gcoeff(A, i, k),
    1001      288172 :                                   ff->red(E, ff->mul(E, gcoeff(A, pr, k), v)));
    1002             :     }
    1003       29911 :     if (gc_needed(av, 2)) A = gc_GEN(av, A);
    1004             :   }
    1005       16007 :   setlg(*R, j);
    1006       16007 :   *C = vecslice(A, 1, j - 1);
    1007       16007 :   if (U) *U = rowpermute(A, *R);
    1008       16007 :   return j - 1;
    1009             : }
    1010             : 
    1011             : static const long gen_CUP_LIMIT = 5;
    1012             : 
    1013             : static long
    1014       10598 : gen_CUP(GEN A, GEN *R, GEN *C, GEN *U, GEN *P, void *E, const struct bb_field *ff,
    1015             :         GEN (*mul)(void *E, GEN a, GEN))
    1016             : {
    1017       10598 :   long m = nbrows(A), m1, n = lg(A) - 1, i, r1, r2, r;
    1018             :   GEN R1, C1, U1, P1, R2, C2, U2, P2;
    1019             :   GEN A1, A2, B2, C21, U11, U12, T21, T22;
    1020       10598 :   pari_sp av = avma;
    1021             : 
    1022       10598 :   if (m < gen_CUP_LIMIT || n < gen_CUP_LIMIT)
    1023             :     /* destroy A; not called at the outermost recursion level */
    1024        5985 :     return gen_CUP_basecase(A, R, C, U, P, E, ff);
    1025        4613 :   m1 = (minss(m, n) + 1)/2;
    1026        4613 :   A1 = rowslice(A, 1, m1);
    1027        4613 :   A2 = rowslice(A, m1 + 1, m);
    1028        4613 :   r1 = gen_CUP(A1, &R1, &C1, &U1, &P1, E, ff, mul);
    1029        4613 :   if (r1 == 0)
    1030             :   {
    1031         489 :     r2 = gen_CUP(A2, &R2, &C2, &U2, &P2, E, ff, mul);
    1032         489 :     *R = cgetg(r2 + 1, t_VECSMALL);
    1033         798 :     for (i = 1; i <= r2; i++) (*R)[i] = R2[i] + m1;
    1034         489 :     *C = vconcat(gen_zeromat(m1, r2, E, ff), C2);
    1035         489 :     *U = U2;
    1036         489 :     *P = P2;
    1037         489 :     r = r2;
    1038             :   }
    1039             :   else
    1040             :   {
    1041        4124 :     U11 = vecslice(U1, 1, r1);
    1042        4124 :     U12 = vecslice(U1, r1 + 1, n);
    1043        4124 :     T21 = vecslicepermute(A2, P1, 1, r1);
    1044        4124 :     T22 = vecslicepermute(A2, P1, r1 + 1, n);
    1045        4124 :     C21 = gen_lsolve_upper(U11, T21, E, ff, mul);
    1046        4124 :     if (gc_needed(av, 1))
    1047           0 :       (void)gc_all(av, 7, &R1, &C1, &P1, &U11, &U12, &T22, &C21);
    1048        4124 :     B2 = gen_matsub(T22, mul(E, C21, U12), E, ff);
    1049        4124 :     r2 = gen_CUP(B2, &R2, &C2, &U2, &P2, E, ff, mul);
    1050        4124 :     r = r1 + r2;
    1051        4124 :     *R = cgetg(r + 1, t_VECSMALL);
    1052       19021 :     for (i = 1; i <= r1; i++) (*R)[i] = R1[i];
    1053       19879 :     for (     ; i <= r; i++)  (*R)[i] = R2[i - r1] + m1;
    1054        4124 :     *C = shallowconcat(vconcat(C1, C21),
    1055             :                        vconcat(gen_zeromat(m1, r2, E, ff), C2));
    1056        4124 :     *U = shallowconcat(vconcat(U11, gen_zeromat(r2, r1, E, ff)),
    1057             :                        vconcat(vecpermute(U12, P2), U2));
    1058             : 
    1059        4124 :     *P = cgetg(n + 1, t_VECSMALL);
    1060       19021 :     for (i = 1; i <= r1; i++) (*P)[i] = P1[i];
    1061       49559 :     for (     ; i <= n; i++)  (*P)[i] = P1[P2[i - r1] + r1];
    1062             :   }
    1063        4613 :   if (gc_needed(av, 1)) (void)gc_all(av, 4, R, C, U, P);
    1064        4613 :   return r;
    1065             : }
    1066             : 
    1067             : /* column echelon form */
    1068             : static long
    1069       17685 : gen_echelon(GEN A, GEN *R, GEN *C, void *E, const struct bb_field *ff,
    1070             :             GEN (*mul)(void*, GEN, GEN))
    1071             : {
    1072       17685 :   long j, j1, j2, m = nbrows(A), n = lg(A) - 1, n1, r, r1, r2;
    1073             :   GEN A1, A2, R1, R1c, C1, R2, C2;
    1074             :   GEN A12, A22, B2, C11, C21, M12;
    1075       17685 :   pari_sp av = avma;
    1076             : 
    1077       17685 :   if (m < gen_CUP_LIMIT || n < gen_CUP_LIMIT)
    1078       10022 :     return gen_CUP_basecase(shallowcopy(A), R, C, NULL, NULL, E, ff);
    1079             : 
    1080        7663 :   n1 = (n + 1)/2;
    1081        7663 :   A1 = vecslice(A, 1, n1);
    1082        7663 :   A2 = vecslice(A, n1 + 1, n);
    1083        7663 :   r1 = gen_echelon(A1, &R1, &C1, E, ff, mul);
    1084        7663 :   if (!r1) return gen_echelon(A2, R, C, E, ff, mul);
    1085        6781 :   if (r1 == m) { *R = R1; *C = C1; return r1; }
    1086        6634 :   R1c = indexcompl(R1, m);
    1087        6634 :   C11 = rowpermute(C1, R1);
    1088        6634 :   C21 = rowpermute(C1, R1c);
    1089        6634 :   A12 = rowpermute(A2, R1);
    1090        6634 :   A22 = rowpermute(A2, R1c);
    1091        6634 :   M12 = gen_rsolve_lower_unit(C11, A12, E, ff, mul);
    1092        6634 :   B2 = gen_matsub(A22, mul(E, C21, M12), E, ff);
    1093        6634 :   r2 = gen_echelon(B2, &R2, &C2, E, ff, mul);
    1094        6634 :   if (!r2) { *R = R1; *C = C1; r = r1; }
    1095             :   else
    1096             :   {
    1097        4350 :     R2 = perm_mul(R1c, R2);
    1098        4350 :     C2 = rowpermute(vconcat(gen_zeromat(r1, r2, E, ff), C2),
    1099             :                     perm_inv(vecsmall_concat(R1, R1c)));
    1100        4350 :     r = r1 + r2;
    1101        4350 :     *R = cgetg(r + 1, t_VECSMALL);
    1102        4350 :     *C = cgetg(r + 1, t_MAT);
    1103       33176 :     for (j = j1 = j2 = 1; j <= r; j++)
    1104       28826 :       if (j2 > r2 || (j1 <= r1 && R1[j1] < R2[j2]))
    1105             :       {
    1106       16362 :         gel(*C, j) = gel(C1, j1);
    1107       16362 :         (*R)[j] = R1[j1++];
    1108             :       }
    1109             :       else
    1110             :       {
    1111       12464 :         gel(*C, j) = gel(C2, j2);
    1112       12464 :         (*R)[j] = R2[j2++];
    1113             :       }
    1114             :   }
    1115        6634 :   if (gc_needed(av, 1)) (void)gc_all(av, 2, R, C);
    1116        6634 :   return r;
    1117             : }
    1118             : 
    1119             : static GEN
    1120         610 : gen_pivots_CUP(GEN x, long *rr, void *E, const struct bb_field *ff,
    1121             :                GEN (*mul)(void*, GEN, GEN))
    1122             : {
    1123             :   pari_sp av;
    1124         610 :   long i, n = lg(x) - 1, r;
    1125         610 :   GEN R, C, U, P, d = zero_zv(n);
    1126         610 :   av = avma;
    1127         610 :   r = gen_CUP(x, &R, &C, &U, &P, E, ff, mul);
    1128        5157 :   for(i = 1; i <= r; i++)
    1129        4547 :     d[P[i]] = R[i];
    1130         610 :   set_avma(av);
    1131         610 :   *rr = n - r;
    1132         610 :   return d;
    1133             : }
    1134             : 
    1135             : static GEN
    1136         140 : gen_det_CUP(GEN a, void *E, const struct bb_field *ff,
    1137             :             GEN (*mul)(void*, GEN, GEN))
    1138             : {
    1139         140 :   pari_sp av = avma;
    1140             :   GEN R, C, U, P, d;
    1141         140 :   long i, n = lg(a) - 1, r;
    1142         140 :   r = gen_CUP(a, &R, &C, &U, &P, E, ff, mul);
    1143         140 :   if (r < n)
    1144           0 :     d = ff->s(E, 0);
    1145             :   else {
    1146         140 :     d = ff->s(E, perm_sign(P) == 1 ? 1: - 1);
    1147        2730 :     for (i = 1; i <= n; i++)
    1148        2590 :       d = ff->red(E, ff->mul(E, d, gcoeff(U, i, i)));
    1149             :   }
    1150         140 :   return gc_upto(av, d);
    1151             : }
    1152             : 
    1153             : static long
    1154          35 : gen_matrank(GEN x, void *E, const struct bb_field *ff,
    1155             :             GEN (*mul)(void*, GEN, GEN))
    1156             : {
    1157          35 :   pari_sp av = avma;
    1158             :   long r;
    1159          35 :   if (lg(x) - 1 >= gen_CUP_LIMIT && nbrows(x) >= gen_CUP_LIMIT)
    1160             :   {
    1161             :     GEN R, C;
    1162          28 :     return gc_long(av, gen_echelon(x, &R, &C, E, ff, mul));
    1163             :   }
    1164           7 :   (void) gen_Gauss_pivot(x, &r, E, ff);
    1165           7 :   return gc_long(av, lg(x)-1 - r);
    1166             : }
    1167             : 
    1168             : static GEN
    1169          63 : gen_invimage_CUP(GEN A, GEN B, void *E, const struct bb_field *ff,
    1170             :                  GEN (*mul)(void*, GEN, GEN))
    1171             : {
    1172          63 :   pari_sp av = avma;
    1173             :   GEN R, Rc, C, U, P, B1, B2, C1, C2, X, Y, Z;
    1174          63 :   long r = gen_CUP(A, &R, &C, &U, &P, E, ff, mul);
    1175          63 :   Rc = indexcompl(R, nbrows(B));
    1176          63 :   C1 = rowpermute(C, R);
    1177          63 :   C2 = rowpermute(C, Rc);
    1178          63 :   B1 = rowpermute(B, R);
    1179          63 :   B2 = rowpermute(B, Rc);
    1180          63 :   Z = gen_rsolve_lower_unit(C1, B1, E, ff, mul);
    1181          63 :   if (!gequal(mul(E, C2, Z), B2))
    1182          42 :     return NULL;
    1183          21 :   Y = vconcat(gen_rsolve_upper(vecslice(U, 1, r), Z, E, ff, mul),
    1184          21 :               gen_zeromat(lg(A) - 1 - r, lg(B) - 1, E, ff));
    1185          21 :   X = rowpermute(Y, perm_inv(P));
    1186          21 :   return gc_GEN(av, X);
    1187             : }
    1188             : 
    1189             : static GEN
    1190        2373 : gen_ker_echelon(GEN x, void *E, const struct bb_field *ff,
    1191             :                 GEN (*mul)(void*, GEN, GEN))
    1192             : {
    1193        2373 :   pari_sp av = avma;
    1194             :   GEN R, Rc, C, C1, C2, S, K;
    1195        2373 :   long n = lg(x) - 1, r;
    1196        2373 :   r = gen_echelon(shallowtrans(x), &R, &C, E, ff, mul);
    1197        2373 :   Rc = indexcompl(R, n);
    1198        2373 :   C1 = rowpermute(C, R);
    1199        2373 :   C2 = rowpermute(C, Rc);
    1200        2373 :   S = gen_lsolve_lower_unit(C1, C2, E, ff, mul);
    1201        2373 :   K = vecpermute(shallowconcat(gen_matneg(S, E, ff), gen_matid(n - r, E, ff)),
    1202             :                  perm_inv(vecsmall_concat(R, Rc)));
    1203        2373 :   K = shallowtrans(K);
    1204        2373 :   return gc_GEN(av, K);
    1205             : }
    1206             : 
    1207             : static GEN
    1208         105 : gen_deplin_echelon(GEN x, void *E, const struct bb_field *ff,
    1209             :                    GEN (*mul)(void*, GEN, GEN))
    1210             : {
    1211         105 :   pari_sp av = avma;
    1212             :   GEN R, Rc, C, C1, C2, s, v;
    1213         105 :   long i, n = lg(x) - 1, r;
    1214         105 :   r = gen_echelon(shallowtrans(x), &R, &C, E, ff, mul);
    1215         105 :   if (r == n) return gc_NULL(av);
    1216          70 :   Rc = indexcompl(R, n);
    1217          70 :   i = Rc[1];
    1218          70 :   C1 = rowpermute(C, R);
    1219          70 :   C2 = rowslice(C, i, i);
    1220          70 :   s = row(gen_lsolve_lower_unit(C1, C2, E, ff, mul), 1);
    1221          70 :   settyp(s, t_COL);
    1222          70 :   v = vecpermute(shallowconcat(gen_colneg(s, E, ff), gen_colei(n - r, 1, E, ff)),
    1223             :                  perm_inv(vecsmall_concat(R, Rc)));
    1224          70 :   return gc_GEN(av, v);
    1225             : }
    1226             : 
    1227             : static GEN
    1228         559 : gen_gauss_CUP(GEN a, GEN b, void *E, const struct bb_field *ff,
    1229             :               GEN (*mul)(void*, GEN, GEN))
    1230             : {
    1231             :   GEN R, C, U, P, Y;
    1232         559 :   long n = lg(a) - 1, r;
    1233         559 :   if (nbrows(a) < n || (r = gen_CUP(a, &R, &C, &U, &P, E, ff, mul)) < n)
    1234          56 :     return NULL;
    1235         503 :   Y = gen_rsolve_lower_unit(rowpermute(C, R), rowpermute(b, R), E, ff, mul);
    1236         503 :   return rowpermute(gen_rsolve_upper(U, Y, E, ff, mul), perm_inv(P));
    1237             : }
    1238             : 
    1239             : static GEN
    1240        3036 : gen_gauss(GEN a, GEN b, void *E, const struct bb_field *ff,
    1241             :           GEN (*mul)(void*, GEN, GEN))
    1242             : {
    1243        3036 :   if (lg(a) - 1 >= gen_CUP_LIMIT)
    1244         559 :     return gen_gauss_CUP(a, b, E, ff, mul);
    1245        2477 :   return gen_Gauss(a, b, E, ff);
    1246             : }
    1247             : 
    1248             : static GEN
    1249        3672 : gen_ker_i(GEN x, long deplin, void *E, const struct bb_field *ff,
    1250             :           GEN (*mul)(void*, GEN, GEN)) {
    1251        3672 :   if (lg(x) - 1 >= gen_CUP_LIMIT && nbrows(x) >= gen_CUP_LIMIT)
    1252        2478 :     return deplin? gen_deplin_echelon(x, E, ff, mul): gen_ker_echelon(x, E, ff, mul);
    1253        1194 :   return gen_ker(x, deplin, E, ff);
    1254             : }
    1255             : 
    1256             : static GEN
    1257         140 : gen_invimage(GEN A, GEN B, void *E, const struct bb_field *ff,
    1258             :              GEN (*mul)(void*, GEN, GEN))
    1259             : {
    1260         140 :   long nA = lg(A)-1, nB = lg(B)-1;
    1261             : 
    1262         140 :   if (!nB) return cgetg(1, t_MAT);
    1263         140 :   if (nA + nB >= gen_CUP_LIMIT && nbrows(B) >= gen_CUP_LIMIT)
    1264          63 :     return gen_invimage_CUP(A, B, E, ff, mul);
    1265          77 :   return gen_matinvimage(A, B, E, ff);
    1266             : }
    1267             : 
    1268             : /* find z such that A z = y. Return NULL if no solution */
    1269             : static GEN
    1270          71 : gen_matcolinvimage_i(GEN A, GEN y, void *E, const struct bb_field *ff,
    1271             :                      GEN (*mul)(void*, GEN, GEN))
    1272             : {
    1273          71 :   pari_sp av = avma;
    1274          71 :   long i, l = lg(A);
    1275             :   GEN M, x, t;
    1276             : 
    1277          71 :   M = gen_ker_i(shallowconcat(A, y), 0, E, ff, mul);
    1278          71 :   i = lg(M) - 1;
    1279          71 :   if (!i) return gc_NULL(av);
    1280             : 
    1281          71 :   x = gel(M, i);
    1282          71 :   t = gel(x, l);
    1283          71 :   if (ff->equal0(t)) return gc_NULL(av);
    1284             : 
    1285          50 :   t = ff->neg(E, ff->inv(E, t));
    1286          50 :   setlg(x, l);
    1287         178 :   for (i = 1; i < l; i++)
    1288         128 :     gel(x, i) = ff->red(E, ff->mul(E, t, gel(x, i)));
    1289          50 :   return gc_GEN(av, x);
    1290             : }
    1291             : 
    1292             : static GEN
    1293         420 : gen_det_i(GEN a, void *E, const struct bb_field *ff,
    1294             :           GEN (*mul)(void*, GEN, GEN))
    1295             : {
    1296         420 :   if (lg(a) - 1 >= gen_CUP_LIMIT)
    1297         140 :     return gen_det_CUP(a, E, ff, mul);
    1298             :   else
    1299         280 :     return gen_det(a, E, ff);
    1300             : }
    1301             : 
    1302             : static GEN
    1303        1722 : gen_pivots(GEN x, long *rr, void *E, const struct bb_field *ff,
    1304             :            GEN (*mul)(void*, GEN, GEN))
    1305             : {
    1306        1722 :   if (lg(x) - 1 >= gen_CUP_LIMIT && nbrows(x) >= gen_CUP_LIMIT)
    1307         610 :     return gen_pivots_CUP(x, rr, E, ff, mul);
    1308        1112 :   return gen_Gauss_pivot(x, rr, E, ff);
    1309             : }
    1310             : 
    1311             : /* r = dim Ker x, n = nbrows(x) */
    1312             : static GEN
    1313          21 : gen_get_suppl(GEN x, GEN d, long n, long r, void *E, const struct bb_field *ff)
    1314             : {
    1315             :   GEN y, c;
    1316          21 :   long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
    1317             : 
    1318          21 :   if (rx == n && r == 0) return gcopy(x);
    1319          21 :   c = zero_zv(n);
    1320          21 :   y = cgetg(n+1, t_MAT);
    1321             :   /* c = lines containing pivots (could get it from RgM_pivots, but cheap)
    1322             :    * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
    1323         119 :   for (k = j = 1; j<=rx; j++)
    1324          98 :     if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gcopy(gel(x,j)); }
    1325         203 :   for (j=1; j<=n; j++)
    1326         182 :     if (!c[j]) gel(y,k++) = gen_colei(n, j, E, ff);
    1327          21 :   return y;
    1328             : }
    1329             : 
    1330             : static GEN
    1331          21 : gen_suppl(GEN x, void *E, const struct bb_field *ff,
    1332             :           GEN (*mul)(void*, GEN, GEN))
    1333             : {
    1334             :   GEN d;
    1335          21 :   long n = nbrows(x), r;
    1336             : 
    1337          21 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    1338          21 :   d = gen_pivots(x, &r, E, ff, mul);
    1339          21 :   return gen_get_suppl(x, d, n, r, E, ff);
    1340             : }
    1341             : 
    1342             : /*******************************************************************/
    1343             : /*                                                                 */
    1344             : /*                MATRIX MULTIPLICATION MODULO P                   */
    1345             : /*                                                                 */
    1346             : /*******************************************************************/
    1347             : 
    1348             : GEN
    1349          21 : F2xqM_F2xqC_mul(GEN A, GEN B, GEN T) {
    1350             :   void *E;
    1351          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1352          21 :   return gen_matcolmul(A, B, E, ff);
    1353             : }
    1354             : 
    1355             : GEN
    1356          35 : FlxqM_FlxqC_mul(GEN A, GEN B, GEN T, ulong p) {
    1357             :   void *E;
    1358          35 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1359          35 :   return gen_matcolmul(A, B, E, ff);
    1360             : }
    1361             : 
    1362             : GEN
    1363          63 : FqM_FqC_mul(GEN A, GEN B, GEN T, GEN p) {
    1364             :   void *E;
    1365          63 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    1366          63 :   return gen_matcolmul(A, B, E, ff);
    1367             : }
    1368             : 
    1369             : GEN
    1370        1449 : F2xqM_mul(GEN A, GEN B, GEN T) {
    1371             :   void *E;
    1372        1449 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1373        1449 :   return gen_matmul(A, B, E, ff);
    1374             : }
    1375             : 
    1376             : GEN
    1377      148988 : FlxqM_mul(GEN A, GEN B, GEN T, ulong p) {
    1378             :   void *E;
    1379             :   const struct bb_field *ff;
    1380      148988 :   long n = lg(A) - 1;
    1381             : 
    1382      148988 :   if (n == 0)
    1383           0 :     return cgetg(1, t_MAT);
    1384      148988 :   if (n > 1)
    1385       81581 :     return FlxqM_mul_Kronecker(A, B, T, p);
    1386       67407 :   ff = get_Flxq_field(&E, T, p);
    1387       67407 :   return gen_matmul(A, B, E, ff);
    1388             : }
    1389             : 
    1390             : GEN
    1391       86016 : FqM_mul(GEN A, GEN B, GEN T, GEN p) {
    1392             :   void *E;
    1393       86016 :   long n = lg(A) - 1;
    1394             :   const struct bb_field *ff;
    1395       86016 :   if (n == 0)
    1396           0 :     return cgetg(1, t_MAT);
    1397       86016 :   if (n > 1)
    1398       81851 :     return FqM_mul_Kronecker(A, B, T, p);
    1399        4165 :   ff = get_Fq_field(&E, T, p);
    1400        4165 :   return gen_matmul(A, B, E, ff);
    1401             : }
    1402             : 
    1403             : /*******************************************************************/
    1404             : /*                                                                 */
    1405             : /*                    LINEAR ALGEBRA MODULO P                      */
    1406             : /*                                                                 */
    1407             : /*******************************************************************/
    1408             : 
    1409             : static GEN
    1410           0 : _F2xqM_mul(void *E, GEN A, GEN B)
    1411           0 : { return F2xqM_mul(A, B, (GEN) E); }
    1412             : 
    1413             : struct _Flxq {
    1414             :   GEN aut;
    1415             :   GEN T;
    1416             :   ulong p;
    1417             : };
    1418             : 
    1419             : static GEN
    1420        7924 : _FlxqM_mul(void *E, GEN A, GEN B)
    1421             : {
    1422        7924 :   struct _Flxq *D = (struct _Flxq*)E;
    1423        7924 :   return FlxqM_mul(A, B, D->T, D->p);
    1424             : }
    1425             : 
    1426             : static GEN
    1427       22487 : _FpM_mul(void *E, GEN A, GEN B)
    1428       22487 : { return FpM_mul(A, B, (GEN) E); }
    1429             : 
    1430             : struct _Fq_field
    1431             : {
    1432             :   GEN T, p;
    1433             : };
    1434             : 
    1435             : static GEN
    1436        6349 : _FqM_mul(void *E, GEN A, GEN B)
    1437             : {
    1438        6349 :   struct _Fq_field *D = (struct _Fq_field*) E;
    1439        6349 :   return FqM_mul(A, B, D->T, D->p);
    1440             : }
    1441             : 
    1442             : static GEN
    1443     1289627 : FpM_init(GEN a, GEN p, ulong *pp)
    1444             : {
    1445     1289627 :   if (lgefint(p) == 3)
    1446             :   {
    1447     1285341 :     *pp = uel(p,2);
    1448     1285341 :     return (*pp==2)? ZM_to_F2m(a): ZM_to_Flm(a, *pp);
    1449             :   }
    1450        4286 :   *pp = 0; return a;
    1451             : }
    1452             : static GEN
    1453     1308695 : FpM_init3(GEN a, GEN p, ulong *pp)
    1454             : {
    1455     1308695 :   if (lgefint(p) == 3)
    1456             :   {
    1457     1306140 :     *pp = uel(p,2);
    1458     1306140 :     switch(*pp)
    1459             :     {
    1460      705309 :       case 2: return ZM_to_F2m(a);
    1461      157123 :       case 3: return ZM_to_F3m(a);
    1462      443708 :       default:return ZM_to_Flm(a, *pp);
    1463             :     }
    1464             :   }
    1465        2555 :   *pp = 0; return a;
    1466             : }
    1467             : GEN
    1468        4599 : RgM_Fp_init(GEN a, GEN p, ulong *pp)
    1469             : {
    1470        4599 :   if (lgefint(p) == 3)
    1471             :   {
    1472        4319 :     *pp = uel(p,2);
    1473        4319 :     return (*pp==2)? RgM_to_F2m(a): RgM_to_Flm(a, *pp);
    1474             :   }
    1475         280 :   *pp = 0; return RgM_to_FpM(a,p);
    1476             : }
    1477             : static GEN
    1478         658 : RgM_Fp_init3(GEN a, GEN p, ulong *pp)
    1479             : {
    1480         658 :   if (lgefint(p) == 3)
    1481             :   {
    1482         588 :     *pp = uel(p,2);
    1483         588 :     switch(*pp)
    1484             :     {
    1485          35 :       case 2: return RgM_to_F2m(a);
    1486          77 :       case 3: return RgM_to_F3m(a);
    1487         476 :       default:return RgM_to_Flm(a, *pp);
    1488             :     }
    1489             :   }
    1490          70 :   *pp = 0; return RgM_to_FpM(a,p);
    1491             : }
    1492             : 
    1493             : static GEN
    1494         315 : FpM_det_gen(GEN a, GEN p)
    1495             : {
    1496             :   void *E;
    1497         315 :   const struct bb_field *S = get_Fp_field(&E,p);
    1498         315 :   return gen_det_i(a, E, S, _FpM_mul);
    1499             : }
    1500             : GEN
    1501        4683 : FpM_det(GEN a, GEN p)
    1502             : {
    1503        4683 :   pari_sp av = avma;
    1504             :   ulong pp, d;
    1505        4683 :   a = FpM_init(a, p, &pp);
    1506        4683 :   switch(pp)
    1507             :   {
    1508         315 :   case 0: return FpM_det_gen(a, p);
    1509        1750 :   case 2: d = F2m_det_sp(a); break;
    1510        2618 :   default:d = Flm_det_sp(a,pp); break;
    1511             :   }
    1512        4368 :   return gc_utoi(av, d);
    1513             : }
    1514             : 
    1515             : GEN
    1516           7 : F2xqM_det(GEN a, GEN T)
    1517             : {
    1518             :   void *E;
    1519           7 :   const struct bb_field *S = get_F2xq_field(&E, T);
    1520           7 :   return gen_det_i(a, E, S, _F2xqM_mul);
    1521             : }
    1522             : 
    1523             : GEN
    1524          28 : FlxqM_det(GEN a, GEN T, ulong p) {
    1525             :   void *E;
    1526          28 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1527          28 :   return gen_det_i(a, E, S, _FlxqM_mul);
    1528             : }
    1529             : 
    1530             : GEN
    1531          70 : FqM_det(GEN x, GEN T, GEN p)
    1532             : {
    1533             :   void *E;
    1534          70 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    1535          70 :   return gen_det_i(x, E, S, _FqM_mul);
    1536             : }
    1537             : 
    1538             : static GEN
    1539        1214 : FpM_gauss_pivot_gen(GEN x, GEN p, long *rr)
    1540             : {
    1541             :   void *E;
    1542        1214 :   const struct bb_field *S = get_Fp_field(&E,p);
    1543        1214 :   return gen_pivots(x, rr, E, S, _FpM_mul);
    1544             : }
    1545             : 
    1546             : static GEN
    1547      640637 : FpM_gauss_pivot(GEN x, GEN p, long *rr)
    1548             : {
    1549             :   ulong pp;
    1550      640637 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1551      635561 :   x = FpM_init(x, p, &pp);
    1552      635561 :   switch(pp)
    1553             :   {
    1554        1214 :   case 0: return FpM_gauss_pivot_gen(x, p, rr);
    1555      352679 :   case 2: return F2m_gauss_pivot(x, rr);
    1556      281668 :   default:return Flm_pivots(x, pp, rr, 1);
    1557             :   }
    1558             : }
    1559             : 
    1560             : static GEN
    1561          21 : F2xqM_gauss_pivot(GEN x, GEN T, long *rr)
    1562             : {
    1563             :   void *E;
    1564          21 :   const struct bb_field *S = get_F2xq_field(&E,T);
    1565          21 :   return gen_pivots(x, rr, E, S, _F2xqM_mul);
    1566             : }
    1567             : 
    1568             : static GEN
    1569         361 : FlxqM_gauss_pivot(GEN x, GEN T, ulong p, long *rr) {
    1570             :   void *E;
    1571         361 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1572         361 :   return gen_pivots(x, rr, E, S, _FlxqM_mul);
    1573             : }
    1574             : 
    1575             : static GEN
    1576         105 : FqM_gauss_pivot_gen(GEN x, GEN T, GEN p, long *rr)
    1577             : {
    1578             :   void *E;
    1579         105 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    1580         105 :   return gen_pivots(x, rr, E, S, _FqM_mul);
    1581             : }
    1582             : static GEN
    1583         438 : FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr)
    1584             : {
    1585         438 :   if (lg(x)==1) { *rr = 0; return NULL; }
    1586         438 :   if (!T) return FpM_gauss_pivot(x, p, rr);
    1587         438 :   if (lgefint(p) == 3)
    1588             :   {
    1589         333 :     pari_sp av = avma;
    1590         333 :     ulong pp = uel(p,2);
    1591         333 :     GEN Tp = ZXT_to_FlxT(T, pp);
    1592         333 :     GEN d = FlxqM_gauss_pivot(ZXM_to_FlxM(x, pp, get_Flx_var(Tp)), Tp, pp, rr);
    1593         333 :     return d ? gc_uptoleaf(av, d): d;
    1594             :   }
    1595         105 :   return FqM_gauss_pivot_gen(x, T, p, rr);
    1596             : }
    1597             : 
    1598             : GEN
    1599      330550 : FpM_image(GEN x, GEN p)
    1600             : {
    1601             :   long r;
    1602      330550 :   GEN d = FpM_gauss_pivot(x,p,&r); /* d left on stack for efficiency */
    1603      330550 :   return image_from_pivot(x,d,r);
    1604             : }
    1605             : 
    1606             : GEN
    1607       40859 : Flm_image(GEN x, ulong p)
    1608             : {
    1609             :   long r;
    1610       40859 :   GEN d = Flm_pivots(x, p, &r, 0); /* d left on stack for efficiency */
    1611       40859 :   return image_from_pivot(x,d,r);
    1612             : }
    1613             : 
    1614             : GEN
    1615           7 : F2m_image(GEN x)
    1616             : {
    1617             :   long r;
    1618           7 :   GEN d = F2m_gauss_pivot(F2m_copy(x),&r); /* d left on stack for efficiency */
    1619           7 :   return image_from_pivot(x,d,r);
    1620             : }
    1621             : 
    1622             : GEN
    1623           7 : F2xqM_image(GEN x, GEN T)
    1624             : {
    1625             :   long r;
    1626           7 :   GEN d = F2xqM_gauss_pivot(x,T,&r); /* d left on stack for efficiency */
    1627           7 :   return image_from_pivot(x,d,r);
    1628             : }
    1629             : 
    1630             : GEN
    1631          21 : FlxqM_image(GEN x, GEN T, ulong p)
    1632             : {
    1633             :   long r;
    1634          21 :   GEN d = FlxqM_gauss_pivot(x, T, p, &r); /* d left on stack for efficiency */
    1635          21 :   return image_from_pivot(x,d,r);
    1636             : }
    1637             : 
    1638             : GEN
    1639          49 : FqM_image(GEN x, GEN T, GEN p)
    1640             : {
    1641             :   long r;
    1642          49 :   GEN d = FqM_gauss_pivot(x,T,p,&r); /* d left on stack for efficiency */
    1643          49 :   return image_from_pivot(x,d,r);
    1644             : }
    1645             : 
    1646             : long
    1647          56 : FpM_rank(GEN x, GEN p)
    1648             : {
    1649          56 :   pari_sp av = avma;
    1650             :   long r;
    1651          56 :   (void)FpM_gauss_pivot(x,p,&r);
    1652          56 :   return gc_long(av, lg(x)-1 - r);
    1653             : }
    1654             : 
    1655             : long
    1656           7 : F2xqM_rank(GEN x, GEN T)
    1657             : {
    1658           7 :   pari_sp av = avma;
    1659             :   long r;
    1660           7 :   (void)F2xqM_gauss_pivot(x,T,&r);
    1661           7 :   return gc_long(av, lg(x)-1 - r);
    1662             : }
    1663             : 
    1664             : long
    1665          35 : FlxqM_rank(GEN x, GEN T, ulong p)
    1666             : {
    1667             :   void *E;
    1668          35 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1669          35 :   return gen_matrank(x, E, S, _FlxqM_mul);
    1670             : }
    1671             : 
    1672             : long
    1673          70 : FqM_rank(GEN x, GEN T, GEN p)
    1674             : {
    1675          70 :   pari_sp av = avma;
    1676             :   long r;
    1677          70 :   (void)FqM_gauss_pivot(x,T,p,&r);
    1678          70 :   return gc_long(av, lg(x)-1 - r);
    1679             : }
    1680             : 
    1681             : static GEN
    1682          35 : FpM_invimage_gen(GEN A, GEN B, GEN p)
    1683             : {
    1684             :   void *E;
    1685          35 :   const struct bb_field *ff = get_Fp_field(&E, p);
    1686          35 :   return gen_invimage(A, B, E, ff, _FpM_mul);
    1687             : }
    1688             : 
    1689             : GEN
    1690           0 : FpM_invimage(GEN A, GEN B, GEN p)
    1691             : {
    1692           0 :   pari_sp av = avma;
    1693             :   ulong pp;
    1694             :   GEN y;
    1695             : 
    1696           0 :   A = FpM_init(A, p, &pp);
    1697           0 :   switch(pp)
    1698             :   {
    1699           0 :   case 0: return FpM_invimage_gen(A, B, p);
    1700           0 :   case 2:
    1701           0 :     y = F2m_invimage(A, ZM_to_F2m(B));
    1702           0 :     if (!y) return gc_NULL(av);
    1703           0 :     y = F2m_to_ZM(y);
    1704           0 :     return gc_upto(av, y);
    1705           0 :   default:
    1706           0 :     y = Flm_invimage_i(A, ZM_to_Flm(B, pp), pp);
    1707           0 :     if (!y) return gc_NULL(av);
    1708           0 :     y = Flm_to_ZM(y);
    1709           0 :     return gc_upto(av, y);
    1710             :   }
    1711             : }
    1712             : 
    1713             : GEN
    1714          21 : F2xqM_invimage(GEN A, GEN B, GEN T) {
    1715             :   void *E;
    1716          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1717          21 :   return gen_invimage(A, B, E, ff, _F2xqM_mul);
    1718             : }
    1719             : 
    1720             : GEN
    1721          42 : FlxqM_invimage(GEN A, GEN B, GEN T, ulong p) {
    1722             :   void *E;
    1723          42 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1724          42 :   return gen_invimage(A, B, E, ff, _FlxqM_mul);
    1725             : }
    1726             : 
    1727             : GEN
    1728          42 : FqM_invimage(GEN A, GEN B, GEN T, GEN p) {
    1729             :   void *E;
    1730          42 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    1731          42 :   return gen_invimage(A, B, E, ff, _FqM_mul);
    1732             : }
    1733             : 
    1734             : static GEN
    1735           8 : FpM_FpC_invimage_gen(GEN A, GEN y, GEN p)
    1736             : {
    1737             :   void *E;
    1738           8 :   const struct bb_field *ff = get_Fp_field(&E, p);
    1739           8 :   return gen_matcolinvimage_i(A, y, E, ff, _FpM_mul);
    1740             : }
    1741             : 
    1742             : GEN
    1743      297548 : FpM_FpC_invimage(GEN A, GEN x, GEN p)
    1744             : {
    1745      297548 :   pari_sp av = avma;
    1746             :   ulong pp;
    1747             :   GEN y;
    1748             : 
    1749      297548 :   A = FpM_init(A, p, &pp);
    1750      297563 :   switch(pp)
    1751             :   {
    1752           8 :   case 0: return FpM_FpC_invimage_gen(A, x, p);
    1753      193251 :   case 2:
    1754      193251 :     y = F2m_F2c_invimage(A, ZV_to_F2v(x));
    1755      193245 :     if (!y) return y;
    1756      193245 :     y = F2c_to_ZC(y);
    1757      193244 :     return gc_upto(av, y);
    1758      104304 :   default:
    1759      104304 :     y = Flm_Flc_invimage(A, ZV_to_Flv(x, pp), pp);
    1760      104304 :     if (!y) return y;
    1761      104304 :     y = Flc_to_ZC(y);
    1762      104303 :     return gc_upto(av, y);
    1763             :   }
    1764             : }
    1765             : 
    1766             : GEN
    1767          21 : F2xqM_F2xqC_invimage(GEN A, GEN B, GEN T) {
    1768             :   void *E;
    1769          21 :   const struct bb_field *ff = get_F2xq_field(&E, T);
    1770          21 :   return gen_matcolinvimage_i(A, B, E, ff, _F2xqM_mul);
    1771             : }
    1772             : 
    1773             : GEN
    1774          21 : FlxqM_FlxqC_invimage(GEN A, GEN B, GEN T, ulong p) {
    1775             :   void *E;
    1776          21 :   const struct bb_field *ff = get_Flxq_field(&E, T, p);
    1777          21 :   return gen_matcolinvimage_i(A, B, E, ff, _FlxqM_mul);
    1778             : }
    1779             : 
    1780             : GEN
    1781          21 : FqM_FqC_invimage(GEN A, GEN B, GEN T, GEN p) {
    1782             :   void *E;
    1783          21 :   const struct bb_field *ff = get_Fq_field(&E, T, p);
    1784          21 :   return gen_matcolinvimage_i(A, B, E, ff, _FqM_mul);
    1785             : }
    1786             : 
    1787             : static GEN
    1788        2642 : FpM_ker_gen(GEN x, GEN p, long deplin)
    1789             : {
    1790             :   void *E;
    1791        2642 :   const struct bb_field *S = get_Fp_field(&E,p);
    1792        2642 :   return gen_ker_i(x, deplin, E, S, _FpM_mul);
    1793             : }
    1794             : static GEN
    1795     1308713 : FpM_ker_i(GEN x, GEN p, long deplin)
    1796             : {
    1797     1308713 :   pari_sp av = avma;
    1798             :   ulong pp;
    1799             :   GEN y;
    1800             : 
    1801     1308713 :   if (lg(x)==1) return cgetg(1,t_MAT);
    1802     1308713 :   x = FpM_init3(x, p, &pp);
    1803     1308726 :   switch(pp)
    1804             :   {
    1805        2572 :   case 0: return FpM_ker_gen(x,p,deplin);
    1806      705321 :   case 2:
    1807      705321 :     y = F2m_ker_sp(x, deplin);
    1808      705311 :     if (!y) return gc_NULL(av);
    1809      705328 :     y = deplin? F2c_to_ZC(y): F2m_to_ZM(y);
    1810      705328 :     return gc_upto(av, y);
    1811      157123 :   case 3:
    1812      157123 :     y = F3m_ker_sp(x, deplin);
    1813      157123 :     if (!y) return gc_NULL(av);
    1814      157123 :     y = deplin? F3c_to_ZC(y): F3m_to_ZM(y);
    1815      157123 :     return gc_upto(av, y);
    1816      443710 :   default:
    1817      443710 :     y = Flm_ker_sp(x, pp, deplin);
    1818      443709 :     if (!y) return gc_NULL(av);
    1819      443709 :     y = deplin? Flc_to_ZC(y): Flm_to_ZM(y);
    1820      443709 :     return gc_upto(av, y);
    1821             :   }
    1822             : }
    1823             : 
    1824             : GEN
    1825      849705 : FpM_ker(GEN x, GEN p) { return FpM_ker_i(x,p,0); }
    1826             : 
    1827             : static GEN
    1828          21 : F2xqM_ker_i(GEN x, GEN T, long deplin)
    1829             : {
    1830             :   const struct bb_field *ff;
    1831             :   void *E;
    1832             : 
    1833          21 :   if (lg(x)==1) return cgetg(1,t_MAT);
    1834          21 :   ff = get_F2xq_field(&E,T);
    1835          21 :   return gen_ker_i(x,deplin, E, ff, _F2xqM_mul);
    1836             : }
    1837             : 
    1838             : GEN
    1839           7 : F2xqM_ker(GEN x, GEN T)
    1840             : {
    1841           7 :   return F2xqM_ker_i(x, T, 0);
    1842             : }
    1843             : 
    1844             : static GEN
    1845         812 : FlxqM_ker_i(GEN x, GEN T, ulong p, long deplin) {
    1846             :   void *E;
    1847         812 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1848         812 :   return gen_ker_i(x, deplin, E, S, _FlxqM_mul);
    1849             : }
    1850             : 
    1851             : GEN
    1852          28 : FlxqM_ker(GEN x, GEN T, ulong p)
    1853             : {
    1854          28 :   return FlxqM_ker_i(x, T, p, 0);
    1855             : }
    1856             : 
    1857             : static GEN
    1858         126 : FqM_ker_gen(GEN x, GEN T, GEN p, long deplin)
    1859             : {
    1860             :   void *E;
    1861         126 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    1862         126 :   return gen_ker_i(x,deplin,E,S,_FqM_mul);
    1863             : }
    1864             : static GEN
    1865        3535 : FqM_ker_i(GEN x, GEN T, GEN p, long deplin)
    1866             : {
    1867        3535 :   if (!T) return FpM_ker_i(x,p,deplin);
    1868         875 :   if (lg(x)==1) return cgetg(1,t_MAT);
    1869             : 
    1870         875 :   if (lgefint(p)==3)
    1871             :   {
    1872         749 :     pari_sp av = avma;
    1873         749 :     ulong l = p[2];
    1874         749 :     GEN Tl = ZXT_to_FlxT(T,l);
    1875         749 :     GEN Ml = ZXM_to_FlxM(x, l, get_Flx_var(Tl));
    1876         749 :     GEN K = FlxqM_ker_i(Ml, Tl, l, deplin);
    1877         749 :     if (!deplin) K = FlxM_to_ZXM(K);
    1878          28 :     else if (!K) return gc_NULL(av);
    1879          21 :     else K = FlxC_to_ZXC(K);
    1880         742 :     return gc_upto(av, K);
    1881             :   }
    1882         126 :   return FqM_ker_gen(x, T, p, deplin);
    1883             : }
    1884             : 
    1885             : GEN
    1886        3451 : FqM_ker(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,0); }
    1887             : 
    1888             : GEN
    1889      456355 : FpM_deplin(GEN x, GEN p) { return FpM_ker_i(x,p,1); }
    1890             : 
    1891             : GEN
    1892          14 : F2xqM_deplin(GEN x, GEN T)
    1893             : {
    1894          14 :   return F2xqM_ker_i(x, T, 1);
    1895             : }
    1896             : 
    1897             : GEN
    1898          35 : FlxqM_deplin(GEN x, GEN T, ulong p)
    1899             : {
    1900          35 :   return FlxqM_ker_i(x, T, p, 1);
    1901             : }
    1902             : 
    1903             : GEN
    1904          84 : FqM_deplin(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,1); }
    1905             : 
    1906             : static GEN
    1907        2749 : FpM_gauss_gen(GEN a, GEN b, GEN p)
    1908             : {
    1909             :   void *E;
    1910        2749 :   const struct bb_field *S = get_Fp_field(&E,p);
    1911        2749 :   return gen_gauss(a,b, E, S, _FpM_mul);
    1912             : }
    1913             : /* a an FpM, lg(a)>1; b an FpM or NULL (replace by identity) */
    1914             : static GEN
    1915      351871 : FpM_gauss_i(GEN a, GEN b, GEN p, ulong *pp)
    1916             : {
    1917      351871 :   long n = nbrows(a);
    1918      351871 :   a = FpM_init(a,p,pp);
    1919      351869 :   switch(*pp)
    1920             :   {
    1921        2749 :   case 0:
    1922        2749 :     if (!b) b = matid(n);
    1923        2749 :     return FpM_gauss_gen(a,b,p);
    1924      228715 :   case 2:
    1925      228715 :     if (b) b = ZM_to_F2m(b); else b = matid_F2m(n);
    1926      228716 :     return F2m_gauss_sp(a,b);
    1927      120405 :   default:
    1928      120405 :     if (b) b = ZM_to_Flm(b, *pp); else b = matid_Flm(n);
    1929      120405 :     return Flm_gauss_sp(a,b, NULL, *pp);
    1930             :   }
    1931             : }
    1932             : GEN
    1933          35 : FpM_gauss(GEN a, GEN b, GEN p)
    1934             : {
    1935          35 :   pari_sp av = avma;
    1936             :   ulong pp;
    1937             :   GEN u;
    1938          35 :   if (lg(a) == 1 || lg(b)==1) return cgetg(1, t_MAT);
    1939          35 :   u = FpM_gauss_i(a, b, p, &pp);
    1940          35 :   if (!u) return gc_NULL(av);
    1941          28 :   switch(pp)
    1942             :   {
    1943          28 :   case 0: return gc_GEN(av, u);
    1944           0 :   case 2:  u = F2m_to_ZM(u); break;
    1945           0 :   default: u = Flm_to_ZM(u); break;
    1946             :   }
    1947           0 :   return gc_upto(av, u);
    1948             : }
    1949             : 
    1950             : static GEN
    1951          63 : F2xqM_gauss_gen(GEN a, GEN b, GEN T)
    1952             : {
    1953             :   void *E;
    1954          63 :   const struct bb_field *S = get_F2xq_field(&E, T);
    1955          63 :   return gen_gauss(a, b, E, S, _F2xqM_mul);
    1956             : }
    1957             : 
    1958             : GEN
    1959          14 : F2xqM_gauss(GEN a, GEN b, GEN T)
    1960             : {
    1961          14 :   pari_sp av = avma;
    1962          14 :   long n = lg(a)-1;
    1963             :   GEN u;
    1964          14 :   if (!n || lg(b)==1) retgc_const(av, cgetg(1, t_MAT));
    1965          14 :   u = F2xqM_gauss_gen(a, b, T);
    1966          14 :   if (!u) return gc_NULL(av);
    1967          14 :   return gc_GEN(av, u);
    1968             : }
    1969             : 
    1970             : static GEN
    1971          91 : FlxqM_gauss_i(GEN a, GEN b, GEN T, ulong p) {
    1972             :   void *E;
    1973          91 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    1974          91 :   return gen_gauss(a, b, E, S, _FlxqM_mul);
    1975             : }
    1976             : 
    1977             : GEN
    1978          21 : FlxqM_gauss(GEN a, GEN b, GEN T, ulong p)
    1979             : {
    1980          21 :   pari_sp av = avma;
    1981          21 :   long n = lg(a)-1;
    1982             :   GEN u;
    1983          21 :   if (!n || lg(b)==1) retgc_const(av, cgetg(1, t_MAT));
    1984          21 :   u = FlxqM_gauss_i(a, b, T, p);
    1985          21 :   if (!u) return gc_NULL(av);
    1986          14 :   return gc_GEN(av, u);
    1987             : }
    1988             : 
    1989             : static GEN
    1990         133 : FqM_gauss_gen(GEN a, GEN b, GEN T, GEN p)
    1991             : {
    1992             :   void *E;
    1993         133 :   const struct bb_field *S = get_Fq_field(&E,T,p);
    1994         133 :   return gen_gauss(a,b,E,S,_FqM_mul);
    1995             : }
    1996             : GEN
    1997          21 : FqM_gauss(GEN a, GEN b, GEN T, GEN p)
    1998             : {
    1999          21 :   pari_sp av = avma;
    2000             :   GEN u;
    2001             :   long n;
    2002          21 :   if (!T) return FpM_gauss(a,b,p);
    2003          21 :   n = lg(a)-1; if (!n || lg(b)==1) return cgetg(1, t_MAT);
    2004          21 :   u = FqM_gauss_gen(a,b,T,p);
    2005          21 :   if (!u) return gc_NULL(av);
    2006          14 :   return gc_GEN(av, u);
    2007             : }
    2008             : 
    2009             : GEN
    2010          14 : FpM_FpC_gauss(GEN a, GEN b, GEN p)
    2011             : {
    2012          14 :   pari_sp av = avma;
    2013             :   ulong pp;
    2014             :   GEN u;
    2015          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2016          14 :   u = FpM_gauss_i(a, mkmat(b), p, &pp);
    2017          14 :   if (!u) return gc_NULL(av);
    2018          14 :   switch(pp)
    2019             :   {
    2020          14 :   case 0: return gc_GEN(av, gel(u,1));
    2021           0 :   case 2:  u = F2c_to_ZC(gel(u,1)); break;
    2022           0 :   default: u = Flc_to_ZC(gel(u,1)); break;
    2023             :   }
    2024           0 :   return gc_upto(av, u);
    2025             : }
    2026             : 
    2027             : GEN
    2028          14 : F2xqM_F2xqC_gauss(GEN a, GEN b, GEN T)
    2029             : {
    2030          14 :   pari_sp av = avma;
    2031             :   GEN u;
    2032          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2033          14 :   u = F2xqM_gauss_gen(a, mkmat(b), T);
    2034          14 :   if (!u) return gc_NULL(av);
    2035           7 :   return gc_GEN(av, gel(u,1));
    2036             : }
    2037             : 
    2038             : GEN
    2039          14 : FlxqM_FlxqC_gauss(GEN a, GEN b, GEN T, ulong p)
    2040             : {
    2041          14 :   pari_sp av = avma;
    2042             :   GEN u;
    2043          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2044          14 :   u = FlxqM_gauss_i(a, mkmat(b), T, p);
    2045          14 :   if (!u) return gc_NULL(av);
    2046           7 :   return gc_GEN(av, gel(u,1));
    2047             : }
    2048             : 
    2049             : GEN
    2050          14 : FqM_FqC_gauss(GEN a, GEN b, GEN T, GEN p)
    2051             : {
    2052          14 :   pari_sp av = avma;
    2053             :   GEN u;
    2054          14 :   if (!T) return FpM_FpC_gauss(a,b,p);
    2055          14 :   if (lg(a) == 1) return cgetg(1, t_COL);
    2056          14 :   u = FqM_gauss_gen(a,mkmat(b),T,p);
    2057          14 :   if (!u) return gc_NULL(av);
    2058           7 :   return gc_GEN(av, gel(u,1));
    2059             : }
    2060             : 
    2061             : GEN
    2062      351822 : FpM_inv(GEN a, GEN p)
    2063             : {
    2064      351822 :   pari_sp av = avma;
    2065             :   ulong pp;
    2066             :   GEN u;
    2067      351822 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    2068      351822 :   u = FpM_gauss_i(a, NULL, p, &pp);
    2069      351817 :   if (!u) return gc_NULL(av);
    2070      351803 :   switch(pp)
    2071             :   {
    2072        2693 :   case 0: return gc_GEN(av, u);
    2073      228705 :   case 2:  u = F2m_to_ZM(u); break;
    2074      120405 :   default: u = Flm_to_ZM(u); break;
    2075             :   }
    2076      349110 :   return gc_upto(av, u);
    2077             : }
    2078             : 
    2079             : GEN
    2080          35 : F2xqM_inv(GEN a, GEN T)
    2081             : {
    2082          35 :   pari_sp av = avma;
    2083             :   GEN u;
    2084          35 :   if (lg(a) == 1) retgc_const(av, cgetg(1, t_MAT));
    2085          35 :   u = F2xqM_gauss_gen(a, matid_F2xqM(nbrows(a),T), T);
    2086          35 :   if (!u) return gc_NULL(av);
    2087          28 :   return gc_GEN(av, u);
    2088             : }
    2089             : 
    2090             : GEN
    2091          56 : FlxqM_inv(GEN a, GEN T, ulong p)
    2092             : {
    2093          56 :   pari_sp av = avma;
    2094             :   GEN u;
    2095          56 :   if (lg(a) == 1) retgc_const(av, cgetg(1, t_MAT));
    2096          56 :   u = FlxqM_gauss_i(a, matid_FlxqM(nbrows(a),T,p), T,p);
    2097          56 :   if (!u) return gc_NULL(av);
    2098          42 :   return gc_GEN(av, u);
    2099             : }
    2100             : 
    2101             : GEN
    2102          98 : FqM_inv(GEN a, GEN T, GEN p)
    2103             : {
    2104          98 :   pari_sp av = avma;
    2105             :   GEN u;
    2106          98 :   if (!T) return FpM_inv(a,p);
    2107          98 :   if (lg(a) == 1) return cgetg(1, t_MAT);
    2108          98 :   u = FqM_gauss_gen(a,matid(nbrows(a)),T,p);
    2109          98 :   if (!u) return gc_NULL(av);
    2110          70 :   return gc_GEN(av, u);
    2111             : }
    2112             : 
    2113             : GEN
    2114      263912 : FpM_intersect_i(GEN x, GEN y, GEN p)
    2115             : {
    2116      263912 :   long j, lx = lg(x);
    2117             :   GEN z;
    2118             : 
    2119      263912 :   if (lx == 1 || lg(y) == 1) return cgetg(1,t_MAT);
    2120      263912 :   if (lgefint(p) == 3)
    2121             :   {
    2122      263912 :     ulong pp = p[2];
    2123      263912 :     return Flm_to_ZM(Flm_intersect_i(ZM_to_Flm(x,pp), ZM_to_Flm(y,pp), pp));
    2124             :   }
    2125           0 :   z = FpM_ker(shallowconcat(x,y), p);
    2126           0 :   for (j=lg(z)-1; j; j--) setlg(gel(z,j),lx);
    2127           0 :   return FpM_mul(x,z,p);
    2128             : }
    2129             : GEN
    2130           0 : FpM_intersect(GEN x, GEN y, GEN p)
    2131             : {
    2132           0 :   pari_sp av = avma;
    2133             :   GEN z;
    2134           0 :   if (lgefint(p) == 3)
    2135             :   {
    2136           0 :     ulong pp = p[2];
    2137           0 :     z = Flm_to_ZM(Flm_image(Flm_intersect_i(ZM_to_Flm(x,pp), ZM_to_Flm(y,pp), pp), pp));
    2138             :   }
    2139             :   else
    2140           0 :     z = FpM_image(FpM_intersect_i(x,y,p), p);
    2141           0 :   return gc_upto(av, z);
    2142             : }
    2143             : 
    2144             : /* HACK: avoid overwriting d from RgM_pivots after set_avma(av) in suppl
    2145             :  * or indexrank-type functions */
    2146             : static void
    2147      267810 : init_suppl(GEN x)
    2148             : {
    2149      267810 :   if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
    2150      267810 :   (void)new_chunk(lgcols(x) * 2);
    2151      267809 : }
    2152             : static void
    2153     1925629 : init_pivot_list(GEN x) { (void)new_chunk(3 + 2*lg(x)); /* HACK */ }
    2154             : 
    2155             : GEN
    2156      267308 : FpM_suppl(GEN x, GEN p)
    2157             : {
    2158             :   GEN d;
    2159             :   long r;
    2160      267308 :   init_suppl(x); d = FpM_gauss_pivot(x,p, &r);
    2161      267306 :   return get_suppl(x,d,nbrows(x),r,&col_ei);
    2162             : }
    2163             : 
    2164             : GEN
    2165          14 : F2m_suppl(GEN x)
    2166             : {
    2167             :   GEN d;
    2168             :   long r;
    2169          14 :   init_suppl(x); d = F2m_gauss_pivot(F2m_copy(x), &r);
    2170          14 :   return get_suppl(x,d,mael(x,1,1),r,&F2v_ei);
    2171             : }
    2172             : 
    2173             : GEN
    2174         105 : Flm_suppl(GEN x, ulong p)
    2175             : {
    2176             :   GEN d;
    2177             :   long r;
    2178         105 :   init_suppl(x); d = Flm_pivots(x, p, &r, 0);
    2179         105 :   return get_suppl(x,d,nbrows(x),r,&vecsmall_ei);
    2180             : }
    2181             : 
    2182             : GEN
    2183           7 : F2xqM_suppl(GEN x, GEN T)
    2184             : {
    2185             :   void *E;
    2186           7 :   const struct bb_field *S = get_F2xq_field(&E, T);
    2187           7 :   return gen_suppl(x, E, S, _F2xqM_mul);
    2188             : }
    2189             : 
    2190             : GEN
    2191          14 : FlxqM_suppl(GEN x, GEN T, ulong p)
    2192             : {
    2193             :   void *E;
    2194          14 :   const struct bb_field *S = get_Flxq_field(&E, T, p);
    2195          14 :   return gen_suppl(x, E, S, _FlxqM_mul);
    2196             : }
    2197             : 
    2198             : GEN
    2199        1481 : FqM_suppl(GEN x, GEN T, GEN p)
    2200             : {
    2201        1481 :   pari_sp av = avma;
    2202             :   GEN d;
    2203             :   long r;
    2204             : 
    2205        1481 :   if (!T) return FpM_suppl(x,p);
    2206         312 :   init_suppl(x);
    2207         312 :   d = FqM_gauss_pivot(x,T,p,&r);
    2208         312 :   set_avma(av); return get_suppl(x,d,nbrows(x),r,&col_ei);
    2209             : }
    2210             : 
    2211             : GEN
    2212       42726 : FpM_indexrank(GEN x, GEN p) {
    2213       42726 :   pari_sp av = avma;
    2214             :   long r;
    2215             :   GEN d;
    2216       42726 :   init_pivot_list(x);
    2217       42726 :   d = FpM_gauss_pivot(x,p,&r);
    2218       42726 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    2219             : }
    2220             : 
    2221             : GEN
    2222       58448 : Flm_indexrank(GEN x, ulong p) {
    2223       58448 :   pari_sp av = avma;
    2224             :   long r;
    2225             :   GEN d;
    2226       58448 :   init_pivot_list(x);
    2227       58449 :   d = Flm_pivots(x, p, &r, 0);
    2228       58450 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    2229             : }
    2230             : 
    2231             : GEN
    2232          53 : F2m_indexrank(GEN x) {
    2233          53 :   pari_sp av = avma;
    2234             :   long r;
    2235             :   GEN d;
    2236          53 :   init_pivot_list(x);
    2237          53 :   d = F2m_gauss_pivot(F2m_copy(x),&r);
    2238          53 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    2239             : }
    2240             : 
    2241             : GEN
    2242           7 : F2xqM_indexrank(GEN x, GEN T) {
    2243           7 :   pari_sp av = avma;
    2244             :   long r;
    2245             :   GEN d;
    2246           7 :   init_pivot_list(x);
    2247           7 :   d = F2xqM_gauss_pivot(x, T, &r);
    2248           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    2249             : }
    2250             : 
    2251             : GEN
    2252           7 : FlxqM_indexrank(GEN x, GEN T, ulong p) {
    2253           7 :   pari_sp av = avma;
    2254             :   long r;
    2255             :   GEN d;
    2256           7 :   init_pivot_list(x);
    2257           7 :   d = FlxqM_gauss_pivot(x, T, p, &r);
    2258           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    2259             : }
    2260             : 
    2261             : GEN
    2262           7 : FqM_indexrank(GEN x, GEN T, GEN p) {
    2263           7 :   pari_sp av = avma;
    2264             :   long r;
    2265             :   GEN d;
    2266           7 :   init_pivot_list(x);
    2267           7 :   d = FqM_gauss_pivot(x, T, p, &r);
    2268           7 :   set_avma(av); return indexrank0(lg(x) - 1, r, d);
    2269             : }
    2270             : 
    2271             : /*******************************************************************/
    2272             : /*                                                                 */
    2273             : /*                       Solve A*X=B (Gauss pivot)                 */
    2274             : /*                                                                 */
    2275             : /*******************************************************************/
    2276             : /* x a column, x0 same column in the original input matrix (for reference),
    2277             :  * c list of pivots so far */
    2278             : static long
    2279     2593004 : gauss_get_pivot_max(GEN X, GEN X0, long ix, GEN c)
    2280             : {
    2281     2593004 :   GEN p, r, x = gel(X,ix), x0 = gel(X0,ix);
    2282     2593004 :   long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
    2283     2593004 :   if (c)
    2284             :   {
    2285      585454 :     for (i=1; i<lx; i++)
    2286      365768 :       if (!c[i])
    2287             :       {
    2288      149178 :         long e = gexpo(gel(x,i));
    2289      149178 :         if (e > ex) { ex = e; k = i; }
    2290             :       }
    2291             :   }
    2292             :   else
    2293             :   {
    2294     8418916 :     for (i=ix; i<lx; i++)
    2295             :     {
    2296     6045588 :       long e = gexpo(gel(x,i));
    2297     6045598 :       if (e > ex) { ex = e; k = i; }
    2298             :     }
    2299             :   }
    2300     2593014 :   if (!k) return lx;
    2301     2477852 :   p = gel(x,k);
    2302     2477852 :   r = gel(x0,k); if (isrationalzero(r)) r = x0;
    2303     2477856 :   return cx_approx0(p, r)? lx: k;
    2304             : }
    2305             : static long
    2306      201820 : gauss_get_pivot_padic(GEN X, GEN p, long ix, GEN c)
    2307             : {
    2308      201820 :   GEN x = gel(X, ix);
    2309      201820 :   long i, k = 0, ex = (long)HIGHVALPBIT, lx = lg(x);
    2310      201820 :   if (c)
    2311             :   {
    2312         504 :     for (i=1; i<lx; i++)
    2313         378 :       if (!c[i] && !gequal0(gel(x,i)))
    2314             :       {
    2315         245 :         long e = gvaluation(gel(x,i), p);
    2316         245 :         if (e < ex) { ex = e; k = i; }
    2317             :       }
    2318             :   }
    2319             :   else
    2320             :   {
    2321     1721352 :     for (i=ix; i<lx; i++)
    2322     1519658 :       if (!gequal0(gel(x,i)))
    2323             :       {
    2324     1147068 :         long e = gvaluation(gel(x,i), p);
    2325     1147068 :         if (e < ex) { ex = e; k = i; }
    2326             :       }
    2327             :   }
    2328      201820 :   return k? k: lx;
    2329             : }
    2330             : static long
    2331        3752 : gauss_get_pivot_NZ(GEN X, GEN x0/*unused*/, long ix, GEN c)
    2332             : {
    2333        3752 :   GEN x = gel(X, ix);
    2334        3752 :   long i, lx = lg(x);
    2335             :   (void)x0;
    2336        3752 :   if (c)
    2337             :   {
    2338        9891 :     for (i=1; i<lx; i++)
    2339        9002 :       if (!c[i] && !gequal0(gel(x,i))) return i;
    2340             :   }
    2341             :   else
    2342             :   {
    2343        2002 :     for (i=ix; i<lx; i++)
    2344        1988 :       if (!gequal0(gel(x,i))) return i;
    2345             :   }
    2346         903 :   return lx;
    2347             : }
    2348             : 
    2349             : /* Set pivot seeking function appropriate for the domain of x with RgM_type t
    2350             :  * (first non zero pivot, maximal pivot...)
    2351             :  * x0 is a reference point used when guessing whether x[i,j] ~ 0
    2352             :  * (iff x[i,j] << x0[i,j]); typical case: mateigen, Gauss pivot on x - vp.Id,
    2353             :  * but use original x when deciding whether a prospective pivot is nonzero */
    2354             : static void
    2355     1908146 : set_pivot_fun(pivot_fun *fun, GEN *data, long t, GEN x0, GEN p)
    2356             : {
    2357     1908146 :   switch(t)
    2358             :   {
    2359     1799250 :     case t_REAL:
    2360     1799250 :     case t_COMPLEX: *data = x0; *fun = gauss_get_pivot_max; break;
    2361       26998 :     case t_PADIC: *data = p; *fun = gauss_get_pivot_padic; break;
    2362       81898 :     default: *data = NULL; *fun = gauss_get_pivot_NZ;
    2363             :   }
    2364     1908146 : }
    2365             : static void
    2366       26786 : set_pivot_fun_all(pivot_fun *fun, GEN *data, GEN x)
    2367             : {
    2368             :   GEN p, pol;
    2369       26786 :   long pa, t = RgM_type(x, &p,&pol,&pa);
    2370       26785 :   set_pivot_fun(fun, data, t, x, p);
    2371       26785 : }
    2372             : 
    2373             : static GEN
    2374     1265739 : get_col(GEN a, GEN b, GEN p, long li)
    2375             : {
    2376     1265739 :   GEN u = cgetg(li+1,t_COL);
    2377             :   long i, j;
    2378             : 
    2379     1265739 :   gel(u,li) = gdiv(gel(b,li), p);
    2380     5151792 :   for (i=li-1; i>0; i--)
    2381             :   {
    2382     3886062 :     pari_sp av = avma;
    2383     3886062 :     GEN m = gel(b,i);
    2384    17098053 :     for (j=i+1; j<=li; j++) m = gsub(m, gmul(gcoeff(a,i,j), gel(u,j)));
    2385     3886002 :     gel(u,i) = gc_upto(av, gdiv(m, gcoeff(a,i,i)));
    2386             :   }
    2387     1265730 :   return u;
    2388             : }
    2389             : 
    2390             : /* bk -= m * bi */
    2391             : static void
    2392    18302602 : _submul(GEN b, long k, long i, GEN m)
    2393             : {
    2394    18302602 :   gel(b,k) = gsub(gel(b,k), gmul(m, gel(b,i)));
    2395    18302505 : }
    2396             : static int
    2397     2376940 : init_gauss(GEN a, GEN *b, long *aco, long *li, int *iscol)
    2398             : {
    2399     2376940 :   *iscol = *b ? (typ(*b) == t_COL): 0;
    2400     2376940 :   *aco = lg(a) - 1;
    2401     2376940 :   if (!*aco) /* a empty */
    2402             :   {
    2403          70 :     if (*b && lg(*b) != 1) pari_err_DIM("gauss");
    2404          70 :     *li = 0; return 0;
    2405             :   }
    2406     2376870 :   *li = nbrows(a);
    2407     2376872 :   if (*li < *aco) pari_err_INV("gauss [no left inverse]", a);
    2408     2376873 :   if (*b)
    2409             :   {
    2410     2112562 :     switch(typ(*b))
    2411             :     {
    2412      121622 :       case t_MAT:
    2413      121622 :         if (lg(*b) == 1) return 0;
    2414      121622 :         *b = RgM_shallowcopy(*b);
    2415      121620 :         break;
    2416     1990941 :       case t_COL:
    2417     1990941 :         *b = mkmat( leafcopy(*b) );
    2418     1990941 :         break;
    2419           0 :       default: pari_err_TYPE("gauss",*b);
    2420             :     }
    2421     2112561 :     if (nbrows(*b) != *li) pari_err_DIM("gauss");
    2422             :   }
    2423             :   else
    2424      264311 :     *b = matid(*li);
    2425     2376870 :   return 1;
    2426             : }
    2427             : 
    2428             : static GEN
    2429        2051 : RgM_inv_FpM(GEN a, GEN p)
    2430             : {
    2431             :   ulong pp;
    2432        2051 :   a = RgM_Fp_init(a, p, &pp);
    2433        2051 :   switch(pp)
    2434             :   {
    2435          35 :   case 0:
    2436          35 :     a = FpM_inv(a,p);
    2437          35 :     if (a) a = FpM_to_mod(a, p);
    2438          35 :     break;
    2439         189 :   case 2:
    2440         189 :     a = F2m_inv(a);
    2441         189 :     if (a) a = F2m_to_mod(a);
    2442         189 :     break;
    2443        1827 :   default:
    2444        1827 :     a = Flm_inv_sp(a, NULL, pp);
    2445        1827 :     if (a) a = Flm_to_mod(a, pp);
    2446             :   }
    2447        2051 :   return a;
    2448             : }
    2449             : 
    2450             : static GEN
    2451          42 : RgM_inv_FqM(GEN x, GEN pol, GEN p)
    2452             : {
    2453          42 :   pari_sp av = avma;
    2454          42 :   GEN b, T = RgX_to_FpX(pol, p);
    2455          42 :   if (signe(T) == 0) pari_err_OP("^",x,gen_m1);
    2456          42 :   b = FqM_inv(RgM_to_FqM(x, T, p), T, p);
    2457          42 :   if (!b) return gc_NULL(av);
    2458          28 :   return gc_upto(av, FqM_to_mod(b, T, p));
    2459             : }
    2460             : 
    2461             : /* Returns gen_0 instead of NULL for 'no fast algorithm'. NULL is already
    2462             :  * reserved for 'not invertible' */
    2463             : static GEN
    2464      526350 : RgM_inv_fast(GEN x, pivot_fun *fun, GEN *data)
    2465             : {
    2466             :   GEN p, pol;
    2467      526350 :   long pa, t = RgM_type(x, &p,&pol,&pa);
    2468      526355 :   set_pivot_fun(fun, data, t, x, p);
    2469      526356 :   switch(t)
    2470             :   {
    2471       48496 :     case t_INT:    /* Fall back */
    2472       48496 :     case t_FRAC:   return QM_inv(x);
    2473         147 :     case t_FFELT:  return FFM_inv(x, pol);
    2474        2051 :     case t_INTMOD: return RgM_inv_FpM(x, p);
    2475          42 :     case RgX_type_code(t_POLMOD, t_INTMOD):
    2476          42 :                    return RgM_inv_FqM(x, pol, p);
    2477      475620 :     default:       return gen_0;
    2478             :   }
    2479             : }
    2480             : 
    2481             : static GEN
    2482          63 : RgM_RgC_solve_FpC(GEN a, GEN b, GEN p)
    2483             : {
    2484          63 :   pari_sp av = avma;
    2485             :   ulong pp;
    2486          63 :   a = RgM_Fp_init(a, p, &pp);
    2487          63 :   switch(pp)
    2488             :   {
    2489          14 :   case 0:
    2490          14 :     b = RgC_to_FpC(b, p);
    2491          14 :     a = FpM_FpC_gauss(a,b,p);
    2492          14 :     return a ? gc_upto(av, FpC_to_mod(a, p)): NULL;
    2493          28 :   case 2:
    2494          28 :     b = RgV_to_F2v(b);
    2495          28 :     a = F2m_F2c_gauss(a,b);
    2496          28 :     return a ? gc_upto(av, F2c_to_mod(a)): NULL;
    2497          21 :   default:
    2498          21 :     b = RgV_to_Flv(b, pp);
    2499          21 :     a = Flm_Flc_gauss(a, b, pp);
    2500          21 :     return a ? gc_upto(av, Flc_to_mod(a, pp)): NULL;
    2501             :   }
    2502             : }
    2503             : 
    2504             : static GEN
    2505         105 : RgM_solve_FpM(GEN a, GEN b, GEN p)
    2506             : {
    2507         105 :   pari_sp av = avma;
    2508             :   ulong pp;
    2509         105 :   a = RgM_Fp_init(a, p, &pp);
    2510         105 :   switch(pp)
    2511             :   {
    2512          35 :   case 0:
    2513          35 :     b = RgM_to_FpM(b, p);
    2514          35 :     a = FpM_gauss(a,b,p);
    2515          35 :     return a ? gc_upto(av, FpM_to_mod(a, p)): NULL;
    2516          28 :   case 2:
    2517          28 :     b = RgM_to_F2m(b);
    2518          28 :     a = F2m_gauss(a,b);
    2519          28 :     return a ? gc_upto(av, F2m_to_mod(a)): NULL;
    2520          42 :   default:
    2521          42 :     b = RgM_to_Flm(b, pp);
    2522          42 :     a = Flm_gauss(a,b,pp);
    2523          42 :     return a ? gc_upto(av, Flm_to_mod(a, pp)): NULL;
    2524             :   }
    2525             : }
    2526             : 
    2527             : /* Gaussan Elimination. If a is square, return a^(-1)*b;
    2528             :  * if a has more rows than columns and b is NULL, return c such that c a = Id.
    2529             :  * a is a (not necessarily square) matrix
    2530             :  * b is a matrix or column vector, NULL meaning: take the identity matrix,
    2531             :  *   effectively returning the inverse of a
    2532             :  * If a and b are empty, the result is the empty matrix.
    2533             :  *
    2534             :  * li: number of rows of a and b
    2535             :  * aco: number of columns of a
    2536             :  * bco: number of columns of b (if matrix)
    2537             :  */
    2538             : static GEN
    2539     1692257 : RgM_solve_basecase(GEN a, GEN b, pivot_fun pivot, GEN data)
    2540             : {
    2541     1692257 :   pari_sp av = avma;
    2542             :   long i, j, k, li, bco, aco;
    2543             :   int iscol;
    2544             :   GEN p, u;
    2545             : 
    2546     1692257 :   if (lg(a)-1 == 2 && nbrows(a) == 2)
    2547             :   { /* 2x2 matrix, start by inverting a */
    2548     1026277 :     GEN u = gcoeff(a,1,1), v = gcoeff(a,1,2);
    2549     1026277 :     GEN w = gcoeff(a,2,1), x = gcoeff(a,2,2);
    2550     1026277 :     GEN D = gsub(gmul(u,x), gmul(v,w)), ainv;
    2551     1026266 :     if (gequal0(D)) return NULL;
    2552     1026264 :     ainv = mkmat2(mkcol2(x, gneg(w)), mkcol2(gneg(v), u));
    2553     1026272 :     ainv = RgM_Rg_mul(ainv, ginv(D));
    2554     1026263 :     if (b) ainv = gmul(ainv, b);
    2555     1026265 :     return gc_upto(av, ainv);
    2556             :   }
    2557      665980 :   if (!init_gauss(a, &b, &aco, &li, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    2558      665983 :   a = RgM_shallowcopy(a);
    2559      665983 :   bco = lg(b)-1;
    2560      665983 :   if(DEBUGLEVEL>4) err_printf("Entering gauss\n");
    2561             : 
    2562      665983 :   p = NULL; /* gcc -Wall */
    2563     2291863 :   for (i=1; i<=aco; i++)
    2564             :   {
    2565             :     /* k is the line where we find the pivot */
    2566     2291853 :     k = pivot(a, data, i, NULL);
    2567     2291865 :     if (k > li) return NULL;
    2568     2291850 :     if (k != i)
    2569             :     { /* exchange the lines s.t. k = i */
    2570     1795200 :       for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    2571     1739280 :       for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
    2572             :     }
    2573     2291850 :     p = gcoeff(a,i,i);
    2574     2291850 :     if (i == aco) break;
    2575             : 
    2576     5118339 :     for (k=i+1; k<=li; k++)
    2577             :     {
    2578     3492481 :       GEN m = gcoeff(a,k,i);
    2579     3492481 :       if (!gequal0(m))
    2580             :       {
    2581     2837467 :         m = gdiv(m,p);
    2582    12115839 :         for (j=i+1; j<=aco; j++) _submul(gel(a,j),k,i,m);
    2583    11861975 :         for (j=1;   j<=bco; j++) _submul(gel(b,j),k,i,m);
    2584             :       }
    2585             :     }
    2586     1625858 :     if (gc_needed(av,1))
    2587             :     {
    2588          12 :       if(DEBUGMEM>1) pari_warn(warnmem,"gauss. i=%ld",i);
    2589          12 :       (void)gc_all(av,2, &a,&b);
    2590             :     }
    2591             :   }
    2592             : 
    2593      665975 :   if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
    2594      665975 :   u = cgetg(bco+1,t_MAT);
    2595     1931695 :   for (j=1; j<=bco; j++) gel(u,j) = get_col(a,gel(b,j),p,aco);
    2596      665958 :   return gc_GEN(av, iscol? gel(u,1): u);
    2597             : }
    2598             : 
    2599             : /* Returns gen_0 instead of NULL for 'no fast algorithm'. NULL is already
    2600             :  * reserved for 'not invertible' */
    2601             : static GEN
    2602     1177476 : RgM_RgC_solve_fast(GEN x, GEN y, pivot_fun *fun, GEN *data)
    2603             : {
    2604             :   GEN p, pol;
    2605     1177476 :   long pa, t = RgM_RgC_type(x, y, &p,&pol,&pa);
    2606     1177477 :   set_pivot_fun(fun, data, t, x, p);
    2607     1177477 :   switch(t)
    2608             :   {
    2609        9252 :     case t_INT:    return ZM_gauss(x, y);
    2610           7 :     case t_FRAC:   return QM_gauss(x, y);
    2611          63 :     case t_INTMOD: return RgM_RgC_solve_FpC(x, y, p);
    2612          42 :     case t_FFELT:  return FFM_FFC_gauss(x, y, pol);
    2613     1168113 :     default:       return gen_0;
    2614             :   }
    2615             : }
    2616             : static GEN
    2617       48783 : RgM_solve_fast(GEN x, GEN y, pivot_fun *fun, GEN *data)
    2618             : {
    2619             :   GEN p, pol;
    2620       48783 :   long pa, t = RgM_type2(x, y, &p,&pol,&pa);
    2621       48783 :   set_pivot_fun(fun, data, t, x, p);
    2622       48783 :   switch(t)
    2623             :   {
    2624          77 :     case t_INT:    return ZM_gauss(x, y);
    2625          14 :     case t_FRAC:   return QM_gauss(x, y);
    2626         105 :     case t_INTMOD: return RgM_solve_FpM(x, y, p);
    2627          56 :     case t_FFELT:  return FFM_gauss(x, y, pol);
    2628       48531 :     default:       return gen_0;
    2629             :   }
    2630             : }
    2631             : 
    2632             : GEN
    2633     1226259 : RgM_solve(GEN a, GEN b)
    2634             : {
    2635     1226259 :   pari_sp av = avma;
    2636             :   pivot_fun fun;
    2637             :   GEN u, data;
    2638     1226259 :   if (!b) return RgM_inv(a);
    2639       48783 :   u = typ(b)==t_MAT ? RgM_solve_fast(a, b, &fun, &data)
    2640     1226259 :                     : RgM_RgC_solve_fast(a, b, &fun, &data);
    2641     1226260 :   if (!u) return gc_NULL(av);
    2642     1226155 :   if (u != gen_0) return u;
    2643     1216644 :   return RgM_solve_basecase(a, b, fun, data);
    2644             : }
    2645             : 
    2646             : GEN
    2647          28 : RgM_div(GEN a, GEN b)
    2648             : {
    2649          28 :   pari_sp av = avma;
    2650          28 :   GEN u = RgM_solve(shallowtrans(b), shallowtrans(a));
    2651          28 :   if (!u) return gc_NULL(av);
    2652          21 :   return gc_GEN(av, shallowtrans(u));
    2653             : }
    2654             : 
    2655             : GEN
    2656      526350 : RgM_inv(GEN a)
    2657             : {
    2658             :   pivot_fun fun;
    2659      526350 :   GEN data, b = RgM_inv_fast(a, &fun, &data);
    2660      526342 :   return b==gen_0? RgM_solve_basecase(a, NULL, fun, data): b;
    2661             : }
    2662             : 
    2663             : /* assume dim A >= 1, A invertible + upper triangular  */
    2664             : static GEN
    2665     3230304 : RgM_inv_upper_ind(GEN A, long index)
    2666             : {
    2667     3230304 :   long n = lg(A)-1, i = index, j;
    2668     3230304 :   GEN u = zerocol(n);
    2669     3230303 :   gel(u,i) = ginv(gcoeff(A,i,i));
    2670     6534856 :   for (i--; i>0; i--)
    2671             :   {
    2672     3304551 :     pari_sp av = avma;
    2673     3304551 :     GEN m = gneg(gmul(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
    2674    14644142 :     for (j=i+2; j<=n; j++) m = gsub(m, gmul(gcoeff(A,i,j),gel(u,j)));
    2675     3304537 :     gel(u,i) = gc_upto(av, gdiv(m, gcoeff(A,i,i)));
    2676             :   }
    2677     3230305 :   return u;
    2678             : }
    2679             : GEN
    2680     1617186 : RgM_inv_upper(GEN A)
    2681             : {
    2682             :   long i, l;
    2683     1617186 :   GEN B = cgetg_copy(A, &l);
    2684     4847485 :   for (i = 1; i < l; i++) gel(B,i) = RgM_inv_upper_ind(A, i);
    2685     1617181 :   return B;
    2686             : }
    2687             : 
    2688             : static GEN
    2689     4517002 : split_realimag_col(GEN z, long r1, long r2)
    2690             : {
    2691     4517002 :   long i, ru = r1+r2;
    2692     4517002 :   GEN x = cgetg(ru+r2+1,t_COL), y = x + r2;
    2693    12538021 :   for (i=1; i<=r1; i++) {
    2694     8021015 :     GEN a = gel(z,i);
    2695     8021015 :     if (typ(a) == t_COMPLEX) a = gel(a,1); /* paranoia: a should be real */
    2696     8021015 :     gel(x,i) = a;
    2697             :   }
    2698     7224303 :   for (   ; i<=ru; i++) {
    2699     2707297 :     GEN b, a = gel(z,i);
    2700     2707297 :     if (typ(a) == t_COMPLEX) { b = gel(a,2); a = gel(a,1); } else b = gen_0;
    2701     2707297 :     gel(x,i) = a;
    2702     2707297 :     gel(y,i) = b;
    2703             :   }
    2704     4517006 :   return x;
    2705             : }
    2706             : GEN
    2707     2570175 : split_realimag(GEN x, long r1, long r2)
    2708             : {
    2709     2570175 :   if (typ(x) == t_COL) return split_realimag_col(x,r1,r2);
    2710     4502761 :   pari_APPLY_same(split_realimag_col(gel(x,i), r1, r2));
    2711             : }
    2712             : 
    2713             : /* assume M = (r1+r2) x (r1+2r2) matrix and y compatible vector or matrix
    2714             :  * r1 first lines of M,y are real. Solve the system obtained by splitting
    2715             :  * real and imaginary parts. */
    2716             : GEN
    2717     1215494 : RgM_solve_realimag(GEN M, GEN y)
    2718             : {
    2719     1215494 :   long l = lg(M), r2 = l - lgcols(M), r1 = l-1 - 2*r2;
    2720     1215494 :   return RgM_solve(split_realimag(M, r1,r2),
    2721             :                    split_realimag(y, r1,r2));
    2722             : }
    2723             : 
    2724             : GEN
    2725         434 : gauss(GEN a, GEN b)
    2726             : {
    2727             :   GEN z;
    2728         434 :   long t = typ(b);
    2729         434 :   if (typ(a)!=t_MAT) pari_err_TYPE("gauss",a);
    2730         434 :   if (t!=t_COL && t!=t_MAT) pari_err_TYPE("gauss",b);
    2731         434 :   z = RgM_solve(a,b);
    2732         434 :   if (!z) pari_err_INV("gauss",a);
    2733         329 :   return z;
    2734             : }
    2735             : 
    2736             : /* #C = n, C[z[i]] = K[i], complete by 0s */
    2737             : static GEN
    2738          14 : RgC_inflate(GEN K, GEN z, long n)
    2739             : {
    2740          14 :   GEN c = zerocol(n);
    2741          14 :   long j, l = lg(K);
    2742          28 :   for (j = 1; j < l; j++) gel(c, z[j]) = gel(K, j);
    2743          14 :   return c;
    2744             : }
    2745             : /* in place: C[i] *= cB / v[i] */
    2746             : static void
    2747        6314 : QC_normalize(GEN C, GEN v, GEN cB)
    2748             : {
    2749        6314 :   long l = lg(C), i;
    2750       47838 :   for (i = 1; i < l; i++)
    2751             :   {
    2752       41524 :     GEN c = cB, k = gel(C,i), d = gel(v,i);
    2753       41524 :     if (d)
    2754             :     {
    2755       24619 :       if (isintzero(d)) { gel(C,i) = gen_0; continue; }
    2756       24619 :       c = div_content(c, d);
    2757             :     }
    2758       41524 :     gel(C,i) = c? gmul(k,c): k;
    2759             :   }
    2760        6314 : }
    2761             : 
    2762             : /* same as above, M rational; if flag = 1, call indexrank and return 1 sol */
    2763             : GEN
    2764        6307 : QM_gauss_i(GEN M, GEN B, long flag)
    2765             : {
    2766        6307 :   pari_sp av = avma;
    2767             :   long i, l, n;
    2768        6307 :   int col = typ(B) == t_COL;
    2769        6307 :   GEN K, cB, N = cgetg_copy(M, &l), v = cgetg(l, t_VEC), z2 = NULL;
    2770             : 
    2771       47859 :   for (i = 1; i < l; i++)
    2772       41552 :     gel(N,i) = Q_primitive_part(gel(M,i), &gel(v,i));
    2773        6307 :   if (flag)
    2774             :   {
    2775         329 :     GEN z = ZM_indexrank(N), z1 = gel(z,1);
    2776         329 :     z2 = gel(z,2);
    2777         329 :     N = shallowmatextract(N, z1, z2);
    2778         329 :     B = col? vecpermute(B,z1): rowpermute(B,z1);
    2779         329 :     if (lg(z2) == l) z2 = NULL; else v = vecpermute(v, z2);
    2780             :   }
    2781        6307 :   B = Q_primitive_part(B, &cB);
    2782        6307 :   K = ZM_gauss(N, B); if (!K) return gc_NULL(av);
    2783        6307 :   n = l - 1;
    2784        6307 :   if (col)
    2785             :   {
    2786        6279 :     QC_normalize(K, v, cB);
    2787        6279 :     if (z2) K = RgC_inflate(K, z2, n);
    2788             :   }
    2789             :   else
    2790             :   {
    2791          28 :     long lK = lg(K);
    2792          63 :     for (i = 1; i < lK; i++)
    2793             :     {
    2794          35 :       QC_normalize(gel(K,i), v, cB);
    2795          35 :       if (z2) gel(K,i) = RgC_inflate(gel(K,i), z2, n);
    2796             :     }
    2797             :   }
    2798        6307 :   return gc_GEN(av, K);
    2799             : }
    2800             : GEN
    2801        5978 : QM_gauss(GEN M, GEN B) { return QM_gauss_i(M, B, 0); }
    2802             : 
    2803             : static GEN
    2804      794207 : ZM_inv_slice(GEN A, GEN P, GEN *mod)
    2805             : {
    2806      794207 :   pari_sp av = avma;
    2807      794207 :   long i, n = lg(P)-1;
    2808             :   GEN H, T;
    2809      794207 :   if (n == 1)
    2810             :   {
    2811      761561 :     ulong p = uel(P,1);
    2812      761561 :     GEN Hp, a = ZM_to_Flm(A, p);
    2813      761561 :     Hp = Flm_adjoint(a, p);
    2814      761559 :     Hp = gc_upto(av, Flm_to_ZM(Hp));
    2815      761561 :     *mod = utoipos(p); return Hp;
    2816             :   }
    2817       32646 :   T = ZV_producttree(P);
    2818       32646 :   A = ZM_nv_mod_tree(A, P, T);
    2819       32646 :   H = cgetg(n+1, t_VEC);
    2820      181998 :   for(i=1; i <= n; i++)
    2821      149352 :     gel(H,i) = Flm_adjoint(gel(A, i), uel(P,i));
    2822       32646 :   H = nmV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    2823       32646 :   *mod = gmael(T, lg(T)-1, 1); return gc_all(av, 2, &H, mod);
    2824             : }
    2825             : 
    2826             : static GEN
    2827      720797 : RgM_true_Hadamard(GEN a)
    2828             : {
    2829      720797 :   pari_sp av = avma;
    2830      720797 :   long n = lg(a)-1, i;
    2831             :   GEN B;
    2832      720797 :   if (n == 0) return gen_1;
    2833      720797 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    2834      720799 :   B = gnorml2(gel(a,1));
    2835     2988504 :   for (i = 2; i <= n; i++) B = gmul(B, gnorml2(gel(a,i)));
    2836      720797 :   return gc_INT(av, ceil_safe(sqrtr(B)));
    2837             : }
    2838             : 
    2839             : GEN
    2840      794208 : ZM_inv_worker(GEN P, GEN A)
    2841             : {
    2842      794208 :   GEN V = cgetg(3, t_VEC);
    2843      794207 :   gel(V,1) = ZM_inv_slice(A, P, &gel(V,2));
    2844      794207 :   return V;
    2845             : }
    2846             : 
    2847             : static GEN
    2848       43531 : ZM_inv0(GEN A, GEN *pden)
    2849             : {
    2850       43531 :   if (pden) *pden = gen_1;
    2851       43531 :   (void)A; return cgetg(1, t_MAT);
    2852             : }
    2853             : static GEN
    2854      644315 : ZM_inv1(GEN A, GEN *pden)
    2855             : {
    2856      644315 :   GEN a = gcoeff(A,1,1);
    2857      644315 :   long s = signe(a);
    2858      644315 :   if (!s) return NULL;
    2859      644315 :   if (pden) *pden = absi(a);
    2860      644315 :   retmkmat(mkcol(s == 1? gen_1: gen_m1));
    2861             : }
    2862             : static GEN
    2863      725097 : ZM_inv2(GEN A, GEN *pden)
    2864             : {
    2865             :   GEN a, b, c, d, D, cA;
    2866             :   long s;
    2867      725097 :   A = Q_primitive_part(A, &cA);
    2868      725093 :   a = gcoeff(A,1,1); b = gcoeff(A,1,2);
    2869      725093 :   c = gcoeff(A,2,1); d = gcoeff(A,2,2);
    2870      725093 :   D = subii(mulii(a,d), mulii(b,c)); /* left on stack */
    2871      725088 :   s = signe(D);
    2872      725088 :   if (!s) return NULL;
    2873      725074 :   if (s < 0) D = negi(D);
    2874      725077 :   if (pden) *pden = mul_denom(D, cA);
    2875      725077 :   if (s > 0)
    2876      683427 :     retmkmat2(mkcol2(icopy(d), negi(c)), mkcol2(negi(b), icopy(a)));
    2877             :   else
    2878       41650 :     retmkmat2(mkcol2(negi(d), icopy(c)), mkcol2(icopy(b), negi(a)));
    2879             : }
    2880             : 
    2881             : /* to be used when denom(M^(-1)) << det(M) and a sharp multiple is
    2882             :  * not available. Return H primitive such that M*H = den*Id */
    2883             : GEN
    2884           0 : ZM_inv_ratlift(GEN M, GEN *pden)
    2885             : {
    2886           0 :   pari_sp av2, av = avma;
    2887             :   GEN Hp, q, H;
    2888             :   ulong p;
    2889           0 :   long m = lg(M)-1;
    2890             :   forprime_t S;
    2891             :   pari_timer ti;
    2892             : 
    2893           0 :   if (m == 0) return ZM_inv0(M,pden);
    2894           0 :   if (m == 1 && nbrows(M)==1) return ZM_inv1(M,pden);
    2895           0 :   if (m == 2 && nbrows(M)==2) return ZM_inv2(M,pden);
    2896             : 
    2897           0 :   if (DEBUGLEVEL>5) timer_start(&ti);
    2898           0 :   init_modular_big(&S);
    2899           0 :   av2 = avma;
    2900           0 :   H = NULL;
    2901           0 :   while ((p = u_forprime_next(&S)))
    2902             :   {
    2903             :     GEN Mp, B, Hr;
    2904           0 :     Mp = ZM_to_Flm(M,p);
    2905           0 :     Hp = Flm_inv_sp(Mp, NULL, p);
    2906           0 :     if (!Hp) continue;
    2907           0 :     if (!H)
    2908             :     {
    2909           0 :       H = ZM_init_CRT(Hp, p);
    2910           0 :       q = utoipos(p);
    2911             :     }
    2912             :     else
    2913           0 :       ZM_incremental_CRT(&H, Hp, &q, p);
    2914           0 :     B = sqrti(shifti(q,-1));
    2915           0 :     Hr = FpM_ratlift(H,q,B,B,NULL);
    2916           0 :     if (DEBUGLEVEL>5)
    2917           0 :       timer_printf(&ti,"ZM_inv mod %lu (ratlift=%ld)", p,!!Hr);
    2918           0 :     if (Hr) {/* DONE ? */
    2919           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    2920           0 :       if (ZM_isscalar(ZM_mul(Hl, M), *pden)) { H = Hl; break; }
    2921             :     }
    2922             : 
    2923           0 :     if (gc_needed(av,2))
    2924             :     {
    2925           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv_ratlift");
    2926           0 :       (void)gc_all(av2, 2, &H, &q);
    2927             :     }
    2928             :   }
    2929           0 :   if (!*pden) *pden = gen_1;
    2930           0 :   return gc_all(av, 2, &H, pden);
    2931             : }
    2932             : 
    2933             : GEN
    2934       76804 : FpM_ratlift_worker(GEN A, GEN mod, GEN B)
    2935             : {
    2936             :   long l, i;
    2937       76804 :   GEN H = cgetg_copy(A, &l);
    2938      161612 :   for (i = 1; i < l; i++)
    2939             :   {
    2940       84813 :      GEN c = FpC_ratlift(gel(A,i), mod, B, B, NULL);
    2941       84808 :      gel(H,i) = c? c: gen_0;
    2942             :   }
    2943       76799 :   return H;
    2944             : }
    2945             : static int
    2946      765936 : can_ratlift(GEN x, GEN mod, GEN B)
    2947             : {
    2948      765936 :   pari_sp av = avma;
    2949             :   GEN a, b;
    2950      765936 :   return gc_bool(av, Fp_ratlift(x, mod, B, B, &a,&b));
    2951             : }
    2952             : static GEN
    2953     2738408 : FpM_ratlift_parallel(GEN A, GEN mod, GEN B)
    2954             : {
    2955     2738408 :   pari_sp av = avma;
    2956             :   GEN worker;
    2957     2738408 :   long i, l = lg(A), m = mt_nbthreads();
    2958     2738408 :   int test = !!B;
    2959             : 
    2960     2738408 :   if (l == 1 || lgcols(A) == 1) return gcopy(A);
    2961     2738408 :   if (!B) B = sqrti(shifti(mod,-1));
    2962     2738364 :   if (m == 1 || l == 2 || lgcols(A) < 10)
    2963             :   {
    2964     2730581 :     A = FpM_ratlift(A, mod, B, B, NULL);
    2965     2730616 :     return A? A: gc_NULL(av);
    2966             :   }
    2967             :   /* test one coefficient first */
    2968        7783 :   if (test && !can_ratlift(gcoeff(A,1,1), mod, B)) return gc_NULL(av);
    2969        7665 :   worker = snm_closure(is_entry("_FpM_ratlift_worker"), mkvec2(mod,B));
    2970        7665 :   A = gen_parapply_slice(worker, A, m);
    2971       84030 :   for (i = 1; i < l; i++) if (typ(gel(A,i)) != t_COL) return gc_NULL(av);
    2972        6657 :   return A;
    2973             : }
    2974             : 
    2975             : static GEN
    2976      758673 : ZM_adj_ratlift(GEN A, GEN H, GEN mod, GEN T)
    2977             : {
    2978      758673 :   pari_sp av = avma;
    2979             :   GEN B, D, g;
    2980      758673 :   D = ZMrow_ZC_mul(H, gel(A,1), 1);
    2981      758672 :   if (T) D = mulii(T, D);
    2982      758672 :   g = gcdii(D, mod);
    2983      758671 :   if (!equali1(g))
    2984             :   {
    2985          14 :     mod = diviiexact(mod, g);
    2986          14 :     H = FpM_red(H, mod);
    2987             :   }
    2988      758671 :   D = Fp_inv(Fp_red(D, mod), mod);
    2989             :   /* test 1 coeff first */
    2990      758669 :   B = sqrti(shifti(mod,-1));
    2991      758665 :   if (!can_ratlift(Fp_mul(D, gcoeff(A,1,1), mod), mod, B)) return gc_NULL(av);
    2992      737507 :   H = FpM_Fp_mul(H, D, mod);
    2993      737501 :   H = FpM_ratlift_parallel(H, mod, B);
    2994      737505 :   return H? H: gc_NULL(av);
    2995             : }
    2996             : 
    2997             : /* if (T) return T A^(-1) in Mn(Q), else B in Mn(Z) such that A B = den*Id */
    2998             : static GEN
    2999     2133744 : ZM_inv_i(GEN A, GEN *pden, GEN T)
    3000             : {
    3001     2133744 :   pari_sp av = avma;
    3002     2133744 :   long m = lg(A)-1, n, k1 = 1, k2;
    3003     2133744 :   GEN H = NULL, D, H1 = NULL, mod1 = NULL, worker;
    3004             :   ulong bnd, mask;
    3005             :   forprime_t S;
    3006             :   pari_timer ti;
    3007             : 
    3008     2133744 :   if (m == 0) return ZM_inv0(A,pden);
    3009     2090213 :   if (pden) *pden = gen_1;
    3010     2090213 :   if (nbrows(A) < m) return NULL;
    3011     2090206 :   if (m == 1 && nbrows(A)==1 && !T) return ZM_inv1(A,pden);
    3012     1445891 :   if (m == 2 && nbrows(A)==2 && !T) return ZM_inv2(A,pden);
    3013             : 
    3014      720794 :   if (DEBUGLEVEL>=5) timer_start(&ti);
    3015      720794 :   init_modular_big(&S);
    3016      720797 :   bnd = expi(RgM_true_Hadamard(A));
    3017      720796 :   worker = snm_closure(is_entry("_ZM_inv_worker"), mkvec(A));
    3018      720799 :   gen_inccrt("ZM_inv_r", worker, NULL, k1, 0, &S, &H1, &mod1, nmV_chinese_center, FpM_center);
    3019      720799 :   n = (bnd+1)/expu(S.p)+1;
    3020      720799 :   if (DEBUGLEVEL>=5) timer_printf(&ti,"inv (%ld/%ld primes)", k1, n);
    3021      720799 :   mask = quadratic_prec_mask(n);
    3022      720799 :   for (k2 = 0;;)
    3023       66162 :   {
    3024             :     GEN Hr;
    3025      786961 :     if (k2 > 0)
    3026             :     {
    3027       58835 :       gen_inccrt("ZM_inv_r", worker, NULL, k2, 0, &S, &H1, &mod1,nmV_chinese_center,FpM_center);
    3028       58835 :       k1 += k2;
    3029       58835 :       if (DEBUGLEVEL>=5) timer_printf(&ti,"CRT (%ld/%ld primes)", k1, n);
    3030             :     }
    3031      786961 :     if (mask == 1) break;
    3032      758673 :     k2 = (mask&1UL) ? k1-1: k1;
    3033      758673 :     mask >>= 1;
    3034             : 
    3035      758673 :     Hr = ZM_adj_ratlift(A, H1, mod1, T);
    3036      758670 :     if (DEBUGLEVEL>=5) timer_printf(&ti,"ratlift (%ld/%ld primes)", k1, n);
    3037      758671 :     if (Hr) {/* DONE ? */
    3038      696383 :       GEN Hl = Q_primpart(Hr), R = ZM_mul(Hl, A), d = gcoeff(R,1,1);
    3039      696383 :       if (gsigne(d) < 0) { d = gneg(d); Hl = ZM_neg(Hl); }
    3040      696383 :       if (DEBUGLEVEL>=5) timer_printf(&ti,"mult (%ld/%ld primes)", k1, n);
    3041      696383 :       if (equali1(d))
    3042             :       {
    3043      595799 :         if (ZM_isidentity(R)) { H = Hl; break; }
    3044             :       }
    3045      100584 :       else if (ZM_isscalar(R, d))
    3046             :       {
    3047       96712 :         if (T) T = gdiv(T,d);
    3048       89635 :         else if (pden) *pden = d;
    3049       96712 :         H = Hl; break;
    3050             :       }
    3051             :     }
    3052             :   }
    3053      720798 :   if (!H)
    3054             :   {
    3055             :     GEN d;
    3056       28288 :     H = H1;
    3057       28288 :     D = ZMrow_ZC_mul(H, gel(A,1), 1);
    3058       28288 :     if (signe(D)==0) pari_err_INV("ZM_inv", A);
    3059       28288 :     if (T) T = gdiv(T, D);
    3060             :     else
    3061             :     {
    3062       27139 :       d = gcdii(Q_content_safe(H), D);
    3063       27139 :       if (signe(D) < 0) d = negi(d);
    3064       27139 :       if (!equali1(d))
    3065             :       {
    3066       15421 :         H = ZM_Z_divexact(H, d);
    3067       15421 :         D = diviiexact(D, d);
    3068             :       }
    3069       27139 :       if (pden) *pden = D;
    3070             :     }
    3071             :   }
    3072      720798 :   if (T && !isint1(T)) H = ZM_Q_mul(H, T);
    3073      720798 :   return gc_all(av, pden? 2: 1, &H, pden);
    3074             : }
    3075             : GEN
    3076     2068417 : ZM_inv(GEN A, GEN *pden) { return ZM_inv_i(A, pden, NULL); }
    3077             : 
    3078             : /* same as above, M rational */
    3079             : GEN
    3080       65325 : QM_inv(GEN M)
    3081             : {
    3082       65325 :   pari_sp av = avma;
    3083             :   GEN den, dM, K;
    3084       65325 :   M = Q_remove_denom(M, &dM);
    3085       65325 :   K = ZM_inv_i(M, &den, dM);
    3086       65325 :   if (!K) return gc_NULL(av);
    3087       65304 :   if (den && !equali1(den)) K = ZM_Q_mul(K, ginv(den));
    3088       65290 :   return gc_upto(av, K);
    3089             : }
    3090             : 
    3091             : static GEN
    3092      105428 : ZM_ker_filter(GEN A, GEN P)
    3093             : {
    3094      105428 :   long i, j, l = lg(A), n = 1, d = lg(gmael(A,1,1));
    3095      105428 :   GEN B, Q, D = gmael(A,1,2);
    3096      215597 :   for (i=2; i<l; i++)
    3097             :   {
    3098      110169 :     GEN Di = gmael(A,i,2);
    3099      110169 :     long di = lg(gmael(A,i,1));
    3100      110169 :     int c = vecsmall_lexcmp(D, Di);
    3101      110169 :     if (di==d && c==0) n++;
    3102       45588 :     else if (d > di || (di==d && c>0))
    3103       37680 :     { n = 1; d = di; D = Di; }
    3104             :   }
    3105      105428 :   B = cgetg(n+1, t_VEC);
    3106      105428 :   Q = cgetg(n+1, typ(P));
    3107      321025 :   for (i=1, j=1; i<l; i++)
    3108             :   {
    3109      215597 :     if (lg(gmael(A,i,1))==d &&  vecsmall_lexcmp(D, gmael(A,i,2))==0)
    3110             :     {
    3111      170009 :       gel(B,j) = gmael(A,i,1);
    3112      170009 :       Q[j] = P[i];
    3113      170009 :       j++;
    3114             :     }
    3115             :   }
    3116      105428 :   return mkvec3(B,Q,D);
    3117             : }
    3118             : 
    3119             : static GEN
    3120       69755 : ZM_ker_chinese(GEN A, GEN P, GEN *mod)
    3121             : {
    3122       69755 :   GEN BQD = ZM_ker_filter(A, P);
    3123       69755 :   return mkvec2(nmV_chinese_center(gel(BQD,1), gel(BQD,2), mod), gel(BQD,3));
    3124             : }
    3125             : 
    3126             : static GEN
    3127      133560 : ZM_ker_slice(GEN A, GEN P, GEN *mod)
    3128             : {
    3129      133560 :   pari_sp av = avma;
    3130      133560 :   long i, n = lg(P)-1;
    3131             :   GEN BQD, B, Q, D, H, HD, T;
    3132      133560 :   if (n == 1)
    3133             :   {
    3134       97887 :     ulong p = uel(P,1);
    3135       97887 :     GEN K = Flm_ker_sp(ZM_to_Flm(A, p), p, 2);
    3136       97887 :     *mod = utoipos(p); return mkvec2(Flm_to_ZM(gel(K,1)), gel(K,2));
    3137             :   }
    3138       35673 :   T = ZV_producttree(P);
    3139       35673 :   A = ZM_nv_mod_tree(A, P, T);
    3140       35673 :   H = cgetg(n+1, t_VEC);
    3141      111524 :   for(i=1 ; i <= n; i++)
    3142       75851 :     gel(H,i) = Flm_ker_sp(gel(A, i), P[i], 2);
    3143       35673 :   BQD = ZM_ker_filter(H, P);
    3144       35673 :   B = gel(BQD,1); Q = gel(BQD,2); D = gel(BQD, 3);
    3145       35673 :   if (lg(Q) != lg(P)) T = ZV_producttree(Q);
    3146       35673 :   H = nmV_chinese_center_tree_seq(B, Q, T, ZV_chinesetree(Q,T));
    3147       35673 :   *mod = gmael(T, lg(T)-1, 1);
    3148       35673 :   HD = mkvec2(H, D);
    3149       35673 :   return gc_all(av, 2, &HD, mod);
    3150             : }
    3151             : 
    3152             : GEN
    3153      133560 : ZM_ker_worker(GEN P, GEN A)
    3154             : {
    3155      133560 :   GEN V = cgetg(3, t_VEC);
    3156      133560 :   gel(V,1) = ZM_ker_slice(A, P, &gel(V,2));
    3157      133560 :   return V;
    3158             : }
    3159             : 
    3160             : /* assume lg(A) > 1 */
    3161             : static GEN
    3162       66629 : ZM_ker_i(GEN A)
    3163             : {
    3164             :   pari_sp av;
    3165       66629 :   long k, m = lg(A)-1;
    3166       66629 :   GEN HD = NULL, mod = gen_1, worker;
    3167             :   forprime_t S;
    3168             : 
    3169       66629 :   if (m >= 2*nbrows(A))
    3170             :   {
    3171        3059 :     GEN v = ZM_indexrank(A), y = gel(v,2), z = indexcompl(y, m);
    3172             :     GEN B, A1, A1i, d;
    3173        3059 :     A = rowpermute(A, gel(v,1)); /* same kernel */
    3174        3059 :     A1 = vecpermute(A, y); /* maximal rank submatrix */
    3175        3059 :     B = vecpermute(A, z);
    3176        3059 :     A1i = ZM_inv(A1, &d);
    3177        3059 :     if (!d) d = gen_1;
    3178        3059 :     B = vconcat(ZM_mul(ZM_neg(A1i), B), scalarmat_shallow(d, lg(B)-1));
    3179        3059 :     if (!gequal(y, identity_perm(lg(y)-1)))
    3180         685 :       B = rowpermute(B, perm_inv(shallowconcat(y,z)));
    3181        3059 :     return vec_Q_primpart(B);
    3182             :   }
    3183       63570 :   init_modular_big(&S);
    3184       63570 :   worker = snm_closure(is_entry("_ZM_ker_worker"), mkvec(A));
    3185       63570 :   av = avma;
    3186       63570 :   for (k = 1;; k <<= 1)
    3187       65542 :   {
    3188             :     pari_timer ti;
    3189             :     GEN H, Hr;
    3190      129112 :     gen_inccrt_i("ZM_ker", worker, NULL, (k+1)>>1, 0,
    3191             :                  &S, &HD, &mod, ZM_ker_chinese, NULL);
    3192      129112 :     (void)gc_all(av, 2, &HD, &mod);
    3193      146258 :     H = gel(HD, 1); if (lg(H) == 1) return H;
    3194       82688 :     if (DEBUGLEVEL >= 4) timer_start(&ti);
    3195       82688 :     Hr = FpM_ratlift_parallel(H, mod, NULL);
    3196       82688 :     if (DEBUGLEVEL >= 4) timer_printf(&ti,"ZM_ker: ratlift (%ld)",!!Hr);
    3197       82688 :     if (Hr)
    3198             :     {
    3199             :       GEN MH;
    3200       71743 :       Hr = vec_Q_primpart(Hr);
    3201       71743 :       MH = ZM_mul(A, Hr);
    3202       71743 :       if (DEBUGLEVEL >= 4) timer_printf(&ti,"ZM_ker: QM_mul");
    3203       71743 :       if (ZM_equal0(MH)) return Hr;
    3204             :     }
    3205             :   }
    3206             : }
    3207             : 
    3208             : GEN
    3209       49269 : ZM_ker(GEN M)
    3210             : {
    3211       49269 :   pari_sp av = avma;
    3212       49269 :   long l = lg(M)-1;
    3213       49269 :   if (l==0) return cgetg(1, t_MAT);
    3214       49269 :   if (lgcols(M)==1) return matid(l);
    3215       49269 :   return gc_GEN(av, ZM_ker_i(M));
    3216             : }
    3217             : 
    3218             : static GEN
    3219     2018708 : ZM_gauss_slice(GEN A, GEN B, GEN P, GEN *mod)
    3220             : {
    3221     2018708 :   pari_sp av = avma;
    3222     2018708 :   long i, n = lg(P)-1;
    3223             :   GEN H, T;
    3224     2018708 :   if (n == 1)
    3225             :   {
    3226     1946560 :     ulong p = uel(P,1);
    3227     1946560 :     GEN Hp = Flm_gauss(ZM_to_Flm(A, p) , ZM_to_Flm(B, p) ,p);
    3228     1946561 :     if (!Hp)  { *mod=gen_1; return zeromat(lg(A)-1,lg(B)-1); }
    3229     1946561 :     Hp = gc_upto(av, Flm_to_ZM(Hp));
    3230     1946564 :     *mod = utoipos(p); return Hp;
    3231             :   }
    3232       72148 :   T = ZV_producttree(P);
    3233       72148 :   A = ZM_nv_mod_tree(A, P, T);
    3234       72148 :   B = ZM_nv_mod_tree(B, P, T);
    3235       72148 :   H = cgetg(n+1, t_VEC);
    3236      452735 :   for(i=1; i <= n; i++)
    3237             :   {
    3238      380587 :     GEN Hi = Flm_gauss(gel(A, i), gel(B,i), uel(P,i));
    3239      380587 :     gel(H,i) = Hi ? Hi: zero_Flm(lg(A)-1,lg(B)-1);
    3240      380587 :     if (!Hi) uel(P,i)=1;
    3241             :   }
    3242       72148 :   H = nmV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    3243       72148 :   *mod = gmael(T, lg(T)-1, 1); return gc_all(av, 2, &H, mod);
    3244             : }
    3245             : 
    3246             : GEN
    3247     2018710 : ZM_gauss_worker(GEN P, GEN A, GEN B)
    3248             : {
    3249     2018710 :   GEN V = cgetg(3, t_VEC);
    3250     2018708 :   gel(V,1) = ZM_gauss_slice(A, B, P, &gel(V,2));
    3251     2018711 :   return V;
    3252             : }
    3253             : 
    3254             : /* assume lg(A) > 1 */
    3255             : static GEN
    3256     1710958 : ZM_gauss_i(GEN A, GEN B)
    3257             : {
    3258             :   pari_sp av;
    3259             :   long k, m, ncol;
    3260             :   int iscol;
    3261     1710958 :   GEN y, y1, y2, Hr, H = NULL, mod = gen_1, worker;
    3262             :   forprime_t S;
    3263     1710958 :   if (!init_gauss(A, &B, &m, &ncol, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
    3264     1710888 :   init_modular_big(&S);
    3265     1710890 :   y = ZM_indexrank(A); y1 = gel(y,1); y2 = gel(y,2);
    3266     1710893 :   if (lg(y2)-1 != m) return NULL;
    3267     1710865 :   A = rowpermute(A, y1);
    3268     1710865 :   B = rowpermute(B, y1);
    3269             :   /* a is square and invertible */
    3270     1710865 :   ncol = lg(B);
    3271     1710865 :   worker = snm_closure(is_entry("_ZM_gauss_worker"), mkvec2(A,B));
    3272     1710865 :   av = avma;
    3273     1710865 :   for (k = 1;; k <<= 1)
    3274      207357 :   {
    3275             :     pari_timer ti;
    3276     1918222 :     gen_inccrt_i("ZM_gauss", worker, NULL, (k+1)>>1 , m,
    3277             :                  &S, &H, &mod, nmV_chinese_center, FpM_center);
    3278     1918212 :     (void)gc_all(av, 2, &H, &mod);
    3279     1918223 :     if (DEBUGLEVEL >= 4) timer_start(&ti);
    3280     1918223 :     Hr = FpM_ratlift_parallel(H, mod, NULL);
    3281     1918207 :     if (DEBUGLEVEL >= 4) timer_printf(&ti,"ZM_gauss: ratlift (%ld)",!!Hr);
    3282     1918209 :     if (Hr)
    3283             :     {
    3284             :       GEN MH, c;
    3285     1765163 :       MH = ZM_mul(A, Q_remove_denom(Hr, &c));
    3286     1765153 :       if (DEBUGLEVEL >= 4) timer_printf(&ti,"ZM_gauss: QM_mul");
    3287     1765161 :       if (ZM_equal(MH, c ? ZM_Z_mul(B, c): B)) break;
    3288             :     }
    3289             :   }
    3290     1710855 :   return iscol ? gel(Hr, 1): Hr;
    3291             : }
    3292             : 
    3293             : GEN
    3294     1710953 : ZM_gauss(GEN A, GEN B)
    3295             : {
    3296     1710953 :   pari_sp av = avma;
    3297     1710953 :   GEN C = ZM_gauss_i(A,B);
    3298     1710951 :   return C ? gc_GEN(av, C): NULL;
    3299             : }
    3300             : 
    3301             : GEN
    3302       18235 : QM_ker(GEN M)
    3303             : {
    3304       18235 :   pari_sp av = avma;
    3305       18235 :   long l = lg(M)-1;
    3306       18235 :   if (l==0) return cgetg(1, t_MAT);
    3307       18193 :   if (lgcols(M)==1) return matid(l);
    3308       17276 :   return gc_GEN(av, ZM_ker_i(row_Q_primpart(M)));
    3309             : }
    3310             : 
    3311             : /* x a ZM. Return a multiple of the determinant of the lattice generated by
    3312             :  * the columns of x. From Algorithm 2.2.6 in GTM138 */
    3313             : GEN
    3314       49964 : detint(GEN A)
    3315             : {
    3316       49964 :   if (typ(A) != t_MAT) pari_err_TYPE("detint",A);
    3317       49964 :   RgM_check_ZM(A, "detint");
    3318       49964 :   return ZM_detmult(A);
    3319             : }
    3320             : GEN
    3321      166024 : ZM_detmult(GEN A)
    3322             : {
    3323      166024 :   pari_sp av1, av = avma;
    3324             :   GEN B, c, v, piv;
    3325      166024 :   long rg, i, j, k, m, n = lg(A) - 1;
    3326             : 
    3327      166024 :   if (!n) return gen_1;
    3328      166024 :   m = nbrows(A);
    3329      166024 :   if (n < m) return gen_0;
    3330      165947 :   c = zero_zv(m);
    3331      165947 :   av1 = avma;
    3332      165947 :   B = zeromatcopy(m,m);
    3333      165947 :   v = cgetg(m+1, t_COL);
    3334      165946 :   piv = gen_1; rg = 0;
    3335      718511 :   for (k=1; k<=n; k++)
    3336             :   {
    3337      718497 :     GEN pivprec = piv;
    3338      718497 :     long t = 0;
    3339     5335499 :     for (i=1; i<=m; i++)
    3340             :     {
    3341     4617006 :       pari_sp av2 = avma;
    3342             :       GEN vi;
    3343     4617006 :       if (c[i]) continue;
    3344             : 
    3345     2667998 :       vi = mulii(piv, gcoeff(A,i,k));
    3346    28320855 :       for (j=1; j<=m; j++)
    3347    25652773 :         if (c[j]) vi = addii(vi, mulii(gcoeff(B,j,i),gcoeff(A,j,k)));
    3348     2668082 :       if (!t && signe(vi)) t = i;
    3349     2668082 :       gel(v,i) = gc_INT(av2, vi);
    3350             :     }
    3351      718493 :     if (!t) continue;
    3352             :     /* at this point c[t] = 0 */
    3353             : 
    3354      718402 :     if (++rg >= m) { /* full rank; mostly done */
    3355      165932 :       GEN det = gel(v,t); /* last on stack */
    3356      165932 :       if (++k > n)
    3357      165800 :         det = absi(det);
    3358             :       else
    3359             :       {
    3360             :         /* improve further; at this point c[i] is set for all i != t */
    3361         132 :         gcoeff(B,t,t) = piv; v = centermod(gel(B,t), det);
    3362         418 :         for ( ; k<=n; k++)
    3363         286 :           det = gcdii(det, ZV_dotproduct(v, gel(A,k)));
    3364             :       }
    3365      165933 :       return gc_INT(av, det);
    3366             :     }
    3367             : 
    3368      552470 :     piv = gel(v,t);
    3369     4450572 :     for (i=1; i<=m; i++)
    3370             :     {
    3371             :       GEN mvi;
    3372     3898103 :       if (c[i] || i == t) continue;
    3373             : 
    3374     1949051 :       gcoeff(B,t,i) = mvi = negi(gel(v,i));
    3375    22981570 :       for (j=1; j<=m; j++)
    3376    21032520 :         if (c[j]) /* implies j != t */
    3377             :         {
    3378     5711487 :           pari_sp av2 = avma;
    3379     5711487 :           GEN z = addii(mulii(gcoeff(B,j,i), piv), mulii(gcoeff(B,j,t), mvi));
    3380     5711487 :           if (rg > 1) z = diviiexact(z, pivprec);
    3381     5711487 :           gcoeff(B,j,i) = gc_INT(av2, z);
    3382             :         }
    3383             :     }
    3384      552469 :     c[t] = k;
    3385      552469 :     if (gc_needed(av,1))
    3386             :     {
    3387           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"detint. k=%ld",k);
    3388           0 :       (void)gc_all(av1, 2, &piv,&B); v = zerovec(m);
    3389             :     }
    3390             :   }
    3391          14 :   return gc_const(av, gen_0);
    3392             : }
    3393             : 
    3394             : /* Reduce x modulo (invertible) y */
    3395             : GEN
    3396        9133 : closemodinvertible(GEN x, GEN y)
    3397             : {
    3398        9133 :   return gmul(y, ground(RgM_solve(y,x)));
    3399             : }
    3400             : GEN
    3401           7 : reducemodinvertible(GEN x, GEN y)
    3402             : {
    3403           7 :   return gsub(x, closemodinvertible(x,y));
    3404             : }
    3405             : GEN
    3406           0 : reducemodlll(GEN x,GEN y)
    3407             : {
    3408           0 :   return reducemodinvertible(x, ZM_lll(y, 0.75, LLL_INPLACE));
    3409             : }
    3410             : 
    3411             : /*******************************************************************/
    3412             : /*                                                                 */
    3413             : /*                    KERNEL of an m x n matrix                    */
    3414             : /*          return n - rk(x) linearly independent vectors          */
    3415             : /*                                                                 */
    3416             : /*******************************************************************/
    3417             : static GEN
    3418          28 : RgM_deplin_i(GEN x0)
    3419             : {
    3420          28 :   pari_sp av = avma, av2;
    3421          28 :   long i, j, k, nl, nc = lg(x0)-1;
    3422             :   GEN D, x, y, c, l, d, ck;
    3423             : 
    3424          28 :   if (!nc) return NULL;
    3425          28 :   nl = nbrows(x0);
    3426          28 :   c = zero_zv(nl);
    3427          28 :   l = cgetg(nc+1, t_VECSMALL); /* not initialized */
    3428          28 :   av2 = avma;
    3429          28 :   x = RgM_shallowcopy(x0);
    3430          28 :   d = const_vec(nl, gen_1); /* pivot list */
    3431          28 :   ck = NULL; /* gcc -Wall */
    3432          98 :   for (k=1; k<=nc; k++)
    3433             :   {
    3434          91 :     ck = gel(x,k);
    3435         196 :     for (j=1; j<k; j++)
    3436             :     {
    3437         105 :       GEN cj = gel(x,j), piv = gel(d,j), q = gel(ck,l[j]);
    3438         420 :       for (i=1; i<=nl; i++)
    3439         315 :         if (i!=l[j]) gel(ck,i) = gsub(gmul(piv, gel(ck,i)), gmul(q, gel(cj,i)));
    3440             :     }
    3441             : 
    3442          91 :     i = gauss_get_pivot_NZ(x, NULL, k, c);
    3443          91 :     if (i > nl) break;
    3444          70 :     if (gc_needed(av,1))
    3445             :     {
    3446           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"deplin k = %ld/%ld",k,nc);
    3447           0 :       (void)gc_all(av2, 2, &x, &d);
    3448           0 :       ck = gel(x,k);
    3449             :     }
    3450          70 :     gel(d,k) = gel(ck,i);
    3451          70 :     c[i] = k; l[k] = i; /* pivot d[k] in x[i,k] */
    3452             :   }
    3453          28 :   if (k > nc) return gc_NULL(av);
    3454          21 :   if (k == 1) { set_avma(av); return scalarcol_shallow(gen_1,nc); }
    3455          21 :   y = cgetg(nc+1,t_COL);
    3456          21 :   gel(y,1) = gcopy(gel(ck, l[1]));
    3457          49 :   for (D=gel(d,1),j=2; j<k; j++)
    3458             :   {
    3459          28 :     gel(y,j) = gmul(gel(ck, l[j]), D);
    3460          28 :     D = gmul(D, gel(d,j));
    3461             :   }
    3462          21 :   gel(y,j) = gneg(D);
    3463          21 :   for (j++; j<=nc; j++) gel(y,j) = gen_0;
    3464          21 :   y = primitive_part(y, &c);
    3465          21 :   return c? gc_upto(av, y): gc_GEN(av, y);
    3466             : }
    3467             : static GEN
    3468           0 : RgV_deplin(GEN v)
    3469             : {
    3470           0 :   pari_sp av = avma;
    3471           0 :   long n = lg(v)-1;
    3472           0 :   GEN y, p = NULL;
    3473           0 :   if (n <= 1)
    3474             :   {
    3475           0 :     if (n == 1 && gequal0(gel(v,1))) return mkcol(gen_1);
    3476           0 :     return cgetg(1, t_COL);
    3477             :   }
    3478           0 :   if (gequal0(gel(v,1))) return scalarcol_shallow(gen_1, n);
    3479           0 :   v = primpart(mkvec2(gel(v,1),gel(v,2)));
    3480           0 :   if (RgV_is_FpV(v, &p) && p) v = centerlift(v);
    3481           0 :   y = zerocol(n);
    3482           0 :   gel(y,1) = gneg(gel(v,2));
    3483           0 :   gel(y,2) = gcopy(gel(v,1));
    3484           0 :   return gc_upto(av, y);
    3485             : 
    3486             : }
    3487             : 
    3488             : static GEN
    3489         105 : RgM_deplin_FpM(GEN x, GEN p)
    3490             : {
    3491         105 :   pari_sp av = avma;
    3492             :   ulong pp;
    3493         105 :   x = RgM_Fp_init3(x, p, &pp);
    3494         105 :   switch(pp)
    3495             :   {
    3496          35 :   case 0:
    3497          35 :     x = FpM_ker_gen(x,p,1);
    3498          35 :     if (!x) return gc_NULL(av);
    3499          21 :     x = FpC_center(x,p,shifti(p,-1));
    3500          21 :     break;
    3501          14 :   case 2:
    3502          14 :     x = F2m_ker_sp(x,1);
    3503          14 :     if (!x) return gc_NULL(av);
    3504           7 :     x = F2c_to_ZC(x); break;
    3505           0 :   case 3:
    3506           0 :     x = F3m_ker_sp(x,1);
    3507           0 :     if (!x) return gc_NULL(av);
    3508           0 :     x = F3c_to_ZC(x); break;
    3509          56 :   default:
    3510          56 :     x = Flm_ker_sp(x,pp,1);
    3511          56 :     if (!x) return gc_NULL(av);
    3512          35 :     x = Flv_center(x, pp, pp>>1);
    3513          35 :     x = zc_to_ZC(x);
    3514          35 :     break;
    3515             :   }
    3516          63 :   return gc_upto(av, x);
    3517             : }
    3518             : 
    3519             : /* FIXME: implement direct modular ZM_deplin ? */
    3520             : static GEN
    3521         119 : QM_deplin(GEN M)
    3522             : {
    3523         119 :   pari_sp av = avma;
    3524         119 :   long l = lg(M)-1;
    3525             :   GEN k;
    3526         119 :   if (l==0) return NULL;
    3527          84 :   if (lgcols(M)==1) return col_ei(l, 1);
    3528          84 :   k = ZM_ker_i(row_Q_primpart(M));
    3529          84 :   if (lg(k)== 1) return gc_NULL(av);
    3530          70 :   return gc_GEN(av, gel(k,1));
    3531             : }
    3532             : 
    3533             : static GEN
    3534          49 : RgM_deplin_FqM(GEN x, GEN pol, GEN p)
    3535             : {
    3536          49 :   pari_sp av = avma;
    3537          49 :   GEN b, T = RgX_to_FpX(pol, p);
    3538          49 :   if (signe(T) == 0) pari_err_OP("deplin",x,pol);
    3539          49 :   b = FqM_deplin(RgM_to_FqM(x, T, p), T, p);
    3540          49 :   if (!b) return gc_NULL(av);
    3541          35 :   return gc_upto(av, b);
    3542             : }
    3543             : 
    3544             : /* Returns gen_0 instead of NULL for 'no fast algorithm'. NULL is already
    3545             :  * reserved for 'not invertible' */
    3546             : static GEN
    3547         385 : RgM_deplin_fast(GEN x)
    3548             : {
    3549             :   GEN p, pol;
    3550         385 :   long pa, t = RgM_type(x, &p,&pol,&pa);
    3551         385 :   switch(t)
    3552             :   {
    3553         119 :     case t_INT:    /* fall through */
    3554         119 :     case t_FRAC:   return QM_deplin(x);
    3555          84 :     case t_FFELT:  return FFM_deplin(x, pol);
    3556         105 :     case t_INTMOD: return RgM_deplin_FpM(x, p);
    3557          49 :     case RgX_type_code(t_POLMOD, t_INTMOD):
    3558          49 :                    return RgM_deplin_FqM(x, pol, p);
    3559          28 :     default:       return gen_0;
    3560             :   }
    3561             : }
    3562             : 
    3563             : static GEN
    3564         385 : RgM_deplin(GEN x)
    3565             : {
    3566         385 :   GEN z = RgM_deplin_fast(x);
    3567         385 :   if (z!= gen_0) return z;
    3568          28 :   return RgM_deplin_i(x);
    3569             : }
    3570             : 
    3571             : GEN
    3572         385 : deplin(GEN x)
    3573             : {
    3574         385 :   switch(typ(x))
    3575             :   {
    3576         385 :     case t_MAT:
    3577             :     {
    3578         385 :       GEN z = RgM_deplin(x);
    3579         385 :       if (z) return z;
    3580         147 :       return cgetg(1, t_COL);
    3581             :     }
    3582           0 :     case t_VEC: return RgV_deplin(x);
    3583           0 :     default: pari_err_TYPE("deplin",x);
    3584             :   }
    3585             :   return NULL;/*LCOV_EXCL_LINE*/
    3586             : }
    3587             : 
    3588             : /*******************************************************************/
    3589             : /*                                                                 */
    3590             : /*         GAUSS REDUCTION OF MATRICES  (m lines x n cols)         */
    3591             : /*           (kernel, image, complementary image, rank)            */
    3592             : /*                                                                 */
    3593             : /*******************************************************************/
    3594             : /* return the transform of x under a standard Gauss pivot.
    3595             :  * x0 is a reference point when guessing whether x[i,j] ~ 0
    3596             :  * (iff x[i,j] << x0[i,j])
    3597             :  * Set r = dim ker(x). d[k] contains the index of the first nonzero pivot
    3598             :  * in column k */
    3599             : static GEN
    3600        1271 : gauss_pivot_ker(GEN x, GEN *dd, long *rr, pivot_fun pivot, GEN data)
    3601             : {
    3602             :   GEN c, d, p;
    3603             :   pari_sp av;
    3604             :   long i, j, k, r, t, n, m;
    3605             : 
    3606        1271 :   n=lg(x)-1; if (!n) { *dd=NULL; *rr=0; return cgetg(1,t_MAT); }
    3607        1271 :   m=nbrows(x); r=0;
    3608        1271 :   x = RgM_shallowcopy(x);
    3609        1271 :   c = zero_zv(m);
    3610        1271 :   d = cgetg(n+1,t_VECSMALL);
    3611        1271 :   av=avma;
    3612        7475 :   for (k=1; k<=n; k++)
    3613             :   {
    3614        6204 :     j = pivot(x, data, k, c);
    3615        6204 :     if (j > m)
    3616             :     {
    3617        1463 :       r++; d[k]=0;
    3618        6496 :       for(j=1; j<k; j++)
    3619        5033 :         if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
    3620             :     }
    3621             :     else
    3622             :     { /* pivot for column k on row j */
    3623        4741 :       c[j]=k; d[k]=j; p = gdiv(gen_m1,gcoeff(x,j,k));
    3624        4741 :       gcoeff(x,j,k) = gen_m1;
    3625             :       /* x[j,] /= - x[j,k] */
    3626       24169 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3627       42136 :       for (t=1; t<=m; t++)
    3628       37395 :         if (t!=j)
    3629             :         { /* x[t,] -= 1 / x[j,k] x[j,] */
    3630       32654 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3631       32654 :           if (gequal0(p)) continue;
    3632       86934 :           for (i=k+1; i<=n; i++)
    3633       69470 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i),gmul(p,gcoeff(x,j,i)));
    3634       17464 :           if (gc_needed(av,1)) gc_ker(av, x,k,t);
    3635             :         }
    3636             :     }
    3637             :   }
    3638        1271 :   *dd=d; *rr=r; return x;
    3639             : }
    3640             : 
    3641             : /* r = dim ker(x).
    3642             :  * Returns d:
    3643             :  *   d[k] != 0 contains the index of a nonzero pivot in column k
    3644             :  *   d[k] == 0 if column k is a linear combination of the (k-1) first ones */
    3645             : GEN
    3646      167640 : RgM_pivots(GEN x0, long *rr, pivot_fun pivot, GEN data)
    3647             : {
    3648             :   GEN x, c, d, p;
    3649      167640 :   long i, j, k, r, t, m, n = lg(x0)-1;
    3650             :   pari_sp av;
    3651             : 
    3652      167640 :   if (RgM_is_ZM(x0)) return ZM_pivots(x0, rr);
    3653      152390 :   if (!n) { *rr = 0; return NULL; }
    3654             : 
    3655      152390 :   d = cgetg(n+1, t_VECSMALL);
    3656      152390 :   x = RgM_shallowcopy(x0);
    3657      152390 :   m = nbrows(x); r = 0;
    3658      152390 :   c = zero_zv(m);
    3659      152399 :   av = avma;
    3660      927845 :   for (k=1; k<=n; k++)
    3661             :   {
    3662      775454 :     j = pivot(x, data, k, c);
    3663      775457 :     if (j > m) { r++; d[k] = 0; }
    3664             :     else
    3665             :     {
    3666      291661 :       c[j] = k; d[k] = j; p = gdiv(gen_m1, gcoeff(x,j,k));
    3667     1881250 :       for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
    3668             : 
    3669     1054358 :       for (t=1; t<=m; t++)
    3670      762708 :         if (!c[t]) /* no pivot on that line yet */
    3671             :         {
    3672      257110 :           p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
    3673     4120461 :           for (i=k+1; i<=n; i++)
    3674     3863348 :             gcoeff(x,t,i) = gadd(gcoeff(x,t,i), gmul(p, gcoeff(x,j,i)));
    3675      257113 :           if (gc_needed(av,1)) gc_gauss(av, x,k,t,j,c);
    3676             :         }
    3677     2172992 :       for (i=k; i<=n; i++) gcoeff(x,j,i) = gen_0; /* dummy */
    3678             :     }
    3679             :   }
    3680      152391 :   *rr = r; return gc_const((pari_sp)d, d);
    3681             : }
    3682             : 
    3683             : static long
    3684     4214003 : ZM_count_0_cols(GEN M)
    3685             : {
    3686     4214003 :   long i, l = lg(M), n = 0;
    3687    18110559 :   for (i = 1; i < l; i++)
    3688    13896559 :     if (ZV_equal0(gel(M,i))) n++;
    3689     4214000 :   return n;
    3690             : }
    3691             : 
    3692             : static void indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol);
    3693             : /* As RgM_pivots, integer entries. Set *rr = dim Ker M0 */
    3694             : GEN
    3695     4227675 : ZM_pivots(GEN M0, long *rr)
    3696             : {
    3697     4227675 :   GEN d, dbest = NULL;
    3698             :   long m, mm, n, nn, i, imax, rmin, rbest, zc;
    3699     4227675 :   int beenthere = 0;
    3700     4227675 :   pari_sp av, av0 = avma;
    3701             :   forprime_t S;
    3702             : 
    3703     4227675 :   rbest = n = lg(M0)-1;
    3704     4227675 :   if (n == 0) { *rr = 0; return NULL; }
    3705     4214004 :   zc = ZM_count_0_cols(M0);
    3706     4213991 :   if (n == zc) { *rr = zc; return zero_zv(n); }
    3707             : 
    3708     4213859 :   m = nbrows(M0);
    3709     4213859 :   rmin = maxss(zc, n-m);
    3710     4213857 :   init_modular_small(&S);
    3711     4213874 :   if (n <= m) { nn = n; mm = m; } else { nn = m; mm = n; }
    3712     4213874 :   imax = (nn < 16)? 1: (nn < 64)? 2: 3; /* heuristic */
    3713             : 
    3714             :   for(;;)
    3715           0 :   {
    3716             :     GEN row, col, M, KM, IM, RHS, X, cX;
    3717             :     long rk;
    3718     4237095 :     for (av = avma, i = 0;; set_avma(av), i++)
    3719       23224 :     {
    3720     4237095 :       ulong p = u_forprime_next(&S);
    3721             :       long rp;
    3722     4237088 :       if (!p) pari_err_OVERFLOW("ZM_pivots [ran out of primes]");
    3723     4237088 :       d = Flm_pivots(ZM_to_Flm(M0, p), p, &rp, 1);
    3724     4237082 :       if (rp == rmin) { rbest = rp; goto END; } /* maximal rank, return */
    3725       45061 :       if (rp < rbest) { /* save best r so far */
    3726       21862 :         rbest = rp;
    3727       21862 :         guncloneNULL(dbest);
    3728       21862 :         dbest = gclone(d);
    3729       21862 :         if (beenthere) break;
    3730             :       }
    3731       45061 :       if (!beenthere && i >= imax) break;
    3732             :     }
    3733       21837 :     beenthere = 1;
    3734             :     /* Dubious case: there is (probably) a non trivial kernel */
    3735       21837 :     indexrank_all(m,n, rbest, dbest, &row, &col);
    3736       21837 :     M = rowpermute(vecpermute(M0, col), row);
    3737       21837 :     rk = n - rbest; /* (probable) dimension of image */
    3738       21837 :     if (n > m) M = shallowtrans(M);
    3739       21837 :     IM = vecslice(M,1,rk);
    3740       21837 :     KM = vecslice(M,rk+1, nn);
    3741       21837 :     M = rowslice(IM, 1,rk); /* square maximal rank */
    3742       21837 :     X = ZM_gauss(M, rowslice(KM, 1,rk));
    3743       21836 :     RHS = rowslice(KM,rk+1,mm);
    3744       21837 :     M = rowslice(IM,rk+1,mm);
    3745       21837 :     X = Q_remove_denom(X, &cX);
    3746       21837 :     if (cX) RHS = ZM_Z_mul(RHS, cX);
    3747       21837 :     if (ZM_equal(ZM_mul(M, X), RHS)) { d = vecsmall_copy(dbest); goto END; }
    3748           0 :     set_avma(av);
    3749             :   }
    3750     4213858 : END:
    3751     4213858 :   *rr = rbest; guncloneNULL(dbest);
    3752     4213855 :   return gc_uptoleaf(av0, d);
    3753             : }
    3754             : 
    3755             : /* compute ker(x) */
    3756             : static GEN
    3757        1271 : ker_aux(GEN x, pivot_fun fun, GEN data)
    3758             : {
    3759        1271 :   pari_sp av = avma;
    3760             :   GEN d,y;
    3761             :   long i,j,k,r,n;
    3762             : 
    3763        1271 :   x = gauss_pivot_ker(x,&d,&r, fun, data);
    3764        1271 :   if (!r) retgc_const(av, cgetg(1, t_MAT));
    3765        1211 :   n = lg(x)-1; y=cgetg(r+1,t_MAT);
    3766        2674 :   for (j=k=1; j<=r; j++,k++)
    3767             :   {
    3768        1463 :     GEN p = cgetg(n+1,t_COL);
    3769             : 
    3770        5586 :     gel(y,j) = p; while (d[k]) k++;
    3771        6496 :     for (i=1; i<k; i++)
    3772        5033 :       if (d[i])
    3773             :       {
    3774        4641 :         GEN p1=gcoeff(x,d[i],k);
    3775        4641 :         gel(p,i) = gcopy(p1); gunclone(p1);
    3776             :       }
    3777             :       else
    3778         392 :         gel(p,i) = gen_0;
    3779        2541 :     gel(p,k) = gen_1; for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
    3780             :   }
    3781        1211 :   return gc_upto(av,y);
    3782             : }
    3783             : 
    3784             : static GEN
    3785         553 : RgM_ker_FpM(GEN x, GEN p)
    3786             : {
    3787         553 :   pari_sp av = avma;
    3788             :   ulong pp;
    3789         553 :   x = RgM_Fp_init3(x, p, &pp);
    3790         553 :   switch(pp)
    3791             :   {
    3792          35 :     case 0: x = FpM_to_mod(FpM_ker_gen(x,p,0),p); break;
    3793          21 :     case 2: x = F2m_to_mod(F2m_ker_sp(x,0)); break;
    3794          77 :     case 3: x = F3m_to_mod(F3m_ker_sp(x,0)); break;
    3795         420 :     default:x = Flm_to_mod(Flm_ker_sp(x,pp,0), pp); break;
    3796             :   }
    3797         553 :   return gc_upto(av, x);
    3798             : }
    3799             : 
    3800             : static GEN
    3801          91 : RgM_ker_FqM(GEN x, GEN pol, GEN p)
    3802             : {
    3803          91 :   pari_sp av = avma;
    3804          91 :   GEN b, T = RgX_to_FpX(pol, p);
    3805          91 :   if (signe(T) == 0) pari_err_OP("ker",x,pol);
    3806          84 :   b = FqM_ker(RgM_to_FqM(x, T, p), T, p);
    3807          84 :   return gc_upto(av, FqM_to_mod(b, T, p));
    3808             : }
    3809             : 
    3810             : static GEN
    3811       10668 : RgM_ker_fast(GEN x, pivot_fun *fun, GEN *data)
    3812             : {
    3813             :   GEN p, pol;
    3814       10668 :   long pa, t = RgM_type(x, &p,&pol,&pa);
    3815       10668 :   set_pivot_fun(fun, data, t, x, p);
    3816       10668 :   switch(t)
    3817             :   {
    3818        9079 :     case t_INT:    /* fall through */
    3819        9079 :     case t_FRAC:   return QM_ker(x);
    3820          63 :     case t_FFELT:  return FFM_ker(x, pol);
    3821         553 :     case t_INTMOD: return RgM_ker_FpM(x, p);
    3822          91 :     case RgX_type_code(t_POLMOD, t_INTMOD):
    3823          91 :                    return RgM_ker_FqM(x, pol, p);
    3824         882 :     default:       return NULL;
    3825             :   }
    3826             : }
    3827             : 
    3828             : GEN
    3829       10668 : ker(GEN x)
    3830             : {
    3831             :   pivot_fun fun;
    3832       10668 :   GEN data, b = RgM_ker_fast(x, &fun, &data);
    3833       10661 :   if (b) return b;
    3834         882 :   return ker_aux(x, fun, data);
    3835             : }
    3836             : 
    3837             : GEN
    3838       46221 : matker0(GEN x,long flag)
    3839             : {
    3840       46221 :   if (typ(x)!=t_MAT) pari_err_TYPE("matker",x);
    3841       46221 :   if (!flag) return ker(x);
    3842       45934 :   RgM_check_ZM(x, "matker");
    3843       45934 :   return ZM_ker(x);
    3844             : }
    3845             : 
    3846             : static GEN
    3847         525 : RgM_image_FpM(GEN x, GEN p)
    3848             : {
    3849         525 :   pari_sp av = avma;
    3850             :   ulong pp;
    3851         525 :   x = RgM_Fp_init(x, p, &pp);
    3852         525 :   switch(pp)
    3853             :   {
    3854          28 :     case 0: x = FpM_to_mod(FpM_image(x,p),p); break;
    3855           7 :     case 2: x = F2m_to_mod(F2m_image(x)); break;
    3856         490 :     default:x = Flm_to_mod(Flm_image(x,pp), pp); break;
    3857             :   }
    3858         525 :   return gc_upto(av, x);
    3859             : }
    3860             : 
    3861             : static GEN
    3862          35 : RgM_image_FqM(GEN x, GEN pol, GEN p)
    3863             : {
    3864          35 :   pari_sp av = avma;
    3865          35 :   GEN b, T = RgX_to_FpX(pol, p);
    3866          35 :   if (signe(T) == 0) pari_err_OP("image",x,pol);
    3867          28 :   b = FqM_image(RgM_to_FqM(x, T, p), T, p);
    3868          28 :   return gc_upto(av, FqM_to_mod(b, T, p));
    3869             : }
    3870             : 
    3871             : GEN
    3872        6181 : QM_image_shallow(GEN A)
    3873             : {
    3874        6181 :   A = vec_Q_primpart(A);
    3875        6181 :   return vecpermute(A, ZM_indeximage(A));
    3876             : }
    3877             : GEN
    3878        5411 : QM_image(GEN A)
    3879             : {
    3880        5411 :   pari_sp av = avma;
    3881        5411 :   return gc_GEN(av, QM_image_shallow(A));
    3882             : }
    3883             : 
    3884             : static GEN
    3885        6034 : RgM_image_fast(GEN x, pivot_fun *fun, GEN *data)
    3886             : {
    3887             :   GEN p, pol;
    3888        6034 :   long pa, t = RgM_type(x, &p,&pol,&pa);
    3889        6034 :   set_pivot_fun(fun, data, t, x, p);
    3890        6034 :   switch(t)
    3891             :   {
    3892        5411 :     case t_INT:    /* fall through */
    3893        5411 :     case t_FRAC:   return QM_image(x);
    3894          49 :     case t_FFELT:  return FFM_image(x, pol);
    3895         525 :     case t_INTMOD: return RgM_image_FpM(x, p);
    3896          35 :     case RgX_type_code(t_POLMOD, t_INTMOD):
    3897          35 :                    return RgM_image_FqM(x, pol, p);
    3898          14 :     default:       return NULL;
    3899             :   }
    3900             : }
    3901             : 
    3902             : GEN
    3903        6034 : image(GEN x)
    3904             : {
    3905             :   pivot_fun fun;
    3906             :   GEN d, M, data;
    3907             :   long r;
    3908             : 
    3909        6034 :   if (typ(x)!=t_MAT) pari_err_TYPE("matimage",x);
    3910        6034 :   M = RgM_image_fast(x, &fun, &data);
    3911        6027 :   if (M) return M;
    3912          14 :   d = RgM_pivots(x, &r, fun, data); /* d left on stack for efficiency */
    3913          14 :   return image_from_pivot(x,d,r);
    3914             : }
    3915             : 
    3916             : static GEN
    3917          84 : imagecompl_aux(GEN d, long r)
    3918             : {
    3919          84 :   GEN y = cgetg(r+1,t_VECSMALL);
    3920             :   long j, i;
    3921         126 :   for (i = j = 1; j<=r; i++)
    3922          42 :     if (!d[i]) y[j++] = i;
    3923          84 :   return y;
    3924             : }
    3925             : GEN
    3926          84 : imagecompl(GEN x)
    3927             : {
    3928          84 :   pari_sp av = avma;
    3929             :   GEN data, d;
    3930             :   long r;
    3931             :   pivot_fun fun;
    3932             : 
    3933          84 :   if (typ(x)!=t_MAT) pari_err_TYPE("imagecompl",x);
    3934          84 :   init_pivot_list(x); set_pivot_fun_all(&fun, &data, x);
    3935          84 :   d = RgM_pivots(x, &r, fun, data); /* if (!d) then r = 0 */
    3936          84 :   set_avma(av); return imagecompl_aux(d, r);
    3937             : }
    3938             : GEN
    3939           0 : ZM_imagecompl(GEN x)
    3940             : {
    3941           0 :   pari_sp av = avma;
    3942             :   GEN d;
    3943             :   long r;
    3944             : 
    3945           0 :   init_pivot_list(x);
    3946           0 :   d = ZM_pivots(x, &r); /* if (!d) then r = 0 */
    3947           0 :   set_avma(av); return imagecompl_aux(d, r);
    3948             : }
    3949             : 
    3950             : static GEN
    3951          28 : RgM_RgC_invimage_FpC(GEN A, GEN y, GEN p)
    3952             : {
    3953          28 :   pari_sp av = avma;
    3954             :   ulong pp;
    3955             :   GEN x;
    3956          28 :   A = RgM_Fp_init(A,p,&pp);
    3957          28 :   switch(pp)
    3958             :   {
    3959           7 :   case 0:
    3960           7 :     y = RgC_to_FpC(y,p);
    3961           7 :     x = FpM_FpC_invimage(A, y, p);
    3962           7 :     return x ? gc_upto(av, FpC_to_mod(x,p)): NULL;
    3963           7 :   case 2:
    3964           7 :     y = RgV_to_F2v(y);
    3965           7 :     x = F2m_F2c_invimage(A, y);
    3966           7 :     return x ? gc_upto(av, F2c_to_mod(x)): NULL;
    3967          14 :   default:
    3968          14 :     y = RgV_to_Flv(y,pp);
    3969          14 :     x = Flm_Flc_invimage(A, y, pp);
    3970          14 :     return x ? gc_upto(av, Flc_to_mod(x,pp)): NULL;
    3971             :   }
    3972             : }
    3973             : 
    3974             : /* Returns gen_0 instead of NULL for 'no fast algorithm'. NULL is already
    3975             :  * reserved for 'not invertible' */
    3976             : static GEN
    3977        3654 : RgM_RgC_invimage_fast(GEN x, GEN y)
    3978             : {
    3979             :   GEN p, pol;
    3980        3654 :   long pa, t = RgM_RgC_type(x, y, &p,&pol,&pa);
    3981        3654 :   switch(t)
    3982             :   {
    3983          28 :     case t_INTMOD: return RgM_RgC_invimage_FpC(x, y, p);
    3984          63 :     case t_FFELT:  return FFM_FFC_invimage(x, y, pol);
    3985        3563 :     default:       return gen_0;
    3986             :   }
    3987             : }
    3988             : 
    3989             : GEN
    3990        3759 : RgM_RgC_invimage(GEN A, GEN y)
    3991             : {
    3992        3759 :   pari_sp av = avma;
    3993        3759 :   long i, l = lg(A);
    3994             :   GEN M, x, t;
    3995        3759 :   if (l==1) return NULL;
    3996        3654 :   if (lg(y) != lgcols(A)) pari_err_DIM("inverseimage");
    3997        3654 :   M = RgM_RgC_invimage_fast(A, y);
    3998        3654 :   if (!M) return gc_NULL(av);
    3999        3633 :   if (M != gen_0) return M;
    4000        3563 :   M = ker(shallowconcat(A, y));
    4001        3563 :   i = lg(M)-1;
    4002        3563 :   if (!i) return gc_NULL(av);
    4003             : 
    4004        3304 :   x = gel(M,i); t = gel(x,l);
    4005        3304 :   if (gequal0(t)) return gc_NULL(av);
    4006             : 
    4007        1862 :   t = gneg_i(t); setlg(x,l);
    4008        1862 :   return gc_upto(av, RgC_Rg_div(x, t));
    4009             : }
    4010             : 
    4011             : /* Return X such that m X = v (t_COL or t_MAT), resp. an empty t_COL / t_MAT
    4012             :  * if no solution exist */
    4013             : GEN
    4014        3920 : inverseimage(GEN m, GEN v)
    4015             : {
    4016             :   GEN y;
    4017        3920 :   if (typ(m)!=t_MAT) pari_err_TYPE("inverseimage",m);
    4018        3920 :   switch(typ(v))
    4019             :   {
    4020        3682 :     case t_COL:
    4021        3682 :       y = RgM_RgC_invimage(m,v);
    4022        3682 :       return y? y: cgetg(1,t_COL);
    4023         238 :     case t_MAT:
    4024         238 :       y = RgM_invimage(m, v);
    4025         238 :       return y? y: cgetg(1,t_MAT);
    4026             :   }
    4027           0 :   pari_err_TYPE("inverseimage",v);
    4028             :   return NULL;/*LCOV_EXCL_LINE*/
    4029             : }
    4030             : 
    4031             : static GEN
    4032          84 : RgM_invimage_FpM(GEN A, GEN B, GEN p)
    4033             : {
    4034          84 :   pari_sp av = avma;
    4035             :   ulong pp;
    4036             :   GEN x;
    4037          84 :   A = RgM_Fp_init(A,p,&pp);
    4038          84 :   switch(pp)
    4039             :   {
    4040          35 :   case 0:
    4041          35 :     B = RgM_to_FpM(B,p);
    4042          35 :     x = FpM_invimage_gen(A, B, p);
    4043          35 :     return x ? gc_upto(av, FpM_to_mod(x, p)): x;
    4044           7 :   case 2:
    4045           7 :     B = RgM_to_F2m(B);
    4046           7 :     x = F2m_invimage_i(A, B);
    4047           7 :     return x ? gc_upto(av, F2m_to_mod(x)): x;
    4048          42 :   default:
    4049          42 :     B = RgM_to_Flm(B,pp);
    4050          42 :     x = Flm_invimage_i(A, B, pp);
    4051          42 :     return x ? gc_upto(av, Flm_to_mod(x, pp)): x;
    4052             :   }
    4053             : }
    4054             : 
    4055             : /* Returns gen_0 instead of NULL for 'no fast algorithm'. NULL is already
    4056             :  * reserved for 'not invertible' */
    4057             : static GEN
    4058         364 : RgM_invimage_fast(GEN x, GEN y)
    4059             : {
    4060             :   GEN p, pol;
    4061         364 :   long pa, t = RgM_type2(x, y, &p,&pol,&pa);
    4062         364 :   switch(t)
    4063             :   {
    4064          84 :     case t_INTMOD: return RgM_invimage_FpM(x, y, p);
    4065         105 :     case t_FFELT:  return FFM_invimage(x, y, pol);
    4066         175 :     default:       return gen_0;
    4067             :   }
    4068             : }
    4069             : 
    4070             : /* find Z such that A Z = B. Return NULL if no solution */
    4071             : GEN
    4072         364 : RgM_invimage(GEN A, GEN B)
    4073             : {
    4074         364 :   pari_sp av = avma;
    4075             :   GEN d, x, X, Y;
    4076         364 :   long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
    4077         364 :   X = RgM_invimage_fast(A, B);
    4078         364 :   if (!X) return gc_NULL(av);
    4079         252 :   if (X != gen_0) return X;
    4080         175 :   x = ker(shallowconcat(RgM_neg(A), B));
    4081             :   /* AX = BY, Y in strict upper echelon form with pivots = 1.
    4082             :    * We must find T such that Y T = Id_nB then X T = Z. This exists iff
    4083             :    * Y has at least nB columns and full rank */
    4084         175 :   nY = lg(x)-1;
    4085         175 :   if (nY < nB) return gc_NULL(av);
    4086         161 :   Y = rowslice(x, nA+1, nA+nB); /* nB rows */
    4087         161 :   d = cgetg(nB+1, t_VECSMALL);
    4088         721 :   for (i = nB, j = nY; i >= 1; i--, j--)
    4089             :   {
    4090         805 :     for (; j>=1; j--)
    4091         756 :       if (!gequal0(gcoeff(Y,i,j))) { d[i] = j; break; }
    4092         609 :     if (!j) return gc_NULL(av);
    4093             :   }
    4094             :   /* reduce to the case Y square, upper triangular with 1s on diagonal */
    4095         112 :   Y = vecpermute(Y, d);
    4096         112 :   x = vecpermute(x, d);
    4097         112 :   X = rowslice(x, 1, nA);
    4098         112 :   return gc_upto(av, RgM_mul(X, RgM_inv_upper(Y)));
    4099             : }
    4100             : 
    4101             : static GEN
    4102          70 : RgM_suppl_FpM(GEN x, GEN p)
    4103             : {
    4104          70 :   pari_sp av = avma;
    4105             :   ulong pp;
    4106          70 :   x = RgM_Fp_init(x, p, &pp);
    4107          70 :   switch(pp)
    4108             :   {
    4109          21 :   case 0: x = FpM_to_mod(FpM_suppl(x,p), p); break;
    4110          14 :   case 2: x = F2m_to_mod(F2m_suppl(x)); break;
    4111          35 :   default:x = Flm_to_mod(Flm_suppl(x,pp), pp); break;
    4112             :   }
    4113          70 :   return gc_upto(av, x);
    4114             : }
    4115             : 
    4116             : static GEN
    4117         175 : RgM_suppl_fast(GEN x, pivot_fun *fun, GEN *data)
    4118             : {
    4119             :   GEN p, pol;
    4120         175 :   long pa, t = RgM_type(x,&p,&pol,&pa);
    4121         175 :   set_pivot_fun(fun, data, t, x, p);
    4122         175 :   switch(t)
    4123             :   {
    4124          70 :     case t_INTMOD: return RgM_suppl_FpM(x, p);
    4125          35 :     case t_FFELT:  return FFM_suppl(x, pol);
    4126          70 :     default:       return NULL;
    4127             :   }
    4128             : }
    4129             : 
    4130             : /* x is an n x k matrix, rank(x) = k <= n. Return an invertible n x n matrix
    4131             :  * whose first k columns are given by x. If rank(x) < k, undefined result. */
    4132             : GEN
    4133         175 : suppl(GEN x)
    4134             : {
    4135         175 :   pari_sp av = avma;
    4136             :   pivot_fun fun;
    4137             :   GEN d, M, data;
    4138             :   long r;
    4139         175 :   if (typ(x)!=t_MAT) pari_err_TYPE("suppl",x);
    4140         175 :   M = RgM_suppl_fast(x, &fun, &data);
    4141         175 :   if (M) return M;
    4142          70 :   init_suppl(x);
    4143          70 :   d = RgM_pivots(x, &r, fun, data); set_avma(av);
    4144          70 :   return get_suppl(x,d,nbrows(x),r,&col_ei);
    4145             : }
    4146             : 
    4147             : GEN
    4148           7 : image2(GEN x)
    4149             : {
    4150           7 :   pari_sp av = avma;
    4151             :   long k, n, i;
    4152             :   GEN A, B;
    4153             : 
    4154           7 :   if (typ(x)!=t_MAT) pari_err_TYPE("image2",x);
    4155           7 :   if (lg(x) == 1) return cgetg(1,t_MAT);
    4156           7 :   A = ker(x); k = lg(A)-1;
    4157           7 :   if (!k) { set_avma(av); return gcopy(x); }
    4158           7 :   A = suppl(A); n = lg(A)-1;
    4159           7 :   B = cgetg(n-k+1, t_MAT);
    4160          21 :   for (i = k+1; i <= n; i++) gel(B,i-k) = RgM_RgC_mul(x, gel(A,i));
    4161           7 :   return gc_upto(av, B);
    4162             : }
    4163             : 
    4164             : GEN
    4165         217 : matimage0(GEN x,long flag)
    4166             : {
    4167         217 :   switch(flag)
    4168             :   {
    4169         210 :     case 0: return image(x);
    4170           7 :     case 1: return image2(x);
    4171           0 :     default: pari_err_FLAG("matimage");
    4172             :   }
    4173             :   return NULL; /* LCOV_EXCL_LINE */
    4174             : }
    4175             : 
    4176             : static long
    4177         126 : RgM_rank_FpM(GEN x, GEN p)
    4178             : {
    4179         126 :   pari_sp av = avma;
    4180             :   ulong pp;
    4181             :   long r;
    4182         126 :   x = RgM_Fp_init(x,p,&pp);
    4183         126 :   switch(pp)
    4184             :   {
    4185          28 :   case 0: r = FpM_rank(x,p); break;
    4186          63 :   case 2: r = F2m_rank(x); break;
    4187          35 :   default:r = Flm_rank(x,pp); break;
    4188             :   }
    4189         126 :   return gc_long(av, r);
    4190             : }
    4191             : 
    4192             : static long
    4193          49 : RgM_rank_FqM(GEN x, GEN pol, GEN p)
    4194             : {
    4195          49 :   pari_sp av = avma;
    4196             :   long r;
    4197          49 :   GEN T = RgX_to_FpX(pol, p);
    4198          49 :   if (signe(T) == 0) pari_err_OP("rank",x,pol);
    4199          42 :   r = FqM_rank(RgM_to_FqM(x, T, p), T, p);
    4200          42 :   return gc_long(av,r);
    4201             : }
    4202             : 
    4203             : static long
    4204         371 : RgM_rank_fast(GEN x, pivot_fun *fun, GEN *data)
    4205             : {
    4206             :   GEN p, pol;
    4207         371 :   long pa, t = RgM_type(x,&p,&pol,&pa);
    4208         371 :   set_pivot_fun(fun, data, t, x, p);
    4209         371 :   switch(t)
    4210             :   {
    4211          98 :     case t_INT:    return ZM_rank(x);
    4212          21 :     case t_FRAC:   return QM_rank(x);
    4213         126 :     case t_INTMOD: return RgM_rank_FpM(x, p);
    4214          70 :     case t_FFELT:  return FFM_rank(x, pol);
    4215          49 :     case RgX_type_code(t_POLMOD, t_INTMOD):
    4216          49 :                    return RgM_rank_FqM(x, pol, p);
    4217           7 :     default:       return -1;
    4218             :   }
    4219             : }
    4220             : 
    4221             : long
    4222         371 : rank(GEN x)
    4223             : {
    4224         371 :   pari_sp av = avma;
    4225             :   pivot_fun fun;
    4226             :   GEN data;
    4227             :   long r;
    4228             : 
    4229         371 :   if (typ(x)!=t_MAT) pari_err_TYPE("rank",x);
    4230         371 :   r = RgM_rank_fast(x, &fun, &data);
    4231         364 :   if (r >= 0) return r;
    4232           7 :   (void)RgM_pivots(x, &r, fun, data);
    4233           7 :   return gc_long(av, lg(x)-1 - r);
    4234             : }
    4235             : 
    4236             : /* d a t_VECSMALL of integers in 1..n. Return the vector of the d[i]
    4237             :  * followed by the missing indices */
    4238             : static GEN
    4239       43674 : perm_complete(GEN d, long n)
    4240             : {
    4241       43674 :   GEN y = cgetg(n+1, t_VECSMALL);
    4242       43674 :   long i, j = 1, k = n, l = lg(d);
    4243       43674 :   pari_sp av = avma;
    4244       43674 :   char *T = stack_calloc(n+1);
    4245      214758 :   for (i = 1; i < l; i++) T[d[i]] = 1;
    4246      418519 :   for (i = 1; i <= n; i++)
    4247      374845 :     if (T[i]) y[j++] = i; else y[k--] = i;
    4248       43674 :   return gc_const(av, y);
    4249             : }
    4250             : 
    4251             : /* n = dim x, r = dim Ker(x), d from RgM_pivots */
    4252             : static GEN
    4253        6181 : indeximage0(long n, long r, GEN d)
    4254             : {
    4255             :   long i, j;
    4256             :   GEN v;
    4257             : 
    4258        6181 :   r = n - r; /* now r = dim Im(x) */
    4259        6181 :   v = cgetg(r+1,t_VECSMALL);
    4260       34419 :   if (d) for (i=j=1; j<=n; j++)
    4261       28238 :     if (d[j]) v[i++] = j;
    4262        6181 :   return v;
    4263             : }
    4264             : /* x an m x n t_MAT, n > 0, r = dim Ker(x), d from RgM_pivots */
    4265             : static void
    4266       21837 : indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol)
    4267             : {
    4268       21837 :   GEN IR = indexrank0(n, r, d);
    4269       21837 :   *prow = perm_complete(gel(IR,1), m);
    4270       21837 :   *pcol = perm_complete(gel(IR,2), n);
    4271       21837 : }
    4272             : 
    4273             : static GEN
    4274          28 : RgM_indexrank_FpM(GEN x, GEN p)
    4275             : {
    4276          28 :   pari_sp av = avma;
    4277             :   ulong pp;
    4278             :   GEN r;
    4279          28 :   x = RgM_Fp_init(x,p,&pp);
    4280          28 :   switch(pp)
    4281             :   {
    4282           7 :   case 0:  r = FpM_indexrank(x,p); break;
    4283           7 :   case 2:  r = F2m_indexrank(x); break;
    4284          14 :   default: r = Flm_indexrank(x,pp); break;
    4285             :   }
    4286          28 :   return gc_upto(av, r);
    4287             : }
    4288             : 
    4289             : static GEN
    4290           0 : RgM_indexrank_FqM(GEN x, GEN pol, GEN p)
    4291             : {
    4292           0 :   pari_sp av = avma;
    4293           0 :   GEN r, T = RgX_to_FpX(pol, p);
    4294           0 :   if (signe(T) == 0) pari_err_OP("indexrank",x,pol);
    4295           0 :   r = FqM_indexrank(RgM_to_FqM(x, T, p), T, p);
    4296           0 :   return gc_upto(av, r);
    4297             : }
    4298             : 
    4299             : static GEN
    4300       77599 : RgM_indexrank_fast(GEN x, pivot_fun *fun, GEN *data)
    4301             : {
    4302             :   GEN p, pol;
    4303       77599 :   long pa, t = RgM_type(x,&p,&pol,&pa);
    4304       77599 :   set_pivot_fun(fun, data, t, x, p);
    4305       77599 :   switch(t)
    4306             :   {
    4307         406 :     case t_INT:    return ZM_indexrank(x);
    4308        1344 :     case t_FRAC:   return QM_indexrank(x);
    4309          28 :     case t_INTMOD: return RgM_indexrank_FpM(x, p);
    4310          21 :     case t_FFELT:  return FFM_indexrank(x, pol);
    4311           0 :     case RgX_type_code(t_POLMOD, t_INTMOD):
    4312           0 :                    return RgM_indexrank_FqM(x, pol, p);
    4313       75800 :     default:       return NULL;
    4314             :   }
    4315             : }
    4316             : 
    4317             : GEN
    4318       77599 : indexrank(GEN x)
    4319             : {
    4320             :   pari_sp av;
    4321             :   pivot_fun fun;
    4322             :   long r;
    4323             :   GEN d, data;
    4324       77599 :   if (typ(x)!=t_MAT) pari_err_TYPE("indexrank",x);
    4325       77599 :   d = RgM_indexrank_fast(x, &fun, &data);
    4326       77599 :   if (d) return d;
    4327       75800 :   av = avma;
    4328       75800 :   init_pivot_list(x); d = RgM_pivots(x, &r, fun, data);
    4329       75800 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    4330             : }
    4331             : 
    4332             : GEN
    4333        6181 : ZM_indeximage(GEN x) {
    4334        6181 :   pari_sp av = avma;
    4335             :   long r;
    4336             :   GEN d;
    4337        6181 :   init_pivot_list(x); d = ZM_pivots(x,&r);
    4338        6181 :   set_avma(av); return indeximage0(lg(x)-1, r, d);
    4339             : }
    4340             : long
    4341     2225235 : ZM_rank(GEN x) {
    4342     2225235 :   pari_sp av = avma;
    4343             :   long r;
    4344     2225235 :   (void)ZM_pivots(x,&r);
    4345     2225226 :   return gc_long(av, lg(x)-1-r);
    4346             : }
    4347             : GEN
    4348     1742320 : ZM_indexrank(GEN x) {
    4349     1742320 :   pari_sp av = avma;
    4350             :   long r;
    4351             :   GEN d;
    4352     1742320 :   init_pivot_list(x); d = ZM_pivots(x,&r);
    4353     1742317 :   set_avma(av); return indexrank0(lg(x)-1, r, d);
    4354             : }
    4355             : 
    4356             : long
    4357          21 : QM_rank(GEN x)
    4358             : {
    4359          21 :   pari_sp av = avma;
    4360          21 :   return gc_long(av, ZM_rank(Q_primpart(x)));
    4361             : }
    4362             : 
    4363             : GEN
    4364        1344 : QM_indexrank(GEN x)
    4365             : {
    4366        1344 :   pari_sp av = avma;
    4367        1344 :   return gc_upto(av, ZM_indexrank(Q_primpart(x)));
    4368             : }
    4369             : 
    4370             : /*******************************************************************/
    4371             : /*                                                                 */
    4372             : /*                             ZabM                                */
    4373             : /*                                                                 */
    4374             : /*******************************************************************/
    4375             : 
    4376             : static GEN
    4377        1332 : FpXM_ratlift(GEN a, GEN q)
    4378             : {
    4379             :   GEN B, y;
    4380        1332 :   long i, j, l = lg(a), n;
    4381        1332 :   B = sqrti(shifti(q,-1));
    4382        1332 :   y = cgetg(l, t_MAT);
    4383        1332 :   if (l==1) return y;
    4384        1332 :   n = lgcols(a);
    4385        3213 :   for (i=1; i<l; i++)
    4386             :   {
    4387        2502 :     GEN yi = cgetg(n, t_COL);
    4388       32871 :     for (j=1; j<n; j++)
    4389             :     {
    4390       30990 :       GEN v = FpX_ratlift(gmael(a,i,j), q, B, B, NULL);
    4391       30990 :       if (!v) return NULL;
    4392       30369 :       gel(yi, j) = RgX_renormalize(v);
    4393             :     }
    4394        1881 :     gel(y,i) = yi;
    4395             :   }
    4396         711 :   return y;
    4397             : }
    4398             : 
    4399             : static GEN
    4400        4652 : FlmV_recover_pre(GEN a, GEN M, ulong p, ulong pi, long sv)
    4401             : {
    4402        4652 :   GEN a1 = gel(a,1);
    4403        4652 :   long i, j, k, l = lg(a1), n, lM = lg(M);
    4404        4652 :   GEN v = cgetg(lM, t_VECSMALL);
    4405        4652 :   GEN y = cgetg(l, t_MAT);
    4406        4652 :   if (l==1) return y;
    4407        4652 :   n = lgcols(a1);
    4408       23948 :   for (i=1; i<l; i++)
    4409             :   {
    4410       19296 :     GEN yi = cgetg(n, t_COL);
    4411      373167 :     for (j=1; j<n; j++)
    4412             :     {
    4413     4749477 :       for (k=1; k<lM; k++) uel(v,k) = umael(gel(a,k),i,j);
    4414      353871 :       gel(yi, j) = Flm_Flc_mul_pre_Flx(M, v, p, pi, sv);
    4415             :     }
    4416       19296 :     gel(y,i) = yi;
    4417             :   }
    4418        4652 :   return y;
    4419             : }
    4420             : 
    4421             : static GEN
    4422           0 : FlkM_inv(GEN M, GEN P, ulong p)
    4423             : {
    4424           0 :   ulong PI = get_Fl_red(p), pi = SMALL_ULONG(p)? 0: PI;
    4425           0 :   GEN R = Flx_roots_pre(P, p, pi);
    4426           0 :   long l = lg(R), i;
    4427           0 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4428           0 :   GEN V = cgetg(l, t_VEC);
    4429           0 :   for(i=1; i<l; i++)
    4430             :   {
    4431           0 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, PI);
    4432           0 :     GEN H = Flm_inv_sp(FlxM_eval_powers_pre(M, pows, p, pi), NULL, p);
    4433           0 :     if (!H) return NULL;
    4434           0 :     gel(V, i) = H;
    4435             :   }
    4436           0 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    4437             : }
    4438             : 
    4439             : static GEN
    4440        3320 : FlkM_adjoint(GEN M, GEN P, ulong p)
    4441             : {
    4442        3320 :   ulong PI = get_Fl_red(p), pi = SMALL_ULONG(p)? 0: PI;
    4443        3320 :   GEN R = Flx_roots_pre(P, p, pi);
    4444        3320 :   long l = lg(R), i;
    4445        3320 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4446        3320 :   GEN V = cgetg(l, t_VEC);
    4447       15910 :   for(i=1; i<l; i++)
    4448             :   {
    4449       12590 :     GEN pows = Fl_powers_pre(uel(R,i), degpol(P), p, PI);
    4450       12590 :     gel(V, i) = Flm_adjoint(FlxM_eval_powers_pre(M, pows, p, pi), p);
    4451             :   }
    4452        3320 :   return FlmV_recover_pre(V, W, p, pi, P[1]);
    4453             : }
    4454             : 
    4455             : static GEN
    4456        2063 : ZabM_inv_slice(GEN A, GEN Q, GEN P, GEN *mod)
    4457             : {
    4458        2063 :   pari_sp av = avma;
    4459        2063 :   long i, n = lg(P)-1, w = varn(Q);
    4460             :   GEN H, T;
    4461        2063 :   if (n == 1)
    4462             :   {
    4463        1610 :     ulong p = uel(P,1);
    4464        1610 :     GEN Qp = ZX_to_Flx(Q, p);
    4465        1610 :     GEN Ap = ZXM_to_FlxM(A, p, get_Flx_var(Qp));
    4466        1610 :     GEN Hp = FlkM_adjoint(Ap, Qp, p);
    4467        1610 :     Hp = gc_upto(av, FlxM_to_ZXM(Hp));
    4468        1610 :     *mod = utoipos(p); return Hp;
    4469             :   }
    4470         453 :   T = ZV_producttree(P);
    4471         453 :   A = ZXM_nv_mod_tree(A, P, T, w);
    4472         453 :   Q = ZX_nv_mod_tree(Q, P, T);
    4473         453 :   H = cgetg(n+1, t_VEC);
    4474        2163 :   for(i=1; i <= n; i++)
    4475             :   {
    4476        1710 :     ulong p = P[i];
    4477        1710 :     GEN a = gel(A,i), q = gel(Q, i);
    4478        1710 :     gel(H,i) = FlkM_adjoint(a, q, p);
    4479             :   }
    4480         453 :   H = nxMV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P,T));
    4481         453 :   *mod = gmael(T, lg(T)-1, 1); return gc_all(av, 2, &H, mod);
    4482             : }
    4483             : 
    4484             : GEN
    4485        2063 : ZabM_inv_worker(GEN P, GEN A, GEN Q)
    4486             : {
    4487        2063 :   GEN V = cgetg(3, t_VEC);
    4488        2063 :   gel(V,1) = ZabM_inv_slice(A, Q, P, &gel(V,2));
    4489        2063 :   return V;
    4490             : }
    4491             : 
    4492             : static GEN
    4493        5817 : vecnorml1(GEN x)
    4494       65338 : { pari_APPLY_same(gnorml1_fake(gel(x,i))); }
    4495             : 
    4496             : static GEN
    4497        1897 : ZabM_true_Hadamard(GEN a)
    4498             : {
    4499        1897 :   pari_sp av = avma;
    4500        1897 :   long n = lg(a)-1, i;
    4501             :   GEN B;
    4502        1897 :   if (n == 0) return gen_1;
    4503        1897 :   if (n == 1) return gnorml1_fake(gcoeff(a,1,1));
    4504        1239 :   B = gen_1;
    4505        7056 :   for (i = 1; i <= n; i++)
    4506        5817 :     B = gmul(B, gnorml2(RgC_gtofp(vecnorml1(gel(a,i)),DEFAULTPREC)));
    4507        1239 :   return gc_INT(av, ceil_safe(sqrtr_abs(B)));
    4508             : }
    4509             : 
    4510             : GEN
    4511        1897 : ZabM_inv(GEN A, GEN Q, long n, GEN *pt_den)
    4512             : {
    4513        1897 :   pari_sp av = avma;
    4514             :   forprime_t S;
    4515             :   GEN bnd, H, D, d, mod, worker;
    4516        1897 :   if (lg(A) == 1)
    4517             :   {
    4518           0 :     if (pt_den) *pt_den = gen_1;
    4519           0 :     return cgetg(1, t_MAT);
    4520             :   }
    4521        1897 :   bnd = ZabM_true_Hadamard(A);
    4522        1897 :   worker = snm_closure(is_entry("_ZabM_inv_worker"), mkvec2(A, Q));
    4523        1897 :   u_forprime_arith_init(&S, HIGHBIT+1, ULONG_MAX, 1, n);
    4524        1897 :   H = gen_crt("ZabM_inv", worker, &S, NULL, expi(bnd), 0, &mod,
    4525             :               nxMV_chinese_center, FpXM_center);
    4526        1897 :   D = RgMrow_RgC_mul(H, gel(A,1), 1);
    4527        1897 :   D = ZX_rem(D, Q);
    4528        1897 :   d = Z_content(mkvec2(H, D));
    4529        1897 :   if (d)
    4530             :   {
    4531         546 :     D = ZX_Z_divexact(D, d);
    4532         546 :     H = Q_div_to_int(H, d);
    4533             :   }
    4534        1897 :   if (!pt_den) return gc_upto(av, H);
    4535        1897 :   *pt_den = D; return gc_all(av, 2, &H, pt_den);
    4536             : }
    4537             : 
    4538             : GEN
    4539           0 : ZabM_inv_ratlift(GEN M, GEN P, long n, GEN *pden)
    4540             : {
    4541           0 :   pari_sp av2, av = avma;
    4542             :   GEN q, H;
    4543           0 :   ulong m = LONG_MAX>>1;
    4544           0 :   ulong p= 1 + m - (m % n);
    4545           0 :   long lM = lg(M);
    4546           0 :   if (lM == 1) { *pden = gen_1; return cgetg(1,t_MAT); }
    4547             : 
    4548           0 :   av2 = avma;
    4549           0 :   H = NULL;
    4550             :   for(;;)
    4551           0 :   {
    4552             :     GEN Hp, Pp, Mp, Hr;
    4553           0 :     do p += n; while(!uisprime(p));
    4554           0 :     Pp = ZX_to_Flx(P, p);
    4555           0 :     Mp = ZXM_to_FlxM(M, p, get_Flx_var(Pp));
    4556           0 :     Hp = FlkM_inv(Mp, Pp, p);
    4557           0 :     if (!Hp) continue;
    4558           0 :     if (!H)
    4559             :     {
    4560           0 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p);
    4561           0 :       q = utoipos(p);
    4562             :     }
    4563             :     else
    4564           0 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    4565           0 :     Hr = FpXM_ratlift(H, q);
    4566           0 :     if (DEBUGLEVEL>5) err_printf("ZabM_inv mod %ld (ratlift=%ld)\n", p,!!Hr);
    4567           0 :     if (Hr) {/* DONE ? */
    4568           0 :       GEN Hl = Q_remove_denom(Hr, pden);
    4569           0 :       GEN MH = ZXQM_mul(Hl, M, P);
    4570           0 :       if (*pden)
    4571           0 :       { if (RgM_isscalar(MH, *pden)) { H = Hl; break; }}
    4572             :       else
    4573           0 :       { if (RgM_isidentity(MH)) { H = Hl; *pden = gen_1; break; } }
    4574             :     }
    4575             : 
    4576           0 :     if (gc_needed(av,2))
    4577             :     {
    4578           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_inv");
    4579           0 :       (void)gc_all(av2, 2, &H, &q);
    4580             :     }
    4581             :   }
    4582           0 :   return gc_all(av, 2, &H, pden);
    4583             : }
    4584             : 
    4585             : static GEN
    4586        1332 : FlkM_ker(GEN M, GEN P, ulong p)
    4587             : {
    4588        1332 :   ulong PI = get_Fl_red(p), pi = SMALL_ULONG(p)? 0: PI;
    4589        1332 :   GEN R = Flx_roots_pre(P, p, pi);
    4590        1332 :   long l = lg(R), i, dP = degpol(P), r;
    4591             :   GEN M1, K, D;
    4592        1332 :   GEN W = Flv_invVandermonde(R, 1UL, p);
    4593        1332 :   GEN V = cgetg(l, t_VEC);
    4594        1332 :   M1 = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,1), dP, p, PI), p, pi);
    4595        1332 :   K = Flm_ker_sp(M1, p, 2);
    4596        1332 :   r = lg(gel(K,1)); D = gel(K,2);
    4597        1332 :   gel(V, 1) = gel(K,1);
    4598        2764 :   for(i=2; i<l; i++)
    4599             :   {
    4600        1432 :     GEN Mi = FlxM_eval_powers_pre(M, Fl_powers_pre(uel(R,i), dP, p, PI), p, pi);
    4601        1432 :     GEN K = Flm_ker_sp(Mi, p, 2);
    4602        1432 :     if (lg(gel(K,1)) != r || !zv_equal(D, gel(K,2))) return NULL;
    4603        1432 :     gel(V, i) = gel(K,1);
    4604             :   }
    4605        1332 :   return mkvec2(FlmV_recover_pre(V, W, p, pi, P[1]), D);
    4606             : }
    4607             : 
    4608             : static int
    4609         711 : ZabM_ker_check(GEN M, GEN H, ulong p, GEN P, long n)
    4610             : {
    4611             :   GEN pow;
    4612         711 :   long j, l = lg(H);
    4613             :   ulong pi, r;
    4614        4043 :   do p += n; while(!uisprime(p));
    4615         711 :   pi = get_Fl_red(p);
    4616         711 :   P = ZX_to_Flx(P, p);
    4617         711 :   r = Flx_oneroot_pre(P, p, pi);
    4618         711 :   pow = Fl_powers_pre(r, degpol(P),p, (p & HIGHMASK)? pi: 0);
    4619         711 :   M = ZXM_to_FlxM(M, p, P[1]); M = FlxM_eval_powers_pre(M, pow, p, pi);
    4620         711 :   H = ZXM_to_FlxM(H, p, P[1]); H = FlxM_eval_powers_pre(H, pow, p, pi);
    4621        2332 :   for (j = 1; j < l; j++)
    4622        1653 :     if (!zv_equal0(Flm_Flc_mul_pre(M, gel(H,j), p, pi))) return 0;
    4623         679 :   return 1;
    4624             : }
    4625             : 
    4626             : GEN
    4627         679 : ZabM_ker(GEN M, GEN P, long n)
    4628             : {
    4629         679 :   pari_sp av = avma;
    4630             :   pari_timer ti;
    4631         679 :   GEN q, H = NULL, D = NULL;
    4632         679 :   ulong m = LONG_MAX>>1;
    4633         679 :   ulong p = 1 + m - (m % n);
    4634             : 
    4635         679 :   if (DEBUGLEVEL>5) timer_start(&ti);
    4636             :   for(;;)
    4637         653 :   {
    4638             :     GEN Kp, Hp, Dp, Pp, Mp, Hr;
    4639       24429 :     do p += n; while(!uisprime(p));
    4640        1332 :     Pp = ZX_to_Flx(P, p);
    4641        1332 :     Mp = ZXM_to_FlxM(M, p, get_Flx_var(Pp));
    4642        1332 :     Kp = FlkM_ker(Mp, Pp, p);
    4643        1332 :     if (!Kp) continue;
    4644        1332 :     Hp = gel(Kp,1); Dp = gel(Kp,2);
    4645        1332 :     if (H && (lg(Hp)>lg(H) || (lg(Hp)==lg(H) && vecsmall_lexcmp(Dp,D)>0))) continue;
    4646        1332 :     if (!H || (lg(Hp)<lg(H) || vecsmall_lexcmp(Dp,D)<0))
    4647             :     {
    4648         679 :       H = ZXM_init_CRT(Hp, degpol(P)-1, p); D = Dp;
    4649         679 :       q = utoipos(p);
    4650             :     }
    4651             :     else
    4652         653 :       ZXM_incremental_CRT(&H, Hp, &q, p);
    4653        1332 :     Hr = FpXM_ratlift(H, q);
    4654        1332 :     if (DEBUGLEVEL>5) timer_printf(&ti,"ZabM_ker mod %ld (ratlift=%ld)", p,!!Hr);
    4655        1332 :     if (Hr) {/* DONE ? */
    4656         711 :       GEN Hl = vec_Q_primpart(Hr);
    4657         711 :       if (ZabM_ker_check(M, Hl, p, P, n)) { H = Hl;  break; }
    4658             :     }
    4659             : 
    4660         653 :     if (gc_needed(av,2))
    4661             :     {
    4662           4 :       if (DEBUGMEM>1) pari_warn(warnmem,"ZabM_ker");
    4663           4 :       (void)gc_all(av, 3, &H, &D, &q);
    4664             :     }
    4665             :   }
    4666         679 :   return gc_GEN(av, H);
    4667             : }
    4668             : 
    4669             : GEN
    4670        2485 : ZabM_indexrank(GEN M, GEN P, long n)
    4671             : {
    4672        2485 :   pari_sp av = avma;
    4673        2485 :   ulong m = LONG_MAX>>1;
    4674        2485 :   ulong p = 1+m-(m%n), D = degpol(P);
    4675        2485 :   long lM = lg(M), lmax = 0, c = 0;
    4676             :   GEN v;
    4677             :   for(;;)
    4678         777 :   {
    4679             :     GEN R, Pp, Mp, K;
    4680             :     ulong pi;
    4681             :     long l;
    4682       65201 :     do p += n; while (!uisprime(p));
    4683        3262 :     pi = (p & HIGHMASK)? get_Fl_red(p): 0;
    4684        3262 :     Pp = ZX_to_Flx(P, p);
    4685        3262 :     R = Flx_roots_pre(Pp, p, pi);
    4686        3262 :     Mp = ZXM_to_FlxM(M, p, get_Flx_var(Pp));
    4687        3262 :     K = FlxM_eval_powers_pre(Mp, Fl_powers_pre(uel(R,1), D,p,pi), p,pi);
    4688        3262 :     v = Flm_indexrank(K, p);
    4689        3262 :     l = lg(gel(v,2));
    4690        3262 :     if (l == lM) break;
    4691        1036 :     if (lmax >= 0 && l > lmax) { lmax = l; c = 0; } else c++;
    4692        1036 :     if (c > 2)
    4693             :     { /* probably not maximal rank, expensive check */
    4694         259 :       lM -= lg(ZabM_ker(M, P, n))-1; /* actual rank (+1) */
    4695         259 :       if (lmax == lM) break;
    4696           0 :       lmax = -1; /* disable check */
    4697             :     }
    4698             :   }
    4699        2485 :   return gc_upto(av, v);
    4700             : }
    4701             : 
    4702             : #if 0
    4703             : GEN
    4704             : ZabM_gauss(GEN M, GEN P, long n, GEN *den)
    4705             : {
    4706             :   pari_sp av = avma;
    4707             :   GEN v, S, W;
    4708             :   v = ZabM_indexrank(M, P, n);
    4709             :   S = shallowmatextract(M,gel(v,1),gel(v,2));
    4710             :   W = ZabM_inv(S, P, n, den);
    4711             :   return gc_all(av,2,&W,den);
    4712             : }
    4713             : #endif
    4714             : 
    4715             : GEN
    4716         182 : ZabM_pseudoinv(GEN M, GEN P, long n, GEN *pv, GEN *den)
    4717             : {
    4718         182 :   GEN v = ZabM_indexrank(M, P, n);
    4719         182 :   if (pv) *pv = v;
    4720         182 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    4721         182 :   return ZabM_inv(M, P, n, den);
    4722             : }
    4723             : GEN
    4724        5040 : ZM_pseudoinv(GEN M, GEN *pv, GEN *den)
    4725             : {
    4726        5040 :   GEN v = ZM_indexrank(M);
    4727        5040 :   if (pv) *pv = v;
    4728        5040 :   M = shallowmatextract(M,gel(v,1),gel(v,2));
    4729        5040 :   return ZM_inv(M, den);
    4730             : }
    4731             : 
    4732             : /*******************************************************************/
    4733             : /*                                                                 */
    4734             : /*                   Structured Elimination                        */
    4735             : /*                                                                 */
    4736             : /*******************************************************************/
    4737             : 
    4738             : static void
    4739       95966 : rem_col(GEN c, long i, GEN iscol, GEN Wrow, long *rcol, long *rrow)
    4740             : {
    4741       95966 :   long lc = lg(c), k;
    4742       95966 :   iscol[i] = 0; (*rcol)--;
    4743      891434 :   for (k = 1; k < lc; ++k)
    4744             :   {
    4745      795468 :     Wrow[c[k]]--;
    4746      795468 :     if (Wrow[c[k]]==0) (*rrow)--;
    4747             :   }
    4748       95966 : }
    4749             : 
    4750             : static void
    4751        7641 : rem_singleton(GEN M, GEN iscol, GEN Wrow, long idx, long *rcol, long *rrow)
    4752             : {
    4753             :   long i, j;
    4754        7641 :   long nbcol = lg(iscol)-1, last;
    4755             :   do
    4756             :   {
    4757        9568 :     last = 0;
    4758    16915327 :     for (i = 1; i <= nbcol; ++i)
    4759    16905759 :       if (iscol[i])
    4760             :       {
    4761     9074508 :         GEN c = idx ? gmael(M, i, idx): gel(M,i);
    4762     9074508 :         long lc = lg(c);
    4763    83830890 :         for (j = 1; j < lc; ++j)
    4764    74774422 :           if (Wrow[c[j]] == 1)
    4765             :           {
    4766       18040 :             rem_col(c, i, iscol, Wrow, rcol, rrow);
    4767       18040 :             last=1; break;
    4768             :           }
    4769             :       }
    4770        9568 :   } while (last);
    4771        7641 : }
    4772             : 
    4773             : static GEN
    4774        7448 : fill_wcol(GEN M, GEN iscol, GEN Wrow, long *w, GEN wcol)
    4775             : {
    4776        7448 :   long nbcol = lg(iscol)-1;
    4777             :   long i, j, m, last;
    4778             :   GEN per;
    4779       20552 :   for (m = 2, last=0; !last ; m++)
    4780             :   {
    4781    25078159 :     for (i = 1; i <= nbcol; ++i)
    4782             :     {
    4783    25065055 :       wcol[i] = 0;
    4784    25065055 :       if (iscol[i])
    4785             :       {
    4786    13863869 :         GEN c = gmael(M, i, 1);
    4787    13863869 :         long lc = lg(c);
    4788   123889568 :         for (j = 1; j < lc; ++j)
    4789   110025699 :           if (Wrow[c[j]] == m) {  wcol[i]++; last = 1; }
    4790             :       }
    4791             :     }
    4792             :   }
    4793        7448 :   per = vecsmall_indexsort(wcol);
    4794        7448 :   *w = wcol[per[nbcol]];
    4795        7448 :   return per;
    4796             : }
    4797             : 
    4798             : /* M is a RgMs with nbrow rows, A a list of row indices.
    4799             :    Eliminate rows of M with a single entry that do not belong to A,
    4800             :    and the corresponding columns. Also eliminate columns until #colums=#rows.
    4801             :    Return pcol and prow:
    4802             :    pcol is a map from the new columns indices to the old one.
    4803             :    prow is a map from the old rows indices to the new one (0 if removed).
    4804             : */
    4805             : 
    4806             : void
    4807         147 : RgMs_structelim_col(GEN M, long nbcol, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    4808             : {
    4809         147 :   long i, j, k, lA = lg(A);
    4810         147 :   GEN prow = cgetg(nbrow+1, t_VECSMALL);
    4811         147 :   GEN pcol = zero_zv(nbcol);
    4812         147 :   pari_sp av = avma;
    4813         147 :   long rcol = nbcol, rrow = 0, imin = nbcol - usqrt(nbcol);
    4814         147 :   GEN iscol = const_vecsmall(nbcol, 1);
    4815         147 :   GEN Wrow  = zero_zv(nbrow);
    4816         147 :   GEN wcol = cgetg(nbcol+1, t_VECSMALL);
    4817         147 :   pari_sp av2 = avma;
    4818      110397 :   for (i = 1; i <= nbcol; ++i)
    4819             :   {
    4820      110250 :     GEN F = gmael(M, i, 1);
    4821      110250 :     long l = lg(F)-1;
    4822      924686 :     for (j = 1; j <= l; ++j) Wrow[F[j]]++;
    4823             :   }
    4824         147 :   for (j = 1; j < lA; ++j)
    4825             :   {
    4826           0 :     if (Wrow[A[j]] == 0) { *p_col=NULL; return; }
    4827           0 :     Wrow[A[j]] = -1;
    4828             :   }
    4829      228354 :   for (i = 1; i <= nbrow; ++i)
    4830      228207 :     if (Wrow[i]) rrow++;
    4831         147 :   rem_singleton(M, iscol, Wrow, 1, &rcol, &rrow);
    4832         147 :   if (rcol < rrow) pari_err_BUG("RgMs_structelim, rcol<rrow");
    4833        7595 :   while (rcol > rrow)
    4834             :   {
    4835             :     long w;
    4836        7448 :     GEN per = fill_wcol(M, iscol, Wrow, &w, wcol);
    4837       85374 :     for (i = nbcol; i>=imin && wcol[per[i]]>=w && rcol>rrow; i--)
    4838       77926 :       rem_col(gmael(M, per[i], 1), per[i], iscol, Wrow, &rcol, &rrow);
    4839        7448 :     rem_singleton(M, iscol, Wrow, 1, &rcol, &rrow); set_avma(av2);
    4840             :   }
    4841      110397 :   for (j = 1, i = 1; i <= nbcol; ++i)
    4842      110250 :     if (iscol[i]) pcol[j++] = i;
    4843         147 :   setlg(pcol,j);
    4844      228354 :   for (k = 1, i = 1; i <= nbrow; ++i) prow[i] = Wrow[i]? k++: 0;
    4845         147 :   *p_col = pcol; *p_row = prow; set_avma(av);
    4846             : }
    4847             : 
    4848             : void
    4849           0 : RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row)
    4850           0 : { RgMs_structelim_col(M, lg(M)-1, nbrow, A, p_col, p_row); }
    4851             : 
    4852             : GEN
    4853          46 : F2Ms_colelim(GEN M, long nbrow)
    4854             : {
    4855          46 :   long i,j, nbcol = lg(M)-1, rcol = nbcol, rrow = 0;
    4856          46 :   GEN pcol = zero_zv(nbcol);
    4857          46 :   pari_sp av = avma;
    4858          46 :   GEN iscol = const_vecsmall(nbcol, 1), Wrow  = zero_zv(nbrow);
    4859       77470 :   for (i = 1; i <= nbcol; ++i)
    4860             :   {
    4861       77424 :     GEN F = gel(M, i);
    4862       77424 :     long l = lg(F)-1;
    4863     1431938 :     for (j = 1; j <= l; ++j) Wrow[F[j]]++;
    4864             :   }
    4865          46 :   rem_singleton(M, iscol, Wrow, 0, &rcol, &rrow);
    4866       77470 :   for (j = 1, i = 1; i <= nbcol; ++i)
    4867       77424 :     if (iscol[i]) pcol[j++] = i;
    4868          46 :   fixlg(pcol,j); return gc_const(av, pcol);
    4869             : }
    4870             : 
    4871             : /*******************************************************************/
    4872             : /*                                                                 */
    4873             : /*                        EIGENVECTORS                             */
    4874             : /*   (independent eigenvectors, sorted by increasing eigenvalue)   */
    4875             : /*                                                                 */
    4876             : /*******************************************************************/
    4877             : /* assume x is square of dimension > 0 */
    4878             : static int
    4879          53 : RgM_is_symmetric_cx(GEN x, long bit)
    4880             : {
    4881          53 :   pari_sp av = avma;
    4882          53 :   long i, j, l = lg(x);
    4883         239 :   for (i = 1; i < l; i++)
    4884         708 :     for (j = 1; j < i; j++)
    4885             :     {
    4886         522 :       GEN a = gcoeff(x,i,j), b = gcoeff(x,j,i), c = gsub(a,b);
    4887         522 :       if (!gequal0(c) && gexpo(c) - gexpo(a) > -bit) return gc_long(av,0);
    4888             :     }
    4889          21 :   return gc_long(av,1);
    4890             : }
    4891             : static GEN
    4892          53 : eigen_err(int exact, GEN x, long flag, long prec)
    4893             : {
    4894          53 :   pari_sp av = avma;
    4895             :   GEN y;
    4896          53 :   if (RgM_is_symmetric_cx(x, prec - 10))
    4897             :   { /* approximately symmetric: recover */
    4898          21 :     x = jacobi(x, prec); if (flag) return x;
    4899          14 :     return gc_GEN(av, gel(x,2));
    4900             :   }
    4901          32 :   if (!exact) x = bestappr(x, NULL);
    4902          32 :   y = mateigen(x, flag, precdbl(prec));
    4903          32 :   if (exact)
    4904          18 :     y = gprec_wtrunc(y, prec);
    4905          14 :   else if (flag)
    4906           7 :     y = mkvec2(RgV_gtofp(gel(y,1), prec), RgM_gtofp(gel(y,2), prec));
    4907             :   else
    4908           7 :     y = RgM_gtofp(y, prec);
    4909          32 :   return gc_GEN(av, y);
    4910             : }
    4911             : GEN
    4912         144 : mateigen(GEN x, long flag, long prec)
    4913             : {
    4914             :   GEN y, R, T;
    4915         144 :   long k, l, ex, n = lg(x);
    4916             :   int exact;
    4917         144 :   pari_sp av = avma;
    4918             : 
    4919         144 :   if (typ(x)!=t_MAT) pari_err_TYPE("eigen",x);
    4920         144 :   if (n != 1 && n != lgcols(x)) pari_err_DIM("eigen");
    4921         144 :   if (flag < 0 || flag > 1) pari_err_FLAG("mateigen");
    4922         144 :   if (n == 1)
    4923             :   {
    4924          14 :     if (flag) retmkvec2(cgetg(1,t_COL), cgetg(1,t_MAT));
    4925           7 :     return cgetg(1,t_MAT);
    4926             :   }
    4927         130 :   if (n == 2)
    4928             :   {
    4929          14 :     if (flag) retmkvec2(mkcolcopy(gcoeff(x,1,1)), matid(1));
    4930           7 :     return matid(1);
    4931             :   }
    4932             : 
    4933         116 :   ex = 16 - prec;
    4934         116 :   T = charpoly(x,0);
    4935         116 :   exact = RgX_is_QX(T);
    4936         116 :   if (exact)
    4937             :   {
    4938          74 :     T = ZX_radical( Q_primpart(T) );
    4939          74 :     R = nfrootsQ(T); settyp(R, t_COL);
    4940          74 :     if (lg(R)-1 < degpol(T))
    4941             :     { /* add missing complex roots */
    4942          60 :       GEN r = cleanroots(RgX_div(T, roots_to_pol(R, 0)), prec);
    4943          60 :       R = shallowconcat(R, r);
    4944             :     }
    4945             :   }
    4946             :   else
    4947             :   {
    4948          42 :     GEN r1, v = vectrunc_init(lg(T));
    4949             :     long e;
    4950          42 :     R = cleanroots(T,prec);
    4951          42 :     r1 = NULL;
    4952         266 :     for (k = 1; k < lg(R); k++)
    4953             :     {
    4954         224 :       GEN r2 = gel(R,k), r = grndtoi(r2, &e);
    4955         224 :       if (e < ex) r2 = r;
    4956         224 :       if (r1)
    4957             :       {
    4958         182 :         r = gsub(r1,r2);
    4959         182 :         if (gequal0(r) || gexpo(r) < ex) continue;
    4960             :       }
    4961         182 :       vectrunc_append(v, r2);
    4962         182 :       r1 = r2;
    4963             :     }
    4964          42 :     R = v;
    4965             :   }
    4966             :   /* R = distinct complex roots of charpoly(x) */
    4967         116 :   l = lg(R); y = cgetg(l, t_VEC);
    4968         452 :   for (k = 1; k < l; k++)
    4969             :   {
    4970         389 :     GEN M = RgM_Rg_sub_shallow(x, gel(R,k));
    4971         389 :     GEN F = ker_aux(M, gauss_get_pivot_max, x);
    4972         389 :     long d = lg(F)-1;
    4973         389 :     if (!d) { set_avma(av); return eigen_err(exact, x, flag, prec); }
    4974         336 :     gel(y,k) = F;
    4975         336 :     if (flag) gel(R,k) = const_col(d, gel(R,k));
    4976             :   }
    4977          63 :   y = shallowconcat1(y);
    4978          63 :   if (lg(y) > n) { set_avma(av); return eigen_err(exact, x, flag, prec); }
    4979             :   /* lg(y) < n if x is not diagonalizable */
    4980          63 :   if (flag) y = mkvec2(shallowconcat1(R), y);
    4981          63 :   return gc_GEN(av,y);
    4982             : }
    4983             : GEN
    4984           0 : eigen(GEN x, long prec) { return mateigen(x, 0, prec); }
    4985             : 
    4986             : /*******************************************************************/
    4987             : /*                                                                 */
    4988             : /*                           DETERMINANT                           */
    4989             : /*                                                                 */
    4990             : /*******************************************************************/
    4991             : 
    4992             : GEN
    4993       26593 : det0(GEN a,long flag)
    4994             : {
    4995       26593 :   switch(flag)
    4996             :   {
    4997       26579 :     case 0: return det(a);
    4998          14 :     case 1: return det2(a);
    4999           0 :     default: pari_err_FLAG("matdet");
    5000             :   }
    5001             :   return NULL; /* LCOV_EXCL_LINE */
    5002             : }
    5003             : 
    5004             : /* M a 2x2 matrix, returns det(M) */
    5005             : static GEN
    5006       94272 : RgM_det2(GEN M)
    5007             : {
    5008       94272 :   pari_sp av = avma;
    5009       94272 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5010       94272 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5011       94272 :   return gc_upto(av, gsub(gmul(a,d), gmul(b,c)));
    5012             : }
    5013             : /* M a 2x2 ZM, returns det(M) */
    5014             : static GEN
    5015        8722 : ZM_det2(GEN M)
    5016             : {
    5017        8722 :   pari_sp av = avma;
    5018        8722 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
    5019        8722 :   GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
    5020        8722 :   return gc_INT(av, subii(mulii(a,d), mulii(b, c)));
    5021             : }
    5022             : /* M a 3x3 ZM, return det(M) */
    5023             : static GEN
    5024      100472 : ZM_det3(GEN M)
    5025             : {
    5026      100472 :   pari_sp av = avma;
    5027      100472 :   GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2), c = gcoeff(M,1,3);
    5028      100472 :   GEN d = gcoeff(M,2,1), e = gcoeff(M,2,2), f = gcoeff(M,2,3);
    5029      100472 :   GEN g = gcoeff(M,3,1), h = gcoeff(M,3,2), i = gcoeff(M,3,3);
    5030      100472 :   GEN t, D = signe(i)? mulii(subii(mulii(a,e), mulii(b,d)), i): gen_0;
    5031      100472 :   if (signe(g))
    5032             :   {
    5033       66202 :     t = mulii(subii(mulii(b,f), mulii(c,e)), g);
    5034       66202 :     D = addii(D, t);
    5035             :   }
    5036      100472 :   if (signe(h))
    5037             :   {
    5038       77604 :     t = mulii(subii(mulii(c,d), mulii(a,f)), h);
    5039       77604 :     D = addii(D, t);
    5040             :   }
    5041      100472 :   return gc_INT(av, D);
    5042             : }
    5043             : 
    5044             : static GEN
    5045       58228 : det_simple_gauss(GEN a, pivot_fun pivot, GEN data)
    5046             : {
    5047       58228 :   pari_sp av = avma;
    5048       58228 :   long i,j,k, s = 1, nbco = lg(a)-1;
    5049       58228 :   GEN p, x = gen_1;
    5050             : 
    5051       58228 :   a = RgM_shallowcopy(a);
    5052      341992 :   for (i=1; i<nbco; i++)
    5053             :   {
    5054      283770 :     k = pivot(a, data, i, NULL);
    5055      283771 :     if (k > nbco) return gc_GEN(av, gcoeff(a,i,i));
    5056      283764 :     if (k != i)
    5057             :     { /* exchange the lines s.t. k = i */
    5058     1159223 :       for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
    5059      119053 :       s = -s;
    5060             :     }
    5061      283764 :     p = gcoeff(a,i,i);
    5062             : 
    5063      283764 :     x = gmul(x,p);
    5064     1786646 :     for (k=i+1; k<=nbco; k++)
    5065             :     {
    5066     1502883 :       GEN m = gcoeff(a,i,k);
    5067     1502883 :       if (gequal0(m)) continue;
    5068             : 
    5069     1067518 :       m = gdiv(m,p);
    5070     9951071 :       for (j=i+1; j<=nbco; j++)
    5071     8883551 :         gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i)));
    5072             :     }
    5073      283763 :     if (gc_needed(av,2))
    5074             :     {
    5075           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5076           0 :       (void)gc_all(av,2, &a,&x);
    5077             :     }
    5078             :   }
    5079       58222 :   if (s < 0) x = gneg_i(x);
    5080       58222 :   return gc_upto(av, gmul(x, gcoeff(a,nbco,nbco)));
    5081             : }
    5082             : 
    5083             : /* Assumes a a square t_MAT of dimension n > 0. Returns det(a) using
    5084             :  * Gauss-Bareiss. */
    5085             : static GEN
    5086         462 : det_bareiss(GEN a)
    5087             : {
    5088         462 :   pari_sp av = avma;
    5089         462 :   long nbco = lg(a)-1,i,j,k,s = 1;
    5090             :   GEN p, pprec;
    5091             : 
    5092         462 :   a = RgM_shallowcopy(a);
    5093        1337 :   for (pprec=gen_1,i=1; i<nbco; i++,pprec=p)
    5094             :   {
    5095         882 :     int diveuc = (gequal1(pprec)==0);
    5096             :     GEN ci;
    5097             : 
    5098         882 :     p = gcoeff(a,i,i);
    5099         882 :     if (gequal0(p))
    5100             :     {
    5101          14 :       k=i+1; while (k<=nbco && gequal0(gcoeff(a,i,k))) k++;
    5102           7 :       if (k>nbco) return gc_GEN(av, p);
    5103           0 :       swap(gel(a,k), gel(a,i)); s = -s;
    5104           0 :       p = gcoeff(a,i,i);
    5105             :     }
    5106         875 :     ci = gel(a,i);
    5107        2373 :     for (k=i+1; k<=nbco; k++)
    5108             :     {
    5109        1498 :       GEN ck = gel(a,k), m = gel(ck,i);
    5110        1498 :       if (gequal0(m))
    5111             :       {
    5112           7 :         if (gequal1(p))
    5113             :         {
    5114           0 :           if (diveuc)
    5115           0 :             gel(a,k) = gdiv(gel(a,k), pprec);
    5116             :         }
    5117             :         else
    5118          42 :           for (j=i+1; j<=nbco; j++)
    5119             :           {
    5120          35 :             GEN p1 = gmul(p, gel(ck,j));
    5121          35 :             if (diveuc) p1 = gdiv(p1,pprec);
    5122          35 :             gel(ck,j) = p1;
    5123             :           }
    5124             :       }
    5125             :       else
    5126        4662 :         for (j=i+1; j<=nbco; j++)
    5127             :         {
    5128        3171 :           pari_sp av2 = avma;
    5129        3171 :           GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j)));
    5130        3171 :           if (diveuc) p1 = gdiv(p1,pprec);
    5131        3171 :           gel(ck,j) = gc_upto(av2, p1);
    5132             :         }
    5133        1498 :       if (gc_needed(av,2))
    5134             :       {
    5135           0 :         if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
    5136           0 :         (void)gc_all(av,2, &a,&pprec);
    5137           0 :         ci = gel(a,i);
    5138           0 :         p = gcoeff(a,i,i);
    5139             :       }
    5140             :     }
    5141             :   }
    5142         455 :   p = gcoeff(a,nbco,nbco);
    5143         455 :   p = (s < 0)? gneg(p): gcopy(p);
    5144         455 :   return gc_upto(av, p);
    5145             : }
    5146             : 
    5147             : /* count nonzero entries in col j, at most 'max' of them.
    5148             :  * Return their indices */
    5149             : static GEN
    5150        1470 : col_count_non_zero(GEN a, long j, long max)
    5151             : {
    5152        1470 :   GEN v = cgetg(max+1, t_VECSMALL);
    5153        1470 :   GEN c = gel(a,j);
    5154        1470 :   long i, l = lg(a), k = 1;
    5155        5614 :   for (i = 1; i < l; i++)
    5156        5376 :     if (!gequal0(gel(c,i)))
    5157             :     {
    5158        5110 :       if (k > max) return NULL; /* fail */
    5159        3878 :       v[k++] = i;
    5160             :     }
    5161         238 :   setlg(v, k); return v;
    5162             : }
    5163             : /* count nonzero entries in row i, at most 'max' of them.
    5164             :  * Return their indices */
    5165             : static GEN
    5166        1456 : row_count_non_zero(GEN a, long i, long max)
    5167             : {
    5168        1456 :   GEN v = cgetg(max+1, t_VECSMALL);
    5169        1456 :   long j, l = lg(a), k = 1;
    5170        5558 :   for (j = 1; j < l; j++)
    5171        5334 :     if (!gequal0(gcoeff(a,i,j)))
    5172             :     {
    5173        5096 :       if (k > max) return NULL; /* fail */
    5174        3864 :       v[k++] = j;
    5175             :     }
    5176         224 :   setlg(v, k); return v;
    5177             : }
    5178             : 
    5179             : static GEN det_develop(GEN a, long max, double bound);
    5180             : /* (-1)^(i+j) a[i,j] * det RgM_minor(a,i,j) */
    5181             : static GEN
    5182         406 : coeff_det(GEN a, long i, long j, long max, double bound)
    5183             : {
    5184         406 :   GEN c = gcoeff(a, i, j);
    5185         406 :   c = gmul(c, det_develop(RgM_minor(a, i,j), max, bound));
    5186         406 :   if (odd(i+j)) c = gneg(c);
    5187         406 :   return c;
    5188             : }
    5189             : /* a square t_MAT, 'bound' a rough upper bound for the number of
    5190             :  * multiplications we are willing to pay while developing rows/columns before
    5191             :  * switching to Gaussian elimination */
    5192             : static GEN
    5193         658 : det_develop(GEN M, long max, double bound)
    5194             : {
    5195         658 :   pari_sp av = avma;
    5196         658 :   long i,j, n = lg(M)-1, lbest = max+2, best_col = 0, best_row = 0;
    5197         658 :   GEN best = NULL;
    5198             : 
    5199         658 :   if (bound < 1.) return det_bareiss(M); /* too costly now */
    5200             : 
    5201         434 :   switch(n)
    5202             :   {
    5203           0 :     case 0: return gen_1;
    5204           0 :     case 1: return gcopy(gcoeff(M,1,1));
    5205          14 :     case 2: return RgM_det2(M);
    5206             :   }
    5207         420 :   if (max > ((n+2)>>1)) max = (n+2)>>1;
    5208        1876 :   for (j = 1; j <= n; j++)
    5209             :   {
    5210        1470 :     pari_sp av2 = avma;
    5211        1470 :     GEN v = col_count_non_zero(M, j, max);
    5212             :     long lv;
    5213        1470 :     if (!v || (lv = lg(v)) >= lbest) { set_avma(av2); continue; }
    5214         182 :     if (lv == 1) { set_avma(av); return gen_0; }
    5215         182 :     if (lv == 2) {
    5216          14 :       set_avma(av);
    5217          14 :       return gc_upto(av, coeff_det(M,v[1],j,max,bound));
    5218             :     }
    5219         168 :     best = v; lbest = lv; best_col = j;
    5220             :   }
    5221        1862 :   for (i = 1; i <= n; i++)
    5222             :   {
    5223        1456 :     pari_sp av2 = avma;
    5224        1456 :     GEN v = row_count_non_zero(M, i, max);
    5225             :     long lv;
    5226        1456 :     if (!v || (lv = lg(v)) >= lbest) { set_avma(av2); continue; }
    5227           0 :     if (lv == 1) { set_avma(av); return gen_0; }
    5228           0 :     if (lv == 2) {
    5229           0 :       set_avma(av);
    5230           0 :       return gc_upto(av, coeff_det(M,i,v[1],max,bound));
    5231             :     }
    5232           0 :     best = v; lbest = lv; best_row = i;
    5233             :   }
    5234         406 :   if (best_row)
    5235             :   {
    5236           0 :     double d = lbest-1;
    5237           0 :     GEN s = NULL;
    5238             :     long k;
    5239           0 :     bound /= d*d*d;
    5240           0 :     for (k = 1; k < lbest; k++)
    5241             :     {
    5242           0 :       GEN c = coeff_det(M, best_row, best[k], max, bound);
    5243           0 :       s = s? gadd(s, c): c;
    5244             :     }
    5245           0 :     return gc_upto(av, s);
    5246             :   }
    5247         406 :   if (best_col)
    5248             :   {
    5249         168 :     double d = lbest-1;
    5250         168 :     GEN s = NULL;
    5251             :     long k;
    5252         168 :     bound /= d*d*d;
    5253         560 :     for (k = 1; k < lbest; k++)
    5254             :     {
    5255         392 :       GEN c = coeff_det(M, best[k], best_col, max, bound);
    5256         392 :       s = s? gadd(s, c): c;
    5257             :     }
    5258         168 :     return gc_upto(av, s);
    5259             :   }
    5260         238 :   return det_bareiss(M);
    5261             : }
    5262             : 
    5263             : /* area of parallelogram bounded by (v1,v2) */
    5264             : static GEN
    5265       64380 : parallelogramarea(GEN v1, GEN v2)
    5266       64380 : { return gsub(gmul(gnorml2(v1), gnorml2(v2)), gsqr(RgV_dotproduct(v1, v2))); }
    5267             : 
    5268             : /* Square of Hadamard bound for det(a), a square matrix.
    5269             :  * Slight improvement: instead of using the column norms, use the area of
    5270             :  * the parallelogram formed by pairs of consecutive vectors */
    5271             : GEN
    5272       20017 : RgM_Hadamard(GEN a)
    5273             : {
    5274       20017 :   pari_sp av = avma;
    5275       20017 :   long n = lg(a)-1, i;
    5276             :   GEN B;
    5277       20017 :   if (n == 0) return gen_1;
    5278       20017 :   if (n == 1) return gsqr(gcoeff(a,1,1));
    5279       20017 :   a = RgM_gtofp(a, LOWDEFAULTPREC);
    5280       20017 :   B = gen_1;
    5281       84397 :   for (i = 1; i <= n/2; i++)
    5282       64380 :     B = gmul(B, parallelogramarea(gel(a,2*i-1), gel(a,2*i)));
    5283       20017 :   if (odd(n)) B = gmul(B, gnorml2(gel(a, n)));
    5284       20017 :   return gc_INT(av, ceil_safe(B));
    5285             : }
    5286             : 
    5287             : /* If B=NULL, assume B=A' */
    5288             : static GEN
    5289       20872 : ZM_det_slice(GEN A, GEN P, GEN *mod)
    5290             : {
    5291       20872 :   pari_sp av = avma;
    5292       20872 :   long i, n = lg(P)-1;
    5293             :   GEN H, T;
    5294       20872 :   if (n == 1)
    5295             :   {
    5296           0 :     ulong Hp, p = uel(P,1);
    5297           0 :     GEN a = ZM_to_Flm(A, p);
    5298           0 :     Hp = Flm_det_sp(a, p);
    5299           0 :     set_avma(av); *mod = utoipos(p); return utoi(Hp);
    5300             :   }
    5301       20872 :   T = ZV_producttree(P);
    5302       20872 :   A = ZM_nv_mod_tree(A, P, T);
    5303       20872 :   H = cgetg(n+1, t_VECSMALL);
    5304       87546 :   for(i=1; i <= n; i++)
    5305             :   {
    5306       66674 :     ulong p = P[i];
    5307       66674 :     GEN a = gel(A,i);
    5308       66674 :     H[i] = Flm_det_sp(a, p);
    5309             :   }
    5310       20872 :   H = ZV_chinese_tree(H, P, T, ZV_chinesetree(P,T));
    5311       20872 :   *mod = gmael(T, lg(T)-1, 1); return gc_all(av, 2, &H, mod);
    5312             : }
    5313             : 
    5314             : GEN
    5315       20872 : ZM_det_worker(GEN P, GEN A)
    5316             : {
    5317       20872 :   GEN V = cgetg(3, t_VEC);
    5318       20872 :   gel(V,1) = ZM_det_slice(A, P, &gel(V,2));
    5319       20872 :   return V;
    5320             : }
    5321             : 
    5322             : GEN
    5323      130793 : ZM_det(GEN M)
    5324             : {
    5325             :   pari_sp av, av2;
    5326      130793 :   long  n = lg(M)-1;
    5327             :   ulong p, Dp;
    5328             :   forprime_t S;
    5329             :   pari_timer ti;
    5330             :   GEN H, mod, h, q, worker;
    5331             : #ifdef LONG_IS_64BIT
    5332      112116 :   const ulong PMAX = 18446744073709551557UL;
    5333             : #else
    5334       18677 :   const ulong PMAX = 4294967291UL;
    5335             : #endif
    5336             : 
    5337      130793 :   switch(n)
    5338             :   {
    5339           7 :     case 0: return gen_1;
    5340        1575 :     case 1: return icopy(gcoeff(M,1,1));
    5341        8722 :     case 2: return ZM_det2(M);
    5342      100472 :     case 3: return ZM_det3(M);
    5343             :   }
    5344       20017 :   if (DEBUGLEVEL>=4) timer_start(&ti);
    5345       20017 :   av = avma; h = RgM_Hadamard(M); /* |D| <= sqrt(h) */
    5346       20017 :   if (!signe(h)) { set_avma(av); return gen_0; }
    5347       20017 :   h = sqrti(h);
    5348       20017 :   if (lgefint(h) == 3 && (ulong)h[2] <= (PMAX >> 1))
    5349             :   { /* h < p/2 => direct result */
    5350        7233 :     p = PMAX;
    5351        7233 :     Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    5352        7233 :     set_avma(av);
    5353        7233 :     if (!Dp) return gen_0;
    5354        7233 :     return (Dp <= (p>>1))? utoipos(Dp): utoineg(p - Dp);
    5355             :   }
    5356       12784 :   q = gen_1; Dp = 1;
    5357       12784 :   init_modular_big(&S);
    5358       12784 :   p = 0; /* -Wall */
    5359       12784 :   while (cmpii(q, h) <= 0 && (p = u_forprime_next(&S)))
    5360             :   {
    5361       12784 :     av2 = avma; Dp = Flm_det_sp(ZM_to_Flm(M, p), p);
    5362       12784 :     set_avma(av2);
    5363       12784 :     if (Dp) break;
    5364           0 :     q = muliu(q, p);
    5365             :   }
    5366       12784 :   if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
    5367       12784 :   if (!Dp) { set_avma(av); return gen_0; }
    5368       12784 :   worker = snm_closure(is_entry("_ZM_det_worker"), mkvec(M));
    5369       12784 :   H = gen_crt("ZM_det", worker, &S, NULL, expi(h)+1, 0, &mod,
    5370             :               ZV_chinese, NULL);
    5371             :   /* H = det(M) modulo mod, (mod,D) = 1; |det(M) / D| <= h */
    5372       12784 :   H = Fp_center(H, mod, shifti(mod,-1));
    5373       12784 :   return gc_INT(av, H);
    5374             : }
    5375             : 
    5376             : static GEN
    5377        1519 : RgM_det_FpM(GEN a, GEN p)
    5378             : {
    5379        1519 :   pari_sp av = avma;
    5380             :   ulong pp, d;
    5381        1519 :   a = RgM_Fp_init(a,p,&pp);
    5382        1519 :   switch(pp)
    5383             :   {
    5384          70 :   case 0: return gc_upto(av, Fp_to_mod(FpM_det(a,p),p)); break;
    5385          14 :   case 2: d = F2m_det_sp(a); break;
    5386        1435 :   default:d = Flm_det_sp(a, pp); break;
    5387             :   }
    5388        1449 :   set_avma(av); return mkintmodu(d, pp);
    5389             : }
    5390             : 
    5391             : static GEN
    5392          42 : RgM_det_FqM(GEN x, GEN pol, GEN p)
    5393             : {
    5394          42 :   pari_sp av = avma;
    5395          42 :   GEN b, T = RgX_to_FpX(pol, p);
    5396          42 :   if (signe(T) == 0) pari_err_OP("%",x,pol);
    5397          42 :   b = FqM_det(RgM_to_FqM(x, T, p), T, p);
    5398          42 :   if (!b) return gc_NULL(av);
    5399          42 :   return gc_GEN(av, mkpolmod(FpX_to_mod(b, p), FpX_to_mod(T, p)));
    5400             : }
    5401             : 
    5402             : static GEN
    5403       33907 : RgM_det_fast(GEN x, pivot_fun *fun, GEN *data)
    5404             : {
    5405             :   GEN p, pol;
    5406       33907 :   long pa, t = RgM_type(x, &p,&pol,&pa);
    5407       33907 :   set_pivot_fun(fun, data, t, x, p);
    5408       33907 :   switch(t)
    5409             :   {
    5410         301 :     case t_INT:    return ZM_det(x);
    5411         203 :     case t_FRAC:   return QM_det(x);
    5412          63 :     case t_FFELT:  return FFM_det(x, pol);
    5413        1519 :     case t_INTMOD: return RgM_det_FpM(x, p);
    5414          42 :     case RgX_type_code(t_POLMOD, t_INTMOD): return RgM_det_FqM(x, pol, p);
    5415       31779 :     default: return NULL;
    5416             :   }
    5417             : }
    5418             : 
    5419             : static long
    5420         252 : det_init_max(long n)
    5421             : {
    5422         252 :   if (n > 100) return 0;
    5423         252 :   if (n > 50) return 1;
    5424         252 :   if (n > 30) return 4;
    5425         252 :   return 7;
    5426             : }
    5427             : 
    5428             : GEN
    5429      246213 : det(GEN a)
    5430             : {
    5431      246213 :   long n = lg(a)-1;
    5432             :   double B;
    5433             :   GEN data, b;
    5434             :   pivot_fun fun;
    5435             : 
    5436      246213 :   if (typ(a)!=t_MAT) pari_err_TYPE("det",a);
    5437      246213 :   if (!n) return gen_1;
    5438      246171 :   if (n != nbrows(a)) pari_err_DIM("det");
    5439      246164 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5440       69060 :   if (n == 2) return RgM_det2(a);
    5441       33907 :   b = RgM_det_fast(a, &fun, &data);
    5442       33907 :   if (b) return b;
    5443       31779 :   if (data) return det_simple_gauss(a, fun, data);
    5444         252 :   B = (double)n; return det_develop(a, det_init_max(n), B*B*B);
    5445             : }
    5446             : 
    5447             : GEN
    5448      134496 : det2(GEN a)
    5449             : {
    5450      134496 :   long n = lg(a)-1;
    5451             :   GEN data;
    5452             :   pivot_fun fun;
    5453             : 
    5454      134496 :   if (typ(a)!=t_MAT) pari_err_TYPE("det2",a);
    5455      134496 :   if (!n) return gen_1;
    5456      134496 :   if (n != nbrows(a)) pari_err_DIM("det2");
    5457      134496 :   if (n == 1) return gcopy(gcoeff(a,1,1));
    5458       85807 :   if (n == 2) return RgM_det2(a);
    5459       26702 :   set_pivot_fun_all(&fun, &data, a);
    5460       26701 :   return det_simple_gauss(a, fun, data);
    5461             : }
    5462             : 
    5463             : GEN
    5464         203 : QM_det(GEN M)
    5465             : {
    5466         203 :   pari_sp av = avma;
    5467         203 :   GEN cM, pM = Q_primitive_part(M, &cM);
    5468         203 :   GEN b = ZM_det(pM);
    5469         203 :   if (cM) b = gmul(b, gpowgs(cM, lg(M)-1));
    5470         203 :   return gc_upto(av, b);
    5471             : }

Generated by: LCOV version 1.16