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

%module sdc
%import <std_string.i>

%{
#include <vector>

#include "Sdc.hh"
#include "Wireload.hh"
#include "Clock.hh"
#include "PortDelay.hh"
#include "Property.hh"
#include "Sta.hh"

using namespace sta;

%}

////////////////////////////////////////////////////////////////
//
// Empty class definitions to make swig happy.
// Private constructor/destructor so swig doesn't emit them.
//
////////////////////////////////////////////////////////////////

class Clock
{
private:
  Clock();
  ~Clock();
};

class ClockEdge
{
private:
  ClockEdge();
  ~ClockEdge();
};

class ExceptionFrom
{
private:
  ExceptionFrom();
  ~ExceptionFrom();
};

class ExceptionThru
{
private:
  ExceptionThru();
  ~ExceptionThru();
};

class ExceptionTo
{
private:
  ExceptionTo();
  ~ExceptionTo();
};

class OperatingConditions
{
private:
  OperatingConditions();
  ~OperatingConditions();
};

%inline %{

void
write_sdc_cmd(const char *filename,
              bool leaf,
              bool compatible,
              int digits,
              bool gzip,
              bool no_timestamp)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  sta->writeSdc(sdc, filename, leaf, compatible, digits, gzip, no_timestamp);
}

void
set_analysis_type_cmd(const char *analysis_type)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  AnalysisType type;
  if (stringEq(analysis_type, "single"))
    type = AnalysisType::single;
  else if (stringEq(analysis_type, "bc_wc"))
    type = AnalysisType::bc_wc;
  else if (stringEq(analysis_type, "on_chip_variation"))
    type = AnalysisType::ocv;
  else {
    sta->report()->warn(2121, "unknown analysis type");
    type = AnalysisType::single;
  }
  sta->setAnalysisType(type, sdc);
}

OperatingConditions *
operating_conditions(const MinMax *min_max)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  return sta->operatingConditions(min_max, sdc);
}

void
set_operating_conditions_cmd(OperatingConditions *op_cond,
                             const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setOperatingConditions(op_cond, min_max, sdc);
}

const char *
operating_condition_analysis_type()
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  switch (sdc->analysisType()) {
  case AnalysisType::single:
    return "single";
  case AnalysisType::bc_wc:
    return "bc_wc";
  case AnalysisType::ocv:
    return "on_chip_variation";
  }
  // Prevent warnings from lame compilers.
  return "?";
}

void
set_instance_pvt(Instance *inst,
                 const MinMaxAll *min_max,
                 float process,
                 float voltage,
                 float temperature)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  Pvt pvt(process, voltage, temperature);
  sta->setPvt(inst, min_max, pvt, sdc);
}

float
port_ext_pin_cap(const Port *port,
                 const MinMax *min_max)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  float pin_cap, wire_cap;
  int fanout;
  sta->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout);
  return pin_cap;
}

void
set_port_ext_pin_cap(const Port *port,
                     const RiseFallBoth *rf,
                     const MinMaxAll *min_max,
                     float cap)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setPortExtPinCap(port, rf, min_max, cap, sdc);
}

float
port_ext_wire_cap(const Port *port,
                  const MinMax *min_max)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  float pin_cap, wire_cap;
  int fanout;
  sta->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout);
  return wire_cap;
}

void
set_port_ext_wire_cap(const Port *port,
                      const RiseFallBoth *rf,
                      const MinMaxAll *min_max,
                      float cap)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setPortExtWireCap(port, rf, min_max, cap, sdc);
}

void
set_port_ext_fanout_cmd(const Port *port,
                        int fanout,
                        const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setPortExtFanout(port, fanout, min_max, sdc);
}

float
port_ext_fanout(const Port *port,
                const MinMax *min_max)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  float pin_cap, wire_cap;
  int fanout;
  Sta::sta()->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout);
  return fanout;
}

void
set_net_wire_cap(const Net *net,
                 bool subtract_pin_cap,
                 const MinMaxAll *min_max,
                 float cap)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setNetWireCap(net, subtract_pin_cap, min_max, cap, sdc);
}

