// 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.

// Swig TCL input/output type parsers.
%{

#include "Machine.hh"
#include "StringUtil.hh"
#include "PatternMatch.hh"
#include "Network.hh"
#include "Liberty.hh"
#include "FuncExpr.hh"
#include "TimingArc.hh"
#include "TableModel.hh"
#include "TimingRole.hh"
#include "Graph.hh"
#include "NetworkClass.hh"
#include "Clock.hh"
#include "Scene.hh"
#include "Search.hh"
#include "Path.hh"
#include "search/Tag.hh"
#include "PathEnd.hh"
#include "SearchClass.hh"
#include "CircuitSim.hh"
#include "Property.hh"
#include "Sta.hh"
#include "TclTypeHelpers.hh"

namespace sta {

typedef MinMaxAll MinMaxAllNull;

#if TCL_MAJOR_VERSION < 9
    typedef int Tcl_Size;
#endif

template <class TYPE>
std::vector<TYPE> *
tclListSeqPtr(Tcl_Obj *const source,
              swig_type_info *swig_type,
              Tcl_Interp *interp)
{
  Tcl_Size argc;
  Tcl_Obj **argv;

  if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
      && argc > 0) {
    std::vector<TYPE> *seq = new std::vector<TYPE>;
    for (int i = 0; i < argc; i++) {
      void *obj;
      // Ignore returned TCL_ERROR because can't get swig_type_info.
      SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
      seq->push_back(reinterpret_cast<TYPE>(obj));
    }
    return seq;
  }
  else
    return nullptr;
}

template <class TYPE>
std::vector<TYPE>
tclListSeq(Tcl_Obj *const source,
           swig_type_info *swig_type,
           Tcl_Interp *interp)
{
  Tcl_Size argc;
  Tcl_Obj **argv;

  std::vector<TYPE> seq;
  if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
      && argc > 0) {
    for (int i = 0; i < argc; i++) {
      void *obj;
      // Ignore returned TCL_ERROR because can't get swig_type_info.
      SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
      seq.push_back(reinterpret_cast<TYPE>(obj));
    }
  }
  return seq;
}

template <class SET_TYPE, class OBJECT_TYPE>
SET_TYPE *
tclListSetPtr(Tcl_Obj *const source,
              swig_type_info *swig_type,
              Tcl_Interp *interp)
{
  Tcl_Size argc;
  Tcl_Obj **argv;
  if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
      && argc > 0) {
    SET_TYPE *set = new SET_TYPE;
    for (int i = 0; i < argc; i++) {
      void *obj;
      // Ignore returned TCL_ERROR because can't get swig_type_info.
      SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
      set->insert(reinterpret_cast<OBJECT_TYPE*>(obj));
    }
    return set;
  }
  else
    return nullptr;
}

template <class SET_TYPE, class OBJECT_TYPE>
SET_TYPE
tclListSet(Tcl_Obj *const source,
           swig_type_info *swig_type,
           Tcl_Interp *interp)
{
  Tcl_Size argc;
  Tcl_Obj **argv;
  if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
      && argc > 0) {
    SET_TYPE set;
    for (int i = 0; i < argc; i++) {
      void *obj;
      // Ignore returned TCL_ERROR because can't get swig_type_info.
      SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
      set.insert(reinterpret_cast<OBJECT_TYPE>(obj));
    }
    return set;
  }
  else
    return SET_TYPE();
}

template <class SET_TYPE, class OBJECT_TYPE>
SET_TYPE *
tclListNetworkSet(Tcl_Obj *const source,
                  swig_type_info *swig_type,
                  Tcl_Interp *interp,
                  const Network *network)
{
  Tcl_Size argc;
  Tcl_Obj **argv;
  if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
      && argc > 0) {
    SET_TYPE *set = new SET_TYPE(network);
    for (int i = 0; i < argc; i++) {
      void *obj;
      // Ignore returned TCL_ERROR because can't get swig_type_info.
      SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
      set->insert(reinterpret_cast<OBJECT_TYPE*>(obj));
    }
    return set;
  }
  else
    return nullptr;
}

template <class SET_TYPE, class OBJECT_TYPE>
SET_TYPE
tclListNetworkSet1(Tcl_Obj *const source,
                   swig_type_info *swig_type,
                   Tcl_Interp *interp,
                   const Network *network)
{
  Tcl_Size argc;
  Tcl_Obj **argv;
  SET_TYPE set(network);
  if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
      && argc > 0) {
    for (int i = 0; i < argc; i++) {
      void *obj;
      // Ignore returned TCL_ERROR because can't get swig_type_info.
      SWIG_ConvertPtr(argv[i], &obj, swig_type, false);
      set.insert(reinterpret_cast<OBJECT_TYPE*>(obj));
    }
  }
  return set;
}

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

