/*
 * Electric(tm) VLSI Design System
 *
 * File: dbmerge.c
 * Box merging subsystem
 * Written by Philip Attfield, Queens University, Kingston Ontario.
 * Revised by Sid Penstone, Queens University, Kingston Ontario.
 *
 * 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 "egraphics.h"
#include <math.h>

/***************************************************************************
 * Initially, call:
 *    mrginit()
 *
 * For every polygon, call:
 *    mrgstorebox(layer, tech, xsize, ysize, xcenter, ycenter)
 * where "layer" is an integer layer number from technology "tech"; "xsize"
 * and "ysize" are size of the box; "xcenter" and "ycenter" are the center
 * of the box
 *
 * At end of facet, call:
 *    mrgdonefacet(writepolygon)
 * where "writepolygon" is a routine that will be called for every polygon with
 * five parameters:
 *   (1) the layer number (INTSML)
 *   (2) the technology
 *   (3) the X coordinate array (INTBIG array)
 *   (4) the Y coordinate array (INTBIG array)
 *   (5) the number of coordinates in each array (INTSML)
 *
 * When done with merging, call:
 *    mrgterm()
 ***************************************************************************
 */

#define MAXNUMPTS 106   /* maximum allowable number of points on a polygon */
static INTBIG maxnumpts = MAXNUMPTS; /* apply default (PJA:920527) */
# define PTMATCH 2      /* point match */

/* polygon direction and box edge identifiers */
#define	UP          0
#define	RIGHT       1
#define	DOWN        2
#define	LEFT        3

POLYLISTHEAD db_mrg_polycoordlist;
POLYCOORD   *db_mrg_free_poly_list;
BOXPOLY     *db_mrg_free_box_list;
BOXLISTHEAD *db_mrg_free_boxhead_list;
BOXLISTHEAD *db_mrg_curr_facet;

/* working storage for "db_mrg_write_polygon()" */
static INTBIG *db_mrgxbuf, *db_mrgybuf;
static INTSML db_mrgbuflen = 0;

/* prototypes for local routines */
INTBIG mrg_set_maxnumpts(INTBIG);
INTSML db_mrg_included(BOXPOLY*, BOXPOLY*);
void db_mrg_remove_inclusion(BOXLISTHEAD*);
void db_mrg_remove_overlap(BOXLISTHEAD*);
BOXPOLY *db_mrg_breakbox(BOXPOLY*, BOXPOLY*, INTSML*);
BOXPOLY *db_mrg_do_break(BOXPOLY*, BOXPOLY*, INTSML*);
void db_mrg_write_lists(void(*)(INTSML, TECHNOLOGY*, INTBIG*, INTBIG*, INTSML));
POLYCOORD *db_mrg_getpoly(void);
INTSML db_mrg_mergeboxwithpolygon(BOXPOLY*);
INTSML db_mrg_check_intersect(BOXPOLY*, POLYCOORD*, INTSML*);
INTSML db_mrg_check_point(INTBIG[], INTBIG[], INTBIG[], INTBIG[]);
void db_mrg_insert1(INTSML, BOXPOLY*);
void db_mrg_insert2(INTSML, BOXPOLY*);
void db_mrg_insert3(INTSML, BOXPOLY*);
void db_mrg_insert4(INTSML, BOXPOLY*);
void db_mrg_write_polygon(void(*)(INTSML, TECHNOLOGY*, INTBIG*, INTBIG*, INTSML), INTSML, TECHNOLOGY*);
INTSML db_mrg_pred(INTSML);
INTSML db_mrg_succ(INTSML);
INTSML db_mrg_nxtside(INTSML);
INTSML db_mrg_prevside(INTSML);
void db_mrg_assign_edge(POLYCOORD*, INTBIG[], INTBIG[]);
void db_mrg_housekeeper(void);
void db_mrg_insert5(INTSML, INTSML, BOXPOLY*);
BOXPOLY *db_mrg_getbox(void);
void db_mrg_trash(BOXPOLY*);
void db_mrg_dump_free_box_list(void);
void db_mrg_assign_box(BOXPOLY*, INTBIG, INTBIG, INTBIG, INTBIG);
void db_mrg_killpoly(POLYCOORD*);
void db_mrg_dump_free_poly_list(void);
void db_mrg_remove_poly_list(void);
BOXLISTHEAD *db_mrg_get_lay_head(void);
void db_mrg_dump_free_boxhead_list(void);
void db_mrg_remove_boxhead_list(void);

INTBIG mrg_set_maxnumpts(INTBIG npts) /* set maximum number of vertices allowed on polygon (PJA:920527) */
{
	INTBIG cmax;

	if (npts <= 0)
	{ /* nonsense */
		return(0);
	}
	cmax = maxnumpts; /* will return current maximum */
	maxnumpts = npts; /* set as new maximum */
	return(cmax); /* and return the old max */
}

void mrginit(void)
{
}

void mrgterm(void)
{
	db_mrg_dump_free_poly_list();
	db_mrg_dump_free_box_list();
	db_mrg_dump_free_boxhead_list();
}

void mrgdonefacet(void (*writepolygon)(INTSML, TECHNOLOGY*, INTBIG*, INTBIG*, INTSML))
{
	db_mrg_write_lists(writepolygon);
	db_mrg_remove_boxhead_list();
}

void mrgstorebox(INTSML layer, TECHNOLOGY *tech, INTBIG length, INTBIG width,
	INTBIG xc, INTBIG yc)
{
	BOXLISTHEAD *layhead;
	BOXPOLY *creatptr;
	INTBIG left, right, top, bot;

	for(layhead = db_mrg_curr_facet; layhead != NULL; layhead = layhead->nextlayer)
		if (layhead->layer == layer && layhead->tech == tech) break;

	if (layhead == NULL)
	{
		layhead = db_mrg_get_lay_head();
		layhead->nextlayer = db_mrg_curr_facet; /* form link */
		db_mrg_curr_facet = layhead; /* modify facet layer list header */
		layhead->layer = layer;
		layhead->tech = tech;
		layhead->box = NULL;
	}

	/* layhead now points to the correct layer head descriptor */
	creatptr = db_mrg_getbox();
	creatptr->nextbox = layhead->box;
	layhead->box = creatptr; /* insert new descriptor at top of list */
	left = xc - (length/2);
	right = xc + (length/2);
	top = yc + (width/2);
	bot = yc - (width/2);

	/* assign box descriptor values */
	db_mrg_assign_box(creatptr, left, right, top, bot);
}

/************************* INTERNAL ROUTINES *************************/

/*
 * called once at initialization to set globals
 */
void db_mrgdatainit(void)
{
	db_mrg_free_poly_list = NULL;
	db_mrg_free_box_list = NULL;
	db_mrg_free_boxhead_list = NULL;
	db_mrg_curr_facet = NULL;
}

/* checks inclusion */
INTSML db_mrg_included(BOXPOLY *me, BOXPOLY *him)
{
	if (him->left >= me->left && him->right <= me->right &&
		him->top <= me->top && him->bot >= me->bot)
			return(1); /* him is an inclusion */
	return(0); /* him is NOT an inclusion */
}

/* removes inclusions which follow on list */
void db_mrg_remove_inclusion(BOXLISTHEAD *lay_box)
{
	BOXPOLY *me, *him, *oldhim, *garbage;

	me = lay_box->box;
	garbage = NULL;

	if (me->nextbox != NULL) /* if not a singular list */
	{
		for(; me->nextbox != NULL;)
		{
			oldhim = me;
			for(him = me->nextbox; him != NULL;)
			{
				if (db_mrg_included(me, him)) /* if him is an inclusion of me */
				{
					garbage = him;
					oldhim->nextbox = him->nextbox; /* bypass him on list */
					him = him->nextbox;
					garbage->nextbox = NULL;
					db_mrg_trash(garbage);
				} else /* not an inclusion */
				{
					oldhim = him;
					him = him->nextbox;
				}
			} /* end for */

			/* catches case where everyone on list after me was an */
			me = me->nextbox;
			if (me == NULL) break;   /* inclusion */
		} /* end for */
	} /* end if */
}

void db_mrg_remove_overlap(BOXLISTHEAD *layhead)
{
	BOXPOLY *me, *him, *oldme, *garbage, *broken, *temp,
		*endptr;
	INTSML bugflag, rtc;

	oldme = layhead->box;
	me = oldme;
	bugflag  = 0;
	garbage = NULL;

	if (me->nextbox != NULL)
	{
		for(him = me->nextbox; me->nextbox != NULL;)
		{
			if (bugflag)
			{
				oldme = me;
				garbage->nextbox = NULL;
				db_mrg_trash(garbage);
				bugflag = 0;
			}

			/* now check rtc for results */
			broken = db_mrg_breakbox(me,him,&rtc);
			if (rtc == 1) /* if inclusion, remove me from list */
			{
				garbage = me;
				if (me == layhead->box) /* if me is first on list */
				{
					layhead->box = me->nextbox; /* bypass me on list */
					oldme = me;
					bugflag = 1;
				} else /* me is not the first on list */
				{
					oldme->nextbox = me->nextbox;

					/* back me up on list for correct entry on next iter. */
					me = oldme;
					garbage->nextbox = NULL;
					db_mrg_trash(garbage);
				}
			} else if (rtc == 2) /* independent... do nothing */
			{
				;
			} else
			{
				/* rtc = 0, replace me on list with list returned by break */
				garbage = me;
				for(temp=broken; temp!=NULL; )  /*find eol */
				{
					endptr = temp;
					temp = temp->nextbox;
				}
				if (me == layhead->box) /* if me is first on list */
					layhead->box = broken;
						else oldme->nextbox = broken;
				endptr->nextbox = me->nextbox;
				me = endptr;
				garbage->nextbox = NULL;
				db_mrg_trash(garbage);
			}
			oldme=me;
			me=me->nextbox;
			him=me->nextbox;
		} /* end for */
	} /* end if */
}

BOXPOLY *db_mrg_breakbox(BOXPOLY *me, BOXPOLY *him, INTSML *rtc)
{
	BOXPOLY *list, *oldme, *newlist;
	BOXPOLY *garbage, *temp, *endptr, *broken;
	INTSML retc, calrtc, notfinished=1;

	newlist = NULL;
	while (notfinished)
	{
		list = db_mrg_do_break(me, him, &retc);
		if (retc == 2) /* if independent, do again with him->next */
			if (him->nextbox != NULL) him = him->nextbox; else
		{
			/* we are at the end of the him list and have nothing to return */
			if (newlist == NULL)
			{
				*rtc = 2;
				return(NULL);
			}
		} else if (retc == 1)
		{
			/* if an inclusion */
			if (newlist == NULL)
			{
				/* if empty list, let this guy be eaten up */
				*rtc = 1;
				return(NULL);
			} else
			{
				/* if we get here, we are in deep trouble */
			}
		} else /* if we got here, a break occurred..do something with list */
		{
			if (newlist == NULL) /* it has to be anyway */
			{
				newlist = list;
				if (him->nextbox == NULL) /* at end of hims, return code 0 */
				{
					*rtc = 0;
					return(newlist);
				} else /* must recurse across the list */
				{
					oldme = NULL;
					for (me=newlist; me!=NULL; )
					{
						/* do a break */
						broken = db_mrg_breakbox(me, him->nextbox, &calrtc);
						if (calrtc == 2) /* no break done, look at next guy */
						{
							oldme = me;
							me = me->nextbox;
						} else if (calrtc == 1)
						{
							/* me is an inclusion... remove me */
							garbage = me;
							if (me == newlist) /* if me is first on list */
							{
								newlist = me->nextbox;
								me = newlist; /* put me at top of loop again */
								if (me == NULL)
								{
									/* if me was only one left and got eaten */
									*rtc = 1;
									garbage->nextbox = NULL;
									db_mrg_trash(garbage);
									return(NULL);
								}
							} else /* me was not first on list */
							{
								/* oldme still behind me */
								oldme->nextbox = me->nextbox;
								me = me->nextbox;
							}
							garbage->nextbox = NULL;
							db_mrg_trash(garbage);
						} else
						{
							/* calrct=0, add list after newlist or after oldme */
							for(temp=broken;temp!=NULL;) /*get eol */
							{
								endptr=temp;
								temp=temp->nextbox;
							}
							endptr->nextbox = me->nextbox;
							if (me == newlist) /* if me is first on list */
							{
								garbage = me;
								newlist = broken;
								oldme = endptr;
								me = endptr->nextbox;
								garbage->nextbox = NULL;
								db_mrg_trash(garbage);
							} else /* me is not first on list */
							{
								garbage = me;
								oldme->nextbox = broken;
								oldme = endptr;
								me = endptr->nextbox;
								garbage->nextbox = NULL;
								db_mrg_trash(garbage);
							}
						} /* end if calrtc=0 */
					} /* end for */
					*rtc = 0;
					return(newlist);
				}
			}
		}
	} /* end while */
	return(NULL);
}

