/* mbed Microcontroller Library
 * Copyright (c) 2019 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef MSTD_TYPE_TRAITS_
#define MSTD_TYPE_TRAITS_

/* <mstd_type_traits>
 *
 * - includes toolchain's <type_traits>
 * - For ARM C 5, standard C++11/14 features:
 *   - std::integral_constant, std::true_type, std::false_type
 *   - primary type categories (std::is_void, std::is_integral etc)
 *   - composite type categories (std::is_reference etc)
 *   - type properties (std::is_const, std::is_constructible etc), except std::is_final
 *   - type property queries (std::alignment_of, std::rank, std::extent)
 *   - type relations (std::is_same, std::is_base_of, std::is_convertible)
 *   - const-volatile modifications (std::remove_cv, std::add_const etc)
 *   - reference modifications (std::remove_reference, std::add_lvalue_reference etc)
 *   - sign modifications (std::make_signed, std::make_unsigned)
 *   - array modifications (std::remove_extent, std::remove_all_extents)
 *   - pointer modifications (std::remove_pointer, std::add_pointer)
 *   - other modifications:
 *      - std::aligned_storage
 *      - std::decay
 *      - std::enable_if
 *      - std::conditional
 *      - std::common_type
 *      - std::underlying_type
 *      - std::result_of
 * - For all toolchains, C++17/20 backports:
 *   - mstd::type_identity
 *   - mstd::bool_constant
 *   - mstd::void_t
 *   - mstd::is_invocable, mbed::is_invocable_r, etc
 *   - mstd::invoke_result
 *   - logical operator traits (mstd::conjunction, mstd::disjunction, mstd::negation)
 */

#include <mstd_cstddef>
#ifdef __CC_ARM
#include <_move.h>
#else
#include <type_traits>
#endif

// The template stuff in here is too confusing for astyle
// *INDENT-OFF*

/* Start with some core C++11 type trait building blocks for ARMC5 */
#ifdef __CC_ARM

namespace std {

/* integral_constant */
template <typename T, T V>
struct integral_constant {
    using value_type = T;
    using type = integral_constant;
    static constexpr T value = V;
    constexpr operator T() const noexcept
    {
        return V;
    }
    constexpr T operator()() const noexcept
    {
        return V;
    }
};

template<typename T, T V>
constexpr T integral_constant<T, V>::value;

/* true_type, false_type */
using true_type = integral_constant<bool, true>;
using false_type = integral_constant<bool, false>;

} // namespace std
#endif

namespace mstd {

/* C++20 type identity */
template<typename T>
struct type_identity {
    using type = T;
};

template <typename T>
using type_identity_t = typename type_identity<T>::type;

/* C++17 void_t (foundation for detection idiom) */
/* void_t<Args...> is void if args are valid, else a substitution failure */
#if __cpp_lib_void_t >= 201411
using std::void_t;
#elif defined __CC_ARM
namespace impl {
template <typename...>
struct void_helper : type_identity<void> { };
}
template <typename... Ts>
using void_t = typename impl::void_helper<Ts...>::type;
#else
template <typename...>
using void_t = void;
#endif

/* C++17 bool_constant */
#if __cpp_lib_bool_constant >= 201505
using std::bool_constant;
#else
template <bool B>
using bool_constant = std::integral_constant<bool, B>;
#endif

/* Forward declarations */
#if __cpp_lib_is_invocable >= 201703
using std::invoke_result;
#else
template <typename F, typename... Args>
struct invoke_result;
#endif

} // namespace mstd

#ifdef __CC_ARM
namespace std {

/* Fill in core missing C++11/C++14 functionality for ARM C 5 into namespace std */
/* First, the things needed to support the detector idiom. */

/* is_same */
template <typename, typename>
struct is_same : false_type { };

template <typename T>
struct is_same<T, T> : true_type { };

/* conditional */
template <bool B, typename T, typename F>
struct conditional : mstd::type_identity<F> { };

template <typename T, typename F>
struct conditional<true, T, F> : mstd::type_identity<T> { };

template <bool B, typename T, typename F>
using conditional_t = typename conditional<B, T, F>::type;

/* enable_if */
template <bool B, typename T = void>
struct enable_if {  };

template <typename T>
struct enable_if<true, T> : mstd::type_identity<T> { };

template <bool B, typename T = void>
using enable_if_t = typename enable_if<B, T>::type;

/* Forward declarations */
template <typename From, typename To>
struct is_convertible;

template <typename T>
struct is_object;

template <typename T>
struct is_reference;

template <typename T>
class reference_wrapper;

} // namespace std
#endif // __CC_ARM

