/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille
 * 
 * ludovic.apvrille AT enst.fr
 * 
 * This software is a computer program whose purpose is to allow the
 * edition of TURTLE analysis, design and deployment diagrams, to
 * allow the generation of RT-LOTOS or Java code from this diagram,
 * and at last to allow the analysis of formal validation traces
 * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP
 * from INRIA Rhone-Alpes.
 * 
 * This software is governed by the CeCILL  license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 * 
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 * 
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 */




package myutil;

import java.util.ArrayList;


/**
   * Class GraphAlgorithms
   *
   * Creation: 16/09/2004
   * @version 1.0 16/09/2004
   * @author Ludovic APVRILLE
 */
public class GraphAlgorithms {
    public static boolean go = true;


    // Assume that states are numbered from 0 to nbState - 1

    public static boolean hasCycle(Graph g){
        int nbState = g.getNbOfStates();
        int i,j;

        if (nbState == 0) {
            return false;
        }

        TraceManager.addDev("cycle0? Nb state = " + nbState + "\n");
        DijkstraState[] states = new DijkstraState[nbState];

        for(i=0; i<nbState; i++) {
            states[i] = new DijkstraState(i);
            states[i].previous = -1;
            states[i].used = false;
            states[i].weight = 0;
        }

        if (go == false) {
            return false;
        }

        TraceManager.addDev("cycle1? Nb state = " + nbState + "\n");
        // Succ
        int succ;
        for(i=0; i<nbState; i++) {
            /*if ((i % 100) == 0) {
              System.out.println("i=" + i);
              }*/
            for(j=0; j<nbState; j++) {
                succ = g.getWeightOfTransition(i, j);
                if (succ > 0) {
                    states[j].weight ++;
                }
            }
        }

        if (go == false) {
            return false;
        }

        int nb = 0;
        ArrayList<Integer> list = new ArrayList<Integer>();
        TraceManager.addDev("cycle2? Nb state = " + nbState+ "\n");
        for(i=0; i<nbState; i++) {
            if (states[i].weight == 0) {
                list.add(new Integer(i));
                nb ++;
            }
        }

        TraceManager.addDev("cycle3? Nb state = " + nbState +  " nb=" + nb + "\n");
        int index;
        while((list.size() > 0) && (go == true)){
            index = list.get(0).intValue();
            list.remove(0);
            for(j=0; j<nbState; j++) {
                succ = g.getWeightOfTransition(index, j);
                if (succ > 0) {
                    states[j].weight --;
                    if (states[j].weight == 0) {
                        list.add(new Integer(j));
                        nb ++;
                    }
                }
            }
        }

        TraceManager.addDev("cycle4? Nb state = " + nbState +  " nb=" + nb + "\n");

        return !(nb == nbState);

    }



    public static DijkstraState[] ShortestPathFrom(Graph g, int startState){

        int nbState = g.getNbOfStates();
        if (nbState == 0) {
            return null;
        }
        //System.out.println("Nb state = " + nbState + " startState = " + startState);
        DijkstraState[] states = new DijkstraState[nbState];
        int i;

        /* init of states */
        for(i=0; i<nbState; i++) {
            states[i] = new DijkstraState(i);
            states[i].previous = -1;
            states[i].used = false;
            states[i].weight = Integer.MAX_VALUE;
        }

        states[startState].used = false;
        states[startState].weight = 0;
        states[startState].previous = -1;

        if (go == true) {
            iterativeShortestPathFrom(g, states, startState);
        }



        if (go == true) {
            computePaths(states, startState);
        }


        if (go == false) {
            //System.out.println("Algorithm stopped");
            return null;
        }
        return states;
    }

    public static void iterativeShortestPathFrom(Graph g, DijkstraState[] states, int startState) {
        //DijkstraState ds;
        int weight;
        //int[] path;
        int currentState;

        while (((currentState = getNextStateToCompute(states, startState)) != -1) && (go == true)) {
            //System.out.println("State managed:" + currentState);
            states[currentState].used = true;
            for(int i=0; (i<states.length) && (go == true); i++) {
                weight = g.getWeightOfTransition(currentState, i);
                // adjacent state
                if (weight > 0) {
                    if (!states[i].used) {
                        if((states[currentState].weight + weight) < states[i].weight) {
                            states[i].weight = states[currentState].weight + weight;
                            states[i].previous = currentState;
                        }
                    }
                }
            }
            if (go == false) {
                return;
            }
        }
    }

