Sei sulla pagina 1di 26

# Algorithm of Dijkstra

## function Dijkstra(Graph, source):

for each vertex v in Graph: // Initializations
dist[v] := infinity // Unknown distance function from source to v
previous[v] := undefined // Previous node in optimal path from source
dist[source] := 0 // Distance from source to source
Q := the set of all nodes in Graph
// All nodes in the graph are unoptimized - thus are in Q
while Q is not empty: // The main loop
u := vertex in Q with smallest dist[]
if dist[u] = infinity:
break // all remaining vertices are inaccessible
remove u from Q
for each neighbor v of u: // where v has not yet been removed from Q.
alt := dist[u] + dist_between(u, v)
if alt < dist[v]: // Relax (u,v,a)
dist[v] := alt
previous[v] := u
return previous[]

## Algorithm for Bandwidth Minimization

for I = 1 to restart_times do
initialize (labeling)
for j-1 to NC_times do
NC Labeling
If j mod2 = 1then
HC (Labeling)
End if
End for
End for

Algorithm of NS Code

## class CIFQ : public Queue {

public:
CIFQ();
virtual int command(int argc, const char*const* argv);
Packet *deque(void);
void enque(Packet *pkt);

private:
Packet *wf2q_deque(void);
void wf2q_enque(Packet *pkt);
int active(int flowid); /* judge the flow is active or not */
void leave(int flowid); /* when an empty and non-leading session
leave,modify other active session's lag */
double sumw(void);
/* sum of the weights of the active session */
int cansend(int flowid); /* determine whether packet of flow flowid can
be send */
protected:
/* flow structure */
struct flowState {
PacketQueue q_; /* packet queue associated to the corresponding flow
*/
int qmaxSize_; /* maximum queue size (in bytes) */
int qcrtSize_; /* current queue size (in bytes) */
double weight_; /* Weight of the flow */
double S_; /* Starting time of flow , not checked for
wraparound*/
double F_; /* Ending time of flow, not checked for wraparound
*/
double lag_; /* identify the difference between real sys and
preference sys */
int rate_; /*uniform error rate*/
RandomVariable *ranvar_; /* the underlying random variate generator*/
} fs_[MAXFLOWS];

## double V; /* Virtual time , not checked for wraparound!*/

LinkDelay* link_; /* To get the txt time of a packet */
};

## static class CIFQClass : public TclClass {

public:
CIFQClass() : TclClass("Queue/CIFQ") {}
TclObject* create(int argc, const char*const* argv) {
return (new CIFQ);
}
} class_cifq;

CIFQ::CIFQ()
{
/* initialize flow's structure */
for (int i = 0; i < MAXFLOWS; ++i) {
fs_[i].qmaxSize_ = DEF_QUEUE_SIZE;
fs_[i].qcrtSize_ = 0;
fs_[i].weight_ = DEF_FLOW_WEIGHT;
fs_[i].S_ = 0.0;
fs_[i].F_ = 0.0;
fs_[i].lag_ = 0.0;
//fs_[i].send_ = 1;
fs_[i].ranvar_ =NULL;
}
V = 0.0;
}

/*
* entry points from OTcL to set per flow state variables
* - \$q set-queue-size flow_id flow_queue_size
* - \$q set-flow-weight flow_id flow_weight
* - \$q ranvar flowid [new RandomVariable/Uniform] rate
*
* NOTE: \$q represents the discipline queue variable in OTcl.
*/

## int CIFQ::command(int argc, const char*const* argv)

