// This version targets C++11 and later.
//
// Copyright (c) 2016-2018 Martin Moene.
//
// 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)
//
// expected lite is based on:
//   A proposal to add a utility class to represent expected monad
//   by Vicente J. Botet Escriba and Pierre Talbot, http:://wg21.link/p0323

#include <cassert>
#include "expected-main.t.hpp"

#ifndef nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS
#define nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS  0
#endif

#define nsel_HAVE_MAYBE_UNUSED  nsel_CPP17_000

#if nsel_HAVE_MAYBE_UNUSED
# define nsel_MAYBE_UNUSED  [[maybe_unused]]
#else
# define nsel_MAYBE_UNUSED  /*[[maybe_unused]]*/
#endif

// Suppress:
// - unused parameter, for cases without assertions such as [.std...]
#if defined(__clang__)
# pragma clang diagnostic ignored "-Wunused-parameter"
#elif defined __GNUC__
# pragma GCC   diagnostic ignored "-Wunused-parameter"
#endif

//namespace {

using namespace nonstd;

struct Implicit { int x;          Implicit(int v) : x(v) {} };
struct Explicit { int x; explicit Explicit(int v) : x(v) {} };

struct MoveOnly
{
    int x;
    explicit MoveOnly(int x) :x{x} {}

    MoveOnly( MoveOnly const &  ) = delete;
    MoveOnly( MoveOnly && other ) noexcept : x{ other.x } {}

    MoveOnly& operator=( MoveOnly const &  ) = delete;
    MoveOnly& operator=( MoveOnly && other ) noexcept
    {
        if (&other == this)
            return *this;
        x = other.x;
        return *this;
    }
};

struct NonMovableNonCopyable
{
    NonMovableNonCopyable() = default;

    NonMovableNonCopyable( NonMovableNonCopyable const & ) = delete;
    NonMovableNonCopyable( NonMovableNonCopyable &&      ) = delete;
    NonMovableNonCopyable& operator=( NonMovableNonCopyable const & ) = delete;
    NonMovableNonCopyable& operator=( NonMovableNonCopyable &&      ) = delete;
};

bool operator==( Implicit a, Implicit b ) { return a.x == b.x; }
bool operator==( Explicit a, Explicit b ) { return a.x == b.x; }

template< typename R >
bool operator==( MoveOnly const & a, R const & r ) { return a.x == r; }

std::ostream & operator<<( std::ostream & os, Implicit i ) { return os << "Implicit:" << i.x; }
std::ostream & operator<<( std::ostream & os, Explicit e ) { return os << "Explicit:" << e.x; }
std::ostream & operator<<( std::ostream & os, MoveOnly const & m ) { return os << "MoveOnly:" << m.x; }

struct InitList
{
    std::vector<int> vec;
    char c;

    InitList( std::initializer_list<int> il, char k  ) noexcept
    : vec( il ), c( k ) {}

//    InitList( InitList const & ) noexcept = default;
//    InitList( InitList && ) noexcept = default;
//
//    InitList & operator=( InitList const & ) noexcept = default;
//    InitList & operator=( InitList && ) noexcept = default;
};

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

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

    bool operator==( OracleVal const & other ) const { return s==other.s && i==other.i; }
};

struct Oracle
{
    State s;
    OracleVal val;

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

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

    bool operator==( Oracle const & other ) const { return s == other.s && val == other.val;}
};

std::ostream & operator<<( std::ostream & os, OracleVal const & o )
{
    using lest::to_string;
    return os << "[oracle:" << to_string( o.i ) << "]";
}

//} // anonymous namespace

namespace nonstd {

    template< typename T, typename E >
    std::ostream & operator<<( std::ostream & os, expected<T,E> const & e )
    {
        using lest::to_string;
        return os << ( e ? to_string( *e ) : "[error:" + to_string( e.error() ) + "]" );
    }

    template< typename E >
    std::ostream & operator<<( std::ostream & os, unexpected_type<E> const & u )
    {
        using lest::to_string;
        return os << "[unexp:" << to_string( u.value() ) << "]";
    }
}

using namespace nonstd;

//
// test specification:
//

CASE( "A C++11 union can contain non-POD types" "[.]" )
{
    struct nonpod { nonpod(){} };

    union U
    {
        char c;
        nonpod np;
    };
}

// -----------------------------------------------------------------------
// storage_t<>

CASE( "[storage_t]" "[.implement]" )
{
}

// -----------------------------------------------------------------------
// unexpected_type, unexpected_type<std::exception_ptr>

CASE( "unexpected_type: Disallows default construction" )
{
#if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS
    unexpected_type<int> u;
#endif
}

CASE( "unexpected_type: Allows to copy-construct from unexpected_type, default" )
{
    unexpected_type<int> a{ 7 };

    unexpected_type<int> b( a );

    EXPECT( a.error() == 7 );
    EXPECT( b.error() == 7 );
}

CASE( "unexpected_type: Allows to move-construct from unexpected_type, default" )
{
    unexpected_type<int> a{ 7 };

    unexpected_type<int> b( std::move( a ) );

    EXPECT( a.error() == 7 );
    EXPECT( b.error() == 7 );
}

CASE( "unexpected_type: Allows to in-place-construct" )
{
    unexpected_type<Explicit> ue( in_place, 5 );
    unexpected_type<Implicit> ui( in_place, 7 );

    EXPECT( ue.error() == Explicit{5} );
    EXPECT( ui.error() == Implicit{7} );
}

CASE( "unexpected_type: Allows to in-place-construct from initializer_list" )
{
    unexpected_type<InitList> u( in_place, { 7, 8, 9 }, 'a' );

    EXPECT( u.error().vec[0]  ==  7 );
    EXPECT( u.error().vec[1]  ==  8 );
    EXPECT( u.error().vec[2]  ==  9 );
    EXPECT( u.error().c       == 'a');
}

CASE( "unexpected_type: Allows to copy-construct from error_type" )
{
    unexpected_type<int> u{ 7 };

    EXPECT( u.error() == 7 );
}

CASE( "unexpected_type: Allows to move-construct from error_type" )
{
    unexpected_type<int> u{ std::move( 7 ) };

    EXPECT( u.error() == 7 );
}

CASE( "unexpected_type: Allows to copy-construct from unexpected_type, explicit converting" )
{
#if !nsel_USES_STD_EXPECTED // TODO
    unexpected_type<int> a{ 7 };

    unexpected_type<Explicit> b{ a };

    EXPECT( b.error() == Explicit{7} );
#else
    EXPECT( !!"no explicit converting copy-construct for std::unexpected (C++23)." "TODO" );
#endif
}

CASE( "unexpected_type: Allows to copy-construct from unexpected_type, non-explicit converting" )
{
#if !nsel_USES_STD_EXPECTED // TODO
    unexpected_type<int> a{ 7 };

    unexpected_type<Implicit> b( a );

    EXPECT( b.error() == Implicit{7} );
#else
    EXPECT( !!"no non-explicit converting copy-construct for std::unexpected (C++23)." "TODO" );
#endif
}

CASE( "unexpected_type: Allows to move-construct from unexpected_type, explicit converting" )
{
#if !nsel_USES_STD_EXPECTED // TODO
    unexpected_type<int> a{ 7 };

    unexpected_type<Explicit> b{ std::move( a ) };

    EXPECT( b.error() == Explicit{7} );
#else
    EXPECT( !!"no explicit converting move-construct for std::unexpected (C++23)." "TODO" );
#endif
}

CASE( "unexpected_type: Allows to move-construct from unexpected_type, non-explicit converting" )
{
#if !nsel_USES_STD_EXPECTED // TODO
    unexpected_type<int> a{ 7 };

    unexpected_type<Implicit> b( std::move( a ) );

    EXPECT( b.error() == Implicit{7} );
#else
    EXPECT( !!"no non-explicit converting move-construct for std::unexpected (C++23)." "TODO" );
#endif
}

CASE( "unexpected_type: Allows to copy-assign from unexpected_type, default" )
{
    unexpected_type<int> a{ 7 };
    unexpected_type<int> b{ 0 };

    b = a;

    EXPECT( b.error() == 7 );
}

CASE( "unexpected_type: Allows to move-assign from unexpected_type, default" )
{
    unexpected_type<int> a{ 7 };
    unexpected_type<int> b{ 0 };

    b = std::move( a );

    EXPECT( b.error() == 7 );
}

CASE( "unexpected_type: Allows to copy-assign from unexpected_type, converting" )
{
#if !nsel_USES_STD_EXPECTED
    unexpected_type<int> u{ 7 };
    unexpected_type<Explicit> ue{ 0 };
    unexpected_type<Implicit> ui{ 0 };

    ue = u;
    ui = u;

    EXPECT( ue.error() == Explicit{7} );
    EXPECT( ui.error() == Implicit{7} );
#else
    EXPECT( !!"no copy-assignment for std::unexpected (C++23)." );
#endif
}

