Code coverage tests

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

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

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

LCOV - code coverage report
Current view: top level - basemath - bibli1.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.10.0 lcov report (development 20459-9710128) Lines: 984 1034 95.2 %
Date: 2017-04-27 05:33:52 Functions: 59 65 90.8 %
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             : 
      14             : /********************************************************************/
      15             : /**                                                                **/
      16             : /**                 LLL Algorithm and close friends                **/
      17             : /**                                                                **/
      18             : /********************************************************************/
      19             : #include "pari.h"
      20             : #include "paripriv.h"
      21             : 
      22             : /********************************************************************/
      23             : /**             QR Factorization via Householder matrices          **/
      24             : /********************************************************************/
      25             : static int
      26      858504 : no_prec_pb(GEN x)
      27             : {
      28     2345634 :   return (typ(x) != t_REAL || realprec(x) > LOWDEFAULTPREC
      29      860568 :                            || expo(x) < BITS_IN_LONG/2);
      30             : }
      31             : /* Find a Householder transformation which, applied to x[k..#x], zeroes
      32             :  * x[k+1..#x]; fill L = (mu_{i,j}). Return 0 if precision problem [obtained
      33             :  * a 0 vector], 1 otherwise */
      34             : static int
      35      858504 : FindApplyQ(GEN x, GEN L, GEN B, long k, GEN Q, long prec)
      36             : {
      37      858504 :   long i, lx = lg(x)-1;
      38      858504 :   GEN x2, x1, xd = x + (k-1);
      39             : 
      40      858504 :   x1 = gel(xd,1);
      41      858504 :   x2 = mpsqr(x1);
      42      858504 :   if (k < lx)
      43             :   {
      44      576324 :     long lv = lx - (k-1) + 1;
      45      576324 :     GEN beta, Nx, v = cgetg(lv, t_VEC);
      46     2014722 :     for (i=2; i<lv; i++) {
      47     1438398 :       x2 = mpadd(x2, mpsqr(gel(xd,i)));
      48     1438398 :       gel(v,i) = gel(xd,i);
      49             :     }
      50      576324 :     if (!signe(x2)) return 0;
      51      576324 :     Nx = gsqrt(x2, prec); if (signe(x1) < 0) setsigne(Nx, -1);
      52      576324 :     gel(v,1) = mpadd(x1, Nx);
      53             : 
      54      576324 :     if (!signe(x1))
      55         102 :       beta = gtofp(x2, prec); /* make sure typ(beta) != t_INT */
      56             :     else
      57      576222 :       beta = mpadd(x2, mpmul(Nx,x1));
      58      576324 :     gel(Q,k) = mkvec2(invr(beta), v);
      59             : 
      60      576324 :     togglesign(Nx);
      61      576324 :     gcoeff(L,k,k) = Nx;
      62             :   }
      63             :   else
      64      282180 :     gcoeff(L,k,k) = gel(x,k);
      65      858504 :   gel(B,k) = x2;
      66      858504 :   for (i=1; i<k; i++) gcoeff(L,k,i) = gel(x,i);
      67      858504 :   return no_prec_pb(x2);
      68             : }
      69             : 
      70             : /* apply Householder transformation Q = [beta,v] to r with t_INT/t_REAL
      71             :  * coefficients, in place: r -= ((0|v).r * beta) v */
      72             : static void
      73     1438380 : ApplyQ(GEN Q, GEN r)
      74             : {
      75     1438380 :   GEN s, rd, beta = gel(Q,1), v = gel(Q,2);
      76     1438380 :   long i, l = lg(v), lr = lg(r);
      77             : 
      78     1438380 :   rd = r + (lr - l);
      79     1438380 :   s = mpmul(gel(v,1), gel(rd,1));
      80     1438380 :   for (i=2; i<l; i++) s = mpadd(s, mpmul(gel(v,i) ,gel(rd,i)));
      81     1438380 :   s = mpmul(beta, s);
      82    10326198 :   for (i=1; i<l; i++)
      83     8887818 :     if (signe(gel(v,i))) gel(rd,i) = mpsub(gel(rd,i), mpmul(s, gel(v,i)));
      84     1438380 : }
      85             : /* apply Q[1], ..., Q[j-1] to r */
      86             : static GEN
      87      576318 : ApplyAllQ(GEN Q, GEN r, long j)
      88             : {
      89      576318 :   pari_sp av = avma;
      90             :   long i;
      91      576318 :   r = leafcopy(r);
      92      576318 :   for (i=1; i<j; i++) ApplyQ(gel(Q,i), r);
      93      576318 :   return gerepilecopy(av, r);
      94             : }
      95             : 
      96             : /* same, arbitrary coefficients [20% slower for t_REAL at DEFAULTPREC] */
      97             : static void
      98       18720 : RgC_ApplyQ(GEN Q, GEN r)
      99             : {
     100       18720 :   GEN s, rd, beta = gel(Q,1), v = gel(Q,2);
     101       18720 :   long i, l = lg(v), lr = lg(r);
     102             : 
     103       18720 :   rd = r + (lr - l);
     104       18720 :   s = gmul(gel(v,1), gel(rd,1));
     105       18720 :   for (i=2; i<l; i++) s = gadd(s, gmul(gel(v,i) ,gel(rd,i)));
     106       18720 :   s = gmul(beta, s);
     107      411840 :   for (i=1; i<l; i++)
     108      393120 :     if (signe(gel(v,i))) gel(rd,i) = gsub(gel(rd,i), gmul(s, gel(v,i)));
     109       18720 : }
     110             : static GEN
     111         480 : RgC_ApplyAllQ(GEN Q, GEN r, long j)
     112             : {
     113         480 :   pari_sp av = avma;
     114             :   long i;
     115         480 :   r = leafcopy(r);
     116         480 :   for (i=1; i<j; i++) RgC_ApplyQ(gel(Q,i), r);
     117         480 :   return gerepilecopy(av, r);
     118             : }
     119             : 
     120             : int
     121          12 : RgM_QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec)
     122             : {
     123          12 :   x = RgM_gtomp(x, prec);
     124          12 :   return QR_init(x, pB, pQ, pL, prec);
     125             : }
     126             : 
     127             : static void
     128          24 : check_householder(GEN Q)
     129             : {
     130          24 :   long i, l = lg(Q);
     131          24 :   if (typ(Q) != t_VEC) pari_err_TYPE("mathouseholder", Q);
     132         720 :   for (i = 1; i < l; i++)
     133             :   {
     134         702 :     GEN q = gel(Q,i), v;
     135         702 :     if (typ(q) != t_VEC || lg(q) != 3) pari_err_TYPE("mathouseholder", Q);
     136         702 :     v = gel(q,2);
     137         702 :     if (typ(v) != t_VEC || lg(v)+i-2 != l) pari_err_TYPE("mathouseholder", Q);
     138             :   }
     139          18 : }
     140             : 
     141             : GEN
     142          24 : mathouseholder(GEN Q, GEN v)
     143             : {
     144          24 :   long l = lg(Q);
     145          24 :   check_householder(Q);
     146          18 :   switch(typ(v))
     147             :   {
     148             :     case t_MAT:
     149             :     {
     150             :       long lx, i;
     151          12 :       GEN M = cgetg_copy(v, &lx);
     152          12 :       for (i = 1; i < lx; i++) gel(M,i) = RgC_ApplyAllQ(Q, gel(v,i), l);
     153          12 :       return M;
     154             :     }
     155           0 :     case t_COL: if (lg(v) == l) break;
     156             :       /* fall through */
     157           6 :     default: pari_err_TYPE("mathouseholder", v);
     158             :   }
     159           0 :   return RgC_ApplyAllQ(Q, v, l);
     160             : }
     161             : 
     162             : GEN
     163          24 : matqr(GEN x, long flag, long prec)
     164             : {
     165          24 :   pari_sp av = avma;
     166             :   GEN B, Q, L;
     167          24 :   long n = lg(x)-1;
     168          24 :   if (typ(x) != t_MAT) pari_err_TYPE("matqr",x);
     169          24 :   if (!n)
     170             :   {
     171          12 :     if (!flag) retmkvec2(cgetg(1,t_MAT),cgetg(1,t_MAT));
     172           6 :     retmkvec2(cgetg(1,t_VEC),cgetg(1,t_MAT));
     173             :   }
     174          12 :   if (n != nbrows(x)) pari_err_DIM("matqr");
     175          12 :   if (!RgM_QR_init(x, &B,&Q,&L, prec)) pari_err_PREC("matqr");
     176          12 :   if (!flag) Q = shallowtrans(mathouseholder(Q, matid(n)));
     177          12 :   return gerepilecopy(av, mkvec2(Q, shallowtrans(L)));
     178             : }
     179             : 
     180             : /* compute B = | x[k] |^2, Q = Householder transforms and L = mu_{i,j} */
     181             : int
     182      282186 : QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec)
     183             : {
     184      282186 :   long j, k = lg(x)-1;
     185      282186 :   GEN B = cgetg(k+1, t_VEC), Q = cgetg(k, t_VEC), L = zeromatcopy(k,k);
     186     1140678 :   for (j=1; j<=k; j++)
     187             :   {
     188      858504 :     GEN r = gel(x,j);
     189      858504 :     if (j > 1) r = ApplyAllQ(Q, r, j);
     190      858504 :     if (!FindApplyQ(r, L, B, j, Q, prec)) return 0;
     191             :   }
     192      282174 :   *pB = B; *pQ = Q; *pL = L; return 1;
     193             : }
     194             : /* x a square t_MAT with t_INT / t_REAL entries and maximal rank. Return
     195             :  * qfgaussred(x~*x) */
     196             : GEN
     197      281862 : gaussred_from_QR(GEN x, long prec)
     198             : {
     199      281862 :   long j, k = lg(x)-1;
     200             :   GEN B, Q, L;
     201      281862 :   if (!QR_init(x, &B,&Q,&L, prec)) return NULL;
     202      855864 :   for (j=1; j<k; j++)
     203             :   {
     204      574002 :     GEN m = gel(L,j), invNx = invr(gel(m,j));
     205             :     long i;
     206      574002 :     gel(m,j) = gel(B,j);
     207      574002 :     for (i=j+1; i<=k; i++) gel(m,i) = mpmul(invNx, gel(m,i));
     208             :   }
     209      281862 :   gcoeff(L,j,j) = gel(B,j);
     210      281862 :   return shallowtrans(L);
     211             : }
     212             : GEN
     213         312 : R_from_QR(GEN x, long prec)
     214             : {
     215             :   GEN B, Q, L;
     216         312 :   if (!QR_init(x, &B,&Q,&L, prec)) return NULL;
     217         300 :   return shallowtrans(L);
     218             : }
     219             : 
     220             : /********************************************************************/
     221             : /**             QR Factorization via Gram-Schmidt                  **/
     222             : /********************************************************************/
     223             : 
     224             : /* return Gram-Schmidt orthogonal basis (f) attached to (e), B is the
     225             :  * vector of the (f_i . f_i)*/
     226             : GEN
     227         372 : RgM_gram_schmidt(GEN e, GEN *ptB)
     228             : {
     229         372 :   long i,j,lx = lg(e);
     230         372 :   GEN f = RgM_shallowcopy(e), B, iB;
     231             : 
     232         372 :   B = cgetg(lx, t_VEC);
     233         372 :   iB= cgetg(lx, t_VEC);
     234             : 
     235        1230 :   for (i=1;i<lx;i++)
     236             :   {
     237         858 :     GEN p1 = NULL;
     238         858 :     pari_sp av = avma;
     239        1890 :     for (j=1; j<i; j++)
     240             :     {
     241        1032 :       GEN mu = gmul(RgV_dotproduct(gel(e,i),gel(f,j)), gel(iB,j));
     242        1032 :       GEN p2 = gmul(mu, gel(f,j));
     243        1032 :       p1 = p1? gadd(p1,p2): p2;
     244             :     }
     245         858 :     p1 = p1? gerepileupto(av, gsub(gel(e,i), p1)): gel(e,i);
     246         858 :     gel(f,i) = p1;
     247         858 :     gel(B,i) = RgV_dotsquare(gel(f,i));
     248         858 :     gel(iB,i) = ginv(gel(B,i));
     249             :   }
     250         372 :   *ptB = B; return f;
     251             : }
     252             : 
     253             : /* Assume B an LLL-reduced basis, t a vector. Apply Babai's nearest plane
     254             :  * algorithm to (B,t) */
     255             : GEN
     256         372 : RgM_Babai(GEN B, GEN t)
     257             : {
     258         372 :   GEN C, N, G = RgM_gram_schmidt(B, &N), b = t;
     259         372 :   long j, n = lg(B)-1;
     260             : 
     261         372 :   C = cgetg(n+1,t_COL);
     262        1230 :   for (j = n; j > 0; j--)
     263             :   {
     264         858 :     GEN c = gdiv( RgV_dotproduct(b, gel(G,j)), gel(N,j) );
     265             :     long e;
     266         858 :     c = grndtoi(c,&e);
     267         858 :     if (e >= 0) return NULL;
     268         858 :     if (signe(c)) b = RgC_sub(b, RgC_Rg_mul(gel(G,j), c));
     269         858 :     gel(C,j) = c;
     270             :   }
     271         372 :   return C;
     272             : }
     273             : 
     274             : /********************************************************************/
     275             : /**                                                                **/
     276             : /**                          LLL ALGORITHM                         **/
     277             : /**                                                                **/
     278             : /********************************************************************/
     279             : /* Def: a matrix M is said to be -partially reduced- if | m1 +- m2 | >= |m1|
     280             :  * for any two columns m1 != m2, in M.
     281             :  *
     282             :  * Input: an integer matrix mat whose columns are linearly independent. Find
     283             :  * another matrix T such that mat * T is partially reduced.
     284             :  *
     285             :  * Output: mat * T if flag = 0;  T if flag != 0,
     286             :  *
     287             :  * This routine is designed to quickly reduce lattices in which one row
     288             :  * is huge compared to the other rows.  For example, when searching for a
     289             :  * polynomial of degree 3 with root a mod N, the four input vectors might
     290             :  * be the coefficients of
     291             :  *     X^3 - (a^3 mod N), X^2 - (a^2 mod N), X - (a mod N), N.
     292             :  * All four constant coefficients are O(p) and the rest are O(1). By the
     293             :  * pigeon-hole principle, the coefficients of the smallest vector in the
     294             :  * lattice are O(p^(1/4)), hence significant reduction of vector lengths
     295             :  * can be anticipated.
     296             :  *
     297             :  * An improved algorithm would look only at the leading digits of dot*.  It
     298             :  * would use single-precision calculations as much as possible.
     299             :  *
     300             :  * Original code: Peter Montgomery (1994) */
     301             : static GEN
     302          30 : lllintpartialall(GEN m, long flag)
     303             : {
     304          30 :   const long ncol = lg(m)-1;
     305          30 :   const pari_sp av = avma;
     306             :   GEN tm1, tm2, mid;
     307             : 
     308          30 :   if (ncol <= 1) return flag? matid(ncol): gcopy(m);
     309             : 
     310          12 :   tm1 = flag? matid(ncol): NULL;
     311             :   {
     312          12 :     const pari_sp av2 = avma;
     313          12 :     GEN dot11 = ZV_dotsquare(gel(m,1));
     314          12 :     GEN dot22 = ZV_dotsquare(gel(m,2));
     315          12 :     GEN dot12 = ZV_dotproduct(gel(m,1), gel(m,2));
     316          12 :     GEN tm  = matid(2); /* For first two columns only */
     317             : 
     318          12 :     int progress = 0;
     319          12 :     long npass2 = 0;
     320             : 
     321             : /* Row reduce the first two columns of m. Our best result so far is
     322             :  * (first two columns of m)*tm.
     323             :  *
     324             :  * Initially tm = 2 x 2 identity matrix.
     325             :  * Inner products of the reduced matrix are in dot11, dot12, dot22. */
     326          54 :     while (npass2 < 2 || progress)
     327             :     {
     328          36 :       GEN dot12new, q = diviiround(dot12, dot22);
     329             : 
     330          30 :       npass2++; progress = signe(q);
     331          30 :       if (progress)
     332             :       {/* Conceptually replace (v1, v2) by (v1 - q*v2, v2), where v1 and v2
     333             :         * represent the reduced basis for the first two columns of the matrix.
     334             :         * We do this by updating tm and the inner products. */
     335          18 :         togglesign(q);
     336          18 :         dot12new = addii(dot12, mulii(q, dot22));
     337          18 :         dot11 = addii(dot11, mulii(q, addii(dot12, dot12new)));
     338          18 :         dot12 = dot12new;
     339          18 :         ZC_lincomb1_inplace(gel(tm,1), gel(tm,2), q);
     340             :       }
     341             : 
     342             :       /* Interchange the output vectors v1 and v2.  */
     343          30 :       swap(dot11,dot22);
     344          30 :       swap(gel(tm,1), gel(tm,2));
     345             : 
     346             :       /* Occasionally (including final pass) do garbage collection.  */
     347          30 :       if ((npass2 & 0xff) == 0 || !progress)
     348          12 :         gerepileall(av2, 4, &dot11,&dot12,&dot22,&tm);
     349             :     } /* while npass2 < 2 || progress */
     350             : 
     351             :     {
     352             :       long i;
     353           6 :       GEN det12 = subii(mulii(dot11, dot22), sqri(dot12));
     354             : 
     355           6 :       mid = cgetg(ncol+1, t_MAT);
     356          18 :       for (i = 1; i <= 2; i++)
     357             :       {
     358          12 :         GEN tmi = gel(tm,i);
     359          12 :         if (tm1)
     360             :         {
     361          12 :           GEN tm1i = gel(tm1,i);
     362          12 :           gel(tm1i,1) = gel(tmi,1);
     363          12 :           gel(tm1i,2) = gel(tmi,2);
     364             :         }
     365          12 :         gel(mid,i) = ZC_lincomb(gel(tmi,1),gel(tmi,2), gel(m,1),gel(m,2));
     366             :       }
     367          36 :       for (i = 3; i <= ncol; i++)
     368             :       {
     369          30 :         GEN c = gel(m,i);
     370          30 :         GEN dot1i = ZV_dotproduct(gel(mid,1), c);
     371          30 :         GEN dot2i = ZV_dotproduct(gel(mid,2), c);
     372             :        /* ( dot11  dot12 ) (q1)   ( dot1i )
     373             :         * ( dot12  dot22 ) (q2) = ( dot2i )
     374             :         *
     375             :         * Round -q1 and -q2 to nearest integer. Then compute
     376             :         *   c - q1*mid[1] - q2*mid[2].
     377             :         * This will be approximately orthogonal to the first two vectors in
     378             :         * the new basis. */
     379          30 :         GEN q1neg = subii(mulii(dot12, dot2i), mulii(dot22, dot1i));
     380          30 :         GEN q2neg = subii(mulii(dot12, dot1i), mulii(dot11, dot2i));
     381             : 
     382          30 :         q1neg = diviiround(q1neg, det12);
     383          30 :         q2neg = diviiround(q2neg, det12);
     384          30 :         if (tm1)
     385             :         {
     386          60 :           gcoeff(tm1,1,i) = addii(mulii(q1neg, gcoeff(tm,1,1)),
     387          30 :                                   mulii(q2neg, gcoeff(tm,1,2)));
     388          60 :           gcoeff(tm1,2,i) = addii(mulii(q1neg, gcoeff(tm,2,1)),
     389          30 :                                   mulii(q2neg, gcoeff(tm,2,2)));
     390             :         }
     391          30 :         gel(mid,i) = ZC_add(c, ZC_lincomb(q1neg,q2neg, gel(mid,1),gel(mid,2)));
     392             :       } /* for i */
     393             :     } /* local block */
     394             :   }
     395           6 :   if (DEBUGLEVEL>6)
     396             :   {
     397           0 :     if (tm1) err_printf("tm1 = %Ps",tm1);
     398           0 :     err_printf("mid = %Ps",mid); /* = m * tm1 */
     399             :   }
     400           6 :   gerepileall(av, tm1? 2: 1, &mid, &tm1);
     401             :   {
     402             :    /* For each pair of column vectors v and w in mid * tm2,
     403             :     * try to replace (v, w) by (v, v - q*w) for some q.
     404             :     * We compute all inner products and check them repeatedly. */
     405           6 :     const pari_sp av3 = avma;
     406           6 :     long i, j, npass = 0, e = LONG_MAX;
     407           6 :     GEN dot = cgetg(ncol+1, t_MAT); /* scalar products */
     408             : 
     409           6 :     tm2 = matid(ncol);
     410          48 :     for (i=1; i <= ncol; i++)
     411             :     {
     412          42 :       gel(dot,i) = cgetg(ncol+1,t_COL);
     413         210 :       for (j=1; j <= i; j++)
     414         168 :         gcoeff(dot,j,i) = gcoeff(dot,i,j) = ZV_dotproduct(gel(mid,i),gel(mid,j));
     415             :     }
     416             :     for(;;)
     417             :     {
     418          36 :       long reductions = 0, olde = e;
     419         288 :       for (i=1; i <= ncol; i++)
     420             :       {
     421             :         long ijdif;
     422        1764 :         for (ijdif=1; ijdif < ncol; ijdif++)
     423             :         {
     424             :           long d, k1, k2;
     425             :           GEN codi, q;
     426             : 
     427        1512 :           j = i + ijdif; if (j > ncol) j -= ncol;
     428             :           /* let k1, resp. k2,  index of larger, resp. smaller, column */
     429        1512 :           if (cmpii(gcoeff(dot,i,i), gcoeff(dot,j,j)) > 0) { k1 = i; k2 = j; }
     430         876 :           else                                             { k1 = j; k2 = i; }
     431        1512 :           codi = gcoeff(dot,k2,k2);
     432        1512 :           q = signe(codi)? diviiround(gcoeff(dot,k1,k2), codi): gen_0;
     433        1512 :           if (!signe(q)) continue;
     434             : 
     435             :           /* Try to subtract a multiple of column k2 from column k1.  */
     436         600 :           reductions++; togglesign_safe(&q);
     437         600 :           ZC_lincomb1_inplace(gel(tm2,k1), gel(tm2,k2), q);
     438         600 :           ZC_lincomb1_inplace(gel(dot,k1), gel(dot,k2), q);
     439        1200 :           gcoeff(dot,k1,k1) = addii(gcoeff(dot,k1,k1),
     440         600 :                                     mulii(q, gcoeff(dot,k2,k1)));
     441         600 :           for (d = 1; d <= ncol; d++) gcoeff(dot,k1,d) = gcoeff(dot,d,k1);
     442             :         } /* for ijdif */
     443         252 :         if (gc_needed(av3,2))
     444             :         {
     445           0 :           if(DEBUGMEM>1) pari_warn(warnmem,"lllintpartialall");
     446           0 :           gerepileall(av3, 2, &dot,&tm2);
     447             :         }
     448             :       } /* for i */
     449          36 :       if (!reductions) break;
     450          30 :       e = 0;
     451          30 :       for (i = 1; i <= ncol; i++) e += expi( gcoeff(dot,i,i) );
     452          30 :       if (e == olde) break;
     453          30 :       if (DEBUGLEVEL>6)
     454             :       {
     455           0 :         npass++;
     456           0 :         err_printf("npass = %ld, red. last time = %ld, log_2(det) ~ %ld\n\n",
     457             :                     npass, reductions, e);
     458             :       }
     459          30 :     } /* for(;;)*/
     460             : 
     461             :    /* Sort columns so smallest comes first in m * tm1 * tm2.
     462             :     * Use insertion sort. */
     463          42 :     for (i = 1; i < ncol; i++)
     464             :     {
     465          36 :       long j, s = i;
     466             : 
     467         162 :       for (j = i+1; j <= ncol; j++)
     468         126 :         if (cmpii(gcoeff(dot,s,s),gcoeff(dot,j,j)) > 0) s = j;
     469          36 :       if (i != s)
     470             :       { /* Exchange with proper column; only the diagonal of dot is updated */
     471          24 :         swap(gel(tm2,i), gel(tm2,s));
     472          24 :         swap(gcoeff(dot,i,i), gcoeff(dot,s,s));
     473             :       }
     474             :     }
     475             :   } /* local block */
     476           6 :   return gerepileupto(av, ZM_mul(tm1? tm1: mid, tm2));
     477             : }
     478             : 
     479             : GEN
     480          30 : lllintpartial(GEN mat) { return lllintpartialall(mat,1); }
     481             : 
     482             : GEN
     483           0 : lllintpartial_inplace(GEN mat) { return lllintpartialall(mat,0); }
     484             : 
     485             : /********************************************************************/
     486             : /**                                                                **/
     487             : /**                    COPPERSMITH ALGORITHM                       **/
     488             : /**           Finding small roots of univariate equations.         **/
     489             : /**                                                                **/
     490             : /********************************************************************/
     491             : 
     492             : static int
     493         756 : check_condition(double beta, double tau, double rho, long d, long delta, long t)
     494             : {
     495         756 :   long dim = d*delta + t;
     496        1512 :   double cond = d*delta*(delta+1)/2 - beta*delta*dim
     497         756 :     + rho*delta*(delta - 1) / 2
     498         756 :     + rho * t * delta + tau*dim*(dim - 1)/2;
     499             : 
     500         756 :   if (DEBUGLEVEL >= 4)
     501           0 :     err_printf("delta = %d, t = %d, cond = %.1lf\n", delta, t, cond);
     502             : 
     503         756 :   return (cond <= 0);
     504             : }
     505             : 
     506             : static void
     507          12 : choose_params(GEN P, GEN N, GEN X, GEN B, long *pdelta, long *pt)
     508             : {
     509          12 :   long d = degpol(P);
     510          12 :   GEN P0 = leading_coeff(P);
     511          12 :   double logN = gtodouble(glog(N, DEFAULTPREC));
     512             :   double tau, beta, rho;
     513             :   long delta, t;
     514          12 :   tau = gtodouble(glog(X, DEFAULTPREC)) / logN;
     515          12 :   beta = B? gtodouble(glog(B, DEFAULTPREC)) / logN: 1.;
     516          12 :   if (tau >= beta * beta / d)
     517           0 :     pari_err_OVERFLOW("zncoppersmith [bound too large]");
     518             :   /* TODO : remove P0 completely ! */
     519          12 :   rho = gtodouble(glog(P0, DEFAULTPREC)) / logN;
     520             : 
     521             :   /* Enumerate (delta,t) by increasing dimension of resulting lattice.
     522             :    * Not subtle, but o(1) for computing time */
     523          12 :   t = d; delta = 0;
     524             :   for(;;)
     525             :   {
     526         150 :     t += d * delta + 1; delta = 0;
     527        1044 :     while (t >= 0) {
     528         756 :       if (check_condition(beta, tau, rho, d, delta, t)) {
     529          24 :         *pdelta = delta; *pt = t; return;
     530             :       }
     531         744 :       delta++; t -= d;
     532             :     }
     533         138 :   }
     534             : }
     535             : 
     536             : static int
     537       12018 : sol_OK(GEN x, GEN N, GEN B)
     538       12018 : { return B? (cmpii(gcdii(x,N),B) >= 0): !signe(remii(x,N)); }
     539             : /* deg(P) > 0, x >= 0. Find all j such that gcd(P(j), N) >= B, |j| <= x */
     540             : static GEN
     541           6 : do_exhaustive(GEN P, GEN N, long x, GEN B)
     542             : {
     543           6 :   GEN Pe, Po, sol = vecsmalltrunc_init(2*x + 2);
     544             :   pari_sp av;
     545             :   long j;
     546           6 :   RgX_even_odd(P, &Pe,&Po); av = avma;
     547           6 :   if (sol_OK(gel(P,2), N,B)) vecsmalltrunc_append(sol, 0);
     548        6006 :   for (j = 1; j <= x; j++, avma = av)
     549             :   {
     550        6000 :     GEN j2 = sqru(j), E = FpX_eval(Pe,j2,N), O = FpX_eval(Po,j2,N);
     551        6000 :     if (sol_OK(addmuliu(E,O,j), N,B)) vecsmalltrunc_append(sol, j);
     552        6000 :     if (sol_OK(submuliu(E,O,j), N,B)) vecsmalltrunc_append(sol,-j);
     553             :   }
     554           6 :   vecsmall_sort(sol); return zv_to_ZV(sol);
     555             : }
     556             : 
     557             : /* General Coppersmith, look for a root x0 <= p, p >= B, p | N, |x0| <= X.
     558             :  * B = N coded as NULL */
     559             : GEN
     560          18 : zncoppersmith(GEN P0, GEN N, GEN X, GEN B)
     561             : {
     562             :   GEN Q, R, N0, M, sh, short_pol, *Xpowers, sol, nsp, P, Z;
     563          18 :   long delta, i, j, row, d, l, dim, t, bnd = 10;
     564          18 :   const ulong X_SMALL = 1000;
     565             : 
     566          18 :   pari_sp av = avma;
     567             : 
     568          18 :   if (typ(P0) != t_POL) pari_err_TYPE("zncoppersmith",P0);
     569          18 :   if (typ(N) != t_INT) pari_err_TYPE("zncoppersmith",N);
     570          18 :   if (typ(X) != t_INT) {
     571           6 :     X = gfloor(X);
     572           6 :     if (typ(X) != t_INT) pari_err_TYPE("zncoppersmith",X);
     573             :   }
     574          18 :   if (signe(X) < 0) pari_err_DOMAIN("zncoppersmith", "X", "<", gen_0, X);
     575          18 :   d = degpol(P0);
     576          18 :   if (d == 0) { avma = av; return cgetg(1, t_VEC); }
     577          18 :   if (d < 0) pari_err_ROOTS0("zncoppersmith");
     578          18 :   if (B && typ(B) != t_INT) B = gceil(B);
     579             : 
     580          18 :   if (abscmpiu(X, X_SMALL) <= 0)
     581           6 :     return gerepileupto(av, do_exhaustive(P0, N, itos(X), B));
     582             : 
     583          12 :   if (B && equalii(B,N)) B = NULL;
     584          12 :   if (B) bnd = 1; /* bnd-hack is only for the case B = N */
     585          12 :   P = leafcopy(P0);
     586          12 :   if (!gequal1(gel(P,d+2)))
     587             :   {
     588             :     GEN r, z;
     589           6 :     gel(P,d+2) = bezout(gel(P,d+2), N, &z, &r);
     590           6 :     for (j = 0; j < d; j++) gel(P,j+2) = modii(mulii(gel(P,j+2), z), N);
     591             :   }
     592          12 :   if (DEBUGLEVEL >= 2) err_printf("Modified P: %Ps\n", P);
     593             : 
     594          12 :   choose_params(P, N, X, B, &delta, &t);
     595          12 :   if (DEBUGLEVEL >= 2)
     596           0 :     err_printf("Init: trying delta = %d, t = %d\n", delta, t);
     597             :   for(;;)
     598             :   {
     599          12 :     dim = d * delta + t;
     600             : 
     601             :     /* TODO: In case of failure do not recompute the full vector */
     602          12 :     Xpowers = (GEN*)new_chunk(dim + 1);
     603          12 :     Xpowers[0] = gen_1;
     604          12 :     for (j = 1; j <= dim; j++) Xpowers[j] = mulii(Xpowers[j-1], X);
     605             : 
     606             :     /* TODO: in case of failure, use the part of the matrix already computed */
     607          12 :     M = zeromatcopy(dim,dim);
     608             : 
     609             :     /* Rows of M correspond to the polynomials
     610             :      * N^delta, N^delta Xi, ... N^delta (Xi)^d-1,
     611             :      * N^(delta-1)P(Xi), N^(delta-1)XiP(Xi), ... N^(delta-1)P(Xi)(Xi)^d-1,
     612             :      * ...
     613             :      * P(Xi)^delta, XiP(Xi)^delta, ..., P(Xi)^delta(Xi)^t-1 */
     614          12 :     for (j = 1; j <= d;   j++) gcoeff(M, j, j) = gel(Xpowers,j-1);
     615             : 
     616             :     /* P-part */
     617          12 :     if (delta) row = d + 1; else row = 0;
     618             : 
     619          12 :     Q = P;
     620          60 :     for (i = 1; i < delta; i++)
     621             :     {
     622         156 :       for (j = 0; j < d; j++,row++)
     623        1062 :         for (l = j + 1; l <= row; l++)
     624         954 :           gcoeff(M, l, row) = mulii(Xpowers[l-1], gel(Q,l-j+1));
     625          48 :       Q = ZX_mul(Q, P);
     626             :     }
     627          54 :     for (j = 0; j < t; row++, j++)
     628         420 :       for (l = j + 1; l <= row; l++)
     629         378 :         gcoeff(M, l, row) = mulii(Xpowers[l-1], gel(Q,l-j+1));
     630             : 
     631             :     /* N-part */
     632          12 :     row = dim - t; N0 = N;
     633          84 :     while (row >= 1)
     634             :     {
     635         192 :       for (j = 0; j < d; j++,row--)
     636        1218 :         for (l = 1; l <= row; l++)
     637        1086 :           gcoeff(M, l, row) = mulii(gmael(M, row, l), N0);
     638          60 :       if (row >= 1) N0 = mulii(N0, N);
     639             :     }
     640             :     /* Z is the upper bound for the L^1 norm of the polynomial,
     641             :        ie. N^delta if B = N, B^delta otherwise */
     642          12 :     if (B) Z = powiu(B, delta); else Z = N0;
     643             : 
     644          12 :     if (DEBUGLEVEL >= 2)
     645             :     {
     646           0 :       if (DEBUGLEVEL >= 6) err_printf("Matrix to be reduced:\n%Ps\n", M);
     647           0 :       err_printf("Entering LLL\nbitsize bound: %ld\n", expi(Z));
     648           0 :       err_printf("expected shvector bitsize: %ld\n", expi(ZM_det_triangular(M))/dim);
     649             :     }
     650             : 
     651          12 :     sh = ZM_lll(M, 0.75, LLL_INPLACE);
     652             :     /* Take the first vector if it is non constant */
     653          12 :     short_pol = gel(sh,1);
     654          12 :     if (ZV_isscalar(short_pol)) short_pol = gel(sh, 2);
     655             : 
     656          12 :     nsp = gen_0;
     657          12 :     for (j = 1; j <= dim; j++) nsp = addii(nsp, absi(gel(short_pol,j)));
     658             : 
     659          12 :     if (DEBUGLEVEL >= 2)
     660             :     {
     661           0 :       err_printf("Candidate: %Ps\n", short_pol);
     662           0 :       err_printf("bitsize Norm: %ld\n", expi(nsp));
     663           0 :       err_printf("bitsize bound: %ld\n", expi(mului(bnd, Z)));
     664             :     }
     665          12 :     if (cmpii(nsp, mului(bnd, Z)) < 0) break; /* SUCCESS */
     666             : 
     667             :     /* Failed with the precomputed or supplied value */
     668           0 :     t++; if (t == d) { delta++; t = 1; }
     669           0 :     if (DEBUGLEVEL >= 2)
     670           0 :       err_printf("Increasing dim, delta = %d t = %d\n", delta, t);
     671           0 :   }
     672          12 :   bnd = itos(divii(nsp, Z)) + 1;
     673             : 
     674          12 :   while (!signe(gel(short_pol,dim))) dim--;
     675             : 
     676          12 :   R = cgetg(dim + 2, t_POL); R[1] = P[1];
     677         186 :   for (j = 1; j <= dim; j++)
     678         174 :     gel(R,j+1) = diviiexact(gel(short_pol,j), Xpowers[j-1]);
     679          12 :   gel(R,2) = subii(gel(R,2), mului(bnd - 1, N0));
     680             : 
     681          12 :   sol = cgetg(1, t_VEC);
     682          96 :   for (i = -bnd + 1; i < bnd; i++)
     683             :   {
     684          84 :     GEN r = nfrootsQ(R);
     685          84 :     if (DEBUGLEVEL >= 2) err_printf("Roots: %Ps\n", r);
     686         102 :     for (j = 1; j < lg(r); j++)
     687             :     {
     688          18 :       GEN z = gel(r,j);
     689          18 :       if (typ(z) == t_INT && sol_OK(FpX_eval(P,z,N), N,B))
     690          12 :         sol = shallowconcat(sol, z);
     691             :     }
     692          84 :     if (i < bnd) gel(R,2) = addii(gel(R,2), Z);
     693             :   }
     694          12 :   return gerepileupto(av, ZV_sort_uniq(sol));
     695             : }
     696             : 
     697             : /********************************************************************/
     698             : /**                                                                **/
     699             : /**                   LINEAR & ALGEBRAIC DEPENDENCE                **/
     700             : /**                                                                **/
     701             : /********************************************************************/
     702             : 
     703             : static int
     704         630 : real_indep(GEN re, GEN im, long bit)
     705             : {
     706         630 :   GEN d = gsub(gmul(gel(re,1),gel(im,2)), gmul(gel(re,2),gel(im,1)));
     707         630 :   return (!gequal0(d) && gexpo(d) > - bit);
     708             : }
     709             : 
     710             : GEN
     711        2502 : lindep_bit(GEN x, long bit)
     712             : {
     713        2502 :   long lx = lg(x), ly, i, j;
     714        2502 :   pari_sp av = avma;
     715             :   GEN re, im, M;
     716             : 
     717        2502 :   if (! is_vec_t(typ(x))) pari_err_TYPE("lindep2",x);
     718        2502 :   if (lx <= 2)
     719             :   {
     720          18 :     if (lx == 2 && gequal0(x)) return mkcol(gen_1);
     721          12 :     return cgetg(1,t_COL);
     722             :   }
     723        2484 :   re = real_i(x);
     724        2484 :   im = imag_i(x);
     725             :   /* independent over R ? */
     726        2484 :   if (lx == 3 && real_indep(re,im,bit)) { avma = av; return cgetg(1, t_COL); }
     727        2478 :   if (gequal0(im)) im = NULL;
     728        2478 :   ly = im? lx+2: lx+1;
     729        2478 :   M = cgetg(lx,t_MAT);
     730        9954 :   for (i=1; i<lx; i++)
     731             :   {
     732        7476 :     GEN c = cgetg(ly,t_COL); gel(M,i) = c;
     733        7476 :     for (j=1; j<lx; j++) gel(c,j) = (i==j)? gen_1: gen_0;
     734        7476 :     gel(c,lx)           = gtrunc2n(gel(re,i), bit);
     735        7476 :     if (im) gel(c,lx+1) = gtrunc2n(gel(im,i), bit);
     736             :   }
     737        2478 :   M = ZM_lll(M, 0.99, LLL_INPLACE);
     738        2478 :   M = gel(M,1);
     739        2478 :   M[0] = evaltyp(t_COL) | evallg(lx);
     740        2478 :   return gerepilecopy(av, M);
     741             : }
     742             : /* deprecated */
     743             : GEN
     744          90 : lindep2(GEN x, long dig)
     745             : {
     746             :   long bit;
     747          90 :   if (dig < 0) pari_err_DOMAIN("lindep2", "accuracy", "<", gen_0, stoi(dig));
     748          90 :   if (dig) bit = (long) (dig/LOG10_2);
     749             :   else
     750             :   {
     751          78 :     bit = gprecision(x);
     752          78 :     if (!bit)
     753             :     {
     754          30 :       x = Q_primpart(x); /* left on stack */
     755          30 :       bit = 32 + gexpo(x);
     756             :     }
     757             :     else
     758          48 :       bit = (long)prec2nbits_mul(bit, 0.8);
     759             :   }
     760          90 :   return lindep_bit(x, bit);
     761             : }
     762             : 
     763             : /* x is a vector of elts of a p-adic field */
     764             : GEN
     765          12 : lindep_padic(GEN x)
     766             : {
     767          12 :   long i, j, prec = LONG_MAX, nx = lg(x)-1, v;
     768          12 :   pari_sp av = avma;
     769          12 :   GEN p = NULL, pn, m, a;
     770             : 
     771          12 :   if (nx < 2) return cgetg(1,t_COL);
     772          42 :   for (i=1; i<=nx; i++)
     773             :   {
     774          30 :     GEN c = gel(x,i), q;
     775          30 :     if (typ(c) != t_PADIC) continue;
     776             : 
     777          18 :     j = precp(c); if (j < prec) prec = j;
     778          18 :     q = gel(c,2);
     779          18 :     if (!p) p = q; else if (!equalii(p, q)) pari_err_MODULUS("lindep_padic", p, q);
     780             :   }
     781          12 :   if (!p) pari_err_TYPE("lindep_padic [not a p-adic vector]",x);
     782          12 :   v = gvaluation(x,p); pn = powiu(p,prec);
     783          12 :   if (v) x = gmul(x, powis(p, -v));
     784          12 :   x = RgV_to_FpV(x, pn);
     785             : 
     786          12 :   a = negi(gel(x,1));
     787          12 :   m = cgetg(nx,t_MAT);
     788          30 :   for (i=1; i<nx; i++)
     789             :   {
     790          18 :     GEN c = zerocol(nx);
     791          18 :     gel(c,1+i) = a;
     792          18 :     gel(c,1) = gel(x,i+1);
     793          18 :     gel(m,i) = c;
     794             :   }
     795          12 :   m = ZM_lll(ZM_hnfmodid(m, pn), 0.99, LLL_INPLACE);
     796          12 :   return gerepilecopy(av, gel(m,1));
     797             : }
     798             : /* x is a vector of t_POL/t_SER */
     799             : GEN
     800          36 : lindep_Xadic(GEN x)
     801             : {
     802          36 :   long i, prec = LONG_MAX, deg = 0, lx = lg(x), vx, v;
     803          36 :   pari_sp av = avma;
     804             :   GEN m;
     805             : 
     806          36 :   if (lx == 1) return cgetg(1,t_COL);
     807          36 :   vx = gvar(x);
     808          36 :   v = gvaluation(x, pol_x(vx));
     809          36 :   if (!v)         x = shallowcopy(x);
     810           0 :   else if (v > 0) x = gdiv(x, pol_xn(v, vx));
     811           0 :   else            x = gmul(x, pol_xn(-v, vx));
     812             :   /* all t_SER have valuation >= 0 */
     813         264 :   for (i=1; i<lx; i++)
     814             :   {
     815         228 :     GEN c = gel(x,i);
     816         228 :     if (gvar(c) != vx) { gel(x,i) = scalarpol_shallow(c, vx); continue; }
     817         222 :     switch(typ(c))
     818             :     {
     819         108 :       case t_POL: deg = maxss(deg, degpol(c)); break;
     820           0 :       case t_RFRAC: pari_err_TYPE("lindep_Xadic", c);
     821             :       case t_SER:
     822         114 :         prec = minss(prec, valp(c)+lg(c)-2);
     823         114 :         gel(x,i) = ser2rfrac_i(c);
     824             :     }
     825             :   }
     826          36 :   if (prec == LONG_MAX) prec = deg+1;
     827          36 :   m = RgXV_to_RgM(x, prec);
     828          36 :   return gerepileupto(av, deplin(m));
     829             : }
     830             : static GEN
     831          30 : vec_lindep(GEN x)
     832             : {
     833          30 :   pari_sp av = avma;
     834          30 :   long i, l = lg(x); /* > 1 */
     835          30 :   long t = typ(gel(x,1)), h = lg(gel(x,1));
     836          30 :   GEN m = cgetg(l, t_MAT);
     837         108 :   for (i = 1; i < l; i++)
     838             :   {
     839          84 :     GEN y = gel(x,i);
     840          84 :     if (lg(y) != h || typ(y) != t) pari_err_TYPE("lindep",x);
     841          78 :     if (t != t_COL) y = shallowtrans(y); /* Sigh */
     842          78 :     gel(m,i) = y;
     843             :   }
     844          24 :   return gerepileupto(av, deplin(m));
     845             : }
     846             : 
     847             : GEN
     848           0 : lindep(GEN x) { return lindep2(x, 0); }
     849             : 
     850             : GEN
     851         288 : lindep0(GEN x,long bit)
     852             : {
     853         288 :   long i, tx = typ(x);
     854         288 :   if (tx == t_MAT) return deplin(x);
     855         114 :   if (! is_vec_t(tx)) pari_err_TYPE("lindep",x);
     856         342 :   for (i = 1; i < lg(x); i++)
     857         276 :     switch(typ(gel(x,i)))
     858             :     {
     859           6 :       case t_PADIC: return lindep_padic(x);
     860             :       case t_POL:
     861             :       case t_RFRAC:
     862          12 :       case t_SER: return lindep_Xadic(x);
     863             :       case t_VEC:
     864          30 :       case t_COL: return vec_lindep(x);
     865             :     }
     866          66 :   return lindep2(x, bit);
     867             : }
     868             : 
     869             : GEN
     870          42 : algdep0(GEN x, long n, long bit)
     871             : {
     872          42 :   long tx = typ(x), i;
     873             :   pari_sp av;
     874             :   GEN y;
     875             : 
     876          42 :   if (! is_scalar_t(tx)) pari_err_TYPE("algdep0",x);
     877          42 :   if (tx==t_POLMOD) { y = RgX_copy(gel(x,1)); setvarn(y,0); return y; }
     878          42 :   if (gequal0(x)) return pol_x(0);
     879          42 :   if (n <= 0)
     880             :   {
     881          12 :     if (!n) return gen_1;
     882           6 :     pari_err_DOMAIN("algdep", "degree", "<", gen_0, stoi(n));
     883             :   }
     884             : 
     885          30 :   av = avma; y = cgetg(n+2,t_COL);
     886          30 :   gel(y,1) = gen_1;
     887          30 :   gel(y,2) = x; /* n >= 1 */
     888          30 :   for (i=3; i<=n+1; i++) gel(y,i) = gmul(gel(y,i-1),x);
     889          30 :   if (typ(x) == t_PADIC)
     890           6 :     y = lindep_padic(y);
     891             :   else
     892          24 :     y = lindep2(y, bit);
     893          30 :   if (lg(y) == 1) pari_err(e_DOMAIN,"algdep", "degree(x)",">", stoi(n), x);
     894          30 :   y = RgV_to_RgX(y, 0);
     895          30 :   if (signe(leading_coeff(y)) > 0) return gerepilecopy(av, y);
     896           0 :   return gerepileupto(av, ZX_neg(y));
     897             : }
     898             : 
     899             : GEN
     900           0 : algdep(GEN x, long n)
     901             : {
     902           0 :   return algdep0(x,n,0);
     903             : }
     904             : 
     905             : GEN
     906          24 : seralgdep(GEN s, long p, long r)
     907             : {
     908          24 :   pari_sp av = avma;
     909             :   long vy, i, m, n, prec;
     910             :   GEN S, v, D;
     911             : 
     912          24 :   if (typ(s) != t_SER) pari_err_TYPE("seralgdep",s);
     913          24 :   if (p <= 0) pari_err_DOMAIN("seralgdep", "p", "<=", gen_0, stoi(p));
     914          24 :   if (r < 0) pari_err_DOMAIN("seralgdep", "r", "<", gen_0, stoi(r));
     915          24 :   if (is_bigint(addiu(muluu(p, r), 1))) pari_err_OVERFLOW("seralgdep");
     916          24 :   vy = varn(s);
     917          24 :   if (!vy) pari_err_PRIORITY("seralgdep", s, ">", 0);
     918          24 :   r++; p++;
     919          24 :   prec = valp(s) + lg(s)-2;
     920          24 :   if (r > prec) r = prec;
     921          24 :   S = cgetg(p+1, t_VEC); gel(S, 1) = s;
     922          24 :   for (i = 2; i <= p; i++) gel(S,i) = gmul(gel(S,i-1), s);
     923          24 :   v = cgetg(r*p+1, t_VEC); /* v[r*n+m+1] = s^n * y^m */
     924             :   /* n = 0 */
     925          24 :   for (m = 0; m < r; m++) gel(v, m+1) = pol_xn(m, vy);
     926          60 :   for(n=1; n < p; n++)
     927         150 :     for (m = 0; m < r; m++)
     928             :     {
     929         114 :       GEN c = gel(S,n);
     930         114 :       if (m)
     931             :       {
     932          78 :         c = shallowcopy(c);
     933          78 :         setvalp(c, valp(c) + m);
     934             :       }
     935         114 :       gel(v, r*n + m + 1) = c;
     936             :     }
     937          24 :   D = lindep_Xadic(v);
     938          24 :   if (lg(D) == 1) { avma = av; return gen_0; }
     939          18 :   v = cgetg(p+1, t_VEC);
     940          60 :   for (n = 0; n < p; n++)
     941          42 :     gel(v, n+1) = RgV_to_RgX(vecslice(D, r*n+1, r*n+r), vy);
     942          18 :   return gerepilecopy(av, RgV_to_RgX(v, 0));
     943             : }
     944             : 
     945             : /********************************************************************/
     946             : /**                                                                **/
     947             : /**                              MINIM                             **/
     948             : /**                                                                **/
     949             : /********************************************************************/
     950             : void
     951       16548 : minim_alloc(long n, double ***q, GEN *x, double **y,  double **z, double **v)
     952             : {
     953             :   long i, s;
     954             : 
     955       16548 :   *x = cgetg(n, t_VECSMALL);
     956       16548 :   *q = (double**) new_chunk(n);
     957       16548 :   s = n * sizeof(double);
     958       16548 :   *y = (double*) stack_malloc_align(s, sizeof(double));
     959       16548 :   *z = (double*) stack_malloc_align(s, sizeof(double));
     960       16548 :   *v = (double*) stack_malloc_align(s, sizeof(double));
     961       16548 :   for (i=1; i<n; i++) (*q)[i] = (double*) stack_malloc_align(s, sizeof(double));
     962       16548 : }
     963             : 
     964             : static GEN
     965      173682 : ZC_canon(GEN V)
     966             : {
     967      173682 :   long l = lg(V), j;
     968      173682 :   for (j = 1; j < l  &&  signe(gel(V,j)) == 0; ++j);
     969      173682 :   return (j < l  &&  signe(gel(V,j)) < 0)? ZC_neg(V): V;
     970             : }
     971             : 
     972             : static GEN
     973      173682 : ZM_zc_mul_canon(GEN u, GEN x)
     974             : {
     975      173682 :   return ZC_canon(ZM_zc_mul(u,x));
     976             : }
     977             : 
     978             : struct qfvec
     979             : {
     980             :   GEN a, r, u;
     981             : };
     982             : 
     983             : static void
     984           0 : err_minim(GEN a)
     985             : {
     986           0 :   pari_err_DOMAIN("minim0","form","is not",
     987             :                   strtoGENstr("positive definite"),a);
     988           0 : }
     989             : 
     990             : static GEN
     991         480 : minim_lll(GEN a, GEN *u)
     992             : {
     993         480 :   *u = lllgramint(a);
     994         480 :   if (lg(*u) != lg(a)) err_minim(a);
     995         480 :   return qf_apply_ZM(a,*u);
     996             : }
     997             : 
     998             : static void
     999         480 : forqfvec_init_dolll(struct qfvec *qv, GEN a, long dolll)
    1000             : {
    1001             :   GEN r, u;
    1002         480 :   if (!dolll) u = NULL;
    1003             :   else
    1004             :   {
    1005         444 :     if (typ(a) != t_MAT || !RgM_is_ZM(a)) pari_err_TYPE("qfminim",a);
    1006         444 :     a = minim_lll(a, &u);
    1007             :   }
    1008         480 :   qv->a = RgM_gtofp(a, DEFAULTPREC);
    1009         480 :   r = qfgaussred_positive(qv->a);
    1010         480 :   if (!r)
    1011             :   {
    1012           0 :     r = qfgaussred_positive(a); /* exact computation */
    1013           0 :     if (!r) err_minim(a);
    1014           0 :     r = RgM_gtofp(r, DEFAULTPREC);
    1015             :   }
    1016         480 :   qv->r = r;
    1017         480 :   qv->u = u;
    1018         480 : }
    1019             : 
    1020             : static void
    1021          18 : forqfvec_init(struct qfvec *qv, GEN a)
    1022          18 : { forqfvec_init_dolll(qv, a, 1); }
    1023             : 
    1024             : static void
    1025          18 : forqfvec_i(void *E, long (*fun)(void *, GEN, GEN, double), struct qfvec *qv, GEN BORNE)
    1026             : {
    1027          18 :   GEN x, a = qv->a, r = qv->r, u = qv->u;
    1028          18 :   long n = lg(a), i, j, k;
    1029             :   double p,BOUND,*v,*y,*z,**q;
    1030          18 :   const double eps = 0.0001;
    1031          18 :   if (!BORNE) BORNE = gen_0;
    1032             :   else
    1033             :   {
    1034          12 :     BORNE = gfloor(BORNE);
    1035          12 :     if (typ(BORNE) != t_INT) pari_err_TYPE("minim0",BORNE);
    1036             :   }
    1037          36 :   if (n == 1) return;
    1038          12 :   minim_alloc(n, &q, &x, &y, &z, &v);
    1039          12 :   n--;
    1040          36 :   for (j=1; j<=n; j++)
    1041             :   {
    1042          24 :     v[j] = rtodbl(gcoeff(r,j,j));
    1043          24 :     for (i=1; i<j; i++) q[i][j] = rtodbl(gcoeff(r,i,j));
    1044             :   }
    1045             : 
    1046          12 :   if (gequal0(BORNE))
    1047             :   {
    1048             :     double c;
    1049           6 :     p = rtodbl(gcoeff(a,1,1));
    1050           6 :     for (i=2; i<=n; i++) { c = rtodbl(gcoeff(a,i,i)); if (c < p) p = c; }
    1051           6 :     BORNE = roundr(dbltor(p));
    1052             :   }
    1053             :   else
    1054           6 :     p = gtodouble(BORNE);
    1055          12 :   BOUND = p * (1 + eps);
    1056          12 :   if (BOUND == p) pari_err_PREC("minim0");
    1057             : 
    1058          12 :   k = n; y[n] = z[n] = 0;
    1059          12 :   x[n] = (long)sqrt(BOUND/v[n]);
    1060          24 :   for(;;x[1]--)
    1061             :   {
    1062             :     do
    1063             :     {
    1064          42 :       if (k>1)
    1065             :       {
    1066          18 :         long l = k-1;
    1067          18 :         z[l] = 0;
    1068          18 :         for (j=k; j<=n; j++) z[l] += q[l][j]*x[j];
    1069          18 :         p = (double)x[k] + z[k];
    1070          18 :         y[l] = y[k] + p*p*v[k];
    1071          18 :         x[l] = (long)floor(sqrt((BOUND-y[l])/v[l])-z[l]);
    1072          18 :         k = l;
    1073             :       }
    1074             :       for(;;)
    1075             :       {
    1076          48 :         p = (double)x[k] + z[k];
    1077          48 :         if (y[k] + p*p*v[k] <= BOUND) break;
    1078           6 :         k++; x[k]--;
    1079           6 :       }
    1080          42 :     } while (k > 1);
    1081          36 :     if (! x[1] && y[1]<=eps) break;
    1082             : 
    1083          24 :     p = (double)x[1] + z[1]; p = y[1] + p*p*v[1]; /* norm(x) */
    1084          24 :     if (fun(E, u, x, p)) break;
    1085          24 :   }
    1086             : }
    1087             : 
    1088             : void
    1089           0 : forqfvec(void *E, long (*fun)(void *, GEN, GEN, double), GEN a, GEN BORNE)
    1090             : {
    1091           0 :   pari_sp av = avma;
    1092             :   struct qfvec qv;
    1093           0 :   forqfvec_init(&qv, a);
    1094           0 :   forqfvec_i(E, fun, &qv, BORNE);
    1095           0 :   avma = av;
    1096           0 : }
    1097             : 
    1098             : static long
    1099          24 : _gp_forqf(void *E, GEN u, GEN x, double p/*unused*/)
    1100             : {
    1101          24 :   pari_sp av = avma;
    1102             :   (void)p;
    1103          24 :   set_lex(-1, ZM_zc_mul_canon(u, x));
    1104          24 :   closure_evalvoid((GEN)E);
    1105          24 :   avma = av;
    1106          24 :   return loop_break();
    1107             : }
    1108             : 
    1109             : void
    1110          18 : forqfvec0(GEN a, GEN BORNE, GEN code)
    1111             : {
    1112          18 :   pari_sp av = avma;
    1113             :   struct qfvec qv;
    1114          18 :   forqfvec_init(&qv, a);
    1115          18 :   push_lex(gen_0, code);
    1116          18 :   forqfvec_i((void*) code, &_gp_forqf, &qv, BORNE);
    1117          18 :   pop_lex(1);
    1118          18 :   avma = av;
    1119          18 : }
    1120             : 
    1121             : enum { min_ALL = 0, min_FIRST, min_VECSMALL, min_VECSMALL2 };
    1122             : 
    1123             : /* Minimal vectors for the integral definite quadratic form: a.
    1124             :  * Result u:
    1125             :  *   u[1]= Number of vectors of square norm <= BORNE
    1126             :  *   u[2]= maximum norm found
    1127             :  *   u[3]= list of vectors found (at most STOCKMAX, unless NULL)
    1128             :  *
    1129             :  *  If BORNE = NULL: Minimal non-zero vectors.
    1130             :  *  flag = min_ALL,   as above
    1131             :  *  flag = min_FIRST, exits when first suitable vector is found.
    1132             :  *  flag = min_VECSMALL, return a t_VECSMALL of (half) the number of vectors
    1133             :  *  for each norm
    1134             :  *  flag = min_VECSMALL2, same but count only vectors with even norm, and shift
    1135             :  *  the answer */
    1136             : static GEN
    1137         480 : minim0_dolll(GEN a, GEN BORNE, GEN STOCKMAX, long flag, long dolll)
    1138             : {
    1139             :   GEN x, u, r, L, gnorme;
    1140         480 :   long n = lg(a), i, j, k, s, maxrank, sBORNE;
    1141         480 :   pari_sp av = avma, av1;
    1142             :   double p,maxnorm,BOUND,*v,*y,*z,**q;
    1143         480 :   const double eps = 1e-10;
    1144         480 :   int stockall = 0;
    1145             :   struct qfvec qv;
    1146             : 
    1147         480 :   if (!BORNE)
    1148          48 :     sBORNE = 0;
    1149             :   else
    1150             :   {
    1151         432 :     BORNE = gfloor(BORNE);
    1152         432 :     if (typ(BORNE) != t_INT) pari_err_TYPE("minim0",BORNE);
    1153         432 :     if (is_bigint(BORNE)) pari_err_PREC( "qfminim");
    1154         432 :     sBORNE = itos(BORNE); avma = av;
    1155             :   }
    1156         480 :   if (!STOCKMAX)
    1157             :   {
    1158         204 :     stockall = 1;
    1159         204 :     maxrank = 200;
    1160             :   }
    1161             :   else
    1162             :   {
    1163         276 :     STOCKMAX = gfloor(STOCKMAX);
    1164         276 :     if (typ(STOCKMAX) != t_INT) pari_err_TYPE("minim0",STOCKMAX);
    1165         276 :     maxrank = itos(STOCKMAX);
    1166         276 :     if (maxrank < 0)
    1167           0 :       pari_err_TYPE("minim0 [negative number of vectors]",STOCKMAX);
    1168             :   }
    1169             : 
    1170         480 :   switch(flag)
    1171             :   {
    1172             :     case min_VECSMALL:
    1173             :     case min_VECSMALL2:
    1174         234 :       if (sBORNE <= 0) return cgetg(1, t_VECSMALL);
    1175         234 :       L = zero_zv(sBORNE);
    1176         234 :       if (flag == min_VECSMALL2) sBORNE <<= 1;
    1177         234 :       if (n == 1) return L;
    1178         234 :       break;
    1179             :     case min_FIRST:
    1180          30 :       if (n == 1 || (!sBORNE && BORNE)) return cgetg(1,t_VEC);
    1181          18 :       L = NULL; /* gcc -Wall */
    1182          18 :       break;
    1183             :     case min_ALL:
    1184         216 :       if (n == 1 || (!sBORNE && BORNE))
    1185           6 :         retmkvec3(gen_0, gen_0, cgetg(1, t_MAT));
    1186         210 :       L = new_chunk(1+maxrank);
    1187         210 :       break;
    1188             :     default:
    1189           0 :       return NULL;
    1190             :   }
    1191         462 :   minim_alloc(n, &q, &x, &y, &z, &v);
    1192             : 
    1193         462 :   forqfvec_init_dolll(&qv, a, dolll);
    1194         462 :   av1 = avma;
    1195         462 :   r = qv.r;
    1196         462 :   u = qv.u;
    1197         462 :   n--;
    1198        4044 :   for (j=1; j<=n; j++)
    1199             :   {
    1200        3582 :     v[j] = rtodbl(gcoeff(r,j,j));
    1201        3582 :     for (i=1; i<j; i++) q[i][j] = rtodbl(gcoeff(r,i,j)); /* |.| <= 1/2 */
    1202             :   }
    1203             : 
    1204         462 :   if (sBORNE) maxnorm = 0.;
    1205             :   else
    1206             :   {
    1207          48 :     GEN B = gcoeff(a,1,1);
    1208          48 :     long t = 1;
    1209         528 :     for (i=2; i<=n; i++)
    1210             :     {
    1211         480 :       GEN c = gcoeff(a,i,i);
    1212         480 :       if (cmpii(c, B) < 0) { B = c; t = i; }
    1213             :     }
    1214          48 :     if (flag == min_FIRST) return gerepilecopy(av, mkvec2(B, gel(u,t)));
    1215          42 :     maxnorm = -1.; /* don't update maxnorm */
    1216          42 :     if (is_bigint(B)) return NULL;
    1217          42 :     sBORNE = itos(B);
    1218             :   }
    1219         456 :   BOUND = sBORNE * (1 + eps);
    1220         456 :   if ((long)BOUND != sBORNE) return NULL;
    1221             : 
    1222         444 :   s = 0;
    1223         444 :   k = n; y[n] = z[n] = 0;
    1224         444 :   x[n] = (long)sqrt(BOUND/v[n]);
    1225      882798 :   for(;;x[1]--)
    1226             :   {
    1227             :     do
    1228             :     {
    1229     1637568 :       if (k>1)
    1230             :       {
    1231      754764 :         long l = k-1;
    1232      754764 :         z[l] = 0;
    1233      754764 :         for (j=k; j<=n; j++) z[l] += q[l][j]*x[j];
    1234      754764 :         p = (double)x[k] + z[k];
    1235      754764 :         y[l] = y[k] + p*p*v[k];
    1236      754764 :         x[l] = (long)floor(sqrt((BOUND-y[l])/v[l])-z[l]);
    1237      754764 :         k = l;
    1238             :       }
    1239             :       for(;;)
    1240             :       {
    1241     2389374 :         p = (double)x[k] + z[k];
    1242     2389374 :         if (y[k] + p*p*v[k] <= BOUND) break;
    1243      751806 :         k++; x[k]--;
    1244      751806 :       }
    1245             :     }
    1246     1637568 :     while (k > 1);
    1247      883242 :     if (! x[1] && y[1]<=eps) break;
    1248             : 
    1249      882804 :     p = (double)x[1] + z[1]; p = y[1] + p*p*v[1]; /* norm(x) */
    1250      882804 :     if (maxnorm >= 0)
    1251             :     {
    1252      880620 :       if (p > maxnorm) maxnorm = p;
    1253             :     }
    1254             :     else
    1255             :     { /* maxnorm < 0 : only look for minimal vectors */
    1256        2184 :       pari_sp av2 = avma;
    1257        2184 :       gnorme = roundr(dbltor(p));
    1258        2184 :       if (cmpis(gnorme, sBORNE) >= 0) avma = av2;
    1259             :       else
    1260             :       {
    1261          12 :         sBORNE = itos(gnorme); avma = av1;
    1262          12 :         BOUND = sBORNE * (1+eps);
    1263          12 :         L = new_chunk(maxrank+1);
    1264          12 :         s = 0;
    1265             :       }
    1266             :     }
    1267      882804 :     s++;
    1268             : 
    1269      882804 :     switch(flag)
    1270             :     {
    1271             :       case min_FIRST:
    1272           6 :         if (dolll) x = ZM_zc_mul_canon(u,x);
    1273           6 :         return gerepilecopy(av, mkvec2(roundr(dbltor(p)), x));
    1274             : 
    1275             :       case min_ALL:
    1276      175740 :         if (s > maxrank && stockall) /* overflow */
    1277             :         {
    1278         348 :           long maxranknew = maxrank << 1;
    1279         348 :           GEN Lnew = new_chunk(1 + maxranknew);
    1280         348 :           for (i=1; i<=maxrank; i++) Lnew[i] = L[i];
    1281         348 :           L = Lnew; maxrank = maxranknew;
    1282             :         }
    1283      175740 :         if (s<=maxrank) gel(L,s) = leafcopy(x);
    1284      175740 :         break;
    1285             : 
    1286             :       case min_VECSMALL:
    1287       33600 :         { ulong norm = (ulong)(p + 0.5); L[norm]++; }
    1288       33600 :         break;
    1289             : 
    1290             :       case min_VECSMALL2:
    1291      673458 :         { ulong norm = (ulong)(p + 0.5); if (!odd(norm)) L[norm>>1]++; }
    1292      673458 :         break;
    1293             : 
    1294             :     }
    1295      882798 :   }
    1296         438 :   switch(flag)
    1297             :   {
    1298             :     case min_FIRST:
    1299           6 :       avma = av; return cgetg(1,t_VEC);
    1300             :     case min_VECSMALL:
    1301             :     case min_VECSMALL2:
    1302         234 :       avma = (pari_sp)L; return L;
    1303             :   }
    1304         198 :   r = (maxnorm >= 0) ? roundr(dbltor(maxnorm)): stoi(sBORNE);
    1305         198 :   k = minss(s,maxrank);
    1306         198 :   L[0] = evaltyp(t_MAT) | evallg(k + 1);
    1307         198 :   if (dolll)
    1308         168 :     for (j=1; j<=k; j++) gel(L,j) = ZM_zc_mul_canon(u, gel(L,j));
    1309         198 :   return gerepilecopy(av, mkvec3(stoi(s<<1), r, L));
    1310             : }
    1311             : 
    1312             : static GEN
    1313         444 : minim0(GEN a, GEN BORNE, GEN STOCKMAX, long flag)
    1314             : {
    1315         444 :   GEN v = minim0_dolll(a, BORNE, STOCKMAX, flag, 1);
    1316         444 :   if (!v) pari_err_PREC("qfminim");
    1317         438 :   return v;
    1318             : }
    1319             : 
    1320             : GEN
    1321         234 : qfrep0(GEN a, GEN borne, long flag)
    1322         234 : { return minim0(a, borne, gen_0, (flag & 1)? min_VECSMALL2: min_VECSMALL); }
    1323             : 
    1324             : GEN
    1325          90 : qfminim0(GEN a, GEN borne, GEN stockmax, long flag, long prec)
    1326             : {
    1327          90 :   switch(flag)
    1328             :   {
    1329          36 :     case 0: return minim0(a,borne,stockmax,min_ALL);
    1330          30 :     case 1: return minim0(a,borne,gen_0   ,min_FIRST);
    1331             :     case 2:
    1332             :     {
    1333          24 :       long maxnum = -1;
    1334          24 :       if (typ(a) != t_MAT) pari_err_TYPE("qfminim",a);
    1335          24 :       if (stockmax) {
    1336          12 :         if (typ(stockmax) != t_INT) pari_err_TYPE("qfminim",stockmax);
    1337          12 :         maxnum = itos(stockmax);
    1338             :       }
    1339          24 :       a = fincke_pohst(a,borne,maxnum,prec,NULL);
    1340          24 :       if (!a) pari_err_PREC("qfminim");
    1341          24 :       return a;
    1342             :     }
    1343           0 :     default: pari_err_FLAG("qfminim");
    1344             :   }
    1345             :   return NULL; /* LCOV_EXCL_LINE */
    1346             : }
    1347             : 
    1348             : GEN
    1349         144 : minim(GEN a, GEN borne, GEN stockmax)
    1350         144 : { return minim0(a,borne,stockmax,min_ALL); }
    1351             : 
    1352             : GEN
    1353          36 : minim_raw(GEN a, GEN BORNE, GEN STOCKMAX)
    1354          36 : { return minim0_dolll(a, BORNE, STOCKMAX, min_ALL, 0); }
    1355             : 
    1356             : GEN
    1357           0 : minim2(GEN a, GEN borne, GEN stockmax)
    1358           0 : { return minim0(a,borne,stockmax,min_FIRST); }
    1359             : 
    1360             : /* If V depends linearly from the columns of the matrix, return 0.
    1361             :  * Otherwise, update INVP and L and return 1. No GC. */
    1362             : static int
    1363        1416 : addcolumntomatrix(GEN V, GEN invp, GEN L)
    1364             : {
    1365        1416 :   long i,j,k, n = lg(invp);
    1366        1416 :   GEN a = cgetg(n, t_COL), ak = NULL, mak;
    1367             : 
    1368       72198 :   for (k = 1; k < n; k++)
    1369       71748 :     if (!L[k])
    1370             :     {
    1371       23838 :       ak = RgMrow_zc_mul(invp, V, k);
    1372       23838 :       if (!gequal0(ak)) break;
    1373             :     }
    1374        1416 :   if (k == n) return 0;
    1375         966 :   L[k] = 1;
    1376         966 :   mak = gneg_i(ak);
    1377       37074 :   for (i=k+1; i<n; i++)
    1378       36108 :     gel(a,i) = gdiv(RgMrow_zc_mul(invp, V, i), mak);
    1379       37614 :   for (j=1; j<=k; j++)
    1380             :   {
    1381       36648 :     GEN c = gel(invp,j), ck = gel(c,k);
    1382       36648 :     if (gequal0(ck)) continue;
    1383        8118 :     gel(c,k) = gdiv(ck, ak);
    1384        8118 :     if (j==k)
    1385       37074 :       for (i=k+1; i<n; i++)
    1386       36108 :         gel(c,i) = gmul(gel(a,i), ck);
    1387             :     else
    1388      179982 :       for (i=k+1; i<n; i++)
    1389      172830 :         gel(c,i) = gadd(gel(c,i), gmul(gel(a,i), ck));
    1390             :   }
    1391         966 :   return 1;
    1392             : }
    1393             : 
    1394             : GEN
    1395          36 : perf(GEN a)
    1396             : {
    1397          36 :   pari_sp av = avma;
    1398             :   GEN u, L;
    1399          36 :   long r, s, k, l, n = lg(a)-1;
    1400             : 
    1401          36 :   if (!n) return gen_0;
    1402          36 :   if (typ(a) != t_MAT || !RgM_is_ZM(a)) pari_err_TYPE("qfperfection",a);
    1403          36 :   a = minim_lll(a, &u);
    1404          36 :   L = minim_raw(a,NULL,NULL);
    1405          36 :   r = (n*(n+1)) >> 1;
    1406          36 :   if (L)
    1407             :   {
    1408             :     GEN D, V, invp;
    1409          30 :     L = gel(L, 3); l = lg(L);
    1410          30 :     if (l == 2) { avma = av; return gen_1; }
    1411             : 
    1412          18 :     D = zero_zv(r);
    1413          18 :     V = cgetg(r+1, t_VECSMALL);
    1414          18 :     invp = matid(r);
    1415          18 :     s = 0;
    1416        1422 :     for (k = 1; k < l; k++)
    1417             :     {
    1418        1416 :       pari_sp av2 = avma;
    1419        1416 :       GEN x = gel(L,k);
    1420             :       long i, j, I;
    1421       18084 :       for (i = I = 1; i<=n; i++)
    1422       16668 :         for (j=i; j<=n; j++,I++) V[I] = x[i]*x[j];
    1423        1416 :       if (!addcolumntomatrix(V,invp,D)) avma = av2;
    1424         966 :       else if (++s == r) break;
    1425             :     }
    1426             :   }
    1427             :   else
    1428             :   {
    1429             :     GEN M;
    1430           6 :     L = fincke_pohst(a,NULL,-1, DEFAULTPREC, NULL);
    1431           6 :     if (!L) pari_err_PREC("qfminim");
    1432           6 :     L = gel(L, 3); l = lg(L);
    1433           6 :     if (l == 2) { avma = av; return gen_1; }
    1434           6 :     M = cgetg(l, t_MAT);
    1435         822 :     for (k = 1; k < l; k++)
    1436             :     {
    1437         816 :       GEN x = gel(L,k), c = cgetg(r+1, t_COL);
    1438             :       long i, I, j;
    1439       13872 :       for (i = I = 1; i<=n; i++)
    1440       13056 :         for (j=i; j<=n; j++,I++) gel(c,I) = mulii(gel(x,i), gel(x,j));
    1441         816 :       gel(M,k) = c;
    1442             :     }
    1443           6 :     s = ZM_rank(M);
    1444             :   }
    1445          24 :  avma = av; return utoipos(s);
    1446             : }
    1447             : 
    1448             : static GEN
    1449          54 : clonefill(GEN S, long s, long t)
    1450             : { /* initialize to dummy values */
    1451          54 :   GEN T = S, dummy = cgetg(1, t_STR);
    1452             :   long i;
    1453          54 :   for (i = s+1; i <= t; i++) gel(S,i) = dummy;
    1454          54 :   S = gclone(S); if (isclone(T)) gunclone(T);
    1455          54 :   return S;
    1456             : }
    1457             : 
    1458             : /* increment ZV x, by incrementing cell of index k. Initial value x0[k] was
    1459             :  * chosen to minimize qf(x) for given x0[1..k-1] and x0[k+1,..] = 0
    1460             :  * The last non-zero entry must be positive and goes through x0[k]+1,2,3,...
    1461             :  * Others entries go through: x0[k]+1,-1,2,-2,...*/
    1462             : INLINE void
    1463      784542 : step(GEN x, GEN y, GEN inc, long k)
    1464             : {
    1465      784542 :   if (!signe(gel(y,k))) /* x[k+1..] = 0 */
    1466        7698 :     gel(x,k) = addiu(gel(x,k), 1); /* leading coeff > 0 */
    1467             :   else
    1468             :   {
    1469      776844 :     long i = inc[k];
    1470      776844 :     gel(x,k) = addis(gel(x,k), i),
    1471      776844 :     inc[k] = (i > 0)? -1-i: 1-i;
    1472             :   }
    1473      784542 : }
    1474             : 
    1475             : /* 1 if we are "sure" that x < y, up to few rounding errors, i.e.
    1476             :  * x < y - epsilon. More precisely :
    1477             :  * - sign(x - y) < 0
    1478             :  * - lgprec(x-y) > 3 || expo(x - y) - expo(x) > -24 */
    1479             : static int
    1480      308022 : mplessthan(GEN x, GEN y)
    1481             : {
    1482      308022 :   pari_sp av = avma;
    1483      308022 :   GEN z = mpsub(x, y);
    1484      308022 :   avma = av;
    1485      308022 :   if (typ(z) == t_INT) return (signe(z) < 0);
    1486      308022 :   if (signe(z) >= 0) return 0;
    1487       50364 :   if (realprec(z) > LOWDEFAULTPREC) return 1;
    1488       50364 :   return ( expo(z) - mpexpo(x) > -24 );
    1489             : }
    1490             : 
    1491             : /* 1 if we are "sure" that x > y, up to few rounding errors, i.e.
    1492             :  * x > y + epsilon */
    1493             : static int
    1494     1241958 : mpgreaterthan(GEN x, GEN y)
    1495             : {
    1496     1241958 :   pari_sp av = avma;
    1497     1241958 :   GEN z = mpsub(x, y);
    1498     1241958 :   avma = av;
    1499     1241958 :   if (typ(z) == t_INT) return (signe(z) > 0);
    1500     1241958 :   if (signe(z) <= 0) return 0;
    1501      702858 :   if (realprec(z) > LOWDEFAULTPREC) return 1;
    1502      133920 :   return ( expo(z) - mpexpo(x) > -24 );
    1503             : }
    1504             : 
    1505             : /* x a t_INT, y  t_INT or t_REAL */
    1506             : INLINE GEN
    1507      306702 : mulimp(GEN x, GEN y)
    1508             : {
    1509      306702 :   if (typ(y) == t_INT) return mulii(x,y);
    1510      306702 :   return signe(x) ? mulir(x,y): gen_0;
    1511             : }
    1512             : /* x + y*z, x,z two mp's, y a t_INT */
    1513             : INLINE GEN
    1514     3861990 : addmulimp(GEN x, GEN y, GEN z)
    1515             : {
    1516     3861990 :   if (!signe(y)) return x;
    1517     1670022 :   if (typ(z) == t_INT) return mpadd(x, mulii(y, z));
    1518     1670022 :   return mpadd(x, mulir(y, z));
    1519             : }
    1520             : 
    1521             : /* yk + vk * (xk + zk)^2 */
    1522             : static GEN
    1523     1544388 : norm_aux(GEN xk, GEN yk, GEN zk, GEN vk)
    1524             : {
    1525     1544388 :   GEN t = mpadd(xk, zk);
    1526     1544388 :   if (typ(t) == t_INT) { /* probably gen_0, avoid loss of accuracy */
    1527       14358 :     yk = addmulimp(yk, sqri(t), vk);
    1528             :   } else {
    1529     1530030 :     yk = mpadd(yk, mpmul(sqrr(t), vk));
    1530             :   }
    1531     1544388 :   return yk;
    1532             : }
    1533             : /* yk + vk * (xk + zk)^2 < B + epsilon */
    1534             : static int
    1535     1090950 : check_bound(GEN B, GEN xk, GEN yk, GEN zk, GEN vk)
    1536             : {
    1537     1090950 :   pari_sp av = avma;
    1538     1090950 :   int f = mpgreaterthan(norm_aux(xk,yk,zk,vk), B);
    1539     1090950 :   avma = av; return !f;
    1540             : }
    1541             : 
    1542             : /* q(k-th canonical basis vector), where q is given in Cholesky form
    1543             :  * q(x) = sum_{i = 1}^n q[i,i] (x[i] + sum_{j > i} q[i,j] x[j])^2.
    1544             :  * Namely q(e_k) = q[k,k] + sum_{i < k} q[i,i] q[i,k]^2
    1545             :  * Assume 1 <= k <= n. */
    1546             : static GEN
    1547         156 : cholesky_norm_ek(GEN q, long k)
    1548             : {
    1549         156 :   GEN t = gcoeff(q,k,k);
    1550             :   long i;
    1551         156 :   for (i = 1; i < k; i++) t = norm_aux(gen_0, t, gcoeff(q,i,k), gcoeff(q,i,i));
    1552         156 :   return t;
    1553             : }
    1554             : 
    1555             : /* q is the Cholesky decomposition of a quadratic form
    1556             :  * Enumerate vectors whose norm is less than BORNE (Algo 2.5.7),
    1557             :  * minimal vectors if BORNE = NULL (implies check = NULL).
    1558             :  * If (check != NULL) consider only vectors passing the check, and assumes
    1559             :  *   we only want the smallest possible vectors */
    1560             : static GEN
    1561         672 : smallvectors(GEN q, GEN BORNE, long maxnum, FP_chk_fun *CHECK)
    1562             : {
    1563         672 :   long N = lg(q), n = N-1, i, j, k, s, stockmax, checkcnt = 1;
    1564             :   pari_sp av, av1;
    1565             :   GEN inc, S, x, y, z, v, p1, alpha, norms;
    1566             :   GEN norme1, normax1, borne1, borne2;
    1567         672 :   GEN (*check)(void *,GEN) = CHECK? CHECK->f: NULL;
    1568         672 :   void *data = CHECK? CHECK->data: NULL;
    1569         672 :   const long skipfirst = CHECK? CHECK->skipfirst: 0;
    1570         672 :   const int stockall = (maxnum == -1);
    1571             : 
    1572         672 :   alpha = dbltor(0.95);
    1573         672 :   normax1 = gen_0;
    1574             : 
    1575         672 :   v = cgetg(N,t_VEC);
    1576         672 :   inc = const_vecsmall(n, 1);
    1577             : 
    1578         672 :   av = avma;
    1579         672 :   stockmax = stockall? 2000: maxnum;
    1580         672 :   norms = cgetg(check?(stockmax+1): 1,t_VEC); /* unused if (!check) */
    1581         672 :   S = cgetg(stockmax+1,t_VEC);
    1582         672 :   x = cgetg(N,t_COL);
    1583         672 :   y = cgetg(N,t_COL);
    1584         672 :   z = cgetg(N,t_COL);
    1585        4110 :   for (i=1; i<N; i++) {
    1586        3438 :     gel(v,i) = gcoeff(q,i,i);
    1587        3438 :     gel(x,i) = gel(y,i) = gel(z,i) = gen_0;
    1588             :   }
    1589         672 :   if (BORNE)
    1590             :   {
    1591         660 :     borne1 = BORNE;
    1592         660 :     if (typ(borne1) != t_REAL)
    1593             :     {
    1594             :       long prec;
    1595         366 :       if (gequal0(borne1)) retmkvec3(gen_0, gen_0, cgetg(1,t_MAT));
    1596         360 :       prec = nbits2prec(gexpo(borne1) + 10);
    1597         360 :       borne1 = gtofp(borne1, maxss(prec, DEFAULTPREC));
    1598             :     }
    1599             :   }
    1600             :   else
    1601             :   {
    1602          12 :     borne1 = gcoeff(q,1,1);
    1603         168 :     for (i=2; i<N; i++)
    1604             :     {
    1605         156 :       GEN b = cholesky_norm_ek(q, i);
    1606         156 :       if (gcmp(b, borne1) < 0) borne1 = b;
    1607             :     }
    1608             :     /* borne1 = norm of smallest basis vector */
    1609             :   }
    1610         666 :   borne2 = mulrr(borne1,alpha);
    1611         666 :   if (DEBUGLEVEL>2)
    1612           0 :     err_printf("smallvectors looking for norm < %P.4G\n",borne1);
    1613         666 :   s = 0; k = n;
    1614      145992 :   for(;; step(x,y,inc,k)) /* main */
    1615             :   { /* x (supposedly) small vector, ZV.
    1616             :      * For all t >= k, we have
    1617             :      *   z[t] = sum_{j > t} q[t,j] * x[j]
    1618             :      *   y[t] = sum_{i > t} q[i,i] * (x[i] + z[i])^2
    1619             :      *        = 0 <=> x[i]=0 for all i>t */
    1620             :     do
    1621             :     {
    1622      452694 :       int skip = 0;
    1623      452694 :       if (k > 1)
    1624             :       {
    1625      306702 :         long l = k-1;
    1626      306702 :         av1 = avma;
    1627      306702 :         p1 = mulimp(gel(x,k), gcoeff(q,l,k));
    1628      306702 :         for (j=k+1; j<N; j++) p1 = addmulimp(p1, gel(x,j), gcoeff(q,l,j));
    1629      306702 :         gel(z,l) = gerepileuptoleaf(av1,p1);
    1630             : 
    1631      306702 :         av1 = avma;
    1632      306702 :         p1 = norm_aux(gel(x,k), gel(y,k), gel(z,k), gel(v,k));
    1633      306702 :         gel(y,l) = gerepileuptoleaf(av1, p1);
    1634             :         /* skip the [x_1,...,x_skipfirst,0,...,0] */
    1635      306702 :         if ((l <= skipfirst && !signe(gel(y,skipfirst)))
    1636      306408 :          || mplessthan(borne1, gel(y,l))) skip = 1;
    1637             :         else /* initial value, minimizing (x[l] + z[l])^2, hence qf(x) for
    1638             :                 the given x[1..l-1] */
    1639      306408 :           gel(x,l) = mpround( mpneg(gel(z,l)) );
    1640      306702 :         k = l;
    1641             :       }
    1642      306702 :       for(;; step(x,y,inc,k))
    1643             :       { /* at most 2n loops */
    1644      759396 :         if (!skip)
    1645             :         {
    1646      759102 :           if (check_bound(borne1, gel(x,k),gel(y,k),gel(z,k),gel(v,k))) break;
    1647      331848 :           step(x,y,inc,k);
    1648      331848 :           if (check_bound(borne1, gel(x,k),gel(y,k),gel(z,k),gel(v,k))) break;
    1649             :         }
    1650      307368 :         skip = 0; inc[k] = 1;
    1651      307368 :         if (++k > n) goto END;
    1652      306702 :       }
    1653             : 
    1654      452028 :       if (gc_needed(av,2))
    1655             :       {
    1656           6 :         if(DEBUGMEM>1) pari_warn(warnmem,"smallvectors");
    1657           6 :         if (stockmax) S = clonefill(S, s, stockmax);
    1658           6 :         if (check) {
    1659           6 :           GEN dummy = cgetg(1, t_STR);
    1660           6 :           for (i=s+1; i<=stockmax; i++) gel(norms,i) = dummy;
    1661             :         }
    1662           6 :         gerepileall(av,7,&x,&y,&z,&normax1,&borne1,&borne2,&norms);
    1663             :       }
    1664             :     }
    1665      452028 :     while (k > 1);
    1666      145992 :     if (!signe(gel(x,1)) && !signe(gel(y,1))) continue; /* exclude 0 */
    1667             : 
    1668      145620 :     av1 = avma;
    1669      145620 :     norme1 = norm_aux(gel(x,1),gel(y,1),gel(z,1),gel(v,1));
    1670      145620 :     if (mpgreaterthan(norme1,borne1)) { avma = av1; continue; /* main */ }
    1671             : 
    1672      145620 :     norme1 = gerepileuptoleaf(av1,norme1);
    1673      145620 :     if (check)
    1674             :     {
    1675       86838 :       if (checkcnt < 5 && mpcmp(norme1, borne2) < 0)
    1676             :       {
    1677         366 :         if (!check(data,x)) { checkcnt++ ; continue; /* main */}
    1678         150 :         if (DEBUGLEVEL>4) err_printf("New bound: %Ps", norme1);
    1679         150 :         borne1 = norme1;
    1680         150 :         borne2 = mulrr(borne1, alpha);
    1681         150 :         s = 0; checkcnt = 0;
    1682             :       }
    1683             :     }
    1684             :     else
    1685             :     {
    1686       58782 :       if (!BORNE) /* find minimal vectors */
    1687             :       {
    1688        1614 :         if (mplessthan(norme1, borne1))
    1689             :         { /* strictly smaller vector than previously known */
    1690           0 :           borne1 = norme1; /* + epsilon */
    1691           0 :           s = 0;
    1692             :         }
    1693             :       }
    1694             :       else
    1695       57168 :         if (mpcmp(norme1,normax1) > 0) normax1 = norme1;
    1696             :     }
    1697      145404 :     if (++s > stockmax) continue; /* too many vectors: no longer remember */
    1698      144606 :     if (check) gel(norms,s) = norme1;
    1699      144606 :     gel(S,s) = leafcopy(x);
    1700      144606 :     if (s != stockmax) continue; /* still room, get next vector */
    1701             : 
    1702             :     /* overflow, eliminate vectors failing "check" */
    1703          48 :     if (check)
    1704             :     {
    1705          30 :       pari_sp av2 = avma;
    1706             :       long imin, imax;
    1707          30 :       GEN per = indexsort(norms);
    1708          30 :       if (DEBUGLEVEL>2) err_printf("sorting... [%ld elts]\n",s);
    1709             :       /* let N be the minimal norm so far for x satisfying 'check'. Keep
    1710             :        * all elements of norm N */
    1711        3150 :       for (i = 1; i <= s; i++)
    1712             :       {
    1713        3150 :         long k = per[i];
    1714        3150 :         if (check(data,gel(S,k))) { borne1 = gel(norms,k); break; }
    1715             :       }
    1716          30 :       imin = i;
    1717         234 :       for (; i <= s; i++)
    1718         234 :         if (mpgreaterthan(gel(norms,per[i]), borne1)) break;
    1719          30 :       imax = i;
    1720          30 :       for (i=imin, s=0; i < imax; i++) gel(S,++s) = gel(S,per[i]);
    1721          30 :       avma = av2;
    1722          30 :       if (s)
    1723             :       {
    1724          30 :         borne2 = mulrr(borne1, alpha);
    1725          30 :         checkcnt = 0;
    1726             :       }
    1727          30 :       if (!stockall) continue;
    1728          30 :       if (s > stockmax/2) stockmax <<= 1;
    1729          30 :       norms = cgetg(stockmax+1, t_VEC);
    1730          30 :       for (i = 1; i <= s; i++) gel(norms,i) = borne1;
    1731             :     }
    1732             :     else
    1733             :     {
    1734          18 :       if (!stockall && BORNE) goto END;
    1735          18 :       if (!stockall) continue;
    1736          18 :       stockmax <<= 1;
    1737             :     }
    1738             : 
    1739             :     {
    1740          48 :       GEN Snew = cgetg(stockmax + 1, t_VEC);
    1741          48 :       for (i = 1; i <= s; i++) gel(Snew,i) = gel(S,i);
    1742          48 :       Snew = clonefill(Snew, s, stockmax);
    1743          48 :       if (isclone(S)) gunclone(S);
    1744          48 :       S = Snew;
    1745             :     }
    1746      145992 :   }
    1747             : END:
    1748         666 :   if (s < stockmax) stockmax = s;
    1749         666 :   if (check)
    1750             :   {
    1751             :     GEN per, alph, pols, p;
    1752         648 :     if (DEBUGLEVEL>2) err_printf("final sort & check...\n");
    1753         648 :     setlg(norms,stockmax+1); per = indexsort(norms);
    1754         648 :     alph = cgetg(stockmax+1,t_VEC);
    1755         648 :     pols = cgetg(stockmax+1,t_VEC);
    1756        6510 :     for (j=0,i=1; i<=stockmax; i++)
    1757             :     {
    1758        5880 :       long t = per[i];
    1759        5880 :       GEN N = gel(norms,t);
    1760        5880 :       if (j && mpgreaterthan(N, borne1)) break;
    1761        5862 :       if ((p = check(data,gel(S,t))))
    1762             :       {
    1763        5304 :         if (!j) borne1 = N;
    1764        5304 :         j++;
    1765        5304 :         gel(pols,j) = p;
    1766        5304 :         gel(alph,j) = gel(S,t);
    1767             :       }
    1768             :     }
    1769         648 :     setlg(pols,j+1);
    1770         648 :     setlg(alph,j+1);
    1771         648 :     if (stockmax && isclone(S)) { alph = gcopy(alph); gunclone(S); }
    1772         648 :     return mkvec2(pols, alph);
    1773             :   }
    1774          18 :   if (stockmax)
    1775             :   {
    1776          12 :     setlg(S,stockmax+1);
    1777          12 :     settyp(S,t_MAT);
    1778          12 :     if (isclone(S)) { p1 = S; S = gcopy(S); gunclone(p1); }
    1779             :   }
    1780             :   else
    1781           6 :     S = cgetg(1,t_MAT);
    1782          18 :   return mkvec3(utoi(s<<1), borne1, S);
    1783             : }
    1784             : 
    1785             : /* solve q(x) = x~.a.x <= bound, a > 0.
    1786             :  * If check is non-NULL keep x only if check(x).
    1787             :  * If a is a vector, assume a[1] is the LLL-reduced Cholesky form of q */
    1788             : GEN
    1789         684 : fincke_pohst(GEN a, GEN B0, long stockmax, long PREC, FP_chk_fun *CHECK)
    1790             : {
    1791         684 :   pari_sp av = avma;
    1792             :   VOLATILE long i,j,l;
    1793         684 :   VOLATILE GEN r,rinv,rinvtrans,u,v,res,z,vnorm,rperm,perm,uperm, bound = B0;
    1794             : 
    1795         684 :   if (typ(a) == t_VEC)
    1796             :   {
    1797         300 :     r = gel(a,1);
    1798         300 :     u = NULL;
    1799             :   }
    1800             :   else
    1801             :   {
    1802         384 :     long prec = PREC;
    1803         384 :     l = lg(a);
    1804         384 :     if (l == 1)
    1805             :     {
    1806           6 :       if (CHECK) pari_err_TYPE("fincke_pohst [dimension 0]", a);
    1807           6 :       retmkvec3(gen_0, gen_0, cgetg(1,t_MAT));
    1808             :     }
    1809         378 :     u = lllfp(a, 0.75, LLL_GRAM);
    1810         378 :     if (lg(u) != lg(a)) return NULL;
    1811         378 :     r = qf_apply_RgM(a,u);
    1812         378 :     i = gprecision(r);
    1813         378 :     if (i)
    1814         354 :       prec = i;
    1815             :     else {
    1816          24 :       prec = DEFAULTPREC + nbits2extraprec(gexpo(r));
    1817          24 :       if (prec < PREC) prec = PREC;
    1818             :     }
    1819         378 :     if (DEBUGLEVEL>2) err_printf("first LLL: prec = %ld\n", prec);
    1820         378 :     r = qfgaussred_positive(r);
    1821         378 :     if (!r) return NULL;
    1822        1710 :     for (i=1; i<l; i++)
    1823             :     {
    1824        1332 :       GEN s = gsqrt(gcoeff(r,i,i), prec);
    1825        1332 :       gcoeff(r,i,i) = s;
    1826        1332 :       for (j=i+1; j<l; j++) gcoeff(r,i,j) = gmul(s, gcoeff(r,i,j));
    1827             :     }
    1828             :   }
    1829             :   /* now r~ * r = a in LLL basis */
    1830         678 :   rinv = RgM_inv_upper(r);
    1831         678 :   if (!rinv) return NULL;
    1832         678 :   rinvtrans = shallowtrans(rinv);
    1833         678 :   if (DEBUGLEVEL>2)
    1834           0 :     err_printf("Fincke-Pohst, final LLL: prec = %ld\n", gprecision(rinvtrans));
    1835         678 :   v = lll(rinvtrans);
    1836         678 :   if (lg(v) != lg(rinvtrans)) return NULL;
    1837             : 
    1838         678 :   rinvtrans = RgM_mul(rinvtrans, v);
    1839         678 :   v = ZM_inv(shallowtrans(v),gen_1);
    1840         678 :   r = RgM_mul(r,v);
    1841         678 :   u = u? ZM_mul(u,v): v;
    1842             : 
    1843         678 :   l = lg(r);
    1844         678 :   vnorm = cgetg(l,t_VEC);
    1845         678 :   for (j=1; j<l; j++) gel(vnorm,j) = gnorml2(gel(rinvtrans,j));
    1846         678 :   rperm = cgetg(l,t_MAT);
    1847         678 :   uperm = cgetg(l,t_MAT); perm = indexsort(vnorm);
    1848         678 :   for (i=1; i<l; i++) { uperm[l-i] = u[perm[i]]; rperm[l-i] = r[perm[i]]; }
    1849         678 :   u = uperm;
    1850         678 :   r = rperm; res = NULL;
    1851         678 :   pari_CATCH(e_PREC) { }
    1852             :   pari_TRY {
    1853             :     GEN q;
    1854         678 :     if (CHECK && CHECK->f_init) bound = CHECK->f_init(CHECK, r, u);
    1855         672 :     q = gaussred_from_QR(r, gprecision(vnorm));
    1856         672 :     if (!q) pari_err_PREC("fincke_pohst");
    1857         672 :     res = smallvectors(q, bound, stockmax, CHECK);
    1858         672 :   } pari_ENDCATCH;
    1859         678 :   if (CHECK)
    1860             :   {
    1861         654 :     if (CHECK->f_post) res = CHECK->f_post(CHECK, res, u);
    1862         654 :     return res;
    1863             :   }
    1864          24 :   if (!res) pari_err_PREC("fincke_pohst");
    1865             : 
    1866          24 :   z = cgetg(4,t_VEC);
    1867          24 :   gel(z,1) = gcopy(gel(res,1));
    1868          24 :   gel(z,2) = gcopy(gel(res,2));
    1869          24 :   gel(z,3) = ZM_mul(u, gel(res,3)); return gerepileupto(av,z);
    1870             : }

Generated by: LCOV version 1.11