//! \file test_expected.cpp

// Copyright Pierre Talbot 2013.
// Copyright Vicente J. Botet Escriba 2013,2014.

// 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)


// Notes by ned:
// Original is at https://github.com/viboes/std-make/blob/master/test/expected/expected_pass.cpp
// This edition modified to use result with throw_bad_result_access policy
// Quite a lot of the test suite I had to disable, not because our Expected implementation is
// incorrect, but because the reference test suite is testing an Expected quite far away from
// the latest WG21 proposal paper, and we're implementing that latest edition.

#include <utility>

#include "../include/outcome/iostream_support.hpp"
#include "../include/outcome/std_result.hpp"

#if defined(__clang__) || __GNUC__ >= 7 || !defined(BOOST_MSVC) || (BOOST_MSVC > 0 && _HAS_CXX17)

#define QUICKCPPLIB_BOOST_UNIT_TEST_CUSTOM_MAIN_DEFINED
#include "quickcpplib/boost/test/unit_test.hpp"

#define JASEL_NORETURN
#ifndef BOOST_TEST
#define BOOST_TEST(expr) BOOST_CHECK(expr)
#endif
#ifndef BOOST_TEST_EQ
#define BOOST_TEST_EQ(a, b) BOOST_CHECK_EQUAL((a), (b))
#endif
#ifndef BOOST_TEST_THROWS
#define BOOST_TEST_THROWS(expr, ex) BOOST_CHECK_THROW((expr), ex)
#endif
#ifndef BOOST_CONSTEXPR
#define BOOST_CONSTEXPR constexpr
#endif

#ifdef _MSC_VER
#pragma warning(disable : 4127)  // conditional expression is constant
#pragma warning(disable : 4244)  // conversion from int to short
#endif

namespace stde
{
#if __cplusplus >= 201700 || _HAS_CXX17
  using in_place_t = std::in_place_t;
  using std::in_place;
#else
  struct in_place_t
  {
    explicit in_place_t() = default;
  };
  constexpr in_place_t in_place{};
#endif

  //! [expected_implementation]
  /* Here is a fairly conforming implementation of P0323R3 `expected<T, E>` using `checked<T, E>`.
  It passes the reference test suite for P0323R3 at
  https://github.com/viboes/std-make/blob/master/test/expected/expected_pass.cpp with modifications
  only to move the test much closer to the P0323R3 Expected, as the reference test suite is for a
  much older proposed Expected.

  Known differences from P0323R3 in this implementation:
  - `T` and `E` cannot be the same type.
  - No variant storage is implemented.
  */

  namespace detail
  {
    template <class T, class E> using expected_result = OUTCOME_V2_NAMESPACE::checked<T, E>;
    template <class T, class E> struct enable_default_constructor : public expected_result<T, E>
    {
      using base = expected_result<T, E>;
      using base::base;
      constexpr enable_default_constructor()
          : base{OUTCOME_V2_NAMESPACE::in_place_type<T>}
      {
      }
    };
    template <class T, class E> using select_expected_base = std::conditional_t<std::is_default_constructible<T>::value, enable_default_constructor<T, E>, expected_result<T, E>>;
  }  // namespace detail
  template <class T, class E> class expected : public detail::select_expected_base<T, E>
  {
    static_assert(!std::is_same<T, E>::value, "T and E cannot be the same in this expected implementation");
    using base = detail::select_expected_base<T, E>;

  public:
    // Inherit base's constructors
    using base::base;
    expected() = default;

    // Expected takes in_place not in_place_type
    template <class... Args>
    constexpr explicit expected(in_place_t /*unused*/, Args &&... args)
        : base{OUTCOME_V2_NAMESPACE::in_place_type<T>, std::forward<Args>(args)...}
    {
    }

    // Expected always accepts a T even if ambiguous
    OUTCOME_TEMPLATE(class U)
    OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_constructible<T, U>::value))
    constexpr expected(U &&v)  // NOLINT
    : base{OUTCOME_V2_NAMESPACE::in_place_type<T>, std::forward<U>(v)}
    {
    }

    // Expected has an emplace() modifier
    template <class... Args> void emplace(Args &&... args) { *static_cast<base *>(this) = base{OUTCOME_V2_NAMESPACE::in_place_type<T>, std::forward<Args>(args)...}; }

    // Expected has a narrow operator* and operator->
    constexpr const T &operator*() const & { return base::assume_value(); }
    constexpr T &operator*() & { return base::assume_value(); }
    constexpr const T &&operator*() const && { return base::assume_value(); }
    constexpr T &&operator*() && { return base::assume_value(); }
    constexpr const T *operator->() const { return &base::assume_value(); }
    constexpr T *operator->() { return &base::assume_value(); }

    // Expected has a narrow error() observer
    constexpr const E &error() const & { return base::assume_error(); }
    constexpr E &error() & { return base::assume_error(); }
    constexpr const E &&error() const && { return base::assume_error(); }
    constexpr E &error() && { return base::assume_error(); }
  };
  template <class E> class expected<void, E> : public OUTCOME_V2_NAMESPACE::result<void, E, OUTCOME_V2_NAMESPACE::policy::throw_bad_result_access<E, void>>
  {
    using base = OUTCOME_V2_NAMESPACE::result<void, E, OUTCOME_V2_NAMESPACE::policy::throw_bad_result_access<E, void>>;

  public:
    // Inherit base constructors
    using base::base;

    // Expected has a narrow operator* and operator->
    constexpr void operator*() const { base::assume_value(); }
    constexpr void operator->() const { base::assume_value(); }
  };
  template <class E> using unexpected = OUTCOME_V2_NAMESPACE::failure_type<E>;
  template <class E> unexpected<E> make_unexpected(E &&arg) { return OUTCOME_V2_NAMESPACE::failure<E>(std::forward<E>(arg)); }
  template <class E, class... Args> unexpected<E> make_unexpected(Args &&... args) { return OUTCOME_V2_NAMESPACE::failure<E>(std::forward<Args>(args)...); }
  template <class E> using bad_expected_access = OUTCOME_V2_NAMESPACE::bad_result_access_with<E>;
  //! [expected_implementation]

  // Not actually part of the Expected proposal, but needed to pass the test
  template <typename T> using exception_or = expected<T, std::exception_ptr>;
}  // namespace stde

template <class T> using expected_sc = stde::expected<T, std::error_code>;

struct NoDefaultConstructible
{
  NoDefaultConstructible() = delete;
  NoDefaultConstructible(int /*unused*/) {}  // NOLINT
};

struct NoCopyConstructible
{
  NoCopyConstructible() = default;
  NoCopyConstructible(NoCopyConstructible const &) = delete;
  NoCopyConstructible(NoCopyConstructible &&) noexcept = default;
};
struct NoMoveConstructible
{
  NoMoveConstructible() = default;
  NoMoveConstructible(NoMoveConstructible const &) noexcept = default;
  NoMoveConstructible(NoMoveConstructible &&) = delete;
  NoMoveConstructible &operator=(NoMoveConstructible const &) noexcept = default;
  NoMoveConstructible &operator=(NoMoveConstructible &&) = delete;
};

enum State
{
  sDefaultConstructed,
  sValueCopyConstructed,
  sValueMoveConstructed,
  sCopyConstructed,
  sMoveConstructed,
  sMoveAssigned,
  sCopyAssigned,
  sValueCopyAssigned,
  sValueMoveAssigned,
  sMovedFrom,
  sValueConstructed
};

struct OracleVal
{
  State s{sValueConstructed};
  int i;
  constexpr OracleVal(int i_ = 0)  // NOLINT
  : i(i_)
  {
  }
};

struct Oracle
{
  State s{sDefaultConstructed};
  OracleVal val;