CASE( "unexpected_type: Allows to move-assign from unexpected, converting" )
{
#if !nsel_USES_STD_EXPECTED
    unexpected_type<int> u{ 7 };
    unexpected_type<int> v{ 7 };
    unexpected_type<Explicit> ue{ 0 };
    unexpected_type<Implicit> ui{ 0 };

    ue = std::move( u );
    ui = std::move( v );

    EXPECT( ue.error() == Explicit{7} );
    EXPECT( ui.error() == Implicit{7} );
#else
    EXPECT( !!"no move-assignment for std::unexpected (C++23)." );
#endif
}

CASE( "unexpected_type: Allows to observe its value via a l-value reference" )
{
    unexpected_type<int>  u{ 7 };
    unexpected_type<int> uc{ 7 };

    EXPECT(  u.error() == 7 );
    EXPECT( uc.error() == 7 );
}

CASE( "unexpected_type: Allows to observe its value via a r-value reference" )
{
    unexpected_type<int>  u{ 7 };
    unexpected_type<int> uc{ 7 };

    EXPECT( std::move( u).error() == 7 );
    EXPECT( std::move(uc).error() == 7 );
}

CASE( "unexpected_type: Allows to modify its value via a l-value reference" )
{
    unexpected_type<int> u{ 5 };

    u.error() = 7;

    EXPECT( u.error() == 7 );
}

//CASE( "unexpected_type: Allows to modify its value via a r-value reference" )
//{
//    const auto v = 9;
//    unexpected_type<int> u{ 7 };
//
//    std::move( u.error() ) = v;
//
//    EXPECT( u.error() == v );
//}

CASE( "unexpected_type: Allows to be swapped" )
{
    unexpected_type<int> a{ 5 };
    unexpected_type<int> b{ 7 };

    a.swap( b );

    EXPECT( a.error() == 7 );
    EXPECT( b.error() == 5 );
}

//CASE( "unexpected_type: Allows reset via = {}" )
//{
//    unexpected_type<int> u( 3 );
//
//    u = {};
//}

// -----------------------------------------------------------------------
// unexpected_type<std::exception_ptr>

// TODO: unexpected_type: Should expected be specialized for particular E types such as exception_ptr and how?
//       See p0323r7 2.1. Ergonomics, http://wg21.link/p0323

namespace {

std::exception_ptr make_ep()
{
    try
    {
        // this generates an std::out_of_range:
        (void) std::string().at(1);
    }
    catch(...)
    {
        return std::current_exception();
    }
    return nullptr;
}
} // anonymous namespace

CASE( "unexpected_type<std::exception_ptr>: Disallows default construction" )
{
#if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS
    unexpected_type<std::exception_ptr> u;
#endif
}

CASE( "unexpected_type<std::exception_ptr>: Allows to copy-construct from error_type" )
{
    auto ep = make_ep();

    unexpected_type<std::exception_ptr> u{ ep };

    EXPECT( u.error() == ep );
}

CASE( "unexpected_type<std::exception_ptr>: Allows to move-construct from error_type" )
{
    auto ep_move = make_ep();
    const auto ep_copy = ep_move;

    unexpected_type<std::exception_ptr> u{ std::move( ep_move ) };

    EXPECT( u.error() == ep_copy );
}

CASE( "unexpected_type<std::exception_ptr>: Allows to copy-construct from an exception" )
{
    std::string text = "hello, world";

    unexpected_type<std::exception_ptr> u{ std::make_exception_ptr( std::logic_error( text.c_str() ) ) };

    try
    {
         std::rethrow_exception( u.error() );
    }
    catch( std::exception const & e )
    {
        EXPECT( e.what() == text );
    }
}

CASE( "unexpected_type<std::exception_ptr>: Allows to observe its value" )
{
    const auto ep = make_ep();
    unexpected_type<std::exception_ptr> u{ ep };

    EXPECT( u.error() == ep );
}

CASE( "unexpected_type<std::exception_ptr>: Allows to modify its value" )
{
    const auto ep1 = make_ep();
    const auto ep2 = make_ep();
    unexpected_type<std::exception_ptr> u{ ep1 };

    u.error() = ep2;

    EXPECT( u.error() == ep2 );
}

//CASE( "unexpected_type: Allows reset via = {}, std::exception_ptr specialization" )
//{
//    unexpected_type<int> u( 3 );
//
//    u = {};
//}

// -----------------------------------------------------------------------
// unexpected_type relational operators

CASE( "unexpected_type: Provides relational operators" )
{
    SETUP( "" ) {
        unexpected_type<int> u1( 6 );
        unexpected_type<int> u2( 7 );

    // compare engaged expected with engaged expected

    SECTION( "==" ) { EXPECT( u1 == u1 ); }
    SECTION( "!=" ) { EXPECT( u1 != u2 ); }
#if nsel_P0323R <= 2
    SECTION( "< " ) { EXPECT( u1 <  u2 ); }
    SECTION( "> " ) { EXPECT( u2 >  u1 ); }
    SECTION( "<=" ) { EXPECT( u1 <= u1 ); }
    SECTION( "<=" ) { EXPECT( u1 <= u2 ); }
    SECTION( ">=" ) { EXPECT( u1 >= u1 ); }
    SECTION( ">=" ) { EXPECT( u2 >= u1 ); }
#endif
    }
}

CASE( "unexpected_type: Provides relational operators, std::exception_ptr specialization" )
{
    SETUP( "" ) {
        unexpected_type<std::exception_ptr>  u( make_ep() );
        unexpected_type<std::exception_ptr> u2( make_ep() );

    // compare engaged expected with engaged expected

    SECTION( "==" ) { EXPECT    ( u == u ); }
    SECTION( "!=" ) { EXPECT    ( u != u2); }
#if nsel_P0323R <= 2
    SECTION( "< " ) { EXPECT_NOT( u <  u ); }
    SECTION( "> " ) { EXPECT_NOT( u >  u ); }
    SECTION( "< " ) { EXPECT_NOT( u <  u2); }
    SECTION( "> " ) { EXPECT_NOT( u >  u2); }
    SECTION( "<=" ) { EXPECT    ( u <= u ); }
    SECTION( ">=" ) { EXPECT    ( u >= u ); }
#endif
    }
}

// unexpected: traits

CASE( "is_unexpected<X>: Is true for unexpected_type" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    EXPECT( is_unexpected<unexpected_type<std::exception_ptr>>::value );
#else
    EXPECT( !!"is_unexpected<> is not available (nsel_P0323R > 3)" );
#endif
}

CASE( "is_unexpected<X>: Is false for non-unexpected_type (int)" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    EXPECT_NOT( is_unexpected<int>::value );
#else
    EXPECT( !!"is_unexpected<> is not available (nsel_P0323R > 3)" );
#endif
}

// unexpected: factory

CASE( "make_unexpected(): Allows to create an unexpected_type<E> from an E" )
{
    const auto error = 7;
    auto u = make_unexpected( error );

    EXPECT( u.error() == error );
}

CASE( "make_unexpected(): Allows to in-place create an unexpected_type<E> from an E" )
{
    const auto a = 'a';
    const auto b =  7;

    auto u = make_unexpected< std::pair<char, int> >( in_place, a, b );

    EXPECT( u.error().first  == a );
    EXPECT( u.error().second == b );
}

CASE( "make_unexpected_from_current_exception(): Allows to create an unexpected_type<std::exception_ptr> from the current exception" "[.deprecated]" )
{
#if nsel_P0323R <= 2
    std::string text = "hello, world";

    try
    {
         throw std::logic_error( text.c_str() );
    }
    catch( std::exception const & )
    {
        auto u = make_unexpected_from_current_exception() ;

        try
        {
            std::rethrow_exception( u.error() );
        }
        catch( std::exception const & e )
        {
            EXPECT( e.what() == text );
        }
    }
#else
    EXPECT( !!"make_unexpected_from_current_exception() is not available (nsel_P0323R > 2)" );
#endif
}

CASE( "unexpected: C++17 and later provide unexpected_type as unexpected" )
{
#if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141
    nsel_MAYBE_UNUSED nonstd::unexpected<int> u{7};
#else
    EXPECT( !!"unexpected is not available (no C++17)." );
#endif
}

// -----------------------------------------------------------------------
// x.x.6 bad_expected_access<>

CASE( "bad_expected_access: Disallows default construction" )
{
#if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS
    bad_expected_access<int> bad;
#endif
}

CASE( "bad_expected_access: Allows construction from error_type" )
{
    bad_expected_access<int> bea( 123 );
}

