/* Copyright (c) 2001 by Kevin Forchione.  All Rights Reserved. */
/*
 *  TADS ADV.T/STD.T LIBRARY EXTENSION
 *  DIJKSTRA.T				
 *  Version 1.1
 *
 *      Djikstra's algorithm (named after its discover, E.W. Dijkstra)
 *      solves the problem of finding the shortest path from a point 
 *      in a graph (the source) to a destination. It turns out that one 
 *      can find the shortest paths from a given source to all points 
 *      in a graph in the same time, hence this problem is sometimes 
 *      called the single-source shortest paths problem. 
 *      
 *      This problem is related to the spanning tree one. The graph 
 *      representing all the paths from one vertex to all the others 
 *      must be a spanning tree - it must include all vertices. There 
 *      will also be no cycles as a cycle would define more than one 
 *      path from the selected vertex to at least one other vertex.
 *
 *----------------------------------------------------------------------
 *  REQUIREMENTS
 *
 *      + HTML TADS 2.2.6 or later
 *      + Should be #included after ADV.T and STD.T 
 *
 *----------------------------------------------------------------------
 *  IMPORTANT LIBRARY INTERFACE AND MODIFICATION
 *
 *      + none
 *
 *----------------------------------------------------------------------
 *  COPYRIGHT NOTICE
 *
 *  	You may modify and use this file in any way you want, provided that
 *		if you redistribute modified copies of this file in source form, the
 *   	copies must include the original copyright notice (including this
 *   	paragraph), and must be clearly marked as modified from the original
 *   	version.
 *
 *------------------------------------------------------------------------------
 *  REVISION HISTORY
 *
 *		21-Jan-01:	Creation.
 *		08-Feb-01:  Changed MAX_VALUE to INFINITY and added logic
 *				    to handle cases where (a+b)mod INFINITY < (a+b).
 */

#ifndef _DIJKSTRA_T_
#define _DIJKSTRA_T_

#pragma C+

#define INFINITY	0x7fffffff

class Entry: object
    vertex      	    = nil
    known       	    = nil
    predecessor 	    = nil
    value       	    = nil
    adjacentEntryList   = []

    instantiate( v, p, n ) = {
        local e = new Entry;

        e.vertex        = v;
        e.predecessor   = p;
        e.value         = n;

        return e;
    }
;

