/* Copyright Dassault Systemes, 1999, 2010 */

/*=========================================================================*\
 *
 * This example file shows an implementation of Hooke-Jeeves optimizer
 * as an external executable program integrated into Isight.
 *
 * All communication between Isight and this executable program is done
 * via writing and reading files.
 *
\*=========================================================================*/

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

/* 
 * remove this #define statement if you are 
 * compiling on non-Win32 platform
 */
#define WIN32

#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

#define NUM_COMMAND_LINE_ARGS 6
#define DV_TYPE_REAL 0
#define DV_TYPE_INTEGER 1
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
#define LOG_FILE_NAME "Hooke-Jeeves-log-file.txt"

static int     numDesignVariables        = 0;
static int     numConstraints            = 0;
static int     numEqConstraints          = 0;
static char**  designVariableNames       = NULL;
static double* designVariableValues      = NULL;
static double* designVariableLowerBounds = NULL;
static double* designVariableUpperBounds = NULL;
static int*    designVariableTypes       = NULL;

static char*   goRunSignalFileName       = NULL;
static char*   inputValsFileName         = NULL;
static char*   resultsFileName           = NULL;
static FILE*   logFile                   = NULL;

#define MAX_NUM_TECHNIQUE_OPTIONS 15
static double  techniqueOptions[MAX_NUM_TECHNIQUE_OPTIONS];
static char techniqueOptionNames[MAX_NUM_TECHNIQUE_OPTIONS][128];

static int funevals = 0;
#define DEFAULT_BOUND 1e15

/*=========================================================================*\
 * Utility function that calls calloc() and in case of memory
 * allocation failure prints a message to stderr and exits the program.
\*=========================================================================*/
static void * allocateArray(size_t nElem, size_t elemSize, char* arrayName) {
    void * memPointer = calloc(nElem, elemSize);
    if(memPointer == NULL) {
        fprintf(stderr, "Failed to allocate memory for %s\n", arrayName);
        exit(1);
    }
    return memPointer;
}

