/*
 * DRCbasic.c --
 *
 * This file provides routines that make perform basic design-rule
 * checking:  given an area of a cell definition, this file will
 * find all of the rule violations and call a client procedure for
 * each one.
 *
 *     *********************************************************************
 *     * 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-8.0/drc/DRCbasic.c,v 1.7 2010/09/20 21:13:22 tim Exp $";
#endif	/* not lint */

#include <sys/types.h>
#include <stdio.h>
#include <string.h>		// for memcpy()
#include <math.h>		// for sqrt() for diagonal check

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "drc/drc.h"
#include "utils/signals.h"
#include "utils/maxrect.h"
#include "utils/malloc.h"
#include "utils/undo.h"
#include "textio/textio.h"
#include "cif/CIFint.h"

int dbDRCDebug = 0;

/* The following DRC cookie is used when there are tiles of type
 * TT_ERROR_S found during the basic DRC.  These arise during
 * hierarchical checking when there are illegal overlaps.
 */

static DRCCookie drcOverlapCookie = {
    0, 0, 0, 0,
    { {0} }, { {0} },
    0, DRC_EXCEPTION_NONE, 0, 0,
    DRC_OVERLAP_TAG,
    (DRCCookie *) NULL
};

/* Forward references: */

extern int areaCheck();
extern int drcTile();
extern MaxRectsData *drcCanonicalMaxwidth();

/*
 *-----------------------------------------------------------------------
 *
 * drcFoundOneFunc --
 *
 *	Simple callback for a plane search on a mask-hint plane inside
 *	a DRC check area.
 *
 * Results:
 *	Return 1 always, indicating that a tile has been found in the
 *	DRC search area, and the search can end.
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */

int
drcFoundOneFunc(Tile *tile,
    TileType dinfo,
    ClientData cdata)
{
    return 1;
}

/*
 *-----------------------------------------------------------------------
 *
 * drcCifPointToSegment --
 *
 *	Euclidean-distance point-to-segment distance (squared)
 *	calculation (borrowed from XCircuit)
 *
 * Results:
 *	Squared Euclidean distance of the closest approach of the
 *	line segment to the point (long result).
 *
 * Side Effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */

long
drcCifPointToSegment(px, py, s1x, s1y, s2x, s2y)
    int px, py;		/* The position of the point */
    int s1x, s1y;	/* One endpoint of the line segment */
    int s2x, s2y;	/* The other endpoint of the line segment */
{
    long x, y;
    long a, b, c, frac;
    float protod;

    x = (long)s2x - (long)s1x;
    y = (long)s2y - (long)s1y;
    c = (x * x + y * y);

    x = (long)px - (long)s1x;
    y = (long)py - (long)s1y;
    a = (x * x + y * y);

    x = (long)px - (long)s2x;
    y = (long)py - (long)s2y;
    b = (x * x + y * y);

    frac = a - b;
    if (frac >= c) return b;
    else if (-frac >= c) return a;
    else
    {
	protod = (float)(c + a - b);
	return (a - (long)((protod * protod) / (float)(c << 2)));
    }
}

/* Define Euclidean distance checks */

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


