// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, 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 "Sta.hh"

#include "Machine.hh"
#include "DispatchQueue.hh"
#include "ReportTcl.hh"
#include "Debug.hh"
#include "Stats.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "TimingArc.hh"
#include "FuncExpr.hh"
#include "EquivCells.hh"
#include "Liberty.hh"
#include "liberty/LibertyReader.hh"
#include "LibertyWriter.hh"
#include "SdcNetwork.hh"
#include "MakeConcreteNetwork.hh"
#include "PortDirection.hh"
#include "VerilogReader.hh"
#include "Graph.hh"
#include "GraphCmp.hh"
#include "Sdc.hh"
#include "Variables.hh"
#include "WriteSdc.hh"
#include "ExceptionPath.hh"
#include "MakeConcreteParasitics.hh"
#include "Parasitics.hh"
#include "parasitics/SpefReader.hh"
#include "parasitics/ReportParasiticAnnotation.hh"
#include "DelayCalc.hh"
#include "ArcDelayCalc.hh"
#include "GraphDelayCalc.hh"
#include "sdf/SdfWriter.hh"
#include "Levelize.hh"
#include "Sim.hh"
#include "ClkInfo.hh"
#include "TagGroup.hh"
#include "PathAnalysisPt.hh"
#include "Corner.hh"
#include "Search.hh"
#include "Latches.hh"
#include "PathGroup.hh"
#include "CheckTiming.hh"
#include "CheckSlewLimits.hh"
#include "CheckFanoutLimits.hh"
#include "CheckCapacitanceLimits.hh"
#include "CheckMinPulseWidths.hh"
#include "CheckMinPeriods.hh"
#include "CheckMaxSkews.hh"
#include "ClkSkew.hh"
#include "ClkLatency.hh"
#include "FindRegister.hh"
#include "ReportPath.hh"
#include "VisitPathGroupVertices.hh"
#include "Genclks.hh"
#include "ClkNetwork.hh"
#include "power/Power.hh"
#include "VisitPathEnds.hh"
#include "PathExpanded.hh"
#include "MakeTimingModel.hh"
#include "spice/WritePathSpice.hh"

namespace sta {

using std::min;
using std::max;

static const ClockEdge *clk_edge_wildcard = reinterpret_cast<ClockEdge*>(1);

static bool
libertyPortCapsEqual(const LibertyPort *port1,
		     const LibertyPort *port2);
static bool
hasDisabledArcs(Edge *edge,
		Graph *graph);
static InstanceSet
pinInstances(PinSet &pins,
	     const Network *network);

////////////////////////////////////////////////////////////////
//
// Observers are used to propagate updates from a component
// to other components.
//
////////////////////////////////////////////////////////////////

// When an incremental change is made the delay calculation
// changes downstream.  This invalidates the required times
// for all vertices upstream of the changes.
class StaDelayCalcObserver : public DelayCalcObserver
{
public:
  explicit StaDelayCalcObserver(Search *search);
  virtual void delayChangedFrom(Vertex *vertex);
  virtual void delayChangedTo(Vertex *vertex);
  virtual void checkDelayChangedTo(Vertex *vertex);

private:
  Search *search_;
};

StaDelayCalcObserver::StaDelayCalcObserver(Search *search) :
  DelayCalcObserver(),
  search_(search)
{
}

void
StaDelayCalcObserver::delayChangedFrom(Vertex *vertex)
{
  search_->requiredInvalid(vertex);
}

void
StaDelayCalcObserver::delayChangedTo(Vertex *vertex)
{
  search_->arrivalInvalid(vertex);
}

void
StaDelayCalcObserver::checkDelayChangedTo(Vertex *vertex)
{
  search_->requiredInvalid(vertex);
}

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

class StaSimObserver : public SimObserver
{
public:
  StaSimObserver(GraphDelayCalc *graph_delay_calc,
		 Levelize *levelize,
		 Search *search);
  virtual void valueChangeAfter(Vertex *vertex);
  virtual void faninEdgesChangeAfter(Vertex *vertex);
  virtual void fanoutEdgesChangeAfter(Vertex *vertex);

private:
  GraphDelayCalc *graph_delay_calc_;
  Levelize *levelize_;
  Search *search_;
};

StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc,
			       Levelize *levelize,
			       Search *search) :
  SimObserver(),
  graph_delay_calc_(graph_delay_calc),
  levelize_(levelize),
  search_(search)
{
}

// When pins with constant values are incrementally connected to a net
// the downstream delays and arrivals will not be updated (removed)
// because the search predicate does not search through constants.
// This observer makes sure the delays and arrivals are invalidated.
void
StaSimObserver::valueChangeAfter(Vertex *vertex)
{
  graph_delay_calc_->delayInvalid(vertex);
  search_->arrivalInvalid(vertex);
  search_->requiredInvalid(vertex);
  search_->endpointInvalid(vertex);
  levelize_->invalidFrom(vertex);
}

void
StaSimObserver::faninEdgesChangeAfter(Vertex *vertex)
{
  graph_delay_calc_->delayInvalid(vertex);
  search_->arrivalInvalid(vertex);
  search_->endpointInvalid(vertex);
}

void
StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex)
{
  search_->requiredInvalid(vertex);
  search_->endpointInvalid(vertex);
}

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

class StaLevelizeObserver : public LevelizeObserver
{
public:
  StaLevelizeObserver(Search *search);
  virtual void levelChangedBefore(Vertex *vertex);

private:
  Search *search_;
};

StaLevelizeObserver::StaLevelizeObserver(Search *search) :
  search_(search)
{
}

void
StaLevelizeObserver::levelChangedBefore(Vertex *vertex)
{
  search_->levelChangedBefore(vertex);
}

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

void
initSta()
{
  initElapsedTime();
  PortDirection::init();
  initLiberty();
  initDelayConstants();
  registerDelayCalcs();
  initPathSenseThru();
}

void
deleteAllMemory()
{
  Sta *sta = Sta::sta();
  if (sta) {
    delete sta;
    Sta::setSta(nullptr);
  }
  deleteDelayCalcs();
  PortDirection::destroy();
  deleteLiberty();
}

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

// Singleton used by TCL commands.
Sta *Sta::sta_;

Sta::Sta() :
  StaState(),
  current_instance_(nullptr),
  cmd_corner_(nullptr),
  verilog_reader_(nullptr),
  check_timing_(nullptr),
  check_slew_limits_(nullptr),
  check_fanout_limits_(nullptr),
  check_capacitance_limits_(nullptr),
  check_min_pulse_widths_(nullptr),
  check_min_periods_(nullptr),
  check_max_skews_(nullptr),
  clk_skews_(nullptr),
  report_path_(nullptr),
  power_(nullptr),
  update_genclks_(false),
  equiv_cells_(nullptr),
  graph_sdc_annotated_(false),
  // Default to same parasitics for all corners.
  parasitics_per_corner_(false)
{
}

void
Sta::makeComponents()
{
  makeVariables();
  makeReport();
  makeDebug();
  makeUnits();
  makeNetwork();
  makeSdc();
  makeLevelize();
  makeParasitics();
  makeCorners();
  makeArcDelayCalc();
  makeGraphDelayCalc();
  makeSim();
  makeSearch();
  makeLatches();
  makeClkNetwork();
  makeSdcNetwork();
  makeReportPath();
  makePower();
  setCmdNamespace1(CmdNamespace::sdc);
  setThreadCount1(defaultThreadCount());
  updateComponentsState();

  makeObservers();
  // This must follow updateComponentsState.
  makeParasiticAnalysisPts();
}

void
Sta::makeObservers()
{
  graph_delay_calc_->setObserver(new StaDelayCalcObserver(search_));
  sim_->setObserver(new StaSimObserver(graph_delay_calc_, levelize_, search_));
  levelize_->setObserver(new StaLevelizeObserver(search_));
}

int
Sta::defaultThreadCount() const
{
  return 1;
}

void
Sta::setThreadCount(int thread_count)
{
  setThreadCount1(thread_count);
  updateComponentsState();
}

void
Sta::setThreadCount1(int thread_count)
{
  thread_count_ = thread_count;
  if (dispatch_queue_)
    dispatch_queue_->setThreadCount(thread_count);
  else if (thread_count > 1)
    dispatch_queue_ = new DispatchQueue(thread_count);
}

void
Sta::updateComponentsState()
{
  network_->copyState(this);
  cmd_network_->copyState(this);
  sdc_network_->copyState(this);
  if (graph_)
    graph_->copyState(this);
  sdc_->copyState(this);
  corners_->copyState(this);
  levelize_->copyState(this);
  parasitics_->copyState(this);
  arc_delay_calc_->copyState(this);
  sim_->copyState(this);
  search_->copyState(this);
  latches_->copyState(this);
  graph_delay_calc_->copyState(this);
  report_path_->copyState(this);
  if (check_timing_)
    check_timing_->copyState(this);
  clk_network_->copyState(this);
  if (clk_skews_)
    clk_skews_->copyState(this);
  if (power_)
    power_->copyState(this);
}

void
Sta::makeReport()
{
  report_ = new ReportTcl();
}

void
Sta::makeDebug()
{
  debug_ = new Debug(report_);
}

void
Sta::makeUnits()
{
  units_ = new Units();
}

void
Sta::makeNetwork()
{
  network_ = makeConcreteNetwork();
}

void
Sta::makeSdc()
{
  sdc_ = new Sdc(this);
}

void
Sta::makeLevelize()
{
  levelize_ = new Levelize(this);
}

void
Sta::makeParasitics()
{
  parasitics_ = makeConcreteParasitics(this);
}

void
Sta::makeArcDelayCalc()
{
  arc_delay_calc_ = makeDelayCalc("dmp_ceff_elmore", this);
}

void
Sta::makeGraphDelayCalc()
{
  graph_delay_calc_ = new GraphDelayCalc(this);
}

void
Sta::makeSim()
{
  sim_ = new Sim(this);
}

void
Sta::makeSearch()
{
  search_ = new Search(this);
}

void
Sta::makeLatches()
{
  latches_ = new Latches(this);
}

void
Sta::makeSdcNetwork()
{
  sdc_network_ = sta::makeSdcNetwork(network_);
}

void
Sta::makeCheckTiming()
{
  check_timing_ = new CheckTiming(this);
}

void
Sta::makeCheckSlewLimits()
{
  check_slew_limits_ = new CheckSlewLimits(this);
}

void
Sta::makeCheckFanoutLimits()
{
  check_fanout_limits_ = new CheckFanoutLimits(this);
}

void
Sta::makeCheckCapacitanceLimits()
{
  check_capacitance_limits_ = new CheckCapacitanceLimits(this);
}

void
Sta::makeCheckMinPulseWidths()
{
  check_min_pulse_widths_ = new CheckMinPulseWidths(this);
}

void
Sta::makeCheckMinPeriods()
{
  check_min_periods_ = new CheckMinPeriods(this);
}

void
Sta::makeCheckMaxSkews()
{
  check_max_skews_ = new CheckMaxSkews(this);
}

void
Sta::makeReportPath()
{
  report_path_ = new ReportPath(this);
}

void
Sta::makeClkNetwork()
{
  clk_network_ = new ClkNetwork(this);
}

void
Sta::makePower()
{
  power_ = new Power(this);
}

void
Sta::makeVariables()
{
  variables_ = new Variables();
}

void
Sta::setSta(Sta *sta)
{
  sta_ = sta;
}

Sta *
Sta::sta()
{
  return sta_;
}

Sta::~Sta()
{
  delete variables_;
  // Verilog modules refer to the network in the sta so it has
  // to deleted before the network.
  delete verilog_reader_;
  // Delete "top down" to minimize chance of referencing deleted memory.
  delete check_slew_limits_;
  delete check_fanout_limits_;
  delete check_capacitance_limits_;
  delete check_min_pulse_widths_;
  delete check_min_periods_;
  delete check_max_skews_;
  delete clk_skews_;
  delete check_timing_;
  delete report_path_;
  // Sdc references search filter, so delete search first.
  delete search_;
  delete latches_;
  delete parasitics_;
  delete arc_delay_calc_;
  delete graph_delay_calc_;
  delete sim_;
  delete levelize_;
  delete sdc_;
  delete corners_;
  delete graph_;
  delete sdc_network_;
  delete network_;
  delete debug_;
  delete units_;
  delete report_;
  delete clk_network_;
  delete power_;
  delete equiv_cells_;
  delete dispatch_queue_;
}

void
Sta::clear()
{
  clkPinsInvalid();
  // Constraints reference search filter, so clear search first.
  search_->clear();
  sdc_->clear();
  graph_sdc_annotated_ = false;
  // corners are NOT cleared because they are used to index liberty files.
  levelize_->clear();
  if (parasitics_)
    parasitics_->clear();
  graph_delay_calc_->clear();
  sim_->clear();
  if (check_min_pulse_widths_)
    check_min_pulse_widths_->clear();
  if (check_min_periods_)
    check_min_periods_->clear();
  delete graph_;
  graph_ = nullptr;
  current_instance_ = nullptr;
  // Notify components that graph is toast.
  updateComponentsState();
}

void
Sta::networkChanged()
{
  // Everything else from clear().
  search_->clear();
  levelize_->clear();
  if (parasitics_)
    parasitics_->clear();
  graph_delay_calc_->clear();
  sim_->clear();
  if (check_min_pulse_widths_)
    check_min_pulse_widths_->clear();
  if (check_min_periods_)
    check_min_periods_->clear();
  delete graph_;
  graph_ = nullptr;
  graph_sdc_annotated_ = false;
  current_instance_ = nullptr;
  updateComponentsState();
}

void
Sta::setTclInterp(Tcl_Interp *interp)
{
  tcl_interp_ = interp;
  report_->setTclInterp(interp);
}

Tcl_Interp *
Sta::tclInterp()
{
  return tcl_interp_;
}

CmdNamespace
Sta::cmdNamespace()
{
  return cmd_namespace_;
}

void
Sta::setCmdNamespace(CmdNamespace namespc)
{
  setCmdNamespace1(namespc);
  updateComponentsState();
}

void
Sta::setCmdNamespace1(CmdNamespace namespc)
{
  cmd_namespace_ = namespc;
  switch (cmd_namespace_) {
  case CmdNamespace::sta:
    cmd_network_ = network_;
    break;
  case CmdNamespace::sdc:
    cmd_network_ = sdc_network_;
    break;
  }
}

Instance *
Sta::currentInstance() const
{
  if (current_instance_ == nullptr)
    return network_->topInstance();
  else
    return current_instance_;
}

void
Sta::setCurrentInstance(Instance *inst)
{
  current_instance_ = inst;
}

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

LibertyLibrary *
Sta::readLiberty(const char *filename,
		 Corner *corner,
		 const MinMaxAll *min_max,
		 bool infer_latches)
{
  Stats stats(debug_, report_);
  LibertyLibrary *library = readLibertyFile(filename, corner, min_max,
                                            infer_latches);
  if (library
      // The default library is the first library read.
      // This corresponds to a link_path of '*'.
      && network_->defaultLibertyLibrary() == nullptr) {
    network_->setDefaultLibertyLibrary(library);
    // Set units from default (first) library.
    *units_ = *library->units();
  }
  stats.report("Read liberty");
  return library;
}

LibertyLibrary *
Sta::readLibertyFile(const char *filename,
		     Corner *corner,
		     const MinMaxAll *min_max,
		     bool infer_latches)
{
  LibertyLibrary *liberty = sta::readLibertyFile(filename, infer_latches,
						 network_);
  if (liberty) {
    // Don't map liberty cells if they are redefined by reading another
    // library with the same cell names.
    if (min_max == MinMaxAll::all()) {
      readLibertyAfter(liberty, corner, MinMax::min());
      readLibertyAfter(liberty, corner, MinMax::max());
    }
    else
      readLibertyAfter(liberty, corner, min_max->asMinMax());
    network_->readLibertyAfter(liberty);
  }
  return liberty;
}

LibertyLibrary *
Sta::readLibertyFile(const char *filename,
		     bool infer_latches)
{
  return sta::readLibertyFile(filename, infer_latches, network_);
}

void
Sta::readLibertyAfter(LibertyLibrary *liberty,
		      Corner *corner,
		      const MinMax *min_max)
{
  corner->addLiberty(liberty, min_max);
  LibertyLibrary::makeCornerMap(liberty, corner->libertyIndex(min_max),
				network_, report_);
}

bool
Sta::setMinLibrary(const char *min_filename,
		   const char *max_filename)
{
  LibertyLibrary *max_lib = network_->findLibertyFilename(max_filename);
  if (max_lib) {
    LibertyLibrary *min_lib = readLibertyFile(min_filename, cmd_corner_,
					      MinMaxAll::min(), false);
    return min_lib != nullptr;
  }
  else
    return false;
}

bool
Sta::readVerilog(const char *filename)
{
  NetworkReader *network = networkReader();
  if (network) {
    if (verilog_reader_ == nullptr)
      verilog_reader_ = new VerilogReader(network);
    readNetlistBefore();
    return verilog_reader_->read(filename);
  }
  else
    return false;
}

void
Sta::readNetlistBefore()
{
  clear();
  NetworkReader *network_reader = networkReader();
  if (network_reader)
    network_reader->readNetlistBefore();
}

bool
Sta::linkDesign(const char *top_cell_name,
                bool make_black_boxes)
{
  clear();
  Stats stats(debug_, report_);
  bool status = network_->linkNetwork(top_cell_name,
				      make_black_boxes,
				      report_);
  stats.report("Link");
  return status;
}

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

void
Sta::setDebugLevel(const char *what,
		   int level)
{
  debug_->setLevel(what, level);
}

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

void
Sta::setAnalysisType(AnalysisType analysis_type)
{
  if (analysis_type != sdc_->analysisType()) {
    sdc_->setAnalysisType(analysis_type);
    delaysInvalid();
    search_->deletePathGroups();
    corners_->analysisTypeChanged();
    if (graph_)
      graph_->setDelayCount(corners_->dcalcAnalysisPtCount());
  }
}

OperatingConditions *
Sta::operatingConditions(const MinMax *min_max) const
{
  return sdc_->operatingConditions(min_max);
}

void
Sta::setOperatingConditions(OperatingConditions *op_cond,
			    const MinMaxAll *min_max)
{
  sdc_->setOperatingConditions(op_cond, min_max);
  corners_->operatingConditionsChanged();
  delaysInvalid();
}

const Pvt *
Sta::pvt(Instance *inst,
	 const MinMax *min_max)
{
  return sdc_->pvt(inst, min_max);
}

void
Sta::setPvt(Instance *inst,
	    const MinMaxAll *min_max,
	    float process,
	    float voltage,
	    float temperature)
{
  Pvt pvt(process, voltage, temperature);
  setPvt(inst, min_max, pvt);
}

void
Sta::setPvt(const Instance *inst,
	    const MinMaxAll *min_max,
	    const Pvt &pvt)
{
  sdc_->setPvt(inst, min_max, pvt);
  delaysInvalidFrom(inst);
}

