LARA

Verifying Dijkstra's algorithm in Jahob

This project consist to a verification Case Study of an implementation of the Dijkstra algorithm. Here is the part of the code we will analyze:

Report: final

Complete Dijkstra program: Dijkstra_Application

Slides: presentation

Here are the annotations for the verification of the Dijkstra Algorithm!

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//
//		  Dijkstra Program
//	Verification Case Study using Jahob
//	   By Robin Mange & Jonathan Kuhn
//
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
 
class Node
{
	public /*: claimedby Dijkstra */ int x,y;
	public /*: claimedby Dijkstra */ Path[] paths;
	public /*: claimedby Dijkstra */ int nb_paths;
}
 
class Path
{
	public /*: claimedby Node */ int dest;
	public /*: claimedby Node */ int value;
}
 
//////////////////////////////////////////////////////////
//Vector Class simulating the Vector Object	(OK)
//////////////////////////////////////////////////////////
 
class Vector
{
	private static Integer[] a;
	public /* readonly */ int size;
	/*:
	public static ghost specvar init :: bool;
 
	public static specvar content :: objset;
	vardefs
		"content == {n. EX j. n = a.[j] & 0 <= j & j < size}";
 
	invariant "init --> a ~= null & 0<a..Array.length & 0 <= 20 & 20<=a..Array.length";
	*/
 
	public void initialize()
	/*:
	requires "True"
	modifies init, content, size
	ensures "init & content = {} & size = 0";
	*/
	{
		a = new Integer[20];
		size = 0;
		//: init := "True";
	}
 
	public void add(Integer e)
	/*:
      	requires "init & e~=null"
      	modifies content, "Array.arrayState", size
      	ensures "((content = old content Un {e}) & (size = (old size) + 1)) | ((content = old content) & (size = (old size)))";
    	*/
	{
		if ((a == null) || (e == null)) {
			//: noteThat "content = old content";
			//: noteThat "size = old size";
			return;
		}
 		if ((size>=0) && (size < a.length)) {
			if (a[size] == null) a[size] = new Integer();
            		a[size] = e;
            		size = size + 1;
            		//: noteThat "content = old content Un {e}";
			//: noteThat "size = old size + 1";
        	} else {
            		//: noteThat "content = old content";
			//: noteThat "size = old size";
        	}
	}
	public void removeElement(Integer e)
	/*:
      	requires "init"
      	modifies content, size, "Array.arrayState"
      	ensures "((content = old content - {e}) & (size = (old size) - 1)) | ((content = old content) & (size = (old size)))";
    	*/
	{
		if ((size<=0) || (size > a.length)) {
			//: noteThat "content = old content";
			return;
		}
		int i=size-1;
		int save = -1;
		while //: inv "i>=-1 & i <size";
		(i>=0) 
		{
 			if (a[i] == e) save = i;
		}
 
		if (save==-1) {
			//: noteThat "content = old content";
			return;
		}
		else {
			if (size > 0) {
				a[save] = a[size-1];
				size = size - 1;
				//: noteThat "content = old content - {e}";
			}
		}
	}
	public boolean contains(Integer e)
	/*:
      	requires "init & e ~= null & size>=0"
      	ensures "result = (e : content) | result = False";
     	*/
	{
		//: ghost specvar content_i :: objset;
        	int i = 0;
		if (size > a.length) return false;
 
        	//: content_i := "{}";
        	while /*: inv "0 <= i & i <= size &
                       (content_i = {n. EX j. n = a.[j] & 0 <= j & j < i }) &
                       (e ~: content_i)" */
            	(i < size) {
            		if (a[i] == e) {
				return true;
            		} else {
                		//: content_i := "content_i Un {a.[i]}";
                		i = i + 1;
            		}
        	}
        	//: noteThat "i = size";
        	//: noteThat "content_i = content";
        	return false;   
	}
}
 
//////////////////////////////////////////////////////////
//Integer Class simulating the Integer Object	(OK)
//////////////////////////////////////////////////////////
 
public class Integer
{
	public  /*: claimedby Dijkstra */ int a;
}
 
//////////////////////////////////////////////////////////
//Dijkstra Class				(OK)
//////////////////////////////////////////////////////////
 
public class Dijkstra
{
	private static Integer[] results;
	private static Integer[] previous;
	public /*: readonly */ static int num;
 
	/*: 
	public static ghost specvar init :: bool;
	public static ghost specvar initFields :: bool;
 
	public static specvar content1 :: objset;
      	vardefs
        	"content1 == {n. EX j. n = results.[j] & 0 <= j & j < num}";
 
	invariant initInv: "init --> (results ~= null & 0 < results..Array.length & 
	                    20 <= results..Array.length)";
 
	invariant initFieldsInv: "initFields --> (ALL j. 0 <= j & j < num --> results.[j] ~= null)";
 
	public static specvar content2 :: objset;
      	vardefs
        	"content2 == {n. EX j. n = previous.[j] & 0 <= j & j < num}";
 
	invariant prevInv: "init --> (previous ~= null & 0 < previous..Array.length & 
						20 <= previous..Array.length)";
 
	invariant prevFieldInv: "initFields --> (ALL j. 0 <= j & j < num --> previous.[j] ~= null)";
        */
 
	public static void initialize() 
    	/*:
      	modifies init, content1, content2, num
      	ensures "init & content1 = {} & content2 = {}";
    	*/
    	{
			results = new /*: hidden */ Integer[20];
			previous = new /*: hidden */ Integer[20];
			num = 0;
        	//: init := "True";
    	}
 
