// 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 "Liberty.hh"

#include <cstddef>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <utility>

#include "ConcreteLibrary.hh"
#include "ContainerHelpers.hh"
#include "Debug.hh"
#include "Delay.hh"
#include "EnumNameMap.hh"
#include "Error.hh"
#include "Format.hh"
#include "FuncExpr.hh"
#include "InternalPower.hh"
#include "LibertyClass.hh"
#include "MinMax.hh"
#include "Mutex.hh"
#include "Network.hh"
#include "NetworkClass.hh"
#include "ObjectId.hh"
#include "PatternMatch.hh"
#include "PortDirection.hh"
#include "Report.hh"
#include "Scene.hh"
#include "Sequential.hh"
#include "TableModel.hh"
#include "TimingArc.hh"
#include "TimingRole.hh"
#include "Transition.hh"
#include "Units.hh"
#include "Wireload.hh"

namespace sta {

void
initLiberty()
{
  TimingArcSet::init();
}

void
deleteLiberty()
{
  TimingArcSet::destroy();
}

LibertyLibrary::LibertyLibrary(std::string_view name,
                               std::string_view filename) :
  ConcreteLibrary(name, filename, true),
  units_(new Units())
{
  // Scalar templates are builtin.
  for (int i = 0; i != table_template_type_count; i++) {
    TableTemplateType type = static_cast<TableTemplateType>(i);
    makeTableTemplate("scalar", type);
  }
}

LibertyLibrary::~LibertyLibrary()
{
  for (auto rf_index : RiseFall::rangeIndex()) {
    TableModel *model = wire_slew_degradation_tbls_[rf_index];
    delete model;
  }
  delete units_;
  // default_ocv_derate_ points into ocv_derate_map_; no separate delete

  delete buffers_;
  delete inverters_;
}

LibertyCell *
LibertyLibrary::findLibertyCell(std::string_view name) const
{
  return static_cast<LibertyCell*>(findCell(name));
}

LibertyCellSeq
LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern)
{
  LibertyCellSeq matches;
  LibertyCellIterator cell_iter(this);
  while (cell_iter.hasNext()) {
    LibertyCell *cell = cell_iter.next();
    if (pattern->match(cell->name()))
      matches.push_back(cell);
  }
  return matches;
}

LibertyCellSeq *
LibertyLibrary::inverters()
{
  if (inverters_ == nullptr) {
    inverters_ = new LibertyCellSeq;
    LibertyCellIterator cell_iter(this);
    while (cell_iter.hasNext()) {
      LibertyCell *cell = cell_iter.next();
      if (!cell->dontUse()
          && cell->isInverter())
        inverters_->push_back(cell);
    }
  }
  return inverters_;
}

LibertyCellSeq *
LibertyLibrary::buffers()
{
  if (buffers_ == nullptr) {
    buffers_ = new LibertyCellSeq;
    LibertyCellIterator cell_iter(this);
    while (cell_iter.hasNext()) {
      LibertyCell *cell = cell_iter.next();
      if (!cell->dontUse()
          && cell->isBuffer())
        buffers_->push_back(cell);
    }
  }
  return buffers_;
}

void
LibertyLibrary::setDelayModelType(DelayModelType type)
{
  delay_model_type_ = type;
}

BusDcl *
LibertyLibrary::makeBusDcl(std::string_view name,
                           int from,
                           int to)
{
  std::string key(name);
  auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::string(name), from, to);
  return &it->second;
}

BusDcl *
LibertyLibrary::findBusDcl(std::string_view name)
{
  return findStringValuePtr(bus_dcls_, name);
}

BusDclSeq
LibertyLibrary::busDcls() const
{
  BusDclSeq dcls;
  for (auto &[key, dcl] : bus_dcls_)
    dcls.push_back(const_cast<BusDcl *>(&dcl));
  return dcls;
}

TableTemplate *
LibertyLibrary::makeTableTemplate(std::string_view name,
                                  TableTemplateType type)
{
  std::string key(name);
  auto [it, inserted] = template_maps_[static_cast<size_t>(type)].try_emplace(std::move(key),
                                                             std::string(name),
                                                             type);
  return &it->second;
}

TableTemplate *
LibertyLibrary::findTableTemplate(std::string_view name,
                                  TableTemplateType type)
{
  return findStringValuePtr(template_maps_[static_cast<size_t>(type)], name);
}

TableTemplateSeq
LibertyLibrary::tableTemplates() const
{
  TableTemplateSeq tbl_templates;
  for (const auto & template_map : template_maps_) {
    for (auto &[key, tbl_template] : template_map)
      tbl_templates.push_back(const_cast<TableTemplate *>(&tbl_template));
  }
  return tbl_templates;
}

TableTemplateSeq
LibertyLibrary::tableTemplates(TableTemplateType type) const
{
  TableTemplateSeq tbl_templates;
  for (auto &[key, tbl_template] : template_maps_[static_cast<size_t>(type)])
    tbl_templates.push_back(const_cast<TableTemplate *>(&tbl_template));
  return tbl_templates;
}

void
LibertyLibrary::setNominalProcess(float process)
{
  nominal_process_ = process;
}

void
LibertyLibrary::setNominalVoltage(float voltage)
{
  nominal_voltage_ = voltage;
}

void
LibertyLibrary::setNominalTemperature(float temperature)
{
  nominal_temperature_ = temperature;
}

void
LibertyLibrary::setScaleFactors(ScaleFactors *scales)
{
  scale_factors_ = scales;
}

ScaleFactors *
LibertyLibrary::makeScaleFactors(std::string_view name)
{
  std::string key(name);
  auto [it, inserted] = scale_factors_map_.emplace(std::move(key), std::string(name));
  return &it->second;
}

ScaleFactors *
LibertyLibrary::findScaleFactors(std::string_view name)
{
  return findStringValuePtr(scale_factors_map_,name);
}

float
LibertyLibrary::scaleFactor(ScaleFactorType type,
                            const Pvt *pvt) const
{
  return scaleFactor(type, 0, nullptr, pvt);
}

float
LibertyLibrary::scaleFactor(ScaleFactorType type,
                            const LibertyCell *cell,
                            const Pvt *pvt) const
{
  return scaleFactor(type, 0, cell, pvt);
}

float
LibertyLibrary::scaleFactor(ScaleFactorType type,
                            int rf_index,
                            const LibertyCell *cell,
                            const Pvt *pvt) const
{
  if (pvt == nullptr)
    pvt = default_operating_conditions_;
  // If there is no operating condition, nominal pvt values are used.
  // All scale factors are unity for nominal pvt.
  if (pvt) {
    ScaleFactors *scale_factors = nullptr;
    // Cell level scale factors have precedence over library scale factors.
    if (cell)
      scale_factors = cell->scaleFactors();
    if (scale_factors == nullptr)
      scale_factors = scale_factors_;
    if (scale_factors) {
      float process_scale = 1.0F + (pvt->process() - nominal_process_)
        * scale_factors->scale(type, ScaleFactorPvt::process, rf_index);
      float temp_scale = 1.0F + (pvt->temperature() - nominal_temperature_)
        * scale_factors->scale(type, ScaleFactorPvt::temp, rf_index);
      float volt_scale = 1.0F + (pvt->voltage() - nominal_voltage_)
        * scale_factors->scale(type, ScaleFactorPvt::volt, rf_index);
      float scale = process_scale * temp_scale * volt_scale;
      return scale;
    }
  }
  return 1.0F;
}

void
LibertyLibrary::setWireSlewDegradationTable(TableModel *model,
                                            const RiseFall *rf)
{
  int rf_index = rf->index();
  if (wire_slew_degradation_tbls_[rf_index])
    delete wire_slew_degradation_tbls_[rf_index];
  wire_slew_degradation_tbls_[rf_index] = model;
}

TableModel *
LibertyLibrary::wireSlewDegradationTable(const RiseFall *rf) const
{
  return wire_slew_degradation_tbls_[rf->index()];
}

float
LibertyLibrary::degradeWireSlew(const RiseFall *rf,
                                float in_slew,
                                float wire_delay) const
{
  const TableModel *model = wireSlewDegradationTable(rf);
  if (model)
    return degradeWireSlew(model, in_slew, wire_delay);
  else
    return in_slew;
}

float
LibertyLibrary::degradeWireSlew(const TableModel *model,
                                float in_slew,
                                float wire_delay) const
{
  switch (model->order()) {
  case 0:
    return model->findValue(0.0, 0.0, 0.0);
  case 1: {
    const TableAxis *axis1 = model->axis1();
    TableAxisVariable var1 = axis1->variable();
    if (var1 == TableAxisVariable::output_pin_transition)
      return model->findValue(in_slew, 0.0, 0.0);
    else if (var1 == TableAxisVariable::connect_delay)
      return model->findValue(wire_delay, 0.0, 0.0);
    else {
      criticalError(1116, "unsupported slew degradation table axes");
      return 0.0;
    }
  }
  case 2: {
    const TableAxis *axis1 = model->axis1();
    const TableAxis * axis2 = model->axis2();
    TableAxisVariable var1 = axis1->variable();
    TableAxisVariable var2 = axis2->variable();
    if (var1 == TableAxisVariable::output_pin_transition
        && var2 == TableAxisVariable::connect_delay)
      return model->findValue(in_slew, wire_delay, 0.0);
    else if (var1 == TableAxisVariable::connect_delay
             && var2 == TableAxisVariable::output_pin_transition)
      return model->findValue(wire_delay, in_slew, 0.0);
    else {
      criticalError(1117, "unsupported slew degradation table axes");
      return 0.0;
    }
  }
  default:
    criticalError(1118, "unsupported slew degradation table order");
    return 0.0;
  }
}

