/*
 * Electric(tm) VLSI Design System
 *
 * File: netflat.c
 * Network tool: module for fully instantiating a hierarchical network
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

/*
 * The usage of this module is:
 *   #include "network.h"
 *   PCOMP *pcomp;
 *   PNET *pnet;
 *   INTSML components, nets, powernets, groundnets;
 *   pcomp = net_makepseudo(facet, &components, &nets, &powernets, &groundnets,
 *	&pnet, hierarchical, sortports, useequiv);
 *   .....
 *        do something with the network in "pcomp" and "pnet"
 *   .....
 *   net_freeallpnet(pnet);
 *   net_freeallpcomp(pcomp);
 *
 * which builds a pseudonetwork structure that represents the network in
 * facet "facet".  If "net_makepseudo" returns NOPCOMP, there is an error.
 * If "hierarchical" is nonzero, the network will be fully instantiated.
 * A linked list of PCOMP objects is returned, one for every component
 * in the pseudonetwork.  A linked list of PNET objects is also returned,
 * one for every network in the pseudonetwork.  Finally, the number of
 * components, networks, power networks, and ground networks is returned
 * in the reference parameters "components", "nets", "powernets", and
 * "groundnets".
 */

#include "global.h"
#include "network.h"
#include "efunction.h"

static INTSML    net_pseudonode;	/* net number for pseudonetworks */
static NODEINST *net_toplevelinst;	/* actual top-instance associated with this node */

/* prototypes for local routines */
PCOMP *net_buildpseudo(NODEPROTO*, PCOMP*, PNET**, PNET**, INTSML*, PNET**, INTSML, INTSML, INTSML);
PNET *net_newpnet(PNET**);
PCOMP *net_allocpcomp(void);
void net_freepcomp(PCOMP*);
PCONN *net_allocpconn(void);
void net_freepconn(PCONN*);
PNET *net_allocpnet(void);
void net_freepnet(PNET*);

/*
 * Routine to free all memory associated with this module.
 */
void net_freeflatmemory(void)
{
	REGISTER PCOMP *p;
	REGISTER PCONN *pc;
	REGISTER PNET *pn;

	while (net_pcompfree != NOPCOMP)
	{
		p = net_pcompfree;
		net_pcompfree = net_pcompfree->nextpcomp;
		efree((char *)p);
	}
	while (net_pconnfree != NOPCONN)
	{
		pc = net_pconnfree;
		net_pconnfree = net_pconnfree->nextpconn;
		efree((char *)pc);
	}
	while (net_pnetfree != NOPNET)
	{
		pn = net_pnetfree;
		net_pnetfree = net_pnetfree->nextpnet;
		efree((char *)pn);
	}
}

/*********************** PSEUDO-NETWORK CONSTRUCTION ***********************/

/*
 * routine to make a list of pseudo-components from the contents of facet "facet"
 * and return the list.  Returns the number of pseudo-components in "components",
 * the number of pseudo-nets in "nets", the number of power nets in "powernets",
 * and the number of ground nets in "groundnets".  Extraction is done
 * hierarchically if "comphier" is nonzero.  Ports are sorted alphabetically
 * in each component if "sortports" is nonzero.  Equivalences are used if
 * "useequivs" is nonzero.  Returns NOPCOMP if there is an error
 */
