// file: $isip/class/algo/Math/math_10.cc
// version: $Id: math_10.cc 8283 2002-07-03 21:08:37Z picone $
//

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

// method: computeFuncCalcEnumerateCDouble
//
// 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 VectorComplexDouble splitted
// by number of operands. These can be VectorComplexDouble. 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::computeFuncCalcEnumerateCDouble(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"computeFuncCalcEnumerateCDouble", Error::ARG,
                         __FILE__, __LINE__);
  }

  // compute the number of combinations
  //
  if (num_operands_d != input_a.length()) {
    return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			 Error::ARG, __FILE__, __LINE__);
  }

  // input should be either vector or matrix
  //
  for (long j = 0; j < num_operands_d; j++) {
    if ((input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) &&
	(input_a(j).getDataType() != AlgorithmData::MATRIX_COMPLEX_DOUBLE)) {
      return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			   Error::ARG, __FILE__, __LINE__);
    }
  }

  // special case for one complex operation such as mag, real and imag
  //
  if (num_operands_d == (long)1) {
    if (function_d(0) == (long)MAG) {
      input_a(0).getVectorComplexDouble().mag(output_a.makeVectorDouble());
      output_a.getVectorDouble().add((double)const_d);
      return true;
    }
    else if (function_d(0) == (long)REAL) {
      input_a(0).getVectorComplexDouble().real(output_a.makeVectorDouble());
      output_a.getVectorDouble().add((double)const_d);
      return true;
    }
    else if (function_d(0) == (long)IMAG) {
      input_a(0).getVectorComplexDouble().imag(output_a.makeVectorDouble());
      output_a.getVectorDouble().add((double)const_d);
      return true;
    }
  }
  
  // set the mode of output
  //
  if (input_a(0).getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
    output_a.makeVectorComplexDouble();
  }
  else {
    output_a.makeMatrixComplexDouble();
  }

  // loop over the number of input operands
  //
  for (long j = 0; j < num_operands_d; j++) {
    
    // declare temporary input variable
    //
    AlgorithmData tmp_input;
    
    // set the type to either matrix or vector, depending upon the
    // type of the input
    //
    if (input_a(j).getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
      tmp_input.makeVectorComplexDouble().assign(input_a(j).getVectorComplexDouble());
    }
    else {
      tmp_input.makeMatrixComplexDouble().assign(input_a(j).getMatrixComplexDouble());
    }

    // branch on function:
    //  apply function and corresponding weight
    //
    if (function_d(j) == (long)IDENTITY) {
      
      // do nothing
      //
    }
    
    // function: EXP
    //
    else if (function_d(j) == (long)EXP) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().exp();
    }
    
    // function: EXP2
    //    
    else if (function_d(j) == (long)EXP2) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().exp2();
    }
    
    // function: EXP10
    //    
    else if (function_d(j) == (long)EXP10) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().exp10();
    }
    
    // function: FACTORIAL
    //    
    else if (function_d(j) == (long)FACTORIAL) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().factorial();
    }
    
    // function: LOG
    //        
    else if (function_d(j) == (long)LOG) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
  
      // check vector elements
      //
      for (long index =  tmp_input.getVectorComplexDouble().length() - 1;
	   index >= 0; index--) {
	if (tmp_input.getVectorComplexDouble()(index) <= (double)0.0 ) {
	  return Error::handle(name(), L"computeFuncCalcEnumerateCDouble", Error::ARG, __FILE__, __LINE__);
	}
      }
      tmp_input.getVectorComplexDouble().log();
    }
    
    // function: LOG2
    //            
    else if (function_d(j) == (long)LOG2) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      
      // check vector elements
      //
      for (long index =  tmp_input.getVectorComplexDouble().length() - 1;
	   index >= 0; index--) {
	if (tmp_input.getVectorComplexDouble()(index) <= (double)0.0 ) {
	  return Error::handle(name(), L"computeFuncCalcEnumerateCDouble", Error::ARG, __FILE__, __LINE__);
	}
      }
      tmp_input.getVectorComplexDouble().log2();
    }
    
    // function: LOG10
    //            
    else if (function_d(j) == (long)LOG10) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      
      // check vector elements
      //
      for (long index =  tmp_input.getVectorComplexDouble().length() - 1;
	   index >= 0; index--) {
	if (tmp_input.getVectorComplexDouble()(index) <= (double)0.0 ) {
	  return Error::handle(name(), L"computeFuncCalcEnumerateCDouble", Error::ARG, __FILE__, __LINE__);
	}
      }
      tmp_input.getVectorComplexDouble().log10();
    }
    
    // function: LOG1P
    //            
    else if (function_d(j) == (long)LOG1P) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      
      // check vector elements
      //
      for (long index =  tmp_input.getVectorComplexDouble().length() - 1;
	   index >= 0; index--) {
	if (tmp_input.getVectorComplexDouble()(index) <= (double)0.0 ) {
	  return Error::handle(name(), L"computeFuncCalcEnumerateCDouble", Error::ARG, __FILE__, __LINE__);
	}
      }
      tmp_input.getVectorComplexDouble().log1p();
    }
    
    // function: ABS
    //            
    else if (function_d(j) == (long)ABS) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			   ERR_UNKFUN, __FILE__, __LINE__);
    }
    
    // function: NEG
    //            
    else if (function_d(j) == (long)NEG) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	tmp_input.getVectorComplexDouble().neg();
      }
      else if (input_a(j).getDataType() != AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
	tmp_input.getMatrixComplexDouble().neg();
      }
      else {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     Error::ARG, __FILE__, __LINE__);
      }
    }
    
    // function: ROUND
    //                 
    else if (function_d(j) == (long)ROUND) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().round();
    }
    
    // function: CEIL
    //                
    else if (function_d(j) == (long)CEIL) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().ceil();
    }
    
    // function: FLOOR
    //                    
    else if (function_d(j) == (long)FLOOR) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().floor();
    }
    
    // function: RFLOOR
    //                        
    else if (function_d(j) == (long)RFLOOR) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().rfloor();
    }
    
    // function: SIN
    //                        
    else if (function_d(j) == (long)SIN) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().sin();
    }
    
    // function: COS
    //                            
    else if (function_d(j) == (long)COS) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().cos();
    }

    // function: TAN
    //                            
    else if (function_d(j) == (long)TAN) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().tan();
    }
    
    // function: ASIN
    //                            
    else if (function_d(j) == (long)ASIN) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().asin();
    }
    
    // function: ACOS
    //                            
    else if (function_d(j) == (long)ACOS) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().acos();
    }
    
    // function: ATAN
    //                            
    else if (function_d(j) == (long)ATAN) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().atan();
    }
    
    // function: SQUARE
    //                            
    else if (function_d(j) == (long)SQUARE) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().square();
    }
    
    // function: SQRT
    //                            
    else if (function_d(j) == (long)SQRT) {
      if (input_a(j).getDataType() != AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getVectorComplexDouble().sqrt();
    }
    
    // function: INVERSE
    //                            
    else if (function_d(j) == (long)INVERSE) {
      if (input_a(j).getDataType() != AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getMatrixComplexDouble().inverse();
    }
    
    // function: TRANSPOSE
    //                            
    else if (function_d(j) == (long)TRANSPOSE) {
      if (input_a(j).getDataType() != AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_FNDTYP, __FILE__, __LINE__);
      }
      tmp_input.getMatrixComplexDouble().transpose();
    }
    
    // invalid function
    //
    else {
      return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			   ERR_UNKFUN, __FILE__, __LINE__);
    }
    
    // apply weight
    //
    if (input_a(j).getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
      tmp_input.getVectorComplexDouble().mult(complexdouble(weight_d(j)));
    }
    else if (input_a(j).getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
      tmp_input.getMatrixComplexDouble().mult(complexdouble(weight_d(j)));
    }
    else {
      return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			   Error::ARG, __FILE__, __LINE__);
    }

    // branch on operation:
    //  Operation: ASSIGN
    //
    if (operation_d(j) == (long)ASSIGN) {
      
      // this operation is possible between same data types
      //
      if (output_a.getDataType() != input_a(j).getDataType()) {
	return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
      if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
	output_a.makeVectorComplexDouble().assign(tmp_input.getVectorComplexDouble());
      }
      else {
	output_a.makeMatrixComplexDouble().assign(tmp_input.getMatrixComplexDouble());
      }
    }
    
    //  Operation: ADD
    //
    else if (operation_d(j) == (long)ADD) {
      
      computeAddCDouble(output_a, tmp_input);
    }

    //  Operation: SUBTRACT
    //
    else if (operation_d(j) == (long)SUBTRACT) {
      
      computeSubCDouble(output_a, tmp_input);
    }

    //  Operation: MULTIPLY
    //
    else if (operation_d(j) == (long)MULTIPLY) {

      computeMultCDouble(output_a, tmp_input);
    }
    
    //  Operation: DIVIDE
    //
    else if (operation_d(j) == (long)DIVIDE) {
      
      computeDivCDouble(output_a, tmp_input);
    }
    
    // invalid operation
    //
    else {
      return Error::handle(name(), L"computeFuncCalcEnumerateCDouble",
			   ERR_UNKOPE, __FILE__, __LINE__);
    }
  }

  // add the constant term
  //
  if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
    output_a.getVectorComplexDouble().add((complexdouble)const_d);
  }
  else {
    output_a.getMatrixComplexDouble().add((complexdouble)const_d);
  }
  
  // exit gracefully
  //
  return true;
}

