/*
 * 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/drc/DRCbasic.c,v 1.1.1.1 2005/05/09 23:29:24 tim Exp $";
#endif	/* not lint */

#include <sys/types.h>
#include <stdio.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"

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 }, (DRCCookie *) NULL,
    "Can't overlap those layers",
    0, 0, 0};

/* Forward references: */

extern int areaCheck();
extern int drcTile();

/*
 * ----------------------------------------------------------------------------
 *
 * 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).
 *	1 if the rule is a trigger rule and an error rectangle is present
 *	(the trigger rule does not create error paint).
 *
 * Side effects:
 *      Applies the function passed as an argument.
 *
 * ----------------------------------------------------------------------------
 */

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

#ifdef NONMANHATTAN

    /* If the tile has a legal type, then return.  This is where we	*/
    /* would want to add extra processing to check whether the half of	*/
    /* the tile that is of an illegal type is inside or outside the	*/
    /* area of interest.						*/

    if (IsSplit(tile))
    {
	if (TTMaskHasType(&arg->dCD_cptr->drcc_mask, SplitLeftType(tile)) &&
		TTMaskHasType(&arg->dCD_cptr->drcc_mask, SplitRightType(tile)))
	    return 0;
    }
    else 
#endif
    if (TTMaskHasType(&arg->dCD_cptr->drcc_mask, TiGetType(tile))) 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);

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

    /* Apply extra clipping for radial distance check on corner
     * extensions.
     */

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

	for (i = 0x1000; i <= 0x8000; i <<= 1)
	{
	    if ((arg->dCD_radial & i) != 0)
	    {
	 	/* check for correct orientation */
		if ((sqx = (i == 0x4000 || i == 0x8000) ?
	  		rect.r_xbot - arg->dCD_constraint->r_xtop + sdist:
	  		arg->dCD_constraint->r_xbot + sdist - rect.r_xtop) < 0)
	  	    continue;
		if ((sqy = (i == 0x2000 || i == 0x8000) ?
			rect.r_ybot - arg->dCD_constraint->r_ytop + sdist:
			arg->dCD_constraint->r_ybot + sdist - rect.r_ytop) < 0)
		    continue;

		if ((sqx * sqx + sqy * sqy) >= ssdist)
		    return 0;
	    }
	}
    }

    if (arg->dCD_cptr->drcc_flags & DRC_TRIGGER)
	return 1;

    (*(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 withing 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;

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

    for (planeNum = PL_TECHDEPBASE; planeNum < DBNumPlanes; planeNum++)
    {
        arg.dCD_plane = planeNum;
#ifdef	DRC_EXTENSIONS
	DBResetTilePlane(celldef->cd_planes[planeNum], DRC_UNPROCESSED);
#endif
        (void) DBSrPaintArea ((Tile *) NULL, celldef->cd_planes[planeNum],
		checkRect, &DBAllTypeBits, drcTile, (ClientData) &arg);
    }
#ifdef	DRC_CIF_EXTENSIONS
    drcCifCheck(&arg);
#endif
    return (errors);
}

/*
 * ----------------------------------------------------------------------------
 *
 * 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, arg)
    Tile *tile;	/* Tile being examined */
    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, *rMask;
    unsigned char stackpending = (char)0;
    bool triggered;		/* Hack for widespacing rule */
    int cdist, ccdist, result;

    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++;

    /* 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))++;
    }

#ifdef NONMANHATTAN
    /* This drc is only for the left edge of the tile */
    if (IsSplit(tile) && SplitSide(tile)) goto checkbottom;
#endif  

    /*
     * 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;
	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);

        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;

	    /*
	     * Stacked contact types have no rules.  Any stacked contact
	     * type must have DRC rules applied to each of its constituent
	     * types.  But, if the residue mask for the stacking type
	     * contains the other type, then this is a "false" edge, and
	     * we will not process it.  Otherwise, start with the first
	     * type encountered for each.
	     */
	    stackpending = (unsigned char)0x0;
	    if (tt >= DBNumUserLayers)
	    {
		rMask = DBResidueMask(tt);
		if (to >= DBNumUserLayers)
		{
		    TileTypeBitMask combined;
		    TTMaskAndMask3(&combined, rMask, DBResidueMask(to));
		    if (!TTMaskIsZero(&combined))
			continue;
		}
		else if (TTMaskHasType(rMask, to)) continue;
		stackpending |= (unsigned char)0x01;
		for (tt = TT_TECHDEPBASE; tt < DBNumUserLayers; tt++)
		    if (TTMaskHasType(rMask, tt))
			break;
	    }
	    if (to >= DBNumUserLayers)
	    {
		rMask = DBResidueMask(to);
		if (TTMaskHasType(rMask, tt)) continue;
		stackpending |= (unsigned char)0x02;
		for (to = TT_TECHDEPBASE; to < DBNumUserLayers; to++)
		    if (TTMaskHasType(rMask, to))
			break;
	    }

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

	    while(1)
	    {
		if (stackpending && (to == tt)) goto hnextstacktype;
		
		triggered = FALSE;
		for (cptr = DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
			cptr = cptr->drcc_next)
		{
		    if (cptr->drcc_flags & DRC_CATECORNER)
		    {
			ccdist = cptr->drcc_cdist;
			cdist = cptr->drcc_dist;
		    }
		    else
			cdist = cptr->drcc_cdist;

		    /* Contact images exist in multiple planes; only check	*/
		    /* rules that are for the plane being checked.		*/
		    /* NOTE:  Ideally, the plane of the edge should be recorded	*/
		    /* in the rule; i.e., we should have cptr->drcc_edgeplane	*/
		    /* and compare that to arg->dCD_plane.  However, the	*/
		    /* following eliminates most of the redundant checks.	*/
		
		    if ((!(cptr->drcc_flags & DRC_XPLANE)) &&
				(arg->dCD_plane != cptr->drcc_plane))
		    {
			if (cptr->drcc_flags & DRC_TRIGGER) cptr = cptr->drcc_next;
			continue;
		    }

		    DRCstatRules++;
		    if (triggered)
		    {
			/* Widespacing hack---rule is triggered by MAXWIDTH	*/
			/* algorithm, but we only want to look in the region	*/
			/* of the maxwidth rectangle.  Otherwise, a violation	*/
			/* is incorrectly flagged on thin wires that happen to	*/
			/* share a common edge with the maxwidth region.	*/

			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;
			triggered = FALSE;
		    }
		    else
		    {
			errRect.r_ytop = edgeTop;
			errRect.r_ybot = edgeBot;
		    }

#ifdef DRC_EXTENSIONS
		    if (cptr->drcc_flags & DRC_AREA)
		    {
			drcCheckArea(tile,arg,cptr);
			continue;
		    }
		    if (cptr->drcc_flags & (DRC_MAXWIDTH | DRC_BENDS))
		    {
			/* New algorithm --- Tim 3/6/05 */
			Rect *lr;

			if (cptr->drcc_flags & DRC_REVERSE)
			    lr = drcCanonicalMaxwidth(tpleft, GEO_WEST, arg, cptr);
			else
			    lr = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr);
		        if (cptr->drcc_flags & DRC_TRIGGER)
			{
			    if (lr)
			    {
			        errRect = *lr;
				triggered = TRUE;
			    }
			    else
				cptr = cptr->drcc_next;
			}
			else if (lr)
			{
		            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)
		    {
			/* bends_illegal option only */
			drcCheckMaxwidth(tile, arg, cptr);
			continue;
		    }
		    if (cptr->drcc_flags & DRC_RECTSIZE)
		    {
		        /* only checked for bottom-left tile in a rect area */
		        if (!TTMaskHasType(&cptr->drcc_mask,
					TiGetRightType(BL(tile))) &&
					!TTMaskHasType(&cptr->drcc_mask,
					TiGetTopType(LB(tile))))
			    drcCheckRectSize(tile, arg, cptr);
			continue;
		    }
#endif

		    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 -= cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= 0x1000;
			}

			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 += cdist;
				if (DRCEuclidean)
				    arg->dCD_radial |= 0x2000;
			    }
			}

			/*
			 * 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
				&& !(cptr->drcc_flags & DRC_XPLANE)
				&& TTMaskHasType(&cptr->drcc_mask,
				TiGetRightType(tpleft)))
			{
			    if (cptr->drcc_flags & DRC_TRIGGER)
				cptr = cptr->drcc_next;
			    continue;
			}
			errRect.r_xtop = edgeX;
			arg->dCD_initial = tile;
#ifdef DRC_EXTENSIONS
			arg->dCD_which = DRC_LEFT;
#endif
		    }
		    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 += cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= 0x8000;
			}

			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 -= cdist;
				if (DRCEuclidean)
				    arg->dCD_radial |= 0x4000;
			    }
			}

			/*
			 * 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
				&& !(cptr->drcc_flags & DRC_XPLANE)
				&& TTMaskHasType(&cptr->drcc_mask,
				TiGetLeftType(tile)))
			{
			    if (cptr->drcc_flags & DRC_TRIGGER)
				cptr = cptr->drcc_next;
			    continue;
			}
			errRect.r_xbot = edgeX;
			arg->dCD_initial= tpleft;
#ifdef DRC_EXTENSIONS
			arg->dCD_which = DRC_RIGHT;
#endif
		    }
		    if (arg->dCD_radial)
		    {
			arg->dCD_radial &= 0xf000;
			arg->dCD_radial |= (0xfff & cdist);
		    }

		    DRCstatSlow++;
		    arg->dCD_cptr = cptr;
		    TTMaskCom2(&tmpMask, &cptr->drcc_mask);

		    if (cptr->drcc_flags & DRC_CATECORNER)
		    {
			int tmpy = errRect.r_ybot;
			errRect.r_ybot = edgeTop + ccdist;
			if (errRect.r_ybot < errRect.r_ytop)
			{
			    /* result = 0; */ /* to be finished */
			    result = DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);
			}
			errRect.r_ybot = tmpy;
			tmpy = errRect.r_ytop;
			errRect.r_ytop = edgeBot - ccdist;
			if (errRect.r_ytop > errRect.r_ybot)
			{
			    /* result = 0; */ /* to be finished */
			    result = DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);
			}

			/* (to be done)(?) */
		    }
		    else 
			result = DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);

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

			if (cptr->drcc_flags & DRC_TRIGGER)
			    cptr = cptr->drcc_next;
		    }
		}
		DRCstatEdges++;

