
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResUtils.c,v 1.3 2010/06/24 12:37:56 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/stack.h"
#include "utils/tech.h"
#include "textio/txcommands.h"
#include "resis/resis.h"


/*
 * ---------------------------------------------------------------------------
 *
 * ResFirst -- Checks to see if tile is a contact. If it is, allocate a 
 *	contact structure.
 * 
 *
 * Results: Always returns NULL (in the form of a Region pointer)
 *
 * Side effects:
 *	Memory is allocated by ResFirst.
 *	We cons the newly allocated region onto the front of the existing
 *	region list.
 *
 *
 * -------------------------------------------------------------------------
 */

Region *
ResFirst(tile, arg)
    Tile *tile;
    FindRegion *arg;
{
    ResContactPoint *reg;
    TileType t;
    int i;
    
    if (IsSplit(tile))
    {
	t = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile);
    }
    else
	t = TiGetType(tile);

    if (DBIsContact(t))
    {
	    reg = (ResContactPoint *) mallocMagic((unsigned) (sizeof(ResContactPoint)));
	    reg->cp_center.p_x = (LEFT(tile)+RIGHT(tile))>>1;
	    reg->cp_center.p_y = (TOP(tile)+BOTTOM(tile))>>1;
	    reg->cp_status = FALSE;
	    reg->cp_type = t;
	    reg->cp_width = RIGHT(tile)-LEFT(tile);
	    reg->cp_height = TOP(tile)-BOTTOM(tile);
	    for (i=0; i< LAYERS_PER_CONTACT; i++)
	    {
	    	 reg->cp_tile[i] = (Tile *) NULL;
		 reg->cp_cnode[i] = (resNode *) NULL;
	    }
	    reg->cp_currentcontact = 0;
	    reg->cp_rect.r_ll.p_x = tile->ti_ll.p_x;
	    reg->cp_rect.r_ll.p_y = tile->ti_ll.p_y;
	    reg->cp_rect.r_ur.p_x = RIGHT(tile);
	    reg->cp_rect.r_ur.p_y = TOP(tile);
	    reg->cp_contactTile = tile;
	    /* Prepend it to the region list */
	    reg->cp_nextcontact = (ResContactPoint *) arg->fra_region;
	    arg->fra_region = (Region *) reg;
    }
    return((Region *) NULL);
}

/*
 *--------------------------------------------------------------------------
 *
 * ResEach--
 *
 * ResEach calls ResFirst unless this is the first contact, in which case it 
 * has alreay been processed
 *
 * results: returns 0
 *
 * Side Effects: see ResFirst
 *
 * -------------------------------------------------------------------------
 */

int
ResEach(tile, pNum, arg)
    Tile *tile;
    int pNum;
    FindRegion *arg;
{
   
   if ( ((ResContactPoint *)(arg->fra_region))->cp_contactTile != tile)
   {
   	(void) ResFirst(tile, arg);
   }
   return(0);
}

/*
 *-------------------------------------------------------------------------
 *
 * ResAddPlumbing-- Each tile is a tileJunk structure associated with it
 * to keep track of various things used by the extractor. ResAddPlumbing
 * adds this structure and sets the tile's ClientData field to point to it.
 * If the tile is a device, then a device structure is also added;
 * all connected device tiles are enumerated and their deviceList 
 * fields set to the new structure.
 *
 * Results: always returns 0
 *
 * Side Effects:see above
 *
 *-------------------------------------------------------------------------
 */

int
ResAddPlumbing(tile, arg)
     Tile	*tile;
     ClientData *arg;

