/*
 **********************************************************************
 *
 *      CLP(R) Version 2.0	(Example Programs Release)
 *	(C) Copyright, March 1986, Monash University
 *
 **********************************************************************
 */

%
% cpm critical path routine
% Network is an input project network of the form
%	[ [node1 , node2, time ] .... ]
%	Graph is the critical path graph produced
%	Latest is the latest possible completion time is specified
% cpm/3 is used if the latest time is specified
% otherwise use cpm/2
%

% Explanation of output
/*
		Node	Es	Lc (Gives the Earliest Start time and Latest Completion
						time for the particular node)
Node1	Node2	T	Ls	Ec	Tf	Ff
(Details the times relating to the activity between Node1 & Node2
	T is the time required for the activity
	Ls the Latest Start time
	Ec the Earliest Completion time
	Tf the Total Float
	Ff the Free Float)
 Activities on the critical path are marked with an asterix
 The start node and end node are computed automatically and distinguished
*/

%	Sample output
/*
		Node	Es	Lc
Node1	Node2	T	Ls	Ec	Tf	Ff
--------------------------------------------------
START NODE	n1	0	0
--------------------------------------------------
n1	n2	3	2	3	2	0
n1	n3	2	0	2	0	0 *
--------------------------------------------------
		n2	3	5
--------------------------------------------------
n2	n4	2	5	5	2	2
--------------------------------------------------
		n3	2	2
--------------------------------------------------
n3	n4	5	2	7	0	0 *
--------------------------------------------------
END NODE	n4	7	7
*/

cpm(Network,Graph,Latest) :-
	build(Network,Graph),
	early_late(Graph,Graph,End,Latest),
	Latest >= End,
	analyse(Graph,Graph).
	
cpm(Network,Graph) :-
	build(Network,Graph),
	early_late(Graph,Graph,End),
	analyse(Graph,Graph).

% build an adjacency graph out of the network
build([],Graph) :-
	buildv([],_,Graph).
build([[I,J,C]|T],Graph) :-
	buildv(ed(I,J,C),to,Graph),
	buildv(ed(I,J,C),from,Graph),
	build(T,Graph).

buildv([],_,[]) :- !.
buildv([],_,[ad(_,_,_,To,From)|T]) :-
	!,addedg([],_,To),
	addedg([],_,From),
	buildv([],_,T).
buildv(ed(I,J,C),to,[ad(I,Es,Lc,To,From)|T]) :-
	!,addedg(J,C,To).
buildv(Edge,to,[H|T]) :-
	!,buildv(Edge,to,T).
buildv(ed(I,J,C),from,[ad(J,Es,Lc,To,From)|T]) :-
	!,addedg(I,C,From).
buildv(Edge,from,[H|T]) :-
	!,buildv(Edge,from,T).

addedg([],_,[]) :- !.
addedg([],_,[H|T]) :- !,
	addedg([],_,T).
addedg(V,C,[ed(V,C,_,_,_,_)|T]) :- !.
addedg(V,C,[H|T]) :-
	addedg(V,C,T).

% Get early start times and latest completion times
% early/4 is used when a ending time is given
% otherwise early/3 assumes that the early start time
% for the end node is equal to the latest completion time
early_late([],_,_,_).
early_late([ad(I,Es,Lc,To,From)|T],G,End,Latest) :-
	setearly(From,To,G,End,Es),
	setlate(To,G,Latest,Lc),
	early_late(T,G,End,Latest).

early_late([],_,_).
early_late([ad(I,Es,Lc,To,From)|T],G,End) :-
	setearly(From,To,G,End,Es),
	setlate(To,G,End,Lc),
	early_late(T,G,End).

setearly([],_,_,_,0).
setearly([ed(V,C,_,_,_,_)|T],[],G,Es,Es) :-
	!,
	getnode(V,G,Es1,_),
	setmax(T,G,Es1+C,Es).