void
Sta::setVoltage(const MinMax *min_max,
                float voltage)
{
  sdc_->setVoltage(min_max, voltage);
}

void
Sta::setVoltage(const Net *net,
                const MinMax *min_max,
                float voltage)
{
  sdc_->setVoltage(net, min_max, voltage);
}

void
Sta::setTimingDerate(TimingDerateType type,
		     PathClkOrData clk_data,
		     const RiseFallBoth *rf,
		     const EarlyLate *early_late,
		     float derate)
{
  sdc_->setTimingDerate(type, clk_data, rf, early_late, derate);
  // Delay calculation results are still valid.
  // The search derates delays while finding arrival times.
  search_->arrivalsInvalid();
}

void
Sta::setTimingDerate(const Net *net,
		     PathClkOrData clk_data,
		     const RiseFallBoth *rf,
		     const EarlyLate *early_late,
		     float derate)
{
  sdc_->setTimingDerate(net, clk_data, rf, early_late, derate);
  // Delay calculation results are still valid.
  // The search derates delays while finding arrival times.
  search_->arrivalsInvalid();
}

void
Sta::setTimingDerate(const Instance *inst,
		     TimingDerateCellType type,
		     PathClkOrData clk_data,
		     const RiseFallBoth *rf,
		     const EarlyLate *early_late,
		     float derate)
{
  sdc_->setTimingDerate(inst, type, clk_data, rf, early_late, derate);
  // Delay calculation results are still valid.
  // The search derates delays while finding arrival times.
  search_->arrivalsInvalid();
}

void
Sta::setTimingDerate(const LibertyCell *cell,
		     TimingDerateCellType type,
		     PathClkOrData clk_data,
		     const RiseFallBoth *rf,
		     const EarlyLate *early_late,
		     float derate)
{
  sdc_->setTimingDerate(cell, type, clk_data, rf, early_late, derate);
  // Delay calculation results are still valid.
  // The search derates delays while finding arrival times.
  search_->arrivalsInvalid();
}

void
Sta::unsetTimingDerate()
{
  sdc_->unsetTimingDerate();
  // Delay calculation results are still valid.
  // The search derates delays while finding arrival times.
  search_->arrivalsInvalid();
}

void
Sta::setInputSlew(const Port *port,
		  const RiseFallBoth *rf,
		  const MinMaxAll *min_max,
		  float slew)
{
  sdc_->setInputSlew(port, rf, min_max, slew);
  delaysInvalidFrom(port);
}

void
Sta::setDriveCell(const LibertyLibrary *library,
		  const LibertyCell *cell,
		  const Port *port,
		  const LibertyPort *from_port,
		  float *from_slews,
		  const LibertyPort *to_port,
		  const RiseFallBoth *rf,
		  const MinMaxAll *min_max)
{
  sdc_->setDriveCell(library, cell, port, from_port, from_slews, to_port,
		     rf, min_max);
  delaysInvalidFrom(port);
}

void
Sta::setDriveResistance(const Port *port,
			const RiseFallBoth *rf,
			const MinMaxAll *min_max,
			float res)
{
  sdc_->setDriveResistance(port, rf, min_max, res);
  delaysInvalidFrom(port);
}

void
Sta::setLatchBorrowLimit(const Pin *pin,
			 float limit)
{
  sdc_->setLatchBorrowLimit(pin, limit);
  search_->requiredInvalid(pin);
}

void
Sta::setLatchBorrowLimit(const Instance *inst,
			 float limit)
{
  sdc_->setLatchBorrowLimit(inst, limit);
  search_->requiredInvalid(inst);
}

void
Sta::setLatchBorrowLimit(const Clock *clk,
			 float limit)
{
  sdc_->setLatchBorrowLimit(clk, limit);
  search_->arrivalsInvalid();
}

void
Sta::setMinPulseWidth(const RiseFallBoth *rf,
		      float min_width)
{
  sdc_->setMinPulseWidth(rf, min_width);
}

void
Sta::setMinPulseWidth(const Pin *pin,
		      const RiseFallBoth *rf,
		      float min_width)
{
  sdc_->setMinPulseWidth(pin, rf, min_width);
}

void
Sta::setMinPulseWidth(const Instance *inst,
		      const RiseFallBoth *rf,
		      float min_width)
{
  sdc_->setMinPulseWidth(inst, rf, min_width);
}

void
Sta::setMinPulseWidth(const Clock *clk,
		      const RiseFallBoth *rf,
		      float min_width)
{
  sdc_->setMinPulseWidth(clk, rf, min_width);
}

void
Sta::setWireloadMode(WireloadMode mode)
{
  sdc_->setWireloadMode(mode);
  delaysInvalid();
}

void
Sta::setWireload(Wireload *wireload,
		 const MinMaxAll *min_max)
{
  sdc_->setWireload(wireload, min_max);
  delaysInvalid();
}

void
Sta::setWireloadSelection(WireloadSelection *selection,
			  const MinMaxAll *min_max)
{
  sdc_->setWireloadSelection(selection, min_max);
  delaysInvalid();
}

void
Sta::setSlewLimit(Clock *clk,
		  const RiseFallBoth *rf,
		  const PathClkOrData clk_data,
		  const MinMax *min_max,
		  float slew)
{
  sdc_->setSlewLimit(clk, rf, clk_data, min_max, slew);
}

void
Sta::setSlewLimit(Port *port,
		  const MinMax *min_max,
		  float slew)
{
  sdc_->setSlewLimit(port, min_max, slew);
}

void
Sta::setSlewLimit(Cell *cell,
		  const MinMax *min_max,
		  float slew)
{
  sdc_->setSlewLimit(cell, min_max, slew);
}

void
Sta::setCapacitanceLimit(Cell *cell,
			 const MinMax *min_max,
			 float cap)
{
  sdc_->setCapacitanceLimit(cell, min_max, cap);
}

void
Sta::setCapacitanceLimit(Port *port,
			 const MinMax *min_max,
			 float cap)
{
  sdc_->setCapacitanceLimit(port, min_max, cap);
}

void
Sta::setCapacitanceLimit(Pin *pin,
			 const MinMax *min_max,
			 float cap)
{
  sdc_->setCapacitanceLimit(pin, min_max, cap);
}

void
Sta::setFanoutLimit(Cell *cell,
		    const MinMax *min_max,
		    float fanout)
{
  sdc_->setFanoutLimit(cell, min_max, fanout);
}

void
Sta::setFanoutLimit(Port *port,
		    const MinMax *min_max,
		    float fanout)
{
  sdc_->setFanoutLimit(port, min_max, fanout);
}

void
Sta::setMaxArea(float area)
{
  sdc_->setMaxArea(area);
}

void
Sta::makeClock(const char *name,
	       PinSet *pins,
	       bool add_to_pins,
	       float period,
	       FloatSeq *waveform,
	       char *comment)
{
  sdc_->makeClock(name, pins, add_to_pins, period, waveform, comment);
  update_genclks_ = true;
  search_->arrivalsInvalid();
}

void
Sta::makeGeneratedClock(const char *name,
			PinSet *pins,
			bool add_to_pins,
			Pin *src_pin,
			Clock *master_clk,
			int divide_by,
			int multiply_by,
			float duty_cycle,
			bool invert,
			bool combinational,
			IntSeq *edges,
			FloatSeq *edge_shifts,
			char *comment)
{
  sdc_->makeGeneratedClock(name, pins, add_to_pins,
			   src_pin, master_clk,
			   divide_by, multiply_by, duty_cycle,
			   invert, combinational,
			   edges, edge_shifts, comment);
  update_genclks_ = true;
  search_->arrivalsInvalid();
}

void
Sta::removeClock(Clock *clk)
{
  sdc_->removeClock(clk);
  search_->arrivalsInvalid();
}

bool
Sta::isClockSrc(const Pin *pin) const
{
  return sdc_->isClock(pin);
}

void
Sta::setPropagatedClock(Clock *clk)
{
  sdc_->setPropagatedClock(clk);
  delaysInvalid();
  clkPinsInvalid();
}

void
Sta::removePropagatedClock(Clock *clk)
{
  sdc_->removePropagatedClock(clk);
  delaysInvalid();
  clkPinsInvalid();
}

void
Sta::setPropagatedClock(Pin *pin)
{
  sdc_->setPropagatedClock(pin);
  delaysInvalid();
  clkPinsInvalid();
}

void
Sta::removePropagatedClock(Pin *pin)
{
  sdc_->removePropagatedClock(pin);
  delaysInvalid();
  clkPinsInvalid();
}

void
Sta::setClockSlew(Clock *clk,
		  const RiseFallBoth *rf,
		  const MinMaxAll *min_max,
		  float slew)
{
  sdc_->setClockSlew(clk, rf, min_max, slew);
  clockSlewChanged(clk);
}

void
Sta::removeClockSlew(Clock *clk)
{
  sdc_->removeClockSlew(clk);
  clockSlewChanged(clk);
}

void
Sta::clockSlewChanged(Clock *clk)
{
  for (const Pin *pin : clk->pins())
    graph_delay_calc_->delayInvalid(pin);
  search_->arrivalsInvalid();
}

void
Sta::setClockLatency(Clock *clk,
		     Pin *pin,
		     const RiseFallBoth *rf,
		     const MinMaxAll *min_max,
		     float delay)
{
  sdcChangedGraph();
  sdc_->setClockLatency(clk, pin, rf, min_max, delay);
  search_->arrivalsInvalid();
}

void
Sta::sdcChangedGraph()
{
  if (graph_sdc_annotated_)
    sdc_->removeGraphAnnotations();
  graph_sdc_annotated_ = false;
}

void
Sta::ensureGraphSdcAnnotated()
{
  if (!graph_sdc_annotated_) {
    sdc_->annotateGraph();
    graph_sdc_annotated_ = true;
  }
}

void
Sta::removeClockLatency(const Clock *clk,
			const Pin *pin)
{
  sdcChangedGraph();
  sdc_->removeClockLatency(clk, pin);
  search_->arrivalsInvalid();
}

void
Sta::setClockInsertion(const Clock *clk,
		       const Pin *pin,
		       const RiseFallBoth *rf,
		       const MinMaxAll *min_max,
		       const EarlyLateAll *early_late,
		       float delay)
{
  sdc_->setClockInsertion(clk, pin, rf, min_max, early_late, delay);
  search_->arrivalsInvalid();
}

void
Sta::removeClockInsertion(const Clock *clk,
			  const Pin *pin)
{
  sdc_->removeClockInsertion(clk, pin);
  search_->arrivalsInvalid();
}

void
Sta::setClockUncertainty(Clock *clk,
			 const SetupHoldAll *setup_hold,
			 float uncertainty)
{
  clk->setUncertainty(setup_hold, uncertainty);
  search_->arrivalsInvalid();
}

void
Sta::removeClockUncertainty(Clock *clk,
			    const SetupHoldAll *setup_hold)
{
  clk->removeUncertainty(setup_hold);
  search_->arrivalsInvalid();
}

void
Sta::setClockUncertainty(Pin *pin,
			 const SetupHoldAll *setup_hold,
			 float uncertainty)
{
  sdc_->setClockUncertainty(pin, setup_hold, uncertainty);
  search_->arrivalsInvalid();
}

void
Sta::removeClockUncertainty(Pin *pin,
			    const SetupHoldAll *setup_hold)
{
  sdc_->removeClockUncertainty(pin, setup_hold);
  search_->arrivalsInvalid();
}

void
Sta::setClockUncertainty(Clock *from_clk,
			 const RiseFallBoth *from_rf,
			 Clock *to_clk,
			 const RiseFallBoth *to_rf,
			 const SetupHoldAll *setup_hold,
			 float uncertainty)
{
  sdc_->setClockUncertainty(from_clk, from_rf, to_clk, to_rf,
			    setup_hold, uncertainty);
  search_->arrivalsInvalid();
}

void
Sta::removeClockUncertainty(Clock *from_clk,
			    const RiseFallBoth *from_rf,
			    Clock *to_clk,
			    const RiseFallBoth *to_rf,
			    const SetupHoldAll *setup_hold)
{
  sdc_->removeClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold);
  search_->arrivalsInvalid();
}

ClockGroups *
Sta::makeClockGroups(const char *name,
		     bool logically_exclusive,
		     bool physically_exclusive,
		     bool asynchronous,
		     bool allow_paths,
		     const char *comment)
{
  ClockGroups *groups = sdc_->makeClockGroups(name,
					      logically_exclusive,
					      physically_exclusive,
					      asynchronous,
					      allow_paths,
					      comment);
  search_->requiredsInvalid();
  return groups;
}

void
Sta::removeClockGroupsLogicallyExclusive(const char *name)
{
  sdc_->removeClockGroupsLogicallyExclusive(name);
  search_->requiredsInvalid();
}

void
Sta::removeClockGroupsPhysicallyExclusive(const char *name)
{
  sdc_->removeClockGroupsPhysicallyExclusive(name);
  search_->requiredsInvalid();
}

void
Sta::removeClockGroupsAsynchronous(const char *name)
{
  sdc_->removeClockGroupsAsynchronous(name);
  search_->requiredsInvalid();
}

void
Sta::makeClockGroup(ClockGroups *clk_groups,
		    ClockSet *clks)
{
  sdc_->makeClockGroup(clk_groups, clks);
}

void
Sta::setClockSense(PinSet *pins,
		   ClockSet *clks,
		   ClockSense sense)
{
  sdc_->setClockSense(pins, clks, sense);
  search_->arrivalsInvalid();
}

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

void
Sta::setClockGatingCheck(const RiseFallBoth *rf,
			 const SetupHold *setup_hold,
			 float margin)
{
  sdc_->setClockGatingCheck(rf, setup_hold, margin);
  search_->arrivalsInvalid();
}

void
Sta::setClockGatingCheck(Clock *clk,
			 const RiseFallBoth *rf,
			 const SetupHold *setup_hold,
			 float margin)
{
  sdc_->setClockGatingCheck(clk, rf, setup_hold, margin);
  search_->arrivalsInvalid();
}

void
Sta::setClockGatingCheck(Instance *inst,
			 const RiseFallBoth *rf,
			 const SetupHold *setup_hold,
			 float margin,
			 LogicValue active_value)
{
  sdc_->setClockGatingCheck(inst, rf, setup_hold, margin,active_value);
  search_->arrivalsInvalid();
}

void
Sta::setClockGatingCheck(Pin *pin,
			 const RiseFallBoth *rf,
			 const SetupHold *setup_hold,
			 float margin,
			 LogicValue active_value)
{
  sdc_->setClockGatingCheck(pin, rf, setup_hold, margin,active_value);
  search_->arrivalsInvalid();
}

void
Sta::setDataCheck(Pin *from,
		  const RiseFallBoth *from_rf,
		  Pin *to,
		  const RiseFallBoth *to_rf,
		  Clock *clk,
		  const SetupHoldAll *setup_hold,
		  float margin)
{
  sdcChangedGraph();
  sdc_->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold,margin);
  search_->requiredInvalid(to);
}

void
Sta::removeDataCheck(Pin *from,
		     const RiseFallBoth *from_rf,
		     Pin *to,
		     const RiseFallBoth *to_rf,
		     Clock *clk,
		     const SetupHoldAll *setup_hold)
{
  sdc_->removeDataCheck(from, from_rf, to, to_rf, clk, setup_hold);
  search_->requiredInvalid(to);
}

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

void
Sta::disable(Pin *pin)
{
  sdcChangedGraph();
  sdc_->disable(pin);
  // Levelization respects disabled edges.
  levelize_->invalid();
  graph_delay_calc_->delayInvalid(pin);
  search_->arrivalsInvalid();
}

void
Sta::removeDisable(Pin *pin)
{
  sdcChangedGraph();
  sdc_->removeDisable(pin);
  disableAfter();
  // Levelization respects disabled edges.
  levelize_->invalid();
  graph_delay_calc_->delayInvalid(pin);
  search_->arrivalsInvalid();
}

void
Sta::disable(Instance *inst,
	     LibertyPort *from,
	     LibertyPort *to)
{
  sdcChangedGraph();
  sdc_->disable(inst, from, to);

  if (from) {
    Pin *from_pin = network_->findPin(inst, from);
    graph_delay_calc_->delayInvalid(from_pin);
  }
  if (to) {
    Pin *to_pin = network_->findPin(inst, to);
    graph_delay_calc_->delayInvalid(to_pin);
  }
  if (from == nullptr && to == nullptr) {
    InstancePinIterator *pin_iter = network_->pinIterator(inst);
    while (pin_iter->hasNext()) {
      Pin *pin = pin_iter->next();
      graph_delay_calc_->delayInvalid(pin);
    }
    delete pin_iter;
  }
  // Levelization respects disabled edges.
  levelize_->invalid();
  search_->arrivalsInvalid();
}

void
Sta::removeDisable(Instance *inst,
		   LibertyPort *from,
		   LibertyPort *to)
{
  sdcChangedGraph();
  sdc_->removeDisable(inst, from, to);

  if (from) {
    Pin *from_pin = network_->findPin(inst, from);
    graph_delay_calc_->delayInvalid(from_pin);
  }
  if (to) {
    Pin *to_pin = network_->findPin(inst, to);
    graph_delay_calc_->delayInvalid(to_pin);
  }
  if (from == nullptr && to == nullptr) {
    InstancePinIterator *pin_iter = network_->pinIterator(inst);
    while (pin_iter->hasNext()) {
      Pin *pin = pin_iter->next();
      graph_delay_calc_->delayInvalid(pin);
    }
    delete pin_iter;
  }
  // Levelization respects disabled edges.
  levelize_->invalid();
  search_->arrivalsInvalid();
}

void
Sta::disable(LibertyCell *cell,
	     LibertyPort *from,
	     LibertyPort *to)
{
  sdc_->disable(cell, from, to);
  disableAfter();
}

void
Sta::removeDisable(LibertyCell *cell,
		   LibertyPort *from,
		   LibertyPort *to)
{
  sdc_->removeDisable(cell, from, to);
  disableAfter();
}

void
Sta::disable(LibertyPort *port)
{
  sdcChangedGraph();
  sdc_->disable(port);
  disableAfter();
}

void
Sta::removeDisable(LibertyPort *port)
{
  sdcChangedGraph();
  sdc_->removeDisable(port);
  disableAfter();
}

void
Sta::disable(Port *port)
{
  sdc_->disable(port);
  disableAfter();
}

void
Sta::removeDisable(Port *port)
{
  sdc_->removeDisable(port);
  disableAfter();
}

void
Sta::disable(Edge *edge)
{
  sdc_->disable(edge);
  disableAfter();
}

void
Sta::removeDisable(Edge *edge)
{
  sdc_->removeDisable(edge);
  disableAfter();
}

void
Sta::disable(TimingArcSet *arc_set)
{
  sdc_->disable(arc_set);
  disableAfter();
}

void
Sta::removeDisable(TimingArcSet *arc_set)
{
  sdc_->removeDisable(arc_set);
  disableAfter();
}

