/*
 * Electric(tm) VLSI Design System
 *
 * File: erc.c
 * Electrical Rules Checking tool
 * Written by: Steven M. Rubin
 *
 * 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 "config.h"
#if ERCAID

#include "global.h"
#include "efunction.h"
#include "egraphics.h"
#include "erc.h"
#include "edialogs.h"

/* the ERC aid table */
static KEYWORD ercopt[] =
{
	{"analyze",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"current-error",  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"next-error",     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"previous-error", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP erc_tablep = {ercopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", "Electrical Rules action", "show defaults"};

/* ERC: Options Dialog */
DIALOGITEM erc_optionsdialogitems[] =
{
 /*  1 */ {0, {108,396,132,460}, BUTTON, "OK"},
 /*  2 */ {0, {108,296,132,360}, BUTTON, "Cancel"},
 /*  3 */ {0, {32,8,48,228}, RADIO, "Must have contact in every area"},
 /*  4 */ {0, {56,8,72,228}, RADIO, "Must have at least 1 contact"},
 /*  5 */ {0, {80,8,96,228}, RADIO, "Do not check for contacts"},
 /*  6 */ {0, {8,44,24,144}, MESSAGE, "For well:"},
 /*  7 */ {0, {32,248,48,480}, RADIO, "Must have contact in every area"},
 /*  8 */ {0, {56,248,72,480}, RADIO, "Must have at least 1 contact"},
 /*  9 */ {0, {80,248,96,480}, RADIO, "Do not check for contacts"},
 /* 10 */ {0, {8,284,24,384}, MESSAGE, "For substrate:"}
};
DIALOG erc_optionsdialog = {{50,75,191,564}, "ERC Options", 10, erc_optionsdialogitems};

/* substrate/well contacts */
#define NOSUBWELLCON ((SUBWELLCON *)-1)

typedef struct Isubwellcon
{
	INTBIG              x, y;
	INTBIG              netnum;
	INTSML              fun;
	NODEPROTO          *np;
	struct Isubwellcon *nextsubwellcon;
} SUBWELLCON;

SUBWELLCON *erc_subwellconfree = NOSUBWELLCON;
SUBWELLCON *erc_firstsubwellcon = NOSUBWELLCON;
INTBIG      erc_worstwelldist;
INTBIG      erc_worstwellconx, erc_worstwellcony;
INTBIG      erc_worstwelledgex, erc_worstwelledgey;
INTBIG      erc_worstsubdist;
INTBIG      erc_worstsubconx, erc_worstsubcony;
INTBIG      erc_worstsubedgex, erc_worstsubedgey;

/* substrate/well areas */
#define NOSUBWELLAREA ((SUBWELLAREA *)-1)

typedef struct Isubwellarea
{
	INTBIG            lx, hx, ly, hy;
	INTBIG            netnum;
	POLYGON           *poly;
	INTSML            layer;
	TECHNOLOGY        *tech;
	struct Isubwellarea *nextsubwellarea;
} SUBWELLAREA;

SUBWELLAREA *erc_subwellareafree = NOSUBWELLAREA;
SUBWELLAREA *erc_firstsubwellarea = NOSUBWELLAREA;

/* ERC errors */
#define NOERCERROR ((ERCERROR *)-1)

#define NOSUB         1			/* no substrate contact found in facet */
#define NOSUBINAREA   2			/* no well contact found in this area */
#define SUBPROX       3			/* substrate areas too close to each other */
#define SUBFLOATS     4			/* substrate contact is floating */
#define SUBDISCON     5			/* substrate contacts disconnected from each other */
#define NOWELL        6			/* no well contact found in facet */
#define NOWELLINAREA  7			/* no substrate contact found in this area */
#define WELLPROX      8			/* well areas too close to each other */
#define WELLFLOATS    9			/* well contact is floating */
#define WELLDISCON   10			/* well contacts disconnected from each other */
#define FLOATINGARC  11			/* arc not globally connected */

typedef struct Iercerror
{
	INTBIG            error;
	INTBIG            x1, y1;
	INTBIG            x2, y2;
	POLYGON          *poly1;
	POLYGON          *poly2;
	struct Iercerror *prevercerror;
	struct Iercerror *nextercerror;
} ERCERROR;

ERCERROR   *erc_ercerrorfree = NOERCERROR;
ERCERROR   *erc_firstercerror = NOERCERROR;
ERCERROR   *erc_curercerror = NOERCERROR;
ERCERROR   *erc_nextercerror = NOERCERROR;
ERCERROR   *erc_prevercerror = NOERCERROR;

/* miscellaneous */

/* the meaning of "ERC_options" */
#define WELLCONOPTIONS  03
#define WELLCONPERAREA   0
#define WELLCONONCE      1
#define WELLCONIGNORE    2
#define SUBCONOPTIONS  014
#define SUBCONPERAREA    0
#define SUBCONONCE      04
#define SUBCONIGNORE   010

AIDENTRY   *erc_aid;						/* this tool */
NODEPROTO  *erc_facet;
INTBIG      erc_globalnetnumber;			/* global network number */
INTBIG      erc_attachednetnumber;			/* attached network number (above this floats) */
INTBIG      erc_optionskey;					/* key for "ERC_options" */
INTBIG      erc_options;					/* value of "ERC_options" */
INTBIG      erc_jobsize;					/* number of polygons to analyze */
INTBIG      erc_polyscrunched;				/* number of polygons analyzed */
INTBIG      erc_realrun;					/* nonzero if this is the real analysis */

extern AIDENTRY *us_aid;

/* prototypes for local routines */
ERCERROR    *erc_allocercerror(INTBIG errtype);
SUBWELLAREA *erc_allocsubwellarea(void);
SUBWELLCON  *erc_allocsubwellcon(void);
void         erc_analyzefacet(NODEPROTO *facet);
void         erc_freeallerrors(void);
void         erc_freeallsubwellareas(void);
void         erc_freeallsubwellcons(void);
void         erc_freeercerror(ERCERROR *ee);
void         erc_freesubwellarea(SUBWELLAREA *swa);
void         erc_freesubwellcon(SUBWELLCON *swc);
void         erc_gathersubandwell(NODEPROTO *np, XARRAY trans);
void         erc_getpolygon(INTSML layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTSML count);
void         erc_identifypoint(INTBIG x, INTBIG y, NODEPROTO *facet);
void         erc_optionsdlog(void);
void         erc_reporterror(void);
void         erc_showpoly(POLYGON *poly);
INTSML       erc_substratelayer(TECHNOLOGY *tech, INTSML layer);
INTSML       erc_truesubstratelayer(TECHNOLOGY *tech, INTSML layer);
INTSML       erc_truewelllayer(TECHNOLOGY *tech, INTSML layer);
INTSML       erc_welllayer(TECHNOLOGY *tech, INTSML layer);

/************************ CONTROL ***********************/

/*
 * tool initialization
 */
void erc_init(INTBIG *argc, char *argv[], AIDENTRY *thisaid)
{
	/* only initialize during pass 1 */
	if (thisaid == NOAID || thisaid == 0) return;

	/* copy aid pointer during pass 1 */
	erc_aid = thisaid;
	erc_optionskey = makekey("ERC_options");
	DiaDeclareHook("ercopt", &erc_tablep, erc_optionsdlog);
}

void erc_done(void)
{
	erc_freeallerrors();
	erc_freeallsubwellcons();
	erc_freeallsubwellareas();
}

/*
 * Handle commands to the tool from the user.
 */
INTSML erc_set(INTSML count, char *par[])
{
	REGISTER INTSML l;
	REGISTER NODEPROTO *np;
	REGISTER char *pp;

	if (count == 0)
	{
		ttyputerr("Usage: tellaid erc analyze");
		return(0);
	}

	l = strlen(pp = par[0]);

	if (namesamen(pp, "analyze", l) == 0)
	{
		/* analyze facet */
		np = getcurfacet();
		if (np == NONODEPROTO) return(0);
		erc_analyzefacet(np);
		return(0);
	}
	if (namesamen(pp, "current-error", l) == 0)
	{
		erc_reporterror();
		return(0);
	}
	if (namesamen(pp, "next-error", l) == 0)
	{
		if (erc_nextercerror == NOERCERROR)
		{
			ttyputmsg("No more ERC errors");
			return(0);
		}
		erc_curercerror = erc_nextercerror;
		erc_nextercerror = erc_curercerror->nextercerror;
		erc_prevercerror = erc_curercerror->prevercerror;
		erc_reporterror();
		return(0);
	}
	if (namesamen(pp, "previous-error", l) == 0)
	{
		if (erc_prevercerror == NOERCERROR)
		{
			ttyputmsg("No more ERC errors");
			return(0);
		}
		erc_curercerror = erc_prevercerror;
		erc_nextercerror = erc_curercerror->nextercerror;
		erc_prevercerror = erc_curercerror->prevercerror;
		erc_reporterror();
		return(0);
	}

	ttyputerr("Bad TELLAID ERC option: %s", pp);
	return(1);
}

/******************** ANALYSIS ********************/

/*
 * Routine to analyze facet "facet" and build a list of ERC errors.
 */
void erc_analyzefacet(NODEPROTO *facet)
{
	REGISTER INTBIG errors, minsep, dist, errtype;
	extern AIDENTRY *net_aid;
	REGISTER NODEPROTO *np;
	REGISTER TECHNOLOGY *tech;
	REGISTER char *abortseq;
	REGISTER SUBWELLCON *swc, *oswc;
	REGISTER ERCERROR *ee;
	REGISTER VARIABLE *var;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER SUBWELLAREA *swa, *oswa;
	extern DIALOG us_progressdialog;

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

	/* announce start of analysis */
	(void)initinfstr();
	(void)addstringtoinfstr("Checking electrical rules...");
	abortseq = getinterruptkey();
	if (abortseq != 0)
	{
		(void)addstringtoinfstr(" (type ");
		(void)addstringtoinfstr(abortseq);
		(void)addstringtoinfstr(" to abort)");
	}
	ttyputmsg(returninfstr());
	if (DiaInitDialog(&us_progressdialog) != 0) return;
	DiaPercent(1, 0);
	DiaSetText(2, "Preparing to analyze...");

	/* initialize to analyze facet */
	erc_freeallerrors();
	erc_freeallsubwellcons();
	erc_freeallsubwellareas();
	erc_worstwelldist = erc_worstsubdist = 0;
	erc_facet = facet;
	var = getvalkey((INTBIG)erc_aid, VAID, VINTEGER, erc_optionskey);
	if (var == NOVARIABLE) erc_options = 0; else
		erc_options = var->addr;

	/* put global network information on the ports of this facet */
	erc_globalnetnumber = 1;
	erc_attachednetnumber = 0;
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		pp->temp1 = 0;
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->temp1 != 0) continue;
		pp->temp1 = erc_globalnetnumber++;
		for(opp = pp->nextportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
		{
			if (opp->temp1 != 0) continue;
			if (opp->network == pp->network) opp->temp1 = pp->temp1;
		}
	}

	/* reset count of primitives */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = 0;

	/* run through the work and count the number of polygons */
	erc_polyscrunched = 0;
	erc_realrun = 0;
	erc_gathersubandwell(facet, el_matid);
	erc_jobsize = erc_polyscrunched;

	/* now gather all of the geometry into the polygon merging system */
	DiaSetText(2, "Gathering geometry...");
	mergeinit();
	erc_realrun = 1;
	erc_polyscrunched = 0;
	erc_gathersubandwell(facet, el_matid);

	/* determine the number of polygon points in the analysis */
	DiaSetText(2, "Analyzing...");
	erc_polyscrunched = 0;
	erc_realrun = 0;
	mergedone(erc_getpolygon);
	erc_jobsize = erc_polyscrunched;

	/* do the analysis */
	erc_realrun = 1;
	erc_polyscrunched = 0;
	mergedone(erc_getpolygon);
	DiaPercent(1, 100);

	/* make sure all of the contacts are on the same net */
	for(swc = erc_firstsubwellcon; swc != NOSUBWELLCON; swc = swc->nextsubwellcon)
	{
		if (swc->netnum == 0)
		{
			if (swc->fun == NPWELL) errtype = WELLFLOATS; else
				errtype = SUBFLOATS;
			ee = erc_allocercerror(errtype);
			ee->x1 = swc->x;
			ee->y1 = swc->y;
			continue;
		}
		for(oswc = swc->nextsubwellcon; oswc != NOSUBWELLCON; oswc = oswc->nextsubwellcon)
		{
			if (oswc->netnum == 0) continue;
			if (oswc->fun != swc->fun) continue;
			if (oswc->netnum == swc->netnum) continue;
			if (swc->fun == NPWELL) errtype = WELLDISCON; else
				errtype = SUBDISCON;
			ee = erc_allocercerror(errtype);
			ee->x1 = swc->x;
			ee->y1 = swc->y;
			ee->x2 = oswc->x;
			ee->y2 = oswc->y;
			break;
		}
	}

	/* if just 1 substrate contact is needed, see if it is there */
	if ((erc_options&SUBCONOPTIONS) == SUBCONONCE)
	{
		for(swc = erc_firstsubwellcon; swc != NOSUBWELLCON; swc = swc->nextsubwellcon)
			if (swc->fun == NPSUBSTRATE) break;
		if (swc == NOSUBWELLCON)
		{
			(void)erc_allocercerror(NOSUB);
		}
	}

	/* if just 1 well contact is needed, see if it is there */
	if ((erc_options&WELLCONOPTIONS) == WELLCONONCE)
	{
		for(swc = erc_firstsubwellcon; swc != NOSUBWELLCON; swc = swc->nextsubwellcon)
			if (swc->fun == NPWELL) break;
		if (swc == NOSUBWELLCON)
		{
			(void)erc_allocercerror(NOWELL);
		}
	}

	/* make sure the substrates and wells are separated properly */
	for(swa = erc_firstsubwellarea; swa != NOSUBWELLAREA; swa = swa->nextsubwellarea)
	{
		for(oswa = swa->nextsubwellarea; oswa != NOSUBWELLAREA; oswa = oswa->nextsubwellarea)
		{
			if (swa->tech != oswa->tech || swa->layer != oswa->layer) continue;
			if (swa->netnum == oswa->netnum && swa->netnum >= 0)
				minsep = drcmindistance(swa->tech, swa->layer, swa->layer, 1); else
					minsep = drcmindistance(swa->tech, swa->layer, swa->layer, 0);
			if (minsep < 0) continue;
			dist = polyseparation(swa->poly, oswa->poly);
			if (dist < minsep)
			{
				if (erc_welllayer(swa->tech, swa->layer) != 0) errtype = WELLPROX; else
					errtype = SUBPROX;
				ee = erc_allocercerror(errtype);
				ee->poly1 = swa->poly;
				ee->poly2 = oswa->poly;
			}
		}
	}

	/* show the farthest distance from a well/substrate contact */
	if (erc_worstwelldist > 0 || erc_worstsubdist > 0)
	{
		(void)askaid(us_aid, "clear");
		if (erc_worstwelldist > 0)
		{
			(void)askaid(us_aid, "show-line", erc_worstwellconx, erc_worstwellcony,
				erc_worstwelledgex, erc_worstwelledgey, erc_facet);
			ttyputmsg("Farthest distance from a well contact is %s",
				latoa(erc_worstwelldist));
		}
		if (erc_worstsubdist > 0)
		{
			(void)askaid(us_aid, "show-line", erc_worstsubconx, erc_worstsubcony,
				erc_worstsubedgex, erc_worstsubedgey, erc_facet);
			ttyputmsg("Farthest distance from a substrate contact is %s",
				latoa(erc_worstsubdist));
		}
	}

	/* report the number of transistors */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (np->temp1 == 0) continue;
			switch ((np->userbits&NFUNCTION) >> NFUNCTIONSH)
			{
				case NPTRANMOS:
				case NPTRADMOS:
				case NPTRAPMOS:
				case NPTRANPN:
				case NPTRAPNP:
				case NPTRANJFET:
				case NPTRAPJFET:
				case NPTRADMES:
				case NPTRAEMES:
				case NPTRANE2L:
				case NPTRANSREF:
				case NPTRANS:
					ttyputmsg("%ld nodes of type %s", np->temp1, describenodeproto(np));
					break;
			}
		}
	}

	/* report the number of networks */
	DiaDoneDialog();
	ttyputmsg("%ld networks found", erc_globalnetnumber);
	
	/* report the number of errors found */
	errors = 0;
	for(ee = erc_firstercerror; ee != NOERCERROR; ee = ee->nextercerror)
		errors++;
	if (errors == 0)
		ttyputmsg("No ERC errors found"); else
			ttyputmsg("FOUND %ld ERC %s", errors, makeplural("ERROR", errors));
	setval((INTBIG)us_aid, VAID, "USER_local_capv", errors, VINTEGER|VDONTSAVE);
	erc_curercerror = NOERCERROR;
	erc_nextercerror = erc_firstercerror;
	erc_prevercerror = NOERCERROR;
}