  Oracle() = default;
  Oracle(const OracleVal &v)  // NOLINT
  : s(sValueCopyConstructed),
    val(v)
  {
  }
  Oracle(OracleVal &&v) noexcept : s(sValueMoveConstructed), val(v) { v.s = sMovedFrom; }  // NOLINT
  Oracle(const Oracle &o)
      : s(sCopyConstructed)
      , val(o.val)
  {
  }
  Oracle(Oracle &&o) noexcept : s(sMoveConstructed), val(std::move(o.val)) { o.s = sMovedFrom; }  // NOLINT

  Oracle &operator=(const OracleVal &v)
  {
    s = sValueCopyConstructed;
    val = v;
    return *this;
  }
  Oracle &operator=(OracleVal &&v) noexcept
  {
    s = sValueMoveConstructed;
    val = std::move(v);  // NOLINT
    v.s = sMovedFrom;
    return *this;
  }
  Oracle &operator=(const Oracle &o)
  {
    s = sCopyConstructed;
    val = o.val;
    return *this;
  }
  Oracle &operator=(Oracle &&o) noexcept
  {
    s = sMoveConstructed;
    val = std::move(o.val);  // NOLINT
    o.s = sMovedFrom;
    return *this;
  }
};

struct Guard
{
  std::string val;
  Guard() = default;
  explicit Guard(std::string s, int /*unused*/ = 0)
      : val(std::move(s))
  {
  }
  Guard(const Guard &) = delete;
  Guard(Guard &&) = delete;
  void operator=(const Guard &) = delete;
  void operator=(Guard &&) = delete;
};

struct ExplicitStr
{
  std::string s;
  explicit ExplicitStr(const char *chp)
      : s(chp)
  {
  }
};

struct Date
{
  int i;
  Date() = delete;
  Date(int i_) noexcept : i{i_} {}  // NOLINT
  Date(Date &&d) noexcept : i(d.i) { d.i = 0; }
  Date(const Date &) = delete;
  Date &operator=(const Date &) = delete;
  Date &operator=(Date &&d) noexcept
  {
    i = d.i;
    d.i = 0;
    return *this;
  }
};

struct TExcept
{
  int i;
  TExcept() = delete;
  TExcept(int i_)  // NOLINT
  : i{i_}
  {
  }
  TExcept(TExcept &&d)
      : i(d.i)
  {
    d.i = 0;
  }
  TExcept(const TExcept &) = delete;
  TExcept &operator=(const TExcept &) = delete;
  TExcept &operator=(TExcept &&d)
  {
    i = d.i;
    d.i = 0;
    return *this;
  }
};

template <class T> struct MoveAware
{
  T val;
  bool moved;
  MoveAware(T val_)  // NOLINT
  : val(val_),
    moved(false)
  {
  }
  MoveAware(MoveAware const &) = delete;
  MoveAware(MoveAware &&rhs)
      : val(rhs.val)
      , moved(rhs.moved)
  {
    rhs.moved = true;
  }
  MoveAware &operator=(MoveAware const &) = delete;
  MoveAware &operator=(MoveAware &&rhs)
  {
    val = (rhs.val);
    moved = (rhs.moved);
    rhs.moved = true;
    return *this;
  }
};

struct OverloadedAddressOf
{
  OverloadedAddressOf() = default;
  OverloadedAddressOf *operator&() const { return nullptr; }
};

// using namespace boost;
// using namespace boost::functional;

class test_exception : public std::exception
{
};

int throwing_fun()
{
  throw test_exception();
}
int nothrowing_fun()
{
  return 4;
}

JASEL_NORETURN void void_throwing_fun()
{
  throw test_exception();
}
void do_nothing_fun()
{
}

void except_default_constructor()
{
  // From value constructor.
  expected_sc<int> e{};
  try
  {
    int i = e.value();
    (void) i;
    BOOST_TEST(true);
  }
  catch(...)
  {
    BOOST_TEST(false);
  };
  BOOST_TEST(e.has_value());
  BOOST_TEST(e);
  BOOST_TEST(static_cast<bool>(e));
}


void except_default_constructor_error_code()
{
  // From value constructor.
  stde::expected<int, std::error_code> e;
  BOOST_TEST(e.has_value());
  BOOST_TEST(e);
  BOOST_TEST(static_cast<bool>(e));
}

void except_default_constructor_constexpr()
{
  // From value constructor.
  BOOST_CONSTEXPR stde::expected<int, long> e;
  BOOST_TEST(e.has_value());
}

void expected_from_value()
{
  // using T = int;
  using E = std::error_code;

  // static_assert(noexcept(stde::adl::swap_impl(std::declval<T &>(), std::declval<T &>())), "");
  static_assert(std::is_nothrow_copy_constructible<E>::value, "");
  // static_assert(noexcept(stde::adl::swap_impl(std::declval<E &>(), std::declval<E &>())), "");

  static_assert(std::is_nothrow_copy_constructible<expected_sc<int>>::value, "");
  static_assert(std::is_nothrow_copy_assignable<expected_sc<int>>::value, "");
  static_assert(std::is_nothrow_move_constructible<expected_sc<int>>::value, "");
  static_assert(std::is_nothrow_move_assignable<expected_sc<int>>::value, "");

  // From value constructor.
  expected_sc<int> e(5);
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), 5);
  BOOST_TEST_EQ(*e, 5);
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

void expected_from_value2()
{
  // From value constructor.
  expected_sc<int> e(5);
  e = {};
  BOOST_TEST(e.has_value());
  BOOST_TEST_EQ(e.value(), 0);
}

void expected_from_cnv_value()
{
  OracleVal v;
  expected_sc<Oracle> e(v);
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST(!!e);
  BOOST_TEST(e.has_value());
  BOOST_TEST(bool(e));
  BOOST_TEST_EQ(e.value().s, sValueCopyConstructed);
  BOOST_TEST_EQ(v.s, sValueConstructed);

  expected_sc<Oracle> e2(std::move(v));  // NOLINT
  // BOOST_REQUIRE_NO_THROW(e2.value());
  BOOST_TEST(!!e2);
  BOOST_TEST(e2.has_value());
  BOOST_TEST(bool(e2));
  BOOST_TEST_EQ(e2.value().s, sValueMoveConstructed);
  BOOST_TEST_EQ(v.s, sMovedFrom);
}

struct NDCE  // no default constructor
{            // (no default date exists)
  explicit NDCE(int /*unused*/) {}
};

void except_constructor_NDCE()
{
  expected_sc<NDCE> e{NDCE{1}};
  BOOST_TEST(e.has_value());
}
struct NDC                // no default constructor
{                         // (no default date exists)
  NDC(int /*unused*/) {}  // NOLINT
};

void except_constructor_NDC()
{
  static_assert(std::is_nothrow_copy_constructible<expected_sc<NDC>>::value, "");
  static_assert(std::is_nothrow_copy_assignable<expected_sc<NDC>>::value, "");
  static_assert(std::is_nothrow_move_constructible<expected_sc<NDC>>::value, "");
  static_assert(std::is_nothrow_move_assignable<expected_sc<NDC>>::value, "");
  expected_sc<NDC> e{1};
  BOOST_TEST(e.has_value());
}

void except_constructor_Date()
{
  static_assert(std::is_nothrow_move_constructible<expected_sc<Date>>::value, "");
  static_assert(std::is_nothrow_move_assignable<expected_sc<Date>>::value, "");
  expected_sc<Date> e{Date{1}};
  BOOST_TEST(e.has_value());
}

void except_constructor_TExcept()
{
  static_assert(!std::is_nothrow_move_constructible<expected_sc<TExcept>>::value, "");
  static_assert(!std::is_nothrow_move_assignable<expected_sc<TExcept>>::value, "");
  expected_sc<TExcept> e{TExcept{1}};
  BOOST_TEST(e.has_value());
}