PCOMP *net_makepseudo(NODEPROTO *facet, INTSML *components, INTSML *nets, INTSML *powernets,
	INTSML *groundnets, PNET **pnetlist, INTSML comphier, INTSML sortports, INTSML useequivs)
{
	REGISTER PCONN *w;
	REGISTER PCOMP *pcomp, *pcomplist, *p;
	REGISTER INTSML list, i;
	PNET *power, *ground;
	REGISTER PNET *net;
	REGISTER PORTPROTO *pp, *opp;

	/* first create net numbers external to this facet */
	net_pseudonode = 0;
	*pnetlist = NOPNET;
	power = ground = NOPNET;
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		pp->temp1 = (INTBIG)NOPNET;
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->temp1 != (INTBIG)NOPNET) continue;
		net = net_newpnet(pnetlist);
		if (net == NOPNET) return(NOPCOMP);
		if ((pp->userbits&STATEBITS) == PWRPORT)
		{
			net->flags = POWERNET;
			power = net;
		} else if ((pp->userbits&STATEBITS) == GNDPORT)
		{
			net->flags = GROUNDNET;
			ground = net;
		}
		net->flags |= EXPORTEDNET;
		net->realport = pp;
		pp->temp1 = (INTBIG)net;
		for(opp = pp->nextportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
			if (pp->network == opp->network) opp->temp1 = pp->temp1;
	}

	/* create a list of pseudocomponents in this facet */
	*components = 0;
	begintraversehierarchy();
	net_toplevelinst = NONODEINST;
	pcomplist = net_buildpseudo(facet, NOPCOMP, &power, &ground, components,
		pnetlist, comphier, sortports, useequivs);
	if (pcomplist == 0) return(NOPCOMP);
	if (*components == 0)
	{
		ttyputmsg("There are no components to associate in facet %s", describenodeproto(facet));
		return(NOPCOMP);
	}

	/* report the total number of nets */
	*nets = net_pseudonode;

	/* count the power and ground nets */
	*powernets = *groundnets = 0;
	for(net = *pnetlist; net != NOPNET; net = net->nextpnet)
	{
		if ((net->flags&POWERNET) != 0) (*powernets)++;
		if ((net->flags&GROUNDNET) != 0) (*groundnets)++;
	}

	/* now run through each network, accumulating pointers */
	for(pcomp = pcomplist; pcomp != NOPCOMP; pcomp = pcomp->nextpcomp)
	{
		for(list=0; list<pcomp->wirecount; list++)
		{
			net = pcomp->netnumbers[list];

			/* accumulate all other nodes that have this connection */
			for(p = pcomplist; p != NOPCOMP; p = p->nextpcomp)
			{
				for(i=0; i<p->wirecount; i++)
				{
					if (p->netnumbers[i] != net) continue;
					if (p == pcomp && i == list) continue;

					/* create a pseudo-net object for this */
					w = net_allocpconn();
					if (w == NOPCONN) return(NOPCOMP);
					w->nextpconn = pcomp->pconnlist[list];
					pcomp->pconnlist[list] = w;
					w->pcomp = p;
					pcomp->count[list]++;
				}
			}
		}
	}
	return(pcomplist);
}

/*
 * routine to build a linked list of pseudocomponents in facet "facet".  The
 * list is appended to "initiallist" and returned.  The values of "power" and
 * "ground" are the PNETs of such components.  Routine increments the
 * integer at "components" for every component created.  If
 * "compare_hierarchically" is nonzero, net is flattened.  The value zero is
 * returned on error.
 */
