//-----------------------------------------------------------------------------
// 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: device.cch
//
// Purpose: Models a netlist device
//
// Remarks: 
//
// History: 03/22/97 - MAR - created.
//          01/26/04 - STF - Modified to pass information to
//		       Automatic Schematics Generation library
//		       linked to xcircuit application. Addition of
//		       Add2TermModule(), Add3TermModule(), etc.
//
// Copyright (c) 1997 Michael A. Riepe
// Copyright (c) 2004 Stephen T. Frezza
//
// RCS Version:
//     $Id: device.cc,v 1.1.1.1 2002/05/01 14:23:27 mguthaus Exp $
//-----------------------------------------------------------------------------

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <map>
#include <vector>
#include <list>
using namespace std;
#include "defs.hh"
#include "utils.hh"
#include "device.hh"

#define _USING_CC
#include "../../asg/asg_parse_support.h"
#undef _USING_CC

// ****************************************************************************
// class: device
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: device()
//
// Purpose: Constructors.  Can take either void, a string containing the
//          name of the device, or another device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

device::device() : _Dparams(), _Sparams() {
    _name = NULL;
    //_number = 0;
    _parent = NULL;
}
device::device(char *name) : _Dparams() , _Sparams() {
    _name = name;
    //_number = atoi(name+1); // skips first M or N
    _parent = NULL;
}
device::device(device &copy) : _Dparams() , _Sparams() {
		//cout << "device copy constructor"<<endl;
    _name = strdup(copy.name());
    //_number = copy.number();
    _parent = NULL;
    
    // copy Dparams
    map<char*, double, ltstr>::iterator d_iter;
    for (d_iter=copy.Dparams().begin(); d_iter != copy.Dparams().end(); d_iter++)
        _Dparams[strdup((*d_iter).first)] = (*d_iter).second;

    // copy Sparams
    map<char*, char*, ltstr>::iterator s_iter;
    for (s_iter=copy.Sparams().begin(); s_iter != copy.Sparams().end(); d_iter++)
        _Sparams[strdup((*s_iter).first)] = strdup((*s_iter).second);
}

//-----------------------------------------------------------------------------
// Function: ~device()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

device::~device() {
		DBG0(cout <<"device destructor: "<<_name<<endl;);
    free (_name);

    map<char*, double, ltstr>::iterator D_curr;
    for (D_curr = _Dparams.begin(); D_curr != _Dparams.end(); D_curr++) {
        free ((*D_curr).first);
    }
    map<char*, char*, ltstr>::iterator S_curr;
    for (S_curr = _Sparams.begin(); S_curr != _Sparams.end(); S_curr++) {
        free ((*S_curr).first);
        free ((*S_curr).second);
    }
}

//-----------------------------------------------------------------------------
// Function: set_parameter
//
// Purpose: sets a parameter pair in the device.  May accept a
//          a string/integer pair, a string/double pair, or a string/string pair.
//          Integers are converted to doubles.
//
// Side-effects: modifies the device database
//
// Notes: 
//-----------------------------------------------------------------------------
void device::set_parameter(char *pName, int pValue) {
    DBG0(cout<<"setting parameter "<<name()<<" to "<<pValue<<endl;); 
		assert(_Dparams[pName]);
    _Dparams[pName] = (double) pValue;
}	
void device::set_parameter(char *pName, double pValue) {
    DBG0(cout<<"setting parameter "<<name()<<" to "<<pValue<<endl;); 
		assert(_Dparams[pName]);
    _Dparams[pName] = pValue;
}	
void device::set_parameter(char *pName, char *pValue) {
    DBG0(cout<<"setting parameter "<<name()<<" to "<<pValue<<endl;); 
		assert(_Sparams[pName]);
		free(_Sparams[pName]);
    _Sparams[pName] = strdup(pValue);
}	
//-----------------------------------------------------------------------------
// Function: add_parameter
//
// Purpose: adds a parameter pair to the device.  May accept a
//          ParameterArgument pointer, a string/integer pair,
//          a string/double pair, or a string/string pair.
//          Integers are converted to doubles.
//
// Side-effects: modifies the device database
//
// Notes: 
//-----------------------------------------------------------------------------

