/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution 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.
*
* logisim-evolution 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 logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.analyze.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TruthTable {
private class MyListener implements VariableListListener {
private void inputsChanged(VariableListEvent event) {
int action = event.getType();
if (action == VariableListEvent.ADD) {
for (Map.Entry<String, Entry[]> curEntry : outputColumns
.entrySet()) {
String output = curEntry.getKey();
Entry[] column = curEntry.getValue();
Entry[] newColumn = new Entry[2 * column.length];
for (int i = 0; i < column.length; i++) {
newColumn[2 * i] = column[i];
newColumn[2 * i + 1] = column[i];
}
outputColumns.put(output, newColumn);
}
} else if (action == VariableListEvent.REMOVE) {
int index = ((Integer) event.getData()).intValue();
for (Map.Entry<String, Entry[]> curEntry : outputColumns
.entrySet()) {
String output = curEntry.getKey();
Entry[] column = curEntry.getValue();
Entry[] newColumn = removeInput(column, index);
outputColumns.put(output, newColumn);
}
} else if (action == VariableListEvent.MOVE) {
int delta = ((Integer) event.getData()).intValue();
int newIndex = model.getInputs().indexOf(event.getVariable());
for (Map.Entry<String, Entry[]> curEntry : outputColumns
.entrySet()) {
String output = curEntry.getKey();
Entry[] column = curEntry.getValue();
Entry[] newColumn = moveInput(column, newIndex - delta,
newIndex);
outputColumns.put(output, newColumn);
}
}
}
public void listChanged(VariableListEvent event) {
if (event.getSource() == model.getInputs()) {
inputsChanged(event);
} else {
outputsChanged(event);
}
fireStructureChanged(event);
}
private Entry[] moveInput(Entry[] old, int oldIndex, int newIndex) {
int inputs = model.getInputs().size();
oldIndex = inputs - 1 - oldIndex;
newIndex = inputs - 1 - newIndex;
Entry[] ret = new Entry[old.length];
int sameMask = (old.length - 1)
^ ((1 << (1 + Math.max(oldIndex, newIndex))) - 1)
^ ((1 << Math.min(oldIndex, newIndex)) - 1); // bits that
// don't
// change
int moveMask = 1 << oldIndex; // bit that moves
int moveDist = Math.abs(newIndex - oldIndex);
boolean moveLeft = newIndex > oldIndex;
int blockMask = (old.length - 1) ^ sameMask ^ moveMask; // bits that
// move by
// one
for (int i = 0; i < old.length; i++) {
int j; // new index
if (moveLeft) {
j = (i & sameMask) | ((i & moveMask) << moveDist)
| ((i & blockMask) >> 1);
} else {
j = (i & sameMask) | ((i & moveMask) >> moveDist)
| ((i & blockMask) << 1);
}
ret[j] = old[i];
}
return ret;
}
private void outputsChanged(VariableListEvent event) {
int action = event.getType();
if (action == VariableListEvent.ALL_REPLACED) {
outputColumns.clear();
} else if (action == VariableListEvent.REMOVE) {
outputColumns.remove(event.getVariable());
} else if (action == VariableListEvent.REPLACE) {
Entry[] column = outputColumns.remove(event.getVariable());
if (column != null) {
int index = ((Integer) event.getData()).intValue();
String newVariable = model.getOutputs().get(index);
outputColumns.put(newVariable, column);
}
}
}
private Entry[] removeInput(Entry[] old, int index) {
int oldInputCount = model.getInputs().size() + 1;
Entry[] ret = new Entry[old.length / 2];
int j = 0;
int mask = 1 << (oldInputCount - 1 - index);
for (int i = 0; i < old.length; i++) {
if ((i & mask) == 0) {
Entry e0 = old[i];
Entry e1 = old[i | mask];
ret[j] = (e0 == e1 ? e0 : Entry.DONT_CARE);
j++;
}
}
return ret;
}
}
public static boolean isInputSet(int row, int column, int inputs) {
return ((row >> (inputs - 1 - column)) & 0x1) == 1;
}
private static final Entry DEFAULT_ENTRY = Entry.DONT_CARE;
private MyListener myListener = new MyListener();
private List<TruthTableListener> listeners = new ArrayList<TruthTableListener>();
private AnalyzerModel model;
private HashMap<String, Entry[]> outputColumns = new HashMap<String, Entry[]>();
public TruthTable(AnalyzerModel model) {
this.model = model;
model.getInputs().addVariableListListener(myListener);
model.getOutputs().addVariableListListener(myListener);
}
public void addTruthTableListener(TruthTableListener l) {
listeners.add(l);
}
private void fireCellsChanged(int column) {
TruthTableEvent event = new TruthTableEvent(this, column);
for (TruthTableListener l : listeners) {
l.cellsChanged(event);
}
}
private void fireStructureChanged(VariableListEvent cause) {
TruthTableEvent event = new TruthTableEvent(this, cause);
for (TruthTableListener l : listeners) {
l.structureChanged(event);
}
}
public int getInputColumnCount() {
return model.getInputs().size();
}
public Entry getInputEntry(int row, int column) {
int rows = getRowCount();
int inputs = model.getInputs().size();
if (row < 0 || row >= rows) {
throw new IllegalArgumentException("row index: " + row + " size: "
+ rows);
}
if (column < 0 || column >= inputs) {
throw new IllegalArgumentException("column index: " + column
+ " size: " + inputs);
}
return isInputSet(row, column, inputs) ? Entry.ONE : Entry.ZERO;
}
public String getInputHeader(int column) {
return model.getInputs().get(column);
}
public int getInputIndex(String input) {
return model.getInputs().indexOf(input);
}
public Entry[] getOutputColumn(int column) {
int outputs = model.getOutputs().size();
if (column < 0 || column >= outputs) {
throw new IllegalArgumentException("index: " + column + " size: "
+ outputs);
}
String outputName = model.getOutputs().get(column);
Entry[] columnData = outputColumns.get(outputName);
if (columnData == null) {
columnData = new Entry[getRowCount()];
Arrays.fill(columnData, DEFAULT_ENTRY);
outputColumns.put(outputName, columnData);
}
return columnData;
}
public int getOutputColumnCount() {
return model.getOutputs().size();
}
public Entry getOutputEntry(int row, int column) {
int outputs = model.getOutputs().size();
if (row < 0 || row >= getRowCount() || column < 0 || column >= outputs) {
return Entry.DONT_CARE;
} else {
String outputName = model.getOutputs().get(column);
Entry[] columnData = outputColumns.get(outputName);
if (columnData == null)
return DEFAULT_ENTRY;
if (row < 0 || row >= columnData.length)
return Entry.DONT_CARE;
return columnData[row];
}
}
public String getOutputHeader(int column) {
return model.getOutputs().get(column);
}
public int getOutputIndex(String output) {
return model.getOutputs().indexOf(output);
}
public int getRowCount() {
int sz = model.getInputs().size();
return 1 << sz;
}
public void removeTruthTableListener(TruthTableListener l) {
listeners.remove(l);
}
public void setOutputColumn(int column, Entry[] values) {
if (values != null && values.length != getRowCount()) {
throw new IllegalArgumentException(
"argument to setOutputColumn is wrong length");
}
int outputs = model.getOutputs().size();
if (column < 0 || column >= outputs) {
throw new IllegalArgumentException("index: " + column + " size: "
+ outputs);
}
String outputName = model.getOutputs().get(column);
Entry[] oldValues = outputColumns.get(outputName);
if (oldValues == values)
return;
else if (values == null)
outputColumns.remove(outputName);
else
outputColumns.put(outputName, values);
fireCellsChanged(column);
}
public void setOutputEntry(int row, int column, Entry value) {
int rows = getRowCount();
int outputs = model.getOutputs().size();
if (row < 0 || row >= rows) {
throw new IllegalArgumentException("row index: " + row + " size: "
+ rows);
}
if (column < 0 || column >= outputs) {
throw new IllegalArgumentException("column index: " + column
+ " size: " + outputs);
}
String outputName = model.getOutputs().get(column);
Entry[] columnData = outputColumns.get(outputName);
if (columnData == null) {
if (value == DEFAULT_ENTRY)
return;
columnData = new Entry[getRowCount()];
outputColumns.put(outputName, columnData);
Arrays.fill(columnData, DEFAULT_ENTRY);
columnData[row] = value;
} else {
if (columnData[row] == value)
return;
columnData[row] = value;
}
fireCellsChanged(column);
}
}