#!/usr/bin/env python
"""
    elliptic.py

    Implements the Jacobi theta and Jacobi elliptic functions, using
    arbitrary precision math library

    Author of the first version: M.T. Taschuk

    References:

    [1] Abramowitz & Stegun. 'Handbook of Mathematical Functions, 9th Ed.',
        (Dover duplicate of 1972 edition)
    [2] Whittaker 'A Course of Modern Analysis, 4th Ed.', 1946,
        Cambridge Univeristy Press

"""
import sys

from mptypes import (mpf, mpc, mp, mpmathify, eps, one, zero, j)
from functions import (pi, sqrt, cos, sin, exp, log, tanh, ellipk,
                       sech, nthroot)
from libmpf import to_fixed, MP_ZERO, mpf_shift, from_man_exp
from sympy.mpmath.libelefun import cos_sin

# The series for the Jacobi theta functions converge for |q| < 1;
# in the current implementation they throw a ValueError for
# abs(q) > Q_LIM
Q_LIM = 1 - 10**-7

def calculate_nome(k):
    """
    Calculate the nome, q, from the value for k.

    Useful factoids:

    k**2 = m;   m is used in Abramowitz
    """
    k = mpmathify(k)

    if abs(k) > one:             # range error
        raise ValueError

    if k == zero:
        return zero
    elif k == one:
        return one
    else:
        kprimesquared = one - k**2
        kprime = sqrt(kprimesquared)
        top = ellipk(kprimesquared)
        bottom = ellipk(k**2)

        argument = -pi*top/bottom

        nome = exp(argument)
        return nome

def calculate_k(q):
    """
    Calculates the value of k for a particular nome, q,
    using jacobi theta functions.
    """

    q = mpmathify(q)

    v2 = jacobi_theta(2, 0, q)
    v3 = jacobi_theta(3, 0, q)
    m = v2**2/v3**2
    return m


