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 - bb_hnf.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.12.1 lcov report (development 25406-bf255ab81b) Lines: 630 670 94.0 %
Date: 2020-06-04 05:59:24 Functions: 51 55 92.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2000  The PARI group.
       2             : 
       3             : This file is part of the PARI/GP package.
       4             : 
       5             : PARI/GP is free software; you can redistribute it and/or modify it under the
       6             : terms of the GNU General Public License as published by the Free Software
       7             : Foundation. It is distributed in the hope that it will be useful, but WITHOUT
       8             : ANY WARRANTY WHATSOEVER.
       9             : 
      10             : Check the License for details. You should have received a copy of it, along
      11             : with the package; see the file 'COPYING'. If not, write to the Free Software
      12             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      13             : #include "pari.h"
      14             : #include "paripriv.h"
      15             : 
      16             : #define dbg_printf(lvl) if (DEBUGLEVEL >= (lvl) + 3) err_printf
      17             : 
      18             : /********************************************************************/
      19             : /**                                                                **/
      20             : /**          BLACK BOX HERMITE RINGS AND HOWELL NORMAL FORM        **/
      21             : /**                 contributed by Aurel Page (2017)               **/
      22             : /**                                                                **/
      23             : /********************************************************************/
      24             : 
      25             : /*
      26             :   bb_hermite R:
      27             :     - add(a,b): a+b
      28             :     - neg(a): -a
      29             :     - mul(a,b): a*b
      30             :     - extgcd(a,b,&small): [d,U] with d in R and U in GL_2(R) such that [0;d] = [a;b]*U.
      31             :       set small==1 to assert that U is a 'small' operation (no red needed).
      32             :     - rann(a): b in R such that b*R = {x in R | a*x==0}
      33             :     - lquo(a,b,&r): q in R such that r=a-b*q is a canonical representative
      34             :       of the image of a in R/b*R. The canonical lift of 0 must be 0.
      35             :     - unit(a): u unit in R^* such that a*u is a canonical generator of the ideal a*R
      36             :     - equal0(a): a==0?
      37             :     - equal1(a): a==1?
      38             :     - s(n): image of the small integer n in R
      39             :     - red(a): unique representative of a as an element of R
      40             : 
      41             :   op encoding of elementary operations:
      42             :     - t_VECSMALL: the corresponding permutation (vecpermute)
      43             :     - [Vecsmall([i,j])]: the transposition Ci <-> Cj
      44             :     - [Vecsmall([i]),u], u in R^*: Ci <- Ci*u
      45             :     - [Vecsmall([i,j]),a], a in R: Ci <- Ci + Cj*a
      46             :     - [Vecsmall([i,j,0]),U], U in GL_2(R): (Ci|Cj) <- (Ci|Cj)*U
      47             : */
      48             : 
      49             : struct bb_hermite
      50             : {
      51             :   GEN (*add)(void*, GEN, GEN);
      52             :   GEN (*neg)(void*, GEN);
      53             :   GEN (*mul)(void*, GEN, GEN);
      54             :   GEN (*extgcd)(void*, GEN, GEN, int*);
      55             :   GEN (*rann)(void*, GEN);
      56             :   GEN (*lquo)(void*, GEN, GEN, GEN*);
      57             :   GEN (*unit)(void*, GEN);
      58             :   int (*equal0)(GEN);
      59             :   int (*equal1)(GEN);
      60             :   GEN (*s)(void*, long);
      61             :   GEN (*red)(void*, GEN);
      62             : };
      63             : 
      64             : static GEN
      65    23506674 : _Fp_add(void *data, GEN x, GEN y) { (void) data; return addii(x,y); }
      66             : 
      67             : static GEN
      68     3023360 : _Fp_neg(void *data, GEN x) { (void) data; return negi(x); }
      69             : 
      70             : static GEN
      71    34014780 : _Fp_mul(void *data, GEN x, GEN y) { (void) data; return mulii(x,y); }
      72             : 
      73             : static GEN
      74     1423564 : _Fp_rann(void *data, GEN x)
      75             : {
      76     1423564 :   GEN d, N = (GEN)data;
      77     1423564 :   if (!signe(x)) return gen_1;
      78     1277901 :   d = gcdii(x,N);
      79     1277901 :   return modii(diviiexact(N,d),N);
      80             : }
      81             : 
      82             : static GEN
      83     2212378 : _Fp_lquo(void *data, GEN x, GEN y, GEN* r) { (void) data; return truedvmdii(x,y,r); }
      84             : 
      85             : /* D=MN, p|M => !p|a, p|N => p|a, return M */
      86             : static GEN
      87           0 : Z_split(GEN D, GEN a)
      88             : {
      89             :   long i, n;
      90             :   GEN N;
      91           0 :   n = expi(D);
      92           0 :   n = n<2 ? 1 : expu(n)+1;
      93           0 :   for (i=1;i<=n;i++)
      94           0 :     a = Fp_sqr(a,D);
      95           0 :   N = gcdii(a,D);
      96           0 :   return diviiexact(D,N);
      97             : }
      98             : 
      99             : /* c s.t. gcd(a+cb,N) = gcd(a,b,N) without factoring */
     100             : static GEN
     101           0 : Z_stab(GEN a, GEN b, GEN N)
     102             : {
     103             :   GEN g, a2, N2;
     104           0 :   g = gcdii(a,b);
     105           0 :   g = gcdii(g,N);
     106           0 :   N2 = diviiexact(N,g);
     107           0 :   a2 = diviiexact(a,g);
     108           0 :   return Z_split(N2,a2);
     109             : }
     110             : 
     111             : static GEN
     112     3741290 : _Fp_unit(void *data, GEN x)
     113             : {
     114     3741290 :   GEN g,s,v,d,N=(GEN)data,N2;
     115             :   long i;
     116     3741290 :   if (!signe(x)) return NULL;
     117     3314773 :   g = bezout(x,N,&s,&v);
     118     3314773 :   if (equali1(g) || equali1(gcdii(s,N))) return mkvec2(g,s);
     119       44660 :   N2 = diviiexact(N,g);
     120       61152 :   for (i=0; i<5; i++)
     121             :   {
     122       61152 :     s = addii(s,N2);
     123       61152 :     if (equali1(gcdii(s,N))) return mkvec2(g,s);
     124             :   }
     125           0 :   d = Z_stab(s,N2,N);
     126           0 :   d = mulii(d,N2);
     127           0 :   v = Fp_add(s,d,N);
     128           0 :   if (equali1(v)) return NULL;
     129           0 :   return mkvec2(g,v);
     130             : }
     131             : 
     132             : static GEN
     133     2992217 : _Fp_extgcd(void *data, GEN x, GEN y, int* smallop)
     134             : {
     135             :   GEN d,u,v,m;
     136     2992217 :   if (equali1(y))
     137             :   {
     138      545326 :     *smallop = 1;
     139      545326 :     return mkvec2(y,mkmat2(
     140             :           mkcol2(gen_1,Fp_neg(x,(GEN)data)),
     141             :           mkcol2(gen_0,gen_1)));
     142             :   }
     143     2446891 :   *smallop = 0;
     144     2446891 :   d = bezout(x,y,&u,&v);
     145     2446891 :   if (!signe(d)) return mkvec2(d,matid(2));
     146     2446891 :   m = cgetg(3,t_MAT);
     147     2446891 :   m = mkmat2(
     148             :     mkcol2(diviiexact(y,d),negi(diviiexact(x,d))),
     149             :     mkcol2(u,v));
     150     2446891 :   return mkvec2(d,m);
     151             : }
     152             : 
     153             : static int
     154    69736006 : _Fp_equal0(GEN x) { return !signe(x); }
     155             : 
     156             : static int
     157    20200488 : _Fp_equal1(GEN x) { return equali1(x); }
     158             : 
     159             : static GEN
     160    10569321 : _Fp_s(void *data, long x)
     161             : {
     162    10569321 :   if (!x) return gen_0;
     163      932648 :   if (x==1) return gen_1;
     164           0 :   return modsi(x,(GEN)data);
     165             : }
     166             : 
     167             : static GEN
     168    33486400 : _Fp_red(void *data, GEN x) { return Fp_red(x, (GEN)data); }
     169             : 
     170             : /* p not necessarily prime */
     171             : static const struct bb_hermite Fp_hermite=
     172             :   {_Fp_add,_Fp_neg,_Fp_mul,_Fp_extgcd,_Fp_rann,_Fp_lquo,_Fp_unit,_Fp_equal0,_Fp_equal1,_Fp_s,_Fp_red};
     173             : 
     174             : static const struct bb_hermite*
     175      359819 : get_Fp_hermite(void **data, GEN p)
     176             : {
     177      359819 :   *data = (void*)p; return &Fp_hermite;
     178             : }
     179             : 
     180             : static void
     181    12108456 : gen_redcol(GEN C, long lim, void* data, const struct bb_hermite *R)
     182             : {
     183             :   long i;
     184    45985886 :   for (i=1; i<=lim; i++)
     185    33877430 :     if (!R->equal0(gel(C,i)))
     186    20373414 :       gel(C,i) = R->red(data, gel(C,i));
     187    12108456 : }
     188             : 
     189             : static GEN
     190             : /* return NULL if a==0 */
     191             : /* assume C*a is zero after lim */
     192    15574954 : gen_rightmulcol(GEN C, GEN a, long lim, int fillzeros, void* data, const struct bb_hermite *R)
     193             : {
     194             :   GEN Ca,zero;
     195             :   long i;
     196    15574954 :   if (R->equal1(a)) return C;
     197     9869343 :   if (R->equal0(a)) return NULL;
     198     7186202 :   Ca = cgetg(lg(C),t_COL);
     199    26667694 :   for (i=1; i<=lim; i++)
     200    19481492 :     gel(Ca,i) = R->mul(data, gel(C,i), a);
     201     7186202 :   if (fillzeros && lim+1 < lg(C))
     202             :   {
     203     5489646 :     zero = R->s(data,0);
     204    23868613 :     for (i=lim+1; i<lg(C); i++)
     205    18378967 :       gel(Ca,i) = zero;
     206             :   }
     207     7186202 :   return Ca;
     208             : }
     209             : 
     210             : static void
     211             : /* C1 <- C1 + C2 */
     212             : /* assume C2[i]==0 for i>lim */
     213     4910839 : gen_addcol(GEN C1, GEN C2, long lim, void* data, const struct bb_hermite *R)
     214             : {
     215             :   long i;
     216    17801800 :   for (i=1; i<=lim; i++)
     217    12890961 :     gel(C1,i) = R->add(data, gel(C1,i), gel(C2,i));
     218     4910839 : }
     219             : 
     220             : static void
     221             : /* H[,i] <- H[,i] + C*a */
     222             : /* assume C is zero after lim */
     223     2546009 : gen_addrightmul(GEN H, GEN C, GEN a, long i, long lim, void* data, const struct bb_hermite *R)
     224             : {
     225             :   GEN Ca;
     226     2546009 :   if (R->equal0(a)) return;
     227     1320726 :   Ca = gen_rightmulcol(C, a, lim, 0, data, R);
     228     1320726 :   gen_addcol(gel(H,i), Ca, lim, data, R);
     229             : }
     230             : 
     231             : static GEN
     232     1234413 : gen_zerocol(long n, void* data, const struct bb_hermite *R)
     233             : {
     234     1234413 :   GEN C = cgetg(n+1,t_COL), zero = R->s(data, 0);
     235             :   long i;
     236     6030275 :   for (i=1; i<=n; i++) gel(C,i) = zero;
     237     1234413 :   return C;
     238             : }
     239             : 
     240             : static GEN
     241      591783 : gen_zeromat(long m, long n, void* data, const struct bb_hermite *R)
     242             : {
     243      591783 :   GEN M = cgetg(n+1,t_MAT);
     244             :   long i;
     245     1683788 :   for (i=1; i<=n; i++) gel(M,i) = gen_zerocol(m, data, R);
     246      591783 :   return M;
     247             : }
     248             : 
     249             : static GEN
     250      364112 : gen_colei(long n, long i, void* data, const struct bb_hermite *R)
     251             : {
     252      364112 :   GEN C = cgetg(n+1,t_COL), zero = R->s(data, 0);
     253             :   long j;
     254     2489970 :   for (j=1; j<=n; j++)
     255     2125858 :     if (i!=j)   gel(C,j) = zero;
     256      364112 :     else        gel(C,j) = R->s(data,1);
     257      364112 :   return C;
     258             : }
     259             : 
     260             : static GEN
     261       56056 : gen_matid_hermite(long n, void* data, const struct bb_hermite *R)
     262             : {
     263       56056 :   GEN M = cgetg(n+1,t_MAT);
     264             :   long i;
     265      196119 :   for (i=1; i<=n; i++) gel(M,i) = gen_colei(n, i, data, R);
     266       56056 :   return M;
     267             : }
     268             : 
     269             : static GEN
     270     2090200 : gen_matmul_hermite(GEN A, GEN B, void* data, const struct bb_hermite *R)
     271             : {
     272     2090200 :   GEN M,sum,prod,zero = R->s(data,0);
     273             :   long a,b,c,c2,i,j,k;
     274     2090200 :   RgM_dimensions(A,&a,&c);
     275     2090200 :   RgM_dimensions(B,&c2,&b);
     276     2090200 :   if (c!=c2) pari_err_DIM("gen_matmul_hermite");
     277     2090200 :   M = cgetg(b+1,t_MAT);
     278     4512564 :   for (j=1; j<=b; j++)
     279             :   {
     280     2422364 :     gel(M,j) = cgetg(a+1,t_COL);
     281     6725299 :     for (i=1; i<=a; i++)
     282             :     {
     283     4302935 :       sum = zero;
     284    13948578 :       for (k=1; k<=c; k++)
     285             :       {
     286     9645643 :         prod = R->mul(data, gcoeff(A,i,k), gcoeff(B,k,j));
     287     9645643 :         sum = R->add(data, sum, prod);
     288             :       }
     289     4302935 :       gcoeff(M,i,j) = sum;
     290             :     }
     291     2422364 :     gen_redcol(gel(M,j), a, data, R);
     292             :   }
     293     2090200 :   return M;
     294             : }
     295             : 
     296             : static void
     297             : /* U = [u1,u2]~, C <- A*u1 + B*u2 */
     298             : /* assume both A, B and C are zero after lim */
     299     6162108 : gen_rightlincomb(GEN A, GEN B, GEN U, GEN *C, long lim, void* data, const struct bb_hermite *R)
     300             : {
     301             :   GEN Au1, Bu2;
     302     6162108 :   Au1 = gen_rightmulcol(A, gel(U,1), lim, 1, data, R);
     303     6162108 :   Bu2 = gen_rightmulcol(B, gel(U,2), lim, 1, data, R);
     304     6162108 :   if (!Au1 && !Bu2) { *C = gen_zerocol(lg(A)-1, data, R); return; }
     305     6162108 :   if (!Au1) { *C = Bu2; return; }
     306     3837687 :   if (!Bu2) { *C = Au1; return; }
     307     3589987 :   gen_addcol(Au1, Bu2, lim, data, R);
     308     3589987 :   *C = Au1;
     309             : }
     310             : 
     311             : static void
     312             : /* (H[,i] | H[,j]) <- (H[,i] | H[,j]) * U */
     313             : /* assume both columns are zero after lim */
     314     3081054 : gen_elem(GEN H, GEN U, long i, long j, long lim, void* data, const struct bb_hermite *R)
     315             : {
     316             :   GEN Hi, Hj;
     317     3081054 :   Hi = shallowcopy(gel(H,i));
     318     3081054 :   Hj = shallowcopy(gel(H,j));
     319     3081054 :   gen_rightlincomb(Hi, Hj, gel(U,1), &gel(H,i), lim, data, R);
     320     3081054 :   gen_rightlincomb(Hi, Hj, gel(U,2), &gel(H,j), lim, data, R);
     321     3081054 : }
     322             : 
     323             : static int
     324             : /* assume C is zero after lim */
     325     2365977 : gen_is_zerocol(GEN C, long lim, void* data, const struct bb_hermite *R)
     326             : {
     327             :   long i;
     328             :   (void) data;
     329     4361177 :   for (i=1; i<=lim; i++)
     330     3261213 :     if (!R->equal0(gel(C,i))) return 0;
     331     1099964 :   return 1;
     332             : }
     333             : 
     334             : /* The mkop* functions return NULL if the corresponding operation is the identity */
     335             : 
     336             : static GEN
     337             : /* Ci <- Ci + Cj*a */
     338     1468313 : mkoptransv(long i, long j, GEN a, void* data, const struct bb_hermite *R)
     339             : {
     340     1468313 :   a = R->red(data,a);
     341     1468313 :   if (R->equal0(a)) return NULL;
     342      995642 :   return mkvec2(mkvecsmall2(i,j),a);
     343             : }
     344             : 
     345             : static GEN
     346             : /* (Ci|Cj) <- (Ci|Cj)*U */
     347      770627 : mkopU(long i, long j, GEN U, void* data, const struct bb_hermite *R)
     348             : {
     349      770627 :   if (R->equal1(gcoeff(U,1,1)) && R->equal0(gcoeff(U,1,2))
     350      387999 :       && R->equal1(gcoeff(U,2,2))) return mkoptransv(i,j,gcoeff(U,2,1),data,R);
     351      382628 :   return mkvec2(mkvecsmall3(i,j,0),U);
     352             : }
     353             : 
     354             : static GEN
     355             : /* Ci <- Ci*u */
     356      652652 : mkopmul(long i, GEN u, const struct bb_hermite *R)
     357             : {
     358      652652 :   if (R->equal1(u)) return NULL;
     359      112236 :   return mkvec2(mkvecsmall(i),u);
     360             : }
     361             : 
     362             : static GEN
     363             : /* Ci <-> Cj */
     364       45843 : mkopswap(long i, long j)
     365             : {
     366       45843 :   return mkvec(mkvecsmall2(i,j));
     367             : }
     368             : 
     369             : /* M: t_MAT. Apply the operation op to M by right multiplication. */
     370             : static void
     371      374465 : gen_rightapply(GEN M, GEN op, void* data, const struct bb_hermite *R)
     372             : {
     373             :   GEN M2, ind, X;
     374      374465 :   long i, j, m = lg(gel(M,1))-1;
     375      374465 :   switch (typ(op))
     376             :   {
     377       56014 :     case t_VECSMALL:
     378       56014 :       M2 = vecpermute(M,op);
     379      266056 :       for (i=1; i<lg(M); i++) gel(M,i) = gel(M2,i);
     380       56014 :       return;
     381      318451 :     case t_VEC:
     382      318451 :       ind = gel(op,1);
     383      318451 :       switch (lg(op))
     384             :       {
     385       16135 :         case 2:
     386       16135 :           swap(gel(M,ind[1]),gel(M,ind[2]));
     387       16135 :           return;
     388      302316 :         case 3:
     389      302316 :           X = gel(op,2);
     390      302316 :           i = ind[1];
     391      302316 :           switch (lg(ind))
     392             :           {
     393       37786 :             case 2:
     394       37786 :               gel(M,i) = gen_rightmulcol(gel(M,i), X, m, 0, data, R);
     395       37786 :               gen_redcol(gel(M,i), m, data, R);
     396       37786 :               return;
     397      175693 :             case 3:
     398      175693 :               gen_addrightmul(M, gel(M,ind[2]), X, i, m, data, R);
     399      175693 :               gen_redcol(gel(M,i), m, data, R);
     400      175693 :               return;
     401       88837 :             case 4:
     402       88837 :               j = ind[2];
     403       88837 :               gen_elem(M, X, i, j, m, data, R);
     404       88837 :               gen_redcol(gel(M,i), m, data, R);
     405       88837 :               gen_redcol(gel(M,j), m, data, R);
     406       88837 :               return;
     407             :           }
     408             :       }
     409             :   }
     410             : }
     411             : 
     412             : /* C: t_COL. Apply the operation op to C by left multiplication. */
     413             : static void
     414    10152210 : gen_leftapply(GEN C, GEN op, void* data, const struct bb_hermite *R)
     415             : {
     416             :   GEN C2, ind, X;
     417             :   long i, j;
     418    10152210 :   switch (typ(op))
     419             :   {
     420      576037 :     case t_VECSMALL:
     421      576037 :       C2 = vecpermute(C,perm_inv(op));
     422     4715200 :       for (i=1; i<lg(C); i++) gel(C,i) = gel(C2,i);
     423      576037 :       return;
     424     9576173 :     case t_VEC:
     425     9576173 :       ind = gel(op,1);
     426     9576173 :       switch (lg(op))
     427             :       {
     428      169134 :         case 2:
     429      169134 :           swap(gel(C,ind[1]),gel(C,ind[2]));
     430      169134 :           return;
     431     9407039 :         case 3:
     432     9407039 :           X = gel(op,2);
     433     9407039 :           i = ind[1];
     434     9407039 :           switch (lg(ind))
     435             :           {
     436      372033 :             case 2:
     437      372033 :               gel(C,i) = R->mul(data, X, gel(C,i));
     438      372033 :               gel(C,i) = R->red(data, gel(C,i));
     439      372033 :               return;
     440     7154435 :             case 3:
     441     7154435 :               j = ind[2];
     442     7154435 :               if (R->equal0(gel(C,i))) return;
     443      969965 :               gel(C,j) = R->add(data, gel(C,j), R->mul(data, X, gel(C,i)));
     444      969965 :               return;
     445     1880571 :             case 4:
     446     1880571 :               j = ind[2];
     447     1880571 :               C2 = gen_matmul_hermite(X, mkmat(mkcol2(gel(C,i),gel(C,j))), data, R);
     448     1880571 :               gel(C,i) = gcoeff(C2,1,1);
     449     1880571 :               gel(C,j) = gcoeff(C2,2,1);
     450     1880571 :               return;
     451             :           }
     452             :       }
     453             :   }
     454             : }
     455             : 
     456             : /* \prod_i det ops[i]. Only makes sense if R is commutative. */
     457             : static GEN
     458          42 : gen_detops(GEN ops, void* data, const struct bb_hermite *R)
     459             : {
     460          42 :   GEN d = R->s(data,1);
     461          42 :   long i, l = lg(ops);
     462         231 :   for (i = 1; i < l; i++)
     463             :   {
     464         189 :     GEN X, op = gel(ops,i);
     465         189 :     switch (typ(op))
     466             :     {
     467           0 :       case t_VECSMALL:
     468           0 :         if (perm_sign(op) < 0) d = R->neg(data,d);
     469           0 :         break;
     470         189 :       case t_VEC:
     471         189 :         switch (lg(op))
     472             :         {
     473           0 :           case 2:
     474           0 :             d = R->neg(data,d);
     475           0 :             break;
     476         189 :           case 3:
     477         189 :             X = gel(op,2);
     478         189 :             switch (lg(gel(op,1)))
     479             :             {
     480           0 :               case 2:
     481           0 :                  d = R->mul(data, d, X);
     482           0 :                  d = R->red(data, d);
     483           0 :                  break;
     484         105 :               case 4:
     485             :                {
     486         105 :                  GEN A = gcoeff(X,1,1), B = gcoeff(X,1,2);
     487         105 :                  GEN C = gcoeff(X,2,1), D = gcoeff(X,2,2);
     488         105 :                  GEN AD = R->mul(data,A,D);
     489         105 :                  GEN BC = R->mul(data,B,C);
     490         105 :                  d = R->mul(data, d, R->add(data, AD, R->neg(data,BC)));
     491         105 :                  d = R->red(data, d);
     492         105 :                  break;
     493             :                }
     494             :             }
     495         189 :             break;
     496             :         }
     497         189 :         break;
     498             :     }
     499         189 :   }
     500          42 :   return d;
     501             : }
     502             : 
     503             : static int
     504      209202 : gen_is_inv(GEN x, void* data, const struct bb_hermite *R)
     505             : {
     506      209202 :   GEN u = R->unit(data, x);
     507      209202 :   if (!u) return R->equal1(x);
     508       71743 :   return R->equal1(gel(u,1));
     509             : }
     510             : 
     511             : static long
     512      193431 : gen_last_inv_diago(GEN A, void* data, const struct bb_hermite *R)
     513             : {
     514             :   long i,m,n,j;
     515      193431 :   RgM_dimensions(A,&m,&n);
     516      223461 :   for (i=1,j=n-m+1; i<=m; i++,j++)
     517      209202 :     if (!gen_is_inv(gcoeff(A,i,j),data,R)) return i-1;
     518       14259 :   return m;
     519             : }
     520             : 
     521             : static GEN
     522             : /* remove_zerocols: 0 none, 1 until square, 2 all */
     523             : /* early abort: if not right-invertible, abort, return NULL, and set ops to the
     524             :  * non-invertible pivot */
     525      458302 : gen_howell_i(GEN A, long remove_zerocols, long permute_zerocols, long early_abort, long only_triangular, GEN* ops, void *data, const struct bb_hermite *R)
     526             : {
     527      458302 :   pari_sp av = avma, av1;
     528      458302 :   GEN H,U,piv=gen_0,u,q,a,perm,iszero,C,zero=R->s(data,0),d,g,r,op,one=R->s(data,1);
     529      458302 :   long m,n,i,j,s,si,i2,si2,nbz,lim,extra,maxop=0,nbop=0,lastinv=0;
     530             :   int smallop;
     531             : 
     532      458302 :   av1 = avma;
     533             : 
     534      458302 :   RgM_dimensions(A,&m,&n);
     535      458302 :   if (early_abort && n<m)
     536             :   {
     537       14000 :     if (ops) *ops = zero;
     538       14000 :     return NULL;
     539             :   }
     540      444302 :   if (n<m+1)
     541             :   {
     542      346008 :     extra = m+1-n;
     543      346008 :     H = shallowmatconcat(mkvec2(gen_zeromat(m,extra,data,R),A));
     544             :   }
     545             :   else
     546             :   {
     547       98294 :     extra = 0;
     548       98294 :     H = RgM_shallowcopy(A);
     549             :   }
     550      444302 :   RgM_dimensions(H,&m,&n);
     551      444302 :   s = n-m; /* shift */
     552             : 
     553      444302 :   if(ops)
     554             :   {
     555      250871 :     maxop = m*n + (m*(m+1))/2 + 1;
     556      250871 :     *ops = zerovec(maxop); /* filled with placeholders so gerepile can handle it */
     557             :   }
     558             : 
     559             :   /* put in triangular form */
     560     1861130 :   for (i=m,si=s+m; i>0 && si>extra; i--,si--) /* si = s+i */
     561             :   {
     562     1421742 :     if (R->red) gcoeff(H,i,si) = R->red(data, gcoeff(H,i,si));
     563             :     /* bottom-right diagonal */
     564     6695889 :     for (j = extra+1; j < si; j++)
     565             :     {
     566     5274147 :       if (R->red) gcoeff(H,i,j) = R->red(data, gcoeff(H,i,j));
     567     5274147 :       if (R->equal0(gcoeff(H,i,j))) continue;
     568     2845560 :       U = R->extgcd(data, gcoeff(H,i,j), gcoeff(H,i,si), &smallop);
     569     2845560 :       d = gel(U,1);
     570     2845560 :       U = gel(U,2);
     571     2845560 :       if (n>10)
     572             :       {
     573             :         /* normalize diagonal coefficient -> faster reductions on this row */
     574     1772666 :         u = R->unit(data, d);
     575     1772666 :         if (u)
     576             :         {
     577     1772666 :           g = gel(u,1);
     578     1772666 :           u = gel(u,2);
     579     1772666 :           gcoeff(U,1,2) = R->mul(data, gcoeff(U,1,2), u);
     580     1772666 :           gcoeff(U,2,2) = R->mul(data, gcoeff(U,2,2), u);
     581     1772666 :           d = g;
     582             :         }
     583             :       }
     584     2845560 :       gen_elem(H, U, j, si, i-1, data, R);
     585     2845560 :       if (ops)
     586             :       {
     587      734822 :         op =  mkopU(j,si,U,data,R);
     588      734822 :         if (op) { nbop++; gel(*ops, nbop) = op; }
     589             :       }
     590     2845560 :       gcoeff(H,i,j) = zero;
     591     2845560 :       gcoeff(H,i,si) = d;
     592     2845560 :       if (R->red && !smallop)
     593             :       {
     594     2309789 :         gen_redcol(gel(H,si), i-1, data, R);
     595     2309789 :         gen_redcol(gel(H,j), i-1, data, R);
     596             :       }
     597             :     }
     598             : 
     599     1421742 :     if (early_abort)
     600             :     {
     601      234738 :       d = gcoeff(H,i,si);
     602      234738 :       u = R->unit(data, d);
     603      234738 :       if (u) d = gel(u,1);
     604      234738 :       if (!R->equal1(d))
     605             :       {
     606        4914 :         if (ops) *ops = d;
     607        4914 :         return NULL;
     608             :       }
     609             :     }
     610             : 
     611     1416828 :     if (gc_needed(av,1))
     612             :     {
     613          16 :       if (DEBUGMEM>1) pari_warn(warnmem,"gen_howell[1]. i=%ld",i);
     614          16 :       gerepileall(av1,ops?2:1,&H,ops);
     615             :     }
     616             :   }
     617             : 
     618      439388 :   if (!ops)
     619      193431 :     lastinv = gen_last_inv_diago(H, data, R);
     620             : 
     621             :   /* put in reduced Howell form */
     622      439388 :   if (!only_triangular)
     623             :   {
     624     1994060 :     for (i=m,si=s+m; i>0; i--,si--) /* si = s+i */
     625             :     {
     626             :       /* normalize diagonal coefficient */
     627     1554714 :       if (i<=lastinv) /* lastinv>0 => !ops */
     628       30030 :         gcoeff(H,i,si) = one;
     629             :       else
     630             :       {
     631     1524684 :         u = R->unit(data,gcoeff(H,i,si));
     632     1524684 :         if (u)
     633             :         {
     634     1235773 :           g = gel(u,1);
     635     1235773 :           u = gel(u,2);
     636     1235773 :           gel(H,si) = gen_rightmulcol(gel(H,si), u, i-1, 1, data, R);
     637     1235773 :           gcoeff(H,i,si) = g;
     638     1235773 :           if (R->red) gen_redcol(gel(H,si), i-1, data, R);
     639     1235773 :           if (ops)
     640             :           {
     641      652652 :             op = mkopmul(si,u,R);
     642      652652 :             if (op) { nbop++; gel(*ops,nbop) = op; }
     643             :           }
     644             :         }
     645      288911 :         else if (R->red) gcoeff(H,i,si) = R->red(data, gcoeff(H,i,si));
     646             :       }
     647     1554714 :       piv = gcoeff(H,i,si);
     648             : 
     649             :       /* reduce above diagonal */
     650     1554714 :       if (!R->equal0(piv))
     651             :       {
     652     1265803 :         C = gel(H,si);
     653     3662306 :         for (j=si+1; j<=n; j++)
     654             :         {
     655     2396503 :           if (i<=lastinv) /* lastinv>0 => !ops */
     656       26187 :             gcoeff(H,i,j) = zero;
     657             :           else
     658             :           {
     659     2370316 :             gcoeff(H,i,j) = R->red(data, gcoeff(H,i,j));
     660     2370316 :             if (R->equal1(piv)) { q = gcoeff(H,i,j); r = zero; }
     661     1559439 :             else                q = R->lquo(data, gcoeff(H,i,j), piv, &r);
     662     2370316 :             q = R->neg(data,q);
     663     2370316 :             gen_addrightmul(H, C, q, j, i-1, data, R);
     664     2370316 :             if (ops)
     665             :             {
     666      931438 :               op = mkoptransv(j,si,q,data,R);
     667      931438 :               if (op) { nbop++; gel(*ops,nbop) = op; }
     668             :             }
     669     2370316 :             gcoeff(H,i,j) = r;
     670             :           }
     671             :         }
     672             :       }
     673             : 
     674             :       /* ensure Howell property */
     675     1554714 :       if (i>1)
     676             :       {
     677     1115452 :         a = R->rann(data, piv);
     678     1115452 :         if (!R->equal0(a))
     679             :         {
     680      545307 :           gel(H,1) = gen_rightmulcol(gel(H,si), a, i-1, 1, data, R);
     681      545307 :           if (gel(H,1) == gel(H,si)) gel(H,1) = shallowcopy(gel(H,1)); /* in case rightmulcol cheated */
     682      545307 :           if (ops)
     683             :           {
     684      148876 :             op = mkoptransv(1,si,a,data,R);
     685      148876 :             if (op) { nbop++; gel(*ops,nbop) = op; }
     686             :           }
     687     2185372 :           for (i2=i-1,si2=s+i2; i2>0; i2--,si2--)
     688             :           {
     689     1640065 :             if (R->red) gcoeff(H,i2,1) = R->red(data, gcoeff(H,i2,1));
     690     1640065 :             if (R->equal0(gcoeff(H,i2,1))) continue;
     691      277354 :             if (R->red) gcoeff(H,i2,si2) = R->red(data, gcoeff(H,i2,si2));
     692      277354 :             if (R->equal0(gcoeff(H,i2,si2)))
     693             :             {
     694      130697 :               swap(gel(H,1), gel(H,si2));
     695      130697 :               if (ops) { nbop++; gel(*ops,nbop) = mkopswap(1,si2); }
     696      130697 :               continue;
     697             :             }
     698      146657 :             U = R->extgcd(data, gcoeff(H,i2,1), gcoeff(H,i2,si2), &smallop);
     699      146657 :             d = gel(U,1);
     700      146657 :             U = gel(U,2);
     701      146657 :             gen_elem(H, U, 1, si2, i2-1, data, R);
     702      146657 :             if (ops)
     703             :             {
     704       35805 :               op = mkopU(1,si2,U,data,R);
     705       35805 :               if (op) { nbop++; gel(*ops,nbop) = op; }
     706             :             }
     707      146657 :             gcoeff(H,i2,1) = zero;
     708      146657 :             gcoeff(H,i2,si2) = d;
     709      146657 :             if (R->red && !smallop)
     710             :             {
     711      137102 :               gen_redcol(gel(H,si2), i2, data, R);
     712      137102 :               gen_redcol(gel(H,1), i2-1, data, R);
     713             :             }
     714             :           }
     715             :         }
     716             :       }
     717             : 
     718     1554714 :       if (gc_needed(av,1))
     719             :       {
     720          15 :         if (DEBUGMEM>1) pari_warn(warnmem,"gen_howell[2]. i=%ld",i);
     721          15 :         gerepileall(av1,ops?3:2,&H,&piv,ops);
     722             :       }
     723             :     }
     724             :   }
     725             : 
     726      439388 :   if (R->red)
     727     2761440 :     for (j=1; j<=n; j++)
     728             :     {
     729     2322052 :       lim = maxss(0,m-n+j);
     730     2322052 :       gen_redcol(gel(H,j), lim, data, R);
     731     9231672 :       for (i=lim+1; i<=m; i++) gcoeff(H,i,j) = zero;
     732             :     }
     733             : 
     734             :   /* put zero columns first */
     735      439388 :   iszero = cgetg(n+1,t_VECSMALL);
     736             : 
     737      439388 :   nbz = 0;
     738     2761440 :   for (i=1; i<=n; i++)
     739             :   {
     740     2322052 :     iszero[i] = gen_is_zerocol(gel(H,i), maxss(0,m-n+i), data, R);
     741     2322052 :     if (iszero[i]) nbz++;
     742             :   }
     743             : 
     744      439388 :   j = 1;
     745      439388 :   if (permute_zerocols)
     746             :   {
     747      154637 :     perm = cgetg(n+1, t_VECSMALL);
     748      897071 :     for (i=1; i<=n; i++)
     749      742434 :       if (iszero[i])
     750             :       {
     751      313152 :         perm[j] = i;
     752      313152 :         j++;
     753             :       }
     754             :   }
     755      284751 :   else perm = cgetg(n-nbz+1, t_VECSMALL);
     756     2761440 :   for (i=1; i<=n; i++)
     757     2322052 :     if (!iszero[i])
     758             :     {
     759     1265964 :       perm[j] = i;
     760     1265964 :       j++;
     761             :     }
     762             : 
     763      439388 :   if (permute_zerocols || remove_zerocols==2) H = vecpermute(H, perm);
     764      439388 :   if (permute_zerocols && remove_zerocols==2) H = vecslice(H, nbz+1, n);
     765      439388 :   if (remove_zerocols==1) H = vecslice(H, s+1, n);
     766      439388 :   if (permute_zerocols && ops) { nbop++; gel(*ops,nbop) = perm; }
     767             : 
     768      439388 :   if (ops) { setlg(*ops, nbop+1); } /* should have nbop <= maxop */
     769             : 
     770      439388 :   return H;
     771             : }
     772             : 
     773             : static GEN
     774       94948 : gen_howell(GEN A, long remove_zerocols, long permute_zerocols, long early_abort, long only_triangular, GEN* ops, void *data, const struct bb_hermite *R)
     775             : {
     776       94948 :   pari_sp av = avma;
     777       94948 :   GEN H = gen_howell_i(A, remove_zerocols, permute_zerocols, early_abort, only_triangular, ops, data, R);
     778       94948 :   gerepileall(av, ops?2:1, &H, ops);
     779       94948 :   return H;
     780             : }
     781             : 
     782             : static GEN
     783      150962 : gen_matimage(GEN A, GEN* U, void *data, const struct bb_hermite *R)
     784             : {
     785             :   GEN ops, H;
     786      150962 :   if (U)
     787             :   {
     788       56014 :     pari_sp av = avma, av1;
     789             :     long m, n, i, r, n2, pergc;
     790       56014 :     RgM_dimensions(A,&m,&n);
     791       56014 :     H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
     792       56014 :     av1 = avma;
     793       56014 :     r = lg(H)-1;
     794       56014 :     *U = shallowmatconcat(mkvec2(gen_zeromat(n, maxss(0,m-n+1), data, R), gen_matid_hermite(n, data, R)));
     795       56014 :     n2 = lg(*U)-1;
     796       56014 :     pergc = maxss(m,n);
     797      430479 :     for (i=1; i<lg(ops); i++)
     798             :     {
     799      374465 :       gen_rightapply(*U, gel(ops,i), data, R);
     800      374465 :       if (!(i%pergc) && gc_needed(av,1))
     801             :       {
     802           0 :         if (DEBUGMEM>1) pari_warn(warnmem,"gen_matimage. i=%ld",i);
     803           0 :         gerepileall(av1,1,U);
     804             :       }
     805             :     }
     806       56014 :     if (r<n2) *U = vecslice(*U, n2-r+1, n2);
     807       56014 :     gerepileall(av, 2, &H, U);
     808       56014 :     return H;
     809             :   }
     810       94948 :   else return gen_howell(A, 2, 0, 0, 0, NULL, data, R);
     811             : }
     812             : 
     813             : static GEN
     814             : /* H in true Howell form: no zero columns */
     815       98483 : gen_kernel_howell(GEN H, void *data, const struct bb_hermite *R)
     816             : {
     817             :   GEN K, piv, FK;
     818             :   long m, n, j, j2, i;
     819       98483 :   RgM_dimensions(H,&m,&n);
     820       98483 :   K = gen_zeromat(n, n, data, R);
     821      406595 :   for (j=n,i=m; j>0; j--)
     822             :   {
     823      521619 :     while (R->equal0(gcoeff(H,i,j))) i--;
     824      308112 :     piv = gcoeff(H,i,j);
     825      308112 :     if (R->equal0(piv)) continue;
     826      308112 :     gcoeff(K,j,j) = R->rann(data, piv);
     827      308112 :     if (j<n)
     828             :     {
     829      209629 :       FK = gen_matmul_hermite(matslice(H,i,i,j+1,n), matslice(K, j+1, n, j+1, n), data, R);
     830      751422 :       for (j2=j+1; j2<=n; j2++)
     831      541793 :         gcoeff(K,j,j2) = R->neg(data, R->lquo(data, gcoeff(FK,1,j2-j), piv, NULL));
     832             :         /* remainder has to be zero */
     833             :     }
     834             :   }
     835       98483 :   return K;
     836             : }
     837             : 
     838             : static GEN
     839             : /* (H,ops) Howell form of A, n = number of columns of A, return a kernel of A */
     840       98525 : gen_kernel_from_howell(GEN H, GEN ops, long n, void *data, const struct bb_hermite *R)
     841             : {
     842             :   pari_sp av;
     843             :   GEN K, KH, zC;
     844             :   long m, r, n2, nbz, i, o, extra, j;
     845       98525 :   RgM_dimensions(H,&m,&r);
     846       98525 :   if (!r) return gen_matid_hermite(n, data, R); /* zerology: what if 0==1 in R? */
     847       98483 :   n2 = maxss(n,m+1);
     848       98483 :   extra = n2-n;
     849       98483 :   nbz = n2-r;
     850             :   /* compute kernel of augmented matrix */
     851       98483 :   KH = gen_kernel_howell(H, data, R);
     852       98483 :   zC = gen_zerocol(nbz, data, R);
     853       98483 :   K = cgetg(nbz+r+1, t_MAT);
     854      322532 :   for (i=1; i<=nbz; i++)
     855      224049 :     gel(K,i) = gen_colei(nbz+r, i, data, R);
     856      406595 :   for (i=1; i<=r; i++)
     857      308112 :     gel(K,nbz+i) = shallowconcat(zC, gel(KH,i));
     858      630644 :   for (i=1; i<lg(K); i++)
     859             :   {
     860      532161 :     av = avma;
     861     9775220 :     for (o=lg(ops)-1; o>0; o--)
     862     9243059 :       gen_leftapply(gel(K,i), gel(ops,o), data, R);
     863      532161 :     gen_redcol(gel(K,i), nbz+r, data, R);
     864      532161 :     gerepileall(av, 1, &gel(K,i));
     865             :   }
     866             :   /* deduce kernel of original matrix */
     867       98483 :   K = rowpermute(K, cyclic_perm(n2,extra));
     868       98483 :   K = gen_howell_i(K, 2, 0, 0, 0, NULL, data, R);
     869      225029 :   for (j=lg(K)-1, i=n2; j>0; j--)
     870             :   {
     871      299453 :     while (R->equal0(gcoeff(K,i,j))) i--;
     872      196546 :     if (i<=n) return matslice(K, 1, n, 1, j);
     873             :   }
     874       28483 :   return cgetg(1,t_MAT);
     875             : }
     876             : 
     877             : /* not GC-clean */
     878             : static GEN
     879       54698 : gen_kernel(GEN A, GEN* im, void *data, const struct bb_hermite *R)
     880             : {
     881       54698 :   pari_sp av = avma;
     882       54698 :   long n = lg(A)-1;
     883             :   GEN H, ops, K;
     884       54698 :   H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
     885       54698 :   gerepileall(av,2,&H,&ops);
     886       54698 :   K = gen_kernel_from_howell(H, ops, n, data, R);
     887       54698 :   if (im) *im = H;
     888       54698 :   return K;
     889             : }
     890             : 
     891             : /* right inverse, not GC-clean */
     892             : static GEN
     893      110192 : gen_inv(GEN A, void* data, const struct bb_hermite *R)
     894             : {
     895             :   pari_sp av;
     896      110192 :   GEN ops, H, U, un=R->s(data,1);
     897             :   long m,n,j,o,n2;
     898      110192 :   RgM_dimensions(A,&m,&n);
     899      110192 :   av = avma;
     900      110192 :   H = gen_howell_i(A, 0, 0, 1, 0, &ops, data, R);
     901      110192 :   if (!H) pari_err_INV("gen_inv", ops);
     902       91278 :   n2 = lg(H)-1;
     903       91278 :   ops = gerepilecopy(av,ops); /* get rid of H */
     904       91278 :   U = gen_zeromat(n2, m, data, R);
     905      314648 :   for (j=1; j<=m; j++)
     906      223370 :     gcoeff(U,j+n2-m,j) = un;
     907      314648 :   for (j=1; j<=m; j++)
     908             :   {
     909      223370 :     av = avma;
     910      734361 :     for (o=lg(ops)-1; o>0; o--)
     911      510991 :       gen_leftapply(gel(U,j), gel(ops,o), data, R);
     912      223370 :     gen_redcol(gel(U,j), n2, data, R);
     913      223370 :     gerepileall(av, 1, &gel(U,j));
     914             :   }
     915       91278 :   if (n2>n) U = rowslice(U, n2-n+1, n2);
     916       91278 :   return U;
     917             : }
     918             : 
     919             : /*
     920             :   H true Howell form (no zero columns).
     921             :   Compute Z = Y - HX canonical representative of R^m mod H(R^n)
     922             : */
     923             : static GEN
     924       43925 : gen_reduce_mod_howell(GEN H, GEN Y, GEN *X, void* data, const struct bb_hermite *R)
     925             : {
     926       43925 :   pari_sp av = avma;
     927             :   long m,n,i,j;
     928             :   GEN Z, q, r, C;
     929       43925 :   RgM_dimensions(H,&m,&n);
     930       43925 :   if (X) *X = gen_zerocol(n,data,R);
     931       43925 :   Z = shallowcopy(Y);
     932       43925 :   i = m;
     933      155071 :   for (j=n; j>0; j--)
     934             :   {
     935      180348 :     while (R->equal0(gcoeff(H,i,j))) i--;
     936      111146 :     q = R->lquo(data, gel(Z,i), gcoeff(H,i,j), &r);
     937      111146 :     gel(Z,i) = r;
     938      111146 :     C = gen_rightmulcol(gel(H,j), R->neg(data,q), i, 0, data, R);
     939      111146 :     if (C) gen_addcol(Z, C, i-1, data, R);
     940      111146 :     if (X) gel(*X,j) = q;
     941             :   }
     942       43925 :   gen_redcol(Z, lg(Z)-1, data, R);
     943       43925 :   if (X)
     944             :   {
     945       43925 :     gerepileall(av, 2, &Z, X);
     946       43925 :     return Z;
     947             :   }
     948           0 :   return gerepilecopy(av, Z);
     949             : }
     950             : 
     951             : /* H: Howell form of A */
     952             : /* (m,n): dimensions of original matrix A */
     953             : /* return NULL if no solution */
     954             : static GEN
     955       43925 : gen_solve_from_howell(GEN H, GEN ops, long m, long n, GEN Y, void* data, const struct bb_hermite *R)
     956             : {
     957       43925 :   pari_sp av = avma;
     958             :   GEN Z, X;
     959             :   long n2, mH, nH, i;
     960       43925 :   RgM_dimensions(H,&mH,&nH);
     961       43925 :   n2 = maxss(n,m+1);
     962       43925 :   Z = gen_reduce_mod_howell(H, Y, &X, data, R);
     963       43925 :   if (!gen_is_zerocol(Z,m,data,R)) { set_avma(av); return NULL; }
     964             : 
     965       43876 :   X = shallowconcat(zerocol(n2-nH),X);
     966      442036 :   for (i=lg(ops)-1; i>0; i--) gen_leftapply(X, gel(ops,i), data, R);
     967       43876 :   X = vecslice(X, n2-n+1, n2);
     968       43876 :   gen_redcol(X, n, data, R);
     969       43876 :   return gerepilecopy(av, X);
     970             : }
     971             : 
     972             : /* return NULL if no solution, not GC-clean */
     973             : static GEN
     974       43925 : gen_solve(GEN A, GEN Y, GEN* K, void* data, const struct bb_hermite *R)
     975             : {
     976             :   GEN H, ops, X;
     977             :   long m,n;
     978             : 
     979       43925 :   RgM_dimensions(A,&m,&n);
     980       43925 :   if (!n) m = lg(Y)-1;
     981       43925 :   H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
     982       43925 :   X = gen_solve_from_howell(H, ops, m, n, Y, data, R);
     983       43925 :   if (!X) return NULL;
     984       43876 :   if (K) *K = gen_kernel_from_howell(H, ops, n, data, R);
     985       43876 :   return X;
     986             : }
     987             : 
     988             : GEN
     989      150997 : matimagemod(GEN A, GEN d, GEN* U)
     990             : {
     991             :   void *data;
     992             :   const struct bb_hermite* R;
     993      150997 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matimagemod", A);
     994      150983 :   if (typ(d)!=t_INT) pari_err_TYPE("matimagemod", d);
     995      150976 :   if (signe(d)<=0) pari_err_DOMAIN("matimagemod", "d", "<=", gen_0, d);
     996      150969 :   if (equali1(d)) return cgetg(1,t_MAT);
     997      150962 :   R = get_Fp_hermite(&data, d);
     998      150962 :   return gen_matimage(A, U, data, R);
     999             : }
    1000             : 
    1001             : /* for testing purpose */
    1002             : /*
    1003             : GEN
    1004             : ZM_hnfmodid2(GEN A, GEN d)
    1005             : {
    1006             :   pari_sp av = avma;
    1007             :   void *data;
    1008             :   long i;
    1009             :   const struct bb_hermite* R = get_Fp_hermite(&data, d);
    1010             :   GEN H;
    1011             :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("ZM_hnfmodid2", A);
    1012             :   if (typ(d)!=t_INT) pari_err_TYPE("ZM_hnfmodid2", d);
    1013             :   H = gen_howell_i(A, 1, 0, 0, 0, NULL, data, R);
    1014             :   for (i=1; i<lg(H); i++)
    1015             :     if (!signe(gcoeff(H,i,i))) gcoeff(H,i,i) = d;
    1016             :   return gerepilecopy(av,H);
    1017             : }
    1018             : */
    1019             : 
    1020             : GEN
    1021          84 : matdetmod(GEN A, GEN d)
    1022             : {
    1023          84 :   pari_sp av = avma;
    1024             :   void *data;
    1025             :   const struct bb_hermite* R;
    1026          84 :   long n = lg(A)-1, i;
    1027             :   GEN D, H, ops;
    1028          84 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matdetmod", A);
    1029          70 :   if (typ(d)!=t_INT) pari_err_TYPE("matdetmod", d);
    1030          70 :   if (signe(d)<=0) pari_err_DOMAIN("matdetmod", "d", "<=", gen_0, d);
    1031          63 :   if (!n) return equali1(d) ? gen_0 : gen_1;
    1032          56 :   if (n != nbrows(A)) pari_err_DIM("matdetmod");
    1033          49 :   if (equali1(d)) return gen_0;
    1034          42 :   R = get_Fp_hermite(&data, d);
    1035          42 :   H = gen_howell_i(A, 1, 0, 0, 1, &ops, data, R);
    1036          42 :   D = gen_detops(ops, data, R);
    1037          42 :   D = Fp_inv(D, d);
    1038         203 :   for (i = 1; i <= n; i++) D = Fp_mul(D, gcoeff(H,i,i), d);
    1039          42 :   return gerepileuptoint(av, D);
    1040             : }
    1041             : 
    1042             : GEN
    1043       54733 : matkermod(GEN A, GEN d, GEN* im)
    1044             : {
    1045       54733 :   pari_sp av = avma;
    1046             :   void *data;
    1047             :   const struct bb_hermite* R;
    1048             :   long m,n;
    1049             :   GEN K;
    1050       54733 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matkermod", A);
    1051       54719 :   if (typ(d)!=t_INT) pari_err_TYPE("matkermod", d);
    1052       54712 :   if (signe(d)<=0) pari_err_DOMAIN("makermod", "d", "<=", gen_0, d);
    1053       54705 :   if (equali1(d)) return cgetg(1,t_MAT);
    1054       54698 :   R = get_Fp_hermite(&data, d);
    1055       54698 :   RgM_dimensions(A,&m,&n);
    1056       54698 :   if (!im && m>2*n) /* TODO tune treshold */
    1057        3577 :     A = shallowtrans(matimagemod(shallowtrans(A),d,NULL));
    1058       54698 :   K = gen_kernel(A, im, data, R);
    1059       54698 :   gerepileall(av,im?2:1,&K,im);
    1060       54698 :   return K;
    1061             : }
    1062             : 
    1063             : /* left inverse */
    1064             : GEN
    1065      117101 : matinvmod(GEN A, GEN d)
    1066             : {
    1067      117101 :   pari_sp av = avma;
    1068             :   void *data;
    1069             :   const struct bb_hermite* R;
    1070             :   GEN U;
    1071      117101 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matinvmod", A);
    1072      117087 :   if (typ(d)!=t_INT) pari_err_TYPE("matinvmod", d);
    1073      117080 :   if (signe(d)<=0) pari_err_DOMAIN("matinvmod", "d", "<=", gen_0, d);
    1074      117073 :   if (equali1(d)) {
    1075             :     long m,n;
    1076        6881 :     RgM_dimensions(A,&m,&n);
    1077        6881 :     if (m<n) pari_err_INV("matinvmod",A);
    1078        6874 :     return zeromatcopy(n,m);
    1079             :   }
    1080      110192 :   R = get_Fp_hermite(&data, d);
    1081      110192 :   U = gen_inv(shallowtrans(A), data, R);
    1082       91278 :   return gerepilecopy(av, shallowtrans(U));
    1083             : }
    1084             : 
    1085             : /* assume all D[i]>0, not GC-clean */
    1086             : static GEN
    1087       43925 : matsolvemod_finite(GEN M, GEN D, GEN Y, long flag)
    1088             : {
    1089             :   void *data;
    1090             :   const struct bb_hermite* R;
    1091             :   GEN X, K, d;
    1092             :   long m, n;
    1093             : 
    1094       43925 :   RgM_dimensions(M,&m,&n);
    1095       43925 :   if (typ(D)==t_COL)
    1096             :   { /* create extra variables for the D[i] */
    1097             :     GEN MD;
    1098          84 :     long i, i2, extra = 0;
    1099          84 :     d = gen_1;
    1100         231 :     for (i=1; i<lg(D); i++) d = lcmii(d,gel(D,i));
    1101         231 :     for (i=1; i<lg(D); i++) if (!equalii(gel(D,i),d)) extra++;
    1102          84 :     MD = cgetg(extra+1,t_MAT);
    1103          84 :     i2 = 1;
    1104         231 :     for (i=1; i<lg(D); i++)
    1105         147 :       if (!equalii(gel(D,i),d))
    1106             :       {
    1107          77 :         gel(MD,i2) = Rg_col_ei(gel(D,i),m,i);
    1108          77 :         i2++;
    1109             :       }
    1110          84 :     M = shallowconcat(M,MD);
    1111             :   }
    1112       43841 :   else d = D;
    1113             : 
    1114       43925 :   R = get_Fp_hermite(&data, d);
    1115       43925 :   X = gen_solve(M, Y, flag? &K: NULL, data, R);
    1116       43925 :   if (!X) return gen_0;
    1117       43876 :   X = vecslice(X,1,n);
    1118             : 
    1119       43876 :   if (flag)
    1120             :   {
    1121       43827 :     K = rowslice(K,1,n);
    1122       43827 :     K = hnfmodid(shallowconcat(zerocol(n),K),d);
    1123       43827 :     X = mkvec2(X,K);
    1124             :   }
    1125       43876 :   return X;
    1126             : }
    1127             : 
    1128             : /* Return a solution of congruence system sum M[i,j] X_j = Y_i mod D_i
    1129             :  * If pU != NULL, put in *pU a Z-basis of the homogeneous system.
    1130             :  * Works for all non negative D_i but inefficient compared to
    1131             :  * matsolvemod_finite; to be used only when one D_i is 0 */
    1132             : static GEN
    1133          70 : gaussmoduloall(GEN M, GEN D, GEN Y, GEN *pU)
    1134             : {
    1135          70 :   pari_sp av = avma;
    1136          70 :   long n, m, j, l, lM = lg(M);
    1137             :   GEN delta, H, U, u1, u2, x;
    1138             : 
    1139          70 :   if (lM == 1)
    1140             :   {
    1141          28 :     long lY = 0;
    1142          28 :     switch(typ(Y))
    1143             :     {
    1144           0 :       case t_INT: break;
    1145          28 :       case t_COL: lY = lg(Y); break;
    1146           0 :       default: pari_err_TYPE("gaussmodulo",Y);
    1147             :     }
    1148          28 :     switch(typ(D))
    1149             :     {
    1150          14 :       case t_INT: break;
    1151          14 :       case t_COL: if (lY && lY != lg(D)) pari_err_DIM("gaussmodulo");
    1152          14 :                   break;
    1153           0 :       default: pari_err_TYPE("gaussmodulo",D);
    1154             :     }
    1155          28 :     if (pU) *pU = cgetg(1, t_MAT);
    1156          28 :     return cgetg(1,t_COL);
    1157             :   }
    1158          42 :   n = nbrows(M);
    1159          42 :   switch(typ(D))
    1160             :   {
    1161          14 :     case t_COL:
    1162          14 :       if (lg(D)-1!=n) pari_err_DIM("gaussmodulo");
    1163          14 :       delta = diagonal_shallow(D); break;
    1164          28 :     case t_INT: delta = scalarmat_shallow(D,n); break;
    1165           0 :     default: pari_err_TYPE("gaussmodulo",D);
    1166             :       return NULL; /* LCOV_EXCL_LINE */
    1167             :   }
    1168          42 :   switch(typ(Y))
    1169             :   {
    1170           0 :     case t_INT: Y = const_col(n, Y); break;
    1171          42 :     case t_COL:
    1172          42 :       if (lg(Y)-1!=n) pari_err_DIM("gaussmodulo");
    1173          42 :       break;
    1174           0 :     default: pari_err_TYPE("gaussmodulo",Y);
    1175             :       return NULL; /* LCOV_EXCL_LINE */
    1176             :   }
    1177          42 :   H = ZM_hnfall_i(shallowconcat(M,delta), &U, 1);
    1178          42 :   Y = hnf_solve(H,Y); if (!Y) return gen_0;
    1179          35 :   l = lg(H); /* may be smaller than lM if some moduli are 0 */
    1180          35 :   n = l-1;
    1181          35 :   m = lg(U)-1 - n;
    1182          35 :   u1 = cgetg(m+1,t_MAT);
    1183          35 :   u2 = cgetg(n+1,t_MAT);
    1184          84 :   for (j=1; j<=m; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u1,j) = c; }
    1185          35 :   U += m;
    1186          77 :   for (j=1; j<=n; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u2,j) = c; }
    1187             :   /*       (u1 u2)
    1188             :    * (M D) (*  * ) = (0 H) */
    1189          35 :   u1 = ZM_lll(u1, 0.75, LLL_INPLACE);
    1190          35 :   Y = ZM_ZC_mul(u2,Y);
    1191          35 :   x = ZC_reducemodmatrix(Y, u1);
    1192          35 :   if (!pU) x = gerepileupto(av, x);
    1193             :   else
    1194             :   {
    1195          14 :     gerepileall(av, 2, &x, &u1);
    1196          14 :     *pU = u1;
    1197             :   }
    1198          35 :   return x;
    1199             : }
    1200             : /* to be used when one D_i is 0 */
    1201             : static GEN
    1202          70 : msolvemod0(GEN M, GEN D, GEN Y, long flag)
    1203             : {
    1204          70 :   pari_sp av = avma;
    1205             :   GEN y, x, U;
    1206          70 :   if (!flag) return gaussmoduloall(M,D,Y,NULL);
    1207          28 :   y = cgetg(3,t_VEC);
    1208          28 :   x = gaussmoduloall(M,D,Y,&U);
    1209          28 :   if (x == gen_0) { set_avma(av); return gen_0; }
    1210          21 :   gel(y,1) = x;
    1211          21 :   gel(y,2) = U; return y;
    1212             : 
    1213             : }
    1214             : GEN
    1215       44107 : matsolvemod(GEN M, GEN D, GEN Y, long flag)
    1216             : {
    1217       44107 :   pari_sp av = avma;
    1218       44107 :   long m, n, i, char0 = 0;
    1219       44107 :   if (typ(M)!=t_MAT || !RgM_is_ZM(M)) pari_err_TYPE("matsolvemod (M)",M);
    1220       44093 :   RgM_dimensions(M,&m,&n);
    1221       44093 :   if (typ(D)!=t_INT && (typ(D)!=t_COL || !RgV_is_ZV(D)))
    1222          28 :     pari_err_TYPE("matsolvemod (D)",D);
    1223       44065 :   if (n)
    1224       43932 :     { if (typ(D)==t_COL && lg(D)!=m+1) pari_err_DIM("matsolvemod [1]"); }
    1225             :   else
    1226         133 :     { if (typ(D)==t_COL) m = lg(D)-1; }
    1227       44058 :   if (typ(Y)==t_INT)
    1228       43862 :     Y = const_col(m,Y);
    1229         196 :   else if (typ(Y)!=t_COL || !RgV_is_ZV(Y)) pari_err_TYPE("matsolvemod (Y)",Y);
    1230       44030 :   if (!n && !m) m = lg(Y)-1;
    1231       43974 :   else if (m != lg(Y)-1) pari_err_DIM("matsolvemod [2]");
    1232       44009 :   if (typ(D)==t_INT)
    1233             :   {
    1234       43890 :     if (signe(D)<0) pari_err_DOMAIN("matsolvemod","D","<",gen_0,D);
    1235       43883 :     if (!signe(D)) char0 = 1;
    1236             :   }
    1237             :   else /*typ(D)==t_COL*/
    1238         301 :     for (i=1; i<=m; i++)
    1239             :     {
    1240         189 :       if (signe(gel(D,i))<0)
    1241           7 :         pari_err_DOMAIN("matsolvemod","D[i]","<",gen_0,gel(D,i));
    1242         182 :       if (!signe(gel(D,i))) char0 = 1;
    1243             :     }
    1244       87920 :   return gerepilecopy(av, char0? msolvemod0(M,D,Y,flag)
    1245       43925 :                                : matsolvemod_finite(M,D,Y,flag));
    1246             : }
    1247             : GEN
    1248           0 : gaussmodulo2(GEN M, GEN D, GEN Y) { return matsolvemod(M,D,Y, 1); }
    1249             : GEN
    1250           0 : gaussmodulo(GEN M, GEN D, GEN Y) { return matsolvemod(M,D,Y, 0); }

Generated by: LCOV version 1.13