// computing numerical limits

/***************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you 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.
 *
 * Copyright 1999-2007 Rogue Wave Software, Inc.
 * 
 **************************************************************************/

#include "config.h"

#include <stdio.h>    // for printf()

#ifndef _RWSTD_NO_LIMITS_H
   // avoid including broken <limits.h> (due to the use of #include_next)
   // with EDG eccp on Linux
#  include <limits.h>   // for MB_LEN_MAX
#endif   // _RWSTD_NO_LIMITS_H


// establish a dependency on the test for long long
// and #define the LONG_LONG macro used in "type.h"
#ifndef _RWSTD_NO_LONG_LONG
#  define LONG_LONG long long
#else   // if defined (_RWSTD_NO_LONG_LONG)
#  if defined (_MSC_VER)
#    define LONG_LONG   __int64
#  endif   // _MSC_VER
#endif   // _RWSTD_NO_LONG_LONG

#include "types.h"   // for type_name()


#ifndef _RWSTD_NO_HONOR_STD
#  ifdef _RWSTD_NO_STD_TERMINATE
#    include "terminate.h"
#  endif   // _RWSTD_NO_STD_TERMINATE
#endif   // _RWSTD_NO_HONOR_STD


// set to 1 if this is not a two's complement architecture
int no_twos_complement = 0;

// volatile to foil aggressive optimizers
volatile int zero            = 0;
volatile int one             = zero + 1;
volatile int two             = one + 1;
volatile int zero_complement = ~zero;



template<class T>
void print_limit (T n, const char *pfx, const char *sfx,
                  bool is_max, const char *type)
{
    // prevents meaningless comparison warnings when T is unsigned
    // e.g., for (n < 0)
    /* not const */ T zero = T (0);

    char nstr [64] = { 0 };

    char* pnstr = nstr + sizeof nstr - 2;

    const char digit [] = {
        '0','1','2','3','4','5','6','7','8','9', 'a', 'b', 'c', 'd', 'e', 'f'
    };

    if (is_max || n >= zero) {

        T tnstr = n;

        *--pnstr = digit [tnstr % 16];
        while (tnstr /= 16)
            *--pnstr = digit [tnstr % 16];
    }

    *--pnstr = 'x';

    if (   'c' == type [0]
        && 'h' == type [1]
        && 'a' == type [2]
        && 'r' == type [3]
        && '\0' == type [4]) {

        *--pnstr = '\\';
        *--pnstr = '\'';
        nstr [sizeof nstr - 2] = '\'';
    }
    else {
        *--pnstr = '0';
    }

    char macro_name [64];
    char macro_value [64];

    if (is_max) {
        sprintf (macro_name, "_RWSTD_%s_MAX", pfx);
        sprintf (macro_value, "%s%s", pnstr, sfx);

    }
    else {
        sprintf (macro_name, "_RWSTD_%s_MIN ", pfx);

        if (n < zero) {
            if (no_twos_complement) {
                sprintf (macro_value, "-_RWSTD_%s_MAX", pfx);
            }
            else {
                sprintf (macro_value, "(-_RWSTD_%s_MAX - 1%s)", pfx, sfx);
            }
        }
        else {
            sprintf (macro_value, "%s%s", pnstr, sfx);
        }
    }

    printf ("#define %-18s %s\n", macro_name, macro_value);
}


template <class T>
T compute_min (T min)
{
    // prevents overzealous gcc optimizer from invoking
    // undefined behavior on signed integer over/underflow
    for (T tmp; ; --min) {
        tmp = T (min - one);
        if (tmp >= min)
            break;
    }

    return min;
}


template <class T>
const char* type_suffix (T) { return ""; }

const char* type_suffix (short) { return ""; }
const char* type_suffix (unsigned short) { return "U"; }
const char* type_suffix (int) { return ""; }
const char* type_suffix (unsigned int) { return "U"; }
const char* type_suffix (long) { return "L"; }
const char* type_suffix (unsigned long) { return "UL"; }

#ifndef _RWSTD_NO_LONG_LONG
const char* type_suffix (long long) { return "LL"; }
const char* type_suffix (unsigned long long) { return "ULL"; }
#else
#  if defined (_MSC_VER)
const char* type_suffix (__int64) { return "L"; }
const char* type_suffix (unsigned __int64) { return "UL"; }
#  endif   // _MSC_VER
#endif


