import numpy as np
import pytest
from mathpy.numerical.roots import newtonraph, bisection, secant


class TestRoots:

    @staticmethod
    def _test_func1(x):
        return x ** 2 - 10

    @staticmethod
    def _test_func2(x):
        return x ** 3 - 2 * x - 5

    @staticmethod
    def _test_func3(x):
        return x

    def test_newton_raph(self):
        np.testing.assert_almost_equal(newtonraph(self._test_func1, 3)[0], 3.16227766017)
        np.testing.assert_almost_equal(newtonraph(self._test_func2, 2)[0], 2.094551481495814)
        np.testing.assert_equal(newtonraph(self._test_func3, 0.0), 0.0)

        with pytest.raises(TypeError):
            newtonraph([1,2,3], 2)

    def test_bisection(self):
        np.testing.assert_almost_equal(bisection(self._test_func1, 3, 4)[0], 3.16227766017)
        np.testing.assert_almost_equal(bisection(self._test_func2, 2, 3)[0], 2.094551481495814)

        with pytest.raises(TypeError):
            bisection([1, 2, 3], 1, 2)

        with pytest.raises(ValueError):
            bisection(self._test_func1, 1, 2)

        with pytest.raises(ValueError):
            bisection(self._test_func1, 10, 11)

    def test_secant(self):
        np.testing.assert_almost_equal(secant(self._test_func1, 0, 2)[0], 3.16227766017)
        np.testing.assert_almost_equal(secant(self._test_func2, 2, 3)[0], 2.094551481495814)

        with pytest.raises(TypeError):
            secant([1, 2, 3], 1, 2)