CASE( "bad_expected_access: Allows to observe its error" )
{
    const int error = 7;
    bad_expected_access<int> bea( error );
    bad_expected_access<int> const beac( error );

    EXPECT(             bea.error()   == error );
    EXPECT(            beac.error()   == error );
    EXPECT( std::move(  bea.error() ) == error );
    EXPECT( std::move( beac.error() ) == error );
}

CASE( "bad_expected_access: Allows to change its error" )
{
    const int old_error = 0;
    const int new_error = 7;
    bad_expected_access<int> bea( old_error );

    bea.error() = new_error;

    EXPECT( bea.error() == new_error  );
}

CASE( "bad_expected_access: Provides non-empty what()" )
{
    bad_expected_access<int> bea( 123 );

    EXPECT( ! std::string( bea.what() ).empty() );
}

// -----------------------------------------------------------------------
// expected<>

// x.x.4.1 expected<> constructors (value)

CASE( "expected: Allows to default construct" )
{
    expected<int, char> e;

    EXPECT( e.has_value() );
}

CASE( "expected: Allows to default construct from noncopyable, noncopyable value type" )
{
    expected<NonMovableNonCopyable, NonMovableNonCopyable> e;

    EXPECT( e.has_value() );
}

CASE( "expected: Allows to default construct from noncopyable, noncopyable error type" )
{
    expected<NonMovableNonCopyable, NonMovableNonCopyable> e{ unexpect_t{} };

    EXPECT( !e.has_value() );
}

CASE( "expected: Allows to copy-construct from expected: value" )
{
    expected<int, char> a = 7;

    expected<int, char> b{ a };

    EXPECT( b              );
    EXPECT( b.value() == 7 );
}

