/*
 * DRCcif.c --
 *
 ******************************************************************************
 * Copyright (C) 1989 Digital Equipment Corporation
 * 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.  Digital Equipment Corporation
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 ****************************************************************************
 *
 */

#ifndef	lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/drc/DRCcif.c,v 1.5 2010/10/20 20:34:20 tim Exp $";
#endif	/* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "dbwind/dbwtech.h"
#include "drc/drc.h"
#include "cif/cif.h"
#include "cif/CIFint.h"
#include "utils/signals.h"
#include "utils/stack.h"
#include "utils/malloc.h"

/* C99 compat */
#include "utils/tech.h"
#include "textio/textio.h"

extern int  drcCifTile();
extern int  areaCifCheck();
extern void drcCheckCifMaxwidth();
extern void drcCheckCifArea();

extern Stack	*DRCstack;

#define PUSHTILE(tp) \
    if (TiGetClient(tp) == DRC_UNPROCESSED) { \
        TiSetClientINT(tp, DRC_PENDING); \
        STACKPUSH((ClientData) (tp), DRCstack); \
    }

extern CIFStyle	*drcCifStyle;
extern bool DRCForceReload;
TileTypeBitMask drcCifGenLayers;

DRCCookie *drcCifRules[MAXCIFLAYERS][2];
DRCCookie *drcCifCur = NULL;
int	drcCifValid = FALSE;
bool	beenWarned = FALSE;
char	*drcNeedStyle = NULL;

#define DRC_CIF_SPACE		0
#define DRC_CIF_SOLID		1

/* Define Euclidean distance checks */

#define RADIAL_NW       0x1000
#define RADIAL_NE       0x8000
#define RADIAL_SW       0x2000
#define RADIAL_SE       0x4000

/*
 * ----------------------------------------------------------------------------
 *
 * drcCifSetStyle --
 *
 * 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 DRC section.  Instead, if the
 *	reported CIF style is not current, flag a warning.  The DRC
 *	will be re-read with the CIF extensions when the CIF output
 *	style is changed.
 *
 * ----------------------------------------------------------------------------
 */

