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.18.0 lcov report (development 29589-e347ff5c25) Lines: 643 664 96.8 %
Date: 2024-10-06 09:07:08 Functions: 53 55 96.4 %
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; 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             : #include "pari.h"
      15             : #include "paripriv.h"
      16             : 
      17             : #define DEBUGLEVEL DEBUGLEVEL_mathnf
      18             : 
      19             : #define dbg_printf(lvl) if (DEBUGLEVEL >= (lvl) + 3) err_printf
      20             : 
      21             : /********************************************************************/
      22             : /**                                                                **/
      23             : /**          BLACK BOX HERMITE RINGS AND HOWELL NORMAL FORM        **/
      24             : /**                 contributed by Aurel Page (2017)               **/
      25             : /**                                                                **/
      26             : /********************************************************************/
      27             : 
      28             : /*
      29             :   bb_hermite R:
      30             :     - add(a,b): a+b
      31             :     - neg(a): -a
      32             :     - mul(a,b): a*b
      33             :     - extgcd(a,b,&small): [d,U] with d in R and U in GL_2(R) such that [0;d] = [a;b]*U.
      34             :       set small==1 to assert that U is a 'small' operation (no red needed).
      35             :     - rann(a): b in R such that b*R = {x in R | a*x==0}
      36             :     - lquo(a,b,&r): q in R such that r=a-b*q is a canonical representative
      37             :       of the image of a in R/b*R. The canonical lift of 0 must be 0.
      38             :     - unit(a): u unit in R^* such that a*u is a canonical generator of the ideal a*R
      39             :     - equal0(a): a==0?
      40             :     - equal1(a): a==1?
      41             :     - s(n): image of the small integer n in R
      42             :     - red(a): unique representative of a as an element of R
      43             : 
      44             :   op encoding of elementary operations:
      45             :     - t_VECSMALL: the corresponding permutation (vecpermute)
      46             :     - [Vecsmall([i,j])]: the transposition Ci <-> Cj
      47             :     - [Vecsmall([i]),u], u in R^*: Ci <- Ci*u
      48             :     - [Vecsmall([i,j]),a], a in R: Ci <- Ci + Cj*a
      49             :     - [Vecsmall([i,j,0]),U], U in GL_2(R): (Ci|Cj) <- (Ci|Cj)*U
      50             : */
      51             : 
      52             : struct bb_hermite
      53             : {
      54             :   GEN (*add)(void*, GEN, GEN);
      55             :   GEN (*neg)(void*, GEN);
      56             :   GEN (*mul)(void*, GEN, GEN);
      57             :   GEN (*extgcd)(void*, GEN, GEN, int*);
      58             :   GEN (*rann)(void*, GEN);
      59             :   GEN (*lquo)(void*, GEN, GEN, GEN*);
      60             :   GEN (*unit)(void*, GEN);
      61             :   int (*equal0)(GEN);
      62             :   int (*equal1)(GEN);
      63             :   GEN (*s)(void*, long);
      64             :   GEN (*red)(void*, GEN);
      65             : };
      66             : 
      67             : static GEN
      68    26101097 : _Fp_add(void *data, GEN x, GEN y) { (void) data; return addii(x,y); }
      69             : 
      70             : static GEN
      71     4305178 : _Fp_neg(void *data, GEN x) { (void) data; return negi(x); }
      72             : 
      73             : static GEN
      74    37382729 : _Fp_mul(void *data, GEN x, GEN y) { (void) data; return mulii(x,y); }
      75             : 
      76             : static GEN
      77     2168283 : _Fp_rann(void *data, GEN x)
      78             : {
      79     2168283 :   GEN d, N = (GEN)data;
      80     2168283 :   if (!signe(x)) return gen_1;
      81     2021577 :   d = gcdii(x,N);
      82     2021383 :   return modii(diviiexact(N,d),N);
      83             : }
      84             : 
      85             : static GEN
      86     2207975 : _Fp_lquo(void *data, GEN x, GEN y, GEN* r) { (void) data; return truedvmdii(x,y,r); }
      87             : 
      88             : /* D=MN, p|M => !p|a, p|N => p|a, return M */
      89             : static GEN
      90          28 : Z_split(GEN D, GEN a)
      91             : {
      92             :   long i, n;
      93             :   GEN N;
      94          28 :   n = expi(D);
      95          28 :   n = n<2 ? 1 : expu(n)+1;
      96         196 :   for (i=1;i<=n;i++)
      97         168 :     a = Fp_sqr(a,D);
      98          28 :   N = gcdii(a,D);
      99          28 :   return diviiexact(D,N);
     100             : }
     101             : 
     102             : /* c s.t. gcd(a+cb,N) = gcd(a,b,N) without factoring */
     103             : static GEN
     104          28 : Z_stab(GEN a, GEN b, GEN N)
     105             : {
     106             :   GEN g, a2, N2;
     107          28 :   g = gcdii(a,b);
     108          28 :   g = gcdii(g,N);
     109          28 :   N2 = diviiexact(N,g);
     110          28 :   a2 = diviiexact(a,g);
     111          28 :   return Z_split(N2,a2);
     112             : }
     113             : 
     114             : static GEN
     115     6249258 : _Fp_unit(void *data, GEN x)
     116             : {
     117     6249258 :   GEN g,s,v,d,N=(GEN)data,N2;
     118             :   long i;
     119     6249258 :   if (!signe(x)) return NULL;
     120     5817792 :   g = bezout(x,N,&s,&v);
     121     5818345 :   if (equali1(g) || equali1(gcdii(s,N))) return mkvec2(g,s);
     122       44591 :   N2 = diviiexact(N,g);
     123       61196 :   for (i=0; i<5; i++)
     124             :   {
     125       61168 :     s = addii(s,N2);
     126       61168 :     if (equali1(gcdii(s,N))) return mkvec2(g,s);
     127             :   }
     128          28 :   d = Z_stab(s,N2,N);
     129          28 :   d = mulii(d,N2);
     130          28 :   v = Fp_add(s,d,N);
     131          28 :   if (equali1(v)) return NULL;
     132          28 :   return mkvec2(g,v);
     133             : }
     134             : 
     135             : static GEN
     136     3203028 : _Fp_extgcd(void *data, GEN x, GEN y, int* smallop)
     137             : {
     138             :   GEN d,u,v,m;
     139     3203028 :   if (equali1(y))
     140             :   {
     141      601826 :     *smallop = 1;
     142      601826 :     return mkvec2(y,mkmat2(
     143             :           mkcol2(gen_1,Fp_neg(x,(GEN)data)),
     144             :           mkcol2(gen_0,gen_1)));
     145             :   }
     146     2601202 :   *smallop = 0;
     147     2601202 :   d = bezout(x,y,&u,&v);
     148     2601204 :   if (!signe(d)) return mkvec2(d,matid(2));
     149     2601204 :   m = cgetg(3,t_MAT);
     150     2601203 :   m = mkmat2(
     151             :     mkcol2(diviiexact(y,d),negi(diviiexact(x,d))),
     152             :     mkcol2(u,v));
     153     2601205 :   return mkvec2(d,m);
     154             : }
     155             : 
     156             : static int
     157    89700674 : _Fp_equal0(GEN x) { return !signe(x); }
     158             : 
     159             : static int
     160    26232743 : _Fp_equal1(GEN x) { return equali1(x); }
     161             : 
     162             : static GEN
     163    14535021 : _Fp_s(void *data, long x)
     164             : {
     165    14535021 :   if (!x) return gen_0;
     166     1899302 :   if (x==1) return gen_1;
     167           0 :   return modsi(x,(GEN)data);
     168             : }
     169             : 
     170             : static GEN
     171    42231192 : _Fp_red(void *data, GEN x) { return Fp_red(x, (GEN)data); }
     172             : 
     173             : /* p not necessarily prime */
     174             : static const struct bb_hermite Fp_hermite=
     175             :   {_Fp_add,_Fp_neg,_Fp_mul,_Fp_extgcd,_Fp_rann,_Fp_lquo,_Fp_unit,_Fp_equal0,_Fp_equal1,_Fp_s,_Fp_red};
     176             : 
     177             : static const struct bb_hermite*
     178      843106 : get_Fp_hermite(void **data, GEN p)
     179             : {
     180      843106 :   *data = (void*)p; return &Fp_hermite;
     181             : }
     182             : 
     183             : static void
     184    17073165 : gen_redcol(GEN C, long lim, void* data, const struct bb_hermite *R)
     185             : {
     186             :   long i;
     187    61384958 :   for (i=1; i<=lim; i++)
     188    44314178 :     if (!R->equal0(gel(C,i)))
     189    23667880 :       gel(C,i) = R->red(data, gel(C,i));
     190    17070780 : }
     191             : 
     192             : static GEN
     193             : /* return NULL if a==0 */
     194             : /* assume C*a is zero after lim */
     195    17714041 : gen_rightmulcol(GEN C, GEN a, long lim, int fillzeros, void* data, const struct bb_hermite *R)
     196             : {
     197             :   GEN Ca,zero;
     198             :   long i;
     199    17714041 :   if (R->equal1(a)) return C;
     200    10576534 :   if (R->equal0(a)) return NULL;
     201     7586814 :   Ca = cgetg(lg(C),t_COL);
     202    27933252 :   for (i=1; i<=lim; i++)
     203    20346456 :     gel(Ca,i) = R->mul(data, gel(C,i), a);
     204     7586796 :   if (fillzeros && lim+1 < lg(C))
     205             :   {
     206     5817231 :     zero = R->s(data,0);
     207    24870761 :     for (i=lim+1; i<lg(C); i++)
     208    19053531 :       gel(Ca,i) = zero;
     209             :   }
     210     7586795 :   return Ca;
     211             : }
     212             : 
     213             : static void
     214             : /* C1 <- C1 + C2 */
     215             : /* assume C2[i]==0 for i>lim */
     216     5098837 : gen_addcol(GEN C1, GEN C2, long lim, void* data, const struct bb_hermite *R)
     217             : {
     218             :   long i;
     219    18527468 :   for (i=1; i<=lim; i++)
     220    13428630 :     gel(C1,i) = R->add(data, gel(C1,i), gel(C2,i));
     221     5098838 : }
     222             : 
     223             : static void
     224             : /* H[,i] <- H[,i] + C*a */
     225             : /* assume C is zero after lim */
     226     3825390 : gen_addrightmul(GEN H, GEN C, GEN a, long i, long lim, void* data, const struct bb_hermite *R)
     227             : {
     228             :   GEN Ca;
     229     3825390 :   if (R->equal0(a)) return;
     230     1393736 :   Ca = gen_rightmulcol(C, a, lim, 0, data, R);
     231     1393741 :   gen_addcol(gel(H,i), Ca, lim, data, R);
     232             : }
     233             : 
     234             : static GEN
     235     2940471 : gen_zerocol(long n, void* data, const struct bb_hermite *R)
     236             : {
     237     2940471 :   GEN C = cgetg(n+1,t_COL), zero = R->s(data, 0);
     238             :   long i;
     239    13973395 :   for (i=1; i<=n; i++) gel(C,i) = zero;
     240     2940444 :   return C;
     241             : }
     242             : 
     243             : static GEN
     244     1556438 : gen_zeromat(long m, long n, void* data, const struct bb_hermite *R)
     245             : {
     246     1556438 :   GEN M = cgetg(n+1,t_MAT);
     247             :   long i;
     248     4353353 :   for (i=1; i<=n; i++) gel(M,i) = gen_zerocol(m, data, R);
     249     1556369 :   return M;
     250             : }
     251             : 
     252             : static GEN
     253      365253 : gen_colei(long n, long i, void* data, const struct bb_hermite *R)
     254             : {
     255      365253 :   GEN C = cgetg(n+1,t_COL), zero = R->s(data, 0);
     256             :   long j;
     257     2494233 :   for (j=1; j<=n; j++)
     258     2128980 :     if (i!=j)   gel(C,j) = zero;
     259      365253 :     else        gel(C,j) = R->s(data,1);
     260      365253 :   return C;
     261             : }
     262             : 
     263             : static GEN
     264       56084 : gen_matid_hermite(long n, void* data, const struct bb_hermite *R)
     265             : {
     266       56084 :   GEN M = cgetg(n+1,t_MAT);
     267             :   long i;
     268      196196 :   for (i=1; i<=n; i++) gel(M,i) = gen_colei(n, i, data, R);
     269       56084 :   return M;
     270             : }
     271             : 
     272             : static GEN
     273     2570460 : gen_matmul_hermite(GEN A, GEN B, void* data, const struct bb_hermite *R)
     274             : {
     275     2570460 :   GEN M,sum,prod,zero = R->s(data,0);
     276             :   long a,b,c,c2,i,j,k;
     277     2570459 :   RgM_dimensions(A,&a,&c);
     278     2570461 :   RgM_dimensions(B,&c2,&b);
     279     2570462 :   if (c!=c2) pari_err_DIM("gen_matmul_hermite");
     280     2570462 :   M = cgetg(b+1,t_MAT);
     281     5474920 :   for (j=1; j<=b; j++)
     282             :   {
     283     2904525 :     gel(M,j) = cgetg(a+1,t_COL);
     284     8169170 :     for (i=1; i<=a; i++)
     285             :     {
     286     5264704 :       sum = zero;
     287    16849874 :       for (k=1; k<=c; k++)
     288             :       {
     289    11585224 :         prod = R->mul(data, gcoeff(A,i,k), gcoeff(B,k,j));
     290    11585001 :         sum = R->add(data, sum, prod);
     291             :       }
     292     5264650 :       gcoeff(M,i,j) = sum;
     293             :     }
     294     2904466 :     gen_redcol(gel(M,j), a, data, R);
     295             :   }
     296     2570395 :   return M;
     297             : }
     298             : 
     299             : static void
     300             : /* U = [u1,u2]~, C <- A*u1 + B*u2 */
     301             : /* assume both A, B and C are zero after lim */
     302     6583720 : gen_rightlincomb(GEN A, GEN B, GEN U, GEN *C, long lim, void* data, const struct bb_hermite *R)
     303             : {
     304             :   GEN Au1, Bu2;
     305     6583720 :   Au1 = gen_rightmulcol(A, gel(U,1), lim, 1, data, R);
     306     6583737 :   Bu2 = gen_rightmulcol(B, gel(U,2), lim, 1, data, R);
     307     6583727 :   if (!Au1 && !Bu2) { *C = gen_zerocol(lg(A)-1, data, R); return; }
     308     6583727 :   if (!Au1) { *C = Bu2; return; }
     309     4080023 :   if (!Bu2) { *C = Au1; return; }
     310     3704972 :   gen_addcol(Au1, Bu2, lim, data, R);
     311     3704973 :   *C = Au1;
     312             : }
     313             : 
     314             : static void
     315             : /* (H[,i] | H[,j]) <- (H[,i] | H[,j]) * U */
     316             : /* assume both columns are zero after lim */
     317     3291868 : gen_elem(GEN H, GEN U, long i, long j, long lim, void* data, const struct bb_hermite *R)
     318             : {
     319             :   GEN Hi, Hj;
     320     3291868 :   Hi = shallowcopy(gel(H,i));
     321     3291868 :   Hj = shallowcopy(gel(H,j));
     322     3291868 :   gen_rightlincomb(Hi, Hj, gel(U,1), &gel(H,i), lim, data, R);
     323     3291858 :   gen_rightlincomb(Hi, Hj, gel(U,2), &gel(H,j), lim, data, R);
     324     3291868 : }
     325             : 
     326             : static int
     327             : /* assume C is zero after lim */
     328     4094595 : gen_is_zerocol(GEN C, long lim, void* data, const struct bb_hermite *R)
     329             : {
     330             :   long i;
     331             :   (void) data;
     332     7357126 :   for (i=1; i<=lim; i++)
     333     5752990 :     if (!R->equal0(gel(C,i))) return 0;
     334     1604136 :   return 1;
     335             : }
     336             : 
     337             : /* The mkop* functions return NULL if the corresponding operation is the identity */
     338             : 
     339             : static GEN
     340             : /* Ci <- Ci + Cj*a */
     341     2767302 : mkoptransv(long i, long j, GEN a, void* data, const struct bb_hermite *R)
     342             : {
     343     2767302 :   a = R->red(data,a);
     344     2767287 :   if (R->equal0(a)) return NULL;
     345     1077801 :   return mkvec2(mkvecsmall2(i,j),a);
     346             : }
     347             : 
     348             : static GEN
     349             : /* (Ci|Cj) <- (Ci|Cj)*U */
     350      924587 : mkopU(long i, long j, GEN U, void* data, const struct bb_hermite *R)
     351             : {
     352      924587 :   if (R->equal1(gcoeff(U,1,1)) && R->equal0(gcoeff(U,1,2))
     353      403263 :       && R->equal1(gcoeff(U,2,2))) return mkoptransv(i,j,gcoeff(U,2,1),data,R);
     354      521324 :   return mkvec2(mkvecsmall3(i,j,0),U);
     355             : }
     356             : 
     357             : static GEN
     358             : /* Ci <- Ci*u */
     359     1875163 : mkopmul(long i, GEN u, const struct bb_hermite *R)
     360             : {
     361     1875163 :   if (R->equal1(u)) return NULL;
     362      191476 :   return mkvec2(mkvecsmall(i),u);
     363             : }
     364             : 
     365             : static GEN
     366             : /* Ci <-> Cj */
     367       45843 : mkopswap(long i, long j)
     368             : {
     369       45843 :   return mkvec(mkvecsmall2(i,j));
     370             : }
     371             : 
     372             : /* M: t_MAT. Apply the operation op to M by right multiplication. */
     373             : static void
     374      374465 : gen_rightapply(GEN M, GEN op, void* data, const struct bb_hermite *R)
     375             : {
     376             :   GEN M2, ind, X;
     377      374465 :   long i, j, m = lg(gel(M,1))-1;
     378      374465 :   switch (typ(op))
     379             :   {
     380       56014 :     case t_VECSMALL:
     381       56014 :       M2 = vecpermute(M,op);
     382      266056 :       for (i=1; i<lg(M); i++) gel(M,i) = gel(M2,i);
     383       56014 :       return;
     384      318451 :     case t_VEC:
     385      318451 :       ind = gel(op,1);
     386      318451 :       switch (lg(op))
     387             :       {
     388       16135 :         case 2:
     389       16135 :           swap(gel(M,ind[1]),gel(M,ind[2]));
     390       16135 :           return;
     391      302316 :         case 3:
     392      302316 :           X = gel(op,2);
     393      302316 :           i = ind[1];
     394      302316 :           switch (lg(ind))
     395             :           {
     396       37786 :             case 2:
     397       37786 :               gel(M,i) = gen_rightmulcol(gel(M,i), X, m, 0, data, R);
     398       37786 :               gen_redcol(gel(M,i), m, data, R);
     399       37786 :               return;
     400      175693 :             case 3:
     401      175693 :               gen_addrightmul(M, gel(M,ind[2]), X, i, m, data, R);
     402      175693 :               gen_redcol(gel(M,i), m, data, R);
     403      175693 :               return;
     404       88837 :             case 4:
     405       88837 :               j = ind[2];
     406       88837 :               gen_elem(M, X, i, j, m, data, R);
     407       88837 :               gen_redcol(gel(M,i), m, data, R);
     408       88837 :               gen_redcol(gel(M,j), m, data, R);
     409       88837 :               return;
     410             :           }
     411             :       }
     412             :   }
     413             : }
     414             : 
     415             : /* C: t_COL. Apply the operation op to C by left multiplication. */
     416             : static void
     417    11421740 : gen_leftapply(GEN C, GEN op, void* data, const struct bb_hermite *R)
     418             : {
     419             :   GEN C2, ind, X;
     420             :   long i, j;
     421    11421740 :   switch (typ(op))
     422             :   {
     423      578788 :     case t_VECSMALL:
     424      578788 :       C2 = vecpermute(C,perm_inv(op));
     425     4729627 :       for (i=1; i<lg(C); i++) gel(C,i) = gel(C2,i);
     426      578788 :       return;
     427    10842951 :     case t_VEC:
     428    10842951 :       ind = gel(op,1);
     429    10842951 :       switch (lg(op))
     430             :       {
     431      169134 :         case 2:
     432      169134 :           swap(gel(C,ind[1]),gel(C,ind[2]));
     433      169134 :           return;
     434    10673817 :         case 3:
     435    10673817 :           X = gel(op,2);
     436    10673817 :           i = ind[1];
     437    10673817 :           switch (lg(ind))
     438             :           {
     439      700717 :             case 2:
     440      700717 :               gel(C,i) = R->mul(data, X, gel(C,i));
     441      700713 :               gel(C,i) = R->red(data, gel(C,i));
     442      700709 :               return;
     443     7612878 :             case 3:
     444     7612878 :               j = ind[2];
     445     7612878 :               if (R->equal0(gel(C,i))) return;
     446     1087427 :               gel(C,j) = R->add(data, gel(C,j), R->mul(data, X, gel(C,i)));
     447     1087428 :               return;
     448     2360258 :             case 4:
     449     2360258 :               j = ind[2];
     450     2360258 :               C2 = gen_matmul_hermite(X, mkmat(mkcol2(gel(C,i),gel(C,j))), data, R);
     451     2360199 :               gel(C,i) = gcoeff(C2,1,1);
     452     2360199 :               gel(C,j) = gcoeff(C2,2,1);
     453     2360199 :               return;
     454             :           }
     455             :       }
     456             :   }
     457             : }
     458             : 
     459             : /* \prod_i det ops[i]. Only makes sense if R is commutative. */
     460             : static GEN
     461          42 : gen_detops(GEN ops, void* data, const struct bb_hermite *R)
     462             : {
     463          42 :   GEN d = R->s(data,1);
     464          42 :   long i, l = lg(ops);
     465         231 :   for (i = 1; i < l; i++)
     466             :   {
     467         189 :     GEN X, op = gel(ops,i);
     468         189 :     switch (typ(op))
     469             :     {
     470           0 :       case t_VECSMALL:
     471           0 :         if (perm_sign(op) < 0) d = R->neg(data,d);
     472           0 :         break;
     473         189 :       case t_VEC:
     474         189 :         switch (lg(op))
     475             :         {
     476           0 :           case 2:
     477           0 :             d = R->neg(data,d);
     478           0 :             break;
     479         189 :           case 3:
     480         189 :             X = gel(op,2);
     481         189 :             switch (lg(gel(op,1)))
     482             :             {
     483           0 :               case 2:
     484           0 :                  d = R->mul(data, d, X);
     485           0 :                  d = R->red(data, d);
     486           0 :                  break;
     487         105 :               case 4:
     488             :                {
     489         105 :                  GEN A = gcoeff(X,1,1), B = gcoeff(X,1,2);
     490         105 :                  GEN C = gcoeff(X,2,1), D = gcoeff(X,2,2);
     491         105 :                  GEN AD = R->mul(data,A,D);
     492         105 :                  GEN BC = R->mul(data,B,C);
     493         105 :                  d = R->mul(data, d, R->add(data, AD, R->neg(data,BC)));
     494         105 :                  d = R->red(data, d);
     495         105 :                  break;
     496             :                }
     497             :             }
     498         189 :             break;
     499             :         }
     500         189 :         break;
     501             :     }
     502             :   }
     503          42 :   return d;
     504             : }
     505             : 
     506             : static int
     507      211589 : gen_is_inv(GEN x, void* data, const struct bb_hermite *R)
     508             : {
     509      211589 :   GEN u = R->unit(data, x);
     510      211589 :   if (!u) return R->equal1(x);
     511       72219 :   return R->equal1(gel(u,1));
     512             : }
     513             : 
     514             : static long
     515      195510 : gen_last_inv_diago(GEN A, void* data, const struct bb_hermite *R)
     516             : {
     517             :   long i,m,n,j;
     518      195510 :   RgM_dimensions(A,&m,&n);
     519      226009 :   for (i=1,j=n-m+1; i<=m; i++,j++)
     520      211589 :     if (!gen_is_inv(gcoeff(A,i,j),data,R)) return i-1;
     521       14420 :   return m;
     522             : }
     523             : 
     524             : static GEN
     525             : /* remove_zerocols: 0 none, 1 until square, 2 all */
     526             : /* early abort: if not right-invertible, abort, return NULL, and set ops to the
     527             :  * noninvertible pivot */
     528      942715 : 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)
     529             : {
     530      942715 :   pari_sp av = avma, av1;
     531      942715 :   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);
     532      942716 :   long m,n,i,j,s,si,i2,si2,nbz,lim,extra,maxop=0,nbop=0,lastinv=0;
     533             :   int smallop;
     534             : 
     535      942716 :   av1 = avma;
     536             : 
     537      942716 :   RgM_dimensions(A,&m,&n);
     538      942717 :   if (early_abort && n<m)
     539             :   {
     540       14000 :     if (ops) *ops = zero;
     541       14000 :     return NULL;
     542             :   }
     543      928717 :   if (n<m+1)
     544             :   {
     545      828358 :     extra = m+1-n;
     546      828358 :     H = shallowmatconcat(mkvec2(gen_zeromat(m,extra,data,R),A));
     547             :   }
     548             :   else
     549             :   {
     550      100359 :     extra = 0;
     551      100359 :     H = RgM_shallowcopy(A);
     552             :   }
     553      928720 :   RgM_dimensions(H,&m,&n);
     554      928719 :   s = n-m; /* shift */
     555             : 
     556      928719 :   if(ops)
     557             :   {
     558      733207 :     maxop = m*n + (m*(m+1))/2 + 1;
     559      733207 :     *ops = zerovec(maxop); /* filled with placeholders so gerepile can handle it */
     560             :   }
     561             : 
     562             :   /* put in triangular form */
     563     3572880 :   for (i=m,si=s+m; i>0 && si>extra; i--,si--) /* si = s+i */
     564             :   {
     565     2649184 :     if (R->red) gcoeff(H,i,si) = R->red(data, gcoeff(H,i,si));
     566             :     /* bottom-right diagonal */
     567     9249530 :     for (j = extra+1; j < si; j++)
     568             :     {
     569     6600721 :       if (R->red) gcoeff(H,i,j) = R->red(data, gcoeff(H,i,j));
     570     6600669 :       if (R->equal0(gcoeff(H,i,j))) continue;
     571     3055331 :       U = R->extgcd(data, gcoeff(H,i,j), gcoeff(H,i,si), &smallop);
     572     3055401 :       d = gel(U,1);
     573     3055401 :       U = gel(U,2);
     574     3055401 :       if (n>10)
     575             :       {
     576             :         /* normalize diagonal coefficient -> faster reductions on this row */
     577     1831409 :         u = R->unit(data, d);
     578     1831409 :         if (u)
     579             :         {
     580     1831409 :           g = gel(u,1);
     581     1831409 :           u = gel(u,2);
     582     1831409 :           gcoeff(U,1,2) = R->mul(data, gcoeff(U,1,2), u);
     583     1831409 :           gcoeff(U,2,2) = R->mul(data, gcoeff(U,2,2), u);
     584     1831409 :           d = g;
     585             :         }
     586             :       }
     587     3055401 :       gen_elem(H, U, j, si, i-1, data, R);
     588     3055401 :       if (ops)
     589             :       {
     590      888544 :         op =  mkopU(j,si,U,data,R);
     591      888544 :         if (op) { nbop++; gel(*ops, nbop) = op; }
     592             :       }
     593     3055401 :       gcoeff(H,i,j) = zero;
     594     3055401 :       gcoeff(H,i,si) = d;
     595     3055401 :       if (R->red && !smallop)
     596             :       {
     597     2463739 :         gen_redcol(gel(H,si), i-1, data, R);
     598     2463737 :         gen_redcol(gel(H,j), i-1, data, R);
     599             :       }
     600             :     }
     601             : 
     602     2648809 :     if (early_abort)
     603             :     {
     604     1455376 :       d = gcoeff(H,i,si);
     605     1455376 :       u = R->unit(data, d);
     606     1455593 :       if (u) d = gel(u,1);
     607     1455593 :       if (!R->equal1(d))
     608             :       {
     609        4914 :         if (ops) *ops = d;
     610        4914 :         return NULL;
     611             :       }
     612             :     }
     613             : 
     614     2644112 :     if (gc_needed(av,1))
     615             :     {
     616          14 :       if (DEBUGMEM>1) pari_warn(warnmem,"gen_howell[1]. i=%ld",i);
     617          14 :       gerepileall(av1,ops?2:1,&H,ops);
     618             :     }
     619             :   }
     620             : 
     621      923696 :   if (!ops)
     622      195510 :     lastinv = gen_last_inv_diago(H, data, R);
     623             : 
     624             :   /* put in reduced Howell form */
     625      923696 :   if (!only_triangular)
     626             :   {
     627     3705569 :     for (i=m,si=s+m; i>0; i--,si--) /* si = s+i */
     628             :     {
     629             :       /* normalize diagonal coefficient */
     630     2781865 :       if (i<=lastinv) /* lastinv>0 => !ops */
     631       30499 :         gcoeff(H,i,si) = one;
     632             :       else
     633             :       {
     634     2751366 :         u = R->unit(data,gcoeff(H,i,si));
     635     2751571 :         if (u)
     636             :         {
     637     2459618 :           g = gel(u,1);
     638     2459618 :           u = gel(u,2);
     639     2459618 :           gel(H,si) = gen_rightmulcol(gel(H,si), u, i-1, 1, data, R);
     640     2459627 :           gcoeff(H,i,si) = g;
     641     2459627 :           if (R->red) gen_redcol(gel(H,si), i-1, data, R);
     642     2459632 :           if (ops)
     643             :           {
     644     1875161 :             op = mkopmul(si,u,R);
     645     1875168 :             if (op) { nbop++; gel(*ops,nbop) = op; }
     646             :           }
     647             :         }
     648      291953 :         else if (R->red) gcoeff(H,i,si) = R->red(data, gcoeff(H,i,si));
     649             :       }
     650     2782091 :       piv = gcoeff(H,i,si);
     651             : 
     652             :       /* reduce above diagonal */
     653     2782091 :       if (!R->equal0(piv))
     654             :       {
     655     2490136 :         C = gel(H,si);
     656     6166799 :         for (j=si+1; j<=n; j++)
     657             :         {
     658     3676662 :           if (i<=lastinv) /* lastinv>0 => !ops */
     659       26978 :             gcoeff(H,i,j) = zero;
     660             :           else
     661             :           {
     662     3649684 :             gcoeff(H,i,j) = R->red(data, gcoeff(H,i,j));
     663     3649682 :             if (R->equal1(piv)) { q = gcoeff(H,i,j); r = zero; }
     664     1552572 :             else                q = R->lquo(data, gcoeff(H,i,j), piv, &r);
     665     3649673 :             q = R->neg(data,q);
     666     3649689 :             gen_addrightmul(H, C, q, j, i-1, data, R);
     667     3649696 :             if (ops)
     668             :             {
     669     2215557 :               op = mkoptransv(j,si,q,data,R);
     670     2215546 :               if (op) { nbop++; gel(*ops,nbop) = op; }
     671             :             }
     672     3649685 :             gcoeff(H,i,j) = r;
     673             :           }
     674             :         }
     675             :       }
     676             : 
     677             :       /* ensure Howell property */
     678     2782085 :       if (i>1)
     679             :       {
     680     1858541 :         a = R->rann(data, piv);
     681     1858271 :         if (!R->equal0(a))
     682             :         {
     683      544390 :           gel(H,1) = gen_rightmulcol(gel(H,si), a, i-1, 1, data, R);
     684      544390 :           if (gel(H,1) == gel(H,si)) gel(H,1) = shallowcopy(gel(H,1)); /* in case rightmulcol cheated */
     685      544390 :           if (ops)
     686             :           {
     687      148484 :             op = mkoptransv(1,si,a,data,R);
     688      148484 :             if (op) { nbop++; gel(*ops,nbop) = op; }
     689             :           }
     690     2174746 :           for (i2=i-1,si2=s+i2; i2>0; i2--,si2--)
     691             :           {
     692     1630356 :             if (R->red) gcoeff(H,i2,1) = R->red(data, gcoeff(H,i2,1));
     693     1630356 :             if (R->equal0(gcoeff(H,i2,1))) continue;
     694      278054 :             if (R->red) gcoeff(H,i2,si2) = R->red(data, gcoeff(H,i2,si2));
     695      278054 :             if (R->equal0(gcoeff(H,i2,si2)))
     696             :             {
     697      130424 :               swap(gel(H,1), gel(H,si2));
     698      130424 :               if (ops) { nbop++; gel(*ops,nbop) = mkopswap(1,si2); }
     699      130424 :               continue;
     700             :             }
     701      147630 :             U = R->extgcd(data, gcoeff(H,i2,1), gcoeff(H,i2,si2), &smallop);
     702      147630 :             d = gel(U,1);
     703      147630 :             U = gel(U,2);
     704      147630 :             gen_elem(H, U, 1, si2, i2-1, data, R);
     705      147630 :             if (ops)
     706             :             {
     707       36043 :               op = mkopU(1,si2,U,data,R);
     708       36043 :               if (op) { nbop++; gel(*ops,nbop) = op; }
     709             :             }
     710      147630 :             gcoeff(H,i2,1) = zero;
     711      147630 :             gcoeff(H,i2,si2) = d;
     712      147630 :             if (R->red && !smallop)
     713             :             {
     714      137466 :               gen_redcol(gel(H,si2), i2, data, R);
     715      137466 :               gen_redcol(gel(H,1), i2-1, data, R);
     716             :             }
     717             :           }
     718             :         }
     719             :       }
     720             : 
     721     2781878 :       if (gc_needed(av,1))
     722             :       {
     723          12 :         if (DEBUGMEM>1) pari_warn(warnmem,"gen_howell[2]. i=%ld",i);
     724          12 :         gerepileall(av1,ops?3:2,&H,&piv,ops);
     725             :       }
     726             :     }
     727             :   }
     728             : 
     729      923671 :   if (R->red)
     730     4974010 :     for (j=1; j<=n; j++)
     731             :     {
     732     4050415 :       lim = maxss(0,m-n+j);
     733     4050396 :       gen_redcol(gel(H,j), lim, data, R);
     734    13510459 :       for (i=lim+1; i<=m; i++) gcoeff(H,i,j) = zero;
     735             :     }
     736             : 
     737             :   /* put zero columns first */
     738      923478 :   iszero = cgetg(n+1,t_VECSMALL);
     739             : 
     740      923745 :   nbz = 0;
     741     4974384 :   for (i=1; i<=n; i++)
     742             :   {
     743     4050580 :     iszero[i] = gen_is_zerocol(gel(H,i), maxss(0,m-n+i), data, R);
     744     4050639 :     if (iszero[i]) nbz++;
     745             :   }
     746             : 
     747      923804 :   j = 1;
     748      923804 :   if (permute_zerocols)
     749             :   {
     750      155729 :     perm = cgetg(n+1, t_VECSMALL);
     751      900963 :     for (i=1; i<=n; i++)
     752      745234 :       if (iszero[i])
     753             :       {
     754      314321 :         perm[j] = i;
     755      314321 :         j++;
     756             :       }
     757             :   }
     758      768075 :   else perm = cgetg(n-nbz+1, t_VECSMALL);
     759     4974556 :   for (i=1; i<=n; i++)
     760     4050761 :     if (!iszero[i])
     761             :     {
     762     2490456 :       perm[j] = i;
     763     2490456 :       j++;
     764             :     }
     765             : 
     766      923795 :   if (permute_zerocols || remove_zerocols==2) H = vecpermute(H, perm);
     767      923803 :   if (permute_zerocols && remove_zerocols==2) H = vecslice(H, nbz+1, n);
     768      923803 :   if (remove_zerocols==1) H = vecslice(H, s+1, n);
     769      923803 :   if (permute_zerocols && ops) { nbop++; gel(*ops,nbop) = perm; }
     770             : 
     771      923803 :   if (ops) { setlg(*ops, nbop+1); } /* should have nbop <= maxop */
     772             : 
     773      923797 :   return H;
     774             : }
     775             : 
     776             : static GEN
     777       95963 : 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)
     778             : {
     779       95963 :   pari_sp av = avma;
     780       95963 :   GEN H = gen_howell_i(A, remove_zerocols, permute_zerocols, early_abort, only_triangular, ops, data, R);
     781       95963 :   return gc_all(av, ops?2:1, &H, ops);
     782             : }
     783             : 
     784             : static GEN
     785      151977 : gen_matimage(GEN A, GEN* U, void *data, const struct bb_hermite *R)
     786             : {
     787             :   GEN ops, H;
     788      151977 :   if (U)
     789             :   {
     790       56014 :     pari_sp av = avma, av1;
     791             :     long m, n, i, r, n2, pergc;
     792       56014 :     RgM_dimensions(A,&m,&n);
     793       56014 :     H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
     794       56014 :     av1 = avma;
     795       56014 :     r = lg(H)-1;
     796       56014 :     *U = shallowmatconcat(mkvec2(gen_zeromat(n, maxss(0,m-n+1), data, R), gen_matid_hermite(n, data, R)));
     797       56014 :     n2 = lg(*U)-1;
     798       56014 :     pergc = maxss(m,n);
     799      430479 :     for (i=1; i<lg(ops); i++)
     800             :     {
     801      374465 :       gen_rightapply(*U, gel(ops,i), data, R);
     802      374465 :       if (!(i%pergc) && gc_needed(av,1))
     803             :       {
     804           0 :         if (DEBUGMEM>1) pari_warn(warnmem,"gen_matimage. i=%ld",i);
     805           0 :         gerepileall(av1,1,U);
     806             :       }
     807             :     }
     808       56014 :     if (r<n2) *U = vecslice(*U, n2-r+1, n2);
     809       56014 :     return gc_all(av, 2, &H, U);
     810             :   }
     811       95963 :   return gen_howell(A, 2, 0, 0, 0, NULL, data, R);
     812             : }
     813             : 
     814             : static GEN
     815             : /* H in true Howell form: no zero columns */
     816       99547 : gen_kernel_howell(GEN H, void *data, const struct bb_hermite *R)
     817             : {
     818             :   GEN K, piv, FK;
     819             :   long m, n, j, j2, i;
     820       99547 :   RgM_dimensions(H,&m,&n);
     821       99547 :   K = gen_zeromat(n, n, data, R);
     822      409290 :   for (j=n,i=m; j>0; j--)
     823             :   {
     824      523859 :     while (R->equal0(gcoeff(H,i,j))) i--;
     825      309743 :     piv = gcoeff(H,i,j);
     826      309743 :     if (R->equal0(piv)) continue;
     827      309743 :     gcoeff(K,j,j) = R->rann(data, piv);
     828      309743 :     if (j<n)
     829             :     {
     830      210196 :       FK = gen_matmul_hermite(matslice(H,i,i,j+1,n), matslice(K, j+1, n, j+1, n), data, R);
     831      754453 :       for (j2=j+1; j2<=n; j2++)
     832      544257 :         gcoeff(K,j,j2) = R->neg(data, R->lquo(data, gcoeff(FK,1,j2-j), piv, NULL));
     833             :         /* remainder has to be zero */
     834             :     }
     835             :   }
     836       99547 :   return K;
     837             : }
     838             : 
     839             : static GEN
     840             : /* (H,ops) Howell form of A, n = number of columns of A, return a kernel of A */
     841       99617 : gen_kernel_from_howell(GEN H, GEN ops, long n, void *data, const struct bb_hermite *R)
     842             : {
     843             :   pari_sp av;
     844             :   GEN K, KH, zC;
     845             :   long m, r, n2, nbz, i, o, extra, j;
     846       99617 :   RgM_dimensions(H,&m,&r);
     847       99617 :   if (!r) return gen_matid_hermite(n, data, R); /* zerology: what if 0==1 in R? */
     848       99547 :   n2 = maxss(n,m+1);
     849       99547 :   extra = n2-n;
     850       99547 :   nbz = n2-r;
     851             :   /* compute kernel of augmented matrix */
     852       99547 :   KH = gen_kernel_howell(H, data, R);
     853       99547 :   zC = gen_zerocol(nbz, data, R);
     854       99547 :   K = cgetg(nbz+r+1, t_MAT);
     855      324688 :   for (i=1; i<=nbz; i++)
     856      225141 :     gel(K,i) = gen_colei(nbz+r, i, data, R);
     857      409290 :   for (i=1; i<=r; i++)
     858      309743 :     gel(K,nbz+i) = shallowconcat(zC, gel(KH,i));
     859      634431 :   for (i=1; i<lg(K); i++)
     860             :   {
     861      534884 :     av = avma;
     862     9913666 :     for (o=lg(ops)-1; o>0; o--)
     863     9378782 :       gen_leftapply(gel(K,i), gel(ops,o), data, R);
     864      534884 :     gen_redcol(gel(K,i), nbz+r, data, R);
     865      534884 :     gerepileall(av, 1, &gel(K,i));
     866             :   }
     867             :   /* deduce kernel of original matrix */
     868       99547 :   K = rowpermute(K, cyclic_perm(n2,extra));
     869       99547 :   K = gen_howell_i(K, 2, 0, 0, 0, NULL, data, R);
     870      226338 :   for (j=lg(K)-1, i=n2; j>0; j--)
     871             :   {
     872      300734 :     while (R->equal0(gcoeff(K,i,j))) i--;
     873      197694 :     if (i<=n) return matslice(K, 1, n, 1, j);
     874             :   }
     875       28644 :   return cgetg(1,t_MAT);
     876             : }
     877             : 
     878             : /* not GC-clean */
     879             : static GEN
     880       55762 : gen_kernel(GEN A, GEN* im, void *data, const struct bb_hermite *R)
     881             : {
     882       55762 :   pari_sp av = avma;
     883       55762 :   long n = lg(A)-1;
     884             :   GEN H, ops, K;
     885       55762 :   H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
     886       55762 :   gerepileall(av,2,&H,&ops);
     887       55762 :   K = gen_kernel_from_howell(H, ops, n, data, R);
     888       55762 :   if (im) *im = H;
     889       55762 :   return K;
     890             : }
     891             : 
     892             : /* right inverse, not GC-clean */
     893             : static GEN
     894      591434 : gen_inv(GEN A, void* data, const struct bb_hermite *R)
     895             : {
     896             :   pari_sp av;
     897      591434 :   GEN ops, H, U, un=R->s(data,1);
     898             :   long m,n,j,o,n2;
     899      591434 :   RgM_dimensions(A,&m,&n);
     900      591434 :   av = avma;
     901      591434 :   H = gen_howell_i(A, 0, 0, 1, 0, &ops, data, R);
     902      591430 :   if (!H) pari_err_INV("gen_inv", ops);
     903      572516 :   n2 = lg(H)-1;
     904      572516 :   ops = gerepilecopy(av,ops); /* get rid of H */
     905      572536 :   U = gen_zeromat(n2, m, data, R);
     906     2016936 :   for (j=1; j<=m; j++)
     907     1444419 :     gcoeff(U,j+n2-m,j) = un;
     908     2016968 :   for (j=1; j<=m; j++)
     909             :   {
     910     1444427 :     av = avma;
     911     3089067 :     for (o=lg(ops)-1; o>0; o--)
     912     1644748 :       gen_leftapply(gel(U,j), gel(ops,o), data, R);
     913     1444319 :     gen_redcol(gel(U,j), n2, data, R);
     914     1444035 :     gerepileall(av, 1, &gel(U,j));
     915             :   }
     916      572541 :   if (n2>n) U = rowslice(U, n2-n+1, n2);
     917      572522 :   return U;
     918             : }
     919             : 
     920             : /*
     921             :   H true Howell form (no zero columns).
     922             :   Compute Z = Y - HX canonical representative of R^m mod H(R^n)
     923             : */
     924             : static GEN
     925       43953 : gen_reduce_mod_howell(GEN H, GEN Y, GEN *X, void* data, const struct bb_hermite *R)
     926             : {
     927       43953 :   pari_sp av = avma;
     928             :   long m,n,i,j;
     929             :   GEN Z, q, r, C;
     930       43953 :   RgM_dimensions(H,&m,&n);
     931       43953 :   if (X) *X = gen_zerocol(n,data,R);
     932       43953 :   Z = shallowcopy(Y);
     933       43953 :   i = m;
     934      155099 :   for (j=n; j>0; j--)
     935             :   {
     936      180348 :     while (R->equal0(gcoeff(H,i,j))) i--;
     937      111146 :     q = R->lquo(data, gel(Z,i), gcoeff(H,i,j), &r);
     938      111146 :     gel(Z,i) = r;
     939      111146 :     C = gen_rightmulcol(gel(H,j), R->neg(data,q), i, 0, data, R);
     940      111146 :     if (C) gen_addcol(Z, C, i-1, data, R);
     941      111146 :     if (X) gel(*X,j) = q;
     942             :   }
     943       43953 :   gen_redcol(Z, lg(Z)-1, data, R);
     944       43953 :   return gc_all(av, X?2:1, &Z, X);
     945             : }
     946             : 
     947             : /* H: Howell form of A */
     948             : /* (m,n): dimensions of original matrix A */
     949             : /* return NULL if no solution */
     950             : static GEN
     951       43953 : gen_solve_from_howell(GEN H, GEN ops, long m, long n, GEN Y, void* data, const struct bb_hermite *R)
     952             : {
     953       43953 :   pari_sp av = avma;
     954             :   GEN Z, X;
     955             :   long n2, mH, nH, i;
     956       43953 :   RgM_dimensions(H,&mH,&nH);
     957       43953 :   n2 = maxss(n,m+1);
     958       43953 :   Z = gen_reduce_mod_howell(H, Y, &X, data, R);
     959       43953 :   if (!gen_is_zerocol(Z,m,data,R)) { set_avma(av); return NULL; }
     960             : 
     961       43904 :   X = shallowconcat(zerocol(n2-nH),X);
     962      442113 :   for (i=lg(ops)-1; i>0; i--) gen_leftapply(X, gel(ops,i), data, R);
     963       43904 :   X = vecslice(X, n2-n+1, n2);
     964       43904 :   gen_redcol(X, n, data, R);
     965       43904 :   return gerepilecopy(av, X);
     966             : }
     967             : 
     968             : /* return NULL if no solution, not GC-clean */
     969             : static GEN
     970       43953 : gen_solve(GEN A, GEN Y, GEN* K, void* data, const struct bb_hermite *R)
     971             : {
     972             :   GEN H, ops, X;
     973             :   long m,n;
     974             : 
     975       43953 :   RgM_dimensions(A,&m,&n);
     976       43953 :   if (!n) m = lg(Y)-1;
     977       43953 :   H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
     978       43953 :   X = gen_solve_from_howell(H, ops, m, n, Y, data, R);
     979       43953 :   if (!X) return NULL;
     980       43904 :   if (K) *K = gen_kernel_from_howell(H, ops, n, data, R);
     981       43904 :   return X;
     982             : }
     983             : 
     984             : GEN
     985      152012 : matimagemod(GEN A, GEN d, GEN* U)
     986             : {
     987             :   void *data;
     988             :   const struct bb_hermite* R;
     989      152012 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matimagemod", A);
     990      151998 :   if (typ(d)!=t_INT) pari_err_TYPE("matimagemod", d);
     991      151991 :   if (signe(d)<=0) pari_err_DOMAIN("matimagemod", "d", "<=", gen_0, d);
     992      151984 :   if (equali1(d)) return cgetg(1,t_MAT);
     993      151977 :   R = get_Fp_hermite(&data, d);
     994      151977 :   return gen_matimage(A, U, data, R);
     995             : }
     996             : 
     997             : /* for testing purpose */
     998             : /*
     999             : GEN
    1000             : ZM_hnfmodid2(GEN A, GEN d)
    1001             : {
    1002             :   pari_sp av = avma;
    1003             :   void *data;
    1004             :   long i;
    1005             :   const struct bb_hermite* R = get_Fp_hermite(&data, d);
    1006             :   GEN H;
    1007             :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("ZM_hnfmodid2", A);
    1008             :   if (typ(d)!=t_INT) pari_err_TYPE("ZM_hnfmodid2", d);
    1009             :   H = gen_howell_i(A, 1, 0, 0, 0, NULL, data, R);
    1010             :   for (i=1; i<lg(H); i++)
    1011             :     if (!signe(gcoeff(H,i,i))) gcoeff(H,i,i) = d;
    1012             :   return gerepilecopy(av,H);
    1013             : }
    1014             : */
    1015             : 
    1016             : GEN
    1017          84 : matdetmod(GEN A, GEN d)
    1018             : {
    1019          84 :   pari_sp av = avma;
    1020             :   void *data;
    1021             :   const struct bb_hermite* R;
    1022          84 :   long n = lg(A)-1, i;
    1023             :   GEN D, H, ops;
    1024          84 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matdetmod", A);
    1025          70 :   if (typ(d)!=t_INT) pari_err_TYPE("matdetmod", d);
    1026          70 :   if (signe(d)<=0) pari_err_DOMAIN("matdetmod", "d", "<=", gen_0, d);
    1027          63 :   if (!n) return equali1(d) ? gen_0 : gen_1;
    1028          56 :   if (n != nbrows(A)) pari_err_DIM("matdetmod");
    1029          49 :   if (equali1(d)) return gen_0;
    1030          42 :   R = get_Fp_hermite(&data, d);
    1031          42 :   H = gen_howell_i(A, 1, 0, 0, 1, &ops, data, R);
    1032          42 :   D = gen_detops(ops, data, R);
    1033          42 :   D = Fp_inv(D, d);
    1034         203 :   for (i = 1; i <= n; i++) D = Fp_mul(D, gcoeff(H,i,i), d);
    1035          42 :   return gerepileuptoint(av, D);
    1036             : }
    1037             : 
    1038             : GEN
    1039       55797 : matkermod(GEN A, GEN d, GEN* im)
    1040             : {
    1041       55797 :   pari_sp av = avma;
    1042             :   void *data;
    1043             :   const struct bb_hermite* R;
    1044             :   long m,n;
    1045             :   GEN K;
    1046       55797 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matkermod", A);
    1047       55783 :   if (typ(d)!=t_INT) pari_err_TYPE("matkermod", d);
    1048       55776 :   if (signe(d)<=0) pari_err_DOMAIN("makermod", "d", "<=", gen_0, d);
    1049       55769 :   if (equali1(d)) return cgetg(1,t_MAT);
    1050       55762 :   R = get_Fp_hermite(&data, d);
    1051       55762 :   RgM_dimensions(A,&m,&n);
    1052       55762 :   if (!im && m>2*n) /* TODO tune treshold */
    1053        4592 :     A = shallowtrans(matimagemod(shallowtrans(A),d,NULL));
    1054       55762 :   K = gen_kernel(A, im, data, R); return gc_all(av,im?2:1,&K,im);
    1055             : }
    1056             : 
    1057             : /* left inverse */
    1058             : GEN
    1059      677661 : matinvmod(GEN A, GEN d)
    1060             : {
    1061      677661 :   pari_sp av = avma;
    1062             :   void *data;
    1063             :   const struct bb_hermite* R;
    1064             :   GEN U;
    1065      677661 :   if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matinvmod", A);
    1066      677668 :   if (typ(d)!=t_INT) pari_err_TYPE("matinvmod", d);
    1067      677661 :   if (signe(d)<=0) pari_err_DOMAIN("matinvmod", "d", "<=", gen_0, d);
    1068      677654 :   if (equali1(d)) {
    1069             :     long m,n;
    1070       86315 :     RgM_dimensions(A,&m,&n);
    1071       86315 :     if (m<n) pari_err_INV("matinvmod",A);
    1072       86308 :     return zeromatcopy(n,m);
    1073             :   }
    1074      591347 :   R = get_Fp_hermite(&data, d);
    1075      591373 :   U = gen_inv(shallowtrans(A), data, R);
    1076      572522 :   return gerepilecopy(av, shallowtrans(U));
    1077             : }
    1078             : 
    1079             : /* assume all D[i]>0, not GC-clean */
    1080             : static GEN
    1081       43953 : matsolvemod_finite(GEN M, GEN D, GEN Y, long flag)
    1082             : {
    1083             :   void *data;
    1084             :   const struct bb_hermite* R;
    1085             :   GEN X, K, d;
    1086             :   long m, n;
    1087             : 
    1088       43953 :   RgM_dimensions(M,&m,&n);
    1089       43953 :   if (typ(D)==t_COL)
    1090             :   { /* create extra variables for the D[i] */
    1091             :     GEN MD;
    1092          84 :     long i, i2, extra = 0;
    1093          84 :     d = gen_1;
    1094         231 :     for (i=1; i<lg(D); i++) d = lcmii(d,gel(D,i));
    1095         231 :     for (i=1; i<lg(D); i++) if (!equalii(gel(D,i),d)) extra++;
    1096          84 :     MD = cgetg(extra+1,t_MAT);
    1097          84 :     i2 = 1;
    1098         231 :     for (i=1; i<lg(D); i++)
    1099         147 :       if (!equalii(gel(D,i),d))
    1100             :       {
    1101          77 :         gel(MD,i2) = Rg_col_ei(gel(D,i),m,i);
    1102          77 :         i2++;
    1103             :       }
    1104          84 :     M = shallowconcat(M,MD);
    1105             :   }
    1106       43869 :   else d = D;
    1107             : 
    1108       43953 :   R = get_Fp_hermite(&data, d);
    1109       43953 :   X = gen_solve(M, Y, flag? &K: NULL, data, R);
    1110       43953 :   if (!X) return gen_0;
    1111       43904 :   X = vecslice(X,1,n);
    1112             : 
    1113       43904 :   if (flag)
    1114             :   {
    1115       43855 :     K = rowslice(K,1,n);
    1116       43855 :     K = hnfmodid(shallowconcat(zerocol(n),K),d);
    1117       43855 :     X = mkvec2(X,K);
    1118             :   }
    1119       43904 :   return X;
    1120             : }
    1121             : 
    1122             : /* Return a solution of congruence system sum M[i,j] X_j = Y_i mod D_i
    1123             :  * If pU != NULL, put in *pU a Z-basis of the homogeneous system.
    1124             :  * Works for all non negative D_i but inefficient compared to
    1125             :  * matsolvemod_finite; to be used only when one D_i is 0 */
    1126             : static GEN
    1127          70 : gaussmoduloall(GEN M, GEN D, GEN Y, GEN *pU)
    1128             : {
    1129          70 :   pari_sp av = avma;
    1130          70 :   long n, m, j, l, lM = lg(M);
    1131             :   GEN delta, H, U, u1, u2, x;
    1132             : 
    1133          70 :   if (lM == 1)
    1134             :   {
    1135          28 :     long lY = 0;
    1136          28 :     switch(typ(Y))
    1137             :     {
    1138           0 :       case t_INT: break;
    1139          28 :       case t_COL: lY = lg(Y); break;
    1140           0 :       default: pari_err_TYPE("gaussmodulo",Y);
    1141             :     }
    1142          28 :     switch(typ(D))
    1143             :     {
    1144          14 :       case t_INT: break;
    1145          14 :       case t_COL: if (lY && lY != lg(D)) pari_err_DIM("gaussmodulo");
    1146          14 :                   break;
    1147           0 :       default: pari_err_TYPE("gaussmodulo",D);
    1148             :     }
    1149          28 :     if (pU) *pU = cgetg(1, t_MAT);
    1150          28 :     return cgetg(1,t_COL);
    1151             :   }
    1152          42 :   n = nbrows(M);
    1153          42 :   switch(typ(D))
    1154             :   {
    1155          14 :     case t_COL:
    1156          14 :       if (lg(D)-1!=n) pari_err_DIM("gaussmodulo");
    1157          14 :       delta = diagonal_shallow(D); break;
    1158          28 :     case t_INT: delta = scalarmat_shallow(D,n); break;
    1159           0 :     default: pari_err_TYPE("gaussmodulo",D);
    1160             :       return NULL; /* LCOV_EXCL_LINE */
    1161             :   }
    1162          42 :   switch(typ(Y))
    1163             :   {
    1164           0 :     case t_INT: Y = const_col(n, Y); break;
    1165          42 :     case t_COL:
    1166          42 :       if (lg(Y)-1!=n) pari_err_DIM("gaussmodulo");
    1167          42 :       break;
    1168           0 :     default: pari_err_TYPE("gaussmodulo",Y);
    1169             :       return NULL; /* LCOV_EXCL_LINE */
    1170             :   }
    1171          42 :   H = ZM_hnfall_i(shallowconcat(M,delta), &U, 1);
    1172          42 :   Y = hnf_solve(H,Y); if (!Y) return gen_0;
    1173          35 :   l = lg(H); /* may be smaller than lM if some moduli are 0 */
    1174          35 :   n = l-1;
    1175          35 :   m = lg(U)-1 - n;
    1176          35 :   u1 = cgetg(m+1,t_MAT);
    1177          35 :   u2 = cgetg(n+1,t_MAT);
    1178          84 :   for (j=1; j<=m; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u1,j) = c; }
    1179          35 :   U += m;
    1180          77 :   for (j=1; j<=n; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u2,j) = c; }
    1181             :   /*       (u1 u2)
    1182             :    * (M D) (*  * ) = (0 H) */
    1183          35 :   u1 = ZM_lll(u1, 0.75, LLL_INPLACE);
    1184          35 :   Y = ZM_ZC_mul(u2,Y);
    1185          35 :   x = ZC_reducemodmatrix(Y, u1);
    1186          35 :   if (!pU) x = gerepileupto(av, x);
    1187             :   else
    1188             :   {
    1189          14 :     gerepileall(av, 2, &x, &u1);
    1190          14 :     *pU = u1;
    1191             :   }
    1192          35 :   return x;
    1193             : }
    1194             : /* to be used when one D_i is 0 */
    1195             : static GEN
    1196          70 : msolvemod0(GEN M, GEN D, GEN Y, long flag)
    1197             : {
    1198          70 :   pari_sp av = avma;
    1199             :   GEN y, x, U;
    1200          70 :   if (!flag) return gaussmoduloall(M,D,Y,NULL);
    1201          28 :   y = cgetg(3,t_VEC);
    1202          28 :   x = gaussmoduloall(M,D,Y,&U);
    1203          28 :   if (x == gen_0) { set_avma(av); return gen_0; }
    1204          21 :   gel(y,1) = x;
    1205          21 :   gel(y,2) = U; return y;
    1206             : 
    1207             : }
    1208             : GEN
    1209       44149 : matsolvemod(GEN M, GEN D, GEN Y, long flag)
    1210             : {
    1211       44149 :   pari_sp av = avma;
    1212       44149 :   long m, n, i, char0 = 0;
    1213       44149 :   if (typ(M)!=t_MAT || !RgM_is_ZM(M)) pari_err_TYPE("matsolvemod (M)",M);
    1214       44135 :   RgM_dimensions(M,&m,&n);
    1215       44135 :   if (typ(D)!=t_INT && (typ(D)!=t_COL || !RgV_is_ZV(D)))
    1216          28 :     pari_err_TYPE("matsolvemod (D)",D);
    1217       44107 :   if (n)
    1218       43960 :     { if (typ(D)==t_COL && lg(D)!=m+1) pari_err_DIM("matsolvemod [1]"); }
    1219             :   else
    1220         147 :     { if (typ(D)==t_COL) m = lg(D)-1; }
    1221       44100 :   if (typ(Y)==t_INT)
    1222       43890 :     Y = const_col(m,Y);
    1223         210 :   else if (typ(Y)!=t_COL || !RgV_is_ZV(Y)) pari_err_TYPE("matsolvemod (Y)",Y);
    1224       44072 :   if (typ(D)==t_COL && typ(Y)==t_COL && lg(D)!=lg(Y))
    1225          28 :     pari_err_DIM("matsolvemod [2]");
    1226       44044 :   if (!n && !m) m = lg(Y)-1;
    1227       43988 :   else if (m != lg(Y)-1) pari_err_DIM("matsolvemod [3]");
    1228       44037 :   if (typ(D)==t_INT)
    1229             :   {
    1230       43918 :     if (signe(D)<0) pari_err_DOMAIN("matsolvemod","D","<",gen_0,D);
    1231       43911 :     if (!signe(D)) char0 = 1;
    1232             :   }
    1233             :   else /*typ(D)==t_COL*/
    1234         301 :     for (i=1; i<=m; i++)
    1235             :     {
    1236         189 :       if (signe(gel(D,i))<0)
    1237           7 :         pari_err_DOMAIN("matsolvemod","D[i]","<",gen_0,gel(D,i));
    1238         182 :       if (!signe(gel(D,i))) char0 = 1;
    1239             :     }
    1240       87976 :   return gerepilecopy(av, char0? msolvemod0(M,D,Y,flag)
    1241       43953 :                                : matsolvemod_finite(M,D,Y,flag));
    1242             : }
    1243             : GEN
    1244           0 : gaussmodulo2(GEN M, GEN D, GEN Y) { return matsolvemod(M,D,Y, 1); }
    1245             : GEN
    1246           0 : gaussmodulo(GEN M, GEN D, GEN Y) { return matsolvemod(M,D,Y, 0); }

Generated by: LCOV version 1.16