/* * Copyright (C) 2009-2012 University of Freiburg * * This file is part of SMTInterpol. * * SMTInterpol is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SMTInterpol is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>. */ package de.uni_freiburg.informatik.ultimate.smtinterpol.aiger; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import de.uni_freiburg.informatik.ultimate.logic.FormulaUnLet; import de.uni_freiburg.informatik.ultimate.logic.FormulaUnLet.UnletType; import de.uni_freiburg.informatik.ultimate.logic.Logics; import de.uni_freiburg.informatik.ultimate.logic.Model; import de.uni_freiburg.informatik.ultimate.logic.Script; import de.uni_freiburg.informatik.ultimate.logic.Script.LBool; import de.uni_freiburg.informatik.ultimate.logic.Sort; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.TermVariable; import de.uni_freiburg.informatik.ultimate.smtinterpol.IParser; import de.uni_freiburg.informatik.ultimate.smtinterpol.option.OptionMap; public class AIGERFrontEnd implements IParser { private static final int BUFFER_SIZE = 4096; private final static boolean USE_DEFINITIONS = true; /** * Token type "magic number". */ private static final int TT_MAGIC = 0; /** * Token type "one space". */ private static final int TT_SPACE = 1; /** * Token type "non-negative number". */ private static final int TT_NUMBER = 2; /** * Token type "newline". */ private static final int TT_NEWLINE = 3; /** * Token type "EOF". */ private static final int TT_EOF = 4; /** * Token type "symbol type specifier". */ private static final int TT_STS = 5; /** * Token type "string". */ private static final int TT_STRING = 6; /** * Token type "comment start". */ private static final int TT_COMMENT = 7; /** * Token type "binary number". */ private static final int TT_BNUMBER = 8; private int mLine; private int mCol; private String mFilename; private InputStream mInputStream; private Script mSolver; // The header data private BigInteger mNumAnds; private String[] mInputs; // Parsing data private byte[] mBuffer = new byte[BUFFER_SIZE]; private int mBufpos = 0; private int mBufsize = -1; public AIGERFrontEnd() { mLine = 1; mCol = 0; } private void reportError(String msg) { System.err.print(mFilename); System.err.print(':'); System.err.print(mLine); System.err.print(':'); System.err.print(mCol); System.err.print(':'); System.err.println(msg); System.exit(2); } private final int nextChar() throws IOException { if (mBufpos >= mBufsize) { mBufsize = mInputStream.read(mBuffer); if (mBufsize == -1) { return -1; // EOF } mBufpos = 0; } ++mCol; return mBuffer[mBufpos++] & 0xff;// NOCHECKSTYLE } private final void ungetLastChar() { assert(mBufpos > 0); --mCol; --mBufpos; } private Object nextToken(int expected) { try { int ch = nextChar(); String result; switch (expected) { case TT_MAGIC: // magic: only "aig" supported if (ch != 'a') { return null; } ch = nextChar(); if (ch != 'i') { return null; } ch = nextChar(); if (ch != 'g') { return null; } return "aig"; case TT_SPACE: if (ch == ' ') { return " "; } else { ungetLastChar(); return null; } case TT_NUMBER: { final StringBuilder buffer = new StringBuilder(); while (ch != -1 && Character.isDigit((char) ch)) { buffer.append((char) ch); ch = nextChar(); } ungetLastChar(); if (buffer.length() == 0) { return null; } result = buffer.toString(); return result; } case TT_EOF: if (ch == -1) { return "";// Result has to be non-null but isn't interesting } ungetLastChar(); return null; case TT_NEWLINE: if (ch == '\n') { ++mLine; mCol = 0; return "\n"; } ungetLastChar(); return null; case TT_STS: if (ch == 'i' || ch == 'l' || ch == 'o') { return Character.toString((char) ch); } ungetLastChar(); return null; case TT_STRING: { final StringBuilder buffer = new StringBuilder(); // Strings expand until the next newline // They may contain any ascii symbol while (ch != -1 && ch != '\n') { buffer.append((char) ch); ch = nextChar(); } ungetLastChar(); result = buffer.toString(); return result; } case TT_COMMENT: if (ch == 'c') { return "c"; } ungetLastChar(); return null; case TT_BNUMBER: { BigInteger x = BigInteger.ZERO; int i = 0; while ((ch & 0x80) != 0) { // NOCHECKSTYLE BigInteger tmp = BigInteger.valueOf(ch & 0x7f);// NOCHECKSTYLE assert (7*i >= 0);// NOCHECKSTYLE tmp = tmp.shiftLeft(7 * i++);// NOCHECKSTYLE x = x.or(tmp); ch = nextChar(); if (ch == -1) { System.err.println("File corrupted"); System.exit(5);// NOCHECKSTYLE } } BigInteger tmp = BigInteger.valueOf(ch & 0x7f);// NOCHECKSTYLE assert (7*i >= 0);// NOCHECKSTYLE tmp = tmp.shiftLeft(7 * i);// NOCHECKSTYLE x = x.or(tmp); return x; } default: ungetLastChar(); return null; } } catch (final IOException eioe) { System.err.println(eioe.getMessage()); System.exit(1); // Unreachable, but Java does not detect this... return null; } } private final void getOneSpace() { if (nextToken(TT_SPACE) == null) { reportError("Expected one space"); } } private final void getNewline() { if (nextToken(TT_NEWLINE) == null) { reportError("Expected newline"); } } private final String getNumber() { final String res = (String) nextToken(TT_NUMBER); if (res == null) { reportError("Expected a number"); } return res; } /** * Parses the header of an AIGER file. This file has to be in binary * format. */ private void parseHeader() { if (nextToken(TT_MAGIC) == null) { reportError("Expected magic (\"aig\")"); } getOneSpace(); final BigInteger maxVarNum = new BigInteger(getNumber()); getOneSpace(); mInputs = new String[Integer.parseInt(getNumber())]; getOneSpace(); if (!getNumber().equals("0")) { System.err.println("No latches allowed for SAT checking"); System.exit(3);// NOCHECKSTYLE } getOneSpace(); if (!getNumber().equals("1")) { System.err.println("Only one output allowed for SAT checking"); System.exit(3);// NOCHECKSTYLE } getOneSpace(); mNumAnds = new BigInteger(getNumber()); getNewline(); // Do a consistency check... if (!maxVarNum.equals( mNumAnds.add(BigInteger.valueOf(mInputs.length)))) { System.err.println("File header corrupted!"); System.exit(5);// NOCHECKSTYLE } } private void parseSymbolTable() { String sts; while ((sts = (String) nextToken(TT_STS)) != null) { final String num = getNumber(); getOneSpace(); final String name = (String) nextToken(TT_STRING); if (name == null) { reportError("Expected a string"); System.exit(2); } getNewline(); if (sts.equals("i")) { final int idx = Integer.parseInt(num); mInputs[idx] = name; } // I ignore a possible name for the output } } private void parseCommentSection() { if (nextToken(TT_COMMENT) != null) { while (true) { getNewline(); if (nextToken(TT_STRING) == null) { break; // Token is EOF; } } } } private Term toTerm(BigInteger lit) { if (lit.equals(BigInteger.ZERO)) { return mSolver.term("false"); } if (lit.equals(BigInteger.ONE)) { return mSolver.term("true"); } Term res = mSolver.term(lit.shiftRight(1).toString()); if (lit.testBit(0)) { res = mSolver.term("not", res); } return res; } private void parse() { parseHeader(); final Sort bool = mSolver.sort("Bool"); final Sort[] empty = new Sort[0]; for (int i = 0; i < mInputs.length; ++i) { mSolver.declareFun(Integer.toString(i + 1), empty, bool); } // No latches... final BigInteger output = new BigInteger(getNumber()); getNewline(); final TermVariable[] emptyVars = new TermVariable[0]; BigInteger start = BigInteger.valueOf(mInputs.length); final BigInteger end = start.add(mNumAnds); for (start = start.add(BigInteger.ONE); start.compareTo(end) <= 0; start = start.add(BigInteger.ONE)) { final BigInteger delta0 = (BigInteger) nextToken(TT_BNUMBER); final BigInteger delta1 = (BigInteger) nextToken(TT_BNUMBER); final BigInteger rhs0 = start.shiftLeft(1).subtract(delta0); assert(start.shiftLeft(1).compareTo(rhs0) > 0); final Term[] args = new Term[2]; args[0] = toTerm(rhs0); final BigInteger rhs1 = rhs0.subtract(delta1); assert (rhs0.compareTo(rhs1) >= 0); args[1] = toTerm(rhs1); if (USE_DEFINITIONS) { mSolver.defineFun(start.toString(), emptyVars, bool, mSolver.term("and", args)); } else { final String name = start.toString(); mSolver.declareFun(name, empty, bool); mSolver.assertTerm(mSolver.term("=", mSolver.term(name), mSolver.term("and", args))); } } parseSymbolTable(); parseCommentSection(); // Make some space for solving mBuffer = null; System.err.println("Finished parsing"); final Term formula = toTerm(output); if (USE_DEFINITIONS) { final FormulaUnLet unlet = new FormulaUnLet(UnletType.EXPAND_DEFINITIONS); mSolver.assertTerm(unlet.unlet(formula)); } else { mSolver.assertTerm(formula); } System.err.println("Asserted formula"); } @Override public int run(Script solver, String filename, OptionMap options) { mSolver = solver; if (filename == null) { filename = "<stdin>"; mInputStream = System.in; } else { try { mInputStream = new FileInputStream(filename); } catch (final FileNotFoundException efnfe) { System.err.println(efnfe.getMessage()); return 4;// NOCHECKSTYLE } } mFilename = filename; mSolver.setOption(":produce-models", Boolean.TRUE); mSolver.setLogic(Logics.CORE); parse(); final LBool isSat = mSolver.checkSat(); System.out.println(isSat); if (isSat == LBool.SAT) { System.out.println("Stimuli:"); final Model m = mSolver.getModel(); final Term trueTerm = mSolver.term("true"); for (int i = 0; i < mInputs.length; ++i) { final Term var = mSolver.term(Integer.toString(i)); if (m.evaluate(var) != trueTerm) { System.out.print("not "); } // Variable 0 is reserved for false... System.out.println(mInputs[i] == null ? (i + 1) : mInputs[i]); } } return 0; } }