#include "exchange_plan.hpp"

#include <cassert>
#include <iostream>
#include <vector>

int main() {
  const xcom::Plan rowZero = xcom::assemble(1, 0, 1);
  const xcom::Plan rowTen = xcom::assemble(21, 20, 1);
  const xcom::Plan rowEleven = xcom::assemble(23, 22, 1);
  assert(rowZero.receiver[4] == 0x61900000u);
  assert(rowTen.receiver[4] == 0x62300000u);
  assert(rowEleven.receiver[4] == 0x62400000u);

  const std::vector<unsigned> receivers = {1, 2, 3, 4, 5, 6, 7};
  const auto tree = xcom::buildRelayTree(0, receivers);
  assert(tree.size() == 3);
  assert(tree[0].size() == 1);
  assert(tree[1].size() == 2);
  assert(tree[2].size() == 4);
  const auto small = xcom::dispatchFanOut(0, {1, 2}, 3);
  assert(small.strategy == xcom::FanOutStrategy::ProperMulticast);
  const auto large = xcom::dispatchFanOut(0, receivers, 1024);
  assert(large.strategy == xcom::FanOutStrategy::ProperMulticast);

  const auto multicast = xcom::assembleMulticast(0, {274, 1286}, 3);
  assert(multicast.sender[0] == 0x41800003u);
  assert(multicast.sender[1] == 0x40a00016u);
  assert(multicast.sender[2] == 0x78400003u);
  assert(multicast.receivers[0][1] == 0x64000000u);
  assert(multicast.receivers[0][2] == 0x64008640u);
  assert(multicast.receivers[0][3] == 0x61814000u);

  const auto varied = xcom::assembleMulticast(0, {2, 1000}, 3);
  assert(varied.sender[1] == 0x40a0001eu);
  assert(varied.receivers[0][1] == 0x64000000u);
  assert(varied.receivers[1][1] == 0x6401c000u);
  assert(varied.receivers[1][3] == 0x61d14000u);

  const auto boundary = xcom::assembleMulticast(736, {100, 900}, 52);
  assert(boundary.receivers[0][2] ==
         (xcom::detail::delayPic(58) | 0x00014000u));
  assert(boundary.receivers[0][3] == xcom::detail::delayXPic(0, 0, 0x640));

  const auto immediate = xcom::assembleMulticast(736, {0, 2}, 3);
  assert(immediate.sender[1] == 0x78400003u);

  const auto offset = xcom::assembleMulticast(0, {274, 1286}, 3, 160);
  assert(offset.sender[1] == xcom::detail::delay(182));
  assert(offset.receivers[0][1] == xcom::detail::delayXPic(160, 0, 0));
  std::cout << "PASS\n";
}