int
drcCifSetStyle(
    int argc,
    char *argv[])
{
    CIFKeep     *new;

    for (new = CIFStyleList; new != NULL; new = new->cs_next)
    {
	if (!strcmp(new->cs_name, argv[1]))
	{
	    drcNeedStyle = new->cs_name;
	    DRCForceReload = TRUE;
	    if (!strcmp(new->cs_name, CIFCurStyle->cs_name))
		drcCifStyle = CIFCurStyle;
	    else
	    {
		drcCifStyle = NULL;
		beenWarned = TRUE;	/* post no more error messages */
	    }
	    return 0;
	}
    }
    TechError("Unknown DRC cifstyle %s\n",argv[1]);
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 * ----------------------------------------------------------------------------
 */

int
drcCifWarning()
{
    if (!beenWarned)
    {
	TechError("Missing cif style for drc\n\t"
		"This message will not be repeated.\n");
	beenWarned = TRUE;
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcCifWidth -- same this as drcCifWidth, except that it works on
 *	cif layers
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * Notes:  "centidistance" is by default in centimicrons, but it is really in
 * whatever units are declared in the cifoutput section;  so if cifoutput
 * declares nanometers, then units are in nanometers.  This may be in
 * different units from the non-CIF part of the DRC section.  The escape
 * sequence substitution in the "Why" text will make sure that the correct
 * physical dimensions are presented to the end user.
 *
 * ----------------------------------------------------------------------------
 */

int
drcCifWidth(
    int argc,
    char *argv[])
{
    char *layername = argv[1];
    int scalefactor;
    int centidistance = atoi(argv[2]);
    int why = drcWhyCreate(argv[3]);
    TileTypeBitMask set, setC, tmp1;
    int	thislayer = -1;
    DRCCookie *dpnew,*dpnext;
    TileType i;

    if (drcCifStyle == NULL)
	return drcCifWarning();

    for (i = 0; i < drcCifStyle->cs_nLayers;i++)
    {
    	 CIFLayer	*layer = drcCifStyle->cs_layers[i];

	 if (strcmp(layer->cl_name,layername) == 0)
	 {
	      thislayer = i;
	      break;
	 }
    }
    if (thislayer == -1)
    {
    	 TechError("Unknown cif layer: %s\n",layername);
         return (0);
    }

    scalefactor = drcCifStyle->cs_scaleFactor;
    // centidistance *= drcCifStyle->cs_expander;		// BSI

    dpnext = drcCifRules[thislayer][DRC_CIF_SPACE];
    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
    drcCifAssign(dpnew, centidistance, dpnext, &CIFSolidBits,
    		&CIFSolidBits, why, centidistance,
		DRC_FORWARD | DRC_CIFRULE, thislayer, 0);
    drcCifRules[thislayer][DRC_CIF_SPACE] = dpnew;

    return ((centidistance+scalefactor-1)/scalefactor);
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcCifSpacing -- same this as drcSpacing, except that it works on cif
 * 	layers.
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcCifSpacing(
    int argc,
    char *argv[])
{
    char *adjacency = argv[4];
    int why = drcWhyCreate(argv[5]);
    DRCCookie *dpnext, *dpnew;
    int needReverse = FALSE;
    TileType i, j;
    int scalefactor;
    int centidistance = atoi(argv[3]);
    char *layers[2];
    TileType layer[2];
    TileTypeBitMask	cmask;
    int	k;

    layers[0] = argv[1];
    layers[1] = argv[2];

    if (drcCifStyle == NULL)
	return drcCifWarning();

    for (k=0; k!= 2;k++)
    {
         for (i = 0; i < drcCifStyle->cs_nLayers;i++)
         {
    	      CIFLayer	*l = drcCifStyle->cs_layers[i];

	      if (strcmp(l->cl_name,layers[k]) == 0)
	      {
	      	   layer[k]=i;
	           break;
	      }
         }
         if (i == drcCifStyle->cs_nLayers || layer[k] == -1)
         {
    	      TechError("Unknown cif layer: %s",layers[k]);
              return (0);
         }
    }

    if (strcmp (adjacency, "touching_ok") == 0)
    {
	/* If touching is OK, everything must fall in the same plane. */
	if (layer[0] != layer[1])
	{
	    TechError(
		"Spacing check with touching ok must all be in one plane.\n");
	    return (0);
	}
	cmask = DBSpaceBits;
    }
    else if (strcmp (adjacency, "touching_illegal") == 0)
    {
	 cmask = DBAllTypeBits;
	 needReverse = TRUE;
         /* nothing for now */
    }
    else
    {
	TechError("Badly formed drc spacing line\n");
	return (0);
    }

    scalefactor = drcCifStyle->cs_scaleFactor;
    // centidistance *= drcCifStyle->cs_expander;		// BSI
    dpnext = drcCifRules[layer[0]][DRC_CIF_SOLID];
    dpnew = (DRCCookie *) mallocMagic((unsigned) sizeof (DRCCookie));
    drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits,
    		&cmask, why, centidistance, DRC_FORWARD | DRC_CIFRULE, layer[1], 0);
    drcCifRules[layer[0]][DRC_CIF_SOLID] = dpnew;
    if (needReverse) dpnew->drcc_flags |= DRC_BOTHCORNERS;

    // Add rule in reverse direction
    dpnext = drcCifRules[layer[0]][DRC_CIF_SPACE];
    dpnew = (DRCCookie *) mallocMagic((unsigned) sizeof (DRCCookie));
    drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits,
    		&cmask, why, centidistance, DRC_REVERSE | DRC_CIFRULE, layer[1], 0);
    drcCifRules[layer[0]][DRC_CIF_SPACE] = dpnew;

    if (needReverse)
    {
	 // This is not so much "reverse" as it is just the
	 // rule for b->a spacing that matches the a->b spacing.

         dpnew->drcc_flags |= DRC_BOTHCORNERS;
         dpnext = drcCifRules[layer[1]][DRC_CIF_SOLID];
         dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
         drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits, &cmask,
		why, centidistance, DRC_FORWARD | DRC_BOTHCORNERS | DRC_CIFRULE,
		layer[0], 0);
         drcCifRules[layer[1]][DRC_CIF_SOLID] = dpnew;

	 // Add rule in reverse direction
	 dpnext = drcCifRules[layer[1]][DRC_CIF_SPACE];
	 dpnew = (DRCCookie *) mallocMagic((unsigned) sizeof (DRCCookie));
	 drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits, &cmask,
		why, centidistance, DRC_REVERSE | DRC_BOTHCORNERS | DRC_CIFRULE,
		layer[0], 0);
	 drcCifRules[layer[1]][DRC_CIF_SPACE] = dpnew;

	 if (layer[0] == layer[1])
	 {
              dpnext = drcCifRules[layer[1]][DRC_CIF_SPACE];
              dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
              drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits,
			&cmask, why, centidistance,
			DRC_REVERSE | DRC_BOTHCORNERS | DRC_CIFRULE,
			layer[0], 0);
                 drcCifRules[layer[1]][DRC_CIF_SPACE] = dpnew;

              dpnext = drcCifRules[layer[0]][DRC_CIF_SPACE];
              dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
              drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits, &cmask,
			why, centidistance,
			DRC_REVERSE | DRC_BOTHCORNERS | DRC_CIFRULE,
			layer[1], 0);
                 drcCifRules[layer[0]][DRC_CIF_SPACE] = dpnew;
	 }
    }

    if (layer[0] != layer[1]) /* make sure they don't overlap exactly */
    {
         dpnext = drcCifRules[layer[1]][DRC_CIF_SPACE];
         dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
         drcCifAssign(dpnew, scalefactor, dpnext, &DBSpaceBits, &DBZeroTypeBits,
			why, scalefactor, DRC_FORWARD | DRC_CIFRULE, layer[0], 0);
         drcCifRules[layer[1]][DRC_CIF_SPACE] = dpnew;

         dpnext = drcCifRules[layer[0]][DRC_CIF_SPACE];
         dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
         drcCifAssign(dpnew, scalefactor, dpnext, &DBSpaceBits, &DBZeroTypeBits,
			why, scalefactor, DRC_FORWARD | DRC_CIFRULE, layer[1], 0);
         drcCifRules[layer[0]][DRC_CIF_SPACE] = dpnew;
    }

    return ((centidistance+scalefactor-1)/scalefactor);
}

/*
 * ----------------------------------------------------------------------------
 * Scale CIF/DRC rules to match grid scaling.  Scale by factor (n / d)
 * ----------------------------------------------------------------------------
 */

void
drcCifScale(
    int n,
    int d)
{
    DRCCookie *dp;
    int i, j;

    if (DRCCurStyle != NULL)
    {
	for (i = 0; i != MAXCIFLAYERS; i++)
	    for (j = 0; j < 2; j++)
		for (dp = drcCifRules[i][j]; dp != NULL; dp = dp->drcc_next)
		{
		    if (dp->drcc_dist != 0)
		    {
			dp->drcc_dist *= n;
			dp->drcc_dist /= d;
		    }
		    if (dp->drcc_cdist != 0)
		    {
			dp->drcc_cdist *= n;
			dp->drcc_cdist /= d;
		    }
		}
    }
}

/*
 * ----------------------------------------------------------------------------
 * ----------------------------------------------------------------------------
 */