BOXPOLY *db_mrg_do_break(BOXPOLY *me, BOXPOLY *him, INTSML *rtc)
{
	BOXPOLY *broken, *breakk, *temp;

	/* check for disjointedness */
	if (me->right <= him->left || me->top <= him->bot || me->left >= him->right ||
		me->bot >= him->top)
	{
		*rtc = 2;
		return(NULL);
	}

	/* check for inclusion */
	if (me->top <= him->top && me->left >= him->left && me->bot >= him->bot &&
		me->right <= him->right)
	{
		*rtc = 1;
		return(NULL);
	}

	/* check for identical boxes */
	if (me->top == him->top && me->bot == him->bot && me->left == him->left &&
		me->right == him->right)
	{
		*rtc = 1;
		return(NULL);
	}

	if (me->top > him->top && me->left < him->left && me->bot < him->bot
		&& me->right < him->right && me->right > him->left)
	{
		temp = db_mrg_getbox();
		db_mrg_assign_box(temp,me->left,me->right,me->top,him->top);
		broken = temp;
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,me->left,him->left,him->top,him->bot);
		breakk = db_mrg_getbox();
		temp->nextbox = breakk;
		db_mrg_assign_box(breakk,me->left,me->right,him->bot,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->bot < him->bot && me->left < him->left && me->right > him->right
		&& me->top > him->bot && me->top < him->top)
	{
		temp = db_mrg_getbox();
		db_mrg_assign_box(temp,me->left,him->left,me->top,me->bot);
		broken = temp;
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,him->left,him->right,him->bot,me->bot);
		breakk = db_mrg_getbox();
		temp->nextbox = breakk;
		db_mrg_assign_box(breakk,him->right,me->right,me->top,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->right > him->right && me->top > him->top && me->bot < him->bot
		&& me->left < him->right && me->left > him->left)
	{
		temp = db_mrg_getbox();
		db_mrg_assign_box(temp,me->left,me->right,me->top,him->top);
		broken = temp;
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,him->right,me->right,him->top,him->bot);
		breakk = db_mrg_getbox();
		temp->nextbox = breakk;
		db_mrg_assign_box(breakk,me->left,me->right,him->bot,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->left < him->left && me->right > him->right && me->top > him->top
		&& me->bot > him->bot && me->bot < him->top)
	{
		temp = db_mrg_getbox();
		db_mrg_assign_box(temp,me->left,him->left,me->top,me->bot);
		broken = temp;
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,him->left,him->right,me->top,him->top);
		breakk = db_mrg_getbox();
		temp->nextbox = breakk;
		db_mrg_assign_box(breakk,him->right,me->right,me->top,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (him->left <= me->left && him->top >= me->top && him->bot <= me->bot
		&& me->right > him->right && me->left < him->right)
	{
		broken = db_mrg_getbox();
		db_mrg_assign_box(broken,him->right,me->right,me->top,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->left < him->left && him->top >= me->top && him->bot <= me->bot
		&& him->right >= me->right && me->right > him->left)
	{
		broken = db_mrg_getbox();
		db_mrg_assign_box(broken,me->left,him->left,me->top,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (him->left <= me->left && him->right >= me->right && him->bot <= me->bot
		&& me->bot < him->top && me->top > him->top)
	{
		broken = db_mrg_getbox();
		db_mrg_assign_box(broken,me->left,me->right,me->top,him->top);
		*rtc = 0;
		return(broken);
	}

	if (him->left <= me->left && him->right >= me->right && him->top >= me->top
		&& me->top > him->bot && me->bot < him->bot)
	{
		broken = db_mrg_getbox();
		db_mrg_assign_box(broken,me->left,me->right,him->bot,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->top > him->top && me->left < him->left && me->bot < him->top &&
		me->bot >= him->bot && me->right > him->left && me->right <= him->right)
	{
		temp = db_mrg_getbox();
		broken = temp;
		db_mrg_assign_box(temp,me->left,me->right,me->top,him->top);
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,me->left,him->left,him->top,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->top > him->top && me->right > him->right && me->left >= him->left
		&& me->left < him->right && me->bot < him->top && me->bot >= him->bot)
	{
		temp = db_mrg_getbox();
		broken = temp;
		db_mrg_assign_box(temp,me->left,me->right,me->top,him->top);
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,him->right,me->right,him->top,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->right > him->right && me->bot < him->bot && me->left >= him->left
		&& me->left < him->right && me->top > him->bot && me->top <= him->top)
	{
		temp = db_mrg_getbox();
		broken = temp;
		db_mrg_assign_box(temp,him->right,me->right,me->top,him->bot);
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,me->left,me->right,him->bot,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->left < him->left && me->bot < him->bot && me->top > him->bot &&
		me->top <= him->top && me->right > him->left && me->right <= him->right)
	{
		temp = db_mrg_getbox();
		broken = temp;
		db_mrg_assign_box(temp,me->left,him->left,me->top,him->bot);
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,me->left,me->right,him->bot,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->left < him->left && me->right > him->right
		&& him->top >= me->top && him->bot <= me->bot)
	{
		temp = db_mrg_getbox();
		broken = temp;
		db_mrg_assign_box(temp,me->left,him->left,me->top,me->bot);
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,him->right,me->right,me->top,me->bot);
		*rtc = 0;
		return(broken);
	}

	if (me->top > him->top && me->bot < him->bot
		&& me->left >= him->left && me->right <= him->right)
	{
		temp = db_mrg_getbox();
		broken = temp;
		db_mrg_assign_box(temp,me->left,me->right,me->top,him->top);
		temp = db_mrg_getbox();
		broken->nextbox = temp;
		db_mrg_assign_box(temp,me->left,me->right,him->bot,me->bot);
		*rtc = 0;
		return(broken);
	}

	/* give the user a warning if this routine did not catch some case */
	ttyputerr(_("Warning: merging done incorrectly"));
	ttyputerr("me: left = %ld, right = %ld, top = %ld, bot = %ld",
		me->left, me->right, me->top, me->bot);
	ttyputerr("him: left = %ld, right = %ld, top = %ld, bot = %ld",
		him->left, him->right, him->top, him->bot);

	/* although this is in error, it will enable a completion of the write */
	*rtc = 1;
	return(NULL);
}

void db_mrg_write_lists(void (*writepolygon)(INTSML, TECHNOLOGY*, INTBIG*, INTBIG*, INTSML))
{
	BOXLISTHEAD *lay_box;
	BOXPOLY *boxscanptr, *garbage, *oldboxscan;
	INTSML lay_not_done, merged;

	for(lay_box=db_mrg_curr_facet; lay_box!=NULL; lay_box=lay_box->nextlayer) /* for each layer... */
	{
		db_mrg_remove_inclusion(lay_box); /* remove inclusions down list */
		db_mrg_remove_overlap(lay_box); /* break into nonoverlapping boxes */
		db_mrg_remove_inclusion(lay_box);
		lay_not_done = 1;
/*		(*setlayer)(lay_box->layer, lay_box->tech); */
		db_mrg_polycoordlist.firstpoly = NULL;
		db_mrg_polycoordlist.numedge = 0;
		while (lay_not_done)
		{
			db_mrg_polycoordlist.modified = 0;
			for(boxscanptr=lay_box->box; boxscanptr != NULL; )
			{
				/* passing through list of boxes */
				merged = db_mrg_mergeboxwithpolygon(boxscanptr);
				if (merged) /* if list was modified */
				{
					/* set flag that we made a modification */
					db_mrg_polycoordlist.modified = 1;
					merged = 0; /* reset indicator flag */

					/* if box is first on list */
					if (boxscanptr == lay_box->box)
					{
						garbage = boxscanptr;
						lay_box->box = boxscanptr->nextbox;
						boxscanptr = lay_box->box;
						garbage->nextbox = NULL;
						db_mrg_trash(garbage);

						/* if NULL list, we are done */
						if (lay_box->box == NULL)
						{
							lay_not_done = 0;
							break;
						}
					} else
					{
						/* not the first on list */
						garbage = boxscanptr;
						oldboxscan->nextbox = boxscanptr->nextbox;
						boxscanptr = boxscanptr->nextbox;
						garbage->nextbox = NULL;
						db_mrg_trash(garbage);
					}
				} else
				{
					/* no merge done, check next box */
					oldboxscan = boxscanptr;
					boxscanptr = boxscanptr->nextbox;
				}

				/* allow maxnumpts as a max */
				if (db_mrg_polycoordlist.numedge > maxnumpts) break;
			} /* end for */

			/*
			 * write out box if: (1) entire pass yielded no merges
			 * (2) end of list (3) too many points on list
			 */
			if ((!db_mrg_polycoordlist.modified) || (!lay_not_done) ||
				(db_mrg_polycoordlist.numedge > maxnumpts))
			{
				db_mrg_write_polygon(writepolygon, lay_box->layer, lay_box->tech);
				db_mrg_remove_poly_list(); /* dispose of polygon records */

				/* re-initialize for next pass */
				db_mrg_polycoordlist.firstpoly = NULL;
				db_mrg_polycoordlist.numedge = 0;
				db_mrg_polycoordlist.modified = 0;
			}
		} /* end while */
	}
}

/* Macro to insert edge on polygon */
# define INSERT_EDGE(pred,tmp,copy,succ,dmaep1,dmaep2) \
  tmp = db_mrg_getpoly(); \
  tmp->nextpoly = succ; \
  if (pred != NULL) {pred->nextpoly = tmp;} \
  db_mrg_assign_edge(tmp, dmaep1, dmaep2); \
  copy = tmp;

/* Macro to remove n edges (nedge) from polygon */
# define REMOVE_EDGE(pred,start,tmp,nedge) \
  for (i = 0; i < nedge; i++) { \
	  tmp = start->nextpoly; \
	  pred->nextpoly = tmp; \
	  if (start == db_mrg_polycoordlist.firstpoly) { \
		  db_mrg_polycoordlist.firstpoly = tmp; \
	  } \
	  db_mrg_killpoly(start); \
	  start = tmp; \
  }

INTSML db_mrg_mergeboxwithpolygon(BOXPOLY *boxptr)
{
	POLYCOORD *polyptr,*creatptr,*backptr;
	INTSML pair, ok, one_con, i, firstcon, err, ed_con;
	INTBIG connect, auxx, ed_chk, j;

	pair = 0; /* flag to indicate a pair */
	one_con = 0; /* flas to intdicate that intersection was found */
	boxptr->numsides = 0;
	ok = 0;
	backptr = NULL;

	/* initialise seen field if list exists */
	if (db_mrg_polycoordlist.firstpoly != NULL)
	{
		/* initialize pointer to list */
		polyptr = db_mrg_polycoordlist.firstpoly;
		for(i=0; i!=db_mrg_polycoordlist.numedge;)
		{
			polyptr->seen = 1;
			i++;
			polyptr = polyptr->nextpoly;
		}
	}
	for(i=0; i<4; i++)
	{
		boxptr->numint[i] = 0;
		boxptr->polyint[i]=boxptr->auxpolyint[i] = NULL;
	}
	if (db_mrg_polycoordlist.firstpoly == NULL) /* if nothing on list */
	{
		/* make first edge (at head) */
		INSERT_EDGE(backptr,creatptr,backptr,NULL,boxptr->ll,boxptr->ul)
		db_mrg_polycoordlist.firstpoly = creatptr;

		/* second edge */
		INSERT_EDGE(backptr,creatptr,backptr,NULL,boxptr->ul,boxptr->ur)

		/* third edge */
		INSERT_EDGE(backptr,creatptr,backptr,NULL,boxptr->ur,boxptr->lr)

		/* fourth edge (and form ring) */
		INSERT_EDGE(backptr,creatptr,backptr,db_mrg_polycoordlist.firstpoly,boxptr->lr,boxptr->ll)

		db_mrg_housekeeper(); /* set up edge numbers */;
		return(1);  /* all done */
	} else /* we have some edges already */
	{
		for(polyptr=db_mrg_polycoordlist.firstpoly; polyptr->seen; /* check for intersection to all edges */
			polyptr=polyptr->nextpoly)
		{
			polyptr->seen = 0; /* indicate that we have been here */
			connect = db_mrg_check_intersect(boxptr, polyptr, &ed_con);
			if (connect == PTMATCH) /* if a point intersection was found */
				return(0); /* point intersections are not permitted */
			if (connect) /* if connection was found */
			{
				one_con = 1; /* found at least 1 connection */

				/* > 2 connections on one side */
				if (boxptr->numint[ed_con] == 2) return(0);
				if (pair && boxptr->polyint[ed_con] != NULL) /* 2 pairs found */
					return(0);
				if (boxptr->polyint[ed_con] == NULL) /* if first connection */
				{
					/* link edge to polygon descriptor */
					boxptr->polyint[ed_con] = polyptr;
					boxptr->numint[ed_con] += 1; /* increment count for side */

					/* increment number sides intersected */
					boxptr->numsides += 1;
				} else /* second connection for one "first" edge */
				{
					/* link to poly descriptor */
					boxptr->auxpolyint[ed_con] = polyptr;

					/* increment side connection count */
					boxptr->numint[ed_con] += 1;
					pair = 1; /* we have found a pair */
				}
			} /* end if connect */
		} /* end for */

		if (!one_con) /* if no connections found */
			return(0);
		else /* found at least one connection */
		{
			/* based on #sides of box which touched polygon */
			switch (boxptr->numsides)
			{
				case 4: /* intersects on 4 edges */
					if (pair) /* intersects 1 edge twice */
					{
						/* find edge which intersects twice */
						for(i=0; boxptr->numint[i]!=2; i++)
							;
						firstcon = i;
						if ((boxptr->polyint[db_mrg_nxtside(i)]->indx) ==
							db_mrg_succ((INTSML)(boxptr->polyint[i]->indx)))
								auxx = 0;  /* check the statement below */
						else if((boxptr->polyint[db_mrg_nxtside(i)]->indx) ==
							db_mrg_succ((INTSML)(boxptr->auxpolyint[i]->indx)))
								auxx = 1;
						else /* neither edge connection had next side with correct index number */
							return(0);
						if (auxx) /* if auxiliary pointer is the start */
						{
							for(ed_chk=db_mrg_nxtside(i); ed_chk!=i;
								ed_chk=db_mrg_nxtside((INTSML)ed_chk))
									if ((boxptr->polyint[db_mrg_nxtside((INTSML)ed_chk)]->indx) ==
										db_mrg_succ((INTSML)(boxptr->polyint[ed_chk]->indx)))
											continue;
									else
										/* successor had wrong index number */
										return(0);

							/* if we got here all is well */
							db_mrg_insert5(firstcon, (INTSML)auxx, boxptr);
						} else /* auxx = 0 */
						{
							for(ed_chk=db_mrg_nxtside(i); ed_chk != i;
								ed_chk=db_mrg_nxtside((INTSML)ed_chk))
							{
								/* check for last edge */
								if (ed_chk != db_mrg_prevside(firstcon))
								{
									if (boxptr->polyint[db_mrg_nxtside(
										(INTSML)ed_chk)]->indx == db_mrg_succ(
											(INTSML)(boxptr->polyint[ed_chk]->indx)))
												continue;
									return(0);	/* index numbers out of sequence */
								} else
								{
									if ((boxptr->auxpolyint[db_mrg_nxtside(
										(INTSML)ed_chk)]->indx) == (db_mrg_succ(
										(INTSML)(boxptr->polyint[ed_chk]->indx))))
											continue;
									return(0);	/* index numbers out of sequence */
								}
							}
							db_mrg_insert5(firstcon, (INTSML)auxx, boxptr);
						}
					} else /* intersects 4 edges, 1 time each edge */
					{
						ok = 0;

						/* find "start" edge of intersect sequence */
						for(i=0;i<4;i++)
						{
							err = 0;
							ed_chk = i;
							for(j = 0; j < 3; j++)
							{
								if ((boxptr->polyint[db_mrg_nxtside(
									(INTSML)ed_chk)]->indx) != (db_mrg_succ(
										(INTSML)(boxptr->polyint[ed_chk]->indx))))
											err = 1; /* error found */
								ed_chk = db_mrg_nxtside((INTSML)ed_chk);
							}
							if (!err)
							{
								ok = 1;
								break;
							}
						} /* end for */
						if (ok) /* check if we found a correct sequence */
						{
							firstcon = i;
							db_mrg_insert4(firstcon,boxptr);
						} else
							return(0); /* index out of sequence */
					}
					break;

				case 3:
					/* if a pair of contacts on 1 edge of box ... loop found */
					if (pair) return(0);
						else
					{
						/* i is index of edge which doesn't intersect polygon */
						for(i = 0; i < 4; i++)
							if (boxptr->polyint[i] == NULL) break;

						/* index of first non null side */
						firstcon = db_mrg_nxtside(i);
						for(j = firstcon; j != db_mrg_prevside(i);
							j = db_mrg_nxtside((INTSML)j))
								if ((boxptr->polyint[db_mrg_nxtside((INTSML)j)]->indx) !=
									(db_mrg_succ((INTSML)(boxptr->polyint[j]->indx))))
										return(0);
						db_mrg_insert3(firstcon,boxptr);
					}
					break;

				case 2: /* box has 2 edges which intersect polygon */
					if (pair) /* the usual */
						return(0);
					else
					{
						for(i = 0; i < 4; i++) /* find a NULL edge */
							if (boxptr->polyint[i] == NULL) break;
						for(j = i; boxptr->polyint[j] == NULL;
							j = db_mrg_nxtside((INTSML)j))
								;
						firstcon = (INTSML)j; /* first edge connection */

						/* have detected nonsadjacent pair of intersections */
						if (boxptr->polyint[db_mrg_nxtside(firstcon)] == NULL)
							return(0);
						if ((boxptr->polyint[db_mrg_nxtside((INTSML)j)]->indx) !=
							(db_mrg_succ((INTSML)(boxptr->polyint[j]->indx))))
								return(0); /* index numbers out of sequence... */
						db_mrg_insert2(firstcon,boxptr);
					}
					break;

				case 1: /* one edge intersects */
					if (pair) /* if a pair, we have a loop */
						return(0);
					else
					{
						/* find edge which intersects */
						for(i=0; boxptr->polyint[i]==NULL; i++)
							;
						firstcon = i;
						db_mrg_insert1(firstcon,boxptr);
					}
					break;

				default:
					break;
			} /* end switch */

			db_mrg_housekeeper();
			return(1); /* successful addition of box to polygon */
		} /* end else */
	} /* end have edges already */
}

/* returns 0 if no intersection,else 1 (line intersection) or 2 (point intersection) */
INTSML db_mrg_check_intersect(BOXPOLY *boxptr, POLYCOORD *polyptr, INTSML *ed_con)
{
	switch (polyptr->ed_or)
	{
		case UP:
			if (boxptr->right == polyptr->ed_val) /* may be colinear */
			{
				if ((boxptr->bot >= polyptr->ed_start[1] &&
					boxptr->bot < polyptr->ed_end[1]) ||
						(boxptr->top <= polyptr->ed_end[1] &&
							boxptr->top > polyptr->ed_start[1]) ||
								(boxptr->bot < polyptr->ed_start[1] &&
									boxptr->top > polyptr->ed_end[1]))
				{
					*ed_con = DOWN;
					return(1);
				}
				return(db_mrg_check_point(polyptr->ed_start,boxptr->ur,
					polyptr->ed_end,boxptr->lr)); /* check for pt connect */
			}
			return(0);

		case RIGHT:
			if (boxptr->bot == polyptr->ed_val)
			{
				if ((boxptr->left >= polyptr->ed_start[0] &&
					boxptr->left < polyptr->ed_end[0]) ||
						(boxptr->right > polyptr->ed_start[0] &&
							boxptr->right <= polyptr->ed_end[0]) ||
								(boxptr->left < polyptr->ed_start[0] &&
									boxptr->right > polyptr->ed_end[0]))
				{
					*ed_con = LEFT;
					return(1);
				}
				return(db_mrg_check_point(polyptr->ed_start,boxptr->lr,
					polyptr->ed_end,boxptr->ll));
			}
			return(0);

		case DOWN:
			if (boxptr->left == polyptr->ed_val)
			{
				if ((boxptr->bot >= polyptr->ed_end[1] &&
					boxptr->bot < polyptr->ed_start[1]) ||
						(boxptr->top > polyptr->ed_end[1] &&
							boxptr->top <= polyptr->ed_start[1]) ||
								(boxptr->top > polyptr->ed_start[1] &&
									boxptr->bot < polyptr->ed_end[1]))
				{
					*ed_con = UP;
					return(1);
				}
				return(db_mrg_check_point(polyptr->ed_start,boxptr->ll,
					polyptr->ed_end,boxptr->ul));
			}
			return(0);

		case LEFT:
			if (boxptr->top == polyptr->ed_val)
			{
				if ((boxptr->left >= polyptr->ed_end[0] &&
					boxptr->left < polyptr->ed_start[0]) ||
						(boxptr->right > polyptr->ed_end[0] &&
							boxptr->right <= polyptr->ed_start[0]) ||
								(boxptr->left < polyptr->ed_end[0] &&
									boxptr->right > polyptr->ed_start[0]))
				{
					*ed_con = RIGHT;
					return(1);
				}
				return(db_mrg_check_point(polyptr->ed_start,boxptr->ul,
					polyptr->ed_end,boxptr->ur));
			}
			return(0);
	} /* end switch */
	return(0);
}

/* PoinT EQual macro */

# define PTEQ(x,y) \
(((x)[0] == (y)[0]) && ((x)[1] == (y)[1]))

/* checks if points a1, a2 match or if points b1, b2 match */
INTSML db_mrg_check_point(INTBIG pointa1[], INTBIG pointa2[], INTBIG pointb1[],
	INTBIG pointb2[])
{
	if (PTEQ(pointa1, pointa2) || PTEQ(pointb1, pointb2)) {
		return(PTMATCH); /* at least one pair matches */
	}
	return(0); /* neither pair matches */
}

void db_mrg_insert1(INTSML firstcon, BOXPOLY *boxptr)
{
	POLYCOORD *polyptr, *nextedge, *creatptr, *backptr;
	INTBIG *p0, *p1, *p2, *p3;

	/* get pointer to edge of intersection */
	polyptr = boxptr->polyint[firstcon];
	nextedge = polyptr->nextpoly;
	for(backptr = db_mrg_polycoordlist.firstpoly; backptr->nextpoly != polyptr;
		backptr = backptr->nextpoly) /* gat pointer to edge before polyptr */
			;

	switch (polyptr->ed_or) { /* based on polygon edge orientation */
		case UP:
			/*  p3 --- p1  |
				|      \/  ^
				|      \/  ^
				p2 --- p0  |
			*/
			p0 = boxptr->lr;
			p1 = boxptr->ur;
			p2 = boxptr->ll;
			p3 = boxptr->ul;
			break;

		case RIGHT:
			/*  p2 --- p3
				|      |
				|      |
				p0 -<- p1
				--->>>---
			 */
			p0 = boxptr->ll;
			p1 = boxptr->lr;
			p2 = boxptr->ul;
			p3 = boxptr->ur;
			break;

		case DOWN:
			/*  | p0 --- p2
			   \/ ^      |
			   \/ ^      |
				| p1 --- p3
			 */
			p0 = boxptr->ul;
			p1 = boxptr->ll;
			p2 = boxptr->ur;
			p3 = boxptr->lr;
			break;

		case LEFT:
			/* ---<<<---
			   p1 ->- p0
			   |      |
			   |      |
			   p3 --- p2
			 */
			p0 = boxptr->ur;
			p1 = boxptr->ul;
			p2 = boxptr->lr;
			p3 = boxptr->ll;
			break;

		default:
			break;
	} /* end switch */
	/*
	  Generic code to handle case of 1 box edge touching 1 polygon edge
	  p0: point on box which might intersect start point of edge
	  p1: point on box which might intersect end point of edge
	  p2: point on box which is diagonally opposite p1
	  p3: point on box which is diagonally opposite p0
	  */

	/* if first point matches */
	if PTEQ(polyptr->ed_start, p0) {
		/* if second point matches */
		if PTEQ(polyptr->ed_end, p1) {
			/* both points match (adjust edge start/end points) */
			db_mrg_assign_edge(backptr, backptr->ed_start, p2);
			db_mrg_assign_edge(polyptr, p2, p3);
			db_mrg_assign_edge(nextedge, p3, nextedge->ed_end);
		}
		else { /* only first point matches (adjust 2 edges, add 2 edges) */
			db_mrg_assign_edge(backptr, backptr->ed_start, p2);
			db_mrg_assign_edge(polyptr, p2, p3);
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p3, p1)
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p1, nextedge->ed_start)
		}
	}
	else { /* first point does not match */
		/* if second point matches */
		if PTEQ(polyptr->ed_end, p1) {
			/* only second point matches (adjust 2 edges, add 2 edges) */
			db_mrg_assign_edge(polyptr,polyptr->ed_start, p0);
			db_mrg_assign_edge(nextedge, p3,nextedge->ed_end);
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p0, p2)
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p2, p3)
		}
		else {
			/* neither point matches (adjust first edge, add 4 new edges) */
			db_mrg_assign_edge(polyptr,polyptr->ed_start, p0);
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p0, p2)
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p2, p3)
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p3, p1)
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge, p1, nextedge->ed_start)
		}
	}
}