CASE( "expected: Allows to copy-construct from expected: error" )
{
    expected<char, int> a{ unexpect, 7 };

    expected<char, int> b{ a };

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected: Allows to move-construct from expected: value" )
{
    expected<int, char> a = 7;

    expected<int, char> b{ std::move( a ) };

    EXPECT( b              );
    EXPECT( b.value() == 7 );
    EXPECT( a              );   // postcondition: unchanged!
}

CASE( "expected: Allows to move-construct from expected: error" )
{
    expected<char, int> a{ unexpect, 7 };

    expected<char, int> b{ std::move( a ) };

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected: Allows to copy-construct from expected; value, explicit converting" )
{
    expected<int, char> a = 7;

    expected<Explicit, char> b{ a };

    EXPECT( b                        );
    EXPECT( b.value() == Explicit{7} );
}

CASE( "expected: Allows to copy-construct from expected; error, explicit converting" )
{
    expected<char, int> a{ unexpect, 7 };

    expected<char, Explicit> b{ a };

    EXPECT( !b                        );
    EXPECT(  b.error() == Explicit{7} );
}

CASE( "expected: Allows to copy-construct from expected; value, non-explicit converting" )
{
    expected<int, char> a = 7;

    expected<Implicit, char> b( a );

    EXPECT( b                        );
    EXPECT( b.value() == Implicit{7} );
}

CASE( "expected: Allows to copy-construct from expected; error, non-explicit converting" )
{
    expected<char, int> a{ unexpect, 7 };

    expected<char, Implicit> b( a );

    EXPECT( !b                        );
    EXPECT(  b.error() == Implicit{7} );
}

CASE( "expected: Allows to move-construct from expected; value, explicit converting" )
{
    expected<int, char> a = 7;

    expected<Explicit, char> b{ std::move( a ) };

    EXPECT( b                        );
    EXPECT( b.value() == Explicit{7} );
}

CASE( "expected: Allows to move-construct from expected; error, explicit converting" )
{
    expected<char, int> a{ unexpect, 7 };

    expected<char, Explicit> b{ std::move( a ) };

    EXPECT( !b                        );
    EXPECT(  b.error() == Explicit{7} );
}

CASE( "expected: Allows to move-construct from expected; value, non-explicit converting" )
{
    expected<int, char> a = 7;

    expected<Implicit, char> b( std::move( a ) );

    EXPECT( b                        );
    EXPECT( b.value() == Implicit{7} );
}

CASE( "expected: Allows to move-construct from expected; error, non-explicit converting" )
{
    expected<char, int> a{ unexpect, 7 };

    expected<char, Implicit> b( std::move( a ) );

    EXPECT( !b                        );
    EXPECT(  b.error() == Implicit{7} );
}

CASE( "expected: Allows to forward-construct from value, explicit converting" )
{
    auto v = 7;

    expected<Explicit, char> b{ std::move( v ) };

    EXPECT( b                        );
    EXPECT( b.value() == Explicit{7} );
}

CASE( "expected: Allows to forward-construct from value, non-explicit converting" )
{
    auto v = 7;

    expected<Implicit, char> b{ std::move( v ) };

    EXPECT( b                        );
    EXPECT( b.value() == Implicit{7} );
}

CASE( "expected: Allows to in-place-construct value" )
{
    auto v = 7;

    expected<Implicit, char> b{ in_place, v };

    EXPECT( b                        );
    EXPECT( b.value() == Implicit{7} );
}

CASE( "expected: Allows to in-place-construct value from initializer_list" )
{
    expected<InitList, char> b{ in_place, { 7, 8, 9 }, 'a' };

    EXPECT( b                );
    EXPECT( b->vec[0]  ==  7 );
    EXPECT( b->vec[1]  ==  8 );
    EXPECT( b->vec[2]  ==  9 );
    EXPECT( b->c       == 'a');
}

// x.x.4.1 expected<> constructors (error)

CASE( "expected: Allows to copy-construct from unexpected, explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<int, Explicit> e{ u };

    EXPECT( e.error() == Explicit{7} );
}

CASE( "expected: Allows to copy-construct from unexpected, non-explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<int, Implicit> e{ u };

    EXPECT( e.error() == Implicit{7} );
}

CASE( "expected: Allows to move-construct from unexpected, explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<int, Explicit> e{ std::move( u ) };

    EXPECT( e.error() == Explicit{7} );
}

CASE( "expected: Allows to move-construct from unexpected, non-explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<int, Implicit> e{ std::move( u ) };

    EXPECT( e.error() == Implicit{7} );
}

CASE( "expected: Allows to in-place-construct error" )
{
    expected<int, Explicit> e{ unexpect, 7 };
    expected<int, Implicit> i{ unexpect, 7 };

    EXPECT( e.error() == Explicit{7} );
    EXPECT( i.error() == Implicit{7} );
}

CASE( "expected: Allows to in-place-construct error from initializer_list" )
{
    expected<char, InitList> e{ unexpect, { 7, 8, 9 }, 'a' };

    EXPECT( !e                       );
    EXPECT(  e.error().vec[0]  ==  7 );
    EXPECT(  e.error().vec[1]  ==  8 );
    EXPECT(  e.error().vec[2]  ==  9 );
    EXPECT(  e.error().c       == 'a');
}

// x.x.4.3 expected<> assignment

CASE( "expected: Allows to copy-assign from expected, value" )
{
    expected<int, char> a{ 7 };
    expected<int, char> b;

    b = a;

    EXPECT( b              );
    EXPECT( b.value() == 7 );
}

CASE( "expected: Allows to copy-assign from expected, error" )
{
    expected<char, int> a{ unexpect, 7 };
    expected<char, int> b;

    b = a;

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected: Allows to move-assign from expected, value" )
{
    expected<int, char> a{ 7 };
    expected<int, char> b;

    b = std::move( a );

    EXPECT( b              );
    EXPECT( b.value() == 7 );
}

CASE( "expected: Allows to move-assign from expected, error" )
{
    expected<char, int> a{ unexpect, 7 };
    expected<char, int> b;

    b = std::move( a );

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected: Allows to forward-assign from value" )
{
    expected<int, char> a;
    expected<int, char> b;

    a = '7';
    b =  7;

    EXPECT( a                );
    EXPECT( b                );
    EXPECT( a.value() == '7' );
    EXPECT( b.value() ==  7  );
}

CASE( "expected: Allows to copy-assign from unexpected" )
{
    expected<char, int>  e;
    unexpected_type<int> u{ 7 } ;

    e = u;

    EXPECT( e.error() == 7 );
}

CASE( "expected: Allows to move-assign from unexpected" )
{
    expected<char, int>  e;
    unexpected_type<int> u{ 7 } ;

    e = std::move( u );

    EXPECT( e.error() == 7 );
}

CASE( "expected: Allows to move-assign from move-only unexpected" )
{
    expected<char, MoveOnly> e;
    unexpected_type<MoveOnly> u{7};

    e = std::move( u );

    EXPECT( e.error() == 7 );
}

CASE( "expected: Allows to emplace value" )
{
    expected<int, char> a;
    expected<int, char> b;

    auto va = a.emplace( '7' );
    auto vb = b.emplace(  7  );

    EXPECT( va == '7' );
    EXPECT( vb ==  7  );
    EXPECT( a.value() == '7' );
    EXPECT( b.value() ==  7  );
}

CASE( "expected: Allows to emplace value from initializer_list" )
{
    expected<InitList, char> e{ in_place, std::initializer_list<int>{}, 'x'};

    auto ve = e.emplace( { 7, 8, 9 }, 'a' );

    EXPECT( e                );
    EXPECT( ve.vec[0]  ==  7 );
    EXPECT( ve.vec[1]  ==  8 );
    EXPECT( ve.vec[2]  ==  9 );
    EXPECT( ve.c       == 'a');
    EXPECT( e.value().vec[0]  ==  7 );
    EXPECT( e.value().vec[1]  ==  8 );
    EXPECT( e.value().vec[2]  ==  9 );
    EXPECT( e.value().c       == 'a');
}

// x.x.4.4 expected<> swap

CASE( "expected: Allows to be swapped" )
{
    using std::swap;

    SETUP("") {

    expected<int, char> e1{ 1 };
    expected<int, char> e2{ 2 };
    expected<int, char> u1{ unexpect, '1' };
    expected<int, char> u2{ unexpect, '2' };

    SECTION("value-value, member swap")
    {
        e1.swap( e2 );

        EXPECT( e1.value() == 2 );
        EXPECT( e2.value() == 1 );
    }
    SECTION("error-error, member swap")
    {
        u1.swap( u2 );

        EXPECT( u1.error() == '2' );
        EXPECT( u2.error() == '1' );
    }
    SECTION("value-error, member swap")
    {
        e1.swap( u1 );

        EXPECT( e1.error() == '1' );
        EXPECT( u1.value() ==  1  );
    }
    SECTION("error-value, member swap")
    {
        u1.swap( e1 );

        EXPECT( u1.value() ==  1  );
        EXPECT( e1.error() == '1' );
    }
    }
}

// x.x.4.5 expected<> observers

CASE( "expected: Allows to observe its value via a pointer" )
{
    const auto v = 7;
    expected<Implicit, char> e{ v };

    EXPECT( e->x == v );
}

CASE( "expected: Allows to observe its value via a pointer to constant" )
{
    const auto v = 7;
    const expected<Implicit, char> e{ v };

    EXPECT( e->x == v );
}

CASE( "expected: Allows to modify its value via a pointer" )
{
    const auto v1 = 7;
    const auto v2 = 42;
    expected<Implicit, char> e{ v1 };

    e->x = v2;

    EXPECT( e->x == v2 );
}

CASE( "expected: Allows to observe its value via a l-value reference" )
{
    const auto v = 7;
    expected<Implicit, char>        e{ v };
    expected<Implicit, char> const ec{ v };

    EXPECT(  *e == Implicit{v} );
    EXPECT( *ec == Implicit{v} );
}

CASE( "expected: Allows to observe its value via a r-value reference" )
{
    const auto v = 7;
    expected<Implicit, char>        e{ v };
    expected<Implicit, char> const ec{ v };

    EXPECT( *std::move( e) == Implicit{v} );
    EXPECT( *std::move(ec) == Implicit{v} );
}

CASE( "expected: Allows to modify its value via a l-value reference" )
{
    const auto v1 = 7;
    const auto v2 = 42;
    expected<Implicit, char> e{ v1 };

    *e = Implicit{v2};

    EXPECT( *e == Implicit{v2} );
}

CASE( "expected: Allows to modify its value via a r-value reference" )
{
    const auto v1 = 7;
    const auto v2 = 42;
    expected<Implicit, char> e{ v1 };

#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
    *std::move(e) = Implicit{v2};
#else
    *e = Implicit{v2};  // non-r-value
#endif

    EXPECT( *e == Implicit{v2} );
}

CASE( "expected: Allows to observe if it contains a value (or error)" )
{
    expected<int, int> e;
    expected<int, int> u{ unexpect, 3 };

    EXPECT(  e );
    EXPECT( !u );
}

CASE( "expected: Allows to observe its value" )
{
    const auto v = 7;
    expected<Implicit, char>        e{ v };
    expected<Implicit, char> const ec{ v };

    EXPECT(  e.value() == Implicit{v} );
    EXPECT( ec.value() == Implicit{v} );
}

CASE( "expected: Allows to modify its value" )
{
    const auto v1 = 7;
    const auto v2 = 42;
    expected<Implicit, char> e{ v1 };

    e.value() = Implicit{v2};

    EXPECT( e.value() == Implicit{v2} );
}

CASE( "expected: Allows to move its value" )
{
    const auto v = 7;
    expected<Implicit, char>        m{ v };
    expected<Implicit, char> const mc{ v };

    expected<Implicit, char>  e{ std::move(m ).value() };
    expected<Implicit, char> ec{ std::move(mc).value() };

    EXPECT(  e.value() == Implicit{v} );
    EXPECT( ec.value() == Implicit{v} );
}

CASE( "expected: Allows to observe its error" )
{
    const auto v = 7;
    expected<char, Implicit>        e{ unexpect, v };
    expected<char, Implicit> const ec{ unexpect, v };

    EXPECT(  e.error() == Implicit{v} );
    EXPECT( ec.error() == Implicit{v} );
}

CASE( "expected: Allows to modify its error" )
{
    const auto v1 = 7;
    const auto v2 = 42;
    expected<char, Implicit> e{ unexpect, v1 };

    e.error() = Implicit{v2};

    EXPECT( e.error() == Implicit{v2} );
}

CASE( "expected: Allows to move its error" )
{
    const auto v = 7;
    expected<char, Implicit>        m{ unexpect, v };
    expected<char, Implicit> const mc{ unexpect, v };

    expected<char, Implicit>  e{ unexpect, std::move(m ).error() };
    expected<char, Implicit> ec{ unexpect, std::move(mc).error() };

    EXPECT(  e.error() == Implicit{v} );
    EXPECT( ec.error() == Implicit{v} );
}

CASE( "expected: Allows to observe its error as unexpected" )
{
#if !nsel_USES_STD_EXPECTED
    const auto v = 7;
    expected<char, Implicit> e{ unexpect, v };

    EXPECT( e.get_unexpected().error() == v );
#else
    EXPECT( !!"expected::get_unexpected() is not available (using C++23 std::expected)" );
#endif
}

CASE( "expected: Allows to query if it contains an exception of a specific base type" )
{
#if !nsel_USES_STD_EXPECTED
    expected<int, std::out_of_range> e{ unexpect, std::out_of_range( "oor" ) };

    EXPECT(  e.has_exception< std::logic_error   >() );
    EXPECT( !e.has_exception< std::runtime_error >() );
#else
    EXPECT( !!"expected::has_exception() is not available (using C++23 std::expected)" );
#endif
}

CASE( "expected: Allows to observe its value if available, or obtain a specified value otherwise" )
{
    const auto ve = 3;
    const auto vu = 7;
    expected<int, int> e{ ve };
    expected<int, int> u{ unexpect, 0 };

    EXPECT( e.value_or( vu ) == ve );
    EXPECT( u.value_or( vu ) == vu );
}

CASE( "expected: Allows to move its value if available, or obtain a specified value otherwise" )
{
    const auto ve = 3;
    const auto vu = 7;
    expected<int, int> mv{ ve };
    expected<int, int> mu{ unexpect, 0 };

    EXPECT( std::move( mv ).value_or( vu ) == ve );
    EXPECT( std::move( mu ).value_or( vu ) == vu );
}

CASE( "expected: Throws bad_expected_access on value access when disengaged" )
{
    expected<char, int >  e{ unexpect, 7 };
    expected<char, int > ec{ unexpect, 7 };

    EXPECT_THROWS(     e.value() );
    EXPECT_THROWS_AS(  e.value(), bad_expected_access<int> );
    EXPECT_THROWS(    ec.value() );
    EXPECT_THROWS_AS( ec.value(), bad_expected_access<int> );

    EXPECT_THROWS(    std::move( e).value() );
    EXPECT_THROWS_AS( std::move( e).value(), bad_expected_access<int> );
    EXPECT_THROWS(    std::move(ec).value() );
    EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access<int> );
}

#if nsel_P2505R >= 4
CASE( "expected: Allows to observe its unexpected value, or fallback to the specified value with error_or" " [monadic p2505r4]")
{
    const auto ve = 3;
    const auto vu = 7;
    expected<int, int> e{ ve };
    expected<int, int> u{ unexpect, 0 };

    EXPECT( e.error_or( vu ) == vu );
    EXPECT( u.error_or( vu ) ==  0 );
}
#endif // nsel_P2505R >= 4

#if nsel_P2505R >= 3
CASE( "expected: Allows to map value with and_then" " [monadic p2505r3]" )
{
    const auto mul2 = []( int n ) -> expected<int, int> { return n * 2; };
    const auto to_unexpect42 = []( int ) -> expected<int, int> { return make_unexpected( 42 ); };

    {
        expected<int, int> e{ 11 };
        const expected<int, int> ce{ 21 };
        expected<int, int> ue{ unexpect, 42 };
        EXPECT( e.and_then( mul2 ).value() == 22 );
        EXPECT( ce.and_then( mul2 ).value() == 42 );
        EXPECT( !ue.and_then( mul2 ).has_value());
        EXPECT( ue.and_then( mul2 ).error() == 42 );
        EXPECT( !e.and_then( to_unexpect42 ).has_value());
        EXPECT( e.and_then( to_unexpect42 ).error() == 42 );
        EXPECT( ce.and_then( to_unexpect42 ).error() == 42 );
    }

    const auto moveonly_x_mul2 = [](MoveOnly val) -> expected<int, int> { return val.x * 2; };
    EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).and_then( moveonly_x_mul2 ).value() == 66 );
    EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 15 } }).and_then( [](MoveOnly&&) -> expected<MoveOnly, int> { return make_unexpected( 42 ); } ).error() == 42 );

    const auto map_to_void = [](int) -> expected<void, int> { return {}; };
    const auto map_to_void_unexpect42 = [](int) -> expected<void, int> { return make_unexpected( 42 ); };
    static_assert( std::is_same< expected<void, int>, decltype( expected<int, int>( 3 ).and_then( map_to_void ) ) >::value,
        "and_then mapping to void results in expected<void>");
    EXPECT( (expected<int, int>(3)).and_then( map_to_void ).has_value() );
    EXPECT( !(expected<int, int>(3)).and_then( map_to_void_unexpect42 ).has_value() );
    EXPECT( (expected<int, int>(3)).and_then( map_to_void_unexpect42 ).error() == 42 );
}