PCOMP *net_buildpseudo(NODEPROTO *facet, PCOMP *initiallist, PNET **power,
	PNET **ground, INTSML *components, PNET **pnetlist, INTSML compare_hierarchically,
	INTSML sortports, INTSML useequivs)
{
	REGISTER PCOMP *pcomp;
	REGISTER PCONN *pswap;
	REGISTER PORTPROTO *pp, *opp, *realpp;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER ARCINST *ai, *oai;
	REGISTER NODEINST *ni;
	REGISTER INTSML fun, i, toldshort, swap, still, toplevel, flattenit;
	REGISTER NODEPROTO *realnp;
	REGISTER PNET *net;
	REGISTER EQUIV *e;

	if (net_toplevelinst == NONODEINST) toplevel = 1; else
		toplevel = 0;

	/* place pseudo-netnumbers on all arcs, taken from ports */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->temp1 = (INTBIG)NOPNET;
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			if (ai->network == pp->network) ai->temp1 = pp->temp1;
	}

	/* spread power and ground information from appropriate nodes */
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto->primindex == 0) continue;
		fun = net_getfunction(ni);
		if (fun != NPCONPOWER && fun != NPCONGROUND) continue;
		if (fun == NPCONPOWER)
		{
			net = *power;
			if (net == NOPNET)
			{
				net = net_newpnet(pnetlist);
				net->realport = NOPORTPROTO;
				net->flags = POWERNET;
				*power = net;
			}
		}
		if (fun == NPCONGROUND)
		{
			net = *ground;
			if (net == NOPNET)
			{
				net = net_newpnet(pnetlist);
				net->realport = NOPORTPROTO;
				net->flags = GROUNDNET;
				*ground = net;
			}
		}
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			ai = pi->conarcinst;
			if (ai->temp1 != (INTBIG)NOPNET)
			{
				if (ai->temp1 != (INTBIG)net)
				{
					ttyputerr("Warning: power/ground node connected to non-power/ground port in facet %s",
						describenodeproto(facet));
				}
			}
			ai->temp1 = (INTBIG)net;
			for(oai = facet->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
				if (oai->network == ai->network) oai->temp1 = ai->temp1;
		}
	}

	/* generate new pseudo-netnumbers for arcs not connected to ports */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->temp1 != (INTBIG)NOPNET) continue;
		net = net_newpnet(pnetlist);
		if (net == NOPNET) return(0);
		ai->temp1 = (INTBIG)net;
		net->realport = NOPORTPROTO;
		for(oai = ai->nextarcinst; oai != NOARCINST; oai = oai->nextarcinst)
			if (oai->network == ai->network) oai->temp1 = ai->temp1;
	}

	/* search every component in the facet */
	toldshort = 0;
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (toplevel != 0) net_toplevelinst = ni;

		/* if flattening the circuit, explore contents of facet instances */
		flattenit = 0;
		if (ni->proto->primindex == 0 && compare_hierarchically != 0) flattenit = 1;

		if (flattenit != 0 && useequivs != 0)
		{
			/* see if this is equivalenced */
			for(e = net_firstequiv; e != NOEQUIV; e = e->nextequiv)
			{
				/* check the validity of the equivalence */
				if (e->first == ni || e->second == ni) flattenit = 0;
			}
		}

		if (flattenit != 0)		
		{
			/* if there is an alternate contents facet, use it */
			realnp = contentsview(ni->proto);
			if (realnp == NONODEPROTO) realnp = ni->proto;

			/* put pseudo-netnumbers on the edge of this facet */
			for(realpp = realnp->firstportproto; realpp != NOPORTPROTO; realpp = realpp->nextportproto)
				realpp->temp1 = (INTBIG)NOPNET;
			for(realpp = realnp->firstportproto; realpp != NOPORTPROTO; realpp = realpp->nextportproto)
			{
				if (realpp->temp1 != (INTBIG)NOPNET) continue;

				/* if there is an alternate contents facet, compute the port */
				if (realnp == ni->proto) pp = realpp; else
				{
					pp = equivalentport(realnp, realpp, ni->proto);
					if (pp == NOPORTPROTO) pp = ni->proto->firstportproto;
				}

				/* see if an arc connects to the port */
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					if (pi->proto->network == pp->network) break;
				if (pi != NOPORTARCINST) realpp->temp1 = pi->conarcinst->temp1; else
				{
					/* see if the port is exported */
					for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
						if (pe->proto->network == pp->network) break;
					if (pe != NOPORTEXPINST) realpp->temp1 = pe->exportproto->temp1; else
					{
						net = net_newpnet(pnetlist);
						if (net == NOPNET) return(0);
						net->realport = NOPORTPROTO;
						if ((pp->userbits&STATEBITS) == PWRPORT)
						{
							net->flags = POWERNET;
						} else if ((pp->userbits&STATEBITS) == GNDPORT)
						{
							net->flags = GROUNDNET;
						}
						net->flags |= EXPORTEDNET;
						realpp->temp1 = (INTBIG)net;
					}
				}

				/* propagate net numbers to other connected ports */
				for(opp = realpp->nextportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
					if (opp->network == realpp->network)
						opp->temp1 = realpp->temp1;
			}

			/* recurse into the facet */
			downhierarchy(ni);
			initiallist = net_buildpseudo(realnp, initiallist, power, ground,
				components, pnetlist, compare_hierarchically, sortports, useequivs);
			uphierarchy();
			if (initiallist == 0) return(0);
			continue;
		}

		/* nonflattenable component: add it to the pseudocomponent list */
		if (ni->proto->primindex == 0) fun = NPUNKNOWN; else
		{
			fun = net_getfunction(ni);
			if (fun == NPCONNECT || fun == NPART || fun == NPUNKNOWN) continue;

			/* allow only one power and one ground component */
			if (fun == NPCONPOWER && *power != NOPNET) continue;
			if (fun == NPCONGROUND && *ground != NOPNET) continue;
		}

		/* create a pseudo-component */
		pcomp = net_allocpcomp();
		if (pcomp == NOPCOMP) return(0);
		pcomp->nextpcomp = initiallist;
		initiallist = pcomp;
		pcomp->id = -1;
		pcomp->function = fun;
		pcomp->actual = ni;
		pcomp->topactual = net_toplevelinst;
		(*components)++;

		/* count the number of electrically distinct nets on the component */
		pcomp->wirecount = 0;
		for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			/* special case for isolated ports */
			if ((pp->userbits&PORTISOLATED) != 0)
			{
				/* add one wire for each arc on the port */
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					if (pi->proto == pp) pcomp->wirecount++;
				continue;
			}

			/* see if this port is equivalent to a previous one */
			for(opp = ni->proto->firstportproto; opp != pp; opp = opp->nextportproto)
				if (opp->network == pp->network) break;
			if (opp != pp) continue;

			/* new port */
			pcomp->wirecount++;
		}

		/* allocate the port and connection lists */
		if (pcomp->wirecount != 0)
		{
			pcomp->pconnlist = (PCONN **)emalloc(((sizeof (PCONN *)) * pcomp->wirecount),
				net_aid->cluster);
			if (pcomp->pconnlist == 0) return(0);
			pcomp->portlist = (PORTPROTO **)emalloc(((sizeof (PORTPROTO *)) * pcomp->wirecount),
				net_aid->cluster);
			if (pcomp->portlist == 0) return(0);
			pcomp->count = (INTSML *)emalloc((SIZEOFINTSML * pcomp->wirecount),
				net_aid->cluster);
			if (pcomp->count == 0) return(0);
			pcomp->state = (INTSML *)emalloc((SIZEOFINTSML * pcomp->wirecount),
				net_aid->cluster);
			if (pcomp->state == 0) return(0);
			pcomp->netnumbers = (PNET **)emalloc(((sizeof (PNET *)) * pcomp->wirecount),
				net_aid->cluster);
			if (pcomp->netnumbers == 0) return(0);
			for(i=0; i<pcomp->wirecount; i++)
			{
				pcomp->pconnlist[i] = NOPCONN;
				pcomp->count[i] = 0;
				pcomp->state[i] = 0;
			}
		}

		/* initialize the lists */
		pcomp->wirecount = 0;
		for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			/* special case for isolated ports */
			if ((pp->userbits&PORTISOLATED) != 0)
			{
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					if (pi->proto == pp)
				{
					/* pickup the network number of this connection */
					ai = pi->conarcinst;
					pcomp->netnumbers[pcomp->wirecount] = (PNET *)ai->temp1;
					pcomp->portlist[pcomp->wirecount] = pi->proto;
					if ((ai->userbits&ISNEGATED) != 0)
					{
						if ((ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) ||
							(ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0))
								pcomp->state[pcomp->wirecount] = NEGATEDPORT;
					}
					if ((pcomp->netnumbers[pcomp->wirecount]->flags&EXPORTEDNET) != 0)
						pcomp->state[pcomp->wirecount] |= EXPORTEDPORT;
					pcomp->wirecount++;
				}
				continue;
			}

			/* see if this port is equivalent to a previous one */
			for(opp = ni->proto->firstportproto; opp != pp; opp = opp->nextportproto)
				if (opp->network == pp->network) break;
			if (opp != pp) continue;

			/* new wire */
			pcomp->portlist[pcomp->wirecount] = pp;
			pcomp->netnumbers[pcomp->wirecount] = NOPNET;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				if (pi->proto->network == pp->network)
			{
				/* pickup the network number of this connection */
				ai = pi->conarcinst;
				pcomp->netnumbers[pcomp->wirecount] = (PNET *)ai->temp1;
				if ((ai->userbits&ISNEGATED) != 0)
				{
					if ((ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) ||
						(ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0))
							pcomp->state[pcomp->wirecount] = NEGATEDPORT;
				}
				if ((pcomp->netnumbers[pcomp->wirecount]->flags&EXPORTEDNET) != 0)
					pcomp->state[pcomp->wirecount] |= EXPORTEDPORT;
				break;
			}
			if (pcomp->netnumbers[pcomp->wirecount] == NOPNET)
			{
				for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
					if (pe->proto->network == pp->network)
				{
					pcomp->netnumbers[pcomp->wirecount] = (PNET *)pe->exportproto->temp1;
					pcomp->state[pcomp->wirecount] |= EXPORTEDPORT;
					break;
				}
			}
			if (pcomp->netnumbers[pcomp->wirecount] == NOPNET)
			{
				net = net_newpnet(pnetlist);
				if (net == NOPNET) return(0);
				net->realport = NOPORTPROTO;
				pcomp->netnumbers[pcomp->wirecount] = net;
			}
			pcomp->wirecount++;
		}

		/* sort the list if requested */
		if (sortports != 0)
		{
			still = 1;
			while (still != 0)
			{
				still = 0;
				for(i=1; i<pcomp->wirecount; i++)
				{
					if (namesame(pcomp->portlist[i]->protoname, pcomp->portlist[i-1]->protoname) >= 0)
						continue;
					still = 1;
					opp = pcomp->portlist[i];
					pcomp->portlist[i] = pcomp->portlist[i-1];
					pcomp->portlist[i-1] = opp;
					swap = pcomp->state[i];
					pcomp->state[i] = pcomp->state[i-1];
					pcomp->state[i-1] = swap;
					swap = pcomp->count[i];
					pcomp->count[i] = pcomp->count[i-1];
					pcomp->count[i-1] = swap;
					net = pcomp->netnumbers[i];
					pcomp->netnumbers[i] = pcomp->netnumbers[i-1];
					pcomp->netnumbers[i-1] = net;
					pswap = pcomp->pconnlist[i];
					pcomp->pconnlist[i] = pcomp->pconnlist[i-1];
					pcomp->pconnlist[i-1] = pswap;
					break;
				}
			}
		}

		/* for power or ground components, set the characteristics on the nets */
		if (fun == NPCONPOWER || fun == NPCONGROUND)
		{
			for(i=0; i<pcomp->wirecount; i++)
			{
				if (fun == NPCONPOWER)
				{
					if ((pcomp->netnumbers[i]->flags&GROUNDNET) != 0 && toldshort == 0)
					{
						ttyputerr("Power and ground are shorted in %s", describenodeproto(facet));
						toldshort++;
					}
					pcomp->netnumbers[i]->flags |= POWERNET;
					*power = pcomp->netnumbers[i];
				}
				if (fun == NPCONGROUND)
				{
					if ((pcomp->netnumbers[i]->flags&POWERNET) != 0 && toldshort == 0)
					{
						ttyputerr("Power and ground are shorted in %s", describenodeproto(facet));
						toldshort++;
					}
					pcomp->netnumbers[i]->flags |= GROUNDNET;
					*ground = pcomp->netnumbers[i];
				}
			}
		}
	}

	return(initiallist);
}