void
drcCifFreeStyle()
{
    DRCCookie *dp;
    int i;
    char *old;

    if (DRCCurStyle != NULL)
    {
	for (i = 0; i != MAXCIFLAYERS; i++)
	{
	    dp = drcCifRules[i][DRC_CIF_SPACE];
	    while (dp != NULL)
	    {
		old = (char *)dp;
	 	dp = dp->drcc_next;
		freeMagic(old);
	    }
	    dp = drcCifRules[i][DRC_CIF_SOLID];
	    while (dp != NULL)
	    {
		old = (char *)dp;
	 	dp = dp->drcc_next;
		freeMagic(old);
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 * ----------------------------------------------------------------------------
 */

void
drcCifInit()
{
    int i;

    if (drcCifValid == TRUE)
	drcCifFreeStyle();

    for (i = 0; i != MAXCIFLAYERS; i++)
    {
	drcCifRules[i][DRC_CIF_SPACE] = NULL;
	drcCifRules[i][DRC_CIF_SOLID] = NULL;
    }
    drcCifValid = FALSE;
    TTMaskZero(&drcCifGenLayers);
    beenWarned = FALSE;
}

/*
 * ----------------------------------------------------------------------------
 * ----------------------------------------------------------------------------
 */

void
drcCifFinal()
{
    int i;

    for (i = 0; i != MAXCIFLAYERS; i++)
    {
        DRCCookie *dp;

	for (dp = drcCifRules[i][DRC_CIF_SPACE]; dp; dp = dp->drcc_next)
	{
	    drcCifValid = TRUE;
            TTMaskSetType(&drcCifGenLayers, i);
            TTMaskSetType(&drcCifGenLayers, dp->drcc_plane);
	}
	for (dp = drcCifRules[i][DRC_CIF_SOLID]; dp; dp = dp->drcc_next)
	{
     	    drcCifValid = TRUE;
            TTMaskSetType(&drcCifGenLayers, i);
            TTMaskSetType(&drcCifGenLayers, dp->drcc_plane);
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 * drcCifCheck---
 *
 * This is the primary routine for design-rule checking on CIF layers.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Error paint, CIF layer generation, lots of stuff going on.
 *
 * ----------------------------------------------------------------------------
 */

void
drcCifCheck(
    struct drcClientData *arg)
{
    Rect	*checkRect = arg->dCD_rect;
    Rect	cifrect;
    int		scale;
    int		i,j;
    int		oldTiles;
    CIFStyle	*CIFSaveStyle = NULL;

    if (CIFCurStyle != drcCifStyle)
    {
	if (drcNeedStyle == NULL) return;

	CIFSaveStyle = CIFCurStyle;

	if (drcCifStyle == NULL)
	{
	    TxPrintf("Loading DRC CIF style.\n");
	    CIFCurStyle = NULL;
	    CIFLoadStyle(drcNeedStyle);
	    if (drcCifValid != FALSE)
		CIFCurStyle = CIFSaveStyle;
	    else
		drcCifStyle = CIFCurStyle;
	}
	if (drcCifStyle == NULL)
	{
	    TxError("Error:  Failed to load CIF DRC style.\n");
	    return;
	}
	CIFCurStyle = drcCifStyle;
    }
    if (drcCifValid == FALSE)
    {
	if (CIFSaveStyle != NULL)
	    CIFCurStyle = CIFSaveStyle;
	return;
    }

    scale = drcCifStyle->cs_scaleFactor;
    cifrect = *checkRect;
    cifrect.r_xbot *= scale;
    cifrect.r_xtop *= scale;
    cifrect.r_ybot *= scale;
    cifrect.r_ytop *= scale;
    arg->dCD_rect = &cifrect;
    oldTiles = DRCstatTiles;

    CIFGen(arg->dCD_celldef, arg->dCD_celldef, checkRect, CIFPlanes,
	    &DBAllTypeBits, TRUE, TRUE, FALSE, (ClientData)NULL);

    for (i = 0; i < drcCifStyle->cs_nLayers; i++)
    {
        for (j = 0; j != 2; j++)
	{
	    for (drcCifCur = drcCifRules[i][j];
	       		drcCifCur; drcCifCur = drcCifCur->drcc_next)
            {
	  	TileTypeBitMask	*mask;

		arg->dCD_plane = i;
	        DBSrPaintArea((Tile *) NULL, CIFPlanes[i], &cifrect,
			(j == DRC_CIF_SOLID) ? &DBSpaceBits : &CIFSolidBits,
	  		drcCifTile, arg);
     	     }
	 }
     }
     arg->dCD_rect = checkRect;
     DRCstatCifTiles += DRCstatTiles - oldTiles;

     /* Put it back the way you found it */
     if (CIFSaveStyle != NULL) CIFCurStyle = CIFSaveStyle;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcCifTile --
 *
 * Results:
 *	Zero (so that the search will continue), unless an interrupt
 *	occurs, in which case 1 is returned to stop the check.
 *
 * Side effects:
 *	Calls the client's error function if errors are found.
 *
 * ----------------------------------------------------------------------------
 */

int
drcCifTile(
    Tile *tile,			/* Tile being examined */
    TileType dinfo,		/* Split tile information */
    struct drcClientData *arg)
{
    DRCCookie *cptr;	/* Current design rule on list */
    Tile *tp;		/* Used for corner checks */
    Rect *rect = arg->dCD_rect;	/* Area being checked */
    Rect errRect;		/* Area checked for an individual rule */
    TileTypeBitMask tmpMask;

    arg->dCD_constraint = &errRect;
    arg->dCD_radial = 0;

    /*
     * If we were interrupted, we want to
     * abort the check as quickly as possible.
     */
    if (SigInterruptPending) return 1;
    DRCstatTiles++;

    /* For non-Manhattan tiles, if the left side of tile is not the */
    /* type that is declared by the rule, then skip the left-right  */
    /* check.							    */

    if (IsSplit(tile))
	if (dinfo & TT_SIDE)
	    goto tbcheck;

    /*
     * Check design rules along a vertical boundary between two tiles.
     *
     *			      1 | 4
     *				T
     *				|
     *			tpleft	|  tile
     *				|
     *				B
     *			      2 | 3
     *
     * The labels "T" and "B" indicate pointT and pointB respectively.
     *
     * If a rule's direction is FORWARD, then check from left to right.
     *
     *	    * Check the top right corner if the 1x1 lambda square
     *	      on the top left corner (1) of pointT matches the design
     *	      rule's "corner" mask.
     *
     *	    * Check the bottom right corner if the rule says check
     *	      BOTHCORNERS and the 1x1 lambda square on the bottom left
     *	      corner (2) of pointB matches the design rule's "corner" mask.
     *
     * If a rule's direction is REVERSE, then check from right to left.
     *
     *	    * Check the bottom left corner if the 1x1 lambda square
     *	      on the bottom right corner (3) of pointB matches the design
     *	      rule's "corner" mask.
     *
     *	    * Check the top left corner if the rule says check BOTHCORNERS
     *	      and the 1x1 lambda square on the top right corner (4) of
     *	      pointT matches the design rule's "corner" mask.
     */

    if (drcCifCur->drcc_flags & DRC_AREA)
    {
    	 drcCheckCifArea(tile, arg, drcCifCur);
	 return 0;
    }
    if (drcCifCur->drcc_flags & DRC_MAXWIDTH)
    {
    	 drcCheckCifMaxwidth(tile, arg, drcCifCur);
	 return 0;
    }
    if (LEFT(tile) >= rect->r_xbot)		/* check tile against rect */
    {
	Tile *tpleft;
	int edgeTop, edgeBot;
        int top = MIN(TOP(tile), rect->r_ytop);
        int bottom = MAX(BOTTOM(tile), rect->r_ybot);
	int edgeX = LEFT(tile);

        for (tpleft = BL(tile); BOTTOM(tpleft) < top; tpleft = RT(tpleft))
        {
	    /* Don't check synthetic edges, i.e. edges with same type on
             * both sides.  Such "edges" have no physical significance, and
	     * depend on internal-details of how paint is spit into tiles.
	     * Thus checking them just leads to confusion.  (When edge rules
	     * involving such edges are encountered during technology readin
	     * the user is warned that such edges are not checked).
	     */
	    if (TiGetRightType(tpleft) == TiGetLeftType(tile))
	        continue;

	    /*
	     * Go through list of design rules triggered by the
	     * left-to-right edge.
	     */
	    edgeTop = MIN(TOP (tpleft), top);
	    edgeBot = MAX(BOTTOM(tpleft), bottom);
	    if (edgeTop <= edgeBot)
		continue;

	    /* do this more intelligently later XXX */
	    cptr = drcCifCur;
	    {
		errRect.r_ytop = edgeTop;
		errRect.r_ybot = edgeBot;

		if (cptr->drcc_flags & DRC_REVERSE)
		{
		    /*
		     * Determine corner extensions.
		     * Find the point (3) to the bottom right of pointB
		     */
		    for (tp = tile; BOTTOM(tp) >= errRect.r_ybot; tp = LB(tp))
			/* Nothing */;
		    if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tp)))
		    {
			errRect.r_ybot -= cptr->drcc_cdist;
			if (DRCEuclidean)
			    arg->dCD_radial |= RADIAL_SW;
		    }

		    if (cptr->drcc_flags & DRC_BOTHCORNERS)
		    {
			/*
			 * Check the other corner by finding the
			 * point (4) to the top right of pointT.
			 */
			if (TOP(tp = tile) <= errRect.r_ytop)
			    for (tp = RT(tp); LEFT(tp) > edgeX; tp = BL(tp))
				/* Nothing */;
			if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tp)))
			{
			    errRect.r_ytop += cptr->drcc_cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= RADIAL_NW;
			}
		    }

		    /*
		     * Just for grins, see if we could avoid a messy search
		     * by looking only at tpleft.
		     */
		    errRect.r_xbot = edgeX - cptr->drcc_dist;
		    if (LEFT(tpleft) <= errRect.r_xbot
			&& BOTTOM(tpleft) <= errRect.r_ybot
			&& TOP(tpleft) >= errRect.r_ytop
			&& arg->dCD_plane == cptr->drcc_plane
			&& TTMaskHasType(&cptr->drcc_mask, TiGetType(tpleft)))
			    continue;
		    errRect.r_xtop = edgeX;
		    arg->dCD_initial = tile;
		}
		else	/* FORWARD */
		{
		    /*
		     * Determine corner extensions.
		     * Find the point (1) to the top left of pointT
		     */
		    for (tp = tpleft; TOP(tp) <= errRect.r_ytop; tp = RT(tp))
			/* Nothing */;
		    if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tp)))
		    {
			errRect.r_ytop += cptr->drcc_cdist;
			if (DRCEuclidean)
			    arg->dCD_radial |= RADIAL_NE;
		    }

		    if (cptr->drcc_flags & DRC_BOTHCORNERS)
		    {
			/*
			 * Check the other corner by finding the
			 * point (2) to the bottom left of pointB.
			 */
			if (BOTTOM(tp = tpleft) >= errRect.r_ybot)
			    for (tp = LB(tp); RIGHT(tp) < edgeX; tp = TR(tp))
				/* Nothing */;
			if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tp)))
			{
			    errRect.r_ybot -= cptr->drcc_cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= RADIAL_SE;
			}
		    }

		    /*
		     * Just for grins, see if we could avoid a messy search
		     * by looking only at tile.
		     */
		    errRect.r_xtop = edgeX + cptr->drcc_dist;
		    if (RIGHT(tile) >= errRect.r_xtop
			&& BOTTOM(tile) <= errRect.r_ybot
			&& TOP(tile) >= errRect.r_ytop
			&& arg->dCD_plane == cptr->drcc_plane
			&& TTMaskHasType(&cptr->drcc_mask, TiGetLeftType(tile)))
			    continue;
		    errRect.r_xbot = edgeX;
		    arg->dCD_initial= tpleft;
		}
		if (arg->dCD_radial)
		{
		    arg->dCD_radial &= 0xf000;
		    arg->dCD_radial |= (0xfff & cptr->drcc_cdist);
		}

		DRCstatSlow++;
		arg->dCD_cptr = (DRCCookie *)cptr;
		TTMaskCom2(&tmpMask, &cptr->drcc_mask);
		(void) DBSrPaintArea((Tile *) NULL,
		    CIFPlanes[cptr->drcc_plane],
		    &errRect, &tmpMask, areaCifCheck, (ClientData) arg);
	    }
	    DRCstatEdges++;
        }
    }

