/*
 * Routines for comparing AIF objects.
 *
 * 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	<string.h>
#include	<math.h>

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

int
AIFIsZero(AIF *a, int *val)
{
	char *	f;
	char *	d;

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

	f = AIF_FORMAT(a);
	d = AIF_DATA(a);

	return _aif_is_zero(&f, &d, AIF_LEN(a), val);
}

int
_aif_is_zero(char **fds, char **data, int len, int *val)
{
	int			i;
	int			nv;
	int			bytes;
	char		c;
	char *		target;
	AIFIndex *	ix;
	char *		fmt;
	AIFDOUBLEST	v;

	if ( **fds == FDS_NAME )
	{
		*fds = _fds_skipto(*fds, "/");
		(*fds)++;
	}

	switch ( FDSType(*fds) )
	{
	case AIF_UNION:
		bytes = FDSTypeSize(*fds);
		*val = 1; /* zero unless shown otherwise */

		(*fds)++; /* past open brace */

		_fds_skip_typename(fds);

		while ( **fds != FDS_UNION_END )
		{
			*fds = strchr(*fds, FDS_UNION_FIELD_NAME_END) + 1; /* to start of field */

            if ( (i = FDSTypeSize(*fds)) < 0 )
				return -1;

			fmt = _aif_alloc( sizeof(char) * i );
			target = fmt;
			memcpy(target, *data, i);

			if ( _aif_is_zero(fds, &target, len, val) < 0 )
			{
				_aif_free(fmt);
				return -1;
			}

			if ( *val == 0 )
				return 0; /* zero res means no need to look further */
			if ( **fds == FDS_UNION_FIELD_SEP )
				(*fds)++;
		}

		*fds = strchr(*fds, FDS_AGGREGATE_END) + 1; /*past close brace*/
		*data += bytes;

		return 0;

	case AIF_ENUM:
		*fds = _fds_base_type(*fds);
		bytes = _fds_count_bytes(fds);
		*val = _aif_int_is_zero(*data, bytes);
		(*data) += bytes;

		return 0;

	case AIF_POINTER:
		*val = (_get_pointer_type(*data) == AIF_PTR_NIL);
		_fds_skip_data(fds, data);
		return 0;

	case AIF_CHARACTER:
		bytes = _fds_count_bytes(fds);

		if ( _aif_to_char(*data, &c) < 0 )
			return -1;

		*val = (c == '\0');
		(*data) += bytes;

		return 0;

	case AIF_INTEGER:
	case AIF_BOOLEAN: /* if it is FALSE, val = 1, if it is TRUE, val = 0 */
		bytes = _fds_count_bytes(fds);
		*val = _aif_int_is_zero(*data, bytes);
		(*data) += bytes;

		return 0;

	case AIF_FLOATING:
		bytes = _fds_count_bytes(fds);

		if ( _aif_to_doublest(*data, bytes, &v) < 0 )
			return -1;

		*val = (v == (AIFDOUBLEST)0.0L);
		(*data) += bytes;

		return 0;

	case AIF_ARRAY:
		ix = FDSArrayIndexInit(*fds);

		_fds_advance(fds); /* skip over the entire array fds */

		for ( i = 0 ; i < ix->i_nel ; i++ )
		{
			fmt = ix->i_btype;

			if ( _aif_is_zero(&fmt, data, ix->i_bsize, &nv) < 0 )
				return -1;

			if ( nv == 0 ) /* no need to look further */
			{
				*val = 0;
				return 0;
			}
		}

		AIFArrayIndexFree(ix);

		*val = 1;

		return 0;

	/*
	** only uses the public section in AIF_AGGREGATE
	*/
	case AIF_AGGREGATE:
		*val = 1; /* zero unless shown otherwise */

		(*fds)++; /* past open brace */
		_fds_skip_typename(fds);

		while ( **fds != FDS_AGGREGATE_ACCESS_SEP )
		{
			*fds = strchr(*fds, FDS_AGGREGATE_FIELD_NAME_END) + 1; /* to start of field */

			if ( _aif_is_zero(fds, data, len, val) < 0 )
				return -1;

			if ( *val == 0 )
				return 0; /* zero res means no need to look further */
			if ( **fds == FDS_AGGREGATE_FIELD_SEP )
				(*fds)++;
		}

		*fds = strchr(*fds, FDS_AGGREGATE_END) + 1; /*past close brace*/

		return 0;

	case AIF_STRING:
		(*fds)++; /* past "s" */
		bytes = (*(*data)++ & 0xff) << 8;
		bytes += *(*data)++ & 0xff;
		
		*data += bytes;

		*val = (bytes == 0);

		return 0;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}
}

int
_aif_int_is_zero(char *data, int len)
{
	int	i;

	for ( i = 0 ; i < len ; i++ )
		if ( (int)*data++ != 0 )
			return 0;

	return 1;
}

/*
 * Compare types of two AIF values. Return true if they are equivalent,
 * false if not.
 */
int
AIFTypeCompare(AIF *a1, AIF *a2)
{
	return FDSTypeCompare(AIF_FORMAT(a1), AIF_FORMAT(a2));
}

int
AIFCompare(int depth, AIF *a1, AIF *a2, int *res)
{
	char *	f1;
	char *	f2;
	char *	d1;
	char *	d2;

	if ( a1 == (AIF *)NULL || a2 == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}

	f1 = AIF_FORMAT(a1);
	f2 = AIF_FORMAT(a2);
	d1 = AIF_DATA(a1);
	d2 = AIF_DATA(a2);

	return _aif_compare(depth, res, &f1, &d1, &f2, &d2);
}

int
_aif_cmp_int(int *res, char *d1, int l1, char *d2, int l2)
{
	int		i;
	int		l;
	char *	r1;
	char *	r2 = NULL;

	r1 = (char *)_aif_alloc(l2);

	_aif_neg_int(r1, d2, l2);

	_aif_add_int(&r2, &l, d1, l1, r1, l2);

	_aif_free(r1);

	if ( *r2 & 0x80 )
	{
		/*
		** negative result => a < b
		*/
		_aif_free(r2);
		*res = -1;
		return 0;
	}

	/*
	** Check for zero
	*/

	for ( i = 0 ; i < MAX(l1, l2) ; i++ )
	{
		if ( r2[i] != 0x0 )
		{
			/*
			** positive result => a > b
			*/
			_aif_free(r2);
			*res = 1;
			return 0;
		}
	}

	/*
	** zero result => a = b
	*/
	_aif_free(r2);
	*res = 0;
	return 0;
}

