"""Tests for range exercises"""
from copy import deepcopy
import unittest

from helpers import BaseTestCase
from ranges import (
    get_factors,
    is_range,
    annual_return,
    power_list,
    is_prime,
    identity,
    reverse_difference,
    with_previous,
    matrix_add,
    parse_row,
    triples,
    interleave,
)


class GetFactorsTests(BaseTestCase):

    """Tests for get_factors."""

    def test_2(self):
        self.assertEqual(get_factors(2), [1, 2])

    def test_6(self):
        self.assertEqual(get_factors(6), [1, 2, 3, 6])

    def test_100(self):
        self.assertEqual(get_factors(100), [1, 2, 4, 5, 10, 20, 25, 50, 100])

    @unittest.skip("Comment this line for negative factors exercise.")
    def test_negatives(self):
        message = "Only positive numbers are supported"
        with self.assertRaisesRegexp(ValueError, message):
            get_factors(-1)
        with self.assertRaisesRegexp(ValueError, message):
            get_factors(-1000)


class AnnualReturnTests(unittest.TestCase):

    """Tests for annual_return."""

    def assertReturns(self, actual_returns, expected_returns):
        actual_returns = [round(n) for n in actual_returns]
        expected_returns = [round(n) for n in expected_returns]
        self.assertEqual(actual_returns, expected_returns)

    def test_zero_start_and_yield(self):
        self.assertReturns(
            annual_return(start=0, annual_yield=0, years=10, annual_contribution=0),
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        )

    def test_one_year(self):
        self.assertReturns(
            annual_return(start=100, annual_yield=0.07, years=1, annual_contribution=0),
            [107],
        )

    def test_ten_years_at_7_percent_doubles(self):
        self.assertReturns(
            annual_return(
                start=100, annual_yield=0.07, years=10, annual_contribution=0
            ),
            [107, 114, 123, 131, 140, 150, 161, 172, 184, 197],
        )

    def test_forty_years_at_4_percent(self):
        self.assertReturns(
            annual_return(
                start=100, annual_yield=0.04, years=40, annual_contribution=0
            ),
            [
                104,
                108,
                112,
                117,
                122,
                127,
                132,
                137,
                142,
                148,
                154,
                160,
                167,
                173,
                180,
                187,
                195,
                203,
                211,
                219,
                228,
                237,
                246,
                256,
                267,
                277,
                288,
                300,
                312,
                324,
                337,
                351,
                365,
                379,
                395,
                410,
                427,
                444,
                462,
                480,
            ],
        )

    def test_no_annual_yield(self):
        self.assertReturns(
            annual_return(start=100, annual_yield=0, years=10, annual_contribution=0),
            [100] * 10,
        )

    def test_no_start(self):
        self.assertReturns(
            annual_return(start=0, annual_yield=0.07, years=10, annual_contribution=0),
            [0] * 10,
        )

    def test_zero_years(self):
        self.assertEqual(
            list(
                annual_return(start=0, annual_yield=0, years=0, annual_contribution=0)
            ),
            [],
        )

    def test_annual_contribution_same_as_start(self):
        self.assertReturns(
            annual_return(
                start=100,
                annual_yield=0.07,
                annual_contribution=100,
                years=10,
            ),
            [207, 321, 444, 575, 715, 865, 1026, 1198, 1382, 1578],
        )

    def test_contribution_with_no_start_value(self):
        self.assertReturns(
            annual_return(
                start=0,
                annual_yield=0.09,
                annual_contribution=100,
                years=10,
            ),
            [100, 209, 328, 457, 598, 752, 920, 1103, 1302, 1519],
        )


class IsRangeTests(BaseTestCase):

    """Tests for is_range."""

    def test_range_starting_at_zero(self):
        self.assertTrue(is_range([0, 1, 2, 3]))

    def test_nonrange_starting_at_zero(self):
        self.assertFalse(is_range([0, 2, 1, 3]))

    def test_range_starting_at_nonzero(self):
        self.assertTrue(is_range([5, 6, 7, 8]))

    def test_nonrange_starting_at_nonzero(self):
        self.assertFalse(is_range([5, 6, 8]))


class PrimalityTests(BaseTestCase):

    """Tests for is_prime."""

    def test_21(self):
        self.assertFalse(is_prime(21))

    def test_23(self):
        self.assertTrue(is_prime(23))


class PowerListTests(BaseTestCase):

    """Tests for power_list."""

    def test_integers(self):
        self.assertEqual(power_list([3, 2, 5]), [1, 2, 25])

    def test_floats(self):
        inputs = [78, 700, 82, 16, 2, 3, 9.5]
        outputs = [1, 700, 6724, 4096, 16, 243, 735091.890625]
        self.assertEqual(power_list(inputs), outputs)


class ReverseDifferenceTests(BaseTestCase):

    """Tests for reverse_difference."""

    def test_empty(self):
        self.assertEqual(reverse_difference([]), [])

    def test_one(self):
        self.assertEqual(reverse_difference([1]), [0])

    def test_two(self):
        self.assertEqual(reverse_difference([0, 0]), [0, 0])

    def test_three(self):
        self.assertEqual(reverse_difference([3, 2, 1]), [2, 0, -2])

    def test_four(self):
        self.assertEqual(reverse_difference([9, 8, 7, 6]), [3, 1, -1, -3])

    def test_five(self):
        inputs = [1, 2, 3, 4, 5]
        outputs = [-4, -2, 0, 2, 4]
        self.assertEqual(reverse_difference(inputs), outputs)


