/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Technology.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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.technology;

import com.sun.electric.database.EObjectInputStream;
import com.sun.electric.database.EObjectOutputStream;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.Environment;
import com.sun.electric.database.ImmutableArcInst;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.id.ArcProtoId;
import com.sun.electric.database.id.IdManager;
import com.sun.electric.database.id.LayerId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.id.TechId;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.text.Pref;
import com.sun.electric.database.text.Setting;
import com.sun.electric.database.text.Version;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.GDSLayers.GDSLayerType;
import com.sun.electric.technology.Layer.LayerSortingType;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.GEM;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.ToolSettings;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.UserInterfaceMain;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.collections.ImmutableArrayList;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.ECoord;
import com.sun.electric.util.math.Orientation;

import java.awt.Color;
import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InvalidObjectException;
import java.io.NotSerializableException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeMap;
import java.util.prefs.Preferences;

/**
 * Technology is the base class for all of the specific technologies in Electric.
 *
 * It is organized into two main areas: nodes and arcs.
 * Both nodes and arcs are composed of Layers.
 *<P>
 * Subclasses of Technology usually start by defining the Layers (such as Metal-1, Metal-2, etc.)
 * Then the ArcProto objects are created, built entirely from Layers.
 * Next PrimitiveNode objects are created, and they have Layers as well as connectivity to the ArcProtos.
 * The Technology concludes with miscellaneous data assignments of technology-wide information.
 * <P>
 * Here are the nodes in a sample CMOS technology.
 * Note that there are two types of transistors and diffusion contacts, one for Well and one for Substrate.
 * Each layer that can exist as a wire must have a pin node (in this case, metal, polysilicon, and two flavors of diffusion.
 * Note that there are pure-layer nodes at the bottom which allow arbitrary geometry to be constructed.
 * <CENTER><IMG SRC="doc-files/Technology-1.gif"></CENTER>
 * <P>
 * The Schematic technology has some unusual features.
 * <CENTER><IMG SRC="doc-files/Technology-2.gif"></CENTER>
 * <P>
 * Conceptually, a Technology has 3 types of information:
 * <UL><LI><I>Geometry</I>.  Each node and arc can be described in terms of polygons on different Layers.
 * The ArcLayer and NodeLayer subclasses help define those polygons.
 * <LI><I>Connectivity</I>.  The very structure of the nodes and arcs established a set of rules of connectivity.
 * Examples include the list of allowable arc types that may connect to each port, and the use of port "network numbers"
 * to identify those that are connected internally.
 * <LI><I>Behavior</I>.  Behavioral information takes many forms, but they can all find a place here.
 * For example, each layer, node, and arc has a "function" that describes its general behavior.
 * Some information applies to the technology as a whole, for example SPICE model cards.
 * Other examples include Design Rules and technology characteristics.
 * </UL>
 * @author Steven M. Rubin
 */
public class Technology implements Comparable<Technology>, Serializable {

    /** Jelib writes base sizes since this Electric Version */
    public static final Version DISK_VERSION_1 = Version.parseVersion("8.05g");
    /** Jelib writes oversize over standard primitive since this Electric Version */
    public static final Version DISK_VERSION_2 = Version.parseVersion("8.05o");
    public static final Technology[] NULL_ARRAY = {};
    public static final ImmutableArrayList<Technology> EMPTY_LIST = ImmutableArrayList.of();
    /** key of Variable for saving scalable transistor contact information. */
    public static final Variable.Key TRANS_CONTACT = Variable.newKey("MOCMOS_transcontacts");
    /** Relative path in Preferences where technology Settings and Preferences are stored */
    public static final String TECH_NODE = "technology/technologies";
    // strings used in the Component MenuNodeLayer
    public static final String SPECIALMENUCELL = "Cell";
    public static final String SPECIALMENUMISC = "Misc.";
    public static final String SPECIALMENUPURE = "Pure";
    public static final String SPECIALMENUSPICE = "Spice";
    public static final String SPECIALMENUEXPORT = "Export";
    public static final String SPECIALMENUTEXT = "Text";
    public static final String SPECIALMENUHIGH = "High";
    public static final String SPECIALMENUPORT = "Port";
    public static final String SPECIALMENUSEPARATOR = "-";

    /**
     * Defines a single layer of a ArcProto.
     * A ArcProto has a list of these ArcLayer objects, one for
     * each layer in a typical ArcInst.
     * Each ArcProto is composed of a number of ArcLayer descriptors.
     * A descriptor converts a specific ArcInst into a polygon that describe this particular layer.
     */
    public static class ArcLayer {

        private final Layer layer;
        private final Poly.Type style;
        private ECoord extend;

        /**
         * Constructs an <CODE>ArcLayer</CODE> with the specified description.
         * @param layer the Layer of this ArcLayer.
         * @param arcLayerWidth the width of this ArcLayer in standard ArcInst.
         * @param style the Poly.Style of this ArcLayer.
         */
        public ArcLayer(Layer layer, double arcLayerWidth, Poly.Type style) {
            this(layer, style, 0.5 * arcLayerWidth);
        }

        /**
         * Constructs an <CODE>ArcLayer</CODE> with the specified description.
         * @param layer the Layer of this ArcLayer.
         * @param style the Poly.Style of this ArcLayer.
         * @param lambdaExtend lambda fraction of extend
         */
        public ArcLayer(Layer layer, Poly.Type style, double lambdaExtend) {
            this.layer = layer;
            this.style = style;
            extend = ECoord.fromLambdaRoundGrid(lambdaExtend);
            if (extend.getGrid() < 0 || extend.getGrid() >= Integer.MAX_VALUE / 8) {
                throw new IllegalArgumentException("extend=" + extend);
            }
        }

        /**
         * Returns the Layer from the Technology to be used for this ArcLayer.
         * @return the Layer from the Technology to be used for this ArcLayer.
         */
        public Layer getLayer() {
            return layer;
        }

        /**
         * Returns the distance from the center of the standard ArcInst to the outsize of this ArcLayer as ECoord object.
         * The distance from the center of arbitrary ArcInst ai to the outsize of its ArcLayer is
         * ai.getD().getExtendOverMin() + arcLayer.getGridExtend()
         * @return the distance from the outside of the ArcInst to this ArcLayer as ECoord object.
         */
        public ECoord getExtend() {
            return extend;
        }

        /**
         * Returns the Poly.Style of this ArcLayer.
         * @return the Poly.Style of this ArcLayer.
         */
        public Poly.Type getStyle() {
            return style;
        }

        void dump(PrintWriter out) {
            out.println("\t\tarcLayer layer=" + layer.getName()
                    + " style=" + style.name()
                    + " extend=" + extend);
        }

        Xml.ArcLayer makeXml() {
            Xml.ArcLayer al = new Xml.ArcLayer();
            al.layer = layer.getName();
            al.style = style;
            al.extend.addLambda(extend.getLambda());
            return al;
        }
    }

    /**
     * Defines a point in space that is relative to a NodeInst's bounds.
     * The TechPoint has two coordinates: X and Y.
     * Each of these coordinates is represented by an Edge class (EdgeH for X
     * and EdgeV for Y).
     * The Edge classes have two numbers: a multiplier and an adder.
     * The desired coordinate takes the NodeInst's center, adds in the
     * product of the Edge multiplier and the NodeInst's size, and then adds
     * in the Edge adder.
     * <P>
     * Arrays of TechPoint objects can be used to describe the bounds of
     * a particular layer in a NodeInst.  Typically, four TechPoint objects
     * can describe a rectangle.  Circles only need two (center and edge).
     * The <CODE>Poly.Style</CODE> class defines the possible types of
     * geometry.
     * @see EdgeH
     * @see EdgeV
     */
    public static class TechPoint implements Serializable {

        private final EdgeH x;
        private final EdgeV y;

        /**
         * Constructs a <CODE>TechPoint</CODE> with the specified description.
         * @param x the EdgeH that converts a NodeInst into an X coordinate on that NodeInst.
         * @param y the EdgeV that converts a NodeInst into a Y coordinate on that NodeInst.
         */
        public TechPoint(EdgeH x, EdgeV y) {
            if (x == null || y == null) {
                throw new NullPointerException();
            }
            this.x = x;
            this.y = y;
        }

        /**
         * Method to make a 2-long TechPoint array that describes a point at the center of the node.
         * @return a new TechPoint array that describes a point at the center of the node.
         */
        public static TechPoint[] makeCenterBox() {
            return new Technology.TechPoint[]{
                        new Technology.TechPoint(EdgeH.fromCenter(0), EdgeV.fromCenter(0)),
                        new Technology.TechPoint(EdgeH.fromCenter(0), EdgeV.fromCenter(0))};
        }

        /**
         * Method to make a 2-long TechPoint array that describes indentation from the center by a specified amount.
         * @param amountX the amount to indent from the center the box along X.
         * @param amountY the amount to indent from the center the box along Y.
         * @return a new TechPoint array that describes this indented box.
         */
        public static TechPoint[] makeIndentedFromCenter(double amountX, double amountY) {
            return new Technology.TechPoint[]{
                        new Technology.TechPoint(EdgeH.fromCenter(-amountX), EdgeV.fromCenter(-amountY)),
                        new Technology.TechPoint(EdgeH.fromCenter(amountX), EdgeV.fromCenter(amountY))};
        }

        /**
         * Returns the EdgeH that converts a NodeInst into an X coordinate on that NodeInst.
         * @return the EdgeH that converts a NodeInst into an X coordinate on that NodeInst.
         */
        public EdgeH getX() {
            return x;
        }

        /**
         * Returns the TechPoint with a new EdgeH
         * @param x new EdgeH
         * @return the TechPoint with the new EdgeH
         */
        public TechPoint withX(EdgeH x) {
            if (x.equals(this.x)) {
                return this;
            }
            return new TechPoint(x, this.y);
        }

        /**
         * Returns the EdgeV that converts a NodeInst into a Y coordinate on that NodeInst.
         * @return the EdgeV that converts a NodeInst into a Y coordinate on that NodeInst.
         */
        public EdgeV getY() {
            return y;
        }

        /**
         * Returns the TechPoint with a new EdgeV
         * @param y new EdgeV
         * @return the TechPoint with the new EdgeV
         */
        public TechPoint withY(EdgeV y) {
            if (y.equals(this.y)) {
                return this;
            }
            return new TechPoint(this.x, y);
        }
    }

    /**
     * Defines a single layer of a PrimitiveNode.
     * A PrimitiveNode has a list of these NodeLayer objects, one for
     * each layer in a typical NodeInst.
     * Each PrimitiveNode is composed of a number of NodeLayer descriptors.
     * A descriptor converts a specific NodeInst into a polygon that describe this particular layer.
     */
    public static class NodeLayer {

        private final Layer layer;
        private final int portNum;
        private final Poly.Type style;
        private final int representation;
        private final TechPoint[] points;
        private String message;
        private TextDescriptor descriptor;
        private ECoord lWidth;
        private ECoord rWidth;
        private ECoord extentT;
        private ECoord extendB;
        private ECoord cutSizeX = ECoord.ZERO;
        private ECoord cutSizeY = ECoord.ZERO;
        private ECoord cutSep1D = ECoord.ZERO;
        private ECoord cutSep2D = ECoord.ZERO;
        // the meaning of "representation"
        /**
         * Indicates that the "points" list defines scalable points.
         * Each point here becomes a point on the Poly.
         */
        public static final int POINTS = 0;
        /**
         * Indicates that the "points" list defines a rectangle.
         * It contains two diagonally opposite points.
         */
        public static final int BOX = 1;
//		/**
//		 * Indicates that the "points" list defines a minimum sized rectangle.
//		 * It contains two diagonally opposite points, like BOX,
//		 * and also contains a minimum box size beyond which the polygon will not shrink
//		 * (again, two diagonally opposite points).
//		 */
//		public static final int MINBOX = 2;
        /**
         * Indicates that the "points" list defines a rectangle,
         * where centers of multi-cut are located
         * It contains two diagonally opposite points.
         */
        public static final int MULTICUTBOX = 3;
        /** key of Variable for overriding cut spacing. */
        public static final Variable.Key CUT_SPACING = Variable.newKey("CUT_spacing");
        /** key of Variable for overriding cut alignment */
        public static final Variable.Key CUT_ALIGNMENT = Variable.newKey("CUT_alignment");
        /** key of Variable for overriding metal surround. */
        public static final Variable.Key METAL_OFFSETS = Variable.newKey("METAL_offsets");
        /** key of Variable for number of tubes in CNFET. */
        public static final Variable.Key CARBON_NANOTUBE_COUNT = Variable.newKey("CARBON_NANOTUBE_count");
        /** key of Variable for spacing of tubes in CNFET. */
        public static final Variable.Key CARBON_NANOTUBE_PITCH = Variable.newKey("CARBON_NANOTUBE_pitch");
        /** CUT_ALIGNMENT: cuts centered in the node */
        public static final int MULTICUT_CENTERED = 0;
        /** CUT_ALIGNMENT: cuts spread to edges of node */
        public static final int MULTICUT_SPREAD = 1;
        /** CUT_ALIGNMENT: cuts pushed to corner of node */
        public static final int MULTICUT_CORNER = 2;

        /**
         * Constructs a <CODE>NodeLayer</CODE> with the specified description.
         * @param layer the <CODE>Layer</CODE> this is on.
         * @param portNum a 0-based index of the port (from the actual NodeInst) on this layer.
         * A negative value indicates that this layer is not connected to an electrical layer.
         * @param style the Poly.Type this NodeLayer will generate (polygon, circle, text, etc.).
         * @param representation tells how to interpret "points".  It can be POINTS, BOX, or MULTICUTBOX.
         * @param points the list of coordinates (stored as TechPoints) associated with this NodeLayer.
         */
        public NodeLayer(Layer layer, int portNum, Poly.Type style, int representation, TechPoint[] points) {
            this.layer = layer;
            this.portNum = portNum;
            this.style = style;
            this.representation = representation;
            this.points = points.clone();
            descriptor = TextDescriptor.EMPTY;
            this.lWidth = this.rWidth = this.extentT = this.extendB = ECoord.ZERO;
        }

        /**
         * Constructs a <CODE>NodeLayer</CODE> with the specified description.
         * This form of the method, with 4 additional parameters at the end,
         * is only used for serpentine transistors.
         * @param layer the <CODE>Layer</CODE> this is on.
         * @param portNum a 0-based index of the port (from the actual NodeInst) on this layer.
         * A negative value indicates that this layer is not connected to an electrical layer.
         * @param style the Poly.Type this NodeLayer will generate (polygon, circle, text, etc.).
         * @param representation tells how to interpret "points".  It can be POINTS, BOX, or MULTICUTBIX.
         * @param points the list of coordinates (stored as TechPoints) associated with this NodeLayer.
         * @param lWidth the left extension of this layer, measured from the <I>centerline</I>.
         * The centerline is the path that the serpentine transistor follows (it defines the path of the
         * polysilicon).  So, for example, if lWidth is 4 and rWidth is 4, it creates a NodeLayer that is 8 wide
         * (with 4 to the left and 4 to the right of the centerline).
         * Left and Right widths define the size of the Active layers.
         * @param rWidth the right extension the right of this layer, measured from the <I>centerline</I>.
         * @param extentT the top extension of this layer, measured from the end of the <I>centerline</I>.
         * The top and bottom extensions apply to the ends of the centerline, and not to each segment
         * along it.  They define the extension of the polysilicon.  For example, if extendT is 2,
         * it indicates that the NodeLayer extends by 2 from the top end of the centerline.
         * @param extendB the bottom extension of this layer, measured from the end of the <I>centerline</I>.
         */
        public NodeLayer(Layer layer, int portNum, Poly.Type style, int representation, TechPoint[] points,
                ECoord lWidth, ECoord rWidth, ECoord extentT, ECoord extendB) {
            this.layer = layer;
            this.portNum = portNum;
            this.style = style;
            this.representation = representation;
            this.points = points.clone();
            descriptor = TextDescriptor.EMPTY;
            this.lWidth = lWidth;
            this.rWidth = rWidth;
            this.extentT = extentT;
            this.extendB = extendB;
        }

        /**
         * Constructs a <CODE>NodeLayer</CODE> from given node
         * @param node
         */
        public NodeLayer(NodeLayer node) {
            this.layer = node.getLayer();
            this.portNum = node.getPortNum();
            this.style = node.getStyle();
            this.representation = node.getRepresentation();
            this.descriptor = TextDescriptor.EMPTY;
            this.points = node.getPoints().clone();
            this.lWidth = this.rWidth = this.extentT = this.extendB = ECoord.ZERO;
        }

        public static NodeLayer makeMulticut(Layer layer, int portNum, Poly.Type style, TechPoint[] techPoints,
                double sizeX, double sizeY, double sep1d, double sep2d) {
            NodeLayer nl = new NodeLayer(layer, portNum, style, Technology.NodeLayer.MULTICUTBOX, techPoints);
            nl.cutSizeX = ECoord.fromLambdaRoundGrid(sizeX);
            nl.cutSizeY = ECoord.fromLambdaRoundGrid(sizeY);
            nl.cutSep1D = ECoord.fromLambdaRoundGrid(sep1d);
            nl.cutSep2D = ECoord.fromLambdaRoundGrid(sep2d);
            return nl;
        }

        public static NodeLayer makeMulticut(Layer layer, int portNum, Poly.Type style, TechPoint[] techPoints,
                ECoord sizeX, ECoord sizeY, ECoord sep1d, ECoord sep2d) {
            NodeLayer nl = new NodeLayer(layer, portNum, style, Technology.NodeLayer.MULTICUTBOX, techPoints);
            nl.cutSizeX = sizeX;
            nl.cutSizeY = sizeY;
            nl.cutSep1D = sep1d;
            nl.cutSep2D = sep2d;
            return nl;
        }

        /**
         * Returns the <CODE>Layer</CODE> object associated with this NodeLayer.
         * @return the <CODE>Layer</CODE> object associated with this NodeLayer.
         */
        public Layer getLayer() {
            return layer;
        }

        /**
         * Returns the 0-based index of the port associated with this NodeLayer.
         * @return the 0-based index of the port associated with this NodeLayer.
         */
        public int getPortNum() {
            return portNum;
        }

        /**
         * Returns the port associated with this NodeLayer in specified PrimitiveNode.
         * @param pn specified PrimitiveNode
         * @return the port associated with this NodeLayer.
         */
        public PrimitivePort getPort(PrimitiveNode pn) {
            return portNum >= 0 ? pn.getPort(portNum) : null;
        }

        /**
         * Returns the Poly.Type this NodeLayer will generate.
         * @return the Poly.Type this NodeLayer will generate.
         * Examples are polygon, lines, splines, circle, text, etc.
         */
        public Poly.Type getStyle() {
            return style;
        }

        /**
         * Returns the method of interpreting "points".
         * @return the method of interpreting "points".
         * It can be POINTS, BOX, MINBOX, or MULTICUTBOX.
         */
        public int getRepresentation() {
            return representation;
        }

        public static String getRepresentationName(int rep) {
            if (rep == POINTS) {
                return "points";
            }
            if (rep == BOX) {
                return "box";
            }
//			if (rep == MINBOX) return "min-box";
            if (rep == MULTICUTBOX) {
                return "multi-cut-box";
            }
            return "?";
        }

        /**
         * Returns the list of coordinates (stored as TechPoints) associated with this NodeLayer.
         * @return the list of coordinates (stored as TechPoints) associated with this NodeLayer.
         */
        public TechPoint[] getPoints() {
            return points;
        }

//        /**
//         * Method to set new points to this NodeLayer
//         * @param pts
//         */
//        public void setPoints(TechPoint [] pts) {points = pts; }
        /**
         * Returns the left edge coordinate (a scalable EdgeH object) associated with this NodeLayer.
         * @return the left edge coordinate associated with this NodeLayer.
         * It only makes sense if the representation is BOX or MINBOX.
         * The returned coordinate is a scalable EdgeH object.
         */
        public EdgeH getLeftEdge() {
            return points[0].getX();
        }

        /**
         * Returns the bottom edge coordinate (a scalable EdgeV object) associated with this NodeLayer.
         * @return the bottom edge coordinate associated with this NodeLayer.
         * It only makes sense if the representation is BOX or MINBOX.
         * The returned coordinate is a scalable EdgeV object.
         */
        public EdgeV getBottomEdge() {
            return points[0].getY();
        }

        /**
         * Returns the right edge coordinate (a scalable EdgeH object) associated with this NodeLayer.
         * @return the right edge coordinate associated with this NodeLayer.
         * It only makes sense if the representation is BOX or MINBOX.
         * The returned coordinate is a scalable EdgeH object.
         */
        public EdgeH getRightEdge() {
            return points[1].getX();
        }

        /**
         * Returns the top edge coordinate (a scalable EdgeV object) associated with this NodeLayer.
         * @return the top edge coordinate associated with this NodeLayer.
         * It only makes sense if the representation is BOX or MINBOX.
         * The returned coordinate is a scalable EdgeV object.
         */
        public EdgeV getTopEdge() {
            return points[1].getY();
        }

        /**
         * Returns the text message associated with this list NodeLayer.
         * @return the text message associated with this list NodeLayer.
         * This only makes sense if the style is one of the TEXT types.
         */
        public String getMessage() {
            return message;
        }

        /**
         * Sets the text to be drawn by this NodeLayer.
         * @param message the text to be drawn by this NodeLayer.
         * This only makes sense if the style is one of the TEXT types.
         */
        public void setMessage(String message) {
            this.message = message;
        }

        /**
         * Returns the text descriptor associated with this list NodeLayer.
         * @return the text descriptor associated with this list NodeLayer.
         * This only makes sense if the style is one of the TEXT types.
         */
        public TextDescriptor getDescriptor() {
            return descriptor;
        }

