/** Number of admissible files */
unsigned num;
};
-static struct afs_statistics statistics = {.normalization_divisor = 1};
+/**
+ * Stores an instance of an open mood (parser and statistics).
+ *
+ * A structure of this type is allocated and initialized at mood open time.
+ */
struct mood {
- /** The name of this mood. */
+ /** NULL means that this is the "dummy" mood. */
char *name;
- /** Info for the bison parser. */
+ /** Bison's abstract syntax tree, used to determine admissibility. */
struct mp_context *parser_context;
+ /** To compute the score. */
+ struct afs_statistics stats;
};
/*
* If current_mood is NULL then no mood is currently open. If
* current_mood->name is NULL, the dummy mood is currently open.
+ *
+ * The statistics are adjusted dynamically through this pointer as files are
+ * added, removed or played.
*/
static struct mood *current_mood;
return res;
}
-/* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
-{
- if (!m)
- return -E_NO_MOOD;
- return mp_eval_row(aft_row, m->parser_context);
-}
-
static void destroy_mood(struct mood *m)
{
if (!m)
struct mood *m = zalloc(sizeof(struct mood));
if (name)
m->name = para_strdup(name);
+ m->stats.normalization_divisor = 1;
return m;
}
-static int load_mood(const struct osl_row *mood_row, struct mood **m,
- char **errmsg)
+static int init_mood_parser(const char *mood_name, struct mood **m, char **err)
{
- char *mood_name;
struct osl_object mood_def;
int ret;
- ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
+ if (!*mood_name) {
+ if (err)
+ *err = make_message("empty mood name\n");
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ }
+ ret = mood_get_def_by_name(mood_name, &mood_def);
if (ret < 0) {
- if (errmsg)
- *errmsg = make_message(
- "could not read mood definition");
+ if (err)
+ *err = make_message("could not read mood definition\n");
return ret;
}
- assert(*mood_name);
*m = alloc_new_mood(mood_name);
PARA_INFO_LOG("opening mood %s\n", mood_name);
- ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
+ ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, err);
osl_close_disk_object(&mood_def);
if (ret < 0)
destroy_mood(*m);
static long compute_score(struct afs_info *afsi)
{
- long score = -normalized_value(afsi->num_played, statistics.num,
- statistics.num_played_sum, statistics.num_played_qd);
- score -= normalized_value(afsi->last_played, statistics.num,
- statistics.last_played_sum, statistics.last_played_qd);
+ const struct afs_statistics *stats = ¤t_mood->stats;
+ long score = -normalized_value(afsi->num_played, stats->num,
+ stats->num_played_sum, stats->num_played_qd);
+ score -= normalized_value(afsi->last_played, stats->num,
+ stats->last_played_sum, stats->last_played_qd);
return score / 2;
}
uint64_t n, x, s, q;
struct afs_info afsi;
int ret;
+ struct afs_statistics *stats = ¤t_mood->stats;
ret = get_afsi_of_row(row, &afsi);
if (ret < 0)
return ret;
- n = statistics.num;
+ n = stats->num;
x = afsi.last_played;
- s = statistics.last_played_sum;
+ s = stats->last_played_sum;
if (n > 0) {
q = (x > s / n)? x - s / n : s / n - x;
- statistics.last_played_qd += q * q * n / (n + 1);
+ stats->last_played_qd += q * q * n / (n + 1);
}
- statistics.last_played_sum += x;
+ stats->last_played_sum += x;
x = afsi.num_played;
- s = statistics.num_played_sum;
+ s = stats->num_played_sum;
if (n > 0) {
q = (x > s / n)? x - s / n : s / n - x;
- statistics.num_played_qd += q * q * n / (n + 1);
+ stats->num_played_qd += q * q * n / (n + 1);
}
- statistics.num_played_sum += x;
- statistics.num++;
+ stats->num_played_sum += x;
+ stats->num++;
return 1;
}
static int del_afs_statistics(const struct osl_row *row)
{
+ struct afs_statistics *stats = ¤t_mood->stats;
uint64_t n, s, q, a, new_s;
struct afs_info afsi;
int ret;
ret = get_afsi_of_row(row, &afsi);
if (ret < 0)
return ret;
- n = statistics.num;
+ n = stats->num;
assert(n);
if (n == 1) {
- memset(&statistics, 0, sizeof(statistics));
- statistics.normalization_divisor = 1;
+ memset(stats, 0, sizeof(*stats));
+ stats->normalization_divisor = 1;
return 1;
}
- s = statistics.last_played_sum;
- q = statistics.last_played_qd;
+ s = stats->last_played_sum;
+ q = stats->last_played_qd;
a = afsi.last_played;
new_s = s - a;
- statistics.last_played_sum = new_s;
- statistics.last_played_qd = q + s * s / n - a * a
+ stats->last_played_sum = new_s;
+ stats->last_played_qd = q + s * s / n - a * a
- new_s * new_s / (n - 1);
- s = statistics.num_played_sum;
- q = statistics.num_played_qd;
+ s = stats->num_played_sum;
+ q = stats->num_played_qd;
a = afsi.num_played;
new_s = s - a;
- statistics.num_played_sum = new_s;
- statistics.num_played_qd = q + s * s / n - a * a
+ stats->num_played_sum = new_s;
+ stats->num_played_qd = q + s * s / n - a * a
- new_s * new_s / (n - 1);
- statistics.num--;
+ stats->num--;
return 1;
}
*/
static int add_if_admissible(struct osl_row *aft_row, void *data)
{
+ const struct afs_statistics *stats = ¤t_mood->stats;
struct admissible_array *aa = data;
- int ret;
- ret = row_is_admissible(aft_row, aa->m);
- if (ret <= 0)
- return ret;
- if (statistics.num >= aa->size) {
+ if (!mp_eval_row(aft_row, aa->m->parser_context))
+ return 0;
+ if (stats->num >= aa->size) {
aa->size *= 2;
aa->size += 100;
- aa->array = para_realloc(aa->array,
- aa->size * sizeof(struct osl_row *));
+ aa->array = arr_realloc(aa->array, aa->size,
+ sizeof(struct osl_row *));
}
- aa->array[statistics.num] = aft_row;
+ aa->array[stats->num] = aft_row;
return add_afs_statistics(aft_row);
}
return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
}
-static int update_afs_statistics(struct afs_info *old_afsi,
+static void update_afs_statistics(struct afs_info *old_afsi,
struct afs_info *new_afsi)
{
- unsigned n;
- int ret = get_num_admissible_files(&n);
-
- if (ret < 0)
- return ret;
- assert(n);
-
- statistics.last_played_qd = update_quadratic_deviation(n,
- statistics.last_played_qd, old_afsi->last_played,
- new_afsi->last_played, statistics.last_played_sum);
- statistics.last_played_sum += new_afsi->last_played - old_afsi->last_played;
-
- statistics.num_played_qd = update_quadratic_deviation(n,
- statistics.num_played_qd, old_afsi->num_played,
- new_afsi->num_played, statistics.num_played_sum);
- statistics.num_played_sum += new_afsi->num_played - old_afsi->num_played;
- return 1;
+ struct afs_statistics *stats = ¤t_mood->stats;
+
+ assert(stats->num > 0);
+ stats->last_played_qd = update_quadratic_deviation(stats->num,
+ stats->last_played_qd, old_afsi->last_played,
+ new_afsi->last_played, stats->last_played_sum);
+ stats->last_played_sum += new_afsi->last_played - old_afsi->last_played;
+
+ stats->num_played_qd = update_quadratic_deviation(stats->num,
+ stats->num_played_qd, old_afsi->num_played,
+ new_afsi->num_played, stats->num_played_sum);
+ stats->num_played_sum += new_afsi->num_played - old_afsi->num_played;
}
static int add_to_score_table(const struct osl_row *aft_row)
struct afs_info *old_afsi)
{
long score, percent;
- int ret, is_admissible, was_admissible = 0;
+ int ret;
+ bool is_admissible, was_admissible;
struct afs_info afsi;
unsigned rank;
if (ret < 0)
return ret;
was_admissible = ret;
- ret = row_is_admissible(aft_row, current_mood);
- if (ret < 0)
- return ret;
- is_admissible = (ret > 0);
+ is_admissible = mp_eval_row(aft_row, current_mood->parser_context);
if (!was_admissible && !is_admissible)
return 1;
if (was_admissible && !is_admissible)
ret = get_afsi_of_row(aft_row, &afsi);
if (ret < 0)
return ret;
- if (old_afsi) {
- ret = update_afs_statistics(old_afsi, &afsi);
- if (ret < 0)
- return ret;
- }
+ if (old_afsi)
+ update_afs_statistics(old_afsi, &afsi);
score = compute_score(&afsi);
PARA_DEBUG_LOG("score: %li\n", score);
percent = (score + 100) / 3;
/* sse: seconds since epoch. */
static void log_statistics(int64_t sse)
{
- unsigned n = statistics.num;
+ const struct afs_statistics *stats = ¤t_mood->stats;
+ unsigned n = stats->num;
int mean_days, sigma_days;
assert(current_mood);
PARA_WARNING_LOG("no admissible files\n");
return;
}
- PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
- mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
- sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
+ PARA_NOTICE_LOG("%u admissible files\n", stats->num);
+ mean_days = (sse - stats->last_played_sum / n) / 3600 / 24;
+ sigma_days = int_sqrt(stats->last_played_qd / n) / 3600 / 24;
PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
- statistics.num_played_sum / n,
- int_sqrt(statistics.num_played_qd / n));
+ stats->num_played_sum / n,
+ int_sqrt(stats->num_played_qd / n));
PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
- statistics.num_played_correction);
+ stats->num_played_correction);
PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
- statistics.last_played_correction);
+ stats->last_played_correction);
PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
- statistics.normalization_divisor);
+ stats->normalization_divisor);
}
/**
{
destroy_mood(current_mood);
current_mood = NULL;
- memset(&statistics, 0, sizeof(statistics));
- statistics.normalization_divisor = 1;
}
static void compute_correction_factors(int64_t sse)
{
- struct afs_statistics *s = &statistics;
+ struct afs_statistics *s = ¤t_mood->stats;
if (s->num > 0) {
s->normalization_divisor = int_sqrt(s->last_played_qd)
if (mood_name) {
struct mood *m;
- struct osl_row *row;
- struct osl_object obj;
-
- if (!*mood_name) {
- *errmsg = make_message("empty mood name");
- return -ERRNO_TO_PARA_ERROR(EINVAL);
- }
- obj.data = (char *)mood_name;
- obj.size = strlen(mood_name) + 1;
- ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row));
- if (ret < 0) {
- if (errmsg)
- *errmsg = make_message("no such mood: %s",
- mood_name);
- return ret;
- }
- ret = load_mood(row, &m, errmsg);
+ ret = init_mood_parser(mood_name, &m, errmsg);
if (ret < 0)
return ret;
close_current_mood();
clock_get_realtime(&rnow);
compute_correction_factors(rnow.tv_sec);
log_statistics(rnow.tv_sec);
- for (i = 0; i < statistics.num; i++) {
+ for (i = 0; i < current_mood->stats.num; i++) {
ret = add_to_score_table(aa.array[i]);
if (ret < 0) {
if (errmsg)
goto out;
}
}
- ret = statistics.num;
+ ret = current_mood->stats.num;
out:
free(aa.array);
if (ret < 0)
* This function is called on events which render the current list of
* admissible files useless, for example if an attribute is removed from the
* attribute table.
- *
- * If no mood is currently open, the function returns success.
*/
static int reload_current_mood(void)
{
int ret;
char *mood_name = NULL;
+ assert(current_mood);
ret = clear_score_table();
if (ret < 0)
return ret;
- if (!current_mood)
- return 1;
PARA_NOTICE_LOG("reloading %s\n", current_mood->name?
current_mood->name : "(dummy)");
if (current_mood->name)