#ifndef NCBI_LIMITS__HPP
#define NCBI_LIMITS__HPP

/*  $Id: ncbi_limits.hpp 601610 2020-02-11 15:01:11Z ucko $
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Author:  Denis Vakatov
 *
 * File Description:
 *
 *   Temporary replacement for "numeric_limits<>".
 *   Extremely incomplete implementation,
 *
 *    only min() and max() methods for:
 *
 *      numeric_limits<char>
 *      numeric_limits<signed   char>
 *      numeric_limits<unsigned char>
 *
 *      numeric_limits<signed   short>
 *      numeric_limits<unsigned short>
 *      numeric_limits<signed   int>
 *      numeric_limits<unsigned int>
 *      numeric_limits<signed   long>
 *      numeric_limits<unsigned long>
 *
 *      numeric_limits<float>
 *      numeric_limits<double>
 *
 *      (platform-specific)
 *      numeric_limits<signed   long long>
 *      numeric_limits<unsigned long long>
 *      numeric_limits<signed   __int64>
 *      numeric_limits<unsigned __int64>
 *
 */

#include <corelib/ncbi_limits.h>
#include <corelib/ncbistl.hpp>


/** @addtogroup Portability
 *
 * @{
 */


// Always get rid of the old non-conformant min/max macros
#ifdef min
#  undef min
#endif
#ifdef max
#  undef max
#endif

#if defined(HAVE_LIMITS)  &&  ( !defined(NCBI_COMPILER_WORKSHOP) || NCBI_COMPILER_VERSION >= 550)
// Ideally, we would use std::numeric_limits<> whenever available.
// However, certain compiler versions leave out support for extensions such
// as long long, so we still have to use our implementation with them.
#  include <limits>
#else

BEGIN_NCBI_SCOPE

///
///  Pre-declaration of the "numeric_limits<>" template
///  Forcibly overrides (using preprocessor) the original "numeric_limits<>"!
///

#  define numeric_limits ncbi_numeric_limits
template <class T> class numeric_limits;


///
///  Auxiliary macro to implement (a limited edition of) the
///  "numeric_limits<>" template
///

