#pragma once

/*
Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

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

------

Original repository: https://github.com/Sedeniono/tiny-optional
*/

#include <cassert>
#include <climits>
#include <cstdint> // Required for std::uint64_t etc.
#include <cstring> // Required for memcpy
#include <functional> // Required for std::hash and std::invoke
#include <limits> // Required for std::numeric_limits
#include <optional> // Required for std::nullopt etc.
#include <type_traits>

// In principle the following headers are required, but we rely on the standard header <optional> to include the
// necessary pieces from the omitted headers. This is a build performance optimization, especially when using gcc's
// libstdc++, which includes certain internal smaller headers directly.
// #include <compare> // For operator<=>
// #include <initializer_list>
// #include <memory> // Required for std::addressof
// #include <utility> // Required for std::move, std::swap, etc.


// TINY_OPTIONAL_VERSION % 100 is the patch level
// TINY_OPTIONAL_VERSION / 100 % 1000 is the minor version
// TINY_OPTIONAL_VERSION / 100000 is the major version
// So the format is:          MmmmPP, where 'M'=major, 'm'=minor and 'P'=patch.
#define TINY_OPTIONAL_VERSION 100300


#if (!defined(__cplusplus) || __cplusplus < 201703L) && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L)
  #error "The tiny::optional library requires at least C++17"
#endif

#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
  #define TINY_OPTIONAL_CPP20
#endif

// https://stackoverflow.com/a/66249936
#if (defined(__x86_64__) || defined(_M_X64)) /* Is it x64?*/
  #define TINY_OPTIONAL_x64
#elif (defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)) /*Is it x86?*/
  #define TINY_OPTIONAL_x86
#endif

// The user can define TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS to disable the exploits of undefined
// behavior. This allows compilation on non x86/x64 platforms. This means that the only remaining feature of this
// library that sets it apart from std::optional is the ability to use a custom sentinel (and the stuff with
// optional_flag_manipulator).
#ifdef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS
// Disable exploits of unused bits e.g. for double, pointers, etc, and use a separate bool instead.
  #ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UNUSED_BITS
    #define TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UNUSED_BITS
  #endif
// Disable storing the empty state in members and use a separate bool instead.
  #ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
    #define TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
  #endif
#endif

#ifdef __cpp_lib_three_way_comparison
  #define TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON
  #if !defined(__clang__) && (defined(__GNUC__) || defined(__GNUG__))
    // With C++20, most of the comparison operators (==, <=, etc) that compare with a std::nullopt are no longer defined
    // in the standard. Instead, the code relies on the C++20 reversal of arguments or the three way comparison
    // operator. Unfortunately, gcc (at least up to and including gcc 13.2) does not implemented CWG 2445, meaning that
    // e.g. std::nullopt==someOptional calls the wrong function. See https://stackoverflow.com/q/74330356/3740047.
    // To work around this issue, we define all operators for gcc.
    #define TINY_OPTIONAL_GCC_WORKAROUND_CWG2445
  #endif
#endif

#if defined(__cpp_concepts) && defined(__cpp_lib_concepts)
  // The implementation of or_else() uses C++20 concepts.
  #define TINY_OPTIONAL_ENABLE_ORELSE
#endif


#ifdef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UNUSED_BITS
  #define TINY_OPTIONAL_UNUSED_BITS_NS_PART noBit
#else
  #define TINY_OPTIONAL_UNUSED_BITS_NS_PART bit
#endif

#ifdef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
  #define TINY_OPTIONAL_MEMBER_NS_PART noMem
#else
  #define TINY_OPTIONAL_MEMBER_NS_PART mem
#endif

#define TINY_OPTIONAL_CONCAT_NS_IMPL(a, b, c) tiny##a##_##b##_##c
#define TINY_OPTIONAL_CONCAT_NS(a, b, c) TINY_OPTIONAL_CONCAT_NS_IMPL(a, b, c)

