/*
*	In this example we assume that we have very limited memory.
*	The example demonstrates a real-life problem which was solved the
*	way described below. In this example header files and and other
*	niceties are omitted, only the actual solution is shown.
*
*	The situation:
*	An application running on an NXP LPC23xx microcontroller.
*	The chip has 16K general purpose RAM. Furthermore, it has
*	an Ethernet controller and a USB controller, neither of which
*	is used by the application. The USB has a private 8K RAM block
*	which can be used as general memory if the USB is not in use.
*	The same is true for the Ethernet block, except that it has
*	16K RAM.
*
*	The application has to store information about some objects.
*	The max number of objects is TBD, but it must be at least 1000.
*	The unique key is 5 bytes. The application related data (on top
*	of the key) is 16 bytes.
*
*	The "database" is fairly often changed, thus something that is fast
*	to insert, delete and look up nodes is needed. The AVL tree meets
*	these requirements.
*
*	However, if the tree needs 2 pointers (4 bytes each) plus 1 byte
*	height plus 5 bytes key plus 16 bytes application data, then the
*	data structure is 30 bytes long. That will be rounded up to 32 bytes
*	by the compiler due to alignment requirements, so it would take 32K
*	to store the data. That would leave 8K for the rest of the application,
*	which is a bit tight. Naturally, the 16 bytes data plus the 5 bytes
*	key can't be saved. However, the 9 bytes AVL administrative info seems
*	to be a lot (almost 30% of the node size). Every byte saved in a node
*	is roughly an extra kilobyte for the rest of the code.
*
*	The solution is:
*
*	1.	We realise that since we use pre-allocated memory to store
*		the nodes, even if we use some malloc()/free() we will allocate
*		from an array of nodes.
*
*	2.	We also realise that since we use an array, we can split
*		nodes and store the parts in separate arrays, since the
*		array index links the node fragments at no extra cost.
*
*	Therefore, we could store the 16 byte application data in one array,
*	the key and the node administration in an other. The 16 byte data
*	consumes 16K RAM: a nice fit for the Ethernet RAM. Now if we could
*	pack the rest (the key and the AVL tree administrative data) into
*	the USB RAM somehow, that would leave the 16K general purpose RAM
*	completely free. Since the USB RAM is 8K, we have 8 bytes per node.
*	The key itself is 5 bytes. Thus, we have 3 bytes to store two node
*	references and the tree height. Currently we need 9 bytes for that.
*	So, how do we cram 9 bytes into 3?
*
*	The rest of the solution is:
*
*	3.	The AVL library uses node references and not pointers. We can use
*		array indices instead of pointers. Using unsigned short indices
*		decreases our memory consumption from 4+4+1=9 to 2+2+1=5 bytes.
*
*	4.	We only have to store 1000 nodes. A 1000 element array index is
*		only 10 bits. If we define the max number of nodes to 1023 (thus
*		exceeding the minimal requirement - payrise in order!), reserving
*		one value for the NULL reference, we could pack the two references
*		into 2*10 bits. 20 bits can be stored in 3 bytes, plus the byte for
*		the height of the subtree: we're down to 4 bytes AVL info per node.
*
*	5.	Consulting the table in the AVL documantation reveals that for a
*		tree with 1023 nodes the height information can be stored in 4 bits.
*		Thus, we have two 10-bit references and a 4-bit height. All together
*		24 bits, that is, 3 bytes. We managed to fit the key plus the AVL
*		information into 8 bytes, the problem has been solved.
*
*	Below is the implementation. Note that the data structure stored in
*	the Ethernet RAM and the associated access functions are not shown.
*	The example is only about the handling of the key plus AVL info,
*	stored in the USB RAM. The functions all return an array index,
*	that can be used to access the data in the Ethernet RAM.
*/

/*
*	Define an array of 1024 elements of 8 bytes each that is
*	in the USB RAM block. We define a pointer to 8 byte objects
*	and initialise it to the USB RAM's base address.
*/

static unsigned char (* const array)[ 8 ] = (void *) USB_RAM_ADDRESS;

/*
*	We will store the information in the 8 bytes as follows:
*	The first 5 (0-4) bytes are the key. Byte 5 will be the lower
*	8 bits of the left reference, byte 6 will store the lower 8 bits
*	of the right reference. The last byte has the height in its top
*	4 bits, then the next two bits store the upper two bits of the
*	left reference while the least significant two bits will hold the
*	upper two bits of the right reference. We define the node type
*	and the functions accordingly.
*/

typedef int			AVL_USER;		// We don't use it
typedef int			AVL_NODE;		// It is an index, actually
typedef char		*AVL_KEY;		// Pointer to the key
#define	AVL_PLEN	15				// As per the size table

/*
*	We have to define the AVL_NULL value. When working with arrays,
*	one would instinctively choose the array size as the NULL value,
*	since the indices go from 0 to N-1. However, we have memory to
*	store 1024 elements and we only store 1023 of them. Thus, we can
*	use 0 as the NULL value and index the array from 1 to 1023. Since
*	comparison against 0 is usually more efficient than comparison
*	against an arbitrary number, this choice gives us a slight speed
*	(and code size) advantage.
*/

#define	AVL_NULL	0


/*
*	We tell the AVL template that we need deletion
*/

#define	AVL_USE_DELETE


/*
*	GET functions
*/