setearly([ed(V,C,_,_,_,_)|T],_,G,End,Es) :-
	getnode(V,G,Es1,_),
	setmax(T,G,Es1+C,Es).

setmax([],_,Max,Max).
setmax([ed(V,C,_,_,_,_)|T],G,Max0,Max) :-
	getnode(V,G,Es1,_),
	setmax(T,G,max(Max0,Es1+C),Max).

setlate([],_,Last,Last).
setlate([ed(V,C,_,_,_,_)|T],G,Last,Lc) :-
	getnode(V,G,_,Lc1),
	setmin(T,G,Lc1-C,Lc).

setmin([],_,Min,Min).
setmin([ed(V,C,_,_,_,_)|T],G,Min0,Min) :-
	getnode(V,G,_,Lc1),
	setmin(T,G,min(Min0,Lc1-C),Min).

% Search graph for the early & late times for a node
getnode(I,[ad(I,Es,Lc,_,_)|T],Es,Lc).
getnode(I,[H|T],Es,Lc) :-
	getnode(I,T,Es,Lc).

% Compute the other times :
%		Ls - latest start time
%		Ec - earliest completion time
%		Tf - total float time
%		Ff - free float time
analyse([],G).
analyse([ad(I,Es,Lc,To,_)|T],G) :-
	analyse_times(To,Es,Lc,G),
	analyse(T,G).

analyse_times([],_,_,_).
analyse_times([ed(V,C,Ls,Ec,Tf,Ff)|T],Esi,Lci,G) :-
	getnode(V,G,Esj,Lcj),
	compute(Ls,Ec,Tf,Ff,Esj,Lcj,Esi,Lci,C),
	analyse_times(T,Esi,Lci,G).

% Indirect way of doing the calculation just to speed things up
% can be removed and inserted directly in analyse_times
compute(Ls,Ec,Tf,Ff,Esj,Lcj,Esi,Lci,C) :-
	X = Esi+C,
	Ls = Lcj-C,
	Ec = Esi+C,
	Tf = Lcj-X,
	Ff = Esj-X.

% display routines
print_analysis(G) :-
	printf("\t\tNode\tEs\tLc\n",[]),
	printf("Node1\tNode2\tT\tLs\tEc\tTf\tFf\n",[]),
	print_analysis1(G).
print_analysis1([]).
print_analysis1([H|T]) :- 
	print_node(H),
	print_analysis1(T).
print_node(ad(I,Es,Lc,[],From)) :-
	!,	
	printf("--------------------------------------------------\n",[]),
	printf("END NODE\t%\t%\t%\n",[I,Es,Lc]).
print_node(ad(I,Es,Lc,To,[])) :-
	!,
	printf("--------------------------------------------------\n",[]),
	printf("START NODE\t%\t%\t%\n",[I,Es,Lc]),
	printf("--------------------------------------------------\n",[]),
	print_times(To,I).
print_node(ad(I,Es,Lc,To,From)) :-
	printf("--------------------------------------------------\n",[]),
	printf("\t\t%\t%\t%\n",[I,Es,Lc]),
	printf("--------------------------------------------------\n",[]),
	print_times(To,I).

print_times([],_).
print_times([ed(V,C,Ls,Ec,Tf,Ff)|T],I) :-
	printf("%\t%\t%\t%\t%\t%\t%",[I,V,C,Ls,Ec,Tf,Ff]),
	is_critical(Tf),
	print_times(T,I).

is_critical(0) :-
	printf(" *\n",[]).
is_critical(Tf) :-
	Tf > 0,
	printf("\n",[]).

go1 :-  cpm([
		[n1,n2,3],[n1,n3,2],[n3,n4,5],[n2,n4,2]],G),
	print_analysis(G).

