/*
 * AIF routines that operate specifically on arrays.
 *
 * Copyright (c) 1996-2002 by Guardsoft Pty Ltd.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 */

#ifdef HAVE_CONFIG_H
#include	<config.h>
#endif /* HAVE_CONFIG_H */

#include	<stdio.h>
#include	<memory.h>
#include	<string.h>

#include	"aif.h"
#include	"aiferr.h"
#include	"aifint.h"

/*
 * Find the rank of the array.
 */
int
AIFArrayRank(AIF *a)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return FDSArrayRank(AIF_FORMAT(a));
}

/*
 * Find the total number of elements in an array. This
 * will count all the elements in a nested array.
 */
int
AIFArraySize(AIF *a)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return FDSArraySize(AIF_FORMAT(a));
}

/*
 * Parse array type descriptor and extract the minimum index and
 * number of elements for each dimension.
 */
int
AIFArrayBounds(AIF *a, int rank, int **min, int **size)
{
	if ( a == (AIF *)NULL || min == (int **)NULL || size == (int **)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	FDSArrayBounds(AIF_FORMAT(a), rank, min, size);

	return 0;
}

/*
 * Extract info about an array. Returns the index type of the
 * first dimension (assumes all dimensions are the same type),
 * the element type of the array, and the number of dimensions
 * of the array.
 */
int
AIFArrayInfo(AIF *a, int *rank, char **type, int *idx)
{
	char *	itype;

	if ( a == (AIF *)NULL || rank == (int *)NULL || type == (char **)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	FDSArrayInfo(AIF_FORMAT(a), rank, (char **)type, &itype);

	if ( idx != (int *)NULL )
		*idx = FDSType(itype);

	return 0;
}

/* A post-processing routine that is called by AIFArraySlice().
 * This routine checks the fds and the data of an AIF object, and it creates
 * a new data for the object 'rdata' which is consistent. The function checks
 * all of the AIF_PTR_REFERENCEs and it changes the first occurence of a
 * particular AIF_PTR_REFERENCE to an appropriate AIF_PTR_NAME based on the
 * 'res' array. It modifies the 'fds' and the 'data'. 'rdata' will also
 * be advanced to the end.
 *
 * The routine gets 4 parameters:
 * fds - the fds of an AIF object
 * data - the original data of an AIF object
 * rdata - the new data for the object
 * res - a 'char *' array that stores the references
 */
int
_aif_array_slice_post(char **fds, char **data, char **rdata, char **res)
{
	int 	datalen;
	int 	index;
	char *	fmt;
	char **	d;

	_fds_resolve(fds);

	switch ( FDSType(*fds) )
	{
	case AIF_REFERENCE:
		fmt = _fds_lookup(fds);
		_aif_array_slice_post(&fmt, data, rdata, res);
		return 0;

	case AIF_ARRAY:
		{
			AIFIndex * ix;
			int i;
			char * fbase;

			ix = FDSArrayIndexInit(*fds);
			_fds_advance(fds);

			for ( i = 0; i < ix->i_nel; i++ )
			{
				fbase = ix->i_btype;
				_aif_array_slice_post(&fbase, data, rdata, res);
			}

			return 0;
		}

	case AIF_AGGREGATE:
		(*fds)++;
		_fds_skip_typename(fds);

		while ( **fds != FDS_AGGREGATE_ACCESS_SEP )
		{
			*fds = strchr(*fds, FDS_AGGREGATE_FIELD_NAME_END) + 1;
			_aif_array_slice_post(fds, data, rdata, res);

			if ( **fds == FDS_AGGREGATE_FIELD_SEP )
				(*fds)++;
		}

		*fds = strchr(*fds, FDS_AGGREGATE_END) + 1;

		return 0;

	case AIF_POINTER:

		(*fds)++;

		switch ( (int)**data )
		{
		case AIF_PTR_NIL:
			**rdata = **data;
			(*data)++;
			(*rdata)++;
			_fds_advance(fds);
			return 0;

		case AIF_PTR_NORMAL:
			**rdata = **data;
			(*data)++;
			(*rdata)++;
			_aif_array_slice_post(fds, data, rdata, res);
			return 0;

		case AIF_PTR_NAME:
			memcpy(*rdata, *data, 5); /* 1 + 4 (ptr name) */
			(*data) += 5;
			(*rdata) += 5;
			_aif_array_slice_post(fds, data, rdata, res);
			return 0;

		case AIF_PTR_REFERENCE:
			d = data;
			_ptrname_to_int(d, &index);
			if ( res[index] != NULL )
			{
				*(*rdata)++ = AIF_PTR_NAME;
				_int_to_ptrname(index, *rdata);
				*rdata += 4;
				datalen = FDSDataSize(*fds, res[index]);
				memcpy(*rdata, res[index], datalen);
				(*rdata) += datalen;
				(*data) += 5;
				_fds_advance(fds);
				res[index]=NULL;
			}
			else
			{
				memcpy(*rdata, *data, 5);
				(*data) += 5;
				(*rdata) += 5;
			}
			return 0;
		}

	default:
			datalen = FDSDataSize(*fds, *data);
			_fds_advance(fds);
			memcpy(*rdata, *data, datalen);
			(*data) += datalen;
			(*rdata) += datalen;
			return 0;
	}
}

/*
 * A pre-processing routine that is called by AIFArraySlice().
 * This routine checks the fds and the data, and it saves all of the
 * references that are pointed by AIF_PTR_NAMEs into 'res'.
 *
 * The function gets 3 parameters, the fds and the data of an AIF object
 * that is going to be scanned, and a 'char *' array (res) to store
 * the results. All of the elements of the 'res' array must be initialized
 * to NULL. It modifies 'fds' and 'data'.
 */
int
_aif_array_slice_pre(char **fds, char **data, char **res)
{
	int 	datalen;
	char * 	fmt;

	_fds_resolve(fds);

	switch ( FDSType(*fds) )
	{
	case AIF_REFERENCE:
		fmt = _fds_lookup(fds);
		_aif_array_slice_pre(&fmt, data, res);
		return 0;

	case AIF_ARRAY:
		{
			AIFIndex * ix;
			int i;
			char * fbase;

			ix = FDSArrayIndexInit(*fds);
			_fds_advance(fds);

			for ( i = 0; i < ix->i_nel; i++ )
			{
				fbase = ix->i_btype;
				_aif_array_slice_pre(&fbase, data, res);
			}

			return 0;
		}

	case AIF_AGGREGATE:
		(*fds)++;
		_fds_skip_typename(fds);

		while ( **fds != FDS_AGGREGATE_ACCESS_SEP )
		{
			*fds = strchr(*fds, FDS_AGGREGATE_FIELD_NAME_END) + 1;
			_aif_array_slice_pre(fds, data, res);

			if ( **fds == FDS_AGGREGATE_FIELD_SEP )
				(*fds)++;
		}

		*fds = strchr(*fds, FDS_AGGREGATE_END) + 1;

		return 0;

	case AIF_POINTER:
		{
			int index;

			(*fds)++;

			switch ( (int)**data )
			{
			case AIF_PTR_NIL:
				(*data)++;
				_fds_advance(fds);
				return 0;

			case AIF_PTR_NORMAL:
				(*data)++;
				_aif_array_slice_pre(fds, data, res);
				return 0;

			case AIF_PTR_NAME:
				(*data)++;
				_ptrname_to_int(data, &index);
				res[index] = *data;
				_aif_array_slice_pre(fds, data, res);
				return 0;

			case AIF_PTR_REFERENCE:
				(*data) += 5;
				_fds_advance(fds);
				return 0;
			}
		}

	default:
			datalen = FDSDataSize(*fds, *data);
			_fds_advance(fds);
			(*data) += datalen;
			return 0;
	}
}

/*
 * Evaluate array slice.
 * XXX: TOFIX
 */
AIF *
AIFArraySlice(AIF *a, int rank, int *mn, int *sz)
{
	int			i;
	int			nrank = 0;
	int *		rankp;
	int			rl;
	char *		rf;
	char *		fds;
	char *		r;
	AIF *		ra;
	AIFIndex *	ix1;
	AIFIndex *	ix2 = (AIFIndex *)NULL;
	char *		res_array[MAX_VALUES_SEEN+1] = {NULL};

	if ( a == (AIF *)NULL || mn == (int *)NULL || sz == (int *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return (AIF *)NULL;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return (AIF *)NULL;
	}

	ix1 = FDSArrayIndexInit(AIF_FORMAT(a));

	if ( rank != ix1->i_rank )
	{
		SetAIFError(AIFERR_INDEX, NULL);
		AIFArrayIndexFree(ix1);
		return (AIF *)NULL;
	}

	/*
	** Create rankp array used by AIFIndexInRange().
	*/
	rankp = (int *)_aif_alloc(rank * sizeof(int *));

	/*
	** Construct a new fds.
	*/
	rl = strlen(AIF_FORMAT(a)) + 1;
	rf = fds = (char *)_aif_alloc(rl);

	/*
	** Calculate nrank, the number of dimensions of the (new) array.
	** This corresponds to the number of elements where sz > 0
	** (i.e. where an empty slice has not been specified.)
	** When sz == -1 we slice the whole dimension.
	*/
	for ( i = 0 ; i < rank ; i++ )
	{
		if ( sz[i] < 0 )
		{
			mn[i] = ix1->i_min[i];
			sz[i] = ix1->i_size[i];
		}
		else if
		(
			mn[i] < ix1->i_min[i] 
			||
			mn[i] >= ix1->i_min[i] + ix1->i_size[i]
			||
			sz[i] > ix1->i_size[i]
		)
		{
			SetAIFError(AIFERR_INDEX, NULL);
			AIFArrayIndexFree(ix1);
			_aif_free(fds);
			_aif_free(rankp);
			return (AIF *)NULL;
		}
		else if ( sz[i] == 0 ) {
			continue;
		}

		rankp[nrank++] = i;

		*rf++ = FDS_ARRAY_START;
		rl--;
		r = FDSRangeInit(mn[i], sz[i]);
		strncpy(rf, r, rl);
		rf += strlen(r);
		rl -= strlen(r);
		*rf++ = FDS_ARRAY_END;
		rl--;
		_aif_free(r);
	}

	strcpy(rf, ix1->i_btype);

	/*
	** Create a new structure to hold the sliced array.
	*/

	/* In the old implementation, we use : ra = MakeAIF(fds, NULL); 
	 * However, with complex data structures, we cannot use MakeAIF()
	 * since the actual size of the ra (i.e. AIF_LEN(ra)) cannot be known 
	 * only from the variable 'fds'.
	 */

	ra = NewAIF(0, BUFSIZ);
	AIF_FORMAT(ra) = fds;

	if ( nrank > 0 )
		ix2 = FDSArrayIndexInit(AIF_FORMAT(ra));

	/*
	** Now build the data.
	*/
	for ( i = 0 ; i < ix1->i_nel ; i++ )
	{
		char *	data;
		char *	ndata;
		int counter, limit;
		int size = 0;
		int subsize = 0;
		int len;

		if ( !AIFIndexInRange(rank, ix1->i_index, mn, sz) )
		{
			AIFArrayIndexInc(ix1);
			continue;
		}

		limit = AIFIndexOffset(ix1->i_rank, ix1->i_index, 
					ix1->i_min, ix1->i_size, NULL);
		for ( counter = 0; counter < limit; counter++ )
		{
			subsize = FDSDataSize(ix1->i_btype,AIF_DATA(a)+size);
			size += subsize;
		}

		data = AIF_DATA(a) + size;
		len = FDSDataSize(ix1->i_btype, data);

		/* old code: data = AIF_DATA(a) + AIFIndexOffset(ix1->i_rank, ix1->i_index, ix1->i_min, ix1->i_max, NULL) * ix1->i_bsize; */

		if ( nrank == 0 )
		{
			memcpy(AIF_DATA(ra), data, len);
			break;
		}

		size = 0; subsize = 0;
		limit = AIFIndexOffset(ix2->i_rank, ix2->i_index, 
					ix2->i_min, ix2->i_size, NULL);
		for ( counter = 0; counter < limit; counter++ )
		{
			subsize = FDSDataSize(ix2->i_btype,AIF_DATA(ra)+size);
			size += subsize;
		}

		ndata = AIF_DATA(ra) + size;

		/* old code: ndata = AIF_DATA(ra) + AIFIndexOffset(ix2->i_rank, ix2->i_index, ix2->i_min, ix2->i_max, NULL) * ix2->i_bsize; */

		/* old code: memcpy(ndata, data, ix1->i_bsize); */
		memcpy(ndata, data, len);

		AIFArrayIndexInc(ix1);
		AIFArrayIndexInc(ix2);
	}

	/* Do pre-processing and post-processing steps */
	{
		char * f = AIF_FORMAT(a);
		char * d = AIF_DATA(a);
		char * n;
		char * tmp;

		_aif_array_slice_pre(&f, &d, res_array);

		f = AIF_FORMAT(ra);
		d = _aif_alloc(BUFSIZ);
		tmp = d;
		memcpy(d, AIF_DATA(ra), BUFSIZ);
		n = AIF_DATA(ra);

		_aif_array_slice_post(&f, &d, &n, res_array);
		/* we need to use 'tmp' because after the call of
		 * _aif_array_slice_post(), d points to the end, thus
		 * the memory cannot be freed by using _aif_free(d)
		 */

		_aif_free(tmp);
	}

	AIFArrayIndexFree(ix1);

	if ( nrank > 0 )
		AIFArrayIndexFree(ix2);

	_aif_free(rankp);

	return ra;
}

/*
 * XXX: TOFIX
 */
/*
 * The old AIFArrayPerm() relied on the assumption that all elements of the
 * AIF array have the same size (i.e. it wrote the data for the 
 * resultant array in a "random access" way).
 * To solve this problem, we create dynamic arrays to hold the data and the
 * length, after that we iterate the arrays to copy the data to the resultant
 * AIF array.
 */
AIF *
AIFArrayPerm(AIF *a, int *index)
{
	int		i;
	int		rl;
	char *		rf;
	AIF *		ra;
	AIFIndex *	ix;
	int 		len;

	char **		data_array;
	int *		len_array;

	if ( a == (AIF *)NULL || index == (int *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return (AIF *)NULL;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return (AIF *)NULL;
	}

	/*
	** Initialise the index arrays.
	*/
	ix = FDSArrayIndexInit(AIF_FORMAT(a));

	/*
	** Create a new structure to hold the permuted array.
	*/
	ra = NewAIF(strlen(AIF_FORMAT(a))+1, BUFSIZ);

	/*
	** Construct a new fds.
	*/
	memcpy(AIF_FORMAT(ra), AIF_FORMAT(a), strlen(AIF_FORMAT(a))+1);
	rf = AIF_FORMAT(ra);
	rl = strlen(rf) + 1;

	for ( i = 0 ; i < ix->i_rank ; i++ )
	{
		char * r = FDSRangeInit(ix->i_min[index[i]], ix->i_size[index[i]]);
		*rf++ = FDS_ARRAY_START;
		rl--;
		strncpy(rf, r, rl);
		rf += strlen(r);
		rl -= strlen(r);
		*rf++ = FDS_ARRAY_END;
		rl--;
		_aif_free(r);
	}

	strcat(AIF_FORMAT(ra), ix->i_btype);

	len_array = _aif_alloc( ix->i_nel * sizeof(int) );
	data_array = _aif_alloc( ix->i_nel * sizeof(char *) );

	/*
	** Now build the data.
	*/
	for ( i = 0 ; i < ix->i_nel ; i++ )
	{
		char *	d;
		int 	counter, limit;
		int	size = 0; 
		int	subsize = 0;

		limit = AIFIndexOffset(ix->i_rank, ix->i_index,
				ix->i_min, ix->i_size, NULL);

		for ( counter = 0; counter < limit; counter++ )
		{
			subsize = FDSDataSize(ix->i_btype, AIF_DATA(a)+size);
			size += subsize;
		}

		d = AIF_DATA(a) + size;
		len = FDSDataSize(ix->i_btype, d);

		size = 0; subsize = 0;
		limit = AIFIndexOffset(ix->i_rank, ix->i_index,
				ix->i_min, ix->i_size, NULL);

		data_array[limit] = d;
		len_array[limit] = len;

		AIFArrayIndexInc(ix);
	}

	len = 0;

	/* Copy the data to the resultant AIF array */
	for ( i = 0 ; i < ix->i_nel ; i++ )
	{
		char * rd;

		rd = AIF_DATA(ra) + len;

		memcpy(rd, data_array[i], len_array[i]);

		len += len_array[i];
	}

	AIFArrayIndexFree(ix);

	_aif_free(data_array);
	_aif_free(len_array);

	return ra;
}

char *
AIFArrayIndexType(AIF *a)
{

	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return NULL;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return NULL;
	}

	return FDSArrayIndexType(AIF_FORMAT(a));
}

int
AIFArrayMinIndex(AIF *a, int n)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return FDSArrayMinIndex(AIF_FORMAT(a), n);
}

int
AIFArrayRankSize(AIF *a, int n)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return FDSArrayRankSize(AIF_FORMAT(a), n);
}

/*
 * The index array is used to compute the location of data in
 * a multi-dimensional array, and basically corresponds to a loop
 * counter in normal circumstances.
 */

#define PERMUTE(p, d) ((p) != NULL ? (p)[d] : d)

/*
 * Initialise an index array to the minimum index value for each 
 * dimension, given the number of dimensions (rank), and the minimum 
 * and maximum values of each dimension (min, max). Returns the number 
 * of elements in the array.
 */
AIFIndex *
AIFArrayIndexInit(AIF *a)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return (AIFIndex *)NULL;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return (AIFIndex *)NULL;
	}

	return FDSArrayIndexInit(AIF_FORMAT(a));
}

