#include <cstdint>
#include <iostream>
#include <random>
#include <string>

#include <boost/endian/conversion.hpp>
#include <boost/filesystem.hpp>

#include <rocksdb/db.h>

#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>

#define perq_WITH_STATS
// #define preq_DISABLE_STATS_OPERATIONS
#include <PersistentQueue.hpp>

namespace fs = boost::filesystem;

using namespace perq;

auto twister = std::mt19937(std::random_device()());
auto makeRandomNumber(uint32_t min, uint32_t max) -> uint32_t {
  auto distribution = std::uniform_int_distribution<uint32_t>(min, max);
  return distribution(twister);
}

auto makeRandomString(size_t size = makeRandomNumber(50, 3000)) -> std::string {
  auto ret = std::string(size, 0);
  for (size_t i = 0; i < size; ++i) {
    ret[i] = static_cast<char>(makeRandomNumber(0, 255));
  }
  return ret;
}

template <typename T>
bool IsEmpty(T& queue) {
  return !queue.Top().second && !queue.Pop() && !queue.Poll().second && queue.Size() == 0;
}

template <typename T>
bool IsSize(T& queue, size_t size) {
  return queue.Top().second && queue.Size() == size;
}

TEST_CASE("PrefixedNumericalKeyConverter", "[PrefixedNumericalKeyConverter]") {
  SECTION("16/8") {
    auto converter = PrefixedNumericalKeyConverter<uint16_t, uint8_t>(0x00);

    REQUIRE(converter.GetMaxId() == 0xFFu);
    uint16_t id = 0x00u;
    uint16_t key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x0000u)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xF0u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x00F0u)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x80u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x0080u)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xFFu;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x00FFu)));
    REQUIRE(converter.ToId(key) == 0xFFul);

    converter = PrefixedNumericalKeyConverter<uint16_t, uint8_t>(0x0F);

    REQUIRE(converter.GetMaxId() == 0xFFu);
    id = 0x00u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x0F00u)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xF0u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x0FF0u)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x80u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x0F80u)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xFFu;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0x0FFFu)));
    REQUIRE(converter.ToId(key) == 0xFFu);

    converter = PrefixedNumericalKeyConverter<uint16_t, uint8_t>(0xFFu);

    REQUIRE(converter.GetMaxId() == 0xFFu);
    id = 0x00u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0xFF00ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xF0u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0xFFF0ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x80u;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0xFF80ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xFFu;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint16_t>(0xFFFFul)));
    REQUIRE(converter.ToId(key) == 0xFFu);
  }

  SECTION("32/16") {
    auto converter = PrefixedNumericalKeyConverter<uint32_t, uint16_t>(0x0001);

    REQUIRE(converter.GetMaxId() == 0xFFFFul);
    uint32_t id = 0x00000000ul;
    uint32_t key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x00010000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x000000F0ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x000100F0ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00000800ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x00010800ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x0000A000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0001A000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00008000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x00018000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xFFFFFFFFul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0001FFFFul)));
    REQUIRE(converter.ToId(key) == 0x0000FFFFul);

    converter = PrefixedNumericalKeyConverter<uint32_t, uint16_t>(0x0F0F);

    REQUIRE(converter.GetMaxId() == 0xFFFFul);
    id = 0x00000000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0F0F0000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x000000F0ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0F0F00F0ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00000800ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0F0F0800ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x0000A000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0F0FA000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00008000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0F0F8000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xFFFFFFFFul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0x0F0FFFFFul)));
    REQUIRE(converter.ToId(key) == 0x0000FFFFul);

    converter = PrefixedNumericalKeyConverter<uint32_t, uint16_t>(0xFFFF);

    REQUIRE(converter.GetMaxId() == 0xFFFFul);
    id = 0x00000000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0xFFFF0000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x000000F0ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0xFFFF00F0ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00000800ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0xFFFF0800ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x0000A000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0xFFFFA000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00008000ul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0xFFFF8000ul)));
    REQUIRE(converter.ToId(key) == id);
    id = 0xFFFFFFFFul;
    key = converter.ToKey(id);
    REQUIRE(key == boost::endian::native_to_big(static_cast<uint32_t>(0xFFFFFFFFul)));
    REQUIRE(converter.ToId(key) == 0x0000FFFF);
  }

  SECTION("64/8") {
    auto converter = PrefixedNumericalKeyConverter<uint64_t, uint8_t>(0x01);

    REQUIRE(converter.GetMaxId() == 0x00FFFFFFFFFFFFFFull);
    uint64_t id = 0x0000000000000000ull;
    uint64_t key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x0100000000000000ull)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00FFFFFFFFFFFFFFull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x01FFFFFFFFFFFFFFull)));
    REQUIRE(converter.ToId(key) == id);

    converter = PrefixedNumericalKeyConverter<uint64_t, uint8_t>(0x0F);

    REQUIRE(converter.GetMaxId() == 0x00FFFFFFFFFFFFFFull);
    id = 0x0000000000000000ull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x0F00000000000000ull)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00FFFFFFFFFFFFFFull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x0FFFFFFFFFFFFFFFull)));
    REQUIRE(converter.ToId(key) == id);

    converter = PrefixedNumericalKeyConverter<uint64_t, uint8_t>(0xFF);

    REQUIRE(converter.GetMaxId() == 0x00FFFFFFFFFFFFFFull);
    id = 0x0000000000000000ull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0xFF00000000000000ull)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00FFFFFFFFFFFFFFull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0xFFFFFFFFFFFFFFFFull)));
    REQUIRE(converter.ToId(key) == id);
  }

  SECTION("No prefix") {
    auto converter = PrefixedNumericalKeyConverter<uint64_t, NoPrefix>(0);

    REQUIRE(converter.GetMaxId() == 0xFFFFFFFFFFFFFFFFull);
    uint64_t id = 0x0000000000000000ull;
    uint64_t key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x0000000000000000ull)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00FFFFFFFFFFFFFFull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x00FFFFFFFFFFFFFFull)));
    REQUIRE(converter.ToId(key) == id);

    converter = PrefixedNumericalKeyConverter<uint64_t, NoPrefix>(0x0F);

    REQUIRE(converter.GetMaxId() == 0xFFFFFFFFFFFFFFFFull);
    id = 0x0000000000000000ull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x0000000000000000ull)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00FFFFFFFFFFFFFFull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x00FFFFFFFFFFFFFFull)));
    REQUIRE(converter.ToId(key) == id);

    converter = PrefixedNumericalKeyConverter<uint64_t, NoPrefix>(0xFF);

    REQUIRE(converter.GetMaxId() == 0xFFFFFFFFFFFFFFFFull);
    id = 0x0000000000000000ull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x0000000000000000ull)));
    REQUIRE(converter.ToId(key) == id);
    id = 0x00FFFFFFFFFFFFFFull;
    key = converter.ToKey(id);
    REQUIRE(
      key == boost::endian::native_to_big(static_cast<uint64_t>(0x00FFFFFFFFFFFFFFull)));
    REQUIRE(converter.ToId(key) == id);
  }
}

TEST_CASE("PersistentQueueIdCorrector basic", "[PersistentQueueIdCorrector][basic]") {
  auto corrector = PersistentQueueIdCorrector<uint16_t>(0, 255, 50);

  REQUIRE(!corrector.IsOverEnd());
  REQUIRE(corrector.head() == 0);
  REQUIRE(corrector.tail() == 0);
  REQUIRE(corrector.previous_checked_head() == std::numeric_limits<uint16_t>::max());
  REQUIRE(corrector.previous_checked_tail() == std::numeric_limits<uint16_t>::max());

  for (size_t i = 1; i < 256; ++i) {
    REQUIRE(corrector.FeedNext(i) == i);
    REQUIRE(!corrector.IsOverEnd());
    REQUIRE(corrector.head() == 0);
    REQUIRE(corrector.tail() == i);
  }

  REQUIRE_THROWS_WITH(
    corrector.FeedNext(256),
    "Severe misuse of `FeedNext`: the provided ID is greater than the maximum ID");

  for (size_t i = 0; i < 256; ++i) {
    REQUIRE_THROWS_WITH(
      corrector.FeedNext(i),
      "Severe misuse of `PersistentQueueIdCorrector::FeedNext`: the queue is not over the end, but next ID passes the end");
    REQUIRE(!corrector.IsOverEnd());
    REQUIRE(corrector.head() == 0);
    REQUIRE(corrector.tail() == 255);
  }

  corrector = PersistentQueueIdCorrector<uint16_t>(0, 255, 50);

  for (size_t i = 1; i < 20; ++i)
    corrector.FeedNext(i);

  for (size_t i = 70; i < 256; ++i) {
    REQUIRE(corrector.FeedNext(i) == i);
    REQUIRE(corrector.IsOverEnd());
    REQUIRE(corrector.head() == 70);
    REQUIRE(corrector.tail() == i);
  }

  for (size_t i = 0; i < 20; ++i) {
    REQUIRE(corrector.FeedNext(i) == i);
    REQUIRE(corrector.IsOverEnd());
    REQUIRE(corrector.head() == 70);
    REQUIRE(corrector.tail() == i);
  }

  REQUIRE_THROWS_WITH(
    corrector.FeedNext(70),
    "Severe misuse of `PersistentQueueIdCorrector::FeedNext`: the queue is over then end for the second time");
}

template <typename TKey, typename TPrefix>
PersistentQueue<TKey, TPrefix, 231> createQueue(rocksdb::DB* db,
                                                size_t max_thread_number
                                                = std::numeric_limits<size_t>::max()) {
  if (max_thread_number == std::numeric_limits<size_t>::max())
    return PersistentQueue<TKey, TPrefix, 231>(db);
  else
    return PersistentQueue<TKey, TPrefix, 231>(db, max_thread_number);
}

template <typename TKey>
PersistentQueue<TKey> createQueue(rocksdb::DB* db,
                                  size_t max_thread_number
                                  = std::numeric_limits<size_t>::max()) {
  if (max_thread_number == std::numeric_limits<size_t>::max())
    return PersistentQueue<TKey>(db);
  else
    return PersistentQueue<TKey>(db, max_thread_number);
}