namespace mstd {

using std::is_same;
using std::conditional;
using std::conditional_t;
using std::enable_if;
using std::enable_if_t;
using std::is_convertible;
using std::is_object;
using std::is_reference;

/* Reinvent or pull in good stuff not in C++14 into namespace mstd */
/* C++17 logical operations on traits */
#if __cpp_lib_logical_traits >= 201510
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<class...>
struct conjunction : std::true_type { };
template<class B1>
struct conjunction<B1> : B1 { };
template<class B1, class... BN>
struct conjunction<B1, BN...> : std::conditional_t<bool(B1::value), conjunction<BN...>, B1> { };

template<class...>
struct disjunction : std::false_type { };
template<class B1>
struct disjunction<B1> : B1 { };
template<class B1, class... BN>
struct disjunction<B1, BN...> : std::conditional_t<bool(B1::value), B1, disjunction<BN...>> { };

template<class B>
struct negation : bool_constant<!bool(B::value)> { };
#endif

/* C++ detection idiom from Library fundamentals v2 TS */
/* Place into mstd::experimental to match their std::experimental */
namespace experimental {

namespace impl {
template <class Default, class Void, template<class...> class Op, class... Args>
struct detector {
    using value_t = std::false_type;
    using type = Default;
};

template <class Default, template<class...> class Op, class... Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
    using value_t = std::true_type;
    using type = Op<Args...>;
};

} // namespace impl

struct nonesuch {
    ~nonesuch() = delete;
    nonesuch(nonesuch const &) = delete;
    void operator=(nonesuch const &) = delete;
};

#if 0
/* Deactivated because impl::detector appears to not work on ARM C 5; it seems to produce
 * hard errors in the template template parameter expansion. You can use void_t directly instead.
 *
 * Reactivate if working ARM C 5 implementation discovered, or ARM C 5 support
 * dropped.
 */

template<template<class...> class Op, class... Args>
using is_detected = typename impl::detector<nonesuch, void, Op, Args...>::value_t;

template<template<class...> class Op, class... Args>
using detected_t = typename impl::detector<nonesuch, void, Op, Args...>::type;

template<class Default, template<class...> class Op, class... Args>
using detected_or = typename impl::detector<Default, void, Op, Args...>;

template<class Default, template<class...> class Op, class... Args>
using detected_or_t = typename detected_or<Default, Op, Args...>::type;

template<class Expected, template<class...> class Op, class... Args>
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;

template<class To, template<class...> class Op, class... Args>
using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>;
#endif // if 0 - deactivated detector idiom
} // namespace experimental
} // namespace mstd

#ifdef __CC_ARM
/* More missing C++11/C++14 functionality for ARM C 5 */
namespace std {

/* remove_const/volatile/cv */
template <typename T>
struct remove_const : mstd::type_identity<T> { };

template <typename T>
struct remove_const<const T> : mstd::type_identity<T> { };

template <typename T>
using remove_const_t = typename remove_const<T>::type;

template <typename T>
struct remove_volatile : mstd::type_identity<T> { };

template <typename T>
struct remove_volatile<volatile T> : mstd::type_identity<T> { };

template <typename T>
using remove_volatile_t = typename remove_volatile<T>::type;

template <typename T>
struct remove_cv : remove_volatile<remove_const_t<T>> { };

template <typename T>
using remove_cv_t = typename remove_cv<T>::type;

/* add_const/volatile/cv */
template <typename T>
struct add_const : mstd::type_identity<const T> { };

template <typename T>
using add_const_t = typename add_const<T>::type;

template <typename T>
struct add_volatile : mstd::type_identity<volatile T> { };

template <typename T>
using add_volatile_t = typename add_volatile<T>::type;

template <typename T>
struct add_cv : mstd::type_identity<const volatile T> { };

template <typename T>
using add_cv_t = typename add_cv<T>::type;

/* add_lvalue_reference / add_value_reference */
namespace impl {
template <typename T> // Base test - objects and references
struct is_referenceable : mstd::disjunction<is_object<T>, is_reference<T>> { };
template <typename R, typename... Args> // Specialisation - unqualified functions (non-variadic)
struct is_referenceable<R(Args...)> : true_type { };
template <typename R, typename... Args> // Specialisation - unqualified functions (variadic)
struct is_referenceable<R(Args... ...)> : true_type { };

template <typename T, bool = is_referenceable<T>::value>
struct add_lvalue_reference : mstd::type_identity<T> { };
template <typename T>
struct add_lvalue_reference<T, true> : mstd::type_identity<T &> { };

template <typename T, bool = is_referenceable<T>::value>
struct add_rvalue_reference : mstd::type_identity<T> { };
template <typename T>
struct add_rvalue_reference<T, true> : mstd::type_identity<T &&> { };
} // namespace impl

template <typename T>
struct add_lvalue_reference : impl::add_lvalue_reference<T> { };
template <typename T>
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;

template <typename T>
struct add_rvalue_reference : impl::add_rvalue_reference<T> { };
template <typename T>
using add_rvalue_reference_t = typename add_rvalue_reference<T>::type;

// [declval]
template <class T>
add_rvalue_reference_t<T> declval() noexcept;

// [meta.unary.cat]
/* is_void */
template <typename T>
struct is_void : is_same<void, remove_cv_t<T>> { };

/* is_null_pointer */
template <typename T>
struct is_null_pointer : is_same<decltype(nullptr), remove_cv_t<T>> { };

/* is_integral */
template <typename T>
struct is_integral :
    mstd::disjunction<
        is_same<bool, remove_cv_t<T>>,
        is_same<char, remove_cv_t<T>>,
        is_same<signed char, remove_cv_t<T>>,
        is_same<unsigned char, remove_cv_t<T>>,
        is_same<char16_t, remove_cv_t<T>>,
        is_same<char32_t, remove_cv_t<T>>,
        is_same<wchar_t, remove_cv_t<T>>,
        is_same<short, remove_cv_t<T>>,
        is_same<unsigned short, remove_cv_t<T>>,
        is_same<int, remove_cv_t<T>>,
        is_same<unsigned int, remove_cv_t<T>>,
        is_same<long, remove_cv_t<T>>,
        is_same<unsigned long, remove_cv_t<T>>,
        is_same<long long, remove_cv_t<T>>,
        is_same<unsigned long long, remove_cv_t<T>>> { };

/* is_floating_point */
template <typename T>
struct is_floating_point :
    mstd::disjunction<
        is_same<float, remove_cv_t<T>>,
        is_same<double, remove_cv_t<T>>,
        is_same<long double, remove_cv_t<T>>> { };

/* is_array */
template <typename T>
struct is_array : false_type { };

template <typename T>
struct is_array<T[]> : true_type { };

template <typename T, size_t N>
struct is_array<T[N]> : true_type { };

/* is_pointer */
namespace impl {
template <typename T>
struct is_unqualified_pointer : false_type { };
template <typename T>
struct is_unqualified_pointer<T *> : true_type { };
} // namespace impl

template <typename T>
struct is_pointer : impl::is_unqualified_pointer<remove_cv_t<T>> { };

/* is_lvalue_reference */
template <typename T>
struct is_lvalue_reference : false_type { };

template <typename T>
struct is_lvalue_reference<T &> : true_type { };

/* is_rvalue_reference */
template <typename T>
struct is_rvalue_reference : false_type { };

template <typename T>
struct is_rvalue_reference<T &&> : true_type { };

/* is_enum */
template <typename T>
struct is_enum : mstd::bool_constant<__is_enum(T)> { };

/* is_union */
template <typename T>
struct is_union : mstd::bool_constant<__is_union(T)> { };

/* is_class */
template <typename T>
struct is_class : mstd::bool_constant<__is_class(T)> { };

/* is_function */
template <typename T>
struct is_function : false_type { };

template <typename R, typename... Args>
struct is_function<R(Args...)> : true_type { }; // 0 or more named arguments
template <typename R, typename... Args>
struct is_function<R(Args... ...)> : true_type { }; // 0 or more named arguments and variadic

template <typename R, typename... Args>
struct is_function<R(Args...) &> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) &> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) &&> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) &&> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) const> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) const> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) const &> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) const &> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) const &&> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) const &&> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) volatile> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) volatile> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) volatile &> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) volatile &> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) volatile &&> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) volatile &&> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) const volatile> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) const volatile> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) const volatile &> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) const volatile &> : true_type { };

