Merge branch 'master' into treeSearch

This commit is contained in:
hneemann 2021-02-20 11:19:04 +01:00
commit b4cbe8d9e0
21 changed files with 2813 additions and 137 deletions

View File

@ -2,7 +2,9 @@ Release Notes
HEAD, planned as v0.27
- Added fixed point and floating point number formats
- Added fixed point and floating point number formats.
- Added a CSV truth table export and import.
- Added 74299
- Refactoring of the expression format setting.
CAUTION: All the general settings are maybe lost at restart!
To avoid this, open the .digital.cfg file and remove the <entry>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2021 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.analyse;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.quinemc.BoolTableByteArray;
import de.neemann.digital.lang.Lang;
import java.io.*;
import java.util.ArrayList;
/**
* Used to read a CSV file
*/
public final class CSVImporter {
private CSVImporter() {
}
/**
* Reads a CSV file
*
* @param file the file
* @return the truth table
* @throws IOException IOException
*/
public static TruthTable readCSV(File file) throws IOException {
return readCSV(new FileReader(file));
}
/**
* Reads a CSV file
*
* @param csv the string to read
* @return the truth table
* @throws IOException IOException
*/
public static TruthTable readCSV(String csv) throws IOException {
return readCSV(new StringReader(csv));
}
/**
* Reads a CSV file
*
* @param csv the reader
* @return the truth table
* @throws IOException IOException
*/
public static TruthTable readCSV(Reader csv) throws IOException {
BufferedReader r = new BufferedReader(csv);
TruthTable tt = readHeader(r);
while (true) {
String line = r.readLine();
if (line == null)
return tt;
line = line.trim();
if (!line.isEmpty())
parseLine(tt, line);
}
}
private static TruthTable readHeader(BufferedReader r) throws IOException {
String header;
do {
header = r.readLine();
} while (header != null && header.length() == 0);
if (header == null)
throw new IOException(Lang.get("err_csvNoHeaderFound"));
ArrayList<Variable> vars = new ArrayList<>();
TruthTable tt = null;
for (String ss : header.split(",")) {
String h = ss.trim();
if (h.isEmpty())
tt = new TruthTable(vars);
else {
if (tt == null)
vars.add(new Variable(h));
else
tt.addResult(h, new BoolTableByteArray(1 << vars.size()));
}
}
if (tt == null || tt.getResultCount() == 0)
throw new IOException(Lang.get("err_csvNoOutputValuesFound"));
return tt;
}
private static void parseLine(TruthTable tt, String line) throws IOException {
int resNum = tt.getResultCount();
int varNum = tt.getVars().size();
int mask = 1 << (varNum - 1);
ArrayList<Integer> dc = new ArrayList<>();
int row = 0;
int rCol = 0;
Generator generator = null;
for (String ss : line.split(",")) {
String e = ss.trim().toLowerCase();
if (e.isEmpty())
generator = new Generator(row, dc);
else {
if (generator == null) {
if (mask == 0)
throw new IOException(Lang.get("err_csvToManyValues"));
if (e.equals("1"))
row |= mask;
else if (e.equals("x"))
dc.add(mask);
mask = mask >> 1;
} else {
if (rCol >= resNum)
throw new IOException(Lang.get("err_csvToManyValues"));
if (e.equals("1"))
generator.addCol(rCol);
rCol++;
}
}
}
if (mask != 0)
throw new IOException(Lang.get("err_csvNotEnoughValues"));
if (generator != null)
generator.applyTo(tt);
}
private static final class Generator {
private final int row;
private final ArrayList<Integer> dc;
private final ArrayList<Integer> cols;
private Generator(int row, ArrayList<Integer> dc) {
this.row = row;
this.dc = dc;
cols = new ArrayList<>();
}
public void addCol(int rCol) {
cols.add(rCol);
}
public void applyTo(TruthTable tt) {
if (cols.isEmpty())
return;
int vars = tt.getVars().size();
if (dc.isEmpty())
for (int col : cols)
tt.setValue(row, vars + col, 1);
else {
int dcRows = 1 << dc.size();
for (int i = 0; i < dcRows; i++) {
int r = row;
int bitMask = 1;
for (int orMask : dc) {
if ((i & bitMask) != 0)
r = r | orMask;
bitMask *= 2;
}
for (int col : cols)
tt.setValue(r, vars + col, 1);
}
}
}
}
}

View File

