/*
 * Electric(tm) VLSI Design System
 *
 * File: netdiff.c
 * Network tool: module for network comparison
 * 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
 */

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

static INTSML     net_bucketids;				/* for numbering buckets */
static PCOMP     *net_pcomp1 = NOPCOMP, *net_pcomp2 = NOPCOMP;
static PNET      *net_nodelist1 = NOPNET, *net_nodelist2 = NOPNET;
static NODEPROTO *net_facet1, *net_facet2;

/* working memory for "net_diffwires()" */
static INTSML     net_difflist1size = 0, net_difflist2size = 0;
static INTSML    *net_difflist1, *net_difflist2;

/* working memory for "net_diffwirelist()" */
static INTSML     net_difflistsize = 0;
static INTSML    *net_difflist;

/* working memory for "net_allalike()" */
static INTSML     net_diffalikelistsize = 0;
static INTSML    *net_diffalikelist;

static INTBIG     net_ncc_options;				/* options to use in NCC */

/*********************** BUCKET MODULES ***********************/

#define	NOBUCKET ((BUCKET *)-1)

typedef struct Ibucket
{
	INTSML          bucketid;				/* id of this bucket */
	INTSML          function;				/* function of this component */
	INTBIG          length;					/* length of this component (if FET) */
	INTBIG          width;					/* width of this component (if FET) */
	INTSML          wirecount;				/* number of wires on this component */
	INTSML          net1count;				/* number of components from net 1 */
	PCOMP          *net1list;				/* linked list of net 1 components */
	INTSML          net2count;				/* number of components from net 2 */
	PCOMP          *net2list;				/* linked list of net 2 components */
	struct Ibucket *nextbucket;				/* next in list */
} BUCKET;
static BUCKET *net_firstbucket = NOBUCKET;
static BUCKET *net_bucketfree = NOBUCKET;
static INTSML  net_bucketnumber = 1;

/*********************** EQUIV MODULES ***********************/

EQUIV *net_equivfree = NOEQUIV;
EQUIV *net_firstequiv = NOEQUIV;

/* prototypes for local routines */
INTSML  net_addbucket(PCOMP*, INTSML, INTSML);
INTSML  net_allalike(PCOMP*, INTSML);
BUCKET *net_allocbucket(INTSML, INTSML);
EQUIV  *net_allocequiv(void);
INTSML  net_diffwirelist(PCOMP*, PCOMP*, BUCKET*, BUCKET*, INTSML);
INTSML  net_diffwires(PCONN*, INTSML, INTSML, PNET*, PCONN*, INTSML, INTSML, PNET*, BUCKET*, BUCKET*);
BUCKET *net_findsimilarbucket(PCOMP*, INTSML, INTSML);
void    net_freeallbuckets(void);
void    net_freebucket(BUCKET*);
void    net_freeequiv(EQUIV*);
INTSML  net_getfacets(NODEPROTO**, NODEPROTO**);
void    net_movecomp(PCOMP*, BUCKET*, BUCKET*, INTSML);
INTSML  net_nccalreadydone(NODEPROTO *facet1, NODEPROTO *facet2);
INTSML  net_ncconelevel(NODEPROTO *facet1, NODEPROTO *facet2);
void    net_printbuckets(BUCKET*, NODEPROTO*, NODEPROTO*);
char   *net_printwirelist(PCOMP*);
void    net_reportunusedpnet(NODEPROTO *facet, PNET *pn);
INTSML  net_samewirelist(PCOMP*, PCOMP*, INTSML, INTSML);
INTSML  net_scancomplist(BUCKET*, INTSML, INTSML, INTSML);

/*********************** ALLOCATION ROUTINES ***********************/

/*
 * Routine to free all memory associated with this module.
 */
void net_freediffmemory(void)
{
	REGISTER BUCKET *b;
	REGISTER EQUIV *e;

	if (net_pcomp1 != NOPCOMP) net_freeallpcomp(net_pcomp1);
	if (net_pcomp2 != NOPCOMP) net_freeallpcomp(net_pcomp2);
	if (net_nodelist1 != NOPNET) net_freeallpnet(net_nodelist1);
	if (net_nodelist2 != NOPNET) net_freeallpnet(net_nodelist2);

	net_freeallbuckets();
	while (net_bucketfree != NOBUCKET)
	{
		b = net_bucketfree;
		net_bucketfree = net_bucketfree->nextbucket;
		efree((char *)b);
	}
	while (net_equivfree != NOEQUIV)
	{
		e = net_equivfree;
		net_equivfree = net_equivfree->nextequiv;
		efree((char *)e);
	}
	if (net_difflistsize > 0) efree((char *)net_difflist);
	if (net_difflist1size > 0) efree((char *)net_difflist1);
	if (net_difflist2size > 0) efree((char *)net_difflist2);
	if (net_diffalikelistsize > 0) efree((char *)net_diffalikelist);
}

/*
 * routine to allocate a new bucket module from the pool (if any) or memory
 */
BUCKET *net_allocbucket(INTSML fun, INTSML count)
{
	REGISTER BUCKET *b;

	if (net_bucketfree == NOBUCKET)
	{
		b = (BUCKET *)emalloc(sizeof (BUCKET), net_aid->cluster);
		if (b == 0) return(NOBUCKET);
	} else
	{
		b = net_bucketfree;
		net_bucketfree = (BUCKET *)b->nextbucket;
	}

	/* take module from free list */
	b->function = fun;
	b->wirecount = count;
	b->length = 0;
	b->width = 0;
	b->net1list = NOPCOMP;
	b->net1count = 0;
	b->net2list = NOPCOMP;
	b->net2count = 0;
	return(b);
}

/*
 * routine to return bucket module "b" to the pool of free modules
 */
void net_freebucket(BUCKET *b)
{
	b->nextbucket = net_bucketfree;
	net_bucketfree = b;
}

/*
 * routine to allocate a new equiv module from the pool (if any) or memory
 */
EQUIV *net_allocequiv(void)
{
	REGISTER EQUIV *e;

	if (net_equivfree == NOEQUIV)
	{
		e = (EQUIV *)emalloc(sizeof (EQUIV), net_aid->cluster);
		if (e == 0) return(NOEQUIV);
	} else
	{
		e = net_equivfree;
		net_equivfree = (EQUIV *)e->nextequiv;
	}
	return(e);
}

/*
 * routine to return equiv module "e" to the pool of free modules
 */
void net_freeequiv(EQUIV *e)
{
	e->nextequiv = net_equivfree;
	net_equivfree = e;
}

/*********************** INTERFACE ROUTINES ***********************/

/*
 * routine to identify the currently highlighted node as the first of
 * two objects that must be equated
 */
INTSML net_setfirst(void)
{
	REGISTER NODEINST *ni;
	REGISTER INTSML fun;
	REGISTER EQUIV *e;

	ni = (NODEINST *)askaid(us_aid, "get-node");
	if (ni == NONODEINST) return(1);

	fun = net_getfunction(ni);
	if (fun == NPCONNECT)
	{
		ttyputerr(_("Must associate active components"));
		return(1);
	}
	if (fun == NPCONPOWER || fun == NPCONGROUND)
	{
		ttyputerr(_("Power and ground cannot be equated to other nodes"));
		return(1);
	}

	e = net_allocequiv();
	if (e == NOEQUIV) return(1);
	e->nextequiv = net_firstequiv;
	net_firstequiv = e;
	e->first = ni;
	e->second = NONODEINST;
	return(0);
}

