security/nss/lib/freebl/mpi/utils/pi.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * pi.c
michael@0 3 *
michael@0 4 * Compute pi to an arbitrary number of digits. Uses Machin's formula,
michael@0 5 * like everyone else on the planet:
michael@0 6 *
michael@0 7 * pi = 16 * arctan(1/5) - 4 * arctan(1/239)
michael@0 8 *
michael@0 9 * This is pretty effective for up to a few thousand digits, but it
michael@0 10 * gets pretty slow after that.
michael@0 11 *
michael@0 12 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 13 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 14 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 15
michael@0 16 #include <stdio.h>
michael@0 17 #include <stdlib.h>
michael@0 18 #include <string.h>
michael@0 19 #include <limits.h>
michael@0 20 #include <time.h>
michael@0 21
michael@0 22 #include "mpi.h"
michael@0 23
michael@0 24 mp_err arctan(mp_digit mul, mp_digit x, mp_digit prec, mp_int *sum);
michael@0 25
michael@0 26 int main(int argc, char *argv[])
michael@0 27 {
michael@0 28 mp_err res;
michael@0 29 mp_digit ndigits;
michael@0 30 mp_int sum1, sum2;
michael@0 31 clock_t start, stop;
michael@0 32 int out = 0;
michael@0 33
michael@0 34 /* Make the user specify precision on the command line */
michael@0 35 if(argc < 2) {
michael@0 36 fprintf(stderr, "Usage: %s <num-digits>\n", argv[0]);
michael@0 37 return 1;
michael@0 38 }
michael@0 39
michael@0 40 if((ndigits = abs(atoi(argv[1]))) == 0) {
michael@0 41 fprintf(stderr, "%s: you must request at least 1 digit\n", argv[0]);
michael@0 42 return 1;
michael@0 43 }
michael@0 44
michael@0 45 start = clock();
michael@0 46 mp_init(&sum1); mp_init(&sum2);
michael@0 47
michael@0 48 /* sum1 = 16 * arctan(1/5) */
michael@0 49 if((res = arctan(16, 5, ndigits, &sum1)) != MP_OKAY) {
michael@0 50 fprintf(stderr, "%s: arctan: %s\n", argv[0], mp_strerror(res));
michael@0 51 out = 1; goto CLEANUP;
michael@0 52 }
michael@0 53
michael@0 54 /* sum2 = 4 * arctan(1/239) */
michael@0 55 if((res = arctan(4, 239, ndigits, &sum2)) != MP_OKAY) {
michael@0 56 fprintf(stderr, "%s: arctan: %s\n", argv[0], mp_strerror(res));
michael@0 57 out = 1; goto CLEANUP;
michael@0 58 }
michael@0 59
michael@0 60 /* pi = sum1 - sum2 */
michael@0 61 if((res = mp_sub(&sum1, &sum2, &sum1)) != MP_OKAY) {
michael@0 62 fprintf(stderr, "%s: mp_sub: %s\n", argv[0], mp_strerror(res));
michael@0 63 out = 1; goto CLEANUP;
michael@0 64 }
michael@0 65 stop = clock();
michael@0 66
michael@0 67 /* Write the output in decimal */
michael@0 68 {
michael@0 69 char *buf = malloc(mp_radix_size(&sum1, 10));
michael@0 70
michael@0 71 if(buf == NULL) {
michael@0 72 fprintf(stderr, "%s: out of memory\n", argv[0]);
michael@0 73 out = 1; goto CLEANUP;
michael@0 74 }
michael@0 75 mp_todecimal(&sum1, buf);
michael@0 76 printf("%s\n", buf);
michael@0 77 free(buf);
michael@0 78 }
michael@0 79
michael@0 80 fprintf(stderr, "Computation took %.2f sec.\n",
michael@0 81 (double)(stop - start) / CLOCKS_PER_SEC);
michael@0 82
michael@0 83 CLEANUP:
michael@0 84 mp_clear(&sum1);
michael@0 85 mp_clear(&sum2);
michael@0 86
michael@0 87 return out;
michael@0 88
michael@0 89 }
michael@0 90
michael@0 91 /* Compute sum := mul * arctan(1/x), to 'prec' digits of precision */
michael@0 92 mp_err arctan(mp_digit mul, mp_digit x, mp_digit prec, mp_int *sum)
michael@0 93 {
michael@0 94 mp_int t, v;
michael@0 95 mp_digit q = 1, rd;
michael@0 96 mp_err res;
michael@0 97 int sign = 1;
michael@0 98
michael@0 99 prec += 3; /* push inaccuracies off the end */
michael@0 100
michael@0 101 mp_init(&t); mp_set(&t, 10);
michael@0 102 mp_init(&v);
michael@0 103 if((res = mp_expt_d(&t, prec, &t)) != MP_OKAY || /* get 10^prec */
michael@0 104 (res = mp_mul_d(&t, mul, &t)) != MP_OKAY || /* ... times mul */
michael@0 105 (res = mp_mul_d(&t, x, &t)) != MP_OKAY) /* ... times x */
michael@0 106 goto CLEANUP;
michael@0 107
michael@0 108 /*
michael@0 109 The extra multiplication by x in the above takes care of what
michael@0 110 would otherwise have to be a special case for 1 / x^1 during the
michael@0 111 first loop iteration. A little sneaky, but effective.
michael@0 112
michael@0 113 We compute arctan(1/x) by the formula:
michael@0 114
michael@0 115 1 1 1 1
michael@0 116 - - ----- + ----- - ----- + ...
michael@0 117 x 3 x^3 5 x^5 7 x^7
michael@0 118
michael@0 119 We multiply through by 'mul' beforehand, which gives us a couple
michael@0 120 more iterations and more precision
michael@0 121 */
michael@0 122
michael@0 123 x *= x; /* works as long as x < sqrt(RADIX), which it is here */
michael@0 124
michael@0 125 mp_zero(sum);
michael@0 126
michael@0 127 do {
michael@0 128 if((res = mp_div_d(&t, x, &t, &rd)) != MP_OKAY)
michael@0 129 goto CLEANUP;
michael@0 130
michael@0 131 if(sign < 0 && rd != 0)
michael@0 132 mp_add_d(&t, 1, &t);
michael@0 133
michael@0 134 if((res = mp_div_d(&t, q, &v, &rd)) != MP_OKAY)
michael@0 135 goto CLEANUP;
michael@0 136
michael@0 137 if(sign < 0 && rd != 0)
michael@0 138 mp_add_d(&v, 1, &v);
michael@0 139
michael@0 140 if(sign > 0)
michael@0 141 res = mp_add(sum, &v, sum);
michael@0 142 else
michael@0 143 res = mp_sub(sum, &v, sum);
michael@0 144
michael@0 145 if(res != MP_OKAY)
michael@0 146 goto CLEANUP;
michael@0 147
michael@0 148 sign *= -1;
michael@0 149 q += 2;
michael@0 150
michael@0 151 } while(mp_cmp_z(&t) != 0);
michael@0 152
michael@0 153 /* Chop off inaccurate low-order digits */
michael@0 154 mp_div_d(sum, 1000, sum, NULL);
michael@0 155
michael@0 156 CLEANUP:
michael@0 157 mp_clear(&v);
michael@0 158 mp_clear(&t);
michael@0 159
michael@0 160 return res;
michael@0 161 }
michael@0 162
michael@0 163 /*------------------------------------------------------------------------*/
michael@0 164 /* HERE THERE BE DRAGONS */

mercurial