/* bernoulli.c: example program; computes bernoulli numbers mod p Copyright (C) 2007, 2008, David Harvey This file is part of the zn_poly library (version 0.8). This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "zn_poly.h" /* Finds distinct prime factors of n, in the range k < p <= n. Assumes that n does not have any prime factors p <= k. Stores them in increasing order starting at res. Returns number of factors written. */ unsigned prime_factors_helper(ulong* res, ulong k, ulong n) { if (n == 1) return 0; ulong i; for (i = k + 1; i * i <= n; i++) { if (n % i == 0) { // found a factor *res = i; // remove that factor entirely for (n /= i; n % i == 0; n /= i); return prime_factors_helper(res + 1, i, n) + 1; } } // no more factors *res = n; return 1; } /* Finds distinct prime factors of n. Writes them to res in increasing order, returns number of factors found. */ unsigned prime_factors(ulong* res, ulong n) { return prime_factors_helper(res, 1, n); } /* A really dumb primality test. */ int is_prime(ulong n) { ulong i; for (i = 2; i * i <= n; i++) if (n % i == 0) return 0; return 1; } /* Assuming p >= 3 is prime, find the minimum primitive root mod p. Stores it at g, and stores its inverse mod p at g_inv. Note: this is probably a terrible algorithm, but it's fine compared to the running time of bernoulli() for large p. */ void primitive_root(ulong* g, ulong* g_inv, ulong p) { zn_mod_t mod; zn_mod_init(mod, p); // find prime factors of p-1 ulong factors[200]; unsigned num_factors, i; num_factors = prime_factors(factors, p-1); // loop through candidates ulong x; for (x = 2; ; x++) { ZNP_ASSERT(x < p); // it's a generator if x^((p-1)/q) != 1 for all primes q dividing p-1 int good = 1; for (i = 0; i < num_factors && good; i++) good = good && (zn_mod_pow(x, (p-1) / factors[i], mod) != 1); if (good) { *g = x; *g_inv = zn_mod_pow(x, p-2, mod); zn_mod_clear(mod); return; } } } /* Computes bernoulli numbers B(0), B(2), ..., B((p-3)/2) mod p. If res != NULL, it stores the result in res, which needs to be of length (p-1)/2. If irregular != NULL, it stores the number of irregular indices at irregular[0], follows by the irregular indices. The largest permitted index of irregularity is irregular_max; if there are more indices than that to store, the program will abort. p must be a prime >= 3 p must be < 2^(ULONG_BITS/2) */ void bernoulli(ulong* res, ulong* irregular, unsigned irregular_max, ulong p) { ZNP_ASSERT(p < (1UL << (ULONG_BITS/2))); ulong g, g_inv; primitive_root(&g, &g_inv, p); ulong len = (p-1) / 2; zn_mod_t mod; zn_mod_init(mod, p); // allocate our own res if the caller didn't int own_res = 0; if (!res) { res = (ulong*) malloc(len * sizeof(ulong)); own_res = 1; } if (irregular) irregular[0] = 0; ulong* G = (ulong*) malloc(len * sizeof(ulong)); ulong* P = (ulong*) malloc((2*len - 1) * sizeof(ulong)); ulong* J = res; // ------------------------------------------------------------------------- // Step 1: compute polynomials G(X) and J(X) // g_pow = g^(i-1), g_pow = g^(-i) at beginning of each iteration ulong g_pow = g_inv; ulong g_pow_inv = 1; // bias = (g-1)/2 mod p ulong bias = (g - 1 + ((g & 1) ? 0 : p)) / 2; // fudge = g^(i^2), fudge_inv = g^(-i^2) at each iteration ulong fudge = 1; ulong fudge_inv = 1; ulong i; for (i = 0; i < len; i++) { ulong prod = g * g_pow; // quo = floor(prod / p) // rem = prod % p ulong quo = zn_mod_quotient(prod, mod); ulong rem = prod - quo * p; // h = h(g^i) / g^i mod p ulong h = g_pow_inv * zn_mod_sub_slim(bias, quo, mod); h = zn_mod_reduce(h, mod); // update g_pow and g_pow_inv for next iteration g_pow = rem; g_pow_inv = zn_mod_reduce(g_pow_inv * g_inv, mod); // X^i coefficient of G(X) is g^(i^2) * h(g^i) / g^i G[i] = zn_mod_reduce(h * fudge, mod); // X^i coefficient of J(X) is g^(-i^2) J[i] = fudge_inv; // update fudge and fudge_inv for next iteration fudge = zn_mod_reduce(fudge * g_pow, mod); fudge = zn_mod_reduce(fudge * g_pow, mod); fudge = zn_mod_reduce(fudge * g, mod); fudge_inv = zn_mod_reduce(fudge_inv * g_pow_inv, mod); fudge_inv = zn_mod_reduce(fudge_inv * g_pow_inv, mod); fudge_inv = zn_mod_reduce(fudge_inv * g, mod); } J[0] = 0; // ------------------------------------------------------------------------- // Step 2: compute product P(X) = G(X) * J(X) zn_array_mul(P, J, len, G, len, mod); // ------------------------------------------------------------------------- // Step 3: extract output from P(X), and verify result res[0] = 1; // we will verify that \sum_{j=0}^{(p-3)/2} 4^j (2j+1) B(2j) = -2 mod p ulong check_accum = 1; ulong check_four_pow = 4 % p; // g_sqr = g^2 // g_sqr_inv = g^(-2) ulong g_sqr = zn_mod_reduce(g * g, mod); ulong g_sqr_inv = zn_mod_reduce(g_inv * g_inv, mod); // make table with J[i] = (1 - g^(2i+2))(1 - g^(2i+4)) ... (1 - g^(p-3)) // for 0 <= i < (p-1)/2 ulong g_sqr_inv_pow = g_sqr_inv; J[len-1] = 1; for (i = 1; i < len; i++) { J[len-i-1] = zn_mod_reduce(J[len-i] * (p + 1 - g_sqr_inv_pow), mod); g_sqr_inv_pow = zn_mod_reduce(g_sqr_inv_pow * g_sqr_inv, mod); } // fudge = g^(i^2) at each iteration fudge = g; // g_sqr_pow = g^(2i) at each iteration ulong g_sqr_pow = g_sqr; // prod_inv = [(1 - g^(2i))(1 - g^(2i+2)) ... (1 - g^(p-3))]^(-1) // at each iteration (todo: for i == 1, it's experimentally equal to -1/2 // mod p, need to prove this) ulong prod_inv = p - 2; for (i = 1; i < len; i++) { ulong val = (i == (len-1)) ? 0 : P[i + len]; if (len & 1) val = zn_mod_neg(val, mod); val = zn_mod_add_slim(val, G[i], mod); val = zn_mod_add_slim(val, P[i], mod); // multiply by 4 * i * g^(i^2) val = zn_mod_reduce(val * fudge, mod); val = zn_mod_reduce(val * (2*i), mod); val = zn_mod_add_slim(val, val, mod); // divide by (1 - g^(2i)) val = zn_mod_reduce(val * prod_inv, mod); val = zn_mod_reduce(val * J[i], mod); prod_inv = zn_mod_reduce(prod_inv * (1 + p - g_sqr_pow), mod); // store output coefficient if requested if (!own_res) res[i] = val; // store irregular index if requested if (irregular) { if (val == 0) { irregular[0]++; if (irregular[0] >= irregular_max) { printf("too many irregular indices for p = %lu\n", p); abort(); } irregular[irregular[0]] = 2*i; } } // update fudge and g_sqr_pow g_sqr_pow = zn_mod_reduce(g_sqr_pow * g, mod); fudge = zn_mod_reduce(fudge * g_sqr_pow, mod); g_sqr_pow = zn_mod_reduce(g_sqr_pow * g, mod); // update verification data ulong check_term = zn_mod_reduce(check_four_pow * (2*i + 1), mod); check_term = zn_mod_reduce(check_term * val, mod); check_accum = zn_mod_add_slim(check_accum, check_term, mod); check_four_pow = zn_mod_add_slim(check_four_pow, check_four_pow, mod); check_four_pow = zn_mod_add_slim(check_four_pow, check_four_pow, mod); } if (check_accum != p-2) { printf("bernoulli failed correctness check for p = %lu\n", p); abort(); } if (own_res) free(res); free(P); free(G); zn_mod_clear(mod); } /* Same as bernoulli(), but handles two primes simultaneously. p1 and p2 must be distinct primes >= 3. */ void bernoulli_dual( ulong* res1, ulong* irregular1, unsigned irregular1_max, ulong p1, ulong* res2, ulong* irregular2, unsigned irregular2_max, ulong p2) { ZNP_ASSERT(p1 < (1UL << (ULONG_BITS/2))); ZNP_ASSERT(p2 < (1UL << (ULONG_BITS/2))); ZNP_ASSERT(p1 != p2); // swap them to make p1 < p2 if (p1 > p2) { { ulong temp = p2; p2 = p1; p1 = temp; } { ulong* temp = res1; res1 = res2; res2 = temp; } { ulong* temp = irregular1; irregular1 = irregular2; irregular2 = temp; } { unsigned temp = irregular1_max; irregular1_max = irregular2_max; irregular2_max = temp; } } ulong g1, g_inv1; ulong g2, g_inv2; primitive_root(&g1, &g_inv1, p1); primitive_root(&g2, &g_inv2, p2); ulong len1 = (p1-1) / 2; ulong len2 = (p2-1) / 2; zn_mod_t mod1, mod2; zn_mod_init(mod1, p1); zn_mod_init(mod2, p2); ulong q = p1 * p2; zn_mod_t mod; zn_mod_init(mod, q); // allocate our own res2 if the caller didn't int own_res2 = 0; if (!res2) { res2 = (ulong*) malloc(len2 * sizeof(ulong)); own_res2 = 1; } if (irregular1) irregular1[0] = 0; if (irregular2) irregular2[0] = 0; // find idempotents to CRT modulo p1 and p2, i.e. // id1 = 1 mod p1, id1 = 0 mod p2 // id2 = 0 mod p1, id2 = 1 mod p2 mpz_t p1_mpz, p2_mpz, a1_mpz, a2_mpz, g_mpz; mpz_init(p1_mpz); mpz_init(p2_mpz); mpz_init(a1_mpz); mpz_init(a2_mpz); mpz_init(g_mpz); mpz_set_ui(p1_mpz, p1); mpz_set_ui(p2_mpz, p2); mpz_gcdext(g_mpz, a1_mpz, a2_mpz, p1_mpz, p2_mpz); mpz_mul(a1_mpz, a1_mpz, p1_mpz); mpz_mod_ui(a1_mpz, a1_mpz, q); ulong id2 = mpz_get_ui(a1_mpz); mpz_mul(a2_mpz, a2_mpz, p2_mpz); mpz_mod_ui(a2_mpz, a2_mpz, q); ulong id1 = mpz_get_ui(a2_mpz); mpz_clear(g_mpz); mpz_clear(a2_mpz); mpz_clear(a1_mpz); mpz_clear(p2_mpz); mpz_clear(p1_mpz); ulong* G = (ulong*) malloc(len2 * sizeof(ulong)); ulong* P = (ulong*) malloc((2*len2 - 1) * sizeof(ulong)); ulong* J = res2; // ------------------------------------------------------------------------- // Step 1: compute polynomials G(X) and J(X) // g_pow = g^(i-1), g_pow = g^(-i) at beginning of each iteration ulong g_pow1 = g_inv1; ulong g_pow2 = g_inv2; ulong g_pow_inv1 = 1; ulong g_pow_inv2 = 1; // bias = (g-1)/2 mod p ulong bias1 = (g1 - 1 + ((g1 & 1) ? 0 : p1)) / 2; ulong bias2 = (g2 - 1 + ((g2 & 1) ? 0 : p2)) / 2; // fudge = g^(i^2), fudge_inv = g^(-i^2) at each iteration ulong fudge1 = 1; ulong fudge2 = 1; ulong fudge_inv1 = 1; ulong fudge_inv2 = 1; ulong i; for (i = 0; i < len1; i++) { ulong prod1 = g1 * g_pow1; ulong prod2 = g2 * g_pow2; // quo = floor(prod / p) // rem = prod % p ulong quo1 = zn_mod_quotient(prod1, mod1); ulong rem1 = prod1 - quo1 * p1; ulong quo2 = zn_mod_quotient(prod2, mod2); ulong rem2 = prod2 - quo2 * p2; // h = h(g^i) / g^i mod p ulong h1 = g_pow_inv1 * zn_mod_sub_slim(bias1, quo1, mod1); h1 = zn_mod_reduce(h1, mod1); ulong h2 = g_pow_inv2 * zn_mod_sub_slim(bias2, quo2, mod2); h2 = zn_mod_reduce(h2, mod2); // update g_pow and g_pow_inv for next iteration g_pow1 = rem1; g_pow_inv1 = zn_mod_reduce(g_pow_inv1 * g_inv1, mod1); g_pow2 = rem2; g_pow_inv2 = zn_mod_reduce(g_pow_inv2 * g_inv2, mod2); // X^i coefficient of G(X) is g^(i^2) * h(g^i) / g^i // (combine via CRT) ulong Gval1 = zn_mod_reduce(h1 * fudge1, mod1); Gval1 = zn_mod_mul(Gval1, id1, mod); ulong Gval2 = zn_mod_reduce(h2 * fudge2, mod2); Gval2 = zn_mod_mul(Gval2, id2, mod); G[i] = zn_mod_add(Gval1, Gval2, mod); // X^i coefficient of J(X) is g^(-i^2) ulong Jval1 = zn_mod_mul(fudge_inv1, id1, mod); ulong Jval2 = zn_mod_mul(fudge_inv2, id2, mod); J[i] = zn_mod_add(Jval1, Jval2, mod); // update fudge and fudge_inv for next iteration fudge1 = zn_mod_reduce(fudge1 * g_pow1, mod1); fudge1 = zn_mod_reduce(fudge1 * g_pow1, mod1); fudge1 = zn_mod_reduce(fudge1 * g1, mod1); fudge_inv1 = zn_mod_reduce(fudge_inv1 * g_pow_inv1, mod1); fudge_inv1 = zn_mod_reduce(fudge_inv1 * g_pow_inv1, mod1); fudge_inv1 = zn_mod_reduce(fudge_inv1 * g1, mod1); fudge2 = zn_mod_reduce(fudge2 * g_pow2, mod2); fudge2 = zn_mod_reduce(fudge2 * g_pow2, mod2); fudge2 = zn_mod_reduce(fudge2 * g2, mod2); fudge_inv2 = zn_mod_reduce(fudge_inv2 * g_pow_inv2, mod2); fudge_inv2 = zn_mod_reduce(fudge_inv2 * g_pow_inv2, mod2); fudge_inv2 = zn_mod_reduce(fudge_inv2 * g2, mod2); } // finished with p1, now finish the loop for p2 for (; i < len2; i++) { ulong prod2 = g2 * g_pow2; // quo = floor(prod / p) // rem = prod % p ulong quo2 = zn_mod_quotient(prod2, mod2); ulong rem2 = prod2 - quo2 * p2; // h = h(g^i) / g^i mod p ulong h2 = g_pow_inv2 * zn_mod_sub_slim(bias2, quo2, mod2); h2 = zn_mod_reduce(h2, mod2); // update g_pow and g_pow_inv for next iteration g_pow2 = rem2; g_pow_inv2 = zn_mod_reduce(g_pow_inv2 * g_inv2, mod2); // X^i coefficient of G(X) is g^(i^2) * h(g^i) / g^i // (combine via CRT) ulong Gval2 = zn_mod_reduce(h2 * fudge2, mod2); G[i] = zn_mod_mul(Gval2, id2, mod); // X^i coefficient of J(X) is g^(-i^2) J[i] = zn_mod_mul(fudge_inv2, id2, mod); // update fudge and fudge_inv for next iteration fudge2 = zn_mod_reduce(fudge2 * g_pow2, mod2); fudge2 = zn_mod_reduce(fudge2 * g_pow2, mod2); fudge2 = zn_mod_reduce(fudge2 * g2, mod2); fudge_inv2 = zn_mod_reduce(fudge_inv2 * g_pow_inv2, mod2); fudge_inv2 = zn_mod_reduce(fudge_inv2 * g_pow_inv2, mod2); fudge_inv2 = zn_mod_reduce(fudge_inv2 * g2, mod2); } J[0] = 0; // ------------------------------------------------------------------------- // Step 2: compute product P(X) = G(X) * J(X) #if 1 zn_array_mul_fft_dft(P, J, len2, G, len2, 3, mod); #else zn_array_mul(P, J, len2, G, len2, mod); #endif // ------------------------------------------------------------------------- // Step 3: extract output from P(X), and verify result if (res1) res1[0] = 1; // we will verify that \sum_{j=0}^{(p-3)/2} 4^j (2j+1) B(2j) = -2 mod p ulong check_accum1 = 1; ulong check_four_pow1 = 4 % p1; // g_sqr = g^2 // g_sqr_inv = g^(-2) ulong g_sqr1 = zn_mod_reduce(g1 * g1, mod1); ulong g_sqr_inv1 = zn_mod_reduce(g_inv1 * g_inv1, mod1); // make table with J[i] = (1 - g^(2i+2))(1 - g^(2i+4)) ... (1 - g^(p-3)) // for 0 <= i < (p-1)/2 ulong g_sqr_inv_pow1 = g_sqr_inv1; J[len1-1] = 1; for (i = 1; i < len1; i++) { J[len1-i-1] = zn_mod_reduce(J[len1-i] * (p1 + 1 - g_sqr_inv_pow1), mod1); g_sqr_inv_pow1 = zn_mod_reduce(g_sqr_inv_pow1 * g_sqr_inv1, mod1); } // fudge = g^(i^2) at each iteration fudge1 = g1; // g_sqr_pow = g^(2i) at each iteration ulong g_sqr_pow1 = g_sqr1; // prod_inv = [(1 - g^(2i))(1 - g^(2i+2)) ... (1 - g^(p-3))]^(-1) // at each iteration (todo: for i == 1, it's experimentally equal to -1/2 // mod p, need to prove this) ulong prod_inv1 = p1 - 2; for (i = 1; i < len1; i++) { ulong val = (i == (len1-1)) ? 0 : P[i + len1]; if (len1 & 1) val = zn_mod_neg(val, mod); val = zn_mod_add(val, G[i], mod); val = zn_mod_add(val, P[i], mod); // reduce it mod p1 val = zn_mod_reduce(val, mod1); // multiply by 4 * i * g^(i^2) val = zn_mod_reduce(val * fudge1, mod1); val = zn_mod_reduce(val * (2*i), mod1); val = zn_mod_add_slim(val, val, mod1); // divide by (1 - g^(2i)) val = zn_mod_reduce(val * prod_inv1, mod1); val = zn_mod_reduce(val * J[i], mod1); prod_inv1 = zn_mod_reduce(prod_inv1 * (1 + p1 - g_sqr_pow1), mod1); // store output coefficient if requested if (res1) res1[i] = val; // store irregular index if requested if (irregular1) { if (val == 0) { irregular1[0]++; if (irregular1[0] >= irregular1_max) { printf("too many irregular indices for p = %lu\n", p1); abort(); } irregular1[irregular1[0]] = 2*i; } } // update fudge and g_sqr_pow g_sqr_pow1 = zn_mod_reduce(g_sqr_pow1 * g1, mod1); fudge1 = zn_mod_reduce(fudge1 * g_sqr_pow1, mod1); g_sqr_pow1 = zn_mod_reduce(g_sqr_pow1 * g1, mod1); // update verification data ulong check_term1 = zn_mod_reduce(check_four_pow1 * (2*i + 1), mod1); check_term1 = zn_mod_reduce(check_term1 * val, mod1); check_accum1 = zn_mod_add_slim(check_accum1, check_term1, mod1); check_four_pow1 = zn_mod_add_slim(check_four_pow1, check_four_pow1, mod1); check_four_pow1 = zn_mod_add_slim(check_four_pow1, check_four_pow1, mod1); } if (check_accum1 != p1-2) { printf("bernoulli_dual failed correctness check for p1 = %lu\n", p1); abort(); } // ------------------------------------------------------------------------- // Do step 3 again for the second prime res2[0] = 1; // we will verify that \sum_{j=0}^{(p-3)/2} 4^j (2j+1) B(2j) = -2 mod p ulong check_accum2 = 1; ulong check_four_pow2 = 4 % p2; // g_sqr = g^2 // g_sqr_inv = g^(-2) ulong g_sqr2 = zn_mod_reduce(g2 * g2, mod2); ulong g_sqr_inv2 = zn_mod_reduce(g_inv2 * g_inv2, mod2); // make table with J[i] = (1 - g^(2i+2))(1 - g^(2i+4)) ... (1 - g^(p-3)) // for 0 <= i < (p-1)/2 ulong g_sqr_inv_pow2 = g_sqr_inv2; J[len2-1] = 1; for (i = 1; i < len2; i++) { J[len2-i-1] = zn_mod_reduce(J[len2-i] * (p2 + 1 - g_sqr_inv_pow2), mod2); g_sqr_inv_pow2 = zn_mod_reduce(g_sqr_inv_pow2 * g_sqr_inv2, mod2); } // fudge = g^(i^2) at each iteration fudge2 = g2; // g_sqr_pow = g^(2i) at each iteration ulong g_sqr_pow2 = g_sqr2; // prod_inv = [(1 - g^(2i))(1 - g^(2i+2)) ... (1 - g^(p-3))]^(-1) // at each iteration (todo: for i == 1, it's experimentally equal to -1/2 // mod p, need to prove this) ulong prod_inv2 = p2 - 2; for (i = 1; i < len2; i++) { ulong val = (i == (len2-1)) ? 0 : P[i + len2]; if (len2 & 1) val = zn_mod_neg(val, mod); val = zn_mod_add(val, G[i], mod); val = zn_mod_add(val, P[i], mod); // reduce it mod p2 val = zn_mod_reduce(val, mod2); // multiply by 4 * i * g^(i^2) val = zn_mod_reduce(val * fudge2, mod2); val = zn_mod_reduce(val * (2*i), mod2); val = zn_mod_add_slim(val, val, mod2); // divide by (1 - g^(2i)) val = zn_mod_reduce(val * prod_inv2, mod2); val = zn_mod_reduce(val * J[i], mod2); prod_inv2 = zn_mod_reduce(prod_inv2 * (1 + p2 - g_sqr_pow2), mod2); // store output coefficient if requested if (!own_res2) res2[i] = val; // store irregular index if requested if (irregular2) { if (val == 0) { irregular2[0]++; if (irregular2[0] >= irregular2_max) { printf("too many irregular indices for p = %lu\n", p2); abort(); } irregular2[irregular2[0]] = 2*i; } } // update fudge and g_sqr_pow g_sqr_pow2 = zn_mod_reduce(g_sqr_pow2 * g2, mod2); fudge2 = zn_mod_reduce(fudge2 * g_sqr_pow2, mod2); g_sqr_pow2 = zn_mod_reduce(g_sqr_pow2 * g2, mod2); // update verification data ulong check_term2 = zn_mod_reduce(check_four_pow2 * (2*i + 1), mod2); check_term2 = zn_mod_reduce(check_term2 * val, mod2); check_accum2 = zn_mod_add_slim(check_accum2, check_term2, mod2); check_four_pow2 = zn_mod_add_slim(check_four_pow2, check_four_pow2, mod2); check_four_pow2 = zn_mod_add_slim(check_four_pow2, check_four_pow2, mod2); } if (check_accum2 != p2-2) { printf("bernoulli_dual failed correctness check for p2 = %lu\n", p2); abort(); } if (own_res2) free(res2); free(P); free(G); zn_mod_clear(mod); zn_mod_clear(mod2); zn_mod_clear(mod1); } /* Prints out irregular pairs for all p in the arithmetic progression p = a mod n, in the range min_p <= p < max_p. Must have 0 <= a < n. */ void arithmetic_progression(ulong n, ulong a, ulong min_p, ulong max_p) { ulong p, p1, p2; for (p1 = min_p; p1 < max_p; p1++) { if (p1 % n == a && is_prime(p1)) { // find next prime for (p2 = p1 + 1; p2 < max_p; p2++) { if (p2 % n == a && is_prime(p2)) break; } if (p2 == max_p) p2 = 0; // only one prime to handle ulong irregular1[50]; ulong irregular2[50]; if (p2) bernoulli_dual(NULL, irregular1, 49, p1, NULL, irregular2, 49, p2); else bernoulli(NULL, irregular1, 49, p1); ulong i; printf("%lu", p1); for (i = 1; i <= irregular1[0]; i++) printf(" %lu", irregular1[i]); printf("\n"); if (p2) { printf("%lu", p2); for (i = 1; i <= irregular2[0]; i++) printf(" %lu", irregular2[i]); printf("\n"); } fflush(stdout); if (p2) p1 = p2; } } } int main(int argc, char* argv[]) { if (argc == 2) { ulong i, p = atol(argv[1]); ulong irregular[30]; bernoulli(NULL, irregular, 29, p); printf("irregular indices for p = %lu: ", p); for (i = 1; i <= irregular[0]; i++) printf("%lu ", irregular[i]); printf("\n"); } else if (argc == 3) { ulong i, p1 = atol(argv[1]), p2 = atol(argv[2]); ulong irregular1[30]; ulong irregular2[30]; bernoulli_dual(NULL, irregular1, 29, p1, NULL, irregular2, 29, p2); printf("irregular indices for p = %lu: ", p1); for (i = 1; i <= irregular1[0]; i++) printf("%lu ", irregular1[i]); printf("\n"); printf("irregular indices for p = %lu: ", p2); for (i = 1; i <= irregular2[0]; i++) printf("%lu ", irregular2[i]); printf("\n"); } else if (argc == 5) { ulong n = atol(argv[1]); ulong a = atol(argv[2]); ulong min_p = atol(argv[3]); ulong max_p = atol(argv[4]); arithmetic_progression(n, a, min_p, max_p); } else { printf("usage:\n"); printf("\n"); printf(" bernoulli p\n"); printf(" prints irregular indices for p\n"); printf("\n"); printf(" bernoulli p1 p2\n"); printf(" prints irregular indices for p1 and p2\n"); printf("\n"); printf(" bernoulli n a min max\n"); printf(" prints irregular indices for p = a mod n, min <= p < max\n"); } return 0; } // end of file ****************************************************************