{
//Tcl& tcl = Tcl::instance();
if (argc == 4) {
if (strcmp(argv, "set-queue-size") == 0) {
int flowId = atoi(argv);
if (flowId >= MAXFLOWS) {
fprintf(stderr, "CIFQ: Flow id=%d too large; it should be < %d!\n",
abort }
fs_[flowId].qmaxSize_ = atoi(argv);
return (TCL_OK);
}
else if (strcmp(argv, "set-flow-weight") == 0) {
int flowId = atoi(argv);
if (flowId >= MAXFLOWS) {
fprintf(stderr, "CIFQ: Flow id=%d too large; it should be < %d!\n",
flowId, MAXFLOWS);
abort();
}
double flowweight = atof(argv);
if (flowweight <= 0) {
fprintf(stderr, "CIFQ: Flow Weight=%.4f > 0\n",
flowweight);
abort();
}
fs_[flowId].weight_ = flowweight;
return (TCL_OK);
}
}
if (argc==5) {
if (strcmp(argv, "ranvar") == 0) {
int flowId = atoi(argv);

## if (flowId >= MAXFLOWS) {

fprintf(stderr, "CIFQ: Flow id=%d too large; it should be < %d!\n",
flowId, MAXFLOWS);
abort();
}
fs_[flowId].ranvar_ = (RandomVariable*) TclObject::lookup(argv);
fs_[flowId].rate_ =atoi(argv);
return (TCL_OK);
}
}
if (argc == 3){
if (strcmp(argv, "link") == 0) {
if (del == 0) {
fprintf(stderr, "CIFQ: no LinkDelay object %s\n",
argv);
return(TCL_ERROR);
}
// set ptc now
return (TCL_OK);
}
}
return (Queue::command(argc, argv));
}

/*
* identify the flow state
*
*/
int CIFQ::active(int flowid)
{
if
(fs_[flowid].qcrtSize_||(!fs_[flowid].qcrtSize_&&fs_[flowid].lag_<0.0))
return 1;
else
return 0;
}

/*
* when an empty and non-leading session leave,modify other active
session's lag
*
*/
void CIFQ::leave(int flowid)
{
printf("ok");
int m;

for (m=0;m<MAXFLOWS;m++)
{
if (active(m))

{ fs_[m].lag_+=fs_[flowid].lag_*fs_[flowid].weight_/sumw();
if (!fs_[m].qcrtSize_&&fs_[m].lag_>=0.0)
leave(m);
}
}
}

/*
*calculate the sum of the weights of the active session,return the sum
*
*/
double CIFQ::sumw(void)
{
double sum=0.0;
int n;
for (n=0;n<MAXFLOWS;n++)
{
if (active(n))
sum+=fs_[n].weight_;
}
return sum;
}

/*
*Determine wether a flow can send data packet
*/
int CIFQ::cansend(int flowid)
{
// if no random var is specified, assume uniform random variable
double u = fs_[flowid].ranvar_ ? fs_[flowid].ranvar_->value() :
Random::uniform();
return (u >= fs_[flowid].rate_);
}

/*
*
*
*/
void CIFQ::enque(Packet *pkt)
{
wf2q_enque(pkt);
}

## void CIFQ::wf2q_enque(Packet* pkt)

{
hdr_cmn* hdr = hdr_cmn::access(pkt);
hdr_ip* hip = hdr_ip::access(pkt);
int flowId = hip->flowid();
int pktSize = hdr->size();

## if (flowId >= MAXFLOWS) {

fprintf(stderr, "CIFQ::enqueue-Flow id=%d too large; it should be <
%d!\n",
flowId, MAXFLOWS);
drop(pkt);
}

## /* if buffer full, drop the packet; else enqueue it */

