/*
 * Example of a user-defined optimization method.
 *
 * The routine ascfitusersearchmethod_ is used to fid the minima of the
 * function func_(vec, nvec), where vec is a vector
 * of nvec entries which should stay between xvlo[] and xvhi[]
 * xvec[] are the starting values.
 *
 * The code below is presented for educative purposes and should
 * NOT be used for scientific analysis.
 */

#include <stdio.h>
#include <stdlib.h>

/* 
 * The routine to be minimized is callable using the symbol func_.
 * Do not change this declaration.
 */
extern double func_(double* xvec, int* nvec);

/* prototype */
void ascfitusersearchmethod_(double* xvlo, 
			     double* xvec, 
			     double* xvhi, 
			     int* nvec);

/* definition */
void ascfitusersearchmethod_(double* xvlo, 
			     double* xvec, 
			     double* xvhi, 
			     int* nvec)
{  
  int i;
  double newFunctionValue;

  /* Find the starting value */
  double functionValue = func_(xvec, nvec);

  /* then decide how you want to vary the parameters
   allocate a vector to hold the steps */
  double* deltas = (double*)(malloc(*nvec * sizeof(double)));
  /* set step size to 1/100 of range */
  for(i = 0; i < *nvec; i++)
    deltas[i] = (xvhi[i] - xvlo[i])/100;

  newFunctionValue = functionValue;

  /* go through the parameters one by one and try both adding and
   subtracting delta. Set the parameter to the value that gives the best
   function value. Do this for all parameters until there are no
   further improvements. */
  do{
    functionValue = newFunctionValue;
    for(i = 0; i < *nvec; i++) {
      double lowValue, highValue;
      if(xvec[i] + deltas[i] > xvhi[i]) // keep within ranges
	xvec[i] -= deltas[i];
      if(xvec[i] - deltas[i] < xvlo[i]) // keep within ranges
	xvec[i] += deltas[i];

      // try varying downwards
      xvec[i] -= deltas[i];
      lowValue = func_(xvec, nvec);

      // try varying upwards
      xvec[i] += 2*deltas[i];   // vary first parameter
      highValue = func_(xvec, nvec);

      if((lowValue < newFunctionValue) && (lowValue < highValue)){
	xvec[i] -= 2*deltas[i];
	newFunctionValue = lowValue;
	}
      else 
	if(highValue < newFunctionValue)
	  newFunctionValue = highValue;
	else  // the old value (newFunctionValue) is still the best, keep it
	  xvec[i] -= deltas[i];
    }
  }while(newFunctionValue != functionValue);

  free(deltas);
}
