/*
*******************************************************************************
*                                                                             *
* COPYRIGHT:                                                                  *
*   (C) Copyright International Business Machines Corporation,  1998          *
*   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
*   US Government Users Restricted Rights - Use, duplication, or disclosure   *
*   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
*                                                                             *
*******************************************************************************
*
* File ieeetest.cpp
*
* Modification History:
*
*   Date        Name        Description
*   08/21/98    stephen	    Creation.
*******************************************************************************
*/

#include <iostream.h>		// cout, cerr
#include <float.h>		// DBL_MAX

#include "ieeetest.h"
#include "utypes.h"

//==============================

int
main(int argc, 
     char **argv)
{
  int flags = IEEETest::kNone;

  // parse command line switches
  for(int i = 1; i < argc; ++i) {
    if(argv[i][0] == '-') {
      switch(argv[i][1]) {
      case 'v':
	flags += IEEETest::kVerboseMode;
	break;
				
      case '?':
      case 'h':
      case 'H':
	usage(argv[0]);
	return 0;
	break;

      default:
	break;
      }
    }
  }

  IEEETest test(flags);	

  return test.run();
}

//==============================

void
usage(const char *execName)
{
  cout << endl;
  cout << "usage: " <<  execName << " [flags]" << endl << endl;
  cout << "Flags:" << endl;
  cout << " -v  Verbose mode" << endl;
}

//==============================

IEEETest::IEEETest(int flags)
  : mFlags(flags), 
    mTestLevel(0), 
    mNeedLogIndent(TRUE), 
    mNeedErrIndent(TRUE)
{}

//==============================

IEEETest::~IEEETest()
{}

//==============================

int
IEEETest::run()
{
  int errCount = 0;

  logln();
  log("Starting IEEETest").logln();
  increaseTestLevel();

  // add more tests here
  errCount += runTest("NaN behavior", &IEEETest::testNaN);
  errCount += runTest("+Infinity behavior", &IEEETest::testPositiveInfinity);
  errCount += runTest("-Infinity behavior", &IEEETest::testNegativeInfinity);
  errCount += runTest("Zero behavior", &IEEETest::testZero);
	
  decreaseTestLevel();
  if(errCount == 0)
    log("IEEETest Passed");
  else {
    log("IEEETest failed with ").log(errCount)
      .log(errCount == 1 ? " error." : " errors.");
  }
  logln();

  if(errCount == 0 && ! (mFlags & kVerboseMode))
    cout << endl << "All tests passed without error." << endl;

  return errCount;
}

//==============================
int
IEEETest::runTest(const char *testName, 
		  int (IEEETest::*testFunc)(void))
{
  logln().log("Running test ").log(testName).logln();
  increaseTestLevel();
  int errCount = (this->*testFunc)();
  decreaseTestLevel();
  log("Test ").log(testName);
  if(errCount == 0)
    log(" passed.");
  else {
    log(" failed with ").log(errCount)
      .log(errCount == 1 ? " error." : " errors.");
  }

  logln().logln();

  return errCount;
}


//==============================

// NaN is weird- comparisons with NaN _always_ return false, with the
// exception of !=, which _always_ returns true
int
IEEETest::testNaN()
{
  int errCount = 0;

  errCount += runTest("isNaN", &IEEETest::testIsNaN);
  errCount += runTest("NaN >", &IEEETest::NaNGT);
  errCount += runTest("NaN <", &IEEETest::NaNLT);
  errCount += runTest("NaN >=", &IEEETest::NaNGTE);
  errCount += runTest("NaN <=", &IEEETest::NaNLTE);
  errCount += runTest("NaN ==", &IEEETest::NaNE);
  errCount += runTest("NaN !=", &IEEETest::NaNNE);
	
  return errCount;
}

//==============================