class Path: object
    entryList       	    = []
    shortestPathEntryList   = []
    entryListPreloaded	    = nil

    /* the vertex of the initial entry object */
    startingVertex  	= nil

    /*
     *  We loop through the dijkstra algorithm until there are no more
     *  unknown vertices. This approach attempts to avoid stack
     *  overflow due to recursion.
     */
    main = {
	    while( self.dijkstra ) {}
    }

    dijkstra = {
        local i, v, w, len, adjEntryList, edgeValue;

        /*
         *  From the set of vertices for which (e.known == nil), select
         *  the vertex having the smallest tentative value.
         */
         v = self.getMinUnknownEntry;

        /*
         * If we have no unknown vertices we are done.
         */
        if ( v == nil )
            return nil;

        /*
         *  Set vertex v.known to true.
         */
        v.known = true;

	    if ( entryListPreloaded )
	        /*
	         *  The entry list has been pre-loaded at instantiation,
	         *  we already have the vertex's adjacentEntryList. Simply
	         *  retrieve it.
	         */
	        adjEntryList = v.adjacentEntryList;
	    else
	        /*
	         *  We are building the path entry list with each iteration
	         *  of dijkstra(). Build the entry's adjacent entry list for
	         *  this vertex.
	         */
	        adjEntryList = self.buildAdjacentEntryList( v );

        /*
         *  For each vertex w adjacent to v for which (w.known == nil),
         *  test whether the tentative value w.value > v.value + c(v,w).
         *  If it is, set w.value = v.value + c(v,w) and set
         *  w.predecessor = v.
         */
         len = length( adjEntryList );
         for ( i = 1; i <= len; ++i )
         {
            w = adjEntryList[ i ];

            if ( w.known == nil )
            {
		    local tot;

                edgeValue = self.computeEdge( v, w );
	
		    tot = v.value + edgeValue;

		    /*
		     *  If tot is smaller than either the 
		     *  v.value or the edgeValue then we
		     *  really want tot to be INFINITY.
		     */
		    if ( tot < v.value && tot < edgeValue )
		        tot = INFINITY;

                if ( w.value > tot )
                {
                    w.value = tot;
                    w.predecessor = v;
                }
            }
         }

         /* continue with the next iteration of the algorithm */
         return true;
    }

    /*
     *  Retrieve the entry with known attribute == nil that has
     *  the minimum value.
     */
    getMinUnknownEntry = {
        local i, len, s, unknownList = [];

        /* make a list of all unknown entries */
        len = length( self.entryList );
        for ( i = 1; i <= len; ++i )
        {
            if ( self.entryList[ i ].known == nil )
            {
                unknownList += self.entryList[ i ];
            }
        }

        /* if the list is empty we signal that we are done */
        len = length( unknownList );
        if ( len == 0 )
            return nil;

        /*
         *  Retrieve the first entry from the unknown list that
         *  has the smallest value.
         */
        s = unknownList[ 1 ];
        for ( i = 2; i <= len; ++i )
        {
            if ( unknownList[ i ].value < s.value )
            {
                s = unknownList[ i ];
            }
        }

        return s;
    }

    /*
     *  Method loads entries to the path entry list. First the
     *  list is searched by vertex. If a match is found then we
     *  simply return the entry. If no match is found we instantiate
     *  an entry, add it to the list, then return the new entry.
     */
    addToEntryList( v ) = {
	    local e;

	    /*
	     *  If we don't find an entry for this vertex,
	     *  instantiate an entry for it and add the entry
	     *  to the path entry list.
	     */
	    e = self.getEntryByVertex( v );
	    if ( e == nil )
	    {
            e = Entry.instantiate( v, nil, INFINITY );
            self.entryList += e;
	    }

	    /* return the entry */
	    return e;
    }

    /*
     *  Search the path entry list for the entry matching
     *  this vertex. If we don't find an entry for this vertex
     *  return nil.
     */
    getEntryByVertex( v, ... ) = {
	    local i, e, len, eList = self.entryList;

	    if ( argcount == 2 ) eList = getarg(2);

        len = length( eList );
        for ( i = 1; i <= len; ++i )
        {
            if ( eList[ i ].vertex == v )
            {
                e = eList[ i ];
                break;
            }
        }

	    return e;
    }

    /*
     *  Builds the entry's adjacent entry list.
     */
    buildAdjacentEntryList( e ) = {
        local i, len, vList;

        /* We call the vertex to retrieve its adjacent vertices */
        vList = e.vertex.getAdjacentVertices;

	    /*
         *  If the vertex doesn't return an adjacent vertex
         *  list or the method is not defined for the vertex
         *  then we use an empty list.
         */
        if ( datatype( vList ) != DTY_LIST )
            vList = [];

        len = length( vList );
        for ( i = 1; i <= len; ++i )
        {
            /*
             *  We get the corresponding entry for each vertex of
             *  the adjacency list. If the entry does not already
             *  exist in the path entry list then addToEntryList()
             *  will create a new entry and add it to the path
	         *  entry list.
             */
            e.adjacentEntryList += self.addToEntryList( vList[ i ] );
        }

	    return e.adjacentEntryList;
    }

    /*
     *  Provide a value for the edge between vertices v and w.
     *  The default is to assume that the edge is unweighted.
     */
    computeEdge( v, w ) = {
        return 1;
    }

    /*
     *  Retrieve only the entries whose values are less than
     *  INFINITY. When called after main() this produces
     *  a list of entries for which the starting vertex has
     *  a path.
     */
    getNonMaxValues = {
        local i, e, len, nonMaxList = [];

        len = length( self.entryList );
        for ( i = 1; i <= len; ++i )
        {
            e = self.entryList[ i ];
            if ( e.value != INFINITY )
            {
                nonMaxList += e;
            }
        }

        return nonMaxList;
    }

    /*
     *  An abstract method, this should be called on the Path class.
     *  Produces an entry list that indicates the shortest path from
     *  s to v.
     */
    shortestPathEntries( s, v, ... ) = {
	    local path, e, eList, tmp, lst = [], pArg;

	    /* create a path entry list for s */
        if ( argcount == 3 )
            pArg = getarg(3);
    
        switch( datatype( pArg ) )
        {
            case DTY_LIST:
                path = Path.instantiate( s, getarg(3) );
                break;
                
            case DTY_OBJECT:
                if ( isclass( pArg, Path ) )
                {
                    path = pArg;
                    break;
                }
                
            default:
	            path = Path.instantiate( s );
        }
        
	    path.main;

	    /* remove any entries not reachable by s */
	    eList = path.getNonMaxValues;

	    /* get the entry for v using our modified list */
	    e = self.getEntryByVertex( v, eList );

	    /* build the list in order from s to v */
	    while( e != nil )
	    {
	        tmp = lst;
	        lst = [ e ] + tmp;
	        e = e.predecessor;
	    }
	    
	    path.shortestPathEntryList = lst;
	    
	    return path;
    }

    /*
     *  An abstract method, this should be called on the Path class.
     *  Produces a vertex list that indicates the shortest path from
     *  s to v.
     */
    shortestPathVertices( s, v, ... ) = {
	    local i, e, len, eList, vList = [], path;
	    
	    /* build the shortest path entry list from s to v */
	    if ( argcount == 3 )
	        path = self.shortestPathEntries( s, v, getarg(3) );
	    else
	        path = self.shortestPathEntries( s, v );
	        
	    eList = path.shortestPathEntryList;

	    /* build the vertex list from the entry list */
	    len = length( eList );
	    for ( i = 1; i <= len; ++i )
	    {
	        e = eList[ i ];
	        vList += e.vertex;
	    }

	    /* clean up path and entry objects */
	    delete path;

	    return vList;
    }

    instantiate( s, ... ) = {
        local i, len, p, e;

        /* create a new path */
        p = new Path;

        /* set the path starting vertex */
        p.startingVertex = s;

        /*
         *  The addToEntryList() method will create a new
         *  default entry and add it to the path entry list.
         */
        e = p.addToEntryList( s );

        /* Indicate that this is the path starting entry */
        e.value = 0;

        /*
         *  If we've been passed a vertex list then we 'pre-load'
         *  the path entry list before dijkstra processing.
         */
	    if ( argcount == 2 ) {
	        local vList = getarg( 2 );

            /* build the adjcent entry list for our initial entry */
		    p.buildAdjacentEntryList( e );
		    
             /* remove any occurence of s from vList */
            vList -= s;

            len = length( vList );
            for ( i = 1; i <= len; ++i )
            {
                /*
                 *  The addToEntryList() method will create a
                 *  new entry or retrieve an existing one. If
                 *  the entry is new then it is added to the
                 *  path entry list and its adjacent entry list
                 *  is set up.
                 */
                e = p.addToEntryList( vList[ i ] );
		        p.buildAdjacentEntryList( e );
            }
            p.entryListPreloaded = true;
	    }

        return p;
    }

    /* clean up the entries */
    destruct = {
        local i, o, len;

        len = length( self.entryList );
        for ( i = 1; i <= len; ++i )
        {
            o = self.entryList[ i ];
            delete o;
        }
    }
;

#pragma C-

#endif  /* _DIJKSTRA_T_ */