tbcheck:

    /* For non-Manhattan tiles, if the bottom side of tile is not   */
    /* the type that is declared by the rule, then skip the top-    */
    /* bottom check.						    */

    if (IsSplit(tile))
	if (((dinfo & TT_SIDE) ? 1 : 0) == SplitDirection(tile))
	    return 0;

    /*
     * Check design rules along a horizontal boundary between two tiles.
     *
     *			 4	tile	    3
     *			--L----------------R--
     *			 1	tpbot	    2
     *
     * The labels "L" and "R" indicate pointL and pointR respectively.
     * If a rule's direction is FORWARD, then check from bottom to top.
     *
     *      * Check the top left corner if the 1x1 lambda square on the bottom
     *        left corner (1) of pointL matches the design rule's "corner" mask.
     *
     *      * Check the top right corner if the rule says check BOTHCORNERS and
     *        the 1x1 lambda square on the bottom right (2) corner of pointR
     *	      matches the design rule's "corner" mask.
     *
     * If a rule's direction is REVERSE, then check from top to bottom.
     *
     *	    * Check the bottom right corner if the 1x1 lambda square on the top
     *	      right corner (3) of pointR matches the design rule's "corner"
     *	      mask.
     *
     *	    * Check the bottom left corner if the rule says check BOTHCORNERS
     *	      and the 1x1 lambda square on the top left corner (4) of pointL
     *	      matches the design rule's "corner" mask.
     */

    if (BOTTOM(tile) >= rect->r_ybot)
    {
	Tile *tpbot;
	int edgeLeft, edgeRight;
        int left = MAX(LEFT(tile), rect->r_xbot);
        int right = MIN(RIGHT(tile), rect->r_xtop);
	int edgeY = BOTTOM(tile);

	/* Go right across bottom of tile */
        for (tpbot = LB(tile); LEFT(tpbot) < right; tpbot = TR(tpbot))
        {

	    /* Don't check synthetic edges, i.e. edges with same type on
             * both sides.  Such "edges" have no physical significance, and
	     * depend on internal-details of how paint is spit into tiles.
	     * Thus checking them just leads to confusion.  (When edge rules
	     * involving such edges are encountered during technology readin
	     * the user is warned that such edges are not checked).
	     */
	    if(TiGetTopType(tpbot) == TiGetBottomType(tile))
	        continue;

	    /*
	     * Check to insure that we are inside the clip area.
	     * Go through list of design rules triggered by the
	     * bottom-to-top edge.
	     */
	    edgeLeft = MAX(LEFT(tpbot), left);
	    edgeRight = MIN(RIGHT(tpbot), right);
	    if (edgeLeft >= edgeRight)
		continue;

	    cptr = drcCifCur;
	    {
		DRCstatRules++;
		errRect.r_xbot = edgeLeft;
		errRect.r_xtop = edgeRight;

		/* top to bottom */
		if (cptr->drcc_flags & DRC_REVERSE)
		{
		    /*
		     * Determine corner extensions.
		     * Find the point (3) to the top right of pointR
		     */
		    if (RIGHT(tp = tile) <= errRect.r_xtop)
			for (tp = TR(tp); BOTTOM(tp) > edgeY; tp = LB(tp))
			    /* Nothing */;
		    if (TTMaskHasType(&cptr->drcc_corner, TiGetLeftType(tp)))
		    {
			errRect.r_xtop += cptr->drcc_cdist;
			if (DRCEuclidean)
			    arg->dCD_radial |= RADIAL_SE;
		    }

		    if (cptr->drcc_flags & DRC_BOTHCORNERS)
		    {
			/*
			 * Check the other corner by finding the
			 * point (4) to the top left of pointL.
			 */
			for (tp = tile; LEFT(tp) >= errRect.r_xbot; tp = BL(tp))
			    /* Nothing */;
			if (TTMaskHasType(&cptr->drcc_corner, TiGetRightType(tp)))
			{
			    errRect.r_xbot -= cptr->drcc_cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= RADIAL_SW;
			}
		    }

		    /*
		     * Just for grins, see if we could avoid
		     * a messy search by looking only at tpbot.
		     */
		    errRect.r_ybot = edgeY - cptr->drcc_dist;
		    if (BOTTOM(tpbot) <= errRect.r_ybot
			&& LEFT(tpbot) <= errRect.r_xbot
			&& RIGHT(tpbot) >= errRect.r_xtop
			&& arg->dCD_plane == cptr->drcc_plane
			&& TTMaskHasType(&cptr->drcc_mask, TiGetTopType(tpbot)))
			    continue;
		    errRect.r_ytop = edgeY;
		    arg->dCD_initial = tile;
		}
		else	/* FORWARD */
		{
		    /*
		     * Determine corner extensions.
		     * Find the point (1) to the bottom left of pointL
		     */
		    if (LEFT(tp = tpbot) >= errRect.r_xbot)
			for (tp = BL(tp); TOP(tp) < edgeY; tp = RT(tp))
			    /* Nothing */;

		    if (TTMaskHasType(&cptr->drcc_corner, TiGetRightType(tp)))
		    {
			errRect.r_xbot -= cptr->drcc_cdist;
			if (DRCEuclidean)
			    arg->dCD_radial |= RADIAL_NW;
		    }

		    if (cptr->drcc_flags & DRC_BOTHCORNERS)
		    {
			/*
			 * Check the other corner by finding the
			 * point (2) to the bottom right of pointR.
			 */
			for (tp=tpbot; RIGHT(tp) <= errRect.r_xtop; tp=TR(tp))
			    /* Nothing */;
			if (TTMaskHasType(&cptr->drcc_corner, TiGetLeftType(tp)))
			{
			    errRect.r_xtop += cptr->drcc_cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= RADIAL_NE;
			}
		    }

		    /*
		     * Just for grins, see if we could avoid
		     * a messy search by looking only at tile.
		     */
		    errRect.r_ytop = edgeY + cptr->drcc_dist;
		    if (TOP(tile) >= errRect.r_ytop
			&& LEFT(tile) <= errRect.r_xbot
			&& RIGHT(tile) >= errRect.r_xtop
			&& arg->dCD_plane == cptr->drcc_plane
			&& TTMaskHasType(&cptr->drcc_mask, TiGetType(tile)))
			    continue;
		    errRect.r_ybot = edgeY;
		    arg->dCD_initial = tpbot;
		}
		if (arg->dCD_radial)
		{
		    arg->dCD_radial &= 0xf000;
		    arg->dCD_radial |= (0xfff & cptr->drcc_cdist);
		}

		DRCstatSlow++;
		arg->dCD_cptr = (DRCCookie *)cptr;
		TTMaskCom2(&tmpMask, &cptr->drcc_mask);
		(void) DBSrPaintArea((Tile *) NULL,
		    CIFPlanes[cptr->drcc_plane],
		    &errRect, &tmpMask, areaCifCheck, (ClientData) arg);
	    }
	    DRCstatEdges++;
        }
    }
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * areaCifCheck --
 *
 * Call the function passed down from DRCBasicCheck() if the current tile
 * violates the rule in the given DRCCookie.  If the rule's connectivity
 * flag is set, then make sure the violating material isn't connected
 * to what's on the initial side of the edge before calling the client
 * error function.
 *
 * This function is called from DBSrPaintArea().
 *
 * Results:
 *	Zero (so that the search will continue).
 *
 * Side effects:
 *      Applies the function passed as an argument.
 *
 * ----------------------------------------------------------------------------
 */

