/*========================== begin_copyright_notice ============================
Copyright (C) 2021-2024 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/
/*!
 * @file Basic type definitions in GTPin
 */

#ifndef GT_BASIC_DEFS_H_
#define GT_BASIC_DEFS_H_

#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include <assert.h>
#include <type_traits>
#include <algorithm>
#include <utility>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
#include <memory>

/* ============================================================================================= */
// Supported C++ and STL standards
/* ============================================================================================= */
#if ((__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L))
#define _GTPIN_CPP_STD 20
#elif ((__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
#define _GTPIN_CPP_STD 17
#elif ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L))
#define _GTPIN_CPP_STD 14
#elif ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L))
#define _GTPIN_CPP_STD 11
#else
#error GTPin build error: Unsupported compiler, C++11 complince required
#endif

#if !defined (STLPORT) && !defined(PIN_CRT)
#define _GTPIN_STL_STD _GTPIN_CPP_STD
#else
#define _GTPIN_STL_STD 0
#endif

namespace gtpin
{
#pragma pack(push, 8)

/* ============================================================================================= */
// STLPort complements
/* ============================================================================================= */

/// @see std::to_string
template <typename T> std::string ToString(T val)
{
#if (_GTPIN_STL_STD >= 11)
    return std::to_string(val);
#else
    std::ostringstream os; os << val; return os.str();
#endif
}

#if (_GTPIN_CPP_STD >= 11)
/// @see std::remove_reference
template<typename T> struct RemoveRef_                          { using type = T; };
template<typename T> struct RemoveRef_<T&>                      { using type = T; };
template<typename T> struct RemoveRef_<T&&>                     { using type = T; };
template<typename T> using  RemoveRef =                         typename RemoveRef_<T>::type;

/// @see std::remove_cv
template<typename T> struct RemoveCv_                           { using type = T; };
template<typename T> struct RemoveCv_<const T>                  { using type = T; };
template<typename T> struct RemoveCv_<volatile T>               { using type = T; };
template<typename T> struct RemoveCv_<const volatile T>         { using type = T; };
template<typename T> using  RemoveCv =                          typename RemoveCv_<T>::type;

/// @see std::remove_cvref
template<typename T> using RemoveCvRef =                        RemoveCv<RemoveRef<T>>;

/// @see std::enable_if
template<bool B, typename T = void> struct EnableIf_            {};
template<typename T>                struct EnableIf_<true, T>   { using type = T; };
template<bool B, typename T = void> using  EnableIf =           typename EnableIf_<B, T>::type;

/// @see std::is_same
template<typename T, typename U>    struct IsSameType           { static constexpr bool value = false; };
template<typename T>                struct IsSameType<T, T>     { static constexpr bool value = true; };
template<typename T, typename U>    using  IsAkinType =         IsSameType<RemoveCvRef<T>, RemoveCvRef<U>>;
template<typename T>                using  IsVoidType =         IsAkinType<T, void>;

/// @see std::is_pointer
template<typename T> struct         IsPointer                   { static constexpr bool value = false; };
template<typename T> struct         IsPointer<T*>               { static constexpr bool value = true; };

/// @see std::is_array
template<typename T>                struct  IsArray             { static constexpr bool value = false; };
template<typename T>                struct  IsArray<T[]>        { static constexpr bool value = true; };
template<typename T, size_t N>      struct  IsArray<T[N]>       { static constexpr bool value = true; };

/// Structure that checks if T and U refer/point to the same type, after recursively removing qualifiers and pointers
template<typename T, typename U>    struct IsAkinPtr_           { static constexpr bool value = IsSameType<T, U>::value; };
template<typename T, typename U >   struct IsAkinPtr_<T*, U*>   { static constexpr bool value = IsAkinPtr_<RemoveCv<T>, RemoveCv<U>>::value; };
template<typename T, typename U>    using  IsAkinPtr =          IsAkinPtr_<RemoveCvRef<T>, RemoveCvRef<U>>;

/// @return rvalue reference to argument 'arg'. Like std::move, used to indicate that 'arg' may be "moved from".
template<typename T> constexpr RemoveRef<T> && ToMove(T&& arg) noexcept
{
    return static_cast<RemoveRef<T> &&>(arg);
}

#if (_GTPIN_STL_STD >= 11)
template<typename T> struct IsIntegral { static constexpr bool value = std::is_integral<T>::value; };
#else
template<typename T> struct IsIntegral { static constexpr bool value = (IsSameType<T, bool>::value ||
                                                                        IsSameType<T, char>::value ||
                                                                        IsSameType<T, short>::value ||
                                                                        IsSameType<T, int>::value ||
                                                                        IsSameType<T, long>::value ||
                                                                        IsSameType<T, long long>::value ||
                                                                        IsSameType<T, unsigned char>::value ||
                                                                        IsSameType<T, unsigned short>::value ||
                                                                        IsSameType<T, unsigned int>::value ||
                                                                        IsSameType<T, unsigned long>::value ||
                                                                        IsSameType<T, unsigned long long>::value); };
#endif

#endif

/* ============================================================================================= */
// Static assertions
/* ============================================================================================= */

/// Static (compile-time) asserts
#define GTPIN_STATIC_ASSERT(x)          static_assert(x, "GTPIN_STATIC_ASSERT: " #x)
#define GTPIN_STATIC_ASSERT_MSG(x, m)   static_assert(x, m)

/*!
 * GTPin assumes compatible implementations of 'bool' type in tool and GTPin core.
 * The assumption seems to be valid for all modern compilers on platforms supported by GTPin.
 */
GTPIN_STATIC_ASSERT_MSG((sizeof(bool) == 1), "Incompatible bool type implementation");

#if (_GTPIN_STL_STD >= 11)

/// Assert the type 'T' is a standard-layout type
#define GTPIN_ASSERT_STANDARD_LAYOUT(T) static_assert(std::is_standard_layout<T>::value, #T " layout is not standard")

/// Assert the type 'T' is default-constructible
#define GTPIN_ASSERT_DEFAULT_CONSTRUCTIBLE(T) static_assert(std::is_default_constructible<T>::value, #T " is not default-constructible")

/// Assert the type 'T' is an empty type - a non-union class type with no non-static data members
#define GTPIN_ASSERT_EMPTY_TYPE(T) static_assert(std::is_empty<T>::value, #T " is not empty")

/// Assert the type 'T' is trivially copyable
#define GTPIN_ASSERT_TRIVIALLY_COPYABLE(T) static_assert(std::is_trivially_copyable<T>::value, #T " is not trivially copyable")

/*!
 * Assert the type 'T' is a pure abstract class type
 * @note C++ does not define a standard method to check pure abstract types. The following assert
 *       assumes that the only data in the pure abstract is vft pointer.
 *       The assumption seems to be valid for all modern compilers on platforms supported by GTPin.
 */
#define GTPIN_ASSERT_PURE_ABSTRACT(T) static_assert(std::is_abstract<T>::value && (sizeof(T) == sizeof(void*)), #T " is not pure abstract")

#else
#define GTPIN_ASSERT_STANDARD_LAYOUT(T)         GTPIN_STATIC_ASSERT(true)
#define GTPIN_ASSERT_DEFAULT_CONSTRUCTIBLE(T)   GTPIN_STATIC_ASSERT(true)
#define GTPIN_ASSERT_EMPTY_TYPE(T)              GTPIN_STATIC_ASSERT(true)
#define GTPIN_ASSERT_TRIVIALLY_COPYABLE(T)      GTPIN_STATIC_ASSERT(true)
#define GTPIN_ASSERT_PURE_ABSTRACT(T)           GTPIN_STATIC_ASSERT(true)
#endif

/* ============================================================================================= */
// Runtime assertions
/* ============================================================================================= */

/*!
 * Log the specified error message along with the code location information. Abort the current process.
 * @param[in] text      Text of the message
 * @param[in] fun       Name of the function associated with the error
 * @param[in] line      Code line number associated with the error
 */
#if defined(GTPIN_IMPL)
[[ noreturn ]] void GtPinError(const std::string& text, const std::string& fun, uint32_t line);
#else
[[ noreturn ]] inline void GtPinError(const std::string& text, const std::string& fun, uint32_t line)
{
    std::cerr << "GTPin ERROR: " << text << std::endl << "at: "<< fun << " : " << line << std::endl; exit(EXIT_FAILURE);
}
#endif

/*!
 * Log the specified warning message
 * @param[in] text      Text of the message
 */
#if defined(GTPIN_IMPL)
void GtPinWarning(const std::string& text);
#else
inline void GtPinWarning(const std::string& text)
{
    std::cerr << "GTPin WARNING: " << text << std::endl;
}
#endif

#define GTPIN_ERROR_MSG(m)      do { gtpin::GtPinError(m, __FUNCTION__, __LINE__); } while(0)
#define GTPIN_WARNING(m)        do { gtpin::GtPinWarning(m); } while(0)
#define GTPIN_ERROR()           GTPIN_ERROR_MSG("")
#define GTPIN_ASSERT_MSG(x,m)   do { if (!(x)) { GTPIN_ERROR_MSG(m); } } while(0)
#define GTPIN_ASSERT(x)         GTPIN_ASSERT_MSG(x, #x)

/* ============================================================================================= */
// Macro EXPORT_C_FUNC
/* ============================================================================================= */
/// Attribute of the C function exported by dynamic library
#if defined(_MSC_VER)
#define EXPORT_C_FUNC extern "C" __declspec(dllexport)
#else
#define EXPORT_C_FUNC extern "C"
#endif

/* ============================================================================================= */
// Struct IntSized
/* ============================================================================================= */
/// Signed integer type of a fixed size
template <size_t> struct IntSized {};
template <>       struct IntSized<sizeof(int64_t)>  { using type = int64_t; };
template <>       struct IntSized<sizeof(int32_t)>  { using type = int32_t; };
template <>       struct IntSized<sizeof(int16_t)>  { using type = int16_t; };
template <>       struct IntSized<sizeof(int8_t)>   { using type = int8_t; };

/* ============================================================================================= */
// Struct UintSized
/* ============================================================================================= */
/// Unsigned integer type of a fixed size
template <size_t> struct UintSized {};
template <>       struct UintSized<sizeof(uint64_t)> { using type = uint64_t; };
template <>       struct UintSized<sizeof(uint32_t)> { using type = uint32_t; };
template <>       struct UintSized<sizeof(uint16_t)> { using type = uint16_t; };
template <>       struct UintSized<sizeof(uint8_t)>  { using type = uint8_t; };

/* ============================================================================================= */
// Class Validatable
/* ============================================================================================= */
/*! @ingroup GTPIN_UTILITIES
 * @brief Wrapper of type 'T' with a distinct value 'INV' which is considered as invalid.
 * 
 * The 'UniqueType' argument can be used to create unique types out of this template, e.g.
 * Validatable<uint32_t, 0, struct A> and Validatable<uint32_t, 0, struct B>.
 */
template <typename T, T INV, typename UniqueType = void> struct Validatable
{
    using value_type = T;

    constexpr Validatable() : _value(INV) {}            ///< Default constructor (of invalid value)
    constexpr Validatable(const T& v) : _value(v) {}    ///< Conversion constructor

    constexpr operator T() const { return _value; }     ///< Conversion operator

    static constexpr T Invalid() { return INV; }                        ///< Return invalid value
    constexpr bool     IsValid()   const { return (_value != INV); }    ///< Return true, if value is valid
    constexpr bool     IsInvalid() const { return (_value == INV); }    ///< Return true, if value is invalid

    T _value;
};

/// @ingroup GTPIN_UTILITIES
/// Validatable uint32_t type with '-1' as invalid value
template <typename UniqueType = void> using Uint32Validatable = Validatable<uint32_t, uint32_t(-1), UniqueType>;
/// Validatable uint64_t type with '-1' as invalid value
template <typename UniqueType = void> using Uint64Validatable = Validatable<uint64_t, uint64_t(-1), UniqueType>;
/// Validatable uintptr_t type with '0' as invalid value
template <typename UniqueType = void> using UintPtrValidatable = Validatable<uintptr_t, uint64_t(0), UniqueType>;

/* ============================================================================================= */
// Struct Enum32T
/* ============================================================================================= */
/*! @internal
 * Wrapper of enumerated type 'T' with the fixed 32-bit underlying implementation.
 * @note This wrapper can be used to ensure compatibilty of enumerators in external library
 * interfaces. It is mostly intended for wrapping external/legacy types. New enumerated types
 * in the GTPin interface can use the standard C++ syntax "enum name :uint32_t" to ensure 32-bit
 * underlying implementation.
 */
template <typename T> struct Enum32T
{
    constexpr Enum32T() = default;
    constexpr Enum32T(T v) : _value(v) {}
    constexpr operator T() const { return T(_value); }

    uint32_t _value;
};

/* ============================================================================================= */
// Struct Span
/* ============================================================================================= */
/*! @ingroup GTPIN_UTILITIES
 * @brief Structure that points to an array of elements of type 'T', without owning it.
 * 
 * Similar to std::span (C++20) with static extent
 */
template <typename T> struct Span
{
    using value_type        = T;
    using element_type      = T;
    using pointer           = T*;
    using const_pointer     = T*;
    using reference         = T&;
    using const_reference   = T&;
    using iterator          = T*;
    using const_iterator    = T*;

    // Constructors
    constexpr Span() : _ptr(nullptr), _size(0) {}

    template<typename U, EnableIf<IsAkinPtr<U, T>::value, bool> = true>
    constexpr Span(U* p, size_t s) : _ptr(p), _size(s) {}

    template<typename U, size_t N, EnableIf<IsAkinPtr<U, T>::value, bool> = true>
    constexpr Span(U (&arr)[N]) : _ptr(&arr[0]), _size(N) {}

    template<typename U, EnableIf<IsAkinPtr<typename U::const_pointer, pointer>::value, bool> = true>
    constexpr Span(const U& u) : _ptr(u.data()), _size(u.size()) {}

    template<typename U, EnableIf<IsAkinPtr<typename U::pointer, pointer>::value, bool> = true>
    constexpr Span(U& u) : _ptr(u.data()), _size(u.size()) {}

    constexpr Span(const Span&) = default;

    constexpr reference operator[] (size_t pos)  const { return _ptr[pos]; } ///< Return reference to the element at the specified position

    constexpr iterator begin()  const { return _ptr; }          ///< Return iterator addressing the first element of the array
    constexpr iterator end()    const { return _ptr + _size; }  ///< Return Iiterator that indicates end of the array

    constexpr size_t    size()  const { return _size; }         ///< Return number of elements in the array
    constexpr bool      empty() const { return (_size == 0); }  ///< Return true if array is empty
    constexpr pointer   data()  const { return _ptr;}           ///< Return pointer to the beginning of the array of elements
    constexpr reference first() const { return *begin(); }      ///< Return reference to the first element of the array. @pre !empty()
    constexpr reference last()  const { return *(end() - 1); }  ///< Return reference to the last element of the array. @pre !empty()

    pointer _ptr;
    size_t  _size;
};

using ByteSpan      = Span<uint8_t>;
using ConstByteSpan = Span<const uint8_t>;

/* ============================================================================================= */
// Class UniquePtr
/* ============================================================================================= */
/*! @ingroup GTPIN_UTILITIES
 * @brief A smart pointer that provides some of the std::unique_ptr functionality for clients that cannot
 * use C++11 STL
 * 
 * @param   T   Type of the object managed by UniquePtr
 * @param   D   Type of the deleter - function object that implements operator()(T*) const.
 *              For the sake of simplicity, this type is assumed to be empty (std::is_empty) -
 *              a type with no non-static data members
 */
template<typename T, typename D> class UniquePtr
{
public:
    GTPIN_ASSERT_DEFAULT_CONSTRUCTIBLE(D);
    GTPIN_ASSERT_EMPTY_TYPE(D);

    using element_type = T;
    using pointer      = T*;
    using reference    = T&;

public:
    UniquePtr()                                          noexcept : _ptr(nullptr)      {} ///< Construct UniquePtr(nullptr)
    explicit UniquePtr(pointer ptr)                      noexcept : _ptr(ptr)          {} ///< Construct UniquePtr that owns 'ptr'

    #if (_GTPIN_CPP_STD >= 11)
    template<typename U> UniquePtr(UniquePtr<U, D>&& up) noexcept : _ptr(up.Release()) {} ///< Construct UniquePtr by moving 'up' to *this

    /// Move assignment
    template<typename U> UniquePtr& operator = (UniquePtr<U, D>&& rh) noexcept
    {
        Reset(rh.Release());
        return *this;
    }
    #endif

    UniquePtr(const UniquePtr&)            = delete;        ///< @internal Deleted ctor
    UniquePtr& operator=(const UniquePtr&) = delete;        ///< @internal Deleted assignment operator

    /// Destructor
    ~UniquePtr() noexcept
    {
        if (_ptr)
        {
            D()(_ptr);
        }
    }

    pointer Get()            const noexcept {return _ptr;}  ///< Return pointer to the owned object
    pointer operator->()     const noexcept {return _ptr;}  ///< Return pointer to the owned object
    reference operator*()    const          {return *_ptr;} ///< Return reference to the owned object

    explicit operator bool() const noexcept {return _ptr;}  ///< Return true if *this owns an object, false otherwise

    /// Swap the managed objects
    void Swap(UniquePtr& rh) noexcept
    {
        std::swap(_ptr, rh._ptr);
    }

    /*!
     * @brief Release the ownership of the managed object if any. The caller is responsible for deleting the object
     * @return Pointer to the managed object or nullptr
     */
    pointer Release() noexcept
    {
        pointer ptr = _ptr;
        _ptr = nullptr;
        return ptr;
    }

    /// Delete the managed object and replace it with the specified one
    void Reset(pointer ptr = nullptr) noexcept
    {
        pointer tmp = _ptr;
        _ptr = ptr;
        if (tmp)
        {
            D()(tmp);
        }
    }

private:
    pointer   _ptr;
};

/*! @addtogroup GTPIN_UTILITIES 
    @{
 */

/// %UniquePtr equality operator
template <typename T, typename D,  typename U, typename E>
inline bool operator ==(const UniquePtr<T, D>& up1, const UniquePtr<U, E>& up2) { return up1.Get() == up2.Get(); }
/// %UniquePtr inequality operator
template <typename T, typename D,  typename U, typename E>
inline bool operator !=(const UniquePtr<T, D>& up1, const UniquePtr<U, E>& up2) { return up1.Get() != up2.Get(); }

#if (_GTPIN_STL_STD >= 11)
template <typename T, typename D>
inline bool operator ==(const UniquePtr<T, D>& up, std::nullptr_t) { return up.Get() == nullptr; }
template <typename T, typename D> inline bool operator !=(const UniquePtr<T, D>& up, std::nullptr_t)
{ return up.Get() != nullptr; }
#endif

/*! @} */

/* ============================================================================================= */
// Class Array
/* ============================================================================================= */
/*! @ingroup GTPIN_UTILITIES
 * @brief A container that encapsulates fixed size arrays
 * 
 * This template class provides functionality similar to std::array for clients that cannot use C++11 STL
 * 
 * @param   T   Type of the element
 * @param   N   Number of elements in the array
 */
template <typename T, size_t N> class Array
{
public:
    using value_type      = T;
    using element_type    = T;
    using pointer         = T*;
    using const_pointer   = const T*;
    using reference       = T&;
    using const_reference = const T&;
    using iterator       = T*;
    using const_iterator = const T*;

              iterator          begin()                         noexcept    { return _arr; }
    constexpr const_iterator    begin()                 const   noexcept    { return _arr; }
              iterator          end()                           noexcept    { return _arr + N; }
    constexpr const_iterator    end()                   const   noexcept    { return _arr + N; }
    constexpr size_t            size()                  const   noexcept    { return N; }
    constexpr size_t            max_size()              const   noexcept    { return N; }
    constexpr bool              empty()                 const   noexcept    { return (N == 0); }
              reference         front()                                     { return _arr[0]; }
    constexpr const_reference   front()                 const               { return _arr[0]; }
              reference         back()                                      { return _arr[N - 1]; }
    constexpr const_reference   back()                  const               { return _arr[N - 1]; }
              pointer           data()                          noexcept    { return _arr; }
    constexpr const_pointer     data()                  const   noexcept    { return _arr; }
              reference         at(size_t pos)                              { return _arr[pos]; }
    constexpr const_reference   at(size_t pos)          const               { return _arr[pos]; }
              reference         operator[](size_t pos)                      { return _arr[pos]; }
    constexpr const_reference   operator[](size_t pos)  const               { return _arr[pos]; }

    void fill(const T& val)
    {
        for (iterator it = begin(); it != end(); ++it) { *it = val; }
    }

    void swap(Array& rh) noexcept
    {
        if (this != &rh)
        {
            for (iterator it1 = begin(), it2 = rh.begin(); it1 != end(); ++it1, ++it2) { std::swap(*it1, *it2); }
        }
    }

    T _arr[N];
};

/// Specialization of Array template for zero-size array
template <typename T> class Array<T, 0>
{
public:
    using value_type      = T;
    using element_type    = T;
    using pointer         = T*;
    using const_pointer   = const T*;
    using reference       = T&;
    using const_reference = const T&;
    using iterator       = T*;
    using const_iterator = const T*;

              iterator          begin()                         noexcept    { return nullptr; }
    constexpr const_iterator    begin()                 const   noexcept    { return nullptr; }
              iterator          end()                           noexcept    { return nullptr; }
    constexpr const_iterator    end()                   const   noexcept    { return nullptr; }
    constexpr size_t            size()                  const   noexcept    { return 0; }
    constexpr size_t            max_size()              const   noexcept    { return 0; }
    constexpr bool              empty()                 const   noexcept    { return true; }
              reference         front()                                     { return OutOfBounds(); }
    constexpr const_reference   front()                 const               { return OutOfBounds(); }
              reference         back()                                      { return OutOfBounds(); }
    constexpr const_reference   back()                  const               { return OutOfBounds(); }
              pointer           data()                          noexcept    { return nullptr; }
    constexpr const_pointer     data()                  const   noexcept    { return nullptr; }
              reference         at(size_t)                                  { return OutOfBounds(); }
    constexpr const_reference   at(size_t)              const               { return OutOfBounds(); }
              reference         operator[](size_t)                          { return OutOfBounds(); }
    constexpr const_reference   operator[](size_t)      const               { return OutOfBounds(); }
    void                        fill(const T&)                              {}
    void                        swap(Array&)                    noexcept    {}

private:
    static reference OutOfBounds()
    {
        GTPIN_ERROR_MSG("Array<T, 0> subscript out of range");
        return *(iterator());
    }
};

/* ===================================================================================================== */
// Class IntRange
/* ===================================================================================================== */
/*! @ingroup GTPIN_UTILITIES
 * @brief Iterable range of integral values
 * 
 * @param   T   type of the value 
 * @param   S   type of the range size
 */
template <typename T, typename S = size_t> class IntRange
{
    GTPIN_STATIC_ASSERT(IsIntegral<T>::value);
    using UnsignedT = typename UintSized<sizeof(T)>::type;

public:
    /// Random iterator in the range
    class Iterator
    {
        T _value;
    public:
        using value_type    = T;
        using reference     = const T&;
        using pointer       = const T*;

        constexpr Iterator (const T& value) : _value(value) {}

        constexpr reference operator*()       const   { return _value; }
        constexpr pointer   operator->()      const   { return &_value; }

        constexpr Iterator  operator+(S diff) const   { return Iterator(T(_value + diff)); }
        constexpr Iterator  operator-(S diff) const   { return Iterator(T(_value - diff)); }

                  Iterator& operator+=(S diff)        { _value += diff; return *this; }
                  Iterator& operator-=(S diff)        { _value -= diff; return *this; }
                  Iterator& operator++()              { ++_value; return *this; }
                  Iterator  operator++(int)           { return Iterator(_value++); }
                  Iterator& operator--()              { --_value; return *this; }
                  Iterator  operator--(int)           { return Iterator(_value--); }

        constexpr bool operator==(const Iterator& other) const { return (_value == other._value); }
        constexpr bool operator!=(const Iterator& other) const { return !(*this == other); }
    };

    using value_type        = T;
    using pointer           = const T*;
    using const_pointer     = const T*;
    using reference         = const T&;
    using const_reference   = const T&;
    using iterator          = Iterator;
    using const_iterator    = Iterator;

public:
    // Constructors
    constexpr IntRange() : _first(0), _size(0) {}                         ///< Construct an empty range
    constexpr IntRange(T first, S size) : _first(first), _size(size) {}   ///< Construct a range in the specified bounds

    // Standard accessors
    constexpr iterator begin()    const   { return Iterator(_first); }
    constexpr iterator end()      const   { return Iterator(_first + _size); }
    constexpr size_t   size()     const   { return _size; }
    constexpr bool     empty()    const   { return (_size == 0); }

    // Accessors
    constexpr T    First()        const   { return _first; }              ///< @return The first value in the range
    constexpr T    Base()         const   { return _first; }              ///< @return The first value in the range
    constexpr T    End()          const   { return T(_first + _size); }   ///< @return The end value of the range
    constexpr S    Size()         const   { return size(); }              ///< @return The number of values in the range
    constexpr bool IsEmpty()      const   { return empty(); }             ///< @return true if the range is empty

    /// @return true if specified value belongs to the range
    constexpr  bool Contains(T value) const { return (UnsignedT(value - _first) < _size); }

    /// @return true if this range contains the specified other range
    constexpr bool Contains(const IntRange<T, S>& other) const
    {
        return (Contains(other._first) && Contains(other.End() - 1) && !other.IsEmpty());
    }

    /// @return true if this range overlaps 'other' range
    constexpr bool Overlaps(const IntRange<T, S>& other) const
    {
        return ((Contains(other._first) && !other.IsEmpty()) || (other.Contains(_first) && !IsEmpty()));
    }

    // Mutators
    IntRange& SetFirst(T first)  { _first = first; return *this; }        ///< Set origin, retain size of the range
    IntRange& SetSize(S size)    { _size  = size;  return *this; }        ///< Set size, retain origin of the range

    /// If needed, extend boundaries of this range to include the specified value
    IntRange& Include(T value)
    {
        if (!Contains(value))
        {
            if (_size == 0)
            {
                _size  = 1;
                _first = value;
            }
            else if (value < _first)
            {
                _size  = End() - value;
                _first = value;
            }
            else
            {
                _size = (value + 1) - _first;
            }
        }
        return *this;
    }

    /// If needed, extend boundaries of this range to include the specified 'other' range
    IntRange& Include(const IntRange<T, S>& other)
    {
        if (!other.IsEmpty())
        {
            Include(other.First());
            Include(other.End() - 1);
        }
        return *this;
    }

    /// Intersect this range with the specified 'other' range
    IntRange& Intersect(const IntRange<T, S>& other)
    {
        const T thisEnd  = End();
        const T otherEnd = other.End();

        if (Contains(other._first))
        {
            _first = other._first;
            _size  = (other.Contains(thisEnd) ? thisEnd : otherEnd) - _first;
        }
        else if (other.Contains(_first))
        {
            _size  = (Contains(otherEnd) ? otherEnd : thisEnd) - _first;
        }
        return *this;
    }

private:
    T _first;    ///< First value in the range
    S _size;     ///< Number of values in the range
};

using Uint32Range   = IntRange<uint32_t, uint32_t>; ///< Range of uint32_t values
using Uint64Range   = IntRange<uint64_t, uint64_t>; ///< Range of uint64_t values
using AddrRange     = IntRange<uintptr_t>;          ///< Range of uintptr_t values

#if (_GTPIN_CPP_STD >= 11)
/* ===================================================================================================== */
// Class VariadicElement
/* ===================================================================================================== */
/*!
 * Class that provides compile-time indexed access to the types of elements in a variadic template
 * @param  IDX    Ordinal number of the element in a variadic template
 * @param  ARGS  Parameter pack of a variadic template
 * 
 * Example:
 * template <uint32_t IDX, class... ARGS> using MyElementType = typename VariadicElement<IDX, ARGS...>::type;
 */
template <uint32_t IDX, typename... ARGS> class VariadicElement
{
    template <typename... _T>              struct _S {}; // A variadic template
    template <uint32_t _I, typename... _T> struct _E;    // _E::type is the type of the _I-th element of _S 

    template <uint32_t _I, typename _Head, typename... _Tail> struct _E<_I, _S<_Head, _Tail...>> : _E<_I-1, _S<_Tail...>> {};
    template < typename _Head, typename... _Tail > struct _E<0, _S<_Head, _Tail...>>                 { using type = _Head; };

public:
    using type = typename _E<IDX, _S<ARGS...>>::type;
};
#endif

/* ===================================================================================================== */
// Class BitField
/* ===================================================================================================== */
/*! @internal
 * Bit field of an integral type T comprising a single contiguous interval of bits
 */
template <typename T> class BitField
{
public:
    using field_type = T;

    /// Construct an empty bit field
    constexpr BitField() :  _firstPos(0), _width(0) {}

    /*!
     * Construct a bit field that starts at 'firstPos' and spans 'width' bits.
     * if the value of 'numBits' is greater than the number of bits in T, the behavior is undefined.
     * if the value of 'firstBitPos' is greater or equal to the number of bits in T, the behavior is undefined.
     */
    constexpr BitField(uint32_t firstPos, uint32_t width) : _firstPos(firstPos), _width(width) {}

    /*!
     * Accessors.
     * In the empty bit field, FirstPos(), LastPos() and EndPos() values are undefined
     */
    constexpr bool     IsEmpty()  const { return (_width == 0); }            ///< @return true if this field is empty
    constexpr uint32_t Width()    const { return _width; }                   ///< @return Number of bits in this field
    constexpr uint32_t FirstPos() const { return _firstPos; }                ///< @return Position of the first bit
    constexpr uint32_t LastPos()  const { return _firstPos + (_width - 1); } ///< @return Position of the last bit
    constexpr uint32_t EndPos()   const { return _firstPos + _width; }       ///< @return Position beyond the last bit

    /// @return Value of type T in which bits of this bit field are set one. Return zero if this bit field is empty
    constexpr T Mask() const
    {
        return ((_width == 0) ? 0 : (((T(2) << (_width - 1)) - 1) << _firstPos));
    }

    /// @return value stored in this bit field of value 'val'. Return zero if this bit field is empty
    constexpr T GetValue(T val) const
    {
        return ((val & Mask()) >> _firstPos);
    }

    /*
     * Put value 'fieldVal' into this bit field of value 'val'. If this bit field is empty, do nothing.
     * @return modified 'val'
     */
    T SetValue(T val, T fieldVal) const
    {
        const T mask = Mask();
        return (val & ~mask) | ((fieldVal << _firstPos) & mask);
    }

private:
    uint32_t    _firstPos;  ///< Position of the first bit of the field
    uint32_t    _width;     ///< Number of bits in the field
};

/* ===================================================================================================== */
// Struct ScatteredBitField
/* ===================================================================================================== */
/*! @internal
 * Bit field of an integral type T scattered through multiple bit intervals.
 * It is modeled as an array of BitField objects representing bit intervals from LSB to MSB
 */
template <typename T> struct ScatteredBitField
{
public:
    using field_type = T;

    using value_type        = BitField<T>;
    using reference         = BitField<T>&;
    using const_reference   = const BitField<T>&;
    using iterator          = BitField<T>*;
    using const_iterator    = const BitField<T>*;

    /// Constructors
    constexpr ScatteredBitField() : _numIntervals(0) {}
    ScatteredBitField(const BitField<T>& bf) : _numIntervals(0) { if (!bf.IsEmpty()) { _intervals[_numIntervals++] = bf; }}
    template< typename ... Args > ScatteredBitField(const Args&... args) : _numIntervals(0) { Append(args...);}

    // Iteration through bit intervals
    constexpr const_iterator begin() const { return &_intervals[0]; }             ///< @return Iterator pointing to the first interval
    constexpr const_iterator end()   const { return &_intervals[_numIntervals]; } ///< @return Iterator pointing beyond the last interval
    iterator                 begin()       { return &_intervals[0]; }             ///< @return Iterator pointing to the first interval
    iterator                 end()         { return &_intervals[_numIntervals]; } ///< @return Iterator pointing beyond the last interval

    constexpr bool     IsEmpty()      const { return (_numIntervals == 0); }///< @return true if this field is empty
    constexpr uint32_t NumIntervals() const { return _numIntervals; }       ///< @return Number of intervals in this field

    /// @return Maximum number of intervals in bit fields of type T
    static constexpr uint32_t MaxIntervals() { return sizeof(_intervals) / sizeof(_intervals[0]); }

    /// @return Total Number of bits in all intervals of this field
    uint32_t Width() const
    {
        uint32_t width = 0; for (const auto& bf : *this) { width += bf.Width(); }
        return width;
    }

    /// Append 'bf' bit interval to this bit field. Combine adjacent intervals. Do nothing if 'bf' is empty
    ScatteredBitField& Append(const BitField<T>& bf)
    {
        if (!bf.IsEmpty())
        {
            if (!IsEmpty() && (_intervals[_numIntervals - 1].EndPos()) == bf.FirstPos())
            {
                BitField<T>& lastInterval = _intervals[_numIntervals - 1];
                lastInterval = {lastInterval.FirstPos(), lastInterval.Width() + bf.Width()}; // Combine 'lastInterval' and 'bf'
            }
            else
            {
                GTPIN_ASSERT(_numIntervals < MaxIntervals()); _intervals[_numIntervals++] = bf;
            }
        }
        return *this;
    }

    /// Append 'sbf' bit intervals to this bit field. Combine adjacent intervals
    ScatteredBitField& Append(const ScatteredBitField<T>& sbf)
    {
        for (const auto& bf : sbf) { Append(bf); }
        return *this;
    }

    /// Append bit intervals from the specified parameter pack, which contains BitField and ScatteredBitField elements
    template< typename ... Args > ScatteredBitField& Append(const Args&... args)
    {
        ScatteredBitField fields[] = { args... };
        for (const auto& sbf : fields) { Append(sbf); }
        return *this;
    }

    /// @return Value stored in this bit field of value 'val'. Return zero if this bit field is empty
    T GetValue(T val) const
    {
        T fieldVal = 0;
        uint32_t shift = 0;
        for (const auto& bf : *this) { fieldVal |= (bf.GetValue(val) << shift); shift += bf.Width(); }
        return fieldVal;
    }

    /*!
     * Put value 'fieldVal' into this bit field of value 'val'. If this bit field is empty, do nothing.
     * @return modified 'val'
     */
    T SetValue(T val, T fieldVal) const
    {
        for (auto& bf : *this)
        {
            val = bf.SetValue(val, BitField<T>(0, bf.Width()).GetValue(fieldVal));
            fieldVal >>= bf.Width();
        }
        return val;
    }

private:
    BitField<T> _intervals[sizeof(T) * CHAR_BIT]; ///< Intervals of bits in this bit field, from LSB to MSB
    uint32_t    _numIntervals;                    ///< Number of non-empty bit intervals in this bit field
};

using BitFieldU32 = BitField<uint32_t>; ///< uint32_t bit field
using BitFieldU64 = BitField<uint64_t>; ///< uint64_t bit field
using ScatteredBitFieldU32 = ScatteredBitField<uint32_t>; ///< uint32_t scattered bit field
using ScatteredBitFieldU64 = ScatteredBitField<uint64_t>; ///< uint64_t scattered bit field

/* ===================================================================================================== */
// Class IMemAllocator
/* ===================================================================================================== */
/*! @ingroup GTPIN_UTILITIES
 * @brief Abstract low-level interface to the memory storage
 */
class IMemAllocator
{
public:
    /*!
     * @brief Allocate memory block of the specified size and alignment
     * 
     * @param[in]   size        Number of bytes to allocate
     * @param[in]   alignment   Required alignment of the memory block
     * @return Pointer to suitably aligned memory block, or null pointer on allocation failure
     */
    virtual void* Allocate(size_t size, size_t alignment = 8) = 0;

    /*!
     * @brief Deallocate memory block previously allocated by Allocate()
     * 
     * @param[in]   ptr         Pointer to a memory block to deallocate or a null pointer
     */
    virtual void Deallocate(void* ptr) noexcept = 0;

public:
    /// @internal Destructor
    virtual ~IMemAllocator() = default;
};

#pragma pack(pop)
}
#endif