void expected_from_in_place_value()
{
  OracleVal v;
  expected_sc<Oracle> e{stde::in_place, v};
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST(!!e);
  BOOST_TEST(e.has_value());
  BOOST_TEST(bool(e));
  BOOST_TEST_EQ(e.value().s, sValueCopyConstructed);
  BOOST_TEST_EQ(v.s, sValueConstructed);

  expected_sc<Oracle> e2{stde::in_place, std::move(v)};  // NOLINT
  // BOOST_REQUIRE_NO_THROW(e2.value());
  BOOST_TEST(!!e2);
  BOOST_TEST(e2.has_value());
  BOOST_TEST(bool(e2));
  BOOST_TEST_EQ(e2.value().s, sValueMoveConstructed);
  BOOST_TEST_EQ(v.s, sMovedFrom);
}

#if 0
void expected_from_exception()
{
  // From stde::unexpected constructor.
  stde::exception_or<int> e(stde::make_unexpected(test_exception()));
  BOOST_TEST_THROWS(e.value(), test_exception);
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}
#endif

void expected_from_copy_value()
{
  // From copy constructor.
  expected_sc<int> ef(5);
  expected_sc<int> e(ef);
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), 5);
  BOOST_TEST_EQ(*e, 5);
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

#if 0
void expected_from_copy_exception()
{
  // From stde::unexpected constructor.
  stde::exception_or<int> ef(stde::make_unexpected(test_exception()));
  stde::exception_or<int> e(ef);
  BOOST_TEST_THROWS(e.value(), test_exception);
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}
#endif

void expected_from_in_place()
{
  // From stde::in_place constructor.
  expected_sc<std::string> e(stde::in_place, "stde::in_place");
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), "stde::in_place");
  BOOST_TEST_EQ(*e, "stde::in_place");
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

#if 0
void expected_from_exception_ptr()
{
  // From exception_ptr constructor.
  stde::exception_or<int> e(stde::make_unexpected(std::make_exception_ptr(test_exception())));
  BOOST_TEST_THROWS(e.value(), test_exception);
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}
#endif

void expected_from_moved_value()
{
  // From move value constructor.
  std::string value = "my value";
  expected_sc<std::string> e = std::move(value);
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), "my value");
  BOOST_TEST_EQ(*e, "my value");
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

void expected_from_catch_block()
{
  // From catch block
  try
  {
    throw test_exception();
  }
  catch(...)
  {
    stde::exception_or<int> e(stde::make_unexpected(std::current_exception()));

    BOOST_TEST_THROWS(e.value(), std::exception);
    BOOST_TEST_EQ(e.has_value(), false);
    BOOST_TEST_EQ(static_cast<bool>(e), false);
  }
}


void make_expected_E_from_value()
{
  // auto e = stde::make_expected<std::string>( 5 );
  // BOOST_TEST_EQ(e.has_value(), false);
}
void make_expected_const_from_value()
{
#if defined __clang__ && __clang_major__ >= 4 && __cplusplus > 201402L
  const int i = 0;
  auto e = expected_sc<const int>(i);
  (void) e;
// static_assert(std::is_same<decltype(e), stde::success<const int>>::value, "");
#endif
}
void make_expected_from_U_value()
{
  expected_sc<int> e = expected_sc<int>(short(5));
  static_assert(std::is_same<decltype(e), expected_sc<int>>{}, "");
  BOOST_TEST_EQ(e.has_value(), true);
}
void make_expected_from_U_value2()
{
  expected_sc<std::string> e = expected_sc<std::string>("aa");
  static_assert(std::is_same<decltype(e), expected_sc<std::string>>{}, "");
  BOOST_TEST_EQ(e.has_value(), true);
}

void expected_from_value_error_condition()
{
  // From value constructor.
  stde::expected<int, std::error_condition> e(5);
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), 5);
  BOOST_TEST_EQ(*e, 5);
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

void expected_from_error_error_condition()
{
  // From stde::unexpected constructor.
  stde::expected<int, std::error_condition> e(stde::make_unexpected<std::error_condition>(std::make_error_condition(std::errc::invalid_argument)));
  auto error_from_except_check = [](const stde::bad_expected_access<std::error_condition> &except) { return std::errc(except.error().value()) == std::errc::invalid_argument; };
  try
  {
    (void) e.value();
  }
  catch(stde::bad_expected_access<std::error_condition> &ex)
  {
    BOOST_TEST(error_from_except_check(ex));
  }
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}


void expected_from_error_convertible()
{
  {
    stde::expected<int, short> e1 = stde::make_unexpected<short>(1);
    stde::expected<int, long> e2(e1);
    BOOST_TEST_EQ(e2.has_value(), false);
    BOOST_TEST_EQ(static_cast<bool>(e2), false);
    BOOST_TEST_EQ(e2.error(), 1);
  }
  {
    stde::expected<void, short> e1 = stde::make_unexpected<short>(1);
    stde::expected<void, int> e2(e1);
    BOOST_TEST_EQ(e2.has_value(), false);
    BOOST_TEST_EQ(static_cast<bool>(e2), false);
    BOOST_TEST_EQ(e2.error(), 1);
  }
}

void except_valid_constexpr_int()
{
  // From value constructor.
  BOOST_CONSTEXPR stde::expected<int, long> e;
  BOOST_CONSTEXPR bool b = e.has_value();
  BOOST_TEST(b);
}
void except_value_constexpr_int()
{
  // From value constructor.
  BOOST_CONSTEXPR stde::expected<int, long> e(1);
  BOOST_CONSTEXPR int x = e.value();
  BOOST_TEST_EQ(x, 1);
}

void expected_from_value3()
{
  expected_sc<int> e(5);
  BOOST_TEST_EQ(e.value(), 5);

  // From value assignment.
  e = 8;
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), 8);
  BOOST_TEST_EQ(*e, 8);
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

void expected_from_copy_expected()
{
  expected_sc<int> e(5);
  expected_sc<int> e2(8);

  // From value assignment.
  e = e2;
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), 8);
  BOOST_TEST_EQ(*e, 8);
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

void expected_from_moved_expected()
{
  expected_sc<std::string> e("e");
  expected_sc<std::string> e2("e2");

  // From value assignment.
  e = std::move(e2);
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), "e2");
  BOOST_TEST_EQ(*e, "e2");
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));

// BOOST_REQUIRE_NO_THROW(e2.value());
#ifndef __GLIBCXX__
  BOOST_TEST_EQ(e2.value(), "");
  BOOST_TEST_EQ(*e2, "");
#endif
  BOOST_TEST(e2.has_value());
  BOOST_TEST(static_cast<bool>(e2));
}

void expected_from_in_place2()
{
  // From stde::in_place constructor.
  expected_sc<std::string> e(stde::in_place, "stde::in_place");
  BOOST_TEST_EQ(e.value(), "stde::in_place");

  // From emplace method.
  e.emplace("emplace method");
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), "emplace method");
  BOOST_TEST_EQ(*e, "emplace method");
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

void expected_from_move_value()
{
  expected_sc<std::string> e("v");

  std::string value = "my value";
  // From assignment operator.
  e = std::move(value);
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), "my value");
  BOOST_TEST_EQ(*e, "my value");
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}


void expected_from_in_place3()
{
  // From stde::in_place factory.
  // auto e = stde::make_expected<std::string>("stde::in_place");
  auto e = expected_sc<std::string>("stde::in_place");
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), "stde::in_place");
  BOOST_TEST_EQ(*e, "stde::in_place");
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}
void expected_from_in_place_error()
{
  // From stde::in_place factory.
  auto e = stde::expected<std::string, std::error_condition>("stde::in_place");
  // BOOST_REQUIRE_NO_THROW(e.value());
  BOOST_TEST_EQ(e.value(), "stde::in_place");
  BOOST_TEST_EQ(*e, "stde::in_place");
  BOOST_TEST(e.has_value());
  BOOST_TEST(static_cast<bool>(e));
}

