/*
 * mzTech.c --
 *
 * Technology file read-in for the maze router.
 * Processes mzrouter and drc sections of tech file to
 * set up maze routing parameter sets.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1988, 1990 Michael H. Arnold and the 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[] = "$Header: /ufs/repository/magic/mzrouter/mzTech.c,v 1.2 2001/01/12 22:13:03 jsolomon Exp $";
#endif  not lint

#include <stdio.h>
#include "misc/magic.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/doubleint.h"
#include "utils/heap.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "signals/signals.h"
#include "textio/textio.h"
#include "wiring/wiring.h"
#include "utils/utils.h"
#include "tech/tech.h"
#include "utils/malloc.h"
#include "utils/list.h"
#include "windows/windows.h"
#include "main/main.h"
#include "mzrouter/mzrouter.h"
#include "mzrouter/mzInternal.h"

/* Procedures referenced before they are defined 
 * (these procedures are local to this file)
 */
RouteType *mzFindRouteType();
RouteLayer *mzFindRouteLayer();
RouteContact *mzFindRouteContact();

/* Techspacing structure -
 * 
 * This structure is used to build a temporary list of spacings from
 * spacing lines in the mzrouter section of the technology file.  Spacings are
 * copied to RouteTypes after technology readin completes.
 */
typedef struct techspacing
{
    RouteType *ts_routeType;
    TileType ts_tileType;
    int ts_spacing;
    struct techspacing *ts_next;
} TechSpacing;
TechSpacing *mzTechSpacingList = NULL;  /* Temporary list of explicit spacings.
					 * Generated by spacing lines in 
					 * mzrouter
					 * section.  Used in mzTechFinal to
					 * set spacings ispacings n RouteTypes
					 * after technology readin completes.
					 */

/* mask of all routetypes - referenced in this file only */
TileTypeBitMask mzRTypesMask;      /* And of bitmaps for tileTypes of
				    * all routeTypes of all styles
				    * (note image types for
				    * contacts included).
				    */


/*
 * ----------------------------------------------------------------------------
 *
 * MZTechInit --
 *
 * Initialize the technology datastructures for the maze router.
 * This routine is made a client of the tech module in main/main.c.
 * It is called when the beginning of the "mzrouter" section is encountered.
 *
 * Results:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
MZTechInit()
{
    MazeStyle *style;

    /* Clear out any old information */
    if (mzStyles != NULL)
    {
	for (style = mzStyles; style != NULL; style = style->ms_next)
	{
	    ListDealloc(style->ms_spacingL);
	    freeMagic(style->ms_name);
	    freeMagic(style);
	}
    }

    /* Initially no route types defined */
    mzRTypesMask = DBZeroTypeBits;
  
    /* Initially no maze styles are defined */
    mzStyles = NULL;

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * MZTechFinal --
 *
 * Close out final style.
 * This routine is made a client of the tech module in main/main.c.
 * It is called when the end of the "mzrouter" section is encountered.
 *
 * Results:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
