CO205 Coursework: Abstract Data-Type Graph


AdtGraph.PAS


(*
	Graph ADT Module

	CO205 Coursework: GRAPHS
	Eamonn Martin (BSc Computing)
	ID: 96D59682
*)

UNIT ADTGraph;

INTERFACE

USES
	ADTStack;
CONST
	MAXNODENAME = 20;		(* Maximum length of a node label *)
	MAXLINKNAME = 20;		(* Maximum length of a link label *)
	NUMLINKNODE = 2;		(* Number of nodes in a link *)
TYPE
	nlab_t = Array[0..MAXNODENAME-1] Of Char; (* Node label type *)
	llab_t = Array[0..MAXLINKNAME-1] Of Char; (* Link label type *)

	nval_t = Integer;		(* Node value type (integer/real) *)
	lval_t = Integer;		(* Link value type (integer/real) *)

	NODEPTR = ^NODE_T;		(* Pointer to a node type *)
	LINKPTR = ^LINK_T;		(* Pointer to a link type *)
	GRAPHPTR = ^GRAPH_T;		(* Pointer to a graph type *)

	NODE_T = Record			(* Node data structure *)
	  next:  NODEPTR;		(* Next node *)
	  name:	 nlab_t;		(* Node label *)
	  value: nval_t			(* Node value *)
	End;

	LINK_T = Record			(* Link data structure *)
	  next:  LINKPTR;		(* Next link *)
	  name:  llab_t;		(* Link label *)
	  value: lval_t;		(* Link value *)
	  node:  Array[1..NUMLINKNODE] Of Integer (* Linked nodes *)
	End;

	GRAPH_T = Record		(* Graph ADT data type *)
	  node: NODE_T;			(* Root of node list *)
	  link: LINK_T			(* Root of link list *)
	End;


(* Set the name of node to name *)
PROCEDURE SetNodeName (node: NODEPTR; name: PChar);

(* Set the value of node to value *)
PROCEDURE SetNodeValue (node: NODEPTR; value: nval_t);

(* Set the next-node pointer in node to next *)
PROCEDURE SetNextNode (node, next: NODEPTR);

(* Get the name of node *)
FUNCTION GetNodeName (node: NODEPTR): PChar;

(* Get the value of node *)
FUNCTION GetNodeValue (node: NODEPTR): nval_t;

(* Return the next-node pointer in node *)
FUNCTION GetNextNode (node: NODEPTR): NODEPTR;

(* Set the name of link to name *)
PROCEDURE SetLinkName (link: LINKPTR; name: PChar);

(* Set the value of link to value *)
PROCEDURE SetLinkValue (link: LINKPTR; value: lval_t);

(* Set the value of node n in link to value *)
PROCEDURE SetLinkNode (link: LINKPTR; n, value: Integer);

(* Set the next-link pointer in link to next *)
PROCEDURE SetNextLink (link, next: LINKPTR);

(* Get the name of link *)
FUNCTION GetLinkName (link: LINKPTR): PChar;

(* Get the value of link *)
FUNCTION GetLinkValue (link: LINKPTR): lval_t;

(* Get the value of node n in link *)
FUNCTION GetLinkNode (link: LINKPTR; n: Integer): Integer;

(* Return the next-link pointer in link *)
FUNCTION GetNextLink (link: LINKPTR): LINKPTR;

(* Return the root of the node list in graph *)
FUNCTION RootNode (graph: GRAPHPTR): NODEPTR;

(* Return the root of the link list in graph *)
FUNCTION RootLink (graph: GRAPHPTR): LINKPTR;

(* Return the first node listed in graph *)
FUNCTION FirstNode (graph: GRAPHPTR): NODEPTR;

(* Return the last node in the list in graph *)
FUNCTION LastNode (graph: GRAPHPTR): NODEPTR;

(* Return the index of node name in graph (or -1 if not found) *)
FUNCTION NodeIndex (name: PChar; graph: GRAPHPTR): Integer;

(* Return a pointer to node number index in graph *)
FUNCTION NodeNumber (index: Integer; graph: GRAPHPTR): NODEPTR;

(* Return the first link listed in graph *)
FUNCTION FirstLink (graph: GRAPHPTR): LINKPTR;

(* Return the last link in the list in graph *)
FUNCTION LastLink (graph: GRAPHPTR): LINKPTR;

(* Return the index of link name in graph (or -1 if not found) *)
FUNCTION LinkIndex (name: PChar; graph: GRAPHPTR): Integer;