// Check for supported axis variables.
// Return true if axes are supported.
bool
LibertyLibrary::checkSlewDegradationAxes(const TableModel *table_model)
{
  switch (table_model->order()) {
  case 0:
    return true;
  case 1: {
    const TableAxis *axis1 = table_model->axis1();
    TableAxisVariable var1 = axis1->variable();
    return var1 == TableAxisVariable::output_pin_transition
      || var1 == TableAxisVariable::connect_delay;
  }
  case 2: {
    const TableAxis *axis1 = table_model->axis1();
    const TableAxis *axis2 = table_model->axis2();
    TableAxisVariable var1 = axis1->variable();
    TableAxisVariable var2 = axis2->variable();
    return (var1 == TableAxisVariable::output_pin_transition
            && var2 == TableAxisVariable::connect_delay)
      || (var1 == TableAxisVariable::connect_delay
          && var2 == TableAxisVariable::output_pin_transition);
  }
  default:
    criticalError(1119, "unsupported slew degradation table axes");
    return 0.0;
  }
}

void
LibertyLibrary::defaultMaxFanout(float &fanout,
                                 bool &exists) const
{
  fanout = default_max_fanout_;
  exists = default_max_fanout_exists_;
}

void
LibertyLibrary::setDefaultMaxFanout(float fanout)
{
  default_max_fanout_ = fanout;
  default_max_fanout_exists_ = true;
}

void
LibertyLibrary::defaultMaxSlew(float &slew,
                               bool &exists) const
{
  slew = default_max_slew_;
  exists = default_max_slew_exists_;
}

void
LibertyLibrary::setDefaultMaxSlew(float slew)
{
  default_max_slew_ = slew;
  default_max_slew_exists_ = true;
}

void
LibertyLibrary::defaultMaxCapacitance(float &cap,
                                      bool &exists) const
{
  cap = default_max_cap_;
  exists = default_max_cap_exists_;
}

void
LibertyLibrary::setDefaultMaxCapacitance(float cap)
{
  default_max_cap_ = cap;
  default_max_cap_exists_ = true;
}

void
LibertyLibrary::defaultFanoutLoad(// Return values.
                                  float &fanout,
                                  bool &exists) const
{
  fanout = default_fanout_load_;
  exists = default_fanout_load_exists_;
}

void
LibertyLibrary::setDefaultFanoutLoad(float load)
{
  default_fanout_load_ = load;
  default_fanout_load_exists_ = true;
}

void
LibertyLibrary::setDefaultBidirectPinCap(float cap)
{
  default_bidirect_pin_cap_ = cap;
}

void
LibertyLibrary::setDefaultInputPinCap(float cap)
{
  default_input_pin_cap_ = cap;
}

void
LibertyLibrary::setDefaultOutputPinCap(float cap)
{
  default_output_pin_cap_ = cap;
}

void
LibertyLibrary::defaultIntrinsic(const RiseFall *rf,
                                 // Return values.
                                 float &intrinsic,
                                 bool &exists) const
{
  default_intrinsic_.value(rf, intrinsic, exists);
}

void
LibertyLibrary::setDefaultIntrinsic(const RiseFall *rf,
                                    float value)
{
  default_intrinsic_.setValue(rf, value);
}

void
LibertyLibrary::defaultPinResistance(const RiseFall *rf,
                                     const PortDirection *dir,
                                     // Return values.
                                     float &res,
                                     bool &exists) const
{
  if (dir->isAnyTristate())
    defaultBidirectPinRes(rf, res, exists);
  else
    defaultOutputPinRes(rf, res, exists);
}

void
LibertyLibrary::defaultBidirectPinRes(const RiseFall *rf,
                                      // Return values.
                                      float &res,
                                      bool &exists) const
{
  default_inout_pin_res_.value(rf, res, exists);
}

void
LibertyLibrary::setDefaultBidirectPinRes(const RiseFall *rf,
                                         float value)
{
  default_inout_pin_res_.setValue(rf, value);
}

void
LibertyLibrary::defaultOutputPinRes(const RiseFall *rf,
                                    // Return values.
                                    float &res,
                                    bool &exists) const
{
  default_output_pin_res_.value(rf, res, exists);
}

void
LibertyLibrary::setDefaultOutputPinRes(const RiseFall *rf,
                                       float value)
{
  default_output_pin_res_.setValue(rf, value);
}

Wireload *
LibertyLibrary::makeWireload(std::string_view name)
{
  std::string key(name);
  auto [it, inserted] = wireloads_.try_emplace(std::move(key), std::string(name), this);
  return &it->second;
}

const Wireload *
LibertyLibrary::findWireload(std::string_view name)
{
  return findStringValuePtr(wireloads_, name);
}

void
LibertyLibrary::setDefaultWireload(const Wireload *wireload)
{
  default_wire_load_ = wireload;
}

const Wireload *
LibertyLibrary::defaultWireload() const
{
  return default_wire_load_;
}

WireloadSelection *
LibertyLibrary::makeWireloadSelection(std::string_view name)
{
  std::string key(name);
  auto [it, inserted] = wire_load_selections_.try_emplace(std::move(key),
                                                        std::string(name));
  return &it->second;
}

const WireloadSelection *
LibertyLibrary::findWireloadSelection(std::string_view name) const
{
  return findStringValuePtr(wire_load_selections_, name);
}

const WireloadSelection *
LibertyLibrary::defaultWireloadSelection() const
{
  return default_wire_load_selection_;
}

void
LibertyLibrary::setDefaultWireloadSelection(const WireloadSelection *selection)
{
  default_wire_load_selection_ = selection;
}

WireloadMode
LibertyLibrary::defaultWireloadMode() const
{
  return default_wire_load_mode_;
}

void
LibertyLibrary::setDefaultWireloadMode(WireloadMode mode)
{
  default_wire_load_mode_ = mode;
}

OperatingConditions *
LibertyLibrary::makeOperatingConditions(std::string_view name)
{
  std::string key(name);
  auto [it, inserted] = operating_conditions_.try_emplace(std::move(key), std::string(name));
  return &it->second;
}

OperatingConditions *
LibertyLibrary::findOperatingConditions(std::string_view name)
{
  return findStringValuePtr(operating_conditions_, name);
}

OperatingConditions *
LibertyLibrary::defaultOperatingConditions() const
{
  return default_operating_conditions_;
}

void
LibertyLibrary::setDefaultOperatingConditions(OperatingConditions *op_cond)
{
  default_operating_conditions_ = op_cond;
}

float
LibertyLibrary::inputThreshold(const RiseFall *rf) const
{
  return input_threshold_[rf->index()];
}

void
LibertyLibrary::setInputThreshold(const RiseFall *rf,
                                  float th)
{
  input_threshold_[rf->index()] = th;
}

float
LibertyLibrary::outputThreshold(const RiseFall *rf) const
{
  return output_threshold_[rf->index()];
}

void
LibertyLibrary::setOutputThreshold(const RiseFall *rf,
                                   float th)
{
  output_threshold_[rf->index()] = th;
}

float
LibertyLibrary::slewLowerThreshold(const RiseFall *rf) const
{
  return slew_lower_threshold_[rf->index()];
}

void
LibertyLibrary::setSlewLowerThreshold(const RiseFall *rf,
                                      float th)
{
  slew_lower_threshold_[rf->index()] = th;
}

float
LibertyLibrary::slewUpperThreshold(const RiseFall *rf) const
{
  return slew_upper_threshold_[rf->index()];
}

void
LibertyLibrary::setSlewUpperThreshold(const RiseFall *rf,
                                      float th)
{
  slew_upper_threshold_[rf->index()] = th;
}

float
LibertyLibrary::slewDerateFromLibrary() const
{
  return slew_derate_from_library_;
}

void
LibertyLibrary::setSlewDerateFromLibrary(float derate)
{
  slew_derate_from_library_ = derate;
}

LibertyCell *
LibertyLibrary::makeScaledCell(std::string_view name,
                               std::string_view filename)
{
  return new LibertyCell(this, name, filename);
}

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

void
LibertyLibrary::makeSceneMap(LibertyLibrary *lib,
                             Scene *scene,
                             const MinMaxAll *min_max,
                             Network *network,
                             Report *report)
{
  LibertyCellIterator cell_iter(lib);
  while (cell_iter.hasNext()) {
    LibertyCell *cell = cell_iter.next();
    LibertyCell *link_cell = network->findLibertyCell(cell->name());
    if (link_cell)
      makeSceneMap(link_cell, cell, scene, min_max, report);
  }
}

// Map a cell linked in the network to the corresponding liberty cell
// to use for delay calculation at a scene.
void
LibertyLibrary::makeSceneMap(LibertyCell *link_cell,
                             LibertyCell *scene_cell,
                             Scene *scene,
                             const MinMaxAll *min_max,
                             Report *report)
{
  for (const MinMax *mm : min_max->range()) {
    size_t lib_ap_index = scene->libertyIndex(mm);
    link_cell->setSceneCell(scene_cell, lib_ap_index);
  }

  LibertyCellPortBitIterator port_iter1(link_cell);
  while (port_iter1.hasNext()) {
    LibertyPort *port1 = port_iter1.next();
    LibertyPort *port2 = scene_cell->findLibertyPort(port1->name());
    if (port2) {
      for (const MinMax *mm : min_max->range()) {
        size_t lib_ap_index = scene->libertyIndex(mm);
        port1->setScenePort(port2, lib_ap_index);
      }
    }
    else
      report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.",
                   link_cell->library()->name(),
                   link_cell->name(),
                   port1->name(),
                   scene_cell->library()->name(),
                   scene_cell->name());
  }

  for (TimingArcSet *arc_set1 : link_cell->timing_arc_sets_) {
    TimingArcSet *arc_set2 = scene_cell->findTimingArcSet(arc_set1);
    if (arc_set2) {
      const TimingArcSeq &arcs1 = arc_set1->arcs();
      const TimingArcSeq &arcs2 = arc_set2->arcs();
      auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin();
      for (;
           arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end();
           arc_itr1++, arc_itr2++) {
        TimingArc *arc1 = *arc_itr1;
        TimingArc *arc2 = *arc_itr2;
        if (TimingArc::equiv(arc1, arc2)) {
          for (const MinMax *mm : min_max->range()) {
            size_t lib_ap_index = scene->libertyIndex(mm);
            arc1->setSceneArc(arc2, lib_ap_index);
          }
        }
      }
    }
    else
      report->warn(1111, "cell {}/{} {} -> {} timing group {} not found in cell {}/{}.",
                   link_cell->library()->name(),
                   link_cell->name(),
                   arc_set1->from() ? arc_set1->from()->name() : "",
                   arc_set1->to()->name(),
                   arc_set1->role()->to_string(),
                   scene_cell->library()->name(),
                   scene_cell->name());
  }
}