/*
 * Recursive helper routine to gather all substrate and well areas in facet "np"
 * (transformed by "trans").
 */
void erc_gathersubandwell(NODEPROTO *np, XARRAY trans)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *subnp;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER NETWORK *net;
	REGISTER INTSML i, tot, nfun, layer;
	char *extra;
	static INTBIG checkstop = 0;
	XARRAY localrot, localtran, final, subrot;
	static POLYGON *poly = NOPOLYGON;
	REGISTER SUBWELLCON *swc;

	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, erc_aid->cluster);

	/* grab global network information from the ports */
	if (erc_realrun != 0)
	{
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
			net->temp1 = 0;
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			pp->network->temp1 = pp->temp1;
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		{
			if (net->temp1 == 0) net->temp1 = erc_globalnetnumber++;
		}
		if (erc_attachednetnumber == 0) erc_attachednetnumber = erc_globalnetnumber;
		DiaPercent(1, erc_polyscrunched * 100 / erc_jobsize);
	}

	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		subnp = ni->proto;
		if (erc_realrun != 0)
		{
			makerot(ni, localrot);
			transmult(localrot, trans, final);
		}
		if (subnp->primindex == 0)
		{
			if (erc_realrun != 0)
			{
				if (stopping("ERC") != 0) return;

				/* propagate global network numbers into this instance */
				for(pp = subnp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					pp->temp1 = 0;
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					pi->proto->temp1 = pi->conarcinst->network->temp1;
				for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
					pe->proto->temp1 = pe->exportproto->network->temp1;
				for(pp = subnp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					if (pp->temp1 != 0) continue;
					for(opp = subnp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
						if (opp != pp && opp->temp1 != 0 && opp->network == pp->network) break;
					if (opp != NOPORTPROTO) pp->temp1 = opp->temp1; else
						pp->temp1 = erc_globalnetnumber++;
				}

				maketrans(ni, localtran);
				transmult(localtran, final, subrot);
			}
			erc_gathersubandwell(subnp, subrot);
		} else
		{
			if (erc_realrun != 0)
			{
				checkstop++;
				if ((checkstop%10) == 0)
				{
					if (stopping("ERC") != 0) return;
				}
				nfun = nodefunction(ni, &extra);
				ni->proto->temp1++;

				if (nfun == NPWELL || nfun == NPSUBSTRATE)
				{
					swc = erc_allocsubwellcon();
					if (swc == NOSUBWELLCON) return;
					xform((ni->lowx + ni->highx) / 2, (ni->lowy + ni->highy) / 2,
						&swc->x, &swc->y, final);
					swc->np = subnp;
					swc->fun = nfun;
					if (ni->firstportarcinst != NOPORTARCINST)
					{
						swc->netnum = ni->firstportarcinst->conarcinst->network->temp1;
					} else if (ni->firstportexpinst != NOPORTEXPINST)
					{
						swc->netnum = ni->firstportexpinst->exportproto->network->temp1;
					} else swc->netnum = 0;
					swc->nextsubwellcon = erc_firstsubwellcon;
					erc_firstsubwellcon = swc;
				}
			}

			tot = nodepolys(ni);
			erc_polyscrunched += tot;
			if (erc_realrun != 0)
			{
				for(i=0; i<tot; i++)
				{
					shapenodepoly(ni, i, poly);
					if (nfun == NPWELL)
					{
						layer = erc_truewelllayer(poly->tech, poly->layer);
					} else if (nfun == NPSUBSTRATE)
					{
						layer = erc_truesubstratelayer(poly->tech, poly->layer);
					} else layer = poly->layer;
					if (erc_welllayer(poly->tech, layer) == 0 &&
						erc_substratelayer(poly->tech, layer) == 0) continue;
					xformpoly(poly, final);
					mergestorepolygon(layer, poly->tech, poly);
				}
			}
		}
	}

	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		checkstop++;
		if ((checkstop%10) == 0)
		{
			if (stopping("ERC") != 0) return;
		}
		tot = arcpolys(ai);
		erc_polyscrunched += tot;
		if (erc_realrun != 0)
		{
			for(i=0; i<tot; i++)
			{
				shapearcpoly(ai, i, poly);
				if (erc_welllayer(poly->tech, poly->layer) == 0 &&
					erc_substratelayer(poly->tech, poly->layer) == 0) continue;
				xformpoly(poly, trans);
				mergestorepolygon(poly->layer, poly->tech, poly);
			}
		}
	}
}

