/* array.c
********************************************************************************
* array.c -- Routines for array series objects.
*
* User routines:
*	Pro_ArraySeries()	creates a series object out of an internal
*				array of values.
*
* Mark Showalter & Neil Heather, PDS Ring-Moon Systems Node, March 1998
*******************************************************************************/
#include <stdio.h>
#include <string.h>
#include "profile.h"
#include "fortran.h"

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

typedef struct ZPRO_ARRAY_STRUCT {
    XPRO_CLASS	class;
    RL_INT4	kmin, kmax;
    RL_BOOL	isdouble, isduped;
    RL_FLT8	missing, invalid;
    RL_FLT4	*table1;
    RL_FLT8	*table2;
} ZPRO_ARRAY;

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

static XPRO_CLASS array_class = {XPRO_ARRAY_CLASS, "array", NULL};

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

static RL_FLT8 ZPro_ArrayValue RL_PROTO((RL_VOID *pointer, RL_INT4 k,
                                        RL_INT4 *flag));
static void    ZPro_FreeArray  RL_PROTO((RL_VOID *pointer));
static void    ZPro_PrintArray RL_PROTO((RL_VOID *pointer));

/*
********************************************************************************
* EXPORTED USER ROUTINES
********************************************************************************
*$ Component_name:
*	Pro_ArraySeries (array.c)
*$ Abstract:
*	This routine creates and returns an array series object.  An array
*	series is one in which the sample values have been stored in a table in
*	memory by the user.
*$ Keywords:
*	PROFILE, SERIES
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	PRO_OBJECT	*Pro_ArraySeries(table, k1, nsamples, x1, dx,
*				missing, invalid, isdouble, dupe)
*	RL_VOID		table[];
*	RL_INT4		k1, nsamples;
*	RL_FLT8		x1, dx, missing, invalid;
*	RL_BOOL		isdouble, dupe;
*$ Inputs:
*	table		pointer to the first element of the sample array, in
*			either single or double precision.
*	k1		starting index.
*	nsamples	number of samples in table.
*	x1		x-value of first sample in table.
*	dx		x-interval between consecutive samples.
*	missing		table value to indicate sample is missing.
*	invalid		table value to indicate sample is invalid.
*	isdouble	TRUE if table is in double precision; FALSE if it is in
*			single precision.
*	dupe		TRUE to duplicate the array internally; FALSE to point
*			to array provided by the user.
*$ Outputs:
*	none
*$ Returns:
*	pointer to a new array object, or NULL on non-fatal error.
*$ Detailed_description:
*	This routine creates and returns an array series object.  An array
*	series is one in which the sample values have been stored in a table in
*	memory by the user.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	Memory is allocated.
*$ Examples:
*	This snippet of code defines a series with seven samples with
*	sequential values -1. through 5.  It then converts the values to
*	integers and writes them out on a single line.  The result is
*	-1 0 1 2 3 4 5
*
*	PRO_OBJECT	*series;
*	RL_FLT8		numbers[] = {-1., 0., 1., 2., 3., 4., 5.};
*	RL_INT4		k;
*
*	\* Create a series that returns -1 through 5 *\
*	series = Pro_ArraySeries(numbers, 1, 7, 1., 1.,
*				-999., -999., TRUE, FALSE);
*	\* Print values *\
*	for (k=1; k<=7; k++)
*		printf("%1d ", (RL_INT4) Pro_SeriesValue(series,k));
*	printf("\n");
*
*$ Error_handling:
*	Profile toolkit error handling is in effect.
*
*	Conditions raised:
*	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_ArraySeries(table, k1, nsamples, x1, dx, missing, invalid,
		                 isdouble, dupe)
RL_VOID		*table;
RL_INT4		k1, nsamples;
RL_FLT8		x1, dx, missing, invalid;
RL_BOOL		isdouble, dupe;
{
ZPRO_ARRAY	*array;
PRO_OBJECT	*new;
RL_INT4		i, size;
RL_VOID		*temp;
RL_FLT4		*temp1;
RL_FLT8		*temp2;

    /* Allocate new array structure */
    array = (ZPRO_ARRAY *) XRL_Malloc(sizeof(ZPRO_ARRAY));
    if (array == NULL) return NULL;

    /* Initialize structure */
    array->class = array_class;

    array->kmin = k1;
    array->kmax = k1 + nsamples - 1;
    array->isdouble = isdouble;
    array->missing  = isdouble ? missing : (RL_FLT4) missing;
    array->invalid  = isdouble ? invalid : (RL_FLT4) invalid;
    array->table1   = NULL;
    array->table2   = NULL;

    /* Allocate table if necessary */
    if (dupe) {
        size = isdouble ? sizeof(RL_FLT8) : sizeof(RL_FLT4);
	temp = XRL_Malloc(nsamples * size);
	if (temp == NULL) {
	    XRL_Free((RL_VOID *) array);
	    return NULL;
	}
    }
    else {
	temp = table;
    }

    /* Save pointer */
    if (isdouble) array->table2 = (RL_FLT8 *) temp;
    else          array->table1 = (RL_FLT4 *) temp;

    /* Copy data into table if necessary */
    if (dupe) {
	if (isdouble) {
	    temp2 = (RL_FLT8 *) table;
	    for (i = 0; i < nsamples; i++) (array->table2)[i] = temp2[i];
	}
	else {
	    temp1 = (RL_FLT4 *) table;
	    for (i = 0; i < nsamples; i++) (array->table1)[i] = temp1[i];
	}
    }

    array->isduped = dupe;

    /* Create new object */
    new = XPro_MakeSeries(array->kmin, array->kmax, x1, dx,
                          ZPro_ArrayValue, ZPro_FreeArray, ZPro_PrintArray,
                          (RL_VOID *) array);

    return new;
}