/*********************** ALLOCATION ***********************/

/*
 * routine to create a new PNET module and increase the net number count in the
 * global "net_pseudonode", add the module to the list in "pnetlist", and
 * return the module.  Returns NOPNET on error.
 */
PNET *net_newpnet(PNET **pnetlist)
{
	REGISTER PNET *p;

	p = net_allocpnet();
	if (p == 0) return(NOPNET);
	p->flags = 0;
	p->realport = NOPORTPROTO;
	net_pseudonode++;
	p->nextpnet = *pnetlist;
	*pnetlist = p;
	return(p);
}

/*
 * routine to allocate a new pcomp module from the pool (if any) or memory
 */
PCOMP *net_allocpcomp(void)
{
	REGISTER PCOMP *p;

	if (net_pcompfree == NOPCOMP)
	{
		p = (PCOMP *)emalloc(sizeof (PCOMP), net_aid->cluster);
		if (p == 0) return(NOPCOMP);
	} else
	{
		p = net_pcompfree;
		net_pcompfree = (PCOMP *)p->nextpcomp;
	}
	return(p);
}

/*
 * routine to return pcomp module "p" to the pool of free modules
 */
void net_freepcomp(PCOMP *p)
{
	p->nextpcomp = net_pcompfree;
	net_pcompfree = p;
}