hnextstacktype:

		/* Check if more processing is required for stacked	*/
		/* contact types 					*/

		if (!stackpending) break;
		else if (stackpending == (unsigned char)0x01)
		{
		    rMask = DBResidueMask(TiGetLeftType(tile));
		    for (++tt; tt < DBNumUserLayers; tt++)
			if (TTMaskHasType(rMask, tt))
			    break;
		}
		else if (stackpending == (unsigned char)0x02)
		{
		    rMask = DBResidueMask(TiGetRightType(tpleft));
		    for (++to; to < DBNumUserLayers; to++)
			if (TTMaskHasType(rMask, to))
			    break;
		}
		else /* stackpending for both types */
		{
		    rMask = DBResidueMask(TiGetLeftType(tile));
		    for (++tt; tt < DBNumUserLayers; tt++)
			if (TTMaskHasType(rMask, tt))
			    break;

		    if (tt >= DBNumUserLayers)	
		    {
			for (tt = TT_TECHDEPBASE; tt < DBNumUserLayers; tt++)
			    if (TTMaskHasType(rMask, tt))
				break;

			rMask = DBResidueMask(TiGetRightType(tpleft));
			for (++to; to < DBNumUserLayers; to++)
			    if (TTMaskHasType(rMask, to))
				break;
		    }
		}
		if ((to >= DBNumUserLayers) || (tt >= DBNumUserLayers)) break;
	    }
        }
    }