        /**
         * Sets the text descriptor to be drawn by this NodeLayer.
         * @param descriptor the text descriptor to be drawn by this NodeLayer.
         * This only makes sense if the style is one of the TEXT types.
         */
        public void setDescriptor(TextDescriptor descriptor) {
            this.descriptor = descriptor;
        }

        /**
         * Returns the left extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @return the left extension of this layer.
         */
        public ECoord getSerpentineLWidth() {
            return lWidth;
        }

        /**
         * Sets the left extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @param lWidth the left extension of this layer.
         */
        public void setSerpentineLWidth(double lWidth) {
            this.lWidth = ECoord.fromLambdaRoundGrid(lWidth);
        }

        /**
         * Returns the right extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @return the right extension of this layer.
         */
        public ECoord getSerpentineRWidth() {
            return rWidth;
        }

        /**
         * Sets the right extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @param rWidth the right extension of this layer.
         */
        public void setSerpentineRWidth(double rWidth) {
            this.rWidth = ECoord.fromLambdaRoundGrid(rWidth);
        }

        /**
         * Returns the top extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @return the top extension of this layer.
         */
        public ECoord getSerpentineExtentT() {
            return extentT;
        }

        /**
         * Sets the top extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @param extentT the top extension of this layer.
         */
        public void setSerpentineExtentT(double extentT) {
            this.extentT = ECoord.fromLambdaRoundGrid(extentT);
        }

        /**
         * Returns the bottom extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @return the bottom extension of this layer.
         */
        public ECoord getSerpentineExtentB() {
            return extendB;
        }

        /**
         * Sets the bottom extension of this layer.
         * Only makes sense when this is a layer in a serpentine transistor.
         * @param extendB the bottom extension of this layer.
         */
        public void setSerpentineExtentB(double extendB) {
            this.extendB = ECoord.fromLambdaRoundGrid(extendB);
        }

        public ECoord getMulticutSizeX() {
            return cutSizeX;
        }

        public ECoord getMulticutSizeY() {
            return cutSizeY;
        }

        public ECoord getMulticutSep1D() {
            return cutSep1D;
        }

        public ECoord getMulticutSep2D() {
            return cutSep2D;
        }

        void dump(PrintWriter out, EPoint correction, boolean isSerp) {
            out.println("\tlayer=" + getLayer().getName() + " port=" + getPortNum() + " style=" + getStyle().name() + " repr=" + getRepresentation());
            if (getMessage() != null) {
                TextDescriptor td = getDescriptor();
                out.println("\t\tmessage=\"" + getMessage() + "\" td=" + Long.toHexString(td.lowLevelGet()) + " colorIndex=" + td.getColorIndex() + " disp=" + td.isDisplay());
            }
            if (getMulticutSizeX().signum() != 0 || getMulticutSizeY().signum() != 0 || getMulticutSep1D().signum() != 0 || getMulticutSep2D().signum() != 0) {
                out.println("\t\tmultiSizeX=" + getMulticutSizeX() + " multiSizeY=" + getMulticutSizeY() + " multiSep=" + getMulticutSep1D() + " multiSpe2D=" + getMulticutSep2D());
            }

            if (isSerp) {
                out.println("\t\tLWidth=" + getSerpentineLWidth() + " rWidth=" + getSerpentineRWidth() + " bExtend=" + getSerpentineExtentB() + " tExtend=" + getSerpentineExtentT());
            }
            for (Technology.TechPoint p : getPoints()) {
                out.println("\t\tpoint xm=" + p.getX().getMultiplier() + " xa=" + DBMath.round(p.getX().getAdder().getLambda() - p.getX().getMultiplier() * correction.getLambdaX())
                        + " ym=" + p.getY().getMultiplier() + " ya=" + DBMath.round(p.getY().getAdder().getLambda() - p.getY().getMultiplier() * correction.getLambdaY()));
            }
        }

        public Xml.NodeLayer makeXml(boolean isSerp, boolean inLayers, boolean inElectricalLayers, Map<Object,Map<String,Object>> additionalAttributes) {
            Xml.NodeLayer nld = new Xml.NodeLayer();
            nld.layer = getLayer().getName();
            nld.style = getStyle();
            nld.portNum = getPortNum();
            nld.inLayers = inLayers;
            nld.inElectricalLayers = inElectricalLayers;
            nld.representation = getRepresentation();
            Technology.TechPoint[] points = getPoints();
            if (nld.representation == Technology.NodeLayer.BOX || nld.representation == Technology.NodeLayer.MULTICUTBOX) {
                nld.lx.k = points[0].getX().getMultiplier() * 2;
                nld.lx.addLambda(DBMath.round(points[0].getX().getAdder().getLambda()));
                nld.hx.k = points[1].getX().getMultiplier() * 2;
                nld.hx.addLambda(DBMath.round(points[1].getX().getAdder().getLambda()));
                nld.ly.k = points[0].getY().getMultiplier() * 2;
                nld.ly.addLambda(DBMath.round(points[0].getY().getAdder().getLambda()));
                nld.hy.k = points[1].getY().getMultiplier() * 2;
                nld.hy.addLambda(DBMath.round(points[1].getY().getAdder().getLambda()));
            } else {
                for (Technology.TechPoint p : points) {
                    nld.techPoints.add(p);
                }
            }
            if (nld.representation == Technology.NodeLayer.MULTICUTBOX) {
                nld.sizex = getMulticutSizeX().getLambda();
                nld.sizey = getMulticutSizeY().getLambda();
                nld.sep1d = getMulticutSep1D().getLambda();
                nld.sep2d = getMulticutSep2D().getLambda();
            }
            if (isSerp) {
                nld.lWidth = getSerpentineLWidth().getLambda();
                nld.rWidth = getSerpentineRWidth().getLambda();
                nld.tExtent = getSerpentineExtentT().getLambda();
                nld.bExtent = getSerpentineExtentB().getLambda();
            }
            if (additionalAttributes != null && getMessage() != null) {
                LinkedHashMap<String,Object> attrs = new LinkedHashMap<String,Object>();
                attrs.put("message", getMessage());
                attrs.put("relSize", getDescriptor().getSize().getSize());
                additionalAttributes.put(nld, attrs);
            }
            return nld;
        }
    }

    public Map<TechFactory.Param, Object> getParamValues() {
        return paramValues;
    }

    protected Map<TechFactory.Param, Object> checkParamValues(Map<TechFactory.Param, Object> paramValues) {
        LinkedHashMap<TechFactory.Param, Object> fixedParamValues = new LinkedHashMap<TechFactory.Param, Object>();
        for (TechFactory.Param param : techFactory.getTechParams()) {
            Object value = paramValues.get(param);
            if (value == null || value.getClass() != param.factoryValue.getClass()) {
                value = param.factoryValue;
            }
            fixedParamValues.put(param, value);
        }
        return Collections.unmodifiableMap(fixedParamValues);
    }

    public class SizeCorrector {

        public final HashMap<ArcProtoId, ECoord> arcExtends = new HashMap<ArcProtoId, ECoord>();
        public final HashMap<PrimitiveNodeId, EPoint> nodeExtends = new HashMap<PrimitiveNodeId, EPoint>();

        private SizeCorrector(Version version, boolean isJelib) {
            int techVersion = 0;
            if (isJelib) {
                if (version.compareTo(DISK_VERSION_2) >= 0) {
                    techVersion = 2;
                } else if (version.compareTo(DISK_VERSION_1) >= 0) {
                    techVersion = 1;
                }
            }
            for (ArcProto ap : arcs.values()) {
                ECoord correction = ECoord.ZERO;
                switch (techVersion) {
                    case 0:
                        correction = ap.getBaseExtend().add(ECoord.fromLambdaRoundGrid(0.5 * ap.getLambdaElibWidthOffset()));
                        break;
                    case 1:
                        correction = ap.getBaseExtend();
                        break;
                }
                arcExtends.put(ap.getId(), correction);
            }
            for (PrimitiveNode pn : nodes.values()) {
                EPoint correction = techVersion == 2 ? EPoint.ORIGIN : pn.getSizeCorrector(techVersion);
//                switch (techVersion) {
//                    case 0:
//                        correction = EPoint.fromGrid(-pn.sizeCorrector.getGridX(), -pn.sizeCorrector.getGridY());
//                        break;
//                    case 1:
//                        SizeOffset so = pn.getProtoSizeOffset();
//                        double lambdaX = -0.5*(so.getLowXOffset() + so.getHighXOffset()) - pn.sizeCorrector.getLambdaX();
//                        double lambdaY = -0.5*(so.getLowYOffset() + so.getHighYOffset()) - pn.sizeCorrector.getLambdaY();
//                        correction = EPoint.fromLambda(lambdaX, lambdaY);
//                        break;
//                }
                nodeExtends.put(pn.getId(), correction);
            }
        }

        public boolean isIdentity() {
            for (ECoord arcExtend : arcExtends.values()) {
                if (arcExtend.signum() != 0) {
                    return false;
                }
            }
            for (EPoint nodeExtend : nodeExtends.values()) {
                if (nodeExtend.getX() != 0 || nodeExtend.getY() != 0) {
                    return false;
                }
            }
            return true;
        }

        public long getExtendFromDisk(ArcProto ap, double width) {
            return DBMath.lambdaToGrid(0.5 * width) - arcExtends.get(ap.getId()).getGrid();
        }

        public long getExtendToDisk(ImmutableArcInst a) {
            return a.getGridExtendOverMin() + arcExtends.get(a.protoId).getGrid();
        }

        public long getWidthToDisk(ImmutableArcInst a) {
            return 2 * getExtendToDisk(a);
        }

        public EPoint getSizeFromDisk(PrimitiveNode pn, EPoint size) {
            EPoint correction = nodeExtends.get(pn.getId());
            return EPoint.fromGrid(size.getGridX() - 2 * correction.getGridX(), size.getGridY() - 2 * correction.getGridY());
        }

        public EPoint getSizeToDisk(ImmutableNodeInst n) {
            EPoint size = n.size;
            EPoint correction = nodeExtends.get(n.protoId);
            if (!correction.equals(EPoint.ORIGIN)) {
                size = EPoint.fromLambda(size.getLambdaX() + 2 * correction.getLambdaX(), size.getLambdaY() + 2 * correction.getLambdaY());
            }
            return size;
        }
    }

    public SizeCorrector getSizeCorrector(Version version, Map<Setting, Object> projectSettings, boolean isJelib, boolean keepExtendOverMin) {
        return new SizeCorrector(version, isJelib);
    }

    protected void setArcCorrection(SizeCorrector sc, String arcName, double lambdaBaseWidth) {
        ArcProto ap = findArcProto(arcName);
        ECoord correction = sc.arcExtends.get(ap.getId());
        ECoord baseExtend = ECoord.fromLambdaRoundGrid(0.5 * lambdaBaseWidth);
        if (!baseExtend.equals(ap.getBaseExtend())) {
            correction = correction.add(baseExtend).subtract(ap.getBaseExtend());
            sc.arcExtends.put(ap.getId(), correction);
        }
    }

    protected void setNodeCorrection(SizeCorrector sc, String nodeName, double lambdaBaseWidth, double lambdaBaseHeight) {
        PrimitiveNode np = findNodeProto(nodeName);
        EPoint correction = sc.nodeExtends.get(np.getId());
        int gridWidthExtend = (int) DBMath.lambdaToGrid(0.5 * lambdaBaseWidth);
        int gridHeightExtend = (int) DBMath.lambdaToGrid(0.5 * lambdaBaseHeight);
        ERectangle baseRectangle = np.getBaseRectangle();
        if (gridWidthExtend != baseRectangle.getGridWidth() / 2 || gridHeightExtend != baseRectangle.getGridHeight() / 2) {
            correction = EPoint.fromGrid(
                    correction.getGridX() + gridWidthExtend - baseRectangle.getGridWidth() / 2,
                    correction.getGridY() + gridHeightExtend - baseRectangle.getGridHeight() / 2);
            sc.nodeExtends.put(np.getId(), correction);
        }
    }
    /** technology is not electrical */
    private static final int NONELECTRICAL = 01;
    /** has no directional arcs */
    private static final int NODIRECTIONALARCS = 02;
    /** has no negated arcs */
    private static final int NONEGATEDARCS = 04;
    /** nonstandard technology (cannot be edited) */
    private static final int NONSTANDARD = 010;
    /** statically allocated (don't deallocate memory) */
    private static final int STATICTECHNOLOGY = 020;
    /** no primitives in this technology (don't auto-switch to it) */
    private static final int NOPRIMTECHNOLOGY = 040;
//	/** the current technology in Electric */				private static Technology curTech = null;
//	/** the current layout technology in Electric */		private static Technology curLayoutTech = null;
    /** Generic technology for this Technology */
    final Generic generic;
    /** name of this technology */
    private final TechId techId;
    /** short, readable name of this technology */
    private String techShortName;
    /** full description of this technology */
    private String techDesc;
    /** flags for this technology */
    private int userBits;
    /** true if "scale" is relevant to this technology */
    private boolean scaleRelevant;
    /** Setting Group for this Technology */
    private final Setting.RootGroup rootSettings;
    /** Setting Group for this Technology */
    private final Setting.Group settings;
    /** factory transparent colors for this technology */
    private Color[] factoryTransparentColors = {};
    /** list of layers in this technology */
    private final List<Layer> layers = new ArrayList<Layer>();
    /** map from layer names to layers in this technology */
    private final HashMap<String, Layer> layersByName = new HashMap<String, Layer>();
    /** array of layers by layerId.chronIndex */
    private Layer[] layersByChronIndex = {};
    /** True when layer allocation is finished. */
    private boolean layersAllocationLocked;
    /** list of primitive nodes in this technology */
    private final LinkedHashMap<String, PrimitiveNode> nodes = new LinkedHashMap<String, PrimitiveNode>();
    /** array of nodes by nodeId.chronIndex */
    private PrimitiveNode[] nodesByChronIndex = {};
    /** Old names of primitive nodes */
    protected final HashMap<String, PrimitiveNode> oldNodeNames = new HashMap<String, PrimitiveNode>();
    /** count of primitive nodes in this technology */
    private int nodeIndex = 0;
    /** list of node groups in this technology */
    final ArrayList<PrimitiveNodeGroup> primitiveNodeGroups = new ArrayList<PrimitiveNodeGroup>();
    /** list of arcs in this technology */
    private final LinkedHashMap<String, ArcProto> arcs = new LinkedHashMap<String, ArcProto>();
    /** array of arcs by arcId.chronIndex */
    private ArcProto[] arcsByChronIndex = {};
    /** Old names of arcs */
    protected final HashMap<String, ArcProto> oldArcNames = new HashMap<String, ArcProto>();
    /** Spice header cards, level 1. */
    private String[] spiceHeaderLevel1;
    /** Spice header cards, level 2. */
    private String[] spiceHeaderLevel2;
    /** Spice header cards, level 3. */
    private String[] spiceHeaderLevel3;
    /** Factory resolution for this Technology */
    private ECoord factoryResolution = ECoord.ZERO;
    /** static list of all Manufacturers in Electric */
    protected final List<Foundry> foundries = new ArrayList<Foundry>();
    /** default foundry Setting for this Technology */
    private final Setting cacheFoundry;
    /** default foundry name for this Technology */
    protected String paramFoundry;
    /** scale for this Technology. */
    private Setting cacheScale;
    /** number of metals Setting for this Technology. */
    private final Setting cacheNumMetalLayers;
    /** number of metals for this Technology. */
    protected Integer paramNumMetalLayers;
    /** Minimum resistance for this Technology. */
    private Setting cacheMinResistance;
    /** Minimum capacitance for this Technology. */
    private Setting cacheMinCapacitance;
    /** Gate Length subtraction (in microns) for this Tech*/
    private final Setting cacheGateLengthSubtraction;
    /** Include gate in Resistance calculation */
    private final Setting cacheIncludeGate;
    /** Include ground network in parasitics calculation */
    private final Setting cacheIncludeGnd;
    /** Include ground network in parasitics calculation */
    private final Setting cacheMaxSeriesResistance;
//	/** Logical effort global fanout preference. */			private final Setting cacheGlobalFanout;
//	/** Logical effort convergence (epsilon) preference. */	private final Setting cacheConvergenceEpsilon;
//	/** Logical effort maximum iterations preference. */	private final Setting cacheMaxIterations;
    /** Logical effort gate capacitance preference. */
    private Setting cacheGateCapacitance;
    /** Logical effort wire ratio preference. */
    private Setting cacheWireRatio;
    /** Factory rules for the technology. */
	protected XMLRules factoryRules = null;
	/** Logical effort diff alpha preference. */
    private Setting cacheDiffAlpha;
//	/** Logical effort keeper ratio preference. */			private final Setting cacheKeeperRatio;
//	/** Default Logical effort global fanout. */			private static double DEFAULT_GLOBALFANOUT = 4.7;
//	/** Default Logical effort convergence (epsilon). */	private static double DEFAULT_EPSILON      = 0.001;
//	/** Default Logical effort maximum iterations. */		private static int    DEFAULT_MAXITER      = 30;
//	/** Default Logical effort keeper ratio. */				private static double DEFAULT_KEEPERRATIO  = 0.1;
    /** Default Logical effort gate capacitance. */
    private static double DEFAULT_GATECAP = Xml.DEFAULT_LE_GATECAP;
    /** Default Logical effort wire ratio. */
    private static double DEFAULT_WIRERATIO = Xml.DEFAULT_LE_WIRERATIO;
    /** Default Logical effort diff alpha. */
    private static double DEFAULT_DIFFALPHA = Xml.DEFAULT_LE_DIFFALPHA;
    /** indicates n-type objects. */
    public static final int N_TYPE = 1;
    /** indicates p-type objects. */
    public static final int P_TYPE = 0;
    /** Cached rules for the technology. */
    protected XMLRules cachedRules = null;
    /** TechFactory which created this Technology */
    protected final TechFactory techFactory;
    /** Parameters of this Technology */
    private final Map<TechFactory.Param, Object> paramValues;
    /** Xml representation of this Technology */
    protected Xml.Technology xmlTech;
    /** Xml representation of menu palette */
    protected Xml.MenuPalette factoryMenuPalette;

    /****************************** CONTROL ******************************/
    /**
     * Constructs a <CODE>Technology</CODE>.
     * This should not be called directly, but instead is invoked through each subclass's factory.
     */
    protected Technology(Generic generic, TechFactory techFactory) {
        this(generic, techFactory, Foundry.Type.NONE, 0);
    }

    /**
     * Constructs a <CODE>Technology</CODE>.
     * This should not be called directly, but instead is invoked through each subclass's factory.
     */
    protected Technology(Generic generic, TechFactory techFactory, Foundry.Type defaultFoundry, int defaultNumMetals) {
        this(generic.getId().idManager, generic, techFactory, Collections.<TechFactory.Param, Object>emptyMap(), defaultFoundry, defaultNumMetals);
    }

    /**
     * Constructs a <CODE>Technology</CODE>.
     * This should not be called directly, but instead is invoked through each subclass's factory.
     */
    protected Technology(Generic generic, TechFactory techFactory, Map<TechFactory.Param, Object> techParams, Foundry.Type defaultFoundry, int defaultNumMetals) {
        this(generic.getId().idManager, generic, techFactory, techParams, defaultFoundry, defaultNumMetals);
    }

    /**
     * Constructs a <CODE>Technology</CODE>.
     * This should not be called directly, but instead is invoked through each subclass's factory.
     */
    protected Technology(IdManager idManager, Generic generic, TechFactory techFactory, Map<TechFactory.Param, Object> techParams, Foundry.Type defaultFoundry, int defaultNumMetals) {
        if (this instanceof Generic) {
            assert generic == null;
            generic = (Generic) this;
        }
        this.generic = generic;
        this.techId = idManager.newTechId(techFactory.techName);
        this.techFactory = techFactory;
        assert techParams.size() == techFactory.getTechParams().size();
        for (TechFactory.Param param : techFactory.getTechParams()) {
            assert techParams.get(param).getClass() == param.factoryValue.getClass();
        }
        paramValues = checkParamValues(techParams);
        //this.scale = 1.0;
        this.scaleRelevant = true;
        userBits = 0;
        rootSettings = new Setting.RootGroup();
        settings = rootSettings.node(getTechName());
        cacheFoundry = makeStringSetting("SelectedFoundryFor" + getTechName(),
                "Technology tab", getTechName() + " foundry", "Foundry", defaultFoundry.getName());
        paramFoundry = defaultFoundry.getName();
        cacheNumMetalLayers = makeIntSetting(getTechName() + "NumberOfMetalLayers",
                "Technology tab", getTechName() + ": Number of Metal Layers", "NumMetalLayers", defaultNumMetals);
        paramNumMetalLayers = Integer.valueOf(defaultNumMetals);

        cacheMaxSeriesResistance = makeParasiticSetting("MaxSeriesResistance", 10.0);
        cacheGateLengthSubtraction = makeParasiticSetting("GateLengthSubtraction", 0.0);
        cacheIncludeGate = makeParasiticSetting("Gate Inclusion", false);
        cacheIncludeGnd = makeParasiticSetting("Ground Net Inclusion", false);
//      cacheGlobalFanout = makeLESetting("GlobalFanout", DEFAULT_GLOBALFANOUT);
//		cacheConvergenceEpsilon = makeLESetting("ConvergenceEpsilon", DEFAULT_EPSILON);
//		cacheMaxIterations = makeLESetting("MaxIterations", DEFAULT_MAXITER);
//		cacheGateCapacitance = makeLESetting("GateCapacitance", DEFAULT_GATECAP);
//		cacheWireRatio = makeLESetting("WireRatio", DEFAULT_WIRERATIO);
//		cacheDiffAlpha = makeLESetting("DiffAlpha", DEFAULT_DIFFALPHA);
//        cacheKeeperRatio = makeLESetting("KeeperRatio", DEFAULT_KEEPERRATIO);
    }