@ -14,8 +14,6 @@ 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.BoolTableByteArray;
import de.neemann.digital.analyse.quinemc.ThreeStateValue;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.undo.Copyable;
import java.io.*;
@ -41,9 +39,13 @@ public class TruthTable implements Copyable<TruthTable> {
* @throws IOException IOException
*/
public static TruthTable readFromFile(File filename) throws IOException {
XStream xStream = getxStream();
try (InputStream in = new FileInputStream(filename)) {
return (TruthTable) xStream.fromXML(in);
if (filename.getName().toLowerCase().endsWith(".csv"))
return CSVImporter.readCSV(filename);
else {
XStream xStream = getxStream();
try (InputStream in = new FileInputStream(filename)) {
return (TruthTable) xStream.fromXML(in);
}
}
}
@ -61,45 +63,6 @@ public class TruthTable implements Copyable<TruthTable> {
}
}
/**
* Save the table as hex file to be loaded in a ROM or LUT element.
*
* @param filename filename
* @throws IOException IOException
*/
public void saveHex(File filename) throws IOException {
if (results.size() > 63)
throw new IOException(Lang.get("err_tableHasToManyResultColumns"));
try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), StandardCharsets.UTF_8))) {
saveHex(out);
}
}
/**
* Save the table as hex file to be loaded in a ROM or LUT element.
*
* @param writer the filename to use
* @throws IOException IOException
*/
public void saveHex(Writer writer) throws IOException {
writer.write("v2.0 raw\n");
int count = results.get(0).getValues().size();
for (int i = 0; i < count; i++) {
long val = 0;
long mask = 1;
for (Result r : results) {
ThreeStateValue v = r.getValues().get(i);
if (v == ThreeStateValue.one)
val |= mask;
mask *= 2;
}
writer.write(Long.toHexString(val));
writer.write('\n');
}
}
private static XStream getxStream() {
XStream xStream = new XStreamValid();
xStream.alias("truthTable", TruthTable.class);

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2021 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.analyse.format;
import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.quinemc.ThreeStateValue;
import de.neemann.digital.core.Bits;
/**
* Exports a table in LogicFriday format
*/
public class TruthTableFormatterCSV implements TruthTableFormatter {
@Override
public String format(TruthTable truthTable) throws ExpressionException {
StringBuilder sb = new StringBuilder();
for (String n : truthTable.getVarNames())
sb.append(n).append(",");
for (String n : truthTable.getResultNames())
sb.append(',').append(n);
sb.append('\n');
export(sb, truthTable);
return sb.toString();
}
private void export(StringBuilder sb, TruthTable truthTable) {
int vars = truthTable.getVars().size();
for (int r = 0; r < truthTable.getRows(); r++) {
long m = Bits.up(1, vars - 1);
for (int c = 0; c < vars; c++) {
if ((r & m) == 0)
sb.append('0');
else
sb.append('1');
sb.append(',');
m = m >> 1;
}
for (int c = 0; c < truthTable.getResultCount(); c++) {
ThreeStateValue v = truthTable.getResult(c).get(r);
sb.append(',').append(v.toString());
}
sb.append('\n');
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.analyse.format;
import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.quinemc.ThreeStateValue;
import de.neemann.digital.lang.Lang;
/**
* Formats a truth table
*/
public class TruthTableFormatterHex implements TruthTableFormatter {
@Override
public String format(TruthTable truthTable) throws ExpressionException {
if (truthTable.getResultCount() > 63)
throw new ExpressionException(Lang.get("err_tableHasToManyResultColumns"));
StringBuilder sb = new StringBuilder();
sb.append("v2.0 raw\n");
int count = truthTable.getResult(0).size();
for (int i = 0; i < count; i++) {
long val = 0;
long mask = 1;
for (int j = 0; j < truthTable.getResultCount(); j++) {
ThreeStateValue v = truthTable.getResult(j).get(i);
if (v == ThreeStateValue.one)
val |= mask;
mask *= 2;
}
sb.append(Long.toHexString(val));
sb.append('\n');
}
return sb.toString();
}
}

View File

@ -145,10 +145,14 @@ public final class Bits {
if (str.indexOf(':') >= 0)
return decodeFixed(str);
if (str.indexOf('.') > -1) {
if (str.endsWith("d") || str.endsWith("D"))
return Double.doubleToLongBits(Double.parseDouble(str.substring(0, str.length() - 1)));
else
return Float.floatToIntBits(Float.parseFloat(str));
try {
if (str.endsWith("d") || str.endsWith("D"))
return Double.doubleToLongBits(Double.parseDouble(str.substring(0, str.length() - 1)));
else
return Float.floatToIntBits(Float.parseFloat(str));
} catch (java.lang.NumberFormatException e) {
throw new NumberFormatException(str, 0);
}
}
int p = 0;

View File

@ -499,9 +499,17 @@ public enum IntFormat {
switch (inValue.getBits()) {
case 32:
return Float.toString(Float.intBitsToFloat((int) inValue.getValue()));
float f = Float.intBitsToFloat((int) inValue.getValue());
if (Float.isFinite(f))
return Float.toString(f);
else
return HEX_FORMATTER.formatToEdit(inValue);
case 64:
return Double.longBitsToDouble(inValue.getValue()) + "d";
double d = Double.longBitsToDouble(inValue.getValue());
if (Double.isFinite(d))
return d + "d";
else
return HEX_FORMATTER.formatToEdit(inValue);
default:
return HEX_FORMATTER.formatToEdit(inValue);
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2021 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.gui.components.table;
import de.neemann.digital.analyse.expression.*;
import de.neemann.digital.analyse.expression.format.FormatterException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
/**
* Exports a CSV table containing only the prime implicants
*/
public class ExpressionListenerCSVCondensed implements ExpressionListener {
private final ArrayList<Result> results;
private final HashSet<String> names;
private final VariableVisitor variables;
private StringBuilder str;
/**
* Creates a new instance
*/
public ExpressionListenerCSVCondensed() {
results = new ArrayList<>();
names = new HashSet<>();
variables = new VariableVisitor();
}
@Override
public void resultFound(String name, Expression expression) throws FormatterException, ExpressionException {
if (!names.contains(name)) {
names.add(name);
results.add(new Result(name, expression, results.size()));
expression.traverse(variables);
}
}
@Override
public void close() throws FormatterException, ExpressionException {
str = new StringBuilder();
for (Variable var : variables.getVariables())
str.append(var.getIdentifier()).append(",");
for (Result r : results)
str.append(",").append(r.name);
str.append("\n");
for (Result r : results)
r.createString(str, variables.getVariables(), results.size());
}
@Override
public String toString() {
return str.toString();
}
private static final class Result {
private final String name;
private final Expression expression;
private final int number;
private Result(String name, Expression expression, int number) {
this.name = name;
this.expression = expression;
this.number = number;
}
public void createString(StringBuilder sb, Collection<Variable> variables, int results) throws ExpressionException {
if (expression instanceof Operation.Or) {
ArrayList<Expression> o = ((Operation.Or) expression).getExpressions();
for (Expression e : o)
addPrime(sb, e, variables, results);
} else
addPrime(sb, expression, variables, results);
}
private void addPrime(StringBuilder sb, Expression and, Collection<Variable> variables, int results) throws ExpressionException {
if (and instanceof Operation.And)
addAnd(sb, and, variables, results);
else if (and instanceof Variable)
addVar(sb, ((Variable) and).getIdentifier(), variables, results, false);
else if (and instanceof Not && ((Not) and).getExpression() instanceof Variable)
addVar(sb, ((Variable) (((Not) and).getExpression())).getIdentifier(), variables, results, true);
else
throw new ExpressionException("invalid expression");
}
private void addVar(StringBuilder sb, String identifier, Collection<Variable> variables, int results, boolean invert) {
for (Variable var : variables) {
if (var.getIdentifier().endsWith(identifier)) {
if (invert)
sb.append("0,");
else
sb.append("1,");
} else
sb.append("X,");
}
for (int i = 0; i < results; i++) {
if (i == number)
sb.append(",1");
else
sb.append(",0");
}
sb.append('\n');
}
private void addAnd(StringBuilder sb, Expression and, Collection<Variable> variables, int results) throws ExpressionException {
HashSet<String> v = new HashSet<>();
HashSet<String> nv = new HashSet<>();
if (and instanceof Operation.And) {
Operation.And a = (Operation.And) and;
for (Expression va : a.getExpressions()) {
Expression var = va;
HashSet<String> map = v;
if (var instanceof Not) {
map = nv;
var = ((Not) var).getExpression();
}
if (var instanceof Variable)
map.add(((Variable) var).getIdentifier());
else
throw new ExpressionException("invalid expression");
}
} else
throw new ExpressionException("invalid expression");
for (Variable var : variables) {
if (v.contains(var.getIdentifier()))
sb.append("1,");
else if (nv.contains(var.getIdentifier()))
sb.append("0,");
else
sb.append("X,");
}
for (int i = 0; i < results; i++) {
if (i == number)
sb.append(",1");
else
sb.append(",0");
}
sb.append('\n');
}
}
}

View File

@ -16,6 +16,8 @@ import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.format.FormatterException;
import de.neemann.digital.analyse.expression.modify.*;
import de.neemann.digital.analyse.format.TruthTableFormatter;
import de.neemann.digital.analyse.format.TruthTableFormatterCSV;
import de.neemann.digital.analyse.format.TruthTableFormatterHex;
import de.neemann.digital.analyse.format.TruthTableFormatterTestCase;
import de.neemann.digital.analyse.quinemc.BoolTableByteArray;
import de.neemann.digital.builder.ATF150x.ATFDevice;
@ -60,7 +62,9 @@ import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -373,6 +377,7 @@ public class TableDialog extends JDialog {
JFileChooser fc = new MyFileChooser();
if (TableDialog.this.filename != null)
fc.setSelectedFile(SaveAsHelper.checkSuffix(TableDialog.this.filename, "tru"));
fc.setFileFilter(new FileNameExtensionFilter(Lang.get("msg_truthTableCSV"), "csv"));
fc.setFileFilter(new FileNameExtensionFilter(Lang.get("msg_truthTable"), "tru"));
if (fc.showOpenDialog(TableDialog.this) == JFileChooser.APPROVE_OPTION) {
try {
@ -437,22 +442,29 @@ public class TableDialog extends JDialog {
}
}.setToolTip(Lang.get("menu_table_createFunctionFixture_tt")).createJMenuItem());
fileMenu.add(new ToolTipAction(Lang.get("menu_table_exportHex")) {
JMenu export = new JMenu(Lang.get("menu_export"));
fileMenu.add(export);
export.add(new FileExportActionConfirm(Lang.get("menu_table_exportHex"), "hex") {
@Override
public void actionPerformed(ActionEvent e) {
int res = JOptionPane.OK_OPTION;
if (undoManager.getActual().getVars().size() > 20)
res = JOptionPane.showConfirmDialog(TableDialog.this, Lang.get("msg_tableHasManyRowsConfirm"));
if (res == JOptionPane.OK_OPTION) {
JFileChooser fc = new MyFileChooser();
if (TableDialog.this.filename != null)
fc.setSelectedFile(SaveAsHelper.checkSuffix(TableDialog.this.filename, "hex"));
new SaveAsHelper(TableDialog.this, fc, "hex")
.checkOverwrite(file -> undoManager.getActual().saveHex(file));
}
protected String getString() throws ExpressionException {
return new TruthTableFormatterHex().format(undoManager.getActual());
}
}.setToolTip(Lang.get("menu_table_exportHex_tt")).createJMenuItem());
export.add(new FileExportAction(Lang.get("menu_table_exportCSVCondensed"), "csv") {
@Override
protected String getString() throws FormatterException, ExpressionException {
ExpressionListenerCSVCondensed expressionListener = new ExpressionListenerCSVCondensed();
lastGeneratedExpressions.replayTo(expressionListener);
expressionListener.close();
return expressionListener.toString();
}
}.setToolTip(Lang.get("menu_table_exportCSVCondensed_tt")).createJMenuItem());
export.add(new FileExportActionConfirm(Lang.get("menu_table_exportCSV"), "csv") {
@Override
protected String getString() throws ExpressionException {
return new TruthTableFormatterCSV().format(undoManager.getActual());
}
}.setToolTip(Lang.get("menu_table_exportCSV_tt")).createJMenuItem());
createJK = new JCheckBoxMenuItem(Lang.get("menu_table_JK"));
createJK.addActionListener(e -> calculateExpressions());
@ -998,4 +1010,55 @@ public class TableDialog extends JDialog {
abstract ExpressionListener createExpressionListener() throws ExpressionException;
}
private abstract class FileExportAction extends ToolTipAction {
private final String suffix;
private FileExportAction(String name, String suffix) {
super(name);
this.suffix = suffix;
}
@Override
public void actionPerformed(ActionEvent e) {
if (confirmExport()) {
JFileChooser fc = new MyFileChooser();
if (TableDialog.this.filename != null)
fc.setSelectedFile(SaveAsHelper.checkSuffix(TableDialog.this.filename, suffix));
new SaveAsHelper(TableDialog.this, fc, suffix)
.checkOverwrite(file -> {
try {
try (Writer w = new FileWriter(file)) {
w.write(getString());
}
} catch (FormatterException | ExpressionException ex) {
throw new IOException(ex);
}
});
}
}
protected boolean confirmExport() {
return true;
}
protected abstract String getString() throws FormatterException, ExpressionException;
}
private abstract class FileExportActionConfirm extends FileExportAction {
private FileExportActionConfirm(String name, String suffix) {
super(name, suffix);
}
@Override
protected boolean confirmExport() {
int res = JOptionPane.OK_OPTION;
if (undoManager.getActual().getVars().size() > 20)
res = JOptionPane.showConfirmDialog(TableDialog.this, Lang.get("msg_tableHasManyRowsConfirm"));
return res == JOptionPane.OK_OPTION;
}
}
}

View File

@ -184,7 +184,7 @@ public class Tokenizer {
boolean wasChar = true;
do {
c = readChar();
if (isNumberChar(c) || isHexChar(c) || c == 'x' || c == 'X') {
if (isNumberChar(c) || isHexChar(c) || c == 'x' || c == 'X' || c == ':' || c == '.') {
builder.append((char) c);
} else {
unreadChar(c);

View File

@ -1216,6 +1216,11 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="err_ROM_noFileGivenToLoad">Es ist kein Dateiname für das automatische Neuladen verfügbar!</string>
<string name="err_virtualSignal_N_DeclaredTwiceInLine_N">Virtuelles Signal {0} doppelt definiert in Zeile {1}!</string>
<string name="err_csvNoHeaderFound">Kein Header gefunden!</string>
<string name="err_csvNoOutputValuesFound">Keine Ausganswerte gefunden!</string>
<string name="err_csvNotEnoughValues">Nicht genug Werte in einer Zeile!</string>
<string name="err_csvToManyValues">Zu viel Werte in einer Zeile!</string>
<string name="key_AddrBits">Adress-Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
<string name="key_AddrBits_tt">Anzahl der Adress-Bits, die verwendet werden.</string>
<string name="key_Bits">Daten-Bits</string>
@ -1835,8 +1840,12 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
rein kombinatorisch ist!
</string>
<string name="menu_table_exportHex">Erzeuge HEX</string>
<string name="menu_table_exportHex">HEX</string>
<string name="menu_table_exportHex_tt">Die HEX-Datei kann in ein ROM oder eine LUT geladen werden.</string>
<string name="menu_table_exportCSV">CSV</string>
<string name="menu_table_exportCSV_tt">Eine CSV Datei, welche die komplette Warheitstabelle enthält.</string>
<string name="menu_table_exportCSVCondensed">CSV, Primimplikanten</string>
<string name="menu_table_exportCSVCondensed_tt">Eine CSV Datei, welche nur die Primimplikanten enthält.</string>
<string name="menu_table_new">Neu</string>
<string name="menu_table_new_combinatorial">Kombinatorisch</string>
<string name="menu_table_new_sequential">Automat</string>
@ -2059,6 +2068,7 @@ Stellen Sie sicher, dass der Flash-Vorgang abgeschlossen ist, bevor Sie diesen D
<string name="msg_testPassed_N">{0} Testzeilen überprüft</string>
<string name="msg_testFile">Testdatei</string>
<string name="msg_truthTable">Wahrheitstabelle</string>
<string name="msg_truthTableCSV">Comma Separated Values, CSV</string>
<string name="msg_errorImportingSvg">Fehler beim Import der SVG-Datei.</string>
<string name="msg_errorCreatingSvgTemplate">Fehler beim Erzeugen der SVG-Datei.</string>
<string name="msg_couldNotCreateStats">Statistik konnte nicht erzeugt werden.</string>

View File

@ -1203,6 +1203,11 @@
<string name="err_ROM_noFileGivenToLoad">There is no file name available for the automatic reload!</string>
<string name="err_virtualSignal_N_DeclaredTwiceInLine_N">Virtual signal {0} declared twice in line {1}!</string>
<string name="err_csvNoHeaderFound">No header found!</string>
<string name="err_csvNoOutputValuesFound">No output values found!</string>
<string name="err_csvNotEnoughValues">Not enough values in one line!</string>
<string name="err_csvToManyValues">Too many values in one line!</string>
<string name="key_AddrBits">Address Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
<string name="key_AddrBits_tt">Number of address bits used.</string>
<string name="key_Bits">Data Bits</string>
@ -1822,8 +1827,12 @@
purely combinatorial!
</string>
<string name="menu_table_exportHex">Export HEX</string>
<string name="menu_table_exportHex">HEX</string>
<string name="menu_table_exportHex_tt">You can load the HEX file to a ROM or a LUT.</string>
<string name="menu_table_exportCSV">CSV</string>
<string name="menu_table_exportCSV_tt">A CSV file containing the complete truth table.</string>
<string name="menu_table_exportCSVCondensed">CSV, prime implicants</string>
<string name="menu_table_exportCSVCondensed_tt">A CSV file containing only the prime implicants.</string>
<string name="menu_table_new">New</string>
<string name="menu_table_new_combinatorial">Combinatorial</string>
<string name="menu_table_new_sequential">Sequential</string>
@ -2054,6 +2063,7 @@
<string name="msg_testPassed_N">{0} test rows passed</string>
<string name="msg_testFile">File Tested</string>
<string name="msg_truthTable">Truth Table</string>
<string name="msg_truthTableCSV">Comma Separated Values, CSV</string>
<string name="msg_errorImportingSvg">Error while importing the SVG file.</string>
<string name="msg_errorCreatingSvgTemplate">Error creating the SVG template.</string>
<string name="msg_couldNotCreateStats">Statistics could not be created.</string>

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2021 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.analyse;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.quinemc.ThreeStateValue;
import de.neemann.digital.gui.components.table.ExpressionListenerCSVCondensed;
import junit.framework.TestCase;
import java.io.IOException;
import java.util.ArrayList;
public class CSVImporterTest extends TestCase {
public void testSimple() throws IOException {
TruthTable tt = CSVImporter.readCSV("A,B,,Y\n0,0,,0\n0,1,,0\n1,0,,0\n1,1,,1");
assertNotNull(tt);
ArrayList<Variable> vars = tt.getVars();
assertEquals(2, vars.size());
assertEquals("A", vars.get(0).getIdentifier());
assertEquals("B", vars.get(1).getIdentifier());
assertEquals(1, tt.getResultCount());
assertEquals("Y", tt.getResultName(0));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(0));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(1));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(2));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(3));
}
public void testDC() throws IOException {
TruthTable tt = CSVImporter.readCSV("A,B,,Y,X\nx,x,,1,0\n1,x,,0,1");
assertNotNull(tt);
ArrayList<Variable> vars = tt.getVars();
assertEquals(2, vars.size());
assertEquals("A", vars.get(0).getIdentifier());
assertEquals("B", vars.get(1).getIdentifier());
assertEquals(2, tt.getResultCount());
assertEquals("Y", tt.getResultName(0));
assertEquals("X", tt.getResultName(1));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(0));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(1));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(2));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(3));
assertEquals(ThreeStateValue.zero, tt.getResult(1).get(0));
assertEquals(ThreeStateValue.zero, tt.getResult(1).get(1));
assertEquals(ThreeStateValue.one, tt.getResult(1).get(2));
assertEquals(ThreeStateValue.one, tt.getResult(1).get(3));
}
public void testDC2() throws IOException {
TruthTable tt = CSVImporter.readCSV("A,B,C,,Y\nx,1,x,,1\n");
assertNotNull(tt);
ArrayList<Variable> vars = tt.getVars();
assertEquals(3, vars.size());
assertEquals("A", vars.get(0).getIdentifier());
assertEquals("B", vars.get(1).getIdentifier());
assertEquals("C", vars.get(2).getIdentifier());
assertEquals(1, tt.getResultCount());
assertEquals("Y", tt.getResultName(0));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(0));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(1));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(2));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(3));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(4));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(5));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(6));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(7));
}
public void testMultiplePrimeUsages() throws IOException {
TruthTable tt = CSVImporter.readCSV("A,B,,Y,X\n1,1,,1,1\n1,0,,0,1\n0,1,,0,1");
assertNotNull(tt);
ArrayList<Variable> vars = tt.getVars();
assertEquals(2, vars.size());
assertEquals("A", vars.get(0).getIdentifier());
assertEquals("B", vars.get(1).getIdentifier());
assertEquals(2, tt.getResultCount());
assertEquals("Y", tt.getResultName(0));
assertEquals("X", tt.getResultName(1));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(0));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(1));
assertEquals(ThreeStateValue.zero, tt.getResult(0).get(2));
assertEquals(ThreeStateValue.one, tt.getResult(0).get(3));
assertEquals(ThreeStateValue.zero, tt.getResult(1).get(0));
assertEquals(ThreeStateValue.one, tt.getResult(1).get(1));
assertEquals(ThreeStateValue.one, tt.getResult(1).get(2));
assertEquals(ThreeStateValue.one, tt.getResult(1).get(3));
}
public void testBug1() throws IOException {
TruthTable tt = CSVImporter.readCSV("A,B,,Y,X,Z\n1,1,,1,1,1\n1,0,,0,1,1\n0,1,,0,1,1");
assertNotNull(tt);
try {
CSVImporter.readCSV("A,B,,Y,X,Z\n1,1,,1,1,1\n1,0,,0,1,1,1\n0,1,,0,1,1");
fail();
} catch (IOException e) {
assertTrue(true);
}
}
public void testIncomplete() {
try {
CSVImporter.readCSV("\n\n");
fail();
} catch (IOException e) {
assertTrue(true);
}
try {
CSVImporter.readCSV("A,B,Y,X\n1,");
fail();
} catch (IOException e) {
assertTrue(true);
}
try {
CSVImporter.readCSV("A,B,Y,,\n1,");
fail();
} catch (IOException e) {
assertTrue(true);
}
try {
CSVImporter.readCSV("A,B,,Y,X\n1,");
fail();
} catch (IOException e) {
assertTrue(true);
}
try {
CSVImporter.readCSV("A,B,,Y,X\n1,1,1");
fail();
} catch (IOException e) {
assertTrue(true);
}
try {
CSVImporter.readCSV("A,B,,Y,X\n1,1,,1,1,1");
fail();
} catch (IOException e) {
assertTrue(true);
}
}
public void testAdder() throws Exception {
loopTest("A_0,A_1,B_0,B_1,,S_1,S_0,C\n" +
"1,0,1,0,,1,0,0\n" +
"1,1,1,1,,1,0,0\n" +
"0,0,X,1,,1,0,0\n" +
"0,1,X,0,,1,0,0\n" +
"X,0,0,1,,1,0,0\n" +
"X,1,0,0,,1,0,0\n" +
"0,X,1,X,,0,1,0\n" +
"1,X,0,X,,0,1,0\n" +
"1,X,1,1,,0,0,1\n" +
"1,1,1,X,,0,0,1\n" +
"X,1,X,1,,0,0,1\n");
}
public void testAndOr() throws Exception {
loopTest("A,B,,X,Y\n" +
"1,1,,1,0\n" +
"1,X,,0,1\n" +
"X,1,,0,1\n");
}
private void loopTest(String csv) throws Exception {
TruthTable tt = CSVImporter.readCSV(csv);
ExpressionListenerCSVCondensed elCSV = new ExpressionListenerCSVCondensed();
for (int i = 0; i < tt.getResultCount(); i++) {
MinimizerInterface mi = new MinimizerQuineMcCluskey();
mi.minimize(tt.getVars(), tt.getResult(i), tt.getResultName(i), elCSV);
}
elCSV.close();
assertEquals(csv, elCSV.toString());
}
}

