+ for (i = 0; i < mnemon->num_categories; i++) {
+ category = &mnemon->categories[i];
+ item_index = item - category->items;
+ if (item_index < category->num_items)
+ return category;
+ }
+
+ assert (0);
+}
+
+void
+mnemon_select_item (mnemon_t *mnemon,
+ bin_t **bin_ret,
+ int *item_index_ret,
+ category_t **category_ret,
+ int *introduced_ret)
+{
+ int bin_index, item_index;
+ bin_t *bin;
+ item_t *item;
+ category_t *category;
+
+ bin_index = rand_within_exponential (mnemon->num_bins);
+ bin = &mnemon->bins[bin_index];
+
+ /* The most intuitive understanding of the introduced flag that
+ * it's tracking never-before-learned items as they are pulled
+ * from the bin with score 0. But that bin can become empty. So
+ * the refined rule is that we also set introduced whenever we
+ * pull from the lowest-indexed bin with a non-negative score. */
+ if (bin->score >=0 &&
+ (bin_index == 0 || mnemon->bins[bin_index-1].score < 0))
+ {
+ *introduced_ret = 1;
+ }
+ else
+ {
+ *introduced_ret = 0;
+ }
+
+ item_index = rand_within (bin->num_items);
+
+ item = bin->items[item_index];
+ category = mnemon_item_category (mnemon, item);
+
+ if (bin->score == 0) {
+ if (category->order == CATEGORY_ORDER_SEQUENTIAL) {
+ item = category_next_bin_zero_item (category);
+ if (item)
+ item_index = bin_item_index (bin, item);
+ }
+ }
+
+ *bin_ret = bin;
+ *item_index_ret = item_index;
+ *category_ret = category;
+}
+
+void
+mnemon_score_item (mnemon_t *mnemon,
+ bin_t *bin,
+ unsigned int item_index,
+ bool_t correct)
+{
+ item_t *item;
+
+ if (item_index >= bin->num_items)
+ return;
+
+ item = bin->items[item_index];
+ bin_remove_item (bin, item_index);
+
+ /* If the bin is now empty, we must remove it. */
+ if (bin->num_items == 0)
+ {
+ mnemon_remove_bin (mnemon, bin->score);
+ }
+
+ if (correct)
+ {
+ item->score++;
+ /* We reserve an item score of 0 for an item that has
+ * never been asked. */
+ if (item->score == 0)
+ item->score = 1;
+ }
+ else
+ {
+ /* Penalize an incorrect response by forcing the score
+ * negative. */
+ if (item->score >= 0) {
+ /* We go to -2 to force a little extra reinforcement
+ * when re-learning an item, (otherwise, it will often
+ * get asked again immediately where it is easy to get
+ * a correct response without any learning). */
+ item->score = -2;
+ } else {
+ item->score--;
+ }
+ }