// file: $isip/class/algo/Calculus/calc_00.cc
// version: $Id: calc_00.cc 8165 2002-05-31 21:57:33Z picone $
//

// isip include files
//
#include "Calculus.h"

//------------------------------------------------------------------------
//
// required public methods
//
//-----------------------------------------------------------------------

// method: assign
//
// arguments:
//  const Calculus& arg: (input) object to be assigned
//
// return: a boolean value indicating status
//
// this method assigns the input object to the current object
//
boolean Calculus::assign(const Calculus& arg_a) {

  // assign common data
  //
  algorithm_d = arg_a.algorithm_d;
  implementation_d = arg_a.implementation_d;
  order_d = arg_a.order_d;

  // check common algorithms: differentiation
  //
  if (arg_a.algorithm_d == DIFFERENTIATION) {

    // check implementation: regression, central_difference,
    //  backward_difference
    //
    if ((arg_a.implementation_d == REGRESSION) ||
	(arg_a.implementation_d == CENTRAL_DIFFERENCE) ||
	(arg_a.implementation_d == BACKWARD_DIFFERENCE)) {

      // assign all protected data
      //
      delta_win_d = arg_a.delta_win_d;
    }

    // check implementation: unknown
    //
    else {
      return Error::handle(name(), L"assign",
			   ERR_UNKIMP, __FILE__, __LINE__);
    }

    // copy the base class
    //
    return AlgorithmBase::assign(arg_a);
  }

  // check common algorithms: integration
  //
  else if (arg_a.algorithm_d == INTEGRATION) {
    return Error::handle(name(), L"assign",
			 ERR_UNSUPA, __FILE__, __LINE__);
  }  

  // check common algorithms: unknown
  //
  else {
    return Error::handle(name(), L"assign",
			 ERR_UNKALG, __FILE__, __LINE__);
  }  
}

// method: eq
//
// arguments:
//  const Calculus& arg: (input) object to be compared
//
// return: a boolean value indicating status
//
// this method checks whether the current object is identical to the
// input object
//
boolean Calculus::eq(const Calculus& arg_a) const {

  // check the algorithm
  //
  if (algorithm_d != arg_a.algorithm_d) {
    return false;
  }

  // check parameters common to all algorithms
  //
  else if (implementation_d != arg_a.implementation_d) {
    return false;
  }
  else if (order_d != arg_a.order_d) {
    return false;
  }

  // check specific algorithms: differentiation
  //
  else if (algorithm_d == DIFFERENTIATION) {

    // check specific implementations
    //
    if ((implementation_d == REGRESSION) ||
	(implementation_d == CENTRAL_DIFFERENCE) ||
	(implementation_d == BACKWARD_DIFFERENCE)) {
      if (delta_win_d != arg_a.delta_win_d) {
	return false;
      }
    }
    else {
      return Error::handle(name(), L"eq",
			 ERR_UNKIMP, __FILE__, __LINE__);
    }
  }

  // check specific algorithms: integration
  //
  else if (algorithm_d == INTEGRATION) {
    return Error::handle(name(), L"eq",
			 ERR_UNSUPA, __FILE__, __LINE__);
  }

  // check specific algorithms: unknown
  //
  else {
    return Error::handle(name(), L"eq",
			 ERR_UNKALG, __FILE__, __LINE__);
  }

  // exit gracefully by checking the base class
  //
  return AlgorithmBase::eq(arg_a);
}

// method: clear
//
// arguments:
//  Integral::CMODE ctype: (input) clear mode
//
// return: a boolean value indicating status
//
// this method resets the data members to the default values
//
boolean Calculus::clear(Integral::CMODE ctype_a) {

  // reset all protected data
  //
  if (ctype_a != Integral::RETAIN) {
    algorithm_d = DEF_ALGORITHM;
    implementation_d = DEF_IMPLEMENTATION;
    order_d = DEF_ORDER;
    delta_win_d = DEF_DELTAWIN;
  }
  
  // make the status invalid
  //
  is_valid_d = false;

  // call the base clear method
  //
  return AlgorithmBase::clear(ctype_a);
}

//---------------------------------------------------------------------------
//
// class-specific public methods:
//  public methods required by the AlgorithmBase interface contract
//
//---------------------------------------------------------------------------

// method: assign
//
// arguments:
//  const AlgorithmBase& arg: (input) object to be assigned
//
// return: a boolean value indicating status
//
// this method assigns the input algorithm object to the current
// Calculus object, if the input algorithm object is not a Calculus
// object, it returns an error
//
boolean Calculus::assign(const AlgorithmBase& arg_a) {

  // case: input is a Calculus object
  //
  if (typeid(arg_a) == typeid(Calculus)) {
    return assign((Calculus&)arg_a);
  }

  // case: other
  //  if the input algorithm object is not a Calculus object, error
  //
  else {
    return Error::handle(name(), L"assign", Error::ARG, __FILE__, __LINE__);
  }
}

