/*
 * 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-7.5/drc/DRCcif.c,v 1.8 2010/10/20 20:31:16 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"
#include "utils/utils.h"

extern char *drcWhyDup(); 
extern int  drcCifTile();
extern int  areaCifCheck();
extern void drcCheckCifMaxwidth();
extern void drcCheckCifArea();

extern Stack	*DRCstack;

#define PUSHTILE(tp) \
    if ((tp)->ti_client == (ClientData) DRC_UNPROCESSED) { \
        (tp)->ti_client = (ClientData)  DRC_PENDING; \
        STACKPUSH((ClientData) (tp), DRCstack); \
    }

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

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

#define DRC_CIF_SPACE		0
#define DRC_CIF_SOLID		1

/*
 * ----------------------------------------------------------------------------
 *
 * 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(argc, argv)
    int argc;
    char *argv[];
{
    CIFKeep     *new;

    for (new = CIFStyleList; new != NULL; new = new->cs_next)
    {
	if (!strcmp(new->cs_name, argv[1]))
	{
	    DRCForceReload = TRUE;
	    if (!strcmp(new->cs_name, CIFCurStyle->cs_name))
		drcCifStyle = CIFCurStyle;
	    else
	    {
		TechError("DRC cif extensions are not enabled.\n\t"
			"Use \"cif ostyle %s\" to enable them.\n",
			new->cs_name);
		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.
 *
 * ----------------------------------------------------------------------------
 */