int
areaCifCheck(
    Tile *tile,
    TileType dinfo,
    struct drcClientData *arg)
{
    Rect rect;		/* Area where error is to be recorded. */
    Rect cifrect;	/* rect, in CIF coordinates */
    int			scale = drcCifStyle->cs_scaleFactor;

    /* If the tile has a legal type, then return. */

    if (TTMaskHasType(&arg->dCD_cptr->drcc_mask, TiGetType(tile))) return 0;

    /* Only consider the portion of the suspicious tile that overlaps
     * the clip area for errors.
     */

    TiToRect(tile, &cifrect);
    GeoClip(&cifrect, arg->dCD_constraint);
    if ((cifrect.r_xbot >= cifrect.r_xtop) || (cifrect.r_ybot >= cifrect.r_ytop))
	return 0;
    rect = cifrect;
    rect.r_xbot /= scale;
    rect.r_xtop /= scale;
    if (rect.r_xbot == rect.r_xtop)
    {
    	 if (rect.r_xbot < 0) rect.r_xbot--; else rect.r_xtop++;
    }
    rect.r_ybot /= scale;
    rect.r_ytop /= scale;
    if (rect.r_ybot == rect.r_ytop)
    {
    	 if (rect.r_ybot < 0) rect.r_ybot--; else rect.r_ytop++;
    }
    GeoClip(&rect, arg->dCD_clip);
    if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
	return 0;