View File

@ -38,52 +38,4 @@ public class TruthTableTest extends TestCase {
}
}
public void testHexExportSingle() throws Exception {
ArrayList<Variable> vars = Variable.vars(3);
TruthTable t = new TruthTable(vars).addResult();
BoolTableByteArray result = (BoolTableByteArray) t.getResult(0);
for (int i = 0; i < t.getRows(); i++) {
result.set(i, i % 2);
}
StringWriter w = new StringWriter();
t.saveHex(w);
w.close();
assertEquals("v2.0 raw\n" +
"0\n" +
"1\n" +
"0\n" +
"1\n" +
"0\n" +
"1\n" +
"0\n" +
"1\n", w.toString());
}
public void testHexExportTwo() throws Exception {
ArrayList<Variable> vars = Variable.vars(3);
TruthTable t = new TruthTable(vars).addResult();
BoolTableByteArray result = (BoolTableByteArray) t.getResult(0);
for (int i = 0; i < t.getRows(); i++) {
result.set(i, i % 2);
}
t.addResult();
result = (BoolTableByteArray) t.getResult(1);
for (int i = 0; i < t.getRows(); i++) {
result.set(i, (i + 1) % 2);
}
StringWriter w = new StringWriter();
t.saveHex(w);
w.close();
assertEquals("v2.0 raw\n" +
"2\n" +
"1\n" +
"2\n" +
"1\n" +
"2\n" +
"1\n" +
"2\n" +
"1\n", w.toString());
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2021 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.analyse.format;
import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.quinemc.BoolTableByteArray;
import junit.framework.TestCase;
public class TruthTableFormatterCSVTest extends TestCase {
public void testSimple() throws ExpressionException {
TruthTable tt = new TruthTable(2);
tt.addResult("X", new BoolTableByteArray(new byte[]{0, 1, 1, 1}));
tt.addResult("Y", new BoolTableByteArray(new byte[]{0, 0, 0, 1}));
String res = new TruthTableFormatterCSV().format(tt);
assertEquals("A,B,,X,Y\n" +
"0,0,,0,0\n" +
"0,1,,1,0\n" +
"1,0,,1,0\n" +
"1,1,,1,1\n", res);
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2021 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.analyse.format;
import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.quinemc.BoolTableByteArray;
import junit.framework.TestCase;
import java.io.StringWriter;
import java.util.ArrayList;
public class TruthTableFormatterHexTest extends TestCase {
public void testHexExportSingle() throws Exception {
ArrayList<Variable> vars = Variable.vars(3);
TruthTable t = new TruthTable(vars).addResult();
BoolTableByteArray result = (BoolTableByteArray) t.getResult(0);
for (int i = 0; i < t.getRows(); i++) {
result.set(i, i % 2);
}
String hex = new TruthTableFormatterHex().format(t);
assertEquals("v2.0 raw\n" +
"0\n" +
"1\n" +
"0\n" +
"1\n" +
"0\n" +
"1\n" +
"0\n" +
"1\n", hex);
}
public void testHexExportTwo() throws Exception {
ArrayList<Variable> vars = Variable.vars(3);
TruthTable t = new TruthTable(vars).addResult();
BoolTableByteArray result = (BoolTableByteArray) t.getResult(0);
for (int i = 0; i < t.getRows(); i++) {
result.set(i, i % 2);
}
t.addResult();
result = (BoolTableByteArray) t.getResult(1);
for (int i = 0; i < t.getRows(); i++) {
result.set(i, (i + 1) % 2);
}
String hex = new TruthTableFormatterHex().format(t);
assertEquals("v2.0 raw\n" +
"2\n" +
"1\n" +
"2\n" +
"1\n" +
"2\n" +
"1\n" +
"2\n" +
"1\n", hex);
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2021 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.gui.components.table;
import de.neemann.digital.analyse.CSVImporter;
import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.expression.*;
import de.neemann.digital.analyse.expression.format.FormatterException;
import de.neemann.digital.analyse.quinemc.ThreeStateValue;
import junit.framework.TestCase;
import java.io.IOException;
import java.util.ArrayList;
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;
public class ExpressionListenerCSVCondensedTest extends TestCase {
Variable a = new Variable("A");
Variable b = new Variable("B");
Variable c = new Variable("C");
public void testSimple() throws FormatterException, ExpressionException, IOException {
Expression expression = or(and(a, b), c);
assertEquals("A,B,C,,Y\n" +
"1,1,X,,1\n" +
"X,X,1,,1\n", create(expression));
}
public void testNot() throws FormatterException, ExpressionException, IOException {
Expression expression = or(and(a, not(b)), not(c));
assertEquals("A,B,C,,Y\n" +
"1,0,X,,1\n" +
"X,X,0,,1\n", create(expression));
}
public void testTwo() throws FormatterException, ExpressionException, IOException {
Expression e1 = new NamedExpression("Y", or(and(a, b), c));
Expression e2 = new NamedExpression("X", a);
assertEquals("A,B,C,,Y,X\n" +
"1,1,X,,1,0\n" +
"X,X,1,,1,0\n" +
"1,X,X,,0,1\n", create(e1, e2));
}
public void testXor() throws FormatterException, ExpressionException, IOException {
Expression expression = or(
and(not(a), not(b), c),
and(not(a), b, not(c)),
and(a, not(b), not(c)),
and(a, b, c)
);
assertEquals("A,B,C,,Y\n" +
"0,0,1,,1\n" +
"0,1,0,,1\n" +
"1,0,0,,1\n" +
"1,1,1,,1\n", create(expression));
}
private String create(Expression... expressions) throws FormatterException, ExpressionException, IOException {
ExpressionListenerCSVCondensed el = new ExpressionListenerCSVCondensed();
for (Expression e : expressions) {
String name = "Y";
if (e instanceof NamedExpression) {
NamedExpression ne = (NamedExpression) e;
name = ne.getName();
e = ne.getExpression();
}
el.resultFound(name, e);
}
el.close();
String s = el.toString();
loopCheck(s, expressions);
return s;
}
private void loopCheck(String csv, Expression[] expressions) throws IOException, ExpressionException {
TruthTable tt = CSVImporter.readCSV(csv);
for (int e = 0; e < expressions.length; e++) {
ArrayList<Variable> vars = tt.getVars();
int count = 1 << vars.size();
ContextFiller cf = new ContextFiller(vars);
for (int i = 0; i < count; i++) {
cf.setContextTo(i);
boolean expected = expressions[e].calculate(cf);
ThreeStateValue found = tt.getResult(e).get(i);
assertEquals(ThreeStateValue.value(expected), found);
}
}
}
}

View File

@ -40,8 +40,8 @@ public class TestExamples extends TestCase {
*/
public void testDistExamples() throws Exception {
File examples = new File(Resources.getRoot().getParentFile().getParentFile(), "/main/dig");
assertEquals(315, new FileScanner(this::check).scan(examples));
assertEquals(500, testCasesInFiles);
assertEquals(316, new FileScanner(this::check).scan(examples));
assertEquals(502, testCasesInFiles);
}
/**

View File

@ -1220,14 +1220,13 @@ public class TestInGUI extends TestCase {
xMin -= loc.x + SIZE * 5;
yMin -= loc.y + SIZE * 2;
boolean firstWire = true;
for (Wire w : circuit.getWires()) {
guiTester.mouseClickNow(w.p1.x - xMin, w.p1.y - yMin, InputEvent.BUTTON1_DOWN_MASK);
if (w.p1.x != w.p2.x && w.p1.y != w.p2.y)
guiTester.typeNow("typed d");
guiTester.mouseClickNow(w.p2.x - xMin, w.p2.y - yMin, InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(50);
guiTester.mouseClickNow(w.p2.x - xMin, w.p2.y - yMin, InputEvent.BUTTON3_DOWN_MASK);
if (firstWire) { // Draw first wire twice! Sometimes the first wire is lost and i can't figure out why!
firstWire = false; // This is a dirty hack!
drawWire(guiTester, xMin, yMin, w);
}
drawWire(guiTester, xMin, yMin, w);
}
for (VisualElement v : circuit.getElements()) {
@ -1240,6 +1239,16 @@ public class TestInGUI extends TestCase {
Thread.sleep(400);
}
}
private void drawWire(GuiTester guiTester, int xMin, int yMin, Wire w) throws InterruptedException {
guiTester.mouseClickNow(w.p1.x - xMin, w.p1.y - yMin, InputEvent.BUTTON1_DOWN_MASK);
if (w.p1.x != w.p2.x && w.p1.y != w.p2.y)
guiTester.typeNow("typed d");
guiTester.mouseClickNow(w.p2.x - xMin, w.p2.y - yMin, InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(50);
guiTester.mouseClickNow(w.p2.x - xMin, w.p2.y - yMin, InputEvent.BUTTON3_DOWN_MASK);
}
}
private static Point getCircuitPos(Main main) {

View File

@ -50,21 +50,27 @@ public class ParserTest extends TestCase {
}
public void testHex() throws IOException, ParserException {
Parser parser = new Parser("A B\n0 0xff").parse();
LineCollector td = new LineCollector(parser);
assertEquals(2, td.getNames().size());
assertEquals(1, td.getLines().size());
assertEquals(0, td.getLines().get(0).getValue(0).getValue());
assertEquals(Value.Type.NORMAL, td.getLines().get(0).getValue(0).getType());
assertEquals(255, td.getLines().get(0).getValue(1).getValue());
assertEquals(Value.Type.NORMAL, td.getLines().get(0).getValue(1).getType());
checkValueParser("A B\n0 0xff", 255);
}
public void testBin() throws IOException, ParserException {
Parser parser = new Parser("A B\n0 0b11111111").parse();
checkValueParser("A B\n0 0b11111111", 255);
}
public void testFixed() throws IOException, ParserException {
checkValueParser("A B\n0 0.5:4", 8);
}
public void testFloat() throws IOException, ParserException {
checkValueParser("A B\n0 0.5", Float.floatToIntBits(0.5f));
}
public void testDouble() throws IOException, ParserException {
checkValueParser("A B\n0 0.5d", Double.doubleToLongBits(0.5));
}
void checkValueParser(String s, long val) throws IOException, ParserException {
Parser parser = new Parser(s).parse();
LineCollector td = new LineCollector(parser);
assertEquals(2, td.getNames().size());
@ -73,7 +79,7 @@ public class ParserTest extends TestCase {
assertEquals(0, td.getLines().get(0).getValue(0).getValue());
assertEquals(Value.Type.NORMAL, td.getLines().get(0).getValue(0).getType());
assertEquals(255, td.getLines().get(0).getValue(1).getValue());
assertEquals(val, td.getLines().get(0).getValue(1).getValue());
assertEquals(Value.Type.NORMAL, td.getLines().get(0).getValue(1).getType());
}