template <typename R, typename... Args>
struct is_function<R(Args...) const volatile &&> : true_type { };
template <typename R, typename... Args>
struct is_function<R(Args... ...) const volatile &&> : true_type { };

/* is_member_function_pointer */
namespace impl {
/* Two helper filters to complement is_function */
template <typename>
using always_true = true_type;
template <typename T>
using is_not_function = mstd::negation<is_function<T>>;

template <typename T, template<typename> class Filter = always_true>
struct is_unqualified_member_pointer : false_type { };
template <typename T, template<typename> class Filter, typename U>
struct is_unqualified_member_pointer<T U::*, Filter> :  Filter<T> { };

} // namespace impl

template <typename T>
struct is_member_function_pointer : impl::is_unqualified_member_pointer<remove_cv_t<T>, is_function> { };

/* is_member_object_pointer */
template <typename T>
struct is_member_object_pointer : impl::is_unqualified_member_pointer<remove_cv_t<T>, impl::is_not_function> { };

/* is_reference = (is_lvalue_reference || is_rvalue_reference) */
template <typename T>
struct is_reference : false_type { };

template <typename T>
struct is_reference<T &> : true_type { };

template <typename T>
struct is_reference<T &&> : true_type { };

/* is_arithmetic */
template <typename T>
struct is_arithmetic : mstd::disjunction<is_integral<T>, is_floating_point<T>> { };

/* is_fundamental */
template <typename T>
struct is_fundamental : mstd::disjunction<is_arithmetic<T>, is_void<T>, is_null_pointer<T>> { };

/* is_compound */
template <typename T>
struct is_compound : mstd::negation<is_fundamental<T>> { };

/* is_member_pointer */
template <typename T>
struct is_member_pointer : impl::is_unqualified_member_pointer<remove_cv_t<T>> { };

/* is_scalar */
template <typename T>
struct is_scalar : mstd::disjunction<is_arithmetic<T>, is_enum<T>, is_pointer<T>, is_member_pointer<T>, is_null_pointer<T>> { };

/* is_object */
template <typename T>
struct is_object : mstd::disjunction<is_scalar<T>, is_array<T>, is_union<T>, is_class<T>> { };

// [meta.unary.prop]
/* is_const */
template <typename T>
struct is_const : false_type { };

template <typename T>
struct is_const<const T> : true_type { };

/* is_volatile */
template <typename T>
struct is_volatile : false_type { };

template <typename T>
struct is_volatile<volatile T> : true_type { };

/* is_trivial */
template <typename T>
struct is_trivial : mstd::bool_constant<__is_trivial(T)> { };

/* is_trivially_copyable */
template <typename T>
struct is_trivially_copyable : mstd::bool_constant<__is_trivially_copyable(T)> { };

/* is_standard_layout */
template <typename T>
struct is_standard_layout : mstd::bool_constant<__is_standard_layout(T)> { };

/* is_pod */
template <typename T>
struct is_pod : mstd::bool_constant<__is_pod(T)> { };

/* is_literal_type */
template <typename T>
struct is_literal_type : mstd::bool_constant<__is_literal_type(T)> { };

/* is_empty */
template <typename T>
struct is_empty : mstd::bool_constant<__is_empty(T)> { };

/* is_polymorphic */
template <typename T>
struct is_polymorphic : mstd::bool_constant<__is_polymorphic(T)> { };

/* is_abstract */
template <typename T>
struct is_abstract : mstd::bool_constant<__is_abstract(T)> { };

/* is_final (C++14) not supported */

