/*
 * 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-7.4/drc/DRCtech.c,v 1.5 2007/11/02 05:12:54 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",
#ifdef DRC_CIF_EXTENSIONS
        "cifstyle",	 2,	2,	drcSetCifStyle,
    "cif_style",
	"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 must be no wider than 4"
 *	maxwidth trench 4 bend_ok "trench width must be exactly 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 == 4)
    {
	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
 *
 * 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.
 *
 * 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;

    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;
	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"))
    {
	/* If touching is OK, everything must fall in the same plane. */
	if (plane == DBNumPlanes)
	{
	    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 | DRC_BENDS, 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 (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 | DRC_BENDS, 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;

		    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.  Do NOT attempt to update the CIF style
 *	in the middle of reading the section.  This causes all sorts
 *	of problems, since the CIF sections are independently loaded
 *	and changing styles will most likely cause Magic to crash.
 *	This needs to be fixed.
 *
 * ----------------------------------------------------------------------------
 */

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 (!strcmp(new->cs_name, CIFCurStyle->cs_name))
		drcCifStyle = CIFCurStyle;
	    else
		TechError("DRC cifstyle must be the default style!\n");
	    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 each mask, if
     * any residue type is allowed, then the stacked type is allowed.
     * Note that this is incorrect and needs to be fixed!
     */

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

	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);
		    TTMaskAndMask3(&tmpMask, rMask, &dp->drcc_mask);
		    if (!TTMaskIsZero(&tmpMask))
			TTMaskSetType(&dp->drcc_mask, k);

		    TTMaskAndMask3(&tmpMask, rMask, &dp->drcc_corner);
		    if (!TTMaskIsZero(&tmpMask))
			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;
}
