+ if (!cat)
+ return;
+ assert (!cat->df_to_iact);
+ assert (!cat->cat_to_iact);
+
+ double weight;
+ weight = cat->wv ? case_num (c, cat->wv) : 1.0;
+ weight = var_force_valid_weight (cat->wv, weight, NULL);
+
+ /* Update the frequency table for each variable. */
+ struct variable_node *vn;
+ HMAP_FOR_EACH (vn, struct variable_node, node, &cat->varmap)
+ {
+ const int width = var_get_width (vn->var);
+ const union value *val = case_data (c, vn->var);
+ unsigned int hash = value_hash (val, width, 0);
+
+ struct value_node *valn = lookup_value (&vn->valmap, val, hash, width);
+ if (valn == NULL)
+ {
+ valn = pool_malloc (cat->pool, sizeof *valn);
+ valn->index = -1;
+ value_init_pool (cat->pool, &valn->val, width);
+ value_copy (&valn->val, val, width);
+ hmap_insert (&vn->valmap, &valn->node, hash);
+ }
+ }
+
+ /* Update the frequency table for full interactions. */
+ for (int i = 0; i < cat->n_iap; ++i)
+ {
+ struct interact_params *iap = &cat->iap[i];
+ const struct interaction *iact = iap->iact;
+ if (interaction_case_is_missing (iact, c, cat->fctr_excl))
+ continue;
+
+ unsigned int hash = interaction_case_hash (iact, c, 0);
+ struct interaction_value *node = lookup_case (&iap->ivmap, iact, c);
+ if (!node)
+ {
+ node = pool_malloc (cat->pool, sizeof *node);
+ node->ccase = case_ref (c);
+ node->cc = weight;
+
+ hmap_insert (&iap->ivmap, &node->node, hash);
+
+ if (cat->payload)
+ node->user_data = cat->payload->create (cat->aux1, cat->aux2);
+ }
+ else
+ node->cc += weight;
+ iap->cc += weight;
+
+ if (cat->payload)
+ cat->payload->update (cat->aux1, cat->aux2, node->user_data, c,
+ weight);
+ }
+}
+
+/* Return the number of categories (distinct values) for interaction IDX in
+ CAT. */
+size_t
+categoricals_n_count (const struct categoricals *cat, size_t idx)
+{
+ return hmap_count (&cat->iap[idx].ivmap);
+}
+
+/* Return the total number of categories across all interactions in CAT. */
+size_t
+categoricals_n_total (const struct categoricals *cat)
+{
+ return categoricals_is_complete (cat) ? cat->n_cats_total : 0;
+}
+
+/* Returns the number of degrees of freedom for interaction IDX within CAT. */
+size_t
+categoricals_df (const struct categoricals *cat, size_t idx)
+{
+ const struct interact_params *iap = &cat->iap[idx];
+ return iap->df_prod;
+}
+
+/* Returns the total degrees of freedom for CAT. */
+size_t
+categoricals_df_total (const struct categoricals *cat)
+{
+ return cat ? cat->df_sum : 0;
+}
+
+/* Returns true iff categoricals_done() has been called for CAT. */
+bool
+categoricals_is_complete (const struct categoricals *cat)
+{
+ return cat->df_to_iact != NULL;
+}