/* inserts box into polygon where 2 edges touched */
void db_mrg_insert2(INTSML firstcon, BOXPOLY *boxptr)
{
	POLYCOORD *polyptr, *nextedge, *creatptr, *backptr;
	INTBIG *p0, *p1, *p2;

	/* assign polyptr to first edge where intersection occurs */
	polyptr = boxptr->polyint[firstcon];
	nextedge = polyptr->nextpoly; /* get pointer to next edge */

	switch (polyptr->ed_or) { /* based on polygon edge direction */
		case RIGHT:
		/*
			p2-p1 |
			|  |  ^
			p0-|  ^
				  |
			-->>--|
		 */
			p0 = boxptr->ll;
			p1 = boxptr->ur;
			p2 = boxptr->ul;
			break;

		case LEFT:
		/*
			--<<--
			| |--p0
		   \/ |   |
		   \/ |   |
			| p1-p2
		 */
			p0 = boxptr->ur;
			p1 = boxptr->ll;
			p2 = boxptr->lr;
			break;

		case UP:
		/*
			--<<--|
				  |
			p1--| ^
			|   | ^
			|   | |
			p2-p0 |
		 */
			p0 = boxptr->lr;
			p1 = boxptr->ul;
			p2 = boxptr->ll;
			break;

		case DOWN:
		/*
			 | p0--p2
			\/ |   |
			\/ |   |
			 | |---p1
			 |
			 -->>----
		 */
			p0 = boxptr->ul;
			p1 = boxptr->lr;
			p2 = boxptr->ur;
			break;

		default:
			break;
	} /* end switch */
	/*
	  Generic code to handle case of 2 box edges touching 2 polygon edges
	  p0: point on box which might intersect start point of firsst polygon edge
	  p1: point on box which might intersect end point of second polygon edge
	  p2: point on box which is diagonally opposite "corner" of intersection of polygon edges
	  */

	/* if first point matches */
	if PTEQ(polyptr->ed_start, p0) {
		/* if second point matches */
		if PTEQ(nextedge->ed_end, p1) {
			/* both points match */
			db_mrg_assign_edge(polyptr,polyptr->ed_start,p2);
			db_mrg_assign_edge(nextedge,p2,nextedge->ed_end);
		}
		else {
			/* first matches only */
			for(backptr = db_mrg_polycoordlist.firstpoly;
				backptr->nextpoly != polyptr;
				backptr = backptr->nextpoly)
					;
			db_mrg_assign_edge(backptr,backptr->ed_start,p2);
			db_mrg_assign_edge(polyptr,p2,p1);
			db_mrg_assign_edge(nextedge,p1,nextedge->ed_end);
		}
	}
	else { /* first point doesnt match */
		/* second point matches */
		if PTEQ(nextedge->ed_end, p1) {
			db_mrg_assign_edge(polyptr,polyptr->ed_start,p0);
			db_mrg_assign_edge(nextedge,p0,p2);
			nextedge = nextedge->nextpoly;
			db_mrg_assign_edge(nextedge,p2,nextedge->ed_end);
		}
		else {
			/* neither point touches */
			db_mrg_assign_edge(polyptr,polyptr->ed_start,p0);
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge,p0,p2)
			INSERT_EDGE(polyptr,creatptr,polyptr,nextedge,p2,p1)
			db_mrg_assign_edge(nextedge,p1,nextedge->ed_end);
		}
	}
}