int
drcCifWidth(argc, argv)
    int argc;
    char *argv[];
{
    char *layername = argv[1];
    int scalefactor;
    int centidistance = atoi(argv[2]);
    char *why = drcWhyDup(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)));
    drcAssign(dpnew, centidistance, dpnext, &CIFSolidBits,
    		&CIFSolidBits, why, centidistance, DRC_FORWARD, 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(argc, argv)
    int argc;
    char *argv[];
{
    char *adjacency = argv[4];
    char *why = drcWhyDup(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));
    drcAssign(dpnew, centidistance, dpnext, &DBSpaceBits,
    		&cmask, why, centidistance, DRC_FORWARD, layer[1], 0);
    drcCifRules[layer[0]][DRC_CIF_SOLID] = dpnew;
    
    if (needReverse)
    {
         dpnew->drcc_flags |= DRC_BOTHCORNERS;
         dpnext = drcCifRules[layer[1]][DRC_CIF_SOLID];
         dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
         drcAssign(dpnew, centidistance, dpnext, &DBSpaceBits, &cmask,
		why, centidistance, DRC_FORWARD|DRC_BOTHCORNERS, layer[0], 0);
         drcCifRules[layer[1]][DRC_CIF_SOLID] = dpnew;
	 
	 if (layer[0] == layer[1])
	 {
              dpnext = drcCifRules[layer[1]][DRC_CIF_SPACE];
              dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
              drcAssign(dpnew, centidistance, dpnext, &DBSpaceBits,
			&cmask, why, centidistance, DRC_REVERSE | DRC_BOTHCORNERS,
			layer[0], 0);
                 drcCifRules[layer[1]][DRC_CIF_SPACE] = dpnew;
	 
              dpnext = drcCifRules[layer[0]][DRC_CIF_SPACE];
              dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
              drcAssign(dpnew, centidistance, dpnext, &DBSpaceBits, &cmask,
			why, centidistance, DRC_REVERSE | DRC_BOTHCORNERS,
			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)));
         drcAssign(dpnew, scalefactor, dpnext, &DBSpaceBits, &DBZeroTypeBits,
			why, scalefactor, DRC_FORWARD, layer[0], 0);
         drcCifRules[layer[1]][DRC_CIF_SPACE] = dpnew;

         dpnext = drcCifRules[layer[0]][DRC_CIF_SPACE];
         dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
         drcAssign(dpnew, scalefactor, dpnext, &DBSpaceBits, &DBZeroTypeBits,
			why, scalefactor, DRC_FORWARD, 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(arg)
    struct drcClientData *arg;
{
    Rect	*checkRect = arg->dCD_rect;
    Rect	cifrect;
    int		scale;
    int		i,j;
    int		oldTiles;

    if (drcCifValid == FALSE) return;
    else if (CIFCurStyle != drcCifStyle) 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, checkRect, CIFPlanes, &DBAllTypeBits, TRUE, TRUE);

    for (i = 0; i < drcCifStyle->cs_nLayers; i++)
    {
        for (j = 0; j != 2; j++)
	{
	    for (drcCifCur = drcCifRules[i][j]; 
	       		drcCifCur; drcCifCur = drcCifCur->drcc_next)
            {
     	        Plane *plane = CIFPlanes[i];
	  	TileTypeBitMask	*mask;
	  
	        DBSrPaintArea((Tile *) NULL, CIFPlanes[i], &cifrect,
			(j == DRC_CIF_SOLID) ? &DBSpaceBits : &CIFSolidBits,
	  		drcCifTile, arg);
     	     }
	 }
     }
     arg->dCD_rect = checkRect;
     DRCstatCifTiles += DRCstatTiles - oldTiles;
}

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

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

    /*
     * 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 |= 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 += cptr->drcc_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
			&& 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 |= 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 -= cptr->drcc_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
			&& 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 &= 0xf0000;
		    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++;
        }
    }


    /*
     * 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 |= 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 -= cptr->drcc_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
			&& 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 |= 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 += cptr->drcc_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
			&& 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, arg) 
    Tile *tile;
    struct drcClientData *arg;
{
    Rect rect;		/* Area where error is to be recorded. */
    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, &rect);
    GeoClip(&rect, arg->dCD_constraint);
    if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
	return 0;
    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;

    (*(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(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    int centiarea = atoi(argv[2]);
    int	centihorizon = atoi(argv[3]);
    char *why = drcWhyDup(argv[4]);
    TileTypeBitMask set, setC, tmp1;
    DRCCookie *dpnext, *dpnew;
    TileType i, j;
    int plane;
    int	thislayer;
    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)));
    drcAssign(dpnew, centihorizon, dpnext, &CIFSolidBits, &CIFSolidBits,
		why, centiarea, DRC_AREA | DRC_FORWARD, 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(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    int centidistance = atoi(argv[2]);
    char *bends = argv[3];
    char *why = drcWhyDup(argv[4]);
    TileTypeBitMask set, setC, tmp1;
    DRCCookie *dpnext, *dpnew;
    TileType i, j;
    int plane;
    int bend;
    int thislayer;
    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
    {
    	 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)));
    drcAssign(dpnew, centidistance, dpnext, &CIFSolidBits, &CIFSolidBits,
		why, centidistance, DRC_MAXWIDTH | 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(starttile, arg, cptr)
    Tile		 *starttile;
    struct drcClientData *arg;
    DRCCookie	 *cptr;
{
     int		arealimit = cptr->drcc_cdist;
     int		area=0;
     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 (tile->ti_client != (ClientData)DRC_PENDING) continue;
	area += (RIGHT(tile)-LEFT(tile))*(TOP(tile)-BOTTOM(tile));
	tile->ti_client = (ClientData)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 >= 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 <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 */
     starttile->ti_client = (ClientData)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 (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)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(starttile,arg,cptr)
    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
        one dimension is the max.  This is pretty stupid, but it correctly
	calculates the trench width rule. dcs 12.06.89 */
    if (cptr->drcc_flags & DRC_BENDS)
    {
	 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;
    }
    /* Mark this tile as pending and push it */
    PUSHTILE(starttile);
    TiToRect(starttile,&boundrect);

    while (!StackEmpty(DRCstack))
    {
	tile = (Tile *) STACKPOP(DRCstack);
	if (tile->ti_client != (ClientData)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);
	tile->ti_client = (ClientData)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 */
     starttile->ti_client = (ClientData)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 (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

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

     }
}

