All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.electric.database.CellBackup Maven / Gradle / Ivy

There is a newer version: 9.02-e
Show newest version
/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: CellBackup.java
 * Written by: Dmitry Nadezhin, Sun Microsystems.
 *
 * Copyright (c) 2005, 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.
 */
package com.sun.electric.database;

import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.id.IdManager;
import com.sun.electric.database.id.IdReader;
import com.sun.electric.database.id.IdWriter;
import com.sun.electric.database.id.NodeProtoId;
import com.sun.electric.database.id.PortProtoId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.text.CellName;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.AbstractShapeBuilder;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.BoundsBuilder;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.TechPool;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.util.collections.ArrayIterator;
import com.sun.electric.util.collections.ImmutableArrayList;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * CellBackup is a pair of CellRevision and TechPool.
 * It caches data that can be calculated when Technology is already
 * known, but subcells are unknown.
 */
public class CellBackup {

    private static final int BIN_SORT_THRESHOLD = 32;
    public static final CellBackup[] NULL_ARRAY = {};
    public static final ImmutableArrayList EMPTY_LIST = new ImmutableArrayList(NULL_ARRAY);
    static int cellBackupsCreated = 0;
    static int cellBackupsMemoized = 0;
    /** Cell data. */
    public final CellRevision cellRevision;
    /** Technologies mapping */
    public final TechPool techPool;
    /** "Modified" flag of the Cell. */
    public final boolean modified;
    /** Memoized data for size computation (connectivity etc). */
    private volatile Memoization m;
    /** Arc shrinkage data */
    private AbstractShapeBuilder.Shrinkage shrinkage;
    /** Bounds of primitive arcs in this Cell. */
    private ERectangle primitiveBounds;

    /** Creates a new instance of CellBackup */
    private CellBackup(CellRevision cellRevision, TechPool techPool, boolean modified) {
        this.cellRevision = cellRevision;
        this.techPool = techPool;
        this.modified = modified;
        cellBackupsCreated++;
    }

    /** Creates a new instance of CellBackup */
    public static CellBackup newInstance(ImmutableCell d, TechPool techPool) {
        if (d.cellId.idManager != techPool.idManager) {
            throw new IllegalArgumentException();
        }
        if (techPool.getTech(d.techId) == null) {
            throw new IllegalArgumentException();
        }
        CellRevision cellRevision = new CellRevision(d);
        TechPool restrictedPool = techPool.restrict(cellRevision.techUsages, techPool);
        return new CellBackup(cellRevision, restrictedPool, true);
    }

    /**
     * Creates a new instance of CellBackup which differs from this CellBackup.
     * Four array parameters are supplied. Each parameter may be null if its contents is the same as in this Snapshot.
     * @param d new persistent data of a cell.
     * @param nodesArray new array of nodes
     * @param arcsArray new array of arcs
     * @param exportsArray new array of exports
     * @param superPool TechPool which defines all used technologies
     * @return new snapshot which differs froms this Snapshot or this Snapshot.
     * @throws IllegalArgumentException on invariant violation.
     * @throws ArrayOutOfBoundsException on some invariant violations.
     */
    public CellBackup with(ImmutableCell d,
            ImmutableNodeInst[] nodesArray, ImmutableArcInst[] arcsArray, ImmutableExport[] exportsArray,
            TechPool superPool) {
        CellRevision newRevision = cellRevision.with(d, nodesArray, arcsArray, exportsArray);
        TechPool restrictedPool = superPool.restrict(newRevision.techUsages, techPool);
        if (newRevision == cellRevision && restrictedPool == techPool) {
            return this;
        }
        if (arcsArray != null) {
            for (ImmutableArcInst a : arcsArray) {
                if (a != null && !a.check(restrictedPool)) {
                    throw new IllegalArgumentException("arc " + a.name + " is not compatible with TechPool");
                }
            }
        }
        return new CellBackup(newRevision, restrictedPool, modified || newRevision != cellRevision);
    }

    /**
     * Creates a new instance of CellBackup which differs from this CellBackup by revision date.
     * @param revisionDate new revision date.
     * @return new CellBackup which differs from this CellBackup by revision date.
     */
    public CellBackup withRevisionDate(long revisionDate) {
        CellRevision newRevision = cellRevision.withRevisionDate(revisionDate);
        if (newRevision == cellRevision) {
            return this;
        }
        return new CellBackup(newRevision, this.techPool, true);
    }

    /**
     * Creates a new instance of CellBackup with modified flag off.
     * @return new snapshot which differs froms this Snapshot or this Snapshot.
     */
    public CellBackup withoutModified() {
        if (!this.modified) {
            return this;
        }
        return new CellBackup(this.cellRevision, this.techPool, false);
    }

