/* function.c
********************************************************************************
* function.c -- routines for generic function objects
*
* User routines:
*	Pro_FuncValue(func,x)		returns the value of a function at x.
*	Pro_WindowFunc(func,x1,x2)	creates a new function that duplicates
*					another but over a new domain (x1,x2).
*
* Programmer routines:
*	XPro_MakeFunc()			used to create a new function object.
*	XPro_FuncPtr()			returns a pointer to the sub-object.
*
* Version 1.0: Original release.
*              Mark Showalter & Neil Heather, PDS Ring-Moon Systems Node, March 1998.
* Version 1.1: QUICK compilation mode added.
*              Mark Showalter, October 1999.
* Version 1.2: Corrected potential memory management error.
*              Mark Showalter, September 2002.
*******************************************************************************/
#include <stdio.h>
#include <string.h>
#include "profile.h"
#include "fortran.h"

/*************************
 * Data type definitions *
 *************************/

typedef struct ZPRO_FUNCTION_STRUCT {
    XPRO_CLASS	class;
    RL_FLT8	x1, x2;
    RL_FLT8	(*evalfunc)  RL_PROTO((RL_VOID *pointer, RL_FLT8 x));
    void	(*freefunc)  RL_PROTO((RL_VOID *pointer));
    void	(*printfunc) RL_PROTO((RL_VOID *pointer));
    RL_BOOL	is_windowed;
} ZPRO_FUNCTION;

/************************************
 * Prototypes of internal functions *
 ************************************/

static void	      ZPro_FreeFunc  RL_PROTO((RL_VOID * func));
static void           ZPro_PrintFunc RL_PROTO((RL_VOID * pointer));
static ZPRO_FUNCTION *ZPro_GetFunc   RL_PROTO((PRO_OBJECT * object));

/********************
 * Static variables *
 ********************/

static RL_CHAR    ZPRO_BLANK = '\0';
static XPRO_CLASS func_class = {XPRO_FUNCTION_CLASS, "function", NULL};

/*********************
 * Macro definitions *
 *********************/

#ifdef QUICK
#define ZPro_GetFunc(object) ((ZPRO_FUNCTION *) XPro_ObjectPtr(object))
#endif

/*
********************************************************************************
* EXPORTED USER ROUTINES
********************************************************************************
*$ Component_name:
*	Pro_FuncValue (function.c)
*$ Abstract:
*	This routine evaluates and returns the value of a function.
*$ Keywords:
*	PROFILE, FUNCTION
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_FLT8    	Pro_FuncValue(object, x)
*	PRO_OBJECT 	*object;
*	RL_FLT8    	x;
*$ Inputs:
*	object		pointer to the function object.
*	x		argument at which to evaluate function.
*$ Outputs:
*	none
*$ Returns:
*	value of function.
*$ Detailed_description:
*	This routine evaluates and returns the value of a function.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose square is a function that returns x squared between -3. and 3.
*
*	Then Pro_FuncValue(square, 2.) returns 4.;
*	     Pro_FuncValue(square, 3.) returns 9.;
*	     Pro_FuncValue(square, 4.) raises PRO_DOMAIN_ERROR and returns 9.
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if the object is NULL or not a function.
*	PRO_DOMAIN_ERROR	if x is outside the function domain.
*	PRO_EVALUATION_FAILURE	if function could not be evaluated.
*
*	On a non-fatal PRO_DOMAIN_ERROR the value at the nearest endpoint is
*	returned; on other non-fatal errors, 0. is returned.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*	1.1: October 1999
*$ Change_history:
*	1.1: QUICK mode compile option added.
*******************************************************************************/