/*
 * Check that the index value of each index in rankp is within the range
 * min .. min+size-1. Returns 1 if they are, otherwise 0.
 *
 * Note: rankp, index, min and size are guaranteed to have at least
 * rank elements.
 */
int
AIFIndexInRange(int rank, int *index, int *min, int *size)
{
	int	d;

	for ( d = rank - 1 ; d >= 0 ; d-- )
	{
		if ( min[d] < 0 )
			continue;

		if ( index[d] < min[d] || index[d] >= min[d] + size[d] )
			return 0;
	}

	return 1;
}

/*
 * Step through indexes in the order specified by the perm array.
 * Indexes cycle from min to max. Returns 1 on carry out.
 */
int
AIFArrayIndexInc(AIFIndex *ix)
{
	int	d;

	for ( d = ix->i_rank - 1 ; d >= 0 ; d-- )
	{
		if ( ++(ix->i_index[d]) >= ix->i_min[d] + ix->i_size[d] )
		{
			ix->i_index[d] = ix->i_min[d];
			continue;
		}

		return 0;
	}

	ix->i_finished = 1;

	return 1;
}

void
AIFArrayIndexFree(AIFIndex *ix)
{
	_aif_free(ix->i_btype);
	_aif_free(ix->i_index);
	_aif_free(ix->i_min);
	_aif_free(ix->i_size);
	_aif_free(ix);
}

