static char *SccsId = "@(#)enumPair.c 4.14 (TU-Delft) 05/14/93";
/**********************************************************

Name/Version      : space/4.14

Language          : C
Operating system  : UNIX SYSTEM V
Host machine      : HP9000

Author(s)         : A.J. van Genderen
		  : N.P. van der Meijs
Creation date     : 15-Mar-1988
Modified by       :
Modification date :


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 786234

        COPYRIGHT (C) 1988. All rights reserved.
**********************************************************/
#include <math.h>
#include <stdio.h>
#include "include/config.h"
#include "include/type.h"
#include "aux/aux.h"
#include "extract/define.h"
#include "extract/extern.h"
#include "lump/define.h"
#include "lump/lump.h"

static tileBoundary_t * bdr;

/* enumPair (tile, newerTile, edgeOrien)
 *
 * will update the network description for the influence
 * of the boundary between 'tile' and 'newerTile'.
 *
 * actions performed :
 *
 * - For both 'tile' and 'newerTile' a list of recognized
 *   elements is fetched.
 *
 * - If 'newerTile' is passed to enumPair for the first time
 *   resistance and transistor elements are investigated to
 *   allocate appropriate subnodes and subtors for newerTile.
 *   When resistance extraction is performed, a point
 *   will also be allocated for 'newerTile'.
 *
 * - Joins are made between subnodes and subtors of the
 *   same type that are present in both 'tile' and 
 *   'newerTile'.
 *
 * - Terminal names are assigned to the appropriate subnodes.
 *   When resistances are extracted a point is placed for
 *   each terminal position.
 *
 * - When a transistor is present in one tile and a 
 *   transistor of the same type is not present in the
 *   other tile, the transistor edge is updated.
 *
 * - Then the edge capacitances are calculated.
 *   For both 'tile' and 'newerTile' the list of recognized
 *   elements is searched for capacitor elements that have
 *   one pin connection layer that is an edge layer and one
 *   pin connection layer that is a a surface layer.
 *   Capacitances are calculated and assigned between the 
 *   subnodes of the two pin connection layers.
 *
 */

