/*
 * ext2spice.c --
 *
 * Program to flatten hierarchical .ext files and produce
 * a .spice file, suitable for use as input to spiceulators
 * such as spice and hspice.
 *
 * Flattens the tree rooted at file.ext, reading in additional .ext
 * files as specified by "use" lines in file.ext.  The output is left
 * in file.spice, unless '-o esSpiceFile' is specified, in which case the
 * output is left in 'esSpiceFile'.
 *
 */

#ifndef lint
static char rcsid[] = "$Header: /ufs/repository/magic/ext2spice/ext2spice.c,v 1.4 2001/12/04 21:44:14 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <ctype.h>
#ifdef SYSV
#include <string.h>
#endif
#include <varargs.h>

#ifdef MAGIC_WRAPPER
#include "magic/tclmagic.h"
#ifndef free_all_mem
#define free_all_mem
#endif
#endif

#include "misc/magic.h"
#include "utils/malloc.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/dqueue.h"
#include "utils/utils.h"
#ifdef MAGIC_WRAPPER
#include "tiles/tile.h"
#include "database/database.h"
#include "windows/windows.h"
#include "textio/textio.h"
#endif
#include "extflat/extflat.h"
#include "extflat/EFint.h"
#include "utils/runstats.h"

/* Forward declarations */
int spcmainArgs();
int spccapVisit(), spcfetVisit(), spcnodeVisit(), subcktVisit(), spcresistVisit(), 
    fetMergeVisit(), fetDistJunctVisit();
EFNode *spcfetSubstrate();
char *nodeSpiceName();

/* Options specific to ext2spice */
bool esFetNodesOnly = FALSE;
bool esNoAttrs = FALSE;
bool esHierAP = FALSE;
char spcesDefaultOut[FNSIZE];
int  esCapAccuracy = 1;
char esCapFormat[FNSIZE];
char *spcesOutName = spcesDefaultOut;
FILE *esSpiceF = NULL;
float esScale = -1.0 ; /* negative if hspice the EFScale/100 otherwise */

#define	SPICE2	0
#define	SPICE3	1
#define	HSPICE	2

static unsigned short esFormat = HSPICE ;

int esCapNum  = 0, esFetNum = 1000, esResNum = 0;
int esNodeNum = 10; /* just in case we' re extracting spice2 */
int esSbckNum = 0;  /* used in hspice node name shortening   */

/*
 * The following hash table and associated functions are used only if 
 * the format is hspice, to keep the translation between the hierarchical 
 * prefix of a node and the x num that we use to output valid hspice 
 * which also are meaningful.
 */
HashTable subcktNameTable ; /* the hash table itself */
DQueue    subcktNameQueue ; /* q used to print it sorted at the end*/


#define MAX_STR_SIZE (1<<11) /* 2K should be enough for keeping temp
                                names even of the most complicated design */

struct {
   short resClassSD ;  /* the resistance class of the src/drn of the fet */
   short resClassSub ; /* the resistance class of the substrate of the fet */
   char  *defSubs ;    /* the default substrate node */
} fetInfo[MAXFETTYPES];


/* Node clients for figuring out areas and perimeters of sources and drains */
typedef struct { 
	long	_duml:MAXFETTYPES; 
} _dum; /* if you get an error here you should change 
           the data structures and visitMask */

typedef union {
	long	visitMask; /* mask for normal visits */
	float	*widths; /* width used for distributing area perim */
} maskOrWidth ;

typedef struct {
	char		*spiceNodeName;
	maskOrWidth	m_w;
} nodeClient;

typedef struct {
	HierName 	*lastPrefix;
	maskOrWidth	m_w;
} nodeClientHier;

#define	NO_RESCLASS	-1

#define markVisited(client, rclass) \
  { (client)->m_w.visitMask |= (1<<rclass); }

#define clearVisited(client) \
   { (client)->m_w.visitMask = (long)0; }

#define beenVisited(client, rclass)  \
   ( (client)->m_w.visitMask & (1<<rclass))

/*
 * Used to mark the nodes which are connected to fets. initMask  is set to
 * FET_CONNET_MASK only when we are in visitFets 
 */
#define	FET_CONNECT_MASK	((unsigned long)1<<(sizeof(long)*BITSPERCHAR-1))
unsigned long	initMask = 0;

#define initNodeClient(node) \
{ \
	MALLOC(ClientData,(node)->efnode_client,sizeof(nodeClient)); \
	 (( nodeClient *)(node)->efnode_client)->spiceNodeName = NULL; \
	(( nodeClient *)(node)->efnode_client)->m_w.visitMask = (unsigned long)initMask; \
}


#define initNodeClientHier(node) \
{ \
	MALLOC(ClientData,(node)->efnode_client,sizeof(nodeClientHier)); \
	((nodeClientHier *) (node)->efnode_client)->m_w.visitMask = (unsigned long) 0; \
}



/* attributes controlling the Area/Perimeter extraction of fet terminals */
#define ATTR_FLATAP	"*[Ee][Xx][Tt]:[Aa][Pp][Ff]*"
#define ATTR_HIERAP	"*[Ee][Xx][Tt]:[Aa][Pp][Hh]*"
#define ATTR_SUBSAP	"*[Ee][Xx][Tt]:[Aa][Pp][Ss]*"

bool esMergeFetsA = FALSE; /* aggresive merging of fets L1=L2 merge them */
bool esMergeFetsC = FALSE; /* conservative merging of fets L1=L2 and W1=W2 */
			   /* used with the hspice multiplier */
bool esDistrJunct = FALSE;

/* 
 *---------------------------------------------------------
 * Variables & macros used for merging parallel fets       
 * The merging of fets is based on the fact that spcfetVisit 
 * visits the fets in the same order all the time so the 
 * value of esFMult[i] keeps the multiplier for the ith fet
 *---------------------------------------------------------
 */
#define	FET_KILLED	((float) -1.0)
#define	FMULT_SIZE	(1<<10)

static float	*esFMult = NULL;         /* the array itself */
static int	 esFMIndex = 0;          /* current index to it */
static int	 esFMSize = FMULT_SIZE ; /* its current size (growable) */

static int	 esFetsMerged;

#define	fetIsKilled(n) ( esFMult[(n)] <=(float)0.0 )

#define	FET_KILLED	((float) -1.0)

int nodeVisitDebug();

/* macro to add a fet's multiplier to the table and grow it if necessary */
#define addFetMult(f) \
{  \
	if ( esFMult == NULL ) { \
	  MALLOC(float *, esFMult, esFMSize*sizeof(float));  \
	} else if ( esFMIndex >= esFMSize ) {  \
	  int i;  \
	  float *op = esFMult ;  \
	  MALLOC(float *, esFMult, (esFMSize = esFMSize*2)*sizeof(float)); \
	  for ( i = 0 ; i < esFMSize/2 ; i++ ) esFMult[i] = op[i]; \
	  if (op) FREE(op); \
	}  \
	esFMult[esFMIndex++] = (float)(f); \
}

#define	setFetMult(i,f) { esFMult[(i)] = (float)(f); }

