//
// boost limits_test.cpp   test your <limits> file for important
// Copyright Jens Maurer 2000
// Permission to use, copy, modify, sell, and distribute this software
// is hereby granted without fee provided that the above copyright notice
// appears in all copies and that both that copyright notice and this
// permission notice appear in supporting documentation,
// Jens Maurer makes no representations about the suitability of this
// software for any purpose. It is provided "as is" without express or
// implied warranty.
// 
//

#include <limits>

#include "cppunit/cppunit_proxy.h"

#if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES)
using namespace std;
#endif

//
// TestCase class
//
class LimitTest : public CPPUNIT_NS::TestCase
{
  CPPUNIT_TEST_SUITE(LimitTest);
  CPPUNIT_TEST(test);
#  if defined (__BORLANDC__)
  CPPUNIT_IGNORE;
#  endif
  CPPUNIT_TEST(qnan_test);
  CPPUNIT_TEST(limits_cov);
  CPPUNIT_TEST_SUITE_END();

protected:
  void test();
  void qnan_test();
  void limits_cov();
};

CPPUNIT_TEST_SUITE_REGISTRATION(LimitTest);

#  define CHECK_COND(X) if (!(X)) return false;

bool valid_sign_info(bool, bool)
{ return true; }

template <class _Tp>
bool valid_sign_info(bool limit_is_signed, const _Tp &) {
  return limit_is_signed && _Tp(-1) < 0 ||
         !limit_is_signed && _Tp(-1) > 0;
}

template <class _Tp>
bool test_integral_limits(const _Tp &, bool unknown_sign = true, bool is_signed = true) {
  typedef numeric_limits<_Tp> lim;

  CHECK_COND(lim::is_specialized);
  CHECK_COND(lim::is_integer);
  /*CHECK_COND(lim::is_modulo);*/
  CHECK_COND(lim::min() < lim::max());
  CHECK_COND((unknown_sign && ((lim::is_signed && (lim::min() != 0)) || (!lim::is_signed && (lim::min() == 0)))) ||
             (!unknown_sign && ((lim::is_signed && is_signed) || (!lim::is_signed && !is_signed))));

  if (unknown_sign) {
    CHECK_COND(valid_sign_info(lim::is_signed, _Tp()));
  }
  return true;
}

template <class _Tp>
bool test_signed_integral_limits(const _Tp &__val) {
  return test_integral_limits(__val, false, true);
}
template <class _Tp>
bool test_unsigned_integral_limits(const _Tp &__val) {
  return test_integral_limits(__val, false, false);
}

template <class _Tp>
bool test_float_limits(const _Tp &) {
  typedef numeric_limits<_Tp> lim;
  CHECK_COND(lim::is_specialized);
  CHECK_COND(!lim::is_modulo);
  CHECK_COND(!lim::is_integer);
  CHECK_COND(lim::is_signed);

  CHECK_COND(lim::max() > 1000);
  CHECK_COND(lim::min() > 0);
  CHECK_COND(lim::min() < 0.001);
  CHECK_COND(lim::epsilon() > 0);

  if (lim::is_iec559) {
    CHECK_COND(lim::has_infinity);
    CHECK_COND(lim::has_quiet_NaN);
    CHECK_COND(lim::has_signaling_NaN);
    CHECK_COND(lim::signaling_NaN);
  }

  if (lim::has_infinity) {
    const _Tp infinity = lim::infinity();
    /* Make sure those values are not 0 or similar nonsense.
    * Infinity must compare as if larger than the maximum representable value.
    */
    CHECK_COND(infinity > lim::max());
    CHECK_COND(-infinity < -lim::max());
  }
  return true;
}