#  define NCBI_NUMERIC_LIMITS(type, alias) \
  template <> \
  class numeric_limits<type> \
  { \
  public: \
      static inline type min() THROWS_NONE { return kMin_##alias; } \
      static inline type max() THROWS_NONE { return kMax_##alias; } \
  }

#  define NCBI_NUMERIC_LIMITS_UNSIGNED(type, alias) \
  template <> \
  class numeric_limits<type> \
  { \
  public: \
      static inline type min() THROWS_NONE { return 0; } \
      static inline type max() THROWS_NONE { return kMax_##alias; } \
  }


//
//  Implement (a limited edition of) the "numeric_limits<>" template
//  for various built-in types
//

NCBI_NUMERIC_LIMITS          (         char,  Char);
NCBI_NUMERIC_LIMITS          (signed   char, SChar);
NCBI_NUMERIC_LIMITS_UNSIGNED (unsigned char, UChar);

#if defined(HAVE_WCHAR_H)  &&  defined(WCHAR_MIN)
NCBI_NUMERIC_LIMITS          (wchar_t, WChar);
#endif

NCBI_NUMERIC_LIMITS          (signed   short,  Short);
NCBI_NUMERIC_LIMITS_UNSIGNED (unsigned short, UShort);

NCBI_NUMERIC_LIMITS          (signed   int,  Int);
NCBI_NUMERIC_LIMITS_UNSIGNED (unsigned int, UInt);

NCBI_NUMERIC_LIMITS          (signed   long,  Long);
NCBI_NUMERIC_LIMITS_UNSIGNED (unsigned long, ULong);

NCBI_NUMERIC_LIMITS          (float,  Float);
NCBI_NUMERIC_LIMITS          (double, Double);

#  if (SIZEOF_LONG_LONG > 0)
NCBI_NUMERIC_LIMITS          (signed   long long,  LongLong);
NCBI_NUMERIC_LIMITS_UNSIGNED (unsigned long long, ULongLong);
#  endif

#  if defined(NCBI_INT8_IS_INT64)
NCBI_NUMERIC_LIMITS          (signed   __int64,  Int64);
NCBI_NUMERIC_LIMITS_UNSIGNED (unsigned __int64, UInt64);
#  endif


END_NCBI_SCOPE

#endif // !HAVE_LIMITS  ||  NCBI_COMPILER_WORKSHOP


BEGIN_NCBI_SCOPE

// The formal is_signed parameter helps avoid warnings under MSVC.
template <typename T, bool is_signed = numeric_limits<T>::is_signed>
struct SAutoMinMaxLimits
{
    typedef numeric_limits<T> TStdLim;
    static T max(void) { return TStdLim::max(); }

    /// For non-integral types, yield a huge negative value rather than a
    /// tiny positive one.
    static T min(void)
    {
#if defined(NCBI_HAVE_CXX11)  &&  !defined(NCBI_COMPILER_ICC)
        // Be conservative with ICC, which could wind up using a libstdc++
        // lacking lowest() despite running in C++ '11 mode itself.
        return TStdLim::lowest();
#else
        return TStdLim::is_integer ? TStdLim::min() : T(-TStdLim::max());
#endif
    }
};

template <typename T>
struct SAutoMinMaxLimits<T, false>
{
    typedef numeric_limits<T> TStdLim;
    static T max(void) { return TStdLim::max(); }
    static T min(void) { return 0; }
};

struct SAutoMax
{
    template <typename T>
    operator T(void) const { return SAutoMinMaxLimits<T>::max(); }
};

/// Generic stand-in for type-specific kMax_* constants from ncbi_limits.h,
/// useful in any context with exactly one preferred type.
static const SAutoMax kMax_Auto = {};

struct SAutoMin
{
    template <typename T>
    operator T(void) const { return SAutoMinMaxLimits<T>::min(); }
};

/// Generic stand-in for type-specific kMin_* constants from ncbi_limits.h,
/// useful in any context with exactly one preferred type.  NB: For
/// non-integral types, yields a huge negative value rather than a tiny
/// positive one.
static const SAutoMin kMin_Auto = {};


#  define NCBI_FORBID_AUTOMINMAX_OPERATION(op, T1, T2) \
template <typename T> \
inline bool operator op(const T1&, const T2&) { \
    typename T::T1##T2##OperationNotSupported tmp; \
    return false; \
}

#  define NCBI_FORBID_AUTOMINMAX_OPERATIONS(op) \
NCBI_FORBID_AUTOMINMAX_OPERATION(op, SAutoMax, T) \
NCBI_FORBID_AUTOMINMAX_OPERATION(op, SAutoMin, T) \
NCBI_FORBID_AUTOMINMAX_OPERATION(op, T, SAutoMax) \
NCBI_FORBID_AUTOMINMAX_OPERATION(op, T, SAutoMin)

NCBI_FORBID_AUTOMINMAX_OPERATIONS(==)
NCBI_FORBID_AUTOMINMAX_OPERATIONS(!=)
NCBI_FORBID_AUTOMINMAX_OPERATIONS(<)
NCBI_FORBID_AUTOMINMAX_OPERATIONS(<=)
NCBI_FORBID_AUTOMINMAX_OPERATIONS(>)
NCBI_FORBID_AUTOMINMAX_OPERATIONS(>=)


/// Generic template to get STD limits by a variable.
/// Typical use:
/// <pre>
///  int a = 10; 
///  get_limits(a).max();
/// </pre>
/// @note 
///   Causes a compile-time failure if used
///   instead of the specialized implementations.
template<typename T> 
inline numeric_limits<T> get_limits(const T&)
{
    typename T::TypeIsNotSupported tmp; 
    return numeric_limits<T>();
}

/// Macro to declare specialized get_limits
#  define NCBI_GET_NUMERIC_LIMITS(type) \
    EMPTY_TEMPLATE \
    inline numeric_limits<type> get_limits(const type&) \
        { return numeric_limits<type>(); }

NCBI_GET_NUMERIC_LIMITS(         char)
NCBI_GET_NUMERIC_LIMITS(signed   char)
NCBI_GET_NUMERIC_LIMITS(unsigned char)

NCBI_GET_NUMERIC_LIMITS(signed   short)
NCBI_GET_NUMERIC_LIMITS(unsigned short)

NCBI_GET_NUMERIC_LIMITS(signed   int)
NCBI_GET_NUMERIC_LIMITS(unsigned int)

NCBI_GET_NUMERIC_LIMITS(signed   long)
NCBI_GET_NUMERIC_LIMITS(unsigned long)

NCBI_GET_NUMERIC_LIMITS(float)
NCBI_GET_NUMERIC_LIMITS(double)

#  if (SIZEOF_LONG_LONG > 0)
NCBI_GET_NUMERIC_LIMITS(signed   long long)
NCBI_GET_NUMERIC_LIMITS(unsigned long long)
#  endif

#  if defined(NCBI_INT8_IS_INT64)
NCBI_GET_NUMERIC_LIMITS(signed   __int64)
NCBI_GET_NUMERIC_LIMITS(unsigned __int64)
#  endif


END_NCBI_SCOPE

/* @} */

#endif /* NCBI_LIMITS__HPP */
