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