void log10(const mpz_t n, mpz_t l, int prec) { mpf_t tmp, tmp2; mpf_inits(tmp, tmp2, NULL); mpf_set_z(tmp, n); log10(tmp, tmp2, prec); mpz_set_f(l, tmp2); mpf_clears(tmp, tmp2, NULL); }
void log2(const mpz_t n, mpz_t l) { mpf_t tmp, tmp2; mpf_inits(tmp, tmp2, NULL); mpf_set_z(tmp, n); log2(tmp, tmp2); mpz_set_f(l, tmp2); mpf_clears(tmp, tmp2, NULL); }
void log(const mpz_t n, mpz_t l, mpz_t b) { mpf_t tmp, tmp2, tmp3; mpf_inits(tmp, tmp2, tmp3, NULL); mpf_set_z(tmp, n); mpf_set_z(tmp3, b); log(tmp, tmp2, tmp3); mpz_set_f(l, tmp2); mpf_clears(tmp, tmp2, tmp3, NULL); }
void log(const mpf_t n, mpf_t l, mpf_t b) { // l = log10(n)/log10(b) mpf_t tmp, tmp2; mpf_inits(tmp, tmp2, NULL); log10(n, tmp); log10(b, tmp2); mpf_div(l, tmp, tmp2); mpf_clears(tmp, tmp2, NULL); }
void log10(const mpf_t n, mpf_t l, int prec) { // Approximate log10 to prec digits. // Ref.: http://www.brics.dk/RS/04/17/BRICS-RS-04-17.pdf mpf_t tmp, tmp2; mpf_inits(tmp, tmp2, NULL); mpf_set(tmp, n); stringstream res; for (int i = 0; i < prec + 1; i++) { int d = digits(tmp); // m_(i+1) = (m_i / (10 ^ a_i)) ^ 10 mpf_set_ui(tmp2, 10); mpf_pow_ui(tmp2, tmp2, d); mpf_div(tmp2, tmp, tmp2); mpf_pow_ui(tmp, tmp2, 10); res << d; if (i == 0) res << "."; } mpf_set_str(l, res.str().c_str(), 10); mpf_clears(tmp, tmp2, NULL); }
//Le but de la fonction est de calculer le développement en fractions continue jusqu'à un certain rang de racine carrée de kN et de stocker les couples (A_n-1,Q_n) comme décrit dans la section (à venir) cfrac expand(const mpz_t N, const long long unsigned int rang, const mpz_t k) { cfrac res; //Contient l'ensemble des A_n-1 et l'ensemble Q_n mpz_inits(res.N, res.k, res.g, NULL); //Initialisation des variables mpz_t* A = (mpz_t*)malloc((rang+1)*sizeof(mpz_t)); //Le tableau contenant les A_n-1 mpz_t* Q = (mpz_t*)malloc((rang+2)*sizeof(mpz_t)); //Le tableau contenant les Q_n mpz_t* P = (mpz_t*)malloc((rang+1)*sizeof(mpz_t)); //Éléments reliés au Q_n mpz_t* r = (mpz_t*)malloc((rang+1)*sizeof(mpz_t)); //Interviennent dans le calcul des A_n-1 & Q_n mpz_t* q = (mpz_t*)malloc(rang*sizeof(mpz_t)); //Idem mpz_t g, tempz; //Idem, tempz = variable à tout faire mpf_t sqrtkN, tempf, tempf2; //sqrtkN = sqrt(k*N), tempf = variable à tout faire //Valeurs d'initialisation de la boucle mpz_inits(g, A[0], Q[0], r[0], tempz, NULL); mpf_inits(tempf, sqrtkN, tempf2, NULL); //Initialisation des différentes valeurs "indépendantes" mpz_set(tempz, N); mpz_mul(tempz, tempz, k); mpf_set_z(sqrtkN, tempz); mpf_sqrt(sqrtkN, sqrtkN); mpf_floor(tempf, sqrtkN); mpz_set_f(g, tempf); //g = [sqrt(kN)] mpz_set(Q[0], k); mpz_mul(Q[0], Q[0], N); //Q_-1 = kN = Q[0] mpz_set(r[0], g); //r_-1 = r[0] mpz_set_ui(A[0], 1); //A_-1 = A[0] //Calcul de P_0 & Q_0 mpz_init_set_ui(Q[1], 1); //Q_0 = Q[1] mpz_init_set_ui(P[0], 0); //P_0 = P[0] for(long long int i = 0; i < rang; i++) { switch(i) { case 0: //Calcul de q_0 mpz_init_set(q[0], g); //q_0 = [(sqrt(kN) + P_0)/Q_0] avec P_0 = 0 et Q_0 = 1 //Calcul de A_0 mpz_init_set(A[1],A[0]); mpz_mul(A[1], A[1], q[0]); //A_0 = q_0*A_-1 mpz_mod(A[1], A[1], N); //On réduit mod N //Calcul de r_0 mpz_init_set_ui(r[1], 0); //r_0 = P_0 + g - q_0.Q_0 = 0 + g - g.1 = 0 //Calcul de P_1 mpz_init_set(P[1], g); //P_1 = g - r_0 = g - 0 = g //Calcul de Q_1 = Q[2] mpz_init_set(Q[2], r[1]); mpz_sub(Q[2], Q[2], r[0]); mpz_mul(Q[2], Q[2], q[0]); mpz_add(Q[2], Q[2], Q[0]); break; default: //Calcul q_i mpz_init(q[i]); mpf_set_z(tempf, P[i]); mpf_set_z(tempf2, Q[i+1]); mpf_add(tempf, tempf, sqrtkN); //sqrt(kN) + P_i mpf_div(tempf, tempf, tempf2); mpf_floor(tempf, tempf); //floor((sqrt(kN) + P_i)/Q_i) mpz_set_f(q[i], tempf); //Calcul de r_n = r[n+1] mpz_init(r[i+1]); mpz_submul(r[i+1], q[i], Q[i+1]); mpz_add(r[i+1], r[i+1], P[i]); mpz_add(r[i+1], r[i+1], g); //Calcul de A_n = A[n+1] mpz_init_set(A[i+1],A[i]); mpz_mul(A[i+1], A[i+1], q[i]); //A_i-1*q_i mpz_add(A[i+1], A[i+1], A[i-1]); //A_i-1*q_i + A_i-2 mpz_mod(A[i+1], A[i+1], N); //réduction modulo N //Calcul P_n+1 mpz_init_set(P[i+1], g); mpz_sub(P[i+1], P[i+1], r[i+1]); //P[n+1] = g - r_n = g - r[n+1] //Calcul Q_n+1 = Q[n+2] mpz_init_set(Q[i+2], r[i+1]); mpz_sub(Q[i+2], Q[i+2], r[i]); //(r_n - r_n-1) mpz_mul(Q[i+2], Q[i+2], q[i]); //q_n(r_n - r_n-1) mpz_add(Q[i+2], Q[i+2], Q[i]); //Q_n-1 + q_n(r_n - r_n-1) break; } } //Test de routine pour voir si le développement de la fraction continue s'est bien passé mpz_t tempsqrt; mpz_init(tempsqrt); mpz_set(tempsqrt, k); mpz_mul(tempsqrt, tempsqrt, N); mpz_sqrt(tempsqrt, tempsqrt); mpz_mul_ui(tempsqrt, tempsqrt, 2); for(int i = 1; i < rang; i++) //Éviter de commencer à i = 0 puisque cela représente Q_-1 qui n'intervient uniquement dans l'algo et non dans le développement en fraction continue { if(mpz_cmp(Q[i], tempsqrt) >= 0) //Si Q_n >= 2sqrt(kN) { res.rang = 0; mpz_clears(g, tempz, NULL); mpf_clears(sqrtkN, tempf, tempf2, NULL); return res; } } //Assignation des tableaux dans le résultat res.A = A; res.Q = Q; res.r = r; res.q = q; res.P = P; mpz_set(res.N, N); mpz_set(res.k, k); res.rang = rang; mpz_set(res.g, g); //Libération de mémoire mpz_clears(g, tempz, NULL); mpf_clears(sqrtkN, tempf, tempf2, NULL); return res; }
void check_random (long reps) { unsigned long test; gmp_randstate_ptr rands = RANDS; mpf_t a, b, x; mpz_t ds; int hibits, lshift1, lshift2; int xtra; #define HIBITS 10 #define LSHIFT1 10 #define LSHIFT2 10 mpf_set_default_prec ((1 << HIBITS) + (1 << LSHIFT1) + (1 << LSHIFT2)); mpz_init (ds); mpf_inits (a, b, x, NULL); for (test = 0; test < reps; test++) { mpz_urandomb (ds, rands, HIBITS); hibits = mpz_get_ui (ds) + 1; mpz_urandomb (ds, rands, hibits); mpz_setbit (ds, hibits - 1); /* make sure msb is set */ mpf_set_z (a, ds); mpf_set_z (b, ds); mpz_urandomb (ds, rands, LSHIFT1); lshift1 = mpz_get_ui (ds); mpf_mul_2exp (a, a, lshift1 + 1); mpf_mul_2exp (b, b, lshift1 + 1); mpf_add_ui (a, a, 1); /* make a one-bit difference */ mpz_urandomb (ds, rands, LSHIFT2); lshift2 = mpz_get_ui (ds); mpf_mul_2exp (a, a, lshift2); mpf_mul_2exp (b, b, lshift2); mpz_urandomb (ds, rands, lshift2); mpf_set_z (x, ds); mpf_add (a, a, x); mpf_add (b, b, x); insert_random_low_zero_limbs (a, rands); insert_random_low_zero_limbs (b, rands); if (mpf_eq (a, b, lshift1 + hibits) == 0 || mpf_eq (b, a, lshift1 + hibits) == 0) { dump_abort (a, b, lshift1 + hibits, lshift1, lshift2, hibits, 1, test); } for (xtra = 1; xtra < 100; xtra++) if (mpf_eq (a, b, lshift1 + hibits + xtra) != 0 || mpf_eq (b, a, lshift1 + hibits + xtra) != 0) { dump_abort (a, b, lshift1 + hibits + xtra, lshift1, lshift2, hibits, 0, test); } } mpf_clears (a, b, x, NULL); mpz_clear (ds); }
void *chudnovsky_chunk(void *arguments) { unsigned long int k, threek, total_iterations; mpz_t a, b, c, d, e, rt, rb; mpf_t rtf, rbf, r; struct thread_args *args = (struct thread_args *)arguments; total_iterations = args->iter; /* Init libgmp variables */ mpz_inits(a, b, c, d, e, rt, rb, NULL); mpf_inits(rtf, rbf, r, NULL); /* Main loop of a thread */ while (1) { /* Check which k needs calculation */ pthread_mutex_lock(&args->start_mutex); k = args->k; args->k++; pthread_mutex_unlock(&args->start_mutex); /* If all ks are done, say return */ if (k > total_iterations) break; /* 3k */ threek = k * 3; /* (6k!) */ mpz_fac_ui(a, threek << 1); /* 545140134k */ mpz_set_ui(b, BCONST1); mpz_mul_ui(b, b, k); /* 13591409 + 545140134k */ mpz_add_ui(b, b, BCONST2); /* (3k)! */ mpz_fac_ui(c, threek); /* (k!)^3 */ mpz_fac_ui(d, k); mpz_pow_ui(d, d, DCONST1); /* (-640320)^3k */ mpz_set_ui(e, ECONST1); mpz_pow_ui(e, e, threek); if ((threek & 1) == 1) mpz_neg(e, e); /* Get everything together */ /* (6k)! (13591409 + 545140134k) */ mpz_mul(rt, a, b); /* (3k)! (k!)^3 */ mpz_mul(rb, c, d); mpz_mul(rb, rb, e); /* Switch to floats */ mpf_set_z(rtf, rt); mpf_set_z(rbf, rb); /* divide top/bottom */ mpf_div(r, rtf, rbf); /* add result to the sum */ pthread_mutex_lock(&args->sum_mutex); mpf_add(args->sum, args->sum, r); pthread_mutex_unlock(&args->sum_mutex); } /* Deinit variables, result is passed via args->sum */ mpz_clears(a, b, c, d, e, rt, rb, NULL); mpf_clears(rtf, rbf, r, NULL); }
int chudnovsky(int digits, int threads) { int res = 0; unsigned long int i, iter, precision, rest, per_cpu, k; mpf_t ltf, sum, result; pthread_t *pthreads; struct thread_args targs; mp_exp_t exponent; char *pi; /* If threads is not specified, check how many CPUs are avail */ if (threads == 0) { threads = get_cpu_count(); } pthreads = malloc(threads * sizeof(pthread_t)); if (pthreads == NULL) { res = -ENOMEM; goto chudnovsky_exit; } /* Calculate and set precision */ precision = (digits * BPD) + 1; mpf_set_default_prec(precision); /* Calculate number of iterations */ iter = digits/DPI + 1; /* Init all objects */ mpf_inits(ltf, sum, result, targs.sum, NULL); mpf_set_ui(sum, 0); mpf_set_ui(targs.sum, 0); /* Set pthread specific stuff */ targs.k = 0; targs.iter = iter; pthread_mutex_init(&targs.start_mutex, NULL); pthread_mutex_init(&targs.sum_mutex, NULL); /* Prepare the constant from the left side of the equation */ mpf_sqrt_ui(ltf, LTFCON1); mpf_mul_ui(ltf, ltf, LTFCON2); printf("Starting summing, using:\n" "%d digits - %lu iterations - %d threads\n", digits, iter, threads); for (i = 0; i < threads; i++) { pthread_create(&pthreads[i], NULL, &chudnovsky_chunk, (void *) &targs); } /* Wait for threads to finish and take their sums */ for (i = 0; i < threads; i++) { pthread_join(pthreads[i], NULL); } printf("Starting final steps\n"); /* Invert sum */ mpf_ui_div(sum, 1, targs.sum); mpf_mul(result, sum, ltf); /* Get one more char then needed and then trunc to avoid rounding */ pi = mpf_get_str(NULL, &exponent, 10, digits + 2, result); pi[digits+1] = '\0'; if (strlen(pi) < LAST_DIGITS_PRINT + 1) { printf("Calculated PI:\n"); printf("\t%.*s.%s\n", (int)exponent, pi, pi + exponent); } else { printf("Last digits of Pi are:\n"); printf("\t%s\n", pi+(digits-(LAST_DIGITS_PRINT-1))); } free(pi); mpf_clears(ltf, sum, result, NULL); pthread_mutex_destroy(&targs.start_mutex); pthread_mutex_destroy(&targs.sum_mutex); /* TODO: add verification here! */ chudnovsky_free_pthreads: free(pthreads); chudnovsky_exit: return res; }