#include <gtest/gtest.h>
#include <cctype>
#include <algorithm>
#include <functional>
#include <iterator>
#include <boost/utility/string_ref.hpp>
#include <ka/parse.hpp>
#include <ka/testutils.hpp>
#include <ka/conceptpredicate.hpp>
#include <ka/src.hpp>

using namespace ka;
using namespace ka::parse;
using namespace ka::test;

// A result of parsing is Regular.
TEST(ParseResult, Regular) {
  A const c[4];
  auto const t = type_t<A>{};
  // `ok` and `err` returns parsing results. Iterators are arbitrary values here.
  EXPECT_TRUE(is_regular({ ok(A{9}, c+0), ok(A{0}, c+1), err(t, c+2), err(t, c+3) }));
}

// A result of parsing contains optionally a value and an iterator to what
// remains to parse.
TEST(ParseResult, Basic) {
  auto const a = A{4};
  auto const i = &a + 1;
  {
    auto const res = ok(a, i); // Ok: contains a value.
    EXPECT_EQ(a, src(res));
    EXPECT_EQ(i, iter(res));
  }
  {
    auto const res = err(type_t<A>{}, i); // Ko: does not contain a value.
    EXPECT_TRUE(ka::empty(res)); // Therefore, calling `src` is undefined behavior.
    EXPECT_EQ(i, iter(res)); // Always ok to get the iterator.
  }
}

// A result of parsing is convertible to an optional.
TEST(ParseResult, ToOpt) {
  using std::get;
  auto const t = type_t<A>{};
  auto test_data = array_fn(
    //           input             expected output
    //           ----------------  ---------------
    ka::product( ok(A{4}, &t),     ka::opt(A{4})  ), // Iterators are arbitrary.
    ka::product( err(t, &t + 3),   ka::opt_t<A>() ),
    ka::product( ok(A{9}, &t + 1), ka::opt(A{9})  )
  );
  for (auto const& x: test_data) {
    EXPECT_EQ(get<1>(x), to_opt(get<0>(x)));
  }
}

struct equ_fn_res_t {
  // Function<res_t<B, I> (res_t<A, I>)> F, G
  template<typename F, typename G>
  auto operator()(F f, G g) const -> bool {
    auto const a = A{45};
    auto const t = type_t<A>{};
    for (auto const& x: array_fn(ok(A{4}, &a), ok(A{5}, &a+1), ok(A{81}, &a+2),
        err(t, &a), err(t, &a+1), err(t, &a+2))) {
      if (f(x) != g(x)) return false;
    }
    return true;
  }
};

// `parse::res_t` is a functor on its value type.
TEST(ParseResult, Functor) {
  // Composable functions.
  auto const f = [](A a) -> B { return B{2 * a.value}; };
  auto const g = [](B b) -> C { return C{b.value + 1}; };

  EXPECT_TRUE(is_functor(ka::fmap, array_fn(ka::product(g, f)), equ_fn_res_t{}));
}

// A parser is a function from a range of elements to a parsing result.
// Range of elements is bound by a pair of iterators.
// A parser can succeed or fail. If it fails, input is not consumed (i.e. begin
// iterator is returned).
TEST(ParseElement, Basic) {
  using namespace ka::parse;
  auto p = symbol_t{}; // Parser of one symbol.
  A const v[] = {A{5}, A{-46}};

  // Succeeds.
  EXPECT_EQ(ok(v[0], v + 1), p(v + 0, v + 2));

  // Fails.
  auto const t = type_t<A>{};
  EXPECT_EQ(err(t, v + 1), p(v + 1, v + 1)); // empty range
}

// Linearizable<Linearizable> L
// Relation<ParseResult> R
template<typename L, typename R = equal_t>
struct equ_parser_t {
  L ranges;
  R equiv; // equivalence(equiv)

  template<typename PA, typename PB>
  auto operator()(PA pa, PB pb) const -> bool {
    for (auto const& r: ranges) {
      auto b = r.begin();
      auto e = r.end();
      if (! equiv(pa(b, e), pb(b, e))) return false;
    }
    return true;
  }
};

template<typename L, typename R = equal_t>
auto equ_parser(L ranges, R equiv = {}) -> equ_parser_t<L, R> {
  return {mv(ranges), mv(equiv)};
}

struct equ_fn_symbol_t {
  // Function<Parser<A> (Parser)> F, G
  template<typename F, typename G>
  auto operator()(F f, G g) const -> bool {
    using V = std::vector<A>;
    auto equ = equ_parser(array_fn(V{}, V{A{4}}, V{A{0}, A{9}, A{-8}, A{836}}));
    return equ(f(symbol), g(symbol));
  }
};

// A parametrized parser is a functor on its value type.
TEST(ParseFunctor, Basic) {
  // Composable functions.
  auto const f = [](A a) -> B {return B{2 * a.value};};
  auto const g = [](B b) -> C {return C{b.value + 1};};

  // Functor test algorithm:
  // 1) Lift functions f,g to functions of parsers.
  //    (f: A -> B)   -> (f': symbol_t -> symbol_t)  // Done via
  //    (g: B -> C)   -> (g': symbol_t -> symbol_t)  // ka::fmap.
  //    (g∘f: A -> C) -> ((g∘f)': symbol_t -> symbol_t)
  //
  // 2) Compare lifted functions through the given equivalence:
  //    g' ∘ f' ~ (g'∘f')
  EXPECT_TRUE(is_functor(ka::fmap, array_fn(ka::product(g, f)), equ_fn_symbol_t{}));
}

namespace test_parse {
  // Characters associated to test types, for parsing purpose.
  auto char_(type_t<A>) -> char {return 'A';}
  auto char_(type_t<B>) -> char {return 'B';}
  auto char_(type_t<C>) -> char {return 'C';}

  // Simple parser for test purpose.
  // Expected format: char digit
  //  (where `char` depends on type `T`)
  // Example of valid input for type `A`: "A4"
  template<typename T>
  struct sym_t {
  // Regular:
    KA_GENERATE_FRIEND_REGULAR_OPS_0(sym_t)
  // Parser:
    template<typename I> constexpr
    auto operator()(I b, I e) const -> parse::res_t<T, I> {
      return (b != e && *b == char_(type_t<T>{}) && ++b != e && std::isdigit(*b))
        ? ok(T{*b - '0'}, std::next(b))
        : err(type_t<T>{}, b);
    }
  // Functor:
    template<typename F>
    auto fmap(F f) const -> fmapped_t<F, sym_t> {
      return {mv(f), *this};
    }
  };

  auto input_sample() -> std::array<boost::string_ref, 14> const& {
    static auto const a = std::array<boost::string_ref, 14>{
      "", "A3", "A3A6", "A3A6A7", "B3", "B3A6", "3A", "$jH9w_?", "A3B5", "A3C7",
      "B8A7A", "C0A1", "B9C7A8A8", "C0B0"
    };
    return a;
  }
} // namespace test_parse

// Product of parsers succeeds if the sequence of all of them succeeds.
TEST(ParseProduct, Basic) {
  auto const s = std::string("A5B2C5");
  auto const b = s.begin();
  auto const e = s.end();
  auto const pa = test_parse::sym_t<A>{};
  auto const pb = test_parse::sym_t<B>{};

  // Ok.
  EXPECT_EQ(ok(ka::product(A{5}, B{2}), b + 4), parse::product(pa, pb)(b, e));

  // Ko: first parser failed (could not parse a `B`).
  auto const t0 = type_t<ka::product_t<B, A>>{};
  EXPECT_EQ(err(t0, b), parse::product(pb, pa)(b, e));

  // Ko: second parser failed (could not parse an `A`).
  auto const t1 = type_t<ka::product_t<A, A>>{};
  EXPECT_EQ(err(t1, b), parse::product(pa, pa)(b, e));
}

