/*
 * 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[] = "$Header: /ufs/repository/magic/drc/DRCtech.c,v 1.5 2006/02/08 03:46:18 fang Exp $";
#endif  /* not lint */

#include <stdio.h>
#include "misc/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 "tech/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;
#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 drcStepSize();
#ifdef DRC_EXTENSIONS
int drcMaxwidth(), drcArea(), drcRectangle();
#ifdef DRC_CIF_EXTENSIONS
int drcSetCifStyle(),drcCifWidth(),drcCifSpacing();
int drcCifMaxwidth(),drcCifArea();
extern void drcCifInit(void);
extern void drcCifFinal(void);
#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.
 * ----------------------------------------------------------------------------
 */

static void
drcWhyClear(void)
{
    while (drcWhyList != NULL)
    {
	register char * old = (char *) drcWhyList;
	(void) StrDup(&(drcWhyList->dwl_string), (char *) NULL);
	drcWhyList = drcWhyList->dwl_next;
	FREE(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().
 * ----------------------------------------------------------------------------
 */

#ifdef DRC_CIF_EXTENSIONS
char *
#else
static char *
#endif
drcWhyDup(why)
    char * why;
{
    struct drcwhylist * new;

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

    return new->dwl_string;
}

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

/* Void */
void
DRCTechInit(void)
{
    register int i, j, plane;
    register DRCCookie *dp;

    TechHalo = 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)
	    {
		register char * old = (char *) dp;
		dp = dp->drcc_next;
		FREE(old);
	    }
	    MALLOC(DRCCookie *, dp, sizeof (DRCCookie));
	    dp->drcc_dist = -1;
	    dp->drcc_next = (DRCCookie *) NULL;
	    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++)
    {
	/* printf("Plane is %s.\n", DBPlaneLongName(plane)); */
	for (i = 0; i < DBNumTypes; i++)
	    for (j = 0; j < DBNumTypes; j++)
	    {
		PaintResultType 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)
			|| (DBPlane(j) != plane)
			|| !DBPaintOnPlane(i, DBPlane(j)))
		    DRCPaintTable[plane][i][j] = result;
		else if ((!TTMaskHasType(&DBLayerTypeMaskTbl[i], result)
			&& !TTMaskHasType(&DBLayerTypeMaskTbl[j], result))
			|| ((result != DBPaintResultTbl[plane][j][i])
			&& (DBPlane(i) == plane)
			&& DBPaintOnPlane(j, DBPlane(i))))
		{
		    DRCPaintTable[plane][i][j] = TT_ERROR_S;
		    /* printf("Error: %s on %s, was %s\n", DBTypeShortName(i),
			    DBTypeShortName(j), DBTypeShortName(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.
 *
 * ----------------------------------------------------------------------------
 */

#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)

	/* 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"},
	{"spacing",	 6,	6,	drcSpacing,
    "layers1 layers2 separation adjacency why"},
	{"stepsize",	 2,	2,	drcStepSize,
    "step_size"},
	{"width",	 4,	4,	drcWidth,
    "layers width why"},
#ifdef DRC_EXTENSIONS
        {"area",		5,	5,	drcArea,
    "layers area horizon why"},
        {"maxwidth",	5,	5,	drcMaxwidth,
    "layers maxwidth bends why"},
        {"cifstyle",	 2,	2,	drcSetCifStyle,
#ifdef DRC_CIF_EXTENSIONS
    "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,		0,	0,	0,	0}
    }, *rp;

    drcRulesSpecified += 1;

    which = LookupStructTable(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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
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))
	    goto widthOK;
    TechError("All layers for \"width\" must be on same plane\n");
    return (0);

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

    for (i = 0; i < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; 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 (SamePlane(i, j)
		    && TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
	    {
		/* find bucket preceding the new one we wish to insert */
		for (dp = DRCRulesTbl [i][j];
			 dp->drcc_next != (DRCCookie *) NULL &&
			 dp->drcc_next->drcc_dist < distance;
			     dp = dp->drcc_next)
		    ; /* null body */

		MALLOC(DRCCookie *, dpnew, 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 distance why
 *
 * e.g,
 *
 *	area pmc 4 "poly contact area must be at least 4"
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
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))
	    goto widthOK;
    TechError("All layers for \"area\" must be on same plane\n");
    return (0);

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

    for (i = 0; i < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; 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 (SamePlane(i, j)
		    && TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
	    {
		/* find bucket preceding the new one we wish to insert */
		for (dp = DRCRulesTbl [i][j];
			 dp->drcc_next != (DRCCookie *) NULL &&
			 dp->drcc_next->drcc_dist < horizon;
			     dp = dp->drcc_next)
		    ; /* null body */

		MALLOC(DRCCookie *, dpnew, 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
 *
 * e.g,
 *
 *	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 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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
int
drcMaxwidth(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    int distance = atoi(argv[2]);
    char *bends = argv[3];
    char *why = drcWhyDup(argv[4]);
    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))
	    goto widthOK;
    TechError("All layers for \"area\" must be on same plane\n");
    return (0);

widthOK:
    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);
    }
    set = tmp1;
    TTMaskCom2(&setC, &set);

    for (i = 0; i < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; 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 (SamePlane(i, j)
		    && TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
	    {
		/* find bucket preceding the new one we wish to insert */
		for (dp = DRCRulesTbl [i][j];
			 dp->drcc_next != (DRCCookie *) NULL &&
			 dp->drcc_next->drcc_dist < distance;
			     dp = dp->drcc_next)
		    ; /* null body */

		MALLOC(DRCCookie *, dpnew, 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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
int
drcSpacing(argc, argv)
    int argc;
    char *argv[];
{
    char *layers1 = argv[1], *layers2 = argv[2];
    int distance = atoi(argv[3]);
    char *adjacency = argv[4];
    char *why = drcWhyDup(argv[5]);
    TileTypeBitMask set1, set2, tmp1, tmp2, setR, setRreverse;
    int plane,curPlane, planes1, planes2;
    DRCCookie *dp, *dpnew;
    int needReverse = FALSE;
    TileType i, j;

    DBTechNoisyNameMask(layers1, &set1);
    planes1 = DBTechMinSetPlanes(set1, &set1);
    DBTechNoisyNameMask(layers2, &set2);
    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") == 0)
    {
	/* If touching is OK, everything must fall in the same plane. */
	if (plane == DBNumPlanes)
	{
	    TechError(
		"Spacing check with touching ok must all be 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") == 0)
    {
	/* 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 < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; j++)
	{
	    if (i == j || !SamePlane(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 */
		for (dp = DRCRulesTbl [i][j];
			 dp->drcc_next != (DRCCookie *) NULL &&
			 dp->drcc_next->drcc_dist < distance;
			     dp = dp->drcc_next)
		    ; /* null body */
		
		/* May have to insert several buckets on different planes */
		for (curPlane = PL_TECHDEPBASE; curPlane < DBNumPlanes; curPlane++)
		{
		    if (!PlaneMaskHasPlane(planes2, curPlane)) continue;
		    MALLOC(DRCCookie *, dpnew, sizeof (DRCCookie));
		    TTMaskClearMask3(&tmp1, &DBPlaneTypes[curPlane], &set2);
		    TTMaskAndMask3(&tmp2, &DBPlaneTypes[curPlane], &setR);
		    ASSIGN(dpnew, distance, dp->drcc_next,
			tmp1, tmp2, why, distance, DRC_FORWARD, curPlane);

		    if (i == TT_SPACE)
		    {
			if (curPlane != DBPlane(j))
			    dpnew->drcc_flags |= DRC_XPLANE;
		    }
		    else
		    {
			if (curPlane != DBPlane(i))
			    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 */
			 for (dp = DRCRulesTbl [j][i];
			          dp->drcc_next != (DRCCookie *) NULL &&
   			          dp->drcc_next->drcc_dist < distance;
			          dp = dp->drcc_next)
			     ; /* null body */
			 
			 MALLOC(DRCCookie *, dpnew, sizeof (DRCCookie));
			 ASSIGN(dpnew,distance,dp->drcc_next,
				tmp1, tmp2, why, distance, 
				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;

	    /* 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 */
		for (dp = DRCRulesTbl [i][j];
			 dp->drcc_next != (DRCCookie *) NULL &&
			 dp->drcc_next->drcc_dist < distance;
			     dp = dp->drcc_next)
		    ; /* null body */

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

		    MALLOC(DRCCookie *, dpnew, 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 (curPlane != DBPlane(j))
			    dpnew->drcc_flags |= DRC_XPLANE;
		    }
		    else
		    {
			if (curPlane != DBPlane(i))
			    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 */
			 for (dp = DRCRulesTbl [j][i];
			          dp->drcc_next != (DRCCookie *) NULL &&
   			          dp->drcc_next->drcc_dist < distance;
			          dp = dp->drcc_next)
			     ; /* null body */
			 
			 MALLOC(DRCCookie *, dpnew, 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 (curPlane == DBPlane(j))
			    continue;
		    }
		    else
		    {
			if (curPlane == DBPlane(i))
			    continue;
		    }

		    /* create new check and add it to list */
		    MALLOC(DRCCookie *, dpnew, 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 (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;
    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))
	    goto edgeOK;
    }
    TechError("All edges in edge rule must lie in one plane.\n");
    return (0);

edgeOK:
    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);
    }

    checkPlane = plane;
    if (argc == 9)
    {
	checkPlane = DBTechNoisyNamePlane(argv[8]);
	if (checkPlane < 0)
	    return (0);
    }

    DBTechNoisyNameMask(okTypes, &setM);
    if (!DBTechSubsetLayers(setM, DBPlaneTypes[checkPlane], &setM))
    {
	TechError("OK types aren't all in the right plane.\n");
	return (0);
    }

    for (i = 0; i < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; j++)
	{
	    if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
	    {
		/* Find bucket preceding the new one we wish to insert */
		for (dp = DRCRulesTbl [i][j];
		    dp->drcc_next != (DRCCookie *) NULL &&
		    dp->drcc_next->drcc_dist < distance;
			dp = dp->drcc_next)
		    ; /* null body */

		MALLOC(DRCCookie *, dpnew, 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 */
		    for (dp = DRCRulesTbl [j][i];
			dp->drcc_next != (DRCCookie *) NULL &&
			dp->drcc_next->drcc_dist < distance;
			    dp = dp->drcc_next)
			; /* null body */

		    MALLOC(DRCCookie *, dpnew, 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));
}

/*
 * ----------------------------------------------------------------------------
 *
 * 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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
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 < DBNumTypes; i++)
	for (j = 0; j < DBNumTypes; 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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
int
drcExactOverlap(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    TileTypeBitMask set;
    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);

    /*
     * Go through the types and eliminate all but the first image
     * of each contact.
     */

    for (i = 0; i < DBNumTypes; i++)
    {
	if (!TTMaskHasType(&set, i)) continue;
	TTMaskSetType(&DRCExactOverlapTypes, i);
	TTMaskClearMask(&set, &DBLayerTypeMaskTbl[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 < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; 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 */
		for (dp = DRCRulesTbl[i][j];
		    dp->drcc_next != (DRCCookie *) NULL &&
		    dp->drcc_next->drcc_dist < distance;
			dp = dp->drcc_next)
		    ; /* null body */

		MALLOC(DRCCookie *, dpnew, 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 */
		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 */

		MALLOC(DRCCookie *, dpnew, 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 */

		    MALLOC(DRCCookie *, dpnew, 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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
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
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
int
drcSetCifStyle(argc, argv)
    int argc;
    char *argv[];
{
    CIFStyle	*new;
    
    for (new = CIFStyleList; new != NULL; new = new->cs_next)
    {
    	 if (strcmp(new->cs_name,argv[1]) == 0)
	 {
	          drcCifStyle = new;
		  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 */
void
DRCTechFinal(void)
{
    TileTypeBitMask tmpMask, nextMask;
    DRCCookie  *dummy, *dp, *next;
    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;
	    FREE ((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 < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; 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;
		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! */
		FREE((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 < DBNumTypes; i++)
    {
	for (j = 0; j < DBNumTypes; j++)
	{
	    for (dp = DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
	    {
		/*
		 * 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)
		{
		    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. */

		/* 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;
		*dp2back = dp->drcc_next;
		/* Don't free the shared drcc_why string here! */
		FREE ((char *) dp);
		drcRulesOptimized += 1;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * 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(void)
{
#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 ((i != TT_SPACE) && (j != TT_SPACE) &&
		(DBPlane(i) != DBPlane(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.
 *
 * ----------------------------------------------------------------------------
 */

/* Void */
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)
	    {
		dp->drcc_dist *= scaled;
		dp->drcc_dist /= scalen;
		dp->drcc_cdist *= scaled;
		dp->drcc_cdist /= scalen;
	    }
	}
    }
}

