/* 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 java.io.IOException; import java.io.RandomAccessFile; import edu.mit.csail.sdg.alloy4.Util; import kodkod.engine.satlab.SATFactory; import kodkod.engine.satlab.SATSolver; /** An implementation of SATSolver that dumps the CNF to a file and then throws an exception * (this code is adapted from ExternalSolver from Kodkod). */ final class WriteCNF implements SATSolver { /** This runtime exception is thrown when the CNF file has been written successfully. */ public static final class WriteCNFCompleted extends RuntimeException { /** This ensures the class can be serialized reliably. */ private static final long serialVersionUID = 0; /** This constructs a new WriteCNFCompleted exception. */ public WriteCNFCompleted() { super("CNF written successfully."); } } /** This is the CNF file we are generating. */ private final RandomAccessFile cnf; /** This buffers up the clauses we are writing to the CNF file, to avoid excessive I/O. */ private final StringBuilder buffer; /** This is the buffer size. */ private static final int capacity = 8192; /** The number of variables so far. */ private int vars = 0; /** The number of clauses so far. */ private int clauses = 0; /** Helper method that returns a factory for WriteCNF instances. */ public static final SATFactory factory(final String filename) { return new SATFactory() { /** {@inheritDoc} */ @Override public SATSolver instance() { return new WriteCNF(filename); } /** {@inheritDoc} */ @Override public boolean incremental() { return false; } }; } /** Constructs a WriteCNF solver that will write CNF into the given file, without solving it. */ private WriteCNF(String filename) { try { this.cnf = new RandomAccessFile(filename, "rw"); this.cnf.setLength(0); this.buffer = new StringBuilder(capacity); for(int i = String.valueOf(Integer.MAX_VALUE).length()*2 + 8; i > 0; i--) { // get enough space into the buffer for the cnf header, which will be written last buffer.append(' '); } buffer.append('\n'); } catch (Exception ex) { throw new RuntimeException("WriteCNF failed.", ex); } } /** Helper method that flushes the buffer. */ private void flush() { try { cnf.writeBytes(buffer.toString()); buffer.setLength(0); } catch (IOException ex) { throw new RuntimeException("WriteCNF failed.", ex); } } /** {@inheritDoc} */ @Override protected void finalize() throws Throwable { super.finalize(); free(); } /** {@inheritDoc} */ public void free() { Util.close(cnf); } /** {@inheritDoc} */ public void addVariables(int numVars) { if (numVars >= 0) vars += numVars; } /** {@inheritDoc} */ public boolean addClause(int[] lits) { if (lits.length>0) { clauses++; if (buffer.length() > capacity) flush(); for(int i=0; i<lits.length; i++) buffer.append(lits[i]).append(' '); buffer.append("0\n"); return true; } return false; } /** {@inheritDoc} */ public int numberOfVariables() { return vars; } /** {@inheritDoc} */ public int numberOfClauses() { return clauses; } /** {@inheritDoc} */ public boolean solve() { try { flush(); cnf.seek(0); cnf.writeBytes("p cnf " + vars + " " + clauses); cnf.close(); } catch (Exception ex) { throw new RuntimeException("WriteCNF failed.", ex); } throw new WriteCNFCompleted(); } /** {@inheritDoc} */ public boolean valueOf(int variable) { throw new IllegalStateException("This solver just writes the CNF without solving them."); } }