// Product operator is associative.
// Product operator flattens products, instead of nesting them.
// E.g. `pa * pb * pc` has type `parse::product<PA, PB, PC>` instead of
// `parse::product<parse::product<PA, PB>, PC>`.
TEST(ParseProductMonoid, Operator) {
  using parse::ops::operator*;
  auto pa = test_parse::sym_t<A>{};
  auto pb = test_parse::sym_t<B>{};
  auto pc = test_parse::sym_t<C>{};

  EXPECT_EQ((pa * pb) * pc, pa * (pb * pc));

  EXPECT_EQ(parse::product(pa, pb), pa * pb);
  EXPECT_EQ(parse::product(pa, pb, pc), pa * pb * pc);
  EXPECT_EQ(parse::product(pa, pb, pc, pb, pa), pa * pb * pc * pb * pa);
}

// Unit-parser always succeeds without consuming input and returns ka::unit.
TEST(ParseUnit, Basic) {
  {
    auto const v = std::array<A, 3>{A{5}, A{1}, A{9}};
    EXPECT_EQ(ok(ka::unit, v.begin()), parse::unit(v.begin(), v.end()));
  } {
    auto const v = std::array<B, 2>{B{7}, B{1}};
    EXPECT_EQ(ok(ka::unit, v.begin()), parse::unit(v.begin(), v.end()));
  }
}

namespace test_parse {
  // Makes res_t<T, I> comparable with res_t<product_t<T>, I> to implement logic
  // "up to isomorphism" (T is isomorphic to product_t<T>).
  template<typename T, typename I>
  auto extract(parse::res_t<T, I> const& a) -> parse::res_t<T, I> const& {
    return a;
  }

  template<typename T, typename I>
  auto extract(parse::res_t<ka::product_t<T>, I> const& a) -> parse::res_t<T, I> {
    return a.fmap([](ka::product_t<T> const& p) -> T {
      return std::get<0>(p);
    });
  }

  // A ≅ (A)
  // (A) ≅ A
  struct equiv_res_product_t {
    template<typename T, typename I, typename U>
    auto operator()(parse::res_t<T, I> const& a, parse::res_t<U, I> const& b) const
      -> bool {
      return extract(a) == extract(b);
    }
  } equiv_res_product;
} // namespace test_parse

// Unit-parser is the unit of the parser product (up to isomorphism).
TEST(ParseProductMonoid, Unit) {
  using parse::ops::operator*;
  using namespace test_parse;
  auto p = sym_t<A>{};
  auto equ = equ_parser(input_sample(), test_parse::equiv_res_product);
  auto _1 = parse::unit;
  EXPECT_EQ(_1, parse::product());
  EXPECT_TRUE(equ(_1 * _1, _1));
  EXPECT_TRUE(equ(p * _1, p));
  EXPECT_TRUE(equ(_1 * p, p));
}

// Sum of parsers succeeds if any succeeds. Parsers are tried in left-to-right
// order.
TEST(ParseSum, Basic) {
  auto const s = std::string("A5B2C5");
  auto const b = s.begin();
  auto const e = s.end();
  auto const pa = test_parse::sym_t<A>{};
  auto const pb = test_parse::sym_t<B>{};
  auto const pc = test_parse::sym_t<C>{};

  { // Ok: First alternative.
    using sum = SumValue<A, B>;
    EXPECT_EQ(ok(sum{indexed<0>(A{5})}, b + 2), parse::sum(pa, pb)(b, e));
  }
  { // Ok: second alternative.
    using sum = SumValue<B, A>;
    EXPECT_EQ(ok(sum{indexed<1>(A{5})}, b + 2), parse::sum(pb, pa)(b, e));
  }
  { // Ko.
    using sum = SumValue<B, C>;
    auto t = type_t<sum>{};
    EXPECT_EQ(err(t, b), parse::sum(pb, pc)(b, e));
  }
}

// Sum operator is associative.
// Sum operator flattens sums, instead of nesting them.
// E.g. `pa + pb + pc` has type `parse::sum<PA, PB, PC>` instead of
// `parse::sum<parse::sum<PA, PB>, PC>`.
TEST(ParseSumMonoid, Operator) {
  using parse::ops::operator+;
  auto pa = test_parse::sym_t<A>{};
  auto pb = test_parse::sym_t<B>{};
  auto pc = test_parse::sym_t<C>{};

  EXPECT_EQ((pa + pb) + pc, pa + (pb + pc));
  EXPECT_EQ(parse::sum(pa, pb), pa + pb);
  EXPECT_EQ(parse::sum(pa, pb, pc), pa + pb + pc);
  EXPECT_EQ(parse::sum(pa, pb, pc, pb, pa), pa + pb + pc + pb + pa);
}

// Zero-parser always failed without consuming input. Its value type is
// uninstantiable (sum of zero type).
TEST(ParseZero, Basic) {
  auto const t = type_t<ka::zero_t>{};
  {
    auto const v = std::array<A, 3>{A{5}, A{1}, A{9}};
    EXPECT_EQ(err(t, v.begin()), parse::zero(v.begin(), v.end()));
  } {
    auto const v = std::array<B, 2>{B{7}, B{1}};
    EXPECT_EQ(err(t, v.begin()), parse::zero(v.begin(), v.end()));
  }
}

namespace test_parse {
  // The following functions implement parsing result equality up-to-isomorphism.
  // Their precondition is that results are not empty.
  struct equiv_res_zero_t {
    // 0 + 0 = 0
    // Should not be defined, but linker complains...
    auto equ_val(SumValue<ka::zero_t, ka::zero_t>, ka::zero_t) const -> bool {
      return true;
    }
    // A + 0 ≅ A
    template<typename A>
    auto equ_val(SumValue<A, ka::zero_t> x, A y) const -> bool {
      return *boost::get<indexed_t<0, A>>(x) == y;
    }
    // 0 + A ≅ A
    template<typename A, int = 0> // TODO: remove the int when MSVC > 2015
    auto equ_val(SumValue<ka::zero_t, A> x, A y) const -> bool {
      return *boost::get<indexed_t<1, A>>(x) == y;
    }
    // 0 ≅ 0 * A
    // Should not be defined, but linker complains...
    template<typename A>
    auto equ_val(ka::zero_t, ka::product_t<ka::zero_t, A>) const -> bool {
      return true;
    }
    // 0 ≅ A * 0
    // Should not be defined, but linker complains...
    template<typename A>
    auto equ_val(ka::zero_t, ka::product_t<A, ka::zero_t>) const -> bool {
      return true;
    }
    template<typename A, typename I, typename B>
    auto operator()(res_t<A, I> const& a, res_t<B, I> const& b) const -> bool {
      auto ea = a.empty();
      auto eb = b.empty();
      return ea == eb && iter(a) == iter(b) && (ea || equ_val(src(a), src(b)));
    }
  } equiv_res_zero;

} // namespace test_parse

// Zero-parser is the unit of the parser sum (up to isomorphism).
TEST(ParseSumMonoid, Zero) {
  using parse::ops::operator+;
  using namespace test_parse;
  auto equ = equ_parser(input_sample(), equiv_res_zero);
  auto _0 = parse::zero;
  auto p = sym_t<A>{};
  EXPECT_EQ(_0, parse::sum());
  EXPECT_TRUE(equ(_0 + _0, _0));
  EXPECT_TRUE(equ(p + _0, p));
  EXPECT_TRUE(equ(_0 + p, p));
}

// A semiring relates sum and product in the usual manner:
//  - 0 ≠ 1 (ensured by type system)
//  - annihilation property (this test):
///     0 = 0 * a = a * 0
//  - distributivity (next test):
//      a * (b + c) = (a * b) + (a * c)
//      (b + c) * a = (b * a) + (c * a)
TEST(ParseQuasiSemiring, AnnihilationProperty) {
  using parse::ops::operator*;
  using namespace test_parse;
  auto equ = equ_parser(input_sample(), equiv_res_zero);
  auto a = sym_t<A>{};
  auto _0 = parse::zero;
  EXPECT_TRUE(equ(_0, _0 * a));
  EXPECT_TRUE(equ(_0, a * _0));
}

