//**************************************************************************************************
//                                          CnvtType.hpp                                           *
//                                         --------------                                          *
// Started     : 2004-09-21                                                                        *
// Last Update : 2018-09-23                                                                        *
// Copyright   : (C) 2004-2016 MSWaters                                                            *
//**************************************************************************************************

//**************************************************************************************************
//                                                                                                 *
//      This program is free software; you can redistribute it and/or modify it under the          *
//      terms of the GNU General Public License as published by the Free Software Foundation;      *
//      either version 3 of the License, or (at your option) any later version.                    *
//                                                                                                 *
//**************************************************************************************************

#include "CnvtType.hpp"

//**************************************************************************************************
// Allocate storage for static data members.

int  CnvtType::m_iFltRes = CNVT_DEF_FLT_RES;

//**************************************************************************************************
// Constructor.

CnvtType::CnvtType( void )
{
}

//**************************************************************************************************
// Destructor.

CnvtType::~CnvtType( )
{
}

//**************************************************************************************************
// Extract the numeric part of a string and convert it to scientific notation ie. a mantissa and
// exponent.
//
// (Eg. 10.0E-2uVolt becomes 10.0E-8 and 0.0E-2uF becomes 0.0E-8)
//
// Argument List :
//   rosNum - The string containing a numeric value
//
// Return Values :
//   Success - The numeric part of the string
//   Failure - An empty string

TYPE_STR & CnvtType::rosGetNum( const TYPE_STR & rosNum )
{
  static  TYPE_STR      osNum;  // The number to be returned
          TYPE_STR      osInt;  // The integer part of the number
          TYPE_STR      osFrac; // The part of the string after a decimal point
          TYPE_STR      osExp;  // The exponent part of the string
          TYPE_CHR      oc1;
          size_t        sz1, sz2;
          long          li1;
          int           i1;
#if ! defined( __WXGTK__ ) && ! defined( __WXMSW__ ) && ! defined( __WXOSX__ )
          stringstream  oss1;
#endif

  // Clear the local static variable
	osNum = wxT("");

  // Basic argument test
  if( rosNum.length( ) == 0 )    return( osNum );

  // Extract the first non-space character (any leading non-space characters are a show stopper)
  sz1 = rosNum.find_first_of( wxT("+-.0123456789") );
  if( sz1 == std::string::npos ) return( osNum );
  for( sz2=0; sz2<sz1; sz2++ )
  {
    oc1 = rosNum.at( sz2 );
    if( oc1 != wxT(' ') )        return( osNum );
  }
  oc1 = rosNum.at( sz1 );

  // Extract the first part of the number
  osInt = wxT("");
  if( oc1 != wxT('.') ) osInt += oc1;
  for( ++sz1; sz1<rosNum.length( ); sz1++ )
  {
    oc1 = rosNum.at( sz1 );
    if( ! IS_DIGIT( oc1 ) ) break;
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
    osInt << oc1;
#else
    osInt += oc1;
#endif
  }

  // Extract the part of the string after a decimal point
  osFrac = wxT("");
  if( oc1 == wxT('.') )
  {
    for( ++sz1; sz1<rosNum.length( ); sz1++ )
    {
      oc1 = rosNum.at( sz1 );
      if( ! IS_DIGIT( oc1 ) ) break;
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
      osFrac << oc1;
#else
      osFrac += oc1;
#endif
    }
  }

  // Extract the part of the string after an exponent
  osExp = wxT("");
  if( oc1==wxT('e') || oc1==wxT('E') )
  {
    for( ++sz1; sz1<rosNum.length( ); sz1++ )
    {
      oc1 = rosNum.at( sz1 );

      if( osExp.length( ) == 0 )
        if( oc1==wxT('+') || oc1==wxT('-') )
          { osExp += oc1; continue; }

      if( ! IS_DIGIT( oc1 ) ) break;

#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
      osExp << oc1;
#else
      osExp += oc1;
#endif
    }
  }

  // Check for a units specifier
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  if( ! osExp.ToLong( &li1 ) ) li1 = 0;
#else
  oss1.str( osExp );
  oss1 >> li1;
  if( oss1.fail( ) ) li1 = 0;
#endif
  if( CnvtType::bUnitPfxToExp( oc1, &i1 ) ) li1 += (long) i1;
  if( li1 != 0 )
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
    osExp = wxString::Format( wxT("%i"), (int) li1 );
#else
    osExp = to_string( li1 );
#endif

  // Construct the number string
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  osNum << osInt;
  if( osFrac.length( ) > 0 ) osNum << wxT('.') << osFrac;
  if( osExp .length( ) > 0 ) osNum << wxT('E') << osExp;
#else
  osNum += osInt;
  if( osFrac.length( ) > 0 ) osNum +=     '.'  +  osFrac;
  if( osExp .length( ) > 0 ) osNum +=     'E'  +  osExp;
#endif

  return( osNum );
}