/*
 * routine to identify the currently highlighted node as the second of
 * two objects that must be equated
 */
INTSML net_setsecond(void)
{
	REGISTER NODEINST *ni;
	REGISTER INTSML fun;
	REGISTER EQUIV *e;
	REGISTER NODEPROTO *facet1, *facet2;

	ni = (NODEINST *)askaid(us_aid, "get-node");
	if (ni == NONODEINST) return(1);

	fun = net_getfunction(ni);
	if (fun == NPCONNECT)
	{
		ttyputerr(_("Must associate active components"));
		return(1);
	}
	if (fun == NPCONPOWER || fun == NPCONGROUND)
	{
		ttyputerr(_("Power and ground cannot be equated to other nodes"));
		return(1);
	}

	e = net_firstequiv;
	if (e == NOEQUIV || e->second != NONODEINST)
	{
		ttyputerr(_("Must select the a 'First' equate before the second"));
		return(1);
	}

	if (e->first->parent == ni->parent)
	{
		ttyputerr(_("Two equated nodes must be in different facets"));
		return(1);
	}
	e->second = ni;

	/* make sure the two facets are consistent in the list */
	facet1 = e->first->parent;
	facet2 = e->second->parent;

	for(e = net_firstequiv; e != NOEQUIV; e = e->nextequiv)
	{
		if (e->first->parent == facet1 && e->second->parent == facet2) continue;
		if (e->first->parent == facet2 && e->second->parent == facet1) continue;
		ttyputmsg(_("Warning: set of equivalences are from more than two facets"));
		break;
	}
	return(0);
}

/*
 * routine to automatically set equivalences for all nodes that have the
 * same name.
 */
INTSML net_setequiv(void)
{
	REGISTER NODEINST *ni1, *ni2;
	NODEPROTO *facet1, *facet2;
	REGISTER INTSML fun1, fun2;
	REGISTER EQUIV *e;
	REGISTER VARIABLE *var1, *var2;

	if (net_getfacets(&facet1, &facet2) != 0)
	{
		ttyputerr(_("Must have two windows with two different facets"));
		return(1);
	}

	for(ni1 = facet1->firstnodeinst; ni1 != NONODEINST; ni1 = ni1->nextnodeinst)
	{
		var1 = getvalkey((INTBIG)ni1, VNODEINST, VSTRING, el_node_name);
		if (var1 == NOVARIABLE) continue;
		fun1 = net_getfunction(ni1);
		if (fun1 == NPCONNECT) continue;
		if (fun1 == NPCONPOWER || fun1 == NPCONGROUND) continue;

		/* search other facet for this name */
		for(ni2 = facet2->firstnodeinst; ni2 != NONODEINST; ni2 = ni2->nextnodeinst)
		{
			var2 = getvalkey((INTBIG)ni2, VNODEINST, VSTRING, el_node_name);
			if (var2 == NOVARIABLE) continue;
			if (namesame((char *)var1->addr, (char *)var2->addr) != 0) continue;
			fun2 = net_getfunction(ni2);
			if (fun2 == NPCONNECT) continue;
			if (fun2 == NPCONPOWER || fun2 == NPCONGROUND) continue;

			/* set an equivalence between the two nodes */
			e = net_allocequiv();
			if (e == NOEQUIV) return(1);
			e->nextequiv = net_firstequiv;
			net_firstequiv = e;
			e->first = ni1;
			e->second = ni2;
			ttyputmsg(_("Associating component %s"), (char *)var1->addr);
		}
	}
	return(0);
}

/*
 * routine to clear the list of equivalents for comparison
 */
INTSML net_clearequiv(void)
{
	REGISTER EQUIV *e, *nexte;

	if (net_firstequiv == NOEQUIV)
	{
		ttyputmsg(_("No equivalences to clear"));
		return(0);
	}
	for(e = net_firstequiv; e != NOEQUIV; e = nexte)
	{
		nexte = e->nextequiv;
		net_freeequiv(e);
	}
	net_firstequiv = NOEQUIV;
	return(0);
}

/*
 * routine to identify the equivalent object associated with the currently
 * highlighted one (comparison must have been done).  If "noise" is nonzero,
 * report errors.  Otherwise simply return.
 */
INTSML net_equate(INTSML noise)
{
	REGISTER GEOM *obj;
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni;
	REGISTER BUCKET *b;
	REGISTER PCOMP *p, *op;
	REGISTER INTSML i, j, first;
	REGISTER NODEPROTO *np;
	REGISTER NETWORK *initialnet;

	/* make sure an association has been done */
	if (net_firstbucket == NOBUCKET || net_pcomp1 == NOPCOMP || net_pcomp2 == NOPCOMP)
	{
		if (noise != 0) ttyputerr(_("First associate with '-tellaid network compare'"));
		return(1);
	}

	/* get the highlighted object */
	obj = (GEOM *)askaid(us_aid, "get-object");
	if (obj == NOGEOM)
	{
		if (noise != 0) ttyputerr(_("Must select something to be equated"));
		return(1);
	}

	/* make sure this object is in one of the associated facets */
	np = geomparent(obj);
	if (np != net_facet1 && np != net_facet2)
	{
		if (noise != 0) ttyputerr(_("This object is not in one of the two associated facets"));
		return(1);
	}

	/* highlight the associated object */
	if (obj->entrytype == OBJNODEINST)
	{
		ni = obj->entryaddr.ni;
		for(p = (np==net_facet1 ? net_pcomp1 : net_pcomp2); p != NOPCOMP; p = p->nextpcomp)
			if (p->actual == ni) break;
		if (p != NOPCOMP && p->function != NPCONPOWER && p->function != NPCONGROUND)
		{
			b = (BUCKET *)p->id;

			/* do not highlight if this is the only object in its class */
			if (b->net1count + b->net2count == 1)
			{
				if (noise != 0) ttyputmsg(_("This node is not associated with anything else"));
				return(1);
			}
			(void)askaid(us_aid, "clear");
			(void)initinfstr();
			first = 0;
			for(p = b->net1list; p != NOPCOMP; p = p->nextinbucket)
			{
				if (first != 0) (void)addtoinfstr('\n');
				first++;
				(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
					describenodeproto(geomparent(p->actual->geom)), (INTBIG)p->actual->geom);
			}
			for(p = b->net2list; p != NOPCOMP; p = p->nextinbucket)
			{
				if (first != 0) (void)addtoinfstr('\n');
				first++;
				(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
					describenodeproto(geomparent(p->actual->geom)), (INTBIG)p->actual->geom);
			}
			(void)askaid(us_aid, "show-multiple", (INTBIG)returninfstr());
			return(0);
		}

		/* node is not in buckets: must be a contact, use the arc */
		initialnet = net_gethighlightednet(0, 1);
		if (initialnet == NONETWORK) return(1);
	} else initialnet = obj->entryaddr.ai->network;

	/* search current facet for netnumber associated with arc "ai" */
	for(p = (np==net_facet1 ? net_pcomp1 : net_pcomp2); p != NOPCOMP; p = p->nextpcomp)
	{
		for(i=0; i<p->wirecount; i++)
			if (p->netnumbers[i]->realport != NOPORTPROTO &&
				p->netnumbers[i]->realport->network == initialnet) break;
		if (i < p->wirecount) break;
	}
	if (p == NOPCOMP)
	{
		if (noise != 0) ttyputmsg(_("Cannot find this arc in the comparison network"));
		return(1);
	}
	b = (BUCKET *)p->id;

	/* find the equivalent pseudocomponent in the other facet */
	for(op = (np==net_facet1 ? b->net2list : b->net1list); op != NOPCOMP; op = op->nextpcomp)
	{
		for(j=0; j<op->wirecount; j++)
			if (net_diffwires(op->pconnlist[j], op->count[j], op->state[j], op->netnumbers[j],
				p->pconnlist[i], p->count[i], p->state[i], p->netnumbers[i], b, b) == 0) break;
		if (j < op->wirecount) break;
	}
	if (op == NOPCOMP)
	{
		if (noise != 0) ttyputmsg(_("Cannot find associated arc in other facet"));
		return(1);
	}

	/* highlight network "realnet" in facet 2 */
	(void)askaid(us_aid, "clear");
	(void)initinfstr();
	first = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		if (ai->network == initialnet)
	{
		if (first != 0) (void)addtoinfstr('\n');
		first++;
		(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
			describenodeproto(ai->parent), (INTBIG)ai->geom);
	}
	if (np == net_facet1) ai = net_facet2->firstarcinst; else
		ai = net_facet1->firstarcinst;
	if (op->netnumbers[j]->realport != NOPORTPROTO)
		for( ; ai != NOARCINST; ai = ai->nextarcinst)
			if (ai->network == op->netnumbers[j]->realport->network)
	{
		if (first != 0) (void)addtoinfstr('\n');
		first++;
		(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
			describenodeproto(ai->parent), (INTBIG)ai->geom);
	}
	(void)askaid(us_aid, "show-multiple", (INTBIG)returninfstr());
	return(0);
}

