#include <assert.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
#define LINK_GRAMMAR_DLL_EXPORT 0
#endif /* _MSC_VER */
#include <dict-api.h>
#include <dict-defines.h>
#include "generator-utilities.h"
/**
* Patch a subscript to SUBSCRIPT_DOT, or remove it iff
* \p leave_subscript is false.
*/
static const char *cond_subscript(const char *ow, bool leave_subscript)
{
const char *sm = strchr(ow, SUBSCRIPT_MARK);
if (sm == NULL) return ow;
static char w[MAX_WORD + 1]; // XXX FIXME this is not thread-safe!
strcpy(w, ow);
w[sm - ow] = leave_subscript ? SUBSCRIPT_DOT : '\0';
return w;
}
static void print_sent(size_t nwords, const char** words,
bool subscript)
{
for(WordIdx w = 0; w < nwords; w++)
{
assert(NULL != words[w] /* Failed to select word! */);
printf("%s", cond_subscript(words[w], subscript));
if (w < nwords-1) printf(" ");
}
printf("\n");
}
/**
* Recursive sentence odometer.
*
* Loops over all possible word-selections for each disjunct in the
* linkage. Calls the callback for each possible word-selection has
* been made.
*/
static void sent_odom(const Category* catlist,
size_t nwords, const char** words,
const Category_cost** cclist,
unsigned int* cclen,
const char** selected_words,
void (*callback)(void*),
void* data,
WordIdx cur_word)
{
/* We've reached the end of the sentence; we are done. */
if (cur_word >= nwords)
{
callback(data);
return;
}
/* If there aren't any categories, there is only one word-choice. */
const Category_cost *cc = cclist[cur_word];
if (cc == NULL)
{
selected_words[cur_word] = words[cur_word];
sent_odom(catlist, nwords, words,
cclist, cclen, selected_words, callback, data, cur_word+1);
return;
}
/* Loop over all categories at the current odometer position. */
for (unsigned int catidx = 0; catidx < cclen[cur_word]; catidx++)
{
/* Subtract 1 because there isn't any "category zero" */
unsigned int catno = cc[catidx].num - 1;
unsigned int num_words = catlist[catno].num_words;
/* Loop over all words for the selected category */
for (unsigned int widx = 0; widx < num_words; widx++)
{
selected_words[cur_word] = catlist[catno].word[widx];
sent_odom(catlist, nwords, words,
cclist, cclen, selected_words, callback, data, cur_word+1);
}
}
}
static void count_choices(void* data)
{
size_t* count = data;
(*count) ++;
}
typedef struct {
size_t nwords;
const char** selected_words;
bool subscript;
double chance;
size_t nprinted;
} sent_data;
static void print_word_choices(void* data)
{
sent_data* sd = data;
/* If chance is greater than one, always print. */
/* Otherwise, expect chance to be between zero and one */
bool prt = (1.0 <= sd->chance) ||
(((double) rand()) / ((double) RAND_MAX) < sd->chance);
if (prt)
{
print_sent(sd->nwords, sd->selected_words, sd->subscript);
sd->nprinted ++;
}
}
static size_t print_several(const Category* catlist,
Linkage linkage, size_t nwords, const char** words,
bool subscript, double fraction)
{
const Category_cost* cclist[MAX_SENTENCE];
unsigned int cclen[MAX_SENTENCE];
const char* selected_words[MAX_SENTENCE];
for(WordIdx w = 0; w < nwords; w++)
{
const Category_cost* cc = linkage_get_categories(linkage, w);
cclist[w] = cc;
unsigned int dj_num_cats = 0;
cclen[w] = 1;
selected_words[w] = NULL;
if (NULL != cclist[w])
{
/* Count the number of categories for this disjunct */
while (cc[dj_num_cats].num != 0) dj_num_cats++;
assert(dj_num_cats != 0 /* Bad disjunct! */);
cclen[w] = dj_num_cats;
}
}
/* First, count how many word choices there might be */
size_t num_word_choices = 0;
sent_odom(catlist, nwords, words,
cclist, cclen, selected_words,
count_choices, &num_word_choices, 0);
double chance = fraction / ((double) num_word_choices);
printf("# num possible word choices for linkage = %zu chance to print=%f\n",
num_word_choices, chance);
/* Now, print those choices */
sent_data sd;
sd.nwords = nwords;
sd.selected_words = selected_words;
sd.subscript = subscript;
sd.chance = chance;
sd.nprinted = 0;
/* Print at least one sentence! Keep trying till we get one. */
while (0 == sd.nprinted)
{
sent_odom(catlist, nwords, words,
cclist, cclen, selected_words,
print_word_choices, &sd, 0);
}
return sd.nprinted;
}
static const char *select_random_word(const Category *catlist,
const Category_cost *cc,
WordIdx w)
{
/* Count the number of categories for this disjunct */
unsigned int dj_num_cats = 0;
while (cc[dj_num_cats].num != 0)
dj_num_cats++;
assert(dj_num_cats != 0 /* Bad disjunct! */);
/* Select a category on this disjunct. */
unsigned int r = (unsigned int)rand();
unsigned int catidx = r % dj_num_cats;
/* Subtract 1 because Category 0 is undefined. */
unsigned int catnum = cc[catidx].num - 1;
if (verbosity_level >= 5)
{
printf("Word %zu: r=%08x category %u/%u \"%u\";", w, r,
catidx, dj_num_cats, catnum);
}
unsigned int num_words = catlist[catnum].num_words;
/* Select a dictionary word from the selected disjunct category. */
r = (unsigned int)rand();
unsigned int dict_word_idx = r % num_words;
const char *word = catlist[catnum].word[dict_word_idx];
if (verbosity_level >= 5)
{
printf(" r=%08x word %u/%u \"%s\"\n",
r, dict_word_idx, num_words, word);
}
return word;
}
/**
* Print one or more sample sentences per linkage.
* Return the number of sentences actually printed.
*
* `max_samples` should be the average number of sentences to be
* printed. If it is larger than the total number of possible
* word-choices for this linkage, then one sentence will be printed
* for each possible word-choice. If it is smaller than the total,
* then word-choices will be drawn at random, until, on average,
* this many word-choices have been sampled.
*
* If `max_samples` is less than one, then exactly one sentence will
* be printed.
*/
size_t print_sentences(const Category* catlist,
Linkage linkage, size_t nwords, const char** words,
bool subscript, double max_samples)
{
if (1.0 < max_samples)
{
return print_several(catlist, linkage, nwords, words,
subscript, max_samples);
}
for(WordIdx w = 0; w < nwords; w++)
{
const Category_cost* cc = linkage_get_categories(linkage, w);
if (cc == NULL)
{
printf("%s", cond_subscript(words[w], subscript));
}
else
{
const char *word = select_random_word(catlist, cc, w);
printf("%s", cond_subscript(word, subscript));
}
if (w < nwords-1) printf(" ");
}
printf("\n");
return 1;
}
/* ------------------------------------------------------------ */
void dump_categories(Dictionary dict, const Category *catlist)
{
#define TAB " "
const int maxcol = 60; /* Account for initial formatting tabs. */
printf(TAB"\"categories\": [\n");
for (unsigned int n = 0; catlist[n].num_words != 0; n++)
{
printf(TAB"{\n");
if (catlist[n].name[0] != '\0')
printf(TAB TAB"\"name\": %s,\n", catlist[n].name);
printf(TAB TAB"\"category_num\": %u,\n", n + 1);
printf(TAB TAB"\"exp\": %s,\n", lg_exp_stringify(catlist[n].exp));
printf(TAB TAB"\"num_words\": %u,\n", catlist[n].num_words);
printf(TAB TAB"\"words\": [\n");
printf(TAB TAB TAB);
int currcol = 0;
for (unsigned int wi = 0; wi < catlist[n].num_words; wi++)
{
const char *word = catlist[n].word[wi];
if ((currcol > 0) && currcol + (int)strlen(word) > maxcol)
{
if (wi != catlist[n].num_words - 1) printf("\n"TAB TAB TAB);
currcol = 0;
}
currcol += printf("\"%s\"", word); /* No crash if printf() fails. */
if (wi != catlist[n].num_words - 1)
{
printf(",");
currcol++;
}
else
{
printf("\n");
}
}
printf(TAB TAB"]\n");
printf(TAB"}");
if (catlist[n+1].num_words != 0)
printf(",");
printf("\n");
}
printf(TAB"]\n");
printf("}\n");
}
/* =========================== END OF FILE =====================*/