// method: eq
//
// arguments:
//  const AlgorithmBase& arg: (input) object to be compared
//
// return: a boolean value indicating status
//
// this method checks whether the current Calculus object is identical
// to the input algorithm object, if the input algorithm object is not
// a Calculus object, it returns an error
//
boolean Calculus::eq(const AlgorithmBase& arg_a) const {

  // case: input is a Calculus object
  //
  if (typeid(arg_a) == typeid(Calculus)) {
    return eq((Calculus&)arg_a);
  }

  // case: other
  //  if the input algorithm object is not a Calculus object, error
  //
  else {
    return Error::handle(name(), L"eq", Error::ARG, __FILE__, __LINE__);
  }
}

// method: init
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method precomputes various constants used in this class
// 
boolean Calculus::init() {

  // check algorithm: differentiation
  //
  if (algorithm_d == DIFFERENTIATION) {

    // check implementation: regression
    //
    if (implementation_d == REGRESSION) {

      // check the order: we currently only support a first-order regression
      //
      if (order_d != (long)1) {
	return Error::handle(name(), L"init", ERR_ORDER,
			     __FILE__, __LINE__);
      }
  
      // compute the number of elements used in the calculation
      //
      num_elements_d = 2 * delta_win_d + 1;
      offset_d = -num_elements_d / 2;

      // compute the denominator of the regression formula
      //
      denom_d = 0.0;
      long N = (long)delta_win_d;
      for (long i = 1; i <= N; i++) {
	denom_d += i*i;
      }
      denom_d *= 2.0;

      // invert the value
      //
      if (denom_d != (double)0.0) {
	denom_d = 1.0 / denom_d;
      }
      else {
	return Error::handle(name(), L"init", ERR, __FILE__, __LINE__,
			     Error::WARNING);
      }
    }      

    // check implementation: central difference
    //
    else if (implementation_d == CENTRAL_DIFFERENCE) {

      // check the order: we currently only support a first-order difference
      //
      if (order_d != (long)1) {
	return Error::handle(name(), L"init", ERR_ORDER,
			     __FILE__, __LINE__);
      }
  
      // compute the number of elements used in the calculation
      //
      num_elements_d = 2 * delta_win_d + 1;
      offset_d = -num_elements_d / 2;

      // compute the denominator of the difference
      //
      denom_d = 1.0 / (2.0 * (double)delta_win_d);
    }

    // check implementation: backward difference
    //
    else if (implementation_d == BACKWARD_DIFFERENCE) {

      // check the order: we currently only support a first-order difference
      //
      if (order_d != (long)1) {
	return Error::handle(name(), L"init", ERR_ORDER,
			     __FILE__, __LINE__);
      }
  
      // compute the number of elements used in the calculation
      //
      num_elements_d = delta_win_d + (long)1;
      offset_d = -delta_win_d;

      // compute the denominator of the difference
      //
      denom_d = 1.0 / (double)delta_win_d;
    }

    // check implementation: unknown
    //
    else {
      return Error::handle(name(), L"init",
			   ERR_UNKIMP, __FILE__, __LINE__);
    }
  }

  // check algorithm: integration
  //
  else if (algorithm_d == INTEGRATION) {
    return Error::handle(name(), L"init",
			 ERR_UNSUPA, __FILE__, __LINE__);
  }  

  // check algorithm: unknown
  //
  else {
    return Error::handle(name(), L"init",
			 ERR_UNKALG, __FILE__, __LINE__);
  }  
  
  // make the status valid
  //
  is_valid_d = true;

  // exit gracefully
  //
  return true;
}

//-----------------------------------------------------------------------------
//
// we define non-integral constants in the default constructor
//      
//-----------------------------------------------------------------------------

// constants: class name
//
const String Calculus::CLASS_NAME(L"Calculus");

// constants: i/o related constants
//
const String Calculus::DEF_PARAM(L"");
const String Calculus::PARAM_ALGORITHM(L"algorithm");
const String Calculus::PARAM_IMPLEMENTATION(L"implementation");
const String Calculus::PARAM_CMODE(L"compute_mode");
const String Calculus::PARAM_ORDER(L"order");
const String Calculus::PARAM_DELTAWIN(L"delta_win");

// constants: NameMap(s) for the enumerated values
//
const NameMap Calculus::ALGO_MAP(L"DIFFERENTIATION, INTEGRATION");
const NameMap Calculus::IMPL_MAP(L"REGRESSION, CENTRAL_DIFFERENCE, BACKWARD_DIFFERENCE");

// static instantiations: memory manager
//
MemoryManager Calculus::mgr_d(sizeof(Calculus), Calculus::name());