namespace test_parse {
  struct equiv_res_distrib_t {
    template<typename IndexedPtr>
    auto src_idx(IndexedPtr p) const -> decltype(&**p) {
      return p != nullptr ? &**p : nullptr;
    }
    // Used by test for distributivity.
    template<typename A, typename B, typename C>
    auto distrib_equ(A& x_a, B* x_b, C* x_c, A& y_a, B* y_b, C* y_c) const -> bool {
      if ( (x_b == nullptr) != (y_b == nullptr)
        || (x_c == nullptr) != (y_c == nullptr)) return false;
      return x_a == y_a
        && (x_b != nullptr
            ? *x_b == *y_b
            : *x_c == *y_c);
    }
    // A * (B + C) ≅ (A * B) + (A * C)
    template<typename A, typename B, typename C>
    auto equ_val(
      ka::product_t<                                     // *
        A,                                               // A
        boost::variant<indexed_t<0, B>, indexed_t<1, C>> // B + C
      > const& x,
      boost::variant<                      // +
        indexed_t<0, ka::product_t<A, B>>, // A * B
        indexed_t<1, ka::product_t<A, C>>  // A * C
      > const& y) const -> bool {

      auto* y_ab = src_idx(boost::get<indexed_t<0, ka::product_t<A, B>>>(&y));
      auto* y_ac = src_idx(boost::get<indexed_t<1, ka::product_t<A, C>>>(&y));
      auto& y_a = y_ab != nullptr ? std::get<0>(*y_ab) : std::get<0>(*y_ac);
      auto* y_b = y_ab != nullptr ? &std::get<1>(*y_ab) : nullptr;
      auto* y_c = y_ac != nullptr ? &std::get<1>(*y_ac) : nullptr;
      auto& x_a = std::get<0>(x);
      auto* x_b = src_idx(boost::get<indexed_t<0, B>>(&std::get<1>(x)));
      auto* x_c = src_idx(boost::get<indexed_t<1, C>>(&std::get<1>(x)));
      return distrib_equ(x_a, x_b, x_c, y_a, y_b, y_c);
    }

    // (B + C) * A ≅ (B * A) + (C * A)
    // Factorization seems more trouble than having two versions.
    template<typename A, typename B, typename C>
    auto equ_val(
      ka::product_t<                                      // *
        boost::variant<indexed_t<0, B>, indexed_t<1, C>>, // B + C
        A                                                 // A
      > const& x,
      boost::variant<                      // +
        indexed_t<0, ka::product_t<B, A>>, // B * A
        indexed_t<1, ka::product_t<C, A>>  // C * A
      > const& y) const -> bool {

      auto* y_ba = src_idx(boost::get<indexed_t<0, ka::product_t<B, A>>>(&y));
      auto* y_ca = src_idx(boost::get<indexed_t<1, ka::product_t<C, A>>>(&y));
      auto& y_a = y_ba != nullptr ? std::get<1>(*y_ba) : std::get<1>(*y_ca);
      auto* y_b = y_ba != nullptr ? &std::get<0>(*y_ba) : nullptr;
      auto* y_c = y_ca != nullptr ? &std::get<0>(*y_ca) : nullptr;
      auto& x_a = std::get<1>(x);
      auto* x_b = src_idx(boost::get<indexed_t<0, B>>(&std::get<0>(x)));
      auto* x_c = src_idx(boost::get<indexed_t<1, C>>(&std::get<0>(x)));
      return distrib_equ(x_a, x_b, x_c, y_a, y_b, y_c);
    }
    template<typename A, typename I, typename B>
    auto operator()(res_t<A, I> const& a, res_t<B, I> const& b) const -> bool {
      auto ea = a.empty();
      auto eb = b.empty();
      return ea == eb && iter(a) == iter(b) && (ea || equ_val(src(a), src(b)));
    }
  } equiv_res_distrib;
} // namespace test_parse

TEST(ParseQuasiSemiring, Distributivity) {
  using parse::ops::operator*;
  using parse::ops::operator+;
  using namespace test_parse;
  auto equ = equ_parser(input_sample(), equiv_res_distrib);
  auto a = sym_t<A>{};
  auto b = sym_t<B>{};
  auto c = sym_t<C>{};
  EXPECT_TRUE(equ(a * (b + c), (a * b) + (a * c)));
  EXPECT_TRUE(equ((b + c) * a, (b * a) + (c * a)));
}

namespace test_parse {
  struct equiv_res_opt_t {
    // ka::opt_t<A> ≅ A + 1
    template<typename A>
    auto equ_val(ka::opt_t<A> x, SumValue<A, ka::unit_t> y) const -> bool {
      using ka::src;
      auto* y_a = boost::get<indexed_t<0, A>>(&y);
      return x.empty()
        ? y_a == nullptr
        : src(x) == src(src(y_a));
    }
    template<typename A, typename I, typename B>
    auto operator()(res_t<A, I> const& a, res_t<B, I> const& b) const -> bool {
      auto ea = a.empty();
      auto eb = b.empty();
      return ea == eb && iter(a) == iter(b) && (ea || equ_val(src(a), src(b)));
    }
  } equiv_res_opt;

} // namespace test_parse

// An optional parser of `A` always succeeds: if `A` cannot be parsed, it
// returns unit.
// opt(p) ≅ p + 1
TEST(ParseOpt, Basic) {
  using parse::ops::operator+;
  using namespace test_parse;
  auto _1 = parse::unit;
  auto p = sym_t<A>{};
  auto equ = equ_parser(input_sample(), equiv_res_opt);
  EXPECT_TRUE(equ(parse::opt(p), p + _1));
}

TEST(ParseQuantify, Basic) {
  auto const pa = test_parse::sym_t<A>{};
  using V = std::vector<A>;
  {
    { // Ok: No element.
      auto const s = std::string("A5A2B5");
      auto const b = s.begin();
      auto const e = s.end();
      auto p = quantify(pa, 0, 0);
      EXPECT_EQ(ok(V{}, b), p(b, e));
      EXPECT_EQ(ok(V{}, b), p(b, b)); // empty range
    }
    { // Ok: No element.
      auto const s = std::string("C5A2B5");
      auto const b = s.begin();
      auto const e = s.end();
      EXPECT_EQ(ok(V{}, b), quantify(pa, 0, 1)(b, e));
      EXPECT_EQ(ok(V{}, b), quantify(pa, 0, 2)(b, e));
      EXPECT_EQ(ok(V{}, b), quantify(pa, 0, 3)(b, e));
      EXPECT_EQ(ok(V{}, b), quantify(pa, 0)(b, e));
    }
    { // Ok: One element.
      auto const s = std::string("A5C2B5");
      auto const b = s.begin();
      auto const e = s.end();
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 0, 1)(b, e));
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 0, 2)(b, e));
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 0, 3)(b, e));
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 0)(b, e));
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 1, 1)(b, e));
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 1, 2)(b, e));
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 1, 3)(b, e));
      EXPECT_EQ(ok(V{A{5}}, b + 2), quantify(pa, 1)(b, e));
    }
    { // Ok: One or two elements.
      auto const s = std::string("A5A2B5");
      auto const b = s.begin();
      auto const e = s.end();
      EXPECT_EQ(ok(V{A{5}},       b + 2), quantify(pa, 0, 1)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 0, 2)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 0, 3)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 0)(b, e));
      EXPECT_EQ(ok(V{A{5}},       b + 2), quantify(pa, 1, 1)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 1, 2)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 1, 3)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 1)(b, e));
    }
    { // Ok: Two elements.
      auto const s = std::string("A5A2B5");
      auto const b = s.begin();
      auto const e = s.end();
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 0, 2)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 0)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 1, 2)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 1)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 2, 2)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 2, 3)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 2, 4)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), quantify(pa, 2)(b, e));
    }
    { // Ok: From two to three elements.
      auto const s = std::string("A5A2A6A9B5");
      auto const b = s.begin();
      auto const e = s.end();
      EXPECT_EQ(ok(V{A{5}, A{2}, A{6}}, b + 6), quantify(pa, 2, 3)(b, e));
      EXPECT_EQ(ok(V{A{5}, A{2}, A{6}}, b + 6), quantify(pa, 2, 3)(b, b + 6));
    }
    { // Ko: At least one element.
      auto const s = std::string("B5A2A6A9B5");
      auto const b = s.begin();
      auto const e = s.end();
      auto const t = type_t<V>{};
      EXPECT_EQ(err(t, b), quantify(pa, 1, 1)(b, e));
      EXPECT_EQ(err(t, b), quantify(pa, 1, 2)(b, e));
      EXPECT_EQ(err(t, b), quantify(pa, 1, 3)(b, e));
      EXPECT_EQ(err(t, b), quantify(pa, 1)(b, e));
    }
    { // Ko: From three to four elements.
      auto const s = std::string("A5A2B5");
      auto const b = s.begin();
      auto const e = s.end();
      auto const t = type_t<V>{};
      EXPECT_EQ(err(t, b), quantify(pa, 3, 4)(b, e));
    }
  }
}

