//-----------------------------------------------------------------------------
// COPYRIGHT  (c)  1997 
// THE REGENTS OF THE UNIVERSITY OF MICHIGAN
// ALL RIGHTS RESERVED
// 
// PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS 
// AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR 
// ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG AS 
// THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, AND 
// THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO LONG 
// AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY 
// ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR 
// DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN 
// PRIOR AUTHORIZATION.
// 
// THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION 
// FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS 
// FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE 
// UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR 
// IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 
// REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE 
// FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 
// CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT 
// HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH 
// DAMAGES.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// File: circuit.cc
//
// Purpose: models a spice circuit netlist
//
// Remarks: 
//
// History: 03/22/97 - MAR - created.
// 					05/06/02 - MRG - completed netlist flattening.
// 					05/07/02 - MRG - added output operators
//					01/16/04 - STF - modified to support ASG work
//
// Copyright (c) 1997 Michael A. Riepe
// Copyright (c) 2002 Matthew R. Guthaus
//
// RCS Version:
//     $Id: circuit.cc,v 1.4 2002/05/07 01:22:21 mguthaus Exp $
//-----------------------------------------------------------------------------

#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include <vector>
#include <map>
#include <list>
using namespace std;

#include "defs.hh"
#include "utils.hh"
#include "circuit.hh"

// ****************************************************************************
// class: subCircuit
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: subCircuit
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the subcircuit.
//
// Side-effects:
//
// Notes: 
//-----------------------------------------------------------------------------

subCircuit::subCircuit() : device(), _devices(), _nets(), _port_map(), _port_vec() {}
subCircuit::subCircuit(char *name) : device(name), _devices(), _nets(), _port_map(),
    _port_vec() {}

//-----------------------------------------------------------------------------
// Function: ~subCircuit
//
// Purpose: Destructor.  Deletes the objects stored in the _devices and _ports
//          lists and the map of nets.
//
// Side-effects:
//
// Notes: 
//-----------------------------------------------------------------------------

subCircuit::~subCircuit() {
		DBG0(cout <<"subckt destructor"<<endl;);

    map<char*, device*, ltstr>::iterator dev_curr;
    for (dev_curr = _devices.begin(); dev_curr != _devices.end(); dev_curr++)
        delete (*dev_curr).second;

    vector<port*>::iterator port_curr;
    for (port_curr = _port_vec.begin(); port_curr != _port_vec.end(); port_curr++)
        delete *port_curr;
    
    map<char*, net*, ltstr>::iterator net_curr;
    for (net_curr = _nets.begin(); net_curr != _nets.end(); net_curr++)
        delete (*net_curr).second;
}
    
//-----------------------------------------------------------------------------
// Function: subCircuit
//
// Purpose: Copy constructor.  
//
// Side-effects:
//
// Notes: 
//-----------------------------------------------------------------------------
subCircuit::subCircuit(subCircuit& s)
{
		//cout << "subckt copy constructor"<<endl;
		name() = strdup(s.name());

    parent() = NULL;
    
		// copy parameters
		copy_parameters(s.Sparams());
		copy_parameters(s.Dparams());

		// copy devices
    map<char*, device*, ltstr>::iterator dev_curr;
    for (dev_curr = s.devices().begin(); dev_curr != s.devices().end(); dev_curr++)
			{
        device *dev = (*dev_curr).second;
        switch(dev->which()) {
            case (device::_SUBCKTINST_):
	        copy_subcktinst(NULL,(subcktinst *)dev, NULL, NULL);
                break;
            case (device::_BJT_):
                copy_bjt(NULL,(bjt *)dev, NULL, NULL);
                break;
	    case (device::_CAPACITOR_):
                copy_capacitor(NULL,(capacitor *)dev, NULL, NULL);
                break;
            case (device::_DIODE_):
                copy_diode(NULL,(diode *)dev, NULL, NULL);
                break;
            case (device::_INDUCTOR_):
                copy_inductor(NULL,(inductor *)dev, NULL, NULL);
                break;
            case (device::_ISOURCE_):
                copy_isource(NULL,(isource *)dev, NULL, NULL);
                break;
            case (device::_JFET_):
                copy_jfet(NULL,(jfet *)dev, NULL, NULL);
                break;
            case (device::_MOSFET_):
                copy_mosfet(NULL,(mosfet *)dev, NULL, NULL);
                break;
            case (device::_RESISTOR_):
                copy_resistor(NULL,(resistor *)dev, NULL, NULL);
                break;
            case (device::_VSOURCE_):
                copy_vsource(NULL,(vsource *)dev, NULL, NULL);
                break;
 	    case (device::_COUPLING_):
                copy_coupling(NULL,(coupling *)dev, NULL, NULL);
                break;
        }
    	}

		// at this point, we have all the devices and nets from the copy_*
		// but we only have the ports on devices, so we need to add them for the subckt

		// copy port vector
    vector<port*>::iterator port_curr;
    for (port_curr = s.port_vec().begin(); port_curr != s.port_vec().end(); port_curr++)
		{ 
			//cout << "updating port " << (*port_curr)->name() << endl;
			// add the port, this automatically updates device_ref to point to this
			port *p=add_port(strdup((*port_curr)->name()),(*port_curr)->direction());
			// update the net ref to point to same name as in port vector
			p->net_ref()=_nets[(*port_curr)->net_ref()->name()];
			// make the port map too
			_port_map[p->name()]=p;
		}

}
//-----------------------------------------------------------------------------
// Function: operator<<
//
// Purpose: output operator overload for circuit and subCircuit
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

ostream &operator<<(ostream& os, subCircuit& s) {
	// subcircuit interface
	os << ".SUBC " << s.name() << " ";
	vector<port*>::iterator port_curr;
  /*for (port_curr = s.port_vec().begin(); port_curr != s.port_vec().end(); port_curr++) {
      os << **port_curr << " ";
  }   */     
	os << endl;

	// should dump the parameters too...

  // dump the devices
  map<char*, device*, ltstr>::iterator dev_curr;
  for (dev_curr = s._devices.begin(); dev_curr != s._devices.end(); dev_curr++) {
			dev_curr->second->print(os); // need to call virtual print function
  }

	os << ".ENDS " << s.name() << endl;
	return(os);
}

ostream &operator<<(ostream& os, circuit& s) {
	os << "* " << s.name() << endl;
  map<char*, subCircuit*, ltstr>::iterator sub_curr;
  for (sub_curr = s.subckts().begin(); sub_curr != s.subckts().end(); sub_curr++) {
			os << *(sub_curr->second) << endl;
	}

  map<char*, device*, ltstr>::iterator dev_curr;
  for (dev_curr = s.devices().begin(); dev_curr != s.devices().end(); dev_curr++) {
			dev_curr->second->print(os); // need to call virtual print function
	}

	os << ".END" << endl;
	return(os);
}

//-----------------------------------------------------------------------------
// Function: add_bjt
//
// Purpose: adds a new instance of a bjt to the subcircuit netlist.  This
//          routine is responsible for creating the bjt and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

