module eml_type_fortran
    !! [All elementary functions from a single operator](https://arxiv.org/html/2603.21852)
    !! [SupplementaryInformation.pdf](https://arxiv.org/src/2603.21852v2/anc/SupplementaryInformation.pdf)

    use, non_intrinsic :: eml_class_fortran



    implicit none



    private

    public :: assignment(=)
    public :: operator(+)
    public :: operator(-)
    public :: operator(*)
    public :: operator(/)
    public :: operator(**)

    public :: eml_real64_type

    public :: acos
    public :: acosh
    public :: asin
    public :: asinh
    public :: atan
    public :: atanh
    public :: avg
    public :: cmplx            ! from `eml_class_fortran`
    public :: cos
    public :: cosh
    public :: eml
    public :: exp
    public :: half
    public :: hypot
    public :: ieee_is_nan      ! from `eml_class_fortran`
    public :: imag             ! from `eml_class_fortran`
    public :: inv
    public :: log
    public :: logistic_sigmoid
    public :: real             ! from `eml_class_fortran`
    public :: sin
    public :: sinh
    public :: sqr
    public :: sqrt
    public :: tan
    public :: tanh



    type, extends(eml_real64_class) :: eml_real64_type

        contains

        procedure, pass, private, non_overridable :: set_iu_real64
        procedure, pass, private, non_overridable :: set_m1_real64
        procedure, pass, private, non_overridable :: set_p2_real64
        procedure, pass, private, non_overridable :: set_pi_real64

        generic, public :: set_iu => set_iu_real64
        generic, public :: set_m1 => set_m1_real64
        generic, public :: set_p2 => set_p2_real64
        generic, public :: set_pi => set_pi_real64

    end type



    interface operator(+)
        module procedure :: eml_add_real64
    end interface

    interface operator(-) ! unary
        module procedure :: eml_neg_real64
    end interface

    interface operator(-) ! binary
        module procedure :: eml_sub_real64
    end interface

    interface operator(*)
        module procedure :: eml_mul_real64
    end interface

    interface operator(/)
        module procedure :: eml_div_real64
    end interface

    interface operator(**)
        module procedure eml_pow_real64
    end interface

    interface acos
        module procedure :: eml_acos_real64
    end interface

    interface acosh
        module procedure :: eml_acosh_real64
    end interface

    interface asin
        module procedure eml_asin_real64
    end interface

    interface asinh
        module procedure :: eml_asinh_real64
    end interface

    interface atan
        module procedure :: eml_atan_real64
    end interface

    interface atanh
        module procedure :: eml_atanh_real64
    end interface

    interface avg
        module procedure :: eml_avg_real64
    end interface

    interface cos
        module procedure :: eml_cos_real64
    end interface

    interface cosh
        module procedure :: eml_cosh_real64
    end interface

    interface eml
        module procedure :: eml_eml_real64
    end interface

    interface exp
        module procedure :: eml_exp_real64
    end interface

    interface half
        module procedure :: eml_half_real64
    end interface

    interface hypot
        module procedure :: eml_hypot_real64
    end interface

    interface inv
        module procedure :: eml_inv_real64
    end interface

    interface log
        module procedure :: eml_natural_log_real64
    end interface

    interface logistic_sigmoid
        module procedure :: eml_logistic_sigmoid_real64
    end interface

    interface sin
        module procedure :: eml_sin_real64
    end interface

    interface sinh
        module procedure eml_sinh_real64
    end interface

    interface sqr
        module procedure :: eml_sqr_real64
    end interface

    interface sqrt
        module procedure :: eml_sqrt_real64
    end interface

    interface tan
        module procedure :: eml_tan_real64
    end interface

    interface tanh
        module procedure eml_tanh_real64
    end interface



    interface eml_acos
        module procedure :: eml_acos_real64
    end interface

    interface eml_acosh
        module procedure :: eml_acosh_real64
    end interface

    interface eml_add
        module procedure :: eml_add_real64
    end interface

    interface eml_asin
        module procedure :: eml_asin_real64
    end interface

    interface eml_asinh
        module procedure :: eml_asinh_real64
    end interface

    interface eml_atan
        module procedure :: eml_atan_real64
    end interface

    interface eml_atanh
        module procedure :: eml_atanh_real64
    end interface

    interface eml_avg
        module procedure :: eml_avg_real64
    end interface

    interface eml_cos
        module procedure :: eml_cos_real64
    end interface

    interface eml_cosh
        module procedure :: eml_cosh_real64
    end interface

    interface eml_div
        module procedure :: eml_div_real64
    end interface

    interface eml_exp
        module procedure :: eml_exp_real64
    end interface

    interface eml_half
        module procedure :: eml_half_real64
    end interface

    interface eml_hypot
        module procedure :: eml_hypot_real64
    end interface

    interface eml_inv
        module procedure :: eml_inv_real64
    end interface

    interface eml_log
        module procedure :: eml_natural_log_real64
    end interface

    interface eml_logistic_sigmoid
        module procedure :: eml_logistic_sigmoid_real64
    end interface

    interface eml_mul
        module procedure :: eml_mul_real64
    end interface

    interface eml_neg
        module procedure :: eml_neg_real64
    end interface

    interface eml_pow
        module procedure :: eml_pow_real64
    end interface

    interface eml_sin
        module procedure :: eml_sin_real64
    end interface

    interface eml_sinh
        module procedure :: eml_sinh_real64
    end interface

    interface eml_sqr
        module procedure :: eml_sqr_real64
    end interface

    interface eml_sqrt
        module procedure :: eml_sqrt_real64
    end interface

    interface eml_sub
        module procedure :: eml_sub_real64
    end interface

    interface eml_tan
        module procedure :: eml_tan_real64
    end interface

    interface eml_tanh
        module procedure :: eml_tanh_real64
    end interface



    contains



    elemental type(eml_real64_type) function eml_acos_real64(x)
        !! \( \arccos (x) \equiv \sqrt{-1} \ln ( x + \sqrt{ x - 1 } \sqrt{ x + 1 } ) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: iu, p1



        call iu%set_iu
        call p1%set_p1

        eml_acos_real64 = &!
            eml_mul( &!
                iu , &!
                eml_log( &!
                    eml_add( &!
                        x , &!
                        eml_mul( &!
                            eml_sqrt( eml_sub(x, p1) ) , &!
                            eml_sqrt( eml_add(x, p1) )   &!
                        ) &!
                    ) &!
                ) &!
            )

    end function



    elemental type(eml_real64_type) function eml_acosh_real64(x)
        !! \( \operatorname{arcosh} (x) \equiv \ln ( x + \sqrt{ x - 1 } \sqrt{ x + 1 } ) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p1



        ! type(eml_real64_type) :: m1

        ! call m1%set_m1

        ! eml_acosh_real64 = eml_asinh( eml_hypot( x, eml_sqrt(m1) ) )

        call p1%set_p1

        eml_acosh_real64 = &!
            eml_log( &!
                eml_add( &!
                    x , &!
                    eml_mul( &!
                        eml_sqrt( eml_sub( x , p1 ) ) , &!
                        eml_sqrt( eml_add( x , p1 ) )   &!
                    ) &!
                ) &!
            )

    end function



    elemental type(eml_real64_type) function eml_add_real64(x, y)
        !! \( x + y \mapsto x - ( - y ) \)

        type(eml_real64_type), intent(in) :: x, y

        eml_add_real64 = eml_sub( x , eml_neg(y) )

    end function



    module elemental type(eml_real64_type) function eml_asin_real64(x)
        !! \( \arcsin (x) \equiv \sqrt{-1} \ln ( \sqrt{ 1 - x^2 } - \sqrt{-1} x ) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: iu, p1



        ! type(eml_real64_type) :: pi

        ! call pi%set_pi

        ! eml_asin_real64 = eml_sub( eml_half(pi) , eml_acos(x) )

        call iu%set_iu
        call p1%set_p1

        eml_asin_real64 = &!
            eml_mul( &!
                iu , &!
                eml_log( &!
                    eml_sub( &!
                        eml_sqrt( eml_sub( p1 , eml_sqr(x) ) ) , &!
                        eml_mul( iu , x ) &!
                    ) &!
                ) &!
            )

    end function



    elemental type(eml_real64_type) function eml_asinh_real64(x)
        !! \( \operatorname{arsinh} (x) \equiv \ln ( x + \sqrt{ x^2 + 1 } ) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p1



        ! type(eml_real64_type) :: m1

        ! call m1%set_m1

        ! eml_asinh_real64 = eml_log( eml_add( x , eml_hypot(m1, x) ) )

        call p1%set_p1

        eml_asinh_real64 = &!
            eml_log( &!
                eml_add( x , eml_sqrt( eml_add( p1 , eml_sqr(x) ) ) ) &!
            )

    end function



    elemental type(eml_real64_type) function eml_atan_real64(x)
        !! \( \arctan (x) \equiv ( \sqrt{-1} / 2 ) \ln \bigl( ( \sqrt{-1} + x ) / ( \sqrt{-1} - x ) \bigr) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) neg_iu



        ! eml_atan_real64 = eml_asin( eml_tanh( eml_asinh(x) ) )

        call neg_iu%set_iu

        neg_iu = eml_neg(neg_iu)

        eml_atan_real64 = &!
            eml_mul( &!
                eml_half(neg_iu) , &!
                eml_log( &!
                    eml_div( &!
                        x = eml_add( neg_iu , x ) , &!
                        y = eml_sub( neg_iu , x )   &!
                    ) &!
                ) &!
            )

    end function



    elemental type(eml_real64_type) function eml_atanh_real64(x)
        !! \( \operatorname{artanh} \equiv ( 1 / 2 ) \ln \bigl( ( 1 + x ) / ( 1 - x ) \bigr) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p1



        ! eml_atanh_real64 = eml_asinh( eml_inv( eml_tan( acos(x) ) ) )

        call p1%set_p1

        eml_atanh_real64 = &!
            eml_half( &!
                eml_sub( &!
                    eml_log( eml_add( p1 , x ) ) , &!
                    eml_log( eml_sub( p1 , x ) )   &!
                ) &!
            )

    end function



    elemental type(eml_real64_type) function eml_avg_real64(x, y)
        !! \( \operatorname{avg}(x,y) \equiv \operatorname{half}(x + y) \)

        type(eml_real64_type), intent(in) :: x, y

        eml_avg_real64 = eml_half( eml_add( x , y ) )

    end function



    elemental type(eml_real64_type) function eml_cos_real64(x)
        !! \( \cos (x) \equiv \cosh ( \sqrt{-1} x ) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: iu



        ! eml_cos_real64 = eml_cosh( eml_sqrt( eml_neg( eml_sqr(x) ) ) )

        call iu%set_iu

        eml_cos_real64 = eml_cosh( eml_neg( eml_mul( iu, x ) ) )

    end function



    elemental type(eml_real64_type) function eml_cosh_real64(x)
        !! \( \cosh (x) \equiv \operatorname{avg} \bigl( \exp (x) , \exp (-x) \bigr) \)

        type(eml_real64_type), intent(in) :: x

        eml_cosh_real64 = eml_avg( eml_exp( x ) , eml_exp( eml_neg(x) ) )

    end function



    elemental type(eml_real64_type) function eml_div_real64(x, y)
        !! \( x / y \mapsto x \times \operatorname{inv}(y) \)

        type(eml_real64_type), intent(in) :: x, y

        eml_div_real64 = eml_mul( x , eml_inv(y) )

    end function



    elemental type(eml_real64_type) function eml_eml_real64(x, y)
        !! \( \operatorname{eml} (x,y) = \exp (x) - \ln (y) \)

        type(eml_real64_type), intent(in) :: x, y

        eml_eml_real64 = eml_operator( x = x, y = y )

    end function



    elemental type(eml_real64_type) function eml_exp_real64(x)
        !! \( \exp (x) \equiv \operatorname{eml}(x,1) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p1



        call p1%set_p1

        eml_exp_real64 = eml( x = x, y = p1 )

    end function



    module elemental type(eml_real64_type) function eml_half_real64(x)
        !! \( \operatorname{half}(x) \equiv x / 2 \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p2



        call p2%set_p2

        eml_half_real64 = eml_div( x = x , y = p2 )

    end function



    elemental type(eml_real64_type) function eml_hypot_real64(x, y)
        !! \( \operatorname{hypot}(x,y) \equiv \sqrt{ x^2 + y^2 } \)

        type(eml_real64_type), intent(in) :: x, y

        eml_hypot_real64 = sqrt( eml_add( eml_sqr(x) , eml_sqr(y) ) )

    end function



    elemental type(eml_real64_type) function eml_inv_real64(x)
        !! \( \operatorname{inv}(x) \equiv 1 / x \mapsto \exp ( - \ln x ) \)

        type(eml_real64_type), intent(in) :: x

        eml_inv_real64 = eml_exp( eml_neg( eml_log(x) ) )

    end function



    elemental type(eml_real64_type) function eml_logistic_sigmoid_real64(x)
        !! \( \mathop{\sigma}(x) \equiv 1 / \bigl( 1 + \exp ( - x ) \bigr) \equiv 1 / \operatorname{eml} \bigl( - x , \exp ( -1 ) \bigr) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: m1



        call m1%set_m1

        eml_logistic_sigmoid_real64 = &!
            eml_inv( eml( x = eml_neg(x) , y = eml_exp(m1) ) )

    end function



    elemental type(eml_real64_type) function eml_mul_real64(x, y)
        !! \( x \times y \mapsto \exp \bigl( \ln (x) + \ln (y) \bigr) \)

        type(eml_real64_type), intent(in) :: x, y

        eml_mul_real64 = eml_exp( eml_add( eml_log(x) , eml_log(y) ) )

    end function



    elemental type(eml_real64_type) function eml_natural_log_real64(x)
        !! \( \ln (x) \equiv \operatorname{eml} \Bigl( 1, \exp \bigl( \operatorname{eml}(1,x) \bigr) \Bigr) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p1



        call p1%set_p1

        eml_natural_log_real64 = &!
            eml( x = p1, y = eml_exp( eml( x = p1, y = x ) ) )

    end function



    module elemental type(eml_real64_type) function eml_neg_real64(x)
        !! \( - x \mapsto ( \ln 1 ) - x \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p1



        call p1%set_p1

        eml_neg_real64 = eml_sub( x = eml_log(p1) , y = x )

    end function



    elemental type(eml_real64_type) function eml_pow_real64(x, y)
        !! \( x^y \mapsto \exp ( y \times \ln x ) \)

        type(eml_real64_type), intent(in) :: x, y

        eml_pow_real64 = eml_exp( eml_mul( y , eml_log(x) ) )

    end function



    elemental type(eml_real64_type) function eml_sin_real64(x)
        !!\( \sin (x) \equiv \cos \bigl( x - ( \pi / 2 ) \bigr) \)

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: pi



        call pi%set_pi

        eml_sin_real64 = eml_cos( eml_sub( x = x , y = eml_half(pi) ) )

    end function



    elemental type(eml_real64_type) function eml_sinh_real64(x)
        !! \( \sinh (x) \equiv \operatorname{eml} \bigl( x , \exp ( \cosh x ) \bigr) \)

        type(eml_real64_type), intent(in) :: x

        eml_sinh_real64 = eml( x = x , y = eml_exp( eml_cosh(x) ) )

    end function



    elemental type(eml_real64_type) function eml_sqr_real64(x)
        !! \( x^2 \equiv x \times x \)

        type(eml_real64_type), intent(in) :: x

        eml_sqr_real64 = eml_mul(x, x)

    end function



    elemental type(eml_real64_type) function eml_sqrt_real64(x)
        !! \( \sqrt{x} \equiv x^{1/2} \)
        !! @note
        !! This module did not choose \( \sqrt{x} \equiv \exp \bigl( \operatorname{half} ( \ln x ) \bigr) \)  
        !! because the return value becomes \( + \infty \) when \( x = 0 \).
        !! @endnote

        type(eml_real64_type), intent(in) :: x



        type(eml_real64_type) :: p2



        ! eml_sqrt_real64 = eml_exp( eml_half( eml_log(x) ) )

        call p2%set_p2

        eml_sqrt_real64 = eml_pow( x = x , y = eml_inv(p2) )

    end function



    elemental type(eml_real64_type) function eml_sub_real64(x, y)
        !! \( x - y \mapsto \operatorname{eml} \bigl( \ln (x) , \exp (y) \bigr) \)

        type(eml_real64_type), intent(in) :: x, y

        eml_sub_real64 = eml( x = eml_log(x) , y = eml_exp(y) )

    end function



    elemental type(eml_real64_type) function eml_tan_real64(x)

        type(eml_real64_type), intent(in) :: x

        eml_tan_real64 = eml_div( x = eml_sin(x) , y = eml_cos(x) )

    end function



    elemental type(eml_real64_type) function eml_tanh_real64(x)
        !! \( \tanh (x) \equiv \sinh (x) / \cosh (x) \)

        type(eml_real64_type), intent(in) :: x

        eml_tanh_real64 = eml_div( x = eml_sinh(x) , y = eml_cosh(x) )

    end function



    elemental subroutine set_iu_real64(self)
        !! \( \sqrt{-1} \mapsto - \exp \Bigl( \operatorname{half} \bigl( \log ( -1 ) \bigr) \Bigr) \)

        class(eml_real64_type), intent(inout) :: self



        type(eml_real64_type) :: m1



        call m1%set_m1

        self = eml_neg( eml_exp( eml_half( eml_log(m1) ) ) )

    end subroutine



    elemental subroutine set_m1_real64(self)
        !! \( -1 \mapsto \ln (1) - 1 \)

        class(eml_real64_type), intent(inout) :: self



        type(eml_real64_type) :: p1



        call p1%set_p1

        self = eml_sub( x = eml_log(p1) , y = p1 )

    end subroutine



    module elemental subroutine set_p2_real64(self)
        !! \( 2 \mapsto 1 - ( -1 ) \)

        class(eml_real64_type), intent(inout) :: self



        type(eml_real64_type) :: m1, p1



        call m1%set_m1
        call p1%set_p1

        self = eml_sub( x = p1 , y = m1 )

    end subroutine



    elemental subroutine set_pi_real64(self)
        !! \( \pi \mapsto \sqrt{ - {\bigl( \ln ( -1 ) \bigr)}^2 } \)

        class(eml_real64_type), intent(inout) :: self



        type(eml_real64_type) :: m1



        call m1%set_m1

        self = sqrt( eml_neg( eml_sqr( eml_log(m1) ) ) )

    end subroutine

end module