#define	getCurFetMult() ((esFMult) ? esFMult[esFMIndex-1] : (float)1.0)

/* cache list used to find parallel fets */
typedef struct _fetMerge {
	int     l, w;
	EFNode *g, *s, *d, *b;
	Fet * fet;
	int	  esFMIndex;
	HierName *hierName;
	struct _fetMerge *next;
} fetMerge;

fetMerge *fetMergeList = NULL ;



#ifdef MAGIC_WRAPPER

/* Forward declaration */
int _ext2spice_start();

/*
 * ----------------------------------------------------------------------------
 *
 * Tcl package initialization function
 *
 * ----------------------------------------------------------------------------
 */

int
Exttospice_Init(interp)
    Tcl_Interp *interp;
{
    /* Sanity checks! */
    if (interp == NULL) return TCL_ERROR;
    if (Tcl_PkgRequire(interp, "Tclmagic", "7.2", 0) == NULL) return TCL_ERROR;
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) return TCL_ERROR;

    Tcl_CreateCommand(interp, "exttospice::exttospice", _ext2spice_start,
		(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);

    Tcl_Eval(interp, "namespace eval exttospice namespace export *");

    Tcl_PkgProvide(interp, "Exttospice", "7.2");
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * Main Tcl callback for command "exttospice::exttospice"
 *
 * ----------------------------------------------------------------------------
 */

int
_ext2spice_start(ClientData clientData,
    Tcl_Interp *interp, int argc, char *argv[])
{

#else

/*
 * ----------------------------------------------------------------------------
 *
 * main --
 *
 * Top level of ext2spice.
 *
 * ----------------------------------------------------------------------------
 */

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

#endif	/* MAGIC_WRAPPER */

    int i,flatFlags;
    char *inName;
    FILE *f;

#ifdef MAGIC_WRAPPER
    MagWindow *w;
    int wid;

    /* Did we start in the same interpreter as we initialized? */
    if (magicinterp != interp)
    {
        TxError("Warning:  Switching interpreters.  Tcl-magic is not set up " 
                "to handle this.\n");
        magicinterp = interp;
    }
#endif

    esFetsMerged = 0;

    EFInit();
    EFResistThreshold = INFINITE_THRESHOLD ;
    /* create default fetinfo entries (MOSIS) which can be overriden by
       the command line arguments */
    for ( i = 0 ; i < MAXFETTYPES ; i++ ) {
	fetInfo[i].resClassSD = NO_RESCLASS;
	fetInfo[i].resClassSub = NO_RESCLASS;
	fetInfo[i].defSubs = NULL;
    }
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "nfet");
    fetInfo[i].resClassSD = 0 ;
    fetInfo[i].resClassSub = NO_RESCLASS ;
    fetInfo[i].defSubs = "Gnd!";
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "pfet");
    fetInfo[i].resClassSD = 1 ;
    fetInfo[i].resClassSub = 6 ;
    fetInfo[i].defSubs = "Vdd!";
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "nmos");
    fetInfo[i].resClassSD = 0 ;
    fetInfo[i].resClassSub = NO_RESCLASS ;
    fetInfo[i].defSubs = "Gnd!";
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "pmos");
    fetInfo[i].resClassSD = 1 ;
    fetInfo[i].resClassSub = 6 ;
    fetInfo[i].defSubs = "Vdd!";
    /* Process command line arguments */

    inName = EFArgs(argc, argv, spcmainArgs, (ClientData) NULL);
    if (inName == NULL)

#ifdef MAGIC_WRAPPER
    {
	/* Assume that we want to do exttospice on the currently loaded cell */
	
	if ((wid = TxGetPoint(NULL)) >= 0)
	{
	    w = WindSearchWid(wid);
	}
	else
	{
	    w = NULL;
	    windCheckOnlyWindow(&w);
	}

	if (w == (MagWindow *) NULL)
	{
	    TxError("Point to a window or specify a cell name.\n");
	    return TCL_ERROR;
	}
	if ((inName = ((CellUse *) w->w_surfaceID)->cu_def->cd_name) == NULL)
	{
	    TxError("No cell present\n");
	    return TCL_ERROR;
	}
    }

#else
	exit (1);
#endif

    /*
     * Initializations specific to this program.
     * Make output name inName.spice if they weren't 
     * explicitly specified
     */

    if (spcesOutName == spcesDefaultOut)
	sprintf(spcesDefaultOut, "%s.spice", inName);

    if ((esSpiceF = fopen(spcesOutName, "w")) == NULL)
    {
#ifdef MAGIC_WRAPPER
	char *tclres = Tcl_Alloc(128);
	sprintf(tclres, "exttospice: Unable to open file %s for writing\n",
		spcesOutName);
	Tcl_SetResult(magicinterp, tclres, TCL_DYNAMIC);
        return TCL_ERROR;
#else
	perror(spcesOutName);
	exit (1);
#endif
    }

    /* Read the hierarchical description of the input circuit */
    if (EFReadFile(inName, TRUE) == FALSE)
    {
#ifdef MAGIC_WRAPPER
        return TCL_ERROR;
#else
	exit (1);
#endif
    }

    fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n",
	    (esFormat == SPICE2) ? "SPICE2" :  
	      ( (esFormat == SPICE3) ? "SPICE3" : "HSPICE" ),  
	    inName, EFTech);
    if ( esFormat == HSPICE ) 
    	fprintf(esSpiceF,".option scale=%gu\n\n", ((float) EFScale)/100.0);
    else esScale = EFScale/100.0;

    /* Convert the hierarchical description to a flat one */
    flatFlags = EF_FLATNODES;
    EFTrimFlags |= EF_TRIMGLOB ;
    if (EFCapThreshold < INFINITE_THRESHOLD) flatFlags |= EF_FLATCAPS;
    if (esFormat == HSPICE ) {
	EFTrimFlags |= EF_TRIMLOCAL ;
	HashInit(&subcktNameTable, 32, HT_STRINGKEYS);
#ifndef UNSORTED_SUBCKT
	DQInit(&subcktNameQueue, 64);
#endif
    }
    EFFlatBuild(inName, flatFlags);

    initMask = ( esDistrJunct  ) ? (unsigned long)0 : FET_CONNECT_MASK ;

    if ( esMergeFetsA || esMergeFetsC ) {
     	EFVisitFets(fetMergeVisit, (ClientData) NULL);
	TxPrintf("Fets merged: %d\n", esFetsMerged);
	esFMIndex = 0 ;
#ifdef free_all_mem
	{
	  register fetMerge *p;

	  for ( p = fetMergeList ; p != NULL ; p=p->next ) FREE(p);
	}
#endif
    } else if ( esDistrJunct )
     	EFVisitFets(fetDistJunctVisit, (ClientData) NULL);
    EFVisitFets(spcfetVisit, (ClientData) NULL);
    initMask = (unsigned long) 0;
    if (flatFlags & EF_FLATCAPS) {
	(void) sprintf( esCapFormat,  "C%%d %%s %%s %%.%dlffF\n",esCapAccuracy);
	EFVisitCaps(spccapVisit, (ClientData) NULL);
    }
    EFVisitResists(spcresistVisit, (ClientData) NULL);
    EFVisitSubcircuits(subcktVisit, (ClientData) NULL);
    (void) sprintf( esCapFormat, "C%%d %%s GND %%.%dlffF%%s", esCapAccuracy);
    EFVisitNodes(spcnodeVisit, (ClientData) NULL);

    if ( esFormat == HSPICE ) 
	printSubcktDict();

