Code coverage tests

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

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

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

LCOV - code coverage report
Current view: top level - basemath - qfisom.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.18.1 lcov report (development 30101-620df3499e) Lines: 862 879 98.1 %
Date: 2025-03-28 09:18:47 Functions: 54 54 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2012  The PARI group.
       2             : 
       3             : This file is part of the PARI/GP package.
       4             : 
       5             : PARI/GP is free software; you can redistribute it and/or modify it under the
       6             : terms of the GNU General Public License as published by the Free Software
       7             : Foundation; either version 2 of the License, or (at your option) any later
       8             : version. It is distributed in the hope that it will be useful, but WITHOUT
       9             : ANY WARRANTY WHATSOEVER.
      10             : 
      11             : Check the License for details. You should have received a copy of it, along
      12             : with the package; see the file 'COPYING'. If not, write to the Free Software
      13             : Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
      14             : 
      15             : #include "pari.h"
      16             : #include "paripriv.h"
      17             : 
      18             : #define DEBUGLEVEL DEBUGLEVEL_qfisom
      19             : 
      20             : /*To be moved elsewhere */
      21             : 
      22             : static long
      23      549227 : Z_trunc(GEN z)
      24             : {
      25      549227 :   long s = signe(z);
      26      549227 :   return s==0 ? 0: (long)(s*mod2BIL(z));
      27             : }
      28             : 
      29             : static GEN
      30       76384 : ZV_trunc_to_zv(GEN z)
      31             : {
      32       76384 :   long i, l = lg(z);
      33       76384 :   GEN x = cgetg(l, t_VECSMALL);
      34      625611 :   for (i=1; i<l; i++) x[i] = Z_trunc(gel(z,i));
      35       76384 :   return x;
      36             : }
      37             : 
      38             : /* returns scalar product of vectors x and y with respect to Gram-matrix F */
      39             : static long
      40      245686 : scp(GEN x, GEN F, GEN y)
      41             : {
      42      245686 :   long i, j, n = lg(F)-1;
      43      245686 :   ulong sum = 0;
      44     4108020 :   for (i = 1; i <= n; ++i)
      45             :   {
      46     3862334 :     ulong xi = uel(x,i);
      47     3862334 :     if (xi)
      48             :     {
      49     1579382 :       GEN Fi = gel(F, i);
      50    26466328 :       for (j = 1; j <= n; ++j) sum += xi * uel(Fi,j) * uel(y,j);
      51             :     }
      52             :   }
      53      245686 :   return sum;
      54             : }
      55             : 
      56             : static GEN
      57       15498 : ZM_trunc_to_zm(GEN z)
      58             : {
      59       15498 :   long i, l = lg(z);
      60       15498 :   GEN x = cgetg(l,t_MAT);
      61       91882 :   for (i=1; i<l; i++) gel(x,i) = ZV_trunc_to_zv(gel(z,i));
      62       15498 :   return x;
      63             : }
      64             : 
      65             : static GEN
      66       11347 : zm_divmod(GEN A, GEN B, ulong p)
      67             : {
      68       11347 :   pari_sp av = avma;
      69       11347 :   GEN Ap = zm_to_Flm(A,p), Bp = zm_to_Flm(B,p);
      70       11347 :   GEN C = Flm_center(Flm_mul(Flm_inv(Ap, p), Bp, p), p, p>>1);
      71       11347 :   return gerepileupto(av, C);
      72             : }
      73             : 
      74             : static GEN
      75          35 : ZM_to_zm_canon(GEN V)
      76             : {
      77          35 :   GEN W = ZM_to_zm(V);
      78          35 :   long i, l = lg(W);
      79        5663 :   for (i=1; i<l; i++) (void)zv_canon_inplace(gel(W,i));
      80          35 :   return W;
      81             : }
      82             : 
      83             : static GEN
      84          56 : zm_apply_zm(GEN M, GEN U)
      85          56 : { return zm_mul(zm_transpose(U),zm_mul(M, U)); }
      86             : 
      87             : static GEN
      88          56 : zmV_apply_zm(GEN x, GEN U)
      89         112 : { pari_APPLY_same(zm_apply_zm(gel(x,i), U)); }
      90             : 
      91             : /********************************************************************/
      92             : /**                                                                **/
      93             : /**      QFAUTO/QFISOM ported from B. Souvignier ISOM program      **/
      94             : /**                                                                **/
      95             : /********************************************************************/
      96             : 
      97             : /* This is a port by Bill Allombert of the program ISOM by Bernt Souvignier
      98             : which implements
      99             : W. PLESKEN, B. SOUVIGNIER, Computing Isometries of Lattices,
     100             : Journal of Symbolic Computation, Volume 24, Issues 3-4, September 1997,
     101             : Pages 327-334, ISSN 0747-7171, 10.1006/jsco.1996.0130.
     102             : (http://www.sciencedirect.com/science/article/pii/S0747717196901303)
     103             : 
     104             : We thank Professor Souvignier for giving us permission to port his code.
     105             : */
     106             : 
     107             : struct group
     108             : {
     109             :   GEN ord, ng, nsg, g;
     110             : };
     111             : 
     112             : struct fingerprint
     113             : {
     114             :   GEN diag, per, e;
     115             : };
     116             : 
     117             : struct qfauto
     118             : {
     119             :   long dim;
     120             :   GEN F, U, V, W, v;
     121             :   ulong p;
     122             : };
     123             : 
     124             : struct qfcand
     125             : {
     126             :   long cdep;
     127             :   GEN comb, bacher_pol;
     128             : };
     129             : 
     130             : static long
     131       12460 : possible(GEN F, GEN Ftr, GEN V, GEN W, GEN per, long I, long J)
     132             : {
     133       12460 :   long i, j, k, count = 0, n = lg(W)-1, f = lg(F)-1;
     134             : 
     135    19985952 :   for (j = 1; j <= n; j++)
     136             :   {
     137    19973492 :     GEN Wj = gel(W,j), Vj = gel(V,j);
     138    19973492 :     i = I+1;
     139             :     /* check vector length */
     140    37385992 :     for (k = 1; k <= f && i > I && Wj[k] == mael3(F,k,J,J); k++)
     141    25456473 :       for (i = 1; i <= I; i++) /* check scalar products with basis vectors */
     142    24722866 :         if (zv_dotproduct(Vj,gmael(Ftr,k,per[i])) != mael(gel(F,k),J,per[i]))
     143    16678893 :           break;
     144    19973492 :     if (k == f+1 && i > I) ++count;
     145             :     /* same for the negative vector */
     146    19973492 :     i = I+1;
     147    37385992 :     for (k = 1; k <= f && i > I && Wj[k] == mael3(F,k,J,J); k++)
     148    25853821 :       for (i = 1; i <= I ; i++)
     149    25008179 :         if (zv_dotproduct(Vj,gmael(Ftr,k,per[i])) != -mael(gel(F,k),J,per[i]))
     150    16566858 :           break;
     151    19973492 :     if (k == f+1 && i > I) ++count;
     152             :   }
     153       12460 :   return count;
     154             : }
     155             : 
     156             : static void
     157         196 : fingerprint(struct fingerprint *fp, struct qfauto *qf)
     158             : {
     159             :   pari_sp av;
     160         196 :   GEN V=qf->V, W=qf->W, F=qf->F, Mf, Ftr;
     161         196 :   long i, j, k, min, dim = qf->dim, n = lg(V)-1, f = lg(F)-1;
     162         196 :   fp->per = identity_perm(dim);
     163         196 :   fp->e = cgetg(dim+1, t_VECSMALL);
     164         196 :   fp->diag = cgetg(dim+1, t_VECSMALL);
     165         196 :   av = avma;
     166         196 :   Ftr = cgetg(f+1,t_VEC);
     167         406 :   for (i = 1; i <= f; i++) gel(Ftr,i) = zm_transpose(gel(F,i));
     168         196 :   Mf = zero_Flm_copy(dim, dim);
     169             :   /* the first row of the fingerprint has as entry nr. i the number of
     170             :    vectors, which have the same length as the i-th basis-vector with
     171             :    respect to every invariant form */
     172      174041 :   for (j = 1; j <= n; j++)
     173             :   {
     174      173845 :     GEN Wj = gel(W,j);
     175     2888662 :     for (i = 1; i <= dim; i++)
     176             :     {
     177     4464446 :       for (k = 1; k <= f && Wj[k] == mael3(F,k,i,i); ++k);
     178     2714817 :       if (k == f+1) mael(Mf,1,i) += 2;
     179             :     }
     180             :   }
     181        2009 :   for (i = 1; i <= dim-1; ++i)
     182             :   { /* a minimal entry != 0 in the i-th row is chosen */
     183        1813 :     min = i;
     184       14273 :     for (j = i+1; j <= dim; j++)
     185       12460 :       if (mael(Mf,i,fp->per[j]) < mael(Mf,i,fp->per[min])) min = j;
     186        1813 :     lswap(fp->per[i],fp->per[min]);
     187             :     /* the column below the minimal entry is set to 0 */
     188       14273 :     for (j = i+1; j <= dim; j++) mael(Mf,j,fp->per[i]) = 0;
     189             :     /* compute the row i+1 of the fingerprint */
     190       14273 :     for (j = i+1; j <= dim; j++)
     191       12460 :       mael(Mf,i+1,fp->per[j]) = possible(F, Ftr, V, W, fp->per, i, fp->per[j]);
     192             :   }
     193        2205 :   for (i = 1; i <= dim; i++)
     194             :   {
     195        2009 :     fp->diag[i] = mael(Mf,i,fp->per[i]); /* only diag(f) is needed later */
     196        2009 :     if ((fp->e[i] = vecvecsmall_search(V,vecsmall_ei(dim,fp->per[i]))) < 0)
     197           0 :       pari_err_BUG("qfisom, standard basis vector not found");
     198             :   }
     199         196 :   set_avma(av);
     200         196 : }
     201             : 
     202             : /* The Bacher polynomial for v[I] with scalar product S is defined as follows:
     203             :  * let L be the vectors with same length as v[I] and scalar product S with v[I];
     204             :  * for each vector w in L let nw be the number of pairs (y,z) of vectors in L,
     205             :  * such that all scalar products between w,y and z are S, then the Bacher
     206             :  * polynomial is the sum over the w in list of the monomials X^nw  */
     207             : static GEN
     208          42 : bacher(long I, long S, struct qfauto *qf)
     209             : {
     210          42 :   pari_sp av = avma;
     211          42 :   GEN V=qf->V, W=qf->W, Fv=gel(qf->v,1), list, listxy, counts, vI, coef;
     212          42 :   long i, j, k, nlist, nxy, sum, mind, maxd, n = lg(V)-1;
     213             : 
     214          42 :   I = labs(I); /* Bacher polynomials of v[I] and -v[I] are equal */
     215          42 :   vI = gel(V,I);
     216          42 :   list = zero_Flv(2*n); /* vectors that have scalar product S with v[I] */
     217          42 :   nlist = 0;
     218       62678 :   for (i = 1; i <= n; ++i)
     219       62636 :     if (mael(W,i,1) == mael(W,I,1))
     220             :     {
     221        5740 :       long s = zv_dotproduct(vI, gel(Fv,i));
     222        5740 :       if (s == S) list[++nlist] = i;
     223        5740 :       if (-s == S) list[++nlist] = -i;
     224             :     }
     225             :   /* there are nlist vectors that have scalar product S with v[I] */
     226          42 :   sum = nlist;
     227          42 :   if (nlist==0) retmkvec2(mkvecsmall3(0,0,0),cgetg(1,t_VEC));
     228          14 :   counts = cgetg(nlist+1, t_VECSMALL);
     229          14 :   listxy = cgetg(nlist+1, t_VECSMALL);
     230        1162 :   for (i = 1; i <= nlist; ++i)
     231             :   {
     232             :     long S1;
     233             :     /* listxy is the list of the nxy vectors from list that have scalar
     234             :        product S with v[list[i]] */
     235       95284 :     for (j = 1; j <= nlist; ++j) listxy[j] = 0;
     236        1148 :     nxy = 0;
     237        1148 :     S1 = list[i] > 0 ? S : -S;
     238       95284 :     for (j = 1; j <= nlist; ++j)
     239             :     {
     240       94136 :       long S2 = list[j] > 0 ? S1 : -S1;
     241             :       /* note: for i > 0 is v[-i] = -v[i] */
     242       94136 :       if (zv_dotproduct(gel(V,labs(list[i])), gel(Fv,labs(list[j]))) == S2)
     243       30240 :         listxy[++nxy] = list[j];
     244             :     }
     245             :     /* counts[i] is the number of pairs for the vector v[list[i]] */
     246        1148 :     counts[i] = 0;
     247       31388 :     for (j = 1; j <= nxy; ++j)
     248             :     {
     249       30240 :       long S1 = listxy[j] > 0 ? S : -S;
     250       30240 :       GEN Vj = gel(V, labs(listxy[j]));
     251      423360 :       for (k = j+1; k <= nxy; ++k)
     252             :       {
     253      393120 :         long S2 = listxy[k] > 0 ? S1 : -S1, lk = labs(listxy[k]);
     254      393120 :         if (zv_dotproduct(Vj, gel(Fv,lk)) == S2) counts[i]++;
     255             :       }
     256             :     }
     257             :   }
     258             :    /* maxd = max degree of the Bacher-polynomial, mind = min degree */
     259          14 :   mind = maxd = counts[1];
     260        1148 :   for (i = 2; i <= nlist; i++)
     261             :   {
     262        1134 :     if (counts[i] > maxd) maxd = counts[i];
     263        1134 :     else if (counts[i] < mind) mind = counts[i];
     264             :   }
     265          14 :   coef = zero_Flv(maxd - mind + 1);
     266        1162 :   for (i = 1; i <= nlist; i++) coef[1+counts[i] - mind] += 1;
     267          14 :   if (DEBUGLEVEL)
     268           0 :     err_printf("QFIsom: mind=%ld maxd=%ld sum=%ld\n",mind,maxd,sum);
     269             :   /* Bacher polynomial = sum_{mind <= i <= maxd} coef[i - mind] * X^i */
     270          14 :   return gc_GEN(av, mkvec2(mkvecsmall3(sum, mind, maxd), coef));
     271             : }
     272             : 
     273             : static GEN
     274         196 : init_bacher(long bachdep, struct fingerprint *fp, struct qfauto *qf)
     275             : {
     276         196 :   GEN z = cgetg(bachdep+1,t_VEC);
     277             :   long i;
     278         238 :   for (i=1;i<=bachdep;i++)
     279             :   {
     280          42 :     long bachscp = mael(qf->W,fp->e[i],1) / 2;
     281          42 :     gel(z,i) = bacher(fp->e[i], bachscp, qf);
     282             :   }
     283         196 :   return z;
     284             : }
     285             : 
     286             : /* checks, whether the vector v[I] has Bacher polynomial pol  */
     287             : static long
     288         154 : bachcomp(GEN pol, long I, GEN V, GEN W, GEN Fv)
     289             : {
     290         154 :   pari_sp av = avma;
     291         154 :   GEN co, list, listxy, vI, coef = gel(pol,2);
     292             :   long i, j, k, nlist, nxy, count;
     293         154 :   const long n = lg(V)-1, S = mael(W,I,1) / 2;
     294         154 :   long sum = mael(pol,1,1), mind = mael(pol,1,2), maxd = mael(pol,1,3);
     295         154 :   vI = gel(V,I);
     296         154 :   list = zero_Flv(sum);
     297         154 :   nlist = 0; /* nlist should be equal to pol.sum */
     298      137186 :   for (i = 1; i <= n && nlist <= sum; i++)
     299      137032 :     if (mael(W,i,1) == mael(W,I,1))
     300             :     {
     301       22218 :       long s = zv_dotproduct(vI, gel(Fv,i));
     302       22218 :       if (s == S)
     303             :       {
     304        3612 :         if (nlist < sum) list[nlist+1] = i;
     305        3612 :         nlist++;
     306             :       }
     307       22218 :       if (-s == S)
     308             :       {
     309        1022 :         if (nlist < sum) list[nlist+1] = -i;
     310        1022 :         nlist++;
     311             :       }
     312             :     }
     313             :   /* the number of vectors with scalar product S is already different */
     314         154 :   if (nlist != sum) return gc_long(av,0);
     315         112 :   if (nlist == 0) return gc_long(av,1);
     316             :   /* listxy is the list of the nxy vectors from list that have scalar product S
     317             :      with v[list[i]] */
     318          56 :   listxy = cgetg(nlist+1,t_VECSMALL);
     319          56 :   co = zero_Flv(maxd - mind + 1);
     320        4648 :   for (i = 1; i <= nlist; i++)
     321             :   {
     322        4592 :     long S1 = list[i] > 0 ? S : -S;
     323        4592 :     GEN Vi = gel(V, labs(list[i]));
     324      381136 :     for (j = 1; j <= nlist; j++) listxy[j] = 0;
     325        4592 :     nxy = 0;
     326      381136 :     for (j = 1; j <= nlist; j++)
     327             :     {
     328      376544 :       long S2 = list[j] > 0 ? S1 : -S1;
     329      376544 :       if (zv_dotproduct(Vi, gel(Fv,labs(list[j]))) == S2)
     330      120960 :         listxy[++nxy] = list[j];
     331             :     }
     332        4592 :     count = 0; /* number of pairs */
     333      125552 :     for (j = 1; j <= nxy && count <= maxd; j++)
     334             :     {
     335      120960 :       long S1 = listxy[j] > 0 ? S : -S;
     336      120960 :       GEN Vj = gel(V, labs(listxy[j]));
     337     1693440 :       for (k = j+1; k <= nxy && count <= maxd; k++)
     338             :       {
     339     1572480 :         long S2 = listxy[k] > 0 ? S1 : -S1, lk = labs(listxy[k]);
     340     1572480 :         if (zv_dotproduct(Vj, gel(Fv,lk)) == S2) count++;
     341             :       }
     342             :     }
     343             :     /* Bacher polynomials can not be equal */
     344        4592 :     if (count < mind  ||  count > maxd  ||
     345        4592 :         co[count-mind+1] >= coef[count-mind+1]) return gc_long(av, 0);
     346        4592 :     co[count-mind+1]++;
     347             :   }
     348          56 :   return gc_long(av, 1); /* Bacher-polynomials are equal */
     349             : }
     350             : 
     351             : static GEN
     352         266 : checkvecs(GEN V, GEN F, GEN norm)
     353             : {
     354         266 :   long i, j, k, n = lg(V)-1, f = lg(F)-1;
     355         266 :   GEN W = cgetg(n+1, t_MAT);
     356         266 :   j = 0;
     357      245924 :   for (i = 1; i <= n; i++)
     358             :   {
     359      245658 :     GEN normvec = cgetg(f+1, t_VECSMALL), Vi = gel(V,i);
     360      491344 :     for (k = 1; k <= f; ++k) normvec[k] = scp(Vi, gel(F,k), Vi);
     361      245658 :     if (vecvecsmall_search(norm,normvec) < 0) ++j;
     362             :     else
     363             :     {
     364      245644 :       gel(V,i-j) = Vi;
     365      245644 :       gel(W,i-j) = normvec;
     366             :     }
     367             :   }
     368         266 :   setlg(V, n+1-j);
     369         266 :   setlg(W, n+1-j); return W;
     370             : }
     371             : 
     372             : static long
     373     6417971 : operate(long nr, GEN A, GEN V)
     374             : {
     375     6417971 :   pari_sp av = avma;
     376             :   long im,eps;
     377     6417971 :   GEN w = zm_zc_mul(A,gel(V,labs(nr)));
     378     6417971 :   eps = zv_canon_inplace(w);
     379     6417971 :   if (nr < 0) eps = -eps; /* -w */
     380     6417971 :   if ((im = vecvecsmall_search(V,w)) < 0)
     381           0 :     pari_err_BUG("qfauto, image of vector not found");
     382     6417971 :   return gc_long(av, eps * im);
     383             : }
     384             : 
     385             : static GEN
     386        3752 : orbit(GEN pt, long ipt, long npt, GEN H, GEN V)
     387             : {
     388        3752 :   pari_sp av = avma;
     389        3752 :   long i, cnd, im, n = lg(V)-1, nH = lg(H)-1, no = npt;
     390        3752 :   GEN flag = zero_Flv(2*n+1)+n+1; /* need negative indices */
     391        3752 :   GEN orb = cgetg(2*n+1,t_VECSMALL);
     392        6979 :   for (i = 1; i <= npt; ++i)
     393             :   {
     394        3227 :     orb[i] = pt[ipt+i];
     395        3227 :     flag[pt[ipt+i]] = 1;
     396             :   }
     397       59430 :   for (cnd=1; cnd <= no; ++cnd)
     398      347431 :     for (i = 1; i <= nH; ++i)
     399             :     {
     400      291753 :       im = operate(orb[cnd], gel(H,i), V);
     401             :       /* image is a new point in the orbit */
     402      291753 :       if (flag[im] == 0) { orb[++no] = im; flag[im] = 1; }
     403             :     }
     404        3752 :   setlg(orb,no+1); return gerepileuptoleaf(av, orb);
     405             : }
     406             : 
     407             : /* return the length of the orbit of pt under the first nG matrices in G */
     408             : static long
     409       21994 : orbitlen(long pt, long orblen, GEN G, long nG, GEN V)
     410             : {
     411       21994 :   pari_sp av = avma;
     412       21994 :   long i, len, cnd, n = lg(V)-1;
     413             :   GEN orb, flag;
     414             :   /* if flag[i + n+1] = 1, -n <= i <= n, then i is already in the orbit */
     415       21994 :   flag = zero_F2v(2*n + 1);
     416       21994 :   orb = zero_Flv(orblen); orb[1] = pt;
     417       21994 :   F2v_set(flag,pt+n+1);
     418       21994 :   len = 1;
     419      963137 :   for (cnd = 1; cnd <= len && len < orblen; cnd++)
     420     6636231 :     for (i = 1; i <= nG && len < orblen; i++)
     421             :     {
     422     5695088 :       long im = operate(orb[cnd], gel(G,i), V);
     423             :       /* image is a new point in the orbit */
     424     5695088 :       if (!F2v_coeff(flag,im+n+1)) { orb[++len] = im; F2v_set(flag,im+n+1); }
     425             :     }
     426       21994 :   return gc_long(av, len);
     427             : }
     428             : 
     429             : /* delete the elements in orb2 from orb1, an entry 0 marks the end of the
     430             :  * list, returns the length of orb1 */
     431             : static long
     432        9177 : orbdelete(GEN orb1, GEN orb2)
     433             : {
     434        9177 :   long i, j, len, l1 = lg(orb1)-1, l2 = lg(orb2)-1;
     435      223188 :   for (i = 1; i <= l1 && orb1[i] != 0; ++i);
     436        9177 :   len = i - 1;
     437       70280 :   for (i = 1; i <= l2 && orb2[i] != 0; ++i)
     438             :   {
     439       61103 :     long o2i = orb2[i];
     440     9354051 :     for (j = 1; j <= len && orb1[j] != o2i; ++j);
     441             :     /* orb1[j] = orb2[i], hence delete orb1[j] from orb1 */
     442       61103 :     if (j <= len) { orb1[j] = orb1[len]; orb1[len--] = 0; }
     443             :   }
     444        9177 :   return len;
     445             : }
     446             : 
     447             : static long
     448        3752 : orbsubtract(GEN Cs, GEN pt, long ipt, long npt, GEN H, GEN V, long *len)
     449             : {
     450        3752 :   pari_sp av = avma;
     451        3752 :   GEN orb = orbit(pt, ipt, npt, H, V);
     452        3752 :   if (len) *len = lg(orb)-1;
     453        3752 :   return gc_long(av, orbdelete(Cs, orb));
     454             : }
     455             : 
     456             : /* Generates the matrix X whose per[i]-th row is the vector V[x[i]] */
     457             : static GEN
     458       21469 : matgen(GEN x, GEN per, GEN V)
     459             : {
     460       21469 :   long i, j, n = lg(x)-1;
     461       21469 :   GEN X = cgetg(n+1,t_MAT);
     462      331051 :   for (i = 1; i <= n; i++)
     463             :   {
     464      309582 :     GEN Xp = cgetg(n+1,t_VECSMALL);
     465      309582 :     long xi = x[i];
     466     4906398 :     for (j = 1; j <= n; j++) Xp[j] = xi > 0? mael(V,xi,j): -mael(V,-xi,j);
     467      309582 :     gel(X, per[i]) = Xp;
     468             :   }
     469       21469 :   return X;
     470             : }
     471             : /* x1 corresponds to an element X1 mapping some vector e on p1, x2 to an
     472             :  * element X2 mapping e on p2 and G is a generator mapping p1 on p2, then
     473             :  * S = X1*G*X2^-1 stabilizes e */
     474             : static GEN
     475       10339 : stabil(GEN x1, GEN x2, GEN per, GEN G, GEN V, ulong p)
     476             : {
     477       10339 :   pari_sp av = avma;
     478       10339 :   long i, n = lg(x1)-1;
     479       10339 :   GEN XG, X2, x = cgetg(n+1,t_VECSMALL);
     480      159663 :   for (i = 1; i <= n; i++) x[i] = operate(x1[i], G, V);
     481             :   /* XG is the composite mapping of the matrix corresponding to x1 and G */
     482       10339 :   XG = matgen(x, per, V);
     483       10339 :   X2 = matgen(x2, per, V);
     484       10339 :   return gerepileupto(av, zm_divmod(X2,XG,p));
     485             : }
     486             : 
     487             : /* computes the orbit of fp.e[I] under the generators in G->g[I]...G->g[n-1]
     488             :  * and elements stabilizing fp.e[I], has some heuristic break conditions, the
     489             :  * generators in G->g[i] stabilize fp.e[0]...fp.e[i-1] but not fp.e[i],
     490             :  * G->ng[i] is the number of generators in G->g[i], the first G->nsg[i] of
     491             :  * which are elements which are obtained as stabilizer elements in
     492             :  * <G->g[0],...,G->g[i-1]>, G->ord[i] is the orbit length of fp.e[i] under
     493             :  * <G->g[i],...,G->g[n-1]> */
     494             : static void
     495         854 : stab(long I, struct group *G, struct fingerprint *fp, GEN V, ulong p)
     496             : {
     497             :   GEN orb, w, flag, H, Hj, S;
     498             :   long len, cnd, i, j, k, l, im, nH, fail;
     499         854 :   long Maxfail, Rest, dim = lg(fp->diag)-1, n = lg(V)-1;
     500             :   /* Heuristic break conditions for the computation of stabilizer elements:
     501             :    * it is too expensive to calculate all the stabilizer generators, which are
     502             :    * obtained from the orbit, since this is highly redundant. On the other hand
     503             :    * every new generator which enlarges the group is cheaper than one obtained
     504             :    * from the backtrack, after Maxfail subsequent stabilizer elements, that do
     505             :    * not enlarge the group, Rest more elements are calculated even if they
     506             :    * leave the group unchanged, since it turned out that this is often useful
     507             :    * in the following steps. Increasing the parameters may decrease the number
     508             :    * of generators for the group while increasing the running time. */
     509        7700 :   for (Rest = 0, i = I; i <= dim; i++)
     510        6846 :     if (fp->diag[i] > 1 && G->ord[i] < fp->diag[i]) ++Rest;
     511       12390 :   for (Maxfail = Rest, i = 1; i <= dim; i++)
     512       11536 :     if (fp->diag[i] > 1) ++Maxfail;
     513        7700 :   for (nH = 0, i = I; i <= dim; i++)
     514        6846 :     nH += G->ng[i];
     515             :   /* generators of the group in which the stabilizer is computed */
     516         854 :   H = cgetg(nH+1,t_MAT);
     517         854 :   Hj = cgetg(nH+2,t_MAT);
     518        7700 :   for (i = I, k = 0; i <= dim; i++)
     519       15442 :     for (j = 1; j <= G->ng[i]; j++) gel(H,++k) = gmael(G->g,i,j);
     520             :   /* in w[V.n+i] an element is stored that maps fp.e[I] on v[i] */
     521         854 :   w = cgetg(2*n+2,t_VEC);
     522         854 :   orb = zero_Flv(2*n); /* the orbit of fp.e[I] */
     523         854 :   flag = zero_F2v(2*n+1); /* if flag[i + V.n], then i is already in orbit */
     524         854 :   orb[1] = fp->e[I];
     525         854 :   F2v_set(flag,orb[1]+n+1);
     526         854 :   gel(w,orb[1]+n+1) = cgetg(dim+1,t_VECSMALL);
     527       12390 :   for (i = 1; i <= dim; i++) mael(w,orb[1]+n+1,i) = fp->e[i];
     528         854 :   cnd = len = 1;
     529         854 :   fail = 0; /* number of successive failures */
     530        5593 :   while (cnd <= len && fail < Maxfail+Rest)
     531             :   {
     532       38045 :     for (i = 1; i <= nH && fail < Maxfail+Rest; ++i)
     533             :     {
     534       33306 :       if (fail >= Maxfail)
     535             :         /* already Maxfail successive failures: apply a random generator to a
     536             :          * random point of the orbit to get Rest more stabilizer elements */
     537             :       {
     538        5222 :         cnd = 1+(long)random_Fl(len);
     539        5222 :         i = 1+(long)random_Fl(nH);
     540             :       }
     541       33306 :       im = operate(orb[cnd], gel(H,i), V);
     542       33306 :       if (F2v_coeff(flag,im+n+1) == 0)
     543             :       { /* found new element, appended to the orbit; an element mapping
     544             :          *  fp.e[I] to im is stored in w[im+V.n] */
     545             :         GEN wim;
     546       13146 :         orb[++len] = im;
     547       13146 :         F2v_set(flag,im+n+1);
     548       13146 :         wim = cgetg(dim+1,t_VECSMALL);
     549       13146 :         gel(w,im+n+1) = wim;
     550      177380 :         for (j = 1; j <= dim; ++j)
     551      164234 :           wim[j] = operate(mael(w,orb[cnd]+n+1,j), gel(H,i), V);
     552             :       }
     553             :       else /* image was already in the orbit */
     554             :       { /* j = first index where images of old and new element mapping e[I] on
     555             :          * im differ */
     556       86429 :         for (j = I; j <= dim; j++)
     557       82551 :           if (operate(mael(w,orb[cnd]+n+1,j), gel(H,i), V) != mael(w,im+n+1,j))
     558       16282 :             break;
     559       20160 :         if (j <= dim  && (G->ord[j] < fp->diag[j] || fail >= Maxfail))
     560       10339 :         {
     561       10339 :           GEN wo = gel(w,orb[cnd]+n+1);
     562       10339 :           long tmplen, nHj = 1;
     563             :         /* new stabilizer element S = w[orb[cnd]+V.n] * H[i] * (w[im+V.n])^-1 */
     564       10339 :           S = stabil(wo, gel(w,im+n+1), fp->per, gel(H,i), V, p);
     565       10339 :           gel(Hj,1) = S;
     566      114793 :           for (k = j; k <= dim; k++)
     567      179326 :             for (l = 1; l <= G->ng[k]; l++) gel(Hj, ++nHj) = gmael(G->g,k,l);
     568       10339 :           tmplen = orbitlen(fp->e[j], fp->diag[j], Hj, nHj, V);
     569       10339 :           if (tmplen > G->ord[j]  ||  fail >= Maxfail)
     570             :             /* the new stabilizer element S either enlarges the orbit of e[j]
     571             :                or it is one of the additional elements after MAXFAIL failures */
     572        4326 :           {
     573             :             GEN Ggj;
     574        4326 :             G->ord[j] = tmplen;
     575        4326 :             G->ng[j]++;
     576        4326 :             G->nsg[j]++;
     577             :             /* allocate memory for new generator */
     578        4326 :             gel(G->g,j) = vec_lengthen(gel(G->g,j),G->ng[j]);
     579        4326 :             Ggj = gel(G->g,j);
     580             :             /* new generator is inserted as stabilizer element nsg[j]-1 */
     581        4326 :             for (k = G->ng[j]; k > G->nsg[j]; k--) gel(Ggj,k) = gel(Ggj,k-1);
     582        4326 :             gel(Ggj,G->nsg[j]) = S;
     583        4326 :             nH++;
     584        4326 :             H = vec_lengthen(H, nH);
     585        4326 :             Hj = vec_lengthen(Hj, nH+1);
     586        4326 :             gel(H,nH) = gel(Ggj,G->nsg[j]); /* append new generator to H */
     587        4326 :             if (fail < Maxfail)
     588         868 :               fail = 0; /* number of failures is reset to 0 */
     589             :             else
     590        3458 :               ++fail;
     591             :           }
     592             :           else /* new stabilizer element S does not enlarge the orbit of e[j] */
     593        6013 :             ++fail;
     594             :         }
     595        9821 :         else if ((j < dim && fail < Maxfail)  || (j == dim && fail >= Maxfail))
     596        5838 :           ++fail;
     597             :         /* if S is the identity and fail < Maxfail, nothing is done */
     598             :       }
     599             :     }
     600        4739 :     if (fail < Maxfail) ++cnd;
     601             :   }
     602         854 : }
     603             : 
     604             : /* tests, whether x[1],...,x[I-1] is a partial automorphism, using scalar
     605             :  * product combinations and Bacher-polynomials depending on the chosen options,
     606             :  * puts the candidates for x[I] (i.e. the vectors vec such that the scalar
     607             :  * products of x[1],...,x[I-1],vec are correct) on CI, returns their number
     608             :  * (should be fp.diag[I]) */
     609             : static long
     610         182 : qfisom_candidates_novec(GEN CI, long I, GEN x, struct qfauto *qf,
     611             :        struct qfauto *qff, struct fingerprint *fp)
     612             : {
     613         182 :   pari_sp av = avma;
     614             :   long i, j, k, okp, okm, nr, fail;
     615         182 :   GEN vec, F =qf->F, V=qff->V, W=qff->W, v=qff->v;
     616         182 :   long n = lg(V)-1, f = lg(F)-1;
     617         182 :   vec = cgetg(I,t_VECSMALL);
     618       34888 :   for (i = 1; i <= fp->diag[I]; i++) CI[i] = 0; /* list for the candidates */
     619         182 :   nr = fail = 0;
     620      173999 :   for (j = 1; j <= n && fail == 0; j++)
     621             :   {
     622      173817 :     GEN Vj = gel(V,j), Wj = gel(W, j);
     623      173817 :     okp = okm = 0;
     624      347662 :     for (i = 1; i <= f; i++)
     625             :     {
     626      173845 :       GEN FAiI = gmael(F,i,fp->per[I]), FFvi = gel(v,i);
     627             :       /* vec is the vector of scalar products of V.v[j] with the first I base
     628             :          vectors x[0]...x[I-1] */
     629      173845 :       for (k = 1; k < I; k++)
     630             :       {
     631           0 :         long xk = x[k];
     632           0 :         if (xk > 0)
     633           0 :           vec[k] = zv_dotproduct(Vj, gel(FFvi,xk));
     634             :         else
     635           0 :           vec[k] = -zv_dotproduct(Vj, gel(FFvi,-xk));
     636             :       }
     637      173845 :       for (k = 1; k < I && vec[k] == FAiI[fp->per[k]]; k++);
     638      173845 :       if (k == I && Wj[i] == FAiI[fp->per[I]]) ++okp;
     639             :         /* V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
     640      173845 :       for (k = 1; k < I && vec[k] == -FAiI[fp->per[k]]; k++);
     641      173845 :       if (k == I && Wj[i] == FAiI[fp->per[I]]) ++okm;
     642             :         /* -V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
     643             :     }
     644      173817 :     if (okp == f) /* V.v[j] is a candidate for x[I] */
     645             :     {
     646       17353 :       if (nr < fp->diag[I])
     647       17353 :         CI[++nr] = j;
     648             :       else
     649           0 :         fail = 1; /* too many candidates */
     650             :     }
     651      173817 :     if (okm == f) /* -V.v[j] is a candidate for x[I] */
     652             :     {
     653       17353 :       if (nr < fp->diag[I])
     654       17353 :         CI[++nr] = -j;
     655             :       else
     656           0 :         fail = 1; /* too many candidates */
     657             :     }
     658             :   }
     659         182 :   return gc_long(av, fail? 0: nr);
     660             : }
     661             : 
     662             : static long
     663       14448 : qfisom_candidates(GEN CI, long I, GEN x, struct qfauto *qf,
     664             :       struct qfauto *qff, struct fingerprint *fp, struct qfcand *qfcand)
     665             : {
     666       14448 :   pari_sp av = avma;
     667             :   GEN vec, xvec, xbase, Fxbase, scpvec, com, list, trans, ccoef, cF;
     668       14448 :   GEN F =qf->F, V=qff->V, W=qff->W, v=qff->v, FF= qff->F;
     669             :   long i, j, k, okp, okm, nr, nc, vj, rank, num;
     670       14448 :   long dim = qf->dim, n = lg(V)-1, f = lg(F)-1;
     671       14448 :   long DEP = qfcand->cdep, len = f * DEP;
     672       14448 :   if (I >= 2 && I <= lg(qfcand->bacher_pol))
     673             :   {
     674         154 :     long t = labs(x[I-1]);
     675         154 :     GEN bpolI = gel(qfcand->bacher_pol,I-1);
     676         154 :     if (bachcomp(bpolI, t, V, W, gel(v,1)) == 0) return 0;
     677             :   }
     678       14406 :   if (I==1 || DEP ==0) return qfisom_candidates_novec(CI,I,x,qf,qff,fp);
     679       14224 :   vec = cgetg(I,t_VECSMALL);
     680       14224 :   scpvec = cgetg(len+1,t_VECSMALL);
     681       14224 :   com = gel(qfcand->comb,I-1);
     682       14224 :   list=gel(com,1); trans = gel(com,2); ccoef = gel(com,3); cF=gel(com,4);
     683       14224 :   rank = lg(trans)-1;
     684       14224 :   nc = lg(list)-2;
     685             :   /* xvec is the list of vector sums which are computed with respect to the
     686             :      partial basis in x */
     687       14224 :   xvec = zero_Flm_copy(dim,nc+1);
     688             :   /* xbase should be a basis for the lattice generated by the vectors in xvec,
     689             :      it is obtained via the transformation matrix comb[I-1].trans */
     690       14224 :   xbase = cgetg(rank+1,t_MAT);
     691       57470 :   for (i = 1; i <= rank; ++i)
     692       43246 :     gel(xbase,i) = cgetg(dim+1,t_VECSMALL);
     693       14224 :   Fxbase = cgetg(rank+1,t_MAT); /* product of a form F with the base xbase */
     694       57470 :   for (i = 1; i <= rank; ++i) gel(Fxbase,i) = cgetg(dim+1,t_VECSMALL);
     695       93121 :   for (i = 1; i <= fp->diag[I]; ++i) CI[i] = 0; /* list for the candidates */
     696       14224 :   nr = 0;
     697    15966461 :   for (j = 1; j <= n; ++j)
     698             :   {
     699             :     long sign;
     700    15952335 :     GEN Vj = gel(V,j), Wj = gel(W, j);
     701    15952335 :     okp = okm = 0;
     702    42984277 :     for (k = 1; k <= len; ++k) scpvec[k] = 0;
     703    31904712 :     for (i = 1; i <= f; ++i)
     704             :     {
     705    15952377 :       GEN FAiI = gmael(F,i,fp->per[I]), FFvi = gel(v,i);
     706             :       /* vec is the vector of scalar products of V.v[j] with the first I base
     707             :          vectors x[0]...x[I-1] */
     708   143665137 :       for (k = 1; k < I; ++k)
     709             :       {
     710   127712760 :         long xk = x[k];
     711   127712760 :         if (xk > 0)
     712    82658247 :           vec[k] = zv_dotproduct(Vj, gel(FFvi,xk));
     713             :         else
     714    45054513 :           vec[k] = -zv_dotproduct(Vj, gel(FFvi,-xk));
     715             :       }
     716    24756949 :       for (k = 1; k < I && vec[k] == FAiI[fp->per[k]]; ++k);
     717    15952377 :       if (k == I && Wj[i] == FAiI[fp->per[I]]) ++okp;
     718             :         /* V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
     719    24408258 :       for (k = 1; k < I && vec[k] == -FAiI[fp->per[k]]; ++k);
     720    15952377 :       if (k == I && Wj[i] == FAiI[fp->per[I]]) ++okm;
     721             :         /* -V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
     722    42511483 :       for (k = I-1; k >= 1 && k > I-1-DEP; --k)
     723    26559106 :         scpvec[(i-1)*DEP+I-k] = vec[k];
     724             :     }
     725             :     /* check, whether the scalar product combination scpvec is contained in the
     726             :        list comb[I-1].list */
     727    15952335 :     if (!zv_equal0(scpvec))
     728             :     {
     729    11397246 :       sign = zv_canon_inplace(scpvec);
     730             :       /* x[0..I-1] not a partial automorphism ? */
     731    11397246 :       if ((num = vecvecsmall_search(list,scpvec)) < 0) return gc_long(av,0);
     732             :       else
     733             :       {
     734    11397239 :         GEN xnum = gel(xvec,num);
     735   184970989 :         for (k = 1; k <= dim; ++k) xnum[k] += sign * Vj[k];
     736             :       }
     737             :     }
     738    15952328 :     if (okp == f) /* V.v[j] is a candidate for x[I] */
     739             :     {
     740       34433 :       if (nr >= fp->diag[I]) return gc_long(av,0); /* too many candidates */
     741       34419 :       CI[++nr] = j;
     742             :     }
     743    15952314 :     if (okm == f) /* -V.v[j] is a candidate for x[I] */
     744             :     {
     745       23128 :       if (nr >= fp->diag[I]) return gc_long(av,0); /* too many candidates */
     746       23051 :       CI[++nr] = -j;
     747             :     }
     748             :   }
     749       14126 :   if (nr == fp->diag[I])
     750             :   { /* compute the basis of the lattice generated by the vectors in xvec via
     751             :        the transformation matrix comb[I-1].trans */
     752       39935 :     for (i = 1; i <= rank; ++i)
     753             :     {
     754       30310 :       GEN comtri = gel(trans,i);
     755      470456 :       for (j = 1; j <= dim; ++j)
     756             :       {
     757      440146 :         long xbij = 0;
     758     6904926 :         for (k = 1; k <= nc+1; ++k) xbij += comtri[k] * mael(xvec,k,j);
     759      440146 :         mael(xbase,i,j) = xbij;
     760             :       }
     761             :     }
     762             :     /* check, whether the base xbase has the right scalar products */
     763       19236 :     for (i = 1; i <= f; ++i)
     764             :     {
     765       39949 :       for (j = 1; j <= rank; ++j)
     766      470477 :         for (k = 1; k <= dim; ++k)
     767      440160 :           mael(Fxbase,j,k) = zv_dotproduct(gmael(FF,i,k), gel(xbase,j));
     768       39844 :       for (j = 1; j <= rank; ++j)
     769      107030 :         for (k = 1; k <= j; ++k) /* a scalar product is wrong ? */
     770       76818 :           if (zv_dotproduct(gel(xbase,j), gel(Fxbase,k)) != mael3(cF,i,j,k))
     771          21 :             return gc_long(av, 0);
     772             :     }
     773      117208 :     for (i = 1; i <= nc+1; ++i)
     774             :     {
     775      107604 :       GEN comcoi = gel(ccoef,i);
     776     1667428 :       for (j = 1; j <= dim; ++j)
     777             :       {
     778     1559824 :         vj = 0;
     779     7989324 :         for (k = 1; k <= rank; ++k)
     780     6429500 :           vj += comcoi[k] * mael(xbase,k,j);
     781     1559824 :         if (vj != mael(xvec,i,j)) return gc_long(av,0); /* an entry is wrong */
     782             :       }
     783             :     }
     784             :   }
     785       14105 :   return gc_long(av, nr);
     786             : }
     787             : 
     788             : static long
     789        8057 : aut(long step, GEN x, GEN C, struct group *G, struct qfauto *qf,
     790             :     struct fingerprint *fp, struct qfcand *cand)
     791             : {
     792        8057 :   long dim = qf->dim;
     793             :   GEN orb;
     794             :   /* found new automorphism */
     795        8057 :   if (step == dim && mael(C,dim,1)) { x[dim] = mael(C,dim,1); return 1; }
     796        7357 :   orb = cgetg(2,t_VECSMALL);
     797       12782 :   while (mael(C,step,1))
     798             :   {
     799             :     long nbc;
     800             :     /* choose the image of the base-vector nr. step */
     801       11053 :     x[step] = mael(C,step,1);
     802             :     /* check, whether x[0..step] is a partial automorphism and compute
     803             :        the candidates for x[step+1] */
     804       11053 :     nbc = qfisom_candidates(gel(C,step+1), step+1, x, qf, qf, fp, cand);
     805       11053 :     if (nbc == fp->diag[step+1]
     806        7231 :         && aut(step+1, x, C, G, qf, fp, cand)) return 1;
     807        5425 :     orb[1] = x[step];
     808             :     /* delete chosen vector from list of candidates */
     809        5425 :     (void)orbdelete(gel(C,step), orb);
     810             :   }
     811        1729 :   return 0;
     812             : }
     813             : 
     814             : /* search new automorphisms until on all levels representatives for all orbits
     815             :  * have been tested */
     816             : static void
     817         126 : autom(struct group *G, struct qfauto *qf, struct fingerprint *fp,
     818             :       struct qfcand *cand)
     819             : {
     820             :   long i, j, step, im, nC, found, tries, nbad;
     821         126 :   GEN x, bad, H, V = qf->V;
     822         126 :   long dim = qf->dim, n = lg(V)-1, STAB = 1;
     823         126 :   GEN C = cgetg(dim+1,t_VEC);
     824             :   /* C[i] is the list of candidates for the image of the i-th base-vector */
     825        1442 :   for (i = 1; i <= dim; ++i)
     826        1316 :     gel(C,i) = cgetg(fp->diag[i]+1, t_VECSMALL);
     827             :   /* x is the new base i.e. x[i] is the index in V.v of the i-th base-vector */
     828         126 :   x = cgetg(dim+1, t_VECSMALL);
     829        1442 :   for (step = STAB; step <= dim; ++step)
     830             :   {
     831        1316 :     long nH = 0;
     832        1316 :     if (DEBUGLEVEL) err_printf("QFAuto: Step %ld/%ld\n",step,dim);
     833       10668 :     for (nH = 0, i = step; i <= dim; ++i)
     834        9352 :       nH += G->ng[i];
     835        1316 :     H = cgetg(nH+1,t_VEC);
     836       10668 :     for (nH = 0, i = step; i <= dim; ++i)
     837       20545 :       for (j = 1; j <= G->ng[i]; ++j)
     838       11193 :         gel(H,++nH) = gmael(G->g,i,j);
     839        1316 :     bad = zero_Flv(2*n);
     840        1316 :     nbad = 0;
     841             :     /* the first step base-vectors are fixed */
     842        9352 :     for (i = 1; i < step; ++i)
     843        8036 :       x[i] = fp->e[i];
     844             :     /* compute the candidates for x[step] */
     845        1316 :     if (fp->diag[step] > 1)
     846             :       /* if fp.diag[step] > 1 compute the candidates for x[step] */
     847        1064 :       nC = qfisom_candidates(gel(C,step), step, x, qf, qf, fp, cand);
     848             :     else
     849             :       /* if fp.diag[step] == 1, fp.e[step] is the only candidate */
     850             :     {
     851         252 :       mael(C,step,1) = fp->e[step];
     852         252 :       nC = 1;
     853             :     }
     854             :     /* delete the orbit of the step-th base-vector from the candidates */
     855        1316 :     nC = orbsubtract(gel(C,step), fp->e, step-1, 1, H, V, &(G->ord[step]));
     856        2877 :     while (nC > 0 && (im = mael(C,step,1)) != 0)
     857             :     {
     858        1561 :       found = 0;
     859             :       /* tries vector V.v[im] as image of the step-th base-vector */
     860        1561 :       x[step] = im;
     861        1561 :       if (step < dim)
     862             :       {
     863             :         long nbc;
     864             :         /* check, whether x[0]...x[step] is a partial basis and compute the
     865             :            candidates for x[step+1] */
     866        1526 :         nbc = qfisom_candidates(gel(C,step+1), step+1, x, qf, qf, fp, cand);
     867        1526 :         if (nbc == fp->diag[step+1])
     868             :           /* go into the recursion */
     869         826 :           found = aut(step+1, x, C, G, qf, fp, cand);
     870             :         else
     871         700 :           found = 0;
     872             :       }
     873             :       else
     874          35 :         found = 1;
     875        1561 :       if (!found) /* x[0..step] can not be continued to an automorphism */
     876             :       { /* delete the orbit of im from the candidates for x[step] */
     877         826 :         nC = orbsubtract(gel(C,step),mkvecsmall(im), 0, 1, H, V, NULL);
     878         826 :         bad[++nbad] = im;
     879             :       }
     880             :       else
     881             :       { /* new generator has been found */
     882             :         GEN Gstep;
     883         735 :         ++G->ng[step];
     884             :         /* append new generator to G->g[step] */
     885         735 :         Gstep = vec_lengthen(gel(G->g,step),G->ng[step]);
     886         735 :         gel(Gstep,G->ng[step]) = matgen(x, fp->per, V);
     887         735 :         gel(G->g,step) = Gstep;
     888         735 :         ++nH;
     889         735 :         H = cgetg(nH+1, t_VEC);
     890        7798 :         for (nH = 0, i = step; i <= dim; i++)
     891       12789 :           for (j = 1; j <= G->ng[i]; j++) gel(H,++nH) = gmael(G->g,i,j);
     892         735 :         nC = orbsubtract(gel(C,step), fp->e, step-1, 1, H, V, &(G->ord[step]));
     893         735 :         nC = orbsubtract(gel(C,step), bad, 0, nbad, H, V, NULL);
     894             :       }
     895             :     }
     896             :     /* test, whether on step STAB some generators may be omitted */
     897        1708 :     if (step == STAB) for (tries = G->nsg[step]+1; tries <= G->ng[step]; tries++)
     898             :     {
     899         392 :       nH = 0;
     900         462 :       for (j = 1; j < tries; j++)              gel(H,++nH) = gmael(G->g,step,j);
     901         882 :       for (j = tries+1; j <= G->ng[step]; j++) gel(H,++nH) = gmael(G->g,step,j);
     902        4606 :       for (i = step+1; i <= dim; i++)
     903        4214 :         for (j = 1; j <= G->ng[i]; j++) gel(H,++nH) = gmael(G->g,i,j);
     904         392 :       if (orbitlen(fp->e[step], G->ord[step], H, nH, V) == G->ord[step])
     905             :       { /* generator g[step][tries] can be omitted */
     906         196 :         G->ng[step]--;
     907         616 :         for (i = tries; i <= G->ng[step]; ++i)
     908         420 :           gmael(G->g,step,i) = gmael(G->g,step,i+1);
     909         196 :         tries--;
     910             :       }
     911             :     }
     912             :     /* calculate stabilizer elements fixing basis-vectors fp.e[0..step] */
     913        1316 :     if (step < dim && G->ord[step] > 1) stab(step, G, fp, V, qf->p);
     914             :   }
     915         126 : }
     916             : 
     917             : #define MAXENTRY (1L<<((BITS_IN_LONG-2)>>1))
     918             : #define MAXNORM (1L<<(BITS_IN_LONG-2))
     919             : 
     920             : static long
     921         462 : zm_maxdiag(GEN A)
     922             : {
     923         462 :   long dim = lg(A)-1, max = coeff(A,1,1), i;
     924        4851 :   for (i = 2; i <= dim; ++i)
     925        4389 :     if (coeff(A,i,i) > max) max = coeff(A,i,i);
     926         462 :   return max;
     927             : }
     928             : 
     929             : static GEN
     930         266 : init_qfauto(GEN F, GEN U, long max, struct qfauto *qf, GEN norm, GEN minvec)
     931             : {
     932          14 :   GEN V = minvec ? ZM_to_zm_canon(minvec)
     933         266 :                  : gel(minim_zm(zm_to_ZM(gel(F,1)), stoi(max), NULL), 3);
     934             :   GEN W, v;
     935         266 :   long i, j, k, n = lg(V)-1, f = lg(F)-1, dim = lg(gel(F,1))-1;
     936      245924 :   for (i = 1; i <= n; i++)
     937             :   {
     938      245658 :     GEN Vi = gel(V,i);
     939     4107936 :     for (k = 1; k <= dim; k++)
     940             :     {
     941     3862278 :       long l = labs(Vi[k]);
     942     3862278 :       if (l > max) max = l;
     943             :     }
     944             :   }
     945         266 :   if (max > MAXENTRY) pari_err_OVERFLOW("qfisom [lattice too large]");
     946         266 :   qf->p = unextprime(2*max+1);
     947         266 :   V = vecvecsmall_sort_uniq(V);
     948         266 :   if (!norm)
     949             :   {
     950         196 :     norm = cgetg(dim+1,t_VEC);
     951        2205 :     for (i = 1; i <= dim; i++)
     952             :     {
     953        2009 :       GEN Ni = cgetg(f+1,t_VECSMALL);
     954        4046 :       for (k = 1; k <= f; k++) Ni[k] = mael3(F,k,i,i);
     955        2009 :       gel(norm,i) = Ni;
     956             :     }
     957         196 :     norm = vecvecsmall_sort_uniq(norm);
     958             :   }
     959         266 :   W = checkvecs(V, F, norm);
     960         266 :   v = cgetg(f+1,t_VEC);
     961             :   /* the product of the maximal entry in the short vectors with the maximal
     962             :      entry in v[i] should not exceed MAXNORM to avoid overflow */
     963         266 :   max = MAXNORM / max;
     964         546 :   for (i = 1; i <= f; ++i)
     965             :   {
     966         280 :     GEN Fi = gel(F,i), vi = cgetg(n+1,t_MAT);
     967         280 :     gel(v,i) = vi;
     968      245966 :     for (j = 1; j <= n; ++j)
     969             :     {
     970      245686 :       GEN Vj = gel(V,j), vij = cgetg(dim+1, t_VECSMALL);
     971      245686 :       gel(vi,j) = vij;
     972     4108020 :       for (k = 1; k <= dim; ++k)
     973             :       {
     974     3862334 :         vij[k] = zv_dotproduct(gel(Fi,k), Vj);
     975     3862334 :         if (labs(vij[k]) > max) pari_err_OVERFLOW("qfisom [lattice too large]");
     976             :       }
     977             :     }
     978             :   }
     979         266 :   qf->dim = dim; qf->F = F; qf->V = V; qf->W = W; qf->v = v; qf->U = U;
     980         266 :   return norm;
     981             : }
     982             : 
     983             : static void
     984         126 : init_qfgroup(struct group *G, struct fingerprint *fp, struct qfauto *qf)
     985             : {
     986         126 :   GEN H, M, V = qf->V;
     987         126 :   long nH, i, j, k, dim = qf->dim;
     988         126 :   G->ng  = zero_Flv(dim+1);
     989         126 :   G->nsg = zero_Flv(dim+1);
     990         126 :   G->ord = cgetg(dim+1,t_VECSMALL);
     991         126 :   G->g = cgetg(dim+1,t_VEC);
     992        1442 :   for (i = 1; i <= dim; ++i) gel(G->g,i) = mkvec(gen_0);
     993         126 :   M = matid_Flm(dim);
     994         126 :   gmael(G->g,1,1) = M;
     995         126 :   G->ng[1] = 1;
     996             :   /* -Id is always an automorphism */
     997        1442 :   for (i = 1; i <= dim; i++) mael(M,i,i) = -1;
     998         126 :   nH = 0;
     999        1442 :   for (i = 1; i <= dim; i++) nH += G->ng[i];
    1000         126 :   H = cgetg(nH+1,t_MAT);
    1001             :   /* calculate the orbit lengths under the automorphisms known so far */
    1002        1442 :   for (i = 1; i <= dim; ++i)
    1003             :   {
    1004        1316 :     if (G->ng[i] > 0)
    1005             :     {
    1006         126 :       nH = 0;
    1007        1442 :       for (j = i; j <= dim; j++)
    1008        1442 :         for (k = 1; k <= G->ng[j]; k++) gel(H,++nH) = gmael(G->g,j,k);
    1009         126 :       G->ord[i] = orbitlen(fp->e[i], fp->diag[i], H, nH, V);
    1010             :     }
    1011             :     else
    1012        1190 :       G->ord[i] = 1;
    1013             :   }
    1014         126 : }
    1015             : 
    1016             : /* calculates the scalar products of the vector w with the base vectors
    1017             :  * v[b[I]] down to v[b[I-dep+1]] with respect to all invariant forms and puts
    1018             :  * them on scpvec  */
    1019             : static GEN
    1020     7046130 : scpvector(GEN w, GEN b, long I, long dep, GEN v)
    1021             : {
    1022     7046130 :   long  i, j, n = lg(v)-1;
    1023     7046130 :   GEN scpvec = zero_Flv(dep*n);
    1024    18731650 :   for (i = I; i >= 1 && i > I-dep; i--)
    1025             :   {
    1026    11685520 :     long bi = b[i];
    1027    11685520 :     if (bi > 0)
    1028    23371124 :       for (j = 1; j <= n; j++)
    1029    11685604 :         scpvec[1+(j-1)*dep + I-i] = zv_dotproduct(w, gmael(v,j,bi));
    1030             :     else
    1031           0 :       for (j = 1; j <= n; j++)
    1032           0 :         scpvec[1+(j-1)*dep + I-i] = -zv_dotproduct(w, gmael(v,j,-bi));
    1033             :   }
    1034     7046130 :   return scpvec;
    1035             : }
    1036             : 
    1037             : /* computes the list of scalar product combinations of the vectors
    1038             :  * in V.v with the basis-vectors in b */
    1039             : static GEN
    1040        5320 : scpvecs(GEN *pt_vec, long I, GEN b, long dep, struct qfauto *qf)
    1041             : {
    1042        5320 :   GEN list, vec, V = qf->V, F = qf->F, v = qf->v;
    1043        5320 :   long j, n = lg(V)-1, dim = lg(gel(F,1))-1, len = (lg(F)-1)*dep;
    1044             :   /* the first vector in the list is the 0-vector and is not counted */
    1045        5320 :   list = mkvec(zero_Flv(len));
    1046        5320 :   vec  = mkvec(zero_Flv(dim));
    1047     7051450 :   for (j = 1; j <= n; ++j)
    1048             :   {
    1049     7046130 :     GEN Vvj = gel(V,j), scpvec = scpvector(Vvj, b, I, dep, v);
    1050             :     long i, nr, sign;
    1051     7046130 :     if (zv_equal0(scpvec)) continue;
    1052     4856950 :     sign = zv_canon_inplace(scpvec);
    1053     4856950 :     nr = vecvecsmall_search(list, scpvec);
    1054     4856950 :     if (nr > 0) /* scpvec already in list */
    1055             :     {
    1056     4807908 :       GEN vecnr = gel(vec,nr);
    1057    80328948 :       for (i = 1; i <= dim; i++) vecnr[i] += sign * Vvj[i];
    1058             :     }
    1059             :     else /* scpvec is a new scalar product combination */
    1060             :     {
    1061       49042 :       nr = -nr;
    1062       49042 :       list = vec_insert(list,nr,scpvec);
    1063       49042 :       vec = vec_insert(vec,nr,sign < 0 ? zv_neg(Vvj) : zv_copy(Vvj));
    1064             :     }
    1065             :   }
    1066        5320 :   settyp(list,t_MAT);
    1067        5320 :   settyp(vec,t_MAT);
    1068        5320 :   *pt_vec = vec; return list;
    1069             : }
    1070             : 
    1071             : /* com->F[i] is the Gram-matrix of the basis b with respect to F.A[i] */
    1072             : static GEN
    1073        5166 : scpforms(GEN b, struct qfauto *qf)
    1074             : {
    1075        5166 :   GEN F = qf->F;
    1076        5166 :   long i, j, k, n = lg(F)-1, dim = lg(gel(F,1))-1, nb = lg(b)-1;
    1077        5166 :   GEN gram = cgetg(n+1, t_VEC), Fbi = cgetg(nb+1, t_MAT);
    1078             :   /* list of products of F.A[i] with the vectors in b */
    1079       18375 :   for (j = 1; j <= nb; j++) gel(Fbi, j) = cgetg(dim+1, t_VECSMALL);
    1080       10360 :   for (i = 1; i <= n; i++)
    1081             :   {
    1082        5194 :     GEN FAi = gel(F,i);
    1083        5194 :     gel(gram, i) = cgetg(nb+1, t_MAT);
    1084       18445 :     for (j = 1; j <= nb; j++)
    1085      201446 :       for (k = 1; k <= dim; k++)
    1086      188195 :         mael(Fbi,j,k) = zv_dotproduct(gel(FAi,k), gel(b,j));
    1087       18445 :     for (j = 1; j <= nb; j++)
    1088             :     {
    1089       13251 :       GEN Gij = cgetg(nb+1, t_VECSMALL);
    1090       60074 :       for (k = 1; k <= nb; k++) Gij[k] = zv_dotproduct(gel(b,j), gel(Fbi,k));
    1091       13251 :       gmael(gram,i,j) = Gij;
    1092             :     }
    1093             :   }
    1094        5166 :   return gram;
    1095             : }
    1096             : 
    1097             : static GEN
    1098         588 : gen_comb(long cdep, GEN A, GEN e, struct qfauto *qf, long lim)
    1099             : {
    1100         588 :   long i, dim = lg(A)-1;
    1101         588 :   GEN comb = cgetg(dim+1,t_VEC);
    1102        5754 :   for (i = 1; i <= dim; ++i)
    1103             :   {
    1104        5320 :     pari_sp av = avma;
    1105             :     GEN trans, ccoef, cF, B, BI, sumveclist, sumvecbase;
    1106        5320 :     GEN list = scpvecs(&sumveclist, i, e, cdep, qf);
    1107        5320 :     GEN M = zm_to_ZM(sumveclist);
    1108        5320 :     GEN T = ZM_lll(qf_ZM_apply(A,M), .99, LLL_NOFLATTER | LLL_IM | LLL_GRAM);
    1109        5320 :     if (lim && lg(T)-1>=lim) return NULL;
    1110        5166 :     B = ZM_mul(M,T);
    1111        5166 :     BI = RgM_inv(B);
    1112        5166 :     sumvecbase = ZM_trunc_to_zm(B);
    1113        5166 :     trans = ZM_trunc_to_zm(T);
    1114        5166 :     ccoef = ZM_trunc_to_zm(RgM_mul(BI,M));
    1115        5166 :     cF = scpforms(sumvecbase, qf);
    1116        5166 :     gel(comb,i) = gc_GEN(av, mkvec4(list, trans, ccoef, cF));
    1117             :   }
    1118         434 :   return comb;
    1119             : }
    1120             : 
    1121             : static void
    1122         154 : init_comb(struct qfcand *cand, GEN A, GEN e, struct qfauto *qf)
    1123             : {
    1124         154 :   long dim = lg(A)-1;
    1125         154 :   GEN Am = zm_to_ZM(A);
    1126         392 :   for (cand->cdep = 1; ; cand->cdep++)
    1127             :   {
    1128         392 :     cand->comb = gen_comb(cand->cdep, Am, e, qf, (dim+1)>>1);
    1129         392 :     if (!cand->comb) break;
    1130             :   }
    1131         154 :   cand->cdep= maxss(1, cand->cdep-1);
    1132         154 :   cand->comb = gen_comb(cand->cdep, Am, e, qf, 0);
    1133         154 : }
    1134             : 
    1135             : static void
    1136         196 : init_flags(struct qfcand *cand, GEN A, struct fingerprint *fp,
    1137             :                                        struct qfauto *qf, GEN flags)
    1138             : {
    1139         196 :   if (!flags)
    1140             :   {
    1141         154 :     init_comb(cand, A, fp->e, qf);
    1142         154 :     cand->bacher_pol = init_bacher(0, fp, qf);
    1143             :   }
    1144             :   else
    1145             :   {
    1146             :     long cdep, bach;
    1147          42 :     if (typ(flags)!=t_VEC || lg(flags)!=3) pari_err_TYPE("qfisominit",flags);
    1148          42 :     cdep = gtos(gel(flags,1));
    1149          42 :     bach = minss(gtos(gel(flags,2)), lg(fp->e)-1);
    1150          42 :     if (cdep<0 || bach<0) pari_err_FLAG("qfisom");
    1151          42 :     cand->cdep = cdep;
    1152          42 :     cand->comb = cdep? gen_comb(cdep, zm_to_ZM(A), fp->e, qf, 0): NULL;
    1153          42 :     cand->bacher_pol = init_bacher(bach, fp, qf);
    1154             :   }
    1155         196 : }
    1156             : 
    1157             : static GEN
    1158         126 : gen_group(struct group *G, GEN U)
    1159             : {
    1160         126 :   GEN V, o = gen_1;
    1161         126 :   long i, j, n, dim = lg(G->ord)-1;
    1162        1442 :   for (i = 1; i <= dim; i++) o = muliu(o, G->ord[i]);
    1163        1442 :   for (i = n = 1; i <= dim; i++) n += G->ng[i] - G->nsg[i];
    1164         126 :   V = cgetg(n, t_VEC);
    1165        1442 :   for (i = n = 1; i <= dim; ++i)
    1166        1981 :     for (j=G->nsg[i]+1; j<=G->ng[i]; j++)
    1167         665 :       gel(V,n++) = U ? zm_mul(gel(U,1), zm_mul(gmael(G->g,i,j), gel(U,2)))
    1168         665 :                      : gmael(G->g,i,j);
    1169         126 :   return mkvec2(o, V);
    1170             : }
    1171             : 
    1172             : static long
    1173         476 : is_qfisom(GEN F)
    1174             : {
    1175         189 :   return (lg(F)==6 && typ(F)==t_VEC && typ(gel(F,1))==t_VEC
    1176         665 :                    && typ(gel(F,3))==t_VEC && typ(gel(F,4))==t_VEC);
    1177             : }
    1178             : 
    1179             : static GEN
    1180          84 : unpack_qfisominit(GEN F, GEN *norm, struct qfauto *qf,
    1181             :       struct fingerprint *fp, struct qfcand *cand)
    1182             : {
    1183          84 :   GEN QF = gel(F,3);
    1184          84 :   qf->F = gel(QF,1);
    1185          84 :   qf->V = gel(QF,2);
    1186          84 :   qf->W = gel(QF,3);
    1187          84 :   qf->v = gel(QF,4);
    1188          84 :   qf->p = itou(gel(QF,5));
    1189          84 :   qf->U = lg(QF)>6 ? gel(QF,6):NULL;
    1190          84 :   QF = gel(F,4);
    1191          84 :   fp->diag = gel(QF,1);
    1192          84 :   fp->per  = gel(QF,2);
    1193          84 :   fp->e    = gel(QF,3);
    1194          84 :   QF = gel(F,5);
    1195          84 :   cand->cdep =itos(gel(QF,1));
    1196          84 :   cand->comb = gel(QF,2);
    1197          84 :   cand->bacher_pol = gel(QF,3);
    1198          84 :   *norm = gel(F,2);
    1199          84 :   qf->dim = lg(gmael(F,1,1))-1;
    1200          84 :   return qf->F;
    1201             : }
    1202             : 
    1203             : static GEN
    1204         182 : qfisom_bestmat(GEN A, long *pt_max)
    1205             : {
    1206         182 :   long max = zm_maxdiag(A), max2;
    1207         182 :   GEN A2, A1 = zm_to_ZM(A), U = lllgramint(A1);
    1208         182 :   if (lg(U) != lg(A1))
    1209           0 :     pari_err_DOMAIN("qfisom","form","is not",
    1210             :                     strtoGENstr("positive definite"), A1);
    1211         182 :   A2 = ZM_to_zm(qf_ZM_apply(A1, U));
    1212         182 :   max2 = zm_maxdiag(A2);
    1213         182 :   if (max2 >= max) { *pt_max = max; return NULL; }
    1214          56 :   *pt_max = max2; return mkvec2(ZM_to_zm(U), ZM_to_zm(ZM_inv(U,NULL)));
    1215             : }
    1216             : 
    1217             : static GEN
    1218         280 : init_qfisom(GEN F, struct fingerprint *fp, struct qfcand *cand,
    1219             :             struct qfauto *qf, GEN flags, long *max, GEN minvec)
    1220             : {
    1221             :   GEN U, A, norm;
    1222         280 :   if (is_qfisom(F))
    1223             :   {
    1224          84 :     F = unpack_qfisominit(F, &norm, qf, fp, cand);
    1225          84 :     A = gel(F,1);
    1226          84 :     *max = zm_maxdiag(A);
    1227          84 :     if (flags)
    1228           0 :       init_flags(cand, A, fp, qf, flags);
    1229             :   }
    1230             :   else
    1231             :   {
    1232         196 :     if (lg(F)<2) pari_err_TYPE("qfisom",F);
    1233         196 :     A = gel(F,1);
    1234         196 :     if (lg(A)<2) pari_err_TYPE("qfisom",A);
    1235         196 :     if (!minvec)
    1236             :     {
    1237         182 :       U = qfisom_bestmat(A, max);
    1238         182 :       if (DEBUGLEVEL) err_printf("QFIsom: max=%ld\n",*max);
    1239         182 :       if (U) F = zmV_apply_zm(F, gel(U,1));
    1240             :     } else
    1241             :     {
    1242          14 :       *max = zm_maxdiag(A); U = NULL;
    1243          14 :       if (typ(minvec)==t_VEC && lg(minvec)==4 && typ(gel(minvec,2))==t_INT)
    1244             :       {
    1245           7 :         long n = itos(gel(minvec,2));
    1246           7 :         if (n != *max)
    1247           0 :           pari_err_DOMAIN("qfisominit","m[2]","!=",stoi(*max),stoi(n));
    1248           7 :         minvec = gel(minvec, 3);
    1249             :       }
    1250          14 :       if (typ(minvec)!=t_MAT || lg(gel(minvec,1))!=lg(A))
    1251           0 :         pari_err_TYPE("qfisominit",minvec);
    1252             :     }
    1253         196 :     norm = init_qfauto(F, U, *max, qf, NULL, minvec);
    1254         196 :     fingerprint(fp, qf);
    1255         196 :     if (DEBUGLEVEL) err_printf("QFIsom: fp=%Ps\n",fp->diag);
    1256         196 :     init_flags(cand, A, fp, qf, flags);
    1257             :   }
    1258         280 :   return norm;
    1259             : }
    1260             : 
    1261             : GEN
    1262         126 : qfauto(GEN F, GEN flags)
    1263             : {
    1264         126 :   pari_sp av = avma;
    1265             :   struct fingerprint fp;
    1266             :   struct group G;
    1267             :   struct qfcand cand;
    1268             :   struct qfauto qf;
    1269             :   long max;
    1270         126 :   (void)init_qfisom(F, &fp, &cand, &qf, flags, &max, NULL);
    1271         126 :   init_qfgroup(&G, &fp, &qf);
    1272         126 :   autom(&G, &qf, &fp, &cand);
    1273         126 :   return gc_GEN(av, gen_group(&G, qf.U));
    1274             : }
    1275             : 
    1276             : static GEN
    1277         301 : qf_to_zmV(GEN F)
    1278             : {
    1279         301 :   switch(typ(F))
    1280             :   {
    1281         252 :     case t_MAT: return RgM_is_ZM(F)? mkvec(ZM_to_zm(F)): NULL;
    1282          49 :     case t_VEC: return RgV_is_ZMV(F)? ZMV_to_zmV(F): NULL;
    1283             :   }
    1284           0 :   return NULL;
    1285             : }
    1286             : 
    1287             : GEN
    1288         126 : qfauto0(GEN x, GEN flags)
    1289             : {
    1290         126 :   pari_sp av = avma;
    1291             :   GEN F, G;
    1292         126 :   if (is_qfisom(x)) F = x;
    1293             :   else
    1294             :   {
    1295          77 :     F = qf_to_zmV(x);
    1296          77 :     if (!F) pari_err_TYPE("qfauto",x);
    1297             :   }
    1298         126 :   G = qfauto(F, flags);
    1299         126 :   return gc_GEN(av, mkvec2(gel(G,1), zmV_to_ZMV(gel(G,2))));
    1300             : }
    1301             : /* computes the orbit of V.v[pt] under the generators G[0],...,G[nG-1] and
    1302             :  * elements stabilizing V.v[pt], which are stored in H, returns the number of
    1303             :  * generators in H */
    1304             : static GEN
    1305         609 : isostab(long pt, GEN G, GEN V, long Maxfail, ulong p)
    1306             : {
    1307         609 :   pari_sp av = avma;
    1308             :   long  i, im, nH, fail, len, cnd, orblen, rpt;
    1309         609 :   long dim = lg(gel(V,1))-1, n = lg(V)-1, nG = lg(G)-1;
    1310             :   GEN w, flag, orb, H;
    1311         609 :   H = cgetg(2,t_VEC); /* generators of the stabilizer of V.v[pt] */
    1312         609 :   nH = 0;
    1313         609 :   w = cgetg(2*n+2,t_MAT); /* w[i+V.n] is a matrix that maps V.v[pt] on V.v[i] */
    1314         609 :   orb = zero_Flv(2*n);
    1315         609 :   orblen = 1; /* length of the orbit of a random vector in V.v */
    1316         609 :   flag = zero_Flv(2*n+1); /* if flag[i+V.n] = 1, then i is already in orbit */
    1317         609 :   orb[1] = pt;
    1318         609 :   flag[orb[1]+n+1] = 1;
    1319             :   /* w[pt+V.n] is the Identity */
    1320         609 :   gel(w,orb[1]+n+1) = matid_Flm(dim);
    1321         609 :   cnd = len = 1;
    1322         609 :   fail = 0; /* number of successive failures */
    1323        1568 :   while (cnd <= len && fail < Maxfail)
    1324             :   {
    1325        2674 :     for (i = 1; i <= nG && fail < Maxfail; i++)
    1326             :     {
    1327        1715 :       im = operate(orb[cnd], gel(G,i), V);
    1328        1715 :       if (flag[im+n+1] == 0)
    1329             :       { /* new element is found, append to the orbit and store an element
    1330             :          * mapping V.v[pt] to im in w[im+V.n] */
    1331         434 :         orb[++len] = im;
    1332         434 :         flag[im+n+1] = 1;
    1333         434 :         gel(w,im+n+1) = zm_mul(gel(G,i), gel(w,orb[cnd]+n+1));
    1334             :       }
    1335             :       else
    1336             :       { /* image was already in orbit */
    1337        1281 :         GEN B = zm_mul(gel(G,i), gel(w,orb[cnd]+n+1));
    1338             :         /* check whether the old and the new element mapping pt on im differ */
    1339        1281 :         if (!zvV_equal(B, gel(w,im+n+1)))
    1340             :         {
    1341             :           long tmplen;
    1342        1008 :           gel(H,nH+1) = zm_divmod(gel(w,im+n+1),B,p);
    1343        1008 :           rpt = 1+(long)random_Fl(n);
    1344        1008 :           tmplen = orbitlen(rpt, 2*n, H, nH+1, V);
    1345       11137 :           while (tmplen < orblen)
    1346             :           { /* orbit of this vector is shorter than a previous one:
    1347             :              * choose new random vector */
    1348       10129 :             rpt = 1+(long)random_Fl(n);
    1349       10129 :             tmplen = orbitlen(rpt, 2*n, H, nH+1, V);
    1350             :           }
    1351        1008 :           if (tmplen > orblen)
    1352             :           { /* new stabilizer element H[nH] enlarges the group generated by H */
    1353         196 :             orblen = tmplen;
    1354         196 :             H = vec_lengthen(H, (++nH)+1); /* allocate for new generator */
    1355         196 :             fail = 0;
    1356             :           }
    1357             :           else /* new stabilizer element does not enlarge random vector orbit */
    1358         812 :             ++fail;
    1359             :         }
    1360             :         /* if H[nH] is the identity, do nothing */
    1361             :       }
    1362             :     }
    1363         959 :     ++cnd;
    1364             :   }
    1365         609 :   setlg(H, nH+1); return gc_GEN(av, H);
    1366             : }
    1367             : 
    1368             : /* the heart of the program: the recursion */
    1369             : static long
    1370         665 : iso(long step, GEN x, GEN C, struct qfauto *qf, struct qfauto *qff,
    1371             :     struct fingerprint *fp, GEN G, struct qfcand *cand)
    1372             : {
    1373         665 :   long dim = qf->dim;
    1374             :   /* found isomorphism */
    1375         665 :   if (step == dim && mael(C,dim,1)) { x[dim] = mael(C,dim,1); return 1; }
    1376         749 :   while (mael(C,step,1))
    1377             :   {
    1378             :     long nbc;
    1379             :     /* choose the image of the base-vector nr. step */
    1380         749 :     x[step] = mael(C,step,1);
    1381             :     /* check whether x[0]...x[step] is a partial automorphism and compute the
    1382             :        candidates for x[step+1] */
    1383         749 :     nbc = qfisom_candidates(gel(C,step+1), step+1, x, qf, qff, fp, cand);
    1384         749 :     if (nbc == fp->diag[step+1])
    1385             :     { /* go deeper into the recursion */
    1386         609 :       long i, Maxfail = 0;
    1387             :       GEN H;
    1388             :       /* heuristic value of Maxfail for break condition in isostab */
    1389        5019 :       for (i = 1; i <= step; ++i)
    1390        4410 :         if (fp->diag[i] > 1) Maxfail++;
    1391        5019 :       for (; i <= dim; ++i)
    1392        4410 :         if (fp->diag[i] > 1) Maxfail += 2;
    1393             :       /* compute the stabilizer H of x[step] in G */
    1394         609 :       H = isostab(x[step], G, qff->V, Maxfail,qff->p);
    1395         609 :       if (iso(step+1, x, C, qf, qff, fp, H, cand)) return 1;
    1396             :     }
    1397             :     /* delete the orbit of the chosen vector from the list of candidates */
    1398         140 :     orbsubtract(gel(C,step), x, step-1, 1, G, qff->V, NULL);
    1399             :   }
    1400           0 :   return 0;
    1401             : }
    1402             : 
    1403             : /* search for an isometry */
    1404             : static GEN
    1405          56 : isometry(struct qfauto *qf, struct qfauto *qff, struct fingerprint *fp, GEN G,
    1406             :          struct qfcand *cand)
    1407             : 
    1408             : {
    1409          56 :   long i, dim = qf->dim;
    1410          56 :   GEN x, C = cgetg(dim+1,t_VEC);
    1411             :   /* C[i] is the list of candidates for the image of the i-th base-vector */
    1412         721 :   for (i = 1; i <= dim; ++i) gel(C,i) = cgetg(fp->diag[i]+1, t_VECSMALL);
    1413          56 :   x = cgetg(dim+1, t_VECSMALL);
    1414             :   /* compute the candidates for x[1] */
    1415          56 :   qfisom_candidates(gel(C,1), 1, x, qf, qff, fp, cand);
    1416          56 :   return iso(1, x, C, qf, qff, fp, G, cand)? matgen(x, fp->per, qff->V): NULL;
    1417             : }
    1418             : 
    1419             : GEN
    1420          84 : qfisominit(GEN F, GEN flags, GEN minvec)
    1421             : {
    1422          84 :   pari_sp av = avma;
    1423             :   struct fingerprint fp;
    1424             :   struct qfauto qf;
    1425             :   struct qfcand cand;
    1426             :   long max;
    1427          84 :   GEN norm = init_qfisom(F, &fp, &cand, &qf, flags, &max, minvec);
    1428         168 :   return gc_GEN(av, mkvec5(F, norm,
    1429          84 :                           mkvecn(qf.U?6:5, qf.F, qf.V, qf.W, qf.v, utoi(qf.p), qf.U),
    1430             :                           mkvec3(fp.diag, fp.per, fp.e),
    1431          84 :                           mkvec3(stoi(cand.cdep),cand.comb?cand.comb:cgetg(1,t_VEC),
    1432             :                                  cand.bacher_pol)));
    1433             : }
    1434             : 
    1435             : GEN
    1436          84 : qfisominit0(GEN x, GEN flags, GEN minvec)
    1437             : {
    1438          84 :   pari_sp av = avma;
    1439          84 :   GEN F = qf_to_zmV(x);
    1440          84 :   if (!F) pari_err_TYPE("qfisom",x);
    1441          84 :   return gerepileupto(av, qfisominit(F, flags, minvec));
    1442             : }
    1443             : 
    1444             : GEN
    1445          70 : qfisom(GEN F, GEN FF, GEN flags, GEN G)
    1446             : {
    1447          70 :   pari_sp av = avma;
    1448             :   struct fingerprint fp;
    1449             :   GEN res, detf, detff;
    1450             :   struct qfauto qf, qff;
    1451             :   struct qfcand cand;
    1452             :   long max;
    1453          70 :   GEN norm = init_qfisom(F, &fp, &cand, &qf, flags, &max, NULL);
    1454          70 :   init_qfauto(FF, NULL, max, &qff, norm, NULL);
    1455          70 :   detf = ZM_det(zm_to_ZM(gel(qf.F,1)));
    1456          70 :   detff = ZM_det(zm_to_ZM(gel(qff.F,1)));
    1457          70 :   if (lg(qf.W)!=lg(qff.W) || !equalii(detf, detff)
    1458          56 :       || !zvV_equal(vecvecsmall_sort_shallow(qf.W),
    1459          14 :                     vecvecsmall_sort_shallow(qff.W))) return gc_const(av,gen_0);
    1460          56 :   if (!G) G = mkvec(scalar_Flm(-1, qff.dim));
    1461          56 :   res = isometry(&qf, &qff, &fp, G, &cand);
    1462          56 :   if (!res) return gc_const(av, gen_0);
    1463          56 :   return gc_GEN(av, zm_to_ZM(qf.U? zm_mul(res,gel(qf.U, 2)): res));
    1464             : }
    1465             : 
    1466             : static GEN
    1467          35 : check_qfauto(GEN G)
    1468             : {
    1469          35 :   if (typ(G)==t_VEC && lg(G)==3 && typ(gel(G,1))==t_INT) G = gel(G,2);
    1470          35 :   return qf_to_zmV(G);
    1471             : }
    1472             : 
    1473             : GEN
    1474          70 : qfisom0(GEN x, GEN y, GEN flags, GEN G)
    1475             : {
    1476          70 :   pari_sp av = avma;
    1477             :   GEN F, FF;
    1478          70 :   if (is_qfisom(x)) F = x;
    1479             :   else
    1480             :   {
    1481          35 :     F = qf_to_zmV(x);
    1482          35 :     if (!F) pari_err_TYPE("qfisom",x);
    1483             :   }
    1484          70 :   FF = qf_to_zmV(y);
    1485          70 :   if (!FF) pari_err_TYPE("qfisom",y);
    1486          70 :   if (G) G = check_qfauto(G);
    1487          70 :   return gerepileupto(av, qfisom(F, FF, flags, G));
    1488             : }
    1489             : 
    1490             : static GEN
    1491          42 : ZM_to_GAP(GEN M)
    1492             : {
    1493          42 :   pari_sp av = avma;
    1494          42 :   long i, j, c, rows = nbrows(M), cols = lg(M)-1;
    1495          42 :   GEN comma = strtoGENstr(", "), bra = strtoGENstr("["), ket = strtoGENstr("]");
    1496          42 :   GEN s = cgetg(2*rows*cols+2*rows+2,t_VEC);
    1497          42 :   gel(s,1) = bra; c=2;
    1498         210 :   for (i = 1; i <= rows; i++)
    1499             :   {
    1500         168 :     if (i > 1) gel(s,c++) = comma;
    1501         168 :     gel(s,c++) = bra;
    1502         840 :     for (j = 1; j <= cols; j++)
    1503             :     {
    1504         672 :       if (j > 1) gel(s,c++) = comma;
    1505         672 :       gel(s,c++) = GENtoGENstr(gcoeff(M,i,j));
    1506             :     }
    1507         168 :     gel(s,c++) = ket;
    1508             :   }
    1509          42 :   gel(s,c++) = ket;
    1510          42 :   return gc_GEN(av, shallowconcat1(s));
    1511             : }
    1512             : 
    1513             : GEN
    1514          14 : qfautoexport(GEN G, long flag)
    1515             : {
    1516          14 :   pari_sp av = avma;
    1517          14 :   long i, lgen,  c = 2;
    1518          14 :   GEN gen, str, comma = strtoGENstr(", ");
    1519          14 :   if (typ(G)!=t_VEC || lg(G)!=3) pari_err_TYPE("qfautoexport", G);
    1520          14 :   if (flag!=0 && flag!=1) pari_err_FLAG("qfautoexport");
    1521          14 :   gen = gel(G,2); lgen = lg(gen)-1;
    1522          14 :   str = cgetg(2+2*lgen,t_VEC);
    1523             :   /* in GAP or MAGMA the matrix group is called BG */
    1524          14 :   if (flag == 0)
    1525           7 :     gel(str,1) = strtoGENstr("Group(");
    1526             :   else
    1527             :   {
    1528           7 :     long dim = lg(gmael(gen,1,1))-1;
    1529           7 :     gel(str,1) = gsprintf("MatrixGroup<%d, Integers() |",dim);
    1530             :   }
    1531          56 :   for (i = 1; i <= lgen; i++)
    1532             :   {
    1533          42 :     if (i!=1) gel(str,c++) = comma;
    1534          42 :     gel(str,c++) = ZM_to_GAP(gel(gen,i));
    1535             :   }
    1536          14 :   gel(str,c++) = strtoGENstr(flag ? ">":")");
    1537          14 :   return gc_GEN(av, shallowconcat1(str));
    1538             : }
    1539             : 
    1540             : GEN
    1541          21 : qforbits(GEN G, GEN V)
    1542             : {
    1543          21 :   pari_sp av = avma;
    1544             :   GEN gen, w, W, p, v, orb, o;
    1545          21 :   long i, j, n, ng, nborbits = 0;
    1546          21 :   gen = check_qfauto(G);
    1547          21 :   if (!gen) pari_err_TYPE("qforbits", G);
    1548          21 :   if (typ(V)==t_VEC && lg(V)==4
    1549           7 :       && typ(gel(V,1))==t_INT && typ(gel(V,2))==t_INT) V = gel(V,3);
    1550          21 :   if (typ(V)!=t_MAT || !RgM_is_ZM(V)) pari_err_TYPE("qforbits", V);
    1551          21 :   n = lg(V)-1; ng = lg(gen)-1;
    1552          21 :   W = ZM_to_zm_canon(V);
    1553          21 :   p = vecvecsmall_indexsort(W);
    1554          21 :   v = vecpermute(W, p);
    1555          21 :   w = zero_zv(n);
    1556          21 :   orb = cgetg(n+1, t_VEC);
    1557          21 :   o = cgetg(n+1, t_VECSMALL);
    1558          21 :   if (lg(v) != lg(V)) return gen_0;
    1559         357 :   for (i = 1; i <= n; i++)
    1560             :   {
    1561         336 :     long cnd, no = 1;
    1562             :     GEN T;
    1563         336 :     if (w[i]) continue;
    1564          42 :     w[i] = ++nborbits;
    1565          42 :     o[1] = i;
    1566         378 :     for (cnd=1; cnd <= no; ++cnd)
    1567        3696 :       for (j=1; j <= ng; j++)
    1568             :       {
    1569        3360 :         GEN Vij = zm_zc_mul(gel(gen, j), gel(v, o[cnd]));
    1570             :         long k;
    1571        3360 :         (void) zv_canon_inplace(Vij);
    1572        3360 :         if ((k = vecvecsmall_search(v, Vij)) < 0) return gc_const(av, gen_0);
    1573        3360 :         if (w[k] == 0) { o[++no] = k; w[k] = nborbits; }
    1574             :       }
    1575          42 :     gel(orb, nborbits) = T = cgetg(no+1, t_VEC);
    1576         378 :     for (j=1; j<=no; j++) gel(T,j) = gel(V,p[o[j]]);
    1577             :   }
    1578          21 :   setlg(orb, nborbits+1); return gc_GEN(av, orb);
    1579             : }

Generated by: LCOV version 1.16