(* Return a pointer to link number index in graph *)
FUNCTION LinkNumber (index: Integer; graph: GRAPHPTR): LINKPTR;

(* Create a new node with name and value *)
FUNCTION MakeNode (name: PChar; value: nval_t): NODEPTR;

(* Append node to the node list in graph *)
PROCEDURE AddNode (node: NODEPTR; graph: GRAPHPTR);

(* Create a new link between node n1 and node n2 with name and value *)
FUNCTION MakeLink (name: PChar; value: lval_t; n1, n2: Integer): LINKPTR;

(* Append link to the link list in graph *)
PROCEDURE AddLink (link: LINKPTR; graph: GRAPHPTR);

(* Initialize graph ADT data structure *)
PROCEDURE InitGraphADT (graph: GRAPHPTR);

(* Free all nodes and links in graph *)
PROCEDURE FreeGraphADT (graph: GRAPHPTR);

(* If node n is in link then return the linked node, otherwise -1 *)
FUNCTION LinkedNode (n: Integer; link: LINKPTR): Integer;

(* Return the link between nodes n1 and n2 in graph (or Nil if not found) *)
FUNCTION NodeLink (n1, n2: Integer; graph: GRAPHPTR): LINKPTR;

(* Find the shortest path (by link values) between nodes n1 and n2 *)
(* The path is stored in stack and the path length (or -1) is returned *)
(* If stack is Nil the path is discarded and only the length is returned *)
FUNCTION GraphPath (n1, n2: Integer; stack: STACKPTR; graph: GRAPHPTR): lval_t;

(* Load next link from file f and add it to graph *)
FUNCTION LoadLink (graph: GRAPHPTR; VAR f: Text): Boolean;

(* Load and add all links in file fname to graph *)
FUNCTION LoadLinkFile (fname: PChar; graph: GRAPHPTR): Boolean;


IMPLEMENTATION

USES
	Strings;

(*** Graph ADT Basic Primitives: NODES ***)

(* Set the name of node to name *)
PROCEDURE SetNodeName (node: NODEPTR; name: PChar);
BEGIN
	StrCopy(node^.name, name)
END;

(* Set the value of node to value *)
PROCEDURE SetNodeValue (node: NODEPTR; value: nval_t);
BEGIN
	node^.value := value
END;

(* Set the next-node pointer in node to next *)
PROCEDURE SetNextNode (node, next: NODEPTR);
BEGIN
	node^.next := next
END;

(* Get the name of node *)
FUNCTION GetNodeName (node: NODEPTR): PChar;
BEGIN
	GetNodeName := node^.name
END;

(* Get the value of node *)
FUNCTION GetNodeValue (node: NODEPTR): nval_t;
BEGIN
	GetNodeValue := node^.value
END;

(* Return the next-node pointer in node *)
FUNCTION GetNextNode (node: NODEPTR): NODEPTR;
BEGIN
	GetNextNode := node^.next
END;


(*** Graph ADT Basic Primitives: LINKS ***)

(* Set the name of link to name *)
PROCEDURE SetLinkName (link: LINKPTR; name: PChar);
BEGIN
	StrCopy(link^.name, name)
END;

(* Set the value of link to value *)
PROCEDURE SetLinkValue (link: LINKPTR; value: lval_t);
BEGIN
	link^.value := value
END;

(* Set the value of node n in link to value *)
PROCEDURE SetLinkNode (link: LINKPTR; n, value: Integer);
BEGIN
	link^.node[n] := value
END;

(* Set the next-link pointer in link to next *)
PROCEDURE SetNextLink (link, next: LINKPTR);
BEGIN
	link^.next := next
END;

(* Get the name of link *)
FUNCTION GetLinkName (link: LINKPTR): PChar;
BEGIN
	GetLinkName := link^.name
END;

(* Get the value of link *)
FUNCTION GetLinkValue (link: LINKPTR): lval_t;
BEGIN
	GetLinkValue := link^.value
END;

(* Get the value of node n in link *)
FUNCTION GetLinkNode (link: LINKPTR; n: Integer): Integer;
BEGIN
	GetLinkNode := link^.node[n]
END;

(* Return the next-link pointer in link *)
FUNCTION GetNextLink (link: LINKPTR): LINKPTR;
BEGIN
	GetNextLink := link^.next
END;


(*** Graph ADT Basic Primitives: GRAPHS ***)

(* Return the root of the node list in graph *)
FUNCTION RootNode (graph: GRAPHPTR): NODEPTR;
BEGIN
	RootNode := @graph^.node
END;

