From 5344d520a4a694047da89c8ca7c8886acd8a7be0 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sun, 8 May 2016 15:46:51 +0200 Subject: [PATCH] added expression creator which uses the QMC algorithm --- .../analyse/DetermineJKStateMachine.java | 152 +++++++++++ .../neemann/digital/analyse/TruthTable.java | 112 ++++++++ .../digital/analyse/TruthTableTableModel.java | 31 +++ .../java/de/neemann/digital/gui/Main.java | 3 +- .../components/table/AllSolutionsFrame.java | 44 +++ .../components/table/ExpressionCreator.java | 88 ++++++ .../digital/gui/components/table/Reorder.java | 72 +++++ .../gui/components/table/TableFrame.java | 257 ++++++++++++++++++ .../gui/components/table/package-info.java | 6 + .../analyse/DetermineJKStateMachineTest.java | 76 ++++++ .../digital/analyse/TruthTableTest.java | 19 ++ .../gui/components/table/ReorderTest.java | 31 +++ 12 files changed, 890 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/neemann/digital/analyse/DetermineJKStateMachine.java create mode 100644 src/main/java/de/neemann/digital/gui/components/table/AllSolutionsFrame.java create mode 100644 src/main/java/de/neemann/digital/gui/components/table/ExpressionCreator.java create mode 100644 src/main/java/de/neemann/digital/gui/components/table/Reorder.java create mode 100644 src/main/java/de/neemann/digital/gui/components/table/TableFrame.java create mode 100644 src/main/java/de/neemann/digital/gui/components/table/package-info.java create mode 100644 src/test/java/de/neemann/digital/analyse/DetermineJKStateMachineTest.java create mode 100644 src/test/java/de/neemann/digital/gui/components/table/ReorderTest.java diff --git a/src/main/java/de/neemann/digital/analyse/DetermineJKStateMachine.java b/src/main/java/de/neemann/digital/analyse/DetermineJKStateMachine.java new file mode 100644 index 000000000..d6eddf8fe --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/DetermineJKStateMachine.java @@ -0,0 +1,152 @@ +package de.neemann.digital.analyse; + +import de.neemann.digital.analyse.expression.Constant; +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.ExpressionException; +import de.neemann.digital.analyse.expression.Operation; +import de.neemann.digital.analyse.expression.format.FormatToExpression; +import de.neemann.digital.analyse.expression.format.FormatterException; + +import java.util.Iterator; + +import static de.neemann.digital.analyse.expression.Not.not; +import static de.neemann.digital.analyse.expression.Operation.and; +import static de.neemann.digital.analyse.expression.Operation.or; + +/** + * Creates the expressions to create a JK-FF state machine + * + * @author hneemann + */ +public class DetermineJKStateMachine { + private Expression j = null; + private Expression nk = null; + + /** + * Creates a new instance + * + * @param name the name of the state variable + * @param e the expression to split in J and K expression + * @throws ExpressionException ExpressionException + * @throws FormatterException FormatterException + */ + public DetermineJKStateMachine(String name, Expression e) throws ExpressionException, FormatterException { + String notName = "¬" + name; + + boolean wasK = false; + boolean wasJ = false; + for (Expression or : getOrs(e)) { + + Expression term = null; + boolean belongsToK = false; + boolean belongsToJ = false; + + for (Expression a : getAnds(or)) { + String str = FormatToExpression.FORMATTER_UNICODE.format(a); + if (str.equals(name)) { + belongsToK = true; + wasK = true; + } else if (str.equals(notName)) { + belongsToJ = true; + wasJ = true; + } else { + term = and(term, a); + } + } + + if (belongsToJ && belongsToK) { + throw new ExpressionException("contains var and not var"); + } else { + if (belongsToJ) { + j = or(term, j); + } else if (belongsToK) { + nk = or(term, nk); + } else { + j = or(term, j); + nk = or(term, nk); + } + } + } + if (j == null) { + if (wasJ) j = Constant.ONE; + else j = Constant.ZERO; + } + if (nk == null) { + if (wasK) nk = Constant.ONE; + else nk = Constant.ZERO; + } + } + + private Iterable getOrs(Expression e) { + if (e instanceof Operation.Or) { + return ((Operation.Or) e).getExpressions(); + } else + return new AsIterable<>(e); + } + + private Iterable getAnds(Expression e) { + if (e instanceof Operation.And) { + return ((Operation.And) e).getExpressions(); + } else + return new AsIterable<>(e); + } + + /** + * @return the J expression + */ + public Expression getJ() { + return j; + } + + /** + * @return the not(K) expression + */ + public Expression getNK() { + return nk; + } + + /** + * @return the K expression + */ + public Expression getK() { + return not(nk); + } + + private static final class AsIterable implements Iterable { + private final T item; + + private AsIterable(T item) { + this.item = item; + } + + @Override + public Iterator iterator() { + return new SingleItemIterator<>(item); + } + + private static final class SingleItemIterator implements Iterator { + private T item; + + private SingleItemIterator(T item) { + this.item = item; + } + + @Override + public boolean hasNext() { + return item != null; + } + + @Override + public T next() { + T ee = item; + item = null; + return ee; + } + + @Override + public void remove() { + } + } + } + +} diff --git a/src/main/java/de/neemann/digital/analyse/TruthTable.java b/src/main/java/de/neemann/digital/analyse/TruthTable.java index daf15261b..55d82a152 100644 --- a/src/main/java/de/neemann/digital/analyse/TruthTable.java +++ b/src/main/java/de/neemann/digital/analyse/TruthTable.java @@ -1,6 +1,8 @@ package de.neemann.digital.analyse; import de.neemann.digital.analyse.expression.BitSetter; +import de.neemann.digital.analyse.expression.Context; +import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.analyse.expression.Variable; import de.neemann.digital.analyse.quinemc.BoolTable; import de.neemann.digital.analyse.quinemc.BoolTableIntArray; @@ -44,6 +46,19 @@ public class TruthTable { results = new ArrayList<>(); } + /** + * Creates a new instance + * + * @param newVars the variables to use + * @param oldTable delivers the column names for the results + */ + public TruthTable(ArrayList newVars, TruthTable oldTable) { + this(newVars); + for (int i = 0; i < oldTable.getResultCount(); i++) { + addResult(oldTable.results.get(i).getName(), new BoolTableIntArray(getRows())); + } + } + /** * Returns the number of rows * @@ -63,6 +78,17 @@ public class TruthTable { results.add(new Result(name, values)); } + /** + * Adds a new column + * + * @return this for call chaining + */ + public TruthTable addResult() { + results.add(new Result("Y", new BoolTableIntArray(getRows()))); + return this; + } + + /** * Adds a variable * @@ -179,6 +205,92 @@ public class TruthTable { } } + /** + * Sets the column name + * + * @param columnIndex the column + * @param name the new name + */ + public void setColumnName(int columnIndex, String name) { + if (columnIndex < variables.size()) + variables.set(columnIndex, new Variable(name)); + else { + results.get(columnIndex - variables.size()).setName(name); + } + } + + /** + * @return the used variables + */ + public ArrayList getVars() { + return variables; + } + + /** + * Gets the value which is determined by the actual context state + * + * @param result the result index + * @param context the context + * @return the table value + * @throws ExpressionException ExpressionException + */ + public int getByContext(int result, Context context) throws ExpressionException { + return results.get(result).getValues().get(getIndexByContext(context)).asInt(); + } + + /** + * Sets the value which is determined by the actual context state + * + * @param result the result index + * @param context the context + * @param value the new value + * @throws ExpressionException ExpressionException + */ + public void setByContext(int result, Context context, int value) throws ExpressionException { + BoolTable v = results.get(result).getValues(); + if (v instanceof BoolTableIntArray) + ((BoolTableIntArray) v).set(getIndexByContext(context), value); + } + + private int getIndexByContext(Context context) throws ExpressionException { + int mask = 1 << (variables.size() - 1); + int index = 0; + for (int i = 0; i < variables.size(); i++) { + if (context.get(variables.get(i))) { + index |= mask; + } + mask >>= 1; + } + return index; + } + + /** + * @return the number of results + */ + public int getResultCount() { + return results.size(); + } + + /** + * Returns the result with the given index + * + * @param result the result index + * @return the table representing the result + */ + public BoolTable getResult(int result) { + return results.get(result).getValues(); + } + + /** + * Returns the results name + * + * @param result index of result + * @return the name + */ + public String getResultName(int result) { + return results.get(result).getName(); + } + /** * A single result column */ diff --git a/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java b/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java index 72ef5682e..891b4cfce 100644 --- a/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java +++ b/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java @@ -1,7 +1,9 @@ package de.neemann.digital.analyse; +import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.TableModel; +import java.util.ArrayList; /** * Used to visualize a truthTable instance in a JTable @@ -9,7 +11,13 @@ import javax.swing.table.TableModel; * @author hneemann */ public class TruthTableTableModel implements TableModel { + /** + * String representation of the states + */ + public static final String[] STATENAMES = new String[]{"0", "1", "x"}; + private final TruthTable truthTable; + private ArrayList listeners = new ArrayList<>(); /** * Creates a new instance @@ -54,13 +62,36 @@ public class TruthTableTableModel implements TableModel { public void setValueAt(Object aValue, int rowIndex, int columnIndex) { if (aValue instanceof Integer) truthTable.setValue(rowIndex, columnIndex, (Integer) aValue); + if (aValue instanceof String) { + if (aValue.toString().equals("0")) + truthTable.setValue(rowIndex, columnIndex, 0); + else if (aValue.toString().equals("1")) + truthTable.setValue(rowIndex, columnIndex, 1); + else + truthTable.setValue(rowIndex, columnIndex, 2); + } + fireModelEvent(rowIndex); + } + + private void fireModelEvent(int rowIndex) { + TableModelEvent e = new TableModelEvent(this, rowIndex); + for (TableModelListener l : listeners) + l.tableChanged(e); } @Override public void addTableModelListener(TableModelListener l) { + listeners.add(l); } @Override public void removeTableModelListener(TableModelListener l) { } + + /** + * @return the truth table used by this model + */ + public TruthTable getTable() { + return truthTable; + } } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index cfc9847c5..efce82326 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -19,6 +19,7 @@ import de.neemann.digital.draw.shapes.ShapeFactory; import de.neemann.digital.gui.components.*; import de.neemann.digital.gui.components.data.DataSetDialog; import de.neemann.digital.gui.components.listing.ROMListingDialog; +import de.neemann.digital.gui.components.table.TableFrame; import de.neemann.digital.gui.state.State; import de.neemann.digital.gui.state.StateManager; import de.neemann.digital.lang.Lang; @@ -401,7 +402,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E public void actionPerformed(ActionEvent e) { try { Model model = new ModelDescription(circuitComponent.getCircuit(), library).createModel(false); - new TableDialog(Main.this, new ModelAnalyser(model).analyse()).setVisible(true); + new TableFrame(Main.this, new ModelAnalyser(model).analyse()).setVisible(true); elementState.activate(); } catch (PinException | NodeException | AnalyseException e1) { showErrorAndStopModel(Lang.get("msg_annalyseErr"), e1); diff --git a/src/main/java/de/neemann/digital/gui/components/table/AllSolutionsFrame.java b/src/main/java/de/neemann/digital/gui/components/table/AllSolutionsFrame.java new file mode 100644 index 000000000..4fe8792f7 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/table/AllSolutionsFrame.java @@ -0,0 +1,44 @@ +package de.neemann.digital.gui.components.table; + +import javax.swing.*; +import java.awt.*; + +/** + * Simple Dialog to show all possible functions of a truth table + * + * @author hneemann + */ +public class AllSolutionsFrame extends JDialog { + private final JTextArea textArea; + + /** + * Creates a new Frame + * + * @param owner the owner frame + * @param font the font to use + */ + public AllSolutionsFrame(Frame owner, Font font) { + super(owner, "Alle möglichen Lösungen", false); + setDefaultCloseOperation(HIDE_ON_CLOSE); + + textArea = new JTextArea(6, 30); + textArea.setFont(font); + textArea.setEditable(false); + textArea.setTabSize(3); + + getContentPane().add(new JScrollPane(textArea)); + pack(); + setLocationRelativeTo(owner); + } + + /** + * Sets the gicen text to the frame + * + * @param text the text + * @return this for call chaining + */ + public AllSolutionsFrame setText(String text) { + textArea.setText(text); + return this; + } +} 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 new file mode 100644 index 000000000..e84648827 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/table/ExpressionCreator.java @@ -0,0 +1,88 @@ +package de.neemann.digital.gui.components.table; + +import de.neemann.digital.analyse.DetermineJKStateMachine; +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.QuineMcCluskey; +import de.neemann.digital.analyse.quinemc.TableRow; +import de.neemann.digital.analyse.quinemc.primeselector.BruteForceGetAll; + +import java.util.ArrayList; + +/** + * Used to generate the expressions belonging to the given truth table + * + * @author hneemann + */ +public abstract class ExpressionCreator { + + private final TruthTable theTable; + + /** + * Creates a new instance + * + * @param theTable the table to use + */ + public ExpressionCreator(TruthTable theTable) { + this.theTable = theTable; + } + + /** + * Creates the expressions + * + * @throws ExpressionException ExpressionException + * @throws FormatterException FormatterException + */ + public void create() throws ExpressionException, FormatterException { + ArrayList vars = theTable.getVars(); + for (int table = 0; table < theTable.getResultCount(); table++) { + BruteForceGetAll ps = new BruteForceGetAll(); + Expression e = new QuineMcCluskey(vars) + .fillTableWith(theTable.getResult(table)) + .simplify(ps) + .getExpression(); + + if (ps.getAllSolutions() != null) { + for (ArrayList i : ps.getAllSolutions()) { + resultFoundInt(theTable.getResultName(table), QuineMcCluskey.addAnd(null, i, vars)); + } + } else { + resultFoundInt(theTable.getResultName(table), e); + } + } + } + + + private void resultFoundInt(String name, Expression expression) throws FormatterException, ExpressionException { + resultFound(name, expression); + + if (name.endsWith("n+1")) { + String detName = name.substring(0, name.length() - 2); + DetermineJKStateMachine jk = new DetermineJKStateMachine(detName, expression); + resultFound("J" + detName, jk.getJ()); + Expression s = QuineMcCluskey.simplify(jk.getJ()); + if (s != null) { + resultFound("", s); + } + resultFound("K" + detName, jk.getK()); + s = QuineMcCluskey.simplify(jk.getK()); + if (s != null) { + resultFound("", s); + } + } + } + + /** + * Method to overload to handle all found solutions + * + * @param name the results name + * @param expression the calculated expressdion + * @throws FormatterException FormatterException + * @throws ExpressionException ExpressionException + */ + public abstract void resultFound(String name, Expression expression) throws FormatterException, ExpressionException; + +} diff --git a/src/main/java/de/neemann/digital/gui/components/table/Reorder.java b/src/main/java/de/neemann/digital/gui/components/table/Reorder.java new file mode 100644 index 000000000..44160c111 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/table/Reorder.java @@ -0,0 +1,72 @@ +package de.neemann.digital.gui.components.table; + +import de.neemann.digital.analyse.TruthTable; +import de.neemann.digital.analyse.expression.ContextFiller; +import de.neemann.digital.analyse.expression.ExpressionException; +import de.neemann.digital.analyse.expression.Variable; + +import java.util.ArrayList; + +/** + * Used to reorder the variables + * + * @author hneemann + */ +public class Reorder { + + private final TruthTable table; + + /** + * Creates a new instance + * + * @param table the table to use + */ + public Reorder(TruthTable table) { + this.table = table; + } + + /** + * Reorders the variables + * + * @param swap the ordering + * @return the new table + * @throws ExpressionException ExpressionException + */ + public TruthTable reorder(int[] swap) throws ExpressionException { + checkSwapTable(swap); + + ArrayList newVars = new ArrayList<>(); + for (int j = 0; j < table.getVars().size(); j++) { + newVars.add(table.getVars().get(swap[j])); + } + + TruthTable newTable = new TruthTable(newVars, table); + + ContextFiller fc = new ContextFiller(table.getVars()); + for (int row = 0; row < table.getRows(); row++) { + fc.setContextTo(row); + for (int t = 0; t < table.getResultCount(); t++) + newTable.setByContext(t, fc, table.getByContext(t, fc)); + } + + return newTable; + } + + private void checkSwapTable(int[] swap) { + int cols = table.getVars().size(); + if (swap.length != cols) + throw new RuntimeException("wrong swap list length!"); + + for (int i = 0; i < cols; i++) { + if (swap[i] < 0) + throw new RuntimeException("swap index<0"); + if (swap[i] >= cols) + throw new RuntimeException("swap index>" + (table.getCols() - 1)); + for (int j = 0; j < cols; j++) { + if ((i != j) && swap[i] == swap[j]) + throw new RuntimeException("two times the same swap index " + (swap[i])); + } + } + } + +} diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableFrame.java b/src/main/java/de/neemann/digital/gui/components/table/TableFrame.java new file mode 100644 index 000000000..0c542f8b7 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/table/TableFrame.java @@ -0,0 +1,257 @@ +package de.neemann.digital.gui.components.table; + +import de.neemann.digital.analyse.TruthTable; +import de.neemann.digital.analyse.TruthTableTableModel; +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.ExpressionException; +import de.neemann.digital.analyse.expression.format.FormatToExpression; +import de.neemann.digital.analyse.expression.format.FormatterException; +import de.neemann.gui.ErrorMessage; + +import javax.swing.*; +import javax.swing.border.MatteBorder; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableColumn; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/** + * @author hneemann + */ +public class TableFrame extends JFrame { + private static final Color MYGRAY = new Color(230, 230, 230); + private final JLabel label; + private final JTable table; + private final JTableHeader header; + private final JTextField text; + private final JPopupMenu renamePopup; + private final Font font; + private final JMenu reorderMenu; + private TruthTableTableModel model; + private TableColumn column; + private int columnIndex; + private AllSolutionsFrame allSolutionsFrame; + + /** + * Creates a new instance + * + * @param parent the parent frame + * @param truthTable the table to show + */ + public TableFrame(JFrame parent, TruthTable truthTable) { + super("Table"); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + + label = new JLabel(); + font = label.getFont().deriveFont(20.0f); + label.setFont(font); + table = new JTable(model); + JComboBox comboBox = new JComboBox(TruthTableTableModel.STATENAMES); + table.setDefaultEditor(Integer.class, new DefaultCellEditor(comboBox)); + table.setDefaultRenderer(Integer.class, new CenterDefaultTableCellRenderer(true)); + table.setRowHeight(25); + + allSolutionsFrame = new AllSolutionsFrame(this, font); + + header = table.getTableHeader(); + header.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + editColumnAt(event.getPoint()); + } + } + }); + + text = new JTextField(); + text.setBorder(null); + text.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + column.setHeaderValue(text.getText()); + renamePopup.setVisible(false); + header.repaint(); + model.getTable().setColumnName(columnIndex, text.getText()); + calculateExpressions(); + } + }); + + renamePopup = new JPopupMenu(); + renamePopup.setBorder(new MatteBorder(0, 1, 1, 1, Color.DARK_GRAY)); + renamePopup.add(text); + + JMenuBar bar = new JMenuBar(); + + JMenu sizeMenu = new JMenu("Size"); + for (int i = 2; i <= 8; i++) + sizeMenu.add(new JMenuItem(new SizeAction(i))); + bar.add(sizeMenu); + + reorderMenu = new JMenu("Reorder"); + bar.add(reorderMenu); + + JMenu colsMenu = new JMenu("Columns"); + colsMenu.add(new JMenuItem(new AbstractAction("add column") { + @Override + public void actionPerformed(ActionEvent actionEvent) { + TruthTable t = model.getTable(); + t.addResult(); + setModel(new TruthTableTableModel(t)); + } + })); + bar.add(colsMenu); + + setJMenuBar(bar); + + setModel(new TruthTableTableModel(truthTable)); + + getContentPane().add(new JScrollPane(table)); + getContentPane().add(label, BorderLayout.SOUTH); + pack(); + setLocationRelativeTo(parent); + } + + private void editColumnAt(Point p) { + columnIndex = header.columnAtPoint(p); + + if (columnIndex != -1) { + column = header.getColumnModel().getColumn(columnIndex); + Rectangle columnRectangle = header.getHeaderRect(columnIndex); + + text.setText(column.getHeaderValue().toString()); + renamePopup.setPreferredSize( + new Dimension(columnRectangle.width, columnRectangle.height - 1)); + renamePopup.show(header, columnRectangle.x, 0); + + text.requestFocusInWindow(); + text.selectAll(); + } + } + + private void setInputVariables(int n) { + setModel(new TruthTableTableModel(new TruthTable(n).addResult())); + } + + private void setModel(TruthTableTableModel model) { + this.model = model; + model.addTableModelListener(new CalculationTableModelListener()); + table.setModel(model); + reorderMenu.removeAll(); + int cols = model.getTable().getVars().size(); + reorderMenu.add(new JMenuItem(new ReorderAction(cols))); + for (int i = 0; i < cols - 1; i++) { + reorderMenu.add(new JMenuItem(new ReorderAction(cols, i))); + } + calculateExpressions(); + } + + + private class CalculationTableModelListener implements TableModelListener { + @Override + public void tableChanged(TableModelEvent tableModelEvent) { + calculateExpressions(); + } + } + + private void calculateExpressions() { + try { + final StringBuilder sb = new StringBuilder(); + new ExpressionCreator(model.getTable()) { + private int count = 0; + + @Override + public void resultFound(String name, Expression expression) throws FormatterException { + String expr = name + "\t=" + FormatToExpression.FORMATTER_UNICODE.format(expression); + if (count == 0) + label.setText(expr); + if (sb.length() > 0) sb.append('\n'); + sb.append(expr); + count++; + if (count == 2) + allSolutionsFrame.setVisible(true); + } + }.create(); + + if (sb.length() == 0) + label.setText(""); + + allSolutionsFrame.setText(sb.toString()); + } catch (ExpressionException | FormatterException e1) { + new ErrorMessage("error during calculation").addCause(e1).show(); + } + } + + private final class SizeAction extends AbstractAction { + + private int n; + + private SizeAction(int n) { + super(n + " Vars"); + this.n = n; + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + setInputVariables(n); + } + } + + private final class CenterDefaultTableCellRenderer extends DefaultTableCellRenderer { + private final boolean gray; + + private CenterDefaultTableCellRenderer(boolean gray) { + this.gray = gray; + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + label.setHorizontalAlignment(SwingConstants.CENTER); + label.setFont(font); + if (gray) + label.setBackground(MYGRAY); + else + label.setBackground(Color.WHITE); + return label; + } + } + + private final class ReorderAction extends AbstractAction { + + private final int[] swap; + + private ReorderAction(int cols) { + super("reverse"); + swap = new int[cols]; + for (int i = 0; i < cols; i++) + swap[cols - i - 1] = i; + } + + private ReorderAction(int cols, int swapIndex) { + super("swap " + swapIndex + " and " + (swapIndex + 1)); + swap = new int[cols]; + for (int i = 0; i < cols; i++) + swap[i] = i; + + int z = swap[swapIndex]; + swap[swapIndex] = swap[swapIndex + 1]; + swap[swapIndex + 1] = z; + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + try { + setModel(new TruthTableTableModel(new Reorder(model.getTable()).reorder(swap))); + } catch (ExpressionException e) { + e.printStackTrace(); + } + } + } + +} diff --git a/src/main/java/de/neemann/digital/gui/components/table/package-info.java b/src/main/java/de/neemann/digital/gui/components/table/package-info.java new file mode 100644 index 000000000..922912432 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/table/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes to visualize a truth table + * + * @author hneemann + */ +package de.neemann.digital.gui.components.table; diff --git a/src/test/java/de/neemann/digital/analyse/DetermineJKStateMachineTest.java b/src/test/java/de/neemann/digital/analyse/DetermineJKStateMachineTest.java new file mode 100644 index 000000000..891214c57 --- /dev/null +++ b/src/test/java/de/neemann/digital/analyse/DetermineJKStateMachineTest.java @@ -0,0 +1,76 @@ +package de.neemann.digital.analyse; + + +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.format.FormatToExpression; +import de.neemann.digital.analyse.expression.format.FormatterException; +import junit.framework.TestCase; + +import static de.neemann.digital.analyse.expression.Not.not; +import static de.neemann.digital.analyse.expression.Operation.and; +import static de.neemann.digital.analyse.expression.Operation.or; +import static de.neemann.digital.analyse.expression.Variable.v; + + +/** + * @author hneemann + */ +public class DetermineJKStateMachineTest extends TestCase { + + private Expression a; + private Expression nota; + private Expression b; + private Expression notb; + private Expression c; + private Expression notc; + + public void setUp() throws Exception { + a = v("a"); + nota = not(a); + b = v("b"); + notb = not(b); + c = v("c"); + notc = not(c); + } + + public void testSimple() throws Exception { + + Expression e = or(and(a, c), and(nota, notb)); + + DetermineJKStateMachine jk = new DetermineJKStateMachine("a", e); + assertEquals(toStr(notb), toStr(jk.getJ())); + assertEquals(toStr(notc), toStr(jk.getK())); + + } + + private String toStr(Expression expression) throws FormatterException { + return FormatToExpression.FORMATTER_UNICODE.format(expression); + } + + public void testSimple2() throws Exception { + + Expression e = or(and(a, c), and(nota, notb), and(b, c)); + + DetermineJKStateMachine jk = new DetermineJKStateMachine("a", e); + assertEquals("(b ∧ c) ∨ ¬b", toStr(jk.getJ())); + assertEquals("¬((b ∧ c) ∨ c)", toStr(jk.getK())); + + } + + public void testSimple3() throws Exception { + Expression e = or(nota); + + DetermineJKStateMachine jk = new DetermineJKStateMachine("a", e); + assertEquals("1", toStr(jk.getJ())); + assertEquals("1", toStr(jk.getK())); + } + + public void testSimple4() throws Exception { + Expression e = or(a); + + DetermineJKStateMachine jk = new DetermineJKStateMachine("a", e); + assertEquals("0", toStr(jk.getJ())); + assertEquals("0", toStr(jk.getK())); + } + +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/analyse/TruthTableTest.java b/src/test/java/de/neemann/digital/analyse/TruthTableTest.java index afdce9234..c666d10a3 100644 --- a/src/test/java/de/neemann/digital/analyse/TruthTableTest.java +++ b/src/test/java/de/neemann/digital/analyse/TruthTableTest.java @@ -1,7 +1,12 @@ package de.neemann.digital.analyse; +import de.neemann.digital.analyse.expression.ContextFiller; +import de.neemann.digital.analyse.expression.Variable; +import de.neemann.digital.analyse.quinemc.BoolTableIntArray; import junit.framework.TestCase; +import java.util.ArrayList; + /** * @author hneemann */ @@ -13,4 +18,18 @@ public class TruthTableTest extends TestCase { assertEquals(8, t.getRows()); } + public void testGetByContext() throws Exception { + ArrayList vars = Variable.vars(5); + TruthTable t = new TruthTable(vars).addResult(); + BoolTableIntArray result = (BoolTableIntArray) t.getResult(0); + for (int i = 0; i < t.getRows(); i++) { + result.set(i, i % 3); + } + + ContextFiller fc = new ContextFiller(vars); + for (int i = 0; i < t.getRows(); i++) { + fc.setContextTo(i); + assertEquals(i % 3, t.getByContext(0, fc)); + } + } } \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/gui/components/table/ReorderTest.java b/src/test/java/de/neemann/digital/gui/components/table/ReorderTest.java new file mode 100644 index 000000000..67e483166 --- /dev/null +++ b/src/test/java/de/neemann/digital/gui/components/table/ReorderTest.java @@ -0,0 +1,31 @@ +package de.neemann.digital.gui.components.table; + +import de.neemann.digital.analyse.TruthTable; +import de.neemann.digital.analyse.expression.ContextFiller; +import de.neemann.digital.analyse.quinemc.BoolTableIntArray; +import junit.framework.TestCase; + +/** + * @author hneemann + */ +public class ReorderTest extends TestCase { + + public void testReorder() throws Exception { + TruthTable t = new TruthTable(5).addResult(); + BoolTableIntArray col = (BoolTableIntArray) t.getResult(0); + for (int i = 0; i < t.getRows(); i++) + col.set(i, i + 1); + + + int[] swap = new int[]{4, 3, 2, 0, 1}; + TruthTable newTable = new Reorder(t).reorder(swap); + + + ContextFiller cf = new ContextFiller(t.getVars()); + for (int i = 0; i < t.getRows(); i++) { + cf.setContextTo(i); + assertEquals(newTable.getByContext(0, cf), t.getByContext(0, cf)); + } + } + +} \ No newline at end of file