/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */

/*
 * ----------------------------------------------------------------------------------
 * Most of the code in this file is copied (with some modifications) from:
 *    limits
 * ----------------------------------------------------------------------------------
 * Copyright (C) 2006 Garrett A. Kajmowicz. All rights reserved.
 *
 * This file is part of the uClibc++ Library.
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any later
 * version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 * ----------------------------------------------------------------------------------
 */


#ifndef ECXX_CTL_NUMERIC_LIMITS_H
#define ECXX_CTL_NUMERIC_LIMITS_H


#include <eCxx/CTL/UninstantiableUncopyable.h>


namespace eCxx {


enum class FloatRoundStyle : int8_t {
    Indeterminate          = -1,
    TowardZero             =  0,
    ToNearest              =  1,
    TowardInfinity         =  2,
    TowardNegativeInfinity =  3
};


template <int> struct BitsToBase10;
template <>    struct BitsToBase10<  7> { static constexpr int Size =  2; };
template <>    struct BitsToBase10<  8> { static constexpr int Size =  2; };
template <>    struct BitsToBase10< 15> { static constexpr int Size =  4; };
template <>    struct BitsToBase10< 16> { static constexpr int Size =  4; };
template <>    struct BitsToBase10< 31> { static constexpr int Size =  9; };
template <>    struct BitsToBase10< 32> { static constexpr int Size =  9; };
template <>    struct BitsToBase10< 63> { static constexpr int Size = 18; };
template <>    struct BitsToBase10< 64> { static constexpr int Size = 19; };


template <int> struct BitsToBase10Max;
template <>    struct BitsToBase10Max<  7> { static constexpr int Size =  3; };
template <>    struct BitsToBase10Max<  8> { static constexpr int Size =  3; };
template <>    struct BitsToBase10Max< 15> { static constexpr int Size =  5; };
template <>    struct BitsToBase10Max< 16> { static constexpr int Size =  5; };
template <>    struct BitsToBase10Max< 31> { static constexpr int Size = 10; };
template <>    struct BitsToBase10Max< 32> { static constexpr int Size = 10; };
template <>    struct BitsToBase10Max< 63> { static constexpr int Size = 19; };
template <>    struct BitsToBase10Max< 64> { static constexpr int Size = 20; };


//
// A simple numeric-limits class
//
// NOTE : # The NumericLimits<T> class would be mostly useful on AVR where it does not include
//          an implementation of the C++ STL library.
//        # This eCxx's implementation of the NumericLimits<T> classes are simplified and most
//          likely incomplete and have incompatibilities with std::numeric_limits<T> classes.
//
template <typename ValueT>
class NumericLimits : public Uninstantiable {
    public:
        static constexpr bool isSpecialized = false;

        // General
        static constexpr ValueT min   () { return 0; }
        static constexpr ValueT max   () { return 0; }
        static constexpr ValueT lowest() { return 0; }

        static constexpr int  radix       = 0;
        static constexpr int  digits      = 0;
        static constexpr int  digits10    = 0;
        static constexpr int  digits10Max = 0;

        static constexpr bool isSigned    = false;
        static constexpr bool isInteger   = false;
        static constexpr bool isExact     = false;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = false;
        static constexpr bool isBounded   = false;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::Indeterminate;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<bool> : public Uninstantiable {
    public:
        using ValueT = bool;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return false; }
        static constexpr ValueT max   () { return true;  }
        static constexpr ValueT lowest() { return min(); }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = 1;
        static constexpr int  digits10    = 0;
        static constexpr int  digits10Max = 1;