void
set_wire_load_mode_cmd(const char *mode_name)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  WireloadMode mode = stringWireloadMode(mode_name);
  if (mode == WireloadMode::unknown)
    sta->report()->warn(2122, "unknown wire load mode");
  else
    sta->setWireloadMode(mode, sdc);
}

void
set_wire_load_cmd(Wireload *wireload,
                  const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setWireload(wireload, min_max, sdc);
}

void
set_wire_load_selection_group_cmd(WireloadSelection *selection,
                                  const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setWireloadSelection(selection, min_max, sdc);
}

void
set_net_resistance(Net *net,
                   const MinMaxAll *min_max,
                   float res)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setResistance(net, min_max, res, sdc);
}

void
make_clock(const char *name,
           PinSet *pins,
           bool add_to_pins,
           float period,
           FloatSeq *waveform,
           char *comment)
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  sta->makeClock(name, pins, add_to_pins, period, waveform, comment, mode);
}

void
make_generated_clock(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)
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  sta->makeGeneratedClock(name, pins, add_to_pins,
                          src_pin, master_clk,
                          divide_by, multiply_by, duty_cycle, invert,
                          combinational, edges, edge_shifts,
                          comment, mode);
}

void
remove_clock_cmd(Clock *clk)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClock(clk, sdc);
}

void
set_propagated_clock_cmd(Clock *clk)
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  sta->setPropagatedClock(clk, mode);
}

void
set_propagated_clock_pin_cmd(Pin *pin)
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  sta->setPropagatedClock(pin, mode);
}

void
unset_propagated_clock_cmd(Clock *clk)
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  sta->removePropagatedClock(clk, mode);
}

void
unset_propagated_clock_pin_cmd(Pin *pin)
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  sta->removePropagatedClock(pin, mode);
}

void
set_clock_slew_cmd(Clock *clk,
                   const RiseFallBoth *rf,
                   const MinMaxAll *min_max,
                   float slew)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockSlew(clk, rf, min_max, slew, sdc);
}

void
unset_clock_slew_cmd(Clock *clk)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockSlew(clk, sdc);
}

void
set_clock_latency_cmd(Clock *clk,
                      Pin *pin,
                      const RiseFallBoth *rf,
                      MinMaxAll *min_max, float delay)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockLatency(clk, pin, rf, min_max, delay, sdc);
}

void
set_clock_insertion_cmd(Clock *clk,
                        Pin *pin,
                        const RiseFallBoth *rf,
                        const MinMaxAll *min_max,
                        const EarlyLateAll *early_late,
                        float delay)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockInsertion(clk, pin, rf, min_max, early_late, delay, sdc);
}

void
unset_clock_latency_cmd(Clock *clk,
                        Pin *pin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockLatency(clk, pin, sdc);
}

void
unset_clock_insertion_cmd(Clock *clk,
                          Pin *pin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockInsertion(clk, pin, sdc);
}

void
set_clock_uncertainty_clk(Clock *clk,
                          const SetupHoldAll *setup_hold,
                          float uncertainty)
{
  Sta *sta = Sta::sta();
  sta->setClockUncertainty(clk, setup_hold, uncertainty);
}

void
unset_clock_uncertainty_clk(Clock *clk,
                            const SetupHoldAll *setup_hold)
{
  Sta *sta = Sta::sta();
  sta->removeClockUncertainty(clk, setup_hold);
}

void
set_clock_uncertainty_pin(Pin *pin,
                          const MinMaxAll *min_max,
                          float uncertainty)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockUncertainty(pin, min_max, uncertainty, sdc);
}

void
unset_clock_uncertainty_pin(Pin *pin,
                            const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockUncertainty(pin, min_max, sdc);
}

void
set_inter_clock_uncertainty(Clock *from_clk,
                            const RiseFallBoth *from_tr,
                            Clock *to_clk,
                            const RiseFallBoth *to_tr,
                            const MinMaxAll *min_max,
                            float uncertainty)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max,
                           uncertainty, sdc);
}

