/* * xtc - The eXTensible Compiler * Copyright (C) 2009-2012 New York University * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.lang.cpp; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import xtc.tree.GNode; import xtc.tree.Node; import xtc.tree.Visitor; import org.sat4j.core.VecInt; import org.sat4j.minisat.SolverFactory; /** * Manager for SAT solving and clauses. Converts expressions to CNF * form, manages variable names, and initializes and runs the SAT * solver. After construction, use addClause() to add boolean * expressions as parse trees. Then the clauses can be iterated over, * returning an integer array on each iteration corresponding to one * CNF clause. * * @author Paul Gazzillo * @version $Revision: 1.10 $ */ class Clauses implements Iterable<ArrayList<Integer>> { private ArrayList<String> variables; private Map<String, Integer> varmap; private ArrayList<ArrayList<Integer>> clauses; private boolean isFalse; public Clauses() { this.varmap = new HashMap<String, Integer>(); this.variables = new ArrayList<String>(); this.clauses = new ArrayList<ArrayList<Integer>>(); this.isFalse = false; } /** * The copy constructor. * * @param clauses The clauses object to copy. */ public Clauses(Clauses clausesin) { this.varmap = new HashMap<String, Integer>(clausesin.varmap); this.variables = new ArrayList<String>(clausesin.variables); this.clauses = new ArrayList<ArrayList<Integer>>(clausesin.clauses); this.isFalse = clausesin.isFalse; } public boolean isFalse() { return this.isFalse; } public Map<String, Integer> getVarmap() { return varmap; } /** * Add an int array clause. * * @param int_clause The clause. */ public void addClause(ArrayList<Integer> int_clause) { this.clauses.add(int_clause); } private ArrayList<ArrayList<Integer>> getClauses(Node tree) { ArrayList<ArrayList<Integer>> newClauses = (ArrayList<ArrayList<Integer>>) cnfVisitor.dispatch(tree); this.isFalse = newClauses == ZERO; return newClauses; } private void addClauses(ArrayList<ArrayList<Integer>> newClauses) { if (null != newClauses) { this.clauses.addAll(newClauses); } else { System.err.println("warning: null tree"); } } /** * Add a clause given as a Rats! C expression AST. * * @param tree The expression tree. */ public void addClause(Node tree) { ArrayList<ArrayList<Integer>> newClauses = getClauses(tree); addClauses(newClauses); } /** * Add a clause in CNF style, formatted like "(VARNAME, * -VARNAME2,)(VARNAME3)" * * @param clausestring The clause string. */ public void addClauses(String clausestring) { int i = 0; char c; //read all clauses and add them while (i < clausestring.length()) { c = clausestring.charAt(i); if ('1' == c) { // true clauses contribute nothing i++; continue; } if ('0' == c) { // false clauses make the entire expression false this.isFalse = true; break; } // read each clause ArrayList<Integer> int_clause = new ArrayList<Integer>(); if ('(' != c) { System.err.println("invalid clause string"); break; } // move past the ( i++; c = clausestring.charAt(i); while (')' != c) { boolean isneg = false; if ('-' == c) { isneg = true; // move past the - i++; c = clausestring.charAt(i); } // read each var StringBuilder sb_varname = new StringBuilder(); while (',' != c) { sb_varname.append(c); i++; c = clausestring.charAt(i); } // move past the , i++; c = clausestring.charAt(i); // convert the varname to a varnum // System.err.println(sb_varname.toString()); int varnum = this.getVarNum(sb_varname.toString()); sb_varname = null; if (isneg) { varnum *= -1; } // TODO add varnum to clause vector int_clause.add(varnum); } // System.err.println(int_clause); if (int_clause.size() > 0) { this.addClause(int_clause); } i++; } } public boolean varExists(String var) { return this.varmap.containsKey(var); } public int getVarNum(String var) { if (varExists(var)) { return this.varmap.get(var); } else { this.variables.add(var); this.varmap.put(var, this.variables.size()); return this.variables.size(); } } public String getVarName(int varnum) { int arrayIndex = varnum - 1; if (arrayIndex >= 0 && arrayIndex < this.variables.size()) { return this.variables.get(arrayIndex); } else { return null; } } public int getNumVars() { return this.variables.size(); } public int size() { return this.clauses.size(); } public Iterator<ArrayList<Integer>> iterator() { return this.clauses.iterator(); } final private static ArrayList<ArrayList<Integer>> ONE = new ArrayList<ArrayList<Integer>>(); final private static ArrayList<ArrayList<Integer>> ZERO = new ArrayList<ArrayList<Integer>>(); private Visitor cnfVisitor = new Visitor() { /** Process an integer constant. */ public ArrayList<ArrayList<Integer>> visitIntegerConstant(GNode n) { // xtc.type.Type result = cops.typeInteger(n.getString(0)); // return result.getConstant().bigIntValue().longValue(); String s = n.getString(0); if (s.equals("0")) { return ZERO; } else if (s.equals("1")) { return ONE; } else { return null; } } // /** Process a character constant. */ // public ArrayList<ArrayList<Integer>> visitCharacterConstant(GNode n) { // // xtc.type.Type result = cops.typeCharacter(n.getString(0)); // // return result.getConstant().bigIntValue().longValue(); // return null; // } /** Process primary identifier. */ public ArrayList<ArrayList<Integer>> visitPrimaryIdentifier(GNode n) { // create clause with a single variable, hashing to a variable // number ArrayList<ArrayList<Integer>> a = new ArrayList<ArrayList<Integer>>(); ArrayList<Integer> clause = new ArrayList<Integer>(); clause.add(getVarNum(n.getString(0))); a.add(clause); return a; } /** Process defined expression. */ public Object visitDefinedExpression(GNode n) { return visitPrimaryIdentifier(n.getGeneric(0)); } /** Process a logical negation. */ public ArrayList<ArrayList<Integer>> visitLogicalNegationExpression(GNode n) { // get resulting clauses and do de morgan's on them ArrayList<ArrayList<Integer>> a = (ArrayList<ArrayList<Integer>>) dispatch(n.getGeneric(0)); ArrayList<ArrayList<Integer>> newa = null; if (null == a) return null; if (ONE == a) return ZERO; if (ZERO == a) return ONE; // do de morgan's for (ArrayList<Integer> clause : a) { ArrayList<ArrayList<Integer>> pendinga = new ArrayList<ArrayList<Integer>>(); // first negate the clause ArrayList<Integer> negatedVars = new ArrayList<Integer>(); for (Integer i : clause) { negatedVars.add(-1 * i); } if (null == newa) { pendinga.add(negatedVars); } else { for (ArrayList<Integer> oldClause : newa) { for (Integer negi : negatedVars) { ArrayList<Integer> newClause = new ArrayList<Integer>(); newClause.addAll(oldClause); newClause.add(negi); pendinga.add(newClause); } } } newa = pendinga; } return newa; } /** Process a equality expression. */ public ArrayList<ArrayList<Integer>> visitEqualityExpression(GNode n) { // ArrayList<ArrayList<Integer>> a, b, result; // String op; // boolean mydostring = dostring; // nonboolean = true; // dostring = true; // a = (ArrayList<ArrayList<Integer>>) dispatch(n.getGeneric(0)); // b = (ArrayList<ArrayList<Integer>>) dispatch(n.getGeneric(2)); // op = n.getString(1); // dostring = false; // if (a instanceof Long && b instanceof Long) { // if (op.equals("==")) { // result = ((Long) a).equals((Long) b); // } // else if (op.equals("!=")) { // result = ! ((Long) a).equals((Long) b); // } // else { // result = ""; // } // } // else { // String sa, sb; // if (a instanceof String) { // sa = (String) a; // } // else if (a instanceof Long) { // sa = ((Long) a).toString(); // } // else { // return null; // } // if (b instanceof String) { // sb = (String) b; // } // else if (b instanceof Long) { // sb = ((Long) b).toString(); // } // else { // return null; // } // if (op.equals("==") && sa.equals(sb)) { // result = mydostring ? "1" : B.one(); // } // else { // result = parens(sa) + " " + op + " " + parens(sb); // } // } // return result; // do nothing with equality return null; } /** Process a logical and. */ public ArrayList<ArrayList<Integer>> visitLogicalAndExpression(GNode n) { // append clauses to a single list of clauses ArrayList<ArrayList<Integer>> a = (ArrayList<ArrayList<Integer>>) dispatch(n.getGeneric(0)); ArrayList<ArrayList<Integer>> b = (ArrayList<ArrayList<Integer>>) dispatch(n.getGeneric(1)); ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); if (ONE == a) return b; if (ZERO == a) return ZERO; if (ONE == b) return a; if (ZERO == b) return ZERO; if (null != a) result.addAll(a); if (null != b) result.addAll(b); // return new clause return result; } /** Process a logical or. */ public ArrayList<ArrayList<Integer>> visitLogicalOrExpression(GNode n) { // cartesian product of all clauses from each term of the // operation ArrayList<ArrayList<Integer>> a = (ArrayList<ArrayList<Integer>>) dispatch(n.getGeneric(0)); ArrayList<ArrayList<Integer>> b = (ArrayList<ArrayList<Integer>>) dispatch(n.getGeneric(1)); if (ONE == a) return ONE; if (ZERO == a) return b; if (ONE == b) return ONE; if (ZERO == b) return a; if (null == a) return b; if (null == b) return a; ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); for (ArrayList<Integer> ai : a) { for (ArrayList<Integer> bi : b) { ArrayList<Integer> ab = new ArrayList<Integer>(); ab.addAll(ai); ab.addAll(bi); result.add(ab); } } return result; } }; }