(* Return the root of the link list in graph *)
FUNCTION RootLink (graph: GRAPHPTR): LINKPTR;
BEGIN
	RootLink := @graph^.link
END;


(*** Graph ADT Locator Functions: NODES ***)

(* Return the first node listed in graph *)
FUNCTION FirstNode (graph: GRAPHPTR): NODEPTR;
BEGIN
	FirstNode := GetNextNode(RootNode(graph))
END;

(* Return the last node in the list in graph *)
FUNCTION LastNode (graph: GRAPHPTR): NODEPTR;
 FUNCTION _LastNode (node: NODEPTR): NODEPTR;
 BEGIN
	IF GetNextNode(node)=Nil THEN _LastNode := node
	ELSE _LastNode := _LastNode(GetNextNode(node))
 END;
BEGIN
	LastNode := _LastNode(RootNode(graph))
END;

(* Return the index of node name in graph (or -1 if not found) *)
FUNCTION NodeIndex (name: PChar; graph: GRAPHPTR): Integer;
 FUNCTION _NodeIndex (node: NODEPTR; name: PChar; n: Integer): Integer;
 BEGIN
	IF node=Nil THEN _NodeIndex := -1
	ELSE IF StrIComp(name, GetNodeName(node))=0 THEN _NodeIndex := n
	ELSE _NodeIndex := _NodeIndex(GetNextNode(node), name, n+1)
 END;
BEGIN
	NodeIndex := _NodeIndex(FirstNode(graph), name, 0)
END;

(* Return a pointer to node number index in graph *)
FUNCTION NodeNumber (index: Integer; graph: GRAPHPTR): NODEPTR;
 FUNCTION _NodeNumber (index: Integer; node: NODEPTR): NODEPTR;
 BEGIN
	IF index=0 THEN _NodeNumber := node
	ELSE _NodeNumber := _NodeNumber(index-1, GetNextNode(node))
 END;
BEGIN
	NodeNumber := _NodeNumber(index, FirstNode(graph))
END;


(*** Graph ADT Locator Functions: LINKS ***)

(* Return the first link listed in graph *)
FUNCTION FirstLink (graph: GRAPHPTR): LINKPTR;
BEGIN
	FirstLink := GetNextLink(RootLink(graph))
END;

(* Return the last link in the list in graph *)
FUNCTION LastLink (graph: GRAPHPTR): LINKPTR;
 FUNCTION _LastLink (link: LINKPTR): LINKPTR;
 BEGIN
	IF GetNextLink(link)=Nil THEN _LastLink := link
	ELSE _LastLink := _LastLink(GetNextLink(link))
 END;
BEGIN
	LastLink := _LastLink(RootLink(graph))
END;

(* Return the index of link name in graph (or -1 if not found) *)
FUNCTION LinkIndex (name: PChar; graph: GRAPHPTR): Integer;
 FUNCTION _LinkIndex (link: LINKPTR; name: PChar; n: Integer): Integer;
 BEGIN
	IF link=Nil THEN _LinkIndex := -1
	ELSE IF StrIComp(name, GetLinkName(link))=0 THEN _LinkIndex := n
	ELSE _LinkIndex := _LinkIndex(GetNextLink(link), name, n+1)
 END;
BEGIN
	LinkIndex := _LinkIndex(FirstLink(graph), name, 0)
END;

(* Return a pointer to link number index in graph *)
FUNCTION LinkNumber (index: Integer; graph: GRAPHPTR): LINKPTR;
 FUNCTION _LinkNumber (index: Integer; link: LINKPTR): LINKPTR;
 BEGIN
	IF index=0 THEN _LinkNumber := link
	ELSE _LinkNumber := _LinkNumber(index-1, GetNextLink(link))
 END;
BEGIN
	LinkNumber := _LinkNumber(index, FirstLink(graph))
END;


(*** Graph ADT Constructor Functions ***)

(* Create a new node with name and value *)
FUNCTION MakeNode (name: PChar; value: nval_t): NODEPTR;
VAR
	node: NODEPTR;
BEGIN
	New(node);			(* Create a new node *)
	SetNextNode(node, Nil);		(* Initialize next-node pointer *)
	SetNodeName(node, name);	(* Set node name *)
	SetNodeValue(node, value);	(* Set node value *)
	MakeNode := node		(* Return node pointer *)
END;

(* Append node to the node list in graph *)
PROCEDURE AddNode (node: NODEPTR; graph: GRAPHPTR);
BEGIN
	SetNextNode(LastNode(graph), node)
