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