/*
 * Coroutine of the polygon merging package that is given a merged polygon with
 * "count" points in (x,y) with area "area".
 */
void erc_getpolygon(INTSML layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTSML count)
{
	REGISTER INTSML i, first, welltype;
	REGISTER INTBIG dist, bestdist, desiredcontact, perareabit, nocontacterror, prev, xp, yp;
	REGISTER SUBWELLCON *swc, *bestswc;
	REGISTER ERCERROR *ee;
	REGISTER SUBWELLAREA *swa;
	static INTBIG checkstop = 0;

	erc_polyscrunched += count;
	if (erc_realrun == 0) return;

	checkstop++;
	if ((checkstop%10) == 0)
	{
		if (stopping("ERC") != 0) return;
	} else
	{
		if (el_pleasestop != 0) return;
	}
	DiaPercent(1, erc_polyscrunched * 100 / erc_jobsize);

	/* save the polygon */
	swa = erc_allocsubwellarea();
	swa->poly = allocpolygon(count, erc_aid->cluster);
	for(i=0; i<count; i++)
	{
		swa->poly->xv[i] = x[i];
		swa->poly->yv[i] = y[i];
	}
	swa->poly->count = count;
	swa->poly->style = FILLED;
	swa->layer = layer;
	swa->netnum = 0;
	swa->tech = tech;
	getbbox(swa->poly, &swa->lx, &swa->hx, &swa->ly, &swa->hy);
	swa->nextsubwellarea = erc_firstsubwellarea;
	erc_firstsubwellarea = swa;

	/* special checks for the well/substrate areas */
	welltype = erc_welllayer(tech, layer);
	if (welltype == 0) return;
	if (welltype > 0)
	{
		/* P-well */
		desiredcontact = NPWELL;
		perareabit = WELLCONPERAREA;
		nocontacterror = NOWELLINAREA;
	} else
	{
		/* Select */
		desiredcontact = NPSUBSTRATE;
		perareabit = SUBCONPERAREA;
		nocontacterror = NOSUBINAREA;
	}

	/* find a contact in the area */
	for(swc = erc_firstsubwellcon; swc != NOSUBWELLCON; swc = swc->nextsubwellcon)
	{
		if (swc->fun != desiredcontact) continue;
		if (isinside(swc->x, swc->y, swa->poly) == 0) continue;
		swa->netnum = swc->netnum;
		break;
	}

	/* if no contact, issue appropriate errors */
	if (swc == NOSUBWELLCON)
	{
		if ((erc_options&WELLCONOPTIONS) == perareabit)
		{
			ee = erc_allocercerror(nocontacterror);
			ee->poly1 = swa->poly;
		}
		return;
	}

	/* find the worst distance to the edge of the area */
	for(i=0; i<count*2; i++)
	{
		/* figure out which point is being analyzed */
		if (i < count)
		{
			if (i == 0) prev = count-1; else prev = i-1;
			xp = (x[prev] + x[i]) / 2;
			yp = (y[prev] + y[i]) / 2;
		} else
		{
			xp = x[i-count];
			yp = y[i-count];
		}

		/* find the closest contact to this point */
		first = 1;
		bestdist = 0;
		for(swc = erc_firstsubwellcon; swc != NOSUBWELLCON; swc = swc->nextsubwellcon)
		{
			if (swc->fun != desiredcontact) continue;
			if (isinside(swc->x, swc->y, swa->poly) == 0) continue;
			dist = computedistance(swc->x, swc->y, xp, yp);
			if (first != 0 || dist < bestdist)
			{
				bestdist = dist;
				bestswc = swc;
			}
			first = 0;
		}
		if (first != 0) continue;

		/* accumulate worst distances to edges */
		if (welltype > 0)
		{
			if (bestdist > erc_worstwelldist)
			{
				erc_worstwelldist = bestdist;
				erc_worstwellconx = bestswc->x;
				erc_worstwellcony = bestswc->y;
				erc_worstwelledgex = xp;
				erc_worstwelledgey = yp;
			}
		} else
		{
			if (bestdist > erc_worstsubdist)
			{
				erc_worstsubdist = bestdist;
				erc_worstsubconx = bestswc->x;
				erc_worstsubcony = bestswc->y;
				erc_worstsubedgex = xp;
				erc_worstsubedgey = yp;
			}
		}
	}
}