/*
 * routine to allocate a new pconn module from the pool (if any) or memory
 */
PCONN *net_allocpconn(void)
{
	REGISTER PCONN *p;

	if (net_pconnfree == NOPCONN)
	{
		p = (PCONN *)emalloc(sizeof (PCONN), net_aid->cluster);
		if (p == 0) return(NOPCONN);
	} else
	{
		p = net_pconnfree;
		net_pconnfree = (PCONN *)p->nextpconn;
	}
	return(p);
}

/*
 * routine to return pconn module "p" to the pool of free modules
 */
void net_freepconn(PCONN *p)
{
	p->nextpconn = net_pconnfree;
	net_pconnfree = p;
}

/*
 * routine to allocate a new pnet module from the pool (if any) or memory
 */
PNET *net_allocpnet(void)
{
	REGISTER PNET *p;

	if (net_pnetfree == NOPNET)
	{
		p = (PNET *)emalloc(sizeof (PNET), net_aid->cluster);
		if (p == 0) return(NOPNET);
	} else
	{
		p = net_pnetfree;
		net_pnetfree = (PNET *)p->nextpnet;
	}

	p->flags = 0;
	return(p);
}

/*
 * routine to return pnet module "p" to the pool of free modules
 */
void net_freepnet(PNET *p)
{
	p->nextpnet = net_pnetfree;
	net_pnetfree = p;
}