void
unset_inter_clock_uncertainty(Clock *from_clk,
                              const RiseFallBoth *from_tr,
                              Clock *to_clk,
                              const RiseFallBoth *to_tr,
                              const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, sdc);
}

void
set_clock_gating_check_cmd(const RiseFallBoth *rf,
                           const SetupHold *setup_hold,
                           float margin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockGatingCheck(rf, setup_hold, margin, sdc);
}

void
set_clock_gating_check_clk_cmd(Clock *clk,
                               const RiseFallBoth *rf,
                               const SetupHold *setup_hold,
                               float margin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockGatingCheck(clk, rf, setup_hold, margin, sdc);
}

void
set_clock_gating_check_pin_cmd(Pin *pin,
                               const RiseFallBoth *rf,
                               const SetupHold *setup_hold,
                               float margin,
                               LogicValue active_value)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockGatingCheck(pin, rf, setup_hold, margin, active_value, sdc);
}

void
set_clock_gating_check_instance_cmd(Instance *inst,
                                    const RiseFallBoth *rf,
                                    const SetupHold *setup_hold,
                                    float margin,
                                    LogicValue active_value)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setClockGatingCheck(inst, rf, setup_hold, margin, active_value, sdc);
}

void
set_data_check_cmd(Pin *from,
                   const RiseFallBoth *from_rf,
                   Pin *to,
                   const RiseFallBoth *to_rf,
                   Clock *clk,
                   const SetupHoldAll *setup_hold,
                   float margin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin, sdc);
}

void
unset_data_check_cmd(Pin *from,
                     const RiseFallBoth *from_tr,
                     Pin *to,
                     const RiseFallBoth *to_tr,
                     Clock *clk,
                     const SetupHoldAll *setup_hold)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDataCheck(from, from_tr, to, to_tr, clk, setup_hold, sdc);
}

void
set_input_delay_cmd(Pin *pin,
                    RiseFallBoth *rf,
                    Clock *clk,
                    RiseFall *clk_rf,
                    Pin *ref_pin,
                    bool source_latency_included,
                    bool network_latency_included,
                    MinMaxAll *min_max,
                    bool add,
                    float delay)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setInputDelay(pin, rf, clk, clk_rf, ref_pin,
                     source_latency_included, network_latency_included,
                     min_max, add, delay, sdc);
}

void
unset_input_delay_cmd(Pin *pin,
                      RiseFallBoth *rf, 
                      Clock *clk,
                      RiseFall *clk_rf, 
                      MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeInputDelay(pin, rf, clk, clk_rf, min_max, sdc);
}

void
set_output_delay_cmd(Pin *pin,
                     const RiseFallBoth *rf,
                     Clock *clk,
                     const RiseFall *clk_rf,
                     Pin *ref_pin,
                     bool source_latency_included,
                     bool network_latency_included,
                     const MinMaxAll *min_max,
                     bool add,
                     float delay)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setOutputDelay(pin, rf, clk, clk_rf, ref_pin,
                      source_latency_included, network_latency_included,
                      min_max, add, delay, sdc);
}

void
unset_output_delay_cmd(Pin *pin,
                       RiseFallBoth *rf, 
                       Clock *clk,
                       RiseFall *clk_rf, 
                       MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeOutputDelay(pin, rf, clk, clk_rf, min_max, sdc);
}

void
disable_cell(LibertyCell *cell,
             LibertyPort *from,
             LibertyPort *to)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disable(cell, from, to, sdc);
}

void
unset_disable_cell(LibertyCell *cell,
                   LibertyPort *from,
                   LibertyPort *to)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisable(cell, from, to, sdc);
}

void
disable_lib_port(LibertyPort *port)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disable(port, sdc);
}

void
unset_disable_lib_port(LibertyPort *port)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisable(port, sdc);
}

void
disable_port(Port *port)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disable(port, sdc);
}

void
unset_disable_port(Port *port)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisable(port, sdc);
}

void
disable_instance(Instance *instance,
                 LibertyPort *from,
                 LibertyPort *to)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disable(instance, from, to, sdc);
}