// We use an inline namespace to prevent mixing of symbols from different versions of the library or
// different TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS settings.
#define TINY_OPTIONAL_INLINE_NS_BEGIN                                                                                  \
  inline namespace TINY_OPTIONAL_CONCAT_NS(                                                                            \
      TINY_OPTIONAL_VERSION,                                                                                           \
      TINY_OPTIONAL_UNUSED_BITS_NS_PART,                                                                               \
      TINY_OPTIONAL_MEMBER_NS_PART)                                                                                    \
  {

#define TINY_OPTIONAL_INLINE_NS_END }


#if defined(__GNUG__) && !defined(__clang__)
// Disable incorrect warning for gcc that occurs in release builds. It sometimes fails to realize that certain branches
// (such as calls to DestroyPayload()) are protected by a has_value() check, and thus cannot actually perform any
// uninitialized access. In fact, this warning is notorious for producing false positives, and can even be triggered
// for std::optional (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635#c69) at least until gcc 13.
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif

// Forward declaration of optional_flag_manipulator, which is a user customization point.
// Reason for the #ifndef: Only the **first** declaration can specify the default values for the template parameters.
// Since the user is supposed to specialize this, he or she needs to know the forward declaration. Thus there is a
// dedicated header that contains the forward declaration. We could simply #include the header here, but then we force
// every user to handle 2 files, not just 1, even if the user never wants to specialize the flag manipulator. At the
// same time, we do need the forward declaration below (because of the default argument). Solution: We forward declare
// it only if it wasn't already declared, which is checked by the #ifndef.
#ifndef TINY_OPTIONAL_FLAG_MANIPULATOR_ALREADY_DECLARED
  #define TINY_OPTIONAL_FLAG_MANIPULATOR_ALREADY_DECLARED
namespace tiny
{
template <class PayloadType, class Enable = void>
struct optional_flag_manipulator;
}
#endif


namespace tiny
{
TINY_OPTIONAL_INLINE_NS_BEGIN
// Special type and value to indicate that the user did not specify a certain template parameter type/value.
enum UseDefaultType
{
  UseDefaultValue
};


// Forward declaration (required for transform()).
template <class PayloadType_, auto sentinelOrMemPtr = UseDefaultValue, auto irrelevantOrSentinel = UseDefaultValue>
class optional;


namespace impl
{
  // Helper tag to indicate that no optional_flag_manipulator specialization is known.
  struct NoCustomInplaceFlagManipulator
  {
  };
} // namespace impl
TINY_OPTIONAL_INLINE_NS_END


// The user can specialize the optional_flag_manipulator template to 'inject' a custom FlagManipulator for some
// payload type or types. If the user provides one, tiny::optional assumes that the 'IsEmpty' flag is stored inplace
// somewhere in the payload type, and thus does not use a separate bool flag. Instead, it will use the
// 'MyCustomFlagManipulator' to manipulator the 'IsEmpty' flag. Also compare the description of the 'FlagManipulator'
// concept somewhere below.
//
// Notes:
// - The specialization should be declared right below where the custom type payload type (i.e. the type which is put
//   into the optional) is defined. If the specialization is declared anywhere else, it could happen easily that some
//   instantiation of optional does not see the it while other instantiations do see it. This causes undefined behavior
//   (violation of the one definition rule, ODR).
// - The idea for using specializations as customization point is essentially the same as for std::hash and
//   fmt::formatter.
// - The original idea was to not use class specializations, but instead use function overloads and rely on ADL
//   (argument dependent lookup). The functions would have never been called, but their return type would have defined
//   the FlagManipulator. The advantage would have been that user code does not need to know about the original
//   optional_flag_manipulator template (i.e. no #include or forward declaration in user code necessary). However,
//   the major issue was that MSVC, gcc and clang (the compilers and the static analyzers) complained about either
//   unused or undefined functions, especially when put into anonymous namespaces. The warnings about unused functions
//   were easily fixable by prepending [[maybe_unused]], except in cases of friend functions (where the attribute is not
//   allowed). Getting rid of the warning about undefined functions meant to provide a dummy implementation (e.g. simply
//   throwing an exception), or disabling the warning via a #pragma. All of this seemed to rather annoying, hence we
//   chose the specialization approach.
//
// This here is the definition of the original 'optional_flag_manipulator' template, that the user can specialize. We
// also specialize it below for e.g. floating point types.
//
// The 'Enable' parameter can be used in conjunction with std::enable_if to provide a specialization for multiple types
// at once satisfying a common concept. Compare the library's specialization for floating point types. Note that it has
// the default value 'void' (defined at the declaration of the template).
//
// Note: Outside of the inline namespace (TINY_OPTIONAL_INLINE_NS_BEGIN) because we don't want to add a dependency on
// the version and configuration to the optional_flag_manipulator_fwd.h file.
template <class PayloadType, class Enable>
struct optional_flag_manipulator : impl::NoCustomInplaceFlagManipulator
{
};


TINY_OPTIONAL_INLINE_NS_BEGIN
// Helper to allow the user to implement an optional_flag_manipulator specialization more easily in case it is a simple
// comparison with a sentinel value. This can be especially useful for enumerations: Instead of writing e.g.
// tiny::optional<EnumType, EnumType::InvalidValue> always, the user can simply once define the specialization
//      template <> struct tiny::optional_flag_manipulator<EnumType>
//       : tiny::sentinel_flag_manipulator<EnumType, EnumType::InvalidValue> { };
// and then use tiny::optional<EnumType>.
template <class PayloadType, auto SentinelValue>
struct sentinel_flag_manipulator
{
  static bool is_empty(PayloadType const & payload) noexcept
  {
    return payload == SentinelValue;
  }

  static void init_empty_flag(PayloadType & uninitializedPayloadMemory) noexcept
  {
    ::new (&uninitializedPayloadMemory) PayloadType(SentinelValue);
  }

  static void invalidate_empty_flag(PayloadType & emptyPayload) noexcept
  {
    emptyPayload.~PayloadType();
  }
};


namespace impl
{
  template <class U>
  constexpr std::enable_if_t<U::is_tiny_optional, bool> IsTinyOptionalHelper(U const *)
  {
    return true;
  }

  template <class>
  constexpr bool IsTinyOptionalHelper(...)
  {
    return false;
  }
} // namespace impl


// Can be used to check whether the given type 'U' is some tiny optional type
// (i.e. tiny::optional, tiny::optional_inplace, tiny::optional_aip or tiny::optional_sentinel_via_type).
template <class U>
inline constexpr bool is_tiny_optional_v = impl::IsTinyOptionalHelper<U>(nullptr);
TINY_OPTIONAL_INLINE_NS_END
} // namespace tiny


namespace tiny
{
TINY_OPTIONAL_INLINE_NS_BEGIN
namespace impl
{

  //====================================================================================
  // Utilities for the implementation
  //====================================================================================

  // Marked as [[maybe_unused]] since clang till version 8 emits a warning that
  // this variable is supposedly unused.
  template <class... T>
  [[maybe_unused]] inline constexpr bool always_false = false;


  // std::remove_cvref is only available since C++20.
  template <class T>
  using my_remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;


  // Similar to std::conditional, but instead of resulting in a type it results in a value.
  // I would like to use ?: instead, but ?: requires 'compatible' types in the two branches.
  template <bool B, auto val1, auto val2>
  struct value_conditional
  {
    static constexpr auto value = val1;
  };

  template <auto val1, auto val2>
  struct value_conditional<false, val1, val2>
  {
    static constexpr auto value = val2;
  };


  // Helpers for the std::hash specializations.
  // Cannot use std::enable_if_t<..., optional> directly because it results in a compiler error 'the template
  // parameter not used or deducible in partial specialization'.
  template <class Type, class>
  using ConditionalIdentity = Type;

  // clang-format off
  template <class Type, class TypeToCheckForHashable>
  using EnableHashHelper = ConditionalIdentity<
    Type,
    std::enable_if_t< 
      std::is_default_constructible_v<std::hash<std::remove_const_t<TypeToCheckForHashable>>> 
    >
  >;
  // clang-format on


  // Helper to check whether some type T is a std::optional.
  template <class T>
  inline constexpr bool IsStdOptional = false;
  template <class T>
  inline constexpr bool IsStdOptional<std::optional<T>> = true;


  template <class T>
  inline constexpr bool IsSomeOptional = is_tiny_optional_v<T> || IsStdOptional<T>;


  // Extracts the class and target variable of a given member pointer.
  // https://stackoverflow.com/a/41475087
  template <auto memberPointer>
  struct MemberPointerFragments;

  template <typename ClassType_, typename VariableType_, VariableType_ ClassType_::*memberPointer>
  struct MemberPointerFragments<memberPointer>
  {
    using ClassType = ClassType_;
    using VariableType = VariableType_;
  };


  // Returns true if the numerical value of the integral 'value' fits into the literal 'TargetT'.
  // Similar to std::in_range but works also for char, etc.
  // Also, std::in_range is not available in C++17.
  template <class TargetT, class SourceT>
  static constexpr bool IsIntegralInRange(SourceT value) noexcept
  {
    static_assert(std::is_integral_v<TargetT>);
    static_assert(std::is_integral_v<SourceT>);

    if constexpr (std::is_signed_v<TargetT> == std::is_signed_v<SourceT>) {
      using CommonT = std::common_type_t<TargetT, SourceT>;
      return static_cast<CommonT>(std::numeric_limits<TargetT>::lowest()) <= static_cast<CommonT>(value)
             && static_cast<CommonT>(value) <= static_cast<CommonT>(std::numeric_limits<TargetT>::max());
    }
    else if constexpr (std::is_signed_v<SourceT>) {
      // Source = signed, target = unsigned
      return value >= 0 && IsIntegralInRange<TargetT>(static_cast<std::make_unsigned_t<SourceT>>(value));
    }
    else {
      constexpr TargetT targetMax = std::numeric_limits<TargetT>::max();

      // Source = unsigned, target = signed
      static_assert(std::is_signed_v<TargetT>);
      static_assert(targetMax > 0);

      using TargetUnsignedT = std::make_unsigned_t<TargetT>;
      using CommonUnsignedT = std::common_type_t<SourceT, TargetUnsignedT>;
      return static_cast<CommonUnsignedT>(value) <= static_cast<CommonUnsignedT>(targetMax);
    }
  }


  // Helper tag for optional::transform(func). The problem is that the value returned by func() should be directly used
  // to initialize the payload, WITHOUT any moving of the variable in-between (to support non-movable types). But this
  // means that the user function passed to transform() needs to be handed down into the core of the optional.
  struct DirectInitializationFromFunctionTag
  {
  };


  //====================================================================================
  // SentinelForExploitingUnusedBits
  //====================================================================================

  // The specializations of SentinelForExploitingUnusedBits define the bit pattern to use to indicate an empty value
  // when the 'IsEmpty'-flag is stored inplace for various standard types. Which standard types are supported is
  // defined by the GetTinyOptionalInplaceFlagManipulator() overloads somewhere below.
  // Note: By construction, we exploit implementation-defined behavior here, and use type punning. Thus, the
  // SentinelForExploitingUnusedBits::value cannot be of the same type as the IsEmpty-flag-variable.
  template <typename T>
  struct SentinelForExploitingUnusedBits
  {
    static_assert(
        always_false<T>,
        "Instantiation of SentinelForExploitingUnusedBits with unknown type. Either an explicit specialization of "
        "SentinelForExploitingUnusedBits is missing, or it should not have been instantiated in the first place. "
        "This is a bug in the tiny-optional library. Please report it!");
  };


#ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UNUSED_BITS

  // So far the implementation defined exploits are only implemented and tested for x64 and x86.
  #if !defined(TINY_OPTIONAL_x86) && !defined(TINY_OPTIONAL_x64)
    #error Exploiting of unused bits is not implemented for the target architecture. Note that you can disable UB-tricks via TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS. See Readme.
  #endif


  template <>
  struct SentinelForExploitingUnusedBits<bool>
  {
    // We could use any value besides 0x00 and 0x01.
    // If a bool contains any other numerical value than 0 or 1, the bool can be true and false 'at the same time', i.e.
    // weird stuff can happen. See https://stackoverflow.com/q/56369080. In the code here we never use the bool as a
    // bool when its value is not 0 or 1.
    static constexpr std::uint8_t value = 0xfe;
    static_assert(sizeof(value) <= sizeof(bool));
  };


  template <>
  struct SentinelForExploitingUnusedBits<double>
  {
    // Compare https://cwiki.apache.org/confluence/display/stdcxx/FloatingPoint
    // We use a NaN value that is not used by default as signaling or quiet NaN on any platform.
    // A quiet NaN is used to prevent accidental generation of a floating point exception when loaded into a register.
    static constexpr std::uint64_t value = 0x7ff8fedcba987654;
    static_assert(sizeof(value) == sizeof(double));
    static_assert(std::numeric_limits<double>::is_iec559);
  };


  template <>
  struct SentinelForExploitingUnusedBits<float>
  {
    // Compare https://cwiki.apache.org/confluence/display/stdcxx/FloatingPoint
    // and https://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html

    // We use a quiet NaN value that is not used by default as signaling or quiet NaN on any platform.
    // A quiet NaN is used to prevent accidental generation of a floating point exception when loaded into a register.
    static constexpr std::uint32_t value = 0x7fedcba9;
    static_assert(sizeof(value) == sizeof(float));
    static_assert(std::numeric_limits<float>::is_iec559);
  };


  // Ordinary pointer or function pointer (but not a member pointer or member function pointer; those are rather
  // special, so we do not support to place the flag inplace for them).
  template <class T>
  struct SentinelForExploitingUnusedBits<T *>
  {
    // We support a compressed optional<T*> especially because it allows compressed optionals of structs/classes that
    // contain a pointer. I.e. the 'IsEmpty'-flag can then be stored in a member variable which is a pointer.
    // The sentinel value is chosen by considering the following points:
    // - We obviously don't want to use a nullptr. An optional containing a nullptr should be possible and should not
    //   appear to be empty.
    // - A sensible idea at first sight seems to be to use the highest possible value (i.e. 0xffff'ffff for 32 bit).
    //   The problem is that on Windows there is the HANDLE type as well as similar types such as HINSTANCE or HWND.
    //   They are just typedefs to void*. In case of errors, some WinAPIs return INVALID_HANDLE_VALUE as value, which
    //   is defined as -1 (i.e. 0xffff'ffff on 32 bit). Moreover, Windows defines so called 'pseudo handles' which can
    //   be returned by various WinAPI functions. The known values are -1, -2, -3, -4, -5 and -6. See
    //   https://github.com/winsiderss/systeminformer/blob/c19d69317a8eedcce773eb7317462eac8dfebf66/phnt/include/ntpsapi.h#L1294
    //   https://devblogs.microsoft.com/oldnewthing/20210105-00/?p=104667
    //   So all of these should not be used as sentinels.
    // - Similar, small values such as 0x01 (which, in principle, shouldn't appear in practice as valid addresses) are
    //   not a good choice either because at least the WinAPI function ShellExecute() function can return such values as
    //   error codes.
    // - Not all addresses are possible on 64 bit. Current CPUs only implement 48 meaningful bits since this is cheaper.
    //   Having enough memory to require true 64 bits is a thing for the distant future (thousands of petabytes...).
    //   These so-called 'canonical addresses' range from 0x0000'0000'0000'0000 to 0x0000'7fff'ffff'ffff (which is
    //   reserved for user space addresses) and from 0xffff'8000'0000'0000 to 0xffff'ffff'ffff'ffff (which is reserved
    //   for kernel space addresses). All values in the gap between these two ranges can never occur as a real valid
    //   virtual address. Thus, on 64 bit, we want to use a value from this gap of non-canonical addresses as sentinel.
    //   Also see https://read.seas.harvard.edu/cs161/2018/doc/memory-layout/
    // - Allocated memory is usually aligned to 4 bytes on today's system. Of course, one can have valid addresses also
    //   to non-aligned locations. But they occur less often in practice. Thus, if we choose a sentinel value that is
    //   not divisible by 4, or even better not divisible by 2, we minimize the chance that the chosen sentinel value is
    //   encountered as valid address in practice.

  #ifdef TINY_OPTIONAL_x64
    // We simply use the highest non-canonical address on 64 bit.
    static constexpr std::uintptr_t value = 0xffff'8000'0000'0000ull - 1;
  #elif defined(TINY_OPTIONAL_x86)
    // >= 0xffff'ffff-5 are not possible due to the pseudo handles on Windows. 0xffff'ffff-6 would be possible. Just to
    // get a bit more distance to the space of pseudo handles (in case another one will be introduced), we use
    // 0xffff'ffff-8. The value 0xffff'ffff-7 is not used to satisfy the note about alignment above.
    static constexpr std::uintptr_t value = 0xffff'ffff - 8;
  #else
    #error Unknown architecture.
  #endif

    static_assert(sizeof(value) == sizeof(T *));

    // Cross-check the thoughts about alignment explained above: Value should not be divisible by 2.
    static_assert(value % 2 == 1);
  };
#endif // #ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UNUSED_BITS


  //====================================================================================
  // Types used as storage in the optional.
  //====================================================================================

  // When optional is not doing anything special, i.e. in cases where it behaves just like std::optional, it uses
  // this wrapper to store the 'IsEmpty'-Flag outside of the payload.
  template <class PayloadType>
  struct SeparateFlagStorage
  {
    union
    {
      std::remove_const_t<PayloadType> payload;
    };

    bool isEmptyFlag; // True if the optional is empty, false otherwise.

    SeparateFlagStorage() { }
    ~SeparateFlagStorage() { }
  };


  // Used by the optional if the IsEmpty flag is stored within the payload.
  template <class PayloadType>
  struct InplaceStorage
  {
    // Union to prevent automatic initialization of mStorage. I.e. this only allocates the memory without
    // calling the constructor of PayloadType.
    union
    {
      std::remove_const_t<PayloadType> storage;
    };

    InplaceStorage() { }
    ~InplaceStorage() { }
  };


  //====================================================================================
  // StoredTypeDecomposition
  //====================================================================================

  /*
   * The StoredTypeDecomposition concept defines how the data stored in the optional is
   * decomposed into payload and 'IsEmpty'-flag. It consist of:
   *
   * - The payload type, i.e. the type that the user actually wanted to store in the optional.
   *
   * - The StoredType indicates the type that the optional is actually storing internally. So
   *   this is basically the type containing both the PayloadType and the IsEmptyFlagType.
   *   Note that they might be identical.
   *
   * - The type of the IsEmptyFlagType, i.e. the type of the variable storing the IsEmpty-flag.
   *   This is not explicity defined via a 'using' but by the return value of GetIsEmptyFlag().
   *
   * - GetIsEmptyFlag(): Given the StoredType, returns a mutable reference to the IsEmpty-flag.
   *
   * - GetPayload(): Given the StoredType, returns a mutable reference to the payload.
   */


  // Decomposition used when the optional is behaving as a std::optional, i.e. stores the
  // 'IsEmpty'-flag in a separate bool variable. This is realized using the SeparateFlagStorage.
  template <class PayloadType_>
  struct DecompositionForSeparateFlag
  {
    using PayloadType = PayloadType_;
    using StoredType = SeparateFlagStorage<PayloadType>;

    [[nodiscard]] static constexpr bool & GetIsEmptyFlag(StoredType & v) noexcept
    {
      return v.isEmptyFlag;
    }

    [[nodiscard]] static constexpr PayloadType & GetPayload(StoredType & v) noexcept
    {
      return v.payload;
    }
  };


  // Decomposition used when the stored type is both the payload and the 'IsEmpty'-flag at the same time.
  // The 'IsEmpty'-value can be stored by exploiting unused bit patterns or by 'swallowing' some
  // user specified value from the variable's value range.
  template <class PayloadType_>
  struct InplaceStoredTypeDecomposition
  {
    using StoredType = InplaceStorage<PayloadType_>;
    using PayloadType = PayloadType_;

    [[nodiscard]] static constexpr auto & GetIsEmptyFlag(StoredType & v) noexcept
    {
      return v.storage;
    }

    [[nodiscard]] static constexpr PayloadType & GetPayload(StoredType & v) noexcept
    {
      return v.storage;
    }
  };


#ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
  // Decomposition used when the StoredType and the PayloadType are the same and identify a class/struct,
  // where the 'IsEmpty'-flag is stored inplace of one of the member variables of that class/struct.
  // This member variable is identified by the member pointer 'memPtrToIsEmptyFlag'.
  // The actual 'IsEmpty'-value can be stored by exploiting unused bit patterns or by 'swallowing' some
  // user specified value from the variable's value range.
  //
  // Note: We rely on undefined behavior here. We get the address of the IsEmpty-flag by using the provided member
  // pointer on the StoredType variable. But the StoredType object does not exist yet when we do so, since the
  // StoredType and the PayloadType are basically the same. The PayloadType cannot exist yet because the whole point of
  // an empty optional is to not require the existence of the payload. In other words, we apply the member pointer to a
  // non-existing payload.
  template <class PayloadType_, auto memPtrToIsEmptyFlag>
  struct InplaceDecompositionViaMemPtr
  {
  // As explained above, we exploit undefined behavior here. I have tested this only on x86/x64 platforms.
  #if !defined(TINY_OPTIONAL_x86) && !defined(TINY_OPTIONAL_x64)
    #error Storing the empty state in a member is not supported on the target architecture. Note that you can disable UB-tricks via TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS. See Readme.
  #endif

    static_assert(std::is_member_object_pointer_v<decltype(memPtrToIsEmptyFlag)>);
    static_assert(memPtrToIsEmptyFlag != nullptr);

    using StoredType = InplaceStorage<PayloadType_>;
    using PayloadType = PayloadType_;

    [[nodiscard]] static constexpr auto & GetIsEmptyFlag(StoredType & v) noexcept
    {
      return v.storage.*memPtrToIsEmptyFlag;
    }

    [[nodiscard]] static constexpr PayloadType & GetPayload(StoredType & v) noexcept
    {
      return v.storage;
    }
  };
#endif


  //====================================================================================
  // FlagManipulator
  //====================================================================================

  /**
   * The FlagManipulator concept provides functions to initialize, destroy and query
   * a given 'IsEmpty'-flag. Functions:
   *
   * - init_empty_flag(): This function receives the address of the already allocated
   *   memory that should contain the 'IsEmpty'-flag. The function must initialize this memory
   *   such that the flag indicates an empty state.
   *
   * - invalidate_empty_flag(): This function receives the flag (which currently indicates
   *   the empty state, i.e. is_empty() returns true for it). The function is called just before
   *   the payload is constructed. It must deconstruct the value such that after the payload
   *   has been constructed the 'IsEmpty'-flag must indicate that some value is set.
   *   Note: It must NOT free the memory!
   *
   * - is_empty(): This function receives a const-ref to the flag and must return 'true' if it
   *   indicates that the optional contains no value, and 'false' if it indicates that some
   *   value is set.
   *
   * We are using snake_case for the function names since users of the library might need to use
   * the concept (via tiny::optional_flag_manipulator or optional_inplace), and the whole public
   * interface of the library uses snake_case (because std::optional does).
   */


  // Used when the optional is behaving as a std::optional, i.e. when the 'IsEmpty' flag is
  // stored in a separate bool variable (via SeparateFlagStorage).
  struct SeparateFlagManipulator
  {
    [[nodiscard]] static bool is_empty(bool isEmptyFlag) noexcept
    {
      return isEmptyFlag;
    }

    static void init_empty_flag(bool & isEmptyFlag) noexcept
    {
      // Using placement new would be wrong here: The constructor of SeparateFlagStorage already pops the bool object
      // into existence (but with an indeterminate value). Also, invalidate_empty_flag() does not destroy the
      // bool. So all we have to do is to set the value.
      isEmptyFlag = true;
    }

    static void invalidate_empty_flag(bool & isEmptyFlag) noexcept
    {
      // We do not destruct the bool object, since SeparateFlagStorage::isEmptyFlag should remain valid during the whole
      // lifetime of the optional. That is the whole point of the SeparateFlagStorage.
      isEmptyFlag = false;
    }
  };


  // Used when we exploit that the payload (or a member variable within the payload) has unused bit patterns.
  // In this case we use type punning and set the flag's bits directly to SentinelValue::value, which is typically
  // given by SentinelForExploitingUnusedBits.
  // Note that FlagType and the type of the given SentinelValue::value can have different types. They are compared
  // and copied 'raw' (in the sense of std::memcmp and std::memcpy).
  template <class FlagType, class SentinelValue>
  struct MemcpyAndCmpFlagManipulator
  {
  private:
    static constexpr auto valueToIndicateEmpty = SentinelValue::value;
    static_assert(sizeof(valueToIndicateEmpty) <= sizeof(FlagType));

    // Required so that we can use std::memcpy in a way that is covered by the C++ standard.
    // Compare https://stackoverflow.com/a/59522771/3740047.
    // Until gcc <=9, however, CWG 2094 was not implemented (https://stackoverflow.com/q/36098055/3740047),
    // meaning that this static_assert fails with volatile payloads.
#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ <= 9
    static_assert(std::is_trivially_copyable_v<std::remove_cv_t<FlagType>>);
#else
    static_assert(std::is_trivially_copyable_v<FlagType>);
#endif

    static_assert(std::is_trivial_v<FlagType>);

  public:
    [[nodiscard]] static bool is_empty(FlagType const & isEmptyFlag) noexcept
    {
      // Regarding the cast: https://stackoverflow.com/q/63325244/3740047
      return std::memcmp(
                 const_cast<void *>(static_cast<void volatile const *>(std::addressof(isEmptyFlag))),
                 &valueToIndicateEmpty,
                 sizeof(valueToIndicateEmpty))
             == 0;
    }

    static void init_empty_flag(FlagType & uninitializedIsEmptyFlagMemory) noexcept
    {
      // Similar to placement new, memcpy pops the flag object into existence:
      // https://en.cppreference.com/w/cpp/string/byte/memcpy
      // To this end note the static_asserts above: The flag is trivially copyable.
      // Regarding the cast: https://stackoverflow.com/q/63325244/3740047
      std::memcpy(
          const_cast<void *>(static_cast<void volatile const *>(std::addressof(uninitializedIsEmptyFlagMemory))),
          &valueToIndicateEmpty,
          sizeof(valueToIndicateEmpty));
    }

    static void invalidate_empty_flag(FlagType & isEmptyFlag) noexcept
    {
      // Destroy the flag object. In cases such as a simple 'double', this does not really translate to any
      // instructions. But it ensures that we formally destroy the object that was previously created in
      // init_empty_flag(). By design, the payload that will be constructed will overlap with the flag memory and
      // assign a value that can never be equal to valueToIndicateEmpty.
      isEmptyFlag.~FlagType();
    }
  };


  // Used when the user specified a specific value to 'swallow' and use to indicate the empty state.
  // For example, if the payload is an integer, the user might specify to use MAX_INT to indicate
  // the empty state. No need to use hacky memcpy and memcmp operations in this case.
  template <class FlagType, class SentinelValue>
  struct AssignmentFlagManipulator
  {
  private:
    // The empty value given by the user (SentinelValue) might have a different type than the flag variable.
    // This can happen very easily if the user e.g. specified the literal 42 as empty value (which is an 'int'),
    // but the optional stores an 'unsigned int'. It would be annoying for the user to always specify the
    // literals with the 'correct' postfix (e.g. 42u). Thus, we cast the value here. But before this we
    // check that the literal is not changed in the process. For example, specifying -1 for an optional
    // storing an 'unsigned' results in a compiler error.
    [[nodiscard]] static constexpr std::remove_cv_t<FlagType> ConvertSentinelToFlagType() noexcept
    {
      using ValueType = std::remove_cv_t<decltype(SentinelValue::value)>;
      using FlagTypeNoCV = std::remove_cv_t<FlagType>;

      if constexpr (std::is_same_v<FlagTypeNoCV, bool> || std::is_same_v<ValueType, bool>) {
        static_assert(
            std::is_same_v<FlagTypeNoCV, ValueType>,
            "Either the variable used by the optional as IsEmpty flag or the specified compile-time constant "
            "for the sentinel is a bool, but not both. If one is a bool, both should be a bool.");
        return SentinelValue::value;
      }
      else if constexpr (std::is_integral_v<FlagTypeNoCV> && std::is_integral_v<ValueType>) {
        // The static_cast prevents compiler warnings.
        // That we do not change the numeric value is ensured by the static_assert.
        static_assert(
            IsIntegralInRange<FlagTypeNoCV>(SentinelValue::value),
            "The specified compile-time constant for the empty value is outside of the range supported "
            "by the type of the variable used by the optional as IsEmpty-flag. Just as an example: "
            "'optional<unsigned, -1>' triggers this because the value -1 cannot be held by an 'unsigned'.");
        return static_cast<FlagTypeNoCV>(SentinelValue::value);
      }
      else if constexpr (std::is_floating_point_v<FlagTypeNoCV> || std::is_floating_point_v<ValueType>) {
        // Compile-time floating point values are fiddly, so for safety we require the types to be the same in this
        // case.
        static_assert(
            std::is_same_v<FlagTypeNoCV, ValueType>,
            "The IsEmpty-flag or the specified sentinel (the literal which indicates the empty state) is a floating point type. "
            "Please ensure that they both have the exact same type. For example, if the IsEmpty-flag is a float, "
            "ensure that the sentinel value is also a float and not e.g. a double.");
        return SentinelValue::value;
      }
      else {
        // If you get a compiler error here, then the specified compile-time constant for the empty value has a type
        // that is not compatible with the type used by the optional as IsEmpty-flag.
        return SentinelValue::value;
      }
    }

    static constexpr FlagType valueToIndicateEmpty = ConvertSentinelToFlagType();


  public:
    [[nodiscard]] static bool is_empty(FlagType const & isEmptyFlag) noexcept
    {
      // static_assert: Because tiny::optional requires is_empty() to be noexcept; otherwise, it could not give the same
      // noexcept guarantees as std::optional.
      static_assert(
          noexcept(isEmptyFlag == valueToIndicateEmpty),
          "The comparison operator of the flag type must be noexcept.");

      // clang-tidy apparently does not correctly see placement news, resulting in false positive warnings. So suppress
      // the warning.
      // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
      return isEmptyFlag == valueToIndicateEmpty;
    }

    static void init_empty_flag(FlagType & uninitializedIsEmptyFlagMemory) noexcept
    {
      // static_assert: Because tiny::optional requires init_empty_flag() to be noexcept; otherwise, it could not
      // give the same noexcept guarantees as std::optional.
      static_assert(
          noexcept(*const_cast<std::remove_cv_t<FlagType> *>(&uninitializedIsEmptyFlagMemory) = valueToIndicateEmpty),
          "The assignment operator of the flag type must be noexcept.");
      // Regarding the cast: https://stackoverflow.com/q/63325244/3740047
      ::new (const_cast<void *>(static_cast<void volatile const *>(std::addressof(uninitializedIsEmptyFlagMemory))))
          FlagType(valueToIndicateEmpty);
    }

    static void invalidate_empty_flag(FlagType & isEmptyFlag) noexcept
    {
      // Destroy the object that was previously created in init_empty_flag() (but do not free the
      // associated memory!).
      isEmptyFlag.~FlagType();
    }
  };
} // namespace impl
TINY_OPTIONAL_INLINE_NS_END


//====================================================================================
// optional_flag_manipulator: Library specialization and helpers
//====================================================================================

TINY_OPTIONAL_INLINE_NS_BEGIN
namespace impl
{
  // True if the tiny optional library knows a special sentinel for the given payload type that exploits the type's
  // unused bit patterns to represent the empty state.
  template <class PayloadType>
  inline constexpr bool SentinelForExploitingUnusedBitsIsKnown =
#ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UNUSED_BITS
      (std::is_floating_point_v<PayloadType> && !std::is_same_v<std::remove_cv_t<PayloadType>, long double>)
      || std::is_same_v<std::remove_cv_t<PayloadType>, bool>
      || std::is_pointer_v<PayloadType>; // Pointers and function pointers, but not member pointers or member
                                         // function pointers.
#else
      false;
#endif
} // namespace impl
TINY_OPTIONAL_INLINE_NS_END


// Specialization of optional_flag_manipulator for floats, doubles, bools, pointers and functions pointers. The
// library exploits unused bit patterns for these types to encode the 'IsEmpty' flag without removing any value from
// the value's value range. long double is simply not yet supported (we would need to figure out how NaNs on different
// platorms behave for them).
// Note: Needs to be defined in the tiny namespace, not the tiny::impl namespace.
template <class PayloadType>
struct optional_flag_manipulator<
    PayloadType,
    std::enable_if_t<impl::SentinelForExploitingUnusedBitsIsKnown<PayloadType>>>
  : impl::MemcpyAndCmpFlagManipulator<PayloadType, impl::SentinelForExploitingUnusedBits<std::remove_cv_t<PayloadType>>>
{
};


TINY_OPTIONAL_INLINE_NS_BEGIN
namespace impl
{
  // True if there is a custom flag manipulator was 'registered' for the given payload type.
  template <class PayloadType>
  inline constexpr bool HasCustomInplaceFlagManipulator
      = !std::is_base_of_v<NoCustomInplaceFlagManipulator, optional_flag_manipulator<PayloadType>>;
} // namespace impl
TINY_OPTIONAL_INLINE_NS_END


//====================================================================================
// StorageBase
//====================================================================================

TINY_OPTIONAL_INLINE_NS_BEGIN
namespace impl
{
  // This is the lowest base for TinyOptionalImpl and contains the actual data and some basic manipulation functions.
  // The stuff cannot be part TinyOptionalImpl: To properly implement the constructors of TinyOptionalImpl, we need
  // to derive from various intermediate classes that implement the proper versions of the constructors depending on the
  // payload type. But all of these intermediate classes need to access the storage. Hence, all of them need to derive
  // from some common base class that contains the storage, which is this class here.
  template <class StoredTypeDecomposition, class FlagManipulator>
  struct StorageBase
  {
    using StoredType = typename StoredTypeDecomposition::StoredType;
    using PayloadType = typename StoredTypeDecomposition::PayloadType;
    using FlagType
        = std::remove_reference_t<decltype(StoredTypeDecomposition::GetIsEmptyFlag(std::declval<StoredType &>()))>;
    static constexpr bool is_compressed = sizeof(StoredType) == sizeof(PayloadType);

    // The various helper functions must be noexcept so that we can get the same noexcept specification
    // as for std::optional.
    // Especially FlagManipulator::init_empty_flag() must be noexcept because otherwise an optional could be
    // left in a weird uninitialized state. I.e. setting the IsEmpty-flag must always be possible. Also, various
    // noexcept specifications of member functions would be more complex.
    static_assert(
        noexcept(StoredTypeDecomposition::GetIsEmptyFlag(std::declval<StoredType &>())),
        "StoredTypeDecomposition::GetIsEmptyFlag() must be noexcept");
    static_assert(
        noexcept(StoredTypeDecomposition::GetPayload(std::declval<StoredType &>())),
        "StoredTypeDecomposition::GetPayload() must be noexcept");
    static_assert(
        noexcept(FlagManipulator::is_empty(std::declval<FlagType const &>())),
        "FlagManipulator::is_empty() must be noexcept");
    static_assert(
        noexcept(FlagManipulator::init_empty_flag(std::declval<FlagType &>())),
        "FlagManipulator::init_empty_flag() must be noexcept");
    static_assert(
        noexcept(FlagManipulator::invalidate_empty_flag(std::declval<FlagType &>())),
        "FlagManipulator::invalidate_empty_flag() must be noexcept");

    static_assert(
        !std::is_same_v<std::remove_cv_t<PayloadType>, std::nullopt_t>,
        "Placing a std::nullopt_t in an optional is not allowed by the C++ standard.");
    static_assert(
        !std::is_same_v<std::remove_cv_t<PayloadType>, std::in_place_t>,
        "Placing a std::in_place_t in an optional is not allowed by the C++ standard.");
    static_assert(
        std::is_object_v<PayloadType> && std::is_destructible_v<PayloadType> && !std::is_array_v<PayloadType>,
        "The payload type must meet the C++ requirement 'Destructible'.");

    StorageBase() noexcept
    {
      FlagManipulator::init_empty_flag(GetIsEmptyFlag());
      assert(!has_value());
    }

    template <class... ArgsT>
    explicit StorageBase(std::in_place_t, ArgsT &&... args)
    {
      // Initialize the IsEmpty flag first since invalidate_empty_flag() might depend on it.
      FlagManipulator::init_empty_flag(GetIsEmptyFlag());
      ConstructPayload(std::forward<ArgsT>(args)...);
    }

    template <class FuncT, class ArgT>
    StorageBase(DirectInitializationFromFunctionTag, FuncT && func, ArgT && arg)
    {
      // Initialize the IsEmpty flag first since invalidate_empty_flag() might depend on it.
      FlagManipulator::init_empty_flag(GetIsEmptyFlag());
      ConstructPayloadFromFunction(std::forward<FuncT>(func), std::forward<ArgT>(arg));
    }

    // TODO: The standard wants a trivial destructor if possible.
    ~StorageBase()
    {
      if (has_value()) {
        DestroyPayload();
      }
    }

    // The proper implemention of the following are defined in the derived classes.
    // It cannot be done here because depending on the payload they need to be different, and in C++17
    // there is no way to have multiple versions of each depending on the payload type.
    StorageBase(StorageBase const &) = delete;
    StorageBase(StorageBase &&) = delete;
    StorageBase & operator=(StorageBase const &) = delete;
    StorageBase & operator=(StorageBase &&) = delete;

    [[nodiscard]] bool has_value() const noexcept
    {
      return !FlagManipulator::is_empty(GetIsEmptyFlag());
    }

    [[nodiscard]] FlagType & GetIsEmptyFlag() noexcept
    {
      return StoredTypeDecomposition::GetIsEmptyFlag(this->mStorage);
    }

    [[nodiscard]] FlagType const & GetIsEmptyFlag() const noexcept
    {
      return const_cast<StorageBase &>(*this).GetIsEmptyFlag();
    }

    [[nodiscard]] PayloadType & GetPayload() noexcept
    {
      return StoredTypeDecomposition::GetPayload(this->mStorage);
    }

    [[nodiscard]] PayloadType const & GetPayload() const noexcept
    {
      return const_cast<StorageBase &>(*this).GetPayload();
    }

    void DestroyPayload() noexcept
    {
      GetPayload().~PayloadType();
    }


    struct InitializeIsEmptyFlagScope
    {
      explicit InitializeIsEmptyFlagScope(StorageBase & opt) noexcept
        : opt(opt)
      {
      }

      ~InitializeIsEmptyFlagScope()
      {
        if (!doNotInitialize) {
          FlagManipulator::init_empty_flag(opt.GetIsEmptyFlag());
        }
      }

      StorageBase & opt;
      bool doNotInitialize = false;
    };


    template <class... ArgsT>
    void ConstructPayload(ArgsT &&... args) noexcept(std::is_nothrow_constructible_v<PayloadType, ArgsT...>)
    {
      // We first need to call the prepare function because it might free memory etc.
      // But that means, if the placement new throws, we need to initialize the
      // empty flag again afterwards. This is done by means of InitializeIsEmptyFlagScope.

      // Regarding the volatile casts: https://stackoverflow.com/q/63325244/3740047

      assert(!has_value());
      FlagManipulator::invalidate_empty_flag(GetIsEmptyFlag());

      // clang-tidy apparently does not correctly see placement news, resulting in false positive warnings. So suppress
      // the warning.
      // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign)

      if constexpr (std::is_nothrow_constructible_v<PayloadType, ArgsT...>) {
        // Don't burden the optimizer with optimizing away the InitializeIsEmptyFlagScope if the scope is unnecessary in
        // the first place (i.e. if the construction cannot throw).
        ::new (const_cast<void *>(static_cast<void volatile const *>(std::addressof(GetPayload()))))
            PayloadType(std::forward<ArgsT>(args)...);
      }
      else {
        InitializeIsEmptyFlagScope initScope{*this};
        ::new (const_cast<void *>(static_cast<void volatile const *>(std::addressof(GetPayload()))))
            PayloadType(std::forward<ArgsT>(args)...);
        initScope.doNotInitialize = true;
      }

      // NOLINTEND(clang-analyzer-core.uninitialized.Assign)

      // For example: A tiny optional storing an int and the special value MAX_INT indicates an empty optional.
      // If you then try to put MAX_INT directly into the optional, this assert gets triggered.
      // You must use reset() instead. Otherwise, we could run into inconsistencies with FlagManipulator.
      assert(
          has_value()
          && "Maybe the special sentinel value used to indicate an empty optional was assigned. Use reset() instead.");
    }


    template <class FuncT, class ArgT>
    void ConstructPayloadFromFunction(FuncT && func, ArgT && arg) noexcept(
        std::is_nothrow_constructible_v<PayloadType, std::invoke_result_t<FuncT, ArgT>>)
    {
      assert(!has_value());
      FlagManipulator::invalidate_empty_flag(GetIsEmptyFlag());

      // clang-tidy apparently does not correctly see placement news, resulting in false positive warnings. So suppress
      // the warning.
      // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign)

      // In analogy to ConstructPayload().
      if constexpr (std::is_nothrow_constructible_v<PayloadType, std::invoke_result_t<FuncT, ArgT>>) {
        ::new (const_cast<void *>(static_cast<void volatile const *>(std::addressof(GetPayload()))))
            PayloadType(std::invoke(std::forward<FuncT>(func), std::forward<ArgT>(arg)));
      }
      else {
        InitializeIsEmptyFlagScope initScope{*this};
        ::new (const_cast<void *>(static_cast<void volatile const *>(std::addressof(GetPayload()))))
            PayloadType(std::invoke(std::forward<FuncT>(func), std::forward<ArgT>(arg)));
        initScope.doNotInitialize = true;
      }

      // NOLINTEND(clang-analyzer-core.uninitialized.Assign)

      // For example: A tiny optional storing an int and the special value MAX_INT indicates an empty optional.
      // If you then try to put MAX_INT directly into the optional, this assert gets triggered.
      // You must use reset() instead. Otherwise, we could run into inconsistencies with FlagManipulator.
      assert(
          has_value()
          && "Maybe the special sentinel value used to indicate an empty optional was assigned. Use reset() instead.");
    }


    template <class T>
    void AssignValue(T && v)
    {
      if (has_value()) {
        GetPayload() = std::forward<T>(v);
      }
      else {
        ConstructPayload(std::forward<T>(v));
      }

      // For example: A tiny optional storing an int and the special value MAX_INT indicates an empty optional.
      // If you then try to put MAX_INT directly into the optional, this assert gets triggered.
      // You must use reset() instead. Otherwise, we could run into inconsistencies with FlagManipulator.
      assert(
          has_value()
          && "Maybe the special sentinel value used to indicate an empty optional was assigned. Use reset() instead.");
    }


    void reset() noexcept
    {
      if (has_value()) {
        this->DestroyPayload();
        FlagManipulator::init_empty_flag(GetIsEmptyFlag());
        assert(!has_value());
      }
    }


  private:
    std::remove_const_t<StoredType> mStorage;
  };


  //====================================================================================
  // MoveConstructionBase
  //====================================================================================

  // If the payload is not move constructible then the move constructor of TinyOptionalImpl must not participate in
  // overload resolution, meaning that the copy constructor should be called instead. Without C++20 concepts, there is
  // no way to let the move constructor participate conditionally in overload resolution if it gets defined directly in
  // TinyOptionalImpl. Therefore, TinyOptionalImpl simply defaults the move constructor and inherits from
  // MoveConstructionBase. Using template specialization, MoveConstructionBase either implements the move constructor or
  // deletes it, depending on whether the payload is move constructible or not. The defaulted move constructor of
  // TinyOptionalImpl will then automatically participate in overload resolution or not.

  // First version of MoveConstructionBase if the payload is move constructible.
  template <
      class StoredTypeDecomposition,
      class FlagManipulator,
      bool = std::is_move_constructible_v<typename StoredTypeDecomposition::PayloadType>>
  struct MoveConstructionBase : StorageBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = StorageBase<StoredTypeDecomposition, FlagManipulator>;

    using Base::Base;
    using PayloadType = typename Base::PayloadType;

    MoveConstructionBase() = default;
    MoveConstructionBase(MoveConstructionBase const &) = default;

    // TODO: It is a trivial constructor if std::is_trivially_move_constructible_v<T> is true
    MoveConstructionBase(MoveConstructionBase && rhs) noexcept(std::is_nothrow_move_constructible_v<PayloadType>)
      // Call Base's default constructor since the whole purpose of the present class is to implement
      // the proper move constructor (so the base class does not have a proper one).
      : Base()
    {
      if (rhs.has_value()) {
        this->ConstructPayload(std::move(rhs.GetPayload()));
      }
      else {
        assert(!this->has_value());
      }

      // The C++ standard requires the moved-from optional to be non-empty if it was non-empty before.
      // Also, we might run into problems with deallocations if an optional becomes empty 'magically'.
      assert(this->has_value() == rhs.has_value());
    }
  };


  // Version with deleted move constructor.
  template <class StoredTypeDecomposition, class FlagManipulator>
  struct MoveConstructionBase<StoredTypeDecomposition, FlagManipulator, false>
    : StorageBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = StorageBase<StoredTypeDecomposition, FlagManipulator>;
    using Base::Base;

    MoveConstructionBase() = default;
    MoveConstructionBase(MoveConstructionBase const &) = default;
    MoveConstructionBase(MoveConstructionBase &&) = delete;
  };


  //====================================================================================
  // CopyConstructionBase
  //====================================================================================

  // According to the standard, the copy constructor of optional should be defined as deleted if the
  // payload is not copy constructible. Similar to MoveConstructionBase, we need to use a dedicated base class to
  // actually implement the conditional deletion.

  // First version of CopyConstructionBase if the payload is copy constructible.
  template <
      class StoredTypeDecomposition,
      class FlagManipulator,
      bool = std::is_copy_constructible_v<typename StoredTypeDecomposition::PayloadType>>
  struct CopyConstructionBase : MoveConstructionBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = MoveConstructionBase<StoredTypeDecomposition, FlagManipulator>;

    using PayloadType = typename Base::PayloadType;
    using Base::Base;

    CopyConstructionBase() = default;
    CopyConstructionBase(CopyConstructionBase &&) = default;

    // TODO: It is a trivial constructor if std::is_trivially_copy_constructible_v<T> is true.
    CopyConstructionBase(CopyConstructionBase const & rhs)
      // Call Base's default constructor since the whole purpose of the present class is to implement
      // the proper copy constructor (so the base class does not have a proper one).
      : Base()
    {
      if (rhs.has_value()) {
        this->ConstructPayload(rhs.GetPayload());
      }
      else {
        assert(!this->has_value());
      }
      assert(this->has_value() == rhs.has_value());
    }
  };


  // Version with deleted copy constructor.
  template <class StoredTypeDecomposition, class FlagManipulator>
  struct CopyConstructionBase<StoredTypeDecomposition, FlagManipulator, false>
    : MoveConstructionBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = MoveConstructionBase<StoredTypeDecomposition, FlagManipulator>;
    using Base::Base;

    CopyConstructionBase() = default;
    CopyConstructionBase(CopyConstructionBase const &) = delete;
    CopyConstructionBase(CopyConstructionBase &&) = default;
  };


  //====================================================================================
  // MoveAssignmentBase
  //====================================================================================

  // According to the standard, the move assignment operator of optional should be defined as deleted if the
  // payload is not move constructible and move assignable. Similar to MoveConstructionBase, we need to use a dedicated
  // base class to actually implement the conditional deletion.

  // First version of MoveAssignmentBase if the payload is both move constructible and move assignable.
  template <
      class StoredTypeDecomposition,
      class FlagManipulator,
      bool = std::is_move_constructible_v<typename StoredTypeDecomposition::PayloadType>
             && std::is_move_assignable_v<typename StoredTypeDecomposition::PayloadType>>
  struct MoveAssignmentBase : CopyConstructionBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = CopyConstructionBase<StoredTypeDecomposition, FlagManipulator>;

    using Base::Base;
    using PayloadType = typename Base::PayloadType;

    MoveAssignmentBase() = default;
    MoveAssignmentBase(MoveAssignmentBase const &) = default;
    MoveAssignmentBase(MoveAssignmentBase &&) = default;
    MoveAssignmentBase & operator=(MoveAssignmentBase const &) = default;

    // TODO: It should be trivial if certain conditions are met.
    MoveAssignmentBase & operator=(MoveAssignmentBase && rhs) noexcept(
        std::is_nothrow_move_assignable_v<PayloadType> && std::is_nothrow_move_constructible_v<PayloadType>)
    {
      if (rhs.has_value()) {
        this->AssignValue(std::move(rhs.GetPayload()));
      }
      else {
        this->reset();
      }

      // The C++ standard requires the moved-from optional to be non-empty if it was non-empty before.
      // Also, we might run into problems with deallocations if an optional becomes empty 'magically'.
      assert(this->has_value() == rhs.has_value());

      return *this;
    }
  };


  // Version with deleted move assignment operator.
  template <class StoredTypeDecomposition, class FlagManipulator>
  struct MoveAssignmentBase<StoredTypeDecomposition, FlagManipulator, false>
    : CopyConstructionBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = CopyConstructionBase<StoredTypeDecomposition, FlagManipulator>;
    using Base::Base;

    MoveAssignmentBase() = default;
    MoveAssignmentBase(MoveAssignmentBase const &) = default;
    MoveAssignmentBase(MoveAssignmentBase &&) = default;
    MoveAssignmentBase & operator=(MoveAssignmentBase const &) = default;
    MoveAssignmentBase & operator=(MoveAssignmentBase &&) = delete;
  };


  //====================================================================================
  // CopyAssignmentBase
  //====================================================================================

  // According to the standard, the copy assignment operator of optional should be defined as deleted if the
  // payload is not copy constructible or copy assignable. Similar to MoveConstructionBase, we need to use a dedicated
  // base class to actually implement the conditional deletion.

  // First version of CopyAssignmentBase if the payload is copy constructible and copy assignable.
  template <
      class StoredTypeDecomposition,
      class FlagManipulator,
      bool = std::is_copy_constructible_v<typename StoredTypeDecomposition::PayloadType>
             && std::is_copy_assignable_v<typename StoredTypeDecomposition::PayloadType>>
  struct CopyAssignmentBase : MoveAssignmentBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = MoveAssignmentBase<StoredTypeDecomposition, FlagManipulator>;
    using Base::Base;

    CopyAssignmentBase() = default;
    CopyAssignmentBase(CopyAssignmentBase &&) = default;
    CopyAssignmentBase(CopyAssignmentBase const &) = default;
    CopyAssignmentBase & operator=(CopyAssignmentBase &&) = default;

    // TODO: It should be trivial if certain conditions are met.
    CopyAssignmentBase & operator=(CopyAssignmentBase const & rhs)
    {
      if (rhs.has_value()) {
        this->AssignValue(rhs.GetPayload());
      }
      else {
        this->reset();
      }

      assert(this->has_value() == rhs.has_value());
      return *this;
    }
  };


  // Version with deleted assignment operator.
  template <class StoredTypeDecomposition, class FlagManipulator>
  struct CopyAssignmentBase<StoredTypeDecomposition, FlagManipulator, false>
    : MoveAssignmentBase<StoredTypeDecomposition, FlagManipulator>
  {
    using Base = MoveAssignmentBase<StoredTypeDecomposition, FlagManipulator>;
    using Base::Base;

    CopyAssignmentBase() = default;
    CopyAssignmentBase(CopyAssignmentBase &&) = default;
    CopyAssignmentBase(CopyAssignmentBase const &) = default;
    CopyAssignmentBase & operator=(CopyAssignmentBase &&) = default;

    CopyAssignmentBase & operator=(CopyAssignmentBase const & rhs) = delete;
  };


  //====================================================================================
  // TinyOptionalImpl
  //====================================================================================

  // Actual implementation of optional.
  template <class StoredTypeDecomposition, class FlagManipulator>
  class TinyOptionalImpl : public CopyAssignmentBase<StoredTypeDecomposition, FlagManipulator>
  {
  private:
    using Base = CopyAssignmentBase<StoredTypeDecomposition, FlagManipulator>;

    using PayloadType = typename Base::PayloadType;

    using Base::ConstructPayload;
    using Base::GetIsEmptyFlag;
    using Base::GetPayload;

    // Helper for the constructor taking a U&& value.
    // Compare the std::optional standard for the conditions.
    template <class U>
    using EnableConvertingConstructor = std::bool_constant<
        std::is_constructible_v<PayloadType, U>
        && !std::is_same_v<my_remove_cvref_t<U>, std::in_place_t>
        // The next ensures that the copy or move constructor is called instead.
        && !std::is_same_v<my_remove_cvref_t<U>, TinyOptionalImpl>
        // The following is not in the standard. But we need it because we derive from TinyOptionalImpl,
        // and the constructor where this is used is incorrectly inherited in some versions of MSVC (in which
        // case std::is_same is not sufficient because it does not recognize if U is derived from
        // TinyOptionalImpl); compare https://stackoverflow.com/a/71010787.
        && !std::is_base_of_v<TinyOptionalImpl, my_remove_cvref_t<U>>>;


  protected:
    template <class TinyOptionalType, class U>
    using EnableConvertingAssignment = std::bool_constant<
        !std::is_same_v<my_remove_cvref_t<U>, TinyOptionalType> && std::is_constructible_v<PayloadType, U>
        && std::is_assignable_v<PayloadType &, U>
        // The following ensures that, if e.g. PayloadType==int, "o = {};" does not call this assignment operator here
        // with int initialized to 0, but instead constructs and then assigns an empty optional.
        // Compare https://stackoverflow.com/q/33511641/3740047
        && (!std::is_scalar_v<PayloadType> || !std::is_same_v<std::decay_t<U>, PayloadType>)>;


  public:
    using value_type = PayloadType;
    using Base::has_value;
    using Base::reset;

    // Marker that can be useful to check if a given type is a TinyOptionalImpl.
    static constexpr bool is_tiny_optional = true;

    // true if the optional is not using more space than the payload.
    using Base::is_compressed;

  public:
    // Implemented in base classes.
    TinyOptionalImpl() = default;
    TinyOptionalImpl(TinyOptionalImpl const &) = default;
    TinyOptionalImpl(TinyOptionalImpl &&) = default;
    TinyOptionalImpl & operator=(TinyOptionalImpl const &) = default;
    TinyOptionalImpl & operator=(TinyOptionalImpl &&) = default;
    ~TinyOptionalImpl() = default;


    TinyOptionalImpl(std::nullopt_t) noexcept
      : TinyOptionalImpl()
    {
    }


    template <class... ArgsT, class = std::enable_if_t<std::is_constructible_v<PayloadType, ArgsT...>>>
    explicit TinyOptionalImpl(std::in_place_t, ArgsT &&... args)
      : Base(std::in_place, std::forward<ArgsT>(args)...)
    {
    }


    template <
        class U,
        class... ArgsT,
        class = std::enable_if_t<std::is_constructible_v<PayloadType, std::initializer_list<U> &, ArgsT...>>>
    explicit TinyOptionalImpl(std::in_place_t, std::initializer_list<U> ilist, ArgsT &&... args)
      : Base(std::in_place, ilist, std::forward<ArgsT>(args)...)
    {
    }


    // Special constructor only to be used by transform().
    template <class FuncT, class ArgT>
    TinyOptionalImpl(DirectInitializationFromFunctionTag tag, FuncT && func, ArgT && arg)
      : Base(tag, std::forward<FuncT>(func), std::forward<ArgT>(arg))
    {
    }


    // Non-explicit converting constructor for types U that are implicitly convertible to the payload.
    template <
        class U = PayloadType,
        std::enable_if_t<EnableConvertingConstructor<U>::value && std::is_convertible_v<U, PayloadType>, int> = 0>
    TinyOptionalImpl(U && v)
      : Base(std::in_place, std::forward<U>(v))
    {
    }

    // Explicit constructor for types U that are not implicitly convertible to the payload.
    template <
        class U = PayloadType,
        std::enable_if_t<EnableConvertingConstructor<U>::value && !std::is_convertible_v<U, PayloadType>, int> = 0>
    explicit TinyOptionalImpl(U && v)
      : Base(std::in_place, std::forward<U>(v))
    {
    }


    // TODO: What about the converting copy and move constructors and assignments?
    // Problem: Assume we have
    //    optional<unsigned, 1> o1;
    // So the value 1 is used to indicate an empty state. And we have
    //    optional<int, 42> o2 = 1;
    // So o2 is not empty and contains the value 1.
    // What should happen at 'o1 = o2'? o1 cannot contain the value 1 because it indicates an empty state,
    // but o2 is not empty, so o1 should not be empty, too?
    // - Throw an exception? But how to actually detect this? Querying that has_value() is the same afterwards
    //   can be dangerous: We could run into inconsistencies with FlagManipulator.
    // - Somehow defer to the FlagManipulator and/or StoredTypeDecomposition?
    // - Do not implement them?
    // - In principle we could allow all conversions if the rhs did not use any swallowed value
    //   (meaning either the rhs exploits unused bits or uses a separate bool or is a std::optional).
    //   If not, issue a static_assert. Or throw an exception?
    // For now we do not implement them.


    TinyOptionalImpl & operator=(std::nullopt_t) noexcept
    {
      reset();
      return *this;
    }


    template <class U = PayloadType, std::enable_if_t<EnableConvertingAssignment<TinyOptionalImpl, U>::value, int> = 0>
    TinyOptionalImpl & operator=(U && v)
    {
      this->AssignValue(std::forward<U>(v));
      return *this;
    }


    template <class... ArgsT>
    PayloadType & emplace(ArgsT &&... args)
    {
      reset();
      ConstructPayload(std::forward<ArgsT>(args)...);
      assert(has_value());
      return GetPayload();
    }


    template <
        class U,
        class... ArgsT,
        class = std::enable_if_t<std::is_constructible_v<PayloadType, std::initializer_list<U> &, ArgsT...>>>
    PayloadType & emplace(std::initializer_list<U> ilist, ArgsT &&... args)
    {
      reset();
      ConstructPayload(ilist, std::forward<ArgsT>(args)...);
      assert(has_value());
      return GetPayload();
    }


    explicit operator bool() const noexcept
    {
      return has_value();
    }


    [[nodiscard]] PayloadType * operator->() noexcept
    {
      assert(has_value() && "operator->() called on an empty optional");
      return std::addressof(GetPayload());
    }

    [[nodiscard]] PayloadType const * operator->() const noexcept
    {
      assert(has_value() && "operator->() called on an empty optional");
      return std::addressof(GetPayload());
    }


    [[nodiscard]] PayloadType & operator*() & noexcept
    {
      assert(has_value() && "operator*() called on an empty optional");
      return GetPayload();
    }

    [[nodiscard]] PayloadType const & operator*() const & noexcept
    {
      assert(has_value() && "operator*() called on an empty optional");
      return GetPayload();
    }

    [[nodiscard]] PayloadType && operator*() && noexcept
    {
      assert(has_value() && "operator*() called on an empty optional");
      return std::move(GetPayload());
    }

    [[nodiscard]] PayloadType const && operator*() const && noexcept
    {
      assert(has_value() && "operator*() called on an empty optional");
      return std::move(GetPayload());
    }


    [[nodiscard]] PayloadType & value() &
    {
      if (!has_value()) {
        throw std::bad_optional_access{};
      }
      return GetPayload();
    }

    [[nodiscard]] PayloadType const & value() const &
    {
      if (!has_value()) {
        throw std::bad_optional_access{};
      }
      return GetPayload();
    }

    [[nodiscard]] PayloadType && value() &&
    {
      if (!has_value()) {
        throw std::bad_optional_access{};
      }
      return std::move(GetPayload());
    }

    [[nodiscard]] PayloadType const && value() const &&
    {
      if (!has_value()) {
        throw std::bad_optional_access{};
      }
      return std::move(GetPayload());
    }


    template <class U>
    [[nodiscard]] std::remove_cv_t<PayloadType> value_or(U && defaultValue) const &
    {
      static_assert(
          std::is_copy_constructible_v<PayloadType>,
          "PayloadType must be copy constructible for value_or().");
      static_assert(std::is_convertible_v<U, PayloadType>, "U must be convertible to PayloadType for value_or().");

      return has_value() ? GetPayload() : static_cast<std::remove_cv_t<PayloadType>>(std::forward<U>(defaultValue));
    }

    template <class U>
    [[nodiscard]] std::remove_cv_t<PayloadType> value_or(U && defaultValue) &&
    {
      static_assert(
          std::is_move_constructible_v<PayloadType>,
          "PayloadType must be move constructible for value_or().");
      static_assert(std::is_convertible_v<U, PayloadType>, "U must be convertible to PayloadType for value_or().");

      return has_value() ? std::move(GetPayload())
                         : static_cast<std::remove_cv_t<PayloadType>>(std::forward<U>(defaultValue));
    }


    void swap(TinyOptionalImpl & other) noexcept(
        std::is_nothrow_move_constructible_v<PayloadType> && std::is_nothrow_swappable_v<PayloadType>)
    {
      static_assert(std::is_move_constructible_v<PayloadType> && std::is_swappable_v<PayloadType>);

      bool const thisHasValue = has_value();
      if (thisHasValue == other.has_value()) {
        if (thisHasValue) {
          using std::swap;
          swap(GetPayload(), other.GetPayload());
        }
      }
      else {
        if (thisHasValue) {
          other.ConstructPayload(std::move(GetPayload()));
          reset();
        }
        else {
          ConstructPayload(std::move(other.GetPayload()));
          other.reset();
        }
      }
    }


    template <class F>
    constexpr auto and_then(F && f) &
    {
      using ReturnTypeOfF = my_remove_cvref_t<std::invoke_result_t<F, PayloadType &>>;
      static_assert(IsSomeOptional<ReturnTypeOfF>, "The standard requires 'f' to return an optional.");
      if (has_value()) {
        return std::invoke(std::forward<F>(f), **this);
      }
      else {
        return ReturnTypeOfF();
      }
    }

    template <class F>
    constexpr auto and_then(F && f) const &
    {
      using ReturnTypeOfF = my_remove_cvref_t<std::invoke_result_t<F, PayloadType const &>>;
      static_assert(IsSomeOptional<ReturnTypeOfF>, "The standard requires 'f' to return an optional.");
      if (has_value()) {
        return std::invoke(std::forward<F>(f), **this);
      }
      else {
        return ReturnTypeOfF();
      }
    }

    template <class F>
    constexpr auto and_then(F && f) &&
    {
      using ReturnTypeOfF = my_remove_cvref_t<std::invoke_result_t<F, PayloadType>>;
      static_assert(IsSomeOptional<ReturnTypeOfF>, "The standard requires 'f' to return an optional.");
      if (has_value()) {
        return std::invoke(std::forward<F>(f), std::move(**this));
      }
      else {
        return ReturnTypeOfF();
      }
    }

    template <class F>
    constexpr auto and_then(F && f) const &&
    {
      using ReturnTypeOfF = my_remove_cvref_t<std::invoke_result_t<F, PayloadType const>>;
      static_assert(IsSomeOptional<ReturnTypeOfF>, "The standard requires 'f' to return an optional.");
      if (has_value()) {
        return std::invoke(std::forward<F>(f), std::move(**this));
      }
      else {
        return ReturnTypeOfF();
      }
    }

    template <class F>
    constexpr auto transform(F && f) &
    {
      using U = std::remove_cv_t<std::invoke_result_t<F, PayloadType &>>;

      static_assert(!std::is_same_v<U, std::nullopt_t>, "The standard requires 'f' to not return a std::nullopt_t.");
      static_assert(!std::is_same_v<U, std::in_place_t>, "The standard requires 'f' to not return a std::in_place_t.");
      static_assert(
          std::is_object_v<U> && !std::is_array_v<U>,
          "The standard requires 'f' to return a non-array object type.");

      // We need to return some optional type. The standard does not allow the user to influence it. Nevertheless, we
      // could probably add additional template parameters to transform() to allow some customization. But for now
      // we simply use the generic one of this library, i.e. tiny::optional. The user can always simply use and_then()
      // to return a specific optional type.
      if (has_value()) {
        return ::tiny::optional<U>(DirectInitializationFromFunctionTag{}, std::forward<F>(f), **this);
      }
      else {
        return ::tiny::optional<U>();
      }
    }

    template <class F>
    constexpr auto transform(F && f) const &
    {
      using U = std::remove_cv_t<std::invoke_result_t<F, PayloadType const &>>;

      static_assert(!std::is_same_v<U, std::nullopt_t>, "The standard requires 'f' to not return a std::nullopt_t.");
      static_assert(!std::is_same_v<U, std::in_place_t>, "The standard requires 'f' to not return a std::in_place_t.");
      static_assert(
          std::is_object_v<U> && !std::is_array_v<U>,
          "The standard requires 'f' to return a non-array object type.");

      // Regarding the return of ::tiny::optional, see first overload.
      if (has_value()) {
        return ::tiny::optional<U>(DirectInitializationFromFunctionTag{}, std::forward<F>(f), **this);
      }
      else {
        return ::tiny::optional<U>();
      }
    }

    template <class F>
    constexpr auto transform(F && f) &&
    {
      using U = std::remove_cv_t<std::invoke_result_t<F, PayloadType>>;

      static_assert(!std::is_same_v<U, std::nullopt_t>, "The standard requires 'f' to not return a std::nullopt_t.");
      static_assert(!std::is_same_v<U, std::in_place_t>, "The standard requires 'f' to not return a std::in_place_t.");
      static_assert(
          std::is_object_v<U> && !std::is_array_v<U>,
          "The standard requires 'f' to return a non-array object type.");

      // Regarding the return of ::tiny::optional, see first overload.
      if (has_value()) {
        return ::tiny::optional<U>(DirectInitializationFromFunctionTag{}, std::forward<F>(f), std::move(**this));
      }
      else {
        return ::tiny::optional<U>();
      }
    }

    template <class F>
    constexpr auto transform(F && f) const &&
    {
      using U = std::remove_cv_t<std::invoke_result_t<F, PayloadType const>>;

      static_assert(!std::is_same_v<U, std::nullopt_t>, "The standard requires 'f' to not return a std::nullopt_t.");
      static_assert(!std::is_same_v<U, std::in_place_t>, "The standard requires 'f' to not return a std::in_place_t.");
      static_assert(
          std::is_object_v<U> && !std::is_array_v<U>,
          "The standard requires 'f' to return a non-array object type.");

      // Regarding the return of ::tiny::optional, see first overload.
      if (has_value()) {
        return ::tiny::optional<U>(DirectInitializationFromFunctionTag{}, std::forward<F>(f), std::move(**this));
      }
      else {
        return ::tiny::optional<U>();
      }
    }


#ifdef TINY_OPTIONAL_ENABLE_ORELSE
    template <class F>
      requires(std::invocable<F> && std::copy_constructible<value_type>)
    constexpr TinyOptionalImpl or_else(F && f) const &
    {
      static_assert(
          std::is_same_v<my_remove_cvref_t<std::invoke_result_t<F>>, TinyOptionalImpl>,
          "The function F passed to OPT::or_else(F&&) needs to return an optional of the same type OPT.");
      return has_value() ? *this : std::forward<F>(f)();
    }

    template <class F>
      requires(std::invocable<F> && std::move_constructible<value_type>)
    constexpr TinyOptionalImpl or_else(F && f) &&
    {
      static_assert(
          std::is_same_v<my_remove_cvref_t<std::invoke_result_t<F>>, TinyOptionalImpl>,
          "The function F passed to OPT::or_else(F&&) needs to return an optional of the same type OPT.");
      return has_value() ? std::move(*this) : std::forward<F>(f)();
    }
#endif
  };


  // clang-format off
  template <
      class StoredTypeDecomposition,
      class FlagManipulator,
      std::enable_if_t<
          std::is_move_constructible_v<typename StoredTypeDecomposition::PayloadType> 
          && std::is_swappable_v<typename StoredTypeDecomposition::PayloadType>,
        int> = 0>
  void swap(
      TinyOptionalImpl<StoredTypeDecomposition, FlagManipulator> & lhs,
      TinyOptionalImpl<StoredTypeDecomposition, FlagManipulator> & rhs) 
    noexcept(noexcept(lhs.swap(rhs)))
  {
    lhs.swap(rhs);
  }
  // clang-format on

} // namespace impl
TINY_OPTIONAL_INLINE_NS_END


