29 #ifndef PIRANHA_BINOMIAL_HPP 30 #define PIRANHA_BINOMIAL_HPP 32 #include <boost/math/constants/constants.hpp> 35 #include <type_traits> 38 #include <piranha/config.hpp> 40 #include <piranha/mp_integer.hpp> 41 #include <piranha/pow.hpp> 53 inline T compute_3_gamma(
const T &a,
const T &b,
const T &c)
56 piranha_assert(a >= T(0) || std::trunc(a) != a);
57 piranha_assert(b >= T(0) || std::trunc(b) != b);
58 piranha_assert(c >= T(0) || std::trunc(c) != c);
59 const T pi = boost::math::constants::pi<T>();
62 tmp0 -= std::lgamma(T(1) - a);
63 tmp1 *= pi / std::sin(a * pi);
65 tmp0 += std::lgamma(a);
68 tmp0 += std::lgamma(T(1) - b);
69 tmp1 *= std::sin(b * pi) / pi;
71 tmp0 -= std::lgamma(b);
74 tmp0 += std::lgamma(T(1) - c);
75 tmp1 *= std::sin(c * pi) / pi;
77 tmp0 -= std::lgamma(c);
79 return std::exp(tmp0) * tmp1;
84 inline T math_fp_binomial(
const T &x,
const T &y)
86 static_assert(std::is_floating_point<T>::value,
"Invalid type for fp_binomial.");
87 if (unlikely(!std::isfinite(x) || !std::isfinite(y))) {
89 "cannot compute binomial coefficient with non-finite floating-point argument(s)");
91 const bool neg_int_x = std::trunc(x + T(1)) == (x + T(1)) && (x + T(1)) <= T(0),
92 neg_int_y = std::trunc(y + T(1)) == (y + T(1)) && (y + T(1)) <= T(0),
93 neg_int_x_y = std::trunc(x - y + T(1)) == (x - y + T(1)) && (x - y + T(1)) <= T(0);
94 const unsigned mask = unsigned(neg_int_x) + (unsigned(neg_int_y) << 1u) + (
unsigned(neg_int_x_y) << 2u);
98 return compute_3_gamma(x + T(1), y + T(1), x - y + T(1));
111 const auto phase = std::pow(T(-1), x + T(1)) / std::pow(T(-1), y + T(1));
112 return compute_3_gamma(-y, -x, x - y + T(1)) * phase;
115 const auto phase = std::pow(T(-1), x - y + T(1)) / std::pow(T(-1), x + T(1));
116 return compute_3_gamma(-(x - y), y + T(1), -x) * phase;
125 template <
typename T,
typename U>
126 using binomial_fp_arith_enabler
127 = enable_if_t<conjunction<std::is_arithmetic<T>, std::is_arithmetic<U>,
128 disjunction<std::is_floating_point<T>, std::is_floating_point<U>>>::value>;
139 template <
typename T,
typename U,
typename =
void>
148 template <
typename T,
typename U>
171 return math_fp_binomial(static_cast<result_type>(x), static_cast<result_type>(y));
176 inline namespace impl
180 template <
typename T,
typename U>
181 using math_binomial_type_ = decltype(math::binomial_impl<T, U>{}(std::declval<const T &>(), std::declval<const U &>()));
183 template <
typename T,
typename U>
184 using math_binomial_type = enable_if_t<is_returnable<math_binomial_type_<T, U>>::value, math_binomial_type_<T, U>>;
214 template <
typename T,
typename U>
215 inline math_binomial_type<T, U>
binomial(
const T &x,
const U &y)
221 inline namespace impl
225 template <
typename T,
typename U>
226 using integer_binomial_enabler = integer_pow_enabler<T, U>;
229 template <
typename T,
typename U>
230 auto mp_integer_binomial_wrapper(
const T &base,
const U &exp) -> decltype(binomial(base, exp))
232 return binomial(base, exp);
254 template <
typename T,
typename U>
258 template <
typename T2,
typename U2,
259 enable_if_t<conjunction<std::is_integral<T2>, std::is_integral<U2>>::value,
int> = 0>
260 static integer impl(
const T2 &x,
const U2 &y)
262 return mp_integer_binomial_wrapper(
integer{x}, y);
265 template <
typename T2,
typename U2, enable_if_t<disjunction<conjunction<is_mp_
integer<T2>, std::is_
integral<U2>>,
266 conjunction<is_mp_
integer<U2>, std::is_
integral<T2>>,
267 is_same_mp_
integer<T2, U2>>::value,
269 static auto impl(
const T2 &x,
const U2 &y) -> decltype(mp_integer_binomial_wrapper(x, y))
271 return mp_integer_binomial_wrapper(x, y);
274 template <
typename T2,
typename U2,
275 enable_if_t<conjunction<std::is_floating_point<T2>, is_mp_integer<U2>>::value,
int> = 0>
276 static T2 impl(
const T2 &x,
const U2 &y)
281 template <
typename T2,
typename U2,
282 enable_if_t<conjunction<std::is_floating_point<U2>, is_mp_integer<T2>>::value,
int> = 0>
283 static U2 impl(
const T2 &x,
const U2 &y)
288 using ret_type = decltype(impl(std::declval<const T &>(), std::declval<const U &>()));
312 template <
typename T,
typename U>
315 template <
typename T2,
typename U2>
316 using binomial_t = decltype(
math::binomial(std::declval<const T2 &>(), std::declval<const U2 &>()));
317 static const bool implementation_defined = is_detected<binomial_t, T, U>::value;
321 static const bool value = implementation_defined;
325 template <
typename T,
typename U>
Multiprecision integer class.
result_type operator()(const T &x, const U &y) const
Call operator.
Default functor for the implementation of piranha::math::binomial().
static const bool value
Value of the type trait.
Type trait to detect the presence of the piranha::math::binomial() function.
#define piranha_throw(exception_type,...)
Exception-throwing macro.
math_binomial_type< T, U > binomial(const T &x, const U &y)
Generalised binomial coefficient.
typename std::common_type< T, U >::type result_type
Result type for the call operator.
ret_type operator()(const T &x, const U &y) const
Call operator.