{
     tileJunk		*Junk,*junk2;
     static	Stack	*resDevStack=NULL;
     TileType		loctype, t1;
     Tile		*tp1,*tp2,*source;
     resDevice		*resDev;
     ExtDevice		*devptr;
     
     if (resDevStack == NULL)
     	  resDevStack = StackNew(64);

     if (tile->ti_client == (ClientData) CLIENTDEFAULT)
     {
	if (IsSplit(tile))
	    loctype = (SplitSide(tile)) ? SplitRightType(tile) :
			SplitLeftType(tile);
	else
	    loctype = TiGetTypeExact(tile);
	  
	devptr = ExtCurStyle->exts_device[loctype];
     	junk2 = resAddField(tile);
	if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), loctype))
	{
	    int i, nterms;

	    /* Count SD terminals of the device */
	    nterms = 0;
	    for (i = 0; ; i++)
	    {
		if (TTMaskIsZero(&(devptr->exts_deviceSDTypes[i]))) break;
		nterms++;
	    }
	    if (nterms < devptr->exts_deviceSDCount)
		nterms = devptr->exts_deviceSDCount;

	    /* resDev terminals includes device identifier (e.g., gate) and
	     * substrate, so add two to nterms.
	     */
	    nterms += 2;

   	    resDev = (resDevice *) mallocMagic((unsigned)(sizeof(resDevice)));
	    resDev->rd_nterms = nterms;
	    resDev->rd_terminals = (resNode **) mallocMagic(nterms * sizeof(resNode *));
	    for (i=0; i != nterms;i++)
	    	resDev->rd_terminals[i] = (resNode *) NULL;

            resDev->rd_tile = tile;
	    resDev->rd_inside.r_ll.p_x = LEFT(tile);
	    resDev->rd_inside.r_ll.p_y = BOTTOM(tile);
	    resDev->rd_inside.r_ur.p_x = RIGHT(tile);
	    resDev->rd_inside.r_ur.p_y = TOP(tile);
	    resDev->rd_devtype = loctype;
            resDev->rd_tiles = 0;
	    resDev->rd_length = 0;
	    resDev->rd_width = 0;
	    resDev->rd_perim = 0;
	    resDev->rd_area = 0;
	    resDev->rd_status = 0;
            resDev->rd_nextDev = (resDevice *) *arg;
	    *arg = (ClientData)resDev;
	    junk2->deviceList =  resDev;
	    junk2->tj_status |= RES_TILE_DEV;
	       
	    source = NULL;
	    /* find diffusion (if present) to be source contact */

	    /* top */
	    for (tp2= RT(tile); RIGHT(tp2) > LEFT(tile); tp2 = BL(tp2))
	    {
	      	 if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetBottomType(tp2))
		 {
		    junk2->sourceEdge |= TOPEDGE;
		    source = tp2;
		    Junk = resAddField(source);
		    Junk->tj_status |= RES_TILE_SD;
		    break;
		 }
	    }

	    /*bottom*/
	    if (source == NULL)
	    for (tp2= LB(tile); LEFT(tp2) < RIGHT(tile); tp2 = TR(tp2))
	    {
	      	if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetTopType(tp2))
		{
		    junk2->sourceEdge |= BOTTOMEDGE;
		    source = tp2;
		    Junk = resAddField(source);
		    Junk->tj_status |= RES_TILE_SD;
		    break;
		}
	    }

	    /*right*/
	    if (source == NULL)
	    for (tp2= TR(tile); TOP(tp2) > BOTTOM(tile); tp2 = LB(tp2))
	    {
	      	if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetLeftType(tp2))
		{
		    junk2->sourceEdge |= RIGHTEDGE;
		    source = tp2;
		    Junk = resAddField(source);
		    Junk->tj_status |= RES_TILE_SD;
		    break;
		}
	    }

	    /*left*/
	    if (source == NULL)
	    for (tp2= BL(tile); BOTTOM(tp2) < TOP(tile); tp2 = RT(tp2))
	    {
	      	if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetRightType(tp2))
		{
		    source = tp2;
		    Junk = resAddField(source);
		    Junk->tj_status |= RES_TILE_SD;
		    junk2->sourceEdge |= LEFTEDGE;
		    break;
		}
	    }
	       
	    /* We need to know whether a given diffusion tile connects to
	     * the source or to the drain of a device.  A single 
	     * diffusion tile is marked, and all connecting diffusion tiles
	     * are enumerated and called the source.  Any other SD tiles
	     * are assumed to be the drain.  BUG: this does not work 
	     * correctly with multi SD structures. 
	     */

	    if (source != (Tile *) NULL)
	    {
	        STACKPUSH((ClientData) (source),resDevStack);
	    }
	    while (!StackEmpty(resDevStack))
	    {
	       	tp1 = (Tile *) STACKPOP(resDevStack);
		if (IsSplit(tp1))
		{
		    t1 = (SplitSide(tp1)) ? SplitRightType(tp1) :
				SplitLeftType(tp1);
		}
		else
		    t1 = TiGetTypeExact(tp1);

		/* top */
		for (tp2= RT(tp1); RIGHT(tp2) > LEFT(tp1); tp2 = BL(tp2))
		{
		    if (TiGetBottomType(tp2) == t1)
		    {
		        tileJunk	 *j= resAddField(tp2);
			if ((j->tj_status & RES_TILE_SD) ==0)
			{
			    j->tj_status |= RES_TILE_SD;
	            	    STACKPUSH((ClientData)tp2,resDevStack);
			}
		    }
		}
		/*bottom*/
		for (tp2= LB(tp1); LEFT(tp2) < RIGHT(tp1); tp2 = TR(tp2))
		{
		    if (TiGetTopType(tp2) == t1)
		    {
		        tileJunk	 *j= resAddField(tp2);
			if ((j->tj_status & RES_TILE_SD) == 0)
			{
			    j->tj_status |= RES_TILE_SD;
	            	    STACKPUSH((ClientData) (tp2),resDevStack);
			}
		    }
		}
		/*right*/
		for (tp2= TR(tp1); TOP(tp2) > BOTTOM(tp1); tp2 = LB(tp2))
		{
		    if (TiGetLeftType(tp2) == t1)
		    {
		        tileJunk	 *j= resAddField(tp2);
			if ((j->tj_status & RES_TILE_SD) == 0)
			{
			    j->tj_status |= RES_TILE_SD;
	            	    STACKPUSH((ClientData) (tp2),resDevStack);
			}
		    }
		}
		/*left*/
		for (tp2= BL(tp1); BOTTOM(tp2) < TOP(tp1); tp2 = RT(tp2))
		{
		    if (TiGetRightType(tp2) == t1)
		    {
		        tileJunk	 *j= resAddField(tp2);
			if ((j->tj_status & RES_TILE_SD) == 0)
			{
			    j->tj_status |= RES_TILE_SD;
	            	    STACKPUSH((ClientData) (tp2),resDevStack);
			}
		    }
		}
	    }

	    /* find rest of device; search for source edges */

	    STACKPUSH((ClientData) (tile), resDevStack);
	    while (!StackEmpty(resDevStack))
	    {
	       	tileJunk	*j0;

		tp1= (Tile *) STACKPOP(resDevStack);
		if (IsSplit(tp1))
		{
		    t1 = (SplitSide(tp1)) ? SplitRightType(tp1) :
				SplitLeftType(tp1);
		}
		else
		    t1 = TiGetTypeExact(tp1);

		devptr = ExtCurStyle->exts_device[t1];
		j0 = (tileJunk *) tp1->ti_client;
		/* top */
		for (tp2= RT(tp1); RIGHT(tp2) > LEFT(tp1); tp2 = BL(tp2))
		{
		    if ((TiGetBottomType(tp2) == t1) &&
			      (tp2->ti_client == (ClientData) CLIENTDEFAULT))
		    {
     	  		Junk = resAddField(tp2);
			STACKPUSH((ClientData)(tp2),resDevStack);
	       		Junk->deviceList =  resDev;
	       		Junk->tj_status |= RES_TILE_DEV;
				    
		    }
	      	    else if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetBottomType(tp2))
		    {
			Junk = resAddField(tp2);
			if (Junk->tj_status & RES_TILE_SD)
			       	    j0->sourceEdge |= TOPEDGE;
		    }
		}
		/*bottom*/
		for (tp2= LB(tp1); LEFT(tp2) < RIGHT(tp1); tp2 = TR(tp2))
		{
		    if ((TiGetTopType(tp2) == t1) &&
			      (tp2->ti_client == (ClientData) CLIENTDEFAULT))
		    {
     	  		Junk = resAddField(tp2);
			STACKPUSH((ClientData)(tp2),resDevStack);
	       		Junk->deviceList =  resDev;
	       		Junk->tj_status |= RES_TILE_DEV;
		    }
	      	    else if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetTopType(tp2))
		    {
			Junk = resAddField(tp2);
			if (Junk->tj_status & RES_TILE_SD)
			    j0->sourceEdge |= BOTTOMEDGE;
		    }
		}
		/*right*/
		for (tp2= TR(tp1); TOP(tp2) > BOTTOM(tp1); tp2 = LB(tp2))
		{
		    if ((TiGetLeftType(tp2) == t1) &&
			      (tp2->ti_client == (ClientData) CLIENTDEFAULT))
		    {
			Junk = resAddField(tp2);
			STACKPUSH((ClientData)(tp2),resDevStack);
	       		Junk->deviceList =  resDev;
	       		Junk->tj_status |= RES_TILE_DEV;
		    }
	      	    else if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetLeftType(tp2))
		   {
     	  		Junk = resAddField(tp2);
			if (Junk->tj_status & RES_TILE_SD)
			    j0->sourceEdge |= RIGHTEDGE;
		    }
		}
		/*left*/
		for (tp2= BL(tp1); BOTTOM(tp2) < TOP(tp1); tp2 = RT(tp2))
		{
		    if ((TiGetRightType(tp2) == t1) &&
			      (tp2->ti_client == (ClientData) CLIENTDEFAULT))
		    {
     	  		Junk = resAddField(tp2);
			STACKPUSH((ClientData)(tp2),resDevStack);
	       		Junk->deviceList =  resDev;
	       		Junk->tj_status |= RES_TILE_DEV;
		    }
	      	    else if TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),
				TiGetRightType(tp2))
		    {
     	  		Junk = resAddField(tp2);
			if (Junk->tj_status & RES_TILE_SD)
			    j0->sourceEdge |= LEFTEDGE;
		    }
		}
	    }

	    /* unmark all tiles marked as being part of source */

	    if (source != (Tile *) NULL)
	    {
	        tileJunk	*j = (tileJunk *) source->ti_client;

		STACKPUSH((ClientData) (source),resDevStack);
		j->tj_status &= ~RES_TILE_SD;
	    }
	    while (!StackEmpty(resDevStack))
	    {
	        tp1 = (Tile *) STACKPOP(resDevStack);
		if (IsSplit(tp1))
		{
		    t1 = (SplitSide(tp1)) ? SplitRightType(tp1) :
				SplitLeftType(tp1);
		}
		else
		    t1 = TiGetTypeExact(tp1);

		/* top */
		for (tp2= RT(tp1); RIGHT(tp2) > LEFT(tp1); tp2 = BL(tp2))
		{
		    tileJunk	*j2 = (tileJunk *) tp2->ti_client;
		    if (TiGetBottomType(tp2) == t1)
		    {
			if (j2->tj_status & RES_TILE_SD)
			{
			    j2->tj_status &= ~RES_TILE_SD;
	            	    STACKPUSH((ClientData) tp2,resDevStack);
			}
		    }
		}
		/*bottom*/
		for(tp2= LB(tp1); LEFT(tp2) < RIGHT(tp1); tp2 = TR(tp2))
		{
		    tileJunk	*j2 = (tileJunk *) tp2->ti_client;
		    if (TiGetTopType(tp2) == t1)
		    {
			if (j2->tj_status & RES_TILE_SD)
			{
			    j2->tj_status &= ~RES_TILE_SD;
	           	    STACKPUSH((ClientData) tp2,resDevStack);
			}
		    }
		}
		/*right*/
		for (tp2= TR(tp1); TOP(tp2) > BOTTOM(tp1); tp2 = LB(tp2))
		{
		    tileJunk	*j2 = (tileJunk *) tp2->ti_client;
		    if (TiGetLeftType(tp2) == t1)
		    {
			if (j2->tj_status & RES_TILE_SD)
			{
			    j2->tj_status &= ~RES_TILE_SD;
	            	    STACKPUSH((ClientData) tp2,resDevStack);
			}
		    }
		}
		/*left*/
		for (tp2= BL(tp1); BOTTOM(tp2) < TOP(tp1); tp2 = RT(tp2))
		{
		    tileJunk	*j2 = (tileJunk *) tp2->ti_client;
		    if (TiGetRightType(tp2) == t1)
		    {
			if (j2->tj_status & RES_TILE_SD)
			{
			    j2->tj_status &= ~RES_TILE_SD;
	            	    STACKPUSH((ClientData) tp2,resDevStack);
			}
		    }
		}
	    }
	}
    }
    return(0);
}