void db_mrg_insert3(INTSML firstcon, BOXPOLY *boxptr)
{
	POLYCOORD *polyptr, *nextedge, *garbage, *backptr, *temp;
	INTSML i;
	INTBIG *p0, *p1;

	/* assign polyptr to first edge */
	polyptr = boxptr->polyint[firstcon];
	nextedge = polyptr->nextpoly;
	nextedge = nextedge->nextpoly; /* third edge which intersects */
	for(backptr = db_mrg_polycoordlist.firstpoly; backptr->nextpoly != polyptr;
		backptr = backptr->nextpoly)  /* get back pointer */
			;

	switch (polyptr->ed_or) { /* based on polygon edge direction */
		case RIGHT:
		/*
			---<<---|
					|
			p1----| |
			|     | ^
			|     | ^
			p0----| |
					|
			--->>---|
		 */
			p0 = boxptr->ll;
			p1 = boxptr->ul;
			break;

		case LEFT:
		/*
		   |---<<---
		   |
		   | |----p0
		  \/ |     |
		  \/ |     |
		   | |----p1
		   |
		   |--->>---
		 */
			p0 = boxptr->ur;
			p1 = boxptr->lr;
			break;

		case UP:
		/*
		  |---<<----|
		  |         |
		  | |-----| |
		 \/ |     | ^
		 \/ |     | ^
		  | p1---p0 |
		  |         |
		 */
			p0 = boxptr->lr;
			p1 = boxptr->ll;
			break;

		case DOWN:
		/*
		  |         |
		  | p0---p1 |
		 \/ |     | ^
		 \/ |     | ^
		  | |-----| |
		  |         |
		  |--->>----|
		 */
			p0 = boxptr->ul;
			p1 = boxptr->ur;
			break;

		default:
			break;
	} /* end switch */
	/*
	  Generic code to handle case of 3 box edges touching 3 polygon edges
	  p0: point on box which might intersect start point of first polygon edge
	  p1: point on box which might intersect end point of third polygon edge
	  */

	/* if first point matches */
	if PTEQ(polyptr->ed_start, p0) {
		/* if second point matches */
		if PTEQ(nextedge->ed_end, p1) {
		  /* both points match */
			nextedge = nextedge->nextpoly;
			db_mrg_assign_edge(backptr,backptr->ed_start,
							   nextedge->ed_end);
			REMOVE_EDGE(backptr,polyptr,garbage,4) /* remove 4 edges beginning with polyptr */
		}
		else { /* first point matches only */
			db_mrg_assign_edge(backptr,backptr->ed_start,p1);
			db_mrg_assign_edge(nextedge,p1,nextedge->ed_end);
			REMOVE_EDGE(backptr,polyptr,garbage,2) /* remove 2 edges beginning with polyptr */
		}
	}
	else { /* first point doesn't match */
		if PTEQ(nextedge->ed_end, p1) {
			/* second point matches only */
			garbage = polyptr->nextpoly;
			nextedge = nextedge->nextpoly;
			db_mrg_assign_edge(polyptr,polyptr->ed_start,p0);
			db_mrg_assign_edge(nextedge,p0,nextedge->ed_end);
			REMOVE_EDGE(polyptr,garbage,temp,2) /* remove 2 edges beginning with garbage */
		}
		else { /* neither point matches */
			db_mrg_assign_edge(polyptr,polyptr->ed_start,p0);
			temp = polyptr->nextpoly;
			db_mrg_assign_edge(temp,p0,p1);
			db_mrg_assign_edge(nextedge,p1,nextedge->ed_end);
		}
	}
}

/* inserts box where each edge intersected once */
void db_mrg_insert4(INTSML firstcon, BOXPOLY *boxptr)
{
	INTSML i;
	POLYCOORD *polyptr, *nextedge, *garbage, *temp, *backptr;
	INTBIG *p0;
	INTBIG descendp; /* for the below/above/rightof/leftof case */

	polyptr = boxptr->polyint[firstcon];
	nextedge = boxptr->polyint[db_mrg_prevside(firstcon)];
	for(backptr = db_mrg_polycoordlist.firstpoly; backptr->nextpoly != polyptr;
		backptr = backptr->nextpoly)  /* get back pointer */
			;

	switch (polyptr->ed_or) { /* based on polygon edge direction */
		case RIGHT:
		/*
		   |----<<---|
		   |         |
		  \/ |-----| |
		  \/ |     | ^
		   | |     | ^
			 p0----| |
					 |
			 --->>---|
		 */
			p0 = boxptr->ll;
			descendp = ((nextedge->ed_end[1] < polyptr->ed_start[1]) ? 1 : 0);
			break;

		case LEFT:
		/*
		   |---<<---
		   |
		   | |----p0
		  \/ |     | |
		  \/ |     | ^
		   | |-----| ^
		   |         |
		   |--->>----|
		 */
			p0 = boxptr->ur;
			descendp = ((nextedge->ed_end[1] > polyptr->ed_start[1]) ? 1 : 0);
			break;

		case UP:
		/*
		  |---<<----|
		  |         |
		  | |-----| |
		 \/ |     | ^
		 \/ |     | ^
		  | |----p0 |
		  |         |
		  |--->>-
		 */
			p0 = boxptr->lr;
			descendp = ((nextedge->ed_end[0] > polyptr->ed_start[0]) ? 1 : 0);
			break;

		case DOWN:
		/*
			  -<<---|
		  |         |
		  | p0---p1 |
		 \/ |     | ^
		 \/ |     | ^
		  | |-----| |
		  |         |
		  |--->>----|
		 */
			p0 = boxptr->ul;
			descendp = ((nextedge->ed_end[0] < polyptr->ed_start[0]) ? 1 : 0);
			break;

		default:
			break;
	} /* end switch */
	/*
	  Generic code to handle case where 4 polygon edges perfectly enclose
	  (not necessarily fullythough) box
	  p0: start point on box which intersects first edge on polygon
	  */

	/* if first point matches */
	if PTEQ(polyptr->ed_start, p0) {
		db_mrg_assign_edge(backptr,backptr->ed_start,nextedge->ed_end);
		REMOVE_EDGE(backptr,polyptr,garbage,4) /* remove 4 edges beginning with polyptr */
	}
	else if PTEQ(nextedge->ed_end, p0) { /* if second point */
		nextedge = nextedge->nextpoly;
		db_mrg_assign_edge(polyptr,polyptr->ed_start,nextedge->ed_end);
		temp = polyptr->nextpoly;
		REMOVE_EDGE(polyptr,temp,garbage,4) /* remove 4 edges following polyptr */
	}
	else if (descendp) {
		/* this is a weird case (next->end "below"/"above"/"rightof"/"leftof" p0) */
		db_mrg_assign_edge(nextedge,p0,nextedge->ed_end);
		db_mrg_assign_edge(polyptr,polyptr->ed_start,p0);
		temp = polyptr->nextpoly;
		REMOVE_EDGE(polyptr,temp,garbage,2) /* remove 2 edges following polyptr */
	}
	else { /* more normal case */
		db_mrg_assign_edge(polyptr,polyptr->ed_start,p0);
		temp = polyptr->nextpoly;
		db_mrg_assign_edge(nextedge,p0,nextedge->ed_end);
		REMOVE_EDGE(polyptr,temp,garbage,2) /* remove 2 edges following polyptr */
	}
}

/* write polygon */
void db_mrg_write_polygon(void (*writepolygon)(INTSML, TECHNOLOGY*, INTBIG*, INTBIG*, INTSML),
	INTSML layer, TECHNOLOGY *tech)
{
	POLYCOORD *polyptr;
	INTSML i;

	if (db_mrg_polycoordlist.numedge > db_mrgbuflen)
	{
		/* verify adequate buffer space */
		if (db_mrgbuflen != 0)
		{
			efree((char *)db_mrgxbuf);
			efree((char *)db_mrgybuf);
		}
		db_mrgbuflen = db_mrg_polycoordlist.numedge;
		db_mrgxbuf = (INTBIG *)emalloc((db_mrgbuflen * SIZEOFINTBIG), db_cluster);
		db_mrgybuf = (INTBIG *)emalloc((db_mrgbuflen * SIZEOFINTBIG), db_cluster);
	}

	polyptr = db_mrg_polycoordlist.firstpoly;
	for(i=0; i<db_mrg_polycoordlist.numedge; i++)
	{
		db_mrgxbuf[i] = polyptr->ed_start[0];
		db_mrgybuf[i] = polyptr->ed_start[1];
		polyptr = polyptr->nextpoly; /* increment pointer */
	}
	(*writepolygon)(layer, tech, db_mrgxbuf, db_mrgybuf, db_mrg_polycoordlist.numedge);
}

/* returns successor polygon edge # */
INTSML db_mrg_succ(INTSML aindex)
{
	if (aindex == (db_mrg_polycoordlist.numedge - 1))
		return(0);
	return(aindex + 1);
}

/* returns index number of next edge on box, in CCW order */
INTSML db_mrg_nxtside(INTSML aindex)
{
	if (aindex == 0) return(3);
	return(aindex - 1);
}

/* returns index number of previous edge on box */
INTSML db_mrg_prevside(INTSML aindex)
{
	if (aindex == 3) return(0);
	return(aindex+1);
}