class WithPreviousTests(BaseTestCase):

    """Tests for with_previous."""

    def test_three(self):
        inputs = [1, 2, 3]
        outputs = [(1, None), (2, 1), (3, 2)]
        self.assertEqual(with_previous(inputs), outputs)

    def test_empty(self):
        self.assertEqual(with_previous([]), [])

    def test_one_item(self):
        self.assertEqual(with_previous([1]), [(1, None)])

    def test_string(self):
        inputs = "hey"
        outputs = [("h", None), ("e", "h"), ("y", "e")]
        self.assertEqual(with_previous(inputs), outputs)


class IdentityTests(BaseTestCase):

    """Tests for identity."""

    def test_2_by_2_matrix(self):
        expected = [[1, 0], [0, 1]]
        self.assertEqual(identity(2), expected)

    def test_3_by_3_matrix(self):
        expected = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
        self.assertEqual(identity(3), expected)

    def test_4_by_4_matrix(self):
        expected = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
        self.assertEqual(identity(4), expected)


class MatrixAddTests(BaseTestCase):

    """Tests for matrix_add."""

    def test_single_items(self):
        self.assertEqual(matrix_add([[5]], [[-2]]), [[3]])

    def test_two_by_two_matrixes(self):
        m1 = [[6, 6], [3, 1]]
        m2 = [[1, 2], [3, 4]]
        m3 = [[7, 8], [6, 5]]
        self.assertEqual(matrix_add(m1, m2), m3)

    def test_two_by_three_matrixes(self):
        m1 = [[1, 2, 3], [4, 5, 6]]
        m2 = [[-1, -2, -3], [-4, -5, -6]]
        m3 = [[0, 0, 0], [0, 0, 0]]
        self.assertEqual(matrix_add(m1, m2), m3)

    def test_input_unchanged(self):
        m1 = [[6, 6], [3, 1]]
        m2 = [[1, 2], [3, 4]]
        m1_original = deepcopy(m1)
        m2_original = deepcopy(m2)
        matrix_add(m1, m2)
        self.assertEqual(m1, m1_original)
        self.assertEqual(m2, m2_original)

    @unittest.skip("Any number of matrices")
    def test_any_number_of_matrixes(self):
        m1 = [[6, 6], [3, 1]]
        m2 = [[1, 2], [3, 4]]
        m3 = [[2, 1], [3, 4]]
        m4 = [[9, 9], [9, 9]]
        m5 = [[31, 32], [27, 24]]
        self.assertEqual(matrix_add(m1, m2, m3), m4)
        self.assertEqual(matrix_add(m2, m3, m1, m1, m2, m4, m1), m5)


class ParseRowTests(BaseTestCase):

    """Tests for parse_row."""

    def test_multiple_colors(self):
        inputs = "purple,indigo,red,blue,green\n0.15,0.25,0.3,0.05,0.25"
        outputs = {
            "blue": "0.05",
            "green": "0.25",
            "purple": "0.15",
            "red": "0.3",
            "indigo": "0.25",
        }
        self.assertEqual(parse_row(inputs), outputs)


class InterleaveTests(BaseTestCase):

    """Tests for interleave."""

    def test_empty_lists(self):
        self.assertEqual(interleave([], []), [])

    def test_single_item_each(self):
        self.assertEqual(interleave([1], [2]), [1, 2])

    def test_two_items_each(self):
        self.assertEqual(interleave([1, 2], [3, 4]), [1, 3, 2, 4])

    def test_four_items_each(self):
        in1 = [1, 2, 3, 4]
        in2 = [5, 6, 7, 8]
        out = [1, 5, 2, 6, 3, 7, 4, 8]
        self.assertEqual(interleave(in1, in2), out)

    def test_none_value(self):
        in1 = [1, 2, 3, None]
        in2 = [4, 5, 6, 7]
        out = [1, 4, 2, 5, 3, 6, None, 7]
        self.assertEqual(interleave(in1, in2), out)

    def test_more_than_two_arguments(self):
        in1 = [1, 2, 3]
        in2 = [7, 8, 9]
        out = [1, 7, 2, 8, 3, 9]
        self.assertEqual(interleave(in1, in2), out)


class TriplesTests(BaseTestCase):

    """Tests for triples."""

    def test_triples_1(self):
        expected = []
        self.assertEqual(triples(1), expected)

    def test_triples_6(self):
        expected = [(3, 4, 5)]
        self.assertEqual(triples(6), expected)

    def test_triples_25(self):
        expected = [
            (3, 4, 5),
            (5, 12, 13),
            (6, 8, 10),
            (8, 15, 17),
            (9, 12, 15),
            (12, 16, 20),
        ]
        self.assertEqual(triples(25), expected)


if __name__ == "__main__":
    from helpers import error_message

    error_message()