    /**
     * Returns CellBackup which differs from this CellBackup by TechPool.
     * @param techPool technology map.
     * @return CellBackup with new TechPool.
     */
    public CellBackup withTechPool(TechPool techPool) {
        TechPool restrictedPool = techPool.restrict(cellRevision.techUsages, this.techPool);
        if (this.techPool == restrictedPool) {
            return this;
        }
        if (techPool.idManager != this.techPool.idManager) {
            throw new IllegalArgumentException();
        }
//        for (Technology tech: this.techPool.values()) {
//            if (techPool.get(tech.getId()) != tech)
//                throw new IllegalArgumentException();
//        }
        return new CellBackup(this.cellRevision, restrictedPool, this.modified);
    }

    /**
     * Returns CellBackup which differs from this CellBackup by renamed Ids.
     * @param idMapper a map from old Ids to new Ids.
     * @return CellBackup with renamed Ids.
     */
    CellBackup withRenamedIds(IdMapper idMapper, CellName newGroupName) {
        CellRevision newRevision = cellRevision.withRenamedIds(idMapper, newGroupName);
        if (newRevision == cellRevision) {
            return this;
        }
        return new CellBackup(newRevision, this.techPool, true);
    }

    /**
     * Writes this CellBackup to IdWriter.
     * @param writer where to write.
     */
    void write(IdWriter writer) throws IOException {
        cellRevision.write(writer);
        writer.writeBoolean(modified);
    }

    /**
     * Reads CellBackup from SnapshotReader.
     * @param reader where to read.
     */
    static CellBackup read(IdReader reader, TechPool techPool) throws IOException {
        CellRevision newRevision = CellRevision.read(reader);
        boolean modified = reader.readBoolean();
        TechPool restrictedPool = techPool.restrict(newRevision.techUsages, techPool);
        return new CellBackup(newRevision, restrictedPool, modified);
    }

    /**
     * Checks invariant of this CellBackup.
     * @throws AssertionError if invariant is broken.
     */
    public void check() {
        cellRevision.check();
        IdManager idManager = cellRevision.d.cellId.idManager;
        assert techPool.idManager == idManager;
        BitSet techUsages = new BitSet();
        for (Technology tech : techPool.values()) {
            int techIndex = tech.getId().techIndex;
            assert !techUsages.get(techIndex);
            techUsages.set(techIndex);
        }
        assert techUsages.equals(cellRevision.techUsages);
//        for (int techIndex = 0; techIndex < cellRevision.techUsages.length(); techIndex++) {
//            if (cellRevision.techUsages.get(techIndex))
//                assert techPool.getTech(idManager.getTechId(techIndex)) != null;
//        }
        for (ImmutableArcInst a : cellRevision.arcs) {
            if (a != null) {
                a.check(techPool);
            }
        }

        if (m != null) {
            m.check();
        }
    }

    @Override
    public String toString() {
        return cellRevision.toString();
    }

    /**
     * Returns data for size computation (connectivity etc).
     * @return data for size computation.
     */
    public Memoization getMemoization() {
        Memoization m = this.m;
        if (m != null) {
            return m;
        }
        return this.m = new Memoization();
    }

    public Memoization computeMemoization() {
        return new Memoization();
    }

    /**
     * Returns data for arc shrinkage computation.
     * @return data for arc shrinkage computation.
     */
    public AbstractShapeBuilder.Shrinkage getShrinkage() {
        if (shrinkage == null) {
            shrinkage = new AbstractShapeBuilder.Shrinkage(this);
        }
        return shrinkage;
    }

    /**
     * Returns bounds of all primitive arcs in this Cell or null if there are not primitives.
     * @return bounds of all primitive arcs or null.
     */
    public ERectangle getPrimitiveBounds() {
        ERectangle primitiveBounds = this.primitiveBounds;
        if (primitiveBounds != null) {
            return primitiveBounds;
        }
        return this.primitiveBounds = computePrimitiveBounds();
    }

    public ERectangle computePrimitiveBounds() {
        ERectangle primitiveArcBounds = computePrimitiveBoundsOfArcs();
        long gridMinX = Long.MAX_VALUE, gridMinY = Long.MAX_VALUE, gridMaxX = Long.MIN_VALUE, gridMaxY = Long.MIN_VALUE;
        long[] gridCoords = new long[4];
        for (ImmutableNodeInst n : cellRevision.nodes) {
            if (!(n.protoId instanceof PrimitiveNodeId)) {
                continue;
            }
            PrimitiveNode pn = techPool.getPrimitiveNode((PrimitiveNodeId) n.protoId);

            // special case: do not include "cell center" primitives from Generic
            if (pn == Generic.tech().cellCenterNode) {
                continue;
            }

            // special case for invisible pins: do not include if inheritable or interior-only
            if (pn == Generic.tech().invisiblePinNode) {
                boolean found = false;
                for (Iterator it = n.getVariables(); it.hasNext();) {
                    Variable var = it.next();
                    if (var.isDisplay()) {
                        TextDescriptor td = var.getTextDescriptor();
                        if (td.isInterior() || td.isInherit()) {
                            found = true;
                            break;
                        }
                    }
                }
                if (found) {
                    continue;
                }
            }

            pn.genBounds(n, gridCoords);
            gridMinX = Math.min(gridMinX, gridCoords[0]);
            gridMinY = Math.min(gridMinY, gridCoords[1]);
            gridMaxX = Math.max(gridMaxX, gridCoords[2]);
            gridMaxY = Math.max(gridMaxY, gridCoords[3]);
        }

        if (gridMinX > gridMaxX || gridMinY > gridMaxY) {
            return primitiveArcBounds;
        }
        if (primitiveArcBounds != null) {
            gridMinX = Math.min(gridMinX, primitiveArcBounds.getGridMinX());
            gridMaxX = Math.max(gridMaxX, primitiveArcBounds.getGridMaxX());
            gridMinY = Math.min(gridMinY, primitiveArcBounds.getGridMinY());
            gridMaxY = Math.max(gridMaxY, primitiveArcBounds.getGridMaxY());
        }
        return ERectangle.fromGrid(gridMinX, gridMinY, gridMaxX - gridMinX, gridMaxY - gridMinY);
    }