//====================================================================================
// SelectDecomposition
//====================================================================================

TINY_OPTIONAL_INLINE_NS_BEGIN
namespace impl
{

  // Since the template specialization of SelectDecomposition are rather non-trivial, we want
  // some easy direct way of testing if the correct one was selected (i.e. while we modify
  // the code of SelectDecomposition). To this end, each (valid) specialization gets its
  // own enum value assign that we can static_assert against.
  enum SelectedDecompositionTest
  {
    NoArgsAndBehavesAsStdOptional,
    NoArgsAndHasCustomFlagManipulator,
    SentinelValueSpecifiedForInplaceSwallowingForTypeWithCustomFlagManipulator,
    SentinelValueSpecifiedForInplaceSwallowing,
    MemPtrSpecifiedToVariableWithCustomFlagManipulator,
    MemPtrSpecifiedToVariableWithoutCustomFlagManipulator,
    SentinelValueAndMemPtrSpecifiedForInplaceSwallowing,
    SentinelValueAndMemPtrSpecifiedForInplaceSwallowingForTypeWithCustomFlagManipulator
  };


  // Given the options chosen by the user, selects the proper StoredTypeDecomposition and FlagManipulator
  // with which to instantiate the TinyOptionalImpl. I.e. this is responsible for choosing if the 'IsEmpty'
  // flag should be stored inplace or out-of-place, and how it should be manipulated.
  //
  // PayloadType: The actual value that the user wants to store in the optional.
  // SentinelValue: Either UseDefaultType to let the implementation here choose the most suitable value to indicate an
  //             empty state. Otherwise, SentinelValue::value must be the value to use to indicate the empty state.
  // memPtrToFlag: Either UseDefaultValue if the 'IsEmpty'-flag should not be stored in a member variable of the
  //             payload. Otherwise, this must be a member pointer to the member variable where to store the
  //             'IsEmpty'-flag in-place.
  template <class PayloadType, class SentinelValue, auto memPtrToFlag, class = void>
  struct SelectDecomposition
  {
    static_assert(
        always_false<PayloadType>,
        "Failed to select proper specialization of decomposition for tiny optional.");
  };