#ifdef	free_all_mem
    EFFlatDone(); 
    EFDone();
#endif	free_all_mem

    if (esSpiceF) fclose(esSpiceF);

#ifdef MAGIC_WRAPPER
    TxPrintf("exttospice finished.\n");
    return TCL_OK;
#else
    TxPrintf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL));
    exit (0);
#endif

}

/*
 * ----------------------------------------------------------------------------
 *
 * spcmainArgs --
 *
 * Process those arguments that are specific to ext2spice.
 * Assumes that *pargv[0][0] is '-', indicating a flag
 * argument.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	After processing an argument, updates *pargc and *pargv
 *	to point to after the argument.
 *
 *	May initialize various global variables based on the
 *	arguments given to us.
 *
 *	Exits in the event of an improper argument.
 *
 * ----------------------------------------------------------------------------
 */

Void
spcmainArgs(pargc, pargv)
    int *pargc;
    char ***pargv;
{
    char **argv = *pargv, *cp;
    int argc = *pargc;

    switch (argv[0][1])
    {
	case 'd':
	    esDistrJunct = TRUE;
	    break;
	case 'M':
	    esMergeFetsA = TRUE;
	    break;
	case 'm':
	    esMergeFetsC = TRUE;
	    break;
	case 'B':
	    esNoAttrs = TRUE;
	    break;
	case 'F':
	    esFetNodesOnly = TRUE;
	    break;
	case 'o':
	    if ((spcesOutName = ArgStr(&argc, &argv, "filename")) == NULL)
		goto usage;
	    break;
	case 'f': {
	     char *ftmp ;

	     if ((ftmp = ArgStr(&argc, &argv, "format")) == NULL)
		goto usage;
	     if ( strcasecmp(ftmp,"SPICE2") == 0 )
	        esFormat = SPICE2 ;
	     else if ( strcasecmp(ftmp,"SPICE3") == 0 )
		esFormat = SPICE3 ;
	     else if ( strcasecmp(ftmp,"HSPICE") == 0 ) 
		esFormat = HSPICE ;
	     else goto usage;
	     break;
	     }
	case 'J':
	     {
	     char *ftmp ;

	     if ((ftmp = ArgStr(&argc, &argv, "hierAP_SD")) == NULL)
		goto usage;
	     if ( strcasecmp(ftmp, "HIER") == 0 )
		esHierAP = TRUE ;
	     else if ( strcasecmp(ftmp, "FLAT") == 0 )
		esHierAP = FALSE ;
	     else goto usage;

	     break;
	     }
	case 'y': {
	      char *t;

	      if (( t =  ArgStr(&argc, &argv, "cap-accuracy") ) == NULL)
		goto usage;
	      esCapAccuracy = atoi(t);
	      break;
	      }
	case 'j':
	    {
	    char *rp,  subsNode[80] ;
	    int   ndx, rClass, rClassSub = -1;

	    if ((cp = ArgStr(&argc,&argv,"resistance class")) == NULL)
		goto usage;
	    if ( (rp = (char *) index(cp,':')) == NULL )
		goto usage;
	    else *rp++ = '\0';
	    if ( sscanf(rp, "%d/%d/%s", &rClass, &rClassSub, subsNode) != 3 ) {
		rClassSub = -1 ;
	    	if ( sscanf(rp, "%d/%s",  &rClass, subsNode) != 2 ) goto usage;
	    }
	    ndx = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, cp);
	    fetInfo[ndx].resClassSD = rClass;
	    fetInfo[ndx].resClassSub = rClassSub;
	    if ( ((1<<rClass) & FET_CONNECT_MASK) ||
	         ((1<<rClass) & FET_CONNECT_MASK)   ) {
	        TxError("Oops it seems that you have 31\n");
	        TxError("resistance classes. You will need to recompile");
	        TxError("the extflat package and change ext2sim/spice\n");
	        TxError("FET_CONNECT_MASK and or nodeClient\n");
#ifdef MAGIC_WRAPPER
        	return;
#else
		exit (1);
#endif
	    }
	    MALLOC(char *, fetInfo[ndx].defSubs, (strlen(subsNode)+1));
	    strcpy(fetInfo[ndx].defSubs,subsNode);
	    TxError("info: fet %s(%d) sdRclass=%d subRclass=%d dSub=%s\n", 
	    cp, ndx, fetInfo[ndx].resClassSD, fetInfo[ndx].resClassSub, 
            fetInfo[ndx].defSubs);
	    break;
	    }
	default:
	    TxError("Unrecognized flag: %s\n", argv[0]);
	    goto usage;
    }

    *pargv = argv;
    *pargc = argc;
    return;

usage:
    TxError("Usage: ext2spice [-B] [-o spicefile] [-M|-m] [-y cap_digits] "
		"[-J flat|hier]\n"
		"[-j device:sdRclass[/subRclass]/defaultSubstrate]\n"
		"[-f spice2|spice3|hspice] [-M] [-m] "
#ifdef MAGIC_WRAPPER
		"[file]\n"
#else
		"file\n\n    or else see options to extcheck(1)\n"
#endif
		);

#ifdef MAGIC_WRAPPER
    return TCL_ERROR;
#else
    exit (1);
#endif
}

/*
 * ----------------------------------------------------------------------------
 *
 * GetNode --
 *
 * function to find a node given its hierarchical prefix and suffix
 *
 * Results:
 *  a pointer to the node struct or NULL
 *
 * ----------------------------------------------------------------------------
 */