void
LibertyLibrary::checkScenes(LibertyCell *cell,
                             const SceneSeq &scenes,
                             Report *report)
{
  for (const Scene *scene : scenes) {
    for (auto min_max : MinMax::range()) {
      if (!cell->checkSceneCell(scene, min_max))
        report->error(1112, "Liberty cell {}/{} for corner {}/{} not found.",
                      cell->libertyLibrary()->name(),
                      cell->name(),
                      scene->name(),
                      min_max->to_string());
    }
  }
}

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

float
LibertyLibrary::ocvArcDepth() const
{
  return ocv_arc_depth_;
}

void
LibertyLibrary::setOcvArcDepth(float depth)
{
  ocv_arc_depth_ = depth;
}

OcvDerate *
LibertyLibrary::defaultOcvDerate() const
{
  return default_ocv_derate_;
}

void
LibertyLibrary::setDefaultOcvDerate(OcvDerate *derate)
{
  default_ocv_derate_ = derate;
}

OcvDerate *
LibertyLibrary::makeOcvDerate(std::string_view name)
{
  std::string key(name);
  auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::string(name));
  return &it->second;
}

OcvDerate *
LibertyLibrary::findOcvDerate(std::string_view derate_name)
{
  return findStringValuePtr(ocv_derate_map_, derate_name);
}

void
LibertyLibrary::addSupplyVoltage(std::string_view supply_name,
                                 float voltage)
{
  supply_voltage_map_[std::string(supply_name)] = voltage;
}

void
LibertyLibrary::supplyVoltage(std::string_view supply_name,
                              // Return value.
                              float &voltage,
                              bool &exists) const
{
  auto itr = supply_voltage_map_.find(supply_name);
  if (itr != supply_voltage_map_.end()) {
    voltage = itr->second;
    exists = true;
  }
  else {
    voltage = 0.0;
    exists = false;
  }
}

bool
LibertyLibrary::supplyExists(std::string_view supply_name) const
{
  return supply_voltage_map_.contains(supply_name);
}

DriverWaveform *
LibertyLibrary::findDriverWaveform(std::string_view name)
{
  return findStringValuePtr(driver_waveform_map_, name);
}

DriverWaveform *
LibertyLibrary::makeDriverWaveform(std::string_view name,
                                   const TablePtr &waveforms)
{
  std::string key(name);
  auto [it, inserted] = driver_waveform_map_.try_emplace(std::move(key),
                                                           std::string(name),
                                                           waveforms);
  return &it->second;
}

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

LibertyCellIterator::LibertyCellIterator(const LibertyLibrary *library) :
  iter_(library->cell_map_)
{
}

bool
LibertyCellIterator::hasNext()
{
  return iter_.hasNext();
}

LibertyCell *
LibertyCellIterator::next()
{
  return static_cast<LibertyCell*>(iter_.next());
}

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

LibertyCell::LibertyCell(LibertyLibrary *library,
                         std::string_view name,
                         std::string_view filename) :
  ConcreteCell(name, filename, true, library),
  liberty_library_(library)
{
  liberty_cell_ = this;
}

LibertyCell::~LibertyCell()
{
  deleteContents(timing_arc_sets_);

  delete statetable_;
  deleteContents(scaled_cells_);

  delete test_cell_;
}

LibertyPort *
LibertyCell::findLibertyPort(std::string_view name) const
{
  return static_cast<LibertyPort*>(findPort(name));
}

LibertyPortSeq
LibertyCell::findLibertyPortsMatching(PatternMatch *pattern) const
{
  LibertyPortSeq matches;
  LibertyCellPortIterator port_iter(this);
  while (port_iter.hasNext()) {
    LibertyPort *port = port_iter.next();
    if (pattern->match(port->name()))
      matches.push_back(port);
    if (port->hasMembers()) {
      LibertyPortMemberIterator port_iter2(port);
      while (port_iter2.hasNext()) {
        LibertyPort *port2 = port_iter2.next();
        if (pattern->match(port2->name()))
          matches.push_back(port2);
      }
    }
  }
  return matches;
}

void
LibertyCell::addPort(ConcretePort *port)
{
  ConcreteCell::addPort(port);
  if (port->direction()->isInternal())
    has_internal_ports_ = true;
}


void
LibertyCell::setHasInternalPorts(bool has_internal)
{
  has_internal_ports_ = has_internal;
}

ModeDef *
LibertyCell::makeModeDef(std::string_view name)
{
  std::string key(name);
  auto [it, inserted] = mode_defs_.try_emplace(std::move(key), std::string(name));
  return &it->second;
}

const ModeDef *
LibertyCell::findModeDef(std::string_view name) const
{
  return findStringValuePtr(mode_defs_, name);
}

void
LibertyCell::setScaleFactors(ScaleFactors *scale_factors)
{
  scale_factors_ = scale_factors;
}

BusDcl *
LibertyCell::makeBusDcl(std::string_view name,
                        int from,
                        int to)
{
  std::string key(name);
  auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::string(name), from, to);
  return &it->second;
}

BusDcl *
LibertyCell::findBusDcl(std::string_view name)
{
  return findStringValuePtr(bus_dcls_, name);
}

void
LibertyCell::setArea(float area)
{
  area_ = area;
}

void
LibertyCell::setDontUse(bool dont_use)
{
  dont_use_ = dont_use;
}

void
LibertyCell::setIsMacro(bool is_macro)
{
  is_macro_ = is_macro;
}

void
LibertyCell::setIsMemory(bool is_memory)
{
  is_memory_ = is_memory;
}

void
LibertyCell::setIsPad(bool is_pad)
{
  is_pad_ = is_pad;
}

void
LibertyCell::setIsClockCell(bool is_clock_cell)
{
  is_clock_cell_ = is_clock_cell;
}

void
LibertyCell::setIsLevelShifter(bool is_level_shifter)
{
  is_level_shifter_ = is_level_shifter;
}

void
LibertyCell::setLevelShifterType(LevelShifterType level_shifter_type)
{
  level_shifter_type_ = level_shifter_type;
}

void
LibertyCell::setIsIsolationCell(bool is_isolation_cell)
{
  is_isolation_cell_ = is_isolation_cell;
}

void
LibertyCell::setAlwaysOn(bool always_on)
{
  always_on_ = always_on;
}

void
LibertyCell::setSwitchCellType(SwitchCellType switch_cell_type)
{
  switch_cell_type_ = switch_cell_type;
}

void
LibertyCell::setInterfaceTiming(bool value)
{
  interface_timing_ = value;
}

bool
LibertyCell::isClockGateLatchPosedge() const
{
  return clock_gate_type_ == ClockGateType::latch_posedge;
}

bool
LibertyCell::isClockGateLatchNegedge() const
{
  return clock_gate_type_ == ClockGateType::latch_negedge;
}

bool
LibertyCell::isClockGateOther() const
{
  return clock_gate_type_ == ClockGateType::other;
}

bool
LibertyCell::isClockGate() const
{
  return clock_gate_type_ != ClockGateType::none;
}

void
LibertyCell::setClockGateType(ClockGateType type)
{
  clock_gate_type_ = type;
}

bool
LibertyCell::isBuffer() const
{
  LibertyPort *input;
  LibertyPort *output;
  bufferPorts(input, output);
  return input && output
    && hasBufferFunc(input, output)
    && !is_level_shifter_
    && !is_pad_;
}

bool
LibertyCell::hasBufferFunc(const LibertyPort *input,
                           const LibertyPort *output) const
{
  FuncExpr *func = output->function();
  return func
    && func->op() == FuncExpr::Op::port
    && func->port() == input;
}

bool
LibertyCell::isInverter() const
{
  LibertyPort *input;
  LibertyPort *output;
  bufferPorts(input, output);
  return input && output
    && hasInverterFunc(input, output)
    && !is_level_shifter_
    && !is_pad_;
}

bool
LibertyCell::hasInverterFunc(const LibertyPort *input,
                             const LibertyPort *output) const
{
  FuncExpr *func = output->function();
  return func
    && func->op() == FuncExpr::Op::not_
    && func->left()->op() == FuncExpr::Op::port
    && func->left()->port() == input;
}

void
LibertyCell::bufferPorts(// Return values.
                         LibertyPort *&input,
                         LibertyPort *&output) const
{
  input = nullptr;
  output = nullptr;
  for (ConcretePort *cport : ports_) {
    LibertyPort *port = static_cast<LibertyPort*>(cport);
    PortDirection *dir = port->direction();
    if (dir->isInput()) {
      if (input) {
        // More than one input.
        input = nullptr;
        output = nullptr;
        break;
      }
      input = port;
    }
    else if (dir->isOutput()) {
      if (output) {
        // More than one output.
        input = nullptr;
        output = nullptr;
        break;
      }
      output = port;
    }
    else if (!port->isPwrGnd()) {
        // Invalid direction.
	input = nullptr;
	output = nullptr;
	break;
    }
  }
}

TimingArcSet *
LibertyCell::makeTimingArcSet(LibertyPort *from,
                              LibertyPort *to,
                              LibertyPort *related_out,
                              const TimingRole *role,
                              TimingArcAttrsPtr attrs)
{
  size_t set_index = timing_arc_sets_.size();
  TimingArcSet *arc_set = new TimingArcSet(this, from, to, related_out, role,
                                           std::move(attrs), set_index);
  timing_arc_sets_.push_back(arc_set);
  return arc_set;
}

void
LibertyCell::makeInternalPower(LibertyPort *port,
                               LibertyPort *related_port,
                               LibertyPort *related_pg_pin,
                               const std::shared_ptr<FuncExpr> &when,
                               const InternalPowerModels &models)
{
  internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models);
  port_internal_powers_[port].push_back(internal_powers_.size() - 1);
}

