#ifndef _DEPOOLMULTISET_H
#define _DEPOOLMULTISET_H
/*-------------------------------------------------------------------------
 * drawElements Memory Pool Library
 * --------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Memory pool multiset class.
 *//*--------------------------------------------------------------------*/

#include "deDefs.h"
#include "deMemPool.h"
#include "dePoolHash.h"
#include "deInt32.h"

DE_BEGIN_EXTERN_C

void	dePoolMultiSet_selfTest		(void);

DE_END_EXTERN_C

/*--------------------------------------------------------------------*//*!
 * \brief Declare a template pool multiset class interface.
 * \param TYPENAME	Type name of the declared multiset.
 * \param KEYTYPE	Type of the key.
 *
 * This macro declares the interface for a multiset. For the implementation
 * of the multiset, see DE_IMPLEMENT_POOL_MULTISET. Usually this macro is put
 * into the header file and the implementation macro is put in some .c file.
 *
 * \todo [petri] Detailed description.
 *
 * The functions for operating the multiset are:
 * \todo [petri] Figure out how to comment these in Doxygen-style.
 *
 * \code
 * MultiSet* MultiSet_create            (deMemPool* pool);
 * int       MultiSet_getNumElements    (const MultiSet* set);
 * deBool    MultiSet_exists            (const MultiSet* set, Key key);
 * deBool    MultiSet_insert            (MultiSet* set, Key key);
 * void      MultiSet_delete            (MultiSet* set, Key key);
 * int       MultiSet_getKeyCount       (const MultiSet* set, Key key);
 * deBool    MultiSet_setKeyCount       (MultiSet* set, Key key, int count);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_MULTISET(TYPENAME, KEYTYPE)		\
\
DE_DECLARE_POOL_HASH(TYPENAME##Hash, KEYTYPE, int);	\
\
typedef struct TYPENAME##_s				\
{										\
	deMemPool*			pool;			\
	int					numElements;    \
	TYPENAME##Hash*		hash;			\
} TYPENAME; /* NOLINT(TYPENAME) */		\
\
TYPENAME*	TYPENAME##_create		(deMemPool* pool);    \
void		TYPENAME##_reset		(DE_PTR_TYPE(TYPENAME) set);    \
deBool		TYPENAME##_setKeyCount	(DE_PTR_TYPE(TYPENAME) set, KEYTYPE key, int newCount);	\
\
DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* set)    \
{    \
	return set->numElements;    \
}    \
\
DE_INLINE int TYPENAME##_getKeyCount (const TYPENAME* set, KEYTYPE key)	\
{	\
	int* countPtr	= TYPENAME##Hash_find(set->hash, key);	\
	int  count		= countPtr ? *countPtr : 0;	\
	DE_ASSERT(count > 0 || !countPtr);	\
	return count;	\
}	\
\
DE_INLINE deBool TYPENAME##_exists (const TYPENAME* set, KEYTYPE key)    \
{    \
	return (TYPENAME##_getKeyCount(set, key) > 0);	\
}    \
\
DE_INLINE deBool TYPENAME##_insert (DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)    \
{	\
	int oldCount = TYPENAME##_getKeyCount(set, key);	\
	return TYPENAME##_setKeyCount(set, key, oldCount + 1);	\
}	\
\
DE_INLINE void TYPENAME##_delete (DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)    \
{    \
	int oldCount = TYPENAME##_getKeyCount(set, key);	\
	DE_ASSERT(oldCount > 0);	\
	TYPENAME##_setKeyCount(set, key, oldCount - 1);	\
}    \
\
struct TYPENAME##DeclareDummy_s { int dummy; }

/*--------------------------------------------------------------------*//*!
 * \brief Implement a template pool multiset class.
 * \param TYPENAME	Type name of the declared multiset.
 * \param KEYTYPE	Type of the key.
 * \param HASHFUNC	Function used for hashing the key.
 * \param CMPFUNC	Function used for exact matching of the keys.
 *
 * This macro has implements the set declared with DE_DECLARE_POOL_MULTISET.
 * Usually this macro should be used from a .c file, since the macro expands
 * into multiple functions. The TYPENAME and KEYTYPE parameters
 * must match those of the declare macro.
*//*--------------------------------------------------------------------*/
#define DE_IMPLEMENT_POOL_MULTISET(TYPENAME, KEYTYPE, HASHFUNC, CMPFUNC)		\
\
DE_IMPLEMENT_POOL_HASH(TYPENAME##Hash, KEYTYPE, int, HASHFUNC, CMPFUNC);	\
\
TYPENAME* TYPENAME##_create (deMemPool* pool)    \
{   \
	/* Alloc struct. */ \
	DE_PTR_TYPE(TYPENAME) set = DE_POOL_NEW(pool, TYPENAME); \
	if (!set) \
		return DE_NULL; \
\
	/* Init. */ \
	memset(set, 0, sizeof(TYPENAME)); \
	set->pool = pool; \
\
	set->hash = TYPENAME##Hash_create(pool);	\
\
	return set; \
} \
\
void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) set)    \
{   \
	TYPENAME##Hash_reset(set->hash);	\
	set->numElements = 0;	\
}	\
\
deBool TYPENAME##_setKeyCount (DE_PTR_TYPE(TYPENAME) set, KEYTYPE key, int newCount)	\
{	\
	int* countPtr	= TYPENAME##Hash_find(set->hash, key);	\
	int  oldCount	= countPtr ? *countPtr : 0;	\
\
	DE_ASSERT(oldCount > 0 || !countPtr);	\
	DE_ASSERT(newCount >= 0);	\
	set->numElements += (newCount - oldCount);	\
\
	if (newCount == 0 && countPtr)	\
		TYPENAME##Hash_delete(set->hash, key);	\
	else if (newCount > 0 && countPtr)	\
		*countPtr = newCount;	\
	else if (newCount > 0)	\
		return TYPENAME##Hash_insert(set->hash, key, newCount);	\
	return DE_TRUE;	\
}	\
\
struct TYPENAME##ImplementDummy_s { int dummy; }

