Newer
Older
Import / research / ui / TweakableProperties / toolkit / include / Containers.h
#ifndef CONTAINERS_H
#define CONTAINERS_H


#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <vector>
#include <string>
#include <map>
#include <stdarg.h>  // for va_start, etc


#include "Namespace.h"
BEGIN_NAMESPACE


class AbstractGrowthStrategy
{
public:
	virtual ~AbstractGrowthStrategy() {}
	virtual uint64_t calculateNewReservedSize(uint64_t a_oldSize, uint64_t a_newRequiredSize) = 0;
};


enum AllocationDomain
{
	AD_Stack,
	AD_Heap,
	AD_PagedToDisk
};


/*
enum ContainerPersistence
{
	CP_Volatile,
	CP_CacheFavoringMRU,
	CP_CacheFavoringMostAccessed,
	CP_BackedToRAM,
	CP_BackedToDisk
};
*/


enum ContainerType
{
	CT_ItemStore,
	CT_AssociativeSet,
	CT_MultiAssociativeSet,
	CT_Cache
};


enum ContainerCapabilities
{
	CC_KeyBasedLookup      = 1<<0,  // forward map   \  -
	CC_ValueBasedLookup    = 1<<0,  // reversed map  /  If both, then it is a bimap
	CC_IndexBasedLookup    = 1<<0,  // vector
	CC_IteratorBasedLookup = 1<<1,  // list

	CC_ForwardIterator     = 1<<2,  // list           \  -
	CC_BackwardsIterator   = 1<<3,  // reversed list  /  If both, then a doubly linked list

	CC_Prepend             = 1<<4,  // either a list, or space reserved before 1st element
	CC_Append              = 1<<5,  // either a list, or space reserved after last element
	CC_InsertWithIndex	   = 1<<6,  // 
	CC_InsertWithIterator  = 1<<6,  // 
	CC_SortedInsertions    = 1<<6,  // 

	CC_DeleteFront		   = 1<<7,
	CC_DeleteBack		   = 1<<8,
	CC_DeleteWithIndex     = 1<<9,
	CC_DeleteWithIterator  = 1<<10,

	CC_Sort,
	CC_Search,

	CC_Ephemeral,           // No history
	CC_Persistent,          // Undo/Redo

	CC_Serializable,
	CC_SharedMemory,

};


/*

idea: 
	unrolled linked list eg:
nodes:     ABCDEF ->  GHIJK -> LMNO -> PQRST   ( '->'  are the linking ptrs, A, B, C etc are nodes )
	with subtraction pointers, eg:
node:	A     B     C     D     E
ptrs:	     C-A   D-B   E-C

Can therefore iterate forwards or backwards
Sort of a compromise between vectors and lists

The subtraction pointers are good for serialization and de-serialization (or being in shared memory)

Something about the unrolled linked list is that it can balance itself between being more list like
with a smaller count per node collection, or more vector like if this is larger, so if it could dynamically
adjust this value as it is running to tune itself, then it might be able to find the optimial value and
balance. As it is running, if no inserts in the middle are being done, it could become more vector like,
but if it is more frequently doing this, it could become more list like.

Other data structures
	B-Trees
	VLists

queue - a FIFO queue, only can add at one end, and remove from the other
deque - doubly ended queue, bit like a linked list with a head and tail pointer, can add/remove from either end
queues and stacks are really sub-classes of deques

*/

template <typename T, size_t BlockSize>
class DequeBase
{
public:
	DequeBase() : m_headBlock(0), m_tailBlock(0), m_blockSize(BlockSize), m_nodeCount(0) {}

	~DequeBase()
	{
		// deallocate the blocks
	}

	void push_front(const T& a_item)
	{
		m_nodeCount++;
		if (m_headBlock && m_headBlock->m_frontIndex)
		{
			m_headBlock->m_frontIndex--;
			m_headBlock->m_data[m_headBlock->m_frontIndex] = a_item;
		} else {
			DequeNodeBlock* block = new DequeNodeBlock(a_item);
			block->m_nextBlock = m_headBlock;
			if (m_headBlock)
				m_headBlock->m_prevBlock = block;
			else
				m_tailBlock = block;
			m_headBlock = block;
		}
	}

