// file: $isip/class/system/Integral/Integral.h
// version: $Id: Integral.h 10640 2007-01-27 02:36:04Z tm334 $
//

// make sure definitions are only made once:
//  the isip convention is to use the class name preceded by "ISIP_"
//
#ifndef ISIP_INTEGRAL
#define ISIP_INTEGRAL

// system include files - since all classes include a debug method
// which calls Console, a FILE* is required. we will include stdio.h
// here, in order that every class need not replicate this. for
// convenience, we also include stdlib.h, since this contains many
// useful C functions. math.h and limits.h define useful constants and
// system functions. unistd.h is included for the definition of
// sleep.
//
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <unistd.h>
#include <time.h>
#include <iostream>
using namespace std;

// possibly include the dmalloc library
//
#ifdef ISIP_DMALLOC
#include <dmalloc.h>
#endif

// system include files needed for non Solaris systems
//
#if ISIP_WCHAR_MODE == ISIP_WCHAR_REDHAT
#include <wchar.h>
#endif

// include the configure output
//
#define ISIP_INTERNAL_USE_ONLY
#include <IntegralConfigure.h>
#undef ISIP_INTERNAL_USE_ONLY

// include the internal integral types
//
#define ISIP_INTERNAL_USE_ONLY
#include <IntegralTypes.h>
#undef ISIP_INTERNAL_USE_ONLY

// include the internal namespace conflict prevention list
//
#define ISIP_INTERNAL_USE_ONLY
#include <IntegralNameSpace.h>
#undef ISIP_INTERNAL_USE_ONLY

// forward class definition(s)
//
class SysString;
class SysChar;

// Integral: define some low level system functions to be used by
// other classes.
//
class Integral {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
 public:
  
  // define the class name
  //
  static const SysString CLASS_NAME;
  
  //-----------------------------------------
  //
  // operating system related string constants
  //
  //-----------------------------------------  

  // define environmental variables which should be present
  //
  static const SysString ENV_ISIP;
  static const SysString ENV_HOME;

  // define some constants that facilitate parsing of environment variables
  // and file pathnames under unix
  //
  static const SysChar ENV_MARKER;
  static const SysChar DIR_DELIM;
  static const SysChar DIR_HOME;

  // define some unix commands
  //
  static const SysString UNIX_CMD_COPY;
  static const SysString UNIX_CMD_MOVE;
  static const SysString UNIX_CMD_REDIR;

  // define some constants for temporary filename creation
  //
  static const SysString TMPDIR;
  static const SysString TMP_FMT_0;
  static const SysString TMP_FMT_1;
  static const SysString TMP_TEMPLATE;

  // define some constants related to help message generation
  //
  static const SysString HELP_FORMAT;
  static const SysString HELP_OPTION;

  //----------------------------------------
  //
  // operating system related enumerated constants
  //
  //----------------------------------------  

  // define negative and positive sign
  //
  static const long NEGATIVE = -1;
  static const long POSITIVE = 1;
  
  // define some sorting and comparison constants - they are useful from time
  // to time to have a global notion of the result of the order of things.
  // these constants should be used for sorting classes, comparisons, etc.
  //
  enum ORDER { ASCENDING = 0, DESCENDING, COLLATING, DEF_ORDER = ASCENDING };
  enum COMPARE { LESSER = 0, EQUAL, GREATER, DEF_COMPARE = EQUAL };

  // define constants that control the behavior of the clear method
  //
  enum CMODE { RETAIN = 0, RESET, RELEASE, FREE, DEF_CMODE = RESET };

  // define constants that control the behavior of the graph class
  //
  enum COLOR { WHITE = 0, GREY, BLACK, BLUE, GREEN, DEF_COLOR = WHITE };
  
  // define constants for representing the storage mode of a matrix -
  // it is again useful to have a global notion of the storage type of
  // a matrix:
  //
  //  MatrixFloat a(Integral::SYMMETRIC);
  //  MatrixLong  b(Integral::SYMMETRIC);
  //
  // vs.
  // 
  //  MatrixFloat a(MatrixFloat::SYMMETRIC);
  //  MatrixLong  b(MatrixLong::SYMMETRIC);
  //
  enum MTYPE { FULL = 0, DIAGONAL, SYMMETRIC,
	       LOWER_TRIANGULAR, UPPER_TRIANGULAR, SPARSE,
	       UNCHANGED, UNKNOWN, DEF_MTYPE = FULL };

