--| Variable declarations
const
    NODE_NUM : 2;
    DATA_NUM : 2;
    BUF_SIZE : 4; --3
    TRANS_SIZE : 2; -- 1
type
    NODE : scalarset(NODE_NUM);
    absNODE : union { NODE, enum { Other } };
    DATA : scalarset(DATA_NUM);
    NODE_IND : 0..NODE_NUM - 1;
    BUF_IND : 0..BUF_SIZE - 1;
    BUF_INDP : 0..BUF_SIZE;
    TRANS_IND : 0..TRANS_SIZE - 1;
    TRANS_INDP : 0..TRANS_SIZE;
    CACHE_STATE : enum { I, S, E };
    CACHE : record
        State : CACHE_STATE;
        Data : DATA;
    end;
    MSG_CMD : enum { Empty, ReqS, ReqE, Inv, InvAck, GntS, GntE };
    MSG : record
        Cmd : MSG_CMD;
        Data : DATA;
    end;
    RNAME : enum { Norule, SendReqS, RecvReqS, SendReqE, RecvReqE, SendInv,
                   RecvInv, SendInvAck, RecvInvAck, SendGntS, RecvGntS,
                   SendGntE, RecvGntE, Store };
    TNAME : enum { Notrans, ReqSTr, ReqETr, SendInvTr };
    BUF_ENT : record
        Rname : RNAME;
        Tname : TNAME;
        multiple : boolean;
    end;
    DIR_REC : record
        Rname : RNAME;
        Tname : TNAME;
        agent : absNODE;
    end;
var
    Cache : array[NODE] of CACHE;
    Chan1 : array[NODE] of MSG;
    Chan2 : array[NODE] of MSG;
    Chan3 : array[NODE] of MSG;
    InvSet : array[NODE] of boolean;
    ShrSet : array[NODE] of boolean;
    ExGntd : boolean;
    CurCmd : MSG_CMD;
    CurPtr : absNODE;
    MemData : DATA;
    AuxData : DATA;
    otherbuffer : array[BUF_IND] of BUF_ENT;
    dirrec : DIR_REC;
    concbuffer : array[NODE] of array[TRANS_IND] of BUF_ENT;
    system_nondet : boolean;
    nondet0 : boolean;
--| Function and procedure declarations
function findindex1 ( t : TNAME) : BUF_INDP;
    begin
        for i : BUF_IND do
            if otherbuffer[i].Tname = t then
                return i;
            endif;
        endfor;
        return BUF_SIZE;
    end;
function findindex2 ( m : RNAME;  t : TNAME) : BUF_INDP;
    begin
        for i : BUF_IND do
            if (otherbuffer[i].Rname = m) & (otherbuffer[i].Tname = t) then
                return i;
            endif;
        endfor;
        return BUF_SIZE;
    end;
function find ( r : RNAME;  t : TNAME;  a : NODE) : TRANS_INDP;
    begin
        for i : TRANS_IND do
            if (concbuffer[a][i].Rname = r) & (concbuffer[a][i].Tname = t) then
                return i;
            endif;
        endfor;
        return TRANS_SIZE;
    end;
function findtrans ( t : TNAME;  a : NODE) : TRANS_INDP;
    begin
        for i : TRANS_IND do
            if concbuffer[a][i].Tname = t then
                return i;
            endif;
        endfor;
        return TRANS_SIZE;
    end;
function existsconctrans(t: TNAME; a: NODE): boolean;
var i: TRANS_INDP;

   begin
i := findtrans(t,a);
if i < TRANS_SIZE then return true; else return false;
endif;   
   end;

procedure entername ( rn : RNAME;  tn : TNAME;
                      a :NODE);
    var
        i : TRANS_INDP;
    begin
    --assert (rn != SendInv);
        i := find(Norule, Notrans, a);
        if i < TRANS_SIZE then
            concbuffer[a][i].Rname := rn;
            concbuffer[a][i].Tname := tn;
        else
            assert false;
        endif;
        --if (dirrec.Tname = tn) & (dirrec.agent = a) then
          --  dirrec.Rname := rn;
        --endif;
    end;