/*
 * Index array element and return as doublest
 * XXX: TOFIX
 */
int
AIFArrayElementToDoublest(AIF *a, AIFIndex *ix, AIFDOUBLEST *val)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	if ( AIFBaseType(a) != AIF_FLOATING )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return _aif_to_doublest(AIF_DATA(a) + AIFIndexOffset(ix->i_rank, ix->i_index, ix->i_min, ix->i_size, NULL) * ix->i_bsize, ix->i_bsize, val);
}

int
AIFArrayElementToDouble(AIF *a, AIFIndex *ix, double *val)
{
	int		res;
	AIFDOUBLEST	dd;

	res = AIFArrayElementToDoublest(a, ix, &dd);
	
	if ( res >= 0 )
		*val = (double)dd; /* maybe lose some precision */

	return res;
}

/*
 * Index array element and return as longest
 * XXX: TOFIX
 */
int
AIFArrayElementToLongest(AIF *a, AIFIndex *ix, AIFLONGEST *val)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	if ( AIFBaseType(a) != AIF_INTEGER )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return _aif_to_longest(AIF_DATA(a) + AIFIndexOffset(ix->i_rank, ix->i_index, ix->i_min, ix->i_size, NULL) * ix->i_bsize, ix->i_bsize, val);
}

int
AIFArrayElementToInt(AIF *a, AIFIndex *ix, int *val)
{
	int			res;
	AIFLONGEST	l;

	res = AIFArrayElementToLongest(a, ix, &l);

	if ( res >= 0 )
		*val = (int)l;
	
	return res;
}