int			
IEEETest::testPositiveInfinity()
{
  int errCount = 0;
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	ten		= 10.0;

  if(icu_isInfinite(pinf) != TRUE) {
    err("FAIL: isInfinite(+Infinity) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isPositiveInfinity(pinf) != TRUE) {
    err("FAIL: isPositiveInfinity(+Infinity) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isNegativeInfinity(pinf) != FALSE) {
    err("FAIL: isNegativeInfinity(+Infinity) returned TRUE, should be FALSE.").errln();
    errCount++;
  }

  if(pinf > DBL_MAX != TRUE) {
    err("FAIL: +Infinity > DBL_MAX returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(pinf > DBL_MIN != TRUE) {
    err("FAIL: +Infinity > DBL_MIN returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(pinf > ninf != TRUE) {
    err("FAIL: +Infinity > -Infinity returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(pinf > ten != TRUE) {
    err("FAIL: +Infinity > 10.0 returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  return errCount;
}

//==============================

int			
IEEETest::testNegativeInfinity()
{
  int errCount = 0;
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	ten			= 10.0;

  if(icu_isInfinite(ninf) != TRUE) {
    err("FAIL: isInfinite(-Infinity) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isNegativeInfinity(ninf) != TRUE) {
    err("FAIL: isNegativeInfinity(-Infinity) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isPositiveInfinity(ninf) != FALSE) {
    err("FAIL: isPositiveInfinity(-Infinity) returned TRUE, should be FALSE.").errln();
    errCount++;
  }

  if(ninf < DBL_MAX != TRUE) {
    err("FAIL: -Infinity < DBL_MAX returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(ninf < DBL_MIN != TRUE) {
    err("FAIL: -Infinity < DBL_MIN returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(ninf < pinf != TRUE) {
    err("FAIL: -Infinity < +Infinity returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(ninf < ten != TRUE) {
    err("FAIL: -Infinity < 10.0 returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  return errCount;
}

//==============================

// notes about zero:
// -0.0 == 0.0 == TRUE
// -0.0 <  0.0 == FALSE
// generating -0.0 must be done at runtime.  compiler apparently ignores sign?
int			
IEEETest::testZero()
{
  int errCount = 0;
  double	ten			= 10.0;
  double	pzero		= 0.0;
  double	nzero		= 0.0;

  nzero *= -1;

  if(pzero == nzero != TRUE) {
    err("FAIL: 0.0 == -0.0 returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(pzero > nzero != FALSE) {
    err("FAIL: 0.0 > -0.0 returned TRUE, should be FALSE.").errln();
    errCount++;
  }

  if(pzero >= nzero != TRUE) {
    err("FAIL: 0.0 >= -0.0 returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(pzero < nzero != FALSE) {
    err("FAIL: 0.0 < -0.0 returned TRUE, should be FALSE.").errln();
    errCount++;
  }

  if(pzero <= nzero != TRUE) {
    err("FAIL: 0.0 <= -0.0 returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isInfinite(1/pzero) != TRUE) {
    err("FAIL: isInfinite(1/0.0) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isInfinite(1/nzero) != TRUE) {
    err("FAIL: isInfinite(1/-0.0) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isPositiveInfinity(1/pzero) != TRUE) {
    err("FAIL: isPositiveInfinity(1/0.0) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  if(icu_isNegativeInfinity(1/nzero) != TRUE) {
    err("FAIL: isNegativeInfinity(1/-0.0) returned FALSE, should be TRUE.").errln();
    errCount++;
  }

  return errCount;
}

//==============================

int
IEEETest::testIsNaN()
{
  int numErrors	= 0;
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	nan			= icu_getNaN();
  double	ten			= 10.0;

  if(icu_isNaN(nan) == FALSE) {
    err("FAIL: isNaN() returned FALSE for NaN.").errln();
    numErrors++;
  }

  if(icu_isNaN(pinf) == TRUE) {
    err("FAIL: isNaN() returned TRUE for +Infinity.").errln();
    numErrors++;
  }

  if(icu_isNaN(ninf) == TRUE) {
    err("FAIL: isNaN() returned TRUE for -Infinity.").errln();
    numErrors++;
  }

  if(icu_isNaN(ten) == TRUE) {
    err("FAIL: isNaN() returned TRUE for 10.0.").errln();
    numErrors++;
  }

  return numErrors;
}

//==============================

int
IEEETest::NaNGT()
{
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	nan			= icu_getNaN();
  double	ten			= 10.0;
  int numErrors	= 0;

  if(nan > nan != FALSE) {
    err("FAIL: NaN > NaN returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan > pinf != FALSE) {
    err("FAIL: NaN > +Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan > ninf != FALSE) {
    err("FAIL: NaN > -Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan > ten != FALSE) {
    err("FAIL: NaN > 10.0 returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  return numErrors;
}

//==============================

int				
IEEETest::NaNLT()
{
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	nan			= icu_getNaN();
  double	ten			= 10.0;
  int numErrors	= 0;

  if(nan < nan != FALSE) {
    err("FAIL: NaN < NaN returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan < pinf != FALSE) {
    err("FAIL: NaN < +Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan < ninf != FALSE) {
    err("FAIL: NaN < -Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan < ten != FALSE) {
    err("FAIL: NaN < 10.0 returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  return numErrors;
}

//==============================

int				
IEEETest::NaNGTE()
{
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	nan			= icu_getNaN();
  double	ten			= 10.0;
  int numErrors	= 0;

  if(nan >= nan != FALSE) {
    err("FAIL: NaN >= NaN returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan >= pinf != FALSE) {
    err("FAIL: NaN >= +Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan >= ninf != FALSE) {
    err("FAIL: NaN >= -Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan >= ten != FALSE) {
    err("FAIL: NaN >= 10.0 returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  return numErrors;
}

//==============================

int				
IEEETest::NaNLTE()
{
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	nan			= icu_getNaN();
  double	ten			= 10.0;
  int numErrors	= 0;

  if(nan <= nan != FALSE) {
    err("FAIL: NaN <= NaN returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan <= pinf != FALSE) {
    err("FAIL: NaN <= +Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan <= ninf != FALSE) {
    err("FAIL: NaN <= -Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan <= ten != FALSE) {
    err("FAIL: NaN <= 10.0 returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  return numErrors;
}

//==============================

int				
IEEETest::NaNE()
{
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	nan			= icu_getNaN();
  double	ten			= 10.0;
  int numErrors	= 0;

  if(nan == nan != FALSE) {
    err("FAIL: NaN == NaN returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan == pinf != FALSE) {
    err("FAIL: NaN == +Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan == ninf != FALSE) {
    err("FAIL: NaN == -Infinity returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  if(nan == ten != FALSE) {
    err("FAIL: NaN == 10.0 returned TRUE, should be FALSE").errln();
    numErrors++;
  }

  return numErrors;
}

//==============================

int				
IEEETest::NaNNE()
{
  double	pinf		= icu_getInfinity();
  double	ninf		= -icu_getInfinity();
  double	nan			= icu_getNaN();
  double	ten			= 10.0;
  int numErrors	= 0;

  if(nan != nan != TRUE) {
    err("FAIL: NaN != NaN returned FALSE, should be TRUE").errln();
    numErrors++;
  }

  if(nan != pinf != TRUE) {
    err("FAIL: NaN != +Infinity returned FALSE, should be TRUE").errln();
    numErrors++;
  }

  if(nan != ninf != TRUE) {
    err("FAIL: NaN != -Infinity returned FALSE, should be TRUE").errln();
    numErrors++;
  }

  if(nan != ten != TRUE) {
    err("FAIL: NaN != 10.0 returned FALSE, should be TRUE").errln();
    numErrors++;
  }

  return numErrors;
}

//==============================

IEEETest& 
IEEETest::log(char c)
{
  if(mFlags & kVerboseMode) {
    if(mNeedLogIndent) {
      for(int j = 0; j < 2 * getTestLevel(); ++j)
	cout << ' ';
      mNeedLogIndent = FALSE;
    }
    cout << c;
  }
  return *this;
}

//==============================

IEEETest& 
IEEETest::log(const char *s)
{
  if(mFlags & kVerboseMode) {
    if(mNeedLogIndent) {
      for(int j = 0; j < 2 * getTestLevel(); ++j)
	cout << ' ';
      mNeedLogIndent = FALSE;
    }
    cout << s;
  }
  return *this;
}

//==============================

IEEETest& 
IEEETest::log(int i)
{
  if(mFlags & kVerboseMode) {
    if(mNeedLogIndent) {
      for(int j = 0; j < 2 * getTestLevel(); ++j)
	cout << ' ';
      mNeedLogIndent = FALSE;
    }
    cout << i;
  }
  return *this;
}

//==============================

IEEETest& 
IEEETest::log(long l)
{
  if(mFlags & kVerboseMode) {
    if(mNeedLogIndent) {
      for(int j = 0; j < 2 * getTestLevel(); ++j)
	cout << ' ';
      mNeedLogIndent = FALSE;
    }
    cout << l;
  }
  return *this;
}

//==============================

IEEETest& 
IEEETest::log(double d)
{
  if(mFlags & kVerboseMode) {
    if(mNeedLogIndent) {
      for(int j = 0; j < 2 * getTestLevel(); ++j)
	cout << ' ';
      mNeedLogIndent = FALSE;
    }
    cout << d;
  }
  return *this;
}

//==============================

IEEETest& 
IEEETest::logln()
{
  if(mFlags & kVerboseMode)
    cout << endl;
  mNeedLogIndent = TRUE;
  return *this;
}

//==============================

IEEETest& 
IEEETest::err(char c)
{
  if(mNeedErrIndent) {
    for(int j = 0; j < 2 * getTestLevel(); ++j)
      cerr << ' ';
    mNeedErrIndent = FALSE;
  }
  cerr << c;
  return *this;
}

//==============================

IEEETest& 
IEEETest::err(const char *s)
{
  if(mNeedErrIndent) {
    for(int j = 0; j < 2 * getTestLevel(); ++j)
      cerr << ' ';
    mNeedErrIndent = FALSE;
  }
  cerr << s;
  return *this;
}

//==============================

IEEETest& 
IEEETest::err(int i)
{
  if(mNeedErrIndent) {
    for(int j = 0; j < 2 * getTestLevel(); ++j)
      cerr << ' ';
    mNeedErrIndent = FALSE;
  }
  cerr << i;
  return *this;
}

//==============================

IEEETest& 
IEEETest::err(long l)
{
  if(mNeedErrIndent) {
    for(int j = 0; j < 2 * getTestLevel(); ++j)
      cerr << ' ';
    mNeedErrIndent = FALSE;
  }
  cerr << l;
  return *this;
}

//==============================

IEEETest& 
IEEETest::err(double d)
{
  if(mNeedErrIndent) {
    for(int j = 0; j < 2 * getTestLevel(); ++j)
      cerr << ' ';
    mNeedErrIndent = FALSE;
  }
  cerr << d;
  return *this;
}

//==============================

IEEETest& 
IEEETest::errln()
{
  cerr << endl;
  mNeedErrIndent = TRUE;
  return *this;
}

//eof