procedure removename ( tn : TNAME;  rp : RNAME;
                       a :NODE);
    var
        i : TRANS_INDP;
    begin
        i := find(rp, tn, a);
        if i < TRANS_SIZE then
            concbuffer[a][i].Rname := Norule;
            concbuffer[a][i].Tname := Notrans;
        else
            assert false;
        endif;
    end;
procedure enternameother ( rn : RNAME;  t : TNAME);
    var
        i : BUF_INDP;
    var
        j : BUF_INDP;
    begin
        i := findindex2(rn, t);
        if i < BUF_SIZE then
            otherbuffer[i].multiple := true;
        else
            j := findindex2(Norule, Notrans);
            if j < BUF_SIZE then
                otherbuffer[j].Rname := rn;
                otherbuffer[j].Tname := t;
                otherbuffer[j].multiple := false;
            else
                assert false;
            endif;
        endif;
        if (dirrec.Tname = t) & (dirrec.agent = Other) then
            dirrec.Rname := rn;
        endif;
    end;
procedure shiftup ( i : BUF_IND);
    begin
        for j : BUF_IND do
            if (j < (BUF_SIZE - 1)) & (j >= i) then
                otherbuffer[j].Rname := otherbuffer[j + 1].Rname;
                otherbuffer[j].Tname := otherbuffer[j + 1].Tname;
                otherbuffer[j].multiple := otherbuffer[j + 1].multiple;
            endif;
        endfor;
        otherbuffer[BUF_SIZE - 1].Rname := Norule;
        otherbuffer[BUF_SIZE - 1].Tname := Notrans;
        otherbuffer[BUF_SIZE - 1].multiple := false;
    end;
procedure removenameother ( t : TNAME;  r : RNAME);
    var
        i : BUF_INDP;
    begin
        i := findindex2(r, t);
        if i = BUF_SIZE then
            assert false;
        endif;
        if otherbuffer[i].multiple = true then
            otherbuffer[i].multiple := system_nondet;
        else
            shiftup(i);
        endif;
    end;
procedure removenamedir ( rn : RNAME;  tn : TNAME;  agent : absNODE);
    begin
        if ((dirrec.Rname = rn) & (dirrec.Tname = tn)) & (dirrec.agent = agent) then
            dirrec.Rname := Norule;
            dirrec.Tname := Notrans;
            dirrec.agent := Other;
        endif;
    end;
function existsbuf ( t : TNAME;  r : RNAME) : boolean;
    var
        i : BUF_INDP;
    begin
        i := findindex2(r, t);
        if i < BUF_SIZE then
            return true;
        else
            return false;
        endif;
    end;
function existsconc ( t : TNAME;  r : RNAME;
                      a : NODE) : boolean;
    var
        i : TRANS_INDP;
    begin
        i := find(r, t, a);
        if i < TRANS_SIZE then
            return true;
        else
            return false;
        endif;
    end;
function nosubtrans ( t : TNAME) : boolean;
    var
        i : BUF_INDP;
    var
        k : TRANS_INDP;
    begin
        i := findindex1(t);
        if i < BUF_SIZE then
            return false;
        else
            if t = SendInvTr then
                i := 2;
            else
                if t = ReqSTr then
                    i := 1;
                else
                    i := 0;
                endif;
            endif;
            for j : NODE do
                k := findtrans(t, j);
                if k < TRANS_SIZE then
                    return false;
                endif;
            endfor;
        endif;
        return true;
    end;
--| Rules
ruleset
    d : DATA