  template <class PayloadType>
  struct SelectDecomposition<
      PayloadType,
      UseDefaultType,
      UseDefaultValue,
      std::enable_if_t<!HasCustomInplaceFlagManipulator<PayloadType>>>
  {
    static constexpr auto test = SelectedDecompositionTest::NoArgsAndBehavesAsStdOptional;

    using StoredTypeDecomposition = DecompositionForSeparateFlag<PayloadType>;
    using FlagManipulator = SeparateFlagManipulator;
  };


  template <class PayloadType>
  struct SelectDecomposition<
      PayloadType,
      UseDefaultType,
      UseDefaultValue,
      std::enable_if_t<HasCustomInplaceFlagManipulator<PayloadType>>>
  {
    static constexpr auto test = SelectedDecompositionTest::NoArgsAndHasCustomFlagManipulator;

    using StoredTypeDecomposition = InplaceStoredTypeDecomposition<PayloadType>;
    using FlagManipulator = optional_flag_manipulator<PayloadType>;
  };


  template <class PayloadType, class SentinelValue>
  struct SelectDecomposition<
      PayloadType,
      SentinelValue,
      UseDefaultValue,
      std::enable_if_t<HasCustomInplaceFlagManipulator<PayloadType> && !std::is_same_v<SentinelValue, UseDefaultType>>>
  {
    // The user specified a sentinel value to swallow for a type that has a custom flag manipulator (e.g. unused bits).
    // So the swallowing is in principle unnecessary. But maybe the user deliberately wants to mark
    // the value as 'invalid', so we do not static_assert this but instead support it. But we ignore
    // the custom flag manipulator and use ordinary assignment; we currently don't support an API to allow the
    // user to handle the sentinel value manually.

    static constexpr auto test
        = SelectedDecompositionTest::SentinelValueSpecifiedForInplaceSwallowingForTypeWithCustomFlagManipulator;

    using StoredTypeDecomposition = InplaceStoredTypeDecomposition<PayloadType>;
    using FlagManipulator = AssignmentFlagManipulator<PayloadType, SentinelValue>;
  };


