fc, CompModule root,
+ int lineOffset, String filename, String prefix, int initialResolutionMode) throws Err, FileNotFoundException, IOException {
+ Reader isr=null;
+ try {
+ if (root==null && prefix.length()!=0) throw new ErrorFatal("Internal error (parse subfile with root==null)");
+ if (root!=null && prefix.length()==0) throw new ErrorFatal("Internal error (parse topfile with root!=null)");
+ CompModule u = new CompModule(root, filename, prefix);
+ if (root == null)
+ u.addOpen(null, null, ExprVar.make(null, "util/integer"), null, ExprVar.make(null, "integer"));
+ u.resolution = initialResolutionMode;
+ String content = fc!=null ? fc.get(filename) : null;
+ if (content==null && loaded!=null) content = loaded.get(filename);
+ if (content==null) content = Util.readAll(filename);
+ if (loaded!=null) loaded.put(filename,content);
+ content = Util.convertLineBreak(content);
+ isr = new StringReader(content);
+ CompFilter s = new CompFilter(u, seenDollar, filename, lineOffset, new BufferedReader(isr));
+ CompParser p = new CompParser(s);
+ p.alloymodule=u;
+ try {p.parse();} catch(Throwable ex) {if (ex instanceof Err) throw (Err)ex; throw new ErrorFatal("Parser Exception", ex);}
+ // if no sigs are defined by the user, add one
+ if (root == null && u.getAllSigs().isEmpty()) {
+ u.addGhostSig();
+ }
+ return u;
+ } finally {
+ Util.close(isr);
+ }
+ }
+
+:};
+
+action code {:
+ /** This function is needed to handle a difficult parsing ambiguity.
+ *
+ *
+ * "some EXPR", "one EXPR", and "lone EXPR"
+ * can be either formulas (saying the EXPR has at least 1, exactly 1, or at most 1 tuple),
+ * or multiplicity constraints (saying something else has this multiplicity).
+ *
+ *
+ * So we let the parser generate the former by default.
+ * And whenever we construct a Decl "x: y" object,
+ * or an binary expression "x in y", or a function return type,
+ * we call this method on y to convert it into a multiplicity constraint.
+ *
+ *
+ * This is safe, because in all 3 cases, a formula would be illegal.
+ * So the first form is always wrong.
+ *
+ *
+ * And this is sufficient, because those are the only 3 places
+ * where a mulitplicity constraint is allowed to appear.
+ *
+ * @return a newly formed multiplciity constraint (if this.op==SOME or LONE or ONE),
+ * otherwise it just returns the original node.
+ */
+ private Expr mult(Expr x) throws Err {
+ if (x instanceof ExprUnary) {
+ ExprUnary y=(ExprUnary)x;
+ if (y.op==ExprUnary.Op.SOME) return ExprUnary.Op.SOMEOF.make(y.pos, y.sub);
+ if (y.op==ExprUnary.Op.LONE) return ExprUnary.Op.LONEOF.make(y.pos, y.sub);
+ if (y.op==ExprUnary.Op.ONE) return ExprUnary.Op.ONEOF.make(y.pos, y.sub);
+ }
+ return x;
+ }
+ private void nod(ExprVar name) throws Err {
+ if (name.label.indexOf('$')>=0) throw new ErrorSyntax(name.pos, "The name cannot contain the '$' symbol.");
+ }
+ private void nod(List names) throws Err {
+ if (names!=null) for(ExprVar n:names) if (n!=null && n.label.indexOf('$')>=0) throw new ErrorSyntax(n.pos, "The name cannot contain the '$' symbol.");
+ }
+ private void c(boolean follow, ExprVar o, ExprVar x, ExprVar n, Expr e, List s, ExprConstant c) throws Err {
+ if (n!=null) nod(n);
+ int bitwidth=(-1), maxseq=(-1), overall=(-1), expects=(c==null ? -1 : c.num);
+ Pos p = o.pos.merge(n!=null ? n.span() : e.span());
+ for(int i=s.size()-1; i>=0; i--) {
+ Sig j=s.get(i).sig; int k=s.get(i).startingScope;
+ p=p.merge(j.pos);
+ if (j.label.equals("univ")) { overall=k; s.remove(i); continue; }
+ if (j.label.equals("int")) { if (bitwidth>=0) throw new ErrorSyntax(j.pos, "The bitwidth cannot be specified more than once."); bitwidth=k; s.remove(i); continue; }
+ if (j.label.equals("seq")) { if (maxseq>=0) throw new ErrorSyntax(j.pos, "The maximum sequence length cannot be specified more than once."); maxseq=k; s.remove(i); continue; }
+ }
+ if (n!=null)
+ parser.alloymodule.addCommand(follow, p, n.label, o.label.equals("c"), overall, bitwidth, maxseq, expects, s, x);
+ else
+ parser.alloymodule.addCommand(follow, p, e, o.label.equals("c"), overall, bitwidth, maxseq, expects, s, x);
+ }
+ private Expr t(Pos pos, Pos oldClosing, Expr left, Expr right, Pos close) throws Err {
+ if (right instanceof ExprVar) {
+ String n = ((ExprVar)right).label;
+ if (n.equals("int")) return ExprUnary.Op.CAST2INT.make(pos, left);
+ if (n.equals("disj")) return ExprList.makeDISJOINT(pos, close, Util.asList(left));
+ if (n.equals("pred/totalOrder")) return ExprList.makeTOTALORDER(pos, close, Util.asList(left));
+ }
+ else if (right instanceof ExprList) {
+ return ((ExprList)right).addArg(left);
+ }
+ return ExprBadJoin.make(pos, oldClosing, left, right);
+ }
+:};
+
+//===========================================================================//
+
+terminal Pos ARROW; // ->
+terminal Pos ANY_ARROW_SOME; // ->some // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos ANY_ARROW_ONE; // ->one // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos ANY_ARROW_LONE; // ->lone // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos SOME_ARROW_ANY; // some-> // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos SOME_ARROW_SOME; // some->some // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos SOME_ARROW_ONE; // some->one // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos SOME_ARROW_LONE; // some->lone // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos ONE_ARROW_ANY; // one-> // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos ONE_ARROW_SOME; // one->some // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos ONE_ARROW_ONE; // one->one // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos ONE_ARROW_LONE; // one->lone // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos LONE_ARROW_ANY; // lone->any // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos LONE_ARROW_SOME; // lone->some // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos LONE_ARROW_ONE; // lone->one // The filter allows whitespace/comment in these 15 "*->*" tokens
+terminal Pos LONE_ARROW_LONE; // lone->lone // The filter allows whitespace/comment in these 15 "*->*" tokens
+
+terminal Pos INTADD;
+terminal Pos INTSUB;
+terminal Pos INTMUL;
+terminal Pos INTDIV;
+terminal Pos INTREM;
+terminal Pos INTMIN;
+terminal Pos INTMAX;
+terminal Pos INTNEXT;
+terminal Pos TOTALORDER;
+
+terminal Pos ABSTRACT; // abstract
+terminal Pos ALL; // all // The filter enables us to disambiguate
+terminal Pos ALL2; // all // The filter enables us to disambiguate
+terminal Pos AMPERSAND; // &
+terminal Pos AND; // && and
+terminal Pos AS; // as
+terminal Pos ASSERT; // assert
+terminal Pos AT; // @
+terminal Pos BAR; // |
+terminal Pos BUT; // but
+terminal Pos CARET; // ^
+terminal Pos CHECK; // check
+terminal Pos COLON; // :
+terminal Pos COMMA; // ,
+terminal Pos DISJ; // disj disjoint
+terminal Pos DOMAIN; // <:
+terminal Pos DOT; // .
+terminal Pos ELSE; // else
+terminal Pos ENUM; // enum
+terminal Pos EQUALS; // = ==
+terminal Pos EXACTLY; // exactly
+terminal Pos EXH; // exh exhaustive
+terminal Pos EXPECT; // expect
+terminal Pos EXTENDS; // extends
+terminal Pos FACT; // fact
+terminal Pos FOR; // for
+terminal Pos FUN; // fun
+terminal Pos GT; // >
+terminal Pos GTE; // >=
+terminal Pos HASH; // #
+terminal Pos IDEN; // iden
+terminal Pos IFF; // <=> iff
+terminal Pos IMPLIES; // => implies
+terminal Pos IN; // in
+terminal Pos INT; // int
+terminal Pos LBRACE; // {
+terminal Pos LBRACKET; // [
+terminal Pos LET; // let
+terminal Pos LONE2; // lone // The filter enables us to disambiguate
+terminal Pos LONE; // lone // The filter enables us to disambiguate
+terminal Pos LPAREN; // (
+terminal Pos LT; // <
+terminal Pos LTE; // <= =<
+terminal Pos MINUS; // -
+terminal Pos MODULE; // module
+terminal Pos NO2; // no // The filter enables us to disambiguate
+terminal Pos NO; // no // The filter enables us to disambiguate
+terminal Pos NONE; // none
+terminal Pos NOT; // ! not
+terminal Pos NOTEQUALS; // != not= // The filter allows whitespace/comment in between
+terminal Pos NOTGT; // !> not> // The filter allows whitespace/comment in between
+terminal Pos NOTGTE; // !>= not>= // The filter allows whitespace/comment in between
+terminal Pos NOTIN; // !in notin // The filter allows whitespace/comment in between
+terminal Pos NOTLT; // !< not< // The filter allows whitespace/comment in between
+terminal Pos NOTLTE; // !=< not=< // The filter allows whitespace/comment in between
+terminal Pos ONE2; // one // The filter enables us to disambiguate
+terminal Pos ONE; // one // The filter enables us to disambiguate
+terminal Pos OPEN; // open
+terminal Pos OR; // || or
+terminal Pos PART; // part partition
+terminal Pos PLUS; // +
+terminal Pos PLUSPLUS; // ++
+terminal Pos PRED; // pred
+terminal Pos PRIVATE; // private
+terminal Pos RANGE; // :>
+terminal Pos RBRACE; // }
+terminal Pos RBRACKET; // ]
+terminal Pos RPAREN; // )
+terminal Pos RUN; // run
+terminal Pos SEQ; // seq
+terminal Pos SET; // set
+terminal Pos SHL; // <<
+terminal Pos SHR; // >>>
+terminal Pos SHA; // >>
+terminal Pos SIG; // sig
+terminal Pos SIGINT; // Int
+terminal Pos SLASH; // /
+terminal Pos SOME2; // some // The filter enables us to disambiguate
+terminal Pos SOME; // some // The filter enables us to disambiguate
+terminal Pos STAR; // *
+terminal Pos STRING; // String
+terminal Pos SUM2; // sum // The filter enables us to disambiguate
+terminal Pos SUM; // sum // The filter enables us to disambiguate
+terminal Pos THIS; // this
+terminal Pos TILDE; // ~
+terminal Pos UNIV; // univ
+
+terminal ExprVar ID;
+
+terminal ExprConstant NUMBER, STR;
+
+//===========================================================================//
+
+nonterminal Expr AndExprA;
+nonterminal Expr AndExprB;
+nonterminal Expr BaseExpr;
+nonterminal Expr Bind;
+nonterminal Expr BracketExprA;
+nonterminal Expr BracketExprB;
+nonterminal Expr CompareExprA;
+nonterminal Expr CompareExprB;
+nonterminal Command;
+nonterminal ExprVar CommandPrefix;
+nonterminal Decl Decla;
+nonterminal Decl Declb;
+nonterminal List Declp;
+nonterminal List Decls;
+nonterminal List Declz;
+nonterminal Expr DomainExprA;
+nonterminal Expr DomainExprB;
+nonterminal Expr DotExprA;
+nonterminal Expr DotExprB;
+nonterminal Expr EquivExprA;
+nonterminal Expr EquivExprB;
+nonterminal ExprConstant Expects;
+nonterminal Expr Expr;
+nonterminal Expr Super;
+nonterminal Expr SuperOpt;
+nonterminal Expr SuperP;
+nonterminal Expr SuperOrBar;
+nonterminal List Exprs;
+nonterminal List Exprp;
+nonterminal Function;
+nonterminal Expr ImpliesExprA;
+nonterminal Expr ImpliesExprB;
+nonterminal Expr ImpliesExprCloseA;
+nonterminal Expr ImpliesExprCloseB;
+nonterminal Expr ImpliesExprOpenA;
+nonterminal Expr ImpliesExprOpenB;
+nonterminal Expr IntersectExprA;
+nonterminal Expr IntersectExprB;
+nonterminal Expr Let;
+nonterminal Macro;
+nonterminal Expr MacroBody;
+nonterminal ExprVar Name;
+nonterminal ExprVar NameHelper;
+nonterminal List Names;
+nonterminal List Namex;
+nonterminal Expr NegExprA;
+nonterminal Expr NegExprB;
+nonterminal Expr NumUnopExprA;
+nonterminal Expr NumUnopExprB;
+nonterminal Expr OrExprA;
+nonterminal Expr OrExprB;
+nonterminal Expr OverrideExprA;
+nonterminal Expr OverrideExprB;
+nonterminal Predicate;
+nonterminal Expr RangeExprA;
+nonterminal Expr RangeExprB;
+nonterminal Pair RelOp;
+nonterminal Expr RelationExprA;
+nonterminal Expr RelationExprB;
+nonterminal List Scope;
+nonterminal Sig;
+nonterminal List SigIn;
+nonterminal List SigQual;
+nonterminal List SigQuals;
+nonterminal ExprVar SigRef;
+nonterminal List SigRefp;
+nonterminal List SigRefs;
+nonterminal List SigRefu;
+nonterminal File;
+nonterminal Spec;
+nonterminal CommandScope TypeNumber;
+nonterminal CommandScope Typescope;
+nonterminal List Typescopes;
+nonterminal Expr ShiftExprA;
+nonterminal Expr ShiftExprB;
+nonterminal Expr MulExprA;
+nonterminal Expr MulExprB;
+nonterminal Expr UnionDiffExprA;
+nonterminal Expr UnionDiffExprB;
+nonterminal Expr UnopExprA;
+nonterminal Expr UnopExprB;
+nonterminal Pos Vis;
+
+//===========================================================================//
+
+File ::= Spec {: parser.alloymodule.doneParsing(); :};
+
+Spec ::= Spec MODULE:o Name:n {: nod(n); parser.alloymodule.addModelName(o.merge(n.pos) , n.label , new ArrayList()); :};
+Spec ::= Spec MODULE:o Name:n LBRACKET Namex:b RBRACKET:r {: nod(n); nod(b); parser.alloymodule.addModelName(o.merge(r) , n.label , b ); :};
+Spec ::= Spec Vis:p OPEN:o Name:a {: nod(a); parser.alloymodule.addOpen(o.merge(a.pos), p, a, null, null); :};
+Spec ::= Spec Vis:p OPEN:o Name:a AS Name:c {: nod(a); nod(c); parser.alloymodule.addOpen(o.merge(c.pos), p, a, null, c); :};
+Spec ::= Spec Vis:p OPEN:o Name:a LBRACKET SigRefs:b RBRACKET:c {: nod(a); parser.alloymodule.addOpen(o.merge(c), p, a, b, null); :};
+Spec ::= Spec Vis:p OPEN:o Name:a LBRACKET SigRefs:b RBRACKET AS Name:c {: nod(a); nod(c); parser.alloymodule.addOpen(o.merge(c.pos), p, a, b, c); :};
+Spec ::= Spec Vis:p ENUM:o Name:a LBRACE Names:n RBRACE:c {: nod(a); parser.alloymodule.addEnum(o.merge(c), p, a, n, c); :};
+Spec ::= Spec Vis:p ENUM:o Name:a LBRACE RBRACE:c {: nod(a); parser.alloymodule.addEnum(o.merge(c), p, a, null, c); :};
+Spec ::= Spec FACT:o Super:e {: parser.alloymodule.addFact (o , "" , e); :};
+Spec ::= Spec FACT:o Name:n Super:e {: nod(n); parser.alloymodule.addFact (o , n.label , e); :};
+Spec ::= Spec FACT:o STR:n Super:e {: parser.alloymodule.addFact (o , n.string , e); :};
+Spec ::= Spec ASSERT:o Super:e {: parser.alloymodule.addAssertion (o , "" , e); :};
+Spec ::= Spec ASSERT:o Name:n Super:e {: nod(n); parser.alloymodule.addAssertion (o , n.label , e); :};
+Spec ::= Spec ASSERT:o STR:n Super:e {: parser.alloymodule.addAssertion (o , n.string , e); :};
+Spec ::= Spec Sig ;
+Spec ::= Spec Function ;
+Spec ::= Spec Predicate ;
+Spec ::= Spec Macro ;
+Spec ::= Spec Command ;
+Spec ::= ;
+
+CommandPrefix ::= CHECK:c {: RESULT = ExprVar.make(c, "c"); :};
+CommandPrefix ::= RUN:r {: RESULT = ExprVar.make(r, "r"); :};
+
+Command ::= CommandPrefix:o Name:x Super:e Scope:s Expects:c {: c(false,o,x ,null,e ,s,c); :};
+Command ::= CommandPrefix:o Super:e Scope:s Expects:c {: c(false,o,null,null,e ,s,c); :};
+Command ::= Command IMPLIES CommandPrefix:o Name:x Super:e Scope:s Expects:c {: c(true ,o,x ,null,e ,s,c); :};
+Command ::= Command IMPLIES CommandPrefix:o Super:e Scope:s Expects:c {: c(true ,o,null,null,e ,s,c); :};
+Command ::= CommandPrefix:o Name:x Name:n Scope:s Expects:c {: c(false,o,x ,n ,null,s,c); :};
+Command ::= CommandPrefix:o Name:n Scope:s Expects:c {: c(false,o,null,n ,null,s,c); :};
+Command ::= Command IMPLIES CommandPrefix:o Name:x Name:n Scope:s Expects:c {: c(true ,o,x ,n ,null,s,c); :};
+Command ::= Command IMPLIES CommandPrefix:o Name:n Scope:s Expects:c {: c(true ,o,null,n ,null,s,c); :};
+
+Expects ::= {: RESULT=null; :};
+Expects ::= EXPECT NUMBER:a {: RESULT=a; :};
+
+Scope ::= FOR NUMBER:a {: RESULT=new ArrayList(); RESULT.add(new CommandScope(a.pos, new PrimSig("univ", AttrType.WHERE.make(a.pos)), true, a.num, a.num, 1)); :};
+Scope ::= FOR NUMBER:a BUT Typescopes:b {: RESULT=b; b.add(new CommandScope(a.pos, new PrimSig("univ", AttrType.WHERE.make(a.pos)), true, a.num, a.num, 1)); :};
+Scope ::= FOR Typescopes:b {: RESULT=b; :};
+Scope ::= {: RESULT=new ArrayList(); :};
+
+Typescopes ::= Typescope:a {: RESULT=new ArrayList(); RESULT.add(a); :};
+Typescopes ::= Typescopes:a COMMA Typescope:b {: RESULT=a; a.add(b); :};
+
+Typescope ::= TypeNumber:a Name:b {:
+ nod(b);
+ RESULT = new CommandScope(a.pos.merge(b.pos), new PrimSig(b.label, AttrType.WHERE.make(a.pos.merge(b.pos))), a.isExact, a.startingScope, a.endingScope, a.increment);
+:};
+
+//[AM]: INT -> SIGINT
+Typescope ::= TypeNumber:a SIGINT:b {:
+ Pos p = a.pos.merge(b);
+ if (a.endingScope>a.startingScope) throw new ErrorSyntax(p, "Cannot specify a growing scope for \"Int\"");
+ if (a.isExact) throw new ErrorSyntax(p, "The exactly keyword is redundant here since the integer bitwidth must be exact.");
+ RESULT = new CommandScope(p, new PrimSig("int", AttrType.WHERE.make(p)), a.isExact, a.startingScope, a.startingScope, 1);
+:};
+
+Typescope ::= TypeNumber:a INT:b {:
+ Pos p = a.pos.merge(b);
+ if (a.endingScope>a.startingScope) throw new ErrorSyntax(p, "Cannot specify a growing scope for \"Int\"");
+ if (a.isExact) throw new ErrorSyntax(p, "The exactly keyword is redundant here since the integer bitwidth must be exact.");
+ RESULT = new CommandScope(p, new PrimSig("int", AttrType.WHERE.make(p)), a.isExact, a.startingScope, a.startingScope, 1);
+:};
+
+Typescope ::= TypeNumber:a SEQ:b {:
+ Pos p = a.pos.merge(b);
+ if (a.endingScope>a.startingScope) throw new ErrorSyntax(p, "Cannot specify a growing scope for \"seq\"");
+ if (a.isExact) throw new ErrorSyntax(p, "The exactly keyword is redundant here since the number of sequence index has to be exact.");
+ RESULT = new CommandScope(p, new PrimSig("seq", AttrType.WHERE.make(p)), a.isExact, a.startingScope, a.startingScope, 1);
+:};
+
+Typescope ::= TypeNumber:e UNIV:f {: if (1==1) throw new ErrorSyntax(e.pos.merge(f), "You cannot set a scope on univ."); :};
+
+Typescope ::= TypeNumber:a STRING:b {: RESULT = new CommandScope(a.pos.merge(b), new PrimSig("String", AttrType.WHERE.make(a.pos.merge(b))), a.isExact, a.startingScope, a.endingScope, a.increment); :};
+
+//[AM] Typescope ::= TypeNumber:e SIGINT:f {: if (1==1) throw new ErrorSyntax(e.pos.merge(f), "You can no longer set a scope on Int; the number of Int atoms is always exactly equal to 2^(integer bitwidth).\n"); :};
+
+Typescope ::= TypeNumber:e NONE:f {: if (1==1) throw new ErrorSyntax(e.pos.merge(f), "You cannot set a scope on none."); :};
+
+TypeNumber ::= EXACTLY:e NUMBER:a {: RESULT = new CommandScope( e.merge(a.pos), Sig.NONE, true, a.num, a.num, 1 ); :};
+TypeNumber ::= EXACTLY:e NUMBER:a DOT DOT NUMBER:b {: if (!Version.experimental) throw new ErrorSyntax(a.pos, "Syntax error here."); RESULT = new CommandScope( e.merge(b.pos), Sig.NONE, true, a.num, b.num, 1 ); :};
+TypeNumber ::= EXACTLY:e NUMBER:a DOT DOT NUMBER:b COLON NUMBER:i {: if (!Version.experimental) throw new ErrorSyntax(a.pos, "Syntax error here."); RESULT = new CommandScope( e.merge(i.pos), Sig.NONE, true, a.num, b.num, i.num); :};
+TypeNumber ::= EXACTLY:e NUMBER:a COLON NUMBER:i {: if (!Version.experimental) throw new ErrorSyntax(a.pos, "Syntax error here."); RESULT = new CommandScope( e.merge(i.pos), Sig.NONE, true, a.num, Integer.MAX_VALUE, i.num); :};
+TypeNumber ::= NUMBER:a {: RESULT = new CommandScope(a.pos , Sig.NONE, false, a.num, a.num, 1 ); :};
+TypeNumber ::= NUMBER:a DOT DOT NUMBER:b {: if (!Version.experimental) throw new ErrorSyntax(a.pos, "Syntax error here."); RESULT = new CommandScope(a.pos.merge(b.pos), Sig.NONE, false, a.num, b.num, 1 ); :};
+TypeNumber ::= NUMBER:a DOT DOT NUMBER:b COLON NUMBER:i {: if (!Version.experimental) throw new ErrorSyntax(a.pos, "Syntax error here."); RESULT = new CommandScope(a.pos.merge(i.pos), Sig.NONE, false, a.num, b.num, i.num); :};
+TypeNumber ::= NUMBER:a COLON NUMBER:i {: if (!Version.experimental) throw new ErrorSyntax(a.pos, "Syntax error here."); RESULT = new CommandScope(a.pos.merge(i.pos), Sig.NONE, false, a.num, Integer.MAX_VALUE, i.num); :};
+
+Macro ::= Vis:p LET:o Name:n LPAREN Names:d RPAREN MacroBody:v {: nod(n); parser.alloymodule.addMacro(o.merge(v.span()), p, n.label, d , v); :};
+Macro ::= Vis:p LET:o Name:n LPAREN RPAREN MacroBody:v {: nod(n); parser.alloymodule.addMacro(o.merge(v.span()), p, n.label, null , v); :};
+Macro ::= Vis:p LET:o Name:n LBRACKET Names:d RBRACKET MacroBody:v {: nod(n); parser.alloymodule.addMacro(o.merge(v.span()), p, n.label, d , v); :};
+Macro ::= Vis:p LET:o Name:n LBRACKET RBRACKET MacroBody:v {: nod(n); parser.alloymodule.addMacro(o.merge(v.span()), p, n.label, null , v); :};
+Macro ::= Vis:p LET:o Name:n MacroBody:v {: nod(n); parser.alloymodule.addMacro(o.merge(v.span()), p, n.label, null , v); :};
+
+MacroBody ::= Super:a {: RESULT=a; :};
+MacroBody ::= EQUALS Expr:a {: RESULT=a; :};
+
+Function ::= Vis:p FUN:o Name:n LPAREN Decls:d RPAREN COLON Expr:r Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, null, d , mult(r), v); :};
+Function ::= Vis:p FUN:o Name:n LBRACKET Decls:d RBRACKET COLON Expr:r Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, null, d , mult(r), v); :};
+Function ::= Vis:p FUN:o Name:n COLON Expr:r Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, null, null , mult(r), v); :};
+Function ::= Vis:p FUN:o SigRef:f DOT Name:n LPAREN Decls:d RPAREN COLON Expr:r Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, f , d , mult(r), v); :};
+Function ::= Vis:p FUN:o SigRef:f DOT Name:n LBRACKET Decls:d RBRACKET COLON Expr:r Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, f , d , mult(r), v); :};
+Function ::= Vis:p FUN:o SigRef:f DOT Name:n COLON Expr:r Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, f , null , mult(r), v); :};
+
+Predicate ::= Vis:p PRED:o Name:n LPAREN Decls:d RPAREN Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, null, d , null, v); :};
+Predicate ::= Vis:p PRED:o Name:n LBRACKET Decls:d RBRACKET Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, null, d , null, v); :};
+Predicate ::= Vis:p PRED:o Name:n Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, null, null , null, v); :};
+Predicate ::= Vis:p PRED:o SigRef:f DOT Name:n LPAREN Decls:d RPAREN Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, f , d , null, v); :};
+Predicate ::= Vis:p PRED:o SigRef:f DOT Name:n LBRACKET Decls:d RBRACKET Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, f , d , null, v); :};
+Predicate ::= Vis:p PRED:o SigRef:f DOT Name:n Super:v {: nod(n); parser.alloymodule.addFunc(o.merge(v.span()), p, n.label, f , null , null, v); :};
+
+Vis ::= {: RESULT=null; :};
+Vis ::= PRIVATE:p {: RESULT=p; :};
+
+Sig ::= SigQuals:a Names:b SigIn:c LBRACE Decls:d RBRACE:o SuperOpt:e
+ {:
+ if (e==null) e = ExprConstant.Op.TRUE.make(o, 0);
+ ExprVar cc = (c!=null && c.size()>0) ? c.remove(c.size()-1) : null;
+ for(ExprVar bb:b) {
+ parser.alloymodule.addSig(bb.label, cc, c, d, e,
+ AttrType.WHERE .makenull(bb.pos.merge(e==null ? o : e.span())),
+ AttrType.ABSTRACT.makenull(a.get(0)),
+ AttrType.LONE .makenull(a.get(1)),
+ AttrType.ONE .makenull(a.get(2)),
+ AttrType.SOME .makenull(a.get(3)),
+ AttrType.PRIVATE .makenull(a.get(4)));
+ }
+ :};
+
+SigQual ::= ABSTRACT:x {: RESULT=new ArrayList(5); RESULT.add(x); RESULT.add(null); RESULT.add(null); RESULT.add(null); RESULT.add(null); :};
+SigQual ::= LONE:x {: RESULT=new ArrayList(5); RESULT.add(null); RESULT.add(x); RESULT.add(null); RESULT.add(null); RESULT.add(null); :};
+SigQual ::= ONE:x {: RESULT=new ArrayList(5); RESULT.add(null); RESULT.add(null); RESULT.add(x); RESULT.add(null); RESULT.add(null); :};
+SigQual ::= SOME:x {: RESULT=new ArrayList(5); RESULT.add(null); RESULT.add(null); RESULT.add(null); RESULT.add(x); RESULT.add(null); :};
+SigQual ::= PRIVATE:x {: RESULT=new ArrayList(5); RESULT.add(null); RESULT.add(null); RESULT.add(null); RESULT.add(null); RESULT.add(x); :};
+
+SigQuals ::= SIG {: RESULT=new ArrayList(5); RESULT.add(null); RESULT.add(null); RESULT.add(null); RESULT.add(null); RESULT.add(null); :};
+SigQuals ::= SigQual:a SigQuals:b {: RESULT=a; for(int i=0;i<5;i++) if (a.get(i)==null) a.set(i,b.get(i)); else if (b.get(i)!=null) throw new ErrorSyntax(b.get(i), "The same qualifer cannot be specified more than once for the same sig."); :};
+
+SigIn ::= EXTENDS:a SigRef:x {: RESULT=new ArrayList(2); RESULT.add(x); RESULT.add(ExprVar.make(a, "extends")); :};
+SigIn ::= IN:a SigRefu:x {: RESULT=x; x.add(ExprVar.make(a,"in")); :};
+SigIn ::= EQUALS:a SigRefu:x {: RESULT=x; x.add(ExprVar.make(a,"=")); :};
+SigIn ::= {: RESULT=null; :};
+
+SigRef ::= Name:x {: RESULT=x; :};
+SigRef ::= UNIV:x {: RESULT=ExprVar.make(x, "univ"); :};
+SigRef ::= STRING:x {: RESULT=ExprVar.make(x, "String"); :};
+SigRef ::= SIGINT:x {: RESULT=ExprVar.make(x, "Int"); :};
+SigRef ::= SEQ:a SLASH SIGINT:b {: RESULT=ExprVar.make(a.merge(b), "seq/Int"); :};
+SigRef ::= NONE:x {: RESULT=ExprVar.make(x, "none"); :};
+
+SigRefs ::= {: RESULT=new ArrayList(); :};
+SigRefs ::= SigRefp:x {: RESULT=x; :};
+
+SigRefp ::= SigRef:x {: RESULT=new ArrayList(); RESULT.add(x); :};
+SigRefp ::= SigRefp:a COMMA SigRef:b {: a.add(b); RESULT=a; :};
+
+SigRefu ::= SigRef:x {: RESULT=new ArrayList(); RESULT.add(x); :};
+SigRefu ::= SigRefu:a PLUS SigRef:b {: a.add(b); RESULT=a; :};
+
+Name ::= NameHelper:x {: RESULT=x; :};
+Name ::= THIS:a SLASH NameHelper:b {: RESULT=ExprVar.make(a.merge(b.pos), "this/"+b.label); :};
+Name ::= SEQ:a SLASH NameHelper:b {: RESULT=ExprVar.make(a.merge(b.pos), "seq/"+b.label); :};
+
+NameHelper ::= ID:x {: RESULT=x; :};
+NameHelper ::= NameHelper:a SLASH ID:b {: RESULT=ExprVar.make(a.pos.merge(b.pos), a.label+"/"+b.label); :};
+
+Names ::= Name:x {: nod(x); RESULT=new ArrayList(); RESULT.add(x); :};
+Names ::= Names:a COMMA Name:b {: nod(b); a.add(b); RESULT=a; :};
+
+Namex ::= Name:x {: nod(x); RESULT=new ArrayList(); RESULT.add(x); :};
+Namex ::= EXACTLY Name:x {: nod(x); RESULT=new ArrayList(); RESULT.add(null); RESULT.add(x); :};
+Namex ::= Namex:a COMMA Name:b {: nod(b); a.add(b); RESULT=a; :};
+Namex ::= Namex:a COMMA EXACTLY Name:b {: nod(b); a.add(null); a.add(b); RESULT=a; :};
+
+Decla ::= PART:k Names COLON Expr {: if (1==1) throw CompModule.hint(k, "part"); :};
+Decla ::= EXH:k Names COLON Expr {: if (1==1) throw CompModule.hint(k, "exh"); :};
+Decla ::= DISJ:k Names:a COLON Expr:b {: RESULT=new Decl(null, k, null, a, mult(b)); :};
+Decla ::= PRIVATE:p DISJ:k Names:a COLON Expr:b {: RESULT=new Decl(p, k, null, a, mult(b)); :};
+Decla ::= PRIVATE:p Names:a COLON Expr:b {: RESULT=new Decl(p, null, null, a, mult(b)); :};
+Decla ::= Names:a COLON Expr:b {: RESULT=new Decl(null, null, null, a, mult(b)); :};
+
+Decla ::= PART:k Names COLON DISJ Expr {: if (1==1) throw CompModule.hint(k, "part"); :};
+Decla ::= EXH:k Names COLON DISJ Expr {: if (1==1) throw CompModule.hint(k, "exh"); :};
+Decla ::= DISJ:k Names:a COLON DISJ:d Expr:b {: RESULT=new Decl(null, k, d, a, mult(b)); :};
+Decla ::= PRIVATE:p DISJ:k Names:a COLON DISJ:d Expr:b {: RESULT=new Decl(p, k, d, a, mult(b)); :};
+Decla ::= PRIVATE:p Names:a COLON DISJ:d Expr:b {: RESULT=new Decl(p, null, d, a, mult(b)); :};
+Decla ::= Names:a COLON DISJ:d Expr:b {: RESULT=new Decl(null, null, d, a, mult(b)); :};
+
+Declb ::= Decla:x {: RESULT=x; :};
+
+Declb ::= PART:k Names EQUALS Expr {: if (1==1) throw CompModule.hint(k, "part"); :};
+Declb ::= EXH:k Names EQUALS Expr {: if (1==1) throw CompModule.hint(k, "exh"); :};
+Declb ::= DISJ:d Names EQUALS Expr {: if (1==1) throw new ErrorSyntax(d, "Defined fields cannot be disjoint."); :};
+Declb ::= PRIVATE DISJ:d Names EQUALS Expr {: if (1==1) throw new ErrorSyntax(d, "Defined fields cannot be disjoint."); :};
+Declb ::= PRIVATE:p Names:a EQUALS Expr:b {: RESULT=new Decl(p, null, null, a, ExprUnary.Op.EXACTLYOF.make(null, b)); :};
+Declb ::= Names:a EQUALS Expr:b {: RESULT=new Decl(null, null, null, a, ExprUnary.Op.EXACTLYOF.make(null, b)); :};
+
+Declb ::= PART:k Names EQUALS DISJ Expr {: if (1==1) throw CompModule.hint(k, "part"); :};
+Declb ::= EXH:k Names EQUALS DISJ Expr {: if (1==1) throw CompModule.hint(k, "exh"); :};
+Declb ::= DISJ Names EQUALS DISJ:d Expr {: if (1==1) throw new ErrorSyntax(d, "Defined fields cannot be disjoint."); :};
+Declb ::= PRIVATE DISJ Names EQUALS DISJ:d Expr {: if (1==1) throw new ErrorSyntax(d, "Defined fields cannot be disjoint."); :};
+Declb ::= PRIVATE Names EQUALS DISJ:d Expr {: if (1==1) throw new ErrorSyntax(d, "Defined fields cannot be disjoint."); :};
+Declb ::= Names EQUALS DISJ:d Expr {: if (1==1) throw new ErrorSyntax(d, "Defined fields cannot be disjoint."); :};
+
+Declz ::= Declz:x COMMA Decla:y {: RESULT=x; RESULT.add(y); :};
+Declz ::= Decla:y {: RESULT=new ArrayList(); RESULT.add(y); :};
+
+Declp ::= Declp:x COMMA Declb:y {: RESULT=x; RESULT.add(y); :};
+Declp ::= Declb:y {: RESULT=new ArrayList(); RESULT.add(y); :};
+
+Decls ::= {: RESULT=new ArrayList(); :};
+Decls ::= Declb:x {: RESULT=new ArrayList(); RESULT.add(x); :};
+Decls ::= Declb:x COMMA Decls:y {: RESULT=y; RESULT.add(0,x); :};
+Decls ::= COMMA Decls:y {: RESULT=y; :};
+
+Let ::= Name:a EQUALS:o Expr:b SuperOrBar:x {:
+ nod(a);
+ if (a.label.indexOf('/')>=0) throw new ErrorSyntax(a.pos, "Let variable name cannot contain \'/\'");
+ if (a.label.indexOf('@')>=0) throw new ErrorSyntax(a.pos, "Let variable name cannot contain \'@\'");
+ RESULT = ExprLet.make(o, ExprVar.make(a.pos, a.label), b, x);
+:};
+
+Let ::= Name:a EQUALS:o Expr:b COMMA Let:x {:
+ nod(a);
+ if (a.label.indexOf('/')>=0) throw new ErrorSyntax(a.pos, "Let variable name cannot contain \'/\'");
+ if (a.label.indexOf('@')>=0) throw new ErrorSyntax(a.pos, "Let variable name cannot contain \'@\'");
+ RESULT = ExprLet.make(o, ExprVar.make(a.pos, a.label), b, x);
+:};
+
+SuperOpt ::= {: RESULT=null; :};
+SuperOpt ::= Super:x {: RESULT=x; :};
+Super ::= LBRACE:a SuperP:x RBRACE:b {: RESULT=ExprUnary.Op.NOOP.make(a.merge(b), x); :};
+Super ::= LBRACE:a RBRACE:b {: RESULT=ExprConstant.Op.TRUE.make(a.merge(b), 0); :};
+SuperP ::= Expr:a {: RESULT=a; :};
+SuperP ::= SuperP:a Expr:b {: RESULT=ExprBinary.Op.AND.make(null, null, a, b); :};
+
+SuperOrBar ::= BAR Expr:x {: RESULT=x; :};
+SuperOrBar ::= Super:x {: RESULT=x; :};
+
+Exprs ::= {: RESULT=new ArrayList(); :};
+Exprs ::= Exprp:x {: RESULT=x; :};
+Exprp ::= Expr:x {: RESULT=new ArrayList(); RESULT.add(x); :};
+Exprp ::= Exprp:a COMMA Expr:b {: a.add(b); RESULT=a; :};
+
+//=============================================================================
+
+Expr ::= OrExprA:x {: RESULT = x; :};
+Expr ::= OrExprB:x {: RESULT = x; :};
+Expr ::= Bind:x {: RESULT = x; :};
+Bind ::= LET Let:x {: RESULT = x; :};
+Bind ::= ALL2:o Declp:a SuperOrBar:b {: RESULT = ExprQt.Op.ALL .make(o, null, a, b); :};
+Bind ::= NO2:o Declp:a SuperOrBar:b {: RESULT = ExprQt.Op.NO .make(o, null, a, b); :};
+Bind ::= SOME2:o Declp:a SuperOrBar:b {: RESULT = ExprQt.Op.SOME.make(o, null, a, b); :};
+Bind ::= LONE2:o Declp:a SuperOrBar:b {: RESULT = ExprQt.Op.LONE.make(o, null, a, b); :};
+Bind ::= ONE2:o Declp:a SuperOrBar:b {: RESULT = ExprQt.Op.ONE .make(o, null, a, b); :};
+Bind ::= SUM2:o Declp:a SuperOrBar:b {: RESULT = ExprQt.Op.SUM .make(o, null, a, b); :};
+
+OrExprA ::= EquivExprA:a {: RESULT=a; :};
+OrExprA ::= OrExprB:a OR:o Bind:b {: RESULT=ExprBinary.Op.OR.make(o, null, a, b); :};
+OrExprB ::= EquivExprB:b {: RESULT=b; :};
+OrExprB ::= OrExprB:a OR:o EquivExprB:b {: RESULT=ExprBinary.Op.OR.make(o, null, a, b); :};
+
+EquivExprA ::= ImpliesExprA:b {: RESULT=b; :};
+EquivExprA ::= EquivExprB:a IFF:o Bind:b {: RESULT=ExprBinary.Op.IFF.make(o, null, a, b); :};
+EquivExprB ::= ImpliesExprB:b {: RESULT=b; :};
+EquivExprB ::= EquivExprB:a IFF:o ImpliesExprB:b {: RESULT=ExprBinary.Op.IFF.make(o, null, a, b); :};
+
+ImpliesExprA ::= ImpliesExprCloseA:a {: RESULT=a; :};
+ImpliesExprA ::= ImpliesExprOpenA:a {: RESULT=a; :};
+ImpliesExprCloseA ::= AndExprA:a {: RESULT=a; :};
+ImpliesExprCloseA ::= AndExprB:a IMPLIES:o ImpliesExprCloseB:b ELSE ImpliesExprCloseA:c {: RESULT = ExprITE.make(o,a,b,c); :};
+ImpliesExprOpenA ::= AndExprB:a IMPLIES:o ImpliesExprCloseB:b ELSE ImpliesExprOpenA:c {: RESULT = ExprITE.make(o,a,b,c); :};
+ImpliesExprOpenA ::= AndExprB:a IMPLIES:o ImpliesExprA:b {: RESULT = ExprBinary.Op.IMPLIES.make(o, null, a, b); :};
+
+ImpliesExprCloseA ::= AndExprB:a IMPLIES:o ImpliesExprCloseB:b ELSE Bind:c {: RESULT = ExprITE.make(o,a,b,c); :};
+ImpliesExprOpenA ::= AndExprB:a IMPLIES:o Bind:b {: RESULT = ExprBinary.Op.IMPLIES.make(o, null, a, b); :};
+
+ImpliesExprB ::= ImpliesExprCloseB:a {: RESULT=a; :};
+ImpliesExprB ::= ImpliesExprOpenB:a {: RESULT=a; :};
+ImpliesExprCloseB ::= AndExprB:a {: RESULT=a; :};
+ImpliesExprCloseB ::= AndExprB:a IMPLIES:o ImpliesExprCloseB:b ELSE ImpliesExprCloseB:c {: RESULT = ExprITE.make(o,a,b,c); :};
+ImpliesExprOpenB ::= AndExprB:a IMPLIES:o ImpliesExprCloseB:b ELSE ImpliesExprOpenB:c {: RESULT = ExprITE.make(o,a,b,c); :};
+ImpliesExprOpenB ::= AndExprB:a IMPLIES:o ImpliesExprB:b {: RESULT = ExprBinary.Op.IMPLIES.make(o, null, a, b); :};
+
+AndExprA ::= NegExprA:a {: RESULT=a; :};
+AndExprA ::= AndExprB:a AND:o Bind:b {: RESULT=ExprBinary.Op.AND.make(o, null, a, b); :};
+AndExprB ::= NegExprB:b {: RESULT=b; :};
+AndExprB ::= AndExprB:a AND:o NegExprB:b {: RESULT=ExprBinary.Op.AND.make(o, null, a, b); :};
+
+NegExprA ::= CompareExprA:b {: RESULT=b; :};
+NegExprA ::= NOT:o Bind:b {: RESULT=ExprUnary.Op.NOT.make(o, b); :};
+NegExprA ::= NOT:o NegExprA:b {: RESULT=ExprUnary.Op.NOT.make(o, b); :};
+NegExprB ::= CompareExprB:b {: RESULT=b; :};
+NegExprB ::= NOT:o NegExprB:b {: RESULT=ExprUnary.Op.NOT.make(o, b); :};
+
+CompareExprA ::= CompareExprB:a IN:o ShiftExprA:b {: RESULT=ExprBinary.Op.IN .make(o, null, a, mult(b)); :};
+CompareExprA ::= CompareExprB:a EQUALS:o ShiftExprA:b {: RESULT=ExprBinary.Op.EQUALS .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a LT:o ShiftExprA:b {: RESULT=ExprBinary.Op.LT .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a GT:o ShiftExprA:b {: RESULT=ExprBinary.Op.GT .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a LTE:o ShiftExprA:b {: RESULT=ExprBinary.Op.LTE .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a GTE:o ShiftExprA:b {: RESULT=ExprBinary.Op.GTE .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a NOTIN:o ShiftExprA:b {: RESULT=ExprBinary.Op.NOT_IN .make(o, null, a, mult(b)); :};
+CompareExprA ::= CompareExprB:a NOTEQUALS:o ShiftExprA:b {: RESULT=ExprBinary.Op.NOT_EQUALS.make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a NOTLT:o ShiftExprA:b {: RESULT=ExprBinary.Op.NOT_LT .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a NOTGT:o ShiftExprA:b {: RESULT=ExprBinary.Op.NOT_GT .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a NOTLTE:o ShiftExprA:b {: RESULT=ExprBinary.Op.NOT_LTE .make(o, null, a, b); :};
+CompareExprA ::= CompareExprB:a NOTGTE:o ShiftExprA:b {: RESULT=ExprBinary.Op.NOT_GTE .make(o, null, a, b); :};
+CompareExprA ::= ALL:o ShiftExprA {: if (1==1) throw new ErrorSyntax(o,"The \"all x\" construct is no longer supported. If you know the range of possible values of x, consider rewriting it as \"x == set_of_all_possible_values\"."); :};
+CompareExprA ::= NO:o ShiftExprA:b {: RESULT=ExprUnary.Op.NO .make(o, b); :};
+CompareExprA ::= SOME:o ShiftExprA:b {: RESULT=ExprUnary.Op.SOME .make(o, b); :};
+CompareExprA ::= LONE:o ShiftExprA:b {: RESULT=ExprUnary.Op.LONE .make(o, b); :};
+CompareExprA ::= ONE:o ShiftExprA:b {: RESULT=ExprUnary.Op.ONE .make(o, b); :};
+CompareExprA ::= SET:o ShiftExprA:b {: RESULT=ExprUnary.Op.SETOF.make(o, b); :};
+CompareExprA ::= SEQ:o ShiftExprA:b {: RESULT=ExprBinary.Op.ISSEQ_ARROW_LONE.make(o, null, ExprVar.make(o, "seq/Int"), b); parser.alloymodule.addSeq(o); :};
+CompareExprA ::= ShiftExprA:b {: RESULT=b; :};
+
+CompareExprB ::= CompareExprB:a IN:o ShiftExprB:b {: RESULT=ExprBinary.Op.IN .make(o, null, a, mult(b)); :};
+CompareExprB ::= CompareExprB:a EQUALS:o ShiftExprB:b {: RESULT=ExprBinary.Op.EQUALS .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a LT:o ShiftExprB:b {: RESULT=ExprBinary.Op.LT .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a GT:o ShiftExprB:b {: RESULT=ExprBinary.Op.GT .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a LTE:o ShiftExprB:b {: RESULT=ExprBinary.Op.LTE .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a GTE:o ShiftExprB:b {: RESULT=ExprBinary.Op.GTE .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a NOTIN:o ShiftExprB:b {: RESULT=ExprBinary.Op.NOT_IN .make(o, null, a, mult(b)); :};
+CompareExprB ::= CompareExprB:a NOTEQUALS:o ShiftExprB:b {: RESULT=ExprBinary.Op.NOT_EQUALS.make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a NOTLT:o ShiftExprB:b {: RESULT=ExprBinary.Op.NOT_LT .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a NOTGT:o ShiftExprB:b {: RESULT=ExprBinary.Op.NOT_GT .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a NOTLTE:o ShiftExprB:b {: RESULT=ExprBinary.Op.NOT_LTE .make(o, null, a, b); :};
+CompareExprB ::= CompareExprB:a NOTGTE:o ShiftExprB:b {: RESULT=ExprBinary.Op.NOT_GTE .make(o, null, a, b); :};
+CompareExprB ::= ALL:o ShiftExprB {: if (1==1) throw new ErrorSyntax(o,"The \"all x\" construct is no longer supported. If you know the range of possible values of x, consider rewriting it as \"x == set_of_all_possible_values\"."); :};
+CompareExprB ::= NO:o ShiftExprB:b {: RESULT=ExprUnary.Op.NO .make(o, b); :};
+CompareExprB ::= SOME:o ShiftExprB:b {: RESULT=ExprUnary.Op.SOME .make(o, b); :};
+CompareExprB ::= LONE:o ShiftExprB:b {: RESULT=ExprUnary.Op.LONE .make(o, b); :};
+CompareExprB ::= ONE:o ShiftExprB:b {: RESULT=ExprUnary.Op.ONE .make(o, b); :};
+CompareExprB ::= SET:o ShiftExprB:b {: RESULT=ExprUnary.Op.SETOF.make(o, b); :};
+CompareExprB ::= SEQ:o ShiftExprB:b {: RESULT=ExprBinary.Op.ISSEQ_ARROW_LONE.make(o, null, ExprVar.make(o,"seq/Int"), b); parser.alloymodule.addSeq(o); :};
+CompareExprB ::= ShiftExprB:b {: RESULT=b; :};
+
+ShiftExprA ::= UnionDiffExprA:b {: RESULT=b; :};
+ShiftExprA ::= ShiftExprB:a SHL:o Bind:b {: RESULT=ExprBinary.Op.SHL.make(o, null, a, b); :};
+ShiftExprA ::= ShiftExprB:a SHR:o Bind:b {: RESULT=ExprBinary.Op.SHR.make(o, null, a, b); :};
+ShiftExprA ::= ShiftExprB:a SHA:o Bind:b {: RESULT=ExprBinary.Op.SHA.make(o, null, a, b); :};
+ShiftExprB ::= UnionDiffExprB:b {: RESULT=b; :};
+ShiftExprB ::= ShiftExprB:a SHL:o UnionDiffExprB:b {: RESULT=ExprBinary.Op.SHL.make(o, null, a, b); :};
+ShiftExprB ::= ShiftExprB:a SHR:o UnionDiffExprB:b {: RESULT=ExprBinary.Op.SHR.make(o, null, a, b); :};
+ShiftExprB ::= ShiftExprB:a SHA:o UnionDiffExprB:b {: RESULT=ExprBinary.Op.SHA.make(o, null, a, b); :};
+
+UnionDiffExprA ::= MulExprA:b {: RESULT=b; :};
+UnionDiffExprA ::= UnionDiffExprB:a PLUS:o Bind:b {: RESULT=ExprBinary.Op.PLUS .make(o, null, a, b); :};
+UnionDiffExprA ::= UnionDiffExprB:a MINUS:o Bind:b {: RESULT=ExprBinary.Op.MINUS.make(o, null, a, b); :};
+UnionDiffExprA ::= UnionDiffExprB:a INTADD:o Bind:b {: RESULT=ExprBinary.Op.IPLUS.make(o, null, a, b); :};
+UnionDiffExprA ::= UnionDiffExprB:a INTSUB:o Bind:b {: RESULT=ExprBinary.Op.IMINUS.make(o, null, a, b); :};
+UnionDiffExprB ::= MulExprB:b {: RESULT=b; :};
+UnionDiffExprB ::= UnionDiffExprB:a PLUS:o MulExprB:b {: RESULT=ExprBinary.Op.PLUS .make(o, null, a, b); :};
+UnionDiffExprB ::= UnionDiffExprB:a MINUS:o MulExprB:b {: RESULT=ExprBinary.Op.MINUS.make(o, null, a, b); :};
+UnionDiffExprB ::= UnionDiffExprB:a INTADD:o MulExprB:b {: RESULT=ExprBinary.Op.IPLUS.make(o, null, a, b); :};
+UnionDiffExprB ::= UnionDiffExprB:a INTSUB:o MulExprB:b {: RESULT=ExprBinary.Op.IMINUS.make(o, null, a, b); :};
+
+MulExprA ::= NumUnopExprA:b {: RESULT=b; :};
+MulExprA ::= MulExprB:a INTMUL:o Bind:b {: RESULT=ExprBinary.Op.MUL .make(o, null, a, b); :};
+MulExprA ::= MulExprB:a INTDIV:o Bind:b {: RESULT=ExprBinary.Op.DIV .make(o, null, a, b); :};
+MulExprA ::= MulExprB:a INTREM:o Bind:b {: RESULT=ExprBinary.Op.REM .make(o, null, a, b); :};
+MulExprB ::= NumUnopExprB:b {: RESULT=b; :};
+MulExprB ::= MulExprB:a INTMUL:o NumUnopExprB:b {: RESULT=ExprBinary.Op.MUL .make(o, null, a, b); :};
+MulExprB ::= MulExprB:a INTDIV:o NumUnopExprB:b {: RESULT=ExprBinary.Op.DIV .make(o, null, a, b); :};
+MulExprB ::= MulExprB:a INTREM:o NumUnopExprB:b {: RESULT=ExprBinary.Op.REM .make(o, null, a, b); :};
+
+NumUnopExprA ::= OverrideExprA:b {: RESULT=b; :};
+NumUnopExprA ::= HASH:o Bind:b {: RESULT=ExprUnary.Op.CARDINALITY.make(o, b); :};
+NumUnopExprA ::= SUM:o Bind:b {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, b)); :};
+//[AM]: INT->SIGINT
+NumUnopExprA ::= INT:o Bind:b {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, b)); :};
+NumUnopExprA ::= HASH:o NumUnopExprA:b {: RESULT=ExprUnary.Op.CARDINALITY.make(o, b); :};
+NumUnopExprA ::= SUM:o NumUnopExprA:b {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, b)); :};
+//[AM]: INT->SIGINT
+NumUnopExprA ::= INT:o NumUnopExprA:b {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, b)); :};
+NumUnopExprB ::= OverrideExprB:b {: RESULT=b; :};
+NumUnopExprB ::= HASH:o NumUnopExprB:b {: RESULT=ExprUnary.Op.CARDINALITY.make(o, b); :};
+NumUnopExprB ::= SUM:o NumUnopExprB:b {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, b)); :};
+//[AM]: INT->SIGINT
+NumUnopExprB ::= INT:o NumUnopExprB:b {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, b)); :};
+
+OverrideExprA ::= IntersectExprA:b {: RESULT=b; :};
+OverrideExprA ::= OverrideExprB:a PLUSPLUS:o Bind:b {: RESULT=ExprBinary.Op.PLUSPLUS.make(o, null, a, b); :};
+OverrideExprB ::= IntersectExprB:b {: RESULT=b; :};
+OverrideExprB ::= OverrideExprB:a PLUSPLUS:o IntersectExprB:b {: RESULT=ExprBinary.Op.PLUSPLUS.make(o, null, a, b); :};
+
+IntersectExprA ::= RelationExprA:b {: RESULT=b; :};
+IntersectExprA ::= IntersectExprB:a AMPERSAND:o Bind:b {: RESULT=ExprBinary.Op.INTERSECT.make(o, null, a, b); :};
+IntersectExprB ::= RelationExprB:b {: RESULT=b; :};
+IntersectExprB ::= IntersectExprB:a AMPERSAND:o RelationExprB:b {: RESULT=ExprBinary.Op.INTERSECT.make(o, null, a, b); :};
+
+RelOp ::= ARROW:o {: RESULT=new Pair(o, ExprBinary.Op.ARROW ); :};
+RelOp ::= ANY_ARROW_SOME:o {: RESULT=new Pair(o, ExprBinary.Op.ANY_ARROW_SOME ); :};
+RelOp ::= ANY_ARROW_ONE:o {: RESULT=new Pair(o, ExprBinary.Op.ANY_ARROW_ONE ); :};
+RelOp ::= ANY_ARROW_LONE:o {: RESULT=new Pair(o, ExprBinary.Op.ANY_ARROW_LONE ); :};
+RelOp ::= SOME_ARROW_ANY:o {: RESULT=new Pair(o, ExprBinary.Op.SOME_ARROW_ANY ); :};
+RelOp ::= SOME_ARROW_SOME:o {: RESULT=new Pair(o, ExprBinary.Op.SOME_ARROW_SOME); :};
+RelOp ::= SOME_ARROW_ONE:o {: RESULT=new Pair(o, ExprBinary.Op.SOME_ARROW_ONE ); :};
+RelOp ::= SOME_ARROW_LONE:o {: RESULT=new Pair(o, ExprBinary.Op.SOME_ARROW_LONE); :};
+RelOp ::= ONE_ARROW_ANY:o {: RESULT=new Pair(o, ExprBinary.Op.ONE_ARROW_ANY ); :};
+RelOp ::= ONE_ARROW_SOME:o {: RESULT=new Pair(o, ExprBinary.Op.ONE_ARROW_SOME ); :};
+RelOp ::= ONE_ARROW_ONE:o {: RESULT=new Pair(o, ExprBinary.Op.ONE_ARROW_ONE ); :};
+RelOp ::= ONE_ARROW_LONE:o {: RESULT=new Pair(o, ExprBinary.Op.ONE_ARROW_LONE ); :};
+RelOp ::= LONE_ARROW_ANY:o {: RESULT=new Pair(o, ExprBinary.Op.LONE_ARROW_ANY ); :};
+RelOp ::= LONE_ARROW_SOME:o {: RESULT=new Pair(o, ExprBinary.Op.LONE_ARROW_SOME); :};
+RelOp ::= LONE_ARROW_ONE:o {: RESULT=new Pair(o, ExprBinary.Op.LONE_ARROW_ONE ); :};
+RelOp ::= LONE_ARROW_LONE:o {: RESULT=new Pair(o, ExprBinary.Op.LONE_ARROW_LONE); :};
+
+RelationExprA ::= DomainExprA:a {: RESULT=a; :};
+RelationExprA ::= DomainExprB:a RelOp:o Bind:b {: RESULT=o.b.make(o.a, null, a, b); :};
+RelationExprB ::= DomainExprB:a {: RESULT=a; :};
+RelationExprB ::= DomainExprB:a RelOp:o RelationExprB:b {: RESULT=o.b.make(o.a, null, a, b); :};
+
+DomainExprA ::= RangeExprA:b {: RESULT=b; :};
+DomainExprA ::= DomainExprB:a DOMAIN:o Bind:b {: RESULT=ExprBinary.Op.DOMAIN.make(o, null, a, b); :};
+DomainExprB ::= RangeExprB:b {: RESULT=b; :};
+DomainExprB ::= DomainExprB:a DOMAIN:o RangeExprB:b {: RESULT=ExprBinary.Op.DOMAIN.make(o, null, a, b); :};
+
+RangeExprA ::= BracketExprA:b {: RESULT=b; :};
+RangeExprA ::= RangeExprB:a RANGE:o Bind:b {: RESULT=ExprBinary.Op.RANGE.make(o, null, a, b); :};
+RangeExprB ::= BracketExprB:b {: RESULT=b; :};
+RangeExprB ::= RangeExprB:a RANGE:o BracketExprB:b {: RESULT=ExprBinary.Op.RANGE.make(o, null, a, b); :};
+
+BracketExprA ::= DotExprA:b {: RESULT=b; :};
+BracketExprB ::= DotExprB:b {: RESULT=b; :};
+BracketExprB ::= BracketExprB:a LBRACKET Exprs:b RBRACKET:c {: Expr aa=a; for(Expr bb:b) aa=t(aa.span().merge(bb.span()), c, bb, aa, c); RESULT=aa; :};
+BracketExprB ::= DISJ:a LBRACKET Exprs:b RBRACKET:c {: Expr aa=ExprVar.make(a, "disj"); for(Expr bb:b) aa=t(aa.span().merge(bb.span()), c, bb, aa, c); RESULT=aa; :};
+BracketExprB ::= TOTALORDER:a LBRACKET Exprs:b RBRACKET:c {: Expr aa=ExprVar.make(a, "pred/totalOrder"); for(Expr bb:b) aa=t(aa.span().merge(bb.span()), c, bb, aa, c); RESULT=aa; :};
+//[AM]: INT->SIGINT
+BracketExprB ::= INT:a LBRACKET Exprs:b RBRACKET:c {: Expr aa=ExprVar.make(a, "int"); for(Expr bb:b) aa=t(aa.span().merge(bb.span()), c, bb, aa, c); RESULT=ExprUnary.Op.CAST2SIGINT.make(a, aa); :};
+BracketExprB ::= SUM:a LBRACKET Exprs:b RBRACKET:c {: Expr aa=ExprVar.make(a, "int"); for(Expr bb:b) aa=t(aa.span().merge(bb.span()), c, bb, aa, c); RESULT=ExprUnary.Op.CAST2SIGINT.make(a, aa); :};
+
+DotExprA ::= UnopExprA:b {: RESULT=b; :};
+DotExprA ::= BracketExprB:a DOT:o Bind:b {: RESULT=t(o, null, a, b, null); :};
+DotExprB ::= UnopExprB:b {: RESULT=b; :};
+DotExprB ::= BracketExprB:a DOT:o UnopExprB:b {: RESULT=t(o, null, a, b, null); :};
+DotExprB ::= BracketExprB:a DOT:o DISJ:b {: RESULT=t(o, null, a, ExprVar.make(b, "disj"), null); :};
+DotExprB ::= BracketExprB:a DOT:o TOTALORDER:b {: RESULT=t(o, null, a, ExprVar.make(b, "pred/totalOrder"), null); :};
+//[AM]: INT->SIGINT
+DotExprB ::= BracketExprB:a DOT:o INT {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, a)); :};
+DotExprB ::= BracketExprB:a DOT:o SUM {: RESULT=ExprUnary.Op.CAST2SIGINT.make(o, ExprUnary.Op.CAST2INT.make(o, a)); :};
+
+UnopExprA ::= TILDE:o Bind:b {: RESULT=ExprUnary.Op.TRANSPOSE.make(o,b); :};
+UnopExprA ::= STAR:o Bind:b {: RESULT=ExprUnary.Op.RCLOSURE .make(o,b); :};
+UnopExprA ::= CARET:o Bind:b {: RESULT=ExprUnary.Op.CLOSURE .make(o,b); :};
+UnopExprA ::= TILDE:o UnopExprA:b {: RESULT=ExprUnary.Op.TRANSPOSE.make(o,b); :};
+UnopExprA ::= STAR:o UnopExprA:b {: RESULT=ExprUnary.Op.RCLOSURE .make(o,b); :};
+UnopExprA ::= CARET:o UnopExprA:b {: RESULT=ExprUnary.Op.CLOSURE .make(o,b); :};
+UnopExprB ::= BaseExpr:b {: RESULT=b; :};
+UnopExprB ::= TILDE:o UnopExprB:b {: RESULT=ExprUnary.Op.TRANSPOSE.make(o,b); :};
+UnopExprB ::= STAR:o UnopExprB:b {: RESULT=ExprUnary.Op.RCLOSURE .make(o,b); :};
+UnopExprB ::= CARET:o UnopExprB:b {: RESULT=ExprUnary.Op.CLOSURE .make(o,b); :};
+
+BaseExpr ::= NUMBER:x {: RESULT = x; :};
+BaseExpr ::= STR:x {: RESULT = x; :};
+BaseExpr ::= IDEN:o {: RESULT = ExprVar.make(o, "iden"); :};
+BaseExpr ::= THIS:o {: RESULT = ExprVar.make(o, "this"); :};
+BaseExpr ::= INTMIN:o {: RESULT = ExprConstant.Op.MIN.make(o, 0); :};
+BaseExpr ::= INTMAX:o {: RESULT = ExprConstant.Op.MAX.make(o, 0); :};
+BaseExpr ::= INTNEXT:o {: RESULT = ExprConstant.Op.NEXT.make(o, 0); :};
+BaseExpr ::= LPAREN Expr:x RPAREN {: RESULT = x; :};
+BaseExpr ::= SigRef:x {: RESULT = x; :};
+BaseExpr ::= AT:o Name:x {: nod(x); RESULT = ExprVar.make(o.merge(x.pos), "@"+x.label); :};
+BaseExpr ::= Super:x {: RESULT = x; :};
+BaseExpr ::= LBRACE:o Declz:a SuperOrBar:b RBRACE:c {: RESULT = ExprQt.Op.COMPREHENSION.make(o, c, a, b); :};
+BaseExpr ::= LBRACE:o Declz:a RBRACE:c {: RESULT = ExprQt.Op.COMPREHENSION.make(o, c, a, ExprConstant.TRUE); :};
+
+//=============================================================================
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/Alloy.lex b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/Alloy.lex
new file mode 100644
index 00000000..b6f2af29
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/Alloy.lex
@@ -0,0 +1,218 @@
+// Alloy Analyzer 4 -- Copyright (c) 2006-2008, Felix Chang
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package edu.mit.csail.sdg.alloy4compiler.parser;
+
+import edu.mit.csail.sdg.alloy4.Err;
+import edu.mit.csail.sdg.alloy4.ErrorSyntax;
+import edu.mit.csail.sdg.alloy4.Pos;
+import edu.mit.csail.sdg.alloy4.Version;
+import edu.mit.csail.sdg.alloy4compiler.ast.ExprConstant;
+import edu.mit.csail.sdg.alloy4compiler.ast.ExprVar;
+import java.util.List;
+import java_cup.runtime.*;
+
+/** Autogenerated by JFlex 1.4.1 */
+
+%%
+
+// There are 3 sets of "special tokens" that the lexer will not output.
+// But the Parser expects them.
+// So a special Filter class is written that sits between Lexer and Parser.
+// The Filter class observes the stream of tokens, and intelligently
+// merges or changes some primitive tokens into special tokens.
+// For more details, refer to the main documentation.
+//
+// But, very briefly, here are the 3 groups:
+//
+// (1) The lexer will generate only ALL, NO, LONE, ONE, SUM, SOME.
+// It will not output ALL2, NO2, LONE2, ONE2, SUM2, SOME2.
+// (The Filter class will change some ONE into ONE2, etc)
+//
+// (2) The lexer won't output NOTEQUALS, NOTIN, NOTLT, NOTLTE, NOTGT, NOTGTE.
+// Instead it outputs them as separate tokens (eg. "NOT" "EQUALS").
+// (The Filter class is used to merge them into a single "NOTEQUALS" token)
+//
+// (3) The lexer willn't output the 15 special arrows (eg. ONE_ARROW_ONE)
+// Instead it outputs them as separate tokens (eg. "ONE", "ARROW", "ONE")
+// (The Filter class is used to merge them into a single "ONE_ARROW_ONE" token)
+
+%class CompLexer // The ordering of these directives is important
+%cupsym CompSym
+%cup
+%eofval{
+ return new Symbol(CompSym.EOF, alloy_here(" "), alloy_here(" "));
+%eofval}
+%public
+%final
+%unicode
+%line
+%column
+%pack
+
+%{
+ public String alloy_filename="";
+ public int alloy_lineoffset=0; // If not zero, it is added to the current LINE NUMBER
+ public List alloy_seenDollar;
+ public CompModule alloy_module;
+ private final Pos alloy_here(String txt) {
+ return new Pos(alloy_filename,yycolumn+1,yyline+1+alloy_lineoffset,yycolumn+txt.length(),yyline+1);
+ }
+ private final Symbol alloy_sym(String txt, int type) {
+ Pos p = alloy_here(txt); return new Symbol(type, p, p);
+ }
+ private final Symbol alloy_string(String txt) throws Err {
+ Pos p = alloy_here(txt);
+ if (!Version.experimental) throw new ErrorSyntax(p, "String literal is not currently supported.");
+ StringBuilder sb = new StringBuilder(txt.length());
+ for(int i=0; i=txt.length()) throw new ErrorSyntax(p, "String literal cannot end with a single \\");
+ c = txt.charAt(i);
+ if (c=='n') c='\n'; else if (c!='\'' && c!='\"' && c!='\\') throw new ErrorSyntax(p, "String literal currenty only supports\nfour escape sequences: \\\\, \\n, \\\', and \\\"");
+ }
+ sb.append(c);
+ }
+ txt = sb.toString();
+ if (txt.length()==2) throw new ErrorSyntax(p, "Empty string is not allowed; try rewriting your model to use an empty set instead.");
+ return new Symbol(CompSym.STR, p, ExprConstant.Op.STRING.make(p, txt));
+ }
+ private final Symbol alloy_id(String txt) throws Err {
+ Pos p=alloy_here(txt);
+ if (alloy_seenDollar.size()==0 && txt.indexOf('$')>=0) alloy_seenDollar.add(null);
+ return new Symbol(CompSym.ID, p, ExprVar.make(p,txt));
+ }
+ private final Symbol alloy_num(String txt) throws Err {
+ Pos p=alloy_here(txt);
+ int n=0;
+ try {
+ n=Integer.parseInt(txt);
+ } catch(NumberFormatException ex) {
+ throw new ErrorSyntax(p, "The number "+txt+" is too large to be stored in a Java integer");
+ }
+ return new Symbol(CompSym.NUMBER, p, ExprConstant.Op.NUMBER.make(p, n));
+ }
+%}
+
+%%
+
+"!" { return alloy_sym(yytext(), CompSym.NOT );}
+"#" { return alloy_sym(yytext(), CompSym.HASH );}
+"&&" { return alloy_sym(yytext(), CompSym.AND );}
+"&" { return alloy_sym(yytext(), CompSym.AMPERSAND );}
+"(" { return alloy_sym(yytext(), CompSym.LPAREN );}
+")" { return alloy_sym(yytext(), CompSym.RPAREN );}
+"*" { return alloy_sym(yytext(), CompSym.STAR );}
+"++" { return alloy_sym(yytext(), CompSym.PLUSPLUS );}
+"+" { return alloy_sym(yytext(), CompSym.PLUS );}
+"," { return alloy_sym(yytext(), CompSym.COMMA );}
+"->" { return alloy_sym(yytext(), CompSym.ARROW );}
+"-" { return alloy_sym(yytext(), CompSym.MINUS );}
+"." { return alloy_sym(yytext(), CompSym.DOT );}
+"/" { return alloy_sym(yytext(), CompSym.SLASH );}
+"::" { return alloy_sym(yytext(), CompSym.DOT );}
+":>" { return alloy_sym(yytext(), CompSym.RANGE );}
+":" { return alloy_sym(yytext(), CompSym.COLON );}
+"<=>" { return alloy_sym(yytext(), CompSym.IFF );}
+"<=" { return alloy_sym(yytext(), CompSym.LTE );}
+"<:" { return alloy_sym(yytext(), CompSym.DOMAIN );}
+"<<" { return alloy_sym(yytext(), CompSym.SHL );}
+"<" { return alloy_sym(yytext(), CompSym.LT );}
+"=<" { return alloy_sym(yytext(), CompSym.LTE );}
+"=>" { return alloy_sym(yytext(), CompSym.IMPLIES );}
+"=" { return alloy_sym(yytext(), CompSym.EQUALS );}
+">>>" { return alloy_sym(yytext(), CompSym.SHR );}
+">>" { return alloy_sym(yytext(), CompSym.SHA );}
+">=" { return alloy_sym(yytext(), CompSym.GTE );}
+">" { return alloy_sym(yytext(), CompSym.GT );}
+"@" { return alloy_sym(yytext(), CompSym.AT );}
+"[" { return alloy_sym(yytext(), CompSym.LBRACKET );}
+"]" { return alloy_sym(yytext(), CompSym.RBRACKET );}
+"^" { return alloy_sym(yytext(), CompSym.CARET );}
+"{" { return alloy_sym(yytext(), CompSym.LBRACE );}
+"||" { return alloy_sym(yytext(), CompSym.OR );}
+"|" { return alloy_sym(yytext(), CompSym.BAR );}
+"}" { return alloy_sym(yytext(), CompSym.RBRACE );}
+"~" { return alloy_sym(yytext(), CompSym.TILDE );}
+"abstract" { return alloy_sym(yytext(), CompSym.ABSTRACT );}
+"all" { return alloy_sym(yytext(), CompSym.ALL );}
+"and" { return alloy_sym(yytext(), CompSym.AND );}
+"assert" { return alloy_sym(yytext(), CompSym.ASSERT );}
+"as" { return alloy_sym(yytext(), CompSym.AS );}
+"but" { return alloy_sym(yytext(), CompSym.BUT );}
+"check" { return alloy_sym(yytext(), CompSym.CHECK );}
+"disjoint" { return alloy_sym(yytext(), CompSym.DISJ );}
+"disj" { return alloy_sym(yytext(), CompSym.DISJ );}
+"else" { return alloy_sym(yytext(), CompSym.ELSE );}
+"enum" { return alloy_sym(yytext(), CompSym.ENUM );}
+"exactly" { return alloy_sym(yytext(), CompSym.EXACTLY );}
+"exhaustive" { return alloy_sym(yytext(), CompSym.EXH );}
+"exh" { return alloy_sym(yytext(), CompSym.EXH );}
+"expect" { return alloy_sym(yytext(), CompSym.EXPECT );}
+"extends" { return alloy_sym(yytext(), CompSym.EXTENDS );}
+"fact" { return alloy_sym(yytext(), CompSym.FACT );}
+"for" { return alloy_sym(yytext(), CompSym.FOR );}
+"fun" { return alloy_sym(yytext(), CompSym.FUN );}
+"iden" { return alloy_sym(yytext(), CompSym.IDEN );}
+"iff" { return alloy_sym(yytext(), CompSym.IFF );}
+"implies" { return alloy_sym(yytext(), CompSym.IMPLIES );}
+"Int" { return alloy_sym(yytext(), CompSym.SIGINT );}
+"int" { return alloy_sym(yytext(), CompSym.INT );}
+"in" { return alloy_sym(yytext(), CompSym.IN );}
+"let" { return alloy_sym(yytext(), CompSym.LET );}
+"lone" { return alloy_sym(yytext(), CompSym.LONE );}
+"module" { return alloy_sym(yytext(), CompSym.MODULE );}
+"none" { return alloy_sym(yytext(), CompSym.NONE );}
+"not" { return alloy_sym(yytext(), CompSym.NOT );}
+"no" { return alloy_sym(yytext(), CompSym.NO );}
+"one" { return alloy_sym(yytext(), CompSym.ONE );}
+"open" { return alloy_sym(yytext(), CompSym.OPEN );}
+"or" { return alloy_sym(yytext(), CompSym.OR );}
+"partition" { return alloy_sym(yytext(), CompSym.PART );}
+"part" { return alloy_sym(yytext(), CompSym.PART );}
+"pred" { return alloy_sym(yytext(), CompSym.PRED );}
+"private" { return alloy_sym(yytext(), CompSym.PRIVATE );}
+"run" { return alloy_sym(yytext(), CompSym.RUN );}
+"seq" { return alloy_sym(yytext(), CompSym.SEQ );}
+"set" { return alloy_sym(yytext(), CompSym.SET );}
+"sig" { return alloy_sym(yytext(), CompSym.SIG );}
+"some" { return alloy_sym(yytext(), CompSym.SOME );}
+"String" { return alloy_sym(yytext(), CompSym.STRING );}
+"sum" { return alloy_sym(yytext(), CompSym.SUM );}
+"this" { return alloy_sym(yytext(), CompSym.THIS );}
+"univ" { return alloy_sym(yytext(), CompSym.UNIV );}
+
+[\"] ([^\\\"] | ("\\" .))* [\"] [\$0-9a-zA-Z_\'\"] [\$0-9a-zA-Z_\'\"]* { throw new ErrorSyntax(alloy_here(yytext()),"String literal cannot be followed by a legal identifier character."); }
+[\"] ([^\\\"] | ("\\" .))* [\"] { return alloy_string(yytext()); }
+[\"] ([^\\\"] | ("\\" .))* { throw new ErrorSyntax(alloy_here(yytext()),"String literal is missing its closing \" character"); }
+[0-9][0-9]*[\$a-zA-Z_\'\"][\$0-9a-zA-Z_\'\"]* { throw new ErrorSyntax(alloy_here(yytext()),"Name cannot start with a number."); }
+[0-9][0-9]* { return alloy_num (yytext()); }
+[:jletter:][[:jletterdigit:]\'\"]* { return alloy_id (yytext()); }
+//[\$a-zA-Z][\$0-9a-zA-Z_\'\"]* { return alloy_id (yytext()); }
+
+"/**" ~"*/" { }
+
+"/*" ~"*/" { }
+
+("//"|"--") [^\r\n]* [\r\n] { }
+
+("//"|"--") [^\r\n]* { } // This rule is shorter than the previous rule,
+ // so it will only apply if the final line of a file is missing the \n or \r character.
+
+[ \t\f\r\n] { }
+
+. { throw new ErrorSyntax(alloy_here(" "), "Syntax error at the "+yytext()+" character."); }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-lexer.sh b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-lexer.sh
new file mode 100644
index 00000000..de50c662
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-lexer.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+java -cp $LIB_SDG/jars-external/JFlex.jar JFlex.Main --nobak -d . Alloy.lex
+
+sed -i 's/public java_cup.runtime.Symbol next_token() throws java.io.IOException/public java_cup.runtime.Symbol next_token() throws java.io.IOException, Err/' CompLexer.java
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-parser.sh b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-parser.sh
new file mode 100644
index 00000000..f27fddeb
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-parser.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+java -cp $LIB_SDG/jars-external/java-cup-11a.jar java_cup.Main \
+ -package edu.mit.csail.sdg.alloy4compiler.parser \
+ -parser CompParser \
+ -progress -time -compact_red \
+ -symbols CompSym < Alloy.cup
\ No newline at end of file
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-parsing-trace-from-log.sh b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-parsing-trace-from-log.sh
new file mode 100644
index 00000000..4c640e8d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/parser/generate-parsing-trace-from-log.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+## ----------------------------------------------------------------------
+##
+## This script takes a parsing log and from it generates a human readable
+## parsing trace, i.g. a sequence of shifts and reduces that took place.
+##
+## To get the parsing log just set the "debug" environment variable to
+## "yes" before runngin Alloy (or add "-Ddebug=yes" to the list of JVM
+## arguments)
+##
+## This script must be invoked from exactly the folder where it resides
+## because it needs to find the CompParser.java and CompSym.java files
+## in the same folder.
+##
+## ----------------------------------------------------------------------
+
+logFile=$1
+
+if [[ -z $logFile ]]
+then
+ echo "usage: $0 "
+ exit
+fi
+
+if [[ ! -f $logFile ]]
+then
+ echo "file $logFile doesn't exist"
+ exit
+fi
+
+while read line
+do
+ if [[ ! -z $(echo $line | grep "^reduce ") ]]
+ then
+ act=$(echo $line | sed 's/reduce //')
+ echo "reduce"
+ grep "case $act:" CompParser.java
+ elif [[ ! -z $(echo $line | grep "^shift ") ]]
+ then
+ sym=$(echo $line | sed 's/shift //')
+ sgn=$(grep " = $sym;" CompSym.java | sed 's/ public static final int //' | sed 's/ = '$sym';//')
+ echo "shift"
+ echo " $sgn"
+ else
+ echo $line
+ fi
+done < $logFile
\ No newline at end of file
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/translator/instance.txt b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/translator/instance.txt
new file mode 100644
index 00000000..dda19365
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/edu/mit/csail/sdg/alloy4compiler/translator/instance.txt
@@ -0,0 +1,26 @@
+WHOLEFILE = exactly 1 INSTANCE
+ 0 or more SOURCE
+
+INSTANCE = 0 or more PRIMSIG
+ 0 or more SUBSETSIG
+ 0 or more FIELD
+ 0 or more SKOLEM
+
+PRIMSIG = 0 or more ATOM
+
+SUBSETSIG = 0 or more ATOM
+ 1 or more TYPE
+
+FIELD = 0 or more TUPLE
+ 1 or more TYPES
+
+SKOLEM = 0 or more TUPLE
+ 1 or more TYPES
+
+TUPLE = 1 or more ATOM
+ATOM =
+
+TYPES = 1 or more TYPE
+TYPE =
+
+SOURCE =
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/error.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/error.gif
new file mode 100644
index 00000000..d37e28b4
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/error.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/general.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/general.gif
new file mode 100644
index 00000000..52976128
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/general.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/pref.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/pref.gif
new file mode 100644
index 00000000..eb2fd296
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/pref.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/theme.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/theme.gif
new file mode 100644
index 00000000..ad9e58d1
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/theme.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/toolbar.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/toolbar.gif
new file mode 100644
index 00000000..f3cbda1b
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/toolbar.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/toolbarviz.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/toolbarviz.gif
new file mode 100644
index 00000000..98abc4f5
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/toolbarviz.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/tree.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/tree.gif
new file mode 100644
index 00000000..40bd56a8
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/tree.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/viz.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/viz.gif
new file mode 100644
index 00000000..0f27b631
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/viz.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/help/image/xml.gif b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/xml.gif
new file mode 100644
index 00000000..45316860
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/help/image/xml.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/black.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/black.gif
new file mode 100644
index 00000000..bba84da2
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/black.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/blue.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/blue.gif
new file mode 100644
index 00000000..e6b2a7e9
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/blue.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cadetblue.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cadetblue.gif
new file mode 100644
index 00000000..d01b37fe
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cadetblue.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/chartreuse2.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/chartreuse2.gif
new file mode 100644
index 00000000..0502739f
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/chartreuse2.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cornflowerblue.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cornflowerblue.gif
new file mode 100644
index 00000000..64a86a64
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cornflowerblue.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cyan.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cyan.gif
new file mode 100644
index 00000000..720fb8cd
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/cyan.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/darkolivegreen2.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/darkolivegreen2.gif
new file mode 100644
index 00000000..827b3f9c
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/darkolivegreen2.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/gold.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/gold.gif
new file mode 100644
index 00000000..f6dc8ce8
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/gold.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/green2.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/green2.gif
new file mode 100644
index 00000000..5b689651
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/green2.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/lightgoldenrod.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/lightgoldenrod.gif
new file mode 100644
index 00000000..27fb805b
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/lightgoldenrod.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/lightgray.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/lightgray.gif
new file mode 100644
index 00000000..b3f1bfa7
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/lightgray.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/limegreen.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/limegreen.gif
new file mode 100644
index 00000000..29edc92b
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/limegreen.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/magenta.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/magenta.gif
new file mode 100644
index 00000000..8d3d29ca
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/magenta.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/magic.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/magic.gif
new file mode 100644
index 00000000..61598714
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/magic.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/palevioletred.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/palevioletred.gif
new file mode 100644
index 00000000..1de7a229
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/palevioletred.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/red.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/red.gif
new file mode 100644
index 00000000..c7604a5a
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/red.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/salmon.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/salmon.gif
new file mode 100644
index 00000000..1d3dca18
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/salmon.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/white.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/white.gif
new file mode 100644
index 00000000..02a767d3
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/white.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/yellow.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/yellow.gif
new file mode 100644
index 00000000..a09f1c23
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ColorIcons/yellow.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Mcircle.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Mcircle.gif
new file mode 100644
index 00000000..49a659f8
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Mcircle.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Mdiamond.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Mdiamond.gif
new file mode 100644
index 00000000..071f9807
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Mdiamond.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Msquare.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Msquare.gif
new file mode 100644
index 00000000..2db39335
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/Msquare.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/box.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/box.gif
new file mode 100644
index 00000000..788dee55
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/box.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/circle.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/circle.gif
new file mode 100644
index 00000000..87fe62e4
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/circle.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/diamond.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/diamond.gif
new file mode 100644
index 00000000..e0a1df4b
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/diamond.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/doublecircle.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/doublecircle.gif
new file mode 100644
index 00000000..33156e8c
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/doublecircle.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/doubleoctagon.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/doubleoctagon.gif
new file mode 100644
index 00000000..064307f3
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/doubleoctagon.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/egg.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/egg.gif
new file mode 100644
index 00000000..3d49e5a9
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/egg.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/ellipse.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/ellipse.gif
new file mode 100644
index 00000000..83dc2f41
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/ellipse.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/hexagon.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/hexagon.gif
new file mode 100644
index 00000000..e85ae033
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/hexagon.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/house.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/house.gif
new file mode 100644
index 00000000..4812b028
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/house.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invhouse.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invhouse.gif
new file mode 100644
index 00000000..71dc4b83
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invhouse.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invtrapezium.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invtrapezium.gif
new file mode 100644
index 00000000..7eaf0c04
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invtrapezium.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invtriangle.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invtriangle.gif
new file mode 100644
index 00000000..c3394467
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/invtriangle.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/octagon.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/octagon.gif
new file mode 100644
index 00000000..7ec0b4bb
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/octagon.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/parallelogram.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/parallelogram.gif
new file mode 100644
index 00000000..a4925d03
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/parallelogram.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/trapezium.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/trapezium.gif
new file mode 100644
index 00000000..2381a684
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/trapezium.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/triangle.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/triangle.gif
new file mode 100644
index 00000000..730a1b0c
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/triangle.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/tripleoctagon.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/tripleoctagon.gif
new file mode 100644
index 00000000..1f604228
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/ShapeIcons/tripleoctagon.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/bold.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/bold.gif
new file mode 100644
index 00000000..dda8863b
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/bold.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/dashed.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/dashed.gif
new file mode 100644
index 00000000..b12004d2
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/dashed.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/dotted.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/dotted.gif
new file mode 100644
index 00000000..be61201c
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/dotted.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/solid.gif b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/solid.gif
new file mode 100644
index 00000000..7d837f15
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/icons/StyleIcons/solid.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_execute.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_execute.gif
new file mode 100644
index 00000000..ddd93932
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_execute.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_execute_abort2.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_execute_abort2.gif
new file mode 100644
index 00000000..332d42ab
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_execute_abort2.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_graph.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_graph.gif
new file mode 100644
index 00000000..ac830a0a
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_graph.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_history.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_history.gif
new file mode 100644
index 00000000..c18eb6b4
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_history.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_new.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_new.gif
new file mode 100644
index 00000000..9d9f0ee2
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_new.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_open.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_open.gif
new file mode 100644
index 00000000..de4696ce
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_open.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_plaintext.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_plaintext.gif
new file mode 100644
index 00000000..7eace4b3
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_plaintext.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_reload.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_reload.gif
new file mode 100644
index 00000000..2652ff48
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_reload.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_save.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_save.gif
new file mode 100644
index 00000000..e243bd53
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_save.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings.gif
new file mode 100644
index 00000000..0195f5bb
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply.gif
new file mode 100644
index 00000000..87fe2dc1
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply2.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply2.gif
new file mode 100644
index 00000000..b91773cb
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply2.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply3.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply3.gif
new file mode 100644
index 00000000..7472cf1e
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_apply3.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close.gif
new file mode 100644
index 00000000..a550fa6d
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close2.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close2.gif
new file mode 100644
index 00000000..652ad79f
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close2.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close3.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close3.gif
new file mode 100644
index 00000000..20f9c5fb
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close3.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close4.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close4.gif
new file mode 100644
index 00000000..50209486
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close4.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close5.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close5.gif
new file mode 100644
index 00000000..3c417b5e
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_settings_close5.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/24_texttree.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_texttree.gif
new file mode 100644
index 00000000..ab735b1f
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/24_texttree.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/cb0.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/cb0.gif
new file mode 100644
index 00000000..4e47578d
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/cb0.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/cb1.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/cb1.gif
new file mode 100644
index 00000000..491dcb86
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/cb1.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/logo.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/logo.gif
new file mode 100644
index 00000000..c23a7f05
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/logo.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/menu0.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/menu0.gif
new file mode 100644
index 00000000..b1d498cd
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/menu0.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/menu1.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/menu1.gif
new file mode 100644
index 00000000..f204c0b5
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/menu1.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb01.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb01.gif
new file mode 100644
index 00000000..dd3972f8
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb01.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb02.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb02.gif
new file mode 100644
index 00000000..7755c002
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb02.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb03.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb03.gif
new file mode 100644
index 00000000..d3ad1e95
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb03.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb04.gif b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb04.gif
new file mode 100644
index 00000000..d0dff531
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/bin/images/tcb04.gif differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/addressBook1.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/addressBook1.als
new file mode 100644
index 00000000..06c2abf8
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/addressBook1.als
@@ -0,0 +1,19 @@
+module appendixA/addressBook1
+
+abstract sig Name {
+ address: set Addr+Name
+ }
+
+sig Alias, Group extends Name { }
+
+sig Addr { }
+
+fact {
+ // the invariants should go here
+ }
+
+pred show {
+ // simulation constraints should go here
+ }
+
+run show for 3
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/addressBook2.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/addressBook2.als
new file mode 100644
index 00000000..02ba6373
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/addressBook2.als
@@ -0,0 +1,27 @@
+module appendixA/addressBook2
+
+sig Addr, Name { }
+
+sig Book {
+ addr: Name -> (Name + Addr)
+ }
+
+pred inv [b: Book] {
+ let addr = b.addr |
+ all n: Name {
+ n not in n.^addr
+ some addr.n => some n.^addr & Addr
+ }
+ }
+
+pred add [b, b': Book, n: Name, t: Name+Addr] {
+ b'.addr = b.addr + n->t
+ }
+
+pred del [b, b': Book, n: Name, t: Name+Addr] {
+ b'.addr = b.addr - n->t
+ }
+
+fun lookup [b: Book, n: Name] : set Addr {
+ n.^(b.addr) & Addr
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/barbers.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/barbers.als
new file mode 100644
index 00000000..6205b5f4
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/barbers.als
@@ -0,0 +1,9 @@
+module appendixA/barbers
+
+sig Man { shaves: set Man }
+
+one sig Barber extends Man { }
+
+fact {
+ Barber.shaves = { m: Man | m not in m.shaves }
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/closure.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/closure.als
new file mode 100644
index 00000000..6a50e404
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/closure.als
@@ -0,0 +1,16 @@
+module appendixA/closure
+
+pred transCover [R, r: univ->univ] {
+ // You have to fill in the appropriate formula here
+}
+
+pred transClosure [R, r: univ->univ] {
+ transCover [R, r]
+ // You have to fill in the appropriate formula here
+}
+
+assert Equivalence {
+ all R, r: univ->univ | transClosure [R,r] iff R = ^r
+}
+
+check Equivalence for 3
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/distribution.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/distribution.als
new file mode 100644
index 00000000..6614b2e6
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/distribution.als
@@ -0,0 +1,7 @@
+module appendixA/distribution
+
+assert union {
+ all s: set univ, p, q: univ->univ | s.(p+q) = s.p + s.q
+}
+
+check union for 4
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/phones.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/phones.als
new file mode 100644
index 00000000..98c13677
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/phones.als
@@ -0,0 +1,6 @@
+module appendixA/phones
+
+sig Phone {
+ requests: set Phone,
+ connects: lone Phone
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/prison.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/prison.als
new file mode 100644
index 00000000..42e9ea00
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/prison.als
@@ -0,0 +1,17 @@
+module appendixA/prison
+
+sig Gang { members: set Inmate }
+
+sig Inmate { room: Cell }
+
+sig Cell { }
+
+pred safe {
+ // your constraints should go here
+ }
+
+pred show {
+ // your constraints should go here
+ }
+
+run show
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/properties.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/properties.als
new file mode 100644
index 00000000..cc06cc1e
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/properties.als
@@ -0,0 +1,23 @@
+module appendixA/properties
+
+pred show {
+ some r: univ->univ {
+ some r -- nonempty
+ r.r in r -- transitive
+ no iden & r -- irreflexive
+ ~r in r -- symmetric
+ ~r.r in iden -- functional
+ r.~r in iden -- injective
+ univ in r.univ -- total
+ univ in univ.r -- onto
+ }
+ }
+
+run show for 4
+
+assert ReformulateNonEmptinessOK {
+ all r: univ->univ |
+ some r iff (some x, y: univ | x->y in r)
+ }
+
+check ReformulateNonEmptinessOK
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/ring.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/ring.als
new file mode 100644
index 00000000..ec577666
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/ring.als
@@ -0,0 +1,9 @@
+module appendixA/ring
+
+sig Node { next: set Node }
+
+pred isRing {
+ // You have to fill in the appropriate formula here
+}
+
+run isRing for exactly 4 Node
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/spanning.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/spanning.als
new file mode 100644
index 00000000..5218e700
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/spanning.als
@@ -0,0 +1,17 @@
+module appendixA/spanning
+
+pred isTree [r: univ->univ] {
+ // You have to fill in the appropriate formula here
+}
+
+pred spans [r1, r2: univ->univ] {
+ // You have to fill in the appropriate formula here
+}
+
+pred show [r, t1, t2: univ->univ] {
+ spans [t1,r] and isTree [t1]
+ spans [t2,r] and isTree [t2]
+ t1 != t2
+}
+
+run show for 3
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/tree.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/tree.als
new file mode 100644
index 00000000..74750d40
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/tree.als
@@ -0,0 +1,7 @@
+module appendixA/tree
+
+pred isTree [r:univ->univ] {
+ // You have to fill in the appropriate formula here
+}
+
+run isTree for 4
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/tube.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/tube.als
new file mode 100644
index 00000000..03af8de9
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/tube.als
@@ -0,0 +1,21 @@
+module appendixA/tube
+
+abstract sig Station {
+ jubilee, central, circle: set Station
+ }
+
+sig Jubilee, Central, Circle in Station {}
+
+one sig
+ Stanmore, BakerStreet, BondStreet, Westminster, Waterloo,
+ WestRuislip, EalingBroadway, NorthActon, NottingHillGate,
+ LiverpoolStreet, Epping
+ extends Station {}
+
+fact {
+ // the constraints should go here
+ }
+
+pred show {}
+
+run show
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/undirected.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/undirected.als
new file mode 100644
index 00000000..937cedb1
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixA/undirected.als
@@ -0,0 +1,11 @@
+module appendixA/undirected
+
+sig Node { adjs: set Node }
+
+pred acyclic {
+ adjs = ~adjs
+ // You have to fill in additional formula here
+}
+
+run acyclic for 4
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/hotel.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/hotel.thm
new file mode 100644
index 00000000..fe20fbe1
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/hotel.thm
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p300-hotel.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p300-hotel.als
new file mode 100644
index 00000000..813846b2
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p300-hotel.als
@@ -0,0 +1,66 @@
+module hotel
+
+open util/ordering [Time] as timeOrder
+
+sig Key, Time {}
+
+sig Card {
+ fst, snd: Key
+ }
+
+sig Room {
+ key: Key one->Time
+ }
+
+one sig Desk {
+ issued: Key->Time,
+ prev: (Room->lone Key)->Time
+ }
+
+sig Guest {
+ cards: Card->Time
+ }
+
+pred init [t: Time] {
+ Desk.prev.t = key.t
+ no issued.t and no cards.t ------ bug! (see page 303)
+ }
+
+pred checkin [t,t': Time, r: Room, g: Guest] {
+ some c: Card {
+ c.fst = r.(Desk.prev.t)
+ c.snd not in Desk.issued.t
+ cards.t' = cards.t + g->c ------------- bug! (see page 306)
+ Desk.issued.t' = Desk.issued.t + c.snd
+ Desk.prev.t' = Desk.prev.t ++ r->c.snd
+ }
+ key.t = key.t'
+ }
+
+pred enter [t,t': Time, r: Room, g: Guest] {
+ some c: g.cards.t |
+ let k = r.key.t {
+ c.snd = k and key.t' = key.t
+ or c.fst = k and key.t' = key.t ++ r->c.snd
+ }
+ issued.t = issued.t' and (Desk<:prev).t = prev.t'
+ cards.t = cards.t'
+ }
+
+fact Traces {
+ init [first]
+ all t: Time - last | some g: Guest, r: Room |
+ checkin [t, t.next, r, g] or enter[t, t.next, r, g]
+ }
+
+assert NoIntruder {
+ no t1: Time, g: Guest, g': Guest-g, r: Room |
+ let t2=t1.next, t3=t2.next, t4=t3.next {
+ enter [t1, t2, r, g]
+ enter [t2, t3, r, g']
+ enter [t3, t4, r, g]
+ }
+ }
+
+-- This check reveals a bug (similar to Fig E.3) in which the initial key was issued twice.
+check NoIntruder for 3 but 6 Time, 1 Room, 2 Guest
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p303-hotel.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p303-hotel.als
new file mode 100644
index 00000000..652de3d8
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p303-hotel.als
@@ -0,0 +1,70 @@
+module hotel
+
+open util/ordering [Time] as timeOrder
+
+sig Key, Time {}
+
+sig Card {
+ fst, snd: Key
+ }
+
+sig Room {
+ key: Key one->Time
+ }
+
+one sig Desk {
+ issued: Key->Time,
+ prev: (Room->lone Key)->Time
+ }
+
+sig Guest {
+ cards: Card->Time
+ }
+
+pred init [t: Time] {
+ Desk.prev.t = key.t
+ Desk.issued.t = Room.key.t and no cards.t
+ }
+
+pred checkin [t,t': Time, r: Room, g: Guest] {
+ some c: Card {
+ c.fst = r.(Desk.prev.t)
+ c.snd not in Desk.issued.t
+ cards.t' = cards.t + g->c ------------- bug! (see page 306)
+ Desk.issued.t' = Desk.issued.t + c.snd
+ Desk.prev.t' = Desk.prev.t ++ r->c.snd
+ }
+ key.t = key.t'
+ }
+
+pred enter [t,t': Time, r: Room, g: Guest] {
+ some c: g.cards.t |
+ let k = r.key.t {
+ c.snd = k and key.t' = key.t
+ or c.fst = k and key.t' = key.t ++ r->c.snd
+ }
+ issued.t = issued.t' and (Desk<:prev).t = prev.t'
+ cards.t = cards.t'
+ }
+
+fact Traces {
+ init [first]
+ all t: Time - last | some g: Guest, r: Room |
+ checkin [t, t.next, r, g] or enter[t, t.next, r, g]
+ }
+
+assert NoIntruder {
+ no t1: Time, g: Guest, g': Guest-g, r: Room |
+ let t2=t1.next, t3=t2.next, t4=t3.next {
+ enter [t1, t2, r, g]
+ enter [t2, t3, r, g']
+ enter [t3, t4, r, g]
+ }
+ }
+
+-- This check now succeeds without finding any counterexample.
+check NoIntruder for 3 but 6 Time, 1 Room, 2 Guest
+
+-- To increase our confidence, we can increase the scope.
+-- This time, it finds a counterexample.
+check NoIntruder for 4 but 7 Time, 1 Room, 2 Guest
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p306-hotel.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p306-hotel.als
new file mode 100644
index 00000000..efcd7e7d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/appendixE/p306-hotel.als
@@ -0,0 +1,73 @@
+module hotel
+
+open util/ordering [Time] as timeOrder
+
+sig Key, Time {}
+
+sig Card {
+ fst, snd: Key
+ }
+
+sig Room {
+ key: Key one->Time
+ }
+
+one sig Desk {
+ issued: Key->Time,
+ prev: (Room->lone Key)->Time
+ }
+
+sig Guest {
+ cards: Card->Time
+ }
+
+pred init [t: Time] {
+ Desk.prev.t = key.t
+ Desk.issued.t = Room.key.t and no cards.t
+ }
+
+pred checkin [t,t': Time, r: Room, g: Guest] {
+ some c: Card {
+ c.fst = r.(Desk.prev.t)
+ c.snd not in Desk.issued.t
+ cards.t' = cards.t ++ g->c
+ Desk.issued.t' = Desk.issued.t + c.snd
+ Desk.prev.t' = Desk.prev.t ++ r->c.snd
+ }
+ key.t = key.t'
+ }
+
+pred enter [t,t': Time, r: Room, g: Guest] {
+ some c: g.cards.t |
+ let k = r.key.t {
+ c.snd = k and key.t' = key.t
+ or c.fst = k and key.t' = key.t ++ r->c.snd
+ }
+ issued.t = issued.t' and (Desk<:prev).t = prev.t'
+ cards.t = cards.t'
+ }
+
+fact Traces {
+ init [first]
+ all t: Time - last | some g: Guest, r: Room |
+ checkin [t, t.next, r, g] or enter[t, t.next, r, g]
+ }
+
+assert NoIntruder {
+ no t1: Time, g: Guest, g': Guest-g, r: Room |
+ let t2=t1.next, t3=t2.next, t4=t3.next {
+ enter [t1, t2, r, g]
+ enter [t2, t3, r, g']
+ enter [t3, t4, r, g]
+ }
+ }
+
+-- This check now succeeds without finding any counterexample.
+check NoIntruder for 3 but 6 Time, 1 Room, 2 Guest
+
+-- This check now succeeds without finding any counterexample.
+check NoIntruder for 4 but 7 Time, 1 Room, 2 Guest
+
+-- We can try to increase the scope further.
+-- This check also succeeds without finding any counterexample.
+check NoIntruder for 6 but 12 Time, 3 Room, 3 Guest
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1a.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1a.als
new file mode 100644
index 00000000..643c0f2c
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1a.als
@@ -0,0 +1,12 @@
+module tour/addressBook1a ----- Page 6
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred show { }
+
+// This command generates an instance similar to Fig 2.1
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1b.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1b.als
new file mode 100644
index 00000000..1d4255e9
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1b.als
@@ -0,0 +1,14 @@
+module tour/addressBook1b ----- Page 8
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred show [b: Book] {
+ #b.addr > 1
+}
+
+// This command generates an instance similar to Fig 2.2
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1c.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1c.als
new file mode 100644
index 00000000..85b18616
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1c.als
@@ -0,0 +1,15 @@
+module tour/addressBook1c ----- Page 8
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred show [b: Book] {
+ #b.addr > 1
+ some n: Name | #n.(b.addr) > 1
+}
+
+// This command should not find any instance.
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1d.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1d.als
new file mode 100644
index 00000000..c553a4f9
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1d.als
@@ -0,0 +1,15 @@
+module tour/addressBook1d ----- Page 9
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred show [b: Book] {
+ #b.addr > 1
+ #Name.(b.addr) > 1
+}
+
+// This command generates an instance similar to Fig 2.3
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1e.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1e.als
new file mode 100644
index 00000000..18165f34
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1e.als
@@ -0,0 +1,14 @@
+module tour/addressBook1e ----- Page 11
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred add [b, b': Book, n: Name, a: Addr] {
+ b'.addr = b.addr + n->a
+}
+
+// This command generates an instance similar to Fig 2.4
+run add for 3 but 2 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1f.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1f.als
new file mode 100644
index 00000000..8774cd7b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1f.als
@@ -0,0 +1,19 @@
+module tour/addressBook1f ----- Page 12
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred add [b, b': Book, n: Name, a: Addr] {
+ b'.addr = b.addr + n->a
+}
+
+pred showAdd [b, b': Book, n: Name, a: Addr] {
+ add [b, b', n, a]
+ #Name.(b'.addr) > 1
+}
+
+// This command generates an instance similar to Fig 2.5
+run showAdd for 3 but 2 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1g.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1g.als
new file mode 100644
index 00000000..4eeac73a
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1g.als
@@ -0,0 +1,29 @@
+module tour/addressBook1g ----- Page 14
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred add [b, b': Book, n: Name, a: Addr] {
+ b'.addr = b.addr + n->a
+}
+
+pred del [b, b': Book, n: Name] {
+ b'.addr = b.addr - n->Addr
+}
+
+fun lookup [b: Book, n: Name] : set Addr {
+ n.(b.addr)
+}
+
+assert delUndoesAdd {
+ all b, b', b'': Book, n: Name, a: Addr |
+ add [b, b', n, a] and del [b', b'', n]
+ implies
+ b.addr = b''.addr
+}
+
+// This command generates an instance similar to Fig 2.6
+check delUndoesAdd for 3
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1h.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1h.als
new file mode 100644
index 00000000..115eab78
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook1h.als
@@ -0,0 +1,64 @@
+module tour/addressBook1h ------- Page 14..16
+
+sig Name, Addr { }
+
+sig Book {
+ addr: Name -> lone Addr
+}
+
+pred show [b: Book] {
+ #b.addr > 1
+ #Name.(b.addr) > 1
+}
+run show for 3 but 1 Book
+
+pred add [b, b': Book, n: Name, a: Addr] {
+ b'.addr = b.addr + n->a
+}
+
+pred del [b, b': Book, n: Name] {
+ b'.addr = b.addr - n->Addr
+}
+
+fun lookup [b: Book, n: Name] : set Addr {
+ n.(b.addr)
+}
+
+pred showAdd [b, b': Book, n: Name, a: Addr] {
+ add [b, b', n, a]
+ #Name.(b'.addr) > 1
+}
+run showAdd for 3 but 2 Book
+
+assert delUndoesAdd {
+ all b, b', b'': Book, n: Name, a: Addr |
+ no n.(b.addr) and add [b, b', n, a] and del [b', b'', n]
+ implies
+ b.addr = b''.addr
+}
+
+assert addIdempotent {
+ all b, b', b'': Book, n: Name, a: Addr |
+ add [b, b', n, a] and add [b', b'', n, a]
+ implies
+ b'.addr = b''.addr
+}
+
+assert addLocal {
+ all b, b': Book, n, n': Name, a: Addr |
+ add [b, b', n, a] and n != n'
+ implies
+ lookup [b, n'] = lookup [b', n']
+}
+
+// This command should not find any counterexample.
+check delUndoesAdd for 3
+
+// This command should not find any counterexample.
+check delUndoesAdd for 10 but 3 Book
+
+// This command should not find any counterexample.
+check addIdempotent for 3
+
+// This command should not find any counterexample.
+check addLocal for 3 but 2 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2a.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2a.als
new file mode 100644
index 00000000..ab94951b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2a.als
@@ -0,0 +1,16 @@
+module tour/addressBook2a ----- Page 18
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ addr: Name->Target
+}
+
+pred show [b:Book] { some b.addr }
+
+// This command generates an instance similar to Fig 2.9
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2b.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2b.als
new file mode 100644
index 00000000..a6d1da91
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2b.als
@@ -0,0 +1,18 @@
+module tour/addressBook2b ----- Page 19
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ addr: Name->Target
+} {
+ no n: Name | n in n.^addr
+}
+
+pred show [b:Book] { some b.addr }
+
+// This command generates an instance similar to Fig 2.10
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2c.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2c.als
new file mode 100644
index 00000000..33ded6b3
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2c.als
@@ -0,0 +1,18 @@
+module tour/addressBook2c ----- Page 20
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ addr: Name->Target
+} {
+ no n: Name | n in n.^addr
+}
+
+pred show [b:Book] { some Alias.(b.addr) }
+
+// This command generates an instance similar to Fig 2.11
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2d.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2d.als
new file mode 100644
index 00000000..adac4fbe
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2d.als
@@ -0,0 +1,19 @@
+module tour/addressBook2d ----- Page 21
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ addr: Name->Target
+} {
+ no n: Name | n in n.^addr
+ all a: Alias | lone a.addr
+}
+
+pred show [b:Book] { some Alias.(b.addr) }
+
+// This command generates an instance similar to Fig 2.12
+run show for 3 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2e.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2e.als
new file mode 100644
index 00000000..3baea5ac
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook2e.als
@@ -0,0 +1,56 @@
+module tour/addressBook2e --- this is the final model in Fig 2.14
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ names: set Name,
+ addr: names->some Target
+} {
+ no n: Name | n in n.^addr
+ all a: Alias | lone a.addr
+}
+
+pred add [b, b': Book, n: Name, t: Target] { b'.addr = b.addr + n->t }
+pred del [b, b': Book, n: Name, t: Target] { b'.addr = b.addr - n->t }
+fun lookup [b: Book, n: Name] : set Addr { n.^(b.addr) & Addr }
+
+assert delUndoesAdd {
+ all b, b', b'': Book, n: Name, t: Target |
+ no n.(b.addr) and add [b, b', n, t] and del [b', b'', n, t]
+ implies
+ b.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check delUndoesAdd for 3
+
+assert addIdempotent {
+ all b, b', b'': Book, n: Name, t: Target |
+ add [b, b', n, t] and add [b', b'', n, t]
+ implies
+ b'.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check addIdempotent for 3
+
+assert addLocal {
+ all b, b': Book, n, n': Name, t: Target |
+ add [b, b', n, t] and n != n'
+ implies
+ lookup [b, n'] = lookup [b', n']
+}
+
+// This shows a counterexample similar to Fig 2.13
+check addLocal for 3 but 2 Book
+
+assert lookupYields {
+ all b: Book, n: b.names | some lookup [b,n]
+}
+
+// This shows a counterexample similar to Fig 2.12
+check lookupYields for 4 but 1 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3a.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3a.als
new file mode 100644
index 00000000..5ff9eee0
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3a.als
@@ -0,0 +1,38 @@
+module tour/addressBook3a ----- Page 25
+
+open util/ordering [Book] as BookOrder
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ names: set Name,
+ addr: names->some Target
+} {
+ no n: Name | n in n.^addr
+ all a: Alias | lone a.addr
+}
+
+pred add [b, b': Book, n: Name, t: Target] { b'.addr = b.addr + n->t }
+pred del [b, b': Book, n: Name, t: Target] { b'.addr = b.addr - n->t }
+fun lookup [b: Book, n: Name] : set Addr { n.^(b.addr) & Addr }
+
+pred init [b: Book] { no b.addr }
+
+fact traces {
+ init [first]
+ all b: Book-last |
+ let b' = b.next |
+ some n: Name, t: Target |
+ add [b, b', n, t] or del [b, b', n, t]
+}
+
+------------------------------------------------------
+
+pred show { }
+
+// This command generates an instance similar to Fig 2.15
+run show for 4
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3b.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3b.als
new file mode 100644
index 00000000..5c4b835d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3b.als
@@ -0,0 +1,76 @@
+module tour/addressBook3b ----- Page 26
+
+open util/ordering [Book] as BookOrder
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ names: set Name,
+ addr: names->some Target
+} {
+ no n: Name | n in n.^addr
+ all a: Alias | lone a.addr
+}
+
+pred add [b, b': Book, n: Name, t: Target] { b'.addr = b.addr + n->t }
+pred del [b, b': Book, n: Name, t: Target] { b'.addr = b.addr - n->t }
+fun lookup [b: Book, n: Name] : set Addr { n.^(b.addr) & Addr }
+
+pred init [b: Book] { no b.addr }
+
+fact traces {
+ init [first]
+ all b: Book-last |
+ let b' = b.next |
+ some n: Name, t: Target |
+ add [b, b', n, t] or del [b, b', n, t]
+}
+
+------------------------------------------------------
+
+assert delUndoesAdd {
+ all b, b', b'': Book, n: Name, t: Target |
+ no n.(b.addr) and add [b, b', n, t] and del [b', b'', n, t]
+ implies
+ b.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check delUndoesAdd for 3
+
+------------------------------------------------------
+
+assert addIdempotent {
+ all b, b', b'': Book, n: Name, t: Target |
+ add [b, b', n, t] and add [b', b'', n, t]
+ implies
+ b'.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check addIdempotent for 3
+
+------------------------------------------------------
+
+assert addLocal {
+ all b, b': Book, n, n': Name, t: Target |
+ add [b, b', n, t] and n != n'
+ implies
+ lookup [b, n'] = lookup [b', n']
+}
+
+// This should not find any counterexample.
+check addLocal for 3 but 2 Book
+
+------------------------------------------------------
+
+assert lookupYields {
+ all b: Book, n: b.names | some lookup [b,n]
+}
+
+// This shows a counterexample similar to Fig 2.16
+check lookupYields for 3 but 4 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3c.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3c.als
new file mode 100644
index 00000000..284f8c36
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3c.als
@@ -0,0 +1,81 @@
+module tour/addressBook3c ----- Page 27
+
+open util/ordering [Book] as BookOrder
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ names: set Name,
+ addr: names->some Target
+} {
+ no n: Name | n in n.^addr
+ all a: Alias | lone a.addr
+}
+
+pred add [b, b': Book, n: Name, t: Target] {
+ t in Addr or some lookup [b, Name&t]
+ b'.addr = b.addr + n->t
+}
+
+pred del [b, b': Book, n: Name, t: Target] { b'.addr = b.addr - n->t }
+
+fun lookup [b: Book, n: Name] : set Addr { n.^(b.addr) & Addr }
+
+pred init [b: Book] { no b.addr }
+
+fact traces {
+ init [first]
+ all b: Book-last |
+ let b' = b.next |
+ some n: Name, t: Target |
+ add [b, b', n, t] or del [b, b', n, t]
+}
+
+------------------------------------------------------
+
+assert delUndoesAdd {
+ all b, b', b'': Book, n: Name, t: Target |
+ no n.(b.addr) and add [b, b', n, t] and del [b', b'', n, t]
+ implies
+ b.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check delUndoesAdd for 3
+
+------------------------------------------------------
+
+assert addIdempotent {
+ all b, b', b'': Book, n: Name, t: Target |
+ add [b, b', n, t] and add [b', b'', n, t]
+ implies
+ b'.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check addIdempotent for 3
+
+------------------------------------------------------
+
+assert addLocal {
+ all b, b': Book, n, n': Name, t: Target |
+ add [b, b', n, t] and n != n'
+ implies
+ lookup [b, n'] = lookup [b', n']
+}
+
+// This should not find any counterexample.
+check addLocal for 3 but 2 Book
+
+------------------------------------------------------
+
+assert lookupYields {
+ all b: Book, n: b.names | some lookup [b,n]
+}
+
+// This shows a counterexample similar to Fig 2.17
+check lookupYields for 3 but 4 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3d.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3d.als
new file mode 100644
index 00000000..aceac514
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/addressBook3d.als
@@ -0,0 +1,87 @@
+module tour/addressBook3d ----- this is the final model in fig 2.18
+
+open util/ordering [Book] as BookOrder
+
+abstract sig Target { }
+sig Addr extends Target { }
+abstract sig Name extends Target { }
+
+sig Alias, Group extends Name { }
+
+sig Book {
+ names: set Name,
+ addr: names->some Target
+} {
+ no n: Name | n in n.^addr
+ all a: Alias | lone a.addr
+}
+
+pred add [b, b': Book, n: Name, t: Target] {
+ t in Addr or some lookup [b, Name&t]
+ b'.addr = b.addr + n->t
+}
+
+pred del [b, b': Book, n: Name, t: Target] {
+ no b.addr.n or some n.(b.addr) - t
+ b'.addr = b.addr - n->t
+}
+
+fun lookup [b: Book, n: Name] : set Addr { n.^(b.addr) & Addr }
+
+pred init [b: Book] { no b.addr }
+
+fact traces {
+ init [first]
+ all b: Book-last |
+ let b' = b.next |
+ some n: Name, t: Target |
+ add [b, b', n, t] or del [b, b', n, t]
+}
+
+------------------------------------------------------
+
+assert delUndoesAdd {
+ all b, b', b'': Book, n: Name, t: Target |
+ no n.(b.addr) and add [b, b', n, t] and del [b', b'', n, t]
+ implies
+ b.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check delUndoesAdd for 3
+
+------------------------------------------------------
+
+assert addIdempotent {
+ all b, b', b'': Book, n: Name, t: Target |
+ add [b, b', n, t] and add [b', b'', n, t]
+ implies
+ b'.addr = b''.addr
+}
+
+// This should not find any counterexample.
+check addIdempotent for 3
+
+------------------------------------------------------
+
+assert addLocal {
+ all b, b': Book, n, n': Name, t: Target |
+ add [b, b', n, t] and n != n'
+ implies
+ lookup [b, n'] = lookup [b', n']
+}
+
+// This should not find any counterexample.
+check addLocal for 3 but 2 Book
+
+------------------------------------------------------
+
+assert lookupYields {
+ all b: Book, n: b.names | some lookup [b,n]
+}
+
+// This should not find any counterexample.
+check lookupYields for 3 but 4 Book
+
+// This should not find any counterexample.
+check lookupYields for 6
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/theme.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/theme.thm
new file mode 100644
index 00000000..2b839993
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter2/theme.thm
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/filesystem.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/filesystem.als
new file mode 100644
index 00000000..c45e776b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/filesystem.als
@@ -0,0 +1,28 @@
+module chapter4/filesystem ----- The model from page 125
+
+abstract sig Object {}
+
+sig Dir extends Object {contents: set Object}
+
+one sig Root extends Dir { }
+
+sig File extends Object {}
+
+fact {
+ Object in Root.*contents
+ }
+
+assert SomeDir {
+ all o: Object - Root | some contents.o
+ }
+check SomeDir // This assertion is valid
+
+assert RootTop {
+ no o: Object | Root in o.contents
+ }
+check RootTop // This assertion should produce a counterexample
+
+assert FileInDir {
+ all f: File | some contents.f
+ }
+check FileInDir // This assertion is valid
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa1.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa1.als
new file mode 100644
index 00000000..c05c85c9
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa1.als
@@ -0,0 +1,44 @@
+module language/grandpa1 ---- Page 84, 85
+
+abstract sig Person {
+ father: lone Man,
+ mother: lone Woman
+ }
+
+sig Man extends Person {
+ wife: lone Woman
+ }
+
+sig Woman extends Person {
+ husband: lone Man
+ }
+
+fact {
+ no p: Person | p in p.^(mother+father)
+ wife = ~husband
+ }
+
+assert NoSelfFather {
+ no m: Man | m = m.father
+ }
+
+// This should not find any counterexample.
+check NoSelfFather
+
+fun grandpas [p: Person] : set Person {
+ p.(mother+father).father
+ }
+
+pred ownGrandpa [p: Person] {
+ p in p.grandpas
+ }
+
+// This should not find any instance.
+run ownGrandpa for 4 Person
+
+assert NoSelfGrandpa {
+ no p: Person | p in p.grandpas
+ }
+
+// This should not find any counterexample
+check NoSelfGrandpa for 4 Person
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa2.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa2.als
new file mode 100644
index 00000000..08dda731
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa2.als
@@ -0,0 +1,38 @@
+module language/grandpa2 ---- Page 86
+
+abstract sig Person {
+ father: lone Man,
+ mother: lone Woman
+ }
+
+sig Man extends Person {
+ wife: lone Woman
+ }
+
+sig Woman extends Person {
+ husband: lone Man
+ }
+
+fact {
+ no p: Person | p in p.^(mother+father)
+ wife = ~husband
+ }
+
+assert NoSelfFather {
+ no m: Man | m = m.father
+ }
+
+// This should not find any counterexample.
+check NoSelfFather
+
+fun grandpas [p: Person] : set Person {
+ let parent = mother + father + father.wife + mother.husband |
+ p.parent.parent & Man
+ }
+
+pred ownGrandpa [p: Person] {
+ p in p.grandpas
+ }
+
+// This generates an instance similar to Fig 4.2
+run ownGrandpa for 4 Person
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa3.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa3.als
new file mode 100644
index 00000000..d9147cbf
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/grandpa3.als
@@ -0,0 +1,70 @@
+module language/grandpa3 ---- the final model in fig 4.4
+
+abstract sig Person {
+ father: lone Man,
+ mother: lone Woman
+ }
+
+sig Man extends Person {
+ wife: lone Woman
+ }
+
+sig Woman extends Person {
+ husband: lone Man
+ }
+
+fact Biology {
+ no p: Person | p in p.^(mother+father)
+ }
+
+fact Terminology {
+ wife = ~husband
+ }
+
+fact SocialConvention {
+ no (wife+husband) & ^(mother+father)
+ }
+
+------------------------------------------
+
+assert NoSelfFather {
+ no m: Man | m = m.father
+ }
+
+// This should not find any counterexample.
+check NoSelfFather
+
+------------------------------------------
+
+fun grandpas [p: Person] : set Person {
+ let parent = mother + father + father.wife + mother.husband |
+ p.parent.parent & Man
+ }
+
+pred ownGrandpa [p: Person] {
+ p in p.grandpas
+ }
+
+// This generates an instance similar to Fig 4.3
+run ownGrandpa for 4 Person
+
+------------------------------------------
+
+pred SocialConvention1 {
+ no (wife + husband) & ^(mother + father)
+ }
+
+pred SocialConvention2 {
+ let parent = mother + father {
+ no m: Man | some m.wife and m.wife in m.*parent.mother
+ no w: Woman | some w.husband and w.husband in w.*parent.father
+ }
+ }
+
+// This assertion was described on page 90.
+assert Same {
+ SocialConvention1 iff SocialConvention2
+ }
+
+// This should not find any counterexample
+check Same
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/lights.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/lights.als
new file mode 100644
index 00000000..24997705
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter4/lights.als
@@ -0,0 +1,41 @@
+module chapter4/lights ----- The model from page 127
+
+abstract sig Color {}
+
+one sig Red, Yellow, Green extends Color {}
+
+fun colorSequence: Color -> Color {
+ Color <: iden + Red->Green + Green->Yellow + Yellow->Red
+ }
+
+sig Light {}
+sig LightState {color: Light -> one Color}
+sig Junction {lights: set Light}
+
+fun redLights [s: LightState]: set Light { s.color.Red }
+
+pred mostlyRed [s: LightState, j: Junction] {
+ lone j.lights - redLights[s]
+ }
+
+pred trans [s, s': LightState, j: Junction] {
+ lone x: j.lights | s.color[x] != s'.color[x]
+ all x: j.lights |
+ let step = s.color[x] -> s'.color[x] {
+ step in colorSequence
+ step in Red->(Color-Red) => j.lights in redLights[s]
+ }
+ }
+
+assert Safe {
+ all s, s': LightState, j: Junction |
+ mostlyRed [s, j] and trans [s, s', j] => mostlyRed [s', j]
+ }
+
+check Safe for 3 but 1 Junction
+
+//assert ColorSequenceDeterministic {
+// all c: Color | lone c.colorSequence
+// }
+//
+//check ColorSequenceDeterministic
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/addressBook.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/addressBook.als
new file mode 100644
index 00000000..5d7dee56
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/addressBook.als
@@ -0,0 +1,26 @@
+module chapter5/addressBook --- the model in fig 5.1
+
+abstract sig Target {}
+
+sig Addr extends Target {}
+sig Name extends Target {}
+sig Book {addr: Name -> Target}
+
+fact Acyclic {all b: Book | no n: Name | n in n.^(b.addr)}
+
+pred add [b, b': Book, n: Name, t: Target] {
+ b'.addr = b.addr + n -> t
+ }
+
+// This command should produce an instance similar to Fig 5.2
+run add for 3 but 2 Book
+
+fun lookup [b: Book, n: Name]: set Addr {n.^(b.addr) & Addr}
+
+assert addLocal {
+ all b,b': Book, n,n': Name, t: Target |
+ add [b,b',n,t] and n != n' => lookup [b,n'] = lookup [b',n']
+ }
+
+// This command should produce a counterexample similar to Fig 5.3
+check addLocal for 3 but 2 Book
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/lists.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/lists.als
new file mode 100644
index 00000000..79cbd0da
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/lists.als
@@ -0,0 +1,23 @@
+module chapter5/lists ---- page 157
+
+some sig Element {}
+
+abstract sig List {}
+one sig EmptyList extends List {}
+sig NonEmptyList extends List {
+ element: Element,
+ rest: List
+ }
+
+fact ListGenerator {
+ all list: List, e: Element |
+ some list': List | list'.rest = list and list'.element = e
+ }
+
+assert FalseAssertion {
+ all list: List | list != list
+ }
+
+// This check finds no counterexample since
+// the only possible counterexamples are infinite.
+check FalseAssertion
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/sets1.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/sets1.als
new file mode 100644
index 00000000..dc998820
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/sets1.als
@@ -0,0 +1,16 @@
+module chapter5/sets1 ----- page 156
+
+sig Set {
+ elements: set Element
+}
+
+sig Element {}
+
+assert Closed {
+ all s0, s1: Set |
+ some s2: Set |
+ s2.elements = s0.elements + s1.elements
+ }
+
+// This check should produce a counterexample
+check Closed
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/sets2.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/sets2.als
new file mode 100644
index 00000000..265a1e64
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter5/sets2.als
@@ -0,0 +1,21 @@
+module chapter5/sets2 ----- page 157
+
+sig Set {
+ elements: set Element
+}
+
+sig Element {}
+
+assert Closed {
+ all s0, s1: Set |
+ some s2: Set |
+ s2.elements = s0.elements + s1.elements
+ }
+
+fact SetGenerator {
+ some s: Set | no s.elements
+ all s: Set, e: Element | some s': Set | s'.elements = s.elements + e
+ }
+
+// This check should not produce a counterexample
+check Closed for 4 Element, 16 Set
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel.thm
new file mode 100644
index 00000000..acdb8d09
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel.thm
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel1.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel1.als
new file mode 100644
index 00000000..22610689
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel1.als
@@ -0,0 +1,101 @@
+module chapter6/hotel1 --- the model up to the top of page 191
+
+open util/ordering[Time] as to
+open util/ordering[Key] as ko
+
+sig Key {}
+sig Time {}
+
+sig Room {
+ keys: set Key,
+ currentKey: keys one -> Time
+ }
+
+fact DisjointKeySets {
+ -- each key belongs to at most one room
+ Room<:keys in Room lone-> Key
+ }
+
+one sig FrontDesk {
+ lastKey: (Room -> lone Key) -> Time,
+ occupant: (Room -> Guest) -> Time
+ }
+
+sig Guest {
+ keys: Key -> Time
+ }
+
+fun nextKey [k: Key, ks: set Key]: set Key {
+ min [k.nexts & ks]
+ }
+
+pred init [t: Time] {
+ no Guest.keys.t
+ no FrontDesk.occupant.t
+ all r: Room | FrontDesk.lastKey.t [r] = r.currentKey.t
+ }
+
+pred entry [t, t': Time, g: Guest, r: Room, k: Key] {
+ k in g.keys.t
+ let ck = r.currentKey |
+ (k = ck.t and ck.t' = ck.t) or
+ (k = nextKey[ck.t, r.keys] and ck.t' = k)
+ noRoomChangeExcept [t, t', r]
+ noGuestChangeExcept [t, t', none]
+ noFrontDeskChange [t, t']
+ }
+
+pred noFrontDeskChange [t, t': Time] {
+ FrontDesk.lastKey.t = FrontDesk.lastKey.t'
+ FrontDesk.occupant.t = FrontDesk.occupant.t'
+ }
+
+pred noRoomChangeExcept [t, t': Time, rs: set Room] {
+ all r: Room - rs | r.currentKey.t = r.currentKey.t'
+ }
+
+pred noGuestChangeExcept [t, t': Time, gs: set Guest] {
+ all g: Guest - gs | g.keys.t = g.keys.t'
+ }
+
+pred checkout [t, t': Time, g: Guest] {
+ let occ = FrontDesk.occupant {
+ some occ.t.g
+ occ.t' = occ.t - Room ->g
+ }
+ FrontDesk.lastKey.t = FrontDesk.lastKey.t'
+ noRoomChangeExcept [t, t', none]
+ noGuestChangeExcept [t, t', none]
+ }
+
+pred checkin [t, t': Time, g: Guest, r: Room, k: Key] {
+ g.keys.t' = g.keys.t + k
+ let occ = FrontDesk.occupant {
+ no occ.t [r]
+ occ.t' = occ.t + r -> g
+ }
+ let lk = FrontDesk.lastKey {
+ lk.t' = lk.t ++ r -> k
+ k = nextKey [lk.t [r], r.keys]
+ }
+ noRoomChangeExcept [t, t', none]
+ noGuestChangeExcept [t, t', g]
+ }
+
+fact traces {
+ init [first]
+ all t: Time-last | let t' = t.next |
+ some g: Guest, r: Room, k: Key |
+ entry [t, t', g, r, k]
+ or checkin [t, t', g, r, k]
+ or checkout [t, t', g]
+ }
+
+assert NoBadEntry {
+ all t: Time, r: Room, g: Guest, k: Key |
+ let t' = t.next, o = FrontDesk.occupant.t[r] |
+ entry [t, t', g, r, k] and some o => g in o
+ }
+
+// This generates a counterexample similar to Fig 6.6
+check NoBadEntry for 3 but 2 Room, 2 Guest, 5 Time
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel2.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel2.als
new file mode 100644
index 00000000..09378f2e
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel2.als
@@ -0,0 +1,110 @@
+module chapter6/hotel2 --- the final model in Fig 6.7
+
+open util/ordering[Time] as to
+open util/ordering[Key] as ko
+
+sig Key {}
+sig Time {}
+
+sig Room {
+ keys: set Key,
+ currentKey: keys one -> Time
+ }
+
+fact DisjointKeySets {
+ -- each key belongs to at most one room
+ Room<:keys in Room lone-> Key
+ }
+
+one sig FrontDesk {
+ lastKey: (Room -> lone Key) -> Time,
+ occupant: (Room -> Guest) -> Time
+ }
+
+sig Guest {
+ keys: Key -> Time
+ }
+
+fun nextKey [k: Key, ks: set Key]: set Key {
+ min [k.nexts & ks]
+ }
+
+pred init [t: Time] {
+ no Guest.keys.t
+ no FrontDesk.occupant.t
+ all r: Room | FrontDesk.lastKey.t [r] = r.currentKey.t
+ }
+
+pred entry [t, t': Time, g: Guest, r: Room, k: Key] {
+ k in g.keys.t
+ let ck = r.currentKey |
+ (k = ck.t and ck.t' = ck.t) or
+ (k = nextKey[ck.t, r.keys] and ck.t' = k)
+ noRoomChangeExcept [t, t', r]
+ noGuestChangeExcept [t, t', none]
+ noFrontDeskChange [t, t']
+ }
+
+pred noFrontDeskChange [t, t': Time] {
+ FrontDesk.lastKey.t = FrontDesk.lastKey.t'
+ FrontDesk.occupant.t = FrontDesk.occupant.t'
+ }
+
+pred noRoomChangeExcept [t, t': Time, rs: set Room] {
+ all r: Room - rs | r.currentKey.t = r.currentKey.t'
+ }
+
+pred noGuestChangeExcept [t, t': Time, gs: set Guest] {
+ all g: Guest - gs | g.keys.t = g.keys.t'
+ }
+
+pred checkout [t, t': Time, g: Guest] {
+ let occ = FrontDesk.occupant {
+ some occ.t.g
+ occ.t' = occ.t - Room ->g
+ }
+ FrontDesk.lastKey.t = FrontDesk.lastKey.t'
+ noRoomChangeExcept [t, t', none]
+ noGuestChangeExcept [t, t', none]
+ }
+
+pred checkin [t, t': Time, g: Guest, r: Room, k: Key] {
+ g.keys.t' = g.keys.t + k
+ let occ = FrontDesk.occupant {
+ no occ.t [r]
+ occ.t' = occ.t + r -> g
+ }
+ let lk = FrontDesk.lastKey {
+ lk.t' = lk.t ++ r -> k
+ k = nextKey [lk.t [r], r.keys]
+ }
+ noRoomChangeExcept [t, t', none]
+ noGuestChangeExcept [t, t', g]
+ }
+
+fact traces {
+ init [first]
+ all t: Time-last | let t' = t.next |
+ some g: Guest, r: Room, k: Key |
+ entry [t, t', g, r, k]
+ or checkin [t, t', g, r, k]
+ or checkout [t, t', g]
+ }
+
+fact NoIntervening {
+ all t: Time-last | let t' = t.next, t" = t'.next |
+ all g: Guest, r: Room, k: Key |
+ checkin [t, t', g, r, k] => (entry [t', t", g, r, k] or no t")
+ }
+
+assert NoBadEntry {
+ all t: Time, r: Room, g: Guest, k: Key |
+ let t' = t.next, o = FrontDesk.occupant.t[r] |
+ entry [t, t', g, r, k] and some o => g in o
+ }
+
+// After adding the NoIntervening fact,
+// these commands no longer generate counterexamples
+check NoBadEntry for 3 but 2 Room, 2 Guest, 5 Time
+check NoBadEntry for 3 but 3 Room, 3 Guest, 7 Time
+check NoBadEntry for 5 but 3 Room, 3 Guest, 9 Time
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel3.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel3.als
new file mode 100644
index 00000000..b3ad4b49
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel3.als
@@ -0,0 +1,92 @@
+module chapter6/hotel3 --- model in Fig 6.10 without the NonIntervening fact
+
+open util/ordering[Time] as to
+open util/ordering[Key] as ko
+
+sig Key, Time {}
+
+sig Room {
+ keys: set Key,
+ currentKey: keys one -> Time
+ }
+
+fact {
+ Room <: keys in Room lone -> Key
+ }
+
+one sig FrontDesk {
+ lastKey: (Room -> lone Key) -> Time,
+ occupant: (Room -> Guest) -> Time
+ }
+
+sig Guest {
+ keys: Key -> Time
+ }
+
+fun nextKey [k: Key, ks: set Key]: set Key {
+ min [k.nexts & ks]
+ }
+
+pred init [t: Time] {
+ no Guest.keys.t
+ no FrontDesk.occupant.t
+ all r: Room | FrontDesk.lastKey.t [r] = r.currentKey.t
+ }
+
+abstract sig Event {
+ pre, post: Time,
+ guest: Guest
+ }
+
+abstract sig RoomKeyEvent extends Event {
+ room: Room,
+ key: Key
+ }
+
+sig Entry extends RoomKeyEvent { } {
+ key in guest.keys.pre
+ let ck = room.currentKey |
+ (key = ck.pre and ck.post = ck.pre) or
+ (key = nextKey[ck.pre, room.keys] and ck.post = key)
+ currentKey.post = currentKey.pre ++ room->key
+ }
+
+sig Checkin extends RoomKeyEvent { } {
+ keys.post = keys.pre + guest -> key
+ let occ = FrontDesk.occupant {
+ no occ.pre [room]
+ occ.post = occ.pre + room -> guest
+ }
+ let lk = FrontDesk.lastKey {
+ lk.post = lk.pre ++ room -> key
+ key = nextKey [lk.pre [room], room.keys]
+ }
+ }
+
+sig Checkout extends Event { } {
+ let occ = FrontDesk.occupant {
+ some occ.pre.guest
+ occ.post = occ.pre - Room -> guest
+ }
+ }
+
+fact Traces {
+ init [first]
+ all t: Time-last |
+ let t' = t.next |
+ some e: Event {
+ e.pre = t and e.post = t'
+ currentKey.t != currentKey.t' => e in Entry
+ occupant.t != occupant.t' => e in Checkin + Checkout
+ (lastKey.t != lastKey.t' or keys.t != keys.t') => e in Checkin
+ }
+ }
+
+assert NoBadEntry {
+ all e: Entry |
+ let o=FrontDesk.occupant.(e.pre) [e.room] |
+ some o => e.guest in o
+ }
+
+// This generates a counterexample similar to Fig 6.13
+check NoBadEntry for 5 but 3 Room, 3 Guest, 5 Time, 4 Event
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel4.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel4.als
new file mode 100644
index 00000000..35f3566b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/hotel4.als
@@ -0,0 +1,103 @@
+module chapter6/hotel4 --- model in Fig 6.10 with the NonIntervening fact
+
+open util/ordering[Time] as to
+open util/ordering[Key] as ko
+
+sig Key, Time {}
+
+sig Room {
+ keys: set Key,
+ currentKey: keys one -> Time
+ }
+
+fact {
+ Room <: keys in Room lone -> Key
+ }
+
+one sig FrontDesk {
+ lastKey: (Room -> lone Key) -> Time,
+ occupant: (Room -> Guest) -> Time
+ }
+
+sig Guest {
+ keys: Key -> Time
+ }
+
+fun nextKey [k: Key, ks: set Key]: set Key {
+ min [k.nexts & ks]
+ }
+
+pred init [t: Time] {
+ no Guest.keys.t
+ no FrontDesk.occupant.t
+ all r: Room | FrontDesk.lastKey.t [r] = r.currentKey.t
+ }
+
+abstract sig Event {
+ pre, post: Time,
+ guest: Guest
+ }
+
+abstract sig RoomKeyEvent extends Event {
+ room: Room,
+ key: Key
+ }
+
+sig Entry extends RoomKeyEvent { } {
+ key in guest.keys.pre
+ let ck = room.currentKey |
+ (key = ck.pre and ck.post = ck.pre) or
+ (key = nextKey[ck.pre, room.keys] and ck.post = key)
+ currentKey.post = currentKey.pre ++ room->key
+ }
+
+sig Checkin extends RoomKeyEvent { } {
+ keys.post = keys.pre + guest -> key
+ let occ = FrontDesk.occupant {
+ no occ.pre [room]
+ occ.post = occ.pre + room -> guest
+ }
+ let lk = FrontDesk.lastKey {
+ lk.post = lk.pre ++ room -> key
+ key = nextKey [lk.pre [room], room.keys]
+ }
+ }
+
+sig Checkout extends Event { } {
+ let occ = FrontDesk.occupant {
+ some occ.pre.guest
+ occ.post = occ.pre - Room -> guest
+ }
+ }
+
+fact Traces {
+ init [first]
+ all t: Time-last |
+ let t' = t.next |
+ some e: Event {
+ e.pre = t and e.post = t'
+ currentKey.t != currentKey.t' => e in Entry
+ occupant.t != occupant.t' => e in Checkin + Checkout
+ (lastKey.t != lastKey.t' or keys.t != keys.t') => e in Checkin
+ }
+ }
+
+assert NoBadEntry {
+ all e: Entry |
+ let o=FrontDesk.occupant.(e.pre) [e.room] |
+ some o => e.guest in o
+ }
+
+fact NoIntervening {
+ all c: Checkin |
+ c.post = last
+ or some e: Entry {
+ e.pre = c.post
+ e.room = c.room
+ e.guest = c.guest
+ }
+ }
+
+// After adding the NoIntervening fact,
+// this command no longer generates a counterexample
+check NoBadEntry for 5 but 3 Room, 3 Guest, 9 Time, 8 Event
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/mediaAssets.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/mediaAssets.als
new file mode 100644
index 00000000..668b3130
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/mediaAssets.als
@@ -0,0 +1,117 @@
+module chapter6/mediaAssets
+
+sig ApplicationState {
+ catalogs: set Catalog,
+ catalogState: catalogs -> one CatalogState,
+ currentCatalog: catalogs,
+ buffer: set Asset
+ }
+
+sig Catalog, Asset {}
+
+sig CatalogState {
+ assets: set Asset,
+ disj hidden, showing: set assets,
+ selection: set assets + Undefined
+ } {
+ hidden+showing = assets
+ }
+
+one sig Undefined {}
+
+pred catalogInv [cs: CatalogState] {
+ cs.selection = Undefined or (some cs.selection and cs.selection in cs.showing)
+ }
+
+pred appInv [xs: ApplicationState] {
+ all cs: xs.catalogs | catalogInv [xs.catalogState[cs]]
+ }
+
+pred showSelected [cs, cs': CatalogState] {
+ cs.selection != Undefined
+ cs'.showing = cs.selection
+ cs'.selection = cs.selection
+ cs'.assets = cs.assets
+ }
+
+pred hideSelected [cs, cs': CatalogState] {
+ cs.selection != Undefined
+ cs'.hidden = cs.hidden + cs.selection
+ cs'.selection = Undefined
+ cs'.assets = cs.assets
+ }
+
+pred cut [xs, xs': ApplicationState] {
+ let cs = xs.currentCatalog.(xs.catalogState), sel = cs.selection {
+ sel != Undefined
+ xs'.buffer = sel
+ some cs': CatalogState {
+ cs'.assets = cs.assets - sel
+ cs'.showing = cs.showing - sel
+ cs'.selection = Undefined
+ xs'.catalogState = xs.catalogState ++ xs.currentCatalog -> cs'
+ }
+ }
+ xs'.catalogs = xs.catalogs
+ xs'.currentCatalog = xs.currentCatalog
+ }
+
+pred paste [xs, xs': ApplicationState] {
+ let cs = xs.currentCatalog.(xs.catalogState), buf = xs.buffer {
+ xs'.buffer = buf
+ some cs': CatalogState {
+ cs'.assets = cs.assets + buf
+ cs'.showing = cs.showing + (buf - cs.assets)
+ cs'.selection = buf - cs.assets
+ xs'.catalogState = xs.catalogState ++ xs.currentCatalog -> cs'
+ }
+ }
+ xs'.catalogs = xs.catalogs
+ xs'.currentCatalog = xs.currentCatalog
+ }
+
+assert HidePreservesInv {
+ all cs, cs': CatalogState |
+ catalogInv [cs] and hideSelected [cs, cs'] => catalogInv [cs']
+ }
+
+// This check should not find any counterexample
+check HidePreservesInv
+
+pred sameApplicationState [xs, xs': ApplicationState] {
+ xs'.catalogs = xs.catalogs
+ all c: xs.catalogs | sameCatalogState [c.(xs.catalogState), c.(xs'.catalogState)]
+ xs'.currentCatalog = xs.currentCatalog
+ xs'.buffer = xs.buffer
+ }
+
+pred sameCatalogState [cs, cs': CatalogState] {
+ cs'.assets = cs.assets
+ cs'.showing = cs.showing
+ cs'.selection = cs.selection
+ }
+
+assert CutPaste {
+ all xs, xs', xs": ApplicationState |
+ (appInv [xs] and cut [xs, xs'] and paste [xs', xs"]) => sameApplicationState [xs, xs"]
+ }
+
+// This check should find a counterexample
+check CutPaste
+
+assert PasteCut {
+ all xs, xs', xs": ApplicationState |
+ (appInv [xs] and paste [xs, xs'] and cut [xs', xs"]) => sameApplicationState [xs, xs"]
+ }
+
+// This check should find a counterexample
+check PasteCut
+
+assert PasteNotAffectHidden {
+ all xs, xs': ApplicationState |
+ (appInv [xs] and paste [xs, xs']) =>
+ let c = xs.currentCatalog | xs'.catalogState[c].hidden = xs.catalogState[c].hidden
+ }
+
+// This check should not find any counterexample
+check PasteNotAffectHidden
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/abstractMemory.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/abstractMemory.als
new file mode 100644
index 00000000..3d676d50
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/abstractMemory.als
@@ -0,0 +1,33 @@
+module chapter6/memory/abstractMemory [Addr, Data] ----- the model from page 217
+
+sig Memory {
+ data: Addr -> lone Data
+ }
+
+pred init [m: Memory] {
+ no m.data
+ }
+
+pred write [m, m': Memory, a: Addr, d: Data] {
+ m'.data = m.data ++ a -> d
+ }
+
+pred read [m: Memory, a: Addr, d: Data] {
+ let d' = m.data [a] | some d' implies d = d'
+ }
+
+fact Canonicalize {
+ no disj m, m': Memory | m.data = m'.data
+ }
+
+// This command should not find any counterexample
+WriteRead: check {
+ all m, m': Memory, a: Addr, d1, d2: Data |
+ write [m, m', a, d1] and read [m', a, d2] => d1 = d2
+ }
+
+// This command should not find any counterexample
+WriteIdempotent: check {
+ all m, m', m": Memory, a: Addr, d: Data |
+ write [m, m', a, d] and write [m', m", a, d] => m' = m"
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/cacheMemory.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/cacheMemory.als
new file mode 100644
index 00000000..c95fe22b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/cacheMemory.als
@@ -0,0 +1,43 @@
+module chapter6/memory/cacheMemory [Addr, Data] ----- the model from page 219
+
+sig CacheSystem {
+ main, cache: Addr -> lone Data
+ }
+
+pred init [c: CacheSystem] {
+ no c.main + c.cache
+ }
+
+pred write [c, c': CacheSystem, a: Addr, d: Data] {
+ c'.main = c.main
+ c'.cache = c.cache ++ a -> d
+ }
+
+pred read [c: CacheSystem, a: Addr, d: Data] {
+ some d
+ d = c.cache [a]
+ }
+
+pred load [c, c': CacheSystem] {
+ some addrs: set c.main.Data - c.cache.Data |
+ c'.cache = c.cache ++ addrs <: c.main
+ c'.main = c.main
+ }
+
+pred flush [c, c': CacheSystem] {
+ some addrs: some c.cache.Data {
+ c'.main = c.main ++ addrs <: c.cache
+ c'.cache = c.cache - addrs -> Data
+ }
+ }
+
+// This command should not find any counterexample
+LoadNotObservable: check {
+ all c, c', c": CacheSystem, a1, a2: Addr, d1, d2, d3: Data |
+ {
+ read [c, a2, d2]
+ write [c, c', a1, d1]
+ load [c', c"]
+ read [c", a2, d3]
+ } implies d3 = (a1=a2 => d1 else d2)
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/checkCache.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/checkCache.als
new file mode 100644
index 00000000..8214389b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/checkCache.als
@@ -0,0 +1,34 @@
+module chapter6/memory/checkCache [Addr, Data]
+
+open chapter6/memory/cacheMemory [Addr, Data] as cache
+open chapter6/memory/abstractMemory [Addr, Data] as amemory
+
+fun alpha [c: CacheSystem]: Memory {
+ {m: Memory | m.data = c.main ++ c.cache}
+ }
+
+// This check should not produce a counterexample
+ReadOK: check {
+ // introduction of m, m' ensures that they exist, and gives witnesses if counterexample
+ all c: CacheSystem, a: Addr, d: Data, m: Memory |
+ cache/read [c, a, d] and m = alpha [c] => amemory/read [m, a, d]
+ }
+
+// This check should not produce a counterexample
+WriteOK: check {
+ all c, c': CacheSystem, a: Addr, d: Data, m, m': Memory |
+ cache/write [c, c', a, d] and m = alpha [c] and m' = alpha [c']
+ => amemory/write [m, m', a, d]
+ }
+
+// This check should not produce a counterexample
+LoadOK: check {
+ all c, c': CacheSystem, m, m': Memory |
+ cache/load [c, c'] and m = alpha [c] and m' = alpha [c'] => m = m'
+ }
+
+// This check should not produce a counterexample
+FlushOK: check {
+ all c, c': CacheSystem, m, m': Memory |
+ cache/flush [c, c'] and m = alpha [c] and m' = alpha [c'] => m = m'
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/checkFixedSize.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/checkFixedSize.als
new file mode 100644
index 00000000..20d291d1
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/checkFixedSize.als
@@ -0,0 +1,28 @@
+module chapter6/memory/checkFixedSize [Addr, Data]
+
+open chapter6/memory/fixedSizeMemory_H [Addr, Data] as fmemory
+open chapter6/memory/abstractMemory [Addr, Data] as amemory
+
+// define abstraction function from history-extended concrete state to abstract state
+pred alpha [fm: fmemory/Memory_H, am: amemory/Memory] {
+ am.data = fm.data - (fm.unwritten -> Data)
+ }
+
+// This check should not find a counterexample
+initOk: check {
+ all fm: fmemory/Memory_H, am: amemory/Memory |
+ fmemory/init [fm] and alpha [fm, am] => amemory/init [am]
+ }
+
+// This check should not find a counterexample
+readOk: check {
+ all fm: fmemory/Memory_H, a: Addr, d: Data, am: amemory/Memory |
+ fmemory/read [fm, a, d] and alpha [fm, am] => amemory/read [am, a, d]
+ }
+
+// This check should not find a counterexample
+writeOk: check {
+ all fm, fm': fmemory/Memory_H, a: Addr, d: Data, am, am': amemory/Memory |
+ fmemory/write [fm, fm', a, d] and alpha [fm, am] and alpha [fm', am']
+ implies amemory/write [am, am', a, d]
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/fixedSizeMemory.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/fixedSizeMemory.als
new file mode 100644
index 00000000..df6ab2a9
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/fixedSizeMemory.als
@@ -0,0 +1,21 @@
+module chapter6/memory/fixedSizeMemory [Addr, Data]
+
+sig Memory {
+ data: Addr -> one Data
+ }
+
+pred init [m: Memory] {
+ // This predicate is empty in order to allow non-deterministic initialization
+ }
+
+pred write [m, m': Memory, a: Addr, d: Data] {
+ m'.data = m.data ++ a -> d
+ }
+
+pred read [m: Memory, a: Addr, d: Data] {
+ d = m.data [a]
+ }
+
+fact Canonicalize {
+ no disj m, m': Memory | m.data = m'.data
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/fixedSizeMemory_H.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/fixedSizeMemory_H.als
new file mode 100644
index 00000000..15785991
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/memory/fixedSizeMemory_H.als
@@ -0,0 +1,21 @@
+module chapter6/memory/fixedSizeMemory_H [Addr, Data]
+
+open chapter6/memory/fixedSizeMemory [Addr, Data] as memory
+
+sig Memory_H extends memory/Memory {
+ unwritten: set Addr
+ }
+
+pred init [m: Memory_H] {
+ memory/init [m]
+ m.unwritten = Addr
+ }
+
+pred read [m: Memory_H, a: Addr, d: Data] {
+ memory/read [m, a, d]
+ }
+
+pred write [m, m': Memory_H, a: Addr, d: Data] {
+ memory/write [m, m', a, d]
+ m'.unwritten = m.unwritten - a
+ }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection.thm
new file mode 100644
index 00000000..0d172180
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection.thm
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection1.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection1.als
new file mode 100644
index 00000000..043b6a07
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection1.als
@@ -0,0 +1,57 @@
+module chapter6/ringElection1 --- the version up to the top of page 181
+
+open util/ordering[Time] as TO
+open util/ordering[Process] as PO
+
+sig Time {}
+
+sig Process {
+ succ: Process,
+ toSend: Process -> Time,
+ elected: set Time
+ }
+
+fact ring {
+ all p: Process | Process in p.^succ
+ }
+
+pred init [t: Time] {
+ all p: Process | p.toSend.t = p
+ }
+
+pred step [t, t': Time, p: Process] {
+ let from = p.toSend, to = p.succ.toSend |
+ some id: from.t {
+ from.t' = from.t - id
+ to.t' = to.t + (id - p.succ.prevs)
+ }
+ }
+
+fact defineElected {
+ no elected.first
+ all t: Time-first | elected.t = {p: Process | p in p.toSend.t - p.toSend.(t.prev)}
+ }
+
+fact traces {
+ init [first]
+ all t: Time-last |
+ let t' = t.next |
+ all p: Process |
+ step [t, t', p] or step [t, t', succ.p] or skip [t, t', p]
+ }
+
+pred skip [t, t': Time, p: Process] {
+ p.toSend.t = p.toSend.t'
+ }
+
+pred show { some elected }
+run show for 3 Process, 4 Time
+// This generates an instance similar to Fig 6.4
+
+assert AtMostOneElected { lone elected.Time }
+check AtMostOneElected for 3 Process, 7 Time
+// This should not find any counterexample
+
+assert AtLeastOneElected { some t: Time | some elected.t }
+check AtLeastOneElected for 3 Process, 7 Time
+// This generates a counterexample in which nothing happens
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection2.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection2.als
new file mode 100644
index 00000000..16bee928
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/book/chapter6/ringElection2.als
@@ -0,0 +1,74 @@
+module chapter6/ringElection2 --- the final version (as depicted in Fig 6.1)
+
+open util/ordering[Time] as TO
+open util/ordering[Process] as PO
+
+sig Time {}
+
+sig Process {
+ succ: Process,
+ toSend: Process -> Time,
+ elected: set Time
+ }
+
+fact ring {
+ all p: Process | Process in p.^succ
+ }
+
+pred init [t: Time] {
+ all p: Process | p.toSend.t = p
+ }
+
+pred step [t, t': Time, p: Process] {
+ let from = p.toSend, to = p.succ.toSend |
+ some id: from.t {
+ from.t' = from.t - id
+ to.t' = to.t + (id - p.succ.prevs)
+ }
+ }
+
+fact defineElected {
+ no elected.first
+ all t: Time-first | elected.t = {p: Process | p in p.toSend.t - p.toSend.(t.prev)}
+ }
+
+fact traces {
+ init [first]
+ all t: Time-last |
+ let t' = t.next |
+ all p: Process |
+ step [t, t', p] or step [t, t', succ.p] or skip [t, t', p]
+ }
+
+pred skip [t, t': Time, p: Process] {
+ p.toSend.t = p.toSend.t'
+ }
+
+pred show { some elected }
+run show for 3 Process, 4 Time
+// This generates an instance similar to Fig 6.4
+
+assert AtMostOneElected { lone elected.Time }
+check AtMostOneElected for 3 Process, 7 Time
+// This should not find any counterexample
+
+pred progress {
+ all t: Time - TO/last |
+ let t' = TO/next [t] |
+ some Process.toSend.t => some p: Process | not skip [t, t', p]
+ }
+
+assert AtLeastOneElected { progress => some elected.Time }
+check AtLeastOneElected for 3 Process, 7 Time
+// This should not find any counterexample
+
+pred looplessPath { no disj t, t': Time | toSend.t = toSend.t' }
+
+// This produces an instance
+run looplessPath for 3 Process, 12 Time
+
+// This does not produce an instance
+run looplessPath for 3 Process, 13 Time
+
+// Therefore, we can conclude that a scope of 12 for Time is
+// sufficient to reach all states of the protocol for a three-node ring.
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/dijkstra.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/dijkstra.als
new file mode 100644
index 00000000..ab342811
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/dijkstra.als
@@ -0,0 +1,120 @@
+module examples/algorithms/dijkstra
+
+/*
+ * Models how mutexes are grabbed and released by processes, and
+ * how Dijkstra's mutex ordering criterion can prevent deadlocks.
+ *
+ * For a detailed description, see:
+ * E. W. Dijkstra, Cooperating sequential processes. Technological
+ * University, Eindhoven, The Netherlands, September 1965.
+ * Reprinted in Programming Languages, F. Genuys, Ed., Academic
+ * Press, New York, 1968, 43-112.
+ *
+ * Acknowledgements to Ulrich Geilmann for finding and fixing a bug
+ * in the GrabMutex predicate.
+ *
+ */
+
+open util/ordering [State] as so
+open util/ordering [Mutex] as mo
+
+sig Process {}
+sig Mutex {}
+
+sig State { holds, waits: Process -> Mutex }
+
+
+pred Initial [s: State] { no s.holds + s.waits }
+
+pred IsFree [s: State, m: Mutex] {
+ // no process holds this mutex
+ no m.~(s.holds)
+ // all p: Process | m !in p.(this.holds)
+}
+
+pred IsStalled [s: State, p: Process] { some p.(s.waits) }
+
+pred GrabMutex [s: State, p: Process, m: Mutex, s': State] {
+ // a process can only act if it is not
+ // waiting for a mutex
+ !s.IsStalled[p]
+ // can only grab a mutex we do not yet hold
+ m !in p.(s.holds)
+ // mutexes are grabbed in order
+ all m': p.(s.holds) | mo/lt[m',m]
+ s.IsFree[m] => {
+ // if the mutex is free, we now hold it,
+ // and do not become stalled
+ p.(s'.holds) = p.(s.holds) + m
+ no p.(s'.waits)
+ } else {
+ // if the mutex was not free,
+ // we still hold the same mutexes we held,
+ // and are now waiting on the mutex
+ // that we tried to grab.
+ p.(s'.holds) = p.(s.holds)
+ p.(s'.waits) = m
+ }
+ all otherProc: Process - p | {
+ otherProc.(s'.holds) = otherProc.(s.holds)
+ otherProc.(s'.waits) = otherProc.(s.waits)
+ }
+}
+
+pred ReleaseMutex [s: State, p: Process, m: Mutex, s': State] {
+ !s.IsStalled[p]
+ m in p.(s.holds)
+ p.(s'.holds) = p.(s.holds) - m
+ no p.(s'.waits)
+ no m.~(s.waits) => {
+ no m.~(s'.holds)
+ no m.~(s'.waits)
+ } else {
+ some lucky: m.~(s.waits) | {
+ m.~(s'.waits) = m.~(s.waits) - lucky
+ m.~(s'.holds) = lucky
+ }
+ }
+ all mu: Mutex - m {
+ mu.~(s'.waits) = mu.~(s.waits)
+ mu.~(s'.holds)= mu.~(s.holds)
+ }
+}
+
+/**
+ * for every adjacent (pre,post) pair of States,
+ * one action happens: either some process grabs a mutex,
+ * or some process releases a mutex,
+ * or nothing happens (have to allow this to show deadlocks)
+ */
+pred GrabOrRelease {
+ Initial[so/first] &&
+ (
+ all pre: State - so/last | let post = so/next [pre] |
+ (post.holds = pre.holds && post.waits = pre.waits)
+ ||
+ (some p: Process, m: Mutex | pre.GrabMutex [p, m, post])
+ ||
+ (some p: Process, m: Mutex | pre.ReleaseMutex [p, m, post])
+
+ )
+}
+
+pred Deadlock {
+ some Process
+ some s: State | all p: Process | some p.(s.waits)
+}
+
+assert DijkstraPreventsDeadlocks {
+ GrabOrRelease => ! Deadlock
+}
+
+
+pred ShowDijkstra {
+ GrabOrRelease && Deadlock
+ some waits
+}
+
+run Deadlock for 3 expect 1
+run ShowDijkstra for 5 State, 2 Process, 2 Mutex expect 1
+check DijkstraPreventsDeadlocks for 5 State, 5 Process, 4 Mutex expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/dijkstra.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/dijkstra.thm
new file mode 100644
index 00000000..3be2d6c2
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/dijkstra.thm
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/messaging.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/messaging.als
new file mode 100644
index 00000000..0815fb44
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/messaging.als
@@ -0,0 +1,228 @@
+module examples/algorithms/messaging
+
+/*
+ * Generic messaging among several nodes
+ *
+ * By default, messages can be lost (i.e. never become visible to the
+ * recipient node) or may be arbitrarily delayed. Also, by default
+ * out-of-order delivery is allowed.
+ */
+
+open util/ordering[Tick] as ord
+open util/relation as rel
+
+sig Node {}
+
+sig MsgState {
+ /** Node that sent the message */
+ from: Node,
+
+ /** Intended recipient(s) of a message; note that broadcasts are allowed */
+ to: set Node
+}
+
+sig Msg {
+ state: MsgState,
+
+ /** Timestamp: the tick on which the message was sent */
+ sentOn: Tick,
+
+ /** tick at which node reads message, if read */
+ readOn: Node -> lone Tick
+}{
+ readOn.Tick in state.to
+}
+
+sig Tick {
+ /** The state of each node */
+ state: Node -> one NodeState,
+
+ /**
+ * Definition of what each node does on this tick:
+ *
+ * Typically, a node would determine
+ * the messages it sends and its next state, based on its current
+ * state and the messages it reads.
+ *
+ * Messages that the node _can_ read in this tick, i.e. messages available
+ * for reading at the beginning of this tick. The messages that
+ * the node actually reads are a subset of this set. Determined by
+ * constraints in this module.
+ */
+ visible: Node -> Msg,
+
+ /**
+ * Messages that the node _actually reads_ in this tick. Must be a subset
+ * of visible. Determined by constraints of the particular system
+ * that uses this module.
+ */
+ read: Node -> Msg,
+
+ /**
+ * Messages sent by the node in this tick. They become visible to
+ * (and can be read by) their recipients on the next tick.
+ */
+ sent: Node -> Msg,
+
+ /**
+ * Messages available for sending at this tick. A given message
+ * atom is only used once, and then it gets removed from the available
+ * set and cannot be used to represent messages sent on subsequent ticks.
+ * Also, two different nodes cannot send the same message atom.
+ * So, a message atom represents a particular single physical message
+ * sent by a given node on a given tick.
+ */
+ available: set Msg,
+
+ /**
+ * For each node, at each tick, the number of messages it _needs_ to send.
+ * Used to rule out "proofs" of liveness violations that are caused
+ * solely by not having enough messages available for sending.
+ */
+ needsToSend: Node -> Msg
+}
+
+fun MsgsSentOnTick[t: Tick]: set Msg { t.sent[Node] }
+fun MsgsVisibleOnTick[t: Tick]: set Msg { t.visible[Node] }
+fun MsgsReadOnTick[t: Tick]: set Msg { t.read[Node] }
+
+fact MsgMovementConstraints {
+ // At the beginning, no messages have been sent yet
+ no ord/first.visible[Node]
+
+ // Messages sent on a given tick become visible to recipient(s)
+ // on the subsequent tick.
+ all pre: Tick - ord/last |
+ let post = ord/next[pre] | {
+ // messages sent on this tick are no longer available on subsequent tick
+ post.available = pre.available - MsgsSentOnTick[pre]
+ }
+
+ all t: Tick | {
+ // Messages sent on a tick are taken from the pool of available
+ // (not-yet-sent) message atoms
+ MsgsSentOnTick[t] in t.available
+
+ // Timestamps are correct
+ MsgsSentOnTick[t].sentOn in t
+ MsgsReadOnTick[t].readOn[Node] in t
+
+ // The only new message atoms are those sent by nodes
+ MsgsSentOnTick[t] = t.sent[Node]
+
+ all n: Node, m: Msg |
+ m.readOn[n] = t => m in t.read[n]
+ // Return addresses are correct
+ all n: Node | t.sent[n].state.from in n
+
+ // messages sent to a node on a tick become visible to that node on some subseqent tick,
+ // and permanently stop being visible to that node on the tick after that node reads the message
+ all n: Node, m: Msg | {
+ // message starts being visible to node n no earlier than it is sent;
+ // only messages sent to this node are visible to this node.
+ (m in t.visible[n] => (n in m.state.to && m.sentOn in ord/prevs[t]))
+ // message permanently stops being visible immediately after being read
+ (m in t.read[n] => m !in ord/nexts[t].visible[n])
+ }
+ }
+}
+
+sig NodeState {
+}
+
+fun MsgsLiveOnTick[t: Tick]: set Msg {
+ Msg - { future: Msg | future.sentOn in ord/nexts[t] }
+ - { past: Msg | all n: past.state.to | past.readOn[n] in ord/prevs[t] }
+}
+
+pred TicksEquivalent[t1, t2: Tick] {
+ t1.state = t2.state
+ some r: (MsgsLiveOnTick[t1] - MsgsVisibleOnTick[t1]) one -> one (MsgsLiveOnTick[t2] - MsgsVisibleOnTick[t2]) |
+ all m1: dom[r] | let m2 = m1.r | {
+ m1.(Msg<:state) = m2.state
+ }
+ some r: MsgsVisibleOnTick[t1] one -> one MsgsVisibleOnTick[t2] |
+ all m1: dom[r] | let m2 = m1.r | {
+ m1.(Msg<:state) = m2.state
+ }
+}
+
+pred Loop {
+ some t: Tick - ord/last | TicksEquivalent[t, ord/last]
+}
+
+fact CleanupViz {
+ // cleans up visualization without precluding any interesting traces
+
+ // all messages must be sent
+ Msg in Tick.sent[Node]
+}
+
+pred ReadInOrder {
+ //
+ // This function ensures that messages are read in order.
+ //
+
+ // for all pairs of nodes
+ all n1, n2: Node |
+ // for all pairs of messages sent from n1 to n2
+ all m1, m2: Msg |
+ {
+ m1.state.from = n1
+ m2.state.from = n1
+ m1.state.to = n2
+ m2.state.to = n2
+ } => {
+ // if both m1 and m2 are read by n2, *and*
+ // n2 reads m1 before m2, then m1 must have
+ // been sent before m2
+ (some m1.readOn[n2] && some m2.readOn[n2] &&
+ m1.readOn[n2] in ord/prevs[m2.readOn[n2]]) =>
+ ord/lte[m1.sentOn, m2.sentOn]
+ }
+}
+
+fact ReadOnlyVisible { read in visible }
+
+/**
+ * this function ensures that messages will not
+ * be lost, i.e. a message send to a node will
+ * eventually be visible to that node
+ */
+pred NoLostMessages {
+ all m: Msg |
+ (m.sentOn != ord/last) => (all n: m.state.to |
+ some t: ord/nexts[m.sentOn] |
+ m in t.visible[n])
+}
+
+/**
+ * this function ensures that there will be
+ * no shortage of messages in the available
+ * message pool during the trace
+ */
+pred NoMessageShortage {
+ all t: Tick - ord/last |
+ (sum n: Node | # t.needsToSend[n]) =< # t.available
+}
+
+pred SomeState {
+ # Node > 1
+ //# Tick$read > 1
+}
+
+pred OutOfOrder {
+ ! ReadInOrder
+ # Msg = 2
+}
+
+run SomeState for 2 expect 1
+run OutOfOrder for 4 expect 1
+
+
+
+// DEFINED VARIABLES
+// Defined variables are uncalled, no-argument functions.
+// They are helpful for getting good visualization.
+fun FROM: Msg -> Node {{m: Msg, n: Node | n in m.state.from}}
+fun TO: Msg -> Node {{m: Msg, n: Node | n in m.state.to}}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/messaging.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/messaging.thm
new file mode 100644
index 00000000..53f28d8d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/messaging.thm
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/opt_spantree.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/opt_spantree.als
new file mode 100644
index 00000000..7c129325
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/opt_spantree.als
@@ -0,0 +1,220 @@
+module examples/algorithms/opt_spantree
+
+/*
+ * Direct specification of a distributed spanning tree algorithm
+ * over arbitrary network topologies
+ *
+ * Each process has a parent and a level, both of which are
+ * initally null. A distinct root node exists at which the
+ * algorithm starts. In the first step, the root assigns itself
+ * the level of zero and sends its level to its neighbors.
+ * Subsequently, if a node reads a message with level k, it sets
+ * its level to k+1, records the sender of the message as its
+ * parent, and sends the level k+1 to its neighbors. Once a node
+ * has set its level and parent, it ignores subsequent messages.
+ * Eventually, the parent pointers will form a spanning tree,
+ * rooted at Root.
+ *
+ * We model communication through a state-reading model, in which
+ * nodes can directly read the state of their neighbors. Messages
+ * are not explicity modelled. This makes no difference for this
+ * algorithm since once a node sends a message, the state of the
+ * node stays the same as the contents of the message.
+ */
+
+open util/ordering[Lvl] as lo
+open util/ordering[State] as so
+open util/graph[Process] as graph
+
+sig Process {
+ adj : set Process
+}
+
+one sig Root extends Process {}
+
+/**
+ * intuitively, the depth level at which
+ * a process resides in the spanning tree,
+ * with the root at level zero
+ */
+sig Lvl {}
+
+fact processGraph {
+ graph/noSelfLoops[adj] // for viz
+ graph/undirected[adj] // adjacency is symmetric
+ Process in Root.*adj // everything reachable from root
+}
+
+sig State {
+ /**
+ * the set of processes which execute in this state.
+ * used to allow flexibility in how many processes
+ * run simultaneously
+ */
+ runs : set Process,
+
+ /**
+ * the level of a process in this state
+ */
+ lvl: Process -> lone Lvl,
+
+ /**
+ * who the process thinks is its parent in this state.
+ * the parent pointers should eventually become
+ * the spanning tree
+ */
+ parent: Process -> lone Process
+}
+
+/**
+ * initially, the lvl and parent fields are blank
+ */
+pred Init {
+ let fs = so/first | {
+ no fs.lvl
+ no fs.parent
+ }
+}
+
+/**
+ * simple NOP transition
+ */
+pred TRNop[p : Process, pre, post: State] {
+ pre.lvl[p] = post.lvl[p]
+ pre.parent[p] = post.parent[p]
+}
+
+/**
+ * preconditions for a process to actually act
+ * in a certain pre-state
+ * used to preclude stalling of entire system
+ * for no reason (see TransIfPossible)
+ */
+pred TRActPreConds[p : Process, pre : State] {
+ // can't already have a level
+ no pre.lvl[p]
+ // must have a neighbor with a set level so
+ // p can read it
+ // Root doesn't need to read value from a
+ // neighbor
+ (p = Root || some pre.lvl[p.adj])
+}
+
+/**
+ * transition which changes state of a process
+ */
+pred TRAct[p : Process, pre, post : State] {
+ // can't already have a level
+ no pre.lvl[p]
+ (p = Root) => {
+ // the root sets its level to
+ // 0, and has no parent pointer
+ post.lvl[p] = lo/first
+ no post.parent[p]
+ } else {
+ // choose some adjacent process
+ // whose level is already set
+ some adjProc: p.adj |
+ let nLvl = pre.lvl[adjProc] | {
+ some nLvl
+ // p's parent is the adjacent
+ // process, and p's level is one greater than
+ // the level of the adjacent process (since
+ // its one level deeper)
+ post.lvl[p] = lo/next[nLvl]
+ post.parent[p] = adjProc
+ }
+ }
+}
+
+pred Trans[p : Process, pre, post : State] {
+ TRAct[p, pre, post] ||
+ TRNop[p, pre, post]
+}
+
+/**
+ * all processes do a nop transition in some
+ * state only when no process can act because
+ * preconditions are not met
+ */
+fact TransIfPossible {
+ all s : State - so/last |
+ (all p : Process | TRNop[p, s, so/next[s]]) =>
+ (all p : Process | !TRActPreConds[p,s])
+}
+
+fact LegalTrans {
+ Init
+ all s : State - so/last |
+ let s' = so/next[s] | {
+ all p : Process |
+ p in s.runs => Trans[p, s, s'] else TRNop[p,s,s']
+ }
+}
+
+pred PossTrans[s, s' : State] {
+ all p : Process | Trans[p,s,s']
+}
+
+pred SpanTreeAtState[s : State] {
+ // all processes reachable through inverted parent pointers
+ // from root (spanning)
+ Process in Root.*~(s.parent)
+ // parent relation is a tree (DAG)
+ // we only need to check the DAG condition since there can
+ // be at most one parent for a process (constrained by
+ // multiplicity)
+ graph/dag[~(s.parent)]
+}
+
+/**
+ * show a run that produces a spanning tree
+ */
+pred SuccessfulRun {
+ SpanTreeAtState[so/last]
+ all s : State - so/last | !SpanTreeAtState[s]
+}
+
+/**
+ * show a trace without a loop
+ */
+pred TraceWithoutLoop {
+ all s, s' : State | s!=s' => {
+ !EquivStates[s, s']
+ (s' in so/nexts[s] && (s' != so/next[s])) => !PossTrans[s,s']
+ }
+ all s: State | !SpanTreeAtState[s]
+}
+
+/**
+ * defines equivalent states
+ */
+pred EquivStates[s, s' : State] {
+ s.lvl = s'.lvl
+ s.parent = s'.parent
+}
+
+/**
+ * show a trace that violates liveness
+ */
+pred BadLivenessTrace {
+ // two different states equivalent (loop)
+ some s, s' : State | s!=s' && EquivStates[s, s']
+ all s : State | !SpanTreeAtState[s]
+}
+
+/**
+ * check that once spanning tree is constructed,
+ * it remains
+ */
+assert Closure {
+ all s : State - so/last |
+ SpanTreeAtState[s] => (s.parent = so/next[s].parent)
+}
+
+// note that for the worst case topology and choice of root,
+// the scope of Lvl must equal the scope of Process
+run SuccessfulRun for 4 State, exactly 5 Process, 3 Lvl expect 1
+// run TraceWithoutLoop for 8 but 9 State expect 1
+run BadLivenessTrace for 5 but 7 State expect 0
+check Closure for 5 but 7 State expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/opt_spantree.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/opt_spantree.thm
new file mode 100644
index 00000000..1254faed
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/opt_spantree.thm
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/peterson.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/peterson.als
new file mode 100644
index 00000000..6e6d59e5
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/peterson.als
@@ -0,0 +1,206 @@
+module examples/algorithms/peterson
+
+/*
+ * Model of Peterson's algorithm for mutual exclusion of n
+ * processes. The names kept similar to Murphi specification
+ * to make correspondence clear.
+ */
+
+open util/ordering[priority] as po
+open util/ordering[State] as so
+
+sig pid {
+}
+
+sig priority {
+}
+
+fact {
+ # priority = # pid + 1
+}
+
+abstract sig label_t {}
+
+// here subtyping would help
+one sig L0, L1, L2, L3, L4 extends label_t {}
+
+sig State {
+ P: pid -> label_t,
+ Q: pid -> priority,
+ turn: priority -> pid,
+ localj: pid -> priority
+}
+
+pred NOPTrans[i: pid, pre, post : State] {
+ post.P[i] = pre.P[i]
+ post.Q[i] = pre.Q[i]
+ post.localj[i] = pre.localj[i]
+}
+
+pred L0TransPre[i : pid, pre : State] {
+ // precondition
+ pre.P[i] = L0
+}
+
+pred L0Trans[i: pid, pre, post : State] {
+ L0TransPre[i, pre]
+ // localj[i] := 1
+ post.localj[i] = po/next[po/first]
+ post.P[i] = L1
+ post.Q[i] = pre.Q[i]
+ // something for turn?
+ post.turn = pre.turn
+}
+
+pred L1TransPre[i : pid, pre : State] {
+ // precondition
+ pre.P[i] = L1
+}
+
+pred L1Trans[i : pid, pre, post : State] {
+ L1TransPre[i, pre]
+ post.localj[i] = pre.localj[i]
+ post.Q[i] = pre.localj[i]
+ post.P[i] = L2
+ // something for turn?
+ post.turn = pre.turn
+}
+
+pred L2TransPre[i : pid, pre : State] {
+ // precondition
+ pre.P[i] = L2
+}
+
+pred L2Trans[i : pid, pre, post : State] {
+ L2TransPre[i, pre]
+ post.localj[i] = pre.localj[i]
+ post.Q[i] = pre.Q[i]
+ post.P[i] = L3
+ post.turn[post.localj[i]] = i
+ all j : priority - post.localj[i] |
+ post.turn[j] = pre.turn[j]
+}
+
+pred L3TransPre[i : pid, pre : State] {
+ // precondition
+ pre.P[i] = L3
+
+ all k : pid - i |
+ po/lt[pre.Q[k], pre.localj[i]] ||
+ pre.turn[pre.localj[i]] != i
+}
+
+pred L3Trans[i : pid, pre, post : State] {
+ L3TransPre[i, pre]
+ post.localj[i] = po/next[pre.localj[i]]
+ po/lt[post.localj[i], po/last] =>
+ post.P[i] = L1
+ else
+ post.P[i] = L4
+ post.Q[i] = pre.Q[i]
+ post.turn = pre.turn
+}
+
+pred L4TransPre[i : pid, pre : State] {
+ // precondition
+ pre.P[i] = L4
+}
+
+pred L4Trans[i : pid, pre, post : State] {
+ L4TransPre[i, pre]
+
+ post.P[i] = L0
+ post.Q[i] = po/first
+ post.localj[i] = pre.localj[i]
+ post.turn = pre.turn
+}
+
+pred RealTrans[i : pid, pre, post : State] {
+ L0Trans[i,pre,post] ||
+ L1Trans[i,pre,post] ||
+ L2Trans[i,pre,post] ||
+ L3Trans[i,pre,post] ||
+ L4Trans[i,pre,post]
+}
+
+pred SomePre[i : pid, pre : State] {
+ L0TransPre[i, pre] ||
+ L1TransPre[i, pre] ||
+ L2TransPre[i, pre] ||
+ L3TransPre[i, pre] ||
+ L4TransPre[i, pre]
+}
+
+fact Init {
+ let firstState = so/first | {
+ all i : pid | {
+ firstState.P[i] = L0
+ firstState.Q[i] = po/first
+ }
+ no firstState.turn
+ no firstState.localj
+ }
+}
+
+fact LegalTrans {
+ all pre : State - so/last |
+ let post = so/next[pre] | {
+ /*some i : pid | {
+ // HACK:
+ // need to specify that if some node
+ // can make a non-NOP transition, it
+ // does, but i can't figure out how
+ // right now
+ Trans(i,pre,post) && !NOPTrans(i,pre,post)
+ all j : pid - i |
+ NOPTrans(j,pre,post)
+ }*/
+ all i : pid |
+ RealTrans[i,pre,post] || NOPTrans[i,pre,post]
+ (all i : pid | NOPTrans[i,pre,post]) => {
+ all i : pid | !SomePre[i,pre]
+ post.turn = pre.turn
+ }
+ }
+}
+
+assert Safety {
+ all i1, i2 : pid, s : State | i1!=i2 => not (s.P[i1] = L4 && s.P[i2] = L4)
+}
+
+assert NotStuck {
+ all pre : State - so/last |
+ let post = so/next[pre] |
+ some i : pid |
+ RealTrans[i, pre, post] && !NOPTrans[i,pre,post]
+}
+
+pred TwoRun {
+ some s1, s2: State, i1, i2: pid | {
+ s1!=s2
+ i1!=i2
+ s1.P[i1] = L4
+ s2.P[i2] = L4
+ }
+}
+
+pred ThreeRun {
+ some disj s1, s2, s3: State, disj i1, i2, i3: pid | {
+ s1.P[i1] = L4
+ s2.P[i2] = L4
+ s3.P[i3] = L4
+ }
+}
+
+// 2 pids requires 8 states
+// 3 pids requires 16 states
+run TwoRun for 13 but 3 pid, 4 priority, 5 label_t expect 1
+
+// haven't run this one successfully yet
+run ThreeRun for 19 but 3 pid,4 priority,5 label_t expect 1
+
+// how many states do we need for this to match murphi?
+check Safety for 10 but 2 pid, 3 priority, 5 label_t expect 0
+
+// this assertion is trivial because of the hack described above
+check NotStuck for 10 but 2 pid, 3 priority, 5 label_t expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/ringlead.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/ringlead.als
new file mode 100644
index 00000000..b5a786a8
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/ringlead.als
@@ -0,0 +1,150 @@
+module examples/algorithms/ringlead
+
+/*
+ * Model of leader election on a ring
+ *
+ * Each process has a unique ID, IDs are ordered.
+ * The algorithm elects the process with the highest
+ * ID the leader, as follows. First, each process
+ * sends its own ID to its right neighbor.
+ * Then, whenever a process receives an ID, if the
+ * ID is greater than the process' own ID it forwards
+ * the ID to its right neighbor, otherwise does nothing.
+ * When a process receives its own ID that process
+ * is the leader.
+ */
+
+open util/boolean as bool
+open examples/algorithms/messaging as msg
+open util/ordering[msg/Node] as nodeOrd
+open util/ordering[msg/Tick] as tickOrd
+
+sig RingLeadNode extends msg/Node {
+ rightNeighbor: msg/Node
+}
+
+fact DefineRing {
+ (one msg/Node || (no n: msg/Node | n = n.rightNeighbor))
+ all n: msg/Node | msg/Node in n.^rightNeighbor
+}
+
+sig RingLeadMsgState extends msg/MsgState {
+ id: msg/Node
+}
+
+sig MsgViz extends msg/Msg {
+ vFrom: msg/Node,
+ vTo: set msg/Node,
+ vId: msg/Node
+}
+
+fact {
+ MsgViz = msg/Msg
+ vFrom = state.from
+ vTo = state.to
+ vId = state.id
+}
+
+
+sig RingLeadNodeState extends msg/NodeState {
+ leader: Bool
+}
+
+
+pred RingLeadFirstTrans [self: msg/Node, pre, post: msg/NodeState,
+ sees, reads, sends, needsToSend: set msg/Msg] {
+ one sends
+ # needsToSend = 1
+ sends.state.to = self.rightNeighbor
+ sends.state.id = self
+ post.leader = False
+}
+
+fact InitRingLeadState {
+ all n: msg/Node |
+ tickOrd/first.state[n].leader = False
+}
+
+pred RingLeadRestTrans [self: msg/Node, pre, post: msg/NodeState,
+ sees, reads, sends, needsToSend: set msg/Msg] {
+ RingLeadTransHelper[self, sees, reads, sends, needsToSend]
+ post.leader = True iff (pre.leader = True ||
+ self in reads.state.id)
+}
+
+/**
+ * we take any messages whose node ids are higher than ours,
+ * and we forward them to the right neighbor. we drop
+ * all other messages. if we get a message with our own
+ * id, we're the leader.
+ */
+pred RingLeadTransHelper[self: msg/Node, sees, reads, sends, needsToSend: set msg/Msg] {
+ reads = sees
+
+ all received: reads |
+ (received.state.id in nodeOrd/nexts[self]) =>
+ (one weSend: sends | (weSend.state.id = received.state.id && weSend.state.to = self.rightNeighbor))
+
+ all weSend: sends | {
+ let mID = weSend.state.id | {
+ mID in nodeOrd/nexts[self]
+ mID in reads.state.id
+ weSend.state.to = self.rightNeighbor
+ }
+ //weSend.sentBecauseOf = { received : reads | received.id = weSend.id }
+ //all otherWeSend: sends - weSend | otherWeSend.id != weSend.id
+ }
+
+ # needsToSend = # { m: reads | m.state.id in nodeOrd/nexts[self] }
+}
+fact RingLeadTransitions {
+ all n: msg/Node {
+ all t: msg/Tick - tickOrd/last | {
+ t = tickOrd/first =>
+ RingLeadFirstTrans[n, t.state[n], tickOrd/next[t].state[n], t.visible[n], t.read[n], t.sent[n], t.needsToSend[n]]
+ else
+ RingLeadRestTrans[n, t.state[n], tickOrd/next[t].state[n], t.visible[n], t.read[n], t.sent[n], t.needsToSend[n]]
+ }
+ // also constrain last tick
+ RingLeadTransHelper[n, tickOrd/last.visible[n], tickOrd/last.read[n], tickOrd/last.sent[n], tickOrd/last.needsToSend[n]]
+ }
+}
+
+assert OneLeader {
+ all t: msg/Tick |
+ lone n: msg/Node |
+ t.state[n].leader = True
+}
+
+fact CleanupViz {
+ RingLeadNode = msg/Node
+ RingLeadMsgState = msg/MsgState
+ RingLeadNodeState = msg/NodeState
+}
+
+pred SomeLeaderAtTick[t: msg/Tick] {
+ some n: msg/Node | t.state[n].leader = True
+}
+
+pred NeverFindLeader {
+ msg/Loop
+ all t: msg/Tick | ! SomeLeaderAtTick[t]
+}
+
+assert Liveness {
+ (msg/NoLostMessages && msg/NoMessageShortage) => ! NeverFindLeader
+}
+
+pred SomeLeader { some t: msg/Tick | SomeLeaderAtTick[t] }
+
+assert LeaderHighest {
+ all t: msg/Tick, n: msg/Node |
+ t.state[n].leader = True => n = nodeOrd/last
+}
+
+run NeverFindLeader for 1 but 3 msg/Tick, 2 Bool, 2 msg/NodeState expect 1
+check Liveness for 3 but 6 msg/Msg, 2 Bool, 2 msg/NodeState expect 0
+check OneLeader for 5 but 2 Bool, 2 msg/NodeState expect 0
+run SomeLeader for 2 but 3 msg/Node, 5 msg/Msg, 5 msg/Tick, 5 msg/MsgState expect 1
+check LeaderHighest for 3 but 2 msg/NodeState, 5 msg/Msg, 5 msg/MsgState, 5 msg/Tick expect 0
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/ringlead.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/ringlead.thm
new file mode 100644
index 00000000..95977a51
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/ringlead.thm
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/s_ringlead.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/s_ringlead.als
new file mode 100644
index 00000000..8f96b477
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/s_ringlead.als
@@ -0,0 +1,153 @@
+module examples/algorithms/s_ringlead
+
+/*
+ * Model of leader election on a ring.
+ *
+ * Each process has a unique ID, IDs are ordered. The algorithm
+ * elects the process with the highest ID the leader, as follows.
+ * First, each process sends its own ID to its right neighbor.
+ * Then, whenever a process receives an ID, if the ID is greater
+ * than the process' own ID it forwards the ID to its right
+ * neighbor, otherwise does nothing. When a process receives its
+ * own ID that process is the leader.
+ *
+ * Note: This file needs higher order quantifiers turned on.
+ */
+
+open util/ordering[State] as so
+open util/ordering[Process] as po
+open util/graph[Process] as graph
+
+sig Process {
+ rightNeighbor: Process
+}
+
+sig State {
+ // buffer which the right neighbor can read from
+ buffer: Process -> Process,
+ //sends, reads: Process -> Process,
+ runs: set Process,
+ leader: set Process
+}
+
+fact DefineRing {
+ graph/ring[rightNeighbor]
+}
+
+fact InitialState {
+ no so/first.buffer
+ no so/first.leader
+ Process in so/first.runs
+}
+
+
+fact CleanupLast {
+ let ls = so/last |
+ no ls.runs
+}
+
+pred ValidTrans2[s, s': State] {
+ all p : s.runs | VT2Helper[p,s,s']
+ all p : Process - s.runs | NOP2[p,s,s']
+ NoMagicMsg[s,s']
+
+}
+
+pred NoMagicMsg[s, s' : State] {
+ // no magically appearing messages
+ all p : Process, m : s'.buffer[p] |
+ m in s.buffer[p] || (!NOP2[p,s,s'] &&
+ ((s = so/first && m = p) ||
+ (s != so/first && m in s.buffer[p.~rightNeighbor]
+ && m !in s'.buffer[p.~rightNeighbor] && po/gt[m,p])))
+}
+
+pred PossTrans[s, s' : State] {
+ all p : Process | VT2Helper[p,s,s'] || NOP2[p,s,s']
+ NoMagicMsg[s,s']
+}
+
+pred VT2Helper[p : Process, s, s' : State] {
+ (
+ let readable=s.buffer[p.~rightNeighbor] |
+ (s = so/first) => {
+ p = s'.buffer[p]
+ readable in s'.buffer[p.~rightNeighbor]
+ p !in s'.leader
+ } else {
+ (some readable) => {
+ some m : set readable | {
+ m !in s'.buffer[p.~rightNeighbor]
+ // nothing else gets deleted
+ readable - m in s'.buffer[p.~rightNeighbor]
+ { m': m | po/gt[m',p] } /*m & nexts(p)*/ in s'.buffer[p]
+ p in s'.leader iff (p in s.leader || p in m)
+ }
+ } else NOP2[p,s,s']
+ }
+ )
+}
+
+pred NOP2[p : Process, s,s': State] {
+ p in s'.leader iff p in s.leader
+ // no reads
+ s.buffer[p.~rightNeighbor] in s'.buffer[p.~rightNeighbor]
+ // no sends
+ s'.buffer[p] in s.buffer[p]
+}
+
+pred Preconds[p : Process, s : State] {
+ s = so/first || some s.buffer[p.~rightNeighbor]
+}
+
+fact TransIfPossible {
+ all s : State - so/last |
+ (all p : Process | NOP2[p, s, so/next[s]]) =>
+ (all p : Process | !Preconds[p,s])
+}
+
+fact LegalTrans {
+ all s : State - so/last |
+ let s' = so/next[s] |
+ ValidTrans2[s,s']
+}
+
+pred EquivStates[s, s': State] {
+ s.buffer = s'.buffer
+ s.leader = s'.leader
+}
+
+assert Safety {
+ all s : State | lone s.leader
+}
+
+pred Legit[s : State] {
+ one s.leader
+}
+
+pred BadLivenessTrace {
+ all s : State | !Legit[s]
+ let ls = so/last |
+ some s : State - ls | {
+ EquivStates[s, ls]
+ Process in (so/nexts[s] + s).runs
+ }
+}
+
+pred TraceWithoutLoop {
+ all t1, t2 : State | t1!=t2 => !EquivStates[t1,t2]
+ all s, s' : State | (s' in (so/nexts[s] - so/next[s])) => !PossTrans[s,s']
+ all s : State | !Legit[s]
+}
+
+pred AltTrans {
+ SomeLeader
+}
+
+pred SomeLeader { some State.leader }
+
+run BadLivenessTrace for 3 but 8 State expect 0
+run SomeLeader for 4 but 6 State expect 1
+check Safety for 7 expect 0
+// run TraceWithoutLoop for 5 but 13 State expect 1
+run AltTrans for 5 but 8 State expect 1
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_mutex_ring.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_mutex_ring.als
new file mode 100644
index 00000000..d4a21f12
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_mutex_ring.als
@@ -0,0 +1,202 @@
+module examples/algorithms/stable_mutex_ring
+
+/*
+ * Dijkstra's K-state mutual exclusion algorithm for a ring
+ *
+ * Original paper describing the algorithm:
+ * [1] E.W.Dijkstra, "Self-Stabilizing Systems in Spite of
+ * Distributed Control", Comm. ACM, vol. 17, no. 11, pp.
+ * 643-644, Nov. 1974
+ *
+ * Proof of algorithm's correctness:
+ * [2] E.W.Dijkstra, "A Belated Proof of Self-Stabilization",
+ * in Distributed Computing, vol. 1, no. 1, pp. 5-6, 1986
+ *
+ * SMV analysis of this algorithm is described in:
+ * [3] "Symbolic Model Checking for Self-Stabilizing Algorithms",
+ * by Tatsuhiro Tsuchiya, Shini'ichi Nagano, Rohayu Bt Paidi, and
+ * Tohru Kikuno, in IEEE Transactions on Parallel and Distributed
+ * Systems, vol. 12, no. 1, January 2001
+ *
+ * Description of algorithm (adapted from [3]):
+ *
+ * Consider a distributed system that consists of n processes
+ * connected in the form of a ring. We assume the state-reading
+ * model in which processes can directly read the state of their
+ * neighbors. We define _privilege_ of a process as its ability to
+ * change its current state. This ability is based on a Boolean
+ * predicate that consists of its current state and the state of
+ * one of its neighboring processes.
+ *
+ * We then define the legitimate states as those in which the
+ * following two properties hold: 1) exactly one process has a
+ * privilege, and 2) every process will eventually have a privilege.
+ * These properties correspond to a form of mutual exclusion, because
+ * the privileged process can be regarded as the only process that is
+ * allowed in its critical section.
+ *
+ * In the K-state algorithm, the state of each process is in
+ * {0,1,2,...,K-1}, where K is an integer larger than or equal to n.
+ * For any process p_i, we use the symbols S and L to denote its
+ * state and the state of its neighbor p_{i-1}, respectively, and
+ * process p_0 is treated differently from all other processes. The
+ * K-state algorithm is described below.
+ *
+ * process p_0: if (L=S) { S := (S+1) mod K; }
+ * process P_i(i=1,...,n-1): if (L!=S) { S:=L; }
+ */
+
+open util/ordering[Tick] as to
+open util/graph[Process] as pg
+open util/graph[Val] as vg
+
+sig Process {
+ rightNeighbor: Process
+}
+
+sig Val {
+ nextVal : Val
+}
+
+fact MoreValThanProcess {
+ # Val > # Process
+}
+
+fact DefineRings {
+ pg/ring[rightNeighbor]
+ vg/ring[nextVal]
+ //Val$nextVal = Ord[Val].next + (Ord[Val].last -> Ord[Val].first)
+}
+
+sig Tick {
+ val: Process -> one Val,
+ runs: set Process, // processes scheduled to run on this tick
+ // for visualization
+ priv: set Process // the set of priviledged processes on this tick
+}
+{
+ priv = { p : Process | Privileged[p, this] }
+}
+
+one sig FirstProc extends Process {
+}
+
+
+fun FirstProcTrans[curVal, neighborVal : Val]: Val {
+ (curVal = neighborVal) => curVal.nextVal else curVal
+}
+
+fun RestProcTrans[curVal, neighborVal : Val]: Val {
+ (curVal != neighborVal) => neighborVal else curVal
+}
+
+fact LegalTrans {
+ all tp : Tick - to/last |
+ let tn = to/next[tp] | {
+ all p: Process |
+ let curVal = tp.val[p], neighborVal = tp.val[p.rightNeighbor], newVal = tn.val[p] | {
+ p !in tp.runs => newVal = curVal else {
+ p = FirstProc =>
+ newVal = FirstProcTrans[curVal, neighborVal]
+ else
+ newVal = RestProcTrans[curVal, neighborVal]
+ }
+ }
+ }
+}
+
+pred TickTrans[tp, tn : Tick] {
+ all p : Process |
+ let curVal = tp.val[p], neighborVal = tp.val[p.rightNeighbor], newVal = tn.val[p] | {
+ p = FirstProc =>
+ newVal = FirstProcTrans[curVal, neighborVal]
+ else
+ newVal = RestProcTrans[curVal, neighborVal]
+ }
+}
+
+/**
+ * whether this process can enter its critical section
+ * on this tick
+ */
+pred Privileged[p : Process, t : Tick] {
+ p = FirstProc =>
+ t.val[p] = t.val[p.rightNeighbor]
+ else
+ t.val[p] != t.val[p.rightNeighbor]
+}
+
+pred IsomorphicStates[val1, val2: Process -> one Val] {
+ some processMap: Process one -> one Process,
+ valMap: Val one -> one Val | {
+ FirstProc.processMap = FirstProc
+ all p: Process, v: Val | {
+ p->v in val1 iff (p.processMap) -> (v.valMap) in val2
+ }
+ all v1,v2: Val | v1->v2 in nextVal iff (v1.valMap) -> (v2.valMap) in nextVal
+ all p1,p2: Process | p1->p2 in rightNeighbor
+ iff (p1.processMap) -> (p2.processMap) in rightNeighbor
+ }
+}
+
+/**
+ * Find a trace that goes into a loop
+ * containing a bad tick, i.e. a tick
+ * at which two distinct processes
+ * try to run their critical sections
+ * simultaneously. In such a trace the
+ * algorithm never "stabilizes".
+ */
+pred BadSafetyTrace {
+ let lst = to/last |
+ some t : Tick - lst | {
+ //IsomorphicStates(ft.val, lst.val)
+ t.val = lst.val
+ Process in (to/nexts[t] + t - lst).runs
+ some badTick : to/nexts[t] + t |
+ BadTick[badTick]
+ }
+}
+
+/**
+ * Two different processes simultaneously
+ * try to run their critical sections at this tick
+ */
+pred BadTick[badTick : Tick] {
+ some p1 , p2 : Process | {
+ p1!=p2
+ Privileged[p1, badTick]
+ Privileged[p2, badTick]
+ }
+}
+
+assert Closure {
+ not BadTick[to/first] => (all t : Tick | not BadTick[t])
+}
+
+pred TwoPrivileged {
+ BadTick[to/first]
+ some p1, p2 : Process, t1, t2 : Tick - to/first | {
+ p1!=p2
+ Privileged[p1,t1]
+ Privileged[p2,t2]
+ }
+}
+
+pred TraceWithoutLoop {
+ all t1, t2 : Tick | t1!=t2 => t1.val != t2.val
+}
+
+pred TraceShorterThanMaxSimpleLoop {
+ to/first.val = to/last.val
+ all t : Tick - to/first - to/last |
+ !(t.val = to/first.val)
+}
+
+run TraceShorterThanMaxSimpleLoop for 7 but 2 Process, 3 Val expect 1
+run TwoPrivileged for 5 but 3 Process, 4 Val expect 1
+check Closure for 5 but 5 Process, 6 Val expect 0
+//run BadSafetyTrace for 16 but 3 Process, 4 Val
+//run TraceWithoutLoop for 21 but 4 Process, 5 Val
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_mutex_ring.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_mutex_ring.thm
new file mode 100644
index 00000000..5f31f3ac
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_mutex_ring.thm
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_orient_ring.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_orient_ring.als
new file mode 100644
index 00000000..c6fec3fe
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_orient_ring.als
@@ -0,0 +1,90 @@
+module examples/algorithms/stable_orient_ring
+
+/*
+ * A self-stabilizing algorithm for orienting uniform rings.
+ * Communication model is the state-reading model.
+ */
+
+open util/boolean as bool
+open util/ordering[Tick] as ord
+open util/graph[Process] as graph
+
+sig Process {
+ rightNeighbor: Process,
+ AP1, AP2: Process
+}
+
+fun leftNeighbor[p: Process]: Process {
+ p.~(rightNeighbor)
+}
+
+fact {
+ all p: Process {
+ (p.AP1=p.rightNeighbor && p.AP2=leftNeighbor[p]) ||
+ (p.AP2=p.rightNeighbor && p.AP1=leftNeighbor[p])
+ }
+}
+
+fact DefineRing {
+ graph/ring[rightNeighbor]
+}
+
+sig Tick {
+ runs: set Process,
+ dir, S, T: Process -> one Bool,
+ ring_: Process -> Process
+}
+{
+ all p: Process | p.ring_ = (p.dir=True => p.AP1 else p.AP2)
+}
+
+pred Eq3[b1,b2,b3: Bool] { b1 = b2 && b2 = b3 }
+pred Eq4[b1,b2,b3,b4: Bool] { Eq3[b1,b2,b3] && b3=b4 }
+
+fact Transitions {
+ all tp: Tick - ord/last | let tn = ord/next[tp] |
+ all p: Process |
+ let p1 = p.AP1, p2 = p.AP2, pS = tp.S, pT=tp.T, nS=tn.S, nT=tn.T |
+ let S1p=p1.pS, S2p=p2.pS,
+ T1p=p1.pT, T2p=p2.pT,
+ Sp = p.pS, Sn=p.nS,
+ Tp = p.pT, Tn=p.nT,
+ dirp = p.(tp.dir), dirn = p.(tn.dir) | {
+ p !in tp.runs => ( Sn = Sp && Tn = Tp && dirn = dirp ) else (
+ S1p = S2p => ( Sn = Not[S1p] && Tn = True && dirn=dirp) else (
+ (Eq3[S1p, Sp, Not[S2p]] &&
+ Eq4[Not[T1p],Tp,T2p,True]) =>
+ (Sn = Not[Sp] && Tn = False && dirn = True)
+ else (
+ (Eq3[Not[S1p],Sp,S2p] && Eq4[T1p,Tp,Not[T2p],True]) =>
+ (Sn = Not[Sp] && Tn = False && dirn = False) else (
+ ((Eq3[S1p,Sp,Not[S2p]] && T1p=Tp) ||
+ (Eq3[Not[S1p],Sp,S2p] && Tp=T2p)) =>
+ (Tn = Not[Tp] && Sn=Sp && dirn=dirp) else (
+ Sn=Sp && Tn=Tp && dirn=dirp
+ )
+ )
+ )
+ )
+ )
+ }
+}
+
+pred RingAtTick[t: Tick] {
+ let rng = t.ring_ |
+ graph/ring[rng] || graph/ring[~rng]
+}
+
+assert Closure {
+ // if the ring is properly oriented
+ all t: Tick - ord/last |
+ RingAtTick[t] => RingAtTick[ord/next[t]]
+}
+
+pred SomeState {
+ !graph/ring[ord/first.ring_]
+ some t: Tick | graph/ring[t.ring_]
+}
+
+run SomeState for 1 but 2 Tick, 2 Bool, 3 Process expect 1
+check Closure for 1 but 2 Tick, 2 Bool, 3 Process expect 1
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_orient_ring.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_orient_ring.thm
new file mode 100644
index 00000000..0a8cba73
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_orient_ring.thm
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_ringlead.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_ringlead.als
new file mode 100644
index 00000000..577257f8
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_ringlead.als
@@ -0,0 +1,180 @@
+module examples/algorithms/stable_ringlead
+
+/*
+ * Huang's self-stabilizing leader-election algorithm
+ * for rings.
+ */
+
+open util/ordering[Process] as po
+open util/ordering[Val] as vo
+open util/ordering[State] as so
+open util/graph[Process] as graph
+
+sig Process {
+ rightNeighbor: Process
+}
+
+sig Val {
+ nextVal : Val
+}
+
+fact {
+ graph/ring[rightNeighbor]
+ vo/next + (vo/last -> vo/first) = nextVal
+ # Val = # Process
+}
+
+sig State {
+ val : Process -> one Val,
+ running : set Process
+ // for visualization
+ //leader : set Process
+} {
+ //leader = { p : Process | LeaderAtState(p, this) }
+}
+
+fact {
+ no so/last.running
+}
+
+fun LeadersAtState[t : State] : set Process {
+ { p : Process | LeaderAtState[p,t] }
+}
+
+pred LeaderAtState[p : Process, t : State] { ValAtState[p,t] = vo/first }
+
+fun ValAtState[p : Process, t : State] : Val { t.val[p] }
+
+fun LeftValAtState[p : Process, t : State] : Val { t.val[p.~rightNeighbor] }
+
+fun RightValAtState[p : Process, t : State] : Val { t.val[p.rightNeighbor] }
+
+fun XAtState[p : Process, t : State] : Int {
+ g[LeftValAtState[p,t],ValAtState[p,t]]
+}
+
+fun YAtState[p : Process, t : State] : Int {
+ g[ValAtState[p,t],RightValAtState[p,t]]
+}
+
+fun g[a, b : Val] : Int {
+ (a = b) => Int[# Val] else minus[b,a]
+}
+
+fun minus[v1, v2 : Val] : Int {
+ Int[ (v1 = v2) => 0
+ else vo/gt[v1, v2] => (# (vo/nexts[v2] & vo/prevs[v1] + v1))
+ else (# (Val - (vo/nexts[v1] & vo/prevs[v2] + v1)))
+ ]
+}
+
+fun Trans[oldVal : Val, x, y : Int] : Val {
+ ((int x = int y && int y = # Val) || (int x < int y)) => oldVal.nextVal else oldVal
+}
+
+pred OneAtATimeTrans {
+ all tp: State - so/last |
+ let tn = so/next[tp] |
+ some p : Process | {
+ tp.running = p
+ TransHelper[p,tp,tn]
+ all other : Process - p |
+ ValAtState[other,tn] = ValAtState[other,tp]
+ }
+}
+
+pred DDaemonTrans {
+ all tp: State - so/last |
+ let tn = so/next[tp] | {
+ some tp.running
+ all p : tp.running | TransHelper[p,tp,tn]
+ all other : Process - tp.running |
+ ValAtState[other,tn] = ValAtState[other,tp]
+ }
+}
+
+pred TransHelper[p : Process, tp, tn : State] {
+ let oldVal = ValAtState[p, tp],
+ newVal = ValAtState[p, tn],
+ x = XAtState[p, tp],
+ y = YAtState[p,tp] |
+ newVal = Trans[oldVal, x, y]
+
+}
+
+pred StateTrans[s, s' : State] {
+ all p : Process |
+ TransHelper[p, s, s'] || ValAtState[p,s] = ValAtState[p,s']
+}
+
+
+
+pred CBadLivenessTrace {
+ OneAtATimeTrans
+ BadLivenessHelper
+}
+
+pred DBadLivenessTrace {
+ DDaemonTrans
+ BadLivenessHelper
+}
+
+pred BadLivenessHelper {
+ let ls = so/last |
+ some s : State - ls | {
+ s.val = ls.val
+ // fair
+ Process in (so/nexts[s] + s - ls).running
+ }
+ all s : State | ! Legit[s]
+ }
+
+pred CTraceWithoutLoop {
+ OneAtATimeTrans
+ all t, t' : State | t!=t' => t.val != t'.val
+}
+
+pred DTraceWithoutLoop {
+ DDaemonTrans
+ all t, t' : State | t!=t' => {
+ t.val != t'.val
+ (t' in so/nexts[t] && t' != so/next[t]) => !StateTrans[t,t']
+ }
+ all t : State | !Legit[t]
+}
+
+pred ConvergingRun {
+ OneAtATimeTrans
+ !Legit[so/first]
+ some t : State | Legit[t]
+}
+
+pred OnlyFairLoops {
+ OneAtATimeTrans
+ all s, s' : State |
+ (s' in so/nexts[s] && s'.val = s.val) =>
+ (let loopStates = (so/nexts[s] & so/prevs[s']) + s + s' | Process in loopStates.running)
+}
+
+assert CMustConverge {
+ OnlyFairLoops => (some s : State | Legit[s])
+}
+
+pred Legit [s : State] {
+ one LeadersAtState[s]
+ all p : Process | {
+ int XAtState[p,s] < # Val
+ int YAtState[p,s] < # Val
+ }
+ all p, p' : Process | {
+ int XAtState[p,s] = int XAtState[p',s]
+ int YAtState[p,s] = int YAtState[p',s]
+ }
+}
+
+run ConvergingRun for 3 but 4 State expect 1
+run DTraceWithoutLoop for 3 but 4 State expect 1
+run DBadLivenessTrace for 3 but 4 State expect 1
+run CTraceWithoutLoop for 3 but 5 State expect 0
+run CBadLivenessTrace for 4 but 5 State expect 1
+check CMustConverge for 3 but 4 State expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_ringlead.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_ringlead.thm
new file mode 100644
index 00000000..d580f3a1
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/algorithms/stable_ringlead.thm
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/INSLabel.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/INSLabel.als
new file mode 100644
index 00000000..9f8a2024
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/INSLabel.als
@@ -0,0 +1,149 @@
+module examples/case_studies/INSLabel
+
+/*
+ * Models an Intentional Naming System (INS), a scheme for
+ * dynamic resource discovery in a dynamic environment.
+ *
+ * For a detailed description, see:
+ * http://sdg.lcs.mit.edu/pubs/2000/INS_ASE00.pdf
+ *
+ * author: Sarfraz Khurshid
+ */
+
+sig Record {}
+
+sig Label {}
+
+sig Node {
+ label: Label
+}
+
+sig LabelTree {
+ root: Node,
+ nodes: set Node,
+ children: nodes one -> (nodes - root)
+}
+{ // connected
+ nodes = root.*children
+ some root.children
+}
+
+pred Get[db: DB, r: Record, a:Advertisement] {
+ root[a] = root[db]
+ nodes[a] =
+ nodes[db] & r.~(db.recs).*(~(db.children))
+ anodes[a] =
+ anodes[db] & r.~(db.recs).*(~(db.children))
+ vnodes[a] =
+ vnodes[db] & r.~(db.recs).*(~(db.children))
+ all n: a.nodes |
+ n.~(a.children) = n.~(db.children)
+}
+
+sig Attribute extends Label {}
+
+sig Value extends Label {}
+
+one sig Wildcard, Null extends Value {}
+
+sig AVTree extends LabelTree {
+ vnodes, anodes: set nodes
+}
+{
+ root in vnodes
+ root.label = Null
+ Null !in (vnodes - root).label + anodes.label
+ anodes.label in Attribute
+ vnodes.label in Value
+ all n: nodes | all /* disj */ c,c': n.children |
+ c.label != c'.label
+ all a: anodes | a.children in vnodes && some a.children
+ all v: vnodes | v.children in anodes
+ no Wildcard.~label.children
+}
+
+one sig Query extends AVTree {}
+{
+ all a: anodes | one a.children
+}
+
+one sig Advertisement extends AVTree {}
+{
+ Wildcard !in vnodes.label
+}
+
+one sig DB extends AVTree {
+ records: set Record,
+ recs: (vnodes - root) some -> records
+}
+{
+ Wildcard !in vnodes.label
+ all a: anodes | no a.recs
+ all v: vnodes {
+ no v.children => some v.recs
+ no v.recs & v.^(~children).recs }
+ all a: anodes | all disj v,v': a.children |
+ (no v.*children.recs & v'.*children.recs)
+}
+
+one sig State {
+ conforms: Query -> Advertisement -> Node -> Node,
+ lookup: DB -> Query -> Node -> Node -> Record
+}
+
+fact ConformsFixPoint {
+ all q: Query | all a: Advertisement |
+ all nq: Node | all na: Node |
+ q.ConformsAux[a,nq,na] <=>
+ {
+ nq.label in Wildcard + na.label
+ all nq': q.children[nq] | some na': a.children[na] |
+ q.ConformsAux[a,nq',na']
+ }
+}
+
+pred Query.ConformsAux[a: Advertisement, nq: Node, na: Node] {
+ na in State.conforms[this][a][nq]
+}
+
+pred Conforms[q: Query, a:Advertisement] {
+ q.ConformsAux[a, q.root, a.root]
+}
+
+fact LookupFixPoint {
+ all db: DB, q: Query, T: Node, n: Node, r: Record |
+ r in db.LookupAux[q,T,n] <=> // record r is in the result if and only if
+ {
+ all na: n.(q.children) | all nv: na.(q.children) | // for all child av-pairs (na,nv) of av-pair n in q
+ some Ta: T.(db.children) {
+ Ta.label = na.label // Ta is a child node with attribute na
+ nv.label = Wildcard => // wildcard matching
+ r in Ta.^(db.children).(db.recs) else // r is a record of any child of Ta
+ (some Tv: Ta.(db.children) { // normal matching
+ Tv.label = nv.label // Tv is a child of Ta with value nv
+ no nv.(q.children) => // if Tv is a leaf-node
+ r in Tv.*(db.children).(db.recs) else // r is a record of Tv or of v
+ r in db.LookupAux[q,Tv,nv] }) } // else r is a record of the recursive call at Tv
+ }
+}
+
+fun DB.LookupAux[q: Query, vd: Node, vq: Node]: set Record { // helper function for Lookup
+ State.lookup[this][q][vd][vq]
+}
+
+fun Lookup[db: DB, q: Query]: set Record { // models Lookup-Name algorithm invocation
+ db.LookupAux[q, db.root, q.root]
+}
+
+assert LookupConforms2 { //soundness and completeness
+ all q: Query | all db: DB | all r: Record | all a: Advertisement |
+ Get[db,r,a] => // all n: a.nodes | n.~(db.children)
+ {r in db.Lookup[q] <=> q.Conforms[a]}
+}
+
+// < 10 sec
+check LookupConforms2 for 4 but 1 State, 3 LabelTree, 2 Record expect 0
+// ~ 25 min
+//check LookupConforms2 for 6 but 1 State, 3 LabelTree, 2 Record
+//check LookupConforms2 for 6 but 1 State, 3 LabelTree, 3 Record
+run Lookup for 3 expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chord.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chord.als
new file mode 100644
index 00000000..f7a042f1
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chord.als
@@ -0,0 +1,260 @@
+module examples/case_studies/chord
+
+/*
+ * Models the chord distributed hash table lookup protocol.
+ *
+ * For a detailed description, see:
+ * http://www.pdos.lcs.mit.edu/papers/chord:sigcomm01/
+ */
+
+open util/relation as rel
+
+sig Id {next: Id}
+fact {all i: Id | Id in i.*next}
+
+/**
+ * true iff i precedes j in the order starting at from
+ */
+pred less_than [from, i,j: Id] {
+ let next' = Id<:next - (Id->from) | j in i.^next' // if from=j, returns true if # nodes > 1
+ }
+pred less_than_eq [from, i,j: Id] {
+ let next' = Id<:next - (Id->from) | j in i.*next'
+ }
+
+sig Node {id: Id}
+fact {all m,n: Node | m!=n => m.id != n.id}
+
+
+sig NodeData {
+ prev, next: Node,
+ finger: Id -> lone Node,
+ closest_preceding_finger: Id -> one Node,
+ find_predecessor: Id -> one Node,
+ find_successor: Id -> one Node
+ }
+
+sig State {
+ active: set Node,
+ data: active -> one NodeData
+ }
+
+/**
+ * node n's next node is defined to be the m where n's finger table maps the id
+ * that follows n.id to m
+ * next holds the first entry of the finger table
+ */
+fact {all s: State | all n: s.active | n.(s.data).next = n.(s.data).finger[n.id.next]}
+
+pred NextCorrect [s: State] {
+ all n: s.active {
+ -- no intervening node (ie, close enough)
+ no n': s.active - n | less_than [n.id, n'.id, n.(s.data).next.id]
+ -- can reach all other active nodes (ie, far enough)
+ -- need this because can't rule out case of next being node itself (because of 1-node ring)
+ -- s.active in n.*(s.data.next)
+ n.(s.data).next != n || #s.active = 1
+ }
+ }
+
+pred NextCorrect' [s: State] {
+-- next seems to be correct for 1,2,3 nodes
+ all n: s.active | let nd = (s.data)[n] {
+ let next' = Id<:next - (Id -> nd.next.id) {
+ no n' : s.active { n'.id in n.id.^next' }
+ }}
+ }
+
+// valid
+assert Same1 {all s: State | NextCorrect[s] => NextCorrect'[s]}
+check Same1 for 3 but 1 State expect 0
+
+// valid unless active condition removed
+assert Same2 {all s: State | s.active = Node => (NextCorrect'[s] => NextCorrect[s])}
+check Same2 for 3 but 1 State expect 0
+
+-- assert NextInFinger {all s: State | all n: s.active | some n.s.data.finger[n.id.next] }
+
+
+-- says that finger entry maps an id to a node so that there are no intervening nodes
+-- between the id and the node
+pred FingersCorrect [s: State] {
+ all nd: s.active.(s.data) | all start:nd.finger.univ |
+ nd.finger[start] in s.active &&
+ (no n' : s.active | less_than [start, n'.id, nd.finger[start].id])
+ }
+
+
+pred FingersCorrect' [s: State] {
+ all n: s.active | let nd = (s.data)[n] | all start: Node.~(nd.finger) {
+ nd.finger[start] in s.active &&
+ (let next' = Id<:next - (nd.finger[start].id -> Id) {
+ no n' : s.active - nd.finger[start] {
+ n'.id in start.*next'
+ }
+ })
+ }
+ }
+
+
+assert SameFC {all s: State | FingersCorrect [s] iff FingersCorrect'[s]}
+check SameFC for 3 but 1 State expect 0
+
+
+pred ShowMeFC {
+ all s : State | s.active = Node && FingersCorrect[s]
+}
+
+run ShowMeFC for 2 but 1 State expect 1
+
+pred ClosestPrecedingFinger[s: State] {
+ all n: s.active | let nd = n.(s.data) |
+ all i: Id | let cpf = nd.closest_preceding_finger[i] {
+ no n': nd.finger[Id] + n - cpf | less_than [cpf.id, n'.id, i]
+ cpf in nd.finger[Id] + n
+ cpf.id != i || # s.active = 1
+ //less_than (n.id, cpf.id, i)
+ }
+ }
+
+
+pred ClosestPrecedingFinger'[s: State] {
+ all n: s.active | let nd = (s.data)[n] | all i: Id {
+ let next' = Id<:next - (Id -> i) {
+ nd.next.id in n.id.^next' =>
+ // nd.closest_preceding_finger[i] = nd.next,
+ (some n1: nd.finger[Id] {
+ nd.closest_preceding_finger[i] = n1
+ //n1 in nd.finger[Id]
+ n1.id in n.id.^next'
+ no n2: nd.finger[Id] | n2.id in n1.id.^next'
+ }) else
+ nd.closest_preceding_finger[i] = n
+ }}
+ }
+
+
+assert SameCPF {all s: State | FingersCorrect[s] => (ClosestPrecedingFinger [s] iff ClosestPrecedingFinger' [s])}
+assert SameCPF1 {all s: State | FingersCorrect[s] => (ClosestPrecedingFinger [s] => ClosestPrecedingFinger' [s])}
+assert SameCPF2 {
+ all s: State | ((s.active = Node && FingersCorrect[s] && ClosestPrecedingFinger' [s])
+ => ClosestPrecedingFinger [s]) }
+
+check SameCPF for 3 but 1 State expect 0
+check SameCPF1 for 2 but 1 State expect 0
+check SameCPF2 for 3 but 1 State expect 0
+
+
+pred ShowMeCPF {
+ all s : State | s.active = Node && FingersCorrect[s] &&
+ // not ClosestPrecedingFinger(s) && ClosestPrecedingFinger'(s)
+ ClosestPrecedingFinger[s]
+ //all s : State | all nd : s.active.s.data | nd.finger[Id] = Node
+ # Node = 2
+ # State = 1
+}
+
+
+run ShowMeCPF for 2 but 1 State expect 1
+
+
+pred FindPredecessor[s: State] {
+ all n: s.active | let nd = n.(s.data) | all i: Id {
+ nd.find_predecessor[i] =
+ (less_than_eq [n.id, i, nd.next.id] && (n.id != i || # s.active = 1)
+ => n
+ else (nd.closest_preceding_finger[i].(s.data).find_predecessor)[i])
+ }
+ }
+
+
+assert FPisActive {
+ all s: State | FingersCorrect[s] && ClosestPrecedingFinger[s] && FindPredecessor[s]
+ => (all n: s.active | all nd: n.(s.data) | nd.find_predecessor[Id] in s.active) }
+check FPisActive for 3 but 1 State expect 1
+
+
+pred FindPredecessor'[s: State] {
+ all n: s.active | let nd = (s.data)[n] | all i: Id {
+ let next' = Id<:next - (nd.next.id -> Id) {
+ one s.active or i in n.id.^next' => // *next' -> ^next' 1/8/02
+ nd.find_predecessor[i] = n else
+ nd.find_predecessor[i] =
+ ((s.data)[nd.closest_preceding_finger[i]]).find_predecessor[i]
+ }}
+ }
+
+
+assert SameFP {all s: State | FingersCorrect[s] // && s.active = Node
+ => (FindPredecessor [s] iff FindPredecessor' [s])}
+
+assert SameFP1 {
+ all s: State | FingersCorrect[s] && s.active = Node
+ => (FindPredecessor [s] => FindPredecessor' [s])}
+assert SameFP2 {
+ all s: State | FingersCorrect[s] && s.active = Node
+ => (FindPredecessor' [s] => FindPredecessor [s])}
+
+check SameFP for 3 but 1 State expect 1
+check SameFP1 for 3 but 1 State expect 0
+check SameFP2 for 3 but 1 State expect 0
+
+
+pred FindSuccessor[s: State] {
+ all n: s.active | let nd = (s.data)[n] | all i: Id {
+ nd.find_successor[i] = ((s.data)[nd.find_predecessor[i]]).next
+ }}
+
+
+// should be able to check that closest_p_f, etc returns
+// only active nodes if FingersCorrect.
+
+
+pred ShowMe1Node {
+ #Node = 1
+ all s : State | NextCorrect[s]
+ State.active = Node
+}
+
+run ShowMe1Node for 2 but 1 State, 1 Node expect 1
+
+pred ShowMe1 {
+ #Node = 2
+ #State = 1
+ all s : State | NextCorrect[s]
+ State.active = Node
+}
+
+
+pred ShowMe2 {
+ #Node = 3
+ #State = 1
+ all s : State | NextCorrect[s] && FingersCorrect[s]
+ State.active = Node
+ //all n: NodeData | one n.finger[Id]
+}
+
+
+assert OK1 {
+ #Node = 3 &&
+ #State = 1 &&
+ (all s : State | NextCorrect[s] && FingersCorrect[s]) &&
+ State.active = Node
+}
+
+
+run ShowMe1 for 3 expect 1
+run ShowMe2 for 3 expect 1
+
+assert InjectiveIds {all i, j: Id | i!=j => i.next != j.next}
+check InjectiveIds for 5 expect 0
+
+
+assert FindSuccessorWorks {
+ all s: State, i: Id |
+ let nd = s.active.(s.data) |
+ let succ = nd.find_successor [i] |
+ FingersCorrect [s] // && s.active = Node
+ => (no n': s.active | less_than [i, n'.id, succ.id])
+ }
+check FindSuccessorWorks for 3 but 1 State expect 1
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chord2.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chord2.als
new file mode 100644
index 00000000..ea7c3429
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chord2.als
@@ -0,0 +1,274 @@
+module examples/case_studies/chord2
+
+/*
+ * Models the chord distributed hash table lookup protocol.
+ *
+ * For a detailed description, see:
+ * http://www.pdos.lcs.mit.edu/papers/chord:sigcomm01/
+ */
+
+open util/relation as rel
+
+sig Id {next: Id}
+fact {all i: Id | Id in i.*next}
+
+/**
+ * true iff i precedes j in the order starting at from
+ */
+pred less_than [from, i,j: Id] {
+ let next' = Id<:next - (Id->from) | j in i.^next' // if from=j, returns true if # nodes > 1
+ }
+pred less_than_eq [from, i,j: Id] {
+ let next' = Id<:next - (Id->from) | j in i.*next'
+ }
+
+sig Node {id: Id}
+fact {all m,n: Node | m!=n => m.id != n.id}
+
+sig NodeData {
+ prev, next: Node,
+ finger: Id -> lone Node,
+ closest_preceding_finger: Id -> one Node,
+ find_predecessor: Id -> one Node,
+ find_successor: Id -> one Node
+ }
+
+sig State {
+ active: set Node,
+ data: active -> one NodeData
+ }
+
+/**
+ * node n's next node is defined to be the m where n's finger table maps the id
+ * that follows n.id to m
+ * next holds the first entry of the finger table
+ */
+fact {all s: State | all n: s.active | n.(s.data).next = n.(s.data).finger[n.id.next]}
+
+pred NextCorrect [s: State] {
+ all n: s.active {
+ -- no intervening node (ie, close enough)
+ no n': s.active - n | less_than [n.id, n'.id, n.(s.data).next.id]
+ -- can reach all other active nodes (ie, far enough)
+ -- need this because can't rule out case of next being node itself (because of 1-node ring)
+ -- s.active in n.*(s.data.next)
+ n.(s.data).next != n || #s.active = 1
+ }
+ }
+
+/*
+-- abortive attempt at simplifying next condition
+fun NextCorrect" (s: State) {
+ all n: s.active | let nx = s.data.next {
+ s.active in n.*nx
+ less_than (n.id, n.id, n.nx.id)
+ }
+ }
+*/
+
+pred NextCorrect' [s: State] {
+-- next seems to be correct for 1,2,3 nodes
+ all n: s.active | let nd = (s.data)[n] {
+ let next' = Id<:next - (Id -> nd.next.id) {
+ no n' : s.active { n'.id in n.id.^next' }
+ }}
+ }
+
+assert Same1 {all s: State | NextCorrect[s] => NextCorrect'[s]}
+//check Same1 for 3 but 1 State -- valid
+assert Same2 {all s: State | s.active = Node => (NextCorrect'[s] => NextCorrect[s])}
+//check Same2 for 3 but 1 State -- invalid if active condition removed
+
+-- assert NextInFinger {all s: State | all n: s.active | some n.s.data.finger[n.id.next] }
+
+/**
+ * says that finger entry maps an id to a node so that there are no intervening nodes
+ * between the id and the node
+ */
+pred FingersCorrect [s: State] {
+ all nd: s.active.(s.data) | all start:nd.finger.univ |
+ nd.finger[start] in s.active &&
+ (no n' : s.active | less_than [start, n'.id, nd.finger[start].id])
+ }
+
+pred FingersCorrect' [s: State] {
+ all n: s.active | let nd = (s.data)[n] | all start: Node.~(nd.finger) {
+ nd.finger[start] in s.active &&
+ (let next' = Id<:next - (nd.finger[start].id -> Id) {
+ no n' : s.active - nd.finger[start] {
+ n'.id in start.*next'
+ }})}
+ }
+
+assert SameFC {all s: State | FingersCorrect [s] iff FingersCorrect'[s]}
+//check SameFC for 3 but 1 State
+
+pred ShowMeFC {
+ all s : State | s.active = Node && FingersCorrect[s]
+}
+
+//run ShowMeFC for 2 but 1 State
+
+/*
+fun ClosestPrecedingFinger(s: State) {
+ all n: s.active | let nd = n.s.data |
+ all i: Id | let cpf = nd.closest_preceding_finger[i] {
+ no n': nd.finger[Id] | less_than (cpf.id, n'.id, i)
+ cpf in nd.finger[Id] + n
+ less_than (n.id, cpf.id, i)
+ }
+ }
+*/
+
+pred ClosestPrecedingFinger_SAVE [s: State] {
+ all n: s.active | let nd = n.(s.data) |
+ all i: Id | let cpf = nd.closest_preceding_finger[i] {
+ no n': (nd.finger[Id] + n) - cpf | less_than [cpf.id, n'.id, i]
+ cpf in nd.finger[Id] + n
+ cpf.id != i || # s.active = 1
+ //less_than (n.id, cpf.id, i)
+ }
+ }
+
+pred CPFBody [s: State, n: Node, nd: NodeData, i: Id, cpf: Node] {
+ no n': (nd.finger[Id] + n) - cpf | less_than [cpf.id, n'.id, i]
+ cpf in nd.finger[Id] + n
+ cpf.id != i || # s.active = 1
+ }
+pred ClosestPrecedingFinger[s: State] {
+ all n: s.active | let nd = n.(s.data) |
+ all i: Id |
+ some cpf: Node | CPFBody [s,n,nd,i,cpf] => CPFBody [s,n,nd,i,nd.closest_preceding_finger[i]]
+ }
+
+pred ClosestPrecedingFinger'[s: State] {
+ all n: s.active | let nd = (s.data)[n] | all i: Id {
+ let next' = Id<:next - (Id -> i) {
+ nd.next.id in n.id.^next' =>
+ // nd.closest_preceding_finger[i] = nd.next,
+ (some n1: nd.finger[Id] {
+ nd.closest_preceding_finger[i] = n1
+ //n1 in nd.finger[Id]
+ n1.id in n.id.^next'
+ no n2: nd.finger[Id] | n2.id in n1.id.^next'
+ }) else
+ nd.closest_preceding_finger[i] = n
+ }}
+ }
+
+assert SameCPF {all s: State | FingersCorrect[s] => (ClosestPrecedingFinger [s] iff ClosestPrecedingFinger' [s])}
+assert SameCPF1 {all s: State | FingersCorrect[s] => (ClosestPrecedingFinger [s] => ClosestPrecedingFinger' [s])}
+assert SameCPF2 {
+ all s: State | ((s.active = Node && FingersCorrect[s] && ClosestPrecedingFinger' [s])
+ => ClosestPrecedingFinger [s]) }
+//check SameCPF for 3 but 1 State
+//check SameCPF1 for 2 but 1 State
+//check SameCPF2 for 3 but 1 State
+
+pred ShowMeCPF {
+ all s : State | s.active = Node && FingersCorrect[s] &&
+ // not ClosestPrecedingFinger(s) && ClosestPrecedingFinger'(s)
+ ClosestPrecedingFinger[s]
+ //all s : State | all nd : s.active.s.data | nd.finger[Id] = Node
+ # Node = 2
+ # State = 1
+}
+
+//run ShowMeCPF for 2 but 1 State
+
+pred FindPredecessor[s: State] {
+ all n: s.active | let nd = n.(s.data) | all i: Id {
+ nd.find_predecessor[i] =
+ ((less_than_eq [n.id, i, nd.next.id] && (n.id != i || # s.active = 1))
+ => n
+ else (nd.closest_preceding_finger[i].(s.data).find_predecessor)[i])
+ }
+ }
+-- problem : could return node that's inactive ???
+
+assert FPisActive {
+ all s: State | FingersCorrect[s] && ClosestPrecedingFinger[s] && FindPredecessor[s]
+ => (all n: s.active | all nd: n.(s.data) | nd.find_predecessor[Id] in s.active)
+}
+
+//check FPisActive for 3 but 1 State
+
+pred FindPredecessor'[s: State] {
+ all n: s.active | let nd = (s.data)[n] | all i: Id {
+ let next' = Id<:next - (nd.next.id -> Id) {
+ one s.active or i in n.id.^next' =>
+ nd.find_predecessor[i] = n else
+ nd.find_predecessor[i] =
+ ((s.data)[nd.closest_preceding_finger[i]]).find_predecessor[i]
+ }}
+ }
+
+assert SameFP {all s: State | FingersCorrect[s] && s.active = Node
+ => (FindPredecessor [s] iff FindPredecessor' [s])}
+assert SameFP1 {
+ all s: State | FingersCorrect[s] && s.active = Node
+ => (FindPredecessor [s] => FindPredecessor' [s])}
+assert SameFP2 {
+ all s: State | FingersCorrect[s] && s.active = Node
+ => (FindPredecessor' [s] => FindPredecessor [s])}
+//check SameFP for 3 but 1 State
+//check SameFP1 for 3 but 1 State
+//check SameFP2 for 3 but 1 State
+
+pred FindSuccessor[s: State] {
+ all n: s.active | let nd = (s.data)[n] | all i: Id {
+ nd.find_successor[i] = ((s.data)[nd.find_predecessor[i]]).next
+ }}
+
+fact { all s : State {
+ ClosestPrecedingFinger[s]
+ FindPredecessor[s]
+ FindSuccessor[s]
+ }}
+
+// should be able to //check that closest_p_f, etc returns
+// only active nodes if FingersCorrect.
+
+pred ShowMe1Node {
+ #Node = 1
+ all s : State | NextCorrect[s]
+ State.active = Node
+}
+
+//run ShowMe1Node for 2 but 1 State, 1 Node
+-- does the expected correct thing for 1 node.
+
+pred ShowMe1 {
+ #Node = 2
+ #State = 1
+ all s : State | NextCorrect[s]
+ State.active = Node
+}
+
+pred ShowMe2 {
+ #Node = 3
+ #State = 1
+ all s : State | NextCorrect[s] && FingersCorrect[s]
+ State.active = Node
+ //all n: NodeData | one n.finger[Id]
+}
+
+assert OK1 {
+ #Node = 3 &&
+ #State = 1 &&
+ (all s : State | NextCorrect[s] && FingersCorrect[s]) &&
+ State.active = Node
+}
+
+assert FindSuccessorWorks {
+ all s: State, i: Id |
+ let nd = s.active.(s.data) |
+ let succ = nd.find_successor [i] |
+ FingersCorrect [s]
+ => {
+ no n': s.active | less_than [i, n'.id, succ.id]
+ succ in s.active
+ }
+ }
+
+check FindSuccessorWorks for 4 but 1 State, 3 Node, 3 NodeData expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chordbugmodel.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chordbugmodel.als
new file mode 100644
index 00000000..de6c36bb
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/chordbugmodel.als
@@ -0,0 +1,232 @@
+module examples/case_studies/chord
+
+/*
+ * Models the chord distributed hash table lookup protocol.
+ *
+ * For a detailed description, see:
+ * http://www.pdos.lcs.mit.edu/papers/chord:sigcomm01/
+ */
+
+sig Id {next: Id}
+fact {all i: Id | Id in i.*next}
+
+pred less_than [from, i,j: Id] {
+ let next' = Id<:next - (Id->from) | j in i.^next'
+}
+
+pred less_than_eq [from, i,j: Id] {
+ let next' = Id<:next - (Id->from) | j in i.*next'
+}
+
+sig Node {id: Id}
+fact {all m,n: Node | m!=n => m.id != n.id}
+
+sig NodeData {
+ next: Node,
+ finger: Id -> lone Node,
+ closest_preceding_finger: Id -> one Node,
+ find_successor: Id -> one Node
+}
+
+sig State {
+ active: set Node,
+ data: active -> one NodeData
+}
+
+fact {
+ all s: State | all n: s.active |
+ n.(s.data).next = n.(s.data).finger[n.id.next]
+}
+
+pred NextCorrect [s: State] {
+ all n: s.active | let succ = n.(s.data).next {
+ no n': s.active - n | less_than [n.id, n'.id, succ.id]
+ succ != n || #s.active = 1
+ succ in s.active
+ }
+}
+
+pred FingersCorrect [s: State] {
+ all nd: s.active.(s.data) | all start: (nd.finger).Node |
+ nd.finger[start] in s.active &&
+ (no n' : s.active | less_than [start, n'.id, nd.finger[start].id])
+}
+
+pred save_ClosestPrecedingFinger [s: State] {
+ all n: s.active | let nd = n.(s.data) |
+ all i: Id | let cpf = nd.closest_preceding_finger[i] {
+ no n': (nd.finger[Id] + n) - cpf | less_than [cpf.id, n'.id, i]
+ cpf in nd.finger[Id] + n
+ cpf.id != i || # s.active = 1
+ }
+}
+
+pred save_FindSuccessor[s: State] {
+ all n: s.active | let nd = n.(s.data) | all i: Id {
+ nd.find_successor[i] =
+ (((less_than_eq [n.id, i, nd.next.id] && n.id != i) || # s.active = 1)
+ => nd.next
+ else
+ (nd.closest_preceding_finger[i].(s.data).find_successor)[i])
+ }
+}
+
+pred IrrelevantFact1 {
+ all s : State {
+ ClosestPrecedingFinger[s]
+ FindSuccessor[s]
+ }
+}
+
+pred ShowMe1Node {
+ #Node = 1
+ all s : State | NextCorrect[s] && FingersCorrect[s]
+ State.active = Node
+}
+
+run ShowMe1Node for 2 but 1 State, 1 Node expect 1
+
+pred ShowMeGood {
+ #Id = 4
+ all s : State | NextCorrect[s] && FingersCorrect[s]
+ State.active = Node
+}
+
+run ShowMeGood for 4 but 1 State, 2 Node expect 1
+
+pred FindSuccessorIsCorrect[s: State] {
+ all i: Id | all n: s.active |
+ let succ = (n.(s.data)).find_successor [i] {
+ succ in s.active
+ no n': s.active | less_than [i, n'.id, succ.id]
+ }
+}
+
+pred ShowMeCorrectSuccessorEg {
+ #Node = 3
+ State.active = Node
+ all s: State | FingersCorrect[s] && FindSuccessorIsCorrect[s]
+}
+
+run ShowMeCorrectSuccessorEg for 3 but 1 State expect 1
+
+pred ShowMe3 {
+ #Id = 5
+ #Node = 3
+ #State = 1
+ all s : State | NextCorrect[s] && !FingersCorrect[s]
+ State.active = Node
+}
+
+run ShowMe3 for 5 but 1 State expect 1
+
+pred FindSuccessorWorks {
+ IrrelevantFact1
+ ! (
+ all s: State | FingersCorrect[s]
+ => FindSuccessorIsCorrect[s]
+ )
+}
+
+assert StrongerFindSuccessorWorks {
+ all s: State | NextCorrect[s] => FindSuccessorIsCorrect[s]
+}
+
+run FindSuccessorWorks for 4 but 1 State expect 0
+check StrongerFindSuccessorWorks for 4 but 1 State expect 1
+
+/*
+\section Variations
+
+In the pseudocode presented in [\cite{chord1},
+\cite{chord2}], there is some ambiguity as to what the
+expression \tt<(n, n.successor]> means in boundary cases
+where there is exactly one node and \tt.
+The intention of the authors is that the set includes
+\tt. We consider variations of the alloy model with the
+bug where the set \tt<(n, n]> does not include \tt, and
+observe how it affects the \tt and
+the \tt routines.
+
+\subsection faulty \tt
+
+Suppose we change \tt as follows:
+
+\code
+*/
+
+pred ClosestPrecedingFinger [s: State] {
+ all n: s.active | let nd = n.(s.data) |
+ all i: Id | let cpf = nd.closest_preceding_finger[i] {
+ no n': (nd.finger[Id] + n) - cpf | less_than [cpf.id, n'.id, i]
+ cpf in nd.finger[Id] + n
+ cpf.id != i
+ }
+}
+
+/*
+The only change here is in the last line
+\cite{cpf-variation}, where we removed the clause \tt< || #
+s.active=1>. The assertion \tt will
+still hold for scope up to 4, but \tt will fail
+to generate an example! This is an example of a
+over-constraint, where the inconsistency only shows up when
+there is exactly one node. What happens here is that the
+model requires that a closest preceding finger node has a
+distinct identifier from the input identifier, but this
+cannot happen if there is exactly one node and if the input
+identifer equals that of the node.
+
+\subsection faulty \tt
+
+Consider the following pseudocode segment from [\cite{chord2}]:
+
+n.find_successor(id)
+ if (id in (n, n.successor])
+ return n.successor;
+ else
+ n' = closest_preceding_finger(id);
+ return n'.find_successor(id);
+
+In the buggy scenario with a single node, the \tt loop
+always terminates at \cite{if-condition1}, leading to an
+infinite loop.
+
+Consider the corresponding change to \tt as follows:
+
+*/
+
+pred FindSuccessor[s: State] {
+ all n: s.active | let nd = n.(s.data) | all i: Id {
+ nd.find_successor[i] =
+ ((less_than_eq [n.id, i, nd.next.id] && n.id != i)
+ => nd.next
+ else
+ (nd.closest_preceding_finger[i].(s.data).find_successor)[i])
+ }
+}
+
+/*
+The only change here is in the fourth line
+\cite{sf-variation}, where we removed the clause \tt< || #
+s.active = 1>. For the same reason, the \tt loop
+in this case always proceeds to the \tt clause,
+and since \tt always returns
+\tt (the only node in the network), we end up
+with a tautological statement:
+
+\code
+ nd.find_successor[i] = n.s.data.find_successor)[i]
+
+This means that there is no additional constraint placed on
+\tt, other than that its return type is
+\tt. Now, if there is no distinction between active
+and inactive nodes, that is, we have exactly one active node
+in the network and no inactive ones, \tt
+will return the right answer due to the type constraint,
+therefore obscuring the bug. On the other hand, since we
+have introduced inactive nodes, the assertion
+\tt now fails with exactly one active
+node and some inactive node(s), with \tt
+returning an inactive node.
+*/
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/com.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/com.als
new file mode 100644
index 00000000..8cf6f08c
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/com.als
@@ -0,0 +1,96 @@
+module examples/case_studies/com
+
+/*
+ * Model of Microsoft Component Object Model (COM) query
+ * interface and aggregation mechanism.
+ *
+ * For a detailed description, see:
+ * http://sdg.lcs.mit.edu/~dnj/publications/com-fse00.pdf
+ *
+ * author: Daniel Jackson
+ */
+
+open util/relation as rel
+
+sig IID {}
+
+sig Interface {
+ qi : IID -> lone Interface,
+ iids : set IID,
+ // next two lines should use domain() or range() functions
+ iidsKnown : IID,
+ reaches : Interface
+}{
+ iidsKnown = dom[qi]
+ reaches = ran[qi]
+}
+
+sig Component {
+ interfaces : set Interface,
+ iids : set IID, // can't do iids = interfaces.Interface$iids
+ first, identity : interfaces,
+ eqs: set Component,
+ aggregates : set Component
+}
+
+fact defineEqs {
+ all c1, c2: Component |
+ c1->c2 in eqs <=> c1.identity = c2.identity
+}
+
+fact IdentityAxiom {
+ some unknown : IID | all c : Component |
+ all i : c.interfaces | unknown.(i.qi) = c.identity
+}
+
+fact ComponentProps {
+ all c : Component {
+ c.iids = c.interfaces.iids
+ all i : c.interfaces | all x : IID | x.(i.qi) in c.interfaces
+ }
+}
+
+sig LegalInterface extends Interface { }
+fact { all i : LegalInterface | all x : i.iidsKnown | x in x.(i.qi).iids}
+
+sig LegalComponent extends Component { }
+fact { LegalComponent.interfaces in LegalInterface }
+
+fact Reflexivity { all i : LegalInterface | i.iids in i.iidsKnown }
+fact Symmetry { all i, j : LegalInterface | j in i.reaches => i.iids in j.iidsKnown }
+fact Transitivity { all i, j : LegalInterface | j in i.reaches => j.iidsKnown in i.iidsKnown }
+
+fact Aggregation {
+ no c : Component | c in c.^aggregates
+ all outer : Component | all inner : outer.aggregates |
+ (some inner.interfaces & outer.interfaces)
+ && (some o: outer.interfaces | all i: inner.interfaces - inner.first | all x: Component | (x.iids).(i.qi) = (x.iids).(o.qi))
+ }
+
+assert Theorem1 {
+ all c: LegalComponent | all i: c.interfaces | i.iidsKnown = c.iids
+ }
+
+assert Theorem2 {
+ all outer: Component | all inner : outer.aggregates |
+ inner in LegalComponent => inner.iids in outer.iids
+ }
+
+assert Theorem3 {
+ all outer: Component | all inner : outer.aggregates | inner in outer.eqs
+ }
+
+assert Theorem4a {
+ all c1: Component, c2: LegalComponent |
+ some (c1.interfaces & c2.interfaces) => c2.iids in c1.iids
+ }
+
+assert Theorem4b {
+ all c1, c2: Component | some (c1.interfaces & c2.interfaces) => c1 in c2.eqs
+ }
+
+check Theorem1 for 3 expect 0
+check Theorem2 for 3 expect 0
+check Theorem3 for 3 expect 0
+check Theorem4a for 3 expect 0
+check Theorem4b for 3 expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/firewire.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/firewire.als
new file mode 100644
index 00000000..5de1afaf
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/firewire.als
@@ -0,0 +1,384 @@
+module examples/case_studies/firewire
+
+/*
+ * A model of leader election in the Firewire protocol.
+ *
+ * Adapted from:
+ * [DG+01] M.C.A. Devillers, W.O.D. GriAEoen, J.M.T Romijn, and F.W. Vaandrager.
+ * Verification of a leader election protocol -- formal methods applied to IEEE
+ * 1394. Technical Report CSI-R9728, Computing Science Institute, University of
+ * Nijmegen, December 1997. Also, Formal Methods in System Design, 2001.
+ *
+ * This model describes a leader election protocol used in Firewire, an IEEE
+ * standard for connecting consumer electronic devices. The model is a
+ * straightforward translation into Alloy of a model [DG+01] developed in Lynch's
+ * IO Automata which has been analyzed using PVS, a theorem prover, but which, as
+ * far as we know, has not been subjected to fully automatic analysis. We are able
+ * to express the key correctness property -- that exactly one leader is elected
+ * -- more directly, as a trace property rather than a refinement property, and
+ * can check it without the need for the 15 invariants used in the more
+ * traditional proof. And the analysis does not hardwire
+ * a particular topology, so would be tricky to do with a standard model checker.
+ *
+ * The network is assumed to consist of a collection of nodes connected by
+ * links. Each link between a pair of nodes is matched by a link in the other
+ * direction. Viewing a link and its dual as a single, undirected edge, the
+ * network as a whole is assumed to form a tree. The purpose of the algorithm is
+ * to construct such a tree; in the model, this is achieved by labelling some
+ * subset of the links as parent links (each pointing from a node to its parent),
+ * and by marking a single node as the root.
+ *
+ * The algorithm, described in detail elsewhere [DG+01], works briefly as
+ * follows. When a node detects that all of its incoming links (or all but one)
+ * has been marked as a parent link, it sends a message on each outgoing link,
+ * either an acknowledgment (indicating its willingness to act as parent), or a
+ * request (indicating its desire to be a child), according to whether the dual of
+ * the outgoing link has been marked or not. Leaf nodes (with only one incoming
+ * link) may thus initiate the algorithm by sending requests to their adjacent
+ * nodes. Performing this action changes a node's status from {\em waiting} to
+ * {\em active}. A node that is still waiting, and which receives a message on a
+ * link, may label that link a parent link. Once active, a node that receives an
+ * acknowledgment on a link may also label the link, but if it receives a request,
+ * instead changes its node status to {\em contending}. The resolving of
+ * contentions is modelled simplistically by a single action that arbitrarily
+ * labels one of the two links a pair of contending nodes. Finally, a node all of
+ * whose incoming links are parent links designates itself a root.
+ *
+ * The specification is given below. Each signature introduces a basic type
+ * and some relations whose first column has that type:
+ *
+ * \begin{itemize}
+ *
+ * \item {\em Msg} represents the type of messages. {\em Req} is the request
+ * message and {\em Ack} is the acknowledgment message; these are actually
+ * declared as singleton (keyword {\em static}) subsets of {\em Msg}, the set of
+ * all messages, that form a partition (keyword {\em part}).
+ *
+ * \item {\em Node} represents the nodes of the network. The relations {\em to}
+ * and {\em from} associate each node with a set of incoming and outgoing links
+ * respectively.
+ *
+ * \item {\em Link} represents the links. The relations {\em target} and {\em
+ * source} map a link to its end nodes; {\em reverse} maps a link to its dual. The
+ * facts in the signatures {\em Node} and {\em Link} ensure that all these
+ * relations are consistent with one another: that the incoming links of a node
+ * are those whose target is that node, etc.
+ *
+ * \item {\em Op} introduces a set of names for the operations of the
+ * protocol. This is merely for convenience; it allows us to ask for an execution
+ * in which named operations occur, or example.
+ *
+ * \item {\em State} represents the global states. Each state has a partition of
+ * the nodes into one of four statuses: {\em waiting} to participate, {\em active}
+ * (having sent messages on outgoing links), {\em contending} (having sent a
+ * request on a link and received a request on its dual), and {\em elected}
+ * (having designated itself as a root). A set of links are labelled as parent
+ * links. There is a message queue associated with each link. Finally, the state
+ * is associated with the operation that produced it.
+ *
+ * \item {\em Queue} represents the message queues. Each queue has a slot that
+ * optionally contains a message; the relation {\em slot} is a partial function
+ * from queues to messages. In our first attempt at a model, we represented a
+ * queue as a sequence (a partial function from a prefix of the integers to
+ * messages), but having checked that no queue ever contained more than one
+ * message, we simplified the model. The {\em overflow} field is included just in
+ * case this was a mistake; a write to a queue that already contains a message
+ * puts an arbitrary value there, which is easily detected.
+ *
+ * \end{itemize}
+ *
+ * The {\em facts} record the assumptions about the topology. The one named {\em
+ * Topology} says that there is some partial function on nodes and some root such
+ * that (1) every node is reachable from the root ({\tt *r} being the reflexive
+ * transitive closure of the relation {\tt r}); (2) there are no cycles (expressed
+ * by saying that the transitive closure has no intersection with the identity
+ * relation on nodes); and (3) the relation obtained by following the {\em source}
+ * relation backwards (from a node to the link for which it is a source), and then
+ * the {\em target} relation forwards (from the link to its target) is this
+ * relation, plus its transpose (so that each tree edge becomes two
+ * links). Although the quantifier appears to be higher-order, it will be
+ * skolemized away by the analyzer.
+ *
+ * The {\em functions} of the model are parameterized formulas. The function {\em
+ * Trans} relates a pre-state {\tt s} to a post-state {\tt s'}. It has a case for
+ * each operation. Look at the clause for the operation {\em WriteReqOrAck}, for
+ * example. If this operation is deemed to have occurred, each of the constraints
+ * in the curly braces must hold. The first says that the labelling of links as
+ * parent links is unchanged. The second constraint (the quantified formula)
+ * constrains with respect to the node at which the operation occurs. The
+ * subformulas, from first to last, say that the node belongs to the waiting set
+ * before and the active set afterwards; that there is at most one ({\em sole})
+ * link that is incoming but not a parent link in the pre-state; that there are no
+ * changes to node status except at this node; that a message is queued onto each
+ * outgoing link; and that queues on all other links are unchanged.
+ *
+ * An 'invoked' function is simply short for the formula in its body with the
+ * formal arguments replaced by the actual expressions. {\em WriteQueue}, for
+ * example, says that if the queue's slot is not filled in the pre-state, then the
+ * new queue in the post-state (given the local name {\tt q}) contains the message
+ * {\tt m} in its slot, and has no message in its overflow. Otherwise, some
+ * message is placed arbitrarily in the overflow, and the slot is
+ * unconstrained. In {\em WriteReqOrAck}, the arguments {\tt s} and {\tt s'} are
+ * bound to the {\tt s} and {\tt s'} of {\em Trans}; {\tt x} is bound to one of
+ * the outgoing links from the set {\tt n.from}; and {\tt msg} is bound either to
+ * the acknowledgment or request message.
+ *
+ * The function {\em Execution} constrains the set of states. It makes use of a
+ * library module that defines a polymorphic ordering relation. The expression
+ * {\tt Ord[State]} gives an ordering on all states. The two formulas of the
+ * function say that {\tt Initialization} holds in the first state, and that any
+ * pair of adjacent states is related by {\tt Trans}. The function {\em NoRepeats}
+ * adds the constraints that there are no equivalent states in the trace, and that
+ * no stuttering occurs.
+ *
+ * The three assertions are theorems for which the analyzer will search for
+ * counterexamples. They assert respectively that: in every state of the trace,
+ * there is at most one node that has been elected; that there is some state in
+ * which a node has been elected; and that no queue overflows.
+ *
+ * The rest of the model is a collection of commands executed to find instances of
+ * the functions or counterexamples to the theorems. We started by presenting a
+ * variety of functions as a sanity check; here, only one is given, that asks for
+ * an execution involving 2 nodes, 4 links, 4 queues and a trace of 6 states. The
+ * standard semantics of these {\em scope} declarations in Alloy is that the
+ * numbers represent an upper bound, so an instance may involve fewer than 4
+ * queues, for example. The ordering module (not shown here), however, for
+ * technical reasons, constrains the ordered set to match its scope, so a trace
+ * with fewer than 6 states will not be acceptable.
+ *
+ * We then established some bounds on the diameter of the state machine for
+ * various topology bounds. For 2 nodes and 2 links, for example, there are no
+ * non-repeating traces of length 4; checking traces of length 3 is thus
+ * sufficient in this case. The number of queues was limited to 5, to accommodate
+ * the empty queue, a queue containing an {\tt Ack} or {\tt Req}, and each of
+ * these with overflow. For 3 nodes and 6 links, a trace length of 8 suffices.
+ *
+ * We then checked that for these various topology bounds, the queues never
+ * overflow. Finally, we checked the correctness properties, taken advantage of
+ * the earlier results that justify the short traces and queues. We are thus able
+ * to verify the properties for all topologies involving the given number of nodes
+ * and links, without any assumptions about trace length, queue size or the
+ * particular topological structure.
+ *
+ * author: Daniel Jackson
+ * visualization: Robert Seater
+ */
+
+open util/ordering[State] as ord
+
+abstract sig Msg {}
+one sig Req, Ack extends Msg {}
+
+sig Node {to, from: set Link} {
+ to = {x: Link | x.target = this}
+ from = {x: Link | x.source = this}
+ }
+
+sig Link {target, source: Node, reverse: Link} {
+ reverse.@source = target
+ reverse.@target = source
+ }
+
+/**
+ * at most one link between a pair of nodes in a given direction
+ */
+fact {no x,y: Link | x!=y && x.source = y.source && x.target = y.target}
+
+/**
+ * topology is tree-like: acyclic when viewed as an undirected graph
+ */
+fact Topology {
+some tree: Node lone -> Node, root: Node {
+ Node in root.*tree
+ no ^tree & iden & Node->Node
+ tree + ~tree = ~source.target
+ }
+}
+
+sig Op {}
+one sig Init, AssignParent, ReadReqOrAck, Elect, WriteReqOrAck,
+ResolveContention, Stutter extends Op {}
+
+sig State {
+ disj waiting, active, contending, elected: set Node,
+ parentLinks: set Link,
+ queue: Link -> one Queue,
+ op: Op -- the operation that produced the state
+ } {
+ waiting + active + contending + elected = Node
+}
+
+pred SameState [s, s': State] {
+ s.waiting = s'.waiting
+ s.active = s'.active
+ s.contending = s'.contending
+ s.elected = s'.elected
+ s.parentLinks = s'.parentLinks
+ all x: Link | SameQueue [s.queue[x], s'.queue[x]]
+ }
+
+pred Trans [s, s': State] {
+ s'.op != Init
+ s'.op = Stutter => SameState [s, s']
+ s'.op = AssignParent => {
+ some x: Link {
+ x.target in s.waiting & s'.waiting
+ NoChangeExceptAt [s, s', x.target]
+ ! IsEmptyQueue [s, x]
+ s'.parentLinks = s.parentLinks + x
+ ReadQueue [s, s', x]
+ }}
+ s'.op = ReadReqOrAck => {
+ s'.parentLinks = s.parentLinks
+ some x: Link {
+ x.target in s.(active + contending) & (PeekQueue [s, x, Ack] => s'.contending else s'.active)
+ NoChangeExceptAt [s, s', x.target]
+ ! IsEmptyQueue [s, x]
+ ReadQueue [s', s, x]
+ }}
+ s'.op = Elect => {
+ s'.parentLinks = s.parentLinks
+ some n: Node {
+ n in s.active & s'.elected
+ NoChangeExceptAt [s, s', n]
+ n.to in s.parentLinks
+ QueuesUnchanged [s, s', Link]
+ }}
+ s'.op = WriteReqOrAck => {
+ -- note how this requires access to child ptr
+ s'.parentLinks = s.parentLinks
+ some n: Node {
+ n in s.waiting & s'.active
+ lone n.to - s.parentLinks
+ NoChangeExceptAt [s, s', n]
+ all x: n.from |
+ let msg = (x.reverse in s.parentLinks => Ack else Req) |
+ WriteQueue [s, s', x, msg]
+ QueuesUnchanged [s, s', Link - n.from]
+ }}
+ s'.op = ResolveContention => {
+ some x: Link {
+ let contenders = x.(source + target) {
+ contenders in s.contending & s'.active
+ NoChangeExceptAt [s, s', contenders]
+ }
+ s'.parentLinks = s.parentLinks + x
+ }
+ QueuesUnchanged [s, s', Link]
+ }
+}
+
+pred NoChangeExceptAt [s, s': State, nodes: set Node] {
+ let ns = Node - nodes {
+ ns & s.waiting = ns & s'.waiting
+ ns & s.active = ns & s'.active
+ ns & s.contending = ns & s'.contending
+ ns & s.elected = ns & s'.elected
+ }}
+
+sig Queue {slot: lone Msg, overflow: lone Msg}
+
+pred SameQueue [q, q': Queue] {
+ q.slot = q'.slot && q.overflow = q'.overflow
+ }
+
+pred ReadQueue [s, s': State, x: Link] {
+-- let q = s'.queue[x] | no q.(slot + overflow)
+ no s'.queue[x].(slot + overflow)
+ all x': Link - x | s'.queue[x'] = s.queue[x']
+ }
+
+pred PeekQueue [s: State, x: Link, m: Msg] {
+ m = s.queue[x].slot
+ }
+
+pred WriteQueue [s, s': State, x: Link, m: Msg] {
+ let q = s'.queue[x] |
+ no s.queue[x].slot =>
+ ( q.slot = m && no q.overflow) else
+ some q.overflow
+ }
+
+pred QueuesUnchanged [s, s': State, xs: set Link] {
+ all x: xs | s'.queue[x] = s.queue[x]
+ }
+
+pred IsEmptyQueue [s: State, x: Link] {
+ no s.queue[x].(slot + overflow)
+-- let q = s.queue[x] | no q.(slot + overflow)
+ }
+
+pred Initialization [s: State] {
+ s.op = Init
+ Node in s.waiting
+ no s.parentLinks
+ all x: Link | IsEmptyQueue [s, x]
+ }
+
+pred Execution {
+ Initialization [ord/first]
+ all s: State - ord/last | let s' = ord/next[s] | Trans [s, s']
+ }
+
+pred ElectionHappens {
+ Execution
+ some s: State | some s.elected
+ some s: State | no s.elected
+}
+
+pred NoRepeats {
+ Execution
+ no s, s': State | s!=s' && SameState [s, s']
+ no s: State | s.op = Stutter
+ }
+
+pred NoShortCuts {
+ all s: State | -- remove this to speed up analysis - Ord[State].last - OrdPrev (Ord[State].last) |
+ ! Trans [s, ord/next[ord/next[s]]]
+ }
+
+assert AtMostOneElected {
+ Execution => (all s: State | lone s.elected)
+ }
+
+assert OneEventuallyElected {
+ Execution => (some s: State | some s.elected)
+ }
+
+assert NoOverflow {
+ Execution => (all s: State, x: Link | no s.queue[x].overflow)
+ }
+
+run Execution for 7 Op, 2 Msg,
+ 2 Node, 4 Link, 4 Queue, 6 State expect 1
+
+run ElectionHappens for 7 Op, 2 Msg,
+ exactly 3 Node, 6 Link, 3 Queue, 7 State expect 1
+
+-- solution for 3 State but not for 4 State
+run NoRepeats for 7 Op, 2 Msg,
+ 2 Node, 2 Link, 2 Queue, 4 State expect 0
+
+-- solution for 8 but not 9 State
+run NoRepeats for 7 Op, 2 Msg,
+ 3 Node, 6 Link, 6 Queue, 8 State expect 0
+
+-- only 5 queues needed: just count
+-- no solution: establishes at most 3 queues needed
+check NoOverflow for 7 Op, 2 Msg,
+ 3 Node, 6 Link, 5 Queue, 9 State expect 0
+
+check AtMostOneElected for 7 Op, 2 Msg,
+ 3 Node, 6 Link, 3 Queue, 9 State expect 0
+
+check OneEventuallyElected for 7 Op, 2 Msg,
+ 3 Node, 6 Link, 3 Queue, 9 State expect 1
+
+
+
+// DEFINED VARIABLES
+// Defined variables are uncalled, no-argument functions.
+// They are helpful for getting good visualization.
+fun queued: State -> Link -> Msg {
+ {s: State, L: Link, m: Msg | m in L.(s.queue).slot}
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/firewire.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/firewire.thm
new file mode 100644
index 00000000..04793a40
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/firewire.thm
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/ins.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/ins.als
new file mode 100644
index 00000000..e5596229
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/ins.als
@@ -0,0 +1,115 @@
+module examples/case_studies/ins
+
+/*
+ * Models an Intentional Naming System (INS), a scheme for
+ * dynamic resource discovery in a dynamic environment.
+ *
+ * For a detailed description, see:
+ * http://sdg.lcs.mit.edu/pubs/2000/INS_ASE00.pdf
+ *
+ * author: Sarfraz Khurshid
+ */
+
+open util/relation as rel
+
+sig Attribute {}
+sig Value {}
+sig Record {}
+
+one sig Wildcard extends Value {}
+
+sig AVTree {
+ values: set Value,
+ attributes: set Attribute,
+ root: values - Wildcard,
+ av: attributes one -> some (values - root),
+ va: (values - Wildcard) one -> attributes
+}{
+ // values (and attributes) of tree are those reachable from root
+ values = root.*(va.av)
+}
+
+sig Query extends AVTree {} {all a:attributes | one a.av}
+
+sig DB extends AVTree {
+ records : set Record,
+ recs: (values - root) some -> records,
+ lookup : Query -> (values -> records)
+}{
+ Wildcard !in values
+}
+
+fact AddInvariants {
+ all db: DB {
+ all v: db.values | no v.(db.recs) & v.^(~(db.av).~(db.va)).(db.recs)
+ all a: db.attributes | all disj v1, v2: a.(db.av) |
+ (some rr: *((db.va).(db.av)).(db.recs) | no v1.rr & v2.rr)
+ }
+}
+
+pred Get [db: DB, r: Record, q: Query] {
+ q.values = r.~(db.recs).*(~(db.av).~(db.va))
+ q.attributes = q.values.~(db.av)
+ q.root = db.root
+ all a : attributes| a.~(q.va) = a.~(db.va)
+ all v : values | v.~(q.av) = v.~(db.av)
+}
+
+pred Conforms [db: DB, q: Query, r: Record] {
+ some p: Query {
+ db.Get[r, p]
+ q.va in p.va
+ (q.av - Attribute -> Wildcard) in p.av
+ }
+}
+
+pred indSubset[db : DB, q: Query, r: set Record, v: Value] {
+ all a : v.(q.va) |
+ (a.(q.av) in a.(db.av) => r in (a.(q.av)).(q.(db.lookup))) &&
+ (a.(q.av) = Wildcard => r in a.(db.av).*((db.va).(db.av)).(db.recs))
+}
+
+pred Lookup[db: DB, q: Query, found: set Record] {
+ all v: Value | not v.(q.va) in v.(db.va) => no v.(q.(db.lookup))
+ all v: Value | all a : v.(q.va) |
+ a.(q.av) != Wildcard && not a.(q.av) in a.(db.av) => no v.(q.(db.lookup))
+ all v: Value - Wildcard |
+ no v.(q.va) => v.(q.(db.lookup)) = v.*((db.va).(db.av)).(db.recs)
+ all v: Value |
+ some v.(q.va) => indSubset[db, q, v.(q.(db.lookup)), v] &&
+ (no r: Record - v.(q.(db.lookup)) | indSubset[db, q, v.(q.(db.lookup)) + r, v])
+ found = db.root.(q.(db.lookup))
+}
+
+assert CorrectLookup {
+ all db: DB | all q : Query | all r : Record |
+ Conforms [db,q,r] <=> db.Lookup[q, r]
+}
+
+pred Add [me: DB, adv: Query, r: Record, db: DB] {
+ // restricted version - only advertisements with fresh attributes and values added
+ no me.attributes & adv.attributes
+ me.values & adv.values = me.root
+ me.root = adv.root
+ Wildcard !in adv.values
+ r !in me.records
+ db.values = me.values + adv.values
+ db.attributes = me.attributes + adv.attributes
+ db.root = me.root
+ db.av = me.av + adv.av
+ db.va = me.va + adv.va
+ db.recs = me.recs + ((db.values - dom[db.va]) -> r)
+}
+
+pred RemoveWildCard[me: Query, q: Query] {
+ q.values = me.values - Wildcard
+ q.attributes = me.attributes - Wildcard.~(me.av)
+ q.root = me.root
+ q.av = me.av - Attribute -> Wildcard
+ q.va = me.va - Value -> Wildcard.~(me.av)
+}
+
+assert MissingAttributeAsWildcard {
+ all db : DB, q, q' : Query, found: set Record |
+ db.Lookup[q, found] && q.RemoveWildCard[q'] => db.Lookup[q', found]
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/iolus.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/iolus.als
new file mode 100644
index 00000000..cc27ba39
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/iolus.als
@@ -0,0 +1,320 @@
+module examples/case_studies/iolus
+
+/*
+ * This is a model of Iolus, a scheme for secure multicasting.
+ * In this scheme, nodes multicast messages to other nodes
+ * within a group whose membership changes dynamically. The
+ * group is partitioned into subgroups, arranged in a tree,
+ * each with its own Key Distribution Server (KDS).
+ *
+ * For a detailed description, see:
+ * Mana Taghdiri, "Lightweight Modelling and Automatic Analysis
+ * of Multicast Key Management Schemes", Masters Thesis, Dept.
+ * of EECS, MIT, Dec. 2002.
+ * http://sdg.lcs.mit.edu/pubs/theses/taghdiri_masters.pdf
+ *
+ * author: Mana Taghdiri
+ */
+
+open util/ordering[Tick] as ord
+
+sig Tick {}
+
+/**
+ * It can be abstract, since the fact below says Key=GroupKey
+ */
+abstract sig Key {}
+
+/**
+ * It can be abstract, since the fact below says Message=DataMessage
+ */
+abstract sig Message {
+ sender : Member,
+ sentTime : Tick,
+ key : Key
+}
+
+/**
+ * It can be abstract, since the fact below says KDS=GSA
+ */
+abstract sig KDS {
+ keys : Tick -> Key,
+ members : Tick -> Member
+}{
+ Monotonic[keys]
+ all t : Tick | let t' = ord/prev[t] {
+ all m : members[t]-members[t'] | Join[m, t, this]
+ all m : members[t']-members[t] | Leave[m, t]
+ }
+}
+
+/**
+ * It can be abstract, since the fact below says "Member=Client"
+ */
+abstract sig Member {
+ ownedKeys : Tick -> Key,
+ receivedMessages : Tick -> Message
+}{
+ Monotonic[ownedKeys]
+ Monotonic[receivedMessages]
+}
+
+fact MemberBehavior {
+ Init[ord/first]
+ all m : Member, t : Tick - ord/first |
+ (some msg : Message |
+ SendMessage[m, t, msg] || ReceiveMessage[m, t, msg]) ||
+ (some kds : KDS | Join[m, t, kds]) ||
+ Leave[m, t] || MemberInactive[m, t]
+}
+
+pred Monotonic[r : Tick -> univ] {
+ all t : Tick | ord/prev[t].r in t.r
+}
+
+----------------------------------------------
+sig GroupKey extends Key {
+ generator : GSA,
+ generatedTime : Tick
+}{
+ some c : Client |
+ (Join[c, generatedTime, c.server] || Leave[c, generatedTime]) &&
+ c.server = generator
+}
+
+sig DataMessage extends Message {
+ gsaID : GSA,
+ retransmitTime : Tick }
+{ SendMessage[sender, sentTime, this] ||
+ (some msg' : DataMessage |
+ Remulticast[gsaID, msg', retransmitTime, this]) }
+
+sig GSA extends KDS {
+ parent : lone GSA }
+{ keys[Tick].generator = this
+ all t : Tick, k : keys[t] - keys[ord/prev[t]] |
+ k.generatedTime = t }
+
+sig Client extends Member {
+ server : GSA }
+{ all t : Tick, k : ownedKeys[t] - ownedKeys[ord/prev[t]] |
+ k.generator = server && k.generatedTime = t }
+
+fact IolusProperties {
+ no k, k' : GroupKey | k!=k' && k.generator = k'.generator && k.generatedTime = k'.generatedTime
+ all g : GSA, msg : DataMessage, t : Tick | RemulticastConditions[g, msg, t] =>
+ (some msg': DataMessage | Remulticast[g, msg, t, msg'])
+}
+
+fact GSATree {
+ let root = {g : GSA | no g.parent} {
+ one root
+ GSA in root.*~parent }}
+
+fact {
+ Member = Client
+ KDS = GSA
+ Message = DataMessage
+ Key = GroupKey
+ no m, m' : DataMessage {
+ m!=m'
+ m.sender = m'.sender
+ m.sentTime = m'.sentTime
+ m.key = m'.key
+ m.gsaID = m'.gsaID
+ m.retransmitTime = m'.retransmitTime }
+}
+
+----------------------------------------------
+pred Init[t : Tick] {
+ no Member.receivedMessages[t]
+ no Member.ownedKeys[t]
+ no KDS.keys[t]
+ no KDS.members[t] }
+
+pred Join[m : Member, t : Tick, kds : KDS] {
+ kds = m.server
+ JoinRequest[m, kds, t]
+ NoChange[m.receivedMessages, t]
+}
+pred JoinRequest[c : Client, gsa : GSA, t : Tick] {
+ c !in gsa.members[ord/prev[t]]
+ KeyUpdate[gsa, t]
+ c in gsa.members[t] }
+
+pred Leave[m : Member, t : Tick] {
+ LeaveRequest[m, m.server, t]
+ NoChange[m.receivedMessages, t] }
+
+pred LeaveRequest[c : Client, gsa : GSA, t : Tick] {
+ c in gsa.members[ord/prev[t]]
+ KeyUpdate[gsa, t]
+ c !in gsa.members[t] }
+
+pred SendMessage[m : Member, t : Tick, msg : Message] {
+ SendRequest[m, m.server, t, msg]
+ m.receivedMessages[t] = m.receivedMessages[ord/prev[t]] + msg
+ ConstantMembership[m, t] }
+
+pred SendRequest[c : Client, gsa : GSA, t : Tick, msg : DataMessage] {
+ c in gsa.members[t]
+ msg.sender = c
+ msg.sentTime = t
+ NewestKey[gsa.keys[t], msg.key]
+ msg.gsaID = gsa
+ msg.retransmitTime = t
+ (some gsa.parent.members[t]) =>
+ (some msg' : DataMessage | Remulticast[gsa, msg, t, msg']) }
+
+pred ReceiveMessage[m : Member, t : Tick, msg : Message] {
+ ReceiveConditions[m, t, msg]
+ m.receivedMessages[t] = m.receivedMessages[ord/prev[t]] + msg }
+
+pred MemberInactive[m : Member, t : Tick] {
+ NoChange[m.receivedMessages, t] --does not constrain owned keys
+ ConstantMembership[m, t] }
+
+pred ReceiveConditions[m : Member, t : Tick, msg : Message] {
+ ConstantMembership[m, t]
+ msg !in m.receivedMessages[ord/prev[t]]
+ msg.retransmitTime in ord/prevs[t]
+ msg.key in m.ownedKeys[t] }
+
+pred CanReceive[m : Member, t : Tick, msg : Message] {
+ some msg' : DataMessage {
+ msg'.sentTime = msg.sentTime
+ msg'.sender = msg.sender
+ msg' in m.receivedMessages[ord/prev[t]] || ReceiveConditions[m, t, msg'] }}
+
+pred IsMember[m : Member, t : Tick] {
+ some kds : KDS | m in kds.members[t]
+}
+
+-------------------------------------------
+pred RemulticastConditions[g : GSA, msg : DataMessage, t : Tick] {
+ msg.retransmitTime in ord/prevs[t]
+ msg.key in g.keys[t] + g.parent.keys[t]
+ some g.parent + g - msg.gsaID }
+
+pred Remulticast[g : GSA, msg : DataMessage, t : Tick, msg': lone DataMessage] {
+ RemulticastConditions[g, msg, t]
+ let g' = g.parent + g - msg.gsaID | NewestKey[g'.keys[msg.sentTime], msg'.key]
+ msg'.sender = msg.sender
+ msg'.sentTime = msg.sentTime
+ msg'.retransmitTime = t
+ msg'.gsaID = g
+}
+
+pred KeyUpdate[g : GSA, t : Tick] {
+ some k : Key {
+ GeneratedKey[g, t, k]
+ all c : Client | c in g.members[t] <=> k in c.ownedKeys[t]
+ k in g.keys[t] }}
+
+pred NewestKey[keys : set GroupKey, newest: lone GroupKey] {
+ some keys <=> some newest
+ newest in keys
+ no ord/nexts[newest.generatedTime] & keys.generatedTime }
+
+pred GeneratedKey[g : GSA, t : Tick, key : GroupKey] {
+ key.generator = g
+ key.generatedTime = t
+}
+
+pred ConstantMembership[c : Client, t : Tick] {
+ IsMember[c, t] <=> IsMember[c, ord/prev[t]] }
+
+
+pred NoChange[r : Tick -> univ, t : Tick] {
+ r[ord/prev[t]] = r[t]
+}
+
+--------------------------------------------
+assert Acyclic {
+ all g : GSA | g !in g.^parent }
+
+//check Acyclic for 6 -- one min
+
+assert Connected {
+ all g, g' : GSA | g in g'.*(parent + ~parent) }
+
+//check Connected for 6
+
+assert TimeProceeds {
+ no msg : DataMessage | msg.retransmitTime in ord/prevs[msg.sentTime] }
+
+//check TimeProceeds for 6
+
+pred LoopFree {
+ no t, t' : Tick {
+ t!=t'
+ all k : KDS | k.members[t] = k.members[t'] -- no constraint on keys
+ all m : Member | m.receivedMessages[t] = m.receivedMessages[t']
+ all m : DataMessage | m.retransmitTime = t =>
+ (some m' : DataMessage {
+ m'.retransmitTime = t'
+ m.sender = m'.sender
+ m.sentTime = m'.sentTime
+ m.gsaID = m'.gsaID
+ m.key = m'.key })
+ }}
+
+//fact NoLoop { LoopFree() } -- Property-specific diameter
+
+------------------------------------------------
+assert loop {
+ !LoopFree }
+//check loop for 13 but 2 Member, 1 KDS, 1 Message
+
+assert NonLinearTopology {
+ (no g : GSA | #g.~parent > 1) ||
+ !(some m, m' : DataMessage | Remulticast[m.gsaID, m', m.retransmitTime, m]
+ && !SendMessage[m.sender, m.sentTime, m]
+ && (some c : Client | m in c.receivedMessages[ord/nexts[m.retransmitTime]]))}
+
+//check NonLinearTopology for 5 but 3 KDS, 3 Member, 2 Message -- > good scenario
+
+assert NotOutside {
+ no msg : DataMessage | !IsMember[msg.sender, msg.sentTime] }
+
+//check NotOutside for 5
+
+assert Trivial {
+ 0 = 1 }
+
+//check Trivial for 2 but 1 KDS
+
+assert x {
+ !(LoopFree && some DataMessage &&
+ (some t : Tick | some m, m' : Member |
+ m!=m' && IsMember[m, t] && IsMember[m', t] && t != ord/next[ord/first]))
+}
+
+//check x for 3 but 2 Member, 2 KDS, 2 Message
+ -------------------------------------------
+assert OutsiderCantRead {
+ no msg : Message, m : Member, t : Tick {
+ IsMember[msg.sender, msg.sentTime]
+ !IsMember[m, msg.sentTime]
+ CanReceive[m, t, msg]
+ }
+}
+assert OutsiderCantSend {
+ no msg : Message, m : Member, t : Tick {
+ !IsMember[msg.sender, msg.sentTime]
+ IsMember[m, t]
+ msg !in m.receivedMessages[ord/prev[t]]
+ CanReceive[m, t, msg]
+ }
+}
+assert InsiderCanRead {
+ all msg : Message, m : Member |
+ some t : Tick - ord/last | all t' : ord/nexts[t] |
+ (IsMember[msg.sender, msg.sentTime] &&
+ IsMember[m, msg.sentTime]) => CanReceive[m, t', msg]
+}
+
+check OutsiderCantRead for 5 but 3 Member expect 0
+//check OutsiderCantSend for 5 but 3 Member -- 5 min
+//check InsiderCanRead for 9 but 2 Member, 1 KDS, 1 Message
+//check InsiderCanRead for 10 but 2 Member, 2 KDS, 2 Message -- not able to check
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/sync.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/sync.als
new file mode 100644
index 00000000..b79c5b5d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/sync.als
@@ -0,0 +1,134 @@
+module examples/case_studies/sync
+
+/*
+ * Model of a generic file synchronizer.
+ *
+ * Adapted from:
+ * Reference: S. Balasubramaniam and Benjamin C. Pierce,
+ * "What is a File Synchronizer", Indiana University CSCI
+ * Technical Report #507, April 22, 1998
+ *
+ * For a detailed description, see:
+ * http://sdg.lcs.mit.edu/pubs/theses/tnolte_masters.pdf
+ *
+ * author: Tina Nolte
+ */
+
+private open util/graph[Name] as graph
+
+/**
+ * The Name atom represents the hierarchy of all name sequences
+ * used in the model. A Name atom represents the name, and the path
+ * in the sequence of names to the root excluding the RootName.
+ */
+sig Name {
+ children: set Name
+}
+
+fact { graph/tree[children] }
+
+one sig RootName extends Name { }
+
+fact { Name in RootName.*children }
+
+// We assume that the empty path always
+
+sig FileContents { }
+one sig Dir extends FileContents { }
+
+pred IsValidFS[fs: Name -> lone FileContents] {
+ all n: Name - RootName | {
+ // files don't have children
+ n.fs != Dir => no (n.^children).fs
+ // if a path maps to something non-nil, all prefixes do also
+ some n.fs => some (n.~children).fs
+ }
+ // the root of a file system must be a directory
+ RootName.fs = Dir
+}
+
+pred IsValidDirty[dirty: set Name] {
+ all n: dirty | n.~children in dirty
+ RootName in dirty => some dirty - RootName
+}
+
+pred DirtiesValid[A, B: Name -> lone FileContents, Adirty, Bdirty: set Name] {
+ some O: Name -> lone FileContents | {
+ IsValidFS[O]
+ { n: Name | n.O != n.A } in Adirty
+ { n: Name | n.O != n.B } in Bdirty
+ }
+}
+
+fun RestrictFS[fs: Name -> lone FileContents, p: Name]: Name -> lone FileContents {
+ fs & (p.*children -> FileContents)
+}
+
+fun RestrictFSComplement[fs: Name -> lone FileContents, p: Name]: Name -> lone FileContents {
+ fs & ((Name - p.*children) -> FileContents)
+}
+
+/**
+ * The following function test whether a particular synchronizer
+ * operation satisfies the "reasonableness" conditions.
+ * The arguments are:
+ * O - the original filesystem.
+ * A,B - separately modified copies
+ * Adirty, Bdirty - sets of paths modified in A and B, respectively, from O.
+ *
+ * A',B' - results of synchronizer operation
+ */
+pred SyncSpec[A, B, A', B': Name -> lone FileContents, Adirty, Bdirty: set Name] {
+ {
+ IsValidFS[A]
+ IsValidFS[B]
+ IsValidDirty[Adirty]
+ IsValidDirty[Bdirty]
+ } => {
+ {
+ all p: Name | IsRelevantPath[p, A, B] => {
+ SyncSpecForPath[p, A, B, A', B', Adirty, Bdirty]
+ }
+ }
+ IsValidFS[A']
+ IsValidFS[B']
+ }
+}
+
+pred SyncSpecForPath[p: Name, A, B, A', B': Name -> lone FileContents, Adirty, Bdirty: set Name] {
+ (p.A = p.B => (p.A' = p.A && p.B' = p.B))
+ (p !in Adirty => (RestrictFS[A', p] = RestrictFS[B, p] && RestrictFS[B', p] = RestrictFS[B, p]))
+ (p !in Bdirty => (RestrictFS[B', p] = RestrictFS[A, p] && RestrictFS[A', p] = RestrictFS[A, p]))
+ ((p in Adirty && p in Bdirty && p.A != p.B) => (RestrictFS[A',p] = RestrictFS[A,p] && RestrictFS[B',p] = RestrictFS[B,p]))
+}
+
+pred IsRelevantPath[p: Name, A, B: Name -> lone FileContents] {
+ p = RootName || {
+ (p.~children).A = Dir
+ (p.~children).B = Dir
+ }
+}
+
+pred SyncSpecExample[A, B, A', B': Name -> lone FileContents, Adirty, Bdirty: set Name] {
+ IsValidFS[A]
+ IsValidFS[B]
+ IsValidDirty[Adirty]
+ IsValidDirty[Bdirty]
+ SyncSpec[A, B, A', B', Adirty, Bdirty]
+ A != A'
+}
+
+//run SyncSpecExample for 3
+
+pred SyncSpecNotUnique {
+ some A, B, A1', B1', A2', B2': Name -> lone FileContents, Adirty, Bdirty: set Name | {
+ IsValidFS[A] && IsValidFS[B]
+ IsValidDirty[Adirty] && IsValidDirty[Bdirty]
+ //DirtiesValid(A, B, Adirty, Bdirty)
+ (A1' != A2' || B1' != B2')
+ SyncSpec[A, B, A1', B1', Adirty, Bdirty]
+ SyncSpec[A, B, A2', B2', Adirty, Bdirty]
+ }
+}
+
+run SyncSpecNotUnique for 5 expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/syncimpl.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/syncimpl.als
new file mode 100644
index 00000000..fe20377e
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/case_studies/syncimpl.als
@@ -0,0 +1,107 @@
+module examples/case_studies/syncimpl
+
+/*
+ * Model of a file synchronizer reconciliation algorithm.
+ *
+ * Adapted from:
+ * Reference: S. Balasubramaniam and Benjamin C. Pierce,
+ * "What is a File Synchronizer", Indiana University CSCI
+ * Technical Report #507, April 22, 1998
+ *
+ * For a detailed description, see:
+ * http://sdg.lcs.mit.edu/pubs/theses/tnolte_masters.pdf
+ *
+ * author: Tina Nolte
+ */
+
+open examples/case_studies/sync as sync
+open util/ordering[sync/Name] as ord
+open util/relation as rel
+
+// Model the reconciliation algorithm
+
+sig ReconName extends Name {
+ Ain, Bin, Aout, Bout: Name->FileContents,
+ p_children: set Name,
+ first_p_child, last_p_child: lone Name,
+ prev_p_child: (p_children - first_p_child) -> p_children
+}
+
+fact {
+ all x: ReconName {
+ x.p_children = ChildrenAB[x.Ain, x.Bin, x]
+ x.first_p_child = { pc: x.p_children | x.p_children in (pc + nexts[pc]) }
+ x.last_p_child = { pc: x.p_children | x.p_children in (pc + prevs[pc]) }
+ all p_child: x.p_children - x.first_p_child | {
+ let earlierChildren = prevs[p_child] & x.p_children |
+ p_child . (x.prev_p_child) = { earlierChild: earlierChildren | earlierChildren in (earlierChild + @prevs[earlierChild]) }
+ }
+ }
+}
+
+fact { ReconName = Name }
+
+fun ChildrenAB[A, B: Name -> lone FileContents, p: Name]: set Name {
+ p.children & (dom[A] + dom[B])
+}
+
+pred reconHelper[Adirty, Bdirty: set Name] {
+ all p: Name {
+ let A = p.Ain, B = p.Bin, A' = p.Aout, B' = p.Bout | {
+ some p.(A+B) => {
+ (p !in Adirty && p !in Bdirty) => (A' = A && B' = B) else {
+ (p.A = Dir && p.B = Dir) => {
+ no p_children => {
+ p.Aout = p.Ain
+ p.Bout = p.Bin
+ } else {
+ p.first_p_child.Ain = p.Ain
+ p.first_p_child.Bin = p.Bin
+ p.Aout = p.last_p_child.Aout
+ p.Bout = p.last_p_child.Bout
+ all pchild: p.p_children - p.first_p_child | {
+ pchild.Ain = (pchild.(p.prev_p_child)).Aout
+ pchild.Bin = (pchild.(p.prev_p_child)).Bout
+ }
+ } // some p_children
+ } else { // !(p.A = Dir && p.B = Dir)
+ p !in Adirty => {
+ A' = RestrictFS[B, p] + RestrictFSComplement[A, p]
+ B' = B
+ } else {
+ p !in Bdirty => {
+ A' = A
+ B' = RestrictFS[A, p] + RestrictFSComplement[B, p]
+ } else {
+ A' = A
+ B' = B
+ }
+ } // not "p !in Adirty"
+ } // not case 2 i.e. not both are dirs
+ } // not both clean
+ } // some p.(A+B)
+ } // let A =, B=, A'=, B'=
+ } // all p: Name
+} // reconHelper()
+
+pred recon[A, B, A', B': Name -> lone FileContents, Adirty, Bdirty: set Name] {
+ A = ReconName.Ain
+ B = ReconName.Bin
+ A' = ReconName.Aout
+ B' = ReconName.Bout
+ reconHelper[Adirty, Bdirty]
+}
+
+assert Correctness {
+ all A, B, A', B': Name -> lone FileContents, Adirty, Bdirty: set Name | {
+ {
+ DirtiesValid[A, B, Adirty, Bdirty]
+ recon[A, B, A', B', Adirty, Bdirty]
+ //no Adirty + Bdirty
+ }
+ =>
+ SyncSpec[A, B, A', B', Adirty, Bdirty]
+ }
+}
+
+check Correctness for 4 but 2 FileContents expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/farmer.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/farmer.als
new file mode 100644
index 00000000..d3636486
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/farmer.als
@@ -0,0 +1,92 @@
+module examples/puzzles/farmer
+
+/*
+ * The classic river crossing puzzle. A farmer is carrying a fox, a
+ * chicken, and a sack of grain. He must cross a river using a boat
+ * that can only hold the farmer and at most one other thing. If the
+ * farmer leaves the fox alone with the chicken, the fox will eat the
+ * chicken; and if he leaves the chicken alone with the grain, the
+ * chicken will eat the grain. How can the farmer bring everything
+ * to the far side of the river intact?
+ *
+ * authors: Greg Dennis, Rob Seater
+ *
+ * Acknowledgements to Derek Rayside and his students for finding and
+ * fixing a bug in the "crossRiver" predicate.
+ */
+
+open util/ordering[State] as ord
+
+/**
+ * The farmer and all his possessions will be represented as Objects.
+ * Some objects eat other objects when the Farmer's not around.
+ */
+abstract sig Object { eats: set Object }
+one sig Farmer, Fox, Chicken, Grain extends Object {}
+
+/**
+ * Define what eats what when the Farmer' not around.
+ * Fox eats the chicken and the chicken eats the grain.
+ */
+fact eating { eats = Fox->Chicken + Chicken->Grain }
+
+/**
+ * The near and far relations contain the objects held on each
+ * side of the river in a given state, respectively.
+ */
+sig State {
+ near: set Object,
+ far: set Object
+}
+
+/**
+ * In the initial state, all objects are on the near side.
+ */
+fact initialState {
+ let s0 = ord/first |
+ s0.near = Object && no s0.far
+}
+
+/**
+ * Constrains at most one item to move from 'from' to 'to'.
+ * Also constrains which objects get eaten.
+ */
+pred crossRiver [from, from', to, to': set Object] {
+ // either the Farmer takes no items
+ (from' = from - Farmer - from'.eats and
+ to' = to + Farmer) or
+ // or the Farmer takes one item
+ (one x : from - Farmer | {
+ from' = from - Farmer - x - from'.eats
+ to' = to + Farmer + x })
+}
+
+/**
+ * crossRiver transitions between states
+ */
+fact stateTransition {
+ all s: State, s': ord/next[s] {
+ Farmer in s.near =>
+ crossRiver[s.near, s'.near, s.far, s'.far] else
+ crossRiver[s.far, s'.far, s.near, s'.near]
+ }
+}
+
+/**
+ * the farmer moves everything to the far side of the river.
+ */
+pred solvePuzzle {
+ ord/last.far = Object
+}
+
+run solvePuzzle for 8 State expect 1
+
+/**
+ * no Object can be in two places at once
+ * this is implied by both definitions of crossRiver
+ */
+assert NoQuantumObjects {
+ no s : State | some x : Object | x in s.near and x in s.far
+}
+
+check NoQuantumObjects for 8 State expect 0
\ No newline at end of file
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/farmer.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/farmer.thm
new file mode 100644
index 00000000..41d7ada7
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/farmer.thm
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/handshake.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/handshake.als
new file mode 100644
index 00000000..8fce6178
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/handshake.als
@@ -0,0 +1,64 @@
+module examples/puzzles/handshake
+
+/*
+ * Alloy model of the Halmos handshake problem
+ *
+ * Hilary and Jocelyn are married. They invite four couples who are friends for dinner. When
+ * they arrive, they shake hands with each other. Nobody shakes hands with him or herself
+ * or with his or her spouse. After there has been some handshaking, Jocelyn jumps up on
+ * a chair and says "Stop shaking hands!", and then asks how many hands each person has
+ * shaken. All the answers are different. How many hands has Hilary shaken?
+ *
+ * The Alloy model represents the problem as a set of constraints. Properties of the spouse
+ * relationship and of handshaking in general are given as facts. The particular situation
+ * is cast as a function.
+ *
+ * There are 9 people answering, and all answers are different. Nobody can shake more than
+ * 8 hands. So answers must be 0..8. The one (p8 say) who answered 8 has shaken everybody's
+ * hand except for his or her own, and his or her spouse's. Now consider the person who shook
+ * 0 hands (p0 say). The persons p0 and p8 are distinct. If they are not married, then p8 cannot
+ * have shaken 8 hands, because he or she did not shake the hand of p0 or of his or her spouse.
+ * So p8's spouse to p0. Now imagine Jocelyn asking the question again, with p0 and p8 out of
+ * the room, and excluding hand shakes with them. Since p8 shook hands with everyone else
+ * except p0 and p8, everyone gives an answer one smaller than they did before, giving 0..6.
+ * The argument now applies recursively. So Hilary is left alone, having shaken 4 hands.
+ *
+ * author: Daniel Jackson, 11/15/01
+ */
+
+sig Person {spouse: Person, shaken: set Person}
+one sig Jocelyn, Hilary extends Person {}
+
+fact ShakingProtocol {
+ // nobody shakes own or spouse's hand
+ all p: Person | no (p + p.spouse) & p.shaken
+ // if p shakes q, q shakes p
+ all p, q: Person | p in q.shaken => q in p.shaken
+ }
+
+fact Spouses {
+ all p, q: Person | p!=q => {
+ // if q is p's spouse, p is q's spouse
+ p.spouse = q => q.spouse = p
+ // no spouse sharing
+ p.spouse != q.spouse
+ }
+ all p: Person {
+ // a person is his or her spouse's spouse
+ p.spouse.spouse = p
+ // nobody is his or her own spouse
+ p != p.spouse
+ }
+ }
+
+pred Puzzle {
+ // everyone but Jocelyn has shaken a different number of hands
+ all p,q: Person - Jocelyn | p!=q => #p.shaken != #q.shaken
+ // Hilary's spouse is Jocelyn
+ Hilary.spouse = Jocelyn
+ }
+
+P10: run Puzzle for exactly 10 Person, 5 int expect 1
+P12: run Puzzle for exactly 12 Person, 5 int expect 1
+P14: run Puzzle for exactly 14 Person, 5 int expect 1
+P16: run Puzzle for exactly 16 Person, 6 int expect 1
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/handshake.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/handshake.thm
new file mode 100644
index 00000000..0bc070b1
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/handshake.thm
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/hanoi.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/hanoi.als
new file mode 100644
index 00000000..f936fa5f
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/hanoi.als
@@ -0,0 +1,140 @@
+module examples/puzzles/hanoi
+
+/*
+ * Towers of Hanoi model
+ *
+ * Description of problem from http://www.cut-the-knot.com/recurrence/hanoi.html
+ *
+ * The Tower of Hanoi puzzle was invented by the French mathematician Edouard Lucas
+ * in 1883. We are given a tower of eight disks, initially stacked in decreasing size on
+ * one of three pegs. The objective is to transfer the entire tower to one of the other
+ * pegs, moving only one disk at a time and never a larger one onto a smaller.
+ *
+ * The Alloy model below is written so that a solution to the model is a complete
+ * sequence of valid moves solving an instance of the problem. We define constraints
+ * for the initial state (all discs on left stake), the final state (all discs on right stake),
+ * and each pair of adjacent states (the top disc is moved from one stake to another,
+ * not putting larger discs on smaller discs), and let Alloy Analyzer solve for the
+ * sequence of states satisfying these constraints. Since each adjacent pair of states is
+ * constrained to be related by a single move, it is easy to see the sequence of moves
+ * once you have the sequence of states.
+ *
+ * For n discs, 2^n states are needed for a solution
+ * (including the initial state and the final state).
+ *
+ * Performance: currently, the problem can be solved for up to 5 discs; this takes
+ * several minutes with the Chaff solver.
+ *
+ * author: Ilya Shlyakhter
+ */
+
+open util/ordering[State] as states
+open util/ordering[Stake] as stakes
+open util/ordering[Disc] as discs
+
+sig Stake { }
+
+sig Disc { }
+
+/**
+ * sig State: the complete state of the system --
+ * which disc is on which stake. An solution is a
+ * sequence of states.
+ */
+sig State {
+ on: Disc -> one Stake // _each_ disc is on _exactly one_ stake
+ // note that we simply record the set of discs on each stake --
+ // the implicit assumption is that on each stake the discs
+ // on that stake are ordered by size with smallest disc on top
+ // and largest on bottom, as the problem requires.
+}
+
+/**
+ * compute the set of discs on the given stake in this state.
+ * ~(this.on) map the stake to the set of discs on that stake.
+ */
+fun discsOnStake[st: State, stake: Stake]: set Disc {
+ stake.~(st.on)
+}
+
+/**
+ * compute the top disc on the given stake, or the empty set
+ * if the stake is empty
+ */
+fun topDisc[st: State, stake: Stake]: lone Disc {
+ { d: st.discsOnStake[stake] | st.discsOnStake[stake] in discs/nexts[d] + d }
+}
+
+/**
+ * Describes the operation of moving the top disc from stake fromStake
+ * to stake toStake. This function is defined implicitly but is
+ * nevertheless deterministic, i.e. the result state is completely
+ * determined by the initial state and fromStake and toStake; hence
+ * the "det" modifier above. (It's important to use the "det" modifier
+ * to tell the Alloy Analyzer that the function is in fact deterministic.)
+ */
+pred Move [st: State, fromStake, toStake: Stake, s': State] {
+ let d = st.topDisc[fromStake] | {
+ // all discs on toStake must be larger than d,
+ // so that we can put d on top of them
+ st.discsOnStake[toStake] in discs/nexts[d]
+ // after, the fromStake has the discs it had before, minus d
+ s'.discsOnStake[fromStake] = st.discsOnStake[fromStake] - d
+ // after, the toStake has the discs it had before, plus d
+ s'.discsOnStake[toStake] = st.discsOnStake[toStake] + d
+ // the remaining stake afterwards has exactly the discs it had before
+ let otherStake = Stake - fromStake - toStake |
+ s'.discsOnStake[otherStake] = st.discsOnStake[otherStake]
+ }
+}
+
+/**
+ * there is a leftStake that has all the discs at the beginning,
+ * and a rightStake that has all the discs at the end
+ */
+pred Game1 {
+ Disc in states/first.discsOnStake[stakes/first]
+ some finalState: State | Disc in finalState.discsOnStake[stakes/last]
+
+ // each adjacent pair of states are related by a valid move of one disc
+ all preState: State - states/last |
+ let postState = states/next[preState] |
+ some fromStake: Stake | {
+ // must have at least one disk on fromStake to be able to move
+ // a disc from fromStake to toStake
+ some preState.discsOnStake[fromStake]
+ // post- results from pre- by making one disc move
+ some toStake: Stake | preState.Move[fromStake, toStake, postState]
+ }
+}
+
+/**
+ * there is a leftStake that has all the discs at the beginning,
+ * and a rightStake that has all the discs at the end
+ */
+pred Game2 {
+ Disc in states/first.discsOnStake[stakes/first]
+ some finalState: State | Disc in finalState.discsOnStake[stakes/last]
+
+ // each adjacent pair of states are related by a valid move of one disc
+ all preState: State - states/last |
+ let postState = states/next[preState] |
+ some fromStake: Stake |
+ let d = preState.topDisc[fromStake] | {
+ // must have at least one disk on fromStake to be able to move
+ // a disc from fromStake to toStake
+ some preState.discsOnStake[fromStake]
+ postState.discsOnStake[fromStake] = preState.discsOnStake[fromStake] - d
+ some toStake: Stake | {
+ // post- results from pre- by making one disc move
+ preState.discsOnStake[toStake] in discs/nexts[d]
+ postState.discsOnStake[toStake] = preState.discsOnStake[toStake] + d
+ // the remaining stake afterwards has exactly the discs it had before
+ let otherStake = Stake - fromStake - toStake |
+ postState.discsOnStake[otherStake] = preState.discsOnStake[otherStake]
+ }
+ }
+ }
+
+run Game1 for 1 but 3 Stake, 5 Disc, 32 State expect 1
+run Game2 for 1 but 3 Stake, 3 Disc, 8 State expect 1
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/hanoi.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/hanoi.thm
new file mode 100644
index 00000000..f56b5722
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/puzzles/hanoi.thm
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/file_system.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/file_system.als
new file mode 100644
index 00000000..60fd959b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/file_system.als
@@ -0,0 +1,59 @@
+module examples/systems/file_system
+
+/*
+ * Model of a generic file system.
+ */
+
+abstract sig Object {}
+
+sig Name {}
+
+sig File extends Object {} { some d: Dir | this in d.entries.contents }
+
+sig Dir extends Object {
+ entries: set DirEntry,
+ parent: lone Dir
+} {
+ parent = this.~@contents.~@entries
+ all e1, e2 : entries | e1.name = e2.name => e1 = e2
+ this !in this.^@parent
+ this != Root => Root in this.^@parent
+}
+
+one sig Root extends Dir {} { no parent }
+
+lone sig Cur extends Dir {}
+
+sig DirEntry {
+ name: Name,
+ contents: Object
+} {
+ one this.~entries
+}
+
+
+/**
+ * all directories besides root have one parent
+ */
+pred OneParent_buggyVersion {
+ all d: Dir - Root | one d.parent
+}
+
+/**
+ * all directories besides root have one parent
+ */
+pred OneParent_correctVersion {
+ all d: Dir - Root | (one d.parent && one contents.d)
+}
+
+/**
+ * Only files may be linked (that is, have more than one entry)
+ * That is, all directories are the contents of at most one directory entry
+ */
+pred NoDirAliases {
+ all o: Dir | lone o.~contents
+}
+
+check { OneParent_buggyVersion => NoDirAliases } for 5 expect 1
+
+check { OneParent_correctVersion => NoDirAliases } for 5 expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/file_system.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/file_system.thm
new file mode 100644
index 00000000..4b740ad3
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/file_system.thm
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/javatypes_soundness.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/javatypes_soundness.als
new file mode 100644
index 00000000..6bf50ed7
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/javatypes_soundness.als
@@ -0,0 +1,140 @@
+module examples/systems/javatypes
+
+/*
+ * Model of the Java type system. The TypeSoundness assertion
+ * claims that if a Java program type checks successfully,
+ * then a field will cannot be assigned an incorrect type.
+ *
+ * author: Daniel Jackson
+ */
+
+open util/graph[Type] as graph
+
+abstract sig Type {
+ xtends: set Type
+ }
+sig Interface extends Type {}
+ { xtends in Interface }
+sig Class extends Type {
+ implements: set Interface,
+ fields: set Field
+ } { lone xtends && xtends in Class }
+-- optional: best omitted to allow private etc
+-- {xtends.@fields in fields}
+sig Field {
+ declType: Type
+ }
+
+fact {
+ graph/dag[xtends]
+ }
+
+abstract sig Value {}
+one sig Null extends Value {}
+sig Object extends Value {
+ type: Class,
+ slot: Field lone -> lone Slot
+ } {slot.Slot = type.fields}
+sig Slot {}
+
+abstract sig Statement {}
+sig Assignment extends Statement {
+ var: Variable,
+ expr: Expr
+ }
+sig Setter extends Statement {
+ field: Field,
+ lexpr, rexpr: Expr
+ }
+
+abstract sig Expr {
+ type: Type,
+ subexprs: set Expr
+ } {subexprs = this + this.^@expr}
+sig Variable extends Expr {
+ declType: Type
+ } {type = declType}
+sig Constructor extends Expr {
+ class: Class
+ }
+sig Getter extends Expr {
+ field: Field,
+ expr: Expr
+ } {type = field.declType}
+
+sig State {
+ objects: set Object,
+ reaches: Object -> Object,
+ vars: set Variable,
+ holds: (Slot + Variable) -> lone Value,
+ val: Expr -> lone Value
+ } {
+ all o: Object | o.reaches = holds[o.slot[Field]] & Object
+ holds.Value & Variable = vars
+ objects = holds[vars].^reaches
+ all e: Expr | let v = val[e] {
+ e in Variable => v = holds[e]
+ e in Getter => v = holds[(val[e.expr]).slot[e.field]]
+ e in Constructor => v in Object and v.type = e.type }
+ }
+
+pred RuntimeTypesOK [s: State] {
+ all o: s.objects, f: o.type.fields |
+ let v = s.holds [o.slot [f]] | HasType [v, f.declType]
+ all var: s.vars |
+ let v = s.holds [var] | HasType [v, var.declType]
+ }
+
+pred HasType [v: Value, t: Type] {
+ v in Null or Subtype [v.type, t]
+ }
+
+pred Subtype [t, t': Type] {
+ t in Class =>
+ (let supers = (t & Class).*(Class<:xtends) |
+ t' in (supers + supers.implements.*(Interface<:xtends)))
+ t in Interface => t' in (t & Interface).*(Interface<:xtends)
+ }
+
+pred TypeChecksSetter [stmt: Setter] {
+ all g: Getter & stmt.(lexpr+rexpr).subexprs | g.field in g.expr.type.fields
+ stmt.field in stmt.lexpr.type.fields
+ Subtype [stmt.rexpr.type, stmt.field.declType]
+ }
+
+pred ExecuteSetter [s, s': State, stmt: Setter] {
+ stmt.(rexpr+lexpr).subexprs & Variable in s.vars
+ s'.objects = s.objects and s'.vars = s.vars
+ let rval = s.val [stmt.rexpr], lval = s.val [stmt.lexpr] {
+ no lval & Null
+ s'.holds = s.holds ++ (lval.slot[stmt.field] -> rval)
+ }
+ }
+
+assert TypeSoundness {
+ all s, s': State, stmt: Setter |
+ {RuntimeTypesOK[s]
+ ExecuteSetter [s, s', stmt]
+ TypeChecksSetter[stmt]
+ } => RuntimeTypesOK[s']
+ }
+
+fact {all o, o': Object | some o.slot[Field] & o'.slot[Field] => o = o'}
+fact {all g: Getter | no g & g.^subexprs}
+
+fact ScopeFact {
+ #Assignment =< 1
+ #Class =< 5
+ #Interface =< 5
+}
+
+check TypeSoundness for 3 expect 0
+
+check TypeSoundness for 2 State, 1 Assignment,
+1 Statement, 5 Interface, 5 Class, 1 Null,
+7 Object, 12 Expr, 3 Field, 3 Slot expect 0
+
+// very slow
+// check TypeSoundness for 2 State, 1 Statement,
+// 10 Type, 8 Value, 12 Expr,
+// 3 Field, 3 Slot expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/lists.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/lists.als
new file mode 100644
index 00000000..aabd5f63
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/lists.als
@@ -0,0 +1,48 @@
+/*
+ * a simple list module
+ * which demonstrates how to create predicates and fields that mirror each other
+ * thus allowing recursive constraints (even though recursive predicates are not
+ * currently supported by Alloy)
+ * author: Robert Seater
+ */
+
+module list
+
+sig Thing {}
+fact NoStrayThings {Thing in List.car}
+
+abstract sig List {
+ equivTo: set List,
+ prefixes: set List
+ }
+sig NonEmptyList extends List {
+ car: one Thing,
+ cdr: one List
+ }
+sig EmptyList extends List {}
+
+pred isFinite [L:List] {some e: EmptyList | e in L.*cdr}
+fact finite {all L: List | isFinite[L]}
+
+fact Equivalence {
+ all a,b: List | (a in b.equivTo) <=> ((a.car = b.car and b.cdr in a.cdr.equivTo) and (#a.*cdr = #b.*cdr))
+ }
+assert reflexive {all L: List | L in L.equivTo}
+check reflexive for 6 expect 0
+assert symmetric {all a,b: List | a in b.equivTo <=> b in a.equivTo}
+check symmetric for 6 expect 0
+assert empties {all a,b: EmptyList | a in b.equivTo}
+check empties for 6 expect 0
+
+fact prefix { //a is a prefix of b
+ all e: EmptyList, L:List | e in L.prefixes
+ all a,b: NonEmptyList | (a in b.prefixes) <=> (a.car = b.car
+ and a.cdr in b.cdr.prefixes
+ and #a.*cdr < #b.*cdr)
+}
+
+pred show {
+ some a, b: NonEmptyList | a!=b && b in a.prefixes
+ }
+run show for 4 expect 1
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/lists.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/lists.thm
new file mode 100644
index 00000000..6da43857
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/lists.thm
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/marksweepgc.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/marksweepgc.als
new file mode 100644
index 00000000..b8081e3f
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/marksweepgc.als
@@ -0,0 +1,83 @@
+module examples/systems/marksweepgc
+
+/*
+ * Model of mark and sweep garbage collection.
+ */
+
+// a node in the heap
+sig Node {}
+
+sig HeapState {
+ left, right : Node -> lone Node,
+ marked : set Node,
+ freeList : lone Node
+}
+
+pred clearMarks[hs, hs' : HeapState] {
+ // clear marked set
+ no hs'.marked
+ // left and right fields are unchanged
+ hs'.left = hs.left
+ hs'.right = hs.right
+}
+
+/**
+ * simulate the recursion of the mark() function using transitive closure
+ */
+fun reachable[hs: HeapState, n: Node] : set Node {
+ n + n.^(hs.left + hs.right)
+}
+
+pred mark[hs: HeapState, from : Node, hs': HeapState] {
+ hs'.marked = hs.reachable[from]
+ hs'.left = hs.left
+ hs'.right = hs.right
+}
+
+/**
+ * complete hack to simulate behavior of code to set freeList
+ */
+pred setFreeList[hs, hs': HeapState] {
+ // especially hackish
+ hs'.freeList.*(hs'.left) in (Node - hs.marked)
+ all n: Node |
+ (n !in hs.marked) => {
+ no hs'.right[n]
+ hs'.left[n] in (hs'.freeList.*(hs'.left))
+ n in hs'.freeList.*(hs'.left)
+ } else {
+ hs'.left[n] = hs.left[n]
+ hs'.right[n] = hs.right[n]
+ }
+ hs'.marked = hs.marked
+}
+
+pred GC[hs: HeapState, root : Node, hs': HeapState] {
+ some hs1, hs2: HeapState |
+ hs.clearMarks[hs1] && hs1.mark[root, hs2] && hs2.setFreeList[hs']
+}
+
+assert Soundness1 {
+ all h, h' : HeapState, root : Node |
+ h.GC[root, h'] =>
+ (all live : h.reachable[root] | {
+ h'.left[live] = h.left[live]
+ h'.right[live] = h.right[live]
+ })
+}
+
+assert Soundness2 {
+ all h, h' : HeapState, root : Node |
+ h.GC[root, h'] =>
+ no h'.reachable[root] & h'.reachable[h'.freeList]
+}
+
+assert Completeness {
+ all h, h' : HeapState, root : Node |
+ h.GC[root, h'] =>
+ (Node - h'.reachable[root]) in h'.reachable[h'.freeList]
+}
+
+check Soundness1 for 3 expect 0
+check Soundness2 for 3 expect 0
+check Completeness for 3 expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/views.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/views.als
new file mode 100644
index 00000000..3a5ab82b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/systems/views.als
@@ -0,0 +1,217 @@
+module examples/systems/views
+
+/*
+ * Model of views in object-oriented programming.
+ *
+ * Two object references, called the view and the backing,
+ * are related by a view mechanism when changes to the
+ * backing are automatically propagated to the view. Note
+ * that the state of a view need not be a projection of the
+ * state of the backing; the keySet method of Map, for
+ * example, produces two view relationships, and for the
+ * one in which the map is modified by changes to the key
+ * set, the value of the new map cannot be determined from
+ * the key set. Note that in the iterator view mechanism,
+ * the iterator is by this definition the backing object,
+ * since changes are propagated from iterator to collection
+ * and not vice versa. Oddly, a reference may be a view of
+ * more than one backing: there can be two iterators on the
+ * same collection, eg. A reference cannot be a view under
+ * more than one view type.
+ *
+ * A reference is made dirty when it is a backing for a view
+ * with which it is no longer related by the view invariant.
+ * This usually happens when a view is modified, either
+ * directly or via another backing. For example, changing a
+ * collection directly when it has an iterator invalidates
+ * it, as does changing the collection through one iterator
+ * when there are others.
+ *
+ * More work is needed if we want to model more closely the
+ * failure of an iterator when its collection is invalidated.
+ *
+ * As a terminological convention, when there are two
+ * complementary view relationships, we will give them types
+ * t and t'. For example, KeySetView propagates from map to
+ * set, and KeySetView' propagates from set to map.
+ *
+ * author: Daniel Jackson
+ */
+
+open util/ordering[State] as so
+open util/relation as rel
+
+sig Ref {}
+sig Object {}
+
+-- t->b->v in views when v is view of type t of backing b
+-- dirty contains refs that have been invalidated
+sig State {
+ refs: set Ref,
+ obj: refs -> one Object,
+ views: ViewType -> refs -> refs,
+ dirty: set refs
+-- , anyviews: Ref -> Ref -- for visualization
+ }
+-- {anyviews = ViewType.views}
+
+sig Map extends Object {
+ keys: set Ref,
+ map: keys -> one Ref
+ }{all s: State | keys + Ref.map in s.refs}
+sig MapRef extends Ref {}
+fact {State.obj[MapRef] in Map}
+
+sig Iterator extends Object {
+ left, done: set Ref,
+ lastRef: lone done
+ }{all s: State | done + left + lastRef in s.refs}
+sig IteratorRef extends Ref {}
+fact {State.obj[IteratorRef] in Iterator}
+
+sig Set extends Object {
+ elts: set Ref
+ }{all s: State | elts in s.refs}
+sig SetRef extends Ref {}
+fact {State.obj[SetRef] in Set}
+
+abstract sig ViewType {}
+one sig KeySetView, KeySetView', IteratorView extends ViewType {}
+fact ViewTypes {
+ State.views[KeySetView] in MapRef -> SetRef
+ State.views[KeySetView'] in SetRef -> MapRef
+ State.views[IteratorView] in IteratorRef -> SetRef
+ all s: State | s.views[KeySetView] = ~(s.views[KeySetView'])
+ }
+
+/**
+ * mods is refs modified directly or by view mechanism
+ * doesn't handle possibility of modifying an object and its view at once?
+ * should we limit frame conds to non-dirty refs?
+ */
+pred modifies [pre, post: State, rs: set Ref] {
+ let vr = pre.views[ViewType], mods = rs.*vr {
+ all r: pre.refs - mods | pre.obj[r] = post.obj[r]
+ all b: mods, v: pre.refs, t: ViewType |
+ b->v in pre.views[t] => viewFrame [t, pre.obj[v], post.obj[v], post.obj[b]]
+ post.dirty = pre.dirty +
+ {b: pre.refs | some v: Ref, t: ViewType |
+ b->v in pre.views[t] && !viewFrame [t, pre.obj[v], post.obj[v], post.obj[b]]
+ }
+ }
+ }
+
+pred allocates [pre, post: State, rs: set Ref] {
+ no rs & pre.refs
+ post.refs = pre.refs + rs
+ }
+
+/**
+ * models frame condition that limits change to view object from v to v' when backing object changes to b'
+ */
+pred viewFrame [t: ViewType, v, v', b': Object] {
+ t in KeySetView => v'.elts = dom [b'.map]
+ t in KeySetView' => b'.elts = dom [v'.map]
+ t in KeySetView' => (b'.elts) <: (v.map) = (b'.elts) <: (v'.map)
+ t in IteratorView => v'.elts = b'.left + b'.done
+ }
+
+pred MapRef.keySet [pre, post: State, setRefs: SetRef] {
+ post.obj[setRefs].elts = dom [pre.obj[this].map]
+ modifies [pre, post, none]
+ allocates [pre, post, setRefs]
+ post.views = pre.views + KeySetView->this->setRefs + KeySetView'->setRefs->this
+ }
+
+pred MapRef.put [pre, post: State, k, v: Ref] {
+ post.obj[this].map = pre.obj[this].map ++ k->v
+ modifies [pre, post, this]
+ allocates [pre, post, none]
+ post.views = pre.views
+ }
+
+pred SetRef.iterator [pre, post: State, iterRef: IteratorRef] {
+ let i = post.obj[iterRef] {
+ i.left = pre.obj[this].elts
+ no i.done + i.lastRef
+ }
+ modifies [pre,post,none]
+ allocates [pre, post, iterRef]
+ post.views = pre.views + IteratorView->iterRef->this
+ }
+
+pred IteratorRef.remove [pre, post: State] {
+ let i = pre.obj[this], i' = post.obj[this] {
+ i'.left = i.left
+ i'.done = i.done - i.lastRef
+ no i'.lastRef
+ }
+ modifies [pre,post,this]
+ allocates [pre, post, none]
+ pre.views = post.views
+ }
+
+pred IteratorRef.next [pre, post: State, ref: Ref] {
+ let i = pre.obj[this], i' = post.obj[this] {
+ ref in i.left
+ i'.left = i.left - ref
+ i'.done = i.done + ref
+ i'.lastRef = ref
+ }
+ modifies [pre, post, this]
+ allocates [pre, post, none]
+ pre.views = post.views
+ }
+
+pred IteratorRef.hasNext [s: State] {
+ some s.obj[this].left
+ }
+
+assert zippishOK {
+ all
+ ks, vs: SetRef,
+ m: MapRef,
+ ki, vi: IteratorRef,
+ k, v: Ref |
+ let s0=so/first,
+ s1=so/next[s0],
+ s2=so/next[s1],
+ s3=so/next[s2],
+ s4=so/next[s3],
+ s5=so/next[s4],
+ s6=so/next[s5],
+ s7=so/next[s6] |
+ ({
+ precondition [s0, ks, vs, m]
+ no s0.dirty
+ ks.iterator [s0, s1, ki]
+ vs.iterator [s1, s2, vi]
+ ki.hasNext [s2]
+ vi.hasNext [s2]
+ ki.this/next [s2, s3, k]
+ vi.this/next [s3, s4, v]
+ m.put [s4, s5, k, v]
+ ki.remove [s5, s6]
+ vi.remove [s6, s7]
+ } => no State.dirty)
+ }
+
+pred precondition [pre: State, ks, vs, m: Ref] {
+ // all these conditions and other errors discovered in scope of 6 but 8,3
+ // in initial state, must have view invariants hold
+ (all t: ViewType, b, v: pre.refs |
+ b->v in pre.views[t] => viewFrame [t, pre.obj[v], pre.obj[v], pre.obj[b]])
+ // sets are not aliases
+-- ks != vs
+ // sets are not views of map
+-- no (ks+vs)->m & ViewType.pre.views
+ // no iterator currently on either set
+-- no Ref->(ks+vs) & ViewType.pre.views
+ }
+
+check zippishOK for 6 but 8 State, 3 ViewType expect 1
+
+/**
+ * experiment with controlling heap size
+ */
+fact {all s: State | #s.obj < 5}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/birthday.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/birthday.als
new file mode 100644
index 00000000..092097f0
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/birthday.als
@@ -0,0 +1,68 @@
+module examples/toys/birthday
+
+/*
+ * Birthday Book
+ *
+ * A classic Z example to explain the basic form of an Alloy model. For the original,
+ * see J.M. Spivey, The Z Notation, Second Edition, Prentice Hall, 1992.
+ *
+ * A birthday book has two fields: known, a set of names (of persons whose birthdays are known),
+ * and date, a function from known names to dates. The operation AddBirthday adds an association
+ * between a name and a date; it uses the relational override operator (++), so any existing
+ * mapping from the name to a date is replaced. DelBirthday removes the entry for a given name.
+ * FindBirthday obtains the date d for a name n. The argument d is declared to be optional (that is,
+ * a singleton or empty set), so if there is no entry for n, d will be empty. Remind gives the set
+ * of names whose birthdays fall on a particular day.
+ *
+ * The assertion AddWorks says that if you add an entry, then look it up, you get back what you
+ * just entered. DelIsUndo says that doing DelBirthday after AddBirthday undoes it, as if the add
+ * had never happened. The first of these assertions is valid; the second isn't.
+ *
+ * The function BusyDay shows a case in which Remind produces more than one card.
+ *
+ * author: Daniel Jackson, 11/14/01
+ */
+
+sig Name {}
+sig Date {}
+sig BirthdayBook {known: set Name, date: known -> one Date}
+
+pred AddBirthday [bb, bb': BirthdayBook, n: Name, d: Date] {
+ bb'.date = bb.date ++ (n->d)
+ }
+
+pred DelBirthday [bb, bb': BirthdayBook, n: Name] {
+ bb'.date = bb.date - (n->Date)
+ }
+
+pred FindBirthday [bb: BirthdayBook, n: Name, d: lone Date] {
+ d = bb.date[n]
+ }
+
+pred Remind [bb: BirthdayBook, today: Date, cards: set Name] {
+ cards = (bb.date).today
+ }
+
+pred InitBirthdayBook [bb: BirthdayBook] {
+ no bb.known
+ }
+
+assert AddWorks {
+ all bb, bb': BirthdayBook, n: Name, d: Date, d': lone Date |
+ AddBirthday [bb,bb',n,d] && FindBirthday [bb',n,d'] => d = d'
+ }
+
+assert DelIsUndo {
+ all bb1,bb2,bb3: BirthdayBook, n: Name, d: Date|
+ AddBirthday [bb1,bb2,n,d] && DelBirthday [bb2,bb3,n]
+ => bb1.date = bb3.date
+ }
+
+check AddWorks for 3 but 2 BirthdayBook expect 0
+check DelIsUndo for 3 but 2 BirthdayBook expect 1
+
+pred BusyDay [bb: BirthdayBook, d: Date]{
+ some cards: set Name | Remind [bb,d,cards] && !lone cards
+ }
+
+run BusyDay for 3 but 1 BirthdayBook expect 1
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/birthday.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/birthday.thm
new file mode 100644
index 00000000..3c3b050b
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/birthday.thm
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/ceilingsAndFloors.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/ceilingsAndFloors.als
new file mode 100644
index 00000000..e02fe438
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/ceilingsAndFloors.als
@@ -0,0 +1,46 @@
+module models/examples/toys/CeilingsAndFloors
+
+/*
+ * In his 1973 song, Paul Simon said "One Man's Ceiling Is Another Man's Floor".
+ * Does it follow that "One Man's Floor Is Another Man's Ceiling"?
+ *
+ * To see why not, check the assertion BelowToo.
+ *
+ * Perhaps simply preventing man's own floor from being his ceiling is enough,
+ * as is done in the Geometry constraint. BelowToo' shows that there are still
+ * cases where Geometry holds but the implication does not, although now
+ * the smallest solution has 3 Men and 3 Platforms instead of just 2 of each.
+ *
+ * What if we instead prevent floors and ceilings from being shared,
+ * as is done in the NoSharing constraint? The assertion BelowToo''
+ * has no counterexamples, demonstrating that the implication now
+ * holds for all small examples.
+ *
+ * model author: Daniel Jackson (11/2001)
+ * modified by Robert Seater (11/2004)
+ */
+
+sig Platform {}
+sig Man {ceiling, floor: Platform}
+
+fact PaulSimon {all m: Man | some n: Man | n.Above[m]}
+
+pred Above[m, n: Man] {m.floor = n.ceiling}
+
+assert BelowToo { all m: Man | some n: Man | m.Above[n] }
+
+check BelowToo for 2 expect 1
+
+pred Geometry {no m: Man | m.floor = m.ceiling}
+
+assert BelowToo' { Geometry => (all m: Man | some n: Man | m.Above[n]) }
+check BelowToo' for 2 expect 0
+check BelowToo' for 3 expect 1
+
+pred NoSharing {
+ no m,n: Man | m!=n && (m.floor = n.floor || m.ceiling = n.ceiling)
+}
+
+assert BelowToo'' { NoSharing => (all m: Man | some n: Man | m.Above[n]) }
+check BelowToo'' for 6 expect 0
+check BelowToo'' for 10 expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/ceilingsAndFloors.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/ceilingsAndFloors.thm
new file mode 100644
index 00000000..b58a5b0d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/ceilingsAndFloors.thm
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/genealogy.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/genealogy.als
new file mode 100644
index 00000000..2a9e2134
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/genealogy.als
@@ -0,0 +1,74 @@
+module examples/toys/genealogy
+
+/*
+ * Toy model of genealogical relationships
+ *
+ * The purpose of this model is to introduce basic concepts in Alloy.
+ * The signature Person introduces a set of persons; this is paritioned into
+ * two subsets, Man and Woman. The subsignature Adam declares a set of men
+ * with one element -- that is, a scalar. Similarly, Eve declares a single
+ * woman.
+ *
+ * The Person signature declares two fields: a person has one or zero spouses
+ * and a set of parents.
+ *
+ * The facts should be self-explanatory. Note that the constraint that
+ * spouse is a symmetric relation (that is, p is a spouse of q if q is a spouse
+ * of p) is written by equating the field, viewed as a relation, to its
+ * transpose. Since signatures have their own namespaces, and the same field
+ * name can refer to different fields in different relations, it is necessary
+ * to indicate which signature the field belongs to. This is not necessary when
+ * dereferencing a field, because the appropriate field is automatically
+ * determined by the type of the referencing expression.
+ *
+ * The command has no solutions. Given only 5 persons, it's not possible
+ * to have a couple distinct from Adam and Eve without incest. To understand
+ * the model, try weakening the constraints by commenting lines out (just
+ * put two hyphens at the start of a line) and rerunning the command.
+ *
+ * author: Daniel Jackson, 11/13/01
+ */
+
+abstract sig Person {spouse: lone Person, parents: set Person}
+sig Man, Woman extends Person {}
+one sig Eve extends Woman {}
+one sig Adam extends Man {}
+
+fact Biology {
+ -- nobody is his or her own ancestor
+ no p: Person | p in p.^parents
+ }
+
+fact Bible {
+ -- every person except Adam and Eve has a mother and father
+ all p: Person - (Adam + Eve) | one mother: Woman, father: Man |
+ p.parents = mother + father
+ -- Adam and Eve have no parents
+ no (Adam + Eve).parents
+ -- Adam's spouse is Eve
+ Adam.spouse = Eve
+ }
+
+fact SocialNorms {
+ -- nobody is his or her own spouse
+ no p: Person | p.spouse = p
+ -- spouse is symmetric
+ spouse = ~spouse
+ -- a man's spouse is a woman and vice versa
+ Man.spouse in Woman && Woman.spouse in Man
+ }
+
+fact NoIncest {
+ -- can't marry a sibling
+ no p: Person | some p.spouse.parents & p.parents
+ -- can't marry a parent
+ no p: Person | some p.spouse & p.parents
+ }
+
+pred Show {
+ some p: Person - (Adam + Eve) | some p.spouse
+ }
+run Show for 6 expect 1
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/genealogy.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/genealogy.thm
new file mode 100644
index 00000000..fbeb5cad
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/genealogy.thm
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/grandpa.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/grandpa.als
new file mode 100644
index 00000000..099cd0d5
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/grandpa.als
@@ -0,0 +1,88 @@
+module grandpa
+
+/*
+ * An Alloy model of the song "I Am My Own Grandpa"
+ * by Dwight B. Latham and Moe Jaffe
+ *
+ * The challenge is to produce a man who is his own grandfather
+ * without resorting to incest or time travel. Executing the predicate
+ * "ownGrandpa" will demonstrate how such a thing can occur.
+ *
+ * The full song lyrics, which describe an isomorophic solution,
+ * are included at the end of this file.
+ *
+ * model author: Daniel Jackson
+ */
+
+abstract sig Person {
+ father: lone Man,
+ mother: lone Woman
+ }
+
+sig Man extends Person { wife: lone Woman }
+
+sig Woman extends Person { husband: lone Man }
+
+fact Biology { no p: Person | p in p.^(mother+father) }
+
+fact Terminology { wife = ~husband }
+
+fact SocialConvention {
+ no wife & *(mother+father).mother
+ no husband & *(mother+father).father
+ }
+
+fun grandpas [p: Person]: set Person {
+ let parent = mother + father + father.wife + mother.husband |
+ p.parent.parent & Man
+ }
+
+pred ownGrandpa [m: Man] { m in grandpas[m] }
+
+run ownGrandpa for 4 Person expect 1
+
+/* defined variables:
+ *
+ * spouse = husband+wife
+ */
+
+/*
+I Am My Own Grandpa
+by Dwight B. Latham and Moe Jaffe
+
+Many many years ago, when I was twenty-three,
+I was married to a widow as pretty as can be,
+This widow had a grown-up daughter who had hair of red,
+My father fell in love with her and soon the two were wed.
+
+ I'm my own grandpa, I'm my own grandpa.
+ It sounds funny, I know, but it really is so
+ I'm my own grandpa.
+
+This made my dad my son-in-law and changed my very life,
+For my daughter was my mother, for she was my father's wife.
+To complicate the matter, even though it brought me joy,
+I soon became the father of a bouncing baby boy.
+
+My little baby thus became a brother-in-law to dad,
+And so became my uncle, though it made me very sad,
+For if he was my uncle then that also made him brother
+To the widow's grown-up daughter, who of course was my step-mother.
+
+Father's wife then had a son who kept them on the run.
+And he became my grandchild for he was my daughter's son.
+My wife is now my mother's mother and it makes me blue,
+Because although she is my wife, she's my grandmother, too.
+
+Oh, if my wife's my grandmother then I am her grandchild.
+And every time I think of it, it nearly drives me wild.
+For now I have become the strangest case you ever saw
+As the husband of my grandmother, I am my own grandpa.
+
+ I'm my own grandpa, I'm my own grandpa.
+ It sounds funny, I know, but it really is so
+ I'm my own grandpa.
+ I'm my own grandpa, I'm my own grandpa.
+ It sounds funny, I know, but it really is so
+ I'm my own grandpa.
+*/
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/grandpa.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/grandpa.thm
new file mode 100644
index 00000000..781b6c9f
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/grandpa.thm
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/javatypes.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/javatypes.als
new file mode 100644
index 00000000..2e60d64d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/javatypes.als
@@ -0,0 +1,46 @@
+module examples/toys/javatypes
+
+/*
+ * A simple model of typing in Java.
+ *
+ * This model describes the basic notions of typing in Java.
+ * It ignores primitive types and null references. Each type has
+ * some set of subtypes. Types are partitioned into class and
+ * interface types. Object is a particular class.
+ *
+ * The fact TypeHierarchy says that every type is a direct or
+ * indirect subtype of Object; that no type is a direct or indirect
+ * of itself; and every type is a subtype of at most one class.
+ *
+ * An object instance has a type (its creation type) that is a class.
+ * A variable may hold an instance, and has a declared type. The
+ * fact TypeSoundness says that all instances held by a variable
+ * have types that are direct or indirect subtypes of the variable's
+ * declared type.
+ *
+ * The function Show specifies a case in which there is a class
+ * distinct from Object; there is some interface; and some variable
+ * has a declared type that is an interface.
+ *
+ * author: Daniel Jackson, 11/13/01
+ */
+
+abstract sig Type {subtypes: set Type}
+sig Class, Interface extends Type {}
+one sig Object extends Class {}
+fact TypeHierarchy {
+ Type in Object.*subtypes
+ no t: Type | t in t.^subtypes
+ all t: Type | lone t.~subtypes & Class
+ }
+sig Instance {type: Class}
+sig Variable {holds: lone Instance, type: Type}
+fact TypeSoundness {
+ all v: Variable | v.holds.type in v.type.*subtypes
+ }
+pred Show {
+ some Class - Object
+ some Interface
+ some Variable.type & Interface
+ }
+run Show for 3 expect 1
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/life.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/life.als
new file mode 100644
index 00000000..5ee1b159
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/life.als
@@ -0,0 +1,94 @@
+module examples/toys/life
+
+/*
+ * John Conway's Game of Life
+ *
+ * For a detailed description, see:
+ * http://www.math.com/students/wonders/life/life.html
+ *
+ * authors: Bill Thies, Manu Sridharan
+ */
+
+open util/ordering[State] as ord
+
+sig Point {
+ right: lone Point,
+ below: lone Point
+}
+
+fact Acyclic {
+ all p: Point | p !in p.^(right + below)
+}
+
+one sig Root extends Point {}
+
+fact InnerSquaresCommute {
+ all p: Point {
+ p.below.right = p.right.below
+ some p.below && some p.right => some p.below.right
+ }
+}
+
+fact TopRow {
+ all p: Point - Root | no p.~below => # p.*below = # Root.*below
+}
+
+fact Connected {
+ Root.*(right + below) = Point
+}
+
+pred Square {
+ # Root.*right = # Root.*below
+}
+
+run Square for 6 Point, 3 State expect 1
+
+pred Rectangle {}
+
+sig State {
+ live : set Point
+}
+
+fun Neighbors[p : Point] : set Point {
+ p.right + p.right.below + p.below
+ + p.below.~right + p.~right
+ + p.~right.~below + p.~below +
+ p.~below.right
+}
+
+fun LiveNeighborsInState[p : Point, s : State] : set Point {
+ Neighbors[p] & s.live
+}
+
+pred Trans[pre, post: State, p : Point] {
+ let preLive = LiveNeighborsInState[p,pre] |
+ // dead cell w/ 3 live neighbors becomes live
+ (p !in pre.live && # preLive = 3) =>
+ p in post.live
+ else (
+ // live cell w/ 2 or 3 live neighbors stays alive
+ (p in pre.live && (# preLive = 2 || # preLive = 3)) =>
+ p in post.live else p !in post.live
+ )
+}
+
+fact ValidTrans {
+ all pre : State - ord/last |
+ let post = ord/next[pre] |
+ all p : Point |
+ Trans[pre,post,p]
+}
+
+pred Show {}
+
+// slow
+run Show for exactly 12 Point, 3 State expect 1
+
+// a small but interesting example
+pred interesting {
+ some State.live
+ some Point - State.live
+ some right
+ some below
+}
+run interesting for exactly 6 Point, 3 State expect 1
\ No newline at end of file
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/life.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/life.thm
new file mode 100644
index 00000000..3a015d56
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/life.thm
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/numbering.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/numbering.als
new file mode 100644
index 00000000..f8c9b5f3
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/numbering.als
@@ -0,0 +1,129 @@
+module examples/toys/numbering
+
+/*
+ * Alloy model of paragraph numbering
+ *
+ * This model addresses some issues that arose in the design of a text tagging tool. The
+ * tool receives as input a text stream in which paragraphs are tagged with style names,
+ * along with a style sheet that indicates how paragraphs of a given style are to be numbered.
+ * In practice, the style sheet includes such details as what symbols to use for numbering (eg,
+ * roman numericals, letters of the alphabet, etc), but these details are uninteresting.
+ *
+ * In the simplest case, the styles are organized into chains. For example, there may be a
+ * single chain, chapter-section-subsection, so that chapters are numbered 1, 2, 3, etc,
+ * sections are numbered 1.1, 1.2, 1.3, etc, and subsections are numbered 1.1.1, 1.1.2,
+ * etc, each paragraph being numbered according to a number associated with its own
+ * style, and a number for each ancestor.
+ *
+ * Some styles, however, should be numbered independently of one another, but still
+ * according to the same ancestors. For example, we might also have a figure style
+ * that is numbered, like section, according to its chapter, with figures and sections in
+ * some arbitrary interleaving, the numbering of one not affecting the other.
+ *
+ * So in our style hierarchy, a style can have more than one "child". A more tricky complication
+ * allows multiple parents. We might want to have an appendix style, for example, with a
+ * different numbering from the chapter style, but would want section and subsection to work
+ * within appendix exactly as they would work within chapter. So the first section in an
+ * appendix numbered A might be numbered A.1, but if placed in a chapter numbered 1,
+ * it would be numbered 1.1 instead.
+ *
+ * To account for this, styles are organized into replacement classes. Chapter and appendix,
+ * for example, are replacements of one another. When a chapter style is encountered, it
+ * is as if the style hierarchy contains only chapter, with children section, figure and so on;
+ * when appendix is encountered subsequently, chapter is replaced, and figure and section
+ * become children of appendix. We'll call the set of styles active in the tree at a given time
+ * the "context".
+ *
+ * The first part focuses on the replacement mechanism. It characterizes a well-formed
+ * style sheet (with the fact StyleSheet), and a well-formed state (with the fact Forest). An
+ * operation addStyleToContext describes how the context is altered, and includes a
+ * precondition requiring that, for example, a child is not encountered before its parents
+ * (a document can't start with subsection, eg). The assertion PreservesForest checks that the
+ * operation preserves the well-formedness of the state; it was analyzing this that helped
+ * determine an appropriate precondition and the appropriate constraints in the invariant.
+ *
+ * The second part adds the numbering of styles. Note the idiom of declaring a subsignature
+ * and then equating it to the supersignature, thus essentially retrofitting the new fields to the
+ * old signature. A second operation describes how numbers are assigned; the conjunction of the
+ * two operations is what happens when a style is encountered. The assertion AddNeverReduces
+ * checks that when a style is encountered the number associated with each style in the context is
+ * not decreased. The first assertion is valid; the second isn't.
+ *
+ * author: Daniel Jackson, 11/15/01
+ */
+
+open util/relation as rel
+
+sig Style {
+ replaces, parents: set Style
+ }
+
+fact StyleSheet {
+ equivalence [replaces, Style]
+ acyclic [parents, Style]
+ all x: Style {
+ x.replaces.parents.replaces = x.parents
+ all y,z: x.parents | y in z.replaces
+ }
+ }
+
+sig State {
+ context: set Style,
+ ancestors: Style -> Style
+ }
+
+fact DefineAncestors {
+ all s: State, x: Style | s.ancestors [x] = x.*parents & s.context
+ }
+
+pred Forest [s: State] {
+ all x: s.context |
+ some root: s.ancestors[x] {
+ no root.parents
+ all y: s.ancestors[x] - root | one y.parents & s.context
+ }
+ all x: Style | lone x.replaces & s.context
+ }
+
+pred AddStyleToContext [s, s': State, style: Style] {
+ all x: style.^parents | some x.replaces & s.context
+ s'.context = s.context - style.replaces + style
+ }
+
+assert PreserveForest {
+ all s,s': State, z: Style |
+ Forest[s] && AddStyleToContext [s,s',z] => Forest[s']
+ }
+
+check PreserveForest for 4 expect 0
+
+sig Value {next: Value}
+sig NumberedStyle extends Style {
+ initial: Value
+ }
+sig NumberedState extends State {
+ value: Style -> one Value
+ }
+fact {Style = NumberedStyle}
+fact {State = NumberedState}
+
+pred AddStyleToNumbering [s, s': State, style: Style] {
+ s'.value[style] = (style in s.context => s.value[style].next else style.initial)
+ s'.context = s.context - style.replaces + style
+ all x: Style - style |
+ s'.value[x] = (style in x.^parents => x.initial else s.value[x])
+ }
+
+pred AddStyle [s, s': State, style: Style] {
+ AddStyleToContext [s,s',style]
+ AddStyleToNumbering [s,s',style]
+ }
+
+assert AddNeverReduces {
+ all s,s': State, z: Style |
+ Forest[s] && AddStyle [s,s',z] =>
+ (all y: s'.context | s'.value[y] in s.value[y].*next)
+ }
+
+check AddNeverReduces for 5 expect 1
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/railway.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/railway.als
new file mode 100644
index 00000000..549bdb92
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/railway.als
@@ -0,0 +1,64 @@
+module examples/toys/railway
+
+/*
+ * A simple model of a railway system. Trains sit on segments of tracks
+ * and segments overlap one another. It shows a that simple gate policy
+ * does not ensure train safety.
+ *
+ * author: Daniel Jackson
+ */
+
+sig Seg {next, overlaps: set Seg}
+fact {all s: Seg | s in s.overlaps}
+fact {all s1, s2: Seg | s1 in s2.overlaps => s2 in s1.overlaps}
+
+sig Train {}
+sig GateState {closed: set Seg}
+sig TrainState {on: Train -> lone Seg, occupied: set Seg}
+fact {all x: TrainState |
+ x.occupied = {s: Seg | some t: Train | t.(x.on) = s}
+ }
+
+pred Safe [x: TrainState] {all s: Seg | lone s.overlaps.~(x.on)}
+
+pred MayMove [g: GateState, x: TrainState, ts: set Train] {
+ no ts.(x.on) & g.closed
+ }
+
+pred TrainsMove [x, x': TrainState, ts: set Train] {
+ all t: ts | t.(x'.on) in t.(x.on).next
+ all t: Train - ts | t.(x'.on) = t.(x.on)
+ }
+
+pred GatePolicy [g: GateState, x: TrainState] {
+ x.occupied.overlaps.~next in g.closed
+ all s1, s2: Seg | some s1.next.overlaps & s2.next => lone (s1+s2) - g.closed
+}
+
+assert PolicyWorks {
+ all x, x': TrainState, g: GateState, ts: set Train |
+ {MayMove [g, x, ts]
+ TrainsMove [x, x', ts]
+ Safe [x]
+ GatePolicy [g, x]
+ } => Safe [x']
+ }
+
+-- has counterexample in scope of 4
+check PolicyWorks for 2 Train, 1 GateState, 2 TrainState, 4 Seg expect 1
+
+pred TrainsMoveLegal [x, x': TrainState, g: GateState, ts: set Train] {
+ TrainsMove [x, x', ts]
+ MayMove [g, x, ts]
+ GatePolicy [g, x]
+ }
+run TrainsMoveLegal for 3 expect 1
+
+
+
+// DEFINED VARIABLES
+// Defined variables are uncalled, no-argument functions.
+// They are helpful for getting good visualization.
+fun contains [] : TrainState -> Seg -> Train {
+ {state: TrainState, seg: Seg, train: Train | seg = train.(state.on)}
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/railway.thm b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/railway.thm
new file mode 100644
index 00000000..40baef57
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/railway.thm
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/trivial.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/trivial.als
new file mode 100644
index 00000000..71f19d3d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/toys/trivial.als
@@ -0,0 +1,8 @@
+//a trivial model whose command has no solution
+module trivial
+
+sig S {}
+
+fact { 1=2 }
+
+run {some S} expect 0
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/tutorial/farmer.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/tutorial/farmer.als
new file mode 100644
index 00000000..94ea0d5d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/examples/tutorial/farmer.als
@@ -0,0 +1,92 @@
+module examples/tutorial/farmer
+
+/*
+ * The classic river crossing puzzle. A farmer is carrying a fox, a
+ * chicken, and a sack of grain. He must cross a river using a boat
+ * that can only hold the farmer and at most one other thing. If the
+ * farmer leaves the fox alone with the chicken, the fox will eat the
+ * chicken; and if he leaves the chicken alone with the grain, the
+ * chicken will eat the grain. How can the farmer bring everything
+ * to the far side of the river intact?
+ *
+ * authors: Greg Dennis, Rob Seater
+ *
+ * Acknowledgements to Derek Rayside and his students for finding and
+ * fixing a bug in the "crossRiver" predicate.
+ */
+
+open util/ordering[State] as ord
+
+/**
+ * The farmer and all his possessions will be represented as Objects.
+ * Some objects eat other objects when the Farmer's not around.
+ */
+abstract sig Object { eats: set Object }
+one sig Farmer, Fox, Chicken, Grain extends Object {}
+
+/**
+ * Define what eats what when the Farmer' not around.
+ * Fox eats the chicken and the chicken eats the grain.
+ */
+fact eating { eats = Fox->Chicken + Chicken->Grain }
+
+/**
+ * The near and far relations contain the objects held on each
+ * side of the river in a given state, respectively.
+ */
+sig State {
+ near: set Object,
+ far: set Object
+}
+
+/**
+ * In the initial state, all objects are on the near side.
+ */
+fact initialState {
+ let s0 = ord/first |
+ s0.near = Object && no s0.far
+}
+
+/**
+ * Constrains at most one item to move from 'from' to 'to'.
+ * Also constrains which objects get eaten.
+ */
+pred crossRiver [from, from', to, to': set Object] {
+ // either the Farmer takes no items
+ (from' = from - Farmer - from'.eats and
+ to' = to + Farmer) or
+ // or the Farmer takes one item
+ (one x : from - Farmer | {
+ from' = from - Farmer - x - from'.eats
+ to' = to + Farmer + x })
+}
+
+/**
+ * crossRiver transitions between states
+ */
+fact stateTransition {
+ all s: State, s': ord/next[s] {
+ Farmer in s.near =>
+ crossRiver[s.near, s'.near, s.far, s'.far] else
+ crossRiver[s.far, s'.far, s.near, s'.near]
+ }
+}
+
+/**
+ * the farmer moves everything to the far side of the river.
+ */
+pred solvePuzzle {
+ ord/last.far = Object
+}
+
+run solvePuzzle for 8 State expect 1
+
+/**
+ * no Object can be in two places at once
+ * this is implied by both definitions of crossRiver
+ */
+assert NoQuantumObjects {
+ no s : State | some x : Object | x in s.near and x in s.far
+}
+
+check NoQuantumObjects for 8 State expect 0
\ No newline at end of file
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/boolean.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/boolean.als
new file mode 100644
index 00000000..61d8744e
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/boolean.als
@@ -0,0 +1,43 @@
+module util/boolean
+
+/*
+ * Creates a Bool type with two singleton subtypes: True
+ * and False. Provides common boolean operations.
+ *
+ * author: Greg Dennis
+ */
+
+abstract sig Bool {}
+one sig True, False extends Bool {}
+
+pred isTrue[b: Bool] { b in True }
+
+pred isFalse[b: Bool] { b in False }
+
+fun Not[b: Bool] : Bool {
+ Bool - b
+}
+
+fun And[b1, b2: Bool] : Bool {
+ subset_[b1 + b2, True]
+}
+
+fun Or[b1, b2: Bool] : Bool {
+ subset_[True, b1 + b2]
+}
+
+fun Xor[b1, b2: Bool] : Bool {
+ subset_[Bool, b1 + b2]
+}
+
+fun Nand[b1, b2: Bool] : Bool {
+ subset_[False, b1 + b2]
+}
+
+fun Nor[b1, b2: Bool] : Bool {
+ subset_[b1 + b2, False]
+}
+
+fun subset_[s1, s2: set Bool] : Bool {
+ (s1 in s2) => True else False
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/graph.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/graph.als
new file mode 100644
index 00000000..b722ac13
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/graph.als
@@ -0,0 +1,78 @@
+module util/graph[node]
+
+/*
+ * Utilities for some common operations and contraints
+ * on graphs.
+ *
+ * author: Greg Dennis
+ */
+
+open util/relation as rel
+
+/** graph in undirected */
+pred undirected [r: node->node] {
+ symmetric[r]
+}
+
+/** graph has no self-loops */
+pred noSelfLoops[r: node->node] {
+ irreflexive[r]
+}
+
+/** graph is weakly connected */
+pred weaklyConnected[r: node->node] {
+ all n1, n2: node | n1 in n2.*(r + ~r) // Changed from ^ to * to permit singleton
+}
+
+/** graph is strongly connected */
+pred stronglyConnected[r: node->node] {
+ all n1, n2: node | n1 in n2.*r // Changed from ^ to * to permit singleton
+}
+
+/** graph is rooted at root */
+pred rootedAt[r: node->node, root: node] {
+ node in root.*r
+}
+
+/** graph is a ring */
+pred ring [r: node->node] {
+ all n: node | one n.r && rootedAt[r, n]
+}
+
+/** graph is a dag */
+pred dag [r: node->node] {
+ acyclic[r, node]
+}
+
+/** graph is a forest */
+pred forest [r: node->node] {
+ dag[r]
+ all n: node | lone r.n
+}
+
+/** graph is a tree */
+pred tree [r: node->node] {
+ forest[r]
+ lone root: node | no r.root
+}
+
+/** graph is a tree rooted at root */
+pred treeRootedAt[r: node->node, root: node] {
+ forest[r]
+ rootedAt[r, root]
+}
+
+/** returns the roots of the graph */
+fun roots [r: node->node] : set node {
+ node - node.^r
+}
+
+/** returns the leaves of the grpah */
+fun leaves [r: node->node] : set node {
+ node - node.^~r
+}
+
+/** returns the inner nodes (non-leaves) of the graph */
+fun innerNodes [r: node->node] : set node {
+ node - leaves[r]
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/integer.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/integer.als
new file mode 100644
index 00000000..488c25e2
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/integer.als
@@ -0,0 +1,112 @@
+module util/integer
+
+/*
+ * A collection of utility functions for using Integers in Alloy.
+ * Note that integer overflows are silently truncated to the current bitwidth
+ * using the 2's complement arithmetic, unless the "forbid overfows" option is
+ * turned on, in which case only models that don't have any overflows are
+ * analyzed.
+ */
+
+fun add [n1, n2: Int] : Int { this/plus[n1, n2] }
+fun plus [n1, n2: Int] : Int { n1 fun/add n2 }
+
+fun sub [n1, n2: Int] : Int { this/minus[n1, n2] }
+fun minus [n1, n2: Int] : Int { n1 fun/sub n2 }
+
+fun mul [n1, n2: Int] : Int { n1 fun/mul n2 }
+
+/**
+ * Performs the division with "round to zero" semantics, except the following 3 cases
+ * 1) if a is 0, then it returns 0
+ * 2) else if b is 0, then it returns 1 if a is negative and -1 if a is positive
+ * 3) else if a is the smallest negative integer, and b is -1, then it returns a
+ */
+fun div [n1, n2: Int] : Int { n1 fun/div n2 }
+
+/** answer is defined to be the unique integer that satisfies "a = ((a/b)*b) + remainder" */
+fun rem [n1, n2: Int] : Int { n1 fun/rem n2 }
+
+/** negate */
+fun negate [n: Int] : Int { 0 fun/sub n }
+
+/** equal to */
+pred eq [n1, n2: Int] { int[n1] = int[n2] }
+
+/** greater than */
+pred gt [n1, n2: Int] { n1 > n2 }
+
+/** less then */
+pred lt [n1, n2: Int] { n1 < n2 }
+
+/** greater than or equal */
+pred gte [n1, n2: Int] { n1 >= n2 }
+
+/** less than or equal */
+pred lte [n1, n2: Int] { n1 <= n2 }
+
+/** integer is zero */
+pred zero [n: Int] { n = 0 }
+
+/** positive */
+pred pos [n: Int] { n > 0 }
+
+/** negative */
+pred neg [n: Int] { n < 0 }
+
+/** non-positive */
+pred nonpos [n: Int] { n <= 0 }
+
+/** non-negative */
+pred nonneg [n: Int] { n >= 0 }
+
+/** signum (aka sign or sgn) */
+fun signum [n: Int] : Int { n<0 => (0 fun/sub 1) else (n>0 => 1 else 0) }
+
+/**
+ * returns the ith element (zero-based) from the set s
+ * in the ordering of 'next', which is a linear ordering
+ * relation like that provided by util/ordering
+ */
+fun int2elem[i: Int, next: univ->univ, s: set univ] : lone s {
+ {e: s | #^next.e = int i }
+}
+
+/**
+ * returns the index of the element (zero-based) in the
+ * ordering of next, which is a linear ordering relation
+ * like that provided by util/ordering
+ */
+fun elem2int[e: univ, next: univ->univ] : lone Int {
+ Int[#^next.e]
+}
+
+/** returns the largest integer in the current bitwidth */
+fun max:one Int { fun/max }
+
+/** returns the smallest integer in the current bitwidth */
+fun min:one Int { fun/min }
+
+/** maps each integer (except max) to the integer after it */
+fun next:Int->Int { fun/next }
+
+/** maps each integer (except min) to the integer before it */
+fun prev:Int->Int { ~next }
+
+/** given a set of integers, return the largest element */
+fun max [es: set Int]: lone Int { es - es.^prev }
+
+/** given a set of integers, return the smallest element */
+fun min [es: set Int]: lone Int { es - es.^next }
+
+/** given an integer, return all integers prior to it */
+fun prevs [e: Int]: set Int { e.^prev }
+
+/** given an integer, return all integers following it */
+fun nexts [e: Int]: set Int { e.^next }
+
+/** returns the larger of the two integers */
+fun larger [e1, e2: Int]: Int { let a=int[e1], b=int[e2] | (a b else a) }
+
+/** returns the smaller of the two integers */
+fun smaller [e1, e2: Int]: Int { let a=int[e1], b=int[e2] | (a a else b) }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/natural.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/natural.als
new file mode 100644
index 00000000..5d9f6246
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/natural.als
@@ -0,0 +1,78 @@
+module util/natural
+
+/*
+ * Utility function and predicates for using the set of
+ * nonnegative integers (0, 1, 2, . . .). The number of
+ * naturals present in an analysis will be equal to the
+ * scope on Natural. Specifically, if the scope on Natural
+ * is N, then the naturals 0 through N-1 will be present.
+ *
+ * Note that the functions that return Naturals, such as
+ * 'add' and 'div', may return an empty set if no such
+ * Natural exists for that integer value.
+ *
+ * To write an Alloy model that makes use of negative
+ * integers, use the util/integer module instead.
+ *
+ * @author Greg Dennis
+ */
+
+private open util/ordering[Natural] as ord
+private open util/integer as integer
+
+sig Natural {}
+
+/** the integer zero */
+one sig Zero in Natural {}
+
+/** the integer one will be the empty set if the scope on Natural is less than two */
+lone sig One in Natural {}
+
+fact {
+ first in Zero
+ next[first] in One
+}
+
+/** returns n + 1 */
+fun inc [n: Natural] : lone Natural { ord/next[n] }
+
+/** returns n - 1 */
+fun dec [n: Natural] : lone Natural { ord/prev[n] }
+
+/** returns n1 + n2 */
+fun add [n1, n2: Natural] : lone Natural {
+ {n: Natural | #ord/prevs[n] = #ord/prevs[n1] + #ord/prevs[n2]}
+}
+
+/** returns n1 - n2 */
+fun sub [n1, n2: Natural] : lone Natural {
+ {n: Natural | #ord/prevs[n1] = #ord/prevs[n2] + #ord/prevs[n]}
+}
+
+/** returns n1 * n2 */
+fun mul [n1, n2: Natural] : lone Natural {
+ {n: Natural | #ord/prevs[n] = #(ord/prevs[n1]->ord/prevs[n2])}
+}
+
+/** returns n1 / n2 */
+fun div [n1, n2: Natural] : lone Natural {
+ {n: Natural | #ord/prevs[n1] = #(ord/prevs[n2]->ord/prevs[n])}
+}
+
+/** returns true iff n1 is greater than n2 */
+pred gt [n1, n2: Natural] { ord/gt [n1, n2] }
+
+/** returns true iff n1 is less than n2 */
+pred lt [n1, n2: Natural] { ord/lt [n1, n2] }
+
+/** returns true iff n1 is greater than or equal to n2 */
+pred gte [n1, n2: Natural] { ord/gte[n1, n2] }
+
+/** returns true iff n1 is less than or equal to n2 */
+pred lte [n1, n2: Natural] { ord/lte[n1, n2] }
+
+/** returns the maximum integer in ns */
+fun max [ns: set Natural] : lone Natural { ord/max[ns] }
+
+/** returns the minimum integer in ns */
+fun min [ns: set Natural] : lone Natural { ord/min[ns] }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/ordering.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/ordering.als
new file mode 100644
index 00000000..e26d7bd0
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/ordering.als
@@ -0,0 +1,106 @@
+module util/ordering[exactly elem]
+
+/*
+ * Creates a single linear ordering over the atoms in elem. It also constrains all
+ * the atoms to exist that are permitted by the scope on elem. That is, if the scope
+ * on a signature S is 5, opening util/ordering[S] will force S to have 5 elements
+ * and create a linear ordering over those five elements. The predicates and
+ * functions below provide access to properties of the linear ordering, such as
+ * which element is first in the ordering, or whether a given element precedes
+ * another. You cannotcreate multiple linear orderings over the same signature with
+ * this model. If you that functionality, try using the util/sequence module instead.
+ *
+ * Technical comment:
+ * An important constraint: elem must contain all atoms permitted by the scope.
+ * This is to let the analyzer optimize the analysis by setting all fields of each
+ * instantiation of Ord to predefined values: e.g. by setting 'last' to the highest
+ * atom of elem and by setting 'next' to {,,...}, where n is
+ * the scope of elem. Without this constraint, it might not be true that Ord.last is
+ * a subset of elem, and that the domain and range of Ord.next lie inside elem.
+ *
+ * author: Ilya Shlyakhter
+ * revisions: Daniel jackson
+ */
+
+private one sig Ord {
+ First: set elem,
+ Next: elem -> elem
+} {
+ pred/totalOrder[elem,First,Next]
+}
+
+/** first */
+fun first: one elem { Ord.First }
+
+/** last */
+fun last: one elem { elem - (next.elem) }
+
+/** return a mapping from each element to its predecessor */
+fun prev : elem->elem { ~(Ord.Next) }
+
+/** return a mapping from each element to its successor */
+fun next : elem->elem { Ord.Next }
+
+/** return elements prior to e in the ordering */
+fun prevs [e: elem]: set elem { e.^(~(Ord.Next)) }
+
+/** return elements following e in the ordering */
+fun nexts [e: elem]: set elem { e.^(Ord.Next) }
+
+/** e1 is less than e2 in the ordering */
+pred lt [e1, e2: elem] { e1 in prevs[e2] }
+
+/** e1 is greater than e2 in the ordering */
+pred gt [e1, e2: elem] { e1 in nexts[e2] }
+
+/** e1 is less than or equal to e2 in the ordering */
+pred lte [e1, e2: elem] { e1=e2 || lt [e1,e2] }
+
+/** e1 is greater than or equal to e2 in the ordering */
+pred gte [e1, e2: elem] { e1=e2 || gt [e1,e2] }
+
+/** returns the larger of the two elements in the ordering */
+fun larger [e1, e2: elem]: elem { lt[e1,e2] => e2 else e1 }
+
+/** returns the smaller of the two elements in the ordering */
+fun smaller [e1, e2: elem]: elem { lt[e1,e2] => e1 else e2 }
+
+/**
+ * returns the largest element in es
+ * or the empty set if es is empty
+ */
+fun max [es: set elem]: lone elem { es - es.^(~(Ord.Next)) }
+
+/**
+ * returns the smallest element in es
+ * or the empty set if es is empty
+ */
+fun min [es: set elem]: lone elem { es - es.^(Ord.Next) }
+
+assert correct {
+ let mynext = Ord.Next |
+ let myprev = ~mynext | {
+ ( all b:elem | (lone b.next) && (lone b.prev) && (b !in b.^mynext) )
+ ( (no first.prev) && (no last.next) )
+ ( all b:elem | (b!=first && b!=last) => (one b.prev && one b.next) )
+ ( !one elem => (one first && one last && first!=last && one first.next && one last.prev) )
+ ( one elem => (first=elem && last=elem && no myprev && no mynext) )
+ ( myprev=~mynext )
+ ( elem = first.*mynext )
+ (all disj a,b:elem | a in b.^mynext or a in b.^myprev)
+ (no disj a,b:elem | a in b.^mynext and a in b.^myprev)
+ (all disj a,b,c:elem | (b in a.^mynext and c in b.^mynext) =>(c in a.^mynext))
+ (all disj a,b,c:elem | (b in a.^myprev and c in b.^myprev) =>(c in a.^myprev))
+ }
+}
+run {} for exactly 0 elem expect 0
+run {} for exactly 1 elem expect 1
+run {} for exactly 2 elem expect 1
+run {} for exactly 3 elem expect 1
+run {} for exactly 4 elem expect 1
+check correct for exactly 0 elem
+check correct for exactly 1 elem
+check correct for exactly 2 elem
+check correct for exactly 3 elem
+check correct for exactly 4 elem
+check correct for exactly 5 elem
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/relation.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/relation.als
new file mode 100644
index 00000000..d648a77c
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/relation.als
@@ -0,0 +1,101 @@
+module util/relation
+
+/*
+ * Utilities for some common operations and constraints
+ * on binary relations. The keyword 'univ' represents the
+ * top-level type, which all other types implicitly extend.
+ * Therefore, all the functions and predicates in this model
+ * may be applied to binary relations of any type.
+ *
+ * author: Greg Dennis
+ */
+
+/** returns the domain of a binary relation */
+fun dom [r: univ->univ] : set (r.univ) { r.univ }
+
+/** returns the range of a binary relation */
+fun ran [r: univ->univ] : set (univ.r) { univ.r }
+
+/** r is total over the domain s */
+pred total [r: univ->univ, s: set univ] {
+ all x: s | some x.r
+}
+
+/** r is a partial function over the domain s */
+pred functional [r: univ->univ, s: set univ] {
+ all x: s | lone x.r
+}
+
+/** r is a total function over the domain s */
+pred function [r: univ->univ, s: set univ] {
+ all x: s | one x.r
+}
+
+/** r is surjective over the codomain s */
+pred surjective [r: univ->univ, s: set univ] {
+ all x: s | some r.x
+}
+
+/** r is injective */
+pred injective [r: univ->univ, s: set univ] {
+ all x: s | lone r.x
+}
+
+/** r is bijective over the codomain s */
+pred bijective[r: univ->univ, s: set univ] {
+ all x: s | one r.x
+}
+
+/** r is a bijection over the domain d and the codomain c */
+pred bijection[r: univ->univ, d, c: set univ] {
+ function[r, d] && bijective[r, c]
+}
+
+/** r is reflexive over the set s */
+pred reflexive [r: univ -> univ, s: set univ] {s<:iden in r}
+
+/** r is irreflexive */
+pred irreflexive [r: univ -> univ] {no iden & r}
+
+/** r is symmetric */
+pred symmetric [r: univ -> univ] {~r in r}
+
+/** r is anti-symmetric */
+pred antisymmetric [r: univ -> univ] {~r & r in iden}
+
+/** r is transitive */
+pred transitive [r: univ -> univ] {r.r in r}
+
+/** r is acyclic over the set s */
+pred acyclic[r: univ->univ, s: set univ] {
+ all x: s | x !in x.^r
+}
+
+/** r is complete over the set s */
+pred complete[r: univ->univ, s: univ] {
+ all x,y:s | (x!=y => x->y in (r + ~r))
+}
+
+/** r is a preorder (or a quasi-order) over the set s */
+pred preorder [r: univ -> univ, s: set univ] {
+ reflexive[r, s]
+ transitive[r]
+}
+
+/** r is an equivalence relation over the set s */
+pred equivalence [r: univ->univ, s: set univ] {
+ preorder[r, s]
+ symmetric[r]
+}
+
+/** r is a partial order over the set s */
+pred partialOrder [r: univ -> univ, s: set univ] {
+ preorder[r, s]
+ antisymmetric[r]
+}
+
+/** r is a total order over the set s */
+pred totalOrder [r: univ -> univ, s: set univ] {
+ partialOrder[r, s]
+ complete[r, s]
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/seqrel.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/seqrel.als
new file mode 100644
index 00000000..1fe3802d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/seqrel.als
@@ -0,0 +1,108 @@
+module util/seqrel[elem]
+
+/*
+ * A sequence utility for modeling sequences as just a
+ * relation as opposed to reifying them into sequence
+ * atoms like the util/sequence module does.
+ *
+ * @author Greg Dennis
+ */
+
+open util/integer
+open util/ordering[SeqIdx] as ord
+
+sig SeqIdx {}
+
+/** sequence covers a prefix of SeqIdx */
+pred isSeq[s: SeqIdx -> elem] {
+ s in SeqIdx -> lone elem
+ s.inds - ord/next[s.inds] in ord/first
+}
+
+/** returns all the elements in this sequence */
+fun elems [s: SeqIdx -> elem]: set elem { SeqIdx.s }
+
+/** returns the first element in the sequence */
+fun first [s: SeqIdx -> elem]: lone elem { s[ord/first] }
+
+/** returns the last element in the sequence */
+fun last [s: SeqIdx -> elem]: lone elem { s[lastIdx[s]] }
+
+/** returns the cdr of the sequence */
+fun rest [s: SeqIdx -> elem] : SeqIdx -> elem {
+ (ord/next).s
+}
+
+/** returns all but the last element of the sequence */
+fun butlast [s: SeqIdx -> elem] : SeqIdx -> elem {
+ (SeqIdx - lastIdx[s]) <: s
+}
+
+/** true if the sequence is empty */
+pred isEmpty [s: SeqIdx -> elem] { no s }
+
+/** true if this sequence has duplicates */
+pred hasDups [s: SeqIdx -> elem] { # elems[s] < # inds[s] }
+
+/** returns all the indices occupied by this sequence */
+fun inds [s: SeqIdx -> elem]: set SeqIdx { s.elem }
+
+/** returns last index occupied by this sequence */
+fun lastIdx [s: SeqIdx -> elem]: lone SeqIdx { ord/max[inds[s]] }
+
+/**
+ * returns the index after the last index
+ * if this sequence is empty, returns the first index,
+ * if this sequence is full, returns empty set
+ */
+fun afterLastIdx [s: SeqIdx -> elem] : lone SeqIdx {
+ ord/min[SeqIdx - inds[s]]
+}
+
+/** returns first index at which given element appears or the empty set if it doesn't */
+fun idxOf [s: SeqIdx -> elem, e: elem] : lone SeqIdx { ord/min[indsOf[s, e]] }
+
+/** returns last index at which given element appears or the empty set if it doesn't */
+fun lastIdxOf [s: SeqIdx -> elem, e: elem] : lone SeqIdx { ord/max[indsOf[s, e]] }
+
+/** returns set of indices at which given element appears or the empty set if it doesn't */
+fun indsOf [s: SeqIdx -> elem, e: elem] : set SeqIdx { s.e }
+
+/**
+ * return the result of appending e to the end of s
+ * just returns s if s exhausted SeqIdx
+ */
+fun add [s: SeqIdx -> elem, e: elem] : SeqIdx -> elem {
+ setAt[s, afterLastIdx[s], e]
+}
+
+/** returns the result of setting the value at index i in sequence to e */
+fun setAt [s: SeqIdx -> elem, i: SeqIdx, e: elem] : SeqIdx -> elem {
+ s ++ i -> e
+}
+
+/** returns the result of inserting value e at index i */
+fun insert [s: SeqIdx -> elem, i: SeqIdx, e: elem] : SeqIdx -> elem {
+ (ord/prevs[i] <: s) + (i->e) + (~(ord/next)).((ord/nexts[i] + i) <: s)
+}
+
+/** returns the result of deleting the value at index i */
+fun delete[s: SeqIdx -> elem, i: SeqIdx] : SeqIdx -> elem {
+ (ord/prevs[i] <: s) + (ord/next).(ord/nexts[i] <: s)
+}
+
+/** appended is the result of appending s2 to s1 */
+fun append [s1, s2: SeqIdx -> elem] : SeqIdx -> elem {
+ let shift = {i', i: SeqIdx | #ord/prevs[i'] = add[#ord/prevs[i], add[#ord/prevs[lastIdx[s1]], 1]] } |
+ s1 + shift.s2
+}
+
+/** returns the subsequence of s between from and to, inclusive */
+fun subseq [s: SeqIdx -> elem, from, to: SeqIdx] : SeqIdx -> elem {
+ let shift = {i', i: SeqIdx | #ord/prevs[i'] = sub[#ord/prevs[i], #ord/prevs[from]] } |
+ shift.((SeqIdx - ord/nexts[to]) <: s)
+}
+
+fun firstIdx: SeqIdx { ord/first }
+
+fun finalIdx: SeqIdx { ord/last }
\ No newline at end of file
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/sequence.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/sequence.als
new file mode 100644
index 00000000..524e3cf7
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/sequence.als
@@ -0,0 +1,166 @@
+module util/sequence[elem]
+
+/*
+ * Creates sequences (or lists) of elements. The ordered signature SeqIdx
+ * represents the indexes at which elements can be located, and a sequence
+ * is modeled as a mapping from indexes to elements. Empty sequences are
+ * allowed, and a sequence may have a single element appear multiple times.
+ * Maximum length of a sequence is determined by the scope of SeqIdx.
+ *
+ * Sequences always cover an initial segment of SeqIdx. That is, every
+ * sequence (except the empty sequence) begins at the first SeqIdx and does
+ * not have gaps in indexes that it covers. In other words, if a sequence has
+ * its last element at index i, then the sequence has elements at all the
+ * indexes that precede i.
+ *
+ * Oftentimes, a model will need to require that all sequences that could
+ * exist, do exist. Calling the allExist predicate will ensure that that there is
+ * a Seq atom for each possible sequences of elements with length less than
+ * or equal to the scope of SeqIdx.
+ *
+ * The functions and predicates at the bottom of this module provide all
+ * functionality of util/ordering on SeqIdx.
+ *
+ * revisions: Greg Dennis
+ */
+
+open util/ordering[SeqIdx] as ord
+
+sig SeqIdx {}
+
+sig Seq {
+ seqElems: SeqIdx -> lone elem
+}
+{
+ // Ensure that elems covers only an initial segment of SeqIdx,
+ // equal to the length of the signature
+ all i: SeqIdx - ord/first | some i.seqElems => some ord/prev[i].seqElems
+}
+
+/** no two sequences are identical */
+fact canonicalizeSeqs {
+ no s1, s2: Seq | s1!=s2 && s1.seqElems=s2.seqElems
+}
+
+/** invoke if you want none of the sequences to have duplicates */
+pred noDuplicates {
+ all s: Seq | !s.hasDups
+}
+
+/** invoke if you want all sequences within scope to exist */
+pred allExist {
+ (some s: Seq | s.isEmpty) &&
+ (all s: Seq | SeqIdx !in s.inds => (all e: elem | some s': Seq | s.add[e, s']))
+}
+
+/** invoke if you want all sequences within scope with no duplicates */
+pred allExistNoDuplicates {
+ some s: Seq | s.isEmpty
+ all s: Seq {
+ !s.hasDups
+ SeqIdx !in s.inds => (all e: elem - s.elems | some s': Seq | s.add[e, s'])
+ }
+}
+
+/** returns element at the given index */
+fun at [s: Seq, i: SeqIdx]: lone elem { i.(s.seqElems) }
+
+/** returns all the elements in this sequence */
+fun elems [s: Seq]: set elem { SeqIdx.(s.seqElems) }
+
+/** returns the first element in the sequence */
+fun first [s:Seq]: lone elem { s.at[ord/first] }
+
+/** returns the last element in the sequence */
+fun last [s:Seq]: lone elem { s.at[s.lastIdx] }
+
+/**
+ * true if the argument is the "cdr" of this sequence
+ * false if this sequence is empty
+ */
+pred rest [s, r: Seq] {
+ !s.isEmpty
+ all i: SeqIdx | r.at[i] = s.at[ord/next[i]]
+}
+
+/** true if the sequence is empty */
+pred isEmpty [s:Seq] { no s.elems }
+
+/** true if this sequence has duplicates */
+pred hasDups [s:Seq] { # elems[s] < # inds[s] }
+
+/** returns all the indices occupied by this sequence */
+fun inds [s:Seq] : set SeqIdx { elem.~(s.seqElems) }
+
+/** returns last index occupied by this sequence */
+fun lastIdx [s:Seq] : lone SeqIdx { ord/max[s.inds] }
+
+/**
+ * returns the index after the last index
+ * if this sequence is empty, returns the first index,
+ * if this sequence is full, returns empty set
+ */
+fun afterLastIdx [s:Seq] : lone SeqIdx {
+ ord/min[SeqIdx - s.inds]
+}
+
+/** returns first index at which given element appears or the empty set if it doesn't */
+fun idxOf [s: Seq, e: elem] : lone SeqIdx { ord/min[s.indsOf[e]] }
+
+/** returns last index at which given element appears or the empty set if it doesn't */
+fun lastIdxOf [s: Seq, e: elem] : lone SeqIdx { ord/max[s.indsOf[e]] }
+
+/** returns set of indices at which given element appears or the empty set if it doesn't */
+fun indsOf [s: Seq, e: elem] : set SeqIdx { (s.seqElems).e }
+
+/** true if this starts with prefix */
+pred startsWith [s, prefix: Seq] {
+ all i: prefix.inds | s.at[i] = prefix.at[i]
+}
+
+/** added is the result of appending e to the end of s */
+pred add [s: Seq, e: elem, added: Seq] {
+ added.startsWith[s]
+ added.seqElems[s.afterLastIdx] = e
+ #added.inds = #s.inds.add[1]
+}
+
+/** setted is the result of setting value at index i to e */
+pred setAt [s: Seq, idx: SeqIdx, e: elem, setted: Seq] {
+ setted.seqElems = s.seqElems ++ idx->e
+}
+
+/** inserts is the result of inserting value e at index i */
+pred insert [s: Seq, idx: SeqIdx, e: elem, inserted: Seq] {
+ inserted.at[idx] = e
+ all i: ord/prevs[idx] | inserted.at[i] = s.at[i]
+ all i: ord/nexts[idx] | inserted.at[i] = s.at[ord/prev[i]]
+ #inserted.inds = #s.inds.add[1]
+}
+
+/** copies source into dest starting at destStart */
+pred copy [source, dest: Seq, destStart: SeqIdx] {
+ all sourceIdx : source.inds | some destIdx: SeqIdx {
+ ord/gte[destIdx, destStart]
+ dest.at[destIdx] = source.at[sourceIdx]
+ #ord/prevs[sourceIdx] = #(ord/prevs[destIdx] - ord/prevs[destStart])
+ }
+}
+
+/** appended is the result of appending s2 to s1 */
+pred append [s1, s2, appended: Seq] {
+ appended.startsWith[s1]
+ copy[s2, appended, s1.afterLastIdx]
+ #appended.inds = #s1.inds.add[#s2.inds]
+}
+
+/** sub is the subsequence of s between from and to, inclusive */
+pred subseq [s, sub: Seq, from, to: SeqIdx] {
+ ord/lte[from, to]
+ copy[sub, s, from]
+ #sub.inds = #(to + ord/prevs[to] - ord/prevs[from])
+}
+
+fun firstIdx: SeqIdx { ord/first }
+
+fun finalIdx: SeqIdx { ord/last }
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/sequniv.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/sequniv.als
new file mode 100644
index 00000000..efa8bf42
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/sequniv.als
@@ -0,0 +1,137 @@
+module util/sequniv
+
+open util/integer as ui
+
+/*
+ * NOTE: Do not include this module manually.
+ * Instead, use the "seq" keyword which will automatically
+ * import this module with the correct additional constraints as needed.
+ */
+
+/*
+ * A sequence utility for modeling sequences as just a
+ * relation as opposed to reifying them into sequence
+ * atoms like the util/sequence module does.
+ *
+ * Precondition: each input sequence must range over a prefix
+ * of seq/Int.
+ *
+ * Postcondition: we guarantee the returned sequence
+ * also ranges over a prefix of seq/Int.
+ *
+ * @author Greg Dennis
+ */
+
+/** sequence covers a prefix of seq/Int */
+pred isSeq[s: Int -> univ] {
+ s in seq/Int -> lone univ
+ s.inds - ui/next[s.inds] in 0
+}
+
+/** returns all the elements in this sequence */
+fun elems [s: Int -> univ]: set (Int.s) { seq/Int . s }
+
+/**
+ * returns the first element in the sequence
+ * (Returns the empty set if the sequence is empty)
+ */
+fun first [s: Int -> univ]: lone (Int.s) { s[0] }
+
+/**
+ * returns the last element in the sequence
+ * (Returns the empty set if the sequence is empty)
+ */
+fun last [s: Int -> univ]: lone (Int.s) { s[lastIdx[s]] }
+
+/**
+ * returns the cdr of the sequence
+ * (Returns the empty sequence if the sequence has 1 or fewer element)
+ */
+fun rest [s: Int -> univ] : s { seq/Int <: ((ui/next).s) }
+
+/** returns all but the last element of the sequence */
+fun butlast [s: Int -> univ] : s {
+ (seq/Int - lastIdx[s]) <: s
+}
+
+/** true if the sequence is empty */
+pred isEmpty [s: Int -> univ] { no s }
+
+/** true if this sequence has duplicates */
+pred hasDups [s: Int -> univ] { # elems[s] < # inds[s] }
+
+/** returns all the indices occupied by this sequence */
+fun inds [s: Int -> univ]: set Int { s.univ }
+
+/**
+ * returns last index occupied by this sequence
+ * (Returns the empty set if the sequence is empty)
+ */
+fun lastIdx [s: Int -> univ]: lone Int { ui/max[inds[s]] }
+
+/**
+ * returns the index after the last index
+ * if this sequence is empty, returns 0
+ * if this sequence is full, returns empty set
+ */
+fun afterLastIdx [s: Int -> univ] : lone Int { ui/min[seq/Int - inds[s]] }
+
+/** returns first index at which given element appears or the empty set if it doesn't */
+fun idxOf [s: Int -> univ, e: univ] : lone Int { ui/min[indsOf[s, e]] }
+
+/** returns last index at which given element appears or the empty set if it doesn't */
+fun lastIdxOf [s: Int -> univ, e: univ] : lone Int { ui/max[indsOf[s, e]] }
+
+/** returns set of indices at which given element appears or the empty set if it doesn't */
+fun indsOf [s: Int -> univ, e: univ] : set Int { s.e }
+
+/**
+ * return the result of appending e to the end of s
+ * (returns s if s exhausted seq/Int)
+ */
+fun add [s: Int -> univ, e: univ] : s + (seq/Int->e) {
+ setAt[s, afterLastIdx[s], e]
+}
+
+/**
+ * returns the result of setting the value at index i in sequence to e
+ * Precondition: 0 <= i < #s
+ */
+fun setAt [s: Int -> univ, i: Int, e: univ] : s + (seq/Int->e) {
+ s ++ i -> e
+}
+
+/**
+ * returns the result of inserting value e at index i
+ * (if sequence was full, the original last element will be removed first)
+ * Precondition: 0 <= i <= #s
+ */
+fun insert [s: Int -> univ, i: Int, e: univ] : s + (seq/Int->e) {
+ seq/Int <: ((ui/prevs[i] <: s) + (i->e) + ui/prev.((ui/nexts[i] + i) <: s))
+}
+
+/**
+ * returns the result of deleting the value at index i
+ * Precondition: 0 <= i < #s
+ */
+fun delete[s: Int -> univ, i: Int] : s {
+ (ui/prevs[i] <: s) + (ui/next).(ui/nexts[i] <: s)
+}
+
+/**
+ * appended is the result of appending s2 to s1
+ * (If the resulting sequence is too long, it will be truncated)
+ */
+fun append [s1, s2: Int -> univ] : s1+s2 {
+ let shift = {i', i: seq/Int | int[i'] = ui/add[int[i], ui/add[int[lastIdx[s1]], 1]] } |
+ no s1 => s2 else (s1 + shift.s2)
+}
+
+/**
+ * returns the subsequence of s between from and to, inclusive
+ * Precondition: 0 <= from <= to < #s
+ */
+fun subseq [s: Int -> univ, from, to: Int] : s {
+ let shift = {i', i: seq/Int | int[i'] = ui/sub[int[i], int[from]] } |
+ shift.((seq/Int - ui/nexts[to]) <: s)
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/ternary.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/ternary.als
new file mode 100644
index 00000000..6dbc0881
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/ternary.als
@@ -0,0 +1,50 @@
+module util/ternary
+
+/*
+ * Utilities for some common operations and constraints
+ * on ternary relations. The keyword 'univ' represents the
+ * top-level type, which all other types implicitly extend.
+ * Therefore, all the functions and predicates in this model
+ * may be applied to ternary relations of any type.
+ *
+ * author: Greg Dennis
+ */
+
+/** returns the domain of a ternary relation */
+fun dom [r: univ->univ->univ] : set ((r.univ).univ) { (r.univ).univ }
+
+/** returns the range of a ternary relation */
+fun ran [r: univ->univ->univ] : set (univ.(univ.r)) { univ.(univ.r) }
+
+/** returns the "middle range" of a ternary relation */
+fun mid [r: univ->univ->univ] : set (univ.(r.univ)) { univ.(r.univ) }
+
+/** returns the first two columns of a ternary relation */
+fun select12 [r: univ->univ->univ] : r.univ {
+ r.univ
+}
+
+/** returns the first and last columns of a ternary relation */
+fun select13 [r: univ->univ->univ] : ((r.univ).univ) -> (univ.(univ.r)) {
+ {x: (r.univ).univ, z: univ.(univ.r) | some (x.r).z}
+}
+
+/** returns the last two columns of a ternary relation */
+fun select23 [r: univ->univ->univ] : univ.r {
+ univ.r
+}
+
+/** flips the first two columns of a ternary relation */
+fun flip12 [r: univ->univ->univ] : (univ.(r.univ))->((r.univ).univ)->(univ.(univ.r)) {
+ {x: univ.(r.univ), y: (r.univ).univ, z: univ.(univ.r) | y->x->z in r}
+}
+
+/** flips the first and last columns of a ternary relation */
+fun flip13 [r: univ->univ->univ] : (univ.(univ.r))->(univ.(r.univ))->((r.univ).univ) {
+ {x: univ.(univ.r), y: univ.(r.univ), z: (r.univ).univ | z->y->x in r}
+}
+
+/** flips the last two columns of a ternary relation */
+fun flip23 [r: univ->univ->univ] : ((r.univ).univ)->(univ.(univ.r))->(univ.(r.univ)) {
+ {x: (r.univ).univ, y: univ.(univ.r), z: univ.(r.univ) | x->z->y in r}
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/models/util/time.als b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/time.als
new file mode 100644
index 00000000..b6f9d8ca
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/models/util/time.als
@@ -0,0 +1,53 @@
+open util/ordering[Time]
+
+sig Time { }
+
+let dynamic[x] = x one-> Time
+
+let dynamicSet[x] = x -> Time
+
+let then [a, b, t, t'] {
+ some x:Time | a[t,x] && b[x,t']
+}
+
+let while = while3
+
+let while9 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while8[cond,body,x,t']
+}
+
+let while8 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while7[cond,body,x,t']
+}
+
+let while7 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while6[cond,body,x,t']
+}
+
+let while6 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while5[cond,body,x,t']
+}
+
+let while5 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while4[cond,body,x,t']
+}
+
+let while4 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while3[cond,body,x,t']
+}
+
+let while3 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while2[cond,body,x,t']
+}
+
+let while2 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while1[cond,body,x,t']
+}
+
+let while1 [cond, body, t, t'] {
+ some x:Time | (cond[t] => body[t,x] else t=x) && while0[cond,body,x,t']
+}
+
+let while0 [cond, body, t, t'] {
+ !cond[t] && t=t'
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/org/sat4j/messages.properties b/Source/eu.modelwriter.alloyanalyzer/bin/org/sat4j/messages.properties
new file mode 100644
index 00000000..2b3f0f00
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/org/sat4j/messages.properties
@@ -0,0 +1,8 @@
+Lanceur.wrong.framework=Wrong framework: try minisat or ubcsat
+MoreThanSAT.0=Satisfiable \!
+MoreThanSAT.1=BackBone:
+MoreThanSAT.2=Counting solutions...
+MoreThanSAT.3=Number of solutions:
+MoreThanSAT.4=Unsatisfiable\!
+MoreThanSAT.5=Unsatisfiable (trivial)\!
+MoreThanSAT.6=Timeout, sorry\!
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/sat4j.version b/Source/eu.modelwriter.alloyanalyzer/bin/sat4j.version
new file mode 100644
index 00000000..a27f8f26
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/sat4j.version
@@ -0,0 +1 @@
+2.3.2.v20120709
diff --git a/Source/eu.modelwriter.alloyanalyzer/bin/target/META-INF/MANIFEST.MF b/Source/eu.modelwriter.alloyanalyzer/bin/target/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..f14a9926
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/bin/target/META-INF/MANIFEST.MF
@@ -0,0 +1,31 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %bundleName
+Bundle-SymbolicName: org.sat4j.core
+Bundle-Version: 2.3.2.v20120709
+Export-Package: org.sat4j;version="2.3.2.v20120709",
+ org.sat4j.core;version="2.3.2.v20120709",
+ org.sat4j.minisat;version="2.3.2.v20120709",
+ org.sat4j.minisat.constraints;version="2.3.2.v20120709",
+ org.sat4j.minisat.constraints.card;version="2.3.2.v20120709",
+ org.sat4j.minisat.constraints.cnf;version="2.3.2.v20120709",
+ org.sat4j.minisat.core;version="2.3.2.v20120709",
+ org.sat4j.minisat.learning;version="2.3.2.v20120709",
+ org.sat4j.minisat.orders;version="2.3.2.v20120709",
+ org.sat4j.minisat.restarts;version="2.3.2.v20120709",
+ org.sat4j.opt;version="2.3.2.v20120709",
+ org.sat4j.reader;version="2.3.2.v20120709",
+ org.sat4j.specs;version="2.3.2.v20120709",
+ org.sat4j.tools;version="2.3.2.v20120709",
+ org.sat4j.tools.xplain;version="2.3.2.v20120709"
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Built-By: Daniel Le Berre
+Main-Class: org.sat4j.BasicLauncher
+Specification-Title: SAT4J
+Specification-Version: NA
+Specification-Vendor: Daniel Le Berre
+Implementation-Title: SAT4J
+Implementation-Version: 2.3.2.v20120709
+Implementation-Vendor: CRIL CNRS UMR 8188 - Universite d'Artois
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/Source/eu.modelwriter.alloyanalyzer/build.properties b/Source/eu.modelwriter.alloyanalyzer/build.properties
new file mode 100644
index 00000000..20001ebb
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/build.properties
@@ -0,0 +1,9 @@
+source.alloyanalyzer.jar = src/
+bin.includes = META-INF/,\
+ lib/alloy4.2.jar,\
+ alloyanalyzer.jar,\
+ bin/,\
+ build.properties,\
+ .settings/,\
+ .project,\
+ .classpath
diff --git a/Source/eu.modelwriter.alloyanalyzer/lib/alloy4.2.jar b/Source/eu.modelwriter.alloyanalyzer/lib/alloy4.2.jar
new file mode 100644
index 00000000..3be21612
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/lib/alloy4.2.jar differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/META-INF/MANIFEST.MF b/Source/eu.modelwriter.alloyanalyzer/src/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..c10f81ea
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Created-By: 1.5.0 (Sun Microsystems Inc.)
+Main-Class: edu.mit.csail.sdg.alloy4whole.SimpleGUI
+
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/README.TXT b/Source/eu.modelwriter.alloyanalyzer/src/README.TXT
new file mode 100644
index 00000000..7cd623ea
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/README.TXT
@@ -0,0 +1,63 @@
+The Alloy Analyzer
+
+ The Alloy Analyzer is a tool developed by the Software Design
+ Group (http://sdg.csail.mit.edu/) for analyzing models written in
+ Alloy, a simple structural modeling language based on first-order
+ logic. The tool can generate instances of invariants, simulate
+ the execution of operations (even those defined implicitly), and
+ check user-specified properties of a model. Alloy and its
+ analyzer have been used primarily to explore abstract software
+ designs. Its use in analyzing code for conformance to a
+ specification and as an automatic test case generator are being
+ investigated in ongoing research projects.
+
+ See the web page for a description of what's new in Alloy:
+
+ http://alloy.mit.edu/
+
+
+Detailed Instructions:
+
+ 1. Java 5 or later
+
+ Java runtimes are available at no economic charge from Sun and
+ IBM and others. One may have come pre-installed in your OS.
+ Alloy does not currently work with gcj because of its limited
+ library support.
+
+ 2. Running Alloy on Mac OS X
+
+ Just double-click on the dmg file,
+ then drag the Alloy application into your application directory.
+
+ 3. Running Alloy on other platforms
+
+ Just double-click on the jar file, or type:
+
+ java -jar alloy4.jar
+
+The source code for the Alloy Analyzer is available
+under the MIT license.
+
+The Alloy Analyzer utilizes several third-party packages whose code may
+be distributed under a different license (see the various LICENSE files
+in the distribution for details). We are extremely grateful to the authors
+of these packages for making their source code freely available.
+
+ * Kodkod
+ http://web.mit.edu/~emina/www/kodkod.html
+
+ * CUP Parser Generator for Java
+ http://www2.cs.tum.edu/projects/cup/
+
+ * JFlex scanner generator for Java
+ http://jflex.de/
+
+ * The zChaff solver
+ http://www.princeton.edu/~chaff/zchaff.html
+
+ * The MiniSat solver
+ http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/
+
+ * The SAT4J solver
+ http://www.sat4j.org/
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/README.md b/Source/eu.modelwriter.alloyanalyzer/src/README.md
new file mode 100644
index 00000000..9942d4bc
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/README.md
@@ -0,0 +1,45 @@
+Alloy Analyzer (source code mirror)
+===================================
+
+Summary
+-------
+This is a copy of the source code for [MIT's Alloy Analyzer model checking tool](http://alloy.mit.edu/alloy/).
+It also includes an Ant build.xml script, which is not part of the original MIT source code.
+This copy was created to facilitate modification to the core Alloy tool (the parts which fall
+under the `edu.mit` package structure).
+
+It was created as follows (not necessarily in this order):
+
+1. Downloaded the JAR file located at: http://alloy.mit.edu/alloy/downloads/alloy4.2.jar
+2. Extracted the JAR file.
+3. Added this `README.md` file and a `build.xml` file.
+3. Deleted core `.class` files (using the _clean_ target in `build.xml`)
+
+Building
+--------
+The Ant build.xml script contains the following targets:
+
+- _build_: Compiles the `.java` files under the `edu` directory.
+
+ Other directories are not touched; it is assumed that these contain libraries
+ which have been pre-compiled.
+
+ The auto-generated parser and lexer `.java` files (located in the `edu/mit/csail/sdg/alloy4compiler/parser` directory)
+ are neither deleted nor generated by the Ant script. The directory already contains shell scripts
+ to re-generate them using JFlex and CUP.
+- _dist_: Creates an executable JAR file in the `dist` directory. This JAR file looks essentially like the official
+ Alloy JAR file released by MIT.
+- _all_: Runs _dist_.
+- _clean_: Deletes the `dist` directory and all class files under the `edu` directory.
+
+Notes
+-----
+
+- As per the manifest, the main class is `edu.mit.csail.sdg.alloy4whole.SimpleGUI`.
+- The version number and build date which the tool displays are not accurate.
+ These are set in the `edu.mit.csail.sdg.alloy4.Version` class, and are supposed to be
+ updated by the build script when building a release.
+ This project was not intended to create official releases, so it was left as-is.
+- There is a class `edu.mit.csail.sdg.alloy4.MailBug` which includes logic to email
+ crash reports to MIT. You should change this class if you are modifying the source code
+ and creating your own release.
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/berkmin b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/berkmin
new file mode 100644
index 00000000..f0aa2cee
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/berkmin differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libminisat.so b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libminisat.so
new file mode 100644
index 00000000..d286aa4b
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libminisat.so differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libminisatprover.so b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libminisatprover.so
new file mode 100644
index 00000000..392474ac
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libminisatprover.so differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libzchaff.so b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libzchaff.so
new file mode 100644
index 00000000..dc9c229d
Binary files /dev/null and b/Source/eu.modelwriter.alloyanalyzer/src/amd64-linux/libzchaff.so differ
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/A4Reporter.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/A4Reporter.java
new file mode 100644
index 00000000..85f61920
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/A4Reporter.java
@@ -0,0 +1,186 @@
+/*
+ * Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+ * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/**
+ * This class receives diagnostic, progress, and warning messages from Alloy4. (This default
+ * implementation ignores all calls; you should subclass it to do the appropriate screen output)
+ */
+
+public class A4Reporter {
+
+ /** If nonnull, then we will forward requests to this reporter. */
+ private final A4Reporter parent;
+
+ /** This is a pre-constructed instance that simply ignores all calls. */
+ public static final A4Reporter NOP = new A4Reporter();
+
+ /** Constructs a default A4Reporter object that does nothing. */
+ public A4Reporter() {
+ parent = null;
+ }
+
+ /** Constructs an A4Reporter that forwards each method to the given A4Reporter. */
+ public A4Reporter(A4Reporter reporter) {
+ parent = reporter;
+ }
+
+ /**
+ * This method is called at various points to report the current progress; it is intended as a
+ * debugging aid for the developers; the messages are generally not useful for end users.
+ */
+ public void debug(String msg) {
+ if (parent != null)
+ parent.debug(msg);
+ }
+
+ /** This method is called by the parser to report parser events. */
+ public void parse(String msg) {
+ if (parent != null)
+ parent.parse(msg);
+ }
+
+ /**
+ * This method is called by the typechecker to report the type for each
+ * field/function/predicate/assertion, etc.
+ */
+ public void typecheck(String msg) {
+ if (parent != null)
+ parent.typecheck(msg);
+ }
+
+ /** This method is called by the typechecker to report a nonfatal type error. */
+ public void warning(ErrorWarning msg) {
+ if (parent != null)
+ parent.warning(msg);
+ }
+
+ /** This method is called by the ScopeComputer to report the scope chosen for each sig. */
+ public void scope(String msg) {
+ if (parent != null)
+ parent.scope(msg);
+ }
+
+ /**
+ * This method is called by the BoundsComputer to report the bounds chosen for each sig and each
+ * field.
+ */
+ public void bound(String msg) {
+ if (parent != null)
+ parent.bound(msg);
+ }
+
+ /**
+ * This method is called by the translator just before it begins generating CNF.
+ *
+ * @param solver - the solver chosen by the user (eg. SAT4J, MiniSat...)
+ * @param bitwidth - the integer bitwidth chosen by the user
+ * @param maxseq - the scope on seq/Int chosen by the user
+ * @param skolemDepth - the skolem function depth chosen by the user (0, 1, 2...)
+ * @param symmetry - the amount of symmetry breaking chosen by the user (0...)
+ */
+ public void translate(String solver, int bitwidth, int maxseq, int skolemDepth, int symmetry) {
+ if (parent != null)
+ parent.translate(solver, bitwidth, maxseq, skolemDepth, symmetry);
+ }
+
+ /**
+ * This method is called by the translator just after it generated the CNF.
+ *
+ * @param primaryVars - the total number of primary variables
+ * @param totalVars - the total number of variables including the number of primary variables
+ * @param clauses - the total number of clauses
+ */
+ public void solve(int primaryVars, int totalVars, int clauses) {
+ if (parent != null)
+ parent.solve(primaryVars, totalVars, clauses);
+ }
+
+ /**
+ * If solver==KK or solver==CNF, this method is called by the translator after it constructed the
+ * Kodkod or CNF file.
+ *
+ * @param filename - the Kodkod or CNF file generated by the translator
+ */
+ public void resultCNF(String filename) {
+ if (parent != null)
+ parent.resultCNF(filename);
+ }
+
+ /**
+ * If solver!=KK and solver!=CNF, this method is called by the translator if the formula is
+ * satisfiable.
+ *
+ * @param command - this is the original Command used to generate this solution
+ * @param solvingTime - this is the number of milliseconds the solver took to obtain this result
+ * @param solution - the satisfying A4Solution object
+ */
+ public void resultSAT(Object command, long solvingTime, Object solution) {
+ if (parent != null)
+ parent.resultSAT(command, solvingTime, solution);
+ }
+
+ /**
+ * If solver!=KK and solver!=CNF, this method is called by the translator before starting the
+ * unsat core minimization.
+ *
+ * @param command - this is the original Command used to generate this solution
+ * @param before - the size of the unsat core before calling minimization
+ */
+ public void minimizing(Object command, int before) {
+ if (parent != null)
+ parent.minimizing(command, before);
+ }
+
+ /**
+ * If solver!=KK and solver!=CNF, this method is called by the translator after performing the
+ * unsat core minimization.
+ *
+ * @param command - this is the original Command used to generate this solution
+ * @param before - the size of the unsat core before calling minimization
+ * @param after - the size of the unsat core after calling minimization
+ */
+ public void minimized(Object command, int before, int after) {
+ if (parent != null)
+ parent.minimized(command, before, after);
+ }
+
+ /**
+ * If solver!=KK and solver!=CNF, this method is called by the translator if the formula is
+ * unsatisfiable.
+ *
+ * @param command - this is the original Command used to generate this solution
+ * @param solvingTime - this is the number of milliseconds the solver took to obtain this result
+ * @param solution - the unsatisfying A4Solution object
+ */
+ public void resultUNSAT(Object command, long solvingTime, Object solution) {
+ if (parent != null)
+ parent.resultUNSAT(command, solvingTime, solution);
+ }
+
+ /**
+ * This method is called by the A4SolutionWriter when it is writing a particular sig, field, or
+ * skolem.
+ */
+ public void write(Object expr) {
+ if (parent != null)
+ parent.write(expr);
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ByteBuffer.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ByteBuffer.java
new file mode 100644
index 00000000..265f9e19
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ByteBuffer.java
@@ -0,0 +1,130 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.zip.Deflater;
+
+/** Mutable; implements a growable array of bytes.
+ *
+ * This class is more efficient than Java's ByteArrayOutputStream when writing large amount of data,
+ * because ByteArrayOutputStream will resize and copy entire existing contents every time the array needs to grow,
+ * whereas this class maintains a linked list of arrays (so when capacity is expanded we don't need to copy old data)
+ */
+
+public final class ByteBuffer {
+
+ /** The size per chunk. */
+ private static final int SIZE = 65536;
+
+ /** The list of chunks allocated so far; always has at least one chunk; every chunk is always exactly of size SIZE. */
+ private final LinkedList list = new LinkedList();
+
+ /** The number of bytes stored in the latest chunk; every chunk before that is always fully filled. */
+ private int n = 0;
+
+ /** Construct an empty byte buffer. */
+ public ByteBuffer() { list.add(new byte[SIZE]); }
+
+ /** Write the given byte into this byte buffer. */
+ private ByteBuffer w(int b) {
+ if (n==SIZE) { list.add(new byte[SIZE]); n=0; }
+ byte[] array = list.getLast();
+ array[n] = (byte)b;
+ n++;
+ return this;
+ }
+
+ /** Write the given array of bytes into this byte buffer. */
+ private ByteBuffer write(byte[] b, int offset, int len) {
+ if (b==null || len<=0) return this; else if (n==SIZE) { list.add(new byte[SIZE]); n=0; }
+ while(true) { // loop invariant: len>0 and SIZE>n
+ byte[] array = list.getLast();
+ if (len <= (SIZE-n)) { System.arraycopy(b, offset, array, n, len); n += len; return this; }
+ System.arraycopy(b, offset, array, n, SIZE-n);
+ offset += (SIZE-n);
+ len -= (SIZE-n);
+ n = 0;
+ list.add(new byte[SIZE]);
+ }
+ }
+
+ /** Write the given String into this byte buffer (by converting the String into its UTF-8 representation) */
+ public ByteBuffer write(String string) {
+ if (string.length() == 0) return this;
+ byte[] b;
+ try { b = string.getBytes("UTF-8"); } catch(UnsupportedEncodingException ex) { return this; } // exception not possible
+ return write(b, 0, b.length);
+ }
+
+ /** Write the given number into this byte buffer, followed by a space. */
+ public ByteBuffer writes(long x) {
+ return write(Long.toString(x)).w(' ');
+ }
+
+ /** Write the given number into this byte buffer (truncated to the range -32767..+32767), followed by a space. */
+ public strictfp ByteBuffer writes(double x) {
+ // These extreme values shouldn't happen, but we want to protect against them
+ if (Double.isNaN(x)) return write("0 "); else if (x>32767) return write("32767 "); else if (x<-32767) return write("-32767 ");
+ long num = (long)(x * 1000000);
+ if (num>=32767000000L) return write("32767 "); else if (num<=(-32767000000L)) return write("-32767 ");
+ // Now, regular doubles... let's allow up to 6 digits after the decimal point
+ if (num<0) { w('-'); num = -num; }
+ String str = Long.toString(num);
+ int len = str.length();
+ if (len<=6) {
+ w('.');
+ while(len<6) { w('0'); len++; }
+ return write(str).w(' ');
+ }
+ return write(str.substring(0, str.length()-6)).w('.').write(str.substring(str.length()-6)).w(' ');
+ }
+
+ /** Write the entire content into the given file using Flate compression (see RFC1951) then return the number of bytes written. */
+ public long dumpFlate(RandomAccessFile os) throws IOException {
+ Deflater zip = new Deflater(Deflater.BEST_COMPRESSION);
+ byte[] output = new byte[8192];
+ Iterator it = list.iterator(); // when null, that means we have told the Deflater that no more input would be coming
+ long ans = 0; // the number of bytes written out so far
+ while(true) {
+ if (it!=null && zip.needsInput() && it.hasNext()) {
+ byte[] in = it.next();
+ if (in == list.getLast()) { zip.setInput(in, 0, n); it=null; zip.finish(); } else { zip.setInput(in, 0, SIZE); }
+ }
+ if (it==null && zip.finished()) break;
+ int count = zip.deflate(output);
+ if (count > 0) {
+ ans = ans + count;
+ if (ans < 0) throw new IOException("Data too large to be written to the output file.");
+ os.write(output, 0, count);
+ }
+ }
+ return ans;
+ }
+
+ /** Write the entire content into the given file as-is, then return the number of bytes written. */
+ public long dump(RandomAccessFile os) throws IOException {
+ if (list.size() >= (Long.MAX_VALUE / SIZE)) throw new IOException("Data too large to be written to the output file.");
+ byte[] last = list.getLast();
+ for(byte[] x: list) if (x!=last) os.write(x);
+ if (n>0) os.write(last, 0, n);
+ return ((long)(list.size()-1)) * SIZE + n;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Computer.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Computer.java
new file mode 100644
index 00000000..3e490f45
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Computer.java
@@ -0,0 +1,26 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** This defines a compute() method that takes an Object input and produces a String output. */
+
+public interface Computer {
+
+ /** This method takes an Object input and produces a String output.
+ * @throws Exception if an error occurred during the computation.
+ */
+ public String compute (Object input) throws Exception;
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstList.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstList.java
new file mode 100644
index 00000000..ffdfcae7
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstList.java
@@ -0,0 +1,154 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.RandomAccess;
+
+/** Immutable; implements a list based on equals(); null values are allowed.
+ *
+ * @param - the type of element
+ */
+
+public final class ConstList extends AbstractList implements Serializable, RandomAccess {
+
+ /** Mutable; this implements a modifiable list that can be used to construct a ConstList; null values are allowed.
+ *
+ * @param - the type of element
+ */
+ public static final class TempList {
+
+ /** The underlying list. */
+ private final ArrayList list;
+
+ /** Nonnull iff this list is no longer modifiable. */
+ private ConstList clist;
+
+ /** Construct an empty TempList. */
+ public TempList() { list = new ArrayList(); }
+
+ /** Construct an empty TempList with initial capacity of n (if n<=0, the list will be given a default capacity of 0) */
+ public TempList(int n) { list = new ArrayList(n>=0 ? n : 0); }
+
+ /** Construct a new TempList whose initial content is n references to the given elem (if n<=0, the created list is empty) */
+ public TempList(int n, T elem) { list = new ArrayList(n>0 ? n : 0); while(n>0) { list.add(elem); n--; } }
+
+ /** Construct a new TempList whose initial content is equal to the given collection. */
+ public TempList(Collection extends T> all) { list = new ArrayList(all); }
+
+ /** Construct a new TempList whose initial content is equal to the given array. */
+ public TempList(T... all) { list = new ArrayList(all.length); for(int i=0; i clear() { chk(); list.clear(); return this; }
+
+ /** Appends the given element to the list, then return itself. */
+ public TempList add(T elem) { chk(); list.add(elem); return this; }
+
+ /** Appends the elements in the given collection to the list, then return itself. */
+ public TempList addAll(Iterable extends T> all) {
+ chk();
+ if (all instanceof Collection) list.addAll((Collection extends T>)all); else if (all!=null) { for(T x: all) list.add(x); }
+ return this;
+ }
+
+ /** Changes the i-th element to be the given element, then return itself. */
+ public TempList set(int index, T elem) { chk(); list.set(index, elem); return this; }
+
+ /** Makes this TempList unmodifiable, then construct a ConstList backed by this TempList. */
+ @SuppressWarnings("unchecked")
+ public ConstList makeConst() { if (clist==null) clist=(list.isEmpty() ? emptylist : new ConstList(list)); return clist; }
+ }
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** The underlying unmodifiable list. */
+ private final List list;
+
+ /** This caches an unmodifiable empty list. */
+ @SuppressWarnings("unchecked")
+ private static final ConstList emptylist = new ConstList(new ArrayList(0));
+
+ /** Construct a ConstList with the given list as its backing store. */
+ private ConstList(List list) {
+ this.list = list;
+ }
+
+ /** Return an unmodifiable empty list. */
+ @SuppressWarnings("unchecked")
+ public static ConstList make() {
+ return (ConstList) emptylist;
+ }
+
+ /** Return an unmodifiable list consisting of "n" references to "elem".
+ * (If n<=0, we'll return an unmodifiable empty list)
+ */
+ public static ConstList make(int n, T elem) {
+ if (n <= 0) return make();
+ ArrayList ans = new ArrayList(n);
+ while(n > 0) { ans.add(elem); n--; }
+ return new ConstList(ans);
+ }
+
+ /** Return an unmodifiable list with the same elements as the given collection.
+ * (If collection==null, we'll return an unmodifiable empty list)
+ */
+ public static ConstList make(Iterable collection) {
+ if (collection == null) return make();
+ if (collection instanceof ConstList) return (ConstList) collection;
+ if (collection instanceof Collection) {
+ Collection col = (Collection)collection;
+ if (col.isEmpty()) return make(); else return new ConstList(new ArrayList(col));
+ }
+ ArrayList ans = null;
+ for(T x: collection) {
+ if (ans == null) ans = new ArrayList();
+ ans.add(x);
+ }
+ if (ans==null) return make(); else return new ConstList(ans);
+ }
+
+ /** Returns the i-th element
+ * @throws ArrayIndexOutOfBoundsException if the given index doesn't exist
+ */
+ @Override public T get(int index) { return list.get(index); }
+
+ /** Returns the number of elements in this list. */
+ @Override public int size() { return list.size(); }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstMap.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstMap.java
new file mode 100644
index 00000000..601922da
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstMap.java
@@ -0,0 +1,83 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.io.Serializable;
+
+/** Immutable; implements a map based on hashCode() and equals(); null key and values are allowed.
+ *
+ * @param - the type of key
+ * @param - the type of value
+ */
+
+public final class ConstMap extends AbstractMap implements Serializable {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** The underlying Collections.unmodifiableMap map. */
+ private final Map map;
+
+ /** This caches a read-only empty map. */
+ private static final ConstMap emptymap = new ConstMap(new HashMap(0));
+
+ /** Constructs an unmodifiable map with the given map as the backing store. */
+ private ConstMap(Map extends K,? extends V> map) {
+ this.map = Collections.unmodifiableMap(map);
+ }
+
+ /** Returns an unmodifiable empty map. */
+ @SuppressWarnings("unchecked")
+ public static ConstMap make() {
+ return (ConstMap) emptymap;
+ }
+
+ /** Returns an unmodifiable map with the same entries and traversal order as the given map.
+ * (If map==null, we'll return an unmodifiable empty map)
+ */
+ public static ConstMap make(Map map) {
+ if (map instanceof ConstMap) return (ConstMap)map;
+ if (map == null || map.isEmpty()) return make(); else return new ConstMap(new LinkedHashMap(map));
+ }
+
+ /** Returns an unmodifiable view of the mappings in this map. */
+ @Override public Set> entrySet() { return map.entrySet(); }
+
+ /** Returns an unmodifiable view of the keys in this map. */
+ @Override public Set keySet() { return map.keySet(); } // overridden for performance
+
+ /** Returns an unmodifiable view of the values in this map. */
+ @Override public Collection values() { return map.values(); } // overridden for performance
+
+ /** Returns the number of (key, value) mapping in this map. */
+ @Override public int size() { return map.size(); } // overridden for performance
+
+ /** Returns true if exists at least one (k, v) mapping where (k==null ? key==null : k.equals(key)) */
+ @Override public boolean containsKey(Object key) { return map.containsKey(key); } // overridden for performance
+
+ /** Returns true if exists at least one (k, v) mapping where (v==null ? value==null : v.equals(value)) */
+ @Override public boolean containsValue(Object value) { return map.containsValue(value); } // overridden for performance
+
+ /** Returns the value associated with the key (or null if not found); null is also returned if the given key maps to null. */
+ @Override public V get(Object key) { return map.get(key); } // overridden for performance
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstSet.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstSet.java
new file mode 100644
index 00000000..f7d79121
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ConstSet.java
@@ -0,0 +1,74 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Iterator;
+import java.io.Serializable;
+
+/** Immutable; implements a set based on hashCode() and equals(); null value is allowed.
+ *
+ * @param - the type of element
+ */
+
+public final class ConstSet extends AbstractSet implements Serializable {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** The underlying Collections.unmodifiableSet set. */
+ private final Set set;
+
+ /** This caches a readonly empty Set. */
+ private static final ConstSet emptyset = new ConstSet(new HashSet(0));
+
+ /** Constructs an unmodifiable map with the given set as the backing store. */
+ private ConstSet(Set extends K> set) {
+ this.set = Collections.unmodifiableSet(set);
+ }
+
+ /** Returns an unmodifiable empty set. */
+ @SuppressWarnings("unchecked")
+ public static ConstSet make() {
+ return (ConstSet) emptyset;
+ }
+
+ /** Returns an unmodifiable set with the same elements and traversal order as the given set.
+ * (If set==null, we'll return an unmodifiable empty set)
+ */
+ public static ConstSet make(Iterable collection) {
+ if (collection instanceof ConstSet) return (ConstSet)collection;
+ LinkedHashSet ans = null;
+ if (collection != null) for(K element: collection) {
+ if (ans == null) ans = new LinkedHashSet();
+ ans.add(element);
+ }
+ if (ans==null) return make(); else return new ConstSet(ans);
+ }
+
+ /** Returns the number of objects in this set. */
+ @Override public int size() { return set.size(); }
+
+ /** Returns a read-only iterator over this set. */
+ @Override public Iterator iterator() { return set.iterator(); }
+
+ /** Returns true if the given object is in this set. */
+ @Override public boolean contains(Object element) { return set.contains(element); } // overridden for performance
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/DirectedGraph.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/DirectedGraph.java
new file mode 100644
index 00000000..319e3278
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/DirectedGraph.java
@@ -0,0 +1,80 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.IdentityHashMap;
+
+/** Mutable; implements a directed graph; null node is allowed.
+ *
+ * Note: it uses n1==n2 for comparing nodes rather than using n1.equals(n2)
+ *
+ * @param - the type of node
+ */
+
+public final class DirectedGraph {
+
+ /** This substitutes for null nodes. This allows hasPath() method to use put() to both insert and test membership in one step. */
+ private static final Object NULL = new Object();
+
+ /** This field maps each node X to a list of "neighbor nodes" that X can reach by following directed edges zero or more times. */
+ private final Map> nodeToTargets = new IdentityHashMap>();
+
+ /** Constructs an empty graph. */
+ public DirectedGraph () { }
+
+ /** Add a directed edge from start node to end node (if there wasn't such an edge already). */
+ public void addEdge (N start, N end) {
+ if (start == end) return;
+ Object a = (start==null ? NULL : start);
+ Object b = (end==null ? NULL : end);
+ List targets = nodeToTargets.get(a);
+ if (targets == null) {
+ targets = new ArrayList();
+ targets.add(b);
+ nodeToTargets.put(a, targets);
+ } else {
+ for (int i = targets.size()-1; i >= 0; i--) if (targets.get(i) == b) return;
+ targets.add(b);
+ }
+ }
+
+ /** Returns whether there is a directed path from start node to end node by following directed edges 0 or more times (breath-first). */
+ public boolean hasPath (N start, N end) {
+ if (start == end) return true;
+ Object a = (start==null ? NULL : start);
+ Object b = (end==null ? NULL : end);
+ List todo = new ArrayList();
+ Map visited = new IdentityHashMap();
+ // The correctness and guaranteed termination relies on following three invariants:
+ // (1) Every time we add X to "visited", we also simultaneously add X to "todo".
+ // (2) Every time we add X to "todo", we also simultaneously add X to "visited".
+ // (3) Nothing is ever removed.
+ visited.put(a, a);
+ todo.add(a);
+ for(int k = 0; k < todo.size(); k++) { // use an integer loop since we will be adding to the "todo" list as we iterate
+ List targets = nodeToTargets.get(todo.get(k));
+ if (targets != null) for (int i = targets.size()-1; i >= 0; i--) {
+ Object next = targets.get(i);
+ if (next == b) { addEdge(start, end); return true; } // Cache so that later hasPath(start,end) returns true immediately
+ if (visited.put(next, next) == null) todo.add(next);
+ }
+ }
+ return false;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Env.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Env.java
new file mode 100644
index 00000000..be5e1643
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Env.java
@@ -0,0 +1,106 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.LinkedHashMap;
+
+/** Mutable; implements a undoable map based on hashCode() and equals(); null key and values are allowed.
+ *
+ * To be more precise, every key is internally mapped to a list of values.
+ * The put(X,Y) method appends Y onto the end of X's list.
+ * The get(X) method returns the last element in X's list.
+ * The remove(X) method removes the last element in X's list.
+ *
+ *
This is very useful for representing lexical scoping: when a local
+ * variable is introduced with the same name as an existing variable,
+ * the new variable "hides" the old mapping; and when the new variable falls
+ * out of scope, the previous mapping is once again "revealed".
+ *
+ * @param - the type for Value
+ */
+
+public final class Env {
+
+ /** If a key is bound to one or more values, this stores the first value.
+ *
+ * For example: if key K is bound to list of values V1,V2,V3...Vn, then map1.get(K) returns V1
+ *
+ * Invariant: map2.containsKey(x) implies (map1.containsKey(x) && map2.get(x).size()>0)
+ */
+ private final Map map1 = new LinkedHashMap();
+
+ /** If a key is bound to more than one value, this stores every value except the first value.
+ *
+ * For example: if key K is bound to list of values V1,V2,V3...Vn, then map2.get(K) returns the sublist V2..Vn
+ *
+ * Invariant: map2.containsKey(x) implies (map1.containsKey(x) && map2.get(x).size()>0)
+ */
+ private final Map> map2 = new LinkedHashMap>();
+
+ /** Constructs an initially empty environment. */
+ public Env () { }
+
+ /** Returns true if the key is mapped to one or more values. */
+ public boolean has (K key) { return map1.containsKey(key); }
+
+ /** Returns the latest value associated with the key (and returns null if none).
+ *
+ * Since null is also a possible value, if you get null as the answer,
+ * you need to call has(key) to determine whether the key really has a mapping or not.
+ */
+ public V get (K key) {
+ LinkedList list = map2.get(key);
+ return (list != null) ? list.getLast() : map1.get(key);
+ }
+
+ /** Associates the key with the value (which can be null). */
+ public void put (K key, V value) {
+ LinkedList list = map2.get(key);
+ if (list != null) {
+ list.add(value);
+ } else if (!map1.containsKey(key)) {
+ map1.put(key, value);
+ } else {
+ list = new LinkedList();
+ list.add(value);
+ map2.put(key, list);
+ }
+ }
+
+ /** Removes the latest mapping for the key (and if the key had previous mappings, they become visible).
+ * If there are no mappings for the key, then this method does nothing.
+ */
+ public void remove (K key) {
+ LinkedList list = map2.get(key);
+ if (list == null) map1.remove(key); else if (list.size() == 1) map2.remove(key); else list.removeLast();
+ }
+
+ /** Removes all mappings. */
+ public void clear() {
+ map1.clear();
+ map2.clear();
+ }
+
+ /** Make a shallow copy of this environment. */
+ public Env dup() {
+ Env ans = new Env();
+ ans.map1.putAll(map1);
+ for(Map.Entry> e: map2.entrySet()) ans.map2.put(e.getKey(), new LinkedList(e.getValue()));
+ return ans;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Err.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Err.java
new file mode 100644
index 00000000..af8f7c53
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Err.java
@@ -0,0 +1,56 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** Immutable; this is the abstract parent class of the various possible errors. */
+
+public abstract class Err extends Exception {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** This stores the filename/line/column information (Pos.UNKNOWN if unknown) (never null) */
+ public final Pos pos;
+
+ /** The actual error message (never null) */
+ public final String msg;
+
+ /** Constructs a new Err object.
+ * @param pos - the filename/line/row information (can be null if unknown)
+ * @param msg - the actual error message (can be null)
+ * @param cause - if nonnull, it will be recorded as the cause of this exception
+ */
+ Err(Pos pos, String msg, Throwable cause) {
+ super((msg==null ? "" : msg), cause);
+ this.pos = (pos==null ? Pos.UNKNOWN : pos);
+ this.msg = (msg==null ? "" : msg);
+ }
+
+ /** Two Err objects are equal if the type, position, and message are the same. */
+ @Override public final boolean equals(Object other) {
+ if (this==other) return true; else if (other==null || getClass()!=other.getClass()) return false;
+ Err that = (Err) other;
+ return pos.equals(that.pos) && msg.equals(that.msg);
+ }
+
+ /** Returns a hash code consistent with equals() */
+ @Override public final int hashCode() {
+ return msg.hashCode();
+ }
+
+ /** Returns this exception type, its error message, and its complete stack trace as a String. */
+ public final String dump() { return MailBug.dump(this); }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorAPI.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorAPI.java
new file mode 100644
index 00000000..01e62c05
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorAPI.java
@@ -0,0 +1,48 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** Immutable; this represents an API usage error. */
+
+public final class ErrorAPI extends Err {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** Constructs a new API usage error.
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorAPI(String msg) { super(null, msg, null); }
+
+ /** Constructs a new API usage error with "cause" as the underlying cause.
+ * @param msg - the actual error message (can be null)
+ * @param cause - if nonnull, it is the cause of this exception
+ */
+ public ErrorAPI(String msg, Throwable cause) { super(null, msg, cause); }
+
+ /** Constructs a new API usage error.
+ * @param pos - the filename/line/row information (can be null if unknown)
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorAPI(Pos pos, String msg) { super(pos, msg, null); }
+
+ /** Returns a textual description of the error. */
+ @Override public String toString() {
+ if (pos==Pos.UNKNOWN) return "API usage error:\n"+msg;
+ if (pos.filename.length()>0) return "API usage error in "+pos.filename+" at line "+pos.y+" column "+pos.x+":\n"+msg;
+ return "API usage error at line " + pos.y + " column " + pos.x + ":\n" + msg;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorFatal.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorFatal.java
new file mode 100644
index 00000000..812b4da3
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorFatal.java
@@ -0,0 +1,48 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** Immutable; this represents a fatal error. */
+
+public final class ErrorFatal extends Err {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** Constructs a new fatal error.
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorFatal(String msg) { super(null, msg, null); }
+
+ /** Constructs a new fatal error with "cause" as the underlying cause.
+ * @param msg - the actual error message (can be null)
+ * @param cause - if nonnull, it is the cause of this exception
+ */
+ public ErrorFatal(String msg, Throwable cause) { super(null, msg, cause); }
+
+ /** Constructs a new fatal error.
+ * @param pos - the filename/line/row information (can be null if unknown)
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorFatal(Pos pos, String msg) { super(pos, msg, null); }
+
+ /** Returns a textual description of the error. */
+ @Override public String toString() {
+ if (pos==Pos.UNKNOWN) return "Fatal error:\n"+msg;
+ if (pos.filename.length()>0) return "Fatal error in "+pos.filename+" at line "+pos.y+" column "+pos.x+":\n"+msg;
+ return "Fatal error at line " + pos.y + " column " + pos.x + ":\n" + msg;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorSyntax.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorSyntax.java
new file mode 100644
index 00000000..11497995
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorSyntax.java
@@ -0,0 +1,48 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** Immutable; this represents a syntax error that should be reported to the user. */
+
+public final class ErrorSyntax extends Err {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** Constructs a new syntax error.
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorSyntax(String msg) { super(null, msg, null); }
+
+ /** Constructs a new syntax error with "cause" as the underlying cause.
+ * @param msg - the actual error message (can be null)
+ * @param cause - if nonnull, it is the cause of this exception
+ */
+ public ErrorSyntax(String msg, Throwable cause) { super(null, msg, cause); }
+
+ /** Constructs a new syntax error.
+ * @param pos - the filename/line/row information (can be null if unknown)
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorSyntax(Pos pos, String msg) { super(pos, msg, null); }
+
+ /** Returns a textual description of the error. */
+ @Override public String toString() {
+ if (pos==Pos.UNKNOWN) return "Syntax error:\n"+msg;
+ if (pos.filename.length()>0) return "Syntax error in "+pos.filename+" at line "+pos.y+" column "+pos.x+":\n"+msg;
+ return "Syntax error at line " + pos.y + " column " + pos.x + ":\n" + msg;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorType.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorType.java
new file mode 100644
index 00000000..518a720d
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorType.java
@@ -0,0 +1,48 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** Immutable; this represents a type error that should be reported to the user. */
+
+public final class ErrorType extends Err {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** Constructs a new type error.
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorType(String msg) { super(null, msg, null); }
+
+ /** Constructs a new type error with "cause" as the underlying cause.
+ * @param msg - the actual error message (can be null)
+ * @param cause - if nonnull, it is the cause of this exception
+ */
+ public ErrorType(String msg, Throwable cause) { super(null, msg, cause); }
+
+ /** Constructs a new type error.
+ * @param pos - the filename/line/row information (can be null if unknown)
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorType(Pos pos, String msg) { super(pos, msg, null); }
+
+ /** Returns a textual description of the error. */
+ @Override public String toString() {
+ if (pos==Pos.UNKNOWN) return "Type error:\n"+msg;
+ if (pos.filename.length()>0) return "Type error in "+pos.filename+" at line "+pos.y+" column "+pos.x+":\n"+msg;
+ return "Type error at line " + pos.y + " column " + pos.x + ":\n" + msg;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorWarning.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorWarning.java
new file mode 100644
index 00000000..959b8314
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/ErrorWarning.java
@@ -0,0 +1,48 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** Immutable; this represents a nonfatal warning that should be reported to the user. */
+
+public final class ErrorWarning extends Err {
+
+ /** This ensures this class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** Constructs a new warning.
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorWarning(String msg) { super(null, msg, null); }
+
+ /** Constructs a new warning with "cause" as the underlying cause.
+ * @param msg - the actual error message (can be null)
+ * @param cause - if nonnull, it is the cause of this exception
+ */
+ public ErrorWarning(String msg, Throwable cause) { super(null, msg, cause); }
+
+ /** Constructs a new warning.
+ * @param pos - the filename/line/row information (can be null if unknown)
+ * @param msg - the actual error message (can be null)
+ */
+ public ErrorWarning(Pos pos, String msg) { super(pos, msg, null); }
+
+ /** Returns a textual description of the error. */
+ @Override public String toString() {
+ if (pos==Pos.UNKNOWN) return msg;
+ if (pos.filename.length()>0) return "Line "+pos.y+" column "+pos.x+" in "+pos.filename+":\n"+msg;
+ return "Line " + pos.y + " column " + pos.x + ":\n" + msg;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/JoinableList.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/JoinableList.java
new file mode 100644
index 00000000..e7bf4b42
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/JoinableList.java
@@ -0,0 +1,92 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.io.Serializable;
+import java.util.AbstractList;
+
+/** Immutable; implements a list where it is combine them; null values are NOT allowed. */
+
+public final class JoinableList extends AbstractList implements Serializable {
+
+ /** This ensures the class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** The number of items stored in this list.
+ * Invariant: count == (pre!=null ? pre.count : 0) + (item!=null ? 1 : 0) + (post!=null ? post.count : 0)
+ */
+ private final int count;
+
+ /** The list of items before "this.item"; may be null. */
+ private final JoinableList pre;
+
+ /** The list of items after "this.item"; may be null. */
+ private final JoinableList post;
+
+ /** If nonnull, it stores an item. */
+ private final E item;
+
+ /** Construct a JoinableList object. */
+ private JoinableList(int count, JoinableList pre, E item, JoinableList post) {
+ this.count = count;
+ this.pre = pre;
+ this.item = item;
+ this.post = post;
+ }
+
+ /** Construct an empty list. */
+ public JoinableList() { this(0, null, null, null); }
+
+ /** Construct a list containing a single item, or return an empty list if item==null. */
+ public JoinableList(E item) { this((item!=null ? 1 : 0), null, item, null); }
+
+ /** Returns a list that represents the concatenation of this list and that list. */
+ public JoinableList make(JoinableList that) {
+ if (that == null || that.count == 0) return this; else if (count == 0) return that;
+ int sum = count + that.count;
+ if (sum < count) throw new OutOfMemoryError(); // integer overflow
+ if (post != null) return new JoinableList(sum, this, null, that); else return new JoinableList(sum, pre, item, that);
+ }
+
+ /** Returns a list that represents the result of appending newItem onto this list; if newItem==null we return this list as-is. */
+ public JoinableList make(E newItem) {
+ if (newItem == null) return this;
+ int sum = count + 1; // integer overflow
+ if (sum < 1) throw new OutOfMemoryError();
+ if (post != null) return new JoinableList(sum, this, newItem, null);
+ if (item != null) return new JoinableList(sum, pre, item, new JoinableList(newItem));
+ return new JoinableList(sum, pre, newItem, null);
+ }
+
+ /** If the list if nonempty, arbitrarily return one of the item, otherwise throw ArrayIndexOutOfBoundsException. */
+ public E pick() { if (item!=null) return item; else return get(0); }
+
+ /** Return the i-th element
+ * @throws ArrayIndexOutOfBoundsException if the given index doesn't exist
+ */
+ @Override public E get(int i) {
+ if (i < 0 || i >= count) throw new ArrayIndexOutOfBoundsException();
+ JoinableList x = this;
+ while(true) {
+ int pre = (x.pre == null) ? 0 : x.pre.count;
+ if (i < pre) { x = x.pre; continue; }
+ if (x.item == null) { i = i - pre; x = x.post; } else if (i != pre) { i = i - pre - 1; x = x.post; } else return x.item;
+ }
+ }
+
+ /** Returns the number of elements in this list. */
+ @Override public int size() { return count; }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Listener.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Listener.java
new file mode 100644
index 00000000..b992f1de
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Listener.java
@@ -0,0 +1,30 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+/** This defines an interface for receiving events. */
+
+public interface Listener {
+
+ /** This defines the list of possible events. */
+ enum Event { CLICK, STATUS_CHANGE, FOCUSED, CTRL_PAGE_UP, CTRL_PAGE_DOWN, CARET_MOVED};
+
+ /** This method is called when the given zero-argument-event occurs. */
+ public Object do_action(Object sender, Event event);
+
+ /** This method is called when the given single-argument-event occurs. */
+ public Object do_action(Object sender, Event event, Object arg);
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Listeners.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Listeners.java
new file mode 100644
index 00000000..2f559505
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/Listeners.java
@@ -0,0 +1,46 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.util.ArrayList;
+import edu.mit.csail.sdg.alloy4.Listener.Event;
+
+/** This stores a list of listeners. */
+
+public final class Listeners {
+
+ /** The actual list of listeners. */
+ private final ArrayList listeners = new ArrayList();
+
+ /** Construct a empty list of listeners. */
+ public Listeners() { }
+
+ /** Add a listener to this group of listeners (if not already in the list) */
+ public void add(Listener listener) {
+ for(Listener x: listeners) if (x == listener) return;
+ listeners.add(listener);
+ }
+
+ /** Send the following zero-argument event to every listener. */
+ public void fire(Object sender, Event event) {
+ for(Listener x: listeners) x.do_action(sender, event);
+ }
+
+ /** Send the following one-argument event to every listener. */
+ public void fire(Object sender, Event event, Object arg) {
+ for(Listener x: listeners) x.do_action(sender, event, arg);
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/MacUtil.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/MacUtil.java
new file mode 100644
index 00000000..fa9bc391
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/MacUtil.java
@@ -0,0 +1,77 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import javax.swing.SwingUtilities;
+import com.apple.eawt.Application;
+import com.apple.eawt.ApplicationAdapter;
+import com.apple.eawt.ApplicationEvent;
+import com.apple.eawt.ApplicationListener;
+
+/** This class provides better integration on Mac OS X.
+ *
+ * You must not call any methods here if you're not on Mac OS X,
+ * since that triggers the loading of com.apple.eawt.* which are not available on other platforms.
+ *
+ *
Thread Safety: Safe.
+ */
+
+public final class MacUtil {
+
+ /** Constructor is private, since this class never needs to be instantiated. */
+ private MacUtil() { }
+
+ /** The cached Application object. */
+ private static Application app = null;
+
+ /** The previous ApplicationListener (or null if there was none). */
+ private static ApplicationListener listener = null;
+
+ /** Register a Mac OS X "ApplicationListener"; if there was a previous listener, it will be removed first.
+ * @param reopen - when the user clicks on the Dock icon, we'll call reopen.run() using SwingUtilities.invokeLater
+ * @param about - when the user clicks on About Alloy4, we'll call about.run() using SwingUtilities.invokeLater
+ * @param open - when a file needs to be opened, we'll call open.run(filename) using SwingUtilities.invokeLater
+ * @param quit - when the user clicks on Quit, we'll call quit.run() using SwingUtilities.invokeAndWait
+ */
+ public synchronized static void registerApplicationListener
+ (final Runnable reopen, final Runnable about, final Runner open, final Runnable quit) {
+ if (app == null) app = new Application(); else if (listener != null) app.removeApplicationListener(listener);
+ listener = new ApplicationAdapter() {
+ @Override public void handleReOpenApplication(ApplicationEvent arg) {
+ SwingUtilities.invokeLater(reopen);
+ }
+ @Override public void handleAbout(ApplicationEvent arg) {
+ arg.setHandled(true);
+ SwingUtilities.invokeLater(about);
+ }
+ @Override public void handleOpenFile(ApplicationEvent arg) {
+ final String filename = arg.getFilename();
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() { open.run(filename); }
+ });
+ }
+ @Override public void handleQuit(ApplicationEvent arg) {
+ try {
+ if (SwingUtilities.isEventDispatchThread()) quit.run(); else SwingUtilities.invokeAndWait(quit);
+ } catch (Throwable e) {
+ // Nothing we can do; we're already trying to quit!
+ }
+ arg.setHandled(false);
+ }
+ };
+ app.addApplicationListener(listener);
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/MailBug.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/MailBug.java
new file mode 100644
index 00000000..334e56f4
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/MailBug.java
@@ -0,0 +1,276 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Map;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.awt.Color;
+import java.awt.Dimension;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+
+/** This class asks the user for permission to email a bug report when an uncaught exception occurs. */
+
+public final class MailBug implements UncaughtExceptionHandler, Runnable {
+
+ /** The version number of the most recent Alloy4 (as queried from alloy.mit.edu); -1 if alloy.mit.edu has not replied yet. */
+ private static int latestAlloyVersion = -1;
+
+ /** The name of the most recent Alloy4 (as queried from alloy.mit.edu); "unknown" if alloy.mit.edu has not replied yet. */
+ private static String latestAlloyVersionName = "unknown";
+
+ /** The URL where the bug report should be sent. */
+ private static final String ALLOY_URL = "http://alloy.mit.edu/postbug4.php";
+
+ /** The URL where the current version info can be queried. */
+ private static final String ALLOY_NOW = "http://alloy.mit.edu/alloy4/download/alloy4.txt";
+
+ /** If alloy.mit.edu has replied, then return the latest Alloy build number, else return -1. */
+ public static int latestBuildNumber() { synchronized(MailBug.class) { return latestAlloyVersion; } }
+
+ /** If alloy.mit.edu has replied, then return the latest Alloy build name, else return "unknown" */
+ public static String latestBuildName() { synchronized(MailBug.class) { return latestAlloyVersionName; } }
+
+ /** Construct a new MailBug object. */
+ private MailBug() { }
+
+ /** Setup the uncaught-exception-handler and use a separate thread to query alloy.mit.edu for latest version number. */
+ public static void setup() {
+ if (Thread.getDefaultUncaughtExceptionHandler() != null) return;
+ MailBug x = new MailBug();
+ Thread.setDefaultUncaughtExceptionHandler(x);
+ new Thread(x).start();
+ }
+
+ /** This method concatenates a Throwable's message and stack trace and all its causes into a single String. */
+ public static String dump (Throwable ex) {
+ StringBuilder sb = new StringBuilder();
+ while(ex != null) {
+ sb.append(ex.getClass()).append(": ").append(ex.getMessage()).append('\n');
+ StackTraceElement[] trace = ex.getStackTrace();
+ if (trace != null) for(int n = trace.length, i = 0; i < n; i++) sb.append(trace[i]).append('\n');
+ ex = ex.getCause();
+ if (ex != null) sb.append("caused by...\n");
+ }
+ return sb.toString().trim();
+ }
+
+ /** This method returns true if the exception appears to be a Sun Java GUI bug. */
+ private static boolean isGUI(Throwable ex) {
+ // If the root of the stack trace is within Java framework itself,
+ // and no where is Alloy, Kodkod, or SAT4J anywhere along the trace,
+ // then it's almost *always* a Sun Java GUI bug from what we've seen.
+ // And it's better to ignore it rather than kill the file that the user is editing.
+ while(ex != null) {
+ StackTraceElement[] trace = ex.getStackTrace();
+ for(int n=(trace==null ? 0 : trace.length), i=0; i e: System.getProperties().entrySet()) {
+ String k = String.valueOf(e.getKey()), v = String.valueOf(e.getValue());
+ pw.printf("%s = %s\n", k.trim(), v.trim());
+ }
+ pw.printf("\n========================= The End ==========================\n\n");
+ pw.close();
+ sw.flush();
+ return sw.toString();
+ }
+
+ /** This method opens a connection then read the entire content (it converts non-ASCII into '?'); if error occurred it returns "".
+ * @param URL - the remote URL we want to read from
+ * @param send - if nonempty we will send it to the remote URL before attempting to read from the remote URL
+ */
+ private static String readAll(String URL, String send, String failure) {
+ BufferedInputStream bis = null;
+ InputStream in = null;
+ OutputStream out = null;
+ String ans;
+ try {
+ URLConnection connection = new URL(URL).openConnection();
+ if (send!=null && send.length() > 0) {
+ connection.setDoOutput(true);
+ out = connection.getOutputStream();
+ out.write(send.getBytes("UTF-8"));
+ out.close();
+ out = null;
+ }
+ in = connection.getInputStream();
+ bis = new BufferedInputStream(in);
+ StringBuilder sb = new StringBuilder();
+ int i;
+ while((i = bis.read()) >= 0) { sb.append((char)(i<=0x7F ? i : '?')); }
+ ans = Util.convertLineBreak(sb.toString());
+ } catch (Throwable ex) {
+ ans = failure;
+ } finally {
+ Util.close(bis);
+ Util.close(in);
+ Util.close(out);
+ }
+ return ans;
+ }
+
+ /** This method will query alloy.mit.edu for the latest version number. */
+ public void run() {
+ String result = readAll(ALLOY_NOW + "?buildnum=" + Version.buildNumber() + "&builddate=" + Version.buildDate(), "", "");
+ if (!result.startsWith("Alloy Build ")) return;
+ // Now that we know we're online, try to remove the old ill-conceived "Java WebStart" versions of Alloy4 BETA1..BETA7
+ //Subprocess.exec(20000, new String[]{
+ // "javaws", "-silent", "-offline", "-uninstall", "http://alloy.mit.edu/alloy4/download/alloy4.jnlp"});
+ // Now parse the result
+ int num = 0;
+ boolean found = false;
+ for(int i=0, len=result.length(); ; i++) {
+ if (i >= len) return; // malformed
+ char c = result.charAt(i);
+ if (!(c>='0' && c<='9')) { if (!found) continue; else { result = result.substring(i).trim(); break; } }
+ found = true;
+ num = num*10 + (c - '0');
+ }
+ synchronized(MailBug.class) { latestAlloyVersionName = result; latestAlloyVersion = num; }
+ }
+
+ /** This method sends the crash report then displays alloy.mit.edu's reply in a text window. */
+ private static void sendCrashReport (Thread thread, Throwable ex, String email, String problem) {
+ try {
+ final String report = prepareCrashReport(thread, ex, email, problem);
+ final String alt = "Sorry. An error has occurred in posting the bug report.\n" +
+ "Please email this report to alloy@mit.edu directly.\n\n" + dump(ex);
+ final JTextArea status = OurUtil.textarea("Sending the bug report... please wait...",
+ 10, 40, false, true, new LineBorder(Color.GRAY));
+ new Thread(new Runnable() {
+ public void run() {
+ final String output = readAll(ALLOY_URL, report, alt);
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ status.setText(output);
+ status.setCaretPosition(0);
+ }
+ });
+ }
+ }).start();
+ OurDialog.showmsg("Sending the bug report... please wait...", status);
+ } finally {
+ System.exit(1);
+ }
+ }
+
+ /** This method is an exception handler for uncaught exceptions. */
+ public void uncaughtException (Thread thread, Throwable ex) {
+ if (isGUI(ex)) return;
+ final int ver;
+ final String name;
+ synchronized(MailBug.class) { ver = latestAlloyVersion; name = latestAlloyVersionName; }
+ if (ex!=null) {
+ System.out.flush();
+ System.err.flush();
+ System.err.println("Exception: " + ex.getClass());
+ System.err.println("Message: " + ex);
+ System.err.println("Stacktrace:");
+ System.err.println(dump(ex));
+ System.err.flush();
+ }
+ final String yes = "Send the Bug Report", no = "Don't Send the Bug Report";
+ final JTextField email = OurUtil.textfield("", 20, new LineBorder(Color.DARK_GRAY));
+ final JTextArea problem = OurUtil.textarea("", 50, 50, true, false, new EmptyBorder(0,0,0,0));
+ final JScrollPane scroll = OurUtil.scrollpane(problem, new LineBorder(Color.DARK_GRAY), new Dimension(300, 200));
+ for(Throwable ex2 = ex; ex2 != null; ex2 = ex2.getCause()) {
+ if (ex2 instanceof StackOverflowError) OurDialog.fatal(new Object[] {
+ "Sorry. The Alloy Analyzer has run out of stack space.",
+ " ",
+ "Try simplifying your model or reducing the scope.",
+ "And try reducing Options->SkolemDepth to 0.",
+ "And try increasing Options->Stack.",
+ " ",
+ "There is no way for Alloy to continue execution, so pressing OK will shut down Alloy."
+ });
+ if (ex2 instanceof OutOfMemoryError) OurDialog.fatal(new Object[] {
+ "Sorry. The Alloy Analyzer has run out of memory.",
+ " ",
+ "Try simplifying your model or reducing the scope.",
+ "And try reducing Options->SkolemDepth to 0.",
+ "And try increasing Options->Memory.",
+ " ",
+ "There is no way for Alloy to continue execution, so pressing OK will shut down Alloy."
+ });
+ }
+ if (ver > Version.buildNumber()) OurDialog.fatal(new Object[] {
+ "Sorry. A fatal error has occurred.",
+ " ",
+ "You are running Alloy Analyzer " + Version.version(),
+ "but the most recent is Alloy Analyzer "+ name,
+ " ",
+ "Please try to upgrade to the newest version",
+ "as the problem may have already been fixed.",
+ " ",
+ "There is no way for Alloy to continue execution, so pressing OK will shut down Alloy."
+ });
+ if (OurDialog.yesno(new Object[] {
+ "Sorry. A fatal internal error has occurred.",
+ " ",
+ "You may submit a bug report (via HTTP).",
+ "The error report will include your system",
+ "configuration, but no other information.",
+ " ",
+ "If you'd like to be notified about a fix,",
+ "please describe the problem and enter your email address.",
+ " ",
+ OurUtil.makeHT("Email:", 5, email, null),
+ OurUtil.makeHT("Problem:", 5, scroll, null)
+ }, yes, no)) sendCrashReport(thread, ex, email.getText(), problem.getText());
+ System.exit(1);
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurAntiAlias.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurAntiAlias.java
new file mode 100644
index 00000000..9fe1d854
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurAntiAlias.java
@@ -0,0 +1,86 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.util.WeakHashMap;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JTextPane;
+import javax.swing.text.DefaultHighlighter;
+
+/** Graphical convenience methods for managing and constructing antialias-capable components.
+ *
+ * Thread Safety: Can be called only by the AWT event thread.
+ */
+
+public final class OurAntiAlias {
+
+ /** This constructor is private, since this utility class never needs to be instantiated. */
+ private OurAntiAlias() { }
+
+ /** Use anti-alias or not. */
+ private static boolean antiAlias = Util.onMac() || Util.onWindows();
+
+ /** Stores weak references of all objects that need to be redrawn when anti-alias setting changes. */
+ private static WeakHashMap map = new WeakHashMap();
+
+ /** Changes whether anti-aliasing should be done or not (when changed, we will automatically repaint all affected components). */
+ public static void enableAntiAlias(boolean enableAntiAlias) {
+ if (antiAlias == enableAntiAlias || Util.onMac() || Util.onWindows()) return;
+ antiAlias = enableAntiAlias;
+ for(JComponent x: map.keySet()) if (x!=null) { x.invalidate(); x.repaint(); x.validate(); }
+ }
+
+ /** Constructs an antialias-capable JLabel.
+ * @param attributes - see {@link edu.mit.csail.sdg.alloy4.OurUtil#make OurUtil.make(component, attributes...)}
+ */
+ public static JLabel label(String label, Object... attributes) {
+ JLabel ans = new JLabel(label) {
+ static final long serialVersionUID = 0;
+ @Override public void paint(Graphics gr) {
+ if (antiAlias && gr instanceof Graphics2D) {
+ ((Graphics2D)gr).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+ super.paint(gr);
+ }
+ };
+ OurUtil.make(ans, attributes);
+ map.put(ans, Boolean.TRUE);
+ return ans;
+ }
+
+ /** Constructs an antialias-capable JTextPane with a DefaultHighlighter associated with it.
+ * @param attributes - see {@link edu.mit.csail.sdg.alloy4.OurUtil#make OurUtil.make(component, attributes...)}
+ */
+ public static JTextPane pane(Object... attributes) {
+ JTextPane ans = new JTextPane() {
+ static final long serialVersionUID = 0;
+ @Override public void paint(Graphics gr) {
+ if (antiAlias && gr instanceof Graphics2D) {
+ ((Graphics2D)gr).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+ super.paint(gr);
+ }
+ };
+ OurUtil.make(ans, attributes);
+ ans.setHighlighter(new DefaultHighlighter());
+ map.put(ans, Boolean.TRUE);
+ return ans;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurBorder.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurBorder.java
new file mode 100644
index 00000000..3338ea76
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurBorder.java
@@ -0,0 +1,89 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+import javax.swing.border.Border;
+
+/** Graphical border on zero, one, two, three, or all four sides of a component.
+ *
+ * Thread Safety: Can be called only by the AWT event thread.
+ */
+
+public final class OurBorder implements Border {
+
+ /** non-null if we want to draw a border line of that Color above the component. */
+ private final Color top;
+
+ /** non-null if we want to draw a border line of that Color to the left of the component. */
+ private final Color left;
+
+ /** non-null if we want to draw a border line of that Color below the component. */
+ private final Color bottom;
+
+ /** non-null if we want to draw a border line of that Color to the right of the component. */
+ private final Color right;
+
+ /** Construct a Border object that draws a border on 0, 1, 2, 3, or all 4 sides of the component.
+ * Note: it paints the borders top, bottom, left, then right.
+ * @param top - nonnull if we want to draw a border line (with that color) above the component
+ * @param left - nonnull if we want to draw a border line (with that color) to the left of the component
+ * @param bottom - nonnull if we want to draw a border line (with that color) below the component
+ * @param right - nonnull if we want to draw a border line (with that color) to the right of the component
+ */
+ public OurBorder (Color top, Color left, Color bottom, Color right) {
+ this.top = top;
+ this.left = left;
+ this.bottom = bottom;
+ this.right = right;
+ }
+
+ /** Construct a Border object that draws a light gray line on 0, 1, 2, 3, or all 4 sides of the component.
+ * Note: it paints the borders top, bottom, left, then right.
+ * @param top - true if we want to draw a Color.LIGHT_GRAY border line above the component
+ * @param left - true if we want to draw a Color.LIGHT_GRAY border line to the left of the component
+ * @param bottom - true if we want to draw a Color.LIGHT_GRAY border line below the component
+ * @param right - true if we want to draw a Color.LIGHT_GRAY border line to the right of the component
+ */
+ public OurBorder (boolean top, boolean left, boolean bottom, boolean right) {
+ this.top = top ? Color.LIGHT_GRAY : null;
+ this.left = left ? Color.LIGHT_GRAY : null;
+ this.bottom = bottom ? Color.LIGHT_GRAY : null;
+ this.right = right ? Color.LIGHT_GRAY : null;
+ }
+
+ /** This method is called by Swing to actually draw the borders. */
+ public void paintBorder (Component component, Graphics graphics, int x, int y, int width, int height) {
+ if (width<1 || height<1) return;
+ Color old = graphics.getColor();
+ if (top != null) { graphics.setColor(top); graphics.drawLine(x, y, x+width-1, y ); }
+ if (bottom != null) { graphics.setColor(bottom); graphics.drawLine(x, y+height-1, x+width-1, y+height-1); }
+ if (left != null) { graphics.setColor(left); graphics.drawLine(x, y, x, y+height-1); }
+ if (right != null) { graphics.setColor(right); graphics.drawLine(x+width-1, y, x+width-1, y+height-1); }
+ graphics.setColor(old);
+ }
+
+ /** This method is called by Swing to retrieve the dimension of the border. */
+ public Insets getBorderInsets (Component c) {
+ return new Insets(top!=null?1:0, left!=null?1:0, bottom!=null?1:0, right!=null?1:0);
+ }
+
+ /** This method is called by Swing to find out whether this border object needs to fill in its own background. */
+ public boolean isBorderOpaque() { return true; }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurCheckbox.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurCheckbox.java
new file mode 100644
index 00000000..34109763
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurCheckbox.java
@@ -0,0 +1,99 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BoxLayout;
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/** Graphical checkbox.
+ *
+ *
Thread Safety: Can be called only by the AWT event thread.
+ */
+
+public abstract class OurCheckbox extends JPanel {
+
+ /** This ensures the class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** The icon to use when the checkbox is off. */
+ public static final Icon OFF = OurUtil.loadIcon("images/cb0.gif");
+
+ /** The icon to use when the checkbox is on. */
+ public static final Icon ON = OurUtil.loadIcon("images/cb1.gif");
+
+ /** The icon to use when the checkbox is off entirely. */
+ public static final Icon ALL_OFF = OurUtil.loadIcon("images/tcb01.gif");
+
+ /** The icon to use when the checkbox is on entirely. */
+ public static final Icon ALL_ON = OurUtil.loadIcon("images/tcb02.gif");
+
+ /** The icon to use when the checkbox is off due to inheritance. */
+ public static final Icon INH_OFF = OurUtil.loadIcon("images/tcb03.gif");
+
+ /** The icon to use when the checkbox is on due to inheritance. */
+ public static final Icon INH_ON = OurUtil.loadIcon("images/tcb04.gif");
+
+ /** The underlying JCheckBox object. */
+ private final JCheckBox jbox;
+
+ /** The JLabel object for displaying a label next to the checkbox. */
+ private final JLabel jlabel;
+
+ /** Constructs a OurCheckbox object.
+ * @param label - the label to display next to the checkbox
+ * @param tooltip - the tool tip to show when the mouse hovers over this checkbox
+ * @param icon - the initial icon to display (should be one of ON/OFF/ALL_ON/ALL_OFF/INH_ON/INH_OFF)
+ */
+ public OurCheckbox(String label, String tooltip, Icon icon) {
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ jbox = new JCheckBox(icon);
+ jbox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Icon icon = do_action();
+ if (icon != jbox.getIcon()) jbox.setIcon(icon);
+ }
+ });
+ jbox.setMaximumSize(jbox.getPreferredSize());
+ jbox.setToolTipText(tooltip);
+ jlabel = OurUtil.label(label, tooltip);
+ if (icon==ON || icon==OFF) { add(jbox); add(jlabel); } else { add(jlabel); add(jbox); }
+ setAlignmentX(RIGHT_ALIGNMENT);
+ }
+
+ /** This method is called when the user clicks on the checkbox; subclasses should override this to provide the custom behavior. */
+ public abstract Icon do_action();
+
+ /** This method is called by Swing to enable/disable a component. */
+ @Override public final void setEnabled(boolean enabled) {
+ if (jbox != null) jbox.setEnabled(enabled);
+ if (jlabel != null) jlabel.setEnabled(enabled);
+ // jbox and jlabel may be null if during the constructor, some method call causes Swing to call this method early
+ }
+
+ /** This method is called by Swing to change its background color. */
+ @Override public final void setBackground(Color color) {
+ super.setBackground(color);
+ if (jbox != null) jbox.setBackground(color);
+ if (jlabel != null) jlabel.setBackground(color);
+ // jbox and jlabel may be null if during the constructor, some method call causes Swing to call this method early
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurCombobox.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurCombobox.java
new file mode 100644
index 00000000..3b25d327
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurCombobox.java
@@ -0,0 +1,97 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.util.Vector;
+import javax.swing.Icon;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.border.EmptyBorder;
+
+/** Graphical combobox.
+ *
+ *
Thread Safety: Can be called only by the AWT event thread.
+ */
+
+public class OurCombobox extends JComboBox {
+
+ /** This ensures the class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** This caches a preconstructed JLabel that is used for the rendering of each Combo value. */
+ private static JLabel jlabel;
+
+ /** Subclass can override this method to provide the custom text for any given value (It should return "" if no text is needed) */
+ public String do_getText(Object value) { return String.valueOf(value); }
+
+ /** Subclass can override this method to provide the custom icon for any given value (It should return null if no icon is needed) */
+ public Icon do_getIcon(Object value) { return null; }
+
+ /** Subclass can override this method to react upon selection change. */
+ public void do_changed(Object newValue) { }
+
+ /** This helper method makes a copy of the list, and then optionally prepend null at the beginning of the list. */
+ private static Vector do_copy (Object[] list, boolean addNull) {
+ Vector answer = new Vector(list.length + (addNull ? 1 : 0));
+ if (addNull) answer.add(null);
+ for(int i=0; i 25) height = 25; // Otherwise, the height is too big on Windows
+ setPreferredSize(new Dimension(width, height));
+ setMaximumSize(new Dimension(width, height));
+ if (!Util.onWindows() && !Util.onMac()) setBorder(new EmptyBorder(4, 3, 4, 0));
+ }
+ if (initialValue != null) { setSelectedItem(initialValue); }
+ addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) { do_changed(getSelectedItem()); }
+ });
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurConsole.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurConsole.java
new file mode 100644
index 00000000..1bf57a79
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurConsole.java
@@ -0,0 +1,279 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.Font;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.swing.AbstractAction;
+import javax.swing.JPanel;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.KeyStroke;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+
+/** Graphical input/output prompt.
+ *
+ * This class's constructor takes a Computer object, then constructs a JScrollPane
+ * in which the user can type commands, and the output from the Computer object will be displayed.
+ * Empty input lines are ignored.
+ * This interactive prompt supports UP and DOWN arrow command histories and basic copy/cut/paste editing.
+ *
+ *
For each user input, if the Computer object returns a String, it is displayed in blue.
+ * But if the Computer object throws an exception, the exception will be displayed in red.
+ *
+ *
Thread Safety: Can be called only by the AWT event thread.
+ */
+
+public final class OurConsole extends JScrollPane {
+
+ /** This ensures the class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** The style for default text. */
+ private final AttributeSet plain = style("Verdana", 14, false, Color.BLACK, 0);
+
+ /** The style for bold text. */
+ private final AttributeSet bold = style("Verdana", 14, true, Color.BLACK, 0);
+
+ /** The style for successful result. */
+ private final AttributeSet good = style("Verdana", 14, false, Color.BLUE, 15);
+
+ /** The style for failed result. */
+ private final AttributeSet bad = style("Verdana", 14, false, Color.RED, 15);
+
+ /** The number of characters that currently exist above the horizontal divider bar.
+ * (The interactive console is composed of a JTextPane which contains 0 or more input/output pairs, followed
+ * by a horizontal divider bar, followed by an embedded sub-JTextPane (where the user can type in the next input))
+ */
+ private int len = 0;
+
+ /** The main JTextPane containing 0 or more input/output pairs, followed by a horizontal bar, followed by this.sub */
+ private final JTextPane main = do_makeTextPane(false, 5, 5, 5);
+
+ /** The sub JTextPane where the user can type in the next command. */
+ private final JTextPane sub = do_makeTextPane(true, 10, 10, 0);
+
+ /** The history of all commands entered so far, plus an extra String representing the user's next command. */
+ private final List history = new ArrayList(); { history.add(""); }
+
+ /** The position in this.history that is currently showing. */
+ private int browse = 0;
+
+ /** Helper method that construct a mutable style with the given font name, font size, boldness, color, and left indentation. */
+ static MutableAttributeSet style(String fontName, int fontSize, boolean boldness, Color color, int leftIndent) {
+ MutableAttributeSet s = new SimpleAttributeSet();
+ StyleConstants.setFontFamily(s, fontName);
+ StyleConstants.setFontSize(s, fontSize);
+ StyleConstants.setBold(s, boldness);
+ StyleConstants.setForeground(s, color);
+ StyleConstants.setLeftIndent(s, leftIndent);
+ return s;
+ }
+
+ /** Construct a JScrollPane that allows the user to interactively type in commands and see replies.
+ *
+ * @param computer - this object is used to evaluate the user input
+ *
+ * @param syntaxHighlighting - if true, the "input area" will be syntax-highlighted
+ *
+ * @param initialMessages - this is a list of String and Boolean; each String is printed to the screen as is,
+ * and Boolean.TRUE will turn subsequent text bold, and Boolean.FALSE will turn subsequent text non-bold.
+ */
+ public OurConsole(final Computer computer, boolean syntaxHighlighting, Object... initialMessages) {
+ super(VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ if (syntaxHighlighting) { sub.setDocument(new OurSyntaxUndoableDocument("Verdana", 14)); }
+ setViewportView(main);
+ // show the initial message
+ AttributeSet st = plain;
+ for(Object x: initialMessages) {
+ if (x instanceof Boolean) st = (Boolean.TRUE.equals(x) ? bold : plain); else do_add(-1, String.valueOf(x), st);
+ }
+ do_add(-1, "\n", plain); // we must add a linebreak to ensure that subsequent text belong to a "different paragraph"
+ // insert the divider and the sub JTextPane
+ StyledDocument doc = main.getStyledDocument();
+ JPanel divider = new JPanel(); divider.setBackground(Color.LIGHT_GRAY); divider.setPreferredSize(new Dimension(1,1));
+ MutableAttributeSet dividerStyle = new SimpleAttributeSet(); StyleConstants.setComponent(dividerStyle, divider);
+ MutableAttributeSet inputStyle = new SimpleAttributeSet(); StyleConstants.setComponent(inputStyle, sub);
+ len = doc.getLength();
+ do_add(-1, " \n", dividerStyle); // The space character won't be displayed; it will instead be drawn as a divider
+ do_add(-1, " \n", inputStyle); // The space character won't be displayed; it will instead display the input buffer
+ final Caret subCaret = sub.getCaret(), mainCaret = main.getCaret();
+ // When caret moves in the sub JTextPane, we cancel any active selection in the main JTextPane
+ subCaret.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ if (mainCaret.getMark() != mainCaret.getDot()) mainCaret.setDot(mainCaret.getDot());
+ }
+ });
+ // When caret moves in the main JTextPane, we cancel any active selection in the sub JTextPane
+ mainCaret.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ if (subCaret.getMark() != subCaret.getDot()) subCaret.setDot(subCaret.getDot());
+ }
+ });
+ // now, create the paste/copy/cut actions
+ AbstractAction alloy_paste = new AbstractAction("alloy_paste") {
+ static final long serialVersionUID = 0;
+ public void actionPerformed(ActionEvent x) { sub.paste(); }
+ };
+ AbstractAction alloy_copy = new AbstractAction("alloy_copy") {
+ static final long serialVersionUID = 0;
+ public void actionPerformed(ActionEvent x) {
+ if (sub.getSelectionStart() != sub.getSelectionEnd()) sub.copy(); else main.copy();
+ }
+ };
+ AbstractAction alloy_cut = new AbstractAction("alloy_cut") {
+ static final long serialVersionUID = 0;
+ public void actionPerformed(ActionEvent x) {
+ if (sub.getSelectionStart() != sub.getSelectionEnd()) sub.cut(); else main.copy();
+ }
+ };
+ // create the keyboard associations: ctrl-{c,v,x,insert} and shift-{insert,delete}
+ for(JTextPane x: Arrays.asList(main, sub)) {
+ x.getActionMap().put("alloy_paste", alloy_paste);
+ x.getActionMap().put("alloy_copy", alloy_copy);
+ x.getActionMap().put("alloy_cut", alloy_cut);
+ x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_V, Event.CTRL_MASK), "alloy_paste");
+ x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.CTRL_MASK), "alloy_copy");
+ x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.CTRL_MASK), "alloy_cut");
+ x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, Event.SHIFT_MASK), "alloy_paste");
+ x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, Event.CTRL_MASK), "alloy_copy");
+ x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, Event.SHIFT_MASK), "alloy_cut");
+ }
+ // configure so that, upon receiving focus, we automatically focus and scroll to the sub-JTextPane
+ FocusAdapter focus = new FocusAdapter() {
+ public void focusGained(FocusEvent e) {
+ sub.requestFocusInWindow();
+ sub.scrollRectToVisible(new Rectangle(0, sub.getY(), 1, sub.getHeight()));
+ }
+ };
+ addFocusListener(focus);
+ sub.addFocusListener(focus);
+ main.addFocusListener(focus);
+ // configure so that mouse clicks in the main JTextPane will immediately transfer focus to the sub JTextPane
+ main.addMouseListener(new MouseAdapter() {
+ public void mousePressed(MouseEvent e) { sub.requestFocusInWindow(); }
+ public void mouseClicked(MouseEvent e) { sub.requestFocusInWindow(); }
+ });
+ // configure the behavior for PAGE_UP, PAGE_DOWN, UP, DOWN, TAB, and ENTER
+ sub.addKeyListener(new KeyListener() {
+ public void keyTyped(KeyEvent e) {
+ if (e.getKeyChar() == '\t') { e.consume(); }
+ if (e.getKeyChar() == '\n') { e.consume(); String cmd = sub.getText(); sub.setText(""); do_command(computer, cmd); }
+ }
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode()==KeyEvent.VK_TAB) e.consume();
+ if (e.getKeyCode() == KeyEvent.VK_PAGE_UP) { e.consume(); do_pageup(); }
+ if (e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) { e.consume(); do_pagedown(); }
+ if (e.getKeyCode() == KeyEvent.VK_UP) {
+ e.consume();
+ if (browse == history.size() - 1) { history.set(browse, sub.getText()); }
+ if (browse > 0 && browse - 1 < history.size()) { browse--; sub.setText(history.get(browse)); }
+ }
+ if (e.getKeyCode() == KeyEvent.VK_DOWN) {
+ e.consume();
+ if (browse < history.size() - 1) { browse++; sub.setText(history.get(browse)); }
+ }
+ }
+ public void keyReleased(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB) e.consume();
+ }
+ });
+ }
+
+ /** This helper method constructs a JTextPane with the given settings. */
+ private static JTextPane do_makeTextPane(boolean editable, int topMargin, int bottomMargin, int otherMargin) {
+ JTextPane x = OurAntiAlias.pane(Color.BLACK, Color.WHITE, new Font("Verdana", Font.PLAIN, 14));
+ x.setEditable(editable);
+ x.setAlignmentX(0);
+ x.setAlignmentY(0);
+ x.setCaretPosition(0);
+ x.setMargin(new Insets(topMargin, otherMargin, bottomMargin, otherMargin));
+ return x;
+ }
+
+ /** This method processes a user command. */
+ private void do_command(Computer computer, String cmd) {
+ cmd = cmd.trim();
+ if (cmd.length()==0) return;
+ StyledDocument doc = main.getStyledDocument();
+ if (history.size()>=2 && cmd.equals(history.get(history.size()-2))) {
+ // If the user merely repeated the most recent command, then don't grow the history
+ history.set(history.size()-1, "");
+ } else {
+ // Otherwise, grow the history
+ history.set(history.size()-1, cmd);
+ history.add("");
+ }
+ browse = history.size()-1;
+ // display the command
+ int old = doc.getLength(); do_add(len, cmd+"\n\n", plain); len += (doc.getLength() - old);
+ // perform the computation
+ boolean isBad = false;
+ try { cmd = computer.compute(cmd); } catch(Throwable ex) { cmd = ex.toString(); isBad = true; }
+ int savePosition = len;
+ // display the outcome
+ old = doc.getLength(); do_add(len, cmd.trim()+"\n\n", (isBad ? bad : good)); len += (doc.getLength() - old);
+ // indent the outcome
+ main.setSelectionStart(savePosition+1); main.setSelectionEnd(len); main.setParagraphAttributes(good, false);
+ // redraw then scroll to the bottom
+ invalidate();
+ repaint();
+ validate();
+ sub.scrollRectToVisible(new Rectangle(0, sub.getY(), 1, sub.getHeight()));
+ do_pagedown(); // need to do this after the validate() so that the scrollbar knows the new limit
+ }
+
+ /** Performs "page up" in the JScrollPane. */
+ private void do_pageup() {
+ JScrollBar bar = getVerticalScrollBar();
+ bar.setValue(bar.getValue() - 200);
+ }
+
+ /** Performs "page down" in the JScrollPane. */
+ private void do_pagedown() {
+ JScrollBar bar = getVerticalScrollBar();
+ bar.setValue(bar.getValue() + 200);
+ }
+
+ /** Insert the given text into the given location and with the given style if where>=0; append the text if where<0. */
+ private void do_add(int where, String text, AttributeSet style) {
+ StyledDocument doc = main.getStyledDocument();
+ try { doc.insertString(where >= 0 ? where : doc.getLength(), text, style); } catch(BadLocationException ex) { }
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurDialog.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurDialog.java
new file mode 100644
index 00000000..9246113f
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurDialog.java
@@ -0,0 +1,247 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Locale;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FileDialog;
+import java.awt.Frame;
+import java.awt.GraphicsEnvironment;
+import java.awt.HeadlessException;
+import java.awt.event.KeyListener;
+import java.awt.event.KeyEvent;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.filechooser.FileFilter;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+
+/** Graphical dialog methods for asking the user some questions.
+ *
+ * Thread Safety: Can be called only by the AWT event thread.
+ */
+
+public final class OurDialog {
+
+ /** The constructor is private, since this utility class never needs to be instantiated. */
+ private OurDialog() { }
+
+ /** Helper method for constructing an always-on-top modal dialog. */
+ private static Object show(String title, int type, Object message, Object[] options, Object initialOption) {
+ if (options == null) { options = new Object[]{"Ok"}; initialOption = "Ok"; }
+ JOptionPane p = new JOptionPane(message, type, JOptionPane.DEFAULT_OPTION, null, options, initialOption);
+ p.setInitialValue(initialOption);
+ JDialog d = p.createDialog(null, title);
+ p.selectInitialValue();
+ d.setAlwaysOnTop(true);
+ d.setVisible(true);
+ d.dispose();
+ return p.getValue();
+ }
+
+ /** Popup the given informative message, then ask the user to click Close to close it. */
+ public static void showmsg(String title, Object... msg) {
+ JButton dismiss = new JButton(Util.onMac() ? "Dismiss" : "Close");
+ Object[] objs = new Object[msg.length + 1];
+ System.arraycopy(msg, 0, objs, 0, msg.length);
+ objs[objs.length - 1] = OurUtil.makeH(null, dismiss, null);
+ JOptionPane about = new JOptionPane(objs, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, new Object[]{});
+ JDialog dialog = about.createDialog(null, title);
+ dismiss.addActionListener(Runner.createDispose(dialog));
+ dialog.setAlwaysOnTop(true);
+ dialog.setVisible(true);
+ dialog.dispose();
+ }
+
+ /** Popup the given error message. */
+ public static void alert(Object message) {
+ show("Error", ERROR_MESSAGE, message, null, null);
+ }
+
+ /** Popup the given error message, then terminate the program. */
+ public static void fatal(Object message) {
+ try { show("Fatal Error", ERROR_MESSAGE, message, null, null); } finally { System.exit(1); }
+ }
+
+ /** Ask if the user wishes to save the file, discard the file, or cancel the entire operation (default is cancel).
+ * @return 'c' if cancel, 's' if save, 'd' if discard
+ */
+ public static char askSaveDiscardCancel(String description) {
+ description = description + " has not been saved. Do you want to";
+ Object ans = show(
+ "Warning", WARNING_MESSAGE,
+ new String[] {description, "cancel the operation, close the file without saving, or save it and close?"},
+ new Object[] {"Save", "Don't Save", "Cancel"},
+ "Cancel"
+ );
+ return (ans == "Save") ? 's' : (ans == "Don't Save" ? 'd' : 'c');
+ }
+
+ /** Ask if the user really wishes to overwrite the file (default is no).
+ * @return true if the user wishes to overwrite the file, false if the user does not wish to overwrite the file.
+ */
+ public static boolean askOverwrite(String filename) {
+ return "Overwrite" == show("Warning: file already exists", WARNING_MESSAGE,
+ new String[] {"The file \"" + filename + "\"", "already exists. Do you wish to overwrite it?"},
+ new String[] {"Overwrite", "Cancel"},
+ "Cancel"
+ );
+ }
+
+ /** This caches the result of the call to get all fonts. */
+ private static String[] allFonts = null;
+
+ /** Returns true if a font with that name exists on the system (comparison is case-insensitive). */
+ public synchronized static boolean hasFont(String fontname) {
+ if (allFonts == null) allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+ for(int i = 0; i < allFonts.length; i++) if (fontname.compareToIgnoreCase(allFonts[i]) == 0) return true;
+ return false;
+ }
+
+ /** Asks the user to choose a font; returns "" if the user cancels the request. */
+ public synchronized static String askFont() {
+ if (allFonts == null) allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+ JComboBox jcombo = new OurCombobox(allFonts);
+ Object ans = show("Font", JOptionPane.INFORMATION_MESSAGE,
+ new Object[] {"Please choose the new font:", jcombo}, new Object[] {"Ok", "Cancel"}, "Cancel"
+ );
+ Object value = jcombo.getSelectedItem();
+ if (ans=="Ok" && (value instanceof String)) return (String)value; else return "";
+ }
+
+ /** True if we should use AWT (instead of Swing) to display the OPEN and SAVE dialog. */
+ private static boolean useAWT = Util.onMac();
+
+ /** Use the platform's preferred file chooser to ask the user to select a file.
+ * Note: if it is a save operation, and the user didn't include an extension, then we'll add the extension.
+ * @param isOpen - true means this is an Open operation; false means this is a Save operation
+ * @param dir - the initial directory (or null if we want to use the default)
+ * @param ext - the file extension (including "."; using lowercase letters; for example, ".als") or ""
+ * @param description - the description for the given extension
+ * @return null if the user didn't choose anything, otherwise it returns the selected file
+ */
+ public static File askFile (boolean isOpen, String dir, final String ext, final String description) {
+ if (dir == null) dir = Util.getCurrentDirectory();
+ if (!(new File(dir).isDirectory())) dir = System.getProperty("user.home");
+ dir = Util.canon(dir);
+ String ans;
+ if (useAWT) {
+ Frame parent = new Frame("Alloy File Dialog"); // this window is unused and not shown; needed by FileDialog and nothing more
+ FileDialog open = new FileDialog(parent, isOpen ? "Open..." : "Save...");
+ open.setAlwaysOnTop(true);
+ open.setMode(isOpen ? FileDialog.LOAD : FileDialog.SAVE);
+ open.setDirectory(dir);
+ if (ext.length()>0) open.setFilenameFilter(new FilenameFilter() {
+ public boolean accept(File dir, String name) { return name.toLowerCase(Locale.US).endsWith(ext); }
+ });
+ open.setVisible(true); // This method blocks until the user either chooses something or cancels the dialog.
+ parent.dispose();
+ if (open.getFile() == null) return null; else ans = open.getDirectory() + File.separatorChar + open.getFile();
+ } else {
+ try {
+ JFileChooser open = new JFileChooser(dir) {
+ private static final long serialVersionUID = 0;
+ public JDialog createDialog(Component parent) throws HeadlessException {
+ JDialog dialog = super.createDialog(null);
+ dialog.setAlwaysOnTop(true);
+ return dialog;
+ }
+ };
+ open.setDialogTitle(isOpen ? "Open..." : "Save...");
+ open.setApproveButtonText(isOpen ? "Open" : "Save");
+ open.setDialogType(isOpen ? JFileChooser.OPEN_DIALOG : JFileChooser.SAVE_DIALOG);
+ if (ext.length()>0) open.setFileFilter(new FileFilter() {
+ public boolean accept(File file) { return !file.isFile() || file.getPath().toLowerCase(Locale.US).endsWith(ext); }
+ public String getDescription() { return description; }
+ });
+ if (open.showDialog(null, null) != JFileChooser.APPROVE_OPTION || open.getSelectedFile() == null) return null;
+ ans = open.getSelectedFile().getPath();
+ } catch(Exception ex) {
+ // Some combination of Windows version and JDK version will trigger this failure.
+ // In such a case, we'll fall back to using the "AWT" file open dialog
+ useAWT = true;
+ return askFile(isOpen, dir, ext, description);
+ }
+ }
+ if (!isOpen) {
+ int lastSlash = ans.lastIndexOf(File.separatorChar);
+ int lastDot = (lastSlash>=0) ? ans.indexOf('.', lastSlash) : ans.indexOf('.');
+ if (lastDot < 0) ans = ans + ext;
+ }
+ return new File(Util.canon(ans));
+ }
+
+ /** Display "msg" in a modal dialog window, and ask the user to choose "yes" versus "no" (default is "no"). */
+ public static boolean yesno(Object msg, String yes, String no) {
+ return show("Question", WARNING_MESSAGE, msg, new Object[]{yes, no}, no) == yes;
+ }
+
+ /** Display "msg" in a modal dialog window, and ask the user to choose "Yes" versus "No" (default is "no"). */
+ public static boolean yesno(Object msg) { return yesno(msg, "Yes", "No"); }
+
+ /** Display a modal dialog window containing the "objects"; returns true iff the user clicks Ok. */
+ public static boolean getInput(String title, Object... objects) {
+ // If there is a JTextField or a JTextArea here, then let the first JTextField or JTextArea be the initially focused widget
+ Object main = "Ok";
+ for(Object obj: objects) if (obj instanceof JTextField || obj instanceof JTextArea) { main = obj; break; }
+ // Construct the dialog panel
+ final JOptionPane pane = new JOptionPane(objects, QUESTION_MESSAGE, YES_NO_OPTION, null, new Object[]{"Ok", "Cancel"}, main);
+ final JDialog dialog = pane.createDialog(null, title);
+ // For each JTextField and JCheckBox, add a KeyListener that detects VK_ENTER and treat it as if the user clicked OK
+ for(Object obj: objects) if (obj instanceof JTextField || obj instanceof JCheckBox) {
+ ((JComponent)obj).addKeyListener(new KeyListener() {
+ public void keyPressed(KeyEvent e) { if (e.getKeyCode()==KeyEvent.VK_ENTER) { pane.setValue("Ok"); dialog.dispose(); } }
+ public void keyReleased(KeyEvent e) { }
+ public void keyTyped(KeyEvent e) { }
+ });
+ }
+ dialog.setAlwaysOnTop(true);
+ dialog.setVisible(true); // This method blocks until the user either chooses something or cancels the dialog.
+ dialog.dispose();
+ return pane.getValue() == "Ok";
+ }
+
+ /** Display a simple non-modal window showing some text. */
+ public static JFrame showtext(String title, String text) {
+ JFrame window = new JFrame(title);
+ JButton done = new JButton("Close");
+ done.addActionListener(Runner.createDispose(window));
+ JScrollPane scrollPane = OurUtil.scrollpane(OurUtil.textarea(text, 20, 60, false, false));
+ window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ window.getContentPane().setLayout(new BorderLayout());
+ window.getContentPane().add(scrollPane, BorderLayout.CENTER);
+ window.getContentPane().add(done, BorderLayout.SOUTH);
+ window.pack();
+ window.setSize(500, 500);
+ window.setLocationRelativeTo(null);
+ window.setVisible(true);
+ return window;
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurHighlighter.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurHighlighter.java
new file mode 100644
index 00000000..183ff4d9
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurHighlighter.java
@@ -0,0 +1,58 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Highlighter;
+import javax.swing.text.JTextComponent;
+
+/** Graphica highlighter.
+ *
+ *
Thread Safety: Can be called only by the AWT event thread.
+ */
+
+public final class OurHighlighter implements Highlighter.HighlightPainter {
+
+ /** The color to use when drawing highlights. */
+ public final Color color;
+
+ /** Construct a highlighter with the given color. */
+ public OurHighlighter(Color color) { this.color = color; }
+
+ /** This method is called by Swing to draw highlights. */
+ public void paint(Graphics gr, int start, int end, Shape shape, JTextComponent text) {
+ Color old = gr.getColor();
+ gr.setColor(color);
+ try {
+ Rectangle box = shape.getBounds(), a = text.getUI().modelToView(text, start), b = text.getUI().modelToView(text, end);
+ if (a.y == b.y) {
+ // same line (Note: furthermore, if start==end, then we draw all the way to the right edge)
+ Rectangle r = a.union(b);
+ gr.fillRect(r.x, r.y, (r.width<=1 ? (box.x + box.width - r.x) : r.width), r.height);
+ } else {
+ // Multiple lines; (Note: on first line we'll draw from "start" and extend to rightmost)
+ gr.fillRect(a.x, a.y, box.x + box.width - a.x, a.height);
+ if (a.y + a.height < b.y) gr.fillRect(box.x, a.y + a.height, box.width, b.y - (a.y + a.height));
+ gr.fillRect(box.x, b.y, b.x - box.x, b.height);
+ }
+ } catch (BadLocationException e) { } // Failure to highlight is not fatal
+ gr.setColor(old);
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurPDFWriter.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurPDFWriter.java
new file mode 100644
index 00000000..7346cb72
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurPDFWriter.java
@@ -0,0 +1,277 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Color;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.PathIterator;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/** Graphical convenience methods for producing PDF files.
+ *
+ *
This implementation explicitly generates a very simple 8.5 inch by 11 inch one-page PDF consisting of graphical operations.
+ * Hopefully this class will no longer be needed in the future once Java comes with better PDF support.
+ */
+
+public final strictfp class OurPDFWriter {
+
+ /** The filename. */
+ private final String filename;
+
+ /** The page width (in terms of dots). */
+ private final long width;
+
+ /** The page height (in terms of dots). */
+ private final long height;
+
+ /** Latest color expressed as RGB (-1 if none has been explicitly set so far) */
+ private int color = -1;
+
+ /** Latest line style (0=normal, 1=bold, 2=dotted, 3=dashed) */
+ private int line = 0;
+
+ /** The buffer that will store the list of graphical operations issued so far (null if close() has been called successfully) */
+ private ByteBuffer buf = new ByteBuffer();
+
+ /** Begin a blank PDF file with the given dots-per-inch and the given scale (the given file, if existed, will be overwritten)
+ * @throws IllegalArgumentException if dpi is less than 50 or is greater than 3000
+ */
+ public OurPDFWriter(String filename, int dpi, double scale) {
+ if (dpi<50 || dpi>3000) throw new IllegalArgumentException("The DPI must be between 50 and 3000");
+ this.filename = filename;
+ width = dpi*8L + (dpi/2L); // "8.5 inches"
+ height = dpi*11L; // "11 inches"
+ // Write the default settings, and flip (0, 0) into the top-left corner of the page, scale the page, then leave 0.5" margin
+ buf.write("q\n" + "1 J\n" + "1 j\n" + "[] 0 d\n" + "1 w\n" + "1 0 0 -1 0 ").writes(height).write("cm\n");
+ buf.writes(scale).write("0 0 ").writes(scale).writes(dpi/2.0).writes(dpi/2.0).write("cm\n");
+ buf.write("1 0 0 1 ").writes(dpi/2.0).writes(dpi/2.0).write("cm\n");
+ }
+
+ /** Changes the color for subsequent graphical drawing. */
+ public OurPDFWriter setColor(Color color) {
+ int rgb = color.getRGB() & 0xFFFFFF, r = (rgb>>16), g = (rgb>>8) & 0xFF, b = (rgb & 0xFF);
+ if (this.color == rgb) return this; else this.color = rgb; // no need to change
+ buf.writes(r/255.0).writes(g/255.0).writes(b/255.0).write("RG\n");
+ buf.writes(r/255.0).writes(g/255.0).writes(b/255.0).write("rg\n");
+ return this;
+ }
+
+ /** Changes the line style to be normal. */
+ public OurPDFWriter setNormalLine() { if (line!=0) buf.write("1 w [] 0 d\n"); line=0; return this; }
+
+ /** Changes the line style to be bold. */
+ public OurPDFWriter setBoldLine() { if (line!=1) buf.write("2 w [] 0 d\n"); line=1; return this; }
+
+ /** Changes the line style to be dotted. */
+ public OurPDFWriter setDottedLine() { if (line!=2) buf.write("1 w [1 3] 0 d\n"); line=2; return this; }
+
+ /** Changes the line style to be dashed. */
+ public OurPDFWriter setDashedLine() { if (line!=3) buf.write("1 w [6 3] 0 d\n"); line=3; return this; }
+
+ /** Shifts the coordinate space by the given amount. */
+ public OurPDFWriter shiftCoordinateSpace(int x, int y) { buf.write("1 0 0 1 ").writes(x).writes(y).write("cm\n"); return this; }
+
+ /** Draws a line from (x1, y1) to (x2, y2). */
+ public OurPDFWriter drawLine(int x1, int y1, int x2, int y2) {
+ buf.writes(x1).writes(y1).write("m ").writes(x2).writes(y2).write("l S\n"); return this;
+ }
+
+ /** Draws a circle of the given radius, centered at (0, 0). */
+ public OurPDFWriter drawCircle(int radius, boolean fillOrNot) {
+ double k = (0.55238 * radius); // Approximate a circle using 4 cubic bezier curves
+ buf.writes( radius).write("0 m ");
+ buf.writes( radius).writes( k).writes( k).writes( radius).write("0 ") .writes( radius).write("c ");
+ buf.writes( -k).writes( radius).writes(-radius).writes( k).writes(-radius).write("0 c ");
+ buf.writes(-radius).writes( -k).writes( -k).writes(-radius).write("0 ") .writes(-radius).write("c ");
+ buf.writes( k).writes(-radius).writes( radius).writes( -k).writes(radius) .write(fillOrNot ? "0 c f\n" : "0 c S\n");
+ return this;
+ }
+
+ /** Draws a shape. */
+ public OurPDFWriter drawShape(Shape shape, boolean fillOrNot) {
+ if (shape instanceof Polygon) {
+ Polygon obj = (Polygon)shape;
+ for(int i = 0; i < obj.npoints; i++) buf.writes(obj.xpoints[i]).writes(obj.ypoints[i]).write(i==0 ? "m\n" : "l\n");
+ buf.write("h\n");
+ } else {
+ double moveX = 0, moveY = 0, nowX = 0, nowY = 0, pt[] = new double[6];
+ for(PathIterator it = shape.getPathIterator(null); !it.isDone(); it.next()) switch(it.currentSegment(pt)) {
+ case PathIterator.SEG_MOVETO:
+ nowX = moveX = pt[0]; nowY = moveY = pt[1]; buf.writes(nowX).writes(nowY).write("m\n"); break;
+ case PathIterator.SEG_CLOSE:
+ nowX = moveX; nowY = moveY; buf.write("h\n"); break;
+ case PathIterator.SEG_LINETO:
+ nowX = pt[0]; nowY = pt[1]; buf.writes(nowX).writes(nowY).write("l\n"); break;
+ case PathIterator.SEG_CUBICTO:
+ nowX = pt[4]; nowY = pt[5];
+ buf.writes(pt[0]).writes(pt[1]).writes(pt[2]).writes(pt[3]).writes(nowX).writes(nowY).write("c\n"); break;
+ case PathIterator.SEG_QUADTO: // Convert quadratic bezier into cubic bezier using de Casteljau algorithm
+ double px = nowX + (pt[0] - nowX)*(2.0/3.0), qx = px + (pt[2] - nowX)/3.0;
+ double py = nowY + (pt[1] - nowY)*(2.0/3.0), qy = py + (pt[3] - nowY)/3.0;
+ nowX = pt[2]; nowY = pt[3];
+ buf.writes(px).writes(py).writes(qx).writes(qy).writes(nowX).writes(nowY).write("c\n"); break;
+ }
+ }
+ buf.write(fillOrNot ? "f\n" : "S\n");
+ return this;
+ }
+
+ /* PDF File Structure Summary:
+ * ===========================
+ *
+ * File should ideally start with the following 13 bytes: "%PDF-1.3" 10 "%" -127 10 10
+ * Now comes one or more objects.
+ * One simple single-page arrangement is to have exactly 5 objects in this order: FONT, CONTENT, PAGE, PAGES, and CATALOG.
+ *
+ * Font Object (1 because FONT is #1)
+ * ==================================
+ *
+ * 1 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj\n\n
+ *
+ * Content Object (2 because CONTENT is #2) (${LEN} is the number of bytes in ${CONTENT} when compressed)
+ * ======================================================================================================
+ *
+ * 2 0 obj << /Length ${LEN} /Filter /FlateDecode >> stream\r\n${CONTENT}endstream endobj\n\n
+ *
+ * Here is a quick summary of various PDF Graphics operations
+ * ==========================================================
+ *
+ * $x $y m --> begins a new path at the given coordinate
+ * $x $y l --> add the segment (LASTx,LASTy)..($x,$y) to the current path
+ * $cx $cy $x $y v --> add the bezier curve (LASTx,LASTy)..(LASTx,LASTy)..($cx,$cy)..($x,$y) to the current path
+ * $cx $cy $x $y y --> add the bezier curve (LASTx,LASTy)....($cx,$cy).....($x,$y)...($x,$y) to the current path
+ * $ax $ay $bx $by $x $y c --> add the bezier curve (LASTx,LASTy)....($ax,$ay)....($bx,$by)..($x,$y) to the current path
+ * h --> close the current subpath by straightline segment from current point to the start of this subpath
+ * $x $y $w $h re --> append a rectangle to the current path as a complete subpath with lower-left corner at $x $y
+ *
+ * S --> assuming we've just described a path, draw the path
+ * f --> assuming we've just described a path, fill the path
+ * B --> assuming we've just described a path, fill then draw the path
+ *
+ * q --> saves the current graphics state
+ * 1 J --> sets the round cap
+ * 1 j --> sets the round joint
+ * [] 0 d --> sets the dash pattern as SOLID
+ * [4 6] 0 d --> sets the dash pattern as 4 UNITS ON then 6 UNITS OFF
+ * 5 w --> sets the line width as 5 UNITS
+ * $a $b $c $d $e $f cm --> appends the given matrix; for example, [1 0 0 1 dx dy] means "translation to dx dy"
+ * $R $G $B RG --> sets the stroke color (where 0 <= $R <= 1, etc)
+ * $R $G $B rg --> sets the fill color (where 0 <= $R <= 1, etc)
+ * Q --> restores the current graphics state
+ *
+ * Page Object (3 because PAGE is #3) (4 beacuse PAGES is #4) (2 because CONTENTS is #2)
+ * =====================================================================================
+ *
+ * 3 0 obj << /Type /Page /Parent 4 0 R /Contents 2 0 R >> endobj\n\n
+ *
+ * Pages Object (4 because PAGES is #4) (3 because PAGE is #3) (${W} is 8.5*DPI, ${H} is 11*DPI) (1 because FONT is #1)
+ * ====================================================================================================================
+ *
+ * 4 0 obj << /Type /Pages /Count 1 /Kids [3 0 R] /MediaBox [0 0 ${W} ${H}] /Resources << /Font << /F1 1 0 R >> >> >> endobj\n\n
+ *
+ * Catalog Object (5 because CATALOG is #5) (4 because PAGES is #4)
+ * ================================================================
+ *
+ * 5 0 obj << /Type /Catalog /Pages 4 0 R >> endobj\n\n
+ *
+ * END_OF_FILE format (assuming we have obj1 obj2 obj3 obj4 obj5 where obj5 is the "PDF Catalog")
+ * ==============================================================================================
+ *
+ * xref\n
+ * 0 6\n // 6 is because it's the number of objects plus 1
+ * 0000000000 65535 f\r\n
+ * ${offset1} 00000 n\r\n // ${offset1} is byte offset of start of obj1, left-padded-with-zero until you get exactly 10 digits
+ * ${offset2} 00000 n\r\n // ${offset2} is byte offset of start of obj2, left-padded-with-zero until you get exactly 10 digits
+ * ${offset3} 00000 n\r\n // ${offset3} is byte offset of start of obj3, left-padded-with-zero until you get exactly 10 digits
+ * ${offset4} 00000 n\r\n // ${offset4} is byte offset of start of obj4, left-padded-with-zero until you get exactly 10 digits
+ * ${offset5} 00000 n\r\n // ${offset5} is byte offset of start of obj5, left-padded-with-zero until you get exactly 10 digits
+ * trailer\n
+ * <<\n
+ * /Size 6\n // 6 is because it's the number of objects plus 1
+ * /Root 5 0 R\n // 5 is because it's the Catalog Object's object ID
+ * >>\n
+ * startxref\n
+ * ${xref}\n // $xref is the byte offset of the start of this entire "xref" paragraph
+ * %%EOF\n
+ */
+
+ /** Helper method that writes the given String to the output file, then return the number of bytes written. */
+ private static int out(RandomAccessFile file, String string) throws IOException {
+ byte[] array = string.getBytes("UTF-8");
+ file.write(array);
+ return array.length;
+ }
+
+ /** Close and save this PDF object. */
+ public void close() throws IOException {
+ if (buf == null) return; // already closed
+ final boolean compressOrNot = true;
+ RandomAccessFile out = null;
+ try {
+ String space = " "; // reserve 20 bytes for the file size, which is far far more than enough
+ final long fontID = 1, contentID = 2, pageID = 3, pagesID = 4, catalogID = 5, offset[] = new long[6];
+ // Write %PDF-1.3, followed by a non-ASCII comment to force the PDF into binary mode
+ out = new RandomAccessFile(filename, "rw");
+ out.setLength(0);
+ byte[] head = new byte[]{'%', 'P', 'D', 'F', '-', '1', '.', '3', 10, '%', -127, 10, 10};
+ out.write(head);
+ long now = head.length;
+ // Font
+ offset[1] = now;
+ now += out(out, fontID + " 0 obj << /Type /Font /Subtype /Type1 /BaseFont"
+ + " /Helvetica /Encoding /WinAnsiEncoding >> endobj\n\n");
+ // Content
+ offset[2] = now;
+ now += out(out, contentID + " 0 obj << /Length " + space
+ + (compressOrNot ? " /Filter /FlateDecode" : "") + " >> stream\r\n");
+ buf.write("Q\n");
+ final long ct = compressOrNot ? buf.dumpFlate(out) : buf.dump(out);
+ now += ct + out(out, "endstream endobj\n\n");
+ // Page
+ offset[3] = now;
+ now += out(out, pageID + " 0 obj << /Type /Page /Parent " + pagesID + " 0 R /Contents " + contentID + " 0 R >> endobj\n\n");
+ // Pages
+ offset[4] = now;
+ now += out(out, pagesID + " 0 obj << /Type /Pages /Count 1 /Kids [" + pageID + " 0 R] /MediaBox [0 0 "
+ + width + " " + height + "] /Resources << /Font << /F1 " + fontID + " 0 R >> >> >> endobj\n\n");
+ // Catalog
+ offset[5] = now;
+ now += out(out, catalogID + " 0 obj << /Type /Catalog /Pages " + pagesID + " 0 R >> endobj\n\n");
+ // Xref
+ String xr = "xref\n" + "0 " + offset.length + "\n";
+ for(int i = 0; i < offset.length; i++) {
+ String txt = Long.toString(offset[i]);
+ while(txt.length() < 10) txt = "0" + txt; // must be exactly 10 characters long
+ if (i==0) xr = xr + txt + " 65535 f\r\n"; else xr = xr + txt + " 00000 n\r\n";
+ }
+ // Trailer
+ xr = xr + "trailer\n<<\n/Size " + offset.length + "\n/Root " + catalogID + " 0 R\n>>\n" + "startxref\n" + now + "\n%%EOF\n";
+ out(out, xr);
+ out.seek(offset[2]);
+ out(out, contentID + " 0 obj << /Length " + ct); // move the file pointer back so we can write out the real Content Size
+ out.close();
+ buf = null; // only set buf to null if the file was saved successfully and no exception was thrown
+ } catch(Throwable ex) {
+ Util.close(out);
+ if (ex instanceof IOException) throw (IOException)ex;
+ if (ex instanceof OutOfMemoryError) throw new IOException("Out of memory trying to save the PDF file to " + filename);
+ if (ex instanceof StackOverflowError) throw new IOException("Out of memory trying to save the PDF file to " + filename);
+ throw new IOException("Error writing the PDF file to " + filename + " (" + ex + ")");
+ }
+ }
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurPNGWriter.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurPNGWriter.java
new file mode 100644
index 00000000..c22bad08
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurPNGWriter.java
@@ -0,0 +1,148 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.awt.image.BufferedImage;
+import javax.imageio.ImageIO;
+
+/** Graphical convenience methods for producing PNG files. */
+
+public final strictfp class OurPNGWriter {
+
+ /** The constructor is private, since this utility class never needs to be instantiated. */
+ private OurPNGWriter () { }
+
+ /** Writes the image as a PNG file with the given horizontal and vertical dots-per-inch. */
+ public static void writePNG (BufferedImage image, String filename, double dpiX, double dpiY) throws IOException {
+ try {
+ ImageIO.write(image, "PNG", new File(filename)); // some versions of Java sometimes throws an exception during saving...
+ setDPI(filename, dpiX, dpiY);
+ } catch(Throwable ex) {
+ if (ex instanceof IOException) throw (IOException)ex;
+ if (ex instanceof StackOverflowError) throw new IOException("Out of memory trying to save the PNG file to " + filename);
+ if (ex instanceof OutOfMemoryError) throw new IOException("Out of memory trying to save the PNG file to " + filename);
+ throw new IOException("Error writing the PNG file to " + filename + " (" + ex + ")");
+ }
+ }
+
+ /* PNG consists of a "8 byte header" followed by one or more CHUNK...
+ *
+ * Each CHUNK:
+ * ===========
+ * 4 bytes: an integer N expressed with most-significant-byte first
+ * 4 bytes: Chunk Type
+ * N bytes: Chunk Data
+ * 4 bytes: Checksum (this checksum is computed over the Chunk Type and Chunk Data)
+ *
+ * Each PNG must contain an IDAT chunk (this is the actual pixels of the image)
+ *
+ * Each PNG may contain an optional pHYs chunk that describes the horizontal and vertical dots-per-meter information.
+ * If such a chunk exists, it must come before (though not necessarily immediately before) the IDAT chunk.
+ *
+ * pHYs CHUNK:
+ * ===========
+ * 4 bytes: 0 , 0 , 0 , 9
+ * 4 bytes: 'p' , 'H' , 'Y' , 's'
+ * 4 bytes: horizontal dots per meter (most-significant-byte first)
+ * 4 bytes: vertical dots per meter (most-significant-byte first)
+ * 1 byte: 1
+ * 4 bytes: Checksum
+ */
+
+ /** Modifies the given PNG file to have the given horizontal and vertical dots-per-inch. */
+ private static void setDPI (String filename, double dpiX, double dpiY) throws IOException {
+ RandomAccessFile f = null;
+ try {
+ f = new RandomAccessFile(filename, "rw");
+ for(long total=f.length(), pos=8; pos>>24, x>>>16, x>>>8, x, y>>>24, y>>>16, y>>>8, y, 1});
+ }
+
+ /** Write the given chunk into the given file; Note: data.length must be at least 4. */
+ private static void writeChunk (RandomAccessFile file, int[] data) throws IOException {
+ int crc = (-1), len = data.length - 4;
+ file.write((len>>>24) & 255); file.write((len>>>16) & 255); file.write((len>>>8) & 255); file.write(len & 255);
+ for(int i=0; i>> 8); file.write(x & 255); }
+ crc = crc ^ (-1);
+ file.write((crc>>>24) & 255); file.write((crc>>>16) & 255); file.write((crc>>>8) & 255); file.write(crc & 255);
+ }
+
+ /** This precomputed table makes it faster to calculate CRC; this is based on the suggestion in the PNG specification. */
+ private static final int[] table = new int[] {
+ 0,1996959894,-301047508,-1727442502,124634137,1886057615,-379345611,-1637575261,249268274
+ ,2044508324,-522852066,-1747789432,162941995,2125561021,-407360249,-1866523247,498536548,1789927666
+ ,-205950648,-2067906082,450548861,1843258603,-187386543,-2083289657,325883990,1684777152,-43845254
+ ,-1973040660,335633487,1661365465,-99664541,-1928851979,997073096,1281953886,-715111964,-1570279054
+ ,1006888145,1258607687,-770865667,-1526024853,901097722,1119000684,-608450090,-1396901568,853044451
+ ,1172266101,-589951537,-1412350631,651767980,1373503546,-925412992,-1076862698,565507253,1454621731
+ ,-809855591,-1195530993,671266974,1594198024,-972236366,-1324619484,795835527,1483230225,-1050600021
+ ,-1234817731,1994146192,31158534,-1731059524,-271249366,1907459465,112637215,-1614814043,-390540237
+ ,2013776290,251722036,-1777751922,-519137256,2137656763,141376813,-1855689577,-429695999,1802195444
+ ,476864866,-2056965928,-228458418,1812370925,453092731,-2113342271,-183516073,1706088902,314042704
+ ,-1950435094,-54949764,1658658271,366619977,-1932296973,-69972891,1303535960,984961486,-1547960204
+ ,-725929758,1256170817,1037604311,-1529756563,-740887301,1131014506,879679996,-1385723834,-631195440
+ ,1141124467,855842277,-1442165665,-586318647,1342533948,654459306,-1106571248,-921952122,1466479909
+ ,544179635,-1184443383,-832445281,1591671054,702138776,-1328506846,-942167884,1504918807,783551873
+ ,-1212326853,-1061524307,-306674912,-1698712650,62317068,1957810842,-355121351,-1647151185,81470997
+ ,1943803523,-480048366,-1805370492,225274430,2053790376,-468791541,-1828061283,167816743,2097651377
+ ,-267414716,-2029476910,503444072,1762050814,-144550051,-2140837941,426522225,1852507879,-19653770
+ ,-1982649376,282753626,1742555852,-105259153,-1900089351,397917763,1622183637,-690576408,-1580100738
+ ,953729732,1340076626,-776247311,-1497606297,1068828381,1219638859,-670225446,-1358292148,906185462
+ ,1090812512,-547295293,-1469587627,829329135,1181335161,-882789492,-1134132454,628085408,1382605366
+ ,-871598187,-1156888829,570562233,1426400815,-977650754,-1296233688,733239954,1555261956,-1026031705
+ ,-1244606671,752459403,1541320221,-1687895376,-328994266,1969922972,40735498,-1677130071,-351390145
+ ,1913087877,83908371,-1782625662,-491226604,2075208622,213261112,-1831694693,-438977011,2094854071
+ ,198958881,-2032938284,-237706686,1759359992,534414190,-2118248755,-155638181,1873836001,414664567
+ ,-2012718362,-15766928,1711684554,285281116,-1889165569,-127750551,1634467795,376229701,-1609899400
+ ,-686959890,1308918612,956543938,-1486412191,-799009033,1231636301,1047427035,-1362007478,-640263460
+ ,1088359270,936918000,-1447252397,-558129467,1202900863,817233897,-1111625188,-893730166,1404277552
+ ,615818150,-1160759803,-841546093,1423857449,601450431,-1285129682,-1000256840,1567103746,711928724
+ ,-1274298825,-1022587231,1510334235,755167117
+ };
+}
diff --git a/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurSyntaxDocument.java b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurSyntaxDocument.java
new file mode 100644
index 00000000..f2b7c051
--- /dev/null
+++ b/Source/eu.modelwriter.alloyanalyzer/src/edu/mit/csail/sdg/alloy4/OurSyntaxDocument.java
@@ -0,0 +1,259 @@
+/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package edu.mit.csail.sdg.alloy4;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.TabSet;
+import javax.swing.text.TabStop;
+import static edu.mit.csail.sdg.alloy4.OurConsole.style;
+
+/** Graphical syntax-highlighting StyledDocument.
+ *
+ * Thread Safety: Can be called only by the AWT event thread
+ */
+
+class OurSyntaxDocument extends DefaultStyledDocument {
+
+ /** This ensures the class can be serialized reliably. */
+ private static final long serialVersionUID = 0;
+
+ /** The "comment mode" at the start of each line (0 = no comment) (1 = block comment) (2 = javadoc comment) (-1 = unknown) */
+ private final List comments = new ArrayList();
+
+ /** Whether syntax highlighting is currently enabled or not. */
+ private boolean enabled = true;
+
+ /** The current font name is. */
+ private String font = "Monospaced";
+
+ /** The current font size. */
+ private int fontSize = 14;
+
+ /** The current tab size. */
+ private int tabSize = 4;
+
+ /** The list of font+color styles (eg. regular text, symbols, keywords, comments, etc). */
+ private final List all = new ArrayList();
+
+ /** The character style for regular text. */
+ private final MutableAttributeSet styleNormal = style(font, fontSize, false, Color.BLACK, 0); { all.add(styleNormal); }
+
+ /** The character style for symbols. */
+ private final MutableAttributeSet styleSymbol = style(font, fontSize, true, Color.BLACK, 0); { all.add(styleSymbol); }
+
+ /** The character style for integer constants. */
+ private final MutableAttributeSet styleNumber = style(font, fontSize, true, new Color(0xA80A0A), 0); { all.add(styleNumber); }
+
+ /** The character style for keywords. */
+ private final MutableAttributeSet styleKeyword = style(font, fontSize, true, new Color(0x1E1EA8), 0); { all.add(styleKeyword); }
+
+ /** The character style for string literals. */
+ private final MutableAttributeSet styleString = style(font, fontSize, false, new Color(0xA80AA8), 0); { all.add(styleString); }
+
+ /** The character style for up-to-end-of-line-style comment. */
+ private final MutableAttributeSet styleComment = style(font, fontSize, false, new Color(0x0A940A), 0); { all.add(styleComment); }
+
+ /** The character style for non-javadoc-style block comment. */
+ private final MutableAttributeSet styleBlock = style(font, fontSize, false, new Color(0x0A940A), 0); { all.add(styleBlock); }
+
+ /** The character style for javadoc-style block comment. */
+ private final MutableAttributeSet styleJavadoc = style(font, fontSize, true, new Color(0x0A940A), 0); { all.add(styleJavadoc); }
+
+ /** The paragraph style for indentation. */
+ private final MutableAttributeSet tabset = new SimpleAttributeSet();
+
+ /** This stores the currently recognized set of reserved keywords. */
+ private static final String[] keywords = new String[] {"abstract", "all", "and", "as", "assert", "but", "check", "disj",
+ "disjoint", "else", "enum", "exactly", "exh", "exhaustive", "expect", "extends", "fact", "for", "fun", "iden",
+ "iff", "implies", "in", "Int", "int", "let", "lone", "module", "no", "none", "not", "one", "open", "or", "part",
+ "partition", "pred", "private", "run", "seq", "set", "sig", "some", "String", "sum", "this", "univ"
+ };
+
+ /** Returns true if array[start .. start+len-1] matches one of the reserved keyword. */
+ private static final boolean do_keyword(String array, int start, int len) {
+ if (len >= 2 && len <= 10) for(int i = keywords.length - 1; i >= 0; i--) {
+ String str = keywords[i];
+ if (str.length()==len) for(int j=0; ;j++) if (j==len) return true; else if (str.charAt(j) != array.charAt(start+j)) break;
+ }
+ return false;
+ }
+
+ /** Returns true if "c" can be in the start or middle or end of an identifier. */
+ private static final boolean do_iden(char c) {
+ return (c>='A' && c<='Z') || (c>='a' && c<='z') || c=='$' || (c>='0' && c<='9') || c=='_' || c=='\'' || c=='\"';
+ }
+
+ /** Constructor. */
+ public OurSyntaxDocument(String fontName, int fontSize) {
+ putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n");
+ tabSize++;
+ do_setFont(fontName, fontSize, tabSize - 1); // assigns the given font, and also forces recomputation of the tab size
+ }
+
+ /** Enables or disables syntax highlighting. */
+ public final void do_enableSyntax (boolean flag) {
+ if (enabled == flag) return; else { enabled = flag; comments.clear(); }
+ if (flag) do_reapplyAll(); else setCharacterAttributes(0, getLength(), styleNormal, false);
+ }
+
+ /** Return the number of lines represented by the current text (where partial line counts as a line).
+ * For example: count("")==1, count("x")==1, count("x\n")==2, and count("x\ny")==2
+ */
+ public final int do_getLineCount() {
+ String txt = toString();
+ for(int n=txt.length(), ans=1, i=0; ; i++) if (i>=n) return ans; else if (txt.charAt(i)=='\n') ans++;
+ }
+
+ /** Return the starting offset of the given line (If "line" argument is too large, it will return the last line's starting offset)
+ *
For example: given "ab\ncd\n", start(0)==0, start(1)==3, start(2...)==6. Same thing when given "ab\ncd\ne".
+ */
+ public final int do_getLineStartOffset(int line) {
+ String txt = toString();
+ for(int n=txt.length(), ans=0, i=0, y=0; ; i++) if (i>=n || y>=line) return ans; else if (txt.charAt(i)=='\n') {ans=i+1; y++;}
+ }
+
+ /** Return the line number that the offset is in (If "offset" argument is too large, it will just return do_getLineCount()-1).
+ *
For example: given "ab\ncd\n", offset(0..2)==0, offset(3..5)==1, offset(6..)==2. Same thing when given "ab\ncd\ne".
+ */
+ public final int do_getLineOfOffset(int offset) {
+ String txt = toString();
+ for(int n=txt.length(), ans=0, i=0; ; i++) if (i>=n || i>=offset) return ans; else if (txt.charAt(i)=='\n') ans++;
+ }
+
+ /** This method is called by Swing to insert a String into this document.
+ * We intentionally ignore "attr" and instead use our own coloring.
+ */
+ @Override public void insertString(int offset, String string, AttributeSet attr) throws BadLocationException {
+ if (string.indexOf('\r')>=0) string = Util.convertLineBreak(string); // we don't want '\r'
+ if (!enabled) { super.insertString(offset, string, styleNormal); return; }
+ int startLine = do_getLineOfOffset(offset);
+ for(int i = 0; i < string.length(); i++) { // For each inserted '\n' we need to shift the values in "comments" array down
+ if (string.charAt(i)=='\n') { if (startLine < comments.size()-1) comments.add(startLine+1, -1); }
+ }
+ super.insertString(offset, string, styleNormal);
+ try { do_update(startLine); } catch(Exception ex) { comments.clear(); }
+ }
+
+ /** This method is called by Swing to delete text from this document. */
+ @Override public void remove(int offset, int length) throws BadLocationException {
+ if (!enabled) { super.remove(offset, length); return; }
+ int i = 0, startLine = do_getLineOfOffset(offset);
+ for(String oldText = toString(); i 0) this.remove(offset, length);
+ if (string != null && string.length() > 0) this.insertString(offset, string, styleNormal);
+ }
+
+ /** Reapply styles assuming the given line has just been modified */
+ private final void do_update(int line) throws BadLocationException {
+ String content = toString();
+ int lineCount = do_getLineCount();
+ while(line>0 && (line>=comments.size() || comments.get(line)<0)) line--; // "-1" in comments array are always contiguous
+ int comment = do_reapply(line==0 ? 0 : comments.get(line), content, line);
+ for (line++; line < lineCount; line++) { // update each subsequent line until it already starts with its expected comment mode
+ if (line < comments.size() && comments.get(line) == comment) break; else comment = do_reapply(comment, content, line);
+ }
+ }
+
+ /** Re-color the given line assuming it starts with a given comment mode, then return the comment mode for start of next line. */
+ private final int do_reapply(int comment, final String txt, final int line) {
+ while (line >= comments.size()) comments.add(-1); // enlarge array if needed
+ comments.set(line, comment); // record the fact that this line starts with the given comment mode
+ for(int n = txt.length(), i = do_getLineStartOffset(line); i < n;) {
+ final int oldi = i;
+ final char c = txt.charAt(i);
+ if (c=='\n') break;
+ if (comment==0 && c=='/' && i0) {
+ AttributeSet style = (comment==1 ? styleBlock : styleJavadoc);
+ while(i='0' && c<='9') ? styleNumber : (do_keyword(txt, oldi, i-oldi) ? styleKeyword : styleNormal);
+ setCharacterAttributes(oldi, i-oldi, style, false);
+ } else {
+ for(i++; i