EFNode *
GetNode(prefix, suffix)
HierName *prefix;
HierName *suffix;
{
    HashEntry *he;
    register EFNodeName *nn;

    he = EFHNConcatLook(prefix, suffix, "output");
    if (he == NULL) return NULL;
    nn = (EFNodeName *) HashGetValue(he);
    if (nn == NULL) return NULL;
    return(nn->efnn_node);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extHierSDAttr --
 * Check if the attribute of the argument fet_terminal or the global
 * settings are such that we want a hierarchical extraction of its S/D
 * 
 *
 * Results:
 *  TRUE or FALSE
 *
 * Side effects:
 *  None.
 *
 * ----------------------------------------------------------------------------
 */
bool extHierSDAttr(term)
FetTerm *term;
{
    bool r = esHierAP;

    if (term->fterm_attrs) {
	if  ( Match(ATTR_HIERAP, term->fterm_attrs ) != FALSE ) 
		r = TRUE ;
	else if ( Match(ATTR_FLATAP, term->fterm_attrs ) != FALSE )
		r = FALSE ;
    }
    return r;
}

/*
 * ----------------------------------------------------------------------------
 *
 * subcktVisit --
 *
 * Procedure to output a subcircuit definition to the .spice file.
 * Called by EFVisitSubcircuits().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSpiceF.
 *
 * Format of a .spice subcircuit call:
 *
 *	X%d node1 node2 ... noden name
 *
 * where
 *	node1 node2 ... noden are the nodes connecting to the ports of
 *	the subcircuit.  "name" is the name of the subcircuit.  It is
 *	assumed that the definition of "name" (.defs name ... .ends)
 *	exists elsewhere and will be appended to the SPICE deck prior
 *	to simulation (as is also assumed for device models).
 *
 * ----------------------------------------------------------------------------
 */
int
subcktVisit(use, hierName, cdata)
    Use *use;
    HierName *hierName;
    ClientData cdata;	/* unused */
{
    EFNode *snode;
    Def *def = use->use_def;
    HierName *nodeName;

    fprintf(esSpiceF, "X%d", esSbckNum++);

    /* The ports of the subcircuit will be ALL the entries in the hash table */
    /* This is not a FET, but "spcfetOutNode" is a general-purpose routine that */
    /* turns a local name in the use's def to a hierarchical name in the     */
    /* calling def.							     */

    for (snode = (EFNode *) def->def_firstn.efnode_next;
		snode != &def->def_firstn;
		snode = (EFNode *) snode->efnode_next)
	spcfetOutNode(hierName, snode->efnode_name->efnn_hier, "subcircuit", esSpiceF); 

    fprintf(esSpiceF, " %s\n", def->def_name);	/* subcircuit model name */

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * spcfetVisit --
 *
 * Procedure to output a single fet to the .spice file.
 * Called by EFVisitFets().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSpiceF.
 *
 * Format of a .spice fet line:
 *
 *	M%d drain gate source substrate type w=w l=l * x y
 *      + ad= pd= as= ps=  * asub= psub=  
 *      **fetattr g= s= d= 
 *
 * where
 *	type is a name identifying this type of transistor
 *      other types of transistors are extracted with
 *      an M card but it should be easy to turn them to whatever 
 *      you want.
 *	gate, source, and drain are the nodes to which these three
 *		terminals connect
 *	l, w are the length and width of the channel
 *	x, y are the x, y coordinates of a point within the channel.
 *	g=, s=, d= are the (optional) attributes; if present, each
 *		is followed by a comma-separated list of attributes.
 *
 * ----------------------------------------------------------------------------
 */

int
spcfetVisit(fet, hierName, trans, l, w)
    Fet *fet;		/* Fet being output */
    HierName *hierName;	/* Hierarchical path down to this fet */
    Transform *trans;	/* Coordinate transform for output */
    int l, w;
{
    FetTerm *gate, *source, *drain;
    EFNode  *subnode, *snode, *dnode, *subnodeFlat;
    int scale; 
    Rect r;
    bool subAP= FALSE, hierS, hierD, extHierSDAttr() ;
    float sdM ; 
    char name[12], *dotptr, devchar;

    sprintf(name, "output");

    /* If no terminals, can't do much of anything */
    if (fet->fet_nterm < 2 ) 
	return 0;

    if ( (esMergeFetsA || esMergeFetsC) && fetIsKilled(esFMIndex++) )
	    return 0;

    /* If only two terminals, connect the source to the drain */
    gate = &fet->fet_terms[0];
    source = drain = &fet->fet_terms[1];
    if (fet->fet_nterm >= 3)
	drain = &fet->fet_terms[2];
    subnode = fet->fet_subsnode;

    /* Output drain, gate, source, and substrate node names and fet type */

    /* Method for generating devices at subcircuit entries */
    if (((dotptr = strrchr(EFFetTypes[fet->fet_type], '.')) != NULL) &&
 	 !strcmp(dotptr, ".subckt"))
    {
	*dotptr = '\0';
	devchar = 'x';		/* use a subcircuit */
    }
    else
	devchar = 'm';

    /* Hack for BiCMOS, Tim 10/4/97 */

    if (!strcmp(EFFetTypes[fet->fet_type], "npn"))
    {
       if (devchar == 'm') devchar = 'q';	/* default device name */
       sprintf(name, "fet");
       if (gate->fterm_attrs)
	        (void) fprintf(esSpiceF, "%c%s", devchar, gate->fterm_attrs);
       else
                (void) fprintf(esSpiceF, "%c%d", devchar, esFetNum++);
       spcfetOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSpiceF); 
    }
    else {
       if (gate->fterm_attrs)
	        (void) fprintf(esSpiceF, "%c%s", devchar, gate->fterm_attrs);
       else
    (void) fprintf(esSpiceF, "%c%d", devchar, esFetNum++);
       spcfetOutNode(hierName, drain->fterm_node->efnode_name->efnn_hier,
		name, esSpiceF);
    }
    spcfetOutNode(hierName, gate->fterm_node->efnode_name->efnn_hier,
		name, esSpiceF);
/* fix mixed up drain/source for bjts hace 2/2/99 */
    if (!strcmp(EFFetTypes[fet->fet_type], "npn") &&
	gate->fterm_node->efnode_name->efnn_hier ==
	source->fterm_node->efnode_name->efnn_hier)
           spcfetOutNode(hierName, drain->fterm_node->efnode_name->efnn_hier,
			name, esSpiceF);
    else
           spcfetOutNode(hierName, source->fterm_node->efnode_name->efnn_hier,
			name, esSpiceF);
    putc(' ', esSpiceF);

    /*
     * Scale L and W appropriately by the same amount as distance
     * values in the transform.  The transform will have a scale
     * different from 1 only in the case when the scale factors of
     * some of the .ext files differed, making it necessary to scale
     * all dimensions explicitly instead of having a single scale
     * factor at the beginning of the .spice file.
     */
    GeoTransRect(trans, &fet->fet_rect, &r);
    scale = GeoScale(trans);
		     /*  hace don't print bjt substrate nodes */
    if (strcmp(EFFetTypes[fet->fet_type], "npn"))
    subnodeFlat = spcfetSubstrate(hierName, subnode->efnode_name->efnn_hier,
	             fet->fet_type, esSpiceF);
    fprintf(esSpiceF, " %s", EFFetTypes[fet->fet_type]);
    sdM = getCurFetMult();

    /* don't output width, length for npn bipolars -- Tim 10/9/97 */

    if (strcmp(EFFetTypes[fet->fet_type], "npn")) {

	if ( esFormat == HSPICE ) {
            fprintf(esSpiceF, " w=%d l=%d", w*scale, l*scale);
	    if ( sdM != 1.0 )
		fprintf(esSpiceF, " M=%g", sdM);
	}
	else  {
	    fprintf(esSpiceF, " w=%gu l=%gu", (float)w*scale*esScale*sdM,
			    (float)l*scale*esScale);
	    sdM = 1.0;
	}

	/*
	 * Check controling attributes and output area and perimeter. 
	 */
	hierS = extHierSDAttr(source);
	hierD = extHierSDAttr(drain);
	if ( gate->fterm_attrs ) 
	    subAP = Match(ATTR_SUBSAP, gate->fterm_attrs ) ;

	fprintf(esSpiceF, "\n+ ");
	if (hierD) 
            spcnAPHier(drain, hierName, fetInfo[fet->fet_type].resClassSD,
               scale, "d", sdM, esSpiceF);
	else {
	    dnode = GetNode(hierName, drain->fterm_node->efnode_name->efnn_hier);
            spcnAP(dnode, fetInfo[fet->fet_type].resClassSD, scale,"d",sdM,esSpiceF,w);
	}
	if (hierS)
	    spcnAPHier(source, hierName, fetInfo[fet->fet_type].resClassSD,
               scale, "s", sdM, esSpiceF);
	else {
	    snode= GetNode(hierName, source->fterm_node->efnode_name->efnn_hier);
	    spcnAP(snode, fetInfo[fet->fet_type].resClassSD, scale,"s",sdM, esSpiceF,w);
	}
	if ( subAP ) {
	    fprintf(esSpiceF, " * ");
	    if ( fetInfo[fet->fet_type].resClassSub < 0 ) {
		TxError("error: subap for fettype %d unspecified\n",
			fet->fet_type);
		fprintf(esSpiceF,"asub=0 psub=0");
	    }
	    else if ( subnodeFlat ) 
		spcnAP(subnodeFlat, fetInfo[fet->fet_type].resClassSub, scale, 
	       		"sub", sdM, esSpiceF, -1);
	    else fprintf(esSpiceF,"asub=0 psub=0");
	}

	/* Now output attributes, if present */
	if (!esNoAttrs)
	{
	    if ( gate->fterm_attrs || source->fterm_attrs || drain->fterm_attrs)
		fprintf(esSpiceF,"\n**fetattr");
	    if (gate->fterm_attrs)
		(void) fprintf(esSpiceF, " g=%s", gate->fterm_attrs);
	    if (source->fterm_attrs) 
		(void) fprintf(esSpiceF, " s=%s", source->fterm_attrs);
	    if (drain->fterm_attrs) 
		(void) fprintf(esSpiceF, " d=%s", drain->fterm_attrs);
	}
    }
    fprintf(esSpiceF, "\n");

    if (dotptr != NULL) *dotptr = '.';

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * spcfetSubstrate -
 *
 * Output the node name of the substrate of a fet. If the suffix is the 
 * same as the default dont go looking for it just output the default
 * (trimmed appropriately). Otherwise look it up ..
 *
 * Results:
 *   NULL if not found or the default substrate or the node pointer
 *   otherwise (might be reused to output area and perimeter of
 *   the substrate).
 *
 * Side effects:
 *  Might allocate the nodeClient for the node through nodeSpiceName.
 *
 * ----------------------------------------------------------------------------
 */
EFNode *spcfetSubstrate( prefix, suffix, type,  outf)
HierName *prefix, *suffix;
int type;
FILE *outf;
{
    HashEntry *he;
    EFNodeName *nn;
    char *suf ;
    int  l ;

    suf = EFHNToStr(suffix);
    if (fetInfo[type].defSubs && strcasecmp(suf,fetInfo[type].defSubs) == 0) {
	if ( outf ) {
    	   l = strlen(suf) - 1;
	   if (  ( EFTrimFlags & EF_TRIMGLOB ) && suf[l] =='!' ||
	         ( EFTrimFlags & EF_TRIMLOCAL ) && suf[l] == '#'  )
	        suf[l] = '\0' ;
	   fprintf(outf, "%s", suf);
	}
	return NULL;
    }
    else {
    	he = EFHNConcatLook(prefix, suffix, "substrate");
    	if (he == NULL)
    	{
		if (outf) 
		   fprintf(outf, "errGnd!");
		return NULL;
    	}
    	/* Canonical name */
    	nn = (EFNodeName *) HashGetValue(he);
	if (outf) 
	   fprintf(outf, nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier));
        return nn->efnn_node;
   }
}


/*
 * ----------------------------------------------------------------------------
 *
 * spcnAP, spcnAPHier --
 *
 * Output the area perimeter of the node with type type if it has not 
 * been visited.
 * The spcnAPHier version outputs the area and perimeter only within the
 * local subcell with hierarchical name hierName.
 *
 * Side effects:
 *     Set the visited flags so that the node A/P will not be output multiple
 *     times
 *
 * ----------------------------------------------------------------------------
 */
Void spcnAP(node, resClass, scale, sterm, m, outf, w)
    EFNode *node;
    int  resClass, scale;
    char *sterm;
    float m;
    FILE *outf;
    int w;
{
	char fmt[30];
	float dsc ;


	if ( node->efnode_client == NULL ) {
	   TxError("spcnAP: major internal inconsistency\n");
	}
	if ( esFormat == HSPICE )
	     sprintf(fmt,"a%s=%%g p%s=%%g ", sterm, sterm);
	else sprintf(fmt,"a%s=%%gp p%s=%%gu ", sterm, sterm);

	if ( !esDistrJunct || w == -1 ) goto oldFmt;

	dsc = w/((nodeClient*)node->efnode_client)->m_w.widths[resClass];
	if ( esFormat == HSPICE )
	   fprintf(outf,fmt,
		node->efnode_pa[resClass].pa_area*scale*scale*dsc,
		node->efnode_pa[resClass].pa_perim*scale*dsc);
	else 
	   fprintf(outf,fmt,
	    ((float)node->efnode_pa[resClass].pa_area*scale*scale)
	    *esScale*esScale*dsc,
	    ((float)node->efnode_pa[resClass].pa_perim*scale)*esScale*dsc);
	return;

oldFmt:
	if ( resClass == NO_RESCLASS ||
	     beenVisited((nodeClient *)node->efnode_client, resClass) ) 
		scale = 0 ;
	else   markVisited((nodeClient *)node->efnode_client, resClass);
	if ( esFormat == HSPICE )
	   fprintf(outf,fmt,
		node->efnode_pa[resClass].pa_area*scale*scale/m,
		node->efnode_pa[resClass].pa_perim*scale/m);
	else 
	   fprintf(outf,fmt,
	    ((float)node->efnode_pa[resClass].pa_area*scale*scale)*esScale*esScale,
	    ((float)node->efnode_pa[resClass].pa_perim*scale)*esScale);
}

Void spcnAPHier(fterm, hierName, resClass, scale, sterm, m, outf)
    FetTerm *fterm;
    HierName *hierName;
    int  resClass, scale;
    float m;
    char *sterm;
    FILE *outf;
{
	EFNode *node = fterm->fterm_node;
	nodeClientHier   *nc ;
	char fmt[30];

	if ( esFormat == HSPICE )
	     sprintf(fmt,"a%s=%%g p%s=%%g ", sterm, sterm);
	else sprintf(fmt,"a%s=%%gp p%s=%%gu ", sterm, sterm);
	if ( node->efnode_client == (ClientData) NULL ) 
	if ( node->efnode_client == (ClientData) NULL ) 
		initNodeClientHier(node);
	nc = (nodeClientHier *)node->efnode_client;
	if ( nc->lastPrefix != hierName ) {
		clearVisited(nc);
		nc->lastPrefix = hierName;
	}
	if (resClass == NO_RESCLASS ||
	    beenVisited((nodeClientHier *)node->efnode_client, resClass) ) 
		scale = 0;
	else markVisited((nodeClientHier *)node->efnode_client, resClass);
	if ( esFormat == HSPICE )
	   fprintf(outf,fmt,
		node->efnode_pa[resClass].pa_area*scale*scale/m,
		node->efnode_pa[resClass].pa_perim*scale/m);
	else
	   fprintf(outf,fmt,
	    ((float)node->efnode_pa[resClass].pa_area*scale)*esScale*esScale,
	    ((float)node->efnode_pa[resClass].pa_perim*scale)*esScale);
}

/*
 * ----------------------------------------------------------------------------
 *
 * spcfetOutNode --
 *
 * Output the name of the node whose hierarchical prefix down to this
 * point is 'prefix' and whose name from the end of suffix down to the
 * leaves is 'suffix', just as in the arguments to EFHNConcat().
 *
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes to the file 'outf'.
 *	Sets the efnode_client field as described above.
 *
 * ----------------------------------------------------------------------------
 */

Void
spcfetOutNode(prefix, suffix, name, outf)
    HierName *prefix;
    HierName *suffix;
    char *name;
    FILE *outf;
{
    HashEntry *he;
    EFNodeName *nn;

    he = EFHNConcatLook(prefix, suffix, name);
    if (he == NULL)
    {
	(void) fprintf(outf, " errGnd!");
	return;
    }
    nn = (EFNodeName *) HashGetValue(he);
    fprintf(outf, " %s", nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier));
}