    protected Object writeReplace() {
        return new TechnologyKey(this);
    }

    private static class TechnologyKey extends EObjectInputStream.Key<Technology> {

        public TechnologyKey() {
        }

        private TechnologyKey(Technology tech) {
            super(tech);
        }

        @Override
        public void writeExternal(EObjectOutputStream out, Technology tech) throws IOException {
            TechId techId = tech.getId();
            if (techId.idManager != out.getIdManager()) {
                throw new NotSerializableException(tech + " from other IdManager");
            }
            if (out.getDatabase().getTechPool().getTech(techId) != tech) {
                throw new NotSerializableException(tech + " not linked");
            }
            out.writeInt(techId.techIndex);
        }

        @Override
        public Technology readExternal(EObjectInputStream in) throws IOException, ClassNotFoundException {
            int techIndex = in.readInt();
            TechId techId = in.getIdManager().getTechId(techIndex);
            Technology tech = in.getDatabase().getTech(techId);
            if (tech == null) {
                throw new InvalidObjectException(techId + " not linked");
            }
            return tech;
        }
    }

    public Technology(Generic generic, TechFactory techFactory, Map<TechFactory.Param, Object> techParams, Xml.Technology t) {
        this(generic, techFactory, techParams, Foundry.Type.valueOf(t.defaultFoundry), t.defaultNumMetals);
        xmlTech = t;
        factoryMenuPalette = t.menuPalette;
        setTechShortName(t.shortTechName);
        setTechDesc(t.description);
        setFactoryScale(t.scaleValue, t.scaleRelevant);
        setFactoryResolution(t.resolutionValue);
        setFactoryParasitics(t.minResistance, t.minCapacitance);
        setFactoryLESettings(t.leGateCapacitance, t.leWireRatio, t.leDiffAlpha);
        if (!t.transparentLayers.isEmpty()) {
            setFactoryTransparentLayers(t.transparentLayers.toArray(new Color[t.transparentLayers.size()]));
        }
        HashMap<String, Layer> layers = new HashMap<String, Layer>();
        for (Xml.Layer l : t.layers) {
            Layer layer = Layer.newInstance(this, l.name, l.desc);
            layers.put(l.name, layer);
            layer.setFunction(l.function, l.extraFunction);
            layer.setFactoryParasitics(l.resistance, l.capacitance, l.edgeCapacitance);
            layer.setFactoryCIFLayer(l.cif != null ? l.cif : "");
            layer.setFactoryDXFLayer("");
            layer.setFactorySkillLayer(l.skill != null ? l.skill : "");
            layer.setFactory3DInfo(l.thick3D, l.height3D);
        }
        HashMap<String, ArcProto> arcs = new HashMap<String, ArcProto>();
        for (Xml.ArcProto a : t.arcs) {
            if (findArcProto(a.name) != null) {
                System.out.println("Error: technology " + getTechName() + " has multiple arcs named " + a.name);
                continue;
            }

            // Check if there is any pin defined to connect to otherwise it should not create the arc!
            if (t.findPinNode(a.name) == null) {
                System.out.println("Error: no pin found for arc '" + a.name + "'");
            }

            ArcLayer[] arcLayers = new ArcLayer[a.arcLayers.size()];
            for (int i = 0; i < arcLayers.length; i++) {
                Xml.ArcLayer al = a.arcLayers.get(i);
                arcLayers[i] = new ArcLayer(layers.get(al.layer), al.style, al.extend.value);
            }
            Double diskOffset1 = a.diskOffset.get(Integer.valueOf(1));
            Double diskOffset2 = a.diskOffset.get(Integer.valueOf(2));
            long halfElibWidthOffset = 0;
            if (diskOffset1 != null && diskOffset2 != null) {
                halfElibWidthOffset = DBMath.lambdaToGrid(diskOffset1.doubleValue() - diskOffset2.doubleValue());
            }
            ArcProto ap;
            if (a.curvable) {
                ap = new ArcProto.Curvable(this, a.name, DBMath.gridToLambda(halfElibWidthOffset * 2), a.function, arcLayers, arcs.size());
            } else {
                ap = new ArcProto(this, a.name, DBMath.gridToLambda(halfElibWidthOffset * 2), a.function, arcLayers, arcs.size());
            }
            addArcProto(ap);

            if (a.oldName != null) {
                oldArcNames.put(a.oldName, ap);
            }
            arcs.put(a.name, ap);
            if (a.wipable) {
                ap.setWipable();
            }
            if (a.special) {
                ap.setSpecialArc();
            }
            if (a.notUsed) {
                ap.setNotUsed(true);
            }
            if (a.skipSizeInPalette) {
                ap.setSkipSizeInPalette();
            }
            ap.setFactoryExtended(a.extended);
            ap.setFactoryFixedAngle(a.fixedAngle);
            ap.setFactoryAngleIncrement(a.angleIncrement);
            ap.setFactoryAntennaRatio(a.antennaRatio);
//            if (a.arcPin != null)
//                ap.makeWipablePin(a.arcPin.name, a.arcPin.portName, a.arcPin.elibSize, makeConnections(a.arcPin.portArcs, arcs));
        }
        setNoNegatedArcs();
        for (Xml.PrimitiveNodeGroup ng : t.nodeGroups) {
            PrimitiveNodeGroup.makePrimitiveNodes(this, ng, layers, arcs);
        }
        for (Xml.Layer l : t.layers) {
            if (l.pureLayerNode == null) {
                continue;
            }
            Layer layer = layers.get(l.name);
            PrimitiveNode pn = layer.makePureLayerNode(l.pureLayerNode.name, l.pureLayerNode.size.value, l.pureLayerNode.style,
                    l.pureLayerNode.port, makeConnections(l.pureLayerNode.name, l.pureLayerNode.port, l.pureLayerNode.portArcs, arcs));
            if (l.pureLayerNode.oldName != null) {
                oldNodeNames.put(l.pureLayerNode.oldName, pn);
            }
        }
        for (Xml.SpiceHeader h : t.spiceHeaders) {
            String[] spiceLines = h.spiceLines.toArray(new String[h.spiceLines.size()]);
            switch (h.level) {
                case 1:
                    setSpiceHeaderLevel1(spiceLines);
                    break;
                case 2:
                    setSpiceHeaderLevel2(spiceLines);
                    break;
                case 3:
                    setSpiceHeaderLevel3(spiceLines);
                    break;
            }
        }
        for (Xml.Foundry f : t.foundries) {
            ArrayList<String> gdsLayers = new ArrayList<String>();
            for (Layer layer : this.layers) {
                String gds = f.layerGds.get(layer.getName());
                if (gds == null) {
                    continue;
                }
                gdsLayers.add(layer.getName() + " " + gds);
            }
            Foundry foundry = new Foundry(this, Foundry.Type.valueOf(f.name), f.rules, gdsLayers.toArray(new String[gdsLayers.size()]));
            foundries.add(foundry);
        }
    }

    static ArcProto[] makeConnections(String nodeName, String portName, List<String> portArcs, Map<String, ArcProto> arcs) {
        ArcProto[] connections = new ArcProto[portArcs.size()];
        for (int j = 0; j < connections.length; j++) {
            ArcProto ap = arcs.get(portArcs.get(j));
            if (ap == null) {
                String error = "No such arcProto '" + portArcs.get(j) + "' found for node '" + nodeName
                        + "', port '" + portName + "'";
                throw new NoSuchElementException(error);
            }
            connections[j] = ap;
        }
        return connections;
    }

    static TechPoint makeTechPoint(Xml.Distance x, Xml.Distance y) {
        return new TechPoint(makeEdgeH(x), makeEdgeV(y));
    }

    static EdgeH makeEdgeH(Xml.Distance x) {
        return new EdgeH(x.k * 0.5, x.value);
    }

    static EdgeV makeEdgeV(Xml.Distance y) {
        return new EdgeV(y.k * 0.5, y.value);
    }

    public boolean isXmlTechAvailable() {
        return xmlTech != null;
    }

    public Xml.Technology getXmlTech() {
        return xmlTech != null ? xmlTech.deepClone() : null;
    }

    public static Environment makeInitialEnvironment() {
        Environment env = IdManager.stdIdManager.getInitialEnvironment();
        env = env.withToolSettings((Setting.RootGroup) ToolSettings.getToolSettings(""));
        Generic generic = Generic.newInstance(IdManager.stdIdManager);
        env = env.addTech(generic);
        for (TechFactory techFactory : TechFactory.getKnownTechs().values()) {
            Map<TechFactory.Param, Object> paramValues = paramValuesFromPreferences(techFactory);
            Technology tech = techFactory.newInstance(generic, paramValues);
            if (tech != null) {
                env = env.addTech(tech);
            }
        }

        Setting.SettingChangeBatch changeBatch = new Setting.SettingChangeBatch();
        Preferences prefRoot = Pref.getPrefRoot();
        for (Setting setting : env.getSettings().keySet()) {
            changeBatch.add(setting, setting.getValueFromPreferences(prefRoot));
        }
        for (Technology t : env.techPool.values()) {
            for (Map.Entry<TechFactory.Param, Object> e : t.getParamValues().entrySet()) {
                TechFactory.Param param = e.getKey();
                changeBatch.add(t.getSetting(param), e.getValue());
            }
        }
        env = env.withSettingChanges(changeBatch);
        return env;
    }

    /**
     * Loads from Java Preferences values of technology parameters.
     * This is called once, at the start of Electric, to initialize the technologies.
     * @return values of technology parameters.
     */
    public static Map<String, Object> getParamValuesByXmlPath() {
        Map<String, Object> paramValuesByXmlPath = new HashMap<String, Object>();
        for (TechFactory techFactory : TechFactory.getKnownTechs().values()) {
            Map<TechFactory.Param, Object> paramValues = Technology.paramValuesFromPreferences(techFactory);
            for (Map.Entry<TechFactory.Param, Object> e : paramValues.entrySet()) {
                paramValuesByXmlPath.put(e.getKey().xmlPath, e.getValue());
            }
        }
        return paramValuesByXmlPath;
    }

    /**
     * This is called once, at the start of Electric, to initialize the technologies.
     * Because of Java's "lazy evaluation", the only way to force the technology constructors to fire
     * and build a proper list of technologies, is to call each class.
     * So, each technology is listed here.  If a new technology is created, this must be added to this list.
     */
    public static void initPreinstalledTechnologies(EDatabase database, Map<String, Object> paramValuesByXmlPath) {
        database.setToolSettings((Setting.RootGroup) ToolSettings.getToolSettings(""));
        assert database.getGeneric() == null;
        Generic generic = Generic.newInstance(database.getIdManager());
        database.addTech(generic);
        for (TechFactory techFactory : TechFactory.getKnownTechs().values()) {
            Map<TechFactory.Param, Object> paramValues = new HashMap<TechFactory.Param, Object>();
            for (TechFactory.Param techParam : techFactory.getTechParams()) {
                Object paramValue = paramValuesByXmlPath.get(techParam.xmlPath);
                if (paramValue == null) {
                    continue;
                }
                paramValues.put(techParam, paramValue);
            }
            Technology tech = techFactory.newInstance(generic, paramValues);
            if (tech != null) {
                database.addTech(tech);
            }
        }
    }

    private static Map<TechFactory.Param, Object> paramValuesFromPreferences(TechFactory techFactory) {
        HashMap<TechFactory.Param, Object> paramValues = new HashMap<TechFactory.Param, Object>();
        for (TechFactory.Param param : techFactory.getTechParams()) {
            String prefPath = param.prefPath;
            int index = prefPath.lastIndexOf('/');
            String prefName = prefPath.substring(index + 1);
            prefPath = prefPath.substring(0, index);
            Preferences prefNode = Pref.getPrefRoot().node(prefPath);
            Object value = null;
            Object factoryValue = param.factoryValue;
            if (factoryValue instanceof Boolean) {
                value = Boolean.valueOf(prefNode.getBoolean(prefName, ((Boolean) factoryValue).booleanValue()));
            } else if (factoryValue instanceof Integer) {
                value = Integer.valueOf(prefNode.getInt(prefName, ((Integer) factoryValue).intValue()));
            } else if (factoryValue instanceof Long) {
                value = Long.valueOf(prefNode.getLong(prefName, ((Long) factoryValue).longValue()));
            } else if (factoryValue instanceof Double) {
                value = Double.valueOf(prefNode.getDouble(prefName, ((Double) factoryValue).doubleValue()));
            } else {
                value = prefNode.get(prefName, factoryValue.toString());
            }
            if (value.equals(factoryValue)) {
                value = factoryValue;
            }
            paramValues.put(param, value);
        }
        return paramValues;
    }

    /**
     * Method to return the MOSIS CMOS technology.
     * @return the MOSIS CMOS technology object.
     */
    public static Technology getMocmosTechnology() {
        return findTechnology("mocmos");
    }

    /**
     * Method to return the TSMC 180 nanometer technology.
     * Since the technology is a "plugin" and not distributed universally, it may not exist.
     * @return the TSMC180 technology object (null if it does not exist).
     */
    public static Technology getTSMC180Technology() {
        return findTechnology("tsmc180");
    }

    /**
     * Method to return the CMOS 90 nanometer technology.
     * Since the technology is a "plugin" and not distributed universally, it may not exist.
     * @return the CMOS90 technology object (null if it does not exist).
     */
    public static Technology getCMOS90Technology() {
        return findTechnology("cmos90");
    }

	/****************************** EXTRA FIELDS IN PRIMITIVE NODEPROTOS ******************************/

	/** extra fields on PrimitiveNodes that users can edit. */
	private static Map<PrimitiveNode,List<ExtraField>> extraFields = new HashMap<PrimitiveNode,List<ExtraField>>();

	/**
	 * Class to define extra fields on a PrimitiveNode that can be user-parameterized.
	 */
	public static class ExtraField implements Serializable
	{
		private String primitiveName;
		private Variable.Key varKey;
		private String description;

		/**
		 * Constructor builds a PrimitiveNode extra field.
		 * @param np the PrimitiveNode being parameterized.
		 * @param key the Variable key where the parameter is stored.
		 * @param desc a description of the parameter for the "Get Info" dialog.
		 */
		public ExtraField(PrimitiveNode np, Variable.Key key, String desc)
		{
			primitiveName = np.getName();
			varKey = key;
			description = desc;
			List<ExtraField> fields = extraFields.get(np);
			if (fields == null) extraFields.put(np, fields = new ArrayList<ExtraField>());
			fields.add(this);
		}

		/**
		 * Method to return the name of the PrimitiveNode extra field that is parameterized.
		 * @return the name of the PrimitiveNode extra field that is parameterized.
		 */
		public String getPrimName() { return primitiveName; }

		/**
		 * Method to return the Variable key that accesses the parameter.
		 * @return the Variable key that accesses the parameter.
		 */
		public Variable.Key getKey() { return varKey; }

		/**
		 * Method to return a description of the parameter, for the "Get Info" dialog.
		 * @return a description of the parameter, for the "Get Info" dialog.
		 */
		public String getDescription() { return description; }
	}

	/**
	 * Method to get a list of extra fields on a PrimitiveNode that users can edit.
	 * @return a List of ExtraField objects.
	 */
	public static List<ExtraField> getExtraFields(PrimitiveNode np) { return extraFields.get(np); }

	/****************************** PHOTONICS SUPPORT ******************************/

	private static boolean photonicsChecked = false;
	private static Class<?> photonicsClass;

	/**
	 * Method to find the Photonics technology module.
	 * @return the Photonics technology (null if no Photonics).
	 */
	public static boolean hasPhotonics()
	{
		if (!photonicsChecked)
		{
			photonicsChecked = true;

			// find the Photonics class
			try {
				photonicsClass = Class.forName("com.sun.electric.plugins.photonics.Photonics");
			} catch (ClassNotFoundException e)
			{
				photonicsClass = null;
				return false;
			}
		}

		// if already initialized, return
		if (photonicsClass == null) return false;
	 	return true;
	}

    /**
     * Method to initialize a technology.
     * Calls the technology's specific "init()" method (if any).
     */
    public void setup() {
        if (cacheMinResistance == null || cacheMinCapacitance == null) {
            setFactoryParasitics(10, 0);
        }
        if (cacheGateCapacitance == null || cacheWireRatio == null || cacheDiffAlpha == null) {
            setFactoryLESettings(DEFAULT_GATECAP, DEFAULT_WIRERATIO, DEFAULT_DIFFALPHA);
        }
        layersAllocationLocked = true;
        for (Foundry foundry : foundries) {
            foundry.finish();
        }
        for (Layer layer : layers) {
            layer.finish();
        }
        for (ArcProto arcProto : arcs.values()) {
            arcProto.finish();
        }

        rootSettings.lock();

        check();
    }

    /**
     * Method to set state of a technology.
     */
    public Technology withTechParams(Map<TechFactory.Param, Object> paramValues) {
        Map<TechFactory.Param, Object> newParams = checkParamValues(paramValues);
        if (newParams.equals(this.paramValues)) {
            return this;
        }
        return techFactory.newInstance(generic, newParams);
    }

    protected void setNotUsed(int numPolys) {
        int numMetals = getNumMetals();
        for (PrimitiveNode pn : nodes.values()) {
            boolean isUsed = true;
            for (NodeLayer nl : pn.getNodeLayers()) {
                isUsed = isUsed && nl.getLayer().getFunction().isUsed(numMetals, numPolys);
            }
            pn.setNotUsed(!isUsed);
        }
        for (ArcProto ap : arcs.values()) {
            boolean isUsed = true;
            for (ArcLayer al : ap.layers) {
                isUsed = isUsed && al.getLayer().getFunction().isUsed(numMetals, numPolys);
            }
            ap.setNotUsed(!isUsed);
        }
    }

    /**
     * Returns the current Technology.
     * @return the current Technology.
     * The current technology is maintained by the system as a default
     * in situations where a technology cannot be determined.
     */
    public static Technology getCurrent() {
        return Job.getUserInterface().getCurrentTechnology();
//        if (curTech == null)
//        {
//            System.out.println("The current technology is null. Check the technology settings.");
//            // tries to get the User default
//            curTech = findTechnology(User.getDefaultTechnology());
//            if (curTech == null)
//            {
//                System.out.println("User default technology is not loaded. Check the technology settings");
//                // tries to get MoCMOS tech
//                curTech = getMocmosTechnology();
//                if (curTech == null)
//                {
//                    System.out.println("Major error: MoCMOS technology not loaded. Check the technology settings");
//                }
//            }
//        }
//        return curTech;
    }

//	/**
//	 * Set this to be the current Technology
//	 * The current technology is maintained by the system as a default
//	 * in situations where a technology cannot be determined.
//	 */
//	public void setCurrent()
//	{
//		curTech = this;
//	}
//
//	/**
//	 * Returns the total number of Technologies currently in Electric.
//	 * @return the total number of Technologies currently in Electric.
//	 */
//	public static int getNumTechnologies()
//	{
//		return technologies.size();
//	}
    /**
     * Find the Technology with a particular name.
     * @param name the name of the desired Technology
     * @return the Technology with the same name, or null if no
     * Technology matches.
     */
    public static Technology findTechnology(String name) {
        if (name == null) {
            return null;
        }
//        TechId techId = EDatabase.theDatabase.getIdManager().newTechId(name);
//		Technology tech = findTechnology(techId);
//		if (tech != null) return tech;

        for (Iterator<Technology> it = getTechnologies(); it.hasNext();) {
            Technology t = it.next();
            if (t.getTechName().equals(name)) //			if (t.getTechName().equalsIgnoreCase(name))
            {
                return t;
            }
        }
        return null;
    }

    /**
     * Find the Technology with a particular TechId.
     * @param techId the TechId of the desired Technology
     * @return the Technology with the same name, or null if no
     * Technology matches.
     */
    public static Technology findTechnology(TechId techId) {
        return TechPool.getThreadTechPool().getTech(techId);
    }

    /**
     * Get an iterator over all of the Technologies.
     * @return an iterator over all of the Technologies.
     */
    public static Iterator<Technology> getTechnologies() {
        return TechPool.getThreadTechPool().values().iterator();
    }

    /**
     * Method to convert any old-style variable information to the new options.
     * May be overridden in subclasses.
     * @param varName name of variable
     * @param value value of variable
     * @return map from project preferences to sitting values if variable was converted
     */
    public Map<Setting, Object> convertOldVariable(String varName, Object value) {
        return null;
    }

    /**
     * Method to clean libraries with unused primitive nodes.
     * May be overridden in technologies. By default it does nothing
     * @param ni NodeInst node to analyze
     * @param list nodes that will be removed in a remove job.
     * @return true if node is not in used
     */
    public boolean cleanUnusedNodesInLibrary(NodeInst ni, List<Geometric> list) {
        NodeProto np = ni.getProto();
        // To remove
        if (np instanceof PrimitiveNode && ((PrimitiveNode) np).isNotUsed()) {
            if (list != null) {
                list.add(ni);
            }
            return true;
        }
        return false;
    }

    public void dump(PrintWriter out, Map<Setting, Object> settings) {
        final String[] techBits = {
            "NONELECTRICAL", "NODIRECTIONALARCS", "NONEGATEDARCS",
            "NONSTANDARD", "STATICTECHNOLOGY", "NOPRIMTECHNOLOGY"
        };

        out.println("Technology " + getTechName());
        out.println(getClass().toString());
        out.println("shortName=" + getTechShortName());
        out.println("techDesc=" + getTechDesc());
        out.print("Bits: ");
        printlnBits(out, techBits, userBits);
        out.print("isScaleRelevant=" + isScaleRelevant());
        printlnSetting(out, settings, getScaleSetting());
        printlnSetting(out, settings, getPrefFoundrySetting());
        printlnSetting(out, settings, getNumMetalsSetting());
        dumpExtraProjectSettings(out, settings);
        printlnSetting(out, settings, getMinResistanceSetting());
        printlnSetting(out, settings, getGateLengthSubtractionSetting());
        printlnSetting(out, settings, getGateIncludedSetting());
        printlnSetting(out, settings, getGroundNetIncludedSetting());
        printlnSetting(out, settings, getMaxSeriesResistanceSetting());
        printlnSetting(out, settings, getGateCapacitanceSetting());
        printlnSetting(out, settings, getWireRatioSetting());
        printlnSetting(out, settings, getDiffAlphaSetting());

        printlnPref(out, 0, "ResolutionValueFor" + getTechName(), new Double(factoryResolution.getLambda()));
        Color[] transparentLayers = getFactoryTransparentLayerColors();
        for (int i = 0; i < transparentLayers.length; i++) {
            out.println("TRANSPARENT_" + (i + 1) + "=" + Integer.toHexString(transparentLayers[i].getRGB()));
        }

        for (Layer layer : layers) {
            layer.dump(out, settings);
        }
        for (ArcProto ap : arcs.values()) {
            ap.dump(out);
        }
        if (!oldArcNames.isEmpty()) {
            out.println("OldArcNames:");
            for (Map.Entry<String, ArcProto> e : getOldArcNames().entrySet()) {
                out.println("\t" + e.getKey() + " --> " + e.getValue().getFullName());
            }
        }
        for (PrimitiveNode pnp : nodes.values()) {
            pnp.dump(out);
        }
        if (!oldNodeNames.isEmpty()) {
            out.println("OldNodeNames:");
            for (Map.Entry<String, PrimitiveNode> e : getOldNodeNames().entrySet()) {
                out.println("\t" + e.getKey() + " --> " + e.getValue().getFullName());
            }
        }
        for (Foundry foundry : foundries) {
            out.println("Foundry " + foundry.getType());
            for (Layer layer : layers) {
                Setting setting = foundry.getGDSLayerSetting(layer);
                out.print("\t");
                printlnSetting(out, settings, setting);
            }
        }

        printSpiceHeader(out, 1, getSpiceHeaderLevel1());
        printSpiceHeader(out, 2, getSpiceHeaderLevel2());
        printSpiceHeader(out, 3, getSpiceHeaderLevel3());

        Xml.MenuPalette menuPalette = getFactoryMenuPalette();
        for (int i = 0; i < menuPalette.menuBoxes.size(); i++) {
            List<?> menuBox = menuPalette.menuBoxes.get(i);
            if (menuBox == null || menuBox.isEmpty()) {
                continue;
            }
            out.print(" menu " + (i / menuPalette.numColumns) + " " + (i % menuPalette.numColumns));
            for (Object menuItem : menuBox) {
                if (menuItem instanceof Xml.ArcProto) {
                    out.print(" arc " + ((Xml.ArcProto) menuItem).name);
                } else if (menuItem instanceof Xml.PrimitiveNode) {
                    out.print(" node " + ((Xml.PrimitiveNode) menuItem).name);
                } else if (menuItem instanceof Xml.MenuNodeInst) {
                    Xml.MenuNodeInst n = (Xml.MenuNodeInst) menuItem;
                    boolean display = n.text != null;
                    out.print(" nodeInst " + n.protoName + ":" + n.function + ":" + Orientation.fromAngle(n.rotation));
                    if (n.text != null) {
                        out.print(":" + n.text + ":" + display);
                    }
                } else {
                    assert menuItem instanceof String;
                    out.print(" " + menuItem);
                }
            }
            out.println();
        }

        for (Iterator<Foundry> it = getFoundries(); it.hasNext();) {
            Foundry foundry = it.next();
            out.println("    <Foundry name=\"" + foundry.getType().getName() + "\">");
            for (Iterator<Layer> lit = getLayers(); lit.hasNext();) {
                Layer layer = lit.next();
                Setting layerGdsSetting = foundry.getGDSLayerSetting(layer);
                String gdsStr = (String) settings.get(layerGdsSetting);
                if (gdsStr == null || gdsStr.length() == 0) {
                    continue;
                }
                out.println("        <layerGds layer=\"" + layer.getName() + "\" gds=\"" + gdsStr + "\"/>");
            }
//            for (Map.Entry<Layer,String> e: foundry.getGDSLayers(sc).entrySet())
//                out.println("        <layerGds layer=\"" + e.getKey().getName() + "\" gds=\"" + e.getValue() + "\"/>");
            List<DRCTemplate> rules = foundry.getRules();
            if (rules != null) {
                for (DRCTemplate rule : rules) {
                    DRCTemplate.exportDRCRule(out, rule);
                }
            }
            out.println("    </Foundry>");
        }
    }

    protected void dumpExtraProjectSettings(PrintWriter out, Map<Setting, Object> settings) {
    }

    protected static void printlnSetting(PrintWriter out, Map<Setting, Object> settings, Setting setting) {
        out.println(setting.getXmlPath() + "=" + settings.get(setting) + "(" + setting.getFactoryValue() + ")");
    }

    static void printlnPref(PrintWriter out, int indent, String prefName, Object factoryValue) {
        while (indent-- > 0) {
            out.print("\t");
        }
        out.println(prefName + "=" + factoryValue);
    }

    protected static void printlnBits(PrintWriter out, String[] bitNames, int bits) {
        for (int i = 0; i < Integer.SIZE; i++) {
            if ((bits & (1 << i)) == 0) {
                continue;
            }
            String bitName = i < bitNames.length ? bitNames[i] : null;
            if (bitName == null) {
                bitName = "BIT" + i;
            }
            out.print(" " + bitName);
        }
        out.println();
    }

    private void printSpiceHeader(PrintWriter out, int level, String[] header) {
        if (header == null) {
            return;
        }
        out.println("SpiceHeader " + level);
        for (String s : header) {
            out.println("\t\"" + s + "\"");
        }
    }

    /**
     * Create Xml structure of this Technology
     */
    public Xml.Technology makeXml() {
        return makeXml(null);
    }

    /**
     * Create Xml structure of this Technology
     */
    public Xml.Technology makeXml(Map<Object,Map<String,Object>> additiionalAttributes) {
        Xml.Technology t = new Xml.Technology();
        t.techName = getTechName();
        if (getClass() != Technology.class) {
            t.className = getClass().getName();
        }

        Xml.Version version;
        version = new Xml.Version();
        version.techVersion = 1;
        version.electricVersion = Technology.DISK_VERSION_1;
        t.versions.add(version);
        version = new Xml.Version();
        version.techVersion = 2;
        version.electricVersion = Technology.DISK_VERSION_2;
        t.versions.add(version);

        t.shortTechName = getTechShortName();
        t.description = getTechDesc();
        int numMetals = ((Integer) getNumMetalsSetting().getFactoryValue()).intValue();
        t.minNumMetals = t.maxNumMetals = t.defaultNumMetals = numMetals;
        t.scaleValue = getScaleSetting().getDoubleFactoryValue();
        t.scaleRelevant = isScaleRelevant();
        t.resolutionValue = getFactoryResolution().getLambda();
        t.defaultFoundry = (String) getPrefFoundrySetting().getFactoryValue();
        t.minResistance = getMinResistanceSetting().getDoubleFactoryValue();
        t.minCapacitance = getMinCapacitanceSetting().getDoubleFactoryValue();
        t.leGateCapacitance = getGateCapacitanceSetting().getDoubleFactoryValue();
        t.leWireRatio = getWireRatioSetting().getDoubleFactoryValue();
        t.leDiffAlpha = getDiffAlphaSetting().getDoubleFactoryValue();
        t.transparentLayers.addAll(Arrays.asList(getFactoryTransparentLayerColors()));

        for (Iterator<Layer> it = getLayers(); it.hasNext();) {
            Layer layer = it.next();
            t.layers.add(layer.makeXml());
        }
//        HashSet<PrimitiveNode> arcPins = new HashSet<PrimitiveNode>();
        for (Iterator<ArcProto> it = getArcs(); it.hasNext();) {
            ArcProto ap = it.next();
            t.arcs.add(ap.makeXml());
//            if (ap.arcPin != null)
//                arcPins.add(ap.arcPin);
        }
        HashSet<PrimitiveNodeGroup> groupsDone = new HashSet<PrimitiveNodeGroup>();
        for (Iterator<PrimitiveNode> it = getNodes(); it.hasNext();) {
            PrimitiveNode pnp = it.next();
            if (pnp.getFunction() == PrimitiveNode.Function.NODE) {
                continue;
            }
            PrimitiveNodeGroup group = pnp.getPrimitiveNodeGroup();
            if (group != null) {
                if (groupsDone.contains(group)) {
                    continue;
                }
                t.nodeGroups.add(group.makeXml());
                groupsDone.add(group);
            } else {
                t.nodeGroups.add(pnp.makeXml(additiionalAttributes));
            }
        }
        addSpiceHeader(t, 1, getSpiceHeaderLevel1());
        addSpiceHeader(t, 2, getSpiceHeaderLevel2());
        addSpiceHeader(t, 3, getSpiceHeaderLevel3());

        t.menuPalette = getFactoryMenuPalette();

        for (Iterator<Foundry> it = getFoundries(); it.hasNext();) {
            Foundry foundry = it.next();
            Xml.Foundry f = new Xml.Foundry();
            f.name = foundry.toString();
            for (Layer layer : layers) {
                Setting setting = foundry.getGDSLayerSetting(layer);
                String gds = (String) setting.getFactoryValue();
                if (gds.length() == 0) {
                    continue;
                }
                f.layerGds.put(layer.getName(), gds);
            }
//            Map<Layer,String> gdsMap = foundry.getGDSLayers();
//            for (Map.Entry<Layer,String> e: gdsMap.entrySet()) {
//                String gds = e.getValue();
//                if (gds.length() == 0) continue;
//                f.layerGds.put(e.getKey().getName(), gds);
//            }
            List<DRCTemplate> rules = foundry.getRules();
            if (rules != null) {
                f.rules.addAll(rules);
            }
            t.foundries.add(f);
        }
        return t;
    }

    private static void addSpiceHeader(Xml.Technology t, int level, String[] spiceLines) {
        if (spiceLines == null) {
            return;
        }
        Xml.SpiceHeader spiceHeader = new Xml.SpiceHeader();
        spiceHeader.level = level;
        for (String spiceLine : spiceLines) {
            spiceHeader.spiceLines.add(spiceLine);
        }
        t.spiceHeaders.add(spiceHeader);
    }

    /****************************** LAYERS ******************************/
    /**
     * Returns an Iterator on the Layers in this Technology.
     * @return an Iterator on the Layers in this Technology.
     */
    public Iterator<Layer> getLayers() {
        layersAllocationLocked = true;
        return layers.iterator();
    }

    /**
     * Returns the Layer in this technology with a particular Id
     * @param layerId the Id of the Layer.
     * @return the Layer in this technology with that Id.
     */
//    public Layer getLayer(LayerId layerId) {
//        assert layerId.techId == techId;
//        int chronIndex = layerId.chronIndex;
//        return chronIndex < layersByChronIndex.length ? layersByChronIndex[chronIndex] : null;
//    }

    /**
     * Returns the Layer in this technology with a particular chron index
     * @param chronIndex index the Id of the Layer.
     * @return the Layer in this technology with that Id.
     */
    Layer getLayerByChronIndex(int chronIndex) {
        return chronIndex < layersByChronIndex.length ? layersByChronIndex[chronIndex] : null;
    }

    /**
     * Returns a specific Layer number in this Technology.
     * @param index the index of the desired Layer.
     * @return the indexed Layer in this Technology.
     */
    public Layer getLayer(int index) {
        return layers.get(index);
    }

    /**
     * Returns the number of Layers in this Technology.
     * @return the number of Layers in this Technology.
     */
    public int getNumLayers() {
        layersAllocationLocked = true;
        return layers.size();
    }

    /**
     * Method to find a Layer with a given name.
     * @param layerName the name of the desired Layer.
     * @return the Layer with that name (null if none found).
     */
    public Layer findLayer(String layerName) {
        Layer layer = layersByName.get(layerName);
        if (layer != null) {
            return layer;
        }
        for (Iterator<Layer> it = getLayers(); it.hasNext();) {
            layer = it.next();
            if (layer.getName().equalsIgnoreCase(layerName)) {
                return layer;
            }
        }
        for (Iterator<Layer> it = getLayers(); it.hasNext();) {
            layer = it.next();
            if (layer == null) {
                continue;
            }
            if (layer.getName().equalsIgnoreCase(layerName)) {
                return layer;
            }
        }
        return null;
    }

    /**
     * Method to determine the index in the upper-left triangle array for two layers/nodes.
     * The sequence of indices is: rules for single layers, rules for nodes, rules that
     * involve more than 1 layers.
     * @param index1 the first layer/node index.
     * @param index2 the second layer/node index.
     * @return the index in the array that corresponds to these two layers/nodes.
     */
    public int getRuleIndex(int index1, int index2) {
        int numLayers = getNumLayers();
        if (index1 > index2) {
            int temp = index1;
            index1 = index2;
            index2 = temp;
        }
        int pIndex = (index1 + 1) * (index1 / 2) + (index1 & 1) * ((index1 + 1) / 2);
        pIndex = index2 + numLayers * index1 - pIndex;
        return numLayers + getNumNodes() + pIndex;
    }

    /**
     * Method to retrieve index of the node in the map containing DRC rules
     * It must add the total number of layers to guarantee indexes don't collide with
     * layer indices.
     * The sequence of indices is: rules for single layers, rules for nodes, rules that
     * involve more than 1 layers.
     * @return the index of this node in its Technology.
     */
    public final int getPrimNodeIndexInTech(PrimitiveNode node) {
        return getNumLayers() + node.getPrimNodeInddexInTech();
    }

    /**
     * Method to determine index of layer or node involved in the rule
     * @param name name of the layer or node
     * @return the index of the rule.
     */
    public int getRuleNodeIndex(String name) {
        // Checking if node is found
        // Be careful because iterator might change over time?
        int count = 0;
        for (Iterator<PrimitiveNode> it = getNodes(); it.hasNext(); count++) {
            PrimitiveNode pn = it.next();
            if (pn.getName().equals(name)) //            if (pn.getName().equalsIgnoreCase(name))
            {
                return (getNumLayers() + count);   // it should use get
            }
        }
        return -1;
    }

    public static Layer getLayerFromOverride(String override, int startPos, char endChr, Technology tech) {
        int endPos = override.indexOf(endChr, startPos);
        if (endPos < 0) {
            return null;
        }
        String layerName = override.substring(startPos, endPos);
        return tech.findLayer(layerName);
    }

    /**
     * Method to find the Layer in this Technology that matches a function description.
     * @param fun the layer function to locate.
     * @param functionExtras
     * @return the Layer that matches this description (null if not found).
     */
    public Layer findLayerFromFunction(Layer.Function fun, int functionExtras) {
        for (Iterator<Layer> it = this.getLayers(); it.hasNext();) {
            Layer lay = it.next();
            Layer.Function lFun = lay.getFunction();
            if (lFun == fun) {
                // Nothing extra to look for or it really matches
                if (functionExtras == -1 || functionExtras == lay.getFunctionExtras()) {
                    return lay;
                }
            }
        }
        return null;
    }

    /**
     * Method to add a new Layer to this Technology.
     * This is usually done during initialization.
     * @param layer the Layer to be added to this Technology.
     */
    public void addLayer(Layer layer) {
        if (layersAllocationLocked) {
            throw new IllegalStateException("layers allocation is locked");
        }
        layer.setIndex(layers.size());
        layers.add(layer);
        Layer oldLayer = layersByName.put(layer.getName(), layer);
        assert oldLayer == null;
        LayerId layerId = layer.getId();
        if (layerId.chronIndex >= layersByChronIndex.length) {
            Layer[] newLayersByChronIndex = new Layer[layerId.chronIndex + 1];
            System.arraycopy(layersByChronIndex, 0, newLayersByChronIndex, 0, layersByChronIndex.length);
            layersByChronIndex = newLayersByChronIndex;
        }
        assert layersByChronIndex[layerId.chronIndex] == null;
        layersByChronIndex[layerId.chronIndex] = layer;
    }

    /**
     * Method to tell whether two layers should be considered equivalent for the purposes of cropping.
     * The method is overridden by individual technologies to provide specific answers.
     * @param layer1 the first Layer.
     * @param layer2 the second Layer.
     * @return true if the layers are equivalent.
     */
    public boolean sameLayer(Layer layer1, Layer layer2) {
        if (layer1 == layer2) {
            return true;
        }
        // Only when the function and the extra bits match. Case of active and active well
        if (layer1.getFunction() == layer2.getFunction()
                && layer1.getFunctionExtras() == layer2.getFunctionExtras()) {
            return true;
        }
        if (layer1.getFunction() == Layer.Function.POLY1 && layer2.getFunction() == Layer.Function.GATE) {
            return true;
        }
        if (layer2.getFunction() == Layer.Function.POLY1 && layer1.getFunction() == Layer.Function.GATE) {
            return true;
        }
        return false;
    }

    /**
     * Method to sort layers based on User preference
     * @return a sorted list of layers based on User preference
     */
    public List<Layer> getLayersSortedByUserPreference()
    {
    	return getLayersSortedByRule(LayerSortingType.findType(User.getLayersSorting()));
    }

    /**
     * Method to make a sorted list of layers in this Technology
     * based on Users preference
     * @return a sorted list of Layers in this Technology.
     */
    public List<Layer> getLayersSortedByRule(Layer.LayerSortingType type)
    {
    	List<Layer> layerList = new ArrayList<Layer>();
        for (Iterator<Layer> it = getLayers(); it.hasNext();)
            layerList.add(it.next());
        if (type != LayerSortingType.ByOrderInTechFile)
        	return Layer.getLayersSortedByRule(layerList, type);
        return layerList;
    }

    /**
     * Dummy method overridden by implementing technologies to define
     * the number of metal layers in the technology.  Applies to layout
     * technologies.  Can by changed by user preferences.
     * @return the number of metal layers currently specified for the technology
     */
    public int getNumMetals() {
        return paramNumMetalLayers.intValue();
    }

    /**
     * Returns project preferences to tell the number of metal layers in the MoCMOS technology.
     * @return project preferences to tell the number of metal layers in the MoCMOS technology (from 2 to 6).
     */
    public Setting getNumMetalsSetting() {
        return cacheNumMetalLayers;
    }

    /****************************** ARCS ******************************/
    /**
     * Method to create a new ArcProto from the parameters.
     * @param protoName the name of this ArcProto.
     * It may not have unprintable characters, spaces, or tabs in it.
     * @param lambdaWidthOffset width offset in lambda units.
     * @param defaultWidth the default width of this ArcProto.
     * @param layers the Layers that make up this ArcProto.
     * @return the newly created ArcProto.
     */
    protected ArcProto newArcProto(String protoName, double lambdaWidthOffset, double defaultWidth, ArcProto.Function function, Technology.ArcLayer... layers) {
        // check the arguments
        if (findArcProto(protoName) != null) {
            System.out.println("Error: technology " + getTechName() + " has multiple arcs named " + protoName);
            return null;
        }
        long gridWidthOffset = DBMath.lambdaToSizeGrid(lambdaWidthOffset);
        if (gridWidthOffset < 0 || gridWidthOffset > Integer.MAX_VALUE) {
            System.out.println("ArcProto " + getTechName() + ":" + protoName + " has invalid width offset " + lambdaWidthOffset);
            return null;
        }
        if (defaultWidth < DBMath.gridToLambda(gridWidthOffset)) {
            System.out.println("ArcProto " + getTechName() + ":" + protoName + " has negative width");
            return null;
        }
        long defaultGridWidth = DBMath.lambdaToSizeGrid(defaultWidth);
        assert layers[0].getExtend().getGrid() == (defaultGridWidth - gridWidthOffset) / 2;
        ArcProto ap = new ArcProto(this, protoName, DBMath.gridToLambda(gridWidthOffset), function, layers, arcs.size());
        addArcProto(ap);
        return ap;
    }

    /**
     * Returns the ArcProto in this technology with a particular name.
     * @param name the name of the ArcProto.
     * @return the ArcProto in this technology with that name.
     */
    public ArcProto findArcProto(String name) {
        if (name == null) {
            return null;
        }
        return arcs.get(name);
    }

    /**
     * Returns the ArcProto in this technology with a particular Id
     * @param arcProtoId the Id of the ArcProto.
     * @return the ArcProto in this technology with that Id.
     */
    public ArcProto getArcProto(ArcProtoId arcProtoId) {
        assert arcProtoId.techId == techId;
        int chronIndex = arcProtoId.chronIndex;
        return chronIndex < arcsByChronIndex.length ? arcsByChronIndex[chronIndex] : null;
    }

    /**
     * Returns the ArcProto in this technology with a particular chron index
     * @param chronIndex index the Id of the ArcProto.
     * @return the ArcProto in this technology with that Id.
     */
    ArcProto getArcProtoByChronIndex(int chronIndex) {
        return chronIndex < arcsByChronIndex.length ? arcsByChronIndex[chronIndex] : null;
    }

    /**
     * Returns an Iterator on the ArcProto objects in this technology.
     * @return an Iterator on the ArcProto objects in this technology.
     */
    public Iterator<ArcProto> getArcs() {
        return arcs.values().iterator();
    }

    /**
     * Return a collection of the ArcProto objects in this technology
     * @return a collection of the ArcProto objects in this technology
     */
    public Collection<ArcProto> getArcsCollection() {
        return arcs.values();
    }

    /**
     * Returns the number of ArcProto objects in this technology.
     * @return the number of ArcProto objects in this technology.
     */
    public int getNumArcs() {
        return arcs.size();
    }

    /**
     * Method to add a new ArcProto to this Technology.
     * This is usually done during initialization.
     * @param ap the ArcProto to be added to this Technology.
     */
    public void addArcProto(ArcProto ap) {
        assert findArcProto(ap.getName()) == null;
        assert ap.primArcIndex == arcs.size();
        arcs.put(ap.getName(), ap);
        ArcProtoId arcProtoId = ap.getId();
        if (arcProtoId.chronIndex >= arcsByChronIndex.length) {
            ArcProto[] newArcsByChronIndex = new ArcProto[arcProtoId.chronIndex + 1];
            System.arraycopy(arcsByChronIndex, 0, newArcsByChronIndex, 0, arcsByChronIndex.length);
            arcsByChronIndex = newArcsByChronIndex;
        }
        arcsByChronIndex[arcProtoId.chronIndex] = ap;
    }

    /**
     * Sets the technology to have no directional arcs.
     * Users should never call this method.
     * It is set once by the technology during initialization.
     * Directional arcs are those with arrows on them, indicating (only graphically) the direction of flow through the arc.
     */
    protected void setNoDirectionalArcs() {
        userBits |= NODIRECTIONALARCS;
    }

    /**
     * Returns true if this technology does not have directional arcs.
     * @return true if this technology does not have directional arcs.
     * Directional arcs are those with arrows on them, indicating (only graphically) the direction of flow through the arc.
     */
    public boolean isNoDirectionalArcs() {
        return (userBits & NODIRECTIONALARCS) != 0;
    }

    /**
     * Sets the technology to have no negated arcs.
     * Users should never call this method.
     * It is set once by the technology during initialization.
     * Negated arcs have bubbles on them to graphically indicated negation.
     * Only Schematics and related technologies allow negated arcs.
     */
    protected void setNoNegatedArcs() {
        userBits |= NONEGATEDARCS;
    }

    /**
     * Returns true if this technology does not have negated arcs.
     * @return true if this technology does not have negated arcs.
     * Negated arcs have bubbles on them to graphically indicated negation.
     * Only Schematics and related technologies allow negated arcs.
     */
    public boolean isNoNegatedArcs() {
        return (userBits & NONEGATEDARCS) != 0;
    }

    /**
     * Returns the polygons that describe arc "ai".
     * @param ai the ArcInst that is being described.
     * @return an array of Poly objects that describes this ArcInst graphically.
     */
    public Poly[] getShapeOfArc(ArcInst ai) {
        return getShapeOfArc(ai, null);
    }

    /**
     * Returns the polygons that describe arc "ai".
     * @param ai the ArcInst that is being described.
     * @param onlyTheseLayers to filter the only required layers
     * @return an array of Poly objects that describes this ArcInst graphically.
     */
    public Poly[] getShapeOfArc(ArcInst ai, Layer.Function.Set onlyTheseLayers) {
        Poly.Builder polyBuilder = Poly.threadLocalLambdaBuilder();
        return polyBuilder.getShapeArray(ai, onlyTheseLayers);
    }

    /**
     * Method to convert old primitive arc names to their proper ArcProtos.
     * @param name the unknown arc name, read from an old Library.
     * @return the proper ArcProto to use for this name.
     */
    public ArcProto convertOldArcName(String name) {
        return oldArcNames.get(name);
    }

    public Map<String, ArcProto> getOldArcNames() {
        return new TreeMap<String, ArcProto>(oldArcNames);
    }

    /****************************** NODES ******************************/
    /**
     * Method to return a sorted list of nodes in the technology
     * @return a list with all nodes sorted
     */
    public List<PrimitiveNode> getNodesSortedByName() {
        TreeMap<String, PrimitiveNode> sortedMap = new TreeMap<String, PrimitiveNode>(TextUtils.STRING_NUMBER_ORDER);
        for (Iterator<PrimitiveNode> it = getNodes(); it.hasNext();) {
            PrimitiveNode pn = it.next();
            sortedMap.put(pn.getName(), pn);
        }
        return new ArrayList<PrimitiveNode>(sortedMap.values());
    }

    /**
     * Returns the PrimitiveNode in this technology with a particular name.
     * @param name the name of the PrimitiveNode.
     * @return the PrimitiveNode in this technology with that name.
     */
    public PrimitiveNode findNodeProto(String name) {
        if (name == null) {
            return null;
        }
        return nodes.get(name);
//		PrimitiveNode primNode = nodes.get(name);
//		if (primNode != null) return primNode;
//
//		for (Iterator<PrimitiveNode> it = getNodes(); it.hasNext(); )
//		{
//			PrimitiveNode pn = it.next();
//			if (pn.getName().equalsIgnoreCase(name))
//				return pn;
//		}
//		return null;
    }

    /**
     * Returns the PrimitiveNode in this technology with a particular Id
     * @param primitiveNodeId the Id of the PrimitiveNode.
     * @return the PrimitiveNiode in this technology with that Id.
     */
    public PrimitiveNode getPrimitiveNode(PrimitiveNodeId primitiveNodeId) {
        assert primitiveNodeId.techId == techId;
        int chronIndex = primitiveNodeId.chronIndex;
        return chronIndex < nodesByChronIndex.length ? nodesByChronIndex[chronIndex] : null;
    }

    /**
     * Returns the PrimitiveNode in this technology with a particular chron index
     * @param chronIndex index the Id of the PrimitiveNode.
     * @return the PrimitiveNode in this technology with that Id.
     */
    PrimitiveNode getPrimitiveNodeByChronIndex(int chronIndex) {
        return chronIndex < nodesByChronIndex.length ? nodesByChronIndex[chronIndex] : null;
    }

    /**
     * Returns an Iterator on the PrimitiveNode objects in this technology.
     * @return an Iterator on the PrimitiveNode objects in this technology.
     */
    public Iterator<PrimitiveNode> getNodes() {
        return nodes.values().iterator();
    }

    /**
     * Return a collection of the PrimitiveNode objects in this technology
     * @return a collection of the PrimitiveNode objects in this technology
     */
    public Collection<PrimitiveNode> getNodesCollection() {
        return nodes.values();
    }

    /**
     * Returns the number of PrimitiveNodes objects in this technology.
     * @return the number of PrimitiveNodes objects in this technology.
     */
    public int getNumNodes() {
        return nodes.size();
    }

    /**
     * Method to create a new PrimitiveNode from the parameters.
     * @param protoName the name of the PrimitiveNode.
     * Primitive names may not contain unprintable characters, spaces, tabs, a colon (:), semicolon (;) or curly braces ({}).
     * @param sizeCorrector1 size corrector point 1 for the PrimitiveNode.
     * @param sizeCorrector2 size corrector point 2 for the PrimitiveNode.
     * @param width the width of the PrimitiveNode.
     * @param height the height of the PrimitiveNode.
     * @param baseRectangle the reported/selected part of the PrimitiveNode with standard size.
     * @param layers the Layers that comprise the PrimitiveNode.
     * @return the newly created PrimitiveNode.
     */
    protected PrimitiveNode newPrimitiveNode(String protoName, EPoint sizeCorrector1, EPoint sizeCorrector2,
            String minSizeRule, double width, double height,
            ERectangle fullRectangle, ERectangle baseRectangle, Technology.NodeLayer[] layers) {
        return new PrimitiveNode(protoName, this, sizeCorrector1, sizeCorrector2, minSizeRule, width, height,
                fullRectangle, baseRectangle, layers);
    }

    /**
     * Method to add a new PrimitiveNode to this Technology.
     * This is usually done during initialization.
     * @param np the PrimitiveNode to be added to this Technology.
     */
    public void addNodeProto(PrimitiveNode np) {
    	if (Job.getDebug() && findNodeProto(np.getName()) != null)
    		System.out.println(np.getName() + " is already defined as primitive node");
        assert findNodeProto(np.getName()) == null;
        np.setPrimNodeIndexInTech(nodeIndex++);
        nodes.put(np.getName(), np);
        PrimitiveNodeId primitiveNodeId = np.getId();
        if (primitiveNodeId.chronIndex >= nodesByChronIndex.length) {
            PrimitiveNode[] newNodesByChronIndex = new PrimitiveNode[primitiveNodeId.chronIndex + 1];
            System.arraycopy(nodesByChronIndex, 0, newNodesByChronIndex, 0, nodesByChronIndex.length);
            nodesByChronIndex = newNodesByChronIndex;
        }
        nodesByChronIndex[primitiveNodeId.chronIndex] = np;
    }
    private static final Layer.Function.Set diffLayers = new Layer.Function.Set(Layer.Function.DIFFP, Layer.Function.DIFFN);

    /**
     * Method to return the size of a resistor-type NodeInst in this Technology.
     * @param ni the NodeInst.
     * @param context the VarContext in which any variables will be evaluated,
     * pass in VarContext.globalContext if no context needed, or set to null
     * to avoid evaluation of variables (if any).
     * @return the size of the NodeInst.
     */
    public PrimitiveNodeSize getResistorSize(NodeInst ni, VarContext context) {
        if (ni.isCellInstance()) {
            return null;
        }
        double length = ni.getLambdaBaseXSize();
        double width = ni.getLambdaBaseYSize();
//        SizeOffset so = ni.getSizeOffset();
//        double length = ni.getXSize() - so.getLowXOffset() - so.getHighXOffset();
//        double width = ni.getYSize() - so.getLowYOffset() - so.getHighYOffset();

        PrimitiveNodeSize size = new PrimitiveNodeSize(new Double(width), new Double(length), false);
        return size;
    }

    /**
     * Method to return length of active region. This will be used for
     * parasitics extraction. Electric layers are used for the calculation
     * @param ni the NodeInst.
     * @return length of the any active region
     */
    public double getTransistorActiveLength(NodeInst ni) {
        Poly[] diffList = getShapeOfNode(ni, true, false, diffLayers);
        double activeLen = 0;
        if (diffList.length > 0) {
            // Since electric layers are used, it takes the first active region
            Poly poly = diffList[0];
            activeLen = poly.getBounds2D().getHeight();
        }
        return activeLen;
    }

    /**
     * Method to return the size of a transistor NodeInst in this Technology.
     * You should most likely be calling NodeInst.getTransistorSize instead of this.
     * @param ni the NodeInst.
     * @param context the VarContext in which any variables will be evaluated,
     * pass in VarContext.globalContext if no context needed, or set to null
     * to avoid evaluation of variables (if any).
     * @return the size of the NodeInst.
     */
    public TransistorSize getTransistorSize(NodeInst ni, VarContext context) {
        double width = ni.getLambdaBaseXSize();
        double height = ni.getLambdaBaseYSize();

        // override if there is serpentine information
        Point2D[] trace = ni.getTrace();
        if (trace != null) {
            width = 0;
            for (int i = 1; i < trace.length; i++) {
                width += trace[i - 1].distance(trace[i]);
            }
            height = 2;
            double serpentineLength = ni.getSerpentineTransistorLength();
            if (serpentineLength > 0) {
                height = serpentineLength;
            }
            //System.out.println("No calculating length for active regions yet");
        }
        double activeLen = getTransistorActiveLength(ni);
        TransistorSize size = new TransistorSize(new Double(width), new Double(height), new Double(activeLen), null, true);
        return size;
    }

    /**
     * Method to set the size of a transistor NodeInst in this Technology.
     * You should be calling NodeInst.setTransistorSize instead of this.
     * @param ni the NodeInst
     * @param width the new width (positive values only)
     * @param length the new length (positive values only)
     */
    private void setPrimitiveNodeSizeLocal(NodeInst ni, double width, double length) {
        double oldWidth = ni.getLambdaBaseXSize();
        double oldLength = ni.getLambdaBaseYSize();
        double dW = width - oldWidth;
        double dL = length - oldLength;
        ni.resize(dW, dL);
    }

    /**
     * Method to set the size of a transistor NodeInst in this Technology.
     * Sense of "width" and "length" are different for resistors and transistors.
     * Default function when transistor gate and resistor length are horizontal.
     * @param ni the NodeInst
     * @param width the new width (positive values only)
     * @param length the new length (positive values only)
     * @param ep EditingPreferences with default TextDescriptors
     */
    public void setPrimitiveNodeSize(NodeInst ni, double width, double length, EditingPreferences ep) {
        if (ni.getFunction().isResistor()) {
            setPrimitiveNodeSizeLocal(ni, length, width);
        } else {
            setPrimitiveNodeSizeLocal(ni, width, length);
        }
    }

    /**
     * Method to return a gate PortInst for this transistor NodeInst.
     * Implementation Note: May want to make this a more general
     * method, getPrimitivePort(PortType), if the number of port
     * types increases.  Note: You should be calling
     * NodeInst.getTransistorGatePort() instead of this, most likely.
     * @param ni the NodeInst
     * @return a PortInst for the gate of the transistor
     */
    public PortInst getTransistorGatePort(NodeInst ni) {
        return ni.getPortInst(0);
    }

    /**
     * Method to return the other gate PortInst for this transistor NodeInst.
     * Only useful for layout transistors that have two gate ports.
     * Implementation Note: May want to make this a more general
     * method, getPrimitivePort(PortType), if the number of port
     * types increases.  Note: You should be calling
     * NodeInst.getTransistorGatePort() instead of this, most likely.
     * @param ni the NodeInst
     * @return a PortInst for the alternate gate of the transistor
     */
    public PortInst getTransistorAltGatePort(NodeInst ni) {
        if (ni.getProto().getTechnology() == Schematics.tech()) {
            return ni.getPortInst(0);
        }
        return ni.getPortInst(2);
    }

    /**
     * Method to return a base PortInst for this transistor NodeInst.
     * @param ni the NodeInst
     * @return a PortInst for the base of the transistor
     */
    public PortInst getTransistorBasePort(NodeInst ni) {
        return ni.getPortInst(0);
    }

    /**
     * Method to return a source PortInst for this transistor NodeInst.
     * Implementation Note: May want to make this a more general
     * method, getPrimitivePort(PortType), if the number of port
     * types increases.  Note: You should be calling
     * NodeInst.getTransistorSourcePort() instead of this, most likely.
     * @param ni the NodeInst
     * @return a PortInst for the source of the transistor
     */
    public PortInst getTransistorSourcePort(NodeInst ni) {
        return ni.getPortInst(1);
    }

    /**
     * Method to return a emitter PortInst for this transistor NodeInst.
     * @param ni the NodeInst
     * @return a PortInst for the emitter of the transistor
     */
    public PortInst getTransistorEmitterPort(NodeInst ni) {
        return ni.getPortInst(1);
    }

    /**
     * Method to return a drain PortInst for this transistor NodeInst.
     * Implementation Note: May want to make this a more general
     * method, getPrimitivePort(PortType), if the number of port
     * types increases.  Note: You should be calling
     * NodeInst.getTransistorDrainPort() instead of this, most likely.
     * @param ni the NodeInst
     * @return a PortInst for the drain of the transistor
     */
    public PortInst getTransistorDrainPort(NodeInst ni) {
        if (ni.getProto().getTechnology() == Schematics.tech()) {
            return ni.getPortInst(2);
        }
        if (ni.getNumPortInsts() < 4) return null;
        return ni.getPortInst(3);
    }

    /**
     * Method to return a collector PortInst for this transistor NodeInst.
     * @param ni the NodeInst
     * @return a PortInst for the collector of the transistor
     */
    public PortInst getTransistorCollectorPort(NodeInst ni) {
        return ni.getPortInst(2);
    }

    /**
     * Method to return a bias PortInst for this transistor NodeInst.
     * Implementation Note: May want to make this a more general
     * method, getPrimitivePort(PortType), if the number of port
     * types increases.  Note: You should be calling
     * NodeInst.getTransistorBiasPort() instead of this, most likely.
     * @param ni the NodeInst
     * @return a PortInst for the bias of the transistor
     */
    public PortInst getTransistorBiasPort(NodeInst ni) {
        // By default, transistors have no bias port
        return null;
    }

    /**
     * Sets the technology to have no primitives.
     * Users should never call this method.
     * It is set once by the technology during initialization.
     * This indicates to the user interface that it should not switch to this technology.
     * The FPGA technology has this bit set because it initially contains no primitives,
     * and they are only created dynamically.
     */
    public void setNoPrimitiveNodes() {
        userBits |= NOPRIMTECHNOLOGY;
    }

    /**
     * Returns true if this technology has no primitives.
     * @return true if this technology has no primitives.
     * This indicates to the user interface that it should not switch to this technology.
     * The FPGA technology has this bit set because it initially contains no primitives,
     * and they are only created dynamically.
     */
    public boolean isNoPrimitiveNodes() {
        return (userBits & NOPRIMTECHNOLOGY) != 0;
    }

    /**
     * Method to set default outline information on a NodeInst.
     * Very few primitives have default outline information (usually just in the Artwork Technology).
     * This method is overridden by the appropriate technology.
     * @param ni the NodeInst to load with default outline information.
     */
    public void setDefaultOutline(NodeInst ni) {
    }

    /**
     * Method to get the base (highlight) ERectangle associated with a NodeInst
     * in this PrimitiveNode.
     * Base ERectangle is a highlight rectangle of standard-size NodeInst of
     * this PrimtiveNode
     * By having this be a method of Technology, it can be overridden by
     * individual Technologies that need to make special considerations.
     * @param ni the NodeInst to query.
     * @return the base ERectangle of this PrimitiveNode.
     */
    public ERectangle getNodeInstBaseRectangle(NodeInst ni) {
        PrimitiveNode pn = (PrimitiveNode) ni.getProto();
        return pn.getBaseRectangle();
    }
    private static final Technology.NodeLayer[] nullPrimLayers = new Technology.NodeLayer[0];

    /**
     * Returns the polygons that describe node "ni".
     * @param ni the NodeInst that is being described.
     * The prototype of this NodeInst must be a PrimitiveNode and not a Cell.
     * @return an array of Poly objects that describes this NodeInst graphically.
     */
    public Poly[] getShapeOfNode(NodeInst ni) {
        return getShapeOfNode(ni, false, false, (Layer.Function.Set) null);
    }

//    private static PrintWriter out;
//
//    public static void startDebug(String fileName) {
//        try {
//            out = new PrintWriter(fileName);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }
//
//    public static void stopDebug() {
//        out.close();
//        out = null;
//    }
    /**
     * Returns the polygons that describe node "ni".
     * @param ni the NodeInst that is being described.
     * The prototype of this NodeInst must be a PrimitiveNode and not a Cell.
     * @param electrical true to get the "electrical" layers.
     * When electrical layers are requested, each layer is tied to a specific port on the node.
     * If any piece of geometry covers more than one port,
     * it must be split for the purposes of an "electrical" description.
     * For example, the MOS transistor has 2 layers: Active and Poly.
     * But it has 3 electrical layers: Active, Active, and Poly.
     * The active must be split since each half corresponds to a different PrimitivePort on the PrimitiveNode.
     * @param reasonable true to get only a minimal set of contact cuts in large contacts.
     * The minimal set covers all edge contacts, but ignores the inner cuts in large contacts.
     * @param onlyTheseLayers a set of layers to draw (if null, draw all layers).
     * @return an array of Poly objects that describes this NodeInst graphically.
     */
    public Poly[] getShapeOfNode(NodeInst ni, boolean electrical, boolean reasonable, Layer.Function.Set onlyTheseLayers) {
        if (ni.isCellInstance()) {
            return null;
        }

        Poly.Builder polyBuilder = Poly.threadLocalLambdaBuilder();
        return polyBuilder.getShapeArray(ni, electrical, reasonable, onlyTheseLayers);
    }

    /**
     * Tells if node can be drawn by simplified algorithm
     * Overridden in subclasses
     * @param ni node to test
     * @param explain if true then print explanation why arc is not easy
     * @return true if arc can be drawn by simplified algorithm
     */
    public boolean isEasyShape(NodeInst ni, boolean explain) {
        return false;
    }

    /**
     * Method to determine if cut case is considered multi cut
     * It gets overridden by CMOS90
     */
    public boolean isMultiCutInTechnology(MultiCutData mcd) {
        if (mcd == null) {
            return false;
        }
        return (mcd.numCuts() > 1);
    }

    /**
     * Method to get a multi-cut structure associated to
     * a NodeInst representing a Multiple-cut contact.
     * @param ni the NodeInst being tested.
     * @return a non-null MultiCutData pointer if it is a Multiple-cut contact
     */
    public MultiCutData getMultiCutData(NodeInst ni) {
        if (ni.isCellInstance()) {
            return null;
        }
        PrimitiveNode pnp = (PrimitiveNode) ni.getProto();
        if (!pnp.isMulticut()) {
            return null;
        }

        return ((new MultiCutData(ni.getD(), ni.getTechPool())));
    }

    /**
     * Method to decide whether a NodeInst is a multi-cut contact.
     * The function is done by the Technologies so that it can be subclassed.
     * @param ni the NodeInst being tested.
     * @return true if it is a Multiple-cut contact.
     */
    public boolean isMultiCutCase(NodeInst ni) {
        MultiCutData data = getMultiCutData(ni);
        if (data == null) {
            return false;
        }

        return (isMultiCutInTechnology(data));
    }

    /**
     * Class MultiCutData determines the locations of cuts in a multi-cut contact node.
     */
    public static class MultiCutData {

        /** the size of each cut */
        private long cutSizeX, cutSizeY;
        /** the separation between cuts */
        private long cutSep1D;
        /** the separation between cuts in 3-neighboring or more cases */
        private long cutSep2D;
        /** the number of cuts in X and Y */
        private int cutsX, cutsY;
        /** the total number of cuts */
        private int cutsTotal;

        /**
         * Constructor to initialize for multiple cuts.
         */
        private MultiCutData(ImmutableNodeInst niD, NodeLayer cutLayer) {
            calculateInternalData(niD, cutLayer);
        }