do
    startstate "Init"
    begin
        for i : NODE do
            Chan1[i].Cmd := Empty;
            Chan2[i].Cmd := Empty;
            Chan3[i].Cmd := Empty;
            Cache[i].State := I;
            InvSet[i] := false;
            ShrSet[i] := false;
        endfor;
        for i : BUF_IND do
            otherbuffer[i].Rname := Norule;
            otherbuffer[i].Tname := Notrans;
            otherbuffer[i].multiple := false;
        endfor;
        for i : NODE do
            for j : TRANS_IND do
                concbuffer[i][j].Rname := Norule;
                concbuffer[i][j].Tname := Notrans;
                concbuffer[i][j].multiple := false;
            endfor;
        endfor;
        dirrec.Rname := Norule;
        dirrec.Tname := Notrans;
        dirrec.agent := Other;
        ExGntd := false;
        CurCmd := Empty;
        MemData := d;
        AuxData := d;
        system_nondet := false;
        nondet0 := false;
    end
;
end;
ruleset
    d : DATA
do
    rule "absStore"
ExGntd & forall i: NODE do Cache[i].State = I &  Chan2[i].Cmd != GntE & Chan3[i].Cmd != InvAck endforall
--ctc1
    ==>
    begin
        AuxData := d;
       -- removenamedir(RecvGntE, ReqETr, Other);
        nondet0 := false;
    end
;
end;
ruleset
    i : NODE;
    d : DATA
do
    rule "Store"
      Cache[i].State = E
    ==>
    begin
        Cache[i].Data := d;
        AuxData := d;
        --removenamedir(RecvGntE, ReqETr, i);
        nondet0 := false;
    end
;
end;
ruleset
    i : NODE
do
    rule "SendReqS"
      (Chan1[i].Cmd = Empty) & (Cache[i].State = I)
    ==>
    begin
        Chan1[i].Cmd := ReqS;
        nondet0 := false;
    end
;
end;
ruleset
    i : NODE
do
    rule "SendReqE"
      (Chan1[i].Cmd = Empty) & ((Cache[i].State = I) | (Cache[i].State = S))
    ==>
    begin
        Chan1[i].Cmd := ReqE;
        nondet0 := false;
    end
;
end;
    rule "absRecvReqS"
      CurCmd = Empty
    ==>
    begin
        CurCmd := ReqS;
        CurPtr := Other;
        for j : NODE do
            InvSet[j] := ShrSet[j];
        endfor;
        enternameother(RecvReqS, ReqSTr);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "RecvReqS"
      (CurCmd = Empty) & (Chan1[i].Cmd = ReqS)
    ==>
    begin
        CurCmd := ReqS;
        CurPtr := i;
        Chan1[i].Cmd := Empty;
        for j : NODE do
            InvSet[j] := ShrSet[j];
        endfor;
        entername(RecvReqS, ReqSTr, i);
        nondet0 := false;
    end
;
end;
    rule "absRecvReqE"
      CurCmd = Empty
    ==>
    begin
        CurCmd := ReqE;
        CurPtr := Other;
        for j : NODE do
            InvSet[j] := ShrSet[j];
        endfor;
        enternameother(RecvReqE, ReqETr);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "RecvReqE"
      (CurCmd = Empty) & (Chan1[i].Cmd = ReqE)
    ==>
    begin
        CurCmd := ReqE;
        CurPtr := i;
        Chan1[i].Cmd := Empty;
        for j : NODE do
            InvSet[j] := ShrSet[j];
        endfor;
        entername(RecvReqE, ReqETr, i);
        nondet0 := false;
    end
;
end;
    rule "absSendInv"
(CurCmd = ReqE) | ((CurCmd = ReqS) & (ExGntd = true))

-- & (forall i : NODE do Cache[i].State != E & Chan2[i].Cmd != GntE endforall)
    ==>
    begin
        --*enternameother(SendInv, SendInvTr);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "SendInv"
      ((Chan2[i].Cmd = Empty) & (InvSet[i] = true)) & ((CurCmd = ReqE) | (
                                                       (CurCmd = ReqS) & (
                                                       ExGntd = true)))
    ==>
    begin
        Chan2[i].Cmd := Inv;
        InvSet[i] := false;
        --*entername(SendInv, SendInvTr, i);
        nondet0 := false;
    end