/* is_signed */
namespace impl {
template <typename T, bool = is_arithmetic<T>::value >
struct is_signed : false_type { };
template <typename T>
struct is_signed<T, true> : mstd::bool_constant<T(-1) < T(0)> { };
} // namespace impl

template <typename T>
struct is_signed : impl::is_signed<T> { };

/* is_unsigned */
namespace impl {
    template <typename T, bool = is_arithmetic<T>::value >
    struct is_unsigned : false_type { };
    template <typename T>
    struct is_unsigned<T, true> : mstd::bool_constant<T(0) < T(-1)> { };
}

template <typename T>
struct is_unsigned : impl::is_unsigned<T> { };

/* is_constructible */
template <typename T, typename... Args>
struct is_constructible : mstd::bool_constant<__is_constructible(T, Args...)> { };

/* is_default_constructible */
template <typename T>
struct is_default_constructible : is_constructible<T> { };

/* is_copy_constructible */
template <typename T>
struct is_copy_constructible : is_constructible<T, add_lvalue_reference_t<add_const_t<T>>> { };

/* is_move_constructible */
template <typename T>
struct is_move_constructible : is_constructible<T, add_rvalue_reference_t<T>> { };

/* is_assignable */
namespace impl {
template <typename To, typename From, typename = void>
struct is_assignable : std::false_type { };

template <typename To, typename From>
struct is_assignable<To, From, mstd::void_t<decltype(std::declval<To>() = std::declval<From>())>> : std::true_type { };
} // namespace impl

template <typename To, typename From>
struct is_assignable : impl::is_assignable<To, From> { };

/* is_copy_assignable */
template <typename T>
struct is_copy_assignable : is_assignable<add_lvalue_reference_t<T>,
                                          add_lvalue_reference_t<add_const_t<T>>> { };

/* is_move_assignable */
template <typename T>
struct is_move_assignable : is_assignable<add_lvalue_reference_t<T>,
                                          add_rvalue_reference_t<T>> { };

/* is_destructible */
template <typename T>
struct is_destructible : mstd::bool_constant<__is_destructible(T)> { };

/* is_trivially_constructible */
template <typename T, typename... Args>
struct is_trivially_constructible : mstd::bool_constant<__is_trivially_constructible(T, Args...)> { };

/* is_trivially_default_constructible */
template <typename T>
struct is_trivially_default_constructible : is_trivially_constructible<T> { };

/* is_trivially_copy_constructible */
template <typename T>
struct is_trivially_copy_constructible : is_trivially_constructible<T, add_lvalue_reference_t<add_const_t<T>>> { };

/* is_trivially_move_constructible */
template <typename T>
struct is_trivially_move_constructible : is_trivially_constructible<T, add_rvalue_reference_t<T>> { };

/* is_trivially_assignable */
template <typename To, typename From>
struct is_trivially_assignable : mstd::bool_constant<__is_trivially_assignable(To, From)> { };

/* is_trivially_copy_assignable */
template <typename T>
struct is_trivially_copy_assignable : is_trivially_assignable<add_lvalue_reference_t<T>,
                                                              add_lvalue_reference_t<add_const_t<T>>> { };

/* is_trivially_move_assignable */
template <typename T>
struct is_trivially_move_assignable : is_trivially_assignable<add_lvalue_reference_t<T>,
                                                              add_rvalue_reference_t<T>> { };

/* is_trivially_destructible */
template <typename T>
struct is_trivially_destructible : mstd::bool_constant<__is_trivially_destructible(T)> { };

/* is_nothrow_constructible */
template <typename T, typename... Args>
struct is_nothrow_constructible : mstd::bool_constant<__is_nothrow_constructible(T, Args...)> { };

/* is_nothrow_default_constructible */
template <typename T>
struct is_nothrow_default_constructible : is_nothrow_constructible<T> { };

/* is_nothrow_copy_constructible */
template <typename T>
struct is_nothrow_copy_constructible : is_nothrow_constructible<T, add_lvalue_reference_t<add_const_t<T>>> { };

/* is_nothrow_move_constructible */
template <typename T>
struct is_nothrow_move_constructible : is_nothrow_constructible<T, add_rvalue_reference_t<T>> { };

/* is_nothrow_assignable */
template <typename To, typename From>
struct is_nothrow_assignable : mstd::bool_constant<__is_nothrow_assignable(To, From)> { };

/* is_copy_assignable */
template <typename T>
struct is_nothrow_copy_assignable : is_nothrow_assignable<add_lvalue_reference_t<T>,
                                                          add_lvalue_reference_t<add_const_t<T>>> { };

/* is_move_assignable */
template <typename T>
struct is_nothrow_move_assignable : is_nothrow_assignable<add_lvalue_reference_t<T>,
                                                          add_rvalue_reference_t<T>> { };

/* is_nothrow_destructible */
template <typename T>
struct is_nothrow_destructible : mstd::bool_constant<__is_nothrow_destructible(T)> { };

/* has_virtual_destructor */
template <typename T>
struct has_virtual_destructor : mstd::bool_constant<__has_virtual_destructor(T)> { };

// [meta.unary.prop.query]
/* alignment_of */
template <typename T>
struct alignment_of : integral_constant<size_t, alignof(T)> { };

/* rank */
template <typename T>
struct rank : integral_constant<size_t, 0> { };

template <typename T>
struct rank<T[]> : integral_constant<size_t, rank<T>::value + 1u> { };

template <typename T, size_t N>
struct rank<T[N]> : integral_constant<size_t, rank<T>::value + 1u> { };

/* extent */
template <typename T, unsigned I = 0>
struct extent : integral_constant<size_t, 0> { };