/*
 *-------------------------------------------------------------------------
 *
 * ResRemovePlumbing-- Removes and deallocates all the tileJunk fields.
 *
 * Results: returns 0
 *
 * Side Effects: frees up memory; resets tile->ti_client fields to CLIENTDEFAULT
 *
 *-------------------------------------------------------------------------
 */

int
ResRemovePlumbing(tile, arg)
     Tile	*tile;
     ClientData *arg;

{
     
     if (tile->ti_client != (ClientData) CLIENTDEFAULT)
     {
          freeMagic(((char *)(tile->ti_client)));
	  tile->ti_client = (ClientData) CLIENTDEFAULT;
     }
     return(0);
}


/*
 *-------------------------------------------------------------------------
 *
 * ResPreprocessDevices-- Given a list of all the device tiles and 
 * a list of all the devices, this procedure calculates the width and 
 * length.  The width is set equal to the sum of all edges that touch 
 * diffusion divided by 2. The length is the remaining perimeter divided by 
 * 2*tiles.  The perimeter and area fields of device structures are also
 * fixed.
 *
 * Results: none
 *
 * Side Effects: sets length and width of devices. "ResDevTile" 
 * structures are freed.
 *
 *-------------------------------------------------------------------------
 */

void
ResPreProcessDevices(TileList, DeviceList, Def)
    ResDevTile		*TileList;
    resDevice		*DeviceList;
    CellDef		*Def;
{
    Tile	*tile;
    ResDevTile	*oldTile;
    tileJunk	*tstruct;
    TileType	tt, residue;
    int		pNum;
     
    while (TileList != (ResDevTile *) NULL)
    {
	tt = TileList->type;
	if (DBIsContact(tt))
	{
	    /* Find which residue of the contact is a device. */
	    TileTypeBitMask ttresidues;

	    DBFullResidueMask(tt, &ttresidues);
	       
	    for (residue = TT_TECHDEPBASE; residue < DBNumUserLayers; residue++)
	    {
		if (TTMaskHasType(&ttresidues, residue))
		{
		    if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, residue))
		    {
			pNum = DBPlane(residue);
			break;
		    }
		}
	    }
	}
	else
	    pNum = DBPlane(tt);		/* always correct for non-contact types */

	tile = (Def->cd_planes[pNum])->pl_hint;
	GOTOPOINT(tile, &(TileList->area.r_ll));

	tt = TiGetType(tile);
	tstruct = (tileJunk *) tile->ti_client;

	if (!TTMaskHasType(&ExtCurStyle->exts_deviceMask, tt) ||
				tstruct->deviceList == NULL)
	{
	    TxError("Bad Device Location at %d,%d\n",
			TileList->area.r_ll.p_x,
			TileList->area.r_ll.p_y);
	}
	else if ((tstruct->tj_status & RES_TILE_MARK) == 0)
	{
	    resDevice	*rd = tstruct->deviceList;

	    tstruct->tj_status |= RES_TILE_MARK;
	    rd->rd_perim += TileList->perim;
	    rd->rd_length += TileList->overlap;
	    rd->rd_area += (TileList->area.r_xtop - TileList->area.r_xbot)
			* (TileList->area.r_ytop - TileList->area.r_ybot);
	    rd->rd_tiles++;
	}
	oldTile = TileList;
	TileList = TileList->nextDev;
	freeMagic((char *)oldTile);
    }

    for(; DeviceList != NULL;DeviceList = DeviceList->rd_nextDev)
    {
     	int width  = DeviceList->rd_perim;
	int length = DeviceList->rd_length;
	if (DeviceList->rd_tiles != 0)
	{
	    if (length)
	    {
	        DeviceList->rd_length = (float) length /
			((float)((DeviceList->rd_tiles) << 1));
	        DeviceList->rd_width = (width-length) >> 1;
	    }
	    else
	    {
	       	double perimeter = DeviceList->rd_perim;
		double area = DeviceList->rd_area;
		    
		perimeter /= 4.0;

		DeviceList->rd_width = perimeter +
			sqrt(perimeter * perimeter-area);
		DeviceList->rd_length = (DeviceList->rd_perim
			- 2 * DeviceList->rd_width) >> 1;
	    }
	}
    }
}


