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

#include <string>
#include <string_view>
#include <utility>

#include "ConcreteLibrary.hh"
#include "FuncExpr.hh"
#include "Liberty.hh"
#include "LibertyClass.hh"
#include "PortDirection.hh"
#include "Transition.hh"
#include "Sequential.hh"
#include "TableModel.hh"
#include "TimingArc.hh"
#include "TimingModel.hh"
#include "TimingRole.hh"

namespace sta {

LibertyBuilder::LibertyBuilder(Debug *debug,
                               Report *report) :
  debug_(debug),
  report_(report)
{
}

LibertyCell *
LibertyBuilder::makeCell(LibertyLibrary *library,
                         std::string_view name,
                         std::string_view filename)
{
  LibertyCell *cell = new LibertyCell(library, std::string(name), std::string(filename));
  library->addCell(cell);
  return cell;
}

LibertyPort *
LibertyBuilder::makePort(LibertyCell *cell,
                         std::string_view port_name)
{
  LibertyPort *port = new LibertyPort(cell, std::string(port_name), false, nullptr,
                                      -1, -1, false, nullptr);
  cell->addPort(port);
  return port;
}

LibertyPort *
LibertyBuilder::makeBusPort(LibertyCell *cell,
                            std::string_view bus_name,
                            int from_index,
                            int to_index,
                            BusDcl *bus_dcl)
{
  LibertyPort *port = new LibertyPort(cell, std::string(bus_name), true, bus_dcl,
                                      from_index, to_index,
                                      false, new ConcretePortSeq);
  cell->addPort(port);
  makeBusPortBits(cell->library(), cell, port, bus_name, from_index, to_index);
  return port;
}

void
LibertyBuilder::makeBusPortBits(ConcreteLibrary *library,
                                LibertyCell *cell,
                                ConcretePort *bus_port,
                                std::string_view bus_name,
                                int from_index,
                                int to_index)
{
  if (from_index < to_index) {
    for (int index = from_index; index <= to_index; index++)
      makeBusPortBit(library, cell, bus_port, bus_name, index);
  }
  else {
    for (int index = from_index; index >= to_index; index--)
      makeBusPortBit(library, cell, bus_port, bus_name, index);
  }
}

void
LibertyBuilder::makeBusPortBit(ConcreteLibrary *library,
                               LibertyCell *cell,
                               ConcretePort *bus_port,
                               std::string_view bus_name,
                               int bit_index)
{
  std::string bit_name;
  bit_name.append(bus_name);
  bit_name += library->busBrktLeft();
  bit_name += std::to_string(bit_index);
  bit_name += library->busBrktRight();
  LibertyPort *port = makePort(cell, std::move(bit_name), bit_index);
  bus_port->addPortBit(port);
  cell->addPortBit(port);
}

LibertyPort *
LibertyBuilder::makePort(LibertyCell *cell,
                         std::string_view bit_name,
                         int bit_index)
{
  LibertyPort *port = new LibertyPort(cell, bit_name, false, nullptr,
                                      bit_index, bit_index, false, nullptr);
  return port;
}

LibertyPort *
LibertyBuilder::makeBundlePort(LibertyCell *cell,
                               std::string_view name,
                               ConcretePortSeq *members)
{
  LibertyPort *port = new LibertyPort(cell, name, false, nullptr, -1, -1, true, members);
  cell->addPort(port);
  for (ConcretePort *member : *members)
    member->setBundlePort(port);
  return port;
}

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

TimingArcSet *
LibertyBuilder::makeTimingArcs(LibertyCell *cell,
                               LibertyPort *from_port,
                               LibertyPort *to_port,
                               LibertyPort *related_out,
                               const TimingArcAttrsPtr &attrs)
{
  FuncExpr *to_func = to_port->function();
  Sequential *seq = (to_func && to_func->port())
    ? cell->outputPortSequential(to_func->port())
    : nullptr;
  TimingType timing_type = attrs->timingType();
  // Register/latch timing group missing timing_type.
  if (attrs->timingType() == TimingType::combinational
      && seq) {
    if (seq->clock() && seq->clock()->hasPort(from_port)) {
      switch (seq->clock()->portTimingSense(from_port)) {
      case TimingSense::positive_unate:
        timing_type = TimingType::rising_edge;
        break;
      case TimingSense::negative_unate:
        timing_type = TimingType::rising_edge;
        break;
      default:
        break;
      }
    }
    else if (seq->clear() && seq->clear()->hasPort(from_port)) {
      timing_type = TimingType::clear;
      if (attrs->timingSense() == TimingSense::unknown) {
        // Missing timing_sense also.
        TimingSense timing_sense = seq->clear()->portTimingSense(from_port);
        attrs->setTimingSense(timing_sense);
      }
    }
    else if (seq->preset() && seq->preset()->hasPort(from_port)) {
      timing_type = TimingType::preset;
      if (attrs->timingSense() == TimingSense::unknown) {
        // Missing timing_sense also.
        TimingSense timing_sense = seq->preset()->portTimingSense(from_port);
        attrs->setTimingSense(timing_sense);
      }
    }
  }
  switch (timing_type) {
  case TimingType::combinational:
    if (seq
        && seq->isLatch()
        && seq->data()
        && seq->data()->hasPort(from_port))
      // Latch D->Q timing arcs.
      return makeLatchDtoQArcs(cell, from_port, to_port,
                               seq->data()->portTimingSense(from_port), attrs);
    else
      return makeCombinationalArcs(cell, from_port, to_port, true, true, attrs);
  case TimingType::combinational_fall:
    return makeCombinationalArcs(cell, from_port, to_port, false, true, attrs);
  case TimingType::combinational_rise:
    return makeCombinationalArcs(cell, from_port, to_port, true, false, attrs);
  case TimingType::setup_rising:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::rise(), TimingRole::setup(),
                                  attrs);
  case TimingType::setup_falling:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::fall(), TimingRole::setup(),
                                  attrs);
  case TimingType::hold_rising:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::rise(), TimingRole::hold(),
                                  attrs);
  case TimingType::hold_falling:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::fall(), TimingRole::hold(),
                                  attrs);
  case TimingType::rising_edge:
    return makeRegLatchArcs(cell, from_port, to_port, RiseFall::rise(), attrs);
  case TimingType::falling_edge:
    return makeRegLatchArcs(cell, from_port, to_port, RiseFall::fall(), attrs);
  case TimingType::preset:
    return makePresetClrArcs(cell, from_port, to_port, RiseFall::rise(), attrs);
  case TimingType::clear:
    return makePresetClrArcs(cell, from_port, to_port, RiseFall::fall(), attrs);
  case TimingType::recovery_rising:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::rise(),TimingRole::recovery(),
                                  attrs);
  case TimingType::recovery_falling:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::fall(),TimingRole::recovery(),
                                  attrs);
  case TimingType::removal_rising:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::rise(), TimingRole::removal(),
                                  attrs);
  case TimingType::removal_falling:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::fall(), TimingRole::removal(),
                                  attrs);
  case TimingType::three_state_disable:
    return makeTristateDisableArcs(cell, from_port, to_port, true, true, attrs);
  case TimingType::three_state_disable_fall:
    return makeTristateDisableArcs(cell, from_port, to_port, false, true, attrs);
  case TimingType::three_state_disable_rise:
    return makeTristateDisableArcs(cell, from_port, to_port, true, false, attrs);
  case TimingType::three_state_enable:
    return makeTristateEnableArcs(cell, from_port, to_port, true, true, attrs);
  case TimingType::three_state_enable_fall:
    return makeTristateEnableArcs(cell, from_port, to_port, false, true, attrs);
  case TimingType::three_state_enable_rise:
    return makeTristateEnableArcs(cell, from_port, to_port, true, false, attrs);
  case TimingType::skew_falling:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::fall(), TimingRole::skew(),
                                  attrs);
  case TimingType::skew_rising:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::rise(), TimingRole::skew(),
                                  attrs);
  case TimingType::non_seq_setup_rising:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::rise(), TimingRole::nonSeqSetup(),
                                  attrs);
  case TimingType::non_seq_setup_falling:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::fall(), TimingRole::nonSeqSetup(),
                                  attrs);
  case TimingType::non_seq_hold_rising:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::rise(), TimingRole::nonSeqHold(),
                                  attrs);
  case TimingType::non_seq_hold_falling:
    return makeFromTransitionArcs(cell, from_port, to_port, related_out,
                                  RiseFall::fall(), TimingRole::nonSeqHold(),
                                  attrs);
  case TimingType::min_clock_tree_path:
    return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMin(), attrs);
  case TimingType::max_clock_tree_path:
    return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(), attrs);
  case TimingType::min_pulse_width:
    return makeMinPulseWidthArcs(cell, from_port, to_port, related_out,
                                 TimingRole::width(), attrs);
  case TimingType::minimum_period:
    return makeMinPulseWidthArcs(cell, from_port, to_port, related_out,
                                 TimingRole::period(), attrs);
  case TimingType::nochange_high_high:
  case TimingType::nochange_high_low:
  case TimingType::nochange_low_high:
  case TimingType::nochange_low_low:
  case TimingType::retaining_time:
  case TimingType::unknown:
    return nullptr;
  }
  // Prevent warnings from lame compilers.
  return nullptr;
}