//**************************************************************************************************
// Test if two floats are equal taking into account boundary conditions.
//
// Argument List :
//   f1 - The 1st float to test
//   f2 - The 2nd float to test
//
// Return Values :
//   true  - The floats are    equal
//   false - The floats aren't equal

bool  CnvtType::bIsEqual( float f1, float f2 )
{
  float  fMax;

  // Determine fMax ie. the largest of : f1, f2 and 1.0
  fMax = std::max( std::fabs( f1 ), std::fabs( f2 ) );
  fMax = std::max( float( 1.0 ), fMax );

  // Determine if f1 and f2 differ significantly
#if wxCHECK_VERSION( 3,0,0 )
  return( std::fabs( f1 - f2 ) <= std::numeric_limits<float>::epsilon( ) * fMax );
#else
  return( std::fabs( f1 - f2 ) <= FLT_EPSILON * fMax );
#endif
}

//**************************************************************************************************

bool  CnvtType::bIsEqual( double df1, double df2 )
{
  double  dfMax;

  // Determine dfMax ie. the largest of : df1, df2 and 1.0
  dfMax = std::max( std::fabs( df1 ), std::fabs( df2 ) );
  dfMax = std::max( double( 1.0 ), dfMax );

  // Determine if df1 and df2 differ significantly
#if wxCHECK_VERSION( 3,0,0 )
  return( std::fabs( df1 - df2 ) <= std::numeric_limits<double>::epsilon( ) * dfMax );
#else
  return( std::fabs( df1 - df2 ) <= DBL_EPSILON * dfMax );
#endif
}

//**************************************************************************************************
// Set the resolution of conversions from floats to strings.
//
// (Ie. the number of digits after the decimal point.)
//
// Argument List :
//   iFltRes - The desired resolution
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CnvtType::bSetFltRes( int iFltRes )
{
  if( iFltRes<CNVT_MIN_FLT_RES || iFltRes>CNVT_MAX_FLT_RES ) return( false );

  m_iFltRes = iFltRes;

  return( true );
}

//**************************************************************************************************
// Is the argument string a pure integer number?
//
// Ie. all characters in the string are part of a valid integer number.
//
// Argument List :
//   rosStr - The string to test
//
// Return Values :
//   true  - The string is    a integer
//   false - The string isn't a integer

bool  CnvtType::bIsInteger( const TYPE_STR & rosStr )
{
  TYPE_STR  os1;
  double    df1, df2;

  os1 = rosGetNum( rosStr );
  if( os1.empty( ) )                            return( false );

  if( rosStr.find( '.' ) != std::string::npos ) return( false );

  if( ! bStrToDbl( rosStr, &df1 ) )             return( false );
  if( modf( df1, &df2 ) != 0.0 )                return( false );

  return( true );
}

//**************************************************************************************************
// Is the argument string a pure floating point number?
//
// Ie. all characters in the string are part of a valid floating point number and the number has a
//     non-zero fractional part.
//
// Argument List :
//   rosStr - The string to test
//
// Return Values :
//   true  - The string is    a float
//   false - The string isn't a float

bool  CnvtType::bIsFloat( const TYPE_STR & rosStr )
{
  TYPE_STR  os1;
  double    df1, df2;

  os1 = rosGetNum( rosStr );
  if( os1.empty( ) )                            return( false );

  if( rosStr.find( '.' ) != std::string::npos ) return( true );

  if( ! bStrToDbl( rosStr, &df1 ) )             return( false );
  if( modf( df1, &df2 ) == 0.0 )                return( false );

  return( true );
}

//**************************************************************************************************
// Parse a float value into it's mantissa and exponent.
//
// Argument List :
//   fNum  - The float value to be parsed
//   pfMan - A pointer to float   to hold the mantissa (1.000 to 9.999)
//   piExp - A pointer to integer to hold the exponent
//
// Return Values :
//   Success - true
//   Failure - false