/*
 * Routine to return nonzero if layer "layer" in technology "tech" is a well layer.
 * (returns 1 if it is P-Well, -1 if it is N-Well).
 */
INTSML erc_welllayer(TECHNOLOGY *tech, INTSML layer)
{
	REGISTER INTBIG fun, funpure;

	fun = layerfunction(tech, layer);
	funpure = fun & LFTYPE;
	if (funpure != LFWELL) return(0);
	if ((fun&LFPTYPE) != 0) return(1);
	return(-1);
}

/*
 * Routine to return nonzero if layer "layer" in technology "tech" is a substrate layer.
 */
INTSML erc_substratelayer(TECHNOLOGY *tech, INTSML layer)
{
	REGISTER INTBIG fun, funpure;

	fun = layerfunction(tech, layer);
	funpure = fun & LFTYPE;
	if (funpure != LFSUBSTRATE && funpure != LFIMPLANT) return(0);
	if ((fun&LFPTYPE) == 0) return(0);
	return(1);
}

/*
 * Routine to convert layer "layer" to an appropriate layer, given that this layer
 * is on a well contact.  Converts the select layer to an unknown layer so that
 * it will be ignored.
 */
INTSML erc_truewelllayer(TECHNOLOGY *tech, INTSML layer)
{
	REGISTER INTBIG fun;

	fun = layerfunction(tech, layer) & LFTYPE;
	if (fun == LFSUBSTRATE || fun == LFIMPLANT) return(-1);
	return(layer);
}