template <typename T>
struct extent<T[], 0> : integral_constant<size_t, 0> { };

template <typename T, unsigned I>
struct extent<T[], I> : extent<T, I - 1> { };

template <typename T, size_t N>
struct extent<T[N], 0> : integral_constant<size_t, N> { };

template <typename T, size_t N, unsigned I>
struct extent<T[N], I> : extent<T, I - 1> { };

// [meta.rel]
/* is_convertible */
/* __is_convertible_to apparently returns true for any From if To is void */
template <typename From, typename To>
struct is_convertible : conditional_t<is_void<To>::value,
                               is_void<From>,
                               mstd::bool_constant<__is_convertible_to(From, To)>> { };

/* is_base_of */
template <typename Base, typename Derived>
struct is_base_of : mstd::bool_constant<__is_base_of(Base, Derived) && __is_class(Derived)> { };


/* make_signed */
namespace impl
{
template <typename CV, typename T, bool = is_const<CV>::value, bool = is_volatile<CV>::value>
struct copy_cv : mstd::type_identity<T> { };

template <typename CV, typename T>
struct copy_cv<CV, T, true, false> : add_const<T> { };

template <typename CV, typename T>
struct copy_cv<CV, T, false, true> : add_volatile<T> { };

template <typename CV, typename T>
struct copy_cv<CV, T, true, true> : add_cv<T> { };

template <typename CV, typename T>
using copy_cv_t = typename copy_cv<CV, T>::type;

template <typename T>
struct make_signed : mstd::type_identity<T> { };

template <typename T>
using make_signed_t = typename make_signed<T>::type;

template <>
struct make_signed<char> : mstd::type_identity<signed char> { };

template <>
struct make_signed<unsigned char> : mstd::type_identity<signed char> { };

template <>
struct make_signed<unsigned short> : mstd::type_identity<short> { };

template <>
struct make_signed<unsigned int> : mstd::type_identity<int> { };

template <>
struct make_signed<unsigned long> : mstd::type_identity<long> { };

template <>
struct make_signed<unsigned long long> : mstd::type_identity<long long> { };

template <typename T>
struct make_unsigned : mstd::type_identity<T> { };

template <typename T>
using make_unsigned_t = typename make_unsigned<T>::type;

template <>
struct make_unsigned<char> : mstd::type_identity<unsigned char> { };

template <>
struct make_unsigned<signed char> : mstd::type_identity<unsigned char> { };

template <>
struct make_unsigned<short> : mstd::type_identity<unsigned short> { };

template <>
struct make_unsigned<int> : mstd::type_identity<unsigned int> { };

template <>
struct make_unsigned<long> : mstd::type_identity<unsigned long> { };

template <>
struct make_unsigned<long long> : mstd::type_identity<unsigned long long> { };

template <typename T, bool IsIntegral = is_integral<T>::value, bool IsEnum = is_enum<T>::value>
struct make_signed_selector;

template <typename T>
class make_signed_selector<T, true, false> { // integral
    using raw_signed_t = make_signed_t<remove_cv_t<T>>;
public:
    using type = copy_cv_t<T, raw_signed_t>;
};

template <typename T>
class make_signed_selector<T, false, true> { // enum
    using raw_signed_t = mstd::conditional_t<sizeof(T) <= 1, signed char,
                         mstd::conditional_t<sizeof(T) <= sizeof(short), short,
                         mstd::conditional_t<sizeof(T) <= sizeof(int), int,
                         mstd::conditional_t<sizeof(T) <= sizeof(long), long, long long>>>>;
public:
    using type = copy_cv_t<T, raw_signed_t>;
};

template <typename T, bool IsIntegral = is_integral<T>::value, bool IsEnum = is_enum<T>::value>
struct make_unsigned_selector;

template <typename T>
class make_unsigned_selector<T, true, false> { // integral
    using raw_unsigned_t = make_unsigned_t<remove_cv_t<T>>;
public:
    using type = copy_cv_t<T, raw_unsigned_t>;
};

template <typename T>
class make_unsigned_selector<T, false, true> { // enum
    using raw_unsigned_t = mstd::conditional_t<sizeof(T) <= 1, unsigned char,
                           mstd::conditional_t<sizeof(T) <= sizeof(short), unsigned short,
                           mstd::conditional_t<sizeof(T) <= sizeof(int), unsigned int,
                           mstd::conditional_t<sizeof(T) <= sizeof(long), unsigned long, unsigned long long>>>>;
public:
    using type = copy_cv_t<T, raw_unsigned_t>;
};

}

template <typename T>
struct make_signed : impl::make_signed_selector<T> { };

template <>
struct make_signed<bool>; // bool is integral, but make_signed does not apply

template <typename T>
using make_signed_t = typename make_signed<T>::type;

template <typename T>
struct make_unsigned : impl::make_unsigned_selector<T> { };

template <>
struct make_unsigned<bool>; // bool is integral, but make_unsigned does not apply

template <typename T>
using make_unsigned_t = typename make_unsigned<T>::type;

/* remove_extent */
template <typename T>
struct remove_extent : mstd::type_identity<T> { };

template <typename T>
struct remove_extent<T[]> : mstd::type_identity<T> { };

template <typename T, size_t N>
struct remove_extent<T[N]> : mstd::type_identity<T> { };

template <typename T>
using remove_extent_t = typename remove_extent<T>::type;

/* remove_all_extents */
template <typename T>
struct remove_all_extents : mstd::type_identity<T> { };

