// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// 
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
// 
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 
// This notice may not be removed or altered from any source distribution.

#include "ConcreteParasitics.hh"

#include <algorithm> // max

#include "ConcreteParasiticsPvt.hh"
#include "Debug.hh"
#include "Error.hh"
#include "Liberty.hh"
#include "MinMax.hh"
#include "Mutex.hh"
#include "Network.hh"
#include "Parasitics.hh"
#include "Report.hh"
#include "Scene.hh"
#include "Sdc.hh"
#include "Wireload.hh"

// Multiple inheritance is used to share elmore and pi model base
// classes, but care is taken to make sure there are no loops in the
// inheritance graph (ConcreteParasitic only included once).

namespace sta {

bool
ConcreteParasitic::isPiElmore() const
{
  return false;
}

bool
ConcreteParasitic::isPiModel() const
{
  return false;
}

bool
ConcreteParasitic::isPiPoleResidue() const
{
  return false;
}

bool
ConcreteParasitic::isPoleResidue() const
{
  return false;
}

bool
ConcreteParasitic::isParasiticNetwork() const
{
  return false;
}

void
ConcreteParasitic::piModel(float &,
                           float &,
                           float &) const
{
}

void
ConcreteParasitic::setPiModel(float,
                              float,
                              float)
{
}

bool
ConcreteParasitic::isReducedParasiticNetwork() const
{
  return false;
}

void
ConcreteParasitic::setIsReduced(bool)
{
}

void
ConcreteParasitic::findElmore(const Pin *,
                              float &,
                              bool &exists) const
{
  exists = false;
}

void
ConcreteParasitic::setElmore(const Pin *,
                             float)
{
}

Parasitic *
ConcreteParasitic::findPoleResidue(const Pin *) const
{
  return nullptr;
}

void
ConcreteParasitic::setPoleResidue(const Pin *,
                                  ComplexFloatSeq *,
                                  ComplexFloatSeq *)
{
}

////////////////////////////////////////////////////////////////

ConcretePi::ConcretePi(float c2,
                       float rpi,
                       float c1) :
  c2_(c2),
  rpi_(rpi),
  c1_(c1)
{
}

float
ConcretePi::capacitance() const
{
  return c1_ + c2_;
}

void
ConcretePi::setPiModel(float c2,
                       float rpi,
                       float c1)
{
  c2_ = c2;
  rpi_ = rpi;
  c1_ = c1;
}

void
ConcretePi::piModel(float &c2,
                    float &rpi,
                    float &c1) const
{
  c2 = c2_;
  rpi = rpi_;
  c1 = c1_;
}

void
ConcretePi::setIsReduced(bool reduced)
{
  is_reduced_ = reduced;
}

////////////////////////////////////////////////////////////////

ConcretePiElmore::ConcretePiElmore(float c2,
                                   float rpi,
                                   float c1) :
  ConcretePi(c2, rpi, c1)
{
}

float
ConcretePiElmore::capacitance() const
{
  return ConcretePi::capacitance();
}

void
ConcretePiElmore::piModel(float &c2,
                          float &rpi,
                          float &c1) const
{
  ConcretePi::piModel(c2, rpi, c1);
}

void
ConcretePiElmore::setPiModel(float c2,
                             float rpi,
                             float c1)
{
  ConcretePi::setPiModel(c2, rpi, c1);
}

bool
ConcretePiElmore::isReducedParasiticNetwork() const
{
  return ConcretePi::isReducedParasiticNetwork();
}

void
ConcretePiElmore::setIsReduced(bool reduced)
{
  ConcretePi::setIsReduced(reduced);
}

void
ConcretePiElmore::findElmore(const Pin *load_pin,
                             float &elmore,
                             bool &exists) const
{
  auto itr = loads_.find(load_pin);
  if (itr == loads_.end())
    exists = false;
  else {
    elmore = itr->second;
    exists = true;
  }
}

void
ConcretePiElmore::setElmore(const Pin *load_pin,
                            float elmore)
{
  loads_[load_pin] = elmore;
}

void
ConcretePiElmore::deleteLoad(const Pin *load_pin)
{
  loads_.erase(load_pin);
}

PinSet
ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin,
                                   const Parasitics *parasitics) const
{
  PinSet loads = parasitics->loads(drvr_pin);
  for (const auto [load, elmore] : loads_)
    loads.erase(load);
  return loads;
}

////////////////////////////////////////////////////////////////

ConcretePoleResidue::~ConcretePoleResidue()
{
  delete poles_;
  delete residues_;
}

size_t
ConcretePoleResidue::poleResidueCount() const
{
  return poles_->size();
}

void
ConcretePoleResidue::poleResidue(int index,
                                 ComplexFloat &pole,
                                 ComplexFloat &residue) const
{
  pole = (*poles_)[index];
  residue = (*residues_)[index];
}

void
ConcretePoleResidue::setPoleResidue(ComplexFloatSeq *poles,
                                    ComplexFloatSeq *residues)
{
  poles_ = poles;
  residues_ = residues;
}

