/******************************************************************************* * 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.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; public class Implicant implements Comparable<Implicant> { private static class TermIterator implements Iterable<Implicant>, Iterator<Implicant> { Implicant source; int currentMask = 0; TermIterator(Implicant source) { this.source = source; } public boolean hasNext() { return currentMask >= 0; } public Iterator<Implicant> iterator() { return this; } public Implicant next() { int ret = currentMask | source.values; int diffs = currentMask ^ source.unknowns; int diff = diffs ^ ((diffs - 1) & diffs); if (diff == 0) { currentMask = -1; } else { currentMask = (currentMask & ~(diff - 1)) | diff; } return new Implicant(0, ret); } public void remove() { } } static List<Implicant> computeMinimal(int format, AnalyzerModel model, String variable) { TruthTable table = model.getTruthTable(); int column = model.getOutputs().indexOf(variable); if (column < 0) return Collections.emptyList(); Entry desired = format == AnalyzerModel.FORMAT_SUM_OF_PRODUCTS ? Entry.ONE : Entry.ZERO; Entry undesired = desired == Entry.ONE ? Entry.ZERO : Entry.ONE; // determine the first-cut implicants, as well as the rows // that we need to cover. HashMap<Implicant, Entry> base = new HashMap<Implicant, Entry>(); HashSet<Implicant> toCover = new HashSet<Implicant>(); boolean knownFound = false; for (int i = 0; i < table.getRowCount(); i++) { Entry entry = table.getOutputEntry(i, column); if (entry == undesired) { knownFound = true; } else if (entry == desired) { knownFound = true; Implicant imp = new Implicant(0, i); base.put(imp, entry); toCover.add(imp); } else { Implicant imp = new Implicant(0, i); base.put(imp, entry); } } if (!knownFound) return null; // work up to more general implicants, discovering // any prime implicants. HashSet<Implicant> primes = new HashSet<Implicant>(); HashMap<Implicant, Entry> current = base; while (current.size() > 1) { HashSet<Implicant> toRemove = new HashSet<Implicant>(); HashMap<Implicant, Entry> next = new HashMap<Implicant, Entry>(); for (Map.Entry<Implicant, Entry> curEntry : current.entrySet()) { Implicant imp = curEntry.getKey(); Entry detEntry = curEntry.getValue(); for (int j = 1; j <= imp.values; j *= 2) { if ((imp.values & j) != 0) { Implicant opp = new Implicant(imp.unknowns, imp.values ^ j); Entry oppEntry = current.get(opp); if (oppEntry != null) { toRemove.add(imp); toRemove.add(opp); Implicant i = new Implicant(opp.unknowns | j, opp.values); Entry e; if (oppEntry == Entry.DONT_CARE && detEntry == Entry.DONT_CARE) { e = Entry.DONT_CARE; } else { e = desired; } next.put(i, e); } } } } for (Map.Entry<Implicant, Entry> curEntry : current.entrySet()) { Implicant det = curEntry.getKey(); if (!toRemove.contains(det) && curEntry.getValue() == desired) { primes.add(det); } } current = next; } // we won't have more than one implicant left, but it // is probably prime. for (Map.Entry<Implicant, Entry> curEntry : current.entrySet()) { Implicant imp = curEntry.getKey(); if (current.get(imp) == desired) { primes.add(imp); } } // determine the essential prime implicants HashSet<Implicant> retSet = new HashSet<Implicant>(); HashSet<Implicant> covered = new HashSet<Implicant>(); for (Implicant required : toCover) { if (covered.contains(required)) continue; int row = required.getRow(); Implicant essential = null; for (Implicant imp : primes) { if ((row & ~imp.unknowns) == imp.values) { if (essential == null) essential = imp; else { essential = null; break; } } } if (essential != null) { retSet.add(essential); primes.remove(essential); for (Implicant imp : essential.getTerms()) { covered.add(imp); } } } toCover.removeAll(covered); // This is an unusual case, but it's possible that the // essential prime implicants don't cover everything. // In that case, greedily pick out prime implicants // that cover the most uncovered rows. while (!toCover.isEmpty()) { // find the implicant covering the most rows Implicant max = null; int maxCount = 0; int maxUnknowns = Integer.MAX_VALUE; for (Iterator<Implicant> it = primes.iterator(); it.hasNext();) { Implicant imp = it.next(); int count = 0; for (Implicant term : imp.getTerms()) { if (toCover.contains(term)) ++count; } if (count == 0) { it.remove(); } else if (count > maxCount) { max = imp; maxCount = count; maxUnknowns = imp.getUnknownCount(); } else if (count == maxCount) { int unk = imp.getUnknownCount(); if (unk > maxUnknowns) { max = imp; maxUnknowns = unk; } } } // add it to our choice, and remove the covered rows if (max != null) { retSet.add(max); primes.remove(max); for (Implicant term : max.getTerms()) { toCover.remove(term); } } } // Now build up our sum-of-products expression // from the remaining terms ArrayList<Implicant> ret = new ArrayList<Implicant>(retSet); Collections.sort(ret); return ret; } static Expression toExpression(int format, AnalyzerModel model, List<Implicant> implicants) { if (implicants == null) return null; TruthTable table = model.getTruthTable(); if (format == AnalyzerModel.FORMAT_PRODUCT_OF_SUMS) { Expression product = null; for (Implicant imp : implicants) { product = Expressions.and(product, imp.toSum(table)); } return product == null ? Expressions.constant(1) : product; } else { Expression sum = null; for (Implicant imp : implicants) { sum = Expressions.or(sum, imp.toProduct(table)); } return sum == null ? Expressions.constant(0) : sum; } } static Implicant MINIMAL_IMPLICANT = new Implicant(0, -1); static List<Implicant> MINIMAL_LIST = Arrays .asList(new Implicant[] { MINIMAL_IMPLICANT }); private int unknowns; private int values; private Implicant(int unknowns, int values) { this.unknowns = unknowns; this.values = values; } public int compareTo(Implicant o) { if (this.values < o.values) return -1; if (this.values > o.values) return 1; if (this.unknowns < o.unknowns) return -1; if (this.unknowns > o.unknowns) return 1; return 0; } @Override public boolean equals(Object other) { if (!(other instanceof Implicant)) return false; Implicant o = (Implicant) other; return this.unknowns == o.unknowns && this.values == o.values; } public int getRow() { if (unknowns != 0) return -1; return values; } public Iterable<Implicant> getTerms() { return new TermIterator(this); } public int getUnknownCount() { int ret = 0; int n = unknowns; while (n != 0) { n &= (n - 1); ret++; } return ret; } @Override public int hashCode() { return (unknowns << 16) | values; } private Expression toProduct(TruthTable source) { Expression term = null; int cols = source.getInputColumnCount(); for (int i = cols - 1; i >= 0; i--) { if ((unknowns & (1 << i)) == 0) { Expression literal = Expressions.variable(source .getInputHeader(cols - 1 - i)); if ((values & (1 << i)) == 0) literal = Expressions.not(literal); term = Expressions.and(term, literal); } } return term == null ? Expressions.constant(1) : term; } private Expression toSum(TruthTable source) { Expression term = null; int cols = source.getInputColumnCount(); for (int i = cols - 1; i >= 0; i--) { if ((unknowns & (1 << i)) == 0) { Expression literal = Expressions.variable(source .getInputHeader(cols - 1 - i)); if ((values & (1 << i)) != 0) literal = Expressions.not(literal); term = Expressions.or(term, literal); } } return term == null ? Expressions.constant(1) : term; } }