/*
********************************************************************************
* INTERNAL FUNCTIONS
********************************************************************************
* ZPro_ArrayValue(pointer, k, flag)
*
* This is the series evaluation function for an array series object.
*
* Inputs:
*	pointer		pointer to the ZPRO_ARRAY data structure.
*	k		index at which to evaluate array.
*
* Outputs:
*	flag		0 if value returned is valid; PRO_MISSING_FLAG if it is
*			missing; PRO_INVALID_FLAG if it is invalid.
*
* Return:		value of array at given index; 0. on error.
*******************************************************************************/

static RL_FLT8	ZPro_ArrayValue(pointer, k, flag)
RL_VOID		*pointer;
RL_INT4		k, *flag;
{
ZPRO_ARRAY	*array;
RL_FLT8		value;

    array = (ZPRO_ARRAY *) pointer;

    /* Make sure k is inside domain */
    if (k < array->kmin || k > array->kmax) {
	*flag = PRO_MISSING_FLAG;
	return 0.;
    }

    /* Look up value in array */
    if (array->isdouble) value = (array->table2)[k - array->kmin];
    else                 value = (array->table1)[k - array->kmin];

    /* Check for flags */
    if (value == array->missing) {
	*flag = PRO_MISSING_FLAG;
	return 0.;
    }
    if (value == array->invalid) {
	*flag = PRO_INVALID_FLAG;
	return 0.;
    }

    /* Return value and normal flag */
    *flag = 0;
    return value;
}

/*
********************************************************************************
* ZPro_FreeArray(pointer)
*
* This is the series freeing function for an array series object.
*
* Inputs:
*	pointer		pointer to the ZPRO_ARRAY data structure.
*******************************************************************************/

static void	ZPro_FreeArray(pointer)
RL_VOID		*pointer;
{
ZPRO_ARRAY	*array;

    array = (ZPRO_ARRAY *) pointer;

    if (array->isduped) {
	XRL_Free((RL_VOID *) array->table1);
	XRL_Free((RL_VOID *) array->table2);
    }

    XRL_Free(pointer);
}

/*
********************************************************************************
* ZPro_PrintArray(pointer)
*
* This is the series printing function for an array series object.
*
* Inputs:
*	pointer		pointer to the ZPRO_ARRAY data structure.
*******************************************************************************/