/*
 * Routine to convert layer "layer" to an appropriate layer, given that this layer
 * is on a substrate contact.  Converts the select layer to P type.
 */
INTSML erc_truesubstratelayer(TECHNOLOGY *tech, INTSML layer)
{
	REGISTER INTBIG fun, funpure, cfun;
	REGISTER INTSML i;
	static INTSML altselectlayer;
	static TECHNOLOGY *cachedtech = NOTECHNOLOGY;

	fun = layerfunction(tech, layer);
	funpure = fun & LFTYPE;
	if (funpure == LFSUBSTRATE || funpure == LFIMPLANT)
	{
		if (cachedtech != tech)
		{
			for(i=0; i<tech->layercount; i++)
			{
				cfun = layerfunction(tech, i);
				if ((cfun&LFTYPE) == funpure && (cfun&LFPTYPE) != (fun&LFPTYPE))
					break;
			}
			if (i < tech->layercount)
			{
				cachedtech = tech;
				altselectlayer = i;
			}
		}
		if (cachedtech == tech) layer = altselectlayer;
	}
	return(layer);
}

void erc_freeallsubwellcons(void)
{
	REGISTER SUBWELLCON *swc;

	while (erc_firstsubwellcon != NOSUBWELLCON)
	{
		swc = erc_firstsubwellcon;
		erc_firstsubwellcon = swc->nextsubwellcon;
		efree((char *)swc);
	}
}