  // define a return value for exiting
  //
  static const long SYS_RETURN_ERROR = 1;
  static const long SYS_RETURN_NOERROR = 0;

  // define some debugging constants
  //
  enum DEBUG { NONE = 0, BRIEF, DETAILED, ALL, DEF_DEBUG = NONE };

  //----------------------------------------
  //
  // other numeric constants
  //
  //----------------------------------------  

  // define some maximum sizes (i/o in unix still needs some fixed
  // buffers every now and then)
  //
  static const long MAX_FNAME_SIZE = PATH_MAX;

  // define some constants related to geometry and pi
  //
  static const double PI = M_PI;
  static const double TWO_PI = (2.0*M_PI);
  static const double HALF_PI = (M_PI_2);
  static const double QUARTER_PI = (M_PI_4);
  static const double SQRT_TWO_PI = 2.506628274631;
  static const double INV_PI = M_1_PI;
  static const double TWO_INV_PI = M_2_PI;
  static const double SQRT_TWO = M_SQRT2;
  static const double INV_SQRT_TWO_PI = (1.0 / 2.506628274631);
  static const double DEGREES_IN_CIRCLE = 360.0;
  
  // define a iteration count for testing random functions in diagnose method
  //
  static const long DIAG_ITER_COUNT = 100;

  // default arguments to find methods
  //
  static const long NO_POS = -1;
  
  // define important powers of two
  //
  static const double TWO_EXP7  = ((ulong)1 << 7);
  static const double TWO_EXP8  = ((ulong)1 << 8);
  static const double TWO_EXP15 = ((ulong)1 << 15);
  static const double TWO_EXP16 = ((ulong)1 << 16);
  static const double TWO_EXP23 = ((ulong)1 << 23);
  static const double TWO_EXP24 = ((ulong)1 << 24);
  static const double TWO_EXP31 = ((ulong)1 << 31);
  static const double TWO_EXP32 = ((ullong)1 << 32);
  static const double TWO_EXP63 = ((ullong)1 << 63);
  static const double TWO_EXP64 = (((ullong)1 << 63) << 1) ;
  
  // define constants related to natural logarithms
  //
  static const double E = M_E;
  static const double LOG2E = M_LOG2E;
  static const double LOG10E = M_LOG10E;
  static const double LN2 = M_LN2;
  static const double LN10 = M_LN10;

  // define constants related to changing the base of a logarithm:
  //  currently, we only need to accommodate base 2
  //
  static const double INV_LN2 = 1.0 / M_LN2;  
  static const double INV_LN10 = 1.0 / M_LN10;  

  // define the square root and inverse of square root of 2
  //
  static const double SQRT2 = M_SQRT2;
  static const double INV_SQRT2 = M_SQRT1_2;
  
  // define dB constants
  //  
  static const double DB_POW_MIN_VALUE = 1.0e-10;
  static const double DB_POW_SCALE_FACTOR = 10.0;
  
  static const double DB_MAG_MIN_VALUE = 1.0e-10;
  static const double DB_MAG_SCALE_FACTOR = 20.0;

  static const double DB_LOG_MIN_VALUE = -1.0e10;  
  static const double DB_MIN_VALUE = -100.0;

  // define dB limits:
  //  1. MIN_LOG_VALUE is computed as follows:
  //     MIN_SCORE = -1e+10 = -1 / exp(MIN_LOG_SCORE)
  //
  static const double MIN_LOG_VALUE = -23.02585093;

  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------  

  // default arguments to methods
  //
  static const long DEF_GROW_SIZE = 1024;
  
  // default precision values
  //
  // the almostEqual method compares two numbers to a user-specified
  // level of precision.  the precision is specified as a percentage
  // of the arguments.  the scale is used to control the range over
  // which the comparison is imposed.
  //
  static const double DEF_PERCENTAGE = 0.1;
  static const double DEF_BOUND = 0.01;