template <typename TKey>
void PersistentQueueBasicTest(size_t max_thread_number
                              = std::numeric_limits<size_t>::max()) {
  auto temp_directory_path = fs::temp_directory_path() / "perq";
  if (fs::exists(temp_directory_path)) {
    fs::remove_all(temp_directory_path);
  }

  std::unique_ptr<rocksdb::DB> db;
  auto temp_db = (rocksdb::DB*){};
  rocksdb::Options options;
  options.create_if_missing = true;
  rocksdb::Status status
    = rocksdb::DB::Open(options, temp_directory_path.string(), &temp_db);
  if (!status.ok())
    REQUIRE(false);
  db.reset(temp_db);

  SECTION("New queue") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(queue.stats() == Stats());

    // Emptiness test
    REQUIRE(IsEmpty(queue));

    // Push test
    auto value = makeRandomString();
    REQUIRE(queue.Push(value));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value, true));
    REQUIRE(IsSize(queue, 1));

    // Pop test
    REQUIRE(queue.Pop());
    REQUIRE(IsEmpty(queue));

    // Push test
    value = makeRandomString();
    REQUIRE(queue.Push(value));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value, true));
    REQUIRE(IsSize(queue, 1));

    // Poll test
    REQUIRE(queue.Poll() == std::pair<std::string, bool>(value, true));
    REQUIRE(IsEmpty(queue));

    // Twice Push and Poll test
    auto value1 = makeRandomString();
    REQUIRE(queue.Push(value1));
    REQUIRE(IsSize(queue, 1));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value1, true));
    auto value2 = makeRandomString();
    REQUIRE(queue.Push(value2));
    REQUIRE(IsSize(queue, 2));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value1, true));
    REQUIRE(queue.Poll() == std::pair<std::string, bool>(value1, true));
    REQUIRE(IsSize(queue, 1));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value2, true));
    REQUIRE(queue.Poll() == std::pair<std::string, bool>(value2, true));
    REQUIRE(IsEmpty(queue));

    // Twice Push and Pop test
    value1 = makeRandomString();
    REQUIRE(queue.Push(value1));
    REQUIRE(IsSize(queue, 1));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value1, true));
    value2 = makeRandomString();
    REQUIRE(queue.Push(value2));
    REQUIRE(IsSize(queue, 2));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value1, true));
    REQUIRE(queue.Pop());
    REQUIRE(IsSize(queue, 1));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value2, true));
    REQUIRE(queue.Pop());
    REQUIRE(IsEmpty(queue));

    // Fill the queue
    for (size_t i = 0; i < 100; ++i) {
      value = makeRandomString();
      REQUIRE(queue.Push(value));
      REQUIRE(IsSize(queue, i + 1));
    }

    REQUIRE(queue.stats() == Stats());
  }

  {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    for (size_t i = 0; i < 100; ++i) {
      queue.Push(makeRandomString());
    }

    db.reset(nullptr);
    status = rocksdb::DB::Open(options, temp_directory_path.string(), &temp_db);
    if (!status.ok())
      REQUIRE(false);

    db.reset(temp_db);

    REQUIRE(queue.stats() == Stats());
  }

  SECTION("Existing queue") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);

    REQUIRE(IsSize(queue, 100));

    REQUIRE(queue.Poll().second);
    REQUIRE(IsSize(queue, 99));
    auto value1 = makeRandomString();
    REQUIRE(queue.Push(value1));
    REQUIRE(IsSize(queue, 100));
    auto value2 = makeRandomString();
    REQUIRE(queue.Push(value2));
    REQUIRE(IsSize(queue, 101));
    REQUIRE(queue.Pop());
    REQUIRE(IsSize(queue, 100));
    REQUIRE(queue.Pop());
    REQUIRE(IsSize(queue, 99));

    REQUIRE(queue.stats() == Stats());
  }

  {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    while (queue.Pop())
      ;
    for (size_t i = 0; i < 99; ++i) {
      queue.Push(makeRandomString());
    }

    db.reset(nullptr);
    status = rocksdb::DB::Open(options, temp_directory_path.string(), &temp_db);
    if (!status.ok())
      REQUIRE(false);

    db.reset(temp_db);

    REQUIRE(queue.stats() == Stats());
  }

  SECTION("No-prefix queue") {
    // This is just a test, never do any similar on real data: do not change the queue
    // type
    auto queue = createQueue<TKey>(db.get(), max_thread_number);
    REQUIRE(IsSize(queue, 99));

    for (size_t i = 0; i < 99; ++i) {
      REQUIRE(queue.Poll().second);
      REQUIRE(queue.Size() == 98 - i);
    }

    // Emptiness test
    REQUIRE(!queue.Top().second);
    REQUIRE(!queue.Pop());
    REQUIRE(!queue.Poll().second);
    REQUIRE(!queue.Size());

    // Push test
    auto value = makeRandomString();
    REQUIRE(queue.Push(value));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value, true));
    REQUIRE(queue.Size() == 1);

    // Pop test
    REQUIRE(queue.Pop());
    REQUIRE(!queue.Top().second);
    REQUIRE(!queue.Pop());
    REQUIRE(!queue.Poll().second);
    REQUIRE(!queue.Size());

    // Push test
    value = makeRandomString();
    REQUIRE(queue.Push(value));
    REQUIRE(queue.Top() == std::pair<std::string, bool>(value, true));
    REQUIRE(queue.Size() == 1);

    // Poll test
    REQUIRE(queue.Poll() == std::pair<std::string, bool>(value, true));
    REQUIRE(!queue.Top().second);
    REQUIRE(!queue.Pop());
    REQUIRE(!queue.Poll().second);
    REQUIRE(!queue.Size());

    REQUIRE(queue.stats() == Stats());
  }
}

template <typename TKey>
void PersistentQueueParallelTest(size_t operation_number,
                                 size_t max_thread_number
                                 = std::numeric_limits<size_t>::max()) {
  auto temp_directory_path = fs::temp_directory_path() / "perq";
  if (fs::exists(temp_directory_path)) {
    fs::remove_all(temp_directory_path);
  }

  std::unique_ptr<rocksdb::DB> db;
  auto temp_db = (rocksdb::DB*){};
  rocksdb::Options options;
  options.create_if_missing = true;
  rocksdb::Status status
    = rocksdb::DB::Open(options, temp_directory_path.string(), &temp_db);
  if (!status.ok())
    REQUIRE(false);
  db.reset(temp_db);

  SECTION("Parallel Top") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(IsEmpty(queue));
    {
      std::thread thread_one([&]() {
        for (size_t i = 0; i < operation_number / 2; ++i)
          queue.Push("small");
      });
      std::thread thread_two([&]() {
        for (size_t i = 0; i < operation_number / 2; ++i)
          queue.Push("small 2");
      });
      thread_one.join();
      thread_two.join();
    }
    REQUIRE(IsSize(queue, operation_number));

    std::thread thread_one([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Top();
    });
    std::thread thread_two([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Top();
    });
    thread_one.join();
    thread_two.join();

    REQUIRE(IsSize(queue, operation_number));

    REQUIRE(queue.stats().top_get_miss_count == 0);
    REQUIRE(queue.stats().top_yield_count == 0);

    while (queue.Pop())
      ;
  }

  SECTION("Parallel Pop") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(IsEmpty(queue));
    {
      std::thread thread_one([&]() {
        for (size_t i = 0; i < operation_number / 2; ++i)
          queue.Push("small");
      });
      std::thread thread_two([&]() {
        for (size_t i = 0; i < operation_number / 2; ++i)
          queue.Push("small 2");
      });
      thread_one.join();
      thread_two.join();
    }
    REQUIRE(IsSize(queue, operation_number));

    std::thread thread_one([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Pop();
    });
    std::thread thread_two([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Pop();
    });
    thread_one.join();
    thread_two.join();
    REQUIRE(IsEmpty(queue));

    std::cerr << "Parallel Pop" << std::endl;
    std::cerr << "Pop cas repetition count: " << queue.stats().pop_cas_repetion_count
              << std::endl;
    std::cerr << "Pop yield count: " << queue.stats().pop_yield_count << std::endl;
    std::cerr << "Pop get miss count: " << queue.stats().pop_get_miss_count << std::endl;
  }

  SECTION("Parallel Poll") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(IsEmpty(queue));
    {
      std::thread thread_one([&]() {
        for (size_t i = 0; i < operation_number / 2; ++i)
          queue.Push("small");
      });
      std::thread thread_two([&]() {
        for (size_t i = 0; i < operation_number / 2; ++i)
          queue.Push("small 2");
      });
      thread_one.join();
      thread_two.join();
    }
    REQUIRE(IsSize(queue, operation_number));

    std::thread thread_one([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Poll();
    });
    std::thread thread_two([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Poll();
    });
    thread_one.join();
    thread_two.join();
    REQUIRE(IsEmpty(queue));

    std::cerr << "Parallel Poll" << std::endl;
    std::cerr << "Poll cas repetition count: " << queue.stats().poll_cas_repetion_count
              << std::endl;
    std::cerr << "Poll yield count: " << queue.stats().poll_yield_count << std::endl;
    std::cerr << "Poll get miss count: " << queue.stats().poll_get_miss_count
              << std::endl;
  }

  SECTION("Parallel Push") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(IsEmpty(queue));

    std::thread thread_one([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Push("small");
    });
    std::thread thread_two([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Push("small 2");
    });
    thread_one.join();
    thread_two.join();

    REQUIRE(IsSize(queue, operation_number));

    std::cerr << "Parallel Push" << std::endl;
    std::cerr << "Push cas repetition count: " << queue.stats().push_cas_repetion_count
              << std::endl;
    std::cerr << "Push yield count: " << queue.stats().push_yield_count << std::endl;
    std::cerr << "Push cas repetition max count: "
              << queue.stats().push_cas_repetion_max_count << std::endl;
    std::cerr << "Push cas yield max count: " << queue.stats().push_cas_yield_max_count
              << std::endl;

    while (queue.Pop())
      ;
  }

  SECTION("Parallel Push and Pop") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(IsEmpty(queue));

    std::thread thread_one([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Push("small");
    });
    std::thread thread_two([&]() {
      for (size_t i = 0; i < operation_number / 2;) {
        if (queue.Pop())
          ++i;
      }
    });
    thread_one.join();
    thread_two.join();

    REQUIRE(IsEmpty(queue));

    std::cerr << "Parallel Push and Pop" << std::endl;
    std::cerr << "Pop get miss count: " << queue.stats().pop_get_miss_count << std::endl;

    REQUIRE(queue.stats().pop_cas_repetion_count == 0);
    REQUIRE(queue.stats().pop_yield_count == 0);
    REQUIRE(queue.stats().push_cas_repetion_count == 0);
    REQUIRE(queue.stats().push_yield_count == 0);
    REQUIRE(queue.stats().push_cas_repetion_max_count == 0);
    REQUIRE(queue.stats().push_cas_yield_max_count == 0);
  }

  SECTION("Parallel Push and Poll") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(IsEmpty(queue));

    std::thread thread_one([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Push("small");
    });
    std::thread thread_two([&]() {
      for (size_t i = 0; i < operation_number / 2;) {
        if (queue.Poll().second)
          ++i;
      }
    });
    thread_one.join();
    thread_two.join();

    REQUIRE(IsEmpty(queue));

    std::cerr << "Parallel Push and Poll" << std::endl;
    std::cerr << "Poll get miss count: " << queue.stats().poll_get_miss_count
              << std::endl;

    REQUIRE(queue.stats().poll_cas_repetion_count == 0);
    REQUIRE(queue.stats().poll_yield_count == 0);
    REQUIRE(queue.stats().push_cas_repetion_count == 0);
    REQUIRE(queue.stats().push_yield_count == 0);
    REQUIRE(queue.stats().push_cas_repetion_max_count == 0);
    REQUIRE(queue.stats().push_cas_yield_max_count == 0);
  }

  SECTION("Parallel Push And Poll And Size") {
    auto queue = createQueue<TKey, uint8_t>(db.get(), max_thread_number);
    REQUIRE(IsEmpty(queue));

    std::atomic_bool is_running(true);

    std::thread thread_one([&]() {
      for (size_t i = 0; i < operation_number / 2; ++i)
        queue.Push("small");
    });
    std::thread thread_two([&]() {
      for (size_t i = 0; i < operation_number / 2;) {
        if (queue.Poll().second)
          ++i;
      }
      is_running = false;
    });
    std::thread thread_three([&]() {
      for (size_t i = 0; is_running; ++i) {
        if (queue.Size() > operation_number / 2)
          throw std::runtime_error("Invalid size");
      }
    });
    thread_one.join();
    thread_two.join();
    thread_three.join();

    REQUIRE(IsEmpty(queue));

    REQUIRE(queue.stats().push_cas_repetion_count == 0);
    REQUIRE(queue.stats().push_yield_count == 0);
    REQUIRE(queue.stats().push_cas_repetion_max_count == 0);
    REQUIRE(queue.stats().push_cas_yield_max_count == 0);
  }
}