    private static int getNextStateToCompute(DijkstraState[] states, int startState) {
        int minWeight = Integer.MAX_VALUE;
        int state = -1;
        for(int i=0; i<states.length; i++) {
            if (go == false) {
                return 0;
            }
            if ((!states[i].used) && (states[i].weight <= minWeight)) {
                state = i;
                minWeight = states[i].weight;
                //System.out.println("MinWeight = " + minWeight + " state=" + i);
            }
        }
        return state;
    }

    // Assumes no cycle!
    public static DijkstraState[] LongestPathFrom(Graph g, int startState){

        //System.out.println("Cycle detection");
        /*boolean result = hasCycle(g);
          if (result) {
          System.out.println("The graph contains cycles");
          return null;
          } else {
          System.out.println("The graph has no cycle");
          }*/


        int nbState = g.getNbOfStates();
        if (nbState == 0) {
            return null;
        }
        //System.out.println("Longest path Nb state = " + nbState + " startState = " + startState);
        DijkstraState[] states = new DijkstraState[nbState];
        int i;

        /* init of states */
        for(i=0; i<nbState; i++) {
            states[i] = new DijkstraState(i);
            states[i].previous = -1;
            states[i].used = false;
            states[i].weight = -1;
        }

        states[startState].used = false;
        states[startState].weight = 0;
        states[startState].previous = -1;

        if (go == true) {
            iterativeLongestPathFrom(g, states, startState);
        }



        if (go == true) {
            computePaths(states, startState);
        }


        if (go == false) {
            //System.out.println("Algorithm stopped");
            return null;
        }
        return states;
    }

    public static void iterativeLongestPathFrom(Graph g, DijkstraState[] states, int startState) {
        //DijkstraState ds;
        int weight;
        //int[] path;
        int currentState;
        int tmp;

        while (((currentState = getNextLongestStateToCompute(states, startState)) != -1) && (go == true)) {
            //System.out.println("State managed:" + currentState + " weight=" + states[currentState].weight);
            states[currentState].used = true;
            for(int i=0; (i<states.length) && (go == true); i++) {
                weight = g.getWeightOfTransition(currentState, i);
                // adjacent state
                if (weight > 0) {
                    //if (!states[i].used) {
                    if(states[currentState].weight + weight > states[i].weight) {
                        tmp =  states[i].weight;
                        //if (!states[i].used) {
                        states[i].weight = states[currentState].weight + weight;
                        states[i].previous = currentState;
                        states[i].used = false;
                        //System.out.println("Changing weight of state " + i + " former=" +  tmp + " new=" +states[i].weight);
                        //}
                    }
                    //}
                }
            }
            if (go == false) {
                return;
            }
        }
    }

    private static int getNextLongestStateToCompute(DijkstraState[] states, int startState) {
        int maxWeight = -2;
        int state = -1;
        for(int i=0; i<states.length; i++) {
            if (go == false) {
                return 0;
            }
            if ((!states[i].used) && (states[i].weight > maxWeight)) {
                state = i;
                maxWeight = states[i].weight;
                //System.out.println("MaxWeight = " + maxWeight + " state=" + i);
            }
        }
        return state;
    }

    private static void computePaths(DijkstraState[] states, int startState) {
        int size;
        int [] path;

        for(int i=0; (i<states.length) && (go == true); i++) {
            size = computePathLength(states, i, startState);
            //System.out.println("i = " + i + " size=" + size);
            if (size ==0) {
                states[i].path = new int[0];
            } else {
                path = new int[size+1];
                states[i].path = path;
                makePath(states, i, path, path.length - 1);
            }

        }
    }

    private static int computePathLength(DijkstraState[] states, int state, int startState) {
        //System.out.println("Compute path, state = " + state);
        if (go == false) {
            return 0;
        }

        if (state == startState) {
            return 0;
        }
        if (states[state].previous == -1) {
            return -1;
        } else {
            int ret = computePathLength(states, states[state].previous, startState);
            if (ret == -1)
                return -1;
            else
                return 1 + ret;
        }
    }

    private static void makePath(DijkstraState[] states, int state, int [] path, int index) {
        while(index>=0) {
            path[index] = state;
            index --;
            state = states[state].previous;
        }
    }

}