  template <class PayloadType, class SentinelValue>
  struct SelectDecomposition<
      PayloadType,
      SentinelValue,
      UseDefaultValue,
      std::enable_if_t<!HasCustomInplaceFlagManipulator<PayloadType> && !std::is_same_v<SentinelValue, UseDefaultType>>>
  {
    static constexpr auto test = SelectedDecompositionTest::SentinelValueSpecifiedForInplaceSwallowing;

    using StoredTypeDecomposition = InplaceStoredTypeDecomposition<PayloadType>;
    using FlagManipulator = AssignmentFlagManipulator<PayloadType, SentinelValue>;
  };


  // clang-format off
  template <class PayloadType, auto memPtrToFlag>
  struct SelectDecomposition<
      PayloadType,
      UseDefaultType,
      memPtrToFlag,
      std::enable_if_t<
          std::is_member_object_pointer_v<decltype(memPtrToFlag)> 
          && HasCustomInplaceFlagManipulator<typename MemberPointerFragments<memPtrToFlag>::VariableType>>>
  // clang-format on
  {
    static constexpr auto test = SelectedDecompositionTest::MemPtrSpecifiedToVariableWithCustomFlagManipulator;

    static_assert(
        std::is_same_v<PayloadType, typename MemberPointerFragments<memPtrToFlag>::ClassType>,
        "The flag given by the member-pointer is not a member of the payload type.");
    using MemVarType = typename MemberPointerFragments<memPtrToFlag>::VariableType;

#ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
    using StoredTypeDecomposition = InplaceDecompositionViaMemPtr<PayloadType, memPtrToFlag>;
    using FlagManipulator = optional_flag_manipulator<MemVarType>;
#else
    using StoredTypeDecomposition = DecompositionForSeparateFlag<PayloadType>;
    using FlagManipulator = SeparateFlagManipulator;
#endif
  };