void
unset_disable_instance(Instance *instance,
                       LibertyPort *from,
                       LibertyPort *to)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisable(instance, from, to, sdc);
}

void
disable_pin(Pin *pin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disable(pin, sdc);
}

void
unset_disable_pin(Pin *pin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisable(pin, sdc);
}

void
disable_edge(Edge *edge)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disable(edge, sdc);
}

void
unset_disable_edge(Edge *edge)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisable(edge, sdc);
}

void
disable_timing_arc_set(TimingArcSet *arc_set)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disable(arc_set, sdc);
}

void
unset_disable_timing_arc_set(TimingArcSet *arc_set)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisable(arc_set, sdc);
}

void
disable_clock_gating_check_inst(Instance *inst)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disableClockGatingCheck(inst, sdc);
}

void
disable_clock_gating_check_pin(Pin *pin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->disableClockGatingCheck(pin, sdc);
}

void
unset_disable_clock_gating_check_inst(Instance *inst)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisableClockGatingCheck(inst, sdc);
}

void
unset_disable_clock_gating_check_pin(Pin *pin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeDisableClockGatingCheck(pin, sdc);
}

EdgeSeq
disabled_edges_sorted()
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  return sta->disabledEdgesSorted(mode);
}

bool
timing_arc_disabled(Edge *edge,
                    TimingArc *arc)
{
  Sta *sta = Sta::sta();
  const Mode *mode = sta->cmdMode();
  return !searchThru(edge, arc, mode);
}

void
make_false_path(ExceptionFrom *from,
                ExceptionThruSeq *thrus,
                ExceptionTo *to,
                const MinMaxAll *min_max,
                const char *comment)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->makeFalsePath(from, thrus, to, min_max, comment, sdc);
}

void
make_multicycle_path(ExceptionFrom *from,
                     ExceptionThruSeq *thrus,
                     ExceptionTo *to,
                     const MinMaxAll *min_max,
                     bool use_end_clk,
                     int path_multiplier,
                     const char *comment)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->makeMulticyclePath(from, thrus, to, min_max, use_end_clk,
                          path_multiplier, comment, sdc);
}

void
make_path_delay(ExceptionFrom *from,
                ExceptionThruSeq *thrus,
                ExceptionTo *to,
                const MinMax *min_max,
                bool ignore_clk_latency,
                bool break_path,
                float delay,
                const char *comment)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->makePathDelay(from, thrus, to, min_max, 
                     ignore_clk_latency, break_path,
                     delay, comment, sdc);
}

void
reset_path_cmd(ExceptionFrom *
               from, ExceptionThruSeq *thrus,
               ExceptionTo *to,
               const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->resetPath(from, thrus, to, min_max, sdc);
  // from/to and thru are owned and deleted by the caller.
  // ExceptionThruSeq thrus arg is made by TclListSeqExceptionThru
  // in the swig converter so it is deleted here.
  delete thrus;
}

void
make_group_path(const char *name,
                bool is_default,
                ExceptionFrom *from,
                ExceptionThruSeq *thrus,
                ExceptionTo *to,
                const char *comment)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->makeGroupPath(name, is_default, from, thrus, to, comment, sdc);
}

bool
is_path_group_name(const char *name)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  return Sta::sta()->isPathGroupName(name, sdc);
}

ExceptionFrom *
make_exception_from(PinSet *from_pins,
                    ClockSet *from_clks,
                    InstanceSet *from_insts,
                    const RiseFallBoth *from_rf)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  return sta->makeExceptionFrom(from_pins, from_clks, from_insts,
                                from_rf, sdc);
}

void
delete_exception_from(ExceptionFrom *from)
{
  Sta *sta = Sta::sta();
  sta->deleteExceptionFrom(from);
}

void
check_exception_from_pins(ExceptionFrom *from,
                          const char *file,
                          int line)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  sta->checkExceptionFromPins(from, file, line, sdc);
}

ExceptionThru *
make_exception_thru(PinSet *pins,
                    NetSet *nets,
                    InstanceSet *insts,
                    const RiseFallBoth *rf)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  return sta->makeExceptionThru(pins, nets, insts, rf, sdc);
}