    ERectangle getElibPrimitiveBounds() {
        ERectangle primitiveArcBounds = computePrimitiveBoundsOfArcs();
        long gridMinX = Long.MAX_VALUE, gridMinY = Long.MAX_VALUE, gridMaxX = Long.MIN_VALUE, gridMaxY = Long.MIN_VALUE;
        long[] gridCoords = new long[4];
        for (ImmutableNodeInst n : cellRevision.nodes) {
            if (!(n.protoId instanceof PrimitiveNodeId)) {
                continue;
            }
            PrimitiveNode pn = techPool.getPrimitiveNode((PrimitiveNodeId) n.protoId);

            // special case: do not include "cell center" primitives from Generic
            if (pn == Generic.tech().cellCenterNode) {
                continue;
            }

            // special case for invisible pins: do not include if inheritable or interior-only
            if (pn == Generic.tech().invisiblePinNode) {
                boolean found = false;
                for (Iterator it = n.getVariables(); it.hasNext();) {
                    Variable var = it.next();
                    if (var.isDisplay()) {
                        TextDescriptor td = var.getTextDescriptor();
                        if (td.isInterior() || td.isInherit()) {
                            found = true;
                            break;
                        }
                    }
                }
                if (found) {
                    continue;
                }
            }

            pn.genElibBounds(getMemoization(), n, gridCoords);
            gridMinX = Math.min(gridMinX, gridCoords[0]);
            gridMinY = Math.min(gridMinY, gridCoords[1]);
            gridMaxX = Math.max(gridMaxX, gridCoords[2]);
            gridMaxY = Math.max(gridMaxY, gridCoords[3]);
        }

        if (gridMinX > gridMaxX || gridMinY > gridMaxY) {
            return primitiveArcBounds;
        }
        if (primitiveArcBounds != null) {
            gridMinX = Math.min(gridMinX, primitiveArcBounds.getGridMinX());
            gridMaxX = Math.max(gridMaxX, primitiveArcBounds.getGridMaxX());
            gridMinY = Math.min(gridMinY, primitiveArcBounds.getGridMinY());
            gridMaxY = Math.max(gridMaxY, primitiveArcBounds.getGridMaxY());
        }
        return ERectangle.fromGrid(gridMinX, gridMinY, gridMaxX - gridMinX, gridMaxY - gridMinY);
    }

    private ERectangle computePrimitiveBoundsOfArcs() {
        ImmutableArrayList arcs = cellRevision.arcs;
        if (arcs.isEmpty()) {
            return null;
        }
        long gridMinX = Long.MAX_VALUE, gridMinY = Long.MAX_VALUE, gridMaxX = Long.MIN_VALUE, gridMaxY = Long.MIN_VALUE;
        long[] gridCoords = new long[4];
        BoundsBuilder boundsBuilder = new BoundsBuilder(techPool);
        for (ImmutableArcInst a : arcs) {
            if (boundsBuilder.genBoundsEasy(a, gridCoords)) {
                gridMinX = Math.min(gridMinX, gridCoords[0]);
                gridMinY = Math.min(gridMinY, gridCoords[1]);
                gridMaxX = Math.max(gridMaxX, gridCoords[2]);
                gridMaxY = Math.max(gridMaxY, gridCoords[3]);
                continue;
            }
            boundsBuilder.genShapeOfArc(a);
        }
        ERectangle bounds = boundsBuilder.makeBounds();
        if (bounds != null) {
            gridMinX = Math.min(gridMinX, bounds.getGridMinX());
            gridMinY = Math.min(gridMinY, bounds.getGridMinY());
            gridMaxX = Math.max(gridMaxX, bounds.getGridMaxX());
            gridMaxY = Math.max(gridMaxY, bounds.getGridMaxY());
        }
        assert gridMinX <= gridMaxX && gridMinY <= gridMaxY;
        long gridW = gridMaxX - gridMinX;
        long gridH = gridMaxY - gridMinY;
        return ERectangle.fromGrid(gridMinX, gridMinY, gridW, gridH);
    }

