- if (!c->n_cats)
- {
- if (c->n_cats >= allocated_cats)
- c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
- c->cats[c->n_cats++] = cat;
- }
-
- if (show_totals)
- {
- if (c->n_cats >= allocated_cats)
- c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
-
- struct ctables_category *totals;
- if (totals_before)
- {
- insert_element (c->cats, c->n_cats, sizeof *c->cats, 0);
- totals = &c->cats[0];
- }
- else
- totals = &c->cats[c->n_cats];
- c->n_cats++;
-
- *totals = (struct ctables_category) {
- .type = CCT_TOTAL,
- .total_label = total_label ? total_label : xstrdup (_("Total")),
- };
- }
-
- struct ctables_category *subtotal = NULL;
- for (size_t i = totals_before ? 0 : c->n_cats;
- totals_before ? i < c->n_cats : i-- > 0;
- totals_before ? i++ : 0)
- {
- struct ctables_category *cat = &c->cats[i];
- switch (cat->type)
- {
- case CCT_NUMBER:
- case CCT_STRING:
- case CCT_NRANGE:
- case CCT_SRANGE:
- case CCT_MISSING:
- case CCT_OTHERNM:
- cat->subtotal = subtotal;
- break;
-
- case CCT_POSTCOMPUTE:
- break;
-
- case CCT_SUBTOTAL:
- subtotal = cat;
- break;
-
- case CCT_TOTAL:
- case CCT_VALUE:
- case CCT_LABEL:
- case CCT_FUNCTION:
- case CCT_EXCLUDED_MISSING:
- break;
- }
- }
-
- if (cats_start_ofs != -1)
- {
- for (size_t i = 0; i < c->n_cats; i++)
- {
- struct ctables_category *cat = &c->cats[i];
- switch (cat->type)
- {
- case CCT_POSTCOMPUTE:
- cat->parse_format = parse_strings ? common_format->type : FMT_F;
- struct msg_location *cats_location
- = lex_ofs_location (lexer, cats_start_ofs, cats_end_ofs);
- bool ok = ctables_recursive_check_postcompute (
- dict, cat->pc->expr, cat, c, cats_location);
- msg_location_destroy (cats_location);
- if (!ok)
- goto error;
- break;
-
- case CCT_NUMBER:
- case CCT_NRANGE:
- for (size_t j = 0; j < n_vars; j++)
- if (var_is_alpha (vars[j]))
- {
- msg_at (SE, cat->location,
- _("This category specification may be applied "
- "only to numeric variables, but this "
- "subcommand tries to apply it to string "
- "variable %s."),
- var_get_name (vars[j]));
- goto error;
- }
- break;
-
- case CCT_STRING:
- if (parse_strings)
- {
- double n;
- if (!parse_category_string (cat->location, cat->string, dict,
- common_format->type, &n))
- goto error;
-
- ss_dealloc (&cat->string);
-
- cat->type = CCT_NUMBER;
- cat->number = n;
- }
- else if (!all_strings (vars, n_vars, cat))
- goto error;
- break;
-
- case CCT_SRANGE:
- if (parse_strings)
- {
- double n[2];
-
- if (!cat->srange[0].string)
- n[0] = -DBL_MAX;
- else if (!parse_category_string (cat->location,
- cat->srange[0], dict,
- common_format->type, &n[0]))
- goto error;
-
- if (!cat->srange[1].string)
- n[1] = DBL_MAX;
- else if (!parse_category_string (cat->location,
- cat->srange[1], dict,
- common_format->type, &n[1]))
- goto error;
-
- ss_dealloc (&cat->srange[0]);
- ss_dealloc (&cat->srange[1]);
-
- cat->type = CCT_NRANGE;
- cat->nrange[0] = n[0];
- cat->nrange[1] = n[1];
- }
- else if (!all_strings (vars, n_vars, cat))
- goto error;
- break;
-
- case CCT_MISSING:
- case CCT_OTHERNM:
- case CCT_SUBTOTAL:
- case CCT_TOTAL:
- case CCT_VALUE:
- case CCT_LABEL:
- case CCT_FUNCTION:
- case CCT_EXCLUDED_MISSING:
- break;
- }
- }
- }
-
- free (vars);
- return true;
-
-error:
- free (vars);
- return false;
-}
-\f
-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)