/*
 * routine to compare the two networks on the screen
 */
INTSML net_compare(void)
{
	REGISTER NODEPROTO *np;
	NODEPROTO *facet1, *facet2;
	REGISTER LIBRARY *lib;
	REGISTER INTSML ret;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0)
	{
		ttyputerr(_("Network tool must be running...turning it on for you"));
		aidturnon(net_aid, 0);
		return(1);
	}

	if (net_getfacets(&facet1, &facet2) != 0)
	{
		ttyputerr(_("Must have two windows with two different facets"));
		return(1);
	}

	/* if the top facets are already checked, stop now */
	if (net_nccalreadydone(facet1, facet2) != 0)
	{
		ttyputmsg("Facets are already checked");
		return(0);
	}

	/* mark all facets as not-checked */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = -1;

	initerrorlogging(_("NCC"));
	ret = net_ncconelevel(facet1, facet2);
	termerrorlogging();
	return(ret);
}

/*
 * Routine to compare facets "facet1" and "facet2" at this level of hierarchy.
 * Returns netative on error, zero if they compare, positive if they are different.
 */
INTSML net_ncconelevel(NODEPROTO *facet1, NODEPROTO *facet2)
{
	REGISTER NODEINST *ni, *oni;
	REGISTER NODEPROTO *subnp1, *subnp2;
	REGISTER BUCKET *b;
	REGISTER GEOM *g;
	REGISTER INTSML changed, ret, found, b1count, b2count, subfacetsbad;
	REGISTER UINTBIG curtime;
	REGISTER EQUIV *e;
	REGISTER void *err;
	INTBIG i;
	INTSML comp1, comp2, net1, net2, power1, power2, ground1, ground2,
		hierarchical, unifyportnames, ignorepwrgnd, recurse, checksize,
		localchecksize1, localchecksize2, localunifyportnames1, localunifyportnames2,
		localunifyportnames;
	REGISTER VARIABLE *var;
	REGISTER PNET *pn;
	REGISTER PCOMP *p, *p1, *p2;

	/* stop if already checked */
	if (facet1->temp1 == 0 && facet2->temp1 == 0) return(0);
	if (facet1->temp1 >= 0 && facet2->temp1 >= 0) return(1);
	if (net_nccalreadydone(facet1, facet2) != 0)
	{
		facet1->temp1 = facet2->temp1 = 0;
		return(0);
	}

	/* get options to use during comparison */
	var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
	if (var == NOVARIABLE) net_ncc_options = 0; else
		net_ncc_options = (INTSML)var->addr;
	recurse = 0;
	if ((net_ncc_options&NCCHIERARCHICAL) != 0) hierarchical = 1; else
	{
		hierarchical = 0;
		if ((net_ncc_options&NCCRECURSE) != 0) recurse = 1;
	}
	if ((net_ncc_options&NCCUNIFYPORTNAMES) != 0) unifyportnames = 1; else
		unifyportnames = 0;
	if ((net_ncc_options&NCCIGNOREPWRGND) != 0) ignorepwrgnd = 1; else
		ignorepwrgnd = 0;
	if ((net_ncc_options&NCCCHECKSIZE) != 0) checksize = 1; else
		checksize = 0;

	/* check for facet overrides */
	localchecksize1 = localchecksize2 = checksize;
	localunifyportnames1 = localunifyportnames2 = unifyportnames;
	var = getvalkey((INTBIG)facet1, VNODEPROTO, VINTEGER, net_ncc_optionskey);
	if (var != NOVARIABLE)
	{
		if ((var->addr&NCCCHECKSIZEOVER) != 0)
		{
			if ((var->addr&NCCCHECKSIZE) != 0) localchecksize1 = 1; else
				localchecksize1 = 0;
		}
		if ((var->addr&NCCUNIFYPORTNAMESOVER) != 0)
		{
			if ((var->addr&NCCUNIFYPORTNAMES) != 0) localunifyportnames1 = 1; else
				localunifyportnames1 = 0;
		}
	}
	var = getvalkey((INTBIG)facet2, VNODEPROTO, VINTEGER, net_ncc_optionskey);
	if (var != NOVARIABLE)
	{
		if ((var->addr&NCCCHECKSIZEOVER) != 0)
		{
			if ((var->addr&NCCCHECKSIZE) != 0) localchecksize2 = 1; else
				localchecksize2 = 0;
		}
		if ((var->addr&NCCUNIFYPORTNAMESOVER) != 0)
		{
			if ((var->addr&NCCUNIFYPORTNAMES) != 0) localunifyportnames2 = 1; else
				localunifyportnames2 = 0;
		}
	}
	if (localchecksize1 != 0 || localchecksize2 != 0) checksize = 1; else
		if (localchecksize1 == 0 && localchecksize2 == 0 && checksize != 0) checksize = 0;
	if (localunifyportnames1 != 0 || localunifyportnames2 != 0) localunifyportnames = 1; else
		localunifyportnames = 0;

	/* if recursing, look at subfacets first */
	subfacetsbad = 0;
	if (recurse != 0)
	{
		for(ni = facet1->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			subnp1 = ni->proto;
			if (subnp1->primindex != 0) continue;
			if (subnp1->cellview == el_iconview)
			{
				subnp1 = anyview(subnp1, facet1->cellview);
				if (subnp1 == NONODEPROTO) continue;
			}

			/* find equivalent to this in the other view */
			subnp2 = anyview(subnp1, facet2->cellview);
			if (subnp2 == NONODEPROTO)
			{
				ttyputerr(_("Cannot find %s view of facet %s"), facet2->cellview->viewname,
					describenodeproto(subnp1));
				continue;
			}
			ret = net_ncconelevel(subnp1, subnp2);
			if (ret < 0)
			{
				facet1->temp1 = facet2->temp1 = 1;
				return(ret);
			}
			if (ret > 0) subfacetsbad = 1;
		}
		for(ni = facet2->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			subnp2 = ni->proto;
			if (subnp2->primindex != 0) continue;
			if (subnp2->cellview == el_iconview)
			{
				subnp2 = anyview(subnp2, facet2->cellview);
				if (subnp2 == NONODEPROTO) continue;
			}

			/* find equivalent to this in the other view */
			subnp1 = anyview(subnp2, facet1->cellview);
			if (subnp1 == NONODEPROTO)
			{
				ttyputerr(_("Cannot find %s view of facet %s"), facet1->cellview->viewname,
					describenodeproto(subnp2));
				continue;
			}
			ret = net_ncconelevel(subnp1, subnp2);
			if (ret < 0)
			{
				facet1->temp1 = facet2->temp1 = 1;
				return(ret);
			}
			if (ret > 0) subfacetsbad = 1;
		}
	}

	/* free any previous data structures */
	if (net_pcomp1 != NOPCOMP)
	{
		net_freeallpcomp(net_pcomp1);
		net_pcomp1 = NOPCOMP;
	}
	if (net_pcomp2 != NOPCOMP)
	{
		net_freeallpcomp(net_pcomp2);
		net_pcomp2 = NOPCOMP;
	}
	if (net_nodelist1 != NOPNET) net_freeallpnet(net_nodelist1);
	if (net_nodelist2 != NOPNET) net_freeallpnet(net_nodelist2);
	if (net_firstbucket != NOBUCKET)
	{
		net_freeallbuckets();
		net_firstbucket = NOBUCKET;
	}

	/* build network of pseudocomponents */
	net_pcomp1 = net_makepseudo(facet1, &comp1, &net1, &power1, &ground1,
		&net_nodelist1, hierarchical, unifyportnames, 1, ignorepwrgnd, 1);
	if (net_pcomp1 == NOPCOMP)
	{
		facet1->temp1 = facet2->temp1 = 1;
		return(-1);
	}
	net_pcomp2 = net_makepseudo(facet2, &comp2, &net2, &power2, &ground2,
		&net_nodelist2, hierarchical, unifyportnames, 1, ignorepwrgnd, 1);
	if (net_pcomp2 == NOPCOMP)
	{
		facet1->temp1 = facet2->temp1 = 1;
		return(-1);
	}
	net_facet1 = facet1;   net_facet2 = facet2;

	/* describe the task ahead */
	ttyputmsg(_("*** Comparing facet %s (%d components, %d nets) with facet %s (%d components, %d nets)"),
		describenodeproto(facet1), comp1, net1, describenodeproto(facet2), comp2, net2);
	if (ignorepwrgnd == 0)
	{
		if (power1 != power2 || ground1 != ground2)
		{
			ttyputmsg(_("Note: facet %s has %d power and %d ground net(s),"),
				describenodeproto(facet1), power1, ground1);
			ttyputmsg(_("while facet %s has %d power and %d ground net(s)"),
				describenodeproto(facet2), power2, ground2);
		} else if (power1 > 1 || ground1 > 1)
			ttyputmsg(_("Note: there are %d power nets and %d ground nets"), power1, ground1);
	}

	/* starting point for numbering buckets */
	net_bucketids = 1;

	/* create known equivalences in unalterable buckets */
	found = 0;
	for(e = net_firstequiv; e != NOEQUIV; e = e->nextequiv)
	{
		/* check the validity of the equivalence */
		if (e->first->parent == facet2 && e->second->parent == facet1)
		{
			ni = e->first;   e->first = e->second;   e->second = ni;
		}
		if (e->first->parent != facet1 || e->second->parent != facet2)
			continue;
		for(oni = facet1->firstnodeinst; oni != NONODEINST; oni = oni->nextnodeinst)
			if (oni == e->first) break;
		if (oni == NONODEINST) continue;
		for(oni = facet2->firstnodeinst; oni != NONODEINST; oni = oni->nextnodeinst)
			if (oni == e->second) break;
		if (oni == NONODEINST) continue;

		/* find the pseudocomponents these nodes */
		for(p1 = net_pcomp1; p1 != NOPCOMP; p1 = p1->nextpcomp)
			if (p1->actual == e->first) break;
		for(p2 = net_pcomp2; p2 != NOPCOMP; p2 = p2->nextpcomp)
			if (p2->actual == e->second) break;
		if (p1 == NOPCOMP || p2 == NOPCOMP) continue;

		/* cannot equate the same node twice */
		if ((BUCKET *)p1->id != NOBUCKET || (BUCKET *)p2->id != NOBUCKET) continue;

		/* create a bucket with "e" */
		b = net_allocbucket(-1, 0);
		if (b == NOBUCKET)
		{
			facet1->temp1 = facet2->temp1 = 1;
			return(-1);
		}
		b->bucketid = net_bucketids++;
		b->nextbucket = net_firstbucket;
		net_firstbucket = b;
		p1->id = (INTBIG)b;
		p2->id = (INTBIG)b;

		/* place first node in first list */
		p1->nextinbucket = b->net1list;
		b->net1list = p1;
		b->net1count++;

		/* place second node in second list */
		p2->nextinbucket = b->net2list;
		b->net2list = p2;
		b->net2count++;
		found++;
	}
	if (found != 0) ttyputmsg(_("Using %d sets of equivalences"), found);

	/* add all nodes in facet 1 to buckets */
	for(p = net_pcomp1; p != NOPCOMP; p = p->nextpcomp)
	{
		if ((BUCKET *)p->id != NOBUCKET) continue;
		if (net_addbucket(p, 1, checksize) == 0) continue;
		ttyputnomemory();
		facet1->temp1 = facet2->temp1 = 1;
		return(-1);
	}

	/* add all nodes in facet 2 to buckets */
	for(p = net_pcomp2; p != NOPCOMP; p = p->nextpcomp)
	{
		if ((BUCKET *)p->id != NOBUCKET) continue;
		if (net_addbucket(p, 2, checksize) == 0) continue;
		ttyputnomemory();
		facet1->temp1 = facet2->temp1 = 1;
		return(-1);
	}

	/* do some simple bucket analysis to spare trouble later */
	for(b = net_firstbucket; b != NOBUCKET; b = b->nextbucket)
	{
		if (b->function == NPCONPOWER && b->net1count != b->net2count)
		{
			if (b->net1count == 0)
			{
				ttyputmsg(_("Warning: facet %s has no components supplying power"),
					describenodeproto(facet1));
				continue;
			}
			if (b->net2count == 0)
			{
				ttyputmsg(_("Warning: facet %s has no components supplying power"),
					describenodeproto(facet2));
				continue;
			}
			ttyputmsg(_("Warning: facet %s has %d power components, but %s has %d"),
				describenodeproto(facet1), b->net1count, describenodeproto(facet2),
					b->net2count);
			continue;
		}
		if (b->function == NPCONGROUND && b->net1count != b->net2count)
		{
			if (b->net1count == 0)
			{
				ttyputmsg(_("Warning: facet %s has no components on ground"),
					describenodeproto(facet1));
				continue;
			}
			if (b->net2count == 0)
			{
				ttyputmsg(_("Warning: facet %s has no components on ground"),
					describenodeproto(facet2));
				continue;
			}
			ttyputmsg(
				_("Warning: facet %s has %d ground components, facet %s has %d"),
					describenodeproto(facet1), b->net1count,
						describenodeproto(facet2), b->net2count);
			continue;
		}
	}

	/* print buckets before NCC */
	if ((net_ncc_options&NCCVERBOSE) != 0)
	{
		ttyputmsg(_("*************** INITIAL BUCKET STRUCTURE:"));
		net_printbuckets(net_firstbucket, facet1, facet2);
		ttyputmsg(_("*************** BUCKET MANIPULATION:"));
	}

	/* iteratively split buckets */
	changed = 1;
	while (changed != 0)
	{
		if (stopping(STOPREASONNCC)) break;
		changed = 0;
		for(b = net_firstbucket; b != NOBUCKET; b = b->nextbucket)
		{
			/* ignore unwired components */
			if (b->wirecount == 0) continue;

			/* ignore buckets that are already associated */
			if (b->net1count == 1 && b->net2count == 1) continue;

			/* ignore buckets that don't have two components */
			if (b->net1count + b->net2count < 2) continue;

			/* examine each component in list 1 for possible splitting */
			ret = net_scancomplist(b, 1, checksize, localunifyportnames);
			if (ret < 0)
			{
				facet1->temp1 = facet2->temp1 = 1;
				return(-1);
			}
			if (ret > 0) changed++;

			/* examine each component in list 2 for possible splitting */
			ret = net_scancomplist(b, 2, checksize, localunifyportnames);
			if (ret < 0)
			{
				facet1->temp1 = facet2->temp1 = 1;
				return(-1);
			}
			if (ret > 0) changed++;
		}
	}

	/* print buckets after NCC */
	if ((net_ncc_options&NCCVERBOSE) != 0)
	{
		/* print buckets */
		ttyputmsg(_("*************** FINAL BUCKET STRUCTURE:"));
		net_printbuckets(net_firstbucket, facet1, facet2);
	}

	/* look for unassociated components */
	found = 0;
	for(b = net_firstbucket; b != NOBUCKET; b = b->nextbucket)
	{
		b1count = b->net1count;   b2count = b->net2count;

		/* if all components are wired in parallel, consider it a single one */
		if (net_allalike(b->net1list, localunifyportnames)) b1count = 1;
		if (net_allalike(b->net2list, localunifyportnames)) b2count = 1;

		/* ignore fully associated buckets */
		if (b1count == 1 && b2count == 1)
		{
			/* make sure they are really the same, even if in the same bucket */
			if (net_diffwirelist(b->net1list, b->net2list, b, b, localunifyportnames) == 0) continue;

			g = b->net1list->topactual->geom;
			err = logerror(_("Nodes are wired differently"), geomparent(g), 0);
			addgeomtoerror(err, g, 1);
			addgeomtoerror(err, b->net2list->topactual->geom, 1);
			found++;
			continue;
		}

		/* show unassociated components */
		if (b1count == 0 && b2count != 0)
		{
			err = logerror(_("Unassociated nodes"), NONODEPROTO, 0);

			for(p = b->net2list; p != NOPCOMP; p = p->nextinbucket)
				addgeomtoerror(err, p->topactual->geom, 1);
			addfacettoerror(err, facet1);
			found++;
			continue;
		}
		if (b2count == 0 && b1count != 0)
		{
			err = logerror(_("Unassociated nodes"), NONODEPROTO, 0);

			for(p = b->net1list; p != NOPCOMP; p = p->nextinbucket)
				addgeomtoerror(err, p->topactual->geom, 1);
			addfacettoerror(err, facet2);
			found++;
			continue;
		}

		err = logerror(_("Ambiguous nodes"), NONODEPROTO, 0);
		for(p = b->net1list; p != NOPCOMP; p = p->nextinbucket)
			addgeomtoerror(err, p->topactual->geom, 1);
		for(p = b->net2list; p != NOPCOMP; p = p->nextinbucket)
			addgeomtoerror(err, p->topactual->geom, 1);
		addfacettoerror(err, facet1);
		addfacettoerror(err, facet2);
		found++;
	}

	/* show unassociated networks */
	for(pn = net_nodelist1; pn != NOPNET; pn = pn->nextpnet)
		pn->flags &= ~USEDNET;
	for(pn = net_nodelist2; pn != NOPNET; pn = pn->nextpnet)
		pn->flags &= ~USEDNET;
	for(b = net_firstbucket; b != NOBUCKET; b = b->nextbucket)
	{
		for(p = b->net1list; p != NOPCOMP; p = p->nextinbucket)
		{
			for(i=0; i<p->wirecount; i++)
				p->netnumbers[i]->flags |= USEDNET;
		}
		for(p = b->net2list; p != NOPCOMP; p = p->nextinbucket)
		{
			for(i=0; i<p->wirecount; i++)
				p->netnumbers[i]->flags |= USEDNET;
		}
	}
	for(pn = net_nodelist1; pn != NOPNET; pn = pn->nextpnet)
	{
		if ((pn->flags&USEDNET) != 0) continue;
		net_reportunusedpnet(facet1, pn);
		found++;
	}
	for(pn = net_nodelist2; pn != NOPNET; pn = pn->nextpnet)
	{
		if ((pn->flags&USEDNET) != 0) continue;
		net_reportunusedpnet(facet2, pn);
		found++;
	}

	/* write summary of NCC */
	if (found != 0)
	{
		ttyputmsg(_("******* Facets are different!"));
		ret = 1;
	} else
	{
		if (subfacetsbad == 0)
		{
			curtime = getcurrenttime();
			(void)setvalkey((INTBIG)facet1, VNODEPROTO, net_lastgoodncckey,
				(INTBIG)curtime, VINTEGER);
			(void)setvalkey((INTBIG)facet2, VNODEPROTO, net_lastgoodncckey,
				(INTBIG)curtime, VINTEGER);
		}
		ttyputmsg(_("Facets are equivalent"));
		ret = subfacetsbad;
	}
	facet1->temp1 = facet2->temp1 = ret;
	return(ret);
}

