Chris@16
|
1 // Copyright John Maddock 2006, 2010.
|
Chris@16
|
2 // Use, modification and distribution are subject to the
|
Chris@16
|
3 // Boost Software License, Version 1.0. (See accompanying file
|
Chris@16
|
4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
5
|
Chris@16
|
6 #ifndef BOOST_MATH_SP_FACTORIALS_HPP
|
Chris@16
|
7 #define BOOST_MATH_SP_FACTORIALS_HPP
|
Chris@16
|
8
|
Chris@16
|
9 #ifdef _MSC_VER
|
Chris@16
|
10 #pragma once
|
Chris@16
|
11 #endif
|
Chris@16
|
12
|
Chris@101
|
13 #include <boost/math/special_functions/math_fwd.hpp>
|
Chris@16
|
14 #include <boost/math/special_functions/gamma.hpp>
|
Chris@16
|
15 #include <boost/math/special_functions/detail/unchecked_factorial.hpp>
|
Chris@16
|
16 #include <boost/array.hpp>
|
Chris@16
|
17 #ifdef BOOST_MSVC
|
Chris@16
|
18 #pragma warning(push) // Temporary until lexical cast fixed.
|
Chris@16
|
19 #pragma warning(disable: 4127 4701)
|
Chris@16
|
20 #endif
|
Chris@16
|
21 #ifdef BOOST_MSVC
|
Chris@16
|
22 #pragma warning(pop)
|
Chris@16
|
23 #endif
|
Chris@16
|
24 #include <boost/config/no_tr1/cmath.hpp>
|
Chris@16
|
25
|
Chris@16
|
26 namespace boost { namespace math
|
Chris@16
|
27 {
|
Chris@16
|
28
|
Chris@16
|
29 template <class T, class Policy>
|
Chris@16
|
30 inline T factorial(unsigned i, const Policy& pol)
|
Chris@16
|
31 {
|
Chris@16
|
32 BOOST_STATIC_ASSERT(!boost::is_integral<T>::value);
|
Chris@16
|
33 // factorial<unsigned int>(n) is not implemented
|
Chris@16
|
34 // because it would overflow integral type T for too small n
|
Chris@16
|
35 // to be useful. Use instead a floating-point type,
|
Chris@16
|
36 // and convert to an unsigned type if essential, for example:
|
Chris@16
|
37 // unsigned int nfac = static_cast<unsigned int>(factorial<double>(n));
|
Chris@16
|
38 // See factorial documentation for more detail.
|
Chris@16
|
39
|
Chris@16
|
40 BOOST_MATH_STD_USING // Aid ADL for floor.
|
Chris@16
|
41
|
Chris@16
|
42 if(i <= max_factorial<T>::value)
|
Chris@16
|
43 return unchecked_factorial<T>(i);
|
Chris@16
|
44 T result = boost::math::tgamma(static_cast<T>(i+1), pol);
|
Chris@16
|
45 if(result > tools::max_value<T>())
|
Chris@16
|
46 return result; // Overflowed value! (But tgamma will have signalled the error already).
|
Chris@16
|
47 return floor(result + 0.5f);
|
Chris@16
|
48 }
|
Chris@16
|
49
|
Chris@16
|
50 template <class T>
|
Chris@16
|
51 inline T factorial(unsigned i)
|
Chris@16
|
52 {
|
Chris@16
|
53 return factorial<T>(i, policies::policy<>());
|
Chris@16
|
54 }
|
Chris@16
|
55 /*
|
Chris@16
|
56 // Can't have these in a policy enabled world?
|
Chris@16
|
57 template<>
|
Chris@16
|
58 inline float factorial<float>(unsigned i)
|
Chris@16
|
59 {
|
Chris@16
|
60 if(i <= max_factorial<float>::value)
|
Chris@16
|
61 return unchecked_factorial<float>(i);
|
Chris@16
|
62 return tools::overflow_error<float>(BOOST_CURRENT_FUNCTION);
|
Chris@16
|
63 }
|
Chris@16
|
64
|
Chris@16
|
65 template<>
|
Chris@16
|
66 inline double factorial<double>(unsigned i)
|
Chris@16
|
67 {
|
Chris@16
|
68 if(i <= max_factorial<double>::value)
|
Chris@16
|
69 return unchecked_factorial<double>(i);
|
Chris@16
|
70 return tools::overflow_error<double>(BOOST_CURRENT_FUNCTION);
|
Chris@16
|
71 }
|
Chris@16
|
72 */
|
Chris@16
|
73 template <class T, class Policy>
|
Chris@16
|
74 T double_factorial(unsigned i, const Policy& pol)
|
Chris@16
|
75 {
|
Chris@16
|
76 BOOST_STATIC_ASSERT(!boost::is_integral<T>::value);
|
Chris@16
|
77 BOOST_MATH_STD_USING // ADL lookup of std names
|
Chris@16
|
78 if(i & 1)
|
Chris@16
|
79 {
|
Chris@16
|
80 // odd i:
|
Chris@16
|
81 if(i < max_factorial<T>::value)
|
Chris@16
|
82 {
|
Chris@16
|
83 unsigned n = (i - 1) / 2;
|
Chris@16
|
84 return ceil(unchecked_factorial<T>(i) / (ldexp(T(1), (int)n) * unchecked_factorial<T>(n)) - 0.5f);
|
Chris@16
|
85 }
|
Chris@16
|
86 //
|
Chris@16
|
87 // Fallthrough: i is too large to use table lookup, try the
|
Chris@16
|
88 // gamma function instead.
|
Chris@16
|
89 //
|
Chris@16
|
90 T result = boost::math::tgamma(static_cast<T>(i) / 2 + 1, pol) / sqrt(constants::pi<T>());
|
Chris@16
|
91 if(ldexp(tools::max_value<T>(), -static_cast<int>(i+1) / 2) > result)
|
Chris@16
|
92 return ceil(result * ldexp(T(1), static_cast<int>(i+1) / 2) - 0.5f);
|
Chris@16
|
93 }
|
Chris@16
|
94 else
|
Chris@16
|
95 {
|
Chris@16
|
96 // even i:
|
Chris@16
|
97 unsigned n = i / 2;
|
Chris@16
|
98 T result = factorial<T>(n, pol);
|
Chris@16
|
99 if(ldexp(tools::max_value<T>(), -(int)n) > result)
|
Chris@16
|
100 return result * ldexp(T(1), (int)n);
|
Chris@16
|
101 }
|
Chris@16
|
102 //
|
Chris@16
|
103 // If we fall through to here then the result is infinite:
|
Chris@16
|
104 //
|
Chris@16
|
105 return policies::raise_overflow_error<T>("boost::math::double_factorial<%1%>(unsigned)", 0, pol);
|
Chris@16
|
106 }
|
Chris@16
|
107
|
Chris@16
|
108 template <class T>
|
Chris@16
|
109 inline T double_factorial(unsigned i)
|
Chris@16
|
110 {
|
Chris@16
|
111 return double_factorial<T>(i, policies::policy<>());
|
Chris@16
|
112 }
|
Chris@16
|
113
|
Chris@16
|
114 namespace detail{
|
Chris@16
|
115
|
Chris@16
|
116 template <class T, class Policy>
|
Chris@16
|
117 T rising_factorial_imp(T x, int n, const Policy& pol)
|
Chris@16
|
118 {
|
Chris@16
|
119 BOOST_STATIC_ASSERT(!boost::is_integral<T>::value);
|
Chris@16
|
120 if(x < 0)
|
Chris@16
|
121 {
|
Chris@16
|
122 //
|
Chris@16
|
123 // For x less than zero, we really have a falling
|
Chris@16
|
124 // factorial, modulo a possible change of sign.
|
Chris@16
|
125 //
|
Chris@16
|
126 // Note that the falling factorial isn't defined
|
Chris@16
|
127 // for negative n, so we'll get rid of that case
|
Chris@16
|
128 // first:
|
Chris@16
|
129 //
|
Chris@16
|
130 bool inv = false;
|
Chris@16
|
131 if(n < 0)
|
Chris@16
|
132 {
|
Chris@16
|
133 x += n;
|
Chris@16
|
134 n = -n;
|
Chris@16
|
135 inv = true;
|
Chris@16
|
136 }
|
Chris@16
|
137 T result = ((n&1) ? -1 : 1) * falling_factorial(-x, n, pol);
|
Chris@16
|
138 if(inv)
|
Chris@16
|
139 result = 1 / result;
|
Chris@16
|
140 return result;
|
Chris@16
|
141 }
|
Chris@16
|
142 if(n == 0)
|
Chris@16
|
143 return 1;
|
Chris@101
|
144 if(x == 0)
|
Chris@101
|
145 {
|
Chris@101
|
146 if(n < 0)
|
Chris@101
|
147 return -boost::math::tgamma_delta_ratio(x + 1, static_cast<T>(-n), pol);
|
Chris@101
|
148 else
|
Chris@101
|
149 return 0;
|
Chris@101
|
150 }
|
Chris@101
|
151 if((x < 1) && (x + n < 0))
|
Chris@101
|
152 {
|
Chris@101
|
153 T val = boost::math::tgamma_delta_ratio(1 - x, static_cast<T>(-n), pol);
|
Chris@101
|
154 return (n & 1) ? T(-val) : val;
|
Chris@101
|
155 }
|
Chris@16
|
156 //
|
Chris@16
|
157 // We don't optimise this for small n, because
|
Chris@16
|
158 // tgamma_delta_ratio is alreay optimised for that
|
Chris@16
|
159 // use case:
|
Chris@16
|
160 //
|
Chris@16
|
161 return 1 / boost::math::tgamma_delta_ratio(x, static_cast<T>(n), pol);
|
Chris@16
|
162 }
|
Chris@16
|
163
|
Chris@16
|
164 template <class T, class Policy>
|
Chris@16
|
165 inline T falling_factorial_imp(T x, unsigned n, const Policy& pol)
|
Chris@16
|
166 {
|
Chris@16
|
167 BOOST_STATIC_ASSERT(!boost::is_integral<T>::value);
|
Chris@16
|
168 BOOST_MATH_STD_USING // ADL of std names
|
Chris@101
|
169 if((x == 0) && (n >= 0))
|
Chris@16
|
170 return 0;
|
Chris@16
|
171 if(x < 0)
|
Chris@16
|
172 {
|
Chris@16
|
173 //
|
Chris@16
|
174 // For x < 0 we really have a rising factorial
|
Chris@16
|
175 // modulo a possible change of sign:
|
Chris@16
|
176 //
|
Chris@16
|
177 return (n&1 ? -1 : 1) * rising_factorial(-x, n, pol);
|
Chris@16
|
178 }
|
Chris@16
|
179 if(n == 0)
|
Chris@16
|
180 return 1;
|
Chris@101
|
181 if(x < 0.5f)
|
Chris@101
|
182 {
|
Chris@101
|
183 //
|
Chris@101
|
184 // 1 + x below will throw away digits, so split up calculation:
|
Chris@101
|
185 //
|
Chris@101
|
186 if(n > max_factorial<T>::value - 2)
|
Chris@101
|
187 {
|
Chris@101
|
188 // If the two end of the range are far apart we have a ratio of two very large
|
Chris@101
|
189 // numbers, split the calculation up into two blocks:
|
Chris@101
|
190 T t1 = x * boost::math::falling_factorial(x - 1, max_factorial<T>::value - 2);
|
Chris@101
|
191 T t2 = boost::math::falling_factorial(x - max_factorial<T>::value + 1, n - max_factorial<T>::value + 1);
|
Chris@101
|
192 if(tools::max_value<T>() / fabs(t1) < fabs(t2))
|
Chris@101
|
193 return boost::math::sign(t1) * boost::math::sign(t2) * policies::raise_overflow_error<T>("boost::math::falling_factorial<%1%>", 0, pol);
|
Chris@101
|
194 return t1 * t2;
|
Chris@101
|
195 }
|
Chris@101
|
196 return x * boost::math::falling_factorial(x - 1, n - 1);
|
Chris@101
|
197 }
|
Chris@101
|
198 if(x <= n - 1)
|
Chris@16
|
199 {
|
Chris@16
|
200 //
|
Chris@16
|
201 // x+1-n will be negative and tgamma_delta_ratio won't
|
Chris@16
|
202 // handle it, split the product up into three parts:
|
Chris@16
|
203 //
|
Chris@16
|
204 T xp1 = x + 1;
|
Chris@16
|
205 unsigned n2 = itrunc((T)floor(xp1), pol);
|
Chris@16
|
206 if(n2 == xp1)
|
Chris@16
|
207 return 0;
|
Chris@16
|
208 T result = boost::math::tgamma_delta_ratio(xp1, -static_cast<T>(n2), pol);
|
Chris@16
|
209 x -= n2;
|
Chris@16
|
210 result *= x;
|
Chris@16
|
211 ++n2;
|
Chris@16
|
212 if(n2 < n)
|
Chris@16
|
213 result *= falling_factorial(x - 1, n - n2, pol);
|
Chris@16
|
214 return result;
|
Chris@16
|
215 }
|
Chris@16
|
216 //
|
Chris@16
|
217 // Simple case: just the ratio of two
|
Chris@16
|
218 // (positive argument) gamma functions.
|
Chris@16
|
219 // Note that we don't optimise this for small n,
|
Chris@16
|
220 // because tgamma_delta_ratio is alreay optimised
|
Chris@16
|
221 // for that use case:
|
Chris@16
|
222 //
|
Chris@16
|
223 return boost::math::tgamma_delta_ratio(x + 1, -static_cast<T>(n), pol);
|
Chris@16
|
224 }
|
Chris@16
|
225
|
Chris@16
|
226 } // namespace detail
|
Chris@16
|
227
|
Chris@16
|
228 template <class RT>
|
Chris@16
|
229 inline typename tools::promote_args<RT>::type
|
Chris@16
|
230 falling_factorial(RT x, unsigned n)
|
Chris@16
|
231 {
|
Chris@16
|
232 typedef typename tools::promote_args<RT>::type result_type;
|
Chris@16
|
233 return detail::falling_factorial_imp(
|
Chris@16
|
234 static_cast<result_type>(x), n, policies::policy<>());
|
Chris@16
|
235 }
|
Chris@16
|
236
|
Chris@16
|
237 template <class RT, class Policy>
|
Chris@16
|
238 inline typename tools::promote_args<RT>::type
|
Chris@16
|
239 falling_factorial(RT x, unsigned n, const Policy& pol)
|
Chris@16
|
240 {
|
Chris@16
|
241 typedef typename tools::promote_args<RT>::type result_type;
|
Chris@16
|
242 return detail::falling_factorial_imp(
|
Chris@16
|
243 static_cast<result_type>(x), n, pol);
|
Chris@16
|
244 }
|
Chris@16
|
245
|
Chris@16
|
246 template <class RT>
|
Chris@16
|
247 inline typename tools::promote_args<RT>::type
|
Chris@16
|
248 rising_factorial(RT x, int n)
|
Chris@16
|
249 {
|
Chris@16
|
250 typedef typename tools::promote_args<RT>::type result_type;
|
Chris@16
|
251 return detail::rising_factorial_imp(
|
Chris@16
|
252 static_cast<result_type>(x), n, policies::policy<>());
|
Chris@16
|
253 }
|
Chris@16
|
254
|
Chris@16
|
255 template <class RT, class Policy>
|
Chris@16
|
256 inline typename tools::promote_args<RT>::type
|
Chris@16
|
257 rising_factorial(RT x, int n, const Policy& pol)
|
Chris@16
|
258 {
|
Chris@16
|
259 typedef typename tools::promote_args<RT>::type result_type;
|
Chris@16
|
260 return detail::rising_factorial_imp(
|
Chris@16
|
261 static_cast<result_type>(x), n, pol);
|
Chris@16
|
262 }
|
Chris@16
|
263
|
Chris@16
|
264 } // namespace math
|
Chris@16
|
265 } // namespace boost
|
Chris@16
|
266
|
Chris@16
|
267 #endif // BOOST_MATH_SP_FACTORIALS_HPP
|
Chris@16
|
268
|