static AVL_NODE	AVL_GET_L( AVL_NODE node, AVL_USER user )
{
	// If you use -W -Wall -Werror (as you should!), gcc gives you an
	// 'unused variable' error for unused parameters. Casting them to
	// void is using them without generating any code, so gcc is happy
	// and we're happy too. It also indicates to other people that you
	// intentionally ignored a function argument.

	(void) user;

	// The low 8 bits are in byte 5, the upper 2 bits are bits 3:2 in byte 7

	return array[ node ][ 5 ] | ( ( array[ node ][ 7 ] & 0x0c ) << 6 );
}

static AVL_NODE	AVL_GET_R( AVL_NODE node, AVL_USER user )
{
	// The low 8 bits are in byte 6, the upper 2 bits are bits 1:0 in byte 7

	(void) user;
	return array[ node ][ 6 ] | ( ( array[ node ][ 7 ] & 0x03 ) << 8 );
}

static unsigned	AVL_GET_H( AVL_NODE node, AVL_USER user )
{
	// The height is in bits 7:4 of byte 7

	(void) user;
	return array[ node ][ 7 ] >> 4;
}

/*
*	SET functions, the in same fashion as the getters.
*/

static void		AVL_SET_L( AVL_NODE node, AVL_NODE tree, AVL_USER user )
{
	(void) user;
	array[ node ][ 5 ] = tree;
	array[ node ][ 7 ] &= 0xf3;
	array[ node ][ 7 ] |= ( tree >> 8 ) << 2;
}

static void		AVL_SET_R( AVL_NODE node, AVL_NODE tree, AVL_USER user )
{
	(void) user;
	array[ node ][ 5 ] = tree;
	array[ node ][ 7 ] &= 0xfc;
	array[ node ][ 7 ] |= tree >> 8;
}

static void		AVL_SET_R( AVL_NODE node, unsigned height, AVL_USER user )
{
	(void) user;
	array[ node ][ 7 ] &= 0x0f;
	array[ node ][ 7 ] |= height << 4;
}

/*
*	The comparison function
*/

static int		AVL_CMP( AVL_NODE node, AVL_KEYT refk, AVL_USER user )
{
	// We have to compare two 5-byte keys. The address of the key of a
	// node is actually the address of the node itself, since the first 5
	// bytes of a node form the key. Furthermore, the memcmp() function
	// in the C library does what we need, so we have not much to do.

	(void) user;
	return memcmp( array[ node ], refk, 5 );
}

/*
*	Get the template
*/

#include <misc/avl.h>

/*
*	Define the free node list, the root and the workspace.
*/

static int		free;
static int 		root;
static AVL_PATH	work;

/*
*	Init
*	We know that our nodes are on word boundaries, thus it is safe to cast
*	a node address to a pointer to short. Therefore, we can build a singly
*	linked list of free nodes. Initially all nodes are free. So, each node
*	should contain an index 1 more than its own. The last free node, which
*	has the index of 1023, should contain AVL_NULL, i.e. 0 as its next.
*	We set the free node index to the first node in the chain, i.e. 1.
*/

void	MyDataInit( void )
{
int		i;

	for ( i = 1 ; i < 1023 ; i++ )

		*((unsigned short *) array[ node ]) = i + 1;

	*((unsigned short *) array[ 1023 ]) = AVL_NULL;

	free = 1;
	root = AVL_NULL;
}

/*
*	Search for a key.
*	The function returns the index or 0 if the key is not found.
*/

int		MyDataSearch( const char key[ 5 ] )
{
	// The AVL_Search() function does everything we need. Note that
	// we have to cast the key to AVL_KEYT. Although AVL_KEYT is
	// actually char * and the key is a character array, the cast
	// is needed because with -W -Wall gcc complains about the
	// const qualifier being omitted when we pass the key. The
	// explicit cast from const char * to char * keeps gcc happy.

	return AVL_Search( root, (AVL_KEYT) key, 0 );
}

/*
*	Insert a new node.
*	We return the index or 0 if the key is already on the tree
*	and -1 if we ran out of nodes.
*/

int		MyDataInsert( const char key[ 5 ] )
{
AVL_NODE	node;

	if ( free == AVL_NULL ) {

		// No free node, report error

		return -1;
	}

	// Allocate a node

	node = free;
	free = *((unsigned short *) array[ node ]);

	// Attempt insertion

	if ( AVL_Insert( &root, node, (AVL_KEYT) key, 0 ) != AVL_NULL ) {

		// The key is already on the tree. Release the node and
		// report error. Since the node was not changed by the
		// AVL_Insert routine, it is enough to restore free.

		free = node;
		return 0;
	}

	// The insertion was successful. Copy the key into the node and
	// return the index. The address of the key in the node is the
	// address of the node itself as the first 5 bytes of the node
	// contain the key.

	memcpy( array[ node ], key, 5 );
	return node;
}

/*
*	Delete a key.
*	If succeeded, then the index is returned. If the key was not on
*	the tree, 0 is returned.
*/

int		MyDataDelete( const char key[ 5 ] )
{
AVL_NODE	node;

	node = AVL_Delete( &root, &work, (AVL_KEYT) key, 0 );

	if ( node == AVL_NULL ) {

		// The key was not on the tree

		return 0;
	}

	// The key was deleted. Put the node back to the
	// free list and return the index.

	*((unsigned short *) array[ node ]) = free;
	free = node;
	return node;
}