	void push_back(const T& a_item)
	{
		m_nodeCount++;
		if (m_tailBlock && m_tailBlock->m_backIndex != (m_blockSize - 1))
		{
			m_tailBlock->m_backIndex++;
			m_tailBlock->m_data[m_tailBlock->m_backIndex] = a_item;
		} else {
			DequeNodeBlock* block = new DequeNodeBlock(a_item);
			block->m_prevBlock = m_tailBlock;
			if (m_tailBlock)
				m_tailBlock->m_nextBlock = block;
			else
				m_headBlock = block;
			m_tailBlock = block;
		}
	}

	const T& front() const
	{
		if (m_headBlock)
			return m_headBlock->m_data[m_headBlock->m_frontIndex];
		return m_defaultItem;
	}

	T& front()
	{
		if (m_headBlock)
			return m_headBlock->m_data[m_headBlock->m_frontIndex];
		return m_defaultItem;
	}

	const T& back() const
	{
		if (m_tailBlock)
			return m_tailBlock->m_data[m_tailBlock->m_backIndex];
		return m_defaultItem;
	}

	T& back()
	{
		if (m_tailBlock)
			return m_tailBlock->m_data[m_tailBlock->m_backIndex];
		return m_defaultItem;
	}

	void pop_front()
	{
		DequeNodeBlock* block = m_headBlock;
		if (block) {
			m_nodeCount--;
			//assert(m_headBlock->m_frontIndex < m_headBlock->m_backIndex);
			block->m_frontIndex++;
			if (block->m_frontIndex == block->m_backIndex) {
				m_headBlock = block->m_nextBlock;
				if (!m_headBlock)
					//assert(block == m_tailBlock);
					m_tailBlock = 0;
				else
					m_headBlock->m_prevBlock = 0;
				delete block;
			}
		}
	}

	void pop_back()
	{
		DequeNodeBlock* block = m_tailBlock;
		if (block) {
			m_nodeCount--;
			//assert(m_headBlock->m_frontIndex < m_headBlock->m_backIndex);
			block->m_backIndex--;
			if (block->m_frontIndex == block->m_backIndex) {
				m_tailBlock = block->m_prevBlock;
				if (!m_tailBlock)
					//assert(block == m_headBlock);
					m_headBlock = 0;
				else
					m_tailBlock->m_nextBlock = 0;
				delete block;
			}
		}
	}

	size_t size()
	{
		return m_nodeCount;
	}

	bool empty()
	{
		return (m_nodeCount == 0);
	}

	const T& operator[](size_t a_index) const
	{
		DequeNodeBlock* block = m_headBlock;
		size_t position = 0;
		while (block)
		{
			size_t nextPosition = position + (block->m_backIndex - block->m_frontIndex);
			if (a_index >= position && a_index < nextPosition)
			{
				return block->m_data[block->m_frontIndex + (a_index - position)];
			}
			block = block->m_nextBlock;
			position = nextPosition;
		}
	}

private:
	class DequeNodeBlock
	{
	public:
		DequeNodeBlock(const T& a_item)
		{
			m_frontIndex = m_blockSize / 2;  // stack and fifo set to 0, 
			m_backIndex = m_blockSize / 2;   // stack and fifo set to 0, 
			m_data[this->m_frontIndex] = a_item;
			m_nextBlock = 0;
			m_prevBlock = 0;
		}
		size_t           m_frontIndex;
		size_t           m_backIndex;
		T                m_data[BlockSize];
		DequeNodeBlock*  m_prevBlock;
		DequeNodeBlock*  m_nextBlock;
	};

	DequeNodeBlock* m_headBlock;
	DequeNodeBlock* m_tailBlock;
	size_t m_blockSize;
	size_t m_nodeCount;

	T m_defaultItem;
};


template <typename T>
class Deque : public DequeBase<T, 64>
{
public:

	// operator=
	// begin
	// end
	// rbegin
	// rend
	// C++11 cbegin
	// C++11 cend
	// C++11 crbegin
	// C++11 crend
	// max_size
	// resize
	// C++11 shrink_to_fit
	// at
	// assign
	// insert
	// erase
	// swap
	// clear
	// C++11 emplace
	// C++11 emplace_front
	// C++11 emplace_back
	// get_allocator
};



template <typename T>
class Stack : public DequeBase<T, 64>
{
public:
	T& top()
	{
		return DequeBase<T, 64>::back();
	}
	const T& top() const
	{
		return DequeBase<T, 64>::back();
	}
	void push(const T& a_item) 
	{
		push_back(a_item);
	}
	void pop()
	{
		DequeBase<T, 64>::pop_back();
	}
	//C++11 emplace(...)
	//C++11 swap()
};