void
Sta::disableAfter()
{
  // Levelization respects disabled edges.
  levelize_->invalid();
  delaysInvalid();
}

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

EdgeSeq
Sta::disabledEdges()
{
  ensureLevelized();
  EdgeSeq disabled_edges;
  VertexIterator vertex_iter(graph_);
  while (vertex_iter.hasNext()) {
    Vertex *vertex = vertex_iter.next();
    VertexOutEdgeIterator edge_iter(vertex, graph_);
    while (edge_iter.hasNext()) {
      Edge *edge = edge_iter.next();
      if (isDisabledConstant(edge)
	  || isDisabledCondDefault(edge)
	  || isDisabledConstraint(edge)
	  || edge->isDisabledLoop()
	  || isDisabledPresetClr(edge))
	disabled_edges.push_back(edge);
    }
  }
  return disabled_edges;
}


EdgeSeq
Sta::disabledEdgesSorted()
{
  EdgeSeq disabled_edges = disabledEdges();
  sortEdges(&disabled_edges, network_, graph_);
  return disabled_edges;
}

bool
Sta::isDisabledConstraint(Edge *edge)
{
  Pin *from_pin = edge->from(graph_)->pin();
  Pin *to_pin = edge->to(graph_)->pin();
  const Instance *inst = network_->instance(from_pin);
  TimingArcSet *arc_set = edge->timingArcSet();
  return sdc_->isDisabled(from_pin)
    || sdc_->isDisabled(to_pin)
    || sdc_->isDisabled(inst, from_pin, to_pin, edge->role())
    || sdc_->isDisabled(edge)
    || sdc_->isDisabled(arc_set);
}

bool
Sta::isDisabledConstant(Edge *edge)
{
  sim_->ensureConstantsPropagated();
  const TimingRole *role = edge->role();
  Vertex *from_vertex = edge->from(graph_);
  Pin *from_pin = from_vertex->pin();
  Vertex *to_vertex = edge->to(graph_);
  Pin *to_pin = to_vertex->pin();
  const Instance *inst = network_->instance(from_pin);
  return sim_->logicZeroOne(from_vertex)
    || sim_->logicZeroOne(to_vertex)
    || (!role->isWire()
	&& (isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_)
	    || isModeDisabled(edge, inst, network_, sim_)
	    || hasDisabledArcs(edge, graph_)));
}

static bool
hasDisabledArcs(Edge *edge,
		Graph *graph)
{
  TimingArcSet *arc_set = edge->timingArcSet();
  for (TimingArc *arc : arc_set->arcs()) {
    if (!searchThru(edge, arc, graph))
      return true;
  }
  return false;
}

bool
Sta::isDisabledLoop(Edge *edge) const
{
  return levelize_->isDisabledLoop(edge);
}

PinSet
Sta::disabledConstantPins(Edge *edge)
{
  sim_->ensureConstantsPropagated();
  PinSet pins(network_);
  Vertex *from_vertex = edge->from(graph_);
  Pin *from_pin = from_vertex->pin();
  Vertex *to_vertex = edge->to(graph_);
  const Pin *to_pin = to_vertex->pin();
  if (sim_->logicZeroOne(from_vertex))
    pins.insert(from_pin);
  if (edge->role()->isWire()) {
    if (sim_->logicZeroOne(to_vertex))
      pins.insert(to_pin);
  }
  else {
    const Instance *inst = network_->instance(to_pin);
    bool is_disabled;
    FuncExpr *disable_cond;
    isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_,
		   is_disabled, disable_cond);
    if (is_disabled)
      exprConstantPins(disable_cond, inst, pins);
    isModeDisabled(edge, inst, network_, sim_,
		   is_disabled, disable_cond);
    if (is_disabled)
      exprConstantPins(disable_cond, inst, pins);
    if (hasDisabledArcs(edge, graph_)) {
      LibertyPort *to_port = network_->libertyPort(to_pin);
      if (to_port) {
	FuncExpr *func = to_port->function();
	if (func
	    && sim_->functionSense(inst, from_pin, to_pin) != edge->sense())
	  exprConstantPins(func, inst, pins);
      }
    }
  }
  return pins;
}

TimingSense
Sta::simTimingSense(Edge *edge)
{
  Pin *from_pin = edge->from(graph_)->pin();
  Pin *to_pin = edge->to(graph_)->pin();
  Instance *inst = network_->instance(from_pin);
  return sim_->functionSense(inst, from_pin, to_pin);
}

void
Sta::exprConstantPins(FuncExpr *expr,
                      const Instance *inst,
                      PinSet &pins)
{
  FuncExprPortIterator port_iter(expr);
  while (port_iter.hasNext()) {
    LibertyPort *port = port_iter.next();
    Pin *pin = network_->findPin(inst, port);
    if (pin) {
      LogicValue value = sim_->logicValue(pin);
      if (value != LogicValue::unknown)
	pins.insert(pin);
    }
  }
}

bool
Sta::isDisabledBidirectInstPath(Edge *edge) const
{
  return !variables_->bidirectInstPathsEnabled()
    && edge->isBidirectInstPath();
}

bool
Sta::isDisabledBidirectNetPath(Edge *edge) const
{
  return !variables_->bidirectNetPathsEnabled()
    && edge->isBidirectNetPath();
}

bool
Sta::isDisabledPresetClr(Edge *edge) const
{
  return !variables_->presetClrArcsEnabled()
    && edge->role() == TimingRole::regSetClr();
}

void
Sta::disableClockGatingCheck(Instance *inst)
{
  sdc_->disableClockGatingCheck(inst);
  search_->endpointsInvalid();
}

void
Sta::disableClockGatingCheck(Pin *pin)
{
  sdc_->disableClockGatingCheck(pin);
  search_->endpointsInvalid();
}

void
Sta::removeDisableClockGatingCheck(Instance *inst)
{
  sdc_->removeDisableClockGatingCheck(inst);
  search_->endpointsInvalid();
}

void
Sta::removeDisableClockGatingCheck(Pin *pin)
{
  sdc_->removeDisableClockGatingCheck(pin);
  search_->endpointsInvalid();
}

void
Sta::setLogicValue(Pin *pin,
		   LogicValue value)
{
  sdc_->setLogicValue(pin, value);
  // Levelization respects constant disabled edges.
  levelize_->invalid();
  sim_->constantsInvalid();
  // Constants disable edges which isolate downstream vertices of the
  // graph from the delay calculator's BFS search.  This means that
  // simply invaldating the delays downstream from the constant pin
  // fails.  This could be more incremental if the graph delay
  // calculator searched thru disabled edges but ignored their
  // results.
  delaysInvalid();
}

void
Sta::setCaseAnalysis(Pin *pin,
		     LogicValue value)
{
  sdc_->setCaseAnalysis(pin, value);
  // Levelization respects constant disabled edges.
  levelize_->invalid();
  sim_->constantsInvalid();
  // Constants disable edges which isolate downstream vertices of the
  // graph from the delay calculator's BFS search.  This means that
  // simply invaldating the delays downstream from the constant pin
  // fails.  This could be handled incrementally by invalidating delays
  // on the output of gates one level downstream.
  delaysInvalid();
}

void
Sta::removeCaseAnalysis(Pin *pin)
{
  sdc_->removeCaseAnalysis(pin);
  // Levelization respects constant disabled edges.
  levelize_->invalid();
  sim_->constantsInvalid();
  // Constants disable edges which isolate downstream vertices of the
  // graph from the delay calculator's BFS search.  This means that
  // simply invaldating the delays downstream from the constant pin
  // fails.  This could be handled incrementally by invalidating delays
  // on the output of gates one level downstream.
  delaysInvalid();
}

void
Sta::setInputDelay(const Pin *pin,
		   const RiseFallBoth *rf,
		   const Clock *clk,
		   const RiseFall *clk_rf,
		   const Pin *ref_pin,
		   bool source_latency_included,
		   bool network_latency_included,
		   const MinMaxAll *min_max,
		   bool add,
		   float delay)
{
  sdc_->setInputDelay(pin, rf, clk, clk_rf, ref_pin,
		      source_latency_included, network_latency_included,
		      min_max, add, delay);

  search_->arrivalInvalid(pin);
}

void 
Sta::removeInputDelay(const Pin *pin,
		      const RiseFallBoth *rf,
		      const Clock *clk,
		      const RiseFall *clk_rf,
		      const MinMaxAll *min_max)
{
  sdc_->removeInputDelay(pin, rf, clk, clk_rf, min_max);
  search_->arrivalInvalid(pin);
}

void
Sta::setOutputDelay(const Pin *pin,
		    const RiseFallBoth *rf,
		    const Clock *clk,
		    const RiseFall *clk_rf,
		    const Pin *ref_pin,
		    bool source_latency_included,
		    bool network_latency_included,
		    const MinMaxAll *min_max,
		    bool add,
		    float delay)
{
  sdc_->setOutputDelay(pin, rf, clk, clk_rf, ref_pin,
		       source_latency_included,network_latency_included,
		       min_max, add, delay);
  sdcChangedGraph();
  search_->requiredInvalid(pin);
}

void 
Sta::removeOutputDelay(const Pin *pin,
		       const RiseFallBoth *rf,
		       const Clock *clk,
		       const RiseFall *clk_rf,
		       const MinMaxAll *min_max)
{
  sdc_->removeOutputDelay(pin, rf, clk, clk_rf, min_max);
  sdcChangedGraph();
  search_->arrivalInvalid(pin);
}

void
Sta::makeFalsePath(ExceptionFrom *from,
		   ExceptionThruSeq *thrus,
		   ExceptionTo *to,
		   const MinMaxAll *min_max,
		   const char *comment)
{
  sdc_->makeFalsePath(from, thrus, to, min_max, comment);
  search_->arrivalsInvalid();
}

void
Sta::makeMulticyclePath(ExceptionFrom *from,
			ExceptionThruSeq *thrus,
			ExceptionTo *to,
			const MinMaxAll *min_max,
			bool use_end_clk,
			int path_multiplier,
			const char *comment)
{
  sdc_->makeMulticyclePath(from, thrus, to, min_max,
			   use_end_clk, path_multiplier,
			   comment);
  search_->arrivalsInvalid();
}

void
Sta::makePathDelay(ExceptionFrom *from,
		   ExceptionThruSeq *thrus,
		   ExceptionTo *to,
		   const MinMax *min_max,
		   bool ignore_clk_latency,
		   float delay,
		   const char *comment)
{
  sdc_->makePathDelay(from, thrus, to, min_max, 
		      ignore_clk_latency, delay,
		      comment);
  search_->endpointsInvalid();
  search_->arrivalsInvalid();
}

void
Sta::resetPath(ExceptionFrom *from,
	       ExceptionThruSeq *thrus,
	       ExceptionTo *to,
	       const MinMaxAll *min_max)
{
  sdc_->resetPath(from, thrus, to, min_max);
  search_->arrivalsInvalid();
}

void
Sta::makeGroupPath(const char *name,
		   bool is_default,
		   ExceptionFrom *from,
		   ExceptionThruSeq *thrus,
		   ExceptionTo *to,
		   const char *comment)
{
  sdc_->makeGroupPath(name, is_default, from, thrus, to, comment);
  search_->arrivalsInvalid();
}

bool
Sta::isGroupPathName(const char *group_name)
{
  return PathGroups::isGroupPathName(group_name)
    || sdc_->findClock(group_name)
    || sdc_->isGroupPathName(group_name);
}

ExceptionFrom *
Sta::makeExceptionFrom(PinSet *from_pins,
		       ClockSet *from_clks,
		       InstanceSet *from_insts,
		       const RiseFallBoth *from_rf)
{
  return sdc_->makeExceptionFrom(from_pins, from_clks, from_insts,
				 from_rf);
}

void
Sta::checkExceptionFromPins(ExceptionFrom *from,
			    const char *file,
			    int line) const
{
  if (from) {
    PinSet::ConstIterator pin_iter(from->pins());
    while (pin_iter.hasNext()) {
      const Pin *pin = pin_iter.next();
      if (exceptionFromInvalid(pin)) {
	if (line)
	  report_->fileWarn(1554, file, line, "'%s' is not a valid start point.",
			    cmd_network_->pathName(pin));
	else
	  report_->warn(1550, "'%s' is not a valid start point.",
			cmd_network_->pathName(pin));
      }
    }
  }
}

bool
Sta::exceptionFromInvalid(const Pin *pin) const
{
  Net *net = network_->net(pin);
  // Floating pins are invalid.
  return (net == nullptr
	  && !network_->isTopLevelPort(pin))
    || (net
	// Pins connected to power/ground are invalid.
	&& (network_->isPower(net)
            || network_->isGround(net)))
    || !((network_->isTopLevelPort(pin)
	  && network_->direction(pin)->isAnyInput())
	 || network_->isRegClkPin(pin)
	 || network_->isLatchData(pin)
         || network_->direction(pin)->isInternal());
}

void
Sta::deleteExceptionFrom(ExceptionFrom *from)
{
  delete from;
}

ExceptionThru *
Sta::makeExceptionThru(PinSet *pins,
		       NetSet *nets,
		       InstanceSet *insts,
		       const RiseFallBoth *rf)
{
  return sdc_->makeExceptionThru(pins, nets, insts, rf);
}

void
Sta::deleteExceptionThru(ExceptionThru *thru)
{
  delete thru;
}

ExceptionTo *
Sta::makeExceptionTo(PinSet *to_pins,
		     ClockSet *to_clks,
		     InstanceSet *to_insts,
		     const RiseFallBoth *rf,
		     const RiseFallBoth *end_rf)
{
  return sdc_->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf);
}

void
Sta::deleteExceptionTo(ExceptionTo *to)
{
  delete to;
}

void
Sta::checkExceptionToPins(ExceptionTo *to,
			  const char *file,
			  int line) const
{
  if (to) {
    PinSet::Iterator pin_iter(to->pins());
    while (pin_iter.hasNext()) {
      const Pin *pin = pin_iter.next();
      if (sdc_->exceptionToInvalid(pin)) {
	if (line)
	  report_->fileWarn(1551, file, line, "'%s' is not a valid endpoint.",
			    cmd_network_->pathName(pin));
	else
	  report_->warn(1552, "'%s' is not a valid endpoint.",
			cmd_network_->pathName(pin));
      }
    }
  }
}

void
Sta::removeConstraints()
{
  levelize_->invalid();
  graph_delay_calc_->clear();
  search_->clear();
  sim_->constantsInvalid();
  if (graph_)
    sdc_->removeGraphAnnotations();
  sdc_->clear();
  clk_network_->clear();
}

void
Sta::constraintsChanged()
{
  levelize_->invalid();
  delaysInvalid();
  sim_->constantsInvalid();
}

void
Sta::writeSdc(const char *filename,
	      bool leaf,
	      bool native,
	      int digits,
              bool gzip,
	      bool no_timestamp)
{
  ensureLibLinked();
  sta::writeSdc(network_->topInstance(), filename, "write_sdc",
		leaf, native, digits, gzip, no_timestamp, sdc_);
}

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

CheckErrorSeq &
Sta::checkTiming(bool no_input_delay,
		 bool no_output_delay,
		 bool reg_multiple_clks,
		 bool reg_no_clks,
		 bool unconstrained_endpoints,
		 bool loops,
		 bool generated_clks)
{
  searchPreamble();
  if (unconstrained_endpoints)
    // Only need non-clock arrivals for unconstrained_endpoints.
    search_->findAllArrivals();
  else
    search_->findClkArrivals();
  if (check_timing_ == nullptr)
    makeCheckTiming();
  return check_timing_->check(no_input_delay, no_output_delay,
			      reg_multiple_clks, reg_no_clks,
			      unconstrained_endpoints,
			      loops, generated_clks);
}

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

bool
Sta::crprEnabled() const
{
  return variables_->crprEnabled();
}

void
Sta::setCrprEnabled(bool enabled)
{
  // Pessimism is only relevant for on_chip_variation analysis.
  if (sdc_->analysisType() == AnalysisType::ocv
      && enabled != variables_->crprEnabled())
    search_->arrivalsInvalid();
  variables_->setCrprEnabled(enabled);
}

CrprMode
Sta::crprMode() const
{
  return variables_->crprMode();
}

void
Sta::setCrprMode(CrprMode mode)
{
  // Pessimism is only relevant for on_chip_variation analysis.
  if (sdc_->analysisType() == AnalysisType::ocv
      && variables_->crprEnabled()
      && variables_->crprMode() != mode)
    search_->arrivalsInvalid();
  variables_->setCrprMode(mode);
}

bool
Sta::pocvEnabled() const
{
  return variables_->pocvEnabled();
}

void
Sta::setPocvEnabled(bool enabled)
{
  if (enabled != variables_->pocvEnabled())
    delaysInvalid();
  variables_->setPocvEnabled(enabled);
}

void
Sta::setSigmaFactor(float factor)
{
  if (!fuzzyEqual(factor, sigma_factor_)) {
    sigma_factor_ = factor;
    search_->arrivalsInvalid();
    updateComponentsState();
  }
}

bool
Sta::propagateGatedClockEnable() const
{
  return variables_->propagateGatedClockEnable();
}

void
Sta::setPropagateGatedClockEnable(bool enable)
{
  if (variables_->propagateGatedClockEnable() != enable)
    search_->arrivalsInvalid();
  variables_->setPropagateGatedClockEnable(enable);
}

bool
Sta::presetClrArcsEnabled() const
{
  return variables_->presetClrArcsEnabled();
}

void
Sta::setPresetClrArcsEnabled(bool enable)
{
  if (variables_->presetClrArcsEnabled() != enable) {
    levelize_->invalid();
    delaysInvalid();
  }
  variables_->setPresetClrArcsEnabled(enable);
}

bool
Sta::condDefaultArcsEnabled() const
{
  return variables_->condDefaultArcsEnabled();
}

void
Sta::setCondDefaultArcsEnabled(bool enabled)
{
  if (variables_->condDefaultArcsEnabled() != enabled) {
    delaysInvalid();
    variables_->setCondDefaultArcsEnabled(enabled);
  }
}

bool
Sta::bidirectInstPathsEnabled() const
{
  return variables_->bidirectInstPathsEnabled();
}

void
Sta::setBidirectInstPathsEnabled(bool enabled)
{
  if (variables_->bidirectInstPathsEnabled() != enabled) {
    levelize_->invalid();
    delaysInvalid();
    variables_->setBidirectInstPathsEnabled(enabled);
  }
}

bool
Sta::bidirectNetPathsEnabled() const
{
  return variables_->bidirectNetPathsEnabled();
}

void
Sta::setBidirectNetPathsEnabled(bool enabled)
{
  if (variables_->bidirectNetPathsEnabled() != enabled) {
    delaysInvalid();
    variables_->setBidirectNetPathsEnabled(enabled);
  }
}

bool
Sta::recoveryRemovalChecksEnabled() const
{
  return variables_->recoveryRemovalChecksEnabled();
} 

