factorials.hpp 8.5 KB

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