AIF *
_aif_array_ref(AIF *a, int rank, int *index, int *min, int *size, char *btype, int bsize)
{
	int		counter;
	int		offset;
	char *	fds;
	char *	data;
	char *	data_start;
	char *	data_end;

	offset = AIFIndexOffset(rank, index, min, size, (int *)NULL);
	data_start = data_end = AIF_DATA(a);

	for ( counter = 0 ; counter <= offset ; counter++ )
	{
		fds = btype;
		data_start = data_end;
		_fds_skip_data(&fds, &data_end);
	}

	data = _aif_alloc(data_end - data_start);
	memcpy(data, data_start, data_end - data_start);

	return _make_aif(strdup(btype), data, data_end - data_start);
}

/*
 * Compute size of array.
 */
int
_aif_array_size(char *fds, char *data)
{
	int		i;
	int		n = 0;
	char *		dstart;
	char *		fmt;
	char *		btype;
	AIFIndex *	ix;

	ix = FDSArrayIndexInit(fds);

	btype = FDSBaseType(fds);

	for ( i = 0 ; i <= ix->i_nel ; i++ )
	{
		fmt = btype;
		dstart = data;
		_fds_skip_data(&fmt, &data);
		n += data - dstart;
	}

	AIFArrayIndexFree(ix);

	return n;
}