void
Sta::setRecoveryRemovalChecksEnabled(bool enabled)
{
  if (variables_->recoveryRemovalChecksEnabled() != enabled) {
    search_->arrivalsInvalid();
    variables_->setRecoveryRemovalChecksEnabled(enabled);
  }
}

bool
Sta::gatedClkChecksEnabled() const
{
  return variables_->gatedClkChecksEnabled();
}

void
Sta::setGatedClkChecksEnabled(bool enabled)
{
  if (variables_->gatedClkChecksEnabled() != enabled) {
    search_->arrivalsInvalid();
    variables_->setGatedClkChecksEnabled(enabled);
  }
}

bool
Sta::dynamicLoopBreaking() const
{
  return variables_->dynamicLoopBreaking();
}

void
Sta::setDynamicLoopBreaking(bool enable)
{
  if (variables_->dynamicLoopBreaking() != enable) {
    if (levelize_->levelized()) {
      if (enable)
	sdc_->makeLoopExceptions();
      else
	sdc_->deleteLoopExceptions();
    }
    search_->arrivalsInvalid();
    variables_->setDynamicLoopBreaking(enable);
  }
}

bool
Sta::useDefaultArrivalClock() const
{
  return variables_->useDefaultArrivalClock();
}

void
Sta::setUseDefaultArrivalClock(bool enable)
{
  if (variables_->useDefaultArrivalClock() != enable) {
    variables_->setUseDefaultArrivalClock(enable);
    search_->arrivalsInvalid();
  }
}

bool
Sta::propagateAllClocks() const
{
  return variables_->propagateAllClocks();
}

void
Sta::setPropagateAllClocks(bool prop)
{
  variables_->setPropagateAllClocks(prop);
}

bool
Sta::clkThruTristateEnabled() const
{
  return variables_->clkThruTristateEnabled();
}

void
Sta::setClkThruTristateEnabled(bool enable)
{
  if (enable != variables_->clkThruTristateEnabled()) {
    search_->arrivalsInvalid();
    variables_->setClkThruTristateEnabled(enable);
  }
}

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

Corner *
Sta::findCorner(const char *corner_name)
{
  return corners_->findCorner(corner_name);
}

bool
Sta::multiCorner()
{
  return corners_->multiCorner();
}

// Init one corner named "default".
void
Sta::makeCorners()
{
  corners_ = new Corners(this);
  StringSet corner_names;
  corner_names.insert("default");
  corners_->makeCorners(&corner_names);
  cmd_corner_ = corners_->findCorner(0);
  sdc_->makeCornersAfter(corners_);
}

void
Sta::makeCorners(StringSet *corner_names)
{
  if (corner_names->size() > corner_count_max)
    report_->error(1553, "maximum corner count exceeded");
  sdc_->makeCornersBefore();
  parasitics_->deleteParasitics();
  corners_->makeCorners(corner_names);
  makeParasiticAnalysisPts();
  cmd_corner_ = corners_->findCorner(0);
  updateComponentsState();
  sdc_->makeCornersAfter(corners_);
}

Corner *
Sta::cmdCorner() const
{
  return cmd_corner_;
}

void
Sta::setCmdCorner(Corner *corner)
{
  cmd_corner_ = corner;
}

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

// from/thrus/to are owned and deleted by Search.
// Returned sequence is owned by the caller.
// PathEnds are owned by Search PathGroups and deleted on next call.
PathEndSeq
Sta::findPathEnds(ExceptionFrom *from,
		  ExceptionThruSeq *thrus,
		  ExceptionTo *to,
		  bool unconstrained,
		  const Corner *corner,
		  const MinMaxAll *min_max,
		  int group_path_count,
		  int endpoint_path_count,
		  bool unique_pins,
		  float slack_min,
		  float slack_max,
		  bool sort_by_slack,
		  PathGroupNameSet *group_names,
		  bool setup,
		  bool hold,
		  bool recovery,
		  bool removal,
		  bool clk_gating_setup,
		  bool clk_gating_hold)
{
  searchPreamble();
  return search_->findPathEnds(from, thrus, to, unconstrained,
			       corner, min_max, group_path_count, endpoint_path_count,
			       unique_pins, slack_min, slack_max,
			       sort_by_slack, group_names,
			       setup, hold,
			       recovery, removal,
			       clk_gating_setup, clk_gating_hold);
}

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

// Overall flow:
//  make graph
//  propagate constants
//  levelize
//  delay calculation
//  update generated clocks
//  find arrivals

void
Sta::searchPreamble()
{
  findDelays();
  updateGeneratedClks();
  sdc_->searchPreamble();
  // Delete results from last findPathEnds because they point to filtered arrivals.
  search_->deletePathGroups();
  search_->deleteFilteredArrivals();
}

void
Sta::setReportPathFormat(ReportPathFormat format)
{
  report_path_->setPathFormat(format);
}

void
Sta::setReportPathFieldOrder(StringSeq *field_names)
{
  report_path_->setReportFieldOrder(field_names);
}

void
Sta::setReportPathFields(bool report_input_pin,
                         bool report_hier_pins,
			 bool report_net,
			 bool report_cap,
			 bool report_slew,
			 bool report_fanout,
			 bool report_src_attr)
{
  report_path_->setReportFields(report_input_pin, report_hier_pins, report_net,
                                report_cap, report_slew, report_fanout,
                                report_src_attr);
}

ReportField *
Sta::findReportPathField(const char *name)
{
  return report_path_->findField(name);
}

void
Sta::setReportPathDigits(int digits)
{
  report_path_->setDigits(digits);
}

void
Sta::setReportPathNoSplit(bool no_split)
{
  report_path_->setNoSplit(no_split);
}

void
Sta::setReportPathSigmas(bool report_sigmas)
{
  report_path_->setReportSigmas(report_sigmas);
}

void
Sta::reportPathEndHeader()
{
  report_path_->reportPathEndHeader();
}

void
Sta::reportPathEndFooter()
{
  report_path_->reportPathEndFooter();
}

void
Sta::reportPathEnd(PathEnd *end)
{
  report_path_->reportPathEnd(end);
}

void
Sta::reportPathEnd(PathEnd *end,
		   PathEnd *prev_end,
                   bool last)
{
  report_path_->reportPathEnd(end, prev_end, last);
}

void
Sta::reportPathEnds(PathEndSeq *ends)
{
  report_path_->reportPathEnds(ends);
}

void
Sta::reportPath(const Path *path)
{
  report_path_->reportPath(path);
}

void
Sta::updateTiming(bool full)
{
  searchPreamble();
  if (full)
    search_->arrivalsInvalid();
  search_->findAllArrivals();
}

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

void
Sta::reportClkSkew(ConstClockSeq &clks,
		   const Corner *corner,
		   const SetupHold *setup_hold,
                   bool include_internal_latency,
		   int digits)
{
  clkSkewPreamble();
  clk_skews_->reportClkSkew(clks, corner, setup_hold,
                            include_internal_latency, digits);
}

float
Sta::findWorstClkSkew(const SetupHold *setup_hold,
                      bool include_internal_latency)
{
  clkSkewPreamble();
  return clk_skews_->findWorstClkSkew(cmd_corner_, setup_hold,
                                      include_internal_latency);
}

void
Sta::clkSkewPreamble()
{
  ensureClkArrivals();
  if (clk_skews_ == nullptr)
    clk_skews_ = new ClkSkews(this);
}

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

void
Sta::reportClkLatency(ConstClockSeq &clks,
                      const Corner *corner,
                      bool include_internal_latency,
                      int digits)
{
  ensureClkArrivals();
  ClkLatency clk_latency(this);
  clk_latency.reportClkLatency(clks, corner, include_internal_latency, digits);
}

ClkDelays
Sta::findClkDelays(const Clock *clk,
                   bool include_internal_latency)
{
  ensureClkArrivals();
  ClkLatency clk_latency(this);
  return clk_latency.findClkDelays(clk, nullptr, include_internal_latency);
}

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

void
Sta::delaysInvalid() const
{
  graph_delay_calc_->delaysInvalid();
  search_->arrivalsInvalid();
}

void
Sta::arrivalsInvalid()
{
  search_->arrivalsInvalid();
}

void
Sta::ensureClkArrivals()
{
  searchPreamble();
  search_->findClkArrivals();
}

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

PinSet
Sta::startpointPins()
{
  ensureGraph();
  PinSet pins(network_);
  VertexPinCollector visitor(pins);
  search_->visitStartpoints(&visitor);
  return pins;
}

VertexSet *
Sta::endpoints()
{
  ensureGraph();
  return search_->endpoints();
}

PinSet
Sta::endpointPins()
{
  ensureGraph();
  PinSet pins(network_);
  for (Vertex *vertex : *search_->endpoints())
    pins.insert(vertex->pin());
  return pins;
}

int
Sta::endpointViolationCount(const MinMax *min_max)
{
  int violations = 0;
  for (Vertex *end : *search_->endpoints()) {
    if (delayLess(vertexSlack(end, min_max), 0.0, this))
      violations++;
  }
  return violations;
}

PinSet
Sta::findGroupPathPins(const char *group_path_name)
{
  if (!(search_->havePathGroups()
        && search_->arrivalsValid())) {
    PathEndSeq path_ends = findPathEnds(// from, thrus, to, unconstrained
                                        nullptr, nullptr, nullptr, false,
                                        // corner, min_max, 
                                        nullptr, MinMaxAll::max(),
                                        // group_path_count, endpoint_path_count, unique_pins
                                        1, 1, false,
                                        -INF, INF, // slack_min, slack_max,
                                        false, // sort_by_slack
                                        nullptr, // group_names
                                        // setup, hold, recovery, removal, 
                                        true, true, true, true,
                                        // clk_gating_setup, clk_gating_hold
                                        true, true);
  }

  PathGroup *path_group = search_->findPathGroup(group_path_name,
						 MinMax::max());
  PinSet pins(network_);
  VertexPinCollector visitor(pins);
  visitPathGroupVertices(path_group, &visitor, this);
  return pins;
}

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

void
Sta::findRequireds()
{
  searchPreamble();
  search_->findAllArrivals();
  search_->findRequireds();
}

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

VertexPathIterator *
Sta::vertexPathIterator(Vertex *vertex,
			const RiseFall *rf,
			const PathAnalysisPt *path_ap)
{
  return new VertexPathIterator(vertex, rf, path_ap, this);
}

VertexPathIterator *
Sta::vertexPathIterator(Vertex *vertex,
			const RiseFall *rf,
			const MinMax *min_max)
{
  return new VertexPathIterator(vertex, rf, min_max, this);
}

Path *
Sta::vertexWorstArrivalPath(Vertex *vertex,
			    const MinMax *min_max)
{
  return vertexWorstArrivalPath(vertex, nullptr, min_max);
}

Path *
Sta::vertexWorstArrivalPath(Vertex *vertex,
			    const RiseFall *rf,
			    const MinMax *min_max)
{
  Path *worst_path = nullptr;
  Arrival worst_arrival = min_max->initValue();
  VertexPathIterator path_iter(vertex, rf, min_max, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    Arrival arrival = path->arrival();
    if (!path->tag(this)->isGenClkSrcPath()
	&& delayGreater(arrival, worst_arrival, min_max, this)) {
      worst_arrival = arrival;
      worst_path = path;
    }
  }
  return worst_path;
}

Path *
Sta::vertexWorstRequiredPath(Vertex *vertex,
                             const MinMax *min_max)
{
  return vertexWorstRequiredPath(vertex, nullptr, min_max);
}

Path *
Sta::vertexWorstRequiredPath(Vertex *vertex,
                             const RiseFall *rf,
                             const MinMax *min_max)
{
  Path *worst_path = nullptr;
  const MinMax *req_min_max = min_max->opposite();
  Arrival worst_req = req_min_max->initValue();
  VertexPathIterator path_iter(vertex, rf, min_max, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    const Required path_req = path->required();
    if (!path->tag(this)->isGenClkSrcPath()
	&& delayGreater(path_req, worst_req, req_min_max, this)) {
      worst_req = path_req;
      worst_path = path;
    }
  }
  return worst_path;
}

Path *
Sta::vertexWorstSlackPath(Vertex *vertex,
			  const RiseFall *rf,
			  const MinMax *min_max)
{
  Path *worst_path = nullptr;
  Slack min_slack = MinMax::min()->initValue();
  VertexPathIterator path_iter(vertex, rf, min_max, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    Slack slack = path->slack(this);
    if (!path->tag(this)->isGenClkSrcPath()
	&& delayLess(slack, min_slack, this)) {
      min_slack = slack;
      worst_path = path;
    }
  }
  return worst_path;
}

Path *
Sta::vertexWorstSlackPath(Vertex *vertex,
			  const MinMax *min_max)

{
  return vertexWorstSlackPath(vertex, nullptr, min_max);
}

Arrival
Sta::pinArrival(const Pin *pin,
                const RiseFall *rf,
                const MinMax *min_max)
{
  Vertex *vertex, *bidirect_vertex;
  graph_->pinVertices(pin, vertex, bidirect_vertex);
  Arrival arrival;
  if (vertex)
    arrival = vertexArrival(vertex, rf, clk_edge_wildcard, nullptr, min_max);
  if (bidirect_vertex) {
    Arrival arrival1 = vertexArrival(bidirect_vertex, rf, clk_edge_wildcard,
                                     nullptr, min_max);
    if (delayLess(arrival1, arrival, this))
      arrival = arrival1;
  }
  return arrival;
}

Arrival
Sta::vertexArrival(Vertex *vertex,
                   const MinMax *min_max)
{
  return vertexArrival(vertex, nullptr, clk_edge_wildcard, nullptr, min_max);
}

Arrival
Sta::vertexArrival(Vertex *vertex,
		   const RiseFall *rf,
		   const PathAnalysisPt *path_ap)
{
  return vertexArrival(vertex, rf, clk_edge_wildcard, path_ap, nullptr);
}

Arrival
Sta::vertexArrival(Vertex *vertex,
		   const RiseFall *rf,
		   const ClockEdge *clk_edge,
		   const PathAnalysisPt *path_ap,
                   const MinMax *min_max)
{
  searchPreamble();
  search_->findArrivals(vertex->level());
  if (min_max == nullptr)
    min_max = path_ap->pathMinMax();
  Arrival arrival = min_max->initValue();
  VertexPathIterator path_iter(vertex, rf, path_ap, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    const Arrival &path_arrival = path->arrival();
    ClkInfo *clk_info = path->clkInfo(search_);
    if ((clk_edge == clk_edge_wildcard
	 || clk_info->clkEdge() == clk_edge)
	&& !clk_info->isGenClkSrcPath()
	&& delayGreater(path->arrival(), arrival, min_max, this))
      arrival = path_arrival;
  }
  return arrival;
}

Required
Sta::vertexRequired(Vertex *vertex,
		    const MinMax *min_max)
{
  return vertexRequired(vertex, nullptr, clk_edge_wildcard, nullptr, min_max);
}

Required
Sta::vertexRequired(Vertex *vertex,
		    const RiseFall *rf,
		    const MinMax *min_max)
{
  return vertexRequired(vertex, rf, clk_edge_wildcard, nullptr, min_max);
}

Required
Sta::vertexRequired(Vertex *vertex,
		    const RiseFall *rf,
		    const PathAnalysisPt *path_ap)
{
  return vertexRequired(vertex, rf, clk_edge_wildcard, path_ap, nullptr);
}

Required
Sta::vertexRequired(Vertex *vertex,
		    const RiseFall *rf,
		    const ClockEdge *clk_edge,
		    const PathAnalysisPt *path_ap)
{
  return vertexRequired(vertex, rf, clk_edge, path_ap, nullptr);
}

Required
Sta::vertexRequired(Vertex *vertex,
		    const RiseFall *rf,
		    const ClockEdge *clk_edge,
		    const PathAnalysisPt *path_ap,
		    const MinMax *min_max)
{
  findRequired(vertex);
  const MinMax *req_min_max = min_max
    ? min_max->opposite()
    : path_ap->pathMinMax()->opposite();
  Required required = req_min_max->initValue();
  VertexPathIterator path_iter(vertex, rf, path_ap, min_max, this);
  while (path_iter.hasNext()) {
    const Path *path = path_iter.next();
    const Required path_required = path->required();
    if ((clk_edge == clk_edge_wildcard
	 || path->clkEdge(search_) == clk_edge)
	&& delayGreater(path_required, required, req_min_max, this))
      required = path_required;
  }
  return required;
}

Slack
Sta::netSlack(const Net *net,
	      const MinMax *min_max)
{
  ensureGraph();
  Slack slack = MinMax::min()->initValue();
  NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
  while (pin_iter->hasNext()) {
    const Pin *pin = pin_iter->next();
    if (network_->isLoad(pin)) {
      Vertex *vertex = graph_->pinLoadVertex(pin);
      Slack pin_slack = vertexSlack(vertex, min_max);
      if (delayLess(pin_slack, slack, this))
	slack = pin_slack;
    }
  }
  return slack;
}

Slack
Sta::pinSlack(const Pin *pin,
	      const MinMax *min_max)
{
  ensureGraph();
  Vertex *vertex, *bidirect_drvr_vertex;
  graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
  Slack slack = MinMax::min()->initValue();
  if (vertex)
    slack = vertexSlack(vertex, min_max);
  if (bidirect_drvr_vertex) {
    Slack slack1 = vertexSlack(bidirect_drvr_vertex, min_max);
    if (delayLess(slack1, slack, this))
      slack = slack1;
  }
  return slack;
}

Slack
Sta::pinSlack(const Pin *pin,
	      const RiseFall *rf,
	      const MinMax *min_max)
{
  ensureGraph();
  Vertex *vertex, *bidirect_drvr_vertex;
  graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
  Slack slack = MinMax::min()->initValue();
  if (vertex)
    slack = vertexSlack(vertex, rf, min_max);
  if (bidirect_drvr_vertex) {
    Slack slack1 = vertexSlack(bidirect_drvr_vertex, rf, min_max);
    if (delayLess(slack1, slack, this))
      slack = slack1;
  }
  return slack;
}

Slack
Sta::vertexSlack(Vertex *vertex,
		 const MinMax *min_max)
{
  findRequired(vertex);
  const MinMax *min = MinMax::min();
  Slack slack = min->initValue();
  VertexPathIterator path_iter(vertex, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    if (path->minMax(this) == min_max) {
      Slack path_slack = path->slack(this);
      if (delayLess(path_slack, slack, this))
	slack = path_slack;
    }
  }
  return slack;
}

Slack
Sta::vertexSlack(Vertex *vertex,
		 const RiseFall *rf,
		 const MinMax *min_max)
{
  findRequired(vertex);
  Slack slack = MinMax::min()->initValue();
  VertexPathIterator path_iter(vertex, rf, min_max, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    Slack path_slack = path->slack(this);
    if (delayLess(path_slack, slack, this))
      slack = path_slack;
  }
  return slack;
}

Slack
Sta::vertexSlack(Vertex *vertex,
		 const RiseFall *rf,
		 const PathAnalysisPt *path_ap)
{
  findRequired(vertex);
  return vertexSlack1(vertex, rf, clk_edge_wildcard, path_ap);
}

Slack
Sta::vertexSlack(Vertex *vertex,
		 const RiseFall *rf,
		 const ClockEdge *clk_edge,
		 const PathAnalysisPt *path_ap)
{
  findRequired(vertex);
  return vertexSlack1(vertex, rf, clk_edge, path_ap);
}

Slack
Sta::vertexSlack1(Vertex *vertex,
		  const RiseFall *rf,
		  const ClockEdge *clk_edge,
		  const PathAnalysisPt *path_ap)
{
  const MinMax *min = MinMax::min();
  Slack slack = min->initValue();
  VertexPathIterator path_iter(vertex, rf, path_ap, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    Slack path_slack = path->slack(this);
    if ((clk_edge == clk_edge_wildcard
	 || path->clkEdge(search_) == clk_edge)
	&& delayLess(path_slack, slack, this))
      slack = path_slack;
  }
  return slack;
}

void
Sta::vertexSlacks(Vertex *vertex,
		  Slack (&slacks)[RiseFall::index_count][MinMax::index_count])
{
  findRequired(vertex);
  for (int rf_index : RiseFall::rangeIndex()) {
    for (const MinMax *min_max : MinMax::range()) {
      slacks[rf_index][min_max->index()] = MinMax::min()->initValue();
    }
  }
  VertexPathIterator path_iter(vertex, this);
  while (path_iter.hasNext()) {
    Path *path = path_iter.next();
    Slack path_slack = path->slack(this);
    int rf_index = path->rfIndex(this);
    int mm_index = path->minMax(this)->index();
    if (delayLess(path_slack, slacks[rf_index][mm_index], this))
      slacks[rf_index][mm_index] = path_slack;
  }
}

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

class MinPeriodEndVisitor : public PathEndVisitor
{
public:
  MinPeriodEndVisitor(const Clock *clk,
                      bool include_port_paths,
                      StaState *sta);
  MinPeriodEndVisitor(const MinPeriodEndVisitor &) = default;
  virtual PathEndVisitor *copy() const;
  virtual void visit(PathEnd *path_end);
  float minPeriod() const { return min_period_; }

private:
  bool pathIsFromInputPort(PathEnd *path_end);

  const Clock *clk_;
  bool include_port_paths_;
  StaState *sta_;
  float min_period_;
};

MinPeriodEndVisitor::MinPeriodEndVisitor(const Clock *clk,
                                         bool include_port_paths,
                                         StaState *sta) :
  clk_(clk),
  include_port_paths_(include_port_paths),
  sta_(sta),
  min_period_(0)
{
}

PathEndVisitor *
MinPeriodEndVisitor::copy() const
{
  return new MinPeriodEndVisitor(*this);
}

void
MinPeriodEndVisitor::visit(PathEnd *path_end)
{
  Network *network = sta_->network();
  Path *path = path_end->path();
  const ClockEdge *src_edge = path_end->sourceClkEdge(sta_);
  const ClockEdge *tgt_edge = path_end->targetClkEdge(sta_);
  PathEnd::Type end_type = path_end->type();
  if ((end_type == PathEnd::Type::check
       || end_type == PathEnd::Type::output_delay)
      && path->minMax(sta_) == MinMax::max()
      && src_edge->clock() == clk_
      && tgt_edge->clock() == clk_
      // Only consider rise/rise and fall/fall paths.
      && src_edge->transition() == tgt_edge->transition()
      && path_end->multiCyclePath() == nullptr
      && (include_port_paths_
          || !(network->isTopLevelPort(path->pin(sta_))
               || pathIsFromInputPort(path_end)))) {
    Slack slack = path_end->slack(sta_);
    float period = clk_->period() - delayAsFloat(slack);
    min_period_ = max(min_period_, period);
  }
}

bool
MinPeriodEndVisitor::pathIsFromInputPort(PathEnd *path_end)
{
  PathExpanded expanded(path_end->path(), sta_);
  const Path *start = expanded.startPath();
  Graph *graph = sta_->graph();
  const Pin *first_pin = start->pin(graph);
  Network *network = sta_->network();
  return network->isTopLevelPort(first_pin);
}

float
Sta::findClkMinPeriod(const Clock *clk,
                      bool include_port_paths)
{
  searchPreamble();
  search_->findArrivals();
  VisitPathEnds visit_ends(this);
  MinPeriodEndVisitor min_period_visitor(clk, include_port_paths, this);
  for (Vertex *vertex : *search_->endpoints()) {
    findRequired(vertex);
    visit_ends.visitPathEnds(vertex, &min_period_visitor);
  }
  return min_period_visitor.minPeriod();
}

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

void
Sta::findRequired(Vertex *vertex)
{
  searchPreamble();
  search_->findAllArrivals();
  search_->findRequireds(vertex->level());
  if (variables_->crprEnabled()
      && search_->crprPathPruningEnabled()
      && !search_->crprApproxMissingRequireds()
      // Clocks invariably have requireds that are pruned but it isn't
      // worth finding arrivals and requireds all over again for
      // the entire fanout of the clock.
      && !search_->isClock(vertex)) {
    // Invalidate arrivals and requireds and disable
    // path pruning on fanout vertices with DFS.
    int fanout = 0;
    disableFanoutCrprPruning(vertex, fanout);
    debugPrint(debug_, "search", 1, "resurrect pruned required %s fanout %d",
               vertex->to_string(this).c_str(),
               fanout);
    // Find fanout arrivals and requireds with pruning disabled.
    search_->findArrivals();
    search_->findRequireds(vertex->level());
  }
}

void
Sta::disableFanoutCrprPruning(Vertex *vertex,
			      int &fanout)
{
  if (!vertex->crprPathPruningDisabled()) {
    search_->arrivalInvalid(vertex);
    search_->requiredInvalid(vertex);
    vertex->setCrprPathPruningDisabled(true);
    fanout++;
    SearchPred *pred = search_->searchAdj();
    VertexOutEdgeIterator edge_iter(vertex, graph_);
    while (edge_iter.hasNext()) {
      Edge *edge = edge_iter.next();
      Vertex *to_vertex = edge->to(graph_);
      if (pred->searchThru(edge)
	  && pred->searchTo(to_vertex))
	disableFanoutCrprPruning(to_vertex, fanout);
    }
  }
}

Slack
Sta::totalNegativeSlack(const MinMax *min_max)
{
  searchPreamble();
  return search_->totalNegativeSlack(min_max);
}

Slack
Sta::totalNegativeSlack(const Corner *corner,
			const MinMax *min_max)
{
  searchPreamble();
  return search_->totalNegativeSlack(corner, min_max);
}

Slack
Sta::worstSlack(const MinMax *min_max)
{
  searchPreamble();
  Slack worst_slack;
  Vertex *worst_vertex;
  search_->worstSlack(min_max, worst_slack, worst_vertex);
  return worst_slack;
}

void
Sta::worstSlack(const MinMax *min_max,
		// Return values.
		Slack &worst_slack,
		Vertex *&worst_vertex)
{
  searchPreamble();
  search_->worstSlack(min_max, worst_slack, worst_vertex);
}

void
Sta::worstSlack(const Corner *corner,
		const MinMax *min_max,
		// Return values.
		Slack &worst_slack,
		Vertex *&worst_vertex)
{
  searchPreamble();
  return search_->worstSlack(corner, min_max, worst_slack, worst_vertex);
}

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

string
Sta::reportDelayCalc(Edge *edge,
		     TimingArc *arc,
		     const Corner *corner,
		     const MinMax *min_max,
		     int digits)
{
  findDelays();
  return graph_delay_calc_->reportDelayCalc(edge, arc, corner, min_max, digits);
}

void
Sta::setArcDelayCalc(const char *delay_calc_name)
{
  delete arc_delay_calc_;
  arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_);
  // Update pointers to arc_delay_calc.
  updateComponentsState();
  delaysInvalid();
}

void
Sta::findDelays(Vertex *to_vertex)
{
  delayCalcPreamble();
  graph_delay_calc_->findDelays(to_vertex->level());
}

void
Sta::findDelays()
{
  delayCalcPreamble();
  graph_delay_calc_->findDelays(levelize_->maxLevel());
}

void
Sta::findDelays(Level level)
{
  delayCalcPreamble();
  graph_delay_calc_->findDelays(level);
}

void
Sta::delayCalcPreamble()
{
  ensureLibLinked();
  ensureClkNetwork();
}

void
Sta::setIncrementalDelayTolerance(float tol)
{
  graph_delay_calc_->setIncrementalDelayTolerance(tol);
}

ArcDelay
Sta::arcDelay(Edge *edge,
	      TimingArc *arc,
	      const DcalcAnalysisPt *dcalc_ap)
{
  findDelays(edge->to(graph_));
  return graph_->arcDelay(edge, arc, dcalc_ap->index());
}

bool
Sta::arcDelayAnnotated(Edge *edge,
		       TimingArc *arc,
		       DcalcAnalysisPt *dcalc_ap)
{
  return graph_->arcDelayAnnotated(edge, arc, dcalc_ap->index());
}

void
Sta::setArcDelayAnnotated(Edge *edge,
			  TimingArc *arc,
			  DcalcAnalysisPt *dcalc_ap,
			  bool annotated)
{
  graph_->setArcDelayAnnotated(edge, arc, dcalc_ap->index(), annotated);
  Vertex *to = edge->to(graph_);
  search_->arrivalInvalid(to);
  search_->requiredInvalid(edge->from(graph_));
  if (!annotated)
    graph_delay_calc_->delayInvalid(to);
}

Slew
Sta::vertexSlew(Vertex *vertex,
		const RiseFall *rf,
                const Corner *corner,
                const MinMax *min_max)
{
  findDelays(vertex);
  const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
  return graph_->slew(vertex, rf, dcalc_ap->index());
}

Slew
Sta::vertexSlew(Vertex *vertex,
		const RiseFall *rf,
		const DcalcAnalysisPt *dcalc_ap)
{
  findDelays(vertex);
  return graph_->slew(vertex, rf, dcalc_ap->index());
}

Slew
Sta::vertexSlew(Vertex *vertex,
		const RiseFall *rf,
		const MinMax *min_max)
{
  findDelays(vertex);
  Slew mm_slew = min_max->initValue();
  for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) {
    Slew slew = graph_->slew(vertex, rf, dcalc_ap->index());
    if (delayGreater(slew, mm_slew, min_max, this))
      mm_slew = slew;
  }
  return mm_slew;
}

Slew
Sta::vertexSlew(Vertex *vertex,
                const MinMax *min_max)
{
  findDelays(vertex);
  Slew mm_slew = min_max->initValue();
  for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) {
    for (const RiseFall *rf : RiseFall::range()) {
      Slew slew = graph_->slew(vertex, rf, dcalc_ap->index());
      if (delayGreater(slew, mm_slew, min_max, this))
        mm_slew = slew;
    }
  }
  return mm_slew;
}

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

// Make sure the network has been read and linked.
// Throwing an error means the caller doesn't have to check the result.
Network *
Sta::ensureLinked()
{
  if (network_ == nullptr || !network_->isLinked())
    report_->error(1570, "No network has been linked.");
  // Return cmd/sdc network.
  return cmd_network_;
}

Network *
Sta::ensureLibLinked()
{
  if (network_ == nullptr || !network_->isLinked())
    report_->error(1571, "No network has been linked.");
  // OpenROAD db is inherently linked but may not have associated
  // liberty files so check for them here.
  if (network_->defaultLibertyLibrary() == nullptr)
    report_->error(2141, "No liberty libraries found.");
  // Return cmd/sdc network.
  return cmd_network_;
}

Graph *
Sta::ensureGraph()
{
  ensureLibLinked();
  if (graph_ == nullptr && network_) {
    makeGraph();
    // Update pointers to graph.
    updateComponentsState();
  }
  return graph_;
}

void
Sta::makeGraph()
{
  graph_ = new Graph(this, 2, corners_->dcalcAnalysisPtCount());
  graph_->makeGraph();
}

void
Sta::ensureLevelized()
{
  ensureGraph();
  ensureGraphSdcAnnotated();
  // Need constant propagation before levelization to know edges that
  // are disabled by constants.
  sim_->ensureConstantsPropagated();
  levelize_->ensureLevelized();
}

void
Sta::updateGeneratedClks()
{
  if (update_genclks_) {
    ensureLevelized();
    bool gen_clk_changed = true;
    while (gen_clk_changed) {
      gen_clk_changed = false;
      for (Clock *clk : sdc_->clks()) {
	if (clk->isGenerated() && !clk->waveformValid()) {
	  search_->genclks()->ensureMaster(clk);
	  Clock *master_clk = clk->masterClk();
	  if (master_clk && master_clk->waveformValid()) {
	    clk->generate(master_clk);
	    gen_clk_changed = true;
	  }
	}
      }
    }
  }
  update_genclks_ = false;
}

Level
Sta::vertexLevel(Vertex *vertex)
{
  ensureLevelized();
  return vertex->level();
}

GraphLoopSeq &
Sta::graphLoops()
{
  ensureLevelized();
  return levelize_->loops();
}

PathAnalysisPt *
Sta::pathAnalysisPt(Path *path)
{
  return path->pathAnalysisPt(this);
}

DcalcAnalysisPt *
Sta::pathDcalcAnalysisPt(Path *path)
{
  return pathAnalysisPt(path)->dcalcAnalysisPt();
}

Vertex *
Sta::maxPathCountVertex() const
{
  Vertex *max_vertex = nullptr;
  int max_count = 0;
  VertexIterator vertex_iter(graph_);
  while (vertex_iter.hasNext()) {
    Vertex *vertex = vertex_iter.next();
    int count = vertexPathCount(vertex);
    if (count > max_count) {
      max_count = count;
      max_vertex = vertex;
    }
  }
  return max_vertex;
}

int
Sta::vertexPathCount(Vertex  *vertex) const
{
  TagGroup *tag_group = search_->tagGroup(vertex);
  if (tag_group)
    return tag_group->pathCount();
  else
    return 0;
}

int
Sta::pathCount() const
{
  int count = 0;
  VertexIterator vertex_iter(graph_);
  while (vertex_iter.hasNext()) {
    Vertex *vertex = vertex_iter.next();
    count += vertexPathCount(vertex);
  }
  return count;
}

TagIndex
Sta::tagCount() const
{
  return search_->tagCount();
}

TagGroupIndex
Sta::tagGroupCount() const
{
  return search_->tagGroupCount();
}

int
Sta::clkInfoCount() const
{
  return search_->clkInfoCount();
}

void
Sta::setArcDelay(Edge *edge,
		 TimingArc *arc,
		 const Corner *corner,
		 const MinMaxAll *min_max,
		 ArcDelay delay)
{
  ensureGraph();
  for (const MinMax *mm : min_max->range()) {
    const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm);
    DcalcAPIndex ap_index = dcalc_ap->index();
    graph_->setArcDelay(edge, arc, ap_index, delay);
    // Don't let delay calculation clobber the value.
    graph_->setArcDelayAnnotated(edge, arc, ap_index, true);
  }
  if (edge->role()->isTimingCheck())
    search_->requiredInvalid(edge->to(graph_));
  else {
    search_->arrivalInvalid(edge->to(graph_));
    search_->requiredInvalid(edge->from(graph_));
  }
}

void
Sta::setAnnotatedSlew(Vertex *vertex,
		      const Corner *corner,
		      const MinMaxAll *min_max,
		      const RiseFallBoth *rf,
		      float slew)
{
  ensureGraph();
  for (const MinMax *mm : min_max->range()) {
    const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm);
    DcalcAPIndex ap_index = dcalc_ap->index();
    for (const RiseFall *rf1 : rf->range()) {
      graph_->setSlew(vertex, rf1, ap_index, slew);
      // Don't let delay calculation clobber the value.
      vertex->setSlewAnnotated(true, rf1, ap_index);
    }
  }
  graph_delay_calc_->delayInvalid(vertex);
}

void
Sta::writeSdf(const char *filename,
	      const Corner *corner,
	      char divider,
	      bool include_typ,
              int digits,
	      bool gzip,
	      bool no_timestamp,
	      bool no_version)
{
  findDelays();
  sta::writeSdf(filename, corner, divider, include_typ, digits, gzip,
                no_timestamp, no_version, this);
}

void
Sta::removeDelaySlewAnnotations()
{
  if (graph_) {
    graph_->removeDelaySlewAnnotations();
    delaysInvalid();
  }
}

LogicValue
Sta::simLogicValue(const Pin *pin)
{
  ensureGraph();
  sim_->ensureConstantsPropagated();
  return sim_->logicValue(pin);
}

void
Sta::findLogicConstants()
{
  ensureGraph();
  sim_->findLogicConstants();
}

void
Sta::clearLogicConstants()
{
  sim_->clear();
}

void
Sta::setPortExtPinCap(const Port *port,
		      const RiseFallBoth *rf,
                      const Corner *corner,
		      const MinMaxAll *min_max,
		      float cap)
{
  for (const RiseFall *rf1 : rf->range()) {
    for (const MinMax *mm : min_max->range()) {
      if (corner == nullptr) {
        for (const Corner *corner : *corners_)
          sdc_->setPortExtPinCap(port, rf1, corner, mm, cap);
      }
      else
        sdc_->setPortExtPinCap(port, rf1, corner, mm, cap);
    }
  }
  delaysInvalidFromFanin(port);
}

void
Sta::portExtCaps(const Port *port,
                 const Corner *corner,
                 const MinMax *min_max,
                 float &pin_cap,
                 float &wire_cap,
                 int &fanout)
{
  bool pin_exists = false;
  bool wire_exists = false;
  bool fanout_exists = false;
  pin_cap = min_max->initValue();
  wire_cap = min_max->initValue();
  fanout = min_max->initValueInt();
  for (const RiseFall *rf : RiseFall::range()) {
    float pin_cap1, wire_cap1;
    int fanout1;
    bool pin_exists1, wire_exists1, fanout_exists1;
    sdc_->portExtCap(port, rf, corner, min_max,
                     pin_cap1, pin_exists1,
                     wire_cap1, wire_exists1,
                     fanout1, fanout_exists1);
    if (pin_exists1) {
      pin_cap = min_max->minMax(pin_cap, pin_cap1);
      pin_exists = true;
    }
    if (wire_exists1) {
      wire_cap = min_max->minMax(wire_cap, wire_cap1);
      wire_exists = true;
    }
    if (fanout_exists1) {
      fanout = min_max->minMax(fanout, fanout1);
      fanout_exists = true;
    }
  }
  if (!pin_exists)
    pin_cap = 0.0;
  if (!wire_exists)
    wire_cap = 0.0;
  if (!fanout_exists)
    fanout = 0;
}

