269 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			269 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | //  Copyright John Maddock 2006, 2010. | ||
|  | //  Use, modification and distribution are subject to the | ||
|  | //  Boost Software License, Version 1.0. (See accompanying file | ||
|  | //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | #ifndef BOOST_MATH_SP_FACTORIALS_HPP | ||
|  | #define BOOST_MATH_SP_FACTORIALS_HPP | ||
|  | 
 | ||
|  | #ifdef _MSC_VER | ||
|  | #pragma once | ||
|  | #endif | ||
|  | 
 | ||
|  | #include <boost/math/special_functions/math_fwd.hpp> | ||
|  | #include <boost/math/special_functions/gamma.hpp> | ||
|  | #include <boost/math/special_functions/detail/unchecked_factorial.hpp> | ||
|  | #include <boost/array.hpp> | ||
|  | #ifdef BOOST_MSVC | ||
|  | #pragma warning(push) // Temporary until lexical cast fixed. | ||
|  | #pragma warning(disable: 4127 4701) | ||
|  | #endif | ||
|  | #ifdef BOOST_MSVC | ||
|  | #pragma warning(pop) | ||
|  | #endif | ||
|  | #include <boost/config/no_tr1/cmath.hpp> | ||
|  | 
 | ||
|  | namespace boost { namespace math | ||
|  | { | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | inline T factorial(unsigned i, const Policy& pol) | ||
|  | { | ||
|  |    BOOST_STATIC_ASSERT(!boost::is_integral<T>::value); | ||
|  |    // factorial<unsigned int>(n) is not implemented | ||
|  |    // because it would overflow integral type T for too small n | ||
|  |    // to be useful. Use instead a floating-point type, | ||
|  |    // and convert to an unsigned type if essential, for example: | ||
|  |    // unsigned int nfac = static_cast<unsigned int>(factorial<double>(n)); | ||
|  |    // See factorial documentation for more detail. | ||
|  | 
 | ||
|  |    BOOST_MATH_STD_USING // Aid ADL for floor. | ||
|  | 
 | ||
|  |    if(i <= max_factorial<T>::value) | ||
|  |       return unchecked_factorial<T>(i); | ||
|  |    T result = boost::math::tgamma(static_cast<T>(i+1), pol); | ||
|  |    if(result > tools::max_value<T>()) | ||
|  |       return result; // Overflowed value! (But tgamma will have signalled the error already). | ||
|  |    return floor(result + 0.5f); | ||
|  | } | ||
|  | 
 | ||
|  | template <class T> | ||
|  | inline T factorial(unsigned i) | ||
|  | { | ||
|  |    return factorial<T>(i, policies::policy<>()); | ||
|  | } | ||
|  | /* | ||
|  | // Can't have these in a policy enabled world? | ||
|  | template<> | ||
|  | inline float factorial<float>(unsigned i) | ||
|  | { | ||
|  |    if(i <= max_factorial<float>::value) | ||
|  |       return unchecked_factorial<float>(i); | ||
|  |    return tools::overflow_error<float>(BOOST_CURRENT_FUNCTION); | ||
|  | } | ||
|  | 
 | ||
|  | template<> | ||
|  | inline double factorial<double>(unsigned i) | ||
|  | { | ||
|  |    if(i <= max_factorial<double>::value) | ||
|  |       return unchecked_factorial<double>(i); | ||
|  |    return tools::overflow_error<double>(BOOST_CURRENT_FUNCTION); | ||
|  | } | ||
|  | */ | ||
|  | template <class T, class Policy> | ||
|  | T double_factorial(unsigned i, const Policy& pol) | ||
|  | { | ||
|  |    BOOST_STATIC_ASSERT(!boost::is_integral<T>::value); | ||
|  |    BOOST_MATH_STD_USING  // ADL lookup of std names | ||
|  |    if(i & 1) | ||
|  |    { | ||
|  |       // odd i: | ||
|  |       if(i < max_factorial<T>::value) | ||
|  |       { | ||
|  |          unsigned n = (i - 1) / 2; | ||
|  |          return ceil(unchecked_factorial<T>(i) / (ldexp(T(1), (int)n) * unchecked_factorial<T>(n)) - 0.5f); | ||
|  |       } | ||
|  |       // | ||
|  |       // Fallthrough: i is too large to use table lookup, try the | ||
|  |       // gamma function instead. | ||
|  |       // | ||
|  |       T result = boost::math::tgamma(static_cast<T>(i) / 2 + 1, pol) / sqrt(constants::pi<T>()); | ||
|  |       if(ldexp(tools::max_value<T>(), -static_cast<int>(i+1) / 2) > result) | ||
|  |          return ceil(result * ldexp(T(1), static_cast<int>(i+1) / 2) - 0.5f); | ||
|  |    } | ||
|  |    else | ||
|  |    { | ||
|  |       // even i: | ||
|  |       unsigned n = i / 2; | ||
|  |       T result = factorial<T>(n, pol); | ||
|  |       if(ldexp(tools::max_value<T>(), -(int)n) > result) | ||
|  |          return result * ldexp(T(1), (int)n); | ||
|  |    } | ||
|  |    // | ||
|  |    // If we fall through to here then the result is infinite: | ||
|  |    // | ||
|  |    return policies::raise_overflow_error<T>("boost::math::double_factorial<%1%>(unsigned)", 0, pol); | ||
|  | } | ||
|  | 
 | ||
|  | template <class T> | ||
|  | inline T double_factorial(unsigned i) | ||
|  | { | ||
|  |    return double_factorial<T>(i, policies::policy<>()); | ||
|  | } | ||
|  | 
 | ||
|  | namespace detail{ | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | T rising_factorial_imp(T x, int n, const Policy& pol) | ||
|  | { | ||
|  |    BOOST_STATIC_ASSERT(!boost::is_integral<T>::value); | ||
|  |    if(x < 0) | ||
|  |    { | ||
|  |       // | ||
|  |       // For x less than zero, we really have a falling | ||
|  |       // factorial, modulo a possible change of sign. | ||
|  |       // | ||
|  |       // Note that the falling factorial isn't defined | ||
|  |       // for negative n, so we'll get rid of that case | ||
|  |       // first: | ||
|  |       // | ||
|  |       bool inv = false; | ||
|  |       if(n < 0) | ||
|  |       { | ||
|  |          x += n; | ||
|  |          n = -n; | ||
|  |          inv = true; | ||
|  |       } | ||
|  |       T result = ((n&1) ? -1 : 1) * falling_factorial(-x, n, pol); | ||
|  |       if(inv) | ||
|  |          result = 1 / result; | ||
|  |       return result; | ||
|  |    } | ||
|  |    if(n == 0) | ||
|  |       return 1; | ||
|  |    if(x == 0) | ||
|  |    { | ||
|  |       if(n < 0) | ||
|  |          return -boost::math::tgamma_delta_ratio(x + 1, static_cast<T>(-n), pol); | ||
|  |       else | ||
|  |          return 0; | ||
|  |    } | ||
|  |    if((x < 1) && (x + n < 0)) | ||
|  |    { | ||
|  |       T val = boost::math::tgamma_delta_ratio(1 - x, static_cast<T>(-n), pol); | ||
|  |       return (n & 1) ? T(-val) : val; | ||
|  |    } | ||
|  |    // | ||
|  |    // We don't optimise this for small n, because | ||
|  |    // tgamma_delta_ratio is alreay optimised for that | ||
|  |    // use case: | ||
|  |    // | ||
|  |    return 1 / boost::math::tgamma_delta_ratio(x, static_cast<T>(n), pol); | ||
|  | } | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | inline T falling_factorial_imp(T x, unsigned n, const Policy& pol) | ||
|  | { | ||
|  |    BOOST_STATIC_ASSERT(!boost::is_integral<T>::value); | ||
|  |    BOOST_MATH_STD_USING // ADL of std names | ||
|  |    if((x == 0) && (n >= 0)) | ||
|  |       return 0; | ||
|  |    if(x < 0) | ||
|  |    { | ||
|  |       // | ||
|  |       // For x < 0 we really have a rising factorial | ||
|  |       // modulo a possible change of sign: | ||
|  |       // | ||
|  |       return (n&1 ? -1 : 1) * rising_factorial(-x, n, pol); | ||
|  |    } | ||
|  |    if(n == 0) | ||
|  |       return 1; | ||
|  |    if(x < 0.5f) | ||
|  |    { | ||
|  |       // | ||
|  |       // 1 + x below will throw away digits, so split up calculation: | ||
|  |       // | ||
|  |       if(n > max_factorial<T>::value - 2) | ||
|  |       { | ||
|  |          // If the two end of the range are far apart we have a ratio of two very large | ||
|  |          // numbers, split the calculation up into two blocks: | ||
|  |          T t1 = x * boost::math::falling_factorial(x - 1, max_factorial<T>::value - 2); | ||
|  |          T t2 = boost::math::falling_factorial(x - max_factorial<T>::value + 1, n - max_factorial<T>::value + 1); | ||
|  |          if(tools::max_value<T>() / fabs(t1) < fabs(t2)) | ||
|  |             return boost::math::sign(t1) * boost::math::sign(t2) * policies::raise_overflow_error<T>("boost::math::falling_factorial<%1%>", 0, pol); | ||
|  |          return t1 * t2; | ||
|  |       } | ||
|  |       return x * boost::math::falling_factorial(x - 1, n - 1); | ||
|  |    } | ||
|  |    if(x <= n - 1) | ||
|  |    { | ||
|  |       // | ||
|  |       // x+1-n will be negative and tgamma_delta_ratio won't | ||
|  |       // handle it, split the product up into three parts: | ||
|  |       // | ||
|  |       T xp1 = x + 1; | ||
|  |       unsigned n2 = itrunc((T)floor(xp1), pol); | ||
|  |       if(n2 == xp1) | ||
|  |          return 0; | ||
|  |       T result = boost::math::tgamma_delta_ratio(xp1, -static_cast<T>(n2), pol); | ||
|  |       x -= n2; | ||
|  |       result *= x; | ||
|  |       ++n2; | ||
|  |       if(n2 < n) | ||
|  |          result *= falling_factorial(x - 1, n - n2, pol); | ||
|  |       return result; | ||
|  |    } | ||
|  |    // | ||
|  |    // Simple case: just the ratio of two | ||
|  |    // (positive argument) gamma functions. | ||
|  |    // Note that we don't optimise this for small n, | ||
|  |    // because tgamma_delta_ratio is alreay optimised | ||
|  |    // for that use case: | ||
|  |    // | ||
|  |    return boost::math::tgamma_delta_ratio(x + 1, -static_cast<T>(n), pol); | ||
|  | } | ||
|  | 
 | ||
|  | } // namespace detail | ||
|  | 
 | ||
|  | template <class RT> | ||
|  | inline typename tools::promote_args<RT>::type | ||
|  |    falling_factorial(RT x, unsigned n) | ||
|  | { | ||
|  |    typedef typename tools::promote_args<RT>::type result_type; | ||
|  |    return detail::falling_factorial_imp( | ||
|  |       static_cast<result_type>(x), n, policies::policy<>()); | ||
|  | } | ||
|  | 
 | ||
|  | template <class RT, class Policy> | ||
|  | inline typename tools::promote_args<RT>::type | ||
|  |    falling_factorial(RT x, unsigned n, const Policy& pol) | ||
|  | { | ||
|  |    typedef typename tools::promote_args<RT>::type result_type; | ||
|  |    return detail::falling_factorial_imp( | ||
|  |       static_cast<result_type>(x), n, pol); | ||
|  | } | ||
|  | 
 | ||
|  | template <class RT> | ||
|  | inline typename tools::promote_args<RT>::type | ||
|  |    rising_factorial(RT x, int n) | ||
|  | { | ||
|  |    typedef typename tools::promote_args<RT>::type result_type; | ||
|  |    return detail::rising_factorial_imp( | ||
|  |       static_cast<result_type>(x), n, policies::policy<>()); | ||
|  | } | ||
|  | 
 | ||
|  | template <class RT, class Policy> | ||
|  | inline typename tools::promote_args<RT>::type | ||
|  |    rising_factorial(RT x, int n, const Policy& pol) | ||
|  | { | ||
|  |    typedef typename tools::promote_args<RT>::type result_type; | ||
|  |    return detail::rising_factorial_imp( | ||
|  |       static_cast<result_type>(x), n, pol); | ||
|  | } | ||
|  | 
 | ||
|  | } // namespace math | ||
|  | } // namespace boost | ||
|  | 
 | ||
|  | #endif // BOOST_MATH_SP_FACTORIALS_HPP | ||
|  | 
 |