// Sequence out to tcl list.
template <class SEQ_TYPE, class OBJECT_TYPE>
void
seqPtrTclList(SEQ_TYPE *seq,
              swig_type_info *swig_type,
              Tcl_Interp *interp)
{
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  for (const OBJECT_TYPE *obj : *seq) {
    Tcl_Obj *tcl_obj = SWIG_NewInstanceObj(const_cast<OBJECT_TYPE*>(obj),
                                           swig_type, false);
    Tcl_ListObjAppendElement(interp, list, tcl_obj);
  }
  Tcl_SetObjResult(interp, list);
}

template <class SEQ_TYPE, class OBJECT_TYPE>
void
seqTclList(SEQ_TYPE &seq,
           swig_type_info *swig_type,
           Tcl_Interp *interp)
{
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  for (const OBJECT_TYPE *obj : seq) {
    Tcl_Obj *tcl_obj = SWIG_NewInstanceObj(const_cast<OBJECT_TYPE*>(obj),
                                           swig_type, false);
    Tcl_ListObjAppendElement(interp, list, tcl_obj);
  }
  Tcl_SetObjResult(interp, list);
}

template <class SET_TYPE, class OBJECT_TYPE>
void
setTclList(SET_TYPE set,
           swig_type_info *swig_type,
           Tcl_Interp *interp)
{
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  for (const OBJECT_TYPE *obj : set) {
    Tcl_Obj *tcl_obj = SWIG_NewInstanceObj(const_cast<OBJECT_TYPE*>(obj),
                                           swig_type, false);
    Tcl_ListObjAppendElement(interp, list, tcl_obj);
  }
  Tcl_SetObjResult(interp, list);
}

template <class SET_TYPE, class OBJECT_TYPE>
void
setPtrTclList(SET_TYPE *set,
              swig_type_info *swig_type,
              Tcl_Interp *interp)
{
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  for (const OBJECT_TYPE *obj : *set) {
    Tcl_Obj *tcl_obj = SWIG_NewInstanceObj(const_cast<OBJECT_TYPE*>(obj),
                                           swig_type, false);
    Tcl_ListObjAppendElement(interp, list, tcl_obj);
  }
  Tcl_SetObjResult(interp, list);
}

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

} // namespace

using namespace sta;

%}

////////////////////////////////////////////////////////////////
//
// SWIG type definitions.
//
////////////////////////////////////////////////////////////////

// String that is deleted after crossing over to tcland.
%typemap(out) std::string {
  std::string &str = $1;
  // String is volatile because it is deleted.
  Tcl_SetResult(interp, const_cast<char*>(str.c_str()), TCL_VOLATILE);
}

// String that is deleted after crossing over to tcland.
%typemap(out) TmpString* {
  string *str = $1;
  if (str) {
    // String is volatile because it is deleted.
    Tcl_SetResult(interp, const_cast<char*>(str->c_str()), TCL_VOLATILE);
    delete str;
  }
  else
    Tcl_SetResult(interp, nullptr, TCL_STATIC);
}

%typemap(in) StringSet* {
  $1 = tclListSetStdString($input, interp);
}

%typemap(in) StringSeq {
  $1 = tclListSeqStdString($input, interp);
}

%typemap(in) const StringSeq & (StringSeq seq) {
  seq = tclListSeqStdString($input, interp);
  $1 = &seq;
}

%typemap(in) StringSeq* {
  $1 = tclListSeqStdStringPtr($input, interp);
}

%typemap(in) StringSet* {
  $1 = tclListSetStdString($input, interp);
}

%typemap(in) StringSeq {
  $1 = tclListSeqStdString($input, interp);
}

%typemap(out) StringSeq {
  StringSeq &strs = $1;
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  for (const std::string &str : strs) {
    Tcl_Obj *obj = Tcl_NewStringObj(str.c_str(), str.size());
    Tcl_ListObjAppendElement(interp, list, obj);
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(out) Library* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) LibraryIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) LibertyLibraryIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) Cell* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) CellSeq* {
  $1 = tclListSeqPtr<Cell*>($input, SWIGTYPE_p_Cell, interp);
}

%typemap(out) CellSeq {
  seqTclList<CellSeq, Cell>($1, SWIGTYPE_p_Cell, interp);
}

%typemap(in) LibertyCellSeq* {
  $1 = tclListSeqPtr<LibertyCell*>($input, SWIGTYPE_p_LibertyCell, interp);
}

%typemap(out) LibertyCellSeq * {
  seqPtrTclList<LibertyCellSeq, LibertyCell>($1, SWIGTYPE_p_LibertyCell, interp);
}

%typemap(out) LibertyCellSeq {
  seqTclList<LibertyCellSeq, LibertyCell>($1, SWIGTYPE_p_LibertyCell, interp);
}

%typemap(in) LibertyPortSeq* {
  $1 = tclListSeqPtr<LibertyPort*>($input, SWIGTYPE_p_LibertyPort, interp);
}

%typemap(out) LibertyPortSeq {
  seqTclList<LibertyPortSeq, LibertyPort>($1, SWIGTYPE_p_LibertyPort, interp);
}

%typemap(out) CellPortIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) LibertyCellPortIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) Port* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) PortSeq* {
  $1 = tclListSeqPtr<const Port*>($input, SWIGTYPE_p_Port, interp);
}