void
delete_exception_thru(ExceptionThru *thru)
{
  Sta *sta = Sta::sta();
  sta->deleteExceptionThru(thru);
}

ExceptionTo *
make_exception_to(PinSet *to_pins,
                  ClockSet *to_clks,
                  InstanceSet *to_insts,
                  const RiseFallBoth *rf,
                  RiseFallBoth *end_rf)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  return sta->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf, sdc);
}

void
delete_exception_to(ExceptionTo *to)
{
  Sta *sta = Sta::sta();
  sta->deleteExceptionTo(to);
}

void
check_exception_to_pins(ExceptionTo *to,
                        const char *file,
                        int line)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  sta->checkExceptionToPins(to, file, line, sdc);
}

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

ClockGroups *
make_clock_groups(const char *name,
                  bool logically_exclusive,
                  bool physically_exclusive,
                  bool asynchronous,
                  bool allow_paths,
                  const char *comment)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  return sta->makeClockGroups(name, logically_exclusive,
                              physically_exclusive, asynchronous,
                              allow_paths, comment, sdc);
}

void
clock_groups_make_group(ClockGroups *clk_groups,
                        ClockSet *clks)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->makeClockGroup(clk_groups, clks, sdc);
}

void
unset_clock_groups_logically_exclusive()
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockGroupsLogicallyExclusive(sdc);
}

void
unset_clock_groups_logically_exclusive(const char *name)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockGroupsLogicallyExclusive(name, sdc);
}

void
unset_clock_groups_physically_exclusive()
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockGroupsPhysicallyExclusive(sdc);
}

void
unset_clock_groups_physically_exclusive(const char *name)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockGroupsPhysicallyExclusive(name, sdc);
}

void
unset_clock_groups_asynchronous()
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockGroupsAsynchronous(sdc);
}

void
unset_clock_groups_asynchronous(const char *name)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->removeClockGroupsAsynchronous(name, sdc);
}

// Debugging function.
bool
same_clk_group(Clock *clk1,
               Clock *clk2)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  return sdc->sameClockGroupExplicit(clk1, clk2);
}

void
set_clock_sense_cmd(PinSet *pins,
                    ClockSet *clks,
                    bool positive,
                    bool negative,
                    bool stop_propagation)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  if (positive)
    sta->setClockSense(pins, clks, ClockSense::positive, sdc);
  else if (negative)
    sta->setClockSense(pins, clks, ClockSense::negative, sdc);
  else if (stop_propagation)
    sta->setClockSense(pins, clks, ClockSense::stop, sdc);
  else
    sta->report()->critical(2123, "unknown clock sense");
}

void
set_input_slew_cmd(Port *port,
                   const RiseFallBoth *rf,
                   const MinMaxAll *min_max,
                   float slew)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setInputSlew(port, rf, min_max, slew, sdc);
}

void
set_drive_cell_cmd(LibertyLibrary *library,
                   LibertyCell *cell,
                   Port *port,
                   LibertyPort *from_port,
                   float from_slew_rise,
                   float from_slew_fall,
                   LibertyPort *to_port,
                   const RiseFallBoth *rf,
                   const MinMaxAll *min_max)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  float from_slews[RiseFall::index_count];
  from_slews[RiseFall::riseIndex()] = from_slew_rise;
  from_slews[RiseFall::fallIndex()] = from_slew_fall;
  sta->setDriveCell(library, cell, port, from_port, from_slews,
                    to_port, rf, min_max, sdc);
}

void
set_drive_resistance_cmd(Port *port,
                         const RiseFallBoth *rf,
                         const MinMaxAll *min_max,
                         float res)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setDriveResistance(port, rf, min_max, res, sdc);
}

void
set_slew_limit_clk(Clock *clk,
                   const RiseFallBoth *rf,
                   PathClkOrData clk_data,
                   const MinMax *min_max,
                   float slew)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setSlewLimit(clk, rf, clk_data, min_max, slew, sdc);
}