void
Sta::setPortExtWireCap(const Port *port,
		       bool subtract_pin_cap,
		       const RiseFallBoth *rf,
                       const Corner *corner,
		       const MinMaxAll *min_max,
		       float cap)
{
  for (const RiseFall *rf1 : rf->range()) {
    for (const MinMax *mm : min_max->range()) {
      if (corner == nullptr) {
        for (const Corner *corner : *corners_)
          sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap);
      }
      else
        sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap);
    }
  }
  delaysInvalidFromFanin(port);
}

void
Sta::removeNetLoadCaps() const
{
  sdc_->removeNetLoadCaps();
  delaysInvalid();
}

void
Sta::setPortExtFanout(const Port *port,
		      int fanout,
                      const Corner *corner,
		      const MinMaxAll *min_max)
{
  for (const MinMax *mm : min_max->range()) {
    if (corner == nullptr) {
      for (const Corner *corner : *corners_)
        sdc_->setPortExtFanout(port, corner, mm, fanout);
    }
    else
      sdc_->setPortExtFanout(port, corner, mm, fanout);
  }
  delaysInvalidFromFanin(port);
}

void
Sta::setNetWireCap(const Net *net,
		   bool subtract_pin_cap,
		   const Corner *corner,
		   const MinMaxAll *min_max,
		   float cap)
{
  for (const MinMax *mm : min_max->range()) {
    if (corner == nullptr) {
      for (const Corner *corner : *corners_)
        sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap);
    }
    else
      sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap);
  }
  delaysInvalidFromFanin(net);
}

void
Sta::connectedCap(const Pin *drvr_pin,
		  const RiseFall *rf,
		  const Corner *corner,
		  const MinMax *min_max,
		  float &pin_cap,
		  float &wire_cap) const
{
  const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
  graph_delay_calc_->loadCap(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap);
}

void
Sta::connectedCap(const Net *net,
		  Corner *corner,
		  const MinMax *min_max,
		  float &pin_cap,
		  float &wire_cap) const
{
  const Pin *drvr_pin = findNetParasiticDrvrPin(net);
  if (drvr_pin) {
    pin_cap = min_max->initValue();
    wire_cap = min_max->initValue();
    for (const Corner *corner : makeCornerSeq(corner)) {
      for (const RiseFall *rf : RiseFall::range()) {
        float pin_cap1, wire_cap1;
        connectedCap(drvr_pin, rf, corner, min_max, pin_cap1, wire_cap1);
        pin_cap = min_max->minMax(pin_cap, pin_cap1);
        wire_cap = min_max->minMax(wire_cap, wire_cap1);
      }
    }
  }
  else {
    pin_cap = 0.0;
    wire_cap = 0.0;
  }
}

CornerSeq
Sta::makeCornerSeq(Corner *corner) const
{
  CornerSeq corners;
  if (corner)
    corners.push_back(corner);
  else
    corners = corners_->corners();
  return corners;
}

float
Sta::capacitance(const LibertyPort *port,
                 Corner *corner,
                 const MinMax *min_max)
{
  OperatingConditions *op_cond = operatingConditions(min_max);
  float cap = min_max->initValue();
  for (const Corner *corner : makeCornerSeq(corner)) {
    const LibertyPort *corner_port = port->cornerPort(corner, min_max);
    for (const RiseFall *rf : RiseFall::range())
      cap = min_max->minMax(cap, corner_port->capacitance(rf, min_max, op_cond, op_cond));
  }
  return cap;
}

// Look for a driver to find a parasitic if the net has one.
// Settle for a load pin if there are no drivers.
const Pin *
Sta::findNetParasiticDrvrPin(const Net *net) const
{
  const Pin *load_pin = nullptr;
  NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
  while (pin_iter->hasNext()) {
    const Pin *pin = pin_iter->next();
    if (network_->isDriver(pin)) {
      delete pin_iter;
      return pin;
    }
    if (network_->isLoad(pin))
      load_pin = pin;
  }
  delete pin_iter;
  return load_pin;
}

void
Sta::setResistance(const Net *net,
		   const MinMaxAll *min_max,
		   float res)
{
  sdc_->setResistance(net, min_max, res);
}

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

bool
Sta::readSpef(const char *filename,
	      Instance *instance,
              const Corner *corner,
	      const MinMaxAll *min_max,
	      bool pin_cap_included,
	      bool keep_coupling_caps,
	      float coupling_cap_factor,
	      bool reduce)
{
  ensureLibLinked();
  setParasiticAnalysisPts(corner != nullptr);
  const MinMax *ap_min_max = (min_max == MinMaxAll::all())
    ? MinMax::max()
    : min_max->asMinMax();
  const Corner *ap_corner = corner ? corner : corners_->corners()[0];
  ParasiticAnalysisPt *ap = ap_corner->findParasiticAnalysisPt(ap_min_max);
  bool success = readSpefFile(filename, instance, ap,
			      pin_cap_included, keep_coupling_caps,
                              coupling_cap_factor, reduce,
			      corner, min_max, this);
  delaysInvalid();
  return success;
}

void
Sta::setParasiticAnalysisPts(bool per_corner)
{
  if (per_corner != parasitics_per_corner_) {
    deleteParasitics();
    parasitics_per_corner_ = per_corner;
    makeParasiticAnalysisPts();
  }
}

void
Sta::makeParasiticAnalysisPts()
{
  corners_->makeParasiticAnalysisPts(parasitics_per_corner_);
}

void
Sta::reportParasiticAnnotation(bool report_unannotated,
                               const Corner *corner)
{
  ensureLibLinked();
  ensureGraph();
  sta::reportParasiticAnnotation(report_unannotated, corner, this);
}

void
Sta::findPiElmore(Pin *drvr_pin,
		  const RiseFall *rf,
		  const MinMax *min_max,
		  float &c2,
		  float &rpi,
		  float &c1,
		  bool &exists) const
{
  Corner *corner = cmd_corner_;
  const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max);
  Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
  if (pi_elmore) {
    parasitics_->piModel(pi_elmore, c2, rpi, c1);
    exists = true;
  }
  else
    exists = false;
}

void
Sta::makePiElmore(Pin *drvr_pin,
		  const RiseFall *rf,
		  const MinMaxAll *min_max,
		  float c2,
		  float rpi,
		  float c1)
{
  const Corner *corner = cmd_corner_;
  for (const MinMax *mm : min_max->range()) {
    ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm);
    parasitics_->makePiElmore(drvr_pin, rf, ap, c2, rpi, c1);
  }
  delaysInvalidFrom(drvr_pin);
}

void
Sta::findElmore(Pin *drvr_pin,
		Pin *load_pin,
		const RiseFall *rf,
		const MinMax *min_max,
		float &elmore,
		bool &exists) const
{
  Corner *corner = cmd_corner_;
  const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max);
  Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
  if (pi_elmore)
    parasitics_->findElmore(pi_elmore, load_pin, elmore, exists);
  else
    exists = false;
}

void
Sta::setElmore(Pin *drvr_pin,
	       Pin *load_pin,
	       const RiseFall *rf,
	       const MinMaxAll *min_max,
	       float elmore)
{
  const Corner *corner = cmd_corner_;
  for (const MinMax *mm : min_max->range()) {
    const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm);
    Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
    if (pi_elmore)
      parasitics_->setElmore(pi_elmore, load_pin, elmore);
  }
  delaysInvalidFrom(drvr_pin);
}

void
Sta::deleteParasitics()
{
  parasitics_->deleteParasitics();
  delaysInvalid();
}

Parasitic *
Sta::makeParasiticNetwork(const Net *net,
                          bool includes_pin_caps,
                          const ParasiticAnalysisPt *ap)
{
  Parasitic *parasitic = parasitics_->makeParasiticNetwork(net, includes_pin_caps, ap);
  delaysInvalidFromFanin(const_cast<Net*>(net));
  return parasitic;
}

////////////////////////////////////////////////////////////////
//
// Network edit commands.
//
// This implementation calls Sta before/after methods to
// update the Sta components.
// A different implementation may let the network edits
// call the before/after methods implicitly so these functions
// should not (Verific).
//
////////////////////////////////////////////////////////////////

NetworkEdit *
Sta::networkCmdEdit()
{
  return dynamic_cast<NetworkEdit*>(cmd_network_);
}

Instance *
Sta::makeInstance(const char *name,
		  LibertyCell *cell,
		  Instance *parent)
{
  NetworkEdit *network = networkCmdEdit();
  Instance *inst = network->makeInstance(cell, name, parent);
  network->makePins(inst);
  makeInstanceAfter(inst);
  return inst;
}

void
Sta::deleteInstance(Instance *inst)
{
  NetworkEdit *network = networkCmdEdit();
  deleteInstanceBefore(inst);
  network->deleteInstance(inst);
}

void
Sta::replaceCell(Instance *inst,
		 LibertyCell *to_lib_cell)
{
  Cell *to_cell = network_->cell(to_lib_cell);
  replaceCell(inst, to_cell, to_lib_cell);
}

void
Sta::replaceCell(Instance *inst,
		 Cell *to_cell)
{
  LibertyCell *to_lib_cell = network_->libertyCell(to_cell);
  replaceCell(inst, to_cell, to_lib_cell);
}

void
Sta::replaceCell(Instance *inst,
		 Cell *to_cell,
		 LibertyCell *to_lib_cell)
{
  NetworkEdit *network = networkCmdEdit();
  LibertyCell *from_lib_cell = network->libertyCell(inst);
  if (sta::equivCells(from_lib_cell, to_lib_cell)) {
    replaceEquivCellBefore(inst, to_lib_cell);
    network->replaceCell(inst, to_cell);
    replaceEquivCellAfter(inst);
  }
  else {
    replaceCellBefore(inst, to_lib_cell);
    network->replaceCell(inst, to_cell);
    replaceCellAfter(inst);
  }
}

Net *
Sta::makeNet(const char *name,
	     Instance *parent)
{
  NetworkEdit *network = networkCmdEdit();
  Net *net = network->makeNet(name, parent);
  // Sta notification unnecessary.
  return net;
}

void
Sta::deleteNet(Net *net)
{
  NetworkEdit *network = networkCmdEdit();
  deleteNetBefore(net);
  network->deleteNet(net);
}

void
Sta::connectPin(Instance *inst,
		Port *port,
		Net *net)
{
  NetworkEdit *network = networkCmdEdit();
  Pin *pin = network->connect(inst, port, net);
  connectPinAfter(pin);
}

void
Sta::connectPin(Instance *inst,
		LibertyPort *port,
		Net *net)
{
  NetworkEdit *network = networkCmdEdit();
  Pin *pin = network->connect(inst, port, net);
  connectPinAfter(pin);
}

void
Sta::disconnectPin(Pin *pin)
{
  NetworkEdit *network = networkCmdEdit();
  disconnectPinBefore(pin);
  network->disconnectPin(pin);
}

void
Sta::makePortPin(const char *port_name,
                 PortDirection *dir)
{
  ensureLinked();
  NetworkReader *network = dynamic_cast<NetworkReader*>(network_);
  Instance *top_inst = network->topInstance();
  Cell *top_cell = network->cell(top_inst);
  Port *port = network->makePort(top_cell, port_name);
  network->setDirection(port, dir);
  Pin *pin = network->makePin(top_inst, port, nullptr);
  makePortPinAfter(pin);
}
 
////////////////////////////////////////////////////////////////
//
// Network edit before/after methods.
//
////////////////////////////////////////////////////////////////

void
Sta::makeInstanceAfter(const Instance *inst)
{
  debugPrint(debug_, "network_edit", 1, "make instance %s",
             sdc_network_->pathName(inst));
  if (graph_) {
    LibertyCell *lib_cell = network_->libertyCell(inst);
    if (lib_cell) {
      // Make vertices and internal instance edges.
      LibertyCellPortBitIterator port_iter(lib_cell);
      while (port_iter.hasNext()) {
        LibertyPort *lib_port = port_iter.next();
        Pin *pin = network_->findPin(inst, lib_port);
        if (pin) {
          Vertex *vertex, *bidir_drvr_vertex;
          graph_->makePinVertices(pin, vertex, bidir_drvr_vertex);

        }
      }
      graph_->makeInstanceEdges(inst);
    }
  }
}

void
Sta::makePortPinAfter(Pin *pin)
{
  if (graph_) {
    Vertex *vertex, *bidir_drvr_vertex;
    graph_->makePinVertices(pin, vertex, bidir_drvr_vertex);
  }
}

void
Sta::replaceEquivCellBefore(const Instance *inst,
			    const LibertyCell *to_cell)
{
  if (graph_) {
    InstancePinIterator *pin_iter = network_->pinIterator(inst);
    while (pin_iter->hasNext()) {
      Pin *pin = pin_iter->next();
      LibertyPort *port = network_->libertyPort(pin);
      if (port) {
        if (port->direction()->isAnyInput()) {
          Vertex *vertex = graph_->pinLoadVertex(pin);
          replaceCellPinInvalidate(port, vertex, to_cell);

          // Replace the timing arc sets in the graph edges.
          VertexOutEdgeIterator edge_iter(vertex, graph_);
          while (edge_iter.hasNext()) {
            Edge *edge = edge_iter.next();
            Vertex *to_vertex = edge->to(graph_);
            if (network_->instance(to_vertex->pin()) == inst) {
              TimingArcSet *from_set = edge->timingArcSet();
              // Find corresponding timing arc set.
              TimingArcSet *to_set = to_cell->findTimingArcSet(from_set);
              if (to_set)
                edge->setTimingArcSet(to_set);
              else
                report_->critical(1553, "corresponding timing arc set not found in equiv cells");
            }
          }
        }
        else {
          // Force delay calculation on output pins.
          Vertex *vertex = graph_->pinDrvrVertex(pin);
          graph_delay_calc_->delayInvalid(vertex);
        }
      }
    }
    delete pin_iter;
  }
}

void
Sta::replaceEquivCellAfter(const Instance *inst)
{
  if (graph_) {
    InstancePinIterator *pin_iter = network_->pinIterator(inst);
    while (pin_iter->hasNext()) {
      Pin *pin = pin_iter->next();
      if (network_->direction(pin)->isAnyInput())
	parasitics_->loadPinCapacitanceChanged(pin);
    }
    delete pin_iter;
  }
}

void
Sta::replaceCellPinInvalidate(const LibertyPort *from_port,
			      Vertex *vertex,
			      const LibertyCell *to_cell)
{
  LibertyPort *to_port = to_cell->findLibertyPort(from_port->name());
  if (to_port == nullptr
      || (!libertyPortCapsEqual(to_port, from_port)
          // If this is an ideal clock pin, do not invalidate
          // arrivals and delay calc on the clock pin driver.
          && !(to_port->isClock()
               && idealClockMode())))
    // Input port capacitance changed, so invalidate delay
    // calculation from input driver.
    delaysInvalidFromFanin(vertex);
  else
    delaysInvalidFrom(vertex);
}

bool
Sta::idealClockMode()
{
  for (Clock *clk : sdc_->clks()) {
    if (clk->isPropagated())
      return false;
  }
  return true;
}

static bool
libertyPortCapsEqual(const LibertyPort *port1,
		     const LibertyPort *port2)
{
  return port1->capacitance(RiseFall::rise(), MinMax::min())
    == port2->capacitance(RiseFall::rise(), MinMax::min())
    && port1->capacitance(RiseFall::rise(), MinMax::max())
    == port2->capacitance(RiseFall::rise(), MinMax::max())
    && port1->capacitance(RiseFall::fall(), MinMax::min())
    == port2->capacitance(RiseFall::fall(), MinMax::min())
    && port1->capacitance(RiseFall::fall(), MinMax::max())
    == port2->capacitance(RiseFall::fall(), MinMax::max());
}

void
Sta::replaceCellBefore(const Instance *inst,
		       const LibertyCell *to_cell)
{
  if (graph_) {
    // Delete all graph edges between instance pins.
    InstancePinIterator *pin_iter = network_->pinIterator(inst);
    while (pin_iter->hasNext()) {
      Pin *pin = pin_iter->next();
      LibertyPort *port = network_->libertyPort(pin);
      if (port->direction()->isAnyInput()) {
	Vertex *vertex = graph_->pinLoadVertex(pin);
	replaceCellPinInvalidate(port, vertex, to_cell);

	VertexOutEdgeIterator edge_iter(vertex, graph_);
	while (edge_iter.hasNext()) {
	  Edge *edge = edge_iter.next();
	  Vertex *to_vertex = edge->to(graph_);
	  if (network_->instance(to_vertex->pin()) == inst)
	    deleteEdge(edge);
	}
      }
    }
    delete pin_iter;
  }
}

void
Sta::replaceCellAfter(const Instance *inst)
{
  if (graph_) {
    graph_->makeInstanceEdges(inst);
    InstancePinIterator *pin_iter = network_->pinIterator(inst);
    while (pin_iter->hasNext()) {
      Pin *pin = pin_iter->next();
      sim_->pinSetFuncAfter(pin);
      if (network_->direction(pin)->isAnyInput())
	parasitics_->loadPinCapacitanceChanged(pin);
    }
    delete pin_iter;
  }
}

void
Sta::connectPinAfter(const Pin *pin)
{
  debugPrint(debug_, "network_edit", 1, "connect %s to %s",
             sdc_network_->pathName(pin),
             sdc_network_->pathName(network_->net(pin)));
  if (graph_) {
    if (network_->isHierarchical(pin)) {
      graph_->makeWireEdgesThruPin(pin);
      EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
      while (edge_iter.hasNext()) {
	Edge *edge = edge_iter.next();
	if (edge->role()->isWire()) {
	  connectDrvrPinAfter(edge->from(graph_));
	  connectLoadPinAfter(edge->to(graph_));
	}
      }
    }
    else {
      Vertex *vertex, *bidir_drvr_vertex;
      graph_->pinVertices(pin, vertex, bidir_drvr_vertex);
      if (vertex) {
        search_->arrivalInvalid(vertex);
        search_->requiredInvalid(vertex);
        if (bidir_drvr_vertex) {
          search_->arrivalInvalid(bidir_drvr_vertex);
          search_->requiredInvalid(bidir_drvr_vertex);
        }

        // Make interconnect edges from/to pin.
        if (network_->isDriver(pin)) {
          graph_->makeWireEdgesFromPin(pin);
          connectDrvrPinAfter(bidir_drvr_vertex ? bidir_drvr_vertex : vertex);
        }
        // Note that a bidirect is both a driver and a load so this
        // is NOT an else clause for the above "if".
        if (network_->isLoad(pin)) {
          graph_->makeWireEdgesToPin(pin);
          connectLoadPinAfter(vertex);
        }
      }
    }
  }
  sdc_->connectPinAfter(pin);
  sim_->connectPinAfter(pin);
}