bjt *subCircuit::add_bjt (char *nName, char *nc, char *nb, char *ne, char *ns) {
    DBG0(cout<<"adding a bjt: "<<nName<<endl;);

    bjt* new_bjt = new bjt(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_bjt;

    // collector
    net *collector_net=add_net(nc);
    collector_net->add_terminal(new_bjt->collector());
    new_bjt->collector().net_ref()=collector_net;

    // base
    net *base_net=add_net(nb);
    base_net->add_terminal(new_bjt->base());
    new_bjt->base().net_ref()=base_net;

    // emitter
    net *emitter_net=add_net(ne);
    emitter_net->add_terminal(new_bjt->emitter());
    new_bjt->emitter().net_ref()=emitter_net;

    // substrate (optional)
    if (ns) {
        net *substrate_net=add_net(ns);
        substrate_net->add_terminal(new_bjt->substrate());
        new_bjt->substrate().net_ref()=substrate_net;
    }

    return(new_bjt);
}


//-----------------------------------------------------------------------------
// Function: add_capacitor
//
// Purpose: adds a new instance of a capacitor to the subcircuit netlist.  This
//          routine is responsible for creating the capacitor and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

capacitor *subCircuit::add_capacitor (char *nName, char *node1, char *node2) {
    DBG0(cout<<"adding a capacitor: "<<nName<<endl;);

    capacitor* new_capacitor = new capacitor(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_capacitor;

    // node1
    net *node1_net=add_net(node1);
    node1_net->add_terminal(new_capacitor->node1());
    new_capacitor->node1().net_ref()=node1_net;

    // node2
    net *node2_net=add_net(node2);
    node2_net->add_terminal(new_capacitor->node2());
    new_capacitor->node2().net_ref()=node2_net;

    return(new_capacitor);
}

//-----------------------------------------------------------------------------
// Function: add_coupling
//
// Purpose: adds a new instance of a capacitor to the subcircuit netlist.  This
//          routine is responsible for creating the coupling and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

coupling *subCircuit::add_coupling (char *nName, char *inductor1, char *inductor2) {
    DBG0(cout<<"adding a coupling: "<<nName<<endl;);

    coupling* new_coupling = new coupling(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_coupling;

    // inductor1
    net *inductor1_net=add_net(inductor1);
    inductor1_net->add_terminal(new_coupling->inductor1());
    new_coupling->inductor1().net_ref()=inductor1_net;

    // node2
    net *inductor2_net=add_net(inductor2);
    inductor2_net->add_terminal(new_coupling->inductor2());
    new_coupling->inductor2().net_ref()=inductor2_net;

    return(new_coupling);
}


//-----------------------------------------------------------------------------
// Function: add_diode
//
// Purpose: adds a new instance of a diode to the subcircuit netlist.  This
//          routine is responsible for creating the diode and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

diode *subCircuit::add_diode (char *nName, char *nplus, char *nminus) {
    DBG0(cout<<"adding a diode: "<<nName<<endl;);

    diode* new_diode = new diode(nName);
    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_diode;;

    // nplus
    net *nplus_net=add_net(nplus);
    nplus_net->add_terminal(new_diode->nplus());
    new_diode->nplus().net_ref()=nplus_net;

    // nminus
    net *nminus_net=add_net(nminus);
    nminus_net->add_terminal(new_diode->nminus());
    new_diode->nminus().net_ref()=nminus_net;

    return(new_diode);
}


//-----------------------------------------------------------------------------
// Function: add_inductor
//
// Purpose: adds a new instance of a inductor to the subcircuit netlist.  This
//          routine is responsible for creating the inductor and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

inductor *subCircuit::add_inductor (char *nName, char *node1, char *node2) {
    DBG0(cout<<"adding a inductor: "<<nName<<endl;);

    inductor* new_inductor = new inductor(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_inductor;

    // node1
    net *node1_net=add_net(node1);
    node1_net->add_terminal(new_inductor->node1());
    new_inductor->node1().net_ref()=node1_net;

    // node2
    net *node2_net=add_net(node2);
    node2_net->add_terminal(new_inductor->node2());
    new_inductor->node2().net_ref()=node2_net;

    return(new_inductor);
}


//-----------------------------------------------------------------------------
// Function: add_isource
//
// Purpose: adds a new instance of a isource to the subcircuit netlist.  This
//          routine is responsible for creating the isource and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

isource *subCircuit::add_isource (char *nName, char *plus_node, char *minus_node) {
    DBG0(cout<<"adding a isource: "<<nName<<endl;);

    isource* new_isource = new isource(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_isource;

    // plus_node
    net *plus_node_net=add_net(plus_node);
    plus_node_net->add_terminal(new_isource->plus_node());
    new_isource->plus_node().net_ref()=plus_node_net;

    // minus_node
    net *minus_node_net=add_net(minus_node);
    minus_node_net->add_terminal(new_isource->minus_node());
    new_isource->minus_node().net_ref()=minus_node_net;

    return(new_isource);

}


//-----------------------------------------------------------------------------
// Function: add_jfet
//
// Purpose: adds a new instance of a jfet to the subcircuit netlist.  This
//          routine is responsible for creating the jfet and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

jfet *subCircuit::add_jfet (char *nName, char *nd, char *ng, char *ns, char *nb) {
    DBG0(cout<<"adding a jfet: "<<nName<<endl;);

    jfet* new_jfet = new jfet(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_jfet;

    // drain
    net *drain_net=add_net(nd);
    drain_net->add_terminal(new_jfet->drain());
    new_jfet->drain().net_ref()=drain_net;

    // gate
    net *gate_net=add_net(ng);
    gate_net->add_terminal(new_jfet->gate());
    new_jfet->gate().net_ref()=gate_net;

    // source
    net *source_net=add_net(ns);
    source_net->add_terminal(new_jfet->source());
    new_jfet->source().net_ref()=source_net;

    // bulk (optional)
    if (nb) {
        net *bulk_net=add_net(nb);
        bulk_net->add_terminal(new_jfet->bulk());
        new_jfet->bulk().net_ref()=bulk_net;
    }

    return(new_jfet);
}

//-----------------------------------------------------------------------------
// Function: remove_port
//
// Purpose: removes references of port in a subcircuit (_port_vec and _port_map).
//					The actually destruction of the port occurs in ~mosfet 
//
// Side-effects: existing references/pointers will be invalidated
//
// Notes: 
//-----------------------------------------------------------------------------
void subCircuit::remove_port (port *p) {
	//cout<<"removing port "<<p->name()<<endl;
	_port_map.erase(p->name());
	vector<port*>::iterator pv;
	for(pv=_port_vec.begin();pv!=_port_vec.end();pv++)
	{
		cout <<(*pv)->name()<<endl;
		if (*pv==p) 
		{
			_port_vec.erase(pv);
			break;
		}
	}
	delete p;
	if (pv==_port_vec.end()) 
		cerr<<"Couldn't find port "<<p->name()<<" to remove."<<endl;
}

//-----------------------------------------------------------------------------
// Function: remove_net
//
// Purpose: removes a net from a subcircuit.
//
// Side-effects: existing references/pointers will be invalidated
//
// Notes: 
//-----------------------------------------------------------------------------
void subCircuit::remove_net (net *n) {
	//cout<<"removing net "<<n->name()<<endl;
	_nets.erase(n->name());
	delete n;
}

//-----------------------------------------------------------------------------
// Function: remove_mosfet
//
// Purpose: removes a mosfet from a subcircuit. this routine is responsible for
//					removing all corresponding ports and references in other nets
//
// Side-effects: existing references/pointers will be invalidated
//
// Notes: 
//-----------------------------------------------------------------------------

void subCircuit::remove_mosfet (mosfet *m) {

	// remove references on drain, gate, source, and bulk (optional) nets
	m->drain().net_ref()->remove_terminal(m->drain());
	m->source().net_ref()->remove_terminal(m->source());
	m->gate().net_ref()->remove_terminal(m->gate());
	m->bulk().net_ref()->remove_terminal(m->bulk());

	// free up the mosfet itself
	map<char*,device*,ltstr>::iterator r=_devices.find(m->name());
	_devices.erase(r);
	delete r->second;
}

//-----------------------------------------------------------------------------
// Function: add_mosfet
//
// Purpose: adds a new instance of a mosfet to the subcircuit netlist.  This
//          routine is responsible for creating the mosfet and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

mosfet *subCircuit::add_mosfet (char* nName, char *nd, char *ng, char *ns, char *nb) {
    DBG0(cout<<"adding a mosfet: "<<nName<<endl;);

    mosfet* new_mosfet = new mosfet(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_mosfet;

    // drain
    net *drain_net=add_net(nd);
    drain_net->add_terminal(new_mosfet->drain());
    new_mosfet->drain().net_ref()=drain_net;

    // gate
    net *gate_net=add_net(ng);
    gate_net->add_terminal(new_mosfet->gate());
    new_mosfet->gate().net_ref()=gate_net;

    // source
    net *source_net=add_net(ns);
    source_net->add_terminal(new_mosfet->source());
    new_mosfet->source().net_ref()=source_net;

    // bulk (optional)
    if (nb) {
        net *bulk_net=add_net(nb);
        bulk_net->add_terminal(new_mosfet->bulk());
        new_mosfet->bulk().net_ref()=bulk_net;
    }

    return(new_mosfet);
}


//-----------------------------------------------------------------------------
// Function: add_resistor
//
// Purpose: adds a new instance of a resistor to the subcircuit netlist.  This
//          routine is responsible for creating the resistor and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

resistor *subCircuit::add_resistor (char* nName, char *node1, char *node2) {
    DBG0(cout<<"adding a resistor: "<<nName<<endl;);

    resistor* new_resistor = new resistor(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_resistor;

    // node1
    net *node1_net=add_net(node1);
    node1_net->add_terminal(new_resistor->node1());
    new_resistor->node1().net_ref()=node1_net;

    // node2
    net *node2_net=add_net(node2);
    node2_net->add_terminal(new_resistor->node2());
    new_resistor->node2().net_ref()=node2_net;

    return(new_resistor);
}


//-----------------------------------------------------------------------------
// Function: add_subcktinst
//
// Purpose: adds a new instance of a subcktinst to the subcircuit netlist.  This
//          routine is responsible for creating the subcktinst and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

subcktinst *subCircuit::add_subcktinst (char* nName, subCircuit &sub_def, vector<char*> &nodes) {
    DBG0(cout<<"adding an instance of subckt "<<sub_def.name()<<": "<<nName<<endl;);

    subcktinst* new_inst = new subcktinst(nName,sub_def.port_vec());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_inst;

    if (nodes.size() != sub_def.port_vec().size()) {
        sprintf(error_msg,"#port osn subcircuit instance %s does not match #ports on subcircuit",nName);
        Abort(error_msg);
    }

    // Step through port map adding ports and connecting nets to them
    for (int i=0; i<nodes.size(); i++) {
        net *this_net = add_net(nodes[i]);
        port *this_port = new_inst->port_vec()[i];
        if (!this_port) Abort("subCircuit::add_subcktinst() internal error");
        this_net->add_terminal(*this_port);
        this_port->net_ref()=this_net;
    }
    
    return(new_inst);
}


//-----------------------------------------------------------------------------
// Function: add_vsource
//
// Purpose: adds a new instance of a vsource to the subcircuit netlist.  This
//          routine is responsible for creating the vsource and connecting its
//          terminals to nets in the subcircuit, creating the nets if
//          necessary
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

vsource *subCircuit::add_vsource (char* nName, char *plus_node, char *minus_node) {
    DBG0(cout<<"adding a vsource: "<<nName<<endl;);

    vsource* new_vsource = new vsource(nName);

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in netlist: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_vsource;

    // plus_node
    net *plus_node_net=add_net(plus_node);
    plus_node_net->add_terminal(new_vsource->plus_node());
    new_vsource->plus_node().net_ref()=plus_node_net;

    // minus_node
    net *minus_node_net=add_net(minus_node);
    minus_node_net->add_terminal(new_vsource->minus_node());
    new_vsource->minus_node().net_ref()=minus_node_net;

    return(new_vsource);

}


//-----------------------------------------------------------------------------
// Function: copy_bjt
//
// Purpose: copys an instance of a bjt from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

bjt *subCircuit::copy_bjt (circuit *top, bjt *in_bjt, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_bjt->name(), '_');
    bjt* new_bjt = new bjt(nName);
    new_bjt->model() = strdup(in_bjt->model());
    new_bjt->polarity() = in_bjt->polarity();
    new_bjt->copy_parameters(in_bjt->Dparams());
    new_bjt->copy_parameters(in_bjt->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_bjt: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_bjt;

		propagate_port(top,in_bjt->collector(),new_bjt->collector(),in_inst,prefix);
		propagate_port(top,in_bjt->base(),new_bjt->base(),in_inst,prefix);
		propagate_port(top,in_bjt->emitter(),new_bjt->emitter(),in_inst,prefix);
		if (in_bjt->substrate().net_ref())
			propagate_port(top,in_bjt->substrate(),new_bjt->substrate(),in_inst,prefix);

    return(new_bjt);
}


//-----------------------------------------------------------------------------
// Function: copy_capacitor
//
// Purpose: copys an instance of a capacitor from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

capacitor *subCircuit::copy_capacitor (circuit *top, capacitor *in_capacitor, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_capacitor->name(), '_');
    capacitor* new_capacitor = new capacitor(nName);
    new_capacitor->copy_parameters(in_capacitor->Dparams());
    new_capacitor->copy_parameters(in_capacitor->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_capacitor: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_capacitor;

		propagate_port(top,in_capacitor->node1(),new_capacitor->node1(),in_inst,prefix);
		propagate_port(top,in_capacitor->node2(),new_capacitor->node2(),in_inst,prefix);
return(new_capacitor);
}

//-----------------------------------------------------------------------------
// Function: copy_coupling
//
// Purpose: copys an instance of a coupling from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------
coupling *subCircuit::copy_coupling (circuit *top, coupling *in_coupling, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_coupling->name(), '_');
    coupling* new_coupling = new coupling(nName);
    new_coupling->copy_parameters(in_coupling->Dparams());
    new_coupling->copy_parameters(in_coupling->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_coupling: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_coupling;

		propagate_port(top,in_coupling->inductor1(),new_coupling->inductor1(),in_inst,prefix);
		propagate_port(top,in_coupling->inductor2(),new_coupling->inductor2(),in_inst,prefix);
return(new_coupling);
}

//-----------------------------------------------------------------------------
// Function: copy_diode
//
// Purpose: copys an instance of a diode from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

diode *subCircuit::copy_diode (circuit *top, diode *in_diode, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_diode->name(), '_');
    diode* new_diode = new diode(nName);
    new_diode->copy_parameters(in_diode->Dparams());
    new_diode->copy_parameters(in_diode->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_diode: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_diode;

		propagate_port(top,in_diode->nplus(),new_diode->nplus(),in_inst,prefix);
		propagate_port(top,in_diode->nminus(),new_diode->nminus(),in_inst,prefix);
return(new_diode);
}


//-----------------------------------------------------------------------------
// Function: copy_inductor
//
// Purpose: copys an instance of a inductor from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

inductor *subCircuit::copy_inductor (circuit *top, inductor *in_inductor, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_inductor->name(), '_');
    inductor* new_inductor = new inductor(nName);
    new_inductor->copy_parameters(in_inductor->Dparams());
    new_inductor->copy_parameters(in_inductor->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_inductor: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_inductor;

		propagate_port(top,in_inductor->node1(),new_inductor->node1(),in_inst,prefix);
		propagate_port(top,in_inductor->node2(),new_inductor->node2(),in_inst,prefix);
return(new_inductor);
}


//-----------------------------------------------------------------------------
// Function: copy_isource
//
// Purpose: copys an instance of a isource from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

isource *subCircuit::copy_isource (circuit *top, isource *in_isource, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_isource->name(), '_');
    isource* new_isource = new isource(nName);
    new_isource->copy_parameters(in_isource->Dparams());
    new_isource->copy_parameters(in_isource->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_isource: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_isource;

		propagate_port(top,in_isource->plus_node(),new_isource->plus_node(),in_inst,prefix);
		propagate_port(top,in_isource->minus_node(),new_isource->minus_node(),in_inst,prefix);

return(new_isource);
}


//-----------------------------------------------------------------------------
// Function: copy_jfet
//
// Purpose: copys an instance of a jfet from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

jfet *subCircuit::copy_jfet (circuit *top, jfet *in_jfet, subcktinst *in_inst, char *prefix) {
    //cout<<"copying jfet instance: "<<in_jfet->name()<<" prefix="<<prefix<<endl;

    char *nName = strcat(prefix, in_jfet->name(), '_');
    jfet* new_jfet = new jfet(nName);
    new_jfet->model() = strdup(in_jfet->model());
    new_jfet->polarity() = in_jfet->polarity();
    new_jfet->copy_parameters(in_jfet->Dparams());
    new_jfet->copy_parameters(in_jfet->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_jfet: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_jfet;

		propagate_port(top,in_jfet->drain(),new_jfet->drain(),in_inst,prefix);
		propagate_port(top,in_jfet->gate(),new_jfet->gate(),in_inst,prefix);
		propagate_port(top,in_jfet->source(),new_jfet->source(),in_inst,prefix);
    if (in_jfet->bulk().net_ref())
			propagate_port(top,in_jfet->bulk(),new_jfet->bulk(),in_inst,prefix);
    return(new_jfet);
}

//-----------------------------------------------------------------------------
// Function: propogate_port
//
// Purpose:  called by copy_* to propogate a ports net connections upwards in hierarchy
//
// Side-effects: new nets may be added to the subcircuit
//
// Notes: 
//-----------------------------------------------------------------------------
void subCircuit::propagate_port(circuit *top,port& inp, port& newp,subcktinst *in_inst,char *prefix) {

//cout<<"port: ";
		net *drain2_net;
		if (!strcmp(inp.net_ref()->name(),top->gnd_node())) 
			drain2_net=add_net(strdup(top->gnd_node()));
		else if ( !strcmp(inp.net_ref()->name(),top->vdd_node()))
			drain2_net=add_net(strdup(top->vdd_node()));
    else if (in_inst && inp.net_ref()->external()) {
//cout << inp.net_ref()->name();
				vector<port*> &terms=inp.net_ref()->terminals();
    		for (int i=0; i != terms.size(); i++) {
					if (in_inst->port_map()[terms[i]->name()]) {
						drain2_net=add_net(strdup(in_inst->port_map()[terms[i]->name()]->net_ref()->name()));
    			}
				}
    }
		else {
    	drain2_net=add_net(strcat(prefix,inp.net_ref()->name(), '_') );
		}
//cout<<":" << drain2_net->name()<<endl;
    drain2_net->add_terminal(newp);
    newp.net_ref()=drain2_net;

}

//-----------------------------------------------------------------------------
// Function: copy_mosfet
//
// Purpose: copies an instance of a mosfet from one subCircuit 
//          to another.  All net connections are made.
///
// Side-effects: a new device and new nets may be added to the 
//               _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

#if 0
mosfet *subCircuit::copy_mosfet (circuit *top, mosfet *in_mosfet, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_mosfet->name(), '_');
    mosfet* new_mosfet = new mosfet(nName);
    new_mosfet->copy_parameters(in_mosfet->Dparams());
    new_mosfet->copy_parameters(in_mosfet->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_mosfet: %s",nName);
        Abort(error_msg);
    } 
    else
        _devices[nName]=new_mosfet;

// NOTE:
// mosfet::node1() and mosfet::node2() are undefined functions...

		propagate_port(top,in_mosfet->node1(),new_mosfet->node1(),in_inst,prefix);
		propagate_port(top,in_mosfet->node2(),new_mosfet->node2(),in_inst,prefix);
return(new_mosfet);
}
#endif


mosfet *subCircuit::copy_mosfet (circuit *top, mosfet *in_mosfet, subcktinst *in_inst, char *prefix) {
    //cout<<"copying mosfet instance: "<<in_mosfet->name()<<" prefix="<<prefix<<endl;

    char *nName = strcat(prefix, in_mosfet->name(), '_');
    mosfet* new_mosfet = new mosfet(nName);
    new_mosfet->model() = strdup(in_mosfet->model());
    new_mosfet->polarity() = in_mosfet->polarity();
    new_mosfet->copy_parameters(in_mosfet->Dparams());
    new_mosfet->copy_parameters(in_mosfet->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_mosfet: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_mosfet;


		propagate_port(top,in_mosfet->drain(),new_mosfet->drain(),in_inst,prefix);
		propagate_port(top,in_mosfet->gate(),new_mosfet->gate(),in_inst,prefix);
		propagate_port(top,in_mosfet->source(),new_mosfet->source(),in_inst,prefix);
    if (in_mosfet->bulk().net_ref())
			propagate_port(top,in_mosfet->bulk(),new_mosfet->bulk(),in_inst,prefix);

    return(new_mosfet);
}

//-----------------------------------------------------------------------------
// Function: copy_resistor
//
// Purpose: copys an instance of a resistor from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

resistor *subCircuit::copy_resistor (circuit *top, resistor *in_resistor, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_resistor->name(), '_');
    resistor* new_resistor = new resistor(nName);
    new_resistor->copy_parameters(in_resistor->Dparams());
    new_resistor->copy_parameters(in_resistor->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_resistor: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_resistor;

		propagate_port(top,in_resistor->node1(),new_resistor->node1(),in_inst,prefix);
		propagate_port(top,in_resistor->node2(),new_resistor->node2(),in_inst,prefix);

return(new_resistor);
}


//-----------------------------------------------------------------------------
// Function: copy_subcktinst
//
// Purpose: copys an instance of a subcktinst from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

subcktinst *subCircuit::copy_subcktinst (circuit *top, subcktinst *in_subcktinst, subcktinst *in_inst, char *prefix) {
    //cout<<"copying subckt instance: "<<in_subcktinst->name()<<" prefix="<<prefix<<endl;

    char *nName = strcat(prefix, in_subcktinst->name(), '_');
    subcktinst* new_subcktinst = new subcktinst(nName,in_subcktinst->port_vec());
    new_subcktinst->model_name() = strdup(in_subcktinst->model_name());
    new_subcktinst->copy_parameters(in_subcktinst->Dparams());
    new_subcktinst->copy_parameters(in_subcktinst->Sparams());

		new_subcktinst->port_map() = in_subcktinst->port_map();	

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_subcktinst: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_subcktinst;


return(new_subcktinst);
}


//-----------------------------------------------------------------------------
// Function: copy_vsource
//
// Purpose: copys an instance of a vsource from one subCircuit to another.  All
//          of the net connections are made.
//
// Side-effects: a new device and new nets may be added to the _devices list
//
// Notes: 
//-----------------------------------------------------------------------------

vsource *subCircuit::copy_vsource (circuit *top, vsource *in_vsource, subcktinst *in_inst, char *prefix) {
    char *nName = strcat(prefix, in_vsource->name(), '_');
    vsource* new_vsource = new vsource(nName);
    new_vsource->copy_parameters(in_vsource->Dparams());
    new_vsource->copy_parameters(in_vsource->Sparams());

    if (_devices.count(nName) > 0) {
        sprintf(error_msg,"duplicate device instance in subCircuit::copy_vsource: %s",nName);
        Abort(error_msg);
    }
    else
        _devices[nName]=new_vsource;

		propagate_port(top,in_vsource->plus_node(),new_vsource->plus_node(),in_inst,prefix);
		propagate_port(top,in_vsource->minus_node(),new_vsource->minus_node(),in_inst,prefix);
return(new_vsource);
}


//-----------------------------------------------------------------------------
// Function: add_port
//
// Purpose: adds a new I/O port to the subCircuit definition with the name
//          port_name.  Also attaches the port to a new net with the same name.
//          This net is created if it doesn't exist.
//
// Side-effects: modifies the circuit database
//
// Notes: 
//-----------------------------------------------------------------------------

port *subCircuit::add_port(char *port_name, port::dirEnum direction) {
    DBG0(cout<<"adding port "<<port_name<<" to "<<name()<<endl;);

    // add the port 
    port *nPort = new port(port_name, direction);
    nPort->device_ref() = this;

    // Attach port to net with same name, create net if necessary
    // This net interfaces to an external net, so set external flag to true.
    char *net_name = strdup(port_name);
    net *nNet = add_net(net_name, true);
    nNet->add_terminal(*nPort);

    nPort->net_ref() = nNet;
    _port_map[net_name] = nPort;

    _port_vec.push_back(nPort);

    return(nPort);
}


//-----------------------------------------------------------------------------
// Function: add_net
//
// Purpose: returns a pointer to the circuit net with the give name.  Creates
//          a new net if necessary. External==false means that the net is
//          purely internal to a subcktinst at some level of hierarchy.
//          External==true means that the net connects to the subcktinst ports,
//          and connects to devices external to the device.  If the supplied
//          external flag is ORed with the net's flag (i.e. this flag is
//          sticly).
//
// Side-effects: modifies the circuit database
//
// Notes: 
//-----------------------------------------------------------------------------

net *subCircuit::add_net(char *net_name, bool external) {
    DBG0(cout<<"adding net "<<net_name<<" to "<<name();
         if (external) cout<<" (external)";
         cout<<endl;);
    static int net_count=0;

    // if net doesn't already exist, create it
    if (! _nets.count(net_name)) {
        net *nNet = new net(net_name,net_count++);
        nNet->external() = external;
        _nets[net_name] = nNet;
        return (nNet);
    }
    // otherwise, clean up and free name string
    else {
        net *existing_net = _nets[net_name];
        existing_net->external() = (existing_net->external() | external);
        free(net_name);
        return (existing_net);
    }
}


//-----------------------------------------------------------------------------
// Function: dump()
//
// Purpose: dump the contents of the circuit class
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void subCircuit::dump(ostream &os) {
   
    // iterators
    map<char*, device*, ltstr>::iterator dev_curr;
    vector<port*>::iterator port_curr;
    map<char*, net *, ltstr>::iterator net_curr;

    // call parent dump
    device::dump(os);

    os<<"Devices:"<<endl;
    for (dev_curr = _devices.begin(); dev_curr != _devices.end(); dev_curr++) {
        os<<"\t"<<(*dev_curr).first<<endl;
    }
    os<<"Ports:"<<endl;
    for (port_curr = _port_vec.begin(); port_curr != _port_vec.end(); port_curr++) {
        os<<"\t"<<(*port_curr)->name()<<endl;
    }        
    os<<"Nets:"<<endl;
    for (net_curr = _nets.begin(); net_curr != _nets.end(); net_curr++) {
        os<<"\t"<<(*net_curr).first<<endl;
    }

    // dump the devices
    os<<"Dumping devices"<<endl;
    for (dev_curr = _devices.begin(); dev_curr != _devices.end(); dev_curr++) {
        (*dev_curr).second->dump(os);
    }

    // dump the ports
    os<<"Dumping ports"<<endl;
    for (port_curr = _port_vec.begin(); port_curr != _port_vec.end(); port_curr++) {
        (*port_curr)->dump(os);
    }

    // dump the nets
    os<<"Dumping nets"<<endl;
    for (net_curr = _nets.begin(); net_curr != _nets.end(); net_curr++) {
        (*net_curr).second->dump(os);
    }


}


// ****************************************************************************
// class: circuit
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: Circuit
//
// Purpose: Constructors. Can take either void or a string containing the name
//          of the circuit.
//
// Side-effects:
//
// Notes: 
//-----------------------------------------------------------------------------

circuit::circuit() : subCircuit(), _models(), _subckts(), _app_commands()
{
    _gnd_node = strdup("VSS");
    _vdd_node = strdup("VDD");
    _doing_subckt = false;
    _curr_subckt = NULL;
}
circuit::circuit(char *name) : subCircuit(name), _models(), _subckts(),
    _app_commands()
{
    _gnd_node = strdup("VSS");
    _vdd_node = strdup("VDD");
    _doing_subckt = false;
    _curr_subckt = NULL;
}

//-----------------------------------------------------------------------------
// Function: ~circuit
//
// Purpose: Destructor. Deletes all of the items in the _subckts and _models
//          maps.
//
// Side-effects:
//
// Notes: 
//-----------------------------------------------------------------------------

circuit::~circuit() {
    // delete the subcircuits
    map<char*, subCircuit*, ltstr>::iterator subckt_curr;
    for (subckt_curr = _subckts.begin(); subckt_curr != _subckts.end(); subckt_curr++) {
        delete (*subckt_curr).second;
    }

    // delete the models
    map<char*, char*, ltstr>::iterator model_curr;
    for (model_curr = _models.begin(); model_curr != _models.end(); model_curr++) {
        free ((*model_curr).first);
        free ((*model_curr).second);
    }

    // delete the app_commands
    for (int i=0; i<_app_commands.size();i++) {
        free(_app_commands[i]);
    }

    // delete other local variables
    free(_gnd_node);
    free(_vdd_node);
}


//-----------------------------------------------------------------------------
// Function: add_bjt
//
// Purpose: adds a new bjt to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_bjt (char *nName, ArgumentVec *args) {
    int arg_count = 0;
    int num_args = 0;
    char *nc = NULL;
    char *nb = NULL;
    char *ne = NULL;
    char *ns = NULL;
    char *mname = NULL;

    char *bjt_name = strdup(nName);
    
    if ((num_args=args->size()) < 4) {
        sprintf(error_msg,"bjt %s must have at least 4 arguments",bjt_name);
        Abort(error_msg);
    }

    if (! (nc=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on bjt %s",bjt_name); Abort(error_msg);
    }
    if (! (nb=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on bjt %s",bjt_name); Abort(error_msg);
    }
    if (! (ne=unpack_identifier((*args)[2]))) {
        sprintf(error_msg,"Syntax error on bjt %s",bjt_name); Abort(error_msg);
    }
    if (! (mname=unpack_identifier((*args)[3]))) {
        sprintf(error_msg,"Syntax error on bjt %s",bjt_name); Abort(error_msg);
    }

    // figure out if args[3] is model or bulk
    arg_count = 4;
    if (! _models.count(mname)) {
    
    // create the bjt
    	bjt *new_bjt = NULL;
    	if (_doing_subckt) {
        	new_bjt=_curr_subckt->add_bjt(bjt_name,nc,nb,ne,ns);
        	new_bjt->parent() = _curr_subckt;
    	}
    	else {
        	new_bjt = subCircuit::add_bjt(bjt_name,nc,nb,ne,ns);
        	new_bjt->parent() = this;
    	}

    // add model name and polarity 
    	new_bjt->model() = mname;
    	if (!strcmp(_models[mname],"NPN"))
        	new_bjt->polarity() = bjt::_NPN_;
    	else if (!strcmp(_models[mname],"PNP"))
        	new_bjt->polarity() = bjt::_PNP_;
    	else {
        	sprintf(error_msg,"model type of bjt %s is not NPN or PNP",bjt_name); Abort(error_msg);
	}

    // next parameter may be optional <aval>
    	if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
        	new_bjt->add_parameter("AREA",((DoubleArgument *)(*args)[arg_count++])->data());
    	else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
        	new_bjt->add_parameter("AREA",((IntegerArgument *)(*args)[arg_count++])->data());

    // add the remaining parameters
    	for (int i=arg_count; i < num_args; i++) {
            if ((*args)[i]->is_a(Argument::_PARAMETER_))
            	new_bjt->add_parameter((ParameterArgument *)(*args)[i]);
            else if ((*args)[i]->is_a(Argument::_STRING_))
            	new_bjt->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
            else {
            	sprintf(error_msg,"syntax error on bjt %s, argument %d.",bjt_name,i);
            	Abort(error_msg);
            }
     	}
    }
}


//-----------------------------------------------------------------------------
// Function: add_coupling
//
// Purpose: adds a new coupling to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_coupling (char *nName, ArgumentVec *args) {
    int num_args = 0;
    char *inductor1 = NULL;
    char *inductor2 = NULL;
  

    char *coupling_name = strdup(nName);
    
    if ((num_args=args->size()) < 2) {
        sprintf(error_msg,"coupling %s must have at least 2 arguments",coupling_name);
        Abort(error_msg);
    }

    if (! (inductor1=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on coupling %s",coupling_name); Abort(error_msg);
    }
    if (! (inductor2=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on coupling %s",coupling_name); Abort(error_msg);
    }

    int arg_count = 2;
    // identify model name, if it exists
    //if (num_args > arg_count && (mname=unpack_S_identifier((*args)[arg_count]))) {
       // arg_count++;
       // if (! _models.count(mname)) {
      //      sprintf(error_msg,"cannot identify model name on capacitor %s",capacitor_name); 
//Abort(error_msg);
       // }
        //if (strcmp(_models[mname],"C")) {
         //   sprintf(error_msg,"model type of capacitor %s is not \"C\"",capacitor_name); 
//Abort(error_msg);
      //  }
  //  }

  coupling *new_coupling = NULL;
    if (_doing_subckt) {
        new_coupling = _curr_subckt->add_coupling(coupling_name,inductor1,inductor2);
        new_coupling->parent() = _curr_subckt;
    }
    else {
        new_coupling = subCircuit::add_coupling(coupling_name,inductor1,inductor2);
        new_coupling->parent() = this;
    }

    // add model name
    //new_coupling->model() = mname;

    // next parameter may be optional <Kval>, followed by optional <TC1<TC2>> pair
    bool found_K = false;
    
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
        new_coupling->add_parameter("K",((DoubleArgument *)(*args)[arg_count++])->data());
        found_K = true;
    }
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
        new_coupling->add_parameter("K",((IntegerArgument *)(*args)[arg_count++])->data());
        found_K = true;
    }
    if (found_K) {
        bool found_tc1 = false;
        if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
            new_coupling->add_parameter("TC1",((DoubleArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
            new_coupling->add_parameter("TC1",((IntegerArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        if (found_tc1) {
            if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
                new_coupling->add_parameter("TC2",((DoubleArgument *)(*args)[arg_count++])->data());
            else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
                new_coupling->add_parameter("TC2",((IntegerArgument *)(*args)[arg_count++])->data());
        }
    }

    // add the remaining parameters
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_coupling->add_parameter((ParameterArgument *)(*args)[i]);
        else if ((*args)[i]->is_a(Argument::_STRING_))
            new_coupling->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
        else {
            sprintf(error_msg,"syntax error on coupling %s, argument %d.",coupling_name,i);
            Abort(error_msg);
        }
    }
}

//-----------------------------------------------------------------------------
// Function: add_capacitor
//
// Purpose: adds a new capacitor to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_capacitor (char *nName, ArgumentVec *args) {
    int num_args = 0;
    char *node1 = NULL;
    char *node2 = NULL;
    char *mname = NULL;

    char *capacitor_name = strdup(nName);
    
    if ((num_args=args->size()) < 2) {
        sprintf(error_msg,"capacitor %s must have at least 2 arguments",capacitor_name);
        Abort(error_msg);
    }

    if (! (node1=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on capacitor %s",capacitor_name); Abort(error_msg);
    }
    if (! (node2=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on capacitor %s",capacitor_name); Abort(error_msg);
    }

    int arg_count = 2;
    // identify model name, if it exists
    if (num_args > arg_count && (mname=unpack_S_identifier((*args)[arg_count]))) {
        arg_count++;
        if (! _models.count(mname)) {
            sprintf(error_msg,"cannot identify model name on capacitor %s",capacitor_name); Abort(error_msg);
        }
        if (strcmp(_models[mname],"C")) {
            sprintf(error_msg,"model type of capacitor %s is not \"C\"",capacitor_name); Abort(error_msg);
        }
    }

    capacitor *new_capacitor = NULL;
    if (_doing_subckt) {
        new_capacitor = _curr_subckt->add_capacitor(capacitor_name,node1,node2);
        new_capacitor->parent() = _curr_subckt;
    }
    else {
        new_capacitor = subCircuit::add_capacitor(capacitor_name,node1,node2);
        new_capacitor->parent() = this;
    }

    // add model name
    new_capacitor->model() = mname;

    // next parameter may be optional <capval>, followed by optional <TC1<TC2>> pair
    bool found_C = false;
    
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
        new_capacitor->add_parameter("C",((DoubleArgument *)(*args)[arg_count++])->data());
        found_C = true;
    }
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
        new_capacitor->add_parameter("C",((IntegerArgument *)(*args)[arg_count++])->data());
        found_C = true;
    }
    if (found_C) {
        bool found_tc1 = false;
        if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
            new_capacitor->add_parameter("TC1",((DoubleArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
            new_capacitor->add_parameter("TC1",((IntegerArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        if (found_tc1) {
            if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
                new_capacitor->add_parameter("TC2",((DoubleArgument *)(*args)[arg_count++])->data());
            else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
                new_capacitor->add_parameter("TC2",((IntegerArgument *)(*args)[arg_count++])->data());
        }
    }

    // add the remaining parameters
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_capacitor->add_parameter((ParameterArgument *)(*args)[i]);
        else if ((*args)[i]->is_a(Argument::_STRING_))
            new_capacitor->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
        else {
            sprintf(error_msg,"syntax error on capacitor %s, argument %d.",capacitor_name,i);
            Abort(error_msg);
        }
    }
}
//-----------------------------------------------------------------------------
// Function: add_diode
//
// Purpose: adds a new diode to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_diode (char *nName, ArgumentVec *args) {
    int num_args = 0;
    char *nplus = NULL;
    char *nminus = NULL;
    char *mname = NULL;

    char *diode_name = strdup(nName);
    
    if ((num_args=args->size()) < 3) {
        sprintf(error_msg,"diode %s must have at least 3 arguments",diode_name);
        Abort(error_msg);
    }

    if (! (nplus=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on diode %s",diode_name); Abort(error_msg);
    }
    if (! (nminus=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on diode %s",diode_name); Abort(error_msg);
    }
    if (! (mname=unpack_S_identifier((*args)[2]))) {
        sprintf(error_msg,"Syntax error on diode %s",diode_name); Abort(error_msg);
    }

    if (! _models.count(mname)) {
        sprintf(error_msg,"cannot identify model name on diode %s",diode_name); Abort(error_msg);
    }

    diode *new_diode = NULL;
    if (_doing_subckt) {
        new_diode = _curr_subckt->add_diode(diode_name,nplus,nminus);
        new_diode->parent() = _curr_subckt;
    }
    else {
        new_diode = subCircuit::add_diode(diode_name,nplus,nminus);
        new_diode->parent() = this;
    }

    // check model name
    new_diode->model() = mname;
    if (strcmp(_models[mname],"D")) {
        sprintf(error_msg,"model type of diode %s is not \"C\"",diode_name); Abort(error_msg);
    }

    // next parameter may be optional <area_val <periphery_val>> pair
    int arg_count = 3;
    bool found_area = false;
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
        new_diode->add_parameter("AREA",((DoubleArgument *)(*args)[arg_count++])->data());
        found_area = true;
    }
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
        new_diode->add_parameter("AREA",((IntegerArgument *)(*args)[arg_count++])->data());
        found_area = true;
    }
    if (found_area) {
        if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
            new_diode->add_parameter("PJ",((DoubleArgument *)(*args)[arg_count++])->data());
        else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
            new_diode->add_parameter("PJ",((IntegerArgument *)(*args)[arg_count++])->data());
    }

    // add the remaining parameters
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_diode->add_parameter((ParameterArgument *)(*args)[i]);
        else if ((*args)[i]->is_a(Argument::_STRING_))
            new_diode->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
        else {
            sprintf(error_msg,"syntax error on diode %s, argument %d.",diode_name,i);
            Abort(error_msg);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: add_inductor
//
// Purpose: adds a new inductor to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_inductor (char *nName, ArgumentVec *args) {
    int num_args = 0;
    char *node1 = NULL;
    char *node2 = NULL;

    char *inductor_name = strdup(nName);
    
    if ((num_args=args->size()) < 2) {
        sprintf(error_msg,"inductor %s must have at least 2 arguments",inductor_name);
        Abort(error_msg);
    }

    if (! (node1=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on inductor %s",inductor_name); Abort(error_msg);
    }
    if (! (node2=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on inductor %s",inductor_name); Abort(error_msg);
    }

    inductor *new_inductor = NULL;
    if (_doing_subckt) {
        new_inductor = _curr_subckt->add_inductor(inductor_name,node1,node2);
        new_inductor->parent() = _curr_subckt;
    }
    else {
        new_inductor = subCircuit::add_inductor(inductor_name,node1,node2);
        new_inductor->parent() = this;
    }

    // next parameter may be optional <lval>
    int arg_count = 2;
    bool found_L = false;
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
        new_inductor->add_parameter("L",((DoubleArgument *)(*args)[arg_count++])->data());
        found_L = true;
    }
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
        new_inductor->add_parameter("L",((IntegerArgument *)(*args)[arg_count++])->data());
        found_L = true;
    }

    // next parameter may be optional <TC1<TC2>> pair
    if (found_L) {
        bool found_tc1 = false;
        if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
            new_inductor->add_parameter("TC1",((DoubleArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
            new_inductor->add_parameter("TC1",((IntegerArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        if (found_tc1) {
            if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
                new_inductor->add_parameter("TC2",((DoubleArgument *)(*args)[arg_count++])->data());
            else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
                new_inductor->add_parameter("TC2",((IntegerArgument *)(*args)[arg_count++])->data());
        }
    }

    // add the remaining parameters
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_inductor->add_parameter((ParameterArgument *)(*args)[i]);
        else if ((*args)[i]->is_a(Argument::_STRING_))
            new_inductor->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
        else {
            sprintf(error_msg,"syntax error on inductor %s, argument %d.",inductor_name,i);
            Abort(error_msg);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: add_isource
//
// Purpose: adds a new isource to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_isource (char *nName, ArgumentVec *args) {
    int num_args = 0;
    char *plus_node = NULL;
    char *minus_node = NULL;

    char *isource_name = strdup(nName);
    
    if ((num_args=args->size()) < 2) {
        sprintf(error_msg,"isource %s must have at least 2 arguments",isource_name);
        Abort(error_msg);
    }

    if (! (plus_node=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on isource %s",isource_name); Abort(error_msg);
    }
    if (! (minus_node=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on isource %s",isource_name); Abort(error_msg);
    }

    isource *new_isource = NULL;
    if (_doing_subckt) {
        new_isource = _curr_subckt->add_isource(isource_name,plus_node,minus_node);
        new_isource->parent() = _curr_subckt;
    }
    else {
        new_isource = subCircuit::add_isource(isource_name,plus_node,minus_node);
        new_isource->parent() = this;
    }
 
    // next parameter may be optional <dcval>
    int arg_count = 2;
    if ( num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
        new_isource->add_parameter("DC",((DoubleArgument *)(*args)[arg_count++])->data());
    else if ( num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
        new_isource->add_parameter("DC",((IntegerArgument *)(*args)[arg_count++])->data());

    // Add the remaining parameters
    // If its not a parameter (lhs=rhs), skip it.  Transfer functions
    // are complex and not implemented yet.
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_isource->add_parameter((ParameterArgument *)(*args)[i]);
        else {
            sprintf(error_msg,"isource %s argument %d: transfer functions unimplemented",isource_name,i);
            Warn(error_msg);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: add_jfet
//
// Purpose: adds a new jfet to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_jfet (char *nName, ArgumentVec *args) {
    int arg_count;
    int num_args;
    char *nd = NULL;
    char *ng = NULL;
    char *ns = NULL;
    char *nb = NULL;
    char *mname = NULL;

    char *jfet_name = strdup(nName);
    
    if ((num_args=args->size()) < 4) {
        sprintf(error_msg,"jfet %s must have at least 4 arguments",jfet_name);
        Abort(error_msg);
    }

    if (! (nd=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on jfet %s",jfet_name); Abort(error_msg);
    }
    if (! (ng=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on jfet %s",jfet_name); Abort(error_msg);
    }
    if (! (ns=unpack_identifier((*args)[2]))) {
        sprintf(error_msg,"Syntax error on jfet %s",jfet_name); Abort(error_msg);
    }
    if (! (mname=unpack_identifier((*args)[3]))) {
        sprintf(error_msg,"Syntax error on jfet %s",jfet_name); Abort(error_msg);
    }

    // figure out if args[3] is model or bulk
    arg_count = 4;
    if (! _models.count(mname)) {
        nb = mname;
        if (num_args <= arg_count) {
            sprintf(error_msg,"cannot identify model name on bjt %s",jfet_name); Abort(error_msg);
        }
        else if (! (mname=unpack_S_identifier((*args)[arg_count++]))) {
            sprintf(error_msg,"Syntax error on jfet %s",jfet_name); Abort(error_msg);
        }
        if (! _models.count(mname)) {
            sprintf(error_msg,"cannot identify model name on jfet %s",jfet_name); Abort(error_msg);
        }
    }

    // create the jfet
    jfet *new_jfet = NULL;
    if (_doing_subckt) {
        new_jfet = _curr_subckt->add_jfet(jfet_name,nd,ng,ns,nb);
        new_jfet->parent() = _curr_subckt;
    }
    else {
        new_jfet = subCircuit::add_jfet(jfet_name,nd,ng,ns,nb);
        new_jfet->parent() = this;
    }

    // add model name and polarity 
    new_jfet->model() = mname;
    if (!strcmp(_models[mname],"NJF"))
        new_jfet->polarity() = jfet::_NFET_;
    else if (!strcmp(_models[mname],"PJF"))
        new_jfet->polarity() = jfet::_PFET_;
    else {
        sprintf(error_msg,"model type of jfet %s is not NJF or PJF",jfet_name); Abort(error_msg);
    }

    // next parameter may be optional <AREA>
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
        new_jfet->add_parameter("AREA",((DoubleArgument *)(*args)[arg_count++])->data());
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
        new_jfet->add_parameter("AREA",((IntegerArgument *)(*args)[arg_count++])->data());

    // add the remaining parameters
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_jfet->add_parameter((ParameterArgument *)(*args)[i]);
        else if ((*args)[i]->is_a(Argument::_STRING_))
            new_jfet->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
        else {
            sprintf(error_msg,"syntax error on jfet %s, argument %d.",jfet_name,i);
            Abort(error_msg);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: add_mosfet
//
// Purpose: adds a new mosfet to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_mosfet (char* nName, ArgumentVec *args) {
    int arg_count = 0;
    int num_args = 0;
    char *nd = NULL;
    char *ng = NULL;
    char *ns = NULL;
    char *nb = NULL;
    char *mname = NULL;

    char *mosfet_name = strdup(nName);
    
    if ((num_args=args->size()) < 4) {
        sprintf(error_msg,"mosfet %s must have at least 4 arguments",mosfet_name);
        Abort(error_msg);
    }

    if (! (nd=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on mosfet %s",mosfet_name); Abort(error_msg);
    }
    if (! (ng=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on mosfet %s",mosfet_name); Abort(error_msg);
    }
    if (! (ns=unpack_identifier((*args)[2]))) {
        sprintf(error_msg,"Syntax error on mosfet %s",mosfet_name); Abort(error_msg);
    }
    if (! (mname=unpack_identifier((*args)[3]))) {
        sprintf(error_msg,"Syntax error on mosfet %s",mosfet_name); Abort(error_msg);
    }

    // figure out if args[3] is model or bulk
    arg_count = 4;
    if (! _models.count(mname)) {
        nb = mname;
        if (num_args <= arg_count) {
            sprintf(error_msg,"cannot identify model name on bjt %s",mosfet_name);
      }
        else if (! (mname=unpack_S_identifier((*args)[arg_count++]))) {
            sprintf(error_msg,"Syntax error on mosfet %s",mosfet_name); Abort(error_msg);
        }
        if (! _models.count(mname)) {
            sprintf(error_msg,"cannot identify model name on mosfet %s",mosfet_name); Abort(error_msg);
        }
    }

    // create the mosfet
    mosfet *new_mosfet = NULL;
    if (_doing_subckt) {
        new_mosfet = _curr_subckt->add_mosfet(mosfet_name,nd,ng,ns,nb);
        new_mosfet->parent() = _curr_subckt;
    }
    else {
        new_mosfet = subCircuit::add_mosfet(mosfet_name,nd,ng,ns,nb);
        new_mosfet->parent() = this;
    }

    // add model name and polarity 
    new_mosfet->model() = mname;
    if (!strcmp(_models[mname],"NMOS")) {
        new_mosfet->polarity() = mosfet::_NFET_;
    }
    else if (!strcmp(_models[mname],"PMOS")) {
        new_mosfet->polarity() = mosfet::_PFET_;
    }
    else {
        sprintf(error_msg,"model type of mosfet %s is not NMOS or PMOS",mosfet_name); Abort(error_msg);
    }

    // next parameter may be optional <lval wval> pair (both must appear)
    bool found_lval = false;
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
        new_mosfet->add_parameter("L",((DoubleArgument *)(*args)[arg_count++])->data());
        found_lval = true;
    }
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
        new_mosfet->add_parameter("L",((IntegerArgument *)(*args)[arg_count++])->data());
        found_lval = true;
    }
    if (found_lval) {
        if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
            new_mosfet->add_parameter("W",((DoubleArgument *)(*args)[arg_count++])->data());
        else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
                new_mosfet->add_parameter("W",((IntegerArgument *)(*args)[arg_count++])->data());
        else {
            sprintf(error_msg,"mosfet %s specifies lval but not wval",mosfet_name); Abort(error_msg);
        }
    }

    // add the remaining parameters
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_mosfet->add_parameter((ParameterArgument *)(*args)[i]);
        else if ((*args)[i]->is_a(Argument::_STRING_))
            new_mosfet->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
        else {
            sprintf(error_msg,"syntax error on mosfet %s, argument %d.",mosfet_name,i);
            Abort(error_msg);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: add_resistor
//
// Purpose: adds a new resistor to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_resistor (char* nName, ArgumentVec *args) {
    int num_args = 0;
    char *node1 = NULL;
    char *node2 = NULL;
    char *mname = NULL;

    char *resistor_name = strdup(nName);
    
    if ((num_args=args->size()) < 2) {
        sprintf(error_msg,"resistor %s must have at least 2 arguments",resistor_name);
        Abort(error_msg);
    }

    if (! (node1=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on resistor %s",resistor_name); Abort(error_msg);
    }
    if (! (node2=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on resistor %s",resistor_name); Abort(error_msg);
    }

    int arg_count = 2;
    if (num_args > arg_count && (mname=unpack_S_identifier((*args)[arg_count]))) {
        arg_count++;
        if (! _models.count(mname)) {
            sprintf(error_msg,"cannot identify model name on resistor %s",resistor_name); Abort(error_msg);
        }
        if (strcmp(_models[mname],"R")) {
            sprintf(error_msg,"model type of resistor %s is not \"R\"",resistor_name); Abort(error_msg);
        }
    }

    resistor *new_resistor = NULL;
    if (_doing_subckt) {
        new_resistor = _curr_subckt->add_resistor(resistor_name,node1,node2);
        new_resistor->parent() = _curr_subckt;
    }
    else {
        new_resistor = subCircuit::add_resistor(resistor_name,node1,node2);
        new_resistor->parent() = this;
    }

    // add model name
    new_resistor->model() = mname;

    // next parameter may be optional <Rval>, followed by optional <TC1<TC2>> pair
    bool found_R = false;
    
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
        new_resistor->add_parameter("R",((DoubleArgument *)(*args)[arg_count++])->data());
        found_R = true;
    }
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
        new_resistor->add_parameter("R",((IntegerArgument *)(*args)[arg_count++])->data());
        found_R = true;
    }
    if (found_R) {
        bool found_tc1 = false;
        if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_)) {
            new_resistor->add_parameter("TC1",((DoubleArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_)) {
            new_resistor->add_parameter("TC1",((IntegerArgument *)(*args)[arg_count++])->data());
            found_tc1 = true;
        }
        if (found_tc1) {
            if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
                new_resistor->add_parameter("TC2",((DoubleArgument *)(*args)[arg_count++])->data());
            else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
                new_resistor->add_parameter("TC2",((IntegerArgument *)(*args)[arg_count++])->data());
        }
    }

    // add the remaining parameters
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_resistor->add_parameter((ParameterArgument *)(*args)[i]);
        else if ((*args)[i]->is_a(Argument::_STRING_))
            new_resistor->add_parameter(((StringArgument *)(*args)[i])->data(),"TRUE");
        else {
            sprintf(error_msg,"syntax error on resistor %s, argument %d.",resistor_name,i);
            Abort(error_msg);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: add_subcktinst
//
// Purpose: adds a new subcktinst to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_subcktinst (char* nName, ArgumentVec *args) {
    int num_args = 0;
    int num_ports = 0;
    
    char *sub_name = NULL;
    char *temp_name = NULL;
    vector<char*> nodes;

    char *subcktinst_name = strdup(nName);
    
    num_args = args->size();
    // Iterate through ports and unpack node names.
    bool done=false;
    for (int i=0; i<num_args && !done; i++) {
        if (temp_name=unpack_identifier((*args)[i])) {
            num_ports++;
            nodes.push_back(temp_name);
        }
        else
            done = true;
    }

    if (num_ports < 2) {
        sprintf(error_msg,"subcircuit instance %s must have at least 2 identifier arguments",subcktinst_name);
        Abort(error_msg);
    }

    // remove the last name from list, its actually the subcircuit name
    sub_name = nodes.back();
    nodes.pop_back();

    // create the instance
    subCircuit *sub_def = _subckts[sub_name];
    if (!sub_def) {
        sprintf(error_msg,
                "sorry, subcircuit %s not defined before instance %s in same file.\n
                Multi-file searching not supported, and not yet a 2-pass parser.",
                sub_name,subcktinst_name);
        Abort(error_msg);
    }        

    subcktinst *new_subcktinst = NULL;
    if (_doing_subckt) {
        new_subcktinst = _curr_subckt->add_subcktinst(subcktinst_name,*sub_def,nodes);
        new_subcktinst->parent() = _curr_subckt;
    }
    else {
        new_subcktinst = subCircuit::add_subcktinst(subcktinst_name,*sub_def,nodes);
        new_subcktinst->parent() = this;
    }
    new_subcktinst->model_name()=sub_name;

    // add the remaining parameters
    for (int i=num_ports; i < num_args; i++) {
        if (!(*args)[i]->is_a(Argument::_PARAMETER_)) {
            sprintf(error_msg,"syntax error on subckt instance %s, argument %d is not a parameter",subcktinst_name,i);
            Abort(error_msg);
        }
        new_subcktinst->add_parameter((ParameterArgument *)(*args)[i]);
    }
}


//-----------------------------------------------------------------------------
// Function: add_vsource
//
// Purpose: adds a new vsource to the circuit.  This routine is responsible for
//          unpacking the I/O port net names from the argument list and
//          calling the corresponding subCircuit routine.  Also adds the model
//          name and model type to the circuit, and unpacks and adds any
//          parameters.
//
//          If a subcircuit is currently being defined, adds the device to the
//          current subcircuit, otherwise adds it to the top-level circuit.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::add_vsource (char* nName, ArgumentVec *args) {
    int num_args = 0;
    char *plus_node = NULL;
    char *minus_node = NULL;
    
    char *vsource_name = strdup(nName);
    
    if ((num_args=args->size()) < 2) {
        sprintf(error_msg,"vsource %s must have at least 2 arguments",vsource_name);
        Abort(error_msg);
    }

    if (! (plus_node=unpack_identifier((*args)[0]))) {
        sprintf(error_msg,"Syntax error on vsource %s",vsource_name); Abort(error_msg);
    }
    if (! (minus_node=unpack_identifier((*args)[1]))) {
        sprintf(error_msg,"Syntax error on vsource %s",vsource_name); Abort(error_msg);
    }

    vsource *new_vsource = NULL;
    if (_doing_subckt) {
        new_vsource = _curr_subckt->add_vsource(vsource_name,plus_node,minus_node);
        new_vsource->parent() = _curr_subckt;
    }
    else {
        new_vsource = subCircuit::add_vsource(vsource_name,plus_node,minus_node);
        new_vsource->parent() = this;
    }

    // next parameter may be optional <dcval>
    int arg_count = 2;
    if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_DOUBLE_))
        new_vsource->add_parameter("DC",((DoubleArgument *)(*args)[arg_count++])->data());
    else if (num_args > arg_count && (*args)[arg_count]->is_a(Argument::_INTEGER_))
        new_vsource->add_parameter("DC",((IntegerArgument *)(*args)[arg_count++])->data());

    // Add the remaining parameters
    // If its not a parameter (lhs=rhs), skip it.  Transfer functions
    // are complex and not implemented yet.
    for (int i=arg_count; i < num_args; i++) {
        if ((*args)[i]->is_a(Argument::_PARAMETER_))
            new_vsource->add_parameter((ParameterArgument *)(*args)[i]);
        else {
            sprintf(error_msg,"vsource %s argument %d: transfer functions unimplemented",vsource_name,i);
            Warn(error_msg);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: begin_subckt
//
// Purpose: start the definition of a new subcircuit
//
// Side-effects: modifies the circuit database
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::begin_subckt (ArgumentVec *args) {

    if (_doing_subckt)
        Abort("recursive subckt definition not permitted");
    if (args->size() < 1)
        Abort(".SUBCKT/.MACRO name missing");
    if (!(*args)[0]->is_a(Argument::_STRING_))
        Abort(".SUBCKT/.MACRO name field is not a string");

    char *new_name = unpack_S_identifier((*args)[0]);
    if (_subckts.count(new_name)) {
        sprintf(error_msg,".SUBCKT/.MACRO %s already defined",new_name);
        Abort(error_msg);
    }

    subCircuit *new_subckt = new subCircuit(new_name);
    _doing_subckt = true;
    _curr_subckt = new_subckt;

    DBG0(cout<<"beginning a subckt: "<<new_name<<endl;);

    _subckts[new_name] = new_subckt;
    
    // process the remaining arguments
    bool doing_ports = true;
    for (int i=1; i<args->size(); i++) {
        if (!(*args)[i]->is_a(Argument::_STRING_))
            doing_ports = false;
        if (doing_ports) {
            new_subckt->add_port(unpack_S_identifier((*args)[i]));
        }
        else {
            if (!(*args)[i]->is_a(Argument::_PARAMETER_)) {
                sprintf(error_msg,".SUBCKT/.MACRO %s contains illegal argument",new_name);
                Warn(error_msg);
                (*args)[i]->dump(cerr);
                Abort("Fatal error");
            }
            new_subckt->add_parameter((ParameterArgument *)(*args)[i]);
        }
    }
}


//-----------------------------------------------------------------------------
// Function: end_subckt
//
// Purpose:
//
// Side-effects:
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::end_subckt (Argument *arg) {
    DBG0(cout<<"ending subckt: ";if (arg) arg->dump(cout);cout<<endl;);

    if (!_doing_subckt)
        Abort(".ENDS/.EOM does not match a .SUBCKT/.MACRO");
    if (arg && !arg->is_a(Argument::_STRING_))
        Abort(".ENDS/.EOM with a non-string argument");
    if (arg && strcmp( ((StringArgument *)arg)->data(),_curr_subckt->name())) {
        sprintf(error_msg,".ENDS/.EOM \"%s\" does not match the correct .SUBCKT/.MACRO \"%s\"",
                ((StringArgument *)arg)->data(), _curr_subckt->name() );
        Abort(error_msg);
    }

    _doing_subckt = false;
    _curr_subckt = NULL;
}


//-----------------------------------------------------------------------------
// Function: add_model
//
// Purpose: adds a model definition to the circuit
//
// Side-effects: the _models symbol table is modified
//
// Notes: the params field is currently unutilized
//-----------------------------------------------------------------------------

void circuit::add_model(Argument *name_arg, Argument *type_arg, ArgumentVec *params) {
    char *model_name = NULL;
    char *model_type = NULL;

    if ( (name_arg->is_a(Argument::_STRING_))
         && (type_arg->is_a(Argument::_STRING_))) {
        model_name = unpack_S_identifier(name_arg);
        model_type = unpack_S_identifier(type_arg);

        _models[model_name] = model_type;

        DBG0(cout<<"adding model: ";
        name_arg->dump(cout); cout<<" ";
        type_arg->dump(cout);cout<<endl;);
    }
    else {
        Warn("non-string argument passed to .MODEL statement");
        cerr<<"name argument: ";name_arg->dump(cout);
        cerr<<"type argument: ";type_arg->dump(cout);
    }
}


//-----------------------------------------------------------------------------
// Function: process_comment
//
// Purpose: comments may contain commands to tools which take the spice
//          deck as input.  This routing extracts those commands and inserts
//          them into the circuit netlist.  Some commands simply operate by
//          adding properties to particular circuit devices, those are
//          processed here.  Any unknown commands are simply added to the
//          _app_commands list for processing by the parent application.
//
// Side-effects:
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::process_comment (StringArgument *comment) {
    DBG0(cout<<"processing a comment"<<endl;);

    char *text = strdup(comment->data());
    char ct[4];
    char *token;

    // get first token not whitespace or '*'
    sprintf(ct,"* \t");
    token = strtok(text,ct);

    // exit if comment not a command
    if (!token || strcmp(token,"hparse:")) {
        free(text);
        return;
    }
        
    // Process commands that can be interpreted by the
    // parser (i.e. that act by adding properties to
    // certain devices in the netlist.)
    sprintf(ct," \t");
    token = strtok(NULL,ct);
    if (!token) {
        cout<<">>> "<<comment->data()<<endl;
        cout<<"syntax error: "<<text<<endl;
        Abort("syntax error");
    }

    // 'hparse: port <port_name> <[input,output,bidir,vdd,gnd]>'
    // Action is to add a port to the circuit with the supplied port name and direction.
    else if (!strcmp(token,"port")) {

        token = strtok(NULL,ct);
        if (!token) {
            cout<<">>> "<<comment->data()<<endl;
            cout<<"USAGE \'hparse: port <port_name> <[input,output,bidir,vdd,gnd]\'"<<endl;
            Abort("syntax error");
        }
        char *port_name = strdup(token);

        token = strtok(NULL,ct);
        if (!token) {
            cout<<">>> "<<comment->data()<<endl;
            cout<<"USAGE \'hparse: port <port_name> <[input,output,bidir,vdd,gnd]\'"<<endl;
            Abort("syntax error");
        }

        if (!strcmp(token,"input")) { add_port(port_name,port::_INPUT_); }
        else if (!strcmp(token,"output")) { add_port(port_name,port::_OUTPUT_); }
        else if (!strcmp(token,"bidir")) { add_port(port_name,port::_BIDIR_); }
        else if (!strcmp(token,"vdd")) {
            if (_vdd_node && strcmp(_vdd_node,port_name)) {
                //cout<<">>> "<<comment->data()<<endl;
                //Abort("more than one VDD node specified in spice netlist");
								free(_vdd_node);
            }
            _vdd_node = port_name;
        }
        else if (!strcmp(token,"gnd")) {
            if (_gnd_node && strcmp(_gnd_node,port_name)) {
                //cout<<">>> "<<comment->data()<<endl;
                //Abort("more than one GND node specified in spice netlist");
								free(_gnd_node);
            }
            _gnd_node = port_name;
        }
        else {
            cout<<">>> "<<comment->data()<<endl;
            cout<<"USAGE \'hparse: port <port_name> <[input,output,bidir,vdd,gnd]\'"<<endl;
            Abort("syntax error");
        }
    }
    else {
        // save remaining commands in _app_command array
        // for further processing by the application
        _app_commands.push_back(strdup(comment->data()));
        free(text);
    }
}

//-----------------------------------------------------------------------------
// Function: dump()
//
// Purpose: dump the contents of the circuit class
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void circuit::dump(ostream &os) {
   
    // iterators
    map<char*, char*, ltstr>::iterator model_curr;
    map<char*, subCircuit *, ltstr>::iterator subckt_curr;

    os<<endl<<"Dumping Circuit"<<endl<<endl;

    // call parent dump
    subCircuit::dump(os);
    
    os<<"Models:"<<endl;
    for (model_curr = _models.begin(); model_curr != _models.end(); model_curr++) {
        os<<"\t"<<(*model_curr).first<<", ";
        os<<(*model_curr).second<<endl;
    }
    os<<"Subckts:"<<endl;
    for (subckt_curr = _subckts.begin(); subckt_curr != _subckts.end(); subckt_curr++) {
        os<<"\t"<<(*subckt_curr).first<<endl;
    }
    os<<"Application Commands:"<<endl;
    for(int i=0; i<_app_commands.size(); i++) {
        cout<<"    "<<_app_commands[i]<<endl;
    }

    // dump the subcircuits
    os<<endl<<"Dumping subCircuits"<<endl<<endl;
    for (subckt_curr = _subckts.begin(); subckt_curr != _subckts.end(); subckt_curr++) {
        (*subckt_curr).second->dump(os);
        os<<endl;
    }
}

//-----------------------------------------------------------------------------
// Function: unpack_identifier
//
// Purpose: extracts an identifier from a supplied Argument.  The argument can be
//          either a StringArgument or IntegerArgument.  Intended to extract
//          node names, which can be either a symbolic name or a node number.
//          Returns the identifier as a string.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------
char *circuit::unpack_identifier(Argument *arg) {
    char temp1[1000];
    
    if (arg->is_a(Argument::_STRING_))
        return strdup( ((StringArgument *)arg)->data() );
    else if (arg->is_a(Argument::_INTEGER_)) {
        sprintf(temp1,"%d",((IntegerArgument *)arg)->data());
        return strdup(temp1);
    }
    else return NULL;
        
}


//-----------------------------------------------------------------------------
// Function: unpack_S_identifier
//
// Purpose: extracts an identifier from a supplied Argument.  Similar to
//          unpack_identifier, except the identifier must be a string.
//          Some arguments, like model_name cannot be numbers.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------
char *circuit::unpack_S_identifier(Argument *arg) {
    char temp1[1000];
    char *temp2 = NULL;
    
    if (arg->is_a(Argument::_STRING_))
        return strdup( ((StringArgument *)arg)->data() );
    else return NULL;
        
}


//-----------------------------------------------------------------------------
// Function: flatten
//
// Purpose: given another (possibly hierarchical) circuit as input, constructs
//          a new circuit with all of the subckt instances replaced by their
//          contents.  Net names internal to the subckts are given hierarchical
//          names.
//
// Side-effects: a new circuit is created.
//
// Notes: The current circuit is required to be empty.  This method acts as
//        a constrcutor.  This routine allocates its own namespace, it is
//        permissible to delete the old circuit after this method is
//        called.
//
//
//-----------------------------------------------------------------------------
void circuit::flatten(circuit *in_circ) {
    subcktinst *sub;
    char* new_prefix;

    //cout<<"---> flattening circuit: "<<in_circ->name()<<endl;
    name() = strdup(in_circ->name());

		//cout<<"Models..."<<endl;
		// step through each model in in_circ and add to *this
		map<char*,char*,ltstr>::iterator model_curr;
		for (model_curr=in_circ->models().begin();model_curr!=in_circ->models().end();model_curr++) {
        //cout<<"adding model: "<<(*model_curr).first<<endl;
				models()[strdup((*model_curr).first)]=strdup((*model_curr).second);
		}

		//cout<<"Ports..."<<endl;
    // step through each port in in_circ and add to *this
		// (this adds the ports as nets too)
    vector<port*>::iterator port_curr;
    for (port_curr = in_circ->port_vec().begin(); port_curr != in_circ->port_vec().end(); port_curr++) {
        //cout<<"adding port: "<<(*port_curr)->name()<<endl;
				add_port(strdup((*port_curr)->name()),(*port_curr)->direction());		
    }

		//cout<<"Devices..."<<endl;
    // step through each device
    map<char*, device*, ltstr>::iterator dev_curr;
    for (dev_curr = in_circ->devices().begin(); dev_curr != in_circ->devices().end(); dev_curr++) {
        device *dev = (*dev_curr).second;
        switch(dev->which()) {
            case (device::_SUBCKTINST_):
                sub = (subcktinst *)dev;
                new_prefix = strdup(sub->name());
                flatten_subcktinst(in_circ, sub, new_prefix);
                free (new_prefix);
                break;
            case (device::_BJT_):
                copy_bjt(in_circ,(bjt *)dev, NULL, NULL);
                break;
            case (device::_CAPACITOR_):
                copy_capacitor(in_circ,(capacitor *)dev, NULL, NULL);
                break;
            case (device::_DIODE_):
                copy_diode(in_circ,(diode *)dev, NULL, NULL);
                break;
            case (device::_INDUCTOR_):
                copy_inductor(in_circ,(inductor *)dev, NULL, NULL);
                break;
            case (device::_ISOURCE_):
                copy_isource(in_circ,(isource *)dev, NULL, NULL);
                break;
            case (device::_JFET_):
                copy_jfet(in_circ,(jfet *)dev, NULL, NULL);
                break;
            case (device::_MOSFET_):
                copy_mosfet(in_circ,(mosfet *)dev, NULL, NULL);
                break;
            case (device::_RESISTOR_):
                copy_resistor(in_circ,(resistor *)dev, NULL, NULL);
                break;
            case (device::_VSOURCE_):
                copy_vsource(in_circ,(vsource *)dev, NULL, NULL);
                break;
        }
    }
}


//-----------------------------------------------------------------------------
// Function: flatten_subcktinst
//
// Purpose: private method to handle the flattening of subcktinst instances
//          into the top-level circuit.  Should only be called by
//          circuit::flatten().
//
// Side-effects: new device instances are created
//
// Notes: This routine allocates its own namespace, it is permissible to
//        delete the old circuit after this method is called.
//
//-----------------------------------------------------------------------------
void circuit::flatten_subcktinst(circuit *in_circ, subcktinst *in_inst, char *prefix) {
    subcktinst *sub;
    char* new_prefix;
    
    subCircuit *in_sub = in_circ->subckts()[in_inst->model_name()];
    //cout<<"flattening through subCircuit: "<<in_sub->name();
    //cout<<" instance: "<<in_inst->name()<<endl;
    
    // step through each device
    map<char*, device*, ltstr>::iterator dev_curr;
    for (dev_curr = in_sub->devices().begin(); dev_curr != in_sub->devices().end(); dev_curr++) {
        device *dev = (*dev_curr).second;
        switch(dev->which()) {
            case (device::_SUBCKTINST_):
                sub = (subcktinst *)dev;
                new_prefix = strcat(prefix, sub->name(), '_');
                if (!strcmp(sub->model_name(),in_sub->name())) {
                    sprintf(error_msg,"recursive subckt instantiation detected on instance: %s",sub->name());
                    Abort(error_msg);
                }
                flatten_subcktinst(in_circ, sub, new_prefix);
                break;
            case (device::_BJT_):
                copy_bjt(in_circ,(bjt *)dev, in_inst, prefix);
                break;
            case (device::_CAPACITOR_):
                copy_capacitor(in_circ,(capacitor *)dev, in_inst, prefix);
                break;
            case (device::_DIODE_):
                copy_diode(in_circ,(diode *)dev, in_inst, prefix);
                break;
            case (device::_INDUCTOR_):
                copy_inductor(in_circ,(inductor *)dev, in_inst, prefix);
                break;
            case (device::_ISOURCE_):
                copy_isource(in_circ,(isource *)dev, in_inst, prefix);
                break;
            case (device::_JFET_):
                copy_jfet(in_circ,(jfet *)dev, in_inst, prefix);
                break;
            case (device::_MOSFET_):
                copy_mosfet(in_circ,(mosfet *)dev, in_inst, prefix);
                break;
            case (device::_RESISTOR_):
                copy_resistor(in_circ,(resistor *)dev, in_inst, prefix);
                break;
            case (device::_VSOURCE_):
                copy_vsource(in_circ,(vsource *)dev, in_inst, prefix);
                break;
        }
    }
}

/*****************************************************************************/