/*
 * ----------------------------------------------------------------------------
 *
 * areaCheck --
 *
 * 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
areaCheck(tile, dinfo, arg)
    Tile *tile;
    TileType dinfo;
    struct drcClientData *arg;
{
    Rect rect;		/* Area where error is to be recorded. */

    TiToRect(tile, &rect);

    /* Only consider the portion of the suspicious tile that overlaps
     * the clip area for errors, unless this is a trigger rule, in
     * which case it should be restricted only to the full check area.
     */

    if (!(arg->dCD_cptr->drcc_flags & DRC_TRIGGER))
	GeoClip(&rect, arg->dCD_clip);
    else
	GeoClip(&rect, arg->dCD_rect);

    GeoClip(&rect, arg->dCD_constraint);
    if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
	return 0;

    /* Run-length rules are ignored unless the width of the error
     * exceeds the run-length distance (which is held in the
     * drcc_cdist entry for the rule).
     */
    if (arg->dCD_cptr->drcc_flags & DRC_RUNLENGTH)
	if (((rect.r_xtop - rect.r_xbot) < arg->dCD_cptr->drcc_cdist) &&
		((rect.r_ytop - rect.r_ybot) < arg->dCD_cptr->drcc_cdist))
	    return 0;

    /*
     * When Euclidean distance checks are enabled, check for error tiles
     * outside of the perimeter of the circle in the corner extension area
     * that extends "sdist" from the corner of the edge.
     *
     * Also check the relatively rare case where the tile is inside the
     * circle perimeter, but only the corner of the triangle projects into
     * the error check rectangle, and is outside of the circle.
     */

    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
			 - rect.r_xtop) >= 0) && ((sqy = rect.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 = rect.r_xbot - arg->dCD_constraint->r_xtop
			+ sdist) >= 0) && ((sqy = rect.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
			- rect.r_xtop) >= 0) &&
			((sqy = arg->dCD_constraint->r_ybot
			+ sdist - rect.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 = rect.r_xbot - arg->dCD_constraint->r_xtop
			+ sdist) >= 0) &&
			((sqy = arg->dCD_constraint->r_ybot
			+ sdist - rect.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;
	    }
	}
    }

    if (arg->dCD_cptr->drcc_flags & DRC_TRIGGER)
    {
	Rect *newrlist;
	int entries = arg->dCD_entries;

	/* The following code allows the rect list to be expanded by	*/
	/* multiples of 8, when necessary.				*/

	arg->dCD_entries++;
	if (arg->dCD_rlist == NULL)
	    arg->dCD_rlist = (Rect *)mallocMagic(8 * sizeof(Rect));
	else if ((arg->dCD_entries & ~(entries | 7)) == arg->dCD_entries)
	{
	    newrlist = (Rect *)mallocMagic((arg->dCD_entries << 1) * sizeof(Rect));
	    memcpy((void *)newrlist, (void *)arg->dCD_rlist, (size_t)entries *
			sizeof(Rect));
	    freeMagic(arg->dCD_rlist);
	    arg->dCD_rlist = newrlist;
	}
	arg->dCD_rlist[arg->dCD_entries - 1] = rect;
    }
    /* "angles 45-only" needs to reject errors that are inside split tiles */
    else if (!IsSplit(tile) ||
		((arg->dCD_cptr->drcc_flags & (DRC_ANGLES_45 | DRC_ANGLES_90))
		!= DRC_ANGLES_45))
    {
	(*(arg->dCD_function))(arg->dCD_celldef, &rect, arg->dCD_cptr,
			arg->dCD_clientData);
	(*(arg->dCD_errors))++;
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * areaNMReject ---
 *
 *	Trivial callback function used by areaNMCheck to see if a tile
 *	found in the error area of a reverse non-manhattan check exists
 *	only on the other side of the original check boundary.  If it
 *	is found in this search, return 1 to immediately stop the search.
 *
 * Results:
 *	Returns 1 if the tile indicated in the ClientData argument was
 *	found in the check area, otherwise return 0 to keep looking.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
areaNMReject(tile, dinfo, arg)
    Tile *tile;
    TileType dinfo;
    ClientData *arg;
{
    Tile *checktile = (Tile *)arg;

    if (tile == checktile)
	return 1;

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * areaNMCheck ---
 *
 *	Check for errors in triangular area of a tile.
 *
 * Results:
 *	Return 0 always to keep the search going.
 *
 * Side effects:
 *	If the tile is not rejected due to being outside of the various
 *	clip areas, then call the function specified in the drcClientData
 *	argument.	
 *
 * ----------------------------------------------------------------------------
 */

int
areaNMCheck(tile, dinfo, arg)
    Tile *tile;
    TileType dinfo;
    struct drcClientData *arg;
{
    Rect rect;		/* Area where error is to be recorded. */

    /* Ignore the tile that initiates the check, because the error area	*/
    /* of a non-Manhattan check may fall inside of it.			*/
    if (tile == arg->dCD_initial) return 0;

    TiToRect(tile, &rect);

    /* Only consider the portion of the suspicious tile that overlaps
     * the clip area for errors, unless this is a trigger rule.
     */

    if (!(arg->dCD_cptr->drcc_flags & DRC_TRIGGER))
	GeoClip(&rect, arg->dCD_clip);
    else
	GeoClip(&rect, arg->dCD_rect);

    GeoClip(&rect, arg->dCD_constraint);
    if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
	return 0;

    if (arg->dCD_entries & TT_DIAGONAL)
    {
	TileTypeBitMask mask;
	int dinfo = arg->dCD_entries;

	/* In the DRC_REVERSE case, the area being searched extends
	 * behind the edge that triggered the DRC check, but any
	 * tile that is outside that edge should be ignored.  This
	 * requires a separate check.
	 */

	TTMaskSetOnlyType(&mask, TiGetLeftType(tile));
	TTMaskSetType(&mask, TiGetRightType(tile));
	if (DBSrPaintNMArea((Tile *)tile, (Plane *)NULL,
		TiGetTypeExact(tile) | dinfo, arg->dCD_rlist,
		&mask, areaNMReject, (ClientData)tile) == 0)
	    return 0;
    }

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

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * DRCBasicCheck --
 *
 * This is the top-level routine for basic design-rule checking.
 *
 * Results:
 *	Number of errors found.
 *
 * Side effects:
 *	Calls function for each design-rule violation in celldef
 *	that is triggered by an edge in rect and whose violation
 *	area falls within clipRect.  This routine makes a flat check:
 *	it considers only information in the paint planes of celldef,
 *	and does not expand children.  Function should have the form:
 *	void
 *	function(def, area, rule, cdarg)
 *	    CellDef *def;
 *	    Rect *area;
 *	    DRCCookie *rule;
 *	    ClientData cdarg;
 *	{
 *	}
 *
 *	In the call to function, def is the definition containing the
 *	basic area being checked, area is the actual area where a
 *	rule is violated, rule is the rule being violated, and cdarg
 *	is the client data passed through all of our routines.
 *
 * Note:
 *	If an interrupt occurs (SigInterruptPending gets set), then
 *	the basic will be aborted immediately.  This means the check
 *	may be incomplete.
 *
 * ----------------------------------------------------------------------------
 */

int
DRCBasicCheck (celldef, checkRect, clipRect, function, cdata)
    CellDef *celldef;	/* CellDef being checked */
    Rect *checkRect;	/* Check rules in this area -- usually two Haloes
			 * larger than the area where changes were made.
			 */
    Rect *clipRect;	/* Clip error tiles against this area. */
    void (*function)();	/* Function to apply for each error found. */
    ClientData cdata;	/* Passed to function as argument. */
{
    struct drcClientData arg;
    int	errors;
    int planeNum;

    if (DRCCurStyle == NULL) return 0;	/* No DRC, no errors */

    /*  Insist on top quality rectangles. */

    if ((checkRect->r_xbot >= checkRect->r_xtop)
	    || (checkRect->r_ybot >= checkRect->r_ytop))
	 return (0);

    errors = 0;

    arg.dCD_celldef = celldef;
    arg.dCD_rect = checkRect;
    arg.dCD_errors = &errors;
    arg.dCD_function = function;
    arg.dCD_clip = clipRect;
    arg.dCD_clientData = cdata;
    arg.dCD_rlist = NULL;
    arg.dCD_entries = 0;

    for (planeNum = PL_TECHDEPBASE; planeNum < DBNumPlanes; planeNum++)
    {
        arg.dCD_plane = planeNum;
	DBResetTilePlane(celldef->cd_planes[planeNum], DRC_UNPROCESSED);
        (void) DBSrPaintArea ((Tile *) NULL, celldef->cd_planes[planeNum],
		checkRect, &DBAllTypeBits, drcTile, (ClientData) &arg);

#ifdef MAGIC_WRAPPER
	/* Execute pending Tcl events, so the DRC process doesn't block.    */

	/* WARNING:  This code cannot be enabled until some method is
	 * worked out to determine if any event resulted in a change
	 * to the DRC check plane which would invalidate the current
	 * search.  If so, the search must end immediately and the
	 * area being checked must be reinstated.  The code was added
	 * to see how it speeds up the response time of magic when
	 * some of the DRC rules are compute-intensive.  It speeds up
	 * performance enough that it is worthwhile to implement the
	 * method just mentioned.
	 */
	#if 0
	UndoEnable();
	while (Tcl_DoOneEvent(TCL_DONT_WAIT));
	UndoDisable();
	#endif
#endif
    }
    drcCifCheck(&arg);
    if (arg.dCD_rlist != NULL) freeMagic(arg.dCD_rlist);
    return (errors);
}

/* Expect that keeping around 3 MaxRectsData records should be sufficient
 * to avoid recomputing drcCanonicalMaxwidth() multiple times.  Note that
 * if a PDK sets up multiple rules on an edge which all require running
 * drcCanonicalMaxwidth(), then this cache size may need to be revisited.
 */
#define MAXRECTSCACHE 3

/*
 * ----------------------------------------------------------------------------
 *
 * drcTile --
 *
 * This is a search function invoked once for each tile in
 * the area to be checked.  It checks design rules along the left
 * and bottom of the given tile.  If the tile extends beyond the
 * clipping rectangle in any direction, then the boundary on that
 * side of the tile will be skipped.
 *
 * 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
drcTile (tile, dinfo, arg)
    Tile *tile;			/* Tile being examined */
    TileType dinfo;		/* Split tile information */
    struct drcClientData *arg;
{
    DRCCookie *cptr;	/* Current design rule on list */
    Rect *rect = arg->dCD_rect;	/* Area being checked */
    Rect errRect;		/* Area checked for an individual rule */
    MaxRectsData *mrd;		/* Used by widespacing rule */
    TileTypeBitMask tmpMask, *rMask;
    bool trigpending;		/* Hack for widespacing rule */
    bool firsttile;
    int triggered;
    int cdist, dist, ccdist, result;

    /* Keep up to three MaxRectsData records to avoid doing the	same
     * expensive computation more than once.
     *
     * mrdcache[0] will be used for the tpleft tile, since it will never
     * be reused.  mrdcache[1] and mrdcache[2] will be used for the tile
     * itself.  Note that if more than 2 DRCCookie entries for the same
     * edge require drcCanonicalMaxwidth(), then mrdcache[2] will be
     * re-used so that at least mrdcache[1] is always a cache hit.
     */

    static MaxRectsData *mrdcache[MAXRECTSCACHE] = {NULL, NULL, NULL};
    DRCCookie *cptrcache;

    arg->dCD_constraint = &errRect;

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

    /* If this tile is an error tile, it arose because of an illegal
     * overlap between things in adjacent cells.  This means that
     * there's an automatic violation over the area of the tile.
     */

    if (TiGetType(tile) == TT_ERROR_S)
    {
	TiToRect(tile, &errRect);
	GeoClip(&errRect, rect);
        (*(arg->dCD_function)) (arg->dCD_celldef, &errRect,
	    &drcOverlapCookie, arg->dCD_clientData);
        (*(arg->dCD_errors))++;
    }

    /* Diagonally split tiles are processed twice.  Just like the   */
    /* DRC searches only one direction on regular tiles, the split  */
    /* tiles are only processed for one of the two cases.	    */

    if (IsSplit(tile) && !(dinfo & TT_SIDE))
    {
	int deltax, deltay;
	TileType tt, to;

	tt = TiGetLeftType(tile);	/* inside type */
	to = TiGetRightType(tile);	/* outside type */

	/* Check rules for DRC_ANGLES_90 rule and process */
	for (cptr = DRCCurStyle->DRCRulesTbl[to][tt];
			cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next)
	    if (cptr->drcc_flags & DRC_ANGLES_90)
	    {
		drcCheckAngles(tile, arg, cptr);
		break;
	    }

	for (cptr = DRCCurStyle->DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
			cptr = cptr->drcc_next)
	{
	    int deltax, deltay, w, h;
	    double r;
	    TileType newdinfo, dsplit;

	    /* Work to be done:  Handle triggering rules for non-Manhattan  */
	    /* edges;  especially important for the wide-spacing rule.	    */

	    if (cptr->drcc_flags & DRC_TRIGGER)
	    {
		cptr = cptr->drcc_next; // Skip both triggering and triggered rules
		continue;
	    }

	    /* Note:  Triggered wide-spacing rule will require handling	*/
	    /* the DRC_MAXWIDTH rule on non-Manhattan edges.		*/

	    if (cptr->drcc_flags & (DRC_ANGLES_90 | DRC_AREA | DRC_MAXWIDTH
		    | DRC_RECTSIZE | DRC_OFFGRID))
		continue;

	    TiToRect(tile, &errRect);

	    /* Find the rule distances according to the scale factor */
	    dist = cptr->drcc_dist;

	    /* drcc_edgeplane is used to avoid checks on edges	*/
	    /* in more than one plane				*/

	    if (arg->dCD_plane != cptr->drcc_edgeplane) continue;

	    DRCstatRules++;

	    DRCstatSlow++;
	    arg->dCD_cptr = cptr;
	    arg->dCD_entries = 0;
	    TTMaskCom2(&tmpMask, &cptr->drcc_mask);
	    TTMaskClearType(&tmpMask, TT_ERROR_S);
	    arg->dCD_initial = tile;

	    /* Compute position that is the rule distance away from */
	    /* the tile's diagonal edge, by Euclidean measure	    */
	    /* (rounded down if fractional).  Use the forward	    */
	    /* position, and negate if reversed.		    */

	    w = RIGHT(tile) - LEFT(tile);
	    h = TOP(tile) - BOTTOM(tile);
	    r = 1.0 / (1.0 + ((double)(w * w) / (double)(h * h)));
	    deltax = (int)((double)cptr->drcc_dist * sqrt(r));
	    deltay = (deltax * w) / h;

	    if (SplitDirection(tile) == 0) deltay = -deltay;

	    newdinfo = TiGetTypeExact(tile) & (TT_DIAGONAL | TT_DIRECTION);
	    if (!(cptr->drcc_flags & DRC_REVERSE))
	    {
		/* Forward case is behind the triangle */
		deltax = -deltax;
		deltay = -deltay;

		/* Split side changes in the reverse case */
		newdinfo |= TT_SIDE;
	    }

	    /* The area to check is bounded between the diagonals of
	     * tile and errRect (which is the tile area, offset).
	     * Pass errRect and newdinfo to areaNMCheck using the
	     * ClientData structure arg->dCD_rlist and arg->dCD_entries,
	     * which are not used by areaNMCheck.
	     */
	    arg->dCD_rlist = (Rect *)mallocMagic(sizeof(Rect));
	    *(arg->dCD_rlist) = errRect;
	    arg->dCD_entries = newdinfo;
	    if (newdinfo & TT_SIDE)
		arg->dCD_entries &= ~TT_SIDE;
	    else
		arg->dCD_entries |= TT_SIDE;

	    /* errRect is the tile area offset by (deltax, deltay) */
	    errRect.r_xbot += deltax;
	    errRect.r_ybot += deltay;
	    errRect.r_xtop += deltax;
	    errRect.r_ytop += deltay;

	    DBSrPaintNMArea((Tile *) NULL,
			arg->dCD_celldef->cd_planes[cptr->drcc_plane], newdinfo,
			&errRect, &tmpMask, areaNMCheck, (ClientData) arg);

	    arg->dCD_entries = 0;
	    freeMagic(arg->dCD_rlist);
	    arg->dCD_rlist = (Rect *)NULL;
	}
	DRCstatEdges++;
    }

    cptrcache = NULL;

    /*
     * 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 (LEFT(tile) >= rect->r_xbot)		/* check tile against rect */
    {
	Tile *tpleft, *tpl, *tpr;
	TileType tt, to;
	int edgeTop, edgeBot;
        int top = MIN(TOP(tile), rect->r_ytop);
        int bottom = MAX(BOTTOM(tile), rect->r_ybot);
	int edgeX = LEFT(tile);

	firsttile = TRUE;
        for (tpleft = BL(tile); BOTTOM(tpleft) < top; tpleft = RT(tpleft))
        {
	    /* Get the tile types to the left and right of the edge */

	    tt = TiGetLeftType(tile);
	    to = TiGetRightType(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 read-in
	     * the user is warned that such edges are not checked).
	     */

	    if (tt == to) 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;

	    triggered = 0;
	    for (cptr = DRCCurStyle->DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
			cptr = cptr->drcc_next)
	    {
		/* Handle rule exceptions and exemptions */
		if (cptr->drcc_exception != DRC_EXCEPTION_NONE)
		{
		    PropertyRecord *proprec;
		    bool propfound, isinside = FALSE;
		    char *name;
		    int idx = cptr->drcc_exception & ~DRC_EXCEPTION_MASK;
		    name = DRCCurStyle->DRCExceptionList[idx];

		    /* Is there any exception area defined? */
		    proprec = DBPropGet(arg->dCD_celldef, name, &propfound);

		    /* If an exception area exists, is the error edge inside? */
		    if (propfound)
		    {
			Rect redge;

			redge.r_xbot = redge.r_xtop = edgeX;
			redge.r_ybot = edgeBot;
			redge.r_ytop = edgeTop;

			if (DBSrPaintArea(PlaneGetHint(proprec->prop_value.prop_plane),
				proprec->prop_value.prop_plane,
				&redge, &CIFSolidBits, drcFoundOneFunc,
				(ClientData)NULL) == 1)
			    isinside = TRUE;
		    }

		    /* Exemption rules are ignored if the edge is inside
		     * an exception area.  Exception rules are ignored if
		     * the edge is outside an exception area.
		     */
		    if (!isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) == 0))
			continue;
		    if (isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) != 0))
			continue;
		}

	    	/* DRC_ANGLES_90 and DRC_SPLITTILE rules are handled by	*/
		/* the code above for non-Manhattan shapes and do not	*/
		/* need to be processed again.				*/
		if (cptr->drcc_flags & (DRC_ANGLES_90 | DRC_SPLITTILE))
		    continue;

		/* Find the rule distances according to the scale factor */
		dist = cptr->drcc_dist;
		cdist = cptr->drcc_cdist;
		trigpending = (cptr->drcc_flags & DRC_TRIGGER) ? TRUE : FALSE;

		/* drcc_edgeplane is used to avoid checks on edges	*/
		/* in more than one plane				*/

		if (arg->dCD_plane != cptr->drcc_edgeplane)
		{
		    if (trigpending) cptr = cptr->drcc_next;
		    continue;
		}

		DRCstatRules++;
		if (!triggered) mrd = NULL;

		if (cptr->drcc_flags & DRC_AREA)
		{
		    if (firsttile)
			drcCheckArea(tile, arg, cptr);
		    continue;
		}

		if ((cptr->drcc_flags & (DRC_MAXWIDTH | DRC_BENDS)) ==
				(DRC_MAXWIDTH | DRC_BENDS))
		{
		    /* New algorithm --- Tim 3/6/05 */
		    Rect *lr;
		    int i;

		    if (!trigpending || (DRCCurStyle->DRCFlags
				& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
			cptr->drcc_dist++;

		    if (cptr->drcc_flags & DRC_REVERSE)
		    {
			mrd = drcCanonicalMaxwidth(tpleft, GEO_WEST, arg, cptr,
					&mrdcache[0]);
			triggered = 0;
		    }
		    else
		    {
			if (cptrcache == NULL)
			{
			    mrd = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr,
					&mrdcache[1]);
			    cptrcache = cptr;
			}
			else if (cptrcache != cptr)
			    mrd = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr,
					&mrdcache[2]);
			else
			    mrd = (mrdcache[1]->entries == 0) ? NULL : mrdcache[1];
			triggered = 0;
		    }
		    if (!trigpending || (DRCCurStyle->DRCFlags
				& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
			cptr->drcc_dist--;
		    if (trigpending)
		    {
			if (mrd)
			{
			    if (cptr->drcc_cdist <= cptr->drcc_dist)
				triggered = mrd->entries;
		            else if ((edgeTop - edgeBot) >= cptr->drcc_cdist)
			    {
				/* Run-length rule */
				for (i = 0; i < mrd->entries; i++)
				{
				    lr = &mrd->rlist[i];
				    if ((lr->r_ytop - lr->r_ybot) > cptr->drcc_cdist)
				    {
					triggered = mrd->entries;
					break;
				    }
				}
				if (i == mrd->entries)
				    cptr = cptr->drcc_next;
			    }
			    else
				cptr = cptr->drcc_next;
			}
			else
			    cptr = cptr->drcc_next;
		    }
		    else if (mrd)
		    {
			for (i = 0; i < mrd->entries; i++)
			{
			    lr = &mrd->rlist[i];
			    GeoClip(lr, arg->dCD_clip);
			    if (!GEO_RECTNULL(lr))
			    {
				(*(arg->dCD_function)) (arg->dCD_celldef,
					lr, cptr, arg->dCD_clientData);
				(*(arg->dCD_errors))++;
			    }
			}
		    }
		    continue;
		}
		else if (cptr->drcc_flags & DRC_MAXWIDTH)
		{
		    if (cptr->drcc_flags & DRC_MAXWIDTH_BOTH)
		    {
		        if (firsttile)
			    drcCheckMaxwidth(tile, arg, cptr, TRUE);
		    }
		    else
		    {
		        /* bends_illegal option only */
		        if (firsttile)
			    drcCheckMaxwidth(tile, arg, cptr, FALSE);
		    }
		    continue;
		}

		if (cptr->drcc_flags & DRC_RECTSIZE)
		{
		    /* only checked for bottom-left tile in a rect area */
		    if (firsttile && !TTMaskHasType(&cptr->drcc_mask,
				TiGetRightType(BL(tile))) &&
				!TTMaskHasType(&cptr->drcc_mask,
				TiGetTopType(LB(tile))))
			drcCheckRectSize(tile, arg, cptr);
		    continue;
		}
		/* Off-grid checks apply only to edge */
		if (cptr->drcc_flags & DRC_OFFGRID)
		{
		    errRect.r_ytop = edgeTop;
		    errRect.r_ybot = edgeBot;
		    errRect.r_xtop = errRect.r_xbot = edgeX;
		    arg->dCD_cptr = cptr;
		    drcCheckOffGrid(&errRect, arg, cptr);
		    continue;
		}

		result = 0;
		arg->dCD_radial = 0;
		arg->dCD_entries = 0;
		do {
		    if (triggered)
		    {
			/* For triggered rules, we want to look	at the	*/
			/* clipped region found by the triggering rule	*/

			if (mrd)
			    errRect = mrd->rlist[--triggered];
			else
			    errRect = arg->dCD_rlist[--triggered];
			errRect.r_ytop += cdist;
			errRect.r_ybot -= cdist;
			if (errRect.r_ytop > edgeTop) errRect.r_ytop = edgeTop;
			if (errRect.r_ybot < edgeBot) errRect.r_ybot = edgeBot;
		    }
		    else
		    {
			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 */
			if (BOTTOM(tile) >= errRect.r_ybot) tpr = LB(tile);
			else tpr = tile;

			/* Also find point (2) to check for edge continuation */
			if (BOTTOM(tpleft) >= errRect.r_ybot)
			    for (tpl = LB(tpleft); RIGHT(tpl) < edgeX; tpl = TR(tpl));
			else tpl = tpleft;

			/* Make sure the edge stops at edgeBot */
			if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
				(TiGetLeftType(tpr) != TiGetLeftType(tile)))
			{
			    if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpr)))
		 	    {
				errRect.r_ybot -= cdist;
				if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
				    arg->dCD_radial |= RADIAL_SW;
			    }
			}

			if (cptr->drcc_flags & DRC_BOTHCORNERS)
			{
			    /*
			     * Check the other corner
			     */

			    /* Find point (4) to the top right of pointT */
			    if (TOP(tile) <= errRect.r_ytop)
				for (tpr = RT(tile); LEFT(tpr) > edgeX; tpr = BL(tpr));
			    else tpr = tile;

			    /* Also find point (1) to check for edge continuation */
			    if (TOP(tpleft) <= errRect.r_ytop) tpl = RT(tpleft);
			    else tpl = tpleft;

			    /* Make sure the edge stops at edgeTop */
			    if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
					(TiGetLeftType(tpr) != TiGetLeftType(tile)))
			    {
			        if (TTMaskHasType(&cptr->drcc_corner,
					TiGetBottomType(tpr)))
				{
				    errRect.r_ytop += cdist;
				    if (DRCEuclidean &&
						!(cptr->drcc_flags & DRC_MANHATTAN))
					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 - dist;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xbot--;
			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,
				TiGetRightType(tpleft)))
			    continue;

			errRect.r_xtop = edgeX;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xtop -= dist;
			arg->dCD_initial = tile;
		    }
		    else  /* FORWARD */
		    {
			/*
			 * Determine corner extensions.
			 */

			/* Find the point (1) to the top left of pointT */
			if (TOP(tpleft) <= errRect.r_ytop) tpl = RT(tpleft);
			else tpl = tpleft;

			/* Also find point (4) to check for edge continuation */
			if (TOP(tile) <= errRect.r_ytop)
			    for (tpr = RT(tile); LEFT(tpr) > edgeX; tpr = BL(tpr));
			else tpr = tile;

			/* Make sure the edge stops at edgeTop */
			if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
				(TiGetLeftType(tpr) != TiGetLeftType(tile)))
			{
			    if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpl)))
			    {
				errRect.r_ytop += cdist;
				if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
				    arg->dCD_radial |= RADIAL_NE;
			    }
			}

			if (cptr->drcc_flags & DRC_BOTHCORNERS)
			{
			    /*
			     * Check the other corner
			     */

			    /* Find point (2) to the bottom left of pointB. */
			    if (BOTTOM(tpleft) >= errRect.r_ybot)
				for (tpl = LB(tpleft); RIGHT(tpl) < edgeX; tpl = TR(tpl));
			    else tpl = tpleft;

			    /* Also find point (3) to check for edge continuation */
			    if (BOTTOM(tile) >= errRect.r_ybot) tpr = LB(tile);
			    else tpr = tile;

			    /* Make sure the edge stops at edgeBot */
			    if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
					(TiGetLeftType(tpr) != TiGetLeftType(tile)))
			    {
				if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpl)))
				{
				    errRect.r_ybot -= cdist;
				    if (DRCEuclidean &&
						!(cptr->drcc_flags & DRC_MANHATTAN))
					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 + dist;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xtop++;
			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;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xbot += dist;
			arg->dCD_initial= tpleft;
		    }
		    if (arg->dCD_radial)
		    {
			arg->dCD_radial &= 0xf000;
			arg->dCD_radial |= (0xfff & cdist);
		    }

		    DRCstatSlow++;
		    arg->dCD_cptr = cptr;
		    arg->dCD_entries = 0;
		    TTMaskCom2(&tmpMask, &cptr->drcc_mask);
		    TTMaskClearType(&tmpMask, TT_ERROR_S);
		    DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);
		} while (triggered);

		if (arg->dCD_entries == 0)
		{
		    /* Trigger rule:  If rule check found errors,	*/
		    /* do the next rule.  Otherwise, skip it.		*/

		    if (trigpending)
			cptr = cptr->drcc_next;
		}
		else
		    triggered = arg->dCD_entries;
	    }
	    DRCstatEdges++;
	    firsttile = FALSE;
        }
    }

    cptrcache = NULL;

    /*
     * 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, *tpx;
	TileType tt, to;
	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 */
	firsttile = TRUE;
        for (tpbot = LB(tile); LEFT(tpbot) < right; tpbot = TR(tpbot))
        {
	    /* Get the tile types to the top and bottom of the edge */

	    tt = TiGetBottomType(tile);
	    to = TiGetTopType(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 (tt == to) 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;

	    triggered = 0;
	    for (cptr = DRCCurStyle->DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
				cptr = cptr->drcc_next)
	    {
		/* Handle rule exceptions and exemptions */
		if (cptr->drcc_exception != DRC_EXCEPTION_NONE)
		{
		    PropertyRecord *proprec;
		    bool propfound, isinside = FALSE;
		    char *name;
		    int idx = cptr->drcc_exception & ~DRC_EXCEPTION_MASK;
		    name = DRCCurStyle->DRCExceptionList[idx];

		    /* Is there any exception area defined? */
		    proprec = DBPropGet(arg->dCD_celldef, name, &propfound);

		    /* If an exception area exists, is the error edge inside? */
		    if (propfound)
		    {
			Rect redge;

			redge.r_ybot = redge.r_ytop = edgeY;
			redge.r_xbot = edgeLeft;
			redge.r_xtop = edgeRight;

			if (DBSrPaintArea(PlaneGetHint(proprec->prop_value.prop_plane),
				proprec->prop_value.prop_plane,
				&redge, &CIFSolidBits, drcFoundOneFunc,
				(ClientData)NULL) == 1)
			    isinside = TRUE;
		    }

		    /* Exemption rules are ignored if the edge is inside
		     * an exception area.  Exception rules are ignored if
		     * the edge is outside an exception area.
		     */
		    if (!isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) == 0))
			continue;
		    if (isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) != 0))
			continue;
		}

	    	/* DRC_ANGLES_90 and DRC_SPLITTILE rules are handled by	*/
		/* the code above for non-Manhattan shapes and do not	*/
		/* need to be processed again.				*/
		if (cptr->drcc_flags & (DRC_ANGLES_90 | DRC_SPLITTILE))
		    continue;

		/* Find the rule distances according to the scale factor */
		dist = cptr->drcc_dist;
		cdist = (cptr->drcc_flags & DRC_RUNLENGTH) ? 0 : cptr->drcc_cdist;
		trigpending = (cptr->drcc_flags & DRC_TRIGGER) ? TRUE : FALSE;

		/* drcc_edgeplane is used to avoid checks on edges	*/
		/* in more than one plane				*/

		if (arg->dCD_plane != cptr->drcc_edgeplane)
		{
		    if (trigpending) cptr = cptr->drcc_next;
		    continue;
		}

		DRCstatRules++;
		if (!triggered) mrd = NULL;

		/* top to bottom */

		if ((cptr->drcc_flags & (DRC_MAXWIDTH | DRC_BENDS)) ==
				(DRC_MAXWIDTH | DRC_BENDS))
		{
		    /* New algorithm --- Tim 3/6/05 */
		    Rect *lr;
		    int i;

		    if (!trigpending || (DRCCurStyle->DRCFlags
				& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
			cptr->drcc_dist++;

		    if (cptr->drcc_flags & DRC_REVERSE)
		    {
			mrd = drcCanonicalMaxwidth(tpbot, GEO_SOUTH, arg, cptr,
					&mrdcache[0]);
			triggered = 0;
		    }
		    else
		    {
			if (cptrcache == NULL)
			{
			    mrd = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr,
					&mrdcache[1]);
			    cptrcache = cptr;
			}
			else if (cptrcache != cptr)
			    mrd = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr,
					&mrdcache[2]);
			else
			    mrd = (mrdcache[1]->entries == 0) ? NULL : mrdcache[1];
			triggered = 0;
		    }
		    if (!trigpending || (DRCCurStyle->DRCFlags
				& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
			cptr->drcc_dist--;
		    if (trigpending)
		    {
			if (mrd)
			{
			    if (cptr->drcc_cdist <= cptr->drcc_dist)
				triggered = mrd->entries;
		            else if ((edgeRight - edgeLeft) >= cptr->drcc_cdist)
			    {
				/* Run-length rule */
				for (i = 0; i < mrd->entries; i++)
				{
				    lr = &mrd->rlist[i];
				    if ((lr->r_xtop - lr->r_xbot) > cptr->drcc_cdist)
				    {
					triggered = mrd->entries;
					break;
				    }
				}
				if (i == mrd->entries)
				    cptr = cptr->drcc_next;
			    }
			    else
				cptr = cptr->drcc_next;
			}
			else
			    cptr = cptr->drcc_next;
		    }
		    else if (mrd)
		    {
			for (i = 0; i < mrd->entries; i++)
			{
			    lr = &mrd->rlist[i];
			    GeoClip(lr, arg->dCD_clip);
			    if (!GEO_RECTNULL(lr))
			    {
				(*(arg->dCD_function)) (arg->dCD_celldef,
					lr, cptr, arg->dCD_clientData);
				(*(arg->dCD_errors))++;
			    }
			}
		    }
		    continue;
		}
		else if (cptr->drcc_flags & (DRC_AREA | DRC_RECTSIZE | DRC_MAXWIDTH))
		{
		    /* only have to do these checks in one direction */
		    if (trigpending) cptr = cptr->drcc_next;
		    continue;
		}
		/* Off-grid checks apply only to edge */
		if (cptr->drcc_flags & DRC_OFFGRID)
		{
		    errRect.r_xtop = edgeRight;
		    errRect.r_xbot = edgeLeft;
		    errRect.r_ytop = errRect.r_ybot = edgeY;
		    arg->dCD_cptr = cptr;
		    drcCheckOffGrid(&errRect, arg, cptr);
		    continue;
		}

		result = 0;
		arg->dCD_radial = 0;
		arg->dCD_entries = 0;
		do {
		    if (triggered)
		    {
			/* For triggered rules, we want to look	at the	*/
			/* clipped region found by the triggering rule	*/

			if (mrd)
			    errRect = mrd->rlist[--triggered];
			else
			    errRect = arg->dCD_rlist[--triggered];
			errRect.r_xtop += cdist;
			errRect.r_xbot -= cdist;
			if (errRect.r_xtop > edgeRight) errRect.r_xtop = edgeRight;
			if (errRect.r_xbot < edgeLeft) errRect.r_xbot = edgeLeft;
		    }
		    else
		    {
			errRect.r_xbot = edgeLeft;
			errRect.r_xtop = edgeRight;
		    }

		    if (cptr->drcc_flags & DRC_REVERSE)
		    {
			/*
			 * Determine corner extensions.
			 * Find the point (3) to the top right of pointR
			 */
			if (RIGHT(tile) <= errRect.r_xtop)
			    for (tpx = TR(tile); BOTTOM(tpx) > edgeY; tpx = LB(tpx));
			else tpx = tile;

		 	if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpx)))
			{
			    errRect.r_xtop += cdist;
			    if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
				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.
			     */

			    if (LEFT(tile) >= errRect.r_xbot) tpx = BL(tile);
			    else tpx = tile;

			    if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpx)))
			    {
				errRect.r_xbot -= cdist;
				if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
				    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 - dist;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ybot--;
			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;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ytop -= dist;
			arg->dCD_initial = tile;
		    }
		    else  /* FORWARD */
		    {
			/*
			 * Determine corner extensions.
			 * Find the point (1) to the bottom left of pointL
			 */

			if (LEFT(tpbot) >= errRect.r_xbot)
			    for (tpx = BL(tpbot); TOP(tpx) < edgeY; tpx = RT(tpx));
			else tpx = tpbot;

			if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpx)))
			{
			    errRect.r_xbot -= cdist;
			    if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
				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.
			     */
			    if (RIGHT(tpbot) <= errRect.r_xtop) tpx = TR(tpbot);
			    else tpx = tpbot;

			    if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpx)))
			    {
				errRect.r_xtop += cdist;
				if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
				    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 + dist;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ytop++;
			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,
				TiGetBottomType(tile)))
			    continue;

			errRect.r_ybot = edgeY;
			if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ybot += dist;
			arg->dCD_initial = tpbot;
		    }
		    if (arg->dCD_radial)
		    {
			arg->dCD_radial &= 0xf000;
			arg->dCD_radial |= (0xfff & cdist);
		    }

		    DRCstatSlow++;
		    arg->dCD_cptr = cptr;
		    arg->dCD_entries = 0;
		    TTMaskCom2(&tmpMask, &cptr->drcc_mask);
		    TTMaskClearType(&tmpMask, TT_ERROR_S);
		    DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);
		} while (triggered);

		if (arg->dCD_entries == 0)
		{
		    /* Trigger rule:  If rule check found errors,	*/
		    /* do the next rule.  Otherwise, skip it.	*/

		    if (trigpending)
			cptr = cptr->drcc_next;
		}
		else
		    triggered = arg->dCD_entries;
	    }
	    DRCstatEdges++;
	    firsttile = FALSE;
        }
    }
    return (0);
}