/*
 * routine to free all allocated structures in the list of pseudonets
 * headed by "pnetlist"
 */
void net_freeallpnet(PNET *pnetlist)
{
	REGISTER PNET *p, *nextp;

	for(p = pnetlist; p != NOPNET; p = nextp)
	{
		nextp = p->nextpnet;
		net_freepnet(p);
	}
}

/*
 * routine to free all allocated structures in the list of pseudocomponents
 * headed by "tp"
 */
void net_freeallpcomp(PCOMP *tp)
{
	REGISTER PCOMP *p, *nextp;
	REGISTER PCONN *n, *nextn;
	REGISTER INTSML i;

	for(p = tp; p != NOPCOMP; p = nextp)
	{
		nextp = p->nextpcomp;

		for(i=0; i<p->wirecount; i++)
		{
			for(n = p->pconnlist[i]; n != NOPCONN; n = nextn)
			{
				nextn = n->nextpconn;
				net_freepconn(n);
			}
		}
		if (p->wirecount != 0)
		{
			efree((char *)p->pconnlist);
			efree((char *)p->portlist);
			efree((char *)p->count);
			efree((char *)p->state);
			efree((char *)p->netnumbers);
		}
		net_freepcomp(p);
	}
}

/*********************** COMPONENT FUNCTION ***********************/

#define	NOTRANMODEL ((TRANMODEL *)-1)

typedef struct Itranmodel
{
	char *modelname;
	INTSML tmindex;
	struct Itranmodel *nexttranmodel;
} TRANMODEL;

static TRANMODEL *net_firsttranmodel = NOTRANMODEL;

/* must be larger than largest node function entry in "efunction.h" */
static INTSML net_tranmodelindex = 100;

/*
 * routine to return the function of node "ni"
 */
INTSML net_getfunction(NODEINST *ni)
{
	char *dummy;
	REGISTER INTSML fun;
	REGISTER PORTEXPINST *pe;
	REGISTER TRANMODEL *tm;

	fun = nodefunction(ni, &dummy);
	switch (fun)
	{
		case NPTRANS:
			fun = NPTRANMOS;

		case NPTRANSREF:
			/* self-referential transistor: lookup the string in the table */
			for(tm = net_firsttranmodel; tm != NOTRANMODEL; tm = tm->nexttranmodel)
				if (namesame(tm->modelname, ni->proto->primname) == 0) break;
			if (tm == NOTRANMODEL)
			{
				/* new table entry */
				tm = (TRANMODEL *)emalloc(sizeof (TRANMODEL), net_aid->cluster);
				if (tm == 0) break;
				(void)allocstring(&tm->modelname, ni->proto->primname, net_aid->cluster);
				tm->tmindex = net_tranmodelindex++;
				tm->nexttranmodel = net_firsttranmodel;
				net_firsttranmodel = tm;
			}
			fun = tm->tmindex;
			break;

		case NPPIN:
		case NPNODE:
		case NPCONTACT:
		case NPWELL:
		case NPSUBSTRATE:
			fun = NPCONNECT;

		case NPCONNECT:
			/* unify the representations of power and ground */
			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
			{
				if (portispower(pe->exportproto) != 0) fun = NPCONPOWER; else
					if (portisground(pe->exportproto) != 0) fun = NPCONGROUND;
			}
			break;
	}
	return(fun);
}
