/*
 * Electric(tm) VLSI Design System
 *
 * File: usrcheck.c
 * User interface aid: database consistency check
 * 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 "database.h"
#include "efunction.h"
#include "tecgen.h"
#include "usr.h"

extern ARCPROTO **gen_upconn;

/* prototypes for local routines */
void us_checklibrary(LIBRARY*, INTSML, INTSML*, INTSML*);
INTSML us_checkvariables(INTSML*, VARIABLE*, LIBRARY*);
INTBIG us_checkrtree(RTNODE*, RTNODE*, NODEPROTO*, INTSML*, INTSML*);

void us_checkdatabase(INTSML verbose)
{
	REGISTER NODEPROTO *np, *pnt, *onp, *lastnp;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER NODEINST *ni;
	REGISTER LIBRARY *lib;
	REGISTER INTBIG co, i, found;
	REGISTER CELL *c, *oc;
	REGISTER TECHNOLOGY *tech;
	REGISTER AIDENTRY *aid;
	REGISTER ARCPROTO *ap;
	REGISTER VIEW *v;
	REGISTER char *ch;
	INTSML warnings, errors;

	errors = warnings = 0;

	if (verbose) ttyputmsg(_("Counting node instances"));

	/* zero the count of each primitive and complex nodeproto */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = 0;
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = 0;
	}

	/* now count the instances in all libraries */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if (ni->proto != NONODEPROTO)
					ni->proto->temp1++;
			}
		}
	}

	if (verbose) ttyputmsg(_("Comparing node instances"));

	/* see if the counts are correct */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		co = 0;  ni = np->firstinst;
		while (ni != NONODEINST) { co++; ni = ni->nextinst; }
		if (co != np->temp1)
		{
			ttyputmsg(_("Technology %s, node %s: says %ld nodes, has %ld"),
				tech->techname, describenodeproto(np), co, np->temp1);
			warnings++;
		}
	}
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		co = 0;  ni = np->firstinst;
		while (ni != NONODEINST) { co++; ni = ni->nextinst; }
		if (co != np->temp1)
		{
			ttyputmsg(_("Facet %s: says %ld nodes, has %ld"), describenodeproto(np), co, np->temp1);
			warnings++;
		}
	}

	/* re-compute list of node instances if it is bad */
	if (warnings != 0)
	{
		ttyputmsg(_("Repairing instance lists"));

		/* first erase lists at each nodeproto */
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
			for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				np->firstinst = NONODEINST;
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				np->firstinst = NONODEINST;

		/* next re-construct lists from every facet in all libraries */
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				pnt = ni->proto;
				if (pnt->firstinst != NONODEINST) pnt->firstinst->lastinst = ni;
				ni->nextinst = pnt->firstinst;
				ni->lastinst = NONODEINST;
				pnt->firstinst = ni;
			}
		}
	}

	/* check variables in the technologies */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		if (us_checkvariables(&tech->numvar, tech->firstvar, NOLIBRARY) != 0)
		{
			warnings++;
			ttyputmsg(_("    Technology %s"), tech->techname);
		}
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			if (us_checkvariables(&ap->numvar, ap->firstvar, NOLIBRARY) != 0)
		{
			warnings++;
			ttyputmsg(_("    Arc %s"), describearcproto(ap));
		}

		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (us_checkvariables(&np->numvar, np->firstvar, NOLIBRARY) != 0)
			{
				warnings++;
				ttyputmsg(_("    Technology %s, node %s"), tech->techname, np->primname);
			}
			if (np->tech != tech)
			{
				ttyputmsg(_("Primitive %s: has wrong technology"), describenodeproto(np));
				np->tech = tech;
				warnings++;
			}
		}
	}

	/* check variables in the aids */
	for(i=0; i<el_maxaid; i++)
	{
		aid = &el_aids[i];
		if (us_checkvariables(&aid->numvar, aid->firstvar, NOLIBRARY) != 0)
		{
			warnings++;
			ttyputmsg(_("    Aid %s"), aid->aidname);
		}
	}

	/* check variables in the views */
	for(v = el_views; v != NOVIEW; v = v->nextview)
	{
		if (us_checkvariables(&v->numvar, v->firstvar, NOLIBRARY) != 0)
		{
			warnings++;
			ttyputmsg(_("    View %s"), v->viewname);
		}
		if ((v->viewstate&MULTIPAGEVIEW) != 0)
		{
			if (namesamen(v->viewname, "schematic-page-", 15) != 0)
			{
				warnings++;
				ttyputmsg(_("    View %s should not be multipage"), v->viewname);
				v->viewstate &= ~MULTIPAGEVIEW;
			}
		} else
		{
			if (namesamen(v->viewname, "schematic-page-", 15) == 0)
			{
				warnings++;
				ttyputmsg(_("    View %s should be multipage"), v->viewname);
				v->viewstate |= MULTIPAGEVIEW;
			}
		}
	}

	/* check every cell in the libraries */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
	{
		if (stopping(STOPREASONCHECK)) return;

		/* check that cell name exists */
		if (*c->cellname == 0)
		{
			ttyputmsg(_("Library %s: null cell name; renamed to 'NULL'"), lib->libname);
			(void)allocstring(&c->cellname, "NULL", c->cluster);
			warnings++;
		}

		/* make sure cell name has no blanks, tabs, colons, etc in it */
		found = 0;
		for(ch = c->cellname; *ch != 0; ch++)
		{
			if (*ch <= ' ' || *ch == ':' || *ch == ';' || *ch == '{' ||
				*ch == '}' || *ch >= 0177)
			{
				if (found == 0)
					ttyputmsg(_("Library %s, cell %s: name cannot contain character '%c' (0%o)"),
						lib->libname, c->cellname, (*ch)&0377, (*ch)&0377);
				found++;
				if (ch == c->cellname) *ch = 'X'; else *ch = '_';
			}
		}
		if (found != 0)
		{
			warnings++;
			ttyputmsg(_("...renamed to '%s'"), c->cellname);
		}

		/* make sure cell name is unique */
		for(;;)
		{
			for(oc = lib->firstcell; oc != NOCELL; oc = oc->nextcell)
				if (oc != c)
			{
				if (namesame(oc->cellname, c->cellname) != 0) continue;
				ttyputmsg(_("Library %s, cell %s: duplicate name"), lib->libname, c->cellname);
				(void)initinfstr();
				(void)addstringtoinfstr(c->cellname);
				(void)addstringtoinfstr(".0");
				(void)reallocstring(&c->cellname, returninfstr(), c->cluster);
				warnings++;
				break;
			}
			if (oc == NOCELL) break;
		}

		/* make sure the cell has facets */
		if (c->firstincell == NONODEPROTO)
		{
			ttyputmsg(_("Library %s, cell %s (E): has no facets"), lib->libname, c->cellname);
			errors++;
		}

		/* make sure chain of facets is sensible */
		for(np = c->firstincell; np != NONODEPROTO; np = np->nextincell)
		{
			lastnp = NONODEPROTO;
			for(onp = np; onp != NONODEPROTO; onp = onp->lastversion)
			{
				if (onp->cell != c)
				{
					ttyputmsg(_("Facet %s: wrong cell pointer"), describenodeproto(np));
					onp->cell = c;
					warnings++;
				}
				if (onp->newestversion != np)
				{
					ttyputmsg(_("Facet %s: wrong newest version"), describenodeproto(onp));
					onp->newestversion = np;
					warnings++;
				}
				if (onp->cellview != np->cellview)
				{
					ttyputmsg(_("Facet %s: wrong view for old version"), describenodeproto(onp));
					onp->cellview = np->cellview;
					warnings++;
				}
				if (lastnp != NONODEPROTO)
				{
					if (onp->version >= lastnp->version)
					{
						ttyputmsg(_("Facet %s: nonascending version order"), describenodeproto(lastnp));
						lastnp->version = onp->version + 1;
						warnings++;
					}
					if (onp->nextincell != NONODEPROTO)
					{
						ttyputmsg(_("Facet %s: old version has bad link"), describenodeproto(onp));
						lastnp->nextincell = NONODEPROTO;
						warnings++;
					}
				}
				lastnp = onp;
			}
		}

		/* make sure the library is correct */
		if (c->lib != lib)
		{
			ttyputmsg(_("Library %s, cell %s: wrong library"), lib->libname, c->cellname);
			c->lib = lib;
			warnings++;
		}

		/* check variables in the cell */
		if (us_checkvariables(&c->numvar, c->firstvar, lib) != 0)
		{
			warnings++;
			ttyputmsg(_("    Library %s, cell %s"), lib->libname, c->cellname);
		}
	}

	/* make sure every facet is both in the library and in a facet */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
			for(np = c->firstincell; np != NONODEPROTO; np = np->nextincell)
				for(onp = np; onp != NONODEPROTO; onp = onp->lastversion)
					onp->temp1 = 0;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = (INTBIG)NOCELL;
	}
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
	{
		for(np = c->firstincell; np != NONODEPROTO; np = np->nextincell)
			for(onp = np; onp != NONODEPROTO; onp = onp->lastversion)
		{
			if (onp->temp1 == 0)
			{
				ttyputmsg(_("Facet %s: in cell list but not library list"), describenodeproto(onp));
				onp->nextnodeproto = lib->firstnodeproto;
				lib->firstnodeproto = onp;
				warnings++;
				continue;
			}
			if (onp->temp1 != (INTBIG)NOCELL)
			{
				ttyputmsg(_("Facet %s (E): in multiple cell lists (%s and %s)"), describenodeproto(onp),
					((CELL *)onp->temp1)->cellname, c->cellname);
				errors++;
				continue;
			}
			onp->temp1 = (INTBIG)c;
		}
	}
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		lastnp = NONODEPROTO;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (np->temp1 == (INTBIG)NOCELL)
			{
				ttyputmsg(_("Library %s, facet %ld: in facet list but not in cell list"), lib->libname, np);
				if (lastnp == NONODEPROTO) lib->firstnodeproto = np->nextnodeproto; else
					lastnp->nextnodeproto = np->nextnodeproto;
				warnings++;
				continue;
			}
			lastnp = np;
		}
	}

	/* check every port in the libraries */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			{
				opp = pp;
				for(;;)
				{
					if (opp->subnodeinst == NONODEINST || opp->subportproto == NOPORTPROTO)
					{
						if (opp == pp)
						{
							ttyputmsg(_("Facet %s, port %s: illegally defined"),
								describenodeproto(np), pp->protoname);
						} else
						{
							ttyputmsg(_("Facet %s, port %s: illegally defined by subport %s"),
								describenodeproto(np), pp->protoname, opp->protoname);
						}
						warnings++;
						break;
					}
					ni = opp->subnodeinst;
					opp = opp->subportproto;
					if (ni->proto->primindex != 0) break;
				}

				if (pp->connects == 0)
				{
					ttyputmsg(_("Facet %s, port %s: no connection list"), describenodeproto(np),
						pp->protoname);
					pp->connects = gen_upconn;
					warnings++;
				}
			}
		}
	}

	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
		us_checklibrary(lib, verbose, &warnings, &errors);
	}

	if (errors == 0 && warnings == 0)
	{
		ttyputmsg(_("All libraries checked: no problems found."));
		return;
	}
	if (errors != 0)
	{
		ttyputmsg(_("DATABASE HAS ERRORS THAT CANNOT BE REPAIRED..."));
		ttyputmsg(_("...Try writing the file as a Readable Dump and reading it back"));
		ttyputmsg(_("...Or try copying error-free facets to another library"));
		return;
	}
	ttyputmsg(_("DATABASE HAD ERRORS THAT WERE REPAIRED..."));
	ttyputmsg(_("...Re-issue the check command to make sure"));
}