%typemap(out) PortSeq {
  seqTclList<PortSeq, Port>($1, SWIGTYPE_p_Port, interp);
}

%typemap(out) PortMemberIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) LibertyCell* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) LibertyPort* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) LibertyPortMemberIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, SWIGTYPE_p_LibertyPortMemberIterator, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) TimingArc* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) TimingArcSetSeq& {
  seqPtrTclList<TimingArcSetSeq, TimingArcSet>($1, SWIGTYPE_p_TimingArcSet, interp);
}

%typemap(out) TimingArcSeq& {
  seqPtrTclList<TimingArcSeq, TimingArc>($1, SWIGTYPE_p_TimingArc, interp);
}

%typemap(out) Wireload* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) WireloadSelection* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) Transition* {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  Transition *tr = Transition::find(arg);
  if (tr == nullptr) {
    tclArgError(interp, 2150, "Unknown transition '{}'.", arg);
    return TCL_ERROR;
  }
  else
    $1 = tr;
}

%typemap(out) Transition* {
  Transition *tr = $1;
  const char *str = "";
  if (tr)
    str = tr->to_string().c_str();
  Tcl_SetResult(interp, const_cast<char*>(str), TCL_STATIC);
}

%typemap(in) RiseFall* {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  const RiseFall *rf = RiseFall::find(arg);
  if (rf == nullptr) {
    tclArgError(interp, 2151, "Unknown rise/fall edge '{}'.", arg);
    return TCL_ERROR;
  }
  // Swig is retarded and drops const on args.
  $1 = const_cast<RiseFall*>(rf);
}

%typemap(out) RiseFall* {
  const RiseFall *rf = $1;
  const char *str = "";
  if (rf)
    str = rf->shortName();
  Tcl_SetResult(interp, const_cast<char*>(str), TCL_STATIC);
}

%typemap(in) RiseFallBoth* {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  const RiseFallBoth *rf = RiseFallBoth::find(arg);
  if (rf == nullptr) {
    tclArgError(interp, 2152, "Unknown transition name '{}'.", arg);
    return TCL_ERROR;
  }
  // Swig is retarded and drops const on args.
  $1 = const_cast<RiseFallBoth*>(rf);
}

%typemap(out) RiseFallBoth* {
  RiseFallBoth *tr = $1;
  const char *str = "";
  if (tr)
    str = tr->shortName();
  Tcl_SetResult(interp, const_cast<char*>(str), TCL_STATIC);
}

%typemap(in) PortDirection* {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  PortDirection *dir = PortDirection::find(arg);
  if (dir == nullptr) {
    tclArgError(interp, 2153, "Unknown port direction '{}'.", arg);
    return TCL_ERROR;
  }
  else
    $1 = dir;
 }

%typemap(in) TimingRole* {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  const TimingRole *role = TimingRole::find(arg);
  if (role)
    // Swig is retarded and drops const on args.
    $1 = const_cast<TimingRole*>(TimingRole::find(arg));
  else {
    tclArgError(interp, 2154, "Unknown timing role '{}'.", arg);
    return TCL_ERROR;
  }
}

%typemap(out) TimingRole* {
  Tcl_SetResult(interp, const_cast<char*>($1->to_string().c_str()), TCL_STATIC);
}

%typemap(in) LogicValue {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEq(arg, "0") || stringEq(arg, "zero"))
    $1 = LogicValue::zero;
  else if (stringEq(arg, "1") || stringEq(arg, "one"))
    $1 = LogicValue::one;
  else if (stringEq(arg, "X"))
    $1 = LogicValue::unknown;
  else if (stringEq(arg, "rise") || stringEq(arg, "rising"))
    $1 = LogicValue::rise;
  else if (stringEq(arg, "fall") || stringEq(arg, "falling"))
    $1 = LogicValue::fall;
  else {
    tclArgError(interp, 2155, "Unknown logic value '{}'.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) AnalysisType {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEqual(arg, "single"))
    $1 = AnalysisType::single;
  else if (stringEqual(arg, "bc_wc"))
    $1 = AnalysisType::bc_wc;
  else if (stringEq(arg, "on_chip_variation"))
    $1 = AnalysisType::ocv;
  else {
    tclArgError(interp, 2156, "Unknown analysis type '{}'.", arg);
    return TCL_ERROR;
  }
}

%typemap(out) Instance* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) InstanceSeq* {
  $1 = tclListSeqPtr<const Instance*>($input, SWIGTYPE_p_Instance, interp);
}

%typemap(in) InstanceSeq {
  $1 = tclListSeq<const Instance*>($input, SWIGTYPE_p_Instance, interp);
 }

%typemap(out) InstanceSeq {
  seqTclList<InstanceSeq, Instance>($1, SWIGTYPE_p_Instance, interp);
}

%typemap(out) InstanceChildIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) LeafInstanceIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) InstancePinIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) InstanceNetIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) LibertyLibrarySeq* {
  $1 = tclListSeqPtr<LibertyLibrary*>($input, SWIGTYPE_p_LibertyLibrary, interp);
}