TimingArcSet *
LibertyBuilder::makeCombinationalArcs(LibertyCell *cell,
                                      LibertyPort *from_port,
                                      LibertyPort *to_port,
                                      bool to_rise,
                                      bool to_fall,
                                      const TimingArcAttrsPtr &attrs)
{
  FuncExpr *func = to_port->function();
  FuncExpr *enable = to_port->tristateEnable();
  TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr,
                                                 TimingRole::combinational(),
                                                 attrs);
  TimingSense sense = attrs->timingSense();
  if (sense == TimingSense::unknown) {
    // Timing sense not specified - find it from function.
    if (func && func->hasPort(from_port))
      sense = func->portTimingSense(from_port);
    // Check tristate enable.
    else if (to_port->direction()->isAnyTristate()
             && enable
             && enable->hasPort(from_port))
      sense = TimingSense::non_unate;
    // Don't warn for functions that reference ff/latch/lut internal ports.
    //else if (func->port() && !func->port()->direction()->isInternal())
    //  report_->fileWarn(172, cell->filename(), line,
    //                    "timing sense cannot be inferred because pin function does not reference related pin %s.",
    //                   from_port->name());
  }

  TimingModel *model;
  const RiseFall *to_rf;
  switch (sense) {
  case TimingSense::positive_unate:
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, RiseFall::rise(), to_rf, model);
    }
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, RiseFall::fall(), to_rf, model);
    }
    break;
  case TimingSense::negative_unate:
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, RiseFall::rise(), to_rf, model);
    }
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, RiseFall::fall(), to_rf, model);
    }
    break;
  case TimingSense::non_unate:
  case TimingSense::unknown:
  case TimingSense::none:
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model) {
        makeTimingArc(arc_set, RiseFall::fall(), to_rf, model);
        makeTimingArc(arc_set, RiseFall::rise(), to_rf, model);
      }
    }
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model) {
        makeTimingArc(arc_set, RiseFall::rise(), to_rf, model);
        makeTimingArc(arc_set, RiseFall::fall(), to_rf, model);
      }
    }
    break;
  }
  return arc_set;
}

