- struct ctables_nest *new = &stack.nests[stack.n++];
- *new = (struct ctables_nest) {
- .vars = vars,
- .scale_idx = (a->scale_idx != SIZE_MAX ? a->scale_idx
- : b->scale_idx != SIZE_MAX ? a->n + b->scale_idx
- : SIZE_MAX),
- .summary_idx = (a->summary_idx != SIZE_MAX ? a->summary_idx
- : b->summary_idx != SIZE_MAX ? a->n + b->summary_idx
- : SIZE_MAX),
- .n = n,
- };
- for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
- ctables_summary_spec_set_clone (&new->specs[sv], &summary_src->specs[sv]);
- }
- ctables_stack_uninit (&s0);
- ctables_stack_uninit (&s1);
- return stack;
-}
-
-static struct ctables_stack
-stack_fts (struct ctables_stack s0, struct ctables_stack s1)
-{
- struct ctables_stack stack = { .nests = xnmalloc (s0.n + s1.n, sizeof *stack.nests) };
- for (size_t i = 0; i < s0.n; i++)
- stack.nests[stack.n++] = s0.nests[i];
- for (size_t i = 0; i < s1.n; i++)
- {
- stack.nests[stack.n] = s1.nests[i];
- stack.nests[stack.n].group_head += s0.n;
- stack.n++;
- }
- assert (stack.n == s0.n + s1.n);
- free (s0.nests);
- free (s1.nests);
- return stack;
-}
-
-static struct ctables_stack
-var_fts (const struct ctables_axis *a)
-{
- struct variable **vars = xmalloc (sizeof *vars);
- *vars = a->var;
-
- bool is_summary = a->specs[CSV_CELL].n || a->scale;
- struct ctables_nest *nest = xmalloc (sizeof *nest);
- *nest = (struct ctables_nest) {
- .vars = vars,
- .n = 1,
- .scale_idx = a->scale ? 0 : SIZE_MAX,
- .summary_idx = is_summary ? 0 : SIZE_MAX,
- };
- if (is_summary)
- for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
- {
- ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
- nest->specs[sv].var = a->var;
- nest->specs[sv].is_scale = a->scale;
- }
- return (struct ctables_stack) { .nests = nest, .n = 1 };
-}
-
-static struct ctables_stack
-enumerate_fts (enum pivot_axis_type axis_type, const struct ctables_axis *a)
-{
- if (!a)
- return (struct ctables_stack) { .n = 0 };
-
- switch (a->op)
- {
- case CTAO_VAR:
- return var_fts (a);
-
- case CTAO_STACK:
- return stack_fts (enumerate_fts (axis_type, a->subs[0]),
- enumerate_fts (axis_type, a->subs[1]));
-
- case CTAO_NEST:
- /* This should consider any of the scale variables found in the result to
- be linked to each other listwise for SMISSING=LISTWISE. */
- return nest_fts (enumerate_fts (axis_type, a->subs[0]),
- enumerate_fts (axis_type, a->subs[1]));
- }
-
- NOT_REACHED ();
-}
-
-union ctables_summary
- {
- /* COUNT, VALIDN, TOTALN. */
- double count;
-
- /* MINIMUM, MAXIMUM, RANGE. */
- struct
- {
- double min;
- double max;
- };
-
- /* MEAN, SEMEAN, STDDEV, SUM, VARIANCE, *.SUM. */
- struct moments1 *moments;
-
- /* MEDIAN, MODE, PTILE. */
- struct
- {
- struct casewriter *writer;
- double ovalid;
- double ovalue;
- };
- };
-
-static void
-ctables_summary_init (union ctables_summary *s,
- const struct ctables_summary_spec *ss)
-{
- switch (ss->function)
- {
- case CTSF_COUNT:
- case CTSF_areaPCT_COUNT:
- case CTSF_areaPCT_VALIDN:
- case CTSF_areaPCT_TOTALN:
- case CTSF_MISSING:
- case CTSF_TOTALN:
- case CTSF_VALIDN:
- s->count = 0;
- break;
-
- case CTSF_areaID:
- break;
-
- case CTSF_MAXIMUM:
- case CTSF_MINIMUM:
- case CTSF_RANGE:
- s->min = s->max = SYSMIS;
- break;
-
- case CTSF_MEAN:
- case CTSF_SUM:
- case CTSF_areaPCT_SUM:
- s->moments = moments1_create (MOMENT_MEAN);
- break;
-
- case CTSF_SEMEAN:
- case CTSF_STDDEV:
- case CTSF_VARIANCE:
- s->moments = moments1_create (MOMENT_VARIANCE);
- break;
-
- case CTSF_MEDIAN:
- case CTSF_MODE:
- case CTSF_PTILE:
- {
- struct caseproto *proto = caseproto_create ();
- proto = caseproto_add_width (proto, 0);
- proto = caseproto_add_width (proto, 0);
-
- struct subcase ordering;
- subcase_init (&ordering, 0, 0, SC_ASCEND);
- s->writer = sort_create_writer (&ordering, proto);
- subcase_uninit (&ordering);
- caseproto_unref (proto);
-
- s->ovalid = 0;
- s->ovalue = SYSMIS;
- }
- break;
- }
-}
-
-static void
-ctables_summary_uninit (union ctables_summary *s,
- const struct ctables_summary_spec *ss)
-{
- switch (ss->function)
- {
- case CTSF_COUNT:
- case CTSF_areaPCT_COUNT:
- case CTSF_areaPCT_VALIDN:
- case CTSF_areaPCT_TOTALN:
- case CTSF_MISSING:
- case CTSF_TOTALN:
- case CTSF_VALIDN:
- break;
-
- case CTSF_areaID:
- break;
-
- case CTSF_MAXIMUM:
- case CTSF_MINIMUM:
- case CTSF_RANGE:
- break;
-
- case CTSF_MEAN:
- case CTSF_SEMEAN:
- case CTSF_STDDEV:
- case CTSF_SUM:
- case CTSF_VARIANCE:
- case CTSF_areaPCT_SUM:
- moments1_destroy (s->moments);
- break;
-
- case CTSF_MEDIAN:
- case CTSF_MODE:
- case CTSF_PTILE:
- casewriter_destroy (s->writer);
- break;
- }
-}
-
-static void
-ctables_summary_add (union ctables_summary *s,
- const struct ctables_summary_spec *ss,
- const union value *value,
- bool is_missing, bool is_included,
- double weight)
-{
- /* To determine whether a case is included in a given table for a particular
- kind of summary, consider the following charts for the variable being
- summarized. Only if "yes" appears is the case counted.
-
- Categorical variables: VALIDN other TOTALN
- Valid values in included categories yes yes yes
- Missing values in included categories --- yes yes
- Missing values in excluded categories --- --- yes
- Valid values in excluded categories --- --- ---
-
- Scale variables: VALIDN other TOTALN
- Valid value yes yes yes
- Missing value --- yes yes
-
- Missing values include both user- and system-missing. (The system-missing
- value is always in an excluded category.)
-
- One way to interpret the above table is that scale variables are like
- categorical variables in which all values are in included categories.
- */
- switch (ss->function)
- {
- case CTSF_TOTALN:
- case CTSF_areaPCT_TOTALN:
- s->count += weight;
- break;
-
- case CTSF_COUNT:
- case CTSF_areaPCT_COUNT:
- if (is_included)
- s->count += weight;
- break;
-
- case CTSF_VALIDN:
- case CTSF_areaPCT_VALIDN:
- if (!is_missing)
- s->count += weight;
- break;
-
- case CTSF_areaID:
- break;
-
- case CTSF_MISSING:
- if (is_missing)
- s->count += weight;
- break;
-
- case CTSF_MAXIMUM:
- case CTSF_MINIMUM:
- case CTSF_RANGE:
- if (!is_missing)
- {
- if (s->min == SYSMIS || value->f < s->min)
- s->min = value->f;
- if (s->max == SYSMIS || value->f > s->max)
- s->max = value->f;
- }
- break;
-
- case CTSF_MEAN:
- case CTSF_SEMEAN:
- case CTSF_STDDEV:
- case CTSF_SUM:
- case CTSF_VARIANCE:
- if (!is_missing)
- moments1_add (s->moments, value->f, weight);
- break;
-
- case CTSF_areaPCT_SUM:
- if (!is_missing)
- moments1_add (s->moments, value->f, weight);
- break;
-
- case CTSF_MEDIAN:
- case CTSF_MODE:
- case CTSF_PTILE:
- if (!is_missing)
- {
- s->ovalid += weight;
-
- struct ccase *c = case_create (casewriter_get_proto (s->writer));
- *case_num_rw_idx (c, 0) = value->f;
- *case_num_rw_idx (c, 1) = weight;
- casewriter_write (s->writer, c);
- }
- break;
- }
-}
-
-static double
-ctables_summary_value (const struct ctables_cell *cell,
- union ctables_summary *s,
- const struct ctables_summary_spec *ss)
-{
- switch (ss->function)
- {
- case CTSF_COUNT:
- return s->count;
-
- case CTSF_areaID:
- return cell->areas[ss->calc_area]->sequence;
-
- case CTSF_areaPCT_COUNT:
- {
- const struct ctables_area *a = cell->areas[ss->calc_area];
- double a_count = a->count[ss->weighting];
- return a_count ? s->count / a_count * 100 : SYSMIS;
- }
-
- case CTSF_areaPCT_VALIDN:
- {
- const struct ctables_area *a = cell->areas[ss->calc_area];
- double a_valid = a->valid[ss->weighting];
- return a_valid ? s->count / a_valid * 100 : SYSMIS;
- }
-
- case CTSF_areaPCT_TOTALN:
- {
- const struct ctables_area *a = cell->areas[ss->calc_area];
- double a_total = a->total[ss->weighting];
- return a_total ? s->count / a_total * 100 : SYSMIS;
- }
-
- case CTSF_MISSING:
- case CTSF_TOTALN:
- case CTSF_VALIDN:
- return s->count;
-
- case CTSF_MAXIMUM:
- return s->max;
-
- case CTSF_MINIMUM:
- return s->min;
-
- case CTSF_RANGE:
- return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
-
- case CTSF_MEAN:
- {
- double mean;
- moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
- return mean;
- }
-
- case CTSF_SEMEAN:
- {
- double weight, variance;
- moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
- return calc_semean (variance, weight);
- }
-
- case CTSF_STDDEV:
- {
- double variance;
- moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
- return variance != SYSMIS ? sqrt (variance) : SYSMIS;
- }
-
- case CTSF_SUM:
- {
- double weight, mean;
- moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
- return weight != SYSMIS && mean != SYSMIS ? weight * mean : SYSMIS;
- }
-
- case CTSF_VARIANCE:
- {
- double variance;
- moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
- return variance;
- }
-
- case CTSF_areaPCT_SUM:
- {
- double weight, mean;
- moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
- if (weight == SYSMIS || mean == SYSMIS)
- return SYSMIS;
-
- const struct ctables_area *a = cell->areas[ss->calc_area];
- const struct ctables_sum *sum = &a->sums[ss->sum_var_idx];
- double denom = sum->sum[ss->weighting];
- return denom != 0 ? weight * mean / denom * 100 : SYSMIS;
- }
-
- case CTSF_MEDIAN:
- case CTSF_PTILE:
- if (s->writer)
- {
- struct casereader *reader = casewriter_make_reader (s->writer);
- s->writer = NULL;
-
- struct percentile *ptile = percentile_create (
- ss->function == CTSF_PTILE ? ss->percentile : 0.5, s->ovalid);
- struct order_stats *os = &ptile->parent;
- order_stats_accumulate_idx (&os, 1, reader, 1, 0);
- s->ovalue = percentile_calculate (ptile, PC_HAVERAGE);
- statistic_destroy (&ptile->parent.parent);
- }
- return s->ovalue;
-
- case CTSF_MODE:
- if (s->writer)
- {
- struct casereader *reader = casewriter_make_reader (s->writer);
- s->writer = NULL;
-
- struct mode *mode = mode_create ();
- struct order_stats *os = &mode->parent;
- order_stats_accumulate_idx (&os, 1, reader, 1, 0);
- s->ovalue = mode->mode;
- statistic_destroy (&mode->parent.parent);
- }
- return s->ovalue;
- }
-
- NOT_REACHED ();
-}
-
-struct ctables_cell_sort_aux
- {
- const struct ctables_nest *nest;
- enum pivot_axis_type a;