  // clang-format off
  template <class PayloadType, auto memPtrToFlag>
  struct SelectDecomposition<
      PayloadType,
      UseDefaultType,
      memPtrToFlag,
      std::enable_if_t<
          std::is_member_object_pointer_v<decltype(memPtrToFlag)> 
          && !HasCustomInplaceFlagManipulator<typename MemberPointerFragments<memPtrToFlag>::VariableType>>>
  // clang-format on
  {
#ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
    // The empty state cannot be stored in a member variable if we do not know a sentinel value (i.e. if the assert
    // triggers, we do know neither a builtin sentinel nor the user specified a custom one).
    static_assert(
        always_false<PayloadType>,
        "The type of the member variable given by the member-pointer cannot be used as flag if you do not specify a custom SentinelValue.");
#else
    static constexpr auto test = SelectedDecompositionTest::MemPtrSpecifiedToVariableWithoutCustomFlagManipulator;
    static_assert(
        std::is_same_v<PayloadType, typename MemberPointerFragments<memPtrToFlag>::ClassType>,
        "The flag given by the member-pointer is not a member of the payload type.");

    using StoredTypeDecomposition = DecompositionForSeparateFlag<PayloadType>;
    using FlagManipulator = SeparateFlagManipulator;
#endif
  };


  // clang-format off
  template <class PayloadType, class SentinelValue, auto memPtrToFlag>
  struct SelectDecomposition<
      PayloadType,
      SentinelValue,
      memPtrToFlag,
      std::enable_if_t<
          !std::is_same_v<SentinelValue, UseDefaultType> 
          && std::is_member_object_pointer_v<decltype(memPtrToFlag)>
          && HasCustomInplaceFlagManipulator<typename MemberPointerFragments<memPtrToFlag>::VariableType>>>
  // clang-format on
  {
    // The user specified a sentinel value to swallow for a type that has a custom flag manipulator (e.g. unused bits).
    // So the swallowing is in principle unnecessary. But maybe the user deliberately wants to mark
    // the value as 'invalid', so we do not static_assert this but instead support it. But we ignore
    // the custom flag manipulator and use ordinary assignment; we currently don't support an API to allow the
    // user to handle the sentinel value manually.

    static constexpr auto test = SelectedDecompositionTest::
        SentinelValueAndMemPtrSpecifiedForInplaceSwallowingForTypeWithCustomFlagManipulator;

    static_assert(
        std::is_same_v<PayloadType, typename MemberPointerFragments<memPtrToFlag>::ClassType>,
        "The flag given by the member-pointer is not a member of the payload type.");
    using MemVarType = typename MemberPointerFragments<memPtrToFlag>::VariableType;

#ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
    using StoredTypeDecomposition = InplaceDecompositionViaMemPtr<PayloadType, memPtrToFlag>;
    using FlagManipulator = AssignmentFlagManipulator<MemVarType, SentinelValue>;
#else
    using StoredTypeDecomposition = DecompositionForSeparateFlag<PayloadType>;
    using FlagManipulator = SeparateFlagManipulator;
#endif
  };