/* assigns characteristics to polygon edge */
void db_mrg_assign_edge(POLYCOORD *polyptr, INTBIG start[], INTBIG end[])
{
	INTSML i;

	if (start[0] == end[0]) /* if same in X */
	{
		polyptr->ed_val = start[0];
		if (start[1] < end[1]) polyptr->ed_or = UP;
			else polyptr->ed_or = DOWN;
	} else /* same in Y */
	{
		polyptr->ed_val = start[1];
		if (start[0] < end[0]) polyptr->ed_or = RIGHT;
			else polyptr->ed_or = LEFT;
	}
	for(i = 0; i < 2; i++)
	{
		polyptr->ed_start[i] = start[i];
		polyptr->ed_end[i] = end[i];
	}
}

/* corrects for co-linear edges, and updates coordinate indices */
void db_mrg_housekeeper(void)
{
	POLYCOORD *polyptr, *garbage, *nextedge;
	INTSML indx;

	for(polyptr = db_mrg_polycoordlist.firstpoly;
		polyptr->nextpoly != db_mrg_polycoordlist.firstpoly;
			polyptr = polyptr->nextpoly)
	polyptr->seen = 1;
	polyptr->seen = 1; /* do last on one ring */
	polyptr = db_mrg_polycoordlist.firstpoly;
	for(nextedge = polyptr->nextpoly; polyptr->seen;)
	{
		if (polyptr->ed_or == nextedge->ed_or) /* if edge orientation is same */
		{
			garbage = nextedge;
			polyptr->ed_end[0] = nextedge->ed_end[0]; /* copy end point */
			polyptr->ed_end[1] = nextedge->ed_end[1];

			/* if about to remove list head */
			if (nextedge == db_mrg_polycoordlist.firstpoly)
				db_mrg_polycoordlist.firstpoly = nextedge->nextpoly;
			polyptr->nextpoly = nextedge->nextpoly;
			nextedge = nextedge->nextpoly;
			garbage->nextpoly = NULL;
			db_mrg_killpoly(garbage);
		} else
		{
			/* not colinear, set as seen */
			polyptr->seen = 0;
			polyptr = nextedge;
			nextedge = nextedge->nextpoly;
		}
	} /* colinearities removed */

	/* update sequence numbers */
	indx = 0;
	for(polyptr = db_mrg_polycoordlist.firstpoly; polyptr->nextpoly !=
		db_mrg_polycoordlist.firstpoly; polyptr = polyptr->nextpoly)
	{
		polyptr->indx = indx;
		indx += 1;
	}
	polyptr->indx = indx; /* do last one */
	db_mrg_polycoordlist.numedge = indx + 1; /* set number of edges */
}

/* inserts box into polygon where intersected 5* */
void db_mrg_insert5(INTSML firstcon, INTSML auxx, BOXPOLY *boxptr)
{
	POLYCOORD *polyptr, *nextedge, *garbage, *temp;
	INTSML i;

	if (auxx) { /* if auxptr points to first intersection */
		polyptr = boxptr->auxpolyint[firstcon];
		nextedge = boxptr->polyint[firstcon];
	} else {
		polyptr = boxptr->polyint[firstcon];
		nextedge = boxptr->auxpolyint[firstcon];
	}

	/* insertion position is independent of edge orientation */
	db_mrg_assign_edge(polyptr,polyptr->ed_start,nextedge->ed_end);
	temp = polyptr->nextpoly;
	REMOVE_EDGE(polyptr,temp,garbage,4) /* remove 4 edges following polyptr */
}

/* some support stuff to do box poly descriptor mem management. */
BOXPOLY *db_mrg_getbox(void)
{
	BOXPOLY *b;

	if (db_mrg_free_box_list == NULL) {
		b = (BOXPOLY *)emalloc(sizeof(BOXPOLY), db_cluster);
		if (b == 0) return(NULL);
		b->nextbox = NULL;
		return(b);
	}
	b = db_mrg_free_box_list;
	db_mrg_free_box_list = b->nextbox;
	b->nextbox = NULL;
	return(b);
}

void db_mrg_trash(BOXPOLY *b)
{
	b->nextbox = db_mrg_free_box_list;
	db_mrg_free_box_list = b;
}

void db_mrg_dump_free_box_list(void)
{
	BOXPOLY *b;

	while (db_mrg_free_box_list != NULL) {
		b = db_mrg_free_box_list;
		db_mrg_free_box_list = b->nextbox;
		b->nextbox = NULL;
		efree((char *)b);
	}
}

void db_mrg_assign_box(BOXPOLY *ptr, INTBIG left, INTBIG right, INTBIG top, INTBIG bot)
{
	ptr->ul[0] = ptr->ll[0] = ptr->left = left;
	ptr->ur[0] = ptr->lr[0] = ptr->right = right;
	ptr->ul[1] = ptr->ur[1] = ptr->top = top;
	ptr->ll[1] = ptr->lr[1] = ptr->bot = bot;
	if ((ptr->right - ptr->left) > (ptr->top - ptr->bot)) {
		ptr->ishor = 1;
	}
	else {
		ptr->ishor = 0;
	}
}

POLYCOORD *db_mrg_getpoly(void)
{
	POLYCOORD *p;

	if (db_mrg_free_poly_list == NULL) {
		p = (POLYCOORD *)emalloc(sizeof(POLYCOORD), db_cluster);
		if (p == 0) return(NULL);
		p->nextpoly = NULL;
		return(p);
	}
	p = db_mrg_free_poly_list;
	db_mrg_free_poly_list = p->nextpoly;
	p->nextpoly = NULL;
	return(p);
}

void db_mrg_killpoly(POLYCOORD *p)
{
	p->nextpoly = db_mrg_free_poly_list;
	db_mrg_free_poly_list = p;
}

void db_mrg_dump_free_poly_list(void)
{
	POLYCOORD *p;

	while (db_mrg_free_poly_list != NULL) {
		p = db_mrg_free_poly_list;
		db_mrg_free_poly_list = p->nextpoly;
		p->nextpoly = NULL;
		efree((char *)p);
	}
}

void db_mrg_remove_poly_list(void)
{
	POLYCOORD *polyptr,*endptr;

	/* find end of list and break ring */
	for(endptr = db_mrg_polycoordlist.firstpoly;
		endptr->nextpoly != db_mrg_polycoordlist.firstpoly;
		endptr = endptr->nextpoly)
			;

	/* ring is broken now */
	endptr->nextpoly = NULL;
	while (db_mrg_polycoordlist.firstpoly != NULL) {
		polyptr = db_mrg_polycoordlist.firstpoly;
		db_mrg_polycoordlist.firstpoly = polyptr->nextpoly;
		polyptr->nextpoly = NULL;
		db_mrg_killpoly(polyptr);
	}
}

BOXLISTHEAD *db_mrg_get_lay_head(void)
{
	BOXLISTHEAD *bl;

	if (db_mrg_free_boxhead_list == NULL) {
		bl = (BOXLISTHEAD *)emalloc(sizeof(BOXLISTHEAD), db_cluster);
		if (bl == 0) return(NULL);
		bl->nextlayer = NULL;
		return(bl);
	}
	bl = db_mrg_free_boxhead_list;
	db_mrg_free_boxhead_list = bl->nextlayer;
	bl->nextlayer = NULL;
	return(bl);
}

void db_mrg_dump_free_boxhead_list(void)
{
	BOXLISTHEAD *bl;

	while (db_mrg_free_boxhead_list != NULL) {
		bl = db_mrg_free_boxhead_list;
		db_mrg_free_boxhead_list = bl->nextlayer;
		bl->nextlayer = NULL;
		bl->box = NULL;
		efree((char *)bl);
	}
}

void db_mrg_remove_boxhead_list(void)
{
	BOXLISTHEAD *bl,*endptr;

	if (db_mrg_curr_facet != NULL) {
		endptr = NULL;
		for(bl = db_mrg_curr_facet; bl != NULL;) {
			bl->box = NULL;
			endptr = bl;
			bl = bl->nextlayer;
		}
		endptr->nextlayer = db_mrg_free_boxhead_list;
		db_mrg_free_boxhead_list = db_mrg_curr_facet;
		db_mrg_curr_facet = NULL;
	}
}

/***************************************************************************
 * Initially, call:
 *    mergeinit()
 *
 * For every polygon, call:
 *    mergestorepolygon(layer, tech, poly)
 * where "layer" is an integer layer number from technology "tech"; "poly"
 * is a polygon to be added
 *
 * At end of facet, call:
 *    mergedone(writepolygon)
 * where "writepolygon" is a routine that will be called for every polygon with
 * five parameters:
 *   (1) the layer number (INTSML)
 *   (2) the technology
 *   (3) the X coordinate array (INTBIG array)
 *   (4) the Y coordinate array (INTBIG array)
 *   (5) the number of coordinates in each array (INTSML)
 ***************************************************************************
 */

#define NOMPOLY ((MPOLY *)-1)

typedef struct Impoly
{
	INTBIG         layer;				/* layer of this polygon */
	TECHNOLOGY    *tech;				/* technology of this polygon */
	INTBIG         count;				/* number of points in this polygon */
	INTBIG         limit;				/* space available in this polygon */
	INTBIG        *x;					/* X coordinates of this polygon */
	INTBIG        *y;					/* Y coordinates of this polygon */
	INTBIG        *seen;				/* whether the point has been visited */
	INTBIG        *intersect;			/* intersection information for the points */
	INTBIG         lx, hx, ly, hy;		/* bounding box of this polygon */
	struct Impoly *nextmpoly;			/* next polygon in linked list */
} MPOLY;

MPOLY  *db_mpolylist = NOMPOLY;			/* the list of active MPOLYs */
MPOLY  *db_mpolyfree = NOMPOLY;			/* the list of unused MPOLYs */
MPOLY  *db_mergenewmp = NOMPOLY;		/* temporary MPOLY for merging */
MPOLY  *db_mergeholemp = NOMPOLY;		/* temporary MPOLY for finding holes */

/* for figuring out what to do at an intersection */
INTBIG *db_mergeintpol;					/* polygon with intersection */
INTBIG *db_mergeintpt;					/* point with intersection */
INTBIG *db_mergeintleaveangle;			/* angle of intersection */
INTBIG  db_mergeintcount;				/* number of intersection */
INTBIG  db_mergeinttotal = 0;			/* size of intersection arrays */

INTSML db_mergeaddholetopoly(MPOLY *holemp, MPOLY *newmp);
void   db_mergeaddintersectionpoint(INTBIG polygon, INTBIG point);
INTSML db_mergeaddintersectionpoints(MPOLY *mp, MPOLY *omp);
MPOLY *db_mergeallocpoly(void);
INTSML db_mergecopypoly(MPOLY *smp, MPOLY *dmp);
INTSML db_mergeensurespace(MPOLY *mp, INTBIG count);
INTBIG db_mergefindstartingpoint(MPOLY *mp, MPOLY *omp);
void   db_mergefreepoly(MPOLY *mp);
INTSML db_mergeinsertpoint(MPOLY *newmp, INTBIG *insert, INTBIG x, INTBIG y);
INTSML db_mergeisinside(INTBIG x, INTBIG y, MPOLY *mp);
INTSML db_mergelinesintersect(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty,
		INTBIG ofx, INTBIG ofy, INTBIG otx, INTBIG oty, INTBIG *ix, INTBIG *iy);
INTSML db_mergepoly(MPOLY *mp, MPOLY *omp);
void   db_mergesimplifyandbbox(MPOLY *mp);
INTSML db_mergewalkedge(MPOLY *mp, INTBIG i, MPOLY *omp, MPOLY *newmp);

/* #define DEBUGWALK    1 */		/* uncomment to debug polygon point traversal */
/* #define SHOWRESULTS  1 */		/* uncomment to show results of each merge */
/* #define SHOWTICKS    1 */		/* uncomment to show coordinate values with results */
/* #define DESIREDLAYER 6 */		/* uncomment to select a specific layer */

/*
 * Routine to free all memory associated with this module.
 */
void db_freemergememory(void)
{
	REGISTER MPOLY *mp;

	/* free memory used by manhattan merger */
	if (db_mrgbuflen != 0)
	{
		efree((char *)db_mrgxbuf);
		efree((char *)db_mrgybuf);
	}

	/* free memory used by polygonal merger */
	if (db_mergenewmp != NOMPOLY) db_mergefreepoly(db_mergenewmp);
	if (db_mergeholemp != NOMPOLY) db_mergefreepoly(db_mergeholemp);
	while (db_mpolylist != NOMPOLY)
	{
		mp = db_mpolylist;
		db_mpolylist = mp->nextmpoly;
		db_mergefreepoly(mp);
	}
	while (db_mpolyfree != NOMPOLY)
	{
		mp = db_mpolyfree;
		db_mpolyfree = mp->nextmpoly;
		if (mp->limit > 0)
		{
			efree((char *)mp->x);
			efree((char *)mp->y);
			efree((char *)mp->seen);
			efree((char *)mp->intersect);
		}
		efree((char *)mp);
	}

	if (db_mergeinttotal > 0)
	{
		efree((char *)db_mergeintpol);
		efree((char *)db_mergeintpt);
		efree((char *)db_mergeintleaveangle);
	}
}

