42concept LogGammaReal = native_math::NativeReal<R>
44 || std::same_as<R, mpreal>
64 requires(!has_runtime_precision_v<R>)
70 : precision_(
p), work_(this->working_precision(
p)), options_(options)
72 if (options.max_terms < 2)
throw std::invalid_argument(
"log-Gamma needs at least two coefficient slots");
73 std::size_t count = 16;
75 if constexpr (std::same_as<R, mpreal>)
77 auto const wanted =
static_cast<std::uint64_t
>(work_.
bit_count() / 6 + 8);
78 if (wanted > options.max_terms || wanted > (std::numeric_limits<std::size_t>::max() - 1) / 2)
79 throw std::invalid_argument(
"log-Gamma coefficient budget is too small for requested precision");
80 count =
static_cast<std::size_t
>(wanted);
81 coefficients_.reserve(count);
82 detail::exact_bernoulli_sequence sequence(2 * count);
83 for (std::size_t n = 0; n <= 2 * count; ++n)
85 auto b = sequence.next();
86 if (n >= 2 && n % 2 == 0)
87 coefficients_.push_back(this->number(b / (
exact_rational{n} * exact_rational{n - 1})));
93 count = std::min(count, options.max_terms);
94 auto table = detail::native_stirling_coefficients<R>();
95 coefficients_.assign(
table.begin(),
table.begin() + count);
98 log_target_ =
math::log(numeric_limits<R>::epsilon(work_) / this->number(32));
99 small_angle_ =
math::sqrt(numeric_limits<R>::epsilon(work_)) / this->number(2);
100 half_log_two_pi_ =
math::log(this->number(2) * (pi<R> * this->number(1))) / this->number(2);
110 accumulator
sum(this->number(0));
111 auto const one = this->number(1), half = one / this->number(2);
112 std::size_t shifts = 0;
113 R log_tail = this->log_remainder(z);
114 while (log_tail > log_target_)
116 this->check_shift(shifts, z);
117 sum.add(-this->logarithm(z));
120 log_tail = this->log_remainder(z);
122 auto const log_z = this->logarithm(z);
126 R
const leading_real =
math::fma(z.real(), log_z.real() - one, -z.imag() * log_z.imag());
127 R
const leading_imag = this->near_real(z) ? z.imag() * log_z.real()
128 :
math::fma(z.imag(), log_z.real() - one, z.real() * log_z.imag());
130 sum.add(-half * log_z);
132 complex_type inverse = this->reciprocal(z), power = inverse;
134 for (std::size_t k = 0; k + 1 < coefficients_.size(); ++k)
136 sum.add(coefficients_[k] * power);
150 R h = this->number(shift);
151 R
const zero = this->number(0), one = this->number(1), half = one / this->number(2);
152 if (!
uni20::isfinite(h))
throw std::invalid_argument(
"log-Gamma ratio requires finite shift");
154 if (!
uni20::isfinite(upper))
throw std::overflow_error(
"log-Gamma shifted argument exceeds scalar range");
155 if (!(upper.real() > zero))
throw std::invalid_argument(
"log-Gamma ratio requires Re(z+h)>0");
156 if (h == zero)
return {
complex_type(this->output(zero)), this->output(zero), this->output(zero), 0, 0};
161 bool const reverse = h < zero;
168 accumulator
sum(zero);
169 R logarithm_tail = zero;
170 std::size_t shifts = 0;
173 R
const two = this->number(2), eight = this->number(8);
174 R
const threshold = log_target_ -
math::log(two);
175 R tail0 = this->log_remainder(z), tail1 = zero;
180 if (tail0 <= threshold)
182 tail1 = this->log_remainder(upper);
183 if (tail1 <= threshold)
break;
185 this->check_shift(shifts, z);
186 sum.add(-this->log_increment(z, h, two, eight));
190 tail0 = this->log_remainder(z);
192 auto const delta = this->log_difference(z, h);
193 bool const near_real = this->near_real(z);
197 add_leading(h * this->logarithm(z));
198 add_leading((h - half) * delta.log1p);
199 add_leading(h * delta.relative_remainder);
200 logarithm_tail +=
math::abs(h - half) * delta.log_error +
math::abs(h) * delta.relative_error;
204 add_leading(h * (this->logarithm(upper) - one));
205 add_leading((z - half) * delta.log1p);
213 R
const product = this->product_quotient(z.imag(), h, z.real());
214 R
const phase = delta.small ?
product * (one + delta.relative_remainder.real()) : z.imag() * delta.log1p.real();
217 complex_type const inv0 = this->reciprocal(z), inv1 = this->reciprocal(upper);
218 complex_type const square0 = inv0 * inv0, square1 = inv1 * inv1;
220 complex_type const delta_square = delta_inv * (inv0 + inv1);
222 for (std::size_t k = 0; k + 1 < coefficients_.size(); ++k)
224 sum.add(coefficients_[k] * delta_power);
225 delta_power = delta_power * square1 + power * delta_square;
229 if (reverse) result.value = -result.value;
237 R scaled_mass, epsilon;
238 explicit accumulator(R zero)
239 :
sum(zero), correction(zero), scaled_mass(zero), epsilon(
numeric_limits<R>::epsilon(zero))
241 static R error(R
const& a, R
const& b, R
const& next)
253 scaled_mass +=
math::hypot(epsilon * term.real(), epsilon * term.imag());
257 struct logarithm_difference
260 R log_error, relative_error;
264 static Precision working_precision(Precision
p)
266 if (
p.is_exact())
throw std::invalid_argument(
"log-Gamma requires explicit finite precision");
267 require_precision<R>(
p);
268 if constexpr (has_runtime_precision_v<R>)
270 if (
p.bit_count() > std::numeric_limits<Precision::bit_count_type>::max() - 64)
271 throw std::overflow_error(
"log-Gamma guard precision exceeds supported range");
276 template <
class T> R number(T
const& x)
const {
return scalar_at_precision<R>(x, work_); }
277 template <
class T> T output(T
const& x)
const {
return scalar_at_precision<T>(x, precision_); }
278 void check_input_precision(Precision
p)
const
280 if (!
p.is_exact() &&
p != precision_)
throw std::invalid_argument(
"log-Gamma input precision mismatch");
285 if (!
uni20::isfinite(z) || !(z.real() > 0))
throw std::invalid_argument(
"log-Gamma requires finite Re(z)>0");
286 return scalar_at_precision<complex_type>(z, work_);
290 bool near_real(
complex_type const& z)
const {
return math::abs(z.imag()) <= z.real() * small_angle_; }
292 R product_quotient(R
const& a, R
const& b, R
const&
c)
const
295 auto const exponent =
static_cast<__int128_t
>(aa.exponent) + bb.exponent - cc.exponent;
298 auto const limited = std::clamp(exponent,
static_cast<__int128_t
>(std::numeric_limits<std::int64_t>::min()),
299 static_cast<__int128_t
>(std::numeric_limits<std::int64_t>::max()));
300 return math::ldexp((aa.fraction * bb.fraction) / cc.fraction,
static_cast<std::int64_t
>(limited));
304 R
const scale = this->scale(z), re = z.real() / scale, im = z.imag() / scale;
309 R
const scale = this->scale(z), re = z.real() / scale, im = z.imag() / scale;
310 R
const norm = re * re + im * im;
311 return {(re /
norm) / scale, (-im /
norm) / scale};
315 R
const scale = this->scale(z), re = z.real() / scale, im = z.imag() / scale;
316 R
const norm =
math::hypot(re, im), n = this->number(coefficients_.size());
317 R
const sec2 = this->number(2) / (this->number(1) + re /
norm);
319 return log_coefficient_ + n *
math::log(sec2) -
329 R
const scale = this->scale(z);
330 if (h > scale / eight)
return this->log_difference(z, h).log1p;
331 R
const re = z.real() / scale, im = z.imag() / scale, delta = h / scale;
332 R
const norm = re * re + im * im;
337 logarithm_difference log_difference(
complex_type const& z, R
const& h)
const
339 R
const zero = this->number(0), one = this->number(1), scale = this->scale(z);
340 if (
math::abs(h) <= scale / this->number(8))
347 R
const target = numeric_limits<R>::epsilon(work_) / this->number(32);
348 R denominator = this->number(2);
349 for (std::size_t k = 0; k < options_.
max_terms; ++k)
358 R
const error = (
math::abs(power) / (denominator + one)) * (this->number(8) / this->number(7));
363 throw std::runtime_error(
"log-Gamma ratio exhausted its logarithm term budget");
366 R
const upper_scale = this->scale(upper);
368 R
const lower_magnitude = lower_binary.fraction *
math::hypot(z.real() / scale, z.imag() / scale);
369 R
const upper_magnitude =
370 upper_binary.fraction *
math::hypot(upper.real() / upper_scale, upper.imag() / upper_scale);
373 R
const real =
math::log(upper_magnitude / lower_magnitude) +
374 this->number(upper_binary.exponent - lower_binary.exponent) *
math::log(this->number(2));
378 void check_shift(std::size_t shifts,
complex_type const& z)
const
380 if (shifts == options_.
max_shifts)
throw std::runtime_error(
"log-Gamma exhausted its recurrence budget");
381 R
const next = z.real() + this->number(1);
383 throw std::overflow_error(
"log-Gamma recurrence cannot advance at working precision");
385 log_gamma_result<R>
finish(accumulator
const&
sum, R
const& tail, std::size_t shifts)
const
387 auto const value = this->output(
sum.sum +
sum.correction);
388 R
const roundoff = this->number(64) * (numeric_limits<R>::epsilon(work_) +
sum.scaled_mass);
389 R
const output_epsilon = numeric_limits<R>::epsilon(precision_);
390 R
const rounded_error = this->output(roundoff) + output_epsilon +
393 throw std::overflow_error(
"log-Gamma value or diagnostic exceeds scalar range");
394 return {
value, this->output(tail), rounded_error, coefficients_.size() - 1, shifts};
397 Precision precision_, work_;
398 log_gamma_options options_;
399 std::vector<R> coefficients_;
400 R log_coefficient_, log_target_, small_angle_, half_log_two_pi_;
AsyncTask square(ReadBuffer< int > in, WriteBuffer< int > out)
Definition async_tbb_reduction_example.cpp:39
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
static constexpr Precision bits(bit_count_type count)
Select a positive binary significand size.
Definition core/precision.hpp:21
constexpr bit_count_type bit_count() const
Number of binary significand bits.
Definition core/precision.hpp:43
Owning arbitrary-size rational with exact, eager arithmetic.
Definition exact_rational.hpp:161
Immutable reusable right-half-plane log-Gamma and real-shift log-ratio context.
Definition complex_log_gamma.hpp:58
log_gamma_result< R > log_gamma_ratio_right(complex_type const &input, R const &shift) const
Evaluate log Gamma(z+h)-log Gamma(z), h real and both real parts positive.
Definition complex_log_gamma.hpp:146
log_gamma_context(log_gamma_options options={})
Construct at the fixed scalar's native precision.
Definition complex_log_gamma.hpp:63
Precision precision() const
Requested output precision.
Definition complex_log_gamma.hpp:104
complex< R > complex_type
Definition complex_log_gamma.hpp:60
log_gamma_context(Precision p, log_gamma_options options={})
Prepare coefficients at explicit finite precision p.
Definition complex_log_gamma.hpp:69
log_gamma_result< R > log_gamma_right(complex_type const &input) const
Evaluate analytic log Gamma(z) on finite Re(z)>0.
Definition complex_log_gamma.hpp:107
locale_t value
Definition data_table.cpp:49
typename detail::complex_type< Real >::type complex
Project-level complex scalar spelling.
Definition types.hpp:66
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Scalar math helper utilities.
T product(std::vector< T > const &values)
Definition mpreal_exact_example.cpp:13
streaming_table table(std::string title, stream destination)
Create a schema-first streaming display table.
Definition display.cpp:800
std::optional< DenseMatrix< W > > correction(Backend &backend, LuFactorization< F > const &factor, DenseMatrix< R > const &rhs, LuRefinementPrecision< F, W, R > p, std::vector< bool > const &active, std::int64_t target_exponent, SolveInfo &info)
Definition lu_refine.hpp:93
constexpr detail::scalar_math::exp_fn exp
Natural exponential with scalar-specific precision handling.
Definition math.hpp:465
constexpr detail::scalar_math::atan2_fn atan2
Quadrant-aware inverse tangent of y/x in radians.
Definition math.hpp:505
constexpr detail::scalar_math::log1p_fn log1p
Natural logarithm of one plus the argument, retaining small increments.
Definition math.hpp:483
constexpr detail::scalar_math::ldexp_fn ldexp
Multiply by a power of two using the scalar's exponent interface.
Definition math.hpp:511
constexpr detail::scalar_math::sqrt_fn sqrt
Square root; exact inputs requiring approximation need an explicit finite precision.
Definition math.hpp:461
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
constexpr detail::scalar_math::fma_fn fma
Fused multiplication and addition with one final rounding.
Definition math.hpp:555
constexpr detail::scalar_math::real_fn real
Read the real component by value; real and integral scalars retain their type.
Definition math.hpp:671
constexpr detail::scalar_math::frexp_fn frexp
Binary fraction and exponent as an owning result.
Definition math.hpp:535
constexpr detail::scalar_math::imag_fn imag
Read the imaginary component by value; real zero retains the input's type and precision.
Definition math.hpp:673
constexpr detail::scalar_math::hypot_fn hypot
Euclidean length with scalar-specific overflow and underflow handling.
Definition math.hpp:507
constexpr detail::scalar_math::log_fn log
Natural logarithm with scalar-specific precision handling.
Definition math.hpp:467
Definition data_table.cpp:8
Definition async/async.hpp:26
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
constexpr Precision precision_of(T const &value)
Read scalar precision without changing any scalar or provider state.
Definition runtime_precision.hpp:53
mpreal norm(mpcomplex const &z)
Definition mpcomplex.hpp:494
int add(int a, int b)
Definition dummy.cpp:6
mpreal remainder(mpreal const &a, mpreal const &b)
Nearest-even-quotient remainder; exact operands retain exact rational results.
Definition mpreal_functions.hpp:534
constexpr Precision native_precision()
Concrete precision of a fixed scalar family, including complex components.
Definition runtime_precision.hpp:41
Work limits for recurrence and Stirling/logarithm series evaluation.
Definition complex_log_gamma.hpp:19
std::size_t max_shifts
Definition complex_log_gamma.hpp:20
std::size_t max_terms
Definition complex_log_gamma.hpp:21
Analytic log-Gamma value, truncation bound and heuristic roundoff indicator.
Definition complex_log_gamma.hpp:31
R roundoff_indicator
Definition complex_log_gamma.hpp:34
std::size_t terms
Definition complex_log_gamma.hpp:35
R truncation_bound
Definition complex_log_gamma.hpp:33
std::size_t shifts
Definition complex_log_gamma.hpp:36
complex< R > value
Definition complex_log_gamma.hpp:32
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16