/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: EquivRecord.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) 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.
*
* Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
// updated to new view of trees, 16 Jan 2004
// Annotated by Ivan Sutherland, 30 January 2004
package com.sun.electric.tool.ncc.trees;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.sun.electric.tool.ncc.NccGlobals;
import com.sun.electric.tool.ncc.lists.LeafList;
import com.sun.electric.tool.ncc.lists.RecordList;
import com.sun.electric.tool.ncc.netlist.NetObject;
import com.sun.electric.tool.ncc.processing.LocalPartitionWires;
import com.sun.electric.tool.ncc.result.EquivRecReport.EquivRecReportable;
import com.sun.electric.tool.ncc.result.NetObjReport.NetObjReportable;
import com.sun.electric.tool.ncc.strategy.Strategy;
import com.sun.electric.tool.Job;
/** Leaf EquivRecords hold Circuits. Internal EquivRecords hold offspring.
* Every EquivRecord is assigned a pseudo random code at birth which it
* retains for life.
* <p>
* A Leaf EquivRecord is "balanced" if all Circuits have the same number of
* NetObjects. "Matched" means balanced with each Circuit having one
* NetObject. "Mismatched" means unbalanced with some some Circuit having
* no NetObject. "Active" means not matched and not mismatched. */
public class EquivRecord implements EquivRecReportable {
/** Get all NetObjects contained by an EquivRecord subtree.
* The NetObjects found in matched EquivRecords are collected
* separately from NetObjects found in not-matched EquivRecords. */
private static class GetNetObjs extends Strategy {
private int numDesigns = -1;
// private NetObject.Type type = null;
private final List<List<NetObject>> matches =
new ArrayList<List<NetObject>>();
private final List<List<NetObject>> notMatches =
new ArrayList<List<NetObject>>();
// private void checkType(NetObject n) {
// NetObject.Type t = n.getNetObjType();
// if (type==null) type=t;
// LayoutLib.error(type!=t, "different types in same EquivRecord");
// LayoutLib.error(type!=NetObject.Type.PART &&
// type!=NetObject.Type.WIRE,
// "expecting only Parts or Wires");
// }
private void appendNetObjsFromCircuit(List<List<NetObject>> lists,
EquivRecord er) {
int i=0;
for (Iterator<Circuit> itC=er.getCircuits(); itC.hasNext(); i++) {
Circuit ckt = (Circuit) itC.next();
for (Iterator<NetObject> itN=ckt.getNetObjs(); itN.hasNext();) {
lists.get(i).add(itN.next());
}
}
Job.error(i!=numDesigns, "wrong number of circuits");
}
@Override public LeafList doFor(EquivRecord er) {
if (er.isLeaf()) {
if (numDesigns==-1) {
// We've encountered the first leaf EquivRec. Now we
// can initialize the Lists.
numDesigns = er.numCircuits();
for (int i=0; i<numDesigns; i++) {
matches.add(new ArrayList<NetObject>());
notMatches.add(new ArrayList<NetObject>());
}
}
appendNetObjsFromCircuit(er.isMatched() ? matches : notMatches, er);
return new LeafList();
} else {
return super.doFor(er);
}
}
// ------------------------- intended interface -----------------------
public GetNetObjs(EquivRecord er) {
super(null);
doFor(er);
}
/** @return one list per Circuit. Each list contains all the matched
* NetObjects in that circuit. For all these lists, elements at the
* same index matched. */
public List<List<NetObject>> getMatchedNetObjs() {return matches;}
/** @return one list per Circuit. Each list contains all not-matched
* NetObjects in that circuit. */
public List<List<NetObject>> getNotMatchedNetObjs() {
return notMatches;
}
//public boolean hasParts() {return type==NetObject.Type.PART;}
}
// points toward root
private EquivRecord parent;
// the immutable random code
private int randCode;
// int that distinguished this Record
private int value;
// comment describing the metric used to partition my parent into my
// siblings and me
private String partitionReason;
// description of Wire PortInst connections
private LocalPartitionWires.Signature wireSignature;
// At any given time only one of this lists is non-null
private RecordList offspring;
private List<Circuit> circuits;
private static void error(boolean pred, String msg) {
Job.error(pred, msg);
}
/** For a leaf record, apply strategy to build one Map for each Circuit
* @param js
* @return list of Maps */
private ArrayList<HashMap<Integer,List<NetObject>>> getOneMapPerCircuit(Strategy js) {
ArrayList<HashMap<Integer,List<NetObject>>> mapPerCkt = new ArrayList<HashMap<Integer,List<NetObject>>>();
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit ckt = it.next();
HashMap<Integer,List<NetObject>> codeToNetObjs = js.doFor(ckt);
mapPerCkt.add(codeToNetObjs);
}
return mapPerCkt;
}
/** Get all the keys of all the maps.
* @param mapPerCkt list of maps
* @return the set of all keys from all the maps */
private Set<Integer> getKeysFromAllMaps(ArrayList<HashMap<Integer,List<NetObject>>> mapPerCkt) {
Set<Integer> keys = new HashSet<Integer>();
for (HashMap<Integer,List<NetObject>> map : mapPerCkt) {
keys.addAll(map.keySet());
}
return keys;
}
/** Create a leaf record for all the Circuits corresponding to a
* given key. If a map has a list of NetObjects for the given key
* then create a Circuit containing those NetObjects.
* Otherwise, add an empty Circuit to the EquivRecord.
* @param mapPerCkt ArrayList of maps. Each map maps:
* (Integer -> ArrayList of NetObjects)
* @param key check each map for a List of NetObjects at this key
* @return a EquivRecord */
private EquivRecord makeEquivRecForKey(ArrayList<HashMap<Integer,List<NetObject>>> mapPerCkt, Integer key, NccGlobals globals) {
List<Circuit> ckts = new ArrayList<Circuit>();
for (HashMap<Integer,List<NetObject>> map : mapPerCkt) {
ArrayList<NetObject> netObjs = (ArrayList<NetObject>) map.get(key);
if (netObjs==null) netObjs = new ArrayList<NetObject>();
ckts.add(Circuit.please(netObjs));
}
return EquivRecord.newLeafRecord(key.intValue(), ckts, globals);
}
/** constructor*/
private EquivRecord(){}
private void addOffspring(EquivRecord r) {
offspring.add(r);
r.setParent(this);
}
private LeafList applyToLeaf(Strategy js) {
ArrayList<HashMap<Integer,List<NetObject>>> mapPerCkt = getOneMapPerCircuit(js);
Set<Integer> keys = getKeysFromAllMaps(mapPerCkt);
error(keys.size()==0, "must have at least one key");
// If everything maps to one hash code then no offspring
if (keys.size()==1) return new LeafList();
// Change this record from leaf to internal
circuits = null;
offspring = new RecordList();
for (Integer key : keys) {
EquivRecord er = makeEquivRecForKey(mapPerCkt, key, js.globals);
addOffspring(er);
}
LeafList el = new LeafList();
el.addAll(offspring);
return el;
}
private LeafList applyToInternal(Strategy js) {
LeafList offspring = new LeafList();
for (Iterator<EquivRecord> it=getOffspring(); it.hasNext();) {
EquivRecord jr= it.next();
offspring.addAll(js.doFor(jr));
}
return offspring;
}
// --------------------------- public methods -----------------------------
/** @return internal EquivRecord that contains me */
public EquivRecord getParent() {return parent;}
/** getCode returns the fixed hash code for this object.
* @return the int fixed hash code for this object. */
public int getCode(){return isMismatched()? 0 : randCode;}
/** Sanity check */
public void checkMe(EquivRecord parent) {
error(getParent()!=parent, "wrong parent");
error(!(offspring==null ^ circuits==null), "bad lists");
}
/** Set my parent to be x */
public void setParent(EquivRecord x) {parent=x;}
/** get the value that a strategy used to distinguish
* this EquivRecord.
* @return the int value that distinguished this EquivRecord */
public int getValue(){return value;}
/** @return an iterator over all the circuits belonging to EquivRecord */
public Iterator<Circuit> getCircuits() {return circuits.iterator();}
/** @return the number of circuits belonging to EquivRecord */
public int numCircuits() {return circuits.size();}
/** Add a new Circuit to EquivRecord */
public void addCircuit(Circuit c) {
circuits.add(c);
c.setParent(this);
}
/** say whether this leaf record contains Parts, Wires, or Ports.
* A leaf record can only hold one kind of NetObject.
* @return PART, WIRE, or PORT */
public NetObject.Type getNetObjType() {
for (Iterator<Circuit> ci=getCircuits(); ci.hasNext();) {
Circuit c = ci.next();
Iterator<NetObject> ni = c.getNetObjs();
if (ni.hasNext()) {
NetObject no = ni.next();
return no.getNetObjType();
}
}
error(true, "no NetObjects in a leaf EquivRecord?");
return null;
}
/** Get total number of NetObjects in all Circuits of a leaf record
* @return number of NetObjects */
public int numNetObjs() {
int sum = 0;
for (Iterator<Circuit> ci=getCircuits(); ci.hasNext();) {
Circuit c = ci.next();
sum += c.numNetObjs();
}
return sum;
}
/** generates a String indicating the size of the
* Circuits in this leaf record
* @return the String */
public String sizeString() {
if(numCircuits() == 0) return "0";
String s= "";
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit jc= it.next();
s= s + " " + jc.numNetObjs() ;
}
return s;
}
/** maxSizeDiff computes the difference in the number of
* NetObjects in the Circuits of this leaf record.
* @return an int with the difference, zero is good */
public int maxSizeDiff() {
int out= 0;
int max= maxSize();
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit j= it.next();
int diff= max-j.numNetObjs();
if(diff > out)out= diff;
}
return out;
}
/** maxSize returns the number of NetObjects in the most populous
* Circuit.
* @return an int with the maximum size of any Circuit in this
* leaf record */
public int maxSize() {
int out= 0;
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit j= it.next();;
out = Math.max(out, j.numNetObjs());
}
return out;
}
/** isActive indicates that this leaf record is neither matched
* nor mismatched.
* @return true if this leaf record is still in play, false otherwise */
public boolean isActive() {
error(numCircuits()==0, "leaf record with no circuits?");
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit c = it.next();
if (c.numNetObjs()==0) return false; // mismatched
if (c.numNetObjs()>1) return true; // live
}
return false;
}
/** @return true if all Circuits have same number of NetObjects. */
public boolean isBalanced() {
boolean first = true;
int sz = 0;
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit c = it.next();
if (first) {
sz = c.numNetObjs();
first = false;
} else {
if (c.numNetObjs()!=sz) return false;
}
}
return true;
}
/** isMatched is a special case of balanced.
* @return true if every Circuit has one NetObject */
public boolean isMatched() {
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit c = it.next();
if (c.numNetObjs()!=1) return false;
}
return true;
}
/** isMismatched is a special case of unbalanced.
* @return true if some Circuit has no NetObject */
public boolean isMismatched() {
// It's impossible for all Circuits to be zero sized so we only
// need to find the first zero sized.
for (Iterator<Circuit> it=getCircuits(); it.hasNext();) {
Circuit c = it.next();
if (c.numNetObjs()==0) return true;
}
return false;
}
/** get offspring of internal record */
public Iterator<EquivRecord> getOffspring() {return offspring.iterator();}
public int numOffspring() {return offspring.size();}
/** The apply method applies a Strategy to this leaf EquivRecord. If the
* divides this Record then this leaf record becomes an internal record.
* @param js the Strategy to apply
* @return a LeafList of the resulting offspring */
public LeafList apply(Strategy js) {
return isLeaf() ? applyToLeaf(js) : applyToInternal(js);
}
/** nameString returns a String of type and name for this parent.
* @return a String identifying this EquivRecord. */
public String nameString() {
String name = "";
if (isLeaf()) {
name = isMatched() ? "Matched" : isMismatched() ? "Mismatched" : "Active";
name += " leaf";
} else {
name = "Internal";
}
name += " Record randCode="+randCode+" value="+value;
name += isLeaf() ? (
" maxSize="+maxSize()
) : (
" #offspring="+numOffspring()
);
return name;
}
/** @return true if EquivRecord is a leaf of the EquivRec tree */
public boolean isLeaf() {return offspring==null;}
/** The fixed strategies annotate EquivRecords with comments
* describing what characteristic made this EquivRecord unique.
* This information is useful for providing pre-analysis information
* to the user. */
public void setPartitionReason(String s) {partitionReason=s;}
public String getPartitionReason() {return partitionReason;}
public List<String> getPartitionReasonsFromRootToMe() {
if (wireSignature!=null) {
return wireSignature.getReasons();
} else {
LinkedList<String> reasons = new LinkedList<String>();
for (EquivRecord r=this; r!=null; r=r.getParent()) {
String reason = r.getPartitionReason();
if (reason!=null) reasons.addFirst(reason);
}
return reasons;
}
}
public void setWireSignature(LocalPartitionWires.Signature sig) {
wireSignature = sig;
}
/** Construct a leaf EquivRecord that holds circuits
* @param ckts Circuits belonging to Equivalence Record
* @param globals used for generating random numbers
* @return the new EquivRecord */
public static EquivRecord newLeafRecord(int key, List<Circuit> ckts, NccGlobals globals) {
EquivRecord r = new EquivRecord();
r.circuits = new ArrayList<Circuit>();
r.value = key;
r.randCode = globals.getRandom();
for (Circuit ckt : ckts) {
r.addCircuit(ckt);
}
error(r.maxSize()==0,
"invalid leaf EquivRecord: all Circuits are empty");
return r;
}
/** Construct an internal EquivRecord that will serve as the root of the
* EquivRecord tree
* @param offspring
* @return the new EquivRecord or null if there are no offspring */
public static EquivRecord newRootRecord(List<EquivRecord> offspring) {
if (offspring.size()==0) return null;
EquivRecord r = new EquivRecord();
r.offspring = new RecordList();
for (EquivRecord er : offspring) {
r.addOffspring(er);
}
return r;
}
/** Get all NetObjects contained by an EquivRecord subtree.
* @param matched list of list of NetObjects from matched EquivRecords
* indexed as: [circuitIndex][netObjectIndex]. NetObjects at the same
* index in each list match.
* @param notMatched list of list of NetObjects from not matched
* EquivRecords indexed as [circuitIndex][netObjectIndex] */
public void getNetObjsFromEntireTree(List<List<NetObject>> matched,
List<List<NetObject>> notMatched) {
GetNetObjs gno = new GetNetObjs(this);
matched.clear();
matched.addAll(gno.getMatchedNetObjs());
notMatched.clear();
notMatched.addAll(gno.getNotMatchedNetObjs());
}
private List<List<NetObjReportable>> coerceToReportable(List<List<NetObject>> no) {
List<List<NetObjReportable>> nor = new ArrayList<List<NetObjReportable>>();
for (List<NetObject> i : no) {
List<NetObjReportable> j = new ArrayList<NetObjReportable>();
j.addAll(i);
nor.add(j);
}
return nor;
}
public void getNetObjReportablesFromEntireTree(
List<List<NetObjReportable>> matched,
List<List<NetObjReportable>> notMatched) {
List<List<NetObject>> m = new ArrayList<List<NetObject>>();
List<List<NetObject>> nm = new ArrayList<List<NetObject>>();
getNetObjsFromEntireTree(m, nm);
matched.clear();
matched.addAll(coerceToReportable(m));
notMatched.clear();
notMatched.addAll(coerceToReportable(nm));
}
}