void enumPair (tile, newerTile, edgeOrien)
tile_t * tile;
tile_t * newerTile;
char edgeOrien;
{
    register int cx;
    register int i;
    register elemDef_t ** elem;
    static int * subnodes = NULL;

    int fromNewer;
    terminal_t * term;
    tileBoundary_t helpBdr;
    double blen = -1.0;

    terminal_t * findNextTerm ();
    elemDef_t ** recognizeElements ();
    tileBoundary_t * giveBoundary ();
    double boundaryLength ();
    void updateEdgeCap ();


    if (! newerTile -> known) {
	newerTile -> cnt = ++tileCnt;
    }

    if (! HasConduc (tile) && ! HasConduc (newerTile)) {
	if (! newerTile -> known)
	    newerTile -> known = -1;
	return;
    }

    bdr = NULL;

    currX = tile -> xr;
    if (tile -> br > newerTile -> bl)
	currY = newerTile -> bl;
    else
	currY = tile -> br;


    elem = NULL;

    if (HasConduc (newerTile)) {

	if (! newerTile -> known) {

	    if (subnodes == NULL)
		subnodes = NEW (int, nrOfExtConductors);

	    for (i = 0; i < nrOfExtConductors; i++)
		subnodes[i] = 0;

	    elem = RecogE (newerTile -> color, tile -> color);
	
	    for (i = 0; elem[i]; i++) {

		switch (elem[i] -> type) {

		    case RESELEM:

			cx = elem[i] -> s.res.con;

                        if (subnodes[cx] == 0) {

			    newerTile -> cons[cx] = CONS (newerTile, cx);

			    if (tile -> cons[cx]) {
				subnodeCopy (tile -> cons[cx], 
					     newerTile -> cons[cx]);
			    }
			    else {
				subnodeNew (newerTile -> cons[cx], 'i');
				newerTile -> cons[cx] -> node -> mask = 
							   conductorMask[cx];
				newerTile -> cons[cx] -> node -> x = 
							   newerTile -> xl;
				newerTile -> cons[cx] -> node -> y = 
							   newerTile -> bl;
			    }


			    subnodes[cx] = 1;
			}

			break;

		    case TORELEM:

			if (tile -> tor
			&& tile -> tor -> type == elem[i]) {
			    subtorCopy (tile, newerTile);
			}
			else {
			    subtorNew (newerTile, elem[i] -> name);
			    newerTile -> tor -> type = elem[i];
			    newerTile -> tor -> xl = newerTile -> xl;
			    newerTile -> tor -> yb = newerTile -> bl;
			}

			break;
			
		    /*
		    case CONTELEM:
		    case SURFCAPELEM:
		    case EDGECAPELEM:
		    case LATCAPELEM:
		    */
                        /* do nothing */
		}
	    }

            if (newerTile -> known == 0)
		newerTile -> known = -1;
	}
	else {

            /* Make joins between subnodes in 'tile' and subnodes
	     * in 'newerTile'.
	     */

	    for (cx = 0; cx < nrOfExtConductors; cx++) {
		if (tile -> cons[cx] && newerTile -> cons[cx]) {
		    subnodeJoin (tile -> cons[cx], newerTile -> cons[cx]);
		}
	    }

            /* If 'tile' and 'newerTile' both have a transistor of 
	     * the same type, join them.
	     */

	    if (tile -> tor && newerTile -> tor
	    && tile -> tor -> type == newerTile -> tor -> type) {
		subtorJoin (tile, newerTile);
	    }
	}
    }
    else if (! newerTile -> known) {
	newerTile -> known = -1;
    }

    /* Assign terminal names to the appropriate subnodes. */

    if (edgeOrien == 'v' && (tile -> terms || newerTile -> terms)) {
	while ((term = findNextTerm (tile, newerTile, &fromNewer)) != NULL) {
	
	    /* only for edgeOrien == 'v' terminals are found */

	    if (newerTile -> cons[ term -> conductor ] == NULL) {
		missingTermCon (term);
	    }

	    nameAdd (newerTile -> cons[ term -> conductor ], term);

	}
    }

    /* Update transistor edges */

    if (newerTile -> tor)
	updateTorEdge (newerTile, tile, tile -> cons, 
		       (subnode_t **) NULL, edgeOrien, 0);

    if (tile -> tor)
	updateTorEdge (tile, newerTile, newerTile -> cons,
		       (subnode_t **) NULL, edgeOrien, 1);

    /* Update edge capacitances */

    if (optCap && CanHaveEdgeCap (tile, newerTile)
	&& (blen = boundaryLength (tile, newerTile, edgeOrien)) > 0.0) {

	if (HasConduc (tile)) {

	    if (elem == NULL)
		elem = RecogE (newerTile -> color, tile -> color);

	    updateEdgeCap (elem, newerTile, tile, blen, edgeOrien, 0);
	}

	if (HasConduc (newerTile)) {

	    elem = RecogE (tile -> color, newerTile -> color);

	    updateEdgeCap (elem, tile, newerTile, blen, edgeOrien, 1);

	}
    }


}




tileBoundary_t * giveBoundary (tile, newerTile, edgeOrien)
tile_t * tile;
tile_t * newerTile;
char edgeOrien;
{
    static tileBoundary_t boundary;

    switch (edgeOrien) {
	case 'v' :
	    boundary.x1 = boundary.x2 = tile -> xr;
	    boundary.y2 = Min (tile -> tr, newerTile -> tl);
	    boundary.y1 = Max (tile -> br, newerTile -> bl);
	    boundary.length = (double)(boundary.y2 - boundary.y1);
	    break;
	case 'h' :
	    if (tile -> xr < newerTile -> xr) {
		boundary.x2 = tile -> xr;
		boundary.y2 = tile -> tr;
	    }
	    else {
		boundary.x2 = newerTile -> xr;
		boundary.y2 = newerTile -> br;
	    }
	    if (tile -> xl > newerTile -> xl) {
		boundary.x1 = tile -> xl;
		boundary.y1 = tile -> tl;
	    }
	    else {
		boundary.x1 = newerTile -> xl;
		boundary.y1 = newerTile -> bl;
	    }
	    if (boundary.y1 == boundary.y2)
		boundary.length = (double)(boundary.x2
			                   - boundary.x1);
	    else
		boundary.length = sqrt (Sqr((double)(boundary.x2
				                      - boundary.x1))
				           + Sqr((double)(boundary.y2
					                  - boundary.y1)));
	    break;
    }
    boundary.length = boundary.length * meters;

    return (&boundary);
}