/*
 * Routine to report that pseudo-net "pn" had no association during NCC.
 */
void net_reportunusedpnet(NODEPROTO *deffacet, PNET *pn)
{
	REGISTER NODEPROTO *np;
	REGISTER NETWORK *net;
	REGISTER ARCINST *ai;
	REGISTER void *err;

	net = pn->network;
	if (net == NONETWORK)
	{
		err = logerror(_("Facet contains unassociated network"), deffacet, 0);
		return;
	}

	np = net->parent;
	if (net->namecount <= 0) err = logerror(_("Unassociated network (has no name)"), np, 0); else
	{
		(void)initinfstr();
		(void)formatinfstr(_("Unassociated network: %s"), net->netname);
		err = logerror(returninfstr(), np, 0);
	}	
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		if (ai->network == net)
			addgeomtoerror(err, ai->geom, 1);
}

/*
 * Routine to return nonzero if the facets "facet1" and "facet2" are already NCC'd.
 */
INTSML net_nccalreadydone(NODEPROTO *facet1, NODEPROTO *facet2)
{
	REGISTER VARIABLE *var1, *var2;
	REGISTER UINTBIG lastgooddate1, lastgooddate2;

	var1 = getvalkey((INTBIG)facet1, VNODEPROTO, VINTEGER, net_lastgoodncckey);
	var2 = getvalkey((INTBIG)facet2, VNODEPROTO, VINTEGER, net_lastgoodncckey);
	if (var1 == NOVARIABLE || var2 == NOVARIABLE) return(0);
	lastgooddate1 = (UINTBIG)var1->addr;
	lastgooddate2 = (UINTBIG)var2->addr;
	if (facet1->revisiondate <= lastgooddate1 && facet2->revisiondate <= lastgooddate2)
		return(1);
	return(0);
}