/*=========================================================================*\
 * This function reads technique options from a file.
 * The format of the technique options file is as follows:
 *
 *     -------------------
 *    |Val_1 OptionName_1 |
 *    |Val_2 OptionName_2 |
 *    |...                |
 *     -------------------
 *
 * NOTE! the order of technique options in the file is not guaranteed to
 * match the displayed order in Isight. When using the values of the
 * technique options, always check the name of the option.
 *
\*=========================================================================*/
static void readTechniqueOptionsFile(char* fName) {

    FILE* f;
    int i;

    /* open the file for reading */
    if((f = fopen(fName, "r")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for reading\n", fName);
        exit(1);
    }

    fprintf(logFile,"Reading TECHNIQUE OPTIONS from \"%s\"\n",fName); fflush(logFile);

    /* read the values and names, store in the global arrays */
    for(i=0; i<MAX_NUM_TECHNIQUE_OPTIONS; i++) {
    if(feof(f)) {
        break;
    }
        if(fscanf(f,"%lf %[^\n]\n", &techniqueOptions[i],techniqueOptionNames[i]) != 2) {
            fprintf(stderr,"Invalid file format, \"%s\", line=%d\n",fName,i+1);
            exit(1);
        }
        fprintf(logFile," %d. %s = %lg\n", i+1, techniqueOptionNames[i], techniqueOptions[i]); fflush(logFile);
    }
}

/*=========================================================================*\
 * This function reads problem setup from a file.
 * The format of the problem setup file is as follows. The first 3 lines
 * are the sizing parameters (# of design variables, # of constraints (all),
 * and # of equality constraints. The rest of the file - design variable
 * definitions, one line for each design variable:
 *
 *     -------------------------------------------- 
 *    |n1 Number of design variables               |
 *    |n2 Number of constraints (total)            |
 *    |n3 Number of equality (target) constraints  |
 *    |Val_1 LB_1 UB_1 Type_1 VariableName_1       |
 *    |Val_2 LB_2 UB_2 Type_2 VariableName_2       |
 *    |...                                         |
 *     -------------------------------------------- 
 *
 *     where
 *       Val_1 - starting value of design variable #1 (DV#1)
 *       LB_1 and UB_1 - lower/upper bounds of DV#1
 *       Type_1 - value type of DV#1 (0 - real, 1 - integer)
 *       VariableName_1 - name of DV#1 (need not be used)
 *
\*=========================================================================*/
static void readProblemSetupFile(char* fName) {

    FILE* f;
    char strBuffer[256];
    int i;
    int numVals;

    /* open the file for reading */
    if((f = fopen(fName, "r")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for reading\n", fName);
        exit(1);
    }

    /* read sizing parameters first, lines 1,2,3 */
    fprintf(logFile,"Reading PROBLEM SETUP data from \"%s\"\n",fName); 
    fflush(logFile);
    numVals = fscanf(f, "%d %*[^\n]\n %d %*[^\n]\n %d %*[^\n]\n", 
            &numDesignVariables, &numConstraints, &numEqConstraints);
    if(numVals != 3) {
        fprintf(stderr,"Invalid file format, \"%s\", " 
            "lines 1-3 must have problem sizing parameters (integer values)\n",
            fName);
        exit(1);
    }

    /* allocate global arrays for storing design variable and constraint data */
    designVariableNames = (char**)allocateArray(numDesignVariables, sizeof(char*), 
            "design variable names array");
    for(i=0; i<numDesignVariables; i++) {
        designVariableNames[i] = (char*)allocateArray(128, sizeof(char), 
                "design variable names array");
    }
    designVariableValues = (double*)allocateArray(numDesignVariables, sizeof(double), 
            "design variable values array");
    designVariableLowerBounds = (double*)allocateArray(numDesignVariables, sizeof(double), 
            "design variable lower bounds array");
    designVariableUpperBounds = (double*)allocateArray(numDesignVariables, sizeof(double), 
            "design variable upper bounds array");
    designVariableTypes = (int*)allocateArray(numDesignVariables, sizeof(int), 
            "design variable types array");

    /* read data into global arrays */
    for(i=0; i<numDesignVariables; i++) {
        numVals = fscanf(f, "%lf %lf %lf %d %[^\n]\n",
            &designVariableValues[i],&designVariableLowerBounds[i],
            &designVariableUpperBounds[i],&designVariableTypes[i],
            designVariableNames[i]);
        if(numVals < 5) {
            fprintf(stderr,"Invalid file format, \"%s\":%d\n",fName,i+1);
            exit(1);
        }
        fprintf(logFile,"%d. \"%s\" = %lg, [%lg...%lg] type=%d\n", i+1, designVariableNames[i],
                designVariableValues[i], designVariableLowerBounds[i], 
                designVariableUpperBounds[i],designVariableTypes[i]); 
    fflush(logFile);
    }

    /* close the file */
    fclose(f);
}

/*=========================================================================*\
 *
 * This routine runs a single design point analysis; constraint and 
 * objective values are stored in the supplied arrays/pointers
 *
 * args:
 * inputValues - array of input values for subflow run
 * constraintValues - array for storing constraint values; if this
 *         value is NULL, the constraint values are not stored
 * objectiveValue - pointer to a variable for storing objective val
 * penaltyValue - pointer to a variable for storing penalty value
 *
\*=========================================================================*/
static void executeDesignPoint(double* inputValues, double* constraintValues, 
        double* objectiveValue, double* penaltyValue) {

    int i;
    FILE* signalFile;
    FILE* inputValsFile;
    FILE* resultsFile;
    double tempVal;

    /* write input values into the specified file */
    fprintf(logFile,"Opening input values file \"%s\"\n", inputValsFileName);
    fflush(logFile);
    remove(inputValsFileName);
    inputValsFile = fopen(inputValsFileName, "w");
    if(inputValsFile == NULL) {
        fprintf(logFile,"Failed to open file \"%s\" for writing\n", 
                inputValsFileName);
        fflush(logFile);
        fprintf(stderr, "Failed to open file \"%s\" for writing\n", 
                inputValsFileName);
        exit(1);
    }
    fprintf(logFile,"Requesting execution of 1 design point, input values:\n");
    fflush(logFile);
    for(i=0; i<numDesignVariables; i++) {
        tempVal = inputValues[i];
        /* for integer variable types, round the value */
        if(designVariableTypes[i] == DV_TYPE_INTEGER) {
            tempVal = round(tempVal);
        }
        fprintf(inputValsFile, "%.15g ", tempVal);
        fprintf(logFile,"  %d. \"%s\" = %.15g\n",i+1,designVariableNames[i],tempVal);
        fflush(logFile);
    }
    fprintf(inputValsFile, "\n");
    fclose(inputValsFile);

    /* 
     * create the signal file to tell Isight that the input
     * values file is ready and a sublfow evaluation must be done 
     */
    signalFile = fopen(goRunSignalFileName,"w");
    fclose(signalFile);

    /* wait for Isight to delete the signal file */
    fprintf(logFile,"Waiting for Isight to delete the signal file...\n"); 
    fflush(logFile);
    while(1) {
        signalFile = fopen(goRunSignalFileName,"r");
        if(signalFile == NULL) {
            /* file not found, Isight is done executing subflow runs */
            break;
        }
        fclose(signalFile);
        #ifdef WIN32
        Sleep(1000);    /* windows only - sleep for 1000 milliseconds */
        #else
        sleep(1);    /* unix only - sleep for 1 second */
        #endif
    }

    /* try to open results file and read constraints and objective values */
    resultsFile = fopen(resultsFileName,"r");
    if(resultsFile == NULL) {
        fprintf(stderr, "Failed to open file \"%s\" for readin\n", 
                resultsFileName);
        exit(1);
    }

    /* read constraints and obj values from the results file */
    fprintf(logFile,"Constraint/Obj values:\n"); 
    fflush(logFile);
    for(i=0; i<numConstraints; i++) {
        if(fscanf(resultsFile, "%lf", &tempVal) != 1) {
            fprintf(stderr, 
                "Failed to read constraint value #%d from file \"%s\"\n", 
                i+1, resultsFileName);
            exit(1);
        }
        if(constraintValues != NULL) {
            constraintValues[i] = tempVal;
        }
        fprintf(logFile,"  CON#%d = %lg\n", i+1, tempVal); 
        fflush(logFile);
    }
    if(fscanf(resultsFile, "%lf", objectiveValue) != 1) {
        fprintf(stderr, "Failed to read objective value from file \"%s\"\n", 
                resultsFileName);
        exit(1);
    }
    fprintf(logFile,"  OBJ = %lg\n", *objectiveValue); 
    fflush(logFile);
    if(fscanf(resultsFile, "%lf", penaltyValue) != 1) {
        fprintf(stderr, "Failed to read penalty value from file \"%s\"\n", 
                resultsFileName);
        exit(1);
    }
    fprintf(logFile,"  PEN = %lg\n", *penaltyValue); 
    fflush(logFile);

    /* remove the results file for the next iteration */
    remove(resultsFileName);
}

/*=========================================================================*\
 * This function runs multiple design points analysis; constraint and 
 * objective values are stored in the supplied arrays
 *
 * args:
 * numDesignPoints - number of design points to be executed
 * inputValues - matrix of input values for subflow runs
 * constraintValues - matrix for storing constraint values; if this
 *         value is NULL, the constraint values are not stored
 * objectiveValues - array for storing objective values
 * penaltyValues - array for storing penalty values; if this value
 *         is NULL, penalty values are not stored
 *
\*=========================================================================*/
static void executeDesignPointSet(int numDesignPoints, double** inputValues, 
        double** constraintValues, double* objectiveValues, 
        double* penaltyValues) {

    int i,row;
    FILE* signalFile;
    FILE* inputValsFile;
    FILE* resultsFile;
    double tempVal;

    /* write all input values into the file */
    remove(inputValsFileName);
    fprintf(logFile,"Opening input values file \"%s\"\n", inputValsFileName); 
    fflush(logFile);
    inputValsFile = fopen(inputValsFileName, "w");
    if(inputValsFile == NULL) {
        fprintf(stderr, "Failed to open file \"%s\" for writing\n", 
                inputValsFileName);
        exit(1);
    }
    fprintf(logFile,"Requesting execution of %d design points, input values:\n", numDesignPoints); 
    fflush(logFile);
    for(row=0; row<numDesignPoints; row++) {
        fprintf(logFile,"-%d-\n",row+1); 
        fflush(logFile);
        for(i=0; i<numDesignVariables; i++) {
            tempVal = inputValues[row][i];
            /* for integer variable types, round the value */
            if(designVariableTypes[i] == DV_TYPE_INTEGER) {
                tempVal = round(tempVal);
            }
            fprintf(logFile,"  %d. \"%s\" = %.15g\n",i+1,designVariableNames[i],tempVal);
            fflush(logFile);
            fprintf(inputValsFile, "%.15g ", tempVal);
        }
        fprintf(inputValsFile, "\n");
    }
    fclose(inputValsFile);

    /* 
     * create the signal file to tell Isight that a 
     * sublfow evaluation must be done 
     */
    signalFile = fopen(goRunSignalFileName,"w");
    fclose(signalFile);

    /* wait for Isight to delete the signal file */
    fprintf(logFile,"Waiting for Isight to delete the signal file...\n"); 
    fflush(logFile);
    while(1) {
        signalFile = fopen(goRunSignalFileName,"r");
        if(signalFile == NULL) {
            /* file not found, Isight is done executing subflow runs */
            break;
        }
        fclose(signalFile);
        #ifdef WIN32
        Sleep(1000);    /* windows only - sleep for 1000 milliseconds */
        #else
        sleep(1);    /* unix only - sleep for 1 second */
        #endif
    }

    /* try to open results file and read constraints and objective values */
    resultsFile = fopen(resultsFileName,"r");
    if(resultsFile == NULL) {
        fprintf(stderr, "Failed to open file \"%s\" for readin\n", 
                resultsFileName);
        exit(1);
    }

    /* read constraints and obj values from the results file */
    fprintf(logFile,"Constraint/Obj values:\n"); 
    fflush(logFile);
    for(row=0; row<numDesignPoints; row++) {
        fprintf(logFile,"-%d-\n",row+1); 
        fflush(logFile);
        for(i=0; i<numConstraints; i++) {
            if(fscanf(resultsFile, "%lf", &tempVal) != 1) {
                fprintf(stderr, 
                    "Failed to read constraint value #%d from file \"%s\"\n", 
                    i+1, resultsFileName);
                exit(1);
            }
            if(constraintValues != NULL) {
                constraintValues[row][i] = tempVal;
            }
            fprintf(logFile,"  CON#%d = %lg\n", i+1, tempVal); 
            fflush(logFile);
        }
        if(fscanf(resultsFile, "%lf", &tempVal) != 1) {
            fprintf(stderr, "Failed to read objective value from file \"%s\"\n", 
                    resultsFileName);
            exit(1);
        }
        if(objectiveValues != NULL) {
            objectiveValues[row] = tempVal;
        }
        fprintf(logFile,"  OBJ = %lg\n", tempVal); 
        fflush(logFile);
        if(fscanf(resultsFile, "%lf", &tempVal) != 1) {
            fprintf(stderr, "Failed to read penalty value from file \"%s\"\n", 
                    resultsFileName);
            exit(1);
        }
        if(penaltyValues != NULL) {
            penaltyValues[row] = tempVal;
        }
        fprintf(logFile,"  PEN = %lg\n", tempVal); 
        fflush(logFile);
    }

    /* remove the results file for the next iteration */
    remove(resultsFileName);
}

/*=========================================================================*\
 * evaluateDesign - eavaluates design point
 * x - double[] array with design variable values
 * returns double - objective+penalty for the design point
\*=========================================================================*/
static double evaluateDesign(double* x) {

    double objectiveValue, penaltyValue;
    executeDesignPoint(x, NULL, &objectiveValue, &penaltyValue);
    funevals++;
    return (penaltyValue + objectiveValue);
}

/*=========================================================================*\
 * inRange - check that the coordinate is in range and correct
 * value - design variable value to be checked
 * minimum - lower bound
 * maximum - upper bound
 * returns double - adjusted design variable value
\*=========================================================================*/
static double inRange(double value, double minimum, double maximum) {

    if (value < minimum) {
        return minimum;
    }
    if (value > maximum) {
        return maximum;
    }
    return value;
}

/*=========================================================================*\
 * bestNearby - given a point, look for a better one nearby,
 * one variable at a time
 * delta - absolute step size
 * point - double[] array with the current design point
 * prevbest - current best objective+penalty value
 * nvars - number of design variables
 * bl - double[] array with lower variable bounds
 * bu - double[] array with upper variable bounds
 * evalmax - maximum # evaluations
 * returns double - best objective value found
\*=========================================================================*/
static double bestNearby(double* delta, double* point, double prevbest, 
        int nvars, double* bl, double* bu, int evalmax) {

    double* z = NULL;
    double minf, ftmp;
    int i;

    z = (double*)allocateArray(nvars, sizeof(double), "design variable array");

    minf = prevbest;
    /* create a copy of the current design point */
    for (i = 0; i < nvars; i++) {
        z[i] = point[i];
    }

    fprintf(logFile, "Entered BestNearby function\n");
    fflush(logFile);

    for (i = 0; i < nvars; i++) {

        if (funevals >= evalmax) {
            break;
        }

        /* perturb one design variable up */
        z[i] = inRange((point[i] + delta[i]), bl[i], bu[i]);
        if (z[i] != point[i]) {
            fprintf(logFile, "Pertubing design variable #%d \"%s\" up by %g, (%g -> %g)\n", 
                    i+1, designVariableNames[i], delta[i], point[i], z[i]);
            fflush(logFile);
            ftmp = evaluateDesign(z);
        }
        else {
            ftmp = minf;
        }

        /* if improved, continue to the next design variable */
        if (ftmp < minf) {
            fprintf(logFile, "Pertubed design point is an improvement, "
                    "continue to the next design variable\n");
            fflush(logFile);
            minf = ftmp;
            continue;
        }

        /* if exceeded max evaluations, return with the last point */
        if (funevals >= evalmax) {
            z[i] = point[i];
            break;
        }

        /* if not improved, try negative perturbation */
        delta[i] = 0.0 - delta[i];
        z[i] = inRange((point[i] + delta[i]), bl[i], bu[i]);
        if (z[i] != point[i]) {
            fprintf(logFile, "Pertubing design variable #%d \"%s\" down by %g, (%g -> %g)\n", 
                    i+1, designVariableNames[i], delta[i], point[i], z[i]);
            fflush(logFile);
            ftmp = evaluateDesign(z);
        }
        else {
            ftmp = minf;
        }

        /* if improved, save the value and go to the next DV */
        if (ftmp < minf) {
            fprintf(logFile, "Pertubed design point is an improvement, "
                    "continue to the next design variable\n");
            fflush(logFile);
            minf = ftmp;
        }
        else {
            z[i] = point[i];
        }
    }
    for (i = 0; i < nvars; i++) {
        point[i] = z[i];
    }
    return minf;
}

/*=========================================================================*\
 * Hooke - entry point for the Hooke-Jeeves algorithm itself.
 * nvars - number of design variables
 * startpt - double[] array with initial design point
 * bl - double[] array with lower variable bounds
 * bu - doub;e[] array with upper variable bounds
 * step - technique option - relative step size
 * rho - technique option - step size reduction factor
 * epsilon - technique option - mininum step size
 * evalmax - technique option - max # evaluations
 * parmType - double[] array with variable data types (0-double, 1-int)
 * returns - double[] array with the final point; NOTE - Isight does not
 * use the final point returned from the algorithm, but instead selects the
 * best point from ALL executed points during optimization.
\*=========================================================================*/
static double* Hooke(int nvars, double* startpt, double* bl, double* bu, 
        double step, double rho, double epsilon, int evalmax, int* parmType) {

    double* delta = NULL;
    double newf, fbefore, steplength, tmp;
    double* xbefore = NULL;
    double* newx = NULL;
    double* endpt = NULL;
    int i, j, keep;
    int iters;

    delta   = (double*)allocateArray(nvars, sizeof(double), "delta array");
    xbefore = (double*)allocateArray(nvars, sizeof(double), "xbefore array");
    newx    = (double*)allocateArray(nvars, sizeof(double), "newx array");
    endpt   = (double*)allocateArray(nvars, sizeof(double), "endpt array");

    /* Calculate initial step sizes for all DVs */
    fprintf(logFile, "Absolute step sizes for design variables:\n");
    fflush(logFile);
    for (i = 0; i < nvars; i++) {
        newx[i] = xbefore[i] = startpt[i];
        /* calculate delta based on init value and relative step WHEN BOTH BOUNDS NOT AVAILABLE */
        if ((bu[i] == DEFAULT_BOUND) || (bl[i] == -1.0 * DEFAULT_BOUND)) {
            delta[i] = fabs(startpt[i] * step);
            fprintf(logFile, "    delta = fabs(startpt * step) = fabs(%g * %g)\n", 
                    startpt[i], step);
            fflush(logFile);
        }
        else {
            /* compute delta assuming step to be a fraction of the design variable domain, drk */
            delta[i] = fabs(step * (bu[i] - bl[i]));
            fprintf(logFile, "    delta = fabs(step * (bu - bl)) = fabs(%g * (%g - %g))\n", 
                    step, bu[i], bl[i]);
            fflush(logFile);
        }
        /* variables of type REAL */
        if (parmType[i] != 1) {
            /* check for zero initial values */
            if (delta[i] == 0.0) {
                delta[i] = step;
            }
        }
        /* variables of type INT and DISCRETE */
        else {
            delta[i] = round(delta[i]);
            if (delta[i] == 0) {
                delta[i] = 1.0;
            }
        }
        fprintf(logFile, "  %d. \"%s\" delta = %g\n", i+1, designVariableNames[i], delta[i]);
        fflush(logFile);
    }

    /* Starting point analysis */
    fprintf(logFile, "Starting point analysis...\n");
    fflush(logFile);
    fbefore = evaluateDesign(newx);
    if (funevals >= evalmax) {
        for (i = 0; i < nvars; i++) {
            endpt[i] = xbefore[i];
        }
        return endpt;
    }

    newf = fbefore;
    steplength = step;
    iters = 0;

    /*
     * Iterate until we exceed max # evaluations now...
     */
    while ((funevals < evalmax) && (steplength > epsilon)) {

        iters++;

        /* find best new point, one coord at a time */
        for (i = 0; i < nvars; i++) {
            newx[i] = xbefore[i];
        }
        newf = bestNearby(delta, newx, fbefore, nvars, bl, bu, evalmax);
        if (funevals >= evalmax) {
            for (i = 0; i < nvars; i++) {
                endpt[i] = xbefore[i];
            }
            return endpt;
        }

        /* if we made some improvements, pursue that direction */
        keep = 1;
        while ((newf < fbefore) && (keep == 1)) {
            fprintf(logFile, "Nearby point is an improvement, continue in the same direction\n");
            fflush(logFile);
            for (i = 0; i < nvars; i++) {
                /* firstly, arrange the sign of delta[] */
                if (newx[i] <= xbefore[i]) {
                    delta[i] = 0.0 - fabs(delta[i]);
                }
                else {
                    delta[i] = fabs(delta[i]);
                }
                /* now, move further in this direction */
                tmp = xbefore[i];
                xbefore[i] = newx[i];
                newx[i] = inRange((newx[i] + newx[i] - tmp), bl[i], bu[i]);
            }
            fbefore = newf;
            newf = bestNearby(delta, newx, fbefore, nvars, bl, bu, evalmax);

            if (funevals >= evalmax) {
                for (i = 0; i < nvars; i++) {
                    endpt[i] = xbefore[i];
                }
                return endpt;
            }

            /* if the further (optimistic) move was bad.... */
            if (newf >= fbefore) {
            fprintf(logFile, "New point in the same direction was NOT an improvement\n");
            fflush(logFile);
                break;
            }

            /*
            // make sure that the differences between the new
            // and the old points are due to actual
            // displacements; beware of roundoff errors that
            // might cause newf < fbefore
            */
            keep = 0;
            for (i = 0; i < nvars; i++) {
                keep = 1;
                if (fabs(newx[i] - xbefore[i]) > (0.5 * fabs(delta[i]))) {
                    break;
                }
                else {
                    keep = 0;
                }
            }
        }
        if ((steplength >= epsilon) && (newf >= fbefore)) {
            fprintf(logFile, "Updated absolute step sizes for design variables:\n");
            fflush(logFile);
            steplength = steplength * rho;
            for (i = 0; i < nvars; i++) {
                delta[i] *= rho;
                /* for INT and DISCRETE parms round off delta */
                if (parmType[i] == 1) {
                    delta[i] = round(delta[i]);
                    if (delta[i] == 0) {
                        delta[i] = 1.0;
                    }
                }
            fprintf(logFile, "  %d. \"%s\" delta = %g\n", i+1, designVariableNames[i], delta[i]);
            fflush(logFile);
            }
        }
    }

    for (i = 0; i < nvars; i++) {
        endpt[i] = xbefore[i];
    }
    return endpt;
}

/*=========================================================================*\
 *
 * Entry point for the program. Isight will create a technique options file 
 * and a problem setup file, then call this program to execute your
 * optimization algorithm.
 *
 * The command syntax is as follows:
 *    "this.exe options problemSetup inputVals goRunSignal runsDoneSignal runResults"
 *
 * where:
 *    <0> this.exe - the name of this executable program
 *    <1> options - name of the file with technique options
 *    <2> problemSetup - name of the file with problem size parameters and design 
 *        variable values, bounds, types, and names
 *    <3> inputsVals - name of the file where this program must write input 
 *        vals for next subflow run(s), Isight will then try to read this
 *        file and execute the subflows for each row of values
 *    <4> goRunSignal - name of the file that this program must create to 
 *        tell Isight that the input values file is ready and to run subflow(s);
 *        Isight will delete this file when the output results are ready!
 *    <5> runResults - name of the file where Isight will put values of 
 *        constraints, objective, and penalty for all executed subflow runs
 *
\*=========================================================================*/
int main(int argc, char** argv) {

    int i,j, row;
    double* x = NULL;
    char* techniqueOptionsFileName = NULL;
    char* problemSetupFileName = NULL;

    /* create local variables for your technique options here */
    double step = 0.02;
    double rho = 0.5;
    double epsilon = 1e-6;
    int evalmax = 100;

    /* validate command line arguments */
    if(argc < NUM_COMMAND_LINE_ARGS) {
        fprintf(stderr,"Invalid command, argc = %d, expected argc = %d\n", 
                argc, NUM_COMMAND_LINE_ARGS);
        exit(1);
    }

    /* set file names from the command line arguments */
    techniqueOptionsFileName  = argv[1];
    problemSetupFileName      = argv[2];
    inputValsFileName         = argv[3];
    goRunSignalFileName       = argv[4];
    resultsFileName           = argv[5];

    /* 
     * open technique log file for all log messages;
     * this file name must be specified in SDK-Generator, on the 
     * "Technique Options" tab
     * */
    logFile = stdout;
    logFile = fopen(LOG_FILE_NAME,"w");

    /* 
     * debugging printout; 
     * all stdOut prints can be seen as DEBUG 
     * messages in Isight job log 
     */
    fprintf(stdout,"**** Easy Native Optimization Technique ****\n");
    fprintf(stdout,"**** args = \n");
    for(i=0; i<argc; i++) {
        fprintf(stdout,"    %s\n", argv[i]);
    }
    fprintf(stdout,"\n");

    /* 
     * read technique options from the file;
     * the values of technique options and their names will
     * be stored in the global arrays
     */
    readTechniqueOptionsFile(techniqueOptionsFileName);

    /* 
     * check option names in the global array, and assign 
     * technique option values to local variables 
     * based on their names
     */
    for(i=0; i<MAX_NUM_TECHNIQUE_OPTIONS; i++) {
        if(strcmp(techniqueOptionNames[i],"Relative Step Size")==0) {
            step = techniqueOptions[i];
        }
        if(strcmp(techniqueOptionNames[i],"Step Size Reduction Factor")==0) {
            rho = techniqueOptions[i];
        }
        if(strcmp(techniqueOptionNames[i],"Termination Step Size")==0) {
            epsilon = techniqueOptions[i];
        }
        if(strcmp(techniqueOptionNames[i],"Max Evaluations")==0) {
            evalmax = (int)techniqueOptions[i];
        }
    }

    /* 
     * read problem setup data from the file;
     * the values of all problem setup parameters will
     * be stored in global arrays
     */
    readProblemSetupFile(problemSetupFileName);

    /* allocate arrays for inputs, constraints, etc. */
    x = (double*)allocateArray(numDesignVariables, sizeof(double), 
            "input values array");
    for(i=0; i<numDesignVariables; i++) {
        x[i] = designVariableValues[i];
    }

    /* call Hooke-Jeeves algorithm */
    Hooke(numDesignVariables, x, designVariableLowerBounds, designVariableUpperBounds,
        step, rho, epsilon, evalmax, designVariableTypes);

    /* 
     * exit with return code = 0; 
     * any non-zero return code means an error
     */
    exit(0);
}