template <typename T>
using remove_all_extents_t = typename remove_all_extents<T>::type;

template <typename T>
struct remove_all_extents<T[]> : mstd::type_identity<remove_all_extents_t<T>> { };

template <typename T, size_t N>
struct remove_all_extents<T[N]> : mstd::type_identity<remove_all_extents_t<T>> { };

/* remove_pointer */
template <typename T>
struct remove_pointer : mstd::type_identity<T> { };

template <typename T>
struct remove_pointer<T *> : mstd::type_identity<T> { };

template <typename T>
struct remove_pointer<T * const>  : mstd::type_identity<T> { };

template <typename T>
struct remove_pointer<T * volatile> : mstd::type_identity<T> { };

template <typename T>
struct remove_pointer<T * const volatile> : mstd::type_identity<T> { };

template <typename T>
using remove_pointer_t = typename remove_pointer<T>::type;

/* add_pointer */
namespace impl {
template <typename T, bool = mstd::disjunction<is_referenceable<T>, is_void<T>>::value>
struct add_pointer : mstd::type_identity<T> { };
template <typename T>
struct add_pointer<T, true> : mstd::type_identity<remove_reference_t<T> *> { };
} // namespace impl

template <typename T>
struct add_pointer : impl::add_pointer<T> { };

template <typename T>
using add_pointer_t = typename add_pointer<T>::type;

/* aligned_storage */
namespace impl {
constexpr size_t possible_alignment_requirement(size_t n)
{
    return n <= 1 ? 1 :
           n <= 2 ? 2 :
           n <= 4 ? 4 : 8;
}
}

template <size_t Len, size_t Align = impl::possible_alignment_requirement(Len)>
struct aligned_storage {
    struct type {
        __attribute__((aligned(Align))) unsigned char data[Len];
    };
};

template <size_t Len, size_t Align = impl::possible_alignment_requirement(Len)>
using aligned_storage_t = typename aligned_storage<Len, Align>::type;

/* decay */
namespace impl {
template <typename T>
struct decay : mstd::type_identity<
                    conditional_t<is_array<T>::value,
                        remove_extent_t<T> *,
                        conditional_t<is_function<T>::value,
                            add_pointer_t<T>,
                            remove_cv_t<T>
                        >
                    >
                > { };
} // namespace impl

template <typename T>
struct decay : impl::decay<remove_reference_t<T>> { };

template <typename T>
using decay_t = typename decay<T>::type;

/* common_type (0 types)  */
template <typename...>
struct common_type { };

template <typename... T>
using common_type_t = typename common_type<T...>::type;

namespace impl {
template <typename T1, typename T2>
using ternary_t = decltype(false ? declval<T1>() : declval<T2>());

template <typename T1, typename T2, typename=void>
struct ct2_default { };

template <typename T1, typename T2>
struct ct2_default<T1, T2, mstd::void_t<ternary_t<T1, T2>>> : mstd::type_identity<decay_t<ternary_t<T1, T2>>> { };

template <typename T1, typename T2, typename D1 = decay_t<T1>, typename D2 = decay_t<T2>>
struct ct2 : common_type<D1, D2> { };

template <typename D1, typename D2>
struct ct2<D1, D2, D1, D2> : ct2_default<D1, D2> { };

template <typename Void, typename T1, typename T2, typename... TN>
struct ct_multi { };

template <typename T1, typename T2, typename... TN>
struct ct_multi<mstd::void_t<common_type<T1, T2>>, T1, T2, TN...> : common_type<common_type_t<T1, T2>, TN...> { };
} // namespace impl

/* common_type (1 type) */
template <typename T>
struct common_type<T> : common_type<T, T> { };

/* common_type (2 types) - applications can add extra 2-type specializations */
template <typename T1, typename T2>
struct common_type<T1, T2> : impl::ct2<T1, T2> { };

/* common_type (3+ types) */
template <typename T1, typename T2, typename... TN>
struct common_type<T1, T2, TN...> : impl::ct_multi<void, T1, T2, TN...> { };

/* underlying_type */
template <typename T>
struct underlying_type : mstd::type_identity<__underlying_type(T)> { };

template <typename T>
using underlying_type_t = typename underlying_type<T>::type;

/* result_of */
template <typename>
struct result_of;

template <typename F, typename... Args>
struct result_of<F(Args...)> : mstd::invoke_result<F, Args...> { };

template <typename T>
using result_of_t = typename result_of<T>::type;

} // namespace std
#endif // __CC_ARM