void expected_from_exception_catch()
{
  // From catch block
  try
  {
    throw test_exception();
  }
  catch(...)
  {
    stde::exception_or<int> e = stde::make_unexpected(std::current_exception());

    BOOST_TEST_THROWS(e.value(), std::exception);
    BOOST_TEST_EQ(e.has_value(), false);
    BOOST_TEST_EQ(static_cast<bool>(e), false);
  }
}

#if 0
void expected_from_error()
{
  // From stde::unexpected constructor.
  auto e = stde::make_expected_from_error<int>(std::make_error_condition(std::errc::invalid_argument));
  auto error_from_except_check = [](const stde::bad_expected_access<std::error_condition> &except) { return std::errc(except.error().value()) == std::errc::invalid_argument; };
  try
  {
    (void) e.value();
  }
  catch(stde::bad_expected_access<std::error_condition> &ex)
  {
    BOOST_TEST(error_from_except_check(ex));
  }
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}

void expected_from_error_U()
{
  // From stde::unexpected constructor.
  auto e = stde::make_expected_from_error<int, short>(42);
  static_assert(std::is_same<decltype(e), stde::expected<int, short>>{}, "");
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}

void expected_from_exception2()
{
  // From stde::unexpected constructor.
  auto e = stde::make_expected_from_exception<int>(test_exception());
  // auto e = expected_sc<int>(stde::unexpected<>(test_exception()));
  BOOST_TEST_THROWS(e.value(),  test_exception );
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}

void expected_from_exception_ptr2()
{
  // From exception_ptr constructor.
  auto e = stde::exception_or<int>(stde::make_unexpected(test_exception()));
  BOOST_TEST_THROWS(e.value(),  test_exception );
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);
}

void make_expected_from_call_fun()
{
  try
  {
    stde::make_expected_from_call(throwing_fun);
    BOOST_TEST(true);
  }
  catch(...)
  {
    BOOST_TEST(false);
  }
  stde::exception_or<int> e = stde::make_expected_from_call(throwing_fun);
  BOOST_TEST_THROWS(e.value(),  std::exception );
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);

  e = stde::make_expected_from_call(nothrowing_fun);
  try
  {
    (void) e.value();
    BOOST_TEST(true);
  }
  catch(...)
  {
    BOOST_TEST(false);
  }
  BOOST_TEST_EQ(e.value(), 4);
  BOOST_TEST_EQ(*e, 4);
  BOOST_TEST_EQ(e.has_value(), true);
  BOOST_TEST_EQ(static_cast<bool>(e), true);

#if 0
  BOOST_TEST_THROWS(stde::make_expected_from_call<std::error_condition>(throwing_fun), test_exception);

  BOOST_TEST_NO_THROW(stde::make_expected_from_call<std::error_condition>(nothrowing_fun));
  stde::expected<int, std::error_condition> e2 = stde::make_expected_from_call<std::error_condition>(nothrowing_fun);
  BOOST_TEST_NO_THROW(e2.value());
  BOOST_TEST_EQ(e2.value(), 4);
  BOOST_TEST_EQ(*e2, 4);
  BOOST_TEST_EQ(e2.has_value(), true);
  BOOST_TEST_EQ(static_cast<bool>(e2), true);
#endif
}

void make_expected_from_call_void_fun()
{
#if 0
  BOOST_TEST_NO_THROW(stde::make_expected_from_call(void_throwing_fun));
  expected_sc<void> e = stde::make_expected_from_call(void_throwing_fun);
  BOOST_TEST_THROWS(e.value(), std::exception);
  BOOST_TEST_EQ(e.has_value(), false);
  BOOST_TEST_EQ(static_cast<bool>(e), false);

  e = stde::make_expected_from_call(do_nothing_fun);
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(e.has_value(), true);
  BOOST_TEST_EQ(static_cast<bool>(e), true);

  BOOST_TEST_THROWS(stde::make_expected_from_call<std::error_condition>(void_throwing_fun), test_exception);

  try {
    stde::make_expected_from_call<std::error_condition>(do_nothing_fun);
    BOOST_TEST(true);
  }
  catch (...)
  {
    BOOST_TEST(false);
  }
  stde::expected<void, std::error_condition> e2 = stde::make_expected_from_call<std::error_condition>(do_nothing_fun);
  try {
    (void)e2.value();
    BOOST_TEST(true);
  }
  catch (...)
  {
    BOOST_TEST(false);
  }
  //BOOST_TEST_NO_THROW(e2.value());
  BOOST_TEST_EQ(e2.has_value(), true);
  BOOST_TEST_EQ(static_cast<bool>(e2), true);
#endif
}
#endif

void expected_swap_value()
{
  // From value constructor.
  expected_sc<int> e(5);
  expected_sc<int> e2(8);

  e.swap(e2);

  BOOST_TEST_EQ(e.value(), 8);
  BOOST_TEST_EQ(e2.value(), 5);

  e2.swap(e);

  BOOST_TEST_EQ(e.value(), 5);
  BOOST_TEST_EQ(e2.value(), 8);
}

#if 0
void expected_swap_exception()
{
  // From value constructor.
  stde::exception_or<int> e = stde::make_unexpected(std::invalid_argument("e"));
  stde::exception_or<int> e2 = stde::make_unexpected(std::invalid_argument("e2"));

  e.swap(e2);

  auto equal_to_e = [](const std::invalid_argument &except) { return std::string(except.what()) == "e"; };
  auto equal_to_e2 = [](const std::invalid_argument &except) { return std::string(except.what()) == "e2"; };

  try
  {
    (void) e.value();
    BOOST_TEST(true);
  }
  catch(std::invalid_argument &ex)
  {
    BOOST_TEST(equal_to_e2(ex));
  }
  try
  {
    (void) e2.value();
    BOOST_TEST(true);
  }
  catch(std::invalid_argument &ex)
  {
    BOOST_TEST(equal_to_e(ex));
  }

  e2.swap(e);

  try
  {
    (void) e.value();
    BOOST_TEST(true);
  }
  catch(std::invalid_argument &ex)
  {
    BOOST_TEST(equal_to_e(ex));
  }
  try
  {
    (void) e2.value();
    BOOST_TEST(true);
  }
  catch(std::invalid_argument &ex)
  {
    BOOST_TEST(equal_to_e2(ex));
  }
}
#endif

void expected_swap_function_value()
{
  // From value constructor.
  expected_sc<int> e(5);
  expected_sc<int> e2(8);

  swap(e, e2);

  BOOST_TEST_EQ(e.value(), 8);
  BOOST_TEST_EQ(e2.value(), 5);

  swap(e, e2);

  BOOST_TEST_EQ(e.value(), 5);
  BOOST_TEST_EQ(e2.value(), 8);
}


