text: #ifndef PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
  #define PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
  
  #include <algorithm>
  #include <cassert>
  #include <map>
  #include <memory>
  
  #include <absl/types/optional.h>
  #include <prjxray/xilinx/xc7series/configuration_row.h>
  #include <prjxray/xilinx/xc7series/frame_address.h>
  #include <yaml-cpp/yaml.h>
  
  namespace prjxray {
  namespace xilinx {
  namespace xc7series {
  
  // GlobalClockRegion represents all the resources associated with a single
  // global clock buffer (BUFG) tile.  In 7-Series FPGAs, there are two BUFG
  // tiles that divide the chip into top and bottom "halves". Each half may
  // contains any number of rows, buses, and columns.
  class GlobalClockRegion {
  public:
    GlobalClockRegion() = default;
  
    // Construct a GlobalClockRegion from iterators that yield
    // FrameAddresses which are known to be valid. The addresses may be
    // noncontinguous and/or unordered but they must share the same row
    // half address component.
    template <typename T> GlobalClockRegion(T first, T last);
  
    // Returns true if the address falls within a valid range inside the
    // global clock region. The row half address component is ignored as it
    // is outside the context of a global clock region.
    bool IsValidFrameAddress(FrameAddress address) const;
  
    // Returns the next numerically increasing address known within this
    // global clock region. If the next address would fall outside this
    // global clock region, no address is returned. If the next address
    // would jump to a different block type, no address is returned as the
    // same block type in other global clock regions come numerically
    // before other block types.
    absl::optional<FrameAddress> GetNextFrameAddress(FrameAddress address) const;
  
  private:
    friend struct YAML::convert<GlobalClockRegion>;
  
    std::map<unsigned int, Row> rows_;
  };
  
  template <typename T> GlobalClockRegion::GlobalClockRegion(T first, T last) {
    assert(std::all_of(first, last, [&](const typename T::value_type &addr) {
      return addr.is_bottom_half_rows() == first->is_bottom_half_rows();
    }));
  
    std::sort(first, last, [](const FrameAddress &lhs, const FrameAddress &rhs) {
      return lhs.row() < rhs.row();
    });
  
    for (auto row_first = first; row_first != last;) {
      auto row_last =
          std::upper_bound(row_first, last, row_first->row(),
                           [](const uint8_t &lhs, const FrameAddress &rhs) {
                             return lhs < rhs.row();
                           });
  
      rows_.emplace(row_first->row(), std::move(Row(row_first, row_last)));
      row_first = row_last;
    }
  }
  
  } // namespace xc7series
  } // namespace xilinx
  } // namespace prjxray
  
  namespace YAML {
  template <> struct convert<prjxray::xilinx::xc7series::GlobalClockRegion> {
    static Node encode(const prjxray::xilinx::xc7series::GlobalClockRegion &rhs);
    static bool decode(const Node &node,
                       prjxray::xilinx::xc7series::GlobalClockRegion &lhs);
  };
  } // namespace YAML
  
  #endif // PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
  