TEST_CASE("PersistentQueue 16 basic", "[PersistentQueue][16][basic]") {
  PersistentQueueBasicTest<uint16_t>(20);
}

TEST_CASE("PersistentQueue 32 basic", "[PersistentQueue][32][basic]") {
  PersistentQueueBasicTest<uint32_t>();
}

TEST_CASE("PersistentQueue 64 basic", "[PersistentQueue][64][basic]") {
  PersistentQueueBasicTest<uint64_t>();
}

TEST_CASE("PersistentQueue 16 parallel", "[PersistentQueue][16][parallel]") {
  PersistentQueueParallelTest<uint16_t>(234, 20);
}

TEST_CASE("PersistentQueue 32 parallel", "[PersistentQueue][32][parallel]") {
  PersistentQueueParallelTest<uint32_t>(100000);
}

TEST_CASE("PersistentQueue 64 parallel", "[PersistentQueue][64][parallel]") {
  PersistentQueueParallelTest<uint64_t>(100000);
}


/*
==============================================================================
KratosTestApplication
A library based on:
Kratos
A General Purpose Software for Multi-Physics Finite Element Analysis
Version 1.0 (Released on march 05, 2007).

Copyright 2007
Pooyan Dadvand, Riccardo Rossi
pooyan@cimne.upc.edu
rrossi@cimne.upc.edu
- CIMNE (International Center for Numerical Methods in Engineering),
Gran Capita' s/n, 08034 Barcelona, Spain

Permission is hereby granted, free  of charge, to any person obtaining
a  copy  of this  software  and  associated  documentation files  (the
"Software"), to  deal in  the Software without  restriction, including
without limitation  the rights to  use, copy, modify,  merge, publish,
distribute,  sublicense and/or  sell copies  of the  Software,  and to
permit persons to whom the Software  is furnished to do so, subject to
the following condition:

Distribution of this code for  any  commercial purpose  is permissible
ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNERS.

The  above  copyright  notice  and  this permission  notice  shall  be
included in all copies or substantial portions of the Software.

THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT  SHALL THE AUTHORS OR COPYRIGHT HOLDERS  BE LIABLE FOR ANY
CLAIM, DAMAGES OR  OTHER LIABILITY, WHETHER IN AN  ACTION OF CONTRACT,
TORT  OR OTHERWISE, ARISING  FROM, OUT  OF OR  IN CONNECTION  WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

==============================================================================
*/

//
//   Project Name:        Kratos
//   Last modified by:    $Author:  $
//   Date:                $Date:  $
//   Revision:            $Revision: 1.2 $
//
//

// System includes

// External includes
#include <boost/python.hpp>

// Project includes
#include "includes/define.h"
#include "processes/process.h"
#include "custom_python/add_custom_utilities_to_python.h"

#include "spaces/ublas_space.h"
#include "linear_solvers/linear_solver.h"

#include "includes/model_part.h"

//#include "custom_utilities/CreateSPHParticle.h"
//#include "custom_utilities/CreateMLSParticle.h"
#include "custom_utilities/CreateMLSParticleGauss.h"
#include "custom_utilities/CreateLMEParticleGauss.h"

#include "custom_utilities/neighbours_calculator_SPH.h"
#include "custom_utilities/neighbours_calculator_MLS.h"
#include "custom_utilities/neighbours_calculator_LME.h"
//#include "custom_processes/node_and_element_erase_process.h"
#include "custom_utilities/nodal_values_utility.h"
#include "custom_utilities/gauss_coordiantes_update.h"
#include "custom_utilities/recompute_neighbours.h"

namespace Kratos
{

namespace Python
{

void  AddCustomUtilitiesToPython()
{
    using namespace boost::python;

//     typedef UblasSpace<double, CompressedMatrix, Vector> SparseSpaceType;
//     typedef UblasSpace<double, Matrix, Vector> LocalSpaceType;
//     typedef LinearSolver<SparseSpaceType, LocalSpaceType > LinearSolverType;

//     typedef Geometry<Node<3> >::GeometryType GeometryType;

    class_<CreateMLSParticleGauss > ("CreateMLSParticleGauss", init<ModelPart& ,unsigned int >())  //the input parameters is a model part
            .def("Execute", &CreateMLSParticleGauss::Execute);
    class_<CreateLMEParticleGauss > ("CreateLMEParticleGauss", init<ModelPart& ,unsigned int >())  //the input parameters is a model part
            .def("Execute", &CreateLMEParticleGauss::Execute);

    //class_<CreateMLSParticle > ("CreateMLSParticle", init<ModelPart& ,unsigned int, float  >())  //the input parameters is a model part
            //.def("Execute", &CreateMLSParticle::Execute);

    //class_<Neighbours_Calculator_MLS<LinearMLSKernel> > ("Neighbours_Calculator_MLS", init<>())  //the input parameters is a model part
            //.def("Search_Neighbours", &Neighbours_Calculator_MLS<LinearMLSKernel>::Search_Neighbours);

//    class_<WCSPHUtils>("WCSPHUtils", init<>())
//            .def("UpdateDensities",&WCSPHUtils::UpdateDensities)
//            .def("UpdatePositions",&WCSPHUtils::UpdatePositions)
//            .def("UpdateVelocities",&WCSPHUtils::UpdateVelocities)
//            .def("ApplyFixedBoundary",&WCSPHUtils::ApplyFixedBoundary)
//            .def("ApplyFreeSurfaceBoundary",&WCSPHUtils::ApplyFreeSurfaceBoundary)
//            .def("UpdatePressures",&WCSPHUtils::UpdatePressures)
//            .def("CheckTimeStep",&WCSPHUtils::CheckTimeStep)
//            .def("ViscousTerm",&WCSPHUtils::ViscousTerm)
//            .def("GradientOfPressure",&WCSPHUtils::GradientOfPressure)
//            .def("RemoveBoundaryForce",&WCSPHUtils::RemoveBoundaryForce)
//            .def("DivergenceOfVelocity",&WCSPHUtils::DivergenceOfVelocity)
//            .def("GetPressureAcceleration",&WCSPHUtils::GetPressureAcceleration)
//            .def("GetViscousAcceleration",&WCSPHUtils::GetViscousAcceleration)
//            .def("GetNeighbours",&WCSPHUtils::GetNeighbours)
//            .def("Printer",&WCSPHUtils::Printer)
//            .def("ApplyInitialConditions",&WCSPHUtils::ApplyInitialConditions)
//            .def("MarkOuterNodes",&WCSPHUtils::MarkOuterNodes)
//            .def("GetBoundaryAcceleration",&WCSPHUtils::GetBoundaryAcceleration);

//    class_<PCISPHUtils>("PCISPHUtils", init<>())
//            .def("UpdatePositions",&PCISPHUtils::UpdatePositions)
//            .def("UpdateVelocities",&PCISPHUtils::UpdateVelocities)
//            .def("ApplyFixedBoundary",&PCISPHUtils::ApplyFixedBoundary)
//            .def("ApplyFreeSurfaceBoundary",&PCISPHUtils::ApplyFreeSurfaceBoundary)
//            .def("CheckTimeStep",&PCISPHUtils::CheckTimeStep)
//            .def("ViscousTerm",&PCISPHUtils::ViscousTerm)
//            .def("GradientOfPressure",&PCISPHUtils::GradientOfPressure)
//            .def("RemoveBoundaryForce",&PCISPHUtils::RemoveBoundaryForce)
//            .def("GetViscousAcceleration",&PCISPHUtils::GetViscousAcceleration)
//            .def("DivergenceOfVelocity",&PCISPHUtils::DivergenceOfVelocity)
//            .def("GetNeighbours",&PCISPHUtils::GetNeighbours)
//            .def("Printer",&PCISPHUtils::Printer)
//            .def("Iterate",&PCISPHUtils::Iterate)
//            .def("MarkOuterNodes",&PCISPHUtils::MarkOuterNodes)
//            .def("SetPressureToZero",&PCISPHUtils::SetPressureToZero)
//            .def("GetBoundaryAcceleration",&PCISPHUtils::GetBoundaryAcceleration);

//    class_<ISPHUtils>("ISPHUtils", init<>())
//            .def("UpdatePositions",&ISPHUtils::UpdatePositions)
//            .def("UpdateToFinalVelocities",&ISPHUtils::UpdateToFinalVelocities)
//            .def("UpdateToTemporaryVelocities",&ISPHUtils::UpdateToTemporaryVelocities)
//            .def("ApplyFixedBoundary",&ISPHUtils::ApplyFixedBoundary)
//            .def("ApplyFreeSurfaceBoundary",&ISPHUtils::ApplyFreeSurfaceBoundary)
//            .def("CheckTimeStep",&ISPHUtils::CheckTimeStep)
//            .def("ViscousTerm",&ISPHUtils::ViscousTerm)
//            .def("GradientOfPressure",&ISPHUtils::GradientOfPressure)
//            .def("RemoveBoundaryForce",&ISPHUtils::RemoveBoundaryForce)
//            .def("GetViscousAcceleration",&ISPHUtils::GetViscousAcceleration)
//            .def("GetNeighbours",&ISPHUtils::GetNeighbours)
//            .def("Printer",&ISPHUtils::Printer)
//            .def("MarkOuterNodes",&ISPHUtils::MarkOuterNodes)
//            .def("SetPressureToZero",&ISPHUtils::SetPressureToZero)
//            .def("SolvePressurePoissonEquation",&ISPHUtils::SolvePressurePoissonEquation)
//            .def("GetBoundaryAcceleration",&ISPHUtils::GetBoundaryAcceleration);

    //class_<NodeAndElementEraseProcess>("NodeAndElementEraseProcess", init < ModelPart& >())
            //.def("Execute", &NodeAndElementEraseProcess::Execute);

    class_<Nodal_Values_Utility>
    ( "Nodal_Values_Utility", init<ModelPart&, int >() )
    .def( "CalculateNodalStress", &Nodal_Values_Utility::CalculateNodalStress )
    .def( "CalculateNodalArea", &Nodal_Values_Utility::CalculateNodalArea )
    ;

    class_<Gauss_Coordinates_Update_Utility>
    ( "Gauss_Coordinates_Update_Utility", init<ModelPart& >() )
    .def( "UpdateGaussCoordinates", &Gauss_Coordinates_Update_Utility::UpdateGaussCoordinates )
    ;

    class_<Recompute_Neighbours_Utility>
    ( "RecomputeNeighbours", init<ModelPart& >() )
    .def( "Recompute_Neighbours", &Recompute_Neighbours_Utility::Recompute_Neighbours )
    ;

}

}  // namespace Python.

} // Namespace Kratos