%typemap(out) LibertyLibrarySeq {
  seqTclList<LibertyLibrarySeq, LibertyLibrary>($1, SWIGTYPE_p_LibertyLibrary, interp);
}

%typemap(out) Pin* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) PinSeq* {
  seqPtrTclList<PinSeq, Pin>($1, SWIGTYPE_p_Pin, interp);
}

%typemap(out) PinSeq {
  seqTclList<PinSeq, Pin>($1, SWIGTYPE_p_Pin, interp);
}

%typemap(out) Net* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) NetSeq* {
  $1 = tclListSeqPtr<const Net*>($input, SWIGTYPE_p_Net, interp);
}

%typemap(out) NetSeq* {
  seqPtrTclList<NetSeq, Net>($1, SWIGTYPE_p_Net, interp);
}

%typemap(in) ConstNetSeq {
  $1 = tclListSeq<const Net*>($input, SWIGTYPE_p_Net, interp);
}

%typemap(out) NetSeq {
  seqTclList<NetSeq, Net>($1, SWIGTYPE_p_Net, interp);
}

%typemap(out) NetPinIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) NetTermIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) NetConnectedPinIterator* {
  Tcl_Obj *obj=SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) PinConnectedPinIterator* {
  Tcl_Obj *obj=SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) Clock* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) ConstClockSeq {
  $1 = tclListSeq<const Clock*>($input, SWIGTYPE_p_Clock, interp);
}

%typemap(in) ClockSeq* {
  $1 = tclListSeqPtr<Clock*>($input, SWIGTYPE_p_Clock, interp);
}

%typemap(out) ClockSeq* {
  seqPtrTclList<ClockSeq, Clock>($1, SWIGTYPE_p_Clock, interp);
}

%typemap(out) ClockSeq {
  seqTclList<ClockSeq, Clock>($1, SWIGTYPE_p_Clock, interp);
}

%typemap(out) ClockEdge* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1,$1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) PinSeq* {
  $1 = tclListSeqPtr<const Pin*>($input, SWIGTYPE_p_Pin, interp);
}

%typemap(in) PinSet {
  Network *network = Sta::sta()->ensureLinked();
  $1 = tclListNetworkSet1<PinSet, Pin>($input, SWIGTYPE_p_Pin, interp, network);
}

%typemap(in) PinSet* {
  Network *network = Sta::sta()->ensureLinked();
  $1 = tclListNetworkSet<PinSet, Pin>($input, SWIGTYPE_p_Pin, interp, network);
}

%typemap(out) PinSet* {
  setPtrTclList<PinSet, Pin>($1, SWIGTYPE_p_Pin, interp);
}

%typemap(out) PinSet {
  setTclList<PinSet, Pin>($1, SWIGTYPE_p_Pin, interp);
}

%typemap(out) const PinSet& {
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  // A swig bug sets the result to PinSet* rather than const PinSet&.
  PinSet *pins = $1;
  for (const Pin *pin : *pins) {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Pin*>(pin), SWIGTYPE_p_Pin, false);
    Tcl_ListObjAppendElement(interp, list, obj);
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(in) ConstClockSet {
  $1 = tclListSet<ConstClockSet, Clock>($input, SWIGTYPE_p_Clock, interp);
}

%typemap(in) ClockSet* {
  $1 = tclListSetPtr<ClockSet, Clock>($input, SWIGTYPE_p_Clock, interp);
}

%typemap(out) ClockSet* {
  setPtrTclList<ClockSet, Clock>($1, SWIGTYPE_p_Clock, interp);
}

%typemap(in) InstanceSet* {
  Network *network = Sta::sta()->ensureLinked();
  $1 = tclListNetworkSet<InstanceSet, Instance>($input, SWIGTYPE_p_Instance,
                                                interp, network);
}

%typemap(out) InstanceSet {
  setTclList<InstanceSet, Instance>($1, SWIGTYPE_p_Instance, interp);
}

%typemap(in) NetSet* {
  Network *network = Sta::sta()->ensureLinked();
  $1 = tclListNetworkSet<NetSet, Net>($input, SWIGTYPE_p_Net, interp, network);
}

%typemap(in) FloatSeq* {
  Tcl_Size argc;
  Tcl_Obj **argv;
  FloatSeq *floats = nullptr;

  if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK) {
    if (argc)
      floats = new FloatSeq;
    for (int i = 0; i < argc; i++) {
      char *arg = Tcl_GetString(argv[i]);
      double value;
      if (Tcl_GetDouble(interp, arg, &value) == TCL_OK)
        floats->push_back(static_cast<float>(value));
      else {
        delete floats;
        tclArgError(interp, 2157, "{} is not a floating point number.", arg);
        return TCL_ERROR;
      }
    }
  }
  $1 = floats;
}