PinSet
ConcretePoleResidue::unannotatedLoads(const Pin *,
                                      const Parasitics *) const
{
  return PinSet();
}

////////////////////////////////////////////////////////////////

ConcretePiPoleResidue::ConcretePiPoleResidue(float c2,
                                             float rpi,
                                             float c1) :
  ConcretePi(c2, rpi, c1)
{
}

float
ConcretePiPoleResidue::capacitance() const
{
  return ConcretePi::capacitance();
}

void
ConcretePiPoleResidue::piModel(float &c2,
                               float &rpi,
                               float &c1) const
{
  ConcretePi::piModel(c2, rpi, c1);
}

void
ConcretePiPoleResidue::setPiModel(float c2,
                                  float rpi,
                                  float c1)
{
  ConcretePi::setPiModel(c2, rpi, c1);
}

bool
ConcretePiPoleResidue::isReducedParasiticNetwork() const
{
  return ConcretePi::isReducedParasiticNetwork();
}

void
ConcretePiPoleResidue::setIsReduced(bool reduced)
{
  ConcretePi::setIsReduced(reduced);
}

Parasitic *
ConcretePiPoleResidue::findPoleResidue(const Pin *load_pin) const
{
  auto itr = load_pole_residue_.find(load_pin);
  if (itr == load_pole_residue_.end())
    return nullptr;
  else
    return &const_cast<ConcretePoleResidue&>(itr->second);
}

void
ConcretePiPoleResidue::setPoleResidue(const Pin *load_pin,
                                      ComplexFloatSeq *poles,
                                      ComplexFloatSeq *residues)
{
  ConcretePoleResidue &pole_residue = load_pole_residue_[load_pin];
  pole_residue.setPoleResidue(poles, residues);
}

void
ConcretePiPoleResidue::deleteLoad(const Pin *load_pin)
{
  load_pole_residue_.erase(load_pin);
}

PinSet
ConcretePiPoleResidue::unannotatedLoads(const Pin *drvr_pin,
                                        const Parasitics *parasitics) const
{
  PinSet loads = parasitics->loads(drvr_pin);
  for (const auto& [load, pole_residue] : load_pole_residue_)
    loads.erase(load);
  return loads;
}

////////////////////////////////////////////////////////////////

ConcreteParasiticNode::ConcreteParasiticNode(const Net *net,
                                             uint32_t id,
                                             bool is_external) :
  is_net_(true),
  is_external_(is_external),
  id_(id),
  cap_(0.0)
{
  net_pin_.net_ = net;
}

ConcreteParasiticNode::ConcreteParasiticNode(const Pin *pin,
                                             bool is_external) :
  is_net_(false),
  is_external_(is_external),
  id_(0),
  cap_(0.0)
{
  net_pin_.pin_ = pin;
}

void
ConcreteParasiticNode::incrCapacitance(float cap)
{
  cap_ += cap;
}

std::string
ConcreteParasiticNode::name(const Network *network) const
{
  if (is_net_) {
    std::string name = std::string(network->pathName(net_pin_.net_))
      + ':'
      + std::to_string(id_);
    return name;
  }
  else
    return network->pathName(net_pin_.pin_);
}

const Pin *
ConcreteParasiticNode::pin() const
{
  if (is_net_)
    return nullptr;
  else
    return net_pin_.pin_;
}

const Net *
ConcreteParasiticNode::net(const Network *network) const
{
  if (is_net_)
    return net_pin_.net_;
  else {
    Net *net = network->net(net_pin_.pin_);
    // Pins on the top level instance may not have nets.
    // Use the net connected to the pin's terminal.
    if (net == nullptr && network->isTopLevelPort(net_pin_.pin_)) {
      Term *term = network->term(net_pin_.pin_);
      if (term)
        return network->net(term);
    }
    return net;
  }
}

////////////////////////////////////////////////////////////////

                       
ConcreteParasiticDevice::ConcreteParasiticDevice(uint32_t id,
                                                 float value,
                                                 ConcreteParasiticNode *node1,
                                                 ConcreteParasiticNode *node2) :
  id_(id),
  value_(value),
  node1_(node1),
  node2_(node2)
{
}

void
ConcreteParasiticDevice::replaceNode(ConcreteParasiticNode *from_node,
                                     ConcreteParasiticNode *to_node)
{
  if (from_node == node1_)
    node1_ = to_node;
  else if (from_node == node2_)
    node2_ = to_node;
}

ConcreteParasiticResistor::ConcreteParasiticResistor(uint32_t id,
                                                     float value,
                                                     ConcreteParasiticNode *node1,
                                                     ConcreteParasiticNode *node2) :
  ConcreteParasiticDevice(id, value, node1, node2)
{
}

ConcreteParasiticCapacitor::ConcreteParasiticCapacitor(uint32_t id,
                                                       float value,
                                                       ConcreteParasiticNode *node1,
                                                       ConcreteParasiticNode *node2) :
  ConcreteParasiticDevice(id, value, node1, node2)
{
}