void us_checklibrary(LIBRARY *curlib, INTSML verbose, INTSML *warnings, INTSML *errors)
{
	REGISTER NODEPROTO *np, *onp;
	REGISTER PORTPROTO *pp, *opp, *subpp, *lpp;
	REGISTER PORTARCINST *pi, *lpi;
	REGISTER PORTEXPINST *pe, *lpe;
	REGISTER NODEINST *ni, *subni;
	REGISTER ARCINST *ai, *oai, *lastai;
	REGISTER VARIABLE *var;
	REGISTER TECHNOLOGY *tech;
	REGISTER NETWORK *net;
	REGISTER VIEW *v;
	REGISTER INTSML found, foundtot, foundtrue, facetcenters, neterrors, len, accumulatesize;
	REGISTER INTBIG totlx, tothx, totly, tothy, truelx, truehx, truely, truehy, i, j, *arr,
		problems;
	INTBIG lx, hx, ly, hy, position[2];
	REGISTER char *ch;
	static POLYGON *poly = NOPOLYGON;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);

	if (verbose) ttyputmsg(_("***** Checking library %s"), curlib->libname);

	/* check variables in the library */
	if (us_checkvariables(&curlib->numvar, curlib->firstvar, curlib) != 0)
	{
		(*warnings)++;
		ttyputmsg(_("    Library %s"), curlib->libname);
	}

	/* check every facet in the library */
	for(np = curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (stopping(STOPREASONCHECK)) return;

		if (verbose) ttyputmsg(_("Checking facet %s"), describenodeproto(np));

		/* check variables in the facet */
		if (us_checkvariables(&np->numvar, np->firstvar, curlib) != 0)
		{
			(*warnings)++;
			ttyputmsg(_("    Facet %s"), describenodeproto(np));
		}

		/* make sure the "facet" primindex is zero */
		if (np->primindex != 0)
		{
			ttyputmsg(_("Facet %s: flagged as primitive"), describenodeproto(np));
			np->primindex = 0;
			(*warnings)++;
		}

		/* make sure there is no technology */
		tech = whattech(np);
		if (np->tech != tech)
		{
			ttyputmsg(_("Facet %s: has wrong technology"), describenodeproto(np));
			np->tech = tech;
			(*warnings)++;
		}

		/* check the port protos on this facet */
		for(lpp = NOPORTPROTO, pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			/* make sure port name has no blanks, tabs, etc in it */
			found = 0;
			for(ch = pp->protoname; *ch != 0; ch++)
			{
				if (*ch <= ' ' || *ch >= 0177)
				{
					if (found == 0)
					{
						ttyputmsg(_("Facet %s, port %s: bad port name (has character 0%o)"),
							describenodeproto(np), pp->protoname, (*ch)&0377);
						(*warnings)++;
					}
					found++;
					*ch = 'X';
				}
			}
			if (found != 0)
			{
				(*warnings)++;
				ttyputmsg(_("...renamed to '%s'"), pp->protoname);
				break;
			}

			/* make sure port name exists */
			if (*pp->protoname == 0)
			{
				ttyputmsg(_("Facet %s: null port name, renamed to 'NULL'"), describenodeproto(np));
				(void)allocstring(&pp->protoname, "NULL", np->cell->cluster);
				(*warnings)++;
			}

			/* make sure port name is unique */
			for(;;)
			{
				for(opp = np->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
					if (opp != pp)
				{
					if (namesame(pp->protoname, opp->protoname) != 0) continue;
					ttyputmsg(_("Facet %s, port %s: duplicate name"),
						describenodeproto(np), pp->protoname);
					(void)reallocstring(&pp->protoname, us_uniqueportname(pp->protoname, np),
						np->cell->cluster);
					(*warnings)++;
					break;
				}
				if (opp == NOPORTPROTO) break;
			}

			/* make sure this port has same arc connections as sub-port */
			if (pp->connects != pp->subportproto->connects)
			{
				ttyputmsg(_("Facet %s, port %s: connects %o should be %o"), describenodeproto(np),
					pp->protoname, pp->connects, pp->subportproto->connects);
				pp->connects = pp->subportproto->connects;
				(*warnings)++;
			}

			/* make sure parent information in port is right */
			if (pp->parent != np)
			{
				ttyputmsg(_("Facet %s, port %s: bad parent"), describenodeproto(np), pp->protoname);
				pp->parent = np;
				(*warnings)++;
			}

			/* make sure userbits of parent matches that of child */
			i = PORTANGLE | PORTARANGE | PORTISOLATED;
			for(subpp = pp->subportproto, subni = pp->subnodeinst; subni->proto->primindex == 0;
				subni = subpp->subnodeinst, subpp = subpp->subportproto) {}
			if ((subpp->userbits&i) != (pp->userbits&i))
			{
				j = (pp->userbits & ~i) | (subpp->userbits & i);
				ttyputmsg(_("Facet %s, port %s: state bits are 0%o, should be 0%o"),
					describenodeproto(np), pp->protoname, pp->userbits, j);
				pp->userbits = j;
				(*warnings)++;
			}

			/* make sure subnodeinst of this port exists in facet */
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
				if (ni == pp->subnodeinst) break;
			if (ni == NONODEINST)
			{
				ttyputmsg(_("Facet %s, port %s: subnode not there"), describenodeproto(np),
					pp->protoname);
				if (lpp == NOPORTPROTO) np->firstportproto = pp->nextportproto; else
					lpp->nextportproto = pp->nextportproto;
				(*warnings)++;
				continue;
			}

			/* make sure port on subnodeinst is correct */
			for(opp = ni->proto->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
				if (opp == pp->subportproto) break;
			if (opp == NOPORTPROTO)
			{
				ttyputmsg(_("Facet %s, port %s: subportproto not there"), describenodeproto(np),
					pp->protoname);
				(*warnings)++;
				if (ni->proto->firstportproto == NOPORTPROTO)
				{
					/* subnodeinst has no ports: delete this export */
					if (lpp == NOPORTPROTO)
						np->firstportproto = pp->nextportproto; else
							lpp->nextportproto = pp->nextportproto;
					continue;
				} else
				{
					/* switch to first port prototype found */
					pp->subportproto = ni->proto->firstportproto;
				}
			}

			/* check the view */
			for(v = el_views; v != NOVIEW; v = v->nextview)
				if (v == np->cellview) break;
			if (v == NOVIEW)
			{
				ttyputmsg(_("Facet %s: invalid view"), describenodeproto(np));
				np->cellview = el_views;
				(*warnings)++;
			}

			/* check variables in the port */
			if (us_checkvariables(&pp->numvar, pp->firstvar, curlib) != 0)
			{
				ttyputmsg(_("    Facet %s, port %s"), describenodeproto(np), pp->protoname);
				(*warnings)++;
			}
		}

		/* if this is an icon facet, check port equivalences to the layout */
		if (np->cellview == el_iconview)
		{
			onp = contentsview(np);
			if (onp != NONODEPROTO)
			{
				/* mark each port on the contents */
				for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
					opp->temp1 = 0;

				/* look at each port on the icon */
				for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					/* see if there is an equivalent in the contents */
					for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
						if (namesame(opp->protoname, pp->protoname) == 0)
					{
						opp->temp1 = 1;
						break;
					}
					if (opp == NOPORTPROTO)
					{
						ttyputmsg(_("Facet %s, port %s: no equivalent in contents"),
							describenodeproto(np), pp->protoname);
						(*errors)++;
					} else
					{
						/* make sure the characteristics match */
						if ((pp->userbits&STATEBITS) != (opp->userbits&STATEBITS))
						{
							ttyputmsg(_("Facet %s, port %s: characteristics differ from port in facet %s"),
								describenodeproto(np), pp->protoname, describenodeproto(onp));
							pp->userbits = (pp->userbits & ~STATEBITS) | (opp->userbits&STATEBITS);
							(*warnings)++;
						}
					}
				}
				for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
					if (opp->temp1 == 0)
				{
					if ((opp->userbits&BODYONLY) == 0)
					{
						ttyputmsg(_("Facet %s, port %s: no equivalent in icon"),
							describenodeproto(onp), opp->protoname);
						(*errors)++;
					}
				}
			}
		}
	}

	/* check geometry modules, nodes, and arcs for sensibility */
	for(np = curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (stopping(STOPREASONCHECK)) return;

		/* initialize facet bounds */
		foundtot = foundtrue = 0;
		totlx = tothx = totly = tothy = 0;
		truelx = truehx = truely = truehy = 0;

		if (verbose) ttyputmsg(_("Checking facet %s"), describenodeproto(np));

		/* check every nodeinst in the facet */
		facetcenters = 0;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto == 0 || ni->proto == NONODEPROTO)
			{
				ttyputmsg(_("Facet %s: node instance with no prototype"), describenodeproto(np));
				ni->proto = gen_univpinprim;
				(*warnings)++;
			}

			/* see if a "facet-center" primitive is in the facet */
			if (ni->proto == gen_facetcenterprim)
			{
				position[0] = (ni->highx+ni->lowx) / 2;
				position[1] = (ni->highy+ni->lowy) / 2;
				if (facetcenters == 1)
				{
					ttyputmsg(_("Facet %s: multiple facet-center primitives"), describenodeproto(np));
					(*warnings)++;
				}
				facetcenters++;

				/* check the center information on the parent facet */
				var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center);
				if (var == NOVARIABLE)
				{
					ttyputmsg(_("Facet %s: facet-center not recorded"), describenodeproto(np));
					(void)setvalkey((INTBIG)np, VNODEPROTO, el_prototype_center,
						(INTBIG)position, VINTEGER|VISARRAY|(2<<VLENGTHSH));
					(*warnings)++;
				} else if (((INTBIG *)var->addr)[0] != position[0] ||
					((INTBIG *)var->addr)[1] != position[1])
				{
					ttyputmsg(_("Facet %s: facet-center not properly recorded"), describenodeproto(np));
					(void)setvalkey((INTBIG)np, VNODEPROTO, el_prototype_center,
						(INTBIG)position, VINTEGER|VISARRAY|(2<<VLENGTHSH));
					(*warnings)++;
				}
			}

			/* check size of this nodeinst */
			if (ni->proto->primindex == 0)
			{
				/* instances of facets must be the size of the facets */
				if (ni->highx-ni->lowx != ni->proto->highx-ni->proto->lowx ||
					ni->highy-ni->lowy != ni->proto->highy-ni->proto->lowy)
				{
					ttyputmsg(_("Facet %s, node %s: node size was %sx%s, should be %sx%s"),
						describenodeproto(np), describenodeinst(ni), latoa(ni->highx-ni->lowx),
							latoa(ni->highy-ni->lowy), latoa(ni->proto->highx-ni->proto->lowx),
								latoa(ni->proto->highy-ni->proto->lowy));
					ni->lowx += ((ni->highx-ni->lowx) - (ni->proto->highx-ni->proto->lowx))/2;
					ni->highx = ni->lowx + ni->proto->highx-ni->proto->lowx;
					ni->lowy += ((ni->highy-ni->lowy) - (ni->proto->highy-ni->proto->lowy))/2;
					ni->highy = ni->lowy + ni->proto->highy-ni->proto->lowy;
					(*warnings)++;
				}
			} else
			{
				/* primitive nodeinst must have positive size */
				if (ni->lowx > ni->highx || ni->lowy > ni->highy)
				{
					ttyputmsg(_("Facet %s, node %s: strange dimensions"), describenodeproto(np),
						describenodeinst(ni));
					if (ni->lowx > ni->highx)
					{
						i = ni->lowx;   ni->lowx = ni->highx;   ni->highx = i;
					}
					if (ni->lowy > ni->highy)
					{
						i = ni->lowy;   ni->lowy = ni->highy;   ni->highy = i;
					}
					(*warnings)++;
				}

				/* check trace information for validity */
				var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER|VISARRAY, el_trace);
				if (var != NOVARIABLE)
				{
					/* make sure the primitive can hold trace information */
					if ((ni->proto->userbits&HOLDSTRACE) == 0)
					{
						ttyputmsg(_("Facet %s, node %s: cannot hold outline information"),
							describenodeproto(np), describenodeinst(ni));
						(*warnings)++;
						nextvarchangequiet();
						(void)delvalkey((INTBIG)ni, VNODEINST, el_trace);
					} else
					{
						/* ensure points in different locations */
						len = getlength(var) / 2;
						arr = (INTBIG *)var->addr;
						for(i=1; i<len; i++)
							if (arr[0] != arr[i*2] || arr[1] != arr[i*2+1]) break;
						if (i >= len || len < 2)
						{
							ttyputmsg(_("Facet %s, node %s: zero-size outline (has %ld points)"),
								describenodeproto(np), describenodeinst(ni), len);
							(*warnings)++;
							nextvarchangequiet();
							(void)delvalkey((INTBIG)ni, VNODEINST, el_trace);
						}
					}
				}
			}

			/* check orientation of this nodeinst */
			if ((ni->transpose != 0 && ni->transpose != 1) ||
				(ni->rotation < 0 || ni->rotation >= 3600))
			{
				ttyputmsg(_("Facet %s, node %s: strange orientation"), describenodeproto(np),
					describenodeinst(ni));
				(*warnings)++;
				if (ni->transpose != 0) ni->transpose = 1;
				ni->rotation = ni->rotation % 3600;
				if (ni->rotation < 0) ni->rotation += 3600;
			}

			/* check the arc connections on this nodeinst */
			for(lpi = NOPORTARCINST, pi = ni->firstportarcinst; pi != NOPORTARCINST;
				pi = pi->nextportarcinst)
			{
				pp = pi->proto;
				if (pp == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s: no portarc prototype"), describenodeproto(np),
						describenodeinst(ni));
					if (lpi == NOPORTARCINST)
						ni->firstportarcinst = pi->nextportarcinst; else
							lpi->nextportarcinst = pi->nextportarcinst;
					(*warnings)++;
					continue;
				}

				/* make sure the port prototype is correct */
				for(opp = ni->proto->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
					if (opp == pp) break;
				if (opp == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s, port %s: arc proto bad"), describenodeproto(np),
						describenodeinst(ni), pp->protoname);
					if (lpi == NOPORTARCINST)
						ni->firstportarcinst = pi->nextportarcinst; else
							lpi->nextportarcinst = pi->nextportarcinst;
					(*warnings)++;
					continue;
				}

				/* make sure connecting arcinst exists */
				if (pi->conarcinst == NOARCINST)
				{
					ttyputmsg(_("Facet %s, node %s, port %s: no arc"), describenodeproto(np),
						describenodeinst(ni), pp->protoname);
					if (lpi == NOPORTARCINST)
						ni->firstportarcinst = pi->nextportarcinst; else
							lpi->nextportarcinst = pi->nextportarcinst;
					(*warnings)++;
					continue;
				}

				/* make sure the arc is in the facet */
				for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
					if (ai == pi->conarcinst) break;
				if (ai == NOARCINST)
				{
					ttyputmsg(_("Facet %s, node %s, port %s: arc not in facet"), describenodeproto(np),
						describenodeinst(ni), pp->protoname);
					if (lpi == NOPORTARCINST)
						ni->firstportarcinst = pi->nextportarcinst; else
							lpi->nextportarcinst = pi->nextportarcinst;
					(*warnings)++;
					continue;
				}
				lpi = pi;

				/* check variables in the portarcinst */
				if (us_checkvariables(&pi->numvar, pi->firstvar, curlib) != 0)
				{
					(*warnings)++;
					ttyputmsg(_("    Facet %s, node %s, port %s"), describenodeproto(np),
						describenodeinst(ni), pp->protoname);
				}
			}

			/* check that the portarcinsts are in the proper sequence */
			pp = ni->proto->firstportproto;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				while (pp != pi->proto && pp != NOPORTPROTO)
					pp = pp->nextportproto;
				if (pp == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s: portarcinst out of order"),
						describenodeproto(np), describenodeinst(ni));
					pi = ni->firstportarcinst;
					ni->firstportarcinst = NOPORTARCINST;
					while (pi != NOPORTARCINST)
					{
						lpi = pi;
						pi = pi->nextportarcinst;
						db_addportarcinst(ni, lpi);
					}
					(*warnings)++;
					break;
				}
			}

			/* check the export connections on this nodeinst */
			for(lpe = NOPORTEXPINST, pe = ni->firstportexpinst; pe != NOPORTEXPINST;
				pe = pe->nextportexpinst)
			{
				pp = pe->proto;
				if (pp == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s: no portexp prototype"),
						describenodeproto(np), describenodeinst(ni));
					if (lpe == NOPORTEXPINST)
						ni->firstportexpinst = pe->nextportexpinst; else
							lpe->nextportexpinst = pe->nextportexpinst;
					(*warnings)++;
					continue;
				}

				/* make sure the port prototype is correct */
				for(opp = ni->proto->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
					if (opp == pp) break;
				if (opp == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s, port %s: exp proto bad"), describenodeproto(np),
						describenodeinst(ni), pp->protoname);
					if (lpe == NOPORTEXPINST)
						ni->firstportexpinst = pe->nextportexpinst; else
							lpe->nextportexpinst = pe->nextportexpinst;
					(*warnings)++;
					continue;
				}

				/* check validity of port if it is an export */
				if (pe->exportproto == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s, port %s: not an export"), describenodeproto(np),
						describenodeinst(ni), pp->protoname);
					if (lpe == NOPORTEXPINST)
						ni->firstportexpinst = pe->nextportexpinst; else
							lpe->nextportexpinst = pe->nextportexpinst;
					(*warnings)++;
					continue;
				}

				/* make sure exported portinst is in list */
				for(opp = np->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
					if (opp == pe->exportproto) break;
				if (opp == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s: export %s bad"), describenodeproto(np),
						describenodeinst(ni), pe->exportproto->protoname);
					(*warnings)++;
					if (lpe == NOPORTEXPINST)
						ni->firstportexpinst = pe->nextportexpinst; else
							lpe->nextportexpinst = pe->nextportexpinst;
					lpe = pe->nextportexpinst;
					(*warnings)++;
					continue;
				}
				lpe = pe;

				/* check variables in the portexpinst */
				if (us_checkvariables(&pe->numvar, pe->firstvar, curlib) != 0)
				{
					(*warnings)++;
					ttyputmsg(_("    Facet %s, node %s, export %s"), describenodeproto(np),
						describenodeinst(ni), pe->exportproto->protoname);
				}
			}

			/* check that the portexpinsts are in the proper sequence */
			pp = ni->proto->firstportproto;
			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
			{
				while (pp != pe->proto && pp != NOPORTPROTO)
					pp = pp->nextportproto;
				if (pp == NOPORTPROTO)
				{
					ttyputmsg(_("Facet %s, node %s: portexpinst out of order"),
						describenodeproto(np), describenodeinst(ni));
					pe = ni->firstportexpinst;
					ni->firstportexpinst = NOPORTEXPINST;
					while (pe != NOPORTEXPINST)
					{
						lpe = pe;
						pe = pe->nextportexpinst;
						db_addportexpinst(ni, lpe);
					}
					(*warnings)++;
					break;
				}
			}

			/* make sure nodeinst parent is right */
			if (ni->parent != np)
			{
				ttyputmsg(_("Facet %s, node %s: wrong parent"), describenodeproto(np),
					describenodeinst(ni));
				ni->parent = np;
				(*warnings)++;
			}

			/* make sure geometry module pointer is right */
			ni->temp1 = 0;
			if (ni->geom->entryaddr.ni != ni)
			{
				ttyputmsg(_("Facet %s, node %s: bad geometry module"), describenodeproto(np),
					describenodeinst(ni));
				ni->geom->entryaddr.ni = ni;
				(*warnings)++;
			}

			/* make sure the geometry module is the right size */
			boundobj(ni->geom, &lx, &hx, &ly, &hy);
			if (lx != ni->geom->lowx || hx != ni->geom->highx ||
				ly != ni->geom->lowy || hy != ni->geom->highy)
			{
				ttyputmsg(_("Facet %s, node %s: geometry size bad (was %s<=X<=%s, %s<=Y<=%s, is %s<=X<=%s, %s<=Y<=%s)"),
					describenodeproto(np), describenodeinst(ni), latoa(ni->geom->lowx),
						latoa(ni->geom->highx), latoa(ni->geom->lowy), latoa(ni->geom->highy),
							latoa(lx), latoa(hx), latoa(ly), latoa(hy));
				(*warnings)++;
				ni->geom->lowx = lx;   ni->geom->highx = hx;
				ni->geom->lowy = ly;   ni->geom->highy = hy;
			}

			/* accumulate facet size information if not a "facet-center" primitive */
			accumulatesize = 1;
			if (ni->proto == gen_facetcenterprim) accumulatesize = 0;
			if (ni->proto == gen_invispinprim)
			{
				for(i=0; i<ni->numvar; i++)
				{
					var = &ni->firstvar[i];
					if ((var->type&VDISPLAY) != 0 &&
						(var->textdescript&VTINTERIOR) != 0)
					{
						accumulatesize = 0;
						break;
					}
				}
			}
			if (accumulatesize != 0)
			{
				if (foundtot == 0)
				{
					totlx = lx;   tothx = hx;   totly = ly;   tothy = hy;
					foundtot = 1;
				} else
				{
					if (lx < totlx) totlx = lx;
					if (hx > tothx) tothx = hx;
					if (ly < totly) totly = ly;
					if (hy > tothy) tothy = hy;
				}
			}
			if (foundtrue == 0)
			{
				truelx = lx;   truehx = hx;   truely = ly;   truehy = hy;
			} else
			{
				if (lx < truelx) truelx = lx;
				if (hx > truehx) truehx = hx;
				if (ly < truely) truely = ly;
				if (hy > truehy) truehy = hy;
			}
			foundtrue = 1;

			/* make sure nodeinst is not marked dead */
			if (ni->userbits&DEADN)
			{
				ttyputmsg(_("Facet %s, node %s: dead node"), describenodeproto(np),
					describenodeinst(ni));
				ni->userbits &= ~DEADN;
				(*warnings)++;
			}

			/* check variables in the nodeinst */
			if (us_checkvariables(&ni->numvar, ni->firstvar, curlib) != 0)
			{
				(*warnings)++;
				ttyputmsg(_("    Facet %s, node %s"), describenodeproto(np), describenodeinst(ni));
			}
			if (us_checkvariables(&ni->geom->numvar, ni->geom->firstvar, curlib) != 0)
			{
				(*warnings)++;
				ttyputmsg(_("    Facet %s, node geom %s"), describenodeproto(np), describenodeinst(ni));
			}
		}

		if (verbose) ttyputmsg(_("   checking arcs"));

		/* check every arcinst in the facet */
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			/* check both arcinst ends */
			for(i=0; i<2; i++)
			{
				/* make sure it has a nodeinst and portarcinst */
				ni = ai->end[i].nodeinst;
				if (ai->end[i].portarcinst->proto->connects == 0) ni = NONODEINST;
				if (ni == NONODEINST)
				{
					/* try to repair */
					for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
					{
						if (ai->end[i].portarcinst != NOPORTARCINST)
						{
							for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
							{
								if (pi == ai->end[i].portarcinst) break;
							}
							if (pi != NOPORTARCINST) break;
						} else
						{
							for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
							{
								for(j=0; pp->connects[j] != NOARCPROTO; j++)
									if (pp->connects[j] == ai->proto) break;
								if (pp->connects[j] == NOARCPROTO) continue;
								shapeportpoly(ni, pp, poly, 0);
								if (isinside(ai->end[i].xpos, ai->end[i].ypos, poly) != 0) break;
							}
							if (pp != NOPORTPROTO) break;
						}
					}
					if (ni != NONODEINST)
					{
						ai->end[i].nodeinst = ni;
						ttyputmsg(_("Facet %s, arc %s: bad node end"), describenodeproto(np),
							describearcinst(ai));
						(*warnings)++;
					} else
					{
						ttyputmsg(_("Facet %s, arc %s: bad node end, deleting arc"), describenodeproto(np),
							describearcinst(ai));
						(*warnings)++;
						lastai = NOARCINST;
						for(oai = np->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
						{
							if (oai == ai) break;
							lastai = oai;
						}
						if (lastai == NOARCINST) np->firstarcinst = ai->nextarcinst; else
							lastai->nextarcinst = ai->nextarcinst;
						break;
					}
					continue;
				}
				if (ai->end[i].portarcinst == NOPORTARCINST)
				{
					ttyputmsg(_("Facet %s, arc %s (E): bad port end"), describenodeproto(np),
						describearcinst(ai));
					(*errors)++;
					continue;
				}

				/* make sure portarcinst is on nodeinst */
				for(pi = ai->end[i].nodeinst->firstportarcinst; pi != NOPORTARCINST;
					pi = pi->nextportarcinst)
						if (ai->end[i].portarcinst == pi) break;
				if (pi == NOPORTARCINST)
				{
					ai->end[i].portarcinst = allocportarcinst(np->cell->cluster);
					pi = ai->end[i].portarcinst;
					pi->proto = NOPORTPROTO;
					for(pp = ai->end[i].nodeinst->proto->firstportproto; pp != NOPORTPROTO;
						pp = pp->nextportproto)
					{
						for(j=0; pp->connects[j] != NOARCPROTO; j++)
							if (pp->connects[j] == ai->proto) break;
						if (pp->connects[j] == NOARCPROTO) continue;
						pi->proto = pp;
						shapeportpoly(ai->end[i].nodeinst, pp, poly, 0);
						if (isinside(ai->end[i].xpos, ai->end[i].ypos, poly) != 0) break;
					}
					if (pi->proto == NOPORTPROTO)
					{
						ttyputmsg(_("Facet %s, arc %s, end %ld (E): cannot connect"),
							describenodeproto(np), describearcinst(ai), i);
						(*errors)++;
						break;
					}
					pi->conarcinst = ai;
					db_addportarcinst(ai->end[i].nodeinst, pi);

					ttyputmsg(_("Facet %s, arc %s, end %ld: missing portarcinst"),
						describenodeproto(np), describearcinst(ai), i);
					(*warnings)++;
				}

				/* make sure nodeinst is not dead */
				if (ai->end[i].nodeinst->userbits&DEADN)
				{
					ttyputmsg(_("Facet %s, arc %s: dead end"), describenodeproto(np),
						describearcinst(ai));
					ai->end[i].nodeinst->userbits &= ~DEADN;
					(*warnings)++;
				}

				/* make sure the nodeinst resides in this facet */
				for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
					if (ni == ai->end[i].nodeinst) break;
				if (ni == NONODEINST)
				{
					ttyputmsg(_("Facet %s, arc %s, end %ld (E): no node"), describenodeproto(np),
						describearcinst(ai), i);
					(*errors)++;
				}

				/* make sure proto of portinst agrees with this arcinst */
				pp = ai->end[i].portarcinst->proto;
				for(j=0; pp->connects[j] != NOARCPROTO; j++)
					if (pp->connects[j] == ai->proto) break;
				if (pp->connects[j] == NOARCPROTO)
				{
					ttyputmsg(_("Facet %s, arc %s: can't connect to port %s"), describenodeproto(np),
						describearcinst(ai), ai->end[i].portarcinst->proto->protoname);

					/* see if it is possible to connect this arc to a different port */
					ni = ai->end[i].nodeinst;
					for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					{
						for(j=0; pp->connects[j] != NOARCPROTO; j++)
							if (pp->connects[j] == ai->proto) break;
						if (pp->connects[j] == NOARCPROTO) continue;
						shapeportpoly(ni, pp, poly, 0);
						if (isinside(ai->end[i].xpos, ai->end[i].ypos, poly) == 0) continue;
						ai->end[i].portarcinst->proto = pp;
						break;
					}
					if (pp == NOPORTPROTO)
					{
						/* convert the arc to "universal" so that it can connect */
						ai->proto = el_technologies->firstarcproto;
					}
					(*warnings)++;
				}

				shapeportpoly(ai->end[i].nodeinst, ai->end[i].portarcinst->proto, poly, 0);
				if (isinside(ai->end[i].xpos, ai->end[i].ypos, poly) == 0)
				{
					portposition(ai->end[i].nodeinst, ai->end[i].portarcinst->proto, &lx, &ly);
					if (lx != ai->end[i].xpos || ly != ai->end[i].ypos)
					{
						/* try to extend vertically */
						if ((ai->end[0].xpos == ai->end[1].xpos) &&
							isinside(ai->end[i].xpos, ly, poly) != 0) ai->end[i].ypos = ly; else
								if ((ai->end[0].ypos == ai->end[1].ypos) &&
									isinside(lx, ai->end[i].ypos, poly) != 0)
										ai->end[i].xpos = lx; else
						{
							ai->end[i].xpos = lx;   ai->end[i].ypos = ly;
						}
						ttyputmsg(_("Facet %s, arc %s: end %ld not in port"), describenodeproto(np),
							describearcinst(ai), i);
						(*warnings)++;
					}
				}
			}

			/* make sure geometry modeule pointer is right */
			ai->temp1 = 0;
			if (ai->geom->entryaddr.ai != ai)
			{
				ttyputmsg(_("Facet %s, arc %s: bad geometry module"), describenodeproto(np),
					describearcinst(ai));
				ai->geom->entryaddr.ai = ai;
				(*warnings)++;
			}

			/* check arcinst length */
			i = computedistance(ai->end[0].xpos, ai->end[0].ypos, ai->end[1].xpos, ai->end[1].ypos);
			if (i != ai->length)
			{
				ttyputmsg(_("Facet %s, arc %s: bad length (was %s)"), describenodeproto(np),
					describearcinst(ai), latoa(ai->length));
				ai->length = i;
				(*warnings)++;
			}

			/* check arcinst width */
			if (ai->width < 0)
			{
				ttyputmsg(_("Facet %s, arc %s: negative width"), describenodeproto(np),
					describearcinst(ai));
				ai->width = -ai->width;
				(*warnings)++;
			}

			/* check arcinst end shrinkage value */
			if (setshrinkvalue(ai, 0) != 0)
			{
				ttyputmsg(_("Facet %s, arc %s: bad endshrink value"), describenodeproto(np),
					describearcinst(ai));
				(*warnings)++;
			}

			/* make sure the geometry module is the right size */
			boundobj(ai->geom, &lx, &hx, &ly, &hy);
			if (lx != ai->geom->lowx || hx != ai->geom->highx ||
				ly != ai->geom->lowy || hy != ai->geom->highy)
			{
				ttyputmsg(_("Facet %s, arc %s: geometry size bad"), describenodeproto(np),
					describearcinst(ai));
				(*warnings)++;
				ai->geom->lowx = lx;   ai->geom->highx = hx;
				ai->geom->lowy = ly;   ai->geom->highy = hy;
			}

			/* accumulate facet size information */
			if (foundtrue == 0)
			{
				foundtrue = 1;
				truelx = lx;   truehx = hx;   truely = ly;   truehy = hy;
				truelx = lx;   truehx = hx;   truely = ly;   truehy = hy;
			} else
			{
				if (lx < totlx) totlx = lx;
				if (hx > tothx) tothx = hx;
				if (ly < totly) totly = ly;
				if (hy > tothy) tothy = hy;

				if (lx < truelx) truelx = lx;
				if (hx > truehx) truehx = hx;
				if (ly < truely) truely = ly;
				if (hy > truehy) truehy = hy;
			}

			/* make sure parent pointer is right */
			if (ai->parent != np)
			{
				ttyputmsg(_("Facet %s, arc %s: bad parent pointer"), describenodeproto(np),
					describearcinst(ai));
				ai->parent = np;
				(*warnings)++;
			}

			/* make sure the arcinst isn't dead */
			if ((ai->userbits&DEADA) != 0)
			{
				ttyputmsg(_("Facet %s, arc %s: dead arc"), describenodeproto(np), describearcinst(ai));
				ai->userbits &= ~DEADA;
				(*warnings)++;
			}

			/* check variables in the arcinst */
			if (us_checkvariables(&ai->numvar, ai->firstvar, curlib) != 0)
			{
				(*warnings)++;
				ttyputmsg(_("    Facet %s, arc %s"), describenodeproto(np), describearcinst(ai));
			}
			if (us_checkvariables(&ai->geom->numvar, ai->geom->firstvar, curlib) != 0)
			{
				(*warnings)++;
				ttyputmsg(_("    Facet %s, arc geom %s"), describenodeproto(np), describearcinst(ai));
			}
		}

		/* now make sure facet size is correct */
		if (np->lowx != totlx || np->highx != tothx || np->lowy != totly || np->highy != tothy)
		{
			ttyputmsg(_("Facet %s: bounds are %s<=X<=%s %s<=Y<=%s, should be %s<=X<=%s %s<=Y<=%s"),
				describenodeproto(np), latoa(np->lowx), latoa(np->highx), latoa(np->lowy),
					latoa(np->highy), latoa(totlx), latoa(tothx), latoa(totly), latoa(tothy));
			np->lowx = totlx;   np->highx = tothx;
			np->lowy = totly;   np->highy = tothy;
			(*warnings)++;
		}

		/* check the R-tree in this facet */
		problems = us_checkrtree(NORTNODE, np->rtree, np, warnings, errors);

		/* complete R-tree check by ensuring that all "temp1" were set */
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			if (ai->temp1 != 1)
		{
			ttyputmsg(_("Facet %s, arc %s: no R-tree object"), describenodeproto(np),
				describearcinst(ai));
			(*errors)++;
			problems++;
		}
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			if (ni->temp1 != 1)
		{
			ttyputmsg(_("Facet %s, node %s: no R-tree object"), describenodeproto(np),
				describenodeinst(ni));
			(*errors)++;
			problems++;
		}

		/* check overall R-tree size */
		if (np->rtree->lowx != truelx || np->rtree->highx != truehx ||
			np->rtree->lowy != truely || np->rtree->highy != truehy)
		{
			ttyputmsg(_("Facet %s: Incorrect R-tree size (was %s<=X<=%s, %s<=Y<=%s, is %s<=X<=%s, %s<=Y<=%s)"),
				describenodeproto(np), latoa(np->rtree->lowx), latoa(np->rtree->highx),
					latoa(np->rtree->lowy), latoa(np->rtree->highy), latoa(truelx), latoa(truehx),
						latoa(truely), latoa(truehy));
			np->rtree->lowx = truelx;
			np->rtree->highx = truehx;
			np->rtree->lowy = truely;
			np->rtree->highy = truehy;
			(*warnings)++;
			problems++;
		}

		/* if there were R-Tree problems, rebuild it */
		if (problems != 0)
		{
			if (geomstructure(np) != 0)
			{
				ttyputmsg(_("Facet %s (E): cannot rebuild R-tree"), describenodeproto(np));
				(*errors)++;
			}
			ttyputmsg(_("Facet %s: rebuilding R-tree"), describenodeproto(np));
			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
				linkgeom(ai->geom, np);
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
				linkgeom(ni->geom, np);
		}
	}

	/* check network information */
	if ((net_aid->aidstate&AIDON) == 0) return;
	neterrors = 0;
	for(np = curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (stopping(STOPREASONCHECK)) return;
		if (verbose) ttyputmsg(_("Checking networks in %s"), describenodeproto(np));

		/* initialize for port count and arc reference count */
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
			net->temp1 = net->temp2 = 0;

		/* check network information on ports */
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			if (pp->network == NONETWORK)
			{
				ttyputmsg(_("Facet %s, port %s: no network"), describenodeproto(np), pp->protoname);
				(*warnings)++;
				neterrors++;
			} else pp->network->temp2++;
		}

		/* check network information on arcs */
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			if (ai->network == NONETWORK)
			{
				if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) != APNONELEC)
				{
					ttyputmsg(_("Facet %s, arc %s: no network"), describenodeproto(np),
						describearcinst(ai));
					(*warnings)++;
					neterrors++;
				}
			} else
			{
				if (ai->network->signals > 1 &&
					((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) != APBUS)
				{
					ttyputmsg(_("Facet %s, arc %s: not a bus but network %s is"), describenodeproto(np),
						describearcinst(ai), describenetwork(ai->network));
					(*warnings)++;
					neterrors++;
				}
				ai->network->temp1++;
			}
			ai->temp1 = 0;
		}

		/* check port and arc reference counts, prepare for arc counts */
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		{
			if (net->arccount == 1) ((ARCINST *)net->arcaddr)->temp1++; else
				for(i=0; i<net->arccount; i++)
					((ARCINST **)net->arcaddr)[i]->temp1++;
			if (net->refcount != net->temp1)
			{
				ttyputmsg(_("Facet %s, network %s: reference count wrong"), describenodeproto(np),
					describenetwork(net));
				(*warnings)++;
				neterrors++;
			}
			if (net->portcount != net->temp2)
			{
				ttyputmsg(_("Facet %s, network %s: port count wrong"), describenodeproto(np),
					describenetwork(net));
				(*warnings)++;
				neterrors++;
			}
		}

		/* check arc names */
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			if (ai->temp1 == 0) continue;
			if (ai->temp1 != 1)
			{
				ttyputmsg(_("Facet %s, arc %s: described by %ld networks"), describenodeproto(np),
					describearcinst(ai), ai->temp1);
				(*warnings)++;
				neterrors++;
			}
			if (ai->network->namecount > 0)
			{
				if (getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name) == NOVARIABLE)
				{
					ttyputmsg(_("Facet %s, arc %s: on network but has no name"), describenodeproto(np),
						describearcinst(ai));
					(*warnings)++;
					neterrors++;
				}
			}
		}

		/* check bus link counts */
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
			net->temp1 = 0;
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		{
			if (net->signals > 1) for(i=0; i<net->signals; i++)
				net->networklist[i]->temp1++;
		}
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
			if (net->buslinkcount != net->temp1)
		{
			ttyputmsg(_("Facet %s, network %s: bus link count wrong"), describenodeproto(np),
				describenetwork(net));
			(*warnings)++;
			neterrors++;
		}

		/* miscellaneous checks */
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		{
			if (net->parent != np)
			{
				ttyputmsg(_("Facet %s, network %s: wrong parent"), describenodeproto(np),
					describenetwork(net));
				neterrors++;
				(*warnings)++;
			}
			if (us_checkvariables(&net->numvar, net->firstvar, curlib) != 0)
			{
				ttyputmsg(_("    Facet %s, network %s"), describenodeproto(np), describenetwork(net));
				(*warnings)++;
			}
		}
	}

	if (neterrors != 0) (void)askaid(net_aid, "total-re-number", (INTBIG)curlib);
}