        static constexpr bool isSigned    = false;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = false;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<signed char> : public Uninstantiable {
    public:
        using ValueT = signed char;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return SCHAR_MIN; }
        static constexpr ValueT max   () { return SCHAR_MAX; }
        static constexpr ValueT lowest() { return min();     }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT - 1;
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<unsigned char> : public Uninstantiable {
    public:
        using ValueT = unsigned char;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return 0;         }
        static constexpr ValueT max   () { return UCHAR_MAX; }
        static constexpr ValueT lowest() { return min();     }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT;
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = false;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<char> : public Uninstantiable {
    public:
        using ValueT = char;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return CHAR_MIN; }
        static constexpr ValueT max   () { return CHAR_MAX; }
        static constexpr ValueT lowest() { return min();    }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = (CHAR_MIN != 0) ? (CHAR_BIT - 1) : CHAR_BIT;
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = (CHAR_MIN != 0);
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<signed short> : public Uninstantiable {
    public:
        using ValueT = signed short;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return SHRT_MIN; }
        static constexpr ValueT max   () { return SHRT_MAX; }
        static constexpr ValueT lowest() { return min();    }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT) - 1;
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<unsigned short> : public Uninstantiable {
    public:
        using ValueT = unsigned short;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return 0;         }
        static constexpr ValueT max   () { return USHRT_MAX; }
        static constexpr ValueT lowest() { return min();     }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT);
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = false;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<signed int> : public Uninstantiable {
    public:
        using ValueT = signed int;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return INT_MIN; }
        static constexpr ValueT max   () { return INT_MAX; }
        static constexpr ValueT lowest() { return min();   }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT) - 1;
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<unsigned int> : public Uninstantiable {
    public:
        using ValueT = unsigned int;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return 0;        }
        static constexpr ValueT max   () { return UINT_MAX; }
        static constexpr ValueT lowest() { return min();    }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT);
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = false;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<signed long> : public Uninstantiable {
    public:
        using ValueT = signed long;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return LONG_MIN; }
        static constexpr ValueT max   () { return LONG_MAX; }
        static constexpr ValueT lowest() { return min();    }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT) - 1;
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<unsigned long> : public Uninstantiable {
    public:
        using ValueT = unsigned long;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return 0;         }
        static constexpr ValueT max   () { return ULONG_MAX; }
        static constexpr ValueT lowest() { return min();     }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT);
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = false;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<signed long long> : public Uninstantiable {
    public:
        using ValueT = signed long long;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return LLONG_MIN; }
        static constexpr ValueT max   () { return LLONG_MAX; }
        static constexpr ValueT lowest() { return min();     }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT) - 1;
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<unsigned long long> : public Uninstantiable {
    public:
        using ValueT = unsigned long long;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return 0;          }
        static constexpr ValueT max   () { return ULLONG_MAX; }
        static constexpr ValueT lowest() { return min();      }

        static constexpr int  radix       = 2;
        static constexpr int  digits      = CHAR_BIT * sizeof(ValueT);
        static constexpr int  digits10    = BitsToBase10   <digits>::Size;
        static constexpr int  digits10Max = BitsToBase10Max<digits>::Size;

        static constexpr bool isSigned    = false;
        static constexpr bool isInteger   = true;
        static constexpr bool isExact     = true;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = true;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return 0; };
        static constexpr ValueT roundError() { return 0; };

        static constexpr int  minExponent     = 0;
        static constexpr int  maxExponent     = 0;
        static constexpr int  minExponent10   = 0;
        static constexpr int  maxExponent10   = 0;

        static constexpr bool hasInfinity     = false;
        static constexpr bool hasQuietNaN     = false;
        static constexpr bool hasSignalingNaN = false;
        static constexpr bool hasDenorm       = false;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = false;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::TowardZero;

        static constexpr ValueT denormMin   () { return 0; };
        static constexpr ValueT infinity    () { return 0; };
        static constexpr ValueT quietNaN    () { return 0; };
        static constexpr ValueT signalingNaN() { return 0; };
};


template <>
class NumericLimits<float> : public Uninstantiable {
    public:
        using ValueT = float;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return  __FLT_MIN__; }
        static constexpr ValueT max   () { return  __FLT_MAX__; }
        static constexpr ValueT lowest() { return -__FLT_MAX__; }

        static constexpr int  radix       = __FLT_RADIX__;
        static constexpr int  digits      = __FLT_MANT_DIG__;
        static constexpr int  digits10    = __FLT_DIG__;
        static constexpr int  digits10Max = 2 + (__FLT_MANT_DIG__) * 643L / 2136; // log10(2) ~= (643 / 2136)

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = false;
        static constexpr bool isExact     = false;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = false;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return __FLT_EPSILON__; };
        static constexpr ValueT roundError() { return 0.5f; };

        static constexpr int  minExponent     = __FLT_MIN_EXP__;
        static constexpr int  maxExponent     = __FLT_MAX_EXP__;
        static constexpr int  minExponent10   = __FLT_MIN_10_EXP__;
        static constexpr int  maxExponent10   = __FLT_MAX_10_EXP__;

        static constexpr bool hasInfinity     = __FLT_HAS_INFINITY__;
        static constexpr bool hasQuietNaN     = __FLT_HAS_QUIET_NAN__;
        static constexpr bool hasSignalingNaN = hasQuietNaN;
        static constexpr bool hasDenorm       = __FLT_HAS_DENORM__;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = hasInfinity && hasQuietNaN && hasDenorm;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::ToNearest;

        static constexpr ValueT denormMin   () { return __FLT_DENORM_MIN__;      };
        static constexpr ValueT infinity    () { return __builtin_huge_valf(  ); };
        static constexpr ValueT quietNaN    () { return __builtin_nanf     (""); };
        static constexpr ValueT signalingNaN() { return __builtin_nansf    (""); };
};


