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