package FlexiGraph;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

import gr.forth.ics.graph.Direction;
import gr.forth.ics.graph.Edge;
import gr.forth.ics.graph.Graph;
import gr.forth.ics.graph.Node;
import gr.forth.ics.graph.PrimaryGraph;
import gr.forth.ics.graph.SecondaryGraph;
import gr.forth.ics.graph.path.Path;
import gr.forth.ics.graph.path.Traverser;
import gr.forth.ics.graph.path.Traverser.PathIterator;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 *
 * @author martin
 */
public class Flexigraph {
    
    public Flexigraph() {
    }
    
    @BeforeClass
    public static void setUpClass() {
    }
    
    @AfterClass
    public static void tearDownClass() {
    }
    
    @Before
    public void setUp() {
    }
    
    @After
    public void tearDown() {
    }
    public static class point{
        int x,y;

        public point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    
    public static class tile{
        String name;

        public tile(String name) {
            this.name = name;
        }
        
    }
    String[] tags = new String[]{"un","deux","trois","quatre","cinq","six","sept","huit","neuf"};
    
    @Test
    public void tile80(){
        Graph graph = new PrimaryGraph();
                
         long a = System.currentTimeMillis();
         for (int x=0;x<100;x+=3)
            for (int y=0;y<100;y+=3){
                point p = new point(x,y);
                tile t = new tile("t"+x+"_"+y);
                Node n = graph.newNode(t);
                graph.newEdge(graph.newNode(p), n);
                for (String s : tags)
                    graph.newEdge(graph.newNode(s), n);
            }
         long b = System.currentTimeMillis();        
         
         for (String s : tags)
             if (graph.containsNode(graph.newNode(s)))
                 for (Node n : graph.adjacentNodes(graph.newNode(s)))
                 {}
         
        long c = System.currentTimeMillis();   
         
         System.out.println("build : "+(b-a)+"ms");
         System.out.println("find by tag : "+(c-b)+"ms");
    }

    
     @Test
     public void bench() {
         Graph graph = new PrimaryGraph();
         
         long a = System.currentTimeMillis();
         List<Node> li = new ArrayList<>();
         for (int i=0;i<1000;i++){
             li.add(graph.newNode(i));
         }
         
         long b = System.currentTimeMillis();
         List<Node> ls = new ArrayList<>();
         for (int i=1000;i<2000;i++){
             ls.add(graph.newNode(Integer.toString(i)));
         }
         
         long c = System.currentTimeMillis();
         for (Node s : ls)
             for (Node i : li)
                 graph.newEdge(s, i);
                  
         long d = System.currentTimeMillis();
         for (Node s : ls)
             for (Node i : graph.adjacentNodes(s)){}
         
         long f = System.currentTimeMillis();
         for (Node s : ls)
             graph.newEdge(s,graph.newNode("chien"));
         
         long g = System.currentTimeMillis();
         for (Node s : ls)
             for (Node i : graph.adjacentNodes(s))
                 if (i.equals(i))
                     graph.removeNode(i);
         
         long h = System.currentTimeMillis();
         
         System.out.println("insert "+ls.size()+" int : "+(b-a)+"ms");
         System.out.println("insert "+li.size()+" str : "+(c-b)+"ms");
         System.out.println("link all : "+(d-c)+"ms");
         System.out.println("iterate adjacent of all int : "+(f-d)+"ms");
         System.out.println("for all int insert chien : "+(g-f)+"ms");
         System.out.println("find all chien and destroy : "+(h-g)+"ms");
         
     }
    
    
    
    @Test
    public void simple() {
        Graph graph = new PrimaryGraph();
        Node n1 = graph.newNode("1");
        Node n2 = graph.newNode("2");
        
        Edge e = graph.newEdge(n1, n2);
    }
    