////////////////////////////////////////////////////////////////

ConcreteParasiticNetwork::ConcreteParasiticNetwork(const Net *net,
                                                   bool includes_pin_caps,
                                                   const Network *network) :
  net_(net),
  sub_nodes_(network),
  pin_nodes_(network),
  includes_pin_caps_(includes_pin_caps)
{
}

ConcreteParasiticNetwork::ConcreteParasiticNetwork(ConcreteParasiticNetwork &&parasitic)
  noexcept :
  net_(parasitic.net_),
  sub_nodes_(std::move(parasitic.sub_nodes_)),
  pin_nodes_(std::move(parasitic.pin_nodes_)),
  max_node_id_(parasitic.max_node_id_),
  includes_pin_caps_(parasitic.includes_pin_caps_)
{
}

ConcreteParasiticNetwork::~ConcreteParasiticNetwork()
{
  deleteDevices();
  deleteNodes();
}

void
ConcreteParasiticNetwork::deleteNodes()
{
  for (const auto& [id, node] : sub_nodes_)
    delete node;
  for (const auto& [pin, node] : pin_nodes_)
    delete node;
}

void
ConcreteParasiticNetwork::deleteDevices()
{
  for (ParasiticResistor *resistor : resistors_) {
    ConcreteParasiticResistor *cresistor =
      reinterpret_cast<ConcreteParasiticResistor*>(resistor);
    delete cresistor;
  }
  for (ParasiticCapacitor *capacitor : capacitors_) {
    ConcreteParasiticCapacitor *ccapacitor =
      reinterpret_cast<ConcreteParasiticCapacitor*>(capacitor);
    delete ccapacitor;
  }
}

void
ConcreteParasiticNetwork::addResistor(ParasiticResistor *resistor)
{
  resistors_.push_back(resistor);
}

void
ConcreteParasiticNetwork::addCapacitor(ParasiticCapacitor *capacitor)
{
  capacitors_.push_back(capacitor);
}

ParasiticNodeSeq
ConcreteParasiticNetwork::nodes() const
{
  ParasiticNodeSeq nodes;
  for (const auto [pin, node] : pin_nodes_)
    nodes.push_back(node);
  for (const auto& [id, node] : sub_nodes_)
    nodes.push_back(node);
  return nodes;
}

float
ConcreteParasiticNetwork::capacitance() const
{
  float cap = 0.0;
  for (const auto& [id, node] : sub_nodes_) {
    if (!node->isExternal())
      cap += node->capacitance();
  }

  for (const auto [pin, node] : pin_nodes_) {
    if (!node->isExternal())
      cap += node->capacitance();
  }

  for (ParasiticCapacitor *capacitor : capacitors_) {
    ConcreteParasiticCapacitor *ccap =
      static_cast<ConcreteParasiticCapacitor*>(capacitor);
    cap += ccap->value();
  }

  return cap;
}

ConcreteParasiticNode *
ConcreteParasiticNetwork::findParasiticNode(const Net *net,
                                            uint32_t id,
                                            const Network *) const
{
  NetIdPair net_id(net, id);
  auto id_node = sub_nodes_.find(net_id);
  if (id_node == sub_nodes_.end()) 
    return nullptr;
  else
    return id_node->second;
}

ConcreteParasiticNode *
ConcreteParasiticNetwork::findParasiticNode(const Pin *pin) const
{
  auto pin_node = pin_nodes_.find(pin);
  if (pin_node == pin_nodes_.end())
    return nullptr;
  else
    return pin_node->second;
}

ConcreteParasiticNode *
ConcreteParasiticNetwork::ensureParasiticNode(const Net *net,
                                              uint32_t id,
                                              const Network *network)
{
  ConcreteParasiticNode *node;
  NetIdPair net_id(net, id);
  auto id_node = sub_nodes_.find(net_id);
  if (id_node == sub_nodes_.end()) {
    Net *net1 = network->highestNetAbove(const_cast<Net*>(net));
    node = new ConcreteParasiticNode(net, id, network->highestNetAbove(net1) != net_);
    sub_nodes_[net_id] = node;
    if (net == net_)
      max_node_id_ = std::max(max_node_id_, id);
  }
  else
    node = id_node->second;
  return node;
}

ConcreteParasiticNode *
ConcreteParasiticNetwork::ensureParasiticNode(const Pin *pin,
                                              const Network *network)
{
  ConcreteParasiticNode *node;
  auto pin_node = pin_nodes_.find(pin);
  if (pin_node == pin_nodes_.end()) {
    Net *net = network->net(pin);
    // Pins on the top level instance may not have nets.
    // Use the net connected to the pin's terminal.
    if (net == nullptr && network->isTopLevelPort(pin)) {
      Term *term = network->term(pin);
      if (term)
        net = network->net(term);
    }
    else if (net)
      net = network->highestNetAbove(net);
    node = new ConcreteParasiticNode(pin, net != net_);
    pin_nodes_[pin] = node;
  }
  else
    node = pin_node->second;
  return node;
}