        /**
         * Constructor to initialize for multiple cuts.
         * @param niD the NodeInst with multiple cuts.
         */
        public MultiCutData(ImmutableNodeInst niD, TechPool techPool) {
            calculateInternalData(niD, techPool.getPrimitiveNode((PrimitiveNodeId) niD.protoId).findMulticut());
        }

        private void calculateInternalData(ImmutableNodeInst niD, NodeLayer cutLayer) {
            EPoint size = niD.size;
            assert cutLayer.representation == NodeLayer.MULTICUTBOX;
            long gridWidth = size.getGridX();
            long gridHeight = size.getGridY();
            TechPoint[] techPoints = cutLayer.points;
            long lx = techPoints[0].getX().getAdder().getGrid() + (long) (gridWidth * techPoints[0].getX().getMultiplier());
            long hx = techPoints[1].getX().getAdder().getGrid() + (long) (gridWidth * techPoints[1].getX().getMultiplier());
            long ly = techPoints[0].getY().getAdder().getGrid() + (long) (gridHeight * techPoints[0].getY().getMultiplier());
            long hy = techPoints[1].getY().getAdder().getGrid() + (long) (gridHeight * techPoints[1].getY().getMultiplier());
            cutSizeX = cutLayer.cutSizeX.getGrid();
            cutSizeY = cutLayer.cutSizeY.getGrid();
            cutSep1D = cutLayer.cutSep1D.getGrid();
            cutSep2D = cutLayer.cutSep2D.getGrid();
            if (!niD.isEasyShape()) {
                // get the value of the cut spacing
                Variable var = niD.getVar(NodeLayer.CUT_SPACING);
                if (var != null) {
                    double spacingD = VarContext.objectToDouble(var.getObject(), -1);
                    if (spacingD != -1) {
                        cutSep1D = cutSep2D = DBMath.lambdaToGrid(spacingD);
                    }
                }
            }

            // determine the actual node size
            long cutAreaWidth = hx - lx;
            long cutAreaHeight = hy - ly;

            // number of cuts depends on the size of cut area
            int oneDcutsX = 1 + (int) (cutAreaWidth / (cutSizeX + cutSep1D));
            int oneDcutsY = 1 + (int) (cutAreaHeight / (cutSizeY + cutSep1D));

            // check if configuration gives 2D cuts
            cutsX = oneDcutsX;
            cutsY = oneDcutsY;
            if (cutsX > 1 && cutsY > 1) {
                // recompute number of cuts for 2D spacing
                int twoDcutsX = 1 + (int) (cutAreaWidth / (cutSizeX + cutSep2D));
                int twoDcutsY = 1 + (int) (cutAreaHeight / (cutSizeY + cutSep2D));
                cutsX = twoDcutsX;
                cutsY = twoDcutsY;
                if (cutsX == 1 || cutsY == 1) {
                    // 1D separation sees a 2D grid, but 2D separation sees a linear array: use 1D linear settings
                    if (cutAreaWidth > cutAreaHeight) {
                        cutsX = oneDcutsX;
                    } else {
                        cutsY = oneDcutsY;
                    }
                }
            }
            if (cutsX <= 0) {
                cutsX = 1;
            }
            if (cutsY <= 0) {
                cutsY = 1;
            }
            cutsTotal = cutsX * cutsY;
        }

        /**
         * Method to return the number of cuts in the contact node.
         * @return the number of cuts in the contact node.
         */
        public int numCuts() {
            return cutsTotal;
        }

        /**
         * Method to return the number of cuts along X axis in the contact node.
         * @return the number of cuts in the contact node along X axis.
         */
        public int numCutsX() {
            return cutsX;
        }

        /**
         * Method to return the number of cuts along Y axis in the contact node.
         * @return the number of cuts in the contact node along Y axis.
         */
        public int numCutsY() {
            return cutsY;
        }
        
        /**
         * Method to return the 1D distance in the contact node
         * @return the distance in a 1D cut
         */
        public long getCutSep1D() {
        	return cutSep1D;
        }
        
        /**
         * Method to return the 2D distance in the contact node
         * @return the distance in a 3-neighboring or more cases
         */
        public long getCutSep2D() {
        	return cutSep2D;
        }
        
        /**
         * Method to return the cut X size of the via
         * @return the cut size along the X axis
         */
        public long getCutSizeX() {
        	return cutSizeX;
        }
        
        /**
         * Method to return the cut Y size of the via
         * @return the cut size along the Y axis
         */
        public long getCutSizeY() {
        	return cutSizeY;
        }
    }

    /**
     * Method to convert old primitive node names to their proper NodeProtos.
     * @param name the unknown node name, read from an old Library.
     * @return the proper PrimitiveNode to use for this name.
     */
    public PrimitiveNode convertOldNodeName(String name) {
        return oldNodeNames.get(name);
    }

    public Map<String, PrimitiveNode> getOldNodeNames() {
        return new TreeMap<String, PrimitiveNode>(oldNodeNames);
    }

    /****************************** PORTS ******************************/
    /**
     * Returns a polygon that describes a particular port on a NodeInst.
     * @param ni the NodeInst that has the port of interest.
     * The prototype of this NodeInst must be a PrimitiveNode and not a Cell.
     * @param pp the PrimitivePort on that NodeInst that is being described.
     * @return a Poly object that describes this PrimitivePort graphically.
     */
    public Poly getShapeOfPort(NodeInst ni, PrimitivePort pp) {
        Poly.Builder polyBuilder = Poly.threadLocalLambdaBuilder();
        return polyBuilder.getShape(ni, pp);
    }

//    /**
//     * Returns a polygon that describes a particular port on a NodeInst.
//     * @param ni the NodeInst that has the port of interest.
//     * The prototype of this NodeInst must be a PrimitiveNode and not a Cell.
//     * @param pp the PrimitivePort on that NodeInst that is being described.
//     * @param selectPt if not null, it requests a new location on the port,
//     * away from existing arcs, and close to this point.
//     * This is useful for "area" ports such as the left side of AND and OR gates.
//     * @return a Poly object that describes this PrimitivePort graphically.
//     */
//    public Poly getShapeOfPort(NodeInst ni, PrimitivePort pp, Point2D selectPt) {
//        Poly.Builder polyBuilder = Poly.threadLocalLambdaBuilder();
//        return polyBuilder.getShape(ni, pp, selectPt);
//    }

    /**
     * Method to convert old primitive port names to their proper PortProtos.
     * This method is overridden by those technologies that have any special port name conversion issues.
     * By default, there is little to be done, because by the time this
     * method is called, normal searches have failed.
     * @param portName the unknown port name, read from an old Library.
     * @param np the PrimitiveNode on which this port resides.
     * @return the proper PrimitivePort to use for this name.
     */
    public PrimitivePort convertOldPortName(String portName, PrimitiveNode np) {
        // some technologies switched from ports ending in "-bot" to the ending "-bottom"
        int len = portName.length() - 4;
        if (len > 0 && portName.substring(len).equals("-bot")) {
            PrimitivePort pp = (PrimitivePort) np.findPortProto(portName + "tom");
            if (pp != null) {
                return pp;
            }
        }
        if (np.getNumPorts() == 1) {
            return np.getPort(0);
        }
        return null;
    }

    /**
     * Tells if all ArcProtos can connect to the PrimitivePort
     * @param pp PrimitivePort to test
     * @return true if all ArcProtos can connect to the PrimitivePort
     */
    public boolean isUniversalConnectivityPort(PrimitivePort pp) {
        return false;
    }

    /*********************** PARASITIC SETTINGS ***************************/
    private Setting makeParasiticSetting(String what, double factory) {
        String techShortName = getTechShortName();
        if (techShortName == null) {
            techShortName = getTechName();
        }
        return getProjectSettings().makeDoubleSetting(what + "IN" + getTechName(), TECH_NODE,
                what, "Parasitic tab", techShortName + " " + what, factory);
    }

    private Setting makeParasiticSetting(String what, boolean factory) {
        String techShortName = getTechShortName();
        if (techShortName == null) {
            techShortName = getTechName();
        }
        return getProjectSettings().makeBooleanSetting(what + "IN" + getTechName(), TECH_NODE,
                what, "Parasitic tab", techShortName + " " + what, factory);
    }

    /**
     * Returns the minimum resistance of this Technology.
     * Default value is 10.0
     * @return the minimum resistance of this Technology.
     */
    public double getMinResistance() {
        return cacheMinResistance.getDouble();
    }

    /**
     * Returns project preferences to tell the minimum resistance of this Technology.
     * @return project preferences to tell the minimum resistance of this Technology.
     */
    public Setting getMinResistanceSetting() {
        return cacheMinResistance;
    }

    /**
     * Returns the minimum capacitance of this Technology.
     * Default value is 0.0
     * @return the minimum capacitance of this Technology.
     */
    public double getMinCapacitance() {
        // 0.0 is the default value
        return cacheMinCapacitance.getDouble();
    }

    /**
     * Returns project preferences to tell the minimum capacitance of this Technology.
     * @return project preferences to tell the minimum capacitance of this Technology.
     */
    public Setting getMinCapacitanceSetting() {
        return cacheMinCapacitance;
    }

    /**
     * Get the maximum series resistance for layout extraction
     *  for this Technology.
     * @return the maximum series resistance of extracted layout nets
     */
    public double getMaxSeriesResistance() {
        return cacheMaxSeriesResistance.getDouble();
    }

    /**
     * Returns project preferences to tell the maximum series resistance for layout extraction
     *  for this Technology.
     * @return project preferences to tell the maximum series resistance for layout extraction
     *  for this Technology.
     */
    public Setting getMaxSeriesResistanceSetting() {
        return cacheMaxSeriesResistance;
    }

    /**
     * Returns true if gate is included in resistance calculation. False is the default.
     * @return true if gate is included in resistance calculation.
     */
    public boolean isGateIncluded() {
        // False is the default
        return cacheIncludeGate.getBoolean();
    }

    /**
     * Returns project preferences to tell gate inclusion.
     * @return project preferences to tell gate inclusion
     */
    public Setting getGateIncludedSetting() {
        return cacheIncludeGate;
    }

    /**
     * Returns true if ground network is included in parasitics calculation. False is the default.
     * @return true if ground network is included.
     */
    public boolean isGroundNetIncluded() {
        // False is the default
        return cacheIncludeGnd.getBoolean();
    }

    /**
     * Returns project preferences to tell ground network inclusion.
     * @return project preferences to tell ground network inclusion
     */
    public Setting getGroundNetIncludedSetting() {
        return cacheIncludeGnd;
    }

    /**
     * Gets the gate length subtraction for this Technology (in microns).
     * This is used because there is sometimes a subtracted offset from the layout
     * to the drawn length.
     * @return the gate length subtraction for this Technology
     */
    public double getGateLengthSubtraction() {
        return cacheGateLengthSubtraction.getDouble();
    }

    /**
     * Returns project preferences to tell the gate length subtraction for this Technology (in microns)
     * This is used because there is sometimes a subtracted offset from the layout
     * to the drawn length.
     * @return project preferences to tell the subtraction value for a gate length in microns
     */
    public Setting getGateLengthSubtractionSetting() {
        return cacheGateLengthSubtraction;
    }

    /**
     * Method to set default parasitic values on this Technology.
     * These values are not saved in the options.
     * @param minResistance the minimum resistance in this Technology.
     * @param minCapacitance the minimum capacitance in this Technology.
     */
    public void setFactoryParasitics(double minResistance, double minCapacitance) {
        cacheMinResistance = makeParasiticSetting("MininumResistance", minResistance);
        cacheMinCapacitance = makeParasiticSetting("MininumCapacitance", minCapacitance);
    }

    /*********************** LOGICAL EFFORT SETTINGS ***************************/
    private Setting.Group getLESettingsNode() {
        return getProjectSettings().node("LogicalEffort");
    }

    private Setting makeLESetting(String what, double factory) {
        String techShortName = getTechShortName();
        if (techShortName == null) {
            techShortName = getTechName();
        }
        return getLESettingsNode().makeDoubleSetting(what + "IN" + getTechName(), TECH_NODE,
                what, "Logical Effort tab", techShortName + " " + what, factory);
    }

//     private Setting makeLESetting(String what, int factory) {
//         String techShortName = getTechShortName();
//         if (techShortName == null) techShortName = getTechName();
//         return Setting.makeIntSetting(what + "IN" + getTechName(), prefs,
//                 getLESettingsNode(), what,
//                 "Logical Effort tab", techShortName + " " + what, factory);
//     }
    // ************************ tech specific?  - start *****************************
//    /**
//	 * Method to get the Global Fanout for Logical Effort.
//	 * The default is DEFAULT_GLOBALFANOUT.
//	 * @return the Global Fanout for Logical Effort.
//	 */
//	public double getGlobalFanout()
//	{
//		return cacheGlobalFanout.getDouble();
//	}
//	/**
//	 * Method to set the Global Fanout for Logical Effort.
//	 * @param fo the Global Fanout for Logical Effort.
//	 */
//	public void setGlobalFanout(double fo)
//	{
//		cacheGlobalFanout.setDouble(fo);
//	}
//
//	/**
//	 * Method to get the Convergence Epsilon value for Logical Effort.
//	 * The default is DEFAULT_EPSILON.
//	 * @return the Convergence Epsilon value for Logical Effort.
//	 */
//	public double getConvergenceEpsilon()
//	{
//		return cacheConvergenceEpsilon.getDouble();
//	}
//	/**
//	 * Method to set the Convergence Epsilon value for Logical Effort.
//	 * @param ep the Convergence Epsilon value for Logical Effort.
//	 */
//	public void setConvergenceEpsilon(double ep)
//	{
//		cacheConvergenceEpsilon.setDouble(ep);
//	}
//
//	/**
//	 * Method to get the maximum number of iterations for Logical Effort.
//	 * The default is DEFAULT_MAXITER.
//	 * @return the maximum number of iterations for Logical Effort.
//	 */
//	public int getMaxIterations()
//	{
//		return cacheMaxIterations.getInt();
//	}
//	/**
//	 * Method to set the maximum number of iterations for Logical Effort.
//	 * @param it the maximum number of iterations for Logical Effort.
//	 */
//	public void setMaxIterations(int it)
//	{
//		cacheMaxIterations.setInt(it);
//	}
//
//    /**
//     * Method to get the keeper size ratio for Logical Effort.
//     * The default is DEFAULT_KEEPERRATIO.
//     * @return the keeper size ratio for Logical Effort.
//     */
//    public double getKeeperRatio()
//    {
//        return cacheKeeperRatio.getDouble();
//    }
//    /**
//     * Method to set the keeper size ratio for Logical Effort.
//     * @param kr the keeper size ratio for Logical Effort.
//     */
//    public void setKeeperRatio(double kr)
//    {
//        cacheKeeperRatio.setDouble(kr);
//    }
    // ************************ tech specific?  - end *****************************
    protected void setFactoryLESettings(double gateCapacitance, double wireRation, double diffAlpha) {
        cacheGateCapacitance = makeLESetting("GateCapacitance", gateCapacitance);
        cacheWireRatio = makeLESetting("WireRatio", wireRation);
        cacheDiffAlpha = makeLESetting("DiffAlpha", diffAlpha);
    }

    /**
     * Method to get the Gate Capacitance for Logical Effort.
     * The default is DEFAULT_GATECAP.
     * @return the Gate Capacitance for Logical Effort.
     */
    public double getGateCapacitance() {
        return cacheGateCapacitance.getDouble();
    }

    /**
     * Returns project preferences to tell the Gate Capacitance for Logical Effort.
     * @return project preferences to tell the Gate Capacitance for Logical Effort.
     */
    public Setting getGateCapacitanceSetting() {
        return cacheGateCapacitance;
    }

    /**
     * Method to get the wire capacitance ratio for Logical Effort.
     * The default is DEFAULT_WIRERATIO.
     * @return the wire capacitance ratio for Logical Effort.
     */
    public double getWireRatio() {
        return cacheWireRatio.getDouble();
    }

    /**
     * Returns project preferences to tell the wire capacitance ratio for Logical Effort.
     * @return project preferences to tell the wire capacitance ratio for Logical Effort.
     */
    public Setting getWireRatioSetting() {
        return cacheWireRatio;
    }

    /**
     * Method to get the diffusion to gate capacitance ratio for Logical Effort.
     * The default is DEFAULT_DIFFALPHA.
     * @return the diffusion to gate capacitance ratio for Logical Effort.
     */
    public double getDiffAlpha() {
        return cacheDiffAlpha.getDouble();
    }

    /**
     * Returns project preferences to tell the diffusion to gate capacitance ratio for Logical Effort.
     * @return project preferences to tell the diffusion to gate capacitance ratio for Logical Effort.
     */
    public Setting getDiffAlphaSetting() {
        return cacheDiffAlpha;
    }

    // ================================================================
    /**
     * Method to return the level-1 header cards for SPICE in this Technology.
     * The default is [""].
     * @return the level-1 header cards for SPICE in this Technology.
     */
    public String[] getSpiceHeaderLevel1() {
        return spiceHeaderLevel1;
    }

    /**
     * Method to set the level-1 header cards for SPICE in this Technology.
     * @param lines the level-1 header cards for SPICE in this Technology.
     */
    public void setSpiceHeaderLevel1(String[] lines) {
        spiceHeaderLevel1 = lines;
    }

    /**
     * Method to return the level-2 header cards for SPICE in this Technology.
     * The default is [""].
     * @return the level-2 header cards for SPICE in this Technology.
     */
    public String[] getSpiceHeaderLevel2() {
        return spiceHeaderLevel2;
    }

    /**
     * Method to set the level-2 header cards for SPICE in this Technology.
     * @param lines the level-2 header cards for SPICE in this Technology.
     */
    public void setSpiceHeaderLevel2(String[] lines) {
        spiceHeaderLevel2 = lines;
    }

    /**
     * Method to return the level-3 header cards for SPICE in this Technology.
     * The default is [""].
     * @return the level-3 header cards for SPICE in this Technology.
     */
    public String[] getSpiceHeaderLevel3() {
        return spiceHeaderLevel3;
    }

    /**
     * Method to set the level-3 header cards for SPICE in this Technology.
     * @param lines the level-3 header cards for SPICE in this Technology.
     */
    public void setSpiceHeaderLevel3(String[] lines) {
        spiceHeaderLevel3 = lines;
    }

    /****************************** MISCELANEOUS ******************************/
    /**
     * Sets the technology to be "non-electrical".
     * Users should never call this method.
     * It is set once by the technology during initialization.
     * Examples of non-electrical technologies are "Artwork" and "Gem".
     */
    protected void setNonElectrical() {
        userBits |= NONELECTRICAL;
    }

    /**
     * Returns true if this technology is "non-electrical".
     * @return true if this technology is "non-electrical".
     * Examples of non-electrical technologies are "Artwork" and "Gem".
     */
    public boolean isNonElectrical() {
        return (userBits & NONELECTRICAL) != 0;
    }

    /**
     * Sets the technology to be non-standard.
     * Users should never call this method.
     * It is set once by the technology during initialization.
     * A non-standard technology cannot be edited in the technology editor.
     * Examples are Schematics and Artwork, which have more complex graphics.
     */
    protected void setNonStandard() {
        userBits |= NONSTANDARD;
    }

    /**
     * Returns true if this technology is non-standard.
     * @return true if this technology is non-standard.
     * A non-standard technology cannot be edited in the technology editor.
     * Examples are Schematics and Artwork, which have more complex graphics.
     */
    public boolean isNonStandard() {
        return (userBits & NONSTANDARD) != 0;
    }

    /**
     * Sets the technology to be "static".
     * Users should never call this method.
     * It is set once by the technology during initialization.
     * Static technologies are the core set of technologies in Electric that are
     * essential, and cannot be deleted.
     * The technology-editor can create others later, and they can be deleted.
     */
    protected void setStaticTechnology() {
        userBits |= STATICTECHNOLOGY;
    }

    /**
     * Returns true if this technology is "static" (cannot be deleted).
     * @return true if this technology is "static" (cannot be deleted).
     * Static technologies are the core set of technologies in Electric that are
     * essential, and cannot be deleted.
     * The technology-editor can create others later, and they can be deleted.
     */
    public boolean isStaticTechnology() {
        return (userBits & STATICTECHNOLOGY) != 0;
    }

    /**
     * Returns the TechId of this technology.
     * Each technology has a unique name, such as "mocmos" (MOSIS CMOS).
     * @return the TechId of this technology.
     */
    public TechId getId() {
        return techId;
    }

    /**
     * Returns the name of this technology.
     * Each technology has a unique name, such as "mocmos" (MOSIS CMOS).
     * @return the name of this technology.
     */
    public String getTechName() {
        return techId.techName;
    }

    /**
     * Sets the name of this technology.
     * Technology names must be unique.
     */
    public void setTechName(String techName) {
        throw new UnsupportedOperationException(); // Correct implementation must also rename ProjectSettings and Preferences of this Technology

//		for(Iterator<Technology> it = Technology.getTechnologies(); it.hasNext(); )
//		{
//			Technology tech = it.next();
//			if (tech == this) continue;
//			if (tech.techName.equalsIgnoreCase(techName))
//			{
//				System.out.println("Cannot rename " + this + "to '" + techName + "' because that name is used by another technology");
//				return;
//			}
//		}
//		if (!jelibSafeName(techName))
//			System.out.println("Technology name " + techName + " is not safe to write into JELIB");
//		this.techName = techName;
    }

    /**
     * Method checks that string is safe to write into JELIB file without
     * conversion.
     * @param str the string to check.
     * @return true if string is safe to write into JELIB file.
     */
    static boolean jelibSafeName(String str) {
        return TechId.jelibSafeName(str);
    }

    /**
     * Returns the short name of this technology.
     * The short name is user readable ("MOSIS CMOS" instead of "mocmos")
     * but is shorter than the "description" which often includes options.
     * @return the short name of this technology.
     */
    public String getTechShortName() {
        return techShortName;
    }

