/*
 * 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[] = "$Header: /ufs/repository/magic/drc/DRCcif.c,v 1.2 2001/01/12 22:12:28 jsolomon Exp $";
#endif	not lint

#include <sys/types.h>
#include <stdio.h>
#include "misc/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 "signals/signals.h"
#include "utils/stack.h"
#include "utils/malloc.h"
#include "utils/utils.h"

#ifdef DRC_CIF_EXTENSIONS

extern char *drcWhyDup(); 

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;
TileTypeBitMask drcCifGenLayers;
int	drcCifTile();
extern int areaCifCheck();

typedef struct drccifcookie
{
    int    	       drcc_dist;	/* Extent of rule from edge. */
    TileTypeBitMask      drcc_mask;	/* Legal types on RHS */
    TileTypeBitMask      drcc_corner;	/* Types that trigger corner check */
    struct drccifcookie * drcc_next;
    char             * drcc_why;	/* Explanation of error found */
    int		       drcc_cdist;	/* Size of corner extension. */
    int		       drcc_flags;	/* Miscellaneous flags, see below. */
    int		       drcc_plane;	
} DRCCifCookie;

DRCCifCookie	*drcCifRules[MAXCIFLAYERS][2];
int	drcCifValid = FALSE;
#define DRC_CIF_SPACE		0
#define DRC_CIF_SOLID		1
DRCCifCookie	*drcCifCur=NULL;

#define ASSIGN(cookie,dist,next,mask,corner,why,cdist,flags,layer) \
	((cookie)->drcc_dist = dist, \
	(cookie)->drcc_next = next, \
	(cookie)->drcc_mask = mask, \
	(cookie)->drcc_corner = corner, \
	(cookie)->drcc_why =  why, \
	(cookie)->drcc_cdist = cdist, \
	(cookie)->drcc_flags = flags, \
        (cookie)->drcc_plane = layer)

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

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

    /*ARGSUSED*/
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;
    DRCCifCookie *dpnew,*dpnext;
    TileType i;

    if (drcCifStyle == NULL)
    {
    	 TechError("Missing cif style for drc\n");
	 return 0;
    } 
    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;
    dpnext = drcCifRules[thislayer][DRC_CIF_SPACE];
    MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
    ASSIGN(dpnew,centidistance,dpnext,CIFSolidBits,
    		CIFSolidBits,why,centidistance,DRC_FORWARD,thislayer);
    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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
drcCifSpacing(argc, argv)
    int argc;
    char *argv[];
{
    char *adjacency = argv[4];
    char *why = drcWhyDup(argv[5]);
    DRCCifCookie *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)
    {
    	 TechError("Missing cif style for drc\n");
	 return 0;
    } 
    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;
    dpnext = drcCifRules[layer[0]][DRC_CIF_SOLID];
    MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
    ASSIGN(dpnew,centidistance,dpnext,DBSpaceBits,
    		cmask,why,centidistance,DRC_FORWARD,layer[1]);
    drcCifRules[layer[0]][DRC_CIF_SOLID] = dpnew;
    
    if (needReverse)
    {
         dpnew->drcc_flags |= DRC_BOTHCORNERS|DRC_XPLANE;
         dpnext = drcCifRules[layer[1]][DRC_CIF_SOLID];
         MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
         ASSIGN(dpnew,centidistance,dpnext,DBSpaceBits,
    		cmask,why,centidistance,
		DRC_XPLANE|DRC_FORWARD|DRC_BOTHCORNERS,layer[0]);
         drcCifRules[layer[1]][DRC_CIF_SOLID] = dpnew;
	 
	 if (layer[0] == layer[1])
	 {
              dpnext = drcCifRules[layer[1]][DRC_CIF_SPACE];
              MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
              ASSIGN(dpnew,centidistance,dpnext,DBSpaceBits,
    		     cmask,why,centidistance,
		     DRC_XPLANE|DRC_REVERSE|DRC_BOTHCORNERS,layer[0]);
                 drcCifRules[layer[1]][DRC_CIF_SPACE] = dpnew;
	 
              dpnext = drcCifRules[layer[0]][DRC_CIF_SPACE];
              MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
              ASSIGN(dpnew,centidistance,dpnext,DBSpaceBits,
    		     cmask,why,centidistance,
		     DRC_XPLANE|DRC_REVERSE|DRC_BOTHCORNERS,layer[1]);
                 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];
         MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
         ASSIGN(dpnew,scalefactor,dpnext,DBSpaceBits,
    		DBZeroTypeBits,why,scalefactor,
		DRC_FORWARD,layer[0]);
         drcCifRules[layer[1]][DRC_CIF_SPACE] = dpnew;

         dpnext = drcCifRules[layer[0]][DRC_CIF_SPACE];
         MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
         ASSIGN(dpnew,scalefactor,dpnext,DBSpaceBits,
    		DBZeroTypeBits,why,scalefactor,
		DRC_FORWARD,layer[1]);
         drcCifRules[layer[0]][DRC_CIF_SPACE] = dpnew;
    }
    
    return ((centidistance+scalefactor-1)/scalefactor);
}


