131131
132132function _expint (z:: Complex{Float64} , :: Val{expscaled} = Val {false} ()) where {expscaled}
133133 if real (z) < 0
134- return _expint (1 , z, 1000 , Val {expscaled} ())
134+ return _expint (one ( real (z)) , z, 1000 , Val {expscaled} ())
135135 else
136136 return expint_opt (z, Val {expscaled} ())
137137 end
160160
161161# Continued fraction for En(ν, z) that doesn't use a term with
162162# the gamma function: https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/10/0001/
163- function En_cf_nogamma (ν:: Number , z:: Number , n:: Int = 1000 )
164- B = float ( z + ν)
163+ function En_cf_nogamma (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} , n:: Int = 1000 ) where {T <: Union{Float16,Float32,Float64} }
164+ B = z + ν
165165 Bprev:: typeof (B) = z
166166 A:: typeof (B) = 1
167167 Aprev:: typeof (B) = 1
@@ -196,18 +196,17 @@ function En_cf_nogamma(ν::Number, z::Number, n::Int=1000)
196196end
197197
198198# Calculate Γ(1 - ν) * z^(ν-1) safely
199- function En_safe_gamma_term (ν:: Number , z:: Number )
200- ν1 = 1 - oftype (z, ν)
199+ function En_safe_gamma_term (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} ) where {T <: Union{Float16,Float32,Float64} }
200+ ν1 = 1 - ν
201201 lgamma, lgammasign = ν1 isa Real ? logabsgamma (ν1) : (loggamma (ν1), 1 )
202202 return lgammasign * exp ((ν - 1 )* log (z) + lgamma)
203203end
204- En_safe_gamma_term (ν:: Integer , z:: Real ) = (z ≥ 0 || isodd (ν) ? 1 : - 1 ) * exp ((ν - 1 )* log (abs (z)) + loggamma (1 - oftype (z, ν)))
205204
206205# continued fraction for En(ν, z) that uses the gamma function:
207206# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/10/0005/
208207# returns the two terms from the above equation separately
209- function En_cf_gamma (ν:: Number , z:: Number , n:: Int = 1000 )
210- A, z = map (float, promote ( 1 - ν, z) )
208+ function En_cf_gamma (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} , n:: Int = 1000 ) where {T <: Union{Float16,Float32,Float64} }
209+ A = 1 - ν + zero (z )
211210 B = oneunit (A)
212211 Bprev = zero (B)
213212 Aprev = oneunit (A)
243242# picks between continued fraction representations in
244243# En_cf_nogamma and En_cf_gamma
245244# returns (evaluated result, # iterations used, whether En_cf_gamma was chosen)
246- function En_cf (ν:: Number , z:: Number , niter:: Int = 1000 )
245+ function En_cf (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} , niter:: Int = 1000 ) where {T <: Union{Float16,Float32,Float64} }
247246 if real (1 - ν) > 0
248247 gammapart, cfpart, iters = En_cf_gamma (ν, z, niter)
249248 gammaabs, cfabs = abs (gammapart), abs (cfpart)
257256
258257# Compute expint(ν, z₀+Δ) given start = expint(ν, z₀), as described by [Amos 1980].
259258# This is used to incrementally approach the negative real axis.
260- function En_taylor (ν:: Number , start:: Number , z₀:: Number , Δ:: Number )
259+ function En_taylor (ν:: Union{T,Complex{T}} , start:: Union{T,Complex{T}} , z₀:: Union{T,Complex{T}} ,
260+ Δ:: Union{T,Complex{T}} ) where {T<: Union{Float16,Float32,Float64} }
261261 a = exp (z₀) * start
262262 k, iters = 0 , 0
263263 asum = a
283283
284284# series about origin, general ν
285285# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/01/04/01/01/0003/
286- function En_expand_origin_general (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} , niter:: Int ) where {T<: AbstractFloat }
286+ function En_expand_origin_general (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} , niter:: Int ) where {T<: Union{Float16,Float32,Float64} }
287287 # gammaterm = En_safe_gamma_term(ν, z)
288288 gammaterm = gamma (1 - ν)* z^ (ν- 1 )
289289 frac = one (z)
313313# is O(δ^5) while the cancelling form loses accuracy like eps/δ; the two meet at
314314# δ ~ eps(T)^(1/6), which is where the switch is made.
315315#
316- # There is no method for complex `BigFloat`, and none for any other `AbstractFloat`:
317- # `polygamma` is not defined for them, and five terms could not serve that precision
318- # in any case.
319- #
320- # Real `BigFloat` is delegated to MPFR instead, which computes
321- # E_ν(z) = z^(ν-1) Γ(1-ν, z) without the cancellation. `mpfr_gamma_inc` needs z > 0
322- # and its cost grows linearly in ν, so the series is kept where it is adequate anyway,
323- # i.e. when no term fell into `blowup` and there was no cancellation to begin with.
324- function En_origin_pole_series (ν:: BigFloat , z:: BigFloat , gammaterm, blowup, sumterm)
325- if ! iszero (blowup) && z > 0 && ν < 1000
326- return z^ (ν- 1 ) * gamma (1 - ν, z)
327- end
328- return gammaterm - (blowup + sumterm)
329- end
330-
316+ # There is no method for any other type: `polygamma` is not defined beyond these
317+ # precisions, and five terms could not serve a wider one in any case. `BigFloat` is
318+ # handled by `expint(::BigFloat, ::BigFloat)`, which delegates to MPFR.
331319function En_origin_pole_series (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} ,
332320 gammaterm, blowup, sumterm) where {T<: Union{Float16,Float32,Float64} }
333321 m = round (ν)
@@ -345,14 +333,12 @@ function En_origin_pole_series(ν::Union{T,Complex{T}}, z::Union{T,Complex{T}},
345333 # expressions for higher order terms found using:
346334 # https://gist.github.com/augustt198/348e8f9ba33c0248f1548309c47c6d0e
347335 ψ₀, ψ₁, ψ₂, ψ₃, ψ₄ = polygamma .((0 ,1 ,2 ,3 ,4 ), n+ 1 )
348- series2 = ψ₀ + (3 * ψ₀^ 2 + π^ 2 - 3 * ψ₁)* δ/ 6 + (ψ₀^ 3 + (π^ 2 - 3 ψ₁)* ψ₀ + ψ₂)δ^ 2 / 6
349- series2 += (7 π^ 4 + 15 * (ψ₀^ 4 + 2 ψ₀^ 2 * (π^ 2 - 3 ψ₁) + ψ₁* (- 2 π^ 2 + 3 ψ₁) + 4 ψ₀* ψ₂) - 15 ψ₃)* δ^ 3 / 360
350- series2 += (3 ψ₀^ 5 + ψ₀^ 3 * (10 π^ 2 - 30 ψ₁) + 30 ψ₀^ 2 * ψ₂ + ψ₀* (45 ψ₁^ 2 - 30 π^ 2 * ψ₁ - 15 ψ₃ + 7 π^ 4 ) - 30 ψ₁* ψ₂ + 10 π^ 2 * ψ₂ + 3 ψ₄)* δ^ 4 / 360
336+ π², π⁴ = T (π)^ 2 , T (π)^ 4
337+ series2 = ψ₀ + (3 * ψ₀^ 2 + π² - 3 * ψ₁)* δ/ 6 + (ψ₀^ 3 + (π² - 3 ψ₁)* ψ₀ + ψ₂)δ^ 2 / 6
338+ series2 += (7 π⁴ + 15 * (ψ₀^ 4 + 2 ψ₀^ 2 * (π² - 3 ψ₁) + ψ₁* (- 2 π² + 3 ψ₁) + 4 ψ₀* ψ₂) - 15 ψ₃)* δ^ 3 / 360
339+ series2 += (3 ψ₀^ 5 + ψ₀^ 3 * (10 π² - 30 ψ₁) + 30 ψ₀^ 2 * ψ₂ + ψ₀* (45 ψ₁^ 2 - 30 π²* ψ₁ - 15 ψ₃ + 7 π⁴) - 30 ψ₁* ψ₂ + 10 π²* ψ₂ + 3 ψ₄)* δ^ 4 / 360
351340
352- # `π^2` and `π^4` above are `Float64`, so the series is evaluated in at least that
353- # precision and narrowed here
354- res = (series1 + series2) * En_safe_expfact (n, z) * z^ (ν- n- 1 ) - sumterm
355- return oftype (gammaterm, res)
341+ return (series1 + series2) * En_safe_expfact (n, z) * z^ (ν- n- 1 ) - sumterm
356342end
357343
358344# Compute (-z)^n / n!, avoiding overflow if possible.
@@ -367,13 +353,13 @@ end
367353# takes over: the term is then negligible next to the sums the callers add it to, and
368354# orders too large to loop over have to be handled as well (`expint(1e15, 2.5)` is
369355# tested, and `Int` is 32 bits on some platforms).
370- function En_safe_expfact (n:: T , z:: T ) where {T<: AbstractFloat }
356+ function En_safe_expfact (n:: T , z:: T ) where {T<: Union{Float16,Float32,Float64} }
371357 n < 12 && return _En_powerterm (n, z)
372358 sgn = z <= 0 ? one (T) : (isodd (n) ? - one (T) : one (T))
373359 return sgn * exp (n* log (abs (z)) - loggamma (n + one (T)))
374360end
375361
376- function En_safe_expfact (n:: T , z:: Complex{T} ) where {T<: AbstractFloat }
362+ function En_safe_expfact (n:: T , z:: Complex{T} ) where {T<: Union{Float16,Float32,Float64} }
377363 n < 12 && return _En_powerterm (n, z)
378364 return exp (n* log (- z) - loggamma (n + one (T)))
379365end
@@ -389,10 +375,10 @@ end
389375
390376# series about the origin, special case for integer n > 0
391377# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/01/04/01/02/0005/
392- function En_expand_origin_posint (n, z:: Number , niter:: Integer )
378+ function En_expand_origin_posint (n:: T , z:: Union{T,Complex{T}} , niter:: Int ) where {T <: Union{Float16,Float32,Float64} }
393379 frac = one (real (z))
394- gammaterm = En_safe_expfact (oftype (frac, n- 1 ) , z) # (-z)^(n-1) / (n-1)!
395- gammaterm *= digamma (oftype (frac,n) ) - log (z)
380+ gammaterm = En_safe_expfact (n- 1 , z) # (-z)^(n-1) / (n-1)!
381+ gammaterm *= digamma (n ) - log (z)
396382 sumterm = n == 1 ? zero (frac) : frac / (1 - n)
397383 k = 1
398384 ϵ = 10 * eps (real (sumterm))
@@ -411,41 +397,45 @@ function En_expand_origin_posint(n, z::Number, niter::Integer)
411397 return gammaterm - sumterm
412398end
413399
414- function En_expand_origin (ν:: Number , z:: Number , niter:: Int )
400+ function En_expand_origin (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} , niter:: Int ) where {T <: Union{Float16,Float32,Float64} }
415401 if isinteger (ν) && real (ν) > 0
416- return real (ν) < ( typemax (Int) >> 2 ) ? En_expand_origin_posint ( Int ( real (ν)), z, niter) : En_expand_origin_posint (real (ν), z, niter)
402+ return En_expand_origin_posint (real (ν), z, niter)
417403 else
418- # `En_expand_origin_general` takes both arguments at the same precision
419- T = typeof (one (real (z)))
420- return En_expand_origin_general (ν isa Real ? convert (T, ν) : convert (Complex{T}, ν), z, niter)
404+ return En_expand_origin_general (ν, z, niter)
421405 end
422406end
423407
424408# can find imaginary part of E_ν(x) for x on negative real axis analytically
425409# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/04/05/01/0003/
426- function En_imagbranchcut (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} ) where {T<: AbstractFloat }
410+ function En_imagbranchcut (ν:: T , z:: Union{T,Complex{T}} ) where {T<: Union{Float16,Float32,Float64} }
411+ a = real (z)
412+ s, c = sincospi (ν)
413+ lgamma, lgammasign = logabsgamma (ν)
414+ return - 2 * lgammasign * Complex (c, - s) * π * exp ((ν- 1 )* log (complex (a)) - lgamma) * im
415+ end
416+ function En_imagbranchcut (ν:: Complex{T} , z:: Union{T,Complex{T}} ) where {T<: Union{Float16,Float32,Float64} }
427417 a = real (z)
428418 e1 = exp (π * imag (ν))
429- e2 = Complex (cospi (real (ν)), - sinpi (real (ν)))
430- lgamma, lgammasign = ν isa Real ? logabsgamma (ν) : (loggamma (ν), 1 )
431- return - 2 * lgammasign * e1 * π * e2 * exp ((ν- 1 )* log (complex (a)) - lgamma) * im
419+ s, c = sincospi (real (ν))
420+ return - 2 * e1 * Complex (c, - s) * π * exp ((ν- 1 )* log (complex (a)) - loggamma (ν)) * im
432421end
433422
434- function En_safeexpmult (z, a)
423+ # compute exp(z) * a, avoiding overflow of the exponential by shifting into the exponent
424+ function En_safeexpmult (z:: Union{T,Complex{T}} , a:: T ) where {T<: Union{Float16,Float32,Float64} }
435425 zexp = exp (z)
436- if isinf (zexp) || iszero (zexp)
437- return a isa Real ? sign (a) * exp (z + log ( abs (a))) : exp (z + log (a))
438- else
439- return zexp * a
440- end
426+ return isinf (zexp) || iszero (zexp) ? sign (a) * exp (z + log ( abs (a))) : zexp * a
427+ end
428+ function En_safeexpmult (z :: Union{T,Complex{T}} , a :: Complex{T} ) where {T <: Union{Float16,Float32,Float64} }
429+ zexp = exp (z)
430+ return isinf (zexp) || iszero (zexp) ? exp (z + log (a)) : zexp * a
441431end
442432
443- function _expint (ν:: Number , z:: Number , niter:: Int = 1000 , :: Val{expscaled} = Val {false} ()) where {expscaled}
433+ function _expint (ν:: Union{T,Complex{T}} , z:: Union{T,Complex{T}} , niter:: Int = 1000 ,
434+ :: Val{expscaled} = Val {false} ()) where {T<: Union{Float16,Float32,Float64} , expscaled}
444435 if abs (ν) > 50 && ! (isreal (ν) && real (ν) > 0 )
445436 throw (ArgumentError (" Unsupported order |ν| > 50 off the positive real axis" ))
446437 end
447438
448- z, = promote (float (z), ν)
449439 if isnan (ν) || isnan (z)
450440 return oftype (z, NaN ) * z
451441 end
@@ -535,9 +525,7 @@ function _expint(ν::Number, z::Number, niter::Int=1000, ::Val{expscaled}=Val{fa
535525
536526 # handle branch cut
537527 if imz == 0
538- # `En_imagbranchcut` takes both arguments at the same precision
539- νc = ν isa Real ? convert (real (typeof (z)), ν) : convert (complex (real (typeof (z))), ν)
540- bc = En_imagbranchcut (νc, z)
528+ bc = En_imagbranchcut (ν, z)
541529 bit = ! signbit (imag (z))
542530 sign = bit ? 1 : - 1
543531 if isreal (ν)
@@ -565,7 +553,14 @@ External links:
565553[DLMF 8.19](https://dlmf.nist.gov/8.19),
566554[Wikipedia](https://en.wikipedia.org/wiki/Exponential_integral)
567555"""
568- expint (ν:: Number , z:: Number , niter:: Int = 1000 ) = _expint (ν, z, niter, Val {false} ())
556+ expint (ν:: Number , z:: Number , niter:: Int = 1000 ) =
557+ _expint (promotereal (ν, float (z))... , niter, Val {false} ())
558+
559+ # For real `BigFloat` arguments MPFR's incomplete gamma gives E_ν(z) = z^(ν-1) Γ(1-ν, z)
560+ # directly, and unlike the series about the origin it does not cancel for `ν` near a
561+ # positive integer. It throws for z < 0, where Γ(1-ν, z) is generally complex. There is
562+ # no scaled counterpart in MPFR, so `expintx` has no `BigFloat` method.
563+ _expint (ν:: BigFloat , z:: BigFloat , niter:: Int , :: Val{false} ) = z^ (ν- 1 ) * gamma (1 - ν, z)
569564
570565
571566@doc raw """
@@ -581,7 +576,8 @@ If ``\nu`` is not specified, ``\nu = 1`` is used. Arbitrary complex
581576
582577See also: [`expint(ν, z)`](@ref SpecialFunctions.expint)
583578"""
584- expintx (ν:: Number , z:: Number , niter:: Int = 1000 ) = _expint (ν, z, niter, Val {true} ())
579+ expintx (ν:: Number , z:: Number , niter:: Int = 1000 ) =
580+ _expint (promotereal (ν, float (z))... , niter, Val {true} ())
585581
586582# #############################################################################
587583# expinti function Ei
0 commit comments