SUBWELLCON *erc_allocsubwellcon(void)
{
	REGISTER SUBWELLCON *swc;

	if (erc_subwellconfree != NOSUBWELLCON)
	{
		swc = erc_subwellconfree;
		erc_subwellconfree = swc->nextsubwellcon;
	} else
	{
		swc = (SUBWELLCON *)emalloc(sizeof (SUBWELLCON), erc_aid->cluster);
		if (swc == 0) return(NOSUBWELLCON);
	}
	return(swc);
}

void erc_freesubwellcon(SUBWELLCON *swc)
{
	swc->nextsubwellcon = erc_subwellconfree;
	erc_subwellconfree = swc;
}

void erc_freeallsubwellareas(void)
{
	REGISTER SUBWELLAREA *swa;

	while (erc_firstsubwellarea != NOSUBWELLAREA)
	{
		swa = erc_firstsubwellarea;
		erc_firstsubwellarea = swa->nextsubwellarea;
		freepolygon(swa->poly);
		efree((char *)swa);
	}
}

SUBWELLAREA *erc_allocsubwellarea(void)
{
	REGISTER SUBWELLAREA *swa;

	if (erc_subwellareafree != NOSUBWELLAREA)
	{
		swa = erc_subwellareafree;
		erc_subwellareafree = swa->nextsubwellarea;
	} else
	{
		swa = (SUBWELLAREA *)emalloc(sizeof (SUBWELLAREA), erc_aid->cluster);
		if (swa == 0) return(NOSUBWELLAREA);
	}
	return(swa);
}

