Added model scenario for existance of long ballots

-- (c) 2010, Dermot Cochran, IT University of Copenhagen
-- http://www.kindsoftware.com/about/people/dc
-- http://www.itu.dk/people/dero

module Voting

open util/integer

-- A person standing for election
sig Candidate {
    identifier: Int,               -- Unique identifier for this candidate
  	votes: 		set Ballot, 	-- First preference ballots assigned to this candidate
	transfers: set Ballot, 	-- Ballots received by transfer from another candidate
	surplus: 	set Ballot, 	-- Ballots given to another candidate (on election or elimination)
	outcome: 	Event
} {
	no b: Ballot | b in votes & transfers
	all b: Ballot | b in votes + transfers => this in b.assignees
	surplus in votes + transfers
	Election.method = Plurality implies #surplus = 0 and #transfers = 0
    0 < identifier
}

-- A digital or paper artifact which accurately records the intentions of the voter
sig Ballot {
  identifier:		Int,					-- Unique identifier for this ballot
  assignees: 		set Candidate,  -- Candidates to which this ballot has been assigned
  preferences: 	seq Candidate,
  votes:				seq Vote,			-- Fragments of the vote
  top:				Int,  					-- Unique identifier of first preference candidate
  length:			Int  					-- Number of preferences expressed on ballot
} {
	assignees in preferences.elems
    not preferences.hasDups
	preferences.elems in Election.candidates
	preferences.first in assignees
	Election.method = Plurality implies length = 1
	top = preferences.first.identifier
    length = #preferences
    0 < length
    0 < identifier
    some v: Vote | v.ballot = identifier and v.candidate = top and v.ranking = 1
    all v: Vote | Election.method = STV and v in votes.elems implies v.ballot = identifier
    votes.first.candidate = preferences.first.identifier
    votes.last.candidate = preferences.last.identifier
    votes.first.ranking = 1
    votes.last.ranking = length
    #votes = #preferences
}

-- Table of fragments of Votes used for encoding of results
sig Vote {
	ballot:	Int,			-- Ballot identifier
    candidate: Int,		-- Candidate identifier
    ranking: Int			-- Ranking of candidate on ballot paper
} {
	0 < ranking
    some b: Ballot | b.identifier = ballot and this in b.votes.elems and ranking <= b.length
    some c: Candidate | c.identifier = candidate
    ranking <= #Election.candidates
}

-- An election result
one sig Scenario {
   	losers: 					set Candidate,
   	winners: 				set Candidate,
	eliminated: 			set Candidate, 		-- Candidates excluded under STV rules
	threshold: 			Int, 						-- Minimum number of votes for a Loser or EarlyLoser
	quota: 					Int						-- Minimum number of votes for a STV Winner or QuotaWinner
}
{
 	winners + losers = Election.candidates
 	#winners = Election.seats
 	no c: Candidate | c in losers & winners
 	0 < #losers
 	all w: Candidate | all l: Candidate | l in losers and w in winners implies
		#l.votes + #l.transfers <= #w.votes + #w.transfers
	0 <= threshold
	threshold < quota
	Election.method = Plurality implies #eliminated = 0
	eliminated in losers
	all c: Candidate | c in losers implies Election.method = Plurality or #c.votes + #c.transfers < quota
}

/*
	There are four winner-outcomes and six loser-outcomes:

	Winner: 						(W1) elected in the first round of counting either by quota or plurality,
	QuotaWinner: 				(W2) elected with transfers from another candidate (STV only),
	CompromiseWinner: 	(W3) elected on the last round of counting without quota (STV only),
	TiedWinner:					(W4) elected by tie breaker,
	TiedLoser:					(L1) loses only by tie breaker but reaches the threshold,
	Loser:			 				(L2) defeated on last round but reaches the minimum threshold of votes,
    TiedEarlyLoser:			(L3) reaches threshold but eliminated by tie breaker (STV only),
	EarlyLoser:					(L4) reaches threshold but is eliminated before last round (STV only),
	TiedSoreLoser:				(L5) loses only by tie breaker but does not reach threshold,
	SoreLoser: 					(L6) does not even reach the mimimum threshold of votes
*/
enum Event {Winner, QuotaWinner, CompromiseWinner, TiedWinner,
	TiedLoser, Loser, TiedEarlyLoser, EarlyLoser, TiedSoreLoser, SoreLoser}

enum Method {Plurality, STV}

-- A collection of ballots cast in an election
one sig BallotBox {
	ballots: set Ballot
} {
    0 < #ballots
	all b: Ballot | b in ballots
}

-- An Election Constituency
one sig Election {
  candidates: 	set Candidate,
  seats: 				Int,
  method: 			Method
}
{
 	0 < seats
 	seats < #candidates
 	all c: Candidate | c in candidates
}

-- Independent (or Fundamental) Axioms

fact uniqueCandidateID {
  no disj a,b: Candidate | a.identifier = b.identifier
}

fact uniqueBallotID {
  no disj a,b: Ballot | a.identifier = b.identifier
}

fact nonDuplicationOfVotes {
  no disj a,b: Vote | a.ballot = b.ballot and a.candidate = b.candidate
}

fact integrity {
  all c: Candidate | all b: Ballot | b in c.votes implies c in b.assignees
}

fact pluralityEvents {
	all c: Candidate | Election.method = Plurality implies
		(c.outcome = Loser or c.outcome = SoreLoser or c.outcome = Winner or
		c.outcome = TiedWinner or c.outcome = TiedLoser)
}

fact wellFormedQuota {
	Election.method = STV implies
		#BallotBox.ballots < Scenario.quota.mul[Election.seats+1] and
		Scenario.quota.mul[Election.seats] <= #BallotBox.ballots
}

fact wellFormedThreshold {
	#BallotBox.ballots <= Scenario.threshold.mul[20] or
	Election.method = STV implies Scenario.quota <= Scenario.threshold.mul[4]
}

fact noStrayBallots {
	all b: Ballot | b in BallotBox.ballots
}

