elliptic-integrals-0.1.0.1: src/Math/EllipticIntegrals/Carlson.hs
module Math.EllipticIntegrals.Carlson
(carlsonRF, carlsonRF',
carlsonRD, carlsonRD',
carlsonRJ, carlsonRJ',
carlsonRC, carlsonRC',
carlsonRG, carlsonRG')
where
import Data.Complex ( magnitude, Complex )
import Math.EllipticIntegrals.Internal ( Cplx, toCplx, atanC )
rf_ :: Cplx -> Cplx -> Cplx -> Double -> ((Double,Double,Double), Cplx)
rf_ x y z err =
let a = (x+y+z)/3 in
let delta = map (\u -> magnitude(1-u/a)) [x,y,z] in
if maximum delta < err
then ((delta !! 0, delta !! 1, delta !! 2), a)
else
let (sqrtx, sqrty, sqrtz) = (sqrt x, sqrt y, sqrt z) in
let lambda = sqrtx*sqrty + sqrty*sqrtz + sqrtz*sqrtx in
rf_ ((x+lambda)/4) ((y+lambda)/4) ((z+lambda)/4) err
-- | Carlson integral RF.
carlsonRF' ::
Double -- ^ bound on the relative error
-> Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double
carlsonRF' err x y z =
if zeros > 1
then error "At most one of x, y, z can be 0"
else
let ((dx,dy,dz), a) = rf_ x y z err in
let (e2,e3) = (dx*dy + dy*dz + dz*dx, dx*dy*dz) in
toCplx(1 - e2/10 + e3/14 + e2*e2/24 - 3*e2*e3/44 - 5*e2*e2*e2/208 +
3*e3*e3/104 + e2*e2*e3/16) / sqrt a
where
zeros = sum (map (\u -> fromEnum (u == 0)) [x,y,z])
-- | Carlson integral RF.
carlsonRF ::
Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double
carlsonRF = carlsonRF' 1e-15
rd_ :: Cplx -> Cplx -> Cplx -> Cplx -> Cplx -> Double ->
((Double,Double,Double), Cplx, Cplx, Cplx)
rd_ x y z s fac err =
let a = (x+y+z+z+z)/5 in
let delta = map (\u -> magnitude(1-u/a)) [x,y,z] in
if maximum delta < err
then ((delta !! 0, delta !! 1, delta !! 2), a, s, fac)
else
let (sqrtx, sqrty, sqrtz) = (sqrt x, sqrt y, sqrt z) in
let lambda = sqrtx*sqrty + sqrty*sqrtz + sqrtz*sqrtx in
let s' = s + fac / (sqrt z * (z + lambda)) in
rd_ ((x+lambda)/4) ((y+lambda)/4) ((z+lambda)/4) s' (fac/4) err
-- | Carlson integral RD.
carlsonRD' ::
Double -- ^ bound on the relative error
-> Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double
carlsonRD' err x y z =
if zeros > 1
then error "At most one of x, y, z can be 0"
else
let ((dx,dy,dz), a, s, fac) = rd_ x y z 0 1 err in
let
(e2,e3,e4,e5) = (dx*dy + dy*dz + 3*dz*dz + 2*dz*dx + dx*dz + 2*dy*dz,
dz*dz*dz + dx*dz*dz + 3*dx*dy*dz + 2*dy*dz*dz + dy*dz*dz + 2*dx*dz*dz,
dy*dz*dz*dz + dx*dz*dz*dz + dx*dy*dz*dz + 2*dx*dy*dz*dz,
dx*dy*dz*dz*dz) in
3*s + fac * toCplx(1 - 3*e2/14 + e3/6 + 9*e2*e2/88 - 3*e4/22 - 9*e2*e3/52 +
3*e5/26 - e2*e2*e2/16 + 3*e3*e3/40 + 3*e2*e4/20 + 45*e2*e2*e3/272 -
9*(e3*e4 + e2*e5)/68) / a / sqrt a
where
zeros = sum (map (\u -> fromEnum (u == 0)) [x,y,z])
-- | Carlson integral RD.
carlsonRD ::
Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double
carlsonRD = carlsonRD' 1e-15
rj_ :: Cplx -> Cplx -> Cplx -> Cplx -> Cplx -> Double -> Cplx -> Int ->
Double -> [Cplx] -> [Cplx] -> Double -> (Cplx, Int, [Cplx], [Cplx])
rj_ x y z p a maxmagns delta f fac d e err =
let q = (4/err)**(1/6) * maxmagns / fromIntegral f in
if magnitude a > q
then (a, f, d, e)
else
let dnew = (sqrt p + sqrt x)*(sqrt p + sqrt y)*(sqrt p + sqrt z)
d' = (fromIntegral f * dnew) : d
e' = (toCplx fac * delta / dnew / dnew) : e
lambda = sqrt x * sqrt y + sqrt y * sqrt z + sqrt z * sqrt x
x' = (x + lambda) / 4
y' = (y + lambda) / 4
z' = (z + lambda) / 4
p' = (p + lambda) / 4
a' = (a + lambda) / 4
in
rj_ x' y' z' p' a' maxmagns delta (4*f) (fac/64) d' e' err
-- | Carlson integral RJ.
carlsonRJ' ::
Double -- ^ bound on the relative error
-> Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double -- ^ fourth variable
-> Complex Double
carlsonRJ' err x y z p =
if zeros > 1
then error "At most one of x, y, z, p can be 0"
else
let a0 = (x + y + z + p + p) / 5
maxmagns = maximum $ map (\u -> magnitude(a0-u)) [x, y, z, p]
delta = (p-x)*(p-y)*(p-z)
in
let (a, f, d, e) = rj_ x y z p a0 maxmagns delta 1 1 [] [] err
f' = fromIntegral f
in
let x' = (a0 - x) / f' / a
y' = (a0 - y) / f' / a
z' = (a0 - z) / f' / a
p' = -(x'+y'+z') / 2
e2 = x'*y' + x'*z' + y'*z' - 3*p'*p'
e3 = x'*y'*z' + 2*e2*p' + 4*p'*p'*p'
e4 = p'*(2*x'*y'*z' + e2*p' + 3*p'*p'*p')
e5 = x'*y'*z'*p'*p'
h = zipWith (\u v -> atanx_over_x(sqrt u) / v) e d
in
(1 - 3*e2/14 + e3/6 + 9*e2*e2/88 - 3*e4/22 - 9*e2*e3/52 + 3*e5/26) /
f' / a / sqrt a + 6 * sum h
where
zeros = sum (map (\u -> fromEnum (u == 0)) [x,y,z,p])
atanx_over_x w = if w == 0 then 1 else atanC w / w
-- | Carlson integral RJ.
carlsonRJ ::
Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double -- ^ fourth variable
-> Complex Double
carlsonRJ = carlsonRJ' 1e-15
rc_ :: Cplx -> Cplx -> Cplx -> Double -> Int -> Double -> (Cplx, Int)
rc_ x y a magn f err =
let q = (1/3/err)**(1/8) * magn / fromIntegral f in
if magnitude a > q
then (a, f)
else
let lambda = 2 * sqrt x * sqrt y + y
a' = (a + lambda) / 4
x' = (x + lambda) / 4
y' = (y + lambda) / 4
in
rc_ x' y' a' magn (4*f) err
-- | Carlson integral RC.
carlsonRC' ::
Double -- ^ bound on the relative error
-> Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double
carlsonRC' err x y =
if y == 0
then error "y cannot be 0"
else
let a0 = (x + y + y) / 3
magn = magnitude(a0-x)
in
let (a, f) = rc_ x y a0 magn 1 err
f' = fromIntegral f
in
let s = (y - a0) / f' / a in
(1 + 3*s*s/10 + s*s*s/7 + 3*s*s*s*s/8 + 9*s*s*s*s*s/22 +
159*s*s*s*s*s*s/208 + 9*s*s*s*s*s*s*s/8) / sqrt a
-- | Carlson integral RC.
carlsonRC ::
Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double
carlsonRC = carlsonRC' 1e-15
-- | Carlson integral RG.
carlsonRG' ::
Double -- ^ bound on the relative error passed to `CarlsonRD'`
-> Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double
carlsonRG' err x y z =
if zeros > 1
then sqrt(x+y+z) / 2
else
if z == 0
then carlsonRG' err z x y
else
(z * carlsonRF' err x y z -
(x-z) * (y-z) * carlsonRD' err x y z / 3 +
sqrt x * sqrt y / sqrt z) / 2
where
zeros = sum (map (\u -> fromEnum (u == 0)) [x,y,z])
-- | Carlson integral RG.
carlsonRG ::
Complex Double -- ^ first variable
-> Complex Double -- ^ second variable
-> Complex Double -- ^ third variable
-> Complex Double
carlsonRG = carlsonRG' 1e-15