------------------------------------------------------------------------------- -- sieve.m ------------------------------------------------------------------------------- -- To verify ae_finish -- use LIVENESS "ae_finish" -- To verify ee_fail -- use its negative INVARIANT "not ee_fail" ------------------------------------------------------------------------------- CONST -------------------------------------------------------- -- Combinations of NumFilter and NextPrime: -- NUM : 1 2 3 4 5 6 7 8 9 10 -- PRIME : 2 3 5 7 11 13 17 19 23 29 31 -- for consumer.N = 4 -- MAX : 11 13 17 19 23 29 31 -------------------------------------------------------- NumFilters : 7; MaxNumber : 31; BufferSize : 1; TYPE FilterID : 0..NumFilters-1; Number : 1..MaxNumber; BufferLength : 0..BufferSize; BufferIndex : 0..BufferSize-1; Generator : RECORD state : ENUM { sending, sentall, genend }; next : Number; END; Channel : RECORD length : BufferLength; buffer : ARRAY [BufferIndex] OF Number; END; FilterState : ENUM { notset, nodata, holddata }; Filter : RECORD state : FilterState; prime : Number; number : Number; END; Consumer : RECORD N : 0..4; MAX : Number; END; VAR gen : Generator; channels : ARRAY [0..NumFilters] OF Channel; filters : ARRAY [FilterID] OF Filter; consumer : Consumer; finish : boolean; fail : boolean; ------------------------------------------------------------------------------- -- buffered channel ------------------------------------------------------------------------------- FUNCTION isfull(Chan: Channel) : boolean; BEGIN RETURN Chan.length = BufferSize; END; FUNCTION isempty(Chan: Channel) : boolean; BEGIN RETURN Chan.length = 0; END; PROCEDURE input(VAR Chan: Channel; VAR v: Number); VAR i : BufferLength; BEGIN assert(!isempty(Chan)); v := Chan.buffer[0]; Chan.length := Chan.length-1; -- shift buffer IF Chan.length > 0 THEN FOR i:= 0 TO Chan.length-1 DO Chan.buffer[i] := Chan.buffer[i+1] END; END; CLEAR Chan.buffer[Chan.length]; END; PROCEDURE output(VAR Chan: Channel; v: Number); BEGIN assert(!isfull(Chan)); Chan.buffer[Chan.length] := v; Chan.length := Chan.length+1; END; ------------------------------------------------------------------------------- -- The number generator ------------------------------------------------------------------------------- ALIAS right: channels[0] DO RULE "generator sending" gen.state = sending & !isfull(right) ==> BEGIN output(right, gen.next); IF gen.next < MaxNumber THEN gen.next := gen.next+1; ELSE gen.state := sentall; END; END; ENDALIAS; ------------------------------------------------------------------------------- -- Filters ------------------------------------------------------------------------------- RULESET n: FilterID DO ALIAS filter: filters[n]; left: channels[n]; right: channels[n+1] DO RULE "set prime" filter.state = notset & !isempty(left) ==> BEGIN input(left, filter.prime); filter.state := nodata END; RULE "get number" filter.state = nodata & !isempty(left) ==> VAR N : Number; BEGIN input(left, N); IF N % filter.prime != 0 THEN filter.number := N; filter.state := holddata; END; END; RULE "send number" filter.state = holddata & !isfull(right) ==> BEGIN output(right, filter.number); CLEAR filter.number; filter.state := nodata; END; ENDALIAS; ENDRULESET; ------------------------------------------------------------------------------- -- The consumer ------------------------------------------------------------------------------- ALIAS left: channels[NumFilters] DO RULE "consumer get" consumer.N > 0 & !isempty(left) ==> VAR X: Number; BEGIN input(left, X); consumer.N := consumer.N-1; IF consumer.N = 0 THEN IF X = consumer.MAX THEN finish := true; ELSE fail := true; END; END; END; ENDALIAS; ------------------------------------------------------------------------------- LIVENESS "ae_finish" ALWAYS EVENTUALLY finish; INVARIANT "not ee_fail" !fail; ------------------------------------------------------------------------------- STARTSTATE CLEAR channels; CLEAR filters; gen.state := sending; gen.next := 2; consumer.N := 4; consumer.MAX := MaxNumber; finish := false; fail := false; END;