void erc_freesubwellarea(SUBWELLAREA *swa)
{
	swa->nextsubwellarea = erc_subwellareafree;
	erc_subwellareafree = swa;
}

/******************** ERROR REPORTING ********************/

void erc_reporterror(void)
{
	REGISTER ERCERROR *ee;

	ee = erc_curercerror;
	if (ee == NOERCERROR)
	{
		ttyputmsg("No ERC errors");
		return;
	}
	(void)askaid(us_aid, "clear");
	switch (ee->error)
	{
		case NOWELL:
			ttyputerr("No well contact found in this facet");
		case NOWELLINAREA:
			erc_showpoly(ee->poly1);
			ttyputerr("No well contact found in this area");
			break;
		case WELLPROX:
			erc_showpoly(ee->poly1);
			erc_showpoly(ee->poly2);
			ttyputerr("Well areas too close");
			break;
		case WELLFLOATS:
			erc_identifypoint(ee->x1, ee->y1, erc_facet);
			ttyputerr("Well contact is floating");
			break;
		case WELLDISCON:
			erc_identifypoint(ee->x1, ee->y1, erc_facet);
			erc_identifypoint(ee->x2, ee->y2, erc_facet);
			ttyputerr("Well contacts are not connected");
			break;
		case NOSUB:
			ttyputerr("No substrate contact found in this facet");
			break;
			break;
		case NOSUBINAREA:
			erc_showpoly(ee->poly1);
			ttyputerr("No substrate contact found in this area");
			break;
		case SUBPROX:
			erc_showpoly(ee->poly1);
			erc_showpoly(ee->poly2);
			ttyputerr("Substrate areas too close");
			break;
		case SUBFLOATS:
			erc_identifypoint(ee->x1, ee->y1, erc_facet);
			ttyputerr("Substrate contact is floating");
			break;
		case SUBDISCON:
			erc_identifypoint(ee->x1, ee->y1, erc_facet);
			erc_identifypoint(ee->x2, ee->y2, erc_facet);
			ttyputerr("Substrate contacts are not connected");
			break;
		case FLOATINGARC:
			erc_showpoly(ee->poly1);
			ttyputerr("Arc not globally connected");
			break;
	}
}

void erc_showpoly(POLYGON *poly)
{
	REGISTER INTSML i;
	REGISTER INTBIG fx, fy, tx, ty;

	for(i=0; i<poly->count; i++)
	{
		if (i == 0)
		{
			fx = poly->xv[poly->count-1];
			fy = poly->yv[poly->count-1];
		} else
		{
			fx = poly->xv[i-1];
			fy = poly->yv[i-1];
		}
		tx = poly->xv[i];
		ty = poly->yv[i];
		(void)askaid(us_aid, "show-line", fx, fy, tx, ty, erc_facet);
	}
}