PinSet
ConcreteParasiticNetwork::unannotatedLoads(const Pin *drvr_pin,
                                           const Parasitics *parasitics) const
{
  PinSet loads = parasitics->loads(drvr_pin);
  ParasiticNode *drvr_node = findParasiticNode(drvr_pin);
  if (drvr_node) {
    ParasiticNodeResistorMap resistor_map =
      parasitics->parasiticNodeResistorMap(this);
    ParasiticNodeSet visited_nodes;
    ParasiticResistorSet loop_resistors;
    unannotatedLoads(drvr_node, nullptr, loads, visited_nodes,
                     loop_resistors, resistor_map, parasitics);
  }
  return loads;
}

void
ConcreteParasiticNetwork::unannotatedLoads(ParasiticNode *node,
                                           ParasiticResistor *from_res,
                                           PinSet &loads,
                                           ParasiticNodeSet &visited_nodes,
                                           ParasiticResistorSet &loop_resistors,
                                           ParasiticNodeResistorMap &resistor_map,
                                           const Parasitics *parasitics) const
{
  const Pin *pin = parasitics->pin(node);
  if (pin)
    loads.erase(const_cast<Pin*>(pin));

  visited_nodes.insert(node);
  ParasiticResistorSeq &resistors = resistor_map[node];
  for (ParasiticResistor *resistor : resistors) {
    if (!loop_resistors.contains(resistor)) {
      ParasiticNode *onode = parasitics->otherNode(resistor, node);
      // One commercial extractor creates resistors with identical from/to nodes.
      if (onode != node
          && resistor != from_res) {
        if (!visited_nodes.contains(onode))
          unannotatedLoads(onode, resistor, loads, visited_nodes,
                           loop_resistors, resistor_map, parasitics);
        else
          // resistor loop
          loop_resistors.insert(resistor);
      }
    }
  }
  visited_nodes.erase(node);
}

////////////////////////////////////////////////////////////////

void
ConcreteParasiticNetwork::disconnectPin(const Pin *pin,
                                        const Net *net,
                                        const Network *network)
{
  auto pin_node = pin_nodes_.find(pin);
  if (pin_node != pin_nodes_.end()) {
    ConcreteParasiticNode *node = pin_node->second;
    // Make a subnode to replace the pin node.
    ConcreteParasiticNode *subnode = ensureParasiticNode(net,max_node_id_+1,
                                                         network);
    // Hand over the devices.
    for (ParasiticResistor *resistor : resistors_) {
      ConcreteParasiticResistor *cresistor =
        reinterpret_cast<ConcreteParasiticResistor*>(resistor);
      cresistor->replaceNode(node, subnode);
    }
    for (ParasiticCapacitor *capacitor : capacitors_) {
      ConcreteParasiticCapacitor *ccapacitor =
        reinterpret_cast<ConcreteParasiticCapacitor*>(capacitor);
      ccapacitor->replaceNode(node, subnode);
    }

    pin_nodes_.erase(pin_node);
    delete node;
  }
}

////////////////////////////////////////////////////////////////

NetIdPairLess::NetIdPairLess(const Network *network) :
  net_less_(network)
{
}

bool
NetIdPairLess::operator()(const NetIdPair &net_id1,
                          const NetIdPair &net_id2) const
{
  const auto& [net1, id1] = net_id1;
  const auto& [net2, id2] = net_id2;
  return net_less_(net1, net2)
    || (net1 == net2
        && id1 < id2);
}

////////////////////////////////////////////////////////////////

ConcreteParasitics::ConcreteParasitics(std::string_view name,
                                       std::string_view filename,
                                       StaState *sta) :
  Parasitics(sta),
  name_(name),
  filename_(filename)
{
}

ConcreteParasitics::~ConcreteParasitics()
{
  deleteParasiticsImpl();
}

bool
ConcreteParasitics::haveParasitics()
{
  return !drvr_parasitic_map_.empty()
    || !parasitic_network_map_.empty();
}

void
ConcreteParasitics::clear()
{
  deleteParasitics();
}

void
ConcreteParasitics::deleteParasitics()
{
  deleteParasiticsImpl();
}

void
ConcreteParasitics::deleteParasiticsImpl()
{
  for (auto &[drvr, parasitics] : drvr_parasitic_map_) {
    for (size_t i = 0; i < min_max_rise_fall_count; i++)
      delete parasitics[i];
  }
  drvr_parasitic_map_.clear();

  parasitic_network_map_.clear();
}

void
ConcreteParasitics::deleteParasitics(const Pin *drvr_pin)
{
  auto itr = drvr_parasitic_map_.find(drvr_pin);
  if (itr != drvr_parasitic_map_.end()) {
    const MinMaxRiseFallParasitics &parasitics = itr->second;
    for (size_t i = 0; i < min_max_rise_fall_count; i++)
      delete parasitics[i];
    drvr_parasitic_map_.erase(itr);
  }
}

