/* filtered.c
********************************************************************************
* filtered.c -- Routines to filter a statistical series objects
*
* User routines
*	Pro_FilteredStat()	creates a filtered statistical series object.
*
* A filtered statistical series object is a series where each sample is a
* weighted sum of the corresponding samples in another series.  The filter
* function is assumed to be constant, so the new series is, in effect, a
* convolution of the old series with a fixed filter.
*
* Mark Showalter, PDS Ring-Moon Systems Node, March 1998
*******************************************************************************/
#include <stdio.h>
#include "profile.h"
#include "fortran.h"

/********************
 * Type definitions *
 ********************/

typedef struct ZPRO_FILTERED_STRUCT {
    PRO_OBJECT	*stat;
    RL_INT4	df1, df2, fsize;
    RL_FLT8	*filter;
} ZPRO_FILTERED;

/********************************
 * Internal function prototypes *
 ********************************/

static void    ZPro_FilteredKRange RL_PROTO((RL_INT4 knew,
                                             RL_INT4 *k1, RL_INT4 *k2,
                                             RL_VOID *params));
static RL_FLT8 ZPro_FilteredWeight RL_PROTO((RL_INT4 knew, RL_INT4 k,
                                             RL_VOID *params));
static void    ZPro_PrintFiltered  RL_PROTO((RL_VOID *params));

/*
********************************************************************************
* EXPORTED USER ROUTINES
********************************************************************************
*$ Component_name:
*	Pro_FilteredStat (filtered.c)
*$ Abstract:
*	This routine creates and returns a filtered statistical series, where
*	each new sample is a weighted sum of samples in another series.
*$ Keywords:
*	PROFILE, SERIES
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	PRO_OBJECT	*Pro_FilteredStat(stat, filter, df1, df2)
*	PRO_OBJECT	*stat;
*	RL_FLT8		*filter;
*	RL_INT4		df1, df2;
*$ Inputs:
*	stat		pointer to the statistical series to resample.
*	filter[] 	array of filter coefficients.  The array should be of
*			size |df2-df1|+1.
*	df1, df2	index offset of each array endpoint from filter center.
*			Typically, df1<0 and df2>0.
*$ Outputs:
*	none
*$ Returns:
*	pointer to a new statistical series, or NULL on non-fatal error.
*$ Detailed_description:
*	This routine creates and returns a filtered statistical series, where
*	each new sample is a weighted sum of samples in another series.
*
*	The new series is, in effect, a convolution of the old series with a
*	fixed filter.  The filter coefficients are always adjusted so that the
*	sum of the weights on each new sample is unity.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	Memory is allocated.  The new object links to the original statistical
*	series.
*$ Examples:
*	Suppose stat is a statistical series.  Then this code creates a new
*	statistical series in which each sample is an average of the three
*	nearest samples in the original series.
*
*	PRO_OBJECT	*stat3;
*	RL_FLT8		filter[3] = {1., 1., 1.};
*
*	stat3 = Pro_FilteredStat(stat, filter, -1, 1);
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if stat is NULL or is not a stat series.
*	RL_MEMORY_ERROR		on memory allocation failure.
*$ 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_FilteredStat(stat, filter, df1, df2)
PRO_OBJECT	*stat;
RL_FLT8		*filter;
RL_INT4		df1, df2;
{
ZPRO_FILTERED	*params;
PRO_OBJECT	*new;
RL_INT4		i, k1, k2;
RL_FLT8		x1, dx;

    /* Verify object type */
    if (XPro_StatPtr(stat) == NULL) return NULL;

    /* Create new filtered statistical series structure */
    params = (ZPRO_FILTERED *) XRL_Malloc(sizeof(ZPRO_FILTERED));
    if (params == NULL) return NULL;

    params->fsize = df2 - df1 + 1;
    if (df2 < df1) params->fsize = df1 - df2 + 1;

    params->filter = (RL_FLT8 *) XRL_Malloc(params->fsize * sizeof(RL_FLT8));
    if (params->filter == NULL) {
	XRL_Free((RL_VOID *) params);
	return NULL;
    }

    /* Fill structure */
    params->stat = stat;

    params->df1 = df1;
    params->df2 = df2;
    for (i=0; i < params->fsize; i++)
	params->filter[i] = filter[i];

    /* Reverse coefficients if necessary */
    if (df2 < df1) {
	params->df1 = df2;
	params->df2 = df1;
	for (i=0; i < params->fsize; i++)
	    params->filter[i] = filter[params->fsize - i];
    }

    /* Create new weighted statistical series object */
    Pro_SeriesSampling(stat, &x1, NULL, &dx);
    Pro_SeriesIndices(stat, &k1, &k2);

    new = Pro_WeightedStat(stat, k1, k2, x1, dx,
                           ZPro_FilteredKRange, ZPro_FilteredWeight,
                           params, sizeof(ZPRO_FILTERED), TRUE,
                           ZPro_PrintFiltered);
    XRL_Free((RL_VOID *) params);