InternalPowerPtrSeq
LibertyCell::internalPowers(const LibertyPort *port) const
{
  InternalPowerPtrSeq result;
  const InternalPowerIndexSeq &pwrs = findKeyValue(port_internal_powers_,port);
  for (size_t idx : pwrs)
    result.push_back(&internal_powers_[idx]);
  return result;
}

void
LibertyCell::makeLeakagePower(LibertyPort *related_pg_port,
                              FuncExpr *when,
                              float power)
{
  leakage_powers_.emplace_back(this, related_pg_port, when, power);
}

void
LibertyCell::setLeakagePower(float leakage)
{
  leakage_power_ = leakage;
  leakage_power_exists_ = true;
}

void
LibertyCell::leakagePower(// Return values.
                          float &leakage,
                          bool &exists) const
{
  leakage = leakage_power_;
  exists = leakage_power_exists_;
}

void
LibertyCell::finish(bool infer_latches,
                    Report *report,
                    Debug *debug)
{
  translatePresetClrCheckRoles();
  makeTimingArcPortMaps();
  findDefaultCondArcs();
  makeLatchEnables(report, debug);
  if (infer_latches)
    inferLatchRoles(report, debug);
}

void
LibertyCell::findDefaultCondArcs()
{
  for (auto &[port_pair, sets] : port_timing_arc_set_map_) {
    bool has_cond_arcs = false;
    for (auto set : sets) {
      if (set->cond()) {
        has_cond_arcs = true;
        break;
      }
    }
    if (has_cond_arcs) {
      for (auto &set : sets) {
        if (!set->cond())
          set->setIsCondDefault(true);
      }
    }
  }
}

// Timing checks for set/clear pins use setup/hold times.  This
// changes their roles to recovery/removal by finding the set/clear
// pins and then translating the timing check roles.
void
LibertyCell::translatePresetClrCheckRoles()
{
  LibertyPortSet pre_clr_ports;
  for (auto arc_set : timing_arc_sets_) {
    if (arc_set->role() == TimingRole::regSetClr())
      pre_clr_ports.insert(arc_set->from());
  }

  if (!pre_clr_ports.empty()) {
    for (auto arc_set : timing_arc_sets_) {
      if (findKey(pre_clr_ports, arc_set->to())) {
        if (arc_set->role() == TimingRole::setup())
          arc_set->setRole(TimingRole::recovery());
        else if (arc_set->role() == TimingRole::hold())
          arc_set->setRole(TimingRole::removal());
      }
    }
  }
}

void
LibertyCell::makeTimingArcPortMaps()
{
  for (TimingArcSet *arc_set : timing_arc_sets_) {
    LibertyPort *from = arc_set->from();
    LibertyPort *to = arc_set->to();
    if (from && to) {
      TimingArcSetSeq &sets = port_timing_arc_set_map_[{from, to}];
      sets.push_back(arc_set);
    }

    TimingArcSetSeq &from_sets = port_timing_arc_set_map_[{from, nullptr}];
    from_sets.push_back(arc_set);

    TimingArcSetSeq &to_sets = port_timing_arc_set_map_[{nullptr, to}];
    to_sets.push_back(arc_set);

    const TimingRole *role = arc_set->role();
    if (role == TimingRole::regClkToQ()
        || role == TimingRole::latchEnToQ()) {
      from->setIsRegClk(true);
      if (to)
        to->setIsRegOutput(true);
    }
    if (role->isTimingCheck())
      from->setIsCheckClk(true);

    timing_arc_set_set_.insert(arc_set);
  }
}

const TimingArcSetSeq &
LibertyCell::timingArcSetsFrom(const LibertyPort *from) const
{
  return timingArcSets(from, nullptr);
}

const TimingArcSetSeq &
LibertyCell::timingArcSetsTo(const LibertyPort *to) const
{
  return timingArcSets(nullptr, to);
}

const TimingArcSetSeq &
LibertyCell::timingArcSets(const LibertyPort *from,
                           const LibertyPort *to) const
{
  static const TimingArcSetSeq null_set;
  auto itr = port_timing_arc_set_map_.find({from, to});
  return (itr == port_timing_arc_set_map_.end()) ? null_set : itr->second;
}

TimingArcSet *
LibertyCell::findTimingArcSet(TimingArcSet *arc_set) const
{
  return findKey(timing_arc_set_set_, arc_set);
}

TimingArcSet *
LibertyCell::findTimingArcSet(size_t index) const
{
  return timing_arc_sets_[index];
}

size_t
LibertyCell::timingArcSetCount() const
{
  return timing_arc_sets_.size();
}

bool
LibertyCell::hasTimingArcs(LibertyPort *port) const
{
  return port_timing_arc_set_map_.contains({port, nullptr})
    || port_timing_arc_set_map_.contains({nullptr, port});
}

void
LibertyCell::makeSequential(int size,
                            bool is_register,
                            FuncExpr *clk,
                            FuncExpr *data,
                            FuncExpr *clear,
                            FuncExpr *preset,
                            LogicValue clr_preset_out,
                            LogicValue clr_preset_out_inv,
                            LibertyPort *output,
                            LibertyPort *output_inv)
{
  for (int bit = 0; bit < size; bit++) {
    FuncExpr *clk_bit = nullptr;
    if (clk)
      clk_bit = clk->bitSubExpr(bit);
    FuncExpr *data_bit = nullptr;
    if (data)
      data_bit = data->bitSubExpr(bit);
    FuncExpr *clear_bit = nullptr;
    if (clear)
      clear_bit = clear->bitSubExpr(bit);
    FuncExpr *preset_bit = nullptr;
    if (preset)
      preset_bit = preset->bitSubExpr(bit);
    LibertyPort *out_bit = output;
    if (output && output->hasMembers())
      out_bit = output->findLibertyMember(bit);
    LibertyPort *out_inv_bit = output_inv;
    if (output_inv && output_inv->hasMembers())
      out_inv_bit = output_inv->findLibertyMember(bit);
    sequentials_.emplace_back(is_register, clk_bit, data_bit,
                              clear_bit, preset_bit,
                              clr_preset_out, clr_preset_out_inv,
                              out_bit, out_inv_bit);
    size_t idx = sequentials_.size() - 1;
    port_to_seq_map_[sequentials_.back().output()] = idx;
    port_to_seq_map_[sequentials_.back().outputInv()] = idx;
  }
  delete clk;
  delete data;
  delete clear;
  delete preset;
}

Sequential *
LibertyCell::outputPortSequential(LibertyPort *port)
{
  auto it = port_to_seq_map_.find(port);
  if (it != port_to_seq_map_.end())
    return &sequentials_[it->second];
  return nullptr;
}

bool
LibertyCell::isSequential() const
{
  return !sequentials_.empty() || statetable_ != nullptr;
}

bool
LibertyCell::hasSequentials() const
{
  return !sequentials_.empty() || statetable_ != nullptr;
}

void
LibertyCell::makeStatetable(LibertyPortSeq &input_ports,
                            LibertyPortSeq &internal_ports,
                            StatetableRows &table)
{
  statetable_ = new Statetable(input_ports, internal_ports, table);
}

void
LibertyCell::addScaledCell(OperatingConditions *op_cond,
                           LibertyCell *scaled_cell)
{
  scaled_cells_[op_cond] = scaled_cell;

  LibertyCellPortBitIterator port_iter1(this);
  LibertyCellPortBitIterator port_iter2(scaled_cell);
  while (port_iter1.hasNext() && port_iter2.hasNext()) {
    LibertyPort *port = port_iter1.next();
    LibertyPort *scaled_port = port_iter2.next();
    port->addScaledPort(op_cond, scaled_port);
  }

  const TimingArcSetSeq &arc_sets1 = this->timingArcSets();
  const TimingArcSetSeq &arc_sets2 = scaled_cell->timingArcSets();
  for (auto set_itr1 = arc_sets1.begin(), set_itr2 = arc_sets2.begin();
       set_itr1 != arc_sets1.end() && set_itr2 != arc_sets2.end();
       set_itr1++, set_itr2++) {
    TimingArcSet *arc_set1 = *set_itr1;
    TimingArcSet *arc_set2 = *set_itr2;
    const TimingArcSeq &arcs1 = arc_set1->arcs();
    const TimingArcSeq &arcs2 = arc_set2->arcs();
    auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin();
    for (;
         arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end();
         arc_itr1++, arc_itr2++) {
      TimingArc *arc = *arc_itr1;
      const TimingArc *scaled_arc = *arc_itr2;

      if (TimingArc::equiv(arc, scaled_arc)) {
        TimingModel *model = scaled_arc->model();
        model->setIsScaled(true);
        arc->addScaledModel(op_cond, model);
      }
    }
  }
}

void
LibertyCell::setLibertyLibrary(LibertyLibrary *library)
{
  liberty_library_ = library;
  library_ = library;
}

void
LibertyCell::setHasInferedRegTimingArcs(bool infered)
{
  has_infered_reg_timing_arcs_ = infered;
}

void
LibertyCell::setTestCell(TestCell *test)
{
  test_cell_ = test;
}

LibertyCell *
LibertyCell::sceneCell(const Scene *scene,
                       const MinMax *min_max)
{
  return sceneCell(scene->libertyIndex(min_max));
}

LibertyCell *
LibertyCell::sceneCell(size_t lib_ap_index)
{
  if (scene_cells_.empty())
    return this;
  else if (lib_ap_index < scene_cells_.size())
    return scene_cells_[lib_ap_index];
  else
    return nullptr;
}

bool
LibertyCell::checkSceneCell(const Scene *scene,
                            const MinMax *min_max) const
{
  size_t lib_index = scene->libertyIndex(min_max);
  return scene_cells_.empty()
    || (lib_index < scene_cells_.size()
        && scene_cells_[lib_index]);
}

void
LibertyCell::setSceneCell(LibertyCell *scene_cell,
                          size_t lib_ap_index)
{
  if (lib_ap_index >= scene_cells_.size())
    scene_cells_.resize(lib_ap_index + 1);
  scene_cells_[lib_ap_index] = scene_cell;
}

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

float
LibertyCell::ocvArcDepth() const
{
  return ocv_arc_depth_;
}

void
LibertyCell::setOcvArcDepth(float depth)
{
  ocv_arc_depth_ = depth;
}

