// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // dawson.hpp // Dawson function // // Provided functions: // dawson(x) — Dawson function F(x) = exp(-x²) ∫₀ˣ exp(t²) dt // // Supported types: float, double, long double #ifndef SANGI_SPECIAL_DAWSON_HPP #define SANGI_SPECIAL_DAWSON_HPP #include #include #include namespace sangi { namespace special { // ================================================================ // Dawson function F(x) = exp(-x²) ∫₀ˣ exp(t²) dt // Algorithm of Rybicki (1989) // ================================================================ template [[nodiscard]] T dawson(T x) { if (std::isnan(x)) return std::numeric_limits::quiet_NaN(); if (x == T(0)) return T(0); // Odd function: F(-x) = -F(x) T sign = T(1); T ax = x; if (x < T(0)) { sign = T(-1); ax = -x; } T result; if (ax < T(0.2)) { // Taylor series: F(x) = x - 2x³/3 + 4x⁵/15 - 8x⁷/105 + ... // F(x) = Σ_{n=0}^∞ (-1)^n · 2^n · x^{2n+1} / (1·3·5···(2n+1)) T x2 = ax * ax; T term = ax; T sum = term; for (int n = 1; n < 50; ++n) { term *= -T(2) * x2 / T(2 * n + 1); sum += term; if (std::abs(term) < std::numeric_limits::epsilon() * std::abs(sum)) break; } result = sum; } else if (ax > T(5.0)) { // Asymptotic expansion: F(x) ≈ 1/(2x) + 1/(4x³) + 3/(8x⁵) + ... // F(x) = (1/(2x)) Σ_{n=0}^∞ (2n-1)!! / (2x²)^n T x2 = ax * ax; T inv_2x2 = T(1) / (T(2) * x2); T term = T(1); T sum = T(1); for (int n = 1; n < 30; ++n) { term *= T(2 * n - 1) * inv_2x2; sum += term; if (std::abs(term) < std::numeric_limits::epsilon() * std::abs(sum)) break; } result = sum / (T(2) * ax); } else { // Simpson's rule: F(x) = exp(-x²) ∫₀ˣ exp(t²) dt int N = static_cast(ax * T(100)) + 100; if (N % 2 != 0) ++N; T dx = ax / T(N); T sum = T(0); T x2 = ax * ax; for (int i = 0; i <= N; ++i) { T t = dx * T(i); T w = (i == 0 || i == N) ? T(1) : (i % 2 == 0 ? T(2) : T(4)); sum += w * std::exp(t * t - x2); } result = sum * dx / T(3); } return sign * result; } } // namespace special } // namespace sangi #endif // SANGI_SPECIAL_DAWSON_HPP