void
set_slew_limit_port(Port *port,
                    const MinMax *min_max,
                    float slew)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setSlewLimit(port, min_max, slew, sdc);
}

void
set_slew_limit_cell(Cell *cell,
                    const MinMax *min_max,
                    float slew)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setSlewLimit(cell, min_max, slew, sdc);
}

void
set_port_capacitance_limit(Port *port,
                           const MinMax *min_max,
                           float cap)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setCapacitanceLimit(port, min_max, cap, sdc);
}

void
set_pin_capacitance_limit(Pin *pin,
                          const MinMax *min_max,
                          float cap)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setCapacitanceLimit(pin, min_max, cap, sdc);
}

void
set_cell_capacitance_limit(Cell *cell,
                           const MinMax *min_max,
                           float cap)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setCapacitanceLimit(cell, min_max, cap, sdc);
}

void
set_latch_borrow_limit_pin(Pin *pin,
                           float limit)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setLatchBorrowLimit(pin, limit, sdc);
}

void
set_latch_borrow_limit_inst(Instance *inst,
                            float limit)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setLatchBorrowLimit(inst, limit, sdc);
}

void
set_latch_borrow_limit_clk(Clock *clk, float limit)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setLatchBorrowLimit(clk, limit, sdc);
}

void
set_min_pulse_width_global(const RiseFallBoth *rf,
                           float min_width)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setMinPulseWidth(rf, min_width, sdc);
}

void
set_min_pulse_width_pin(Pin *pin,
                        const RiseFallBoth *rf,
                        float min_width)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setMinPulseWidth(pin, rf, min_width, sdc);
}

void
set_min_pulse_width_clk(Clock *clk,
                        const RiseFallBoth *rf,
                        float min_width)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setMinPulseWidth(clk, rf, min_width, sdc);
}

void
set_min_pulse_width_inst(Instance *inst,
                         const RiseFallBoth *rf,
                         float min_width)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setMinPulseWidth(inst, rf, min_width, sdc);
}

void
set_max_area_cmd(float area)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setMaxArea(area, sdc);
}

void
set_port_fanout_limit(Port *port,
                      const MinMax *min_max,
                      float fanout)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setFanoutLimit(port, min_max, fanout, sdc);
}

void
set_cell_fanout_limit(Cell *cell,
                      const MinMax *min_max,
                      float fanout)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setFanoutLimit(cell, min_max, fanout, sdc);
}

void
set_logic_value_cmd(Pin *pin,
                    LogicValue value)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  sta->setLogicValue(pin, value, mode);
}

void
set_case_analysis_cmd(Pin *pin,
                      LogicValue value)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  sta->setCaseAnalysis(pin, value, mode);
}

void
unset_case_analysis_cmd(Pin *pin)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  sta->removeCaseAnalysis(pin, mode);
}

void
set_timing_derate_cmd(TimingDerateType type,
                      PathClkOrData clk_data,
                      const RiseFallBoth *rf,
                      const EarlyLate *early_late,
                      float derate)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setTimingDerate(type, clk_data, rf, early_late, derate, sdc);
}

void
set_timing_derate_net_cmd(const Net *net,
                          PathClkOrData clk_data,
                          const RiseFallBoth *rf,
                          const EarlyLate *early_late,
                          float derate)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setTimingDerate(net, clk_data, rf, early_late, derate, sdc);
}

void
set_timing_derate_inst_cmd(const Instance *inst,
                           TimingDerateCellType type,
                           PathClkOrData clk_data,
                           const RiseFallBoth *rf,
                           const EarlyLate *early_late,
                           float derate)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setTimingDerate(inst, type, clk_data, rf, early_late, derate, sdc);
}

void
set_timing_derate_cell_cmd(const LibertyCell *cell,
                           TimingDerateCellType type,
                           PathClkOrData clk_data,
                           const RiseFallBoth *rf,
                           const EarlyLate *early_late,
                           float derate)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setTimingDerate(cell, type, clk_data, rf, early_late, derate, sdc);
}

void
unset_timing_derate_cmd()
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->unsetTimingDerate(sdc);
}

