/*
* funCKit - functional Circuit Kit
* Copyright (C) 2013 Lukas Elsner <open@mindrunner.de>
* Copyright (C) 2013 Peter Dahlberg <catdog2@tuxzone.org>
* Copyright (C) 2013 Julian Stier <mail@julian-stier.de>
* Copyright (C) 2013 Sebastian Vetter <mail@b4sti.eu>
* Copyright (C) 2013 Thomas Poxrucker <poxrucker_t@web.de>
* Copyright (C) 2013 Alexander Treml <alex.treml@directbox.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.sep2011.funckit.util;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import de.sep2011.funckit.converter.sepformat.SEPFormatImportException;
import de.sep2011.funckit.model.graphmodel.Brick;
import de.sep2011.funckit.model.graphmodel.Circuit;
import de.sep2011.funckit.model.graphmodel.Component;
import de.sep2011.funckit.model.graphmodel.Element;
import de.sep2011.funckit.model.graphmodel.Switch;
import de.sep2011.funckit.model.graphmodel.implementations.Light;
import de.sep2011.funckit.model.simulationmodel.LightSimulationState;
import de.sep2011.funckit.model.simulationmodel.Simulation;
import de.sep2011.funckit.model.simulationmodel.SimulationBrick;
import de.sep2011.funckit.model.simulationmodel.SimulationImpl;
import de.sep2011.funckit.model.simulationmodel.SwitchSimulationState;
import de.sep2011.funckit.util.GraphmodelUtil.VerticalThanHorizontalOrderComparator;
import de.sep2011.funckit.validator.LoopCheck;
/**
*
*/
public class BehaviouralCircuitComparator {
private final Circuit referenceCircuit;
private final Circuit circuitToCompare;
private List<Switch> referenceSwitchList;
private List<Switch> toCompareSwitchList;
private List<Light> referenceLightList;
private List<Light> toCompareLightList;
private Simulation referenceSimulation;
private Simulation toCompareSimulation;
private static final int STUPID_SIMULATION_STEPS = 100;
private boolean passed;
public static void main(String[] args) throws SEPFormatImportException, FileNotFoundException {
// Circuit circuit = new SEPFormatConverter("", Mode.FUNCKITFORMAT)
// .doImport(new FileInputStream("/home/peter/zeugs/funCKit/workspace/funCKit/src/test/resources/example-d-FlipFlop/example-dflipflop.fck"));
//
// Circuit circuit2 = new SEPFormatConverter("", Mode.FUNCKITFORMAT)
// .doImport(new FileInputStream("/home/peter/zeugs/funCKit/workspace/funCKit/src/test/resources/example-d-FlipFlop/example-dflipflop.fck"));
//
// Log.gl().debug("Passed: " + new BehaviouralCircuitComparator(circuit, circuit2, null).isPassed());
}
public BehaviouralCircuitComparator(Circuit referenceCircuit, Circuit circuitToCompare,
List<List<Boolean>> simRows) {
this.referenceCircuit = referenceCircuit;
this.circuitToCompare = circuitToCompare;
referenceSwitchList = new ArrayList<Switch>();
toCompareSwitchList = new ArrayList<Switch>();
referenceLightList = new ArrayList<Light>();
toCompareLightList = new ArrayList<Light>();
checkSwitchAndLightPositionsAndFillList();
Collections.sort(referenceSwitchList, new VerticalThanHorizontalOrderComparator());
Collections.sort(toCompareSwitchList, new VerticalThanHorizontalOrderComparator());
Collections.sort(referenceLightList, new VerticalThanHorizontalOrderComparator());
Collections.sort(toCompareLightList, new VerticalThanHorizontalOrderComparator());
initSimulation();
boolean referenceIsCombinatorial = new LoopCheck().perform(referenceCircuit).isPassed();
boolean toCompareIsCombinatorial = new LoopCheck().perform(circuitToCompare).isPassed();
if (referenceIsCombinatorial && toCompareIsCombinatorial) {
passed = compareCombinatorial();
} else if (!referenceIsCombinatorial && !toCompareIsCombinatorial) {
if(simRows == null) {
passed = compareStupid();
} else {
passed = compareSimlist(simRows);
}
} else {
passed = false;
}
}
private void initSimulation() {
referenceSimulation = new SimulationImpl(referenceCircuit);
toCompareSimulation = new SimulationImpl(circuitToCompare);
}
/**
* Start Comparation of a Combinatorial Circuit.
*/
private boolean compareCombinatorial() {
Log.gl().debug("Comparing two combinatorial circuits");
long maxDelayRef = GraphmodelUtil
.getTotalDelayOfCombinatorialCircuit(referenceCircuit);
long maxDelayComp = GraphmodelUtil
.getTotalDelayOfCombinatorialCircuit(circuitToCompare);
for (long i = 0; i < (1l << referenceSwitchList.size()); i++) {
setConfiguration(referenceSimulation, referenceSwitchList, i);
setConfiguration(toCompareSimulation, toCompareSwitchList, i);
runSimulation(referenceSimulation, maxDelayRef + 1);
runSimulation(toCompareSimulation, maxDelayComp + 1);
if(!compareLights()) {
return false;
}
}
return true;
}
/**
* Compare the lights between the reference and the other circuit
*
* @return if minimum one light is not right
*/
private boolean compareLights() {
for (int i = 0; i < referenceLightList.size(); i++) {
SimulationBrick simLampRef = new SimulationBrick(
referenceLightList.get(i), new LinkedList<Component>());
SimulationBrick simLampComp = new SimulationBrick(
toCompareLightList.get(i), new LinkedList<Component>());
LightSimulationState lightStateRef = (LightSimulationState) referenceSimulation
.getSimulationState(simLampRef);
LightSimulationState lightStateComp = (LightSimulationState) toCompareSimulation
.getSimulationState(simLampComp);
boolean valueRef = lightStateRef.getValue(referenceLightList.get(i).getInputA());
boolean valueComp = lightStateComp.getValue(toCompareLightList.get(i).getInputA());
if(valueRef != valueComp) {
return false;
}
}
return true;
}
/**
* run the specified Simulation.
*
* @param simulation
* @param steps repeat specified number of steps
*/
private void runSimulation(Simulation simulation, long steps) {
for (int i = 0; i < steps; i++) {
simulation.nextStep();
}
}
private boolean compareSimlist(List<List<Boolean>> simrows) {
// TODO: check simrows
Log.gl().debug("Comparing with simassign");
if(simrows.isEmpty()) {
Log.gl().warn("no test rows in simassign! Will always return true");
}
for (List<Boolean> row : simrows) {
Log.gl().debug("Testing row: " + row);
setConfigurationToRow(referenceSimulation, referenceSwitchList, row);
setConfigurationToRow(toCompareSimulation, toCompareSwitchList, row);
runSimulation(referenceSimulation, 1);
runSimulation(toCompareSimulation, 1);
if (!compareLights()) {
return false;
}
}
return true;
}
/**
* Stupid method: set a configuration and simulate STUPID_SIMULATION_STEPS
* steps on fresh simulation. Then the same on one simulation.
*/
private boolean compareStupid() {
Log.gl().debug("Comparing using the stupid method");
for (long i = 0; i < (1l << referenceSwitchList.size()); i++) {
initSimulation();
setConfiguration(referenceSimulation, referenceSwitchList, i);
setConfiguration(toCompareSimulation, toCompareSwitchList, i);
runSimulation(referenceSimulation, STUPID_SIMULATION_STEPS);
runSimulation(toCompareSimulation, STUPID_SIMULATION_STEPS);
if(!compareLights()) {
return false;
}
}
initSimulation();
for (long i = 0; i < (1l << referenceSwitchList.size()); i++) {
setConfiguration(referenceSimulation, referenceSwitchList, i);
setConfiguration(toCompareSimulation, toCompareSwitchList, i);
runSimulation(referenceSimulation, STUPID_SIMULATION_STEPS);
runSimulation(toCompareSimulation, STUPID_SIMULATION_STEPS);
if(!compareLights()) {
return false;
}
}
return true;
}
public static List<List<Boolean>> convertSimlistToSimRows(InputStream simlist) {
List<List<Boolean>> simrows = new ArrayList<List<Boolean>>();
BufferedReader r = new BufferedReader(new InputStreamReader(simlist));
try {
for (String line = r.readLine(); line != null; line = r.readLine()) {
String[] insplit = line.split("\\s+");
List<Boolean> inlist = new ArrayList<Boolean>();
boolean error = false;
for (String str : insplit) {
if(!("1".equals(str) || "0".equals(str))) {
error = true;
break;
}
int value = Integer.parseInt(str);
inlist.add(value != 0);
}
if(!error) {
simrows.add(inlist);
} else {
Log.gl().debug("line >>" + line + "<< is a comment or has an error, do not add");
}
}
} catch (IOException e) {
return null;
}
return simrows;
}
private void setConfigurationToRow(Simulation simulation, List<Switch> switches,
List<Boolean> inputValues) {
for (int i = 0; i < inputValues.size(); i++) {
SimulationBrick simSwitch = new SimulationBrick(switches.get(i),
new LinkedList<Component>());
SwitchSimulationState switchState = (SwitchSimulationState) simulation
.getSimulationState(simSwitch);
switchState.setValue(inputValues.get(i));
}
}
/**
* Set the configuration of the switches which represent number
*
* @param simulation
* @param switches
* @param number
*/
private void setConfiguration(Simulation simulation, List<Switch> switches,
long number) {
long convnum = number;
for (int pos = switches.size() - 1; pos >= 0; pos--) {
SimulationBrick simSwitch = new SimulationBrick(switches.get(pos),
new LinkedList<Component>());
SwitchSimulationState switchState = (SwitchSimulationState) simulation
.getSimulationState(simSwitch);
switchState.setValue((convnum % 2) != 0);
convnum /= 2;
}
}
/**
* Check if the circuit to compare has switches and lights on the same
* position as the reference
*
* @param circuitToCompare
* @return
*/
private void checkSwitchAndLightPositionsAndFillList() {
for (Element element : referenceCircuit.getElements()) {
if (element instanceof Switch) {
Brick brick2 = GraphmodelUtil.findBrickAtSamePos(circuitToCompare, element
.getPosition());
if (brick2 instanceof Switch) {
referenceSwitchList.add((Switch) element);
toCompareSwitchList.add((Switch) brick2);
} else {
throw new AssertionError("Switches not at same position");
}
} else if (element instanceof Light) {
Brick brick2 = GraphmodelUtil.findBrickAtSamePos(circuitToCompare, element
.getPosition());
if (brick2 instanceof Light) {
referenceLightList.add((Light) element);
toCompareLightList.add((Light) brick2);
} else {
throw new AssertionError("Lights not at same position");
}
}
}
}
public boolean isPassed() {
return passed;
}
}