github.com/aergoio/aergo@v1.3.1/libtool/src/gmp-6.1.2/tests/rand/statlib.c (about)

     1  /* statlib.c -- Statistical functions for testing the randomness of
     2     number sequences. */
     3  
     4  /*
     5  Copyright 1999, 2000 Free Software Foundation, Inc.
     6  
     7  This file is part of the GNU MP Library test suite.
     8  
     9  The GNU MP Library test suite is free software; you can redistribute it
    10  and/or modify it under the terms of the GNU General Public License as
    11  published by the Free Software Foundation; either version 3 of the License,
    12  or (at your option) any later version.
    13  
    14  The GNU MP Library test suite is distributed in the hope that it will be
    15  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    17  Public License for more details.
    18  
    19  You should have received a copy of the GNU General Public License along with
    20  the GNU MP Library test suite.  If not, see https://www.gnu.org/licenses/.  */
    21  
    22  /* The theories for these functions are taken from D. Knuth's "The Art
    23  of Computer Programming: Volume 2, Seminumerical Algorithms", Third
    24  Edition, Addison Wesley, 1998. */
    25  
    26  /* Implementation notes.
    27  
    28  The Kolmogorov-Smirnov test.
    29  
    30  Eq. (13) in Knuth, p. 50, says that if X1, X2, ..., Xn are independent
    31  observations arranged into ascending order
    32  
    33  	Kp = sqr(n) * max(j/n - F(Xj))		for all 1<=j<=n
    34  	Km = sqr(n) * max(F(Xj) - (j-1)/n))	for all 1<=j<=n
    35  
    36  where F(x) = Pr(X <= x) = probability that (X <= x), which for a
    37  uniformly distributed random real number between zero and one is
    38  exactly the number itself (x).
    39  
    40  
    41  The answer to exercise 23 gives the following implementation, which
    42  doesn't need the observations to be sorted in ascending order:
    43  
    44  for (k = 0; k < m; k++)
    45  	a[k] = 1.0
    46  	b[k] = 0.0
    47  	c[k] = 0
    48  
    49  for (each observation Xj)
    50  	Y = F(Xj)
    51  	k = floor (m * Y)
    52  	a[k] = min (a[k], Y)
    53  	b[k] = max (b[k], Y)
    54  	c[k] += 1
    55  
    56  	j = 0
    57  	rp = rm = 0
    58  	for (k = 0; k < m; k++)
    59  		if (c[k] > 0)
    60  			rm = max (rm, a[k] - j/n)
    61  			j += c[k]
    62  			rp = max (rp, j/n - b[k])
    63  
    64  Kp = sqr (n) * rp
    65  Km = sqr (n) * rm
    66  
    67  */
    68  
    69  #include <stdio.h>
    70  #include <stdlib.h>
    71  #include <math.h>
    72  
    73  #include "gmp.h"
    74  #include "gmpstat.h"
    75  
    76  /* ks (Kp, Km, X, P, n) -- Perform a Kolmogorov-Smirnov test on the N
    77     real numbers between zero and one in vector X.  P is the
    78     distribution function, called for each entry in X, which should
    79     calculate the probability of X being greater than or equal to any
    80     number in the sequence.  (For a uniformly distributed sequence of
    81     real numbers between zero and one, this is simply equal to X.)  The
    82     result is put in Kp and Km.  */
    83  
    84  void
    85  ks (mpf_t Kp,
    86      mpf_t Km,
    87      mpf_t X[],
    88      void (P) (mpf_t, mpf_t),
    89      unsigned long int n)
    90  {
    91    mpf_t Kt;			/* temp */
    92    mpf_t f_x;
    93    mpf_t f_j;			/* j */
    94    mpf_t f_jnq;			/* j/n or (j-1)/n */
    95    unsigned long int j;
    96  
    97    /* Sort the vector in ascending order. */
    98    qsort (X, n, sizeof (__mpf_struct), mpf_cmp);
    99  
   100    /* K-S test. */
   101    /*	Kp = sqr(n) * max(j/n - F(Xj))		for all 1<=j<=n
   102  	Km = sqr(n) * max(F(Xj) - (j-1)/n))	for all 1<=j<=n
   103    */
   104  
   105    mpf_init (Kt); mpf_init (f_x); mpf_init (f_j); mpf_init (f_jnq);
   106    mpf_set_ui (Kp, 0);  mpf_set_ui (Km, 0);
   107    for (j = 1; j <= n; j++)
   108      {
   109        P (f_x, X[j-1]);
   110        mpf_set_ui (f_j, j);
   111  
   112        mpf_div_ui (f_jnq, f_j, n);
   113        mpf_sub (Kt, f_jnq, f_x);
   114        if (mpf_cmp (Kt, Kp) > 0)
   115  	mpf_set (Kp, Kt);
   116        if (g_debug > DEBUG_2)
   117  	{
   118  	  printf ("j=%lu ", j);
   119  	  printf ("P()="); mpf_out_str (stdout, 10, 2, f_x); printf ("\t");
   120  
   121  	  printf ("jnq="); mpf_out_str (stdout, 10, 2, f_jnq); printf (" ");
   122  	  printf ("diff="); mpf_out_str (stdout, 10, 2, Kt); printf (" ");
   123  	  printf ("Kp="); mpf_out_str (stdout, 10, 2, Kp); printf ("\t");
   124  	}
   125        mpf_sub_ui (f_j, f_j, 1);
   126        mpf_div_ui (f_jnq, f_j, n);
   127        mpf_sub (Kt, f_x, f_jnq);
   128        if (mpf_cmp (Kt, Km) > 0)
   129  	mpf_set (Km, Kt);
   130  
   131        if (g_debug > DEBUG_2)
   132  	{
   133  	  printf ("jnq="); mpf_out_str (stdout, 10, 2, f_jnq); printf (" ");
   134  	  printf ("diff="); mpf_out_str (stdout, 10, 2, Kt); printf (" ");
   135  	  printf ("Km="); mpf_out_str (stdout, 10, 2, Km); printf (" ");
   136  	  printf ("\n");
   137  	}
   138      }
   139    mpf_sqrt_ui (Kt, n);
   140    mpf_mul (Kp, Kp, Kt);
   141    mpf_mul (Km, Km, Kt);
   142  
   143    mpf_clear (Kt); mpf_clear (f_x); mpf_clear (f_j); mpf_clear (f_jnq);
   144  }
   145  
   146  /* ks_table(val, n) -- calculate probability for Kp/Km less than or
   147     equal to VAL with N observations.  See [Knuth section 3.3.1] */
   148  
   149  void
   150  ks_table (mpf_t p, mpf_t val, const unsigned int n)
   151  {
   152    /* We use Eq. (27), Knuth p.58, skipping O(1/n) for simplicity.
   153       This shortcut will result in too high probabilities, especially
   154       when n is small.
   155  
   156       Pr(Kp(n) <= s) = 1 - pow(e, -2*s^2) * (1 - 2/3*s/sqrt(n) + O(1/n)) */
   157  
   158    /* We have 's' in variable VAL and store the result in P. */
   159  
   160    mpf_t t1, t2;
   161  
   162    mpf_init (t1); mpf_init (t2);
   163  
   164    /* t1 = 1 - 2/3 * s/sqrt(n) */
   165    mpf_sqrt_ui (t1, n);
   166    mpf_div (t1, val, t1);
   167    mpf_mul_ui (t1, t1, 2);
   168    mpf_div_ui (t1, t1, 3);
   169    mpf_ui_sub (t1, 1, t1);
   170  
   171    /* t2 = pow(e, -2*s^2) */
   172  #ifndef OLDGMP
   173    mpf_pow_ui (t2, val, 2);	/* t2 = s^2 */
   174    mpf_set_d (t2, exp (-(2.0 * mpf_get_d (t2))));
   175  #else
   176    /* hmmm, gmp doesn't have pow() for floats.  use doubles. */
   177    mpf_set_d (t2, pow (M_E, -(2 * pow (mpf_get_d (val), 2))));
   178  #endif
   179  
   180    /* p = 1 - t1 * t2 */
   181    mpf_mul (t1, t1, t2);
   182    mpf_ui_sub (p, 1, t1);
   183  
   184    mpf_clear (t1); mpf_clear (t2);
   185  }
   186  
   187  static double x2_table_X[][7] = {
   188    { -2.33, -1.64, -.674, 0.0, 0.674, 1.64, 2.33 }, /* x */
   189    { 5.4289, 2.6896, .454276, 0.0, .454276, 2.6896, 5.4289} /* x^2 */
   190  };
   191  
   192  #define _2D3 ((double) .6666666666)
   193  
   194  /* x2_table (t, v, n) -- return chi-square table row for V in T[]. */
   195  void
   196  x2_table (double t[],
   197  	  unsigned int v)
   198  {
   199    int f;
   200  
   201  
   202    /* FIXME: Do a table lookup for v <= 30 since the following formula
   203       [Knuth, vol 2, 3.3.1] is only good for v > 30. */
   204  
   205    /* value = v + sqrt(2*v) * X[p] + (2/3) * X[p]^2 - 2/3 + O(1/sqrt(t) */
   206    /* NOTE: The O() term is ignored for simplicity. */
   207  
   208    for (f = 0; f < 7; f++)
   209        t[f] =
   210  	v +
   211  	sqrt (2 * v) * x2_table_X[0][f] +
   212  	_2D3 * x2_table_X[1][f] - _2D3;
   213  }
   214  
   215  
   216  /* P(p, x) -- Distribution function.  Calculate the probability of X
   217  being greater than or equal to any number in the sequence.  For a
   218  random real number between zero and one given by a uniformly
   219  distributed random number generator, this is simply equal to X. */
   220  
   221  static void
   222  P (mpf_t p, mpf_t x)
   223  {
   224    mpf_set (p, x);
   225  }
   226  
   227  /* mpf_freqt() -- Frequency test using KS on N real numbers between zero
   228     and one.  See [Knuth vol 2, p.61]. */
   229  void
   230  mpf_freqt (mpf_t Kp,
   231  	   mpf_t Km,
   232  	   mpf_t X[],
   233  	   const unsigned long int n)
   234  {
   235    ks (Kp, Km, X, P, n);
   236  }
   237  
   238  
   239  /* The Chi-square test.  Eq. (8) in Knuth vol. 2 says that if Y[]
   240     holds the observations and p[] is the probability for.. (to be
   241     continued!)
   242  
   243     V = 1/n * sum((s=1 to k) Y[s]^2 / p[s]) - n */
   244  
   245  void
   246  x2 (mpf_t V,			/* result */
   247      unsigned long int X[],	/* data */
   248      unsigned int k,		/* #of categories */
   249      void (P) (mpf_t, unsigned long int, void *), /* probability func */
   250      void *x,			/* extra user data passed to P() */
   251      unsigned long int n)	/* #of samples */
   252  {
   253    unsigned int f;
   254    mpf_t f_t, f_t2;		/* temp floats */
   255  
   256    mpf_init (f_t); mpf_init (f_t2);
   257  
   258  
   259    mpf_set_ui (V, 0);
   260    for (f = 0; f < k; f++)
   261      {
   262        if (g_debug > DEBUG_2)
   263  	fprintf (stderr, "%u: P()=", f);
   264        mpf_set_ui (f_t, X[f]);
   265        mpf_mul (f_t, f_t, f_t);	/* f_t = X[f]^2 */
   266        P (f_t2, f, x);		/* f_t2 = Pr(f) */
   267        if (g_debug > DEBUG_2)
   268  	mpf_out_str (stderr, 10, 2, f_t2);
   269        mpf_div (f_t, f_t, f_t2);
   270        mpf_add (V, V, f_t);
   271        if (g_debug > DEBUG_2)
   272  	{
   273  	  fprintf (stderr, "\tV=");
   274  	  mpf_out_str (stderr, 10, 2, V);
   275  	  fprintf (stderr, "\t");
   276  	}
   277      }
   278    if (g_debug > DEBUG_2)
   279      fprintf (stderr, "\n");
   280    mpf_div_ui (V, V, n);
   281    mpf_sub_ui (V, V, n);
   282  
   283    mpf_clear (f_t); mpf_clear (f_t2);
   284  }
   285  
   286  /* Pzf(p, s, x) -- Probability for category S in mpz_freqt().  It's
   287     1/d for all S.  X is a pointer to an unsigned int holding 'd'. */
   288  static void
   289  Pzf (mpf_t p, unsigned long int s, void *x)
   290  {
   291    mpf_set_ui (p, 1);
   292    mpf_div_ui (p, p, *((unsigned int *) x));
   293  }
   294  
   295  /* mpz_freqt(V, X, imax, n) -- Frequency test on integers.  [Knuth,
   296     vol 2, 3.3.2].  Keep IMAX low on this one, since we loop from 0 to
   297     IMAX.  128 or 256 could be nice.
   298  
   299     X[] must not contain numbers outside the range 0 <= X <= IMAX.
   300  
   301     Return value is number of observations actually used, after
   302     discarding entries out of range.
   303  
   304     Since X[] contains integers between zero and IMAX, inclusive, we
   305     have IMAX+1 categories.
   306  
   307     Note that N should be at least 5*IMAX.  Result is put in V and can
   308     be compared to output from x2_table (v=IMAX). */
   309  
   310  unsigned long int
   311  mpz_freqt (mpf_t V,
   312  	   mpz_t X[],
   313  	   unsigned int imax,
   314  	   const unsigned long int n)
   315  {
   316    unsigned long int *v;		/* result */
   317    unsigned int f;
   318    unsigned int d;		/* number of categories = imax+1 */
   319    unsigned int uitemp;
   320    unsigned long int usedn;
   321  
   322  
   323    d = imax + 1;
   324  
   325    v = (unsigned long int *) calloc (imax + 1, sizeof (unsigned long int));
   326    if (NULL == v)
   327      {
   328        fprintf (stderr, "mpz_freqt(): out of memory\n");
   329        exit (1);
   330      }
   331  
   332    /* count */
   333    usedn = n;			/* actual number of observations */
   334    for (f = 0; f < n; f++)
   335      {
   336        uitemp = mpz_get_ui(X[f]);
   337        if (uitemp > imax)	/* sanity check */
   338  	{
   339  	  if (g_debug)
   340  	    fprintf (stderr, "mpz_freqt(): warning: input insanity: %u, "\
   341  		     "ignored.\n", uitemp);
   342  	  usedn--;
   343  	  continue;
   344  	}
   345        v[uitemp]++;
   346      }
   347  
   348    if (g_debug > DEBUG_2)
   349      {
   350        fprintf (stderr, "counts:\n");
   351        for (f = 0; f <= imax; f++)
   352  	fprintf (stderr, "%u:\t%lu\n", f, v[f]);
   353      }
   354  
   355    /* chi-square with k=imax+1 and P(x)=1/(imax+1) for all x.*/
   356    x2 (V, v, d, Pzf, (void *) &d, usedn);
   357  
   358    free (v);
   359    return (usedn);
   360  }
   361  
   362  /* debug dummy to drag in dump funcs */
   363  void
   364  foo_debug ()
   365  {
   366    if (0)
   367      {
   368        mpf_dump (0);
   369  #ifndef OLDGMP
   370        mpz_dump (0);
   371  #endif
   372      }
   373  }
   374  
   375  /* merit (rop, t, v, m) -- calculate merit for spectral test result in
   376     dimension T, see Knuth p. 105.  BUGS: Only valid for 2 <= T <=
   377     6. */
   378  void
   379  merit (mpf_t rop, unsigned int t, mpf_t v, mpz_t m)
   380  {
   381    int f;
   382    mpf_t f_m, f_const, f_pi;
   383  
   384    mpf_init (f_m);
   385    mpf_set_z (f_m, m);
   386    mpf_init_set_d (f_const, M_PI);
   387    mpf_init_set_d (f_pi, M_PI);
   388  
   389    switch (t)
   390      {
   391      case 2:			/* PI */
   392        break;
   393      case 3:			/* PI * 4/3 */
   394        mpf_mul_ui (f_const, f_const, 4);
   395        mpf_div_ui (f_const, f_const, 3);
   396        break;
   397      case 4:			/* PI^2 * 1/2 */
   398        mpf_mul (f_const, f_const, f_pi);
   399        mpf_div_ui (f_const, f_const, 2);
   400        break;
   401      case 5:			/* PI^2 * 8/15 */
   402        mpf_mul (f_const, f_const, f_pi);
   403        mpf_mul_ui (f_const, f_const, 8);
   404        mpf_div_ui (f_const, f_const, 15);
   405        break;
   406      case 6:			/* PI^3 * 1/6 */
   407        mpf_mul (f_const, f_const, f_pi);
   408        mpf_mul (f_const, f_const, f_pi);
   409        mpf_div_ui (f_const, f_const, 6);
   410        break;
   411      default:
   412        fprintf (stderr,
   413  	       "spect (merit): can't calculate merit for dimensions > 6\n");
   414        mpf_set_ui (f_const, 0);
   415        break;
   416      }
   417  
   418    /* rop = v^t */
   419    mpf_set (rop, v);
   420    for (f = 1; f < t; f++)
   421      mpf_mul (rop, rop, v);
   422    mpf_mul (rop, rop, f_const);
   423    mpf_div (rop, rop, f_m);
   424  
   425    mpf_clear (f_m);
   426    mpf_clear (f_const);
   427    mpf_clear (f_pi);
   428  }
   429  
   430  double
   431  merit_u (unsigned int t, mpf_t v, mpz_t m)
   432  {
   433    mpf_t rop;
   434    double res;
   435  
   436    mpf_init (rop);
   437    merit (rop, t, v, m);
   438    res = mpf_get_d (rop);
   439    mpf_clear (rop);
   440    return res;
   441  }
   442  
   443  /* f_floor (rop, op) -- Set rop = floor (op). */
   444  void
   445  f_floor (mpf_t rop, mpf_t op)
   446  {
   447    mpz_t z;
   448  
   449    mpz_init (z);
   450  
   451    /* No mpf_floor().  Convert to mpz and back. */
   452    mpz_set_f (z, op);
   453    mpf_set_z (rop, z);
   454  
   455    mpz_clear (z);
   456  }
   457  
   458  
   459  /* vz_dot (rop, v1, v2, nelem) -- compute dot product of z-vectors V1,
   460     V2.  N is number of elements in vectors V1 and V2. */
   461  
   462  void
   463  vz_dot (mpz_t rop, mpz_t V1[], mpz_t V2[], unsigned int n)
   464  {
   465    mpz_t t;
   466  
   467    mpz_init (t);
   468    mpz_set_ui (rop, 0);
   469    while (n--)
   470      {
   471        mpz_mul (t, V1[n], V2[n]);
   472        mpz_add (rop, rop, t);
   473      }
   474  
   475    mpz_clear (t);
   476  }
   477  
   478  void
   479  spectral_test (mpf_t rop[], unsigned int T, mpz_t a, mpz_t m)
   480  {
   481    /* Knuth "Seminumerical Algorithms, Third Edition", section 3.3.4
   482       (pp. 101-103). */
   483  
   484    /* v[t] = min { sqrt (x[1]^2 + ... + x[t]^2) |
   485       x[1] + a*x[2] + ... + pow (a, t-1) * x[t] is congruent to 0 (mod m) } */
   486  
   487  
   488    /* Variables. */
   489    unsigned int ui_t;
   490    unsigned int ui_i, ui_j, ui_k, ui_l;
   491    mpf_t f_tmp1, f_tmp2;
   492    mpz_t tmp1, tmp2, tmp3;
   493    mpz_t U[GMP_SPECT_MAXT][GMP_SPECT_MAXT],
   494      V[GMP_SPECT_MAXT][GMP_SPECT_MAXT],
   495      X[GMP_SPECT_MAXT],
   496      Y[GMP_SPECT_MAXT],
   497      Z[GMP_SPECT_MAXT];
   498    mpz_t h, hp, r, s, p, pp, q, u, v;
   499  
   500    /* GMP inits. */
   501    mpf_init (f_tmp1);
   502    mpf_init (f_tmp2);
   503    for (ui_i = 0; ui_i < GMP_SPECT_MAXT; ui_i++)
   504      {
   505        for (ui_j = 0; ui_j < GMP_SPECT_MAXT; ui_j++)
   506  	{
   507  	  mpz_init_set_ui (U[ui_i][ui_j], 0);
   508  	  mpz_init_set_ui (V[ui_i][ui_j], 0);
   509  	}
   510        mpz_init_set_ui (X[ui_i], 0);
   511        mpz_init_set_ui (Y[ui_i], 0);
   512        mpz_init (Z[ui_i]);
   513      }
   514    mpz_init (tmp1);
   515    mpz_init (tmp2);
   516    mpz_init (tmp3);
   517    mpz_init (h);
   518    mpz_init (hp);
   519    mpz_init (r);
   520    mpz_init (s);
   521    mpz_init (p);
   522    mpz_init (pp);
   523    mpz_init (q);
   524    mpz_init (u);
   525    mpz_init (v);
   526  
   527    /* Implementation inits. */
   528    if (T > GMP_SPECT_MAXT)
   529      T = GMP_SPECT_MAXT;			/* FIXME: Lazy. */
   530  
   531    /* S1 [Initialize.] */
   532    ui_t = 2 - 1;			/* NOTE: `t' in description == ui_t + 1
   533  				   for easy indexing */
   534    mpz_set (h, a);
   535    mpz_set (hp, m);
   536    mpz_set_ui (p, 1);
   537    mpz_set_ui (pp, 0);
   538    mpz_set (r, a);
   539    mpz_pow_ui (s, a, 2);
   540    mpz_add_ui (s, s, 1);		/* s = 1 + a^2 */
   541  
   542    /* S2 [Euclidean step.] */
   543    while (1)
   544      {
   545        if (g_debug > DEBUG_1)
   546  	{
   547  	  mpz_mul (tmp1, h, pp);
   548  	  mpz_mul (tmp2, hp, p);
   549  	  mpz_sub (tmp1, tmp1, tmp2);
   550  	  if (mpz_cmpabs (m, tmp1))
   551  	    {
   552  	      printf ("***BUG***: h*pp - hp*p = ");
   553  	      mpz_out_str (stdout, 10, tmp1);
   554  	      printf ("\n");
   555  	    }
   556  	}
   557        if (g_debug > DEBUG_2)
   558  	{
   559  	  printf ("hp = ");
   560  	  mpz_out_str (stdout, 10, hp);
   561  	  printf ("\nh = ");
   562  	  mpz_out_str (stdout, 10, h);
   563  	  printf ("\n");
   564  	  fflush (stdout);
   565  	}
   566  
   567        if (mpz_sgn (h))
   568  	mpz_tdiv_q (q, hp, h);	/* q = floor(hp/h) */
   569        else
   570  	mpz_set_ui (q, 1);
   571  
   572        if (g_debug > DEBUG_2)
   573  	{
   574  	  printf ("q = ");
   575  	  mpz_out_str (stdout, 10, q);
   576  	  printf ("\n");
   577  	  fflush (stdout);
   578  	}
   579  
   580        mpz_mul (tmp1, q, h);
   581        mpz_sub (u, hp, tmp1);	/* u = hp - q*h */
   582  
   583        mpz_mul (tmp1, q, p);
   584        mpz_sub (v, pp, tmp1);	/* v = pp - q*p */
   585  
   586        mpz_pow_ui (tmp1, u, 2);
   587        mpz_pow_ui (tmp2, v, 2);
   588        mpz_add (tmp1, tmp1, tmp2);
   589        if (mpz_cmp (tmp1, s) < 0)
   590  	{
   591  	  mpz_set (s, tmp1);	/* s = u^2 + v^2 */
   592  	  mpz_set (hp, h);	/* hp = h */
   593  	  mpz_set (h, u);	/* h = u */
   594  	  mpz_set (pp, p);	/* pp = p */
   595  	  mpz_set (p, v);	/* p = v */
   596  	}
   597        else
   598  	break;
   599      }
   600  
   601    /* S3 [Compute v2.] */
   602    mpz_sub (u, u, h);
   603    mpz_sub (v, v, p);
   604  
   605    mpz_pow_ui (tmp1, u, 2);
   606    mpz_pow_ui (tmp2, v, 2);
   607    mpz_add (tmp1, tmp1, tmp2);
   608    if (mpz_cmp (tmp1, s) < 0)
   609      {
   610        mpz_set (s, tmp1);	/* s = u^2 + v^2 */
   611        mpz_set (hp, u);
   612        mpz_set (pp, v);
   613      }
   614    mpf_set_z (f_tmp1, s);
   615    mpf_sqrt (rop[ui_t - 1], f_tmp1);
   616  
   617    /* S4 [Advance t.] */
   618    mpz_neg (U[0][0], h);
   619    mpz_set (U[0][1], p);
   620    mpz_neg (U[1][0], hp);
   621    mpz_set (U[1][1], pp);
   622  
   623    mpz_set (V[0][0], pp);
   624    mpz_set (V[0][1], hp);
   625    mpz_neg (V[1][0], p);
   626    mpz_neg (V[1][1], h);
   627    if (mpz_cmp_ui (pp, 0) > 0)
   628      {
   629        mpz_neg (V[0][0], V[0][0]);
   630        mpz_neg (V[0][1], V[0][1]);
   631        mpz_neg (V[1][0], V[1][0]);
   632        mpz_neg (V[1][1], V[1][1]);
   633      }
   634  
   635    while (ui_t + 1 != T)		/* S4 loop */
   636      {
   637        ui_t++;
   638        mpz_mul (r, a, r);
   639        mpz_mod (r, r, m);
   640  
   641        /* Add new row and column to U and V.  They are initialized with
   642  	 all elements set to zero, so clearing is not necessary. */
   643  
   644        mpz_neg (U[ui_t][0], r); /* U: First col in new row. */
   645        mpz_set_ui (U[ui_t][ui_t], 1); /* U: Last col in new row. */
   646  
   647        mpz_set (V[ui_t][ui_t], m); /* V: Last col in new row. */
   648  
   649        /* "Finally, for 1 <= i < t,
   650  	   set q = round (vi1 * r / m),
   651  	   vit = vi1*r - q*m,
   652  	   and Ut=Ut+q*Ui */
   653  
   654        for (ui_i = 0; ui_i < ui_t; ui_i++)
   655  	{
   656  	  mpz_mul (tmp1, V[ui_i][0], r); /* tmp1=vi1*r */
   657  	  zdiv_round (q, tmp1, m); /* q=round(vi1*r/m) */
   658  	  mpz_mul (tmp2, q, m);	/* tmp2=q*m */
   659  	  mpz_sub (V[ui_i][ui_t], tmp1, tmp2);
   660  
   661  	  for (ui_j = 0; ui_j <= ui_t; ui_j++) /* U[t] = U[t] + q*U[i] */
   662  	    {
   663  	      mpz_mul (tmp1, q, U[ui_i][ui_j]);	/* tmp=q*uij */
   664  	      mpz_add (U[ui_t][ui_j], U[ui_t][ui_j], tmp1); /* utj = utj + q*uij */
   665  	    }
   666  	}
   667  
   668        /* s = min (s, zdot (U[t], U[t]) */
   669        vz_dot (tmp1, U[ui_t], U[ui_t], ui_t + 1);
   670        if (mpz_cmp (tmp1, s) < 0)
   671  	mpz_set (s, tmp1);
   672  
   673        ui_k = ui_t;
   674        ui_j = 0;			/* WARNING: ui_j no longer a temp. */
   675  
   676        /* S5 [Transform.] */
   677        if (g_debug > DEBUG_2)
   678  	printf ("(t, k, j, q1, q2, ...)\n");
   679        do
   680  	{
   681  	  if (g_debug > DEBUG_2)
   682  	    printf ("(%u, %u, %u", ui_t + 1, ui_k + 1, ui_j + 1);
   683  
   684  	  for (ui_i = 0; ui_i <= ui_t; ui_i++)
   685  	    {
   686  	      if (ui_i != ui_j)
   687  		{
   688  		  vz_dot (tmp1, V[ui_i], V[ui_j], ui_t + 1); /* tmp1=dot(Vi,Vj). */
   689  		  mpz_abs (tmp2, tmp1);
   690  		  mpz_mul_ui (tmp2, tmp2, 2); /* tmp2 = 2*abs(dot(Vi,Vj) */
   691  		  vz_dot (tmp3, V[ui_j], V[ui_j], ui_t + 1); /* tmp3=dot(Vj,Vj). */
   692  
   693  		  if (mpz_cmp (tmp2, tmp3) > 0)
   694  		    {
   695  		      zdiv_round (q, tmp1, tmp3); /* q=round(Vi.Vj/Vj.Vj) */
   696  		      if (g_debug > DEBUG_2)
   697  			{
   698  			  printf (", ");
   699  			  mpz_out_str (stdout, 10, q);
   700  			}
   701  
   702  		      for (ui_l = 0; ui_l <= ui_t; ui_l++)
   703  			{
   704  			  mpz_mul (tmp1, q, V[ui_j][ui_l]);
   705  			  mpz_sub (V[ui_i][ui_l], V[ui_i][ui_l], tmp1); /* Vi=Vi-q*Vj */
   706  			  mpz_mul (tmp1, q, U[ui_i][ui_l]);
   707  			  mpz_add (U[ui_j][ui_l], U[ui_j][ui_l], tmp1); /* Uj=Uj+q*Ui */
   708  			}
   709  
   710  		      vz_dot (tmp1, U[ui_j], U[ui_j], ui_t + 1); /* tmp1=dot(Uj,Uj) */
   711  		      if (mpz_cmp (tmp1, s) < 0) /* s = min(s,dot(Uj,Uj)) */
   712  			mpz_set (s, tmp1);
   713  		      ui_k = ui_j;
   714  		    }
   715  		  else if (g_debug > DEBUG_2)
   716  		    printf (", #"); /* 2|Vi.Vj| <= Vj.Vj */
   717  		}
   718  	      else if (g_debug > DEBUG_2)
   719  		printf (", *");	/* i == j */
   720  	    }
   721  
   722  	  if (g_debug > DEBUG_2)
   723  	    printf (")\n");
   724  
   725  	  /* S6 [Advance j.] */
   726  	  if (ui_j == ui_t)
   727  	    ui_j = 0;
   728  	  else
   729  	    ui_j++;
   730  	}
   731        while (ui_j != ui_k);	/* S5 */
   732  
   733        /* From Knuth p. 104: "The exhaustive search in steps S8-S10
   734  	 reduces the value of s only rarely." */
   735  #ifdef DO_SEARCH
   736        /* S7 [Prepare for search.] */
   737        /* Find minimum in (x[1], ..., x[t]) satisfying condition
   738  	 x[k]^2 <= f(y[1], ...,y[t]) * dot(V[k],V[k]) */
   739  
   740        ui_k = ui_t;
   741        if (g_debug > DEBUG_2)
   742  	{
   743  	  printf ("searching...");
   744  	  /*for (f = 0; f < ui_t*/
   745  	  fflush (stdout);
   746  	}
   747  
   748        /* Z[i] = floor (sqrt (floor (dot(V[i],V[i]) * s / m^2))); */
   749        mpz_pow_ui (tmp1, m, 2);
   750        mpf_set_z (f_tmp1, tmp1);
   751        mpf_set_z (f_tmp2, s);
   752        mpf_div (f_tmp1, f_tmp2, f_tmp1);	/* f_tmp1 = s/m^2 */
   753        for (ui_i = 0; ui_i <= ui_t; ui_i++)
   754  	{
   755  	  vz_dot (tmp1, V[ui_i], V[ui_i], ui_t + 1);
   756  	  mpf_set_z (f_tmp2, tmp1);
   757  	  mpf_mul (f_tmp2, f_tmp2, f_tmp1);
   758  	  f_floor (f_tmp2, f_tmp2);
   759  	  mpf_sqrt (f_tmp2, f_tmp2);
   760  	  mpz_set_f (Z[ui_i], f_tmp2);
   761  	}
   762  
   763        /* S8 [Advance X[k].] */
   764        do
   765  	{
   766  	  if (g_debug > DEBUG_2)
   767  	    {
   768  	      printf ("X[%u] = ", ui_k);
   769  	      mpz_out_str (stdout, 10, X[ui_k]);
   770  	      printf ("\tZ[%u] = ", ui_k);
   771  	      mpz_out_str (stdout, 10, Z[ui_k]);
   772  	      printf ("\n");
   773  	      fflush (stdout);
   774  	    }
   775  
   776  	  if (mpz_cmp (X[ui_k], Z[ui_k]))
   777  	    {
   778  	      mpz_add_ui (X[ui_k], X[ui_k], 1);
   779  	      for (ui_i = 0; ui_i <= ui_t; ui_i++)
   780  		mpz_add (Y[ui_i], Y[ui_i], U[ui_k][ui_i]);
   781  
   782  	      /* S9 [Advance k.] */
   783  	      while (++ui_k <= ui_t)
   784  		{
   785  		  mpz_neg (X[ui_k], Z[ui_k]);
   786  		  mpz_mul_ui (tmp1, Z[ui_k], 2);
   787  		  for (ui_i = 0; ui_i <= ui_t; ui_i++)
   788  		    {
   789  		      mpz_mul (tmp2, tmp1, U[ui_k][ui_i]);
   790  		      mpz_sub (Y[ui_i], Y[ui_i], tmp2);
   791  		    }
   792  		}
   793  	      vz_dot (tmp1, Y, Y, ui_t + 1);
   794  	      if (mpz_cmp (tmp1, s) < 0)
   795  		mpz_set (s, tmp1);
   796  	    }
   797  	}
   798        while (--ui_k);
   799  #endif /* DO_SEARCH */
   800        mpf_set_z (f_tmp1, s);
   801        mpf_sqrt (rop[ui_t - 1], f_tmp1);
   802  #ifdef DO_SEARCH
   803        if (g_debug > DEBUG_2)
   804  	printf ("done.\n");
   805  #endif /* DO_SEARCH */
   806      } /* S4 loop */
   807  
   808    /* Clear GMP variables. */
   809  
   810    mpf_clear (f_tmp1);
   811    mpf_clear (f_tmp2);
   812    for (ui_i = 0; ui_i < GMP_SPECT_MAXT; ui_i++)
   813      {
   814        for (ui_j = 0; ui_j < GMP_SPECT_MAXT; ui_j++)
   815  	{
   816  	  mpz_clear (U[ui_i][ui_j]);
   817  	  mpz_clear (V[ui_i][ui_j]);
   818  	}
   819        mpz_clear (X[ui_i]);
   820        mpz_clear (Y[ui_i]);
   821        mpz_clear (Z[ui_i]);
   822      }
   823    mpz_clear (tmp1);
   824    mpz_clear (tmp2);
   825    mpz_clear (tmp3);
   826    mpz_clear (h);
   827    mpz_clear (hp);
   828    mpz_clear (r);
   829    mpz_clear (s);
   830    mpz_clear (p);
   831    mpz_clear (pp);
   832    mpz_clear (q);
   833    mpz_clear (u);
   834    mpz_clear (v);
   835  
   836    return;
   837  }