/*
 * EFhier.c -
 *
 * Procedures for traversing the hierarchical representation
 * of a circuit.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/extflat/EFhier.c,v 1.5 2010/12/16 18:59:03 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <math.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "utils/hash.h"
#include "utils/malloc.h"
#include "utils/utils.h"
#include "extflat/extflat.h"
#include "extflat/EFint.h"
#include "textio/textio.h"

/*
 * ----------------------------------------------------------------------------
 *
 * efHierSrUses --
 *
 * Visit all the children of hc->hc_use->use_def, keeping the transform
 * to flat coordinates and the hierarchical path from the root up to date.
 * For each child, calls the function 'func', which should be of the
 * following form:
 *
 *	int
 *	(*func)(hc, cdata)
 *	    HierContext *hc;
 *	    ClientData cdata;
 *	{
 *	}
 *
 * This procedure should return 0 normally, or 1 to abort the search.
 *
 * Hierarchical names:
 *	The current hierarchical prefix down to this point is given by the
 *	the HierName pointed to by hc->hc_hierName.  To construct a full
 *	hierarchical name from a name local to this def, we prepend a
 *	newly allocated HierName component to hc->hc_hierName.
 *
 * Results:
 *	Returns 0 if completed successfully, 1 if aborted.
 *
 * Side effects:
 *	Whatever (*func)() does.
 *
 * ----------------------------------------------------------------------------
 */

