#include <cstdint>
#include <poplar/Target.hpp>
#include <poplar/Graph.hpp>
#include <gcl/TileAllocation.hpp>
#include <ipu_arch_info/ipuArchInfo.h>
#include <iostream>
#include <set>
#include <stdexcept>
extern "C" unsigned gcl_get_xb_context_index(unsigned);
extern "C" unsigned gcl_get_xb_context_tile_index(unsigned);
int main() {
  const auto &a = ipuArchInfoByName("ipu21");
  std::cout << "internalSyncZones=" << a.TEXCH_NUM_INTERNAL_SYNCZONES
            << " externalSyncZones=" << a.TEXCH_NUM_EXTERNAL_SYNCZONES
            << " localSync=" << a.TEXCH_SYNCZONE_LOCAL
            << " internalSync=" << a.TEXCH_SYNCZONE_INTERNAL << '\n';
  for (unsigned x : {0x41800000u, 0x41800001u, 0x41800002u, 0x41800003u,
                     0x40c00000u, 0x40c00001u, 0x40c000ffu})
    std::cout << std::hex << x << std::dec << ' '
              << a.disassembler.disassemble(true, x) << '\n';
  auto t = poplar::Target::createIPUTarget(1, "C600");
  poplar::Graph g(t);
  std::cout << "tiles=" << t.getTilesPerIPU()
            << " contextsPerXB=" << t.getNumContextsPerXB()
            << " tilesPerContext=" << t.getNumTilesPerXBContext()
            << " minIoTiles=" << gcl::getMinIoTiles(g)
            << " busSharing=" << t.supportsExchangeBusSharing()
            << " tilesPerSharedBus=" << t.getTilesPerSharedExchangeBus()
            << " exchangeBytesPerCycle=" << t.getExchangeBytesPerCycle() << '\n';
  for (bool pairs : {false, true}) {
    auto ids = gcl::perIPUTiles(g, 0, 32, false, pairs);
    std::set<unsigned> contexts;
    std::cout << "pairs=" << pairs << "\nvirtual physical combinedContext position hostXB hostContext hostPosition\n";
    for (auto v : ids) {
      auto p = g.convertVirtualTileToPhysicalTile(v);
      auto c = gcl_get_xb_context_index(p);
      contexts.insert(c);
      std::cout << v << ' ' << p << ' ' << c << ' '
                << gcl_get_xb_context_tile_index(p) << ' '
                << t.getTileHostExchangeXB(v) << ' '
                << t.getTileHostExchangeContext(v) << ' '
                << t.getTileHostExchangeContextPosition(v) << '\n';
    }
    std::cout << "distinctContexts=" << contexts.size() << '\n';
  }
  std::set<unsigned> allContexts;
  for (unsigned v = 0; v < t.getTilesPerIPU(); ++v) {
    auto p = g.convertVirtualTileToPhysicalTile(v);
    auto c = gcl_get_xb_context_index(p);
    auto k = gcl_get_xb_context_tile_index(p);
    if (c != (4 * t.getTileHostExchangeXB(v) + t.getTileHostExchangeContext(v)) ||
        k != t.getTileHostExchangeContextPosition(v) ||
        c != (((p >> 1) & 30) | (p & 1)) ||
        k != (((p >> 5) & 62) | ((p >> 1) & 1)))
      throw std::runtime_error("mapping mismatch");
    allContexts.insert(c);
  }
  auto io = gcl::perIPUTiles(g, 0, 32);
  auto compute = gcl::perIPUTiles(g, 32, t.getTilesPerIPU() - 32);
  std::set<unsigned> partition(io.begin(), io.end());
  for (auto v : compute)
    if (!partition.insert(v).second) throw std::runtime_error("partition overlap");
  if (partition.size() != t.getTilesPerIPU()) throw std::runtime_error("partition incomplete");
  std::cout << "PASS: all tile mappings; " << allContexts.size()
            << " contexts; IO/compute partition covers all tiles without overlap\n";
}