    /*
     * Euclidean distance checks
     */
    if (arg->dCD_radial != 0)
    {
	unsigned int i;
	int sqx, sqy;
	int sdist = arg->dCD_radial & 0xfff;
	long sstest, ssdist = (long) sdist * sdist;

	if ((arg->dCD_radial & RADIAL_NW) != 0)
	{
	    if (((sqx = arg->dCD_constraint->r_xbot + sdist
			- cifrect.r_xtop) >= 0) && ((sqy = cifrect.r_ybot
			- arg->dCD_constraint->r_ytop + sdist) >= 0)
			&& ((sqx * sqx + sqy * sqy) >= ssdist))
		return 0;
	    else if (IsSplit(tile) && !SplitDirection(tile) && !(dinfo & TT_SIDE))
	    {
		sstest = drcCifPointToSegment(arg->dCD_constraint->r_xbot + sdist,
			arg->dCD_constraint->r_ytop - sdist,
			LEFT(tile), BOTTOM(tile), RIGHT(tile), TOP(tile));
		if (sstest > ssdist) return 0;
	    }
	}
	if ((arg->dCD_radial & RADIAL_NE) != 0)
	{
	    if (((sqx = cifrect.r_xbot - arg->dCD_constraint->r_xtop
			+ sdist) >= 0) && ((sqy = cifrect.r_ybot
			- arg->dCD_constraint->r_ytop + sdist) >= 0)
			&& ((sqx * sqx + sqy * sqy) >= ssdist))
		return 0;
	    else if (IsSplit(tile) && SplitDirection(tile) && (dinfo & TT_SIDE))
	    {
		sstest = drcCifPointToSegment(arg->dCD_constraint->r_xtop - sdist,
			arg->dCD_constraint->r_ytop - sdist,
			LEFT(tile), TOP(tile), RIGHT(tile), BOTTOM(tile));
		if (sstest > ssdist) return 0;
	    }
	}
	if ((arg->dCD_radial & RADIAL_SW) != 0)
	{
	    if (((sqx = arg->dCD_constraint->r_xbot + sdist
			- cifrect.r_xtop) >= 0) &&
			((sqy = arg->dCD_constraint->r_ybot
			+ sdist - cifrect.r_ytop) >= 0)
			&& ((sqx * sqx + sqy * sqy) >= ssdist))
		return 0;
	    else if (IsSplit(tile) && SplitDirection(tile) && !(dinfo & TT_SIDE))
	    {
		sstest = drcCifPointToSegment(arg->dCD_constraint->r_xbot + sdist,
			arg->dCD_constraint->r_ybot + sdist,
			LEFT(tile), TOP(tile), RIGHT(tile), BOTTOM(tile));
		if (sstest > ssdist) return 0;
	    }
	}
	if ((arg->dCD_radial & RADIAL_SE) != 0)
	{
	    if (((sqx = cifrect.r_xbot - arg->dCD_constraint->r_xtop
			+ sdist) >= 0) &&
			((sqy = arg->dCD_constraint->r_ybot
			+ sdist - cifrect.r_ytop) >= 0)
			&& ((sqx * sqx + sqy * sqy) >= ssdist))
		return 0;
	    else if (IsSplit(tile) && !SplitDirection(tile) && (dinfo & TT_SIDE))
	    {
		sstest = drcCifPointToSegment(arg->dCD_constraint->r_xtop - sdist,
			arg->dCD_constraint->r_ybot + sdist,
			LEFT(tile), BOTTOM(tile), RIGHT(tile), TOP(tile));
		if (sstest > ssdist) return 0;
	    }
	}
    }

