#undef S
};
-struct ctables_subtable
+enum ctables_domain_type
{
- /* In struct ctables's 'subtables' hmap. Indexed by all the values in all
- the axes except the innermost row and column variable and the scalar
- variable, if any. (If the scalar variable is the innermost row or
- column variable, then the second-to-innermost variable is also omitted
- along that axis.) */
- struct hmap_node node;
-
- const struct ctables_freq *example;
-
- double valid;
- double missing;
+ /* Within a section, where stacked variables divide one section from
+ another. */
+ CTDT_TABLE, /* All layers of a whole section. */
+ CTDT_LAYER, /* One layer within a section. */
+ CTDT_LAYERROW, /* Row in one layer within a section. */
+ CTDT_LAYERCOL, /* Column in one layer within a section. */
+
+ /* Within a subtable, where a subtable pairs an innermost row variable with
+ an innermost column variable within a single layer. */
+ CTDT_SUBTABLE, /* Whole subtable. */
+ CTDT_ROW, /* Row within a subtable. */
+ CTDT_COL, /* Column within a subtable. */
+#define N_CTDTS 7
};
-struct ctables_layer
+struct ctables_domain
{
- /* In struct ctables's 'layers' hmap. Indexed by all the values in the
- layer axis, except the scalar variable, if any. */
struct hmap_node node;
+ const struct ctables_freq *example;
+
double valid;
double missing;
};
axes (except the scalar variable, if any). */
struct hmap_node node;
- /* The subtable that contains this cell. */
- struct ctables_subtable *subtable;
-
- /* The layer that contains this cell. */
+ /* The domains that contains this cell. */
+ struct ctables_domain *domains[N_CTDTS];
struct
{
struct variable **vars;
size_t n;
size_t scale_idx;
- size_t subtable_idx;
+ size_t *domains[N_CTDTS];
+ size_t n_domains[N_CTDTS];
struct ctables_summary_spec *summaries;
size_t n_summaries;
struct var_array2 vaas[PIVOT_N_AXES];
enum pivot_axis_type summary_axis;
struct hmap ft;
- struct hmap subtables;
+ struct hmap domains[N_CTDTS];
enum pivot_axis_type slabels_position;
bool slabels_visible;
struct ctables_chisq *chisq;
struct ctables_pairwise *pairwise;
-
};
struct ctables_var
};
};
+static const struct ctables_cat_value *ctables_categories_match (
+ const struct ctables_categories *, const union value *,
+ const struct variable *);
+
static void
ctables_cat_value_uninit (struct ctables_cat_value *cv)
{
return s->valid;
case CTSF_SUBTABLEPCT_COUNT:
- return f->subtable->valid ? s->valid / f->subtable->valid * 100 : SYSMIS;
+ return f->domains[CTDT_SUBTABLE]->valid ? s->valid / f->domains[CTDT_SUBTABLE]->valid * 100 : SYSMIS;
case CTSF_ROWPCT_COUNT:
+ return f->domains[CTDT_ROW]->valid ? s->valid / f->domains[CTDT_ROW]->valid * 100 : SYSMIS;
+
case CTSF_COLPCT_COUNT:
+ return f->domains[CTDT_COL]->valid ? s->valid / f->domains[CTDT_COL]->valid * 100 : SYSMIS;
+
case CTSF_TABLEPCT_COUNT:
+ return f->domains[CTDT_TABLE]->valid ? s->valid / f->domains[CTDT_TABLE]->valid * 100 : SYSMIS;
+
case CTSF_LAYERPCT_COUNT:
+ return f->domains[CTDT_LAYER]->valid ? s->valid / f->domains[CTDT_LAYER]->valid * 100 : SYSMIS;
+
case CTSF_LAYERROWPCT_COUNT:
+ return f->domains[CTDT_LAYERROW]->valid ? s->valid / f->domains[CTDT_LAYERROW]->valid * 100 : SYSMIS;
+
case CTSF_LAYERCOLPCT_COUNT:
+ return f->domains[CTDT_LAYERCOL]->valid ? s->valid / f->domains[CTDT_LAYERCOL]->valid * 100 : SYSMIS;
+
case CTSF_ROWPCT_VALIDN:
case CTSF_COLPCT_VALIDN:
case CTSF_TABLEPCT_VALIDN:
for (size_t i = 0; i < va->n; i++)
if (i != va->scale_idx)
{
- int cmp = value_compare_3way (&a->axes[aux->a].values[i],
- &b->axes[aux->a].values[i],
- var_get_width (va->vars[i]));
- if (cmp)
+ const struct variable *var = va->vars[i];
+ const union value *val_a = &a->axes[aux->a].values[i];
+ const union value *val_b = &b->axes[aux->a].values[i];
+ int cmp = value_compare_3way (val_a, val_b, var_get_width (var));
+ if (!cmp)
+ continue;
+
+ const struct ctables_categories *cats = aux->t->categories[var_get_dict_index (var)];
+ if (!cats)
return cmp;
+ else if (cats->n_values)
+ {
+ const struct ctables_cat_value *a_cv = ctables_categories_match (cats, val_a, var);
+ const struct ctables_cat_value *b_cv = ctables_categories_match (cats, val_b, var);
+ assert (a_cv && b_cv);
+ return (a_cv == b_cv ? cmp
+ : a_cv > b_cv ? 1
+ : -1);
+ }
+ else
+ {
+ switch (cats->key)
+ {
+ case CTCS_VALUE:
+ /* Nothing to do. */
+ break;
+
+ case CTCS_LABEL:
+ {
+ const char *a_label = var_lookup_value_label (var, val_a);
+ const char *b_label = var_lookup_value_label (var, val_b);
+ int label_cmp = (a_label
+ ? (b_label ? strcmp (a_label, b_label) : 1)
+ : (b_label ? -1 : 0));
+ if (label_cmp)
+ cmp = label_cmp;
+ }
+ break;
+
+ case CTCS_FUNCTION:
+ NOT_REACHED ();
+ }
+
+ return cats->sort_ascending ? cmp : -cmp;
+ }
}
return 0;
}
Fill the table entry using the indexes from before.
*/
-static struct ctables_subtable *
-ctables_subtable_insert (struct ctables_table *t, struct ctables_freq *f)
+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; i++)
- if (i != va->scale_idx && i != va->subtable_idx)
- hash = value_hash (&f->axes[a].values[i],
- var_get_width (va->vars[i]), 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_subtable *st;
- HMAP_FOR_EACH_WITH_HASH (st, struct ctables_subtable, node, hash, &t->subtables)
+ struct ctables_domain *d;
+ HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &t->domains[domain])
{
- const struct ctables_freq *stf = st->example;
+ 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 != stf->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; i++)
- if (i != va->scale_idx && i != va->subtable_idx
- && !value_equal (&stf->axes[a].values[i],
- &f->axes[a].values[i],
- var_get_width (va->vars[i])))
+ 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 st;
+ return d;
not_equal: ;
}
- st = xmalloc (sizeof *st);
- *st = (struct ctables_subtable) { .example = f };
- hmap_insert (&t->subtables, &st->node, hash);
- return st;
+ 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;
}
static void
};
const struct var_array *ss = &t->vaas[t->summary_axis].vas[ix[t->summary_axis]];
+ for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
+ {
+ const struct var_array *va = &t->vaas[a].vas[ix[a]];
+ for (size_t i = 0; i < va->n; i++)
+ {
+ if (i == va->scale_idx)
+ continue;
+
+ const struct variable *var = va->vars[i];
+ const union value *value = case_data (c, var);
+
+ enum mv_class missing = var_is_value_missing (var, value);
+ if (missing == MV_SYSTEM)
+ return;
+
+ const struct ctables_categories *cats = t->categories[var_get_dict_index (var)];
+ if (!cats)
+ {
+ if (missing)
+ return;
+ }
+ else if (cats->n_values)
+ {
+ if (!ctables_categories_match (cats, value, var))
+ return;
+ }
+ else
+ {
+ if (missing && !cats->include_missing)
+ return;
+ }
+ }
+ }
+
size_t hash = 0;
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
{
f->summaries = xmalloc (ss->n_summaries * sizeof *f->summaries);
for (size_t i = 0; i < ss->n_summaries; i++)
ctables_summary_init (&f->summaries[i], &ss->summaries[i]);
- f->subtable = ctables_subtable_insert (t, f);
+ for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+ f->domains[dt] = ctables_domain_insert (t, f, dt);
hmap_insert (&t->ft, &f->node, hash);
summarize:
for (size_t i = 0; i < ss->n_summaries; i++)
ctables_summary_add (&f->summaries[i], &ss->summaries[i], ss->summary_var,
case_data (c, ss->summary_var), weight);
- f->subtable->valid += weight;
+ for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+ f->domains[dt]->valid += weight;
}
static bool
if (t->axes[a])
{
t->vaas[a] = enumerate_fts (a, t->axes[a]);
+
for (size_t j = 0; j < t->vaas[a].n; j++)
{
struct var_array *va = &t->vaas[a].vas[j];
- va->subtable_idx = (
- a == PIVOT_AXIS_LAYER ? SIZE_MAX
- : va->n == 0 ? SIZE_MAX
- : va->scale_idx != va->n - 1 ? va->n - 1
- : va->n == 1 ? SIZE_MAX
- : va->n - 2);
+ for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+ {
+ va->domains[dt] = xmalloc (va->n * sizeof *va->domains[dt]);
+ va->n_domains[dt] = 0;
+
+ for (size_t k = 0; k < va->n; k++)
+ {
+ if (k == va->scale_idx)
+ continue;
+
+ switch (dt)
+ {
+ case CTDT_TABLE:
+ continue;
+
+ case CTDT_LAYER:
+ if (a != PIVOT_AXIS_LAYER)
+ continue;
+ break;
+
+ case CTDT_SUBTABLE:
+ case CTDT_ROW:
+ case CTDT_COL:
+ if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
+ : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
+ : a == PIVOT_AXIS_ROW)
+ {
+ if (k == va->n - 1
+ || (va->scale_idx == va->n - 1
+ && k == va->n - 2))
+ continue;
+ }
+ break;
+
+ case CTDT_LAYERROW:
+ if (a == PIVOT_AXIS_COLUMN)
+ continue;
+ break;
+
+ case CTDT_LAYERCOL:
+ if (a == PIVOT_AXIS_ROW)
+ continue;
+ break;
+ }
+
+ va->domains[dt][va->n_domains[dt]++] = k;
+ }
+ }
}
}
else
struct ctables_table *t = xmalloc (sizeof *t);
*t = (struct ctables_table) {
.ft = HMAP_INITIALIZER (t->ft),
- .subtables = HMAP_INITIALIZER (t->subtables),
.slabels_position = PIVOT_AXIS_COLUMN,
.slabels_visible = true,
.row_labels = CTLP_NORMAL,
.n_categories = dict_get_n_vars (dataset_dict (ds)),
.cilevel = 95,
};
+ for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+ hmap_init (&t->domains[dt]);
ct->tables[ct->n_tables++] = t;
lex_match (lexer, T_EQUALS);