TimingArcSet *
LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell,
                                  LibertyPort *from_port,
                                  LibertyPort *to_port,
                                  TimingSense sense,
                                  const TimingArcAttrsPtr &attrs)
{
  TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr,
                                                 TimingRole::latchDtoQ(),
                                                 attrs);
  TimingModel *model;
  const RiseFall *to_rf = RiseFall::rise();
  model = attrs->model(to_rf);
  if (model) {
    const RiseFall *from_rf = (sense == TimingSense::negative_unate)
      ? to_rf->opposite()
      : to_rf;
    makeTimingArc(arc_set, from_rf, to_rf, model);
  }
  to_rf = RiseFall::fall();
  model = attrs->model(to_rf);
  if (model) {
    const RiseFall *from_rf = (sense == TimingSense::negative_unate)
      ? to_rf->opposite()
      : to_rf;
    makeTimingArc(arc_set, from_rf, to_rf, model);
  }
  return arc_set;
}

TimingArcSet *
LibertyBuilder::makeRegLatchArcs(LibertyCell *cell,
                                 LibertyPort *from_port,
                                 LibertyPort *to_port,
                                 const RiseFall *from_rf,
                                 const TimingArcAttrsPtr &attrs)
{
  FuncExpr *to_func = to_port->function();
  if (to_func) {
    LibertyPortSet to_ports = to_func->ports();
    for (LibertyPort *func_port : to_ports) {
      Sequential *seq = cell->outputPortSequential(func_port);
      if (seq) {
        if (seq->clock() && seq->clock()->hasPort(from_port)) {
          const TimingRole *role = seq->isRegister() ?
            TimingRole::regClkToQ() : TimingRole::latchEnToQ();
          return makeFromTransitionArcs(cell, from_port, to_port, nullptr,
                                        from_rf, role, attrs);
        }
        else if (seq->isLatch()
                 && seq->data()
                 && seq->data()->hasPort(from_port))
          return makeFromTransitionArcs(cell, from_port, to_port, nullptr,
                                        from_rf, TimingRole::latchDtoQ(), attrs);
        else if ((seq->clear() && seq->clear()->hasPort(from_port))
                 || (seq->preset() && seq->preset()->hasPort(from_port)))
          return makeFromTransitionArcs(cell, from_port, to_port, nullptr,
                                        from_rf, TimingRole::regSetClr(), attrs);
      }
    }
  }
  // No associated ff/latch - assume register clk->q.
  cell->setHasInferedRegTimingArcs(true);
  return makeFromTransitionArcs(cell, from_port, to_port, nullptr,
                                from_rf, TimingRole::regClkToQ(), attrs);
}

