optimized model analyser.

The model is used to determine the variables on which an output depends.

Squashed commit of the following:

commit 45efbc50efbf21d242511359c1ba992ccefa7d3f
Author: hneemann <helmut.neemann@arcor.de>
Date:   Sat Jan 13 18:46:21 2018 +0100

    improved test case

commit 208dde9d94c358b76a540d813ac658c6cf34be32
Author: hneemann <helmut.neemann@arcor.de>
Date:   Fri Jan 12 21:39:28 2018 +0100

    added some more backtracking tests

commit 646e50f4e61c879ae7ea51d33c966486a0634691
Author: hneemann <helmut.neemann@arcor.de>
Date:   Fri Jan 12 19:28:23 2018 +0100

    minor changes, more tests are missing

commit 71ce4825051e20252284234029c8be012d510a1c
Author: hneemann <helmut.neemann@arcor.de>
Date:   Fri Jan 12 19:05:23 2018 +0100

    first attempt to optimize the model analysis using backtracking
This commit is contained in:
hneemann 2018-01-15 08:21:52 +01:00
parent a158f6ffe3
commit 0c1714c38c
10 changed files with 701 additions and 144 deletions

View File

@ -0,0 +1,76 @@
package de.neemann.digital.analyse;
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.core.Bits;
import de.neemann.digital.core.Signal;
import java.util.ArrayList;
/**
* Creates a bool table which represents an expression which does not depend on all variables.
*/
public class BoolTableExpanded implements BoolTable {
private final BoolTableByteArray e;
private final ArrayList<Variable> vars;
private final int[] bitsToRemove;
private final int bitRemoveCount;
private final int size;
/**
* Creates a new instance
*
* @param e the values
* @param inputs the variables the expression relay depends on
* @param originalInputs all variables
*/
public BoolTableExpanded(BoolTableByteArray e, ArrayList<Signal> inputs, ArrayList<Signal> originalInputs) {
this.e = e;
bitRemoveCount = originalInputs.size() - inputs.size();
bitsToRemove = new int[bitRemoveCount];
size = 1 << originalInputs.size();
int bit = 0;
int c = 0;
for (int i = originalInputs.size() - 1; i >= 0; i--) {
Signal s = originalInputs.get(i);
if (!inputs.contains(s)) {
bitsToRemove[c] = bit;
c++;
}
bit++;
}
vars = new ArrayList<>();
for (Signal s : inputs)
vars.add(new Variable(s.getName()));
}
@Override
public int size() {
return size;
}
@Override
public ThreeStateValue get(int i) {
for (int b = bitRemoveCount - 1; b >= 0; b--)
i = Bits.removeBitFromValue(i, bitsToRemove[b]);
return e.get(i);
}
/**
* @return the bool table
*/
public BoolTableByteArray getBoolTable() {
return e;
}
/**
* @return the variables
*/
public ArrayList<Variable> getVars() {
return vars;
}
}

View File