void device::add_parameter(char *pName, int pValue) {
    DBG0(cout<<"adding parameter to "<<name()<<endl;); 
    char *new_name = strdup(pName);
    _Dparams[new_name] = (double) pValue;
}
void device::add_parameter(char *pName, double pValue) {
    DBG0(cout<<"adding parameter to "<<name()<<endl;);
    char *new_name = strdup(pName);
    _Dparams[new_name] = pValue;
}
void device::add_parameter(char *pName, char *pValue) {
    DBG0(cout<<"adding parameter to "<<name()<<endl;);
    char *new_name = strdup(pName);
    _Sparams[new_name] = strdup(pValue);
}
void device::add_parameter(ParameterArgument *arg) {
    DBG0(cout<<"adding parameter to "<<name()<<endl;arg->dump(cout);cout<<endl;);
    if (arg->arguments().size() > 1) {
        sprintf(error_msg,"device %s: Multi-valued parameters are not yet supported on devices",name());
        Warn(error_msg);
        cerr<<"offending parameter: ";arg->dump(cerr);cout<<endl;
    }
    char *new_lhs = strdup(arg->lhs());
    switch((arg->arguments())[0]->which()) {
        case (Argument::_INTEGER_) :
            if ( _Dparams.count(new_lhs ) ) {
                arg->dump(cerr);
                Abort("Parameter multiply defined");
            }
            _Dparams[new_lhs] = (double) ((IntegerArgument *)(arg->arguments())[0])->data();
            break;
        case (Argument::_DOUBLE_) :
            if ( _Dparams.count(new_lhs ) ) {
                arg->dump(cerr);
                Abort("Parameter multiply defined");
            }
            _Dparams[new_lhs] = ((DoubleArgument *)(arg->arguments())[0])->data();
            break;
        case (Argument::_STRING_) :
            if (_Sparams.count(new_lhs)) {
                arg->dump(cerr);
                Abort("Parameter multiply defined");
            }
            _Sparams[new_lhs] = strdup(((StringArgument *)(arg->arguments())[0])->data());
            break;
        default:
            arg->dump(cerr);
            Warn("Only string and number parameters currently supported on devices");
    }
}


//-----------------------------------------------------------------------------
// Function: copy_parameters
//
// Purpose: copies either a list of double parameters or a list of string
//          parameters into the appropriate parameter list
//
// Side-effects: new parameters may be added to the device
//
// Notes: 
//-----------------------------------------------------------------------------

void device::copy_parameters(map<char*, double, ltstr> Dp) {
    map<char*, double, ltstr>::iterator D_curr;
    for (D_curr = Dp.begin(); D_curr != Dp.end(); D_curr++)
        add_parameter((*D_curr).first, (*D_curr).second);
}

void device::copy_parameters(map<char*, char*, ltstr> Sp) {
    map<char*, char *, ltstr>::iterator S_curr;
    for (S_curr = Sp.begin(); S_curr != Sp.end(); S_curr++)
        add_parameter((*S_curr).first,(*S_curr).second);
}


//-----------------------------------------------------------------------------
// Function: add_port
//
// Purpose: adds a new port with the given name to a subcktinst.  Used by
//          the constructors.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