MZTechFinal()
{
    if(mzStyles)
    {
	mzStyleEnd();
    }

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzAfterTech --
 *
 * Finish up tech datastructures.
 * This routine called by MZInit after tech file readin.
 *
 * Results:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
mzAfterTech()
{
    MazeStyle *style;
    RouteType *rT;
    int i, maxSpacing;

    /* Now that DRC tech readin is complete, adjust spacings for all styles */
    for(style=mzStyles; style!=NULL; style=style->ms_next)
    {
	List *l;

	/* add (non SUBCELL) spacings in spacingL to spacing 
	 * arrays in RouteTypes */
	for(l=style->ms_spacingL; l!=NULL; l=LIST_TAIL(l))
	{
	    TechSpacing *tS = (TechSpacing *) LIST_FIRST(l);

	    if(tS->ts_tileType != TT_SUBCELL)
	    {
		tS->ts_routeType->rt_spacing[tS->ts_tileType] = tS->ts_spacing;
	    }
	}

	/* Compute spacing to unexpanded subcells for each routeType
	 * = maximum of all spacings for that route type
	 */
	for(rT=style->ms_parms.mp_rTypes; rT!=NULL; rT=rT->rt_next)
	{
	    maxSpacing = 0;
	    for(i=0;i<TT_MAXTYPES;i++)
	    maxSpacing = MAX(maxSpacing,rT->rt_spacing[i]);
	    rT->rt_spacing[TT_SUBCELL] = maxSpacing;
	}

	/* add SUBCELL spacings in spacingL to spacing 
	 * arrays in RouteTypes */
	for(l=style->ms_spacingL; l!=NULL; l=LIST_TAIL(l))
	{
	    TechSpacing *tS = (TechSpacing *) LIST_FIRST(l);

	    if(tS->ts_tileType == TT_SUBCELL)
	    {
		tS->ts_routeType->rt_spacing[tS->ts_tileType] = tS->ts_spacing;
	    }
	}

	/* free up spacing list */
	ListDeallocC(style->ms_spacingL);
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * MZTechLine --
 *
 * This routine is made a client of the tech module in main/main.c.
 * It is invoked during technology file readin, once for each line
 * in the mzrouter section of the technology file.
 *
 * On each invocation one line of the "mzrouter" section is processed.
 *
 * Results:
 *	TRUE always.
 *
 * Side effects:
 *	An element  is built and added to the RouteLayers or RouteContacts
 *      list.  Or in the case of spacing request, the spacing is added to
 *      the deferred spacing list for processing in mzTechFinal after
 *      technology readin is completed.
 *
 * Note: 
 *    The mzrouter section of the technology file must preceed the drc
 *    section.  Thus the route layers and contacts are known prior
 *    to design rule processsing.
 * 
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
bool
MZTechLine(sectionName, argc, argv)
    char *sectionName;	/* Unused */
    int argc;
    char *argv[];
{
    if(strcmp(argv[0], "style") == 0)
    {
        mzTechStyle(argc, argv);
    }
    else if(mzStyles == NULL)
    {
	TechError("Missing style line.\n");
    }
    else if (strcmp(argv[0], "layer") == 0)
    {
	mzTechLayer(argc, argv);
    }
    else if (strcmp(argv[0], "contact") == 0)
    {
	mzTechContact(argc, argv);
    }
    else if (strcmp(argv[0], "notactive") == 0)
    {
	mzTechNotActive(argc, argv);
    }
    else if (strcmp(argv[0], "spacing") == 0)
    {
        mzTechSpacing(argc, argv);
    }
    else if (strcmp(argv[0], "search") == 0)
    {
        mzTechSearch(argc, argv);
    }
    else if (strcmp(argv[0], "width") == 0)
    {
        mzTechWidth(argc, argv);
    }
    else
    {
        TechError("Unrecognized keyword: \"%s\"\n", argv[0]);
    }

    return (TRUE);
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzTechStyle --
 *
 * Process a "style" line from the "mzrouter" section of the tech file.
 * Each set of lines in the "mzrouter" section of the tech file is headed
 * by a style line that gives a name to the set of parameters.
 * Style lines have the form:
 *
 *	style <name>
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Complete previous style and initial this one.
 *
 * ----------------------------------------------------------------------------
 */

Void
mzTechStyle(argc, argv)
    int argc;
    char *argv[];
{

    /* if there is a previous style, complete processing on it */
    if(mzStyles)
    {
	mzStyleEnd();
    }

    /* alloc new style */
    {
	MazeStyle *new;
	MALLOC(MazeStyle*, new, sizeof (MazeStyle));
	
	new->ms_name = StrDup(NULL, argv[1]);
	new->ms_spacingL = NULL;
	new->ms_next = mzStyles;
	mzStyles = new;
    }

    /* set default parameters */
    mzSetParmDefaults(&(mzStyles->ms_parms));

    /* reset temporary lists */
    mzRouteLayers = NULL;
    mzRouteContacts = NULL;
    mzRouteTypes = NULL;

    return;
} 


/*
 * ----------------------------------------------------------------------------
 *
 * mzStyleEnd --
 *
 * Close out style.  (Saves temporary lists in parms for style.) 
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

Void
mzStyleEnd()
{
    /* Reverse mzRouteLayer, mzRouteContacts and mzRouteTypes lists
     * so they are displayed in order they appear in tech file.
     */
    {
	RouteLayer *newRLList;
	RouteContact *newRCList;
	RouteType *newRTList;

	newRLList = NULL;
	while(mzRouteLayers!=NULL)
	{
	    RouteLayer *rL = mzRouteLayers;
	    mzRouteLayers = rL->rl_next;
	    rL->rl_next = newRLList;
	    newRLList = rL;
	}
	mzRouteLayers = newRLList;

	newRCList = NULL;
	while(mzRouteContacts!=NULL)
	{
	    RouteContact *rC = mzRouteContacts;
	    mzRouteContacts = rC->rc_next;
	    rC->rc_next = newRCList;
	    newRCList = rC;
	}
	mzRouteContacts = newRCList;

	newRTList = NULL;
	while(mzRouteTypes!=NULL)
	{
	    RouteType *rT = mzRouteTypes;
	    mzRouteTypes = rT->rt_next;
	    rT->rt_next = newRTList;
	    newRTList = rT;
	}
	mzRouteTypes = newRTList;
    }

    /* Copy lists to parm structure for this style */
    mzStyles->ms_parms.mp_rLayers = mzRouteLayers;
    mzStyles->ms_parms.mp_rContacts = mzRouteContacts;
    mzStyles->ms_parms.mp_rTypes = mzRouteTypes;

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzSetParmDefaults --
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Sets default parameter values.
 *	
 * ----------------------------------------------------------------------------
 */

Void
mzSetParmDefaults(parms)
MazeParameters *parms;
{

    /* initialize penalty factor for excess cost togo */
    parms->mp_penalty.rf_mantissa = DEF_PENALTY_MANTISSA;
    parms->mp_penalty.rf_nExponent = DEF_PENALTY_NEXPONENT;

    /* Initialize other Search Control Parameters */
    DOUBLE_CREATE(parms->mp_wRate, DEF_WINDOW_RATE);
    DOUBLE_CREATE(parms->mp_wWidth, DEF_WINDOW_WIDTH);
    DOUBLE_CREATE(parms->mp_bloomDeltaCost, DEF_BLOOM_DELTA_COST);

    /* initialize blockage generation increment */
    parms->mp_boundsIncrement = DEF_BOUNDS_INCREMENT;

    /* Estimation flags (if set nontrivial estimation used) */
    parms->mp_estimate = DEF_ESTIMATE;

    /* dest expansion flag, if set dest areas are expanded to include
     * all electrically connected areas.
     */
    parms->mp_expandDests = DEF_EXPAND_DESTS;

    /* If set, only hints in toplevel cell are recognized */
    parms->mp_topHintsOnly = DEF_TOP_HINTS_ONLY;
    
    /* Maximum penetration into node (or subcell) blockage to reach
     * destination.
     */
    parms->mp_maxWalkLength = DEF_MAX_WALK_LENGTH;

    /* If nonnull, limits search area for performance 
     * NOTE:   IF NONNULL, IT IS THE USERS RESPONSIBILITY TO CONFINE THE 
     *         ROUTE TO WITHIN THIS AREA BY FENCES.
     */
    parms->mp_boundsHint = NULL;

    /* Amount of messages printed */
    parms->mp_verbosity = DEF_VERBOSITY;

    /* If positive, limit on number of blooms during search */
    parms->mp_bloomLimit = DEF_BLOOM_LIMIT;

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzTechLayer --
 *
 * Process a "layer" line from the "mzrouter" section of the tech file.
 * A layer line defines a route layer.  It has the following form:
 *
 *	layer layername hcost vcost jogcost hintcost
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Builds and adds an element to the RouteLayers list.
 *	RouteType also added to RouteTypes list.
 *
 * ----------------------------------------------------------------------------
 */

Void
mzTechLayer(argc, argv)
    int argc;
    char *argv[];
{
    RouteLayer *new;
    TileType tileType;

    /* Check the argument count */
    if (argc!=6)
    {
	TechError("Malformed \"layer\" line, should be:  \"layer name hCost vCost jogCost hintCost\"\n");
	return;
    }

    /* get the tiletype */
    tileType = DBTechNoisyNameType(argv[1]);
    if (tileType < 0) return;

    /* make sure we don't already have a routeLayer on same plane */
    {
	RouteLayer *rL;

	for(rL=mzRouteLayers; rL!=NULL; rL=rL->rl_next)
	{
	    if(DBTypePlaneTbl[rL->rl_routeType.rt_tileType] ==
	       DBTypePlaneTbl[tileType])
	    {
		TechError("Attempt to define two route layers on same plane:  %s and %s\n", 
			  DBTypeLongNameTbl[tileType], 
			  DBTypeLongNameTbl[rL->rl_routeType.rt_tileType]);
		return;
	    }
	}
    }

    /* Allocate  new route layer */
    CALLOC(RouteLayer *, new, sizeof (RouteLayer));

    /* Initialize RouteType section */
    mzInitRouteType(&(new->rl_routeType),tileType);

    /* Initialize list of contacts connecting this layer to NULL */
    new->rl_contactL = NULL;

    /* set the plane */
    new->rl_planeNum = DBPlane(new->rl_routeType.rt_tileType);
    if (new->rl_planeNum < 0)
    {
	TechError("Type \"%s\" appears on more than one plane\n", argv[1]);
	return;
    }

    /* set hCost */
    if (!StrIsInt(argv[2]))
    {
	TechError("Cost arguments to \"layer\" line must be numeric\n");
	return;
    }
    new->rl_hCost = atoi(argv[2]);
    if (new->rl_hCost <= 0)
    {
	TechError("hCost must be > 0\n");
	return;
    }

    /* set vCost */
    if (!StrIsInt(argv[3]))
    {
	TechError("Cost arguments to \"layer\" line must be numeric\n");
	return;
    }
    new->rl_vCost = atoi(argv[3]);
    if (new->rl_vCost <= 0)
    {
	TechError("vCost must be > 0\n");
	return;
    }

    /* set jogCost */
    if (!StrIsInt(argv[4]))
    {
	TechError("Cost arguments to \"layer\" line must be numeric\n");
	return;
    }
    new->rl_jogCost = atoi(argv[4]);
    if (new->rl_jogCost <= 0)
    {
	TechError("jogCost must be > 0\n");
	return;
    }

    /* set hintCost */
    if (!StrIsInt(argv[5]))
    {
	TechError("Cost arguments to \"layer\" line must be numeric\n");
	return;
    }
    new->rl_hintCost = atoi(argv[5]);
    if (new->rl_hintCost <= 0)
    {
	TechError("hintCost must be > 0\n");
	return;
    }

    /* Add new route layer to list */
    new->rl_next = mzRouteLayers;
    mzRouteLayers = new;

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzTechNotActive --
 *
 * Process a "notactive" line from the "mzrouter" section of the tech file.
 * A notactive line specifies routetype(s) that are to be initially unavail.
 * to the router.
 *
 *	spacing routeType1 ... [routeTypen]
 *
 * Valid spacings are nonnegative integers, or "nil: if no spacing applies.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Build and add an element to spacing list
 *
 * ----------------------------------------------------------------------------
 */

Void
mzTechNotActive(argc, argv)
    int argc;
    char *argv[];
{
    int argI;
    TileType tileType;
    RouteType *rT;

    /* check number of arguments */
    if(argc < 2)
    {
	TechError("Bad form on mzroute notactive.\n");
	TechError("Usage: notactive routeType1 ... [routeTypen]\n");
    return;
    }

    /* process args */
    for(argI = 1; argI<argc; argI += 1)
    {
	/* convert arg to routeType */
	{
	    tileType = DBTechNoisyNameType(argv[argI]);
	    if(tileType < 0) continue;
	    rT = mzFindRouteType(tileType);
	    if(rT==NULL)
	    {
		TechError("Unrecognized route type: \"%.20s\"\n",
			  argv[argI]);
		continue;
	    }
	}

	/* set to not active */
	rT->rt_active = FALSE;
    }

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzTechSpacing --
 *
 * Process a "spacing" line from the "mzrouter" section of the tech file.
 * A spacing line defines spacings from a routetype (layer or contact) to
 * other type(s).  These spacings are stored on a list on technology readin
 * and then actually set in the routeType structure after technology readin
 * is complete.  This is so they will overide default values based on the
 * DRC section.
 *
 *	spacing routeType type1 spacing1 ... [typen] [spacingn]
 *
 * Valid spacings are nonnegative integers, or "nil: if no spacing applies.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Build and add an element to spacing list
 *
 * ----------------------------------------------------------------------------
 */

Void
mzTechSpacing(argc, argv)
    int argc;
    char *argv[];
{
    register TechSpacing *new;
    TileType tileType;
    RouteType *rT;
    int argI, value, which;
    char *s;

    /* Subcell Table */
    static struct
    {
	char	*sT_name;	/* name of value */
	int     sT_value;	/* corresponding interger value */
    } subcellTable[] = {
	"subcell",		TT_SUBCELL,
	0
    };

    /* check number of arguments */
    if(argc <4 || ODD(argc))
    {
	TechError("Bad form on mzroute spacing.\n");
	TechError("Usage: spacing routeType type1 spacing1 ... [typen spacingn]\n"); 
    return;
    }

    /* convert first arg to routeType */
    tileType = DBTechNoisyNameType(argv[1]);
    if(tileType < 0) return;
    rT = mzFindRouteType(tileType);
    if(rT==NULL)
    {
	TechError("Unrecognized route type: \"%.20s\"\n",
	    argv[1]);
	return;
    }

    for(argI = 2; argI<argc; argI += 2)
    {
	/* convert argI to type */
	tileType = DBTechNameType(argv[argI]);
	if(tileType<0) 
	{
	    /* if not a real type, check to see if "SUBCELL" */
	    which = LookupStruct(
		argv[argI],
		(LookupTable *) subcellTable,
		sizeof subcellTable[0]);
	    if ( which>= 0)
	        tileType = TT_SUBCELL;
	}
	if(tileType<0) 
	{
	    TechError("Unrecognized layer (type): \"%.20s\"\n", 
		argv[argI]);
	    continue;
	}

	/* convert argI+1 to  value */
	s = argv[argI+1];
	value = -2;
	if (StrIsInt(s)) 
	{
	    value = atoi(s);
	    if (value < 0)
	    {
		TechError("Bad spacing value: %d\n",value);
		TechError("Valid values are nonnegative integers and \"NIL\".\n");
		return;
	    }
	}
	else if (strcmp(s,"NIL")==0)
	{
	    value = -1;
	}
	else
	{
	    TechError("Bad spacing value: %s\n",s);
	    TechError("Valid values are nonnegative integers and \"NIL\".\n");
	    return;
	}

	/* add entry to spacing list */
	MALLOC(TechSpacing*, new, sizeof (TechSpacing));
	new->ts_routeType = rT;
	new->ts_tileType = tileType;
	new->ts_spacing = value;
	LIST_ADD(new, mzStyles->ms_spacingL);
    }

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzTechSearch --
 *
 * Process a "search" line from the "mzrouter" section of the tech file.
 * A search line sets several parameters controling the search.
 * It has the form: 
 *
 *	search <rate> <width> <penalty>
 *
 * Rate and width must be positive integers, penalty a non-negative decimal.
 * Search lines are optional, if none is given for a style default values
 * are assigned.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Set rate width and penalty parms for this style.
 *
 * ----------------------------------------------------------------------------
 */

Void
mzTechSearch(argc, argv)
    int argc;
    char *argv[];
{
    /* check number of arguments */
    if(argc != 4)
    {
	TechError("Bad form on search.\n");
	TechError("Usage: search <rate> <width> <penalty>\n");
	return;
    }

    /* set WINDOW RATE */
    if (!StrIsInt(argv[1])) 
    {
	TechError("Bad rate: %s\n",argv[1]);
	TechError("Rate must be a positive integer.\n");
    }
    else
    {
	int value = atoi(argv[1]);
	if (value <= 0)
	{
	    TechError("Bad rate: %d\n",value);
	    TechError("Rate must be a positive integer.\n");
	}
	else
	{
	    DOUBLE_CREATE(mzStyles->ms_parms.mp_wRate,value);
	}
    }

    /* set WINDOW WIDTH */
    if (!StrIsInt(argv[2])) 
    {
	TechError("Bad width: %s\n",argv[2]);
	TechError("Width must be a positive integer.\n");
    }
    else
    {
	int value = atoi(argv[2]);
	if (value <= 0)
	{
	    TechError("Bad width: %d\n",value);
	    TechError("Width must be a positive integer.\n");
	}
	else
	{
	    DOUBLE_CREATE(mzStyles->ms_parms.mp_wWidth,value);
	}
    }

    /* set PENALTY */
    {
	float value;

	if(sscanf(argv[3],"%f",&value)!=1)
	{
	    TxError("Bad penalty value: %s\n",argv[3]);
	    TxError("Penalty must be non-negative decimal.\n");
	}
	else
	{
	    if(value<0)
	    {
		TxError("Bad penalty value: %f\n", value);
		TxError("Penalty must be non-negative decimal.\n");
	    }
	    else
	    {
		mzStyles->ms_parms.mp_penalty.rf_mantissa 
		= (int) (value *
			 (1<<mzStyles->ms_parms.mp_penalty.rf_nExponent));
	    }
	}
    }

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzTechWidth --
 *
 * Process  "width" line from the "mzrouter" section of the tech file.
 * A width line defines a routing width for a given route type.  It
 * has the form.
 *
 *	width <routeType> <width>
 *
 * Valid widths are positive integers.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Set width for given routetype in current style.
 *
 * ----------------------------------------------------------------------------
 */

Void
mzTechWidth(argc, argv)
    int argc;
    char *argv[];
{
    TileType tileType;
    RouteType *rT;
    int value;

    /* check number of arguments */
    if(argc !=3)
    {
	TechError("Bad form on mzroute width.\n");
	TechError("Usage: width <routeType> <width>\n");
	return;
    }

    /* convert first arg to routeType */
    tileType = DBTechNoisyNameType(argv[1]);
    if(tileType < 0) return;
    rT = mzFindRouteType(tileType);
    if(rT==NULL)
    {
	TechError("Unrecognized route type: \"%.20s\"\n",
	    argv[1]);
	return;
    }
    
    /* convert 2nd arg to int */
    {
	if (!StrIsInt(argv[2])) 
	{
	    TechError("Bad width: %s\n",argv[2]);
	    TechError("Width must be a positive integer.\n");
	    return;
	}

	value = atoi(argv[2]);
	if (value <= 0)
	{
	    TechError("Bad width: %d\n",value);
	    TechError("Width must be a positive integer.\n");
	    return;
	}
    }

    /* set width */
    rT->rt_width = value;

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzTechContact --
 *
 * Process a "contact" line from the "mzrouter" section of the tech file.
 * A contact line defines a contact between route layers.  It has the form:
 *
 *	contact conlayer routelayer1 routelayer2 cost
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Build and add an element to RouteContacts list.
 *
 * ----------------------------------------------------------------------------
 */

Void
mzTechContact(argc, argv)
    int argc;
    char *argv[];
{
    register RouteContact *new;
    TileType type, tileType;

    /* check arg count */
    if (argc !=  5)
    {
	TechError("Malformed \"contact\" line, should be:  contact layer layer1 layer2 cost\n");
	return;
    }

    /* get tiletype */
    tileType = DBTechNoisyNameType(argv[1]);
    if(tileType < 0) return;

    /* allocate new route contact */
    CALLOC(RouteContact *, new, sizeof (RouteContact));

    /* initialize RouteType section:
     * sets route type, initializes spacing array, etc.
     */
    mzInitRouteType(&(new->rc_routeType),tileType);

    /* set first route layer */
    type = DBTechNoisyNameType(argv[2]);
    if(type<0) return;
    new->rc_rLayer1 = mzFindRouteLayer(type);
    if (new->rc_rLayer1==NULL)
    {
	TechError("route layer must be declared before used in contact.\n");
	return; 
    }

    /* add this contact to contacts list of first route layer */
    LIST_ADD(new, new->rc_rLayer1->rl_contactL);

    /* set second route layer */
    type = DBTechNoisyNameType(argv[3]);
    if(type<0) return;
    new->rc_rLayer2 = mzFindRouteLayer(type);
    if (new->rc_rLayer2==NULL)
    {
	TechError("route layer must be declared before used in contact.\n");
	return;
    }

    /* add this contact to contacts list of second route layer */
    LIST_ADD(new, new->rc_rLayer2->rl_contactL);

    /* set cost */
    if (!StrIsInt(argv[4]))
    {
	TechError("Cost argument to \"contact\" line must be numeric\n");
	return;
    }
    new->rc_cost = atoi(argv[4]);
    if (new->rc_cost <= 0)
    {
	TechError("Cost must be > 0\n");
	return;
    }

    /* add route contact to list */
    new->rc_next = mzRouteContacts;
    mzRouteContacts = new;

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * MZDRCInit --
 *
 * Initialization before processing the "drc" section for the 
 * interactive router.  Nothing to do here.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
MZDRCInit()
{
    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * MZDRCFinal --
 *
 * Cleanup after processing the "drc" section for the 
 * interactive router.  Nothing to do here.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
MZDRCFinal()
{
    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * MZDRCLine --
 *
 * Process a single line of the "drc" section, to garner spacing info.
 * for the route layers.
 * 
 * This routine is made a client of the tech module in main/main.c, and
 * is called once for each line in the drc section of the technogy file.
 * 
 * Results:
 *	TRUE always.
 *
 * Side effects:
 *	Add spacing info to the RouteLayers.
 *
 * Organization:
 *	We select a procedure based on the first keyword (argv[0])
 *	and call it to do the work of implementing the rule.  Each
 *	such procedure is of the following form:
 *
 *	Void
 *	proc(argc, argv)
 *	    int argc;
 *	    char *argv[];
 *	{
 *	}
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
bool
MZDRCLine(sectionName, argc, argv)
    char *sectionName;	/* Unused */
    int argc;
    char *argv[];
{
    Void mzWidthRule();
    Void mzSpacingRule();
    Void mzEdgeRule();

    int which;
    static struct
    {
	char	*rk_keyword;	/* Initial keyword */
	int	 rk_minargs;	/* Min # arguments */
	int	 rk_maxargs;	/* Max # arguments */
	int    (*rk_proc)();	/* Procedure implementing this keyword */
    } ruleKeys[] = {
	"edge",		 8,	9,	mzEdgeRule,
	"edge4way",	 8,	9,	mzEdgeRule,
	"spacing",	 6,	6,	mzSpacingRule,
	"width",	 4,	4,	mzWidthRule,
	0
    }, *rp;

    /*
     * Leave the job of printing error messages to the DRC tech file reader.
     */
    which = LookupStruct(argv[0], (LookupTable *) ruleKeys, sizeof ruleKeys[0]);
    if (which >= 0)
    {
	rp = &ruleKeys[which];
	if (argc >= rp->rk_minargs && argc <= rp->rk_maxargs)
	    (*rp->rk_proc)(argc, argv);
    }

    return (TRUE);
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzWidthRule --
 *
 * Process a width rule.   
 * This is of the form:
 *
 *	width types width error 
 * e.g, width diff,dmc,dfet 2 "diffusion width must be at least 2"
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Add width info to appropriate RouteType strucs.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
mzWidthRule(argc, argv)
    int argc;
    char *argv[];
{
    int width = atoi(argv[2]);
    TileTypeBitMask typeSet;
    register TileType t;

    /* convert type list to mask */
    DBTechNoisyNameMask(argv[1], &typeSet);

    /* for types on list on width rule that pertain to routeTypes */
    for (t = TT_SELECTBASE; t < DBNumTypes; t++) 
    {
	if (TTMaskHasType(&typeSet, t) && TTMaskHasType(&mzRTypesMask, t))
	{
	    MazeStyle *style;
	    
	    /* for routetypes that t is an image of */
	    for(style=mzStyles; style!=NULL; style=style->ms_next)
	    {
		RouteType *rT;

		for (rT=style->ms_parms.mp_rTypes; rT!=NULL; rT = rT->rt_next)
		{
		    if(TTMaskHasType(&(DBLayerTypeMaskTbl[rT->rt_tileType]),t))
		    {
			/* update width in RouteType */
			rT->rt_width = MAX(rT->rt_width,width);
		    }
		}
	    }
	}
    }

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzSpacingRule --
 *
 * Process a spacing rule.
 * This is of the form:
 *
 *	spacing layers1 layers2 distance adjacency why
 * e.g, spacing metal,pmc/m,dmc/m metal,pmc/m,dmc/m 4 touching_ok \
 *		"metal spacing must be at least 4"
 *
 * Adjacency may be either "touching_ok" or "touching_illegal",
 * but this doesn't matter here.  
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Add spacing info to appropriate RouteType strucs.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
mzSpacingRule(argc, argv)
    int argc;
    char *argv[];
{
    int distance = atoi(argv[3]);
    TileTypeBitMask set1, set2;
    register int t, t1, t2;

    /* convert layer lists to masks */
    DBTechNoisyNameMask(argv[1], &set1);
    DBTechNoisyNameMask(argv[2], &set2);

    /* for types in set1 that pertain to routeTypes */
    for (t = TT_SELECTBASE; t < DBNumTypes; t++) 
    {
	if (TTMaskHasType(&set1, t) && TTMaskHasType(&mzRTypesMask, t))
	{
	    MazeStyle *style;
	    
	    /* for routetypes that t is an image of */
	    for(style=mzStyles; style!=NULL; style=style->ms_next)
	    {
		RouteType *rT;

		/* for routetypes that t is an image of */
		for (rT=style->ms_parms.mp_rTypes; rT!=NULL; rT = rT->rt_next)
		{
		    if(TTMaskHasType(&(DBLayerTypeMaskTbl[rT->rt_tileType]),t))
		    {

			/* for tiletypes in set2 */
			for (t2 = TT_SELECTBASE; t2 < DBNumTypes; t2++)
			{
			    if (TTMaskHasType(&set2, t2)) 
			    {
				/* interpolate spacing between route type in 
				 * set1 and arbitarary tiletype in set2 into 
				 * routeLayer data structure
				 */
				mzUpdateSpacing(rT,t2,distance);
			    }
			}
		    }
		}
	    }
	}
    }

    /* for types in set2 that pertain to routeTypes */
    for (t = TT_SELECTBASE; t < DBNumTypes; t++) 
    {
	if (TTMaskHasType(&set2, t) && TTMaskHasType(&mzRTypesMask, t))
	{
	    MazeStyle *style;
	    
	    /* for routetypes that t is an image of */
	    for(style=mzStyles; style!=NULL; style=style->ms_next)
	    {
		RouteType *rT;

		/* for routetypes that t is an image of */
		for (rT=style->ms_parms.mp_rTypes; rT!=NULL; rT = rT->rt_next)
		{
		    if(TTMaskHasType(&(DBLayerTypeMaskTbl[rT->rt_tileType]),t))
		    {
			/* for tiletypes in set1 */
			for (t1 = TT_SELECTBASE; t1 < DBNumTypes; t1++)
			{
			    if (TTMaskHasType(&set1, t1))
			    {
				/* interpolate spacing between route layer 
				 * in set2 and arbitarary tiletype in set1 
				 * into routeLayer data structure
				 */
				mzUpdateSpacing(rT,t1,distance);
			    }
			}
		    }
		}
	    }
	}
    }

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzEdgeRule --
 * ******************* NOTE CURRENTLY THIS FUNC IS A NO OP. ***************
 * Process a primitive edge rule.
 *
 * This is of the form:
 *
 * edge types1 types2 dist okTypes cornerTypes cornerDist errorMsg [plane]
 * or
 * edge4way layers1 layers2 dist okTypes cornerTypes cornerDist errorMsg 
 *        [plane]
 * e.g, 
 * edge poly,pmc s 1 diff poly,pmc "poly-diff separation must be 2"
 *
 * An "edge" rule is applied only down and to the left.
 * An "edge4way" rule is applied in all four directions.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Updates Spacing info associated with RouteTypes.
 *
 * ----------------------------------------------------------------------------
 */
    /*ARGSUSED*/
mzEdgeRule(argc, argv)
    int argc;
    char *argv[];
{
#ifdef notdef
    TileTypeBitMask types1Set, types2Set, okTypesSet, cornerTypesSet;
    TileTypeBitMask tmp1;
    register TileType t1,tCheck;
    RouteType *rT;
    int pNum, checkPlane;
    int distance;

    /* give names to args */
    bool isFourWay = (strcmp(argv[0], "edge4way") == 0);
    char * types1 = argv[1];
    char * types2 = argv[2];
    int dist = atoi(argv[3]);
    char * okTypes = argv[4];
    char * cornerTypes = argv[5];
    int cornerDist = atoi(argv[6]);
    char * errorMsg = argv[7];
    char * plane = ((argc==9) ? argv[8] : NULL);

    /* convert type lists to masks */
    DBTechNoisyNameMask(types1, &types1Set);
    DBTechNoisyNameMask(types2, &types2Set);
    DBTechNoisyNameMask(okTypes, &okTypesSet);
    DBTechNoisyNameMask(cornerTypes, &cornerTypesSet);

    /*---- Find plane common to types1 and types2
     *---- (the types of these sets should be on a common plane,
     *----  if not, we just return)
     *----
     */

    /* first find plane common to types1Set */
    for (pNum = PL_SELECTBASE; pNum < DBNumPlanes; pNum++)
	if (DBTechSubsetLayers(types1Set, DBPlaneTypes[pNum], &tmp1))
	    break;

    /* if none, return */
    if (pNum >= DBNumPlanes)
	return;

    /* if types2Set not on same plane, return */
    if (!DBTechSubsetLayers(types2Set, DBPlaneTypes[pNum], &tmp1))
	return;

    /* Get images of types1 and types2 on common plane
     * (i.e. exclude contact images on other planes) 
     */
    ASSERT(DBTechSubsetLayers(types1Set, DBPlaneTypes[pNum], &types1Set),
	"mzEdgeRule");
    ASSERT(DBTechSubsetLayers(types2Set, DBPlaneTypes[pNum], &types2Set),
	"mzEdgeRule");
    
    /* If an explicit check plane was specified, use it, else use
     * plane of set1 and set2 (computed above).
     */
    if(plane!=NULL) 
    {
	checkPlane = DBTechNamePlane(plane);
	if (checkPlane<0) return;
    }
    else
    {
	checkPlane = pNum;
    }
	
    /* Get the images of everything in okTypes on the check plane */
    if (!DBTechSubsetLayers(okTypesSet, DBPlaneTypes[checkPlane], &okTypesSet))
	return;

    /*
     * PROCESS CASE1:  types2 includes space. 
     * =>
     * Spacings are recorded between the types1 and routeTypes on
     * checkPlane that are not in okTypes.
     *
     * In case1 no distinction is made between edge and edge4way
     * rules and the max of dist and cornerDist is used as the spacing
     * distance, e.g. the rules are interpeted conservatively as
     * simple spacing rules.
     */
    if (TTMaskHasType(&types2Set, TT_SPACE))
    {
	/* spacing distance is max of dist and cornerDist */
	distance = MAX(dist,cornerDist);
	
	/* for each type in types1 */
	for (t1 = TT_SELECTBASE; t1 < DBNumTypes; t1++)
	    if (TTMaskHasType(&types1Set, t1))
	    {
		/* for each routeType on the checkPlane but not in setOK */
		for (tCheck = TT_SELECTBASE; tCheck < DBNumTypes; tCheck++)
		    if(!TTMaskHasType(&okTypesSet,tCheck) && 
			TTMaskHasType(&mzRTypesMask,tCheck) &&
			DBPlane(tCheck)==checkPlane)
		    {
			/* add spacing to routeType struc */
			rT=mzFindRouteType(tCheck);
			ASSERT(rT!=NULL,"mzEdgeRule");
			mzUpdateSpacing(rT,t1,distance);
		    }
	    }
    }

    return;
#endif notdef
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzInitRouteType --
 *
 * Initialize RouteType struc.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Element added to mzRouteTypes list.
 *	Types ORed into mzRTypesMask.
 *
 * ----------------------------------------------------------------------------
 */

mzInitRouteType(rT,tileType)
    RouteType *rT;
    TileType tileType;
{
    int t;

    /* Get mask of all image types of routeType.
     * RouteLayers will have one bit set in mask.  RouteContacts will have 
     * two bits set, one for the contact "home" type, and one for the contact
     * image type on the plane it connects to.
     */
    TileTypeBitMask *imageTypeMask = &(DBLayerTypeMaskTbl[tileType]);

    /* set tileType */
    rT->rt_tileType = tileType;

    /* initialize as active */
    rT->rt_active = TRUE;
    
    /* initialize spacing array (gives spacing between 
     * RouteType and all tiletypes) 
     */
    for (t=0;t<TT_MAXTYPES;t++)
    {
	/* If type "t" is on home plane of RouteType or 
	 * plane of image of RouteContact */
      if( DBTypePlaneTbl[t] >= 0 ) {
	if(TTMaskIntersect(&(DBPlaneTypes[DBTypePlaneTbl[t]]),imageTypeMask)
	    && t!=TT_SPACE)
	{
	    /* initialize to zero spacing - i.e. no overlap allowed */
	    rT->rt_spacing[t] = 0;
	}
	else
	{
	    /* otherwise no default spacing restriction applys */
	    rT->rt_spacing[t] = -1;
	}
      }
    }

    /* Initialize Blockage Planes */
    rT->rt_hBlock = DBNewPlane((ClientData) TT_SPACE);
    rT->rt_vBlock = DBNewPlane((ClientData) TT_SPACE);

    /* Add to RouteType list */
    rT->rt_next = mzRouteTypes;
    mzRouteTypes = rT;

    /* OR type and images into global route types mask */
    TTMaskSetMask(&mzRTypesMask,imageTypeMask);

    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzFindRouteType --
 *
 * Search RouteLayer and RouteContact lists for routeType of given
 * tile type.
 *
 * Results:
 *	pointer to routetype struc if found, else Null.
 *
 * ----------------------------------------------------------------------------
 */

RouteType *
mzFindRouteType(type)
    TileType type;
{
    RouteType *rT;

    /* Search list of routetypes for one with appropriate type */
    for (rT = mzRouteTypes; 
	rT && rT->rt_tileType!=type; 
	rT=rT->rt_next)
	;

    /* return result */
    return(rT);
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzFindRouteLayer --
 *
 * Find routelayer struc for given tiletype.
 *
 * Results:
 *	pointer to routelayer struc if found, else Null.
 *
 * ----------------------------------------------------------------------------
 */

RouteLayer *
mzFindRouteLayer(type)
    TileType type;
{
    RouteLayer *rL;

    /* Search list of routelayers for one with appropriate type */
    for (rL = mzRouteLayers; 
	rL && rL->rl_routeType.rt_tileType!=type; 
	rL=rL->rl_next)
	;

    /* return result */
    return(rL);
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzFindRouteContact --
 *
 * Find routecontact struc for given tiletype.
 *
 * Results:
 *	pointer to routecontact struc if found, else Null.
 *
 * ----------------------------------------------------------------------------
 */

RouteContact *
mzFindRouteContact(type)
    TileType type;
{
    RouteContact *rC;

    /* Search list of routecontacts for one with appropriate type */
    for (rC = mzRouteContacts; 
	rC && rC->rc_routeType.rt_tileType!=type; 
	rC=rC->rc_next)
	;

    /* return result */
    return(rC);
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzUpdateSpacing --
 *
 * Add spacing requirement to RouteType.
 * (RouteTypes are routing layers and contact layers used by the router.)
 *
 * Results:
 *	None.
 *
 * SideEffects:
 *    RouteType struc modifed.
 *
 * ----------------------------------------------------------------------------
 */

mzUpdateSpacing(rType,tType,distance)
    RouteType *rType;	/* Route Type to which spacing applies */
    TileType tType;		/* spacing from this tiletype */
    int distance;		/* min spacing betweeen rType and tType */
{
    rType->rt_spacing[tType] = 
	MAX(rType->rt_spacing[tType],distance);

#ifdef debugmha 
    TxPrintf("UPDATESPACING:\n");
    TxPrintf("\troute type = %s\n",DBTypeLongNameTbl[rType->rt_tileType]);
    TxPrintf("\tother type = %s\n",DBTypeLongNameTbl[tType]);
    TxPrintf("\tdistance = %d\n",distance);
#endif debugmha

    return;
}