void
Sta::connectDrvrPinAfter(Vertex *vertex)
{
  // Invalidate arrival at fanout vertices.
  VertexOutEdgeIterator edge_iter(vertex, graph_);
  while (edge_iter.hasNext()) {
    Edge *edge = edge_iter.next();
    Vertex *to_vertex = edge->to(graph_);
    search_->arrivalInvalid(to_vertex);
    search_->endpointInvalid(to_vertex);
    sdc_->clkHpinDisablesChanged(to_vertex->pin());
  }
  Pin *pin = vertex->pin();
  sdc_->clkHpinDisablesChanged(pin);
  graph_delay_calc_->delayInvalid(vertex);
  search_->requiredInvalid(vertex);
  search_->endpointInvalid(vertex);
  levelize_->invalidFrom(vertex);
  clk_network_->connectPinAfter(pin);
}

void
Sta::connectLoadPinAfter(Vertex *vertex)
{
  // Invalidate delays and required at fanin vertices.
  VertexInEdgeIterator edge_iter(vertex, graph_);
  while (edge_iter.hasNext()) {
    Edge *edge = edge_iter.next();
    Vertex *from_vertex = edge->from(graph_);
    graph_delay_calc_->delayInvalid(from_vertex);
    search_->requiredInvalid(from_vertex);
    sdc_->clkHpinDisablesChanged(from_vertex->pin());
  }
  Pin *pin = vertex->pin();
  sdc_->clkHpinDisablesChanged(pin);
  graph_delay_calc_->delayInvalid(vertex);
  levelize_->invalidFrom(vertex);
  search_->arrivalInvalid(vertex);
  search_->endpointInvalid(vertex);
  clk_network_->connectPinAfter(pin);
}

void
Sta::disconnectPinBefore(const Pin *pin)
{
  debugPrint(debug_, "network_edit", 1, "disconnect %s from %s",
             sdc_network_->pathName(pin),
             sdc_network_->pathName(network_->net(pin)));
  parasitics_->disconnectPinBefore(pin, network_);
  sdc_->disconnectPinBefore(pin);
  sim_->disconnectPinBefore(pin);
  if (graph_) {
    if (network_->isDriver(pin)) {
      Vertex *vertex = graph_->pinDrvrVertex(pin);
      // Delete wire edges from pin.
      if (vertex) {
	VertexOutEdgeIterator edge_iter(vertex, graph_);
	while (edge_iter.hasNext()) {
	  Edge *edge = edge_iter.next();
	  if (edge->role()->isWire())
	    deleteEdge(edge);
	}
	clk_network_->disconnectPinBefore(pin);
      }
    }
    if (network_->isLoad(pin)) {
      // Delete wire edges to pin.
      Vertex *vertex = graph_->pinLoadVertex(pin);
      if (vertex) {
	VertexInEdgeIterator edge_iter(vertex, graph_);
	while (edge_iter.hasNext()) {
	  Edge *edge = edge_iter.next();
	  if (edge->role()->isWire())
	    deleteEdge(edge);
	}
	clk_network_->disconnectPinBefore(pin);
      }
    }
    if (network_->isHierarchical(pin)) {
      // Delete wire edges thru pin.
      EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
      while (edge_iter.hasNext()) {
	Edge *edge = edge_iter.next();
	if (edge->role()->isWire()) {
	  deleteEdge(edge);
	  clk_network_->disconnectPinBefore(edge->from(graph_)->pin());
	}
      }
    }
  }
}

void
Sta::deleteEdge(Edge *edge)
{
  debugPrint(debug_, "network_edit", 1, "delete edge %s -> %s",
             edge->from(graph_)->name(sdc_network_),
             edge->to(graph_)->name(sdc_network_));
  Vertex *to = edge->to(graph_);
  search_->deleteEdgeBefore(edge);
  graph_delay_calc_->delayInvalid(to);
  levelize_->relevelizeFrom(to);
  levelize_->deleteEdgeBefore(edge);
  sdc_->clkHpinDisablesChanged(edge->from(graph_)->pin());
  graph_->deleteEdge(edge);
}

void
Sta::deleteNetBefore(const Net *net)
{
  debugPrint(debug_, "network_edit", 1, "delete net %s",
             sdc_network_->pathName(net));
  if (graph_) {
    NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
    while (pin_iter->hasNext()) {
      const Pin *pin = pin_iter->next();
      if (!network_->isHierarchical(pin)) {
	disconnectPinBefore(pin);
	// Delete wire edges on net pins.
	Vertex *vertex = graph_->pinDrvrVertex(pin);
	if (vertex) {
	  VertexOutEdgeIterator edge_iter(vertex, graph_);
	  while (edge_iter.hasNext()) {
	    Edge *edge = edge_iter.next();
	    if (edge->role()->isWire())
	      deleteEdge(edge);
	  }
	}
      }
    }
    delete pin_iter;
  }
  sdc_->deleteNetBefore(net);
}

void
Sta::deleteInstanceBefore(const Instance *inst)
{
  debugPrint(debug_, "network_edit", 1, "delete instance %s",
             sdc_network_->pathName(inst));
  if (network_->isLeaf(inst)) {
    deleteInstancePinsBefore(inst);
    deleteLeafInstanceBefore(inst);
  }
  else {
    // Delete hierarchical instance children.
    InstanceChildIterator *child_iter = network_->childIterator(inst);
    while (child_iter->hasNext()) {
      Instance *child = child_iter->next();
      deleteInstanceBefore(child);
    }
    delete child_iter;
  }
}

void
Sta::deleteLeafInstanceBefore(const Instance *inst)
{
  sim_->deleteInstanceBefore(inst);
}

void
Sta::deleteInstancePinsBefore(const Instance *inst)
{
  InstancePinIterator *pin_iter = network_->pinIterator(inst);
  while (pin_iter->hasNext()) {
    Pin *pin = pin_iter->next();
    deletePinBefore(pin);
  }
  delete pin_iter;
}

void
Sta::deletePinBefore(const Pin *pin)
{
  if (graph_) {
    if (network_->isLoad(pin)) {
      Vertex *vertex = graph_->pinLoadVertex(pin);
      if (vertex) {
        levelize_->deleteVertexBefore(vertex);
        graph_delay_calc_->deleteVertexBefore(vertex);
        search_->deleteVertexBefore(vertex);

        VertexInEdgeIterator in_edge_iter(vertex, graph_);
        while (in_edge_iter.hasNext()) {
          Edge *edge = in_edge_iter.next();
          if (edge->role()->isWire()) {
            Vertex *from = edge->from(graph_);
            // Only notify to_vertex (from_vertex will be deleted).
            search_->requiredInvalid(from);
          }
          levelize_->deleteEdgeBefore(edge);
        }
        graph_->deleteVertex(vertex);
      }
    }
    if (network_->isDriver(pin)) {
      Vertex *vertex = graph_->pinDrvrVertex(pin);
      if (vertex) {
        levelize_->deleteVertexBefore(vertex);
        graph_delay_calc_->deleteVertexBefore(vertex);
        search_->deleteVertexBefore(vertex);

        VertexOutEdgeIterator edge_iter(vertex, graph_);
        while (edge_iter.hasNext()) {
          Edge *edge = edge_iter.next();
          if (edge->role()->isWire()) {
            // Only notify to vertex (from will be deleted).
            Vertex *to = edge->to(graph_);
            search_->arrivalInvalid(to);
            graph_delay_calc_->delayInvalid(to);
            levelize_->relevelizeFrom(to);
          }
          levelize_->deleteEdgeBefore(edge);
        }
        graph_->deleteVertex(vertex);
      }
    }
    if (network_->direction(pin) == PortDirection::internal()) {
      // Internal pins are not loads or drivers.
      Vertex *vertex = graph_->pinLoadVertex(pin);
      if (vertex) {
        levelize_->deleteVertexBefore(vertex);
        graph_delay_calc_->deleteVertexBefore(vertex);
        search_->deleteVertexBefore(vertex);
        graph_->deleteVertex(vertex);
      }
    }
  }
  sim_->deletePinBefore(pin);
  clk_network_->deletePinBefore(pin);
}

void
Sta::delaysInvalidFrom(const Port *port)
{
  if (graph_) {
    Instance *top_inst = network_->topInstance();
    Pin *pin = network_->findPin(top_inst, port);
    delaysInvalidFrom(pin);
  }
}

void
Sta::delaysInvalidFrom(const Instance *inst)
{
  if (graph_) {
    InstancePinIterator *pin_iter = network_->pinIterator(inst);
    while (pin_iter->hasNext()) {
      Pin *pin = pin_iter->next();
      delaysInvalidFrom(pin);
    }
    delete pin_iter;
  }
}

void
Sta::delaysInvalidFrom(const Pin *pin)
{
  if (graph_) {
    Vertex *vertex, *bidirect_drvr_vertex;
    graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
    delaysInvalidFrom(vertex);
    if (bidirect_drvr_vertex)
      delaysInvalidFrom(bidirect_drvr_vertex);
  }
}

void
Sta::delaysInvalidFrom(Vertex *vertex)
{
  search_->arrivalInvalid(vertex);
  search_->requiredInvalid(vertex);
  graph_delay_calc_->delayInvalid(vertex);
}

void
Sta::delaysInvalidFromFanin(const Port *port)
{
  if (graph_) {
    Instance *top_inst = network_->topInstance();
    Pin *pin = network_->findPin(top_inst, port);
    Vertex *vertex, *bidirect_drvr_vertex;
    graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
    delaysInvalidFromFanin(vertex);
    if (bidirect_drvr_vertex)
      delaysInvalidFromFanin(bidirect_drvr_vertex);
  }
}

void
Sta::delaysInvalidFromFanin(const Pin *pin)
{
  if (graph_) {
    Vertex *vertex, *bidirect_drvr_vertex;
    graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
    if (vertex)
      delaysInvalidFromFanin(vertex);
    if (bidirect_drvr_vertex)
      delaysInvalidFromFanin(bidirect_drvr_vertex);
  }
}

void
Sta::delaysInvalidFromFanin(const Net *net)
{
  if (graph_) {
    NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
    while (pin_iter->hasNext()) {
      const Pin *pin = pin_iter->next();
      if (!network_->isHierarchical(pin)) {
	Vertex *vertex, *bidirect_drvr_vertex;
	graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
	if (vertex)
	  delaysInvalidFrom(vertex);
	if (bidirect_drvr_vertex)
	  delaysInvalidFrom(bidirect_drvr_vertex);
      }
    }
    delete pin_iter;
  }
}

void
Sta::delaysInvalidFromFanin(Vertex *vertex)
{
  VertexInEdgeIterator edge_iter(vertex, graph_);
  while (edge_iter.hasNext()) {
    Edge *edge = edge_iter.next();
    Vertex *from_vertex = edge->from(graph_);
    delaysInvalidFrom(from_vertex);
    search_->requiredInvalid(from_vertex);
  }
}

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

ClockSet
Sta::clocks(const Pin *pin)
{
  ensureClkArrivals();
  return search_->clocks(pin);
}

ClockSet
Sta::clockDomains(const Pin *pin)
{
  searchPreamble();
  search_->findAllArrivals();
  return search_->clockDomains(pin);
}

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

InstanceSet
Sta::findRegisterInstances(ClockSet *clks,
			   const RiseFallBoth *clk_rf,
			   bool edge_triggered,
			   bool latches)
{
  findRegisterPreamble();
  return findRegInstances(clks, clk_rf, edge_triggered, latches, this);
}

PinSet
Sta::findRegisterDataPins(ClockSet *clks,
			  const RiseFallBoth *clk_rf,
			  bool edge_triggered,
			  bool latches)
{
  findRegisterPreamble();
  return findRegDataPins(clks, clk_rf, edge_triggered, latches, this);
}

PinSet
Sta::findRegisterClkPins(ClockSet *clks,
			 const RiseFallBoth *clk_rf,
			 bool edge_triggered,
			 bool latches)
{
  findRegisterPreamble();
  return findRegClkPins(clks, clk_rf, edge_triggered, latches, this);
}

PinSet
Sta::findRegisterAsyncPins(ClockSet *clks,
			   const RiseFallBoth *clk_rf,
			   bool edge_triggered,
			   bool latches)
{
  findRegisterPreamble();
  return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, this);
}

PinSet
Sta::findRegisterOutputPins(ClockSet *clks,
			    const RiseFallBoth *clk_rf,
			    bool edge_triggered,
			    bool latches)
{
  findRegisterPreamble();
  return findRegOutputPins(clks, clk_rf, edge_triggered, latches, this);
}

void
Sta::findRegisterPreamble()
{
  ensureLibLinked();
  ensureGraph();
  ensureGraphSdcAnnotated();
  sim_->ensureConstantsPropagated();
}

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

class FanInOutSrchPred : public SearchPred
{
public:
  FanInOutSrchPred(bool thru_disabled,
		   bool thru_constants,
		   const StaState *sta);
  virtual bool searchFrom(const Vertex *from_vertex);
  virtual bool searchThru(Edge *edge);
  virtual bool searchTo(const Vertex *to_vertex);

protected:
  bool crossesHierarchy(Edge *edge);
  virtual bool searchThruRole(Edge *edge);

  bool thru_disabled_;
  bool thru_constants_;
  const StaState *sta_;
};

FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled,
				   bool thru_constants,
				   const StaState *sta) :
  SearchPred(),
  thru_disabled_(thru_disabled),
  thru_constants_(thru_constants),
  sta_(sta)
{
}

bool
FanInOutSrchPred::searchFrom(const Vertex *from_vertex)
{
  return (thru_disabled_
	  || !from_vertex->isDisabledConstraint())
    && (thru_constants_
	|| !from_vertex->isConstant());
}

bool
FanInOutSrchPred::searchThru(Edge *edge)
{
  return searchThruRole(edge)
    && (thru_disabled_
	|| !(edge->isDisabledConstraint()
	     || edge->isDisabledCond()
	     || sta_->isDisabledCondDefault(edge)))
    && (thru_constants_
	|| edge->simTimingSense() != TimingSense::none);
}

bool
FanInOutSrchPred::searchThruRole(Edge *edge)
{
  const TimingRole *role = edge->role();
  return role == TimingRole::wire()
    || role == TimingRole::combinational()
    || role == TimingRole::tristateEnable()
    || role == TimingRole::tristateDisable();
}

bool
FanInOutSrchPred::crossesHierarchy(Edge *edge)
{
  Network *network = sta_->network();
  Graph *graph = sta_->graph();
  Vertex *from = edge->from(graph);
  Vertex *to = edge->to(graph);
  Instance *from_inst = network->instance(from->pin());
  Instance *to_inst = network->instance(to->pin());
  return network->parent(from_inst) != network->parent(to_inst);
}

bool
FanInOutSrchPred::searchTo(const Vertex *to_vertex)
{
  return (thru_disabled_
	  || !to_vertex->isDisabledConstraint())
    && (thru_constants_
	|| !to_vertex->isConstant());
}

class FaninSrchPred : public FanInOutSrchPred
{
public:
  FaninSrchPred(bool thru_disabled,
		bool thru_constants,
		const StaState *sta);

protected:
  virtual bool searchThruRole(Edge *edge);
};

FaninSrchPred::FaninSrchPred(bool thru_disabled,
			     bool thru_constants,
			     const StaState *sta) :
  FanInOutSrchPred(thru_disabled, thru_constants, sta)
{
}

bool
FaninSrchPred::searchThruRole(Edge *edge)
{
  const TimingRole *role = edge->role();
  return role == TimingRole::wire()
    || role == TimingRole::combinational()
    || role == TimingRole::tristateEnable()
    || role == TimingRole::tristateDisable()
    || role == TimingRole::regClkToQ()
    || role == TimingRole::latchEnToQ();
}

PinSet
Sta::findFaninPins(PinSeq *to,
		   bool flat,
		   bool startpoints_only,
		   int inst_levels,
		   int pin_levels,
		   bool thru_disabled,
		   bool thru_constants)
{
  ensureGraph();
  ensureLevelized();
  PinSet fanin(network_);
  FaninSrchPred pred(thru_disabled, thru_constants, this);
  PinSeq::Iterator to_iter(to);
  while (to_iter.hasNext()) {
    const Pin *pin = to_iter.next();
    if (network_->isHierarchical(pin)) {
      EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
      while (edge_iter.hasNext()) {
	Edge *edge = edge_iter.next();
	findFaninPins(edge->from(graph_), flat, startpoints_only,
		      inst_levels, pin_levels, fanin, pred);
      }
    }
    else {
      Vertex *vertex = graph_->pinLoadVertex(pin);
      findFaninPins(vertex, flat, startpoints_only,
		    inst_levels, pin_levels, fanin, pred);
    }
  }
  return fanin;
}

void
Sta::findFaninPins(Vertex *vertex,
		   bool flat,
		   bool startpoints_only,
		   int inst_levels,
		   int pin_levels,
		   PinSet &fanin,
		   SearchPred &pred)
{
  VertexSet visited(graph_);
  findFaninPins(vertex, flat, inst_levels,
		pin_levels, visited, &pred, 0, 0);
  VertexSet::Iterator visited_iter(visited);
  while (visited_iter.hasNext()) {
    Vertex *visited_vertex = visited_iter.next();
    Pin *visited_pin = visited_vertex->pin();
    if (!startpoints_only
	|| network_->isRegClkPin(visited_pin)
	|| !hasFanin(visited_vertex, &pred, graph_))
      fanin.insert(visited_pin);
  }
}

void
Sta::findFaninPins(Vertex *to,
		   bool flat,
		   int inst_levels,
		   int pin_levels,
		   VertexSet &visited,
		   SearchPred *pred,
		   int inst_level,
		   int pin_level)
{
  debugPrint(debug_, "fanin", 1, "%s",
             to->to_string(this).c_str());
  if (!visited.hasKey(to)) {
    visited.insert(to);
    Pin *to_pin = to->pin();
    bool is_reg_clk_pin = network_->isRegClkPin(to_pin);
    if (!is_reg_clk_pin
	&& (inst_levels <= 0
	    || inst_level < inst_levels)
	&& (pin_levels <= 0
	    || pin_level < pin_levels)
	&& pred->searchTo(to)) {
      VertexInEdgeIterator edge_iter(to, graph_);
      while (edge_iter.hasNext()) {
	Edge *edge = edge_iter.next();
	Vertex *from_vertex = edge->from(graph_);
	if (pred->searchThru(edge)
	    && (flat
		|| !crossesHierarchy(edge))
	    && pred->searchFrom(from_vertex)) {
	  findFaninPins(from_vertex, flat, inst_levels,
			pin_levels, visited, pred,
			edge->role()->isWire() ? inst_level : inst_level+1,
			pin_level+1);
	}
      }
    }
  }
}

InstanceSet
Sta::findFaninInstances(PinSeq *to,
			bool flat,
			bool startpoints_only,
			int inst_levels,
			int pin_levels,
			bool thru_disabled,
			bool thru_constants)
{
  PinSet pins = findFaninPins(to, flat, startpoints_only, inst_levels,
                              pin_levels, thru_disabled, thru_constants);
  return pinInstances(pins, network_);
}