void erc_identifypoint(INTBIG x, INTBIG y, NODEPROTO *facet)
{
	REGISTER INTBIG consize;

	consize = el_curtech->deflambda * 5;
	(void)askaid(us_aid, "show-line", x-consize, y-consize,
		x+consize, y+consize, facet);
	(void)askaid(us_aid, "show-line", x-consize, y+consize,
		x+consize, y-consize, facet);
}

ERCERROR *erc_allocercerror(INTBIG errtype)
{
	REGISTER ERCERROR *ee;

	if (erc_ercerrorfree != NOERCERROR)
	{
		ee = erc_ercerrorfree;
		erc_ercerrorfree = ee->nextercerror;
	} else
	{
		ee = (ERCERROR *)emalloc(sizeof (ERCERROR), erc_aid->cluster);
		if (ee == 0) return(NOERCERROR);
	}
	ee->error = errtype;
	ee->poly1 = NOPOLYGON;
	ee->poly2 = NOPOLYGON;
	ee->prevercerror = NOERCERROR;
	ee->nextercerror = erc_firstercerror;
	if (erc_firstercerror != NOERCERROR) erc_firstercerror->prevercerror = ee;
	erc_firstercerror = ee;
	return(ee);
}

void erc_freeercerror(ERCERROR *ee)
{
	ee->nextercerror = erc_ercerrorfree;
	erc_ercerrorfree = ee;
}

/*
 * Routine to free all previously stored ERC errors.
 */
void erc_freeallerrors(void)
{
	REGISTER ERCERROR *ee;

	while (erc_firstercerror != NOERCERROR)
	{
		ee = erc_firstercerror;
		erc_firstercerror = ee->nextercerror;
		efree((char *)ee);
	}
	erc_curercerror = NOERCERROR;
	erc_nextercerror = NOERCERROR;
	erc_prevercerror = NOERCERROR;
}

/******************** OPTIONS DIALOG ********************/

/*
 * special case for the "ERC Options" dialog
 * Well contact in each area  = 3 (radio)
 * Well contact once          = 4 (radio)
 * Well contact not needed    = 5 (radio)
 * Sub. contact in each area  = 7 (radio)
 * Sub. contact once          = 8 (radio)
 * Sub. contact not needed    = 9 (radio)
 */
void erc_optionsdlog(void)
{
	REGISTER INTBIG itemHit, ercoptions;
	REGISTER VARIABLE *var;

	if (DiaInitDialog(&erc_optionsdialog) != 0) return;
	var = getvalkey((INTBIG)erc_aid, VAID, VINTEGER, erc_optionskey);
	if (var == NOVARIABLE) ercoptions = 0; else
		ercoptions = var->addr;
	switch (ercoptions & WELLCONOPTIONS)
	{
		case WELLCONPERAREA: DiaSetControl(3, 1);   break;
		case WELLCONONCE:    DiaSetControl(4, 1);   break;
		case WELLCONIGNORE:  DiaSetControl(5, 1);   break;
	}
	switch (ercoptions & SUBCONOPTIONS)
	{
		case SUBCONPERAREA: DiaSetControl(7, 1);   break;
		case SUBCONONCE:    DiaSetControl(8, 1);   break;
		case SUBCONIGNORE:  DiaSetControl(9, 1);   break;
	}

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit == 3 || itemHit == 4 || itemHit == 5)
		{
			DiaSetControl(3, 0);
			DiaSetControl(4, 0);
			DiaSetControl(5, 0);
			DiaSetControl(itemHit, 1);
			continue;
		}
		if (itemHit == 7 || itemHit == 8 || itemHit == 9)
		{
			DiaSetControl(7, 0);
			DiaSetControl(8, 0);
			DiaSetControl(9, 0);
			DiaSetControl(itemHit, 1);
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		/* change state of ERC */
		ercoptions = 0;
		if (DiaGetControl(3) != 0) ercoptions |= WELLCONPERAREA; else
			if (DiaGetControl(4) != 0) ercoptions |= WELLCONONCE; else
				ercoptions |= WELLCONIGNORE;
		if (DiaGetControl(7) != 0) ercoptions |= SUBCONPERAREA; else
			if (DiaGetControl(8) != 0) ercoptions |= SUBCONONCE; else
				ercoptions |= SUBCONIGNORE;
		(void)setvalkey((INTBIG)erc_aid, VAID, erc_optionskey, ercoptions, VINTEGER);
	}
	DiaDoneDialog();
}

#endif  /* ERCAID - at top */