CASE( "expected: Allows to map unexpected with or_else" " [monadic p2505r3]" )
{
    const auto to_unexpect43 = []( int ) -> expected<int, int> { return make_unexpected( 43 ); };

    {
        expected<int, int> e{ 11 };
        const expected<int, int> ce{ 21 };
        expected<int, int> ue{ unexpect, 42 };
        EXPECT( e.or_else( to_unexpect43 ).has_value());
        EXPECT( e.or_else( to_unexpect43 ).value() == 11 );
        EXPECT( ce.or_else( to_unexpect43 ).value() == 21 );
        EXPECT( !ue.or_else( to_unexpect43 ).has_value());
        EXPECT( ue.or_else( to_unexpect43 ).error() == 43 );
    }

    const auto fallback_throw = []( int ) -> expected<int, int> { throw std::runtime_error( "or_else" ); };
    EXPECT_THROWS_AS( (void)(expected<int, int>{ unexpect, 42 }).or_else( fallback_throw ), std::runtime_error );

    const auto moveonly_fallback_to_66 = [](int) -> expected<MoveOnly, int> { return MoveOnly{ 66 }; };
    EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).or_else( moveonly_fallback_to_66 ).value() == 33 );
    EXPECT( (expected<MoveOnly, int>{ unexpect, 15 }).or_else(moveonly_fallback_to_66).value() == 66 );
}

CASE( "expected: Allows to transform value" " [monadic p2505r3]" )
{
    const auto mul2 = []( int n ) -> int { return n * 2; };

    {
        expected<int, int> e{ 11 };
        const expected<int, int> ce{ 21 };
        expected<int, int> ue{ unexpect, 42 };
        EXPECT( e.transform( mul2 ).value() == 22 );
        EXPECT( ce.transform( mul2 ).value() == 42 );
        EXPECT( !ue.transform( mul2 ).has_value());
        EXPECT( ue.transform( mul2 ).error() == 42 );
    }

    expected<std::vector<int>, int>{std::vector<int>{}}.transform([](const std::vector<int>&) { });

#if nsel_P2505R >= 5
    // R5 changed remove_cvref_t to remove_cv_t in transform/transform_error, which broke data member pointer transforms,
    // because the result type must be a valid expected value type, but expressions with reference types are not.
    const auto moveonly_map_to_x = [](MoveOnly val) { return val.x; };
#else
    const auto moveonly_map_to_x = &MoveOnly::x;
#endif // nsel_P2505R >= 5
    const auto moveonly_x_mul2 = [](MoveOnly val) -> int { return val.x * 2; };
    EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).transform( moveonly_map_to_x ).value() == 33 );
    EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).transform( moveonly_map_to_x ).transform( mul2 ).value() == 66 );
    EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).transform( moveonly_x_mul2 ).has_value() );
    EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).transform( moveonly_x_mul2 ).value() == 66 );
    EXPECT( !(expected<MoveOnly, int>{ unexpect, 15 }).transform( [](MoveOnly&&) -> int { return 42; } ).has_value() );
    EXPECT( (expected<MoveOnly, int>{ unexpect, 15 }).transform( [](MoveOnly&&) -> int { return 42; } ).error() == 15 );

    const auto map_to_void = [](int) -> void { };
    static_assert( std::is_same< expected<void, int>, decltype( expected<int, int>( 3 ).transform( map_to_void ) ) >::value,
        "transform to void results in expected<void>" );
    EXPECT( (expected<int, int>(3)).transform( map_to_void ).has_value() );
    static_assert( std::is_same< decltype( (expected<int, int>(3)).transform( map_to_void ).value() ), void >::value,
        "transform to void results in void value" );
}

CASE( "expected: Allows to map errors with transform_error" " [monadic p2505r3]" )
{
    const auto to_43 = []( int ) -> int { return 43; };

    {
        expected<int, int> e{ 11 };
        const expected<int, int> ce{ 21 };
        expected<int, int> ue{ unexpect, 42 };
        EXPECT( e.transform_error( to_43 ).has_value());
        EXPECT( e.transform_error( to_43 ).value() == 11 );
        EXPECT( ce.transform_error( to_43 ).value() == 21 );
        EXPECT( !ue.transform_error( to_43 ).has_value());
        EXPECT( ue.transform_error( to_43 ).error() == 43 );
    }
}
#endif // nsel_P2505R >= 3

// -----------------------------------------------------------------------
// expected<void> specialization

// x.x.4.1 expected<void> constructors

CASE( "expected<void>: Allows to default-construct" )
{
    expected<void, char> e;

    EXPECT( e.has_value() );
}

CASE( "expected<void>: Allows to copy-construct from expected<void>: value" )
{
    expected<void, int> a;

    expected<void, int> b{ a };

    EXPECT( b );
}

