/** * */ package kodkod.examples.alloy; import java.util.ArrayList; import java.util.List; import kodkod.ast.Expression; import kodkod.ast.Formula; import kodkod.ast.Relation; import kodkod.ast.Variable; import kodkod.engine.Solution; import kodkod.engine.Solver; import kodkod.engine.fol2sat.HigherOrderDeclException; import kodkod.engine.fol2sat.UnboundLeafException; import kodkod.engine.satlab.SATFactory; import kodkod.instance.Bounds; import kodkod.instance.TupleFactory; import kodkod.instance.Universe; /** * A KK encoding of file_system.als * @author Emina Torlak */ public final class FileSystem { private final Relation Obj, Name, File, Dir, Root, Cur, DirEntry; private final Relation entries, parent, name, contents; /** * Constructs a new instance of the file system problem. */ public FileSystem() { Obj = Relation.unary("Object"); Name = Relation.unary("Name"); File = Relation.unary("File"); Dir = Relation.unary("Dir"); Root = Relation.unary("Root"); Cur = Relation.unary("Cur"); DirEntry = Relation.unary("DirEntry"); entries = Relation.binary("entries"); parent = Relation.binary("parent"); name = Relation.binary("name"); contents = Relation.binary("contents"); } /** * Returns the declaration constraints. * @return declaration constraints */ public final Formula decls() { // File and Dir partition object final Formula f0 = Obj.eq(File.union(Dir)).and(File.intersection(Dir).no()); // Root and Cur are in Dir and do not intersect final Formula f1 = Root.in(Dir).and(Cur.in(Dir)).and(Root.intersection(Cur).no()); // don't need to specify that Dir, Name, and DirEntry are disjoint; implied by bounds final Formula f2 = entries.in(Dir.product(DirEntry)); final Formula f3 = parent.partialFunction(Dir, Dir); final Formula f4 = name.function(DirEntry, Name); final Formula f5 = contents.function(DirEntry, Obj); return f0.and(f1).and(f2).and(f3).and(f4).and(f5); } /** * Returns all facts in the model. * @return the facts. */ public final Formula facts() { // sig File extends Object {} { some d: Dir | this in d.entries.contents } final Variable file = Variable.unary("this"); final Variable d = Variable.unary("d"); final Formula f0 = file.in(d.join(entries).join(contents)).forSome(d.oneOf(Dir)).forAll(file.oneOf(File)); // 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 // } final Variable dir = Variable.unary("this"); final Variable e1 = Variable.unary("e1"), e2 = Variable.unary("e2"); final Formula f1 = (dir.join(parent)).eq(dir.join(contents.transpose()).join(entries.transpose())); final Expression e0 = dir.join(entries); final Formula f2 = e1.join(name).eq(e2.join(name)).implies(e1.eq(e2)).forAll(e1.oneOf(e0).and(e2.oneOf(e0))); final Formula f3 = dir.in(dir.join(parent.closure())).not(); final Formula f4 = dir.eq(Root).not().implies(Root.in(dir.join(parent.closure()))); final Formula f5 = f1.and(f2).and(f3).and(f4).forAll(dir.oneOf(Dir)); // one sig Root extends Dir {} { no parent } final Formula f6 = Root.join(parent).no(); // sig DirEntry { // name: Name, // contents: Object // } { one this.~entries } final Variable entry = Variable.unary("this"); final Formula f7 = entry.join(entries.transpose()).one().forAll(entry.oneOf(DirEntry)); // fact OneParent { // // all directories besides root xhave one parent // all d: Dir - Root | one d.parent // } final Formula f8 = d.join(parent).one().forAll(d.oneOf(Dir.difference(Root))); return f0.and(f5).and(f6).and(f7).and(f8); } /** * Returns the no aliases assertion. * @return the no aliases assertion. */ public final Formula noDirAliases() { //all o: Dir | lone o.~contents final Variable o = Variable.unary("o"); return o.join(contents.transpose()).lone().forAll(o.oneOf(Dir)); } /** * Returns the formula that 'checks' the noDirAliases assertion. * @return decls() and facts() and noDirAliases().not() */ public final Formula checkNoDirAliases() { return decls().and(facts()).and(noDirAliases().not()); } /** * Returns the bounds for the given scope. * @return the bounds for the given scope. */ public final Bounds bounds(int scope) { assert scope > 0; final int n = scope*3; final List<String> atoms = new ArrayList<String>(n); for(int i = 0; i < scope; i++) atoms.add("Object"+i); for(int i = 0; i < scope; i++) atoms.add("Name"+i); for(int i = 0; i < scope; i++) atoms.add("DirEntry"+i); final Universe u = new Universe(atoms); final TupleFactory f = u.factory(); final Bounds b = new Bounds(u); final int max = scope-1; b.bound(Obj, f.range(f.tuple("Object0"), f.tuple("Object"+max))); b.boundExactly(Root, f.setOf("Object0")); b.bound(Cur, b.upperBound(Obj)); b.bound(File, b.upperBound(Obj)); b.bound(Dir, b.upperBound(Obj)); b.bound(Name, f.range(f.tuple("Name0"), f.tuple("Name"+max))); b.bound(DirEntry, f.range(f.tuple("DirEntry0"), f.tuple("DirEntry"+max))); b.bound(entries, b.upperBound(Dir).product(b.upperBound(DirEntry))); b.bound(parent, b.upperBound(Dir).product(b.upperBound(Dir))); b.bound(name, b.upperBound(DirEntry).product(b.upperBound(Name))); b.bound(contents, b.upperBound(DirEntry).product(b.upperBound(Obj))); return b; } private static void usage() { System.out.println("java examples.FileSystem [scope]"); System.exit(1); } /** * Usage: java examples.alloy.FileSystem [scope] */ public static void main(String[] args) { if (args.length < 1) usage(); try { final int n = Integer.parseInt(args[0]); final FileSystem model = new FileSystem(); final Formula f = model.checkNoDirAliases(); System.out.println(f); final Bounds b = model.bounds(n); final Solver solver = new Solver(); solver.options().setSolver(SATFactory.MiniSat); final Solution s = solver.solve(f, b); System.out.println(s); } catch (NumberFormatException nfe) { usage(); } catch (HigherOrderDeclException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (UnboundLeafException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }