/*
 * DRCtech.c --
 *
 * Technology initialization for the DRC module.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic/drc/DRCtech.c,v 1.2 2005/06/01 21:41:40 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "drc/drc.h"
#include "utils/tech.h"
#include "textio/textio.h"
#include "utils/malloc.h"
#ifdef DRC_CIF_EXTENSIONS
#include "cif/cif.h"
#include "cif/CIFint.h"
#endif

/*
 * Largest DRC interaction radius in the given technology
 */
global int TechHalo, MaxHalo;

#ifdef DRC_CIF_EXTENSIONS
CIFStyle        *drcCifStyle=NULL;
#endif

/*
 * List of rules applicable for each possible pair of edges.
 */
global DRCCookie      * DRCRulesTbl [TT_MAXTYPES] [TT_MAXTYPES];

/* The paint table defined below is used when yanking paint for subcell
 * interaction checks.  It turns some kinds of overlaps into automatic
 * errors.
 */

global PaintResultType DRCPaintTable[NP][NT][NT];

/* The mask below defines tile types that are not permitted to overlap
 * themselves across cells unless the overlap is exact (each cell
 * contains exactly the same material).
 */

global TileTypeBitMask DRCExactOverlapTypes;

/* The following variable can be set to zero to turn off
 * any optimizations of design rule lists.
 */

global int DRCRuleOptimization = TRUE;

/* The following variables count how many rules were specified by
 * the technology file and how many edge rules were optimized away.
 */

static int drcRulesSpecified = 0;
static int drcRulesOptimized = 0;

/*
 * Forward declarations.
 */
int drcWidth(), drcSpacing(), drcEdge(), drcNoOverlap(), drcExactOverlap();
int drcSurround(), drcRectOnly(), drcOverhang();
int drcStepSize();
#ifdef DRC_EXTENSIONS
int drcMaxwidth(), drcArea(), drcRectangle();
#ifdef DRC_CIF_EXTENSIONS
int drcSetCifStyle(),drcCifWidth(),drcCifSpacing();
int drcCifMaxwidth(),drcCifArea();
#endif
#endif

/*
 * DRC "why" strings are potentially referred to hundreds of times by
 * DRC cookies in the rule table.  Rather than creating hundreds of
 * copies of each string, we create just one copy and let all the cookies
 * point to that one copy.
 *
 * Since we can't free these shared "why" strings when we delete a cookie,
 * we keep a list of these strings and free them all when convenient.
 */

struct drcwhylist {
    char		* dwl_string;
    struct drcwhylist	* dwl_next;
};

static struct drcwhylist * drcWhyList = NULL;

/*
 * ----------------------------------------------------------------------------
 * drcWhyClear --
 *
 * Free the current set of "why" strings.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All sorts of things get freed.
 * ----------------------------------------------------------------------------
 */

void 
drcWhyClear()
{
    while (drcWhyList != NULL)
    {
	char * old = (char *) drcWhyList;
	(void) StrDup(&(drcWhyList->dwl_string), (char *) NULL);
	drcWhyList = drcWhyList->dwl_next;
	freeMagic(old);
    }
}

/*
 * ----------------------------------------------------------------------------
 * drcWhyDup --
 *
 * Duplicate a shared "why" string using StrDup() and remember it so we can
 * free it sometime later, in drcWhyClear().
 *
 * Returns:
 *	A copy of the given string.
 *
 * Side effects:
 *	Adds to the drcWhyList.  Calls StrDup().
 * ----------------------------------------------------------------------------
 */

char *
drcWhyDup(why)
    char * why;
{
    struct drcwhylist * new;

    new = (struct drcwhylist *) mallocMagic((unsigned) (sizeof *new));
    new->dwl_string = StrDup((char **) NULL, why);
    new->dwl_next = drcWhyList;
    drcWhyList = new;

    return new->dwl_string;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcFindBucket --
 *
 *	Find the bucket preceding the point where we with to insert a new DRC
 *	cookie.  Don't insert a cookie in the middle of a pair of coupled
 *	(trigger + check) rules.
 *
 * Results:
 *	Returns a pointer to the location where we want to insert a rule
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

DRCCookie *
drcFindBucket(i, j, distance)
    int i, j, distance;
{
    DRCCookie *dp;

    /* find bucket preceding the new one we wish to insert */

    for (dp = DRCRulesTbl[i][j];
		dp->drcc_next != (DRCCookie *) NULL;
		dp = dp->drcc_next)
    {
	if (dp->drcc_next->drcc_flags & DRC_TRIGGER)
	{
	    if (dp->drcc_next->drcc_next->drcc_dist >= distance)
		break;
	    else
		dp = dp->drcc_next;
	}
	else if (dp->drcc_next->drcc_dist >= distance) break;
    }

    return dp;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcInitRulesTbl --
 *
 * First and only initialization to ensure that the DRCRulesTbl array is
 * NULL on startup.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory is zeroed.
 *
 * ----------------------------------------------------------------------------
 */

void
drcInitRulesTbl()
{
    int i, j;

    for (i = 0; i < TT_MAXTYPES; i++)
	for (j = 0; j < TT_MAXTYPES; j++)
	    DRCRulesTbl[i][j] = (DRCCookie *)NULL;

    drcWhyList = NULL;
}


/*
 * ----------------------------------------------------------------------------
 * DRCTechInit --
 *
 * Initialize the technology-specific variables for the DRC module.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Clears out all the DRC tables.
 * ----------------------------------------------------------------------------
 */

void
DRCTechInit()
{
    int i, j, plane;
    DRCCookie *dp;
    PaintResultType result;

    TechHalo = MaxHalo = 0;
    drcRulesOptimized = 0;
    drcRulesSpecified = 0;
    TTMaskZero(&DRCExactOverlapTypes);

    /* Release all the old "why" strings.
     */

    drcWhyClear();

    /* Remove all old rules from the DRC rules table and put a dummy
     * rule at the front of each list.
     */

    for (i = 0; i < TT_MAXTYPES; i++)
    {
	for (j = 0; j < TT_MAXTYPES; j++)
	{
	    dp = DRCRulesTbl[i][j];
	    while (dp != NULL)
	    {
		char * old = (char *) dp;
		dp = dp->drcc_next;
		freeMagic(old);
	    }
	    dp = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
	    dp->drcc_dist = -1;
	    dp->drcc_next = (DRCCookie *) NULL;
	    TTMaskZero(&dp->drcc_mask);
	    DRCRulesTbl[i][j] = dp;
	}
    }

    /* Copy the default paint table into the DRC paint table.  The DRC
     * paint table will be modified as we read the drc section.  Also
     * make sure that the error layer is super-persistent (once it
     * appears, it can't be gotten rid of by painting).  Also, make
     * some crossings automatically illegal:  two layers can't cross
     * unless the result of painting one on top of the other is to
     * get one of the layers, and it doesn't matter which is painted
     * on top of which.
     */
    
    for (plane = 0; plane < DBNumPlanes; plane++)
	for (i = 0; i < DBNumTypes; i++)
	    for (j = 0; j < DBNumTypes; j++)
	    {
		result = DBPaintResultTbl[plane][i][j];
		if ((i == TT_ERROR_S) || (j == TT_ERROR_S))
		    DRCPaintTable[plane][i][j] = TT_ERROR_S;
		else if ((i == TT_SPACE) || (j == TT_SPACE)
			|| !DBTypeOnPlane(j, plane)
			|| !DBPaintOnTypePlanes(i, j))
		    DRCPaintTable[plane][i][j] = result;

		/* Modified for stackable types (Tim, 10/3/03) */
		else if ((i >= DBNumUserLayers) ||
			((result >= DBNumUserLayers)
			&& (DBTechFindStacking(i, j) == result)))
		{
		    DRCPaintTable[plane][i][j] = result;
		}
			
		else if ((!TTMaskHasType(&DBLayerTypeMaskTbl[i], result)
			&& !TTMaskHasType(&DBLayerTypeMaskTbl[j], result))
			|| ((result != DBPaintResultTbl[plane][j][i])
			&& DBTypeOnPlane(i, plane)
			&& DBPaintOnTypePlanes(j, i)))
		{
		    DRCPaintTable[plane][i][j] = TT_ERROR_S;
		    /* TxError("Error: %s on %s, was %s\n",
				DBTypeLongNameTbl[i], DBTypeLongNameTbl[j],
				DBTypeLongNameTbl[result]); */
		}
		else
		    DRCPaintTable[plane][i][j] = result;
	    }

#ifdef DRC_CIF_EXTENSIONS
    drcCifInit();
#endif
}

/*
 * ----------------------------------------------------------------------------
 *
 * DRCTechAddRule --
 *
 * Add a new entry to the DRC table.
 *
 * Results:
 *	Always returns TRUE so that tech file read-in doesn't abort.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * Organization:
 *	We select a procedure based on the first keyword (argv[0])
 *	and call it to do the work of implementing the rule.  Each
 *	such procedure is of the following form:
 *
 *	int
 *	proc(argc, argv)
 *	    int argc;
 *	    char *argv[];
 *	{
 *	}
 *
 * 	It returns the distance associated with the design rule,
 *	or -1 in the event of a fatal error that should cause
 *	DRCTechAddRule() to return FALSE (currently, none of them
 *	do, so we always return TRUE).  If there is no distance
 *	associated with the design rule, 0 is returned.
 *
 * ----------------------------------------------------------------------------
 */

/* DRC rule assignment has been modified to limit the mask and corner	*/
/* types to the "user layers" only.  Because stacked contacts are	*/
/* amalgams of two or more other types, the DRCTile routine decomposes	*/
/* each stacked tile encountered into its constituent residue (contact)	*/
/* types and applies the rules separately to each.  Therefore,		*/
/* drcc_mask and drcc_corner should never contain stacked types.	*/

#define ASSIGN(cookie,dist,next,mask,corner,why,cdist,flags,plane) \
	((cookie)->drcc_dist = dist, \
	(cookie)->drcc_next = next, \
	(cookie)->drcc_mask = mask, \
	(cookie)->drcc_corner = corner, \
	(cookie)->drcc_why = why, \
	(cookie)->drcc_cdist = cdist, \
	(cookie)->drcc_flags = flags, \
	(cookie)->drcc_plane = plane, \
	TTMaskAndMask(&(cookie)->drcc_mask, &DBUserLayerBits), \
	TTMaskAndMask(&(cookie)->drcc_corner, &DBUserLayerBits))

	/* ARGSUSED */
bool
DRCTechAddRule(sectionName, argc, argv)
    char *sectionName;		/* Unused */
    int argc;
    char *argv[];
{
    int which, distance;
    char *fmt;
    static struct
    {
	char	*rk_keyword;	/* Initial keyword */
	int	 rk_minargs;	/* Min # arguments */
	int	 rk_maxargs;	/* Max # arguments */
	int    (*rk_proc)();	/* Procedure implementing this keyword */
	char	*rk_err;	/* Error message */
    } ruleKeys[] = {
	"edge",		 8,	9,	drcEdge,
    "layers1 layers2 distance okTypes cornerTypes cornerDistance why [plane]",
	"edge4way",	 8,	9,	drcEdge,
    "layers1 layers2 distance okTypes cornerTypes cornerDistance why [plane]",
	"exact_overlap", 2,	2,	drcExactOverlap,
    "layers",
	"no_overlap",	 3,	3,	drcNoOverlap,
    "layers1 layers2",
	"overhang",	 5,	5,	drcOverhang,
    "layers1 layers2 distance why",
	"rect_only",	 3,	3,	drcRectOnly,
    "layers why",
	"spacing",	 6,	7,	drcSpacing,
    "layers1 layers2 separation adjacency why",
	"stepsize",	 2,	2,	drcStepSize,
    "step_size",
	"surround",	 6,	6,	drcSurround,
    "layers1 layers2 distance presence why",
	"width",	 4,	4,	drcWidth,
    "layers width why",
	"widespacing",	 7,	7,	drcSpacing,
    "layers1 width layers2 separation adjacency why",
#ifdef DRC_EXTENSIONS
        "area",		 5,	5,	drcArea,
    "layers area horizon why",
        "maxwidth",	 4,	5,	drcMaxwidth,
    "layers maxwidth bends why",
        "cifstyle",	 2,	2,	drcSetCifStyle,
    "cif_style",
#ifdef DRC_CIF_EXTENSIONS
	"cifwidth",	 4,	4,	drcCifWidth,
    "layers width why",
	"cifspacing",	 6,	6,	drcCifSpacing,
    "layers1 layers2 separation adjacency why",
        "cifarea",	 5,	5,	drcCifArea,
    "layers area horizon why",
        "cifmaxwidth",	5,	5,	drcCifMaxwidth,
    "layers maxwidth bends why",
#endif
	"rectangle",	5,	5,	drcRectangle,
    "layers maxwidth [even|odd|any] why",
#endif
	0
    }, *rp;

    drcRulesSpecified += 1;

    which = LookupStruct(argv[0], (LookupTable *) ruleKeys, sizeof ruleKeys[0]);
    if (which < 0)
    {
	TechError("Bad DRC rule type \"%s\"\n", argv[0]);
	TxError("Valid rule types are:\n");
	for (fmt = "%s", rp = ruleKeys; rp->rk_keyword; rp++, fmt = ", %s")
	    TxError(fmt, rp->rk_keyword);
	TxError(".\n");
	return (TRUE);
    }
    rp = &ruleKeys[which];
    if (argc < rp->rk_minargs || argc > rp->rk_maxargs)
    {
	TechError("Rule type \"%s\" usage: %s %s\n",
		rp->rk_keyword, rp->rk_keyword, rp->rk_err);
	return (TRUE);
    }

    distance = (*rp->rk_proc)(argc, argv);
    if (distance < 0)
	return (FALSE);

    /* Update the halo to be the maximum distance of any design rule */
    if (distance > TechHalo)
	TechHalo = distance;

    return (TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcWidth --
 *
 * Process a width rule.
 * This is of the form:
 *
 *	width layers distance why
 *
 * e.g,
 *
 *	width poly,pmc 2 "poly width must be at least 2"
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcWidth(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    int distance = atoi(argv[2]);
    char *why = drcWhyDup(argv[3]);
    TileTypeBitMask set, setC, tmp1;
    DRCCookie *dp, *dpnew;
    TileType i, j;
    int plane;

    DBTechNoisyNameMask(layers, &set);
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
	if (DBTechSubsetLayers(set, DBPlaneTypes[plane], &tmp1))
	    break;
    
    if (plane == DBNumPlanes)
    {
	TechError("All layers for \"width\" must be on same plane\n");
	return (0);
    }

    set = tmp1;
    TTMaskCom2(&setC, &set);

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    /*
	     * Must have types in 'set' for at least 'distance'
	     * to the right of any edge between a type in '~set'
	     * and a type in 'set'.
	     */
	    if (DBTypesOnSamePlane(i, j)
		    && TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
	    {
		/* find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);
		dpnew = (DRCCookie *) mallocMagic ((unsigned)sizeof (DRCCookie));
		ASSIGN(dpnew, distance, dp->drcc_next, set, set, why,
			    distance, DRC_FORWARD, plane);

		dp->drcc_next = dpnew;
	    }
	}
    }

    return (distance);
}

#ifdef DRC_EXTENSIONS
/*
 * ----------------------------------------------------------------------------
 *
 * drcArea --
 *
 * Process an area rule.
 * This is of the form:
 *
 *	area layers area horizon why
 *
 * e.g,
 *
 *	area pmc 4 2 "poly contact area must be at least 4"
 *
 * "area" is the total area in lambda^2.
 *
 * "horizon" is the halo distance for the check.  Normally, this would
 * be the area (in lambda^2) divided by the minimum width rule (in
 * lambda).  Anything larger would not be a violation as long as the
 * minimum width rule is satisfied.
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcArea(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    int distance = atoi(argv[2]);
    int	horizon = atoi(argv[3]);
    char *why = drcWhyDup(argv[4]);
    TileTypeBitMask set, setC, tmp1;
    DRCCookie *dp, *dpnew;
    TileType i, j;
    int plane;

    DBTechNoisyNameMask(layers, &set);
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
	if (DBTechSubsetLayers(set, DBPlaneTypes[plane], &tmp1))
	    break;

    if (plane == DBNumPlanes)
    {
	TechError("All layers for \"area\" must be on same plane\n");
	return (0);
    }

    set = tmp1;
    TTMaskCom2(&setC, &set);

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    /*
	     * Must have types in 'set' for at least 'distance'
	     * to the right of any edge between a type in '~set'
	     * and a type in 'set'.
	     */
	    if (DBTypesOnSamePlane(i, j)
		    && TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
	    {
		/* find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, horizon);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, horizon, dp->drcc_next, set, set, why,
			    distance, DRC_AREA|DRC_FORWARD, plane);

		dp->drcc_next = dpnew;
	    }
	}
    }

    return (horizon);
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcMaxwidth --
 *
 * Process a maxwidth rule.
 * This is of the form:
 *
 *	maxwidth layers distance [bends] why
 *
 * This routine was updated 3/6/05 to match the "canonical" definition of
 * a maxwidth region, which is any rectangle containing <layers> that is
 * at least <distance> in both width and height.  If the keyword
 * "bends_illegal" is present, then the definition reverts to the original
 * (see below) for backwards-compatibility.  Otherwise ("bends_ok" or
 * nothing), the new routine is used.
 *
 *	maxwidth metal1 389 "metal1 width > 35um must be slotted"
 *	maxwidth pmc 4 bend_illegal "poly contact area must be at least 4"
 *	maxwidth trench 4 bend_ok "poly contact area must be at least 4"
 *
 *      bend_illegal - means that one_dimension must be distance for any 
 *  		point in the region.  This is used for emitters and contacts
 *		that are rectangular (so we can't generate them with the
 *		squares command) and some exact width in one direction.
 *	bend_ok - Used mainly for wide metal rules where metal greater than
 *		some given width must be slotted.  Also, used for things
 *		like trench, where the width is some fixed value:
 *
 *			XXXXX		XXXXXX
 *			X   X		XXXXXX
 *			X   X		X    X
 *			XXXXX		XXXXXX
 *			
 *			 OK		 BAD		
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcMaxwidth(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    int distance = atoi(argv[2]);
    char *bends = argv[3];
    char *why;
    TileTypeBitMask set, setC, tmp1;
    DRCCookie *dp, *dpnew;
    TileType i, j;
    int plane;
    int bend;

    DBTechNoisyNameMask(layers, &set);
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
	if (DBTechSubsetLayers(set, DBPlaneTypes[plane], &tmp1))
	    break;

    if (plane == DBNumPlanes)
    {
	TechError("All layers for \"area\" must be on same plane\n");
	return (0);
    }

    if (argc == 3)
    {
	bend = DRC_BENDS;
	why = drcWhyDup(argv[3]);
    }
    else
    {
	if (strcmp(bends,"bend_illegal") == 0) bend = 0;
	else if (strcmp(bends,"bend_ok") == 0) bend = DRC_BENDS;
	else
	{
	    TechError("unknown bend option %s\n",bends);
	    return (0);
	}
	why = drcWhyDup(argv[4]);
    }

    
    set = tmp1;
    TTMaskCom2(&setC, &set);

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    /*
	     * Must have types in 'set' for at least 'distance'
	     * to the right of any edge between a type in '~set'
	     * and a type in 'set'.
	     */
	    if (DBTypesOnSamePlane(i, j)
		    && TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
	    {
		/* find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next, set, set, why,
			    distance, DRC_MAXWIDTH | bend, plane);

		dp->drcc_next = dpnew;
	    }
	}
    }

    return (distance);
}
#endif


/*
 * ----------------------------------------------------------------------------
 *
 * drcSpacing --
 *
 * Process a spacing rule.
 * This is of the form:
 *
 *	spacing layers1 layers2 distance adjacency why
 *
 * or (added 3/28/05)
 *
 *	spacing layers1 layers2 distance catecorner dist2 why
 *
 * e.g,
 *
 *	spacing metal,pmc/m,dmc/m metal,pmc/m,dmc/m 4 touching_ok \
 *		"metal spacing must be at least 4"
 *
 * Adjacency may be either "touching_ok" or "touching_illegal"
 * In the first case, no violation occurs when types in layers1 are
 * immediately adjacent to types in layers2.  In the second case,
 * such adjacency causes a violation.
 *
 * The "catecorner" option is designed for two purposes: 1) to catch
 * rule violations that are caused by a cifoutput grow rules followed
 * by a shrink rule.  If a tile is in the catecorner position between
 * distances (G-W) and G, where G is the amount grown, and W is the
 * minimum width of the CIF layer, then a strip of less than W would
 * result, and is therefore a DRC violation.  2) with dist2=0, can be
 * used to indicate that a layer can have no outside corners; that is,
 * corners must be beveled with a non-manhattan (usually 45 degree)
 * angle.
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * Notes:
 * Extended to include the rule syntax:
 *
 *	widespacing layers1 width layers2 distance adjacency why
 *
 * This extension covers rules such as "If m1 width > 10um, then spacing to
 * unconnected m1 must be at least 0.6um".  This assumes that the instantiated
 * edge4way rule is a standard "spacing" rule in which "dist" and "cdist"
 * (corner extension) distances are always the same, so we can use the "cdist"
 * record to encode the width of "layers1" which triggers the rule.  We re-use
 * the CheckMaxwidth() code, but with the following differences: 1) it does not
 * trigger any error painting functions, 2) it returns a value stating whether
 * the maxwidth rule applies or not, 3) it uses the DBLayerTypeMaskTbl[] for
 * the tile type in question, not "oktypes", for its search, and 4) it uses
 * the max width in the "cdist" record, not the "dist" record, for the max
 * width rule.  The "dist" record is copied to "cdist" before actually applying
 * the spacing rule, so that the rule acts like a proper spacing rule.
 *
 * ----------------------------------------------------------------------------
 */

int
drcSpacing(argc, argv)
    int argc;
    char *argv[];
{
    char *layers1 = argv[1], *layers2;
    int distance;
    char *adjacency;
    char *why;
    TileTypeBitMask set1, set2, tmp1, tmp2, setR, setRreverse;
    int plane, curPlane;
    PlaneMask planes1, planes2;
    DRCCookie *dp, *dpnew;
    int needReverse = FALSE;
    TileType i, j;
    bool widerule = (strncmp(argv[0], "wide", 4) == 0);
    int wwidth;
    bool catecorner = FALSE;

    if (widerule)
    {
	wwidth = atoi(argv[2]);
	layers2 = argv[3];
	distance = atoi(argv[4]);
	adjacency = argv[5];
	why = drcWhyDup(argv[6]);
	needReverse = TRUE;	/* Requires checks in both directions! */
	/* TxPrintf("Info:  DRCtech:  widespacing rule for %s width %d:"
		" spacing must be %d\n", layers1, wwidth, distance); */
    }
    else
    {
	layers2 = argv[2];
	distance = atoi(argv[3]);
	adjacency = argv[4];
	wwidth = distance;
	if (!strcmp(adjacency, "catecorner"))
	{
	    if (argc == 7)
	    {
		wwidth = atoi(argv[5]);
		why = drcWhyDup(argv[6]);
		catecorner = TRUE;
	    }
	    else
	    {
		TechError("Not enough arguments in DRC catecorner spacing line.\n");
		return(0);
	    }
	}
	else
	{
	    why = drcWhyDup(argv[5]);
	    if (argc == 7)
	    {
		TechError("Unknown argument in spacing line.\n");
		return(0);
	    }
	}
    }

    DBTechNoisyNameMask(layers1, &set1);
    DBTechNoisyNameMask(layers2, &set2);

    /* Add stacking types */
    for (i = DBNumUserLayers; i < DBNumTypes; i++)
    {
	TileTypeBitMask *rmask = DBResidueMask(i);
	TTMaskAndMask3(&tmp1, &set1, rmask);
	if (!TTMaskIsZero(&tmp1))
	    TTMaskSetType(&set1, i);
	TTMaskAndMask3(&tmp2, &set2, rmask);
	if (!TTMaskIsZero(&tmp2))
	    TTMaskSetType(&set2, i);
    }
    planes1 = DBTechMinSetPlanes(set1, &set1);
    planes2 = DBTechMinSetPlanes(set2, &set2);

    /* Look for plane that contains image of all types in both
     * set1 and set2.
     * NOTE:  plane set to this plane, or DBNumPlanes if none found.
     */
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
    {
	if (DBTechSubsetLayers(set1, DBPlaneTypes[plane], &tmp1)
		&& DBTechSubsetLayers(set2, DBPlaneTypes[plane], &tmp2))
	    break;
    }

    if (!strcmp(adjacency, "touching_ok") || catecorner)
    {
	/* If touching is OK, everything must fall in the same plane. */
	if (plane == DBNumPlanes)
	{
	    if (catecorner)
	        TechError("Catecorner spacing check must have"
			" all types in one plane.\n");
	    else
		TechError("Spacing check with touching ok must have"
			" all types in one plane.\n");
	    return (0);
	}

	/* In "touching_ok rules, spacing to set2  is be checked in FORWARD 
	 * direction at edges between set1 and  (setR = ~set1 AND ~set2).
	 *
	 * In addition, spacing to set1 is checked in FORWARD direction 
	 * at edges between set2 and (setRreverse = ~set1 AND ~set2).
	 *
	 * If set1 and set2 are different, above are checked in REVERSE as
	 * well as forward direction.  This is important since touching
	 * material frequently masks violations in one direction.
	 *
	 * setR and setRreverse are set appropriately below.
	 */

	tmp1 = set1;
	tmp2 = set2; 

	planes1 = planes2 = PlaneNumToMaskBit(plane);
	TTMaskCom(&tmp1);
	TTMaskCom(&tmp2);
	TTMaskAndMask(&tmp1, &tmp2);
	setR = tmp1;
	setRreverse = tmp1;

	/* If set1 != set2, set flag to check rules in both directions */
	if (!TTMaskEqual(&set1, &set2))
	    needReverse = TRUE;
    }
    else if (!strcmp(adjacency, "touching_illegal"))
    {
	/* If touching is illegal, set1 and set2 should not intersect 
	 * (adjacencies between types on the same plane would be missed)
	 */
	if (TTMaskIntersect(&set1, &set2))
	{
	    TechError("Spacing check with touching illegal must be between "
			"non-intersecting type lists.\n");
	    return (0);
	}

	/* In "touching_illegal rules, spacing to set2 will be checked
	 * in FORWARD 
	 * direction at edges between set1 and (setR=~set1). 
	 *
	 * In addition, spacing to set1 will be checked in FORWARD direction
	 * at edges between set2 and (setRreverse=  ~set2).
	 *
	 * setR and setRreverse are set appropriately below.
	 */
	TTMaskCom2(&setR, &set1);
	TTMaskCom2(&setRreverse, &set2);
    }
    else
    {
	TechError("Badly formed drc spacing line\n");
	return (0);
    }

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    if ((i == j) || !DBTypesOnSamePlane(i, j)) continue;

	    /* LHS is an element of set1, RHS is an element of setR */
	    if (TTMaskHasType(&set1, i) && TTMaskHasType(&setR, j))
	    {
		/*
		 * Must not have 'set2' for 'distance' to the right of
		 * an edge between 'set1' and the types not in 'set1'
		 * (touching_illegal case) or in neither
		 * 'set1' nor 'set2' (touching_ok case).
		 */

		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);

		/* May have to insert several buckets on different planes */
		for (curPlane = PL_TECHDEPBASE; curPlane < DBNumPlanes; curPlane++)
		{
		    if (!PlaneMaskHasPlane(planes2, curPlane)) continue;
		    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		    TTMaskClearMask3(&tmp1, &DBPlaneTypes[curPlane], &set2);
		    TTMaskAndMask3(&tmp2, &DBPlaneTypes[curPlane], &setR);

		    if (widerule)
		    {
			DRCCookie *dptrig;

			/* Create two contiguous rules, one for spacing */
			/* and one for width.  These are created in	*/
			/* reverse order due to the stack property of	*/
			/* the linked list.				*/

		        ASSIGN(dpnew, distance, dp->drcc_next, tmp1, tmp2,
				why, distance, DRC_FORWARD, curPlane);
			dptrig = (DRCCookie *) mallocMagic((unsigned)
				(sizeof (DRCCookie)));
		        ASSIGN(dptrig, wwidth, dpnew, set1, set1, why,
				wwidth, DRC_REVERSE | DRC_MAXWIDTH | DRC_TRIGGER,
				curPlane);

			dp->drcc_next = dptrig;
		    }
		    else
		    {
			ASSIGN(dpnew, distance, dp->drcc_next,
				tmp1, tmp2, why, wwidth, DRC_FORWARD,
				curPlane);
			dp->drcc_next = dpnew;
		    }

		    if (i == TT_SPACE)
		    {
			if (!DBTypeOnPlane(j, curPlane))
			    dpnew->drcc_flags |= DRC_XPLANE;
		    }
		    else
		    {
			if (!DBTypeOnPlane(i, curPlane))
			    dpnew->drcc_flags |= DRC_XPLANE;
		    }
		    
		    if (needReverse)
		        dpnew->drcc_flags |= DRC_BOTHCORNERS;

		    if (catecorner)
			dpnew->drcc_flags |= DRC_CATECORNER;

		    if (needReverse)
		    {
			/* Add check in reverse direction, 
			 * NOTE:  am assuming single plane rule here (since reverse
			 * rules only used with touching_ok which must be 
			 * single plane)
			 * so am not setting DRC_XPLANE as above.
			 */
			 
			/* find bucket preceding new one we wish to insert */
			dp = drcFindBucket(j, i, distance);
			dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
			if (widerule)
			{
			    DRCCookie *dptrig;

			    /* Assign two coupled rules (see above) */

			    ASSIGN(dpnew, distance, dp->drcc_next,
				tmp1, tmp2, why, distance,
				DRC_REVERSE | DRC_BOTHCORNERS, curPlane);
			    dptrig = (DRCCookie *) mallocMagic((unsigned)
				(sizeof (DRCCookie)));
			    ASSIGN(dptrig, wwidth, dpnew, set1, set1, why,
				wwidth, DRC_FORWARD | DRC_MAXWIDTH | DRC_TRIGGER,
				curPlane);
			    dp->drcc_next = dptrig;
			}
			else
			{
			    ASSIGN(dpnew,distance,dp->drcc_next,
				tmp1, tmp2, why, wwidth, 
				DRC_REVERSE | DRC_BOTHCORNERS, curPlane);
			    dp->drcc_next = dpnew;
			}
		     }
		}
	    }

	    /*
	     * Now, if set1 and set2 are distinct apply the rule for LHS in set1
	     * and RHS in set2.
	     */
	    if (TTMaskEqual(&set1, &set2)) continue;

	    if (widerule) continue;	/* Can't determine width of set1    */
					/* when looking at the edge of set2 */

	    /* LHS is an element of set2, RHS is an element of setRreverse */
	    if (TTMaskHasType(&set2, i) && TTMaskHasType(&setRreverse, j))
	    {
		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);

		for (curPlane = PL_TECHDEPBASE; curPlane < DBNumPlanes; curPlane++)
		{
		    if (!PlaneMaskHasPlane(planes1, curPlane)) continue;

		    dpnew = (DRCCookie *) mallocMagic ((unsigned) (sizeof (DRCCookie)));
		    TTMaskClearMask3(&tmp1, &DBPlaneTypes[curPlane], &set1);
		    TTMaskAndMask3(&tmp2, &DBPlaneTypes[curPlane], &setRreverse);

		    ASSIGN(dpnew, distance, dp->drcc_next,
			tmp1, tmp2, why, distance, DRC_FORWARD, curPlane);

		    if (i == TT_SPACE)
		    {
			if (!DBTypeOnPlane(j, curPlane))
			    dpnew->drcc_flags |= DRC_XPLANE;
		    }
		    else
		    {
			if (!DBTypeOnPlane(i, curPlane))
			    dpnew->drcc_flags |= DRC_XPLANE;
		    }

		    if (needReverse)
		        dpnew->drcc_flags |= DRC_BOTHCORNERS;

		    if (catecorner)
			dpnew->drcc_flags |= DRC_CATECORNER;

		    dp->drcc_next = dpnew;		    

		    if (needReverse)
		    {
			/* Add check in reverse direction, 
			 * NOTE:  am assuming single plane rule here (since reverse
			 * rules only used with touching_ok which must be 
			 * single plane)
			 * so am not setting DRC_XPLANE as above.
			 */
			 
			/* find bucket preceding new one we wish to insert */
			dp = drcFindBucket(j, i, distance);
			dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
			ASSIGN(dpnew, distance, dp->drcc_next,
				tmp1, tmp2, why, distance, 
				DRC_REVERSE | DRC_BOTHCORNERS, curPlane);
			 
			dp->drcc_next = dpnew;
		    }
		}
	    }

	    /* Finally, if multiplane rule then check that set2 types
	     * are not present just to right of edges with setR on LHS
	     * and set1 on RHS.  This check is necessary to make sure
	     * that a set1 rectangle doesn't coincide exactly with a
	     * set2 rectangle.  
	     * (This check added by Michael Arnold on 4/10/86.)
	     */

	    /* If not cross plane rule, check does not apply */
	    if (plane != DBNumPlanes) continue; 

	    /* LHS is an element of setR, RHS is an element of set1 */
	    if (TTMaskHasType(&setR, i) && TTMaskHasType(&set1, j))
	    {
		/*
		 * Must not have 'set2' for 'distance' to the right of
		 * an edge between the types not in set1 and set1.
		 * (is only checked for cross plane rules - these are
		 * all of type touching_illegal)
		 */

		/* Walk list to last check.  New checks ("cookies") go
		 * at end of list since we are checking for distance of
		 * 1 and the list is sorted in order of decreasing distance.
		 */
		for (dp = DRCRulesTbl [i][j];
		     dp->drcc_next != (DRCCookie *) NULL;
		     dp = dp->drcc_next)
		    ; /* null body */

		/* Insert one check for each plane involved in set2 */
		for (curPlane = PL_TECHDEPBASE; 
		    curPlane < DBNumPlanes; 
		    curPlane++)
		{
		    if (!PlaneMaskHasPlane(planes2, curPlane)) continue;

		    /* filter out checks that are not cross plane */
		    if (i == TT_SPACE) 
		    {
			if (DBTypeOnPlane(j, curPlane))
			    continue;
		    }
		    else
		    {
			if (DBTypeOnPlane(i, curPlane))
			    continue;
		    }

		    /* create new check and add it to list */
		    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		    TTMaskClearMask3(&tmp1, &DBPlaneTypes[curPlane], &set2);
		    TTMaskZero(&tmp2);

		    ASSIGN(dpnew, 1, dp->drcc_next,
			tmp1, tmp2, why, distance, 
			DRC_FORWARD | DRC_XPLANE, curPlane);
		    dp->drcc_next = dpnew;
		}
	    }
	}
    }

    return (MAX(wwidth, distance));
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcEdge --
 *
 * Process a primitive edge rule.
 * This is of the form:
 *
 *	edge layers1 layers2 dist OKtypes cornerTypes cornerDist why [plane]
 * or	edge4way layers1 layers2 dist OKtypes cornerTypes cornerDist why [plane]
 *
 * e.g,
 *
 *	edge poly,pmc s 1 diff poly,pmc "poly-diff separation must be 2"
 *
 * An "edge" rule is applied only down and to the left.
 * An "edge4way" rule is applied in all four directions.
 *
 * Results:
 *	Returns greater of dist and cdist.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcEdge(argc, argv)
    int argc;
    char *argv[];
{
    char *layers1 = argv[1], *layers2 = argv[2];
    int distance = atoi(argv[3]);
    char *okTypes = argv[4], *cornerTypes = argv[5];
    int cdist = atoi(argv[6]);
    char *why = drcWhyDup(argv[7]);
    bool fourway = (strcmp(argv[0], "edge4way") == 0);
    TileTypeBitMask set1, set2, tmp1, tmp2, tmp3, setC, setM;
    DRCCookie *dp, *dpnew;
    int plane, checkPlane, tmpPlane;
    PlaneMask pMask;
    TileType i, j;

    /*
     * Edge4way rules produce [j][i] entries as well as [i][j]
     * ones, and check both corners rather than just one corner.
     */
    DBTechNoisyNameMask(layers1, &set1);
    DBTechNoisyNameMask(layers2, &set2);

    /*
     * Make sure that all edges between the two sets can be
     * found on one plane.
     */
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
	if (DBTechSubsetLayers(set1, DBPlaneTypes[plane], &tmp1)
		&& DBTechSubsetLayers(set2, DBPlaneTypes[plane], &tmp2))
	    break;

    if (plane == DBNumPlanes)
    {
	TechError("All edges in edge rule must lie in one plane.\n");
	return (0);
    }

    set1 = tmp1;
    set2 = tmp2;

    /* Give warning if types1 and types2 intersect */
    if(TTMaskIntersect(&set1, &set2))
	TechError("Warning:  types1 and types2 have nonempty intersection.  "
		"DRC does not check edges with the same type on both sides.\n");

    DBTechNoisyNameMask(cornerTypes, &tmp3);
    if (!DBTechSubsetLayers(tmp3, DBPlaneTypes[plane], &setC))
    {
	TechError("Corner types aren't in same plane as edges.\n");
	return (0);
    }

    if (argc == 9)
	tmpPlane = DBTechNoisyNamePlane(argv[8]);

    /*
     * OKtypes determine the checkPlane.  If checkPlane exists, it should
     * only be used to check against the plane of OKtypes.
     */

    pMask = DBTechNoisyNameMask(okTypes, &setM);
    for (checkPlane = PL_TECHDEPBASE; checkPlane < DBNumPlanes; checkPlane++)
	if (PlaneMaskHasPlane(pMask, checkPlane))
	    break;
 
    if (pMask != PlaneNumToMaskBit(checkPlane))
    {
	/* Technically it should be illegal to specify simply "space"
	 * in the types list for a DRC rule, as it is ambiguous.
	 * However, we will assume that the plane of the edge is
	 * intended.  The "plane" argument may be used to do the
	 * qualification (for backwards compatibility); any other use
	 * gets a warning.
	 */

	if (TTMaskEqual(&DBSpaceBits, &setM))
	{
	    if (argc == 9)
		checkPlane = tmpPlane;
	    else
	    {
		TechError("OK types \"%s\" in more than one plane.\n"
	        	"	Assuming same plane (%s) as edge.\n",
			okTypes, DBPlaneLongNameTbl[plane]);
		checkPlane = plane;
	    }
	}

	/* The case okTypes="0" is explicitly allowed according */
	/* to the manual, so we parse it accordingly.		*/

	else if (!strcmp(okTypes, "0"))
	    checkPlane = plane;
	else
	{
	    TechError("All OK types must lie in one plane.\n");
	    return (0);
	}
    }

    /* "plane" argument deprecated; kept for backward compatibility only */

    if (argc == 9 && ((tmpPlane < 0) || (tmpPlane != checkPlane)))
	    TechError("Ignoring bad plane argument.\n");

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
	    {
		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next,
		    setM, setC, why, cdist, DRC_FORWARD, checkPlane);
		if (fourway) dpnew->drcc_flags |= DRC_BOTHCORNERS;
		if (checkPlane != plane)
		    dpnew->drcc_flags |= DRC_XPLANE;
		dp->drcc_next = dpnew;

		if (fourway)
		{
		    /* find bucket preceding new one we wish to insert */
		    dp = drcFindBucket(j, i, distance);
		    dpnew = (DRCCookie *) mallocMagic ((unsigned) (sizeof (DRCCookie)));
		    ASSIGN(dpnew,distance,dp->drcc_next,
			    setM, setC, why, cdist, DRC_REVERSE, checkPlane);
		    dpnew->drcc_flags |= DRC_BOTHCORNERS;
		    if (checkPlane != plane)
			dpnew->drcc_flags |= DRC_XPLANE;
		    dp->drcc_next = dpnew;
		}
	    }
	}
    }

    return (MAX(distance, cdist));
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcOverhang --
 *
 * Process an overhang rule.
 * This is of the form:
 *
 *	overhang layers1 layers2 dist why
 *
 * indicating that layers2 must overhang layers1 by a distance of at least
 * dist.
 *
 * This rule is equivalent to:
 *
 *	edge4way layers1 space/p2|layers2 dist layers1|layers2 0 0 why
 *
 * ----------------------------------------------------------------------------
 */

int
drcOverhang(argc, argv)
    int argc;
    char *argv[];
{
    char *layers2 = argv[1], *layers1 = argv[2];
    int distance = atoi(argv[3]);
    char *why = drcWhyDup(argv[4]);

    TileTypeBitMask set1, set2, tmp1, tmp2, setM, setC;

    DRCCookie *dp, *dpnew;
    int plane;
    TileType i, j;

    DBTechNoisyNameMask(layers1, &set1);
    DBTechNoisyNameMask(layers2, &set2);

    /* Give warning if types1 and types2 intersect */
    if(TTMaskIntersect(&set1, &set2))
	TechError("Warning:  inside and outside types have nonempty intersection.  "
		"DRC does not check edges with the same type on both sides.\n");

    /* Check that contents of each set are in the same plane */
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
	if (DBTechSubsetLayers(set1, DBPlaneTypes[plane], &tmp1)
		&& DBTechSubsetLayers(set2, DBPlaneTypes[plane], &tmp2))
	    break;

    if (plane == DBNumPlanes)
    {
	TechError("All types in \"overhang\" must be on the same plane\n");
	return (0);
    }

    set1 = tmp1;
    set2 = tmp2;

    /* SetM is the union of set1 and set2 */
    TTMaskSetMask3(&setM, &set1, &set2);

    /* Add space to set2 */
    TTMaskSetType(&set2, TT_SPACE);

    /* SetC is the empty set */
    TTMaskZero(&setC);

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
	    {
		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);
		dpnew = (DRCCookie *) mallocMagic ((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next,
			setM, setC, why, 0, DRC_FORWARD, plane);
		dpnew->drcc_flags |= DRC_BOTHCORNERS;
		dp->drcc_next = dpnew;

		/* find bucket preceding new one we wish to insert */
		dp = drcFindBucket(j, i, distance);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next,
			setM, setC, why, 0, DRC_REVERSE, plane);
		dpnew->drcc_flags |= DRC_BOTHCORNERS;
		dp->drcc_next = dpnew;
	    }
	}
    }
    return distance;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcRectOnly --
 *
 * Process a rectangle-only rule.  This rule prohibits non-rectangular
 * geometry, and is used especially for contacts, as the "squares" operator
 * in the CIF/GDS output generator can't handle non-rectangular areas.
 * The rule is of the form:
 *
 *	rect_only layers why
 *
 * and is equivalent to:
 *
 *	edge4way layers ~(layers)/plane 1 ~(layers)/plane (all_layers)/plane 1
 *
 * The rect_only rule avoids the above contrived construction, especially the
 * requirement of specifying "all_layers" as something like (~(x),x)/p, a sure-
 * fire obfuscation.
 *
 * ----------------------------------------------------------------------------
 */

int
drcRectOnly(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    char *why = drcWhyDup(argv[2]);
    TileTypeBitMask set1, set2, setC, tmp1;
    DRCCookie *dp, *dpnew;
    int plane;
    TileType i, j;

    DBTechNoisyNameMask(layers, &set1);

    /*
     * Make sure that all edges between the two sets can be
     * found on one plane.
     */
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
	if (DBTechSubsetLayers(set1, DBPlaneTypes[plane], &tmp1))
	    break;

    if (plane == DBNumPlanes)
    {
	TechError("All types must lie in one plane.\n");
	return (0);
    }

    set1 = tmp1;

    /* set2 is the inverse of set1 in its plane */
    TTMaskCom2(&set2, &set1);
    TTMaskAndMask(&set2, &DBPlaneTypes[plane]);

    /* setM = set2.  setC = all types in plane */
    TTMaskZero(&setC);
    TTMaskSetMask(&setC, &DBPlaneTypes[plane]);

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
	    {
		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, 1);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, 1, dp->drcc_next,
		    set2, setC, why, 1, DRC_FORWARD, plane);
		dpnew->drcc_flags |= DRC_BOTHCORNERS;
		dp->drcc_next = dpnew;

		/* find bucket preceding new one we wish to insert */
		dp = drcFindBucket(j, i, 1);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew,1,dp->drcc_next,
			    set2, setC, why, 1, DRC_REVERSE, plane);
		dpnew->drcc_flags |= DRC_BOTHCORNERS;
		dp->drcc_next = dpnew;
	    }
	}
    }
    return 1;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcSurround --
 *
 * Process a surround rule.
 * This is of the form:
 *
 *	surround layers1 layers2 dist presence why
 *
 * indicating that layers2 must surround layers1 by at least distance
 * dist in all directions.
 *
 * This rule is equivalent to:
 *
 *	edge4way ~(layers2)/plane2 layers2 dist ~(layers1)/plane1 \
 *		layers2 dist why
 *
 * When presence=absence_illegal, the following additional rule is needed:
 *
 *	edge4way layers1 ~(layers2)/plane1 dist NULL ~(layers2)/plane1 \
 *		dist why
 *
 * 
 *
 * ----------------------------------------------------------------------------
 */

int
drcSurround(argc, argv)
    int argc;
    char *argv[];
{
    char *layers1 = argv[1], *layers2 = argv[2];
    int distance = atoi(argv[3]);
    char *presence = argv[4];
    char *why = drcWhyDup(argv[5]);

    TileTypeBitMask set1, set2, tmp1, tmp2, setM, invM;

    DRCCookie *dp, *dpnew;
    int plane1, plane2;
    TileType i, j;

    DBTechNoisyNameMask(layers1, &setM);
    DBTechNoisyNameMask(layers2, &set2);

    /* Give warning if types1 and types2 intersect */
    if(TTMaskIntersect(&set2, &setM))
	TechError("Warning:  inside and outside types have nonempty intersection.  "
		"DRC does not check edges with the same type on both sides.\n");

    /* Check that contents of each set are in the same plane */
    for (plane1 = PL_TECHDEPBASE; plane1 < DBNumPlanes; plane1++)
	if (DBTechSubsetLayers(setM, DBPlaneTypes[plane1], &tmp2))
	    break;

    if (plane1 == DBNumPlanes)
    {
	TechError("Inside types in \"surround\" must be on the same plane\n");
	return (0);
    }

    for (plane2 = PL_TECHDEPBASE; plane2 < DBNumPlanes; plane2++)
	if (DBTechSubsetLayers(set2, DBPlaneTypes[plane2], &tmp1))
	    break;

    if (plane2 == DBNumPlanes)
    {
	TechError("Outside types in \"surround\" must be on the same plane\n");
	return (0);
    }

    set2 = tmp1;
    setM = tmp2;

    /* set1 is the inverse of set2 (restricted to plane2) */
    TTMaskCom2(&set1, &set2);
    TTMaskAndMask(&set1, &DBPlaneTypes[plane2]);

    /* invert setM (restricted to plane1) */
    TTMaskCom2(&invM, &setM);
    TTMaskAndMask(&invM, &DBPlaneTypes[plane1]);

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
	    {
		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next,
			invM, set2, why, distance, DRC_FORWARD, plane1);
		if (plane2 != plane1)
		    dpnew->drcc_flags |= DRC_XPLANE;
		dpnew->drcc_flags |= DRC_BOTHCORNERS;
		dp->drcc_next = dpnew;

		/* find bucket preceding new one we wish to insert */
		dp = drcFindBucket(j, i, distance);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next,
			invM, set2, why, distance, DRC_REVERSE, plane1);
		if (plane2 != plane1)
		    dpnew->drcc_flags |= DRC_XPLANE;
		dpnew->drcc_flags |= DRC_BOTHCORNERS;
		dp->drcc_next = dpnew;
	    }
	}
    }

    if (strcmp(presence, "absence_illegal")) return distance;

    /* Add an extra rule when presence of the surrounding	*/
    /* layer is required.  Rule is different if planes match.	*/

    if (plane1 == plane2)
    {
	TTMaskZero(&invM);
	TTMaskSetMask(&invM, &setM);
	TTMaskSetMask(&invM, &set2);
	TTMaskCom(&invM);
	TTMaskAndMask(&invM, &DBPlaneTypes[plane1]);

	TTMaskZero(&set1);

	for (i = 0; i < DBNumUserLayers; i++)
	    for (j = 0; j < DBNumUserLayers; j++)
		if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j))
		{
		    /* Find bucket preceding the new one we wish to insert */
		    dp = drcFindBucket(i, j, distance);
		    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		    ASSIGN(dpnew, distance, dp->drcc_next, set1,
				invM, why, distance, DRC_FORWARD, plane2);
		    dpnew->drcc_flags |= DRC_BOTHCORNERS;
		    dp->drcc_next = dpnew;

		    /* find bucket preceding new one we wish to insert */
		    dp = drcFindBucket(j, i, distance);
		    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		    ASSIGN(dpnew,distance,dp->drcc_next, set1,
				invM, why, distance, DRC_REVERSE, plane2);
		    dpnew->drcc_flags |= DRC_BOTHCORNERS;
		    dp->drcc_next = dpnew;
		}
    }
    else
    {
	for (i = 0; i < DBNumUserLayers; i++)
	    for (j = 0; j < DBNumUserLayers; j++)
		if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j))
		{
		    /* Find bucket preceding the new one we wish to insert */
		    dp = drcFindBucket(i, j, distance);
		    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		    ASSIGN(dpnew, distance, dp->drcc_next,
				set2, invM, why, distance, DRC_FORWARD, plane2);
		    dpnew->drcc_flags |= DRC_BOTHCORNERS | DRC_XPLANE;
		    dp->drcc_next = dpnew;

		    /* find bucket preceding new one we wish to insert */
		    dp = drcFindBucket(j, i, distance);
		    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		    ASSIGN(dpnew,distance,dp->drcc_next,
				set2, invM, why, distance, DRC_REVERSE, plane2);
		    dpnew->drcc_flags |= DRC_BOTHCORNERS | DRC_XPLANE;
		    dp->drcc_next = dpnew;
		}
    }
    return distance;
}


/*
 * ----------------------------------------------------------------------------
 *
 * drcNoOverlap --
 *
 * Process a no-overlap rule.
 * This is of the form:
 *
 *	no_overlap layers1 layers2
 *
 * e.g,
 *
 *	no_overlap poly m2contact
 *
 * Results:
 *	Returns 0.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcNoOverlap(argc, argv)
    int argc;
    char *argv[];
{
    char *layers1 = argv[1], *layers2 = argv[2];
    TileTypeBitMask set1, set2;
    TileType i, j;
    int plane;

    /*
     * Grab up two sets of tile types, and make sure that if
     * any type from one set is painted over any type from the
     * other, then an error results.
     */

    DBTechNoisyNameMask(layers1, &set1);
    DBTechNoisyNameMask(layers2, &set2);

    for (i = 0; i < DBNumUserLayers; i++)
	for (j = 0; j < DBNumUserLayers; j++)
	    if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
		for (plane = 0; plane < DBNumPlanes; plane++)
		{
		    DRCPaintTable[plane][j][i] = TT_ERROR_S;
		    DRCPaintTable[plane][i][j] = TT_ERROR_S;
		}

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcExactOverlap --
 *
 * Process an exact overlap
 * This is of the form:
 *
 *	exact_overlap layers
 *
 * e.g,
 *
 *	exact_overlap pmc,dmc
 *
 * Results:
 *	Returns 0.
 *
 * Side effects:
 *	Updates DRCExactOverlapTypes.
 *
 * ----------------------------------------------------------------------------
 */

int
drcExactOverlap(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    TileTypeBitMask set, rmask;
    int i;

    /*
     * Grab up a bunch of tile types, and remember these: tiles
     * of these types cannot overlap themselves in different cells
     * unless they overlap exactly.
     */

    DBTechNoisyNameMask(layers, &set);
    TTMaskSetMask(&DRCExactOverlapTypes, &set);

    /*
     * Stacking types containing types in "set" must also be added
     * to the list of exact overlap types.
     */

    for (i = DBNumUserLayers; i < DBNumTypes; i++)
    {
	TTMaskAndMask3(&rmask, &set, DBResidueMask(i));
	if (!TTMaskIsZero(&rmask))
	    TTMaskSetType(&DRCExactOverlapTypes, i);
    }
    return (0);
}

#ifdef DRC_EXTENSIONS
/*
 * ----------------------------------------------------------------------------
 *
 * drcRectangle --
 *
 * Process a rectangle rule.  This is of the form:
 *
 *	rectangle layers maxwidth [even|odd|any] why
 *
 * The rule checks to make sure that the region is rectangular and that the
 * width and length are even or odd, as specified.  These two criteria ensure 
 * that the squares rule of the cifout section can properly produce via 
 * holes without misaligning them between cells and without putting the via 
 * holes off grid.  The maxwidth is required to make the extent of this rule
 * a finite size, so that we can set the DRChalo to something finite.
 *
 * Results:
 *	maxwidth
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcRectangle(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    char *why = drcWhyDup(argv[4]);
    TileTypeBitMask set, types, nottypes;
    int maxwidth;
    static char *drcRectOpt[4] = {"any", "even", "odd", 0};
    int i, j, even, plane;

    /* parse arguments */
    DBTechNoisyNameMask(layers, &set);
    if (sscanf(argv[2], "%d", &maxwidth) != 1) {
	TechError("bad maxwidth in rectangle rule");
	return 0;
    }
    even = Lookup(argv[3], drcRectOpt);
    if (even < 0) {
	TechError("bad [even|odd|any] selection in rectangle rule");
	return 0;
    }
    even--;  /* -1: any, 0: even, 1: odd */
    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++) {
	if (DBTechSubsetLayers(set, DBPlaneTypes[plane], &types)) break;
    }
    if (plane == DBNumPlanes) {
	TechError("Layers in rectangle rule must lie in a single plane.");
	return 0;
    }
    TTMaskCom2(&nottypes, &types);
    TTMaskAndMask(&nottypes, &DBPlaneTypes[plane]);

    /* Install 2 edge rules: one that checks rectangle-ness, and one that
     * checks size
     */
    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    if (TTMaskHasType(&types, i) && TTMaskHasType(&nottypes, j))
	    {
		DRCCookie *dp, *dpnew;

		/* 
		 * A rule that checks rectangle-ness. 
		 *   left:  oktypes, right: other types
		 * This rule needs to be checked in all 4 directions
		 */
		int distance = 1;

		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(i, j, distance);
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next, 
		    nottypes, DBAllTypeBits, why, distance, 
		    DRC_FORWARD, plane);
		dp->drcc_next = dpnew;

		/* Find bucket preceding the new one we wish to insert */
		dp = drcFindBucket(j, i, distance); /* note: j, i not i, j */
		dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
		ASSIGN(dpnew, distance, dp->drcc_next, 
		    nottypes, DBAllTypeBits, why, distance, 
		    DRC_REVERSE, plane);
		dp->drcc_next = dpnew;

		if (maxwidth > 0) {
		    /* 
		     * A rule that checks size.
		     *   left:  other types, right: oktypes
		     */
		    distance = maxwidth;

		    for (dp = DRCRulesTbl[j][i];  /* note: j, i not i, j */
			dp->drcc_next != (DRCCookie *) NULL &&
			dp->drcc_next->drcc_dist < distance;
			    dp = dp->drcc_next)
			; /* null body */

		    dpnew = (DRCCookie *) mallocMagic ((unsigned) (sizeof (DRCCookie)));
		    ASSIGN(dpnew, distance, dp->drcc_next, 
			types, DBZeroTypeBits, why, even, 
			DRC_RECTSIZE, plane);
		    dp->drcc_next = dpnew;
		}
	    }
	}
    }
    return maxwidth;
}
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * drcStepSize --
 *
 * Process a declaration of the step size.
 * This is of the form:
 *
 *	stepsize step_size
 *
 * e.g,
 *
 *	stepsize 1000
 *
 * Results:
 *	Returns 0.
 *
 * Side effects:
 *	Updates DRCStepSize.
 *
 * ----------------------------------------------------------------------------
 */

int
drcStepSize(argc, argv)
    int argc;
    char *argv[];
{
    DRCStepSize = atoi(argv[1]);
    if (DRCStepSize <= 0)
    {
	TechError("Step size must be a positive integer.\n");
	DRCStepSize = 0;
    }
    else if (DRCStepSize < 16)
    {
	TechError("Warning: abnormally small DRC step size (%d)\n",
		DRCStepSize);
    }

    return (0);
}

#ifdef DRC_CIF_EXTENSIONS
/*
 * ----------------------------------------------------------------------------
 *
 * drcSetCifStyle --
 *
 * Process a declaration of the cif style.
 * This is of the form:
 *
 *	cifstyle cif_style
 *
 * e.g,
 *
 *	cifstyle pg
 *
 * Results:
 *	Returns 0.
 *
 * Side effects:
 *	Updates drcCifStyle and CIFCurStyle
 *
 * ----------------------------------------------------------------------------
 */

int
drcSetCifStyle(argc, argv)
    int argc;
    char *argv[];
{
    CIFKeep	*new;
    
    for (new = CIFStyleList; new != NULL; new = new->cs_next)
    {
    	if (strcmp(new->cs_name,argv[1]) == 0)
	{
	    if (new->cs_name != CIFCurStyle->cs_name)
		CIFLoadStyle(new->cs_name);
	    drcCifStyle = CIFCurStyle;
	    return 0;
	}
    }
    TechError("Unknown DRC cifstyle %s\n",argv[1]);
    return (0);
}
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * DRCTechFinal --
 *
 * Called after all lines of the drc section in the technology file have been
 * read.  The preliminary DRC Rules Table is pruned by removing rules covered
 * by other (longer distance) rules, and by removing the dummy rule at the
 * front of each list.  Where edges are completely illegal, the rule list is
 * pruned to a single rule.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May remove DRCCookies from the linked lists of the DRCRulesTbl.
 *
 * ----------------------------------------------------------------------------
 */

void
DRCTechFinal()
{
    TileTypeBitMask tmpMask, nextMask;
    DRCCookie  *dummy, *dp, *next, *dptrig;
    DRCCookie **dpp, **dp2back;
    TileType i, j;


    /* Remove dummy buckets */
    for (i = 0; i < TT_MAXTYPES; i++)
    {
	for (j = 0; j < TT_MAXTYPES; j++)
	{
	    dpp = &( DRCRulesTbl [i][j]);
	    dummy = *dpp;
	    *dpp = dummy->drcc_next;
	    freeMagic((char *) dummy); 
	}
    }

#ifdef DRC_CIF_EXTENSIONS
    drcCifFinal();
#endif
    if (!DRCRuleOptimization) return;

    /* Check for edges that are completely illegal.  Where this is the
     * case, eliminate all of the edge's rules except one.
     */
    
    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    DRCCookie *keep = NULL;
	    
	    for (dp = DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
	    {
		if (dp->drcc_flags & DRC_XPLANE) continue;
#ifdef DRC_EXTENSIONS
		if (dp->drcc_flags & DRC_WEIRDONES) continue;
#endif
		if (dp->drcc_flags & DRC_REVERSE)
		{
		    if (TTMaskHasType(&dp->drcc_mask, i)) continue;
		}
		else if (TTMaskHasType(&dp->drcc_mask, j)) continue;

		/* In magic 7.3, this edge may be legal on planes other	*/
		/* than drcc_plane if i or j is a contact type or	*/
		/* TT_SPACE.						*/

		if (DBIsContact(i) || DBIsContact(j) || i == TT_SPACE ||
			j == TT_SPACE) continue;

		keep = dp;
		goto illegalEdge;
	    }
	    continue;

	    /* This edge is illegal.  Throw away all rules except the one
	     * needed that is always violated.
	     */
	    
	    illegalEdge:
	    for (dp = DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
	    {
		if (dp == keep) continue;
		/* Don't free the shared drcc_why string here! */
		freeMagic((char *) dp);
		drcRulesOptimized += 1;
	    }
	    DRCRulesTbl[i][j] = keep;
	    keep->drcc_next = NULL;
	    /* TxPrintf("Edge %s-%s is illegal.\n", DBTypeShortName(i),
		DBTypeShortName(j));
	    */
	}
    }

    /*
     * Remove any rule A "covered" by another rule B, i.e.,
     *		B's distance >= A's distance,
     *		B's corner distance >= A's corner distance,
     *		B's RHS type mask is a subset of A's RHS type mask, and
     *		B's corner mask == A's corner mask
     *		B's check plane == A's check plane
     *		either both A and B or neither is a REVERSE direction rule
     *		if A is BOTHCORNERS then B must be, too
     */

    for (i = 0; i < DBNumUserLayers; i++)
    {
	for (j = 0; j < DBNumUserLayers; j++)
	{
	    for (dp = DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
	    {
		/* Don't optimize on trigger rules; optimize on the	*/
		/* rule that gets triggered.				*/
		if (dp->drcc_flags & DRC_TRIGGER)
		{
		    dptrig = dp;
		    dp = dp->drcc_next;
		}
		else
		    dptrig = NULL;
		
		/*
		 * Check following buckets to see if any is a superset.
		 */
#ifdef DRC_EXTENSIONS
		if (dp->drcc_flags & DRC_WEIRDONES) continue;
#endif
		for (next = dp->drcc_next; next != NULL;
			next = next->drcc_next)
		{
		    if (next->drcc_flags & DRC_TRIGGER)
		    {
			/* A triggered rule cannot be considered */
			/* a superset of a non-triggered rule or */
			/* a rule with a different trigger, so   */
			/* we skip all triggered rules and their */
			/* triggering rule.			 */
			next = next->drcc_next;
			continue;
		    }
		    tmpMask = nextMask = next->drcc_mask;
		    TTMaskAndMask(&tmpMask, &dp->drcc_mask);
		    if (!TTMaskEqual(&tmpMask, &nextMask)) continue;
		    if (!TTMaskEqual(&dp->drcc_corner, &next->drcc_corner))
			continue;
		    if (dp->drcc_dist > next->drcc_dist) continue;
		    if (dp->drcc_cdist > next->drcc_cdist) continue;
		    if (dp->drcc_plane != next->drcc_plane) continue;
		    if (dp->drcc_flags & DRC_REVERSE)
		    {
			if (!(next->drcc_flags & DRC_REVERSE)) continue;
		    }
		    else if (next->drcc_flags & DRC_REVERSE) continue;
		    if ((next->drcc_flags & DRC_BOTHCORNERS)
			    && (dp->drcc_flags & DRC_BOTHCORNERS) == 0)
			continue;
#ifdef DRC_EXTENSIONS
		    if (next->drcc_flags & DRC_WEIRDONES) continue;
#endif
		    break;
		}

		if (next == NULL) continue;

		/* "dp" is a subset of "next".  Eliminate it. */

		/* For triggered rules, eliminate both the rule */
		/* and the trigger.				*/
		if (dptrig != NULL) dp = dptrig;

		/* TxPrintf("For edge %s-%s, \"%s\" covers \"%s\"\n",
		    DBTypeShortName(i), DBTypeShortName(j),
		    next->drcc_why, dp->drcc_why);
		*/
		dp2back = &(DRCRulesTbl[i][j]);
		while (*dp2back != dp)
		    dp2back = &(*dp2back)->drcc_next;

		/* Trigger rules */
		if (dptrig != NULL)
		{
		    dptrig = dp->drcc_next;
		    freeMagic((char *)dp->drcc_next);
		    *dp2back = dp->drcc_next->drcc_next;

		    /* Replace this entry so on the next cycle	*/
		    /* dp will be the next rule.  This works	*/
		    /* even though dp is free'd (below), due to	*/
		    /* the one-delayed free mechanism.		*/
		    dp->drcc_next = *dp2back;
		}
		else
		    *dp2back = dp->drcc_next;

		/* Don't free the shared drcc_why string here! */
		freeMagic((char *) dp);
		drcRulesOptimized += 1;
	    }
	}
    }

    /*
     * Add stacked types to all the rules which affect either of the
     * stacked contact type's residue contacts.
     */

    for (i = 0; i < DBNumUserLayers; i++)
    {
	int k, l;
	TileTypeBitMask *rMask;

	for (j = 0; j < DBNumUserLayers; j++)
	    for (dp = DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
		for (k = DBNumUserLayers; k < DBNumTypes; k++)
		{
		    rMask = DBResidueMask(k);
		    for (l = 0; l < DBNumUserLayers; l++)
			if (TTMaskHasType(rMask, l))
			{
			    if (TTMaskHasType(&dp->drcc_mask, l))
				TTMaskSetType(&dp->drcc_mask, k);
			    if (TTMaskHasType(&dp->drcc_corner, l))
				TTMaskSetType(&dp->drcc_corner, k);
			}
		}
    }

    /* Set maximum halo */
    MaxHalo = TechHalo;
}


/*
 * ----------------------------------------------------------------------------
 *
 * DRCTechRuleStats --
 *
 * 	Print out some statistics about the design rule database.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A bunch of stuff gets printed on the terminal.
 *
 * ----------------------------------------------------------------------------
 */

void
DRCTechRuleStats()
{
#define MAXBIN 10
    int counts[MAXBIN+1];
    int edgeRules, overflow;
    int i, j;
    DRCCookie *dp;

    /* Count up the total number of edge rules, and histogram them
     * by the number of rules per edge.
     */
    
    edgeRules = 0;
    overflow = 0;
    for (i=0; i<=MAXBIN; i++) counts[i] = 0;

    for (i=0; i<DBNumTypes; i++)
	for (j=0; j<DBNumTypes; j++)
	{
	    int thisCount = 0;
	    for (dp = DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
		thisCount++;
	    edgeRules += thisCount;
	    if (!DBTypesOnSamePlane(i, j)) continue;
	    if (thisCount <= MAXBIN) counts[thisCount] += 1;
	    else overflow += 1;
	}
    
    /* Print out the results. */

    TxPrintf("Total number of rules specifed in tech file: %d\n",
	drcRulesSpecified);
    TxPrintf("Edge rules optimized away: %d\n", drcRulesOptimized);
    TxPrintf("Edge rules left in database: %d\n", edgeRules);
    TxPrintf("Histogram of # edges vs. rules per edge:\n");
    for (i=0; i<=MAXBIN; i++)
    {
	TxPrintf("  %2d rules/edge: %d.\n", i, counts[i]);
    }
    TxPrintf(" >%2d rules/edge: %d.\n", MAXBIN, overflow);
}

/*
 * ----------------------------------------------------------------------------
 *
 * DRCTechScale --
 *
 * 	Multiply all DRC rule widths and spacings by a factor of scaled/scalen.
 *	(Don't need to use DBScaleValue() because all values must be positive
 *	and cannot be (M)INFINITY.)
 *
 * ----------------------------------------------------------------------------
 */

void
DRCTechScale(scalen, scaled)
    int scalen, scaled;
{
    DRCCookie  *dp;
    TileType i, j;

    for (i = 0; i < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; j++)
	{
	    for (dp = DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
	    {
		if (dp->drcc_dist > 0)
		{
		    dp->drcc_dist *= scaled;
		    dp->drcc_dist /= scalen;
		    dp->drcc_cdist *= scaled;
		    dp->drcc_cdist /= scalen;
		}
	    }
	}
    }

    TechHalo *= scaled;
    TechHalo /= scalen;

    MaxHalo *= scaled;
    MaxHalo /= scalen;
}

/* The following routines are used by the "tech" command (and in other places,
 * such as the LEF file reader) to query the DRC database.
 */

/*
 *-----------------------------------------------------------------------------
 *  DRCGetDefaultLayerWidth ---
 *
 *	Determine a default layer width from the DRC width rules
 *	of a layer.  Break on the first rule found, since rules
 *	are ordered from shortest to longest distance.
 *
 * Results:
 *	The minimum width of the magic layer, in magic internal units
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

int
DRCGetDefaultLayerWidth(ttype)
    TileType ttype;
{
    int routeWidth = 0;
    DRCCookie *cptr;
    TileTypeBitMask *set;

    for (cptr = DRCRulesTbl[TT_SPACE][ttype]; cptr != (DRCCookie *) NULL;
	cptr = cptr->drcc_next)
    {
	if (cptr->drcc_flags == DRC_FORWARD)
	{
	    set = &cptr->drcc_mask;
	    if (TTMaskEqual(set, &cptr->drcc_corner))
		if ((cptr->drcc_plane == DBPlane(ttype)) &&
			(cptr->drcc_dist == cptr->drcc_cdist))
		{
		    routeWidth = cptr->drcc_dist;
		    /* Diagnostic */
		    /*
		    TxPrintf("DRC: Layer %s has default width %d\n",
		    DBTypeLongNameTbl[ttype], routeWidth);
		    */
		    break;
		}
	}
    }
    return routeWidth;
}

/*
 *-----------------------------------------------------------------------------
 *  DRCGetDefaultLayerSpacing ---
 *
 *	Determine a default layer-to-layer spacing from the DRC width
 *	rules of a layer.  Break on the first rule found, since rules
 *	are ordered from shortest to longest distance.
 *
 * Results:
 *	The minimum spacing between the specified magic layer types,
 *	in magic internal units
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

int
DRCGetDefaultLayerSpacing(ttype1, ttype2)
    TileType ttype1, ttype2;
{
    int routeSpacing = 0;
    DRCCookie *cptr;
    TileTypeBitMask *set;

    for (cptr = DRCRulesTbl[ttype1][TT_SPACE]; cptr != (DRCCookie *) NULL;
	cptr = cptr->drcc_next)
    {
	if (cptr->drcc_flags == DRC_FORWARD)
	{
	    set = &cptr->drcc_mask;
	    if (!TTMaskHasType(set, ttype2))
		if ((cptr->drcc_plane == DBPlane(ttype2)) &&
			(cptr->drcc_dist == cptr->drcc_cdist))
		{
		    routeSpacing = cptr->drcc_dist;
		    /* Diagnostic */
		    /*
		    TxPrintf("DRC: Layer %s has default spacing %d to layer %s\n",
			DBTypeLongNameTbl[ttype1], routeSpacing,
			DBTypeLongNameTbl[ttype2]);
		    */
		    break;
		}
	}
    }
    return routeSpacing;
}