    /**
     * Sets the short name of this technology.
     * The short name is user readable ("MOSIS CMOS" instead of "mocmos")
     * but is shorter than the "description" which often includes options.
     * @param techShortName the short name for this technology.
     */
    protected void setTechShortName(String techShortName) {
        this.techShortName = techShortName;
    }

    /**
     * Returns the full description of this Technology.
     * Full descriptions go beyond the one-word technology name by including such
     * information as foundry, number of available layers, and process specifics.
     * For example, "Complementary MOS (from MOSIS, Submicron, 2-6 metals [4], double poly)".
     * @return the full description of this Technology.
     */
    public String getTechDesc() {
        return techDesc;
    }

    /**
     * Sets the full description of this Technology.
     * Full descriptions go beyond the one-word technology name by including such
     * information as foundry, number of available layers, and process specifics.
     * For example, "Complementary MOS (from MOSIS, Submicron, 2-6 metals [4], double poly)".
     */
    public void setTechDesc(String techDesc) {
        this.techDesc = techDesc;
    }

    /**
     * Returns the scale for this Technology.
     * The technology's scale is for manufacturing output, which must convert
     * the unit-based values in Electric to real-world values (in nanometers).
     * @return the scale for this Technology.
     */
    public double getScale() {
        return cacheScale.getDouble();
    }

    /**
     * Method to obtain the Variable name for scaling this Technology.
     * Do not use this for arbitrary use.
     * The method exists so that ELIB readers can handle the unusual location
     * of scale information in the ELIB files.
     * @return the Variable name for scaling this Technology.
     */
    public String getScaleVariableName() {
        return "ScaleFOR" + getTechName();
    }

    /**
     * Sets the factory scale of this technology.
     * The technology's scale is for manufacturing output, which must convert
     * the unit-based values in Electric to real-world values (in nanometers).
     * @param factory the factory scale between this technology and the real units.
     * @param scaleRelevant true if this is a layout technology, and the scale factor has meaning.
     */
    protected void setFactoryScale(double factory, boolean scaleRelevant) {
        this.scaleRelevant = scaleRelevant;
        String techShortName = getTechShortName();
        if (techShortName == null) {
            techShortName = getTechName();
        }
        cacheScale = getProjectSettings().makeDoubleSetting(getScaleVariableName(), TECH_NODE,
                "Scale", "Scale tab", techShortName + " scale", factory);
        cacheScale.setValidOption(isScaleRelevant());
    }

    /**
     * Returns project preferences to tell the scale of this technology.
     * The technology's scale is for manufacturing output, which must convert
     * the unit-based values in Electric to real-world values (in nanometers).
     * @return project preferences to tell the scale between this technology and the real units.
     */
    public Setting getScaleSetting() {
        return cacheScale;
    }

    /**
     * Method to tell whether scaling is relevant for this Technology.
     * Most technologies produce drawings that are exact images of a final product.
     * For these technologies (CMOS, bipolar, etc.) the "scale" from displayed grid
     * units to actual dimensions is a relevant factor.
     * Other technologies, such as schematics, artwork, and generic,
     * are not converted to physical objects, and "scale" is not relevant no meaning for them.
     * @return true if scaling is relevant for this Technology.
     */
    public boolean isScaleRelevant() {
        return scaleRelevant;
    }

    /**
     * Method to set Technology resolution in IO/DRC tools.
     * This has to be stored per technology.
     * @param factory factory value
     */
    protected void setFactoryResolution(double factory) {
        factoryResolution = ECoord.fromLambdaRoundSizeGrid(factory);
    }

    /**
     * Method to retrieve the default resolution associated to the technology.
     * This is the minimum size unit that can be represented.
     * @return the technology's default resolution value.
     */
    public ECoord getFactoryResolution() {
        return factoryResolution;
    }

    /**
     * Method to get foundry in Tech Palette. Different foundry can define different DRC rules.
     * The default is "Generic".
     * @return the foundry to use in Tech Palette
     */
    public String getPrefFoundry() {
        return paramFoundry;
    }

    /**
     * Returns project preferences to tell foundry for DRC rules.
     * @return project preferences to tell the foundry for DRC rules.
     */
    public Setting getPrefFoundrySetting() {
        return cacheFoundry;
    }

    /**
     * Find the Foundry in this technology with a particular name. Protected so sub classes will use it
     * @param name the name of the desired Foundry.
     * @return the Foundry with the same name, or null if no Foundry matches.
     */
    protected Foundry findFoundry(String name) {
        if (name == null) {
            return null;
        }

        for (Foundry f : foundries) {
            Foundry.Type t = f.getType();
            if (t.getName().equalsIgnoreCase(name)) {
                return f;
            }
        }
        return null;
    }

    /**
     * Get an iterator over all of the Manufacturers.
     * @return an iterator over all of the Manufacturers.
     */
    public Iterator<Foundry> getFoundries() {
        return foundries.iterator();
    }

    /**
     * Method to create a new on this technology.
     * @param mode factory type
     * @param fileURL URL of xml file with description of rules
     * @param gdsLayers strings with definition of gds numbers for layers
     */
    protected void newFoundry(Foundry.Type mode, URL fileURL, String... gdsLayers) {
        Foundry foundry = new Foundry(this, mode, fileURL, gdsLayers);
        foundries.add(foundry);
    }

    /**
     * Method to get the foundry index associated with this technology.
     * @return the foundry index associated with this technology.
     */
    public Foundry getSelectedFoundry() {
        String foundryName = getPrefFoundry();
        Foundry f = findFoundry(foundryName);
        if (f != null) {
            return f;
        }
        if (foundries.size() > 0) {
            f = foundries.get(0);
            if (foundryName.length() > 0) {
                System.out.println("Foundry '" + foundryName + "' not available in Technology '" + this.getTechName()
                        + "'. Setting '" + f.toString() + "' as foundry.");
            }
            return f;
        }
        return f;
    }

    /**
     * 
     */
    /**
     * Method to retrieve Electric layers based on GDS numbers. Set with pinLayers is also provided.
     * @param pinLayers Set to store pin numbers. Must not be null.
     * @return Map of layers.
     */
    public Map<Integer,List<Layer>> getLayersPerGDSNumber(Set<Integer> pinLayers)
    {
    	assert(pinLayers != null);
    	Map<Integer,List<Layer>> layerNames = new HashMap<Integer,List<Layer>>();
		
        for(Map.Entry<Layer,String> e: getGDSLayers().entrySet())
		{
			Layer layer = e.getKey();
            String gdsName = e.getValue();
            GDSLayers gdsl = GDSLayers.parseLayerString(gdsName);
            
            for (GDSLayerType type : GDSLayerType.values())
            {
            	if (gdsl.hasLayerType(type))
            	{
                	int val = gdsl.getLayer(type);
                	// not so elegant exception
                	if (type == GDSLayerType.PIN)
                        pinLayers.add(val);
                	
                	List<Layer> list = layerNames.get(val);
                	if (list == null)
                	{
                		list = new ArrayList<Layer>();
                		layerNames.put(new Integer(val), list);
                	}
                	list.add(layer);
            	}
            }
		}	
        return layerNames;
    }
    
    /**
     * Method to return the map from Layers of this Technology to their GDS names in current foundry.
     * Only Layers with non-empty GDS names are present in the map
     * @return the map from Layers to GDS names
     */
    public Map<Layer, String> getGDSLayers() {
        Foundry foundry = getSelectedFoundry();
        Map<Layer, String> gdsLayers = Collections.emptyMap();
        if (foundry != null) {
            gdsLayers = foundry.getGDSLayers();
        }
        return gdsLayers;
    }

    /**
     * Sets the color map for transparent layers in this technology.
     * Users should never call this method.
     * It is set once by the technology during initialization.
     * @param layers is an array of colors, one per transparent layer.
     * This is expanded to a map that is 2 to the power "getNumTransparentLayers()".
     * Color merging is computed automatically.
     */
    protected void setFactoryTransparentLayers(Color[] layers) {
        factoryTransparentColors = layers;
    }

    /**
     * Method to return the factory default colors for the transparent layers in this Technology.
     * @return the factory default colors for the transparent layers in this Technology.
     */
    public Color[] getFactoryTransparentLayerColors() {
        return factoryTransparentColors.clone();
    }

    /**
     * Method to return the colors for the transparent layers in this Technology.
     * @return the factory for the transparent layers in this Technology.
     */
    public Color[] getTransparentLayerColors() {
        return UserInterfaceMain.getGraphicsPreferences().getTransparentLayerColors(this);
    }

    /**
     * Returns the number of transparent layers in this technology.
     * Informs the display system of the number of overlapping or transparent layers
     * in use.
     * @return the number of transparent layers in this technology.
     * There may be 0 transparent layers in technologies that don't do overlapping,
     * such as Schematics.
     */
    public int getNumTransparentLayers() {
        return UserInterfaceMain.getGraphicsPreferences().getNumTransparentLayers(this);
    }

    /**
     * Sets the color map from transparent layers in this technology.
     * @param layers an array of colors, one per transparent layer.
     * This is expanded to a map that is 2 to the power "getNumTransparentLayers()".
     * Color merging is computed automatically.
     */
    public void setColorMapFromLayers(Color[] layers) {
        UserInterfaceMain.setGraphicsPreferences(UserInterfaceMain.getGraphicsPreferences().withTransparentLayerColors(this, layers));
    }

    /**
     * Method to get the factory design rules.
     * Individual technologies subclass this to create their own rules.
     * @return the design rules for this Technology.
     * Returns null if there are no design rules in this Technology.
     */
    public XMLRules getFactoryDesignRules() {
        return makeFactoryDesignRules();
    }

    /**
     * Method to get the factory design rules.
     * Individual technologies subclass this to create their own rules.
     * @return the design rules for this Technology.
     * Returns null if there are no design rules in this Technology.
     */
    protected XMLRules makeFactoryDesignRules() {
        XMLRules rules = new XMLRules(this);

        Foundry foundry = getSelectedFoundry();
        List<DRCTemplate> rulesList = foundry.getRules();
        boolean pSubstrateProcess = User.isPSubstrateProcessLayoutTechnology();

        // load the DRC tables from the explanation table
        if (rulesList != null) {
            for (DRCTemplate rule : rulesList) {
                if (rule.ruleType != DRCTemplate.DRCRuleType.NODSIZ) {
                    rules.loadDRCRules(this, foundry, rule, pSubstrateProcess);
                }
            }
            for (DRCTemplate rule : rulesList) {
                if (rule.ruleType == DRCTemplate.DRCRuleType.NODSIZ) {
                    rules.loadDRCRules(this, foundry, rule, pSubstrateProcess);
                }
            }
        }

        return rules;
    }

    /**
     * Method to compare a Rules set with the "factory" set and construct an override string.
     * @param origRules
     * @param newRules
     * @return a StringBuffer that describes any overrides.  Returns "" if there are none.
     */
    public static StringBuffer getRuleDifferences(XMLRules origRules, XMLRules newRules) {
        return (new StringBuffer(""));
    }

    /**
     * Method to be called from DRC:setRules
     * @param newRules
     */
    public void setRuleVariables(XMLRules newRules) {
    }

    /**
     * Returns the color map for transparent layers in this technology.
     * @return the color map for transparent layers in this technology.
     * The number of entries in this map equals 2 to the power "getNumTransparentLayers()".
     */
    public Color[] getColorMap() {
        return UserInterfaceMain.getGraphicsPreferences().getColorMap(this);
    }

    /**
     * Method to create a full colormap from the transparent colors.
     * @param transparentColors the transparent colors.
     * @return a full map that combines all of the colors.
     * The map is 2^transparentcolors.length in size.
     */
    public static Color[] makeColorMap(Color[] transparentColors) {
        int numEntries = 1 << transparentColors.length;
        Color[] map = new Color[numEntries];
        for (int i = 0; i < numEntries; i++) {
            int r = 200, g = 200, b = 200;
            boolean hasPrevious = false;
            for (int j = 0; j < transparentColors.length; j++) {
                if ((i & (1 << j)) == 0) {
                    continue;
                }
                Color layerColor = transparentColors[j];
                if (hasPrevious) {
                    // get the previous color
                    double[] lastColor = new double[3];
                    lastColor[0] = r / 255.0;
                    lastColor[1] = g / 255.0;
                    lastColor[2] = b / 255.0;
                    normalizeColor(lastColor);

                    // get the current color
                    double[] curColor = new double[3];
                    curColor[0] = layerColor.getRed() / 255.0;
                    curColor[1] = layerColor.getGreen() / 255.0;
                    curColor[2] = layerColor.getBlue() / 255.0;
                    normalizeColor(curColor);

                    // combine them
                    for (int k = 0; k < 3; k++) {
                        curColor[k] += lastColor[k];
                    }
                    normalizeColor(curColor);
                    r = (int) (curColor[0] * 255.0);
                    g = (int) (curColor[1] * 255.0);
                    b = (int) (curColor[2] * 255.0);
                } else {
                    r = layerColor.getRed();
                    g = layerColor.getGreen();
                    b = layerColor.getBlue();
                    hasPrevious = true;
                }
            }
            map[i] = new Color(r, g, b);
        }
        return map;
    }

    /**
     * Method to normalize a color stored in a 3-long array.
     * @param a the array of 3 doubles that holds the color.
     * All values range from 0 to 1.
     * The values are adjusted so that they are normalized.
     */
    private static void normalizeColor(double[] a) {
        double mag = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
        if (mag < 1.0e-11f) {
            return;
        }
        a[0] /= mag;
        a[1] /= mag;
        a[2] /= mag;
    }

    /**
     * Method to determine whether a new technology with the given name would be legal.
     * All technology names must be unique, so the name cannot already be in use.
     * @param techName the name of the new technology that will be created.
     * @return true if the name is valid.
     */
//	private static boolean validTechnology(String techName)
//	{
//		if (Technology.findTechnology(techName) != null)
//		{
//			System.out.println("ERROR: Multiple technologies named " + techName);
//			return false;
//		}
//		return true;
//	}
    /**
     * Method to determine the appropriate Technology to use for a Cell.
     * @param cell the Cell to examine.
     * @return the Technology for that cell.
     */
    public static Technology whatTechnology(NodeProto cell) {
        Technology tech = whatTechnology(cell, null, 0, 0, null);
        return tech;
    }

    /**
     * Method to determine the appropriate technology to use for a cell.
     * The contents of the cell can be defined by the lists of NodeInsts and ArcInsts, or
     * if they are null, then by the contents of the Cell.
     * @param cellOrPrim the Cell to examine.
     * @param nodeProtoList the list of prototypes of NodeInsts in the Cell.
     * @param startNodeProto the starting point in the "nodeProtoList" array.
     * @param endNodeProto the ending point in the "nodeProtoList" array.
     * @param arcProtoList the list of prototypes of ArcInsts in the Cell.
     * @return the Technology for that cell.
     */
    public static Technology whatTechnology(NodeProto cellOrPrim, NodeProto[] nodeProtoList, int startNodeProto, int endNodeProto,
            ArcProto[] arcProtoList) {
        // primitives know their technology
        if (cellOrPrim instanceof PrimitiveNode) {
            return (((PrimitiveNode) cellOrPrim).getTechnology());
        }
        Cell cell = (Cell) cellOrPrim;

        // count the number of technologies
        int maxTech = 0;
        for (Iterator<Technology> it = Technology.getTechnologies(); it.hasNext();) {
            Technology tech = it.next();
            if (tech.getId().techIndex > maxTech) {
                maxTech = tech.getId().techIndex;
            }
        }
        maxTech++;

        // create an array of counts for each technology
        int[] useCount = new int[maxTech];
        for (int i = 0; i < maxTech; i++) {
            useCount[i] = 0;
        }

        // count technologies of all primitive nodes in the cell
        if (nodeProtoList != null) {
            // iterate over the NodeProtos in the list
            for (int i = startNodeProto; i < endNodeProto; i++) {
                NodeProto np = nodeProtoList[i];
                if (np == null) {
                    continue;
                }
                Technology nodeTech = np.getTechnology();
                if (np instanceof Cell) {
                    Cell subCell = (Cell) np;
                    if (subCell.isIcon()) {
                        nodeTech = Schematics.tech();
                    }
                }
                if (nodeTech != null) {
                    useCount[nodeTech.getId().techIndex]++;
                }
            }
        } else {
            for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext();) {
                NodeInst ni = it.next();
                NodeProto np = ni.getProto();
                Technology nodeTech = np.getTechnology();
                if (ni.isCellInstance()) {
                    Cell subCell = (Cell) np;
                    if (subCell.isIcon()) {
                        nodeTech = Schematics.tech();
                    }
                }
                if (nodeTech != null) {
                    useCount[nodeTech.getId().techIndex]++;
                }
            }
        }

        // count technologies of all arcs in the cell
        if (arcProtoList != null) {
            // iterate over the arcprotos in the list
            for (ArcProto ap : arcProtoList) {
                if (ap == null) {
                    continue;
                }
                useCount[ap.getTechnology().getId().techIndex]++;
            }
        } else {
            for (Iterator<ArcInst> it = cell.getArcs(); it.hasNext();) {
                ArcInst ai = it.next();
                ArcProto ap = ai.getProto();
                useCount[ap.getTechnology().getId().techIndex]++;
            }
        }

        // find a consensus
        int best = 0;
        Technology bestTech = null;
        int bestLayout = 0;
        Technology bestLayoutTech = null;
        for (Iterator<Technology> it = Technology.getTechnologies(); it.hasNext();) {
            Technology tech = it.next();

            // always ignore the generic technology
            if (tech instanceof Generic) {
                continue;
            }

            // find the most popular of ALL technologies
            if (useCount[tech.getId().techIndex] > best) {
                best = useCount[tech.getId().techIndex];
                bestTech = tech;
            }

            // find the most popular of the layout technologies
            if (!tech.isLayout()) {
                continue;
            }
            if (useCount[tech.getId().techIndex] > bestLayout) {
                bestLayout = useCount[tech.getId().techIndex];
                bestLayoutTech = tech;
            }
        }

        Technology retTech = null;
        if (cell.isIcon() || cell.getView().isTextView()) {
            // in icons, if there is any artwork, use it
            if (useCount[Artwork.tech().getId().techIndex] > 0) {
                return (Artwork.tech());
            }

            // in icons, if there is nothing, presume artwork
            if (bestTech == null) {
                return (Artwork.tech());
            }

            // use artwork as a default
            retTech = Artwork.tech();
        } else if (cell.isSchematic()) {
            // in schematic, if there are any schematic components, use it
            if (useCount[Schematics.tech().getId().techIndex] > 0) {
                return (Schematics.tech());
            }

            // in schematic, if there is nothing, presume schematic
            if (bestTech == null) {
                return (Schematics.tech());
            }

            // use schematic as a default
            retTech = Schematics.tech();
        } else {
            // use the current layout technology as the default
            retTech = getCurrent();
            if (!retTech.isLayout()) {
                retTech = findTechnology(User.getDefaultTechnology());
            }
            if (retTech == null) {
                retTech = getMocmosTechnology();
            }
        }

        // if a layout technology was voted the most, return it
        if (bestLayoutTech != null) {
            retTech = bestLayoutTech;
        } else {
            // if any technology was voted the most, return it
            if (bestTech != null) {
                retTech = bestTech;
            } else {
//				// if this is an icon, presume the technology of its contents
//				cv = contentsview(cell);
//				if (cv != NONODEPROTO)
//				{
//					if (cv->tech == NOTECHNOLOGY)
//						cv->tech = whattech(cv);
//					retTech = cv->tech;
//				} else
//				{
//					// look at the contents of the sub-cells
//					foundicons = FALSE;
//					for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
//					{
//						np = ni->proto;
//						if (np == NONODEPROTO) continue;
//						if (np->primindex != 0) continue;
//
//						// ignore recursive references (showing icon in contents)
//						if (isiconof(np, cell)) continue;
//
//						// see if the cell has an icon
//						if (np->cellview == el_iconview) foundicons = TRUE;
//
//						// do not follow into another library
//						if (np->lib != cell->lib) continue;
//						onp = contentsview(np);
//						if (onp != NONODEPROTO) np = onp;
//						tech = whattech(np);
//						if (tech == gen_tech) continue;
//						retTech = tech;
//						break;
//					}
//					if (ni == NONODEINST)
//					{
//						// could not find instances that give information: were there icons?
//						if (foundicons) retTech = sch_tech;
//					}
//				}
            }
        }