template <>
class NumericLimits<double> : public Uninstantiable {
    public:
        using ValueT = double;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return  __DBL_MIN__; }
        static constexpr ValueT max   () { return  __DBL_MAX__; }
        static constexpr ValueT lowest() { return -__DBL_MAX__; }

        static constexpr int  radix       = __FLT_RADIX__;
        static constexpr int  digits      = __DBL_MANT_DIG__;
        static constexpr int  digits10    = __DBL_DIG__;
        static constexpr int  digits10Max = 2 + (__DBL_MANT_DIG__) * 643L / 2136; // log10(2) ~= (643 / 2136)

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = false;
        static constexpr bool isExact     = false;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = false;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return __DBL_EPSILON__; };
        static constexpr ValueT roundError() { return 0.5; };

        static constexpr int  minExponent     = __DBL_MIN_EXP__;
        static constexpr int  maxExponent     = __DBL_MAX_EXP__;
        static constexpr int  minExponent10   = __DBL_MIN_10_EXP__;
        static constexpr int  maxExponent10   = __DBL_MAX_10_EXP__;

        static constexpr bool hasInfinity     = __DBL_HAS_INFINITY__;
        static constexpr bool hasQuietNaN     = __DBL_HAS_QUIET_NAN__;
        static constexpr bool hasSignalingNaN = hasQuietNaN;
        static constexpr bool hasDenorm       = __DBL_HAS_DENORM__;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = hasInfinity && hasQuietNaN && hasDenorm;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::ToNearest;

        static constexpr ValueT denormMin   () { return __DBL_DENORM_MIN__;     };
        static constexpr ValueT infinity    () { return __builtin_huge_val(  ); };
        static constexpr ValueT quietNaN    () { return __builtin_nan     (""); };
        static constexpr ValueT signalingNaN() { return __builtin_nans    (""); };
};


template <>
class NumericLimits<long double> : public Uninstantiable {
    public:
        using ValueT = long double;

        static constexpr bool isSpecialized = true;

        // General
        static constexpr ValueT min   () { return  __LDBL_MIN__; }
        static constexpr ValueT max   () { return  __LDBL_MAX__; }
        static constexpr ValueT lowest() { return -__LDBL_MAX__; }

        static constexpr int  radix       = __FLT_RADIX__;
        static constexpr int  digits      = __LDBL_MANT_DIG__;
        static constexpr int  digits10    = __LDBL_DIG__;
        static constexpr int  digits10Max = 2 + (__LDBL_MANT_DIG__) * 643L / 2136; // log10(2) ~= (643 / 2136)

        static constexpr bool isSigned    = true;
        static constexpr bool isInteger   = false;
        static constexpr bool isExact     = false;
        static constexpr bool traps       = false;
        static constexpr bool isModulo    = false;
        static constexpr bool isBounded   = true;

        // Floating-point specific
        static constexpr ValueT epsilon   () { return __LDBL_EPSILON__; };
        static constexpr ValueT roundError() { return 0.5L; };

        static constexpr int  minExponent     = __LDBL_MIN_EXP__;
        static constexpr int  maxExponent     = __LDBL_MAX_EXP__;
        static constexpr int  minExponent10   = __LDBL_MIN_10_EXP__;
        static constexpr int  maxExponent10   = __LDBL_MAX_10_EXP__;

        static constexpr bool hasInfinity     = __LDBL_HAS_INFINITY__;
        static constexpr bool hasQuietNaN     = __LDBL_HAS_QUIET_NAN__;
        static constexpr bool hasSignalingNaN = hasQuietNaN;
        static constexpr bool hasDenorm       = __LDBL_HAS_DENORM__;
        static constexpr bool hasDenormLoss   = false;
        static constexpr bool isIEC559        = hasInfinity && hasQuietNaN && hasDenorm;
        static constexpr bool tinynessBefore  = false;

        static constexpr FloatRoundStyle roundStyle = FloatRoundStyle::ToNearest;

        static constexpr ValueT denormMin   () { return __LDBL_DENORM_MIN__;     };
        static constexpr ValueT infinity    () { return __builtin_huge_vall(  ); };
        static constexpr ValueT quietNaN    () { return __builtin_nanl     (""); };
        static constexpr ValueT signalingNaN() { return __builtin_nansl    (""); };
};


} // namespace eCxx


#endif