  //---------------------------------------
  //
  // error codes
  //
  //---------------------------------------

  static const long ERR = 1500;

  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
 protected:

  // declare a static debug level:
  //  the debug flag is static because the Integral class is never instantiated
  //
  static Integral::DEBUG debug_level_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
 public:
  
  // method: name
  //
  static const SysString& name() {
    return CLASS_NAME;
  }

  // other static methods
  //
  static boolean diagnose(DEBUG debug_level);
  
  // method: setDebug
  //
  static boolean setDebug(Integral::DEBUG level) {
    debug_level_d = level;
    return true;
  }

  // other debug methods:
  //  generally, the debug method is non-static. however,
  //  here it must be static so that the class can be called without being
  //  instantiated
  //
  static boolean debug(const unichar* message);
  
  // destructor/constructor(s) are private
  //

  // assign methods:
  //  these methods are omitted because Integral objects
  //  can not be instantiated
  //

  // operator= methods:
  //  these methods are omitted because Integral objects
  //  can not be instantiated
  //
  
  // i/o methods:
  //  these methods are omitted because Integral objects
  //  can not be instantiated
  //

  // equality methods:
  //  these methods are omitted because Integral objects
  //  can not be instantiated
  //

  // memory management methods:
  //  these methods are omitted because Integral objects
  //  can not be instantiated
  //

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  general operating system methods
  //
  //---------------------------------------------------------------------------

  // wrappers for operating system methods:
  //  environmental variables
  //
  static boolean getEnv(SysString& val, const SysString& var);
  static boolean getLoginDir(SysString& directory,
			     const SysString& login_name);
  static boolean expandName(SysString& exp_file, const SysString& in_file);

  // method: getPid
  //  get a process id number
  //
  static long getPid() {
    return (long)(getpid());
  }

  // method: getParentPid
  //  get the parent process id number
  //
  static long getParentPid() {
    return (long)getppid();
  }
  
  // method: getHostid
  //  get a host id number
  //
  static long getHostid() {
    return (long)(gethostid());
  }
  
  // wrapper for operating system methods:
  //  cleanly exit a program by invoking proper garbage collection
  //
  static long exit();
  
  // wrappers for operating system methods:
  //  temporary files
  //
  static boolean makeTemp(SysString& tmp_file, const SysString& name);
  static boolean makeTemp(SysString& tmp_file);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  bit-level methods
  //
  //---------------------------------------------------------------------------

  // method: almostEqual (generalized case)
  //  the general implementation of almostEqual performs a bitwise comparison
  //  between the arguments and checks for equality. this is useful
  //  for types such as integers and chars. this is not the main use
  //  of this method. it is really intended to compare floating point
  //  quantities to a user-specified level of precision. note that the
  //  default values supplied here are essentially ignored.
  //
  //  note that overloads are used as opposed to default values since
  //  default values do not work in concert with template specializations.
  //
  template <class TIntegral>
    static boolean almostEqual(TIntegral arg1, TIntegral arg2,
			       double percent = DEF_PERCENTAGE,
			       double bound = DEF_BOUND) {
    return (arg1 == arg2);
  }

  // method: almostEqual
  //  define a specialized version for floats. the main reason for this
  //  method is to make sure the double version gets called for floating
  //  point numbers (avoiding type ambiguity).
  //
  static boolean almostEqual(float arg1, float arg2,
			     double percent = DEF_PERCENTAGE,
			     double bound = DEF_BOUND) {
    return almostEqual((double)arg1, (double)arg2, percent, bound);
  }

  // method: almostEqual
  //  define a specialized version for doubles
  //
  static boolean almostEqual(double arg1, double arg2,
			     double percent = DEF_PERCENTAGE,
			     double bound = DEF_BOUND);
  
  // method: almostEqual
  //  define a specialized version for complexfloats. the main reason
  //  for this method is to make sure the double version gets called
  //  for floating point numbers (avoiding type ambiguity).
  //
  static boolean almostEqual(const complexfloat& arg1,
			     const complexfloat& arg2,
			     double percent = DEF_PERCENTAGE,
			     double bound = DEF_BOUND) {
    return (almostEqual(arg1.real(), arg2.real(), percent, bound) &&
	    almostEqual(arg1.imag(), arg2.imag(), percent, bound));
  }

