1 module rmathd.choose;
2 
3 public import rmathd.common;
4 public import rmathd.beta;
5 
6 
7 /*
8 ** dmd choose.d common.d beta.d && ./choose
9 */
10 
11 
12 T lfastchoose(T)(T n, T k)
13 {
14     return -log(n + 1.) - lbeta!T(n - k + 1., k + 1.);
15 }
16 
17 
18 /* mathematically the same:
19    less stable typically, but useful if n-k+1 < 0 : */
20 static T lfastchoose2(T)(T n, T k, int *s_choose)
21 {
22     T r;
23     r = lgammafn_sign(n - k + 1., s_choose);
24     return lgammafn(n + 1.) - lgammafn(k + 1.) - r;
25 }
26 
27 
28 template ODD(T){
29 	T ODD(T k){
30 		return (k != 2 * floor(k/2.0));
31 	}
32 }
33 
34 
35 T lchoose(T: double)(T n, T k)
36 {
37     T k0 = k;
38     k = nearbyint(k);
39     
40     /* NaNs propagated correctly */
41     if(isNaN(n) || isNaN(k))
42     	return n + k;
43     
44     if (fabs(k - k0) > 1e-7){
45 	    //MATHLIB_WARNING2(_("'k' (%.2f) must be integer, rounded to %.0f"), k0, k);
46 	    doNothing();
47     }
48     
49     if (k < 2) {
50 	    if (k <	 0)
51 	    	return -T.infinity;
52 	    if (k == 0)
53 	    	return 0.;
54 	    /* else: k == 1 */
55 	    return log(fabs(n));
56     }
57     /* else: k >= 2 */
58     if (n < 0) {
59 	    return lchoose!T(-n + k - 1, k);
60     } else if (!R_nonint(n)) {
61 	    n = nearbyint(n);
62 	    if(n < k)
63 	    	return -T.infinity;
64 	    /* k <= n :*/
65 	    if(n - k < 2)
66 	    	return lchoose!T(n, n - k); /* <- Symmetry */
67 	    /* else: n >= k+2 */
68 	    return lfastchoose!T(n, k);
69     }
70     /* else non-integer n >= 0 : */
71     if (n < k - 1) {
72 	    int s;
73 	    return lfastchoose2!T(n, k, &s);
74     }
75     return lfastchoose!T(n, k);
76 }
77 
78 
79 enum k_small_max = 30;
80 /* 30 is somewhat arbitrary: it is on the *safe* side:
81  * both speed and precision are clearly improved for k < 30.
82 */
83 
84 
85 T choose(T: double)(T n, T k)
86 {
87     T r, k0 = k;
88     k = nearbyint(k);
89     
90     /* NaNs propagated correctly */
91     if(isNaN(n) || isNaN(k))
92     	return n + k;
93     
94     if (fabs(k - k0) > 1e-7){
95 	    //MATHLIB_WARNING2(_("'k' (%.2f) must be integer, rounded to %.0f"), k0, k);
96 	    doNothing();
97     }
98 
99     if (k < k_small_max) {
100 	    int j;
101 	    if(n - k < k && n >= 0 && !R_nonint(n))
102 	    	k = n - k; /* <- Symmetry */
103 	    if (k <	 0)
104 	    	return 0.;
105 	    if (k == 0)
106 	    	return 1.;
107 	    /* else: k >= 1 */
108 	    r = n;
109 	    for(j = 2; j <= k; j++)
110 	        r *= (n - j + 1)/j;
111 	    return !R_nonint(n) ? nearbyint(r) : r;
112 	    /* might have got rounding errors */
113     }
114     /* else: k >= k_small_max */
115     if (n < 0) {
116 	    r = choose!T(-n + k - 1, k);
117 	    if (ODD!T(k)) r = -r;
118 	    return r;
119     }
120     else if (!R_nonint(n)) {
121 	    n = nearbyint(n);
122 	    if(n < k)
123 	    	return 0.;
124 	    if(n - k < k_small_max)
125 	    	return choose!T(n, n - k); /* <- Symmetry */
126 	    return nearbyint(exp(lfastchoose!T(n, k)));
127     }
128     /* else non-integer n >= 0 : */
129     if (n < k - 1) {
130 	    int s_choose;
131 	    r = lfastchoose2(n, k, /* -> */ &s_choose);
132 	    return s_choose * exp(r);
133     }
134     return exp(lfastchoose!T(n, k));
135 }
136 
137 
138 void test_choose()
139 {
140 	import std.stdio: writeln;
141 	writeln("choose(7., 4.): ", choose(7., 4.));
142 	writeln("lchoose(7., 4.): ", lchoose(7., 4.));
143 }
144