bool  CnvtType::bParseFlt( float fNum, float * pfMan, int * piExp )
{
  if( fNum<-FLT_MAX || fNum>FLT_MAX ) return( false );

  if( fNum != 0.0 )
  {
    *piExp = (int) floorf( log10f( fabsf( fNum ) ) );
    *pfMan = fNum / EXP10F( float( *piExp ) );

    // The following looks for boundary conditions at 1.0 and 10.0 where things can go wrong
    if(        fabsf( *pfMan ) < float(  1.0 ) )
    {
      if( ! bIsEqual( *pfMan,    float(  1.0 ) ) ) { (*pfMan) *= 10.0; (*piExp)--; }
    }
    else if(   fabsf( *pfMan ) < float( 10.0 ) )
    {
      if(   bIsEqual( *pfMan,    float( 10.0 ) ) ) { (*pfMan) /= 10.0; (*piExp)++; }
    }
    else                                           { (*pfMan) /= 10.0; (*piExp)++; }
  }
  else
  {
    *pfMan = 0.0;
    *piExp = 0;
  }

  return( true );
}

//**************************************************************************************************

bool  CnvtType::bParseFlt( double dfNum, double * pdfMan, int * piExp )
{
  if( dfNum<-DBL_MAX || dfNum>DBL_MAX ) return( false );

  if( dfNum != 0.0 )
  {
    *piExp  = (int) floor( log10( fabs( dfNum ) ) );
    *pdfMan = dfNum / EXP10( double( *piExp ) );

    // The following looks for boundary conditions at 1.0 and 10.0 where things can go wrong
    if(         fabs( *pdfMan ) <  1.0 )
    {
      if( ! bIsEqual( *pdfMan,     1.0 ) ) { (*pdfMan) *= 10.0; (*piExp)--; }
    }
    else if(    fabs( *pdfMan ) < 10.0 )
    {
      if(   bIsEqual( *pdfMan,    10.0 ) ) { (*pdfMan) /= 10.0; (*piExp)++; }
    }
    else                                   { (*pdfMan) /= 10.0; (*piExp)++; }
  }
  else
  {
    *pdfMan = 0.0;
    *piExp = 0;
  }

  return( true );
}

//**************************************************************************************************
// Convert a string into an integer.
//
// Some examples of strings that can be converted to an integer :
//  * 12
//  * 12k  ("k" could alternatively be T, Tera, G, Giga, M, Meg, Mega, K)
//  * 12kV ("V" could alternatively be Ohm, F, H, A, Sec and is ignored)
//
// Argument List :
//   rosNum - The string to be converted
//   piNum  - A pointer to an integer to hold the conversion
//
// Return Values :
//   Success - true
//   Failure - false

bool  CnvtType::bStrToInt( const TYPE_STR & rosNum, int * piNum )
{
  long  li1;

  if( ! bStrToInt( rosNum, &li1 ) ) return( false );

  *piNum = (int) li1;

  return( true );
}

//**************************************************************************************************

bool  CnvtType::bStrToInt( const TYPE_STR & rosNum, long * pliNum )
{
  double  df1;

  if( ! bStrToFlt( rosNum, &df1 ) ) return( false );

  *pliNum = lround( df1 );

  return( true );
}

//**************************************************************************************************
// Convert a string into a float point number.
//
// Some examples of strings that can be converted to a float :
//  * 12.3
//  * 12.3m  ("m" could also be T, Tera, G, Giga, M, Meg, Mega, k, u, n, etc.)
//  * 12.3mF ("F" could also be Ohm, H, V, A, Sec, is actually ignored)
//  * 12.3E-3
//
// Argument List :
//   rosNum - The string to be converted
//   piNum  - A pointer to a float to hold the result
//
// Return Values :
//   true  - (part or all of the string was converted)
//   false - (no part of the string could be converted)

bool  CnvtType::bStrToFlt( const TYPE_STR & rosNum, float * pfNum )
{
  double  df1;

  if( ! bStrToFlt( rosNum, &df1 ) ) return( false );

  *pfNum = (float) df1;

  return( true );
}

//**************************************************************************************************