if (fs_[flowId].qcrtSize_ + pktSize > fs_[flowId].qmaxSize_) {

## /* If the queue is not large enough for this packet */

drop(pkt);

} else {
if (!fs_[flowId].qcrtSize_) {

/* If queue for the flow is empty, calculate start and finish times
*/

## fs_[flowId].S_ = max(V, fs_[flowId].F_);

fs_[flowId].F_ = fs_[flowId].S_ +
((double)pktSize/fs_[flowId].weight_);
/* the weight_ parameter better not be 0! */
/* update system virutal clock */

## double minS = fs_[flowId].S_;

for (int i = 0; i < MAXFLOWS; ++i) {
if (active(i)) // original wf2q:if (fs_[i].qcrtSize_)
if (fs_[i].S_ < minS)
minS = fs_[i].S_;
}

V = max(minS, V);
}

fs_[flowId].q_.enque(pkt);
fs_[flowId].qcrtSize_ += pktSize;
}
}
/*
* Dequeue the packet.
*/
Packet* CIFQ::deque()
{
Packet *pkt = NULL, *nextPkt;
Packet *pktj=NULL, *nextPktj;
int i,j;
int pktSize;
int pktjSize;
double minF = MAXDOUBLE;
int flow = -1;
int maxlagflow= -1;
double W = 0.0;
double maxlag;
/* look for the candidate flow with the earliest finish time */
for (i = 0; i< MAXFLOWS; i++){
if (!active(i))
continue;
if (fs_[i].S_ <= V)
if (fs_[i].F_ < minF){
flow = i;
minF = fs_[i].F_;
}
}

## if (flow == -1 || minF == MAXDOUBLE)

return (pkt);

if (fs_[flow].lag_>=0.0&&cansend(flow))
{
pkt = fs_[flow].q_.deque();
pktSize = ((hdr_cmn*)hdr_cmn::access(pkt))->size();
fs_[flow].qcrtSize_ -= pktSize;

/* Set the start and the finish times of the remaining packets in the
* queue */

if (nextPkt) {
fs_[flow].S_ = fs_[flow].F_;
fs_[flow].F_ = fs_[flow].S_ +
((((hdr_cmn*)hdr_cmn::access(nextPkt))->size())/fs_[flow].weight_);
/* the weight parameter better not be 0 */
}

## /* update the virtual clock */

double minS = fs_[flow].S_;
for (i = 0; i < MAXFLOWS; ++i) {
W += fs_[i].weight_;
if (fs_[i].qcrtSize_)
if (fs_[i].S_ < minS)
minS = fs_[i].S_;
}
V = max(minS, (V + ((double)pktSize/W)));
if (fs_[flow].lag_>=0.0&&!fs_[flow].qcrtSize_)
leave (flow);
return(pkt);
}
else
/*search for the session that lag most and active and can send*/
{ j=0;
while(j<MAXFLOWS&&active(j)&&cansend(j))
{ j=j+1;}
if (j<MAXFLOWS)/*there has such flow that active and can send*/
{maxlag=fs_[j].lag_/fs_[j].weight_;
maxlagflow=j;
for (;j<MAXFLOWS;j++)
{
if (active(j)&&cansend(j))
{if(maxlag<fs_[j].lag_/fs_[j].weight_)
{maxlag=fs_[j].lag_/fs_[j].weight_;
maxlagflow=j;
}
}
}
pktj=fs_[maxlagflow].q_.deque();
pktjSize = ((hdr_cmn*)hdr_cmn::access(pktj))->size();
fs_[maxlagflow].qcrtSize_ -= pktSize;
pkt = fs_[flow].q_.deque();
pktSize = ((hdr_cmn*)hdr_cmn::access(pkt))->size();
fs_[flow].lag_+=pktSize ;
fs_[maxlagflow].lag_-=pktjSize;

## /* update the virtual clock */

double minS = fs_[flow].S_;
for (i = 0; i < MAXFLOWS; ++i) {
W += fs_[i].weight_;
if (fs_[i].qcrtSize_)
if (fs_[i].S_ < minS)
minS = fs_[i].S_;
}
V = max(minS, (V + ((double)pktSize/W)));

if (!fs_[maxlagflow].qcrtSize_&&fs_[maxlagflow].lag_>+0)
leave(maxlagflow);
return(pktj);
}
else/*there is no active session ready to send*/
{
/* Set the start and the finish times of the session i */

fs_[flow].S_ = fs_[flow].F_;
fs_[flow].F_ = fs_[flow].S_ + (DELTA/fs_[flow].weight_);
/* the weight parameter better not be 0 */

## /* update the virtual clock */

double minS = fs_[flow].S_;
for (i = 0; i < MAXFLOWS; ++i) {
W += fs_[i].weight_;
if (fs_[i].qcrtSize_)
if (fs_[i].S_ < minS)
minS = fs_[i].S_;
}
V = max(minS, (V + (DELTA/W)));
pkt = fs_[flow].q_.deque();
pktSize = ((hdr_cmn*)hdr_cmn::access(pkt))->size();
if (fs_[flow].lag_<0.0&&!pktSize)/*flow is leading and unbacklogged*/
/*search for session that lag most and active*/
j=0;
while(j<MAXFLOWS&&active(j))
{ j=j+1;}
if (j<MAXFLOWS)/*there has such flow that active */
{ maxlag=fs_[j].lag_/fs_[j].weight_;
maxlagflow=j;
for (;j<MAXFLOWS;j++)
{
if (active(j))
{if(maxlag<fs_[j].lag_/fs_[j].weight_)
{maxlag=fs_[j].lag_/fs_[j].weight_;
maxlagflow=j;
} }}
fs_[flow].lag_+=DELTA;
fs_[maxlagflow].lag_-=DELTA;
//set_time_out(); /*delay time of DELTA/R ,call deque
again*/
}}}
if (fs_[flow].lag_>=0.0&&!fs_[flow].qcrtSize_)
leave (flow);
}

