// file: $isip/class/math/scalar/MScalar/MScalar.h
// version: $Id: MScalar.h 10675 2009-07-21 01:13:30Z tm334 $
//

// make sure definitions are only made once
//
#ifndef ISIP_MSCALAR
#define ISIP_MSCALAR

#include <typeinfo>

// isip include files
//
#ifndef ISIP_MSCALAR_METHODS
#include "MScalarMethods.h"
#endif

#ifndef ISIP_STRING
#include <String.h>
#endif

#ifndef ISIP_RANDOM
#include <Random.h>
#endif

// MScalar: the scalar template class is inherited by other
// scalar classes. TIntegral is an Integral type, TSize is a size
// specific data type.  most of the methods in this class are inline
// wrappers to the MScalarMethods class functions. this is done to
// allow us to write one method per file, thus avoiding the compilation
// overhead inherent in compiling template classes with multiple
// explicit instantiations.
//
template<class TIntegral, class TSize>
class MScalar {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:

  // define the class name
  //
  static const String CLASS_NAME;

  //----------------------------------------
  //
  // other important constants
  //
  //----------------------------------------
  
  // random number related constants
  //
  static const double RAND_BYTE_MAX = ((ulong)1 << 8);
  static const double RAND_USHORT_MAX = ((ulong)1 << 16);
  static const double RAND_ULONG_MAX = ((ullong)1 << 32);
  static const double RAND_ULLONG_MAX = ((ullong)1 << 63) * 2.0;
  static const double RAND_SHORT_MAX = ((ulong)1 << 15);
  static const double RAND_LONG_MAX = ((ulong)1 << 31);
  static const double RAND_LLONG_MAX = ((ullong)1 << 63);

  // i/o related constants
  //
  static const String DEF_PARAM;
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------
  
  // define the default value(s) of the class data
  //
  static const TIntegral DEF_VALUE;

  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 20000;

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

  // the container for the value of this item
  //
  TIntegral value_d;