%typemap(out) FloatSeq* {
  FloatSeq *floats = $1;
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  if (floats) {
    for (float f : *floats) {
      Tcl_Obj *obj = Tcl_NewDoubleObj(f);
      Tcl_ListObjAppendElement(interp, list, obj);
    }
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(out) FloatSeq {
  FloatSeq &floats = $1;
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  for (float f : floats) {
    Tcl_Obj *obj = Tcl_NewDoubleObj(f);
    Tcl_ListObjAppendElement(interp, list, obj);
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(in) IntSeq* {
  Tcl_Size argc;
  Tcl_Obj **argv;
  IntSeq *ints = nullptr;

  if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK) {
    if (argc)
      ints = new IntSeq;
    for (int i = 0; i < argc; i++) {
      char *arg = Tcl_GetString(argv[i]);
      int value;
      if (Tcl_GetInt(interp, arg, &value) == TCL_OK)
        ints->push_back(value);
      else {
        delete ints;
        tclArgError(interp, 2158, "{} is not an integer.", arg);
        return TCL_ERROR;
      }
    }
  }
  $1 = ints;
}

%typemap(out) Table {
  Table &table = $1;
  if (table.axis1()) {
    Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr);
    Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr);
    for (float f : table.axis1()->values()) {
      Tcl_Obj *obj = Tcl_NewDoubleObj(f);
      Tcl_ListObjAppendElement(interp, list1, obj);
    }
    Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr);
    if (table.values()) {
      for (float f : *table.values()) {
        Tcl_Obj *obj = Tcl_NewDoubleObj(f);
        Tcl_ListObjAppendElement(interp, list2, obj);
      }
    }
    Tcl_ListObjAppendElement(interp, list3, list1);
    Tcl_ListObjAppendElement(interp, list3, list2);
    Tcl_SetObjResult(interp, list3);
  }
}

%typemap(out) const Table* {
  const Table *table = $1;
  Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr);
  if (table) {
    Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr);
    if (table->axis1()) {
      for (float f : table->axis1()->values()) {
        Tcl_Obj *obj = Tcl_NewDoubleObj(f);
        Tcl_ListObjAppendElement(interp, list1, obj);
      }
    }
    Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr);
    if (table->values()) {
      for (float f : *table->values()) {
        Tcl_Obj *obj = Tcl_NewDoubleObj(f);
        Tcl_ListObjAppendElement(interp, list2, obj);
      }
    }
    Tcl_ListObjAppendElement(interp, list3, list1);
    Tcl_ListObjAppendElement(interp, list3, list2);
  }
  Tcl_SetObjResult(interp, list3);
}

%typemap(in) MinMax* {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  // Swig is retarded and drops const on args.
  MinMax *min_max = const_cast<MinMax*>(MinMax::find(arg));
  if (min_max)
    $1 = min_max;
  else {
    tclArgError(interp, 2159, "{} not min or max.", arg);
    return TCL_ERROR;
  }
}

%typemap(out) MinMax* {
  Tcl_SetResult(interp, const_cast<char*>($1->to_string().c_str()), TCL_STATIC);
}

%typemap(out) MinMax* {
  Tcl_SetResult(interp, const_cast<char*>($1->to_string().c_str()), TCL_STATIC);
}

