core/sm_trie.cpp
author David Anderson <dvander@alliedmods.net>
Tue Jan 16 20:36:09 2007 +0000 (2007-01-16)
changeset 300 186b2f7bf277
parent 216 38b1bdbc5bc9
child 335 02cc96355b09
permissions -rw-r--r--
fixed up bad includes
     1 #include <string.h>
     2 #include <stdlib.h>
     3 #include <assert.h>
     4 #include "sm_trie.h"
     5 
     6 /**
     7  * Double Array Trie algorithm, based on:
     8  * An Efficient Implementation of Trie Structures, by
     9  *  Jun-ichi Aoe and Katsushi Maromoto, and Takashi Sato
    10  * from Sofiware - Practice and Experience, Vol. 22(9), 695-721 (September 1992)
    11  *
    12  *  A Trie is a simple data structure which stores strings as DFAs, with each 
    13  * transition state being a string entry.  For example, observe the following strings:
    14  *
    15  * BAILOPAN, BAT, BACON, BACK
    16  *  These transition as the follow production rules:
    17  *  B -> ...                  B
    18  *       A -> ...             BA
    19  *            I -> ...        BAI
    20  *                 LOPAN      BAILOPAN
    21  *            T -> ...        BAT
    22  *            C ->            BAC
    23  *                 O -> ...   BACO
    24  *                      N     BACON
    25  *                 K          BACK
    26  *
    27  *  The standard implementation for this - using lists - gives a slow linear lookup, somewhere between
    28  * O(N+M) or O(log n).  A faster implementation is proposed in the paper above, which is based on compacting
    29  * the transition states into two arrays.  In the paper's implementation, two arrays are used, and thus it is
    30  * called the "Double Array" algorithm.  However, the CHECK array's size is maintained the same as BASE, 
    31  * so they can be combined into one structure.  The array seems complex at first, but is very simple: it is a
    32  * tree structure flattened out into a single vector.  I am calling this implementation the Flat Array Trie.
    33  *
    34  *  BASE[] is an array where each member is a node in the Trie.  The node can either be UNUSED (empty), an ARC
    35  * (containing an offset to the next set of ARCs), or a TERMINATOR (contains the rest of a string).
    36  * Each node has an index which must be interpeted based on the node type.  If the node is a TERMINATOR, then the
    37  * index is an index into a string table, to find the rest of the string.  
    38  *  If the node is an ARC, the index is another index into BASE.  For each possible token that can follow the
    39  * current token, the value of those tokens can be added to the index given in the ARC.  Thus, given a current
    40  * position and the next desired token, the current arc will jump to another arc which can contain either:
    41  *   1) An invalid production (collision, no entry exists)
    42  *   2) An empty production (no entry exists)
    43  *   3) Another arc label (the string ends here or continues into more productions)
    44  *   4) A TERMINATOR (the string ends here and contains an unused set of productions)
    45  * 
    46  *  So, given current offset N (starting at N=1), jumping to token C means the next offset will be:
    47  *      offs = BASE[n] + C
    48  *  Thus, the next node will be at:
    49  *      BASE[BASE[n] + C]
    50  * 
    51  *  This allows each ARC to specify the base offset for any of its ARC children, like a tree.  Each node specifies
    52  * its parent ARC -- so if an invalid offset is specified, the parent will not match, and thus no such derived 
    53  * string exists.
    54  *
    55  *  This means that arrays can be laid out "sparsely," maximizing their usage.  Note that N need not be related to
    56  * 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
    57  * insertion comes with a small deal of complexity.  To insert a new set of tokens T, the algorithm finds a new 
    58  * BASE index N such that BASE[N+T[i]] is unused for each T[i].  Thus, indirection is not necessarily linear; 
    59  * traversing a chain of ARC nodes can _and will_ jump around BASE.
    60  *
    61  *  Of course, given this level of flexibility in the array organization, there are collisions.  This is largely 
    62  * where insertions become slow, as the old chain must be relocated before the new one is used.  Relocation means 
    63  * finding one or more new base indexes, and this means traversing BASE until an acceptable index is found, such 
    64  * that each offset is unused (see description in previous paragraph).
    65  *
    66  *  However, it is not insertion time we are concerned about.  The "trie" name comes from reTRIEval.  We are only
    67  * concerned with lookup and deletion.  Both lookup and deletion are O(k), where k is relative to the length of the 
    68  * input string.  Note that it is best case O(1) and worst case O(k).  Deleting the entire trie is always O(1).
    69  */
    70 
    71 /**
    72  * Optimization ideas for the future:
    73  * 1) Store a reference count for each arc, with the count of sub-children.
    74  *    This could let us break out of "children searches" for case 4 easily.
    75  * 2) Use a 'free list' so we can easily search the trie for free points.
    76  *    This would drastically speed up x_check*
    77  */
    78 
    79 enum NodeType
    80 {
    81 	Node_Unused = 0,		/* Node is not being used (sparse) */
    82 	Node_Arc,				/* Node is part of an arc and does not terminate */
    83 	Node_Term,				/* Node is a terminator */
    84 };
    85 
    86 struct TrieNode
    87 {
    88 	/**
    89 	 * For Node_Arc, this index stores the 'base' offset to the next arc chain.
    90 	 *   I.e. to jump from this arc to character C, it will be at base[idx+C].
    91 	 * For Node_Term, this is an index into the string table.
    92 	 */
    93 	unsigned int idx;
    94 
    95 	/**
    96 	 * This contains the prior arc that we must have come from.
    97 	 * For example, if arc 63 has a base jump of index 12, and we want to see if
    98 	 * there is a valid character C, the parent of 12+C must be 63.
    99 	 */
   100 	unsigned int parent;
   101 
   102 	void *value;			/* Value associated with this node */
   103 	NodeType mode;			/* Current usage type of the node */
   104 	bool valset;			/* Whether or not a value is set */
   105 };
   106 
   107 struct Trie
   108 {
   109 	TrieNode *base;			/* Base array for the sparse tables */
   110 	char *stringtab;		/* String table pointer */
   111 	unsigned int baseSize;	/* Size of the base array, in members */
   112 	unsigned int stSize;	/* Size of the string table, in bytes */
   113 	unsigned int tail;		/* Current unused offset into the string table */
   114 };
   115 
   116 inline unsigned char charval(char c)
   117 {
   118 	return (unsigned char)c;
   119 }
   120 
   121 bool sm_trie_grow(Trie *trie)
   122 {
   123 	/* The current # of nodes in the tree is trie->baseSize + 1 */
   124 	unsigned int curSize = trie->baseSize;
   125 	unsigned int newSize = curSize * 2;
   126 
   127 	//:TODO: Make functions calling this return failure if this fails 
   128 	trie->base = (TrieNode *)realloc(trie->base, (newSize + 1) * sizeof(TrieNode));
   129 	if (!trie->base)
   130 	{
   131 		return false;
   132 	}
   133 
   134 	memset(&trie->base[curSize+1], 0, (newSize - curSize) * sizeof(TrieNode));
   135 	trie->baseSize = newSize;
   136 
   137 	return true;
   138 }
   139 
   140 unsigned int x_check(Trie *trie, char c, unsigned int start=1)
   141 {
   142 	TrieNode *base = trie->base;
   143 	unsigned char _c = charval(c);
   144 	unsigned int to_check = trie->baseSize - _c;
   145 	for (unsigned int i=start; i<=to_check; i++)
   146 	{
   147 		if (base[i+_c].mode == Node_Unused)
   148 		{
   149 			return i;
   150 		}
   151 	}
   152 
   153 	sm_trie_grow(trie);
   154 
   155 	return x_check(trie, c, to_check+1);
   156 }
   157 
   158 unsigned int x_check2(Trie *trie, char c1, char c2, unsigned int start=1)
   159 {
   160 	TrieNode *base = trie->base;
   161 	unsigned char _c1 = charval(c1);
   162 	unsigned char _c2 = charval(c2);
   163 	unsigned int to_check = trie->baseSize - (_c1 > _c2 ? _c1 : _c2);
   164 	for (unsigned int i=start; i<=to_check; i++)
   165 	{
   166 		if (base[i+_c1].mode == Node_Unused
   167 			&& base[i+_c2].mode == Node_Unused)
   168 		{
   169 			return i;
   170 		}
   171 	}
   172 
   173 	sm_trie_grow(trie);
   174 
   175 	return x_check2(trie, c1, c2, to_check+1);
   176 }
   177 
   178 unsigned int x_check_multi(Trie *trie, 
   179 							unsigned int offsets[], 
   180 							unsigned int count,
   181 							unsigned int start=1)
   182 {
   183 	TrieNode *base = trie->base;
   184 	TrieNode *cur;
   185 	unsigned int to_check = trie->baseSize;
   186 	unsigned int highest = 0;
   187 
   188 	for (unsigned int i=0; i<count; i++)
   189 	{
   190 		if (offsets[i] > highest)
   191 		{
   192 			highest = offsets[i];
   193 		}
   194 	}
   195 
   196 	to_check -= highest;
   197 
   198 	for (unsigned int i=start; i<=to_check; i++)
   199 	{
   200 		bool okay = true;
   201 		for (unsigned int j=0; j<count; j++)
   202 		{
   203 			cur = &base[i+offsets[j]];
   204 			if (cur->mode != Node_Unused)
   205 			{
   206 				okay = false;
   207 				break;
   208 			}
   209 		}
   210 		if (okay)
   211 		{
   212 			return i;
   213 		}
   214 	}
   215 
   216 	sm_trie_grow(trie);
   217 
   218 	return x_check_multi(trie, offsets, count, to_check+1);
   219 }
   220 
   221 unsigned int x_addstring(Trie *trie, const char *ptr)
   222 {
   223 	size_t len = strlen(ptr) + 1;
   224 
   225 	if (trie->tail + len >= trie->stSize)
   226 	{		
   227 		while (trie->tail + len >= trie->stSize)
   228 		{
   229 			trie->stSize *= 2;
   230 		}
   231 		trie->stringtab = (char *)realloc(trie->stringtab, trie->stSize);
   232 	}
   233 
   234 	unsigned int tail = trie->tail;
   235 	strcpy(&trie->stringtab[tail], ptr);
   236 	trie->tail += len;
   237 
   238 	return tail;
   239 }
   240 
   241 Trie *sm_trie_create()
   242 {
   243 	Trie *t = new Trie;
   244 
   245 	t->base = (TrieNode *)malloc(sizeof(TrieNode) * (256 + 1));
   246 	t->stringtab = (char *)malloc(sizeof(char) * 256);
   247 	t->baseSize = 256;
   248 	t->stSize = 256;
   249 	t->tail = 0;
   250 
   251 	memset(t->base, 0, sizeof(TrieNode) * (256 + 1));
   252 	memset(t->stringtab, 0, sizeof(char) * 256);
   253 
   254 	/* Sentinel root node */
   255 	t->base[1].idx = 1;
   256 	t->base[1].mode = Node_Arc;
   257 	t->base[1].parent = 1;
   258 
   259 	return t;
   260 }
   261 
   262 void sm_trie_destroy(Trie *trie)
   263 {
   264 	free(trie->base);
   265 	free(trie->stringtab);
   266 	delete trie;
   267 }
   268 
   269 bool sm_trie_delete(Trie *trie, const char *key)
   270 {
   271 	unsigned int lastidx = 1;		/* the last node index */
   272 	unsigned int curidx;			/* current node index */
   273 	const char *keyptr = key;		/* input stream at current token */
   274 	TrieNode *node = NULL;			/* current node being processed */
   275 	TrieNode *base = trie->base;
   276 
   277 	if (!*key)
   278 	{
   279 		return false;
   280 	}
   281 
   282 	/* Start traversing at the root node */
   283 	do
   284 	{
   285 		/* Find where the next character is, then advance */
   286 		curidx = base[lastidx].idx;
   287 		node = &base[curidx];
   288 		curidx += charval(*keyptr);
   289 		node = &base[curidx];
   290 		keyptr++;
   291 
   292 		/* Check if this slot is supposed to be empty or is a collision */
   293 		if ((curidx > trie->baseSize) || node->mode == Node_Unused || node->parent != lastidx)
   294 		{
   295 			return false;
   296 		} else if (node->mode == Node_Term) {
   297 			char *term = &trie->stringtab[node->idx];
   298 			if (strcmp(keyptr, term) == 0)
   299 			{
   300 				break;
   301 			}
   302 		}
   303 		lastidx = curidx;
   304 	} while (*keyptr != '\0');
   305 
   306 	assert(node != NULL);
   307 
   308 	if (!node->valset)
   309 	{
   310 		return false;
   311 	}
   312 
   313 	node->valset = false;
   314 	node->value = NULL;
   315 
   316 	return true;
   317 }
   318 
   319 bool sm_trie_retrieve(Trie *trie, const char *key, void **value)
   320 {
   321 	unsigned int lastidx = 1;		/* the last node index */
   322 	unsigned int curidx;			/* current node index */
   323 	const char *keyptr = key;		/* input stream at current token */
   324 	TrieNode *node = NULL;			/* current node being processed */
   325 	TrieNode *base = trie->base;
   326 
   327 	if (!*key)
   328 	{
   329 		return false;
   330 	}
   331 
   332 	/* Start traversing at the root node */
   333 	do
   334 	{
   335 		/* Find where the next character is, then advance */
   336 		curidx = base[lastidx].idx;
   337 		node = &base[curidx];
   338 		curidx += charval(*keyptr);
   339 		node = &base[curidx];
   340 		keyptr++;
   341 
   342 		/* Check if this slot is supposed to be empty or is a collision */
   343 		if ((curidx > trie->baseSize) || node->mode == Node_Unused || node->parent != lastidx)
   344 		{
   345 			return false;
   346 		} else if (node->mode == Node_Term) {
   347 			char *term = &trie->stringtab[node->idx];
   348 			if (strcmp(keyptr, term) == 0)
   349 			{
   350 				break;
   351 			}
   352 		}
   353 		lastidx = curidx;
   354 	} while (*keyptr != '\0');
   355 
   356 	assert(node != NULL);
   357 
   358 	if (!node->valset)
   359 	{
   360 		return false;
   361 	}
   362 
   363 	if (value)
   364 	{
   365 		*value = node->value;
   366 	}
   367 
   368 	return true;
   369 }
   370 
   371 bool sm_trie_insert(Trie *trie, const char *key, void *value)
   372 {
   373 	unsigned int lastidx = 1;		/* the last node index */
   374 	unsigned int curidx;			/* current node index */
   375 	const char *keyptr = key;		/* input stream at current token */
   376 	TrieNode *node = NULL;			/* current node being processed */
   377 	TrieNode *basenode = NULL;		/* current base node being processed */
   378 	unsigned int q;					/* temporary var for x_check results */
   379 	TrieNode *base = trie->base;
   380 	unsigned int curoffs;			/* current offset */
   381 
   382 	/* Do not handle empty strings for simplicity */
   383 	if (!*key)
   384 	{
   385 		return false;
   386 	}
   387 
   388 	/* Start traversing at the root node (1) */
   389 	do
   390 	{
   391 		/* Find where the next character is, then advance */
   392 		curidx = base[lastidx].idx;
   393 		basenode = &base[curidx];
   394 		curoffs = charval(*keyptr);
   395 		curidx += curoffs;
   396 		node = &base[curidx];
   397 		keyptr++;
   398 
   399 		/* Check if this slot is supposed to be empty.  If so, we need to handle CASES 1/2:
   400 		 * Insertion without collisions
   401 		 */
   402 		if ( (curidx > trie->baseSize) || (node->mode == Node_Unused) )
   403 		{
   404 			if (curidx > trie->baseSize)
   405 			{
   406 				if (!sm_trie_grow(trie))
   407 				{
   408 					return false;
   409 				}
   410 				node = &trie->base[curidx];
   411 			}
   412 			node->parent = lastidx;
   413 			if (*keyptr == '\0')
   414 			{
   415 				node->mode = Node_Arc;
   416 			} else {
   417 				node->idx = x_addstring(trie, keyptr);
   418 				node->mode = Node_Term;
   419 			}
   420 			node->valset = true;
   421 			node->value = value;
   422 			
   423 			return true;
   424 		} else if (node->parent != lastidx) {
   425 			/* Collision! We have to split up the tree here.  CASE 4:
   426 			 * Insertion when a new word is inserted with a collision.
   427 			 * NOTE: This is the hardest case to handle.  All below examples are based on:
   428 			 * BACHELOR, BADGE, inserting BABY.
   429 			 * The problematic production here is A -> B, where B is already being used.
   430 			 *
   431 			 * This process has to rotate one half of the 'A' arc.  We generate two lists:
   432 			 *  Outgoing Arcs - Anything leaving this 'A'
   433 			 *  Incoming Arcs - Anything going to this 'A'
   434 			 * Whichever list is smaller will be moved.  Note that this works because the intersection
   435 			 * affects both arc chains, and moving one will make the slot available to either.
   436 			 */
   437 			TrieNode *cur;
   438 			
   439 			/* Find every node arcing from the last node.
   440 			 * I.e. for BACHELOR, BADGE, BABY,
   441 			 * The arcs leaving A will be C and D, but our current node is B -> *.
   442 			 * Thus, we use the last index (A) to find the base for arcs leaving A.
   443 			 */
   444 			unsigned int outgoing_base = base[lastidx].idx;
   445 			unsigned int outgoing_list[256];
   446 			unsigned int outgoing_count = 0;	/* count the current index here */
   447 			cur = &base[outgoing_base] + 1;
   448 			unsigned int outgoing_limit = 255;
   449 
   450 			if (outgoing_base + outgoing_limit > trie->baseSize)
   451 			{
   452 				outgoing_limit = trie->baseSize - outgoing_base;
   453 			}
   454 
   455 			for (unsigned int i=1; i<=outgoing_limit; i++,cur++)
   456 			{
   457 				if (cur->mode == Node_Unused || cur->parent != lastidx)
   458 				{
   459 					continue;
   460 				}
   461 				outgoing_list[outgoing_count++] = i;
   462 			}
   463 			outgoing_list[outgoing_count++] = curidx - outgoing_base;
   464 
   465 			/* Now we need to find all the arcs leaving our parent...
   466 			 * Note: the inconsistency is the base of our parent.  
   467 			 */
   468 			assert(base[node->parent].mode == Node_Arc);
   469 			unsigned int incoming_list[256];
   470 			unsigned int incoming_base = base[node->parent].idx;
   471 			unsigned int incoming_count = 0;
   472 			unsigned int incoming_limit = 255;
   473 			cur = &base[incoming_base] + 1;
   474 
   475 			if (incoming_base + incoming_limit > trie->baseSize)
   476 			{
   477 				incoming_limit = trie->baseSize - incoming_base;
   478 			}
   479 
   480 			assert(incoming_limit > 0 && incoming_limit <= 255);
   481 
   482 			for (unsigned int i=1; i<=255; i++,cur++)
   483 			{
   484 				if (cur->mode == Node_Arc || cur->mode == Node_Term)
   485 				{
   486 					if (cur->parent == node->parent)
   487 					{
   488 						incoming_list[incoming_count++] = i;
   489 					}
   490 				}
   491 			}
   492 
   493 			if (incoming_count < outgoing_count + 1)
   494 			{
   495 				unsigned int q = x_check_multi(trie, incoming_list, incoming_count);
   496 
   497 				base = trie->base;
   498 				node = &base[curidx];
   499 
   500 				/* If we're incoming, we need to modify our parent */
   501 				base[node->parent].idx = q;
   502 
   503 				/* For each node in the "to move" list,
   504 				 * Relocate the node's info to the new position.
   505 				 */
   506 				unsigned int idx, newidx, oldidx;
   507 				for (unsigned int i=0; i<incoming_count; i++)
   508 				{
   509 					idx = incoming_list[i];
   510 					newidx = q + idx;
   511 					oldidx = incoming_base + idx;
   512 					if (oldidx == lastidx)
   513 					{
   514 						/* Important! Make sure we're not invalidating our sacred lastidx */
   515 						lastidx = newidx;
   516 					}
   517 					base[newidx] = base[oldidx];
   518 					assert(base[base[newidx].parent].mode == Node_Arc);
   519 					memset(&base[oldidx], 0, sizeof(TrieNode));
   520 					/* If we are not a terminator, we have children we must take care of */
   521 					if (base[newidx].mode == Node_Arc)
   522 					{
   523 						TrieNode *check_base = &base[base[newidx].idx] + 1;
   524 						for (unsigned int j=1; j<=255; j++, check_base++)
   525 						{
   526 							if (check_base->parent == oldidx)
   527 							{
   528 								check_base->parent = newidx;
   529 							}
   530 						}
   531 					}
   532 				}
   533 			} else {
   534 				unsigned int q = x_check_multi(trie, outgoing_list, outgoing_count);
   535 
   536 				base = trie->base;
   537 				node = &base[curidx];
   538 
   539 				/* If we're outgoing, we need to modify our own base */
   540 				base[lastidx].idx = q;
   541 
   542 				/* Take the last index (curidx) out of the list.  Technically we are not moving this,
   543 				 * since it's already being used by something else.  
   544 				 */
   545 				outgoing_count--;
   546 
   547 				/* For each node in the "to move" list,
   548 				 * Relocate the node's info to the new position.
   549 				 */
   550 				unsigned int idx, newidx, oldidx;
   551 				for (unsigned int i=0; i<outgoing_count; i++)
   552 				{
   553 					idx = outgoing_list[i];
   554 					newidx = q + idx;
   555 					oldidx = outgoing_base + idx;
   556 					if (oldidx == lastidx)
   557 					{
   558 						/* Important! Make sure we're not invalidating our sacred lastidx */
   559 						lastidx = newidx;
   560 					}
   561 					base[newidx] = base[oldidx];
   562 					assert(base[base[newidx].parent].mode == Node_Arc);
   563 					memset(&base[oldidx], 0, sizeof(TrieNode));
   564 					/* If we are not a terminator, we have children we must take care of */
   565 					if (base[newidx].mode == Node_Arc)
   566 					{
   567 						TrieNode *check_base = &base[base[newidx].idx] + 1;
   568 						for (unsigned int j=1; j<=255; j++, check_base++)
   569 						{
   570 							if (check_base->parent == oldidx)
   571 							{
   572 								check_base->parent = newidx;
   573 							}
   574 						}
   575 					}
   576 				}
   577 
   578 				/* Take the invisible node and use it as our new node */
   579 				node = &base[q + outgoing_list[outgoing_count]];
   580 			}
   581 
   582 			/* We're finally done! */
   583 			node->parent = lastidx;
   584 			if (*keyptr == '\0')
   585 			{
   586 				node->mode = Node_Arc;
   587 			} else {
   588 				node->idx = x_addstring(trie, keyptr);
   589 				node->mode = Node_Term;
   590 			}
   591 			node->valset = true;
   592 			node->value = value;
   593 
   594 			return true;
   595 		} else {
   596 			/* See what's in the next node - special case if terminator! */
   597 			if (node->mode == Node_Term)
   598 			{
   599 				/* If we're a terminator, we need to handle CASE 3:
   600 				 * Insertion when a terminating collision occurs
   601 				 */
   602 				char *term = &trie->stringtab[node->idx];
   603 				/* Do an initial browsing to make sure they're not the same string */
   604 				if (strcmp(keyptr, term) == 0)
   605 				{
   606 					if (!node->valset)
   607 					{
   608 						node->valset = true;
   609 						node->value = value;
   610 						return true;
   611 					}
   612 					/* Same string.  We can't insert. */
   613 					return false;
   614 				}
   615 				/* For each matching character pair, we need to disband the terminator.
   616 				 * This splits the similar prefix into a single arc path.
   617 				 * First, save the old values so we can move them to a new node.
   618 				 * Next, for each loop:
   619 				 *  Take the current (invalid) node, and point it to the next arc base.
   620 				 *  Set the current node to the node at the next arc.
   621 				 */
   622 				void *oldvalue = node->value;
   623 				bool oldvalset = node->valset;
   624 				if (*term == *keyptr)
   625 				{
   626 				while (*term == *keyptr)
   627 				{
   628 					/* Find the next free slot in the check array.
   629 					 * This is the "vector base" essentially
   630 					 */
   631 					q = x_check(trie, *term);
   632 						base = trie->base;
   633 						node = &base[curidx];
   634 					/* Point the node to the next new base */
   635 					node->idx = q;
   636 					node->mode = Node_Arc;
   637 					node->valset = false;
   638 					/* Advance the input stream and local variables */
   639 					lastidx = curidx;
   640 					curidx = q + charval(*term);
   641 					node = &base[curidx];
   642 					/* Make sure the new current node has its parent set. */
   643 					node->parent = lastidx;
   644 					node->mode = Node_Arc;	/* Just in case we run x_check again */
   645 					*term = '\0';	/* Unmark the string table here */
   646 					term++;
   647 					keyptr++;
   648 					}
   649 				} else {
   650 					node->valset = false;
   651 				}
   652 				/* We're done inserting new pairs.  If one of them is exhausted,
   653 				 * we take special shortcuts.
   654 				 */
   655 				if (*term == '\0')				//EX: BADGERHOUSE added over B -> ADGER.  
   656 				{
   657 					/* First backpatch the current node - it ends the newly split terminator.
   658 					 * In the example, this would mean the node is the production from R -> ?
   659 					 * This node ends the old BADGER, so we set it here.
   660 					 */
   661 					node->valset = oldvalset;
   662 					node->value = oldvalue;
   663 
   664 					/* The terminator was split up, but pieces of keyptr remain.
   665 					 * We need to generate a new production, in this example, R -> H,
   666 					 * with H being a terminator to OUSE.  Thus we get:
   667 					 * B,A,D,G,E,R*,H*->OUSE (* = value set).
   668 					 * NOTE: parent was last set at the end of the while loop.
   669 					 */
   670 					/* Get the new base and apply re-basing */
   671 					q = x_check(trie, *keyptr);
   672 					base = trie->base;
   673 					node = &base[curidx];
   674 
   675 					node->idx = q;
   676 					node->mode = Node_Arc;
   677 					lastidx = curidx;
   678 					/* Finish the final node */
   679 					curidx = q + charval(*keyptr);
   680 					node = &trie->base[curidx];
   681 					keyptr++;
   682 					/* Optimize - don't add to string table if there's nothing more to eat */
   683 					if (*keyptr == '\0')
   684 					{
   685 						node->mode = Node_Arc;
   686 					} else {
   687 						node->idx = x_addstring(trie, keyptr);
   688 						node->mode = Node_Term;
   689 					}
   690 					node->parent = lastidx;
   691 					node->valset = true;
   692 					node->value = value;
   693 				} else if (*keyptr == '\0') {	//EX: BADGER added over B -> ADGERHOUSE
   694 					/* First backpatch the current node - it ends newly split input string.
   695 					 * This is the exact opposite of the above procedure.
   696 					 */
   697 					node->valset = true;
   698 					node->value = value;
   699 
   700 					/* Get the new base and apply re-basing */
   701 					q = x_check(trie, *term);
   702 					base = trie->base;
   703 					node = &base[curidx];
   704 
   705 					node->idx = q;
   706 					node->mode = Node_Arc;
   707 					lastidx = curidx;
   708 					/* Finish the final node */
   709 					curidx = q + charval(*term);
   710 					node = &trie->base[curidx];
   711 					term++;
   712 					/* Optimize - don't add to string table if there's nothing more to eat */
   713 					if (*term == '\0')
   714 					{
   715 						node->mode = Node_Arc;
   716 					} else {
   717 						node->idx = (term - trie->stringtab); /* Already in the string table! */
   718 						node->mode = Node_Term;
   719 					}
   720 					node->parent = lastidx;
   721 					node->valset = oldvalset;
   722 					node->value = oldvalue;
   723 				} else {
   724 					/* Finally, we have to create two new nodes instead of just one. */
   725 					node->mode = Node_Arc;
   726 					
   727 					/* Get the new base and apply re-basing */
   728 					q = x_check2(trie, *keyptr, *term);
   729 					base = trie->base;
   730 					node = &base[curidx];
   731 
   732 					node->idx = q;
   733 					lastidx = curidx;
   734 
   735 					/* Re-create the old terminated node */
   736 					curidx = q + charval(*term);
   737 					node = &trie->base[curidx];
   738 					term++;
   739 					node->valset = oldvalset;
   740 					node->value = oldvalue;
   741 					node->parent = lastidx;
   742 					if (*term == '\0')
   743 					{
   744 						node->mode = Node_Arc;
   745 					} else {
   746 						node->mode = Node_Term;
   747 						node->idx = (term - trie->stringtab); /* Already in the string table! */
   748 					}
   749 
   750 					/* Create the new keyed input node */
   751 					curidx = q + charval(*keyptr);
   752 					node = &trie->base[curidx];
   753 					keyptr++;
   754 					node->valset = true;
   755 					node->value = value;
   756 					node->parent = lastidx;
   757 					if (*keyptr == '\0')
   758 					{
   759 						node->mode = Node_Arc;
   760 					} else {
   761 						node->mode = Node_Term;
   762 						node->idx = x_addstring(trie, keyptr);
   763 					}
   764 				}
   765 
   766 				/* Phew! */
   767 				return true;
   768 			} else {
   769 				assert(node->mode == Node_Arc);
   770 			}
   771 		}
   772 		lastidx = curidx;
   773 	} while (*keyptr != '\0');
   774 
   775 	assert(node);
   776 
   777 	/* If we've exhausted the string and we have a valid reached node,
   778 	 * the production rule already existed.  Make sure it's valid to set first.
   779 	 */
   780 
   781 	/* We have to be an Arc.  If the last result was anything else, we would have returned a new 
   782 	 * production earlier.
   783 	 */
   784 	assert(node->mode == Node_Arc);
   785 
   786 	/* Furthermore, if this is the last arc label in an arc, we should have a value set. */
   787 	if (node->idx == 0)
   788 	{
   789 		assert(node->valset == true);
   790 	}
   791 
   792 	if (!node->valset)
   793 	{
   794 		/* Insert is only possible if we have no production */
   795 		node->valset = true;
   796 		node->value = value;
   797 		return true;
   798 	}
   799 
   800 	return false;
   801 }