    (*(arg->dCD_function)) (arg->dCD_celldef, &rect,
	arg->dCD_cptr, arg->dCD_clientData);
    (*(arg->dCD_errors))++;
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcCifArea --
 *
 * Process an area rule.
 * This is of the form:
 *
 *	cifarea layers distance why
 *
 * e.g,
 *
 *	cifarea VIA 4 "via area must be at least 4"
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcCifArea(
    int argc,
    char *argv[])
{
    char *layers = argv[1];
    int centiarea = atoi(argv[2]);
    int	centihorizon = atoi(argv[3]);
    int why = drcWhyCreate(argv[4]);
    TileTypeBitMask set, setC, tmp1;
    DRCCookie *dpnext, *dpnew;
    TileType i, j;
    int plane;
    int	thislayer = -1;
    int scalefactor;

    if (drcCifStyle == NULL)
	return drcCifWarning();

    for (i = 0; i < drcCifStyle->cs_nLayers;i++)
    {
    	 CIFLayer	*layer = drcCifStyle->cs_layers[i];

	 if (strcmp(layer->cl_name,layers) == 0)
	 {
	      thislayer = i;
	      break;
	 }
    }
    if (thislayer == -1)
    {
    	 TechError("Unknown cif layer: %s\n",layers);
         return (0);
    }

    scalefactor = drcCifStyle->cs_scaleFactor;
    // centiarea *= (drcCifStyle->cs_expander * drcCifStyle->cs_expander);
    dpnext = drcCifRules[thislayer][DRC_CIF_SPACE];
    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
    drcCifAssign(dpnew, centihorizon, dpnext, &CIFSolidBits, &CIFSolidBits,
		why, centiarea, DRC_AREA | DRC_FORWARD | DRC_CIFRULE, thislayer, 0);
    drcCifRules[thislayer][DRC_CIF_SPACE] = dpnew;


    return ((centihorizon+scalefactor-1)/scalefactor);
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcCifMaxwidth --  cif version of drc list.
 *
 * Results:
 *	Returns distance.
 *
 * Side effects:
 *	Updates the DRC technology variables.
 *
 * ----------------------------------------------------------------------------
 */

int
drcCifMaxwidth(
    int argc,
    char *argv[])
{
    char *layers = argv[1];
    int centidistance = atoi(argv[2]);
    char *bends = argv[3];
    int why = drcWhyCreate(argv[4]);
    TileTypeBitMask set, setC, tmp1;
    DRCCookie *dpnext, *dpnew;
    TileType i, j;
    int plane;
    int bend;
    int thislayer = -1;
    int scalefactor;

    if (drcCifStyle == NULL)
	return drcCifWarning();

    for (i = 0; i < drcCifStyle->cs_nLayers;i++)
    {
    	 CIFLayer	*layer = drcCifStyle->cs_layers[i];

	 if (strcmp(layer->cl_name,layers) == 0)
	 {
	      thislayer = i;
	      break;
	 }
    }
    if (thislayer == -1)
    {
    	 TechError("Unknown cif layer: %s\n",layers);
         return (0);
    }

    if (strcmp(bends, "bend_illegal") == 0) bend = 0;
    else if (strcmp(bends, "bend_ok") == 0) bend = DRC_BENDS;
    else if (strcmp(bends, "both") == 0) bend = DRC_MAXWIDTH_BOTH;
    else
    {
    	 TechError("unknown bend option %s\n", bends);
	 return (0);
    }

    scalefactor = drcCifStyle->cs_scaleFactor;
    // centidistance *= drcCifStyle->cs_expander;		// BSI
    dpnext = drcCifRules[thislayer][DRC_CIF_SPACE];
    dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
    drcCifAssign(dpnew, centidistance, dpnext, &CIFSolidBits, &CIFSolidBits,
		why, centidistance, DRC_MAXWIDTH | DRC_CIFRULE | bend, thislayer, 0);
    drcCifRules[thislayer][DRC_CIF_SPACE] = dpnew;

    return ((centidistance+scalefactor-1)/scalefactor);
}

/*
 *-------------------------------------------------------------------------
 *
 * drcCifCheckArea--
 *
 *	checks to see that a collection of cif tiles
 *	have more than a minimum area.
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	May cause errors to be painted.
 *
 *-------------------------------------------------------------------------
 */

void
drcCheckCifArea(
    Tile *starttile,
    struct drcClientData *arg,
    DRCCookie *cptr)
{
     int		arealimit = cptr->drcc_cdist;
     long		area = 0L;
     TileTypeBitMask	*oktypes = &cptr->drcc_mask;
     Tile		*tile,*tp;
     Rect		*cliprect = arg->dCD_rect;
     int 		scale = drcCifStyle->cs_scaleFactor;

    arg->dCD_cptr = (DRCCookie *)cptr;
    if (DRCstack == (Stack *) NULL)
	DRCstack = StackNew(64);

    /* Mark this tile as pending and push it */
    PUSHTILE(starttile);

    while (!StackEmpty(DRCstack))
    {
	tile = (Tile *) STACKPOP(DRCstack);
	if (TiGetClientINT(tile) != DRC_PENDING) continue;
	area += (long)(RIGHT(tile)-LEFT(tile))*(TOP(tile)-BOTTOM(tile));
	TiSetClientINT(tile, DRC_PROCESSED);
	/* are we at the clip boundary? If so, skip to the end */
	if (RIGHT(tile) == cliprect->r_xtop ||
	    LEFT(tile) == cliprect->r_xbot ||
	    BOTTOM(tile) == cliprect->r_ybot ||
	    TOP(tile) == cliprect->r_ytop) goto forgetit;

         if (area >= (long)arealimit) goto forgetit;

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp)))	PUSHTILE(tp);

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);
     }
     if (area < (long)arealimit)
     {
	 Rect	rect;
	 TiToRect(starttile,&rect);
         rect.r_xbot /= scale;
         rect.r_xtop /= scale;
         rect.r_ybot /= scale;
         rect.r_ytop /= scale;
	 GeoClip(&rect, arg->dCD_clip);
	 if (!GEO_RECTNULL(&rect)) {
	     (*(arg->dCD_function)) (arg->dCD_celldef, &rect,
		 arg->dCD_cptr, arg->dCD_clientData);
	     (*(arg->dCD_errors))++;
	 }

     }