% Answer:
%                  Node    Es      Lc
%  Node1   Node2   T       Ls      Ec      Tf      Ff
%  --------------------------------------------------
%  START NODE      n1      0       0
%  --------------------------------------------------
%  n1      n2      3       2       3       2       0
%  n1      n3      2       0       2       0       0 *
%  --------------------------------------------------
%                  n2      3       5
%  --------------------------------------------------
%  n2      n4      2       5       5       2       2
%  --------------------------------------------------
%                  n3      2       2
%  --------------------------------------------------
%  n3      n4      5       2       7       0       0 *
%  --------------------------------------------------
%  END NODE        n4      7       7

go2 :-	cpm([	
		[n5,n6,9],[n5,n7,5],[n6,n7,0],[n4,n7,4],
		[n1,n2,2],[n2,n3,0],[n1,n3,6],[n1,n4,3],
		[n2,n5,1],[n2,n6,4],[n3,n5,2],[n4,n5,0],[n4,n8,3],
		[n6,n8,4],[n7,n8,6]],G),
	print_analysis(G).

% Answer:
%                  Node    Es      Lc
%  Node1   Node2   T       Ls      Ec      Tf      Ff
%  --------------------------------------------------
%                  n5      8       8
%  --------------------------------------------------
%  n5      n6      9       8       17      0       0 *
%  n5      n7      5       12      13      4       4
%  --------------------------------------------------
%                  n6      17      17
%  --------------------------------------------------
%  n6      n7      0       17      17      0       0 *
%  n6      n8      4       19      21      2       2
%  --------------------------------------------------
%                  n7      17      17
%  --------------------------------------------------
%  n7      n8      6       17      23      0       0 *
%  --------------------------------------------------
%                  n4      3       8
%  --------------------------------------------------
%  n4      n7      4       13      7       10      10
%  n4      n5      0       8       3       5       5
%  n4      n8      3       20      6       17      17
%  --------------------------------------------------
%  START NODE      n1      0       0
%  --------------------------------------------------
%  n1      n2      2       4       2       4       0
%  n1      n3      6       0       6       0       0 *
%  n1      n4      3       5       3       5       0
%  --------------------------------------------------
%                  n2      2       6
%  --------------------------------------------------
%  n2      n3      0       6       2       4       4
%  n2      n5      1       7       3       5       5
%  n2      n6      4       13      6       11      11
%  --------------------------------------------------
%                  n3      6       6
%  --------------------------------------------------
%  n3      n5      2       6       8       0       0 *
%  --------------------------------------------------
%  END NODE        n8      23      23

go3 :-
	cpm([
		[n1,n2,4],[n1,n3,3],[n1,n4,4], [n2,n5,7],[n2,n3,1],[n2,n7,8],
		[n3,n5,4], [n4,n6,2], [n5,n6,1],[n5,n7,3], [n6,n7,4]],G),
	print_analysis(G).

% Answer:
%                  Node    Es      Lc
%  Node1   Node2   T       Ls      Ec      Tf      Ff
%  --------------------------------------------------
%  START NODE      n1      0       0
%  --------------------------------------------------
%  n1      n2      4       0       4       0       0 *
%  n1      n3      3       4       3       4       2
%  n1      n4      4       6       4       6       0
%  --------------------------------------------------
%                  n2      4       4
%  --------------------------------------------------
%  n2      n5      7       4       11      0       0 *
%  n2      n3      1       6       5       2       0
%  n2      n7      8       8       12      4       4
%  --------------------------------------------------
%                  n3      5       7
%  --------------------------------------------------
%  n3      n5      4       7       9       2       2
%  --------------------------------------------------
%                  n4      4       10
%  --------------------------------------------------
%  n4      n6      2       10      6       6       6
%  --------------------------------------------------
%                  n5      11      11
%  --------------------------------------------------
%  n5      n6      1       11      12      0       0 *
%  n5      n7      3       13      14      2       2
%  --------------------------------------------------
%  END NODE        n7      16      16
%  --------------------------------------------------
%                  n6      12      12
%  --------------------------------------------------
%  n6      n7      4       12      16      0       0 *

?- printf("\n>>> Sample goals: go1/0, go2/0, go3/0\n", []).