template <typename T>
class Queue : public DequeBase<T, 64>
{
public:
	void push(const T& a_item) 
	{
		append(a_item);
	}
	void pop(const T& a_item) 
	{
		// TODO: take from the end
	}
};



//Deque<int, 64>  testDeQue;



/*


enum/class		GrowthStrategy
						Linear, Exponential etc

enum/class		AllocationDomain/Paging
						Stack, Heap, OutOfCore

enum/class		ContainerType
						Item Store
						KeyValue Associative Store
						Cache

enum/class		ContainerCapabilities
						IndexBasedLookups, ForwardIterator, BackwardsIterator
						Prepend, Append, MiddleInserts
						DeleteFront, DeleteBack, MiddleDeletes

enum/class		PerformanceCharacteristics/Prioritize Capabilties
						FavourFastLookups, FavourFastMiddleInserts,
						FavourFastPrepends, FavourFastAppends,
						FavourFewerAllocations,
						CanProvideHashFunction, CanProvideComparesFunction



						
						
class List				IndexBasedLookups, FastPrepends, FastAppends, FastForwardIteration, DeleteFromFront, DeleteFromBack, DeleteFromMiddle
class LinkedList		FastMiddleInserts, FastPrepends, FastAppends, FastForwardIteration, FastBackwardIteration, DeleteFromFront, DeleteFromBack, DeleteFromMiddle
class Vector;
class Stack;
class Queue;
class Set;
class Map;			// associative set, name-value pairs,  key-value pairs
class MultiMap;
class Hash;
class MultiHash;

class Cache
class Pair
class VarLengthArray<T, Prealloc>   // stack -> expands to heap



class List;
class LinkedList;
class Vector;
class Stack;
class Queue;
class Set;
class Map;			// associative set, name-value pairs,  key-value pairs
class MultiMap;
class Hash;
class MultiHash;

class Cache
class Pair
class VarLengthArray<T, Prealloc>   // stack -> expands to heap



class Array;
class Map;
class Dictionary;
class Set;
class Set;
class Set;
*/


#define USE_STL_CONTAINERS

#ifdef USE_STL_CONTAINERS


template <typename T>
class Vector : public std::vector<T> {
public:
	Vector() {}
	Vector(size_t _Count, const T& _Val) : std::vector<T>(_Count, _Val) {}
};


template <typename T1, typename T2>
class HashMap : public std::map<T1,T2> {
public:
	void insert(T1 key, T2 value) { std::map<T1,T2>::insert(std::make_pair(key, value)); }
};


template <typename T1, typename T2>
class Pair : public std::pair<T1,T2> {
};


std::string wstring_to_utf8(const std::wstring& utf16);


class String : public std::string {
public:
	String() {}
	String(const char* fmt, ...)
	{
		std::vector<char> str(50,'\0');
		va_list ap;
		int n = -1;
		while ((n <= -1) || (size_t(n) >= str.size())) {
			str.resize((n <= -1) ? (str.size() * 2) : (n + 1));
			va_start(ap, fmt);
			n = vsnprintf(str.data(), str.size(), fmt, ap);
			va_end(ap);
		}
		*this = str.data();
	}
	//String(const char* a_str) : std::string(a_str) {}
	String(wchar_t const* a_str) : std::string(wstring_to_utf8(a_str)) {}
	String(std::string a_str) : std::string(a_str) {}
	const std::string& toUtf8() const { return *this; }
	const std::wstring& toWString() const;
	String& operator=(std::string a_str) { *((std::string*)this) = a_str; return *this; }
	String& operator=(const char* a_str) { *((std::string*)this) = a_str; return *this; }
	operator char const*() const { return data(); }
private:
	mutable std::wstring m_wstr;
};


#else



class TemplatedTypeWrapper
{
public:
	virtual size_t elementSize() = 0;
	virtual void initElement(void* elementPtr) = 0;
	virtual void copyElement(void* dstPtr, void* srcPtr) = 0;


	//TemplatedTypeWrapper() {}
	//TemplatedTypeWrapper(const TemplatedTypeWrapper& a_other) {}
	virtual ~TemplatedTypeWrapper() = 0;
	virtual TemplatedTypeWrapper& operator=(const TemplatedTypeWrapper& a_other) = 0;
	
};