END;

(* Create a new link between node n1 and node n2 with name and value *)
FUNCTION MakeLink (name: PChar; value: lval_t; n1, n2: Integer): LINKPTR;
VAR
	link: LINKPTR;
BEGIN
	New(link);			(* Create a new link *)
	SetNextLink(link, Nil);		(* Initialize next-link pointer *)
	SetLinkName(link, name);	(* Set link name *)
	SetLinkValue(link, value);	(* Set link value *)
	SetLinkNode(link, 1, n1);	(* Set first linked node *)
	SetLinkNode(link, 2, n2);	(* Set second linked node *)
	MakeLink := link		(* Return link pointer *)
END;

(* Append link to the link list in graph *)
PROCEDURE AddLink (link: LINKPTR; graph: GRAPHPTR);
BEGIN
	SetNextLink(LastLink(graph), link)
END;


(*** Graph ADT Initialize/Shutdown ***)

(* Initialize graph ADT data structure *)
PROCEDURE InitGraphADT (graph: GRAPHPTR);
BEGIN
	SetNextNode(RootNode(graph), Nil);	(* Initialize node list *)
	SetNextLink(RootLink(graph), Nil);	(* Initialize link list *)
END;

(* Free all nodes and links in graph *)
PROCEDURE FreeGraphADT (graph: GRAPHPTR);
 PROCEDURE FreeNodes (node: NODEPTR);	(* Free node and its successors *)
 BEGIN
	IF GetNextNode(node)<>Nil THEN FreeNodes(GetNextNode(node));
	Dispose(node)
 END;
 PROCEDURE FreeLinks (link: LINKPTR);	(* Free link and its successors *)
 BEGIN
	IF GetNextLink(link)=Nil THEN FreeLinks(GetNextLink(link));
	Dispose(link)
 END;
BEGIN
	IF FirstNode(graph)<>Nil THEN FreeNodes(FirstNode(graph));
	IF FirstLink(graph)<>Nil THEN FreeLinks(FirstLink(graph));
END;


(*** Graph ADT High Level Functions ***)

(* If node n is in link then return the linked node, otherwise -1 *)
FUNCTION LinkedNode (n: Integer; link: LINKPTR): Integer;
VAR
	n1, n2: Integer;
BEGIN
	n1 := GetLinkNode(link, 1);		(* Get first linked node *)
	n2 := GetLinkNode(link, 2);		(* Get second linked node *)
	IF n=n1 THEN LinkedNode := n2		(* Return second if first *)
	ELSE IF n=n2 THEN LinkedNode := n1	(* Return first if second *)
	ELSE LinkedNode := -1			(* Return -1 if neither *)
END;

(* Return the link between nodes n1 and n2 in graph (or Nil if not found) *)
FUNCTION NodeLink (n1, n2: Integer; graph: GRAPHPTR): LINKPTR;
 FUNCTION _NodeLink (n1, n2: Integer; link: LINKPTR): LINKPTR;
 BEGIN
	IF link=Nil THEN _NodeLink := Nil
	ELSE BEGIN
		IF (LinkedNode(n1, link)<0) OR (LinkedNode(n2, link)<0)
		THEN _NodeLink := _NodeLink(n1, n2, GetNextLink(link))
		ELSE _NodeLink := link
	END
 END;
BEGIN
	NodeLink := _NodeLink(n1, n2, FirstLink(graph))
END;