/*=========================================================================
 *
 *  Copyright David Doria 2012 daviddoria@gmail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#ifndef HistogramDifferenceAcceptanceVisitor_HPP
#define HistogramDifferenceAcceptanceVisitor_HPP

#include <boost/graph/graph_traits.hpp>

// Parent class
#include "Visitors/AcceptanceVisitors/AcceptanceVisitorParent.h"

// Custom
#include "Utilities/Histogram/HistogramGenerator.h"

// Submodules
#include <Mask/Mask.h>
#include <Mask/MaskOperations.h>
#include <ITKHelpers/ITKHelpers.h>

// ITK
#include "itkImage.h"
#include "itkImageRegion.h"

/**

 */
template <typename TGraph, typename TImage>
struct HistogramDifferenceAcceptanceVisitor : public AcceptanceVisitorParent<TGraph>
{
  TImage* Image;
  Mask* MaskImage;

  const unsigned int HalfWidth;

  float DifferenceThreshold;

  std::vector<float> Mins;
  std::vector<float> Maxs;

  typedef typename boost::graph_traits<TGraph>::vertex_descriptor VertexDescriptorType;

  HistogramDifferenceAcceptanceVisitor(TImage* const image, Mask* const mask, const unsigned int halfWidth,
                                       const std::vector<float>& mins, const std::vector<float>& maxs,
                                       const float differenceThreshold = 100.0f) :
    AcceptanceVisitorParent<TGraph>("HistogramDifferenceAcceptanceVisitor"),
    Image(image), MaskImage(mask), HalfWidth(halfWidth), Mins(mins), Maxs(maxs),
    DifferenceThreshold(differenceThreshold)
  {

  }

  bool AcceptMatch(VertexDescriptorType target, VertexDescriptorType source) const
  {
    float computedEnergy;
    return AcceptMatch(target, source, computedEnergy);
  }

  bool AcceptMatch(VertexDescriptorType target, VertexDescriptorType source, float& computedEnergy) const override
  {
    itk::Index<2> targetPixel = ITKHelpers::CreateIndex(target);
    itk::ImageRegion<2> targetRegion = ITKHelpers::GetRegionInRadiusAroundPixel(targetPixel, this->HalfWidth);

    itk::Index<2> sourcePixel = ITKHelpers::CreateIndex(source);
    itk::ImageRegion<2> sourceRegion = ITKHelpers::GetRegionInRadiusAroundPixel(sourcePixel, this->HalfWidth);

    // Get the pixels to use in the target region
    std::vector<typename TImage::PixelType> validPixelsTargetRegion =
             MaskOperations::GetValidPixelsInRegion(Image, MaskImage, targetRegion);

    // Get the pixels to use in the source region
    std::vector<itk::Offset<2> > validOffsets = MaskImage->GetValidOffsetsInRegion(targetRegion);
    std::vector<itk::Index<2> > sourcePatchValidPixelIndices =
           ITKHelpers::OffsetsToIndices(validOffsets, sourceRegion.GetIndex());
    std::vector<typename TImage::PixelType> validPixelsSourceRegion =
           ITKHelpers::GetPixelValues(Image, sourcePatchValidPixelIndices);

    assert(validPixelsSourceRegion.size() == validPixelsTargetRegion.size());

    unsigned int numberOfPixels = validPixelsTargetRegion.size();

    float totalHistogramDifference = 0.0f;

    for(unsigned int component = 0; component < Image->GetNumberOfComponentsPerPixel(); ++component)
    {
      std::vector<float> targetValues(numberOfPixels);
      std::vector<float> sourceValues(numberOfPixels);

      for(unsigned int pixelId = 0; pixelId < numberOfPixels; ++pixelId)
      {
        targetValues[pixelId] = validPixelsTargetRegion[pixelId][component];
        sourceValues[pixelId] = validPixelsSourceRegion[pixelId][component];
      }

      typedef HistogramGenerator<float> HistogramGeneratorType;
      typedef HistogramGeneratorType::HistogramType HistogramType;
      unsigned int numberOfBins = 20;
      HistogramType targetHistogram = HistogramGeneratorType::ScalarHistogram(targetValues, numberOfBins,
                                                                      this->Mins[component], this->Maxs[component]);
      HistogramType sourceHistogram = HistogramGeneratorType::ScalarHistogram(sourceValues, numberOfBins,
                                                                      this->Mins[component], this->Maxs[component]);

      // We normalize the histograms because the magnitude of the histogram difference should not
      // change based on the number of pixels that were in the valid region of the patches.
      Helpers::NormalizeVector(targetHistogram);
      Helpers::NormalizeVector(sourceHistogram);

      float channelHistogramDifference = Helpers::VectorSumOfAbsoluteDifferences(targetHistogram, sourceHistogram);
      totalHistogramDifference += channelHistogramDifference;
    }

    // Compute the difference
    computedEnergy = totalHistogramDifference;
    std::cout << "HistogramDifferenceAcceptanceVisitor Energy: " << computedEnergy << std::endl;

    if(computedEnergy < this->DifferenceThreshold)
      {
      std::cout << "HistogramDifferenceAcceptanceVisitor: Match accepted (less than " << this->DifferenceThreshold
                << ")" << std::endl;
      return true;
      }
    else
      {
      std::cout << "HistogramDifferenceAcceptanceVisitor: Match rejected (greater than " << this->DifferenceThreshold
                << ")" << std::endl;
      return false;
      }
  }

};

#endif


// -*- C++ -*-
//
// Copyright Sylvain Bougerel 2009 - 2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file COPYING or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#define BOOST_TEST_DYN_LINK
#define SPATIAL_ENABLE_ASSERT // detect interal issues that should not occur