    /**
     * Class which memoizes data for size computation (connectivity etc).
     */
    public class Memoization {

//        /**
//         * Base of a segment in portCounts array for valid nodeIds
//         */
//        private int[] portBasesForNodes;
//        /**
//         * Array which has an entry for every port instance.
//         * The index for port instance (nodeInd, portProtoId) is
//         * portBasesForNodes[nodeId] + portProtoId.chronIndex
//         */
//        private int[] portCounts;
        private final int[] connections;
        /** ImmutableExports sorted by original PortInst. */
        private final ImmutableExport[] exportIndexByOriginalPort;
        private final BitSet wiped = new BitSet();
        private final BitSet hardArcs = new BitSet();

        Memoization() {
//            long startTime = System.currentTimeMillis();
            cellBackupsMemoized++;

            connections = makeConnections1();
            exportIndexByOriginalPort = makeExportIndexByOriginalPort1();
//            if (USE_COUNTS_FOR_CONNECTIONS || USE_COUNTS_FOR_EXPORTS) {
//                initPortCounts();
//            }
//            connections = USE_COUNTS_FOR_CONNECTIONS ? makeConnections2() : makeConnections1();
//            exportIndexByOriginalPort = USE_COUNTS_FOR_EXPORTS ? makeExportIndexByOriginalPort2() : makeExportIndexByOriginalPort1();
//            portBasesForNodes = null;
//            portCounts = null;

            for (ImmutableArcInst a : cellRevision.arcs) {
                ArcProto ap = techPool.getArcProto(a.protoId);
                // wipe status
                if (ap.isWipable()) {
                    wiped.set(a.tailNodeId);
                    wiped.set(a.headNodeId);
                }
                // hard arcs
                if (!ap.isEasyShape(a, false)) {
                    hardArcs.set(a.arcId);
                }
            }

            ImmutableArrayList nodes = cellRevision.nodes;
            for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {
                ImmutableNodeInst n = nodes.get(nodeIndex);
                NodeProtoId np = n.protoId;
                if (!(np instanceof PrimitiveNodeId && techPool.getPrimitiveNode((PrimitiveNodeId) np).isArcsWipe())) {
                    wiped.clear(n.nodeId);
                }
            }
//            long stopTime = System.currentTimeMillis();
//            System.out.println("Memoization " + cellRevision.d.cellId + " took " + (stopTime - startTime) + " msec");
            check();
        }

        /**
         * Compute connections using sortConnections
         * @return connections
         */
        private int[] makeConnections1() {
            ImmutableArrayList arcs = cellRevision.arcs;

            // Put connections into buckets by nodeId.
            int[] connections = new int[arcs.size() * 2];
            int[] connectionsByNodeId = new int[cellRevision.nodeIndex.length];
            for (ImmutableArcInst a : arcs) {
                connectionsByNodeId[a.headNodeId]++;
                connectionsByNodeId[a.tailNodeId]++;
            }
            int sum = 0;
            for (int nodeId = 0; nodeId < connectionsByNodeId.length; nodeId++) {
                int start = sum;
                sum += connectionsByNodeId[nodeId];
                connectionsByNodeId[nodeId] = start;
            }
            for (int i = 0; i < arcs.size(); i++) {
                ImmutableArcInst a = arcs.get(i);
                connections[connectionsByNodeId[a.tailNodeId]++] = i * 2;
                connections[connectionsByNodeId[a.headNodeId]++] = i * 2 + 1;
            }

            // Sort each bucket by portId.
            sum = 0;
            for (int nodeId = 0; nodeId < connectionsByNodeId.length; nodeId++) {
                int start = sum;
                sum = connectionsByNodeId[nodeId];
                if (sum - 1 > start) {
                    sortConnections(connections, start, sum - 1);
                }
            }
            return connections;
        }

