text: #pragma once
  
  #include <vector>
  #include <cstring>
  #include <algorithm>
  
  #include "rr_graph.h"
  #include "rr_graph_uxsdcxx_interface.h"
  #include "rr_node.h"
  #include "vpr_error.h"
  #include "rr_metadata.h"
  #include "read_xml_arch_file.h"
  #include "vtr_log.h"
  #include "vtr_version.h"
  #include "vpr_utils.h"
  #include "check_rr_graph.h"
  #include "rr_graph2.h"
  #include "rr_graph_indexed_data.h"
  
  class MetadataBind {
    public:
      MetadataBind(vtr::string_internment* strings, vtr::interned_string empty)
          : is_node_(false)
          , is_edge_(false)
          , ignore_(false)
          , inode_(OPEN)
          , sink_node_(OPEN)
          , switch_id_(OPEN)
          , strings_(strings)
          , name_(empty)
          , value_(empty) {}
  
      ~MetadataBind() {
          assert_clear();
          VTR_ASSERT(!is_node_);
          VTR_ASSERT(!is_edge_);
      }
  
      void set_name(const char* name) {
          if (!ignore_) {
              name_ = strings_->intern_string(vtr::string_view(name));
          }
      }
      void set_value(const char* value) {
          if (!ignore_) {
              value_ = strings_->intern_string(vtr::string_view(value));
          }
      }
      void set_node_target(int inode) {
          VTR_ASSERT(!is_node_);
          VTR_ASSERT(!is_edge_);
  
          is_node_ = true;
          inode_ = inode;
      }
  
      void set_edge_target(int source_node, int sink_node, int switch_id) {
          VTR_ASSERT(!is_node_);
          VTR_ASSERT(!is_edge_);
  
          is_edge_ = true;
          inode_ = source_node;
          sink_node_ = sink_node;
          switch_id_ = switch_id;
      }
  
      void set_ignore() {
          ignore_ = true;
      }
  
      void bind() {
          if (is_node_) {
              vpr::add_rr_node_metadata(inode_, name_, value_);
          } else if (is_edge_) {
              vpr::add_rr_edge_metadata(inode_, sink_node_, switch_id_,
                                        name_,
                                        value_);
          } else if (ignore_) {
              // Do nothing.
          } else {
              VTR_ASSERT(is_node_ || is_edge_ || ignore_);
          }
  
          is_node_ = false;
          is_edge_ = false;
      }
  
      void assert_clear() {
      }
  
      void finish() {
          assert_clear();
          is_node_ = false;
          is_edge_ = false;
      }
  
    private:
      bool is_node_;
      bool is_edge_;
      bool ignore_;
      int inode_;
      int sink_node_;
      int switch_id_;
      vtr::string_internment* strings_;
      vtr::interned_string name_;
      vtr::interned_string value_;
  };
  
  // Context for walking metadata.
  class t_metadata_dict_iterator {
    public:
      explicit t_metadata_dict_iterator(const t_metadata_dict* d, const std::function<void(const char*)>* report_error)
          : meta(d)
          , current_index(0)
          , report_error_(report_error) {}
  
      size_t size() {
          return meta->size();
      }
  
      const t_metadata_dict::value_type* advance(int n) {
          if (n != current_index) {
              (*report_error_)(vtr::string_fmt("Iterator out of sync %d != %d",
                                               n, current_index)
                                   .c_str());
          }
  
          current_index += 1;
          if (n == 0) {
              iter = meta->cbegin();
          } else {
              ++iter;
          }
  
          VTR_ASSERT(iter != meta->end());
          return &*iter;
      }
  
    private:
      const t_metadata_dict* meta;
      t_metadata_dict::const_iterator iter;
      int current_index;
      const std::function<void(const char*)>* report_error_;
  };
  
  class EdgeWalker {
    public:
      void initialize(const t_rr_graph_storage* nodes) {
          nodes_ = nodes;
          num_edges_ = 0;
          current_src_inode_ = 0;
          current_edge_ = 0;
          current_idx_ = 0;
  
          for (const auto& node : *nodes) {
              num_edges_ += node.num_edges();
          }
      }
  
      size_t num_edges() const {
          return num_edges_;
      }
  
      int current_src_node() const {
          return current_src_inode_;
      }
      int current_sink_node() const {
          VTR_ASSERT(current_src_inode_ < nodes_->size());
          return (*nodes_)[current_src_inode_].edge_sink_node(current_edge_);
      }
      int current_switch_id_node() const {
          VTR_ASSERT(current_src_inode_ < nodes_->size());
          return (*nodes_)[current_src_inode_].edge_switch(current_edge_);
      }
  
      size_t advance(int n) {
          VTR_ASSERT(current_idx_ < num_edges_);
          VTR_ASSERT(current_src_inode_ < nodes_->size());
  
          if (n > 0) {
              current_edge_ += 1;
          }
  
          if (current_edge_ >= (*nodes_)[current_src_inode_].num_edges()) {
              // Done with current_src_inode_, advance to the end of the
              // node list, or the next node with at least 1 edge.
              current_edge_ = 0;
  
              do {
                  current_src_inode_ += 1;
  
                  // This is the last edge, return now.
                  if (current_src_inode_ == nodes_->size()) {
                      VTR_ASSERT(current_idx_ + 1 == num_edges_);
                      return current_idx_++;
                  }
              } while ((*nodes_)[current_src_inode_].num_edges() < 1);
          }
  
          VTR_ASSERT(current_src_inode_ < nodes_->size());
  
          return current_idx_++;
      }
  
    private:
      const t_rr_graph_storage* nodes_;
      size_t num_edges_;
      size_t current_src_inode_;
      size_t current_edge_;
      size_t current_idx_;
  };
  
  struct RrGraphContextTypes : public uxsd::DefaultRrGraphContextTypes {
      using XListReadContext = int;
      using YListReadContext = int;
      using TimingReadContext = const t_rr_switch_inf*;
      using SizingReadContext = const t_rr_switch_inf*;
      using SwitchReadContext = const t_rr_switch_inf*;
      using SegmentTimingReadContext = const t_segment_inf*;
      using SegmentReadContext = const t_segment_inf*;
      using PinReadContext = const std::pair<const t_physical_tile_type*, int>;
      using PinClassReadContext = const std::pair<const t_physical_tile_type*, const t_class*>;
      using BlockTypeReadContext = const t_physical_tile_type*;
      using GridLocReadContext = const t_grid_tile*;
      using NodeLocReadContext = const t_rr_node;
      using NodeTimingReadContext = const t_rr_node;
      using NodeSegmentReadContext = const t_rr_node;
      using MetaReadContext = const t_metadata_dict::value_type*;
      using MetadataReadContext = t_metadata_dict_iterator;
      using NodeReadContext = const t_rr_node;
      using EdgeReadContext = const EdgeWalker*;
      using RrEdgesReadContext = EdgeWalker;
      using TimingWriteContext = t_rr_switch_inf*;
      using SizingWriteContext = t_rr_switch_inf*;
      using SwitchWriteContext = t_rr_switch_inf*;
      using SegmentTimingWriteContext = const t_segment_inf*;
      using SegmentWriteContext = const t_segment_inf*;
      using PinWriteContext = const std::pair<const t_physical_tile_type*, int>;
      using PinClassWriteContext = std::tuple<const t_physical_tile_type*, const t_class*, int>;
      using BlockTypeWriteContext = std::pair<const t_physical_tile_type*, int>;
      using NodeLocWriteContext = int;
      using NodeTimingWriteContext = int;
      using NodeSegmentWriteContext = int;
      using MetaWriteContext = MetadataBind;
      using MetadataWriteContext = MetadataBind;
      using NodeWriteContext = int;
      using EdgeWriteContext = MetadataBind;
  };
  
  class RrGraphSerializer final : public uxsd::RrGraphBase<RrGraphContextTypes> {
    public:
      RrGraphSerializer(
          const t_graph_type graph_type,
          const enum e_base_cost_type base_cost_type,
          int* wire_to_rr_ipin_switch,
          bool do_check_rr_graph,
          const char* read_rr_graph_name,
          std::string* read_rr_graph_filename,
          bool read_edge_metadata,
          t_chan_width* chan_width,
          t_rr_graph_storage* rr_nodes,
          RRGraphBuilder* rr_graph_builder,
          RRGraphView* rr_graph,
          std::vector<t_rr_switch_inf>* rr_switch_inf,
          vtr::vector<RRIndexedDataId, t_rr_indexed_data>* rr_indexed_data,
          const size_t num_arch_switches,
          const t_arch_switch_inf* arch_switch_inf,
          const std::vector<t_segment_inf>& segment_inf,
          const std::vector<t_physical_tile_type>& physical_tile_types,
          const DeviceGrid& grid,
          MetadataStorage<int>* rr_node_metadata,
          MetadataStorage<std::tuple<int, int, short>>* rr_edge_metadata,
          vtr::string_internment* strings)
          : wire_to_rr_ipin_switch_(wire_to_rr_ipin_switch)
          , chan_width_(chan_width)
          , rr_nodes_(rr_nodes)
          , rr_graph_builder_(rr_graph_builder)
          , rr_graph_(rr_graph)
          , rr_switch_inf_(rr_switch_inf)
          , rr_indexed_data_(rr_indexed_data)
          , read_rr_graph_filename_(read_rr_graph_filename)
          , graph_type_(graph_type)
          , base_cost_type_(base_cost_type)
          , do_check_rr_graph_(do_check_rr_graph)
          , read_rr_graph_name_(read_rr_graph_name)
          , read_edge_metadata_(read_edge_metadata)
          , num_arch_switches_(num_arch_switches)
          , arch_switch_inf_(arch_switch_inf)
          , segment_inf_(segment_inf)
          , physical_tile_types_(physical_tile_types)
          , grid_(grid)
          , rr_node_metadata_(rr_node_metadata)
          , rr_edge_metadata_(rr_edge_metadata)
          , strings_(strings)
          , empty_(strings_->intern_string(vtr::string_view("")))
          , report_error_(nullptr) {
          // Initialize internal data
          init_side_map();
      }
  
      /* A truth table to help understand the conversion from VPR side mask to uxsd side code
       *
       * index | LEFT BOTTOM RIGHT TOP | mask
       * ======+======================+======
       *  1    |                    X  | 0001
       * ======+======================+======
       *  2    |               X       | 0010
       * ======+======================+======
       *  3    |               X    X  | 0011
       * ======+======================+======
       *  4    |         X             | 0100
       * ======+======================+======
       *  5    |         X          X  | 0101
       * ======+======================+======
       *  6    |         X     X       | 0110
       * ======+======================+======
       *  7    |         X     X    X  | 0111
       * ======+======================+======
       *  8    |  X                    | 1000
       * ======+======================+======
       *  9    |  X                 X  | 1001
       * ======+======================+======
       *  10   |  X            X       | 1010
       * ======+======================+======
       *  11   |  X            X    X  | 1011
       * ======+======================+======
       *  12   |  X      X             | 1100
       * ======+======================+======
       *  13   |  X      X          X  | 1101
       * ======+======================+======
       *  14   |  X      X     X       | 1110
       * ======+======================+======
       *  15   |  X      X     X    X  | 1111
       */
    private:
      virtual void init_side_map() final {
          side_map_[0] = uxsd::enum_loc_side::UXSD_INVALID;
          side_map_[(1 << TOP)] = uxsd::enum_loc_side::TOP;
          side_map_[(1 << RIGHT)] = uxsd::enum_loc_side::RIGHT;
          side_map_[(1 << BOTTOM)] = uxsd::enum_loc_side::BOTTOM;
          side_map_[(1 << LEFT)] = uxsd::enum_loc_side::LEFT;
          side_map_[(1 << TOP) | (1 << LEFT)] = uxsd::enum_loc_side::TOP_LEFT;
          side_map_[(1 << TOP) | (1 << RIGHT)] = uxsd::enum_loc_side::TOP_RIGHT;
          side_map_[(1 << TOP) | (1 << BOTTOM)] = uxsd::enum_loc_side::TOP_BOTTOM;
          side_map_[(1 << RIGHT) | (1 << BOTTOM)] = uxsd::enum_loc_side::RIGHT_BOTTOM;
          side_map_[(1 << RIGHT) | (1 << LEFT)] = uxsd::enum_loc_side::RIGHT_LEFT;
          side_map_[(1 << BOTTOM) | (1 << LEFT)] = uxsd::enum_loc_side::BOTTOM_LEFT;
          side_map_[(1 << TOP) | (1 << RIGHT) | (1 << BOTTOM)] = uxsd::enum_loc_side::TOP_RIGHT_BOTTOM;
          side_map_[(1 << TOP) | (1 << RIGHT) | (1 << LEFT)] = uxsd::enum_loc_side::TOP_RIGHT_LEFT;
          side_map_[(1 << TOP) | (1 << BOTTOM) | (1 << LEFT)] = uxsd::enum_loc_side::TOP_BOTTOM_LEFT;
          side_map_[(1 << RIGHT) | (1 << BOTTOM) | (1 << LEFT)] = uxsd::enum_loc_side::RIGHT_BOTTOM_LEFT;
          side_map_[(1 << TOP) | (1 << RIGHT) | (1 << BOTTOM) | (1 << LEFT)] = uxsd::enum_loc_side::TOP_RIGHT_BOTTOM_LEFT;
      }
  
    public:
      void start_load(const std::function<void(const char*)>* report_error_in) final {
          // report_error_in should be invoked if RrGraphSerializer encounters
          // an error during the read.
          report_error_ = report_error_in;
      }
      void start_write() final {}
      void finish_write() final {}
  
      // error_encountered will be invoked by the reader implementation whenever
      // any error is encountered.
      //
      // This method should **not** be invoked from within RrGraphSerializer,
      // instead the error should be reported via report_error.  This enables
      // the reader implementation to add context (e.g. file and line number).
      void error_encountered(const char* file, int line, const char* message) final {
          vpr_throw(VPR_ERROR_ROUTE, file, line, "%s", message);
      }
  
      /** Generated for complex type "timing":
       * <xs:complexType name="timing">
       *   <xs:attribute name="R" type="xs:float" />
       *   <xs:attribute name="Cin" type="xs:float" />
       *   <xs:attribute name="Cinternal" type="xs:float" />
       *   <xs:attribute name="Cout" type="xs:float" />
       *   <xs:attribute name="Tdel" type="xs:float" />
       * </xs:complexType>
       */
      inline void set_timing_Cin(float Cin, t_rr_switch_inf*& sw) final {
          sw->Cin = Cin;
      }
      inline float get_timing_Cin(const t_rr_switch_inf*& sw) final {
          return sw->Cin;
      }
  
      inline void set_timing_Cinternal(float Cinternal, t_rr_switch_inf*& sw) final {
          sw->Cinternal = Cinternal;
      }
      inline float get_timing_Cinternal(const t_rr_switch_inf*& sw) final {
          return sw->Cinternal;
      }
  
      inline void set_timing_Cout(float Cout, t_rr_switch_inf*& sw) final {
          sw->Cout = Cout;
      }
      inline float get_timing_Cout(const t_rr_switch_inf*& sw) final {
          return sw->Cout;
      }
  
      inline void set_timing_R(float R, t_rr_switch_inf*& sw) final {
          sw->R = R;
      }
      inline float get_timing_R(const t_rr_switch_inf*& sw) final {
          return sw->R;
      }
  
      inline void set_timing_Tdel(float Tdel, t_rr_switch_inf*& sw) final {
          sw->Tdel = Tdel;
      }
      inline float get_timing_Tdel(const t_rr_switch_inf*& sw) final {
          return sw->Tdel;
      }
  
      /** Generated for complex type "switch":
       * <xs:complexType name="switch">
       *   <xs:all>
       *     <xs:element minOccurs="0" name="timing" type="timing" />
       *     <xs:element name="sizing" type="sizing" />
       *   </xs:all>
       *   <xs:attribute name="id" type="xs:int" use="required" />
       *   <xs:attribute name="name" type="xs:string" use="required" />
       *   <xs:attribute name="type" type="switch_type" />
       * </xs:complexType>
       */
      inline void set_switch_name(const char* name, t_rr_switch_inf*& sw) final {
          // Switch names are required to be allocated in the architecture,
          // so search the arch_switch_inf_ for the relevant name, and set the
          // rr graph switch structure with that copy of the string.
          //
          // If the switch name is not present in the architecture, generate an
          // error.
          bool found_arch_name = false;
          for (size_t i = 0; i < num_arch_switches_; ++i) {
              if (strcmp(name, arch_switch_inf_[i].name) == 0) {
                  name = arch_switch_inf_[i].name;
                  found_arch_name = true;
                  break;
              }
          }
          if (!found_arch_name) {
              report_error("Switch name '%s' not found in architecture\n", name);
          }
  
          sw->name = name;
      }
      inline const char* get_switch_name(const t_rr_switch_inf*& sw) final {
          return sw->name;
      }
  
      inline void set_switch_type(uxsd::enum_switch_type type, t_rr_switch_inf*& sw) final {
          sw->set_type(from_uxsd_switch_type(type));
      }
      inline uxsd::enum_switch_type get_switch_type(const t_rr_switch_inf*& sw) final {
          return to_uxsd_switch_type(sw->type());
      }
  
      inline t_rr_switch_inf* init_switch_timing(t_rr_switch_inf*& sw) final {
          return sw;
      }
      inline void finish_switch_timing(t_rr_switch_inf*& /*sw*/) final {}
  
      inline const t_rr_switch_inf* get_switch_timing(const t_rr_switch_inf*& sw) final {
          return sw;
      }
      inline bool has_switch_timing(const t_rr_switch_inf*& /*sw*/) final {
          return true;
      }
  
      inline t_rr_switch_inf* init_switch_sizing(t_rr_switch_inf*& sw, float buf_size, float mux_trans_size) final {
          sw->buf_size = buf_size;
          sw->mux_trans_size = mux_trans_size;
          return sw;
      }
      inline void finish_switch_sizing(t_rr_switch_inf*& /*sw*/) final {}
      inline const t_rr_switch_inf* get_switch_sizing(const t_rr_switch_inf*& sw) final {
          return sw;
      }
  
      inline float get_sizing_buf_size(const t_rr_switch_inf*& sw) final {
          return sw->buf_size;
      }
      inline float get_sizing_mux_trans_size(const t_rr_switch_inf*& sw) final {
          return sw->mux_trans_size;
      }
  
      /** Generated for complex type "switches":
       * <xs:complexType name="switches">
       *   <xs:sequence>
       *     <xs:element maxOccurs="unbounded" name="switch" type="switch" />
       *   </xs:sequence>
       * </xs:complexType>
       */
      inline void preallocate_switches_switch(void*& /*ctx*/, size_t size) final {
          rr_switch_inf_->reserve(size);
      }
  
      inline t_rr_switch_inf* add_switches_switch(void*& /*ctx*/, int id) final {
          // make_room_in_vector will not allocate if preallocate_switches_switch
          // was invoked, but on formats that lack size on read,
          // make_room_in_vector will use an allocation pattern that is
          // amoritized O(1).
          make_room_in_vector(rr_switch_inf_, id);
  
          (*rr_switch_inf_)[id].R = 0;
          (*rr_switch_inf_)[id].Cin = 0;
          (*rr_switch_inf_)[id].Cout = 0;
          (*rr_switch_inf_)[id].Cinternal = 0;
          (*rr_switch_inf_)[id].Tdel = 0;
  
          return &(*rr_switch_inf_)[id];
      }
      inline void finish_switches_switch(t_rr_switch_inf*& /*ctx*/) final {}
      inline size_t num_switches_switch(void*& /*ctx*/) final {
          return rr_switch_inf_->size();
      }
      inline const t_rr_switch_inf* get_switches_switch(int n, void*& /*ctx*/) final {
          return &(*rr_switch_inf_)[n];
      }
  
      inline int get_switch_id(const t_rr_switch_inf*& sw) final {
          return sw - &(*rr_switch_inf_)[0];
      }
  
      inline void* init_rr_graph_switches(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void finish_rr_graph_switches(void*& /*ctx*/) final {
          // If make_room_in_vector was used for allocation, this ensures that
          // the final storage has no overhead.
          rr_switch_inf_->shrink_to_fit();
      }
  
      /** Generated for complex type "meta":
       * <xs:complexType name="meta">
       *   <xs:simpleContent>
       *     <xs:extension base="xs:string">
       *       <xs:attribute name="name" type="xs:string" use="required" />
       *     </xs:extension>
       *   </xs:simpleContent>
       * </xs:complexType>
       */
      inline void set_meta_name(const char* name, MetadataBind& bind) final {
          bind.set_name(name);
      }
  
    private:
      std::string temp_;
  
    public:
      inline const char* get_meta_name(const t_metadata_dict::value_type*& meta_value) final {
          meta_value->first.get(strings_, &temp_);
          return temp_.c_str();
      }
  
      inline void set_meta_value(const char* value, MetadataBind& bind) final {
          bind.set_value(value);
      }
      inline const char* get_meta_value(const t_metadata_dict::value_type*& meta_value) final {
          VTR_ASSERT(meta_value->second.size() == 1);
          meta_value->second[0].as_string().get(strings_, &temp_);
          return temp_.c_str();
      }
  
      /** Generated for complex type "metadata":
       * <xs:complexType name="metadata">
       *   <xs:sequence>
       *     <xs:element maxOccurs="unbounded" name="meta" type="meta" />
       *   </xs:sequence>
       * </xs:complexType>
       */
      inline void preallocate_metadata_meta(MetadataBind& /*bind*/, size_t /*size*/) final {
      }
      inline MetadataBind add_metadata_meta(MetadataBind& bind) final {
          return bind;
      }
      inline void finish_metadata_meta(MetadataBind& bind) final {
          bind.bind();
      }
  
      inline size_t num_metadata_meta(t_metadata_dict_iterator& itr) final {
          return itr.size();
      }
      inline const t_metadata_dict::value_type* get_metadata_meta(int n, t_metadata_dict_iterator& itr) final {
          return itr.advance(n);
      }
  
      /** Generated for complex type "node_loc":
       * <xs:complexType name="node_loc">
       *   <xs:attribute name="xlow" type="xs:int" use="required" />
       *   <xs:attribute name="ylow" type="xs:int" use="required" />
       *   <xs:attribute name="xhigh" type="xs:int" use="required" />
       *   <xs:attribute name="yhigh" type="xs:int" use="required" />
       *   <xs:attribute name="side" type="loc_side" />
       *   <xs:attribute name="ptc" type="xs:int" use="required" />
       * </xs:complexType>
       */
  
      inline int init_node_loc(int& inode, int ptc, int xhigh, int xlow, int yhigh, int ylow) final {
          auto node = (*rr_nodes_)[inode];
          RRNodeId node_id = node.id();
  
          rr_graph_builder_->set_node_coordinates(node_id, xlow, ylow, xhigh, yhigh);
          node.set_ptc_num(ptc);
          return inode;
      }
      inline void finish_node_loc(int& /*inode*/) final {}
      inline const t_rr_node get_node_loc(const t_rr_node& node) final {
          return node;
      }
  
      inline int get_node_loc_ptc(const t_rr_node& node) final {
          return node.ptc_num();
      }
      inline int get_node_loc_xhigh(const t_rr_node& node) final {
          return rr_graph_->node_xhigh(node.id());
      }
      inline int get_node_loc_xlow(const t_rr_node& node) final {
          return rr_graph_->node_xlow(node.id());
      }
      inline int get_node_loc_yhigh(const t_rr_node& node) final {
          return rr_graph_->node_yhigh(node.id());
      }
      inline int get_node_loc_ylow(const t_rr_node& node) final {
          return rr_graph_->node_ylow(node.id());
      }
  
      inline void set_node_loc_side(uxsd::enum_loc_side side, int& inode) final {
          auto node = (*rr_nodes_)[inode];
          const auto& rr_graph = (*rr_graph_);
  
          if (uxsd::enum_loc_side::UXSD_INVALID == side) {
              // node_loc.side is only expected on IPIN/OPIN.
              if (rr_graph.node_type(node.id()) == IPIN || rr_graph.node_type(node.id()) == OPIN) {
                  report_error(
                      "inode %d is type %d, which requires a side, but no side was supplied.",
                      inode, rr_graph.node_type(node.id()));
              }
          } else {
              std::bitset<NUM_SIDES> sides_to_add = from_uxsd_loc_side(side);
              for (const e_side& side_to_add : SIDES) {
                  if (sides_to_add[side_to_add]) {
                      node.add_side(side_to_add);
                  }
              }
          }
      }
      inline uxsd::enum_loc_side get_node_loc_side(const t_rr_node& node) final {
          const auto& rr_graph = (*rr_graph_);
          if (rr_graph.node_type(node.id()) == IPIN || rr_graph.node_type(node.id()) == OPIN) {
              std::bitset<NUM_SIDES> sides_bitset;
              for (const e_side& side : SIDES) {
                  if (node.is_node_on_specific_side(side)) {
                      sides_bitset.set(side);
                  }
              }
              return to_uxsd_loc_side(sides_bitset);
          } else {
              return uxsd::enum_loc_side::UXSD_INVALID;
          }
      }
  
      /** Generated for complex type "node_timing":
       * <xs:complexType name="node_timing">
       *   <xs:attribute name="R" type="xs:float" use="required" />
       *   <xs:attribute name="C" type="xs:float" use="required" />
       * </xs:complexType>
       */
      inline int init_node_timing(int& inode, float C, float R) final {
          auto node = (*rr_nodes_)[inode];
          node.set_rc_index(find_create_rr_rc_data(R, C));
          return inode;
      }
      inline void finish_node_timing(int& /*inode*/) final {}
      inline const t_rr_node get_node_timing(const t_rr_node& node) final {
          return node;
      }
      inline bool has_node_timing(const t_rr_node& /*node*/) final {
          return true;
      }
  
      inline float get_node_timing_C(const t_rr_node& node) final {
          return rr_graph_->node_C(node.id());
      }
      inline float get_node_timing_R(const t_rr_node& node) final {
          return rr_graph_->node_R(node.id());
      }
  
      /** Generated for complex type "node_segment":
       * <xs:complexType name="node_segment">
       *   <xs:attribute name="segment_id" type="xs:int" use="required" />
       * </xs:complexType>
       */
      inline int init_node_segment(int& inode, int segment_id) final {
          const auto& rr_graph = (*rr_graph_);
          if (segment_id > (ssize_t)segment_inf_.size()) {
              report_error(
                  "Specified segment %d is larger than number of known segments %zu",
                  segment_inf_.size());
          }
  
          auto node = (*rr_nodes_)[inode];
          if (GRAPH_GLOBAL == graph_type_) {
              node.set_cost_index(RRIndexedDataId(0));
          } else if (rr_graph.node_type(node.id()) == CHANX) {
              node.set_cost_index(RRIndexedDataId(CHANX_COST_INDEX_START + segment_id));
              seg_index_[rr_graph.node_cost_index(node.id())] = segment_id;
          } else if (rr_graph.node_type(node.id()) == CHANY) {
              node.set_cost_index(RRIndexedDataId(CHANX_COST_INDEX_START + segment_inf_.size() + segment_id));
              seg_index_[rr_graph.node_cost_index(node.id())] = segment_id;
          }
          return inode;
      }
      inline void finish_node_segment(int& /*inode*/) final {}
      inline int get_node_segment_segment_id(const t_rr_node& node) final {
          return (*rr_indexed_data_)[(*rr_graph_).node_cost_index(node.id())].seg_index;
      }
  
      inline const t_rr_node get_node_segment(const t_rr_node& node) final {
          return node;
      }
      inline bool has_node_segment(const t_rr_node& node) final {
          return (*rr_indexed_data_)[(*rr_graph_).node_cost_index(node.id())].seg_index != -1;
      }
  
      inline MetadataBind init_node_metadata(int& inode) final {
          MetadataBind bind(strings_, empty_);
          bind.set_node_target(inode);
          return bind;
      }
      inline void finish_node_metadata(MetadataBind& bind) final {
          bind.finish();
      }
      inline t_metadata_dict_iterator get_node_metadata(const t_rr_node& node) final {
          const auto itr = rr_node_metadata_->find(get_node_id(node));
          return t_metadata_dict_iterator(&itr->second, report_error_);
      }
      inline bool has_node_metadata(const t_rr_node& node) final {
          const auto itr = rr_node_metadata_->find(get_node_id(node));
          return itr != rr_node_metadata_->end();
      }
  
      /** Generated for complex type "rr_nodes":
       * <xs:complexType name="rr_nodes">
       *   <xs:choice maxOccurs="unbounded">
       *     <xs:element name="node" type="node" />
       *   </xs:choice>
       * </xs:complexType>
       */
      inline void preallocate_rr_nodes_node(void*& /*ctx*/, size_t size) final {
          rr_nodes_->reserve(size);
      }
      inline int add_rr_nodes_node(void*& /*ctx*/, unsigned int capacity, unsigned int id, uxsd::enum_node_type type) final {
          // make_room_in_vector will not allocate if preallocate_rr_nodes_node
          // was invoked, but on formats that lack size on read,
          // make_room_in_vector will use an allocation pattern that is
          // amoritized O(1).
          const auto& rr_graph = (*rr_graph_);
          rr_nodes_->make_room_for_node(RRNodeId(id));
          auto node = (*rr_nodes_)[id];
          RRNodeId node_id = node.id();
  
          rr_graph_builder_->set_node_capacity(node_id, capacity);
          node.set_type(from_uxsd_node_type(type));
  
          switch (rr_graph.node_type(node.id())) {
              case CHANX:
                  break;
              case CHANY:
                  break;
              case SOURCE:
                  node.set_cost_index(RRIndexedDataId(SOURCE_COST_INDEX));
                  break;
              case SINK:
                  node.set_cost_index(RRIndexedDataId(SINK_COST_INDEX));
                  break;
              case OPIN:
                  node.set_cost_index(RRIndexedDataId(OPIN_COST_INDEX));
                  break;
              case IPIN:
                  node.set_cost_index(RRIndexedDataId(IPIN_COST_INDEX));
                  break;
              default:
                  report_error(
                      "Invalid node type %d",
                      type);
          }
  
          node.set_rc_index(find_create_rr_rc_data(0, 0));
  
          return id;
      }
      inline void finish_rr_nodes_node(int& /*inode*/) final {
      }
      inline size_t num_rr_nodes_node(void*& /*ctx*/) final {
          return rr_nodes_->size();
      }
      inline const t_rr_node get_rr_nodes_node(int n, void*& /*ctx*/) final {
          return (*rr_nodes_)[n];
      }
  
      inline unsigned int get_node_capacity(const t_rr_node& node) final {
          const auto& rr_graph = (*rr_graph_);
          return rr_graph.node_capacity(node.id());
      }
  
      inline unsigned int get_node_id(const t_rr_node& node) final {
          return size_t(node.id());
      }
      inline uxsd::enum_node_type get_node_type(const t_rr_node& node) final {
          const auto& rr_graph = (*rr_graph_);
          return to_uxsd_node_type(rr_graph.node_type(node.id()));
      }
  
      inline void set_node_direction(uxsd::enum_node_direction direction, int& inode) final {
          const auto& rr_graph = (*rr_graph_);
          auto node = (*rr_nodes_)[inode];
          if (direction == uxsd::enum_node_direction::UXSD_INVALID) {
              if (rr_graph.node_type(node.id()) == CHANX || rr_graph.node_type(node.id()) == CHANY) {
                  report_error(
                      "inode %d is type %d, which requires a direction, but no direction was supplied.",
                      inode, rr_graph.node_type(node.id()));
              }
          } else {
              node.set_direction(from_uxsd_node_direction(direction));
          }
      }
      inline uxsd::enum_node_direction get_node_direction(const t_rr_node& node) final {
          const auto& rr_graph = (*rr_graph_);
          if (rr_graph.node_type(node.id()) == CHANX || rr_graph.node_type(node.id()) == CHANY) {
              return to_uxsd_node_direction(rr_graph.node_direction(node.id()));
          } else {
              return uxsd::enum_node_direction::UXSD_INVALID;
          }
      }
  
      inline void* init_rr_graph_rr_nodes(void*& /*ctx*/) final {
          rr_nodes_->clear();
          seg_index_.resize(CHANX_COST_INDEX_START + 2 * segment_inf_.size(), -1);
          return nullptr;
      }
      inline void finish_rr_graph_rr_nodes(void*& /*ctx*/) final {
          // If make_room_in_vector was used for allocation, this ensures that
          // the final storage has no overhead.
          rr_nodes_->shrink_to_fit();
      }
  
      /** Generated for complex type "rr_edges":
       * <xs:complexType name="rr_edges">
       *   <xs:choice maxOccurs="unbounded">
       *     <xs:element name="edge" type="edge" />
       *   </xs:choice>
       * </xs:complexType>
       */
      /** Generated for complex type "edge":
       * <xs:complexType name="edge">
       *   <xs:all>
       *     <xs:element minOccurs="0" name="metadata" type="metadata" />
       *   </xs:all>
       *   <xs:attribute name="src_node" type="xs:unsignedInt" use="required" />
       *   <xs:attribute name="sink_node" type="xs:unsignedInt" use="required" />
       *   <xs:attribute name="switch_id" type="xs:unsignedInt" use="required" />
       * </xs:complexType>
       */
      inline void preallocate_rr_edges_edge(void*& /*ctx*/, size_t size) final {
          rr_nodes_->reserve_edges(size);
          if (read_edge_metadata_) {
              rr_edge_metadata_->reserve(size);
          }
      }
      inline MetadataBind add_rr_edges_edge(void*& /*ctx*/, unsigned int sink_node, unsigned int src_node, unsigned int switch_id) final {
          if (src_node >= rr_nodes_->size()) {
              report_error(
                  "source_node %d is larger than rr_nodes.size() %d",
                  src_node, rr_nodes_->size());
          }
  
          MetadataBind bind(strings_, empty_);
          if (read_edge_metadata_) {
              bind.set_edge_target(src_node, sink_node, switch_id);
          } else {
              bind.set_ignore();
          }
  
          rr_nodes_->emplace_back_edge(RRNodeId(src_node), RRNodeId(sink_node), switch_id);
          return bind;
      }
      inline void finish_rr_edges_edge(MetadataBind& bind) final {
          bind.finish();
      }
      inline size_t num_rr_edges_edge(EdgeWalker& walker) final {
          return walker.num_edges();
      }
      inline const EdgeWalker* get_rr_edges_edge(int n, EdgeWalker& walker) final {
          size_t cur = walker.advance(n);
          if ((ssize_t)cur != n) {
              report_error("Incorrect edge index %zu != %d", cur, n);
          }
          return &walker;
      }
  
      inline unsigned int get_edge_sink_node(const EdgeWalker*& walker) final {
          return walker->current_sink_node();
      }
      inline unsigned int get_edge_src_node(const EdgeWalker*& walker) final {
          return walker->current_src_node();
      }
      inline unsigned int get_edge_switch_id(const EdgeWalker*& walker) final {
          return walker->current_switch_id_node();
      }
  
      inline MetadataBind init_edge_metadata(MetadataBind& bind) final {
          return bind;
      }
      inline void finish_edge_metadata(MetadataBind& bind) final {
          bind.finish();
      }
      inline t_metadata_dict_iterator get_edge_metadata(const EdgeWalker*& walker) final {
          return t_metadata_dict_iterator(&rr_edge_metadata_->find(
                                                                std::make_tuple(
                                                                    walker->current_src_node(),
                                                                    walker->current_sink_node(),
                                                                    walker->current_switch_id_node()))
                                               ->second,
                                          report_error_);
      }
      inline bool has_edge_metadata(const EdgeWalker*& walker) final {
          return rr_edge_metadata_->find(
                     std::make_tuple(
                         walker->current_src_node(),
                         walker->current_sink_node(),
                         walker->current_switch_id_node()))
                 != rr_edge_metadata_->end();
      }
  
      inline void* init_rr_graph_rr_edges(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void finish_rr_graph_rr_edges(void*& /*ctx*/) final {
          /*initialize a vector that keeps track of the number of wire to ipin switches
           * There should be only one wire to ipin switch. In case there are more, make sure to
           * store the most frequent switch */
          const auto& rr_graph = (*rr_graph_);
          std::vector<int> count_for_wire_to_ipin_switches;
          count_for_wire_to_ipin_switches.resize(rr_switch_inf_->size(), 0);
          //first is index, second is count
          std::pair<int, int> most_frequent_switch(-1, 0);
  
          // Partition the rr graph edges for efficient access to
          // configurable/non-configurable edge subsets. Must be done after RR
          // switches have been allocated.
          rr_nodes_->mark_edges_as_rr_switch_ids();
          rr_nodes_->partition_edges();
  
          for (int source_node = 0; source_node < (ssize_t)rr_nodes_->size(); ++source_node) {
              int num_edges = rr_nodes_->num_edges(RRNodeId(source_node));
              for (int iconn = 0; iconn < num_edges; ++iconn) {
                  size_t sink_node = size_t(rr_nodes_->edge_sink_node(RRNodeId(source_node), iconn));
                  size_t switch_id = rr_nodes_->edge_switch(RRNodeId(source_node), iconn);
                  if (sink_node >= rr_nodes_->size()) {
                      report_error(
                          "sink_node %zu is larger than rr_nodes.size() %zu",
                          sink_node, rr_nodes_->size());
                  }
  
                  if (switch_id >= rr_switch_inf_->size()) {
                      report_error(
                          "switch_id %zu is larger than num_rr_switches %zu",
                          switch_id, rr_switch_inf_->size());
                  }
                  auto node = (*rr_nodes_)[source_node];
  
                  /*Keeps track of the number of the specific type of switch that connects a wire to an ipin
                   * use the pair data structure to keep the maximum*/
                  if (rr_graph.node_type(node.id()) == CHANX || rr_graph.node_type(node.id()) == CHANY) {
                      if (rr_graph.node_type(RRNodeId(sink_node)) == IPIN) {
                          count_for_wire_to_ipin_switches[switch_id]++;
                          if (count_for_wire_to_ipin_switches[switch_id] > most_frequent_switch.second) {
                              most_frequent_switch.first = switch_id;
                              most_frequent_switch.second = count_for_wire_to_ipin_switches[switch_id];
                          }
                      }
                  }
              }
          }
  
          VTR_ASSERT(wire_to_rr_ipin_switch_ != nullptr);
          *wire_to_rr_ipin_switch_ = most_frequent_switch.first;
      }
  
      inline EdgeWalker get_rr_graph_rr_edges(void*& /*ctx*/) final {
          EdgeWalker walker;
          walker.initialize(rr_nodes_);
          return walker;
      }
  
      /** Generated for complex type "channels":
       * <xs:complexType name="channels">
       *   <xs:sequence>
       *     <xs:element name="channel" type="channel" />
       *     <xs:element maxOccurs="unbounded" name="x_list" type="x_list" />
       *     <xs:element maxOccurs="unbounded" name="y_list" type="y_list" />
       *   </xs:sequence>
       * </xs:complexType>
       */
      inline int get_channel_chan_width_max(void*& /*ctx*/) final {
          return chan_width_->max;
      }
      inline int get_channel_x_max(void*& /*ctx*/) final {
          return chan_width_->x_max;
      }
      inline int get_channel_x_min(void*& /*ctx*/) final {
          return chan_width_->x_min;
      }
      inline int get_channel_y_max(void*& /*ctx*/) final {
          return chan_width_->y_max;
      }
      inline int get_channel_y_min(void*& /*ctx*/) final {
          return chan_width_->y_min;
      }
      inline void* init_channels_channel(void*& /*ctx*/, int chan_width_max, int x_max, int x_min, int y_max, int y_min) final {
          chan_width_->max = chan_width_max;
          chan_width_->x_min = x_min;
          chan_width_->y_min = y_min;
          chan_width_->x_max = x_max;
          chan_width_->y_max = y_max;
          chan_width_->x_list.resize(grid_.height());
          chan_width_->y_list.resize(grid_.width());
          return nullptr;
      }
      inline void finish_channels_channel(void*& /*ctx*/) final {
      }
      inline void* get_channels_channel(void*& /*ctx*/) final {
          return nullptr;
      }
  
      inline void preallocate_channels_x_list(void*& /*ctx*/, size_t /*size*/) final {
      }
  
      inline void* add_channels_x_list(void*& /*ctx*/, unsigned int index, int info) final {
          if (index >= chan_width_->x_list.size()) {
              report_error(
                  "index %d on x_list exceeds x_list size %u",
                  index, chan_width_->x_list.size());
          }
          chan_width_->x_list[index] = info;
          return nullptr;
      }
      inline void finish_channels_x_list(void*& /*ctx*/) final {}
  
      inline unsigned int get_x_list_index(int& n) final {
          return n;
      }
      inline int get_x_list_info(int& n) final {
          return chan_width_->x_list[n];
      }
      inline size_t num_channels_x_list(void*& /*ctx*/) final {
          return chan_width_->x_list.size();
      }
      inline int get_channels_x_list(int n, void*& /*iter*/) final {
          return n;
      }
  
      inline void preallocate_channels_y_list(void*& /*ctx*/, size_t /*size*/) final {
      }
  
      inline void* add_channels_y_list(void*& /*ctx*/, unsigned int index, int info) final {
          if (index >= chan_width_->y_list.size()) {
              report_error(
                  "index %d on y_list exceeds y_list size %u",
                  index, chan_width_->y_list.size());
          }
          chan_width_->y_list[index] = info;
  
          return nullptr;
      }
      inline void finish_channels_y_list(void*& /*ctx*/) final {}
  
      inline unsigned int get_y_list_index(int& n) final {
          return n;
      }
      inline int get_y_list_info(int& n) final {
          return chan_width_->y_list[n];
      }
      inline size_t num_channels_y_list(void*& /*ctx*/) final {
          return chan_width_->y_list.size();
      }
      inline int get_channels_y_list(int n, void*& /*ctx*/) final {
          return n;
      }
  
      inline void* init_rr_graph_channels(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void finish_rr_graph_channels(void*& /*ctx*/) final {
      }
  
      /** Generated for complex type "segment_timing":
       * <xs:complexType name="segment_timing">
       *   <xs:attribute name="R_per_meter" type="xs:float" />
       *   <xs:attribute name="C_per_meter" type="xs:float" />
       * </xs:complexType>
       */
      inline const t_segment_inf* init_segment_timing(const t_segment_inf*& segment) final {
          return segment;
      }
      inline void finish_segment_timing(const t_segment_inf*& /*segment*/) final {}
  
      inline const t_segment_inf* get_segment_timing(const t_segment_inf*& segment) final {
          return segment;
      }
      inline bool has_segment_timing(const t_segment_inf*& /*segment*/) final {
          return true;
      }
  
      inline float get_segment_timing_C_per_meter(const t_segment_inf*& segment) final {
          return segment->Cmetal;
      }
      inline void set_segment_timing_C_per_meter(float C_per_meter, const t_segment_inf*& segment) final {
          if (segment->Cmetal != C_per_meter) {
              report_error(
                  "Architecture file does not match RR graph's segment C_per_meter");
          }
      }
  
      inline float get_segment_timing_R_per_meter(const t_segment_inf*& segment) final {
          return segment->Rmetal;
      }
      inline void set_segment_timing_R_per_meter(float R_per_meter, const t_segment_inf*& segment) final {
          if (segment->Rmetal != R_per_meter) {
              report_error(
                  "Architecture file does not match RR graph's segment R_per_meter");
          }
      }
  
      /** Generated for complex type "segment":
       * <xs:complexType name="segment">
       *   <xs:all>
       *     <xs:element minOccurs="0" name="timing" type="segment_timing" />
       *   </xs:all>
       *   <xs:attribute name="id" type="xs:int" use="required" />
       *   <xs:attribute name="name" type="xs:string" use="required" />
       * </xs:complexType>
       */
      inline int get_segment_id(const t_segment_inf*& segment) final {
          return segment - &segment_inf_.at(0);
      }
      inline const char* get_segment_name(const t_segment_inf*& segment) final {
          return segment->name.c_str();
      }
      inline void set_segment_name(const char* name, const t_segment_inf*& segment) final {
          if (segment->name != name) {
              report_error(
                  "Architecture file does not match RR graph's segment name: arch uses %s, RR graph uses %s",
                  segment->name.c_str(), name);
          }
      }
  
      /** Generated for complex type "segments":
       * <xs:complexType name="segments">
       *   <xs:sequence>
       *     <xs:element maxOccurs="unbounded" name="segment" type="segment" />
       *   </xs:sequence>
       * </xs:complexType>
       */
      inline void preallocate_segments_segment(void*& /*ctx*/, size_t size) final {
          if (size != segment_inf_.size()) {
              report_error(
                  "Architecture contains %zu segments and rr graph contains %zu segments",
                  segment_inf_.size(), size);
          }
      }
      inline const t_segment_inf* add_segments_segment(void*& /*ctx*/, int id) final {
          return &segment_inf_.at(id);
      }
      inline void finish_segments_segment(const t_segment_inf*& /*iter*/) final {}
      inline size_t num_segments_segment(void*& /*iter*/) final {
          return segment_inf_.size();
      }
      inline const t_segment_inf* get_segments_segment(int n, void*& /*ctx*/) final {
          return &segment_inf_.at(n);
      }
  
      inline void* init_rr_graph_segments(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void finish_rr_graph_segments(void*& /*ctx*/) final {
      }
  
      /** Generated for complex type "pin":
       * <xs:complexType name="pin">
       *   <xs:simpleContent>
       *     <xs:extension base="xs:string">
       *       <xs:attribute name="ptc" type="xs:int" use="required" />
       *     </xs:extension>
       *   </xs:simpleContent>
       * </xs:complexType>
       */
  
      inline void set_pin_value(const char* value, const std::pair<const t_physical_tile_type*, int>& context) final {
          const t_physical_tile_type* tile;
          int ptc;
          std::tie(tile, ptc) = context;
          if (block_type_pin_index_to_name(tile, ptc) != value) {
              report_error(
                  "Architecture file does not match RR graph's block pin list");
          }
      }
      inline void finish_pin_class_pin(const std::pair<const t_physical_tile_type*, int>& /*ctx*/) final {
      }
  
      /** Generated for complex type "pin_class":
       * <xs:complexType name="pin_class">
       *   <xs:sequence>
       *     <xs:element maxOccurs="unbounded" name="pin" type="pin" />
       *   </xs:sequence>
       *   <xs:attribute name="type" type="pin_type" use="required" />
       * </xs:complexType>
       */
      inline void preallocate_pin_class_pin(std::tuple<const t_physical_tile_type*, const t_class*, int>& context, size_t size) final {
          const t_physical_tile_type* tile;
          const t_class* class_inf;
          std::tie(tile, class_inf, std::ignore) = context;
          auto class_idx = class_inf - &tile->class_inf[0];
  
          if (class_inf->num_pins != (ssize_t)size) {
              report_error(
                  "Incorrect number of pins (%zu != %u) in %zu pin_class in block %s",
                  size, class_inf->num_pins,
                  class_idx, tile->name);
          }
      }
      inline const std::pair<const t_physical_tile_type*, int> add_pin_class_pin(std::tuple<const t_physical_tile_type*, const t_class*, int>& context, int ptc) final {
          const t_physical_tile_type* tile;
          const t_class* class_inf;
          std::tie(tile, class_inf, std::ignore) = context;
  
          // Count number of pins on this pin class.
          int& pin_count = std::get<2>(context);
          pin_count += 1;
          return std::make_pair(tile, ptc);
      }
      inline void finish_block_type_pin_class(std::tuple<const t_physical_tile_type*, const t_class*, int>& context) final {
          const t_physical_tile_type* tile;
          const t_class* class_inf;
          int pin_count;
          std::tie(tile, class_inf, pin_count) = context;
          auto class_idx = class_inf - &tile->class_inf[0];
          if (class_inf->num_pins != pin_count) {
              report_error(
                  "Incorrect number of pins (%zu != %u) in %zu pin_class in block %s",
                  pin_count, class_inf->num_pins,
                  class_idx, tile->name);
          }
      }
  
      inline uxsd::enum_pin_type get_pin_class_type(const std::pair<const t_physical_tile_type*, const t_class*>& context) final {
          return to_uxsd_pin_type(context.second->type);
      }
      inline size_t num_pin_class_pin(const std::pair<const t_physical_tile_type*, const t_class*>& context) final {
          return context.second->num_pins;
      }
  
      inline const char* get_pin_value(const std::pair<const t_physical_tile_type*, int>& context) final {
          const t_physical_tile_type* tile;
          int ptc;
          std::tie(tile, ptc) = context;
          temp_string_ = block_type_pin_index_to_name(tile, ptc);
          return temp_string_.c_str();
      }
      inline int get_pin_ptc(const std::pair<const t_physical_tile_type*, int>& context) final {
          const t_physical_tile_type* tile;
          int ptc;
          std::tie(tile, ptc) = context;
  
          return ptc;
      }
      inline const std::pair<const t_physical_tile_type*, int> get_pin_class_pin(int n, const std::pair<const t_physical_tile_type*, const t_class*>& context) final {
          const t_physical_tile_type* tile;
          const t_class* class_inf;
          std::tie(tile, class_inf) = context;
          return std::make_pair(tile, class_inf->pinlist[n]);
      }
  
      inline int get_block_type_height(const t_physical_tile_type*& tile) final {
          return tile->height;
      }
      inline int get_block_type_id(const t_physical_tile_type*& tile) final {
          return tile->index;
      }
      inline const char* get_block_type_name(const t_physical_tile_type*& tile) final {
          return tile->name;
      }
      inline int get_block_type_width(const t_physical_tile_type*& tile) final {
          return tile->width;
      }
      inline size_t num_block_type_pin_class(const t_physical_tile_type*& tile) final {
          return (int)tile->class_inf.size();
      }
      inline const std::pair<const t_physical_tile_type*, const t_class*> get_block_type_pin_class(int n, const t_physical_tile_type*& tile) final {
          return std::make_pair(tile, &tile->class_inf[n]);
      }
  
      /** Generated for complex type "block_type":
       * <xs:complexType name="block_type">
       *   <xs:sequence>
       *     <xs:element maxOccurs="unbounded" minOccurs="0" name="pin_class" type="pin_class" />
       *   </xs:sequence>
       *   <xs:attribute name="id" type="xs:int" use="required" />
       *   <xs:attribute name="name" type="xs:string" use="required" />
       *   <xs:attribute name="width" type="xs:int" use="required" />
       *   <xs:attribute name="height" type="xs:int" use="required" />
       * </xs:complexType>
       */
      inline void set_block_type_name(const char* name, std::pair<const t_physical_tile_type*, int>& context) final {
          const t_physical_tile_type* tile = context.first;
          if (strcmp(tile->name, name) != 0) {
              report_error(
                  "Architecture file does not match RR graph's block name: arch uses name %s, RR graph uses name %s",
                  tile->name, name);
          }
      }
  
      /** Generated for complex type "block_types":
       * <xs:complexType name="block_types">
       *   <xs:sequence>
       *     <xs:element maxOccurs="unbounded" name="block_type" type="block_type" />
       *   </xs:sequence>
       * </xs:complexType>
       */
      void preallocate_block_types_block_type(void*& /*ctx*/, size_t size) final {
          if (physical_tile_types_.size() != size) {
              report_error(
                  "Architecture defines %zu block types, but rr graph contains %zu block types.",
                  physical_tile_types_.size(), size);
          }
      }
      inline std::pair<const t_physical_tile_type*, int> add_block_types_block_type(void*& /*ctx*/, int height, int id, int width) final {
          const auto& block_info = physical_tile_types_.at(id);
          if (block_info.width != width) {
              report_error(
                  "Architecture file does not match RR graph's block width");
          }
          if (block_info.height != height) {
              report_error(
                  "Architecture file does not match RR graph's block height");
          }
  
          // Going to count how many classes are found.
          return std::make_pair(&block_info, 0);
      }
      inline void preallocate_block_type_pin_class(std::pair<const t_physical_tile_type*, int>& context, size_t size) final {
          const t_physical_tile_type* tile = context.first;
          if ((int)tile->class_inf.size() != (ssize_t)size) {
              report_error("Architecture file does not match block type");
          }
      }
  
      inline std::tuple<const t_physical_tile_type*, const t_class*, int> add_block_type_pin_class(std::pair<const t_physical_tile_type*, int>& context, uxsd::enum_pin_type type) final {
          const t_physical_tile_type* tile = context.first;
          int& num_classes = context.second;
  
          // Count number of pin classes
          if (num_classes >= (int)tile->class_inf.size()) {
              report_error("Architecture file does not match block type");
          }
          const t_class* class_inf = &context.first->class_inf[num_classes++];
  
          if (class_inf->type != from_uxsd_pin_type(type)) {
              report_error(
                  "Architecture file does not match RR graph's block type");
          }
  
          return std::make_tuple(tile, class_inf, 0);
      }
      inline void finish_block_types_block_type(std::pair<const t_physical_tile_type*, int>& context) final {
          const t_physical_tile_type* tile = context.first;
          int num_classes = context.second;
          if ((int)tile->class_inf.size() != num_classes) {
              report_error("Architecture file does not match block type");
          }
      }
  
      inline size_t num_block_types_block_type(void*& /*ctx*/) final {
          return physical_tile_types_.size();
      }
      inline const t_physical_tile_type* get_block_types_block_type(int n, void*& /*ctx*/) final {
          return &physical_tile_types_[n];
      }
      inline void* init_rr_graph_block_types(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void finish_rr_graph_block_types(void*& /*ctx*/) final {
      }
  
      /** Generated for complex type "grid_loc":
       * <xs:complexType name="grid_loc">
       *   <xs:attribute name="x" type="xs:int" use="required" />
       *   <xs:attribute name="y" type="xs:int" use="required" />
       *   <xs:attribute name="block_type_id" type="xs:int" use="required" />
       *   <xs:attribute name="width_offset" type="xs:int" use="required" />
       *   <xs:attribute name="height_offset" type="xs:int" use="required" />
       * </xs:complexType>
       */
      /** Generated for complex type "grid_locs":
       * <xs:complexType name="grid_locs">
       *   <xs:sequence>
       *     <xs:element maxOccurs="unbounded" name="grid_loc" type="grid_loc" />
       *   </xs:sequence>
       * </xs:complexType>
       */
      inline void preallocate_grid_locs_grid_loc(void*& /*ctx*/, size_t size) final {
          if (grid_.matrix().size() != size) {
              report_error(
                  "Architecture file contains %zu grid locations, rr graph contains %zu grid locations.",
                  grid_.matrix().size(), size);
          }
      }
      inline void* add_grid_locs_grid_loc(void*& /*ctx*/, int block_type_id, int height_offset, int width_offset, int x, int y) final {
          const t_grid_tile& grid_tile = grid_[x][y];
  
          if (grid_tile.type->index != block_type_id) {
              report_error(
                  "Architecture file does not match RR graph's block_type_id at (%d, %d): arch used ID %d, RR graph used ID %d.", x, y,
                  (grid_tile.type->index), block_type_id);
          }
          if (grid_tile.width_offset != width_offset) {
              report_error(
                  "Architecture file does not match RR graph's width_offset at (%d, %d)", x, y);
          }
  
          if (grid_tile.height_offset != height_offset) {
              report_error(
                  "Architecture file does not match RR graph's height_offset at (%d, %d)", x, y);
          }
          return nullptr;
      }
      inline void finish_grid_locs_grid_loc(void*& /*ctx*/) final {}
  
      inline void* init_rr_graph_grid(void*& /*ct*/) final {
          return nullptr;
      }
      inline void finish_rr_graph_grid(void*& /*ctx*/) final {
      }
  
      inline int get_grid_loc_block_type_id(const t_grid_tile*& grid_loc) final {
          return grid_loc->type->index;
      }
      inline int get_grid_loc_height_offset(const t_grid_tile*& grid_loc) final {
          return grid_loc->height_offset;
      }
      inline int get_grid_loc_width_offset(const t_grid_tile*& grid_loc) final {
          return grid_loc->width_offset;
      }
      inline int get_grid_loc_x(const t_grid_tile*& grid_loc) final {
          auto diff = grid_loc - &grid_.matrix().get(0);
  
          return diff / grid_.matrix().dim_size(1);
      }
      inline int get_grid_loc_y(const t_grid_tile*& grid_loc) final {
          auto diff = grid_loc - &grid_.matrix().get(0);
  
          return diff % grid_.matrix().dim_size(1);
      }
      inline size_t num_grid_locs_grid_loc(void*& /*iter*/) final {
          return grid_.matrix().size();
      }
      inline const t_grid_tile* get_grid_locs_grid_loc(int n, void*& /*ctx*/) final {
          return &grid_.matrix().get(n);
      }
  
      /** Generated for complex type "rr_graph":
       * <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema">
       *     <xs:all>
       *       <xs:element name="channels" type="channels" />
       *       <xs:element name="switches" type="switches" />
       *       <xs:element name="segments" type="segments" />
       *       <xs:element name="block_types" type="block_types" />
       *       <xs:element name="grid" type="grid_locs" />
       *       <xs:element name="rr_nodes" type="rr_nodes" />
       *       <xs:element name="rr_edges" type="rr_edges" />
       *     </xs:all>
       *     <xs:attribute name="tool_name" type="xs:string" />
       *     <xs:attribute name="tool_version" type="xs:string" />
       *     <xs:attribute name="tool_comment" type="xs:string" />
       *   </xs:complexType>
       */
      inline void set_rr_graph_tool_comment(const char* tool_comment, void*& /*ctx*/) final {
          std::string correct_string = "Generated from arch file ";
          correct_string += get_arch_file_name();
          if (correct_string != tool_comment) {
              VTR_LOG("\n");
              VTR_LOG_WARN("This RR graph file is %s while your input architecture file is %s, compatibility issues may arise\n",
                           tool_comment, get_arch_file_name());
              VTR_LOG("\n");
          }
      }
      inline void set_rr_graph_tool_name(const char* /*tool_name*/, void*& /*ctx*/) final {
      }
      inline void set_rr_graph_tool_version(const char* tool_version, void*& /*ctx*/) final {
          if (strcmp(tool_version, vtr::VERSION) != 0) {
              VTR_LOG("\n");
              VTR_LOG_WARN("This architecture version is for VPR %s while your current VPR version is %s, compatibility issues may arise\n",
                           tool_version, vtr::VERSION);
              VTR_LOG("\n");
          }
      }
  
      inline const char* get_rr_graph_tool_comment(void*& /*ctx*/) final {
          temp_string_.assign("Generated from arch file ");
          temp_string_ += get_arch_file_name();
          return temp_string_.c_str();
      }
      inline const char* get_rr_graph_tool_name(void*& /*ctx*/) final {
          return "vpr";
      }
      inline const char* get_rr_graph_tool_version(void*& /*ctx*/) final {
          return vtr::VERSION;
      }
  
      inline void* get_rr_graph_channels(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void* get_rr_graph_switches(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void* get_rr_graph_segments(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void* get_rr_graph_block_types(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void* get_rr_graph_grid(void*& /*ctx*/) final {
          return nullptr;
      }
      inline void* get_rr_graph_rr_nodes(void*& /*ctx*/) final {
          return nullptr;
      }
  
      void finish_load() final {
          process_rr_node_indices();
  
          rr_nodes_->init_fan_in();
  
          alloc_and_load_rr_indexed_data(
              segment_inf_,
              *wire_to_rr_ipin_switch_,
              base_cost_type_);
  
          VTR_ASSERT(rr_indexed_data_->size() == seg_index_.size());
          for (size_t i = 0; i < seg_index_.size(); ++i) {
              (*rr_indexed_data_)[RRIndexedDataId(i)].seg_index = seg_index_[RRIndexedDataId(i)];
          }
  
          VTR_ASSERT(read_rr_graph_filename_ != nullptr);
          VTR_ASSERT(read_rr_graph_name_ != nullptr);
          read_rr_graph_filename_->assign(read_rr_graph_name_);
  
          if (do_check_rr_graph_) {
              check_rr_graph(graph_type_, grid_, physical_tile_types_);
          }
      }
  
    private:
      /*Allocates and load the rr_node look up table. SINK and SOURCE, IPIN and OPIN
       *share the same look up table. CHANX and CHANY have individual look ups */
      void process_rr_node_indices() {
          auto& rr_graph_builder = (*rr_graph_builder_);
  
          /* Alloc the lookup table */
          for (t_rr_type rr_type : RR_TYPES) {
              if (rr_type == CHANX) {
                  rr_graph_builder.node_lookup().resize_nodes(grid_.height(), grid_.width(), rr_type, NUM_SIDES);
              } else {
                  rr_graph_builder.node_lookup().resize_nodes(grid_.width(), grid_.height(), rr_type, NUM_SIDES);
              }
          }
  
          /* Add the correct node into the vector */
          for (size_t inode = 0; inode < rr_nodes_->size(); inode++) {
              auto node = (*rr_nodes_)[inode];
              rr_graph_builder.add_node_to_all_locs(node.id());
          }
      }
  
      // Enum converters from/to uxsd types
  
      std::bitset<NUM_SIDES> from_uxsd_loc_side(uxsd::enum_loc_side side) {
          std::bitset<NUM_SIDES> side_mask(0x0);
          switch (side) {
              case uxsd::enum_loc_side::TOP:
                  side_mask.set(TOP);
                  break;
              case uxsd::enum_loc_side::TOP_LEFT:
                  side_mask.set(TOP);
                  side_mask.set(LEFT);
                  break;
              case uxsd::enum_loc_side::TOP_RIGHT:
                  side_mask.set(TOP);
                  side_mask.set(RIGHT);
                  break;
              case uxsd::enum_loc_side::TOP_BOTTOM:
                  side_mask.set(TOP);
                  side_mask.set(BOTTOM);
                  break;
              case uxsd::enum_loc_side::TOP_RIGHT_LEFT:
                  side_mask.set(TOP);
                  side_mask.set(RIGHT);
                  side_mask.set(LEFT);
                  break;
              case uxsd::enum_loc_side::TOP_RIGHT_BOTTOM:
                  side_mask.set(TOP);
                  side_mask.set(RIGHT);
                  side_mask.set(BOTTOM);
                  break;
              case uxsd::enum_loc_side::TOP_BOTTOM_LEFT:
                  side_mask.set(TOP);
                  side_mask.set(BOTTOM);
                  side_mask.set(LEFT);
                  break;
              case uxsd::enum_loc_side::RIGHT:
                  side_mask.set(RIGHT);
                  break;
              case uxsd::enum_loc_side::RIGHT_BOTTOM:
                  side_mask.set(RIGHT);
                  side_mask.set(BOTTOM);
                  break;
              case uxsd::enum_loc_side::RIGHT_LEFT:
                  side_mask.set(RIGHT);
                  side_mask.set(LEFT);
                  break;
              case uxsd::enum_loc_side::RIGHT_BOTTOM_LEFT:
                  side_mask.set(RIGHT);
                  side_mask.set(BOTTOM);
                  side_mask.set(LEFT);
                  break;
              case uxsd::enum_loc_side::BOTTOM:
                  side_mask.set(BOTTOM);
                  break;
              case uxsd::enum_loc_side::BOTTOM_LEFT:
                  side_mask.set(BOTTOM);
                  side_mask.set(LEFT);
                  break;
              case uxsd::enum_loc_side::LEFT:
                  side_mask.set(LEFT);
                  break;
              case uxsd::enum_loc_side::TOP_RIGHT_BOTTOM_LEFT:
                  side_mask.set(TOP);
                  side_mask.set(RIGHT);
                  side_mask.set(BOTTOM);
                  side_mask.set(LEFT);
                  break;
              default:
                  report_error(
                      "Invalid side %d", side);
          }
          return side_mask;
      }
  
      uxsd::enum_loc_side to_uxsd_loc_side(std::bitset<NUM_SIDES> sides) {
          // Error out when
          // - the side has no valid bits
          // - the side is beyond the mapping range: this is to warn any changes on side truth table which may cause the mapping failed
          if ((0 == sides.count())
              || (sides.to_ulong() > side_map_.size() - 1)) {
              report_error(
                  "Invalid side %ld", sides.to_ulong());
          }
          return side_map_[sides.to_ulong()];
      }
  
      Direction from_uxsd_node_direction(uxsd::enum_node_direction direction) {
          switch (direction) {
              case uxsd::enum_node_direction::INC_DIR:
                  return Direction::INC;
              case uxsd::enum_node_direction::DEC_DIR:
                  return Direction::DEC;
              case uxsd::enum_node_direction::BI_DIR:
                  return Direction::BIDIR;
              default:
                  report_error(
                      "Invalid node direction %d", direction);
          }
      }
  
      uxsd::enum_node_direction to_uxsd_node_direction(Direction direction) {
          switch (direction) {
              case Direction::INC:
                  return uxsd::enum_node_direction::INC_DIR;
              case Direction::DEC:
                  return uxsd::enum_node_direction::DEC_DIR;
              case Direction::BIDIR:
                  return uxsd::enum_node_direction::BI_DIR;
              default:
                  report_error(
                      "Invalid direction %d", direction);
          }
      }
  
      t_rr_type from_uxsd_node_type(uxsd::enum_node_type type) {
          switch (type) {
              case uxsd::enum_node_type::CHANX:
                  return CHANX;
              case uxsd::enum_node_type::CHANY:
                  return CHANY;
              case uxsd::enum_node_type::SOURCE:
                  return SOURCE;
              case uxsd::enum_node_type::SINK:
                  return SINK;
              case uxsd::enum_node_type::OPIN:
                  return OPIN;
              case uxsd::enum_node_type::IPIN:
                  return IPIN;
              default:
                  report_error(
                      "Invalid node type %d",
                      type);
          }
      }
      uxsd::enum_node_type to_uxsd_node_type(t_rr_type type) {
          switch (type) {
              case CHANX:
                  return uxsd::enum_node_type::CHANX;
              case CHANY:
                  return uxsd::enum_node_type::CHANY;
              case SOURCE:
                  return uxsd::enum_node_type::SOURCE;
              case SINK:
                  return uxsd::enum_node_type::SINK;
              case OPIN:
                  return uxsd::enum_node_type::OPIN;
              case IPIN:
                  return uxsd::enum_node_type::IPIN;
              default:
                  report_error(
                      "Invalid type %d", type);
          }
  
          return uxsd::enum_node_type::UXSD_INVALID;
      }
  
      SwitchType from_uxsd_switch_type(uxsd::enum_switch_type type) {
          SwitchType switch_type = SwitchType::INVALID;
          switch (type) {
              case uxsd::enum_switch_type::TRISTATE:
                  switch_type = SwitchType::TRISTATE;
                  break;
              case uxsd::enum_switch_type::MUX:
                  switch_type = SwitchType::MUX;
                  break;
              case uxsd::enum_switch_type::PASS_GATE:
                  switch_type = SwitchType::PASS_GATE;
                  break;
              case uxsd::enum_switch_type::SHORT:
                  switch_type = SwitchType::SHORT;
                  break;
              case uxsd::enum_switch_type::BUFFER:
                  switch_type = SwitchType::BUFFER;
                  break;
              default:
                  report_error(
                      "Invalid switch type '%d'\n", type);
          }
  
          return switch_type;
      }
  
      uxsd::enum_switch_type to_uxsd_switch_type(SwitchType type) {
          switch (type) {
              case SwitchType::TRISTATE:
                  return uxsd::enum_switch_type::TRISTATE;
              case SwitchType::MUX:
                  return uxsd::enum_switch_type::MUX;
              case SwitchType::PASS_GATE:
                  return uxsd::enum_switch_type::PASS_GATE;
              case SwitchType::SHORT:
                  return uxsd::enum_switch_type::SHORT;
              case SwitchType::BUFFER:
                  return uxsd::enum_switch_type::BUFFER;
              default:
                  report_error(
                      "Invalid switch type '%d'\n",
                      type);
          }
  
          return uxsd::enum_switch_type::UXSD_INVALID;
      }
  
      e_pin_type from_uxsd_pin_type(uxsd::enum_pin_type type) {
          switch (type) {
              case uxsd::enum_pin_type::OPEN:
                  return OPEN;
              case uxsd::enum_pin_type::OUTPUT:
                  return DRIVER;
              case uxsd::enum_pin_type::INPUT:
                  return RECEIVER;
              default:
                  report_error(
                      "Unknown pin class type %d", type);
          }
      }
  
      uxsd::enum_pin_type to_uxsd_pin_type(e_pin_type type) {
          switch (type) {
              case OPEN:
                  return uxsd::enum_pin_type::OPEN;
              case DRIVER:
                  return uxsd::enum_pin_type::OUTPUT;
              case RECEIVER:
                  return uxsd::enum_pin_type::INPUT;
              default:
                  report_error(
                      "Unknown pin class type %d", type);
          }
          return uxsd::enum_pin_type::UXSD_INVALID;
      }
  
      [[noreturn]] void report_error(const char* fmt, ...) {
          // Make a variable argument list
          va_list va_args;
  
          // Initialize variable argument list
          va_start(va_args, fmt);
  
          //Format string
          std::string str = vtr::vstring_fmt(fmt, va_args);
  
          // Reset variable argument list
          va_end(va_args);
  
          if (report_error_) {
              (*report_error_)(str.c_str());
          } else {
              throw std::runtime_error(str);
          }
  
          throw std::runtime_error("Unreachable line!");
      }
  
      // Temporary storage
      vtr::vector<RRIndexedDataId, short> seg_index_;
      std::string temp_string_;
  
      // Constant mapping which is frequently used
      std::array<uxsd::enum_loc_side, 16> side_map_;
  
      // Output for loads, and constant data for writes.
      int* wire_to_rr_ipin_switch_;
      t_chan_width* chan_width_;
      t_rr_graph_storage* rr_nodes_;
      RRGraphBuilder* rr_graph_builder_;
      RRGraphView* rr_graph_;
      std::vector<t_rr_switch_inf>* rr_switch_inf_;
      vtr::vector<RRIndexedDataId, t_rr_indexed_data>* rr_indexed_data_;
      t_rr_node_indices* rr_node_indices_;
      std::string* read_rr_graph_filename_;
  
      // Constant data for loads and writes.
      const t_graph_type graph_type_;
      const enum e_base_cost_type base_cost_type_;
      const bool do_check_rr_graph_;
      const char* read_rr_graph_name_;
      const bool read_edge_metadata_;
  
      const size_t num_arch_switches_;
      const t_arch_switch_inf* arch_switch_inf_;
      const std::vector<t_segment_inf>& segment_inf_;
      const std::vector<t_physical_tile_type>& physical_tile_types_;
      const DeviceGrid& grid_;
      MetadataStorage<int>* rr_node_metadata_;
      MetadataStorage<std::tuple<int, int, short>>* rr_edge_metadata_;
      vtr::string_internment* strings_;
      vtr::interned_string empty_;
      const std::function<void(const char*)>* report_error_;
  };
  