%typemap(in) MinMaxAll* {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  // Swig is retarded and drops const on args.
  MinMaxAll *min_max = const_cast<MinMaxAll*>(MinMaxAll::find(arg));
  if (min_max)
    $1 = min_max;
  else {
    tclArgError(interp, 2160, "{} not min, max or min_max.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) MinMaxAllNull* {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEqual(arg, "NULL"))
    $1 = nullptr;
  else {
    // Swig is retarded and drops const on args.
    MinMaxAll *min_max = const_cast<MinMaxAll*>(MinMaxAll::find(arg));
    if (min_max)
      $1 = min_max;
    else {
      tclArgError(interp, 2161, "{} not min, max or min_max.", arg);
      return TCL_ERROR;
    }
  }
}

%typemap(out) MinMaxAll* {
  Tcl_SetResult(interp, const_cast<char*>($1->asString()), TCL_STATIC);
}

// SetupHold is typedef'd to MinMax.
%typemap(in) const SetupHold* {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  // Swig is retarded and drops const on args.
  if (stringEqual(arg, "hold")
      || stringEqual(arg, "min"))
    $1 = const_cast<MinMax*>(MinMax::min());
  else if (stringEqual(arg, "setup")
           || stringEqual(arg, "max"))
    $1 = const_cast<MinMax*>(MinMax::max());
  else {
    tclArgError(interp, 2162, "{} not setup, hold, min or max.", arg);
    return TCL_ERROR;
  }
}

// SetupHoldAll is typedef'd to MinMaxAll.
%typemap(in) const SetupHoldAll* {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  // Swig is retarded and drops const on args.
  if (stringEqual(arg, "hold")
      || stringEqual(arg, "min"))
    $1 = const_cast<SetupHoldAll*>(SetupHoldAll::min());
  else if (stringEqual(arg, "setup")
           || stringEqual(arg, "max"))
    $1 = const_cast<SetupHoldAll*>(SetupHoldAll::max());
  else if (stringEqual(arg, "setup_hold")
           || stringEqual(arg, "min_max"))
    $1 = const_cast<SetupHoldAll*>(SetupHoldAll::all());
  else {
    tclArgError(interp, 2163, "{} not setup, hold, setup_hold, min, max or min_max.",
                arg);
    return TCL_ERROR;
  }
}

// EarlyLate is typedef'd to MinMax.
%typemap(in) const EarlyLate* {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  // Swig is retarded and drops const on args.
  EarlyLate *early_late = const_cast<EarlyLate*>(EarlyLate::find(arg));
  if (early_late)
    $1 = early_late;
  else {
    tclArgError(interp, 2164, "{} not early/min, late/max or early_late/min_max.", arg);
    return TCL_ERROR;
  }
}

// EarlyLateAll is typedef'd to MinMaxAll.
%typemap(in) const EarlyLateAll* {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  // Swig is retarded and drops const on args.
  EarlyLateAll *early_late = const_cast<EarlyLateAll*>(EarlyLateAll::find(arg));
  if (early_late)
    $1 = early_late;
  else {
    tclArgError(interp, 2165, "{} not early/min, late/max or early_late/min_max.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) TimingDerateType {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEq(arg, "net_delay"))
    $1 = TimingDerateType::net_delay;
  else if (stringEq(arg, "cell_delay"))
    $1 = TimingDerateType::cell_delay;
  else if (stringEq(arg, "cell_check"))
    $1 = TimingDerateType::cell_check;
  else {
    tclArgError(interp, 2166, "{} not net_delay, cell_delay or cell_check.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) TimingDerateCellType {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEq(arg, "cell_delay"))
    $1 = TimingDerateCellType::cell_delay;
  else if (stringEq(arg, "cell_check"))
    $1 = TimingDerateCellType::cell_check;
  else {
    tclArgError(interp, 2167, "{} not cell_delay or cell_check.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) PathClkOrData {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEq(arg, "clk"))
    $1 = PathClkOrData::clk;
  else if (stringEq(arg, "data"))
    $1 = PathClkOrData::data;
  else {
    tclArgError(interp, 2168, "{} not clk or data.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) ReportSortBy {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEq(arg, "group"))
    $1 = sort_by_group;
  else if (stringEq(arg, "slack"))
    $1 = sort_by_slack;
  else {
    tclArgError(interp, 2169, "{} not group or slack.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) ReportPathFormat {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEq(arg, "full"))
    $1 = ReportPathFormat::full;
  else if (stringEq(arg, "full_clock"))
    $1 = ReportPathFormat::full_clock;
  else if (stringEq(arg, "full_clock_expanded"))
    $1 = ReportPathFormat::full_clock_expanded;
  else if (stringEq(arg, "short"))
    $1 = ReportPathFormat::shorter;
  else if (stringEq(arg, "end"))
    $1 = ReportPathFormat::endpoint;
  else if (stringEq(arg, "summary"))
    $1 = ReportPathFormat::summary;
  else if (stringEq(arg, "slack_only"))
    $1 = ReportPathFormat::slack_only;
  else if (stringEq(arg, "json"))
    $1 = ReportPathFormat::json;
  else {
    tclArgError(interp, 2170, "unknown path type {}.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) ExceptionThruSeq* {
  $1 = tclListSeqPtr<ExceptionThru*>($input, SWIGTYPE_p_ExceptionThru, interp);
}

%typemap(out) Vertex* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) Vertex** {
  int i = 0;
  Tcl_ResetResult(interp);
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  while ($1[i]) {
    Tcl_Obj *obj = SWIG_NewInstanceObj($1[i], SWIGTYPE_p_Vertex,false);
    Tcl_ListObjAppendElement(interp, list, obj);
    i++;
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(out) Edge* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) EdgeSeq* {
  $1 = tclListSeqPtr<Edge*>($input, SWIGTYPE_p_Edge, interp);
}

%typemap(out) EdgeSeq {
  seqTclList<EdgeSeq, Edge>($1, SWIGTYPE_p_Edge, interp);
}

%typemap(out) VertexIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) VertexInEdgeIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) VertexOutEdgeIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) CheckErrorSeq & {
  Tcl_Obj *error_list = Tcl_NewListObj(0, nullptr);
  CheckErrorSeq *check_errors = $1;
  for (CheckError *error : *check_errors) {
    Tcl_Obj *string_list = Tcl_NewListObj(0, nullptr);
    for (const std::string &str : *error) {
      Tcl_Obj *obj = Tcl_NewStringObj(str.c_str(),
                                      static_cast<int>(str.size()));
      Tcl_ListObjAppendElement(interp, string_list, obj);
    }
    Tcl_ListObjAppendElement(interp, error_list, string_list);
  }
  Tcl_SetObjResult(interp, error_list);
}

%typemap(out) PathEnd* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) PathEndSeq* {
  $1 = tclListSeqPtr<PathEnd*>($input, SWIGTYPE_p_PathEnd, interp);
}

%typemap(out) PathEndSeq* {
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  const PathEndSeq *path_ends = $1;
  PathEndSeq::ConstIterator end_iter(path_ends);
  while (end_iter.hasNext()) {
    PathEnd *path_end = end_iter.next();
    Tcl_Obj *obj = SWIG_NewInstanceObj(path_end, SWIGTYPE_p_PathEnd, false);
    Tcl_ListObjAppendElement(interp, list, obj);
  }
  // Delete the PathEndSeq, not the ends.
  delete path_ends;
  Tcl_SetObjResult(interp, list);
}

%typemap(out) PathEndSeq {
  seqTclList<PathEndSeq, PathEnd>($1, SWIGTYPE_p_PathEnd, interp);
}

%typemap(out) PathSeq* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);

  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  PathSeq *paths = $1;
  PathSeq::Iterator path_iter(paths);
  while (path_iter.hasNext()) {
    Path *path = &path_iter.next();
    Path *copy = new Path(path);
    Tcl_Obj *obj = SWIG_NewInstanceObj(copy, SWIGTYPE_p_Path, false);
    Tcl_ListObjAppendElement(interp, list, obj);
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(out) VertexPathIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) SlowDrvrIterator* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) ExceptionFrom* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) ExceptionTo* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) ExceptionThru* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(out) OperatingConditions* {
  Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
  Tcl_SetObjResult(interp, obj);
}

%typemap(in) StringSet* {
  $1 = tclListSetConstChar($input, interp);
}

%typemap(out) Mode* {
  const Mode *mode = $1;
  if (mode)
    Tcl_SetResult(interp, const_cast<char*>($1->name().c_str()), TCL_VOLATILE);
  else
    Tcl_SetResult(interp, const_cast<char*>("NULL"), TCL_STATIC);
}

%typemap(in) ModeSeq {
  Tcl_Size argc;
  Tcl_Obj **argv;

  Sta *sta = Sta::sta();
  std::vector<Mode*> seq;
  if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK
      && argc > 0) {
    for (int i = 0; i < argc; i++) {
      int length;
      const char *mode_name = Tcl_GetStringFromObj(argv[i], &length);
      Mode *mode = sta->findMode(mode_name);
      if (mode)
        seq.push_back(mode);
      else {
        tclArgError(interp, 2174, "mode {} not found.", mode_name);
        return TCL_ERROR;
      }
    }
  }
  $1 = seq;
}

%typemap(out) ModeSeq {
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  ModeSeq &modes = $1;
  for (Mode *mode : modes) {
    const std::string &mode_name = mode->name();
    Tcl_Obj *obj = Tcl_NewStringObj(mode_name.c_str(), mode_name.size());
    Tcl_ListObjAppendElement(interp, list, obj);
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(in) Scene* {
  sta::Sta *sta = Sta::sta();
  int length;
  char *scene_name = Tcl_GetStringFromObj($input, &length);
  // parse_scene_or_all support depreated 11/21/2025
  if (stringEq(scene_name, "NULL"))
    $1 = nullptr;
  else {
    Scene *scene = sta->findScene(scene_name);
    if (scene)
      $1 = scene;
    else {
      tclArgError(interp, 2173, "scene {} not found.", scene_name);
      return TCL_ERROR;
    }
  }
}

%typemap(out) Scene* {
  const Scene *scene = $1;
  if (scene)
    Tcl_SetResult(interp, const_cast<char*>($1->name().c_str()), TCL_VOLATILE);
  else
    Tcl_SetResult(interp, const_cast<char*>("NULL"), TCL_STATIC);
}

%typemap(in) SceneSeq {
  Tcl_Size argc;
  Tcl_Obj **argv;

  Sta *sta = Sta::sta();
  std::vector<Scene*> seq;
  if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK
      && argc > 0) {
    for (int i = 0; i < argc; i++) {
      int length;
      const char *scene_name = Tcl_GetStringFromObj(argv[i], &length);
      Scene *scene = sta->findScene(scene_name);
      if (scene)
        seq.push_back(scene);
      else {
        tclArgError(interp, 2172, "scene {} not found.", scene_name);
        return TCL_ERROR;
      }
    }
  }
  $1 = seq;
}

%typemap(out) SceneSeq {
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  SceneSeq &scenes = $1;
  for (Scene *scene : scenes) {
    const std::string &scene_name = scene->name();
    Tcl_Obj *obj = Tcl_NewStringObj(scene_name.c_str(), scene_name.size());
    Tcl_ListObjAppendElement(interp, list, obj);
  }
  Tcl_SetObjResult(interp, list);
}

%typemap(in) PropertyValue {
  int length;
  const char *arg = Tcl_GetStringFromObj($input, &length);
  $1 = PropertyValue(arg);
}

%typemap(out) PropertyValue {
  PropertyValue value = $1;
  switch (value.type()) {
  case PropertyValue::Type::none:
    Tcl_SetResult(interp, const_cast<char*>(""), TCL_STATIC);
    break;
  case PropertyValue::Type::string:
    Tcl_SetResult(interp, const_cast<char*>(value.stringValue()), TCL_VOLATILE);
    break;
  case PropertyValue::Type::float_: {
    const Unit *unit = value.unit();
    std::string float_string = unit->asString(value.floatValue(), 6);
    Tcl_SetResult(interp, const_cast<char*>(float_string.c_str()), TCL_VOLATILE);
  }
    break;
  case PropertyValue::Type::bool_: {
    const char *bool_string = value.boolValue() ? "1" : "0";
    Tcl_SetResult(interp, const_cast<char*>(bool_string), TCL_STATIC);
  }
    break;
  case PropertyValue::Type::library: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Library*>(value.library()),
                                       SWIGTYPE_p_Library, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::cell: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Cell*>(value.cell()),
                                       SWIGTYPE_p_Cell, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::port: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Port*>(value.port()),
                                       SWIGTYPE_p_Port, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::liberty_library: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<LibertyLibrary*>(value.libertyLibrary()),
                                       SWIGTYPE_p_LibertyLibrary, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::liberty_cell: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<LibertyCell*>(value.libertyCell()),
                                       SWIGTYPE_p_LibertyCell, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::liberty_port: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<LibertyPort*>(value.libertyPort()),
                                       SWIGTYPE_p_LibertyPort, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::instance: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Instance*>(value.instance()),
                                       SWIGTYPE_p_Instance, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::pin: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Pin*>(value.pin()),
                                       SWIGTYPE_p_Pin, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::pins: {
    Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
    for (const Pin *pin : *value.pins()) {
      Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Pin*>(pin), SWIGTYPE_p_Pin, false);
      Tcl_ListObjAppendElement(interp, list, obj);
    }
    Tcl_SetObjResult(interp, list);
  }
    break;
  case PropertyValue::Type::net: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Net*>(value.net()),
                                       SWIGTYPE_p_Net, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::clk: {
    Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Clock*>(value.clock()),
                                       SWIGTYPE_p_Clock, false);
    Tcl_SetObjResult(interp, obj);
  }
    break;
  case PropertyValue::Type::clks: {
    Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
    ClockSeq *clks = value.clocks();
    for (Clock *clk : *clks) {
      Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false);
      Tcl_ListObjAppendElement(interp, list, obj);
    }
    Tcl_SetObjResult(interp, list);
  }
    break;
  case PropertyValue::Type::paths: {
    Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
    for (const Path *path : *value.paths()) {
      Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast<Path*>(path), SWIGTYPE_p_Path, false);
      Tcl_ListObjAppendElement(interp, list, obj);
    }
    Tcl_SetObjResult(interp, list);
  }
    break;
  case PropertyValue::Type::pwr_activity: {
    PwrActivity activity = value.pwrActivity();
    Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
    Tcl_Obj *obj;

    std::string density = sta::format("{:.5e}", activity.density());
    obj = Tcl_NewStringObj(density.c_str(), density.size());
    Tcl_ListObjAppendElement(interp, list, obj);

    std::string duty = sta::format("{:.3f}", activity.duty());
    obj = Tcl_NewStringObj(duty.c_str(), duty.size());
    Tcl_ListObjAppendElement(interp, list, obj);

    std::string name = activity.originName();
    obj = Tcl_NewStringObj(name.c_str(), name.size());
    Tcl_ListObjAppendElement(interp, list, obj);

    Tcl_SetObjResult(interp, list);
  }
    break;
  }
}

%typemap(in) CircuitSim {
  int length;
  char *arg = Tcl_GetStringFromObj($input, &length);
  if (stringEq(arg, "hspice"))
    $1 = CircuitSim::hspice;
  else if (stringEq(arg, "ngspice"))
    $1 = CircuitSim::ngspice;
  else if (stringEq(arg, "xyce"))
    $1 = CircuitSim::xyce;
  else {
    tclArgError(interp, 2171, "unknown circuit simulator {}.", arg);
    return TCL_ERROR;
  }
}

%typemap(in) ArcDcalcArg {
  Tcl_Obj *const source = $input;
  $1 = arcDcalcArgTcl(source, interp);
}

%typemap(out) ArcDcalcArg {
  Tcl_Obj *tcl_obj = tclArcDcalcArg($1, interp);
  Tcl_SetObjResult(interp, tcl_obj);
}

%typemap(in) ArcDcalcArgSeq {
  Tcl_Obj *const source = $input;
  Tcl_Size argc;
  Tcl_Obj **argv;

  Sta *sta = Sta::sta();
  ArcDcalcArgSeq seq;
  if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
      && argc > 0) {
    for (int i = 0; i < argc; i++) {
      ArcDcalcArg gate = arcDcalcArgTcl(argv[i], interp);
      if (gate.drvrPin())
        seq.push_back(gate);
    }
  }
  $1 = seq;
}

%typemap(out) ArcDcalcArgSeq {
  Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
  for (ArcDcalcArg &gate : $1) {
    Tcl_Obj *tcl_obj = tclArcDcalcArg(gate, interp);
    Tcl_ListObjAppendElement(interp, list, tcl_obj);
  }
  Tcl_SetObjResult(interp, list);
}