#include <boost/test/unit_test.hpp>
#include "../../src/neighbor_iterator.hpp"
#include "spatial_test_fixtures.hpp"

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_default, Tp, every_quad )
{
  Tp fix(0);
  neighbor_iterator
    <typename Tp::container_type,
     euclidian<typename Tp::container_type, double,
               accessor_minus<quad_access, quad, double> > >
    a;
  a.node = fix.container.end().node;
  neighbor_iterator
    <typename Tp::container_type,
     euclidian<typename Tp::container_type, double,
               accessor_minus<quad_access, quad, double> > >
    b(a);
  neighbor_iterator
    <const typename Tp::container_type,
     euclidian<typename Tp::container_type, double,
               accessor_minus<quad_access, quad, double> > > c;
  c = a;
  neighbor_iterator_pair
    <typename Tp::container_type,
     euclidian<typename Tp::container_type, double,
               accessor_minus<quad_access, quad, double> > >
    p, q(a, b);
  neighbor_iterator_pair
    <const typename Tp::container_type,
     euclidian<typename Tp::container_type, double,
               accessor_minus<quad_access, quad, double> > >
    r, s(c, c), t(p);
  BOOST_CHECK(a == b);
  BOOST_CHECK(!(a != c));
  typename Tp::container_type::iterator i = a;
  BOOST_CHECK(i == a);
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_compact, Tp, double6_maps )
{
  Tp fix(0);
  neighbor_iterator<typename Tp::container_type> a;
  a.node = fix.container.end().node;
  neighbor_iterator<typename Tp::container_type> b(a);
  neighbor_iterator<const typename Tp::container_type> c;
  c = a;
  neighbor_iterator_pair<typename Tp::container_type> p, q(a, b);
  neighbor_iterator_pair<const typename Tp::container_type> r, s(c, c), t(p);
  BOOST_CHECK(a == b);
  BOOST_CHECK(!(a != c));
  typename Tp::container_type::iterator i = a;
  BOOST_CHECK(i == a);
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_const, Tp, double6_sets )
{
  // Check that all these consts operations are working and compiling cleanly.
  Tp fix(1, same());
  neighbor_iterator<const typename Tp::container_type>
    a = neighbor_cbegin(fix.container, make_double6(1.0));
  a++;
  a--;
  a = neighbor_cend(fix.container, make_double6(1.0));
  a = neighbor_clower_bound(fix.container, make_double6(1.0), 1.0);
  a = neighbor_cupper_bound(fix.container, make_double6(1.0), 1.0);
  neighbor_iterator_pair<const typename Tp::container_type>
    p = neighbor_crange(fix.container, make_double6(1.0));
  silence_unused(p);
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_euclidian_neighbor_const, Tp, double6_sets )
{
  // Check that all these consts operations are working and compiling cleanly.
  Tp fix(1, same());
  euclidian_neighbor_iterator<const typename Tp::container_type, double>
    a = euclidian_neighbor_cbegin(fix.container, make_double6(1.0));
  a++;
  a--;
  a = euclidian_neighbor_cend(fix.container, make_double6(1.0));
  a = euclidian_neighbor_clower_bound(fix.container, make_double6(1.0), 1.0);
  a = euclidian_neighbor_cupper_bound(fix.container, make_double6(1.0), 1.0);
  euclidian_neighbor_iterator_pair<const typename Tp::container_type, double>
    p = euclidian_neighbor_crange(fix.container, make_double6(1.0));
  silence_unused(p);
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_quadrance_neighbor_const, Tp, double6_sets )
{
  // Check that all these consts operations are working and compiling cleanly.
  Tp fix(1, same());
  quadrance_neighbor_iterator<const typename Tp::container_type, double>
    a = quadrance_neighbor_cbegin(fix.container, make_double6(1.0));
  a++;
  a--;
  a = quadrance_neighbor_cend(fix.container, make_double6(1.0));
  a = quadrance_neighbor_clower_bound(fix.container, make_double6(1.0), 1.0);
  a = quadrance_neighbor_cupper_bound(fix.container, make_double6(1.0), 1.0);
  quadrance_neighbor_iterator_pair<const typename Tp::container_type, double>
    p = quadrance_neighbor_crange(fix.container, make_double6(1.0));
  silence_unused(p);
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_manhattan_neighbor_const, Tp, double6_sets )
{
  // Check that all these consts operations are working and compiling cleanly.
  Tp fix(1, same());
  manhattan_neighbor_iterator<const typename Tp::container_type, double>
    a = manhattan_neighbor_cbegin(fix.container, make_double6(1.0));
  a++;
  a--;
  a = manhattan_neighbor_cend(fix.container, make_double6(1.0));
  a = manhattan_neighbor_clower_bound(fix.container, make_double6(1.0), 1.0);
  a = manhattan_neighbor_cupper_bound(fix.container, make_double6(1.0), 1.0);
  manhattan_neighbor_iterator_pair<const typename Tp::container_type, double>
    p = manhattan_neighbor_crange(fix.container, make_double6(1.0));
  silence_unused(p);
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_accessors, Tp, double6_maps )
{
  Tp fix(1, same());
  neighbor_iterator<typename Tp::container_type>
    a(fix.container, typename neighbor_iterator<typename Tp::container_type>
      ::metric_type(), make_double6(0., 1., 2., 3., 4., 5.),
      fix.container.end(), .0);
  BOOST_CHECK(target_key(a) == make_double6(0., 1., 2., 3., 4., 5.));
  BOOST_CHECK_EQUAL(.0, distance(a));
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_deference, Tp, double6_maps )
{
  Tp fix(2, same());
  neighbor_iterator<typename Tp::container_type>
    a(fix.container, typename neighbor_iterator<typename Tp::container_type>
      ::metric_type(), make_double6(0., 1., 2., 3., 4., 5.),
      fix.container.begin(), .0);
  BOOST_CHECK(*a == *fix.container.begin());
  BOOST_CHECK(a->first == fix.container.begin()->first);
  a->second = "Value assignment must work.";
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_minimum, Tp, double6_sets )
{
  // Prove that you can find the closest value with N nodes, down to 1 node
  {
    Tp fix(100, randomize(-20, 20));
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(-22, 22)(target, 0, 0);
        unsigned int count = 0;
        distance_type min_dist = metric.distance_to_key
          (fix.container.rank()(), target, *fix.container.begin());
        for(typename Tp::container_type::iterator
              i = fix.container.begin(); i != fix.container.end(); ++i)
          {
            distance_type tmp = metric.distance_to_key(fix.container.rank()(),
                                                       target, *i);
            if (tmp < min_dist) { min_dist = tmp; }
            ++count;
          }
        BOOST_CHECK_EQUAL(count, fix.container.size());
        iter = neighbor_begin(fix.container, target);
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), min_dist, 0.000000001);
        fix.container.erase(iter);
      }
  }
  // Prove that you can find the min in N nodes, with target = nodes
  {
    Tp fix(100, same());
    neighbor_iterator<typename Tp::container_type> iter;
    double6 target;
    while (!fix.container.empty())
      {
        same()(target, 0, 100);
        iter = neighbor_begin(fix.container, target);
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), 0.0, 0.000000001);
        fix.container.erase(iter);
      }
  }
  // Prove that you can find the min if a tree has a single element
  {
    Tp fix(1, same());
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target = make_double6(2.);
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    iter = neighbor_begin(fix.container, target);
    BOOST_CHECK(iter != neighbor_end(fix.container, target));
    distance_type dist = metric.distance_to_key(fix.container.rank()(),
                                                target, *iter);
    BOOST_CHECK_CLOSE(distance(iter), dist, 0.000000001);
    BOOST_CHECK(*iter == *fix.container.begin());
  }
  // Prove that you can find the min in a very unbalanced tree
  {
    Tp fix(40, increase());
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(0, 40)(target, 0, 0);
        unsigned int count = 0;
        distance_type min_dist = metric.distance_to_key
          (fix.container.rank()(), target, *fix.container.begin());
        for(typename Tp::container_type::iterator
              i = fix.container.begin(); i != fix.container.end(); ++i)
          {
            distance_type tmp = metric.distance_to_key(fix.container.rank()(),
                                                       target, *i);
            if (tmp < min_dist) { min_dist = tmp; }
            ++count;
          }
        BOOST_CHECK_EQUAL(count, fix.container.size());
        iter = neighbor_begin(fix.container, target);
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), min_dist, 0.000000001);
        fix.container.erase(iter);
      }
  }
  // Prove that you can find the min in an opposite unbalanced tree
  {
    Tp fix(40, decrease());
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(0, 40)(target, 0, 0);
        unsigned int count = 0;
        distance_type min_dist = metric.distance_to_key
          (fix.container.rank()(), target, *fix.container.begin());
        for(typename Tp::container_type::iterator
              i = fix.container.begin(); i != fix.container.end(); ++i)
          {
            distance_type tmp = metric.distance_to_key(fix.container.rank()(),
                                                       target, *i);
            if (tmp < min_dist) { min_dist = tmp; }
            ++count;
          }
        BOOST_CHECK_EQUAL(count, fix.container.size());
        iter = neighbor_begin(fix.container, target);
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), min_dist, 0.000000001);
        fix.container.erase(iter);
      }
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_lower_pick_apart, Tp, int2_sets )
{
  // This test ensures that if 2 equal minimums are on the left and right of
  // the tree, both nearest and lower bound = 0 will pick the same node
  Tp fix(0);
  int2 target(1, 1);
  fix.container.insert(int2(0, -2)); // start at root (at sqrt(10))
  fix.container.insert(int2(-1, 1)); // will go left (at sqrt(4))
  fix.container.insert(int2(1, -1)); // will go right (at sqrt(4))
  neighbor_iterator<typename Tp::container_type> i
    = neighbor_begin(fix.container, target);
  neighbor_iterator<typename Tp::container_type> j
    = neighbor_lower_bound(fix.container, target, 0.f);
  BOOST_CHECK(i == j);
  BOOST_CHECK_EQUAL(distance(i), std::sqrt(4.0));
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_upper_pick_apart, Tp, int2_sets )
{
  // This test ensures that if 2 equal minimums are on the left and right of
  // the tree, both nearest and upper bound = 0 will pick the same node
  Tp fix(0);
  int2 target(1, 1);
  fix.container.insert(int2(0, -2)); // start at root (at sqrt(10))
  fix.container.insert(int2(-1, 1)); // will go left (at sqrt(4))
  fix.container.insert(int2(1, -1)); // will go right (at sqrt(4))
  neighbor_iterator<typename Tp::container_type> i
    = neighbor_begin(fix.container, target);
  neighbor_iterator<typename Tp::container_type> j
    = neighbor_upper_bound(fix.container, target, 0.f);
  BOOST_CHECK(i == j);
  BOOST_CHECK_EQUAL(distance(i), std::sqrt(4.0));
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_maximum, Tp, double6_sets )
{
  // Prove that you can find the closest value with N nodes, down to 1 node
  {
    Tp fix(100, randomize(-20, 20));
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(-22, 22)(target, 0, 0);
        unsigned int count = 0;
        distance_type max_dist = metric.distance_to_key
          (fix.container.rank()(), target, *fix.container.begin());
        for(typename Tp::container_type::iterator
              i = fix.container.begin(); i != fix.container.end(); ++i)
          {
            distance_type tmp = metric.distance_to_key(fix.container.rank()(),
                                                       target, *i);
            if (tmp > max_dist) { max_dist = tmp; }
            ++count;
          }
        BOOST_CHECK_EQUAL(count, fix.container.size());
        iter = neighbor_end(fix.container, target); --iter;
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), max_dist, 0.000000001);
        neighbor_iterator<typename Tp::container_type> tmp = iter;
        BOOST_CHECK(++tmp == neighbor_end(fix.container, target));
        fix.container.erase(iter);
      }
  }
  // Prove that you can find the min in N nodes, with target = nodes
  {
    Tp fix(100, same());
    neighbor_iterator<typename Tp::container_type> iter;
    double6 target;
    while (!fix.container.empty())
      {
        same()(target, 0, 100);
        iter = neighbor_end(fix.container, target); --iter;
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), 0.0, 0.000000001);
        neighbor_iterator<typename Tp::container_type> tmp = iter;
        BOOST_CHECK(++tmp == neighbor_end(fix.container, target));
        fix.container.erase(iter);
      }
  }
  // Prove that you can find the min if a tree has a single element
  {
    Tp fix(1, same());
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target = make_double6(2.);
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    iter = neighbor_end(fix.container, target); --iter;
    BOOST_CHECK(iter != neighbor_end(fix.container, target));
    distance_type dist = metric.distance_to_key(fix.container.rank()(),
                                                target, *iter);
    BOOST_CHECK_CLOSE(distance(iter), dist, 0.000000001);
    BOOST_CHECK(*iter == *--fix.container.end());
    neighbor_iterator<typename Tp::container_type> tmp = iter;
    BOOST_CHECK(++tmp == neighbor_end(fix.container, target));
  }
  // Prove that you can find the min in a very unbalanced tree
  {
    Tp fix(40, increase());
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(0, 40)(target, 0, 0);
        unsigned int count = 0;
        distance_type max_dist = metric.distance_to_key
          (fix.container.rank()(), target, *fix.container.begin());
        for(typename Tp::container_type::iterator
              i = fix.container.begin(); i != fix.container.end(); ++i)
          {
            distance_type tmp = metric.distance_to_key(fix.container.rank()(),
                                                       target, *i);
            if (tmp > max_dist) { max_dist = tmp; }
            ++count;
          }
        BOOST_CHECK_EQUAL(count, fix.container.size());
        iter = neighbor_end(fix.container, target); --iter;
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), max_dist, 0.000000001);
        neighbor_iterator<typename Tp::container_type> tmp = iter;
        BOOST_CHECK(++tmp == neighbor_end(fix.container, target));
        fix.container.erase(iter);
      }
  }
  // Prove that you can find the min in an opposite unbalanced tree
  {
    Tp fix(40, decrease());
    neighbor_iterator<typename Tp::container_type> iter;
    typename neighbor_iterator<typename Tp::container_type>::metric_type
      metric(iter.metric());
    double6 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(0, 40)(target, 0, 0);
        unsigned int count = 0;
        distance_type max_dist = metric.distance_to_key
          (fix.container.rank()(), target, *fix.container.begin());
        for(typename Tp::container_type::iterator
              i = fix.container.begin(); i != fix.container.end(); ++i)
          {
            distance_type tmp = metric.distance_to_key(fix.container.rank()(),
                                                       target, *i);
            if (tmp > max_dist) { max_dist = tmp; }
            ++count;
          }
        BOOST_CHECK_EQUAL(count, fix.container.size());
        iter = neighbor_end(fix.container, target); --iter;
        BOOST_CHECK(iter != neighbor_end(fix.container, target));
        BOOST_CHECK_CLOSE(distance(iter), max_dist, 0.000000001);
        neighbor_iterator<typename Tp::container_type> tmp = iter;
        BOOST_CHECK(++tmp == neighbor_end(fix.container, target));
        fix.container.erase(iter);
      }
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_increment, Tp, int2_maps )
{
  // Prove that you can iterate N nodes, down to 1 node
  {
    Tp fix(100, randomize(-20, 20));
    neighbor_iterator<typename Tp::container_type> iter;
    int2 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(-22, 22)(target, 0, 0);
        size_t countdown = fix.container.size();
        iter = neighbor_begin(fix.container, target);
        distance_type min_dist = distance(iter++);
        for(; --countdown != 0
              && iter != neighbor_end(fix.container, target); ++iter)
          {
            distance_type tmp = distance(iter);
            BOOST_CHECK_GE(tmp, min_dist);
            min_dist = tmp;
          }
        BOOST_CHECK_EQUAL(countdown, 0u);
        BOOST_CHECK(iter == neighbor_end(fix.container, target));
        fix.container.erase(fix.container.begin());
      }
  }
  // Prove that you can iterate a very unbalanced tree
  {
    Tp fix(40, increase());
    neighbor_iterator<typename Tp::container_type> iter;
    int2 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(0, 40)(target, 0, 0);
        size_t countdown = fix.container.size();
        iter = neighbor_begin(fix.container, target);
        distance_type min_dist = distance(iter++);
        for(; --countdown != 0
              && iter != neighbor_end(fix.container, target); ++iter)
          {
            distance_type tmp = distance(iter);
            BOOST_CHECK_GE(tmp, min_dist);
            min_dist = tmp;
          }
        BOOST_CHECK_EQUAL(countdown, 0u);
        BOOST_CHECK(iter == neighbor_end(fix.container, target));
        fix.container.erase(fix.container.begin());
      }
  }
  // Prove that you can iterate an opposite unbalanced tree
  {
    Tp fix(40, decrease());
    neighbor_iterator<typename Tp::container_type> iter;
    int2 target;
    typedef typename neighbor_iterator<typename Tp::container_type>
      ::distance_type distance_type;
    while (!fix.container.empty())
      {
        randomize(0, 40)(target, 0, 0);
        size_t countdown = fix.container.size();
        iter = neighbor_begin(fix.container, target);
        distance_type min_dist = distance(iter++);
        for(; --countdown != 0
              && iter != neighbor_end(fix.container, target); ++iter)
          {
            distance_type tmp = distance(iter);
            BOOST_CHECK_GE(tmp, min_dist);
            min_dist = tmp;
          }
        BOOST_CHECK_EQUAL(countdown, 0u);
        BOOST_CHECK(iter == neighbor_end(fix.container, target));
        fix.container.erase(fix.container.begin());
      }
  }
  // Prove that you can iterate equivalent nodes
  {
    int2 target;
    same()(target, 0, 100);
    Tp fix(100, same());
    neighbor_iterator<typename Tp::container_type>
      iter = neighbor_begin(fix.container, target),
      end = neighbor_end(fix.container, target);
    size_t count = 0;
    for(; iter != end && count < fix.container.size(); ++iter, ++count)
      {
        BOOST_CHECK_CLOSE(distance(iter), 0.0, 0.000000001);
      }
    BOOST_CHECK(iter == end);
    BOOST_CHECK_EQUAL(count, fix.container.size());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE
( test_neighbor_lower_bound, Tp, quad_sets )
{
  typedef quadrance<typename Tp::container_type, int, quad_diff> metric_type;
  typedef typename metric_type::distance_type distance_type;
  typedef neighbor_iterator<typename Tp::container_type, metric_type>
    neighbor_iterator_type;
  // Prove that you can find lower bound with N nodes, down to 1 node
  {
    Tp fix(100, randomize(-20, 20));
    metric_type metric;
    quad target;
    while (!fix.container.empty())
      {
        randomize(-22, 22)(target, 0, 0);
        // Find min and max dist first
        distance_type min_dist;
        distance_type max_dist;
        typename Tp::container_type::iterator it = fix.container.begin();
        min_dist = max_dist
          = metric.distance_to_key(fix.container.rank()(), *it, target);
        ++it;
        for (; it != fix.container.end(); ++it)
          {
            distance_type tmp
              = metric.distance_to_key(fix.container.rank()(), *it, target);
            if (tmp < min_dist) min_dist = tmp;
            if (tmp > max_dist) max_dist = tmp;
          }
        distance_type avg_dist = (min_dist + max_dist) / 2;
        // use this knowledge to test the lower bound
        neighbor_iterator_type i
          = neighbor_lower_bound(fix.container, metric, target, min_dist);
        BOOST_CHECK(i == neighbor_begin(fix.container, metric, target));
        BOOST_CHECK_EQUAL(min_dist, distance(i));
        i = neighbor_lower_bound(fix.container, metric, target, max_dist);
        BOOST_CHECK(i != neighbor_end(fix.container, metric, target));
        BOOST_CHECK_EQUAL(max_dist, distance(i));
        if (min_dist != max_dist)
          { BOOST_CHECK_LT(distance(--i), max_dist); }
        i = neighbor_lower_bound(fix.container, metric, target, max_dist + 1);
        BOOST_CHECK(i == neighbor_end(fix.container, metric, target));
        i = neighbor_lower_bound(fix.container, metric, target, avg_dist);
        BOOST_CHECK(i != neighbor_end(fix.container, metric, target));
        BOOST_CHECK_GE(distance(i), avg_dist);
        neighbor_iterator_type tmp = i;
        if (min_dist != avg_dist)
          { BOOST_CHECK_LT(distance(--tmp), avg_dist); }
        fix.container.erase(i);
      }
  }
  // Prove that you can find the lower bound when node and target are same
  {
    Tp fix(100, same());
    metric_type metric;
    quad target;
    same()(target, 0, 100);
    // All points and targets are the same.
    while (!fix.container.empty())
      {
        neighbor_iterator_type i
          = neighbor_lower_bound(fix.container, metric, target, 1);
        BOOST_CHECK(i == neighbor_end(fix.container, metric, target));
        i = neighbor_lower_bound(fix.container, metric, target, 0);
        BOOST_CHECK(i != neighbor_end(fix.container, metric, target));
        BOOST_CHECK(i == neighbor_begin(fix.container, metric, target));
        BOOST_CHECK_EQUAL(0, distance(i));
        fix.container.erase(i);
      }
  }
  // Prove that you can find the lower bound in an unbalanced tree
  {
    Tp fix(100, increase());
    metric_type metric;
    quad target;
    while (!fix.container.empty())
      {
        randomize(0, 100)(target, 0, 0);
        // Find min and max dist first
        distance_type min_dist;
        distance_type max_dist;
        typename Tp::container_type::iterator it = fix.container.begin();
        min_dist = max_dist
          = metric.distance_to_key(fix.container.rank()(), *it, target);
        ++it;
        for (; it != fix.container.end(); ++it)
          {
            distance_type tmp
              = metric.distance_to_key(fix.container.rank()(), *it, target);
            if (tmp < min_dist) min_dist = tmp;
            if (tmp > max_dist) max_dist = tmp;
          }
        distance_type avg_dist = (min_dist + max_dist) / 2;
        // Use this knowledge to test the lower bound
        neighbor_iterator_type i
          = neighbor_lower_bound(fix.container, metric, target, min_dist);
        BOOST_CHECK(i == neighbor_begin(fix.container, metric, target));
        BOOST_CHECK_EQUAL(min_dist, distance(i));
        i = neighbor_lower_bound(fix.container, metric, target, max_dist);
        BOOST_CHECK(i != neighbor_end(fix.container, metric, target));
        BOOST_CHECK_EQUAL(max_dist, distance(i));
        if (min_dist != max_dist)
          { BOOST_CHECK_LT(distance(--i), max_dist); }
        i = neighbor_lower_bound(fix.container, metric, target, max_dist + 1);
        BOOST_CHECK(i == neighbor_end(fix.container, metric, target));
        i = neighbor_lower_bound(fix.container, metric, target, avg_dist);
        BOOST_CHECK(i != neighbor_end(fix.container, metric, target));
        BOOST_CHECK_GE(distance(i), avg_dist);
        neighbor_iterator_type tmp = i;
        if (min_dist != avg_dist)
          { BOOST_CHECK_LT(distance(--tmp),  avg_dist); }
        fix.container.erase(i);
      }
  }
  // Prove that you can find the lower bound in an unbalanced tree
  {
    Tp fix(100, decrease());
    metric_type metric;
    quad target;
    while (!fix.container.empty())
      {
        randomize(0, 100)(target, 0, 0);
        // Find min and max dist first
        distance_type min_dist;
        distance_type max_dist;
        typename Tp::container_type::iterator it = fix.container.begin();
        min_dist = max_dist
          = metric.distance_to_key(fix.container.rank()(), *it, target);
        ++it;
        for (; it != fix.container.end(); ++it)
          {
            distance_type tmp
              = metric.distance_to_key(fix.container.rank()(), *it, target);
            if (tmp < min_dist) min_dist = tmp;
            if (tmp > max_dist) max_dist = tmp;
          }
        distance_type avg_dist = (min_dist + max_dist) / 2;
        // Use this knowledge to test the lower bound
        neighbor_iterator_type i
          = neighbor_lower_bound(fix.container, metric, target, min_dist);
        BOOST_CHECK(i == neighbor_begin(fix.container, metric, target));
        BOOST_CHECK_EQUAL(min_dist, distance(i));
        i = neighbor_lower_bound(fix.container, metric, target, max_dist);
        BOOST_CHECK(i != neighbor_end(fix.container, metric, target));
        BOOST_CHECK_EQUAL(max_dist, distance(i));
        if (min_dist != max_dist)
          { BOOST_CHECK_LT(distance(--i), max_dist); }
        i = neighbor_lower_bound(fix.container, metric, target, max_dist + 1);
        BOOST_CHECK(i == neighbor_end(fix.container, metric, target));
        i = neighbor_lower_bound(fix.container, metric, target, avg_dist);
        BOOST_CHECK(i != neighbor_end(fix.container, metric, target));
        BOOST_CHECK_GE(distance(i), avg_dist);
        neighbor_iterator_type tmp = i;
        if (min_dist != avg_dist)
          { BOOST_CHECK_LT(distance(--tmp),  avg_dist); }
        fix.container.erase(i);
      }
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_euclidian_neighbor, Tp, double6_maps)
{
  Tp fix(3, randomize(-2, 2));
  double6 target; same()(target, 0, 2);
  {
    // With default diff with float
    euclidian_neighbor_iterator<typename Tp::container_type, double>
      i,
      j = euclidian_neighbor_begin(fix.container, target),
      k = euclidian_neighbor_end(fix.container, target),
      l = euclidian_neighbor_lower_bound(fix.container, target, 0.0),
      m = euclidian_neighbor_upper_bound(fix.container, target, 0.0);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i), distance(j));
    BOOST_CHECK(j != k);
    BOOST_CHECK(i == l);
    BOOST_CHECK(m == l);
  }
  {
    // With custom diff and const
    euclidian_neighbor_iterator<const typename Tp::container_type,
                                double, double6_diff>
      i,
      j = euclidian_neighbor_cbegin(fix.container, double6_diff(), target),
      k = euclidian_neighbor_cend(fix.container, double6_diff(), target),
      l = euclidian_neighbor_clower_bound(fix.container, double6_diff(),
                                          target, 0.0),
      m = euclidian_neighbor_cupper_bound(fix.container, double6_diff(),
                                          target, 0.0);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i), distance(j));
    BOOST_CHECK(j != k);
    BOOST_CHECK(i == l);
    BOOST_CHECK(m == l);
  }
  {
    // With default diff with float and a pair
    euclidian_neighbor_iterator_pair<typename Tp::container_type, double>
      i,
      j = euclidian_neighbor_range(fix.container, target);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i.first), distance(j.first));
  }
  {
    // With custom diff and const
    euclidian_neighbor_iterator_pair<const typename Tp::container_type,
                                     double, double6_diff>
      i,
      j = euclidian_neighbor_crange(fix.container, double6_diff(), target);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i.first), distance(j.first));
  }
  // Need to test the pair
}

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_quadrance_neighbor, Tp, double6_maps)
{
  Tp fix(3, randomize(-2, 2));
  double6 target; same()(target, 0, 2);
  {
    // With default diff with float
    quadrance_neighbor_iterator<typename Tp::container_type, double>
      i,
      j = quadrance_neighbor_begin(fix.container, target),
      k = quadrance_neighbor_end(fix.container, target),
      l = quadrance_neighbor_lower_bound(fix.container, target, 0.0),
      m = quadrance_neighbor_upper_bound(fix.container, target, 0.0);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i), distance(j));
    BOOST_CHECK(j != k);
    BOOST_CHECK(i == l);
    BOOST_CHECK(m == l);
  }
  {
    // With custom diff and const
    quadrance_neighbor_iterator<const typename Tp::container_type,
                                double, double6_diff>
      i,
      j = quadrance_neighbor_cbegin(fix.container, double6_diff(), target),
      k = quadrance_neighbor_cend(fix.container, double6_diff(), target),
      l = quadrance_neighbor_clower_bound(fix.container, double6_diff(),
                                          target, 0.0),
      m = quadrance_neighbor_cupper_bound(fix.container, double6_diff(),
                                          target, 0.0);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i), distance(j));
    BOOST_CHECK(j != k);
    BOOST_CHECK(i == l);
    BOOST_CHECK(m == l);
  }
  {
    // With default diff with float and a pair
    quadrance_neighbor_iterator_pair<typename Tp::container_type, double>
      i,
      j = quadrance_neighbor_range(fix.container, target);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i.first), distance(j.first));
  }
  {
    // With custom diff and const
    quadrance_neighbor_iterator_pair<const typename Tp::container_type,
                                     double, double6_diff>
      i,
      j = quadrance_neighbor_crange(fix.container, double6_diff(), target);
    i = j;
    BOOST_CHECK(i == j);
    BOOST_CHECK_EQUAL(distance(i.first), distance(j.first));
  }
  // Need to test the pair
}


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2020 Sebastien Boisvert <seb@boisvert.info>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "BenchTimer.h"
#include "../test/MovableScalar.h"