void
ConcreteParasitics::deleteParasitics(const Net *net)
{
  PinSet *drivers = network_->drivers(net);
  for (auto drvr_pin : *drivers)
    deleteParasitics(drvr_pin);

  parasitic_network_map_.erase(net);
}

float
ConcreteParasitics::capacitance(const Parasitic *parasitic) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic->capacitance();
}

bool
ConcreteParasitics::isReducedParasiticNetwork(const Parasitic *parasitic) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic->isReducedParasiticNetwork();
}

void
ConcreteParasitics::setIsReducedParasiticNetwork(Parasitic *parasitic,
                                                 bool is_reduced)
{
  ConcreteParasitic *cparasitic = static_cast<ConcreteParasitic*>(parasitic);
  cparasitic->setIsReduced(is_reduced);
}

void
ConcreteParasitics::disconnectPinBefore(const Pin *pin)
{
  if (haveParasitics()) {
    deleteReducedParasitics(pin);

    const Net *net = findParasiticNet(pin);
    if (net) {
      ConcreteParasiticNetwork *parasitic =
        static_cast<ConcreteParasiticNetwork*>(findParasiticNetwork(pin));
      if (parasitic)
        parasitic->disconnectPin(pin, net, network_);
    }
  }
}

void
ConcreteParasitics::deletePinBefore(const Pin *pin)
{
  // Actions are the same.
  disconnectPinBefore(pin);
}

void
ConcreteParasitics::loadPinCapacitanceChanged(const Pin *pin)
{
  // Delete reduced models that depend on load pin capacitances.
  deleteReducedParasitics(pin);
}

void
ConcreteParasitics::deleteReducedParasitics(const Net *net)
{
  if (!drvr_parasitic_map_.empty()) {
    PinSet *drivers = network_->drivers(net);
    if (drivers) {
      for (auto drvr_pin : *drivers)
        deleteDrvrReducedParasitics(drvr_pin);
    }
  }
}

// Delete reduced models on pin's net.
void
ConcreteParasitics::deleteReducedParasitics(const Pin *pin)
{
  if (!drvr_parasitic_map_.empty()) {
    PinSet *drivers = network_->drivers(pin);
    if (drivers) {
      for (auto drvr_pin : *drivers)
        deleteDrvrReducedParasitics(drvr_pin);
    }
  }
}

void
ConcreteParasitics::deleteDrvrReducedParasitics(const Pin *drvr_pin)
{
  deleteParasitics(drvr_pin);
}

////////////////////////////////////////////////////////////////

bool
ConcreteParasitics::isPiElmore(const Parasitic *parasitic) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic && cparasitic->isPiElmore();
}

size_t
minMaxRiseFallIndex(const MinMax *min_max,
                    const RiseFall *rf)
{
  return min_max->index() * RiseFall::index_count + rf->index();
}

Parasitic *
ConcreteParasitics::findPiElmore(const Pin *drvr_pin,
                                 const RiseFall *rf,
                                 const MinMax *min_max) const
{
  LockGuard lock(lock_);
  auto itr = drvr_parasitic_map_.find(drvr_pin);
  if (itr != drvr_parasitic_map_.end()) {
    const MinMaxRiseFallParasitics &parasitics = itr->second;
    ConcreteParasitic *parasitic = parasitics[minMaxRiseFallIndex(min_max, rf)];
    if (parasitic && parasitic->isPiElmore())
      return parasitic;
  }
  return nullptr;
}

Parasitic *
ConcreteParasitics::makePiElmore(const Pin *drvr_pin,
                                 const RiseFall *rf,
                                 const MinMax *min_max,
                                 float c2,
                                 float rpi,
                                 float c1)
{
  LockGuard lock(lock_);
  auto itr = drvr_parasitic_map_.find(drvr_pin);
  ConcretePiElmore *pi_elmore = nullptr;
  size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf);
  if (itr != drvr_parasitic_map_.end()) {
    MinMaxRiseFallParasitics &parasitics = itr->second;
    ConcreteParasitic *parasitic = parasitics[mm_rf_index];
    if (parasitic && parasitic->isPiElmore()) {
      pi_elmore = dynamic_cast<ConcretePiElmore*>(parasitic);
      pi_elmore->setPiModel(c2, rpi, c1);
      pi_elmore->loads().clear();
    }
    else {
      delete parasitic;
      pi_elmore = new ConcretePiElmore(c2, rpi, c1);
      parasitics[mm_rf_index] = pi_elmore;
    }
  }
  else {
    MinMaxRiseFallParasitics &parasitics = drvr_parasitic_map_[drvr_pin];
    for (size_t i = 0; i < min_max_rise_fall_count; i++)
      parasitics[i] = nullptr;
    pi_elmore = new ConcretePiElmore(c2, rpi, c1);
    parasitics[mm_rf_index] = pi_elmore;
  }
  return pi_elmore;
}