// method: computeAddCDouble
//
// arguments:
//  AlgorithmData& output: (input/output) 1st operand and output
//  const AlgorithmData& input: (input) 2nd operand
//
// return: a boolean value indicating status
//
boolean Math::computeAddCDouble(AlgorithmData& output_a,
				const AlgorithmData& input_a) const {
  
  if (input_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
    
    long ilen = input_a.getVectorComplexDouble().length();

    if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {

      long olen = output_a.getVectorComplexDouble().length();
      
      // if either input is of length 1, perform scalar addition
      //
      if (ilen == 1) {
	complexdouble scalar = input_a.getVectorComplexDouble()(0);
	return output_a.getVectorComplexDouble().add(scalar);
      }
      else if (olen == 1) {
	complexdouble scalar = output_a.getVectorComplexDouble()(0);
	return output_a.getVectorComplexDouble().add(input_a.getVectorComplexDouble(), scalar);
      }
      
      // if they are the same length, do vector addition
      //
      else if (ilen == olen) {
	return output_a.getVectorComplexDouble().add(input_a.getVectorComplexDouble());
      }

      // else error
      //
      else {
	return Error::handle(name(), L"computeAddCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }

    // 1 vector, 1 matrix. if the vector is of length 1, treat it as a
    // scalar
    //
    else if (output_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {

      if (ilen == 1) {
	complexdouble scalar = input_a.getVectorComplexDouble()(0);
	return output_a.getMatrixComplexDouble().add(scalar);
      }
      else {
	return Error::handle(name(), L"computeAddCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }
  }

  else if (input_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {

    // 1 vector, 1 matrix. if the vector is of length 1, treat it as a
    // scalar
    //
    if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
      long olen = output_a.getVectorComplexDouble().length();
      if (olen == 1) {
	complexdouble scalar = output_a.getVectorComplexDouble()(0);
	return output_a.makeMatrixComplexDouble().add(input_a.getMatrixComplexDouble(), scalar);
      }
      else {
	return Error::handle(name(), L"computeAddCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }

    // if both are matrices, just add them
    //
    else if (output_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
      return output_a.getMatrixComplexDouble().add(input_a.getMatrixComplexDouble());
    }
  }

  // exit gracefully
  //
  return true;
}


// method: computeSubCDouble
//
// arguments:
//  AlgorithmData& output: (input/output) 1st operand and output
//  const AlgorithmData& input: (input) 2nd operand
//
// return: a boolean value indicating status
//
boolean Math::computeSubCDouble(AlgorithmData& output_a,
				const AlgorithmData& input_a) const {
  
  if (input_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
    
    long ilen = input_a.getVectorComplexDouble().length();

    if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {

      long olen = output_a.getVectorComplexDouble().length();
      
      // if either input is of length 1, perform scalar subtraction
      //
      if (ilen == 1) {
	complexdouble scalar = input_a.getVectorComplexDouble()(0);
	return output_a.getVectorComplexDouble().sub(scalar);
      }
      else if (olen == 1) {

	// note this is different from addition since the operation is
	// not cummative
	//
	complexdouble scalar = output_a.getVectorComplexDouble()(0);
	output_a.getVectorComplexDouble().setLength(ilen);
	output_a.getVectorComplexDouble().assign(scalar);
	return output_a.getVectorComplexDouble().sub(input_a.getVectorComplexDouble());
      }
      
      // if they are the same length, do vector subtraction
      //
      else if (ilen == olen) {
	return output_a.getVectorComplexDouble().sub(input_a.getVectorComplexDouble());
      }

      // else error
      //
      else {
	return Error::handle(name(), L"computeSubCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }

    // 1 vector, 1 matrix. if the vector is of length 1, treat it as a
    // scalar
    //
    else if (output_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {

      if (ilen == 1) {
	complexdouble scalar = input_a.getVectorComplexDouble()(0);
	return output_a.getMatrixComplexDouble().sub(scalar);
      }
      else {
	return Error::handle(name(), L"computeSubCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }
  }

  else if (input_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {

    // 1 vector, 1 matrix. if the vector is of length 1, treat it as a
    // scalar
    //
    if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
      long olen = output_a.getVectorComplexDouble().length();
      if (olen == 1) {

	// note this is different from addition since the operation is
	// not cummative
	//
	complexdouble scalar = output_a.getVectorComplexDouble()(0);
	output_a.makeMatrixComplexDouble().setDimensions(input_a.getMatrixComplexDouble());
	output_a.getMatrixComplexDouble().assign(scalar);
	return output_a.getMatrixComplexDouble().sub(input_a.getMatrixComplexDouble());
      }
      else {
	return Error::handle(name(), L"computeSubCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }

    // if both are matrices, just subtract them
    //
    else if (output_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {
      return output_a.getMatrixComplexDouble().sub(input_a.getMatrixComplexDouble());
    }
  }

  // exit gracefully
  //
  return true;
}

// method: computeMultCDouble
//
// arguments:
//  AlgorithmData& output: (input/output) 1st operand and output
//  const AlgorithmData& input: (input) 2nd operand
//
// return: a boolean value indicating status
//
boolean Math::computeMultCDouble(AlgorithmData& output_a,
				 const AlgorithmData& input_a) const {
  
  if (input_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {
    
    long ilen = input_a.getVectorComplexDouble().length();

    if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {

      long olen = output_a.getVectorComplexDouble().length();

      // if either input is of length 1, perform scalar multiplication
      //
      if (ilen == 1) {
	complexdouble scalar = input_a.getVectorComplexDouble()(0);
	return output_a.getVectorComplexDouble().mult(scalar);
      }
      else if (olen == 1) {

	complexdouble scalar = output_a.getVectorComplexDouble()(0);
	return output_a.getVectorComplexDouble().mult(input_a.getVectorComplexDouble(), scalar);
      }
      
      // if they are the same length, do vector multiplication
      //
      else if (ilen == olen) {
	return output_a.getVectorComplexDouble().mult(input_a.getVectorComplexDouble());
      }

      // else error
      //
      else {
	return Error::handle(name(), L"computeMultCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }
    else if (output_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {

      // if the vector is length 1, do scalar multiplication
      //
      if (ilen == 1) {
	complexdouble scalar = input_a.getVectorComplexDouble()(0);
	return output_a.getMatrixComplexDouble().mult(scalar);
      }

      // if the vector is the correct length for multv, do it
      //
      else if (ilen == output_a.getMatrixComplexDouble().getNumColumns()) {
	VectorComplexDouble tmp_out;
	output_a.getMatrixComplexDouble().multv(tmp_out, input_a.getVectorComplexDouble());
	output_a.makeVectorComplexDouble().assign(tmp_out);
      }

      // else error
      //
      else {
	return Error::handle(name(), L"computeMultCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }
  }

  // input is a matrix
  //
  else if (input_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {

    if (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) {

      long olen = output_a.getVectorComplexDouble().length();

      // if the vector is of length 1, do scalar multiplication
      //
      if (olen == 1) {
	complexdouble scalar = output_a.getVectorComplexDouble()(0);
	output_a.makeMatrixComplexDouble().assign(input_a.getMatrixComplexDouble());
	return output_a.getMatrixComplexDouble().mult(scalar);
      }

      // if the vector is the correct length for vmult, do it
      //
      else if (olen == input_a.getMatrixComplexDouble().getNumRows()) {

	VectorComplexDouble tmp_out;
	input_a.getMatrixComplexDouble().vmult(tmp_out, output_a.getVectorComplexDouble());
	output_a.getVectorComplexDouble().assign(tmp_out);
      }
	
      // else error
      //
      else {
	return Error::handle(name(), L"computeMultCDouble",
			     ERR_MATCH, __FILE__, __LINE__);
      }
    }
    else if (output_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE) {

      return output_a.getMatrixComplexDouble().mult(input_a.getMatrixComplexDouble());
    }
  }

  // exit gracefully
  //
  return true;
}

// method: computeDivCDouble
//
// arguments:
//  AlgorithmData& output: (input/output) 1st operand and output
//  const AlgorithmData& input: (input) 2nd operand
//
// return: a boolean value indicating status
//
boolean Math::computeDivCDouble(AlgorithmData& output_a,
				const AlgorithmData& input_a) const {    
  
  // vector-matrix division is possible only if length of the
  // vector is 1. no other matrix division is possible
  //
  if ((input_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) &&
      (output_a.getDataType() == AlgorithmData::MATRIX_COMPLEX_DOUBLE)) {      

    if (input_a.getVectorComplexDouble().length() > 1) {
      return Error::handle(name(), L"computeDivCDouble",
			   ERR_MATCH, __FILE__, __LINE__);
    }
    VectorComplexDouble vec(input_a.getVectorComplexDouble());
    complexdouble scalar = vec(0);
    output_a.getMatrixComplexDouble().div(scalar);
  }

  // both inputs are vectors
  //
  else if ((input_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE) &&
	   (output_a.getDataType() == AlgorithmData::VECTOR_COMPLEX_DOUBLE)) {

    // if the input is of length 1, do scalar division
    //
    if (input_a.getVectorComplexDouble().length() == 1) {
      complexdouble scalar = input_a.getVectorComplexDouble()(0);

      return output_a.getVectorComplexDouble().div(scalar);
    }

    // if the lengths are equal, do element-wise division
    //
    else if (input_a.getVectorComplexDouble().length() == output_a.getVectorComplexDouble().length()) {

      return output_a.getVectorComplexDouble().div(input_a.getVectorComplexDouble());

    } 
    else {
      return Error::handle(name(), L"computeDivCDouble",
			   ERR_MATCH, __FILE__, __LINE__);
    }
  }
  else {
    return Error::handle(name(), L"computeDivCDouble",
			 ERR_MATCH, __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;
}
