1/* statlib.c -- Statistical functions for testing the randomness of
2   number sequences. */
3
4/*
5Copyright 1999, 2000 Free Software Foundation, Inc.
6
7This file is part of the GNU MP Library test suite.
8
9The GNU MP Library test suite is free software; you can redistribute it
10and/or modify it under the terms of the GNU General Public License as
11published by the Free Software Foundation; either version 3 of the License,
12or (at your option) any later version.
13
14The GNU MP Library test suite is distributed in the hope that it will be
15useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
17Public License for more details.
18
19You should have received a copy of the GNU General Public License along with
20the 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
23of Computer Programming: Volume 2, Seminumerical Algorithms", Third
24Edition, Addison Wesley, 1998. */
25
26/* Implementation notes.
27
28The Kolmogorov-Smirnov test.
29
30Eq. (13) in Knuth, p. 50, says that if X1, X2, ..., Xn are independent
31observations 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
36where F(x) = Pr(X <= x) = probability that (X <= x), which for a
37uniformly distributed random real number between zero and one is
38exactly the number itself (x).
39
40
41The answer to exercise 23 gives the following implementation, which
42doesn't need the observations to be sorted in ascending order:
43
44for (k = 0; k < m; k++)
45	a[k] = 1.0
46	b[k] = 0.0
47	c[k] = 0
48
49for (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
64Kp = sqr (n) * rp
65Km = 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
83void
84ks (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
148void
149ks_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
186static 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[]. */
194void
195x2_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
216being greater than or equal to any number in the sequence.  For a
217random real number between zero and one given by a uniformly
218distributed random number generator, this is simply equal to X. */
219
220static void
221P (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]. */
228void
229mpf_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
244void
245x2 (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'. */
287static void
288Pzf (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
309unsigned long int
310mpz_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 */
362void
363foo_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. */
377void
378merit (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
429double
430merit_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). */
443void
444f_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
461void
462vz_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
477void
478spectral_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