// file: $isip/class/algo/Math/math_05.cc
// version: $Id: math_05.cc 8037 2002-04-01 20:04:33Z gao $
//

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

// method: apply
//
// arguments:
//  Vector<AlgorithmData>& output: (output) output data
//  const Vector< CircularBuffer<AlgorithmData> >& input: (input) input data
//
// return: a boolean value indicating status
//
// this method selects the appropriate input and output data types
// and produces following output:
//  y = [a(0) * function(0)(X(0))] operation(0) [a(1) * function(1)(X(1))]  ..
//      ... + const,
//
boolean Math::apply(Vector<AlgorithmData>& output_a,
		    const Vector< CircularBuffer<AlgorithmData> >& input_a) {

  // set the length to the number of channels input
  //
  long len = input_a.length();
  output_a.setLength(len);

  // accumulate a result status -- if compute method returns false
  // this apply method should return false
  //  
  boolean res = true;

  // start the debugging output
  //
  displayStart(this);
  
  // loop over the channels and call the compute method
  //
  for (long c = 0; c < len; c++) {

    // display the channel number
    //
    displayChannel(c);
    
    // use output_a directly for this channel to be a AlgorithmData
    // type, and use input for this channel to be a vector <AlgorithmData>
    //          
    if (input_a(c)(0).getDataType() == AlgorithmData::COMBINATION) {
      res &= compute(output_a(c), input_a(c)(0).getCombination());
    }

    // check for single vector value
    else if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_FLOAT) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeVectorFloat().assign(input_a(c)(0).getVectorFloat());
      res &= compute(output_a(c), temp);
    }

    else if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_DOUBLE) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeVectorDouble().assign(input_a(c)(0).getVectorDouble());
      res &= compute(output_a(c), temp);
    }

    else if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_COMPLEX_FLOAT) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeVectorComplexFloat().assign(input_a(c)(0).getVectorComplexFloat());
      res &= compute(output_a(c), temp);
    }

    else if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeVectorComplexDouble().assign(input_a(c)(0).getVectorComplexDouble());
      res &= compute(output_a(c), temp);
    }

    else if (input_a(c)(0).getDataType() == AlgorithmData::MATRIX_FLOAT) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeMatrixFloat().assign(input_a(c)(0).getMatrixFloat());
      res &= compute(output_a(c), temp);
    }

    else if (input_a(c)(0).getDataType() == AlgorithmData::MATRIX_DOUBLE) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeMatrixDouble().assign(input_a(c)(0).getMatrixDouble());
      res &= compute(output_a(c), temp);
    }

    else if (input_a(c)(0).getDataType() == AlgorithmData::MATRIX_COMPLEX_FLOAT) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeMatrixComplexFloat().assign(input_a(c)(0).getMatrixComplexFloat());
      res &= compute(output_a(c), temp);
    }

    else if (input_a(c)(0).getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
      Vector <AlgorithmData> temp;
      temp.setLength(1);
      temp(0).makeMatrixComplexDouble().assign(input_a(c)(0).getMatrixComplexDouble());
      res &= compute(output_a(c), temp);
    }
 
    else {
      return Error::handle(name(), L"apply", ERR_UNSUPA, __FILE__, __LINE__);
    }
    
    // note that the output coefficient type is set in the compute
    // method since it has to use multiple inputs
    //
  }
  
  // finish the debugging output
  //
  displayFinish(this);
  
  // exit gracefully
  //
  return res;  
}

// method: compute
//
// arguments:
//  AlgorithmData& output: (output) output data
//  const Vector<AlgorithmData>& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel index
//
// return: a boolean value indicating status
//
// this method produces following output:
//
// y = [a(0) * function(0)(X(0))] operation(0) [a(1) * function(1)(X(1))]  ..
//      ... + const
//
// where,
//
//  function(i) = specified function (EXP, EXP2, LOG, ...)
//  operation(i) = specified operation (ADD, SUBTRACT, ...)
//  X(i) = input operand (matrix, vector or scalar)
//  a(i) = specified weight
//  const = constant additive term
//
boolean Math::compute(AlgorithmData& output_a,
		      const Vector<AlgorithmData>& input_a,
		      AlgorithmData::COEF_TYPE input_coef_type_a,
		      long index_a) {

  // declare local variables
  //
  boolean status = false;
  
  // branch on algorithm, call compute methods:
  //  function calculator algorithm
  //
  if (algorithm_d == FUNCTION_CALCULATOR) {
    status = computeFuncCalcEnumerate(output_a, input_a);
  }
  
  // determine the output type
  //
  AlgorithmData::COEF_TYPE ctype = input_a(0).getCoefType();

  for (long i = 1; i < input_a.length(); i++) {
    if (input_a(i).getCoefType() != ctype) {
      ctype = AlgorithmData::GENERIC;
      break;
    }
  }

  // set the output coefficient type
  //
  output_a.setCoefType(ctype);
  
  // possibly display the data
  //
  display(output_a, input_a, name());
  
  // exit gracefully
  //
  return status;
}


// method: computeFuncCalcEnumerate
//
// arguments:
//  AlgorithmData& output: (output) output data
//  const Vector<AlgorithmData>& input: (input) input data
//
// return: a boolean value indicating status
//
// this method implements following function:
//   Y = operation(0) {a(0)*function(0)(X(0))} operation(1)
//   {a(1)*function(1)(X(1))} operation(2) {a(2)*function(2)(X(2))}...
//   ... + const,
// where X(0), X(1), ... are the parts of input AlgorithmData splitted
// by number of operands. These can be VectorFloat or MatrixFloat. If
// user wants to implement this for scalar or single vector, then
// number of operands will be 1. For scalar input the length of the
// input vector will be 1.
//
boolean Math::computeFuncCalcEnumerate(AlgorithmData& output_a,
				       const Vector<AlgorithmData>& input_a) {

  // check algorithm and implementation names
  //
  if ((algorithm_d != FUNCTION_CALCULATOR) ||
      (implementation_d != ENUMERATE)) {
    return Error::handle(name(), L"computeFuncCalcEnumerate", Error::ARG,
                         __FILE__, __LINE__);
  }

  if (input_a(0).getDataType() == AlgorithmData::VECTOR_FLOAT ||
      input_a(0).getDataType() == AlgorithmData::MATRIX_FLOAT) {
    computeFuncCalcEnumerateFloat(output_a, input_a);
  }
  
  else if (input_a(0).getDataType() == AlgorithmData::VECTOR_DOUBLE ||
	   input_a(0).getDataType() == AlgorithmData::MATRIX_DOUBLE) {
    computeFuncCalcEnumerateDouble(output_a, input_a);
  }
  
  else if (input_a(0).getDataType() == AlgorithmData::VECTOR_COMPLEX_FLOAT ||
	   input_a(0).getDataType() == AlgorithmData::MATRIX_COMPLEX_FLOAT) {
    computeFuncCalcEnumerateCFloat(output_a, input_a);
  }
  
  else if (input_a(0).getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE ||
	   input_a(0).getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
    computeFuncCalcEnumerateCDouble(output_a, input_a);
  }
  
  else {
    return Error::handle(name(), L"computeFuncCalcEnumerate",
			 ERR_FNDTYP, __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;
}


























