1*658eb9e1SMichael Kruse /*
2*658eb9e1SMichael Kruse Name: pi.c
3*658eb9e1SMichael Kruse Purpose: Computes digits of the physical constant pi.
4*658eb9e1SMichael Kruse Author: M. J. Fromberger
5*658eb9e1SMichael Kruse
6*658eb9e1SMichael Kruse Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved.
7*658eb9e1SMichael Kruse
8*658eb9e1SMichael Kruse Notes:
9*658eb9e1SMichael Kruse Uses Machin's formula, which should be suitable for a few thousand digits.
10*658eb9e1SMichael Kruse
11*658eb9e1SMichael Kruse Permission is hereby granted, free of charge, to any person obtaining a copy
12*658eb9e1SMichael Kruse of this software and associated documentation files (the "Software"), to deal
13*658eb9e1SMichael Kruse in the Software without restriction, including without limitation the rights
14*658eb9e1SMichael Kruse to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15*658eb9e1SMichael Kruse copies of the Software, and to permit persons to whom the Software is
16*658eb9e1SMichael Kruse furnished to do so, subject to the following conditions:
17*658eb9e1SMichael Kruse
18*658eb9e1SMichael Kruse The above copyright notice and this permission notice shall be included in
19*658eb9e1SMichael Kruse all copies or substantial portions of the Software.
20*658eb9e1SMichael Kruse
21*658eb9e1SMichael Kruse THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22*658eb9e1SMichael Kruse IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23*658eb9e1SMichael Kruse FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24*658eb9e1SMichael Kruse AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25*658eb9e1SMichael Kruse LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26*658eb9e1SMichael Kruse OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27*658eb9e1SMichael Kruse SOFTWARE.
28*658eb9e1SMichael Kruse */
29*658eb9e1SMichael Kruse
30*658eb9e1SMichael Kruse #include <stdio.h>
31*658eb9e1SMichael Kruse #include <stdlib.h>
32*658eb9e1SMichael Kruse #include <string.h>
33*658eb9e1SMichael Kruse #include <time.h>
34*658eb9e1SMichael Kruse
35*658eb9e1SMichael Kruse #include "imath.h"
36*658eb9e1SMichael Kruse
37*658eb9e1SMichael Kruse int g_radix = 10; /* use this radix for output */
38*658eb9e1SMichael Kruse
39*658eb9e1SMichael Kruse mp_result arctan(mp_small radix, mp_small mul, mp_small x, mp_small prec,
40*658eb9e1SMichael Kruse mp_int sum);
41*658eb9e1SMichael Kruse
42*658eb9e1SMichael Kruse char g_buf[4096];
43*658eb9e1SMichael Kruse
main(int argc,char * argv[])44*658eb9e1SMichael Kruse int main(int argc, char *argv[]) {
45*658eb9e1SMichael Kruse mp_result res;
46*658eb9e1SMichael Kruse mpz_t sum1, sum2;
47*658eb9e1SMichael Kruse int ndigits, out = 0;
48*658eb9e1SMichael Kruse clock_t start, end;
49*658eb9e1SMichael Kruse
50*658eb9e1SMichael Kruse if (argc < 2) {
51*658eb9e1SMichael Kruse fprintf(stderr, "Usage: %s <num-digits> [<radix>]\n", argv[0]);
52*658eb9e1SMichael Kruse return 1;
53*658eb9e1SMichael Kruse }
54*658eb9e1SMichael Kruse
55*658eb9e1SMichael Kruse if ((ndigits = abs(atoi(argv[1]))) == 0) {
56*658eb9e1SMichael Kruse fprintf(stderr, "%s: you must request at least 1 digit\n", argv[0]);
57*658eb9e1SMichael Kruse return 1;
58*658eb9e1SMichael Kruse } else if ((mp_word)ndigits > MP_DIGIT_MAX) {
59*658eb9e1SMichael Kruse fprintf(stderr, "%s: you may request at most %u digits\n", argv[0],
60*658eb9e1SMichael Kruse (unsigned int)MP_DIGIT_MAX);
61*658eb9e1SMichael Kruse return 1;
62*658eb9e1SMichael Kruse }
63*658eb9e1SMichael Kruse
64*658eb9e1SMichael Kruse if (argc > 2) {
65*658eb9e1SMichael Kruse int radix = atoi(argv[2]);
66*658eb9e1SMichael Kruse
67*658eb9e1SMichael Kruse if (radix < MP_MIN_RADIX || radix > MP_MAX_RADIX) {
68*658eb9e1SMichael Kruse fprintf(stderr, "%s: you may only specify a radix between %d and %d\n",
69*658eb9e1SMichael Kruse argv[0], MP_MIN_RADIX, MP_MAX_RADIX);
70*658eb9e1SMichael Kruse return 1;
71*658eb9e1SMichael Kruse }
72*658eb9e1SMichael Kruse g_radix = radix;
73*658eb9e1SMichael Kruse }
74*658eb9e1SMichael Kruse
75*658eb9e1SMichael Kruse mp_int_init(&sum1);
76*658eb9e1SMichael Kruse mp_int_init(&sum2);
77*658eb9e1SMichael Kruse start = clock();
78*658eb9e1SMichael Kruse
79*658eb9e1SMichael Kruse /* sum1 = 16 * arctan(1/5) */
80*658eb9e1SMichael Kruse if ((res = arctan(g_radix, 16, 5, ndigits, &sum1)) != MP_OK) {
81*658eb9e1SMichael Kruse fprintf(stderr, "%s: error computing arctan: %d\n", argv[0], res);
82*658eb9e1SMichael Kruse out = 1;
83*658eb9e1SMichael Kruse goto CLEANUP;
84*658eb9e1SMichael Kruse }
85*658eb9e1SMichael Kruse
86*658eb9e1SMichael Kruse /* sum2 = 4 * arctan(1/239) */
87*658eb9e1SMichael Kruse if ((res = arctan(g_radix, 4, 239, ndigits, &sum2)) != MP_OK) {
88*658eb9e1SMichael Kruse fprintf(stderr, "%s: error computing arctan: %d\n", argv[0], res);
89*658eb9e1SMichael Kruse out = 1;
90*658eb9e1SMichael Kruse goto CLEANUP;
91*658eb9e1SMichael Kruse }
92*658eb9e1SMichael Kruse
93*658eb9e1SMichael Kruse /* pi = sum1 - sum2 */
94*658eb9e1SMichael Kruse if ((res = mp_int_sub(&sum1, &sum2, &sum1)) != MP_OK) {
95*658eb9e1SMichael Kruse fprintf(stderr, "%s: error computing pi: %d\n", argv[0], res);
96*658eb9e1SMichael Kruse out = 1;
97*658eb9e1SMichael Kruse goto CLEANUP;
98*658eb9e1SMichael Kruse }
99*658eb9e1SMichael Kruse end = clock();
100*658eb9e1SMichael Kruse
101*658eb9e1SMichael Kruse mp_int_to_string(&sum1, g_radix, g_buf, sizeof(g_buf));
102*658eb9e1SMichael Kruse printf("%c.%s\n", g_buf[0], g_buf + 1);
103*658eb9e1SMichael Kruse
104*658eb9e1SMichael Kruse fprintf(stderr, "Computation took %.2f sec.\n",
105*658eb9e1SMichael Kruse (double)(end - start) / CLOCKS_PER_SEC);
106*658eb9e1SMichael Kruse
107*658eb9e1SMichael Kruse CLEANUP:
108*658eb9e1SMichael Kruse mp_int_clear(&sum1);
109*658eb9e1SMichael Kruse mp_int_clear(&sum2);
110*658eb9e1SMichael Kruse
111*658eb9e1SMichael Kruse return out;
112*658eb9e1SMichael Kruse }
113*658eb9e1SMichael Kruse
114*658eb9e1SMichael Kruse /*
115*658eb9e1SMichael Kruse Compute mul * atan(1/x) to prec digits of precision, and store the
116*658eb9e1SMichael Kruse result in sum.
117*658eb9e1SMichael Kruse
118*658eb9e1SMichael Kruse Computes atan(1/x) using the formula:
119*658eb9e1SMichael Kruse
120*658eb9e1SMichael Kruse 1 1 1 1
121*658eb9e1SMichael Kruse atan(1/x) = --- - ---- + ---- - ---- + ...
122*658eb9e1SMichael Kruse x 3x^3 5x^5 7x^7
123*658eb9e1SMichael Kruse
124*658eb9e1SMichael Kruse */
arctan(mp_small radix,mp_small mul,mp_small x,mp_small prec,mp_int sum)125*658eb9e1SMichael Kruse mp_result arctan(mp_small radix, mp_small mul, mp_small x, mp_small prec,
126*658eb9e1SMichael Kruse mp_int sum) {
127*658eb9e1SMichael Kruse mpz_t t, v;
128*658eb9e1SMichael Kruse mp_result res;
129*658eb9e1SMichael Kruse mp_small rem, sign = 1, coeff = 1;
130*658eb9e1SMichael Kruse
131*658eb9e1SMichael Kruse mp_int_init(&t);
132*658eb9e1SMichael Kruse mp_int_init(&v);
133*658eb9e1SMichael Kruse ++prec;
134*658eb9e1SMichael Kruse
135*658eb9e1SMichael Kruse /* Compute mul * radix^prec * x
136*658eb9e1SMichael Kruse The initial multiplication by x saves a special case in the loop for
137*658eb9e1SMichael Kruse the first term of the series.
138*658eb9e1SMichael Kruse */
139*658eb9e1SMichael Kruse if ((res = mp_int_expt_value(radix, prec, &t)) != MP_OK ||
140*658eb9e1SMichael Kruse (res = mp_int_mul_value(&t, mul, &t)) != MP_OK ||
141*658eb9e1SMichael Kruse (res = mp_int_mul_value(&t, x, &t)) != MP_OK)
142*658eb9e1SMichael Kruse goto CLEANUP;
143*658eb9e1SMichael Kruse
144*658eb9e1SMichael Kruse x *= x; /* assumes x <= sqrt(MP_SMALL_MAX) */
145*658eb9e1SMichael Kruse mp_int_zero(sum);
146*658eb9e1SMichael Kruse
147*658eb9e1SMichael Kruse do {
148*658eb9e1SMichael Kruse if ((res = mp_int_div_value(&t, x, &t, &rem)) != MP_OK) goto CLEANUP;
149*658eb9e1SMichael Kruse
150*658eb9e1SMichael Kruse if ((res = mp_int_div_value(&t, coeff, &v, &rem)) != MP_OK) goto CLEANUP;
151*658eb9e1SMichael Kruse
152*658eb9e1SMichael Kruse /* Add or subtract the result depending on the current sign (1 = add) */
153*658eb9e1SMichael Kruse if (sign > 0)
154*658eb9e1SMichael Kruse res = mp_int_add(sum, &v, sum);
155*658eb9e1SMichael Kruse else
156*658eb9e1SMichael Kruse res = mp_int_sub(sum, &v, sum);
157*658eb9e1SMichael Kruse
158*658eb9e1SMichael Kruse if (res != MP_OK) goto CLEANUP;
159*658eb9e1SMichael Kruse sign = -sign;
160*658eb9e1SMichael Kruse coeff += 2;
161*658eb9e1SMichael Kruse
162*658eb9e1SMichael Kruse } while (mp_int_compare_zero(&t) != 0);
163*658eb9e1SMichael Kruse
164*658eb9e1SMichael Kruse res = mp_int_div_value(sum, radix, sum, NULL);
165*658eb9e1SMichael Kruse
166*658eb9e1SMichael Kruse CLEANUP:
167*658eb9e1SMichael Kruse mp_int_clear(&v);
168*658eb9e1SMichael Kruse mp_int_clear(&t);
169*658eb9e1SMichael Kruse
170*658eb9e1SMichael Kruse return res;
171*658eb9e1SMichael Kruse }
172*658eb9e1SMichael Kruse
173*658eb9e1SMichael Kruse /* Here there be dragons */
174