forgetit:
     /* reset the tiles */
     TiSetClient(starttile, DRC_UNPROCESSED);
     STACKPUSH(starttile, DRCstack);
     while (!StackEmpty(DRCstack))
     {
	tile = (Tile *) STACKPOP(DRCstack);

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp,DRCstack);
	    }

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp,DRCstack);
	    }

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp,DRCstack);
	    }

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp,DRCstack);
	    }

     }
}

/*
 *-------------------------------------------------------------------------
 *
 * drcCheckCifMaxwidth --
 *
 *	Checks to see that at least one dimension of a region
 *	does not exceed some amount.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	May cause errors to be painted.
 *
 *-------------------------------------------------------------------------
 */

void
drcCheckCifMaxwidth(
    Tile *starttile,
    struct drcClientData *arg,
    DRCCookie *cptr)
{
    int			edgelimit = cptr->drcc_dist;
    Rect		boundrect;
    TileTypeBitMask	*oktypes = &cptr->drcc_mask;
    Tile		*tile, *tp;
    int 		scale = drcCifStyle->cs_scaleFactor;

    arg->dCD_cptr = (DRCCookie *)cptr;
    if (DRCstack == (Stack *) NULL)
	DRCstack = StackNew(64);

    /* If bends are allowed, just check on a tile-by-tile basis that
     * both dimensions exceed the max.  This is pretty stupid, but it
     * correctly calculates, e.g., the trench width rule. dcs 12.06.89
     *
     * Do the same for the "both" case, checking on a tile-by-tile basis
     * that either dimension exceeds the max.
     */

    if (cptr->drcc_flags & DRC_MAXWIDTH_BOTH)
    {
	Rect	rect;
	TiToRect(starttile, &rect);
	if ((rect.r_xtop-rect.r_xbot > edgelimit) ||
	    (rect.r_ytop-rect.r_ybot > edgelimit))
	{
	    rect.r_xbot /= scale;
	    rect.r_xtop /= scale;
	    rect.r_ybot /= scale;
	    rect.r_ytop /= scale;
	    GeoClip(&rect, arg->dCD_clip);
	    if (!GEO_RECTNULL(&rect))
	    {
		(*(arg->dCD_function)) (arg->dCD_celldef, &rect,
			arg->dCD_cptr, arg->dCD_clientData);
		(*(arg->dCD_errors))++;
	    }
	}
	return;
    }
    else if (cptr->drcc_flags & DRC_BENDS)
    {
	Rect	rect;

	/* Ignore split tiles;  this is a limitation but another
	 * algorithm is needed to check for maximum width in a
	 * non-Manhattan area.
	 */
	if (IsSplit(starttile)) return;

	TiToRect(starttile, &rect);

	if (rect.r_xtop-rect.r_xbot > edgelimit &&
		rect.r_ytop-rect.r_ybot > edgelimit)
	{
	    rect.r_xbot /= scale;
	    rect.r_xtop /= scale;
	    rect.r_ybot /= scale;
	    rect.r_ytop /= scale;
	    GeoClip(&rect, arg->dCD_clip);
	    if (!GEO_RECTNULL(&rect))
	    {
		(*(arg->dCD_function)) (arg->dCD_celldef, &rect,
			arg->dCD_cptr, arg->dCD_clientData);
		(*(arg->dCD_errors))++;
	    }
	}
	return;
    }

    /* Mark this tile as pending and push it */

    PUSHTILE(starttile);
    TiToRect(starttile,&boundrect);

    while (!StackEmpty(DRCstack))
    {
	tile = (Tile *) STACKPOP(DRCstack);
	if (TiGetClientINT(tile) != DRC_PENDING) continue;

	if (boundrect.r_xbot > LEFT(tile)) boundrect.r_xbot = LEFT(tile);
	if (boundrect.r_xtop < RIGHT(tile)) boundrect.r_xtop = RIGHT(tile);
	if (boundrect.r_ybot > BOTTOM(tile)) boundrect.r_ybot = BOTTOM(tile);
	if (boundrect.r_ytop < TOP(tile)) boundrect.r_ytop = TOP(tile);
	TiSetClientINT(tile, DRC_PROCESSED);

        if (boundrect.r_xtop - boundrect.r_xbot > edgelimit &&
		boundrect.r_ytop - boundrect.r_ybot > edgelimit) break;

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp)))PUSHTILE(tp);

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);
    }

    if (boundrect.r_xtop - boundrect.r_xbot > edgelimit &&
		boundrect.r_ytop - boundrect.r_ybot > edgelimit)
    {
	Rect	rect;
	TiToRect(starttile, &rect);
	{
	    rect.r_xbot /= scale;
	    rect.r_xtop /= scale;
	    rect.r_ybot /= scale;
	    rect.r_ytop /= scale;
	    GeoClip(&rect, arg->dCD_clip);
	    if (!GEO_RECTNULL(&rect))
	    {
		(*(arg->dCD_function)) (arg->dCD_celldef, &rect,
			arg->dCD_cptr, arg->dCD_clientData);
		(*(arg->dCD_errors))++;
	    }
	}
    }
    /* reset the tiles */
    TiSetClient(starttile, DRC_UNPROCESSED);
    STACKPUSH(starttile, DRCstack);
    while (!StackEmpty(DRCstack))
    {
	tile = (Tile *)STACKPOP(DRCstack);

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp, DRCstack);
	    }

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp, DRCstack);
	    }

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp, DRCstack);
	    }

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (TiGetClient(tp) != DRC_UNPROCESSED)
	    {
		TiSetClient(tp, DRC_UNPROCESSED);
		STACKPUSH(tp, DRCstack);
	    }
    }
}

