/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: CellTree.java
* Written by: Dmitry Nadezhin, Sun Microsystems.
*
* Copyright (c) 2005 Sun Microsystems and Static Free Software
*
* 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.
*/
package com.sun.electric.database;
import com.sun.electric.database.geometry.DBMath;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.id.CellUsage;
import com.sun.electric.database.text.ImmutableArrayList;
import com.sun.electric.technology.TechPool;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Set;
/**
* CellTree consists of top CellBackup and all CellTrees for all subcells.
* It can compute Cell bounds and shape of Exports.
*/
public class CellTree {
public static final CellTree[] NULL_ARRAY = {};
public static final ImmutableArrayList<CellTree> EMPTY_LIST = new ImmutableArrayList<CellTree>(NULL_ARRAY);
/**
* top CellBackup
*/
public final CellBackup top;
final CellTree[] subTrees;
/**
* TechPool containing all Technologies used in this CellTree
*/
public final TechPool techPool;
/**
* All cells used in this CellTree
*/
public final Set<CellId> allCells;
private ERectangle bounds;
private EquivPorts equivPorts;
private CellTree(CellBackup top, CellTree[] subTrees, TechPool techPool, Set<CellId> allCells) {
this.top = top;
this.subTrees = subTrees;
this.techPool = techPool;
this.allCells = allCells;
}
public static CellTree newInstance(ImmutableCell d, TechPool techPool) {
CellBackup top = CellBackup.newInstance(d, techPool);
assert top.cellRevision.cellUsages.length == 0;
return new CellTree(top, NULL_ARRAY, top.techPool, Collections.singleton(top.cellRevision.d.cellId));
}
/**
* Returns CellTree which differs from this CellTree.
* @param top new top CellBackup
* @param subTrees new subCellTrees
* @param superPool TechPool which contains
* @return CellTree which differs from this CellTree.
*/
public CellTree with(CellBackup top, CellTree[] subTrees, TechPool superPool) {
// Canonize subTrees
if (Arrays.equals(this.subTrees, subTrees)) {
subTrees = this.subTrees;
} else {
subTrees = subTrees.clone();
int l = subTrees.length;
while (l > 0 && subTrees[l - 1] == null) {
l--;
}
if (l == 0) {
subTrees = NULL_ARRAY;
} else if (l != subTrees.length) {
CellTree[] newSubTrees = null;
if (l == this.subTrees.length) {
newSubTrees = this.subTrees;
for (int i = 0; i < l; i++) {
if (newSubTrees[i] != subTrees[i]) {
newSubTrees = null;
break;
}
}
}
if (newSubTrees == null) {
newSubTrees = new CellTree[l];
System.arraycopy(subTrees, 0, newSubTrees, 0, l);
}
subTrees = newSubTrees;
}
}
// Check if unchanged
if (this.top == top && this.subTrees == subTrees) {
return this;
}
// Check if subTrees match the top backup
// Check technologies against superPool
// Compute cells and technologies used in new tree
CellRevision cellRevision = top.cellRevision;
CellId cellId = cellRevision.d.cellId;
BitSet techUsages = new BitSet();
if (top.techPool != superPool.restrict(cellRevision.techUsages, top.techPool)) {
throw new IllegalArgumentException();
}
techUsages.or(cellRevision.techUsages);
HashSet<CellId> allCellsAccum = new HashSet<CellId>();
for (int i = 0; i < cellRevision.cellUsages.length; i++) {
CellRevision.CellUsageInfo cui = cellRevision.cellUsages[i];
CellTree subTree = subTrees[i];
if (cui == null) {
if (subTree != null) {
throw new IllegalArgumentException();
}
continue;
}
BitSet subTechUsages = subTree.techPool.getTechUsages();
if (subTree.techPool != superPool.restrict(subTechUsages, subTree.techPool)) {
throw new IllegalArgumentException();
}
techUsages.or(subTechUsages);
allCellsAccum.addAll(subTree.allCells);
CellRevision subCellRevision = subTree.top.cellRevision;
if (subCellRevision.d.cellId != cellId.getUsageIn(i).protoId) {
throw new IllegalArgumentException();
}
cui.checkUsage(subCellRevision);
}
// Check for recursion
if (allCellsAccum.contains(cellId)) {
throw new IllegalArgumentException("Recursive " + cellId);
}
// Canonize new allCells
allCellsAccum.add(cellId);
Set<CellId> allCells;
if (allCellsAccum.equals(this.allCells)) {
allCells = this.allCells;
} else if (allCellsAccum.size() == 1) {
allCells = Collections.singleton(cellId);
} else {
allCells = Collections.unmodifiableSet(allCellsAccum);
}
assert allCellsAccum.equals(allCells);
// Construct new CellTree
TechPool techPool = superPool.restrict(techUsages, this.techPool);
CellTree newCellTree = new CellTree(top, subTrees, techPool, allCells);
if (this.top == top) {
// Try to reuse cell bounds
if (this.bounds != null) {
assert newCellTree.subTrees.length == this.subTrees.length;
ERectangle cellBounds = this.bounds;
for (int i = 0; i < this.subTrees.length; i++) {
CellTree oldSubTree = this.subTrees[i];
if (oldSubTree == null) continue;
assert oldSubTree.bounds != null;
if (!newCellTree.subTrees[i].getBounds().equals(oldSubTree.bounds)) {
cellBounds = null;
break;
}
}
if (cellBounds != null) {
newCellTree.bounds = cellBounds;
}
}
// Try to reuse NetCell
if (this.equivPorts != null) {
assert newCellTree.subTrees.length == this.subTrees.length;
EquivPorts netCell = this.equivPorts;
for (int i = 0; i < this.subTrees.length; i++) {
CellTree oldSubTree = this.subTrees[i];
if (oldSubTree == null) continue;
assert oldSubTree.equivPorts != null;
if (!newCellTree.subTrees[i].getEquivPorts().equalsPorts(oldSubTree.equivPorts)) {
netCell = null;
break;
}
}
if (netCell != null) {
newCellTree.equivPorts = netCell;
}
}
}
// Return the new CellTree
return newCellTree;
}
public boolean sameNetlist(CellTree that) {
if (this.top != that.top) {
return false;
}
for (int i = 0; i < this.subTrees.length; i++) {
CellTree thisSubTree = this.subTrees[i];
if (thisSubTree == null) continue;
if (!this.subTrees[i].getEquivPorts().equalsPorts(that.subTrees[i].getEquivPorts())) {
return false;
}
}
return true;
}
public CellTree[] getSubTrees() {
return subTrees.clone();
}
/**
* Returns cell bounds of this CellTree
* @return cell bounds of this CellTree
*/
public ERectangle getBounds() {
if (bounds == null) {
bounds = computeBounds(null);
}
return bounds;
}
private ERectangle computeBounds(ERectangle candidateBounds) {
CellRevision cellRevision = top.cellRevision;
// Collect subcell bounds
IdentityHashMap<CellId, ERectangle> subCellBounds = new IdentityHashMap<CellId, ERectangle>(cellRevision.cellUsages.length);
for (CellTree subTree : subTrees) {
if (subTree == null) {
continue;
}
subCellBounds.put(subTree.top.cellRevision.d.cellId, subTree.getBounds());
}
double cellLowX, cellHighX, cellLowY, cellHighY;
boolean boundsEmpty = true;
cellLowX = cellHighX = cellLowY = cellHighY = 0;
Rectangle2D.Double sb = new Rectangle2D.Double();
for (ImmutableNodeInst n : top.cellRevision.nodes) {
if (!(n.protoId instanceof CellId)) {
continue;
}
ERectangle b = subCellBounds.get((CellId) n.protoId);
n.orient.rectangleBounds(b.getMinX(), b.getMinY(), b.getMaxX(), b.getMaxY(),
n.anchor.getX(), n.anchor.getY(), sb);
double lowx = sb.getMinX();
double highx = sb.getMaxX();
double lowy = sb.getMinY();
double highy = sb.getMaxY();
if (boundsEmpty) {
boundsEmpty = false;
cellLowX = lowx;
cellHighX = highx;
cellLowY = lowy;
cellHighY = highy;
} else {
if (lowx < cellLowX) {
cellLowX = lowx;
}
if (highx > cellHighX) {
cellHighX = highx;
}
if (lowy < cellLowY) {
cellLowY = lowy;
}
if (highy > cellHighY) {
cellHighY = highy;
}
}
}
long gridMinX = DBMath.lambdaToGrid(cellLowX);
long gridMinY = DBMath.lambdaToGrid(cellLowY);
long gridMaxX = DBMath.lambdaToGrid(cellHighX);
long gridMaxY = DBMath.lambdaToGrid(cellHighY);
ERectangle primitiveBounds = top.getPrimitiveBounds();
if (primitiveBounds != null) {
if (boundsEmpty) {
gridMinX = primitiveBounds.getGridMinX();
gridMaxX = primitiveBounds.getGridMaxX();
gridMinY = primitiveBounds.getGridMinY();
gridMaxY = primitiveBounds.getGridMaxY();
} else {
gridMinX = Math.min(gridMinX, primitiveBounds.getGridMinX());
gridMaxX = Math.max(gridMaxX, primitiveBounds.getGridMaxX());
gridMinY = Math.min(gridMinY, primitiveBounds.getGridMinY());
gridMaxY = Math.max(gridMaxY, primitiveBounds.getGridMaxY());
}
}
if (candidateBounds != null && gridMinX == candidateBounds.getGridMinX() && gridMinY == candidateBounds.getGridMinY()
&& gridMaxX == candidateBounds.getGridMaxX() && gridMaxY == candidateBounds.getGridMaxY()) {
return candidateBounds;
}
return ERectangle.fromGrid(gridMinX, gridMinY, gridMaxX - gridMinX, gridMaxY - gridMinY);
}
public EquivPorts getEquivPorts() {
if (equivPorts == null) {
equivPorts = new EquivPorts(this);
}
return equivPorts;
}
public EquivPorts computeEquivPorts() {
return new EquivPorts(this);
}
public void check() {
top.check();
CellId cellId = top.cellRevision.d.cellId;
BitSet techUsages = new BitSet();
techUsages.or(top.cellRevision.techUsages);
assert subTrees.length == top.cellRevision.cellUsages.length;
HashSet<CellId> allCells = new HashSet<CellId>();
for (int i = 0; i < subTrees.length; i++) {
CellTree subTree = subTrees[i];
CellRevision.CellUsageInfo cui = top.cellRevision.cellUsages[i];
if (cui == null) {
assert subTree == null;
continue;
}
CellRevision subCellRevision = subTree.top.cellRevision;
CellUsage cu = cellId.getUsageIn(i);
assert subCellRevision.d.cellId == cu.protoId;
cui.checkUsage(subCellRevision);
BitSet subTechUsage = subTree.techPool.getTechUsages();
assert subTree.techPool == techPool.restrict(subTechUsage, subTree.techPool);
techUsages.or(subTechUsage);
allCells.addAll(subTree.allCells);
}
assert top.techPool == techPool.restrict(top.cellRevision.techUsages, top.techPool);
assert techUsages.equals(techPool.getTechUsages());
assert !allCells.contains(cellId);
allCells.add(cellId);
assert allCells.equals(this.allCells);
if (bounds != null) {
assert bounds == computeBounds(bounds);
}
}
@Override
public String toString() {
return top.toString();
}
}