fact winnerSTV {
	all c: Candidate | Election.method = STV implies (
		c.outcome = Winner iff Scenario.quota <= #c.votes)
}

fact transferWinner {
	all c: Candidate | c.outcome = QuotaWinner iff
		#c.votes < Scenario.quota and Scenario.quota <= #c.votes + #c.transfers
}

fact closeWinner {
	all c: Candidate | c.outcome = CompromiseWinner or c.outcome = TiedWinner iff
		c in Scenario.winners and #c.votes + #c.transfers < Scenario.quota
}

fact soreLoser {
	all c: Candidate | (c.outcome = SoreLoser or c.outcome = TiedSoreLoser) iff
		#c.votes + #c.transfers < Scenario.threshold
}

fact loser {
	all c: Candidate | (c.outcome = Loser or c.outcome = TiedLoser or c.outcome = TiedSoreLoser) iff
		c in Scenario.losers - Scenario.eliminated
}

fact earlyLoser {
	all c: Candidate | c.outcome = EarlyLoser or c.outcome = TiedEarlyLoser iff
		(c in Scenario.losers & Scenario.eliminated and Scenario.threshold <= #c.votes + #c.transfers)
}

fact winners {
	all c: Candidate | c in Scenario.winners iff
		c.outcome = Winner or c.outcome = QuotaWinner or c.outcome = CompromiseWinner or
		c.outcome = TiedWinner
}

fact losers {
	all c: Candidate | c in Scenario.losers iff
		(c.outcome = Loser or c.outcome = EarlyLoser or c.outcome = SoreLoser or
		c.outcome = TiedLoser or c.outcome = TiedEarlyLoser or c.outcome = TiedSoreLoser)
}

-- Non-Independent Axioms for Ballot Integrity
fact transfers {
	all c: Candidate | 0 < #c.transfers implies Election.method = STV
}

fact onlyWinnersAndQuotaWinnersHaveSurplus {
	all c: Candidate | 0 < #c.surplus implies
		c.outcome = Winner or c.outcome = QuotaWinner
}

fact winnersNeedNoTransfers {
		all c: Candidate | c.outcome = Winner implies 0 = #c.transfers
}

fact firstPreference {
	all b: Ballot | all c: Candidate | b.preferences.first = c iff b in c.votes
}

fact sizeOfSurplus {
	all c: Candidate | (c in Scenario.winners and Election.method = STV and 0 < #c.surplus) implies
		#c.surplus = #c.votes + #c.transfers - Scenario.quota
}

fact transferToNextPreference {
	all b: Ballot | all disj donor,receiver: Candidate |
		(donor + receiver in b.assignees and
		b in receiver.transfers and b in donor.surplus implies
		b.preferences.idxOf[donor] < b.preferences.idxOf[receiver] and
		receiver in b.preferences.rest.elems)
}

fact pluralityWinner {
	all disj a, b: Candidate | (Election.method = Plurality and Election.seats = 1) and
		a.outcome = Winner implies #b.votes <= #a.votes
}

fact transferToNextContinuingCandidate {
	all disj skipped, receiving: Candidate | all b: Ballot |
		b.preferences.idxOf[skipped] < b.preferences.idxOf[receiving] and
		receiving in b.assignees and (not skipped in b.assignees) implies
		(skipped in Scenario.eliminated or skipped.outcome = Winner or
		skipped.outcome = QuotaWinner)
}

// All ballots transfers are associated with the last candidate to receive the transfer
fact ownership {
	all disj c,d: Candidate | all b: Ballot | b in c.transfers implies c in b.assignees and
		(d not in b.assignees or b.preferences.idxOf[d] < b.preferences.idxOf[c])
}

// Lowest candidate is eliminated first
fact lowestElimination {
	all disj c,d: Candidate | c in Scenario.eliminated and d not in Scenario.eliminated implies
		#c.votes + #c.transfers <= #d.votes + #d.transfers
}

// All ties involve equality between at least one winner and at least one loser on either original votes or
// on transfers plus original votes
fact equalityOfTiedWinner {
   all w: Candidate | some l: Candidate | w.outcome = TiedWinner and
	 	(l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome = TiedEarlyLoser) implies
		(#l.votes = #w.votes) or (#l.votes + #l.transfers = #w.votes + #w.transfers)
}

fact equalityOfTiedLosers {
  all s: Candidate | some w: Candidate | w.outcome = TiedWinner and
       (s.outcome = SoreLoser or s.outcome = TiedLoser or s.outcome = TiedEarlyLoser) implies
       (#s.votes = #w.votes) or (#s.votes + #s.transfers = #w.votes + #w.transfers)
}

// Ranking of votes on ballots
fact firstPreference {
	all v: Vote | some b: Ballot | v.ballot = b.identifier and
		v.ranking = 1 implies b.top = v.candidate
}

fact rankingOfVotes {
   	all v: Vote | some b: Ballot | some c: Candidate | Election.method = STV and
		v.ballot = b.identifier and v.candidate = c.identifier implies
	    c in b.preferences.elems and v.ranking = b.preferences.idxOf[c]
}

-- Scenario Validity Axioms
// When there is a tied sore loser then there are no non-sore losers
fact typeOfTiedLoser {
   no disj a,b: Candidate | a.outcome = TiedSoreLoser and
        (b.outcome = TiedLoser or b.outcome = TiedEarlyLoser or
         b.outcome=Loser or b.outcome=EarlyLoser)
}

fact tieBreaker {
	all w: Candidate | some l: Candidate | w.outcome = TiedWinner implies
		(l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome = TiedEarlyLoser)
}

fact validTieBreaker {
	all l: Candidate | some w: Candidate |
    (l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome = TiedEarlyLoser) implies
    w.outcome = TiedWinner
}

-- Basic Lemmas
assert honestCount {
	  all c: Candidate | all b: Ballot | b in c.votes + c.transfers implies c in b.assignees
}
check honestCount for 15 but 6 int

assert atLeastOneLoser {
	0 < #Scenario.losers
}
check atLeastOneLoser for 15 but 6 int

assert atLeastOneWinner {
	0 < #Scenario.winners
}
check atLeastOneWinner for 14 but 6 int

assert wellFormednessOfBallots {
	all b: Ballot | b.assignees in Election.candidates
}
check wellFormednessOfBallots for 18 but 6 int

assert wellFormedBallotBox {
		#BallotBox.ballots = sum (#Election.candidates.votes)
}
check wellFormedBallotBox for 14 but 6 int

assert plurality {
	all c: Candidate | all b: Ballot | b in c.votes and
		Election.method = Plurality implies c in b.preferences.first
}
check plurality for 18 but 6 int

assert wellFormedTieBreaker {
	some w,l : Candidate | (w in Scenario.winners and l in Scenario.losers and
		#w.votes = #l.votes and #w.transfers = #l.transfers) implies
		w.outcome = TiedWinner and
		(l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome=TiedEarlyLoser)
}
check wellFormedTieBreaker for 18 but 6 int

assert validSurplus {
	all c: Candidate | 0 < #c.surplus implies (c.outcome = Winner or c.outcome = QuotaWinner or
		c in Scenario.eliminated)
}
check validSurplus for 16 but 6 int

-- Advanced Lemmas
// No lost votes during counting
assert accounting {
	all b: Ballot | one c: Candidate | b in c.votes and c in b.assignees
}
check accounting for 16 but 6 int

// No ballots created or destroyed during counting
assert immutability {
	all c: Candidate | c.votes + c.transfers in BallotBox.ballots and c.surplus in BallotBox.ballots
}
check immutability for 16 but 6 int

// Cannot have tie breaker with both tied sore loser and non-sore loser
assert tiedWinnerLoserTiedSoreLoser {
	no disj c,w,l: Candidate | c.outcome = TiedSoreLoser and
	   w.outcome = TiedWinner and (l.outcome = Loser or l.outcome = TiedLoser)
}
check tiedWinnerLoserTiedSoreLoser for 6 int

// Non repeating ranking in ballots
assert wellFormedRanking {
}
check wellFormedRanking for 6

-- Sample scenarios
pred TwoCandidatePlurality {
	Election.method = Plurality
	Election.seats = 1
	#Election.candidates = 2
}
run TwoCandidatePlurality for 10 but 1 Ballot, 6 int

pred PluralityTiedWinner {
	Election.method = Plurality
	Election.seats = 1
	some disj a,b: Candidate | a.outcome = TiedWinner and b.outcome = TiedLoser
}
run PluralityTiedWinner for 10 but 2 Ballot, 6 int

pred ThreeCandidatePlurality {
	Election.method = Plurality
	Election.seats = 1
	#Election.candidates = 3
}
run ThreeCandidatePlurality for 6 int

pred Plurality {
	Election.method = Plurality
	Election.seats = 1
	3 < #Election.candidates
}
run Plurality for 5 but 6 int

pred TenCandidatePluralityWithTwoSeats {
	Election.method = Plurality
	Election.seats = 2
	#Election.candidates = 10
}
run TenCandidatePluralityWithTwoSeats for 10 but 6 int

pred InstantRunoffVoting {
	Election.method = STV
	Election.seats = 1
	3 < #Election.candidates
}
run InstantRunoffVoting for 10 but 6 int

pred STV3 {
	Election.method = STV
	Election.seats = 3
}
run STV3 for 5 but 6 int

pred ThreeWinnersOneLoser {
	some a: Candidate | a.outcome = Winner
   	some b: Candidate | b.outcome = QuotaWinner
	some c: Candidate | c.outcome = CompromiseWinner
	some d: Candidate | d in Scenario.losers
}
run ThreeWinnersOneLoser for 8 but 6 int

pred LoserAndEarlyLoser {
	one d: Candidate | d.outcome = Loser
	one e: Candidate | e.outcome = EarlyLoser
}
run LoserAndEarlyLoser for 5 but 6 int

pred OneWinnerNineLosers {
	#Scenario.winners = 1
	#Scenario.losers = 9
}
run OneWinnerNineLosers for 16 but 6 int

pred TwentyBallots {
	#BallotBox.ballots = 20
}
run TwentyBallots for 20 but 6 int

pred ThreeWayTie {
	some disj a,b,c: Candidate | a.outcome = TiedWinner and b.outcome = TiedLoser and
		c.outcome = TiedLoser
}
run ThreeWayTie for 5 but 6 int

pred FiveWayTie {
	some disj a,b,c,d,e: Candidate | a.outcome = TiedWinner and b.outcome = TiedLoser and
		c.outcome = TiedLoser and d.outcome = TiedLoser and e.outcome = TiedWinner
}
run FiveWayTie for 7 but 6 int

pred TwentyCandidates {
	#Election.candidates = 20
	some c: Candidate | 0 < #c.votes and c in Scenario.losers
}
run TwentyCandidates for 20 but 6 int

pred Outcomes {
 	some disj a,b,c0,d,e,f,g,h,i: Candidate |
		(a.outcome = Winner and b.outcome = QuotaWinner and
		c0.outcome = CompromiseWinner and d.outcome = Loser and
		e.outcome = EarlyLoser and f.outcome = SoreLoser and
		g.outcome = TiedWinner and h.outcome = TiedLoser and i.outcome = TiedEarlyLoser)
}
run Outcomes for 10 but 6 int

pred TenDifferentBallots {
	#BallotBox.ballots = 10
	no disj a,b: Ballot | a.preferences.first = b.preferences.first and #a.preferences = #b.preferences and
		a.preferences.last = b.preferences.last and #a.assignees = #b.assignees
}
run TenDifferentBallots for 10

 but 6 int

pred ScenarioLWW {
	some a: Candidate | a.outcome = Loser
	some disj b,c: Candidate | b.outcome = Winner and c.outcome = Winner
    #Election.candidates = 3
}
run ScenarioLWW for 6 int

pred AnyScenarioWithTiedSoreLoser {
	some c: Candidate | c.outcome = TiedSoreLoser
}
run AnyScenarioWithTiedSoreLoser for 6 int

pred LongBallot {
	some b: Ballot | b.length = 20
}
run LongBallot for 20 int

-- (c) Dermot Cochran, 2010, IT University of Copenhagen
-- http://www.kindsoftware.com
-- http://www.itu.dk

module Voting

open util/integer

-- A person standing for election
sig Candidate {
    identifier: Int,               -- Unique identifier for this candidate
  	votes: 		set Ballot, 	-- First preference ballots assigned to this candidate
	transfers: set Ballot, 	-- Ballots received by transfer from another candidate
	surplus: 	set Ballot, 	-- Ballots given to another candidate (on election or elimination)
	outcome: 	Event
} {
	no b: Ballot | b in votes & transfers
	all b: Ballot | b in votes + transfers => this in b.assignees
	surplus in votes + transfers
	Election.method = Plurality implies #surplus = 0 and #transfers = 0
    0 < identifier
}

-- A digital or paper artifact which accurately records the intentions of the voter
sig Ballot {
  identifier:		Int,					-- Unique identifier for this ballot
  assignees: 		set Candidate,  -- Candidates to which this ballot has been assigned
  preferences: 	seq Candidate,
  votes:				seq Vote,			-- Fragments of the vote
  top:				Int,  					-- Unique identifier of first preference candidate
  length:			Int  					-- Number of preferences expressed on ballot
} {
	assignees in preferences.elems
    not preferences.hasDups
	preferences.elems in Election.candidates
	preferences.first in assignees
	Election.method = Plurality implies length = 1
	top = preferences.first.identifier
    length = #preferences
    0 < length
    0 < identifier
    some v: Vote | v.ballot = identifier and v.candidate = top and v.ranking = 1
    all v: Vote | v in votes.elems implies v.ballot = identifier
    votes.first.candidate = preferences.first.identifier
    votes.last.candidate = preferences.last.identifier
    votes.first.ranking = 1
    votes.last.ranking = length
}

-- Table of fragments of Votes used for encoding of results
sig Vote {
	ballot:	Int,			-- Ballot identifier
    candidate: Int,		-- Candidate identifier
    ranking: Int			-- Ranking of candidate on ballot paper
} {
	0 < ranking
    some b: Ballot | b.identifier = ballot and this in b.votes.elems and ranking <= b.length
    some c: Candidate | c.identifier = candidate
    ranking <= #Election.candidates
}

-- An election result
one sig Scenario {
   	losers: 					set Candidate,
   	winners: 				set Candidate,
	eliminated: 			set Candidate, 		-- Candidates excluded under STV rules
	threshold: 			Int, 						-- Minimum number of votes for a Loser or EarlyLoser
	quota: 					Int						-- Minimum number of votes for a STV Winner or QuotaWinner
}
{
 	winners + losers = Election.candidates
 	#winners = Election.seats
 	no c: Candidate | c in losers & winners
 	0 < #losers
 	all w: Candidate | all l: Candidate | l in losers and w in winners implies
		#l.votes + #l.transfers <= #w.votes + #w.transfers
	0 <= threshold
	threshold < quota
	Election.method = Plurality implies #eliminated = 0
	eliminated in losers
	all c: Candidate | c in losers implies Election.method = Plurality or #c.votes + #c.transfers < quota
}

/*
	There are four winner-outcomes and six loser-outcomes:

	Winner: 						(W1) elected in the first round of counting either by quota or plurality,
	QuotaWinner: 				(W2) elected with transfers from another candidate (STV only),
	CompromiseWinner: 	(W3) elected on the last round of counting without quota (STV only),
	TiedWinner:					(W4) elected by tie breaker,
	TiedLoser:					(L1) loses only by tie breaker but reaches the threshold,
	Loser:			 				(L2) defeated on last round but reaches the minimum threshold of votes,
    TiedEarlyLoser:			(L3) reaches threshold but eliminated by tie breaker (STV only),
	EarlyLoser:					(L4) reaches threshold but is eliminated before last round (STV only),
	TiedSoreLoser:				(L5) loses only by tie breaker but does not reach threshold,
	SoreLoser: 					(L6) does not even reach the mimimum threshold of votes
*/
enum Event {Winner, QuotaWinner, CompromiseWinner, TiedWinner,
	TiedLoser, Loser, TiedEarlyLoser, EarlyLoser, TiedSoreLoser, SoreLoser}

enum Method {Plurality, STV}

-- A collection of ballots cast in an election
one sig BallotBox {
	ballots: set Ballot
} {
    0 < #ballots
	all b: Ballot | b in ballots
}

-- An Election Constituency
one sig Election {
  candidates: 	set Candidate,
  seats: 				Int,
  method: 			Method
}
{
 	0 < seats
 	seats < #candidates
 	all c: Candidate | c in candidates
}

-- Independent (or Fundamental) Axioms

fact uniqueCandidateID {
  no disj a,b: Candidate | a.identifier = b.identifier
}

fact uniqueBallotID {
  no disj a,b: Ballot | a.identifier = b.identifier
}

fact nonDuplicationOfVotes {
  no disj a,b: Vote | a.ballot = b.ballot and a.candidate = b.candidate
}

fact integrity {
  all c: Candidate | all b: Ballot | b in c.votes implies c in b.assignees
}

fact pluralityEvents {
	all c: Candidate | Election.method = Plurality implies
		(c.outcome = Loser or c.outcome = SoreLoser or c.outcome = Winner or
		c.outcome = TiedWinner or c.outcome = TiedLoser)
}

fact wellFormedQuota {
	Election.method = STV implies
		#BallotBox.ballots < Scenario.quota.mul[Election.seats+1] and
		Scenario.quota.mul[Election.seats] <= #BallotBox.ballots
}

fact wellFormedThreshold {
	#BallotBox.ballots <= Scenario.threshold.mul[20] or
	Election.method = STV implies Scenario.quota <= Scenario.threshold.mul[4]
}

fact noStrayBallots {
	all b: Ballot | b in BallotBox.ballots
}

fact winnerSTV {
	all c: Candidate | Election.method = STV implies (
		c.outcome = Winner iff Scenario.quota <= #c.votes)
}

fact transferWinner {
	all c: Candidate | c.outcome = QuotaWinner iff
		#c.votes < Scenario.quota and Scenario.quota <= #c.votes + #c.transfers
}

fact closeWinner {
	all c: Candidate | c.outcome = CompromiseWinner or c.outcome = TiedWinner iff
		c in Scenario.winners and #c.votes + #c.transfers < Scenario.quota
}

fact soreLoser {
	all c: Candidate | (c.outcome = SoreLoser or c.outcome = TiedSoreLoser) iff
		#c.votes + #c.transfers < Scenario.threshold
}

fact loser {
	all c: Candidate | (c.outcome = Loser or c.outcome = TiedLoser or c.outcome = TiedSoreLoser) iff
		c in Scenario.losers - Scenario.eliminated
}

fact earlyLoser {
	all c: Candidate | c.outcome = EarlyLoser or c.outcome = TiedEarlyLoser iff
		(c in Scenario.losers & Scenario.eliminated and Scenario.threshold <= #c.votes + #c.transfers)
}

fact winners {
	all c: Candidate | c in Scenario.winners iff
		c.outcome = Winner or c.outcome = QuotaWinner or c.outcome = CompromiseWinner or
		c.outcome = TiedWinner
}

fact losers {
	all c: Candidate | c in Scenario.losers iff
		(c.outcome = Loser or c.outcome = EarlyLoser or c.outcome = SoreLoser or
		c.outcome = TiedLoser or c.outcome = TiedEarlyLoser or c.outcome = TiedSoreLoser)
}

-- Non-Independent Axioms for Ballot Integrity
fact transfers {
	all c: Candidate | 0 < #c.transfers implies Election.method = STV
}

fact onlyWinnersAndQuotaWinnersHaveSurplus {
	all c: Candidate | 0 < #c.surplus implies
		c.outcome = Winner or c.outcome = QuotaWinner
}

fact winnersNeedNoTransfers {
		all c: Candidate | c.outcome = Winner implies 0 = #c.transfers
}

fact firstPreference {
	all b: Ballot | all c: Candidate | b.preferences.first = c iff b in c.votes
}

fact sizeOfSurplus {
	all c: Candidate | (c in Scenario.winners and Election.method = STV and 0 < #c.surplus) implies
		#c.surplus = #c.votes + #c.transfers - Scenario.quota
}

fact transferToNextPreference {
	all b: Ballot | all disj donor,receiver: Candidate |
		(donor + receiver in b.assignees and
		b in receiver.transfers and b in donor.surplus implies
		b.preferences.idxOf[donor] < b.preferences.idxOf[receiver] and
		receiver in b.preferences.rest.elems)
}

fact pluralityWinner {
	all disj a, b: Candidate | (Election.method = Plurality and Election.seats = 1) and
		a.outcome = Winner implies #b.votes <= #a.votes
}

fact transferToNextContinuingCandidate {
	all disj skipped, receiving: Candidate | all b: Ballot |
		b.preferences.idxOf[skipped] < b.preferences.idxOf[receiving] and
		receiving in b.assignees and (not skipped in b.assignees) implies
		(skipped in Scenario.eliminated or skipped.outcome = Winner or
		skipped.outcome = QuotaWinner)
}

// All ballots transfers are associated with the last candidate to receive the transfer
fact ownership {
	all disj c,d: Candidate | all b: Ballot | b in c.transfers implies c in b.assignees and
		(d not in b.assignees or b.preferences.idxOf[d] < b.preferences.idxOf[c])
}

// Lowest candidate is eliminated first
fact lowestElimination {
	all disj c,d: Candidate | c in Scenario.eliminated and d not in Scenario.eliminated implies
		#c.votes + #c.transfers <= #d.votes + #d.transfers
}

// All ties involve equality between at least one winner and at least one loser on either original votes or
// on transfers plus original votes
fact equalityOfTiedWinner {
   all w: Candidate | some l: Candidate | w.outcome = TiedWinner and
	 	(l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome = TiedEarlyLoser) implies
		(#l.votes = #w.votes) or (#l.votes + #l.transfers = #w.votes + #w.transfers)
}

fact equalityOfTiedLosers {
  all s: Candidate | some w: Candidate | w.outcome = TiedWinner and
       (s.outcome = SoreLoser or s.outcome = TiedLoser or s.outcome = TiedEarlyLoser) implies
       (#s.votes = #w.votes) or (#s.votes + #s.transfers = #w.votes + #w.transfers)
}

// Ranking of votes on ballots
fact firstPreference {
	all v: Vote | some b: Ballot | v.ballot = b.identifier and
		v.ranking = 1 implies b.top = v.candidate
}

fact rankingOfVotes {
   	all v: Vote | some b: Ballot | some c: Candidate |
		v.ballot = b.identifier and v.candidate = c.identifier implies
	    c in b.preferences.elems and v.ranking = b.preferences.idxOf[c]
}

-- Scenario Validity Axioms
// When there is a tied sore loser then there are no non-sore losers
fact typeOfTiedLoser {
   no disj a,b: Candidate | a.outcome = TiedSoreLoser and
        (b.outcome = TiedLoser or b.outcome = TiedEarlyLoser or
         b.outcome=Loser or b.outcome=EarlyLoser)
}

fact tieBreaker {
	all w: Candidate | some l: Candidate | w.outcome = TiedWinner implies
		(l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome = TiedEarlyLoser)
}

fact validTieBreaker {
	all l: Candidate | some w: Candidate |
    (l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome = TiedEarlyLoser) implies
    w.outcome = TiedWinner
}

-- Basic Lemmas
assert honestCount {
	  all c: Candidate | all b: Ballot | b in c.votes + c.transfers implies c in b.assignees
}
check honestCount for 15 but 6 int

assert atLeastOneLoser {
	0 < #Scenario.losers
}
check atLeastOneLoser for 15 but 6 int

assert atLeastOneWinner {
	0 < #Scenario.winners
}
check atLeastOneWinner for 14 but 6 int

assert wellFormednessOfBallots {
	all b: Ballot | b.assignees in Election.candidates
}
check wellFormednessOfBallots for 18 but 6 int

assert wellFormedBallotBox {
		#BallotBox.ballots = sum (#Election.candidates.votes)
}
check wellFormedBallotBox for 14 but 6 int

assert plurality {
	all c: Candidate | all b: Ballot | b in c.votes and
		Election.method = Plurality implies c in b.preferences.first
}
check plurality for 18 but 6 int

assert wellFormedTieBreaker {
	some w,l : Candidate | (w in Scenario.winners and l in Scenario.losers and
		#w.votes = #l.votes and #w.transfers = #l.transfers) implies
		w.outcome = TiedWinner and
		(l.outcome = TiedLoser or l.outcome = TiedSoreLoser or l.outcome=TiedEarlyLoser)
}
check wellFormedTieBreaker for 18 but 6 int

assert validSurplus {
	all c: Candidate | 0 < #c.surplus implies (c.outcome = Winner or c.outcome = QuotaWinner or
		c in Scenario.eliminated)
}
check validSurplus for 16 but 6 int

-- Advanced Lemmas
// No lost votes during counting
assert accounting {
	all b: Ballot | one c: Candidate | b in c.votes and c in b.assignees
}
check accounting for 16 but 6 int

// No ballots created or destroyed during counting
assert immutability {
	all c: Candidate | c.votes + c.transfers in BallotBox.ballots and c.surplus in BallotBox.ballots
}
check immutability for 16 but 6 int

// Cannot have tie breaker with both tied sore loser and non-sore loser
assert tiedWinnerLoserTiedSoreLoser {
	no disj c,w,l: Candidate | c.outcome = TiedSoreLoser and
	   w.outcome = TiedWinner and (l.outcome = Loser or l.outcome = TiedLoser)
}
check tiedWinnerLoserTiedSoreLoser for 6 int

-- Sample scenarios
pred TwoCandidatePlurality {
	Election.method = Plurality
	Election.seats = 1
	#Election.candidates = 2
}
run TwoCandidatePlurality for 10 but 1 Ballot, 6 int

pred PluralityTiedWinner {
	Election.method = Plurality
	Election.seats = 1
	some disj a,b: Candidate | a.outcome = TiedWinner and b.outcome = TiedLoser
}
run PluralityTiedWinner for 10 but 2 Ballot, 6 int

pred ThreeCandidatePlurality {
	Election.method = Plurality
	Election.seats = 1
	#Election.candidates = 3
}
run ThreeCandidatePlurality for 6 int

pred Plurality {
	Election.method = Plurality
	Election.seats = 1
	3 < #Election.candidates
}
run Plurality for 5 but 6 int

pred TenCandidatePluralityWithTwoSeats {
	Election.method = Plurality
	Election.seats = 2
	#Election.candidates = 10
}
run TenCandidatePluralityWithTwoSeats for 10 but 6 int

pred InstantRunoffVoting {
	Election.method = STV
	Election.seats = 1
	3 < #Election.candidates
}
run InstantRunoffVoting for 10 but 6 int

pred STV3 {
	Election.method = STV
	Election.seats = 3
}
run STV3 for 5 but 6 int

pred ThreeWinnersOneLoser {
	some a: Candidate | a.outcome = Winner
   	some b: Candidate | b.outcome = QuotaWinner
	some c: Candidate | c.outcome = CompromiseWinner
	some d: Candidate | d in Scenario.losers
}
run ThreeWinnersOneLoser for 8 but 6 int

pred LoserAndEarlyLoser {
	one d: Candidate | d.outcome = Loser
	one e: Candidate | e.outcome = EarlyLoser
}
run LoserAndEarlyLoser for 5 but 6 int

pred OneWinnerNineLosers {
	#Scenario.winners = 1
	#Scenario.losers = 9
}
run OneWinnerNineLosers for 16 but 6 int

pred TwentyFiveBallots {
	#BallotBox.ballots = 25
}
run TwentyFiveBallots for 25 but 6 int

pred ThreeWayTie {
	some disj a,b,c: Candidate | a.outcome = TiedWinner and b.outcome = TiedLoser and
		c.outcome = TiedLoser
}
run ThreeWayTie for 5 but 6 int

pred FiveWayTie {
	some disj a,b,c,d,e: Candidate | a.outcome = TiedWinner and b.outcome = TiedLoser and
		c.outcome = TiedLoser and d.outcome = TiedLoser and e.outcome = TiedWinner
}
run FiveWayTie for 7 but 6 int

pred TwentyCandidates {
	#Election.candidates = 20
	some c: Candidate | 0 < #c.votes and c in Scenario.losers
}
run TwentyCandidates for 20 but 6 int

pred Outcomes {
 	some disj a,b,c0,d,e,f,g,h,i: Candidate |
		(a.outcome = Winner and b.outcome = QuotaWinner and
		c0.outcome = CompromiseWinner and d.outcome = Loser and
		e.outcome = EarlyLoser and f.outcome = SoreLoser and
		g.outcome = TiedWinner and h.outcome = TiedLoser and i.outcome = TiedEarlyLoser)
}
run Outcomes for 10 but 6 int

pred FifteenDifferentBallots {
	#BallotBox.ballots = 15
	no disj a,b: Ballot | a.preferences.first = b.preferences.first and #a.preferences = #b.preferences and
		a.preferences.last = b.preferences.last and #a.assignees = #b.assignees
}
run FifteenDifferentBallots for 15 but 6 int

pred ScenarioLWW {
	some a: Candidate | a.outcome = Loser
	some disj b,c: Candidate | b.outcome = Winner and c.outcome = Winner
    #Election.candidates = 3
}
run ScenarioLWW for 6 int

pred AnyScenarioWithTiedSoreLoser {
	some c: Candidate | c.outcome = TiedSoreLoser
}
run AnyScenarioWithTiedSoreLoser for 6 int

-- Utility functions

fun length [b : Ballot] : Int {
  #b.preferences
}


making

making

open util/ordering[Time]

abstract sig Command {}

one sig MFence{}
one sig LFence{}
one sig SFence{}

abstract sig Load extends Command{}
abstract sig Store extends Command{}
sig Movl extends Store{
	value: Int,
	target: (0 + 1) -> Time
}
sig CmplJe extends Load{
	value: Int,
	target: (0 + 1) -> Time,
	jump_to: Int
}
one sig CriticalSection extends Store{}

fun movl(_value: Int, _target: (0 + 1) -> Time): Movl{
	{m: Movl | m.value = _value and m.target = _target}
}
fun cmpl_je(_value: Int, _target: (0 + 1) -> Time,
		_jump_to: Int): CmplJe{

	{m: CmplJe |
		m.value = _value and
		m.target = _target and
		m.jump_to = _jump_to
	}
}

sig Time{
	pc: lone Int
}

fact{
	no last.pc
}

sig R{
	// その時刻にまだ実行されていないコマンドの列
	code: (0 + 1) -> Int -> Command -> Time,
	// memory
	turn: (0 + 1) -> Time,
	flag: (0 + 1) -> (0 + 1) -> Time
	//
}{
	all t: Time {
		one turn.t
		one flag[0].t
		one flag[1].t
		(turn + flag[0] + flag[1]).t in (0 + 1)
		no flag[Int - 0 - 1].t
	}

	let t = first {
		// 最初はメモリはみんな0
		turn.t = 0
		flag[0].t = 0
		flag[1].t = 0
 	}

	code.first = (0 -> (
		(0 -> movl[1, flag[0]]) +
		(1 -> movl[1, turn]) +
		(2 -> cmpl_je[0, flag[1], 4]) +
		(3 -> cmpl_je[1, turn, 2]) +
		(4 -> CriticalSection) +
		(5 -> movl[0, flag[0]]) +

		(6 -> movl[1, flag[0]]) +
		(7 -> movl[1, turn]) +
		(8 -> cmpl_je[0, flag[1], 10]) +
		(9 -> cmpl_je[1, turn, 8]) +
		(10 -> CriticalSection) +
		(11 -> movl[0, flag[0]])
	) + (1 -> (
		(0 -> movl[1, flag[1]]) +
		(1 -> movl[1, turn]) +
		(2 -> cmpl_je[0, flag[0], 4]) +
		(3 -> cmpl_je[1, turn, 2]) +
		(4 -> CriticalSection) +
		(5 -> movl[0, flag[1]])
	)))
}
/*
LBB4_1:
        movl    $1, _flag0(%rip)
        movl    $1, _turn(%rip)
LBB4_2:
        cmpl    $0, _flag1(%rip)
        je      LBB4_4
        cmpl    $1, _turn(%rip)
        je      LBB4_2
LBB4_4:
        incl    _counter(%rip)
        movl    $0, _flag0(%rip)
*/

pred InOrder {
	// 選ばれた命令より前に未実行の命令はない
	// 古き良き時代のインオーダー実行
}
pred Intel {
	// 同一メモリに対するLoadとStoreは実行順を入れ替えない
	// 多分Intelはこの挙動
}
pred step {
	// 各時刻に守られているべき制約

}
run {
} for 5 Int

error: pathspec 'Peterson_OoO.als' did not match any file(s) known to git


Split long line

Split long line

Applied Logic Systems, Inc.'s "Copying ALS"

This is the file "Copying ALS".  It applies to files which are marked

    Copyright (c) xxxx Applied Logic Systems, Inc.
    Distribution rights per Copying ALS

This document is Copyright (c) 1996-9 Applied Logic Systems, Inc., and may
be distributed verbatim, but changing it is not permitted.

You may freely copy, modify, and distribute any source file marked

    Copyright (c) xxxx Applied Logic Systems, Inc.
    Distribution rights per Copying ALS

subject to the following restrictions:

i)  You must preserve intact any and all existing copyright and/or authorship
information in any of the files you redistribute.  If you modify a source file,
you must add a notice to that effect to the authorship information in the
source file.  If you substantially merge one of the files into another file,
you must transfer the existing copyright and/or authorship information
to the newly merged file.

ii) You may freely incorporate these files in a binary application distributed
under the terms of the "ALS Binary License", either bound into the application,
or as "obp" files to be loaded.  Your are strongly encouraged to abide by
the spirit of the free software concept, and to
   a) Provide authorship attributions as appropriate in your printed and/or
      on-line documentation, and on your start-up screen(s) if this is
      appropriate, and to
   b) Provide sufficient information so that the recepients of your programs
      can obtain the sources to the programs you incorporated.

iii) No act of distribution and/or incorporation of any of these files, in whole
or in part, will give you proprietary rights or ownership over these files, in
whole or in part.  Any attempt to exercise such rights will automatically
abrogate your rights under this license.

WARRANTY
This program or code file is provided "as is " without warranty of any kind,
either expressed or implied, including, but not limited to, implied warranties
of merchantability and fitness for a particular purpose.  You assume the
entire risk as to the selection of this program to achieve your intended
results and for the installation, use, performance, and results obtained
through the use of this program or code.


mardar 2

mardar 2

open util/ordering[Time]

//登場人物
abstract sig Person {
	belief: Person -> Person -> Place -> Time,
	// 最終的にどこへ行ったか
	real_place: Place -> Time,
	is_killer: lone Person // as bool
}{
	all t: Time | lone real_place.t
}

one sig Me extends Person{} // 読者
one sig PHP extends Person{} // 殺され役
one sig Perl extends Person{}
one sig Python extends Person{}
one sig Ruby extends Person{}
one sig JS extends Person{}

abstract sig Time {}
sig BeforeMardar extends Time {
	// 各時刻に誰かが誰かに自分がどこに行くかを伝える
	who: some Person,
	targets: some Person,
	where: Place
}{
	// 話し手は聞き手に含まれる
	who in targets
	// 聞き手が話し手と同一ではいけない
	some targets - who
}
one sig Mardar extends Time {}
sig AfterMardar extends Time {}

sig Place {}

fact {
	// 同じ人が複数箇所に存在すると信じることはない
	all p1, p2, p3: Person, t: Time{
		lone (p1.belief)[p2, p3].t
	}
}

pred init(t: Time){
	all p: Person {
		no p.belief.t
	}
	all p: Person {
		no p.real_place.t
	}
}

pred no_change(t: Time, changable: univ -> Time){
	changable.t = changable.(t.prev)
}

pred step(t', t: Time){
	all p: Person {
		(p in t.targets) => {
			// 聞いた人pの信念が上書きされる
			// 一緒に聞いていた人(自分を含む)の信念が上書きされる
			all p2: Person {
				(p2 in t.targets) => {
					p.belief[p2].t = p.belief[p2].t' ++ (t.who -> t.where)
				}else{
					// それ以外は変化しない
					p.belief[p2].t = p.belief[p2].t'
				}
			}
		}else{
			// 聞き手以外は信念が変化しない
			p.belief.t = p.belief.t'
		}
		(p in t.who) => {
			// 自分が言ったところに行く(嘘はつかない)
			// 犯人はこれを守らなくてよいように緩めても良い
			p.real_place.t = t.where
		}else{
			p.real_place.t = p.real_place.t'
		}
	}
}

pred on_mardar(t', t: Time){
	all p: Person {
		let go = real_place.t',
				place = p.go,
				meets = go.place {
			// 全員どこかには行った
			one place
			p.real_place.t = place // 犯行時刻にそこにいた
			// 犯行時刻に同じ場所に行った人は信念が更新される
			p.belief[p].t = p.belief[p].t' ++ (meets -> place)
		}
	}
	// 犯人と殺され役だけが犯行時刻に同じ場所にいた
	PHP.real_place.t.~(real_place.t) = PHP + (is_killer.univ)
}

fact {
	BeforeMardar = Mardar.prevs
	AfterMardar = Mardar.nexts
	init[first]
	all t: BeforeMardar - first{
		step[t.prev, t]
	}
	on_mardar[Mardar.prev, Mardar]
	//final[last.prev, last]
}

// whoと同じ場所にいた可能性があるのは？
fun same_place(self: Person, who: Person): Person{
	let where = (self.belief)[self].last {
		who.where.~where + {p: Person | no p.where} - who
	}
}

run {
	one is_killer // 犯人は一人だけ
	// Ruby, Python, PHP, 読者は犯人ではない
	no (Ruby + Python + Me + PHP).is_killer
	// 犯人は自分以外はみな自分が犯人だと考えないと考えている
	let killer = is_killer.univ {
		all other: Person - killer - PHP{
			let where = (killer.belief)[other].last {
				killer not in PHP.where.~where
			}
		}
	}
	// 読者は当初PythonかRubyが犯人だと考えている
	same_place[Me, PHP] = Python + Ruby
	// RubyはPythonが犯人だと、BはAが犯人だと考えている
	same_place[Ruby, PHP] = Python
	same_place[Python, PHP] = Ruby
	// 読者がPythonとRuby
} for 7 Time, 3 Place

error: pathspec 'mardar2.als' did not match any file(s) known to git


Edit alloy model

Edit alloy model

module PowEnj
//SIGNATURES
sig Position
{
latitude:Int, //should  be float
longitude:Int //should  be float
}

sig Visitor{
}

sig DriverLicense{
code:String,
expiration:Int,
drivername:String,
driversurname:String
}

sig User
{
rentedCar: one Car, //field that indicates which car the user is using at the moment
payInfo: some Payment,
position: one Position,
name:String,
surname:String,
license: one DriverLicense,
email:String
}

sig Rent
{
startTime = Int, //should be Float
rentedCar: one Car,
Renter: one User,
applyDiscount: Int,
applyOvertax: Int,
carsAvailable: set Car,
maxDistance: Int, //should be float
initialPos: Position,
finalPos: Position,
totalCost:Int,
passengers: Int // don’t remember if the class (and consequently the signature) should exist or               we decide to remove it; in this case, change Passenger with User
}

sig Plug{}
abstract sig Station{
parkedCar:set Car,
positoin:one Position
}
sig  Parking extends Station{
distanceFromCharge:Int
}
sig safeArea extends Station
{
carsToCharge: set Car,
plugAvailable: set Plug,
}

sig Payment
{
transactionCode: Int,
payInfo : set Payment
}
sig Car
{
user: one User,
position:one Position,
battery: Int,
plate: String
}

fact driverIsUnique
{
//can’t exist two renters who rent the same car at the same time
all r1,r2:Rent | (r1.Renter!=r2.Renter) => r1.rentedCar != r2.rentedCar and r1.startTime!=r2.startTime
}

fact applyDiscountForBattery
{
//if the battery at the end of the trip is at least at 50%, apply a discount
//not sure if it’s possible to use this notation!!
all c:Car, r:Rent | (c.battery>=50) => r.applyDiscount

//alternatively, we can suppose that the attribute “isCharge” is set to a specific value for every kind of discount appliable
all c:Car, r:Rent | (c.isCharge) => r.applyDiscount

}
fact applyDiscountForArea
{
//if the car at the end of trip is let in a service station, apply a discount
all r:Rent, s:ServiceStation | (r.finalPosition=s.position) => r.applyDiscount
}

fact applyOvertax
{
all c:Car, r:Rent, s:Parking | (!(c.isCharge)) => r.applyOvertax or  ((s.distanceFromCharge)>r.maxDistance) => r.applyOvertax
}

fact plateIsUnique {
all  c1,c2: Car | (c1 != c2) => c1.plate != c2.plate
}

fact userIsUnique {
all u1,u2: User | u1 != u2 => u1.email != u2.email
}

fact pathDriverHasAStartAndEnd
{
all r:Rent | (r.RentedCar) => r.InitialPosition and r.finalPosition

}

//30.10.2016

fact rentIfAvailable
{
all u:user, r:rent | r.startTime  u.rentedCar in r.carsAvailable
}

fact noSafeAreaParkNoEndTrip
{
//I can terminate my trip if and only if my car’s position coincides with a parking position or a safa area position
all c:Car, r:Rent, s:Station | r.endTime <=> (c.position = s.position)
}

fact applyDiscountForPassNumb
{
//for every trip that involves a number of passengers >=3, apply a discount for the renter
all r:Rent | (#r.passengers>= 3) => r.applyDiscount
}

assert userMustHaveAtLeastOnePaymentInfo
{
no u:User, p:Payment | u.payInfo not in p.payInfo
}

module PowEnj
//SIGNATURES
sig Position
{
latitude:Int, //should  be float
longitude:Int //should  be float
}

abstract sig User
{
rentedCar: one Car, //field that indicates which car the user is using at the moment
payInfo: some Payment,
position: one Position
}

fact driverIsUnique
{
//can’t exist two renters who rent the same car at the same time
all r1,r2:Rent | (r1.Renter!=r2.Renter) => r1.rentedCar != r2.rentedCar and r1.startTime!=r2.startTime
}

fact applyDiscountForBattery
{
//if the battery at the end of the trip is at least at 50%, apply a discount
//not sure if it’s possible to use this notation!!
all c:Car, r:Rent | (c.battery>=50) => r.applyDiscount

//alternatively, we can suppose that the attribute “isCharge” is set to a specific value for every kind of discount appliable
all c:Car, r:Rent | (c.isCharge) => r.applyDiscount

}
fact applyDiscountForArea
{
//if the car at the end of trip is let in a service station, apply a discount
all r:Rent, s:ServiceStation | (r.finalPosition=s.position) => r.applyDiscount
}

fact applyOvertax
{
all c:Car, r:Rent, s:ServiceStation | (!(c.isCharge)) => r.applyOvertax or  ((s.position – c.position)>r.maxDistance) => r.applyOvertax
}

sig Rent
{
startTime = Int, //should be Float
rentedCar: set Car,
Renter: one User,
applyDiscount: one User,
applyOvertax: one User,
carsAvailable: set Car,
maxDistance: Int, //should be float
initialPos: Position,
finalPos: Position,
passengers: set Passenger // don’t remember if the class (and consequently the signature) should exist or               we decide to remove it; in this case, change Passenger with User
}

sig Car
{
user: one User,
position:Position,
battery: Int,
plate: String
}{#passengers>0}

fact plateIsUnique {
all  c1,c2: Car | (c1 != c2) => c1.plate != c2.plate
}
sig Plug{}
abstract sig safeArea {}
sig serviceStation extends safeArea
{
carsToCharge: set Car,
plugAvailable: set Plug,
position:Position
}
sig Payment
{
transactionCode: Int,
payInfo : set Payment
}

fact userIsUnique {
all u1,u2: User | u1 != u2 => u1.email != u2.email
}

fact pathDriverHasAStartAndEnd
{
all r:Rent | (r.RentedCar) => r.InitialPosition and r.finalPosition

}

//30.10.2016

fact rentIfAvailable
{
all u:user, r:rent | r.startTime  u.rentedCar in r.carsAvailable
}

fact noSafeAreaParkNoEndTrip
{
//I can terminate my trip if and only if my car’s position coincides with a parking position or a safa area position
all c:Car, r:Rent, p:Parking, s:SafeArea | r.endTime <=> (c.position = p.position) or (c.position = s.position)
}

assert allCarsWithPosition
{
all c:Car | c.position
}

fact applyDiscountForPassNumb
{
//for every trip that involves a number of passengers >=3, apply a discount for the renter
all r:Rent | (#r.passengers>= 3) => r.applyDiscount
}

assert userMustHaveAtLeastOnePaymentInfo
{
no u:User, p:Payment | u.payInfo not in p.payInfo
}


Revert "mappings: remove unused experiment"

This reverts commit e4da5cad5d3f7878bb65ab8b9704c9ab4a8951c0.

open ../archs/exec_C[SE] as SW
open ../archs/exec_x86[HE] as HW

/*
A C11-to-x86 mapping that implements SC atomics using
atomic hardware events. Currently broken.
*/

module c11_x86a[SE,HE]

fun myfr_init [X:HW/Exec_X86] : HE->HE {
  ((stor[X.R]) - ((~(X.rf)) . (X.rf))) . (X.sloc) . (stor[X.W])
}

fun myfr [X:HW/Exec_X86] : HE->HE {
  (((myfr_init[X]) + ((~(X.rf)) . (X.co))) - iden)
}

pred apply_map[X:SW/Exec_C, X':HW/Exec_X86, map:SE->HE] {

  // two SW events cannot be compiled to a single HW event
  map in X.EV lone -> X'.EV

  // HW reads/writes cannot be invented by the compiler
  all e : X'.(R+W) | one e.~map

  // SW reads/writes cannot be discarded by the compiler
  all e : X.(R+W) | some e.map

  // a read compiles to a single non-locked read
  all e : X.(R - W) {
    one e.map
    e.map in X'.R - (X'.atom).univ
  }

  // a non-SC write compiles to a single non-locked write
  all e : X.((W - R) - SC) {
    one e.map
    e.map in X'.W - univ.(X'.atom)
  }

  // an SC write compiles to an RMW
  all e : X.((W - R) & SC) | some disj e1,e2 : X'.EV {
    e.map = e1 + e2
    e1 in X'.R
    e2 in X'.W
    (e1 -> e2) in X'.atom & imm[X'.sb]

    // read does not observe a too-late value
    (e2 -> e1) not in ((X'.co) . (X'.rf))

    // read does not observe a too-early value
    (e1 -> e2) not in ((myfr[X']) . (X'.co))
  }

  // RMWs compile to locked RMWs
  all e : X.(R & W) | some disj e1, e2 : X'.EV {
    e.map = e1 + e2
    e1 in X'.R
    e2 in X'.W
    (e1 -> e2) in X'.atom & imm[X'.sb]
  }

  // SC fences compile to full fences
  all e : X.(F & SC) {
    (X.sb) . (stor[e]) . (X.sb) = map . (mfence[none->none,X']) . ~map
  }

  // sb edges are preserved (but more may be introduced)
  X.sb in map . (X'.sb) . ~map

  // rf edges (except those entering the RMWs induced
  // by SC writes) are preserved
  let newR = X'.R & map[X.((W - R) & SC)] |
  X'.rf - (X'.EV -> newR) = ~map . (X.rf) . map

  // the mapping preserves co
  X.co = map . (X'.co) . ~map

  // the mapping preserves address dependencies
  X.ad = map . (X'.ad) . ~map

  // the mapping preserves data dependencies
  X.dd = map . (X'.dd) . ~map

  // the mapping preserves locations
  X.sloc = map . (X'.sloc) . ~map

  // the mapping preserves control dependencies
  X.cd in map . (X'.cd) . ~map

  // the mapping preserves threads
  X.sthd = map . (X'.sthd) . ~map

  // the mapping preserves transactions
  X.stxn = map . (X'.stxn) . ~map
  X.ftxn = map . (X'.ftxn) . ~map

}

error: pathspec 'mappings/c11_x86a.als' did not match any file(s) known to git


open util/ordering[Time]

sig Time {}

sig Person {
	state: State -> Time,
}

enum State {Married, NotMarried}

pred init (t: Time) {
  all p: Person | p.state.t = NotMarried
}

pred step (t, t': Time) {
	some disj p1, p2 : Person {
		{
			// marrige
			p1.state.t = NotMarried and p2.state.t = NotMarried
			p1.state.t' = Married and p2.state.t' = Married
		} or {
			// divorce
			p1.state.t = Married and p2.state.t = Married
			p1.state.t' = NotMarried and p2.state.t' = NotMarried
		}
	}
}

fact Traces {
	init[first]
	all t: Time - last {
		step[t, t.next]
	}
}

run {} for exactly 3 Person, 5 Time

error: pathspec 'etude16.als' did not match any file(s) known to git


make puzzle

make puzzle

enum Person {A, B, C, D, E}
enum Bool {T, F}

// 制約
abstract sig Constrain{}
// 「Xが『Yは嘘つきだ』と言った」という制約
sig is_liar extends Constrain {
	by: one Person,
	who: one Person
}{
	by not in who
}

fact {
	// 違う人は違うことを言う
	all disj p1, p2: Person {
		by.p1 != by.p2
	}
	// 一人の人は3人以上を嘘つき呼ばわりしない
	all p: Person {
		#{by.p} < 3
	}
}

// 与えられたx, y, zの真偽値の対が制約を満たすかどうか返す
// 引数を変えて試すため、述語にくくり出されている必要がある
pred satisfy(cs: Constrain, a, b, c, d, e: Bool){
	let p2b = (A -> a) + (B -> b) + (C -> c) +
		(D -> d) + (E -> e)
		{
		// 嘘つきの人数を指定
		#{p2b.F} = 3
		// すべての制約について、発言者が正直なら充足される
		// 今は「whoは嘘つき」しかない
		all c: cs{
			(c.by.p2b = T) => (c.who.p2b = F)
		}
	}
}

run {
	let answers = {
		a, b, c, d, e: Bool |
		satisfy[Constrain, a, b, c, d, e]}
		{

		// 解は一つ
		one answers

		// どの制約を取り除いても解は一つではなくなる
		all x: Constrain {
			not one {
				a, b, c, d, e: Bool |
				satisfy[Constrain - x, a, b, c, d, e]
			}
		}
	}
} for 10

enum Person {X, Y, Z}
enum Bool {T, F}

// 制約
abstract sig Constrain{}
// 「Xが『Yは嘘つきだ』と言った」という制約
sig is_liar extends Constrain {
	by: one Person,
	who: some Person
}

// 与えられたx, y, zの真偽値の対が制約を満たすかどうか返す
// 引数を変えて試すため、述語にくくり出されている必要がある
pred satisfy(cs: Constrain, x, y, z: Bool){
	let p2b = (X -> x) + (Y -> y) + (Z -> z) {
		// only one liar
		one p2b.F
		// すべての制約について、発言者が正直なら充足される
		// 今は「whoは嘘つき」しかない
		all c: cs{
			(c.by.p2b = T) => (c.who.p2b = F)
		}
	}
}

run {
	let answers = {
		x, y, z: Bool |
		satisfy[Constrain, x, y, z]}{

		one answers
	}
}

fact {
	all c: Constrain {
		c.by not in c.who
		one c.who
	}
}


updated alloy

updated alloy

open util/boolean

// Car, User

sig Car {
	id: Int,
	available: Bool,
	location: Location,
	unlocked: Bool,
	battery_level: Int
} {
	id>0
	battery_level >= 0
	battery_level <= 10
}

sig User {
	credential: Credential,
	payment_info: Payment_Info,
	location: Location
}
sig Credential {}

sig Payment_Info {}

// Reservation, Ride

sig Reservation {
	user: User,
	car: Car,
	start_area: Safe_Area,
	start_time: Time,
	current_cost: Int,
	final_discharged_cost: lone Int,
	expired: Bool,
	ride: lone Ride
} {
	current_cost >= 0
	final_discharged_cost >= 0
}

sig Ride {
	reservation: Reservation,
	passengers: Int,
	pickup_time: Time,
	release_time: lone Time,
	release_battery_level: lone Int,
	release_area: lone Safe_Area,
	release_plugged: lone Bool
} {
	passengers >= 1
	passengers <= 4
	release_battery_level >= 0
	release_battery_level <= 10
	not areEqual[pickup_time, release_time]
}

// Time and CurrentTime

sig Time {
	hour: Int,
	minute: Int
} {
	hour >= 0
	hour =< 23
	minute >= 0
	minute =< 59
}

sig CurrentTime extends Time {}

// Location, Areas

sig Location {
	latitude: Int,
	longitude: Int
}
sig Area {
	center: Location,
}

sig Safe_Area extends Area {}
sig Special_Safe_Area extends Safe_Area {}

/* ----------- Functions ------------ */
fun minutesDiff[t, t': Time]:Int {
	t.minute>t'.minute
	implies
		sub[t.minute, t'.minute]
	else
		sub[t'.minute, t.minute]
}

fun hoursDiff[t, t': Time]: Int {
	t.hour>=t'.hour
	implies(
		sub[t.hour, t'.hour]
	) else (
		sub[t'.hour, t.hour]
	)
}

//Return the cost for a ride from time t to time t'
fun cost[t, t': Time]: Int {
		t.hour = t'.hour
		implies (
			minutesDiff[t, t']
		)
		else (
			t.hour>t'.hour
			implies(
				add[add[sub[60, t'.minute], mul[sub[hoursDiff[t,t'], 1], 60]], t.minute]   //(60-t'.minute) + (t'.hour-t.hour-1)*60 + t.minute
			)
			else(
				add[add[sub[60, t.minute], mul[sub[hoursDiff[t,t'], 1], 60]], t'.minute]   //(60-t.minute) + (t.hour-t'.hour-1)*60 + t'.minute
			)
		)
}
// ----------- Predicates -----------

// Returns true if t == t'
pred areEqual[t, t': Time] {
	t.hour = t'.hour and t.minute = t'.minute
}

// Returns true if t - t' <= 1h
pred isLessThanOneHourAhead[t, t': Time] {
	areEqual[t, t']
	or
		(t.hour = t'.hour and t.minute > t'.minute)
	or
		(t.hour = add[t'.hour,1] and add[sub[60, t'.minute], t.minute] < 60)
}

// Returns true if t >= t'
pred comesAfterOrEqual[t, t': Time] {
		t.hour = t'.hour
		implies t.minute >= t'.minute
		else t.hour >= t'.hour
}

// Returns true if the max distance between latitudes and longitudes is 5
pred isNear[l, l': Location] {
	(l.latitude >= l'.latitude implies
		sub[l.latitude, l'.latitude] <= 4
	else
		sub[l'.latitude, l.latitude] <= 4)
	and
	(l.longitude >= l'.longitude implies
		sub[l.longitude, l'.longitude] <= 4
	else
		sub[l'.longitude, l.longitude] <= 4)
}

pred isFarFromSpecialSafeArea[area: Safe_Area] {
			all ssa: Special_Safe_Area |(
			(int ssa.center.latitude >= int area.center.latitude
			implies
				sub[ssa.center.latitude, area.center.latitude] > 15
			else
				sub[area.center.latitude, ssa.center.latitude] > 15
			)
			and
			(int ssa.center.longitude >= int area.center.longitude implies
				sub[ssa.center.longitude, area.center.longitude] > 15
			else
				sub[area.center.longitude, ssa.center.longitude] > 15)
	)
}
// Reservation r is open <=> r is not expired and "it has not a ride yet or its ride has no release_time"
pred isActive[r: Reservation] {
	r.expired = False and (
		no r.ride
		or
		no r.ride.release_time
	)
}

// ----------- Facts -------------

// There's only a current time
fact currentTimeForeverAlone {
	#CurrentTime = 1
}

// CurrentTime is after all the times in the model
fact currentTimeIsAlwaysAhead {
	all t: Time |
		all ct: CurrentTime |
			comesAfterOrEqual[ct, t]
}

// Each credential/password/payment info is used for a user
fact eachCredentialCorrespondToAUser {
	User.credential=Credential
}

fact eachPaymentInfoCorrespondToAUser {
	User.payment_info=Payment_Info
}

// Different users <=> different credentials
fact noUsersWithSameCredentials {
	all u1, u2: User |
		u1 != u2
		iff
		u1.credential != u2.credential
}

// Different cars <=> different IDs
fact noCarsWithSameIDs {
	all c1, c2: Car |
		c1 != c2
		iff
		c1.id != c2.id
}

// If a ride has a reservation, then that reservation has that ride.
fact ridesAndReservationRelation {
	all rid: Ride | (
		rid = rid.reservation.ride
	) and
	all res: Reservation | (
		one res.ride
		implies
			res=res.ride.reservation
	)
}

//A car can be released plugged only if it is released in a Special Safe Area
fact pluggedOnlyInSafeArea{
	all rid: Ride |(
		rid.release_plugged=True
		implies
		(rid.release_area in Special_Safe_Area)
	)
}

// There can't be two reservations r1 and r2 for the same car overlapping.

fact carsCanBeReservedByOneUserAtOnce {
	all c: Car | (
		all r, r': Reservation | (
			(r.car = c and r'.car = c and r != r' and comesAfterOrEqual[r'.start_time, r.start_time])
			implies (
					(some r.ride and some r.ride.release_time and comesAfterOrEqual[r'.start_time, r.ride.release_time])
					or
					(r.expired = True and not isLessThanOneHourAhead[r'.start_time, r.start_time])
			)
		)
	)
}
//A user can reserve a car only if it has a battery level grater than 80%
fact carReservableCondition {
	all r: Reservation | (
		(no r.ride and r.expired=False)
		implies
		r.car.battery_level>8
	)
}
// A car is available if it has no reservations open and has enough battery
fact carAvailableCondition {
	all c: Car |
		c.available = True
		iff(
		(no res: Reservation |
			res.car = c and isActive[res])
		)
}

// A reservation is expired if there's is not a ride whose pickup_time is less than one hour after the
//    reservation start_time.
fact reservationExpiredCondition {
	all res: Reservation |
		res.expired = True
		iff
		(all currentTime: CurrentTime |
			not isLessThanOneHourAhead[currentTime, res.start_time]
		)
		and
		no res.ride
}

// The pickup time of a ride should be in one hour from its reservation start_time
fact pickupTimeConstraint {
	all rid: Ride | (
		isLessThanOneHourAhead[rid.pickup_time, rid.reservation.start_time]
	)
}

// release_time is always after pickup_time
fact releaseTimeIsAfterPickupTime {
	all rid: Ride | (
		no rid.release_time
		or
		comesAfterOrEqual[rid.release_time, rid.pickup_time]
	)
}

// A car is unlocked if there's a reservation not expired between a user and that car,
//    such user is near it and
//    he hasn't finished the ride (the ride associated with that reservation
//    doesn't have a release_time).
fact carUnlockedConstraint {
	all c: Car | (
		c.unlocked = True
		iff
		some r: Reservation | (
			r.car = c and isActive[r] and isNear[c.location, r.user.location]
		)
	)
}

//If a car is locked and not plugged to a power grid, its battery level is the same as it has been left after last ride
fact batteryLevelConstraint{
	all res: Reservation | (
		(no res': Reservation | (res!=res' and res.car=res'.car and comesAfterOrEqual[res'.start_time, res.start_time]))
		implies (
			(res.ride.release_plugged=False and res.car.location=res.ride.release_area.location)
			implies
				res.ride.release_battery_level = res.car.battery_level
		)
	)
}
//If there is a release time, there are also a release release_battery and a release_area
fact releaseInfoConstraint{
	all rid: Ride| (
		(some rid.release_time
		iff
		some rid.release_battery_level) and
		(some rid.release_area
		iff
		some rid.release_time) and
		(some rid.release_time
		 iff
		some rid.release_plugged)
	)
}

fact feeConstraint {
	all r: Reservation | (
		r.expired = True
		implies
		r.current_cost = 10 and r.final_discharged_cost = 10
	)
}

//The actual cost of a reservation is proportional to minutes spent in the car
fact costOfAReservationConstraint {
	all res: Reservation | (
		res.expired = False
		implies (
			no res.ride
			implies
				res.current_cost=0
			else(
				no res.ride.release_time
				implies(
					all ct : CurrentTime |
						res.current_cost=cost[ct, res.ride.pickup_time]
				)
				else
					res.current_cost=cost[res.ride.release_time, res.ride.pickup_time]
			)
		)
	)
}

fact finalDischargedCostCalculation {
	all res: Reservation | (
		(one res.ride.release_time and res.ride.passengers>2)
		implies(			 //Enabled to 10% discount
			(res.expired = False and res.ride.release_battery_level>=5)
			implies(		 //Enabled to 10%+20% discount
				 (res.ride.release_plugged = True)
				implies(  //Enablet to 10%+20%+30% discount
					res.final_discharged_cost=div[mul[res.current_cost, 2],5]
				)
				else(       //Enabled to 10%+20% discount
					res.final_discharged_cost=div[mul[res.current_cost, 7],10]
				)
			)
			else(
				(res.ride.release_plugged = True)
				implies(  //Enablet to 10%+30% discount
					res.final_discharged_cost=div[mul[res.current_cost, 3],5]
				)
				else(       //Enabled to 10% discount, but 30% extra charging
					(isFarFromSpecialSafeArea[res.ride.release_area] and res.ride.release_battery_level<3)
					implies (
						res.final_discharged_cost=div[mul[res.current_cost, 6],5]
					)
					else(
						res.final_discharged_cost=div[mul[res.current_cost, 9],10]
					)
				)
			)
		)
		else(
			(res.expired = False and res.ride.release_battery_level>=5)
			implies(		 //Enabled to 20% discount
				 (res.ride.release_plugged = True)
				implies(  //Enablet to 20%+30% discount
					res.final_discharged_cost=div[res.current_cost,2]
				)
				else(       //Enabled to 20% discount
					res.final_discharged_cost=div[mul[res.current_cost, 4],5]
				)
			)
			else(
				(res.ride.release_plugged = True)
				implies(  //Enablet to 30% discount
					res.final_discharged_cost=div[mul[res.current_cost, 7],10]
				)
				else(       //There are no discounts
					(isFarFromSpecialSafeArea[res.ride.release_area] and res.ride.release_battery_level<3)
					implies ( //Extra-charging of 30%
						res.final_discharged_cost=div[mul[res.current_cost, 13],10]
					)
					else( // No discounts orr extra-charging
						res.final_discharged_cost=res.current_cost
					)
				)
			)
		)
	)
}

// ------------- Assertions ---------------
assert availabilityAndLockingChecking {
	all c: Car | ((c.available = True implies c.unlocked = False)			  // car available => car locked
						and (c.unlocked = True implies c.available = False))	  // car unlocked => car not available
}

// reservation expired => no ride
assert expiredEntailsNoRide {
	all r: Reservation | r.expired = True implies (no r.ride and r.final_discharged_cost=10)
}

// reservation active => car not available and there aren't any other active reservations for that car
assert activeEntailsNotAvailable {
	all r: Reservation | isActive[r] implies (r.car.available = False
															and no r': Reservation | r'!=r and isActive[r'] and r.car=r'.car)
}

// FALSE

assert falseByPurpose {
	all r: Reservation | r.start_time = r.ride.release_time
	or
	all ride: Ride |  all ct: CurrentTime| ride.release_time=ct
}

// ------------ Show world! --------------

pred show {
	#User = 1
	#Reservation = 2
	#Ride=1
	#Car=1
	some r: Reservation | (one r.ride.release_time)
//	all rid: Ride | all ct: CurrentTime | rid.release_time!=ct
//	some c: Car | c.unlocked = False

}

//run show for 8 Int
//check availabilityAndLockingChecking for 8 Int
//check activeEntailsNotAvailable for 8 Int
check expiredEntailsNoRide for 8 Int
//check activeEntailsNotAvailable for 7 Int
//check falseByPurpose for 8 Int

open util/boolean

// Car, User

sig Car {
	id: Int,
	available: Bool,
	location: Location,
	unlocked: Bool,
	battery_level: Int
} {
	id>0
	battery_level >= 0
	battery_level <= 100
}

sig User {
	credential: Credential,
	payment_info: Payment_Info,
	location: Location
}
sig Credential {}

sig Payment_Info {}

// Reservation, Ride

sig Reservation {
	user: User,
	car: Car,
	start_area: Safe_Area,
	start_time: Time,
	current_cost: Int,
	final_discharged_cost: lone Int,
	expired: Bool,
	ride: lone Ride
} {
	current_cost >= 0
	final_discharged_cost >= 0
}

sig Ride {
	reservation: Reservation,
	passengers: Int,
	pickup_time: Time,
	release_time: lone Time,
	release_battery_level: lone Int,
	release_area: lone Safe_Area,
	release_plugged: lone Bool
} {
	passengers >= 1
	passengers <= 4
	release_battery_level >= 0
	release_battery_level <= 100
	not areEqual[pickup_time, release_time]
}

// Time and CurrentTime

sig Time {
	hour: Int,
	minute: Int
} {
	hour >= 0
	hour =< 23
	minute >= 0
	minute =< 59
}

sig CurrentTime extends Time {}

// Location, Areas

sig Location {
	latitude: Int,
	longitude: Int
}
sig Area {
	center: Location,
//	radius: Int
} {
//	radius > 0
}
sig Safe_Area extends Area {}
sig Special_Safe_Area extends Safe_Area {}

/* ----------- Functions ------------ */
fun minutesDiff[t, t': Time]:Int {
	t.minute>t'.minute
	implies
		sub[t.minute, t'.minute]
	else
		sub[t'.minute, t.minute]
}

fun hoursDiff[t, t': Time]: Int {
	t.hour>=t'.hour
	implies(
		sub[t.hour, t'.hour]
	) else (
		sub[t'.hour, t.hour]
	)
}

//Return the cost for a ride from time t to time t'
fun cost[t, t': Time]: Int {
		t.hour = t'.hour
		implies (
			minutesDiff[t, t']
		)
		else (
			t.hour>t'.hour
			implies(
				add[add[sub[60, t'.minute], mul[sub[hoursDiff[t,t'], 1], 60]], t.minute]   //(60-t'.minute) + (t'.hour-t.hour-1)*60 + t.minute
			)
			else(
				add[add[sub[60, t.minute], mul[sub[hoursDiff[t,t'], 1], 60]], t'.minute]   //(60-t.minute) + (t.hour-t'.hour-1)*60 + t'.minute
			)
		)
}
// ----------- Predicates -----------

// Returns true if t == t'
pred areEqual[t, t': Time] {
	t.hour = t'.hour and t.minute = t'.minute
}

// Returns true if t - t' <= 1h
pred isLessThanOneHourAhead[t, t': Time] {
	areEqual[t, t']
	or
		(t.hour = t'.hour and t.minute > t'.minute)
	or
		(t.hour = add[t'.hour,1] and add[sub[60, t'.minute], t.minute] < 60)
}

// Returns true if t >= t'
pred comesAfterOrEqual[t, t': Time] {
		t.hour = t'.hour
		implies t.minute >= t'.minute
		else t.hour >= t'.hour
}

// Returns true if the max distance between latitudes and longitudes is 5
pred isNear[l, l': Location] {
	(l.latitude >= l'.latitude implies
		sub[l.latitude, l'.latitude] <= 4
	else
		sub[l'.latitude, l.latitude] <= 4)
	and
	(l.longitude >= l'.longitude implies
		sub[l.longitude, l'.longitude] <= 4
	else
		sub[l'.longitude, l.longitude] <= 4)
}

pred isFarFromSpecialSafeArea[area: Safe_Area] {
			all ssa: Special_Safe_Area |(
			(int ssa.center.latitude >= int area.center.latitude
			implies
				sub[ssa.center.latitude, area.center.latitude] > 15
			else
				sub[area.center.latitude, ssa.center.latitude] > 15
			)
			and
			(int ssa.center.longitude >= int area.center.longitude implies
				sub[ssa.center.longitude, area.center.longitude] > 15
			else
				sub[area.center.longitude, ssa.center.longitude] > 15)
	)
}
// Reservation r is open <=> r is not expired and "it has not a ride yet or its ride has no release_time"
pred isActive[r: Reservation] {
	r.expired = False and (
		no r.ride
		or
		no r.ride.release_time
	)
}

// ----------- Facts -------------

// There's only a current time
fact currentTimeForeverAlone {
	#CurrentTime = 1
}

//There are no different Time with the same time
/*fact alwaysDifferent {
	all t, t': Time | t!=t' implies (not areEqual[t, t'])
}*/

// CurrentTime is after all the times in the model
fact currentTimeIsAlwaysAhead {
	all t: Time |
		all ct: CurrentTime |
			comesAfterOrEqual[ct, t]
}

// Each credential/password/payment info is used for a user
fact eachCredentialCorrespondToAUser {
	User.credential=Credential
}

fact eachPaymentInfoCorrespondToAUser {
	User.payment_info=Payment_Info
}

// Different users <=> different credentials
fact noUsersWithSameCredentials {
	all u1, u2: User |
		u1 != u2
		iff
		u1.credential != u2.credential
}

// Different cars <=> different IDs
fact noCarsWithSameIDs {
	all c1, c2: Car |
		c1 != c2
		iff
		c1.id != c2.id
}

// If a ride has a reservation, then that reservation has that ride.
fact ridesAndReservationRelation {
	all rid: Ride | (
		rid = rid.reservation.ride
	) and
	all res: Reservation | (
		one res.ride
		implies
			res=res.ride.reservation
	)
}

// There can't be two reservations made by the same user both open
// COMMENT: Actually it's possible that a user makes a wrong reservation and he lets it expires, while he makes another one
/*
fact noConsecutiveReservations {
	all r, r': Reservation | (
		r.user = r'.user
		implies not (
			isOpen[r] and isOpen[r']
		)
	)
}*/

// There can't be two reservations r1 and r2 for the same car overlapping.

fact carsCanBeReservedByOneUserAtOnce {
	all c: Car | (
		all r, r': Reservation | (
			(r.car = c and r'.car = c and r != r' and comesAfterOrEqual[r'.start_time, r.start_time])
			implies (
					(some r.ride and some r.ride.release_time and comesAfterOrEqual[r'.start_time, r.ride.release_time])
					or
					(r.expired = True and not isLessThanOneHourAhead[r'.start_time, r.start_time])
			)
		)
	)
}

// A car is available if it has no reservations open and has enough battery
fact carAvailableCondition {
	all c: Car |
		c.available = True
		iff(
		(no res: Reservation |
			res.car = c and isActive[res])
		 and  c.battery_level>80
		)
}

// A reservation is expired if there's is not a ride whose pickup_time is less than one hour after the
//    reservation start_time.
fact reservationExpiredCondition {
	all res: Reservation |
		res.expired = True
		iff
		(all currentTime: CurrentTime |
			not isLessThanOneHourAhead[currentTime, res.start_time]
		)
		and
		no res.ride
}

// The pickup time of a ride should be in one hour from its reservation start_time
fact pickupTimeConstraint {
	all rid: Ride | (
		isLessThanOneHourAhead[rid.pickup_time, rid.reservation.start_time]
	)
}

// release_time is always after pickup_time
fact releaseTimeIsAfterPickupTime {
	all rid: Ride | (
		no rid.release_time
		or
		comesAfterOrEqual[rid.release_time, rid.pickup_time]
	)
}

// A car is unlocked if there's a reservation not expired between a user and that car,
//    such user is near it and
//    he hasn't finished the ride (the ride associated with that reservation
//    doesn't have a release_time).
fact carUnlockedConstraint {
	all c: Car | (
		c.unlocked = True
		iff
		some r: Reservation | (
			r.car = c and isActive[r] and isNear[c.location, r.user.location]
		)
	)
}

//If there is a release time, there are also a release release_battery and a release_area
fact releaseInfoConstraint{
	all rid: Ride| (
		(some rid.release_time
		iff
		some rid.release_battery_level) and
		(some rid.release_area
		iff
		some rid.release_time) and
		(some rid.release_time
		 iff
		some rid.release_plugged)
	)
}

fact feeConstraint {
	all r: Reservation | (
		r.expired = True
		implies
		r.current_cost = 10 and r.final_discharged_cost = 10
	)
}

//The actual cost of a reservation is proportional to minutes spent in the car
fact costOfAReservationConstraint {
	all res: Reservation | (
		res.expired = False
		implies (
			no res.ride
			implies
				res.current_cost=0
			else(
				no res.ride.release_time
				implies(
					all ct : CurrentTime |
						res.current_cost=cost[ct, res.ride.pickup_time]
				)
				else
					res.current_cost=cost[res.ride.release_time, res.ride.pickup_time]
			)
		)
	)
}

//Passengers discount of 10%
fact passengersDiscount{
	all res: Reservation | (
		res.expired = False and one res.ride.release_time and res.ride.passengers>2
		implies(
			res.final_discharged_cost=div[mul[res.current_cost, 9],10]
		)
	)
}

//Non-empty battery discount of 20%
fact nonEmptyBatteryDiscount{
	all res: Reservation | (
		res.expired = False and res.ride.release_battery_level>=50
		implies(
			res.final_discharged_cost=div[mul[res.current_cost, 4],5]
		)
	)
}

//Car released charging discount of 30%
fact chargingDiscount{
	all res: Reservation | (
	res.expired = False and (res.ride.release_area in Special_Safe_Area) and res.ride.release_plugged = True
		implies(
			res.final_discharged_cost=div[mul[res.current_cost, 7],10]
		)
	)
}

//Car released far and with low battery extra-charging of 30%
fact lowBatteryExtraFee{
	all res: Reservation | (
		res.expired = False and res.ride.release_battery_level<20 and isFarFromSpecialSafeArea[res.ride.release_area]
		implies(
			res.final_discharged_cost=div[mul[res.current_cost, 13],10]
		)
	)
}
// ------------- Assertions ---------------

// car available => car locked

assert availableEntailsLocked {
	all c: Car | c.available = True implies c.unlocked = False
}

// car unlocked => car not available

assert unlockedEntailsNotAvailable {
	all c: Car | c.unlocked = True implies c.available = False
}

// reservation expired => no ride

assert expiredEntailsNoRide {
	all r: Reservation | r.expired = True implies no r.ride
}

// reservation active => car not available

assert activeEntailsNotAvailable {
	all r: Reservation | isActive[r] implies r.car.available = False
}

// FALSE

assert falseByPurpose {
	all r: Reservation | r.start_time = r.ride.release_time
	or
	all ride: Ride |  all ct: CurrentTime| ride.release_time=ct
}

// ------------ Show world! --------------

pred show {
	#User = 1
	#Reservation = 1
	some r: Reservation | (r.ride.release_battery_level>=50)
	some c: Car | c.available = True
//	some c: Car | c.unlocked = False

}

run show for 8 Int
//check availableEntailsLocked for 7 Int
//check unlockedEntailsNotAvailable for 7 Int
//check expiredEntailsNoRide for 7 Int
//check activeEntailsNotAvailable for 7 Int
//check falseByPurpose for 8 Int