////////////////////////////////////////////////////////////////

bool
ConcreteParasitics::isPiModel(const Parasitic *parasitic) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic && cparasitic->isPiModel();
}

void
ConcreteParasitics::piModel(const Parasitic *parasitic,
                            float &c2,
                            float &rpi,
                            float &c1) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  if (cparasitic->isPiModel())
    cparasitic->piModel(c2, rpi, c1);
  else
    criticalError(2700, "piModel called on non-PiElmore parasitic.");
}

void
ConcreteParasitics::setPiModel(Parasitic *parasitic,
                               float c2,
                               float rpi,
                               float c1)
{
  ConcreteParasitic *cparasitic = static_cast<ConcreteParasitic*>(parasitic);
  if (cparasitic->isPiModel())
    cparasitic->setPiModel(c2, rpi, c1);
  else
    criticalError(2701, "setPiModel called on non-PiElmore parasitic.");
}

////////////////////////////////////////////////////////////////

void
ConcreteParasitics::findElmore(const Parasitic *parasitic,
                               const Pin *load_pin,
                               float &elmore,
                               bool &exists) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  cparasitic->findElmore(load_pin, elmore, exists);
}

void
ConcreteParasitics::setElmore(Parasitic *parasitic,
                              const Pin *load_pin,
                              float elmore)
{
  ConcreteParasitic *cparasitic = static_cast<ConcreteParasitic*>(parasitic);
  cparasitic->setElmore(load_pin, elmore);
}

////////////////////////////////////////////////////////////////

bool
ConcreteParasitics::isPiPoleResidue(const Parasitic* parasitic) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic && cparasitic->isPiPoleResidue();
}

Parasitic *
ConcreteParasitics::findPiPoleResidue(const Pin *drvr_pin,
                                      const RiseFall *rf,
                                      const MinMax *min_max) const
{
  LockGuard lock(lock_);
  auto itr = drvr_parasitic_map_.find(drvr_pin);
  if (itr != drvr_parasitic_map_.end()) {
    const MinMaxRiseFallParasitics &parasitics = itr->second;
    size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf);
    ConcreteParasitic *parasitic = parasitics[mm_rf_index];
    if (parasitic && parasitic->isPiPoleResidue())
      return parasitic;
  }
  return nullptr;
}

Parasitic *
ConcreteParasitics::makePiPoleResidue(const Pin *drvr_pin,
                                      const RiseFall *rf,
                                      const MinMax *min_max,
                                      float c2,
                                      float rpi,
                                      float c1)
{
  LockGuard lock(lock_);
  auto itr = drvr_parasitic_map_.find(drvr_pin);
  ConcretePiPoleResidue *pi_pole_residue = nullptr;
  size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf);
  if (itr != drvr_parasitic_map_.end()) {
    MinMaxRiseFallParasitics &parasitics = itr->second;
    ConcreteParasitic *parasitic = parasitics[mm_rf_index];
    if (parasitic && parasitic->isPoleResidue()) {
      pi_pole_residue = dynamic_cast<ConcretePiPoleResidue*>(parasitic);
      pi_pole_residue->setPiModel(c2, rpi, c1);
      pi_pole_residue->loadResidues().clear();
    }
    else {
      delete parasitic;
      pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1);
      parasitics[mm_rf_index] = pi_pole_residue;
    }
  }
  else {
    MinMaxRiseFallParasitics &parasitics = drvr_parasitic_map_[drvr_pin];
    for (size_t i = 0; i < min_max_rise_fall_count; i++)
      parasitics[i] = nullptr;
    pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1);
    parasitics[mm_rf_index] = pi_pole_residue;
  }
  return pi_pole_residue;
}

Parasitic *
ConcreteParasitics::findPoleResidue(const Parasitic *parasitic,
                                    const Pin *load_pin) const
{
  const ConcreteParasitic *cparasitic =
    static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic->findPoleResidue(load_pin);
}

void
ConcreteParasitics::setPoleResidue(Parasitic *parasitic,
                                   const Pin *load_pin,
                                   ComplexFloatSeq *poles,
                                   ComplexFloatSeq *residues)
{
  ConcreteParasitic *cparasitic =
    static_cast<ConcreteParasitic*>(parasitic);
  cparasitic->setPoleResidue(load_pin, poles, residues);
}

////////////////////////////////////////////////////////////////

bool
ConcreteParasitics::isPoleResidue(const Parasitic *parasitic) const
{
  const ConcreteParasitic *cparasitic =
    static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic->isPoleResidue();
}

size_t
ConcreteParasitics::poleResidueCount(const Parasitic *parasitic) const
{
  const ConcretePoleResidue *pr_parasitic =
    static_cast<const ConcretePoleResidue*>(parasitic);
  return pr_parasitic->poleResidueCount();
}

