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

#include "TimingArc.hh"

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

#include "ContainerHelpers.hh"
#include "Delay.hh"
#include "EnumNameMap.hh"
#include "Error.hh"
#include "FuncExpr.hh"
#include "LibertyClass.hh"
#include "Liberty.hh"
#include "MinMax.hh"
#include "Sdc.hh"
#include "TableModel.hh"
#include "TimingModel.hh"
#include "TimingRole.hh"
#include "Transition.hh"

namespace sta {

static bool
timingArcsEquiv(const TimingArcSet *set1,
                const TimingArcSet *set2);
static bool
timingArcsLess(const TimingArcSet *set1,
               const TimingArcSet *set2);

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

TimingArcAttrs::TimingArcAttrs() :
  timing_type_(TimingType::combinational),
  timing_sense_(TimingSense::unknown),
  cond_(nullptr),
  ocv_arc_depth_(0.0),
  models_{nullptr, nullptr}
{
}

TimingArcAttrs::TimingArcAttrs(TimingSense sense) :
  timing_type_(TimingType::combinational),
  timing_sense_(sense),
  cond_(nullptr),
  ocv_arc_depth_(0.0),
  models_{nullptr, nullptr}
{
}

TimingArcAttrs::~TimingArcAttrs()
{
  delete cond_;
  delete models_[RiseFall::riseIndex()];
  delete models_[RiseFall::fallIndex()];
}

void
TimingArcAttrs::setTimingType(TimingType type)
{
  timing_type_ = type;
}

void
TimingArcAttrs::setTimingSense(TimingSense sense)
{
  timing_sense_ = sense;
}

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

void
TimingArcAttrs::setSdfCond(std::string_view cond)
{
  sdf_cond_ = cond;
  sdf_cond_start_ = sdf_cond_end_ = sdf_cond_;
}

void
TimingArcAttrs::setSdfCondStart(std::string_view cond)
{
  sdf_cond_start_ = cond;
}

void
TimingArcAttrs::setSdfCondEnd(std::string_view cond)
{
  sdf_cond_end_ = cond;
}

void
TimingArcAttrs::setModeName(std::string_view name)
{
  mode_name_ = name;
}

void
TimingArcAttrs::setModeValue(std::string_view value)
{
  mode_value_ = value;
}

TimingModel *
TimingArcAttrs::model(const RiseFall *rf) const
{
  return models_[rf->index()];
}

void
TimingArcAttrs::setModel(const RiseFall *rf,
                         TimingModel *model)
{
  models_[rf->index()] = model;
}

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

float
TimingArc::driveResistance() const
{
  GateTimingModel *model = dynamic_cast<GateTimingModel*>(model_);
  if (model)
    return model->driveResistance(nullptr);
  else
    return 0.0;
}

ArcDelay
TimingArc::intrinsicDelay() const
{
  GateTimingModel *model = dynamic_cast<GateTimingModel*>(model_);
  if (model) {
    float arc_delay, slew;
    model->gateDelay(nullptr, 0.0, 0.0, arc_delay, slew);
    return arc_delay;
  }
  else
    return 0.0;
}

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

TimingArcAttrsPtr TimingArcSet::wire_timing_arc_attrs_ = nullptr;
TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr;
TimingArcSet *TimingArcSet::port_refpin_timing_arc_set_ = nullptr;

TimingArcSet::TimingArcSet(LibertyCell *,
                           LibertyPort *from,
                           LibertyPort *to,
                           LibertyPort *related_out,
                           const TimingRole *role,
                           TimingArcAttrsPtr attrs,
                           size_t index) :
  from_(from),
  to_(to),
  related_out_(related_out),
  role_(role),
  attrs_(std::move(attrs)),
  is_cond_default_(false),
  index_(index),
  from_arc1_{nullptr, nullptr},
  from_arc2_{nullptr, nullptr},
  to_arc_{nullptr, nullptr}
{
}

TimingArcSet::TimingArcSet(const TimingRole *role,
                           TimingArcAttrsPtr attrs) :
  from_(nullptr),
  to_(nullptr),
  related_out_(nullptr),
  role_(role),
  attrs_(std::move(attrs)),
  is_cond_default_(false),
  index_(0),
  from_arc1_{nullptr, nullptr},
  from_arc2_{nullptr, nullptr},
  to_arc_{nullptr, nullptr}
{
}

std::string
TimingArcSet::to_string() const
{
  std::string str = from_->name();
  str += " -> ";
  str += to_->name();
  return str;
}

TimingArcSet::~TimingArcSet()
{
  deleteContents(arcs_);
}

bool
TimingArcSet::isWire() const
{
  return this == wire_timing_arc_set_;
}

LibertyCell *
TimingArcSet::libertyCell() const
{
  if (from_)
    return from_->libertyCell();
  else
    // Wire timing arc set.
    return nullptr;
}

size_t
TimingArcSet::addTimingArc(TimingArc *arc)
{
  size_t arc_index = arcs_.size();
  // Rise/fall to rise/fall.
  if (arc_index > static_cast<size_t>(RiseFall::index_count)
                      * RiseFall::index_count)
    criticalError(243, "timing arc max index exceeded\n");
  arcs_.push_back(arc);

  if (arc->fromEdge()) {
    int from_rf_index = arc->fromEdge()->asRiseFall()->index();
    if (from_arc1_[from_rf_index] == nullptr)
      from_arc1_[from_rf_index] = arc;
    else if (from_arc2_[from_rf_index] == nullptr)
      from_arc2_[from_rf_index] = arc;
  }

  int to_rf_index = arc->toEdge()->asRiseFall()->index();
  to_arc_[to_rf_index] = arc;

  return arc_index;
}

void
TimingArcSet::deleteTimingArc(TimingArc *arc)
{
  TimingArc *last_arc = arcs_.back();
  if (arc == last_arc)
    arcs_.pop_back();
  else {
    last_arc->setIndex(arc->index());
    arcs_[arc->index()] = last_arc;
    arcs_.pop_back();
  }
  int from_rf_index = arc->fromEdge()->asRiseFall()->index();
  if (from_arc1_[from_rf_index] == arc) {
    from_arc1_[from_rf_index] = from_arc2_[from_rf_index];
    from_arc2_[from_rf_index] = nullptr;
  }
  else if (from_arc2_[from_rf_index] == arc)
    from_arc2_[from_rf_index] = nullptr;
  delete arc;
}

TimingArc *
TimingArcSet::findTimingArc(unsigned arc_index)
{
  return arcs_[arc_index];
}

void
TimingArcSet::setRole(const TimingRole *role)
{
  role_ = role;
}

void
TimingArcSet::setIndex(size_t index)
{
  index_ = index;
}

void
TimingArcSet::setIsCondDefault(bool is_default)
{
  is_cond_default_ = is_default;
}

void
TimingArcSet::arcsFrom(const RiseFall *from_rf,
                       // Return values.
                       TimingArc *&arc1,
                       TimingArc *&arc2) const
{
  int rf_index = from_rf->index();
  arc1 = from_arc1_[rf_index];
  arc2 = from_arc2_[rf_index];
}

TimingArc *
TimingArcSet::arcTo(const RiseFall *to_rf) const
{
  return to_arc_[to_rf->index()];
}

TimingSense
TimingArcSet::sense() const
{
  return attrs_->timingSense();
}

const RiseFall *
TimingArcSet::isRisingFallingEdge() const
{
  int arc_count = arcs_.size();
  if (arc_count == 2) {
    const RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall();
    const RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall();
    if (from_rf1 == from_rf2)
      return from_rf1;
  }
  if (arc_count == 1)
    return arcs_[0]->fromEdge()->asRiseFall();
  else
    return nullptr;
}

float
TimingArcSet::ocvArcDepth() const
{
  if (from_) {
    float depth = attrs_->ocvArcDepth();
    if (depth != 0.0)
      return depth;
    else {
      LibertyCell *cell = from_->libertyCell();
      depth = cell->ocvArcDepth();
      if (depth != 0.0)
        return depth;
      else {
        depth = cell->libertyLibrary()->ocvArcDepth();
        if (depth != 0.0)
          return depth;
      }
    }
  }
  // Wire timing arc set.
  return 1.0;
}

bool
TimingArcSet::equiv(const TimingArcSet *set1,
                    const TimingArcSet *set2)
{
  return LibertyPort::equiv(set1->from(), set2->from())
    && LibertyPort::equiv(set1->to(), set2->to())
    && set1->role() == set2->role()
    && FuncExpr::equiv(set1->cond(), set2->cond())
    && set1->sdfCond() == set2->sdfCond()
    && set1->sdfCondStart() == set2->sdfCondStart()
    && set1->sdfCondEnd() == set2->sdfCondEnd()
    && timingArcsEquiv(set1, set2);
}

static bool
timingArcsEquiv(const TimingArcSet *arc_set1,
                const TimingArcSet *arc_set2)
{
  const TimingArcSeq &arcs1 = arc_set1->arcs();
  const TimingArcSeq &arcs2 = arc_set2->arcs();
  if (arcs1.size() != arcs2.size())
    return false;
  auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin();
  for (;
       arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end();
       arc_itr1++, arc_itr2++) {
    const TimingArc *arc1 = *arc_itr1;
    const TimingArc *arc2 = *arc_itr2;
    if (!TimingArc::equiv(arc1, arc2))
      return false;
  }
  return true;
}

bool
TimingArcSet::less(const TimingArcSet *set1,
                   const TimingArcSet *set2)
{
  return timingArcSetLess(set1, set2);
}

bool
timingArcSetLess(const TimingArcSet *set1,
                 const TimingArcSet *set2)
{
  LibertyPort *from1 = set1->from();
  LibertyPort *from2 = set2->from();
  if (LibertyPort::equiv(from1, from2)) {
    LibertyPort *to1 = set1->to();
    LibertyPort *to2 = set2->to();
    if (LibertyPort::equiv(to1, to2)) {
      const TimingRole *role1 = set1->role();
      const TimingRole *role2 = set2->role();
      if (role1 == role2) {
        const FuncExpr *cond1 = set1->cond();
        const FuncExpr *cond2 = set2->cond();
        if (FuncExpr::equiv(cond1, cond2)) {
          const std::string &sdf_cond1 = set1->sdfCond();
          const std::string &sdf_cond2 = set2->sdfCond();
          if (sdf_cond1 == sdf_cond2) {
            const std::string &sdf_cond_start1 = set1->sdfCondStart();
            const std::string &sdf_cond_start2 = set2->sdfCondStart();
            if (sdf_cond_start1 == sdf_cond_start2) {
              const std::string &sdf_cond_end1 = set1->sdfCondEnd();
              const std::string &sdf_cond_end2 = set2->sdfCondEnd();
              if (sdf_cond_end1 == sdf_cond_end2) {
                const std::string &mode_name1 = set1->modeName();
                const std::string &mode_name2 = set2->modeName();
                if (mode_name1 == mode_name2) {
                  const std::string &mode_value1 = set1->modeValue();
                  const std::string &mode_value2 = set2->modeValue();
                  if (mode_value1 == mode_value2)
                    return timingArcsLess(set1, set2);
                  else
                    return mode_value1 < mode_value2;
                }
                else
                  return mode_name1 < mode_name2;
              }
              else
                return sdf_cond_end1 < sdf_cond_end2;
            }
            else
              return sdf_cond_start1 < sdf_cond_start2;
          }
          else
            return sdf_cond1 < sdf_cond2;
        }
        else
          return FuncExpr::less(cond1, cond2);
      }
      else
        return TimingRole::less(role1, role2);
    }
    else
      return LibertyPort::less(to1, to2);
  }
  else
    return LibertyPort::less(from1, from2);
}

static bool
timingArcsLess(const TimingArcSet *arc_set1,
               const TimingArcSet *arc_set2)
{
  const TimingArcSeq &arcs1 = arc_set1->arcs();
  const TimingArcSeq &arcs2 = arc_set2->arcs();
  if (arcs1.size() < arcs2.size())
    return true;
  if (arcs1.size() > arcs2.size())
    return false;
  auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin();
  for (;
       arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end();
       arc_itr1++, arc_itr2++) {
    const TimingArc *arc1 = *arc_itr1;
    const TimingArc *arc2 = *arc_itr2;
    int from_index1 = arc1->fromEdge() ? arc1->fromEdge()->index() : -1;
    int from_index2 = arc2->fromEdge() ? arc2->fromEdge()->index() : -1;
    if (from_index1 < from_index2)
      return true;
    if (from_index1 > from_index2)
      return false;
    // from_index1 == from_index2
    int to_index1 = arc1->toEdge()->index();
    int to_index2 = arc2->toEdge()->index();
    if (to_index1 < to_index2)
      return true;
    if (to_index1 > to_index2)
      return false;
    // Continue if arc transitions are equal.
  }
  return false;
}

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

int
TimingArcSet::wireArcIndex(const RiseFall *rf)
{
  return rf->index();
}

void
TimingArcSet::init()
{
  wire_timing_arc_attrs_ = std::make_shared<TimingArcAttrs>(TimingSense::positive_unate);
  wire_timing_arc_set_ = new TimingArcSet(TimingRole::wire(), wire_timing_arc_attrs_);
  new TimingArc(wire_timing_arc_set_, Transition::rise(),
                Transition::rise(), nullptr);
  new TimingArc(wire_timing_arc_set_, Transition::fall(),
                Transition::fall(), nullptr);

  port_refpin_timing_arc_set_ = new TimingArcSet(TimingRole::portDelayRefPin(),
                                                 wire_timing_arc_attrs_);
}

void
TimingArcSet::destroy()
{
  delete wire_timing_arc_set_;
  wire_timing_arc_set_ = nullptr;
  wire_timing_arc_attrs_ = nullptr;

  delete port_refpin_timing_arc_set_;
  port_refpin_timing_arc_set_ = nullptr;
}

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

TimingArc::TimingArc(TimingArcSet *set,
                     const Transition *from_rf,
                     const Transition *to_rf,
                     TimingModel *model) :
  set_(set),
  from_rf_(from_rf),
  to_rf_(to_rf),
  model_(model)
{
  index_ = set->addTimingArc(this);
}

TimingArc::~TimingArc()
{
  // The models referenced by scaled_models_ are owned by the scaled
  // cells and are deleted by ~LibertyCell.
  delete scaled_models_;
}

std::string
TimingArc::to_string() const
{
  if (set_->role()->isWire()) {
    std::string str = "wire ";
    str += from_rf_->to_string();
    str += " -> ";
    str += to_rf_->to_string();
    return str;
  }
  else {
    std::string str = set_->from()->name();
    str += " ";
    str += from_rf_->to_string();
    str += " -> ";
    str += set_->to()->name();
    str += " ";
    str += to_rf_->to_string();
    return str;
  }
}

GateTimingModel *
TimingArc::gateModel(const Scene *scene,
                     const MinMax *min_max) const
{
  return dynamic_cast<GateTimingModel*>(model(scene, min_max));
}

GateTableModel *
TimingArc::gateTableModel() const
{
  return dynamic_cast<GateTableModel*>(model_);
}

GateTableModel *
TimingArc::gateTableModel(const Scene *scene,
                          const MinMax *min_max) const
{
  return dynamic_cast<GateTableModel*>(model(scene, min_max));
}

CheckTimingModel *
TimingArc::checkModel(const Scene *scene,
                      const MinMax *min_max) const
{
  return dynamic_cast<CheckTimingModel*>(model(scene, min_max));
}

TimingModel *
TimingArc::model(const Scene *scene,
                 const MinMax *min_max) const
{
  const TimingArc *scene_arc = sceneArc(scene->libertyIndex(min_max));
  ScaledTimingModelMap *scaled_models = scene_arc->scaled_models_;
  if (scaled_models) {
    const OperatingConditions *op_cond =
      scene->sdc()->operatingConditions(min_max);
    TimingModel *scaled_model = findKey(*scaled_models, op_cond);
    if (scaled_model)
      return scaled_model;
  }
  return scene_arc->model();
}

void
TimingArc::addScaledModel(const OperatingConditions *op_cond,
                          TimingModel *scaled_model)
{
  if (scaled_models_ == nullptr)
    scaled_models_ = new ScaledTimingModelMap;
  (*scaled_models_)[op_cond] = scaled_model;
}

bool
TimingArc::equiv(const TimingArc *arc1,
                 const TimingArc *arc2)
{
  return arc1->fromEdge() == arc2->fromEdge()
    && arc1->toEdge() == arc2->toEdge();
}

void
TimingArc::setIndex(size_t index)
{
  index_ = index;
}

const TimingArc *
TimingArc::sceneArc(size_t lib_ap_index) const
{
  if (lib_ap_index < scene_arcs_.size()) {
    TimingArc *scene_arc = scene_arcs_[lib_ap_index];
    if (scene_arc)
      return scene_arc;
  }
  return this;
}

void
TimingArc::setSceneArc(TimingArc *scene_arc,
                        size_t lib_ap_index)
{
  if (lib_ap_index >= scene_arcs_.size())
    scene_arcs_.resize(lib_ap_index + 1);
  scene_arcs_[lib_ap_index] = scene_arc;
}

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

TimingSense
TimingArc::sense() const
{
  if ((from_rf_ == Transition::rise()
       && to_rf_ == Transition::rise())
      || (from_rf_ == Transition::fall()
          && to_rf_ == Transition::fall()))
    return TimingSense::positive_unate;
  else if ((from_rf_ == Transition::rise()
       && to_rf_ == Transition::fall())
      || (from_rf_ == Transition::fall()
          && to_rf_ == Transition::rise()))
    return TimingSense::negative_unate;
  else
    return TimingSense::non_unate;
}

static EnumNameMap<TimingSense> timing_sense_name_map =
  {{TimingSense::positive_unate, "positive_unate"},
   {TimingSense::negative_unate, "negative_unate"},
   {TimingSense::non_unate, "non_unate"},
   {TimingSense::none, "none"},
   {TimingSense::unknown, "unknown"}
  };

const std::string &
to_string(TimingSense sense)
{
  return timing_sense_name_map.find(sense);
}

TimingSense
timingSenseOpposite(TimingSense sense)
{
  switch (sense) {
  case TimingSense::positive_unate:
    return TimingSense::negative_unate;
  case TimingSense::negative_unate:
    return TimingSense::positive_unate;
  case TimingSense::non_unate:
    return TimingSense::non_unate;
  case TimingSense::unknown:
    return TimingSense::unknown;
  case TimingSense::none:
    return TimingSense::none;
  }
  // Prevent warnings from lame compilers.
  return TimingSense::unknown;
}

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

// Same order as enum TimingType.
EnumNameMap<TimingType> timing_type_name_map =
  {{TimingType::clear, "clear"},
   {TimingType::combinational, "combinational"},
   {TimingType::combinational_fall, "combinational_fall"},
   {TimingType::combinational_rise, "combinational_rise"},
   {TimingType::falling_edge, "falling_edge"},
   {TimingType::hold_falling, "hold_falling"},
   {TimingType::hold_rising, "hold_rising"},
   {TimingType::min_pulse_width, "min_pulse_width"},
   {TimingType::minimum_period, "minimum_period"},
   {TimingType::nochange_high_high, "nochange_high_high"},
   {TimingType::nochange_high_low, "nochange_high_low"},
   {TimingType::nochange_low_high, "nochange_low_high"},
   {TimingType::nochange_low_low, "nochange_low_low"},
   {TimingType::non_seq_hold_falling, "non_seq_hold_falling"},
   {TimingType::non_seq_hold_rising, "non_seq_hold_rising"},
   {TimingType::non_seq_setup_falling, "non_seq_setup_falling"},
   {TimingType::non_seq_setup_rising, "non_seq_setup_rising"},
   {TimingType::preset, "preset"},
   {TimingType::recovery_falling, "recovery_falling"},
   {TimingType::recovery_rising, "recovery_rising"},
   {TimingType::removal_falling, "removal_falling"},
   {TimingType::removal_rising, "removal_rising"},
   {TimingType::retaining_time, "retaining_time"},
   {TimingType::rising_edge, "rising_edge"},
   {TimingType::setup_falling, "setup_falling"},
   {TimingType::setup_rising, "setup_rising"},
   {TimingType::skew_falling, "skew_falling"},
   {TimingType::skew_rising, "skew_rising"},
   {TimingType::three_state_disable, "three_state_disable"},
   {TimingType::three_state_disable_fall, "three_state_disable_fall"},
   {TimingType::three_state_disable_rise, "three_state_disable_rise"},
   {TimingType::three_state_enable, "three_state_enable"},
   {TimingType::three_state_enable_fall, "three_state_enable_fall"},
   {TimingType::three_state_enable_rise, "three_state_enable_rise"},
   {TimingType::min_clock_tree_path, "min_clock_tree_path"},
   {TimingType::max_clock_tree_path, "max_clock_tree_path"},
   {TimingType::unknown, "unknown"}
  };

std::string_view
timingTypeString(TimingType type)
{
  return timing_type_name_map.find(type);
}

TimingType
findTimingType(std::string_view type_name)
{
  return timing_type_name_map.find(type_name, TimingType::unknown);
}

bool
timingTypeIsCheck(TimingType type)
{
  switch (type) {
  case TimingType::hold_falling:
  case TimingType::hold_rising:
  case TimingType::min_pulse_width:
  case TimingType::minimum_period:
  case TimingType::nochange_high_high:
  case TimingType::nochange_high_low:
  case TimingType::nochange_low_high:
  case TimingType::nochange_low_low:
  case TimingType::non_seq_hold_falling:
  case TimingType::non_seq_hold_rising:
  case TimingType::non_seq_setup_falling:
  case TimingType::non_seq_setup_rising:
  case TimingType::recovery_falling:
  case TimingType::recovery_rising:
  case TimingType::removal_falling:
  case TimingType::removal_rising:
  case TimingType::retaining_time:
  case TimingType::setup_falling:
  case TimingType::setup_rising:
  case TimingType::skew_falling:
  case TimingType::skew_rising:
    return true;
  default:
    return false;
  }
}

ScaleFactorType
timingTypeScaleFactorType(TimingType type)
{
  switch (type) {
  case TimingType::non_seq_setup_falling:
  case TimingType::non_seq_setup_rising:
  case TimingType::setup_falling:
  case TimingType::setup_rising:
    return ScaleFactorType::setup;
  case TimingType::hold_falling:
  case TimingType::hold_rising:
  case TimingType::non_seq_hold_falling:
  case TimingType::non_seq_hold_rising:
    return ScaleFactorType::hold;
  case TimingType::recovery_falling:
  case TimingType::recovery_rising:
    return ScaleFactorType::recovery;
  case TimingType::removal_falling:
  case TimingType::removal_rising:
    return ScaleFactorType::removal;
  case TimingType::skew_falling:
  case TimingType::skew_rising:
    return ScaleFactorType::skew;
  case TimingType::minimum_period:
    return ScaleFactorType::min_period;
  case TimingType::nochange_high_high:
  case TimingType::nochange_high_low:
  case TimingType::nochange_low_high:
  case TimingType::nochange_low_low:
    return ScaleFactorType::nochange;
  case TimingType::min_pulse_width:
    return ScaleFactorType::min_pulse_width;
  case TimingType::clear:
  case TimingType::combinational:
  case TimingType::combinational_fall:
  case TimingType::combinational_rise:
  case TimingType::falling_edge:
  case TimingType::preset:
  case TimingType::retaining_time:
  case TimingType::rising_edge:
  case TimingType::three_state_disable:
  case TimingType::three_state_disable_fall:
  case TimingType::three_state_disable_rise:
  case TimingType::three_state_enable:
  case TimingType::three_state_enable_fall:
  case TimingType::three_state_enable_rise:
  case TimingType::min_clock_tree_path:
  case TimingType::max_clock_tree_path:
    return ScaleFactorType::cell;
  case TimingType::unknown:
    return ScaleFactorType::unknown;
  }
  return ScaleFactorType::unknown;
}

} // namespace sta