Force-based algorithms

## set up initial node velocities to (0,0)

set up initial node positions randomly // make sure no 2 nodes are in exactly the
same position
loop
total_kinetic_energy := 0 // running sum of total kinetic energy over all particles
for each node
net-force := (0, 0) // running sum of total force on this particular node

## for each other node

net-force := net-force + Coulomb_repulsion( this_node, other_node )
next node

## for each spring connected to this node

net-force := net-force + Hooke_attraction( this_node, spring )
next spring

## // without damping, it moves forever

this_node.velocity := (this_node.velocity + timestep * net-force) * damping
this_node.position := this_node.position + timestep * this_node.velocity
total_kinetic_energy := total_kinetic_energy + this_node.mass *
(this_node.velocity)^2
next node
until total_kinetic_energy is less than some small number //the simulation has
stopped moving

## maximum flow Edmonds-Karp algorithm

algorithm EdmondsKarp
input:
C[1..n, 1..n] (Capacity matrix)
E[1..n, 1..?] (Neighbour lists)
s (Source)
t (Sink)
output:
f (Value of maximum flow)
F (A matrix giving a legal flow with the maximum value)
f := 0 (Initial flow is zero)
F := array(1..n, 1..n) (Residual capacity from u to v is C[u,v] - F[u,v])
forever
m, P := BreadthFirstSearch(C, E, s, t)
if m = 0
break
f := f + m
(Backtrack search, and write flow)
v := t
while v ≠ s
u := P[v]
F[u,v] := F[u,v] + m
F[v,u] := F[v,u] - m
v := u
return (f, F)

input:
C, E, s, t
output:
M[t] (Capacity of path found)
P (Parent table)
P := array(1..n)
for u in 1..n
P[u] := -1
P[s] := -2 (make sure source is not rediscovered)
M := array(1..n) (Capacity of found path to node)
M[s] := ∞
Q := queue()
Q.push(s)
while Q.size() > 0
u := Q.pop()
for v in E[u]
(If there is available capacity, and v is not seen before in search)
if C[u,v] - F[u,v] > 0 and P[v] = -1
P[v] := u
M[v] := min(M[u], C[u,v] - F[u,v])
if v ≠ t
Q.push(v)
else
return M[t], P
return 0, P
Bellman-Ford algorithm
procedure BellmanFord(list vertices, list edges, vertex source)
// This implementation takes in a graph, represented as lists of vertices
// and edges, and modifies the vertices so that their distance and
// predecessor attributes store the shortest paths.

## // Step 1: Initialize graph

for each vertex v in vertices:
if v is source then v.distance := 0
else v.distance := infinity
v.predecessor := null

## // Step 2: relax edges repeatedly

for i from 1 to size(vertices)-1:
for each edge uv in edges: // uv is the edge from u to v
u := uv.source
v := uv.destination
if v.distance > u.distance + uv.weight:
v.distance := u.distance + uv.weight
v.predecessor := u

## // Step 3: check for negative-weight cycles

for each edge uv in edges:
u := uv.source
v := uv.destination
if v.distance > u.distance + uv.weight:
error "Graph contains a negative-weight cycle"

## Algorithm after n duplicate ACKs

The pseudo code of the algorithm is the following:
ssthresh = (BWE * RTTmin)/seg_size;
if (cwin > ssthresh) /* congestion avoid */
cwin = ssthresh;
endif
endif

## Algorithm after coarse timeout expiration

The pseudo code of the algorithm is:

## if (coarse timeout expires)

ssthresh = (BWE * RTTmin)/seg_size;
if (ssthresh < 2)
ssthresh = 2;
endif;
cwin = 1;
endif

## 5.1 TEST CASES

Next phase in the project development was testing. Testing
phase basically involved two types of testing, namely α - testing and
β - testing.
α–testing comprised of extensive testing by the team members.
All the modules and code segments were extensively tested for errors.
The errors found during this phase of testing are listed below. The
errors can be broadly classified into 2 categories namely,
• Planning errors
• Designing errors
• Coding errors
Black Box Testing
Black box testing relates to tests that are performed at the
software interface. Although they are designed to identify errors, black
box tests are used to demonstrate that software functions are
operational; that inputs are correctly accepted and the output is
correctly produced.