bool  CnvtType::bStrToFlt( const TYPE_STR & rosNum, double * pdfNum )
{
  double    df1;

#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )

  wxString  osNum;

  osNum = rosGetNum( rosNum );
  if( ! osNum.ToDouble( &df1 ) ) return( false );

#else

  stringstream  oss1;

  oss1 << rosGetNum( rosNum );
  oss1 >> df1;
  if( oss1.fail( ) )             return( false );

#endif

  *pdfNum = df1;

  return( true  );
}

//**************************************************************************************************
// Convert an integer number to a string.
//
// Argument List :
//   fNum   - The integer to be converted
//   rosNum - A reference to a string to hold the conversion
//
// Return Values :
//   Success - true
//   Failure - false

bool  CnvtType::bIntToStr( int iNum, TYPE_STR & rosNum )
{
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  wxString  os1;
#else
  char      sNum[ 16 ];
#endif
  int       i1;

#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  i1 = os1.Printf( wxT("% i"), iNum );
#else
  i1 = snprintf( sNum, 16, "% i", iNum );
#endif

  if( i1 <= 0 ) return( false );

#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  rosNum = os1;
#else
  rosNum = sNum;
#endif

  return( true );
}

//**************************************************************************************************

bool  CnvtType::bIntToStr( long liNum, TYPE_STR & rosNum )
{
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  wxString  os1;
#else
  char      sNum[ 32 ];
#endif
  int       i1;

#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  i1 = os1.Printf( wxT("% li"), liNum );
#else
  i1 = snprintf( sNum, 32, "% li", liNum );
#endif

  if( i1 <= 0 ) return( false );

#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  rosNum = os1;
#else
  rosNum = sNum;
#endif

  return( true );
}

//**************************************************************************************************
// Convert a floating point number to a string.
//
// The format used by this function is as follows :
//    1.234E-05
//   -1.234E+05
//
// Argument List :
//   fNum   - The float to be converted
//   rosNum - A reference to a string to hold the result
//
// Return Values :
//   Success - true
//   Failure - false

bool  CnvtType::bFltToStr( float fNum, TYPE_STR & rosNum )
{
  double  df1;

  df1 = (double) fNum;

  return( bFltToStr( df1, rosNum ) );
}

//**************************************************************************************************

bool  CnvtType::bFltToStr( double dfNum, TYPE_STR & rosNum )
{
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )

  wxString  osFmt;

  osFmt = wxString::Format( wxT("%% %i.%iE"), m_iFltRes+2, m_iFltRes );
  if( rosNum.Printf( osFmt, dfNum ) < 0 ) return( false );

#else

  char  sFmt[ 16 ], sNum[ 32 ];
  int   i1;

  i1 = snprintf( sFmt, 16, "%% %i.%iE", m_iFltRes+2, m_iFltRes );
  if( i1 <= 0 ) return( false );
  i1 = snprintf( sNum, 32, sFmt, dfNum );
  if( i1 <= 0 ) return( false );
  rosNum = sNum;

#endif

  return( true );
}

//**************************************************************************************************
// Convert a floating point value to a string using engineering format.
//
// Eg. convert 100000 to 100k.
//
// Argument List :
//   fNum   - The float to be converted
//   rosNum - A reference to a string to hold the result
//
// Return Values :
//   true  - The string containing the engineering format
//   false - An empty string

bool  CnvtType::bFltToStrEng( float fNum, TYPE_STR & rosNum )
{
  double  df1;

  df1 = (double) fNum;

  return( bFltToStrEng( df1, rosNum ) );
}

//**************************************************************************************************

bool  CnvtType::bFltToStrEng( double dfNum, TYPE_STR & rosNum )
{
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  wxString  osFmt;
#else
  char      sFmt[ 16 ], sNum[ 32 ];
#endif
  TYPE_STR  os1;
  TYPE_CHR  oc1;
  float     fMan;  // Mantissa
  int       iExp;  // Exponent
  int       i1;

  // Setup the format string based on the desired resolution
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  osFmt = wxString::Format( wxT("%% %i.%if"), m_iFltRes+2, m_iFltRes );
#else
  i1 = snprintf( sFmt, 16, "%% %i.%if", m_iFltRes+2, m_iFltRes );
  if( i1 <= 0 )                            return( false );
#endif

  // Parse the number to be converted
  if( ! bParseFlt( dfNum, &fMan, &iExp ) ) return( false );

  // Round the mantissa based on the desired resolution
  fMan *= EXP10F( (float) m_iFltRes );
  fMan = roundf( fMan );
  fMan /= EXP10F( (float) m_iFltRes );

  // Scale the mantissa so that the exponent is a multiple of 3
  while( iExp % 3 )
  {
    iExp--;
    fMan *= 10.0;
  }

  // Determine the appropriate scaling factor to be appended as units
  if( bUnitExpToPfx( iExp, &oc1 ) ) os1 = oc1;
  else                              os1 = wxT("E%02i");
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  osFmt << os1;
#else
  strcat( sFmt, os1.c_str( ) );
#endif

  // Create the string representation of the number
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
  i1 = rosNum.Printf( osFmt, fMan, iExp );
  if( i1 <= 0 )                            return( false );
#else
  i1 = snprintf( sNum, 32, sFmt, fMan, iExp );
  if( i1 <= 0 )                            return( false );
  rosNum = sNum;
#endif

  return( true );
}