template <class T>
T compute_limits (T *pval, const char *pfx, const char *sfx, const char *type)
{
    T min = T (-one);
    T max = T (one);

    int is_signed = T (min - one) < T (zero);

    if (!is_signed)
        max = min;

    // compute the maximum representable value
    for (; T (max * two) > max; max *= two);
    for (*pval = max / (two + two); *pval; ) {
        if (T (max + *pval) < max) {
            if (*pval > T (two))
                *pval /= two;
            else if (max < T (max + one))
                *pval = one;
            else
                break;
        }
        else
            max += *pval;
    }

    // print the maximum representable value
    print_limit (max, pfx, sfx, true, type);

    // compute the minimum representable value
    for (min = -max; T (min * two) < min; min *= two);

    // working around a gcc optimizer "feature" (PR #26211)
    // signed integer overflow is undefined behavior
    // for (; T (min - 1) < min; min -= 1);
    min = compute_min (min);

    print_limit (min, pfx, sfx, false, type);

    return max;
}


unsigned compute_char_bits ()
{
    unsigned bits = 0;

    for (unsigned char c = '\01'; c; c <<= 1, ++bits);

    printf ("#define _RWSTD_CHAR_BIT    %2u\n", bits);

    return bits;
}


// used to compute the size of a pointer to a member function
struct EmptyStruct { };


// to silence printf() format comaptibility warnings
#define SIZEOF(T)   unsigned (sizeof (T))