#include <Eigen/Core>

#include <iostream>
#include <utility>

template <typename MatrixType>
void copy_matrix(MatrixType& m)
{
  MatrixType tmp(m);
  m = tmp;
}

template <typename MatrixType>
void move_matrix(MatrixType&& m)
{
  MatrixType tmp(std::move(m));
  m = std::move(tmp);
}

template<typename Scalar>
void bench(const std::string& label)
{
  using MatrixType = Eigen::Matrix<Eigen::MovableScalar<Scalar>,1,10>;
  Eigen::BenchTimer t;

  int tries = 10;
  int rep = 1000000;

  MatrixType data = MatrixType::Random().eval();
  MatrixType dest;

  BENCH(t, tries, rep, copy_matrix(data));
  std::cout << label << " copy semantics: " << 1e3*t.best(Eigen::CPU_TIMER) << " ms" << std::endl;

  BENCH(t, tries, rep, move_matrix(std::move(data)));
  std::cout << label << " move semantics: " << 1e3*t.best(Eigen::CPU_TIMER) << " ms" << std::endl;
}

int main()
{
  bench<float>("float");
  bench<double>("double");
  return 0;
}


//
// Created by lei on 4/21/19.
//

#ifndef TGO1D_CXX_TOPOGRAPHY_HPP
#define TGO1D_CXX_TOPOGRAPHY_HPP

#include <vector>
#include <armadillo>

class Topography
{
public:
    Topography(const arma::vec &x, const arma::vec &fev,
            int nk);
    std::vector<double> minimize_pool() const;
private:
    arma::vec x_;
    arma::imat topo_;
};
#endif //TGO1D_CXX_TOPOGRAPHY_HPP


#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION

#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <cstdlib>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include "board.h"
#include <boost/thread.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/future.hpp>

#ifdef DEBUG
#define DEBUG_MSG(str) do { std::cout << str << std::endl; } while( false )
#define DEBUG_BOARD(board) do { board->print(); } while ( false )
#else
#define DEBUG_MSG(str) do { } while ( false )
#define DEBUG_BOARD(board) do { } while ( false )
#endif

#define HORIZONTAL_WIN 1
#define VERTICAL_WIN 2
#define PLAYER_1 3
#define PLAYER_2 4

struct Input {
    Board *board;
    bool verticalMove;
};

void solveThread(void* in, boost::promise<int> &p);

int solve(Board *board, bool verticalMove);

int solveThreadPool(Board *board, bool vertical);

int solve(Board *board);


/**
 * @file llsdutil_tut.cpp
 * @author Adroit
 * @date 2007-02
 * @brief LLSD conversion routines test cases.
 *
 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"
#include "lltut.h"
#include "m4math.h"
#include "v2math.h"
#include "v2math.h"
#include "v3color.h"
#include "v3math.h"
#include "v3dmath.h"
#include "v4coloru.h"
#include "v4math.h"
#include "llquaternion.h"
#include "llsdutil.h"
#include "llsdutil_math.h"
#include "stringize.h"
#include <set>
#include <boost/range.hpp>

namespace tut
{
	struct llsdutil_data
	{
        void test_matches(const std::string& proto_key, const LLSD& possibles,
                          const char** begin, const char** end)
        {
            std::set<std::string> succeed(begin, end);
            LLSD prototype(possibles[proto_key]);
            for (LLSD::map_const_iterator pi(possibles.beginMap()), pend(possibles.endMap());
                 pi != pend; ++pi)
            {
                std::string match(llsd_matches(prototype, pi->second));
                std::set<std::string>::const_iterator found = succeed.find(pi->first);
                if (found != succeed.end())
                {
                    // This test is supposed to succeed. Comparing to the
                    // empty string ensures that if the test fails, it will
                    // display the string received so we can tell what failed.
                    ensure_equals("match", match, "");
                }
                else
                {
                    // This test is supposed to fail. If we get a false match,
                    // the string 'match' will be empty, which doesn't tell us
                    // much about which case went awry. So construct a more
                    // detailed description string.
                    ensure(proto_key + " shouldn't match " + pi->first, ! match.empty());
                }
            }
        }
	};
	typedef test_group<llsdutil_data> llsdutil_test;;
	typedef llsdutil_test::object llsdutil_object;
	tut::llsdutil_test tutil("llsdutil");

	template<> template<>
	void llsdutil_object::test<1>()
	{
		LLSD sd;
		U64 valueIn , valueOut;
		valueIn = U64L(0xFEDCBA9876543210);
		sd = ll_sd_from_U64(valueIn);
		valueOut = ll_U64_from_sd(sd);
		ensure_equals("U64 valueIn->sd->valueOut", valueIn, valueOut);
	}

	template<> template<>
	void llsdutil_object::test<2>()
	{
		LLSD sd;
		U32 valueIn, valueOut;
		valueIn = 0x87654321;
		sd = ll_sd_from_U32(valueIn);
		valueOut = ll_U32_from_sd(sd);
		ensure_equals("U32 valueIn->sd->valueOut", valueIn, valueOut);
	}

	template<> template<>
	void llsdutil_object::test<3>()
	{
		U32 valueIn, valueOut;
		valueIn = 0x87654321;
		LLSD sd;
		sd = ll_sd_from_ipaddr(valueIn);
		valueOut = ll_ipaddr_from_sd(sd);
		ensure_equals("valueIn->sd->valueOut", valueIn, valueOut);
	}

	template<> template<>
	void llsdutil_object::test<4>()
	{
		LLSD sd;
		LLVector3 vec1(-1.0, 2.0, -3.0);
		sd = ll_sd_from_vector3(vec1);
		LLVector3 vec2 = ll_vector3_from_sd(sd);
		ensure_equals("vector3 -> sd -> vector3: 1", vec1, vec2);

		LLVector3 vec3(sd);
		ensure_equals("vector3 -> sd -> vector3: 2", vec1, vec3);

		sd.clear();
		vec1.setVec(0., 0., 0.);
		sd = ll_sd_from_vector3(vec1);
		vec2 = ll_vector3_from_sd(sd);
		ensure_equals("vector3 -> sd -> vector3: 3", vec1, vec2);
		sd.clear();
	}

	template<> template<>
	void llsdutil_object::test<5>()
	{
		LLSD sd;
		LLVector3d vec1((F64)(U64L(0xFEDCBA9876543210) << 2), -1., 0);
		sd = ll_sd_from_vector3d(vec1);
		LLVector3d vec2 = ll_vector3d_from_sd(sd);
		ensure_equals("vector3d -> sd -> vector3d: 1", vec1, vec2);

		LLVector3d vec3(sd);
		ensure_equals("vector3d -> sd -> vector3d : 2", vec1, vec3);
	}

	template<> template<>
	void llsdutil_object::test<6>()
	{
		LLSD sd;
		LLVector2 vec((F32) -3., (F32) 4.2);
		sd = ll_sd_from_vector2(vec);
		LLVector2 vec1 = ll_vector2_from_sd(sd);
		ensure_equals("vector2 -> sd -> vector2", vec, vec1);

		LLSD sd2 = ll_sd_from_vector2(vec1);
		ensure_equals("sd -> vector2 -> sd: 2", sd, sd2);
	}

	template<> template<>
	void llsdutil_object::test<7>()
	{
		LLSD sd;
		LLQuaternion quat((F32) 1., (F32) -0.98, (F32) 2.3, (F32) 0xffff);
		sd = ll_sd_from_quaternion(quat);
		LLQuaternion quat1 = ll_quaternion_from_sd(sd);
		ensure_equals("LLQuaternion -> sd -> LLQuaternion", quat, quat1);

		LLSD sd2 = ll_sd_from_quaternion(quat1);
		ensure_equals("sd -> LLQuaternion -> sd ", sd, sd2);
	}

	template<> template<>
	void llsdutil_object::test<8>()
	{
		LLSD sd;
		LLColor4 c(1.0f, 2.2f, 4.0f, 7.f);
		sd = ll_sd_from_color4(c);
		LLColor4 c1 =ll_color4_from_sd(sd);
		ensure_equals("LLColor4 -> sd -> LLColor4", c, c1);

		LLSD sd1 = ll_sd_from_color4(c1);
		ensure_equals("sd -> LLColor4 -> sd", sd, sd1);
	}

    template<> template<>
    void llsdutil_object::test<9>()
    {
        set_test_name("llsd_matches");

        // for this test, construct a map of all possible LLSD types
        LLSD map;
        map.insert("empty",     LLSD());
        map.insert("Boolean",   LLSD::Boolean());
        map.insert("Integer",   LLSD::Integer(0));
        map.insert("Real",      LLSD::Real(0.0));
        map.insert("String",    LLSD::String("bah"));
        map.insert("NumString", LLSD::String("1"));
        map.insert("UUID",      LLSD::UUID());
        map.insert("Date",      LLSD::Date());
        map.insert("URI",       LLSD::URI());
        map.insert("Binary",    LLSD::Binary());
        map.insert("Map",       LLSD().with("foo", LLSD()));
        // Only an empty array can be constructed on the fly
        LLSD array;
        array.append(LLSD());
        map.insert("Array",     array);

        // These iterators are declared outside our various for loops to avoid
        // fatal MSVC warning: "I used to be broken, but I'm all better now!"
        LLSD::map_const_iterator mi, mend(map.endMap());

        /*-------------------------- llsd_matches --------------------------*/

        // empty prototype matches anything
        for (mi = map.beginMap(); mi != mend; ++mi)
        {
            ensure_equals(std::string("empty matches ") + mi->first, llsd_matches(LLSD(), mi->second), "");
        }

        LLSD proto_array, data_array;
        for (int i = 0; i < 3; ++i)
        {
            proto_array.append(LLSD());
            data_array.append(LLSD());
        }

        // prototype array matches only array
        for (mi = map.beginMap(); mi != mend; ++mi)
        {
            ensure(std::string("array doesn't match ") + mi->first,
                   ! llsd_matches(proto_array, mi->second).empty());
        }

        // data array must be at least as long as prototype array
        proto_array.append(LLSD());
        ensure_equals("data array too short", llsd_matches(proto_array, data_array),
                      "Array size 4 required instead of Array size 3");
        data_array.append(LLSD());
        ensure_equals("data array just right", llsd_matches(proto_array, data_array), "");
        data_array.append(LLSD());
        ensure_equals("data array longer", llsd_matches(proto_array, data_array), "");

        // array element matching
        data_array[0] = LLSD::String();
        ensure_equals("undefined prototype array entry", llsd_matches(proto_array, data_array), "");
        proto_array[0] = LLSD::Binary();
        ensure_equals("scalar prototype array entry", llsd_matches(proto_array, data_array),
                      "[0]: Binary required instead of String");
        data_array[0] = LLSD::Binary();
        ensure_equals("matching prototype array entry", llsd_matches(proto_array, data_array), "");

        // build a coupla maps
        LLSD proto_map, data_map;
        data_map["got"] = LLSD();
        data_map["found"] = LLSD();
        for (LLSD::map_const_iterator dmi(data_map.beginMap()), dmend(data_map.endMap());
             dmi != dmend; ++dmi)
        {
            proto_map[dmi->first] = dmi->second;
        }
        proto_map["foo"] = LLSD();
        proto_map["bar"] = LLSD();

        // prototype map matches only map
        for (mi = map.beginMap(); mi != mend; ++mi)
        {
            ensure(std::string("map doesn't match ") + mi->first,
                   ! llsd_matches(proto_map, mi->second).empty());
        }

        // data map must contain all keys in prototype map
        std::string error(llsd_matches(proto_map, data_map));
        ensure_contains("missing keys", error, "missing keys");
        ensure_contains("missing foo", error, "foo");
        ensure_contains("missing bar", error, "bar");
        ensure_does_not_contain("found found", error, "found");
        ensure_does_not_contain("got got", error, "got");
        data_map["bar"] = LLSD();
        error = llsd_matches(proto_map, data_map);
        ensure_contains("missing foo", error, "foo");
        ensure_does_not_contain("got bar", error, "bar");
        data_map["foo"] = LLSD();
        ensure_equals("data map just right", llsd_matches(proto_map, data_map), "");
        data_map["extra"] = LLSD();
        ensure_equals("data map with extra", llsd_matches(proto_map, data_map), "");

        // map element matching
        data_map["foo"] = LLSD::String();
        ensure_equals("undefined prototype map entry", llsd_matches(proto_map, data_map), "");
        proto_map["foo"] = LLSD::Binary();
        ensure_equals("scalar prototype map entry", llsd_matches(proto_map, data_map),
                      "['foo']: Binary required instead of String");
        data_map["foo"] = LLSD::Binary();
        ensure_equals("matching prototype map entry", llsd_matches(proto_map, data_map), "");

        // String
        {
            static const char* matches[] = { "String", "NumString", "Boolean", "Integer",
                                             "Real", "UUID", "Date", "URI" };
            test_matches("String", map, boost::begin(matches), boost::end(matches));
        }

        // Boolean, Integer, Real
        static const char* numerics[] = { "Boolean", "Integer", "Real" };
        for (const char **ni = boost::begin(numerics), **nend = boost::end(numerics);
             ni != nend; ++ni)
        {
            static const char* matches[] = { "Boolean", "Integer", "Real", "String", "NumString" };
            test_matches(*ni, map, boost::begin(matches), boost::end(matches));
        }

        // UUID
        {
            static const char* matches[] = { "UUID", "String", "NumString" };
            test_matches("UUID", map, boost::begin(matches), boost::end(matches));
        }

        // Date
        {
            static const char* matches[] = { "Date", "String", "NumString" };
            test_matches("Date", map, boost::begin(matches), boost::end(matches));
        }

        // URI
        {
            static const char* matches[] = { "URI", "String", "NumString" };
            test_matches("URI", map, boost::begin(matches), boost::end(matches));
        }

        // Binary
        {
            static const char* matches[] = { "Binary" };
            test_matches("Binary", map, boost::begin(matches), boost::end(matches));
        }

        /*-------------------------- llsd_equals ---------------------------*/

        // Cross-product of each LLSD type with every other
        for (LLSD::map_const_iterator lmi(map.beginMap()), lmend(map.endMap());
             lmi != lmend; ++lmi)
        {
            for (LLSD::map_const_iterator rmi(map.beginMap()), rmend(map.endMap());
                 rmi != rmend; ++rmi)
            {
                // Name this test based on the map keys naming the types of
                // interest, e.g "String::Integer".
                // We expect the values (xmi->second) to be equal if and only
                // if the type names (xmi->first) are equal.
                ensure(STRINGIZE(lmi->first << "::" << rmi->first),
                       bool(lmi->first == rmi->first) ==
                       bool(llsd_equals(lmi->second, rmi->second)));
            }
        }

        // Array cases
        LLSD rarray;
        rarray.append(1.0);
        rarray.append(2);
        rarray.append("3");
        LLSD larray(rarray);
        ensure("llsd_equals(equal arrays)", llsd_equals(larray, rarray));
        rarray[2] = "4";
        ensure("llsd_equals(different [2])", ! llsd_equals(larray, rarray));
        rarray = larray;
        rarray.append(LLSD::Date());
        ensure("llsd_equals(longer right array)", ! llsd_equals(larray, rarray));
        rarray = larray;
        rarray.erase(2);
        ensure("llsd_equals(shorter right array)", ! llsd_equals(larray, rarray));

        // Map cases
        LLSD rmap;
        rmap["San Francisco"] = 65;
        rmap["Phoenix"] = 92;
        rmap["Boston"] = 77;
        LLSD lmap(rmap);
        ensure("llsd_equals(equal maps)", llsd_equals(lmap, rmap));
        rmap["Boston"] = 80;
        ensure("llsd_equals(different [\"Boston\"])", ! llsd_equals(lmap, rmap));
        rmap = lmap;
        rmap["Atlanta"] = 95;
        ensure("llsd_equals(superset right map)", ! llsd_equals(lmap, rmap));
        rmap = lmap;
        lmap["Seattle"] = 72;
        ensure("llsd_equals(superset left map)", ! llsd_equals(lmap, rmap));
    }
}


