using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Eppstein
{
#region HelperClasses
///
/// Class for nodes in shortest path tree, with comparing capability
///
public class SP_Node : IComparable
{
///
/// Contained edge
///
public Edge Edge;
///
/// Weight of node (edge)
///
public int Weight;
///
/// Public constructor
///
/// Edge object
/// Weight of node
public SP_Node(Edge _edge, int _weight)
{
this.Edge = _edge;
this.Weight = _weight;
}
///
/// Implements IComparable's CompareTo method
/// Compare two SP_Node objects by it weight values
///
/// Object to compare with
/// -1 if this is shorter than object, 1 if countersense, 0 if both are equal
/// Thrown when obj is not SP_Node type
int IComparable.CompareTo(object _obj)
{
return Math.Sign(this.Weight - ((SP_Node)_obj).Weight);
}
///
/// Returns node's descriptive string
///
/// Edge and weight string
public override string ToString()
{
return /*this.Edge+*/" ["+Weight+"]";
}
}
///
/// Sidetracks collection, with comparing capability
///
public class ST_Node : IComparable
{
///
/// Collection of edges (sidetracks), not exactly a full path
///
public Path Sidetracks;
///
/// Weight of node (sum of sidetracks)
///
public int Weight;
///
/// Public constructor
///
/// Sidetrack collection
/// Weight is calculated using path's weight
public ST_Node(Path _sidetracks)
{
this.Sidetracks = _sidetracks;
this.Weight = _sidetracks.DeltaWeight;
}
///
/// Implements IComparable's CompareTo method
/// Compare two ST_Node objects by it weight values
///
/// Object to compare with
/// -1 if this is shorter than object, 1 if countersense, 0 if both are equal
/// Thrown when obj is not ST_Node type
int IComparable.CompareTo(object _obj)
{
return Math.Sign(this.Weight - ((ST_Node)_obj).Weight);
}
///
/// Returns node's descriptive string
///
/// Weight as string
public override string ToString()
{
return /*this.Sidetracks+*/" ["+Weight+"]";
}
}
#endregion
///
/// Contains a directed graph, composed by vertices and directed edges collections
///
public class Graph
{
#region Private fields
///
/// Linear array of vertices
///
private List Vertices;
///
/// Collections of Sidetracks in graph, ordered by total delta
///
private PriorityQueue PathsHeap;
///
/// Flag to indicate if shortest paths have been recalculated
///
private bool Ready;
///
/// Source vertex, is valid after main shortest path calculation
///
private Vertex SourceVertex;
///
/// Endpoint vertex, is valid after main shortest path calculation
///
private Vertex EndVertex;
#endregion
#region Public methods
///
/// Default constructor
///
public Graph()
{
Vertices = new List();
Ready = false;
}
///
/// Creates all vertices in graph by parsing a string, remove all previous vertices and edges on lists
///
/// String containing a comma-separated list with all vertex labels
/// True if all labels well parsed, false if not
/// A label must start with a letter and have all remain characters letters or digits
/// Repeated and empty labels with be ignored
public bool CreateVertices(string _vertices)
{
// Scans vertices' string, creates objects, and add to vertices' list
Ready = false; // must rebuild trees
Vertices.Clear(); // Remove all previous vertices
string[] vertexList = _vertices.ToUpper().Split(new char[] { ',' });
foreach (string _label in vertexList)
{
if (_label.Length == 0) // ignore empty labels
continue;
if (!char.IsLetter(_label, 0)) // label must start with a letter
return false;
foreach (char c in _label)
{
if (!char.IsLetterOrDigit(c)) // label just can contain letters and digits
return false;
}
if (GetVertex(_label)==null)
Vertices.Add(new Vertex(_label));
}
return true;
}
///
/// Creates directional edges
///
/// Comma-separated list of vertices for tails
/// Comma-separated list of vertices for heads
/// Weight for all edges
/// Group name for all edges
/// True if vertices are valid
/// Count of tails must match count of heads, all vertices must exist
public bool CreateEdges(string _tails, string _heads, int _weight, string _group)
{
List tails = this.ParseVertices(_tails.ToUpper());
List heads = this.ParseVertices(_heads.ToUpper());
string group = _group.ToUpper();
if (tails==null || heads==null)
return false;
if (tails.Count != heads.Count)
return false;
for (int i = 0; i < tails.Count; i++)
{
Edge e = new Edge(tails[i], heads[i], _weight, group);
tails[i].RelatedEdges.Add(e);
if (tails[i] != heads[i]) // Avoids duplicated reference for self-pointing edges
heads[i].RelatedEdges.Add(e);
}
Ready = false;
return true;
}
///
/// Change group weights
///
/// Group name of edges to be affected
/// New weights
public void EdgeGroupWeights(string _group, int _weight)
{
string group = _group.ToUpper();
foreach (Vertex _v in Vertices)
foreach (Edge _e in _v.RelatedEdges)
if (_e.Group == group)
_e.Weight = _weight;
this.Ready = false;
}
///
/// Calculates all shortest paths between s and t and returns the first one
///
/// Label of initial vertex
/// Label of ending vertex
/// Directional path if found, empty path if not or labels not valid
public Path FindShortestPath(string _s, string _t)
{
Ready = false;
// Parse start and end vertex strings
this.SourceVertex = this.GetVertex(_s.ToUpper());
this.EndVertex = this.GetVertex(_t.ToUpper());
if (SourceVertex == null || EndVertex == null)
return new Path(); // Invalid path
// Builds shortest path tree for all vertices to t, according to Dijkstra,
// storing distance to endpoint information on vertices, as described in Eppstein
BuildShortestPathTree();
// Fills a heap with all possible tracks from s to t, as described in Eppstein
// Paths are defined uniquely by sidetrack collections (edges not in shortest paths)
BuildSidetracksHeap();
// Flag to indicate that shortest paths have been calculated
Ready = true;
return FindNextShortestPath();
}
///
/// Recover next pre-calculated shortest path
///
/// Directional path if available, empty path if not remaining paths
public Path FindNextShortestPath()
{
if (!Ready)
return new Path(); // Invalid path
// Pick next track from heap, it is ordered from shortest path to longest
ST_Node node = this.PathsHeap.Dequeue();
if (node == null)
return new Path(); // Invalid path
// Returns path reconstructed from sidetracks
return RebuildPath(node.Sidetracks);
}
#endregion
#region Private methods
///
/// Parses a comma-separated list of vertices
///
/// Comma-separated list of vertex labels
/// List of vertices, null if some vertex does not exist
private List ParseVertices(string _labels)
{
string[] vertexList = _labels.ToUpper().Split(new char[] { ',' });
if (vertexList.Length == 0)
return null;
List result = new List();
foreach (string _label in vertexList)
{
Vertex v = this.GetVertex(_label); // Check if label exists
if (v==null)
return null;
result.Add(v);
}
return result;
}
///
/// Returns a reference for existing vertex
///
/// Label of vertex to search for
/// Vertex reference, null if not fount or empty label
private Vertex GetVertex(string _label)
{
if (string.IsNullOrEmpty(_label))
return null;
foreach (Vertex _v in Vertices)
{
if (_v.Equals(_label))
return _v;
}
return null;
}
///
/// Clears pointers to next edge in shortest path for all vertices
/// Clears distances to endpoint in shortet path vor all vertices
///
private void ResetGraphState()
{
foreach (Vertex _v in this.Vertices)
{
_v.EdgeToPath = null;
_v.Distance = int.MinValue;
}
}
///
/// Builds the shortest path tree using a priority queue for given vertex
///
/// Negative edges are ignored
private void BuildShortestPathTree()
{
ResetGraphState(); // Reset all distances to endpoint and previous shortest path
Vertex v = this.EndVertex;
v.Distance = 0; // Set distance to 0 for endpoint vertex
// Creates a fringe (queue) for storing edge pending to be processed
PriorityQueue fringe = new PriorityQueue(this.Vertices.Count);
// Main loop
do
{
if (v!=null)
foreach (Edge _e in v.RelatedEdges) // Evaluate all incoming edges
if (_e.Head==v && _e.Weight>=0) // Ignore negative edges
fringe.Enqueue(new SP_Node(_e, _e.Weight + _e.Head.Distance));
SP_Node node = fringe.Dequeue(); // Extracts next element in queue
if (node == null) // No pending edges to evaluate, finished
break;
Edge e = node.Edge;
v = e.Tail;
if (v.Distance == int.MinValue) // Vertex distance to endpoint not calculated yet
{
v.Distance = e.Weight + e.Head.Distance;
v.EdgeToPath = e;
}
else
v = null;
} while (true);
}
///
/// Creates all posible paths by describing only the sidetracks for each path
///
///
private void BuildSidetracksHeap()
{
this.PathsHeap = new PriorityQueue(Vertices.Count);
Path empty = new Path();
this.PathsHeap.Enqueue(new ST_Node(empty));
AddSidetracks(empty, this.SourceVertex);
}
///
/// Adds sidetracks recursively for specified vertex and new vertices in shortest path
///
/// Previous sidetrack collection
/// Vertex to evalueate
private void AddSidetracks(Path _p, Vertex _v)
{
foreach (Edge _e in _v.RelatedEdges)
{
if (_e.IsSidetrackOf(_v) && (_e.Head.EdgeToPath != null || _e.Head == this.EndVertex))
{
Path p = new Path();
p.AddRange(_p);
p.Add(_e);
this.PathsHeap.Enqueue(new ST_Node(p));
if (_e.Head != _v) // This avoids infinite cycling
AddSidetracks(p, _e.Head);
}
}
if (_v.Next != null)
AddSidetracks(_p, _v.Next);
}
///
/// Reconstructs path from sidetracks
///
/// Sidetracks collections for this path, could be empty for shortest
/// Full path reconstructed from s to t, crossing sidetracks
private Path RebuildPath(Path _sidetracks)
{
Path path = new Path();
Vertex v = this.SourceVertex;
int i = 0;
// Start from s, following shortest path or sidetracks
while (v != null)
{
// if current vertex is conected to next sidetrack, cross it
if (i < _sidetracks.Count && _sidetracks[i].Tail == v)
{
path.Add(_sidetracks[i]);
v = _sidetracks[i++].Head;
}
else // else continue walking on shortest path
{
if (v.EdgeToPath == null)
break;
path.Add(v.EdgeToPath);
v = v.Next;
}
}
return path;
}
#endregion
}
}