namespace test_parse {
  struct list_fn_t {
    template<typename PA> constexpr
    auto operator()(PA&& pa) const -> decltype(list(fwd<PA>(pa))) {
      return list(fwd<PA>(pa));
    }
  };

  struct quantify_0_fn_t {
    template<typename PA> constexpr
    auto operator()(PA&& pa) const -> decltype(quantify(fwd<PA>(pa), 0)) {
      return quantify(fwd<PA>(pa), 0);
    }
  };

  using list_types = testing::Types<
    list_fn_t,
    quantify_0_fn_t
  >;
} // namespace test_parse

template<typename T> struct ParseList : testing::Test {};
TYPED_TEST_SUITE(ParseList, test_parse::list_types);

// List of a parser applies it as much as possible.
TYPED_TEST(ParseList, Basic) {
  auto list = TypeParam{};
  auto const pa = test_parse::sym_t<A>{};
  using V = std::vector<A>;

  { // Ok: No element.
    auto const s = std::string("C5A2B5");
    auto const b = s.begin();
    auto const e = s.end();
    EXPECT_EQ(ok(V{}, b), list(pa)(b, e));
  }
  { // Ok: One element.
    auto const s = std::string("A5B2B5");
    auto const b = s.begin();
    auto const e = s.end();
    EXPECT_EQ(ok(V{A{5}}, b + 2), list(pa)(b, e));
  }
  { // Ok: Two elements, range's end not reached.
    auto const s = std::string("A5A2C5");
    auto const b = s.begin();
    auto const e = s.end();
    EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), list(pa)(b, e));
  }
  { // Ok: Two elements, range's end reached.
    auto const s = std::string("A5A2");
    auto const b = s.begin();
    auto const e = s.end();
    EXPECT_EQ(ok(V{A{5}, A{2}}, b + 4), list(pa)(b, e));
  }
  { // Ok: Five elements, range's end not reached.
    auto const s = std::string("A5A2A8A9A0B7");
    auto const b = s.begin();
    auto const e = s.end();
    EXPECT_EQ(ok(V{A{5}, A{2}, A{8}, A{9}, A{0}}, b + 10), list(pa)(b, e));
  }
}

// List operator flattens lists, instead of nesting them.
// E.g. `**pa` has type `parse::list<PA>` instead of
// `parse::list<parse::list<PA>>`.
TEST(ParseList, Operator) {
  using parse::ops::operator*;
  auto pa = test_parse::sym_t<A>{};
  EXPECT_EQ(list(pa), *pa);
  EXPECT_EQ(*pa, **pa);
  EXPECT_EQ(*pa, ***pa);
  EXPECT_EQ(*pa, ************pa);
}

// TODO: Test list concatenation when available.
// TODO: Test empty list when available.
// TODO: Test list monoid when available.


#include "Algorithms/HackerRank/InsertionSort.h"
#include "Tools/CaptureCout.h"

#include <boost/test/unit_test.hpp>
#include <string>
#include <vector>

using Algorithms::HackerRank::Sorting::InsertionSort::insertion_sort;
using Algorithms::HackerRank::Sorting::InsertionSort::insertion_sort_1;
using Algorithms::HackerRank::Sorting::InsertionSort::running_time;
using Tools::CaptureCoutFixture;
using std::string;
using std::vector;

BOOST_AUTO_TEST_SUITE(Algorithms)
BOOST_AUTO_TEST_SUITE(HackerRank)
BOOST_AUTO_TEST_SUITE(Sorting)
BOOST_AUTO_TEST_SUITE(InsertionSort_tests)

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_FIXTURE_TEST_CASE(InsertionSort1PrintsSwapsAndInsertions,
	CaptureCoutFixture)
{
	vector<int> arr {1, 2, 4, 5, 3};
	BOOST_TEST_REQUIRE(arr[arr.size() - 1] == 3);
	BOOST_TEST_REQUIRE(arr[arr.size() - 2] == 5);

	insertion_sort_1(arr.size(), arr);

	restore_cout();

  BOOST_TEST(local_oss_.str() == "1 2 4 5 5 \n1 2 4 4 5 \n1 2 3 4 5 ");
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_FIXTURE_TEST_CASE(InsertionSort1PrintsSwapsAndInsertionsExample2,
	CaptureCoutFixture)
{
	vector<int> arr {2, 4, 6, 8, 3};
	BOOST_TEST_REQUIRE(arr[arr.size() - 1] == 3);
	BOOST_TEST_REQUIRE(arr[arr.size() - 2] == 8);

	insertion_sort_1(arr.size(), arr);

	restore_cout();

  BOOST_TEST(local_oss_.str() ==
  	"2 4 6 8 8 \n2 4 6 6 8 \n2 4 4 6 8 \n2 3 4 6 8 ");
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_FIXTURE_TEST_CASE(InsertionSort1PrintsSwapsAndInsertionsTestCase2,
	CaptureCoutFixture)
{
	vector<int> arr {2, 3, 4, 5, 6, 7, 8, 9, 10, 1};
	BOOST_TEST_REQUIRE(arr[arr.size() - 1] == 1);
	BOOST_TEST_REQUIRE(arr[arr.size() - 2] == 10);

	insertion_sort_1(arr.size(), arr);

	restore_cout();

	string expected {"2 3 4 5 6 7 8 9 10 10 \n"};
	expected += "2 3 4 5 6 7 8 9 9 10 \n";
	expected += "2 3 4 5 6 7 8 8 9 10 \n";
	expected += "2 3 4 5 6 7 7 8 9 10 \n";
	expected += "2 3 4 5 6 6 7 8 9 10 \n";
	expected += "2 3 4 5 5 6 7 8 9 10 \n";
	expected += "2 3 4 4 5 6 7 8 9 10 \n";
	expected += "2 3 3 4 5 6 7 8 9 10 \n";
	expected += "2 2 3 4 5 6 7 8 9 10 \n";
	expected += "1 2 3 4 5 6 7 8 9 10 ";

  BOOST_TEST(local_oss_.str() == expected);
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(InsertionSortSorts)
{
	int arr[] {4, 1, 3, 5, 6, 2};

	insertion_sort(6, arr);

	BOOST_TEST(arr[0] == 1);
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(RunningTimeCountsShifts)
{
	{
		vector<int> arr {2, 1, 3, 1, 2};

		BOOST_TEST(running_time(arr) == 4);

		BOOST_TEST(arr[0] == 1);
		BOOST_TEST(arr[1] == 1);
		BOOST_TEST(arr[2] == 2);
		BOOST_TEST(arr[3] == 2);
		BOOST_TEST(arr[4] == 3);
	}
}

// https://www.hackerrank.com/challenges/runningtime/problem?h_r=next-challenge&h_v=zen
// SampleTestCase1
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(RunningTimeCountsNoShiftsOnSortedList)
{
	vector<int> arr {1, 1, 2, 2, 3, 3, 5, 5, 7, 7, 9, 9};

	BOOST_TEST(running_time(arr) == 0);
}

BOOST_AUTO_TEST_SUITE_END() // InsertionSort_tests
BOOST_AUTO_TEST_SUITE_END() // Sorting
BOOST_AUTO_TEST_SUITE_END() // HackerRank
BOOST_AUTO_TEST_SUITE_END() // Algorithms


/**
 * @file    Format.cc
 * @brief   Implementation of double formatting strings
 * @author  [Yi-Mu "Enoch" Chen](https://github.com/yimuchen)
 */

#ifdef CMSSW_GIT_HASH
#include "UserUtils/Common/interface/Format.hpp"
#include "UserUtils/Common/interface/Maths.hpp"
#include "UserUtils/Common/interface/STLUtils/StringUtils.hpp"
#else
#include "UserUtils/Common/Format.hpp"
#include "UserUtils/Common/Maths.hpp"
#include "UserUtils/Common/STLUtils/StringUtils.hpp"
#endif

#include <boost/algorithm/string.hpp>
#include <cmath>
#include <regex>
#include <string>

namespace usr {

namespace fmt {

namespace base {

/*-----------------------------------------------------------------------------
 *  Default settings control variables
   --------------------------------------------------------------------------*/

/**
 * @details default precision is set such that loss of precision due to IO would
 * not be of too much concern, while avoiding artifacts in decimal--binary
 * conversions (ex. 1.7=1.6999999999999999556 )
 */
int precision_default = 8;

/**
 * @details spacing between digits should be universal (i.e. three digits), but
 * can be changed if publishing to journal using another number system.
 */
unsigned spacesep_default = 3;

/**
 * @details Defaults to none, as it is easier on the eyes when testing code with
 * screen I/O, but you might want to set it to the latex "\," string when
 * mass producing numbers for publication.
 */
std::string spacestr_default = "";

/**
 * @details library specifically doesn't allow more digits then 27 to be
 * printed after the decimal point. This is pretty close to the recommended
 * precision limit of doubles anyway.
 */
const unsigned max_precision = 27;

/**
 * @brief generating string to represent double as a string in decimal.
 */
std::string
decimal::str() const
{
  const unsigned op_precision = std::min( abs( _precision ), abs( max_precision ) );
  std::string retstr          = usr::fstr( usr::fstr( "%%.%df", op_precision ), _input );

  // stripping trailing zero after decimal point
  if( _precision < 0 && retstr.find( '.' ) != std::string::npos ){
    boost::trim_right_if( retstr, boost::is_any_of( "0" ) );
    boost::trim_right_if( retstr, boost::is_any_of( "." ) );
  }

  // Adding spacing string every _spacesep digits away from decimal point
  // Largest double is around e308
  if( _spacesep != 0 && _spacestr != "" ){
    int space = ( ( (int)( retstr.length()/_spacesep ) ) + 1 ) * _spacesep;

    while( space > 0 ){
      if( retstr.find( '.' ) != std::string::npos ){
        // If decimal point exists, expand around decimal point
        const std::regex before( usr::fstr( "(.*\\d)(\\d{%d}\\..*)", space ) );
        const std::regex after(  usr::fstr( "(.*\\.\\d{%d})(\\d.*)", space ) );
        retstr = std::regex_replace( retstr, before, "$1"+_spacestr+"$2" );
        retstr = std::regex_replace( retstr, after,  "$1"+_spacestr+"$2" );
      } else {
        // If decimal point doesn't exist, expand around right most side
        const std::regex beforedec(  usr::fstr( "(.*\\d)(\\d{%d})", space ) );
        retstr = std::regex_replace( retstr, beforedec, "$1"+_spacestr+"$2" );
      }
      space -= _spacesep;
    }
  }
  return retstr;
}

/**
 * @brief Requires explicit specification of precision (cannot be negative)
 */
scientific::scientific( const double x, const unsigned p )
{
  precision( p );
  _mant = x;
  _exp  = ReduceToMant( _mant );
}

/**
 * @brief Generating string to represent double as string in scientific
 *  notations.
 */
std::string
scientific::str() const
{
  const std::string base = decimal( _mant, _precision ).dupsetting( *this ).str();
  // largest exponent should be around ~300
  // no need of additional formatting.
  const std::string ans
    = ( _exp == 0 ) ?  base : usr::fstr( "%s \\times 10^{%d}", base, _exp );
  return ans;
}

}/* base */

}/* fmt */

}/* usr */


/*! \file demo_1d_tick_values.cpp
    \brief Demonstration of some simple 1D tick value label formatting.
    \details Quickbook markup to include in documentation.
    \date 19 Jul 2009
    \author Paul A. Bristow
*/

// Copyright Paul A Bristow 2009

// Use, modification and distribution are subject to 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)

// An example to demonstrate simple 1D settings but tick value formatting.
// See also demo_1d_plot.cpp for a wider range of use.

// This file is written to be included from a Quickbook .qbk document.
// It can be compiled by the C++ compiler, and run. Any output can
// also be added here as comment or included or pasted in elsewhere.

// Caution: this file contains Quickbook markup as well as code
// and comments: don't change any of the special comment markups!

//[demo_1d_tick_values_1

/*`As ever, we need a few includes to use Boost.Plot and an STL container.
*/
//] [demo_1d_tick_values_1]

#include <boost/svg_plot/svg_1d_plot.hpp>
  using namespace boost::svg;
  using boost::svg::svg_1d_plot;

  #include <boost/svg_plot/show_1d_settings.hpp>
// using boost::svg::show_1d_plot_settings - Only needed for showing which settings in use.

#include <iostream>
  using std::cout;
  using std::endl;
  using std::hex;
  using std::dec;
  using std::ios_base;
  using std::fixed;
  using std::scientific;

#include <iomanip>
  using std::setprecision;
  using std::setiosflags;

#include <vector>
  using std::vector;

int main()
{
 //[demo_1d_tick_values_2
/*`Some fictional data is pushed into an STL container, here vector<double>:*/
  vector<double> my_data;
  my_data.push_back(-1.6);
  my_data.push_back(4.2563);
  my_data.push_back(0.00333974);
  my_data.push_back(5.4);
  my_data.push_back(6.556);

  try
  { // try'n'catch blocks are needed to ensure error messages from any exceptions are shown.
    svg_1d_plot my_1d_plot; // Construct a plot with all the default constructor values.

    my_1d_plot.title("Demo 1D Tick Values") // Add a string title of the plot.
      .x_range(-5, 10) // Add a range for the X-axis.
      .x_label("temp (°C)"); // Add a label for the X-axis, using Unicode degree symbol.

/*`Add the one data series, `my_data` and a description,
and how the data points are to be marked, a circle with a diameter of 7 pixels.
*/
    my_1d_plot.plot(my_data, "1D Values").shape(circlet).size(7);

/*`If the default size and color are not to your taste, set more options, like:
*/
    my_1d_plot.size(500, 150) // Change plot window from the default image size.
      // Change the X-axis label style:
      .x_axis_label_color(green)
      .x_label_font_family("Arial")
      .x_label_font_size(18)

      // Change the style of the X (major) ticks:
      .x_ticks_values_color(magenta)
      //.x_ticks_values_font_family("Times New Roman")
      .x_ticks_values_font_family("arial")
      .x_ticks_values_font_size(15)

/*`The format of the tick value labels may not suit your data and its range,
so we can use the normal `iostream precision` and `ioflags` to change,
here to reduce the number of digits used from default precision 6 down to a more readable 2,
reducing the risk of collisions between adjacent values.
If values are very close to each other (a small range on the axis),
a higher precision wil be needed to differentiate them).
We could also prescribe the use of scientific format and force a positive sign:
By default, any unnecessary spacing-wasting zeros in the exponent field are removed.

If, perversely, the full 1.123456e+012 format is required, the stripping can be switched off with:
  `my_1d_plot.x_ticks_values_strip_e0s(false);`
*/
      // Change the format from the default "-4", "-2", "0" "2", "4" ...
      // (which makes a 'best guess' at the format)
      // to "-4.00", "-2.00", ..."+2.00", "4.00"
      // showing trailing zeros and a leading positive sign.
      .x_ticks_values_ioflags(ios_base::fixed | std::ios::showpos)
      .x_ticks_values_precision(1) //

      // One could use ios_base::scientific for e format.
      //.x_ticks_values_ioflags(ios_base::fixed | std::ios::showpos )
      //.x_ticks_values_ioflags(std::ios::showpoint | std::ios::showpos)
      //.x_ticks_values_ioflags(std::ios::scientific)
     ;

/*`To use all these settings, finally write the plot to file.
*/
    my_1d_plot.write("demo_1d_tick_values.svg");

/*`If chosen settings do not have the effect that you expect, it may be helpful to display them.

(All the myriad settings can be displayed with `show_1d_plot_settings(my_1d_plot)`.)
*/
    //show_1d_plot_settings(my_1d_plot);
    using boost::svg::detail::operator<<;
    cout << "my_1d_plot.size() " << my_1d_plot.size() << endl;
    cout << "my_1d_plot.x_size() " << my_1d_plot.x_size() << endl;
    cout << "my_1d_plot.y_size() " << my_1d_plot.y_size() << endl;

    cout << "my_1d_plot.x_axis_label_color() " << my_1d_plot.x_axis_label_color() << endl;
    cout << "my_1d_plot.x_label_font_family() " << my_1d_plot.x_label_font_family() << endl;
    cout << "my_1d_plot.x_label_font_size() " << my_1d_plot.x_label_font_size() << endl;

    cout << "my_1d_plot.x_ticks_values_font_family() " << my_1d_plot.x_ticks_values_font_family() << endl;
    cout << "my_1d_plot.x_ticks_values_font_size() " << my_1d_plot.x_ticks_values_font_size() << endl;
    cout << "my_1d_plot.x_ticks_values_color() " << my_1d_plot.x_ticks_values_color() << endl;

    cout << "my_1d_plot.x_ticks_values_precision() " << my_1d_plot.x_ticks_values_precision() << endl;
    cout << "my_1d_plot.x_ticks_values_ioflags() " << hex << my_1d_plot.x_ticks_values_ioflags() << endl;

/*`See demo_1d_ticks_values.cpp for full source code.
*/

//] [demo_1d_tick_values_2]
  }
  catch(const std::exception& e)
  {
    std::cout <<
      "\n""Message from thrown exception was:\n   " << e.what() << std::endl;
  }
  return 0;
} // int main()

/*

//[demo_1d_tick_values_output

Output:

utorun "j:\Cpp\SVG\Debug\demo_1d_ticks_values.exe
my_1d_plot.image_size() 500, 150
my_1d_plot.image_x_size() 500
my_1d_plot.image_y_size() 150
my_1d_plot.x_axis_label_color() RGB(0,128,0)
my_1d_plot.x_label_font_family() Arial
my_1d_plot.x_label_font_size() 18
my_1d_plot.x_ticks_values_font_family() Verdana
my_1d_plot.x_ticks_values_font_size() 12
my_1d_plot.x_ticks_values_color() RGB(255,0,255)
my_1d_plot.x_ticks_values_precision() 1
my_1d_plot.x_ticks_values_ioflags() 2020

//] [demo_1d_tick_values_output]

*/


//////////////////////////////////////////////////////////////////////////////
///   Copyright 2003 and onward LASMEA UMR 6602 CNRS/U.B.P Clermont-Ferrand
///   Copyright 2009 and onward LRI    UMR 8623 CNRS/Univ Paris Sud XI
///
///          Distributed under the Boost Software License, Version 1.0
///                 See accompanying file LICENSE.txt or copy at
///                     http://www.boost.org/LICENSE_1_0.txt
//////////////////////////////////////////////////////////////////////////////
#define NT2_UNIT_MODULE "nt2 bitwise toolbox - lo/scalar Mode"

//////////////////////////////////////////////////////////////////////////////
// Test behavior of bitwise components in scalar mode
//////////////////////////////////////////////////////////////////////////////
/// created  by jt the 18/02/2011
/// modified by jt the 16/03/2011
#include <boost/type_traits/is_same.hpp>
#include <nt2/sdk/functor/meta/call.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/memory/buffer.hpp>
#include <nt2/sdk/constant/real.hpp>
#include <nt2/sdk/constant/infinites.hpp>
#include <nt2/include/functions/ulpdist.hpp>
#include <nt2/toolbox/bitwise/include/lo.hpp>
// specific includes for arity 1 tests
#include<nt2/sdk/meta/downgrade.hpp>

NT2_TEST_CASE_TPL ( lo_real__1,  NT2_REAL_TYPES)
{

  using nt2::lo;
  using nt2::tag::lo_;
  typedef typename nt2::meta::as_integer<T,unsigned>::type ir_t;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<lo_(T)>::type r_t;
  typedef typename nt2::meta::upgrade<T>::type u_t;
  typedef typename nt2::meta::downgrade<ir_t>::type wished_r_t;

  // return type conformity test
  NT2_TEST( (boost::is_same < r_t, wished_r_t >::value) );
  std::cout << std::endl;
  double ulpd;
  ulpd=0.0;

  // specific values tests
  NT2_TEST_EQUAL(lo(nt2::Nan<T>()), nt2::Mone<r_t>());
  NT2_TEST_EQUAL(lo(nt2::One<T>()), nt2::Zero<r_t>());
  NT2_TEST_EQUAL(lo(nt2::Zero<T>()), nt2::Zero<r_t>());
} // end of test for real_

NT2_TEST_CASE_TPL ( lo_int64__1,  (int64_t)(uint64_t))
{

  using nt2::lo;
  using nt2::tag::lo_;
  typedef typename nt2::meta::as_integer<T,unsigned>::type ir_t;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<lo_(T)>::type r_t;
  typedef typename nt2::meta::upgrade<T>::type u_t;
  typedef typename nt2::meta::downgrade<ir_t>::type wished_r_t;

  // return type conformity test
  NT2_TEST( (boost::is_same < r_t, wished_r_t >::value) );
  std::cout << std::endl;
  double ulpd;
  ulpd=0.0;

  // specific values tests
  NT2_TEST_EQUAL(lo(nt2::One<T>()), nt2::One<r_t>());
  NT2_TEST_EQUAL(lo(nt2::Zero<T>()), nt2::Zero<r_t>());
} // end of test for int64_

NT2_TEST_CASE_TPL ( lo_int32__1,  (int32_t)(uint32_t))
{

  using nt2::lo;
  using nt2::tag::lo_;
  typedef typename nt2::meta::as_integer<T,unsigned>::type ir_t;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<lo_(T)>::type r_t;
  typedef typename nt2::meta::upgrade<T>::type u_t;
  typedef typename nt2::meta::downgrade<ir_t>::type wished_r_t;

  // return type conformity test
  NT2_TEST( (boost::is_same < r_t, wished_r_t >::value) );
  std::cout << std::endl;
  double ulpd;
  ulpd=0.0;

  // specific values tests
  NT2_TEST_EQUAL(lo(nt2::One<T>()), nt2::One<r_t>());
  NT2_TEST_EQUAL(lo(nt2::Zero<T>()), nt2::Zero<r_t>());
} // end of test for int32_

NT2_TEST_CASE_TPL ( lo_int16__1,  (int16_t)(uint16_t))
{

  using nt2::lo;
  using nt2::tag::lo_;
  typedef typename nt2::meta::as_integer<T,unsigned>::type ir_t;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<lo_(T)>::type r_t;
  typedef typename nt2::meta::upgrade<T>::type u_t;
  typedef typename nt2::meta::downgrade<ir_t>::type wished_r_t;

  // return type conformity test
  NT2_TEST( (boost::is_same < r_t, wished_r_t >::value) );
  std::cout << std::endl;
  double ulpd;
  ulpd=0.0;

  // specific values tests
  NT2_TEST_EQUAL(lo(nt2::One<T>()), nt2::One<r_t>());
  NT2_TEST_EQUAL(lo(nt2::Zero<T>()), nt2::Zero<r_t>());
} // end of test for int16_


//  Copyright (c) 2019 Rustam Abdumalikov
//
//  "eswitch_v4" library
//
// 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 "qs.cpp"
#include "qs_pivot.cpp"
#include <iostream>
#include <vector>
#include <boost/range/adaptor/indexed.hpp>
#include <chrono>

#include <random>

#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "catch2/catch.hpp"

std::vector<int64_t> getRandomList( int64_t n )
{
    std::random_device rd; // obtain a random number from hardware
    std::mt19937 gen(rd()); // seed the generator
    std::uniform_int_distribution<> distr(0, n*n); // define the range

    std::vector<int64_t> output;
    output.reserve( n );

    for(int64_t i=0; i<n; ++i)
        output.push_back( distr(gen) );

    return output;
}

void printList( const std::vector< int64_t > & pivots ) {
    for( auto p : pivots )
        std::cout << p << ", ";
    std::cout << std::endl;
}

TEST_CASE( "sort_pivots2", "" )
{
    using namespace std;

    SECTION( "sort_pivots" ) {
        vector<int64_t> values { 4, 1, 2, 0, 1 };
        vector<int64_t> pivots { 4, 2, 0, 3 };
        sort_pivots2( values, 0, values.size(), pivots );

        REQUIRE( values == vector<int64_t>{ 0, 1, 1, 2, 4 } );
        REQUIRE( pivots == vector<int64_t>{ 0, 2, 3, 4 } );
    }
}

TEST_CASE( "sort_pivots_1", "" )
{
    using namespace std;

    SECTION( "sort_pivots" ) {
        vector< int64_t > values = { 1, 2, 3, 4, 5, 7, 6 };
        vector< int64_t > pivots = {6};
        const auto results = sort_pivots( values, 0, values.size(), std::move(pivots) );

        REQUIRE( values == vector<int64_t>{ 6, 2, 3, 4, 5, 7, 1  } );
        REQUIRE( results == vector<int64_t>{ 0 } );
    }
}

TEST_CASE( "sort_pivots_2", "" )
{
    using namespace std;

    SECTION( "all_permutations" ) {
        vector< int64_t > pivots = {5,6};
        do {
            //printList( pivots );

            vector< int64_t > values = { 1, 2, 3, 4, 5, 7, 6 };

            auto ps = pivots;
            const auto results = sort_pivots( values, 0, values.size(), std::move(ps) );

            REQUIRE( values == vector<int64_t>{ 6, 7, 3, 4, 5, 1, 2} );
            REQUIRE( results == vector<int64_t>{ 0, 1 } );
        } while( std::next_permutation(std::begin(pivots), std::end(pivots)) );
    }
}

TEST_CASE( "sort_pivots_3", "" )
{
    using namespace std;

    SECTION( "all_permutations" ) {
        vector< int64_t > pivots = {0, 3, 6};
        do {
            //printList( pivots );

            vector< int64_t > values = { 4, 2, 3, 6, 5, 7, 1 };

            auto ps = pivots;
            const auto results = sort_pivots( values, 0, values.size(), std::move(ps) );

            REQUIRE( values == vector<int64_t>{ 1, 4, 6, 2, 5, 7, 3 } );
            REQUIRE( results == vector<int64_t>{ 0, 1, 2 } );
        } while( std::next_permutation(std::begin(pivots), std::end(pivots)) );
    }
}

TEST_CASE( "sort_pivots_4", "" )
{
    using namespace std;

    SECTION( "all_permutations" ) {
        vector< int64_t > pivots = {0, 1, 2, 3};
        do {
            //printList( pivots );

            vector< int64_t > values = { 4, 2, 3, 6, 5, 7, 1 };

            auto ps = pivots;
            const auto results = sort_pivots( values, 0, values.size(), std::move(ps) );

            REQUIRE( values == vector<int64_t>{ 2, 3, 4, 6, 5, 7, 1 } );
            REQUIRE( results == vector<int64_t>{ 0, 1, 2, 3 } );
        } while( std::next_permutation(std::begin(pivots), std::end(pivots)) );
    }
}

TEST_CASE( "sort_pivots_5", "" )
{
    using namespace std;

    SECTION( "all_permutations" ) {
        vector< int64_t > pivots = {2, 3, 4, 5, 6};
        do {
            //printList( pivots );

            vector< int64_t > values = { 4, 2, 3, 6, 5, 7, 1 };

            auto ps = pivots;
            const auto results = sort_pivots( values, 0, values.size(), std::move(ps) );

            REQUIRE( values == vector<int64_t>{ 1, 3, 5, 6, 7, 2, 4 } );
            REQUIRE( results == vector<int64_t>{ 0, 1, 2, 3, 4 } );
        } while( std::next_permutation(std::begin(pivots), std::end(pivots)) );
    }
}

TEST_CASE( "sort_pivots_narrow_range_middle", "" )
{
    using namespace std;

    SECTION( "all_permutations" ) {
        vector< int64_t > pivots = {1, 2, 3};
        do {
            //printList( pivots );

            vector< int64_t > values = { 4, 6, 2, 3, 5, 7, 1 };

            auto ps = pivots;
            const auto results = sort_pivots( values, 0, 4, std::move(ps) );

            REQUIRE( values == vector<int64_t>{ 2, 3, 6, 4, 5, 7, 1 } );
            REQUIRE( results == vector<int64_t>{ 0, 1, 2 } );
        } while( std::next_permutation(std::begin(pivots), std::end(pivots)) );
    }
}

TEST_CASE( "sort_pivots_narrow_range_begin", "" )
{
    using namespace std;

    SECTION( "all_permutations" ) {
        vector< int64_t > pivots = {1, 2, 3};
        do {
            //printList( pivots );

            vector< int64_t > values = { 4, 6, 2, 3, 5, 7, 1 };

            auto ps = pivots;
            const auto results = sort_pivots( values, 0, 3, std::move(ps) );

            REQUIRE( values == vector<int64_t>{ 2, 3, 6, 4, 5, 7, 1 } );
            REQUIRE( results == vector<int64_t>{ 0, 1, 2 } );
        } while( std::next_permutation(std::begin(pivots), std::end(pivots)) );
    }
}

TEST_CASE( "sort_pivots_narrow_range_end", "" )
{
    using namespace std;

    SECTION( "all_permutations" ) {
        vector< int64_t > pivots = {4, 5, 6};
        do {
            //printList( pivots );

            vector< int64_t > values = { 4, 6, 2, 3, 5, 7, 1 };

            auto ps = pivots;
            const auto results = sort_pivots( values, 3, 6, std::move(ps) );

            REQUIRE( values == vector<int64_t>{ 4, 6, 2, 1, 5, 7, 3 } );
            REQUIRE( results == vector<int64_t>{ 3, 4, 5 } );
        } while( std::next_permutation(std::begin(pivots), std::end(pivots)) );
    }
}

TEST_CASE( "get_pivots_narrow_pivot_numbers", "1-10" )
{
    using namespace std;

    SECTION( "full size" ) {
        vector< int64_t > values = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        const auto results = get_pivot( values, 0, values.size()-1, values.size() );

        REQUIRE( results.size() == (values.size()-1)/ 2 );
        REQUIRE( results[0] >= 0 );
        REQUIRE( results[0] <= 4 );
    }
}

TEST_CASE( "get_pivots_1", "1-10" )
{
    using namespace std;

    SECTION( "0-9" ) {
        vector< int64_t > values = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        const auto results = get_pivot( values, 0, values.size()-1, 1 );

        REQUIRE( results.size() == 1 );
        REQUIRE( results[0] >= 0 );
        REQUIRE( results[0] <= 9 );
    }

    SECTION( "0-4" ) {
        vector< int64_t > values = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        const auto results = get_pivot( values, 0, 4, 1 );

        REQUIRE( results.size() == 1 );
        REQUIRE( results[0] >= 0 );
        REQUIRE( results[0] <= 4 );
    }

    SECTION( "5-9" ) {
        vector< int64_t > values = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        const auto results = get_pivot( values, 0, 4, 1 );

        REQUIRE( results.size() == 1 );
        REQUIRE( results[0] >= 0 );
        REQUIRE( results[0] <= 4 );
    }
}

TEST_CASE( "general_partition", "" )
{
    using namespace std;

    SECTION( "1_pivot" ) {
        vector< int64_t > values = { 1, 2, 3, 4, 6, 7, 8, 9, 10 };

        do {
            vector< int64_t > new_values;
            new_values.push_back( 5 );

            for( auto v : values ) new_values.push_back( v );

            vector< int64_t > pivots = { 0 };

            const auto results = general_partition( new_values, 0, new_values.size()-1, pivots );

            REQUIRE( new_values[results[0]] == 5 );

            for( int i = 0; i < new_values.size(); ++i )
            {
                if( i == results[0] ) continue;
                if( i < results[0] )
                    REQUIRE( new_values[i] <= new_values[results[0]] );
                else if( i > results[0] )
                    REQUIRE( new_values[i] > new_values[results[0]] );
            }

        } while( std::next_permutation(std::begin(values), std::end(values)) );

    }
}

TEST_CASE( "general_partition2", "" )
{
    using namespace std;

    SECTION( "2_pivot" ) {
        vector< int64_t > values = { 1, 2, 3, 4, 9, 10, 11 };

        do {
            vector< int64_t > new_values;
            new_values.push_back( 5 );
            new_values.push_back( 8 );

            for( auto v : values ) new_values.push_back( v );

            vector< int64_t > pivots = { 0, 1 };

            const auto results = general_partition( new_values, 0, new_values.size()-1, pivots );

            REQUIRE( new_values[results[0]] == 5 );
            REQUIRE( new_values[results[1]] == 8 );

            for( int i = 0; i < new_values.size(); ++i )
            {
                if( i == results[0] || i == results[1] ) continue;

                if( i < results[0] )
                    REQUIRE( new_values[i] <= new_values[results[0]] );
                else if( i > results[0] && i < results[1] )
                {
                    REQUIRE( new_values[i] > new_values[results[0]] );
                    REQUIRE( new_values[i] <= new_values[results[1]] );
                }
                else if( i > results[0] && i > results[1] )
                {
                    REQUIRE( new_values[i] > new_values[results[1]] );
                }
            }

        } while( std::next_permutation(std::begin(values), std::end(values)) );

    }
}

TEST_CASE( "auxiliary_functions", "" )
{
    SECTION( "less_or_equal_1" ) {
        std::vector< int64_t > values = { 1, 2, 3, 4, 9, 10, 11 };
        std::vector< int64_t > pivots = { 0, 1, 2, 3, 4, 5, 6 };
        int64_t val;
        std::vector< bool > correctResults = { 1, 1, 1, 1, 1, 1, 1, 1 };

        int i =0;
        for(auto v: values){
            val=v;
            auto results=less_or_equal(values,pivots,val);
            REQUIRE(results==correctResults);
            correctResults[i]=0;
            i++;
        }

    }
    SECTION( "greater_1" ) {
        std::vector< int64_t > values = { 1, 2, 3, 4, 9, 10, 11 };
        std::vector< int64_t > pivots = { 0, 1, 2, 3, 4, 5, 6 };
        int64_t val;
        std::vector< bool > correctResults = { 1, 0, 0, 0, 0, 0, 0, 0 };

        int i =0;
        for(auto v: values){
            val=v;
            auto results=greater(values,pivots,val);
            REQUIRE(results==correctResults);
            i++;
            correctResults[i]=1;
        }

    }
}

TEST_CASE( "quicksort", "" )
{
    using namespace std;

    SECTION( "100_entries" ) {
        for( int64_t np = 1; np <= 90; ++np ) {
            std::vector< int64_t > v = getRandomList( 100 );
            quicksort( v, np );

            REQUIRE( std::is_sorted( std::begin( v ), std::end( v ) ) );
        }
    }

    SECTION( "1000_entries" ) {
        for( int64_t np = 1; np <= 900; ++np ) {
            std::vector< int64_t > v = getRandomList( 1000 );
            quicksort( v, np );

            REQUIRE( std::is_sorted( std::begin( v ), std::end( v ) ) );
        }
    }

    SECTION( "10000_entries" ) {
        for( int64_t np = 1; np <= 50; ++np ) {
            std::vector< int64_t > v = getRandomList( 10000 );
            quicksort( v, np );

            REQUIRE( std::is_sorted( std::begin( v ), std::end( v ) ) );
        }
    }
}

TEST_CASE( "qs::range", "" )
{
    using namespace std;

    SECTION( "inside_range" ) {
    for( auto num_of_entries : { 100000} ) {
        for( int64_t np = 1; np <= 30; ++np ) {
            double total_duration = 0.0;
            int64_t num_durations = 0;

            for( int64_t i = 1; i < 90; ++i ) {
                std::vector< int64_t > v = getRandomList( num_of_entries );

                std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();

                quicksort( v, np );

                // std::qsort(
                //     v.data(),
                //     v.size(),
                //     sizeof(int64_t),
                //     [](const void* x, const void* y) {
                //         return ( *(int64_t*)x - *(int64_t*)y );
                //     });

                std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();

                if( !std::is_sorted( std::begin( v ), std::end( v ) ) )
                {
                    std::cout << "Failed to sort!!!" << std::endl;
                    break;
                }

                total_duration +=  std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();

                ++num_durations;

            }

            std::cout << "[" << np << "]" << "[" << num_of_entries << "] Time difference = " << total_duration/num_durations << "[ms]" << std::endl;
        }

        REQUIRE( true );
    }
    }
}

template< int Index >
void ct_quicksort()
{
    double total_duration = 0.0;
    int64_t num_durations = 0;

    for( int64_t i = 1; i < 30; ++i ) {
        std::vector< int64_t > v = getRandomList( 100000 );

        std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();

        tmpl::quicksort<Index>( v );

        std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();

        if( !std::is_sorted( std::begin( v ), std::end( v ) ) )
        {
            std::cout << "Failed to sort!!!" << std::endl;
            break;
        }

        total_duration +=  std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();

        ++num_durations;
    }

    std::cout << "[" << Index << "] Time difference = " << total_duration/num_durations << "[ms]" << std::endl;
}

template< int End, int Index >
void for_loop()
{
    if constexpr( End < Index ) {
        ct_quicksort<Index>();
        for_loop< End,Index+1 >();
    }
}

// TEST_CASE( "qs::range2", "" )
// {
//     using namespace std;

//     SECTION( "inside_range" ) {
//         for_loop< 5, 1 >();

//         REQUIRE( true );
//     }
// }

// bool test_partitioning()
// {
//     for( int64_t i = 0; i < 100; ++i )
//     {
//         std::vector< int64_t > v = getRandomList( 50 );

//         auto l = get_pivot( v, 0, v.size() - 1, 4 );

//         general_partition( v, 0, v.size() - 1,  l );

//         for( auto pivot : l )
//         {
//             for( int64_t j = 0; j <= pivot; ++j )
//             {
//                 if( v[pivot] < v[j] ) { printf( "Error" ); return false; }
//             }
//         }

//         auto lastIdx = *(l.rbegin());

//         for( auto k = lastIdx+1; k < v.size(); ++k )
//         {
//             if( !(v[k] > v[lastIdx]) ) { printf( "Error" ); return false; }
//         }
//     }

//     return true;
// }