CASE( "expected<void>: Allows to copy-construct from expected<void>: error" )
{
    expected<void, int > a{ unexpect, 7 };

    expected<void, int> b{ a };

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected<void>: Allows to move-construct from expected<void>: value" )
{
    expected<void, int> a;

    expected<void, int> b{ std::move( a ) };

    EXPECT( b );
    EXPECT( a );    // postcondition: unchanged!
}

CASE( "expected<void>: Allows to move-construct from expected<void>: error" )
{
    expected<void, int > a{ unexpect, 7 };

    expected<void, int> b{ std::move( a ) };

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected<void>: Allows to in-place-construct" )
{
    expected<void, int > e{ in_place };

    EXPECT( e );
}

CASE( "expected<void>: Allows to copy-construct from unexpected, explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<void, Explicit> e{ u };

    EXPECT( e.error() == Explicit{7} );
}

CASE( "expected<void>: Allows to copy-construct from unexpected, non-explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<void, Implicit> e{ u };

    EXPECT( e.error() == Implicit{7} );
}

CASE( "expected<void>: Allows to move-construct from unexpected, explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<void, Explicit> e{ std::move( u ) };

    EXPECT( e.error() == Explicit{7} );
}

CASE( "expected<void>: Allows to move-construct from unexpected, non-explicit converting" )
{
    unexpected_type<int> u{ 7 };

    expected<void, Implicit> e{ std::move( u ) };

    EXPECT( e.error() == Implicit{7} );
}

CASE( "expected<void>: Allows to in-place-construct unexpected_type" )
{
    expected<void, Explicit> e{ unexpect, 7 };
    expected<void, Implicit> i{ unexpect, 7 };

    EXPECT( e.error() == Explicit{7} );
    EXPECT( i.error() == Implicit{7} );
}

CASE( "expected<void>: Allows to in-place-construct error from initializer_list" )
{
    expected<void, InitList> e{ unexpect, { 7, 8, 9 }, 'a' };

    EXPECT( !e                       );
    EXPECT(  e.error().vec[0]  ==  7 );
    EXPECT(  e.error().vec[1]  ==  8 );
    EXPECT(  e.error().vec[2]  ==  9 );
    EXPECT(  e.error().c       == 'a');
}

// x.x.4.3 expected<void> assignment

CASE( "expected<void>: Allows to copy-assign from expected, value" )
{
    expected<void, int> a;
    expected<void, int> b;

    b = a;

    EXPECT( b );
}

CASE( "expected<void>: Allows to copy-assign from expected, error" )
{
    expected<void, int> a{ unexpect, 7 };
    expected<void, int> b;

    b = a;

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected<void>: Allows to move-assign from expected, value" )
{
    expected<void, int> a;
    expected<void, int> b;

    b = std::move( a );

    EXPECT( b );
}

CASE( "expected<void>: Allows to move-assign from expected, error" )
{
    expected<void, int> a{ unexpect, 7 };
    expected<void, int> b;

    b = std::move( a );

    EXPECT( !b              );
    EXPECT(  b.error() == 7 );
}

CASE( "expected<void>: Allows to emplace value" )
{
    expected<void, int> a{ unexpect, 7 };

    a.emplace();

    EXPECT( a );
}

// x.x.4.4 expected<void> swap

CASE( "expected<void>: Allows to be swapped" )
{
    using std::swap;

    SETUP("") {

    expected<void, char> e1;
    expected<void, char> e2;
    expected<void, char> u1{ unexpect, '1' };
    expected<void, char> u2{ unexpect, '2' };

    SECTION("value-value, member swap")
    {
        e1.swap( e2 );

        EXPECT( e1 );
        EXPECT( e2 );
    }
    SECTION("error-error, member swap")
    {
        u1.swap( u2 );

        EXPECT( u1.error() == '2' );
        EXPECT( u2.error() == '1' );
    }
    SECTION("value-error, member swap")
    {
        e1.swap( u1 );

        EXPECT( e1.error() == '1' );
        EXPECT( u1                );
    }
    SECTION("error-value, member swap")
    {
        u1.swap( e1 );

        EXPECT( u1                );
        EXPECT( e1.error() == '1' );
    }
    }
}

// x.x.4.5 expected<void> observers

CASE( "expected<void>: Allows to observe if it contains a value (or error)" )
{
    expected<void, int> e;
    expected<void, int> u{ unexpect, 3 };

    EXPECT(  e );
    EXPECT( !u );
}

CASE( "expected<void>: Allows to observe its value" )
{
    expected<void, int>        e;
    expected<void, int> const ec;

    EXPECT( (  e.value(), true ) );
    EXPECT( ( ec.value(), true ) );
}

CASE( "expected<void>: Allows to observe its error" )
{
    const auto v = 7;
    expected<void, Implicit>        e{ unexpect, v };
    expected<void, Implicit> const ec{ unexpect, v };

    EXPECT(  e.error() == Implicit{v} );
    EXPECT( ec.error() == Implicit{v} );
}

CASE( "expected<void>: Allows to modify its error" )
{
    const auto v1 = 7;
    const auto v2 = 42;
    expected<void, Implicit> e{ unexpect, v1 };

    e.error() = Implicit{v2};

    EXPECT( e.error() == Implicit{v2} );
}

CASE( "expected<void>: Allows to move its error" )
{
    const auto v = 7;
    expected<void, Implicit>        m{ unexpect, v };
    expected<void, Implicit> const mc{ unexpect, v };

    expected<void, Implicit>  e{ unexpect, std::move(m ).error() };
    expected<void, Implicit> ec{ unexpect, std::move(mc).error() };

    EXPECT(  e.error() == Implicit{v} );
    EXPECT( ec.error() == Implicit{v} );
}

CASE( "expected<void>: Allows to observe its error as unexpected" )
{
#if !nsel_USES_STD_EXPECTED
    const auto value = 7;
    expected<void, int> e{ unexpect, value };

    EXPECT( e.get_unexpected().error() == value );
#else
    EXPECT( !!"expected::get_unexpected() is not available (using C++23 std::expected)" );
#endif
}

CASE( "expected<void>: Allows to query if it contains an exception of a specific base type" )
{
#if !nsel_USES_STD_EXPECTED
    expected<void, std::out_of_range> e{ unexpect, std::out_of_range( "oor" ) };

    EXPECT(  e.has_exception< std::logic_error   >() );
    EXPECT( !e.has_exception< std::runtime_error >() );
#else
    EXPECT( !!"expected::has_exception() is not available (using C++23 std::expected)" );
#endif
}

CASE( "expected<void>: Throws bad_expected_access on value access when disengaged" )
{
    expected<void, int >  e{ unexpect, 7 };
    expected<void, int > ec{ unexpect, 7 };

    EXPECT_THROWS(     e.value() );
    EXPECT_THROWS_AS(  e.value(), bad_expected_access<int> );
    EXPECT_THROWS(    ec.value() );
    EXPECT_THROWS_AS( ec.value(), bad_expected_access<int> );

    EXPECT_THROWS(    std::move( e).value() );
    EXPECT_THROWS_AS( std::move( e).value(), bad_expected_access<int> );
    EXPECT_THROWS(    std::move(ec).value() );
    EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access<int> );
}

#if nsel_P2505R >= 4
CASE( "expected<void>: Allows to observe unexpected value, or fallback to a default value with error_or" " [monadic p2505r4]" )
{
    const auto vu = 7;
    expected<void, int> e;
    expected<void, int> u{ unexpect, 0 };

    EXPECT( e.error_or( vu ) == vu );
    EXPECT( u.error_or( vu ) == 0 );
}
#endif // nsel_P2505R >= 4

#if nsel_P2505R >= 3
CASE( "expected<void>: Allows to call argless functions with and_then" " [monadic p2505r3]" )
{
    const auto ret22 = []() -> expected<int, int> { return 22; };
    const auto unexpect32 = []() -> expected<int, int> { return make_unexpected( 32 ); };

    {
        expected<void, int> e;
        const expected<void, int> ce;
        expected<void, int> ue{ unexpect, 42 };
        EXPECT( e.has_value() );
        EXPECT( ce.has_value() );
        EXPECT( e.and_then( ret22 ).value() == 22 );
        EXPECT( ce.and_then( ret22 ).value() == 22 );
        EXPECT( !ue.and_then( ret22 ).has_value());
        EXPECT( ue.and_then( ret22 ).error() == 42 );
        EXPECT( !e.and_then( unexpect32 ).has_value());
        EXPECT( e.and_then( unexpect32 ).error() == 32 );
        EXPECT( ce.and_then( unexpect32 ).error() == 32 );
    }

    {
        bool called = false;
        expected<void, int> e;
        e.and_then( [&called]() -> expected<void, int> {
            called = true;
            return {};
        } );
        EXPECT( called );
    }

    {
        bool called = false;
        expected<void, int>{}.and_then( [&called]() -> expected<void, int> {
            called = true;
            return {};
        } );
        EXPECT( called );
    }

    {
        bool called = false;
        expected<void, int>{ unexpect, 42 }.and_then( [&called]() -> expected<void, int> {
            called = true;
            return {};
        } );
        EXPECT( !called );
    }

    const bool map_to_unexpect_success = !expected<void, int>{}.and_then( []() -> expected<void, int> { return make_unexpected( 42 ); } ).has_value();
    EXPECT( map_to_unexpect_success );
}

CASE( "expected<void>: Allows to map to expected or unexpected with or_else" " [monadic p2505r3]" )
{
    const auto make_valid = [](int) -> expected<void, int> { return {}; };
    const auto unexpect32 = [](int) -> expected<void, int> { return make_unexpected( 32 ); };

    {
        expected<void, int> e;
        const expected<void, int> ce;
        expected<void, int> ue{ unexpect, 42 };
        EXPECT( e.has_value() );
        EXPECT( ce.has_value() );
        EXPECT( e.or_else( unexpect32 ).has_value());
        static_assert( std::is_same< decltype( e.or_else( unexpect32 ).value() ), void >::value,
            "or_else mapping to void results in void value" );
        EXPECT( ce.or_else( unexpect32 ).has_value());
        EXPECT( !ue.or_else( unexpect32 ).has_value());
        EXPECT( ue.or_else( make_valid ).has_value() );
        static_assert( std::is_same< decltype( ue.or_else( make_valid ).value() ), void >::value,
            "or_else mapping to void results in void value" );
        EXPECT( ue.or_else( unexpect32 ).error() == 32 );
    }
}

CASE( "expected<void>: Allows to assign a new expected value using transform" " [monadic p2505r3]" )
{
    const auto make_int_32 = [] { return 32; };
    const auto mul2 = [](int v) { return v * 2; };
    expected<void, int> e;
    static_assert( std::is_same< decltype( e.transform( make_int_32 ) )::value_type, int >::value, "" );
    EXPECT( e.transform( make_int_32 ).value() == 32 );
    EXPECT( e.transform( make_int_32 ).transform( mul2 ).value() == 64 );
}

CASE( "expected<void>: Allows to map unexpected error value via transform_error" " [monadic p2505r3]" )
{
    const auto mul2 = []( int v ) -> int { return v * 2; };
    enum class my_error { einval };
    const auto map_to_my_error = [](int) { return my_error::einval; };

    {
        expected<void, int> e;
        const expected<void, int> ce;
        expected<void, int> ue{ unexpect, 42 };
        EXPECT( e.transform_error( mul2 ).has_value());
        EXPECT( ce.transform_error( mul2 ).has_value());
        EXPECT( !ue.transform_error( mul2 ).has_value());
        EXPECT( ue.transform_error( mul2 ).error() == 84 );
        EXPECT( ue.transform_error( map_to_my_error ).error() == my_error::einval );
    }
}
#endif // nsel_P2505R >= 3

// [expected<> unwrap()]

// [expected<> factories]

// x.x.4.7 expected<>: relational operators

CASE( "operators: Provides expected relational operators" )
{
    SETUP( "" ) {
        auto v1 = 6;
        auto v2 = 7;
        expected<int, char> e1( v1 );
        expected<int, char> e2( v2 );

        unexpected_type<char> u( 'u' );
        expected<int, char> d( u );

    // compare engaged expected with engaged expected

    SECTION( "engaged    == engaged"    ) { EXPECT( e1 == e1 ); }
    SECTION( "engaged    != engaged"    ) { EXPECT( e1 != e2 ); }
#if nsel_P0323R <= 2
    SECTION( "engaged    <  engaged"    ) { EXPECT( e1 <  e2 ); }
    SECTION( "engaged    >  engaged"    ) { EXPECT( e2 >  e1 ); }
    SECTION( "engaged    <= engaged"    ) { EXPECT( e1 <= e1 ); }
    SECTION( "engaged    <= engaged"    ) { EXPECT( e1 <= e2 ); }
    SECTION( "engaged    >= engaged"    ) { EXPECT( e1 >= e1 ); }
    SECTION( "engaged    >= engaged"    ) { EXPECT( e2 >= e1 ); }
#endif

    // compare engaged expected with value

    SECTION( "engaged    == value"      ) { EXPECT( e1 == v1 ); }
    SECTION( "value      == engaged"    ) { EXPECT( v1 == e1 ); }
    SECTION( "engaged    != value"      ) { EXPECT( e1 != v2 ); }
    SECTION( "value      != engaged"    ) { EXPECT( v1 != e2 ); }
#if nsel_P0323R <= 2
    SECTION( "engaged    <  value"      ) { EXPECT( e1 <  v2 ); }
    SECTION( "value      <  engaged"    ) { EXPECT( v1 <  e2 ); }
    SECTION( "engaged    >  value"      ) { EXPECT( e2 >  v1 ); }
    SECTION( "value      >  engaged"    ) { EXPECT( v2 >  e1 ); }
    SECTION( "engaged    <= value"      ) { EXPECT( e1 <= v2 ); }
    SECTION( "value      <= engaged"    ) { EXPECT( v1 <= e2 ); }
    SECTION( "engaged    >= value"      ) { EXPECT( e2 >= v1 ); }
    SECTION( "value      >= engaged"    ) { EXPECT( v2 >= e1 ); }
#endif

    // compare engaged expected with disengaged expected

    SECTION( "engaged    == disengaged" ) { EXPECT_NOT( e1 == d  ); }
    SECTION( "disengaged == engaged"    ) { EXPECT_NOT( d  == e1 ); }
    SECTION( "engaged    != disengaged" ) { EXPECT    ( e1 != d  ); }
    SECTION( "disengaged != engaged"    ) { EXPECT    ( d  != e2 ); }
#if nsel_P0323R <= 2
    SECTION( "engaged    <  disengaged" ) { EXPECT_NOT( e1 <  d  ); }
    SECTION( "disengaged <  engaged"    ) { EXPECT    ( d  <  e2 ); }
    SECTION( "engaged    >  disengaged" ) { EXPECT    ( e2 >  d  ); }
    SECTION( "disengaged >  engaged"    ) { EXPECT_NOT( d  >  e1 ); }
    SECTION( "engaged    <= disengaged" ) { EXPECT_NOT( e1 <= d  ); }
    SECTION( "disengaged <= engaged"    ) { EXPECT    ( d  <= e2 ); }
    SECTION( "engaged    >= disengaged" ) { EXPECT    ( e2 >= d  ); }
    SECTION( "disengaged >= engaged"    ) { EXPECT_NOT( d  >= e1 ); }
#endif

    // compare engaged expected with unexpected

    SECTION( "disengaged == unexpected" ) { EXPECT    ( d  == u  ); }
    SECTION( "unexpected == disengaged" ) { EXPECT    ( u  == d  ); }
    SECTION( "engaged    != unexpected" ) { EXPECT    ( e1 != u  ); }
    SECTION( "unexpected != engaged"    ) { EXPECT    ( u  != e1 ); }
#if nsel_P0323R <= 2
    SECTION( "disengaged <  unexpected" ) { EXPECT_NOT( d  <  u  ); }
    SECTION( "unexpected <  disengaged" ) { EXPECT_NOT( u  <  d  ); }
    SECTION( "disengaged <= unexpected" ) { EXPECT    ( d  <= u  ); }
    SECTION( "unexpected <= disengaged" ) { EXPECT    ( u  <= d  ); }
    SECTION( "disengaged >  unexpected" ) { EXPECT_NOT( d  >  u  ); }
    SECTION( "unexpected >  disengaged" ) { EXPECT_NOT( u  >  d  ); }
    SECTION( "disengaged >= unexpected" ) { EXPECT    ( d  >= u  ); }
    SECTION( "unexpected >= disengaged" ) { EXPECT    ( u  >= d  ); }
#endif

    }
}

CASE( "operators: Provides expected relational operators (void)" )
{
    SETUP( "" ) {
        expected<void, char> ev1;
        expected<void, char> ev2;
        expected<void, char> evu{ unexpect };

    // compare engaged expected with engaged expected

    SECTION( "  engaged == engaged"     ) { EXPECT(     ev1 == ev2 ); }
    SECTION( "!(engaged != engaged)"    ) { EXPECT_NOT( ev1 != ev2 ); }

    // compare engaged expected with disengaged expected

    SECTION( "  engaged != disengaged"  ) { EXPECT(     ev1 != evu ); }
    SECTION( "!(engaged == disengaged)" ) { EXPECT_NOT( ev1 == evu ); }
    }
}

// -----------------------------------------------------------------------
// expected: specialized algorithms

// -----------------------------------------------------------------------
// Other

CASE( "swap: Allows expected to be swapped" )
{
    using std::swap;

    SETUP("") {

    expected<int, char> e1{ 1 };
    expected<int, char> e2{ 2 };
    expected<int, char> u1{ unexpect, '1' };
    expected<int, char> u2{ unexpect, '2' };

    SECTION("value-value, std::swap")
    {
        swap( e1, e2 );

        EXPECT( e1.value() == 2 );
        EXPECT( e2.value() == 1 );
    }
    SECTION("error-error, std::swap")
    {
        swap( u1, u2 );

        EXPECT( u1.error() == '2' );
        EXPECT( u2.error() == '1' );
    }
    SECTION("value-error, std::swap")
    {
        swap( e1, u1 );

        EXPECT( e1.error() == '1' );
        EXPECT( u1.value() ==  1  );
    }
    SECTION("error-value, std::swap")
    {
        swap( u1, e1 );

        EXPECT( u1.value() ==  1  );
        EXPECT( e1.error() == '1' );
    }
    }
}

CASE( "std::hash: Allows to compute hash value for expected" )
{
#if !nsel_USES_STD_EXPECTED
    expected<int, char> a{ 7 };
    expected<int, char> b{ 7 };

    EXPECT( (std::hash< expected<int, char> >{}( a )) == (std::hash< expected<int, char> >{}( b )) );
#else
    EXPECT( !!"std::hash<std::expected<>> is not available for std::unexpected (C++23)." );
#endif
}

#if nsel_P0323R <= 3

#include <memory>

void vfoo() {}

expected<int> foo()
{
    return make_expected( 7 );
}

expected<std::unique_ptr<int>> bar()
{
    return make_expected( std::unique_ptr<int>( new int(7) ) );
}

#endif // nsel_P0323R

CASE( "make_expected(): create expected from given value" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    auto e = make_expected( 7 );

    EXPECT(  e      );
    EXPECT( *e == 7 );
#else
    EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" );
#endif
}

CASE( "make_expected(): create expected<void>" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    auto e = make_expected();

    EXPECT( e );
#else
    EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" );
#endif
}

CASE( "make_expected_from_current_exception(): create expected from current exception" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    EXPECT( !!"Implement" );
#else
    EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" );
#endif
}

CASE( "make_expected_from_exception(): create expected from given exception" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    EXPECT( !!"Implement" );
#else
    EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" );
#endif
}

CASE( "make_expected_from_call(): non-void return type" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    expected<int> ei = foo();
    expected<std::unique_ptr<int>> eup = bar();

    auto e2   = make_expected_from_call( foo );
    auto eup2 = make_expected_from_call( bar );

    EXPECT( e2   );
    EXPECT( eup2 );
#else
    EXPECT( !!"make_expected_from_call() is not available (nsel_P0323R > 3)" );
#endif
}

CASE( "make_expected_from_call(): void return type" "[.deprecated]" )
{
#if nsel_P0323R <= 3
    auto ev = make_expected_from_call( vfoo );

    EXPECT( ev );
#else
    EXPECT( !!"make_expected_from_call() is not available (nsel_P0323R > 3)" );
#endif
}

CASE( "tweak header: reads tweak header if supported " "[tweak]" )
{
#if expected_HAVE_TWEAK_HEADER
    EXPECT( EXPECTED_TWEAK_VALUE == 42 );
#else
    EXPECT( !!"Tweak header is not available (expected_HAVE_TWEAK_HEADER: 0)." );
#endif
}

// -----------------------------------------------------------------------
// expected: issues

// issue #15, https://github.com/martinmoene/expected-dark/issues/15

CASE( "issue-15d" )
{
    nonstd::expected< int, std::error_code > e = 12;
    (void)e.value();
}

// issue #15, https://github.com/martinmoene/expected-lite/issues/15

CASE( "issue-15" )
{
    (void) nonstd::expected< int, int >( 12).value();
}

// issue #29, https://github.com/martinmoene/expected-lite/issues/29
// issue #32, https://github.com/martinmoene/expected-lite/issues/32

namespace issue_32 {

enum class Error
{
    Bad
};

class MyNonMoveableObject
{
public:
    MyNonMoveableObject() = default;
    MyNonMoveableObject( MyNonMoveableObject const &  ) = default;
    MyNonMoveableObject( MyNonMoveableObject       && ) = delete;
    MyNonMoveableObject& operator=( MyNonMoveableObject const  &) = default;
    MyNonMoveableObject& operator=( MyNonMoveableObject       &&) = delete;

    ~MyNonMoveableObject() = default;
};

nonstd::expected< MyNonMoveableObject, Error > create_copyable()
{
    return nonstd::expected< MyNonMoveableObject, Error >{};
}

class MyNonCopyableObject
{
public:
    MyNonCopyableObject() = default;
    MyNonCopyableObject( MyNonCopyableObject const &  ) = delete;
    MyNonCopyableObject( MyNonCopyableObject       && ) = default;
    MyNonCopyableObject& operator=( MyNonCopyableObject const &  ) = delete;
    MyNonCopyableObject& operator=( MyNonCopyableObject       && ) = default;

    ~MyNonCopyableObject() = default;
};

nonstd::expected< MyNonCopyableObject, Error > create_moveable()
{
    return MyNonCopyableObject{};
}

} // namespace issue_32

CASE( "pr-41" )
{
    expected<int, int> a{7};
    const expected<int, int> ca{7};
    expected<int, int> b{unexpect, 7};
    const expected<int, int> cb{unexpect, 7};
    expected<void, int> c{unexpect, 7};
    const expected<void, int> cc{unexpect, 7};
    EXPECT( *std::move(a) == 7 );
    EXPECT( *std::move(ca) == 7 );
    EXPECT( std::move(b).error() == 7 );
    EXPECT( std::move(cb).error() == 7 );
    EXPECT( std::move(c).error() == 7 );
    EXPECT( std::move(cc).error() == 7 );
}

// issue #50, https://github.com/martinmoene/expected-lite/issues/50

namespace issue_50 {

struct MyConstMemberNonMoveableObject
{
    const int x;
    MyConstMemberNonMoveableObject( int x_ ) : x( x_ ) {}
    MyConstMemberNonMoveableObject( MyConstMemberNonMoveableObject const & ) = default;
};

nonstd::unexpected_type<MyConstMemberNonMoveableObject> create_nonmoveable()
{
    return nonstd::make_unexpected<MyConstMemberNonMoveableObject>( MyConstMemberNonMoveableObject(3) );
}

} // namespace issue_50

namespace issue_51 {

int compare_equal_with_expected_void()
{
    auto ev1 = nonstd::expected<void, int>{};
    auto ev2 = nonstd::expected<void, int>{};
    return ev1 == ev2;
}

int compare_not_equal_with_expected_void()
{
    auto ev1 = nonstd::expected<void, int>{};
    auto ev2 = nonstd::expected<void, int>{};
    return ev1 != ev2;
}

} // namespace issue_51

namespace issue_59 {

struct NonMovableNonCopyable
{
    NonMovableNonCopyable() = default;

    NonMovableNonCopyable( NonMovableNonCopyable const &  ) = delete;
    NonMovableNonCopyable( NonMovableNonCopyable       && ) = delete;
    NonMovableNonCopyable& operator=( NonMovableNonCopyable const &  ) = delete;
    NonMovableNonCopyable& operator=( NonMovableNonCopyable       && ) = delete;
};
} // namespace issue_59

CASE( "issue-58" )
{
    static_assert( !std::is_copy_constructible<issue_59::NonMovableNonCopyable>::value, "is not copy constructible" );
    static_assert( !std::is_move_constructible<issue_59::NonMovableNonCopyable>::value, "is not move constructible" );

    nonstd::expected<issue_59::NonMovableNonCopyable, issue_59::NonMovableNonCopyable> expected;
    nonstd::expected<issue_59::NonMovableNonCopyable, issue_59::NonMovableNonCopyable> unexpected( nonstd::unexpect_t{} );

    EXPECT(  expected.has_value()   );
    EXPECT( !unexpected.has_value() );
}

namespace issue_70 {

    struct overload_addressof
    {
        overload_addressof * operator & ()
        {
            assert( 0 && "operator&() called");
            return this;
        } 
    };
} // namespace issue_70

CASE("issue-70")
{
    nonstd::expected<issue_70::overload_addressof, int> e;
}

#if !nsel_USES_STD_EXPECTED && nsel_P2505R >= 3
CASE( "invoke" )
{
    struct A {
      int x;
      constexpr int get() const { return x; }
      constexpr int get2(char) const { return x; }
    };
    static_assert( nonstd::expected_lite::detail::invoke( &A::x, A{21} ) == 21, "" );
    EXPECT( nonstd::expected_lite::detail::invoke( &MoveOnly::x, MoveOnly(42) ) == 42 );
    constexpr A lval{ 7 };
    static_assert( nonstd::expected_lite::detail::invoke( &A::x, lval ) == 7, "" );
    A mut_lval{ 12 };
    std::reference_wrapper<A> ref{ mut_lval };
    const std::reference_wrapper<const A> cref{ lval };
    EXPECT( nonstd::expected_lite::detail::invoke( &A::x, ref ) == 12 );
    EXPECT( nonstd::expected_lite::detail::invoke( &A::x, cref ) == 7 );
    static_assert( nonstd::expected_lite::detail::invoke(&A::x, &lval) == 7, "" );
    static_assert( nonstd::expected_lite::detail::invoke(&A::get, &lval) == 7, "" );
    static_assert( nonstd::expected_lite::detail::invoke(&A::get, A{77}) == 77, "" );
    EXPECT( nonstd::expected_lite::detail::invoke(&A::get, ref) == 12 );
    EXPECT( nonstd::expected_lite::detail::invoke(&A::get, cref) == 7 );
    static_assert( nonstd::expected_lite::detail::invoke(&A::get2, &lval, 'a') == 7, "" );
    static_assert( nonstd::expected_lite::detail::invoke(&A::get2, A{77}, 'a') == 77, "" );
    EXPECT( nonstd::expected_lite::detail::invoke(&A::get2, ref, 'a') == 12 );
    EXPECT( nonstd::expected_lite::detail::invoke(&A::get2, cref, 'a') == 7 );
}
#endif // nsel_USES_STD_EXPECTED && nsel_P2505R >= 3

// -----------------------------------------------------------------------
//  using as optional

#if 1

/// disengaged optional state tag

struct nullopt_t{};

nsel_MAYBE_UNUSED const nullopt_t nullopt{};

/// optional expressed in expected

template< typename T >
using optional = expected<T, nullopt_t>;

#endif
