import unittest
import networkx as nx
import numpy as np
from openqaoa.problems import MinimumVertexCover


def terms_list_equality(terms_list1, terms_list2):
    """
    Check the terms equality between two terms list
    where the order of edges do not matter.
    """
    if len(terms_list1) != len(terms_list2):
        bool = False
    else:
        for term1, term2 in zip(terms_list1, terms_list2):
            bool = True if (term1 == term2 or term1 == term2[::-1]) else False

    return bool


def terms_list_isclose(terms_list1, terms_list2):
    """
    Check if the distance between two terms list
    where the order of edges do not matter.
    """
    if len(terms_list1) != len(terms_list2):
        bool = False
    else:
        for term1, term2 in zip(terms_list1, terms_list2):
            bool = (
                True
                if np.isclose(term1, term2) or np.isclose(term1, term2[::-1])
                else False
            )

    return bool

class TestMinimumVertexCover(unittest.TestCase):
    """Tests for MinimumVertexCover class"""
    def test_mvc_terms_weights_constant(self):
        """Test terms,weights,constant of QUBO generated by MVC class"""

        mvc_terms = [
            [0, 3],
            [0, 4],
            [1, 2],
            [1, 3],
            [2, 4],
            [3, 4],
            [0],
            [1],
            [2],
            [3],
            [4],
        ]
        mvc_weights = [1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 2.0, 2.0, 2.0, 3.25, 3.25]
        mvc_constant = 10.0

        gr = nx.generators.fast_gnp_random_graph(5, 0.8, seed=1234)
        mvc_prob = MinimumVertexCover(gr, field=1.0, penalty=5).qubo

        self.assertTrue(terms_list_equality(mvc_terms, mvc_prob.terms))
        self.assertEqual(mvc_weights, mvc_prob.weights)
        self.assertEqual(mvc_constant, mvc_prob.constant)

    def test_mvc_random_problem(self):
        """Test the random_instance method of MVC class"""
        mvc_terms = [
            [0, 3],
            [0, 4],
            [1, 2],
            [1, 3],
            [2, 4],
            [3, 4],
            [0],
            [1],
            [2],
            [3],
            [4],
        ]
        mvc_weights = [2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 4.5, 4.5, 4.5, 7.0, 7.0]
        mvc_constant = 17.5

        mvc_prob_random = MinimumVertexCover.random_instance(
            n_nodes=5, edge_probability=0.8, seed=1234
        ).qubo

        self.assertTrue(terms_list_equality(mvc_terms, mvc_prob_random.terms))
        self.assertEqual(mvc_weights, mvc_prob_random.weights)
        self.assertEqual(mvc_constant, mvc_prob_random.constant)

    def test_mvc_type_checking(self):
        """
        Checks if the type-checking returns the right error.
        """

        # graph type-check
        graph_list = [(1, 2), {"node1": 1, "node2": 2}, np.array([1, 2])]
        field = 0.1
        penalty = 0.1

        for each_graph in graph_list:
            with self.assertRaises(TypeError) as e:
                MinimumVertexCover(each_graph, field, penalty)
            self.assertEqual(
                "Input problem graph must be a networkx Graph.", str(e.exception)
            )

        # field capacity type-check
        graph = nx.circulant_graph(6, [1])
        field_list = [np.array(1), np.array(0.5), "oh"]
        penalty = 0.1

        for each_field in field_list:
            with self.assertRaises(TypeError) as e:
                MinimumVertexCover(graph, each_field, penalty)
            self.assertEqual(
                "The input parameter, field, has to be of type float or int",
                str(e.exception),
            )

        # penalty capacity type-check
        graph = nx.circulant_graph(6, [1])
        field = 0.1
        penalty_list = [np.array(1), np.array(0.5), "oh"]

        for each_penalty in penalty_list:
            with self.assertRaises(TypeError) as e:
                MinimumVertexCover(graph, field, each_penalty)
            self.assertEqual(
                "The input parameter, penalty, has to be of type float or int",
                str(e.exception),
            )


if __name__ == "__main__":
    unittest.main()