TimingArcSet *
LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell,
                                       LibertyPort *from_port,
                                       LibertyPort *to_port,
                                       LibertyPort *related_out,
                                       const RiseFall *from_rf,
                                       const TimingRole *role,
                                       const TimingArcAttrsPtr &attrs)
{
  TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port,
                                                 related_out, role, attrs);
  for (auto to_rf : RiseFall::range()) {
    TimingModel *model = attrs->model(to_rf);
    if (model)
      makeTimingArc(arc_set, from_rf, to_rf, model);
  }
  return arc_set;
}

TimingArcSet *
LibertyBuilder::makePresetClrArcs(LibertyCell *cell,
                                  LibertyPort *from_port,
                                  LibertyPort *to_port,
                                  const RiseFall *to_rf,
                                  const TimingArcAttrsPtr &attrs)
{
  TimingArcSet *arc_set = nullptr;
  TimingModel *model = attrs->model(to_rf);
  if (model) {
    arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr,
                                     TimingRole::regSetClr(), attrs);
    const RiseFall *opp_rf = to_rf->opposite();
    switch (attrs->timingSense()) {
    case TimingSense::positive_unate:
      makeTimingArc(arc_set, to_rf, to_rf, model);
      break;
    case TimingSense::negative_unate:
      makeTimingArc(arc_set, opp_rf, to_rf, model);
      break;
    case TimingSense::non_unate:
    case TimingSense::unknown:
      makeTimingArc(arc_set, to_rf, to_rf, model);
      makeTimingArc(arc_set, opp_rf, to_rf, model);
      break;
    case TimingSense::none:
      break;
    }
  }
  return arc_set;
}

// To rise/fall for Z transitions is as follows:
//  0Z, Z1 rise
//  1Z, Z0 fall
TimingArcSet *
LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell,
                                       LibertyPort *from_port,
                                       LibertyPort *to_port,
                                       bool to_rise,
                                       bool to_fall,
                                       const TimingArcAttrsPtr &attrs)
{
  TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr,
                                                 TimingRole::tristateEnable(),
                                                 attrs);
  FuncExpr *tristate_enable = to_port->tristateEnable();
  TimingSense sense = attrs->timingSense();
  if (sense == TimingSense::unknown && tristate_enable)
    sense = tristate_enable->portTimingSense(from_port);
  TimingModel *model;
  const RiseFall *to_rf;
  switch (sense) {
  case TimingSense::positive_unate:
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model);
    }
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model);
    }
    break;
  case TimingSense::negative_unate:
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model);
    }
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model);
    }
    break;
  case TimingSense::non_unate:
  case TimingSense::unknown:
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model) {
        makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model);
        makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model);
      }
    }
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model) {
        makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model);
        makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model);
      }
    }
    break;
  case TimingSense::none:
    break;
  }
  return arc_set;
}