OcvDerate *
LibertyCell::ocvDerate() const
{
  if (ocv_derate_)
    return ocv_derate_;
  else
    return liberty_library_->defaultOcvDerate();
}

void
LibertyCell::setOcvDerate(OcvDerate *derate)
{
  ocv_derate_ = derate;
}

OcvDerate *
LibertyCell::makeOcvDerate(std::string_view name)
{
  std::string key(name);
  auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::string(name));
  return &it->second;
}

OcvDerate *
LibertyCell::findOcvDerate(std::string_view derate_name)
{
  return findStringValuePtr(ocv_derate_map_, derate_name);
}

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

LatchEnable::LatchEnable(LibertyPort *data,
                         LibertyPort *enable,
                         const RiseFall *enable_edge,
                         FuncExpr *enable_func,
                         LibertyPort *output,
                         TimingArcSet *d_to_q,
                         TimingArcSet *en_to_q,
                         TimingArcSet *setup_check) :
  data_(data),
  enable_(enable),
  enable_edge_(enable_edge),
  enable_func_(enable_func),
  output_(output),
  d_to_q_(d_to_q),
  en_to_q_(en_to_q),
  setup_check_(setup_check)
{
}

// Latch enable port/function for a latch D->Q timing arc set.
// This augments cell timing data by linking enables to D->Q arcs.
// Use timing arcs rather than sequentials (because they are optional).
void
LibertyCell::makeLatchEnables(Report *report,
                              Debug *debug)
{
  if (isSequential()
      || hasInferedRegTimingArcs()) {
    for (TimingArcSet *d_to_q : timing_arc_sets_) {
      if (d_to_q->role() == TimingRole::latchDtoQ()) {
        LibertyPort *d = d_to_q->from();
        LibertyPort *q = d_to_q->to();
        TimingArcSet *en_to_q = nullptr;
        TimingArcSet *en_to_q_when = nullptr;
        // Prefer en_to_q with matching when.
        for (TimingArcSet *arc_to_q : timingArcSetsTo(q)) {
          if (arc_to_q->role() == TimingRole::latchEnToQ()) {
            if (condMatch(arc_to_q, d_to_q))
              en_to_q_when = arc_to_q;
            else
              en_to_q = arc_to_q;
          }
        }
        if (en_to_q_when)
          en_to_q = en_to_q_when;
        if (en_to_q) {
          LibertyPort *en = en_to_q->from();
          const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
          if (en_rf) {
            TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, report);
            LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q,
                                                        en_to_q, setup_check, debug);
            FuncExpr *en_func = latch_enable->enableFunc();
            if (en_func) {
              TimingSense en_sense = en_func->portTimingSense(en);
              if (en_sense == TimingSense::positive_unate
                  && en_rf != RiseFall::rise())
                report->warn(1114, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function positive sense.",
                             library_->name(),
                             name(),
                             en->name(),
                             q->name(),
                             en_rf == RiseFall::rise()?"rising":"falling");
              else if (en_sense == TimingSense::negative_unate
                       && en_rf != RiseFall::fall())
                report->warn(1115, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function negative sense.",
                             library_->name(),
                             name(),
                             en->name(),
                             q->name(),
                             en_rf == RiseFall::rise()?"rising":"falling");
            }
          }
        }
        else
          report->warn(1121, "cell {}/{} no latch enable found for {} -> {}.",
                       library_->name(),
                       name(),
                       d->name(),
                       q->name());
      }
    }
  }
}

bool
LibertyCell::condMatch(const TimingArcSet *arc_set1,
                       const TimingArcSet *arc_set2)
{
  FuncExpr *cond1 = arc_set1->cond();
  FuncExpr *cond2 = arc_set2->cond();
  return (cond1 == nullptr && cond2 == nullptr)
    || FuncExpr::equiv(cond1, cond2);
}

TimingArcSet *
LibertyCell::findLatchSetup(const LibertyPort *d,
                            const LibertyPort *en,
                            const RiseFall *en_rf,
                            const LibertyPort *q,
                            const TimingArcSet *en_to_q,
                            Report *report)
{
  TimingArcSetSeq en_d_arcs = timingArcSets(en, d);
  // First search for setup checks with the correct clock edge.
  for (TimingArcSet *arc_set : en_d_arcs) {
    if (arc_set->role() == TimingRole::setup()) {
      for (TimingArc *arc : arc_set->arcs()) {
        const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
        if (from_rf == en_rf->opposite()
            && condMatch(arc_set, en_to_q))
          return arc_set;
      }
    }
  }
  // Then search for setup checks with the opposite clock edge.
  for (TimingArcSet *arc_set : en_d_arcs) {
    if (arc_set->role() == TimingRole::setup()) {
      for (TimingArc *arc : arc_set->arcs()) {
        const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
        if (from_rf == en_rf) {
          report->warn(1113, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with {} -> {} setup_{} check.",
                       library_->name(),
                       name(),
                       en->name(),
                       q->name(),
                       en_rf == RiseFall::rise() ? "rising" : "falling",
                       en->name(),
                       d->name(),
                       from_rf == RiseFall::rise() ? "rising" : "falling");
          return arc_set;
        }
      }
    }
  }
  return nullptr;
}

FuncExpr *
LibertyCell::findLatchEnableFunc(const LibertyPort *d,
                                 const LibertyPort *en,
                                 const RiseFall *en_rf) const
{
  for (const auto &seq : sequentials_) {
    if (seq.isLatch()
        && seq.data()
        && seq.data()->hasPort(d)
        && seq.clock()
        && seq.clock()->hasPort(en)) {
      FuncExpr *en_func = seq.clock();
      TimingSense en_sense = en_func->portTimingSense(en);
      if ((en_sense == TimingSense::positive_unate
           && en_rf == RiseFall::rise())
          || (en_sense == TimingSense::negative_unate
              && en_rf == RiseFall::fall()))
        return seq.clock();
    }
  }
  return nullptr;
}

LatchEnable *
LibertyCell::makeLatchEnable(LibertyPort *d,
                             LibertyPort *en,
                             const RiseFall *en_rf,
                             LibertyPort *q,
                             TimingArcSet *d_to_q,
                             TimingArcSet *en_to_q,
                             TimingArcSet *setup_check,
                             Debug *debug)
{
  FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf);
  latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, setup_check);
  size_t idx = latch_enables_.size() - 1;
  latch_d_to_q_map_[d_to_q] = idx;
  latch_check_map_[setup_check] = idx;
  d->setIsLatchData(true);
  q->setIsLatchOutput(true);
  debugPrint(debug, "liberty_latch", 1,
             "latch {} -> {} | {} {} -> {} | {} {} -> {} setup",
             d->name(),
             q->name(),
             en->name(),
             en_rf->shortName(),
             q->name(),
             en->name(),
             setup_check->arcs()[0]->fromEdge()->asRiseFall()->shortName(),
             q->name());
  return &latch_enables_.back();
}

void
LibertyCell::inferLatchRoles(Report *report,
                             Debug *debug)
{
  if (hasInferedRegTimingArcs()) {
    // Hunt down potential latch D/EN/Q triples.
    for (TimingArcSet *en_to_q : timingArcSets()) {
      // Locate potential d->q arcs from reg clk->q arcs.
      if (en_to_q->role() == TimingRole::regClkToQ()) {
        LibertyPort *en = en_to_q->from();
        LibertyPort *q = en_to_q->to();
        for (TimingArcSet *d_to_q : timingArcSetsTo(q)) {
          // Look for combinational d->q arcs.
          const TimingRole *d_to_q_role = d_to_q->role();
          if (((d_to_q_role == TimingRole::combinational()
                && d_to_q->arcCount() == 2
                && (d_to_q->sense() == TimingSense::positive_unate
                    || d_to_q->sense() == TimingSense::negative_unate))
               // Previously identified as D->Q arc.
               || d_to_q_role == TimingRole::latchDtoQ())
              && condMatch(en_to_q, d_to_q)) {
            LibertyPort *d = d_to_q->from();
            const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
            if (en_rf) {
              TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, en_to_q,
                                                         report);
              makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug);
              d_to_q->setRole(TimingRole::latchDtoQ());
              en_to_q->setRole(TimingRole::latchEnToQ());
            }
          }
        }
      }
    }
  }
}

void
LibertyCell::latchEnable(const TimingArcSet *d_to_q_set,
                         // Return values.
                         const LibertyPort *&enable_port,
                         const FuncExpr *&enable_func,
                         const RiseFall *&enable_rf) const
{
  auto it = latch_d_to_q_map_.find(d_to_q_set);
  if (it != latch_d_to_q_map_.end()) {
    const LatchEnable &latch_enable = latch_enables_[it->second];
    enable_port = latch_enable.enable();
    enable_func = latch_enable.enableFunc();
    enable_rf = latch_enable.enableEdge();
  }
  else {
    enable_port = nullptr;
    enable_func = nullptr;
    enable_rf = nullptr;
  }
}

const RiseFall *
LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
{
  auto it = latch_check_map_.find(check_set);
  if (it != latch_check_map_.end())
    return latch_enables_[it->second].enableEdge();
  else
    return nullptr;
}

void
LibertyCell::ensureVoltageWaveforms(const SceneSeq &scenes)
{
  if (!have_voltage_waveforms_) {
    LockGuard lock(waveform_lock_);
    // Recheck with lock.
    if (!have_voltage_waveforms_) {
      float vdd = 0.0;  // shutup gcc
      bool vdd_exists;
      liberty_library_->supplyVoltage("VDD", vdd, vdd_exists);
      if (!vdd_exists || vdd == 0.0)
        criticalError(1120, "library missing vdd");
      for (TimingArcSet *arc_set : timingArcSets()) {
        for (TimingArc *arc : arc_set->arcs()) {
          for (const Scene *scene : scenes) {
            for (const MinMax *min_max : MinMax::range()) {
              GateTableModel *model = arc->gateTableModel(scene, min_max);
              if (model) {
                OutputWaveforms *output_waveforms = model->outputWaveforms();
                if (output_waveforms)
                  output_waveforms->ensureVoltageWaveforms(vdd);
              }
            }
          }
        }
      }
      have_voltage_waveforms_ = true;
    }
  }
}