## White Box Testing

White box testing is test case design approach that employs the
control architecture of the procedural design to produce test cases.
Test case can be derived such that they:
 Guarantee that all independent paths within a module have been
exercised at least once
 Exercise all logical decisions on their true and false sides,
 Execute all loops at their boundaries and within their operational
bounds and
 Exercise internal data structures to ensure their validity.
Unit Testing
This test focuses verification effort on the small unit of design
module. Here using test plans prepared in design descriptions as
guide, important control paths are tested to uncover errors within
boundary of the module.

## Boundary condition are tested to ensure module operate

properly at boundaries established to limit or restrict processing. All
paths in the control structure are exercised to ensure all statements in
a module are executed at least once.

## 5.2 – TEST DATASET

Planning Errors:
Planning errors are the errors which occurred during the
planning and requirement analysis phase of project development.
These errors came into attention when the ideas developed during the
planning phase were actually implemented in coding. These errors
included
1. Error in Database design: During coding it was discovered that
some of the tables in the database didn’t have all the attributes
needed to implement some of the functionalities of the project.
The database tables were then subsequently modified.
2. Error in code planning: There were some code planning errors
like applying transactions at every place where database
updating or insertion was made. These transactions were later
inserted at proper places.

Designing Errors
Designing errors are the errors which occurred in the designing
phase of the project development. These errors included
1. Error in E-R diagram design: Some aspects of E-R diagram
design were missed during the design phase. These errors
cropped up when this E-R Diagram was actually implemented as
the database in Access 2003.
2. Error in module designing: There were some inherent errors in
dividing the entire project into user modules. The modules had
to be redesigned once the group started working on the
modules.

Coding Errors
The major part of errors encountered during the project
designing belonged to this phase of project development. This phase
encountered a vast array of errors ranging from simple coding
mistakes to big logical blunders. The errors can be listed as
1. Error in Database connectivity: This error was frequently
encountered when the code was migrated from one terminal to
another. The root cause of this error was the difference in server
names at the respective terminals.
2. Error in the implementation of website security: This was
realized when the values were posted between different forms
and they were visible in the query string. This was resolved by
encrypting this secretive information and by avoiding passing
these values wherever possible.
3. Error in the usage of web controls: In some portions of our
code there was error in selection of the type of control i.e.
dropdown list, textboxes, data grids, etc. We used ASP controls
where web controls had to be used.
4. Error in usage of application variables: Error was
encountered when we tried to create application variables and
embed them in our functionality.
5. Error in keeping track of user sessions: We faced errors
initially while trying to keep track of the different users visiting
the website. This error was dealt with by creating a session table
and session variables and using them as and when required.
6. Error in date range validation: Since this part of the code
involved intensive usage of nested “if” and “while” loops it
became tedious to work with this part of the code to achieve the
desired end results.
7. Error in implementing session logout: We encountered a
problem in which even after a user logged out he could access
the sensitive content of the website.

5.3

## 5.3 ACCEPTANCE TESTING

User Acceptance Testing
User acceptance of a system is the key factor for the success of
any system. This is done with regard to following points:
 Input screen design.
 Output screen design.
 On-line guide to the user.
 Format of output, both on the screen and on the hard copy.

## 5.4 TESTING RESULT

The database structure and the software developed in this
project were put to testing to check the proper functioning of different
queries in the software. All the known problems were looked into and
rectified. This testing was performed with the hypothetical data.

Algo complete

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;