        /**
         * Compute exportIndexByOriginalPort using Arrays.sort
         * @return exportIndexByOriginalPort
         */
        private ImmutableExport[] makeExportIndexByOriginalPort1() {
            ImmutableExport[] exportIndexByOriginalPort = cellRevision.exports.toArray(new ImmutableExport[cellRevision.exports.size()]);
            Arrays.sort(exportIndexByOriginalPort, BY_ORIGINAL_PORT);
            return exportIndexByOriginalPort;
        }

//        /**
//         * Compute connections using portBasesForNodes and portCounts
//         * @return connections
//         */
//        private int[] makeConnections2() {
//            ImmutableArrayList arcs = cellRevision.arcs;
//
//            clearPortCounts();
//            for (int arcIndex = 0; arcIndex < arcs.size(); arcIndex++) {
//                ImmutableArcInst a = arcs.get(arcIndex);
//                incrementPortCount(a.tailNodeId, a.tailPortId);
//                incrementPortCount(a.headNodeId, a.headPortId);
//            }
//            portCountsToPortOffsets();
//            int[] connections = new int[arcs.size() * 2];
//            Arrays.fill(connections, -1);
//            for (int arcIndex = 0; arcIndex < arcs.size(); arcIndex++) {
//                ImmutableArcInst a = arcs.get(arcIndex);
//                connections[incrementPortCount(a.tailNodeId, a.tailPortId)] = arcIndex << 1;
//                connections[incrementPortCount(a.headNodeId, a.headPortId)] = (arcIndex << 1) | 1;
//            }
//            return connections;
//        }
//
//        /**
//         * Compute exportIndexByOriginalPort using portBasesForNodes and portCounts
//         * @return exportIndexByOriginalPort
//         */
//        private ImmutableExport[] makeExportIndexByOriginalPort2() {
//            clearPortCounts();
//            for (ImmutableExport e: cellRevision.exports) {
//                incrementPortCount(e.originalNodeId, e.originalPortId);
//            }
//            portCountsToPortOffsets();
//            ImmutableExport[] exportIndexByOriginalPort = new ImmutableExport[cellRevision.exports.size()];
//            for (int exportId = 0; exportId < cellRevision.exportIndex.length; exportId++) {
//                int exportIndex = cellRevision.exportIndex[exportId];
//                if (exportIndex < 0) continue;
//                ImmutableExport e = cellRevision.exports.get(exportIndex);
//                exportIndexByOriginalPort[incrementPortCount(e.originalNodeId, e.originalPortId)] = e;
//            }
//            return exportIndexByOriginalPort;
//        }
//
//        /**
//         * Initialize portBasesForNodes and portCounts
//         */
//        private void initPortCounts() {
//            // Prepare port map
//            CellId cellId = cellRevision.d.cellId;
//            IdentityHashMap maxPortIds = new IdentityHashMap();
//            for (int i = 0; i < cellRevision.cellUsages.length; i++) {
//                CellRevision.CellUsageInfo cui = cellRevision.cellUsages[i];
//                if (cui == null) continue;
//                assert cui.instCount > 0;
//                maxPortIds.put(cellId.getUsageIn(i).protoId, cui.usedExportsLength);
//            }
//            int totalPortCount = 0;
//            portBasesForNodes = new int[nodeIndexByNodeId.length];
//            Arrays.fill(portBasesForNodes, Integer.MIN_VALUE);
//            for (int nodeId = 0; nodeId < nodeIndexByNodeId.length; nodeId++) {
//                int nodeIndex = nodeIndexByNodeId[nodeId];
//                if (nodeIndex < 0) continue;
//                ImmutableNodeInst n = cellRevision.nodes.get(nodeIndex);
//                Integer portCount = maxPortIds.get(n.protoId);
//                if (portCount == null) {
//                    PrimitiveNode pn = techPool.getPrimitiveNode((PrimitiveNodeId)n.protoId);
//                    int maxPortId = -1;
//                    for (Iterator it = pn.getPorts(); it.hasNext(); )
//                        maxPortId = Math.max(maxPortId, it.next().getId().chronIndex);
//                    portCount = maxPortId + 1;
//                    maxPortIds.put(n.protoId, portCount);
//                }
//                portBasesForNodes[n.nodeId] = totalPortCount;
//                totalPortCount += portCount;
//            }
//            portCounts = new int[totalPortCount];
//        }
//
//        /*
//         * Clear portCounts
//         */
//        private void clearPortCounts() {
//            Arrays.fill(portCounts, 0);
//        }
//
//        /**
//         * Increment portCounts entry for specified port intance
//         * @param nodeId nodeId of port instance
//         * @param portId PortProtoId of port instance
//         * @return the value of portCounts entry before increment
//         */
//        private int incrementPortCount(int nodeId, PortProtoId portId) {
//            return portCounts[portBasesForNodes[nodeId] + portId.chronIndex]++;
//        }
//
//        /**
//         * Convert port counts to port offsets
//         */
//        private void portCountsToPortOffsets() {
//            int connOffset = 0;
//            for (int i = 0; i < portCounts.length; i++) {
//                int numConns = portCounts[i];
//                portCounts[i] = connOffset;
//                connOffset += numConns;
//            }
//        }
        /**
         * Returns true of there are Exports on specified NodeInst.
         * @param originalNode specified NodeInst.
         * @return true if there are Exports on specified NodeInst.
         */
        public boolean hasExports(ImmutableNodeInst originalNode) {
            int startIndex = searchExportByOriginalPort(originalNode.nodeId, 0);
            if (startIndex >= exportIndexByOriginalPort.length) {
                return false;
            }
            ImmutableExport e = exportIndexByOriginalPort[startIndex];
            return e.originalNodeId == originalNode.nodeId;
        }

        /**
         * Method to return the number of Exports on specified NodeInst.
         * @param originalNodeId nodeId of specified NodeInst.
         * @return the number of Exports on specified NodeInst.
         */
        public int getNumExports(int originalNodeId) {
            int startIndex = searchExportByOriginalPort(originalNodeId, 0);
            int j = startIndex;
            for (; j < exportIndexByOriginalPort.length; j++) {
                ImmutableExport e = exportIndexByOriginalPort[j];
                if (e.originalNodeId != originalNodeId) {
                    break;
                }
            }
            return j - startIndex;
        }