Clock *
find_clock(const char *name)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  return sdc->findClock(name);
}

bool
is_clock_src(const Pin *pin)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  return sta->isClockSrc(pin, sdc);
}

Clock *
default_arrival_clock()
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  return sdc->defaultArrivalClock();
}

ClockSeq
find_clocks_matching(const char *pattern,
                     bool regexp,
                     bool nocase)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp());
  return sdc->findClocksMatching(&matcher);
}

void
update_generated_clks()
{
  Sta::sta()->updateGeneratedClks();
}

bool
is_clock(Pin *pin)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  return sta->isClock(pin, mode);
}

// variable sta_clock_through_tristate_enabled
bool
clk_thru_tristate_enabled()
{
  return Sta::sta()->clkThruTristateEnabled();
}

// variable sta_clock_through_tristate_enabled
void
set_clk_thru_tristate_enabled(bool enabled)
{
  Sta::sta()->setClkThruTristateEnabled(enabled);
}

PortSeq
all_inputs_cmd(bool no_clocks)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  sta->ensureLinked();
  return sdc->allInputs(no_clocks);
}

PortSeq
all_outputs_cmd()
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  sta->ensureLinked();
  return sdc->allOutputs();
}

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

// all_register variants

InstanceSet
find_register_instances(ClockSet *clks,
                        const RiseFallBoth *clk_tr,
                        bool edge_triggered,
                        bool latches)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  InstanceSet insts = sta->findRegisterInstances(clks, clk_tr,
                                                 edge_triggered,
                                                 latches, mode);
  delete clks;
  return insts;
}

PinSet
find_register_data_pins(ClockSet *clks,
                        const RiseFallBoth *clk_tr,
                        bool edge_triggered,
                        bool latches)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  PinSet pins = sta->findRegisterDataPins(clks, clk_tr, edge_triggered,
                                          latches, mode);
  delete clks;
  return pins;
}

PinSet
find_register_clk_pins(ClockSet *clks,
                       const RiseFallBoth *clk_tr,
                       bool edge_triggered,
                       bool latches)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  PinSet pins = sta->findRegisterClkPins(clks, clk_tr, edge_triggered,
                                         latches, mode);
  delete clks;
  return pins;
}

PinSet
find_register_async_pins(ClockSet *clks,
                         const RiseFallBoth *clk_tr,
                         bool edge_triggered,
                         bool latches)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  PinSet pins = sta->findRegisterAsyncPins(clks, clk_tr, edge_triggered,
                                           latches, mode);
  delete clks;
  return pins;
}

PinSet
find_register_output_pins(ClockSet *clks,
                          const RiseFallBoth *clk_tr,
                          bool edge_triggered,
                          bool latches)
{
  Sta *sta = Sta::sta();
  Mode *mode = sta->cmdMode();
  PinSet pins = sta->findRegisterOutputPins(clks, clk_tr, edge_triggered,
                                            latches, mode);
  delete clks;
  return pins;
}

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

template <typename T> std::vector<T*>
filter_objects(const char *property,
               const char *op,
               const char *pattern,
               std::vector<T*> *objects)
{
  std::vector<T*> filtered_objects;
  if (objects) {
    Sta *sta = Sta::sta();
    Properties &properties = sta->properties();
    bool exact_match = stringEq(op, "==");
    bool pattern_match = stringEq(op, "=~");
    bool not_match = stringEq(op, "!=");
    bool not_pattern_match = stringEq(op, "!~");
    for (T *object : *objects) {
      PropertyValue value(properties.getProperty(object, property));
      std::string prop_str = value.to_string(sta->network());
      const char *prop = prop_str.c_str();
      if (!prop_str.empty()
          && ((exact_match && stringEq(prop, pattern))
              || (not_match && !stringEq(prop, pattern))
              || (pattern_match && patternMatch(pattern, prop))
              || (not_pattern_match && !patternMatch(pattern, prop))))
        filtered_objects.push_back(object);
    }
    delete objects;
  }
  return filtered_objects;
}