template <class _Tp>
bool test_qnan(const _Tp &) {
  typedef numeric_limits<_Tp> lim;
  if (lim::has_quiet_NaN) {
    const _Tp qnan = lim::quiet_NaN();

    /* NaNs shall always compare "false" when compared for equality
    * If one of these fail, your compiler may be optimizing incorrectly,
    * or the STLport is incorrectly configured.
    */
    CHECK_COND(! (qnan == 42));
    CHECK_COND(! (qnan == qnan));
    CHECK_COND(qnan != 42);
    CHECK_COND(qnan != qnan);

    /* The following tests may cause arithmetic traps.
    * CHECK_COND(! (qnan < 42));
    * CHECK_COND(! (qnan > 42));
    * CHECK_COND(! (qnan <= 42));
    * CHECK_COND(! (qnan >= 42));
    */
  }
  return true;
}
void LimitTest::test() {
  CPPUNIT_ASSERT(test_integral_limits(bool()));
  /*
  It is implementation-defined whether a char object can hold 
  negative values. Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned 
  char are three distinct types. 
  */
  //CPPUNIT_ASSERT(test_integral_limits(char()));
  typedef signed char signed_char;
  CPPUNIT_ASSERT(test_signed_integral_limits(signed_char()));
  typedef unsigned char unsigned_char;
  CPPUNIT_ASSERT(test_unsigned_integral_limits(unsigned_char()));
#  if defined (_STLP_HAS_WCHAR_T) && !defined (_STLP_WCHAR_T_IS_USHORT)
  CPPUNIT_ASSERT(test_integral_limits(wchar_t()));
#  endif
  CPPUNIT_ASSERT(test_signed_integral_limits(short()));
  typedef unsigned short unsigned_short;
  CPPUNIT_ASSERT(test_unsigned_integral_limits(unsigned_short()));
  CPPUNIT_ASSERT(test_signed_integral_limits(int()));
  typedef unsigned int unsigned_int;
  CPPUNIT_ASSERT(test_unsigned_integral_limits(unsigned_int()));
  CPPUNIT_ASSERT(test_signed_integral_limits(long()));
  typedef unsigned long unsigned_long;
  CPPUNIT_ASSERT(test_unsigned_integral_limits(unsigned_long()));
#  if defined (_STLP_LONG_LONG)
  typedef _STLP_LONG_LONG long_long;
  CPPUNIT_ASSERT(test_signed_integral_limits(long_long()));
  typedef unsigned _STLP_LONG_LONG unsigned_long_long;
  CPPUNIT_ASSERT(test_unsigned_integral_limits(unsigned_long_long()));
#endif

  CPPUNIT_ASSERT(test_float_limits(float()));
  CPPUNIT_ASSERT(test_float_limits(double()));
#  if !defined ( _STLP_NO_LONG_DOUBLE )
  typedef long double long_double;
  CPPUNIT_ASSERT(test_float_limits(long_double()));
#  endif
}

void LimitTest::qnan_test() {
  CPPUNIT_ASSERT(test_qnan(float()));
  CPPUNIT_ASSERT(test_qnan(double()));
#  if !defined ( _STLP_NO_LONG_DOUBLE )
  typedef long double long_double;
  CPPUNIT_ASSERT(test_qnan(long_double()));
#  endif
}
void LimitTest::limits_cov() 
	{
	if(numeric_limits<float>::has_denorm == false)
		{
		CPPUNIT_ASSERT(numeric_limits<float>::denorm_min( )); // denorm_min for float
		CPPUNIT_ASSERT(numeric_limits<double>::denorm_min( )); // denorm_min for double
		CPPUNIT_ASSERT(numeric_limits<long double>::denorm_min( )); // denorm_min for long double
		}
	numeric_limits<float>::round_error( ); // round_error for float
	numeric_limits<double>::round_error( ); // round_error for double
	numeric_limits<long double>::round_error( ); // round_error for long double
	if(numeric_limits<long double>::is_iec559)
		{
		CPPUNIT_ASSERT(numeric_limits<long double>::infinity( )); // infinity for long double
		CPPUNIT_ASSERT(numeric_limits<long double>::quiet_NaN( ));// quiet_NaN for long double
		}
	}
