diff --git a/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java index 9431874b3..367aa24e1 100644 --- a/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java +++ b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java @@ -23,7 +23,7 @@ import java.util.List; * @author hneemann */ public class ModelAnalyser { - private static final int MAX_INPUTS_ALLOWED = 12; + private static final int MAX_INPUTS_ALLOWED = 24; private final Model model; private final ArrayList inputs; @@ -109,7 +109,8 @@ public class ModelAnalyser { * @throws NodeException NodeException */ public TruthTable analyse() throws NodeException { - + System.out.print("start to analyse the model"); + long time = System.currentTimeMillis(); BitSetter bitsetter = new BitSetter(inputs.size()) { @Override public void setBit(int row, int bit, boolean value) { @@ -128,7 +129,6 @@ public class ModelAnalyser { tt.addResult(s.getName(), e); } - model.init(); for (int row = 0; row < rows; row++) { bitsetter.fill(row); @@ -137,6 +137,8 @@ public class ModelAnalyser { data.get(i).set(row, outputs.get(i).getValue().getBool()); } } + time = System.currentTimeMillis() - time; + System.out.println(": " + time / 1000.0 + " sec"); return tt; } diff --git a/src/main/java/de/neemann/digital/analyse/expression/ContextFiller.java b/src/main/java/de/neemann/digital/analyse/expression/ContextFiller.java index a2025955c..ec79a4c43 100644 --- a/src/main/java/de/neemann/digital/analyse/expression/ContextFiller.java +++ b/src/main/java/de/neemann/digital/analyse/expression/ContextFiller.java @@ -1,15 +1,13 @@ package de.neemann.digital.analyse.expression; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; +import java.util.*; /** * @author hneemann */ public class ContextFiller extends ContextMap implements Iterable { - private final ArrayList vars; + private final List vars; private final int rowCount; private final BitSetter bitSetter; @@ -29,7 +27,16 @@ public class ContextFiller extends ContextMap implements Iterable { * * @param variables the variables to use */ - public ContextFiller(ArrayList variables) { + public ContextFiller(Variable... variables) { + this(Arrays.asList(variables)); + } + + /** + * Creates a new instance + * + * @param variables the variables to use + */ + public ContextFiller(List variables) { vars = variables; rowCount = 1 << vars.size(); bitSetter = new BitSetter(vars.size()) { @@ -83,7 +90,7 @@ public class ContextFiller extends ContextMap implements Iterable { /** * @return the variables to use */ - public ArrayList getVariables() { + public List getVariables() { return vars; } } diff --git a/src/main/java/de/neemann/digital/analyse/quinemc/IndependentChecker.java b/src/main/java/de/neemann/digital/analyse/quinemc/IndependentChecker.java new file mode 100644 index 000000000..8ba169622 --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/quinemc/IndependentChecker.java @@ -0,0 +1,110 @@ +package de.neemann.digital.analyse.quinemc; + +/** + * Checks if a bool table is independent from a certain variable + * Created by hneemann on 12.03.17. + */ +public final class IndependentChecker { + + private final BoolTable boolTable; + private final int vars; + private final int checkSize; + + /** + * Creates a new BoolTable + * + * @param boolTable the table to investigate + */ + public IndependentChecker(BoolTable boolTable) { + this.boolTable = boolTable; + + int v = 0; + int s = boolTable.size(); + while (s > 1) { + s = s / 2; + v++; + } + vars = v; + checkSize = boolTable.size() / 2; + } + + /** + * @return the number of variables + */ + public int getVars() { + return vars; + } + + /** + * checks if the given bool table is dependent on the given variable + * + * @param varNum the variable to check + * @return true if table is independent + */ + public boolean isIndependentFrom(int varNum) { + int bitMask = 1 << (vars - varNum - 1); + int lowMask = bitMask - 1; + int highMask = ~lowMask; + + for (int n = 0; n < checkSize; n++) { + int i1 = (n & lowMask) | ((n & highMask) << 1); + int i2 = i1 | bitMask; + + ThreeStateValue v1 = boolTable.get(i1); + ThreeStateValue v2 = boolTable.get(i2); + + if (v1.equals(v2)) + continue; + + if (v1.equals(ThreeStateValue.dontCare) || v2.equals(ThreeStateValue.dontCare)) + continue; + + return false; + } + + return true; + } + + /** + * Returns a table with the given variable removed + * + * @param varNum the variable to remove + * @return the reduced BoolTable + */ + public BoolTable removeVar(int varNum) { + if (varNum >= vars || varNum < 0) + throw new RuntimeException("variable does not exist"); + return new BoolTableRemoveVar(boolTable, vars, varNum); + } + + private static final class BoolTableRemoveVar implements BoolTable { + private final BoolTable boolTable; + private final int bitMask; + private final int lowMask; + private final int highMask; + private final int size; + + private BoolTableRemoveVar(BoolTable boolTable, int vars, int varNum) { + this.boolTable = boolTable; + bitMask = 1 << (vars - varNum - 1); + lowMask = bitMask - 1; + highMask = ~lowMask; + size = 1 << (vars - 1); + } + + @Override + public int size() { + return size; + } + + @Override + public ThreeStateValue get(int n) { + int i = (n & lowMask) | ((n & highMask) << 1); + ThreeStateValue v = boolTable.get(i); + if (v.equals(ThreeStateValue.dontCare)) + return boolTable.get(i | bitMask); + else + return v; + } + } +} diff --git a/src/main/java/de/neemann/digital/analyse/quinemc/TableReducer.java b/src/main/java/de/neemann/digital/analyse/quinemc/TableReducer.java new file mode 100644 index 000000000..5cede6cdf --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/quinemc/TableReducer.java @@ -0,0 +1,67 @@ +package de.neemann.digital.analyse.quinemc; + +import de.neemann.digital.analyse.expression.Variable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * If the result does not depend on a certain variable, this variable is removed. + *

+ * Created by hneemann on 12.03.17. + */ +public class TableReducer { + + private List vars; + private BoolTable table; + + /** + * Creates a new instance + * + * @param vars the variable + * @param table the bool table + */ + public TableReducer(List vars, BoolTable table) { + this.vars = new ArrayList<>(); + this.vars.addAll(vars); + this.table = table; + } + + /** + * Returns true if there are reduce variables + * + * @return true is reduction was possible + */ + public boolean canReduce() { + boolean isReduced = false; + Iterator it = vars.iterator(); + int var = 0; + while (it.hasNext()) { + it.next(); + IndependentChecker ic = new IndependentChecker(table); + if (ic.isIndependentFrom(var)) { + it.remove(); + table = ic.removeVar(var); + isReduced = true; + } else { + var++; + } + } + return isReduced; + } + + /** + * @return the remaining variables + */ + public List getVars() { + return vars; + } + + /** + * @return the reduced table + */ + public BoolTable getTable() { + return table; + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/table/ExpressionCreator.java b/src/main/java/de/neemann/digital/gui/components/table/ExpressionCreator.java index 97fabf89c..30a7df694 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/ExpressionCreator.java +++ b/src/main/java/de/neemann/digital/gui/components/table/ExpressionCreator.java @@ -1,14 +1,18 @@ package de.neemann.digital.gui.components.table; +import de.neemann.digital.analyse.AnalyseException; import de.neemann.digital.analyse.TruthTable; import de.neemann.digital.analyse.expression.Expression; import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.analyse.expression.Variable; import de.neemann.digital.analyse.expression.format.FormatterException; +import de.neemann.digital.analyse.quinemc.BoolTable; import de.neemann.digital.analyse.quinemc.QuineMcCluskey; +import de.neemann.digital.analyse.quinemc.TableReducer; import de.neemann.digital.analyse.quinemc.TableRow; import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector; import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelectorDefault; +import de.neemann.digital.lang.Lang; import java.util.ArrayList; import java.util.Collections; @@ -24,6 +28,8 @@ import java.util.concurrent.TimeUnit; */ public class ExpressionCreator { + private static final int MAX_INPUTS_ALLOWED = 12; + private final TruthTable theTable; /** @@ -40,24 +46,22 @@ public class ExpressionCreator { * * @throws ExpressionException ExpressionException * @throws FormatterException FormatterException + * @throws AnalyseException AnalyseException */ - public void create(ExpressionListener listener) throws ExpressionException, FormatterException { + public void create(ExpressionListener listener) throws ExpressionException, FormatterException, AnalyseException { final List vars = Collections.unmodifiableList(theTable.getVars()); long time = System.currentTimeMillis(); if (theTable.getResultCount() > 100) { - ExecutorService ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2); + ExecutorService ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); ThreadSaveExpressionListener threadListener = new ThreadSaveExpressionListener(listener); for (int table = 0; table < theTable.getResultCount(); table++) { - final QuineMcCluskey qmc = new QuineMcCluskey(vars) - .fillTableWith(theTable.getResult(table)); - final String resultName = theTable.getResultName(table); final int t = table; ex.submit(() -> { try { System.out.println("start " + t); - calcColumn(threadListener, qmc, resultName, vars); + simplify(listener, vars, theTable.getResultName(t), theTable.getResult(t)); System.out.println("end " + t); - } catch (ExpressionException | FormatterException e) { + } catch (ExpressionException | FormatterException | AnalyseException e) { e.printStackTrace(); } }); @@ -70,17 +74,30 @@ public class ExpressionCreator { } threadListener.close(); } else { - for (int table = 0; table < theTable.getResultCount(); table++) { - QuineMcCluskey qmc = new QuineMcCluskey(vars) - .fillTableWith(theTable.getResult(table)); - calcColumn(listener, qmc, theTable.getResultName(table), vars); - } + for (int table = 0; table < theTable.getResultCount(); table++) + simplify(listener, vars, theTable.getResultName(table), theTable.getResult(table)); listener.close(); } time = System.currentTimeMillis() - time; System.out.println("time: " + time / 1000.0 + " sec"); } + private void simplify(ExpressionListener listener, List vars, String resultName, BoolTable boolTable) throws AnalyseException, ExpressionException, FormatterException { + TableReducer tr = new TableReducer(vars, boolTable); + List localVars = vars; + if (tr.canReduce()) { + System.out.println(resultName + " reduced from " + vars.size() + " to " + tr.getVars().size() + " variables"); + boolTable = tr.getTable(); + localVars = tr.getVars(); + } + if (localVars.size() > MAX_INPUTS_ALLOWED) + throw new AnalyseException(Lang.get("err_toManyInputsIn_N0_max_N1_is_N2", resultName, MAX_INPUTS_ALLOWED, localVars.size())); + + QuineMcCluskey qmc = new QuineMcCluskey(localVars) + .fillTableWith(boolTable); + calcColumn(listener, qmc, resultName, localVars); + } + private static void calcColumn(ExpressionListener listener, QuineMcCluskey qmc, String name, List vars) throws ExpressionException, FormatterException { PrimeSelector ps = new PrimeSelectorDefault(); Expression e = qmc.simplify(ps).getExpression(); diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java index df000c828..7ab454ca5 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java @@ -1,5 +1,6 @@ package de.neemann.digital.gui.components.table; +import de.neemann.digital.analyse.AnalyseException; import de.neemann.digital.analyse.TruthTable; import de.neemann.digital.analyse.TruthTableTableModel; import de.neemann.digital.analyse.expression.Expression; @@ -579,7 +580,7 @@ public class TableDialog extends JDialog { lastGeneratedExpressions = new ExpressionListenerStore(expressionListener); new ExpressionCreator(model.getTable()).create(lastGeneratedExpressions); - } catch (ExpressionException | FormatterException e1) { + } catch (ExpressionException | FormatterException | AnalyseException e1) { new ErrorMessage(Lang.get("msg_errorDuringCalculation")).addCause(e1).show(); } } diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 056f83db2..17887634b 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -423,6 +423,8 @@ Zur Analyse können Sie die Schaltung im Gatterschrittmodus ausführen. Die Bitzahl am Spiltter passt nicht Es sind mindestens zwei Eingänge erforderlich! Zu viele Eingänge. Es sind nur {0} Eingänge erlaubt, es wurden aber {1} gefunden. + Zu viele Variablen bei der Vereinfachung von {0}. +Es sind nur {1} Variablen erlaubt, es wurden aber {2} gefunden. In CUPL ist die Variable {0} nicht erlaubt! Variable {0} ist nicht definiert. Unerwartetes Zeichen {0} diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 45a6cab94..4f6a96c29 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -410,6 +410,8 @@ To analyse you can run the circuit in single gate step mode. Bit count of splitter is not matching Two inputs are required! To many inputs, allowed are {0} but {1} are found. + To many variables used in {0}, +allowed are {1} variables but {2} are found. Variable {0} is not allowed in CUPL source! Variable {0} not defined Unexpected Token {0} diff --git a/src/test/java/de/neemann/digital/analyse/quinemc/IndependentCheckerTest.java b/src/test/java/de/neemann/digital/analyse/quinemc/IndependentCheckerTest.java new file mode 100644 index 000000000..09ec80c45 --- /dev/null +++ b/src/test/java/de/neemann/digital/analyse/quinemc/IndependentCheckerTest.java @@ -0,0 +1,76 @@ +package de.neemann.digital.analyse.quinemc; + +import de.neemann.digital.analyse.expression.ContextFiller; +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.Variable; +import junit.framework.TestCase; + +import static de.neemann.digital.analyse.expression.Operation.and; + +/** + * Created by hneemann on 12.03.17. + */ +public class IndependentCheckerTest extends TestCase { + private Variable a = new Variable("A"); + private Variable b = new Variable("B"); + private Variable c = new Variable("C"); + private Variable d = new Variable("D"); + + public void testSimple() { + Expression ex = and(a, b, c); + + ContextFiller cf = new ContextFiller(a, b, c, d); + + BoolTableExpression bte = new BoolTableExpression(ex, cf); + + IndependentChecker ic = new IndependentChecker(bte); + assertEquals(4, ic.getVars()); + + assertFalse(ic.isIndependentFrom(0)); + assertFalse(ic.isIndependentFrom(1)); + assertFalse(ic.isIndependentFrom(2)); + assertTrue(ic.isIndependentFrom(3)); + } + + public void testSimple2() { + Expression ex = and(a, c, d); + + ContextFiller cf = new ContextFiller(a, b, c, d); + + BoolTableExpression bte = new BoolTableExpression(ex, cf); + + IndependentChecker ic = new IndependentChecker(bte); + assertEquals(4, ic.getVars()); + + assertFalse(ic.isIndependentFrom(0)); + assertTrue(ic.isIndependentFrom(1)); + assertFalse(ic.isIndependentFrom(2)); + assertFalse(ic.isIndependentFrom(3)); + } + + + public void testRemoveVar() { + Expression ex = and(a, b, c); + ContextFiller cf = new ContextFiller(a, b, c, d); + BoolTableExpression bte = new BoolTableExpression(ex, cf); + IndependentChecker ic = new IndependentChecker(bte); + BoolTable btr = ic.removeVar(3); + assertEquals(8, btr.size()); + for (int i = 0; i < 7; i++) + assertEquals(ThreeStateValue.zero, btr.get(i)); + assertEquals(ThreeStateValue.one, btr.get(7)); + } + + public void testRemoveVar2() { + Expression ex = and(a, c, d); + ContextFiller cf = new ContextFiller(a, b, c, d); + BoolTableExpression bte = new BoolTableExpression(ex, cf); + IndependentChecker ic = new IndependentChecker(bte); + BoolTable btr = ic.removeVar(1); + assertEquals(8, btr.size()); + for (int i = 0; i < 7; i++) + assertEquals(ThreeStateValue.zero, btr.get(i)); + assertEquals(ThreeStateValue.one, btr.get(7)); + } + +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/analyse/quinemc/TableReducerTest.java b/src/test/java/de/neemann/digital/analyse/quinemc/TableReducerTest.java new file mode 100644 index 000000000..1845c9f43 --- /dev/null +++ b/src/test/java/de/neemann/digital/analyse/quinemc/TableReducerTest.java @@ -0,0 +1,63 @@ +package de.neemann.digital.analyse.quinemc; + +import de.neemann.digital.analyse.expression.ContextFiller; +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.Variable; +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +import static de.neemann.digital.analyse.expression.Operation.and; + +/** + * Created by hneemann on 12.03.17. + */ +public class TableReducerTest extends TestCase { + private Variable a = new Variable("A"); + private Variable b = new Variable("B"); + private Variable c = new Variable("C"); + private Variable d = new Variable("D"); + + + public void testReduce() { + List vars=new ArrayList<>(); + vars.add(a); + vars.add(b); + vars.add(c); + vars.add(d); + Expression ex = and(a, b, c); + ContextFiller cf = new ContextFiller(vars); + BoolTableExpression bte = new BoolTableExpression(ex, cf); + TableReducer tr = new TableReducer(vars, bte); + + assertTrue(tr.canReduce()); + + vars = tr.getVars(); + assertEquals(3, vars.size()); + assertEquals(a, vars.get(0)); + assertEquals(b, vars.get(1)); + assertEquals(c, vars.get(2)); + } + + public void testReduce2() { + List vars=new ArrayList<>(); + vars.add(a); + vars.add(b); + vars.add(c); + vars.add(d); + Expression ex = and(a, c, d); + ContextFiller cf = new ContextFiller(vars); + BoolTableExpression bte = new BoolTableExpression(ex, cf); + TableReducer tr = new TableReducer(vars, bte); + + assertTrue(tr.canReduce()); + + vars = tr.getVars(); + assertEquals(3, vars.size()); + assertEquals(a, vars.get(0)); + assertEquals(c, vars.get(1)); + assertEquals(d, vars.get(2)); + } + +} \ No newline at end of file