	public boolean DefaultValue(int nb)
	/*:
	      requires "init & nb >= 0 & nb <= 20 & num=0"
	      modifies initFields, content1, content2, "Array.arrayState", num, "Integer.a"
	      ensures "init=True & initFields=True";
	*/
	{
		while /*: inv "0 <= num & num <= nb & (ALL x. 0 <= x & x < num --> results.[x] ~= null & previous.[x] ~= null)";
		      */
		(num < nb) {
			results[num] = new Integer();
			results[num].a = -1; //Set each distance to infinity.
			//: content1 := "content1 Un {results.[num]}";
 			previous[num] = new Integer();
	 		previous[num].a = -1; //Set each previous node to undefined.
			//: content2 := "content2 Un {previous.[num]}";
			num = num+1;
		}
		//: noteThat "num=nb";
		//: initFields := "True";
		if (num==nb) return true;
		else return false;
	}
 
	public void CalculateBranching(Node n, int lim, int u)
	/*:
	      requires "init & initFields & lim >= 0 & lim <= n..Node.paths..Array.length & u>=0 & u < num & n~=null & (n..Node.paths~=null) & (ALL j. j>=0 & j<lim --> n..Node.paths.[j]~=null) & num < 20"
	      modifies content1, content2,  "Integer.a"
	      ensures "True";
	*/
	{
		int i = 0;
		while /*: inv "i>=0 & i<=lim & (n~=null)";
			  */
		(i < lim) {
			if (i >= n.paths.length) return;
			else {
				int v = n.paths[i].dest;
				if ((v < 0) || (v >= num)) return;
				else {
					if (((results[u].a + n.paths[i].value) < results[v].a) || (results[v].a == -1))
					{
						results[v].a = results[u].a + n.paths[i].value;
						previous[v].a = u;
					}
				}
			}
			i = i + 1;
		}
		//: noteThat "i=lim";
	}
 
	public void Algorithm(Node m_node[], int nb, int start)
	    /*:
	      requires "init & nb > 0 & nb <= 20 & num=0 & start>=0 & start<nb & (m_node~=null) & (m_node..Array.length < num) & (ALL x. 0 <= x & x < nb --> m_node.[x] ~= null & m_node.[x]..Node.nb_paths >= 0 & m_node.[x]..Node.nb_paths <= 20 & (ALL y. 0 <= y & y < m_node.[x]..Node.nb_paths --> m_node.[x]..Node.paths.[y] ~= null))"
	      modifies init, initFields, content1, content2, "Array.arrayState", num, "Vector.size", "Vector.content", "Vector.init", "Integer.a"
	      ensures "init=True";
	    */
	{
 
		Vector vec1 = new Vector();
		vec1.initialize();
 
		Vector vec2 = new Vector();
		vec2.initialize();
 
		/////////////////////////////////////////////////////////
		//Reset All variables to initial Values for the algorithm (OK)
		//////////////////////////////////////////////////////////
 
		num = 0;
		DefaultValue(nb);
		//: assert "initFields=True";
		if (start >= num) return;
		else {
			results[start].a = 0; //Initial node, dist set to 0.
 
			//////////////////////////////////////////////////////////////////
			//Enter all nodes except the initial one to one Vector Object (OK)
			//////////////////////////////////////////////////////////////////
 
			Integer tmp = new Integer();
 
			int i = 0;
			while 	//: inv "0 <= i & i <= nb & (vec1~=null) & (tmp~=null) & theinvs";
			(i < nb) {
				tmp.a = i;
				if (i != start) vec1.add(tmp); //Add all elements except start to the vector	
				i = i + 1;
			}
			//: noteThat "i=nb";
 
			/////////////////////////////////////////////////////////////
			//Main Algorithm Loop		(OK)
			/////////////////////////////////////////////////////////////
 
			int i = 0;
			while 	//: inv "(vec1~=null & vec2~=null) & theinvs";		   
 			(vec1.size > 0)
			{
 				int u = SmallestDist(vec1, nb);
 
				if ((u < 0) || (u >= m_node.length)) return;
				else {
 
			    	Integer val = new Integer();
			    	val.a = u;
			    	vec1.removeElement(val);
			    	vec2.add(val);
 
			    	if ((m_node[u] == null) || (m_node[u].paths == null)) return;
					else {
						int lim = m_node[u].nb_paths;
 
						if ((lim < 0) || (lim > m_node[u].paths.length)) return;
						else CalculateBranching(m_node[u], lim, u);
					}
				}
			}
		}
	}
 
	//////////////////////////////////////////////////////////
	//SmallestDist Method	(OK)
	//////////////////////////////////////////////////////////
 
	public int SmallestDist(Vector vec, int nb)
	    /*:
	      requires "init & initFields & nb > 0 & nb <= 20 & vec ~= null & vec..Vector.size >= 0 & Vector.init=True"
	      modifies "Integer.a"
	      ensures "True";
	    */
	{
		int tmp = 10001;
		int index = -1;
		int i = 0;
 
		Integer val = new Integer();
 
		while /*: inv "0 <= i & i <= nb & (val~=null) & theinvs";
		      */
		(i < nb) {
			if (results[i]==null) return;
			else {
				val.a = i;
				if ((tmp > results[i].a) && (results[i].a != -1) && (vec.contains(val)))
				{
					if (results[i]==null) return;
					else {
						tmp = results[i].a;
						index = i;
					}
				}
			}
			i = i + 1;
		}
		return index;
	}
}