/*
 * Return array element at index.
 */
AIF *
AIFArrayElement(AIF *a, AIFIndex *ix)
{
	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return (AIF *)NULL;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return (AIF *)NULL;
	}

	if ( ix->i_finished )
		return (AIF *)NULL;

	return _aif_array_ref(a, ix->i_rank, ix->i_index, ix->i_min, ix->i_size, ix->i_btype, ix->i_bsize);
}

/*
 * Return array element at location.
 */
AIF *
AIFArrayRef(AIF *a, int rank, int *loc)
{
	int		i;
	AIF *		ae;
	AIFIndex *	ix;

	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return (AIF *)NULL;
	}

	if ( AIFType(a) != AIF_ARRAY )
	{
		SetAIFError(AIFERR_TYPE, NULL);
		return (AIF *)NULL;
	}

	ix = AIFArrayIndexInit(a);

	if ( rank != ix->i_rank )
	{
		AIFArrayIndexFree(ix);
		SetAIFError(AIFERR_INDEX, NULL);
		return (AIF *)NULL;
	}

	for ( i = 0 ; i < rank ; i++ )
	{
		if ( loc[i] < ix->i_min[i] || loc[i] >= ix->i_min[i] + ix->i_size[i] )
		{
			AIFArrayIndexFree(ix);
			SetAIFError(AIFERR_INDEX, NULL);
			return (AIF *)NULL;
		}
	}

	ae = _aif_array_ref(a, ix->i_rank, loc, ix->i_min, ix->i_size, ix->i_btype, ix->i_bsize);

	AIFArrayIndexFree(ix);

	return ae;
}

/*
 * Copy data into an array.
 * XXX: TOFIX
 */
int
AIFSetArrayData(AIF *dst, AIFIndex *ix, AIF *src)
{
	char *	dd;

	if
	(
		dst == (AIF *)NULL
		||
		src == (AIF *)NULL
		||
		ix == (AIFIndex *)NULL
	)
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}               

	if
	( 
		AIFType(dst) != AIF_ARRAY
		||
		AIFBaseType(dst) != AIFType(src)
	)
	{       
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	dd = AIF_DATA(dst) + AIFIndexOffset(ix->i_rank, ix->i_index, ix->i_min, ix->i_size, NULL) * ix->i_bsize;

	memcpy(dd, AIF_DATA(src), ix->i_bsize);

	return 0;
}

/*
 * Calculate the data offset in the array given the current value
 * of the indexes, and the minimum indexes and size of each dimension.
 */
int
AIFIndexOffset(int rank, int *index, int *min, int *size, int *perm)
{
	int	d;
	int	p;
	int	off = 0;
	int	sz = 1;

	for ( d = rank - 1 ; d >= 0 ; d-- )
	{
		p = PERMUTE(perm, d);
		off += (index[p] - min[p]) * sz;
		sz *= size[p];
	}

	return off;
}