/*
 *-------------------------------------------------------------------------
 *
 * ResAddToQueue-- adds new nodes to list of nodes requiring processing.
 *
 * Side Effects: nodes are added to list (i.e they have their linked list
 *	pointers modified.)
 *
 *-------------------------------------------------------------------------
 */
void
ResAddToQueue(node,list)
     resNode		*node,**list;
{

     node->rn_more = *list;
     node->rn_less = NULL;
     if (*list) (*list)->rn_less = node;
     *list = node;
}


/*
 *-------------------------------------------------------------------------
 *
 * ResRemoveFromQueue-- removes node from queue. Complains if it notices
 *	that the node isn't in the supplied list.
 *
 * Results: none
 *
 * Side Effects: modifies nodelist
 *
 *-------------------------------------------------------------------------
 */

void
ResRemoveFromQueue(node,list)
	resNode	*node,**list;

{
     
     if (node->rn_less != NULL)
     {
     	  node->rn_less->rn_more = node->rn_more;
     }
     else
     {
     	  if (node != (*list))
	  {
	       TxError("Error: Attempt to remove node from wrong list\n");
	  }
	  else
	  {
	       *list = node->rn_more;
	  }
     }
     if (node->rn_more != NULL)
     {
     	  node->rn_more->rn_less = node->rn_less;
     }
     node->rn_more = NULL;
     node->rn_less = NULL;
}
tileJunk *
resAddField(tile)
	Tile	*tile;

{
        tileJunk *Junk;
	if ((Junk=(tileJunk *)tile->ti_client) == (tileJunk *) CLIENTDEFAULT)
	{
     	      Junk = (tileJunk *) mallocMagic((unsigned) (sizeof(tileJunk)));
	      ResJunkInit(Junk);
	      tile->ti_client = (ClientData) Junk;
	}
	return Junk;
}