;
end;
    rule "absSendInvAck"
      existsbuf(SendInvTr, SendInv)
    ==>
    begin
        --*removenameother(SendInvTr, SendInv);
        --*enternameother(SendInvAck, SendInvTr);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "SendInvAck"
      ((Chan2[i].Cmd = Inv) & (Chan3[i].Cmd = Empty)) --& (existsconc(SendInvTr,SendInv,i))
    ==>
    begin
        Chan2[i].Cmd := Empty;
        Chan3[i].Cmd := InvAck;
        if Cache[i].State = E then
            Chan3[i].Data := Cache[i].Data;
        endif;
        Cache[i].State := I;
        undefine Cache[i].Data;
        --*removename(SendInvTr, SendInv, i);
        --*entername(SendInvAck, SendInvTr, i);
        nondet0 := false;
    end
;
end;
    rule "absRecvInvAck"
(CurCmd != Empty) & (forall j: NODE do Cache[j].State != E & Chan2[j].Cmd != GntE & Chan3[j].Cmd != InvAck endforall)

--& (existsbuf(SendInvTr, SendInvAck))
--ctc1 
    ==>
    begin
        if ExGntd = true then
            ExGntd := false;
--modified with data prop
            MemData := AuxData;
        endif;
        --*removenameother(SendInvTr, SendInvAck);
        --removenamedir(SendInvAck, SendInvTr, Other);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "RecvInvAck"
      ((Chan3[i].Cmd = InvAck) & (CurCmd != Empty)) --& (existsconc(SendInvTr, SendInvAck, i))
    ==>
    begin
        Chan3[i].Cmd := Empty;
        ShrSet[i] := false;
        if ExGntd = true then
            ExGntd := false;
            MemData := Chan3[i].Data;
            undefine Chan3[i].Data;
        endif;
        --*removename(SendInvTr, SendInvAck, i);
        --removenamedir(SendInvAck, SendInvTr, i);
        nondet0 := false;
    end