void
ConcreteParasitics::poleResidue(const Parasitic *parasitic,
                                int pole_index,
                                ComplexFloat &pole,
                                ComplexFloat &residue) const
{
  const ConcretePoleResidue *pr_parasitic =
    static_cast<const ConcretePoleResidue*>(parasitic);
  pr_parasitic->poleResidue(pole_index, pole, residue);
}

////////////////////////////////////////////////////////////////

bool
ConcreteParasitics::isParasiticNetwork(const Parasitic *parasitic) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic && cparasitic->isParasiticNetwork();
}

Parasitic *
ConcreteParasitics::findParasiticNetwork(const Net *net)
{
  LockGuard lock(lock_);
  auto itr = parasitic_network_map_.find(net);
  if (itr != parasitic_network_map_.end())
    return &itr->second;
  else
    return nullptr;
}

Parasitic *
ConcreteParasitics::findParasiticNetwork(const Pin *pin)
{
  if (!parasitic_network_map_.empty()) {
    LockGuard lock(lock_);
    if (!parasitic_network_map_.empty()) {
      // Only call findParasiticNet if parasitics exist.
      const Net *net = findParasiticNet(pin);

      if (!parasitic_network_map_.empty()) {
        auto itr = parasitic_network_map_.find(net);
        if (itr != parasitic_network_map_.end())
          return &itr->second;
      }
    }
  }
  return nullptr;
}

Parasitic *
ConcreteParasitics::makeParasiticNetwork(const Net *net,
                                         bool includes_pin_caps)
{
  LockGuard lock(lock_);
  auto itr = parasitic_network_map_.find(net);
  if (itr != parasitic_network_map_.end()) {
    parasitic_network_map_.erase(itr);
    for (const Pin *drvr_pin : *network_->drivers(net))
      deleteParasitics(drvr_pin);
  }
  parasitic_network_map_.emplace(net, ConcreteParasiticNetwork(net, includes_pin_caps,
                                                               network_));
  return &parasitic_network_map_.find(net)->second;
}

void
ConcreteParasitics::deleteParasiticNetwork(const Net *net)
{
  LockGuard lock(lock_);
  parasitic_network_map_.erase(net);
}

const Net *
ConcreteParasitics::net(const Parasitic *parasitic) const
{
  const ConcreteParasiticNetwork *cparasitic =
    static_cast<const ConcreteParasiticNetwork*>(parasitic);
  if (cparasitic->isParasiticNetwork())
    return cparasitic->net();
  else
    return nullptr;
}

bool
ConcreteParasitics::includesPinCaps(const Parasitic *parasitic) const
{
  const ConcreteParasiticNetwork *cparasitic =
    static_cast<const ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->includesPinCaps();
}

ParasiticNode *
ConcreteParasitics::findParasiticNode(Parasitic *parasitic,
                                      const Net *net,
                                      uint32_t id,
                                      const Network *network) const
{
  const ConcreteParasiticNetwork *cparasitic =
    static_cast<const ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->findParasiticNode(net, id, network);
}

ParasiticNode *
ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic,
                                        const Net *net,
                                        uint32_t id,
                                        const Network *network)
{
  ConcreteParasiticNetwork *cparasitic =
    static_cast<ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->ensureParasiticNode(net, id, network);
}

ParasiticNode *
ConcreteParasitics::findParasiticNode(const Parasitic *parasitic,
                                      const Pin *pin) const
{
  const ConcreteParasiticNetwork *cparasitic =
    static_cast<const ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->findParasiticNode(pin);
}

ParasiticNode *
ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic,
                                        const Pin *pin,
                                        const Network *network)
{
  ConcreteParasiticNetwork *cparasitic =
    static_cast<ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->ensureParasiticNode(pin, network);
}

void
ConcreteParasitics::incrCap(ParasiticNode *node,
                            float cap)
{
  ConcreteParasiticNode *cnode = static_cast<ConcreteParasiticNode*>(node);
  cnode->incrCapacitance(cap);
}

void
ConcreteParasitics::makeCapacitor(Parasitic *parasitic,
                                  uint32_t id,
                                  float cap,
                                  ParasiticNode *node1,
                                  ParasiticNode *node2)
{
  ConcreteParasiticNode *cnode1 = static_cast<ConcreteParasiticNode*>(node1);
  ConcreteParasiticNode *cnode2 = static_cast<ConcreteParasiticNode*>(node2);
  ConcreteParasiticCapacitor *capacitor =
    new ConcreteParasiticCapacitor(id, cap, cnode1, cnode2);
  ConcreteParasiticNetwork *cparasitic =
    static_cast<ConcreteParasiticNetwork*>(parasitic);
  cparasitic->addCapacitor(capacitor);
}

void
ConcreteParasitics::makeResistor(Parasitic *parasitic,
                                 uint32_t id,
                                 float res,
                                 ParasiticNode *node1,
                                 ParasiticNode *node2)
{
  ConcreteParasiticNode *cnode1 = static_cast<ConcreteParasiticNode*>(node1);
  ConcreteParasiticNode *cnode2 = static_cast<ConcreteParasiticNode*>(node2);
  ParasiticResistor *resistor = new ConcreteParasiticResistor(id, res,
                                                              cnode1, cnode2);
  ConcreteParasiticNetwork *cparasitic =
    static_cast<ConcreteParasiticNetwork*>(parasitic);
  cparasitic->addResistor(resistor);
}