class IntTemplatedTypeWrapper : public TemplatedTypeWrapper
{
public:
	IntTemplatedTypeWrapper(int x) : m_int(x) {}
	TemplatedTypeWrapper& operator=(const IntTemplatedTypeWrapper& a_other) { m_int = a_other.m_int; return *this; }
	size_t size() { return sizeof(this); }
private:
	int m_int;
};


template <typename T>
class Vector
{
public:
	typedef Vector<T>	self_t;

	Vector(size_t a_size=64, T a_val=T()) : m_size(a_size), m_used(0) {
		m_array = new T[m_size];
		for (unsigned i = 0; i < m_size; i++)
			m_array[i] = a_val;
	}
	~Vector() {
		delete[] m_array;
	}
	Vector(const self_t& a_right) : m_size(a_right.size()), m_used(a_right.size()) {
		m_array = new T[m_size];
		for (unsigned i = 0; i < m_used; i++)
			m_array[i] = a_right.m_array[i];
	}

	self_t& operator=(const self_t& a_right)
	{
		if (this != &a_right)
		{
			resize(a_right.size());
			memcpy(m_array, a_right.m_array, sizeof(T) * a_right.m_used);
		}
		return (*this);
	}

	void push_back(T& val) {
		if ((m_used*3) > (m_size*2))
			reserve(m_size*2);
		m_array[m_used] = val;
		m_used++;
	}
	T& back() const {
		return m_array[m_used-1];
	}
	void pop_back() {
		m_used--;
	}
	T& operator[](int index) {
		return m_array[index];
	}
	void clear() {
		m_used = 0;
	}
	size_t size() const {
		return m_used;
	}
	void resize(size_t newSize) {
		if (newSize > m_size)
			reserve((newSize*3)/2);
		m_used = newSize;
	}
	T* data() const {
		return m_array;
	}
private:
	void reserve(size_t newSize) {
		T* newArray = new T[newSize];
		for (unsigned i = 0; i < m_used; i++)
			newArray[i] = m_array[i];
		delete[] m_array;
		m_array = newArray;
		m_size = newSize;
	}
	size_t m_size;
	size_t m_used;
	T *m_array;
};


template <typename T1, typename T2>
class Pair
{
public:
	Pair() {}
	Pair(T1 a, T2 b) : first(a), second(b) {}
	T1 first;
	T2 second;
};


template <typename T1, typename T2>
class HashMap
{
public:
	typedef HashMap<T1,T2>	self_t;

	HashMap(size_t a_size=64) : m_size(a_size), m_used(0) {
		m_table = new Pair<T1,T2>[m_size];
		memset(m_table, 0, sizeof(Pair<T1,T2>)*m_size);
	}
	~HashMap() {
		delete[] m_table;
	}
	T2& operator[](T1 key) {
		return lookup(key).second;
	}
	void erase(T1 key) {
		m_used--;
		memset(&lookup(key), 0, sizeof(Pair<T1,T2>));
		resize(m_size); // This is a slow fixup to resolve the hole left from the memset in the hash lookup chain
	}
	void insert(T1 key, T2 value) {
		lookup(key).second = value;
	}
	void insert(Pair<T1,T2> pair) {
		lookup(pair.first).second = pair.second;
	}
	
	class iterator
	{
	public:
		iterator(Pair<T1,T2>* a_pair, const HashMap<T1,T2>* a_parent) : m_ptr(a_pair), m_parent(a_parent) {}
		iterator& operator++() {
			m_ptr += 1;
			while (!m_ptr->second && m_ptr != m_parent->end())
				m_ptr += 1;
			return *this;
		}
		iterator operator++(int) {
			iterator it = *this;
			it.m_ptr += 1;
			while (!it->second && m_ptr != m_parent->end())
				it.m_ptr += 1;
			return it;
		}
		Pair<T1,T2>* operator->() { return m_ptr; }
		operator Pair<T1,T2>*() { return m_ptr; }
	private:
		Pair<T1,T2>*     m_ptr;
		const HashMap<T1,T2>*  m_parent;
	};

	iterator begin() const {
		size_t h = 0;
		while (h < (m_size-1) && (!m_table[h].second))
			h++;
		return iterator(&m_table[h], this);
	}
	iterator end() const {
		return iterator(&m_table[m_size-1], this);
	}
	iterator find(T1 key) const {
		iterator it = findPtr(key);
		return (!it->second) ? end() : it;
	}
	void clear() {
		memset(m_table, 0, sizeof(Pair<T1,T2>)*m_size);
		m_used = 0;
	}
private:
	// Prevent copying of these objects
	HashMap(const self_t& a_right) {}
	self_t& operator=(const self_t& a_right) {}

	iterator findPtr(T1 key) const {
		size_t h = hash<T1>(key) % m_size;
		while ((!!m_table[h].second) && m_table[h].first != key) // TODO: should cap the iterations by the size else it could get stuck in a loop
			h = (h + 13) % m_size;
		return iterator(&m_table[h], this);
	}
	Pair<T1,T2>& lookup(T1 key) {
		iterator it = findPtr(key);
		if (!it->second) {
			it->first = key;
			m_used++;
			if ((m_used*3) > (m_size*2))
				resize(m_size*2);
		}
		return *it;
	}
	void resize(size_t newSize) {
		HashMap newMap(newSize);
		for (unsigned i = 0; i < m_size; i++)
			if (m_table[i].second)
				newMap.insert(m_table[i].first, m_table[i].second);
		Pair<T1,T2> *newTable = newMap.m_table;
		newMap.m_table = m_table;
		m_table = newTable;
		m_size = newSize;
	}
	size_t m_size;
	size_t m_used;
	Pair<T1,T2> *m_table;
};


template <typename T>
size_t hash(T a_key) { return (size_t)a_key; }


template <typename T>
size_t hash(std::string a_str) { return std::hash<std::string>()(a_str); }




template <typename T1, typename T2>
class TreeNode
{
public:
	T1 first;
	T2 second;
	TreeNode* m_childA;
	TreeNode* m_childB;
};


template <typename T1, typename T2>
class Tree
{
public:
	Tree() {}
	~Tree() {}

	typedef Tree<T1,T2>	self_t;

	/*
	HashMap(size_t a_size=64) : m_size(a_size), m_used(0) {
		m_table = new Pair<T1,T2>[m_size];
		memset(m_table, 0, sizeof(Pair<T1,T2>)*m_size);
	}
	~HashMap() {
		delete[] m_table;
	}
	*/

	T2& operator[](T1 key) {
		return lookup(key).second;
	}
	void erase(T1 key) {
		m_used--;
		memset(&lookup(key), 0, sizeof(Pair<T1,T2>));
		resize(m_size); // This is a slow fixup to resolve the hole left from the memset in the hash lookup chain
	}
	void insert(T1 key, T2 value) {
		lookup(key).second = value;
	}
	void insert(Pair<T1,T2> pair) {
		lookup(pair.first).second = pair.second;
	}
	
	class iterator
	{
	public:
		iterator(Pair<T1,T2>* a_pair, const HashMap<T1,T2>* a_parent) : m_ptr(a_pair), m_parent(a_parent) {}
		iterator& operator++() {
			m_ptr += 1;
			while (!m_ptr->second && m_ptr != m_parent->end())
				m_ptr += 1;
			return *this;
		}
		iterator operator++(int) {
			iterator it = *this;
			it.m_ptr += 1;
			while (!it->second && m_ptr != m_parent->end())
				it.m_ptr += 1;
			return it;
		}
		Pair<T1,T2>* operator->() { return m_ptr; }
		operator Pair<T1,T2>*() { return m_ptr; }
	private:
		Pair<T1,T2>*     m_ptr;
		const HashMap<T1,T2>*  m_parent;
	};

	iterator begin() const {
		size_t h = 0;
		while (h < (m_size-1) && (!m_table[h].second))
			h++;
		return iterator(&m_table[h], this);
	}
	iterator end() const {
		return iterator(&m_table[m_size-1], this);
	}
	iterator find(T1 key) const {
		iterator it = findPtr(key);
		return (!it->second) ? end() : it;
	}
	void clear() {
		memset(m_table, 0, sizeof(Pair<T1,T2>)*m_size);
		m_used = 0;
	}
private:
	// Prevent copying of these objects
	Tree(const self_t& a_right) {}
	self_t& operator=(const self_t& a_right) {}

