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

import math
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 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 not 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 nx.is_isomorphic(g1, g2)
    em = iso.numerical_edge_match("nonexistent attribute", 1)
    assert nx.is_isomorphic(g1, g2, edge_match=em)
    em = iso.numerical_edge_match("weight", 1)
    assert not nx.is_isomorphic(g1, g2, edge_match=em)

    g2 = nx.DiGraph()
    g2.add_edge("C", "D")
    assert nx.is_isomorphic(g1, g2, edge_match=em)


class TestMatch_Graph:
    def setup_method(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 nx.is_isomorphic(self.g1, self.g2)

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

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

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

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

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

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

    def test_colorsandweights3(self):
        # make the colors agree and the weights disagree
        self.g1.nodes["A"]["color"] = "blue"
        self.g1.add_edge("A", "B", weight=2)
        assert not nx.is_isomorphic(
            self.g1, self.g2, node_match=self.nm, edge_match=self.em
        )


class TestMatch_DiGraph(TestMatch_Graph):
    def setup_method(self):
        self.g1 = nx.DiGraph()
        self.g2 = nx.DiGraph()
        self.build()


class TestMatch_MultiGraph:
    def setup_method(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=0.5)
        g1.add_edge("A", "B", color="red", weight=1, size=0.35)
        g1.add_edge("A", "B", color="red", weight=2, size=0.65)

        g2.add_edge("C", "D", color="green", weight=1, size=0.5)
        g2.add_edge("C", "D", color="red", weight=0, size=0.45)
        g2.add_edge("C", "D", color="red", weight=2, size=0.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, 0.5],
                [eq, eq, math.isclose],
            )

    def test_weights_only(self):
        assert 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 gm.is_isomorphic()

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

    def test_generic_color_only(self):
        gm = self.GM(self.g1, self.g2, edge_match=self.emg1)
        assert gm.is_isomorphic()

    def test_generic_all_3_attr(self):
        gm = self.GM(self.g1, self.g2, edge_match=self.emg2)
        assert not gm.is_isomorphic()

        self.g2.edges["C", "D", 0]["weight"] = 0
        self.g2.edges["C", "D", 1]["weight"] = 1
        self.g2.edges["C", "D", 1]["size"] = 0.35
        assert gm.is_isomorphic()


class TestMatch_MultiDiGraph(TestMatch_MultiGraph):
    def setup_method(self):
        self.g1 = nx.MultiDiGraph()
        self.g2 = nx.MultiDiGraph()
        self.GM = iso.MultiDiGraphMatcher
        self.build()