(* Find the shortest path (by link values) between nodes n1 and n2 *)
(* The path is stored in stack and the path length (or -1) is returned *)
(* If stack is Nil the path is discarded and only the length is returned *)
FUNCTION GraphPath (n1, n2: Integer; stack: STACKPTR; graph: GRAPHPTR): lval_t;
 FUNCTION _GraphPath (n1, n2: Integer; dist, max: lval_t; stack: STACKPTR; graph: GRAPHPTR): lval_t;
  (* Block or unblock the use of node in a path (using node value) *)
  PROCEDURE NodeBlock (state: Boolean; node: Integer; graph: GRAPHPTR);
  VAR
	v: Integer;
  BEGIN
	IF state THEN v := 1 ELSE v := 0;
	SetNodeValue(NodeNumber(node, graph), v)
  END;
  (* Return true if node has been used in a path (is blocked) *)
  FUNCTION IsBlocked(node: Integer; graph: GRAPHPTR): Boolean;
  BEGIN
	IF GetNodeValue(NodeNumber(node, graph))=0
	THEN IsBlocked := False
	ELSE IsBlocked := True
  END;
 VAR
	link: LINKPTR;
	used: Boolean;
	n: Integer;
	v: lval_t;
 BEGIN
	used := False;			(* This node has not yet been used *)
	NodeBlock(True, n1, graph);	(* Prevent reuse of this node *)
	IF n1=n2 THEN			(* Is n1 the target node? *)
	BEGIN
		IF stack<>Nil THEN	(* Discard current path (if any) *)
		    WHILE StackSize(stack)>0 DO PopStack(stack);
		max := dist;		(* Set new maximum path length *)
		used := True		(* This node has been used *)
	END
	ELSE BEGIN			(* Check all links to node n1 *)
		link := FirstLink(graph);
		WHILE link<>Nil DO
		BEGIN			(* Look for node n1 in link *)
		    n := LinkedNode(n1, link);	(* Check for blocked node *)
		    IF (n>=0) AND NOT IsBlocked(n, graph) THEN
		    BEGIN		(* Check link to n is within range *)
			v := GetLinkValue(link);(* Check for blocked link *)
			IF (v>=0) AND ((max=0) OR (dist+v<max)) THEN
			BEGIN		(* Search all links from node n *)
			    v := _GraphPath(n, n2, dist+v, max, stack, graph);
			    IF v>=0 THEN	(* A path to n2 was found *)
			    BEGIN
				max := v;	(* Update maximum length *)
				used := True	(* This node has been used *)
			    END
			END
		    END;
		    link := GetNextLink(link)
		END
	END;
	NodeBlock(False, n1, graph);	(* Allow reuse of this node *)
	IF used THEN			(* A path to n2 was found *)
	BEGIN				(* Store node n1 on the stack *)
		IF stack<>Nil THEN PushStack(n1, stack);
		_GraphPath := max	(* Return path length *)
	END
	ELSE _GraphPath := -1		(* No path to n2 was found *)
 END;
BEGIN
	GraphPath := _GraphPath(n1, n2, 0, 0, stack, graph);
END;

(* Load next link from file f and add it to graph *)
FUNCTION LoadLink (graph: GRAPHPTR; VAR f: Text): Boolean;
CONST	MAXLINEBUF = 128;		(* Maximum length of a file line *)
 (* Get a non-empty line from file f (returns an empty line if EOF) *)
 FUNCTION GetLine (VAR f: Text): String;
 VAR
	s: String[MAXLINEBUF];
 BEGIN
	GetLine := '';			(* No line has been read *)
	REPEAT
		IF Eof(f) THEN Exit;	(* Terminate if end of file *)
		ReadLn(f, s);		(* Read next line *)
	UNTIL Length(s)>0;		(* Skip empty lines *)
	GetLine := s;			(* A line has been read *)
 END;
 (* Return index of an existing node with name or a node created with name *)
 FUNCTION GetNode (name: PChar; graph: GRAPHPTR): Integer;
 BEGIN
	IF NodeIndex(name, graph)<0		(* Find existing node *)
	THEN AddNode(MakeNode(name, 0), graph);	(* Add a new node *)
	GetNode := NodeIndex(name, graph)	(* Get node index *)
 END;
VAR
	buf: Array[0..MAXLINEBUF] Of Char;
	n1, n2, err: Integer;
	value: lval_t;
BEGIN
	LoadLink := False;			(* No link has been loaded *)
	IF Strlen(StrPCopy(buf, GetLine(f)))=0 THEN Exit; (* Get next line *)
	n1 := GetNode(buf, graph);		(* Get first node index *)
	IF Strlen(StrPCopy(buf, GetLine(f)))=0 THEN Exit; (* Get next line *)
	n2 := GetNode(buf, graph);		(* Get second node index *)
	IF Strlen(StrPCopy(buf, GetLine(f)))=0 THEN Exit; (* Get next line *)
	Val(buf, value, err);			(* Get link value *)
	AddLink(MakeLink('', value, n1, n2), graph);	(* Add a new link *)
	LoadLink := True			(* A link has been loaded *)
END;

(* Load and add all links in file fname to graph *)
FUNCTION LoadLinkFile (fname: PChar; graph: GRAPHPTR): Boolean;
VAR
	f: Text;
BEGIN
	{$I-}
	Assign(f, fname);
	Reset(f);
	IF IOResult<>0 THEN LoadLinkFile := False
	ELSE BEGIN
		WHILE LoadLink(graph, f) DO;
		Close(f);
		LoadLinkFile := True
	END;
	{$I+}
END;

BEGIN
END.

Go To: CO205 Coursework