#ifdef QUICKCPPLIB_BOOST_UNIT_TEST_HPP
int main()
#else
BOOST_AUTO_TEST_CASE(expected_pass)
#endif
{

  static_assert(!std::is_default_constructible<NoDefaultConstructible>::value, "");
  static_assert(!std::is_default_constructible<expected_sc<NoDefaultConstructible>>::value, "");

  static_assert(!std::is_copy_constructible<NoCopyConstructible>::value, "");
  static_assert(!std::is_constructible<expected_sc<NoCopyConstructible>, NoCopyConstructible const &>::value, "");
  static_assert(!std::is_constructible<stde::exception_or<NoCopyConstructible>, stde::exception_or<NoCopyConstructible> const &>::value, "");
  static_assert(!std::is_copy_constructible<stde::exception_or<NoCopyConstructible>>::value, "");

#if 0
  // fixme
  {
    NoMoveConstructible nmc;
    // NoMoveConstructible nmc2 = std::move(nmc); // FAILS as expected

    expected_sc<NoMoveConstructible> x{std::move(nmc)};  // DOESN'T FAIL as copy is selected instead
    (void) x;
  }
// fixme
#if defined __clang__ && __clang_major__ >= 4 && __cplusplus > 201402L
  {
    NoMoveConstructible nmc;
    // NoMoveConstructible nmc2 = std::move(nmc); // FAILS as expected

    expected_sc<NoMoveConstructible> x = std::move(nmc);  // DOESN'T FAIL as copy is selected instead
    (void) x;
  }
#endif
#endif

  static_assert(!std::is_move_constructible<NoMoveConstructible>::value, "");
  static_assert(!std::is_constructible<expected_sc<NoMoveConstructible>, NoMoveConstructible &&>::value, "");
  static_assert(std::is_move_constructible<expected_sc<NoMoveConstructible>>::value, "");

  except_default_constructor();
  except_default_constructor_error_code();
  except_default_constructor_constexpr();
  expected_from_value();
  expected_from_value2();
  expected_from_cnv_value();
  except_constructor_NDCE();
  except_constructor_NDC();
  except_constructor_Date();
  expected_from_in_place_value();
  // expected_from_exception();
  expected_from_copy_value();
  // expected_from_copy_exception();
  expected_from_in_place();
  // expected_from_exception_ptr();
  expected_from_moved_value();
  expected_from_catch_block();
  make_expected_E_from_value();
  make_expected_const_from_value();
  make_expected_from_U_value2();
  expected_from_value_error_condition();
  expected_from_error_error_condition();
  expected_from_error_convertible();
  except_valid_constexpr_int();
  except_value_constexpr_int();
  expected_from_value3();
  expected_from_copy_expected();
  expected_from_moved_expected();
  expected_from_in_place2();
  expected_from_move_value();
  expected_from_in_place3();
  expected_from_in_place_error();
  expected_from_exception_catch();
  // expected_from_error();
  // expected_from_error_U();
  // expected_from_exception2();
  // expected_from_exception_ptr2();
  // make_expected_from_call_fun();
  // make_expected_from_call_void_fun();
  expected_swap_value();
  // expected_swap_exception();
  expected_swap_function_value();

#ifdef QUICKCPPLIB_BOOST_UNIT_TEST_HPP
  return QUICKCPPLIB_NAMESPACE::unit_test::current_test_case()->fails != 0;
#endif
}

#if 0
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////


//void expected_from_error_catch_exception)
//{
//  // From catch block
//  try
//  {
//    throw test_exception();
//  }
//  catch(...)
//  {
//    auto throw_lambda = [](){ return stde::make_expected_from_error<int,std::error_condition>();};
//
//    //BOOST_TEST_THROWS(throw_lambda(), test_exception);
//  }
//}



////////////////////////////////////
BOOST_AUTO_TEST_SUITE(expected_map)

void expected_map()
{
  auto fun = [](bool b) -> expected_sc<int>
  {
    if (b)
      return stde::make_expected(5);
    else
      return stde::make_unexpected(test_exception());
  };

  auto add_five = [](int sum) -> int
  {
    return sum + 5;
  };

  auto launch_except = [](int sum) -> int
  {
    throw test_exception();
  };

  expected_sc<int> e = fun(true).map(add_five);
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 10);

  e = fun(true).map(add_five).map(add_five);
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 15);

  e = fun(false).map(add_five).map(add_five);
  BOOST_TEST_THROWS(e.value(), test_exception);

  BOOST_TEST_THROWS(fun(true).map(launch_except), test_exception);

}

void expected_void_map()
{
  auto fun = [](bool b)
  {
    if (b)
      return stde::make_expected();
    else
      return expected_sc<void>(stde::make_unexpected(test_exception()));
  };

  auto launch_except = []() -> void
  {
    throw test_exception();
  };

  auto do_nothing = []() {};

  expected_sc<void> e = fun(true).map(do_nothing);
  BOOST_TEST_NO_THROW(e.value());

  e = fun(false).map(do_nothing);
  BOOST_TEST_THROWS(e.value(), test_exception);

  BOOST_TEST_THROWS(fun(true).map(launch_except), test_exception);

}

BOOST_AUTO_TEST_SUITE_END()

////////////////////////////////////
BOOST_AUTO_TEST_SUITE(expected_bind)

void expected_bind()
{
  auto fun = [](bool b) -> expected_sc<int>
  {
    if (b)
      return stde::make_expected(5);
    else
      return stde::make_unexpected(test_exception());
  };

  auto add_five = [](int sum) -> expected_sc<int>
  {
    return stde::make_expected(sum + 5);
  };

  auto launch_except = [](int sum) -> expected_sc<int>
  {
    throw test_exception();
  };

  expected_sc<int> e = fun(true).bind(add_five);
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 10);

  e = fun(true).bind(add_five).bind(add_five);
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 15);

  e = fun(false).bind(add_five).bind(add_five);
  BOOST_TEST_THROWS(e.value(), test_exception);

  BOOST_TEST_THROWS(fun(true).bind(launch_except), test_exception);

}

void expected_void_bind()
{
  auto fun = [](bool b)
  {
    if (b)
      return stde::make_expected();
    else
      return expected_sc<void>(stde::make_unexpected(test_exception()));
  };

  auto launch_except = []() -> expected_sc<void>
  {
    throw test_exception();
  };

  auto do_nothing = []() {
    return stde::make_expected();
  };

  expected_sc<void> e = fun(true).bind(do_nothing);
  BOOST_TEST_NO_THROW(e.value());

  e = fun(false).bind(do_nothing);
  BOOST_TEST_THROWS(e.value(), test_exception);

  BOOST_TEST_THROWS(fun(true).bind(launch_except), test_exception);

}

BOOST_AUTO_TEST_SUITE_END()


////////////////////////////////////
BOOST_AUTO_TEST_SUITE(expected_then)

void expected_non_void_then()
{
  auto fun = [](bool b) -> expected_sc<int>
  {
    if (b)
      return stde::make_expected(5);
    else
      return stde::make_unexpected(test_exception());
  };


  auto add_five = [](int sum) -> int
  {
    return sum + 5;
  };
  auto six = []() -> int
  {
    return 6;
  };

  auto pair = [](int a) -> bool
  {
    return (a % 2) == 0;
  };

  auto launch_except = [](int sum) -> int
  {
    throw test_exception();
  };

  auto then_launch_except = [](expected<int>) -> int
  {
    throw test_exception();
  };

  expected_sc<int> e = fun(true).then(if_valued(add_five));
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 10);

  e = fun(true).then(if_valued(ident(six)));
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 6);

  e = fun(false).then(if_unexpected(ident(six)));
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 6);

  expected_sc<bool> e1 = fun(true).then(if_valued(pair));
  BOOST_TEST_NO_THROW(e1.value());
  BOOST_TEST_EQ(*e1, false);



  e = fun(true).then(if_valued(add_five)).then(if_valued(add_five));
  BOOST_TEST_NO_THROW(e.value());
  BOOST_TEST_EQ(*e, 15);

  e = fun(false).then(if_valued(add_five)).then(if_valued(add_five));
  BOOST_TEST_THROWS(e.value(), test_exception);

  BOOST_TEST_THROWS(fun(true).then(if_valued(launch_except)), test_exception);

  e = fun(false).then(catch_all(then_launch_except));
  BOOST_TEST_THROWS(e.value(), test_exception);

}

