factorials.hpp 6.8 KB

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