/*--------------------------------------------------------------------*//*!
 * \brief Declare set-wise operations for a multiset template.
 * \param TYPENAME	Type name of the declared set.
 * \param KEYTYPE	Type of the key.
 *
 * This macro declares union and intersection operations for a multiset.
 * For implementation see DE_IMPLEMENT_POOL_MULTISET_UNION_INTERSECT.
 *
 * \todo [petri] Detailed description.
 *
 * The functions for operating the set are:
 * \todo [petri] Figure out how to comment these in Doxygen-style.
 *
 * \code
 * deBool	MultiSet_union				(Set* to, const Set* a, const Set* b);
 * deBool	MultiSet_unionInplace		(Set* a, const Set* b);
 * deBool	MultiSet_intersect			(Set* to, const Set* a, const Set* b);
 * void		MultiSet_intersectInplace	(Set* a, const Set* b);
 * deBool   MultiSet_sum				(Set* to, const Set* a, const Set* b);
 * deBool   MultiSet_sumInplace			(Set* a, const Set* b);
 * deBool   MultiSet_difference			(Set* to, const Set* a, const Set* b);
 * void		MultiSet_differenceInplace	(Set* a, const Set* b);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_MULTISET_SETWISE_OPERATIONS(TYPENAME)										\
	deBool TYPENAME##_union (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b);		\
	deBool TYPENAME##_unionInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b);					\
	deBool TYPENAME##_intersect (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b);	\
	void TYPENAME##_intersectInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b);					\
	deBool TYPENAME##_sum (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b);			\
	deBool TYPENAME##_sumInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b);						\
	deBool TYPENAME##_difference (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b);	\
	void TYPENAME##_differenceInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b);					\
	struct TYPENAME##SetwiseDeclareDummy_s { int dummy; }

#define DE_IMPLEMENT_POOL_MULTISET_SETWISE_OPERATIONS(TYPENAME, KEYTYPE)	\
deBool TYPENAME##_union (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b)	\
{	\
	TYPENAME##_reset(to);	\
	return TYPENAME##_unionInplace(to, a) && TYPENAME##_unionInplace(to, b);	\
}	\
\
deBool TYPENAME##_unionInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b)	\
{	\
	TYPENAME##HashIter iter;	\
	for (TYPENAME##HashIter_init(b, &iter);	\
		 TYPENAME##HashIter_hasItem(&iter);	\
		 TYPENAME##HashIter_next(&iter))	\
	{	\
		KEYTYPE	key		= TYPENAME##HashIter_getKey(&iter);	\
		int		bCount	= TYPENAME##HashIter_getValue(&iter);	\
		int		aCount	= TYPENAME##_getKeyCount(a, key);	\
		int		count	= deMax32(aCount, bCount);	\
		if (bCount && !TYPENAME##_setKeyCount(a, key, aCount + bCount))	\
			return DE_FALSE;	\
	}	\
	return DE_TRUE;	\
}	\
\
deBool TYPENAME##_intersect (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b)	\
{	\
	TYPENAME##HashIter iter;	\
	TYPENAME##_reset(to);	\
	for (TYPENAME##HashIter_init(a, &iter);	\
		 TYPENAME##HashIter_hasItem(&iter);	\
		 TYPENAME##HashIter_next(&iter))	\
	{	\
		KEYTYPE key		= TYPENAME##HashIter_getKey(&iter);	\
		int		aCount	= TYPENAME##HashIter_getValue(&iter);	\
		int		bCount	= TYPENAME##_getKeyValue(b, key);	\
		int		count	= deMin32(aCount, bCount);	\
		if (count && !TYPENAME##_setKeyCount(to, key, count))	\
			return DE_FALSE;	\
	}	\
	return DE_TRUE;	\
}	\
\
void TYPENAME##_intersectInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b)	\
{	\
	DE_FATAL("Not implemented.");	\
}	\
\
deBool TYPENAME##_sum (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b)	\
{	\
	TYPENAME##_reset(to);	\
	return TYPENAME##_sumInplace(to, a) && TYPENAME##_sumInplace(to, b);	\
}	\
\
deBool TYPENAME##_sumInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b)	\
{	\
	TYPENAME##HashIter iter;	\
	for (TYPENAME##HashIter_init(b, &iter);	\
		 TYPENAME##HashIter_hasItem(&iter);	\
		 TYPENAME##HashIter_next(&iter))	\
	{	\
		KEYTYPE	key		= TYPENAME##HashIter_getKey(&iter);	\
		int		aCount	= TYPENAME##_getKeyValue(a, key);	\
		int		bCount	= TYPENAME##HashIter_getValue(&iter);	\
		int		count	= aCount + bCount;	\
		if (!TYPENAME##_setKeyCount(a, key, count))	\
			return DE_FALSE;	\
	}	\
}	\
\
deBool TYPENAME##_difference (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b)	\
{	\
	TYPENAME##HashIter iter;	\
	TYPENAME##_reset(to);	\
	for (TYPENAME##HashIter_init(a, &iter);	\
		 TYPENAME##HashIter_hasItem(&iter);	\
		 TYPENAME##HashIter_next(&iter))	\
	{	\
		KEYTYPE key		= TYPENAME##HashIter_getKey(&iter);	\
		int		aCount	= TYPENAME##HashIter_getValue(&iter);	\
		int		bCount	= TYPENAME##_getKeyValue(b, key);	\
		int		count	= deMax32(0, aCount - bCount);	\
		if (count && !TYPENAME##_setKeyCount(to, key, count))	\
			return DE_FALSE;	\
	}	\
	return DE_TRUE;	\
}	\
\
void TYPENAME##_differenceInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b)	\
{	\
	DE_FATAL("Not implemented.");	\
}	\
\
struct TYPENAME##SetwiseImplementDummy_s { int dummy; }

#endif /* _DEPOOLMULTISET_H */