void expected_void_then()
{
  auto fun = [](bool b) -> expected_sc<void>
  {
    if (b)
      return stde::make_expected();
    else
      return stde::make_unexpected(test_exception());
  };

  auto launch_except = []()
  {
    throw test_exception();
  };

  auto do_nothing = []() {};

  BOOST_TEST(true);
  expected_sc<void> e = fun(true).then(if_valued(do_nothing));
  BOOST_TEST_NO_THROW(e.value());

  e = fun(false).then(if_valued(do_nothing));
  BOOST_TEST_THROWS(e.value(), test_exception);

  BOOST_TEST_THROWS(fun(true).then(if_valued(launch_except)), test_exception);

}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(expected_recover)

void expected_recover()
{
  auto fun = [](bool b)
  {
    if (b)
      return expected_sc<int>(5);
    else
      return expected_sc<int>(stde::make_unexpected(test_exception()));
  };

  auto add_five = [](int sum) -> expected_sc<int>
  {
    return stde::make_expected(sum + 5);
  };

  auto recover_error = [](std::exception_ptr p)
  {
    return stde::make_expected(0);
  };

  auto recover_error_silent_failure = [](std::exception_ptr p)
  {
    return expected_sc<int>(stde::make_unexpected(p));
  };

  auto recover_error_failure = [](std::exception_ptr p) -> expected_sc<int>
  {
    return expected_sc<int>(stde::make_unexpected(test_exception()));
  };

  auto recover_error_throws = [](std::exception_ptr p) -> expected_sc<int>
  {
    throw test_exception();
  };

  BOOST_TEST_EQ(fun(false).catch_error(recover_error).has_value(), true);
  BOOST_TEST_EQ(fun(false).catch_error(recover_error).value(), 0);
  BOOST_TEST_EQ(fun(true).catch_error(recover_error).value(), 5);
  BOOST_TEST_EQ(fun(false).catch_error(recover_error_silent_failure).has_value(), false);
  BOOST_TEST_EQ(fun(false).catch_error(recover_error_failure).has_value(), false);

  BOOST_TEST_EQ(fun(true).bind(add_five).value(), 10);
  BOOST_TEST_EQ(fun(true).bind(add_five).catch_error(recover_error).value(), 10);
  BOOST_TEST_EQ(fun(true).bind(add_five).catch_error(recover_error_silent_failure).value(), 10);
  BOOST_TEST_EQ(fun(true).bind(add_five).catch_error(recover_error_failure).value(), 10);

  BOOST_TEST_EQ(fun(false).catch_error(recover_error).bind(add_five).value(), 5);
  BOOST_TEST_EQ(fun(false).catch_error(recover_error).bind(add_five).bind(add_five).value(), 10);
  BOOST_TEST_EQ(fun(false).catch_error(recover_error_failure).bind(add_five).has_value(), false);
  BOOST_TEST_EQ(fun(false).bind(add_five).catch_error(recover_error_failure).bind(add_five).has_value(), false);
  BOOST_TEST_EQ(fun(false).bind(add_five).catch_error(recover_error_silent_failure).bind(add_five).has_value(), false);

  BOOST_TEST_THROWS(fun(false).catch_error(recover_error_throws), test_exception);

}

void expected_void_recover()
{
  auto fun = [](bool b)
  {
    if (b)
      return stde::make_expected();
    else
      return expected_sc<void>(boost::stde::make_unexpected(test_exception()));
  };

  auto do_nothing = []() {
    return stde::make_expected();
  };

  auto recover_error = [](std::exception_ptr p)
  {
    return stde::make_expected();
  };

  auto recover_error_silent_failure = [](std::exception_ptr p)
  {
    return expected_sc<void>(boost::stde::make_unexpected(p));
  };

  auto recover_error_failure = [](std::exception_ptr p) -> expected_sc<void>
  {
    throw test_exception();
  };

  // The catch_error doesn't alter the stde::expected if it's valid.
  BOOST_TEST_EQ(fun(true).catch_error(recover_error_failure).has_value(), true);

  // Simple catch_error tests.
  BOOST_TEST_EQ(fun(false).catch_error(recover_error).has_value(), true);
  BOOST_TEST_THROWS(fun(false).catch_error(recover_error_failure), test_exception);
  BOOST_TEST_EQ(fun(false).catch_error(recover_error_silent_failure).has_value(), false);

  // With a bind between.
  BOOST_TEST_THROWS(fun(false).bind(do_nothing).catch_error(recover_error_failure), test_exception);

}
BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(proposal)

