/* 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.alloy4compiler.translator;
import static kodkod.engine.Solution.Outcome.SATISFIABLE;
import static kodkod.engine.Solution.Outcome.TRIVIALLY_SATISFIABLE;
import edu.mit.csail.sdg.alloy4.A4Reporter;
import edu.mit.csail.sdg.alloy4compiler.ast.Sig;
import edu.mit.csail.sdg.alloy4compiler.ast.Sig.Field;
import kodkod.ast.BinaryExpression;
import kodkod.ast.Formula;
import kodkod.ast.Relation;
import kodkod.ast.Expression;
import kodkod.engine.Solver;
import kodkod.engine.Solution;
import kodkod.engine.satlab.SATFactory;
import kodkod.instance.Bounds;
import kodkod.instance.Tuple;
import kodkod.instance.TupleFactory;
import kodkod.instance.TupleSet;
/** Immutable; this class stores the set of solutions from the book, for teaching purpose,
* so that users of the tool will see the same illustration as the book and not get confused by SAT solver nondeterminism.
*/
final class BookExamples {
// It calls the following methods on a bounds-computed A4Solution object:
// getAllReachableSigs(), getFactory(), getBounds(), a2k(), kr2typeCLEAR()
/** Returns the Sig if the list of sig contains a sig with the given label, else returns null. */
private static Sig hasSig (Iterable<Sig> sigs, String label) {
for(Sig s:sigs) if (s.label.equals(label)) return s;
return null;
}
/** If one of the solution is a solution to the given problem, return it, else return null. */
static Solution trial (A4Reporter rep, A4Solution frame, Formula formula, Solver solver, boolean check) {
TupleFactory fac = frame.getFactory();
Solution sol = null;
Iterable<Sig> sigs = frame.getAllReachableSigs();
if (hasSig(sigs, "this/Book")!=null) {
Tuple B0N0A0 = t_tuple(fac, "Book$0", "Name$0", "Addr$0");
Tuple B0N1A0 = t_tuple(fac, "Book$0", "Name$1", "Addr$0");
Tuple B0N2A0 = t_tuple(fac, "Book$0", "Name$2", "Addr$0");
Tuple B0N2A1 = t_tuple(fac, "Book$0", "Name$2", "Addr$1");
Tuple B0N1A1 = t_tuple(fac, "Book$0", "Name$1", "Addr$1");
Tuple B1N0A0 = t_tuple(fac, "Book$1", "Name$0", "Addr$0");
Tuple B1N2A1 = t_tuple(fac, "Book$1", "Name$2", "Addr$1");
Tuple B1N1A1 = t_tuple(fac, "Book$1", "Name$1", "Addr$1");
Tuple B000 = t_tuple(fac, "Book$0", "Target$0", "Target$0");
Tuple B001 = t_tuple(fac, "Book$0", "Target$0", "Target$1");
Tuple B002 = t_tuple(fac, "Book$0", "Target$0", "Target$2");
Tuple B010 = t_tuple(fac, "Book$0", "Target$1", "Target$0");
Tuple B101 = t_tuple(fac, "Book$1", "Target$0", "Target$1");
Tuple B110 = t_tuple(fac, "Book$1", "Target$1", "Target$0");
Tuple B102 = t_tuple(fac, "Book$1", "Target$0", "Target$2");
Tuple B210 = t_tuple(fac, "Book$2", "Target$1", "Target$0");
Tuple B202 = t_tuple(fac, "Book$2", "Target$0", "Target$2");
Tuple B212 = t_tuple(fac, "Book$2", "Target$1", "Target$2");
Tuple B302 = t_tuple(fac, "Book$3", "Target$0", "Target$2");
Tuple B310 = t_tuple(fac, "Book$3", "Target$1", "Target$0");
Tuple B312 = t_tuple(fac, "Book$3", "Target$1", "Target$2");
if (sol==null && B000!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.9",
"Book$0", "", "this/Book", "",
"Target$0", "", "this/Alias", "",
"", "this/Group", "",
"", "this/Addr", "",
B000, "", "this/Book", "addr",
});
if (sol==null && B001!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.10",
"Book$0", "", "this/Book", "",
"", "this/Alias", "",
"Target$0", "", "this/Group", "",
"Target$1", "Target$2", "", "this/Addr", "",
B001, B002, "", "this/Book", "addr",
});
if (sol==null && B001!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.11",
"Book$0", "", "this/Book", "",
"Target$0", "", "this/Alias", "",
"", "this/Group", "",
"Target$1", "Target$2", "", "this/Addr", "",
B001, B002, "", "this/Book", "addr",
});
if (sol==null && B001!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.12",
"Book$0", "", "this/Book", "",
"Target$0", "", "this/Alias", "",
"Target$1", "", "this/Group", "",
"", "this/Addr", "",
B001, "", "this/Book", "addr",
});
if (sol==null && B010!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.13",
"Book$0", "Book$1", "", "this/Book", "",
"", "this/Alias", "",
"Target$0", "Target$1", "", "this/Group", "",
"Target$2", "", "this/Addr", "",
B010, B110, B102, "", "this/Book", "addr",
});
if (sol==null && B312!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.15",
"Book$0", "Book$1", "Book$2", "Book$3", "", "this/Book", "",
"", "this/Alias", "",
"Target$0", "Target$1", "", "this/Group", "",
"Target$2", "", "this/Addr", "",
B102, B210, B202, B212, B302, B312, "", "this/Book", "addr",
});
if (sol==null && B101!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.16",
"Book$0", "Book$1", "Book$2", "Book$3", "", "this/Book", "",
"Target$1", "", "this/Alias", "",
"Target$0", "", "this/Group", "",
"", "this/Addr", "",
B101, "", "this/Book", "addr",
});
if (sol==null && B102!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.17",
"Book$0", "Book$1", "Book$2", "Book$3", "", "this/Book", "",
"Target$0", "", "this/Alias", "",
"Target$1", "", "this/Group", "",
"Target$2", "", "this/Addr", "",
B102, B210, B310, B302, "", "this/Book", "addr",
});
if (sol==null && B0N0A0!=null && check)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.6",
"Book$0", "Book$1", "", "this/Book", "",
"Addr$0", "", "this/Addr", "",
"Name$0", "", "this/Name", "",
B0N0A0, "", "this/Book", "addr",
});
if (sol==null && B1N0A0!=null && !check)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.4",
"Book$0", "Book$1", "", "this/Book", "",
"Addr$0", "", "this/Addr", "",
"Name$0", "", "this/Name", "",
B1N0A0, "", "this/Book", "addr",
});
if (sol==null && B0N2A1!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.5",
"Book$0", "Book$1", "", "this/Book", "",
"Addr$0", "Addr$1", "", "this/Addr", "",
"Name$0", "Name$1", "Name$2", "", "this/Name", "",
B0N2A1, B0N1A1, B1N2A1, B1N1A1, B1N0A0, "", "this/Book", "addr",
});
if (sol==null && B0N0A0!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.1",
"Book$0", "", "this/Book", "",
"Addr$0", "", "this/Addr", "",
"Name$0", "", "this/Name", "",
B0N0A0, "", "this/Book", "addr",
});
if (sol==null && B0N0A0!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.2",
"Book$0", "", "this/Book", "",
"Addr$0", "", "this/Addr", "",
"Name$0", "Name$1", "Name$2", "", "this/Name", "",
B0N0A0, B0N1A0, B0N2A0, "", "this/Book", "addr",
});
if (sol==null && B0N0A0!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 2.3",
"Book$0", "", "this/Book", "",
"Addr$0", "Addr$1", "", "this/Addr", "",
"Name$0", "Name$1", "Name$2", "", "this/Name", "",
B0N0A0, B0N1A0, B0N2A1, "", "this/Book", "addr",
});
if (sol==null && B001!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 5.2",
"Book$0", "Book$1", "", "this/Book", "",
"Target$0", "", "this/Name", "",
"Target$1", "", "this/Addr", "",
B001, B101, "", "this/Book", "addr",
});
if (sol==null && B102!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 5.3",
"Book$0", "Book$1", "", "this/Book", "",
"Target$0", "Target$1", "", "this/Name", "",
"Target$2", "", "this/Addr", "",
B010, B110, B102, "", "this/Book", "addr",
});
}
else if (hasSig(sigs, "this/Woman")!=null) {
Tuple man0_woman0 = t_tuple(fac, "Person$1", "Person$0");
Tuple man1_woman0 = t_tuple(fac, "Person$2", "Person$0");
Tuple man0_woman1 = t_tuple(fac, "Person$1", "Person$3");
Tuple man1_woman1 = t_tuple(fac, "Person$2", "Person$3");
if (sol==null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 4.2",
"Person$1", "", "this/Man", "",
"Person$0", "", "this/Woman", "",
man0_woman0, "", "this/Man", "wife",
man0_woman0, "", "this/Person", "mother",
"", "this/Person", "father",
});
if (sol==null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 4.3",
"Person$1", "Person$2", "", "this/Man", "",
"Person$0", "Person$3", "", "this/Woman", "",
man1_woman0, man0_woman1, "", "this/Man", "wife",
man1_woman1, man0_woman0, "", "this/Person", "mother",
"", "this/Person", "father",
});
}
else if (hasSig(sigs, "this/Process")!=null) {
String p0="Process$0", p1="Process$1", p2="Process$2";
String t0="Time$0", t1="Time$1", t2="Time$2", t3="Time$3";
Tuple s20=t_tuple(fac,p2,p0), s01=t_tuple(fac,p0,p1), s12=t_tuple(fac,p1,p2);
Tuple d000=t_tuple(fac,p0,p0,t0), d110=t_tuple(fac,p1,p1,t0), d220=t_tuple(fac,p2,p2,t0);
Tuple d001=t_tuple(fac,p0,p0,t1), d021=t_tuple(fac,p0,p2,t1), d111=t_tuple(fac,p1,p1,t1);
Tuple d002=t_tuple(fac,p0,p0,t2), d112=t_tuple(fac,p1,p1,t2), d122=t_tuple(fac,p1,p2,t2);
Tuple d003=t_tuple(fac,p0,p0,t3), d113=t_tuple(fac,p1,p1,t3), d223=t_tuple(fac,p2,p2,t3);
if (sol==null && d000!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 6.4",
s20, s01, s12, "", "this/Process", "succ",
d000,d110,d220,d001,d021,d111,d002,d112,d122,d003,d113,d223,"","this/Process","toSend",
t_tuple(fac,p2,t3),"","this/Process","elected",
});
}
else if (hasSig(sigs, "this/Desk")!=null) {
String g0="Guest$0", g1="Guest$1", r="Room$0", k0="Key$0", k1="Key$1";
String t0="Time$0", t1="Time$1", t2="Time$2", t3="Time$3", t4="Time$4", t5="Time$5";
String c0="Card$0", c1="Card$1";
if (sol==null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig E.3",
t_tuple(fac,c0,k0), t_tuple(fac,c1,k1), "", "this/Card", "fst",
t_tuple(fac,c0,k1), t_tuple(fac,c1,k0), "", "this/Card", "snd",
t_tuple(fac,g0,c0,t1),
t_tuple(fac,g0,c0,t2), t_tuple(fac,g1,c1,t2),
t_tuple(fac,g0,c0,t3), t_tuple(fac,g1,c1,t3),
t_tuple(fac,g0,c0,t4), t_tuple(fac,g1,c1,t4),
t_tuple(fac,g0,c0,t5), t_tuple(fac,g1,c1,t5), "", "this/Guest", "cards",
t_tuple(fac,r,k0,t0), t_tuple(fac,r,k0,t1), t_tuple(fac,r,k0,t2),
t_tuple(fac,r,k1,t3), t_tuple(fac,r,k0,t4), t_tuple(fac,r,k1,t5), "", "this/Room", "key",
t_tuple(fac,k1,t1),
t_tuple(fac,k0,t2), t_tuple(fac,k1,t2),
t_tuple(fac,k0,t3), t_tuple(fac,k1,t3),
t_tuple(fac,k0,t4), t_tuple(fac,k1,t4),
t_tuple(fac,k0,t5), t_tuple(fac,k1,t5), "", "this/Desk", "issued",
t_tuple(fac,r,k0,t0), t_tuple(fac,r,k1,t1), t_tuple(fac,r,k0,t2),
t_tuple(fac,r,k0,t3), t_tuple(fac,r,k0,t4), t_tuple(fac,r,k0,t5), "", "this/Desk", "prev"
});
}
else if (hasSig(sigs, "this/FrontDesk")!=null) {
String g0="Guest$0", g1="Guest$1", r="Room$0", k0="Key$0", k1="Key$1", k2="Key$2";
String t0="Time$0", t1="Time$1", t2="Time$2", t3="Time$3", t4="Time$4";
Tuple G0=t_tuple(fac,g0), G1=t_tuple(fac,g1);
Tuple K0=t_tuple(fac,r,k0), K1=t_tuple(fac,r,k1), K2=t_tuple(fac,r,k2);
Tuple K0T0=t_tuple(fac,r,k0,t0), K0T1=t_tuple(fac,r,k0,t1), K0T2=t_tuple(fac,r,k0,t2);
Tuple K0T3=t_tuple(fac,r,k0,t3), K1T4=t_tuple(fac,r,k1,t4);
Tuple F1=t_tuple(fac,r,k0,t0), F2=t_tuple(fac,r,k1,t1), F3=t_tuple(fac,r,k1,t2);
Tuple F4=t_tuple(fac,r,k2,t3), F5=t_tuple(fac,r,k2,t4);
Tuple GK1=t_tuple(fac,g0,k1,t1), GK2=t_tuple(fac,g0,k1,t2), GK3=t_tuple(fac,g0,k1,t3);
Tuple GK4=t_tuple(fac,g1,k2,t3), GK5=t_tuple(fac,g0,k1,t4), GK6=t_tuple(fac,g1,k2,t4);
Tuple O1=t_tuple(fac,r,g0,t1), O2=t_tuple(fac,r,g1,t3), O3=t_tuple(fac,r,g1,t4);
if (sol==null && K0T0!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 6.13",
G0, G1, "", "this/Guest", "",
K0, K1, K2, "", "this/Room", "keys",
K0T0, K0T1, K0T2, K0T3, K1T4, "", "this/Room", "currentKey",
F1, F2, F3, F4, F5, "", "this/FrontDesk", "lastKey",
GK1, GK2, GK3, GK4, GK5, GK6, "", "this/Guest", "keys",
O1, O2, O3, "", "this/FrontDesk", "occupant",
"Event$0", "Event$1", "", "this/Checkin", "",
"Event$2", "", "this/Checkout", "",
"Event$3", "", "this/Entry", "",
t_tuple(fac,"Event$0",t0),
t_tuple(fac,"Event$2",t1),
t_tuple(fac,"Event$1",t2),
t_tuple(fac,"Event$3",t3), "", "this/Event", "pre",
});
if (sol==null && K0T0!=null)
sol=trial(rep, fac, solver, sigs, formula, frame, new Object[]{"Fig 6.6",
G0, G1, "", "this/Guest", "",
K0, K1, K2, "", "this/Room", "keys",
K0T0, K0T1, K0T2, K0T3, K1T4, "", "this/Room", "currentKey",
F1, F2, F3, F4, F5, "", "this/FrontDesk", "lastKey",
GK1, GK2, GK3, GK4, GK5, GK6, "", "this/Guest", "keys",
O1, O2, O3, "", "this/FrontDesk", "occupant",
});
}
return sol;
}
/** This tries a particular solution against the formula. */
private static Solution trial (A4Reporter rep, TupleFactory fac, Solver solver, Iterable<Sig> sigs, Formula f, A4Solution frame, Object[] t) {
try {
frame.kr2typeCLEAR();
Bounds b = null;
TupleSet ts = null;
for(int i=1; i<t.length; i++) {
Object x=t[i];
if (x==null) return null;
if (x instanceof String && ((String)x).length()>0) { // This means it's a unary Tuple containing the given atom
Tuple xx = fac.tuple((String)x);
if (ts==null) ts=fac.noneOf(1);
ts.add(xx);
continue;
}
if (x instanceof Tuple) { // This means it's a Tuple
Tuple xx=(Tuple)x;
if (ts==null) ts=fac.noneOf(xx.arity());
ts.add(xx);
continue;
}
if (x instanceof String) { // The empty string means the sig name follows here
i++;
if (i>=t.length-1 || !(t[i] instanceof String) || !(t[i+1] instanceof String)) return null;
String sigName = (String)(t[i]);
i++;
String fieldName = (String)(t[i]);
Sig first = hasSig(sigs,sigName);
if (first==null) return null;
Expression expr = null;
if (fieldName.length()==0) {
expr=frame.a2k(first);
} else {
for(Field field:first.getFields()) if (field.label.equals(fieldName)) {
expr=frame.a2k(field);
while(expr instanceof BinaryExpression) expr=((BinaryExpression)expr).right();
break;
}
}
if (!(expr instanceof Relation)) return null;
if (b==null) b = frame.getBounds(); // We delay the expansive Bounds.clone() until we really find a possible match
if (ts==null) ts = fac.noneOf(expr.arity());
if (!ts.containsAll(b.lowerBound((Relation)expr))) return null; // Sanity check
if (!b.upperBound((Relation)expr).containsAll(ts)) return null; // Sanity check
b.boundExactly((Relation)expr, ts);
ts=null;
continue;
}
}
SATFactory sat = solver.options().solver();
Solution sol;
try {
solver.options().setSolver(SATFactory.DefaultSAT4J);
sol = solver.solve(f,b);
} finally {
solver.options().setSolver(sat);
}
if (sol==null || (sol.outcome()!=SATISFIABLE && sol.outcome()!=TRIVIALLY_SATISFIABLE)) return null;
if (rep!=null) rep.debug("Comment: "+t[0]+"\n");
return sol;
} catch(Throwable ex) {
return null;
}
}
/** This constructs a Kodkod Tuple from the list of atoms, and returns null if no such Tuple can be constructed. */
private static Tuple t_tuple (TupleFactory factory, Object... atoms) {
try {
if (atoms.length <= 0) return null;
return factory.tuple(atoms);
} catch(Throwable ex) {
return null;
}
}
}