  // method: almostEqual
  //  define a specialized version for complexdouble. the main reason
  //  for this method is to make sure the double version gets called
  //  for floating point numbers (avoiding type ambiguity).
  //
  static boolean almostEqual(const complexdouble& arg1,
			     const complexdouble& arg2,
			     double percent = DEF_PERCENTAGE,
			     double bound = DEF_BOUND) {
    return (almostEqual(arg1.real(), arg2.real(), percent, bound) &&
	    almostEqual(arg1.imag(), arg2.imag(), percent, bound));
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  inlined wrappers for math and other C functions (listed alphabetically)
  //
  //---------------------------------------------------------------------------

  // method: abs
  //
  static double abs(double arg) {
    return ::fabs(arg);
  }

  // method: acos
  //
  static double acos(double arg) {
    return ::acos(arg);
  }

  // method: acosh
  //
  static double acosh(double arg) {
    return ::log(arg + ::sqrt(arg * arg - (double)1));
  }

  // method: asin
  //
  static double asin(double arg) {
    return ::asin(arg);
  }

  // method: asinh
  //
  static double asinh(double arg) {
    return ::log(arg + ::sqrt(arg * arg + (double)1));
  }

  // method: atan
  //
  static double atan(double arg) {
    return ::atan(arg);
  }

  // method: atanh
  //
  static double atanh(double arg) {
    return 0.5 * ::log(((double)1 + arg) / ((double)1 - arg));
  }

  // method: ceil
  //
  static double ceil(double arg) {
    return ::ceil(arg);
  }

  // method: cos
  //
  static double cos(double arg) {
    return ::cos(arg);
  }

  // method: cosh
  //
  static double cosh(double arg) {
    return ::cosh(arg);
  }

  // method: exp
  //
  static double exp(double arg) {
    return ::exp(arg);
  }

  // method: exp2
  //
  static double exp2(double arg) {
    return ::exp(arg * LN2);
  }

  // method: exp10
  //
  static double exp10(double arg) {
    return ::exp(arg * LN10);
  }

#if ISIP_WCHAR_MODE == ISIP_WCHAR_NONE
  
  // method: fgetc
  //
  static long fgetc(FILE* stream) {
    return ::fgetc(stream);
  }
#endif
  
#if ISIP_WCHAR_MODE == ISIP_WCHAR_NONE

  // method: fgets
  //
  static char* fgets(char* s, int n, FILE* stream) {
    return ::fgets(s, n, stream);
  }
#endif

#if ISIP_WCHAR_MODE != ISIP_WCHAR_NONE

  // method: fgetwc
  //
  static wint_t fgetwc(FILE* stream) {
    return ::fgetwc(stream);
  }
#endif

#if ISIP_WCHAR_MODE != ISIP_WCHAR_NONE

  // method: fgetws
  //
  static unichar* fgetws(unichar* s, int n, FILE* stream) {
    return ::fgetws(s, n, stream);
  }
#endif
  
  // method: floor
  //
  static double floor(double arg) {
    return ::floor(arg);
  }


#if ISIP_WCHAR_MODE == ISIP_WCHAR_NONE
  
  // method: fputc
  //
  static long fputc(int c, FILE* stream) {
    return ::fputc(c, stream);
  }
#endif
  
#if ISIP_WCHAR_MODE == ISIP_WCHAR_NONE

  // method: fputs
  //
  static long fputs(char* s, FILE* stream) {
    return ::fputs(s, stream);
  }
#endif
  
#if ISIP_WCHAR_MODE != ISIP_WCHAR_NONE

  // method: fputwc
  //
  static wint_t fputwc(wint_t c, FILE* stream) {
    return ::fputwc(c, stream);
  }
#endif
  
#if ISIP_WCHAR_MODE != ISIP_WCHAR_NONE
  
  // method: fputws
  //
  static long fputws(const unichar* s, FILE* stream) {
    return ::fputws(s, stream);
  }  
#endif
  
  // method: fraction
  //
  static double fraction(double arg) {
    static double temp;
    return ::modf(arg, &temp);
  }

  // method: fseek
  //
  static long fseek(FILE* fptr, long offset, int whence) {
    return fseek(fptr, offset, whence);
  }
  
  // method: hash
  // hash methods for byte, ushort, short, ulong, llong, ullong,
  // float, double, long.
  static ulong hash(byte val, ulong capacity) {
    ulong temp = (ulong)val;
    return hash_32(temp, capacity);    
  }
  
  static ulong hash(ushort val, ulong capacity) {
    ulong temp = (ulong)val;
    return hash_32(temp, capacity);            
  }
  
  static ulong hash(short val, ulong capacity) {
    int32 copy = (int32) val;
    int32 temp = *((int32*) &copy);
    return hash_32(temp, capacity);        
  }
  
  static ulong hash(ulong val, ulong capacity) {
    return hash_32(val, capacity);            
  }
  
  static ulong hash(llong val, ulong capacity) {
    int64 copy = (int64) val;
    int64 temp = *((int64*) &copy);
    return hash_64(temp, capacity);        
  }
  
  static ulong hash(ullong val, ulong capacity) {
    ullong temp = (ullong)val;
    return hash_64(temp, capacity);        
  }
  
  static ulong hash(float val, ulong capacity) {
    // we don't know what size a float is so we move it to a 32 bit number
    // so we can guarantee the size
    //    
    float32 copy = (float32) val;

    // we want to reinterpret those 32 bits as an integer. If we were to
    // cast then we would greatly reduce the variability in the possible
    // numbers. Instead, we cast the location pointer to an integer pointer
    // so the the 32-bit float will be interpreted as a 32-bit integer
    // the full range of variablity in the 32 bits is preserved
    //
    int32 temp = *((int32*) &copy);
    cout << "Integral::hash(float, ulong)" << endl;
    cout << val << endl;
    cout << temp<< endl;
    cout << capacity << endl;
    return hash_32(temp, capacity);    
  }
  
  static ulong hash(double val, ulong capacity) {
    float64 copy = (float64) val;
    int64 temp = *((int64*) &copy);
    cout << "Integral::hash(double, ulong)" << endl;
    cout << val << endl;
    cout << temp<< endl;
    cout << capacity << endl;
    return hash_64(temp, capacity);        
  }
  
  static ulong hash(long val, ulong capacity) {
    int32 copy = (int32) val;
    int32 temp = *((int32*) &copy);
    return hash_32(temp, capacity);        
  }

  // method: hash
  // for 32 bits single value
  static ulong hash_32(ulong val, ulong capacity) {
    return val % (capacity - 1);
  }
  
  // method: hash_64
  // for 64 bits single value
  static ulong hash_64(ullong val, ulong capacity) {
    return val % (capacity - 1);
  }  
  
  // for 32 bits vector
  static long hash(ulong* vector, long num_elements, long capacity);

  // method: integer
  //
  static double integer(double arg) {
    static double integ;
    ::modf(arg, &integ);
    return integ;
  }

  // method: log
  //
  static double log(double arg) {
    return ::log(arg);
  }

  // method: log2
  //
  static double log2(double arg) {
    return ::log(arg) * INV_LN2;
  }

  // method: log10
  //
  static double log10(double arg) {
    return ::log10(arg);
  }

  // method: log1p
  //
  static double log1p(double arg) {
    return ::log((double)1 + arg);
  }

  // method: max
  //
  static double max(double arg1, double arg2) {
    if (arg1 > arg2) {
      return arg1;
    }
    else {
      return arg2;
    }
  }

  // method: min
  //
  static double min(double arg1, double arg2) {
    if (arg1 < arg2) {
      return arg1;
    }
    else {
      return arg2;
    }
  }

  // method: pow
  //
  static double pow(double arg, double exponent) {
    return ::pow(arg, exponent);
  }

  // method: round
  //
  static double round(double arg) {
    if (arg > (double)0) {
      return ::floor(arg + 0.5);
    }
    else {
      return ::ceil(arg - 0.5);
    }
  }

  // method: sin
  //
  static double sin(double arg) {
    return ::sin(arg);
  }

  // method: sinh
  //
  static double sinh(double arg) {
    return ::sinh(arg);
  }

  // method: sleep
  //
  static boolean sleep(long sleep_seconds) {
    ::sleep(sleep_seconds);
    return true;
  }
  
  // method: sqrt
  //
  static double sqrt(double arg) {
    return ::sqrt(arg);
  }

  // method: tan
  //
  static double tan(double arg) {
    return ::tan(arg);
  }

  // method: tanh
  //
  static double tanh(double arg) {
    return ::tanh(arg);
  }
  
#if ISIP_WCHAR_MODE == ISIP_WCHAR_SOLARIS
  
  // method: wcswcs
  //  
  static unichar* wcswcs(const unichar* ws1, const unichar* ws2) {
    
    // call the solaris 2.6 system function
    //
    return ::wcswcs(ws1, ws2);
  }
#endif
  
#if ISIP_WCHAR_MODE != ISIP_WCHAR_SOLARIS
  
  // method: wcsstr
  //
  static unichar* wcsstr(const unichar* ws1, const unichar* ws2) {
    
    // call the more standard system function
    //
    return ::wcsstr(ws1, ws2);    
  }
#endif

#if ISIP_WCHAR_MODE == ISIP_WCHAR_SOLARIS
  
  // method: wcstok
  //
  static unichar* wcstok(unichar* ws, const unichar* delim, unichar** ptr) {
    
    // solaris 2.6 needs an older version of wcstok
    //
    return ::wcstok(ws, delim);
  }
#endif
  
#if ISIP_WCHAR_MODE != ISIP_WCHAR_SOLARIS

  // method: wcstok
  //
  static unichar* wcstok(unichar* ws, const unichar* delim, unichar** ptr) {
    
    // any other supporting operating system uses the new definition
    //
    return ::wcstok(ws, delim, ptr);    
  }  
#endif
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  complex versions of math and other C functions (listed alphabetically)
  //
  //  the implementations used below can be found in:
  //
  //   R.V. Churchill, J.W. Brown, and R.F. Verhey,
  //   Complex Variables and Applications, Mc-Graw Hill,
  //   New York, New York, USA, 1976, pp. 52-71.
  //
  //---------------------------------------------------------------------------

  // method: abs
  //
  static double abs(const complexdouble& arg) {
    return arg.mag();
  }

  // method: acos
  //
  static complexdouble acos(const complexdouble& arg) {
    static const complexdouble j(0, 1);
    return (complexdouble)-1 * j *
      log(arg + j * sqrt((complexdouble)1 - arg * arg));
  }

  // method: acosh
  //
  static complexdouble acosh(const complexdouble& arg) {
    return complexdouble (log(arg + sqrt(arg * arg - (complexdouble)1)));
  }

  // method: asin
  //
  static complexdouble asin(const complexdouble& arg) {
    static const complexdouble j(0, 1);
    return (complexdouble)-1 * j *
      log((j * arg) + sqrt((complexdouble)1 - (arg * arg)));
  }

  // method: asinh
  //
  static complexdouble asinh(const complexdouble& arg) {
    return complexdouble(log(arg + sqrt(arg * arg + (complexdouble)1)));
  }

  // method: atan
  //
  static complexdouble atan(const complexdouble& arg) {
    static const complexdouble j(0, 1);
    return log((j + arg) / (j - arg)) * j / (complexdouble)2;
  }

  // method: atanh
  //
  static complexdouble atanh(const complexdouble& arg) {
    return log(((complexdouble)1 + arg) / 
	       ((complexdouble)1 - arg)) / (complexdouble)2;
  }

  // method: ceil
  //
  static complexdouble ceil(const complexdouble& arg) {
    return complexdouble(ceil(arg.real()), ceil(arg.imag()));
  }

  // method: cos
  //
  static complexdouble cos(const complexdouble& arg) {
    return complexdouble(cos(arg.real()) * cosh(arg.imag()),
			 - sin(arg.real()) * sinh(arg.imag()));
  }
  
  // method: cosh
  //
  static complexdouble cosh(const complexdouble& arg) {
    return complexdouble(cosh(arg.real()) * cos(arg.imag()),
			 + sinh(arg.real()) * sin(arg.imag()));
  }

  // method: exp
  //
  static complexdouble exp(const complexdouble& arg) {
    return complexdouble::polar(exp(arg.real()), arg.imag());
  }

  // method: exp2
  //
  static complexdouble exp2(const complexdouble& arg) {
    return exp(arg * LN2);
  }

  // method: exp10
  //
  static complexdouble exp10(const complexdouble& arg) {
    return exp(arg * LN10);
  }
  
  // method: floor
  //
  static complexdouble floor(const complexdouble& arg) {
    return complexdouble(floor(arg.real()), floor(arg.imag()));
  }
   
  // method: log
  //
  static complexdouble log(const complexdouble& arg) {
    return complexdouble(log(arg.mag()), arg.angle());
  }

  // method: log2
  //
  static complexdouble log2(const complexdouble& arg) {
    return log(arg) * INV_LN2;
  }

  // method: log10
  //
  static complexdouble log10(const complexdouble& arg) {
    return log(arg) * INV_LN10;
  }

  // method: log1p
  //
  static complexdouble log1p(const complexdouble& arg) {
    return log((complexdouble)1 + arg);
  }

  // method: max
  //
  static complexdouble max(const complexdouble& arg1,
			   const complexdouble& arg2) {
    if (arg1 >= arg2) {
      return arg1;
    }
    else {
      return arg2;
    }
  }

  // method: min
  //
  static complexdouble min(const complexdouble& arg1,
			   const complexdouble& arg2) {
    if (arg1 < arg2) {
      return arg1;
    }
    else {
      return arg2;
    }
  }

  // method: pow
  //
  static complexdouble pow(const complexdouble& arg,
			   const complexdouble& exponent) {
    return exp(exponent * log(arg));
  }

  // method: round
  //
  static complexdouble round(const complexdouble& arg) {
    return complexdouble(round(arg.real()), round(arg.imag()));
  }

  // method: sin
  //
  static complexdouble sin(const complexdouble& arg) {
    return complexdouble(sin(arg.real()) * cosh(arg.imag()),
			 cos(arg.real()) * sinh(arg.imag()));
  }

  // method: sinh
  //
  static complexdouble sinh(const complexdouble& arg) {
    return complexdouble(sinh(arg.real()) * cos(arg.imag()),
			 cosh(arg.real()) * sin(arg.imag()));
  }

  // method: sqrt
  //
  static complexdouble sqrt(const complexdouble& arg) {
    double r = arg.mag();
    double nr, ni;
    if (r == 0.0) {
      ni = r;
      nr = r;
    }
    else if (arg.real() > 0) {
      nr = sqrt(0.5 * (r + arg.real()));
      ni = arg.imag() / nr;
      ni /= 2;
    }
    else {
      ni = sqrt(0.5 * (r - arg.real()));
      if (arg.imag() < 0) {
	ni = -ni;
      }
      nr = arg.imag() / ni;
      nr /= 2;
    }
    return complexdouble(nr, ni);
  }

  // method: tan
  //
  static complexdouble tan(const complexdouble& arg) {
    return sin(arg) / cos(arg);
  }

  // method: tanh
  //
  static complexdouble tanh(const complexdouble& arg) {
    return sinh(arg) / cosh(arg);
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  other math functions that are useful for speech research
  //
  //---------------------------------------------------------------------------

  // logarithm-related methods
  //
  static double logAddLog(const double x, const double y);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  functions that are useful for time measurements
  //
  //---------------------------------------------------------------------------
  
  // method: time
  //  method that returns the time in seconds since the Epoch (00:00:00
  //  January 1, 1970, Coordinated Universal Time (UTC))
  //
  static long time() {
    long* p = (long*)NULL;
    return ::time(p);
  }

  // method: cTime
  //
  static boolean cTime(SysString& time);
  
  // method: utcTime
  //
  static boolean utcTime(SysString& time);
  
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
 private:

  // destructor/constructor(s):
  //  these methods are private so the class cannot be instantiated.
  //
  ~Integral();
  Integral();
  Integral(const Integral& arg);
};

  // end of include file
  //
#endif