  // clang-format off
  template <class PayloadType, class SentinelValue, auto memPtrToFlag>
  struct SelectDecomposition<
      PayloadType,
      SentinelValue,
      memPtrToFlag,
      std::enable_if_t<
          !std::is_same_v<SentinelValue, UseDefaultType> 
          && std::is_member_object_pointer_v<decltype(memPtrToFlag)> 
          && !HasCustomInplaceFlagManipulator<typename MemberPointerFragments<memPtrToFlag>::VariableType>>>
  // clang-format on
  {
    static constexpr auto test = SelectedDecompositionTest::SentinelValueAndMemPtrSpecifiedForInplaceSwallowing;

    static_assert(
        std::is_same_v<PayloadType, typename MemberPointerFragments<memPtrToFlag>::ClassType>,
        "The flag given by the member-pointer is not a member of the payload type.");
    using MemVarType = typename MemberPointerFragments<memPtrToFlag>::VariableType;

#ifndef TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_MEMBER
    using StoredTypeDecomposition = InplaceDecompositionViaMemPtr<PayloadType, memPtrToFlag>;
    using FlagManipulator = AssignmentFlagManipulator<MemVarType, SentinelValue>;
#else
    using StoredTypeDecomposition = DecompositionForSeparateFlag<PayloadType>;
    using FlagManipulator = SeparateFlagManipulator;
#endif
  };


  //====================================================================================
  // SelectSentinelValueAndMemPtrFromConstants
  //====================================================================================

  // If the second template parameter of optional is a member-pointer, it indicates the position of the
  // 'IsEmpty'-flag, and the third parameter might contain the specific sentinel value.
  // However, if the second parameter is no a member-pointer, it contains the specific sentinel value (or
  // UseDefaultValue). Figuring out the member-pointer (if any) and the sentinel value (if any) is performed here.
  template <class PayloadType_, auto sentinelOrMemPtr, auto irrelevantOrSentinel>
  struct SelectSentinelValueAndMemPtrFromConstants
  {
    using PayloadType = PayloadType_;

    using sentinelOrMemPtrType = decltype(sentinelOrMemPtr);
    using irrelevantOrSentinelType = decltype(irrelevantOrSentinel);

    // if (sentinelOrMemPtr is a memPtr)
    //    if (irrelevantOrSentinel == UseDefaultValue)
    //        SentinelValue = UseDefaultType
    //    else
    //        SentinelValue = irrelevantOrSentinel
    // else
    //    if (sentinelOrMemPtr == UseDefaultValue)
    //        SentinelValue = UseDefaultType
    //    else
    //        SentinelValue = sentinelOrMemPtr
    using SentinelValue = std::conditional_t<
        std::is_member_object_pointer_v<sentinelOrMemPtrType>,
        std::conditional_t<
            std::is_same_v<irrelevantOrSentinelType, UseDefaultType>,
            UseDefaultType,
            std::integral_constant<irrelevantOrSentinelType, irrelevantOrSentinel>>,
        std::conditional_t<
            std::is_same_v<sentinelOrMemPtrType, UseDefaultType>,
            UseDefaultType,
            std::integral_constant<sentinelOrMemPtrType, sentinelOrMemPtr>>>;

    static constexpr auto memPtr
        = value_conditional<std::is_member_object_pointer_v<sentinelOrMemPtrType>, sentinelOrMemPtr, UseDefaultValue>::
            value;
  };
} // namespace impl
TINY_OPTIONAL_INLINE_NS_END


//====================================================================================
// optional_sentinel_via_type
//====================================================================================

TINY_OPTIONAL_INLINE_NS_BEGIN
namespace impl
{

  // Shortcut for the following type definitions where the two template parameters of TinyOptionalImpl
  // are combined in one type.
  template <class Options>
  using TinyOptionalImplCombined
      = TinyOptionalImpl<typename Options::StoredTypeDecomposition, typename Options::FlagManipulator>;
} // namespace impl


// Basically the same as optional, except that the value for the sentinel flag needs to be specified via a type
// (i.e. SentinelValue::value must be the sentinel).
template <class PayloadType, class SentinelValue, auto memPtr = UseDefaultValue>
using optional_sentinel_via_type
    = impl::TinyOptionalImplCombined<impl::SelectDecomposition<PayloadType, SentinelValue, memPtr>>;
TINY_OPTIONAL_INLINE_NS_END


//====================================================================================
// optional
//====================================================================================

TINY_OPTIONAL_INLINE_NS_BEGIN
namespace impl
{
  // Shortcut for optional definition below so that the SelectSentinelValueAndMemPtrFromConstants
  // needs to be specified only once.
  template <class Selection>
  using TinyOptionalFromSelection = optional_sentinel_via_type<
      typename Selection::PayloadType,
      typename Selection::SentinelValue,
      Selection::memPtr>;
} // namespace impl


// Main tiny optional type to be used by users of the library.
// Optionally allows to specify the value for the 'IsEmpty'-flag (called 'sentinel') via a literal, and a member-pointer
// to indicate where the 'IsEmpty'-flag should be stored.
//
// Implementation note: Unfortunately, we cannot use a simple 'using optional = TinyOptionalFromSelection<...>'
// because such an optional would have no usable deduction guides. I.e. an expression such as
//   optional o{5};
// would not compile. This even holds for C++20 where certain aliases do get deduction guides, but fails here
// because ultimately the template arguments for TinyOptionalImpl are computed via
// SelectDecomposition<...>::StoredTypeDecomposition etc, which results in expressions that prevent their generation.
//
// Moreover, we deliberately use public rather than private inheritance. Assigning an optional to a TinyOptionalImpl
// (which might happen because of the other typedefs for TinyOptionalImpl that the user is supposed to use, such as
// optional_sentinel_via_type) makes sense, especially since it only works if the template parameter of
// optional::Base and the other TinyOptionalImpl are the same. Splicing is irrelevant because optional does not
// contain additional data members. In fact, as explained above, we actually would like optional to be a typedef to
// TinyOptionalImpl, but which is not possible due to the deduction guides. Moreover, public inheritance allows to
// re-use various functions, such as the comparison operators, without re-defining them for all possible combinations.
template <
    class PayloadType_,
    auto sentinelOrMemPtr /*= UseDefaultValue (template default value defined at first declaration)*/,
    auto irrelevantOrSentinel /*= UseDefaultValue (template default value defined at first declaration)*/>
class optional
  : public impl::TinyOptionalFromSelection<
        impl::SelectSentinelValueAndMemPtrFromConstants<PayloadType_, sentinelOrMemPtr, irrelevantOrSentinel>>
{
private:
  using Base = impl::TinyOptionalFromSelection<
      impl::SelectSentinelValueAndMemPtrFromConstants<PayloadType_, sentinelOrMemPtr, irrelevantOrSentinel>>;

public:
  using typename Base::value_type;
  static_assert(std::is_same_v<PayloadType_, value_type>);

  optional() = default;
  optional(optional const &) = default;
  optional(optional &&) = default;
  optional & operator=(optional const &) = default;
  optional & operator=(optional &&) = default;
  ~optional() = default;

  using Base::Base;

  using Base::operator->;
  using Base::operator*;
  using Base::operator bool;
  using Base::and_then;
  using Base::emplace;
  using Base::has_value;
  using Base::reset;
  using Base::swap;
  using Base::transform;
  using Base::value;
  using Base::value_or;


  optional & operator=(std::nullopt_t) noexcept
  {
    Base::operator=(std::nullopt);
    return *this;
  }


  template <
      class U = PayloadType_,
      std::enable_if_t<Base::template EnableConvertingAssignment<optional, U>::value, int> = 0>
  optional & operator=(U && v)
  {
    Base::operator=(std::forward<U>(v));
    return *this;
  }


#ifdef TINY_OPTIONAL_ENABLE_ORELSE
  template <class F>
    requires(std::invocable<F> && std::copy_constructible<value_type>)
  constexpr optional or_else(F && f) const &
  {
    static_assert(
        std::is_same_v<impl::my_remove_cvref_t<std::invoke_result_t<F>>, optional>,
        "The function F passed to OPT::or_else(F&&) needs to return an optional of the same type OPT.");
    return has_value() ? *this : std::forward<F>(f)();
  }

  template <class F>
    requires(std::invocable<F> && std::move_constructible<value_type>)
  constexpr optional or_else(F && f) &&
  {
    static_assert(
        std::is_same_v<impl::my_remove_cvref_t<std::invoke_result_t<F>>, optional>,
        "The function F passed to OPT::or_else(F&&) needs to return an optional of the same type OPT.");
    return has_value() ? std::move(*this) : std::forward<F>(f)();
  }
#endif
};


// Deduction guide.
template <class T>
optional(T) -> optional<T, UseDefaultValue, UseDefaultValue>;


template <class PayloadType, auto sentinelOrMemPtr = UseDefaultValue, auto irrelevantOrSentinel = UseDefaultValue>
[[nodiscard]] optional<std::decay_t<PayloadType>, sentinelOrMemPtr, irrelevantOrSentinel> make_optional(
    PayloadType && v)
{
  return optional<std::decay_t<PayloadType>, sentinelOrMemPtr, irrelevantOrSentinel>{std::forward<PayloadType>(v)};
}


template <
    class PayloadType,
    auto sentinelOrMemPtr = UseDefaultValue,
    auto irrelevantOrSentinel = UseDefaultValue,
    class... ArgsT>
[[nodiscard]] optional<PayloadType, sentinelOrMemPtr, irrelevantOrSentinel> make_optional(ArgsT &&... args)
{
  return optional<PayloadType, sentinelOrMemPtr, irrelevantOrSentinel>{std::in_place, std::forward<ArgsT>(args)...};
}


template <
    class PayloadType,
    auto sentinelOrMemPtr = UseDefaultValue,
    auto irrelevantOrSentinel = UseDefaultValue,
    class U,
    class... ArgsT>
[[nodiscard]] optional<PayloadType, sentinelOrMemPtr, irrelevantOrSentinel> make_optional(
    std::initializer_list<U> il,
    ArgsT &&... args)
{
  return optional<PayloadType, sentinelOrMemPtr, irrelevantOrSentinel>{std::in_place, il, std::forward<ArgsT>(args)...};
}


//====================================================================================
// optional_inplace
//====================================================================================

// A tiny optional that always stores the value inplace, but the user needs to supply the logic to check and set the
// 'IsEmpty'-flag.
template <class PayloadType, class FlagManipulator>
using optional_inplace = impl::TinyOptionalImpl<impl::InplaceStoredTypeDecomposition<PayloadType>, FlagManipulator>;


//====================================================================================
// optional "always in-place"
//====================================================================================

namespace impl
{
  template <class PayloadType>
  struct MissingSentinel
  {
    static_assert(
        always_false<PayloadType>,
        "optional_aip: No automatic sentinel for the PayloadType available. You need to specify one manually, e.g. optional_aip<char, 0>, "
        "or specialize tiny::optional_flag_manipulator. (By definition, optional_aip always may never use a separate bool to store the empty state.)");
  };


  // SwallowingDefaultSentinel: Selects a default sentinel value for types that do not have unused bits. Thus, the
  // chosen sentinel value no longer represents a valid value that can be stored in the optional.
  // Note that we do not provide sentinels for char, unsigned char and signed char: Their value range is so small that
  // the user should really specify the desired sentinel manually.
  // Also note that we do not use std::numeric_limits as a small compile time optimization: Using the C-constants does
  // not require the compiler to instantiate any templates.
  template <class PayloadType> // Default case: Unknown type. Cause a compilation error via the above static_assert.
  inline constexpr auto SwallowingDefaultSentinel = MissingSentinel<PayloadType>::value;

  template <>
  inline constexpr auto SwallowingDefaultSentinel<unsigned short> = USHRT_MAX;
  template <>
  inline constexpr auto SwallowingDefaultSentinel<unsigned int> = UINT_MAX;
  template <>
  inline constexpr auto SwallowingDefaultSentinel<unsigned long> = ULONG_MAX;
  template <>
  inline constexpr auto SwallowingDefaultSentinel<unsigned long long> = ULLONG_MAX;

  template <>
  inline constexpr auto SwallowingDefaultSentinel<signed short> = SHRT_MIN;
  template <>
  inline constexpr auto SwallowingDefaultSentinel<signed int> = INT_MIN;
  template <>
  inline constexpr auto SwallowingDefaultSentinel<signed long> = LONG_MIN;
  template <>
  inline constexpr auto SwallowingDefaultSentinel<signed long long> = LLONG_MIN;


  // Case when the payload type has a custom flag manipulator (e.g. one that exploits unused bits). In this case we rely
  // on tiny::optional.
  template <class PayloadType, bool hasFlagManip = HasCustomInplaceFlagManipulator<PayloadType>>
  inline constexpr auto SelectSentinelValueWithSwallowing = UseDefaultValue;
  // Case when the payload type does not have a custom flag manipulator. Attempt to select some sensible default
  // sentinel, or cause a compilation error if this is not possible.
  template <class PayloadType>
  inline constexpr auto SelectSentinelValueWithSwallowing<PayloadType, false>
      = SwallowingDefaultSentinel<std::remove_cv_t<PayloadType>>;

} // namespace impl


// optional "always in-place": Similar to tiny::optional, but:
// - Uses some default sentinels for most integers
// - Types with unused bits or custom flag manipulators are still exploited
// - If no sentinel is chosen automatically, it causes a compilation error.
// Hence, this type is guaranteed to have the same size as the payload type.
template <class PayloadType, auto sentinelValue = impl::SelectSentinelValueWithSwallowing<PayloadType>>
using optional_aip = optional<PayloadType, sentinelValue>;