ParasiticNodeSeq
ConcreteParasitics::nodes(const Parasitic *parasitic) const
{
  const ConcreteParasiticNetwork *cparasitic =
    static_cast<const ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->nodes();
}

ParasiticResistorSeq
ConcreteParasitics::resistors(const Parasitic *parasitic) const
{
  const ConcreteParasiticNetwork *cparasitic =
    static_cast<const ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->resistors();
}

ParasiticCapacitorSeq
ConcreteParasitics::capacitors(const Parasitic *parasitic) const
{
  const ConcreteParasiticNetwork *cparasitic =
    static_cast<const ConcreteParasiticNetwork*>(parasitic);
  return cparasitic->capacitors();
}


std::string
ConcreteParasitics::name(const ParasiticNode *node) const
{
  const ConcreteParasiticNode *cnode =
    static_cast<const ConcreteParasiticNode*>(node);
  return cnode->name(network_);
}

float
ConcreteParasitics::nodeGndCap(const ParasiticNode *node) const
{
  const ConcreteParasiticNode *cnode =
    static_cast<const ConcreteParasiticNode*>(node);
  return cnode->capacitance();
}

const Pin *
ConcreteParasitics::pin(const ParasiticNode *node) const
{
  const ConcreteParasiticNode *cnode =
    static_cast<const ConcreteParasiticNode*>(node);
  return cnode->pin();
}

const Net *
ConcreteParasitics::net(const ParasiticNode *node,
                        const Network *network) const
{
  const ConcreteParasiticNode *cnode =
    static_cast<const ConcreteParasiticNode*>(node);
  return cnode->net(network);
}

unsigned
ConcreteParasitics::netId(const ParasiticNode *node) const
{
  const ConcreteParasiticNode *cnode =
    static_cast<const ConcreteParasiticNode*>(node);
  return cnode->id();
}

bool
ConcreteParasitics::isExternal(const ParasiticNode *node) const
{
  const ConcreteParasiticNode *cnode =
    static_cast<const ConcreteParasiticNode*>(node);
  return cnode->isExternal();
}

////////////////////////////////////////////////////////////////

uint32_t
ConcreteParasitics::id(const ParasiticResistor *resistor) const
{
  const ConcreteParasiticResistor *cresistor =
    static_cast<const ConcreteParasiticResistor*>(resistor);
  return cresistor->id();
}

float
ConcreteParasitics::value(const ParasiticResistor *resistor) const
{
  const ConcreteParasiticResistor *cresistor =
    static_cast<const ConcreteParasiticResistor*>(resistor);
  return cresistor->value();
}

ParasiticNode *
ConcreteParasitics::node1(const ParasiticResistor *resistor) const
{
  const ConcreteParasiticResistor *cresistor =
    static_cast<const ConcreteParasiticResistor*>(resistor);
  return cresistor->node1();
}

ParasiticNode *
ConcreteParasitics::node2(const ParasiticResistor *resistor) const
{
  const ConcreteParasiticResistor *cresistor =
    static_cast<const ConcreteParasiticResistor*>(resistor);
  return cresistor->node2();
}

////////////////////////////////////////////////////////////////

uint32_t
ConcreteParasitics::id(const ParasiticCapacitor *capacitor) const
{
  const ConcreteParasiticCapacitor *ccapacitor =
    static_cast<const ConcreteParasiticCapacitor*>(capacitor);
  return ccapacitor->id();
}

float
ConcreteParasitics::value(const ParasiticCapacitor *capacitor) const
{
  const ConcreteParasiticCapacitor *ccapacitor =
    static_cast<const ConcreteParasiticCapacitor*>(capacitor);
  return ccapacitor->value();
}

ParasiticNode *
ConcreteParasitics::node1(const ParasiticCapacitor *capacitor) const
{
  const ConcreteParasiticCapacitor *ccapacitor =
    static_cast<const ConcreteParasiticCapacitor*>(capacitor);
  return ccapacitor->node1();
}

ParasiticNode *
ConcreteParasitics::node2(const ParasiticCapacitor *capacitor) const
{
  const ConcreteParasiticCapacitor *ccapacitor =
    static_cast<const ConcreteParasiticCapacitor*>(capacitor);
  return ccapacitor->node2();
}

////////////////////////////////////////////////////////////////

PinSet
ConcreteParasitics::unannotatedLoads(const Parasitic *parasitic,
                                     const Pin *drvr_pin) const
{
  const ConcreteParasitic *cparasitic = static_cast<const ConcreteParasitic*>(parasitic);
  return cparasitic->unannotatedLoads(drvr_pin, this);
}

} // namespace sta