port *device::add_port(char *port_name) {
    DBG0(cout<<"adding port "<<port_name<<" to deFinished parsing input. Elapsed time: 0
        Finished flattening netlist. Elapsed time: 0
Placing internal modules...Segmentation fault
vice "<<name()<<endl;);

    char *new_name = strdup(port_name);
    port *new_port = new port(new_name);
    new_port->device_ref()=this;

    return (new_port);
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the device class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void device::dump(ostream &os) {
    // iterators
    map<char*, double, ltstr>::iterator D_curr;
    map<char*, char *, ltstr>::iterator S_curr;

    os<<"name: "<<name()<<": "<<endl;
    os<<"Dparams:"<<endl;
    for (D_curr = _Dparams.begin(); D_curr != _Dparams.end(); D_curr++) {
        os<<"\t"<<(*D_curr).first<<"=";
        os<<(*D_curr).second<<endl;
    }
    os<<"Sparams:"<<endl;
    for (S_curr = _Sparams.begin(); S_curr != _Sparams.end(); S_curr++) {
        os<<"\t"<<(*S_curr).first<<"=";
        os<<(*S_curr).second<<endl;
    }
}


//-----------------------------------------------------------------------------
// Function: dump_params
//
// Purpose: dumps a space-separated list of the device parameters.  Used by
//          derived class dump routines.
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void device::dump_params(ostream &os) {
    // iterators
    map<char*, double, ltstr>::iterator D_curr;
    map<char*, char *, ltstr>::iterator S_curr;

    for (D_curr = _Dparams.begin(); D_curr != _Dparams.end(); D_curr++) {
        os<<(*D_curr).first<<"=";
        os<<(*D_curr).second<<" ";
    }
    for (S_curr = _Sparams.begin(); S_curr != _Sparams.end(); S_curr++) {
        os<<(*S_curr).first<<"=";
        os<<(*S_curr).second<<" ";
    }
}


// ****************************************************************************
// class: subcktinst
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: subcktinst()
//
// Purpose: Constructors.  Can take either void, a string containing the
//          name of the device, a name string and a list of ports, or
//          another subcktinst to copy.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

subcktinst::subcktinst() : device(), _port_map(), _port_vec() {
    _model_name = NULL;
}
subcktinst::subcktinst(char *name) : device(name), _port_map(), _port_vec() {
    _model_name = NULL;
}
subcktinst::subcktinst(char *name, vector<port*> &nPorts) : device(name),
    _port_map(), _port_vec()
{
    _model_name = NULL;

    // iterate thought the port list and add the same ports
    vector<port*>::iterator port_curr;
    _port_vec.reserve(nPorts.size());
    for (port_curr = nPorts.begin(); port_curr != nPorts.end(); port_curr++) {
        port *new_port = add_port((*port_curr)->name());
        _port_map[(*port_curr)->name()]=new_port;
        _port_vec.push_back(new_port);
    }
}
subcktinst::subcktinst(subcktinst &copy) : device(copy), _port_map(), _port_vec() {
    _model_name = strdup(copy.model_name());

    // iterate thought the ports of the source subcircuit and add the same ports
    vector<port*>::iterator port_curr;
    _port_vec.reserve(copy.port_vec().size());
    for (port_curr = copy.port_vec().begin(); port_curr != copy.port_vec().end(); port_curr++) {
        port *new_port = new port(**port_curr);
        _port_map[(*port_curr)->name()] = new_port;
        _port_vec.push_back(new_port);
    }
}


//-----------------------------------------------------------------------------
// Function: ~subcktinst()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

subcktinst::~subcktinst() {
    free (_model_name);
    vector<port*>::iterator port_curr;
    for (port_curr = _port_vec.begin(); port_curr != _port_vec.end(); port_curr++)
        delete *port_curr;
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the subcktinst class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void subcktinst::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    vector<port*>::iterator port_curr;
    for (port_curr = _port_vec.begin(); port_curr != _port_vec.end(); port_curr++)
        os<<(*port_curr)->name()<<"="<<(*port_curr)->net_ref()->name()<<" ";
    os<<_model_name<<" ";
    dump_params(os);
    os<<endl;
}

//-----------------------------------------------------------------------------
// Function: operator<<
//
// Purpose: output stream operator overloaded for all derived device classes
//
// Side-effects: none
//
// Notes: These are different than the dump(ostream &os) functions in that they
//				output spice formatted data.
//-----------------------------------------------------------------------------
void device::print(ostream& os) {
	Abort("generic device operator<< called.");
}
void subcktinst::print(ostream& os) {
	os << name() << " ";
	vector<port*>::iterator port_curr;
  for (port_curr = _port_vec.begin(); port_curr != _port_vec.end(); port_curr++) {
      os<<**port_curr<<" ";
  }        
	os << model_name() << " ";
	dump_params(os);
	os << endl;
}
void bjt::print(ostream& os) {
	os << name() << " ";
	os << *_collector << " " << *_base << " ";
	os << *_emitter << " ";
	if (_substrate && _substrate->net_ref()) os << *_substrate << " ";
	os << _model << " ";
	dump_params(os);
	os << endl;
}
void jfet::print(ostream& os) {
	os << name() << " ";
	os << *_drain << " " << *_gate << " ";
	os << *_source << " ";
	if (_bulk && _bulk->net_ref()) os << *_bulk << " ";
	os << _model << " ";
	dump_params(os);
	os << endl;
}
void mosfet::print(ostream& os) {
/*
	os << name() << " ";
	os << *_drain << " " << *_gate << " ";
	os << *_source << " ";
	if (_bulk && _bulk->net_ref()) os << *_bulk << " ";
	os << _model << " ";
	dump_params(os);
	os << endl;
*/
	char* pName = name();

	net* pNet1Ref = _drain->net_ref();
	char* pNode1 = pNet1Ref->name();

	net* pNet2Ref = _gate->net_ref();
	char* pNode2 = pNet2Ref->name();
        
	net* pNet3Ref = _source->net_ref();
	char* pNode3 = pNet3Ref->name();

	net* pNet4Ref = _bulk->net_ref();
	char* pNode4 = pNet4Ref->name();
	// This is not quite right.  Sometimes you'll want a four-terminal module here -STF
	Add3TermModule( pName, "MSFET" , pNode1, pNode2, pNode3);

//	if (_bulk && _bulk->net_ref()) {
//	    AddNTermModule( pName, "MSFET" , 4, "D", pNode1, "G", pNode2, "S", pNode3,
//			"B", pNode4);
//	else
//	    AddNTermModule( pName, "MSFET" , 3, "D", pNode1, "G", pNode2, "S", pNode3); */

}
void diode::print(ostream& os) {

/*
	os << name() << " ";
	os << *_nplus << " " << *_nminus << " ";
	dump_params(os);
	os << endl;
*/
	char* pName = name();
	Add2TermModule( pName, "RESTR" , (char*)(_nplus), (char*)(_nminus));

//	AddNTermModule( pName, "RESTR" , 2, "1", (char*)(_nplus), "2",
//		(char*)(_nminus));
}
void resistor::print(ostream& os) {

/*
	os << name() << " ";
	os << *_node1 << " " << *_node2 << " ";
	dump_params(os);
	os << endl;
*/
	char* pName = name();

	net* pNet1Ref = _node1->net_ref();
	char* pNode1 = pNet1Ref->name();

	net* pNet2Ref = _node2->net_ref();
	char* pNode2 = pNet2Ref->name();
        

	Add2TermModule( pName, "RESTR" , pNode1, pNode2);
//	AddNTermModule( pName, "RESTR" , 2, "1", pNode1, "2", pNode2);
}
void capacitor::print(ostream& os) {

/*
	os << name() << " ";
	os << *_node1 << " " << *_node2 << " ";
	dump_params(os);
	os << endl;
*/
	char* pName = name();

	net* pNet1Ref = _node1->net_ref();
	char* pNode1 = pNet1Ref->name();

	net* pNet2Ref = _node2->net_ref();
	char* pNode2 = pNet2Ref->name();

	Add2TermModule( pName, "CAPC" , pNode1, pNode2 );
//	AddNTermModule( pName, "CAPC" , 2, "1", pNode1, "2", pNode2 );
}
void coupling::print(ostream& os) {

/*
	os << name() << " ";
	os << *_inductor1 << " " << *_inductor2 << " ";
	dump_params(os);
	os << endl;
*/
	char* pName = name();
	Add2TermModule( pName, "RESTR" , (char*)(_inductor1), (char*)(_inductor2));

//	AddNTermModule( pName, "RESTR" , 2, "1", (char*)(_inductor1),
//		"2", (char*)(_inductor2));

}
void inductor::print(ostream& os) {
/*
	os << name() << " ";
	os << *_node1 << " " << *_node2 << " ";
	dump_params(os);
	os << endl;
*/
	char* pName = name();
	
	net* pNet1Ref = _node1->net_ref();
	char* pNode1 = pNet1Ref->name();

	net* pNet2Ref = _node2->net_ref();
	char* pNode2 = pNet2Ref->name();


	Add2TermModule( pName, "INDR" , pNode1, pNode2 );

//	AddNTermModule( pName, "INDR" , 2, "1", pNode1, "2", pNode2 );

}
void vsource::print(ostream& os) {
/*
	os << name() << " ";
	os << *_plus_node << " " << *_minus_node << " ";
	dump_params(os);
	os << endl;
*/
	char* pName = name();

	net* pNet1Ref = _plus_node->net_ref();
	char* pNode1 = pNet1Ref->name();

	net* pNet2Ref = _minus_node->net_ref();
	char* pNode2 = pNet2Ref->name();


	Add2TermModule( pName, "VAMP" , pNode1, pNode2 );

//	AddNTermModule( pName, "VAMP" , 2, "P", pNode1, "M", pNode2 );
}
void isource::print(ostream& os) {
/*
	os << name() << " ";
	os << *_plus_node << " " << *_minus_node << " ";
	dump_params(os);
	os << endl;
*/

	char* pName = name();
	
	net* pNet1Ref = _plus_node->net_ref();
	char* pNode1 = pNet1Ref->name();

	net* pNet2Ref = _minus_node->net_ref();
	char* pNode2 = pNet2Ref->name();


	Add2TermModule( pName, "VAMP" , pNode1, pNode2 );

//	AddNTermModule( pName, "VAMP" , 2, "P", pNode1, "M", pNode2 );
}



// ****************************************************************************
// class: mosfet
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: mosfet()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

mosfet::mosfet() : device() {
    _drain = add_port("drain");
    _gate = add_port("gate");
    _source = add_port("source");
    _bulk = add_port("bulk");

    _model = NULL;
    _polarity = _UNSPECIFIED_;
}
mosfet::mosfet(char *name) : device(name) {
    _drain = add_port("drain");
    _gate = add_port("gate");
    _source = add_port("source");
    _bulk = add_port("bulk");

    _model = NULL;
    _polarity = _UNSPECIFIED_;
}
mosfet::mosfet(mosfet &copy) : device(copy) {

    _drain = add_port("drain");
    _gate = add_port("gate");
    _source = add_port("source");
    _bulk = add_port("bulk");

    _model = strdup(copy.model());
    _polarity = copy.polarity();
}

//-----------------------------------------------------------------------------
// Function: ~mosfet()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------
mosfet::~mosfet() {
		DBG0(cout<<"mosfet destructor: "<<name()<<endl;);
    delete _drain;
    delete _gate;
    delete _source;
    delete _bulk;
    free (_model);
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the mosfet class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void mosfet::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_drain->name()<<"="<<_drain->net_ref()->name()<<" ";
    os<<_gate->name()<<"="<<_gate->net_ref()->name()<<" ";
    os<<_source->name()<<"="<<_source->net_ref()->name()<<" ";
    if (_bulk->net_ref())
        os<<_bulk->name()<<"="<<_bulk->net_ref()->name()<<" ";
    os<<_model;
    if (_polarity==_NFET_) os<<" NFET ";
    else if (_polarity==_PFET_) os<<" PFET ";
    else os<<"(UNSPECIFIED) ";
    dump_params(os);
    os<<endl;
}


// ****************************************************************************
// class: jfet
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: jfet()
//
// Purpose: Constructors.  Can either take void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

jfet::jfet() : device() {
    _drain = add_port("drain");
    _gate = add_port("gate");
    _source = add_port("source");
    _bulk = add_port("bulk");

    _model = NULL;
    _polarity = _UNSPECIFIED_;
}

jfet::jfet(char *name) : device(name) {
    _drain = add_port("drain");
    _gate = add_port("gate");
    _source = add_port("source");
    _bulk = add_port("bulk");

    _model = NULL;
    _polarity = _UNSPECIFIED_;
}
jfet::jfet(jfet &copy) : device(copy) {
    _drain = add_port("drain");
    _gate = add_port("gate");
    _source = add_port("source");
    _bulk = add_port("bulk");

    _model = strdup(copy.model());
    _polarity = copy.polarity();
}


//-----------------------------------------------------------------------------
// Function: ~jfet()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

jfet::~jfet() {
    delete _drain;
    delete _gate;
    delete _source;
    delete _bulk;
    free (_model);
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the jfet class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void jfet::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_drain->name()<<"="<<_drain->net_ref()->name()<<" ";
    os<<_gate->name()<<"="<<_gate->net_ref()->name()<<" ";
    os<<_source->name()<<"="<<_source->net_ref()->name()<<" ";
    if (_bulk->net_ref())
        os<<_bulk->name()<<"="<<_bulk->net_ref()->name()<<" ";
    os<<_model;
    if (_polarity==_NFET_) os<<" NFET ";
    else if (_polarity==_PFET_) os<<" PFET ";
    else os<<"(UNSPECIFIED) ";
    dump_params(os);
    os<<endl;
}


// ****************************************************************************
// class: bjt
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: bjt()
//
// Purpose: Constructors.  Can either take void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

bjt::bjt() : device() {
    _collector = add_port("collector");
    _base = add_port("base");
    _emitter = add_port ("emitter");
    _substrate = add_port ("substrate");
    
    _model = NULL;
    _polarity = _UNSPECIFIED_;
}

bjt::bjt(char *name) : device(name) {
    _collector = add_port("collector");
    _base = add_port("base");
    _emitter = add_port ("emitter");
    _substrate = add_port ("substrate");
    
    _model = NULL;
    _polarity = _UNSPECIFIED_;
}
bjt::bjt(bjt &copy) : device(copy) {
    _collector = add_port("collector");
    _base = add_port("base");
    _emitter = add_port ("emitter");
    _substrate = add_port ("substrate");

    _model = strdup(copy.model());
    _polarity = copy.polarity();
}


//-----------------------------------------------------------------------------
// Function: ~bjt()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------
bjt::~bjt() {
    delete _collector;
    delete _base;
    delete _emitter;
    delete _substrate;
    free (_model);
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the bjt class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void bjt::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_collector->name()<<"="<<_collector->net_ref()->name()<<" ";
    os<<_base->name()<<"="<<_base->net_ref()->name()<<" ";
    os<<_emitter->name()<<"="<<_emitter->net_ref()->name()<<" ";
    if (_substrate->net_ref())
        os<<_substrate->name()<<"="<<_substrate->net_ref()->name()<<" ";
    os<<_model;
    if (_polarity==_NPN_) os<<" NPN ";
    else if (_polarity==_PNP_) os<<" PNP ";
    else os<<"(UNSPECIFIED) ";
    dump_params(os);
    os<<endl;
}


// ****************************************************************************
// class: diode
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: diode()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

diode::diode() : device() {
    _nplus = add_port("nplus");
    _nminus = add_port("nminus");

    _model = NULL;
}
diode::diode(char *name) : device(name) {
    _nplus = add_port("nplus");
    _nminus = add_port("nminus");

    _model = NULL;
}
diode::diode(diode &copy) : device(copy) {
    _nplus = add_port("nplus");
    _nminus = add_port("nminus");

    _model = strdup(copy.model());
}


//-----------------------------------------------------------------------------
// Function: ~diode()
//
// Purpose: Dstructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

diode::~diode() {
    delete _nplus;
    delete _nminus;
    free (_model);
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the diode class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void diode::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_nplus->name()<<"="<<_nplus->net_ref()->name()<<" ";
    os<<_nminus->name()<<"="<<_nminus->net_ref()->name()<<" ";
    os<<_model<<" ";
    dump_params(os);
    os<<endl;
}

// ****************************************************************************
// class: resistor
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: resistor()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

resistor::resistor() : device() {
    _node1 = add_port("node1");
    _node2 = add_port("node2");
    
    _model = NULL;
}
resistor::resistor(char *name) : device(name) {
    _node1 = add_port("node1");
    _node2 = add_port("node2");
    
    _model = NULL;
}
resistor::resistor(resistor &copy) : device(copy) {
    _node1 = add_port("node1");
    _node2 = add_port("node2");

    _model = strdup(copy.model());
}


//-----------------------------------------------------------------------------
// Function: ~resistor()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

resistor::~resistor() {
    delete _node1;
    delete _node2;
    free (_model);
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the resistor class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void resistor::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_node1->name()<<"="<<_node1->net_ref()->name()<<" ";
    os<<_node2->name()<<"="<<_node2->net_ref()->name()<<" ";
    os<<_model<<" ";
    dump_params(os);
    os<<endl;
}


// ****************************************************************************
// class: capacitor
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: capacitor()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device,
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

capacitor::capacitor() : device() {
    _node1 = add_port("node1");
    _node2 = add_port("node2");
    
    _model = NULL;
}

capacitor::capacitor(char *name) : device(name) {
    _node1 = add_port("node1");
    _node2 = add_port("node2");
    
    _model = NULL;
}
capacitor::capacitor(capacitor &copy) : device(copy) {
    _node1 = add_port("node1");
    _node2 = add_port("node2");

    _model = strdup(copy.model());
}


//-----------------------------------------------------------------------------
// Function: ~capacitor()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

capacitor::~capacitor() {
    delete _node1;
    delete _node2;
    free (_model);
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the capacitor class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void capacitor::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_node1->name()<<"="<<_node1->net_ref()->name()<<" ";
    os<<_node2->name()<<"="<<_node2->net_ref()->name()<<" ";
    os<<_model<<" ";
    dump_params(os);
    os<<endl;
}
// ****************************************************************************
// class: coupling
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: coupling()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device,
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

coupling::coupling() : device() {
    _inductor1 = add_port("inductor1");
    _inductor2 = add_port("inductor2");
    
}

coupling::coupling(char *name) : device(name) {
    _inductor1 = add_port("inductor1");
    _inductor2 = add_port("inductor2");
    
}
coupling::coupling(coupling &copy) : device(copy) {
    _inductor1 = add_port("inductor1");
    _inductor2 = add_port("inductor2");

    
}


//-----------------------------------------------------------------------------
// Function: ~coupling()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

coupling::~coupling() {
    delete _inductor1;
    delete _inductor2;
    
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the coupling class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void coupling::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_inductor1->name()<<"="<<_inductor1->net_ref()->name()<<" ";
    os<<_inductor2->name()<<"="<<_inductor2->net_ref()->name()<<" ";
    dump_params(os);
    os<<endl;
}


// ****************************************************************************
// class: inductor
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: inductor()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

inductor::inductor() : device() {
    _node1 = add_port("node1");
    _node2 = add_port("node2");
}    

inductor::inductor(char *name) : device(name) {
    _node1 = add_port("node1");
    _node2 = add_port("node2");
}    
inductor::inductor(inductor &copy) : device(copy) {
    _node1 = add_port("node1");
    _node2 = add_port("node2");
}


//-----------------------------------------------------------------------------
// Function: ~inductor()
//
// Purpose: Destructors.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

inductor::~inductor() {
    delete _node1;
    delete _node2;
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the inductor class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void inductor::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_node1->name()<<"="<<_node1->net_ref()->name()<<" ";
    os<<_node2->name()<<"="<<_node2->net_ref()->name()<<" ";
    dump_params(os);
    os<<endl;
}


// ****************************************************************************
// class: vsource
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: vsource()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

vsource::vsource() : device() {
    _plus_node = add_port("plus_node");
    _minus_node = add_port("minus_node");
}

vsource::vsource(char *name) : device(name) {
    _plus_node = add_port("plus_node");
    _minus_node = add_port("minus_node");
}
vsource::vsource(vsource &copy) : device(copy) {
    _plus_node = add_port("plus_node");
    _minus_node = add_port("minus_node");
}


//-----------------------------------------------------------------------------
// Function: ~vsource()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

vsource::~vsource() {
    delete _plus_node;
    delete _minus_node;
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the vsource class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void vsource::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_plus_node->name()<<"="<<_plus_node->net_ref()->name()<<" ";
    os<<_minus_node->name()<<"="<<_minus_node->net_ref()->name()<<" ";
    dump_params(os);
    os<<endl;
}


// ****************************************************************************
// class: isource
// ****************************************************************************


//-----------------------------------------------------------------------------
// Function: isource()
//
// Purpose: Constructors.  Can take either void, or a string containing the
//          name of the device.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

isource::isource() : device() {
    _plus_node = add_port("plus_node");
    _minus_node = add_port("minus_node");
}

isource::isource(char *name) : device(name) {
    _plus_node = add_port("plus_node");
    _minus_node = add_port("minus_node");
}
isource::isource(isource &copy) : device(copy) {
    _plus_node = add_port("plus_node");
    _minus_node = add_port("minus_node");
}


//-----------------------------------------------------------------------------
// Function: ~isource()
//
// Purpose: Destructor.
//
// Side-effects: 
//
// Notes: 
//-----------------------------------------------------------------------------

isource::~isource() {
    delete _plus_node;
    delete _minus_node;
}


//-----------------------------------------------------------------------------
// Function: dump
//
// Purpose: dump the interesting contents of the isource class for debugging
//
// Side-effects: none
//
// Notes: 
//-----------------------------------------------------------------------------

void isource::dump(ostream &os){
    os<<"\t"<<name()<<" ";
    os<<_plus_node->name()<<"="<<_plus_node->net_ref()->name()<<" ";
    os<<_minus_node->name()<<"="<<_minus_node->net_ref()->name()<<" ";
    dump_params(os);
    os<<endl;
}