// Copyright David Abrahams 2006. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include "basics.hpp"
#include <boost/mpl/list.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/size.hpp>
#include <boost/type_traits/add_pointer.hpp>

# include <boost/mpl/contains.hpp>

namespace test
{
  namespace mpl = boost::mpl;

  template <class Set>
  struct assert_in_set
  {
      template <class T>
      void operator()(T*)
      {
          BOOST_MPL_ASSERT((mpl::contains<Set,T>));
      }
  };

  template<class Expected, class Params>
  void f_impl(Params const& p BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Expected))
  {
      BOOST_MPL_ASSERT_RELATION(
          mpl::size<Expected>::value
        , ==
        , mpl::size<Params>::value
      );

      mpl::for_each<Params, boost::add_pointer<mpl::_1> >(assert_in_set<Expected>());
  }

  template<class Expected, class Tester, class Name, class Value, class Index>
  void f(Tester const& t, const Name& name_,
      const Value& value_, const Index& index_ BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Expected))
  {
      f_impl<Expected>(f_parameters()(t, name_, value_, index_));
  }

  template<class Expected, class Tester, class Name, class Value>
  void f(Tester const& t, const Name& name_, const Value& value_ BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Expected))
  {
      f_impl<Expected>(f_parameters()(t, name_, value_));
  }

  template<class Expected, class Tester, class Name>
  void f(Tester const& t, const Name& name_ BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Expected))
  {
      f_impl<Expected>(f_parameters()(t, name_));
  }

  void run()
  {
      typedef test::tag::tester tester_;
      typedef test::tag::name name_;
      typedef test::tag::value value_;
      typedef test::tag::index index_;

      f<mpl::list4<tester_,name_,value_,index_> >(1, 2, 3, 4);
      f<mpl::list3<tester_,name_,index_> >(1, 2, index = 3);
      f<mpl::list3<tester_,name_,index_> >(1, index = 2, name = 3);
      f<mpl::list2<name_,value_> >(name = 3, value = 4);
      f_impl<mpl::list1<value_> >(value = 4);
  }
}

int main()
{
    test::run();
    return 0;
}


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<boost::int64_t, 2, 6> test_long_2_6(const Eigen::Matrix<boost::int64_t, 2, 6>& M) { return M; }
void export_long_2_6() { boost::python::def("test_long_2_6", test_long_2_6); }


/*
   Copyright (c) Marshall Clow 2011-2012.

   Distributed under the Boost Software License, Version 1.0. (See accompanying
   file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

    For more information, see http://www.boost.org
*/

#include <iostream>

#include <boost/config.hpp>
#include <boost/algorithm/cxx11/is_permutation.hpp>

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include <string>
#include <vector>
#include <list>

namespace ba = boost::algorithm;
// namespace ba = boost;

void test_sequence1 () {
    std::vector<int> v, v1;

    v.clear ();
    for ( std::size_t i = 5; i < 15; ++i )
        v.push_back ( i );
    v1 = v;
    BOOST_CHECK ( ba::is_permutation ( v.begin (), v.end (), v.begin ()));  // better be a permutation of itself!
    BOOST_CHECK ( ba::is_permutation ( v.begin (), v.end (), v1.begin ()));

//  With bidirectional iterators.
    std::list<int> l;
    std::copy ( v.begin (), v.end (), std::back_inserter ( l ));
    BOOST_CHECK ( ba::is_permutation ( l.begin (), l.end (), l.begin ()));  // better be a permutation of itself!
    BOOST_CHECK ( ba::is_permutation ( l.begin (), l.end (), v1.begin ()));
    for ( std::size_t i = 0; i < l.size (); ++i ) {
        l.push_back ( *l.begin ()); l.pop_front (); // rotation
        BOOST_CHECK ( ba::is_permutation ( l.begin (), l.end (), v1.begin ()));
        }
    }

BOOST_AUTO_TEST_CASE( test_main )
{
  test_sequence1 ();
}