/* ==========================================
 * JGraphT : a free Java graph-theory library
 * ==========================================
 *
 * Project Info:  http://jgrapht.sourceforge.net/
 * Project Creator:  Barak Naveh (http://sourceforge.net/users/barak_naveh)
 *
 * (C) Copyright 2003-2008, by Barak Naveh and Contributors.
 *
 * This program and the accompanying materials are dual-licensed under
 * either
 *
 * (a) the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation, or (at your option) any
 * later version.
 *
 * or (per the licensee's choosing)
 *
 * (b) the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation.
 */
/* -----------------
 * IsomorphismInspectorTest.java
 * -----------------
 * (C) Copyright 2005-2008, by Assaf Lehr and Contributors.
 *
 * Original Author:  Assaf Lehr
 * Contributor(s):   -
 *
 * $Id$
 *
 * Changes
 * -------
 */
package org.jgrapht.experimental.isomorphism;

import java.util.*;

import junit.framework.*;

import org.jgrapht.*;
import org.jgrapht.experimental.equivalence.*;
import org.jgrapht.experimental.isomorphism.comparators.*;
import org.jgrapht.generate.*;
import org.jgrapht.graph.*;


/**
 * @author Assaf
 * @since May 27, 2005
 */
public class IsomorphismInspectorTest
    extends TestCase
{
    //~ Constructors -----------------------------------------------------------

    /**
     * Constructor for IsomorphismInspectorTest.
     *
     * @param arg0
     */
    public IsomorphismInspectorTest(String arg0)
    {
        super(arg0);
    }

    //~ Methods ----------------------------------------------------------------

    /*
     * @see TestCase#setUp()
     */
    protected void setUp()
        throws Exception
    {
        super.setUp();
    }

    /**
     * Calls the same method with different (default) parameters
     * EqualityGroupChecker vertexChecker = null EqualityGroupChecker
     * edgeChecker = null
     */
    private void assertIsomorphic(
        Graph<Integer, DefaultEdge> [] graphs,
        boolean shouldTheyBeIsomorphic)
    {
        assertIsomorphic(graphs, shouldTheyBeIsomorphic, null, null);
    }

    @SuppressWarnings("unchecked")
    private void assertIsomorphic(
        Graph<Integer, DefaultEdge> [] graphs,
        boolean shouldTheyBeIsomorphic,
        EquivalenceComparator vertexChecker,
        EquivalenceComparator edgeChecker)
    {
        // System.out.println("\nassertIsomorphic:"+shouldTheyBeIsomorphic);
        Graph<Integer, DefaultEdge> g1 = graphs[0];
        Graph<Integer, DefaultEdge> g2 = graphs[1];

        // System.out.println("g1:"+g1);
        // System.out.println("g2:"+g2);
        // long beforeTime=System.currentTimeMillis();
        GraphIsomorphismInspector iso =
            AdaptiveIsomorphismInspectorFactory.createIsomorphismInspector(
                g1,
                g2,
                vertexChecker,
                edgeChecker);
        int counter = 0;
        while (iso.hasNext()) {
            IsomorphismRelation isioResult = (IsomorphismRelation) iso.next();

            if (false) {
                if (counter == 0) {
                    System.out.println(
                        "Graphs are isomorphic. Iterating over all options:");
                }
                System.out.println(counter + " : " + isioResult);
            }
            counter++;
        }

        // if (counter==0)
        // {
        // System.out.println("Graphs are NOT isomorphic.");
        // }
        // long deltaTime=System.currentTimeMillis()-beforeTime;
        // String timeDesc;
        // timeDesc= deltaTime<=10 ?  "<10ms [less than minimum measurement
        // time]": String.valueOf(deltaTime);
        // System.out.println("# Performence: Isomorphism check in
        // MiliSeconds:"+timeDesc);
        if (shouldTheyBeIsomorphic) {
            assertTrue(counter != 0);
        } else {
            assertTrue(counter == 0);
        }
    }

    @SuppressWarnings("unchecked")
    private void checkRelation(
        Graph<Integer, DefaultEdge> [] graphs,
        EquivalenceComparator vertexChecker,
        EquivalenceComparator edgeChecker)
    {
        Graph<Integer, DefaultEdge> g1 = graphs[0];
        Graph<Integer, DefaultEdge> g2 = graphs[1];

        GraphIsomorphismInspector iso =
            AdaptiveIsomorphismInspectorFactory.createIsomorphismInspector(
                g1,
                g2,
                vertexChecker,
                edgeChecker);
        IsomorphismRelation<Integer, DefaultEdge> isoResult;
        if (iso.hasNext()) {
            isoResult = (IsomorphismRelation) iso.next();

            Set<Integer> vertexSet = g1.vertexSet();
            for (
                Iterator<Integer> iter = vertexSet.iterator();
                iter.hasNext();)
            {
                Integer v1 = iter.next();
                Integer v2 = isoResult.getVertexCorrespondence(v1, true);
                if (false) {
                    System.out.println("Vertex relation " + v1 + " to " + v2);
                }
            }
            Set<DefaultEdge> edgeSet = g1.edgeSet();
            for (
                Iterator<DefaultEdge> iter = edgeSet.iterator();
                iter.hasNext();)
            {
                DefaultEdge e1 = iter.next();
                DefaultEdge e2 = isoResult.getEdgeCorrespondence(e1, true);
                if (false) {
                    System.out.println("Vertex relation " + e1 + " to " + e2);
                }
            }

            // if (counter==0)
            // {
            // System.out.println("Graphs are isomorphic. Iterating over all
            // options:");
            // }
            // System.out.println(counter+" : "+isioResult);

        }
    }

    @SuppressWarnings("unchecked")
    public void testWheelGraphAddRemoveParts()
    {
        final int NUM_OF_VERTEXES_IN_WHEEL = 6;
        final int FIRST_INTEGER_FOR_G2 = 13;

        Graph<Integer, DefaultEdge> g1 =
            new DefaultDirectedGraph<Integer, DefaultEdge>(
                DefaultEdge.class);
        Graph<Integer, DefaultEdge> g2 =
            new DefaultDirectedGraph<Integer, DefaultEdge>(
                DefaultEdge.class);
        WheelGraphGenerator<Integer, DefaultEdge> gen1 =
            new WheelGraphGenerator<Integer, DefaultEdge>(
                NUM_OF_VERTEXES_IN_WHEEL);
        gen1.generateGraph(g1, new IntegerVertexFactory(), null);

        // FIRST_INTEGER_FOR_G2-1 , cause first integer is always the next one.
        gen1.generateGraph(
            g2,
            new IntegerVertexFactory(FIRST_INTEGER_FOR_G2),
            null);
        assertIsomorphic(
            new Graph[] {
                g1,
                g2
            },
            true);

        // In a wheel , the last vertex is the wheel center!
        g1.removeVertex(new Integer(NUM_OF_VERTEXES_IN_WHEEL)); // delete one vertex from g1
        assertIsomorphic(
            new Graph[] {
                g1,
                g2
            },
            false);

        // for example: 10+4
        g2.removeVertex(
            new Integer(FIRST_INTEGER_FOR_G2
                + NUM_OF_VERTEXES_IN_WHEEL));
        assertIsomorphic(
            new Graph[] {
                g1,
                g2
            },
            true);

        g1.removeEdge(new Integer(1), new Integer(2));
        assertIsomorphic(
            new Graph[] {
                g1,
                g2
            },
            false);
    }

    @SuppressWarnings("unchecked")
    public void testLinear4vertexIsomorphicGraph()
    {
        Graph<Integer, DefaultEdge> g1 =
            new DefaultDirectedGraph<Integer, DefaultEdge>(
                DefaultEdge.class);
        LinearGraphGenerator gen1 = new LinearGraphGenerator(4);
        gen1.generateGraph(g1, new IntegerVertexFactory(), null);

        Graph<Integer, DefaultEdge> g2 =
            new DefaultDirectedGraph<Integer, DefaultEdge>(
                DefaultEdge.class);
        LinearGraphGenerator gen2 = new LinearGraphGenerator(4);
        gen2.generateGraph(g2, new IntegerVertexFactory(5), null); // start vertex from number 6
        assertIsomorphic(
            new Graph[] {
                g1,
                g2
            },
            true);

        checkRelation(
            new Graph[] {
                g1,
                g2
            },
            null,
            null);
    }

    /**
     * Create two graphs which are topologically the same (same number of
     * vertexes and same edges connection), but the contents of the vertexes
     * belong to different eq. set. g1: 1-->2-->3-->4 g2: 2-->3-->4-->5 g3:
     * 3-->4-->5-->6 The eq-group-check is if the number is even or odd. So, g1
     * and g3 are isomorphic. g2 is not isomorphic to either of them.
     */
    @SuppressWarnings("unchecked")
    public void testLinear4vertexNonIsomorphicCauseOfVertexEqGroup()
    {
        LinearGraphGenerator<Integer, DefaultEdge> gen4 =
            new LinearGraphGenerator<Integer, DefaultEdge>(4);

        Graph<Integer, DefaultEdge> g1 =
            new DefaultDirectedGraph<Integer, DefaultEdge>(
                DefaultEdge.class);
        gen4.generateGraph(g1, new IntegerVertexFactory(), null);

        Graph<Integer, DefaultEdge> g2 =
            new DefaultDirectedGraph<Integer, DefaultEdge>(
                DefaultEdge.class);
        gen4.generateGraph(g2, new IntegerVertexFactory(1), null); // start vertex from number 2

        Graph<Integer, DefaultEdge> g3 =
            new DefaultDirectedGraph<Integer, DefaultEdge>(
                DefaultEdge.class);
        gen4.generateGraph(g3, new IntegerVertexFactory(2), null); // start vertex from number 3

        // first assert all are isomorphic (if vertexChecker is not used)
        assertIsomorphic(
            new Graph[] {
                g1,
                g2
            },
            true);
        assertIsomorphic(
            new Graph[] {
                g2,
                g3
            },
            true);
        assertIsomorphic(
            new Graph[] {
                g1,
                g3
            },
            true);

        // create a functor according to odd even
        EquivalenceComparator vertexEqChecker = new OddEvenGroupComparator();
        assertIsomorphic(
            new Graph[] {
                g1,
                g2
            },
            false,
            vertexEqChecker,
            null);
        assertIsomorphic(
            new Graph[] {
                g2,
                g3
            },
            false,
            vertexEqChecker,
            null);
        assertIsomorphic(
            new Graph[] {
                g1,
                g3
            },
            true,
            vertexEqChecker,
            null);
    }

    /**
     * Passes an EdgeComparator, which compares according to odd-even edge
     * weight. The generated graphs are: A-(12)->B-(10)->C-(5)->D
     * A-(10)->B-(18)->C-(3)->D A-(11)->B-(10)->C-(5)->D (the first here is odd)
     *
     * @author Assaf
     * @since Aug 12, 2005
     */
    @SuppressWarnings("unchecked")
    public void testEdgeComparator()
    {
        int LINEAR_GRAPH_VERTEX_NUM = 4;
        Graph [] graphsArray = new DirectedGraph[3];
        Character [] charArray =
            new Character[] {
                new Character('A'),
                new Character('B'),
                new Character('C'),
                new Character('D')
            };
        int [][] weigthsArray =
            new int[][] {
                {
                    12,
                    10,
                    5
                },
                {
                    10,
                    18,
                    3
                },
                {
                    11,
                    10,
                    5
                }
            };

        for (int i = 0; i < graphsArray.length; i++) {
            Graph<Character, DefaultEdge> currGraph =
                graphsArray[i] =
                    new DefaultDirectedWeightedGraph<Character,
                        DefaultWeightedEdge>(
                        DefaultWeightedEdge.class);
            for (int j = 0; j < LINEAR_GRAPH_VERTEX_NUM; j++) {
                currGraph.addVertex(charArray[j]);
            }

            // create the 3 edges with weights
            for (int j = 0; j < 3; j++) {
                Graphs.addEdge(
                    currGraph,
                    charArray[j],
                    charArray[j + 1],
                    weigthsArray[i][j]);
            }
        }

        // first assert all are isomorphic (if vertexChecker is not used)
        assertIsomorphic(new Graph[] { graphsArray[0], graphsArray[1] },
            true);
        assertIsomorphic(new Graph[] { graphsArray[0], graphsArray[2] },
            true);
        assertIsomorphic(new Graph[] { graphsArray[1], graphsArray[2] },
            true);

        // create a functor according to odd even
        EquivalenceComparator edgeEqChecker =
            new DirectedEdgeWeightOddEvenComparator(graphsArray[0]);
        assertIsomorphic(
            new Graph[] { graphsArray[0], graphsArray[1] },
            true,
            null,
            edgeEqChecker);
        assertIsomorphic(
            new Graph[] { graphsArray[0], graphsArray[2] },
            false,
            null,
            edgeEqChecker);
        assertIsomorphic(
            new Graph[] { graphsArray[1], graphsArray[2] },
            false,
            null,
            edgeEqChecker);
    }

    @SuppressWarnings("unchecked")
    private void assertIsomorphicStopAfterFirstMatch(
        Graph [] graphs,
        boolean assertActive,
        boolean shouldTheyBeIsomorphic,
        EquivalenceComparator vertexChecker,
        EquivalenceComparator edgeChecker)
    {
        if (assertActive) {
            System.out.println("\nassertIsomorphic:"
                + shouldTheyBeIsomorphic);
        }
        Graph g1 = graphs[0];
        Graph g2 = graphs[1];
        System.out.println("g1:" + g1);
        System.out.println("g2:" + g2);
        long beforeTime = System.currentTimeMillis();
        GraphIsomorphismInspector iso =
            AdaptiveIsomorphismInspectorFactory.createIsomorphismInspector(
                g1,
                g2,
                vertexChecker,
                edgeChecker);
        boolean isoResult = iso.isIsomorphic();
        if (isoResult) {
            System.out.println("Graphs are isomorphic. ");
        } else {
            System.out.println("Graphs are NOT isomorphic.");
        }

        long deltaTime = System.currentTimeMillis() - beforeTime;
        String timeDesc;
        timeDesc =
            (deltaTime <= 10) ? "<10ms [less than minumun measurement time]"
            : String.valueOf(deltaTime);
        System.out.println(
            "# Performence: Isomorphism check in MiliSeconds:" + timeDesc);
        if (assertActive) {
            assertEquals(shouldTheyBeIsomorphic, isoResult);
        }
    }

    /**
     * Performance test with different number of vertex, edges. For each number
     * pair, 3 graphs are generated. The first two, using the same generator,
     * the third using a different generator. Note: the 1st and 2nd must be
     * isomorphic. The 3rd will most likely not be isomorphic , but on special
     * occasaions may be, so do not assert it. (example: empty graph, full mesh
     * , rare case that they are not real random). NOTE: RENAME TO testXXX to
     * make it work. It shows output and not assertions, so it cannot be used by
     * automatic tests.
     */
    @SuppressWarnings("unchecked")
    public void performanceTestOnRandomGraphs()
        throws Exception
    {
        final int [] numOfVertexesArray =
            new int[] {
                6,
                6,
                6,
                8,
                8,
                8,
                10,
                10,
                10,
                12,
                14,
                20,
                30,
                99
            };
        final int [] numOfEdgesArray =
            new int[] {
                0,
                4,
                12,
                1,
                15,
                54,
                0,
                40,
                90,
                130,
                50,
                79,
                30,
                234
            };

        // there will be two different generators. The first will be used for
        // 1st,2nd graph
        // the other for the3rd graph
        final int NUM_OF_GENERATORS = 2;
        RandomGraphGenerator [] genArray =
            new RandomGraphGenerator[NUM_OF_GENERATORS];

        String [] graphConctereClassesFullName =
            new String[] { // "org.jgrapht.graph.SimpleGraph" ,
                "org.jgrapht.graph.SimpleDirectedGraph",
                "org.jgrapht.graph.DefaultDirectedGraph",
                // "org.jgrapht.graph.Multigraph",
                // "org.jgrapht.graph.Pseudograph"
            };

        // 3 graphs. 1st,2nd must be isomorphic .3rd probably not iso.
        final int SIZE_OF_GENERATED_GRAPH_ARRAY = 3;

        // graphsArray rows are different graph types. Columns are few
        // instances of that type
        Graph [][] graphsArray =
            new Graph[graphConctereClassesFullName.length][SIZE_OF_GENERATED_GRAPH_ARRAY];

        Graph [] currIsoTestArray = new Graph[2];
        for (int testNum = 0; testNum < numOfVertexesArray.length; testNum++) {
            // recreate the graphs (empty)
            try {
                for (int i = 0; i < graphConctereClassesFullName.length; i++) {
                    for (int j = 0; j < SIZE_OF_GENERATED_GRAPH_ARRAY; j++) {
                        graphsArray[i][j] =
                            (Graph) Class.forName(
                                graphConctereClassesFullName[i]).newInstance();
                    }
                }
            } catch (Exception e) {
                throw new Exception("failed to initilized the graphs", e);
            }

            // create generators for the new vertex/edges number
            for (int i = 0; i < genArray.length; i++) {
                genArray[i] =
                    new RandomGraphGenerator(
                        numOfVertexesArray[testNum],
                        numOfEdgesArray[testNum]);
            }

            for (
                int graphType = 0;
                graphType < graphConctereClassesFullName.length;
                graphType++)
            {
                System.out.println(
                    "### numOfVertexes= " + numOfVertexesArray[testNum]);
                System.out.println(
                    "### numOfEdges= " + numOfEdgesArray[testNum]);
                System.out.println(
                    "######### Graph Type:"
                    + graphConctereClassesFullName[graphType]);
                System.out.println(
                    "# # # # # # # # # # # # # # # # # # # # # # # # # # # #");

                // 1st and 2nd from genArray[0]
                genArray[0].generateGraph(
                    graphsArray[graphType][0],
                    new IntegerVertexFactory(),
                    null);
                genArray[0].generateGraph(
                    graphsArray[graphType][1],
                    new IntegerVertexFactory(),
                    null);

                // 3rd from genArray[1]
                genArray[1].generateGraph(
                    graphsArray[graphType][2],
                    new IntegerVertexFactory(),
                    null);

                // now start testing
                currIsoTestArray[0] = graphsArray[graphType][0];
                currIsoTestArray[1] = graphsArray[graphType][1];

                assertIsomorphicStopAfterFirstMatch(
                    currIsoTestArray,
                    true,
                    true,
                    null,
                    null);

                // remember it is not a MUST - it can be true . DEGUG REASON
                // ONLY , and with care
                currIsoTestArray[1] = graphsArray[graphType][2];
                assertIsomorphicStopAfterFirstMatch(
                    currIsoTestArray,
                    false,
                    false,
                    null,
                    null);
            }
        }
    }
}

// End IsomorphismInspectorTest.java