def _jacobi_theta2(z, q):
    extra1 = 10
    extra2 = 20
    # the loops below break when the fixed precision quantities
    # a and b go to zero;
    # right shifting small negative numbers by wp one obtains -1, not zero,
    # so the condition a**2 + b**2 > MIN is used to break the loops.
    MIN = 2
    if z == zero:
        if isinstance(q, mpf):
            wp = mp.prec + extra1
            x = to_fixed(q._mpf_, wp)
            x2 = (x*x) >> wp
            a = b = x2
            s = x2
            while abs(a) > MIN:
                b = (b*x2) >> wp
                a = (a*b) >> wp
                s += a
            s = (1 << (wp+1)) + (s << 1)
            s = mpf(from_man_exp(s, -wp, mp.prec, 'n'))
        else:
            wp = mp.prec + extra1
            xre, xim = q._mpc_
            xre = to_fixed(xre, wp)
            xim = to_fixed(xim, wp)
            x2re = (xre*xre - xim*xim) >> wp
            x2im = (xre*xim) >> (wp - 1)
            are = bre = x2re
            aim = bim = x2im
            sre = (1<<wp) + are
            sim = aim
            while are**2 + aim**2 > MIN:
                bre, bim = (bre * x2re - bim * x2im) >> wp, \
                           (bre * x2im + bim * x2re) >> wp
                are, aim = (are * bre - aim * bim) >> wp,   \
                           (are * bim + aim * bre) >> wp
                sre += are
                sim += aim
            sre = (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
    else:
        if isinstance(q, mpf) and isinstance(z, mpf):
            wp = mp.prec + extra1
            x = to_fixed(q._mpf_, wp)
            x2 = (x*x) >> wp
            a = b = x2
            c1, s1 = cos_sin(z._mpf_, wp)
            cn = c1 = to_fixed(c1, wp)
            sn = s1 = to_fixed(s1, wp)
            c2 = (c1*c1 - s1*s1) >> wp
            s2 = (c1 * s1) >> (wp - 1)
            cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
            s = c1 + ((a * cn) >> wp)
            while abs(a) > MIN:
                b = (b*x2) >> wp
                a = (a*b) >> wp
                cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
                s += (a * cn) >> wp
            s = (s << 1)
            s = mpf(from_man_exp(s, -wp, mp.prec, 'n'))
            s *= nthroot(q, 4)
            return s
        # case z real, q complex
        elif isinstance(z, mpf):
            wp = mp.prec + extra2
            xre, xim = q._mpc_
            xre = to_fixed(xre, wp)
            xim = to_fixed(xim, wp)
            x2re = (xre*xre - xim*xim) >> wp
            x2im = (xre*xim) >> (wp - 1)
            are = bre = x2re
            aim = bim = x2im
            c1, s1 = cos_sin(z._mpf_, wp)
            cn = c1 = to_fixed(c1, wp)
            sn = s1 = to_fixed(s1, wp)
            c2 = (c1*c1 - s1*s1) >> wp
            s2 = (c1 * s1) >> (wp - 1)
            cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
            sre = c1 + ((are * cn) >> wp)
            sim = ((aim * cn) >> wp)
            while are**2 + aim**2 > MIN:
                bre, bim = (bre * x2re - bim * x2im) >> wp, \
                           (bre * x2im + bim * x2re) >> wp
                are, aim = (are * bre - aim * bim) >> wp,   \
                           (are * bim + aim * bre) >> wp
                cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp

                sre += ((are * cn) >> wp)
                sim += ((aim * cn) >> wp)
            sre = (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
        #case z complex, q real
        elif isinstance(q, mpf):
            wp = mp.prec + extra2
            x = to_fixed(q._mpf_, wp)
            x2 = (x*x) >> wp
            a = b = x2
            prec0 = mp.prec
            mp.prec = wp
            c1 = cos(z)
            s1 = sin(z)
            mp.prec = prec0
            cnre = c1re = to_fixed(c1.real._mpf_, wp)
            cnim = c1im = to_fixed(c1.imag._mpf_, wp)
            snre = s1re = to_fixed(s1.real._mpf_, wp)
            snim = s1im = to_fixed(s1.imag._mpf_, wp)
            #c2 = (c1*c1 - s1*s1) >> wp
            c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp
            c2im = (c1re*c1im - s1re*s1im) >> (wp - 1)
            #s2 = (c1 * s1) >> (wp - 1)
            s2re = (c1re*s1re - c1im*s1im) >> (wp - 1)
            s2im = (c1re*s1im + c1im*s1re) >> (wp - 1)
            #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
            t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
            t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
            t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
            t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
            cnre = t1
            cnim = t2
            snre = t3
            snim = t4

            sre = c1re + ((a * cnre) >> wp)
            sim = c1im + ((a * cnim) >> wp)
            while abs(a) > MIN:
                b = (b*x2) >> wp
                a = (a*b) >> wp
                t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
                t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
                t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
                t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
                cnre = t1
                cnim = t2
                snre = t3
                snim = t4
                sre += ((a * cnre) >> wp)
                sim += ((a * cnim) >> wp)
            sre = (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
        # case z and q complex
        else:
            wp = mp.prec + extra2
            xre, xim = q._mpc_
            xre = to_fixed(xre, wp)
            xim = to_fixed(xim, wp)
            x2re = (xre*xre - xim*xim) >> wp
            x2im = (xre*xim) >> (wp - 1)
            are = bre = x2re
            aim = bim = x2im
            prec0 = mp.prec
            mp.prec = wp
            # cos(z), siz(z) with z complex
            c1 = cos(z)
            s1 = sin(z)
            mp.prec = prec0
            cnre = c1re = to_fixed(c1.real._mpf_, wp)
            cnim = c1im = to_fixed(c1.imag._mpf_, wp)
            snre = s1re = to_fixed(s1.real._mpf_, wp)
            snim = s1im = to_fixed(s1.imag._mpf_, wp)
            c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp
            c2im = (c1re*c1im - s1re*s1im) >> (wp - 1)
            s2re = (c1re*s1re - c1im*s1im) >> (wp - 1)
            s2im = (c1re*s1im + c1im*s1re) >> (wp - 1)
            t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
            t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
            t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
            t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
            cnre = t1
            cnim = t2
            snre = t3
            snim = t4
            n = 1
            termre = c1re
            termim = c1im
            sre = c1re + ((are * cnre - aim * cnim) >> wp)
            sim = c1im + ((are * cnim + aim * cnre) >> wp)

            n = 3
            termre = ((are * cnre - aim * cnim) >> wp)
            termim = ((are * cnim + aim * cnre) >> wp)
            sre = c1re + ((are * cnre - aim * cnim) >> wp)
            sim = c1im + ((are * cnim + aim * cnre) >> wp)

            n = 5
            while are**2 + aim**2 > MIN:
                bre, bim = (bre * x2re - bim * x2im) >> wp, \
                           (bre * x2im + bim * x2re) >> wp
                are, aim = (are * bre - aim * bim) >> wp,   \
                           (are * bim + aim * bre) >> wp
                #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp
                t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
                t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
                t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
                t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
                cnre = t1
                cnim = t2
                snre = t3
                snim = t4
                termre = ((are * cnre - aim * cnim) >> wp)
                termim = ((aim * cnre + are * cnim) >> wp)
                sre += ((are * cnre - aim * cnim) >> wp)
                sim += ((aim * cnre + are * cnim) >> wp)
                n += 2
            sre = (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
    s *= nthroot(q, 4)
    return s

def _djacobi_theta2(z, q, nd):
    MIN = 2
    extra1 = 10
    extra2 = 20
    if isinstance(q, mpf) and isinstance(z, mpf):
        wp = mp.prec + extra1
        x = to_fixed(q._mpf_, wp)
        x2 = (x*x) >> wp
        a = b = x2
        c1, s1 = cos_sin(z._mpf_, wp)
        cn = c1 = to_fixed(c1, wp)
        sn = s1 = to_fixed(s1, wp)
        c2 = (c1*c1 - s1*s1) >> wp
        s2 = (c1 * s1) >> (wp - 1)
        cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
        if (nd&1):
            s = s1 + ((a * sn * 3**nd) >> wp)
        else:
            s = c1 + ((a * cn * 3**nd) >> wp)
        n = 2
        while abs(a) > MIN:
            b = (b*x2) >> wp
            a = (a*b) >> wp
            cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
            if nd&1:
                s += (a * sn * (2*n+1)**nd) >> wp
            else:
                s += (a * cn * (2*n+1)**nd) >> wp
            n += 1
        s = -(s << 1)
        s = mpf(from_man_exp(s, -wp, mp.prec, 'n'))
        # case z real, q complex
    elif isinstance(z, mpf):
        wp = mp.prec + extra2
        xre, xim = q._mpc_
        xre = to_fixed(xre, wp)
        xim = to_fixed(xim, wp)
        x2re = (xre*xre - xim*xim) >> wp
        x2im = (xre*xim) >> (wp - 1)
        are = bre = x2re
        aim = bim = x2im
        c1, s1 = cos_sin(z._mpf_, wp)
        cn = c1 = to_fixed(c1, wp)
        sn = s1 = to_fixed(s1, wp)
        c2 = (c1*c1 - s1*s1) >> wp
        s2 = (c1 * s1) >> (wp - 1)
        cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
        if (nd&1):
            sre = s1 + ((are * sn * 3**nd) >> wp)
            sim = ((aim * sn * 3**nd) >> wp)
        else:
            sre = c1 + ((are * cn * 3**nd) >> wp)
            sim = ((aim * cn * 3**nd) >> wp)
        n = 5
        while are**2 + aim**2 > MIN:
            bre, bim = (bre * x2re - bim * x2im) >> wp, \
                       (bre * x2im + bim * x2re) >> wp
            are, aim = (are * bre - aim * bim) >> wp,   \
                       (are * bim + aim * bre) >> wp
            cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp

            if (nd&1):
                sre += ((are * sn * n**nd) >> wp)
                sim += ((aim * sn * n**nd) >> wp)
            else:
                sre += ((are * cn * n**nd) >> wp)
                sim += ((aim * cn * n**nd) >> wp)
            n += 2
        sre = -(sre << 1)
        sim = -(sim << 1)
        sre = from_man_exp(sre, -wp, mp.prec, 'n')
        sim = from_man_exp(sim, -wp, mp.prec, 'n')
        s = mpc(sre, sim)
    #case z complex, q real
    elif isinstance(q, mpf):
        wp = mp.prec + extra2
        x = to_fixed(q._mpf_, wp)
        x2 = (x*x) >> wp
        a = b = x2
        prec0 = mp.prec
        mp.prec = wp
        c1 = cos(z)
        s1 = sin(z)
        mp.prec = prec0
        cnre = c1re = to_fixed(c1.real._mpf_, wp)
        cnim = c1im = to_fixed(c1.imag._mpf_, wp)
        snre = s1re = to_fixed(s1.real._mpf_, wp)
        snim = s1im = to_fixed(s1.imag._mpf_, wp)
        #c2 = (c1*c1 - s1*s1) >> wp
        c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp
        c2im = (c1re*c1im - s1re*s1im) >> (wp - 1)
        #s2 = (c1 * s1) >> (wp - 1)
        s2re = (c1re*s1re - c1im*s1im) >> (wp - 1)
        s2im = (c1re*s1im + c1im*s1re) >> (wp - 1)
        #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp
        t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
        t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
        t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
        t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
        cnre = t1
        cnim = t2
        snre = t3
        snim = t4

        if (nd&1):
            sre = s1re + ((a * snre * 3**nd) >> wp)
            sim = s1im + ((a * snim * 3**nd) >> wp)
        else:
            sre = c1re + ((a * cnre * 3**nd) >> wp)
            sim = c1im + ((a * cnim * 3**nd) >> wp)
        n = 5
        while abs(a) > MIN:
            b = (b*x2) >> wp
            a = (a*b) >> wp
            t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
            t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
            t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
            t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
            cnre = t1
            cnim = t2
            snre = t3
            snim = t4
            if (nd&1):
                sre += ((a * snre * n**nd) >> wp)
                sim += ((a * snim * n**nd) >> wp)
            else:
                sre += ((a * cnre * n**nd) >> wp)
                sim += ((a * cnim * n**nd) >> wp)
            n += 2
        sre = -(sre << 1)
        sim = -(sim << 1)
        sre = from_man_exp(sre, -wp, mp.prec, 'n')
        sim = from_man_exp(sim, -wp, mp.prec, 'n')
        s = mpc(sre, sim)
    # case z and q complex
    else:
        wp = mp.prec + extra2
        xre, xim = q._mpc_
        xre = to_fixed(xre, wp)
        xim = to_fixed(xim, wp)
        x2re = (xre*xre - xim*xim) >> wp
        x2im = (xre*xim) >> (wp - 1)
        are = bre = x2re
        aim = bim = x2im
        prec0 = mp.prec
        mp.prec = wp
        # cos(2*z), siz(2*z) with z complex
        c1 = cos(z)
        s1 = sin(z)
        mp.prec = prec0
        cnre = c1re = to_fixed(c1.real._mpf_, wp)
        cnim = c1im = to_fixed(c1.imag._mpf_, wp)
        snre = s1re = to_fixed(s1.real._mpf_, wp)
        snim = s1im = to_fixed(s1.imag._mpf_, wp)
        c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp
        c2im = (c1re*c1im - s1re*s1im) >> (wp - 1)
        s2re = (c1re*s1re - c1im*s1im) >> (wp - 1)
        s2im = (c1re*s1im + c1im*s1re) >> (wp - 1)
        t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
        t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
        t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
        t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
        cnre = t1
        cnim = t2
        snre = t3
        snim = t4
        if (nd&1):
            sre = s1re + (((are * snre - aim * snim) * 3**nd) >> wp)
            sim = s1im + (((are * snim + aim * snre)* 3**nd) >> wp)
        else:
            sre = c1re + (((are * cnre - aim * cnim) * 3**nd) >> wp)
            sim = c1im + (((are * cnim + aim * cnre)* 3**nd) >> wp)
        n = 5
        while are**2 + aim**2 > MIN:
            bre, bim = (bre * x2re - bim * x2im) >> wp, \
                       (bre * x2im + bim * x2re) >> wp
            are, aim = (are * bre - aim * bim) >> wp,   \
                       (are * bim + aim * bre) >> wp
            #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp
            t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp
            t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp
            t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp
            t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp
            cnre = t1
            cnim = t2
            snre = t3
            snim = t4
            if (nd&1):
                sre += (((are * snre - aim * snim) * n**nd) >> wp)
                sim += (((aim * snre + are * snim) * n**nd) >> wp)
            else:
                sre += (((are * cnre - aim * cnim) * n**nd) >> wp)
                sim += (((aim * cnre + are * cnim) * n**nd) >> wp)
            n += 2
        sre = -(sre << 1)
        sim = -(sim << 1)
        sre = from_man_exp(sre, -wp, mp.prec, 'n')
        sim = from_man_exp(sim, -wp, mp.prec, 'n')
        s = mpc(sre, sim)
    s *= nthroot(q, 4)
    if (nd&1):
        return (-1)**(nd//2) * s
    else:
        return (-1)**(1 + nd//2) * s

def _jacobi_theta3(z, q):
    extra1 = 10
    extra2 = 20
    MIN = 2
    if z == zero:
        if isinstance(q, mpf):
            wp = mp.prec + extra1
            x = to_fixed(q._mpf_, wp)
            s = x
            a = b = x
            x2 = (x*x) >> wp
            while abs(a) > MIN:
                b = (b*x2) >> wp
                a = (a*b) >> wp
                s += a
            s = (1 << wp) + (s << 1)
            s = mpf(from_man_exp(s, -wp, mp.prec, 'n'))
            return s
        else:
            wp = mp.prec + extra1
            xre, xim = q._mpc_
            xre = to_fixed(xre, wp)
            xim = to_fixed(xim, wp)
            x2re = (xre*xre - xim*xim) >> wp
            x2im = (xre*xim) >> (wp - 1)
            sre = are = bre = xre
            sim = aim = bim = xim
            while are**2 + aim**2 > MIN:
                bre, bim = (bre * x2re - bim * x2im) >> wp, \
                           (bre * x2im + bim * x2re) >> wp
                are, aim = (are * bre - aim * bim) >> wp,   \
                           (are * bim + aim * bre) >> wp
                sre += are
                sim += aim
            sre = (1 << wp) + (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
            return s
    else:
        if isinstance(q, mpf) and isinstance(z, mpf):
            s = MP_ZERO
            wp = mp.prec + extra1
            x = to_fixed(q._mpf_, wp)
            a = b = x
            x2 = (x*x) >> wp
            c1, s1 = cos_sin(mpf_shift(z._mpf_, 1), wp)
            c1 = to_fixed(c1, wp)
            s1 = to_fixed(s1, wp)
            cn = c1
            sn = s1
            s += (a * cn) >> wp
            while abs(a) > MIN:
                b = (b*x2) >> wp
                a = (a*b) >> wp
                cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp
                s += (a * cn) >> wp
            s = (1 << wp) + (s << 1)
            s = mpf(from_man_exp(s, -wp, mp.prec, 'n'))
            return s
        # case z real, q complex
        elif isinstance(z, mpf):
            wp = mp.prec + extra2
            xre, xim = q._mpc_
            xre = to_fixed(xre, wp)
            xim = to_fixed(xim, wp)
            x2re = (xre*xre - xim*xim) >> wp
            x2im = (xre*xim) >> (wp - 1)
            are = bre = xre
            aim = bim = xim
            c1, s1 = cos_sin(mpf_shift(z._mpf_, 1), wp)
            c1 = to_fixed(c1, wp)
            s1 = to_fixed(s1, wp)
            cn = c1
            sn = s1
            sre = (are * cn) >> wp
            sim = (aim * cn) >> wp
            while are**2 + aim**2 > MIN:
                bre, bim = (bre * x2re - bim * x2im) >> wp, \
                           (bre * x2im + bim * x2re) >> wp
                are, aim = (are * bre - aim * bim) >> wp,   \
                           (are * bim + aim * bre) >> wp
                cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp

                sre += (are * cn) >> wp
                sim += (aim * cn) >> wp
            sre = (1 << wp) + (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
            return s
        #case z complex, q real
        elif isinstance(q, mpf):
            wp = mp.prec + extra2
            x = to_fixed(q._mpf_, wp)
            a = b = x
            x2 = (x*x) >> wp
            prec0 = mp.prec
            mp.prec = wp
            c1 = cos(2*z)
            s1 = sin(2*z)
            mp.prec = prec0
            cnre = c1re = to_fixed(c1.real._mpf_, wp)
            cnim = c1im = to_fixed(c1.imag._mpf_, wp)
            snre = s1re = to_fixed(s1.real._mpf_, wp)
            snim = s1im = to_fixed(s1.imag._mpf_, wp)
            sre = (a * cnre) >> wp
            sim = (a * cnim) >> wp
            while abs(a) > MIN:
                b = (b*x2) >> wp
                a = (a*b) >> wp
                t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp
                t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp
                t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp
                t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp
                cnre = t1
                cnim = t2
                snre = t3
                snim = t4
                sre += (a * cnre) >> wp
                sim += (a * cnim) >> wp
            sre = (1 << wp) + (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
            return s
        # case z and q complex
        else:
            wp = mp.prec + extra2
            xre, xim = q._mpc_
            xre = to_fixed(xre, wp)
            xim = to_fixed(xim, wp)
            x2re = (xre*xre - xim*xim) >> wp
            x2im = (xre*xim) >> (wp - 1)
            are = bre = xre
            aim = bim = xim
            prec0 = mp.prec
            mp.prec = wp
            # cos(2*z), sin(2*z) with z complex
            c1 = cos(2*z)
            s1 = sin(2*z)
            mp.prec = prec0
            cnre = c1re = to_fixed(c1.real._mpf_, wp)
            cnim = c1im = to_fixed(c1.imag._mpf_, wp)
            snre = s1re = to_fixed(s1.real._mpf_, wp)
            snim = s1im = to_fixed(s1.imag._mpf_, wp)
            sre = (are * cnre - aim * cnim) >> wp
            sim = (aim * cnre + are * cnim) >> wp
            while are**2 + aim**2 > MIN:
                bre, bim = (bre * x2re - bim * x2im) >> wp, \
                           (bre * x2im + bim * x2re) >> wp
                are, aim = (are * bre - aim * bim) >> wp,   \
                           (are * bim + aim * bre) >> wp
                t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp
                t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp
                t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp
                t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp
                cnre = t1
                cnim = t2
                snre = t3
                snim = t4
                sre += (are * cnre - aim * cnim) >> wp
                sim += (aim * cnre + are * cnim) >> wp
            sre = (1 << wp) + (sre << 1)
            sim = (sim << 1)
            sre = from_man_exp(sre, -wp, mp.prec, 'n')
            sim = from_man_exp(sim, -wp, mp.prec, 'n')
            s = mpc(sre, sim)
            return s

def _djacobi_theta3(z, q, nd):
    """nd=1,2,3 order of the derivative with respect to z"""
    MIN = 2
    extra1 = 10
    extra2 = 20
    if isinstance(q, mpf) and isinstance(z, mpf):
        s = MP_ZERO
        wp = mp.prec + extra1
        x = to_fixed(q._mpf_, wp)
        a = b = x
        x2 = (x*x) >> wp
        c1, s1 = cos_sin(mpf_shift(z._mpf_, 1), wp)
        c1 = to_fixed(c1, wp)
        s1 = to_fixed(s1, wp)
        cn = c1
        sn = s1
        if (nd&1):
            s += (a * sn) >> wp
        else:
            s += (a * cn) >> wp
        n = 2
        while abs(a) > MIN:
            b = (b*x2) >> wp
            a = (a*b) >> wp
            cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp
            if nd&1:
                s += (a * sn * n**nd) >> wp
            else:
                s += (a * cn * n**nd) >> wp
            n += 1
        s = -(s << (nd+1))
        s = mpf(from_man_exp(s, -wp, mp.prec, 'n'))
    # case z real, q complex
    elif isinstance(z, mpf):
        wp = mp.prec + extra2
        xre, xim = q._mpc_
        xre = to_fixed(xre, wp)
        xim = to_fixed(xim, wp)
        x2re = (xre*xre - xim*xim) >> wp
        x2im = (xre*xim) >> (wp - 1)
        are = bre = xre
        aim = bim = xim
        c1, s1 = cos_sin(mpf_shift(z._mpf_, 1), wp)
        c1 = to_fixed(c1, wp)
        s1 = to_fixed(s1, wp)
        cn = c1
        sn = s1
        if (nd&1):
            sre = (are * sn) >> wp
            sim = (aim * sn) >> wp
        else:
            sre = (are * cn) >> wp
            sim = (aim * cn) >> wp
        n = 2
        while are**2 + aim**2 > MIN:
            bre, bim = (bre * x2re - bim * x2im) >> wp, \
                       (bre * x2im + bim * x2re) >> wp
            are, aim = (are * bre - aim * bim) >> wp,   \
                       (are * bim + aim * bre) >> wp
            cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp
            if nd&1:
                sre += (are * sn * n**nd) >> wp
                sim += (aim * sn * n**nd) >> wp
            else:
                sre += (are * cn * n**nd) >> wp
                sim += (aim * cn * n**nd) >> wp
            n += 1
        sre = -(sre << (nd+1))
        sim = -(sim << (nd+1))
        sre = from_man_exp(sre, -wp, mp.prec, 'n')
        sim = from_man_exp(sim, -wp, mp.prec, 'n')
        s = mpc(sre, sim)
    #case z complex, q real
    elif isinstance(q, mpf):
        wp = mp.prec + extra2
        x = to_fixed(q._mpf_, wp)
        a = b = x
        x2 = (x*x) >> wp
        prec0 = mp.prec
        mp.prec = wp
        c1 = cos(2*z)
        s1 = sin(2*z)
        mp.prec = prec0
        cnre = c1re = to_fixed(c1.real._mpf_, wp)
        cnim = c1im = to_fixed(c1.imag._mpf_, wp)
        snre = s1re = to_fixed(s1.real._mpf_, wp)
        snim = s1im = to_fixed(s1.imag._mpf_, wp)
        if (nd&1):
            sre = (a * snre) >> wp
            sim = (a * snim) >> wp
        else:
            sre = (a * cnre) >> wp
            sim = (a * cnim) >> wp
        n = 2
        while abs(a) > MIN:
            b = (b*x2) >> wp
            a = (a*b) >> wp
            t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp
            t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp
            t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp
            t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp
            cnre = t1
            cnim = t2
            snre = t3
            snim = t4
            if (nd&1):
                sre += (a * snre * n**nd) >> wp
                sim += (a * snim * n**nd) >> wp
            else:
                sre += (a * cnre * n**nd) >> wp
                sim += (a * cnim * n**nd) >> wp
            n += 1
        sre = -(sre << (nd+1))
        sim = -(sim << (nd+1))
        sre = from_man_exp(sre, -wp, mp.prec, 'n')
        sim = from_man_exp(sim, -wp, mp.prec, 'n')
        s = mpc(sre, sim)
    # case z and q complex
    else:
        wp = mp.prec + extra2
        xre, xim = q._mpc_
        xre = to_fixed(xre, wp)
        xim = to_fixed(xim, wp)
        x2re = (xre*xre - xim*xim) >> wp
        x2im = (xre*xim) >> (wp - 1)
        are = bre = xre
        aim = bim = xim
        prec0 = mp.prec
        mp.prec = wp
        # cos(2*z), sin(2*z) with z complex
        c1 = cos(2*z)
        s1 = sin(2*z)
        mp.prec = prec0
        cnre = c1re = to_fixed(c1.real._mpf_, wp)
        cnim = c1im = to_fixed(c1.imag._mpf_, wp)
        snre = s1re = to_fixed(s1.real._mpf_, wp)
        snim = s1im = to_fixed(s1.imag._mpf_, wp)
        if (nd&1):
            sre = (are * snre - aim * snim) >> wp
            sim = (aim * snre + are * snim) >> wp
        else:
            sre = (are * cnre - aim * cnim) >> wp
            sim = (aim * cnre + are * cnim) >> wp
        n = 2
        while are**2 + aim**2 > MIN:
            bre, bim = (bre * x2re - bim * x2im) >> wp, \
                       (bre * x2im + bim * x2re) >> wp
            are, aim = (are * bre - aim * bim) >> wp,   \
                       (are * bim + aim * bre) >> wp
            t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp
            t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp
            t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp
            t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp
            cnre = t1
            cnim = t2
            snre = t3
            snim = t4
            if(nd&1):
                sre += ((are * snre - aim * snim) * n**nd) >> wp
                sim += ((aim * snre + are * snim) * n**nd) >> wp
            else:
                sre += ((are * cnre - aim * cnim) * n**nd) >> wp
                sim += ((aim * cnre + are * cnim) * n**nd) >> wp
            n += 1
        sre = -(sre << (nd+1))
        sim = -(sim << (nd+1))
        sre = from_man_exp(sre, -wp, mp.prec, 'n')
        sim = from_man_exp(sim, -wp, mp.prec, 'n')
        s = mpc(sre, sim)
    if (nd&1):
        return (-1)**(nd//2) * s
    else:
        return (-1)**(1 + nd//2) * s

def _jacobi_theta2a(z, q):
    """
    case z.imag != 0
    theta(2, z, q) =
    q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=-inf, inf)
    max term for minimum (2*n+1)*log(q).real - 2* z.imag
    n0 = int(z.imag/log(q).real - 1/2)
    theta(2, z, q) =
    q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=n0, inf) +
    q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n, n0-1, -inf)
    """
    n = n0 = int(z.imag/log(q).real - 1/2)
    e2 = exp(2*j*z)
    e = e0 = exp(j*(2*n + 1)*z)
    a = q**(n*n + n)
    # leading term
    term = a * e
    s = term
    eps1 = eps*abs(term)
    while 1:
        n += 1
        e = e * e2
        term = q**(n*n + n) * e
        if abs(term) < eps1:
            break
        s += term
    e = e0
    e2 = exp(-2*j*z)
    n = n0
    while 1:
        n -= 1
        e = e * e2
        term = q**(n*n + n) * e
        if abs(term) < eps1:
            break
        s += term
    s = s * nthroot(q, 4)
    return s

def _jacobi_theta3a(z, q):
    """
    case z.imag != 0
    theta3(z, q) = Sum(q**(n*n) * exp(j*2*n*z), n, -inf, inf)
    max term for n*abs(log(q).real) + z.imag ~= 0
    n0 = int(- z.imag/abs(log(q).real))
    """
    n = n0 = int(- z.imag/abs(log(q).real))
    e2 = exp(2*j*z)
    e = e0 = exp(j*2*n*z)
    s = term = q**(n*n) * e
    eps1 = eps*abs(term)
    while 1:
        n += 1
        e = e * e2
        term = q**(n*n) * e
        if abs(term) < eps1:
            break
        s += term
    e = e0
    e2 = exp(-2*j*z)
    n = n0
    while 1:
        n -= 1
        e = e * e2
        term = q**(n*n) * e
        if abs(term) < eps1:
            break
        s += term
    return s

def _djacobi_theta2a(z, q, nd):
    """
    case z.imag != 0
    dtheta(2, z, q, nd) =
    j* q**1/4 * Sum(q**(n*n + n) * (2*n+1)*exp(j*(2*n + 1)*z), n=-inf, inf)
    max term for (2*n0+1)*log(q).real - 2* z.imag ~= 0
    n0 = int(z.imag/log(q).real - 1/2)
    """
    n = n0 = int(z.imag/log(q).real - 1/2)
    e2 = exp(2*j*z)
    e = e0 = exp(j*(2*n + 1)*z)
    a = q**(n*n + n)
    # leading term
    term = (2*n+1)**nd * a * e
    s = term
    eps1 = eps*abs(term)
    while 1:
        n += 1
        e = e * e2
        term = (2*n+1)**nd * q**(n*n + n) * e
        if abs(term) < eps1:
            break
        s += term
    e = e0
    e2 = exp(-2*j*z)
    n = n0
    while 1:
        n -= 1
        e = e * e2
        term = (2*n+1)**nd * q**(n*n + n) * e
        if abs(term) < eps1:
            break
        s += term
    return j**nd * s * nthroot(q, 4)

def _djacobi_theta3a(z, q, nd):
    """
    case z.imag != 0
    djtheta3(z, q, nd) = (2*j)**nd *
      Sum(q**(n*n) * n**nd * exp(j*2*n*z), n, -inf, inf)
    max term for minimum n*abs(log(q).real) + z.imag
    """
    n = n0 = int(-z.imag/abs(log(q).real))
    e2 = exp(2*j*z)
    e = e0 = exp(j*2*n*z)
    a = q**(n*n) * e
    s = term = n**nd * a
    if n != 0:
        eps1 = eps*abs(term)
    else:
        eps1 = eps*abs(a)
    while 1:
        n += 1
        e = e * e2
        a = q**(n*n) * e
        term = n**nd * a
        if n != 0:
            aterm = abs(term)
        else:
            aterm = abs(a)
        if aterm < eps1:
            break
        s += term
    e = e0
    e2 = exp(-2*j*z)
    n = n0
    while 1:
        n -= 1
        e = e * e2
        a = q**(n*n) * e
        term = n**nd * a
        if n != 0:
            aterm = abs(term)
        else:
            aterm = abs(a)
        if aterm < eps1:
            break
        s += term
    return (2*j)**nd * s




def jtheta(n, z, q):
    r"""
    Computes the Jacobi theta function `\vartheta_n(z, q)`, where
    `n = 1, 2, 3, 4`. The theta functions are functions of two
    variables:

    * `z` is the *argument*, an arbitrary real or complex number

    * `q` is the *nome*, which must be a real or complex number
      in the unit disk (i.e. `|q| < 1`)

    One also commonly encounters the notation `\vartheta_n(z, \tau)`
    in the literature. The variable `\tau` is called the *parameter*
    and can be converted to a nome using the formula
    `q = \exp(i \pi \tau)`. Note the condition `|q| < 1` requires
    `\Im(\tau) > 0`; i.e. Jacobi theta functions are defined for
    `\tau` in the upper half plane.

    Other notations are also in use. For example, some authors use
    the single-argument form `\vartheta_n(x)`. Depending on context,
    this can mean ``jtheta(n, 0, x)``, ``jtheta(n, x, q)``, or possibly
    something else. Needless to say, it is a good idea to cross-check
    the definitions when working with theta functions.

    **Definition**

    The four Jacobi theta functions as implemented by :func:`jtheta`
    are defined by the following infinite series:

    .. math ::

      \vartheta_1(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty}
        (-1)^n q^{n^2+n\,} \sin((2n+1)z)

      \vartheta_2(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty}
        q^{n^{2\,} + n} \cos((2n+1)z)

      \vartheta_3(z,q) = 1 + 2 \sum_{n=0}^{\infty}
        q^{n^2\,} \cos(2 n z)

      \vartheta_4(z,q) = 1 + 2 \sum_{n=0}^{\infty}
        (-q)^{n^2\,} \cos(2 n z)

    For `|q| \ll 1`, these series converge very quickly, so the
    Jacobi theta functions can efficiently be evaluated to high
    precision.

    **Examples and basic properties**

    Considered as functions of `z`, the Jacobi theta functions may be
    viewed as generalizations of the ordinary trigonometric functions
    cos and sin. They are periodic functions::

        >>> from sympy.mpmath import *
        >>> mp.dps = 15
        >>> print jtheta(1, 0.1, 1/5.)
        0.117756191842059
        >>> print jtheta(1, 0.1 + 2*pi, 1/5.)
        0.117756191842059

    Indeed, the series defining the theta functions are essentially
    trigonometric Fourier series. The coefficients can be retrieved
    using :func:`fourier`::

        >>> nprint(fourier(lambda x: jtheta(2, x, 0.5), [-pi, pi], 4))
        ([0.0, 1.68179, 0.0, 0.420448, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0])

    The Jacobi theta functions are also so-called quasiperiodic
    functions of `z` and `\tau`, meaning that for fixed `\tau`,
    `\vartheta_n(z, q)` and `\vartheta_n(z+\pi \tau, q)` are the same
    except for an exponential factor::

        >>> tau = 0.3*j
        >>> q = exp(pi*j*tau)
        >>> z = 10
        >>> print jtheta(4, z+tau*pi, q)
        (-0.682420280786035 + 1.5266839997214j)
        >>> print -exp(-2*j*z)/q * jtheta(4, z, q)
        (-0.682420280786035 + 1.5266839997214j)

    The Jacobi theta functions satisfy a huge number of other
    functional equations, such as the following identity (valid for
    any `q`)::

        >>> q = 0.3
        >>> print jtheta(3,0,q)**4
        6.82374408935276
        >>> print jtheta(2,0,q)**4 + jtheta(4,0,q)**4
        6.82374408935276

    Extensive listings of identities satisfied by the Jacobi theta
    functions can be found in standard reference works.

    The Jacobi theta functions are related to the gamma function
    for special arguments::

        >>> print jtheta(3, 0, exp(-pi))
        1.08643481121331
        >>> print pi**(1/4.) / gamma(3/4.)
        1.08643481121331

    :func:`jtheta` supports arbitrary precision evaluation and complex
    arguments::

        >>> mp.dps = 50
        >>> print jtheta(4, sqrt(2), 0.5)
        2.0549510717571539127004115835148878097035750653737
        >>> mp.dps = 25
        >>> print jtheta(4, 1+2j, (1+j)/5)
        (7.180331760146805926356634 - 1.634292858119162417301683j)

    **Possible issues**

    For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`jtheta` raises
    ``ValueError``. This exception is also raised for `|q|` extremely
    close to 1 (or equivalently `\tau` very close to 0), since the
    series would converge too slowly::

        >>> jtheta(1, 10, 0.99999999 * exp(0.5*j))
        Traceback (most recent call last):
          ...
        ValueError: abs(q) > Q_LIM = 1.000000

    """
    z = mpmathify(z)
    q = mpmathify(q)

    # Implementation note
    # If z.imag is close to zero, _jacobi_theta2 and _jacobi_theta3
    # are used,
    # which compute the series starting from n=0 using fixed precision
    # numbers;
    # otherwise  _jacobi_theta2a and _jacobi_theta3a are used, which compute
    # the series starting from n=n0, which is the largest term.

    # TODO write _jacobi_theta2a and _jacobi_theta3a using fixed precision.

    if abs(q) > Q_LIM:
        raise ValueError('abs(q) > Q_LIM = %f' % Q_LIM)

    extra = 10
    cz = 0.5
    extra2 = 50
    prec0 = mp.prec
    try:
        mp.prec += extra
        if n == 1:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta2(z - pi/2, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta2a(z - pi/2, q)
            else:
                res = _jacobi_theta2(z - pi/2, q)
        elif n == 2:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta2(z, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta2a(z, q)
            else:
                res = _jacobi_theta2(z, q)
        elif n == 3:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta3(z, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta3a(z, q)
            else:
                res = _jacobi_theta3(z, q)
        elif n == 4:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta3(z, -q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta3a(z, -q)
            else:
                res = _jacobi_theta3(z, -q)
        else:
            raise ValueError
    finally:
        mp.prec = prec0
    return res

def djtheta(n, z, q, nd=1):
    r"""
    For an integer `nd \ge 1`, computes the `nd`:th derivative with
    respect to `z` of the Jacobi theta function `\vartheta_n(z,q)`::

        >>> from sympy.mpmath import *
        >>> mp.dps = 15
        >>> print djtheta(3, 7, 0.2)
        -0.795947847483158
        >>> print diff(lambda x: jtheta(3, x, 0.2), 7)
        -0.795947847483158

    For additional details, see :func:`jtheta`.
    """

    z = mpmathify(z)
    q = mpmathify(q)

    if abs(q) > Q_LIM:
        raise ValueError('abs(q) > Q_LIM = %f' % Q_LIM)
    extra = 10 + mp.prec * nd // 10
    cz = 0.5
    extra2 = 50
    prec0 = mp.prec
    try:
        mp.prec += extra
        if n == 1:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta2(z - pi/2, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta2a(z - pi/2, q, nd)
            else:
                res = _djacobi_theta2(z - pi/2, q, nd)
        elif n == 2:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta2(z, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta2a(z, q, nd)
            else:
                res = _djacobi_theta2(z, q, nd)
        elif n == 3:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta3(z, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta3a(z, q, nd)
            else:
                res = _djacobi_theta3(z, q, nd)
        elif n == 4:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta3(z, -q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta3a(z, -q, nd)
            else:
                res = _djacobi_theta3(z, -q, nd)
        else:
            raise ValueError
    finally:
        mp.prec = prec0
    return res

def jsn(u, m):
    """
    Computes of the Jacobi elliptic sn function in terms
    of Jacobi theta functions.
    `u` is any complex number, `m` must be in the unit disk

    The sn-function is doubly periodic in the complex
    plane with periods `4 K(m)` and `2 i K(1-m)`
    (see :func:`ellipk`)::

        >>> from sympy.mpmath import *
        >>> mp.dps = 25
        >>> print jsn(2, 0.25)
        0.9628981775982774425751399
        >>> print jsn(2+4*ellipk(0.25), 0.25)
        0.9628981775982774425751399
        >>> print chop(jsn(2+2*j*ellipk(1-0.25), 0.25))
        0.9628981775982774425751399

    """
    if abs(m) < eps:
        return sin(u)
    elif m == one:
        return tanh(u)
    else:
        extra = 10
    try:
        mp.prec += extra
        q = calculate_nome(sqrt(m))

        v3 = jtheta(3, zero, q)
        v2 = jtheta(2, zero, q)        # mathworld says v4
        arg1 = u / (v3*v3)
        v1 = jtheta(1, arg1, q)
        v4 = jtheta(4, arg1, q)

        sn = (v3/v2)*(v1/v4)
    finally:
        mp.prec -= extra

    return sn


def jcn(u, m):
    """
    Computes of the Jacobi elliptic cn function in terms
    of Jacobi theta functions.
    `u` is any complex number, `m` must be in the unit disk

    The cn-function is doubly periodic in the complex
    plane with periods `4 K(m)` and `4 i K(1-m)`
    (see :func:`ellipk`)::

        >>> from sympy.mpmath import *
        >>> mp.dps = 25
        >>> print jcn(2, 0.25)
        -0.2698649654510865792581416
        >>> print jcn(2+4*ellipk(0.25), 0.25)
        -0.2698649654510865792581416
        >>> print chop(jcn(2+4*j*ellipk(1-0.25), 0.25))
        -0.2698649654510865792581416

    """
    if abs(m) < eps:
        return cos(u)
    elif m == one:
        return sech(u)
    else:
        extra = 10
    try:
        mp.prec += extra
        q = calculate_nome(sqrt(m))

        v3 = jtheta(3, zero, q)
        v2 = jtheta(2, zero, q)
        v04 = jtheta(4, zero, q)

        arg1 = u / (v3*v3)

        v1 = jtheta(2, arg1, q)
        v4 = jtheta(4, arg1, q)

        cn = (v04/v2)*(v1/v4)
    finally:
        mp.prec -= extra
    return cn


def jdn(u, m):
    """
    Computes of the Jacobi elliptic dn function in terms
    of Jacobi theta functions.
    `u` is any complex number, `m` must be in the unit disk

    The dn-function is doubly periodic in the complex
    plane with periods `2 K(m)` and `4 i K(1-m)`
    (see :func:`ellipk`)::

        >>> from sympy.mpmath import *
        >>> mp.dps = 25
        >>> print jdn(2, 0.25)
        0.8764740583123262286931578
        >>> print jdn(2+2*ellipk(0.25), 0.25)
        0.8764740583123262286931578
        >>> print chop(jdn(2+4*j*ellipk(1-0.25), 0.25))
        0.8764740583123262286931578

    """
    if m == zero:
        return one
    elif m == one:
        return sech(u)
    else:
        extra = 10
    try:
        mp.prec += extra
        q = calculate_nome(sqrt(m))

        v3 = jtheta(3, zero, q)
        v2 = jtheta(2, zero, q)
        v04 = jtheta(4, zero, q)

        arg1 = u / (v3*v3)

        v1 = jtheta(3, arg1, q)
        v4 = jtheta(4, arg1, q)

        cn = (v04/v3)*(v1/v4)
    finally:
        mp.prec -= extra
    return cn

jacobi_elliptic_sn = jsn
jacobi_elliptic_cn = jcn
jacobi_elliptic_dn = jdn

if __name__ == '__main__':
    import doctest
    doctest.testmod()