int
_aif_cmp_float(int *res, char *d1, int l1, char *d2, int l2)
{
	AIFDOUBLEST	v1;
	AIFDOUBLEST	v2;

	if
	(
		_aif_to_doublest(d1, l1, &v1) < 0
		||
		_aif_to_doublest(d2, l2, &v2) < 0
	)
		return -1;

	if ( v1 < v2 )
		*res = -1;
	else if ( v1 > v2 )
		*res = 1;
	else
		*res = 0;

	return 0;
}

int
_aif_cmp_char(int *res, char *d1, char *d2)
{
	char	v1;
	char	v2;

	if
	(
		_aif_to_char(d1, &v1) < 0
		||
		_aif_to_char(d2, &v2) < 0
	)
		return -1;

	if ( v1 < v2 )
		*res = -1;
	else if ( v1 > v2 )
		*res = 1;
	else
		*res = 0;

	return 0;
}

/*
 * Compare two AIF values. 
 *
 * For numeric values, sets res to:
 *
 *      -1 if d1 < d2
 *       0 if d1 == d2
 *       1 if d1 > d2
 *
 * For other values, sets res to:
 *
 *       0 if d1 == d2
 *       1 if d1 != d2
 *
 * For array type, res is 0 if all elements are equal, 1 otherwise.
 * The parameters f1, d1, f2, d2 are advanced through their structures; on
 * return, they are in the position following the relevant parts.
 * The return value is negative if there is a type failure.
 */