        /**
         * Method to return an Iterator over all ImmutableExports on specified NodeInst.
         * @param originalNodeId nodeId of specified NodeInst.
         * @return an Iterator over all ImmutableExports on specified NodeInst.
         */
        public Iterator getExports(int originalNodeId) {
            int startIndex = searchExportByOriginalPort(originalNodeId, 0);
            int j = startIndex;
            for (; j < exportIndexByOriginalPort.length; j++) {
                ImmutableExport e = exportIndexByOriginalPort[j];
                if (e.originalNodeId != originalNodeId) {
                    break;
                }
            }
            return ArrayIterator.iterator(exportIndexByOriginalPort, startIndex, j);
        }

        /**
         * Method to determine whether the display of specified pin NodeInst should be supressed.
         * In Schematics technologies, pins are not displayed if there are 1 or 2 connections,
         * but are shown for 0 or 3 or more connections (called "Steiner points").
         * @param pin specified pin ImmutableNodeInst
         * @return true if specieifed pin NodeInst should be supressed.
         */
        public boolean pinUseCount(ImmutableNodeInst pin) {
            int numConnections = getNumConnections(pin);
            if (numConnections > 2) {
                return false;
            }
            if (hasExports(pin)) {
                return true;
            }
            if (numConnections == 0) {
                return false;
            }
            return true;
        }

        /**
         * Method to return a list of arcs connected to speciefed or all ports of
         * specified ImmutableNodeInst.
         * @param headEnds true if i-th arc connects by head end
         * @param n specified ImmutableNodeInst
         * @param portId specified port or null
         * @return a List of connected ImmutableArcInsts
         */
        public List getConnections(BitSet headEnds, ImmutableNodeInst n, PortProtoId portId) {
            ArrayList result = null;
            if (headEnds != null) {
                headEnds.clear();
            }
            int myNodeId = n.nodeId;
            int chronIndex = 0;
            if (portId != null) {
                assert portId.parentId == n.protoId;
                chronIndex = portId.chronIndex;
            }
            int i = searchConnectionByPort(myNodeId, chronIndex);
            int j = i;
            for (; j < connections.length; j++) {
                int con = connections[j];
                ImmutableArcInst a = getArcs().get(con >>> 1);
                boolean end = (con & 1) != 0;
                int nodeId = end ? a.headNodeId : a.tailNodeId;
                if (nodeId != myNodeId) {
                    break;
                }
                if (portId != null) {
                    PortProtoId endProtoId = end ? a.headPortId : a.tailPortId;
                    if (endProtoId.getChronIndex() != chronIndex) {
                        break;
                    }
                }
                if (result == null) {
                    result = new ArrayList();
                }
                if (headEnds != null && end) {
                    headEnds.set(result.size());
                }
                result.add(a);
            }
            return result != null ? result : Collections.emptyList();
        }

        /**
         * Returns true of there are Connections on specified ImmutableNodeInst
         * connected either to specified port or to all ports
         * @param n specified ImmutableNodeInst
         * @param portId specified port or null
         * @return true if there are Connections on specified ImmutableNodeInst amd specified port.
         */
        public boolean hasConnections(ImmutableNodeInst n, PortProtoId portId) {
            int chronIndex = 0;
            if (portId != null) {
                assert portId.parentId == n.protoId;
                chronIndex = portId.chronIndex;
            }
            int i = searchConnectionByPort(n.nodeId, chronIndex);
            if (i >= connections.length) {
                return false;
            }
            int con = connections[i];
            ImmutableArcInst a = getArcs().get(con >>> 1);
            boolean end = (con & 1) != 0;
            int nodeId = end ? a.headNodeId : a.tailNodeId;
            if (nodeId != n.nodeId) {
                return false;
            }
            return portId == null || portId == (end ? a.headPortId : a.tailPortId);
        }

        /**
         * Method to return the number of Connections on specified ImmutableNodeInst.
         * @param n specified ImmutableNodeInst
         * @return the number of Connections on specified ImmutableNodeInst.
         */
        public int getNumConnections(ImmutableNodeInst n) {
            int myNodeId = n.nodeId;
            int i = searchConnectionByPort(myNodeId, 0);
            int j = i;
            for (; j < connections.length; j++) {
                int con = connections[j];
                ImmutableArcInst a = getArcs().get(con >>> 1);
                boolean end = (con & 1) != 0;
                int nodeId = end ? a.headNodeId : a.tailNodeId;
                if (nodeId != myNodeId) {
                    break;
                }
            }
            return j - i;
        }

