package org.reprap.utilities; import java.util.ArrayList; import java.util.List; import java.util.Comparator; import java.util.Collections; /** /** * @author adrian * * This is a program to automatically generate the Java for dealing with * the simplification of CSG expressions. That is to say that it generates * simplified expressions when two operands in a more complicated expression * are equal, or are complements. * */ /** * Boolean operators and similar * */ enum Bop { ZERO("zero"), ONE("one"), LEAF("leaf"), NOT("not"), LEFT("left"), RIGHT("right"), AND("and"), OR("or"), XOR("xor"); private String name; Bop(String name) { this.name = name; } public String toString() { return name; } /** * All above NOT are diadic; all including and below monadic * @return */ public boolean diadic() { return compareTo(NOT) > 0; } } /** * A single boolean variable with a name * @author ensab * */ class Variable implements Comparator<Variable> { boolean bv; boolean init; String n; public Variable(String s) { init = false; n = s;} public boolean value() { if(!init) Debug.e("Variable undefined!"); return bv; } public boolean isSet() { return init; } public void set(boolean b) { bv = b; init = true;} public String name() { return n; } public void clean() { init = false; } public Variable(Variable v) { if(!v.init) Debug.e("Variable(Variable v): input Variable undefined!"); bv = v.bv; init = v.init; n = new String(v.n); } public static boolean same(Variable a, Variable b) { return(a.compare(a, b) == 0); } /** * Compare means compare the lexical order of the names. */ public final int compare(Variable a, Variable b) { return(a.n.compareTo(b.n)); } } /** * @author adrian * */ class BooleanExpression { /** * */ private BooleanExpression c1, c2; /** * */ private Bop leafOp; /** * */ private Variable leaf; /** * */ private Variable[] variables; /** * */ private int leafCount; /** * Make an expression from three or four atomic expressions in an array. * exp decides the expression. * @param variables * @param exp */ private void makeFromSeveral(BooleanExpression[] be, int exp) { BooleanExpression t1; switch(be.length) { // Bits in exp: ba // Expression = v[0] b (v[1] a v[2]) // a, b == 0 -> OR // a, b == 1 -> AND case 3: leafCount = -1; c1 = be[0]; if((exp & 1) == 1) c2 = new BooleanExpression(be[1], be[2], Bop.AND); else c2 = new BooleanExpression(be[1], be[2], Bop.OR); if((exp & 2) == 2) leafOp = Bop.AND; else leafOp = Bop.OR; recordVariables(); break; // Bits in exp: dcba // d == 0 -> Expression = v[0] c (v[1] b (v[2] a v[3])) // d == 1 -> Expression = (v[0] b v[1]) c (v[2] a v[3]) // a, b, c == 0 -> OR // a, b, c == 1 -> AND case 4: leafCount = -1; if((exp & 8) == 8) { if((exp & 1) == 1) c2 = new BooleanExpression(be[2], be[3], Bop.AND); else c2 = new BooleanExpression(be[2], be[3], Bop.OR); if((exp & 2) == 2) c1 = new BooleanExpression(be[0], be[1], Bop.AND); else c1 = new BooleanExpression(be[0], be[1], Bop.OR); } else { c1 = be[0]; if((exp & 1) == 1) t1 = new BooleanExpression(be[2], be[3], Bop.AND); else t1 = new BooleanExpression(be[2], be[3], Bop.OR); if((exp & 2) == 2) c2 = new BooleanExpression(be[1], t1, Bop.AND); else c2 = new BooleanExpression(be[1], t1, Bop.OR); } if((exp & 4) == 4) leafOp = Bop.AND; else leafOp = Bop.OR; recordVariables(); break; default: Debug.e("BooleanExpression(...): variable number not 3 or 4!"); } } /** * Make an expression from three or four atomic expressions in an array. * exp decides the expression. * @param variables * @param exp */ public BooleanExpression(BooleanExpression[] be, int exp) { makeFromSeveral(be, exp); } /** * Make an expression from three or four variables in an array. * exp decides the expression. * @param variables * @param exp */ public BooleanExpression(Variable[] v, int exp) { BooleanExpression[] be = new BooleanExpression[v.length]; for(int i = 0; i < v.length; i++) be[i] = new BooleanExpression(v[i]); makeFromSeveral(be, exp); } /** * Operand and two operators * @param a * @param b * @param op */ public BooleanExpression(BooleanExpression a, BooleanExpression b, Bop op) { leafCount = -1; if(!op.diadic()) Debug.e("BooleanExpression(a, b): leaf operator or NOT!"); leafOp = op; leaf = null; c1 = a; c2 = b; recordVariables(); } /** * Monadic operator * @param a * @param op */ public BooleanExpression(BooleanExpression a, Bop op) { leafCount = -1; if(op != Bop.NOT) Debug.e("BooleanExpression(..., NOT): op not NOT!"); leafOp = op; leaf = null; c1 = a; c2 = null; recordVariables(); } /** * Variable leaf */ public BooleanExpression(Variable v) { leafCount = -1; c1 = null; c2 = null; leafOp = Bop.LEAF; leaf = v; recordVariables(); } /** * @return */ public int leafCount() { if(leafCount < 0) { if(leafOp == Bop.LEAF) // || leafOp == bop.ZERO || leafOp == bop.ONE) { leafCount = 1; } else if(leafOp == Bop.NOT) { leafCount = c1.leafCount(); } else leafCount = c1.leafCount()+c2.leafCount(); } return leafCount; } private void recordVariables() { int vc = leafCount(); variables = new Variable[vc]; int i = 0; int k; if(leafOp == Bop.LEAF) // || leafOp == bop.ZERO || leafOp == bop.ONE) variables[i++] = leaf; else if(leafOp == Bop.NOT) { for(k = 0; k < c1.variables.length; k++) variables[i++] = c1.variables[k]; } else { for(k = 0; k < c1.variables.length; k++) variables[i++] = c1.variables[k]; for(k = 0; k < c2.variables.length; k++) variables[i++] = c2.variables[k]; } } public void setAll(int i) { TableRow.setAll(variables, i); } public Variable [] getVariables() { return variables; } public int getIndex(Variable v) { for(int i = 0; i < variables.length; i++) { if(v == variables[i]) return i; } Debug.e("getIndex(): variable not found!"); return -1; } /** * @param v * @return */ public boolean value() { boolean r; switch(leafOp) { case LEAF: return leaf.value(); case ZERO: return false; case ONE: return true; case NOT: return !c1.value(); case LEFT: return c1.value(); case RIGHT: return c2.value(); case AND: r = c1.value(); return r & c2.value(); // && case OR: r = c1.value(); return r | c2.value(); // || case XOR: r = c1.value(); return r ^ c2.value(); default: Debug.e("generateValue_r: dud operator!"); } return false; } private String toJava_r(String r) { switch(leafOp) { case LEAF: return r + leaf.name(); case ZERO: return r + "RrCSG.nothing()"; case ONE: return r + "RrCSG.universe()"; case NOT: return c1.toJava_r(r) + ".complement()"; case LEFT: return c1.toJava_r(r); case RIGHT: return c2.toJava_r(r); case AND: r += "RrCSG.intersection("; r = c1.toJava_r(r) + ", "; r = c2.toJava_r(r) + ")"; return r; case OR: r += "RrCSG.union("; r = c1.toJava_r(r) + ", "; r = c2.toJava_r(r) + ")"; return r; case XOR: Debug.e("toJava(): got to an XOR..."); break; default: Debug.e("toJava(): dud operator"); } return r; } public String toJava() { String r = "r = "; return toJava_r(r) + ";"; } } /** * A row of variables in a function table, and the table value. * Also contains useful functions for variable arrays. * @author ensab * */ class TableRow implements Comparator<TableRow> { private Variable[] vs; private boolean b; public TableRow() { vs = null; } public TableRow(Variable[] vin, boolean bin) { vs = sort(vin); b = bin; } public int length() { return vs.length; } public boolean value() { return b; } public Variable get(int i) { return vs[i]; } public Variable[] all() { return vs; } public String toString() { String result = ""; for(int i = 0; i < vs.length; i++) result += vs[i].name() + " "; return result; } /** * Set all the variables in a list according to the corresponding * bits in an integer. * @param vs * @param v */ public static void setAll(Variable[] vars, int v) { int k = 1; for(int i = 0; i < vars.length; i++) { if((v & k) == 0) vars[i].set(false); else vars[i].set(true); k *= 2; } } /** * Remove one variable from a list to make a shorter list * @param vars * @param remove * @return */ public static Variable[] eliminateVariable(Variable[] vars, Variable remove) { Variable[] result = new Variable[vars.length - 1]; int k = 0; for(int i = 0; i < vars.length; i++) { if(vars[i] != remove) { result[k] = new Variable(vars[i]); k++; } } return result; } /** * Take a list of variables and return a copy lexically sorted by name * @param v * @return */ private static Variable[] sort(Variable[] vins) { Variable[] result = new Variable[vins.length]; for(int i = 0; i < vins.length; i++) result[i] = new Variable(vins[i]); java.util.Arrays.sort(result, new Variable("")); return result; } /** * Check if two lists of variables have the same variables in the same order * @param a * @param b */ public static boolean sameOrder(Variable [] a, Variable [] b) { if(a.length != b.length) return false; for(int i = 0; i < a.length; i++) { if(!Variable.same(a[i], b[i])) return false; } return true; } /** * Find the binary number represented by the list * @param a * @return */ public int number() { int result = 0; for(int i = length() - 1; i >= 0; i--) { if(get(i).value()) result |= 1; result = result << 1; } return result; } /** * Compare the binary numbers represented by two lists * @param a * @param b */ public final int compare(TableRow a,TableRow b) { int va = a.number(); int vb = b.number(); if(va < vb) return -1; else if(va > vb) return 1; return 0; } } /** * @author adrian * */ class FunctionTable { /** * */ List<TableRow> rows; /** * */ boolean allFalse, allTrue; /** * */ public FunctionTable() { rows = new ArrayList<TableRow>(); allFalse = true; allTrue = true; } /** * Add a new row to the function table * @param v * @param b */ public void addRow(Variable[] v, boolean b) { if(b) allFalse = false; else allTrue = false; TableRow newOne = new TableRow(v, b); // Check that each has the same variables as the first if(rows.size() > 0) { if(!TableRow.sameOrder(newOne.all(), rows.get(0).all())) Debug.e("FunctionTable.addRow() - variable lists different!"); } rows.add(newOne); } public void tableCheck() { // Check we have the right number of entries int vars = rows.get(0).all().length; int leng = 1; for(int j = 0; j < vars; j++) leng *= 2; if(leng != rows.size()) Debug.e("FunctionTable.tableCheck() - incorrect entry count: " + rows.size() + "(should be " + leng + ")"); Collections.sort(rows, new TableRow()); for(int i = 1; i < rows.size(); i++) if(rows.get(i-1).number() == rows.get(i).number()) Debug.e("FunctionTable.tableDone() - identical rows: " + rows.get(i-1).toString() + rows.get(i).toString()); } /** * @param b */ public FunctionTable(BooleanExpression b) { this(); int i; int inputs = b.leafCount(); int entries = 1; for(i = 0; i < inputs; i++) entries *= 2; for(i = 0; i < entries; i++) { b.setAll(i); addRow(b.getVariables(), b.value()); } tableCheck(); } /** * @param b * @param a * @param equal_a */ public FunctionTable(BooleanExpression b, Variable v, Variable equal_v, boolean opposite) { this(); int i; int inputs = b.leafCount() - 1; int entries = 1; for(i = 0; i < inputs; i++) entries *= 2; for(i = 0; i < entries*2; i++) { b.setAll(i); if(opposite ^ (equal_v.value() == v.value())) addRow(TableRow.eliminateVariable(b.getVariables(), equal_v), b.value()); } tableCheck(); } public boolean allOnes() { return allTrue;} public boolean allZeros() { return allFalse;} public int entries() { return rows.size(); } /** * @param a * @param b * @return */ static boolean same(FunctionTable a, FunctionTable b) { if(!TableRow.sameOrder(a.rows.get(0).all(), b.rows.get(0).all())) return false; if(a.entries() != b.entries()) return false; if(a.allFalse && b.allFalse) return true; if(a.allTrue && b.allTrue) return true; for(int i = 0; i < a.entries(); i++) if(a.rows.get(i).value() != b.rows.get(i).value()) return false; return true; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { String result = "\n\t// " + rows.get(0).toString(); for(int i = 0; i < entries(); i++) { TableRow tr = rows.get(i); Variable[] vs = tr.all(); result += "\n\t// "; for(int j = 0; j < vs.length; j++) { if(vs[j].value()) result += "1 "; else result += "0 "; } result += "| "; if(tr.value()) result += "1 "; else result += "0 "; } return result; } } /** * @author adrian * */ public class CodeGenerator { static Variable[] eliminate(Variable[] v, int k) { int len = v.length; Variable[] result = new Variable[len - 1]; int count = 0; for(int i = 0; i < len; i++) if(i != k) result[count++] = v[i]; return result; } static List<BooleanExpression> generateAllPairs(BooleanExpression[] b2) { if(b2.length != 2) Debug.e("generateAllPairs: array not of length 2: " + b2.length); List<BooleanExpression> bel2 = new ArrayList<BooleanExpression>(); Bop[] bopValues = Bop.values(); for(int i = 0; i < bopValues.length; i++) { if(bopValues[i].diadic()) { BooleanExpression be = new BooleanExpression(b2[0], b2[1], bopValues[i]); bel2.add(be); BooleanExpression bf = new BooleanExpression(be, Bop.NOT); bel2.add(bf); BooleanExpression bg = new BooleanExpression(new BooleanExpression(b2[0], Bop.NOT), b2[1], bopValues[i]); bel2.add(bg); BooleanExpression bh = new BooleanExpression(b2[0], new BooleanExpression(b2[1], Bop.NOT), bopValues[i]); bel2.add(bh); BooleanExpression bi = new BooleanExpression(new BooleanExpression(b2[0], Bop.NOT), new BooleanExpression(b2[1], Bop.NOT), bopValues[i]); bel2.add(bi); } } return bel2; } static List<BooleanExpression> generateAllTripples(BooleanExpression[] b3) { BooleanExpression[] b2a = new BooleanExpression[2]; BooleanExpression[] b2b = new BooleanExpression[2]; List<BooleanExpression> bel3 = new ArrayList<BooleanExpression>(); List<BooleanExpression> bel2a, bel2b; int i, j; b2b[0] = b3[0]; b2a[0] = b3[1]; b2a[1] = b3[2]; bel2a = generateAllPairs(b2a); for(i = 0; i < bel2a.size(); i++) { b2b[1] = bel2a.get(i); bel2b = generateAllPairs(b2b); for(j = 0; j < bel2b.size(); j++) bel3.add(bel2b.get(i)); } b2b[0] = b3[1]; b2a[0] = b3[0]; b2a[1] = b3[2]; bel2a = generateAllPairs(b2a); for(i = 0; i < bel2a.size(); i++) { b2b[1] = bel2a.get(i); bel2b = generateAllPairs(b2b); for(j = 0; j < bel2b.size(); j++) bel3.add(bel2b.get(i)); } b2b[0] = b3[2]; b2a[0] = b3[0]; b2a[1] = b3[1]; bel2a = generateAllPairs(b2a); for(i = 0; i < bel2a.size(); i++) { b2b[1] = bel2a.get(i); bel2b = generateAllPairs(b2b); for(j = 0; j < bel2b.size(); j++) bel3.add(bel2b.get(i)); } return bel3; } static BooleanExpression findEqualTwo(FunctionTable f, Variable[] v) { if(v.length != 2) Debug.e("findEqualTwo: array not of length 2: " + v.length); BooleanExpression[] b2 = new BooleanExpression[2]; b2[0] = new BooleanExpression(v[0]); b2[1] = new BooleanExpression(v[1]); List<BooleanExpression> bel = generateAllPairs(b2); BooleanExpression be; FunctionTable g; for(int i = 0; i < bel.size(); i++) { be = bel.get(i); g = new FunctionTable(be); if(FunctionTable.same(f, g)) return be; } return null; } static BooleanExpression findEqualThree(FunctionTable f, Variable[] v) { if(v.length != 3) Debug.e("findEqualThree: array not of length 3: " + v.length); BooleanExpression[] b3 = new BooleanExpression[3]; b3[0] = new BooleanExpression(v[0]); b3[1] = new BooleanExpression(v[1]); b3[2] = new BooleanExpression(v[2]); List<BooleanExpression> bel = generateAllTripples(b3); BooleanExpression be; FunctionTable g; for(int i = 0; i < bel.size(); i++) { be = bel.get(i); g = new FunctionTable(be); if(FunctionTable.same(f, g)) return be; } return null; } // private static void oneCase3(Variable [] variables, int exp, int j, int k, boolean opposite, boolean fts) // { // BooleanExpression a = new BooleanExpression(variables, exp); // //// FunctionTable tt = new FunctionTable(a); //// System.out.println(tt.toString()+"\n\n"); // // FunctionTable f = new FunctionTable(a, variables[j], variables[k], opposite); // // //BooleanExpression g = findEqualTwo(f, variables[j], variables[3-(j+k)]); // BooleanExpression g = findEqualTwo(f, eliminate(variables, k)); // // int caseVal = 0; // if(opposite) // caseVal |= 1; // if(j == 1) // caseVal |= 2; // if(k == 2) // caseVal |= 4; // caseVal |= exp << 3; // // System.out.println("\tcase " + caseVal + ": "); // if(fts) // { // System.out.println("\t// " + a.toJava()); // System.out.print("\t// " + variables[j].name() + " = "); // if(opposite) // System.out.print("!"); // System.out.println(variables[k].name() + " ->"); // System.out.println(f.toString()); // } // // if(g != null || f.allOnes() || f.allZeros()) // { // if(f.allOnes()) // System.out.println("\t\tr = RrCSG.universe();"); // else if(f.allZeros()) // System.out.println("\t\tr = RrCSG.nothing();"); // else // System.out.println("\t\t" + g.toJava()); //// if(fts) //// { //// FunctionTable h = new FunctionTable(g); //// System.out.println(h.toString()); //// } // } else // System.out.println("\t\t// No equivalence." + "\n"); // System.out.println("\t\tbreak;\n"); // } private static void oneCase4(Variable [] variables, int exp, int j, int k, boolean opposite, boolean fts) { BooleanExpression a = new BooleanExpression(variables, exp); // FunctionTable tt = new FunctionTable(a); // System.out.println(tt.toString()+"\n\n"); FunctionTable f = new FunctionTable(a, variables[j], variables[k], opposite); BooleanExpression g = findEqualThree(f, eliminate(variables, k)); int caseVal = 0; if(opposite) caseVal |= 1; switch(j) { case 0: if(k == 2) caseVal |= 2; else if(k == 3) caseVal |= 4; break; case 1: if(k == 2) caseVal |= 6; else if(k == 3) caseVal |= 8; break; case 2: if(k == 3) caseVal |= 10; break; default: } caseVal |= exp << 4; System.out.println("\tcase " + caseVal + ": "); if(fts) { System.out.println("\t// " + a.toJava()); System.out.print("\t// " + variables[j].name() + " = "); if(opposite) System.out.print("!"); System.out.println(variables[k].name() + " ->"); System.out.println(f.toString()); } if(g != null || f.allOnes() || f.allZeros()) { if(f.allOnes()) System.out.println("\t\tr = RrCSG.universe();"); else if(f.allZeros()) System.out.println("\t\tr = RrCSG.nothing();"); else System.out.println("\t\t" + g.toJava()); // if(fts) // { // FunctionTable h = new FunctionTable(g); // System.out.println(h.toString()); // } } else System.out.println("\t\t// No equivalence." + "\n"); System.out.println("\t\tbreak;\n"); } // private static void allCases3(Variable [] variables) // { // for(int exp = 0; exp < 4; exp++) // { // for(int j = 0; j < 2; j++) // for(int k = j+1; k < 3; k++) // { // oneCase3(variables, exp, j, k, false, true); // oneCase3(variables, exp, j, k, true, true); // } // } // } // private static void allCases4(Variable [] variables) { for(int exp = 0; exp < 16; exp++) { for(int j = 0; j < 3; j++) for(int k = j+1; k < 4; k++) { oneCase4(variables, exp, j, k, false, true); oneCase4(variables, exp, j, k, true, true); } } } /** * @param args */ public static void main(String[] args) { Variable [] variables = new Variable[4]; variables[0] = new Variable("a"); variables[1] = new Variable("b"); variables[2] = new Variable("c"); variables[3] = new Variable("d"); //oneCase3(variables, 2, 0, 2, false, true); //allCases3(variables); allCases4(variables); } }