"""
Faster versions of some key functions in mpmath.libmp.
"""
from __future__ import print_function, absolute_import

from .ext_impl cimport *
from sage.libs.gmp.all cimport *
from sage.rings.integer cimport Integer

from .ext_impl import exp_fixed, cos_sin_fixed, log_int_fixed

# Note: not thread-safe
cdef MPF tmp1
cdef MPF tmp2
MPF_init(&tmp1)
MPF_init(&tmp2)

def mpf_add(tuple x, tuple y, int prec=0, str rnd='d'):
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    MPF_set_tuple(&tmp2, y)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_add(&tmp1, &tmp1, &tmp2, opts)
    return MPF_to_tuple(&tmp1)

def mpf_sub(tuple x, tuple y, int prec=0, str rnd='d'):
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    MPF_set_tuple(&tmp2, y)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_sub(&tmp1, &tmp1, &tmp2, opts)
    return MPF_to_tuple(&tmp1)

def mpf_mul(tuple x, tuple y, int prec=0, str rnd='d'):
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    MPF_set_tuple(&tmp2, y)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_mul(&tmp1, &tmp1, &tmp2, opts)
    return MPF_to_tuple(&tmp1)

def mpf_div(tuple x, tuple y, int prec, str rnd='d'):
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    MPF_set_tuple(&tmp2, y)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_div(&tmp1, &tmp1, &tmp2, opts)
    return MPF_to_tuple(&tmp1)

def mpf_sqrt(tuple x, int prec, str rnd='d'):
    """
    Computes sqrt(x) with mpf value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpf_sqrt, from_float, to_float
        sage: x = from_float(2)
        sage: y = mpf_sqrt(x, 53, 'n')
        sage: print(to_float(y))
        1.41421356237

    """
    if x[0]:
        import mpmath.libmp as libmp
        raise libmp.ComplexResult("square root of a negative number")
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_sqrt(&tmp1, &tmp1, opts)
    return MPF_to_tuple(&tmp1)

def mpf_log(tuple x, int prec, str rnd='d'):
    """
    Computes log(x) with mpf value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpf_log, from_float, to_float
        sage: x = from_float(2)
        sage: y = mpf_log(x, 53, 'n')
        sage: print(to_float(y))
        0.69314718056

    """
    if x[0]:
        import mpmath.libmp as libmp
        raise libmp.ComplexResult("logarithm of a negative number")
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_log(&tmp1, &tmp1, opts)
    return MPF_to_tuple(&tmp1)

def mpf_exp(tuple x, int prec, str rnd='d'):
    """
    Computes exp(x) with mpf value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpf_exp, from_float, to_float
        sage: x = from_float(2)
        sage: z = mpf_exp(x, 53, 'n')
        sage: print(to_float(z))
        7.38905609893

    """
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_exp(&tmp1, &tmp1, opts)
    return MPF_to_tuple(&tmp1)

def mpf_cos(tuple x, int prec, str rnd='d'):
    """
    Computes cos(x) with mpf value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpf_cos, from_float, to_float
        sage: x = from_float(1)
        sage: y = mpf_cos(x, 53, 'n')
        sage: print(to_float(y))
        0.540302305868

    """
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_cos(&tmp1, &tmp1, opts)
    return MPF_to_tuple(&tmp1)

def mpf_sin(tuple x, int prec, str rnd='d'):
    """
    Computes sin(x) with mpf value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpf_sin, from_float, to_float
        sage: x = from_float(1)
        sage: y = mpf_sin(x, 53, 'n')
        sage: print(to_float(y))
        0.841470984808

    """
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_sin(&tmp1, &tmp1, opts)
    return MPF_to_tuple(&tmp1)

def mpc_sqrt(tuple z, int prec, str rnd='d'):
    """
    Computes sqrt(z) with mpc value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpc_sqrt, from_float, to_float
        sage: z = from_float(-2), from_float(0)
        sage: re, im = mpc_sqrt(z, 53, 'n')
        sage: to_float(re), to_float(im)
        (0.0, 1.4142135623730951)
    """
    cdef tuple a, b
    cdef MPopts opts
    a, b = z
    MPF_set_tuple(&tmp1, a)
    MPF_set_tuple(&tmp2, b)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_complex_sqrt(&tmp1, &tmp2, &tmp1, &tmp2, opts)
    return MPF_to_tuple(&tmp1), MPF_to_tuple(&tmp2)

def mpc_exp(tuple z, int prec, str rnd='d'):
    """
    Computes exp(z) with mpc value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpc_exp, from_float, to_float
        sage: z = from_float(0), from_float(1)
        sage: re, im = mpc_exp(z, 53, 'n')
        sage: to_float(re), to_float(im)
        (0.5403023058681398, 0.8414709848078965)
    """
    cdef tuple a, b
    cdef MPopts opts
    a, b = z
    MPF_set_tuple(&tmp1, a)
    MPF_set_tuple(&tmp2, b)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    MPF_complex_exp(&tmp1, &tmp2, &tmp1, &tmp2, opts)
    return MPF_to_tuple(&tmp1), MPF_to_tuple(&tmp2)

def mpf_pow(tuple x, tuple y, int prec, str rnd='d'):
    """
    Computes x ^ y with mpf value tuples.

    EXAMPLES::

        sage: from mpmath.libmp import mpf_pow, from_float, to_float
        sage: x = from_float(2)
        sage: y = from_float(3)
        sage: z = mpf_pow(x, y, 53, 'n')
        sage: to_float(z)
        8.0

    """
    cdef MPopts opts
    MPF_set_tuple(&tmp1, x)
    MPF_set_tuple(&tmp2, y)
    opts.rounding = rndmode_from_python(rnd)
    opts.prec = prec
    if MPF_pow(&tmp1, &tmp1, &tmp2, opts):
        import mpmath.libmp as libmp
        raise libmp.ComplexResult("negative number raised to a fractional power")
    return MPF_to_tuple(&tmp1)