namespace VisualIntelligentScissors
{
/// <summary>
/// Implements a generalized Dijkstra's algorithm to calculate
/// both minimum distance and minimum path.
/// </summary>
/// <remarks>
/// For this algorithm, all nodes should be provided, and handled
/// in the delegate methods, including the start and finish nodes.
/// </remarks>
public class Dijkstra
{
/// <summary>
/// An optional delegate that can help optimize the algorithm
/// by showing it a subset of nodes to consider. Very useful
/// for limited connectivity graphs. (like pixels on a screen!)
/// </summary>
/// <param name="startingNode">
/// The node that is being traveled away FROM.
/// </param>
/// <returns>
/// An array of nodes that might be reached from the
/// <paramref name="startingNode"/>.
/// </returns>
public delegate IEnumerable<int> NearbyNodesHint(int startingNode);
/// <summary>
/// Determines the cost of moving from a given node to another given node.
/// </summary>
/// <param name="start">
/// The node being moved away from.
/// </param>
/// <param name="finish">
/// The node that may be moved to.
/// </param>
/// <returns>
/// The cost of the transition from <paramref name="start"/> to
/// <paramref name="finish"/>, or <see cref="Int32.MaxValue"/>
/// if the transition is impossible (i.e. there is no edge between
/// the two nodes).
/// </returns>
public delegate int InternodeTraversalCost(int start, int finish);

/// <summary>
/// Creates an instance of the <see cref="Dijkstra"/> class.
/// </summary>
/// <param name="totalNodeCount">
/// The total number of nodes in the graph.
/// </param>
/// <param name="traversalCost">
/// The delegate that can provide the cost of a transition between
/// any two nodes.
/// </param>
/// <param name="hint">
/// An optional delegate that can provide a small subset of nodes
/// that a given node may be connected to.
/// </param>
public Dijkstra(int totalNodeCount, InternodeTraversalCost traversalCost,
NearbyNodesHint hint)
{
if (totalNodeCount < 3) throw new ArgumentOutOfRangeException("totalNodeCount",
totalNodeCount, "Expected a minimum of 3.");
if (traversalCost == null) throw new ArgumentNullException("traversalCost");
Hint = hint;
TraversalCost = traversalCost;
TotalNodeCount = totalNodeCount;
}

/// <summary>
/// The composite product of a Dijkstra algorithm.
/// </summary>
public struct Results
{
/// <summary>
/// Prepares a Dijkstra results package.
/// </summary>
/// <param name="minimumPath">
/// The minimum path array, where each array element index corresponds
/// to a node designation, and the array element value is a pointer to
/// the node that should be used to travel to this one.
/// </param>
/// <param name="minimumDistance">
/// The minimum distance from the starting node to the given node.
/// </param>
public Results(int[] minimumPath, int[] minimumDistance)
{
MinimumDistance = minimumDistance;
MinimumPath = minimumPath;
}

/// <summary>
/// The minimum path array, where each array element index corresponds
/// to a node designation, and the array element value is a pointer to
/// the node that should be used to travel to this one.
/// </summary>
/// <summary>
/// The minimum distance from the starting node to the given node.
/// </summary>
}

/// <summary>
/// Performs the Dijkstra algorithm on the data provided when the
/// <see cref="Dijkstra"/> object was instantiated.
/// </summary>
/// <param name="start">
/// The node to use as a starting location.
/// </param>
/// <returns>
/// A struct containing both the minimum distance and minimum path
/// to every node from the given <paramref name="start"/> node.
/// </returns>
public virtual Results Perform(int start)
{
// Initialize the distance to every node from the starting node.
int[] d = GetStartingTraversalCost(start);
// Initialize best path to every node as from the starting node.
int[] p = GetStartingBestPath(start);
ICollection<int> c = GetChoices();

## c.Remove(start); // take starting node out of the list of choices

//Debug.WriteLine("Step v C D P");
//Debug.WriteLine(string.Format("init - {{{0}}} [{1}] [{2}]",
// ArrayToString<int>(",", c), ArrayToString<int>(",", d), ArrayToString<int>(",", p)));
//int step = 0;

## // begin greedy loop

while (c.Count > 1)
{
// Find element v in c, that minimizes d[v]
int v = FindMinimizingDinC(d, c);
c.Remove(v); // remove v from the list of future solutions
// Consider all unselected nodes and consider their cost from v.
foreach (int w in (Hint != null ? Hint(v) : c))
{
if (!c.Contains(w)) continue; // discard pixels not in c
// At this point, relative(Index) points to a candidate pixel,
// that has not yet been selected, and lies within our area of interest.
// Consider whether it is now within closer reach.
int cost = TraversalCost(v, w);
if (cost < int.MaxValue && d[v] + cost < d[w]) // don't let wrap-around negatives slip
by
{
// We have found a better way to get at relative
d[w] = d[v] + cost; // record new distance
// Record how we came to this new pixel
p[w] = v;
}
}
//Debug.WriteLine(string.Format("{4} {3} {{{0}}} [{1}] [{2}]",
// ArrayToString<int>(",", c), ArrayToString<int>(",", d), ArrayToString<int>(",", p), v
+ 1, ++step));
}

## return new Results(p, d);

}