INTSML net_allalike(PCOMP *p, INTSML unifyportnames)
{
	REGISTER PCOMP *nx;
	REGISTER INTSML i, j, firsti;

	/* wire count must be the same */
	for( ; p != NOPCOMP && p->nextinbucket != NOPCOMP; p = p->nextinbucket)
	{
		nx = p->nextinbucket;
		if (p->wirecount != nx->wirecount) return(0);
	}
	if (p == NOPCOMP) return(0);

	/* if ports must match in sequence, check is simpler */
	if (p->function == NPTRANPN || p->function == NPTRAPNP ||
		p->function == NPDIODE || p->function == NPDIODEZ ||
		p->function == NPBUFFER || p->function == NPFLIPFLOP ||
		unifyportnames != 0)
	{
		for( ; p != NOPCOMP && p->nextinbucket != NOPCOMP; p = p->nextinbucket)
		{
			nx = p->nextinbucket;
			for(i=0; i<p->wirecount; i++)
				if (net_samewirelist(p, nx, i, i) == 0) return(0);
		}
		return(1);
	}

	/* make sure there is memory for flags corresponding to list */
	if (p->wirecount > net_diffalikelistsize)
	{
		if (net_diffalikelistsize != 0) efree((char *)net_diffalikelist);
		net_diffalikelist = (INTSML *)emalloc((SIZEOFINTSML * p->wirecount), net_aid->cluster);
		net_diffalikelistsize = p->wirecount;
	}

	/* special case for transistors: gates must match without permutation */
	if (p->function == NPTRANMOS || p->function == NPTRAPMOS || p->function == NPTRADMOS)
	{
		for( ; p != NOPCOMP && p->nextinbucket != NOPCOMP; p = p->nextinbucket)
		{
			nx = p->nextinbucket;
			if (net_samewirelist(p, nx, 0, 0) == 0) return(0);
		}
		firsti = 1;
	} else firsti = 0;

	/* check for like-components wired in parallel */
	for( ; p != NOPCOMP && p->nextinbucket != NOPCOMP; p = p->nextinbucket)
	{
		nx = p->nextinbucket;

		/* reset flags in list */
		for(j=firsti; j<p->wirecount; j++) net_diffalikelist[j] = 0;

		/* compare wire lists */
		for(i=firsti; i<p->wirecount; i++)
		{
			for(j=i; j<p->wirecount; j++)
			{
				if (net_diffalikelist[j] != 0) continue;
				if (net_samewirelist(p, nx, i, j) != 0) break;
			}
			if (j > p->wirecount) return(0);
			net_diffalikelist[j] = 1;
		}
	}
	return(1);
}