        private int searchExportByOriginalPort(int originalNodeId, int originalChronIndex) {
            int low = 0;
            int high = exportIndexByOriginalPort.length - 1;
            while (low <= high) {
                int mid = (low + high) >> 1; // try in a middle
                ImmutableExport e = exportIndexByOriginalPort[mid];
                int cmp = e.originalNodeId - originalNodeId;
                if (cmp == 0) {
                    cmp = e.originalPortId.getChronIndex() >= originalChronIndex ? 1 : -1;
                }

                if (cmp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            return low;
        }

        private int searchConnectionByPort(int nodeId, int chronIndex) {
            int low = 0;
            int high = connections.length - 1;
            while (low <= high) {
                int mid = (low + high) >> 1; // try in a middle
                int con = connections[mid];
                ImmutableArcInst a = cellRevision.arcs.get(con >>> 1);
                boolean end = (con & 1) != 0;
                int endNodeId = end ? a.headNodeId : a.tailNodeId;
                int cmp = endNodeId - nodeId;
                if (cmp == 0) {
                    PortProtoId portId = end ? a.headPortId : a.tailPortId;
                    cmp = portId.getChronIndex() - chronIndex;
                }

                if (cmp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            return low;
        }

        public CellBackup getCellBackup() {
            return CellBackup.this;
        }

        public TechPool getTechPool() {
            return techPool;
        }

        /**
         * Returns arcs in sorted order.
         * @return ImmutableArcInsts in sorted order.
         */
        private ImmutableArrayList getArcs() {
            return cellRevision.arcs;
        }

        /**
         * Method to tell whether the specified ImmutableNodeInst is wiped.
         * Wiped ImmutableNodeInsts are erased.  Typically, pin ImmutableNodeInsts can be wiped.
         * This means that when an arc connects to the pin, it is no longer drawn.
         * In order for a ImmutableNodeInst to be wiped, its prototype must have the "setArcsWipe" state,
         * and the arcs connected to it must have "setWipable" in their prototype.
         * @param n specified ImmutableNodeInst
         * @return true if specified ImmutableNodeInst is wiped.
         */
        public boolean isWiped(ImmutableNodeInst n) {
            return wiped.get(n.nodeId);
        }

        public boolean isHardArc(int arcId) {
            return hardArcs.get(arcId);
        }

        /**
         * Checks invariant of this CellBackup.
         * @throws AssertionError if invariant is broken.
         */
        private void check() {
            assert exportIndexByOriginalPort.length == cellRevision.exports.size();
            ImmutableExport prevE = null;
            for (ImmutableExport e : exportIndexByOriginalPort) {
                if (prevE != null) {
                    assert BY_ORIGINAL_PORT.compare(prevE, e) < 0;
                }
                assert e == cellRevision.getExport(e.exportId);
                prevE = e;
            }
            assert connections.length == cellRevision.arcs.size() * 2;
            for (int i = 1; i < connections.length; i++) {
                assert compareConnections(connections[i - 1], connections[i]) < 0;
            }
        }

        private void sortConnections(int[] connections, int l, int r) {
            ImmutableArrayList arcs = cellRevision.arcs;
            while (r - l > BIN_SORT_THRESHOLD) {
                int x = connections[(l + r) >>> 1];
                ImmutableArcInst ax = arcs.get(x >>> 1);
                boolean endx = (x & 1) != 0;
                PortProtoId portIdX = endx ? ax.headPortId : ax.tailPortId;
                int chronIndexX = portIdX.getChronIndex();
                int i = l, j = r;
                do {
                    // while (compareConnections(connections[i], x) < 0) i++;
                    for (;;) {
                        int con = connections[i];
                        ImmutableArcInst a = arcs.get(con >>> 1);
                        boolean end = (con & 1) != 0;
                        PortProtoId portId = end ? a.headPortId : a.tailPortId;
                        if (portId.getChronIndex() > chronIndexX) {
                            break;
                        }
                        if (portId.getChronIndex() == chronIndexX && con >= x) {
                            break;
                        }
                        i++;
                    }

                    // while (compareConnections(x, connections[j]) < 0) j--;
                    for (;;) {
                        int con = connections[j];
                        ImmutableArcInst a = arcs.get(con >>> 1);
                        boolean end = (con & 1) != 0;
                        PortProtoId portId = end ? a.headPortId : a.tailPortId;
                        if (chronIndexX > portId.getChronIndex()) {
                            break;
                        }
                        if (chronIndexX == portId.getChronIndex() && x >= con) {
                            break;
                        }
                        j--;
                    }

                    if (i <= j) {
                        int w = connections[i];
                        connections[i] = connections[j];
                        connections[j] = w;
                        i++;
                        j--;
                    }
                } while (i <= j);
                if (j - l < r - i) {
                    sortConnections(connections, l, j);
                    l = i;
                } else {
                    sortConnections(connections, i, r);
                    r = j;
                }
            }
            binarySort(connections, l, r + 1);
        }

        /**
         * This is a specifalized version of {@link java.utils.TimSort#binarySort}.
         *
         * Sorts the specified portion of the specified array using a binary
         * insertion sort.  This is the best method for sorting small numbers
         * of elements.  It requires O(n log n) compares, but O(n^2) data
         * movement (worst case).
         *
         * If the initial part of the specified range is already sorted,
         * this method can take advantage of it: the method assumes that the
         * elements from index {@code lo}, inclusive, to {@code start},
         * exclusive are already sorted.
         *
         * @param a the array in which a range is to be sorted
         * @param lo the index of the first element in the range to be sorted
         * @param hi the index after the last element in the range to be sorted
         * @param start the index of the first element in the range that is
         *        not already known to be sorted (@code lo <= start <= hi}
         * @param c comparator to used for the sort
         */
        @SuppressWarnings("fallthrough")
        private void binarySort(int[] connections, int lo, int hi) {
            ImmutableArrayList arcs = cellRevision.arcs;
            assert lo <= hi;
            int start = lo + 1;
            if (start >= hi) {
                return;
            }
            int conS;
            ImmutableArcInst aS;
            PortProtoId portIdS;
            int conL = connections[lo];
            ImmutableArcInst aL = arcs.get(conL >>> 1);
            PortProtoId portIdL = (conL & 1) != 0 ? aL.headPortId : aL.tailPortId;
            for (;;) {
                conS = connections[start];
                aS = arcs.get(conS >>> 1);
                portIdS = (conS & 1) != 0 ? aS.headPortId : aS.tailPortId;
                int cmp = portIdS.chronIndex - portIdL.chronIndex;
                if (cmp < 0) {
                    break;
                }
                if (cmp == 0 && conS < conL) {
                    break;
                }
                start++;
                if (start >= hi) {
                    return;
                }
                conL = conS;
                aL = aS;
                portIdL = portIdS;
            }
            for (;;) {
                assert start < hi;
                // Set left (and right) to the index where a[start] (pivot) belongs
                int left = lo;
                int right = start;
                assert left <= right;
                /*
                 * Invariants:
                 *   pivot >= all in [lo, left).
                 *   pivot <  all in [right, start).
                 */
                while (left < right) {
                    int mid = (left + right) >>> 1;
                    int conM = connections[mid];
                    ImmutableArcInst aM = arcs.get(conM >>> 1);
                    PortProtoId portIdM = (conM & 1) != 0 ? aM.headPortId : aM.tailPortId;
                    int cmp = portIdS.chronIndex - portIdM.chronIndex;
                    if (cmp == 0) {
                        cmp = conS - conM;
                    }
                    if (cmp < 0) {
                        right = mid;
                    } else {
                        left = mid + 1;
                    }
                }
                assert left == right;

                /*
                 * The invariants still hold: pivot >= all in [lo, left) and
                 * pivot < all in [left, start), so pivot belongs at left.  Note
                 * that if there are elements equal to pivot, left points to the
                 * first slot after them -- that's why this sort is stable.
                 * Slide elements over to make room for pivot.
                 */
                int n = start - left;  // The number of elements to move
                // Switch is just an optimization for arraycopy in default case
                switch (n) {
                    case 2:
                        connections[left + 2] = connections[left + 1];
                    case 1:
                        connections[left + 1] = connections[left];
                        break;
                    default:
                        System.arraycopy(connections, left, connections, left + 1, n);
                }
                connections[left] = conS;

                start++;
                if (start >= hi) {
                    return;
                }
                conS = connections[start];
                aS = arcs.get(conS >>> 1);
                portIdS = (conS & 1) != 0 ? aS.headPortId : aS.tailPortId;
            }
        }

        private int compareConnectionsSameNode(int con1, int con2) {
            ImmutableArcInst a1 = cellRevision.arcs.get(con1 >>> 1);
            ImmutableArcInst a2 = cellRevision.arcs.get(con2 >>> 1);
            PortProtoId portId1 = (con1 & 1) != 0 ? a1.headPortId : a1.tailPortId;
            PortProtoId portId2 = (con2 & 1) != 0 ? a2.headPortId : a2.tailPortId;
            int cmp = portId1.chronIndex - portId2.chronIndex;
            return cmp != 0 ? cmp : con1 - con2;
        }

        private int compareConnections(int con1, int con2) {
            ImmutableArcInst a1 = cellRevision.arcs.get(con1 >>> 1);
            ImmutableArcInst a2 = cellRevision.arcs.get(con2 >>> 1);
            boolean end1 = (con1 & 1) != 0;
            boolean end2 = (con2 & 1) != 0;
            int nodeId1 = end1 ? a1.headNodeId : a1.tailNodeId;
            int nodeId2 = end2 ? a2.headNodeId : a2.tailNodeId;
            int cmp = nodeId1 - nodeId2;
            if (cmp != 0) {
                return cmp;
            }
            PortProtoId portId1 = end1 ? a1.headPortId : a1.tailPortId;
            PortProtoId portId2 = end2 ? a2.headPortId : a2.tailPortId;
            cmp = portId1.getChronIndex() - portId2.getChronIndex();
            if (cmp != 0) {
                return cmp;
            }
            return con1 - con2;
        }
    }
    private static final Comparator BY_ORIGINAL_PORT = new Comparator() {

        @Override
        public int compare(ImmutableExport e1, ImmutableExport e2) {
            int result = e1.originalNodeId - e2.originalNodeId;
            if (result != 0) {
                return result;
            }
            result = e1.originalPortId.getChronIndex() - e2.originalPortId.getChronIndex();
            if (result != 0) {
                return result;
            }
            return e1.exportId.chronIndex - e2.exportId.chronIndex;
        }
    };
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy