public/sm_trie_tpl.h
author David Anderson <dvander@alliedmods.net>
Sun Oct 28 08:02:24 2007 +0000 (2007-10-28)
changeset 1648 3c8256da48b9
parent 1579 8951b7134937
child 1667 5588f4dbacbf
permissions -rw-r--r--
added experimental extension interface for loading external extensions from mm:s
hard-bumped the extension api
dvander@1385
     1
/**
dvander@1385
     2
 * vim: set ts=4 :
dvander@1385
     3
 * =============================================================================
dvander@1385
     4
 * SourceMod
dvander@1385
     5
 * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
dvander@1385
     6
 * =============================================================================
dvander@1385
     7
 *
dvander@1385
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@1385
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@1385
    10
 * Free Software Foundation.
dvander@1385
    11
 * 
dvander@1385
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@1385
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@1385
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@1385
    15
 * details.
dvander@1385
    16
 *
dvander@1385
    17
 * You should have received a copy of the GNU General Public License along with
dvander@1385
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1385
    19
 *
dvander@1385
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@1385
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@1385
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@1385
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@1385
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@1385
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@1385
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@1385
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@1385
    28
 *
dvander@1385
    29
 * Version: $Id$
dvander@1385
    30
 */
dvander@1385
    31
dvander@1385
    32
#ifndef _INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_
dvander@1385
    33
#define _INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_
dvander@1385
    34
dvander@1389
    35
#include <new>
dvander@1389
    36
#include <string.h>
dvander@1389
    37
#include <malloc.h>
dvander@1389
    38
#include <assert.h>
dvander@1389
    39
dvander@1389
    40
enum NodeType
dvander@1389
    41
{
dvander@1389
    42
	Node_Unused = 0,		/* Node is not being used (sparse) */
dvander@1389
    43
	Node_Arc,				/* Node is part of an arc and does not terminate */
dvander@1389
    44
	Node_Term,				/* Node is a terminator */
dvander@1389
    45
};
dvander@1385
    46
dvander@1385
    47
/**
dvander@1392
    48
 * @brief Trie class for storing key/value pairs, based on double array tries.
dvander@1389
    49
 * @file sm_trie_tpl.h
dvander@1389
    50
 *
dvander@1389
    51
 * For full works cited and implementation overview, there is a big comment 
dvander@1389
    52
 * block at the bottom of this file.
dvander@1385
    53
 */
dvander@1385
    54
dvander@1385
    55
template <typename K>
dvander@1385
    56
class KTrie
dvander@1385
    57
{
dvander@1389
    58
	class KTrieNode;
dvander@1385
    59
public:
dvander@1389
    60
	/**
dvander@1389
    61
	 * @brief Clears all set objects in the trie.
dvander@1389
    62
	 */
dvander@1385
    63
	void clear()
dvander@1385
    64
	{
dvander@1385
    65
		run_destructors();
dvander@1385
    66
		internal_clear();
dvander@1385
    67
	}
dvander@1389
    68
dvander@1389
    69
	/**
dvander@1389
    70
	 * @brief Removes a key from the trie.
dvander@1389
    71
	 *
dvander@1389
    72
	 * @param key		Key to remove.
dvander@1389
    73
	 * @return			True on success, false if key was never set.
dvander@1389
    74
	 */
dvander@1385
    75
	bool remove(const char *key)
dvander@1385
    76
	{
dvander@1385
    77
		KTrieNode *node = internal_retrieve(key);
dvander@1385
    78
		if (!node || !node->valset)
dvander@1385
    79
		{
dvander@1385
    80
			return false;
dvander@1385
    81
		}
dvander@1385
    82
dvander@1385
    83
		node->value.~K();
dvander@1385
    84
		node->valset = false;
dvander@1385
    85
dvander@1385
    86
		return true;
dvander@1385
    87
	}
dvander@1389
    88
dvander@1389
    89
	/**
dvander@1389
    90
	 * @brief Retrieves a pointer to the object stored at a given key.
dvander@1389
    91
	 *
dvander@1389
    92
	 * @param key		Key to retrieve.
dvander@1389
    93
	 * @return			Pointer to object, or NULL if key was not found or not set.
dvander@1389
    94
	 */
dvander@1385
    95
	K * retrieve(const char *key)
dvander@1385
    96
	{
dvander@1385
    97
		KTrieNode *node = internal_retrieve(key);
dvander@1385
    98
		if (!node || !node->valset)
dvander@1385
    99
		{
dvander@1385
   100
			return NULL;
dvander@1385
   101
		}
dvander@1385
   102
		return &node->value;
dvander@1385
   103
	}
dvander@1389
   104
dvander@1389
   105
	/**
dvander@1389
   106
	 * @brief Inserts or updates the object stored at a key.
dvander@1389
   107
	 *
dvander@1389
   108
	 * @param key		Key to update or insert.
dvander@1389
   109
	 * @param obj		Object to store at the key.
dvander@1389
   110
	 * @return			True on success, false on failure.
dvander@1389
   111
	 */
dvander@1385
   112
	bool replace(const char *key, const K & obj)
dvander@1385
   113
	{
dvander@1385
   114
		KTrieNode *prev_node = internal_retrieve(key);
dvander@1385
   115
		if (!prev_node)
dvander@1385
   116
		{
dvander@1385
   117
			return insert(key, obj);
dvander@1385
   118
		}
dvander@1385
   119
dvander@1385
   120
		if (prev_node->valset)
dvander@1385
   121
		{
dvander@1385
   122
			prev_node->value.~K();
dvander@1385
   123
		}
dvander@1385
   124
dvander@1385
   125
		new (&prev_node->value) K(obj);
dvander@1385
   126
dvander@1385
   127
		return true;
dvander@1385
   128
	}
dvander@1389
   129
dvander@1389
   130
	/**
dvander@1389
   131
	 * @brief Inserts an object at a key.
dvander@1389
   132
	 *
dvander@1389
   133
	 * @param key		Key to insert at.
dvander@1389
   134
	 * @param obj		Object to store at the key.
dvander@1389
   135
	 * @return			True on success, false if the key is already set or 
dvander@1389
   136
	 *					insertion otherwise failed.
dvander@1389
   137
	 */
dvander@1385
   138
	bool insert(const char *key, const K & obj)
dvander@1385
   139
	{
dvander@1385
   140
		unsigned int lastidx = 1;		/* the last node index */
dvander@1385
   141
		unsigned int curidx;			/* current node index */
dvander@1385
   142
		const char *keyptr = key;		/* input stream at current token */
dvander@1385
   143
		KTrieNode *node = NULL;			/* current node being processed */
dvander@1385
   144
		KTrieNode *basenode = NULL;		/* current base node being processed */
dvander@1385
   145
		unsigned int q;					/* temporary var for x_check results */
dvander@1385
   146
		unsigned int curoffs;			/* current offset */
dvander@1385
   147
dvander@1385
   148
		/* Do not handle empty strings for simplicity */
dvander@1385
   149
		if (!*key)
dvander@1385
   150
		{
dvander@1385
   151
			return false;
dvander@1385
   152
		}
dvander@1385
   153
dvander@1385
   154
		/* Start traversing at the root node (1) */
dvander@1385
   155
		do
dvander@1385
   156
		{
dvander@1385
   157
			/* Find where the next character is, then advance */
dvander@1385
   158
			curidx = m_base[lastidx].idx;
dvander@1385
   159
			basenode = &m_base[curidx];
dvander@1385
   160
			curoffs = charval(*keyptr);
dvander@1385
   161
			curidx += curoffs;
dvander@1385
   162
			node = &m_base[curidx];
dvander@1385
   163
			keyptr++;
dvander@1385
   164
dvander@1385
   165
			/* Check if this slot is supposed to be empty.  If so, we need to handle CASES 1/2:
dvander@1386
   166
			 * Insertion without collisions
dvander@1386
   167
			 */
dvander@1385
   168
			if ( (curidx > m_baseSize) || (node->mode == Node_Unused) )
dvander@1385
   169
			{
dvander@1385
   170
				if (curidx > m_baseSize)
dvander@1385
   171
				{
dvander@1385
   172
					if (!grow())
dvander@1385
   173
					{
dvander@1385
   174
						return false;
dvander@1385
   175
					}
dvander@1385
   176
					node = &m_base[curidx];
dvander@1385
   177
				}
dvander@1385
   178
				node->parent = lastidx;
dvander@1385
   179
				if (*keyptr == '\0')
dvander@1385
   180
				{
dvander@1385
   181
					node->mode = Node_Arc;
dvander@1579
   182
				}
dvander@1579
   183
				else
dvander@1579
   184
				{
dvander@1385
   185
					node->idx = x_addstring(keyptr);
dvander@1385
   186
					node->mode = Node_Term;
dvander@1385
   187
				}
dvander@1385
   188
				node->valset = true;
dvander@1385
   189
				new (&node->value) K(obj);
dvander@1385
   190
dvander@1385
   191
				return true;
dvander@1579
   192
			}
dvander@1579
   193
			else if (node->parent != lastidx)
dvander@1579
   194
			{
dvander@1385
   195
				/* Collision! We have to split up the tree here.  CASE 4:
dvander@1385
   196
				 * Insertion when a new word is inserted with a collision.
dvander@1385
   197
				 * NOTE: This is the hardest case to handle.  All below examples are based on:
dvander@1385
   198
				 * BACHELOR, BADGE, inserting BABY.
dvander@1385
   199
				 * The problematic production here is A -> B, where B is already being used.
dvander@1385
   200
			     *
dvander@1385
   201
				 * This process has to rotate one half of the 'A' arc.  We generate two lists:
dvander@1385
   202
				 *  Outgoing Arcs - Anything leaving this 'A'
dvander@1385
   203
				 *  Incoming Arcs - Anything going to this 'A'
dvander@1385
   204
				 * Whichever list is smaller will be moved.  Note that this works because the intersection
dvander@1385
   205
				 * affects both arc chains, and moving one will make the slot available to either.
dvander@1385
   206
				 */
dvander@1385
   207
				KTrieNode *cur;
dvander@1385
   208
dvander@1385
   209
				/* Find every node arcing from the last node.
dvander@1385
   210
				 * I.e. for BACHELOR, BADGE, BABY,
dvander@1385
   211
				 * The arcs leaving A will be C and D, but our current node is B -> *.
dvander@1385
   212
				 * Thus, we use the last index (A) to find the base for arcs leaving A.
dvander@1385
   213
				 */
dvander@1385
   214
				unsigned int outgoing_base = m_base[lastidx].idx;
dvander@1385
   215
				unsigned int outgoing_list[256];
dvander@1385
   216
				unsigned int outgoing_count = 0;	/* count the current index here */
dvander@1385
   217
				cur = &m_base[outgoing_base] + 1;
dvander@1385
   218
				unsigned int outgoing_limit = 255;
dvander@1385
   219
dvander@1385
   220
				if (outgoing_base + outgoing_limit > m_baseSize)
dvander@1385
   221
				{
dvander@1385
   222
					outgoing_limit = m_baseSize - outgoing_base;
dvander@1385
   223
				}
dvander@1385
   224
dvander@1385
   225
				for (unsigned int i=1; i<=outgoing_limit; i++,cur++)
dvander@1385
   226
				{
dvander@1385
   227
					if (cur->mode == Node_Unused || cur->parent != lastidx)
dvander@1385
   228
					{
dvander@1385
   229
						continue;
dvander@1385
   230
					}
dvander@1385
   231
					outgoing_list[outgoing_count++] = i;
dvander@1385
   232
				}
dvander@1385
   233
				outgoing_list[outgoing_count++] = curidx - outgoing_base;
dvander@1385
   234
dvander@1385
   235
				/* Now we need to find all the arcs leaving our parent...
dvander@1385
   236
				 * Note: the inconsistency is the base of our parent.  
dvander@1385
   237
				 */
dvander@1385
   238
				assert(m_base[node->parent].mode == Node_Arc);
dvander@1385
   239
				unsigned int incoming_list[256];
dvander@1385
   240
				unsigned int incoming_base = m_base[node->parent].idx;
dvander@1385
   241
				unsigned int incoming_count = 0;
dvander@1385
   242
				unsigned int incoming_limit = 255;
dvander@1385
   243
				cur = &m_base[incoming_base] + 1;
dvander@1385
   244
dvander@1385
   245
				if (incoming_base + incoming_limit > m_baseSize)
dvander@1385
   246
				{
dvander@1385
   247
					incoming_limit = m_baseSize - incoming_base;
dvander@1385
   248
				}
dvander@1385
   249
dvander@1385
   250
				assert(incoming_limit > 0 && incoming_limit <= 255);
dvander@1385
   251
dvander@1385
   252
				for (unsigned int i=1; i<=incoming_limit; i++,cur++)
dvander@1385
   253
				{
dvander@1385
   254
					if (cur->mode == Node_Arc || cur->mode == Node_Term)
dvander@1385
   255
					{
dvander@1385
   256
						if (cur->parent == node->parent)
dvander@1385
   257
						{
dvander@1385
   258
							incoming_list[incoming_count++] = i;
dvander@1385
   259
						}
dvander@1385
   260
					}
dvander@1385
   261
				}
dvander@1385
   262
dvander@1385
   263
				if (incoming_count < outgoing_count + 1)
dvander@1385
   264
				{
dvander@1385
   265
					unsigned int q = x_check_multi(incoming_list, incoming_count);
dvander@1385
   266
dvander@1385
   267
					node = &m_base[curidx];
dvander@1385
   268
dvander@1385
   269
					/* If we're incoming, we need to modify our parent */
dvander@1385
   270
					m_base[node->parent].idx = q;
dvander@1385
   271
dvander@1385
   272
					/* For each node in the "to move" list,
dvander@1385
   273
					 * Relocate the node's info to the new position.
dvander@1385
   274
					 */
dvander@1385
   275
					unsigned int idx, newidx, oldidx;
dvander@1385
   276
					for (unsigned int i=0; i<incoming_count; i++)
dvander@1385
   277
					{
dvander@1385
   278
						idx = incoming_list[i];
dvander@1385
   279
						newidx = q + idx;
dvander@1385
   280
						oldidx = incoming_base + idx;
dvander@1385
   281
						if (oldidx == lastidx)
dvander@1385
   282
						{
dvander@1385
   283
							/* Important! Make sure we're not invalidating our sacred lastidx */
dvander@1385
   284
							lastidx = newidx;
dvander@1385
   285
						}
dvander@1385
   286
						/* Fully copy the node */
dvander@1385
   287
						memcpy(&m_base[newidx], &m_base[oldidx], sizeof(KTrieNode));
dvander@1385
   288
						if (m_base[oldidx].valset)
dvander@1385
   289
						{
dvander@1385
   290
							new (&m_base[newidx].value) K(m_base[oldidx].value);
dvander@1385
   291
							m_base[oldidx].value.~K();
dvander@1385
   292
						}
dvander@1385
   293
						assert(m_base[m_base[newidx].parent].mode == Node_Arc);
dvander@1385
   294
						/* Erase old data */
dvander@1385
   295
						memset(&m_base[oldidx], 0, sizeof(KTrieNode));
dvander@1385
   296
						/* If we are not a terminator, we have children we must take care of */
dvander@1385
   297
						if (m_base[newidx].mode == Node_Arc)
dvander@1385
   298
						{
dvander@1385
   299
							KTrieNode *check_base = &m_base[m_base[newidx].idx] + 1;
dvander@1385
   300
							outgoing_limit = (m_base + m_baseSize + 1) - check_base;
dvander@1385
   301
							if (outgoing_limit > 255)
dvander@1385
   302
							{
dvander@1385
   303
								outgoing_limit = 255;
dvander@1385
   304
							}
dvander@1385
   305
							for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
dvander@1385
   306
							{
dvander@1385
   307
								if (check_base->parent == oldidx)
dvander@1385
   308
								{
dvander@1385
   309
									check_base->parent = newidx;
dvander@1385
   310
								}
dvander@1385
   311
							}
dvander@1385
   312
						}
dvander@1385
   313
					}
dvander@1579
   314
				}
dvander@1579
   315
				else
dvander@1579
   316
				{
dvander@1385
   317
					unsigned int q = x_check_multi(outgoing_list, outgoing_count);
dvander@1385
   318
dvander@1385
   319
					node = &m_base[curidx];
dvander@1385
   320
dvander@1385
   321
					/* If we're outgoing, we need to modify our own base */
dvander@1385
   322
					m_base[lastidx].idx = q;
dvander@1385
   323
dvander@1385
   324
					/* Take the last index (curidx) out of the list.  Technically we are not moving this,
dvander@1385
   325
					 * since it's already being used by something else.  
dvander@1385
   326
					 */
dvander@1385
   327
					outgoing_count--;
dvander@1385
   328
dvander@1385
   329
					/* For each node in the "to move" list,
dvander@1385
   330
					 * Relocate the node's info to the new position.
dvander@1385
   331
					 */
dvander@1385
   332
					unsigned int idx, newidx, oldidx;
dvander@1385
   333
					for (unsigned int i=0; i<outgoing_count; i++)
dvander@1385
   334
					{
dvander@1385
   335
						idx = outgoing_list[i];
dvander@1385
   336
						newidx = q + idx;
dvander@1385
   337
						oldidx = outgoing_base + idx;
dvander@1385
   338
						if (oldidx == lastidx)
dvander@1385
   339
						{
dvander@1385
   340
							/* Important! Make sure we're not invalidating our sacred lastidx */
dvander@1385
   341
							lastidx = newidx;
dvander@1385
   342
						}
dvander@1385
   343
						/* Fully copy the node */
dvander@1385
   344
						memcpy(&m_base[newidx], &m_base[oldidx], sizeof(KTrieNode));
dvander@1385
   345
						if (m_base[oldidx].valset)
dvander@1385
   346
						{
dvander@1385
   347
							new (&m_base[newidx].value) K(m_base[oldidx].value);
dvander@1385
   348
							m_base[oldidx].value.~K();
dvander@1385
   349
						}
dvander@1385
   350
						assert(m_base[m_base[newidx].parent].mode == Node_Arc);
dvander@1385
   351
						/* Erase old data */
dvander@1385
   352
						memset(&m_base[oldidx], 0, sizeof(KTrieNode));
dvander@1385
   353
						/* If we are not a terminator, we have children we must take care of */
dvander@1385
   354
						if (m_base[newidx].mode == Node_Arc)
dvander@1385
   355
						{
dvander@1385
   356
							KTrieNode *check_base = &m_base[m_base[newidx].idx] + 1;
dvander@1385
   357
							outgoing_limit = (m_base + m_baseSize + 1) - check_base;
dvander@1385
   358
							if (outgoing_limit > 255)
dvander@1385
   359
							{
dvander@1385
   360
								outgoing_limit = 255;
dvander@1385
   361
							}
dvander@1385
   362
							for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
dvander@1385
   363
							{
dvander@1385
   364
								if (check_base->parent == oldidx)
dvander@1385
   365
								{
dvander@1385
   366
									check_base->parent = newidx;
dvander@1385
   367
								}
dvander@1385
   368
							}
dvander@1385
   369
						}
dvander@1385
   370
					}
dvander@1385
   371
dvander@1385
   372
					/* Take the invisible node and use it as our new node */
dvander@1385
   373
					node = &m_base[q + outgoing_list[outgoing_count]];
dvander@1385
   374
				}
dvander@1385
   375
dvander@1385
   376
				/* We're finally done! */
dvander@1385
   377
				node->parent = lastidx;
dvander@1385
   378
				if (*keyptr == '\0')
dvander@1385
   379
				{
dvander@1385
   380
					node->mode = Node_Arc;
dvander@1579
   381
				}
dvander@1579
   382
				else
dvander@1579
   383
				{
dvander@1385
   384
					node->idx = x_addstring(keyptr);
dvander@1385
   385
					node->mode = Node_Term;
dvander@1385
   386
				}
dvander@1385
   387
				node->valset = true;
dvander@1385
   388
				new (&node->value) K(obj);
dvander@1385
   389
dvander@1385
   390
				return true;
dvander@1579
   391
			}
dvander@1579
   392
			else
dvander@1579
   393
			{
dvander@1385
   394
				/* See what's in the next node - special case if terminator! */
dvander@1385
   395
				if (node->mode == Node_Term)
dvander@1385
   396
				{
dvander@1385
   397
					/* If we're a terminator, we need to handle CASE 3:
dvander@1385
   398
					 * Insertion when a terminating collision occurs
dvander@1385
   399
					 */
dvander@1385
   400
					char *term = &m_stringtab[node->idx];
dvander@1385
   401
					/* Do an initial browsing to make sure they're not the same string */
dvander@1385
   402
					if (strcmp(keyptr, term) == 0)
dvander@1385
   403
					{
dvander@1385
   404
						if (!node->valset)
dvander@1385
   405
						{
dvander@1385
   406
							node->valset = true;
dvander@1385
   407
							new (&node->value) K(obj);
dvander@1385
   408
							return true;
dvander@1385
   409
						}
dvander@1385
   410
						/* Same string.  We can't insert. */
dvander@1385
   411
						return false;
dvander@1385
   412
					}
dvander@1385
   413
					/* For each matching character pair, we need to disband the terminator.
dvander@1385
   414
					 * This splits the similar prefix into a single arc path.
dvander@1385
   415
					 * First, save the old values so we can move them to a new node.
dvander@1385
   416
					 * Next, for each loop:
dvander@1385
   417
					 *  Take the current (invalid) node, and point it to the next arc base.
dvander@1385
   418
					 *  Set the current node to the node at the next arc.
dvander@1385
   419
					 */
dvander@1385
   420
					K oldvalue;
dvander@1385
   421
					bool oldvalset = node->valset;
dvander@1385
   422
					if (oldvalset)
dvander@1385
   423
					{
dvander@1385
   424
						oldvalue = node->value;
dvander@1385
   425
					}
dvander@1385
   426
					if (*term == *keyptr)
dvander@1385
   427
					{
dvander@1385
   428
						while (*term == *keyptr)
dvander@1385
   429
						{
dvander@1385
   430
							/* Find the next free slot in the check array.
dvander@1385
   431
							 * This is the "vector base" essentially
dvander@1385
   432
							 */
dvander@1385
   433
							q = x_check(*term);
dvander@1385
   434
							node = &m_base[curidx];
dvander@1385
   435
							/* Point the node to the next new base */
dvander@1385
   436
							node->idx = q;
dvander@1385
   437
							node->mode = Node_Arc;
dvander@1385
   438
							if (node->valset == true)
dvander@1385
   439
							{
dvander@1385
   440
								node->value.~K();
dvander@1385
   441
								node->valset = false;
dvander@1385
   442
							}
dvander@1385
   443
							/* Advance the input stream and local variables */
dvander@1385
   444
							lastidx = curidx;
dvander@1385
   445
							curidx = q + charval(*term);
dvander@1385
   446
							node = &m_base[curidx];
dvander@1385
   447
							/* Make sure the new current node has its parent set. */
dvander@1385
   448
							node->parent = lastidx;
dvander@1385
   449
							node->mode = Node_Arc;	/* Just in case we run x_check again */
dvander@1385
   450
							*term = '\0';	/* Unmark the string table here */
dvander@1385
   451
							term++;
dvander@1385
   452
							keyptr++;
dvander@1385
   453
						}
dvander@1579
   454
					}
dvander@1579
   455
					else if (node->valset)
dvander@1579
   456
					{
dvander@1385
   457
						node->valset = false;
dvander@1385
   458
						node->value.~K();
dvander@1385
   459
					}
dvander@1385
   460
					/* We're done inserting new pairs.  If one of them is exhausted,
dvander@1385
   461
					 * we take special shortcuts.
dvander@1385
   462
					 */
dvander@1385
   463
					if (*term == '\0')				//EX: BADGERHOUSE added over B -> ADGER.  
dvander@1385
   464
					{
dvander@1385
   465
						/* First backpatch the current node - it ends the newly split terminator.
dvander@1385
   466
						 * In the example, this would mean the node is the production from R -> ?
dvander@1385
   467
						 * This node ends the old BADGER, so we set it here.
dvander@1385
   468
						 */
dvander@1385
   469
						node->valset = oldvalset;
dvander@1385
   470
						if (node->valset)
dvander@1385
   471
						{
dvander@1385
   472
							new (&node->value) K(oldvalue);
dvander@1385
   473
						}
dvander@1385
   474
dvander@1385
   475
						/* The terminator was split up, but pieces of keyptr remain.
dvander@1385
   476
						 * We need to generate a new production, in this example, R -> H,
dvander@1385
   477
						 * with H being a terminator to OUSE.  Thus we get:
dvander@1385
   478
						 * B,A,D,G,E,R*,H*->OUSE (* = value set).
dvander@1385
   479
						 * NOTE: parent was last set at the end of the while loop.
dvander@1385
   480
						 */
dvander@1385
   481
						/* Get the new base and apply re-basing */
dvander@1385
   482
						q = x_check(*keyptr);
dvander@1385
   483
						node = &m_base[curidx];
dvander@1385
   484
dvander@1385
   485
						node->idx = q;
dvander@1385
   486
						node->mode = Node_Arc;
dvander@1385
   487
						lastidx = curidx;
dvander@1385
   488
						/* Finish the final node */
dvander@1385
   489
						curidx = q + charval(*keyptr);
dvander@1385
   490
						node = &m_base[curidx];
dvander@1385
   491
						keyptr++;
dvander@1385
   492
						/* Optimize - don't add to string table if there's nothing more to eat */
dvander@1385
   493
						if (*keyptr == '\0')
dvander@1385
   494
						{
dvander@1385
   495
							node->mode = Node_Arc;
dvander@1579
   496
						}
dvander@1579
   497
						else
dvander@1579
   498
						{
dvander@1385
   499
							node->idx = x_addstring(keyptr);
dvander@1385
   500
							node->mode = Node_Term;
dvander@1385
   501
						}
dvander@1385
   502
						node->parent = lastidx;
dvander@1385
   503
						node->valset = true;
dvander@1385
   504
						new (&node->value) K(obj);
dvander@1579
   505
					}
dvander@1579
   506
					else if (*keyptr == '\0')
dvander@1579
   507
					{	//EX: BADGER added over B -> ADGERHOUSE
dvander@1385
   508
						/* First backpatch the current node - it ends newly split input string.
dvander@1385
   509
						 * This is the exact opposite of the above procedure.
dvander@1385
   510
						 */
dvander@1385
   511
						node->valset = true;
dvander@1385
   512
						new (&node->value) K(obj);
dvander@1385
   513
dvander@1385
   514
						/* Get the new base and apply re-basing */
dvander@1385
   515
						q = x_check(*term);
dvander@1385
   516
						node = &m_base[curidx];
dvander@1385
   517
dvander@1385
   518
						node->idx = q;
dvander@1385
   519
						node->mode = Node_Arc;
dvander@1385
   520
						lastidx = curidx;
dvander@1385
   521
						/* Finish the final node */
dvander@1385
   522
						curidx = q + charval(*term);
dvander@1385
   523
						node = &m_base[curidx];
dvander@1385
   524
						term++;
dvander@1385
   525
						/* Optimize - don't add to string table if there's nothing more to eat */
dvander@1385
   526
						if (*term == '\0')
dvander@1385
   527
						{
dvander@1385
   528
							node->mode = Node_Arc;
dvander@1579
   529
						}
dvander@1579
   530
						else
dvander@1579
   531
						{
dvander@1385
   532
							node->idx = (term - m_stringtab); /* Already in the string table! */
dvander@1385
   533
							node->mode = Node_Term;
dvander@1385
   534
						}
dvander@1385
   535
						node->parent = lastidx;
dvander@1385
   536
						node->valset = oldvalset;
dvander@1385
   537
						if (node->valset)
dvander@1385
   538
						{
dvander@1385
   539
							new (&node->value) K(oldvalue);
dvander@1385
   540
						}
dvander@1579
   541
					}
dvander@1579
   542
					else
dvander@1579
   543
					{
dvander@1385
   544
						/* Finally, we have to create two new nodes instead of just one. */
dvander@1385
   545
						node->mode = Node_Arc;
dvander@1385
   546
dvander@1385
   547
						/* Get the new base and apply re-basing */
dvander@1385
   548
						q = x_check2(*keyptr, *term);
dvander@1385
   549
						node = &m_base[curidx];
dvander@1385
   550
dvander@1385
   551
						node->idx = q;
dvander@1385
   552
						lastidx = curidx;
dvander@1385
   553
dvander@1385
   554
						/* Re-create the old terminated node */
dvander@1385
   555
						curidx = q + charval(*term);
dvander@1385
   556
						node = &m_base[curidx];
dvander@1385
   557
						term++;
dvander@1385
   558
						node->valset = oldvalset;
dvander@1385
   559
						if (node->valset)
dvander@1385
   560
						{
dvander@1385
   561
							new (&node->value) K(oldvalue);
dvander@1385
   562
						}
dvander@1385
   563
						node->parent = lastidx;
dvander@1385
   564
						if (*term == '\0')
dvander@1385
   565
						{
dvander@1385
   566
							node->mode = Node_Arc;
dvander@1579
   567
						}
dvander@1579
   568
						else
dvander@1579
   569
						{
dvander@1385
   570
							node->mode = Node_Term;
dvander@1385
   571
							node->idx = (term - m_stringtab); /* Already in the string table! */
dvander@1385
   572
						}
dvander@1385
   573
dvander@1385
   574
						/* Create the new keyed input node */
dvander@1385
   575
						curidx = q + charval(*keyptr);
dvander@1385
   576
						node = &m_base[curidx];
dvander@1385
   577
						keyptr++;
dvander@1385
   578
						node->valset = true;
dvander@1385
   579
						new (&node->value) K(obj);
dvander@1385
   580
						node->parent = lastidx;
dvander@1385
   581
						if (*keyptr == '\0')
dvander@1385
   582
						{
dvander@1385
   583
							node->mode = Node_Arc;
dvander@1579
   584
						}
dvander@1579
   585
						else
dvander@1579
   586
						{
dvander@1385
   587
							node->mode = Node_Term;
dvander@1385
   588
							node->idx = x_addstring(keyptr);
dvander@1385
   589
						}
dvander@1385
   590
					}
dvander@1385
   591
dvander@1385
   592
					/* Phew! */
dvander@1385
   593
					return true;
dvander@1579
   594
				}
dvander@1579
   595
				else
dvander@1579
   596
				{
dvander@1385
   597
					assert(node->mode == Node_Arc);
dvander@1385
   598
				}
dvander@1385
   599
			}
dvander@1385
   600
			lastidx = curidx;
dvander@1385
   601
		} while (*keyptr != '\0');
dvander@1385
   602
dvander@1385
   603
		assert(node);
dvander@1385
   604
dvander@1385
   605
		/* If we've exhausted the string and we have a valid reached node,
dvander@1389
   606
		 * the production rule already existed.  Make sure it's valid to set first.
dvander@1389
   607
		 */
dvander@1385
   608
dvander@1385
   609
		/* We have to be an Arc.  If the last result was anything else, we would have returned a new 
dvander@1389
   610
		 * production earlier.
dvander@1389
   611
		 */
dvander@1385
   612
		assert(node->mode == Node_Arc);
dvander@1385
   613
dvander@1385
   614
		if (!node->valset)
dvander@1385
   615
		{
dvander@1385
   616
			node->valset = true;
dvander@1385
   617
			new (&node->value) K(obj);
dvander@1385
   618
			return true;
dvander@1385
   619
		}
dvander@1385
   620
dvander@1385
   621
		return false;
dvander@1385
   622
	}
dvander@1389
   623
public:
dvander@1389
   624
	KTrie()
dvander@1389
   625
	{
dvander@1389
   626
		m_base = (KTrieNode *)malloc(sizeof(KTrieNode) * (256 + 1));
dvander@1389
   627
		m_stringtab = (char *)malloc(sizeof(char) * 256);
dvander@1389
   628
		m_baseSize = 256;
dvander@1389
   629
		m_stSize = 256;
dvander@1389
   630
dvander@1389
   631
		internal_clear();
dvander@1389
   632
	}
dvander@1389
   633
	~KTrie()
dvander@1389
   634
	{
dvander@1389
   635
		run_destructors();
dvander@1389
   636
		free(m_base);
dvander@1389
   637
		free(m_stringtab);
dvander@1389
   638
	}
dvander@1389
   639
private:
dvander@1389
   640
	class KTrieNode
dvander@1389
   641
	{
dvander@1389
   642
		friend class KTrie;
dvander@1389
   643
	private:
dvander@1389
   644
		/**
dvander@1389
   645
		 * For Node_Arc, this index stores the 'base' offset to the next arc chain.
dvander@1389
   646
		 *   I.e. to jump from this arc to character C, it will be at base[idx+C].
dvander@1389
   647
		 * For Node_Term, this is an index into the string table.
dvander@1389
   648
		 */
dvander@1389
   649
		unsigned int idx;	
dvander@1389
   650
dvander@1389
   651
		/**
dvander@1389
   652
		 * This contains the prior arc that we must have come from.
dvander@1389
   653
		 * For example, if arc 63 has a base jump of index 12, and we want to see if
dvander@1389
   654
		 * there is a valid character C, the parent of 12+C must be 63.
dvander@1389
   655
		 */
dvander@1389
   656
		unsigned int parent;
dvander@1389
   657
		K value;				/* Value associated with this node */
dvander@1389
   658
		NodeType mode;			/* Current usage type of the node */
dvander@1389
   659
		bool valset;			/* Whether or not a value is set */
dvander@1389
   660
	};
dvander@1385
   661
private:
dvander@1385
   662
	KTrieNode *internal_retrieve(const char *key)
dvander@1385
   663
	{
dvander@1385
   664
		unsigned int lastidx = 1;		/* the last node index */
dvander@1385
   665
		unsigned int curidx;			/* current node index */
dvander@1385
   666
		const char *keyptr = key;		/* input stream at current token */
dvander@1385
   667
		KTrieNode *node = NULL;			/* current node being processed */
dvander@1385
   668
dvander@1385
   669
		if (!*key)
dvander@1385
   670
		{
dvander@1385
   671
			return NULL;
dvander@1385
   672
		}
dvander@1385
   673
dvander@1385
   674
		/* Start traversing at the root node */
dvander@1385
   675
		do
dvander@1385
   676
		{
dvander@1385
   677
			/* Find where the next character is, then advance */
dvander@1385
   678
			curidx = m_base[lastidx].idx;
dvander@1385
   679
			node = &m_base[curidx];
dvander@1385
   680
			curidx += charval(*keyptr);
dvander@1385
   681
			node = &m_base[curidx];
dvander@1385
   682
			keyptr++;
dvander@1385
   683
dvander@1385
   684
			/* Check if this slot is supposed to be empty or is a collision */
dvander@1385
   685
			if ((curidx > m_baseSize) || node->mode == Node_Unused || node->parent != lastidx)
dvander@1385
   686
			{
dvander@1385
   687
				return NULL;
dvander@1579
   688
			}
dvander@1579
   689
			else if (node->mode == Node_Term)
dvander@1579
   690
			{
dvander@1385
   691
				char *term = &m_stringtab[node->idx];
dvander@1385
   692
				if (strcmp(keyptr, term) == 0)
dvander@1385
   693
				{
dvander@1385
   694
					break;
dvander@1385
   695
				}
dvander@1579
   696
				else
dvander@1579
   697
				{
dvander@1579
   698
					return NULL;
dvander@1579
   699
				}
dvander@1385
   700
			}
dvander@1385
   701
			lastidx = curidx;
dvander@1385
   702
		} while (*keyptr != '\0');
dvander@1385
   703
dvander@1385
   704
		return node;
dvander@1385
   705
	}
dvander@1385
   706
	bool grow()
dvander@1385
   707
	{
dvander@1385
   708
		/* The current # of nodes in the tree is trie->baseSize + 1 */
dvander@1385
   709
		unsigned int cur_size = m_baseSize;
dvander@1385
   710
		unsigned int new_size = cur_size * 2;
dvander@1385
   711
dvander@1385
   712
		KTrieNode *new_base = (KTrieNode *)malloc((new_size + 1) * sizeof(KTrieNode));
dvander@1385
   713
		if (!new_base)
dvander@1385
   714
		{
dvander@1385
   715
			return false;
dvander@1385
   716
		}
dvander@1385
   717
dvander@1389
   718
		memcpy(new_base, m_base, sizeof(KTrieNode) * (m_baseSize + 1));
dvander@1385
   719
		memset(&new_base[cur_size + 1], 0, (new_size - cur_size) * sizeof(KTrieNode));
dvander@1385
   720
dvander@1385
   721
		for (size_t i = 0; i <= m_baseSize; i++)
dvander@1385
   722
		{
dvander@1385
   723
			if (m_base[i].valset)
dvander@1385
   724
			{
dvander@1385
   725
				/* Placement construct+copy the object, then placement destroy the old. */
dvander@1385
   726
				new (&new_base[i].value) K(m_base[i].value);
dvander@1385
   727
				m_base[i].value.~K();
dvander@1385
   728
			}
dvander@1385
   729
		}
dvander@1385
   730
dvander@1385
   731
		free(m_base);
dvander@1385
   732
		m_base = new_base;
dvander@1385
   733
		m_baseSize = new_size;
dvander@1385
   734
dvander@1385
   735
		return true;
dvander@1385
   736
	}
dvander@1385
   737
	inline unsigned char charval(char c)
dvander@1385
   738
	{
dvander@1385
   739
		return (unsigned char)c;
dvander@1385
   740
	}
dvander@1385
   741
	void internal_clear()
dvander@1385
   742
	{
dvander@1385
   743
		m_tail = 0;
dvander@1385
   744
dvander@1385
   745
		memset(m_base, 0, sizeof(KTrieNode) * (m_baseSize + 1));
dvander@1385
   746
		memset(m_stringtab, 0, sizeof(char) * m_stSize);
dvander@1385
   747
dvander@1385
   748
		/* Sentinel root node */
dvander@1385
   749
		m_base[1].idx = 1;
dvander@1385
   750
		m_base[1].mode = Node_Arc;
dvander@1385
   751
		m_base[1].parent = 1;
dvander@1385
   752
	}
dvander@1385
   753
	void run_destructors()
dvander@1385
   754
	{
dvander@1385
   755
		for (size_t i = 0; i <= m_baseSize; i++)
dvander@1385
   756
		{
dvander@1385
   757
			if (m_base[i].valset)
dvander@1385
   758
			{
dvander@1385
   759
				m_base[i].value.~K();
dvander@1385
   760
			}
dvander@1385
   761
		}
dvander@1385
   762
	}
dvander@1385
   763
	unsigned int x_addstring(const char *ptr)
dvander@1385
   764
	{
dvander@1385
   765
		size_t len = strlen(ptr) + 1;
dvander@1385
   766
dvander@1385
   767
		if (m_tail + len >= m_stSize)
dvander@1385
   768
		{		
dvander@1385
   769
			while (m_tail + len >= m_stSize)
dvander@1385
   770
			{
dvander@1385
   771
				m_stSize *= 2;
dvander@1385
   772
			}
dvander@1385
   773
			m_stringtab = (char *)realloc(m_stringtab,m_stSize);
dvander@1385
   774
		}
dvander@1385
   775
dvander@1385
   776
		unsigned int tail = m_tail;
dvander@1385
   777
		strcpy(&m_stringtab[tail], ptr);
dvander@1385
   778
		m_tail += len;
dvander@1385
   779
dvander@1385
   780
		return tail;
dvander@1385
   781
	}
dvander@1385
   782
	unsigned int x_check(char c, unsigned int start=1)
dvander@1385
   783
	{
dvander@1385
   784
		unsigned char _c = charval(c);
dvander@1385
   785
		unsigned int to_check = m_baseSize - _c;
dvander@1385
   786
		for (unsigned int i=start; i<=to_check; i++)
dvander@1385
   787
		{
dvander@1385
   788
			if (m_base[i+_c].mode == Node_Unused)
dvander@1385
   789
			{
dvander@1385
   790
				return i;
dvander@1385
   791
			}
dvander@1385
   792
		}
dvander@1385
   793
dvander@1385
   794
		grow();
dvander@1385
   795
dvander@1385
   796
		return x_check(c, to_check+1);
dvander@1385
   797
	}
dvander@1385
   798
	unsigned int x_check2(char c1, char c2, unsigned int start=1)
dvander@1385
   799
	{
dvander@1385
   800
		unsigned char _c1 = charval(c1);
dvander@1385
   801
		unsigned char _c2 = charval(c2);
dvander@1385
   802
		unsigned int to_check = m_baseSize - (_c1 > _c2 ? _c1 : _c2);
dvander@1385
   803
		for (unsigned int i=start; i<=to_check; i++)
dvander@1385
   804
		{
dvander@1385
   805
			if (m_base[i+_c1].mode == Node_Unused
dvander@1385
   806
				&& m_base[i+_c2].mode == Node_Unused)
dvander@1385
   807
			{
dvander@1385
   808
				return i;
dvander@1385
   809
			}
dvander@1385
   810
		}
dvander@1385
   811
dvander@1385
   812
		grow();
dvander@1385
   813
dvander@1385
   814
		return x_check2(c1, c2, to_check+1);
dvander@1385
   815
	}
dvander@1385
   816
	unsigned int x_check_multi(
dvander@1385
   817
		unsigned int offsets[], 
dvander@1385
   818
		unsigned int count,
dvander@1385
   819
		unsigned int start=1)
dvander@1385
   820
	{
dvander@1385
   821
		KTrieNode *cur;
dvander@1385
   822
		unsigned int to_check = m_baseSize;
dvander@1385
   823
		unsigned int highest = 0;
dvander@1385
   824
dvander@1385
   825
		for (unsigned int i=0; i<count; i++)
dvander@1385
   826
		{
dvander@1385
   827
			if (offsets[i] > highest)
dvander@1385
   828
			{
dvander@1385
   829
				highest = offsets[i];
dvander@1385
   830
			}
dvander@1385
   831
		}
dvander@1385
   832
dvander@1385
   833
		to_check -= highest;
dvander@1385
   834
dvander@1385
   835
		for (unsigned int i=start; i<=to_check; i++)
dvander@1385
   836
		{
dvander@1385
   837
			bool okay = true;
dvander@1385
   838
			for (unsigned int j=0; j<count; j++)
dvander@1385
   839
			{
dvander@1385
   840
				cur = &m_base[i+offsets[j]];
dvander@1385
   841
				if (cur->mode != Node_Unused)
dvander@1385
   842
				{
dvander@1385
   843
					okay = false;
dvander@1385
   844
					break;
dvander@1385
   845
				}
dvander@1385
   846
			}
dvander@1385
   847
			if (okay)
dvander@1385
   848
			{
dvander@1385
   849
				return i;
dvander@1385
   850
			}
dvander@1385
   851
		}
dvander@1385
   852
dvander@1385
   853
		grow();
dvander@1385
   854
dvander@1385
   855
		return x_check_multi(offsets, count, to_check+1);
dvander@1385
   856
	}
dvander@1385
   857
private:
dvander@1385
   858
	KTrieNode *m_base;			/* Base array for the sparse tables */
dvander@1385
   859
	char *m_stringtab;			/* String table pointer */
dvander@1385
   860
	unsigned int m_baseSize;	/* Size of the base array, in members */
dvander@1385
   861
	unsigned int m_stSize;		/* Size of the string table, in bytes */
dvander@1385
   862
	unsigned int m_tail;		/* Current unused offset into the string table */
dvander@1385
   863
};
dvander@1385
   864
dvander@1389
   865
/**
dvander@1389
   866
 * Double Array Trie algorithm, based on:
dvander@1389
   867
 * An Efficient Implementation of Trie Structures, by
dvander@1389
   868
 *  Jun-ichi Aoe and Katsushi Maromoto, and Takashi Sato
dvander@1389
   869
 * from Software - Practice and Experience, Vol. 22(9), 695-721 (September 1992)
dvander@1389
   870
 *
dvander@1389
   871
 *  A Trie is a simple data structure which stores strings as DFAs, with each 
dvander@1389
   872
 * transition state being a string entry.  For example, observe the following strings:
dvander@1389
   873
 *
dvander@1389
   874
 * BAILOPAN, BAT, BACON, BACK
dvander@1389
   875
 *  These transition as the follow production rules:
dvander@1389
   876
 *  B -> ...                  B
dvander@1389
   877
 *       A -> ...             BA
dvander@1389
   878
 *            I -> ...        BAI
dvander@1389
   879
 *                 LOPAN      BAILOPAN
dvander@1389
   880
 *            T -> ...        BAT
dvander@1389
   881
 *            C ->            BAC
dvander@1389
   882
 *                 O -> ...   BACO
dvander@1389
   883
 *                      N     BACON
dvander@1389
   884
 *                 K          BACK
dvander@1389
   885
 *
dvander@1389
   886
 *  The standard implementation for this - using lists - gives a slow linear lookup, somewhere between
dvander@1389
   887
 * O(N+M) or O(log n).  A faster implementation is proposed in the paper above, which is based on compacting
dvander@1389
   888
 * the transition states into two arrays.  In the paper's implementation, two arrays are used, and thus it is
dvander@1389
   889
 * called the "Double Array" algorithm.  However, the CHECK array's size is maintained the same as BASE, 
dvander@1389
   890
 * so they can be combined into one structure.  The array seems complex at first, but is very simple: it is a
dvander@1389
   891
 * tree structure flattened out into a single vector.  I am calling this implementation the Flat Array Trie.
dvander@1389
   892
 *
dvander@1389
   893
 *  BASE[] is an array where each member is a node in the Trie.  The node can either be UNUSED (empty), an ARC
dvander@1389
   894
 * (containing an offset to the next set of ARCs), or a TERMINATOR (contains the rest of a string).
dvander@1389
   895
 * Each node has an index which must be interpreted based on the node type.  If the node is a TERMINATOR, then the
dvander@1389
   896
 * index is an index into a string table, to find the rest of the string.  
dvander@1389
   897
 *  If the node is an ARC, the index is another index into BASE.  For each possible token that can follow the
dvander@1389
   898
 * current token, the value of those tokens can be added to the index given in the ARC.  Thus, given a current
dvander@1389
   899
 * position and the next desired token, the current arc will jump to another arc which can contain either:
dvander@1389
   900
 *   1) An invalid production (collision, no entry exists)
dvander@1389
   901
 *   2) An empty production (no entry exists)
dvander@1389
   902
 *   3) Another arc label (the string ends here or continues into more productions)
dvander@1389
   903
 *   4) A TERMINATOR (the string ends here and contains an unused set of productions)
dvander@1389
   904
 * 
dvander@1389
   905
 *  So, given current offset N (starting at N=1), jumping to token C means the next offset will be:
dvander@1389
   906
 *      offs = BASE[n] + C
dvander@1389
   907
 *  Thus, the next node will be at:
dvander@1389
   908
 *      BASE[BASE[n] + C]
dvander@1389
   909
 * 
dvander@1389
   910
 *  This allows each ARC to specify the base offset for any of its ARC children, like a tree.  Each node specifies
dvander@1389
   911
 * its parent ARC -- so if an invalid offset is specified, the parent will not match, and thus no such derived 
dvander@1389
   912
 * string exists.
dvander@1389
   913
 *
dvander@1389
   914
 *  This means that arrays can be laid out "sparsely," maximizing their usage.  Note that N need not be related to
dvander@1389
   915
 * the range of tokens (1-256).  I.e., a base index does not have to be at 1, 256, 512, et cetera.  This is because
dvander@1389
   916
 * insertion comes with a small deal of complexity.  To insert a new set of tokens T, the algorithm finds a new 
dvander@1389
   917
 * BASE index N such that BASE[N+T[i]] is unused for each T[i].  Thus, indirection is not necessarily linear; 
dvander@1389
   918
 * traversing a chain of ARC nodes can _and will_ jump around BASE.
dvander@1389
   919
 *
dvander@1389
   920
 *  Of course, given this level of flexibility in the array organization, there are collisions.  This is largely 
dvander@1389
   921
 * where insertions become slow, as the old chain must be relocated before the new one is used.  Relocation means 
dvander@1389
   922
 * finding one or more new base indexes, and this means traversing BASE until an acceptable index is found, such 
dvander@1389
   923
 * that each offset is unused (see description in previous paragraph).
dvander@1389
   924
 *
dvander@1389
   925
 *  However, it is not insertion time we are concerned about.  The "trie" name comes from reTRIEval.  We are only
dvander@1389
   926
 * concerned with lookup and deletion.  Both lookup and deletion are O(k), where k is relative to the length of the 
dvander@1389
   927
 * input string.  Note that it is best case O(1) and worst case O(k).  Deleting the entire trie is always O(1).
dvander@1389
   928
 */
dvander@1389
   929
dvander@1385
   930
#endif //_INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_