//**************************************************************************************************
// Take a units prefix and return the associated exponent.
//
// Eg. "m" or "milli" returns -3.
//
// Argument List :
//   ocPfx - the units prefix
//   piExp - the associated exponent
//
// Return Values :
//   Success - true
//   Failure - false

bool  CnvtType::bUnitPfxToExp( TYPE_CHR ocPfx, int * piExp )
{
  switch( ocPfx )
  {
    case wxT('Y') : *piExp =  24; break;  // Yotta
    case wxT('Z') : *piExp =  21; break;  // Zetta
    case wxT('E') : *piExp =  18; break;  // Exa
    case wxT('P') : *piExp =  15; break;  // Peta
    case wxT('T') : *piExp =  12; break;  // Tera
    case wxT('G') : *piExp =   9; break;  // Giga
    case wxT('M') : *piExp =   6; break;  // Mega
    case wxT('K') :                       // Kilo
    case wxT('k') : *piExp =   3; break;  // kilo
    case wxT('%') : *piExp =  -2; break;  // percent
    case wxT('m') : *piExp =  -3; break;  // milli
    case wxT('u') : *piExp =  -6; break;  // micro
    case wxT('n') : *piExp =  -9; break;  // nano
    case wxT('p') : *piExp = -12; break;  // pico
    case wxT('f') : *piExp = -15; break;  // femto
    case wxT('a') : *piExp = -18; break;  // atto
    case wxT('z') : *piExp = -21; break;  // zepto
    case wxT('y') : *piExp = -24; break;  // yocto

    default       : return( false );      // A units prefix was not recognized
  }

  return( true );
}

//**************************************************************************************************
// Take a units exponent and return the associated prefix.
//
// Eg. the exponent 6 returns "Mega".
//
// Argument List :
//   iExp   - the exponent
//   pocPfx - the associated units prefix
//
// Return Values :
//   Success - true
//   Failure - false

bool  CnvtType::bUnitExpToPfx( int iExp, TYPE_CHR * pocPfx )
{
  switch( iExp )
  {
    case  24 : *pocPfx = wxT('Y'); break;  // Yotta
    case  21 : *pocPfx = wxT('Z'); break;  // Zetta
    case  18 : *pocPfx = wxT('E'); break;  // Exa
    case  15 : *pocPfx = wxT('P'); break;  // Peta
    case  12 : *pocPfx = wxT('T'); break;  // Tera
    case   9 : *pocPfx = wxT('G'); break;  // Giga
    case   6 : *pocPfx = wxT('M'); break;  // Mega
    case   3 : *pocPfx = wxT('k'); break;  // kilo
    case   0 : *pocPfx = wxT(' '); break;  // Empty units prefix
    case  -2 : *pocPfx = wxT('%'); break;  // Percent
    case  -3 : *pocPfx = wxT('m'); break;  // milli
    case  -6 : *pocPfx = wxT('u'); break;  // micro
    case  -9 : *pocPfx = wxT('n'); break;  // nano
    case -12 : *pocPfx = wxT('p'); break;  // pico
    case -15 : *pocPfx = wxT('f'); break;  // femto
    case -18 : *pocPfx = wxT('a'); break;  // atto
    case -21 : *pocPfx = wxT('z'); break;  // zepto
    case -24 : *pocPfx = wxT('y'); break;  // yocto

    default  : return( false );            // No units recognized
  }

  return( true );
}

//**************************************************************************************************
//                                          Test Utility                                           *
//**************************************************************************************************

#ifdef TEST_CNVTTYPE

using  namespace  std;

// System Includes

