"""
    Tests for VF2 isomorphism algorithm for weighted graphs.
"""

from nose.tools import assert_true, assert_false
from operator import eq

import networkx as nx
import networkx.algorithms.isomorphism as iso


def test_simple():
    # 16 simple tests
    w = 'weight'
    edges = [(0, 0, 1), (0, 0, 1.5), (0, 1, 2), (1, 0, 3)]
    for g1 in [nx.Graph(),
               nx.DiGraph(),
               nx.MultiGraph(),
               nx.MultiDiGraph(),
               ]:

        g1.add_weighted_edges_from(edges)
        g2 = g1.subgraph(g1.nodes())
        if g1.is_multigraph():
            em = iso.numerical_multiedge_match('weight', 1)
        else:
            em = iso.numerical_edge_match('weight', 1)
        assert_true(nx.is_isomorphic(g1, g2, edge_match=em))

        for mod1, mod2 in [(False, True), (True, False), (True, True)]:
            # mod1 tests a regular edge
            # mod2 tests a selfloop
            if g2.is_multigraph():
                if mod1:
                    data1 = {0: {'weight': 10}}
                if mod2:
                    data2 = {0: {'weight': 1}, 1: {'weight': 2.5}}
            else:
                if mod1:
                    data1 = {'weight': 10}
                if mod2:
                    data2 = {'weight': 2.5}

            g2 = g1.subgraph(g1.nodes()).copy()
            if mod1:
                if not g1.is_directed():
                    g2._adj[1][0] = data1
                    g2._adj[0][1] = data1
                else:
                    g2._succ[1][0] = data1
                    g2._pred[0][1] = data1
            if mod2:
                if not g1.is_directed():
                    g2._adj[0][0] = data2
                else:
                    g2._succ[0][0] = data2
                    g2._pred[0][0] = data2

            assert_false(nx.is_isomorphic(g1, g2, edge_match=em))


def test_weightkey():
    g1 = nx.DiGraph()
    g2 = nx.DiGraph()

    g1.add_edge('A', 'B', weight=1)
    g2.add_edge('C', 'D', weight=0)

    assert_true(nx.is_isomorphic(g1, g2))
    em = iso.numerical_edge_match('nonexistent attribute', 1)
    assert_true(nx.is_isomorphic(g1, g2, edge_match=em))
    em = iso.numerical_edge_match('weight', 1)
    assert_false(nx.is_isomorphic(g1, g2, edge_match=em))

    g2 = nx.DiGraph()
    g2.add_edge('C', 'D')
    assert_true(nx.is_isomorphic(g1, g2, edge_match=em))


class TestNodeMatch_Graph(object):
    def setUp(self):
        self.g1 = nx.Graph()
        self.g2 = nx.Graph()
        self.build()

    def build(self):

        self.nm = iso.categorical_node_match('color', '')
        self.em = iso.numerical_edge_match('weight', 1)

        self.g1.add_node('A', color='red')
        self.g2.add_node('C', color='blue')

        self.g1.add_edge('A', 'B', weight=1)
        self.g2.add_edge('C', 'D', weight=1)

    def test_noweight_nocolor(self):
        assert_true(nx.is_isomorphic(self.g1, self.g2))

    def test_color1(self):
        assert_false(nx.is_isomorphic(self.g1, self.g2, node_match=self.nm))

    def test_color2(self):
        self.g1.nodes['A']['color'] = 'blue'
        assert_true(nx.is_isomorphic(self.g1, self.g2, node_match=self.nm))

    def test_weight1(self):
        assert_true(nx.is_isomorphic(self.g1, self.g2, edge_match=self.em))

    def test_weight2(self):
        self.g1.add_edge('A', 'B', weight=2)
        assert_false(nx.is_isomorphic(self.g1, self.g2, edge_match=self.em))

    def test_colorsandweights1(self):
        iso = nx.is_isomorphic(self.g1, self.g2,
                               node_match=self.nm, edge_match=self.em)
        assert_false(iso)

    def test_colorsandweights2(self):
        self.g1.nodes['A']['color'] = 'blue'
        iso = nx.is_isomorphic(self.g1, self.g2,
                               node_match=self.nm, edge_match=self.em)
        assert_true(iso)

    def test_colorsandweights3(self):
        # make the weights disagree
        self.g1.add_edge('A', 'B', weight=2)
        assert_false(nx.is_isomorphic(self.g1, self.g2,
                                      node_match=self.nm, edge_match=self.em))


class TestEdgeMatch_MultiGraph(object):
    def setUp(self):
        self.g1 = nx.MultiGraph()
        self.g2 = nx.MultiGraph()
        self.GM = iso.MultiGraphMatcher
        self.build()

    def build(self):
        g1 = self.g1
        g2 = self.g2

        # We will assume integer weights only.
        g1.add_edge('A', 'B', color='green', weight=0, size=.5)
        g1.add_edge('A', 'B', color='red', weight=1, size=.35)
        g1.add_edge('A', 'B', color='red', weight=2, size=.65)

        g2.add_edge('C', 'D', color='green', weight=1, size=.5)
        g2.add_edge('C', 'D', color='red', weight=0, size=.45)
        g2.add_edge('C', 'D', color='red', weight=2, size=.65)

        if g1.is_multigraph():
            self.em = iso.numerical_multiedge_match('weight', 1)
            self.emc = iso.categorical_multiedge_match('color', '')
            self.emcm = iso.categorical_multiedge_match(['color', 'weight'], ['', 1])
            self.emg1 = iso.generic_multiedge_match('color', 'red', eq)
            self.emg2 = iso.generic_multiedge_match(['color', 'weight', 'size'], ['red', 1, .5], [
                                                    eq, eq, iso.matchhelpers.close])
        else:
            self.em = iso.numerical_edge_match('weight', 1)
            self.emc = iso.categorical_edge_match('color', '')
            self.emcm = iso.categorical_edge_match(['color', 'weight'], ['', 1])
            self.emg1 = iso.generic_multiedge_match('color', 'red', eq)
            self.emg2 = iso.generic_edge_match(['color', 'weight', 'size'], ['red', 1, .5], [
                                               eq, eq, iso.matchhelpers.close])

    def test_weights_only(self):
        assert_true(nx.is_isomorphic(self.g1, self.g2, edge_match=self.em))

    def test_colors_only(self):
        gm = self.GM(self.g1, self.g2, edge_match=self.emc)
        assert_true(gm.is_isomorphic())

    def test_colorsandweights(self):
        gm = self.GM(self.g1, self.g2, edge_match=self.emcm)
        assert_false(gm.is_isomorphic())

    def test_generic1(self):
        gm = self.GM(self.g1, self.g2, edge_match=self.emg1)
        assert_true(gm.is_isomorphic())

    def test_generic2(self):
        gm = self.GM(self.g1, self.g2, edge_match=self.emg2)
        assert_false(gm.is_isomorphic())


class TestEdgeMatch_DiGraph(TestNodeMatch_Graph):
    def setUp(self):
        self.g1 = nx.DiGraph()
        self.g2 = nx.DiGraph()
        self.build()


class TestEdgeMatch_MultiDiGraph(TestEdgeMatch_MultiGraph):
    def setUp(self):
        self.g1 = nx.MultiDiGraph()
        self.g2 = nx.MultiDiGraph()
        self.GM = iso.MultiDiGraphMatcher
        self.build()
