/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * * http://www.gnu.org/software/gnugo/ for more information. * * * * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * * 2008 and 2009 by the Free Software Foundation. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation - version 3 or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License in file COPYING for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; if not, write to the Free * * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02111, USA. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * fast pattern matching with DFA version 2.9 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "liberty.h" #include "patterns.h" #include "dfa-mkpat.h" #include "random.h" #include #include #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_UNISTD_H #include #endif /********************* * Public data * *********************/ /* If > 0 more detailed information is given */ int dfa_verbose = 0; /********************* * Private data * *********************/ /* auxiliary dfa's for high level functions */ #define DFA_BINS 33 /* Number of temporary bins used to store intermediate DFAs */ static dfa_t aux_dfa[DFA_BINS]; /* used to store intermediate DFAs */ static dfa_t aux_temp; /* used to store temporary DFAs */ /* To be sure that everything was well initialized */ static int dfa_was_initialized = 0; static int aux_count = 0; /* convert ATT_* values to the corresponding expected values on the board */ static const char att2val[8] = { '.', 'X', 'O', 'x', 'o', ',', 'a', '!' }; #define EXPECTED_VAL(att_val) att2val[att_val] /************************************************ * forward declaration of private functions * ************************************************/ static void clean_dfa(dfa_t *pdfa); static void resize_dfa(dfa_t *pdfa, int max_states, int max_indexes); static void create_dfa(dfa_t *pdfa, const char *str, int att_val); static void do_sync_product(int l, int r); static void sync_product(dfa_t *pout, dfa_t *pleft, dfa_t *pright); static void dfa_prepare_rotation_data(void); /******************************** * manipulating attributes list * ********************************/ /* * Test if val is member of the attributes set att */ static int member_att(dfa_t *pdfa, int att, int val) { while (att != 0) { if (pdfa->indexes[att].val == val) return 1; att = pdfa->indexes[att].next; } return 0; } /* * return the union of two attribute sets att1 & att2 * repectively from dfa1 and dfa2 into * att in dfa. */ static int union_att(dfa_t *pdfa, dfa_t *pdfa1, int att1, dfa_t *pdfa2, int att2) { int att; int att_aux; /* copy att1 in att */ att = 0; while (att1 != 0) { pdfa->last_index++; if (pdfa->last_index >= pdfa->max_indexes) resize_dfa(pdfa, pdfa->max_states, pdfa->max_indexes + DFA_RESIZE_STEP); att_aux = pdfa->last_index; pdfa->indexes[att_aux].val = pdfa1->indexes[att1].val; pdfa->indexes[att_aux].next = att; att = att_aux; att1 = pdfa1->indexes[att1].next; } /* add to att the new elements of att2 */ while (att2 != 0) { if (!member_att(pdfa, att, pdfa2->indexes[att2].val)) { pdfa->last_index++; if (pdfa->last_index >= pdfa->max_indexes) resize_dfa(pdfa, pdfa->max_states, pdfa->max_indexes + DFA_RESIZE_STEP); att_aux = pdfa->last_index; pdfa->indexes[att_aux].val = pdfa2->indexes[att2].val; pdfa->indexes[att_aux].next = att; att = att_aux; } att2 = pdfa2->indexes[att2].next; } return att; } /* Remove all attribute entry repetitions from a dfa. */ static void compactify_att(dfa_t *pdfa) { int k; int last = 0; int save_last = pdfa->last_index; int *map; int *search_first; int *search_next; int size = (save_last + 1) * sizeof(int); map = malloc(size); map[0] = 0; search_first = malloc(size); memset(search_first, 0, size); search_next = malloc(size); memset(search_next, 0, size); for (k = 1; k <= save_last; k++) { int i = search_first[pdfa->indexes[k].val]; if (i) { while (pdfa->indexes[i].next != pdfa->indexes[k].next) { if (!search_next[i]) { search_next[i] = ++last; i = 0; break; } i = search_next[i]; } } else search_first[pdfa->indexes[k].val] = ++last; if (i) map[k] = i; else { map[k] = last; pdfa->indexes[last] = pdfa->indexes[k]; } } free(search_first); free(search_next); if (last < save_last) { pdfa->last_index = last; for (k = 0; k <= pdfa->last_index; k++) pdfa->indexes[k].next = map[pdfa->indexes[k].next]; for (k = 0; k <= pdfa->last_state; k++) pdfa->states[k].att = map[pdfa->states[k].att]; if (0) fprintf(stderr, "compactified: %d attributes left of %d\n", last, save_last); compactify_att(pdfa); } free(map); } /********************** * manipulating dfa's * **********************/ /* * return the effective size of a dfa in kB. */ int dfa_size(dfa_t *pdfa) { int states_size, indexes_size; states_size = (pdfa->last_state + 1) * sizeof(state_rt_t); indexes_size = (pdfa->last_index + 1) * sizeof(attrib_rt_t); return (states_size + indexes_size + sizeof(dfa_rt_t)) / 1024; } /* * resize memory for a dfa */ static void resize_dfa(dfa_t *pdfa, int max_states, int max_indexes) { state_t *pBuf; attrib_t *pBuf2; int i; if (dfa_verbose > 1) fprintf(stderr, "Resizing dfa %s\n", pdfa->name); assert(pdfa->last_state <= pdfa->max_states); assert(pdfa->last_index <= pdfa->max_indexes); pBuf = realloc(pdfa->states, max_states * sizeof(*pBuf)); pBuf2 = realloc(pdfa->indexes, max_indexes * sizeof(*pBuf2)); if (pBuf == NULL || pBuf2 == NULL) { fprintf(stderr, "No memory left for dfa: %s", pdfa->name); exit(EXIT_FAILURE); } for (i = pdfa->max_states; i < max_states; i++) memset(pBuf + i, 0, sizeof(state_t)); for (i = pdfa->max_indexes; i < max_indexes; i++) memset(pBuf2 + i, 0, sizeof(attrib_t)); pdfa->states = pBuf; pdfa->max_states = max_states; pdfa->indexes = pBuf2; pdfa->max_indexes = max_indexes; } /* * dump a dfa (debugging purpose). */ static const char *line = "----------------------------------------------------\n"; void dump_dfa(FILE *f, dfa_t *pdfa) { int i; int att, k; fprintf(f, line); fprintf(f, " name : %s\n", pdfa->name); fprintf(f, " Nb states : %7d, max= %d\n", pdfa->last_state + 1, pdfa->max_states); fprintf(f, " Nb Indexes : %7d, max= %d\n", pdfa->last_index, pdfa->max_indexes); fprintf(f, " memory needed : %d Mb\n", dfa_size(pdfa) / 1024); fprintf(f, line); if (dfa_size(pdfa) > 10000) /* change this value if needed */ return; fprintf(f, " state | . | O | X | # | att \n"); fprintf(f, line); for (i = 1; i != pdfa->last_state + 1; i++) { int *pnext = pdfa->states[i].next; fprintf(f, " %6d |", i); fprintf(f, " %6d | %6d | %6d |", pnext[0], pnext[1], pnext[2]); fprintf(f, " %6d |", pnext[OUT_BOARD]); att = pdfa->states[i].att; k = 0; fprintf(f, " %5d:", att); while (att != 0 && k < 10) { fprintf(f, " %4d", pdfa->indexes[att].val); att = pdfa->indexes[att].next; k++; } if (att != 0) fprintf(f, " ..."); fprintf(f, "\n"); } fprintf(f, line); fflush(f); } /* * Reset a dfa */ static void clean_dfa(dfa_t *pdfa) { memset(pdfa->states, 0, pdfa->max_states * sizeof(state_t)); memset(pdfa->indexes, 0, pdfa->max_indexes * sizeof(attrib_t)); pdfa->last_state = 1; /* initial state */ pdfa->last_index = 0; pdfa->indexes[0].val = -1; } /* * allocate memory for a new dfa */ void new_dfa(dfa_t *pdfa, const char *name) { memset(pdfa, 0, sizeof(dfa_t)); resize_dfa(pdfa, DFA_INIT_SIZE, DFA_INIT_SIZE); clean_dfa(pdfa); if (name != NULL) strcpy(pdfa->name, name); else strcpy(pdfa->name, "noname "); if (dfa_verbose > 1) fprintf(stderr, "dfa %s is born :)\n", pdfa->name); } /* * free memory used by a dfa */ void kill_dfa(dfa_t *pdfa) { free(pdfa->states); free(pdfa->indexes); if (dfa_verbose > 1) fprintf(stderr, "dfa %s is dead :(\n", pdfa->name); memset(pdfa, 0, sizeof(dfa_t)); } /* * Copy a dfa and resize the destination dfa if necessary. */ void copy_dfa(dfa_t *p_to, dfa_t *p_from) { assert(p_to != p_from); if (p_to->max_states < p_from->last_state) resize_dfa(p_to, p_from->max_states, p_to->max_indexes); if (p_to->max_indexes < p_from->last_index) resize_dfa(p_to, p_to->max_states, p_from->max_indexes); clean_dfa(p_to); memcpy(p_to->states, p_from->states, sizeof(state_t) * (p_from->last_state + 1)); memcpy(p_to->indexes, p_from->indexes, sizeof(attrib_t) * (p_from->last_index + 1)); p_to->last_state = p_from->last_state; p_to->last_index = p_from->last_index; } /* * print c dfa: * print the dfa in c format. */ void print_c_dfa(FILE *of, const char *name, dfa_t *pdfa) { int i; if (sizeof(unsigned short) < 2) { fprintf(of, "#error shorts too short"); fprintf(stderr, "Error: shorts are expected to be at least 2 bytes long.\n"); exit(EXIT_FAILURE); } assert(dfa_minmax_delta(pdfa, -1, 1) > -32768); if (dfa_minmax_delta(pdfa, -1, 0) > 32768) { fprintf(of, "#error too many states"); fprintf(stderr, "Error: The dfa states are too disperse. Can't fit delta into a short.\n"); exit(EXIT_FAILURE); } if (pdfa->last_index + 1 > 65535) { fprintf(of, "#error too many states"); fprintf(stderr, "Error: Too many index entries. Can't fit delta into a short.\n"); exit(EXIT_FAILURE); } fprintf(of, "\n#include \"dfa-mkpat.h\"\n"); fprintf(of, "static const state_rt_t state_%s[%d] = {\n", name, pdfa->last_state + 1); for (i = 0; i != pdfa->last_state + 1; i++) { int j; fprintf(of, "{{"); for (j = 0; j < 4; j++) { int n = pdfa->states[i].next[j]; assert((n == 0) || (abs(n - i) < 32768)); fprintf(of, "%d", n ? n - i : 0); if (j != 3) fprintf(of, ","); } fprintf(of, "}, %d},%s", pdfa->states[i].att, ((i+1)%3 ? "\t" : "\n")); } fprintf(of, "};\n\n"); fprintf(of, "static const attrib_rt_t idx_%s[%d] = {\n", name, pdfa->last_index + 1); for (i = 0; i != pdfa->last_index + 1; i++) fprintf(of, "{%d,%d},%s", pdfa->indexes[i].val, pdfa->indexes[i].next, ((i+1)%4 ? "\t" : "\n")); fprintf(of, "};\n\n"); fprintf(of, "static dfa_rt_t dfa_%s = {\n", name); fprintf(of, " \"%s\",\n", name); fprintf(of, "state_%s,\n", name); fprintf(of, "idx_%s", name); fprintf(of, "};\n"); } /* * Create a linear dfa from a string and an attributes value * and resize the dfa if needed. * * For example: * create_dfa(pdfa, "Oo?.", 2001) * gives: * * 1 0,1 0,1,2 0 * (1,{}) -------> (2,{}) -------> (3,{}) -------> (4,{}) ------> (5,{2001}) * * An empty string force a junk pattern : The scanner will always * consider this pattern as active. * * The possible input symbols are : * * '.', ',', '*', '!' for EMPTY expected. * 'X' for BLACK expected. * 'O' for WHITE expected. * 'x' for BLACK|EMPTY expected. * 'o' for WHITE|EMPTY expected. * '#', '+', '-', '|' for OUT_BOARD expected. * '?' for EMPTY|BLACK|WHITE expected. * '$' for EMPTY|BLACK|WHITE|OUT_BOARD expected. */ static void create_dfa(dfa_t *pdfa, const char *str, int att_val) { int new_state; if (dfa_verbose > 1) fprintf(stderr, "linear dfa in %s with string: %s\n", pdfa->name, str); assert(str != NULL); assert(pdfa->max_states > 1); assert(pdfa->max_indexes > 1); clean_dfa(pdfa); new_state = 1; for (; *str != '\0' && strchr("$#+-|OoXx.?,!a*", *str); str++) { memset(pdfa->states[new_state].next, 0, 4 * sizeof(int)); if (strchr("$?.ox,a!*", *str)) pdfa->states[new_state].next[0] = new_state + 1; if (strchr("$?Oo", *str)) pdfa->states[new_state].next[1] = new_state + 1; if (strchr("$?Xx", *str)) pdfa->states[new_state].next[2] = new_state + 1; if (strchr("$#+-|", *str)) pdfa->states[new_state].next[OUT_BOARD] = new_state + 1; new_state++; if (new_state >= pdfa->max_states) resize_dfa(pdfa, pdfa->max_states + DFA_RESIZE_STEP, pdfa->max_indexes); } memset(pdfa->states[new_state].next, 0, 4 * sizeof(int)); pdfa->last_index++; if (pdfa->last_index >= pdfa->max_indexes) resize_dfa(pdfa, pdfa->max_states, pdfa->max_indexes + DFA_RESIZE_STEP); memset(&(pdfa->indexes[pdfa->last_index]), 0, sizeof(attrib_t)); pdfa->states[new_state].att = pdfa->last_index; pdfa->indexes[pdfa->states[new_state].att].val = att_val; pdfa->indexes[pdfa->states[new_state].att].next = 0; pdfa->last_state = new_state; } /************************** * Test array with a * * hash table * **************************/ /* used by sync_product * * to store visited states* **************************/ #define MAX_HASH_VALUE 4096 typedef struct entry { int l, r; /* key */ int val; /* value */ struct entry *pnext; /* NULL if end of list */ } entry_t; typedef struct test_array { entry_t *hash[MAX_HASH_VALUE]; } test_array_t; /* initialize empty lists */ static void new_test_array(test_array_t *pta) { int h; for (h = 0; h != MAX_HASH_VALUE ; h++) pta->hash[h] = NULL; } /* Searh for (l, r) in the linked list plist */ static int get_from_entry_list(entry_t *plist, int l, int r) { int val = 0; while (plist != NULL) { if (plist->l == l && plist->r == r) val = plist->val; plist = plist->pnext; } return val; } /* get the value associated with (l, r) or 0 if none */ static int get_from_test_array(test_array_t *pta, int l, int r) { return get_from_entry_list(pta->hash[(l+r) % MAX_HASH_VALUE], l, r); } /* insert a new entry at the beginning of the linked list pplist */ static void add_to_entry_list(entry_t **pplist, int l, int r, int val) { entry_t *new_entry; /* make sure val > 0: val = 0 is used in get_from_entry_list */ assert(val > 0); assert(!get_from_entry_list(*pplist, l, r)); new_entry = malloc(sizeof(*new_entry)); if (new_entry == NULL) { fprintf(stderr, "No memory left for new entry\n"); exit(EXIT_FAILURE); } new_entry->pnext = *pplist; new_entry->l = l; new_entry->r = r; new_entry->val = val; *pplist = new_entry; } /* add a value at (l, r) */ static void add_to_test_array(test_array_t *pta, int l, int r, int val) { add_to_entry_list(&(pta->hash[(l+r) % MAX_HASH_VALUE]), l, r, val); } /* free the elements of the linked list plist */ static void free_entry_list(entry_t *plist) { entry_t *pentry; while (plist != NULL) { pentry = plist; plist = plist->pnext; free(pentry); } } /* free allocated memory */ static void free_test_array(test_array_t *pta) { int h; for (h = 0; h != MAX_HASH_VALUE; h++) { free_entry_list(pta->hash[h]); pta->hash[h] = NULL; } } /* * Synchronization product between two automata. * * L(A) is the set of patterns recognized by the automaton A. * * A syncronized product betwenn two acyclic deterministic automata * A1 and A2 is an acyclic deterministic classifier A1xA2 that * recognize and classify the languages * L(A1), L(A2), L(A1 Union A2) and L(A1 Inter A2). * * This algorithm do the product and the reduction at the same time. * * See Hopcroft & Ullman "The design and analysis of computer algorithms" * Ed. Addison-Wesley, Reading MA, 1974 * For the theorical aspects. */ /* globals used to improve readability */ static dfa_t *gpout, *gpleft, *gpright; /* Hash table used to test if a state has already been visited and then give its position in the new automaton. */ static test_array_t gtest; static void do_sync_product(int l, int r) { int c; int nextl, nextr; int state; state = gpout->last_state; /* unify the attributes of states l and r */ gpout->states[state].att = union_att(gpout, gpleft, gpleft->states[l].att, gpright, gpright->states[r].att); /* scan each possible out-transition */ for (c = 0; c != 4; c++) { nextl = gpleft->states[l].next[c]; nextr = gpright->states[r].next[c]; assert(nextl < gpleft->last_state + 1); assert(nextr < gpright->last_state + 1); /* transition to (0,0) mean no transition at all */ if (nextl != 0 || nextr != 0) { /* if the out-state doesn't already exist */ if (get_from_test_array(>est, nextl, nextr) == 0) { /* create it! */ gpout->last_state++; if (gpout->last_state >= gpout->max_states) resize_dfa(gpout, gpout->max_states + DFA_RESIZE_STEP, gpout->max_indexes); add_to_test_array(>est, nextl, nextr, gpout->last_state); /* link it */ gpout->states[state].next[c] = gpout->last_state; /* create also its sub-automaton */ do_sync_product(nextl, nextr); } else { /* link it */ gpout->states[state].next[c] = get_from_test_array(>est, nextl, nextr); } } else { /* no output by c from the actual state */ gpout->states[state].next[c] = 0; } } } static void sync_product(dfa_t *pout, dfa_t *pleft, dfa_t *pright) { pout->last_index = 0; if (dfa_verbose > 2) { fprintf(stderr, "Product between %s and %s\n", pleft->name, pright->name); fprintf(stderr, "result in %s\n", pout->name); } gpout = pout; gpleft = pleft; gpright = pright; new_test_array(>est); add_to_test_array(>est, 1, 1, 1); pout->last_state = 1; do_sync_product(1, 1); free_test_array(>est); } /* * Init/end functions */ void dfa_init(void) { int j; if (dfa_verbose > 1) fprintf(stderr, "dfa: init\n"); dfa_was_initialized++; build_spiral_order(); dfa_prepare_rotation_data(); for (j = 0; j < DFA_BINS; j++) new_dfa(&(aux_dfa[j]), "binAux "); new_dfa(&aux_temp, "tempAux "); } void dfa_end(void) { int j; if (dfa_verbose > 1) fprintf(stderr, "dfa: end\n"); for (j = 0; j < DFA_BINS; j++) kill_dfa(&(aux_dfa[j])); kill_dfa(&aux_temp); dfa_was_initialized--; } /* * Returns max or min jump distance from state to next[next_index] for * all states. If next_index < 0, then max/min for all for states. */ int dfa_minmax_delta(dfa_t *pdfa, int next_index, int isMin) { int ret, i, j; assert(next_index <= 3); if (isMin) ret = 99999; else ret = -1; for (i = 0; i <= pdfa->last_state; i++) { for (j = 0; j < 4; j++) { if (j == next_index || next_index < 0) { int next = pdfa->states[i].next[j]; if (!next) continue; if (isMin) { if (ret > next - i) ret = next - i; } else { if (ret < next - i) ret = next - i; } } } } return ret; } #define DFA_ALIGN 2 /* * Re-orders DFA into a canonical form, which does a half-hearted * attempt to reduce the size of jumps for all states entries. */ void dfa_shuffle(dfa_t *pdfa) { struct state *old_states; int *state_to; int *state_from; int *queue1; int *queue2; int *tempq; int next_new_state; int q1p; int q2p; int i, j; state_to = calloc(pdfa->last_state+1, sizeof(*state_to)); state_from = calloc(pdfa->last_state+1, sizeof(*state_from)); queue1 = malloc((pdfa->last_state+1) * sizeof(*queue1)); queue2 = malloc((pdfa->last_state+1) * sizeof(*queue2)); q1p = 1; q2p = 0; queue1[0] = 1; /* i.e. start at state 1. */ state_from[0] = state_to[0] = 0; state_from[1] = state_to[1] = 1; next_new_state = 2; while (q1p) { for (i = 0; i < q1p; i++) { for (j = 0; j < 4; j++) { int n = pdfa->states[queue1[i]].next[j]; /* next_new_state = DFA_ALIGN * ((next_new_state-1) / DFA_ALIGN) + 1;*/ while (n && !state_to[n]) { state_to[n] = next_new_state; state_from[next_new_state] = n; next_new_state++; queue2[q2p++] = n; n = pdfa->states[n].next[0]; } } } tempq = queue1; queue1 = queue2; queue2 = tempq; q1p = q2p; q2p = 0; } old_states = malloc((pdfa->last_state+1) * sizeof(*old_states)); for (i = 1; i <= pdfa->last_state; i++) { for (j = 0; j < 4; j++) { old_states[i].next[j] = pdfa->states[i].next[j]; old_states[i].att = pdfa->states[i].att; } } for (i = 1; i <= pdfa->last_state; i++) { for (j = 0; j < 4; j++) { assert(state_to[i] > 0); pdfa->states[i].next[j] = state_to[old_states[state_from[i]].next[j]]; } pdfa->states[i].att = old_states[state_from[i]].att; } } /* Calculate the maximal number of patterns matched at one point for * one transformation. Multiplying this number by 8 gives an upper * bound for the total number of matched patterns for all * transformation. */ int dfa_calculate_max_matched_patterns(dfa_t *pdfa) { int total_max = 0; int *state_max = calloc(pdfa->last_state + 1, sizeof(int)); char *queued = calloc(pdfa->last_state + 1, sizeof(char)); int *queue = malloc(pdfa->last_state * sizeof(int)); int queue_start = 0; int queue_end = 1; queue[0] = 1; while (queue_start < queue_end) { int state = queue[queue_start++]; int k; /* Increment maximal number of matched patterns for each pattern * matched at current `state'. */ for (k = pdfa->states[state].att; k; k = pdfa->indexes[k].next) state_max[state]++; if (total_max < state_max[state]) total_max = state_max[state]; for (k = 0; k < 4; k++) { int next = pdfa->states[state].next[k]; if (next != 0) { if (!queued[next]) { queue[queue_end++] = next; queued[next] = 1; } if (state_max[next] < state_max[state]) state_max[next] = state_max[state]; } } } assert(queue_end == pdfa->last_state); free(state_max); free(queued); free(queue); return total_max; } /* * Merges cached dfas into the master DFA */ void dfa_finalize(dfa_t *pdfa) { int j; int next_bin = aux_count; int last_bin = aux_count + DFA_BINS - 1; while (next_bin + 1 != last_bin) { for (j = aux_count + 1; j <= last_bin; j += 2) { if (j+1 == next_bin) copy_dfa(&aux_dfa[next_bin % DFA_BINS], &aux_dfa[j % DFA_BINS]); else sync_product(&aux_dfa[next_bin % DFA_BINS], &aux_dfa[j % DFA_BINS], &aux_dfa[(j+1) % DFA_BINS]); next_bin++; } last_bin = next_bin - 1; aux_count--; next_bin = aux_count; } copy_dfa(pdfa, &aux_dfa[last_bin % DFA_BINS]); compactify_att(pdfa); } /* * Add a new string with attribute att_val into the dfa. * if the new size of the dfa respect some size conditions * return increase in kB or -1 if the pattern was rejected. * This function never rejects string of length <= 1. */ float dfa_add_string(dfa_t *pdfa, const char *str, int pattern_index, int ll) { dfa_t *new_dfa = &(aux_dfa[aux_count % DFA_BINS]); dfa_t *old_dfa = &(aux_dfa[(aux_count+1) % DFA_BINS]); float ratio; if (dfa_verbose > 1) { fprintf(stderr, "Adding to dfa %s the string: %s\n", pdfa->name, str); fprintf(stderr, " pat_ind: %d; rotation: %d at bin: %d\n", pattern_index, ll, aux_count); } assert(dfa_was_initialized > 0); assert(pdfa != NULL); create_dfa(&aux_temp, str, pattern_index); /* then we do the synchronization product with dfa */ sync_product(new_dfa, old_dfa, &aux_temp); aux_count++; ratio = 1; if (dfa_size(old_dfa) > 0) ratio = (float) (dfa_size(new_dfa) / dfa_size(old_dfa)); return ratio; } /* Used for quick string rotation. */ static int dfa_rotation_data[DFA_BASE * DFA_BASE]; static void dfa_prepare_rotation_data(void) { int k; for (k = 0; k < DFA_MAX_ORDER; k++) dfa_rotation_data[DFA_POS(0, 0) + spiral[k][0]] = k; } /* Create a transformation of `string' and store it in * `rotated_string'. The latter must be of at least DFA_MAX_ORDER * characters in length. */ void dfa_rotate_string(char *rotated_string, const char *string, int transformation) { if (transformation > 0) { int k; int length = strlen(string); int new_length = 0; memset(rotated_string, '$', DFA_MAX_ORDER); for (k = 0; k < length; k++) { if (string[k] != '$') { int string_position = dfa_rotation_data[DFA_POS(0, 0) + spiral[k][transformation]]; rotated_string[string_position] = string[k]; if (string_position + 1 > new_length) new_length = string_position + 1; } } rotated_string[new_length] = 0; } else strcpy(rotated_string, string); } /* * Build a pattern string from a pattern. `str' must refer a buffer * of size greater than DFA_MAX_ORDER. */ void pattern_2_string(struct pattern *pat, struct patval_b *elements, char *str, int ci, int cj) { char work_space[DFA_MAX_BOARD * 4][DFA_MAX_BOARD * 4]; int m, n; /* anchor position */ int edges, borders, to_test; int i, j, k; char c; m = DFA_MAX_BOARD * 2 + ci; n = DFA_MAX_BOARD * 2 + cj; /* position of the anchor */ assert(dfa_was_initialized); memset(str, 0, DFA_MAX_ORDER); memset(work_space, '#', sizeof(work_space)); if (dfa_verbose > 0) fprintf(stderr, "converting pattern into string.\n"); /* basic edge constraints */ for (i = DFA_MAX_BOARD; i != DFA_MAX_BOARD * 3; i++) for (j = DFA_MAX_BOARD; j != DFA_MAX_BOARD * 3; j++) work_space[i][j] = '$'; /* pattern mask */ for (i = pat->mini + m; i != pat->maxi + m + 1; i++) for (j = pat->minj + n; j != pat->maxj + n + 1; j++) work_space[i][j] = '?'; /* more advanced edge constraints */ /* South constraint */ if (pat->edge_constraints & SOUTH_EDGE) { for (i = m + pat->maxi + 1; i != DFA_MAX_BOARD * 3; i++) for (j = 0; j != DFA_MAX_BOARD * 3; j++) work_space[i][j] = '-'; } /* East constraint */ if (pat->edge_constraints & EAST_EDGE) { for (i = 0; i != DFA_MAX_BOARD * 3; i++) for (j = n + pat->maxj + 1; j != DFA_MAX_BOARD * 3; j++) work_space[i][j] = '|'; } /* North constraint */ if (pat->edge_constraints & NORTH_EDGE) { for (i = 0; i != m + pat->mini; i++) for (j = 0; j != DFA_MAX_BOARD * 4; j++) work_space[i][j] = '-'; } /* West constraint */ if (pat->edge_constraints & WEST_EDGE) { /* take care not to erase the south edge constraint */ for (i = 0; i != m + pat->maxi + 1; i++) for (j = 0; j != n + pat->minj; j++) work_space[i][j] = '|'; /* complete the last corner only if necessary */ if (!(pat->edge_constraints & SOUTH_EDGE)) { for (i = m + pat->maxi + 1; i != DFA_MAX_BOARD * 3; i++) for (j = 0; j != n + pat->minj; j++) work_space[i][j] = '|'; } } /* dump */ if (dfa_verbose > 4) { for (i = DFA_MAX_BOARD - 1; i != DFA_MAX_BOARD * 3 + 1; i++) { for (j = DFA_MAX_BOARD - 1; j != DFA_MAX_BOARD * 3 + 1; j++) { if (i == m && j == n) fprintf(stderr, "s"); /* mark the anchor */ else fprintf(stderr, "%c", work_space[i][j]); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } /* pattern representation on the work space */ for (k = 0; k != pat->patlen; k++) { c = EXPECTED_VAL(elements[k].att); assert(work_space[m + elements[k].x - ci][n + elements[k].y - cj] == '?'); work_space[m + elements[k].x - ci][n + elements[k].y - cj] = c; } /* dump */ if (dfa_verbose > 3) { for (i = DFA_MAX_BOARD - 1; i != DFA_MAX_BOARD * 3 + 1; i++) { for (j = DFA_MAX_BOARD - 1; j != DFA_MAX_BOARD * 3 + 1; j++) { if (i == m && j == n) fprintf(stderr, "s"); /* mark the anchor */ else fprintf(stderr, "%c", work_space[i][j]); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } /* Now we can build the smallest pattern string possible * from the anchor */ to_test = pat->patlen; /* How many positions left to test ? */ edges = pat->edge_constraints; /* how many constraint tested ? */ borders = 0xF; /* we must test at least one intersection by border for * patterns like * * ??? * O.O * ??? * * To ensure edge position. */ for (k = 0; (k != DFA_MAX_ORDER - 1) && ((borders > 0) || edges || to_test > 0); k++) { j = spiral[k][0] % DFA_BASE; if (j >= DFA_MAX_BOARD) j -= DFA_BASE; if (j <= -DFA_MAX_BOARD) j += DFA_BASE; i = (spiral[k][0] - j) / DFA_BASE; if (i == pat->maxi) borders &= ~SOUTH_EDGE; if (i == pat->mini) borders &= ~NORTH_EDGE; if (j == pat->maxj) borders &= ~EAST_EDGE; if (j == pat->minj) borders &= ~WEST_EDGE; assert(m + i < DFA_MAX_BOARD * 3 && m + i < DFA_MAX_BOARD * 3); str[k] = work_space[m + i][n + j]; assert(strchr("XOxo.,a!?$#|-+", str[k])); if (strchr("XOxo.,a!", str[k])) to_test--; if (strchr("#|-+", str[k])) { if (i > pat->maxi) edges &= ~SOUTH_EDGE; if (i < pat->mini) edges &= ~NORTH_EDGE; if (j > pat->maxj) edges &= ~EAST_EDGE; if (j < pat->minj) edges &= ~WEST_EDGE; } } assert(k < DFA_MAX_ORDER); str[k] = '\0'; /* end of string */ if (0 && dfa_verbose > 0) fprintf(stderr, "converted pattern %s into string: %s\n", pat->name, str); } /************************************** * Experimental DFA builder * **************************************/ /* This builder differs from the one above in that it builds the whole dfa * at once. That is, it must have all the patterns to build and cannot add * pattern by pattern. Currently, it is only used in DFA size optimization * (it seems to be significantly faster). */ /* Allocate a new dfa_attrib structure from a dynamic array. */ static dfa_attrib * dfa_attrib_new(dfa_attrib_array *array, int string_index) { dfa_attrib *attribute; if (array->allocated == DFA_ATTRIB_BLOCK_SIZE) { dfa_attrib_block *new_block = malloc(sizeof(*new_block)); assert(new_block); new_block->previous = array->last_block; array->last_block = new_block; array->allocated = 0; } attribute = &(array->last_block->attrib[array->allocated++]); attribute->next = NULL; attribute->string_index = string_index; return attribute; } /* Initialize dfa_attrib_array structure. */ static void dfa_attrib_array_reset(dfa_attrib_array *array) { array->last_block = NULL; array->allocated = DFA_ATTRIB_BLOCK_SIZE; } /* Clear a dynamic array by freeing all blocks befor `cutoff_point'. */ static void dfa_attrib_array_partially_clear(dfa_attrib_block *cutoff_point) { if (cutoff_point) { dfa_attrib_block *block = cutoff_point->previous; while (block) { dfa_attrib_block *previous = block->previous; free(block); block = previous; } cutoff_point->previous = NULL; } } /* Clear a dynamic array completely. All blocks are freed. */ static void dfa_attrib_array_clear(dfa_attrib_array *array) { if (array->last_block) { dfa_attrib_array_partially_clear(array->last_block); free(array->last_block); array->last_block = NULL; } array->allocated = DFA_ATTRIB_BLOCK_SIZE; } /* Allocate a new dfa_node structure in a DFA graph. */ static dfa_node * dfa_node_new(dfa_graph *graph) { dfa_node *node; if (graph->allocated == DFA_NODE_BLOCK_SIZE) { dfa_node_block *new_block = malloc(sizeof(*new_block)); assert(new_block); new_block->previous = graph->last_block; graph->last_block = new_block; graph->allocated = 0; } graph->num_nodes++; node = &(graph->last_block->node[graph->allocated++]); memset(node, 0, sizeof(dfa_node)); return node; } /* This is a hash table used to quickly find a DFA node using a linked list * of its attributes as a key. */ static dfa_hash_entry *dfa_hash_table[DFA_HASH_TABLE_SIZE]; static dfa_hash_block *dfa_hash_last_block = NULL; static int dfa_hash_allocated; /* Allocate a dfa_entry structure dynamically. */ static dfa_hash_entry * dfa_hash_entry_new(void) { if (dfa_hash_allocated == DFA_HASH_BLOCK_SIZE) { dfa_hash_block *new_block = malloc(sizeof(*new_block)); assert(new_block); new_block->previous = dfa_hash_last_block; dfa_hash_last_block = new_block; dfa_hash_allocated = 0; } return &(dfa_hash_last_block->entry[dfa_hash_allocated++]); } /* Clear the hash table completely. Used after having finished a graph level. */ static void dfa_hash_clear(void) { memset(dfa_hash_table, 0, DFA_HASH_TABLE_SIZE * sizeof(dfa_hash_entry *)); if (dfa_hash_last_block) { dfa_hash_block *block = dfa_hash_last_block->previous; while (block) { dfa_hash_block *previous = block->previous; free(block); block = previous; } dfa_hash_last_block->previous = NULL; dfa_hash_allocated = 0; } else dfa_hash_allocated = DFA_HASH_BLOCK_SIZE; } /* Compute the hash value of a key (linked list of attributes). */ static int dfa_hash_value(dfa_attrib *key) { int hash_value = DFA_HASH_VALUE_1 * key->string_index; if (key->next) { hash_value += DFA_HASH_VALUE_2 * key->next->string_index; if (key->next->next) hash_value += DFA_HASH_VALUE_3 * key->next->next->string_index; } return hash_value % DFA_HASH_TABLE_SIZE; } /* Search for a node with a given key in the hash table. */ static dfa_node * dfa_hash_search(dfa_attrib *key) { int hash_value = dfa_hash_value(key); dfa_hash_entry *entry; for (entry = dfa_hash_table[hash_value]; entry; entry = entry->next) { dfa_attrib *left = key; dfa_attrib *right = entry->key; while (left && right) { if (left->string_index != right->string_index) break; left = left->next; right = right->next; } if (!left && !right) return entry->value; } return NULL; } /* Add a node to the table. The list of strings which pass through it is used * as a key. */ static void dfa_hash_add_node(dfa_node *node) { int hash_value = dfa_hash_value(node->passing_strings); dfa_hash_entry *entry; entry = dfa_hash_entry_new(); entry->next = dfa_hash_table[hash_value]; dfa_hash_table[hash_value] = entry; entry->key = node->passing_strings; entry->value = node; } /* DFA iterator. Used to walk the array of nodes in backward direction. */ static dfa_node_block *dfa_iterator_block; static int dfa_iterator_node_num; /* Reset the iterator. The last added node of the specified graph becomes * the current node. */ static dfa_node* dfa_iterator_reset(dfa_graph *graph) { assert(graph->last_block); if (graph->allocated > 0) { dfa_iterator_block = graph->last_block; dfa_iterator_node_num = graph->allocated - 1; } else { dfa_iterator_block = graph->last_block->previous; assert(dfa_iterator_block); dfa_iterator_node_num = DFA_NODE_BLOCK_SIZE - 1; } return &(dfa_iterator_block->node[dfa_iterator_node_num]); } /* Shift the current node pointer one node backwards. */ static dfa_node* dfa_iterate(void) { dfa_iterator_node_num--; if (dfa_iterator_node_num < 0) { dfa_iterator_block = dfa_iterator_block->previous; assert(dfa_iterator_block); dfa_iterator_node_num = DFA_NODE_BLOCK_SIZE - 1; } return &(dfa_iterator_block->node[dfa_iterator_node_num]); } /* Initialize a dfa_graph structure. */ void dfa_graph_reset(dfa_graph *graph) { graph->num_nodes = 0; graph->root = NULL; graph->last_block = NULL; graph->allocated = DFA_NODE_BLOCK_SIZE; dfa_attrib_array_reset(&(graph->attributes)); } /* Free all resources associated with the specified DFA graph. */ static void dfa_graph_clear(dfa_graph *graph) { dfa_node_block *block = graph->last_block; graph->num_nodes = 0; graph->root = NULL; while (block) { dfa_node_block *previous = block->previous; free(block); block = previous; } graph->last_block = NULL; graph->allocated = DFA_NODE_BLOCK_SIZE; dfa_attrib_array_clear(&(graph->attributes)); } /* dfa_graph_build_level() builds a level of a graph. Level `n' is a set of * nodes which correspond to n's element of a string. When matching using a * dfa, nodes of level `n' are only checked at (n + 1)'s iteration (root node * is considered to be level -1, but is matched at iteration 0). */ static void dfa_graph_build_level(dfa_graph *graph, char **strings, int level, dfa_node *terminal_node, dfa_attrib_array *passing_strings_array) { int save_num_nodes = graph->num_nodes; dfa_attrib_block *cutoff_point; dfa_node *node; dfa_node *this_terminal_node = dfa_iterator_reset(graph); cutoff_point = passing_strings_array->last_block; dfa_hash_clear(); /* Walk through all nodes of the previous level (backwards, but that doesn't * matter - it's just because iterator works that way). */ for (node = this_terminal_node; node != terminal_node; node = dfa_iterate()) { int k; int num_masks = 0; char mask[4]; dfa_attrib *passing_string; dfa_attrib **link = &(node->attributes); dfa_attrib *new_passing_strings[4]; dfa_attrib **new_link[4]; /* Calculate all different masks for subnodes. For instance, if there are * three strings passing through a node of level 1, say "X$...", "Xx..." * and "XO...", there will be three masks: 8 (stands for '#'), 5 ('X' and * '.') and 2 ('O'). String "X$..." will pass further through all three * subnodes, "Xx..." - through subnode corresponding to mask 5 and string * "XO..." - through subnode corresponding to mask 2. */ for (passing_string = node->passing_strings; passing_string && num_masks < 4; passing_string = passing_string->next) { int index = passing_string->string_index; char string_mask = strings[index][level]; if (string_mask) { int limit = num_masks; for (k = 0; k < limit; k++) { char common_branches = string_mask & mask[k]; if (common_branches && common_branches != mask[k]) { /* Split a mask, since the string passes through a "part" of it. */ mask[k] ^= common_branches; mask[num_masks++] = common_branches; } string_mask ^= common_branches; } if (string_mask) { /* If there is no mask corresponding to a (part) of the string's * mask, add it now. */ mask[num_masks++] = string_mask; } } else { /* If the string ends at this level, add its index to the list of * matched strings of the current node. Not used at the moment, * since this builder isn't used for actual DFA building. */ *link = dfa_attrib_new(&(graph->attributes), index); link = &((*link)->next); } } for (k = 0; k < num_masks; k++) new_link[k] = &(new_passing_strings[k]); /* Now, for each mask, create a list of all strings which will follow it * (pass through a node corresponding to it). It is possible to merge this * loop with the previous one, but it is simplier to keep them separated. */ for (passing_string = node->passing_strings; passing_string; passing_string = passing_string->next) { int index = passing_string->string_index; for (k = 0; k < num_masks; k++) { if (strings[index][level] & mask[k]) { *(new_link[k]) = dfa_attrib_new(passing_strings_array, index); new_link[k] = &((*(new_link[k]))->next); } } } /* Finally, create new nodes for the masks when necessary. */ for (k = 0; k < num_masks; k++) { int i; /* Maybe we have already added such a node? */ dfa_node *new_node = dfa_hash_search(new_passing_strings[k]); if (!new_node) { /* If not, create it, save the list of passing strings and add the * new node to hash table. */ new_node = dfa_node_new(graph); new_node->passing_strings = new_passing_strings[k]; dfa_hash_add_node(new_node); } /* At this moment we convert the masks to actual transitions. These are * also unused till we use this builder for actual DFA creation. */ for (i = 0; i < 4; i++) { if (mask[k] & (1 << i)) node->branch[i] = new_node; } } } /* Free the lists of passing strings for the previous level. Useful if we * building an exceptionally huge DFA. */ dfa_attrib_array_partially_clear(cutoff_point); if (graph->num_nodes != save_num_nodes) { /* If we have added any nodes, this level is not the last one. */ dfa_graph_build_level(graph, strings, level + 1, this_terminal_node, passing_strings_array); } } /* Convert a pattern to a string of masks. */ static char * dfa_prepare_string(const char *string) { int k; int l = strlen(string); char *dfa_string = malloc(l + 1); assert(dfa_string); for (k = 0; k < l; k++) { switch (string[k]) { case '$': dfa_string[k] = 15; break; /* 1111 */ case '-': case '|': case '+': case '#': dfa_string[k] = 8; break; /* 1000 */ case '.': case ',': case '!': case 'a': dfa_string[k] = 1; break; /* 0001 */ case '?': dfa_string[k] = 7; break; /* 0111 */ case 'O': dfa_string[k] = 2; break; /* 0010 */ case 'X': dfa_string[k] = 4; break; /* 0100 */ case 'o': dfa_string[k] = 3; break; /* 0011 */ case 'x': dfa_string[k] = 5; break; /* 0101 */ default: assert(0); /* Shouldn't happen. */ } } dfa_string[l] = 0; return dfa_string; } /* Initialize a dfa_patterns structure. */ void dfa_patterns_reset(dfa_patterns *patterns) { patterns->num_patterns = 0; patterns->patterns = NULL; patterns->last_pattern = NULL; dfa_graph_reset(&(patterns->graph)); } /* Clear the graph and reset all fields of a dfa_patterns structure. */ void dfa_patterns_clear(dfa_patterns *patterns) { dfa_pattern *pattern = patterns->patterns; while (pattern) { int k; dfa_pattern *next = pattern->next; for (k = 0; k < pattern->num_variations; k++) free(pattern->variation[k]); free(pattern); pattern = next; } patterns->num_patterns = 0; patterns->patterns = NULL; patterns->last_pattern = NULL; dfa_graph_clear(&(patterns->graph)); } /* Add a pattern to a list. If `index' is equal to the index of the last * added pattern, add a variation to that pattern instead. */ void dfa_patterns_add_pattern(dfa_patterns *patterns, const char *string, int index) { dfa_pattern *pattern = NULL; if (index == patterns->num_patterns - 1) { assert(patterns->last_pattern); assert(patterns->last_pattern->num_variations < 8); pattern = patterns->last_pattern; } else { assert(patterns->num_patterns <= index); while (patterns->num_patterns <= index) { patterns->num_patterns++; pattern = malloc(sizeof(*pattern)); pattern->num_variations = 0; if (patterns->last_pattern) patterns->last_pattern->next = pattern; else patterns->patterns = pattern; patterns->last_pattern = pattern; } pattern->current_variation = 0; pattern->next = NULL; } pattern->variation[pattern->num_variations++] = dfa_prepare_string(string); } /* Set the variation of the last pattern. Can be used in actual DFA building * or to set a hint (results of the previous optimization) for optimization. */ void dfa_patterns_set_last_pattern_variation(dfa_patterns *patterns, int variation) { assert(patterns->last_pattern); assert(patterns->last_pattern->num_variations > variation); patterns->last_pattern->current_variation = variation; } /* Make the shortest variation of the last pattern its current variation. It * is used as a starting point in DFA optimization process. */ void dfa_patterns_select_shortest_variation(dfa_patterns *patterns) { int k; int min_length; dfa_pattern *pattern = patterns->last_pattern; assert(pattern); pattern->current_variation = 0; min_length = strlen(pattern->variation[0]); for (k = 1; k < pattern->num_variations; k++) { int length = strlen(pattern->variation[k]); if (length < min_length) { pattern->current_variation = k; min_length = length; } } } /* Build a DFA graph for a list of patterns. */ void dfa_patterns_build_graph(dfa_patterns *patterns) { int k = 0; char **strings; dfa_attrib_array passing_strings_array; dfa_attrib **link; dfa_node *error_state; dfa_graph *graph = &(patterns->graph); dfa_pattern *pattern; strings = malloc(patterns->num_patterns * sizeof(*strings)); assert(strings); dfa_graph_clear(graph); dfa_attrib_array_reset(&passing_strings_array); /* Error state node is used as a terminator for level -1 (root node). */ error_state = dfa_node_new(graph); graph->root = dfa_node_new(graph); /* Add all strings as passing through root node (level -1). */ link = &(graph->root->passing_strings); for (pattern = patterns->patterns; pattern; pattern = pattern->next, k++) { if (pattern->num_variations > 0) { assert(pattern->current_variation < pattern->num_variations); strings[k] = pattern->variation[pattern->current_variation]; *link = dfa_attrib_new(&passing_strings_array, k); link = &((*link)->next); } else strings[k] = NULL; } dfa_graph_build_level(graph, strings, 0, error_state, &passing_strings_array); free(strings); dfa_attrib_array_clear(&passing_strings_array); } /* dfa_patterns_optimize_variations() tries to reduce the size of DFA by * altering pattern variations (in fact, transformations). The algorithm * is to change several patterns' variations and if this happens to give * size reduction, to keep the change, otherwise, revert. * * This function contains many heuristically chosen values for variation * changing probability etc. They have been chosen by observing algorithm * effectiveness and seem to be very good. * * Note that we subtract 1 from the number of nodes to be consistent with * the standard builder, which doesn't count error state. */ int * dfa_patterns_optimize_variations(dfa_patterns *patterns, int iterations) { int k = 0; int failed_iterations = 0; int min_nodes_so_far; int num_nodes_original; int *best_variations; double lower_limit = 2.0 / patterns->num_patterns; double upper_limit = 6.0 / patterns->num_patterns; double change_probability = 4.0 / patterns->num_patterns; dfa_pattern *pattern; best_variations = malloc(patterns->num_patterns * sizeof(*best_variations)); assert(best_variations); for (pattern = patterns->patterns; pattern; pattern = pattern->next, k++) best_variations[k] = pattern->current_variation; dfa_patterns_build_graph(patterns); num_nodes_original = patterns->graph.num_nodes; min_nodes_so_far = num_nodes_original; fprintf(stderr, "Original number of DFA states: %d\n", min_nodes_so_far - 1); fprintf(stderr, "Trying to optimize in %d iterations\n", iterations); gg_srand(num_nodes_original + patterns->num_patterns); while (iterations--) { int changed_variations = 0; int k = 0; /* Randomly change some variations. */ for (pattern = patterns->patterns; pattern; pattern = pattern->next, k++) { if (gg_drand() < change_probability && pattern->num_variations > 1) { int new_variation = gg_rand() % (pattern->num_variations - 1); if (new_variation >= pattern->current_variation) new_variation++; pattern->current_variation = new_variation; changed_variations++; } else pattern->current_variation = best_variations[k]; } if (changed_variations == 0) { iterations++; continue; } fprintf(stderr, "."); dfa_patterns_build_graph(patterns); if (patterns->graph.num_nodes < min_nodes_so_far) { /* If the new set of variations produces smaller dfa, save it. */ int k = 0; for (pattern = patterns->patterns; pattern; pattern = pattern->next, k++) best_variations[k] = pattern->current_variation; fprintf(stderr, "\nOptimized: %d => %d states (%d iterations left)\n", min_nodes_so_far - 1, patterns->graph.num_nodes - 1, iterations); min_nodes_so_far = patterns->graph.num_nodes; failed_iterations = 0; } else failed_iterations++; if (failed_iterations >= 30) { /* If haven't succeded in 30 last iterations, try to alter variation * change probability. */ double delta = gg_drand() / patterns->num_patterns; if (change_probability > upper_limit || (change_probability >= lower_limit && gg_rand() % 2 == 0)) delta = -delta; change_probability += delta; failed_iterations = 0; } } fprintf(stderr, "\nTotal optimization result: %d => %d states\n", num_nodes_original - 1, min_nodes_so_far - 1); dfa_graph_clear(&(patterns->graph)); return best_variations; } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */