"""Tests for hyp2f1 for complex values.

Author: Albert Steppi, with credit to Adam Kullberg (FormerPhycisist) for
the implementation of mp_hyp2f1 below, which modifies mpmath's hyp2f1 to
return the same branch as scipy's on the standard branch cut.
"""

import pytest
import numpy as np
from typing import NamedTuple
from numpy.testing import assert_allclose

from scipy.special import hyp2f1
from scipy.special._testutils import check_version, MissingModule


try:
    import mpmath
except ImportError:
    mpmath = MissingModule("mpmath")


def mp_hyp2f1(a, b, c, z):
    """Return mpmath hyp2f1 calculated on same branch as scipy hyp2f1.

    For most values of a,b,c mpmath returns the x - 0j branch of hyp2f1 on the
    branch cut x=(1,inf) whereas scipy's hyp2f1 calculates the x + 0j branch.
    Thus, to generate the right comparison values on the branch cut, we
    evaluate mpmath.hyp2f1 at x + 1e-15*j.

    The exception to this occurs when c-a=-m in which case both mpmath and
    scipy calculate the x + 0j branch on the branch cut. When this happens
    mpmath.hyp2f1 will be evaluated at the original z point.
    """
    on_branch_cut = z.real > 1.0 and abs(z.imag) < 1.0e-15
    cond1 = abs(c - a - round(c - a)) < 1.0e-15 and round(c - a) <= 0
    cond2 = abs(c - b - round(c - b)) < 1.0e-15 and round(c - b) <= 0
    # Make sure imaginary part is *exactly* zero
    if on_branch_cut:
        z = z.real + 0.0j
    if on_branch_cut and not (cond1 or cond2):
        z_mpmath = z.real + 1.0e-15j
    else:
        z_mpmath = z
    return complex(mpmath.hyp2f1(a, b, c, z_mpmath))


class Hyp2f1TestCase(NamedTuple):
    a: float
    b: float
    c: float
    z: complex
    expected: complex
    rtol: float


class TestHyp2f1:
    """Tests for hyp2f1 for complex values.

    Expected values for test cases were computed using mpmath. See
    `scipy.special._precompute.hyp2f1_data`. The verbose style of specifying
    test cases is used for readability and to make it easier to mark individual
    cases as expected to fail. Expected failures are used to highlight cases
    where improvements are needed. See
    `scipy.special._precompute.hyp2f1_data.make_hyp2f1_test_cases` for a
    function to generate the boilerplate for the test cases.

    Assertions have been added to each test to ensure that the test cases match
    the situations that are intended. A final test `test_test_hyp2f1` checks
    that the expected values in the test cases actually match what is computed
    by mpmath. This test is marked slow even though it isn't particularly slow
    so that it won't run by default on continuous integration builds.
    """