/* NOTE: It is not necessary to enslave old object because Pro_WeightedStat()
   does that for us!
    if (new != NULL && XPro_EnslaveObject(new,stat)) {
	Pro_FreeObject(new);
	return NULL;
    }
*/

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

    return new;
}

/*
********************************************************************************
* INTERNAL ROUTINES
********************************************************************************
* ZPro_FilteredKRange(knew, k1, k2, params)
*
* This internal routine returns the range of nonzero weights in the old series
* corresponding to a given index in the new series.
*
* Inputs:
*	knew		new index.
*	params		pointer to the ZPRO_FILTERED structure.
*
* Outputs:
*	k1, k2		range of indices in old series for which weight is
*			nonzero.
*******************************************************************************/

static void ZPro_FilteredKRange(knew, k1, k2, params)
RL_INT4		knew, *k1, *k2;
RL_VOID		*params;
{
ZPRO_FILTERED	*filtered;

    filtered = (ZPRO_FILTERED *) params;

    *k1 = knew + filtered->df1;
    *k2 = knew + filtered->df2;
}

/*
********************************************************************************
* ZPro_FilteredWeight(knew, k, params)
*
* This internal routine returns the (un-normalized) weight of an old sample in
* the new series.
*
* Inputs:
*	knew		index in new series.
*	k		index in old series.
*	params		pointer to the ZPRO_FILTERED structure.
*
* Return:		weight value.
*******************************************************************************/

static RL_FLT8	ZPro_FilteredWeight(knew, k, params)
RL_INT4		knew, k;
RL_VOID		*params;
{
ZPRO_FILTERED	*filtered;
RL_INT4		index;

    filtered = (ZPRO_FILTERED *) params;

    index = k - knew - filtered->df1;
    if (index < 0 || index >= filtered->fsize) return 0.;

    return filtered->filter[index];
}

/*
********************************************************************************
* ZPro_PrintFiltered(params)
*
* This internal routine prints information about a filtered stat series.
*
* Inputs:
*	params		pointer to the ZPRO_FILTERED data structure.
*******************************************************************************/

static void	ZPro_PrintFiltered(params)
RL_VOID		*params;
{
ZPRO_FILTERED	*filtered;
RL_INT4		i;

    filtered = (ZPRO_FILTERED *) params;

    /* Print filtered statistical series info... */
    printf("\nFiltered statistical series parameters...\n");

    for (i = filtered->df1; i <= filtered->df2; i++) {
	printf("filter[%d] = %#g\n", i, filtered->filter[i - filtered->df1]);
    }
}

/*
********************************************************************************
* FORTRAN INTERFACE ROUTINES
********************************************************************************
*$ Component_name:
*	FPro_FilteredStat (filtered.c)
*$ Abstract:
*	This routine creates and returns a filtered statistical series, where
*	each new sample is a weighted sum of samples in another series.
*$ Keywords:
*	PROFILE, SERIES
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_FilteredStat(stat, filter, df1, df2)
*	integer*4	stat, df1, df2
*	real*8		filter(*)
*$ Inputs:
*	stat		FORTRAN pointer to the statistical series to resample.
*	filter		array of filter coefficients.  The array should be of
*			size |df2-df1|+1.
*	df1, df2	index offset of each array endpoint from filter center.
*			Typically, df1<0 and df2>0.
*$ Outputs:
*	none
*$ Returns:
*	FORTRAN pointer to a new statistical series, or 0 on non-fatal error.
*$ Detailed_description:
*	This routine creates and returns a filtered statistical series, where
*	each new sample is a weighted sum of samples in another series.
*
*	The new series is, in effect, a convolution of the old series with a
*	fixed filter.  The filter coefficients are always adjusted so that the
*	sum of the weights on each new sample is unity.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	Memory is allocated.  The new object links to the original statistical
*	series.
*$ Examples:
*	Suppose stat is a statistical series.  Then this code creates a new
*	statistical series in which each sample is an average of the three
*	nearest samples in the original series.
*
*	integer*4	stat3
*	real*8		filter(3)/ 1.d0, 1.d0, 1.d0 /
*
*	stat3 = FPro_FilteredStat(stat, filter, -1, 1)
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if stat is NULL or is not a stat series.
*	RL_MEMORY_ERROR		on memory allocation failure.
*	FORTRAN_POINTER_ERROR	if stat 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_filteredstat) (stat, filter, df1, df2)
RL_INT4	*stat, *df1, *df2;
RL_FLT8	*filter;
{
RL_VOID *ptr;
RL_INT4 index;

    /* Look up statistical series pointer */
    ptr = FORT_GetPointer(*stat);
    if (ptr == NULL) return 0;

    /* Create filtered statistical series */
    ptr = (RL_VOID *) Pro_FilteredStat((PRO_OBJECT *) ptr, filter, *df1, *df2);
    if (ptr == NULL) return 0;

    /* Return new pointer */
    index = FORT_AddPointer(ptr);
    if (index == 0) Pro_FreeObject((PRO_OBJECT *) ptr);

    return index;
}

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