#ifdef NONMANHATTAN

    /* This drc is only for the bottom edge of the tile */

checkbottom:
    if (IsSplit(tile))
	if (SplitSide(tile) == SplitDirection(tile)) return 0;
#endif          

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

	    /*
	     * Stacked contact types have no rules.  Any stacked contact
	     * type must have DRC rules applied to each of its constituent
	     * types.  Start with the first type encountered for each.
	     */

	    stackpending = (unsigned char)0x0;
	    if (tt >= DBNumUserLayers)
	    {
		rMask = DBResidueMask(tt);
		if (to >= DBNumUserLayers)
		{
		    TileTypeBitMask combined;
		    TTMaskAndMask3(&combined, rMask, DBResidueMask(to));
		    if (!TTMaskIsZero(&combined))
			continue;
		}
		else if (TTMaskHasType(rMask, to)) continue;
		stackpending |= (unsigned char)0x01;
		for (tt = TT_TECHDEPBASE; tt < DBNumUserLayers; tt++)
		    if (TTMaskHasType(rMask, tt))
			break;
	    }
	    if (to >= DBNumUserLayers)
	    {
		rMask = DBResidueMask(to);
		if (TTMaskHasType(rMask, tt)) continue;
		stackpending |= (unsigned char)0x02;
		for (to = TT_TECHDEPBASE; to < DBNumUserLayers; to++)
		    if (TTMaskHasType(rMask, to))
			break;
	    }

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

	    while(1)
	    {
		if (stackpending && (to == tt)) goto vnextstacktype;

		triggered = FALSE;
		for (cptr = DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
				cptr = cptr->drcc_next)
		{
		    if (cptr->drcc_flags & DRC_CATECORNER)
		    {
			ccdist = cptr->drcc_cdist;
			cdist = cptr->drcc_dist;
		    }
		    else
			cdist = cptr->drcc_cdist;

		    /* Contact images exist in multple planes; only check	*/
		    /* rules that are for the plane being checked.		*/
		    if ((!(cptr->drcc_flags & DRC_XPLANE)) &&
				(arg->dCD_plane != cptr->drcc_plane))
		    {
			if (cptr->drcc_flags & DRC_TRIGGER) cptr = cptr->drcc_next;
			continue;
		    }

		    DRCstatRules++;
		    if (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;
			triggered = FALSE;
		    }
		    else
		    {
			errRect.r_xbot = edgeLeft;
			errRect.r_xtop = edgeRight;
		    }

		    /* top to bottom */

#ifdef DRC_EXTENSIONS
		    if (cptr->drcc_flags & (DRC_MAXWIDTH | DRC_BENDS))
		    {
			/* New algorithm --- Tim 3/6/05 */
			Rect *lr;

			if (cptr->drcc_flags & DRC_REVERSE)
			    lr = drcCanonicalMaxwidth(tpbot, GEO_SOUTH, arg, cptr);
			else
			    lr = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr);
		        if (cptr->drcc_flags & DRC_TRIGGER)
			{
			    if (lr)
			    {
				errRect = *lr;
				triggered = TRUE;
			    }
			    else
				cptr = cptr->drcc_next;
			}
			else if (lr)
			{
		            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 (cptr->drcc_flags & DRC_TRIGGER) cptr = cptr->drcc_next;
			continue;
		    }
#endif

		    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 += cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= 0x4000;
			}

			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 -= cdist;
				if (DRCEuclidean)
				    arg->dCD_radial |= 0x1000;
			    }
			}

			/*
			 * 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
				&& !(cptr->drcc_flags & DRC_XPLANE)
				&& TTMaskHasType(&cptr->drcc_mask,
				TiGetTopType(tpbot)))
			{
			    if (cptr->drcc_flags & DRC_TRIGGER)
				cptr = cptr->drcc_next;
			    continue;
			}
			errRect.r_ytop = edgeY;
			arg->dCD_initial = tile;
#ifdef DRC_EXTENSIONS
			arg->dCD_which = DRC_BOTTOM;
#endif
		    }
		    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 -= cdist;
			    if (DRCEuclidean)
				arg->dCD_radial |= 0x2000;
			}

			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 += cdist;
				if (DRCEuclidean)
				    arg->dCD_radial |= 0x8000;
			    }
			}

			/*
			 * 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
				&& !(cptr->drcc_flags & DRC_XPLANE)
				&& TTMaskHasType(&cptr->drcc_mask,
				TiGetBottomType(tile)))
			{
			    if (cptr->drcc_flags & DRC_TRIGGER)
				cptr = cptr->drcc_next;
			    continue;
			}
			errRect.r_ybot = edgeY;
			arg->dCD_initial = tpbot;
#ifdef DRC_EXTENSIONS
			arg->dCD_which = DRC_TOP;
#endif
		    }
		    if (arg->dCD_radial)
		    {
			arg->dCD_radial &= 0xf000;
			arg->dCD_radial |= (0xfff & cdist);
		    }

		    DRCstatSlow++;
		    arg->dCD_cptr = cptr;
		    TTMaskCom2(&tmpMask, &cptr->drcc_mask);
		    if (cptr->drcc_flags & DRC_CATECORNER)
		    {
			int tmpx = errRect.r_xbot;
			errRect.r_xbot = edgeLeft + ccdist;
			if (errRect.r_xbot < errRect.r_xtop)
			{
			    /* result = 0; */ /* to be finished */
			    result = DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);
			}
			errRect.r_xbot = tmpx;
			tmpx = errRect.r_xtop;
			errRect.r_xtop = edgeRight - ccdist;
			if (errRect.r_xtop > errRect.r_xbot)
			{
			    /* result = 0; */ /* to be finished */
			    result = DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);
			}

			/* (to be done)(?) */
		    }
		    else 
			result = DBSrPaintArea((Tile *) NULL,
				arg->dCD_celldef->cd_planes[cptr->drcc_plane],
				&errRect, &tmpMask, areaCheck, (ClientData) arg);

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

			if (cptr->drcc_flags & DRC_TRIGGER)
			    cptr = cptr->drcc_next;
		    }
		}
		DRCstatEdges++;