void
LibertyCell::setFootprint(std::string_view footprint)
{
  footprint_ = footprint;
}

void
LibertyCell::setUserFunctionClass(std::string_view user_function_class)
{
  user_function_class_ = user_function_class;
}

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

LibertyCellPortIterator::LibertyCellPortIterator(const LibertyCell *cell) :
  iter_(cell->ports_)
{
}

bool
LibertyCellPortIterator::hasNext()
{
  return iter_.hasNext();
}

LibertyPort *
LibertyCellPortIterator::next()
{
  return static_cast<LibertyPort*>(iter_.next());
}

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

LibertyCellPortBitIterator::LibertyCellPortBitIterator(const LibertyCell *cell):
  iter_(cell->portBitIterator())
{
}

LibertyCellPortBitIterator::~LibertyCellPortBitIterator()
{
  delete iter_;
}

bool
LibertyCellPortBitIterator::hasNext()
{
  return iter_->hasNext();
}

LibertyPort *
LibertyCellPortBitIterator::next()
{
  return static_cast<LibertyPort*>(iter_->next());
}

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

LibertyPort::LibertyPort(LibertyCell *cell,
                         std::string_view name,
                         bool is_bus,
                         BusDcl *bus_dcl,
                         int from_index,
                         int to_index,
                         bool is_bundle,
                         ConcretePortSeq *members) :
  ConcretePort(name, is_bus, from_index, to_index,
               is_bundle, members, cell),
  liberty_cell_(cell),
  bus_dcl_(bus_dcl)
{
  liberty_port_ = this;
}

LibertyPort::~LibertyPort()
{
  delete function_;
  delete tristate_enable_;
  delete scaled_ports_;
}

void
LibertyPort::setScanSignalType(ScanSignalType type)
{
  scan_signal_type_ = type;
}

static EnumNameMap<ScanSignalType> scan_signal_type_map =
  {{ScanSignalType::enable, "enable"},
   {ScanSignalType::enable_inverted, "enable_inverted"},
   {ScanSignalType::clock, "clock"},
   {ScanSignalType::clock_a, "clock_a"},
   {ScanSignalType::clock_b, "clock_b"},
   {ScanSignalType::input, "input"},
   {ScanSignalType::input_inverted, "input_inverted"},
   {ScanSignalType::output, "output"},
   {ScanSignalType::output_inverted, "output_inverted"},
   {ScanSignalType::none, "none"}};

bool
LibertyPort::isPwrGnd() const
{
  return pwr_gnd_type_ != PwrGndType::none;
}

void
LibertyPort::setPwrGndType(PwrGndType type)
{
  pwr_gnd_type_ = type;
}

void
LibertyPort::setVoltageName(std::string_view voltage_name)
{
  voltage_name_ = voltage_name;
}

static EnumNameMap<PwrGndType> pwr_gnd_type_map =
  {{PwrGndType::none, "node"},
   {PwrGndType::primary_power, "primary_power"},
   {PwrGndType::primary_ground, "primary_ground"},
   {PwrGndType::backup_power, "backup_power"},
   {PwrGndType::backup_ground, "backup_ground"},
   {PwrGndType::internal_power, "internal_power"},
   {PwrGndType::internal_ground, "internal_ground"},
   {PwrGndType::nwell, "nwell"},
   {PwrGndType::pwell, "pwell"},
   {PwrGndType::deepnwell, "deepnwell"},
   {PwrGndType::deeppwell, "deeppwell"}};

const std::string &
pwrGndTypeName(PwrGndType pg_type)
{
  return pwr_gnd_type_map.find(pg_type);
}

PwrGndType
findPwrGndType(std::string_view pg_name)
{
  return pwr_gnd_type_map.find(pg_name, PwrGndType::none);
}

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

const std::string &
scanSignalTypeName(ScanSignalType scan_type)
{
  return scan_signal_type_map.find(scan_type);
}

LibertyPort *
LibertyPort::findLibertyMember(int index) const
{
  return static_cast<LibertyPort*>(findMember(index));
}

LibertyPort *
LibertyPort::findLibertyBusBit(int index) const
{
  return static_cast<LibertyPort*>(findBusBit(index));
}

void
LibertyPort::setCapacitance(float cap)
{
  setCapacitance(RiseFall::rise(), MinMax::min(), cap);
  setCapacitance(RiseFall::fall(), MinMax::min(), cap);
  setCapacitance(RiseFall::rise(), MinMax::max(), cap);
  setCapacitance(RiseFall::fall(), MinMax::max(), cap);
}

void
LibertyPort::setCapacitance(const RiseFall *rf,
                            const MinMax *min_max,
                            float cap)
{
  capacitance_.setValue(rf, min_max, cap);
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      port_bit->setCapacitance(rf, min_max, cap);
    }
  }
}

float
LibertyPort::capacitance() const
{
  float cap;
  bool exists;
  capacitance_.maxValue(cap, exists);
  if (exists)
    return cap;
  else
    return 0.0;
}

float
LibertyPort::capacitance(const MinMax *min_max) const
{
  return capacitance_.value(min_max);
}

float
LibertyPort::capacitance(const RiseFall *rf,
                         const MinMax *min_max) const
{
  float cap;
  bool exists;
  capacitance_.value(rf, min_max, cap, exists);
  if (exists)
    return cap;
  else
    return 0.0;
}

void
LibertyPort::capacitance(const RiseFall *rf,
                         const MinMax *min_max,
                         // Return values.
                         float &cap,
                         bool &exists) const
{
  capacitance_.value(rf, min_max, cap, exists);
}

float
LibertyPort::capacitance(const RiseFall *rf,
                         const MinMax *min_max,
                         const OperatingConditions *op_cond,
                         const Pvt *pvt) const
{
  if (scaled_ports_) {
    LibertyPort *scaled_port = (*scaled_ports_)[op_cond];
    // Scaled capacitance is not derated because scale factors are wrt
    // nominal pvt.
    if (scaled_port)
      return scaled_port->capacitance(rf, min_max);
  }
  LibertyLibrary *lib = liberty_cell_->libertyLibrary();
  float cap = capacitance(rf, min_max);
  return cap * lib->scaleFactor(ScaleFactorType::pin_cap, liberty_cell_, pvt);
}

bool
LibertyPort::capacitanceIsOneValue() const
{
  return capacitance_.isOneValue();
}

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

float
LibertyPort::driveResistance() const
{
  return driveResistance(nullptr, MinMax::max());
}

// Min/max "drive" for all cell timing arcs.
float
LibertyPort::driveResistance(const RiseFall *rf,
                             const MinMax *min_max) const
{
  float max_drive = min_max->initValue();
  bool found_drive = false;
  for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) {
    if (!arc_set->role()->isTimingCheck()) {
      for (TimingArc *arc : arc_set->arcs()) {
        if (rf == nullptr
            || arc->toEdge()->asRiseFall() == rf) {
          float drive = arc->driveResistance();
          if (drive > 0.0) {
            if (min_max->compare(drive, max_drive))
              max_drive = drive;
            found_drive = true;
          }
        }
      }
    }
  }
  if (found_drive)
    return max_drive;
  else
    return 0.0;
}

ArcDelay
LibertyPort::intrinsicDelay(const StaState *sta) const
{
  return intrinsicDelay(nullptr, MinMax::max(), sta);
}

ArcDelay
LibertyPort::intrinsicDelay(const RiseFall *rf,
                            const MinMax *min_max,
                            const StaState *sta) const
{
  ArcDelay max_delay = min_max->initValue();
  bool found_delay = false;
  for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) {
    if (!arc_set->role()->isTimingCheck()) {
      for (TimingArc *arc : arc_set->arcs()) {
        if (rf == nullptr
            || arc->toEdge()->asRiseFall() == rf) {
          ArcDelay delay = arc->intrinsicDelay();
          if (delayGreater(delay, 0.0, sta)) {
            if (delayGreater(delay, max_delay, min_max, sta))
              max_delay = delay;
            found_delay = true;
          }
        }
      }
    }
  }
  if (found_delay)
    return max_delay;
  else
    return 0.0;
}

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

void
LibertyPort::setFunction(FuncExpr *func)
{
  function_ = func;
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    int bit_offset = 0;
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      FuncExpr *sub_expr = func ? func->bitSubExpr(bit_offset) : nullptr;
      port_bit->setFunction(sub_expr);
      bit_offset++;
    }
  }
}

void
LibertyPort::setTristateEnable(FuncExpr *enable)
{
  tristate_enable_ = enable;
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      FuncExpr *sub_expr = enable
        ? enable->bitSubExpr(port_bit->busBitIndex())
        : nullptr;
      port_bit->setTristateEnable(sub_expr);
    }
  }
}

void
LibertyPort::slewLimit(const MinMax *min_max,
                       // Return values.
                       float &limit,
                       bool &exists) const
{
  slew_limit_.value(min_max, limit, exists);
}

void
LibertyPort::setSlewLimit(float slew,
                          const MinMax *min_max)
{
  slew_limit_.setValue(min_max, slew);
  setMemberMinMaxFloat(slew, min_max, &LibertyPort::setSlewLimit);
}

void
LibertyPort::capacitanceLimit(const MinMax *min_max,
                              // Return values.
                              float &limit,
                              bool &exists) const
{
  cap_limit_.value(min_max, limit, exists);
}

void
LibertyPort::setCapacitanceLimit(float cap,
                                 const MinMax *min_max)
{
  cap_limit_.setValue(min_max, cap);
  setMemberMinMaxFloat(cap, min_max, &LibertyPort::setCapacitanceLimit);
}

void
LibertyPort::fanoutLoad(// Return values.
                        float &fanout_load,
                        bool &exists) const
{
  fanout_load = fanout_load_;
  exists = fanout_load_exists_;
}

void
LibertyPort::setFanoutLoad(float fanout_load)
{
  fanout_load_ = fanout_load;
  fanout_load_exists_ = true;
  setMemberFloat(fanout_load, &LibertyPort::setFanoutLoad);
}