INTSML us_checkvariables(INTSML *numvar, VARIABLE *firstvar, LIBRARY *olib)
{
	REGISTER INTSML i, j, errors;
	REGISTER INTBIG ty;
	REGISTER LIBRARY *lib;
	REGISTER NODEPROTO *np, *vnp;
	REGISTER TECHNOLOGY *tech;

	if (*numvar < 0)
	{
		ttyputmsg(_("Variable count is negative on:"));
		*numvar = 0;
		return(1);
	}
	errors = 0;
	for(i = 0; i < *numvar; i++)
	{
		for(j=0; j<el_numnames; j++)
			if ((char *)firstvar[i].key == el_namespace[j]) break;
		if (j >= el_numnames)
		{
			ttyputmsg(_("Variable key %ld is invalid on:"), firstvar[i].key);
			(*numvar)--;
			for(j = i; j < *numvar; j++)
			{
				firstvar[j].key  = firstvar[j+1].key;
				firstvar[j].type = firstvar[j+1].type;
				firstvar[j].addr = firstvar[j+1].addr;
			}
			errors++;
		}

		/* check validity of certain pointer variables */
		ty = firstvar[i].type;
		if ((ty&(VTYPE|VISARRAY)) == VNODEPROTO)
		{
			/* check validity of nonarray NODEPROTO pointers */
			vnp = (NODEPROTO *)firstvar[i].addr;
			if (vnp != NONODEPROTO)
			{
				if (olib == NOLIBRARY)
				{
					for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
					{
						for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
							if (np == vnp) break;
						if (np != NONODEPROTO) break;
					}
				} else
				{
					for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
						if (np == vnp) break;
				}
				if (np == NONODEPROTO)
				{
					for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
					{
						for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
							if (np == vnp) break;
						if (np != NONODEPROTO) break;
					}
				}
				if (np == NONODEPROTO)
				{
					ttyputmsg(_("Bad facet variable '%s':"), firstvar[i].key);
					(*numvar)--;
					for(j = i; j < *numvar; j++)
					{
						firstvar[j].key  = firstvar[j+1].key;
						firstvar[j].type = firstvar[j+1].type;
						firstvar[j].addr = firstvar[j+1].addr;
					}
					errors++;
				}
			}
		}
	}
	if (errors != 0) return(1);
	return(0);
}