vnextstacktype:
		/* Check if more processing is required for stacked	*/
		/* contact types 					*/
		
		if (!stackpending) break;
		else if (stackpending == (unsigned char)0x01)
		{
		    rMask = DBResidueMask(TiGetBottomType(tile));
		    for (++tt; tt < DBNumUserLayers; tt++)
			if (TTMaskHasType(rMask, tt))
			    break;
		}
		else if (stackpending == (unsigned char)0x02)
		{
		    rMask = DBResidueMask(TiGetTopType(tpbot));
		    for (++to; to < DBNumUserLayers; to++)
			if (TTMaskHasType(rMask, to))
			    break;
		}
		else /* stackpending for both types */
		{
		    rMask = DBResidueMask(TiGetBottomType(tile));
		    for (++tt; tt < DBNumUserLayers; tt++)
			if (TTMaskHasType(rMask, tt))
			    break;

		    if (tt >= DBNumUserLayers)	
		    {
			for (tt = TT_TECHDEPBASE; tt < DBNumUserLayers; tt++)
			    if (TTMaskHasType(rMask, tt))
				break;

			rMask = DBResidueMask(TiGetTopType(tpbot));
			for (++to; to < DBNumUserLayers; to++)
			    if (TTMaskHasType(rMask, to))
				break;

		    }
		}
		if ((to >= DBNumUserLayers) || (tt >= DBNumUserLayers)) break;
	    }
        }
    }
    return (0);
}