    @Test
    public void alladjacentnode(){
        Graph g = new PrimaryGraph();
        Node n1 = g.newNode("1");
        Node n2 = g.newNode("2");
        Node n3 = g.newNode("3");
        Edge e12 = g.newEdge(n1, n2);
        Edge e23 = g.newEdge(n2, n3);
        Edge e31 = g.newEdge(n3, n1);
        
//        System.out.println(g.adjacentNodes(n1)); //prints [2, 3]
//        assertEquals("[1-->0<--1-->2-->3]",n1);
//        System.out.println(g.adjacentNodes(n2)); //prints [1, 3]
//        assertEquals("[1-->0<--1-->2-->3]",n2);
//        System.out.println(g.adjacentNodes(n3)); //prints [1, 2]
//        assertEquals("[1-->0<--1-->2-->3]",n3);
//        
//        System.out.println(g.edges(n1)); //prints [{1, 2}, {3, 1}]
//        assertEquals("[1-->0<--1-->2-->3]",p10123);
//        System.out.println(g.edges(n2)); //prints [{2, 3}, {1, 2}]
//        assertEquals("[1-->0<--1-->2-->3]",p10123);
//        System.out.println(g.edges(n3)); //prints [{3, 1}, {2, 3}]
//        assertEquals("[1-->0<--1-->2-->3]",p10123);
        
        List<Node> nodesOutOfN2 = g.adjacentNodes(n2, Direction.OUT).drainToList();
//        System.out.println(nodesOutOfN2); //prints [3]
//        assertEquals("[3]",nodesOutOfN2);
        
        Set<Node> nodesTowardsN2 = g.adjacentNodes(n2, Direction.IN).drainToSet();
//        System.out.println(nodesTowardsN2); //prints [1]
//        assertEquals("[1]",nodesTowardsN2);
        
        //draining to a list or set was not required, just demostrating the ability
        
//        System.out.println("e31 incident to e12? " + e31.isIncident(e12)); //prints true
//        assertEquals("e31 incident to e12? ",e31.isIncident(e12));
        
        Edge e33 = g.newEdge(n3, n3);
//        System.out.println("e12 incident to e33? " + e12.isIncident(e33)); //prints false
//        assertEquals("e12 incident to e33? ",e12.isIncident(e33));
        
        Node other = e12.opposite(n1); //other == n2
        
//        System.out.println("Is e33 self loop? " + e33.isSelfLoop());
//        assertEquals("Is e33 self loop? ",e33.isSelfLoop());
    }
    
    @Test
    public void findallpath(){
        Graph graph = new PrimaryGraph();
        Node n1 = graph.newNode("1");
        Node n2 = graph.newNode("2");
        Node n3 = graph.newNode("3");
        Node n4 = graph.newNode("4");

        graph.newEdge(n1, n2);
        graph.newEdge(n1, n3);
        graph.newEdge(n1, n4);
        graph.newEdge(n2, n3);
        graph.newEdge(n2, n4);
        graph.newEdge(n3, n4);

        Traverser traverser = Traverser.newDfs().build();
        Set<Path> paths = new HashSet<Path>();
        for (Node n : graph.nodes()) {
            PathIterator iterator = traverser.traverse(graph, n, Direction.OUT).iterator();
            while (iterator.hasNext()) {
                Path path = iterator.next();
                if (paths.contains(path)) {
                    iterator.skipExplorationOfLastPath(); //discontinue the exploration of already visited path
                } else {
                    paths.add(path);
                }
            }
        }  
    }
    