static void	ZPro_PrintArray(pointer)
RL_VOID		*pointer;
{
ZPRO_ARRAY	*array;
RL_INT4		k;

    array = (ZPRO_ARRAY *) pointer;

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

    /* Make sure object is an array series */
    if (array->class.id != XPRO_ARRAY_CLASS) {
	printf("PRINT ERROR: Object is not an array series\n");
	return;
    }

    /* Print object info... */
    printf("\nArray series parameters...\n");
    printf("missing flag = %#g\n", array->missing);
    printf("invalid flag = %#g\n", array->invalid);
    printf("   precision = %s\n", array->isdouble ? "double" : "single");
    printf("  duplicated = %s\n", array->isduped ? "true" : "false");

    for (k = array->kmin; k <= array->kmin+5 && k <= array->kmax; k++)
	printf("    array[%1d] = %#g\n", k,
		array->isdouble ? (array->table2)[k - array->kmin] :
		                  (array->table1)[k - array->kmin]);
}

/*
********************************************************************************
* FORTRAN INTERFACE ROUTINES
********************************************************************************
*$ Component_name:
*	FPro_ArraySeries (array.c)
*$ Abstract:
*	This routine creates and returns an array series object.  An array
*	series is one in which the sample values have been stored in a table in
*	memory by the user.
*$ Keywords:
*	PROFILE, SERIES
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_ArraySeries(table, k1, nsamples, x1, dx,
*				missing, invalid, isdouble, dupe)
*	real*(4 or 8)	table(*)
*	real*8		table(*), x1, dx, missing, invalid
*	integer*4	k1, nsamples
*	logical*4	isdouble, dupe
*$ Inputs:
*	table(*)	table of series samples, in either single or double
*			precision.
*	k1		starting index.
*	nsamples	number of samples in table.
*	x1		x-value of first sample in table.
*	dx		x-interval between consecutive samples.
*	missing		value in table to indicate sample is missing.
*	invalid		value to table to indicate sample is invalid.
*	isdouble	.TRUE. if table is in double precision; .FALSE. if it
*			is single precision.
*	dupe		.TRUE. to duplicate the table internally; .FALSE. to
*			point to the table provided by the user;
*$ Outputs:
*	none
*$ Returns:
*	FORTRAN pointer to a new series object, or 0 on non-fatal error.
*$ Detailed_description:
*	This routine creates and returns an array series object.  An array
*	series is one in which the sample values have been stored in a table in
*	memory by the user.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	Memory is allocated.
*$ Examples:
*	This snippet of code defines a series with seven samples with
*	sequential values -1. through 5.  It then write out each value.
*	integer and writes it out.  The result is
*	-1.00
*	 0.00
*	 1.00
*	 2.00
*	 3.00
*	 4.00
*	 5.00
*
*	integer*4	series, k
*	real*8		numbers(7)/ -1., 0., 1., 2., 3., 4., 5. /
*
*	series = FPro_ArraySeries(numbers, 1, 7, 1.d0, 1.d0,
*				-999.d0, -999.d0, .TRUE., .FALSE.)
*
*	do 100 k = 1, 7
*		write(*,'(1x,f5.2)') FPro_SeriesValue(series,k)
*   100 continue
*
*$ Error_handling:
*	Profile toolkit error handling is in effect.
*
*	Conditions raised:
*	PRO_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
*******************************************************************************/

RL_INT4 FORTRAN_NAME(fpro_arrayseries) (table, k1, nsamples, x1, dx,
                                        missing, invalid, isdouble, dupe)
RL_VOID	*table;
RL_INT4 *k1, *nsamples, *isdouble, *dupe;
RL_FLT8 *x1, *dx, *missing, *invalid;
{
RL_VOID *ptr;
RL_INT4 index;

    ptr = (RL_VOID *) Pro_ArraySeries(table, *k1, *nsamples, *x1, *dx,
                                      *missing, *invalid,
                                      (RL_BOOL) *isdouble, (RL_BOOL) *dupe);
    if (ptr == NULL) return 0;

    index = FORT_AddPointer(ptr);
    if (index == 0) Pro_FreeObject((PRO_OBJECT *) ptr);

    return index;
}

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