@ -48,6 +48,21 @@ public class DependencyAnalyser {
return list;
}
/**
* Returns the number of model steps needed to analyse the model.
*
* @param modelAnalyser the model analyser
* @return the number of required steps
*/
public long getRequiredSteps(ModelAnalyser modelAnalyser) {
long num = 0;
for (Signal o : modelAnalyser.getOutputs()) {
int n = getInputs(o).size();
num += (1L << n);
}
return num;
}
private void backtracking(ObservableValue value, Set<ObservableValue> effected) throws PinException, BacktrackException {
if (!effected.contains(value)) {
effected.add(value);

View File

@ -33,7 +33,6 @@ public class ModelAnalyser {
private final Model model;
private final ArrayList<Signal> inputs;
private final ArrayList<Signal> outputs;
private final int rows;
private int uniqueIndex = 0;
/**
@ -95,12 +94,8 @@ public class ModelAnalyser {
if (inputs.size() == 0)
throw new AnalyseException(Lang.get("err_analyseNoInputs"));
if (inputs.size() > MAX_INPUTS_ALLOWED)
throw new AnalyseException(Lang.get("err_toManyInputs_max_N0_is_N1", MAX_INPUTS_ALLOWED, inputs.size()));
if (outputs.size() == 0)
throw new AnalyseException(Lang.get("err_analyseNoOutputs"));
rows = 1 << inputs.size();
LOGGER.debug("table has " + rows + " rows");
}
private String createUniqueName(FlipflopD ff) {
@ -125,6 +120,63 @@ public class ModelAnalyser {
throw new AnalyseException(Lang.get("err_varName_N_UsedTwice", signals.get(i).getName()));
}
private ArrayList<Signal> checkBinaryOutputs(ArrayList<Signal> list) throws AnalyseException {
ArrayList<Signal> outputs = new ArrayList<>();
for (Signal s : list) {
final int bits = s.getValue().getBits();
if (bits == 1)
outputs.add(s);
else {
try {
Splitter sp = Splitter.createOneToN(bits);
sp.setInputs(s.getValue().asList());
int i = 0;
for (ObservableValue out : sp.getOutputs()) {
outputs.add(new Signal(s.getName() + i, out));
i++;
}
s.getValue().fireHasChanged();
} catch (NodeException e) {
throw new AnalyseException(e);
}
}
}
return outputs;
}
private ArrayList<Signal> checkBinaryInputs(ArrayList<Signal> list) throws AnalyseException {
ArrayList<Signal> inputs = new ArrayList<>();
for (Signal s : list) {
final int bits = s.getValue().getBits();
if (bits == 1)
inputs.add(s);
else {
try {
Splitter sp = Splitter.createNToOne(bits);
final ObservableValue out = sp.getOutputs().get(0);
out.addObserver(new NodeWithoutDelay(s.getValue()) {
@Override
public void hasChanged() {
s.getValue().setValue(out.getValue());
}
});
out.fireHasChanged();
ObservableValues.Builder builder = new ObservableValues.Builder();
for (int i = 0; i < bits; i++) {
ObservableValue o = new ObservableValue(s.getName() + i, 1);
builder.add(o);
inputs.add(new Signal(s.getName() + i, o));
}
sp.setInputs(builder.build());
} catch (NodeException e) {
throw new AnalyseException(e);
}
}
}
return inputs;
}
private void checkClock(Node node) throws AnalyseException {
if (!getClock().hasObserver(node))
throw new AnalyseException(Lang.get("err_ffNeedsToBeConnectedToClock"));
@ -192,115 +244,6 @@ public class ModelAnalyser {
return out;
}
private ArrayList<Signal> checkBinaryOutputs(ArrayList<Signal> list) throws AnalyseException {
ArrayList<Signal> outputs = new ArrayList<>();
for (Signal s : list) {
final int bits = s.getValue().getBits();
if (bits == 1)
outputs.add(s);
else {
try {
Splitter sp = Splitter.createOneToN(bits);
sp.setInputs(s.getValue().asList());
int i = 0;
for (ObservableValue out : sp.getOutputs()) {
outputs.add(new Signal(s.getName() + i, out));
i++;
}
s.getValue().fireHasChanged();
} catch (NodeException e) {
throw new AnalyseException(e);
}
}
}
return outputs;
}
private ArrayList<Signal> checkBinaryInputs(ArrayList<Signal> list) throws AnalyseException {
ArrayList<Signal> inputs = new ArrayList<>();
for (Signal s : list) {
final int bits = s.getValue().getBits();
if (bits == 1)
inputs.add(s);
else {
try {
Splitter sp = Splitter.createNToOne(bits);
final ObservableValue out = sp.getOutputs().get(0);
out.addObserver(new NodeWithoutDelay(s.getValue()) {
@Override
public void hasChanged() {
s.getValue().setValue(out.getValue());
}
});
out.fireHasChanged();
ObservableValues.Builder builder = new ObservableValues.Builder();
for (int i = 0; i < bits; i++) {
ObservableValue o = new ObservableValue(s.getName() + i, 1);
builder.add(o);
inputs.add(new Signal(s.getName() + i, o));
}
sp.setInputs(builder.build());
} catch (NodeException e) {
throw new AnalyseException(e);
}
}
}
return inputs;
}
/**
* Analyses the circuit
*
* @return the generated truth table
* @throws NodeException NodeException
* @throws PinException PinException
* @throws BacktrackException BacktrackException
*/
public TruthTable analyse() throws NodeException, PinException, BacktrackException {
LOGGER.debug("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) {
inputs.get(bit).getValue().setBool(value);
}
};
TruthTable tt = new TruthTable().setPinsWithoutNumber(model.getPinsWithoutNumber());
for (Signal s : inputs)
tt.addVariable(s.getName());
for (Signal s : inputs)
tt.addPinNumber(s);
for (Signal s : outputs)
tt.addPinNumber(s);
if (model.getClocks().size() == 1)
tt.setClockPin(model.getClocks().get(0).getClockPin());
ArrayList<BoolTableByteArray> data = new ArrayList<>();
for (Signal s : outputs) {
BoolTableByteArray e = new BoolTableByteArray(rows);
data.add(e);
tt.addResult(s.getName(), e);
}
model.init();
for (int row = 0; row < rows; row++) {
bitsetter.fill(row);
model.doStep();
for (int i = 0; i < outputs.size(); i++) {
data.get(i).set(row, outputs.get(i).getValue().getBool());
}
}
time = System.currentTimeMillis() - time;
LOGGER.debug("model analysis: " + time / 1000.0 + " sec");
return tt;
}
private void replaceJKFF() throws NodeException, AnalyseException {
List<FlipflopJK> jkList = model.findNode(FlipflopJK.class);
@ -374,4 +317,114 @@ public class ModelAnalyser {
public ArrayList<Signal> getOutputs() {
return outputs;
}
/**
* Analyses the circuit
*
* @return the generated truth table
* @throws NodeException NodeException
* @throws PinException PinException
* @throws BacktrackException BacktrackException
* @throws AnalyseException AnalyseException
*/
public TruthTable analyse() throws NodeException, PinException, BacktrackException, AnalyseException {
LOGGER.debug("start to analyse the model...");
TruthTable tt = new TruthTable().setPinsWithoutNumber(model.getPinsWithoutNumber());
for (Signal s : inputs)
tt.addVariable(s.getName());
for (Signal s : inputs)
tt.addPinNumber(s);
for (Signal s : outputs)
tt.addPinNumber(s);
if (model.getClocks().size() == 1)
tt.setClockPin(model.getClocks().get(0).getClockPin());
DependencyAnalyser da = new DependencyAnalyser(this);
long steps = da.getRequiredSteps(this);
long tableRows = 1L << inputs.size();
LOGGER.debug("analyse speedup: " + tableRows + " rows vs " + steps + " steps, speedup " + ((double) tableRows) / steps);
long time = System.currentTimeMillis();
if (tableRows <= steps || tableRows <= 128)
simpleFiller(tt);
else
dependantFiller(tt, da);
time = System.currentTimeMillis() - time;
LOGGER.debug("model analysis: " + time / 1000.0 + " sec");
return tt;
}
private void simpleFiller(TruthTable tt) throws NodeException, AnalyseException {
if (inputs.size() > MAX_INPUTS_ALLOWED)
throw new AnalyseException(Lang.get("err_toManyInputs_max_N0_is_N1", MAX_INPUTS_ALLOWED, inputs.size()));
BitSetter bitsetter = new BitSetter(inputs.size()) {
@Override
public void setBit(int row, int bit, boolean value) {
inputs.get(bit).getValue().setBool(value);
}
};
int rows = 1 << inputs.size();
ArrayList<BoolTableByteArray> data = new ArrayList<>();
for (Signal s : outputs) {
BoolTableByteArray e = new BoolTableByteArray(rows);
data.add(e);
tt.addResult(s.getName(), e);
}
model.init();
for (int row = 0; row < rows; row++) {
bitsetter.fill(row);
model.doStep();
for (int i = 0; i < outputs.size(); i++) {
data.get(i).set(row, outputs.get(i).getValue().getBool());
}
}
}
private void dependantFiller(TruthTable tt, DependencyAnalyser da) throws NodeException, AnalyseException {
model.init();
for (Signal out : outputs) {
ArrayList<Signal> ins = reorder(da.getInputs(out), inputs);
if (ins.size() > MAX_INPUTS_ALLOWED)
throw new AnalyseException(Lang.get("err_toManyInputs_max_N0_is_N1", MAX_INPUTS_ALLOWED, ins.size()));
int rows = 1 << ins.size();
BoolTableByteArray e = new BoolTableByteArray(rows);
BitSetter bitsetter = new BitSetter(ins.size()) {
@Override
public void setBit(int row, int bit, boolean value) {
ins.get(bit).getValue().setBool(value);
}
};
for (int row = 0; row < rows; row++) {
bitsetter.fill(row);
model.doStep();
e.set(row, out.getValue().getBool());
}
tt.addResult(out.getName(), new BoolTableExpanded(e, ins, inputs));
}
}
private ArrayList<Signal> reorder(ArrayList<Signal> ins, ArrayList<Signal> originalOrder) {
ArrayList<Signal> newList = new ArrayList<>();
for (Signal i : originalOrder)
if (ins.contains(i))
newList.add(i);
return newList;
}
}

View File

@ -403,6 +403,19 @@ public class TruthTable {
return results.get(result).getValues();
}
/**
* Returns the result with the given name
*
* @param resultName the result name
* @return the table representing the result or null if not found
*/
public BoolTable getResult(String resultName) {
for (Result r : results)
if (r.getName().equals(resultName))
return r.getValues();
return null;
}
/**
* Returns the results name
*

View File

@ -1,5 +1,6 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.BoolTableExpanded;
import de.neemann.digital.analyse.expression.Variable;
import java.util.ArrayList;
@ -29,12 +30,30 @@ public class TableReducer {
}
/**
* Returns true if there are reduce variables
* Returns true if it is possible to reduce variables
*
* @return true is reduction was possible
*/
public boolean canReduce() {
if (table instanceof BoolTableExpanded) {
BoolTableExpanded t = (BoolTableExpanded) table;
vars = t.getVars();
table = t.getBoolTable();
canReduceOnlyCheckTable();
return true;
} else
return canReduceOnlyCheckTable();
}
/**
* Returns true if it is possible to reduce variables
* Only used for tests!!!
*
* @return true is reduction was possible
*/
public boolean canReduceOnlyCheckTable() {
boolean isReduced = false;
Iterator<Variable> it = vars.iterator();
int var = 0;
while (it.hasNext()) {

View File

@ -104,6 +104,22 @@ public final class Bits {
return outBits;
}
/**
* Removes a bit from a value.
* This means it shifts the higher bits down. Behaves like removing an item from a list.
*
* @param value the value
* @param bit the bit to remove
* @return the new value
*/
public static int removeBitFromValue(int value, int bit) {
if (bit > 0) {
return ((value & (~((1 << (bit + 1)) - 1))) >>> 1) | (value & ((1 << bit) - 1));
} else {
return value >>> 1;
}
}
/**
* Decodes a string to a long.
* Supports decimal, octal, hex, binary and ascii

View File

@ -0,0 +1,110 @@
package de.neemann.digital.analyse;
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.TableReducer;
import de.neemann.digital.analyse.quinemc.ThreeStateValue;
import de.neemann.digital.core.Signal;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.List;
public class BoolTableExpandedTest extends TestCase {
public void testRegression() {
checkTable(new Signals("a", "b").list());
checkTable(new Signals("a", "c").list());
checkTable(new Signals("a", "d").list());
checkTable(new Signals("b", "c").list());
checkTable(new Signals("b", "d").list());
checkTable(new Signals("c", "d").list());
}
private void checkTable(ArrayList<Signal> in1) {
ArrayList<Signal> in2 = new Signals("a", "b", "c", "d").list();
List<Variable> vars = new Vars("a", "b", "c", "d").list();
check(new BoolTableByteArray(new byte[]{1, 1, 0, 1}), in1, in2, vars);
check(new BoolTableByteArray(new byte[]{0, 1, 1, 0}), in1, in2, vars);
check(new BoolTableByteArray(new byte[]{1, 0, 0, 1}), in1, in2, vars);
check(new BoolTableByteArray(new byte[]{0, 0, 0, 1}), in1, in2, vars);
check(new BoolTableByteArray(new byte[]{0, 1, 1, 1}), in1, in2, vars);
}
private void check(BoolTableByteArray e, ArrayList<Signal> in1, ArrayList<Signal> in2, List<Variable> vars) {
BoolTableExpanded bt = new BoolTableExpanded(e, in1, in2);
TableReducer tr = new TableReducer(vars, bt);
assertTrue(tr.canReduceOnlyCheckTable());
List<Variable> v = tr.getVars();
assertEquals(in1.size(), v.size());
for (int i = 0; i < v.size(); i++)
assertEquals(in1.get(i).getName(), v.get(i).getIdentifier());
BoolTable t1 = tr.getTable();
assertEquals(e.size(), t1.size());
for (int r = 0; r < e.size(); r++)
assertEquals(e.get(r), t1.get(r));
}
public void testCombined() {
ArrayList<Signal> in1 = new Signals("b", "c").list();
ArrayList<Signal> in2 = new Signals("a", "b", "c", "d").list();
List<Variable> vars = new Vars("a", "b", "c", "d").list();
BoolTableExpanded bt = new BoolTableExpanded(new BoolTableByteArray(new byte[]{1, 1, 0, 0}), in1, in2);
TableReducer tr = new TableReducer(vars, bt);
assertTrue(tr.canReduce());
List<Variable> v = tr.getVars();
assertEquals(1, v.size());
assertEquals("b", v.get(0).getIdentifier());
BoolTable t1 = tr.getTable();
assertEquals(ThreeStateValue.one, t1.get(0));
assertEquals(ThreeStateValue.zero, t1.get(1));
}
private static class ListBuilder<A, B> {
private final ArrayList<A> list;
private Factory<A, B> factory;
ListBuilder(Factory<A, B> factory) {
this.factory = factory;
list = new ArrayList<>();
}
ListBuilder(Factory<A, B> factory, B[] bs) {
this(factory);
for (B b : bs)
add(b);
}
public ListBuilder<A, B> add(B b) {
list.add(factory.make(b));
return this;
}
public ArrayList<A> list() {
return list;
}
}
public class Signals extends ListBuilder<Signal, String> {
Signals(String... s) {
super((a) -> new Signal(a, null), s);
}
}
public class Vars extends ListBuilder<Variable, String> {
Vars(String... s) {
super(Variable::new, s);
}
}
private interface Factory<A, B> {
A make(B b);
}
}

View File

@ -1,10 +1,15 @@
package de.neemann.digital.analyse;
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.core.Model;
import de.neemann.digital.integration.ToBreakRunner;
import junit.framework.TestCase;
import java.util.ArrayList;
import static de.neemann.digital.analyse.quinemc.ThreeStateValue.one;
import static de.neemann.digital.analyse.quinemc.ThreeStateValue.zero;
@ -103,21 +108,8 @@ public class ModelAnalyserTest extends TestCase {
public void testAnalyzerMultiBit() throws Exception {
Model model = new ToBreakRunner("dig/analyze/multiBitCounter.dig", false).getModel();
TruthTable tt = new ModelAnalyser(model).analyse();
assertEquals("Q0n+1", tt.getResultName(1));
final BoolTable r0 = tt.getResult(1);
assertEquals(4, r0.size());
assertEquals(one, r0.get(0));
assertEquals(zero, r0.get(1));
assertEquals(one, r0.get(2));
assertEquals(zero, r0.get(3));
assertEquals("Q1n+1", tt.getResultName(0));
final BoolTable r1 = tt.getResult(0);
assertEquals(4, r1.size());
assertEquals(zero, r1.get(0));
assertEquals(one, r1.get(1));
assertEquals(one, r1.get(2));
assertEquals(zero, r1.get(3));
checkTable(tt.getResult("Q0n+1"), one, zero, one, zero);
checkTable(tt.getResult("Q1n+1"), zero, one, one, zero);
assertEquals("Y0", tt.getResultName(2));
assertEquals("Y1", tt.getResultName(3));
@ -144,21 +136,50 @@ public class ModelAnalyserTest extends TestCase {
}
private void checkIdent(TruthTable tt) {
assertEquals("B0", tt.getResultName(0));
final BoolTable r0 = tt.getResult(0);
assertEquals(4, r0.size());
assertEquals(zero, r0.get(0));
assertEquals(zero, r0.get(1));
assertEquals(one, r0.get(2));
assertEquals(one, r0.get(3));
checkTable(tt.getResult("B0"), zero, zero, one, one);
checkTable(tt.getResult("B1"), zero, one, zero, one);
}
assertEquals("B1", tt.getResultName(1));
final BoolTable r1 = tt.getResult(1);
assertEquals(4, r1.size());
assertEquals(zero, r1.get(0));
assertEquals(one, r1.get(1));
assertEquals(zero, r1.get(2));
assertEquals(one, r1.get(3));
private void checkTable(BoolTable table, ThreeStateValue... expected) {
assertNotNull("result not found", table);
assertEquals("wrong table size", expected.length, table.size());
for (int i = 0; i < expected.length; i++)
assertEquals("wrong value " + i, expected[i], table.get(i));
}
public void testAnalyzerBacktrack() throws Exception {
Model model = new ToBreakRunner("dig/analyze/analyzeBacktrack.dig", false).getModel();
TruthTable tt = new ModelAnalyser(model).analyse();
final BoolTable Y1 = tt.getResult("1Y");
checkRemaining(Y1, "1A", "1B");
checkTable(getInner(Y1), zero, one, one, zero);
final BoolTable Y2 = tt.getResult("2Y");
checkRemaining(Y2, "2A", "2B");
checkTable(getInner(Y2), one, zero, zero, one);
final BoolTable Y3 = tt.getResult("3Y");
checkRemaining(Y3, "3A", "3B");
checkTable(getInner(Y3), zero, one, one, one);
final BoolTable Y4 = tt.getResult("4Y");
checkRemaining(Y4, "4A", "4B", "4C");
checkTable(getInner(Y4), zero, zero, zero, zero, zero, zero, zero, one);
}
private BoolTableByteArray getInner(BoolTable table) {
assertTrue(table instanceof BoolTableExpanded);
return ((BoolTableExpanded) table).getBoolTable();
}
private void checkRemaining(BoolTable table, String... vars) {
assertTrue(table instanceof BoolTableExpanded);
ArrayList<Variable> v = ((BoolTableExpanded) table).getVars();
assertEquals(vars.length, v.size());
for (int i = 0; i < vars.length; i++)
assertEquals(vars[i], v.get(i).getIdentifier());
}
}

View File

@ -109,4 +109,21 @@ public class BitsTest extends TestCase {
}
}
public void testRemoveBitFromValue() {
assertEquals(0b111, Bits.removeBitFromValue(0b1110, 0));
assertEquals(0b111, Bits.removeBitFromValue(0b1111, 0));
assertEquals(0b101, Bits.removeBitFromValue(0b1010, 0));
assertEquals(0b101, Bits.removeBitFromValue(0b1011, 0));
assertEquals(0b111, Bits.removeBitFromValue(0b1101, 1));
assertEquals(0b111, Bits.removeBitFromValue(0b1111, 1));
assertEquals(0b101, Bits.removeBitFromValue(0b1001, 1));
assertEquals(0b101, Bits.removeBitFromValue(0b1011, 1));
assertEquals(0b110, Bits.removeBitFromValue(0b1100, 1));
assertEquals(0b110, Bits.removeBitFromValue(0b1110, 1));
assertEquals(0b111, Bits.removeBitFromValue(0b1111, 2));
assertEquals(0b000, Bits.removeBitFromValue(0b0100, 2));
assertEquals(0b010, Bits.removeBitFromValue(0b0110, 2));
assertEquals(0b100, Bits.removeBitFromValue(0b1100, 2));
}
}

View File

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="utf-8"?>
<circuit>
<version>1</version>
<attributes/>
<visualElements>
<visualElement>
<elementName>XOr</elementName>
<elementAttributes/>
<pos x="440" y="100"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>1A</string>
</entry>
</elementAttributes>
<pos x="420" y="100"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>1B</string>
</entry>
</elementAttributes>
<pos x="420" y="140"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>1Y</string>
</entry>
</elementAttributes>
<pos x="540" y="120"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>2A</string>
</entry>
</elementAttributes>
<pos x="420" y="180"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>2B</string>
</entry>
</elementAttributes>
<pos x="420" y="220"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>2Y</string>
</entry>
</elementAttributes>
<pos x="540" y="200"/>
</visualElement>
<visualElement>
<elementName>XNOr</elementName>
<elementAttributes/>
<pos x="440" y="180"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>3A</string>
</entry>
</elementAttributes>
<pos x="420" y="260"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>3B</string>
</entry>
</elementAttributes>
<pos x="420" y="300"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>3Y</string>
</entry>
</elementAttributes>
<pos x="540" y="280"/>
</visualElement>
<visualElement>
<elementName>Or</elementName>
<elementAttributes/>
<pos x="440" y="260"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4A</string>
</entry>
</elementAttributes>
<pos x="420" y="340"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4B</string>
</entry>
</elementAttributes>
<pos x="340" y="360"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4Y</string>
</entry>
</elementAttributes>
<pos x="540" y="360"/>
</visualElement>
<visualElement>
<elementName>And</elementName>
<elementAttributes>
<entry>
<string>Inputs</string>
<int>3</int>
</entry>
</elementAttributes>
<pos x="440" y="340"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4C</string>
</entry>
</elementAttributes>
<pos x="420" y="380"/>
</visualElement>
</visualElements>
<wires>
<wire>
<p1 x="420" y="100"/>
<p2 x="440" y="100"/>
</wire>
<wire>
<p1 x="420" y="180"/>
<p2 x="440" y="180"/>
</wire>
<wire>
<p1 x="420" y="260"/>
<p2 x="440" y="260"/>
</wire>
<wire>
<p1 x="420" y="340"/>
<p2 x="440" y="340"/>
</wire>
<wire>
<p1 x="500" y="120"/>
<p2 x="540" y="120"/>
</wire>
<wire>
<p1 x="520" y="200"/>
<p2 x="540" y="200"/>
</wire>
<wire>
<p1 x="500" y="280"/>
<p2 x="540" y="280"/>
</wire>
<wire>
<p1 x="500" y="360"/>
<p2 x="540" y="360"/>
</wire>
<wire>
<p1 x="340" y="360"/>
<p2 x="440" y="360"/>
</wire>
<wire>
<p1 x="420" y="140"/>
<p2 x="440" y="140"/>
</wire>
<wire>
<p1 x="420" y="220"/>
<p2 x="440" y="220"/>
</wire>
<wire>
<p1 x="420" y="300"/>
<p2 x="440" y="300"/>
</wire>
<wire>
<p1 x="420" y="380"/>
<p2 x="440" y="380"/>
</wire>
</wires>
<measurementOrdering/>
</circuit>