File: gamma.5c

package info (click to toggle)
nickle 2.107
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 3,756 kB
  • sloc: ansic: 27,954; yacc: 1,874; lex: 954; sh: 204; makefile: 13; lisp: 1
file content (130 lines) | stat: -rw-r--r-- 3,338 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
extend namespace Math {

	public real gamma(real n)
		/*
		 * Stieljes continuing fraction method of computing
		 * gamma
		 */
	{
		real Stieltjes(real n, int ord, int bits) {

			/*
			 * Compute the continuing fraction coefficients
			 */
			real[*] StieltjesCF(int len, int bits) {

				/* Compute a set of Bernouli numbers
				 * using the Akiyama-Tanigawa algorithm
				 */
				real[*] AkiyamaTanigawa(int l, int bits) {
					int n = 2 * l + 1;
					real[n] t = { imprecise(1, bits) };
					real[l] a;
					int k = 1;
					for (int m = 2; m <= n; m++) {
						t[m-1] = 1/imprecise(m, bits);
						for (int j = m-1; j >= 1; j--)
							t[j-1] = j * (t[j-1] - t[j]);
						if ((m & 1) != 0) {
							real rk = imprecise(k, bits);
							real	v = t[0]/((2*rk-1)*(2*rk));
							if ((k & 1) == 0)
								a[k-1] = -v;
							else
								a[k-1] = v;
							k++;
						}
					}
					return a;
				}
				real[*] s = AkiyamaTanigawa(len, bits);
				real[len,len] m;

				for (int n = 0; n < len; n++)
					m[n,0] = 0;
				for (int n = 0; n < len-1; n++)
					m[n,1] = s[n+1]/s[n];
				for (int k = 3; k <= len; k++) {
					for (int n = 1; n <= len - k + 1; n++) {
						real a = m[n,k-3];
						real b = m[n,k-2];
						real c = m[n-1,k-2];
						if ((k & 1) != 0)
							m[n-1,k-1] = a + b - c;
						else
							m[n-1,k-1] = a * b / c;
					}
				}
				m[0,0] = s[0];
				return (real[len]) { [k] = m[0,k] };
			}

			real N = imprecise(n + 1, bits);
			real q = N;
			real[*] c = StieltjesCF(ord, bits);
			real one = imprecise(1, bits);
			for (int i = ord; i >= 2; i--)
				q = N + c[i-1] / q;
			return sqrt(2 * pi_value(bits)/N) * (N/exp(one)) ** N * exp(one/(12*q));
		}

		/*
		 * For positive integers, just use factorial
		 */
		if (is_int(n)) {
			if (n <= 0)
				raise invalid_argument("gamma of non-positive integer", 0, n);
			return (floor(n)-1)!;
		}

		n = imprecise(n);
		int bits = precision(n);

		/*
		 * Use Euler's reflection formula to solve for arguments < -½
		 *
		 *    Γ(z) = π/(sin(πz)Γ(1-z))
		 */
		if (n < -0.5) {
			int	ibits = bits + 15;
			real	n_ibits = imprecise(n, ibits);
			real	pi_ibits = pi_value(ibits);
			real	sin_n_pi = sin(pi_ibits * n_ibits);
			real	inv_gamma = gamma(1 - n_ibits);
			/* convert divide-by-zero into invalid_argument */
			real	div = sin_n_pi * inv_gamma;
			if (div == 0)
				raise invalid_argument("gamma of non-positive integer", 0, n);
			return	imprecise(pi_ibits / div, bits);
		}

		/*
		 * Smaller numbers take a lot more coefficients to
		 * get the desired number of bits. Make the value
		 * larger, and increase the desired precision to match,
		 * to make the result converge faster.
		 */
		if (n < 10000) {
			int new_bits = bits + 20 - exponent(n);
			real n_new = imprecise(n, new_bits) + 10000;
			real g = gamma(n_new);
			for (int i = 0; i < 10000; i++) {
				n_new -= 1;
				g = g / n_new;
			}
			return imprecise(g, bits);
		}

		/* This is a rough approximation of the
		 * number of coefficients in the fraction
		 * needed to produce the desired precision, it's
		 * good for any value larger than 10000. Larger numbers
		 * could use a smaller number of coefficients, but we
		 * don't know how much smaller
		 */
		int ord = ceil(bits / 20);
		return imprecise(Stieltjes(n-1, ord, bits + 20), bits);
	}

	public real(real n) Γ = gamma;
}