import cmath
from functools import lru_cache
import rundec
import logging

logging.basicConfig(format='%(levelname)s:%(message)s')
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())

@lru_cache(maxsize=5000)
def kaellen(x,y,z):
    return (x-y-z)**2 - 4*y*z

@lru_cache(maxsize=5000)
def tminus(Q2,m12,m22):
    return -0.5*(Q2 - m12 - m22 + cmath.sqrt(kaellen(Q2,m12,m22)))

@lru_cache(maxsize=5000)
def tplus(Q2,m12,m22):
    return -0.5*(Q2 - m12 - m22 - cmath.sqrt(kaellen(Q2,m12,m22)))

@lru_cache(maxsize=5000)
def RunAlfas(Qstart, Qend, AlfasIn, MT=172.5, MB=4.18, looplevel=2):
    """Performs running of $\\alpha_s$ in the SM using RunDec.

    Args:
        Qstart: the input scale in GeV.
        Qend: the end scale in GeV.
        AlfasIn: $\\alpha_s(Q_\\text{start})$.

    Returns:
        $\\alpha_s(Q_\\text{end})$
    """

    if Qstart < MB or Qstart > MT:
        logger.error('Alfas running only implemented for Qstart > MB, Qstart < MT')
        return

    crd = rundec.CRunDec()
    if Qend < MT:
        return crd.AlphasExact(AlfasIn, Qstart, Qend, 5, looplevel)
    else:
        as5MT = crd.AlphasExact(AlfasIn, Qstart, MT, 5, looplevel)
        as6MT = crd.DecAsUpOS(as5MT, MT, 6, looplevel-1)
        return crd.AlphasExact(as6MT, MT, Qend, 6, looplevel)

    # return AlfasIn/(1 + (11 - 10/3.)/(4*cmath.pi)*AlfasIn*cmath.log((Qend/Qstart)**2))

def MZfw(MZrun, GammaZ = 2.4952):
    """Converts $M_Z$ defined with running width to $M_Z$ defined with fixed width.

    Args:
        MZrun: $M_Z$ defined with running width
        GammaZ: total decay width of the $Z$ boson

    Returns:
        $M_Z$ defined with fixed width
    """
    return MZrun - 0.5*GammaZ**2/MZrun

def MZrun(MZfw, GammaZ = 2.4952):
    """Converts $M_Z$ defined with fixed width to $M_Z$ defined with running width.

    Args:
        MZfw: $M_Z$ defined with fixed width
        GammaZ: total decay width of the $Z$ boson

    Returns:
        $M_Z$ defined with running width
    """
    return MZfw + 0.5*GammaZ**2/MZfw

def MWfw(MWrun, AlfasMW, GF):
    """Converts $M_W$ defined with running width to $M_W$ defined with fixed width.

    Args:
        MWrun: $M_W$ defined with running width
        AlfasMW: $\\alpha_s(M_W)$
        GF: the Fermi constant $G_F$

    Returns:
        $M_W$ defined with fixed width
    """
    return MWrun*(1 - (3/(4*cmath.pi)*GF*MWrun**2*(1 + 2/(3*cmath.pi)*AlfasMW))**2)

def MWrun(MWfw, AlfasMW, GF):
    """Converts $M_W$ defined with fixed width to $M_W$ defined with running width.

    Args:
        MWfw: $M_W$ defined with fixed width
        AlfasMW: $\\alpha_s(M_W)$
        GF: the Fermi constant $G_F$

    Returns:
        $M_W$ defined with running width
    """
    return MWfw*(1 + (3/(4*cmath.pi)*GF*MWfw**2*(1 + 2/(3*cmath.pi)*AlfasMW))**2)
