// model is an mdp
mdp

//-------------------------------------------------------------
// VARIABLES
const int N=20; // number of abstract hosts
const int K; // number of probes to send

// PROBABILITIES
//const double old = N/65024; // probability pick an ip address being used
const double old = 1/4; // probability pick an ip address being used
const double new = (1-old); // probability pick a new ip address

// TIMING CONSTANTS
const int CONSEC = 2;  // time interval between sending consecutive probles 
const int TRANSTIME = 1; // upper bound on transmission time delay
const int LONGWAIT = 60; // minimum time delay after a high number of address collisions

const int TIME_MAX_X = 60; // max value of clock x
const int TIME_MAX_Z = 1;  // max value of clock z

// OTHER CONSTANTS
const int MAXCOLL = 10;  // maximum number of collisions before long wait

formula ip_mess = ip;

//-------------------------------------------------------------
// CONCRETE HOST
module host0
	
	x : [0..TIME_MAX_X]; // first clock of the host
	
	coll : [0..MAXCOLL]; // number of address collisions
	probes : [0..K]; // counter (number of probes sent)
	mess : [0..1]; // need to send a message or not
	
	ip : [1..2]; // ip address (1 - in use & 2 - fresh)
	
	l : [0..4] init 1; // location
	// 0 : RECONFIGURE 
	// 1 : RANDOM
	// 2 : WAITSP
	// 3 : WAITSG 
	// 4 : USE
	
	// RECONFIGURE
	[reset] l=0 -> (l'=1);
	
	// RANDOM (choose IP address)
	[rec0] (l=1) -> true; // get message (ignore since have no ip address)
	[rec1] (l=1) -> true; // get message (ignore since have no ip address)
	// small number of collisions (choose straight away)
	[host] l=1 -> 1/3*old : (l'=2) & (ip'=1) & (x'=0) 
		                     + 1/3*old : (l'=2) & (ip'=1) & (x'=1) 
		                     + 1/3*old : (l'=2) & (ip'=1) & (x'=2) 
		                     + 1/3*new : (l'=2) & (ip'=2) & (x'=0) 
		                     + 1/3*new : (l'=2) & (ip'=2) & (x'=1) 
		                     + 1/3*new : (l'=2) & (ip'=2) & (x'=2); 
	// large number of collisions: (wait for LONGWAIT)
	// if i delete this line everything works
	[time] l=1 & coll=MAXCOLL & x<LONGWAIT -> (x'=min(x+1,TIME_MAX_X));
	[host] l=1 & coll=MAXCOLL & x=LONGWAIT -> 1/3*old : (l'=2) & (ip'=1) & (x'=0) 
			                                   + 1/3*old : (l'=2) & (ip'=1) & (x'=1) 
			                                   + 1/3*old : (l'=2) & (ip'=1) & (x'=2) 
			                                   + 1/3*new : (l'=2) & (ip'=2) & (x'=0) 
			                                   + 1/3*new : (l'=2) & (ip'=2) & (x'=1) 
			                                   + 1/3*new : (l'=2) & (ip'=2) & (x'=2);
	
	// WAITSP 
	// let time pass
	[time]  l=2 & x<2 -> (x'=min(x+1,2));
	// send probe
	[send1] l=2 & ip=1 & x=2  & probes<K -> (x'=0) & (probes'=probes+1);
	[send2] l=2 & ip=2 & x=2  & probes<K -> (x'=0) & (probes'=probes+1);
	// sent K probes and waited 2 seconds
	[host] l=2 & x=2 & probes=K -> (l'=3) & (probes'=0) & (coll'=0) & (x'=0);
	// get message and ip does not match: ignore
	[rec0] l=2 & ip!=0 -> (l'=l);
	[rec1] l=2 & ip!=1 -> (l'=l);
	// get a message with matching ip: reconfigure
	[rec1] l=2 & ip=1 -> (l'=0) & (coll'=min(coll+1,MAXCOLL)) & (x'=0) & (probes'=0);
	
	// WAITSG (sends two gratuitious arp probes)
	// time passage
	[time] l=3 & mess=0 & x<CONSEC -> (x'=min(x+1,TIME_MAX_X)); 
	
	// receive message and same ip: suppose always defer
	[rec1] l=3 & mess=0 & ip=1 -> (l'=0) & (probes'=0) & (x'=0);
	// receive message and different ip
	[rec0] l=3 & mess=0 & ip!=0 -> (l'=l);
	[rec1] l=3 & mess=0 & ip!=1 -> (l'=l);
	
		
	// send probe reply or message for defence
	[send1] l=3 & ip=1 & mess=1 -> (mess'=0);
	[send2] l=3 & ip=2 & mess=1 -> (mess'=0);
	// send first gratuitous arp message
	[send1] l=3 & ip=1 & mess=0 & x=CONSEC & probes<1 -> (x'=0) & (probes'=probes+1);
	[send2] l=3 & ip=2 & mess=0 & x=CONSEC & probes<1 -> (x'=0) & (probes'=probes+1);
	// send second gratuitous arp message (move to use)
	[send1] l=3 & ip=1 & mess=0 & x=CONSEC & probes=1 -> (l'=4) & (x'=0) & (probes'=0);
	[send2] l=3 & ip=2 & mess=0 & x=CONSEC & probes=1 -> (l'=4) & (x'=0) & (probes'=0);
	
	// USE (only interested in reaching this state so do not need to add anything here)
	[host] l=4 -> true;
	
endmodule

//-------------------------------------------------------------

// first error automaton: two seconds between send actions
module host_error1

	c : [0..2]; // time since first message
	s : [0..1];
	error1 : [0..1];

	// start counting
	[send1] error1=0 & s=0 -> (s'=1) & (c'=0);
	[send2] error1=0 & s=0 -> (s'=1) & (c'=0);
	[rec0] error1=0 -> (s'=0) & (c'=0);
	[rec1] error1=0 -> (s'=0) & (c'=0);
	[send1] error1=0 & s=1 -> (error1'=1) & (c'=0);
	[send2] error1=0 & s=1 -> (error1'=1) & (c'=0);
	// time passage so update relevant clocks
	[time] s=0 -> true;
	[time] error1=0 & s=1 & c<1 -> (c'=c+1);
	[time] error1=0 & s=1 & c=1 -> (s'=0) & (c'=0);

	// finished so any action can be performed
	[rec1] error1=1 -> true;
	[rec1] error1=1 -> true;
	[time]  error1=1 -> true;
	[send1]  error1=1  -> true;
	[send2]  error1=1  -> true;

endmodule

// second error automaton: reset must be preceeded by rec1
// (with no time passage inbetween)
module host_error2

	s2 : [0..1];
	error2 : [0..1];
	
	[time] error2=0 -> (s2'=0); 
	[rec1] error2=0 -> (s2'=1);

	[reset] error2=0 & s2=0 -> (error2'=1) & (s2'=0); 
	[reset] error2=0 & s2=1 -> true; 

	// finished so any action can be performed
	[rec1] error2=1 -> true;
	[reset] error2=1 -> true;
	[time]  error2=1 -> true;

endmodule

// records a send1 or send2 action occurs (needed in properties)
module timer

	t : [0..1];
	
	[send1] t=0 -> (t'=1);
	[send2] t=0 -> (t'=1);
	[] t>0 -> (t'=0);
	
	// can do all actions when t=0
	[time] t=0 -> true;
	[reset] t=0 -> true;
	[rec0] t=0 -> true;
	[rec1] t=0 -> true;
	[host] t=0 -> true;
	// note cannot do anything when t=1

endmodule