	iterator findPtr(T1 key) const {
		size_t h = hash<T1>(key) % m_size;
		while ((!!m_table[h].second) && m_table[h].first != key) // TODO: should cap the iterations by the size else it could get stuck in a loop
			h = (h + 13) % m_size;
		return iterator(&m_table[h], this);
	}
	Pair<T1,T2>& lookup(T1 key) {
		iterator it = findPtr(key);
		if (!it->second) {
			it->first = key;
			m_used++;
			if ((m_used*3) > (m_size*2))
				resize(m_size*2);
		}
		return *it;
	}
	void resize(size_t newSize) {
		HashMap newMap(newSize);
		for (unsigned i = 0; i < m_size; i++)
			if (m_table[i].second)
				newMap.insert(m_table[i].first, m_table[i].second);
		Pair<T1,T2> *newTable = newMap.m_table;
		newMap.m_table = m_table;
		m_table = newTable;
		m_size = newSize;
	}
	size_t m_size;
	size_t m_used;
	Pair<T1,T2> *m_table;

	TreeNode* m_rootNode;
	size_t m_shortestPathToLeaf;
	size_t m_longestPathToLeaf;
};


/*
Vector<std::string> split(const std::string &s, char delim);
Vector<std::string> split(const std::string &s, const char* delim);
std::string left(const std::string &str, const char* match);
std::string right(const std::string &str, const char* match);
std::string string_format(const char* fmt, ...);
std::string wstring_to_utf8(const std::wstring& utf16);
std::wstring utf8_to_wstring(const std::string& utf8);
std::string to_string(uint32_t i);
*/


class String
{
public:
	String();
	~String();
	String(char);
	String(int8_t);
	String(uint8_t);
	String(int16_t);
	String(uint16_t);
	String(int32_t);
	String(uint32_t);
	String(int64_t);
	String(uint64_t);
	String(float);
	String(double);
	String(const char* a_fmt, ...);
	String(const wchar_t* a_wideString);
	String(const std::string&);
	/*
	String(const std::wstring&);
	*/
	String(const String&);

	Vector<String> split(const char* a_deliminator);

	String replace(const char* a_match, const char* a_replacement);
	String left(const char* a_match);
	String right(const char* a_match);
	size_t find(const char* a_match, size_t a_start=0);

	const std::string& toUtf8() const;
	const std::wstring& toWString() const;
	/*
	*/

	const char* c_str() const;
	const char* data() const;
	operator const char*() const;

	String& operator= (const String&);
	String& operator= (const char*);
	String& operator= (char);
	String& operator+=(const char*);
	String& operator+=(const String&);
	String& operator+=(char);
	bool operator==(const char*) const;
	//bool operator==(const std::string&) const;
	bool operator==(const String&) const;
	bool operator!=(const char*) const;
	//bool operator!=(const std::string&) const;
	bool operator!=(const String&) const;
	bool operator< (const String&) const;
	bool operator> (const String&) const;
	bool operator<=(const String&) const;
	bool operator>=(const String&) const;
	bool operator!() const;

	friend const String operator+(const String&, const String&);
	friend const String operator+(const String&, const char*);
	friend const String operator+(const char*, const String&);
	friend const String operator+(char, const String&);
	friend const String operator+(const String&, char);
	friend bool operator==(const char*, const String&);
	friend bool operator!=(const char*, const String&);

	size_t hash() const;
	
	static const size_t npos;

private:
	//Vector<char> m_str;
	std::string m_str;
	mutable std::wstring m_wstr;
};


template <typename T>
size_t hash(String a_str) { return a_str.hash(); }


#endif


template <typename T1, typename T2>
class BidirectionalMap
{
public:
	size_t size() { return m_forwardMap.size(); }
	
	const T2& forwardFind(const T1& a, const T2& def) const {
		return (m_forwardMap.find(a) != m_forwardMap.end()) ? m_forwardMap.find(a)->second : def;
	}

	const T1& reverseFind(const T2& b, const T1& def) const {
		return (m_reverseMap.find(b) != m_reverseMap.end()) ? m_reverseMap.find(b)->second : def;
	}

	void insert(T1 a, T2 b) {
		m_forwardMap.insert(a, b);
		m_reverseMap.insert(b, a);
	}
private:
	HashMap<T1,T2> m_forwardMap;
	HashMap<T2,T1> m_reverseMap;
};


END_NAMESPACE


#endif // CONTAINERS_H