void proposal_concept()
{
  using namespace std;

  {
    stde::expected<int, string> ei = 0;
    stde::expected<int, string> ej = 1;
    stde::expected<int, string> ek = stde::make_unexpected(string());

    ei = 1;
    ej = stde::make_unexpected(string());;
    ek = 0;

    ei = stde::make_unexpected(string());;
    ej = 0;
    ek = 1;
  }
}
void proposal_init()
{
  using namespace std;
  {
    string s{ "STR" };

    stde::expected<string, int> ep{ stde::make_unexpected(-1) };              // unexpected value, requires Movable<E>
    stde::expected<string, int> eq = { stde::make_unexpected(-1) };           // unexpected value, requires Movable<E>

    expected_sc<string> es{ s };                    // requires Copyable<T>
    expected_sc<string> et = s;                   // requires Copyable<T>
    expected_sc<string> ev = string{ "STR" };       // requires Movable<T>

    expected_sc<string> ew;                       // unexpected value
    expected_sc<string> ex{};                     // unexpected value
    expected_sc<string> ey = {};                  // unexpected value
    expected_sc<string> ez = expected_sc<string>{};  // unexpected value
  }

  {
    stde::expected<Guard, int> eg;                        // unexpected value
    stde::expected<Guard, int> eh{};                      // unexpected value
    stde::expected<Guard, int> ei{ stde::in_place };               // calls Guard{} in place
    stde::expected<Guard, int> ej{ stde::in_place, "arg" };        // calls Guard{"arg"} in place
  }

  {
    stde::expected<int, string> ei{ unexpect };               // unexpected value, calls string{} in place
    stde::expected<int, string> ej{ unexpect, "arg" };        // unexpected value, calls string{"arg"} in place
  }
}
void proposal_make_unexpected_fact()
{
  using namespace std;
  {
    stde::expected<string, int> opt1 = stde::make_unexpected(1);
    stde::expected<string, int> opt2 = { unexpect, 1 };

    opt1 = stde::make_unexpected(1);
    opt2 = { unexpect, 1 };
  }
}
void proposal_error_exception_ts()
{
  using namespace std;
  {
    stde::expected<int, error_exception<std::error_code, std::system_error> > e =
      stde::make_unexpected(make_error_code(errc::invalid_argument));
#if !defined BOOST_MSVC || BOOST_MSVC >= 1900
    BOOST_TEST(e.error() == make_error_code(errc::invalid_argument));
#else
    // VS2013 doesn't match operator==(boost::error_exception<std::error_code,std::system_error>, std::error_code)
    BOOST_TEST(e.error() == (error_exception<std::error_code, std::system_error>(make_error_code(errc::invalid_argument))));
#endif
    try {
      e.value();
      BOOST_TEST(false);
    }
    catch (std::system_error const& ex) {

    }
    catch (...) {
      BOOST_TEST(false);
    }
    stde::expected<int, error_exception<std::error_code, std::system_error> > e2 = stde::make_unexpected(e.error());
#if !defined BOOST_MSVC || BOOST_MSVC >= 1900
    BOOST_TEST(e2.error() == make_error_code(errc::invalid_argument));
#else
    // VS2013 doesn't match operator==(boost::error_exception<std::error_code,std::system_error>, std::error_code)
    BOOST_TEST(e2.error() == (error_exception<std::error_code, std::system_error>(make_error_code(errc::invalid_argument))));
#endif
    try {
      e2.value();
      BOOST_TEST(false);
    }
    catch (std::system_error const& ex) {

    }
    catch (...) {
      BOOST_TEST(false);
    }

  }
}
void proposal_ensured_read_ts()
{
  using namespace std;
  {
    ensured_read<int> e = make_ensured_read(1);
    BOOST_TEST(e == 1);
  }
  {
    ensured_read<int> e = make_ensured_read(1);
    stde::unexpected<ensured_read<int>> ue1 = stde::make_unexpected(std::move(e));
    BOOST_TEST(ue1.value() == 1);
  }
  //  {
  //    stde::make_unexpected(make_ensured_read(1));
  //    // calls to terminate.
  //  }
  //  {
  //    stde::expected<int, ensured_read<int> > e = stde::make_unexpected(make_ensured_read(1));
  //    // calls to terminate.
  //  }
  {
    stde::expected<int, ensured_read<int> > e{ 1 };
    BOOST_TEST(e.value() == 1);
  }
  {
    stde::expected<int, ensured_read<int> > e = stde::make_unexpected(make_ensured_read(1));
    BOOST_TEST(e.error() == 1);
  }
  {
    stde::expected<int, ensured_read<int> > e{ unexpect, 1 };
    BOOST_TEST(e.error() == 1);
  }
  {
    ensured_read<std::exception_ptr> e = make_ensured_read(std::make_exception_ptr(1));
    BOOST_TEST_THROWS(std::rethrow_exception(e.value()), int);
  }
  {
    stde::expected<int, ensured_read<std::exception_ptr> > e = stde::make_unexpected(make_ensured_read(std::make_exception_ptr(1)));
    BOOST_TEST_THROWS(std::rethrow_exception(e.error().value()), int);
  }
}
void proposal_relational_operators()
{
  using namespace std;
  {
    stde::expected<unsigned, int> e0{ 0 };
    stde::expected<unsigned, int> e1{ 1 };
    stde::expected<unsigned, int> eN{ unexpect, -1 };

    BOOST_TEST(eN < e0);
    BOOST_TEST(e0 < e1);
    BOOST_TEST(!(e0 < eN));
    BOOST_TEST(eN <= e0);
    BOOST_TEST(e0 <= e1);

    BOOST_TEST(e0 > eN);
    BOOST_TEST(e1 > e0);
    BOOST_TEST(e0 >= eN);
    BOOST_TEST(e1 >= e0);

    BOOST_TEST(!(eN  < eN));
    BOOST_TEST(!(e1 < e1));
    BOOST_TEST(eN <= eN);
    BOOST_TEST(e1 <= e1);

    BOOST_TEST(eN != e0);
    BOOST_TEST(e0 != e1);
    BOOST_TEST(eN == eN);
    BOOST_TEST(e0 == e0);

    //////

    BOOST_TEST(eN == stde::make_unexpected(-1));
    BOOST_TEST(e0 != stde::make_unexpected(1));
    BOOST_TEST(eN != 1u);
    BOOST_TEST(e1 == 1u);

    BOOST_TEST(eN < 1u);
    BOOST_TEST(eN <= 1u);
    BOOST_TEST(1u > eN);
    BOOST_TEST(!(1u < eN));
    BOOST_TEST(1u >= eN);
    BOOST_TEST(stde::make_unexpected(1) < e0);
    BOOST_TEST(stde::make_unexpected(1) <= e0);
    BOOST_TEST(!(stde::make_unexpected(1) > e0));
    BOOST_TEST(!(stde::make_unexpected(1) >= e0));


    BOOST_TEST(!(e0 < stde::make_unexpected(1)));
    BOOST_TEST(!(e0 <= stde::make_unexpected(1)));
    BOOST_TEST(e0 > stde::make_unexpected(1));
    BOOST_TEST(e0 >= stde::make_unexpected(1));
  }
  {
    stde::expected<void, int> e0{ boost::expect };
    stde::expected<void, int> eN{ unexpect, -1 };

    BOOST_TEST(!(e0 < e0));
    BOOST_TEST(eN < e0);
    BOOST_TEST(!(e0 < eN));
    BOOST_TEST(!(eN < eN));
    BOOST_TEST(e0 <= e0);
    BOOST_TEST(eN <= e0);
    BOOST_TEST(!(e0 <= eN));
    BOOST_TEST(eN <= eN);

    BOOST_TEST(!(e0 > e0));
    BOOST_TEST(e0 > eN);
    BOOST_TEST(!(eN > e0));
    BOOST_TEST(!(eN > eN));
    BOOST_TEST(e0 >= e0);
    BOOST_TEST(e0 >= eN);
    BOOST_TEST(!(eN >= e0));
    BOOST_TEST(eN >= eN);

    BOOST_TEST(!(e0 != e0));
    BOOST_TEST(eN != e0);
    BOOST_TEST(e0 != eN);
    BOOST_TEST(!(eN != eN));
    BOOST_TEST(e0 == e0);
    BOOST_TEST(!(eN == e0));
    BOOST_TEST(!(e0 == eN));
    BOOST_TEST(eN == eN);

    //////

    BOOST_TEST(eN == stde::make_unexpected(-1));
    BOOST_TEST(e0 != stde::make_unexpected(1));

    BOOST_TEST(stde::make_unexpected(1) < e0);
    BOOST_TEST(stde::make_unexpected(1) <= e0);
    BOOST_TEST(!(stde::make_unexpected(1) > e0));
    BOOST_TEST(!(stde::make_unexpected(1) >= e0));

    BOOST_TEST(!(stde::make_unexpected(1) < eN));
    BOOST_TEST(!(stde::make_unexpected(1) <= eN));
    BOOST_TEST(stde::make_unexpected(1) > eN);
    BOOST_TEST(stde::make_unexpected(1) >= eN);

    BOOST_TEST(!(eN < stde::make_unexpected(-1)));
    BOOST_TEST(eN <= stde::make_unexpected(-1));
    BOOST_TEST(!(eN > stde::make_unexpected(-1)));
    BOOST_TEST(eN >= stde::make_unexpected(-1));
  }
}
void proposal_dereference_operators()
{
  using namespace std;
  {
    const string s{ "STR" };

    expected_sc<string> e0{ s };
    const expected_sc<string> e1{ s };
    BOOST_TEST(*e0.operator->() == s);
    BOOST_TEST(*e1.operator->() == s);

    // Test with class which has operator&() overloaded
    const OverloadedAddressOf o{};
    BOOST_TEST(&o == nullptr);

    expected_sc<OverloadedAddressOf> e2{ o };
    const expected_sc<OverloadedAddressOf> e3{ o };
    BOOST_TEST(e2.operator->() != nullptr);
    BOOST_TEST(e3.operator->() != nullptr);
  }
}
BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(movesem)
//////////////////////////////
void movesem_moved_from_state()
{
  // first, test mock:
  MoveAware<int> i{ 1 }, j{ 2 };
  BOOST_TEST(i.val == 1);
  BOOST_TEST(!i.moved);
  BOOST_TEST(j.val == 2);
  BOOST_TEST(!j.moved);

  MoveAware<int> k = std::move(i);
  BOOST_TEST(k.val == 1);
  BOOST_TEST(!k.moved);
  BOOST_TEST(i.val == 1);
  BOOST_TEST(i.moved);

  k = std::move(j);
  BOOST_TEST(k.val == 2);
  BOOST_TEST(!k.moved);
  BOOST_TEST(j.val == 2);
  BOOST_TEST(j.moved);

  // now, test stde::expected
  expected_sc<MoveAware<int>> oi{ 1 }, oj{ 2 };
  BOOST_TEST(oi);
  BOOST_TEST(!oi->moved);
  BOOST_TEST(oj);
  BOOST_TEST(!oj->moved);

  expected_sc<MoveAware<int>> ok{ std::move(oi) };
  BOOST_TEST(ok);
  BOOST_TEST(!ok->moved);
  BOOST_TEST(oi);
  BOOST_TEST(oi->moved);

  ok = std::move(oj);
  BOOST_TEST(ok);
  BOOST_TEST(!ok->moved);
  BOOST_TEST(oj);
  BOOST_TEST(oj->moved);

}
void movesem_move_only_value()
{
  const auto make_int = []() {
    std::unique_ptr<int> value{ new int };
    *value = 100;
    return value;
  };
  const auto return_void = [](std::unique_ptr<int> value) {
    BOOST_TEST(value != nullptr);
    BOOST_TEST(*value == 100);
  };
  const auto return_expected = [](std::unique_ptr<int> value) {
    BOOST_TEST(value != nullptr);
    BOOST_TEST(*value == 100);
    return expected_sc<void>{boost::expect};
  };
  const auto return_int = [](std::unique_ptr<int> value) {
    BOOST_TEST(value != nullptr);
    BOOST_TEST(*value == 100);
    return 200;
  };

  BOOST_TEST(expected<std::unique_ptr<int>>{make_int()}.map(return_void));
  BOOST_TEST(expected<std::unique_ptr<int>>{make_int()}.map(return_expected));
  BOOST_TEST(expected<std::unique_ptr<int>>{make_int()}.map(return_int));
}
void movesem_move_only_value2()
{
  const auto make_int = []() {
    std::unique_ptr<int> value{ new int };
    *value = 100;
    return value;
  };
  const auto return_expected_void = [](std::unique_ptr<int> value) {
    BOOST_TEST(value != nullptr);
    BOOST_TEST(*value == 100);
    return stde::make_expected();
  };
  const auto return_expected = [](std::unique_ptr<int> value) {
    BOOST_TEST(value != nullptr);
    BOOST_TEST(*value == 100);
    return expected_sc<void>{boost::expect};
  };
  BOOST_TEST(expected<std::unique_ptr<int>>{make_int()}.bind(return_expected_void));
  BOOST_TEST(expected<std::unique_ptr<int>>{make_int()}.bind(return_expected));
}
void movesem_copy_move_ctor_optional_int()
{
  expected_sc<int> oi;
  expected_sc<int> oj = oi;

  BOOST_TEST(oj);
  BOOST_TEST(oj == oi);
  BOOST_TEST(bool(oj));

  oi = 1;
  expected_sc<int> ok = oi;
  BOOST_TEST(!!ok);
  BOOST_TEST(bool(ok));
  BOOST_TEST(ok == oi);
  BOOST_TEST(ok != oj);
  BOOST_TEST(*ok == 1);

  expected_sc<int> ol = std::move(oi);
  BOOST_TEST(!!ol);
  BOOST_TEST(bool(ol));
  BOOST_TEST(ol == oi);
  BOOST_TEST(ol != oj);
  BOOST_TEST(*ol == 1);
}
void movesem_expected_expected()
{
  expected_sc<stde::expected<int, int>> oi1 = stde::make_unexpected(-1);
  BOOST_TEST(!oi1);

  {
    expected_sc<expected_sc<int>> oi2{ stde::expect };
    BOOST_TEST(bool(oi2));
    BOOST_TEST((*oi2));
    //std::cout << typeid(**oi2).name() << std::endl;
  }

  {
    expected_sc<stde::expected<int, int>> oi2{ stde::expect, stde::make_unexpected(-1) };
    BOOST_TEST(bool(oi2));
    BOOST_TEST(!*oi2);
  }

  {
    stde::expected<stde::expected<int>> oi2{ stde::expected<int>{} };
    BOOST_TEST(bool(oi2));
    BOOST_TEST(*oi2);
  }

  stde::expected<int> oi;
  auto ooi = stde::make_expected(oi);
  static_assert(std::is_same<expected<expected<int>>, decltype(ooi)>::value, "");

}
BOOST_AUTO_TEST_SUITE_END()

