+static struct ctables_domain *
+ctables_domain_insert (struct ctables_table *t, struct ctables_freq *f,
+ enum ctables_domain_type domain)
+{
+ size_t hash = 0;
+ for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
+ {
+ size_t idx = f->axes[a].vaa_idx;
+ const struct var_array *va = &t->vaas[a].vas[idx];
+ hash = hash_int (idx, hash);
+ for (size_t i = 0; i < va->n_domains[domain]; i++)
+ {
+ size_t v_idx = va->domains[domain][i];
+ hash = value_hash (&f->axes[a].values[v_idx],
+ var_get_width (va->vars[v_idx]), hash);
+ }
+ }
+
+ struct ctables_domain *d;
+ HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &t->domains[domain])
+ {
+ const struct ctables_freq *df = d->example;
+ for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
+ {
+ size_t idx = f->axes[a].vaa_idx;
+ if (idx != df->axes[a].vaa_idx)
+ goto not_equal;
+
+ const struct var_array *va = &t->vaas[a].vas[idx];
+ for (size_t i = 0; i < va->n_domains[domain]; i++)
+ {
+ size_t v_idx = va->domains[domain][i];
+ if (!value_equal (&df->axes[a].values[v_idx],
+ &f->axes[a].values[v_idx],
+ var_get_width (va->vars[v_idx])))
+ goto not_equal;
+ }
+ }
+ return d;
+
+ not_equal: ;
+ }
+
+ d = xmalloc (sizeof *d);
+ *d = (struct ctables_domain) { .example = f };
+ hmap_insert (&t->domains[domain], &d->node, hash);
+ return d;
+}
+
+static const struct ctables_cat_value *
+ctables_categories_match (const struct ctables_categories *cats,
+ const union value *v, const struct variable *var)
+{
+ const struct ctables_cat_value *othernm = NULL;
+ for (size_t i = cats->n_values; i-- > 0; )
+ {
+ const struct ctables_cat_value *cv = &cats->values[i];
+ switch (cv->type)
+ {
+ case CCVT_NUMBER:
+ if (cv->number == v->f)
+ return cv;
+ break;
+
+ case CCVT_STRING:
+ NOT_REACHED ();
+
+ case CCVT_RANGE:
+ if ((cv->range[0] == -DBL_MAX || v->f >= cv->range[0])
+ && (cv->range[1] == DBL_MAX || v->f <= cv->range[1]))
+ return cv;
+ break;
+
+ case CCVT_MISSING:
+ if (var_is_value_missing (var, v))
+ return cv;
+ break;
+
+ case CCVT_OTHERNM:
+ if (!othernm)
+ othernm = cv;
+ break;
+
+ case CCVT_SUBTOTAL:
+ case CCVT_HSUBTOTAL:
+ break;
+ }
+ }
+
+ return var_is_value_missing (var, v) ? NULL : othernm;
+}
+