int
_aif_compare(int depth, int *res, char **f1, char **d1, char **f2, char **d2)
{
	int			i;
	int			l3;
	int			success; /* value to return indicating type match */
	int			bytes1;
	int			bytes2; /* number of bytes in arithmetic data */
	char *		fmt;
	char *		d3 = NULL;
	AIFIndex *	ix1;
	AIFIndex *	ix2;
	AIFLONGEST	val1;
	AIFLONGEST	val2;

	/* these cmp_by_name, f2_x* and d2_x* are used by 'case AIF_AGGREGATE' */
	int	cmp_by_name = 0;
	char *	f2_x1;
	char *	f2_x2;
	char *	d2_x1;
	char *	d2_x2;

	ResetAIFError();

	/*
	** resolve names and references first
	*/
	_fds_resolve(f1);
	_fds_resolve(f2);

	if ( FDSType(*f1) == AIF_REFERENCE )
	{
		fmt = _fds_lookup(f1);
		return _aif_compare(depth, res, &fmt, d1, f2, d2);
	}

	if ( FDSType(*f2) == AIF_REFERENCE )
	{
		fmt = _fds_lookup(f2);
		return _aif_compare(depth, res, f1, d1, &fmt, d2);
	}

	switch ( FDSType(*f1) )
	{
	case AIF_POINTER:
		if ( FDSType(*f2) == AIF_POINTER )
		{
			int t1 = _get_pointer_type(*d1);
			int t2 = _get_pointer_type(*d2);

			if ( depth == -1 )
			{
				*res = 0;

				_fds_skip_data(f1, d1);
				_fds_skip_data(f2, d2);
				
				return 0;
			}
			else if ( depth == 1 )
				depth = -1;
			else if ( depth > 1 )
				depth--;

			/*
			 * Can't compare invalid pointers
			 */
			if ( t1 == AIF_PTR_INVALID || t2 == AIF_PTR_INVALID )
			{
				*res = 0;

				_fds_skip_data(f1, d1);
				_fds_skip_data(f2, d2);

				SetAIFError(AIFERR_ARITH, NULL);

				return -1;
			}

			/*
			 * pointers are equal only if they refer to the same target
			 * this also prevents infinite recursion
			 */
			if ( t1 == AIF_PTR_REFERENCE || t2 == AIF_PTR_REFERENCE ) {
				if (t1 == AIF_PTR_REFERENCE && t2 == AIF_PTR_REFERENCE) {
					*res = (_get_pointer_name(*d1) != _get_pointer_name(*d2));
				} else {
					*res = 1;
				}

				_fds_skip_data(f1, d1);
				_fds_skip_data(f2, d2);

				return 0;
			}

			/*
			 * null pointers are equal only if they are both null
			 */
			if ( t1 == AIF_PTR_NIL || t2 == AIF_PTR_NIL )
			{
				*res = (t1 != t2);

				_fds_skip_data(f1, d1);
				_fds_skip_data(f2, d2);

				return 0;
			}

			/* 
			 * both point to something real so compare targets
			 */

			_find_target(f1, d1, 0);
			_find_target(f2, d2, 1);

			return _aif_compare(depth, res, f1, d1, f2, d2);
		}
		else
		{
			/*
			** incompatible types
			*/
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}

	case AIF_UNION:
		bytes1 = FDSTypeSize(*f1);
		bytes2 = FDSTypeSize(*f2);

		if ( FDSType(*f2) != AIF_UNION )
		{
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}

		(*f1)++; /* past open brace */
		(*f2)++; /* past open brace */
		_fds_skip_typename(f1);
		_fds_skip_typename(f2);

		*res = 0; /* first assumption, they are equal */

		while ( **f1 != FDS_UNION_END )
		{
			int size1, size2;
			char *temp1, *temp2, *data1, *data2;

			if ( **f2 == FDS_UNION_END )
			{ 
				/* 
				** f1 has more fields
				*/
				SetAIFError(AIFERR_CONV, NULL);

				*res = 1; /* in a sense, d1 > d2 */

				return -1;
			}

			*f1 = strchr(*f1, FDS_UNION_FIELD_NAME_END) + 1;
			*f2 = strchr(*f2, FDS_UNION_FIELD_NAME_END) + 1;
			
			size1 = FDSTypeSize(*f1);
			size2 = FDSTypeSize(*f2);
			temp1 = _aif_alloc( sizeof(char) * size1 );
			temp2 = _aif_alloc( sizeof(char) * size2 );
			data1 = temp1;
			data2 = temp2;
			memcpy(data1, *d1, size1);
			memcpy(data2, *d2, size2);

			success = _aif_compare(depth, res, f1, &data1, f2, &data2);
			_aif_free(temp1);
			_aif_free(temp2);

			/*
			** non-zero res means no need to look further
			*/
			if ( *res )
				return 0;

			if ( **f1 == FDS_UNION_FIELD_SEP )
				(*f1)++;

			if ( **f2 == FDS_UNION_FIELD_SEP )
				(*f2)++;
		}

		if ( **f2 != FDS_UNION_END )
		{
			/*
			** f2 has more fields 
			*/
			SetAIFError(AIFERR_CONV, NULL);

			*res = -1; /* in a sense, d1 < d2 */
			
			return -1;
		}

		*f1 = strchr(*f1, FDS_UNION_END) + 1; /*past close brace*/
		*f2 = strchr(*f2, FDS_UNION_END) + 1; /*past close brace*/
		*d1 += bytes1;
		*d2 += bytes2;

		return 0;

	case AIF_ENUM:
		switch ( FDSType(*f2) )
		{
		case AIF_ENUM:
			*f1 = _fds_base_type(*f1);
			*f2 = _fds_base_type(*f2);

			bytes1 = _fds_count_bytes(f1);
			bytes2 = _fds_count_bytes(f2);

			success = _aif_cmp_int(res, *d1, bytes1, *d2, bytes2);

			*d1 += bytes1;
			*d2 += bytes2;

			return success;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}
		break;

	case AIF_INTEGER:
		switch ( FDSType(*f2) )
		{
		case AIF_INTEGER:
			bytes1 = _fds_count_bytes(f1);
			bytes2 = _fds_count_bytes(f2);

			success = _aif_cmp_int(res, *d1, bytes1, *d2, bytes2);

			*d1 += bytes1;
			*d2 += bytes2;

			return success;

		case AIF_FLOATING:
			bytes1 = _fds_count_bytes(f1);
			_aif_int_to_aif_float(&fmt, &d3, &l3, *d1, bytes1);
			*d1 += bytes1;

			if ( l3 < 0 )
			{
				*res = 1;
				return -1;
			}

			bytes2 = _fds_count_bytes(f2);
			success = _aif_cmp_float(res, d3, l3, *d2, bytes2);
			*d2 += bytes2;
			_aif_free(fmt);
			_aif_free(d3);

			return success;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}
		break;

	case AIF_FLOATING:
		switch ( FDSType(*f2) )
		{
		case AIF_INTEGER:
			bytes2 = _fds_count_bytes(f2);
			_aif_int_to_aif_float(&fmt, &d3, &l3, *d2, bytes2);
			*d2 += bytes2;

			if ( l3 < 0 )
			{
				*res = 1;
				return -1;
			}

			bytes1 = _fds_count_bytes(f1);
			success = _aif_cmp_float(res, *d1, bytes1, d3, l3);
			*d1 += bytes1;
			_aif_free(fmt);
			_aif_free(d3);

			return success;
			
		case AIF_FLOATING:
			bytes1 = _fds_count_bytes(f1);
			bytes2 = _fds_count_bytes(f2);

			success = _aif_cmp_float(res, *d1, bytes1, *d2, bytes2);

			*d1 += bytes1;
			*d2 += bytes2;

			return success;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}
		break;

	case AIF_CHARACTER:
		switch ( FDSType(*f2) )
		{
		case AIF_CHARACTER:
			bytes1 = _fds_count_bytes(f1);
			bytes2 = _fds_count_bytes(f2);

			success = _aif_cmp_char(res, *d1, *d2);

			*d1 += bytes1;
			*d2 += bytes2;

			return success;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}
		break;

	case AIF_ARRAY:
		switch ( FDSType(*f2) )
		{
		case AIF_ARRAY:
			ix1 = FDSArrayIndexInit(*f1);
			ix2 = FDSArrayIndexInit(*f2);

			/* 
			** skip over the entire array fds
			*/
			_fds_advance(f1);
			_fds_advance(f2);

			*res = 0; /* first assumption, they are equal */

			if ( ix1->i_nel != ix2->i_nel )
			{
				SetAIFError(AIFERR_SIZE, NULL);
				AIFArrayIndexFree(ix1);
				AIFArrayIndexFree(ix2);

				*res = 1;
				return -1;
			}

			for ( i = 0 ; i < ix1->i_nel ; i++ )
			{
				char *	fmt1;
				char *	fmt2;

				fmt1 = ix1->i_btype;
				fmt2 = ix2->i_btype;

				success = _aif_compare(depth, res, &fmt1, d1, &fmt2, d2);
				if ( success < 0 )
				{
					AIFArrayIndexFree(ix1);
					AIFArrayIndexFree(ix2);
					/* Do not need to set 'res' here,
					 * It's already set from the call
					 * to _aif_compare()
					 */
					return -1;
				}

				if ( *res != 0 )
				{
					/* 
					** no need to continue
					*/
					AIFArrayIndexFree(ix1);
					AIFArrayIndexFree(ix2);
					return 0;
				}
			}

			AIFArrayIndexFree(ix1);
			AIFArrayIndexFree(ix2);

			return 0;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}

	/*
	** only uses the public section in AIF_AGGREGATE
	*/
	case AIF_AGGREGATE:
		/* 
		** aggregate equivalence: we ignore field names
		*/

		if ( FDSType(*f2) != AIF_AGGREGATE )
		{
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}

		if ( AIFGetOption(AIFOPT_CMP_METHOD) == AIF_CMP_BY_NAME )
		{	
			cmp_by_name = 1;
			f2_x1 = *f2;
			d2_x1 = *d2;
			f2_x2 = NULL;
			d2_x2 = NULL;
			_fds_skip_data(&f2_x1, &d2_x1);
			if ( _fds_aggregate_arrange(*f1, *f2, *d2, &f2_x2, &d2_x2) < 0 )
			{
				SetAIFError(AIFERR_CONV, NULL);
				*res = 1;
				return -1;
			}

			*f2 = f2_x2; 
			*d2 = d2_x2;
			/* at the end, we assign f2_x1 to f2, d2_x1 to d2,
			** and we free f2_x2 and d2_x2 */
		}

		(*f1)++; /* past open brace */
		(*f2)++; /* past open brace */
		_fds_skip_typename(f1);
		_fds_skip_typename(f2);

		*res = 0; /* first assumption, they are equal */

		while ( **f1 != FDS_AGGREGATE_ACCESS_SEP )
		{
			if ( **f2 == FDS_AGGREGATE_ACCESS_SEP )
			{ 
				/* 
				** f1 has more fields
				*/
				SetAIFError(AIFERR_CONV, NULL);

				*res = 1; /* in a sense, d1 > d2 */

				if (cmp_by_name == 1)
				{
					_aif_free(f2_x2);
					_aif_free(d2_x2);
					*f2 = f2_x1;
					*d2 = d2_x1;
				}
				return -1;
			}

			*f1 = strchr(*f1, FDS_AGGREGATE_FIELD_NAME_END) + 1; /* to start of field */
			*f2 = strchr(*f2, FDS_AGGREGATE_FIELD_NAME_END) + 1; /* to start of field */

			success = _aif_compare(depth, res, f1, d1, f2, d2);

			if ( success < 0 )
			{
				if (cmp_by_name == 1)
				{
					_aif_free(f2_x2);
					_aif_free(d2_x2);
					*f2 = f2_x1;
					*d2 = d2_x1;
				}
				/* Do not need to set 'res' here,
				 * It's already set from the call
				 * to _aif_compare()
				 */
				return -1;
			}

			/*
			** non-zero res means no need to look further
			*/
			if ( *res )
			{
				if (cmp_by_name == 1)
				{
					_aif_free(f2_x2);
					_aif_free(d2_x2);
					*f2 = f2_x1;
					*d2 = d2_x1;
				}
				return 0;
			}

			if ( **f1 == FDS_AGGREGATE_FIELD_SEP )
				(*f1)++;

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

		if ( **f2 != FDS_AGGREGATE_ACCESS_SEP )
		{
			/*
			** f2 has more fields 
			*/
			SetAIFError(AIFERR_CONV, NULL);

			*res = -1; /* in a sense, d1 < d2 */
			
			if (cmp_by_name == 1)
			{
				_aif_free(f2_x2);
				_aif_free(d2_x2);
				*f2 = f2_x1;
				*d2 = d2_x1;
			}
			return -1;
		}

		*f1 = strchr(*f1, FDS_AGGREGATE_END) + 1; /*past close brace*/
		*f2 = strchr(*f2, FDS_AGGREGATE_END) + 1; /*past close brace*/

		if (cmp_by_name == 1)
		{
			_aif_free(f2_x2);
			_aif_free(d2_x2);
			*f2 = f2_x1;
			*d2 = d2_x1;
		}
		return 0;

	case AIF_STRING:
		if ( FDSType(*f2) != AIF_STRING )
		{
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}

		(*f1)++; /* past "s" */
		(*f2)++; /* past "s" */

		bytes1 = (*(*d1)++ & 0xff) << 8;
		bytes1 += *(*d1)++ & 0xff;
		bytes2 = (*(*d2)++ & 0xff) << 8;
		bytes2 += *(*d2)++ & 0xff;

		*res = strncmp(*d1, *d2, MIN(bytes1, bytes2));

		*d1 += bytes1;
		*d2 += bytes2;

		if ( *res )
			return 0;

		if ( bytes1 == bytes2 )
		{
			/* 
			** they are the same in length and contents
			*/
			return 0;
		}
		else
		{
			/*
			** they start the same, but one is longer
			*/
			*res = (bytes1 < bytes2) ? -1 : 1;
			return 0;
		}

	case AIF_BOOLEAN:
		bytes1 = _fds_count_bytes_na(f1);
		bytes2 = _fds_count_bytes_na(f2);

		switch ( FDSType(*f2) )
		{
		case AIF_BOOLEAN:
			_aif_to_longest(*d1, bytes1, &val1);
			_aif_to_longest(*d2, bytes2, &val2);

			if( val1 == val2)
				*res = 0;
			else
				*res = 1;
			
			*d1 += bytes1;
			*d2 += bytes2;

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			*res = 1;
			return -1;
		}

	case AIF_VOID:
		if ( FDSType(*f2) == AIF_VOID )
			*res = 0;
		else
			*res = 1;

		return 0;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		*res = 1;
		return -1;
	}
}

int
_aif_eps(char *lo_fds, char *lo_data, int lo_len, char *hi_fds, char *hi_data, int hi_len, char *a_fds, char *a_data, int a_len, int *val)
{
	int			ft;
	char *		fds_ref;	

	int			lo_i, hi_i, a_i;
	double		lo_d, hi_d, a_d;

	AIFLONGEST	lo_ll, hi_ll, a_ll;
	AIFDOUBLEST	lo_dd, hi_dd, a_dd;

	ResetAIFError();

	_fds_resolve(&a_fds);

	if ( FDSType(a_fds) == AIF_REFERENCE )
	{
		fds_ref = _fds_lookup(&a_fds);
		return _aif_eps(lo_fds, lo_data, lo_len, hi_fds, hi_data, hi_len, fds_ref, a_data, a_len, val);
	}

	switch ( ft = FDSType(a_fds) )
	{
	case AIF_BOOLEAN:

		if ( FDSType(lo_fds) != AIF_BOOLEAN )
		{	
			SetAIFError(AIFERR_BADARG, NULL);
		 	return -1;
		}

		if ( _aif_to_longest(lo_data, lo_len, &lo_ll) < 0 ||
		     _aif_to_longest(a_data, a_len, &a_ll) < 0 )
			return -1;

		lo_i = (int) lo_ll;
		hi_i = (int) hi_ll;
		a_i = (int) a_ll;

		if ( lo_i == 0 && a_i == 0 )
			*val = -1;
		else if ( lo_i == -1 && a_i == 0 )
			*val = 0;
		else if ( a_i == -1 )
			*val = 1;

		return 0;
		
	case AIF_INTEGER:
	case AIF_CHARACTER:
	case AIF_ENUM:
		
		if ( FDSType(lo_fds) != ft ||
		     FDSType(hi_fds) != ft )
		{	
			SetAIFError(AIFERR_BADARG, NULL);
	 		return -1;
		}

		if ( _aif_to_longest(lo_data, lo_len, &lo_ll) < 0 ||
		     _aif_to_longest(a_data, a_len, &a_ll) < 0 ||
		     _aif_to_longest(hi_data, hi_len, &hi_ll) < 0 )
			return -1;

		lo_i = (int) lo_ll;
		hi_i = (int) hi_ll;
		a_i = (int) a_ll;

		if ( a_i < lo_i )
			*val = -1;
		else if ( lo_i <= a_i && a_i <= hi_i )
			*val = 0;
		else if (a_i > hi_i )
			*val = 1;

		return 0;

	case AIF_STRING:

		if ( FDSType(lo_fds) != AIF_STRING ||
		     FDSType(hi_fds) != AIF_STRING )
		{	
			SetAIFError(AIFERR_BADARG, NULL);
	 		return -1;
		}

		if ( _aif_to_longest(lo_data + 2, lo_len - 2, &lo_ll) < 0 ||
		     _aif_to_longest(a_data + 2, a_len - 2, &a_ll) < 0 ||
		     _aif_to_longest(hi_data + 2, hi_len - 2, &hi_ll) < 0 )
			return -1;

		lo_i = (int) lo_ll;
		hi_i = (int) hi_ll;
		a_i = (int) a_ll;

		if ( a_i < lo_i )
			*val = -1;
		else if ( lo_i <= a_i && a_i <= hi_i )
			*val = 0;
		else if (a_i > hi_i )
			*val = 1;

		return 0;

	case AIF_FLOATING:
		
		if ( FDSType(lo_fds) != AIF_FLOATING ||
		     FDSType(hi_fds) != AIF_FLOATING )
		{	
			SetAIFError(AIFERR_BADARG, NULL);
	 		return -1;
		}

		if ( _aif_to_doublest(lo_data, lo_len, &lo_dd) < 0 ||
		     _aif_to_doublest(a_data, a_len, &a_dd) < 0 ||
		     _aif_to_doublest(hi_data, hi_len, &hi_dd) < 0 )
			return -1;

		lo_d = (int) lo_dd;
		hi_d = (int) hi_dd;
		a_d = (int) a_dd;

		if ( a_d < lo_d )
			*val = -1;
		else if ( lo_d <= a_d && a_d <= hi_d )
			*val = 0;
		else if (a_d > hi_d )
			*val = 1;

		return 0;

	case AIF_ARRAY:
		{
			int 		tmp, i;
			AIFIndex *	ix;

			char *		t_fmt;
			char *		t_dt1;
			char *		t_dt2;
			int		t_len;

			ix = FDSArrayIndexInit(a_fds);

			t_dt1 = a_data;

			for ( i = 0 ; i < ix->i_nel ; i++ )
			{
	
				t_fmt = ix->i_btype;
				t_dt2 = t_dt1;

				_fds_skip_data(&t_fmt, &t_dt1);
				t_len = t_dt1 - t_dt2;

				if ( _aif_eps(lo_fds, lo_data, lo_len, hi_fds, hi_data, hi_len, ix->i_btype, t_dt2, t_len, &tmp) < 0 )
				{
					AIFArrayIndexFree(ix);
					return -1;
				}

				if ( tmp == 0 || tmp == 1 )
				{
					AIFArrayIndexFree(ix);
					*val = tmp;
					return 0;
				}
			}

			AIFArrayIndexFree(ix);
			*val = -1;
			return 0;
		}

	/* only works with the public section in AIF_AGGREGATE */
	case AIF_AGGREGATE:
		{
			int 		tmp;

			char *		t_ft1;
			char *		t_ft2;
			char *		t_dt1;
			char *		t_dt2;
			int		t_len;

			a_fds++;
			_fds_skip_typename(&a_fds);

			t_dt1 = a_data;
			t_ft1 = a_fds;

			while ( *t_ft1 != ';' )
			{
	
				t_ft1 = strchr(t_ft1, FDS_AGGREGATE_FIELD_NAME_END) + 1;

				t_dt2 = t_dt1;
				t_ft2 = t_ft1;

				_fds_skip_data(&t_ft1, &t_dt1);
				t_len = t_dt1 - t_dt2;

				if ( _aif_eps(lo_fds, lo_data, lo_len, hi_fds, hi_data, hi_len, t_ft2, t_dt2, t_len, &tmp) < 0 )
				{
					return -1;
				}

				if ( tmp == 0 || tmp == 1 )
				{
					*val = tmp;
					return 0;
				}
			}

			*val = -1;
			return 0;
		}

	case AIF_POINTER:
		{
			int	tmp;

			// FIXME:_find_target(a_fds, a_data, &a_len, 0);

			if ( _aif_eps(lo_fds, lo_data, lo_len, hi_fds, hi_data, hi_len, a_fds, a_data, a_len, &tmp) < 0 )
			{
				return -1;
			}
			
			*val = tmp;
			return 0;
		}

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}
}

/* AIFEPS
 *
 * return_value is in val
 *
 * AIF_BOOLEAN (type of lo & hi is AIF_BOOLEAN)
 * val = -1		if	!lo & !a
 * val = 0		if	lo & !a
 * val = 1		if 	a
 *
 * AIF_INTEGER, AIF_CHAR, AIF_ENUM, AIF_STRING (type of lo & hi is AIF_INTEGER)
 * val = -1		if	a < lo
 * val = 0		if	lo <= a <= hi
 * val = 1		if	a > hi
 *
 * AIF_FLOATING (type of lo & hi is AIF_FLOATING)
 * val = -1		if	a < lo
 * val = 0		if	lo <= a <= hi
 * val = 1		if	a > hi
 *
 * AIF_ARRAY (type of lo & hi is the base of AIF_ARRAY)
 * val = -1		if	eps(a[i]) == -1 for ALL i
 * val = 0		if	eps(a[i]) == 0 for ANY i
 * val = 1		if	eps(a[i]) == 1 for ANY i
 *
 * AIF_AGGREGATE (type of lo & hi is AIF_AGGREGATE)
 * val = -1		if	eps(element[i]) == -1 for ALL i
 * val = 0		if	eps(element[i]) == 0 for ANY i
 * val = 1		if	eps(element[i]) == 1 for ANY i
 *
 * AIF_POINTER (type of lo & hi is the base of AIF_POINTER)
 * val = eps(a)		if	a = *p
 *
 * Other types
 * ERROR
 */

int
AIFEPS(AIF *lo, AIF *hi, AIF *a, int *val)
{
	return _aif_eps(AIF_FORMAT(lo), AIF_DATA(lo), AIF_LEN(lo), AIF_FORMAT(hi), AIF_DATA(hi), AIF_LEN(hi), AIF_FORMAT(a), AIF_DATA(a), AIF_LEN(a), val);
}


AIF *
AIFDiff(int depth, AIF *a1, AIF *a2)
{
	int		rl;
	char *  rd = NULL;
	char *  rf = NULL;
	char *  f1;
	char *  f2;
	char *  d1;
	char *  d2;

	if ( a1 == (AIF *)NULL || a2 == (AIF *)NULL )
	{
			SetAIFError(AIFERR_BADARG, NULL);
			return NULL;
	}

	f1 = AIF_FORMAT(a1);
	f2 = AIF_FORMAT(a2);
	d1 = AIF_DATA(a1);
	d2 = AIF_DATA(a2);

	if ( _aif_diff(depth, &rf, &rd, &rl, &f1, &d1, &f2, &d2) < 0 )
			return (AIF *)NULL;

	return _make_aif(rf, rd, rl);
}

/*
 * Compare two AIF values. Generates an AIF value that represents the
 * 'difference' between the values. Returns 0 for success or -1 on error.
 *
 * In the case of scalar values the difference is: val1 - val2
 * In the case of booleans it is TRUE if they are different
 * In the case of strings it is a string containing '1' in the positions
 *   where characters differ and '0' in positions where they are the same.
 * In the case of null pointers, the difference is a null pointer
 * 
 * Complex objects can also be compared. In this case the above rules are
 * applied to each field or element in the object.
 */
int
_aif_diff(int depth, char **rf, char **rd, int *rl,
	  	     char **f1, char **d1,
	  	     char **f2, char **d2)
{
	int	i;
	int	bytes1;
	int	bytes2;
	char *	fmt;
	int	datalen; /* to keep track of the length of the resultant data */
	int	currentlen; /* current data length */

	/* these cmp_by_name, f2_x* and d2_x* are used by 'case AIF_AGGREGATE' */
	int	cmp_by_name = 0;
	char *	f2_x1;
	char *	f2_x2;
	char *	d2_x1;
	char *	d2_x2;

	ResetAIFError();

	_fds_resolve(f1);
	_fds_resolve(f2);

	if ( FDSType(*f1) == AIF_REFERENCE )
	{
		fmt = _fds_lookup(f1);
		return _aif_diff(depth, rf, rd, rl, &fmt, d1, f2, d2);
	}

	if ( FDSType(*f2) == AIF_REFERENCE )
	{
		fmt = _fds_lookup(f2);
		return _aif_diff(depth, rf, rd, rl, f1, d1, &fmt, d2);
	}

	switch ( FDSType(*f1) )
	{
	case AIF_INTEGER:
	case AIF_FLOATING:
	case AIF_CHARACTER:
	case AIF_ADDRESS:
		return _aif_binary_op(AIFOP_SUB, rf, rd, rl, f1, d1, f2, d2);

	/* returns FALSE if they are the same */	
	case AIF_BOOLEAN:
		bytes1 = _fds_count_bytes_na(f1);
		bytes2 = _fds_count_bytes_na(f2);

		switch ( FDSType(*f2) )
		{
		case AIF_BOOLEAN:
			{
				AIFLONGEST	val1;
				AIFLONGEST	val2;
				AIFLONGEST	val3;
			
				*rl = MAX(bytes1, bytes2);

				if ( *rd == NULL )
					*rd = (char *)_aif_alloc(*rl);

				_aif_to_longest(*d1, bytes1, &val1);
				_aif_to_longest(*d2, bytes2, &val2);
				val3 = val1 ^ val2;
				_longest_to_aif(rd, *rl, val3);

				if ( *rf == NULL )
					*rf = strdup(AIF_BOOLEAN_TYPE(*rl));

				*d1 += bytes1;
				*d2 += bytes2;

				_fds_advance(f1);
				_fds_advance(f2);
			}

			return 0;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			return -1;
		}
		break;

	case AIF_ARRAY:
		{
			AIFIndex *	ix1;
			AIFIndex *	ix2;
			char *		fmt1;
			char *		fmt2;
			char *		fres = NULL;
			char *		dres = NULL;

			if ( FDSType(*f2) != AIF_ARRAY )
			{
				SetAIFError(AIFERR_TYPE, NULL);
				return -1;
			}

			ix1 = FDSArrayIndexInit(*f1);
			ix2 = FDSArrayIndexInit(*f2);

			if ( ix1->i_nel != ix2->i_nel )
			{
				SetAIFError(AIFERR_SIZE, NULL);
				AIFArrayIndexFree(ix1);
				AIFArrayIndexFree(ix2);
				return -1;
			}

			if ( *rd == NULL )
			{
				/* We allocate rd as big as d1.
				* Note: Since _aif_diff can produce result which
				* is bigger than its arguments, (eg: _aif_diff
				* with STRINGS and POINTERS), we also need to
				* keep track of the length of rd, and resize it
				* when it's not enough.
				*/

				char * temp1 = *f1;
				char * temp2 = *d1;
				int    temp3 = 0;

				_fds_skip_data(&temp1, &temp2);
				temp3 = temp2 - (*d1);
				*rd = (char *)_aif_alloc(temp3);
				datalen = temp3;
			}

			if ( *rf == NULL )
				*rf = strdup(*f1);

			_fds_advance(f1); /* skip over the entire array fds */
			_fds_advance(f2);

			/*
			** If nel[12] > MAX_ADDRESS then we are not going to be
			** able to address all the data in the array. What should
			** we do?
			*/

			currentlen = 0; /* start of our data result region */

			for ( i = 0 ; i < ix1->i_nel ; i++ )
			{
				int	dl;

				if ( dres != NULL )
				{
					_aif_free(dres);
					dres = NULL;
				}

				if ( fres != NULL )
				{
					_aif_free(fres);
					fres = NULL;
				}

				fmt1 = ix1->i_btype;
				fmt2 = ix2->i_btype;

				if ( _aif_diff(depth, &fres, &dres, &dl, &fmt1, d1, &fmt2, d2) < 0 )
				{
					AIFArrayIndexFree(ix1);
					AIFArrayIndexFree(ix2);
					return -1;
				}

				AIFArrayIndexInc(ix1);
				AIFArrayIndexInc(ix2);

				/* Is the *rd large enough? */
				if (datalen < (currentlen + dl))
				{
					*rd = _aif_resize(*rd, datalen + BUFSIZ);
					datalen += BUFSIZ;
				}

				memcpy(*rd + currentlen, dres, dl);
				currentlen += dl;
			}

			*rl = currentlen;

			{
				/* we need to modify 'rf' because it is
				 * possible that _aif_diff returns something
				 * that has a different fds than the operands
				 */

				char *t1, *t2;
				int len;

				t1 = _fds_base_type(*rf);

				/* if it is multi-dimensional array, move t1
				 * to the base type
				 */

				while ( *t1 == FDS_ARRAY_START )
					t1 = _fds_base_type(t1);

				/* we want to keep the FDS_NAME */
				while ( *t1 == FDS_NAME )
				{
					t1++;
					t1 = (char *)_fds_skipnum(t1)+1;
				}

				t2 = t1;
				_fds_advance(&t2);

				len = strlen(fres);
				memmove(t1, fres, len);
				t1 += len;

				/* this part is necessary because it
				 * is possible that the array is part of a
				 * larger structure (ie: we don't want to cut
				 * the rest of the fds)
				 */
				if ( *t2 != '\0' )
				{
					len = strlen(t2);
					memmove(t1, t2, len+1);
				}
				else
					*t1 = '\0';
			}

			_aif_free(fres);
			_aif_free(dres);

			fmt = *rf;
			_fds_advance(&fmt);
			*fmt = '\0';

			AIFArrayIndexFree(ix1);
			AIFArrayIndexFree(ix2);
		}

		return 0;

	/* only works with the public section in AIF_AGGREGATE */
	case AIF_AGGREGATE:
		if ( FDSType(*f2) != AIF_AGGREGATE )
		{
			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}

		if ( AIFGetOption(AIFOPT_CMP_METHOD) == AIF_CMP_BY_NAME )
		{	
			cmp_by_name = 1;
			f2_x1 = *f2;
			d2_x1 = *d2;
			f2_x2 = NULL;
			d2_x2 = NULL;
			_fds_skip_data(&f2_x1, &d2_x1);
			if ( _fds_aggregate_arrange(*f1, *f2, *d2, &f2_x2, &d2_x2) < 0 )
			{
				SetAIFError(AIFERR_CONV, NULL);
				return -1;
			}
			*f2 = f2_x2; 
			*d2 = d2_x2;
			/* at the end, we assign f2_x1 to f2, d2_x1 to d2,
			** and we free f2_x2 and d2_x2 */
		}

		if ( *rd == NULL )
		{
			/* We allocate rd as big as d1.
			* Note: Since _aif_diff can produce result which
			* is bigger than its arguments, (eg: _aif_diff
			* with STRINGS and POINTERS), we also need to
			* keep track of the length of rd, and resize it
			* when it's not enough.
			*/

			char * temp1 = *f1;
			char * temp2 = *d1;
			int    temp3 = 0;

			_fds_skip_data(&temp1, &temp2);
			temp3 = temp2 - (*d1);
			*rd = (char *)_aif_alloc(temp3);
			datalen = temp3;
		}

		if ( *rf == NULL )
			*rf = strdup(*f1);

		currentlen = 0; /* start of our data result region */

		(*f1)++; /* past open brace */
		(*f2)++; /* past open brace */
		_fds_skip_typename(f1);
		_fds_skip_typename(f2);

		while ( **f1 != ';' )
		{ /* do one field */
			int		dl;
			char *	dres = NULL;
			char *	fres = NULL;

			if (**f2 == ';') { /* f1 has more fields */
				SetAIFError(AIFERR_TYPE, NULL);
				if (cmp_by_name == 1)
				{
					_aif_free(f2_x2);
					_aif_free(d2_x2);
					*f2 = f2_x1;
					*d2 = d2_x1;
				}
				return -1;
			}

			*f1 = strchr(*f1, FDS_AGGREGATE_FIELD_NAME_END) + 1; /* to start of field*/
			*f2 = strchr(*f2, FDS_AGGREGATE_FIELD_NAME_END) + 1; /* to start of field*/

			fmt = *f1;

			if ( _aif_diff(depth, &fres, &dres, &dl, f1, d1, f2, d2) < 0 )
			{
				if (cmp_by_name == 1)
				{
					_aif_free(f2_x2);
					_aif_free(d2_x2);
					*f2 = f2_x1;
					*d2 = d2_x1;
				}
				return -1; /* non-zero res means no need to look further */
			}

			/* Is the *rd large enough? */
			if (datalen < (currentlen + dl))
			{
				*rd = _aif_resize(*rd, datalen + BUFSIZ);
				datalen += BUFSIZ;
			}

			memcpy(*rd + currentlen, dres, dl);
			currentlen += dl;

			{
				char *t1, *t2;
				int len;

				t1 = strstr(*rf, fmt);
				t2 = t1;
				_fds_advance(&t2);

				len = strlen(fres);
				memmove(t1, fres, len);
				t1 += len;

				if ( t1 != t2 )
				{
					len = strlen(t2);
					memmove(t1, t2, len+1);
				}
			}

			_aif_free(fres);
			_aif_free(dres);

			if ( **f1 == ',' )
				(*f1)++;

			if ( **f2 == ',' )
				(*f2)++;
		} /* one field */

		*rl = currentlen;

		if ( **f2 != ';' )
		{ /* f2 has more fields */
			if (cmp_by_name == 1)
			{
				_aif_free(f2_x2);
				_aif_free(d2_x2);
				*f2 = f2_x1;
				*d2 = d2_x1;
			}
			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}

		fmt = *rf;
		_fds_advance(&fmt);
		*fmt = '\0';

		*f1 = strchr(*f1, '}') + 1; /* to start of field*/
		*f2 = strchr(*f2, '}') + 1; /* to start of field*/

		if (cmp_by_name == 1)
		{
			_aif_free(f2_x2);
			_aif_free(d2_x2);
			*f2 = f2_x1;
			*d2 = d2_x1;
		}
		return 0;

	case AIF_POINTER:
		if ( FDSType(*f2) != AIF_POINTER )
		{
			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}
		else
		{
			int    ret;
			int    len;

			int t1 = _get_pointer_type(*d1);
			int t2 = _get_pointer_type(*d2);

			if ( depth == -1 )
			{
				if ( *rf == NULL )
				{
					char *tmp2 = (t1 == AIF_PTR_NIL) ? *f1 : *f2;
					char *tmp1 = tmp2;
					_fds_advance(&tmp1);
					len = tmp1 - tmp2;
					*rf = _aif_alloc(sizeof(char)*len + 1);
					strncpy(*rf, tmp2, len);
					(*rf)[len] = '\0';
				}

				if ( *rd != NULL )
					_aif_free(*rd);
				*rd = _aif_alloc(1);
				*rl = 1;
				**rd = AIF_PTR_NIL;

				_fds_skip_data(f1, d1);
				_fds_skip_data(f2, d2);

				return 0;

			}
			else if ( depth == 1 )
				depth = -1;
			else if ( depth > 1 )
				depth--;

			/*
			 * Can't compute diff of invalid pointers
			 */
			if ( t1 == AIF_PTR_INVALID || t2 == AIF_PTR_INVALID )
			{
				_fds_skip_data(f1, d1);
				_fds_skip_data(f2, d2);

				SetAIFError(AIFERR_ARITH, NULL);

				return -1;
			}

			if ( t1 == AIF_PTR_REFERENCE ||
			     t2 == AIF_PTR_REFERENCE )
			{
				if ( *rf == NULL )
				{
					char *tmp2 = (t1==AIF_PTR_REFERENCE)? *f1 : *f2;
					char *tmp1 = tmp2;
					_fds_advance(&tmp1);
					len = tmp1 - tmp2;
					*rf = _aif_alloc(sizeof(char)*len + 1);
					strncpy(*rf, tmp2, len);
					(*rf)[len] = '\0';
				}

				if ( *rd != NULL )
					_aif_free(*rd);
				*rd = _aif_alloc(5); /* 1 + 4 (ptr name) */
				*rl = 5;

				if (t1 == AIF_PTR_REFERENCE) {
					memcpy(*rd, d1, 5);
				} else {
					memcpy(*rd, d2, 5);
				}

				_fds_skip_data(f1, d1);
				_fds_skip_data(f2, d2);

				return 0;
			}

			/*
			 * target1 is null, make target2 as the result
			 */
			if ( t1 == AIF_PTR_NIL )
			{
				if ( *rf == NULL )
				{
					char * tmp_f = *f2;
					_fds_advance(&tmp_f);
					len = tmp_f - *f2;
					*rf = _aif_alloc(sizeof(char)*len + 1);
					strncpy(*rf, *f2, len);
					(*rf)[len] = '\0';
				}

				if ( *rd != NULL )
					_aif_free(*rd);

				{
					char * tmp_d = *d2;
					_fds_skip_data(f2, d2);
					len = *d2 - tmp_d;
					*rd = _aif_alloc(sizeof(char)*len);
					*rl = len;
					memcpy(*rd, tmp_d, len);
				}

				_fds_skip_data(f1, d1);

				return 0;
			}

			/*
			 * target2 is null, make target1 as the result
			 */
			if ( t2 == AIF_PTR_NIL )
			{
				if ( *rf == NULL )
				{
					char * tmp_f = *f1;
					_fds_advance(&tmp_f);
					len = tmp_f - *f1;
					*rf = _aif_alloc(sizeof(char)*len + 1);
					strncpy(*rf, *f1, len);
					(*rf)[len] = '\0';
				}

				if ( *rd != NULL )
					_aif_free(*rd);

				{
					char * tmp_d = *d1;
					_fds_skip_data(f1, d1);
					len = *d1 - tmp_d;
					*rd = _aif_alloc(sizeof(char)*len);
					*rl = len;
					memcpy(*rd, tmp_d, len);
				}

				_fds_skip_data(f2, d2);

				return 0;
			}

			/*
			 * Allocate space for new fds and data.
			 * FIXME: what if length > BUFSIZE!
			 */
			if ( *rf == NULL )
			{
				*rf = _aif_alloc(BUFSIZ);
				**rf = '\0';
				strcat(*rf, "^");
			}

			if ( *rd != NULL )
				_aif_free(*rd);

			char * res_data = *rd = (char *)_aif_alloc(BUFSIZ);
			*res_data++ = (char) AIF_PTR_NORMAL;

			char *addr_fr = NULL;
			char *addr_dr = NULL;
			int addr_len;

			/*
			 * Skip to address
			 */
			(*f1)++;
			*d1 += (t1 == AIF_PTR_NAME) ? 5 : 1;
			(*f2)++;
			*d2 += (t2 == AIF_PTR_NAME) ? 5 : 1;

			/*
			 * Calculate address difference
			 */
			ret = _aif_diff(0, &addr_fr, &addr_dr, &addr_len, f1, d1, f2, d2);
			if (ret < 0)
				return ret;

			/*
			 * Copy address fds and data to result
			 */
			strcat(*rf, addr_fr);
			memcpy(res_data, addr_dr, addr_len);
			res_data += addr_len;

			char * target_f = NULL;
			char * target_d = NULL;

			/*
			 * Now calculate difference of target
			 */
			ret = _aif_diff(depth, &target_f, &target_d, rl, f1, d1, f2, d2);
			if (ret < 0)
				return ret;

			/*
			 * Copy target fds and result to result
			 */
			strcat(*rf, target_f);
			memcpy(res_data, target_d, *rl);

			return ret;

		}

	case AIF_STRING:
		if ( FDSType(*f2) != AIF_STRING )
		{
			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}

		(*f1)++; /* past "s" */
		(*f2)++; /* past "s" */

		bytes1 = (*(*d1)++ & 0xff) << 8;
		bytes1 += *(*d1)++ & 0xff;
		bytes2 = (*(*d2)++ & 0xff) << 8;
		bytes2 += *(*d2)++ & 0xff;

		if ( *rd != NULL )
			_aif_free(*rd);

		*rd = _aif_alloc(bytes1 + bytes2 + 2);
		*rl = bytes1 + bytes2 + 2;

		if ( bytes1 == bytes2 )
		{
			int i;
			int is_equal = 1;
			
			(*rd)[0] = (bytes1 >> 8) & 0xff;
			(*rd)[1] = bytes1 & 0xff;

            for (i=0; i<bytes1; i++)
			{
				if ( strncmp((*d1)+i, (*d2)+i, 1) )
				{
					(*rd)[2+i] = '1';
					is_equal = 0;
				}
				else
					(*rd)[2+i] = '0';
			}

			if ( is_equal == 1 )
			{
				(*rd)[0] = (0 >> 8) & 0xff;
				(*rd)[1] = 0 & 0xff;
			}
		}
		else if ( bytes1 > bytes2 )
		{
			int i;

			(*rd)[0] = (bytes1 >> 8) & 0xff;
			(*rd)[1] = bytes1 & 0xff;

			for ( i = 0 ; i < bytes2 ; i++ )
				(*rd)[2+i] = strncmp((*d1)+i, (*d2)+i, 1) ? '1' : '0';
			for ( i = 0 ; i < bytes1 - bytes2 ; i++ )
				(*rd)[bytes2+2+i] = '1';
			
		}
		else if ( bytes1 < bytes2 )
		{
			int i;

			(*rd)[0] = (bytes2 >> 8) & 0xff;
			(*rd)[1] = bytes2 & 0xff;

			for ( i = 0 ; i < bytes1 ; i++ )
				(*rd)[2+i] = strncmp((*d1)+i, (*d2)+i, 1) ? '1' : '0';
			for ( i = 0 ; i < bytes2 - bytes1 ; i++ )
				(*rd)[bytes1+2+i] = '1';

		}

		*d1 += bytes1;
		*d2 += bytes2;

		if ( *rf == NULL )
			*rf = strdup(AIF_STRING_TYPE());

		return 0;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}
}