int
efHierSrUses(
    HierContext *hc,
    int (*func)(),
    ClientData cdata)
{
    int xlo, xhi, ylo, yhi, xbase, ybase, xsep, ysep;
    HierContext newhc;
    Transform t;
    Use *u;
    HashSearch hs;
    HashEntry *he;

    HashStartSearch(&hs);
    while ((he = HashNext(&hc->hc_use->use_def->def_uses, &hs)))
    {
	u = (Use *)HashGetValue(he);
	newhc.hc_use = u;
	if (!IsArray(u))
	{
	    newhc.hc_hierName = efHNFromUse(&newhc, hc->hc_hierName);
	    GeoTransTrans(&u->use_trans, &hc->hc_trans, &newhc.hc_trans);
	    if ((*func)(&newhc, cdata))
		return (1);
	    continue;
	}

	/* Set up for iterating over all array elements */
	if (u->use_xlo <= u->use_xhi)
	    xlo = u->use_xlo, xhi = u->use_xhi, xsep = u->use_xsep;
	else
	    xlo = u->use_xhi, xhi = u->use_xlo, xsep = -u->use_xsep;
	if (u->use_ylo <= u->use_yhi)
	    ylo = u->use_ylo, yhi = u->use_yhi, ysep = u->use_ysep;
	else
	    ylo = u->use_yhi, yhi = u->use_ylo, ysep = -u->use_ysep;

	GeoTransTrans(&u->use_trans, &hc->hc_trans, &t);
	for (newhc.hc_x = xlo; newhc.hc_x <= xhi; newhc.hc_x++)
	    for (newhc.hc_y = ylo; newhc.hc_y <= yhi; newhc.hc_y++)
	    {
		xbase = xsep * (newhc.hc_x - u->use_xlo);
		ybase = ysep * (newhc.hc_y - u->use_ylo);
		GeoTransTranslate(xbase, ybase, &t, &newhc.hc_trans);
		newhc.hc_hierName = efHNFromUse(&newhc, hc->hc_hierName);
		if ((*func)(&newhc, cdata))
		    return (1);
	    }
    }

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * efHierSrArray --
 *
 * Iterate over the subscripts in the Connection 'conn', deriving the
 * names conn_name1 and conn_name2 for each such subscript, calling
 * the supplied procedure for each.
 *
 * This procedure should be of the following form:
 *
 *	(*proc)(hc, name1, name2, conn, cdata)
 *	    HierContext *hc;
 *	    char *name1;	/# Fully-expanded first name #/
 *	    char *name2;	/# Fully-expanded 2nd name, or NULL #/
 *	    Connection *conn;
 *	    ClientData cdata;
 *	{
 *	}
 *
 * The procedure should return 0 normally, or 1 if it wants us to abort.
 *
 * Results:
 *	0 normally, or 1 if we were aborted.
 *
 * Side effects:
 *	Whatever those of 'proc' are.
 *
 * ----------------------------------------------------------------------------
 */

int
efHierSrArray(
    HierContext *hc,
    Connection *conn,
    int (*proc)(),
    ClientData cdata)
{
    char name1[1024], name2[1024];
    int i, j, i1lo, i2lo, j1lo, j2lo;
    ConnName *c1, *c2;

    /*
     * Only handle three cases:
     *  0 subscripts
     *	1 subscript
     *	2 subscripts
     */
    c1 = &conn->conn_1;
    c2 = &conn->conn_2;
    switch (c1->cn_nsubs)
    {
	case 0:
	    return (*proc)(hc, c1->cn_name, c2->cn_name, conn, cdata);
	    break;
	case 1:
	    i1lo = c1->cn_subs[0].r_lo, i2lo = c2->cn_subs[0].r_lo;
	    for (i = i1lo; i <= c1->cn_subs[0].r_hi; i++)
	    {
		(void) sprintf(name1, c1->cn_name, i);
		if (c2->cn_name)
		    (void) sprintf(name2, c2->cn_name, i - i1lo + i2lo);
		if ((*proc)(hc, name1, c2->cn_name ? name2 : (char *) NULL,
				conn, cdata))
		    return 1;
	    }
	    break;
	case 2:
	    i1lo = c1->cn_subs[0].r_lo, i2lo = c2->cn_subs[0].r_lo;
	    j1lo = c1->cn_subs[1].r_lo, j2lo = c2->cn_subs[1].r_lo;
#ifdef	notdef
	    (void) printf("[%d:%d,%d:%d] [%d:%d,%d:%d]\n",
		i1lo, c1->cn_subs[0].r_hi,
		j1lo, c1->cn_subs[1].r_hi,
		i2lo, c2->cn_subs[0].r_hi,
		j2lo, c2->cn_subs[1].r_hi);
#endif	/* notdef */
	    for (i = i1lo; i <= c1->cn_subs[0].r_hi; i++)
	    {
		for (j = j1lo; j <= c1->cn_subs[1].r_hi; j++)
		{
		    (void) sprintf(name1, c1->cn_name, i, j);
		    if (c2->cn_name)
			(void) sprintf(name2, c2->cn_name,
				    i - i1lo + i2lo, j - j1lo + j2lo);
		    if ((*proc)(hc,name1,c2->cn_name ? name2 : (char *) NULL,
				conn, cdata))
			return 1;
		}
	    }
	    break;
	default:
	    printf("Can't handle > 2 array subscripts\n");
	    break;
    }

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * EFHierSrDefs ---
 *
 *	Traverse the cell definition hierarchy, processing each cell once.
 *	For each definition, call func() with client data argument cdata.
 *	If func() is NULL, then traverse the hierarchy and mark all cell
 *	definitions as unprocessed.
 *
 * ----------------------------------------------------------------------------
 */

int
EFHierSrDefs(
    HierContext *hc,
    int (*func)(),
    ClientData cdata)
{
    HierContext newhc;
    Use *u;
    int retval;
    HashSearch hs;
    HashEntry *he;

    if (func == NULL)
    {
	if (!(hc->hc_use->use_def->def_flags & DEF_PROCESSED))
	    return 0;
	hc->hc_use->use_def->def_flags &= ~DEF_PROCESSED;
    }
    else
    {
	if (hc->hc_use->use_def->def_flags & DEF_PROCESSED)
	    return 0;
	hc->hc_use->use_def->def_flags |= DEF_PROCESSED;
    }

    HashStartSearch(&hs);
    while ((he = HashNext(&hc->hc_use->use_def->def_uses, &hs)))
    {
	u = (Use *)HashGetValue(he);
	newhc.hc_use = u;
	newhc.hc_hierName = NULL;
	GeoTransTrans(&u->use_trans, &hc->hc_trans, &newhc.hc_trans);
	if (EFHierSrDefs(&newhc, func, cdata))
	    return 1;
    }
    if (func == NULL)
	return 0;
    else
    {
	/* Clear DEF_PROCESSED for the duration of running the function */

	hc->hc_use->use_def->def_flags &= ~DEF_PROCESSED;
	retval = (*func)(hc, cdata);
	hc->hc_use->use_def->def_flags |= DEF_PROCESSED;
	return retval;
    }
}

/*----------------------------------------------------------------------*/
/* All the routines below have been copied and modified from EFvisit.c.	*/
/* They are used for hierarchical output, such as a netlist for LVS.	*/
/*----------------------------------------------------------------------*/

/*
 * ----------------------------------------------------------------------------
 *
 * EFHierVisitSubcircuits --
 *
 * Visit all of the "defined" subcircuits in the circuit.
 * This is meant to provide a generic functionality similar to
 * the transistor/resistor/capacitor extraction.  It assumes that the
 * end-user has an existing description of the extracted subcircuit,
 * such as a characterized standard cell, and that magic is not to
 * attempt an extraction itself, but only to call the predefined
 * subcircuit, matching nodes to the subcircuit's port list.
 *
 * For each def encountered, call the user-supplied procedure
 * (*subProc)(), which should be of the following form:
 *
 * 	(*subProc)(hc, use, hierName)
 *	    HierContext *hc;
 *	    bool is_top;
 *	{
 *	}
 *
 * The procedure should return 0 normally, or 1 to abort the search.
 *
 * Results:
 *	Returns 0 if terminated normally, or 1 if the search
 *	was aborted.
 *
 * Side effects:
 *	Whatever (*subProc)() does.
 *
 * ----------------------------------------------------------------------------
 */

int
EFHierVisitSubcircuits(
    HierContext *hc,
    int (*subProc)(),
    ClientData cdata)	/* unused */
{
    CallArg ca;
    int efHierVisitSubcircuits();   /* Forward declaration */

    /* For each subcell of the top-level def that is defined as */
    /* a subcircuit, call subProc.				*/

    ca.ca_proc = subProc;
    ca.ca_cdata = (ClientData)hc->hc_use->use_def;	/* Save top-level def */

    if (efHierSrUses(hc, efHierVisitSubcircuits, (ClientData) &ca))
	return 1;

    return 0;
}

/*
 * Procedure to visit recursively all subcircuits in the design.
 * Does all the work of EFHierVisitSubcircuits() above.
 *
 * Results:
 *	Returns 0 to keep efHierSrUses going.
 *
 * Side effects:
 *	Calls the client procedure (*ca->ca_proc)().
 */

int
efHierVisitSubcircuits(
    HierContext *hc,
    CallArg *ca)
{
    /* Visit all children of this def */
    Def *def = (Def *)ca->ca_cdata;
    bool is_top = (def == hc->hc_use->use_def) ? TRUE : FALSE;

    if ((*ca->ca_proc)(hc->hc_use, hc->hc_hierName, is_top))
	return 1;
    else
	return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * efHierDevKilled --
 *
 * Check all of the nodes to which the dev 'dev' is connected.
 * If any of these nodes have been killed, then the dev is also killed.
 *
 * Results:
 *      TRUE if the dev is connected to a killed node, FALSE if it's ok.
 *
 * Side effects:
 *      None.
 *
 * ----------------------------------------------------------------------------
 */

bool
efHierDevKilled(
    HierContext *hc,
    Dev *dev,
    HierName *prefix)
{
    HierName *suffix;
    HashEntry *he;
    EFNodeName *nn;
    int n;
    Def *def = hc->hc_use->use_def;

    for (n = 0; n < dev->dev_nterm; n++)
    {
	if (dev->dev_terms[n].dterm_node == NULL) continue;
	suffix = dev->dev_terms[n].dterm_node->efnode_name->efnn_hier;
	he = HashLookOnly(&efNodeHashTable, (char *)suffix);
	if (he  && (nn = (EFNodeName *) HashGetValue(he))
		&& (nn->efnn_node->efnode_flags & EF_KILLED))
	    return TRUE;
    }

    return FALSE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * EFHierVisitDevs --
 *
 * Visit all the devs in the circuit.
 * For each dev in the circuit, call the user-supplied procedure
 * (*devProc)(), which should be of the following form:
 *   see also typedef cb_extflat_hiervisitdevs_t:
 *
 *	int (*devProc)(
 *	    HierContext *hc,
 *	    Dev *dev,
 *	    float scale,
 *	    ClientData cdata)
 *	{
 *	}
 *
 * The procedure should return 0 normally, or 1 to abort the
 * search.
 *
 * We ensure that no devs connected to killed nodes are passed
 * to this procedure.
 *
 * Results:
 *	Returns 0 if terminated normally, or 1 if the search
 *	was aborted.
 *
 * Side effects:
 *	Whatever (*devProc)() does.
 *
 * ----------------------------------------------------------------------------
 */

int
EFHierVisitDevs(
    HierContext *hc,
    const cb_extflat_hiervisitdevs_t devProc,
    ClientData cdata)
{
    CallArg ca;

    ca.ca_proc = (int (*)()) devProc;
    ca.ca_cdata = cdata;
    return efHierVisitDevs(hc, (ClientData) &ca);
}

/*
 * Procedure to visit recursively all devs in the design.
 * Does all the work of EFHierVisitDevs() above.
 *
 * Results:
 *	Returns 0 to keep efHierSrUses going.
 *
 * Side effects:
 *	Calls the client procedure (*ca->ca_proc)().
 */

int
efHierVisitDevs(
    HierContext *hc,
    CallArg *ca)
{
    Def *def = hc->hc_use->use_def;
    Dev *dev;
    HashSearch hs;
    HashEntry *he;
    float scale;

    /*
     * Note that the transform passed does not transform
     * the scale;  where def->def_scale != 1.0, the visited
     * procedure will have to multiply values by def->def_scale.
     */

    scale = (efScaleChanged && def->def_scale != 1.0) ? def->def_scale : 1.0;

    /* Visit all devices */
    HashStartSearch(&hs);
    while ((he = HashNext(&def->def_devs, &hs)))
    {
	dev = (Dev *)HashGetValue(he);
	if (efHierDevKilled(hc, dev, hc->hc_hierName))
	{
	    TxError("Error:  Device at (%d %d) is connected to one or more"
			" eliminated nodes.\n",
			dev->dev_rect.r_xbot, dev->dev_rect.r_ybot);
	    /* Output the device anyway, but something needs fixing */
	    // continue;
	}

	const cb_extflat_hiervisitdevs_t ca_hiervisitdevs_proc = (cb_extflat_hiervisitdevs_t) ca->ca_proc; /* FIXME temporary */
	if ((*ca_hiervisitdevs_proc)(hc, dev, scale, ca->ca_cdata)) /* @invoke cb_extflat_hiervisitdevs_t */
	    return 1;
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * efHierVisitSingleResist --
 *
 * Visit a resistor of res->conn_res milliohms between the nodes
 * 'name1' and 'name2' (text names, not hierarchical names).  Don't
 * process the resistor if either terminal is a killed node.
 *
 * Results:
 *	Whatever the user-supplied procedure (*ca->ca_proc)() returns
 *	(type int).
 *
 * Side effects:
 *	Calls the user-supplied procedure.
 *
 * ----------------------------------------------------------------------------
 */

int
efHierVisitSingleResist(
    HierContext *hc,		/* Contains hierarchical pathname to cell */
    const char *name1,
    const char *name2,		/* Names of nodes connecting to resistor */
    Connection *res,		/* Contains resistance to add */
    ClientData cdata)		/* (CallArg *) */
{
    CallArg *ca = (CallArg *) CD2PTR(cdata);
    const EFNode *n1, *n2;
    HashEntry *he;
    Def *def = hc->hc_use->use_def;

    if ((he = HashLookOnly(&def->def_nodes, name1)) == NULL)
	return 0;
    n1 = ((const EFNodeName *) HashGetValue(he))->efnn_node;
    if (n1->efnode_flags & EF_KILLED)
	return 0;

    if ((he = HashLookOnly(&def->def_nodes, name2)) == NULL)
	return 0;
    n2 = ((const EFNodeName *) HashGetValue(he))->efnn_node;
    if (n2->efnode_flags & EF_KILLED)
	return 0;

    /* Do nothing if the nodes aren't different */
    if (n1 == n2)
	return 0;

    const cb_extflat_hiervisitresists_t ca_hiervisitresists_proc = (cb_extflat_hiervisitresists_t) ca->ca_proc; /* FIXME temporary */
    return (*ca_hiervisitresists_proc)(hc, n1->efnode_name->efnn_hier,
		n2->efnode_name->efnn_hier,
		res->conn_res, ca->ca_cdata);
}

/*
 * ----------------------------------------------------------------------------
 *
 * EFHierVisitResists --
 *
 * Visit all the resistors in the circuit.
 * For each resistor in the circuit, call the user-supplied procedure
 * (*resProc)(), which should be of the following form, where hn1 and
 * hn2 are the HierNames of the two nodes connected by the resistor
 *   see also typedef cb_extflat_hiervisitresists_t:
 *
 *	int (*resProc)(
 *	    HierContext *hc,
 *	    const HierName *hierName1,
 *	    const HierName *hierName2,
 *	    float resistance,
 *	    ClientData cdata)
 *	{
 *	}
 *
 * The procedure should return 0 normally, or 1 to abort the
 * search.
 *
 * We ensure that no resistors connected to killed nodes are passed
 * to this procedure.
 *
 * Results:
 *	Returns 0 if terminated normally, or 1 if the search
 *	was aborted.
 *
 * Side effects:
 *	Whatever (*resProc)() does.
 *
 * ----------------------------------------------------------------------------
 */

int
EFHierVisitResists(
    HierContext *hc,
    const cb_extflat_hiervisitresists_t resProc,
    ClientData cdata)
{
    CallArg ca;
    int efHierVisitResists(HierContext *hc, CallArg *ca);	/* Forward reference */

    ca.ca_proc = (int (*)()) resProc;
    ca.ca_cdata = cdata;
    return efHierVisitResists(hc, &ca);
}

/*
 * Procedure to visit recursively all resistors in the design.
 * Does all the work of EFHierVisitResists() above.
 *
 * Results:
 *	Returns 0 to keep efHierSrUses going.
 *
 * Side effects:
 *	Calls the client procedure (*ca->ca_proc)().
 */

int
efHierVisitResists(
    HierContext *hc,
    CallArg *ca)
{
    Def *def = hc->hc_use->use_def;
    Connection *res;
    Transform t;
    int scale;

    /* Visit all resistors */
    for (res = def->def_resistors; res; res = res->conn_next)
    {
	/* Special case for speed if no arraying info */
	if (res->conn_1.cn_nsubs == 0)
	{
	    if (efHierVisitSingleResist(hc, res->conn_name1, res->conn_name2,
			res, ca))
		return 1;
	}
	else if (efHierSrArray(hc, res, efHierVisitSingleResist, PTR2CD(ca)))
	    return 1;
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * EFHierVisitCaps --
 *
 * Visit all the local capacitance records
 * Calls the user-provided procedure (*capProc)()
 * which should be of the following format:
 *
 *	(*capProc)(hc, hierName1, hierName2, cap, cdata)
 *	    HierContext *hc;
 *	    HierName *hierName1, *hierName2;
 *	    EFCapValue cap;
 *	    ClientData cdata;
 *	{
 *	}
 *
 * Here cap is the capacitance in attofarads.
 *
 * Results:
 *	Returns 1 if the client procedure returned 1;
 *	otherwise returns 0.
 *
 * Side effects:
 *	Calls the user-provided procedure (*capProc)().
 *
 * ----------------------------------------------------------------------------
 */

int
EFHierVisitCaps(
    HierContext *hc,
    int (*capProc)(),
    ClientData cdata)
{
    HashSearch hs;
    HashEntry *he;
    EFCoupleKey *ck;
    EFCapValue ccap;

    /* Visit capacitors flattened from a lower level, as well	*/
    /* as our own.  These have been created and saved in	*/
    /* efCapHashTable using efFlatCaps().			*/

    HashStartSearch(&hs);
    while ((he = HashNext(&efCapHashTable, &hs)))
    {
	ccap = CapHashGetValue(he);
	ck = (EFCoupleKey *) he->h_key.h_words;
	if ((*capProc)(hc, ck->ck_1->efnode_name->efnn_hier,
			ck->ck_2->efnode_name->efnn_hier,
			(double) ccap, cdata))
	    return 1;
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * EFHierVisitNodes --
 *
 * Visit all the flat nodes in the circuit.
 * Calls the user-provided procedure (*nodeProc)()
 * which should be of the following format:
 *
 *	(*nodeProc)(hc, hierName1, hierName2, res, cap, cdata)
 *	    HierContext *hc;
 *	    HierName *hierName1, *hierName2;
 *	    int res;
 *	    EFCapValue cap;
 *	    ClientData cdata;
 *	{
 *	}
 *
 * Here cap is the lumped capacitance to substrate in attofarads,
 * and res is the lumped resistance to substrate in milliohms.
 *
 * Results:
 *	Returns 1 if the client procedure returned 1;
 *	otherwise returns 0.
 *
 * Side effects:
 *	Calls the user-provided procedure (*nodeProc)().
 *
 * ----------------------------------------------------------------------------
 */

int
EFHierVisitNodes(
    HierContext *hc,
    int (*nodeProc)(),
    ClientData cdata)
{
    Def *def = hc->hc_use->use_def;
    EFCapValue cap;
    int res;
    EFNode *snode;
    HierName *hierName;

    for (snode = (EFNode *) efNodeList.efnode_next;
            snode != &efNodeList;
            snode = (EFNode *) snode->efnode_next)
    {
	res = EFNodeResist(snode);
	cap = snode->efnode_cap;
	hierName = (HierName *) snode->efnode_name->efnn_hier;
	if (snode->efnode_flags & EF_GLOB_SUBS_NODE)
	    cap = 0;

	if (snode->efnode_flags & EF_KILLED) continue;

	if ((*nodeProc)(hc, snode, res, (double)cap, cdata))
	    return 1;
    }
    return 0;
}