;
end;
    rule "absSendGntS"
      ((((CurCmd = ReqS) & (CurPtr = Other)) & (ExGntd = false)) & (existsbuf(
                                                                              ReqSTr, RecvReqS))) --(nosubtrans(SendInvTr )
    ==>
    begin
        CurCmd := Empty;
        undefine CurPtr;
        removenameother(ReqSTr, RecvReqS);
        --removenamedir(RecvReqS, ReqSTr, Other);
--enternameother(SendGntS,ReqSTr);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "SendGntS"
      (((((CurCmd = ReqS) & (CurPtr = i)) & (Chan2[i].Cmd = Empty)) & (
                                                                       ExGntd = false)) & (existsconc(ReqSTr, RecvReqS, i)) ) --&  (nosubtrans(SendInvTr ))
    ==>
    begin
        Chan2[i].Cmd := GntS;
        Chan2[i].Data := MemData;
        ShrSet[i] := true;
        CurCmd := Empty;
        undefine CurPtr;
        removename(ReqSTr, RecvReqS, i);
        --removenamedir(RecvReqS, ReqSTr, i);
--entername(SendGntS,ReqSTr,i);
        nondet0 := false;
--assert (nosubtrans(SendInvTr));
    end
;
end;
    rule "absSendGntE"
      (((((CurCmd = ReqE) & (CurPtr = Other)) & (ExGntd = false)) & forall 
                                                                    j : NODE do 
                                                                    ShrSet[j] = false end) & (existsbuf(
       ReqETr, RecvReqE))) -- & (nosubtrans(SendInvTr 
    ==>
    begin
        ExGntd := true;
        CurCmd := Empty;
        undefine CurPtr;
        removenameother(ReqETr, RecvReqE);
        --removenamedir(RecvReqE, ReqETr, Other);
--enternameother(SendGntE,ReqETr);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "SendGntE"
      ((((((CurCmd = ReqE) & (CurPtr = i)) & (Chan2[i].Cmd = Empty)) & ( ExGntd = false)) & forall j : NODE do ShrSet[j] = false end))


-- & (existsconc(ReqETr, RecvReqE, i) & (nosubtrans(SendInvTr ))))
    ==>
    begin
        Chan2[i].Cmd := GntE;
        Chan2[i].Data := MemData;
        ShrSet[i] := true;
        ExGntd := true;
        CurCmd := Empty;
        undefine CurPtr;
        removename(ReqETr, RecvReqE, i);
        --removenamedir(RecvReqE, ReqETr, i);
        nondet0 := false;
--entername(SendGntE, ReqETr, i);
--assert (nosubtrans(SendInvTr));         
    end
;
end;
    rule "absRecvGntS"
      existsbuf(ReqSTr, SendGntS)
    ==>
    begin
        --removenameother(ReqSTr, SendGntS);
        --removenamedir(SendGntS, ReqSTr, Other);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "RecvGntS"
      (Chan2[i].Cmd = GntS) --& (existsconc(ReqSTr, SendGntS, i))
    ==>
    begin
        Cache[i].State := S;
        Cache[i].Data := Chan2[i].Data;
        Chan2[i].Cmd := Empty;
        undefine Chan2[i].Data;
        --removename(ReqSTr, SendGntS, i);
        --removenamedir(SendGntS, ReqSTr, i);
        nondet0 := false;
    end
;
end;
    rule "absRecvGntE"
      existsbuf(ReqETr, SendGntE)
    ==>
    begin
        --removenameother(ReqETr, SendGntE);
        --removenamedir(SendGntE, ReqETr, Other);
        nondet0 := false;
    end
;
ruleset
    i : NODE
do
    rule "RecvGntE"
      (Chan2[i].Cmd = GntE) --& (existsconc(ReqETr, SendGntE, i))
    ==>
    begin
        Cache[i].State := E;
        Cache[i].Data := Chan2[i].Data;
        Chan2[i].Cmd := Empty;
        undefine Chan2[i].Data;
        --removename(ReqETr, SendGntE, i);
        --removenamedir(SendGntE, ReqETr, i);
        nondet0 := false;
    end
;
end;

rule "setnondet"
  TRUE
==>
begin
    system_nondet := TRUE;
end;
rule "resetnondet"
  TRUE
==>
begin
    system_nondet := FALSE;
end;
invariant "CntrlProp"
    forall i : NODE do forall j : NODE do (i != j) -> (((Cache[i].State = E) -> (
                                                        Cache[j].State = I)) & (
                                                       (Cache[i].State = S) -> (
                                                       (Cache[j].State = I) | (
                                                       Cache[j].State = S)))) end end;

invariant "DataProp"
  ( ExGntd = false -> MemData = AuxData ) &
  forall i : NODE do Cache[i].State != I -> Cache[i].Data = AuxData end;

--| End of model

---non-interference lemmas-----


invariant "CTC2"
  forall i : NODE do
    Cache[i].State = E ->
    ExGntd = true &   -- 4
    forall j : NODE do
      j != i -> Cache[j].State = I  &   -- & 6
                --Chan2[j].Cmd != GntS &   -- 6
                Chan2[j].Cmd != GntE &  -- &  6
                Chan3[j].Cmd != InvAck   -- 7
    end
  end;

invariant "CTC1"
                                 forall i: NODE do forall j: NODE do (i != j) -> (Chan3[i].Cmd = InvAck & ExGntd = true & CurCmd != Empty -> (Cache[j].State != E & Chan2[j].Cmd != GntE & Chan3[j].Cmd != InvAck)) end end; 