INTSML net_samewirelist(PCOMP *p1, PCOMP *p2, INTSML i1, INTSML i2)
{
	REGISTER BUCKET *b1, *b2;
	REGISTER PCONN *n1, *n2;

	if ((p1->state[i1]&NEGATEDPORT) != (p2->state[i2]&NEGATEDPORT)) return(0);
	if ((p1->state[i1]&EXPORTEDPORT) != (p2->state[i2]&EXPORTEDPORT)) return(0);

	for(n1 = p1->pconnlist[i1], n2 = p2->pconnlist[i2]; n1 != NOPCONN && n2 != NOPCONN;
		n1 = n1->nextpconn, n2 = n2->nextpconn)
	{
		b1 = (BUCKET *)n1->pcomp->id;
		b2 = (BUCKET *)n2->pcomp->id;
		if (b1->bucketid != b2->bucketid) return(0);
	}
	return(1);
}

void net_printbuckets(BUCKET *buck, NODEPROTO *facet1, NODEPROTO *facet2)
{
	REGISTER char *funname;
	REGISTER BUCKET *b;
	REGISTER PCOMP *p;
	REGISTER NODEINST *ni;

	for(b = buck; b != NOBUCKET; b = b->nextbucket)
	{
		if (b->net1list != NOPCOMP) ni = b->net1list->actual; else
			if (b->net2list != NOPCOMP) ni = b->net2list->actual; else
				ni = NONODEINST;
		funname = nodefunctionname(b->function, ni);
		if (*funname == 0 && ni != NONODEINST)
			funname = nodefunctionname((INTSML)((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH), ni);
		if (b->width != 0 || b->length != 0)
		{
			(void)initinfstr();
			(void)formatinfstr("%sx%s %s", latoa(b->length), latoa(b->width), funname);
			funname = returninfstr();
		}
		ttyputmsg(_("Bucket %d (%s attached to %d networks) has"), b->bucketid, funname, b->wirecount);
		if (b->net1count > 0)
		{
			ttyputmsg(_("  %d components from facet %s:"), b->net1count, describenodeproto(facet1));
			for(p = b->net1list; p != NOPCOMP; p = p->nextinbucket)
				ttyputmsg("    %s %s", describenodeinst(p->actual), net_printwirelist(p));
		}
		if (b->net2count > 0)
		{
			ttyputmsg(_("  %d components from facet %s:"), b->net2count, describenodeproto(facet2));
			for(p = b->net2list; p != NOPCOMP; p = p->nextinbucket)
				ttyputmsg("    %s %s", describenodeinst(p->actual), net_printwirelist(p));
		}
	}
}

char *net_printwirelist(PCOMP *p)
{
	char line[100];
	REGISTER PCONN *n;
	REGISTER INTSML i;
	REGISTER BUCKET *b;
	REGISTER PORTPROTO *pp;

	if (p == NOPCOMP) return("");
	(void)initinfstr();
	for(i=0; i<p->wirecount; i++)
	{
		if (i > 0) (void)addstringtoinfstr(" ");
		(void)addstringtoinfstr("[");
		if ((p->state[i]&NEGATEDPORT) != 0) (void)addstringtoinfstr("~");
		if ((p->state[i]&EXPORTEDPORT) != 0)
		{
			pp = p->netnumbers[i]->realport;
			if (pp != NOPORTPROTO)
				(void)formatinfstr("%s: ", pp->protoname);
		}
		for(n = p->pconnlist[i]; n != NOPCONN; n = n->nextpconn)
		{
			if (n != p->pconnlist[i]) (void)addstringtoinfstr(",");
			b = (BUCKET *)n->pcomp->id;
			(void)sprintf(line, "%d", b->bucketid);
			(void)addstringtoinfstr(line);
		}
		(void)addstringtoinfstr("]");
	}
	return(returninfstr());
}

/*********************** HELPER ROUTINES ***********************/

/*
 * routine to scan component list "which" on bucket "b".  Any components that
 * do not match the first entry are moved out (either to a new bucket or to
 * an existing one).
 */
INTSML net_scancomplist(BUCKET *b, INTSML which, INTSML checksize, INTSML unifyportnames)
{
	REGISTER BUCKET *ob;
	REGISTER INTSML ret, moved;
	REGISTER PCOMP *ppro, *p, *firstp, *nextp, *np, *nnextp;

	/* get wirelist for this bucket */
	if (b->net1count != 0) ppro = b->net1list; else
		if (b->net2count != 0) ppro = b->net2list; else
			return(0);

	/* get the head of the list to scan */
	if (which == 1) firstp = b->net1list; else firstp = b->net2list;

	/* compare each component in list against this bucket */
	ret = 0;
	for(p = firstp; p != NOPCOMP; p = nextp)
	{
		nextp = p->nextinbucket;

		/* if the new wire list is the same, don't move it */
		if (net_diffwirelist(ppro, p, b, b, unifyportnames) == 0) continue;

		/* see if this component belongs in an existing other bucket */
		ob = net_findsimilarbucket(p, checksize, unifyportnames);
		if (ob != NOBUCKET)
		{
			if ((net_ncc_options&NCCVERBOSE) != 0)
				ttyputmsg(_("moving %s from bucket %d to bucket %d"), describenodeinst(p->actual),
					b->bucketid, ob->bucketid);
			net_movecomp(p, b, ob, which);
		} else
		{
			/* create a new bucket */
			ob = net_allocbucket(b->function, b->wirecount);
			if (ob == NOBUCKET) return(-1);
			ob->bucketid = net_bucketids++;
			ob->nextbucket = net_firstbucket;
			ob->length = b->length;
			ob->width = b->width;
			net_firstbucket = ob;
			if ((net_ncc_options&NCCVERBOSE) != 0)
				ttyputmsg(_("moving %s from bucket %d to NEW bucket %d"),
					describenodeinst(p->actual), b->bucketid, ob->bucketid);
			net_movecomp(p, b, ob, which);

			/*
			 * once the new bucket "ob" is created, MUST repeatedly check
			 * components in all lists against this and move them all at once
			 */
			moved = 1;
			while (moved != 0)
			{
				moved = 0;
				for(np = b->net1list; np != NOPCOMP; np = nnextp)
				{
					nnextp = np->nextinbucket;
					if (net_diffwirelist(p, np, ob, b, unifyportnames) == 0)
					{
						if ((net_ncc_options&NCCVERBOSE) != 0)
							ttyputmsg(_("also moving %s from bucket %d to bucket %d"),
								describenodeinst(np->actual), b->bucketid, ob->bucketid);
						net_movecomp(np, b, ob, 1);
						if (np == nextp) nextp = nnextp;
						moved++;
					}
				}
				for(np = b->net2list; np != NOPCOMP; np = nnextp)
				{
					nnextp = np->nextinbucket;
					if (net_diffwirelist(p, np, ob, b, unifyportnames) == 0)
					{
						if ((net_ncc_options&NCCVERBOSE) != 0)
							ttyputmsg(_("also moving %s from bucket %d to bucket %d"),
								describenodeinst(np->actual), b->bucketid, ob->bucketid);
						net_movecomp(np, b, ob, 2);
						if (np == nextp) nextp = nnextp;
						moved++;
					}
				}
			}
		}

		/* bucket configuration changed: recompute wirelist */
		ret++;
	}
	return(ret);
}

/*
 * routine to find a bucket that has a similar wiring configuration as in
 * pseudocomponent "p".  Returns NOBUCKET if one cannot be found.
 */
BUCKET *net_findsimilarbucket(PCOMP *p, INTSML checksize, INTSML localunifyportnames)
{
	REGISTER BUCKET *b;
	REGISTER PCOMP *ppro;
	REGISTER INTSML result, gotsize;
	INTBIG len, wid;

	/* get transistor size if it exists */
	gotsize = 0;
	if (p->actual != NONODEINST && p->actual->proto->primindex != 0 && isfet(p->actual->geom) != 0)
	{
		gotsize = 1;
		transistorsize(p->actual, &len, &wid);
	}

	/* look at every bucket */
	for(b = net_firstbucket; b != NOBUCKET; b = b->nextbucket)
	{
		/* cannot be the same bucket */
		if (b == (BUCKET *)p->id) continue;

		/* make sure function and wire count are the same */
		if (b->function != p->function || b->wirecount != p->wirecount) continue;

		/* make sure size matches (if requested) */
		if (checksize != 0 && gotsize != 0)
		{
			if (b->length != len) continue;
			if (b->width != wid) continue;
		}

		/* construct a wirelist for this bucket */
		if (b->net1count != 0) ppro = b->net1list; else
			if (b->net2count != 0) ppro = b->net2list; else continue;

		/* compare the wire lists */
		result = net_diffwirelist(p, ppro, b, b, localunifyportnames);

		/* if they are the same, this is the bucket */
		if (result == 0) return(b);
	}

	/* no bucket found */
	return(NOBUCKET);
}

/*
 * routine to move pseudocomponent "p" from bucket "oldbuck" to bucket "newbuck".
 * the component is in list "which"
 */
void net_movecomp(PCOMP *p, BUCKET *oldbuck, BUCKET *newbuck, INTSML which)
{
	REGISTER PCOMP *find, *lastp;

	/* find "p" in the appropriate bucket */
	if (which == 1) find = oldbuck->net1list; else find = oldbuck->net2list;
	lastp = NOPCOMP;
	for( ; find != NOPCOMP; find = find->nextinbucket)
	{
		if (find != p) { lastp = find;   continue; }

		/* remove "p" from the old bucket */
		if (lastp != NOPCOMP) lastp->nextinbucket = p->nextinbucket; else
		{
			if (which == 1) oldbuck->net1list = p->nextinbucket; else
				oldbuck->net2list = p->nextinbucket;
		}
		if (which == 1) oldbuck->net1count--; else oldbuck->net2count--;

		/* insert "p" in the new bucket */
		if (which == 1)
		{
			p->nextinbucket = newbuck->net1list;
			newbuck->net1list = p;
			newbuck->net1count++;
		} else
		{
			p->nextinbucket = newbuck->net2list;
			newbuck->net2list = p;
			newbuck->net2count++;
		}
		p->id = (INTBIG)newbuck;
		break;
	}
}

/*
 * routine to compare pseudocomponent "p1" and "p2", returning nonzero if they
 * are different.  When comparing, consider that bucket "newbuck" should not
 * exist and references to it should actually use "formerbuck".
 */
INTSML net_diffwirelist(PCOMP *p1, PCOMP *p2, BUCKET *newbuck, BUCKET *formerbuck, INTSML unifyportnames)
{
	REGISTER INTSML i, j, firsti;

	/* simple test: number of wire lists must be equal */
	if (p1->wirecount != p2->wirecount) return(1);

	/* if ports must match in sequence, check is simpler */
	if (p1->function == NPTRANPN || p1->function == NPTRAPNP ||
		p1->function == NPDIODE || p1->function == NPDIODEZ ||
		p1->function == NPBUFFER || p1->function == NPFLIPFLOP ||
		unifyportnames != 0)
	{
		for(i=0; i<p1->wirecount; i++)
		{
			if (net_diffwires(p1->pconnlist[i], p1->count[i], p1->state[i], p1->netnumbers[i],
				p2->pconnlist[i], p2->count[i], p2->state[i], p2->netnumbers[i],
					newbuck, formerbuck) != 0) return(1);
		}
		return(0);
	}

	/* make sure there is memory for flags corresponding to list 2 */
	if (p2->wirecount > net_difflistsize)
	{
		if (net_difflistsize != 0) efree((char *)net_difflist);
		net_difflist = (INTSML *)emalloc((SIZEOFINTSML * p2->wirecount), net_aid->cluster);
		net_difflistsize = p2->wirecount;
	}

	/* reset flags in list 2 */
	for(j=0; j<p2->wirecount; j++) net_difflist[j] = 0;

	/* special case for transistors: gates must match without permutation */
	if (p1->function == NPTRANMOS || p1->function == NPTRAPMOS || p1->function == NPTRADMOS)
	{
		if (net_diffwires(p1->pconnlist[0], p1->count[0], p1->state[0], p1->netnumbers[0],
			p2->pconnlist[0], p2->count[0], p2->state[0], p2->netnumbers[0],
				newbuck, formerbuck) != 0) return(1);
		net_difflist[0] = 1;
		firsti = 1;
	} else firsti = 0;

	for(i=firsti; i<p1->wirecount; i++)
	{
		for(j=0; j<p2->wirecount; j++)
		{
			if (net_difflist[j] != 0) continue;
			if (p1->count[i] != p2->count[j]) continue;
			if (net_diffwires(p1->pconnlist[i], p1->count[i], p1->state[i], p1->netnumbers[i],
				p2->pconnlist[j], p2->count[j], p2->state[j], p2->netnumbers[j],
					newbuck, formerbuck) != 0) continue;
			net_difflist[j] = 1;
			break;
		}
		if (j >= p2->wirecount) return(1);
	}
	return(0);
}

/*
 * routine to compare the "c1" wires in "n1" (with factor "f1", nets "net1") and
 * the "c2" wires in "n2" (with factor "f2", nets "net2"), returning nonzero if
 * they are different.
 * When comparing, consider that bucket "newbuck" should not exist and references
 * to it should actually use "formerbuck".
 */
INTSML net_diffwires(PCONN *n1, INTSML c1, INTSML f1, PNET *net1,
	PCONN *n2, INTSML c2, INTSML f2, PNET *net2, BUCKET *newbuck, BUCKET *formerbuck)
{
	REGISTER PCONN *s1, *s2;
	REGISTER INTSML i1, i2;
	REGISTER BUCKET *e1, *e2;
	REGISTER PORTPROTO *pp1, *pp2;

	/* simple test: number of wire lists must be equal */
	if (c1 != c2) return(1);

	/* simple test: negated factor must be equal */
	if ((f1&NEGATEDPORT) != (f2&NEGATEDPORT)) return(1);

	/* simple test: if an export, names must match */
	if ((f1&EXPORTEDPORT) != 0 && (f2&EXPORTEDPORT) != 0)
	{
		pp1 = net1->realport;
		pp2 = net2->realport;
		if (pp1 != NOPORTPROTO && pp2 != NOPORTPROTO)
		{
			if ((net_ncc_options&NCCUSEPORTNAMES) != 0 &&
				namesame(pp1->protoname, pp2->protoname) != 0) return(1);
		}
	}

	/* make sure there is memory for flags */
	if (c1 > net_difflist1size)
	{
		if (net_difflist1size != 0) efree((char *)net_difflist1);
		net_difflist1 = (INTSML *)emalloc((SIZEOFINTSML * c1), net_aid->cluster);
		net_difflist1size = c1;
	}
	if (c2 > net_difflist2size)
	{
		if (net_difflist2size != 0) efree((char *)net_difflist2);
		net_difflist2 = (INTSML *)emalloc((SIZEOFINTSML * c2), net_aid->cluster);
		net_difflist2size = c2;
	}

	/* erase flags */
	for(s1 = n1, i1=0; s1 != NOPCONN; s1 = s1->nextpconn, i1++) net_difflist1[i1] = 0;
	for(s2 = n2, i2=0; s2 != NOPCONN; s2 = s2->nextpconn, i2++) net_difflist2[i2] = 0;

	/* prescan the lists to match as much as possible */
	s1 = n1;   s2 = n2;    i1 = i2 = 0;
	while (s1 != NOPCONN && s2 != NOPCONN)
	{
		e1 = (BUCKET *)s1->pcomp->id;
		if (e1 == newbuck) e1 = formerbuck;
		e2 = (BUCKET *)s2->pcomp->id;
		if (e2 == newbuck) e2 = formerbuck;
		if (e1 == e2)
		{
			net_difflist1[i1] = 1;
			net_difflist2[i2] = 1;
			s1 = s1->nextpconn;   i1++;
			s2 = s2->nextpconn;   i2++;
			continue;
		}
		if (i1 > i2)
		{
			s2 = s2->nextpconn;   i2++;
		} else
		{
			s1 = s1->nextpconn;   i1++;
		}
	}

	/* examine every element in list 1 */
	for(s1 = n1, i1 = 0; s1 != NOPCONN; s1 = s1->nextpconn, i1++)
	{
		if (net_difflist1[i1] != 0) continue;
		e1 = (BUCKET *)s1->pcomp->id;
		if (e1 == newbuck) e1 = formerbuck;

		/* find the equivalent in list 2 */
		for(s2 = n2, i2 = 0; s2 != NOPCONN; s2 = s2->nextpconn, i2++)
		{
			if (net_difflist2[i2] != 0) continue;
			e2 = (BUCKET *)s2->pcomp->id;
			if (e2 == newbuck) e2 = formerbuck;
			if (e2 != e1) continue;
			net_difflist2[i2] = 1;
			break;
		}
		if (s2 == NOPCONN) return(1);
	}
	return(0);
}

/*
 * routine to add pseudocomponent "p" to a bucket with the same function and
 * wirecount.  The pseudocomponent is added to list "which" of the bucket.  If
 * no similar bucket exists, one is created.
 */
INTSML net_addbucket(PCOMP *p, INTSML which, INTSML checksize)
{
	REGISTER BUCKET *b;
	INTBIG len, wid;
	REGISTER INTBIG gotsize;

	/* get transistor size if it exists */
	gotsize = 0;
	if (p->actual != NONODEINST && p->actual->proto->primindex != 0 && isfet(p->actual->geom) != 0)
	{
		gotsize = 1;
		transistorsize(p->actual, &len, &wid);
	}

	/* look for a bucket that abstractly describes this component */
	for(b = net_firstbucket; b != NOBUCKET; b = b->nextbucket)
	{
		if (b->function != p->function) continue;
		if (b->wirecount != p->wirecount) continue;
		if (checksize != 0 && gotsize != 0)
		{
			if (b->length != len) continue;
			if (b->width != wid) continue;
		}
		break;
	}

	/* if no bucket is found, create one */
	if (b == NOBUCKET)
	{
		b = net_allocbucket(p->function, p->wirecount);
		if (b == NOBUCKET) return(1);
		b->bucketid = net_bucketids++;
		b->nextbucket = net_firstbucket;
		if (checksize != 0 && gotsize != 0)
		{
			b->length = len;
			b->width = wid;
		}
		net_firstbucket = b;
	}

	/* store the bucket for this pseudonode on the pseudonode */
	p->id = (INTBIG)b;

	/* add the pseudonode to this bucket */
	if (which == 1)
	{
		p->nextinbucket = b->net1list;
		b->net1list = p;
		b->net1count++;
	} else
	{
		p->nextinbucket = b->net2list;
		b->net2list = p;
		b->net2count++;
	}
	return(0);
}

/*
 * routine to free all allocated bucket and pseudocomponent structures
 */
void net_freeallbuckets(void)
{
	REGISTER BUCKET *b, *nextb;

	for(b = net_firstbucket; b != NOBUCKET; b = nextb)
	{
		nextb = b->nextbucket;
		net_freebucket(b);
	}
}

/*
 * routine to get the two facets to be compared.  These must be the only
 * two windows on the display.  Returns nonzero if facets cannot be found.
 */
INTSML net_getfacets(NODEPROTO **facet1, NODEPROTO **facet2)
{
	REGISTER WINDOWPART *w;

	/* get two windows */
	*facet1 = *facet2 = NONODEPROTO;
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
	{
		if ((w->state&WINDOWTYPE) != DISPWINDOW &&
			(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
		if (*facet1 == NONODEPROTO) *facet1 = w->curnodeproto; else
		{
			if (*facet2 == NONODEPROTO)
			{
				if (w->curnodeproto != *facet1) *facet2 = w->curnodeproto;
			} else
			{
				if (w->curnodeproto != NONODEPROTO) return(1);
			}
		}
	}
	if (*facet2 == NONODEPROTO) return(1);
	return(0);
}