terminal_t * findNextTerm (tileA, tileB, fromB)
tile_t * tileA;
tile_t * tileB;
int * fromB;
{
    terminal_t * term;
    terminal_t * termA;
    terminal_t * termB;
    terminal_t * prevA;
    terminal_t * prevB;

    prevA = NULL;
    termA = tileA -> terms;
    if (termA) {
	while (termA -> next != NULL) {
	    prevA = termA;
	    termA = termA -> next;
	}
    }
    prevB = NULL;
    termB = tileB -> terms;
    if (termB) {
	while (termB -> next != NULL) {
	    prevB = termB;
	    termB = termB -> next;
	}
    }

    if (termA == NULL && termB == NULL) {
	term = NULL;
    }
    else if (termA == NULL) {
	term = termB;
	if (prevB)
	    prevB -> next = NULL;
	else
	    tileB -> terms = NULL;
	*fromB = 1;
    }
    else if (termB == NULL) {
	term = termA;
	if (prevA)
	    prevA -> next = NULL;
	else
	    tileA -> terms = NULL;
	*fromB = 0;
    }
    else {
	if (termA -> y <= termB -> y) {
	    term = termA;
	    if (prevA)
		prevA -> next = NULL;
	    else
		tileA -> terms = NULL;
	    *fromB = 0;
	}
	else {
	    term = termB;
	    if (prevB)
		prevB -> next = NULL;
	    else
		tileB -> terms = NULL;
	    *fromB = 1;
	}
    }

    if (term) {
	ASSERT (term -> x == tileA -> xr
	        || term -> x == tileB -> xl);
    }

    return (term);
}


updateTorEdge (tile, adjTile, adjCons, adjPointStart, 
	       edgeOrien, adjIsNewer)
tile_t * tile;
tile_t * adjTile;
nodePoint_t * adjPointStart;
subnode_t ** adjCons;
char edgeOrien;
int adjIsNewer;
{
    int cx;
    subnode_t * sn;
    nodePoint_t * point;
    node_t * tn;

    tileBoundary_t * giveBoundary ();

    if (bdr == NULL) {
	if (adjIsNewer)
	    bdr = giveBoundary (tile, adjTile, edgeOrien);
	else
	    bdr = giveBoundary (adjTile, tile, edgeOrien);
    }

    if (tile -> tor != NULL
    && (adjTile -> tor == NULL
	|| adjTile -> tor -> type != tile -> tor -> type)) {

	tile -> tor -> totPerimeter += bdr -> length;

	cx = tile -> tor -> type -> s.tor.dsCon;

	if (adjCons != NULL
	&& (sn = adjCons[cx]) != NULL) {

	    tile -> tor -> dsPerimeter += bdr -> length;

            if (optDsConJoin)
		portAdd (sn, tile, 'd');

	    torBoundary (tile -> tor, bdr -> x1, bdr -> y1, 
			 bdr -> x2, bdr -> y2, bdr -> length, sn);
	    
	    if (optAccWL) {
		tn = tile -> rbPoints -> 
		     cons[ tile -> tor -> type -> s.tor.cx] -> node;

                ASSERT (tn != NULL);

                tn -> type = 't';

		if (tn -> pin == NULL) {
		    tn -> pin = NEW (subnode_t, 1);
		    subnodeCopy (sn, tn -> pin);
		}
	    }

	}
    }
}