drcCifInit()
{
     int	i;

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

drcCifFinal()
{
     int i;

     for (i=0; i != MAXCIFLAYERS; i++)
     {
          DRCCifCookie *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(arg)
	struct drcClientData	*arg;

{
     Rect		*checkRect = arg->dCD_rect;
     Rect		cifrect;
     int		scale;
     CIFStyle		*tmp;
     int		i,j;
     int oldTiles;

     if (drcCifValid == FALSE) return;
     scale = drcCifStyle->cs_scaleFactor;
     tmp = CIFCurStyle;
     CIFCurStyle = drcCifStyle;
     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,&drcCifGenLayers,TRUE,FALSE); */
     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;
		    
		    mask = (j == DRC_CIF_SOLID)?(&DBSpaceBits):&CIFSolidBits;
	  
	            DBSrPaintArea((Tile *) NULL,plane,&cifrect,mask,
	  		drcCifTile, arg);
     	       }
	       
	  }
     }
     arg->dCD_rect = checkRect;
     CIFCurStyle = tmp;
     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)
    register Tile *tile;	/* Tile being examined */
    struct drcClientData *arg;
{
    register DRCCifCookie *cptr;	/* Current design rule on list */
    register 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;

    /*
     * 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 */
    {
	register 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(TiGetType(tpleft) == TiGetType(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, TiGetType(tp)))
			errRect.r_ybot -= cptr->drcc_cdist;

		    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, TiGetType(tp)))
			    errRect.r_ytop += cptr->drcc_cdist;
		    }

		    /*
		     * 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, TiGetType(tpleft)))
			    continue;
		    errRect.r_xtop = edgeX;
		    arg->dCD_initial = tile;
		}
		else
		{
		    /*
		     * 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, TiGetType(tp)))
			errRect.r_ytop += cptr->drcc_cdist;

		    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, TiGetType(tp)))
			    errRect.r_ybot -= cptr->drcc_cdist;
		    }

		    /*
		     * 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, TiGetType(tile)))
			    continue;
		    errRect.r_xbot = edgeX;
		    arg->dCD_initial= tpleft;
		}

		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)
    {
	register 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(TiGetType(tpbot) == TiGetType(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, TiGetType(tp)))
			errRect.r_xtop += cptr->drcc_cdist; 	

		    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, TiGetType(tp)))
			    errRect.r_xbot -= cptr->drcc_cdist; 	
		    }

		    /*
		     * 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, TiGetType(tpbot)))
			    continue;
		    errRect.r_ytop = edgeY;
		    arg->dCD_initial = tile;
		}
		else
		{
		    /*
		     * 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, TiGetType(tp)))
			errRect.r_xbot -= cptr->drcc_cdist;

		    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, TiGetType(tp)))
			    errRect.r_xtop += cptr->drcc_cdist;
		    }

		    /*
		     * 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, TiGetType(tile)))
			    continue;
		    errRect.r_ybot = edgeY;
		    arg->dCD_initial = tpbot;
		}

		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) 
    register Tile *tile;
    register 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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
drcCifArea(argc, argv)
    int argc;
    char *argv[];
{
    char *layers = argv[1];
    int centidistance = atoi(argv[2]);
    int	centihorizon = atoi(argv[3]);
    char *why = drcWhyDup(argv[4]);
    TileTypeBitMask set, setC, tmp1;
    DRCCifCookie *dpnext, *dpnew;
    TileType i, j;
    int plane;
    int	thislayer;
    int scalefactor;

    if (drcCifStyle == NULL)
    {
    	 TechError("Missing cif style for drc\n");
	 return 0;
    } 
    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;
    dpnext = drcCifRules[thislayer][DRC_CIF_SPACE];
    MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
    ASSIGN(dpnew,centihorizon,dpnext,CIFSolidBits,
    		CIFSolidBits,why,centidistance,DRC_AREA|DRC_FORWARD,thislayer);
    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.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
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;
    DRCCifCookie *dpnext, *dpnew;
    TileType i, j;
    int plane;
    int bend;
    int thislayer;
    int scalefactor;

    if (drcCifStyle == NULL)
    {
    	 TechError("Missing cif style for drc\n");
	 return 0;
    } 
    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;
    dpnext = drcCifRules[thislayer][DRC_CIF_SPACE];
    MALLOC(DRCCifCookie *, dpnew, sizeof (DRCCifCookie));
    ASSIGN(dpnew,centidistance,dpnext,CIFSolidBits,
    		CIFSolidBits,why,centidistance,DRC_MAXWIDTH|bend,thislayer);
    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.
 *
 *-------------------------------------------------------------------------
 */
drcCheckCifArea(starttile,arg,cptr)
	Tile	*starttile;
	struct drcClientData	*arg;
	DRCCifCookie	*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);
	     /***
	     DBWAreaChanged(arg->dCD_celldef,&rect, DBW_ALLWINDOWS, 
						    &DBAllButSpaceBits);
	     ***/
	     (*(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);
	    }

     }
     return 0;
}


/*
 *-------------------------------------------------------------------------
 *
 * 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.
 *
 *-------------------------------------------------------------------------
 */
drcCheckCifMaxwidth(starttile,arg,cptr)
	Tile	*starttile;
	struct drcClientData	*arg;
	DRCCifCookie	*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);
		  /***
		  DBWAreaChanged(arg->dCD_celldef,&rect, DBW_ALLWINDOWS, 
						    &DBAllButSpaceBits);
		  ***/
		  (*(arg->dCD_errors))++;
	     }
	 }
	 return 0;
    }
    /* 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);
		  /***
		  DBWAreaChanged(arg->dCD_celldef,&rect, DBW_ALLWINDOWS, 
						    &DBAllButSpaceBits);
		  ***/
		  (*(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);
	    }

     }
     return 0;
}
#endif