/*
 * routine to recursively check the R-tree structure at "rtn".  The expected
 * R-tree parent is "expectedparent" and the facet containing this structure
 * is "facet"
 */
INTBIG us_checkrtree(RTNODE *expectedparent, RTNODE *rtn, NODEPROTO *facet,
	INTSML *warnings, INTSML *errors)
{
	REGISTER INTSML j;
	INTBIG lx, hx, ly, hy, lowestxv, highestxv, lowestyv, highestyv, problems;
	extern INTSML db_maxrtnodesize, db_minrtnodesize;
	REGISTER GEOM *ge;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;

	problems = 0;

	/* sensibility check of this R-tree node */
	if (rtn->parent != expectedparent)
	{
		ttyputmsg(_("Facet %s: R-tree node has wrong parent"), describenodeproto(facet));
		rtn->parent = expectedparent;
		(*warnings)++;
		problems++;
	}
	if (rtn->total > db_maxrtnodesize)
	{
		ttyputmsg(_("Facet %s: R-tree node has too many entries (%d)"), describenodeproto(facet),
			rtn->total);
		rtn->total = db_maxrtnodesize;
		(*warnings)++;
		problems++;
	}
	if (rtn->total < db_minrtnodesize && rtn->parent != NORTNODE)
	{
		ttyputmsg(_("Facet %s: R-tree node has too few entries (%d)"), describenodeproto(facet),
			rtn->total);
		(*warnings)++;
		problems++;
	}

	/* check children */
	for(j=0; j<rtn->total; j++)
	{
		if (rtn->flag == 0)
			problems += us_checkrtree(rtn, (RTNODE *)rtn->pointers[j], facet, warnings, errors); else
		{
			ge = (GEOM *)rtn->pointers[j];
			if (ge->entrytype == OBJNODEINST)
			{
				ni = ge->entryaddr.ni;
				if (ni->temp1 != 0)
				{
					ttyputmsg(_("Facet %s, node %s: has duplicate R-tree pointer"),
						describenodeproto(facet), describenodeinst(ni));
					(*warnings)++;
					problems++;
				}
				ni->temp1 = 1;
			} else
			{
				ai = ge->entryaddr.ai;
				if (ai->temp1 != 0)
				{
					ttyputmsg(_("Facet %s, arc %s: has duplicate R-tree pointer"),
						describenodeproto(facet), describearcinst(ai));
					(*warnings)++;
					problems++;
				}
				ai->temp1 = 1;
			}
		}
	}

	if (rtn->total != 0)
	{
		db_rtnbbox(rtn, 0, &lowestxv, &highestxv, &lowestyv, &highestyv);
		for(j=1; j<rtn->total; j++)
		{
			db_rtnbbox(rtn, j, &lx, &hx, &ly, &hy);
			if (lx < lowestxv) lowestxv = lx;
			if (hx > highestxv) highestxv = hx;
			if (ly < lowestyv) lowestyv = ly;
			if (hy > highestyv) highestyv = hy;
		}
		if (rtn->lowx != lowestxv || rtn->highx != highestxv ||
			rtn->lowy != lowestyv || rtn->highy != highestyv)
		{
			ttyputmsg(_("Facet %s: R-tree node has wrong bounds"), describenodeproto(facet));
			(*warnings)++;
			problems++;
			rtn->lowx = lowestxv;
			rtn->highx = highestxv;
			rtn->lowy = lowestyv;
			rtn->highy = highestyv;
		}
	}
	return(problems);
}