/// <summary>
/// Uses the Dijkstra algorithhm to find the minimum path
/// from one node to another.
/// </summary>
/// <param name="start">
/// The node to use as a starting location.
/// </param>
/// <param name="finish">
/// The node to use as a finishing location.
/// </param>
/// <returns>
/// A struct containing both the minimum distance and minimum path
/// to every node from the given <paramref name="start"/> node.
/// </returns>
public virtual int[] GetMinimumPath(int start, int finish)
{
Results results = Perform(start);
return GetMinimumPath(start, finish, results.MinimumPath);
}

/// <summary>
/// Finds an array of nodes that provide the shortest path
/// from one given node to another.
/// </summary>
/// <param name="start">
/// The starting node.
/// </param>
/// <param name="finish">
/// The finishing node.
/// </param>
/// <param name="shortestPath">
/// The P array of the completed algorithm.
/// </param>
/// <returns>
/// The list of nodes that provide the one step at a time path
/// from <paramref name="start"/> to <paramref name="finish"/> nodes.
/// </returns>
protected virtual int[] GetMinimumPath(int start, int finish, int[] shortestPath)
{
Stack<int> path = new Stack<int>();
do
{
path.Push(finish);
finish = shortestPath[finish]; // step back one step toward the start point
}
while (finish != start);
return path.ToArray();
}

/// <summary>
/// Initializes the P array for the algorithm.
/// </summary>
/// <param name="startingNode">
/// The node that has been designated the starting node for the entire algorithm.
/// </param>
/// <returns>
/// The new P array.
/// </returns>
/// <remarks>
/// A fresh P array will set every single node's source node to be
/// the starting node, including the starting node itself.
/// </remarks>
protected virtual int[] GetStartingBestPath(int startingNode)
{
int[] p = new int[TotalNodeCount];
for (int i = 0; i < p.Length; i++)
p[i] = startingNode;
return p;
}

/// <summary>
/// Finds the yet-unconsidered node that has the least cost to reach.
/// </summary>
/// <param name="d">
/// The cost of reaching any node.
/// </param>
/// <param name="c">
/// The nodes that are still available for picking.
/// </param>
/// <returns>
/// The node that is closest (has the shortest special path).
/// </returns>
protected virtual int FindMinimizingDinC(int[] d, ICollection<int> c)
{
int bestIndex = -1;
foreach (int ci in c)
if (bestIndex == -1 || d[ci] < d[bestIndex])
bestIndex = ci;
return bestIndex;
}

/// <summary>
/// Initializes an collection of all nodes not yet considered.
/// </summary>
/// <returns>
/// The initialized collection.
/// </returns>
protected virtual ICollection<int> GetChoices()
{
ICollection<int> choices = new List<int>(TotalNodeCount);
for (int i = 0; i < TotalNodeCount; i++)
return choices;
}

/// <summary>
/// Initializes the D array for the start of the algorithm.
/// </summary>
/// <param name="start">
/// The starting node.
/// </param>
/// <returns>
/// The contents of the new D array.
/// </returns>
/// <remarks>
/// The traversal cost for every node will be set to impossible
/// (int.MaxValue) unless a connecting edge is found between the
/// <paramref name="start"/>ing node and the node in question.
/// </remarks>
protected virtual int[] GetStartingTraversalCost(int start)
{
int[] subset = new int[TotalNodeCount];
for (int i = 0; i < subset.Length; i++)
subset[i] = int.MaxValue; // all are unreachable
subset[start] = 0; // zero cost from start to start
foreach (int nearby in Hint(start))
subset[nearby] = TraversalCost(start, nearby);
return subset;
}

/// <summary>
/// Joins the elements of an array into a string, using
/// a given separator.
/// </summary>
/// <typeparam name="T">The type of element in the array.</typeparam>
/// <param name="separator">The seperator to insert between each element.</param>
/// <param name="array">The array.</param>
/// <returns>The resulting string.</returns>
/// <remarks>
/// This is very much like <see cref="string.Join"/>, except
/// that it works on arrays of non-strings.
/// </remarks>
protected string ArrayToString<T>(string separator, IEnumerable<int> array)
{
StringBuilder sb = new StringBuilder();
foreach (int t in array)
sb.AppendFormat("{0}{1}", t < int.MaxValue ? t + 1 : t, separator);
sb.Length -= separator.Length;
return sb.ToString();
}

}
}