int main ()
{
    // determine whether this is a two's complement architecture
    // and set the no_twos_complement global variable to 1 if not
    if (two + zero_complement != one)
        no_twos_complement = 1;

    // compute sizes of fundamental types

#ifndef _RWSTD_NO_BOOL
    printf ("#define _RWSTD_BOOL_SIZE   %2u /* sizeof (bool) */\n",
            SIZEOF (bool));
#endif   // _RWSTD_NO_BOOL

    printf ("#define _RWSTD_CHAR_SIZE   %2u /* sizeof (char) */\n",
            SIZEOF (char));
    printf ("#define _RWSTD_SHRT_SIZE   %2u /* sizeof (short) */\n",
            SIZEOF (short));
    printf ("#define _RWSTD_INT_SIZE    %2u /* sizeof (int) */\n",
            SIZEOF (int));
    printf ("#define _RWSTD_LONG_SIZE   %2u /* sizeof (long) */\n",
            SIZEOF (long));

    printf ("#define _RWSTD_FLT_SIZE    %2u /* sizeof (float) */\n",
            SIZEOF (float));
    printf ("#define _RWSTD_DBL_SIZE    %2u /* sizeof (double) */\n",
            SIZEOF (double));

#ifndef _RWSTD_NO_LONG_DOUBLE
    printf ("#define _RWSTD_LDBL_SIZE   %2u /* sizeof (long double) */\n",
            SIZEOF (long double));
#endif   // _RWSTD_NO_LONG_DOUBLE

    // compute the sizes of data and function pointers
    printf ("#define _RWSTD_PTR_SIZE    %2u /* sizeof (void*) */\n",
            SIZEOF (void*));

    typedef void (*fun_ptr_t)();
    printf ("#define _RWSTD_FUNPTR_SIZE %2u /* sizeof (void(*)()) */\n",
            SIZEOF (fun_ptr_t));

    typedef void (EmptyStruct::*memfun_ptr_t)();
    printf ("#define _RWSTD_MEMPTR_SIZE %2u "
            "/* sizeof (void (struct::*)()) */\n",
            SIZEOF (memfun_ptr_t));

    // compute integral limits

    const unsigned char_bits = compute_char_bits ();

#ifndef _RWSTD_NO_BOOL

    printf ("#define _RWSTD_BOOL_MIN   !!0\n");
    printf ("#define _RWSTD_BOOL_MAX    !0\n");

#endif   // _RWSTD_NO_BOOL

    if (0 == no_twos_complement) {
        // comment out the next #define (this is two's complement
        // architecture)
        printf ("%s", "// ");
    }
    printf ("#define _RWSTD_NO_TWOS_COMPLEMENT\n");

#define MKLIMITS(T, pfx, sfx, type)            \
    do {                                       \
        T buf = 0;                             \
        compute_limits (&buf, pfx, sfx, type); \
    } while (0)

    MKLIMITS (char, "CHAR", "", "char");
    MKLIMITS (signed char, "SCHAR", "", "signed char");
    MKLIMITS (unsigned char, "UCHAR", "U", "unsigned char");

    MKLIMITS (short, "SHRT", "", "short");
    MKLIMITS (unsigned short, "USHRT", "U", "unsigned short");

    MKLIMITS (int, "INT", "", "int");
    MKLIMITS (unsigned int, "UINT", "U", "unsigned int");

    MKLIMITS (long, "LONG", "L", "long");
    MKLIMITS (unsigned long, "ULONG", "UL", "unsigned long");

#ifndef _RWSTD_NO_LONG_LONG

#    define LLong long long

    printf ("#define _RWSTD_LLONG_SIZE  %2u\n", SIZEOF (LLong));

    const char llong_name[] = "long long";

    MKLIMITS (LLong, "LLONG", "LL", "long long");
    MKLIMITS (unsigned LLong, "ULLONG", "ULL", "unsigned long long");

#else   // if defined (_RWSTD_NO_LONG_LONG)

#  if defined (_MSC_VER)

#    define LLong __int64

    printf ("#define _RWSTD_LLONG_SIZE  %2u\n", SIZEOF (LLong));

    const char llong_name[] = "__int64";

    MKLIMITS (LLong, "LLONG", "L", "__int64");
    MKLIMITS (unsigned LLong, "ULLONG", "UL", "unsigned __int64");

#    else

#      define LLong long

    const char llong_name[] = "long";

#    endif   // _MSC_VER

#endif   // _RWSTD_NO_LONG_LONG

#ifndef _RWSTD_NO_WCHAR_T

    printf ("#define _RWSTD_WCHAR_SIZE  %2u /* sizeof (wchar_t) */\n",
            SIZEOF (wchar_t));

    const char *suffix = "U";
    if ((wchar_t)~0 < (wchar_t)0)
        suffix = "";
    
    MKLIMITS (wchar_t, "WCHAR", suffix, "wchar_t");

#endif   // _RWSTD_NO_WCHAR_T

#if defined (MB_LEN_MAX)

    printf ("#define _RWSTD_MB_LEN_MAX    %d   /* libc value */\n",
            MB_LEN_MAX);

#else   // if !defined (MB_LEN_MAX)

#  if defined (_AIX)
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* known AIX libc value */\n", 4);
#  elif defined (__hpux)
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* known HP-UX libc value */\n", 4);
#  elif defined (__sgi) || defined (sgi)
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* known IRIX libc value */\n", 5);
#  elif defined (__GLIBC__) && __GLIBC_MINOR__ <= 1
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* known glibc 2.1 value */\n", 6);
#  elif defined (__GLIBC__) && __GLIBC_MINOR__ >= 2
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* known glibc 2.2 value */\n", 16);
#  elif defined (__sun__) || defined (__sun) || defined (__sun)
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* known SunOS libc value */\n", 5);
#  elif defined (_WIN32)
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* known WIN32 libc value */\n", 5);
#  else
    printf ("#define _RWSTD_MB_LEN_MAX %d   /* guess */\n", 8);
#  endif

#endif   // MB_LEN_MAX

    //////////////////////////////////////////////////////////////////
    // #define macros for exact and lest-width integer types

    // width_bits will have the corresponding bit set for each exact-width
    // type already processed (i.e., bit 0 for an 8-bit integer type, bit
    // 1 for a 16-bit integer, etc)
    int width_bits = 0;

#define PRINT_SPECIFIC(width, least, type)                              \
    do {                                                                \
        /* avoid warnings about expression being constant */            \
        const char* least_str = least;                                  \
        if (least_str && *least_str)                                    \
            printf ("#define _RWSTD_INT_LEAST%d_T  %s  _RWSTD_%s_T\n"   \
                    "#define _RWSTD_UINT_LEAST%d_T %s  _RWSTD_U%s_T\n", \
                    width, width < 10 ? " " : "", type,                 \
                    width, width < 10 ? " " : "", type);                \
        else                                                            \
            printf ("#define _RWSTD_INT%d_T %s          signed %s\n"    \
                    "#define _RWSTD_UINT%d_T %s         unsigned %s\n", \
                    width, width < 10 ? " " : "", type,                 \
                    width, width < 10 ? " " : "", type);                \
    } while (0)

    if (8 == char_bits) {
        width_bits |= 1;
        PRINT_SPECIFIC (8, "", "char");
    }
    else if (16 == char_bits) {
        width_bits |= 2;
        PRINT_SPECIFIC (16, "", "char");
    }
    else if (32 == char_bits) {
        width_bits |= 4;
        PRINT_SPECIFIC (32, "", "char");
    }
    else if (64 == char_bits) {
        width_bits |= 8;
        PRINT_SPECIFIC (64, "", "char");
    }

    if (16 == char_bits * sizeof (short) && !(width_bits & 2)) {
        width_bits |= 2;
        PRINT_SPECIFIC (16, "", "short");
    }
    else if (32 == char_bits * sizeof (short) && !(width_bits & 4)) {
        width_bits |= 4;
        PRINT_SPECIFIC (32, "", "short");
    }
    else if (64 == char_bits * sizeof (short) && !(width_bits & 8)) {
        width_bits |= 8;
        PRINT_SPECIFIC (64, "", "short");
    }
    else if (128 == char_bits * sizeof (short) && !(width_bits & 16)) {
        width_bits |= 16;
        PRINT_SPECIFIC (128, "", "short");
    }

    if (32 == char_bits * sizeof (int) && !(width_bits & 4)) {
        width_bits |= 4;
        PRINT_SPECIFIC (32, "", "int");
    }
    else if (64 == char_bits * sizeof (int) && !(width_bits & 8)) {
        width_bits |= 8;
        PRINT_SPECIFIC (64, "", "int");
    }
    else if (128 == char_bits * sizeof (int) && !(width_bits & 16)) {
        width_bits |= 16;
        PRINT_SPECIFIC (128, "", "int");
    }

    if (32 == char_bits * sizeof (long) && !(width_bits & 4)) {
        width_bits |= 4;
        PRINT_SPECIFIC (32, "", "long");
    }
    else if (64 == char_bits * sizeof (long) && !(width_bits & 8)) {
        width_bits |= 8;
        PRINT_SPECIFIC (64, "", "long");
    }
    else if (128 == char_bits * sizeof (long) && !(width_bits & 16)) {
        width_bits |= 16;
        PRINT_SPECIFIC (128, "", "long");
    }

    if (32 == char_bits * sizeof (LLong) && !(width_bits & 4)) {
        width_bits |= 4;
        PRINT_SPECIFIC (32, "", llong_name);
    }
    else if (64 == char_bits * sizeof (LLong) && !(width_bits & 8)) {
        width_bits |= 8;
        PRINT_SPECIFIC (64, "", llong_name);

    }
    else if (128 == char_bits * sizeof (LLong) && !(width_bits & 16)) {
        width_bits |= 16;
        PRINT_SPECIFIC (128, "", llong_name);
    }

    //////////////////////////////////////////////////////////////////
    // print the names of the width-specific least integer types
    // i.e., INT_LEAST8_T, INT_LEAST16_T, INT_LEAST32_T, ...

    if (width_bits & (1 << 0))
        PRINT_SPECIFIC (8, "_LEAST", "INT8");
    else if (width_bits & (1 << 1))
        PRINT_SPECIFIC (8, "_LEAST", "INT16");
    else if (width_bits & (1 << 2))
        PRINT_SPECIFIC (8, "_LEAST", "INT32");
    else if (width_bits & (1 << 3))
        PRINT_SPECIFIC (8, "_LEAST", "INT64");
    else if (width_bits & (1 << 4))
        PRINT_SPECIFIC (8, "_LEAST", "INT128");

    if (width_bits & (1 << 1))
        PRINT_SPECIFIC (16, "_LEAST", "INT16");
    else if (width_bits & (1 << 2))
        PRINT_SPECIFIC (16, "_LEAST", "INT32");
    else if (width_bits & (1 << 3))
        PRINT_SPECIFIC (16, "_LEAST", "INT64");
    else if (width_bits & (1 << 4))
        PRINT_SPECIFIC (16, "_LEAST", "INT128");

    if (width_bits & (1 << 2))
        PRINT_SPECIFIC (32, "_LEAST", "INT32");
    else if (width_bits & (1 << 3))
        PRINT_SPECIFIC (32, "_LEAST", "INT64");
    else if (width_bits & (1 << 4))
        PRINT_SPECIFIC (32, "_LEAST", "INT128");

    if (width_bits & (1 << 3))
        PRINT_SPECIFIC (64, "_LEAST", "INT64");
    else if (width_bits & (1 << 4))
        PRINT_SPECIFIC (64, "_LEAST", "INT128");

    if (width_bits & (1 << 4))
        PRINT_SPECIFIC (128, "_LEAST", "INT128");

    return 0;
}