void process() {}
void process(int) {}
void processNil() {}

BOOST_AUTO_TEST_SUITE(Examples)
//////////////////////////////

void example1()
{
  stde::expected<int> oi; // create disengaged object
  stde::expected<int> oj = { unexpect }; // alternative syntax
  oi = oj; // assign disengaged object
  stde::expected<int> ok = oj; // ok is disengaged

  if (oi) BOOST_TEST(false); // 'if oi is engaged...'
  if (!oi) BOOST_TEST(true); // 'if oi is disengaged...'

  BOOST_TEST(oi == ok); // two disengaged optionals compare equal

                        ///////////////////////////////////////////////////////////////////////////
  stde::expected<int> ol{ 1 }; // ol is engaged; its contained value is 1
  ok = 2; // ok becomes engaged; its contained value is 2
  oj = ol; // oj becomes engaged; its contained value is 1

  BOOST_TEST(oi != ol); // disengaged != engaged
  BOOST_TEST(ok != ol); // different contained values
  BOOST_TEST(oj == ol); // same contained value
                        //BOOST_TEST(oi < ol); // disengaged < engaged
                        //BOOST_TEST(ol < ok); // less by contained value

                        /////////////////////////////////////////////////////////////////////////////
  stde::expected<int> om{ 1 }; // om is engaged; its contained value is 1
  stde::expected<int> on = om; // on is engaged; its contained value is 1
  om = 2; // om is engaged; its contained value is 2
  BOOST_TEST(on != om); // on still contains 3. They are not pointers

                        /////////////////////////////////////////////////////////////////////////////
  int i = *ol; // i obtains the value contained in ol
  BOOST_TEST(i == 1);
  *ol = 9; // the object contained in ol becomes 9
  BOOST_TEST(*ol == 9);
  BOOST_TEST(ol == stde::make_expected(9));

  ///////////////////////////////////
  int p = 1;
  stde::expected<int> op = p;
  BOOST_TEST(*op == 1);
  p = 2;
  BOOST_TEST(*op == 1); // value contained in op is separated from p

                        ////////////////////////////////
  if (ol)
    process(*ol); // use contained value if present
  else
    process(); // proceed without contained value

  if (!om)
    processNil();
  else
    process(*om);

  /////////////////////////////////////////
  process(ol.value_or(0)); // use 0 if ol is disengaged

                           ////////////////////////////////////////////
  ok = { unexpect }; // if ok was engaged calls T's dtor
  oj = {}; // assigns a temporary disengaged stde::expected
}
//////////////////////////////////////////////////
void ValueOr()
{
  stde::expected<int> oi = 1;
  int i = oi.value_or(0);
  BOOST_TEST(i == 1);

  oi = { unexpect };
  BOOST_TEST(oi.value_or(3) == 3);

  stde::expected<std::string> os{ "AAA" };
  BOOST_TEST(os.value_or("BBB") == "AAA");
  os = {};
  BOOST_TEST(os);
  BOOST_TEST(os.value() == "");

  BOOST_TEST(os.value_or(std::string("BBB")) == "");
  {
    constexpr stde::expected<int> e = 1;
    static_assert(e.has_value(), "");
    static_assert(*e == 1, "");
    static_assert(e.value() == 1, "");
  }
  {
    constexpr std::error_code ec = std::make_error_code(std::errc(1));
    constexpr stde::expected<int> e = stde::make_unexpected(ec);
    static_assert(!e.has_value(), "");
    static_assert(e.error() == ec, "");
  }
  {
    constexpr stde::expected<OracleVal> e = 1;
    static_assert(e.has_value(), "");
    static_assert(*e == 1, "");
    static_assert(e.value() == 1, "");
  }
  {
    constexpr std::error_code ec = std::make_error_code(std::errc(1));
    constexpr stde::expected<OracleVal> e = stde::make_unexpected(ec);
    static_assert(!e.has_value(), "");
    static_assert(e.error() == ec, "");
  }
}

//////////////////////////////////////////////////
BOOST_AUTO_TEST_SUITE_END()

#endif

#else
int main(void)
{
  return 0;
}
#endif