PinSet
Sta::findFanoutPins(PinSeq *from,
		    bool flat,
		    bool endpoints_only,
		    int inst_levels,
		    int pin_levels,
		    bool thru_disabled,
		    bool thru_constants)
{
  ensureGraph();
  ensureLevelized();
  PinSet fanout(network_);
  FanInOutSrchPred pred(thru_disabled, thru_constants, this);
  PinSeq::Iterator from_iter(from);
  while (from_iter.hasNext()) {
    const Pin *pin = from_iter.next();
    if (network_->isHierarchical(pin)) {
      EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
      while (edge_iter.hasNext()) {
	Edge *edge = edge_iter.next();
	findFanoutPins(edge->to(graph_), flat, endpoints_only,
		       inst_levels, pin_levels, fanout, pred);
      }
    }
    else {
      Vertex *vertex = graph_->pinDrvrVertex(pin);
      findFanoutPins(vertex, flat, endpoints_only,
		     inst_levels, pin_levels, fanout, pred);
    }
  }
  return fanout;
}

void
Sta::findFanoutPins(Vertex *vertex,
		    bool flat,
		    bool endpoints_only,
		    int inst_levels,
		    int pin_levels,
		    PinSet &fanout,
		    SearchPred &pred)
{
  VertexSet visited(graph_);
  findFanoutPins(vertex, flat, inst_levels,
		 pin_levels, visited, &pred, 0, 0);
  VertexSet::Iterator visited_iter(visited);
  while (visited_iter.hasNext()) {
    Vertex *visited_vertex = visited_iter.next();
    Pin *visited_pin = visited_vertex->pin();
    if (!endpoints_only
	|| search_->isEndpoint(visited_vertex, &pred))
      fanout.insert(visited_pin);
  }
}

// DFS to support level limits.
void
Sta::findFanoutPins(Vertex *from,
		    bool flat,
		    int inst_levels,
		    int pin_levels,
		    VertexSet &visited,
		    SearchPred *pred,
		    int inst_level,
		    int pin_level)
{
  debugPrint(debug_, "fanout", 1, "%s",
             from->to_string(this).c_str());
  if (!visited.hasKey(from)) {
    visited.insert(from);
    if (!search_->isEndpoint(from, pred)
	&& (inst_levels <= 0
	    || inst_level < inst_levels)
	&& (pin_levels <= 0
	    || pin_level < pin_levels)
	&& pred->searchFrom(from)) {
      VertexOutEdgeIterator edge_iter(from, graph_);
      while (edge_iter.hasNext()) {
	Edge *edge = edge_iter.next();
	Vertex *to_vertex = edge->to(graph_);
	if (pred->searchThru(edge)
	    && (flat
		|| !crossesHierarchy(edge))
	    && pred->searchTo(to_vertex)) {
	  findFanoutPins(to_vertex, flat, inst_levels,
			 pin_levels, visited, pred,
			 edge->role()->isWire() ? inst_level : inst_level+1,
			 pin_level+1);
	}
      }
    }
  }
}

InstanceSet
Sta::findFanoutInstances(PinSeq *from,
			 bool flat,
			 bool endpoints_only,
			 int inst_levels,
			 int pin_levels,
			 bool thru_disabled,
			 bool thru_constants)
{
  PinSet pins = findFanoutPins(from, flat, endpoints_only, inst_levels,
                               pin_levels, thru_disabled, thru_constants);
  return pinInstances(pins, network_);
}

static InstanceSet
pinInstances(PinSet &pins,
	     const Network *network)
{
  InstanceSet insts(network);
  for (const Pin *pin : pins)
    insts.insert(network->instance(pin));
  return insts;
}

bool
Sta::crossesHierarchy(Edge *edge) const
{
  Vertex *from = edge->from(graph_);
  Vertex *to = edge->to(graph_);
  const Pin *from_pin = from->pin();
  Instance *from_inst = network_->instance(from_pin);
  Instance *to_inst = network_->instance(to->pin());
  Instance *from_parent, *to_parent;
  // Treat input/output port pins as "inside".
  if (network_->isTopInstance(from_inst))
    from_parent = from_inst;
  else 
    from_parent = network_->parent(from_inst);
  if (network_->isTopInstance(to_inst))
    to_parent = to_inst;
  else 
    to_parent = network_->parent(to_inst);
  return from_parent != to_parent;
}

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

static Slew
instMaxSlew(const Instance *inst,
            Sta *sta)
{
  Network *network = sta->network();
  Graph *graph = sta->graph();
  Slew max_slew = 0.0;
  InstancePinIterator *pin_iter = network->pinIterator(inst);
  while (pin_iter->hasNext()) {
    Pin *pin = pin_iter->next();
    if (network->isDriver(pin)) {
      Vertex *vertex = graph->pinDrvrVertex(pin);
      Slew slew = sta->vertexSlew(vertex, MinMax::max());
      if (delayGreater(slew, max_slew, sta))
        max_slew = slew;
    }
  }
  delete pin_iter;
  return max_slew;
}

InstanceSeq
Sta::slowDrivers(int count)
{
  findDelays();
  InstanceSeq insts = network_->leafInstances();
  sort(insts, [=] (const Instance *inst1,
                   const Instance *inst2) {
    return delayGreater(instMaxSlew(inst1, this),
                        instMaxSlew(inst2, this),
                        this);
  });
  insts.resize(count);
  return insts;
}

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

void
Sta::checkSlewLimitPreamble()
{
  if (sdc_->haveClkSlewLimits())
    // Arrivals are needed to know pin clock domains.
    updateTiming(false);
  else
    findDelays();
  if (check_slew_limits_ == nullptr)
    makeCheckSlewLimits();
  ensureClkNetwork();
}

PinSeq
Sta::checkSlewLimits(Net *net,
                     bool violators,
                     const Corner *corner,
                     const MinMax *min_max)
{
  checkSlewLimitPreamble();
  return check_slew_limits_->checkSlewLimits(net, violators, corner, min_max);
}

void
Sta::reportSlewLimitShortHeader()
{
  report_path_->reportLimitShortHeader(report_path_->fieldSlew());
}

void
Sta::reportSlewLimitShort(Pin *pin,
			  const Corner *corner,
			  const MinMax *min_max)
{
  const Corner *corner1;
  const RiseFall *rf1;
  Slew slew1;
  float limit1, slack1;
  check_slew_limits_->checkSlew(pin, corner, min_max, true,
				corner1, rf1, slew1, limit1, slack1);
  report_path_->reportLimitShort(report_path_->fieldSlew(), pin,
				 delayAsFloat(slew1), limit1, slack1);
}

void
Sta::reportSlewLimitVerbose(Pin *pin,
			    const Corner *corner,
			    const MinMax *min_max)
{
  const Corner *corner1;
  const RiseFall *rf1;
  Slew slew1;
  float limit1, slack1;
  check_slew_limits_->checkSlew(pin, corner, min_max, true,
				corner1, rf1, slew1, limit1, slack1);
  report_path_->reportLimitVerbose(report_path_->fieldSlew(), pin, rf1,
				   delayAsFloat(slew1),
				   limit1, slack1, corner1, min_max);
}

void
Sta::checkSlew(const Pin *pin,
	       const Corner *corner,
	       const MinMax *min_max,
	       bool check_clks,
	       // Return values.
	       const Corner *&corner1,
	       const RiseFall *&rf,
	       Slew &slew,
	       float &limit,
	       float &slack)
{
  check_slew_limits_->checkSlew(pin, corner, min_max, check_clks,
				corner1, rf, slew, limit, slack);
}

void
Sta::maxSlewCheck(// Return values.
                  const Pin *&pin,
                  Slew &slew,
                  float &slack,
                  float &limit)
{
  checkSlewLimitPreamble();
  PinSeq pins = check_slew_limits_->checkSlewLimits(nullptr, false, nullptr,
                                                    MinMax::max());
  pin = nullptr;
  slew = 0.0;
  slack = INF;
  limit = INF;
  if (!pins.empty()) {
    pin = pins[0];
    const Corner *corner;
    const RiseFall *rf;
    check_slew_limits_->checkSlew(pin, nullptr, MinMax::max(), true,
                                  corner, rf, slew, limit, slack);
  }
}

void
Sta::findSlewLimit(const LibertyPort *port,
                   const Corner *corner,
                   const MinMax *min_max,
                   // Return values.
                   float &limit,
                   bool &exists)
{
  if (check_slew_limits_ == nullptr)
    makeCheckSlewLimits();
  check_slew_limits_->findLimit(port, corner, min_max,
                                limit, exists);
}

////////////////////////////////////////////////////////////////'

void
Sta::checkFanoutLimitPreamble()
{
  if (check_fanout_limits_ == nullptr)
    makeCheckFanoutLimits();
  ensureClkNetwork();
}

PinSeq
Sta::checkFanoutLimits(Net *net,
                       bool violators,
                       const MinMax *min_max)
{
  checkFanoutLimitPreamble();
  return check_fanout_limits_->checkFanoutLimits(net, violators, min_max);
}

void
Sta::reportFanoutLimitShortHeader()
{
  report_path_->reportLimitShortHeader(report_path_->fieldFanout());
}

void
Sta::reportFanoutLimitShort(Pin *pin,
			    const MinMax *min_max)
{
  float fanout, limit, slack;
  check_fanout_limits_->checkFanout(pin, min_max,
				    fanout, limit, slack);
  report_path_->reportLimitShort(report_path_->fieldFanout(),
				 pin, fanout, limit, slack);
}

void
Sta::reportFanoutLimitVerbose(Pin *pin,
			      const MinMax *min_max)
{
  float fanout, limit, slack;
  check_fanout_limits_->checkFanout(pin, min_max,
				    fanout, limit, slack);
  report_path_->reportLimitVerbose(report_path_->fieldFanout(),
				   pin, nullptr, fanout,
				   limit, slack, nullptr, min_max);
}

void
Sta::checkFanout(const Pin *pin,
		 const MinMax *min_max,
		 // Return values.
		 float &fanout,
		 float &limit,
		 float &slack)
{
  check_fanout_limits_->checkFanout(pin, min_max,
				    fanout, limit, slack);
}

void
Sta::maxFanoutCheck(// Return values.
                    const Pin *&pin,
                    float &fanout,
                    float &slack,
                    float &limit)
{
  checkFanoutLimitPreamble();
  PinSeq pins = check_fanout_limits_->checkFanoutLimits(nullptr, false, MinMax::max());
  pin = nullptr;
  fanout = 0;
  slack = INF;
  limit = INF;
  if (!pins.empty()) {
    pin = pins[0];
    check_fanout_limits_->checkFanout(pin, MinMax::max(),
                                      fanout, limit, slack);
  }
}

////////////////////////////////////////////////////////////////'

void
Sta::checkCapacitanceLimitPreamble()
{
  if (check_capacitance_limits_ == nullptr)
    makeCheckCapacitanceLimits();
  ensureClkNetwork();
}

PinSeq
Sta::checkCapacitanceLimits(Net *net,
                            bool violators,
                            const Corner *corner,
                            const MinMax *min_max)
{
  checkCapacitanceLimitPreamble();
  return check_capacitance_limits_->checkCapacitanceLimits(net, violators,
                                                           corner, min_max);
}

void
Sta::reportCapacitanceLimitShortHeader()
{
  report_path_->reportLimitShortHeader(report_path_->fieldCapacitance());
}

void
Sta::reportCapacitanceLimitShort(Pin *pin,
				 const Corner *corner,
				 const MinMax *min_max)
{
  const Corner *corner1;
  const RiseFall *rf;
  float capacitance, limit, slack;
  check_capacitance_limits_->checkCapacitance(pin, corner, min_max,
					      corner1, rf, capacitance,
					      limit, slack);
  report_path_->reportLimitShort(report_path_->fieldCapacitance(),
				 pin, capacitance, limit, slack);
}

void
Sta::reportCapacitanceLimitVerbose(Pin *pin,
				   const Corner *corner,
				   const MinMax *min_max)
{
  const Corner *corner1;
  const RiseFall *rf1;
  float capacitance1, limit1, slack1;
  check_capacitance_limits_->checkCapacitance(pin, corner, min_max,
					      corner1, rf1, capacitance1,
					      limit1, slack1);
  report_path_->reportLimitVerbose(report_path_->fieldCapacitance(),
				   pin, rf1, capacitance1,
				   limit1, slack1, corner1, min_max);
}

void
Sta::checkCapacitance(const Pin *pin,
		      const Corner *corner,
		      const MinMax *min_max,
		      // Return values.
		      const Corner *&corner1,
		      const RiseFall *&rf,
		      float &capacitance,
		      float &limit,
		      float &slack)
{
  check_capacitance_limits_->checkCapacitance(pin, corner, min_max,
					      corner1, rf, capacitance,
					      limit, slack);
}

void
Sta::maxCapacitanceCheck(// Return values.
                         const Pin *&pin,
                         float &capacitance,
                         float &slack,
                         float &limit)
{
  checkCapacitanceLimitPreamble();
  PinSeq pins = check_capacitance_limits_->checkCapacitanceLimits(nullptr, false,
                                                                  nullptr,
                                                                  MinMax::max());
  pin = nullptr;
  capacitance = 0.0;
  slack = INF;
  limit = INF;
  if (!pins.empty()) {
    pin = pins[0];
    const Corner *corner;
    const RiseFall *rf;
    check_capacitance_limits_->checkCapacitance(pin, nullptr, MinMax::max(),
                                                corner, rf, capacitance, limit, slack);
  }
}

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

void
Sta::minPulseWidthPreamble()
{
  ensureClkArrivals();
  if (check_min_pulse_widths_ == nullptr)
    makeCheckMinPulseWidths();
}

MinPulseWidthCheckSeq &
Sta::minPulseWidthChecks(PinSeq *pins,
			 const Corner *corner)
{
  minPulseWidthPreamble();
  return check_min_pulse_widths_->check(pins, corner);
}

MinPulseWidthCheckSeq &
Sta::minPulseWidthChecks(const Corner *corner)
{
  minPulseWidthPreamble();
  return check_min_pulse_widths_->check(corner);
}

MinPulseWidthCheckSeq &
Sta::minPulseWidthViolations(const Corner *corner)
{
  minPulseWidthPreamble();
  return check_min_pulse_widths_->violations(corner);
}

MinPulseWidthCheck *
Sta::minPulseWidthSlack(const Corner *corner)
{
  minPulseWidthPreamble();
  return check_min_pulse_widths_->minSlackCheck(corner);
}

void
Sta::reportMpwChecks(MinPulseWidthCheckSeq *checks,
		     bool verbose)
{
  report_path_->reportMpwChecks(checks, verbose);
}

void
Sta::reportMpwCheck(MinPulseWidthCheck *check,
		    bool verbose)
{
  report_path_->reportMpwCheck(check, verbose);
}

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

MinPeriodCheckSeq &
Sta::minPeriodViolations()
{
  minPeriodPreamble();
  return check_min_periods_->violations();
}

MinPeriodCheck *
Sta::minPeriodSlack()
{
  minPeriodPreamble();
  return check_min_periods_->minSlackCheck();
}

void
Sta::minPeriodPreamble()
{
  // Need clk arrivals to know what clks arrive at the clk tree endpoints.
  ensureClkArrivals();
  if (check_min_periods_ == nullptr)
    makeCheckMinPeriods();
}

void
Sta::reportChecks(MinPeriodCheckSeq *checks,
		  bool verbose)
{
  report_path_->reportChecks(checks, verbose);
}

void
Sta::reportCheck(MinPeriodCheck *check,
		 bool verbose)
{
  report_path_->reportCheck(check, verbose);
}

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

MaxSkewCheckSeq &
Sta::maxSkewViolations()
{
  maxSkewPreamble();
  return check_max_skews_->violations();
}

MaxSkewCheck *
Sta::maxSkewSlack()
{
  maxSkewPreamble();
  return check_max_skews_->minSlackCheck();
}

void
Sta::maxSkewPreamble()
{
  ensureClkArrivals();
  if (check_max_skews_ == nullptr)
    makeCheckMaxSkews();
}

void
Sta::reportChecks(MaxSkewCheckSeq *checks,
		  bool verbose)
{
  report_path_->reportChecks(checks, verbose);
}

void
Sta::reportCheck(MaxSkewCheck *check,
		 bool verbose)
{
  report_path_->reportCheck(check, verbose);
}

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

void
Sta::makeEquivCells(LibertyLibrarySeq *equiv_libs,
		    LibertyLibrarySeq *map_libs)
{
  delete equiv_cells_;
  equiv_cells_ = new EquivCells(equiv_libs, map_libs);
}

LibertyCellSeq *
Sta::equivCells(LibertyCell *cell)
{
  if (equiv_cells_)
    return equiv_cells_->equivs(cell);
  else
    return nullptr;
}

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

void
Sta::writeTimingModel(const char *lib_name,
                      const char *cell_name,
                      const char *filename,
                      const Corner *corner)
{
  ensureLibLinked();
  ensureGraph();
  LibertyLibrary *library = makeTimingModel(lib_name, cell_name, filename,
                                            corner, this);
  writeLiberty(library, filename, this);
}

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

void
Sta::powerPreamble()
{
  ensureLibLinked();
  // Use arrivals to find clocking info.
  searchPreamble();
  search_->findAllArrivals();
  ensureClkNetwork();
}

void
Sta::power(const Corner *corner,
	   // Return values.
	   PowerResult &total,
	   PowerResult &sequential,
	   PowerResult &combinational,
	   PowerResult &clock,
	   PowerResult &macro,
	   PowerResult &pad)
{
  powerPreamble();
  power_->power(corner, total, sequential, combinational, clock, macro, pad);
}

PowerResult
Sta::power(const Instance *inst,
	   const Corner *corner)
{
  powerPreamble();
  return power_->power(inst, corner);
}

PwrActivity
Sta::activity(const Pin *pin)
{
  powerPreamble();
  return power_->pinActivity(pin);
}

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

void
Sta::writePathSpice(Path *path,
                    const char *spice_filename,
                    const char *subckt_filename,
                    const char *lib_subckt_filename,
                    const char *model_filename,
                    const char *power_name,
                    const char *gnd_name,
                    CircuitSim ckt_sim)
{
  ensureLibLinked();
  sta::writePathSpice(path, spice_filename, subckt_filename,
                      lib_subckt_filename, model_filename,
                      power_name, gnd_name, ckt_sim, this);
}

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

void
Sta::ensureClkNetwork()
{
  ensureLevelized();
  clk_network_->ensureClkNetwork();
}

bool
Sta::isClock(const Pin *pin) const
{
  return clk_network_->isClock(pin);
}

bool
Sta::isClock(const Net *net) const
{
  return clk_network_->isClock(net);
}

bool
Sta::isIdealClock(const Pin *pin) const
{
  return clk_network_->isIdealClock(pin);
}

bool
Sta::isPropagatedClock(const Pin *pin) const
{
  return clk_network_->isPropagatedClock(pin);
}

const PinSet *
Sta::pins(const Clock *clk)
{
  return clk_network_->pins(clk);
}

void
Sta::clkPinsInvalid()
{
  clk_network_->clkPinsInvalid();
}

} // namespace
