// Sliding window protocol
// Adapted from the dissertation of Roope Kaivola
// by Timo Latvala and Marko Mäkelä

// Use -DFAIR to activate fairness constraints
// Use -DPRIORITY to enable priorities

/*Size of the transmission window*/
unsigned tw = 4;
/*Size of the reception window*/
unsigned rw = 4;
/*Max value for sequnce numbers used*/
typedef unsigned (tw + rw - 1..) seqnum_t;
seqnum_t w = tw + rw - 1;

/*Typedefs for the place definitions*/
typedef enum {red, white, blue} data_t;
typedef unsigned (0..tw) twindow_t;
typedef unsigned (0..w) seq_t;

typedef data_t[queue tw] senderdata_t;

typedef struct {
  twindow_t i;
  twindow_t j;
  seq_t k;
} var_t;

/*Sender channel type*/
typedef struct {
  seq_t seqnum;
  data_t data;
} channel_t;

/*Function definitions*/
seq_t modP(unsigned x, unsigned y) is seq_t ((x + y) % (w + 1));
seq_t modM(unsigned x, unsigned y) is seq_t (((w + 1) + x - y) % (w + 1));

/*The transmission channel*/
unsigned maxCap=w;
place trans_channel (1) channel_t[queue maxCap]: {};
/*Make channel lossy*/
trans trans_loose_msg
in { place trans_channel: channel; }
out { place trans_channel: -channel; }
gate /channel != 0;

/*The acknowledgement channel*/
place ack_channel (1) seq_t[queue maxCap]: {};
/*Make the channel lossy*/
trans ack_loose_msg
in { place ack_channel: channel; }
out { place ack_channel: -channel; }
gate /channel != 0;

/*The sender*/
/*Places modeling the sender variables*/
place sender_data (1) senderdata_t: {};
place sendervars (1) var_t: { 0, 0, 0 };
place lock (1) bool: true;
/*The actions of the sender */
/*if the data table is not full, receive new data from
  the source. If the data is blue, do not receive any more data. */
trans rec_data
in {
  place sendervars: vars;
  place sender_data: d;
  place lock: true;
}
out {
  place sender_data: d + data_t data!;
  place sendervars: is var_t { +vars.i, vars.j, vars.k };
  place lock: data != >data_t;
}
#ifdef UNFOLD
gate %d > 0, /d >= is unsigned (vars.i)
#endif // UNFOLD
gate vars.i < tw;

/*If not all elements of sender_data has been sent,
  send the next one, assuming the channel is not full*/
trans send_msg
in {
  place sendervars: vars;
  place sender_data: data;
  place trans_channel: channel;
}
out {
  place sendervars: is var_t { vars.i, +vars.j, vars.k };
  place trans_channel: channel +
    (is channel_t { modP (vars.j, vars.k), *data[vars.j] });
  place sender_data: data;
}
#ifdef UNFOLD
gate /data > is unsigned (vars.j)
#endif // UNFOLD
gate %channel != 0, vars.j < vars.i;
/*A timeout causes retransmission.  It may only occur when there are no
 messages in the channels. */
trans rec_timeout
#ifdef PRIORITY
!
#endif // PRIORITY
in {
  place sendervars: vars;
  place trans_channel: tchannel;
  place ack_channel: achannel;
}
out {
  place sendervars: is var_t {vars.i, 0, vars.k};
  place trans_channel: tchannel;
  place ack_channel: achannel;
}
gate vars.i == vars.j && vars.i > 0 && /achannel == 0 && /tchannel == 0;
/*Receive an acknowledgement. Remove all acknowledged messages if 
  the acknowledgement number is within the transmission window */
trans rec_ack
#ifdef PRIORITY
!
#endif // PRIORITY
in {
  place sendervars: vars;
  place ack_channel: channel;
  place sender_data: data;
}
{ unsigned a = modM (*channel, vars.k) + 1; }
out {
  place sendervars: a <= tw
    ? is var_t { is twindow_t (is unsigned (vars.i) < a ? 0 : vars.i - a),
		 is twindow_t (is unsigned (vars.j) < a ? 0 : vars.j - a),
		 +*channel }
    : vars;
  place sender_data: a <= tw ? data - a : data;
  place ack_channel: -channel;
}
gate /channel != 0
#ifdef UNFOLD
gate a > tw || /data >= a
#endif // UNFOLD
#ifdef FAIR
strongly_fair seq_t p: p == *channel
#endif // FAIR
;

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

/*The receiver*/

typedef unsigned (0..rw-1) rseq_t;
typedef data_t[rseq_t] receiverdata_t;
typedef bool[rseq_t] ackarray_t;

/*Places modeling the receiver variables*/
place receiver_data (1) receiverdata_t: <receiverdata_t;
place ackarray (1) ackarray_t: <ackarray_t;
place e (1) seq_t: 0;
place pc (1) enum { rec_msg, send_data, send_ack }: rec_msg;
place white_req (1) bool: false;
place blue_req (1) bool: false;

/*The actions of the receiver*/

/*Receive a messsage*/
trans rec_msg
in {
  place pc: rec_msg;
  place trans_channel: ch;
  place e: e;
  place ackarray: r;
  place receiver_data: t;
  place white_req: wr;
  place blue_req: br;
}
{ unsigned s = modM ((*ch).seqnum, e); }
out {
  place e: e;
  place receiver_data: s < rw ? (t.{[is rseq_t s] (*ch).data}) : t;
  place ackarray: s < rw ? (r.{[is rseq_t s] true}) : r;
  place pc: send_data;
  place white_req: (*ch).data == is data_t white;
  place blue_req: (*ch).data != is data_t white;
  place trans_channel: -ch;
}
gate /ch != 0
#ifdef FAIR
strongly_fair seq_t p: p == (*ch).seqnum
#endif // FAIR
;

/*Forward (delete) the sequence of data from the beginning of
  receiver_data up to the first unreceived data item*/
trans send_data
#ifdef PRIORITY
!
#endif // PRIORITY
in {
  place pc: send_data;
  place ackarray: r;
  place e: e;
  place receiver_data: t;
  place white_req: wr;
  place blue_req: br;
}
out {
  place pc: r[0] ? send_data : send_ack;
  place e: r[0] ? +e : e;
  place ackarray: r[0] ? (r << 1).{ [rw - 1] false } : r;
  place receiver_data: r[0] ? (t << 1).{ [rw - 1] red } : t;
  place white_req: false;
  place blue_req: false;
}
#ifdef FAIR
weakly_fair true
#endif // FAIR
;

/*Send an acknowledgement*/
trans send_ack
in {
  place e: e;
  place ack_channel: ack;
  place pc: send_ack;
}
out {
  place ack_channel: ack + |e;
  place pc: rec_msg;
  place e: e;
}
gate %ack != 0
#ifdef FAIR
weakly_fair true
#endif // FAIR
;

// liveness property should hold:
// []<>true equals place white_req || <>true equals place blue_req