/* More post-C++14 stuff */
namespace mstd {

using std::remove_const;
using std::remove_const_t;
using std::remove_volatile;
using std::remove_volatile_t;
using std::remove_cv;
using std::remove_cv_t;
using std::add_const;
using std::add_const_t;
using std::add_volatile;
using std::add_volatile_t;
using std::add_cv;
using std::add_cv_t;
using std::remove_reference;
using std::remove_reference_t;
using std::add_lvalue_reference;
using std::add_rvalue_reference;
using std::is_void;
using std::is_null_pointer;
using std::is_integral;
using std::is_floating_point;
using std::is_array;
using std::is_pointer;
using std::is_lvalue_reference;
using std::is_rvalue_reference;
using std::is_enum;
using std::is_union;
using std::is_class;
using std::is_function;
using std::is_member_function_pointer;
using std::is_member_object_pointer;
using std::is_reference;
using std::is_arithmetic;
using std::is_fundamental;
using std::is_compound;
using std::is_member_pointer;
using std::is_scalar;
using std::is_object;
using std::is_const;
using std::is_volatile;
using std::is_trivial;
using std::is_trivially_copyable;
using std::is_standard_layout;
using std::is_pod;
using std::is_literal_type;
using std::is_empty;
using std::is_polymorphic;
using std::is_abstract;
using std::is_signed;
using std::is_unsigned;
using std::is_constructible;
using std::is_default_constructible;
using std::is_copy_constructible;
using std::is_move_constructible;
using std::is_assignable;
using std::is_copy_assignable;
using std::is_move_assignable;
using std::is_destructible;
using std::is_trivially_constructible;
using std::is_trivially_default_constructible;
using std::is_trivially_copy_constructible;
using std::is_trivially_move_constructible;
using std::is_trivially_assignable;
using std::is_trivially_copy_assignable;
using std::is_trivially_move_assignable;
using std::is_trivially_destructible;
// Exceptions are disabled in mbed, so short-circuit nothrow tests
// (Compilers don't make noexcept() return false with exceptions
// disabled, presumably to preserve binary compatibility, so the
// std versions of these are unduly pessimistic).
template <typename T, typename... Args>
struct is_nothrow_constructible : is_constructible<T, Args...> { };
template <typename T>
struct is_nothrow_default_constructible : is_default_constructible<T> { };
template <typename T>
struct is_nothrow_copy_constructible : is_copy_constructible<T> { };
template <typename T>
struct is_nothrow_move_constructible : is_move_constructible<T> { };
template <typename To, typename From>
struct is_nothrow_assignable: is_assignable<To, From> { };
template <typename T>
struct is_nothrow_copy_assignable : is_copy_assignable<T> { };
template <typename T>
struct is_nothrow_move_assignable : is_move_assignable<T> { };
using std::has_virtual_destructor;
using std::alignment_of;
using std::rank;
using std::extent;
using std::is_convertible;
using std::is_base_of;
using std::make_signed;
using std::make_signed_t;
using std::make_unsigned;
using std::make_unsigned_t;
using std::remove_extent;
using std::remove_extent_t;
using std::remove_all_extents;
using std::remove_all_extents_t;
using std::remove_pointer;
using std::remove_pointer_t;
using std::add_pointer;
using std::add_pointer_t;
using std::aligned_storage;
using std::aligned_storage_t;
using std::decay;
using std::decay_t;
using std::common_type;
using std::common_type_t;
using std::result_of;
using std::result_of_t;

/* C++20 remove_cvref */
template <typename T>
struct remove_cvref : type_identity<std::remove_cv_t<std::remove_reference_t<T>>> { };

template <typename T>
using remove_cvref_t = typename remove_cvref<T>::type;

}

#ifndef __CC_ARM
#if __cpp_lib_invoke < 201411
#include <utility> // want std::forward
#include <functional> // want std::reference_wrapper
#elif __cpp_lib_is_invocable < 201703
#include <functional> // want std::invoke
#endif
#endif