void updateEdgeCap (elem, tile, adjTile, blen, edgeOrien, adjIsNewer)
elemDef_t ** elem;
tile_t * tile;
tile_t * adjTile;
double blen;
char edgeOrien;
int adjIsNewer;
{
    register int i;
    register capElemDef_t * ced;
    register subnode_t * subnA;
    register subnode_t * subnB;
    register subnode_t ** cons;
    double cap;
    tileBoundary_t * giveBoundary ();

    if (optNoCore && (HasCore (tile) || HasCore (adjTile)))
	return;

    for (i = 0; elem[i] != NULL; i++) {

	if (elem[i] -> type == EDGECAPELEM) {

	    ced = &(elem[i] -> s.cap);

	    if (ced -> nCon < 0) {
		/* this is a ground capacitance, simple */
		if ( ! (subnA = adjTile -> cons [ced -> pCon]) ) {
		    if (bdr == NULL) {
			if (adjIsNewer)
			    bdr = giveBoundary (tile, adjTile, edgeOrien);
			else
			    bdr = giveBoundary (adjTile, tile, edgeOrien);
		    }
		    missingCon (ced -> pMask, ced -> pOccurrence,
				tile, adjTile, (tile_t *) NULL,
				elem[i], bdr -> x1, bdr -> y1);
		}
		GndCapAdd (subnA, ced -> val * blen);
	    }
	    else {
		if (ced -> pOccurrence == EDGE)
		    cons = adjTile -> cons;
		else if (ced -> pOccurrence == SURFACE)
		    cons = tile -> cons;

		if ( ! (subnA = cons [ced -> pCon]) ) {
		    if (bdr == NULL) {
			if (adjIsNewer)
			    bdr = giveBoundary (tile, adjTile, edgeOrien);
			else
			    bdr = giveBoundary (adjTile, tile, edgeOrien);
		    }
		    missingCon (ced -> pMask, ced -> pOccurrence,
				tile, adjTile, (tile_t *) NULL,
				elem[i], bdr -> x1, bdr -> y1);
		}

		if (ced -> nOccurrence == EDGE)
		    cons = adjTile -> cons;
		else if (ced -> nOccurrence == SURFACE)
		    cons = tile -> cons;
		if ( ! (subnB = cons [ced -> nCon]) ) {
		    if (bdr == NULL) {
			if (adjIsNewer)
			    bdr = giveBoundary (tile, adjTile, edgeOrien);
			else
			    bdr = giveBoundary (adjTile, tile, edgeOrien);
		    }
		    missingCon (ced -> nMask, ced -> nOccurrence,
				tile, adjTile, (tile_t *) NULL,
				elem[i], bdr -> x1, bdr -> y1);
		}

		cap = ced -> val * blen;
		{
		    GndCapAdd (subnA, cap);
		    GndCapAdd (subnB, cap);
		}
	    }
	}
    }
}


missingCon (mask, occurrence, tile, eTile, oeTile, el, x, y)
int mask;
int occurrence;
tile_t * tile; 
tile_t * eTile; 
tile_t * oeTile; 
elemDef_t * el;
coor_t x;
coor_t y;
{
    char buf[256];
    int i;

    if (occurrence == EDGE)
	sprintf (buf, "conductor '-%s'", masktable[mask].name);
    else if (occurrence == SURFACE)
	sprintf (buf, "conductor '%s'", masktable[mask].name);
    else if (occurrence == OTHEREDGE)
	sprintf (buf, "conductor '=%s'", masktable[mask].name);

    sprintf (buf + strlen (buf), 
    " missing for element '%s' at position (%d, %d)\n",
    el -> name, x / currScale, y / currScale);

    sprintf (buf + strlen (buf), "  masks present :");

    if (tile != NULL) {
	for (i = 0; i < nrOfMasks; i++) {
	     if (masktable[i].color & tile -> color) {
		 sprintf (buf + strlen (buf), " %s", masktable[i].name);
	     }
	}
    }

    if (eTile != NULL) {
	for (i = 0; i < nrOfMasks; i++) {
	     if (masktable[i].color & eTile -> color) {
		 sprintf (buf + strlen (buf), " -%s", masktable[i].name);
	     }
	}
    }

    if (oeTile != NULL) {
	for (i = 0; i < nrOfMasks; i++) {
	     if (masktable[i].color & oeTile -> color) {
		 sprintf (buf + strlen (buf), " =%s", masktable[i].name);
	     }
	}
    }

    say (buf);

    die ();
}

missingTermCon (term)
terminal_t *term;
{
    int i;

    for (i = 0; i < nrOfMasks; i++) {
	if (masktable[i].conductor == term -> conductor)
	    break;
    }

    say ("conductor '%s' missing for terminal '%s' at position (%d, %d)",
	 masktable[i].name, term -> termName, 
	 term -> x / currScale, term -> y / currScale);

    die ();
}

double
boundaryLength (tile, newerTile, edgeOrien)
tile_t * tile;
tile_t * newerTile;
char edgeOrien;
{
    /* Returns the length of a boundary */

    register coor_t dx, dy;

    if (edgeOrien == 'v') {
	return (meters * ( Min (tile -> tr, newerTile -> tl)
			  - Max (tile -> br, newerTile -> bl)));
    }
    else {
	if (tile -> xr < newerTile -> xr) {
	    dx = tile -> xr;
	    dy = tile -> tr;
	}
	else {
	    dx = newerTile -> xr;
	    dy = newerTile -> br;
	}
	if (tile -> xl > newerTile -> xl) {
	    dx -= tile -> xl;
	    dy -= tile -> tl;
	}
	else {
	    dx -= newerTile -> xl;
	    dy -= newerTile -> bl;
	}
	if (dy==0)
	    return (dx * meters);
	else
	    return (meters * sqrt ((double) (dx * dx + dy * dy)));
    }
}