PortSeq
filter_ports(const char *property,
             const char *op,
             const char *pattern,
             PortSeq *ports)
{
  return filter_objects<const Port>(property, op, pattern, ports);
}

InstanceSeq
filter_insts(const char *property,
             const char *op,
             const char *pattern,
             InstanceSeq *insts)
{
  return filter_objects<const Instance>(property, op, pattern, insts);
}

PinSeq
filter_pins(const char *property,
            const char *op,
            const char *pattern,
            PinSeq *pins)
{
  return filter_objects<const Pin>(property, op, pattern, pins);
}

ClockSeq
filter_clocks(const char *property,
              const char *op,
              const char *pattern,
              ClockSeq *clocks)
{
  return filter_objects<Clock>(property, op, pattern, clocks);
}

LibertyCellSeq
filter_lib_cells(const char *property,
                 const char *op,
                 const char *pattern,
                 LibertyCellSeq *cells)
{
  return filter_objects<LibertyCell>(property, op, pattern, cells);
}

LibertyPortSeq
filter_lib_pins(const char *property,
                const char *op,
                const char *pattern,
                LibertyPortSeq *pins)
{
  return filter_objects<LibertyPort>(property, op, pattern, pins);
}

LibertyLibrarySeq
filter_liberty_libraries(const char *property,
                         const char *op,
                         const char *pattern,
                         LibertyLibrarySeq *libs)
{
  return filter_objects<LibertyLibrary>(property, op, pattern, libs);
}

NetSeq
filter_nets(const char *property,
            const char *op,
            const char *pattern,
            NetSeq *nets)
{
  return filter_objects<const Net>(property, op, pattern, nets);
}

EdgeSeq
filter_timing_arcs(const char *property,
                   const char *op,
                   const char *pattern,
                   EdgeSeq *edges)
{
  return filter_objects<Edge>(property, op, pattern, edges);
}

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

StringSeq
group_path_names()
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  StringSeq pg_names;
  for (auto const& [name, group] : sdc->groupPaths())
    pg_names.push_back(name);
  return pg_names;
}

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

void
set_voltage_global(const MinMax *min_max,
                   float voltage)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setVoltage(min_max, voltage, sdc);
}

void
set_voltage_net(const Net *net,
                const MinMax *min_max,
                float voltage)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  sta->setVoltage(net, min_max, voltage, sdc);
}

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

char
pin_case_logic_value(const Pin *pin)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  LogicValue value = LogicValue::unknown;
  bool exists;
  sdc->caseLogicValue(pin, value, exists);
  return logicValueString(value);
}

char
pin_logic_value(const Pin *pin)
{
  Sta *sta = Sta::sta();
  const Sdc *sdc = sta->cmdSdc();
  LogicValue value = LogicValue::unknown;
  bool exists;
  sdc->logicValue(pin, value, exists);
  return logicValueString(value);
}

////////////////////////////////////////////////////////////////
//
// Variables
//
////////////////////////////////////////////////////////////////

bool
propagate_all_clocks()
{
  return Sta::sta()->propagateAllClocks();
}

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

bool
pin_is_constrained(const Pin *pin)
{
  Sta *sta = Sta::sta();
  Sdc *sdc = sta->cmdSdc();
  return sdc->isConstrained(pin);
}

%} // inline

////////////////////////////////////////////////////////////////
//
// Object Methods
//
////////////////////////////////////////////////////////////////

%extend Clock {
float period() { return self->period(); }
FloatSeq *waveform() { return self->waveform(); }
float time(RiseFall *rf) { return self->edge(rf)->time(); }
bool is_generated() { return self->isGenerated(); }
bool waveform_valid() { return self->waveformValid(); }
bool is_virtual() { return self->isVirtual(); }
bool is_propagated() { return self->isPropagated(); }
const PinSet &sources() { return self->pins(); }

float
slew(const RiseFall *rf,
     const MinMax *min_max)
{
  return self->slew(rf, min_max);
}

}

%extend ClockEdge {
Clock *clock() { return self->clock(); }
const RiseFall *transition() { return self->transition(); }
float time() { return self->time(); }
}