namespace mstd {
/* C++17 invoke_result, is_invocable, invoke */
#if __cpp_lib_is_invocable >= 201703
/* Library has complete suite - pull it into mstd */
using std::invoke_result;
using std::invoke_result_t;
using std::is_invocable;
using std::is_nothrow_invocable;
using std::is_invocable_r;
using std::is_nothrow_invocable_r;
#else // __cpp_lib_is_invocable
namespace impl {
#if __cpp_lib_invoke >= 201411
/* Library has just invoke - make it our impl::INVOKE so we can create invoke_result */
template <typename F, typename... Args>
using INVOKE = std::invoke<F, Args...>;
#else // __cpp_lib_invoke
/* Define our own INVOKE */
template <typename T>
struct is_reference_wrapper : std::false_type { };

template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type { };

/* F is pointer to member function, and 1st arg decays to matching class */
template<typename Base, typename F, typename T1, class... Args>
auto INVOKE(F Base::* fn, T1 &&target, Args &&...args)
// Noexcept specifications generate compiler errors unpacking args
//noexcept(noexcept((std::forward<T1>(target).*fn)(std::forward<Args>(args)...)))
 -> std::enable_if_t<std::is_function<F>::value &&
                     std::is_base_of<Base, std::decay_t<T1>>::value,
                     decltype((std::forward<T1>(target).*fn)(std::forward<Args>(args)...))>
{
    return (std::forward<T1>(target).*fn)(std::forward<Args>(args)...);
}
/* F is pointer to member function, and 1st arg is a reference wrapper  */
template<typename Base, typename F, typename T1, class... Args>
auto INVOKE(F Base::* fn, T1 &&target, Args &&...args)
//noexcept(noexcept((std::forward<T1>(target).get().*fn)(std::forward<Args>(args)...)))
 -> std::enable_if_t<std::is_function<F>::value &&
                     is_reference_wrapper<std::decay_t<T1>>::value,
                     decltype((std::forward<T1>(target).get().*fn)(std::forward<Args>(args)...))>
{
    return (std::forward<T1>(target).get().*fn)(std::forward<Args>(args)...);
}
/* F is pointer to member function, and 1st arg doesn't match class and isn't reference wrapper - assume pointer */
template<typename Base, typename F, typename T1, class... Args>
auto INVOKE(F Base::* fn, T1 &&target, Args &&...args)
//noexcept(noexcept(((*std::forward<T1>(target)).*fn)(std::forward<Args>(args)...)))
 -> std::enable_if_t<std::is_function<F>::value &&
                     !std::is_base_of<Base, std::decay_t<T1>>::value &&
                     !is_reference_wrapper<std::decay_t<T1>>::value,
                     decltype(((*std::forward<T1>(target)).*fn)(std::forward<Args>(args)...))>
{
    return ((*std::forward<T1>(target)).*fn)(std::forward<Args>(args)...);
}
/* F is pointer to member object, and only arg decays to matching class */
template<typename Base, typename F, typename T1>
auto INVOKE(F Base::* obj, T1 &&target)
//noexcept(noexcept(std::forward<T1>(target).*obj))
 -> std::enable_if_t<!std::is_function<F>::value &&
                     std::is_base_of<Base, std::decay_t<T1>>::value,
                     decltype(std::forward<T1>(target).*obj)>
{
    return std::forward<T1>(target).*obj;
}
/* F is pointer to member object, and only arg is a reference wrapper */
template<typename Base, typename F, typename T1>
auto INVOKE(F Base::* obj, T1 &&target)
//noexcept(noexcept(std::forward<T1>(target).get().*obj))
 -> std::enable_if_t<!std::is_function<F>::value &&
                     is_reference_wrapper<std::decay_t<T1>>::value,
                     decltype(std::forward<T1>(target).get().*obj)>
{
    return std::forward<T1>(target).get().*obj;
}
/* F is pointer to member object, and only arg doesn't match class and isn't reference wrapper - assume pointer */
template<typename Base, typename F, typename T1>
auto INVOKE(F Base::* obj, T1 &&target)
//noexcept(noexcept((*std::forward<T1>(target)).*obj))
 -> std::enable_if_t<!std::is_function<F>::value &&
                     !std::is_base_of<Base, std::decay_t<T1>>::value &&
                     !is_reference_wrapper<std::decay_t<T1>>::value,
                     decltype((*std::forward<T1>(target)).*obj)>
{
    return (*std::forward<T1>(target)).*obj;
}
/* F is not a pointer to member */
template<typename F, typename... Args>
auto INVOKE(F&& f, Args&&... args)
//noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
 -> std::enable_if_t<!std::is_member_pointer<std::decay_t<F>>::value ||
                      (std::is_member_object_pointer<std::decay_t<F>>::value && sizeof...(args) != 1),
                     decltype(std::forward<F>(f)(std::forward<Args>(args)...))>
{
    return std::forward<F>(f)(std::forward<Args>(args)...);
}
#endif // __cpp_lib_invoke

template <typename Void, typename F, typename... Args>
struct invoke_result { };
template <typename F, typename... Args> // void_t<decltype(INVOKE)> appears not to work here - why?
struct invoke_result<decltype(void(INVOKE(std::declval<F>(), std::declval<Args>()...))), F, Args...> :
    type_identity<decltype(INVOKE(std::declval<F>(), std::declval<Args>()...))> { };

// This would be a lot shorter if we could get the detector idiom to work and use it
template <typename R, typename InvokeResult, typename = void>
struct is_invocable_r : std::false_type { };
template <typename R, typename InvokeResult>
struct is_invocable_r <R, InvokeResult, void_t<typename InvokeResult::type>> :
    disjunction<std::is_void<R>, std::is_convertible<typename InvokeResult::type, R>> { };

template <typename R, typename InvokeResult, typename = void>
struct is_nothrow_invocable_r : std::false_type { };
template <typename R, typename InvokeResult>
struct is_nothrow_invocable_r<R, InvokeResult, void_t<typename InvokeResult::type>> :
    disjunction<std::is_void<R>,
                     conjunction<std::is_convertible<typename InvokeResult::type, R>,
                                 std::is_nothrow_constructible<R, typename InvokeResult::type>>> { };

} //namespace impl

template <class F, class... Args>
struct invoke_result : impl::invoke_result<void, F, Args...> { };

template <class F, class... Args>
using invoke_result_t = typename invoke_result<F, Args...>::type;

template <class F, class... Args>
struct is_invocable : impl::is_invocable_r<void, invoke_result<F, Args...>> { };

#if 0 // No exceptions in mbed OS
template <class F, class... Args>
struct is_nothrow_invocable : impl::is_nothrow_invocable_r<void, invoke_result<F, Args...>> { };
#else
template <class F, class... Args>
struct is_nothrow_invocable : impl::is_invocable_r<void, invoke_result<F, Args...>> { };
#endif

template <typename R, typename F, typename... Args>
struct is_invocable_r : impl::is_invocable_r<R, invoke_result<F, Args...>> { };

#if 0 // No exceptions in mbed OS
template <typename R, typename F, typename... Args>
struct is_nothrow_invocable_r : conjunction<impl::is_nothrow_invocable_r<R, invoke_result<F>>,
                                            std::is_convertible<invoke_result_t<F, Args...>, R>> { };
#else
template <typename R, typename F, typename... Args>
struct is_nothrow_invocable_r : impl::is_invocable_r<R, invoke_result<F, Args...>> { };
#endif

#endif // __cpp_lib_is_invocable


} // namespace mstd

#ifdef __CC_ARM
// And may as well give ourselves all the post C++14 stuff in std
// to aid our implementations of std::tuple etc - not safe to do this for other toolchains
namespace std {
    using mstd::bool_constant;
    using mstd::conjunction;
    using mstd::disjunction;
    using mstd::negation;
    using mstd::void_t;
    using mstd::type_identity;
    using mstd::type_identity_t;
    using mstd::is_invocable;
    using mstd::is_invocable_r;
    using mstd::invoke_result;
    using mstd::invoke_result_t;
    using mstd::remove_cvref;
    using mstd::remove_cvref_t;
}
#endif

#endif /* MSTD_TYPE_TRAITS_ */