  // declare a static debug level for all class instantiations:
  // we don't want every object to waste space on a debug level. let
  //  them share this across all classes.
  //
  static Integral::DEBUG debug_level_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:
  
  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }
  
  // method: diagnose
  //
  static boolean diagnose(Integral::DEBUG debug_level) {
    return MScalarMethods::
      diagnose<MScalar<TIntegral, TSize>, TIntegral>(debug_level);
  }
 
  // method: setDebug
  //
  static boolean setDebug(Integral::DEBUG level) {
    debug_level_d = level;
    return true;
  }

  // other debug methods
  //
  boolean debug(const unichar* msg) const;
  
  // method: destructor
  //
  ~MScalar() {}
 
  // method: default constructor
  //
  MScalar() {}

  // method: copy constructor
  //
  MScalar(const MScalar& arg) {
    assign(arg);
  }

  // method: assign
  //
  boolean assign(const MScalar& arg) {
    value_d = (TIntegral)arg;
    return true;
  }

  // method operator=
  //  we need to have the overloaded operator '=' defined in the
  //  derived classes instead of the base template class. this is
  //  required since c++ predefines the operator '=' for any class by
  //  default and this hides any other definition of this operator in
  //  the base class.
  //

  // method: sofSize
  //
  long sofSize() const {
    return (long)sizeof(TSize);
  }
  
  // method: read
  //
  boolean read(Sof& sof, long tag, const String& name = CLASS_NAME) {
    return MScalarMethods::read(*this, sof, tag, name);
  }
  
  // method: write
  //
  boolean write(Sof& sof, long tag, const String& name = CLASS_NAME) const {
    return MScalarMethods::write(*this, sof, tag, name);
  }

  // method: readData
  //
  boolean readData(Sof& sof, const String& pname = DEF_PARAM,
			  long size = SofParser::FULL_OBJECT,
			  boolean param = true, boolean nested = false) {
    return MScalarMethods::readData(*this, sof, pname, size, param, nested);
  }

  // method: writeData
  //
  boolean writeData(Sof& sof, const String& pname = DEF_PARAM) const {
    return MScalarMethods::writeData(*this, sof, pname);
  }
  
  // method: eq
  //
  boolean eq(const MScalar& arg_a) const {
    return (value_d == arg_a.value_d);
  }

  // memory management methods:
  //  new and delete methods are omitted because they are defined in the
  //  classes that instantiate this template
  //

  // method: clear
  //
  boolean clear(Integral::CMODE ctype = Integral::DEF_CMODE) {
    value_d = (TIntegral)DEF_VALUE;
    return true;
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  extensions to required methods
  //
  //---------------------------------------------------------------------------

  // method: constructor
  //  note that this method is useful to have for the diagnose method.
  //  it is masked by higher-level classes that use this as a template
  //  and have their own version of this constructor.
  //
  MScalar(TIntegral arg) {
    assign(arg);
  }

  // method: assign
  //
  template<class TAIntegral>
  boolean assign(TAIntegral arg) {
    value_d = (TIntegral)arg;
    return true;
  }

  // method: assign
  //
  boolean assign(const String& arg) {
    arg.get(value_d);
    return true;
  }
  
  // method: get
  //
  boolean get(String& arg) const {
    return arg.assign(value_d);
  }

  // method: memSize
  //
  long memSize() const {
    return (long)sizeof(value_d);
  }

  // method: presentValue
  //  define functions which are used to wrap member functions that have
  //  different implementations for different data types, and return the
  //  correct value by using template specialization.
  //
  TIntegral presentValue(double arg) const {
    if ((typeid(TIntegral) == typeid(float)) ||
	(typeid(TIntegral) == typeid(double))) {
      return (TIntegral)arg;
    }
    else {
      return (TIntegral)Integral::round(arg);
    }
  }

  complexdouble presentValue(complexdouble arg) const {
    if ((typeid(TSize) == typeid(float32)) ||
        (typeid(TSize) == typeid(float64))) {
	return (complexdouble)arg;
    }
    else {
      return (complexdouble)Integral::round(arg);
    }
  }

  // method: almostEqual
  //
  boolean almostEqual(TIntegral arg) const {
    return Integral::almostEqual(value_d, arg);
  }

  // method: almostEqual
  //  
  boolean almostEqual(TIntegral arg, double percent,
		      double bound = Integral::DEF_BOUND) const {
    return Integral::almostEqual(value_d, arg, percent, bound);
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  operator overloads
  //
  //---------------------------------------------------------------------------

  // method: operator TIntegral()
  //
  operator TIntegral() const {
    return value_d;
  }

  // method: operator +
  //
  TIntegral operator+ (TIntegral arg) const {
    return value_d + arg;
  }

  // method: operator -
  //
  TIntegral operator- (TIntegral arg) const {
    return value_d - arg;
  }

  // method: operator *
  //
  TIntegral operator* (TIntegral arg) const {
    return value_d * arg;
  }

  // method: operator /
  //
  TIntegral operator/ (TIntegral arg) const {
    return value_d / arg;
  }

  // method: operator %
  //
/*   TIntegral operator% (TIntegral arg) const { */
/*     return value_d % arg; */
/*   } */

  // method: operator ++
  //
  TIntegral operator++ () {
    return ++value_d;
  }

  // method: operator --
  //
  TIntegral operator-- () {
    return --value_d;
  }
  
  // method: operator ++
  //
  TIntegral operator++ (int) {
    return value_d++;
  }
  
  // method: operator --
  //
  TIntegral operator-- (int) {
    return value_d--;
  }

  // method: operator +=
  //
  MScalar& operator+= (TIntegral arg) {
    value_d += arg;
    return *this;
  }

  // method: operator -=
  //
  MScalar& operator-= (TIntegral arg) {
    value_d -= arg;
    return *this;
  }

  // method: operator *=
  //
  MScalar& operator*= (TIntegral arg) {
    value_d *= arg;
    return *this;
  }

  // method: operator /=
  //
  MScalar& operator/= (TIntegral arg) {
    value_d /= arg;
    return *this;
  }

  // method: operator %=
  //
/*   MScalar& operator%= (TIntegral arg) { */
/*     value_d %= arg; */
/*     return *this; */
/*   } */
   
  // method: operator ==
  //
  boolean operator == (TIntegral arg) const {
    return eq(arg);
  }

  // method: operator !=
  //
  boolean operator != (TIntegral arg) const {
    return ne(arg);
  }

  // method: operator <
  //
  boolean operator < (TIntegral arg) const {
    return lt(arg);
  }

  // method: operator <=
  //
  boolean operator <= (TIntegral arg) const {
    return le(arg);
  }

  // method: operator >
  //
  boolean operator > (TIntegral arg) const {
    return gt(arg);
  }

  // method: operator >=
  //
  boolean operator >= (TIntegral arg) const {
    return ge(arg);
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  equality and comparison methods
  //
  //---------------------------------------------------------------------------

  // method: eq
  //
  boolean eq(TIntegral arg) const {
    return (value_d == arg);
  }

  // method: eq
  //
  boolean eq(TIntegral arg1, TIntegral arg2) const {
    return (arg1 == arg2);
  }

  // method: ne
  //
  boolean ne(TIntegral arg) const {
/*     printf("MScalar:\n"); */
/*     std::cout << "typeid(value_d): " << typeid(value_d).name() << endl; */
/*     String s1; */
/*     s1.assign(value_d); */
/*     Console::put(s1); */
    return (value_d != arg);
  }
  
  // method: lt
  //
  boolean lt(TIntegral arg) const {
    return (value_d < arg);
  }

  // method: le
  //
  boolean le(TIntegral arg) const {
    return (value_d <= arg);
  }

  // method: gt
  //
  boolean gt(TIntegral arg) const {
    return (value_d > arg);
  }

  // method: ge
  //
  boolean ge(TIntegral arg) const {
    return (value_d >= arg);
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  basic mathematical methods
  //
  //---------------------------------------------------------------------------

  // method: add
  //
  TIntegral add(TIntegral arg2) {
    return add(value_d, arg2);
  }

  // method: add
  //
  TIntegral add(TIntegral arg1, TIntegral arg2) {
    return (value_d = arg1 + arg2);
  }

  // method: sub
  //
  TIntegral sub(TIntegral arg2) {
    return sub(value_d, arg2);
  }

  // method: sub
  //
  TIntegral sub(TIntegral arg1, TIntegral arg2) {
    return (value_d = arg1 - arg2);
  }

  // method: mult
  //
  TIntegral mult(TIntegral arg2) {
    return mult(value_d, arg2);
  }

  // method: mult
  //
  TIntegral mult(TIntegral arg1, TIntegral arg2) {
    return (value_d = arg1 * arg2);
  }

  // method: div
  //
  TIntegral div(TIntegral arg2) {
    return div(value_d, arg2);
  }

  // method: div
  //
  TIntegral div(TIntegral arg1, TIntegral arg2) {
    return (value_d = arg1 / arg2);
  }

/*   // method: mod */
/*   // */
/*   TIntegral mod(TIntegral arg2) { */
/*     return mod(value_d, arg2); */
/*   } */

/*   // method: mod */
/*   // */
/*   TIntegral mod(TIntegral arg1, TIntegral arg2) { */
/*     return (value_d = arg1 % arg2); */
/*   } */

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  other mathematical methods (listed alphabetically)
  //
  //--------------------------------------------------------------------------

  // method: abs
  //
  TIntegral abs() {
    return abs(value_d);
  }

  // method: abs
  //
  TIntegral abs(TIntegral arg) {
    if (arg > (TIntegral)0) {
      value_d = arg;
    }
    else {
      value_d = -arg;
    }
    return value_d;
  }
 
  // method: acos
  //
  TIntegral acos() {
    return acos(value_d);
  }

  // method: acos
  //
  TIntegral acos(TIntegral arg) {
    return (value_d = (TIntegral)Integral::acos(arg));
  }

  // method: acosh
  //
  TIntegral acosh() {
    return acosh(value_d);
  }

  // method: acosh
  //
  TIntegral acosh(TIntegral arg) {
    return (value_d = (TIntegral)Integral::acosh(arg));
  }

  // method: asin
  //
  TIntegral asin() {
    return asin(value_d);
  }

  // method: asin
  //
  TIntegral asin(TIntegral arg) {
    return (value_d = (TIntegral)Integral::asin(arg));
  }

  // method: asinh
  //
  TIntegral asinh() {
    return asinh(value_d);
  }

  // method: asinh
  //
  TIntegral asinh(TIntegral arg) {
    return (value_d = (TIntegral)Integral::asinh(arg));
  }

  // method: atan
  //
  TIntegral atan() {
    return atan(value_d);
  }

  // method: atan
  //
  TIntegral atan(TIntegral arg) {
    return (value_d = (TIntegral)Integral::atan(arg));
  }

  // method: atanh
  //
  TIntegral atanh() {
    return atanh(value_d);
  }

  // method: atanh
  //
  TIntegral atanh(TIntegral arg) {
    return (value_d = (TIntegral)Integral::atanh(arg));
  }

  // method: ceil
  //
  TIntegral ceil() {
    return ceil(value_d);
  }

  // method: ceil
  //
  template <class TAIntegral>
  TIntegral ceil(TAIntegral arg) {
    return (value_d = (TIntegral)Integral::ceil(arg));
  }

  // method: centerClip
  //
  TIntegral centerClip(TIntegral min, TIntegral max) {
    return MScalarMethods::centerClip(*this, min, max, value_d);
  }

  // method: centerClip
  //
  TIntegral centerClip(TIntegral min, TIntegral max, TIntegral val) {
    return MScalarMethods::centerClip(*this, min, max, val);
  }
  
  // method: cos
  //
  TIntegral cos() {
    return cos(value_d);
  }
  
  // method: cos
  //
  TIntegral cos(TIntegral arg) {
    return (value_d = (TIntegral)Integral::cos(arg));
  }

  // method: cosh
  //
  TIntegral cosh() {
    return cosh(value_d);
  }
  
  // method: cosh
  //
  TIntegral cosh(TIntegral arg) {
    return (value_d = (TIntegral)Integral::cosh(arg));
  }

  // method: exp
  //
  TIntegral exp() {
    return exp(value_d);
  }

  // method: exp
  //
  TIntegral exp(TIntegral arg) {
    return (value_d = presentValue(Integral::exp(arg)));
  }

  // method: exp2
  //
  TIntegral exp2() {
    return exp2(value_d);
  }

  // method: exp2
  //
  TIntegral exp2(TIntegral arg) {
    return (value_d = presentValue(Integral::exp2(arg)));
  }

  // method: exp10
  //
  TIntegral exp10() {
    return exp10(value_d);
  }

  // method: exp10
  //
  TIntegral exp10(TIntegral arg) {
    return (value_d =  presentValue(Integral::exp10(arg)));
  }
  
  // method: factorial
  //
  TIntegral factorial() {
    return factorial(value_d);
  }
  
  // method: factorial
  //
  TIntegral factorial(TIntegral arg) {
    value_d = 1;    
    for (TIntegral count = 2; count <= arg; count++) {
      value_d *= count;
    }
    return value_d;
  }

  // method: floor
  //
  TIntegral floor() {
    return floor(value_d);
  }

  // method: floor
  //
  template <class TAIntegral>
  TIntegral floor(TAIntegral arg) {
    return (value_d = (TIntegral)Integral::floor(arg));
  }

  // method: fraction
  //
  TIntegral fraction() {
    return fraction(value_d);
  }
  
  // method: fraction
  //
  TIntegral fraction(TIntegral arg) {
    return (value_d = (TIntegral)0);
  }
  
  // gaussian random number generation methods
  //
  TIntegral grand(TIntegral mean, TIntegral stddev,
		  Random& generator = Random::GLOBAL_GAUSSIAN);

  // method: hash
  //
  ulong hash(ulong hsize) const{
    // Integral::hash() will give wrong result for the first time invoked with -O2 open
    //
    Integral::hash(value_d, hsize);
    return Integral::hash(value_d, hsize);
  }  
  
  // method: inverse
  //
  TIntegral inverse() {
    return inverse(value_d);
  }
  
  // method: inverse
  //
  TIntegral inverse(TIntegral arg) {
    value_d = 1;
    return (value_d /= arg);
  }

  // method: limit
  //
  TIntegral limit(TIntegral min, TIntegral max) {
    return MScalarMethods::limit(*this, min, max, value_d);
  }
  
  // method: limit
  //
  TIntegral limit(TIntegral min, TIntegral max, TIntegral val) {
    return MScalarMethods::limit(*this, min, max, val);
  }
  
  // method: limitMag
  //
  TIntegral limitMag(TIntegral thresh, TIntegral output) {
    return MScalarMethods::limitMag(*this, thresh, output, value_d);
  }
  
  // method: limitMag
  //
  TIntegral limitMag(TIntegral thresh, TIntegral output, TIntegral val) {
    return MScalarMethods::limitMag(*this, thresh, output, val);
  }

  // method: log
  //
  TIntegral log() {
    return log(value_d);
  }
  
  // method: log
  //
  TIntegral log(TIntegral arg) {

    value_d = presentValue(Integral::log(arg));
    return value_d;
  }

  // method: log2
  //
  TIntegral log2() {
    return log2(value_d);
  }

  // method: log2
  //
  TIntegral log2(TIntegral arg) {
    return (value_d = presentValue(Integral::log2(arg)));
  }
  
  // method: log10
  //
  TIntegral log10() {
    return log10(value_d);
  }
  
  // method: log10
  //
  TIntegral log10(TIntegral arg) {
    return (value_d = presentValue(Integral::log10(arg)));
  }
   
  // method: log1p
  //
  TIntegral log1p() {
    return log1p(value_d);
  }
   
  // method: log1p
  //
  TIntegral log1p(TIntegral arg) {
    return (value_d = presentValue(Integral::log1p(arg)));
  }
   
  // method: max
  //
  TIntegral max(TIntegral arg) {
    return max(value_d, arg);
  }
 
  // method: max
  //
  TIntegral max(TIntegral arg_1, TIntegral arg_2) {
    if (arg_1 > arg_2) {
      return (value_d = arg_1);
    }
    return (value_d = arg_2);
  }
  
  // method: min
  //
  TIntegral min(TIntegral arg) {
    return min(value_d, arg);
  }
  
  // method: min
  //
  TIntegral min(TIntegral arg_1, TIntegral arg_2) {
    if (arg_1 > arg_2) {
      return (value_d = arg_2);
    }
    else {
      return (value_d = arg_1);    
    }
  }

  // method: neg
  //
  TIntegral neg() {
    return neg(value_d);
  }
  
  // method: neg
  //
  TIntegral neg(TIntegral arg) {
    return (value_d = -arg);
  }

  // method: pow
  //
  TIntegral pow(TIntegral exponent) {
    return pow(value_d, exponent);
  }
  
  // method: pow
  //
  TIntegral pow(TIntegral arg, TIntegral exponent) {
    return (value_d = presentValue(Integral::pow(arg, exponent)));
  }

  // random number generation methods
  //
  TIntegral rand(Random& generator = Random::GLOBAL_UNIFORM);
  TIntegral rand(TIntegral min, TIntegral max,
		 Random& generator = Random::GLOBAL_UNIFORM);
  
  // method: rceil
  //
  TIntegral rceil() {
    return rceil(value_d);
  }

  // method: rceil
  //
  TIntegral rceil(TIntegral arg) {
    return (value_d = (TIntegral)(Integral::ceil(arg) - arg));
  }

  // method: rfloor
  //
  TIntegral rfloor() {
    return rfloor(value_d);
  }

  // method: rfloor
  //
  TIntegral rfloor(TIntegral arg) {
    return (value_d = (TIntegral)(Integral::floor(arg) - arg));
  }

  // method: round
  //
  TIntegral round() {
    return round(value_d);
  }

  // method: round
  //
  template <class TAIntegral>
  TIntegral round(TAIntegral arg) {
    return (value_d = (TIntegral)Integral::round(arg));
  }

  // method: sign
  //
  TIntegral sign() {
    return sign(value_d);
  }
  
  // method: sign
  //
  TIntegral sign(TIntegral arg) {
    if (arg < (TIntegral)0.0) {
      return (value_d = (TIntegral)Integral::NEGATIVE);
    }
    return (value_d = (TIntegral)Integral::POSITIVE);    
  }
  
  // method: sin
  //
  TIntegral sin() {
    return sin(value_d);
  }

  // method: sin
  //
  TIntegral sin(TIntegral arg) {
    return (value_d = (TIntegral)Integral::sin(arg));
  }

  // method: sinh
  //
  TIntegral sinh() {
    return sinh(value_d);
  }

  // method: sinh
  //
  TIntegral sinh(TIntegral arg) {
    return (value_d = (TIntegral)Integral::sinh(arg));
  }

  // method: sqrt
  //
  TIntegral sqrt() {
    return sqrt(value_d);
  }
   
  // method: sqrt
  //
  TIntegral sqrt(TIntegral arg) {
    return (value_d = presentValue(Integral::sqrt(arg)));
  }

  // method: square
  //
  TIntegral square() {
    return (value_d * value_d);
  }
   
  // method: square
  //
  TIntegral square(TIntegral arg) {
    return (value_d = arg * arg);
  }

  // method: tan
  //
  TIntegral tan() {
    return tan(value_d);
  }

  // method: tan
  //
  TIntegral tan(TIntegral arg) {
    return (value_d = (TIntegral)Integral::tan(arg));
  }

  // method: tanh
  //
  TIntegral tanh() {
    return tanh(value_d);
  }

  // method: tanh
  //
  TIntegral tanh(TIntegral arg) {
    return (value_d = (TIntegral)Integral::tanh(arg));
  }

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------

private:
  
  // friend functions:
  //  while friend functions are generally discouraged in the ISIP
  //  environment, they are used in the MScalar class to circumvent the
  //  template delayed compilation problem. 
  //
  friend class MScalarMethods;
};

  
// end of include file
//
#endif