/*
 * ----------------------------------------------------------------------------
 *
 * spccapVisit --
 *
 * Procedure to output a single capacitor to the .spice file.
 * Called by EFVisitCaps().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSpiceF. Increments esCapNum.
 *
 * Format of a .spice cap line:
 *
 *	C%d node1 node2 cap
 *
 * where
 *	node1, node2 are the terminals of the capacitor
 *	cap is the capacitance in femtofarads (NOT attofarads).
 *
 * ----------------------------------------------------------------------------
 */

int
spccapVisit(hierName1, hierName2, cap)
    HierName *hierName1;
    HierName *hierName2;
    double cap;
{
    cap = cap / 1000;
    if (cap <= EFCapThreshold)
	return 0;

    fprintf(esSpiceF, esCapFormat ,esCapNum++,nodeSpiceName(hierName1),
                                          nodeSpiceName(hierName2), cap);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * spcresistVisit --
 *
 * Procedure to output a single resistor to the .spice file.
 * Called by EFVisitResists().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSpiceF. Increments esResNum.
 *
 * Format of a .spice resistor line:
 *
 *	R%d node1 node2 res
 *
 * where
 *	node1, node2 are the terminals of the resistor
 *	res is the resistance in ohms (NOT milliohms)
 *
 *
 * ----------------------------------------------------------------------------
 */
int
spcresistVisit(hierName1, hierName2, res)
    HierName *hierName1;
    HierName *hierName2;
    int res;
{
    res = (res+ 500)/1000;

    fprintf(esSpiceF, "R%d %s %s %d\n", esResNum++, nodeSpiceName(hierName1),
                                          nodeSpiceName(hierName2), res);

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * spcnodeVisit --
 *
 * Procedure to output a single node to the .spice file along with its 
 * attributes and its dictionary (if present). Called by EFVisitNodes().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the files esSpiceF
 *
 * ----------------------------------------------------------------------------
 */

int
spcnodeVisit(node, res, cap)
    register EFNode *node;
    int res; 
    double cap;
{
    register EFNodeName *nn;
    HierName *hierName;
    bool isConnected = FALSE;
    char *fmt, *nsn;
    EFAttr *ap;

    if ( node->efnode_client )
    isConnected =  ( esDistrJunct ) ?
	(((nodeClient *)node->efnode_client)->m_w.widths != NULL) :
        (((nodeClient *)node->efnode_client)->m_w.visitMask&FET_CONNECT_MASK);

     if ( !isConnected && esFetNodesOnly) 
	return 0;

    hierName = (HierName *) node->efnode_name->efnn_hier;
    nsn = nodeSpiceName(hierName);

    if (esFormat == SPICE2 || esFormat == HSPICE && strncmp(nsn, "z@", 2)==0 ) {
	static char ntmp[MAX_STR_SIZE];

	EFHNSprintf(ntmp, hierName);
	fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
    }
    cap = cap  / 1000;
    if (cap > EFCapThreshold)
    {
	fprintf(esSpiceF, esCapFormat, esCapNum++, nsn, cap,
			  (isConnected) ?  "\n" : " **FLOATING\n");
    }
    if (node->efnode_attrs && !esNoAttrs)
    {
	(void) fprintf(esSpiceF, "**nodeattr %s :",nsn );
	for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
	{
	    (void) fprintf(esSpiceF, fmt, ap->efa_text);
	    fmt = ",%s";
	}
	putc('\n', esSpiceF);
    }

    return 0;
}

/* a debugging procedure */
int
nodeVisitDebug(node, res, cap)
    register EFNode *node;
    int res; 
    double cap;
{
    register EFNodeName *nn;
    HierName *hierName;
    bool isConnected;
    char *fmt, *nsn;
    EFAttr *ap;
    
    hierName = (HierName *) node->efnode_name->efnn_hier;
    nsn = nodeSpiceName(hierName);
    TxError("** %s (%x)\n", nsn, node);

    printf("\t client.name=%s, client.m_w=%x\n", 
    ((nodeClient *)node->efnode_client)->spiceNodeName,
    ((nodeClient *)node->efnode_client)->m_w.widths);
   return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * nodeSpiceName --
 * Find the real spice name for the node with hierarchical name hname.
 *   SPICE2 ==> numeric
 *   SPICE3 ==> full magic path
 *   HSPICE ==> less than 15 characters long
 *
 * Results:
 *	Returns the spice node name.
 *
 * Side effects:
 *      Allocates nodeClients for the node.
 *
 * ----------------------------------------------------------------------------
 */
static char esTempName[MAX_STR_SIZE];

char *nodeSpiceName(hname)
    HierName *hname;
{
    EFNodeName *nn;
    HashEntry *he;
    EFNode *node;

    he = EFHNLook(hname, (char *) NULL, "nodeName");
    if ( he == NULL )
	return "errGnd!";
    nn = (EFNodeName *) HashGetValue(he);
    node = nn->efnn_node;

    if ( (nodeClient *) (node->efnode_client) == NULL ) {
    	initNodeClient(node);
	goto makeName;
    } else if ( ((nodeClient *) (node->efnode_client))->spiceNodeName == NULL)
	goto makeName;
    else goto retName;


makeName:
    if ( esFormat == SPICE2 ) 
	sprintf(esTempName, "%d", esNodeNum++);
    else {
       EFHNSprintf(esTempName, node->efnode_name->efnn_hier);
       if ( esFormat == HSPICE ) /* more processing */
	nodeHspiceName(esTempName);
    }
   ((nodeClient *) (node->efnode_client))->spiceNodeName = 
	    StrDup(NULL, esTempName);

retName:
    return ((nodeClient *) (node->efnode_client))->spiceNodeName;
}

/*
 * ----------------------------------------------------------------------------
 *
 * EFHNSprintf --
 *
 * Create a hierarchical node name.
 * The flags in EFTrimFlags control whether global (!) or local (#)
 * suffixes are to be trimmed. Also substitutes \. with \@ if the 
 * format is hspice.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Changes the area pointed to by str
 *
 * ----------------------------------------------------------------------------
 */

Void 
EFHNSprintf(str, hierName)
    register char *str;
    HierName *hierName;
{
    bool trimGlob, trimLocal;
    register char *s, *cp, c;
    char *efHNSprintfPrefix();

    s = str;
    if (hierName->hn_parent) str = efHNSprintfPrefix(hierName->hn_parent, str);
    if (EFTrimFlags)
    {
	cp = hierName->hn_name; 
	trimGlob = (EFTrimFlags & EF_TRIMGLOB);
	trimLocal = (EFTrimFlags & EF_TRIMLOCAL);
	while (c = *cp++)
	{
	    switch (c)
	    {
		case '!':	if (!trimGlob) *str++ = c; break;
		case '.':	*str++ = (esFormat == HSPICE)?'@':'.'; break;
		case '#':	if (trimLocal) break;
		default:	*str++ = c; break;
	    }
	}
	*str++ = '\0';
    }
    else strcpy(str, hierName->hn_name);
}

char *efHNSprintfPrefix(hierName, str)
    register HierName *hierName;
    register char *str;
{
    register char *cp, c;

    if (hierName->hn_parent)
	str = efHNSprintfPrefix(hierName->hn_parent, str);

    cp = hierName->hn_name;
    while (*str++ = *cp++) ;
    *(--str) = '/';
    return ++str;
}


/*
 * ----------------------------------------------------------------------------
 *
 * nodeHspiceName --
 *
 * Convert the hierarchical node name used in Berkeley spice 
 * to a name understodd by hspice and hopefully by the user.
 *
 * Results:
 *	A somewhat meaningfull node name
 *
 * Side effects:
 *	Mucks with the hash table above.
 *
 * ----------------------------------------------------------------------------
 */
Void nodeHspiceName(s)
char *s;
{
	register char *p, *sf;
	int l, snum=-1 ;
	HashEntry *he;
	static char map[MAX_STR_SIZE];

	/*
	 * find the suffix 
	 */
	l = strlen(s);
	for ( p = s+l ; (p > s) && *p != '/'  ; p-- ) ; 
	if ( p == s ) {
		sprintf(map, s);
		goto topLevel;
	}
	/* 
	 * break it into prefix '/0' suffix 
	 */
	if ( *p == '/' ) 
	   *p = 0; 
	sf = p+1;

	/* 
	 * look up prefix in the hash table ad create it if doesnt exist 
	 */
	if ( (he = HashLookOnly(&subcktNameTable, s)) == NULL ) {
	        snum = esSbckNum++;
		he = HashFind(&subcktNameTable, s);
		HashSetValue(he, (ClientData) snum);
#ifndef UNSORTED_SUBCKT
		DQPushRear(&subcktNameQueue, he);
#endif
	}
	else 
		snum = (int) HashGetValue(he);
	sprintf(map, "x%d/%s", snum, sf);
topLevel:
	strcpy(s, map);
	if ( strlen(s) > 15 ) { /* still hspice does not get it */
		sprintf(s, "z@%d", esNodeNum++);
		if ( strlen(s) > 15 )
		{ /* screw it: hspice will not work */
		    TxError("Error: too many nodes in this circuit to be "
				"output as names\n");
		    TxError("       use spice2 format or call and complain "
				"to Meta software about their stupid parser\n");
#ifdef MAGIC_WRAPPER
		    return TCL_ERROR;
#else
		  exit(1);
#endif
		}
	}
}

/*
 * ----------------------------------------------------------------------------
 *
 * printSubcktDict --
 *
 * Print out the hspice subcircuit dictionary. Ideally this should go to a
 * pa0 file but uncfortunately hspice crashes if the node names contain
 * dots so we just append it at the end of the spice file
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes to the output file
 *
 * ----------------------------------------------------------------------------
 */
Void printSubcktDict()
{
	HashSearch  hs;
	HashEntry  *he;

	fprintf(esSpiceF,"\n** hspice subcircuit dictionary\n");

#ifndef UNSORTED_SUBCKT
	while (  (he = ( HashEntry *) DQPopFront(&subcktNameQueue) ) != NULL )
#else
	HashStartSearch(&hs);
	while ( (he = HashNext(&subcktNameTable, &hs)) != NULL ) 
#endif
	   fprintf(esSpiceF,"* x%d\t%s\n", HashGetValue(he), he->h_key.h_name);
}

/*
 * ----------------------------------------------------------------------------
 *
 * mkFetMerge --
 * Create a new fetMerge structure.
 *
 * Results:
 *    Obvious
 *
 * Side effects:
 *	Allocates memory and sets the fields of the structure.
 *
 * ----------------------------------------------------------------------------
 */
fetMerge *mkFetMerge(l, w, g, s, d, b, hn, fet)
int     l, w;
EFNode *g, *s, *d, *b;
HierName *hn;
Fet    *fet;
{
	register    fetMerge *fp;

	MALLOC (fetMerge *, fp, sizeof(fetMerge));
	fp->l = l; fp->w = w;
	fp->g = g; fp->s = s;
	fp->d = d; fp->b = b;
	fp->fet = fet;
	fp->esFMIndex = esFMIndex;
	fp->hierName = hn;
	fp->next = NULL;
	addFetMult(1.0);

	return fp;
}



/*
 * ----------------------------------------------------------------------------
 *
 * parallelFets --
 * Macro to look if two fets are in parallel
 *
 * Results:
 *    NOT_PARALLEL  if not in parallel
 *    PARALLEL      if s==s d==d and g==g and bulk=bulk
 *    FLIP_PARALLEL if s==d d==s --->>----------------
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */
#define	NOT_PARALLEL	0
#define	PARALLEL	1
#define	PARALLEL_R	2

#define parallelFets(f1, f2) \
( \
	( (f1)->g == (f2)->g && (f1)->b == (f2)->b && (f1)->l == (f2)->l && \
	  ( esMergeFetsA || (f1)->w == (f2)->w ) )  ? \
	   ( ((f1)->d == (f2)->d && (f1)->s == (f2)->s ) ? \
	       PARALLEL : \
	      (((f1)->s == (f2)->d && (f1)->d == (f2)->s ) ? PARALLEL_R : NOT_PARALLEL) )\
	  : NOT_PARALLEL  \
)

/*
 * ----------------------------------------------------------------------------
 *
 * mergeAttr --
 * Macro to merge two attribute strings
 *
 * Results:
 *  The merged strings
 *
 * Side effects:
 *  Might allocate and free memory.
 *
 * ----------------------------------------------------------------------------
 */
#define	mergeAttr(a1, a2) { \
	if ( a1 == NULL ) a1 = a2 ; \
	else { \
		char *t; \
		int l1 = strlen(a1); \
		int l2 = strlen(a2); \
		MALLOC(char *, t, (l1+l2)+1); \
		t = (char *) strcat(a1,a2); \
		FREE(a1); \
		a1 = t ; \
	} \
}


/*
 * ----------------------------------------------------------------------------
 *
 * fetMergeVisit --
 * Visits each fet throu EFVisitFets and finds if it is in parallel with
 * any previously visited fet.
 *
 * Results:
 *  0 always to keep the caller going.
 *
 * Side effects:
 *  Numerous.
 *
 * ----------------------------------------------------------------------------
 */
int
fetMergeVisit(fet, hierName, trans, l, w)
Fet *fet;		/* Fet to examine */
HierName *hierName;		/* Hierarchical path down to this fet */
Transform *trans;		/* Coordinate transform not used */
int l, w;
{
	FetTerm *gate, *source, *drain;
	Fet     *cf;
	FetTerm *cg, *cs, *cd;
	EFNode *subnode, *snode, *dnode, *gnode;
	int      scale, pmode;
	bool     hS, hD, chS, chD;
    	Rect r;
	register fetMerge *fp, *cfp;
	float m;

	if (esDistrJunct)
		fetDistJunctVisit(fet, hierName, trans, l, w);

	if (fet->fet_nterm < 2) {
		TxError("outPremature\n");
		return 0;
	}

	gate = &fet->fet_terms[0];
	source = drain = &fet->fet_terms[1];
	if (fet->fet_nterm >= 3)
		drain = &fet->fet_terms[2];


	gnode = GetNode (hierName, gate->fterm_node->efnode_name->efnn_hier);
	snode = GetNode (hierName, source->fterm_node->efnode_name->efnn_hier);
	dnode = GetNode (hierName, drain->fterm_node->efnode_name->efnn_hier);
	subnode = spcfetSubstrate(hierName, fet->fet_subsnode->efnode_name->efnn_hier, 
			               fet->fet_type, NULL);
    	GeoTransRect(trans, &fet->fet_rect, &r);
    	scale = GeoScale(trans);
	fp = mkFetMerge(l*scale, w*scale, gnode, snode, 
			dnode, subnode, hierName, fet);
	hS = extHierSDAttr(source); hD = extHierSDAttr(drain);
	/*
	 * run the list of fets. compare the current one with 
	 * each one in the list. if they fullfill the matching requirements
	 * merge them only if:
	 * 1) they have both apf S, D attributes
	 * or 
	 * 2) one of them has aph S, D attributes and they have the same 
	 *    hierarchical prefix
	 * If one of them has apf and the other aph print a warning.
	 */
	for ( cfp = fetMergeList ; cfp != NULL ; cfp = cfp->next ) {
	  if ((pmode = parallelFets(fp, cfp)) != NOT_PARALLEL) {

		cf = cfp->fet;
		cg = &cfp->fet->fet_terms[0];
		cs = cd = &cfp->fet->fet_terms[1];
		if (cfp->fet->fet_nterm >= 3) {
			if ( pmode == PARALLEL ) 
				cd = &cfp->fet->fet_terms[2];
			else if ( pmode == PARALLEL_R ) 
				cs = &cfp->fet->fet_terms[2];
		}
		

		chS = extHierSDAttr(cs); chD = extHierSDAttr(cd);
		if ( ! (chS||chD||hS||hD) ) /* all flat S, D */
			goto mergeThem;
		if ( cfp->hierName != hierName && 
		    ( (chS && !hS) || ( chD && !hD ) ||
		      (!chS && hS) || ( !chD && hD )    ) ) { 

		   efHNSprintfPrefix((cfp->hierName)?cfp->hierName:hierName,
				     esTempName);
		   TxError("Warning: conflicting SD attributes of parallel fets in cell: %s\n", esTempName);
		   break;
		} else if ( cfp->hierName == hierName ) {
		   if ( hS && !chS ) {
			mergeAttr(cs->fterm_attrs, source->fterm_attrs);
		   }
		   if ( hD && !chD ) {
			mergeAttr(cd->fterm_attrs, drain->fterm_attrs);
		   }
		} else /* cfp->hierName != hierName */
			break;
mergeThem:
		m = esFMult[cfp->esFMIndex] + ((float)fp->w/(float)cfp->w);
		setFetMult(fp->esFMIndex, FET_KILLED); 
		setFetMult(cfp->esFMIndex, m);
		esFetsMerged++;
		/* Need to do attribute stuff here */
		FREE(fp);
		return 0;
	  }
	}
	/* No parallel fets to it yet */
	fp->next = fetMergeList;
	fetMergeList = fp;
	return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * update_w --
 * Updates the width client of node n with the current fet width
 *
 * Results:
 *  N/A
 *
 * Side effects:
 *  might allocate node client and widths
 *
 * ----------------------------------------------------------------------------
 */
Void 
update_w(resClass, w,  n)
short  resClass;
int w;
EFNode *n;
{
	nodeClient *nc;
	int i;

	if ( n->efnode_client == NULL ) 
		initNodeClient(n);
	nc = (nodeClient *) n->efnode_client;
	if ( nc->m_w.widths == NULL ) {
		MALLOC(float *, (nc->m_w.widths), sizeof(float)*efNumResistClasses);
		for ( i=0; i<EFFetNumTypes ; i++ )
			nc->m_w.widths[i] = 0.0;
	}
	nc->m_w.widths[resClass] += (float) w;
}



/*
 * ----------------------------------------------------------------------------
 *
 * fetDistJunctVisit --
 *  Called for every fet and updates the nodeclients of its terminals
 *
 * Results:
 *  0 to keep the calling procedure going
 *
 * Side effects:
 *  calls update_w which might allocate stuff
 *
 * ----------------------------------------------------------------------------
 */
int 
fetDistJunctVisit(fet, hierName, trans, l, w)
Fet *fet;		/* Fet to examine */
HierName *hierName;		/* Hierarchical path down to this fet */
Transform *trans;		/* Coordinate transform not used */
int l, w;
{
	EFNode  *n;
	int      scale, i;
    	Rect r;

	if (fet->fet_nterm < 2) {
		TxError("outPremature\n");
		return 0;
	}

    	GeoTransRect(trans, &fet->fet_rect, &r);
    	scale = GeoScale(trans);
	w = w*scale;
	
	for ( i = 1; i<fet->fet_nterm; i++ ) {
		n = GetNode(hierName, fet->fet_terms[i].fterm_node->efnode_name->efnn_hier);
		update_w(fetInfo[fet->fet_type].resClassSD, w, n);
	}
	return 0;
}

#ifndef MAGIC_WRAPPER

/* debugging stuff */
#include <varargs.h>

#define DBG 1

DBPRINT( va_alist )
  va_dcl
{
    va_list  args;
    char     *fmt;
    FILE     *fp;
    char     buff[ 300 ];

    if ( DBG ) {
	va_start( args );
	fp = stderr ;
	fmt = va_arg( args, char * );
	(void) vsprintf( buff, fmt, args );
	va_end( args );

	(void) fputs( buff, fp );
    }
}

#endif