RL_FLT8    	Pro_FuncValue(object, x)
PRO_OBJECT 	*object;
RL_FLT8    	x;
{
ZPRO_FUNCTION  	*func;
RL_FLT8       	value;

    func = ZPro_GetFunc(object);
#ifndef QUICK
    if (func == NULL) return 0.;
#endif

    /* Make sure x is inside domain */
    if (x < func->x1 || x > func->x2) {
	XPro_DomainError("x value", object, func->x1, func->x2, x);

	if (x < func->x1) x = func->x1;
	else              x = func->x2;
    }

    /* Evaluate function */
    value = (func->evalfunc) (func->class.pointer, x);

    return value;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_WindowFunc (function.c)
*$ Abstract:
*	This routine generates a new function object with a narrower domain than
*	the original function.
*$ Keywords:
*	PROFILE, FUNCTION
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	PRO_OBJECT    	*Pro_WindowFunc(object, x1, x2)
*	PRO_OBJECT     	*object;
*	RL_FLT8         x1, x2;
*$ Inputs:
*	object		pointer to the function object.
*	x1, x2		new lower and upper limits of domain (inclusive).
*$ Outputs:
*	none
*$ Returns:
*	pointer to a new function object, or NULL on non-fatal error.
*$ Detailed_description:
*	This routine generates a new function object with a narrower domain than
*	the original function.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	Memory is allocated.  The new function object creates a link to the old.
*$ Examples:
*	Suppose square is a function that returns x squared between -3. and 3.
*
*	Then Pro_WindowFunc(square, -1., 5.) creates a fuction that returns x
*	squared between -1. and 3.
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if the object is NULL or not a function.
*	PRO_EMPTY_DOMAIN 	if the windowed function would have an empty
*				domain.
*	RL_MEMORY_ERROR		on memory allocation error.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
******************************************************************************/

PRO_OBJECT    	*Pro_WindowFunc(object, x1, x2)
PRO_OBJECT     	*object;
RL_FLT8         x1, x2;
{
ZPRO_FUNCTION  *func;
PRO_OBJECT     *new;
RL_FLT8		x1a, x2a;

    func = ZPro_GetFunc(object);
    if (func == NULL) return NULL;

    /* Determine new domain */
    x1a = x1;
    x2a = x2;
    if (x1a < func->x1) x1a = func->x1;
    if (x2a > func->x2) x2a = func->x2;

    /* Make sure domain is not empty */
    if (x1a >= x2a) {
	XPro_EmptyDomain("windowed function", object, NULL,
		func->x1, func->x2, x1, x2);
	return NULL;
    }

    /* Duplicate function object */
    new = XPro_MakeFunc(x1a, x2a, func->evalfunc, func->freefunc,
                        func->printfunc, func->class.pointer);

    /* Enslave original object and check for allocation errors */
    if (new != NULL && XPro_EnslaveObject(new, object)) {
	Pro_FreeObject(new);
	new = NULL;
    }

    /* Transfer coordinate names to new object */
    Pro_RenameObject(new, 1, Pro_ObjectName(object,1));
    Pro_RenameObject(new, 2, Pro_ObjectName(object,2));

    /* Mark function as windowed */
    func = ZPro_GetFunc(new);
    func->is_windowed = TRUE;

    return new;
}

/*
********************************************************************************
* EXPORTED PROGRAMMER ROUTINES
********************************************************************************
* XPro_MakeFunc(x1, x2, evalfunc, freefunc, printfunc, pointer)
*
* This routine creates and initializes a function object.
*
* Inputs:
*	x1, x2		domain limits.
*	evalfunc	routine to evaluate function.
*	freefunc	routine to free data structure.
*	printfunc	routine to print contents of data structure.
*	pointer		pointer to an arbitrary data structure describing the
*			function.
*
* Returns:		new function object.
*
* Errors:
*	RL_MEMORY_ERROR	on memory allocation error.
*******************************************************************************/

PRO_OBJECT     	*XPro_MakeFunc(x1, x2, evalfunc, freefunc, printfunc, pointer)
RL_FLT8         x1, x2;
RL_FLT8 	(*evalfunc)  RL_PROTO((RL_VOID *pointer, RL_FLT8 x));
void		(*freefunc)  RL_PROTO((RL_VOID *pointer));
void		(*printfunc) RL_PROTO((RL_VOID *pointer));
RL_VOID        	*pointer;
{
ZPRO_FUNCTION  	*func;
PRO_OBJECT     	*new;

    /* Allocate new function structure */
    func = (ZPRO_FUNCTION *) XRL_Malloc(sizeof(ZPRO_FUNCTION));
    if (func == NULL) return NULL;

    /* Initialize structure */
    func->class = func_class;
    func->class.pointer = pointer;

    func->x1 = x1;
    func->x2 = x2;
    func->evalfunc = evalfunc;
    func->freefunc = freefunc;
    func->printfunc = printfunc;
    func->is_windowed = FALSE;

    /* Create new object */
    new = XPro_MakeObject(x1, x2, ZPro_FreeFunc, ZPro_PrintFunc, func);

    return new;
}

/*
********************************************************************************
* XPro_FuncPtr(object)
*
* This routine returns a pointer to the function sub-object.
*
* Inputs:
*	object		function object.
*
* Return:		pointer field of the object; NULL on error.
*
* Errors:
*	PRO_CLASS_ERROR		if the object is NULL or not a function.
****************************************************************************/

#ifndef QUICK	/* In QUICK mode, this is defined as a macro in profile.h */

RL_VOID        	*XPro_FuncPtr(object)
PRO_OBJECT     	*object;
{
ZPRO_FUNCTION	*func;

    func = ZPro_GetFunc(object);
    if (func == NULL) return NULL;

    if (func->class.pointer == NULL)
	XPro_NullError("function sub-object", object);

    return func->class.pointer;
}

#endif

/*
********************************************************************************
* INTERNAL ROUTINES
********************************************************************************
* ZPro_FreeFunc(pointer)
*
* This internal routine deallocates the memory used by a generic function
* object.  It is called by Pro_FreeObject().
*
* Input:
*	pointer		pointer to the ZPRO_FUNCTION structure.
*******************************************************************************/

static void	ZPro_FreeFunc(pointer)
RL_VOID        	*pointer;
{
ZPRO_FUNCTION  *func;

    func = (ZPRO_FUNCTION *) pointer;
    if (func == NULL) return;

    if (func->class.pointer != NULL && !func->is_windowed)
	(func->freefunc) (func->class.pointer);
    XRL_Free(func);
}

/*
********************************************************************************
* ZPro_PrintFunc(pointer)
*
* This internal routine prints out information about a function.  It is called
* by Pro_PrintObject() and is used mainly for debugging.
*
* Input:
*	pointer		pointer to the ZPRO_FUNCTION structure.
*******************************************************************************/

static void	ZPro_PrintFunc(pointer)
RL_VOID        	*pointer;
{
ZPRO_FUNCTION	*func;

    func = (ZPRO_FUNCTION *) pointer;

    /* Make sure object is not NULL */
    if (func == NULL) {
	printf("PRINT ERROR: Function pointer is NULL\n");
	return;
    }

    /* Make sure object is a function */
    if (func->class.id != XPRO_FUNCTION_CLASS) {
	printf("PRINT ERROR: Object is not a function\n");
	return;
    }

    /* Print object info... */
    if (func->is_windowed) printf("\nDomain has been windowed\n");

    (func->printfunc)(func->class.pointer);
}

/*
********************************************************************************
* ZPro_GetFunc(object)
*
* This internal routine returns a pointer to the object's ZPRO_FUNCTION data
* structure.
*
* Input:
*	object		function object.
*
* Return:		pointer to ZPRO_FUNCTION structure, or NULL on error.
*
* Errors:
*	PRO_CLASS_ERROR		if object is NULL or not a function.
*******************************************************************************/

#ifndef QUICK	/* In QUICK mode, this is defined as a macro above */

static ZPRO_FUNCTION *ZPro_GetFunc(object)
PRO_OBJECT     	*object;
{
ZPRO_FUNCTION	*func;

    /* Find function object pointer */
    func = (ZPRO_FUNCTION *) XPro_ObjectPtr(object);

    /* Make sure object is not NULL */
    if (func == NULL) {
	XPro_NullError("function", object);
	return NULL;
    }

    /* Make sure object is a function */
    if (func->class.id != XPRO_FUNCTION_CLASS) {
	XPro_ClassError("function", object);
	return NULL;
    }

    return func;
}

#endif

/*
********************************************************************************
* FORTRAN INTERFACE ROUTINES
********************************************************************************
*$ Component_name:
*	FPro_FuncValue (function.c)
*$ Abstract:
*	This routine evaluates and returns the value of a function.
*$ Keywords:
*	PROFILE, FUNCTION
*	FUNCTION, PUBLIC, SUBROUTINE
*$ Declarations:
*	real*8 function Pro_FuncValue(object, x)
*	integer*4	object
*	real*8		x
*$ Inputs:
*	object		FORTRAN pointer to the function object.
*	x		argument at which to evaluate function.
*$ Outputs:
*	none
*$ Returns:
*	value of function.
*$ Detailed_description:
*	This routine evaluates and returns the value of a function.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose square is a function that returns x squared between -3.d0 and
*	3.d0.
*
*	Then FPro_FuncValue(square, 2.d0) returns 4.d0;
*	     FPro_FuncValue(square, 3.d0) returns 9.d0;
*	     FPro_FuncValue(square, 4.d0) raises PRO_DOMAIN_ERROR, returns 9.d0.
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if the object is NULL or not a function.
*	PRO_DOMAIN_ERROR	if x is outside the function domain.
*	PRO_EVALUATION_FAILURE	if function could not be evaluated.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN object pointer.
*
*	On a non-fatal PRO_DOMAIN_ERROR the value at the nearest endpoint is
*	returned; on other non-fatal errors, 0. is returned.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*	1.1: October 1999
*$ Change_history:
*	1.1: QUICK mode compile option added.
*******************************************************************************/

RL_FLT8 FORTRAN_NAME(fpro_funcvalue) (object, x)
RL_INT4 *object;
RL_FLT8 *x;
{
RL_VOID *ptr;

    /* Look up function pointer */
    ptr = FORT_GetPointer(*object);
#ifndef QUICK
    if (ptr == NULL) return 0.;
#endif

    /* Call function */
    return Pro_FuncValue((PRO_OBJECT *) ptr, *x);
}

/*
********************************************************************************
*$ Component_name:
*	FPro_WindowFunc (function.c)
*$ Abstract:
*	This routine generates a new function object with a narrower domain than
*	the original function.
*$ Keywords:
*	PROFILE, FUNCTION
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_WindowFunc(object, x1, x2)
*	integer*4	object
*	real*8		x1, x2
*$ Inputs:
*	object		FORTRAN pointer to the function object.
*	x1, x2		new lower and upper limits of domain (inclusive).
*$ Outputs:
*	none
*$ Returns:
*	FORTRAN pointer to a new function object, or 0 on non-fatal error.
*$ Detailed_description:
*	This routine generates a new function object with a narrower domain than
*	the original function.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	Memory is allocated.  The new function object creates a link to the old.
*$ Examples:
*	Suppose square is a function that returns x squared between -3.d0 and
*	3.d0.
*
*	Then FPro_WindowFunc(square, -1.d0, 5.d0) creates a fuction that returns
*	x squared between -1.d0 and 3.d0.
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if the object is NULL or not a function.
*	PRO_EMPTY_DOMAIN 	if the windowed function would have an empty
*				domain.
*	RL_MEMORY_ERROR		on memory allocation error.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN object pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_INT4 FORTRAN_NAME(fpro_windowfunc) (object, x1, x2)
RL_INT4 *object;
RL_FLT8 *x1, *x2;
{
RL_VOID *ptr1, *ptr2;
RL_INT4 index;

    /* Look up function pointer */
    ptr1 = FORT_GetPointer(*object);
    if (ptr1 == NULL) return 0;

    /* Call function */
    ptr2 = (RL_VOID *) Pro_WindowFunc((PRO_OBJECT *) ptr1, *x1, *x2);
    if (ptr2 == NULL) return 0;

    /* Save new pointer */
    index = FORT_AddPointer(ptr2);
    if (index == 0) Pro_FreeObject((PRO_OBJECT *) ptr2);

    return index;
}

/*******************************************************************************
*/