/*
 * Routine to initialize for polygon merging.  After this call, multiple calls to
 * "mergestorepolygon()" may be made, followed by a single call to "mergedone()"
 * to obtain the merged geometry.
 */
void mergeinit(void)
{
	REGISTER MPOLY *mp;

	/* free up the former list */
	while (db_mpolylist != NOMPOLY)
	{
		mp = db_mpolylist;
		db_mpolylist = mp->nextmpoly;
		db_mergefreepoly(mp);
	}
}

/*
 * Routine to add polygon "poly" to the merged collection.  The polygon is on layer
 * "layer" of technology "tech".
 */
void mergestorepolygon(INTBIG layer, TECHNOLOGY *tech, POLYGON *poly)
{
	REGISTER MPOLY *mp, *omp, *lastmp;
	REGISTER INTBIG i, done;
	INTBIG lx, hx, ly, hy;

	/* copy the polygon into a local structure */
	mp = db_mergeallocpoly();
	if (db_mergeensurespace(mp, poly->count) != 0) return;
	if (poly->style == FILLEDRECT || poly->style == CLOSEDRECT)
	{
		getbbox(poly, &lx, &hx, &ly, &hy);
		makerectpoly(lx, hx, ly, hy, poly);
	}
	if (areapoints(poly->count, poly->xv, poly->yv) < 0)
	{
		/* reverse direction of points while copying */
		for(i=0; i<poly->count; i++)
		{
			mp->x[i] = poly->xv[poly->count-i-1];
			mp->y[i] = poly->yv[poly->count-i-1];
		}
	} else
	{
		for(i=0; i<poly->count; i++)
		{
			mp->x[i] = poly->xv[i];
			mp->y[i] = poly->yv[i];
		}
	}
	mp->count = poly->count;
	mp->layer = layer;
	mp->tech = tech;
	db_mergesimplifyandbbox(mp);

	/* now look for merges */
	for(omp = db_mpolylist; omp != NOMPOLY; omp = omp->nextmpoly)
	{
		if (db_mergepoly(mp, omp) != 0) break;
	}
	if (omp == NOMPOLY)
	{
		/* not merged: just add to the list */
		mp->nextmpoly = db_mpolylist;
		db_mpolylist = mp;
	} else
	{
		/* merged: delete this */
		db_mergefreepoly(mp);

		/* see if any other merging can be done */
		done = 0;
		while (done == 0)
		{
			done = 1;
			lastmp = NOMPOLY;
			for(mp = db_mpolylist; mp != NOMPOLY; mp = mp->nextmpoly)
			{
				if (mp != omp)
				{
					if (db_mergepoly(mp, omp) != 0)
					{
						if (lastmp == NOMPOLY) db_mpolylist = mp->nextmpoly; else
							lastmp->nextmpoly = mp->nextmpoly;
						db_mergefreepoly(mp);
						done = 0;
						break;
					}
				}
				lastmp = mp;
			}
		}
	}

#ifdef SHOWRESULTS

#ifdef DESIREDLAYER
	if (layer == DESIREDLAYER)
#endif
	{
		REGISTER NODEPROTO *facet, *prim;
		REGISTER NODEINST *ni;
		REGISTER INTBIG lx, hx, ly, hy, cx, cy, *newlist;
		static INTBIG xoff = 0, yoff = 0;
		char line[20];

		xoff += el_curlib->lambda[el_curtech->techindex] * 150;
		facet = getcurfacet();
		if (facet == NONODEPROTO) return;
		prim = getnodeproto("metal-1-node");
		for(mp = db_mpolylist; mp != NOMPOLY; mp = mp->nextmpoly)
		{
#ifdef DESIREDLAYER
			if (mp->layer != DESIREDLAYER) continue;
#endif
			lx = hx = mp->x[0];
			ly = hy = mp->y[0];
			for(i=1; i<mp->count; i++)
			{
				if (mp->x[i] < lx) lx = mp->x[i];
				if (mp->x[i] > hx) hx = mp->x[i];
				if (mp->y[i] < ly) ly = mp->y[i];
				if (mp->y[i] > hy) hy = mp->y[i];
			}
			cx = (lx+hx) / 2;   cy = (ly+hy) / 2;
			ni = newnodeinst(prim, lx+xoff, hx+xoff, ly+yoff, hy+yoff, 0, 0, facet);
			newlist = (INTBIG *)emalloc(mp->count * 2 * SIZEOFINTBIG, el_tempcluster);
			if (newlist == 0) return;
			for(i=0; i<mp->count; i++)
			{
				newlist[i*2] = mp->x[i] - cx;
				newlist[i*2+1] = mp->y[i] - cy;
			}
			(void)setvalkey((INTBIG)ni, VNODEINST, el_trace, (INTBIG)newlist,
				VINTEGER|VISARRAY|((mp->count*2)<<VLENGTHSH));
			endobjectchange((INTBIG)ni, VNODEINST);

			(void)initinfstr();
			addstringtoinfstr("Resulting poly on layer ");
			sprintf(line, "%ld", layer);
			addstringtoinfstr(line);
			addstringtoinfstr(" is");
			for(i=0; i<mp->count; i++)
			{
				addstringtoinfstr(" (");
				addstringtoinfstr(latoa(mp->x[i]));
				addstringtoinfstr(",");
				addstringtoinfstr(latoa(mp->y[i]));
				addstringtoinfstr(")");
			}
			ttyputmsg("%s", returninfstr());
		}
#ifdef SHOWTICKS
		for(mp = db_mpolylist; mp != NOMPOLY; mp = mp->nextmpoly)
		{
			if (mp == db_mpolylist)
			{
				lx = hx = mp->x[0];
				ly = hy = mp->y[0];
			}
			for(i=0; i<mp->count; i++)
			{
				if (mp->x[i] < lx) lx = mp->x[i];
				if (mp->x[i] > hx) hx = mp->x[i];
				if (mp->y[i] < ly) ly = mp->y[i];
				if (mp->y[i] > hy) hy = mp->y[i];
			}
		}
		for(mp = db_mpolylist; mp != NOMPOLY; mp = mp->nextmpoly)
		{
			REGISTER INTBIG j, lambda;
			extern NODEPROTO *gen_invispinprim;

			lambda = el_curlib->lambda[el_curtech->techindex];
			for(i=0; i<mp->count; i++)
			{
				/* see if the X value has already been done */
				for(j=0; j<i; j++) if (mp->x[j] == mp->x[i]) break;
				if (j < i) continue;
				for(omp = db_mpolylist; omp != mp; omp = omp->nextmpoly)
				{
					for(j=0; j<omp->count; j++)
						if (omp->x[j] == mp->x[i]) break;
					if (j < omp->count) break;
				}
				if (omp != mp) continue;

				/* show the X coordinate */
				ni = newnodeinst(gen_invispinprim, mp->x[i]+xoff, mp->x[i]+xoff,
					ly-lambda*2+yoff, ly-lambda*2+yoff,
						0, 0, facet);
				if (ni != NONODEINST)
				{
					REGISTER VARIABLE *var;
					var = setvalkey((INTBIG)ni, VNODEINST, el_node_name, (INTBIG)latoa(mp->x[i]),
						VSTRING|VDISPLAY);
					if (var != NOVARIABLE)
						var->textdescript = (var->textdescript & ~(VTSIZE|VTPOSITION)) |
							(VTPOSDOWN | (1<<VTSIZESH));
					endobjectchange((INTBIG)ni, VNODEINST);
				}
			}

			for(i=0; i<mp->count; i++)
			{
				/* see if the Y value has already been done */
				for(j=0; j<i; j++) if (mp->y[j] == mp->y[i]) break;
				if (j < i) continue;
				for(omp = db_mpolylist; omp != mp; omp = omp->nextmpoly)
				{
					for(j=0; j<omp->count; j++)
						if (omp->y[j] == mp->y[i]) break;
					if (j < omp->count) break;
				}
				if (omp != mp) continue;

				/* show the Y coordinate */
				ni = newnodeinst(gen_invispinprim, lx-lambda*2+xoff,
					lx-lambda*2+xoff, mp->y[i]+yoff, mp->y[i]+yoff, 0, 0, facet);
				if (ni != NONODEINST)
				{
					REGISTER VARIABLE *var;
					var = setvalkey((INTBIG)ni, VNODEINST, el_node_name, (INTBIG)latoa(mp->y[i]),
						VSTRING|VDISPLAY);
					if (var != NOVARIABLE)
						var->textdescript = (var->textdescript & ~(VTSIZE|VTPOSITION)) |
							(VTPOSLEFT | (1<<VTSIZESH));
					endobjectchange((INTBIG)ni, VNODEINST);
				}
			}
		}
#endif
	}
#endif
}

/*
 * Routine to finish polygon merging and report all of the merged polygons through
 * the callback routine "writepolygon()".  The routine is given 5 parameters for
 * each merged polygon:
 *   (1) the layer
 *   (2) the technology
 *   (3) the X coordinates
 *   (4) the Y coordinates
 *   (5) the number of coordinates
 */
void mergedone(void (*writepolygon)(INTSML, TECHNOLOGY*, INTBIG*, INTBIG*, INTSML))
{
	REGISTER MPOLY *mp;

	for(mp = db_mpolylist; mp != NOMPOLY; mp = mp->nextmpoly)
	{
		db_mergesimplifyandbbox(mp);
		(*writepolygon)((INTSML)mp->layer, mp->tech, mp->x, mp->y, (INTSML)mp->count);
	}
}

/*
 * Helper routine to combine two polygons "mp" (the first polygon) and "omp" (the
 * second polygon).  If they merge then the routine returns nonzero and places the
 * merged polygon in "omp".
 */
INTSML db_mergepoly(MPOLY *mp, MPOLY *omp)
{
	REGISTER INTBIG i, oi, startpol, startpt, intpoints;
	MPOLY *mps[2];

	/* reject immediately if they are on different layers */
	if (omp->tech != mp->tech || omp->layer != mp->layer) return(0);

	/* reject immediately if the bounding boxes don't intersect */
	if (omp->lx > mp->hx || omp->hx < mp->lx) return(0);
	if (omp->ly > mp->hy || omp->hy < mp->ly) return(0);

	/* reset intersection point information */
	for(i=0; i<mp->count; i++) mp->intersect[i] = 0;
	for(oi=0; oi<omp->count; oi++) omp->intersect[oi] = 0;

	/* create additional points on each polygon at the intersections */
	if (db_mergeaddintersectionpoints(mp, omp) != 0) return(0);
	if (db_mergeaddintersectionpoints(mp, mp) != 0) return(0);
	if (db_mergeaddintersectionpoints(omp, omp) != 0) return(0);

	/* if there are no intersection points, see if one polygon is inside the other */
	intpoints = 0;
	for(i=0; i<mp->count; i++) if (mp->intersect[i] == 2) intpoints++;
	for(oi=0; oi<omp->count; oi++) if (omp->intersect[oi] == 2) intpoints++;
	if (intpoints == 0)
	{
		/* figure out which polygon is smaller */
		if (areapoints(mp->count, mp->x, mp->y) <
			areapoints(omp->count, omp->x, omp->y))
		{
			/* if polygons don't intersect, stop */
			if (db_mergeisinside(mp->x[0], mp->y[0], omp) == 0) return(0);

			/* first polygon is smaller and covered by second */
			return(1);
		} else
		{
			/* if polygons don't intersect, stop */
			if (db_mergeisinside(omp->x[0], omp->y[0], mp) == 0) return(0);

			/* second polygon is smaller, copy first to second */
			if (db_mergecopypoly(mp, omp) != 0) return(0);
			db_mergesimplifyandbbox(omp);
			return(1);
		}
	}

	/* find a starting point (one with no intersections and not inside the other) */
	i = db_mergefindstartingpoint(mp, omp);
	if (i >= 0)
	{
		startpol = 0;
		startpt = i;
	} else
	{
		oi = db_mergefindstartingpoint(omp, mp);
		if (oi < 0)
		{
			/* all points are intersections: return larger polygon */
			if (areapoints(mp->count, mp->x, mp->y) >
				areapoints(omp->count, omp->x, omp->y))
			{
				/* copy first to second */
				if (db_mergecopypoly(mp, omp) != 0) return(0);
			}
			db_mergesimplifyandbbox(omp);
			return(1);
		}
		startpol = 1;
		startpt = oi;
	}

	/* walk around the polygons */
	for(i=0; i<mp->count; i++) mp->seen[i] = 0;
	for(oi=0; oi<omp->count; oi++) omp->seen[oi] = 0;
	mps[0] = mp;   mps[1] = omp;
	if (db_mergenewmp == NOMPOLY) db_mergenewmp = db_mergeallocpoly();
	db_mergenewmp->count = 0;
	if (db_mergewalkedge(mps[startpol], startpt, mps[1-startpol], db_mergenewmp) != 0)
		return(0);

	/* see if there are any points that didn't get seen and are not in the other polygon */
	if (db_mergeholemp == NOMPOLY) db_mergeholemp = db_mergeallocpoly();
	for(i=0; i<mp->count; i++)
	{
		if (mp->seen[i] != 0) continue;
#if 0
		if (mp->intersect[i] != 0) continue;
#endif
		if (db_mergeisinside(mp->x[i], mp->y[i], omp) != 0) continue;
		db_mergeholemp->count = 0;
		if (db_mergewalkedge(mp, i, omp, db_mergeholemp) != 0) return(0);
		if (db_mergeaddholetopoly(db_mergeholemp, db_mergenewmp) != 0) return(0);
	}
	for(oi=0; oi<omp->count; oi++)
	{
		if (omp->seen[oi] != 0) continue;
#if 0
		if (omp->intersect[oi] != 0) continue;
#endif
		if (db_mergeisinside(omp->x[oi], omp->y[oi], mp) != 0) continue;
		db_mergeholemp->count = 0;
		if (db_mergewalkedge(omp, oi, mp, db_mergeholemp) != 0) return(0);
		if (db_mergeaddholetopoly(db_mergeholemp, db_mergenewmp) != 0) return(0);
	}

	/* copy result back to second polygon */
	if (db_mergecopypoly(db_mergenewmp, omp) != 0) return(0);
	db_mergesimplifyandbbox(omp);
	return(1);
}

/*
 * Routine to find a good starting point on polygon "mp" (the other one is "omp").
 * Looks for points with no intersection marks, preferably on the outside edge,
 * and not inside of the other polygon
 */
INTBIG db_mergefindstartingpoint(MPOLY *mp, MPOLY *omp)
{
	REGISTER INTBIG i, lx, hx, ly, hy;

	lx = hx = mp->x[0];
	ly = hy = mp->y[0];
	for(i=1; i<mp->count; i++)
	{
		if (mp->x[i] < lx) lx = mp->x[i];
		if (mp->x[i] > hx) hx = mp->x[i];
		if (mp->y[i] < ly) ly = mp->y[i];
		if (mp->y[i] > hy) hy = mp->y[i];
	}

	/* look for points with no intersection, on edge, and not inside other */
	for(i=0; i<mp->count; i++)
	{
		if (mp->intersect[i] != 0) continue;
		if (mp->x[i] != lx && mp->x[i] != hx &&
			mp->y[i] != ly && mp->y[i] != hy) continue;
		if (db_mergeisinside(mp->x[i], mp->y[i], omp) != 0) continue;
		return(i);
	}

	/* accept points that aren't on the edge */
	for(i=0; i<mp->count; i++)
	{
		if (mp->intersect[i] != 0) continue;
		if (db_mergeisinside(mp->x[i], mp->y[i], omp) != 0) continue;
		return(i);
	}

	/* nothing found */
	return(-1);
}

/*
 * Helper routine to add hole polygon "holemp" to the main polygon "newmp".
 * Finds the closest points on the two and adds the points.
 * Returns nonzero on error.
 */
INTSML db_mergeaddholetopoly(MPOLY *holemp, MPOLY *newmp)
{
	REGISTER INTBIG j, k, dist, bestdist, hint, nint, nx, ny;
	INTBIG bestpos;

	for(j=0; j<holemp->count; j++)
	{
		for(k=0; k<newmp->count; k++)
		{
			dist = computedistance(holemp->x[j], holemp->y[j], newmp->x[k], newmp->y[k]);
			if ((j==0 && k==0) || dist < bestdist)
			{
				bestdist = dist;
				hint = j;
				nint = k;
			}
		}
	}
	bestpos = nint+1;
	nx = newmp->x[nint];   ny = newmp->y[nint];
	if (nx != holemp->x[hint] || ny != holemp->y[hint])
	{
		if (db_mergeinsertpoint(newmp, &bestpos, holemp->x[hint], holemp->y[hint]) != 0) return(1);
	}
	j = hint;
	for(;;)
	{
		j++;
		if (j >= holemp->count) j = 0;
		if (db_mergeinsertpoint(newmp, &bestpos, holemp->x[j], holemp->y[j]) != 0) return(1);
		if (j == hint) break;
	}
	if (nx != holemp->x[hint] || ny != holemp->y[hint])
	{
		if (db_mergeinsertpoint(newmp, &bestpos, nx, ny) != 0) return(1);
	}
	return(0);
}

/*
 * Helper routine to look for intersection points between the two polygons "mp" and "omp"
 * (which may be the same polygon, when looking for self-intersections).  The intersection
 * points are added to the polygon and marked (1 for self-intersection, 2 for intersection
 * with the other polygon).  Returns nonzero on error.
 */
INTSML db_mergeaddintersectionpoints(MPOLY *mp, MPOLY *omp)
{
	REGISTER INTBIG i, j, oi, done, fx, fy, tx, ty, ofx, ofy, otx, oty, last, olast,
		intvalue;
	INTBIG ix, iy;

	if (mp == omp) intvalue = 1; else intvalue = 2;
	done = 0;
	while (done == 0)
	{
		done = 1;
		for(i=0; i<mp->count; i++)
		{
			for(oi = 0; oi < omp->count; oi++)
			{
				/* get the line segment endpoints */
				if (i == 0) last = mp->count-1; else last = i-1;
				fx = mp->x[last];   fy = mp->y[last];
				tx = mp->x[i];      ty = mp->y[i];

				if (oi == 0) olast = omp->count-1; else olast = oi-1;
				ofx = omp->x[olast];   ofy = omp->y[olast];
				otx = omp->x[oi];      oty = omp->y[oi];

				if (mp == omp && (i == oi || last == oi || olast == i)) continue;

				/* see if they intersect */
				if (db_mergelinesintersect(fx,fy, tx,ty, ofx,ofy, otx,oty, &ix,&iy) == 0) continue;

				/* mark points that intersect */
				if (ix == fx && iy == fy) mp->intersect[last] = intvalue;
				if (ix == tx && iy == ty) mp->intersect[i] = intvalue;
				if (ix == ofx && iy == ofy) omp->intersect[olast] = intvalue;
				if (ix == otx && iy == oty) omp->intersect[oi] = intvalue;

				/* ignore if both polygons have this point */
				if ((ix == fx && iy == fy) || (ix == tx && iy == ty))
				{
					if ((ix == ofx && iy == ofy) || (ix == otx && iy == oty)) continue;
				}

				/* add intersection point to the first polygon */
				if ((ix != fx || iy != fy) && (ix != tx || iy != ty))
				{
					if (db_mergeensurespace(mp, mp->count+1) != 0) return(1);
					for(j = mp->count; j > i; j--)
					{
						mp->x[j] = mp->x[j-1];
						mp->y[j] = mp->y[j-1];
						mp->intersect[j] = mp->intersect[j-1];
					}
					mp->count++;
					mp->x[i] = ix;
					mp->y[i] = iy;
					mp->intersect[i] = intvalue;
				}

				/* add intersection point to other polygon */
				if ((ix != ofx || iy != ofy) && (ix != otx || iy != oty))
				{
					if (db_mergeensurespace(omp, omp->count+1) != 0) return(1);
					for(j = omp->count; j > oi; j--)
					{
						omp->x[j] = omp->x[j-1];
						omp->y[j] = omp->y[j-1];
						omp->intersect[j] = omp->intersect[j-1];
					}
					omp->count++;
					omp->x[oi] = ix;
					omp->y[oi] = iy;
					omp->intersect[oi] = intvalue;
				}

				done = 0;
				break;
			}
			if (done == 0) break;
		}
	}
	return(0);
}

/*
 * Helper routine to walk around the edge of polygon "mp", starting at point "i".  If it hits
 * polygon "omp", merge it in.  Resulting polygon is in "newmp".  Returns nonzero on error.
 */
INTSML db_mergewalkedge(MPOLY *mp, INTBIG i, MPOLY *omp, MPOLY *newmp)
{
	MPOLY *mps[2];
	REGISTER MPOLY *mp1, *mp2;
	REGISTER INTSML enterangle, leaveangle;
	REGISTER INTBIG startpol, startpt, last, next, pol, pt, unseenpt,
		unseenthis, smallestthisangle, smallestotherangle;
	INTBIG insert;
	static INTBIG seen = 0;

#ifdef DEBUGWALK
	ttyputmsg("Walking around edge, polygon 0 has %d points:", mp->count);
	for(pt=0; pt<mp->count; pt++) ttyputmsg("  (%s,%s)", latoa(mp->x[pt]), latoa(mp->y[pt]));
	ttyputmsg("Polygon 1 has %d points:", omp->count);
	for(pt=0; pt<omp->count; pt++) ttyputmsg("  (%s,%s)", latoa(omp->x[pt]), latoa(omp->y[pt]));
#endif
	seen++;
	if (seen == 0) seen++;
	mps[0] = mp;   mps[1] = omp;
	startpol = 0;   startpt = i;
	pol = startpol;   pt = startpt;
	insert = 0;
	for(;;)
	{
		/* include this point */
		mp1 = mps[pol];
		mp2 = mps[1-pol];
		mp1->seen[pt] = seen;
#ifdef DEBUGWALK
		ttyputmsg("  Add (%s,%s) from polygon %d", latoa(mps[pol]->x[pt]), latoa(mps[pol]->y[pt]), pol);
#endif
		if (db_mergeinsertpoint(newmp, &insert, mps[pol]->x[pt], mps[pol]->y[pt]) != 0)
			return(1);
		if (newmp->count == 1000)
		{
			ttyputerr(_("Warning: loop detected while merging polygons on layer %s"),
				layername(mp->tech, (INTSML)mp->layer));
			return(1);
		}

		/* see if there are intersection points on this or the other polygon */
		db_mergeintcount = 0;
		for(i=0; i<mp1->count; i++)
		{
			if (i == pt) continue;
			if (mp1->x[i] != mp1->x[pt] || mp1->y[i] != mp1->y[pt]) continue;
			db_mergeaddintersectionpoint(pol, i);
		}
		for(i=0; i<mp2->count; i++)
		{
			if (mp2->x[i] != mp1->x[pt] || mp2->y[i] != mp1->y[pt]) continue;
			db_mergeaddintersectionpoint(1-pol, i);
		}

		/* if point not on other polygon or elsewhere on this, keep walking around edge */
		if (db_mergeintcount == 0)
		{
#ifdef DEBUGWALK
			ttyputmsg("      Not an intersection point");
#endif
			pt++;
			if (pt >= mp1->count) pt = 0;
			if (pol == startpol && pt == startpt) break;
			continue;
		}

		/* intersection point: figure out what to do */
		if (pt == 0) last = mp1->count-1; else last = pt-1;
		enterangle = figureangle(mp1->x[pt], mp1->y[pt], mp1->x[last], mp1->y[last]);

		if (pt == mp1->count-1) next = 0; else next = pt+1;
		leaveangle = figureangle(mp1->x[pt], mp1->y[pt], mp1->x[next], mp1->y[next]);
		if (leaveangle > enterangle) leaveangle -= 3600;
		leaveangle = enterangle - leaveangle;

#ifdef DEBUGWALK
		ttyputmsg("      Intersection point!  enter at %d, leave at %d", enterangle/10, leaveangle/10);
#endif
		for(i=0; i<db_mergeintcount; i++)
		{
			if (db_mergeintpt[i] == mps[db_mergeintpol[i]]->count-1) next = 0; else next = db_mergeintpt[i]+1;
			db_mergeintleaveangle[i] = figureangle(mp1->x[pt], mp1->y[pt],
				mps[db_mergeintpol[i]]->x[next], mps[db_mergeintpol[i]]->y[next]);
			if (db_mergeintleaveangle[i] > enterangle) db_mergeintleaveangle[i] -= 3600;
			db_mergeintleaveangle[i] = enterangle - db_mergeintleaveangle[i];
			if (db_mergeintleaveangle[i] == 0) db_mergeintleaveangle[i] = 7200;
#ifdef DEBUGWALK
			ttyputmsg("        Point on %s polygon goes to (%s,%s), angle=%d",
				(db_mergeintpol[i] == pol ? "this" : "other"), latoa(mps[db_mergeintpol[i]]->x[next]),
				latoa(mps[db_mergeintpol[i]]->y[next]), db_mergeintleaveangle[i]/10);
#endif
		}

		smallestthisangle = 7200;
		unseenthis = -1;
		for(i=0; i<db_mergeintcount; i++)
		{
			if (db_mergeintpol[i] != pol) continue;
			if (db_mergeintleaveangle[i] > smallestthisangle) continue;
			smallestthisangle = db_mergeintleaveangle[i];
			unseenthis = db_mergeintpt[i];
		}

		smallestotherangle = 7200;
		unseenpt = -1;
		for(i=0; i<db_mergeintcount; i++)
		{
			if (db_mergeintpol[i] == pol) continue;
			if (db_mergeintleaveangle[i] > smallestotherangle) continue;
			smallestotherangle = db_mergeintleaveangle[i];
			unseenpt = db_mergeintpt[i];
		}
		if (smallestthisangle < leaveangle && smallestthisangle < smallestotherangle)
		{
			/* staying on the same polygon but jumping to a new point */
#ifdef DEBUGWALK
			ttyputmsg("        Stay on same polygon, jump to new point");
#endif
			pt = unseenthis;
			if (pol == startpol && pt == startpt) break;
			mp1->seen[pt] = seen;

			/* advance to the next point */
			pt++;
			if (pt >= mp1->count) pt = 0;
			if (pol == startpol && pt == startpt) break;
			continue;
		}
		if (leaveangle <= smallestthisangle && leaveangle <= smallestotherangle)
		{
			/* stay on the current polygon */
#ifdef DEBUGWALK
			ttyputmsg("        Stay on same polygon");
#endif
			pt++;
			if (pt >= mp1->count) pt = 0;
		} else
		{
			/* switch to the other polygon */
#ifdef DEBUGWALK
			ttyputmsg("        Switch to other polygon");
#endif
			pt = unseenpt;
			pol = 1 - pol;
			if (pol == startpol && pt == startpt) break;
			mp2->seen[pt] = seen;

			/* advance to the next point */
			pt++;
			if (pt >= mp2->count) pt = 0;
		}
		if (pol == startpol && pt == startpt) break;
	}
	return(0);
}