        // give up and report the generic technology
        return retTech;
    }

    /**
     * Returns true if this Technology is a layout technology.
     * @return true if this Technology is a layout technology.
     */
    public boolean isLayout() {
        return !(this instanceof Artwork /*|| this instanceof EFIDO*/
                || this instanceof GEM || this instanceof Generic || this instanceof Schematics);
    }

    /**
     * Returns true if this Technology is a schematics technology.
     * @return true if this Technology is a schematics technology.
     */
    public boolean isSchematics() {
        return this instanceof Schematics /*|| this instanceof EFIDO*/ || this instanceof GEM;
    }

    /**
     * Compares Technologies by their names.
     * @param that the other Technology.
     * @return a comparison between the Technologies.
     */
    public int compareTo(Technology that) {
        return TextUtils.STRING_NUMBER_ORDER.compare(getTechName(), that.getTechName());
    }

    /**
     * Returns a printable version of this Technology.
     * @return a printable version of this Technology.
     */
    @Override
    public String toString() {
        return "Technology " + getTechName();
    }

    /**
     * Method to check invariants in this Technology.
     * In "debug" mode, prints warnings.
     * @exception AssertionError if invariants are not valid
     */
    private void check() {
        for (TechFactory.Param param : techFactory.getTechParams()) {
            String xmlPath = param.xmlPath;
            String xmlPrefix = getProjectSettings().getXmlPath();
            assert xmlPath.startsWith(xmlPrefix);
            xmlPath = xmlPath.substring(xmlPrefix.length());
            Setting setting = getSetting(xmlPath);
            assert setting.getXmlPath().equals(param.xmlPath);
            assert setting.getPrefPath().equals(param.prefPath);
            assert setting.getFactoryValue().equals(param.factoryValue);
        }
        for (ArcProto ap : arcs.values()) {
            ap.check();
        }

        if (!isNonStandard() && isScaleRelevant() && Job.getDebug()) {
            Map<Layer, ArcProto.Function> layToArcFunction = new HashMap<Layer, ArcProto.Function>();
            for (ArcProto ap : arcs.values()) {
                for (int i = 0; i < ap.getNumArcLayers(); i++) {
                    Layer lay = ap.getLayer(i);
                    Layer.Function fun = lay.getFunction();
                    if (fun.isSubstrate()) {
                        continue;
                    }
                    ArcProto.Function aFun = ap.getFunction();
                    if (aFun.isDiffusion()) {
                        aFun = ArcProto.Function.DIFF;
                    }
//if (getTechName().equals("mocmosold"))
//	System.out.println("-------> ARC "+ap.getName()+" HAS LAYER " + lay.getName()+" WHICH IS FUNCTION " +aFun);
                    if (aFun != ArcProto.Function.WELL || !fun.isDiff()) {
                        layToArcFunction.put(lay, aFun);
                    }
                    break;
                }
            }
            for (Iterator<Layer> it = getLayers(); it.hasNext();) {
                Layer lay = it.next();
                ArcProto.Function aFun = layToArcFunction.get(lay);
                if (aFun != null) {
                    continue;
                }
                Layer.Function lFun = lay.getFunction();
                if (lFun.isDiff()) {
                    aFun = ArcProto.Function.DIFF;
                } else if (lFun.isPoly()) {
                    aFun = ArcProto.Function.getPoly(lFun.getLevel());
                } else if (lFun.isMetal()) {
                    aFun = ArcProto.Function.getMetal(lFun.getLevel());
                }
                if (lFun != null) {
                    layToArcFunction.put(lay, aFun);
//if (getTechName().equals("mocmosold"))
//	System.out.println("-------> LAYER "+lay.getName()+" IS FUNCTION " +aFun);
                }
            }

            // check validity of nodes
            boolean foundNTrans = false, foundPTrans = false;
            for (PrimitiveNode np : nodes.values()) {
                PrimitiveNode.Function fun = np.getFunction();
                if (fun.isNTypeTransistor()) {
                    foundNTrans = true;
                }
                if (fun.isPTypeTransistor()) {
                    foundPTrans = true;
                }
                if (fun.isContact() || fun == PrimitiveNode.Function.CONNECT || fun == PrimitiveNode.Function.WELL) {
                    // check validity of contact nodes
                    Set<ArcProto.Function> neededArcFunctions = new HashSet<ArcProto.Function>();
                    NodeLayer[] nodeLayers = np.getNodeLayers();
                    for (int i = 0; i < nodeLayers.length; i++) {
                        Layer lay = nodeLayers[i].layer;
                        ArcProto.Function aFun = layToArcFunction.get(lay);
//if (getTechName().equals("mocmosold") && np.getName().equals("Metal-1-Well-Con"))
//	System.out.println("-------> "+np.getName()+" HAS LAYER " + lay.getName()+" WHICH IS FUNCTION " +aFun);
                        if (aFun == ArcProto.Function.WELL) {
                            continue;
                        }
                        if (aFun != null) {
                            neededArcFunctions.add(aFun);
                        }
                    }
                    Set<ArcProto.Function> foundArcFunctions = new HashSet<ArcProto.Function>();
                    for (Iterator<PrimitivePort> it = np.getPrimitivePorts(); it.hasNext();) {
                        PrimitivePort pp = it.next();
                        ArcProto[] connections = pp.getConnections();
                        for (int i = 0; i < connections.length; i++) {
                            ArcProto ap = connections[i];
                            ArcProto.Function aFun = ap.getFunction();
                            if (aFun == ArcProto.Function.WELL) {
                                continue;
                            }
                            if (aFun.isDiffusion()) {
                                aFun = ArcProto.Function.DIFF;
                            }
                            if (ap.getTechnology() != this || neededArcFunctions.contains(aFun)) {
                                foundArcFunctions.add(aFun);
                                continue;
                            }

                            // well contacts do not have to have Active even if they connect to that layer
                            if (aFun.isDiffusion() && fun == PrimitiveNode.Function.WELL) {
                                continue;
                            }
                            System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                    + " connects to " + ap.getName() + " but probably should not because that layer is not in the node");
                        }
                    }
                    for (ArcProto.Function aFun : neededArcFunctions) {
                        if (foundArcFunctions.contains(aFun)) {
                            continue;
                        }

                        // well contacts do not have to connect to Active even if that layer is present
                        if (aFun.isDiffusion() && fun == PrimitiveNode.Function.WELL) {
                            continue;
                        }

                        // Discard case if at least one poly arc was found in foundArcFunctions.
                        // This is the case of poly2 contact with extra poly1 as capacitor.
                        if (aFun.isPoly()) {
                            boolean found = false;
                            for (ArcProto.Function f : foundArcFunctions) {
                                if (!f.isPoly()) {
                                    continue; // discard if it is not a poly function
                                }
                                found = f != aFun;
                            }
                            if (found) {
                                continue; // it is a valid poly2 contact
                            }
                        }
                        System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                + " should connect to " + aFun + " because that layer is in the node");
                    }
                } else if (fun.isCapacitor()) {
                    if (np.getNumPorts() < 2) {
                        System.out.println("ERROR: Technology " + getTechName() + ", node " + np.getName()
                                + " must have at least two ports if defined as capacitor");
                    }

                } else if (fun.isFET()) {
                    // check validity of transistors
                    List<PrimitivePort> traPorts = new ArrayList<PrimitivePort>();
                    for (Iterator<PrimitivePort> it = np.getPrimitivePorts(); it.hasNext();) {
                        traPorts.add(it.next());
                    }
                    if (traPorts.size() != 4 && traPorts.size() != 5) {
                        System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                + " should have 4 or 5 ports but has " + traPorts.size());
                    } else {
                        PrimitivePort pLeft = traPorts.get(0);
                        PrimitivePort dTop = traPorts.get(1);
                        PrimitivePort pRight = traPorts.get(2);
                        PrimitivePort dBot = traPorts.get(3);
                        if (!getTechName().startsWith("tft")) {
                            if (!connectsToPoly(pLeft)) {
                                System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                        + ", first port (" + pLeft.getName() + ") should connect to Polysilicon");
                            }
                            if (!connectsToActive(dTop)) {
                                System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                        + ", second port (" + dTop.getName() + ") should connect to Active");
                            }
                            if (!connectsToPoly(pRight)) {
                                System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                        + ", third port (" + pRight.getName() + ") should connect to Polysilicon");
                            }
                            if (!connectsToActive(dBot)) {
                                System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                        + ", fourth port (" + dBot.getName() + ") should connect to Active");
                            }
                        }

                        if (pLeft.getTopology() != pRight.getTopology()) {
                            System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                    + " should connect its Polysilicon ports");
                        }
                        if (dTop.getTopology() == dBot.getTopology()) {
                            System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                    + " should not connect its Active ports to each other");
                        }
                        if (pLeft.getTopology() == dBot.getTopology() || pLeft.getTopology() == dTop.getTopology()) {
                            System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                    + " should not connect its Active ports to its Polysilicon ports");
                        }
                    }
                    // check port connections for electrical layers
                    NodeLayer[] eLayers = np.getElectricalLayers();
                    if (eLayers != null) {
                        boolean foundPort1 = false, foundPort3 = false;
                        int portSize = traPorts.size();
                        for (int i = 0; i < eLayers.length; i++) {
                            if (eLayers[i].getLayer().getFunction().isDiff()) {
                                int portNum = eLayers[i].getPortNum();
                                if (portNum < 0) {
                                    continue;
                                }
                                if (portNum == 1) {
                                    foundPort1 = true;
                                } else if (portNum == 3) {
                                    foundPort3 = true;
                                } else {
                                    System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                            + ", Active layer connected to port " + traPorts.get(portNum).getName());
                                }
                            }
                        }
                        if (!foundPort1) {
                            System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                    + ", no Active layer is connected to port " + ((portSize > 1) ? traPorts.get(1).getName() : ""));
                        }
                        if (!foundPort3) {
                            System.out.println("WARNING: Technology " + getTechName() + ", node " + np.getName()
                                    + ", no Active layer is connected to port " + ((portSize > 3) ? traPorts.get(3).getName() : ""));
                        }
                    }
                }
            }

            // make sure there are N and P transistors
            if (foundNTrans != foundPTrans) {
                // "nmos" technology is known to have just N transistors, "tft" has only P transistors
                if (!getTechName().equals("nmos") && !getTechName().equals("tft")) {
                    String has = foundNTrans ? "N" : "P";
                    String hasnt = foundNTrans ? "P" : "N";
                    System.out.println("WARNING: Technology " + getTechName()
                            + " has " + has + " transistors but has no " + hasnt + " transistors");
                }
            }
        }
    }

    private boolean connectsToPoly(PrimitivePort pp) {
        ArcProto[] connections = pp.getConnections();
        for (int i = 0; i < connections.length; i++) {
            if (connections[i].getFunction().isPoly()) {
                return true;
            }
        }
        return false;
    }

    private boolean connectsToActive(PrimitivePort pp) {
        ArcProto[] connections = pp.getConnections();
        for (int i = 0; i < connections.length; i++) {
            if (connections[i].getFunction().isDiffusion()) {
                return true;
            }
        }
        return false;
    }

    ///////////////////// Generic methods //////////////////////////////////////////////////////////////
//	/**
//	 * Method to change the design rules for layer "layername" layers so that
//	 * the layers are at least "width" wide.  Affects the default arc width
//	 * and the default pin size.
//	 */
//	protected void setLayerMinWidth(String layername, String rulename, double width)
//	{
//		// find the arc and set its default width
//		ArcProto ap = findArcProto(layername);
//		if (ap == null) return;
//
//		boolean hasChanged = false;
//
//        if (ap.getDefaultLambdaBaseWidth() != width)
////        if (ap.getDefaultLambdaFullWidth() != width + ap.getLambdaWidthOffset())
//            hasChanged = true;
//
//		// find the arc's pin and set its size and port offset
//		PrimitiveNode np = ap.findPinProto();
//		if (np == null) return;
//		SizeOffset so = np.getProtoSizeOffset();
//		double newWidth = width + so.getLowXOffset() + so.getHighXOffset();
//		double newHeight = width + so.getLowYOffset() + so.getHighYOffset();
//
//        if (np.getDefHeight() != newHeight || np.getDefWidth() != newWidth)
//            hasChanged = true;
//
//		PrimitivePort pp = (PrimitivePort)np.getPorts().next();
//		EdgeH left = pp.getLeft();
//		EdgeH right = pp.getRight();
//		EdgeV bottom = pp.getBottom();
//		EdgeV top = pp.getTop();
//		double indent = newWidth / 2;
//
//        if (left.getAdder() != indent || right.getAdder() != -indent ||
//            top.getAdder() != -indent || bottom.getAdder() != indent)
//            hasChanged = true;
//		if (hasChanged)
//		{
//			// describe the error
//            String errorMessage = "User preference of " + width + " overwrites original layer minimum size in layer '"
//					+ layername + "', primitive '" + np.getName() + ":" + getTechShortName() + "' by rule " + rulename;
//			if (Job.LOCALDEBUGFLAG) System.out.println(errorMessage);
//		}
//	}
//
//    protected void setDefNodeSize(PrimitiveNode nty, double wid, double hei)
//    {
//        double xindent = (nty.getDefWidth() - wid) / 2;
//		double yindent = (nty.getDefHeight() - hei) / 2;
//		nty.setSizeOffset(new SizeOffset(xindent, xindent, yindent, yindent));  // bug 1040
//    }
    /**
     * Method to set the surround distance of layer "layer" from the via in node "nodename" to "surround".
     */
//	protected void setLayerSurroundVia(PrimitiveNode nty, Layer layer, double surround)
//	{
//		// find the via size
//		double [] specialValues = nty.getSpecialValues();
//		double viasize = specialValues[0];
//		double layersize = viasize + surround*2;
//		double indent = (nty.getDefWidth() - layersize) / 2;
//
//		Technology.NodeLayer oneLayer = nty.findNodeLayer(layer, false);
//		if (oneLayer != null)
//		{
//			TechPoint [] points = oneLayer.getPoints();
//			EdgeH left = points[0].getX();
//			EdgeH right = points[1].getX();
//			EdgeV bottom = points[0].getY();
//			EdgeV top = points[1].getY();
//			left.setAdder(indent);
//			right.setAdder(-indent);
//			top.setAdder(-indent);
//			bottom.setAdder(indent);
//		}
//	}
    /********************* FOR Wiring tool **********************/
    public List<NodeProto> getMetalContactCluster(Layer l1, Layer l2) {
        List<NodeProto> list = new ArrayList<NodeProto>();

        Xml.MenuPalette menu = xmlTech.menuPalette;
        for (List<?> objList : menu.menuBoxes) {
            for (Object obj : objList) {
                if (obj instanceof Xml.MenuNodeInst) {
                    Xml.MenuNodeInst menuItem = (Xml.MenuNodeInst) obj;

                    if (!menuItem.function.isContact()) {
                        continue; // not a contact
                    }
                    NodeProto np = findNodeProto(((Xml.MenuNodeInst) obj).protoName);

                    if (np instanceof PrimitiveNode) {
                        PrimitiveNode pn = (PrimitiveNode) np;
                        boolean found1 = false, found2 = false;
                        for (NodeLayer l : pn.getNodeLayers()) {
                            if (l.getLayer() == l1) {
                                found1 = true;
                            }
                            if (l.getLayer() == l2) {
                                found2 = true;
                            }
                            if (found1 && found2) {
                                break; // found both
                            }
                        }
                        // both layers found in this particular node
                        if (found1 && found2) {
                            list.add(pn);
                        }
                    }
                }
            }
        }
        return list;
    }

    /********************* FOR GUI **********************/
    protected void loadFactoryMenuPalette(URL menuURL) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(menuURL.openConnection().getInputStream()));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            reader.close();
            factoryMenuPalette = parseComponentMenuXML(sb.toString());
        } catch (IOException e) {
            System.out.println("Error parsing XML component menu data");
            e.printStackTrace();
        }
    }

    /**
     * Parses Xml string with component menu definition for this technology
     * @param nodeGroupXML Xml string with component menu definition
     */
    public Xml.MenuPalette parseComponentMenuXML(String nodeGroupXML) {
        // parse the preference and build a component menu
        List<Xml.PrimitiveNodeGroup> xmlNodeGroups = new ArrayList<Xml.PrimitiveNodeGroup>();
        List<Xml.PrimitiveNode> xmlPureLayerNodes = new ArrayList<Xml.PrimitiveNode>();
        HashSet<PrimitiveNodeGroup> groupsDone = new HashSet<PrimitiveNodeGroup>();
        for (Iterator<PrimitiveNode> it = getNodes(); it.hasNext();) {
            PrimitiveNode pnp = it.next();
            if (pnp.getFunction() == PrimitiveNode.Function.NODE) {
                Xml.PrimitiveNode n = new Xml.PrimitiveNode();
                n.name = pnp.getName();
                n.function = PrimitiveNode.Function.NODE;
                xmlPureLayerNodes.add(n);
                continue;
            }
            PrimitiveNodeGroup group = pnp.getPrimitiveNodeGroup();
            if (group != null) {
                if (groupsDone.contains(group)) {
                    continue;
                }
                Xml.PrimitiveNodeGroup ng = new Xml.PrimitiveNodeGroup();
                for (PrimitiveNode pn : group.getNodes()) {
                    Xml.PrimitiveNode n = new Xml.PrimitiveNode();
                    n.name = pn.getName();
                    ng.nodes.add(n);
                }
                xmlNodeGroups.add(ng);
                groupsDone.add(group);
            } else {
                Xml.PrimitiveNodeGroup ng = new Xml.PrimitiveNodeGroup();
                ng.isSingleton = true;
                Xml.PrimitiveNode n = new Xml.PrimitiveNode();
                n.name = pnp.getName();
                ng.nodes.add(n);
                xmlNodeGroups.add(ng);
            }
        }
        List<Xml.ArcProto> xmlArcs = new ArrayList<Xml.ArcProto>();
        for (ArcProto ap : arcs.values()) {
            Xml.ArcProto xap = new Xml.ArcProto();
            xap.name = ap.getName();
            xmlArcs.add(xap);
        }
        return Xml.parseComponentMenuXMLTechEdit(nodeGroupXML, xmlNodeGroups, xmlArcs, xmlPureLayerNodes);
    }

    /**
     * Method to construct a factory default Xml menu palette.
     * @return the factory default Xml menu palette.
     */
    public Xml.MenuPalette getFactoryMenuPalette() {
        if (factoryMenuPalette == null) {
            makeDummyFactoryMenuPalette();
        }
        assert factoryMenuPalette != null;
        return factoryMenuPalette;
    }

    private void makeDummyFactoryMenuPalette() {
        // compute palette information automatically
        List<List<?>> things = new ArrayList<List<?>>();
        for (Iterator<ArcProto> it = getArcs(); it.hasNext();) {
            ArcProto ap = it.next();
            if (ap.isNotUsed()) {
                continue;
            }
            Xml.ArcProto xap = new Xml.ArcProto();
            xap.name = ap.getName();
            List<Xml.ArcProto> list = Collections.singletonList(xap);
            things.add(list);
        }
        Set<PrimitiveNodeGroup> groups = new HashSet<PrimitiveNodeGroup>();
        for (Iterator<PrimitiveNode> it = getNodes(); it.hasNext();) {
            PrimitiveNode np = it.next();
            if (np.isNotUsed()) {
                continue;
            }
            if (np.getFunction() == PrimitiveNode.Function.NODE) {
                continue;
            }
            if (np.group != null) {
                if (groups.contains(np.group)) {
                    continue;
                }
                groups.add(np.group);
                List<Xml.MenuNodeInst> list = new ArrayList<Xml.MenuNodeInst>();
                for (PrimitiveNode gnp : np.group.getNodes()) {
                    Xml.MenuNodeInst xnp = new Xml.MenuNodeInst();
                    xnp.protoName = gnp.getName();
                    xnp.function = gnp.getFunction();
                    list.add(xnp);
                }
                things.add(list);
            } else {
                List<Xml.PrimitiveNode> list = new ArrayList<Xml.PrimitiveNode>();
                Xml.PrimitiveNode xpn = new Xml.PrimitiveNode();
                xpn.name = np.getName();
                list.add(xpn);
                things.add(list);
            }
        }
        things.add(Collections.singletonList(SPECIALMENUPURE));
        things.add(Collections.singletonList(SPECIALMENUMISC));
        things.add(Collections.singletonList(SPECIALMENUCELL));
        int columns = (things.size() + 13) / 14;
        int rows = (things.size() + columns - 1) / columns;
        while (things.size() < columns * rows) {
            things.add(null);
        }

        factoryMenuPalette = new Xml.MenuPalette();
        factoryMenuPalette.numColumns = columns;
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < columns; col++) {
                List<?> list = things.get(col * rows + row);
                factoryMenuPalette.menuBoxes.add(list);
            }
        }
    }

    /**
     * This is the most basic function to determine the widest wire and the parallel distance
     * that run along them. Done because old class (MOSRules) doesn't consider the parallel distance as input.
     */
    public double[] getSpacingDistances(Poly poly1, Poly poly2) {
        double size1 = poly1.getMinSize();
        double size2 = poly2.getMinSize();
        
        double length = 0;
        double wideS = (size1 > size2) ? size1 : size2;
        
        double[] results = new double[2];
        results[0] = wideS;
        results[1] = length;
        return results;
    }

    /**
     * Method to retrieve cached rules
     * @return cached design rules.
     */
    public XMLRules getCachedRules() {
        return cachedRules;
    }

    /**
     * Method to set cached rules
     */
    public void setCachedRules(XMLRules rules) {
        cachedRules = rules;
    }

    /**
     * Method to determine if the rule name matches an existing VT Poly rule
     * @param theRule
     * @return true if it matches
     */
    public boolean isValidVTPolyRule(DRCTemplate theRule) {
        return false;
    }

    public Setting makeBooleanSetting(String name, String location, String description, String xmlName, boolean factory) {
        return getProjectSettings().makeBooleanSetting(name, TECH_NODE, xmlName, location, description, factory);
    }

    public Setting makeIntSetting(String name, String location, String description, String xmlName, int factory, String... trueMeaning) {
        return getProjectSettings().makeIntSetting(name, TECH_NODE, xmlName, location, description, factory, trueMeaning);
    }

    public Setting makeDoubleSetting(String name, String location, String description, String xmlName, double factory) {
        return getProjectSettings().makeDoubleSetting(name, TECH_NODE, xmlName, location, description, factory);
    }

    public Setting makeStringSetting(String name, String location, String description, String xmlName, String factory) {
        return getProjectSettings().makeStringSetting(name, TECH_NODE, xmlName, location, description, factory);
    }

    // -------------------------- Project Preferences -------------------------
    public Setting.Group getProjectSettings() {
        return settings;
    }

    public Setting.RootGroup getProjectSettingsRoot() {
        return rootSettings;
    }

    public Setting getSetting(String xmlPath) {
        return getProjectSettings().getSetting(xmlPath);
    }

    public Setting getSetting(TechFactory.Param param) {
        String xmlPath = param.xmlPath;
        if (xmlPath.startsWith(settings.xmlPath)) {
            return getSetting(xmlPath.substring(settings.xmlPath.length()));
        }
        return null;
    }
}