    @Test
    public void sharing(){
        Graph g1 = new PrimaryGraph();
//        System.out.println(g1.isPrimary()); //prints true
        
        Edge e = g1.newEdge(g1.newNode("1"), g1.newNode("2"));
        
        SecondaryGraph g2 = new SecondaryGraph();
//        System.out.println(g2.isPrimary()); //prints false
        assertEquals(false,g2.isPrimary());
        
        //graph elements can be freely shared in secondary graphs, but they can
        //be at most contained in _one_ primary graph. Primary graphs are less
        //flexible, but faster.
        g2.adoptEdge(e); //also adopts the edge's nodes
        
        System.out.println(g2.edgeCount()); //prints 1
        System.out.println(g2.nodeCount()); //prints 2
        assertEquals(1,g2.edgeCount());
        assertEquals(2,g2.nodeCount());
        
        g2.removeEdge(e); //removes edge, but nodes remain
        
        Edge stillExists = g1.anEdge(); //the edge still exists in the first graph, though removed from the second
        
        g1.newNodes(100); //create some nodes in first graph
        
        g2.adoptGraph(g1); //all those nodes are adopted in the second graph
        
//            System.out.println(g2.nodeCount()); //prints 102
        assertEquals(102,g2.nodeCount());
    }
    
    @Test
    public void loopovernodeandedge(){
                Graph g = new PrimaryGraph();
        g.newNodes("1", "2", "3", "4", 999);
        
        for (Node n : g.nodes()) {
//            System.out.println(n); //prints 1, 2, etc
            //assertEquals("1",n);
        }
        
        Node n1 = g.newNode("n1");
        Node n2 = g.newNode("n2");
        Edge edge1 = g.newEdge(n1, n2);
        Edge edge3 = g.newEdge(n1, n1);
        Edge edge2 = g.newEdge(n2, n1);
        Edge edge4 = g.newEdge(n2, n2);
        
        for (Edge edge : g.edges()) {
            //System.out.println(edge); //prints {n1, n2}, {n1, n1}, etc
//        assertEquals("[1-->0<--1-->2-->3]",edge);
        }
    }
    
    @Test
    public void path(){
                Graph g = new PrimaryGraph();
        Node[] n = g.newNodes(0, 1, 2, 3);
        Edge e01 = g.newEdge(n[0], n[1]);
        Edge e12 = g.newEdge(n[1], n[2]);
        Edge e23 = g.newEdge(n[2], n[3]);

        Path p1 = n[1].asPath(); //[1]
        Path p12 = p1.append(e12.asPath()); //[1-->2]
        Path p123 = p12.append(e23.asPath()); //[1-->2-->3]

        Path p10 = p1.append(e01.asPath(e01.n2())); //[1<--0]
        //p10.headNode() == n[1]
        //p10.tailNode() == n[0]

        Path p101 = p10.append(e01.asPath()); //[1<--0-->1]

        //replaces [1] with [1<--0-->1]
        Path p10123 = p123.replaceFirst(p1, p101);
        
//        System.out.println(p10123); //prints [1-->0<--1-->2-->3]
//        assertEquals("[1-->0<--1-->2-->3]",p10123);
    }
    
    @Test
    public void tuple(){
                Graph g = new PrimaryGraph();
        Node n1 = g.newNode();
        
        n1.put("key1", new Integer(10));
        int x = n1.getInt("key1"); //x == 10
        
        Edge e = g.newEdge(n1, n1);
        e.put("key2", "value2");
        
        String value = e.getString("key2");
        String alias = (String)e.get("key2"); //value == alias == "value2"
        
        g.tuple().put("key", "value");
        
        Object o = g.tuple().remove("key"); //o == "value"
    }
    
    
    private static class ExpensiveObject {}
    public void weakTuple(){
                Graph g = new PrimaryGraph();
        
        Node n = g.newNode();
        
        Object key = new Object(); //key
        n.putWeakly(key, new ExpensiveObject());
        
        //the above entry remains as long as the key is reachable
        
        key = null; //now the key object becomes unreachable
        //thus the garbage collector will eventually clean up the above entry
        
        //as another example, consider the following
        Node[] nodes = g.newNodes(100); //creates a hundred nodes
        key = new Object();
        for (Node node : nodes) {
            node.putWeakly(key, new Object()); //create arbitrary entries
        }
        
        //now, if we would like to clean up all the created entries, the following suffices:
        key = null;
        //i.e. it is not necessary to loop over each node and manually call node.remove(key);

    }
}