/*
 * Helper routine to insert the point (x, y) at "insert" in polygon "mp".
 * Returns nonzero on error.
 */
INTSML db_mergeinsertpoint(MPOLY *mp, INTBIG *insert, INTBIG x, INTBIG y)
{
	REGISTER INTBIG i;

	if (db_mergeensurespace(mp, mp->count+1) != 0) return(1);
	if (*insert <= 0 || mp->x[*insert-1] != x || mp->y[*insert-1] != y)
	{
		for(i=mp->count; i > *insert; i--)
		{
			mp->x[i] = mp->x[i-1];
			mp->y[i] = mp->y[i-1];
		}
		mp->x[*insert] = x;
		mp->y[*insert] = y;
		mp->count++;
		(*insert)++;
	}
	return(0);
}

/*
 * Routine to add polygon "polygon" and point "point" to the global arrays of 
 * intersection points.
 */
void db_mergeaddintersectionpoint(INTBIG polygon, INTBIG point)
{
	REGISTER INTBIG i, newtotal, *newpol, *newpt, *newangle;

	if (db_mergeintcount >= db_mergeinttotal)
	{
		newtotal = db_mergeinttotal * 2;
		if (newtotal <= 0) newtotal = 10;
		newpol = (INTBIG *)emalloc(newtotal * SIZEOFINTBIG, db_cluster);
		if (newpol == 0) return;
		newpt = (INTBIG *)emalloc(newtotal * SIZEOFINTBIG, db_cluster);
		if (newpt == 0) return;
		newangle = (INTBIG *)emalloc(newtotal * SIZEOFINTBIG, db_cluster);
		if (newangle == 0) return;
		for(i=0; i<db_mergeintcount; i++)
		{
			newpol[i] = db_mergeintpol[i];
			newpt[i] = db_mergeintpt[i];
			newangle[i] = db_mergeintleaveangle[i];
		}
		if (db_mergeinttotal > 0)
		{
			efree((char *)db_mergeintpol);
			efree((char *)db_mergeintpt);
			efree((char *)db_mergeintleaveangle);
		}
		db_mergeintpol = newpol;
		db_mergeintpt = newpt;
		db_mergeintleaveangle = newangle;
		db_mergeinttotal = newtotal;
	}
	db_mergeintpol[db_mergeintcount] = polygon;
	db_mergeintpt[db_mergeintcount] = point;
	db_mergeintcount++;
}

/*
 * Helper routine to determine whether the line segement (fx,fy)-(tx,ty)
 * intersects the line segment (ofx,ofy)-(otx,oty).  If it does, the intersection
 * point is placed in (ix,iy) and the routine returns nonzero.
 */
INTSML db_mergelinesintersect(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty,
	INTBIG ofx, INTBIG ofy, INTBIG otx, INTBIG oty, INTBIG *ix, INTBIG *iy)
{
	double fa1, fb1, fc1, fa2, fb2, fc2, fswap, fiy, ang, oang, hang, ohang;

	/* quick check to see if lines don't intersect */
	if (mini(fx,tx) > maxi(ofx,otx)) return(0);
	if (maxi(fx,tx) < mini(ofx,otx)) return(0);
	if (mini(fy,ty) > maxi(ofy,oty)) return(0);
	if (maxi(fy,ty) < mini(ofy,oty)) return(0);

	/* determine the angle of the lines */
	ang = atan2(ty-fy, tx-fx);
	if (ang < 0.0) ang += EPI*2.0;
	oang = atan2(oty-ofy, otx-ofx);
	if (oang < 0.0) oang += EPI*2.0;

	/* special case if they are at the same angle */
	hang = fmod(ang, EPI);
	ohang = fmod(oang, EPI);
	if (fabs(hang - ohang) < 0.0001)
	{
		/* see if the lines are coincident */
		return(0);
	}

	fa1 = sin(ang);   fb1 = -cos(ang);
	fc1 = -fa1 * ((double)fx) - fb1 * ((double)fy);
	fa2 = sin(oang);   fb2 = -cos(oang);
	fc2 = -fa2 * ((double)ofx) - fb2 * ((double)ofy);
	if (fabs(fa1) < fabs(fa2))
	{
		fswap = fa1;   fa1 = fa2;   fa2 = fswap;
		fswap = fb1;   fb1 = fb2;   fb2 = fswap;
		fswap = fc1;   fc1 = fc2;   fc2 = fswap;
	}
	fiy = (fa2 * fc1 / fa1 - fc2) / (fb2 - fa2*fb1/fa1);
	*iy = rounddouble(fiy);
	*ix = rounddouble((-fb1 * fiy - fc1) / fa1);

	/* make sure the intersection is on the lines */
	if (*ix < mini(fx,tx) || *ix < mini(ofx,otx)) return(0);
	if (*ix > maxi(fx,tx) || *ix > maxi(ofx,otx)) return(0);
	if (*iy < mini(fy,ty) || *iy < mini(ofy,oty)) return(0);
	if (*iy > maxi(fy,ty) || *iy > maxi(ofy,oty)) return(0);
	return(1);
}

/*
 * Helper routine to simplify polygon "mp", removing redundant and colinear points,
 * and recomputing its bounding box.
 */
void db_mergesimplifyandbbox(MPOLY *mp)
{
	REGISTER INTBIG i, j, prev, next;

	/* remove redundant points */
	for(i=1; i<mp->count; i++)
	{
		if (mp->x[i] == mp->x[i-1] && mp->y[i] == mp->y[i-1])
		{
			for(j=i; j<mp->count; j++)
			{
				mp->x[j-1] = mp->x[j];
				mp->y[j-1] = mp->y[j];
			}
			mp->count--;
			i--;
			continue;
		}
	}
	while (mp->count >= 2 && mp->x[0] == mp->x[mp->count-1] && mp->y[0] == mp->y[mp->count-1])
		mp->count--;

	/* remove colinear points (only manhattan!!!) */
	for(i=0; i<mp->count; i++)
	{
		if (i == 0) prev = mp->count-1; else prev = i-1;
		if (i == mp->count-1) next = 0; else next = i+1;
		if ((mp->x[prev] == mp->x[i] && mp->x[i] == mp->x[next]) ||
			(mp->y[prev] == mp->y[i] && mp->y[i] == mp->y[next]))
		{
			for(j=i+1; j<mp->count; j++)
			{
				mp->x[j-1] = mp->x[j];
				mp->y[j-1] = mp->y[j];
			}
			mp->count--;
			i--;
			continue;
		}
	}

	/* get bounding box */
	mp->lx = mp->hx = mp->x[0];
	mp->ly = mp->hy = mp->y[0];
	for(i=1; i<mp->count; i++)
	{
		if (mp->x[i] < mp->lx) mp->lx = mp->x[i];
		if (mp->x[i] > mp->hx) mp->hx = mp->x[i];
		if (mp->y[i] < mp->ly) mp->ly = mp->y[i];
		if (mp->y[i] > mp->hy) mp->hy = mp->y[i];
	}
}

/*
 * Helper routine to return nonzero if poing (x,y) is inside of polygon "mp".
 */
INTSML db_mergeisinside(INTBIG x, INTBIG y, MPOLY *mp)
{
	REGISTER INTSML i, ang, lastp, tang, thisp;

	/* general polygon containment by summing angles to vertices */
	ang = 0;
	if (x == mp->x[mp->count-1] && y == mp->y[mp->count-1]) return(1);
	lastp = figureangle(x, y, mp->x[mp->count-1], mp->y[mp->count-1]);
	for(i=0; i<mp->count; i++)
	{
		if (x == mp->x[i] && y == mp->y[i]) return(1);
		thisp = figureangle(x, y, mp->x[i], mp->y[i]);
		tang = lastp - thisp;
		if (tang < -1800) tang += 3600;
		if (tang > 1800) tang -= 3600;
		ang += tang;
		lastp = thisp;
	}
	if (abs(ang) <= mp->count) return(0);
	return(1);
}

/*
 * Helper routine to copy polygon "smp" to polygon "dmp".
 * Returns nonzero on error.
 */
INTSML db_mergecopypoly(MPOLY *smp, MPOLY *dmp)
{
	REGISTER INTBIG i;

	if (db_mergeensurespace(dmp, smp->count) != 0) return(1);
	for(i=0; i<smp->count; i++)
	{
		dmp->x[i] = smp->x[i];
		dmp->y[i] = smp->y[i];
	}
	dmp->count = smp->count;
	return(0);
}

/*
 * Helper routine to make sure that polygon "mp" has room for "count" points.
 * Returns nonzero on error.
 */
INTSML db_mergeensurespace(MPOLY *mp, INTBIG count)
{
	REGISTER INTBIG i, *newx, *newy, *newseen, *newintersect, newlimit;

	if (count <= mp->limit) return(0);
	newlimit = mp->limit * 2;
	if (newlimit < count) newlimit = count+4;
	newx = (INTBIG *)emalloc(newlimit * SIZEOFINTBIG, db_cluster);
	if (newx == 0) return(1);
	newy = (INTBIG *)emalloc(newlimit * SIZEOFINTBIG, db_cluster);
	if (newy == 0) return(1);
	newseen = (INTBIG *)emalloc(newlimit * SIZEOFINTBIG, db_cluster);
	if (newseen == 0) return(1);
	newintersect = (INTBIG *)emalloc(newlimit * SIZEOFINTBIG, db_cluster);
	if (newintersect == 0) return(1);
	for(i=0; i<mp->count; i++)
	{
		newx[i] = mp->x[i];
		newy[i] = mp->y[i];
		newseen[i] = mp->seen[i];
		newintersect[i] = mp->intersect[i];
	}
	if (mp->limit > 0)
	{
		efree((char *)mp->x);
		efree((char *)mp->y);
		efree((char *)mp->seen);
		efree((char *)mp->intersect);
	}
	mp->limit = newlimit;
	mp->x = newx;
	mp->y = newy;
	mp->seen = newseen;
	mp->intersect = newintersect;
	return(0);
}

/*
 * Helper routine to allocate a new polygon.
 * Returns NOMPOLY on error.
 */
MPOLY *db_mergeallocpoly(void)
{
	REGISTER MPOLY *mp;

	if (db_mpolyfree == NOMPOLY)
	{
		mp = (MPOLY *)emalloc(sizeof (MPOLY), db_cluster);
		if (mp == 0) return(NOMPOLY);
		mp->limit = 0;
	} else
	{
		mp = db_mpolyfree;
		db_mpolyfree = mp->nextmpoly;
	}
	mp->count = 0;
	return(mp);
}

/*
 * Helper routine to free polygon "mp".
 */
void db_mergefreepoly(MPOLY *mp)
{
	mp->nextmpoly = db_mpolyfree;
	db_mpolyfree = mp;
}