#include <iostream>
#include <iomanip>

// Function prototypes

void  Test_GetNum( const TYPE_STR & rosNum );
void  Test_IsType( const TYPE_STR & rosNum );
void  PrintLimits( void );
void  Usage( char * psAppName );

//**************************************************************************************************

int  main( int argc, char * argv[ ] )
{
  TYPE_STR  osNum, os1;
  double    dfNum, dfMan;
  float     fNum,  fMan;
  long      liNum;
  int       iNum,  iExp;

  // Validate the argument count passed to the application
  if( argc > 2 )           { Usage( argv[ 0 ] ); exit( EXIT_FAILURE ); }

  // Process the command line arguments
  if( argc > 1 )
  {
#if defined( __WXGTK__ ) || defined( __WXMSW__ ) || defined( __WXOSX__ )
    os1 = wxConvLibc.cMB2WC( argv[ 1 ] );
#else
    os1 = argv[ 1 ];
#endif
    if( os1 == wxT("-h") ) { Usage( argv[ 0 ] ); exit( EXIT_SUCCESS ); }
    else                   { Usage( argv[ 0 ] ); exit( EXIT_FAILURE ); }
  }

  // Display the utility banner
  cout << "\n  CnvtType Class Test Utility"
       << "\n   Version 1.21 (2016-09-29)\n\n";

  //---------------------------------------

  Test_GetNum( wxT("-10.0E-2uF") );
  Test_GetNum( wxT("0.0E2uVolt") );
  Test_GetNum( wxT("bigbums(1)") );

  cout << '\n';

  //---------------------------------------

  Test_IsType( wxT("123.4")  );
  Test_IsType( wxT("1234.")  );
  Test_IsType( wxT("123E-4") );
  Test_IsType( wxT("123E4")  );
  Test_IsType( wxT("1234m")  );
  Test_IsType( wxT("1000m")  );
  Test_IsType( wxT("bum(1)") );

  cout << '\n';

  //---------------------------------------

  cout << "CnvtType::bParseFlt   ( ) : ";
  fNum = 12.3E-04;
  if( CnvtType::bParseFlt( fNum, &fMan, &iExp ) )   cout << "Success";
  else                                              cout << "Failure";
  cout << " ( " << fNum << " -->  " << fMan << "E" << iExp << ")\n";

  cout << "CnvtType::bParseFlt   ( ) : ";
  dfNum = -12.3E-04;
  if( CnvtType::bParseDbl( dfNum, &dfMan, &iExp ) ) cout << "Success";
  else                                              cout << "Failure";
  cout << " (" << dfNum << " --> " << dfMan << "E" << iExp << ")\n";

  cout << '\n';

  //---------------------------------------

  cout << "CnvtType::bStrToInt   ( ) : ";
  osNum = wxT("123.45E-5");
  if( CnvtType::bStrToInt( osNum, &iNum ) )         cout << "Success";
  else                                              cout << "Failure";
  cout << " (\"" << osNum.C_STR( ) << "\" --> " << iNum << ")\n";

  cout << "CnvtType::bStrToLong  ( ) : ";
  osNum = wxT("123.5V");
  if( CnvtType::bStrToLong( osNum, &liNum ) )       cout << "Success";
  else                                              cout << "Failure";
  cout << " (\"" << osNum.C_STR( ) << "\"    --> " << liNum << ")\n";

  cout << '\n';

  //---------------------------------------

  cout << "CnvtType::bStrToFlt   ( ) : ";
  osNum = wxT("123.45E-5");
  if( CnvtType::bStrToFlt( osNum, &fNum ) )         cout << "Success";
  else                                              cout << "Failure";
  cout << " (\"" << osNum.C_STR( ) << "\" --> " << fNum << ")\n";

  cout << "CnvtType::bStrToDbl   ( ) : ";
  osNum = wxT("123.4uV");
  if( CnvtType::bStrToDbl( osNum, &dfNum ) )        cout << "Success";
  else                                              cout << "Failure";
  cout << " (\"" << osNum.C_STR( ) << "\"   --> " << dfNum << ")\n";

  cout << '\n';

  //---------------------------------------

  cout << "CnvtType::bIntToStr   ( ) : ";
  iNum = 123;
  if( CnvtType::bIntToStr( iNum, osNum ) )          cout << "Success";
  else                                              cout << "Failure";
  cout << " ( " << iNum << " --> \"" << osNum.C_STR( ) << "\")\n";

  cout << "CnvtType::bLongToStr  ( ) : ";
  liNum = -123;
  if( CnvtType::bLongToStr( liNum, osNum ) )        cout << "Success";
  else                                              cout << "Failure";
  cout << " (" << liNum << " --> \"" << osNum.C_STR( ) << "\")\n";

  cout << '\n';

  //---------------------------------------

  cout << "CnvtType::bFltToStr   ( ) : ";
  fNum = 1.23E+04;
  if( CnvtType::bFltToStr( fNum, osNum ) )          cout << "Success";
  else                                              cout << "Failure";
  cout << " ( " << fNum << " --> \"" << osNum.C_STR( ) << "\")\n";

  cout << "CnvtType::bDblToStr   ( ) : ";
  dfNum = -1.23E+04;
  if( CnvtType::bDblToStr( dfNum, osNum ) )         cout << "Success";
  else                                              cout << "Failure";
  cout << " (" << dfNum << " --> \"" << osNum.C_STR( ) << "\")\n";

  cout << '\n';

  //---------------------------------------

  cout << "CnvtType::bFltToStrEng( ) : ";
  fNum = 1.23E+04;
  if( CnvtType::bFltToStrEng( fNum, osNum ) )       cout << "Success";
  else                                              cout << "Failure";
  cout << " ( " << fNum << " --> \"" << osNum.C_STR( ) << "\")\n";

  cout << "CnvtType::bDblToStrEng( ) : ";
  dfNum = -1.23E+04;
  if( CnvtType::bDblToStrEng( dfNum, osNum ) )      cout << "Success";
  else                                              cout << "Failure";
  cout << " (" << dfNum << " --> \"" << osNum.C_STR( ) << "\")\n";

  cout << '\n';

  //---------------------------------------

  cout << "Print various macro values defined in TypeDefs.hpp :\n";
  PrintLimits( );

  exit( EXIT_SUCCESS );
}

