/* -*-C-*- ******************************************************************************** * * File: trie.c (Formerly trie.c) * Description: Functions to build a trie data structure. * Author: Mark Seaman, OCR Technology * Created: Fri Oct 16 14:37:00 1987 * Modified: Fri Jul 26 12:18:10 1991 (Mark Seaman) marks@hpgrlt * Language: C * Package: N/A * Status: Reusable Software Component * * (c) Copyright 1987, Hewlett-Packard Company. ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** http://www.apache.org/licenses/LICENSE-2.0 ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. * *********************************************************************************/ /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ #include "trie.h" #include "callcpp.h" #ifdef __UNIX__ #include #endif #include /*---------------------------------------------------------------------- V a r i a b l e s ----------------------------------------------------------------------*/ static inT32 move_counter = 0; static inT32 new_counter = 0; static inT32 edge_counter = 0; inT32 max_new_attempts = 0; /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ /********************************************************************** * add_edge_linkage * * Add a single edge linkage to between the two nodes. This function * can be used to add either forward or backward links. **********************************************************************/ void add_edge_linkage(EDGE_ARRAY dawg, NODE_REF node1, NODE_REF node2, EDGE_RECORD direction, char character, EDGE_RECORD word_end) { EDGE_REF edge1 = node1; EDGE_REF edge2; inT32 num_edges = edges_in_node (dawg, node1); inT32 last_one; word_end = (word_end ? WERD_END_FLAG : 0); if (num_edges == 0) { /* No edges yet */ direction = ((direction == FORWARD_EDGE) ? DIRECTION_FLAG : 0); link_edge (dawg, edge1, node2, character, LAST_FLAG | direction | word_end); } else { /* Forward links */ if (direction == FORWARD_EDGE) { last_one = (forward_edge (dawg, edge1) ? 0 : LAST_FLAG); if (debug) cprintf ("moving edges (nodes = " REFFORMAT ", " REFFORMAT \ ", num = %d)\n", edge1, edge1+1, num_edges); copy_edges (dawg, edge1, edge1+1, num_edges); link_edge (dawg, edge1, node2, character, last_one | DIRECTION_FLAG | word_end); } else { /* Backward links */ if (forward_edge (dawg, edge1)) edge_loop(dawg, edge1); /* Existing back edges */ if (backward_edge (dawg,edge1)) { num_edges = 0; edge2 = edge1; do { num_edges++; } edge_loop(dawg, edge2); if (debug) cprintf ("moving edges (nodes = " REFFORMAT ", " REFFORMAT \ ", num = %d)\n", edge1, edge1+1, num_edges); copy_edges (dawg, edge1, edge1+1, num_edges); link_edge(dawg, edge1, node2, character, word_end); } else { /* First back edge */ link_edge (dawg, edge1, node2, character, LAST_FLAG | word_end); } } } } /********************************************************************** * add_new_edge * * Add an edge between two nodes in the DAWG. Link the nodes both ways. **********************************************************************/ bool add_new_edge(EDGE_ARRAY dawg, NODE_REF *node1, NODE_REF *node2, char character, EDGE_RECORD word_end, inT32 max_num_edges, inT32 reserved_edges) { int direction; if (debug) cprintf ("add_new_edge (nodes = " REFFORMAT ", " REFFORMAT \ ", ch = '%c', end = %d)\n", *node1, *node2, character, word_end); edge_counter++; if (!move_node_if_needed(dawg, node1, max_num_edges, reserved_edges)) return false; if (!move_node_if_needed(dawg, node2, max_num_edges, reserved_edges)) return false; direction = (int) FORWARD_EDGE; add_edge_linkage(dawg, *node1, *node2, direction, character, word_end); direction = (int) BACKWARD_EDGE; add_edge_linkage(dawg, *node2, *node1, direction, character, word_end); return true; } /********************************************************************** * add_word_to_dawg * * Add in a word by creating the necessary nodes and edges. **********************************************************************/ void add_word_to_dawg(EDGE_ARRAY dawg, char *string, inT32 max_num_edges, inT32 reserved_edges) { EDGE_REF edge; NODE_REF last_node = 0; NODE_REF the_next_node; inT32 i; inT32 still_finding_chars = TRUE; inT32 word_end = FALSE; bool add_failed = false; if (debug) cprintf("Adding word %s\n", string); for (i=0; i reserved_edges) { cprintf ("error: Not enough room in root node, %d\n", edges_in_node (dawg, 0)); add_failed = true; } if (add_failed) { cprintf ("Re-initializing document dictionary...\n"); initialize_dawg(dawg,max_num_edges); } } /********************************************************************** * initialize_dawg * * Initialize the DAWG data structure for further used. Reset each of * the edge cells to NO_EDGE. **********************************************************************/ void initialize_dawg(EDGE_ARRAY dawg, inT32 max_num_edges) { inT32 x; //changed by jetsoft // these values were not getting changes. move_counter = 0; new_counter = 0; edge_counter = 0; // end jetsoft clear_all_edges(dawg, x, max_num_edges); } /********************************************************************** * move_node_if_needed * * Move the node location if there is a need to do it because there is * not enough room to add the new edges. **********************************************************************/ bool move_node_if_needed(EDGE_ARRAY dawg, NODE_REF* node, inT32 max_num_edges, inT32 reserved_edges) { if (room_in_node(dawg, *node)) return true; NODE_REF this_new_node; EDGE_REF edge; inT32 num_edges = edges_in_node (dawg, *node); if (debug) print_dawg_node(dawg, *node); this_new_node = new_dawg_node (dawg, num_edges + EDGE_NUM_MARGIN, max_num_edges, reserved_edges); if (this_new_node == 0) return false; if (debug) cprintf ("move_node (from = " REFFORMAT ", to = " REFFORMAT ", num = %d)\n", *node, this_new_node, num_edges); move_edges(dawg, *node, this_new_node, num_edges); if (debug) print_dawg_node(dawg, this_new_node); edge = this_new_node; if (forward_edge (dawg, edge)) { do { relocate_edge (dawg, next_node (dawg, edge), *node, this_new_node); } edge_loop (dawg, edge); } if (backward_edge (dawg, edge)) { do { relocate_edge (dawg, next_node (dawg, edge), *node, this_new_node); } edge_loop (dawg, edge); } move_counter++; *node = this_new_node; return true; } /********************************************************************** * new_dawg_node * * Create a space within the DAWG data structure to house a node that * consists of the requested number of edges. **********************************************************************/ NODE_REF new_dawg_node(EDGE_ARRAY dawg, inT32 num_edges, inT32 max_num_edges, inT32 reserved_edges) { inT32 i; inT32 n; inT32 edge_index; inT32 edge_collision; /* Try several times */ for (i=0; i 0) { fread (&node_index, sizeof (EDGE_REF), 1, file); fread (&num_edges, sizeof (inT32), 1, file); assert (node_index + num_edges < max_num_edges); fread (&dawg[node_index], sizeof (EDGE_RECORD), num_edges, file); if (debug > 2) { print_dawg_node (dawg, node_index); new_line (); } } fclose (file); if (error_occured) exit (1); } /********************************************************************** * read_word_list * * Read the requested file (containing a list of words) and add all * the words to the DAWG. **********************************************************************/ void read_word_list(const char *filename, EDGE_ARRAY dawg, inT32 max_num_edges, inT32 reserved_edges) { FILE *word_file; char string [CHARS_PER_LINE]; int word_count = 0; int old_debug = debug; if (debug > 0 && debug < 3) debug = 0; word_file = open_file (filename, "r"); initialize_dawg(dawg, max_num_edges); while (fgets (string, CHARS_PER_LINE, word_file) != NULL) { string [strlen (string) - 1] = (char) 0; ++word_count; if (debug && word_count % 10000 == 0) cprintf("Read %d words so far\n", word_count); if (string[0] != '\0' /* strlen (string) */) { add_word_to_dawg(dawg, string, max_num_edges, reserved_edges); if (! word_in_dawg (dawg, string)) { cprintf ("error: word not in DAWG after adding it '%s'\n", string); return; } } } debug = old_debug; if (debug) cprintf("Read %d words total.\n", word_count); fclose(word_file); } /********************************************************************** * relocate_edge * * The location of a node has moved, so an edge entry in another node * must be changed. Change the value of this edge entry to match the * new location of the node. **********************************************************************/ void relocate_edge(EDGE_ARRAY dawg, NODE_REF node, NODE_REF old_node, NODE_REF new_node) { EDGE_REF edge; if (debug) cprintf ("relocate (" REFFORMAT ", " REFFORMAT " ==> " \ REFFORMAT ")\n", node, old_node, new_node); edge = node; if (forward_edge (dawg, edge)) { do { if (next_node (dawg, edge) == old_node) { if (debug) cprintf ("forward assign (" REFFORMAT ", " REFFORMAT " ==> " \ REFFORMAT ")\n", edge, old_node, new_node); set_next_edge(dawg, edge, new_node); } } edge_loop (dawg, edge); } if (backward_edge (dawg, edge)) { do { if (next_node (dawg, edge) == old_node) { if (debug) cprintf ("backward assign (" REFFORMAT ", " REFFORMAT " ==> " \ REFFORMAT ")\n", edge, old_node, new_node); set_next_edge(dawg, edge, new_node); } } edge_loop(dawg, edge); } } /********************************************************************** * remove_edge * * Add a single edge linkage to between the two nodes. This function * can be used to add either forward or backward links. **********************************************************************/ void remove_edge(EDGE_ARRAY dawg, NODE_REF node1, NODE_REF node2, char character, EDGE_RECORD word_end) { remove_edge_linkage(dawg, node1, node2, FORWARD_EDGE, character, word_end); remove_edge_linkage(dawg, node2, node1, BACKWARD_EDGE, character, word_end); } /********************************************************************** * remove_edge_linkage * * Remove this edge reference from this node. Move the edge entries in * this node to fill the gap. **********************************************************************/ void remove_edge_linkage(EDGE_ARRAY dawg, NODE_REF node, NODE_REF next, EDGE_RECORD direction, char character, EDGE_RECORD word_end) { inT32 forward_edges; inT32 num_edges; NODE_REF e = node; inT32 last_flag; forward_edges = num_forward_edges (dawg, node); num_edges = edges_in_node (dawg, node); for (e=node; e 0)); fwrite (&edge, sizeof (EDGE_REF), 1, file); fwrite (&num_edges, sizeof (inT32), 1, file); fwrite (&edge_of (dawg,edge), sizeof (EDGE_RECORD), num_edges, file); node_count++; if (debug) { printf ("Writing node index=" REFFORMAT ", node_count=%d, edges=%d\n", node, node_count, num_edges); for (edge = node; edge < node + num_edges; edge++) { printf ("Writing index=" REFFORMAT ", old_node=" REFFORMAT \ ", letter=%c, flags=%d\n", node, next_node (dawg,edge), (char) edge_letter(dawg,edge), (char) edge_flags (dawg,edge)); } } edge = node + num_edges - 1; } } fclose (file); }