TimingArcSet *
LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell,
                                        LibertyPort *from_port,
                                        LibertyPort *to_port,
                                        bool to_rise,
                                        bool to_fall,
                                        const TimingArcAttrsPtr &attrs)
{
  TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr,
                                                 TimingRole::tristateDisable(),
                                                 attrs);
  TimingSense sense = attrs->timingSense();
  FuncExpr *tristate_enable = to_port->tristateEnable();
  if (sense == TimingSense::unknown && tristate_enable)
    sense = timingSenseOpposite(tristate_enable->portTimingSense(from_port));
  TimingModel *model;
  const RiseFall *to_rf;
  switch (sense) {
  case TimingSense::positive_unate:
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model);
    }
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model);
    }
    break;
  case TimingSense::negative_unate:
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model);
    }
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model)
        makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model);
    }
    break;
  case TimingSense::non_unate:
  case TimingSense::unknown:
    if (to_rise) {
      to_rf = RiseFall::rise();
      model = attrs->model(to_rf);
      if (model) {
        makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model);
        makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model);
      }
    }
    if (to_fall) {
      to_rf = RiseFall::fall();
      model = attrs->model(to_rf);
      if (model) {
        makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model);
        makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model);
      }
    }
    break;
  case TimingSense::none:
    break;
  }
  return arc_set;
}

TimingArcSet *
LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell,
                                      LibertyPort *to_port,
                                      const TimingRole *role,
                                      const TimingArcAttrsPtr &attrs)
{
  TimingArcSet *arc_set = cell->makeTimingArcSet(nullptr, to_port, nullptr,
                                                 role, attrs);
  for (const RiseFall *to_rf : RiseFall::range()) {
    TimingModel *model = attrs->model(to_rf);
    if (model) {
      const RiseFall *opp_rf = to_rf->opposite();
      switch (attrs->timingSense()) {
      case TimingSense::positive_unate:
        makeTimingArc(arc_set, to_rf, to_rf, model);
        break;
      case TimingSense::negative_unate:
        makeTimingArc(arc_set, opp_rf, to_rf, model);
        break;
      case TimingSense::non_unate:
      case TimingSense::unknown:
        makeTimingArc(arc_set, to_rf, to_rf, model);
        makeTimingArc(arc_set, opp_rf, to_rf, model);
        break;
      case TimingSense::none:
        break;
      }
    }
  }
  return arc_set;
}

TimingArcSet *
LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell,
                                      LibertyPort *from_port,
                                      LibertyPort *to_port,
                                      LibertyPort *related_out,
                                      const TimingRole *role,
                                      const TimingArcAttrsPtr &attrs)
{
  if (from_port == nullptr)
    from_port = to_port;
  TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, related_out,
                                                 role, attrs);
  for (const RiseFall *from_rf : RiseFall::range()) {
    TimingModel *model = attrs->model(from_rf);
    if (model)
      makeTimingArc(arc_set, from_rf, from_rf->opposite(), model);
  }
  return arc_set;
}

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

TimingArc *
LibertyBuilder::makeTimingArc(TimingArcSet *set,
                              const RiseFall *from_rf,
                              const RiseFall *to_rf,
                              TimingModel *model)
{
  return new TimingArc(set, from_rf->asTransition(),
                       to_rf->asTransition(), model);
}

TimingArc *
LibertyBuilder::makeTimingArc(TimingArcSet *set,
                              const Transition *from_rf,
                              const Transition *to_rf,
                              TimingModel *model)
{
  return new TimingArc(set, from_rf, to_rf, model);
}

} // namespace sta
