#include #include #include #include #include /** * Stuff to replace GLib */ typedef int gboolean; #define FALSE 0 #define TRUE (!FALSE) static int memct = 0; #define g_new0(thing, count) calloc(count, sizeof(thing)), memct += count * sizeof(thing) #define g_malloc(x) malloc(x), memct += x #undef malloc #define g_free(x) memct -= sizeof(*x), free(x) #define g_free_n(x, n) memct -= n, free(x) #undef free #undef strdup static inline char *g_strdup(char *x) { char *ret; ret = g_malloc(strlen(x) + 1); strcpy(ret, x); return ret; } #define g_print printf #define g_error(x...) fprintf(stderr, ##x) static inline void g_strdown(char *word) { int i = 0; while (word[i]) { word[i] = tolower(word[i]); i++; } } static inline void g_strchomp(char *word) { while (strlen(word) && isspace(word[strlen(word) - 1])) word[strlen(word) - 1] = 0; } /** * My generic list stuff, renamed to replace glib */ typedef struct _list GSList; struct _list { struct _list *next; void *data; }; static GSList *g_slist_new(void *data) { GSList *l; l = g_new0(GSList, 1); l->next = NULL; l->data = data; return l; } static GSList *g_slist_append(GSList *l, void *data) { GSList *s = l; if (!s) return g_slist_new(data); while (s->next) s = s->next; s->next = g_slist_new(data); return l; } static GSList *g_slist_remove(GSList *l, void *data) { GSList *s = l, *p = NULL; if (!s) return NULL; if (s->data == data) { p = s->next; g_free(s); return p; } while (s->next) { p = s; s = s->next; if (s->data == data) { p->next = s->next; g_free(s); return l; } } return l; } static GSList *g_slist_copy(GSList *l) { GSList *n = NULL, *t = l; while (t) { n = g_slist_append(n, t->data); t = t->next; } return n; } static GSList *g_slist_concat(GSList *a, GSList *b) { GSList *end; if (!a) return b; if (!b) return a; end = a; while (end->next) end = end->next; end->next = b; return a; } static void g_slist_free(GSList *l) { while (l) { GSList *tmp = l; l = l->next; g_free(tmp); } } /** * Stuff for dictionary */ typedef struct _DTNode DTNode; struct _DTNode { DTNode *children[27]; GSList *words; }; static DTNode *dictroot = NULL; static GSList *wordlist = NULL; static int valid_word(char *word) { int i; for (i = 0; i < strlen(word); i++) if (word[i] < 'a' || word[i] > 'z') return 0; return 1; } static int nodect = 0; static void insert(char *word) { int i, j; DTNode *node; for (i = 0; i < strlen(word); i++) { node = dictroot; for (j = 0; j < strlen(word); j++) { if (j == i) { /* we're adding it to "DOT" */ if (!node->children[26]) { node->children[26] = g_new0(DTNode, 1); nodect++; } node = node->children[26]; } else { if (!node->children[word[j] - 'a']) { node->children[word[j] - 'a'] = g_new0(DTNode, 1); nodect++; } node = node->children[word[j] - 'a']; } } node->words = g_slist_append(node->words, word); } wordlist = g_slist_append(wordlist, word); } static void init_dict(char *dictname, int len) { FILE *dict; char *word; int ct = 0; dictroot = g_new0(DTNode, 1); nodect++; dict = fopen(dictname, "r"); if (!dict) { g_error("Unable to open dictionary file!"); exit(1); } word = g_malloc(1024); while (fgets(word, 1024, dict)) { g_strchomp(word); if (strlen(word) != len) continue; g_strdown(word); if (!valid_word(word)) continue; insert(g_strdup(word)); ct++; } g_free_n(word, 1024); fclose(dict); } static GSList *next_words(char *word) { GSList *ret = NULL, *tmp; int i, j; for (i = 0; i < strlen(word); i++) { DTNode *node = dictroot; for (j = 0; j < strlen(word); j++) { if (j == i) node = node->children[26]; else node = node->children[word[j] - 'a']; if (!node) break; } if (!node) continue; tmp = g_slist_copy(node->words); ret = g_slist_concat(ret, tmp); } return ret; } static void free_dict(DTNode *node) { int i; if (!node) return; nodect--; for (i = 0; i < 27; i++) free_dict(node->children[i]); g_slist_free(node->words); g_free(node); if (node == dictroot) { int ct = 0; while (wordlist) { char *tmp = wordlist->data; wordlist = g_slist_remove(wordlist, tmp); g_free_n(tmp, strlen(tmp) + 1); ct++; } } } /** * Stuff for marking used words */ typedef struct _UWNode UWNode; struct _UWNode { UWNode *node[26]; }; static void make_used(UWNode *uw, char *word) { int i; for (i = 0; i < strlen(word); i++) { if (!uw->node[word[i] - 'a']) uw->node[word[i] - 'a'] = g_new0(UWNode, 1); uw = uw->node[word[i] - 'a']; } } static gboolean is_used(UWNode *uw, char *word) { int i; for (i = 0; i < strlen(word); i++) { if (!uw->node[word[i] - 'a']) return FALSE; uw = uw->node[word[i] - 'a']; } return TRUE; } static void free_used(UWNode *node) { int i; if (!node) return; for (i = 0; i < 26; i++) free_used(node->node[i]); g_free(node); } /** * Stuff to actually go through the transitions */ typedef struct _TRNode TRNode; struct _TRNode { TRNode *parent; int children; char *word; }; static void print_end(TRNode *node) { char *str = g_strdup(node->word); node = node->parent; while (node) { int sz = strlen(node->word) + 4 + strlen(str) + 1; char *tmp; tmp = g_malloc(sz); sprintf(tmp, "%s -> %s", node->word, str); g_free_n(str, strlen(str) + 1); str = tmp; node = node->parent; } g_print("%s\n", str); g_free_n(str, strlen(str) + 1); } static int free_tran(TRNode *node) { int ct = 0; TRNode *parent = node->parent; g_free(node); ct++; if (parent) parent->children--; while (parent && !parent->children) { node = parent; parent = node->parent; g_free(node); ct++; if (parent) parent->children--; } return ct; } static void transition(char *start, char *end) { GSList *queue = NULL; gboolean longest = (end == NULL); TRNode *node; UWNode *usedroot; usedroot = g_new0(UWNode, 1); make_used(usedroot, start); node = g_new0(TRNode, 1); node->word = start; queue = g_slist_append(queue, node); while (queue) { GSList *words; gboolean added = FALSE; node = queue->data; queue = g_slist_remove(queue, node); words = next_words(node->word); if (longest) end = node->word; while (words) { if (!longest && !strcmp(words->data, end)) { TRNode *newnode; newnode = g_new0(TRNode, 1); newnode->parent = node; newnode->word = words->data; node->children++; queue = g_slist_append(queue, newnode); print_end(newnode); free_used(usedroot); g_slist_free(words); while (queue) { node = queue->data; queue = g_slist_remove(queue, node); free_tran(node); } return; } if (!is_used(usedroot, words->data)) { TRNode *newnode; newnode = g_new0(TRNode, 1); make_used(usedroot, words->data); newnode->parent = node; newnode->word = words->data; node->children++; queue = g_slist_append(queue, newnode); added = TRUE; } words = g_slist_remove(words, words->data); } if (!added && (!longest || queue)) { free_tran(node); } } if (longest) { g_print("%s: %d\n", end, free_tran(node)); } else { g_print("No possible transition\n"); } free_used(usedroot); } /** * main! */ int main(int argc, char **argv) { if (argc == 2) { g_strdown(argv[1]); if (!valid_word(argv[1])) { g_error("%s not a valid word (must be a-z)\n", argv[1]); return 1; } /* g_print("initial memct: %d\n", memct); */ init_dict("/usr/share/dict/words", strlen(argv[1])); /* g_print("memct after creating dict: %d\n", memct); */ transition(argv[1], NULL); /* g_print("memct after transition: %d\n", memct); */ free_dict(dictroot); /* g_print("final memct: %d\n", memct); */ return 0; } if ((argc != 3) || (strlen(argv[1]) != strlen(argv[2]))) return 1; g_strdown(argv[1]); if (!valid_word(argv[1])) { g_error("%s not a valid word (must be a-z)\n", argv[1]); return 1; } g_strdown(argv[2]); if (!valid_word(argv[2])) { g_error("%s not a valid word (must be a-z)\n", argv[2]); return 1; } /* g_print("initial memct: %d\n", memct); */ init_dict("/usr/share/dict/words", strlen(argv[1])); /* g_print("memct after creating dict: %d\n", memct); */ transition(argv[1], argv[2]); /* g_print("memct after transition: %d\n", memct); */ free_dict(dictroot); /* g_print("final memct: %d\n", memct); */ return 0; }