Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
mpreal_special.hpp
Go to the documentation of this file.
1#pragma once
2
5#include "math_results.hpp"
6#include "mpreal.hpp"
7#include <vector>
8
9namespace uni20
10{
11namespace detail
12{
13inline bool rational_integer(exact_rational const& x) { return mpz_cmp_ui(mpq_denref(x.native_handle()), 1) == 0; }
14inline unsigned long exact_nonnegative_order(exact_rational const& x)
15{
16 auto q = x.native_handle();
17 if (!rational_integer(x) || mpq_sgn(q) < 0)
18 throw std::domain_error("special function requires a nonnegative integer order");
19 if (!mpz_fits_slong_p(mpq_numref(q))) throw std::out_of_range("special function order does not fit int64_t");
20 return mpz_get_ui(mpq_numref(q));
21}
22inline exact_rational exact_factorial(unsigned long n)
23{
24 return mpreal_access::rational_result([&](mpq_ptr out) { mpz_fac_ui(mpq_numref(out), n); });
25}
26inline mpreal exact_gamma(exact_rational const& x)
27{
28 if (x > 0 && rational_integer(x)) return mpreal(exact_factorial(exact_nonnegative_order(x - exact_rational{1})));
29 throw std::logic_error("Gamma requires finite precision unless its input is a positive integer");
30}
31inline mpreal exact_zeta(exact_rational const& x)
32{
33 if (x == 0) return mpreal(exact_rational{-1} / exact_rational{2});
34 if (x < 0 && rational_integer(x))
35 {
36 if (mpz_even_p(mpq_numref(x.native_handle()))) return mpreal{};
37 // Zeta(-m) = -B_(m+1)/(m+1) for odd positive m. This exact
38 // Bernoulli recurrence has no floating-point approximation or ambient precision.
39 auto order = exact_nonnegative_order(-x) + 1;
40 return mpreal(-exact_bernoulli(order) / exact_rational{order});
41 }
42 throw std::logic_error("Zeta requires finite precision except at nonpositive integers");
43}
44
45template <auto Operation> mpreal special_unary(mpreal const& x)
46{
47 auto p = x.precision();
48 if (p.is_exact())
49 {
50 auto const& q = x.exact_value();
51 if constexpr (same_function_v<Operation, mpfr_gamma>)
52 return exact_gamma(q);
53 else if constexpr (same_function_v<Operation, mpfr_zeta>)
54 return exact_zeta(q);
55 else if constexpr (same_function_v<Operation, mpfr_erf> || same_function_v<Operation, mpfr_li2>)
56 {
57 if (q == 0) return mpreal{};
58 }
59 else if constexpr (same_function_v<Operation, mpfr_erfc>)
60 {
61 if (q == 0) return mpreal{1};
62 }
63 throw std::logic_error("special function requires finite working precision");
64 }
65 return mpreal_access::finite_result(p, [&](mpfr_ptr out) { Operation(out, x.native_handle(), MPFR_RNDN); });
66}
67template <auto Operation> mpreal special_unary(mpreal const& x, Precision p)
68{
69 (void)p.bit_count();
70 return special_unary<Operation>(x.at(p));
71}
72
73inline mpreal exact_beta(exact_rational const& a, exact_rational const& b)
74{
75 if (a > 0 && b > 0 && (rational_integer(a) || rational_integer(b)))
76 {
77 // If both are integers, use the shorter recurrence. In particular B(a,1)
78 // needs no large-order conversion even when a is an arbitrary-size integer.
79 bool const use_a = rational_integer(a) && (!rational_integer(b) || a <= b);
80 auto n = exact_nonnegative_order(use_a ? a : b);
81 auto x = use_a ? b : a;
82 // B(x,n) = (n-1)! / (x (x+1) ... (x+n-1)). The recurrence
83 // avoids computing three enormous factorials for small rational arguments.
84 exact_rational result = exact_rational{1} / x;
85 for (unsigned long k = 1; k < n; ++k)
86 result = result * exact_rational{k} / (x + exact_rational{k});
87 return mpreal(result);
88 }
89 throw std::logic_error("Beta requires finite precision except for positive rational/integer pairs");
90}
91template <auto Operation> mpreal special_binary(mpreal const& a, mpreal const& b)
92{
93 auto p = common_precision(a.precision(), b.precision());
94 if (p.is_exact())
95 {
96 if constexpr (same_function_v<Operation, mpfr_beta>)
97 return exact_beta(a.exact_value(), b.exact_value());
98 else if constexpr (same_function_v<Operation, mpfr_gamma_inc>)
99 {
100 if (b == 0) return exact_gamma(a.exact_value());
101 }
102 else if constexpr (same_function_v<Operation, mpfr_agm>)
103 {
104 if (a >= 0 && b >= 0)
105 {
106 if (a == b) return a;
107 if (a == 0 || b == 0) return mpreal{};
108 }
109 }
110 throw std::logic_error("special function requires finite working precision");
111 }
112 auto av = a.at(p), bv = b.at(p);
113 return mpreal_access::finite_result(
114 p, [&](mpfr_ptr out) { Operation(out, av.native_handle(), bv.native_handle(), MPFR_RNDN); });
115}
116template <auto Operation> mpreal special_binary(mpreal const& a, mpreal const& b, Precision p)
117{
118 (void)p.bit_count();
119 return special_binary<Operation>(a.at(p), b.at(p));
120}
121} // namespace detail
122
124inline mpreal tgamma(mpreal const& x) { return detail::special_unary<mpfr_gamma>(x); }
126inline mpreal tgamma(mpreal const& x, Precision p) { return detail::special_unary<mpfr_gamma>(x, p); }
127
129inline mpreal digamma(mpreal const& x) { return detail::special_unary<mpfr_digamma>(x); }
131inline mpreal digamma(mpreal const& x, Precision p) { return detail::special_unary<mpfr_digamma>(x, p); }
132
134inline mpreal erf(mpreal const& x) { return detail::special_unary<mpfr_erf>(x); }
136inline mpreal erf(mpreal const& x, Precision p) { return detail::special_unary<mpfr_erf>(x, p); }
137
139inline mpreal erfc(mpreal const& x) { return detail::special_unary<mpfr_erfc>(x); }
141inline mpreal erfc(mpreal const& x, Precision p) { return detail::special_unary<mpfr_erfc>(x, p); }
142
144inline mpreal zeta(mpreal const& x) { return detail::special_unary<mpfr_zeta>(x); }
146inline mpreal zeta(mpreal const& x, Precision p) { return detail::special_unary<mpfr_zeta>(x, p); }
147
149inline mpreal expint(mpreal const& x) { return detail::special_unary<mpfr_eint>(x); }
151inline mpreal expint(mpreal const& x, Precision p) { return detail::special_unary<mpfr_eint>(x, p); }
152
154inline mpreal dilog_real(mpreal const& x) { return detail::special_unary<mpfr_li2>(x); }
156inline mpreal dilog_real(mpreal const& x, Precision p) { return detail::special_unary<mpfr_li2>(x, p); }
157
159inline mpreal airy_ai(mpreal const& x) { return detail::special_unary<mpfr_ai>(x); }
161inline mpreal airy_ai(mpreal const& x, Precision p) { return detail::special_unary<mpfr_ai>(x, p); }
162
164inline mpreal beta(mpreal const& a, mpreal const& x) { return detail::special_binary<mpfr_beta>(a, x); }
166inline mpreal beta(mpreal const& a, mpreal const& x, Precision p) { return detail::special_binary<mpfr_beta>(a, x, p); }
167
169inline mpreal upper_gamma(mpreal const& a, mpreal const& x) { return detail::special_binary<mpfr_gamma_inc>(a, x); }
171inline mpreal upper_gamma(mpreal const& a, mpreal const& x, Precision p)
172{
173 return detail::special_binary<mpfr_gamma_inc>(a, x, p);
174}
175
177inline mpreal agm(mpreal const& a, mpreal const& x) { return detail::special_binary<mpfr_agm>(a, x); }
179inline mpreal agm(mpreal const& a, mpreal const& x, Precision p) { return detail::special_binary<mpfr_agm>(a, x, p); }
180
185{
186 auto p = x.precision();
187 if (p.is_exact())
188 {
189 if (x == 1 || x == 2) return {mpreal{}, 1};
190 throw std::logic_error("log-Gamma requires finite precision except at one and two");
191 }
192 int sign = 0;
193 auto value = detail::mpreal_access::finite_result(
194 p, [&](mpfr_ptr out) { mpfr_lgamma(out, &sign, x.native_handle(), MPFR_RNDN); });
195 if (isnan(x) || (x < 0 && (isinf(x) || mpfr_integer_p(x.native_handle())))) sign = 0;
196 return {std::move(value), sign};
197}
200{
201 (void)p.bit_count();
202 return lgamma_sign(x.at(p));
203}
205inline mpreal lgamma(mpreal const& x) { return lgamma_sign(x).value; }
207inline mpreal lgamma(mpreal const& x, Precision p) { return lgamma_sign(x, p).value; }
208
209namespace detail
210{
211template <auto Operation> mpreal integer_bessel(mpreal const& x, std::int64_t order)
212{
213 auto p = x.precision();
214 if (p.is_exact())
215 {
216 if constexpr (same_function_v<Operation, mpfr_jn>)
217 if (x == 0) return mpreal{order == 0 ? 1 : 0};
218 throw std::logic_error("Bessel function requires finite working precision");
219 }
220 static_assert(sizeof(long) >= sizeof(std::int64_t));
221 return mpreal_access::finite_result(
222 p, [&](mpfr_ptr out) { Operation(out, static_cast<long>(order), x.native_handle(), MPFR_RNDN); });
223}
224} // namespace detail
225
227template <detail::MathInteger I> inline mpreal bessel_j(I n, mpreal const& x)
228{
229 return detail::integer_bessel<mpfr_jn>(x, detail::math_integer(n));
230}
232template <detail::MathInteger I> inline mpreal bessel_j(I n, mpreal const& x, Precision p)
233{
234 (void)p.bit_count();
235 return bessel_j(n, x.at(p));
236}
237
239template <detail::MathInteger I> inline mpreal bessel_y(I n, mpreal const& x)
240{
241 return detail::integer_bessel<mpfr_yn>(x, detail::math_integer(n));
242}
244template <detail::MathInteger I> inline mpreal bessel_y(I n, mpreal const& x, Precision p)
245{
246 (void)p.bit_count();
247 return bessel_y(n, x.at(p));
248}
249
253template <detail::MathInteger I> inline mpreal factorial(I n, Precision p)
254{
255 auto order = detail::math_integer(n);
256 if (order < 0) throw std::domain_error("factorial requires a nonnegative order");
257 if (p.is_exact()) return mpreal(detail::exact_factorial(static_cast<unsigned long>(order)));
258 return detail::mpreal_access::finite_result(
259 p, [&](mpfr_ptr out) { mpfr_fac_ui(out, static_cast<unsigned long>(order), MPFR_RNDN); });
260}
261} // namespace uni20
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
Owning exact rational or finite-precision MPFR real, with eager arithmetic.
Definition mpreal.hpp:74
mpreal at(Precision p) const
Definition mpreal.hpp:206
mpfr_srcptr native_handle() const
Borrow an approximate MPFR value; exact and unset states require explicit materialization first.
Definition mpreal.hpp:184
Precision precision() const
Return exact state or the working precision stored by MPFR.
Definition mpreal.hpp:178
locale_t value
Definition data_table.cpp:49
Definition data_table.cpp:8
Definition async/async.hpp:26
bool isnan(mpcomplex const &z)
Definition mpcomplex.hpp:499
mpreal expint(mpreal const &x)
Real exponential integral Ei, with its real principal-value meaning.
Definition mpreal_special.hpp:149
mpreal tgamma(mpreal const &x)
Real Gamma function; exact positive integers produce exact factorials.
Definition mpreal_special.hpp:124
mpreal erf(mpreal const &x)
Error function, retaining exact zero or the input precision.
Definition mpreal_special.hpp:134
mpreal dilog_real(mpreal const &x)
Real part of the dilogarithm; this is not a complex branch-valued API.
Definition mpreal_special.hpp:154
mpreal airy_ai(mpreal const &x)
Real Airy Ai; MPFR recommends arguments with magnitude below about 500.
Definition mpreal_special.hpp:159
R factorial(I n)
Factorial of a checked nonnegative integer, with an explicit native result type.
Definition factorial.hpp:15
bool isinf(mpcomplex const &z)
Definition mpcomplex.hpp:500
mpreal bessel_j(I n, mpreal const &x)
Bessel function of the first kind, with a checked signed integer order.
Definition mpreal_special.hpp:227
lgamma_result< mpreal > lgamma_sign(mpreal const &x)
Log(abs(Gamma(x))) and the sign of Gamma, as owning values.
Definition mpreal_special.hpp:184
mpreal upper_gamma(mpreal const &a, mpreal const &x)
Upper incomplete Gamma, with shape a and lower integration endpoint x.
Definition mpreal_special.hpp:169
constexpr Precision common_precision(Precision a, Precision b)
Combine precisions, treating exact values as neutral and rejecting unequal finite precisions.
Definition core/precision.hpp:57
mpreal zeta(mpreal const &x)
Riemann zeta; nonpositive integer arguments retain exact rational results.
Definition mpreal_special.hpp:144
mpreal erfc(mpreal const &x)
Complementary error function, evaluated without subtracting erf from one.
Definition mpreal_special.hpp:139
mpreal agm(mpreal const &a, mpreal const &x)
Nonnegative real arithmetic-geometric mean.
Definition mpreal_special.hpp:177
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164
mpreal digamma(mpreal const &x)
Logarithmic derivative of Gamma; finite working precision is required.
Definition mpreal_special.hpp:129
mpreal bessel_y(I n, mpreal const &x)
Bessel function of the second kind, with a checked signed integer order.
Definition mpreal_special.hpp:239
mpreal lgamma(mpreal const &x)
Natural logarithm of abs(Gamma(x)), including negative Gamma values.
Definition mpreal_special.hpp:205
Logarithm of the absolute Gamma value and Gamma's sign.
Definition math_results.hpp:43