void
LibertyPort::fanoutLimit(const MinMax *min_max,
                         // Return values.
                         float &limit,
                         bool &exists) const
{
  fanout_limit_.value(min_max, limit, exists);
}

void
LibertyPort::setFanoutLimit(float fanout,
                            const MinMax *min_max)
{
  fanout_limit_.setValue(min_max, fanout);
}

void
LibertyPort::minPeriod(const OperatingConditions *op_cond,
                       const Pvt *pvt,
                       float &min_period,
                       bool &exists) const
{
  if (scaled_ports_) {
    LibertyPort *scaled_port = (*scaled_ports_)[op_cond];
    if (scaled_port) {
      scaled_port->minPeriod(min_period, exists);
      return;
    }
  }
  LibertyLibrary *lib = liberty_cell_->libertyLibrary();
  min_period = min_period_ * lib->scaleFactor(ScaleFactorType::min_period,
                                              liberty_cell_, pvt);
  exists = min_period_exists_;
}

void
LibertyPort::minPeriod(float &min_period,
                       bool &exists) const
{
  min_period = min_period_;
  exists = min_period_exists_;
}

void
LibertyPort::setMinPeriod(float min_period)
{
  min_period_ = min_period;
  min_period_exists_ = true;
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      port_bit->setMinPeriod(min_period);
    }
  }
}

void
LibertyPort::minPulseWidth(const RiseFall *hi_low,
                           float &min_width,
                           bool &exists) const
{
  int hi_low_index = hi_low->index();
  min_width = min_pulse_width_[hi_low_index];
  exists = min_pulse_width_exists_ & (1 << hi_low_index);
}

void
LibertyPort::setMinPulseWidth(const RiseFall *hi_low,
                              float min_width)
{
  int hi_low_index = hi_low->index();
  min_pulse_width_[hi_low_index] = min_width;
  min_pulse_width_exists_ |= (1 << hi_low_index);
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      port_bit->setMinPulseWidth(hi_low, min_width);
    }
  }
}

bool
LibertyPort::equiv(const LibertyPort *port1,
                   const LibertyPort *port2)
{
  return (port1 == nullptr && port2 == nullptr)
    || (port1 != nullptr && port2 != nullptr
        && port1->name() == port2->name()
        && port1->direction() == port2->direction()
        && port1->pwr_gnd_type_ == port2->pwr_gnd_type_);
}

// Note port1 and port2 may be from different cells (timingArcSetLess).
bool
LibertyPort::less(const LibertyPort *port1,
                  const LibertyPort *port2)
{
  if (port1 && port2)
    return port1->name() < port2->name();
  else
    return port1 == nullptr && port2 != nullptr;
}

void
LibertyPort::addScaledPort(OperatingConditions *op_cond,
                           LibertyPort *scaled_port)
{
  if (scaled_ports_ == nullptr)
    scaled_ports_ = new ScaledPortMap;
  (*scaled_ports_)[op_cond] = scaled_port;
}

bool
LibertyPort::isClock() const
{
  return is_clk_;
}

void
LibertyPort::setIsClock(bool is_clk)
{
  is_clk_ = is_clk;
  setMemberFlag(is_clk, &LibertyPort::setIsClock);
}

void
LibertyPort::setIsRegClk(bool is_clk)
{
  is_reg_clk_ = is_clk;
  setMemberFlag(is_clk, &LibertyPort::setIsRegClk);
}

void
LibertyPort::setIsRegOutput(bool is_reg_out)
{
  is_reg_output_ = is_reg_out;
  setMemberFlag(is_reg_out, &LibertyPort::setIsRegOutput);
}

void
LibertyPort::setIsLatchData(bool is_latch_data)
{
  is_latch_data_ = is_latch_data;
  setMemberFlag(is_latch_data, &LibertyPort::setIsLatchData);
}

void
LibertyPort::setIsLatchOutput(bool is_latch_output)
{
  is_latch_output_ = is_latch_output;
  setMemberFlag(is_latch_output, &LibertyPort::setIsLatchOutput);
}

void
LibertyPort::setIsCheckClk(bool is_clk)
{
  is_check_clk_ = is_clk;
}

void
LibertyPort::setIsClockGateClock(bool is_clk_gate_clk)
{
  is_clk_gate_clk_ = is_clk_gate_clk;
}

void
LibertyPort::setIsClockGateEnable(bool is_clk_gate_enable)
{
  is_clk_gate_enable_ = is_clk_gate_enable;
}

void
LibertyPort::setIsClockGateOut(bool is_clk_gate_out)
{
  is_clk_gate_out_ = is_clk_gate_out;
}

void
LibertyPort::setIsPllFeedback(bool is_pll_feedback)
{
  is_pll_feedback_ = is_pll_feedback;
}

void
LibertyPort::setIsolationCellData(bool isolation_cell_data)
{
  isolation_cell_data_ = isolation_cell_data;
  setMemberFlag(isolation_cell_data, &LibertyPort::setIsolationCellData);
}

void
LibertyPort::setIsolationCellEnable(bool isolation_cell_enable)
{
  isolation_cell_enable_ = isolation_cell_enable;
  setMemberFlag(isolation_cell_enable, &LibertyPort::setIsolationCellEnable);
}

void
LibertyPort::setLevelShifterData(bool level_shifter_data)
{
  level_shifter_data_ = level_shifter_data;
  setMemberFlag(level_shifter_data, &LibertyPort::setLevelShifterData);
}

void
LibertyPort::setIsSwitch(bool is_switch)
{
  is_switch_ = is_switch;
  setMemberFlag(is_switch, &LibertyPort::setIsSwitch);
}

void
LibertyPort::setPulseClk(const RiseFall *trigger,
                         const RiseFall *sense)
{
  pulse_clk_trigger_ = trigger;
  pulse_clk_sense_ = sense;
}

void
LibertyPort::setIsPad(bool is_pad)
{
  is_pad_ = is_pad;
}

LibertyPort *
LibertyPort::scenePort(const Scene *scene,
                       const MinMax *min_max)
{
  return scenePort(scene->libertyIndex(min_max));
}

const LibertyPort *
LibertyPort::scenePort(const Scene *scene,
                       const MinMax *min_max) const
{
  return scenePort(scene->libertyIndex(min_max));
}

LibertyPort *
LibertyPort::scenePort(size_t lib_ap_index)
{
  if (scene_ports_.empty())
    return this;
  else if (lib_ap_index < scene_ports_.size())
    return scene_ports_[lib_ap_index];
  else
    return nullptr;
}

const LibertyPort *
LibertyPort::scenePort(size_t lib_ap_index) const
{
  if (scene_ports_.empty())
    return this;
  else if (lib_ap_index < scene_ports_.size())
    return scene_ports_[lib_ap_index];
  else
    return nullptr;
}

void
LibertyPort::setScenePort(LibertyPort *scene_port,
                          size_t lib_ap_index)
{
  if (lib_ap_index >= scene_ports_.size())
    scene_ports_.resize(lib_ap_index + 1);
  scene_ports_[lib_ap_index] = scene_port;
}

void
LibertyPort::setRelatedGroundPort(LibertyPort *related_ground_port)
{
  related_ground_port_ = related_ground_port;
}

void
LibertyPort::setRelatedPowerPort(LibertyPort *related_power_port)
{
  related_power_port_ = related_power_port;
}

void
LibertyPort::setReceiverModel(ReceiverModelPtr receiver_model)
{
  receiver_model_ = std::move(receiver_model);
}

std::string
portLibertyToSta(std::string_view port_name)
{
  constexpr char bus_brkt_left = '[';
  constexpr char bus_brkt_right = ']';
  std::string sta_name;
  for (char ch : port_name) {
    if (ch == bus_brkt_left
        || ch == bus_brkt_right)
      sta_name += '\\';
    sta_name += ch;
  }
  return sta_name;
}

DriverWaveform *
LibertyPort::driverWaveform(const RiseFall *rf) const
{
  return driver_waveform_[rf->index()];
}

void
LibertyPort::setDriverWaveform(DriverWaveform *driver_waveform,
                               const RiseFall *rf)
{
  driver_waveform_[rf->index()] = driver_waveform;
}

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

float
LibertyPort::clkTreeDelay(float in_slew,
                          const RiseFall *rf,
                          const MinMax *min_max) const
{
  return clkTreeDelay(in_slew, rf, rf, min_max);
}

float
LibertyPort::clkTreeDelay(float in_slew,
                          const RiseFall *from_rf,
                          const RiseFall *to_rf,
                          const MinMax *min_max) const
{
  for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) {
    if ((arc_set->role() == TimingRole::clockTreePathMin()
         && min_max == MinMax::min())
        || (arc_set->role() == TimingRole::clockTreePathMax()
            && min_max == MinMax::max())) {
      const TimingArc *arc = arc_set->arcTo(to_rf);
      if (arc->fromEdge()->asRiseFall() == from_rf) {
        const GateTableModel *gate_model = dynamic_cast<GateTableModel*>(arc->model());
        if (gate_model)
          return gate_model->delayModel()->findValue(in_slew, 0.0, 0.0);
      }
    }
  }
  return 0.0;
}

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

void
LibertyPort::setMemberFlag(bool value,
                           const std::function<void(LibertyPort*,
                                                    bool)> &setter)
{
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      setter(port_bit, value);
    }
  }
}

void
LibertyPort::setMemberFloat(float value,
                            const std::function<void(LibertyPort*,
                                                     float)> &setter)
{
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      setter(port_bit, value);
    }
  }
}

void
LibertyPort::setMemberMinMaxFloat(float value,
                                  const MinMax *min_max,
                                  const std::function<void(LibertyPort*,
                                                           float,
                                                           const MinMax *)> &setter)
{
  if (hasMembers()) {
    LibertyPortMemberIterator member_iter(this);
    while (member_iter.hasNext()) {
      LibertyPort *port_bit = member_iter.next();
      setter(port_bit, value, min_max);
    }
  }
}

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

LibertyPortSeq
sortByName(const LibertyPortSet *set)
{
  LibertyPortSeq ports;
  for (LibertyPort *port : *set)
    ports.push_back(port);
  sort(ports, LibertyPortNameLess());
  return ports;
}