//**************************************************************************************************

void  Test_GetNum( const TYPE_STR & rosNum )
{
  TYPE_STR  os1;

  cout << "CnvtType::rosGetNum   ( ) : ";
  os1 = CnvtType::rosGetNum( rosNum );
  cout << "\"" << rosNum.C_STR( ) << "\" --> \"" << os1.C_STR( ) << "\"\n";
}

//**************************************************************************************************

void  Test_IsType( const TYPE_STR & rosNum )
{
  TYPE_STR  os1;

  os1 = TYPE_STR( wxT("\"") ) + rosNum + wxT("\"");

  cout << left;
  cout << "CnvtType::bIsInteger  ( ) : " << setw( 8 ) << os1.C_STR( ) << " --> "
       << (CnvtType::bIsInteger( rosNum ) ? "True" : "False") << '\n';
  cout << "CnvtType::bIsFloat    ( ) : " << setw( 8 ) << os1.C_STR( ) << " --> "
       << (CnvtType::bIsFloat  ( rosNum ) ? "True" : "False") << '\n';
}

//**************************************************************************************************

void  PrintLimits( void )
{
  cout << "  UINT_MAX =  " <<  UINT_MAX << "\n";
  cout << "  ULNG_MAX =  " << ULONG_MAX << "\n";
  cout << "   INT_MIN = "  <<   INT_MIN << "\n";
  cout << "   INT_MAX =  " <<   INT_MAX << "\n";
  cout << "   LNG_MIN = "  <<  LONG_MIN << "\n";
  cout << "   LNG_MAX =  " <<  LONG_MAX << "\n";
  cout << "   LLG_MIN = "  << LLONG_MIN << "\n";
  cout << "   LLG_MAX =  " << LLONG_MAX << "\n";
  cout << "   FLT_MIN =  " <<   FLT_MIN << "\n";
  cout << "   FLT_MAX =  " <<   FLT_MAX << "\n";
  cout << "   DBL_MIN =  " <<   DBL_MIN << "\n";
  cout << "   DBL_MAX =  " <<   DBL_MAX << "\n";
  cout << '\n';
}

//**************************************************************************************************

void  Usage( char * psAppName )
{
  cout << "\nUsage   : " << psAppName << " [-OPTIONS]"
       << "\nOptions :"
       << "\n  -h : Print usage (this message)\n\n";
}

#endif // TEST_CNVTTYPE

//**************************************************************************************************