//====================================================================================
// Comparison operators
//====================================================================================

// Unfortunately, we need quite a bunch of overloads for the comparison between optionals that are essentially the same.
// E.g. we need dedicated overloads of tiny::optional and std::optional. The ones for TinyOptionalImpl and std::optional
// are not sufficient. Without them, a comparison of tiny::optional and std::optional would select the standard operator
// that compares a std::optional with any value U.
// I guess, the amount of copy & paste code justifies a macro here.
// clang-format off
#define TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS(Op, code)                                                           \
  namespace impl                                                                                                         \
  {                                                                                                                      \
    template <class D1, class F1, class D2, class F2>                                                                    \
    [[nodiscard]] bool operator Op(TinyOptionalImpl<D1, F1> const & lhs, TinyOptionalImpl<D2, F2> const & rhs)           \
    {                                                                                                                    \
      code                                                                                                               \
    }                                                                                                                    \
                                                                                                                         \
    template <class D1, class F1, class U>                                                                               \
    [[nodiscard]] bool operator Op(TinyOptionalImpl<D1, F1> const & lhs, std::optional<U> const & rhs)                   \
    {                                                                                                                    \
      code                                                                                                               \
    }                                                                                                                    \
                                                                                                                         \
    template <class U, class D1, class F1>                                                                               \
    [[nodiscard]] bool operator Op(std::optional<U> const & lhs, TinyOptionalImpl<D1, F1> const & rhs)                   \
    {                                                                                                                    \
      code                                                                                                               \
    }                                                                                                                    \
  }                                                                                                                      \
                                                                                                                         \
  template <class P, auto e, auto i, class U>                                                                            \
  [[nodiscard]] bool operator Op(optional<P, e, i> const & lhs, std::optional<U> const & rhs)                            \
  {                                                                                                                      \
    code                                                                                                                 \
  }                                                                                                                      \
                                                                                                                         \
  template <class U, class P, auto e, auto i>                                                                            \
  [[nodiscard]] bool operator Op(std::optional<U> const & lhs, optional<P, e, i> const & rhs)                            \
  {                                                                                                                      \
    code                                                                                                                 \
  }
// clang-format on

//-----------------------
// operator==

// clang-format off
TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS(
  ==, 
  bool const lhsHasValue = lhs.has_value();
  return lhsHasValue == rhs.has_value() && (!lhsHasValue || *lhs == *rhs);
)
// clang-format on

namespace impl
{
  template <class D1, class F1>
  [[nodiscard]] bool operator==(TinyOptionalImpl<D1, F1> const & lhs, std::nullopt_t) noexcept
  {
    return !lhs.has_value();
  }

#if !defined(TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON) || defined(TINY_OPTIONAL_GCC_WORKAROUND_CWG2445)
  template <class D1, class F1>
  [[nodiscard]] bool operator==(std::nullopt_t, TinyOptionalImpl<D1, F1> const & rhs) noexcept
  {
    return !rhs.has_value();
  }
#endif

  template <class D1, class F1, class U>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator==(
      TinyOptionalImpl<D1, F1> const & lhs,
      U const & rhs)
  {
    return lhs.has_value() ? *lhs == rhs : false;
  }

  template <class U, class D1, class F1>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator==(
      U const & lhs,
      TinyOptionalImpl<D1, F1> const & rhs)
  {
    return rhs.has_value() ? lhs == *rhs : false;
  }
} // namespace impl


//-----------------------
// operator!=

// clang-format off
TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS(
  !=, 
  bool const lhsHasValue = lhs.has_value();
  return lhsHasValue != rhs.has_value() || (lhsHasValue && *lhs != *rhs);
)
// clang-format on

namespace impl
{
#if !defined(TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON) || defined(TINY_OPTIONAL_GCC_WORKAROUND_CWG2445)
  template <class D1, class F1>
  [[nodiscard]] bool operator!=(TinyOptionalImpl<D1, F1> const & lhs, std::nullopt_t) noexcept
  {
    return lhs.has_value();
  }

  template <class D1, class F1>
  [[nodiscard]] bool operator!=(std::nullopt_t, TinyOptionalImpl<D1, F1> const & rhs) noexcept
  {
    return rhs.has_value();
  }
#endif

  template <class D1, class F1, class U>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator!=(
      TinyOptionalImpl<D1, F1> const & lhs,
      U const & rhs)
  {
    return lhs.has_value() ? *lhs != rhs : true;
  }

  template <class U, class D1, class F1>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator!=(
      U const & lhs,
      TinyOptionalImpl<D1, F1> const & rhs)
  {
    return rhs.has_value() ? lhs != *rhs : true;
  }
} // namespace impl


//-----------------------
// operator<

// clang-format off
TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS(
  <, 
  return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
)
// clang-format on

namespace impl
{
#if !defined(TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON) || defined(TINY_OPTIONAL_GCC_WORKAROUND_CWG2445)
  template <class D1, class F1>
  [[nodiscard]] bool operator<(TinyOptionalImpl<D1, F1> const &, std::nullopt_t) noexcept
  {
    return false;
  }

  template <class D1, class F1>
  [[nodiscard]] bool operator<(std::nullopt_t, TinyOptionalImpl<D1, F1> const & rhs) noexcept
  {
    return rhs.has_value();
  }
#endif

  template <class D1, class F1, class U>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator<(
      TinyOptionalImpl<D1, F1> const & lhs,
      U const & rhs)
  {
    return lhs.has_value() ? *lhs < rhs : true;
  }

  template <class U, class D1, class F1>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator<(
      U const & lhs,
      TinyOptionalImpl<D1, F1> const & rhs)
  {
    return rhs.has_value() ? lhs < *rhs : false;
  }
} // namespace impl


//-----------------------
// operator<=

// clang-format off
TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS(
  <=, 
  return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
)
// clang-format on

namespace impl
{
#if !defined(TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON) || defined(TINY_OPTIONAL_GCC_WORKAROUND_CWG2445)
  template <class D1, class F1>
  [[nodiscard]] bool operator<=(TinyOptionalImpl<D1, F1> const & lhs, std::nullopt_t) noexcept
  {
    return !lhs.has_value();
  }

  template <class D1, class F1>
  [[nodiscard]] bool operator<=(std::nullopt_t, TinyOptionalImpl<D1, F1> const &) noexcept
  {
    return true;
  }
#endif

  template <class D1, class F1, class U>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator<=(
      TinyOptionalImpl<D1, F1> const & lhs,
      U const & rhs)
  {
    return lhs.has_value() ? *lhs <= rhs : true;
  }

  template <class U, class D1, class F1>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator<=(
      U const & lhs,
      TinyOptionalImpl<D1, F1> const & rhs)
  {
    return rhs.has_value() ? lhs <= *rhs : false;
  }
} // namespace impl


//-----------------------
// operator>

// clang-format off
TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS(
  >, 
  return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
)
// clang-format on

namespace impl
{
#if !defined(TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON) || defined(TINY_OPTIONAL_GCC_WORKAROUND_CWG2445)
  template <class D1, class F1>
  [[nodiscard]] bool operator>(TinyOptionalImpl<D1, F1> const & lhs, std::nullopt_t) noexcept
  {
    return lhs.has_value();
  }

  template <class D1, class F1>
  [[nodiscard]] bool operator>(std::nullopt_t, TinyOptionalImpl<D1, F1> const &) noexcept
  {
    return false;
  }
#endif

  template <class D1, class F1, class U>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator>(
      TinyOptionalImpl<D1, F1> const & lhs,
      U const & rhs)
  {
    return lhs.has_value() ? *lhs > rhs : false;
  }

  template <class U, class D1, class F1>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator>(
      U const & lhs,
      TinyOptionalImpl<D1, F1> const & rhs)
  {
    return rhs.has_value() ? lhs > *rhs : true;
  }
} // namespace impl


//-----------------------
// operator>=

// clang-format off
TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS(
  >=, 
  return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
)
// clang-format on

namespace impl
{
#if !defined(TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON) || defined(TINY_OPTIONAL_GCC_WORKAROUND_CWG2445)
  template <class D1, class F1>
  [[nodiscard]] bool operator>=(TinyOptionalImpl<D1, F1> const &, std::nullopt_t) noexcept
  {
    return true;
  }

  template <class D1, class F1>
  [[nodiscard]] bool operator>=(std::nullopt_t, TinyOptionalImpl<D1, F1> const & rhs) noexcept
  {
    return !rhs.has_value();
  }
#endif

  template <class D1, class F1, class U>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator>=(
      TinyOptionalImpl<D1, F1> const & lhs,
      U const & rhs)
  {
    return lhs.has_value() ? *lhs >= rhs : false;
  }

  template <class U, class D1, class F1>
  [[nodiscard]] std::enable_if_t<!is_tiny_optional_v<U>, bool> operator>=(
      U const & lhs,
      TinyOptionalImpl<D1, F1> const & rhs)
  {
    return rhs.has_value() ? lhs >= *rhs : true;
  }
} // namespace impl


//-----------------------
// operator<=>

#ifdef TINY_OPTIONAL_ENABLE_THREEWAY_COMPARISON
// See TINY_OPTIONAL_IMPL_COMPARE_BETWEEN_OPTIONALS() for the reason of defining
// all the operator<=>(SomeOptional, SomeOptional)
namespace impl
{
  template <class D1, class F1, class D2, class F2>
    requires(std::three_way_comparable_with<
             typename TinyOptionalImpl<D1, F1>::value_type,
             typename TinyOptionalImpl<D2, F2>::value_type>)
  [[nodiscard]] std::compare_three_way_result_t<
      typename TinyOptionalImpl<D1, F1>::value_type,
      typename TinyOptionalImpl<D2, F2>::value_type>
      operator<=>(TinyOptionalImpl<D1, F1> const & lhs, TinyOptionalImpl<D2, F2> const & rhs)
  {
    return (lhs && rhs) ? (*lhs <=> *rhs) : (lhs.has_value() <=> rhs.has_value());
  }

  template <class D1, class F1, class U>
    requires(std::three_way_comparable_with<typename TinyOptionalImpl<D1, F1>::value_type, U>)
  [[nodiscard]] std::compare_three_way_result_t<typename TinyOptionalImpl<D1, F1>::value_type, U> operator<=>(
      TinyOptionalImpl<D1, F1> const & lhs,
      std::optional<U> const & rhs)
  {
    return (lhs && rhs) ? (*lhs <=> *rhs) : (lhs.has_value() <=> rhs.has_value());
  }
} // namespace impl

template <class P, auto e, auto i, std::three_way_comparable_with<P> U>
[[nodiscard]] std::compare_three_way_result_t<P, U> operator<=>(
    optional<P, e, i> const & lhs,
    std::optional<U> const & rhs)
{
  return (lhs && rhs) ? (*lhs <=> *rhs) : (lhs.has_value() <=> rhs.has_value());
}

  #ifdef TINY_OPTIONAL_GCC_WORKAROUND_CWG2445
template <class U, std::three_way_comparable_with<U> P, auto e, auto i>
[[nodiscard]] std::compare_three_way_result_t<U, P> operator<=>(
    std::optional<U> const & lhs,
    optional<P, e, i> const & rhs)
{
  return (lhs && rhs) ? (*lhs <=> *rhs) : (lhs.has_value() <=> rhs.has_value());
}
  #endif

namespace impl
{
  template <class D1, class F1>
  [[nodiscard]] std::strong_ordering operator<=>(TinyOptionalImpl<D1, F1> const & lhs, std::nullopt_t) noexcept
  {
    return lhs.has_value() <=> false;
  }

  template <class D1, class F1, class U>
    requires(!is_tiny_optional_v<U> && std::three_way_comparable_with<typename TinyOptionalImpl<D1, F1>::value_type, U>)
  [[nodiscard]] std::compare_three_way_result_t<typename TinyOptionalImpl<D1, F1>::value_type, U> operator<=>(
      TinyOptionalImpl<D1, F1> const & lhs,
      U const & rhs)
  {
    return lhs.has_value() ? (*lhs <=> rhs) : std::strong_ordering::less;
  }

} // namespace impl

#endif
TINY_OPTIONAL_INLINE_NS_END
} // namespace tiny


//====================================================================================
// std::hash specializations
//====================================================================================

namespace std
{
// clang-format off
template <class StoredTypeDecomposition, class FlagManipulator>
struct hash<
  tiny::impl::EnableHashHelper<
    tiny::impl::TinyOptionalImpl<StoredTypeDecomposition, FlagManipulator>,
    typename StoredTypeDecomposition::PayloadType
  >
>
{
  size_t operator()(tiny::impl::TinyOptionalImpl<StoredTypeDecomposition, FlagManipulator> const & o) const
  {
    return o.has_value() ? hash<std::remove_const_t<typename StoredTypeDecomposition::PayloadType>>{}(*o) : 0;
  }
};

  
template <class PayloadType, auto sentinelOrMemPtr, auto irrelevantOrSentinel>
struct hash<
  tiny::impl::EnableHashHelper<
    tiny::optional<PayloadType, sentinelOrMemPtr, irrelevantOrSentinel>,
    PayloadType
  >
>
{
  size_t operator()(tiny::optional<PayloadType, sentinelOrMemPtr, irrelevantOrSentinel> const & o) const
  {
    return o.has_value() ? hash<std::remove_const_t<PayloadType>>{}(*o) : 0;
  }
};

// clang-format on

} // namespace std

#if defined(__GNUG__) && !defined(__clang__)
  // Pop "-Wmaybe-uninitialized"
  #pragma GCC diagnostic pop
#endif