bool
LibertyPortLess::operator()(const LibertyPort *port1,
                            const LibertyPort *port2) const
{
  return LibertyPort::less(port1, port2);
}

bool
LibertyPortNameLess::operator()(const LibertyPort *port1,
                                const LibertyPort *port2) const
{
  return port1->name() < port2->name();
}

bool
LibertyPortPairLess::operator()(const LibertyPortPair &pair1,
                                const LibertyPortPair &pair2) const
{
  ObjectId id11 = pair1.first ? pair1.first->id() : 0;
  ObjectId id12 = pair1.second ? pair1.second->id() : 0;
  ObjectId id21 = pair2.first ? pair2.first->id() : 0;
  ObjectId id22 = pair2.second ? pair2.second->id() : 0;
  return id11 < id21
    || (id11 == id21
        && id12 < id22);
}

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

LibertyPortMemberIterator::LibertyPortMemberIterator(const LibertyPort *port) :
  iter_(port->memberIterator())
{
}

LibertyPortMemberIterator::~LibertyPortMemberIterator()
{
  delete iter_;
}

bool
LibertyPortMemberIterator::hasNext()
{
  return iter_->hasNext();
}

LibertyPort *
LibertyPortMemberIterator::next()
{
  return static_cast<LibertyPort*>(iter_->next());
}

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

BusDcl::BusDcl(std::string_view name,
               int from,
               int to) :
  name_(name),
  from_(from),
  to_(to)
{
}

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

ModeDef::ModeDef(std::string_view name) :
  name_(name)
{
}

ModeValueDef *
ModeDef::defineValue(std::string_view value)
{
  std::string key(value);
  auto [it, inserted] = values_.try_emplace(std::move(key), std::string(value));
  return &it->second;
}

const ModeValueDef *
ModeDef::findValueDef(std::string_view value) const
{
  return findStringValuePtr(values_, value);
}

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

ModeValueDef::ModeValueDef(std::string_view value) :
  value_(value)
{
}

ModeValueDef::ModeValueDef(ModeValueDef &&other) noexcept :
  value_(std::move(other.value_)),
  cond_(other.cond_),
  sdf_cond_(std::move(other.sdf_cond_))
{
  other.cond_ = nullptr;
}

ModeValueDef::~ModeValueDef()
{
  delete cond_;
}

void
ModeValueDef::setCond(FuncExpr *cond)
{
  cond_ = cond;
}

void
ModeValueDef::setSdfCond(std::string sdf_cond)
{
  sdf_cond_ = std::move(sdf_cond);
}

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

TableTemplate::TableTemplate(std::string_view name) :
  name_(name)
{
}

TableTemplate::TableTemplate(std::string_view name,
                             TableTemplateType type) :
  name_(name),
  type_(type)
{
}

TableTemplate::TableTemplate(std::string_view name,
                             TableAxisPtr axis1,
                             TableAxisPtr axis2,
                             TableAxisPtr axis3) :
  name_(name),
  axis1_(std::move(axis1)),
  axis2_(std::move(axis2)),
  axis3_(std::move(axis3))
{
}

void
TableTemplate::setName(std::string_view name)
{
  name_ = name;
}

void
TableTemplate::setAxis1(TableAxisPtr axis)
{
  axis1_ = std::move(axis);
}

void
TableTemplate::setAxis2(TableAxisPtr axis)
{
  axis2_ = std::move(axis);
}

void
TableTemplate::setAxis3(TableAxisPtr axis)
{
  axis3_ = std::move(axis);
}

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

Pvt::Pvt(float process,
         float voltage,
         float temperature) :
  process_(process),
  voltage_(voltage),
  temperature_(temperature)
{
}

void
Pvt::setProcess(float process)
{
  process_ = process;
}

void
Pvt::setVoltage(float voltage)
{
  voltage_ = voltage;
}

void
Pvt::setTemperature(float temp)
{
  temperature_ = temp;
}

OperatingConditions::OperatingConditions(std::string_view name) :
  Pvt(0.0, 0.0, 0.0),
  name_(name)
{
}

void
OperatingConditions::setWireloadTree(WireloadTree tree)
{
  wire_load_tree_ = tree;
}

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

static EnumNameMap<ScaleFactorType> scale_factor_type_map =
  {{ScaleFactorType::pin_cap, "pin_cap"},
   {ScaleFactorType::wire_cap, "wire_cap"},
   {ScaleFactorType::wire_res, "wire_res"},
   {ScaleFactorType::min_period, "min_period"},
   {ScaleFactorType::cell, "cell"},
   {ScaleFactorType::hold, "hold"},
   {ScaleFactorType::setup, "setup"},
   {ScaleFactorType::recovery, "recovery"},
   {ScaleFactorType::removal, "removal"},
   {ScaleFactorType::nochange, "nochange"},
   {ScaleFactorType::skew, "skew"},
   {ScaleFactorType::leakage_power, "leakage_power"},
   {ScaleFactorType::internal_power, "internal_power"},
   {ScaleFactorType::transition, "transition"},
   {ScaleFactorType::min_pulse_width, "min_pulse_width"},
   {ScaleFactorType::unknown, "unknown"}
  };

const std::string &
scaleFactorTypeName(ScaleFactorType type)
{
  return scale_factor_type_map.find(type);
}

ScaleFactorType
findScaleFactorType(std::string_view name)
{
  return scale_factor_type_map.find(name, ScaleFactorType::unknown);
}

bool
scaleFactorTypeRiseFallSuffix(ScaleFactorType type)
{
  return type == ScaleFactorType::cell
    || type == ScaleFactorType::hold
    || type == ScaleFactorType::setup
    || type == ScaleFactorType::recovery
    || type == ScaleFactorType::removal
    || type == ScaleFactorType::nochange
    || type == ScaleFactorType::skew
    || type == ScaleFactorType::leakage_power
    || type == ScaleFactorType::internal_power;
}

bool
scaleFactorTypeRiseFallPrefix(ScaleFactorType type)
{
  return type == ScaleFactorType::transition;
}

bool
scaleFactorTypeLowHighSuffix(ScaleFactorType type)
{
  return type == ScaleFactorType::min_pulse_width;
}

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

EnumNameMap<ScaleFactorPvt> scale_factor_pvt_names =
  {{ScaleFactorPvt::process, "process"},
   {ScaleFactorPvt::volt, "volt"},
   {ScaleFactorPvt::temp, "temp"},
   {ScaleFactorPvt::unknown, "unknown"}
  };

ScaleFactorPvt
findScaleFactorPvt(std::string_view name)
{
  return scale_factor_pvt_names.find(name, ScaleFactorPvt::unknown);
}

const std::string &
scaleFactorPvtName(ScaleFactorPvt pvt)
{
  return scale_factor_pvt_names.find(pvt);
}

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

ScaleFactors::ScaleFactors(std::string_view name) :
  name_(name)
{
  for (auto &type_factors : scales_) {
    for (auto pvt_factors : type_factors) {
      for (size_t rf_index : RiseFall::rangeIndex())
        pvt_factors[rf_index] = 0.0;
    }
  }
}

void
ScaleFactors::setScale(ScaleFactorType type,
                       ScaleFactorPvt pvt,
                       const RiseFall *rf,
                       float scale)
{
  scales_[static_cast<size_t>(type)][static_cast<size_t>(pvt)][rf->index()] = scale;
}

void
ScaleFactors::setScale(ScaleFactorType type,
                       ScaleFactorPvt pvt,
                       float scale)
{
  scales_[static_cast<size_t>(type)][static_cast<size_t>(pvt)][0] = scale;
}

float
ScaleFactors::scale(ScaleFactorType type,
                    ScaleFactorPvt pvt,
                    const RiseFall *rf)
{
  return scales_[static_cast<size_t>(type)][static_cast<size_t>(pvt)][rf->index()];
}

float
ScaleFactors::scale(ScaleFactorType type,
                    ScaleFactorPvt pvt,
                    int rf_index)
{
  return scales_[static_cast<size_t>(type)][static_cast<size_t>(pvt)][rf_index];
}

float
ScaleFactors::scale(ScaleFactorType type,
                    ScaleFactorPvt pvt)
{
  return scales_[static_cast<size_t>(type)][static_cast<size_t>(pvt)][0];
}

void
ScaleFactors::report(Report *report)
{
  std::string line = "          ";
  for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
    ScaleFactorPvt pvt = static_cast<ScaleFactorPvt>(pvt_index);
    line += sta::format("{:>10}", scaleFactorPvtName(pvt));
  }
  report->reportLine(line);

  for (int type_index = 0; type_index < scale_factor_type_count; type_index++) {
    ScaleFactorType type = static_cast<ScaleFactorType>(type_index);
    std::string line = sta::format("{:>10}", scaleFactorTypeName(type));
    for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
      if (scaleFactorTypeRiseFallSuffix(type)
          || scaleFactorTypeRiseFallPrefix(type)
          || scaleFactorTypeLowHighSuffix(type)) {
        line += sta::format(" {:.3f},{:.3f}",
                     scales_[type_index][pvt_index][RiseFall::riseIndex()],
                     scales_[type_index][pvt_index][RiseFall::fallIndex()]);
      }
      else {
        line += sta::format(" {:.3f}",
                     scales_[type_index][pvt_index][0]);
      }
    }
    report->reportLine(line);
  }
}

TestCell::TestCell(LibertyLibrary *library,
                   std::string_view name,
                   std::string_view filename) :
  LibertyCell(library, name, filename)
{
}

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

OcvDerate::OcvDerate(std::string_view name) :
  name_(name)
{
}

const Table *
OcvDerate::derateTable(const RiseFall *rf,
                       const EarlyLate *early_late,
                       PathType path_type)
{
  return derate_[rf->index()][early_late->index()][static_cast<size_t>(path_type)].get();
}

void
OcvDerate::setDerateTable(const RiseFall *rf,
                          const EarlyLate *early_late,
                          const PathType path_type,
                          TablePtr derate)
{
  derate_[rf->index()][early_late->index()][static_cast<size_t>(path_type)]
    = std::move(derate);
}

} // namespace sta
