/*
 * USERIDMS.C
 *
 * Window DLL for user defined IDMs.
 *
 * Copyright (c) Siemens AG 1993.  All rights reserved.
 *
 * User defined IDMs must provide five functions: xInit, xClear, xClean,
 * xRhs, and xDefuz, where x is the name of the IDM.  In addition, the name
 * of the IDM and its .DLL must be included in the FPL.INI file under the 
 * [InfDefuz] section, and the compiled .DLL must be copied to the SIEFUZZY\BIN 
 * directory before the SieFuzzy tools can recognize and use the method.
 *
 */

#include <windows.h>
#include <fcdslib.h>
#include <memory.h>
#include <stdlib.h>
#include <fpl.h>
#include <fpli.h>

#include <ctype.h>

// Create a dummy function to keep the compiler from complaining about unused
// formal parameters
#define NOTUSED(p)      p=p
HINSTANCE hInst;

// Library main entry point.
//
int FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
{
    NOTUSED(wDataSeg);
    NOTUSED(lpszCmdLine);

    hInst = hInstance;

    if (wHeapSize > 0)
        UnlockData (0);

    return 1;
}

// Standard WEP routine.
//
int FAR PASCAL WEP(int nParam)
{
    NOTUSED(nParam);

    return 1;
}

// Initializes temp space for user defined IDM TakSagIDM
// 
GLOBALHANDLE  FAR PASCAL TakSagIDMInit(BOOL varhasmap, UWORD mapsize)
{
    GLOBALHANDLE hTemp;

    // Currently, both input parameters are ignored.  Later they may not be.
    // varhasmap indicates whether the output variable has a map or function
    // mapsize indicates the size of the map
    
    NOTUSED(varhasmap);
    NOTUSED(mapsize);

    // TakSagIDM is basically Takagi-Sugeno, so we need two double variables
    // for temporary calculations (for two sums)
    // Allocate space for two doubles

    hTemp = GlobalAlloc (GPTR, sizeof(double) * 2);

    // Return a handle to the space we allocated

    return hTemp;
}

// Clears temp space for user defined IDM TakSagIDM
//
int FAR PASCAL TakSagIDMClear(GLOBALHANDLE hTemp)
{
    LPFLOAT ptemp;

    ptemp = (LPFLOAT) GlobalLock (hTemp);

    *ptemp++ = 0.0;

    *ptemp = 0.0;

    GlobalUnlock (hTemp);

    return 0;
}

// Does a rule right hand side on temp space for user defined IDM TakSagIDM
// The Rhs function is called once for each rule in the rulebase.

int FAR PASCAL TakSagIDMRhs(GLOBALHANDLE hTemp, FLOAT alpha, UBYTE weight, LPFLOAT ombf)
{
// hTemp is the handle to the temporary memory allocated in TakSagIDMInit
// alpha is the alpha value from the left hand side of the rule
// weight is the rule weight
// ombftype is the type of output membership function referred to by the rule
// ombf is the actual value of the Takagi-Sugeno polynomial for the current 
// values of the inputs
    int ombftype;
    LPFLOAT ptemp;

    ptemp = (LPFLOAT) GlobalLock (hTemp);

    // This IDM only supports OMBF_EXPR type output
    // membership functions, in which case, *ombf points to a FLOAT value
    // computed by the expression to assign to the output.
    //
    ombftype = (int) *ombf++;

    if (ombftype != OMBF_EXPR)
        return 1;

     // Add to the sum of the numerator of the Takagi-Sugeno equation
    *ptemp++ += alpha * weight * *ombf;

     // Add to the denominator of the Takagi-Sugeno equation
    *ptemp += alpha * weight;

    GlobalUnlock (hTemp);

    return 0;
}

// Defuzzifies temp space for user defined IDM TakSagIDM
//
int FAR PASCAL TakSagIDMDefuz(GLOBALHANDLE hTemp, FLOAT defvalue, LPFLOAT pOutput)
{
// hTemp is the handle to the memory allocated in TakSagIDMInit
// defvalue is the default value for the output
// pOutput is a pointer to the final value of the output variable

    LPFLOAT ptemp;

    ptemp = (LPFLOAT) GlobalLock (hTemp);

    //  if the denominator is not 0, divide numerator sum in *ptemp by 
    //  denominator sum in *(ptemp + 1), else set output to default

    if (*(ptemp + 1) != 0.0)
        *pOutput = *ptemp / *(ptemp + 1);
    else
        *pOutput = defvalue;

    GlobalUnlock (hTemp);

    return 0;
}                                                

// Cleans temp space for user defined IDM TakSagIDM
//
VOID FAR PASCAL TakSagIDMClean(GLOBALHANDLE hTemp)
{
// hTemp is the handle to the memory allocated in TakSagIDMInit

    // Free memory allocated in TakSagIDMInit

    GlobalFree (hTemp);
}

// IDM Max_Alpha collects all rule alphas (multiplied by their weight and
// clipped if the product is larger than 1.0) and assigns the maximum of
// them to the output variable

// Initializes temp space for user defined IDM Max_Alpha
//
GLOBALHANDLE  FAR PASCAL Max_AlphaInit(BOOL varhasmap, UWORD mapsize)
{
    GLOBALHANDLE hTemp;

    // Currently, both input parameters are ignored.  Later they may not be.
    //
    NOTUSED(varhasmap);
    NOTUSED(mapsize);

    // we need one double variable to remember the current maximum alpha
    //
    hTemp = GlobalAlloc (GPTR, sizeof(double));

    return hTemp;
}

// Clears temp space for user defined IDM Max_Alpha
//
int FAR PASCAL Max_AlphaClear(GLOBALHANDLE hTemp)
{
    LPFLOAT ptemp;

    ptemp = (LPFLOAT) GlobalLock (hTemp);

    *ptemp = 0.0;

    GlobalUnlock (hTemp);

    return 0;
}

// Does a rule right hand side on temp space for user defined IDM Max_Alpha
//
int FAR PASCAL Max_AlphaRhs(GLOBALHANDLE hTemp, FLOAT alpha, UBYTE weight, LPFLOAT ombf)
{
    LPFLOAT ptemp;
    FLOAT   add;

    NOTUSED(ombf);

    ptemp = (LPFLOAT) GlobalLock (hTemp);

    // IDM Max_Alpha does not care about the ombftype since it only uses
    // the alpha and weight to calculate its output.
    //
    add = (alpha * weight)/100.0;

    // check if a weight > 100% made add exceed 1.0; if so, clip it to 1.0 
    //
    if (add > 1.0)
        add = 1.0;
    
    if (*ptemp < add)
        *ptemp = add;

    GlobalUnlock (hTemp);

    return 0;
}

// Defuzzifies temp space for user defined IDM Max_Alpha
//
int FAR PASCAL Max_AlphaDefuz(GLOBALHANDLE hTemp, FLOAT defvalue, LPFLOAT pOutput)
{
    LPFLOAT ptemp;

    NOTUSED(defvalue);

    ptemp = (LPFLOAT) GlobalLock (hTemp);

    *pOutput = *ptemp;

    GlobalUnlock (hTemp);

    return 0;
}

// Cleans temp space for user defined IDM Max_Alpha
//
VOID FAR PASCAL Max_AlphaClean(GLOBALHANDLE hTemp)
{
    GlobalFree (hTemp);
}
