1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2021 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 #include "data/casereader.h"
23 #include "data/casewriter.h"
24 #include "data/data-in.h"
25 #include "data/data-out.h"
26 #include "data/dataset.h"
27 #include "data/dictionary.h"
28 #include "data/mrset.h"
29 #include "data/subcase.h"
30 #include "data/value-labels.h"
31 #include "language/command.h"
32 #include "language/lexer/format-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "language/lexer/token.h"
35 #include "language/lexer/variable-parser.h"
36 #include "libpspp/array.h"
37 #include "libpspp/assertion.h"
38 #include "libpspp/hash-functions.h"
39 #include "libpspp/hmap.h"
40 #include "libpspp/i18n.h"
41 #include "libpspp/message.h"
42 #include "libpspp/string-array.h"
43 #include "math/mode.h"
44 #include "math/moments.h"
45 #include "math/percentiles.h"
46 #include "math/sort.h"
47 #include "output/pivot-table.h"
49 #include "gl/minmax.h"
50 #include "gl/xalloc.h"
53 #define _(msgid) gettext (msgid)
54 #define N_(msgid) (msgid)
58 CTVL_NONE = SETTINGS_VALUE_SHOW_DEFAULT,
59 CTVL_NAME = SETTINGS_VALUE_SHOW_VALUE,
60 CTVL_LABEL = SETTINGS_VALUE_SHOW_LABEL,
61 CTVL_BOTH = SETTINGS_VALUE_SHOW_BOTH,
65 - unweighted summaries (U*)
66 - lower confidence limits (*.LCL)
67 - upper confidence limits (*.UCL)
68 - standard error (*.SE)
71 /* All variables. */ \
72 S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT, CTFA_ALL) \
73 S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT, CTFA_ALL) \
74 S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT, CTFA_ALL) \
75 S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT, CTFA_ALL) \
76 S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT, CTFA_ALL) \
77 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT, CTFA_ALL) \
78 S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT, CTFA_ALL) \
79 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT, CTFA_ALL) \
80 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT, CTFA_ALL) \
81 S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
82 S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
83 S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
84 S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
85 S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
86 S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
87 S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
88 S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT, CTFA_ALL) \
89 S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT, CTFA_ALL) \
90 S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT, CTFA_ALL) \
91 S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
92 S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
93 S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
94 S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
96 /* Scale variables, totals, and subtotals. */ \
97 S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE) \
98 S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE) \
99 S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL, CTFA_SCALE) \
100 S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL, CTFA_SCALE) \
101 S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL, CTFA_SCALE) \
102 S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL, CTFA_SCALE) \
103 S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL, CTFA_SCALE) \
104 S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL, CTFA_SCALE) \
105 S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
106 S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
107 S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL, CTFA_SCALE) \
108 S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT, CTFA_SCALE) \
109 S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT, CTFA_SCALE) \
110 S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT, CTFA_SCALE) \
111 S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT, CTFA_SCALE) \
112 S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL, CTFA_SCALE) \
113 S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
114 S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
115 S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
116 S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
117 S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
118 S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
119 S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
121 #if 0 /* Multiple response sets not yet implemented. */
122 S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
123 S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
124 S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
125 S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT, CTFA_MRSETS) \
126 S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT, CTFA_MRSETS) \
127 S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT, CTFA_MRSETS) \
128 S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
129 S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
130 S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
131 S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
132 S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
133 S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
134 S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
135 S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
136 S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
137 S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
138 S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
139 S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
140 S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
141 S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
142 S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
143 S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
146 enum ctables_summary_function
148 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) ENUM,
154 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) +1
155 N_CTSF_FUNCTIONS = SUMMARIES
159 static bool ctables_summary_function_is_count (enum ctables_summary_function);
161 enum ctables_domain_type
163 /* Within a section, where stacked variables divide one section from
165 CTDT_TABLE, /* All layers of a whole section. */
166 CTDT_LAYER, /* One layer within a section. */
167 CTDT_LAYERROW, /* Row in one layer within a section. */
168 CTDT_LAYERCOL, /* Column in one layer within a section. */
170 /* Within a subtable, where a subtable pairs an innermost row variable with
171 an innermost column variable within a single layer. */
172 CTDT_SUBTABLE, /* Whole subtable. */
173 CTDT_ROW, /* Row within a subtable. */
174 CTDT_COL, /* Column within a subtable. */
178 struct ctables_domain
180 struct hmap_node node;
182 const struct ctables_cell *example;
184 double d_valid; /* Dictionary weight. */
187 double e_valid; /* Effective weight */
192 enum ctables_summary_variant
201 /* In struct ctables_section's 'cells' hmap. Indexed by all the values in
202 all the axes (except the scalar variable, if any). */
203 struct hmap_node node;
205 /* The domains that contain this cell. */
206 uint32_t omit_domains;
207 struct ctables_domain *domains[N_CTDTS];
212 enum ctables_summary_variant sv;
214 struct ctables_cell_axis
216 struct ctables_cell_value
218 const struct ctables_category *category;
226 union ctables_summary *summaries;
233 const struct dictionary *dict;
234 struct pivot_table_look *look;
236 /* CTABLES has a number of extra formats that we implement via custom
237 currency specifications on an alternate fmt_settings. */
238 #define CTEF_NEGPAREN FMT_CCA
239 #define CTEF_NEQUAL FMT_CCB
240 #define CTEF_PAREN FMT_CCC
241 #define CTEF_PCTPAREN FMT_CCD
242 struct fmt_settings ctables_formats;
244 /* If this is NULL, zeros are displayed using the normal print format.
245 Otherwise, this string is displayed. */
248 /* If this is NULL, missing values are displayed using the normal print
249 format. Otherwise, this string is displayed. */
252 /* Indexed by variable dictionary index. */
253 enum ctables_vlabel *vlabels;
255 struct hmap postcomputes; /* Contains "struct ctables_postcompute"s. */
257 bool mrsets_count_duplicates; /* MRSETS. */
258 bool smissing_listwise; /* SMISSING. */
259 struct variable *e_weight; /* WEIGHT. */
260 int hide_threshold; /* HIDESMALLCOUNTS. */
262 struct ctables_table **tables;
266 static struct ctables_postcompute *ctables_find_postcompute (struct ctables *,
269 struct ctables_postcompute
271 struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
272 char *name; /* Name, without leading &. */
274 struct msg_location *location; /* Location of definition. */
275 struct ctables_pcexpr *expr;
277 struct ctables_summary_spec_set *specs;
278 bool hide_source_cats;
281 struct ctables_pcexpr
291 enum ctables_postcompute_op
294 CTPO_CONSTANT, /* 5 */
295 CTPO_CAT_NUMBER, /* [5] */
296 CTPO_CAT_STRING, /* ["STRING"] */
297 CTPO_CAT_NRANGE, /* [LO THRU 5] */
298 CTPO_CAT_SRANGE, /* ["A" THRU "B"] */
299 CTPO_CAT_MISSING, /* MISSING */
300 CTPO_CAT_OTHERNM, /* OTHERNM */
301 CTPO_CAT_SUBTOTAL, /* SUBTOTAL */
302 CTPO_CAT_TOTAL, /* TOTAL */
316 /* CTPO_CAT_NUMBER. */
319 /* CTPO_CAT_STRING, in dictionary encoding. */
320 struct substring string;
322 /* CTPO_CAT_NRANGE. */
325 /* CTPO_CAT_SRANGE. */
326 struct substring srange[2];
328 /* CTPO_CAT_SUBTOTAL. */
329 size_t subtotal_index;
331 /* Two elements: CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW.
332 One element: CTPO_NEG. */
333 struct ctables_pcexpr *subs[2];
336 /* Source location. */
337 struct msg_location *location;
340 static void ctables_pcexpr_destroy (struct ctables_pcexpr *);
341 static struct ctables_pcexpr *ctables_pcexpr_allocate_binary (
342 enum ctables_postcompute_op, struct ctables_pcexpr *sub0,
343 struct ctables_pcexpr *sub1);
345 struct ctables_summary_spec_set
347 struct ctables_summary_spec *specs;
351 /* The variable to which the summary specs are applied. */
352 struct variable *var;
354 /* Whether the variable to which the summary specs are applied is a scale
355 variable for the purpose of summarization.
357 (VALIDN and TOTALN act differently for summarizing scale and categorical
361 /* If any of these optional additional scale variables are missing, then
362 treat 'var' as if it's missing too. This is for implementing
363 SMISSING=LISTWISE. */
364 struct variable **listwise_vars;
365 size_t n_listwise_vars;
368 static void ctables_summary_spec_set_clone (struct ctables_summary_spec_set *,
369 const struct ctables_summary_spec_set *);
370 static void ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *);
372 /* A nested sequence of variables, e.g. a > b > c. */
375 struct variable **vars;
378 size_t *domains[N_CTDTS];
379 size_t n_domains[N_CTDTS];
382 struct ctables_summary_spec_set specs[N_CSVS];
385 /* A stack of nestings, e.g. nest1 + nest2 + ... + nestN. */
388 struct ctables_nest *nests;
394 struct hmap_node node;
399 struct ctables_occurrence
401 struct hmap_node node;
405 struct ctables_section
407 struct ctables_table *table;
408 struct ctables_nest *nests[PIVOT_N_AXES];
409 struct hmap *occurrences[PIVOT_N_AXES];
410 struct hmap cells; /* Contains "struct ctable_cell"s. */
411 struct hmap domains[N_CTDTS]; /* Contains "struct ctable_domain"s. */
416 struct ctables *ctables;
417 struct ctables_axis *axes[PIVOT_N_AXES];
418 struct ctables_stack stacks[PIVOT_N_AXES];
419 struct ctables_section *sections;
421 enum pivot_axis_type summary_axis;
422 struct ctables_summary_spec_set summary_specs;
424 const struct variable *clabels_example;
425 struct hmap clabels_values_map;
426 struct ctables_value **clabels_values;
427 size_t n_clabels_values;
429 enum pivot_axis_type slabels_axis;
430 bool slabels_visible;
432 /* The innermost category labels for axis 'a' appear on axis label_axis[a].
434 Most commonly, label_axis[a] == a, and in particular we always have
435 label_axis{PIVOT_AXIS_LAYER] == PIVOT_AXIS_LAYER.
437 If ROWLABELS or COLLABELS is specified, then one of
438 label_axis[PIVOT_AXIS_ROW] or label_axis[PIVOT_AXIS_COLUMN] can be the
439 opposite axis or PIVOT_AXIS_LAYER. Only one of them will differ.
441 enum pivot_axis_type label_axis[PIVOT_N_AXES];
442 enum pivot_axis_type clabels_from_axis;
444 /* Indexed by variable dictionary index. */
445 struct ctables_categories **categories;
454 struct ctables_chisq *chisq;
455 struct ctables_pairwise *pairwise;
458 struct ctables_categories
461 struct ctables_category *cats;
466 struct ctables_category
468 enum ctables_category_type
470 /* Explicit category lists. */
473 CCT_NRANGE, /* Numerical range. */
474 CCT_SRANGE, /* String range. */
479 /* Totals and subtotals. */
483 /* Implicit category lists. */
488 /* For contributing to TOTALN. */
489 CCT_EXCLUDED_MISSING,
493 struct ctables_category *subtotal;
499 double number; /* CCT_NUMBER. */
500 struct substring string; /* CCT_STRING, in dictionary encoding. */
501 double nrange[2]; /* CCT_NRANGE. */
502 struct substring srange[2]; /* CCT_SRANGE. */
506 char *total_label; /* CCT_SUBTOTAL, CCT_TOTAL. */
507 bool hide_subcategories; /* CCT_SUBTOTAL. */
510 const struct ctables_postcompute *pc; /* CCT_POSTCOMPUTE. */
512 /* CCT_VALUE, CCT_LABEL, CCT_FUNCTION. */
515 bool include_missing;
519 enum ctables_summary_function sort_function;
520 struct variable *sort_var;
525 /* Source location. This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
526 CCT_FUNCTION, CCT_EXCLUDED_MISSING. */
527 struct msg_location *location;
531 ctables_category_uninit (struct ctables_category *cat)
542 case CCT_POSTCOMPUTE:
546 ss_dealloc (&cat->string);
550 ss_dealloc (&cat->srange[0]);
551 ss_dealloc (&cat->srange[1]);
556 free (cat->total_label);
564 case CCT_EXCLUDED_MISSING:
570 nullable_substring_equal (const struct substring *a,
571 const struct substring *b)
573 return !a->string ? !b->string : b->string && ss_equals (*a, *b);
577 ctables_category_equal (const struct ctables_category *a,
578 const struct ctables_category *b)
580 if (a->type != b->type)
586 return a->number == b->number;
589 return ss_equals (a->string, b->string);
592 return a->nrange[0] == b->nrange[0] && a->nrange[1] == b->nrange[1];
595 return (nullable_substring_equal (&a->srange[0], &b->srange[0])
596 && nullable_substring_equal (&a->srange[1], &b->srange[1]));
602 case CCT_POSTCOMPUTE:
603 return a->pc == b->pc;
607 return !strcmp (a->total_label, b->total_label);
612 return (a->include_missing == b->include_missing
613 && a->sort_ascending == b->sort_ascending
614 && a->sort_function == b->sort_function
615 && a->sort_var == b->sort_var
616 && a->percentile == b->percentile);
618 case CCT_EXCLUDED_MISSING:
626 ctables_categories_unref (struct ctables_categories *c)
631 assert (c->n_refs > 0);
635 for (size_t i = 0; i < c->n_cats; i++)
636 ctables_category_uninit (&c->cats[i]);
642 ctables_categories_equal (const struct ctables_categories *a,
643 const struct ctables_categories *b)
645 if (a->n_cats != b->n_cats || a->show_empty != b->show_empty)
648 for (size_t i = 0; i < a->n_cats; i++)
649 if (!ctables_category_equal (&a->cats[i], &b->cats[i]))
655 /* Chi-square test (SIGTEST). */
663 /* Pairwise comparison test (COMPARETEST). */
664 struct ctables_pairwise
666 enum { PROP, MEAN } type;
669 bool meansvariance_allcats;
671 enum { BONFERRONI = 1, BH } adjust;
695 struct variable *var;
697 struct ctables_summary_spec_set specs[N_CSVS];
701 struct ctables_axis *subs[2];
704 struct msg_location *loc;
707 static void ctables_axis_destroy (struct ctables_axis *);
716 enum ctables_function_availability
718 CTFA_ALL, /* Any variables. */
719 CTFA_SCALE, /* Only scale variables, totals, and subtotals. */
720 CTFA_MRSETS, /* Only multiple-response sets */
723 struct ctables_summary_spec
725 enum ctables_summary_function function;
726 double percentile; /* CTSF_PTILE only. */
729 struct fmt_spec format;
730 bool is_ctables_format; /* Is 'format' one of CTEF_*? */
736 ctables_summary_spec_clone (struct ctables_summary_spec *dst,
737 const struct ctables_summary_spec *src)
740 dst->label = xstrdup_if_nonnull (src->label);
744 ctables_summary_spec_uninit (struct ctables_summary_spec *s)
751 ctables_summary_spec_set_clone (struct ctables_summary_spec_set *dst,
752 const struct ctables_summary_spec_set *src)
754 struct ctables_summary_spec *specs = xnmalloc (src->n, sizeof *specs);
755 for (size_t i = 0; i < src->n; i++)
756 ctables_summary_spec_clone (&specs[i], &src->specs[i]);
758 *dst = (struct ctables_summary_spec_set) {
763 .is_scale = src->is_scale,
768 ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *set)
770 for (size_t i = 0; i < set->n; i++)
771 ctables_summary_spec_uninit (&set->specs[i]);
776 parse_col_width (struct lexer *lexer, const char *name, double *width)
778 lex_match (lexer, T_EQUALS);
779 if (lex_match_id (lexer, "DEFAULT"))
781 else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
783 *width = lex_number (lexer);
793 parse_bool (struct lexer *lexer, bool *b)
795 if (lex_match_id (lexer, "NO"))
797 else if (lex_match_id (lexer, "YES"))
801 lex_error_expecting (lexer, "YES", "NO");
807 static enum ctables_function_availability
808 ctables_function_availability (enum ctables_summary_function f)
810 static enum ctables_function_availability availability[] = {
811 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
816 return availability[f];
820 ctables_summary_function_is_count (enum ctables_summary_function f)
826 case CTSF_ROWPCT_COUNT:
827 case CTSF_COLPCT_COUNT:
828 case CTSF_TABLEPCT_COUNT:
829 case CTSF_SUBTABLEPCT_COUNT:
830 case CTSF_LAYERPCT_COUNT:
831 case CTSF_LAYERROWPCT_COUNT:
832 case CTSF_LAYERCOLPCT_COUNT:
835 case CTSF_ROWPCT_VALIDN:
836 case CTSF_COLPCT_VALIDN:
837 case CTSF_TABLEPCT_VALIDN:
838 case CTSF_SUBTABLEPCT_VALIDN:
839 case CTSF_LAYERPCT_VALIDN:
840 case CTSF_LAYERROWPCT_VALIDN:
841 case CTSF_LAYERCOLPCT_VALIDN:
842 case CTSF_ROWPCT_TOTALN:
843 case CTSF_COLPCT_TOTALN:
844 case CTSF_TABLEPCT_TOTALN:
845 case CTSF_SUBTABLEPCT_TOTALN:
846 case CTSF_LAYERPCT_TOTALN:
847 case CTSF_LAYERROWPCT_TOTALN:
848 case CTSF_LAYERCOLPCT_TOTALN:
865 case CTSF_ROWPCT_SUM:
866 case CTSF_COLPCT_SUM:
867 case CTSF_TABLEPCT_SUM:
868 case CTSF_SUBTABLEPCT_SUM:
869 case CTSF_LAYERPCT_SUM:
870 case CTSF_LAYERROWPCT_SUM:
871 case CTSF_LAYERCOLPCT_SUM:
879 parse_ctables_summary_function (struct lexer *lexer,
880 enum ctables_summary_function *f)
884 enum ctables_summary_function function;
885 struct substring name;
887 static struct pair names[] = {
888 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
889 { ENUM, SS_LITERAL_INITIALIZER (NAME) },
892 /* The .COUNT suffix may be omitted. */
893 S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
894 S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
895 S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
896 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
897 S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
898 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
899 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
903 if (!lex_force_id (lexer))
906 for (size_t i = 0; i < sizeof names / sizeof *names; i++)
907 if (ss_equals_case (names[i].name, lex_tokss (lexer)))
909 *f = names[i].function;
914 lex_error (lexer, _("Expecting summary function name."));
919 ctables_axis_destroy (struct ctables_axis *axis)
927 for (size_t i = 0; i < N_CSVS; i++)
928 ctables_summary_spec_set_uninit (&axis->specs[i]);
933 ctables_axis_destroy (axis->subs[0]);
934 ctables_axis_destroy (axis->subs[1]);
937 msg_location_destroy (axis->loc);
941 static struct ctables_axis *
942 ctables_axis_new_nonterminal (enum ctables_axis_op op,
943 struct ctables_axis *sub0,
944 struct ctables_axis *sub1,
945 struct lexer *lexer, int start_ofs)
947 struct ctables_axis *axis = xmalloc (sizeof *axis);
948 *axis = (struct ctables_axis) {
950 .subs = { sub0, sub1 },
951 .loc = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
956 struct ctables_axis_parse_ctx
959 struct dictionary *dict;
961 struct ctables_table *t;
964 static struct fmt_spec
965 ctables_summary_default_format (enum ctables_summary_function function,
966 const struct variable *var)
968 static const enum ctables_format default_formats[] = {
969 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
973 switch (default_formats[function])
976 return (struct fmt_spec) { .type = FMT_F, .w = 40 };
979 return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
982 return *var_get_print_format (var);
990 ctables_summary_default_label (enum ctables_summary_function function,
993 static const char *default_labels[] = {
994 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
999 return (function == CTSF_PTILE
1000 ? xasprintf (_("Percentile %.2f"), percentile)
1001 : xstrdup (gettext (default_labels[function])));
1005 ctables_summary_function_name (enum ctables_summary_function function)
1007 static const char *names[] = {
1008 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = NAME,
1012 return names[function];
1016 add_summary_spec (struct ctables_axis *axis,
1017 enum ctables_summary_function function, double percentile,
1018 const char *label, const struct fmt_spec *format,
1019 bool is_ctables_format, const struct msg_location *loc,
1020 enum ctables_summary_variant sv)
1022 if (axis->op == CTAO_VAR)
1024 const char *function_name = ctables_summary_function_name (function);
1025 const char *var_name = var_get_name (axis->var);
1026 switch (ctables_function_availability (function))
1029 msg_at (SE, loc, _("Summary function %s applies only to multiple "
1030 "response sets."), function_name);
1031 msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
1040 _("Summary function %s applies only to scale variables."),
1042 msg_at (SN, axis->loc, _("'%s' is not a scale variable."),
1053 struct ctables_summary_spec_set *set = &axis->specs[sv];
1054 if (set->n >= set->allocated)
1055 set->specs = x2nrealloc (set->specs, &set->allocated,
1056 sizeof *set->specs);
1058 struct ctables_summary_spec *dst = &set->specs[set->n++];
1059 *dst = (struct ctables_summary_spec) {
1060 .function = function,
1061 .percentile = percentile,
1062 .label = xstrdup (label),
1063 .format = (format ? *format
1064 : ctables_summary_default_format (function, axis->var)),
1065 .is_ctables_format = is_ctables_format,
1071 for (size_t i = 0; i < 2; i++)
1072 if (!add_summary_spec (axis->subs[i], function, percentile, label,
1073 format, is_ctables_format, loc, sv))
1079 static struct ctables_axis *ctables_axis_parse_stack (
1080 struct ctables_axis_parse_ctx *);
1083 static struct ctables_axis *
1084 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
1086 if (lex_match (ctx->lexer, T_LPAREN))
1088 struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
1089 if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
1091 ctables_axis_destroy (sub);
1097 if (!lex_force_id (ctx->lexer))
1100 int start_ofs = lex_ofs (ctx->lexer);
1101 struct variable *var = parse_variable (ctx->lexer, ctx->dict);
1105 struct ctables_axis *axis = xmalloc (sizeof *axis);
1106 *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
1108 /* XXX should figure out default measures by reading data */
1109 axis->scale = (lex_match_phrase (ctx->lexer, "[S]") ? true
1110 : lex_match_phrase (ctx->lexer, "[C]") ? false
1111 : var_get_measure (var) == MEASURE_SCALE);
1112 axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
1113 lex_ofs (ctx->lexer) - 1);
1114 if (axis->scale && var_is_alpha (var))
1116 msg_at (SE, axis->loc, _("Cannot use string variable %s as a scale "
1118 var_get_name (var));
1119 ctables_axis_destroy (axis);
1127 has_digit (const char *s)
1129 return s[strcspn (s, "0123456789")] != '\0';
1133 parse_ctables_format_specifier (struct lexer *lexer, struct fmt_spec *format,
1134 bool *is_ctables_format)
1136 char type[FMT_TYPE_LEN_MAX + 1];
1137 if (!parse_abstract_format_specifier__ (lexer, type, &format->w, &format->d))
1140 if (!strcasecmp (type, "NEGPAREN"))
1141 format->type = CTEF_NEGPAREN;
1142 else if (!strcasecmp (type, "NEQUAL"))
1143 format->type = CTEF_NEQUAL;
1144 else if (!strcasecmp (type, "PAREN"))
1145 format->type = CTEF_PAREN;
1146 else if (!strcasecmp (type, "PCTPAREN"))
1147 format->type = CTEF_PCTPAREN;
1150 *is_ctables_format = false;
1151 return (parse_format_specifier (lexer, format)
1152 && fmt_check_output (format)
1153 && fmt_check_type_compat (format, VAL_NUMERIC));
1158 msg (SE, _("Output format %s requires width 2 or greater."), type);
1161 else if (format->d > format->w - 1)
1163 msg (SE, _("Output format %s requires width greater than decimals."),
1169 *is_ctables_format = true;
1174 static struct ctables_axis *
1175 ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
1177 struct ctables_axis *sub = ctables_axis_parse_primary (ctx);
1178 if (!sub || !lex_match (ctx->lexer, T_LBRACK))
1181 enum ctables_summary_variant sv = CSV_CELL;
1184 int start_ofs = lex_ofs (ctx->lexer);
1186 /* Parse function. */
1187 enum ctables_summary_function function;
1188 if (!parse_ctables_summary_function (ctx->lexer, &function))
1191 /* Parse percentile. */
1192 double percentile = 0;
1193 if (function == CTSF_PTILE)
1195 if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
1197 percentile = lex_number (ctx->lexer);
1198 lex_get (ctx->lexer);
1203 if (lex_is_string (ctx->lexer))
1205 label = ss_xstrdup (lex_tokss (ctx->lexer));
1206 lex_get (ctx->lexer);
1209 label = ctables_summary_default_label (function, percentile);
1212 struct fmt_spec format;
1213 const struct fmt_spec *formatp;
1214 bool is_ctables_format = false;
1215 if (lex_token (ctx->lexer) == T_ID
1216 && has_digit (lex_tokcstr (ctx->lexer)))
1218 if (!parse_ctables_format_specifier (ctx->lexer, &format,
1219 &is_ctables_format))
1229 struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
1230 lex_ofs (ctx->lexer) - 1);
1231 add_summary_spec (sub, function, percentile, label, formatp,
1232 is_ctables_format, loc, sv);
1234 msg_location_destroy (loc);
1236 lex_match (ctx->lexer, T_COMMA);
1237 if (sv == CSV_CELL && lex_match_id (ctx->lexer, "TOTALS"))
1239 if (!lex_force_match (ctx->lexer, T_LBRACK))
1243 else if (lex_match (ctx->lexer, T_RBRACK))
1245 if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
1252 ctables_axis_destroy (sub);
1256 static const struct ctables_axis *
1257 find_scale (const struct ctables_axis *axis)
1261 else if (axis->op == CTAO_VAR)
1262 return axis->scale ? axis : NULL;
1265 for (size_t i = 0; i < 2; i++)
1267 const struct ctables_axis *scale = find_scale (axis->subs[i]);
1275 static const struct ctables_axis *
1276 find_categorical_summary_spec (const struct ctables_axis *axis)
1280 else if (axis->op == CTAO_VAR)
1281 return !axis->scale && axis->specs[CSV_CELL].n ? axis : NULL;
1284 for (size_t i = 0; i < 2; i++)
1286 const struct ctables_axis *sum
1287 = find_categorical_summary_spec (axis->subs[i]);
1295 static struct ctables_axis *
1296 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
1298 int start_ofs = lex_ofs (ctx->lexer);
1299 struct ctables_axis *lhs = ctables_axis_parse_postfix (ctx);
1303 while (lex_match (ctx->lexer, T_GT))
1305 struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx);
1309 struct ctables_axis *nest = ctables_axis_new_nonterminal (
1310 CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
1312 const struct ctables_axis *outer_scale = find_scale (lhs);
1313 const struct ctables_axis *inner_scale = find_scale (rhs);
1314 if (outer_scale && inner_scale)
1316 msg_at (SE, nest->loc, _("Cannot nest scale variables."));
1317 msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
1318 msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
1319 ctables_axis_destroy (nest);
1323 const struct ctables_axis *outer_sum = find_categorical_summary_spec (lhs);
1326 msg_at (SE, nest->loc,
1327 _("Summaries may only be requested for categorical variables "
1328 "at the innermost nesting level."));
1329 msg_at (SN, outer_sum->loc,
1330 _("This outer categorical variable has a summary."));
1331 ctables_axis_destroy (nest);
1341 static struct ctables_axis *
1342 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
1344 int start_ofs = lex_ofs (ctx->lexer);
1345 struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
1349 while (lex_match (ctx->lexer, T_PLUS))
1351 struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
1355 lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
1356 ctx->lexer, start_ofs);
1363 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
1364 struct ctables *ct, struct ctables_table *t,
1365 enum pivot_axis_type a)
1367 if (lex_token (lexer) == T_BY
1368 || lex_token (lexer) == T_SLASH
1369 || lex_token (lexer) == T_ENDCMD)
1372 struct ctables_axis_parse_ctx ctx = {
1378 t->axes[a] = ctables_axis_parse_stack (&ctx);
1379 return t->axes[a] != NULL;
1383 ctables_chisq_destroy (struct ctables_chisq *chisq)
1389 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
1395 ctables_table_destroy (struct ctables_table *t)
1400 for (size_t i = 0; i < t->n_categories; i++)
1401 ctables_categories_unref (t->categories[i]);
1402 free (t->categories);
1404 ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
1405 ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
1406 ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
1410 ctables_chisq_destroy (t->chisq);
1411 ctables_pairwise_destroy (t->pairwise);
1416 ctables_destroy (struct ctables *ct)
1421 pivot_table_look_unref (ct->look);
1425 for (size_t i = 0; i < ct->n_tables; i++)
1426 ctables_table_destroy (ct->tables[i]);
1431 static struct ctables_category
1432 cct_nrange (double low, double high)
1434 return (struct ctables_category) {
1436 .nrange = { low, high }
1440 static struct ctables_category
1441 cct_srange (struct substring low, struct substring high)
1443 return (struct ctables_category) {
1445 .srange = { low, high }
1450 ctables_table_parse_subtotal (struct lexer *lexer, bool hide_subcategories,
1451 struct ctables_category *cat)
1454 if (lex_match (lexer, T_EQUALS))
1456 if (!lex_force_string (lexer))
1459 total_label = ss_xstrdup (lex_tokss (lexer));
1463 total_label = xstrdup (_("Subtotal"));
1465 *cat = (struct ctables_category) {
1466 .type = CCT_SUBTOTAL,
1467 .hide_subcategories = hide_subcategories,
1468 .total_label = total_label
1473 static struct substring
1474 parse_substring (struct lexer *lexer, struct dictionary *dict)
1476 struct substring s = recode_substring_pool (
1477 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
1478 ss_rtrim (&s, ss_cstr (" "));
1484 ctables_table_parse_explicit_category (struct lexer *lexer,
1485 struct dictionary *dict,
1487 struct ctables_category *cat)
1489 if (lex_match_id (lexer, "OTHERNM"))
1490 *cat = (struct ctables_category) { .type = CCT_OTHERNM };
1491 else if (lex_match_id (lexer, "MISSING"))
1492 *cat = (struct ctables_category) { .type = CCT_MISSING };
1493 else if (lex_match_id (lexer, "SUBTOTAL"))
1494 return ctables_table_parse_subtotal (lexer, false, cat);
1495 else if (lex_match_id (lexer, "HSUBTOTAL"))
1496 return ctables_table_parse_subtotal (lexer, true, cat);
1497 else if (lex_match_id (lexer, "LO"))
1499 if (!lex_force_match_id (lexer, "THRU"))
1501 if (lex_is_string (lexer))
1503 struct substring sr0 = { .string = NULL };
1504 struct substring sr1 = parse_substring (lexer, dict);
1505 *cat = cct_srange (sr0, sr1);
1507 else if (lex_force_num (lexer))
1509 *cat = cct_nrange (-DBL_MAX, lex_number (lexer));
1515 else if (lex_is_number (lexer))
1517 double number = lex_number (lexer);
1519 if (lex_match_id (lexer, "THRU"))
1521 if (lex_match_id (lexer, "HI"))
1522 *cat = cct_nrange (number, DBL_MAX);
1525 if (!lex_force_num (lexer))
1527 *cat = cct_nrange (number, lex_number (lexer));
1532 *cat = (struct ctables_category) {
1537 else if (lex_is_string (lexer))
1539 struct substring s = parse_substring (lexer, dict);
1540 if (lex_match_id (lexer, "THRU"))
1542 if (lex_match_id (lexer, "HI"))
1544 struct substring sr1 = { .string = NULL };
1545 *cat = cct_srange (s, sr1);
1549 if (!lex_force_string (lexer))
1551 struct substring sr1 = parse_substring (lexer, dict);
1552 *cat = cct_srange (s, sr1);
1556 *cat = (struct ctables_category) { .type = CCT_STRING, .string = s };
1558 else if (lex_match (lexer, T_AND))
1560 if (!lex_force_id (lexer))
1562 struct ctables_postcompute *pc = ctables_find_postcompute (
1563 ct, lex_tokcstr (lexer));
1566 struct msg_location *loc = lex_get_location (lexer, -1, 0);
1567 msg_at (SE, loc, _("Unknown postcompute &%s."),
1568 lex_tokcstr (lexer));
1569 msg_location_destroy (loc);
1574 *cat = (struct ctables_category) { .type = CCT_POSTCOMPUTE, .pc = pc };
1578 lex_error (lexer, NULL);
1585 static struct ctables_category *
1586 ctables_find_category_for_postcompute (const struct ctables_categories *cats,
1587 const struct ctables_pcexpr *e)
1589 struct ctables_category *best = NULL;
1590 size_t n_subtotals = 0;
1591 for (size_t i = 0; i < cats->n_cats; i++)
1593 struct ctables_category *cat = &cats->cats[i];
1596 case CTPO_CAT_NUMBER:
1597 if (cat->type == CCT_NUMBER && cat->number == e->number)
1601 case CTPO_CAT_STRING:
1602 if (cat->type == CCT_STRING && ss_equals (cat->string, e->string))
1606 case CTPO_CAT_NRANGE:
1607 if (cat->type == CCT_NRANGE
1608 && cat->nrange[0] == e->nrange[0]
1609 && cat->nrange[1] == e->nrange[1])
1613 case CTPO_CAT_SRANGE:
1614 if (cat->type == CCT_SRANGE
1615 && nullable_substring_equal (&cat->srange[0], &e->srange[0])
1616 && nullable_substring_equal (&cat->srange[1], &e->srange[1]))
1620 case CTPO_CAT_MISSING:
1621 if (cat->type == CCT_MISSING)
1625 case CTPO_CAT_OTHERNM:
1626 if (cat->type == CCT_OTHERNM)
1630 case CTPO_CAT_SUBTOTAL:
1631 if (cat->type == CCT_SUBTOTAL)
1634 if (e->subtotal_index == n_subtotals)
1636 else if (e->subtotal_index == 0)
1641 case CTPO_CAT_TOTAL:
1642 if (cat->type == CCT_TOTAL)
1656 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0 && n_subtotals > 1)
1662 ctables_recursive_check_postcompute (const struct ctables_pcexpr *e,
1663 struct ctables_category *pc_cat,
1664 const struct ctables_categories *cats,
1665 const struct msg_location *cats_location)
1669 case CTPO_CAT_NUMBER:
1670 case CTPO_CAT_STRING:
1671 case CTPO_CAT_NRANGE:
1672 case CTPO_CAT_MISSING:
1673 case CTPO_CAT_OTHERNM:
1674 case CTPO_CAT_SUBTOTAL:
1675 case CTPO_CAT_TOTAL:
1677 struct ctables_category *cat = ctables_find_category_for_postcompute (
1681 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0)
1683 size_t n_subtotals = 0;
1684 for (size_t i = 0; i < cats->n_cats; i++)
1685 n_subtotals += cats->cats[i].type == CCT_SUBTOTAL;
1686 if (n_subtotals > 1)
1688 msg_at (SE, cats_location,
1689 ngettext ("These categories include %zu instance "
1690 "of SUBTOTAL or HSUBTOTAL, so references "
1691 "from computed categories must refer to "
1692 "subtotals by position.",
1693 "These categories include %zu instances "
1694 "of SUBTOTAL or HSUBTOTAL, so references "
1695 "from computed categories must refer to "
1696 "subtotals by position.",
1699 msg_at (SN, e->location,
1700 _("This is the reference that lacks a position."));
1705 msg_at (SE, pc_cat->location,
1706 _("Computed category &%s references a category not included "
1707 "in the category list."),
1709 msg_at (SN, e->location, _("This is the missing category."));
1710 msg_at (SN, cats_location,
1711 _("To fix the problem, add the missing category to the "
1712 "list of categories here."));
1715 if (pc_cat->pc->hide_source_cats)
1729 for (size_t i = 0; i < 2; i++)
1730 if (e->subs[i] && !ctables_recursive_check_postcompute (
1731 e->subs[i], pc_cat, cats, cats_location))
1741 parse_category_string (const struct ctables_category *cat,
1742 struct substring s, struct dictionary *dict,
1743 enum fmt_type format, double *n)
1746 char *error = data_in (s, dict_get_encoding (dict), format,
1747 settings_get_fmt_settings (), &v, 0, NULL);
1750 msg_at (SE, cat->location,
1751 _("Failed to parse category specification as format %s: %s."),
1752 fmt_name (format), error);
1762 all_strings (struct variable **vars, size_t n_vars,
1763 const struct ctables_category *cat)
1765 for (size_t j = 0; j < n_vars; j++)
1766 if (var_is_numeric (vars[j]))
1768 msg_at (SE, cat->location,
1769 _("This category specification may be applied only to string "
1770 "variables, but this subcommand tries to apply it to "
1771 "numeric variable %s."),
1772 var_get_name (vars[j]));
1779 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
1780 struct ctables *ct, struct ctables_table *t)
1782 if (!lex_match_id (lexer, "VARIABLES"))
1784 lex_match (lexer, T_EQUALS);
1786 struct variable **vars;
1788 if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
1791 const struct fmt_spec *common_format = var_get_print_format (vars[0]);
1792 for (size_t i = 1; i < n_vars; i++)
1794 const struct fmt_spec *f = var_get_print_format (vars[i]);
1795 if (f->type != common_format->type)
1797 common_format = NULL;
1803 && (fmt_get_category (common_format->type)
1804 & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)));
1806 struct ctables_categories *c = xmalloc (sizeof *c);
1807 *c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
1808 for (size_t i = 0; i < n_vars; i++)
1810 struct ctables_categories **cp
1811 = &t->categories[var_get_dict_index (vars[i])];
1812 ctables_categories_unref (*cp);
1816 size_t allocated_cats = 0;
1817 if (lex_match (lexer, T_LBRACK))
1819 int cats_start_ofs = lex_ofs (lexer);
1822 if (c->n_cats >= allocated_cats)
1823 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
1825 int start_ofs = lex_ofs (lexer);
1826 struct ctables_category *cat = &c->cats[c->n_cats];
1827 if (!ctables_table_parse_explicit_category (lexer, dict, ct, cat))
1829 cat->location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
1832 lex_match (lexer, T_COMMA);
1834 while (!lex_match (lexer, T_RBRACK));
1836 struct msg_location *cats_location
1837 = lex_ofs_location (lexer, cats_start_ofs, lex_ofs (lexer) - 1);
1838 for (size_t i = 0; i < c->n_cats; i++)
1840 struct ctables_category *cat = &c->cats[i];
1843 case CCT_POSTCOMPUTE:
1844 if (!ctables_recursive_check_postcompute (cat->pc->expr, cat,
1851 for (size_t j = 0; j < n_vars; j++)
1852 if (var_is_alpha (vars[j]))
1854 msg_at (SE, cat->location,
1855 _("This category specification may be applied "
1856 "only to numeric variables, but this "
1857 "subcommand tries to apply it to string "
1859 var_get_name (vars[j]));
1868 if (!parse_category_string (cat, cat->string, dict,
1869 common_format->type, &n))
1872 ss_dealloc (&cat->string);
1874 cat->type = CCT_NUMBER;
1877 else if (!all_strings (vars, n_vars, cat))
1886 if (!cat->srange[0].string)
1888 else if (!parse_category_string (cat, cat->srange[0], dict,
1889 common_format->type, &n[0]))
1892 if (!cat->srange[1].string)
1894 else if (!parse_category_string (cat, cat->srange[1], dict,
1895 common_format->type, &n[1]))
1898 ss_dealloc (&cat->srange[0]);
1899 ss_dealloc (&cat->srange[1]);
1901 cat->type = CCT_NRANGE;
1902 cat->nrange[0] = n[0];
1903 cat->nrange[1] = n[1];
1905 else if (!all_strings (vars, n_vars, cat))
1916 case CCT_EXCLUDED_MISSING:
1922 struct ctables_category cat = {
1924 .include_missing = false,
1925 .sort_ascending = true,
1927 bool show_totals = false;
1928 char *total_label = NULL;
1929 bool totals_before = false;
1930 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
1932 if (!c->n_cats && lex_match_id (lexer, "ORDER"))
1934 lex_match (lexer, T_EQUALS);
1935 if (lex_match_id (lexer, "A"))
1936 cat.sort_ascending = true;
1937 else if (lex_match_id (lexer, "D"))
1938 cat.sort_ascending = false;
1941 lex_error_expecting (lexer, "A", "D");
1945 else if (!c->n_cats && lex_match_id (lexer, "KEY"))
1947 lex_match (lexer, T_EQUALS);
1948 if (lex_match_id (lexer, "VALUE"))
1949 cat.type = CCT_VALUE;
1950 else if (lex_match_id (lexer, "LABEL"))
1951 cat.type = CCT_LABEL;
1954 cat.type = CCT_FUNCTION;
1955 if (!parse_ctables_summary_function (lexer, &cat.sort_function))
1958 if (lex_match (lexer, T_LPAREN))
1960 cat.sort_var = parse_variable (lexer, dict);
1964 if (cat.sort_function == CTSF_PTILE)
1966 lex_match (lexer, T_COMMA);
1967 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
1969 cat.percentile = lex_number (lexer);
1973 if (!lex_force_match (lexer, T_RPAREN))
1976 else if (ctables_function_availability (cat.sort_function)
1979 bool UNUSED b = lex_force_match (lexer, T_LPAREN);
1984 else if (!c->n_cats && lex_match_id (lexer, "MISSING"))
1986 lex_match (lexer, T_EQUALS);
1987 if (lex_match_id (lexer, "INCLUDE"))
1988 cat.include_missing = true;
1989 else if (lex_match_id (lexer, "EXCLUDE"))
1990 cat.include_missing = false;
1993 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1997 else if (lex_match_id (lexer, "TOTAL"))
1999 lex_match (lexer, T_EQUALS);
2000 if (!parse_bool (lexer, &show_totals))
2003 else if (lex_match_id (lexer, "LABEL"))
2005 lex_match (lexer, T_EQUALS);
2006 if (!lex_force_string (lexer))
2009 total_label = ss_xstrdup (lex_tokss (lexer));
2012 else if (lex_match_id (lexer, "POSITION"))
2014 lex_match (lexer, T_EQUALS);
2015 if (lex_match_id (lexer, "BEFORE"))
2016 totals_before = true;
2017 else if (lex_match_id (lexer, "AFTER"))
2018 totals_before = false;
2021 lex_error_expecting (lexer, "BEFORE", "AFTER");
2025 else if (lex_match_id (lexer, "EMPTY"))
2027 lex_match (lexer, T_EQUALS);
2028 if (lex_match_id (lexer, "INCLUDE"))
2029 c->show_empty = true;
2030 else if (lex_match_id (lexer, "EXCLUDE"))
2031 c->show_empty = false;
2034 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
2041 lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
2042 "TOTAL", "LABEL", "POSITION", "EMPTY");
2044 lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
2051 if (c->n_cats >= allocated_cats)
2052 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2053 c->cats[c->n_cats++] = cat;
2058 if (c->n_cats >= allocated_cats)
2059 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2061 struct ctables_category *totals;
2064 insert_element (c->cats, c->n_cats, sizeof *c->cats, 0);
2065 totals = &c->cats[0];
2068 totals = &c->cats[c->n_cats];
2071 *totals = (struct ctables_category) {
2073 .total_label = total_label ? total_label : xstrdup (_("Total")),
2077 struct ctables_category *subtotal = NULL;
2078 for (size_t i = totals_before ? 0 : c->n_cats;
2079 totals_before ? i < c->n_cats : i-- > 0;
2080 totals_before ? i++ : 0)
2082 struct ctables_category *cat = &c->cats[i];
2091 cat->subtotal = subtotal;
2094 case CCT_POSTCOMPUTE:
2105 case CCT_EXCLUDED_MISSING:
2114 ctables_nest_uninit (struct ctables_nest *nest)
2121 ctables_stack_uninit (struct ctables_stack *stack)
2125 for (size_t i = 0; i < stack->n; i++)
2126 ctables_nest_uninit (&stack->nests[i]);
2127 free (stack->nests);
2131 static struct ctables_stack
2132 nest_fts (struct ctables_stack s0, struct ctables_stack s1)
2139 struct ctables_stack stack = { .nests = xnmalloc (s0.n, s1.n * sizeof *stack.nests) };
2140 for (size_t i = 0; i < s0.n; i++)
2141 for (size_t j = 0; j < s1.n; j++)
2143 const struct ctables_nest *a = &s0.nests[i];
2144 const struct ctables_nest *b = &s1.nests[j];
2146 size_t allocate = a->n + b->n;
2147 struct variable **vars = xnmalloc (allocate, sizeof *vars);
2148 enum pivot_axis_type *axes = xnmalloc (allocate, sizeof *axes);
2150 for (size_t k = 0; k < a->n; k++)
2151 vars[n++] = a->vars[k];
2152 for (size_t k = 0; k < b->n; k++)
2153 vars[n++] = b->vars[k];
2154 assert (n == allocate);
2156 const struct ctables_nest *summary_src;
2157 if (!a->specs[CSV_CELL].var)
2159 else if (!b->specs[CSV_CELL].var)
2164 struct ctables_nest *new = &stack.nests[stack.n++];
2165 *new = (struct ctables_nest) {
2167 .scale_idx = (a->scale_idx != SIZE_MAX ? a->scale_idx
2168 : b->scale_idx != SIZE_MAX ? a->n + b->scale_idx
2172 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2173 ctables_summary_spec_set_clone (&new->specs[sv], &summary_src->specs[sv]);
2175 ctables_stack_uninit (&s0);
2176 ctables_stack_uninit (&s1);
2180 static struct ctables_stack
2181 stack_fts (struct ctables_stack s0, struct ctables_stack s1)
2183 struct ctables_stack stack = { .nests = xnmalloc (s0.n + s1.n, sizeof *stack.nests) };
2184 for (size_t i = 0; i < s0.n; i++)
2185 stack.nests[stack.n++] = s0.nests[i];
2186 for (size_t i = 0; i < s1.n; i++)
2188 stack.nests[stack.n] = s1.nests[i];
2189 stack.nests[stack.n].group_head += s0.n;
2192 assert (stack.n == s0.n + s1.n);
2198 static struct ctables_stack
2199 var_fts (const struct ctables_axis *a)
2201 struct variable **vars = xmalloc (sizeof *vars);
2204 struct ctables_nest *nest = xmalloc (sizeof *nest);
2205 *nest = (struct ctables_nest) {
2208 .scale_idx = a->scale ? 0 : SIZE_MAX,
2210 if (a->specs[CSV_CELL].n || a->scale)
2211 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2213 ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
2214 nest->specs[sv].var = a->var;
2215 nest->specs[sv].is_scale = a->scale;
2217 return (struct ctables_stack) { .nests = nest, .n = 1 };
2220 static struct ctables_stack
2221 enumerate_fts (enum pivot_axis_type axis_type, const struct ctables_axis *a)
2224 return (struct ctables_stack) { .n = 0 };
2232 return stack_fts (enumerate_fts (axis_type, a->subs[0]),
2233 enumerate_fts (axis_type, a->subs[1]));
2236 /* This should consider any of the scale variables found in the result to
2237 be linked to each other listwise for SMISSING=LISTWISE. */
2238 return nest_fts (enumerate_fts (axis_type, a->subs[0]),
2239 enumerate_fts (axis_type, a->subs[1]));
2245 union ctables_summary
2247 /* COUNT, VALIDN, TOTALN. */
2250 /* MINIMUM, MAXIMUM, RANGE. */
2257 /* MEAN, SEMEAN, STDDEV, SUM, VARIANCE, *.SUM. */
2258 struct moments1 *moments;
2260 /* MEDIAN, MODE, PTILE. */
2263 struct casewriter *writer;
2268 /* XXX multiple response */
2272 ctables_summary_init (union ctables_summary *s,
2273 const struct ctables_summary_spec *ss)
2275 switch (ss->function)
2279 case CTSF_ROWPCT_COUNT:
2280 case CTSF_COLPCT_COUNT:
2281 case CTSF_TABLEPCT_COUNT:
2282 case CTSF_SUBTABLEPCT_COUNT:
2283 case CTSF_LAYERPCT_COUNT:
2284 case CTSF_LAYERROWPCT_COUNT:
2285 case CTSF_LAYERCOLPCT_COUNT:
2286 case CTSF_ROWPCT_VALIDN:
2287 case CTSF_COLPCT_VALIDN:
2288 case CTSF_TABLEPCT_VALIDN:
2289 case CTSF_SUBTABLEPCT_VALIDN:
2290 case CTSF_LAYERPCT_VALIDN:
2291 case CTSF_LAYERROWPCT_VALIDN:
2292 case CTSF_LAYERCOLPCT_VALIDN:
2293 case CTSF_ROWPCT_TOTALN:
2294 case CTSF_COLPCT_TOTALN:
2295 case CTSF_TABLEPCT_TOTALN:
2296 case CTSF_SUBTABLEPCT_TOTALN:
2297 case CTSF_LAYERPCT_TOTALN:
2298 case CTSF_LAYERROWPCT_TOTALN:
2299 case CTSF_LAYERCOLPCT_TOTALN:
2311 s->min = s->max = SYSMIS;
2319 case CTSF_ROWPCT_SUM:
2320 case CTSF_COLPCT_SUM:
2321 case CTSF_TABLEPCT_SUM:
2322 case CTSF_SUBTABLEPCT_SUM:
2323 case CTSF_LAYERPCT_SUM:
2324 case CTSF_LAYERROWPCT_SUM:
2325 case CTSF_LAYERCOLPCT_SUM:
2326 s->moments = moments1_create (MOMENT_VARIANCE);
2333 struct caseproto *proto = caseproto_create ();
2334 proto = caseproto_add_width (proto, 0);
2335 proto = caseproto_add_width (proto, 0);
2337 struct subcase ordering;
2338 subcase_init (&ordering, 0, 0, SC_ASCEND);
2339 s->writer = sort_create_writer (&ordering, proto);
2340 subcase_uninit (&ordering);
2341 caseproto_unref (proto);
2351 ctables_summary_uninit (union ctables_summary *s,
2352 const struct ctables_summary_spec *ss)
2354 switch (ss->function)
2358 case CTSF_ROWPCT_COUNT:
2359 case CTSF_COLPCT_COUNT:
2360 case CTSF_TABLEPCT_COUNT:
2361 case CTSF_SUBTABLEPCT_COUNT:
2362 case CTSF_LAYERPCT_COUNT:
2363 case CTSF_LAYERROWPCT_COUNT:
2364 case CTSF_LAYERCOLPCT_COUNT:
2365 case CTSF_ROWPCT_VALIDN:
2366 case CTSF_COLPCT_VALIDN:
2367 case CTSF_TABLEPCT_VALIDN:
2368 case CTSF_SUBTABLEPCT_VALIDN:
2369 case CTSF_LAYERPCT_VALIDN:
2370 case CTSF_LAYERROWPCT_VALIDN:
2371 case CTSF_LAYERCOLPCT_VALIDN:
2372 case CTSF_ROWPCT_TOTALN:
2373 case CTSF_COLPCT_TOTALN:
2374 case CTSF_TABLEPCT_TOTALN:
2375 case CTSF_SUBTABLEPCT_TOTALN:
2376 case CTSF_LAYERPCT_TOTALN:
2377 case CTSF_LAYERROWPCT_TOTALN:
2378 case CTSF_LAYERCOLPCT_TOTALN:
2396 case CTSF_ROWPCT_SUM:
2397 case CTSF_COLPCT_SUM:
2398 case CTSF_TABLEPCT_SUM:
2399 case CTSF_SUBTABLEPCT_SUM:
2400 case CTSF_LAYERPCT_SUM:
2401 case CTSF_LAYERROWPCT_SUM:
2402 case CTSF_LAYERCOLPCT_SUM:
2403 moments1_destroy (s->moments);
2409 casewriter_destroy (s->writer);
2415 ctables_summary_add (union ctables_summary *s,
2416 const struct ctables_summary_spec *ss,
2417 const struct variable *var, const union value *value,
2418 bool is_scale, bool is_scale_missing,
2419 bool is_missing, bool excluded_missing,
2420 double d_weight, double e_weight)
2422 /* To determine whether a case is included in a given table for a particular
2423 kind of summary, consider the following charts for each variable in the
2424 table. Only if "yes" appears for every variable for the summary is the
2427 Categorical variables: VALIDN COUNT TOTALN
2428 Valid values in included categories yes yes yes
2429 Missing values in included categories --- yes yes
2430 Missing values in excluded categories --- --- yes
2431 Valid values in excluded categories --- --- ---
2433 Scale variables: VALIDN COUNT TOTALN
2434 Valid value yes yes yes
2435 Missing value --- yes yes
2437 Missing values include both user- and system-missing. (The system-missing
2438 value is always in an excluded category.)
2440 switch (ss->function)
2443 case CTSF_ROWPCT_TOTALN:
2444 case CTSF_COLPCT_TOTALN:
2445 case CTSF_TABLEPCT_TOTALN:
2446 case CTSF_SUBTABLEPCT_TOTALN:
2447 case CTSF_LAYERPCT_TOTALN:
2448 case CTSF_LAYERROWPCT_TOTALN:
2449 case CTSF_LAYERCOLPCT_TOTALN:
2450 s->count += d_weight;
2454 case CTSF_ROWPCT_COUNT:
2455 case CTSF_COLPCT_COUNT:
2456 case CTSF_TABLEPCT_COUNT:
2457 case CTSF_SUBTABLEPCT_COUNT:
2458 case CTSF_LAYERPCT_COUNT:
2459 case CTSF_LAYERROWPCT_COUNT:
2460 case CTSF_LAYERCOLPCT_COUNT:
2461 if (is_scale || !excluded_missing)
2462 s->count += d_weight;
2466 case CTSF_ROWPCT_VALIDN:
2467 case CTSF_COLPCT_VALIDN:
2468 case CTSF_TABLEPCT_VALIDN:
2469 case CTSF_SUBTABLEPCT_VALIDN:
2470 case CTSF_LAYERPCT_VALIDN:
2471 case CTSF_LAYERROWPCT_VALIDN:
2472 case CTSF_LAYERCOLPCT_VALIDN:
2476 s->count += d_weight;
2481 s->count += d_weight;
2485 if (is_scale || !excluded_missing)
2486 s->count += e_weight;
2493 s->count += e_weight;
2497 s->count += e_weight;
2503 if (!is_scale_missing)
2505 assert (!var_is_alpha (var)); /* XXX? */
2506 if (s->min == SYSMIS || value->f < s->min)
2508 if (s->max == SYSMIS || value->f > s->max)
2518 case CTSF_ROWPCT_SUM:
2519 case CTSF_COLPCT_SUM:
2520 case CTSF_TABLEPCT_SUM:
2521 case CTSF_SUBTABLEPCT_SUM:
2522 case CTSF_LAYERPCT_SUM:
2523 case CTSF_LAYERROWPCT_SUM:
2524 case CTSF_LAYERCOLPCT_SUM:
2525 if (!is_scale_missing)
2526 moments1_add (s->moments, value->f, e_weight);
2532 if (!is_scale_missing)
2534 s->ovalid += e_weight;
2536 struct ccase *c = case_create (casewriter_get_proto (s->writer));
2537 *case_num_rw_idx (c, 0) = value->f;
2538 *case_num_rw_idx (c, 1) = e_weight;
2539 casewriter_write (s->writer, c);
2545 static enum ctables_domain_type
2546 ctables_function_domain (enum ctables_summary_function function)
2570 case CTSF_COLPCT_COUNT:
2571 case CTSF_COLPCT_SUM:
2572 case CTSF_COLPCT_TOTALN:
2573 case CTSF_COLPCT_VALIDN:
2576 case CTSF_LAYERCOLPCT_COUNT:
2577 case CTSF_LAYERCOLPCT_SUM:
2578 case CTSF_LAYERCOLPCT_TOTALN:
2579 case CTSF_LAYERCOLPCT_VALIDN:
2580 return CTDT_LAYERCOL;
2582 case CTSF_LAYERPCT_COUNT:
2583 case CTSF_LAYERPCT_SUM:
2584 case CTSF_LAYERPCT_TOTALN:
2585 case CTSF_LAYERPCT_VALIDN:
2588 case CTSF_LAYERROWPCT_COUNT:
2589 case CTSF_LAYERROWPCT_SUM:
2590 case CTSF_LAYERROWPCT_TOTALN:
2591 case CTSF_LAYERROWPCT_VALIDN:
2592 return CTDT_LAYERROW;
2594 case CTSF_ROWPCT_COUNT:
2595 case CTSF_ROWPCT_SUM:
2596 case CTSF_ROWPCT_TOTALN:
2597 case CTSF_ROWPCT_VALIDN:
2600 case CTSF_SUBTABLEPCT_COUNT:
2601 case CTSF_SUBTABLEPCT_SUM:
2602 case CTSF_SUBTABLEPCT_TOTALN:
2603 case CTSF_SUBTABLEPCT_VALIDN:
2604 return CTDT_SUBTABLE;
2606 case CTSF_TABLEPCT_COUNT:
2607 case CTSF_TABLEPCT_SUM:
2608 case CTSF_TABLEPCT_TOTALN:
2609 case CTSF_TABLEPCT_VALIDN:
2617 ctables_summary_value (const struct ctables_cell *cell,
2618 union ctables_summary *s,
2619 const struct ctables_summary_spec *ss)
2621 switch (ss->function)
2627 case CTSF_ROWPCT_COUNT:
2628 case CTSF_COLPCT_COUNT:
2629 case CTSF_TABLEPCT_COUNT:
2630 case CTSF_SUBTABLEPCT_COUNT:
2631 case CTSF_LAYERPCT_COUNT:
2632 case CTSF_LAYERROWPCT_COUNT:
2633 case CTSF_LAYERCOLPCT_COUNT:
2635 enum ctables_domain_type d = ctables_function_domain (ss->function);
2636 return (cell->domains[d]->e_count
2637 ? s->count / cell->domains[d]->e_count * 100
2641 case CTSF_ROWPCT_VALIDN:
2642 case CTSF_COLPCT_VALIDN:
2643 case CTSF_TABLEPCT_VALIDN:
2644 case CTSF_SUBTABLEPCT_VALIDN:
2645 case CTSF_LAYERPCT_VALIDN:
2646 case CTSF_LAYERROWPCT_VALIDN:
2647 case CTSF_LAYERCOLPCT_VALIDN:
2649 enum ctables_domain_type d = ctables_function_domain (ss->function);
2650 return (cell->domains[d]->e_valid
2651 ? s->count / cell->domains[d]->e_valid * 100
2655 case CTSF_ROWPCT_TOTALN:
2656 case CTSF_COLPCT_TOTALN:
2657 case CTSF_TABLEPCT_TOTALN:
2658 case CTSF_SUBTABLEPCT_TOTALN:
2659 case CTSF_LAYERPCT_TOTALN:
2660 case CTSF_LAYERROWPCT_TOTALN:
2661 case CTSF_LAYERCOLPCT_TOTALN:
2663 enum ctables_domain_type d = ctables_function_domain (ss->function);
2664 return (cell->domains[d]->e_total
2665 ? s->count / cell->domains[d]->e_total * 100
2689 return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
2694 moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
2700 double weight, variance;
2701 moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
2702 return calc_semean (variance, weight);
2708 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
2709 return variance != SYSMIS ? sqrt (variance) : SYSMIS;
2714 double weight, mean;
2715 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
2716 return weight != SYSMIS && mean != SYSMIS ? weight * mean : SYSMIS;
2722 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
2726 case CTSF_ROWPCT_SUM:
2727 case CTSF_COLPCT_SUM:
2728 case CTSF_TABLEPCT_SUM:
2729 case CTSF_SUBTABLEPCT_SUM:
2730 case CTSF_LAYERPCT_SUM:
2731 case CTSF_LAYERROWPCT_SUM:
2732 case CTSF_LAYERCOLPCT_SUM:
2739 struct casereader *reader = casewriter_make_reader (s->writer);
2742 struct percentile *ptile = percentile_create (
2743 ss->function == CTSF_PTILE ? ss->percentile : 0.5, s->ovalid);
2744 struct order_stats *os = &ptile->parent;
2745 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2746 s->ovalue = percentile_calculate (ptile, PC_HAVERAGE);
2747 statistic_destroy (&ptile->parent.parent);
2754 struct casereader *reader = casewriter_make_reader (s->writer);
2757 struct mode *mode = mode_create ();
2758 struct order_stats *os = &mode->parent;
2759 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2760 s->ovalue = mode->mode;
2761 statistic_destroy (&mode->parent.parent);
2769 struct ctables_cell_sort_aux
2771 const struct ctables_nest *nest;
2772 enum pivot_axis_type a;
2776 ctables_cell_compare_3way (const void *a_, const void *b_, const void *aux_)
2778 const struct ctables_cell_sort_aux *aux = aux_;
2779 struct ctables_cell *const *ap = a_;
2780 struct ctables_cell *const *bp = b_;
2781 const struct ctables_cell *a = *ap;
2782 const struct ctables_cell *b = *bp;
2784 const struct ctables_nest *nest = aux->nest;
2785 for (size_t i = 0; i < nest->n; i++)
2786 if (i != nest->scale_idx)
2788 const struct variable *var = nest->vars[i];
2789 const struct ctables_cell_value *a_cv = &a->axes[aux->a].cvs[i];
2790 const struct ctables_cell_value *b_cv = &b->axes[aux->a].cvs[i];
2791 if (a_cv->category != b_cv->category)
2792 return a_cv->category > b_cv->category ? 1 : -1;
2794 const union value *a_val = &a_cv->value;
2795 const union value *b_val = &b_cv->value;
2796 switch (a_cv->category->type)
2802 case CCT_POSTCOMPUTE:
2803 case CCT_EXCLUDED_MISSING:
2804 /* Must be equal. */
2812 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2820 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2822 return a_cv->category->sort_ascending ? cmp : -cmp;
2828 const char *a_label = var_lookup_value_label (var, a_val);
2829 const char *b_label = var_lookup_value_label (var, b_val);
2831 ? (b_label ? strcmp (a_label, b_label) : 1)
2832 : (b_label ? -1 : value_compare_3way (
2833 a_val, b_val, var_get_width (var))));
2835 return a_cv->category->sort_ascending ? cmp : -cmp;
2849 For each ctables_table:
2850 For each combination of row vars:
2851 For each combination of column vars:
2852 For each combination of layer vars:
2854 Make a table of row values:
2855 Sort entries by row values
2856 Assign a 0-based index to each actual value
2857 Construct a dimension
2858 Make a table of column values
2859 Make a table of layer values
2861 Fill the table entry using the indexes from before.
2864 static struct ctables_domain *
2865 ctables_domain_insert (struct ctables_section *s, struct ctables_cell *cell,
2866 enum ctables_domain_type domain)
2869 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2871 const struct ctables_nest *nest = s->nests[a];
2872 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2874 size_t v_idx = nest->domains[domain][i];
2875 struct ctables_cell_value *cv = &cell->axes[a].cvs[v_idx];
2876 hash = hash_pointer (cv->category, hash);
2877 if (cv->category->type != CCT_TOTAL
2878 && cv->category->type != CCT_SUBTOTAL
2879 && cv->category->type != CCT_POSTCOMPUTE)
2880 hash = value_hash (&cv->value,
2881 var_get_width (nest->vars[v_idx]), hash);
2885 struct ctables_domain *d;
2886 HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &s->domains[domain])
2888 const struct ctables_cell *df = d->example;
2889 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2891 const struct ctables_nest *nest = s->nests[a];
2892 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2894 size_t v_idx = nest->domains[domain][i];
2895 struct ctables_cell_value *cv1 = &df->axes[a].cvs[v_idx];
2896 struct ctables_cell_value *cv2 = &cell->axes[a].cvs[v_idx];
2897 if (cv1->category != cv2->category
2898 || (cv1->category->type != CCT_TOTAL
2899 && cv1->category->type != CCT_SUBTOTAL
2900 && cv1->category->type != CCT_POSTCOMPUTE
2901 && !value_equal (&cv1->value, &cv2->value,
2902 var_get_width (nest->vars[v_idx]))))
2911 d = xmalloc (sizeof *d);
2912 *d = (struct ctables_domain) { .example = cell };
2913 hmap_insert (&s->domains[domain], &d->node, hash);
2917 static struct substring
2918 rtrim_value (const union value *v, const struct variable *var)
2920 struct substring s = ss_buffer (CHAR_CAST (char *, v->s),
2921 var_get_width (var));
2922 ss_rtrim (&s, ss_cstr (" "));
2927 in_string_range (const union value *v, const struct variable *var,
2928 const struct substring *srange)
2930 struct substring s = rtrim_value (v, var);
2931 return ((!srange[0].string || ss_compare (s, srange[0]) >= 0)
2932 && (!srange[1].string || ss_compare (s, srange[1]) <= 0));
2935 static const struct ctables_category *
2936 ctables_categories_match (const struct ctables_categories *c,
2937 const union value *v, const struct variable *var)
2939 if (var_is_numeric (var) && v->f == SYSMIS)
2942 const struct ctables_category *othernm = NULL;
2943 for (size_t i = c->n_cats; i-- > 0; )
2945 const struct ctables_category *cat = &c->cats[i];
2949 if (cat->number == v->f)
2954 if (ss_equals (cat->string, rtrim_value (v, var)))
2959 if ((cat->nrange[0] == -DBL_MAX || v->f >= cat->nrange[0])
2960 && (cat->nrange[1] == DBL_MAX || v->f <= cat->nrange[1]))
2965 if (in_string_range (v, var, cat->srange))
2970 if (var_is_value_missing (var, v))
2974 case CCT_POSTCOMPUTE:
2989 return (cat->include_missing || !var_is_value_missing (var, v) ? cat
2992 case CCT_EXCLUDED_MISSING:
2997 return var_is_value_missing (var, v) ? NULL : othernm;
3000 static const struct ctables_category *
3001 ctables_categories_total (const struct ctables_categories *c)
3003 const struct ctables_category *first = &c->cats[0];
3004 const struct ctables_category *last = &c->cats[c->n_cats - 1];
3005 return (first->type == CCT_TOTAL ? first
3006 : last->type == CCT_TOTAL ? last
3010 static struct ctables_cell *
3011 ctables_cell_insert__ (struct ctables_section *s, const struct ccase *c,
3012 const struct ctables_category *cats[PIVOT_N_AXES][10])
3015 enum ctables_summary_variant sv = CSV_CELL;
3016 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3018 const struct ctables_nest *nest = s->nests[a];
3019 for (size_t i = 0; i < nest->n; i++)
3020 if (i != nest->scale_idx)
3022 hash = hash_pointer (cats[a][i], hash);
3023 if (cats[a][i]->type != CCT_TOTAL
3024 && cats[a][i]->type != CCT_SUBTOTAL
3025 && cats[a][i]->type != CCT_POSTCOMPUTE)
3026 hash = value_hash (case_data (c, nest->vars[i]),
3027 var_get_width (nest->vars[i]), hash);
3033 struct ctables_cell *cell;
3034 HMAP_FOR_EACH_WITH_HASH (cell, struct ctables_cell, node, hash, &s->cells)
3036 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3038 const struct ctables_nest *nest = s->nests[a];
3039 for (size_t i = 0; i < nest->n; i++)
3040 if (i != nest->scale_idx
3041 && (cats[a][i] != cell->axes[a].cvs[i].category
3042 || (cats[a][i]->type != CCT_TOTAL
3043 && cats[a][i]->type != CCT_SUBTOTAL
3044 && cats[a][i]->type != CCT_POSTCOMPUTE
3045 && !value_equal (case_data (c, nest->vars[i]),
3046 &cell->axes[a].cvs[i].value,
3047 var_get_width (nest->vars[i])))))
3056 cell = xmalloc (sizeof *cell);
3059 cell->omit_domains = 0;
3060 cell->postcompute = false;
3061 //struct string name = DS_EMPTY_INITIALIZER;
3062 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3064 const struct ctables_nest *nest = s->nests[a];
3065 cell->axes[a].cvs = (nest->n
3066 ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
3068 for (size_t i = 0; i < nest->n; i++)
3070 const struct ctables_category *cat = cats[a][i];
3071 const struct variable *var = nest->vars[i];
3072 const union value *value = case_data (c, var);
3073 if (i != nest->scale_idx)
3075 const struct ctables_category *subtotal = cat->subtotal;
3076 if (cat->hide || (subtotal && subtotal->hide_subcategories))
3079 if (cat->type == CCT_TOTAL
3080 || cat->type == CCT_SUBTOTAL
3081 || cat->type == CCT_POSTCOMPUTE)
3083 /* XXX these should be more encompassing I think.*/
3087 case PIVOT_AXIS_COLUMN:
3088 cell->omit_domains |= ((1u << CTDT_TABLE) |
3089 (1u << CTDT_LAYER) |
3090 (1u << CTDT_LAYERCOL) |
3091 (1u << CTDT_SUBTABLE) |
3094 case PIVOT_AXIS_ROW:
3095 cell->omit_domains |= ((1u << CTDT_TABLE) |
3096 (1u << CTDT_LAYER) |
3097 (1u << CTDT_LAYERROW) |
3098 (1u << CTDT_SUBTABLE) |
3101 case PIVOT_AXIS_LAYER:
3102 cell->omit_domains |= ((1u << CTDT_TABLE) |
3103 (1u << CTDT_LAYER));
3107 if (cat->type == CCT_POSTCOMPUTE)
3108 cell->postcompute = true;
3111 cell->axes[a].cvs[i].category = cat;
3112 value_clone (&cell->axes[a].cvs[i].value, value, var_get_width (var));
3115 if (i != nest->scale_idx)
3117 if (!ds_is_empty (&name))
3118 ds_put_cstr (&name, ", ");
3119 char *value_s = data_out (value, var_get_encoding (var),
3120 var_get_print_format (var),
3121 settings_get_fmt_settings ());
3122 if (cat->type == CCT_TOTAL
3123 || cat->type == CCT_SUBTOTAL
3124 || cat->type == CCT_POSTCOMPUTE)
3125 ds_put_format (&name, "%s=total", var_get_name (var));
3127 ds_put_format (&name, "%s=%s", var_get_name (var),
3128 value_s + strspn (value_s, " "));
3134 //cell->name = ds_steal_cstr (&name);
3136 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3137 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3138 cell->summaries = xmalloc (specs->n * sizeof *cell->summaries);
3139 for (size_t i = 0; i < specs->n; i++)
3140 ctables_summary_init (&cell->summaries[i], &specs->specs[i]);
3141 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3142 cell->domains[dt] = ctables_domain_insert (s, cell, dt);
3143 hmap_insert (&s->cells, &cell->node, hash);
3148 is_scale_missing (const struct ctables_summary_spec_set *specs,
3149 const struct ccase *c)
3151 if (!specs->is_scale)
3154 if (var_is_num_missing (specs->var, case_num (c, specs->var)))
3157 for (size_t i = 0; i < specs->n_listwise_vars; i++)
3159 const struct variable *var = specs->listwise_vars[i];
3160 if (var_is_num_missing (var, case_num (c, var)))
3168 ctables_cell_add__ (struct ctables_section *s, const struct ccase *c,
3169 const struct ctables_category *cats[PIVOT_N_AXES][10],
3170 bool is_missing, bool excluded_missing,
3171 double d_weight, double e_weight)
3173 struct ctables_cell *cell = ctables_cell_insert__ (s, c, cats);
3174 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3176 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3178 bool scale_missing = is_scale_missing (specs, c);
3179 for (size_t i = 0; i < specs->n; i++)
3180 ctables_summary_add (&cell->summaries[i], &specs->specs[i],
3181 specs->var, case_data (c, specs->var), specs->is_scale,
3182 scale_missing, is_missing, excluded_missing,
3183 d_weight, e_weight);
3184 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3185 if (!(cell->omit_domains && (1u << dt)))
3187 struct ctables_domain *d = cell->domains[dt];
3188 d->d_total += d_weight;
3189 d->e_total += e_weight;
3190 if (!excluded_missing)
3192 d->d_count += d_weight;
3193 d->e_count += e_weight;
3197 d->d_valid += d_weight;
3198 d->e_valid += e_weight;
3204 recurse_totals (struct ctables_section *s, const struct ccase *c,
3205 const struct ctables_category *cats[PIVOT_N_AXES][10],
3206 bool is_missing, bool excluded_missing,
3207 double d_weight, double e_weight,
3208 enum pivot_axis_type start_axis, size_t start_nest)
3210 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3212 const struct ctables_nest *nest = s->nests[a];
3213 for (size_t i = start_nest; i < nest->n; i++)
3215 if (i == nest->scale_idx)
3218 const struct variable *var = nest->vars[i];
3220 const struct ctables_category *total = ctables_categories_total (
3221 s->table->categories[var_get_dict_index (var)]);
3224 const struct ctables_category *save = cats[a][i];
3226 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3227 d_weight, e_weight);
3228 recurse_totals (s, c, cats, is_missing, excluded_missing,
3229 d_weight, e_weight, a, i + 1);
3238 recurse_subtotals (struct ctables_section *s, const struct ccase *c,
3239 const struct ctables_category *cats[PIVOT_N_AXES][10],
3240 bool is_missing, bool excluded_missing,
3241 double d_weight, double e_weight,
3242 enum pivot_axis_type start_axis, size_t start_nest)
3244 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3246 const struct ctables_nest *nest = s->nests[a];
3247 for (size_t i = start_nest; i < nest->n; i++)
3249 if (i == nest->scale_idx)
3252 const struct ctables_category *save = cats[a][i];
3255 cats[a][i] = save->subtotal;
3256 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3257 d_weight, e_weight);
3258 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3259 d_weight, e_weight, a, i + 1);
3268 ctables_add_occurrence (const struct variable *var,
3269 const union value *value,
3270 struct hmap *occurrences)
3272 int width = var_get_width (var);
3273 unsigned int hash = value_hash (value, width, 0);
3275 struct ctables_occurrence *o;
3276 HMAP_FOR_EACH_WITH_HASH (o, struct ctables_occurrence, node, hash,
3278 if (value_equal (value, &o->value, width))
3281 o = xmalloc (sizeof *o);
3282 value_clone (&o->value, value, width);
3283 hmap_insert (occurrences, &o->node, hash);
3287 ctables_cell_insert (struct ctables_section *s,
3288 const struct ccase *c,
3289 double d_weight, double e_weight)
3291 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
3293 /* Does at least one categorical variable have a missing value in an included
3294 or excluded category? */
3295 bool is_missing = false;
3297 /* Does at least one categorical variable have a missing value in an excluded
3299 bool excluded_missing = false;
3301 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3303 const struct ctables_nest *nest = s->nests[a];
3304 for (size_t i = 0; i < nest->n; i++)
3306 if (i == nest->scale_idx)
3309 const struct variable *var = nest->vars[i];
3310 const union value *value = case_data (c, var);
3312 bool var_missing = var_is_value_missing (var, value) != 0;
3316 cats[a][i] = ctables_categories_match (
3317 s->table->categories[var_get_dict_index (var)], value, var);
3323 static const struct ctables_category cct_excluded_missing = {
3324 .type = CCT_EXCLUDED_MISSING,
3327 cats[a][i] = &cct_excluded_missing;
3328 excluded_missing = true;
3333 if (!excluded_missing)
3334 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3336 const struct ctables_nest *nest = s->nests[a];
3337 for (size_t i = 0; i < nest->n; i++)
3338 if (i != nest->scale_idx)
3340 const struct variable *var = nest->vars[i];
3341 const union value *value = case_data (c, var);
3342 ctables_add_occurrence (var, value, &s->occurrences[a][i]);
3346 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3347 d_weight, e_weight);
3349 //if (!excluded_missing)
3351 recurse_totals (s, c, cats, is_missing, excluded_missing,
3352 d_weight, e_weight, 0, 0);
3353 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3354 d_weight, e_weight, 0, 0);
3360 const struct ctables_summary_spec_set *set;
3365 merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b)
3367 const struct ctables_summary_spec *as = &a->set->specs[a->ofs];
3368 const struct ctables_summary_spec *bs = &b->set->specs[b->ofs];
3369 if (as->function != bs->function)
3370 return as->function > bs->function ? 1 : -1;
3371 else if (as->percentile != bs->percentile)
3372 return as->percentile < bs->percentile ? 1 : -1;
3373 return strcmp (as->label, bs->label);
3376 static struct pivot_value *
3377 ctables_category_create_label__ (const struct ctables_category *cat,
3378 const struct variable *var,
3379 const union value *value)
3381 return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
3382 ? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
3383 : pivot_value_new_var_value (var, value));
3386 static struct pivot_value *
3387 ctables_postcompute_label (const struct ctables_categories *cats,
3388 const struct ctables_category *cat,
3389 const struct variable *var,
3390 const union value *value)
3392 struct substring in = ss_cstr (cat->pc->label);
3393 struct substring target = ss_cstr (")LABEL[");
3395 struct string out = DS_EMPTY_INITIALIZER;
3398 size_t chunk = ss_find_substring (in, target);
3399 if (chunk == SIZE_MAX)
3401 if (ds_is_empty (&out))
3402 return pivot_value_new_user_text (in.string, in.length);
3405 ds_put_substring (&out, in);
3406 return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
3410 ds_put_substring (&out, ss_head (in, chunk));
3411 ss_advance (&in, chunk + target.length);
3413 struct substring idx_s;
3414 if (!ss_get_until (&in, ']', &idx_s))
3417 long int idx = strtol (idx_s.string, &tail, 10);
3418 if (idx < 1 || idx > cats->n_cats || tail != ss_end (idx_s))
3421 struct ctables_category *cat2 = &cats->cats[idx - 1];
3422 struct pivot_value *label2
3423 = ctables_category_create_label__ (cat2, var, value);
3424 char *label2_s = pivot_value_to_string_defaults (label2);
3425 ds_put_cstr (&out, label2_s);
3427 pivot_value_destroy (label2);
3432 return pivot_value_new_user_text (cat->pc->label, SIZE_MAX);
3435 static struct pivot_value *
3436 ctables_category_create_label (const struct ctables_categories *cats,
3437 const struct ctables_category *cat,
3438 const struct variable *var,
3439 const union value *value)
3441 return (cat->type == CCT_POSTCOMPUTE && cat->pc->label
3442 ? ctables_postcompute_label (cats, cat, var, value)
3443 : ctables_category_create_label__ (cat, var, value));
3446 static struct ctables_value *
3447 ctables_value_find__ (struct ctables_table *t, const union value *value,
3448 int width, unsigned int hash)
3450 struct ctables_value *clv;
3451 HMAP_FOR_EACH_WITH_HASH (clv, struct ctables_value, node,
3452 hash, &t->clabels_values_map)
3453 if (value_equal (value, &clv->value, width))
3459 ctables_value_insert (struct ctables_table *t, const union value *value,
3462 unsigned int hash = value_hash (value, width, 0);
3463 struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
3466 clv = xmalloc (sizeof *clv);
3467 value_clone (&clv->value, value, width);
3468 hmap_insert (&t->clabels_values_map, &clv->node, hash);
3472 static struct ctables_value *
3473 ctables_value_find (struct ctables_table *t,
3474 const union value *value, int width)
3476 return ctables_value_find__ (t, value, width,
3477 value_hash (value, width, 0));
3481 ctables_table_add_section (struct ctables_table *t, enum pivot_axis_type a,
3482 size_t ix[PIVOT_N_AXES])
3484 if (a < PIVOT_N_AXES)
3486 size_t limit = MAX (t->stacks[a].n, 1);
3487 for (ix[a] = 0; ix[a] < limit; ix[a]++)
3488 ctables_table_add_section (t, a + 1, ix);
3492 struct ctables_section *s = &t->sections[t->n_sections++];
3493 *s = (struct ctables_section) {
3495 .cells = HMAP_INITIALIZER (s->cells),
3497 for (a = 0; a < PIVOT_N_AXES; a++)
3500 struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
3502 s->occurrences[a] = xnmalloc (nest->n, sizeof *s->occurrences[a]);
3503 for (size_t i = 0; i < nest->n; i++)
3504 hmap_init (&s->occurrences[a][i]);
3506 for (size_t i = 0; i < N_CTDTS; i++)
3507 hmap_init (&s->domains[i]);
3512 ctpo_add (double a, double b)
3518 ctpo_sub (double a, double b)
3524 ctpo_mul (double a, double b)
3530 ctpo_div (double a, double b)
3532 return b ? a / b : SYSMIS;
3536 ctpo_pow (double a, double b)
3538 int save_errno = errno;
3540 double result = pow (a, b);
3548 ctpo_neg (double a, double b UNUSED)
3553 struct ctables_pcexpr_evaluate_ctx
3555 const struct ctables_cell *cell;
3556 const struct ctables_section *section;
3557 const struct ctables_categories *cats;
3558 enum pivot_axis_type pc_a;
3563 static double ctables_pcexpr_evaluate (
3564 const struct ctables_pcexpr_evaluate_ctx *, const struct ctables_pcexpr *);
3567 ctables_pcexpr_evaluate_nonterminal (
3568 const struct ctables_pcexpr_evaluate_ctx *ctx,
3569 const struct ctables_pcexpr *e, size_t n_args,
3570 double evaluate (double, double))
3572 double args[2] = { 0, 0 };
3573 for (size_t i = 0; i < n_args; i++)
3575 args[i] = ctables_pcexpr_evaluate (ctx, e->subs[i]);
3576 if (!isfinite (args[i]) || args[i] == SYSMIS)
3579 return evaluate (args[0], args[1]);
3583 ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
3584 const struct ctables_cell_value *pc_cv)
3586 const struct ctables_section *s = ctx->section;
3589 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3591 const struct ctables_nest *nest = s->nests[a];
3592 for (size_t i = 0; i < nest->n; i++)
3593 if (i != nest->scale_idx)
3595 const struct ctables_cell_value *cv
3596 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3597 : &ctx->cell->axes[a].cvs[i]);
3598 hash = hash_pointer (cv->category, hash);
3599 if (cv->category->type != CCT_TOTAL
3600 && cv->category->type != CCT_SUBTOTAL
3601 && cv->category->type != CCT_POSTCOMPUTE)
3602 hash = value_hash (&cv->value,
3603 var_get_width (nest->vars[i]), hash);
3607 struct ctables_cell *tc;
3608 HMAP_FOR_EACH_WITH_HASH (tc, struct ctables_cell, node, hash, &s->cells)
3610 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3612 const struct ctables_nest *nest = s->nests[a];
3613 for (size_t i = 0; i < nest->n; i++)
3614 if (i != nest->scale_idx)
3616 const struct ctables_cell_value *p_cv
3617 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3618 : &ctx->cell->axes[a].cvs[i]);
3619 const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
3620 if (p_cv->category != t_cv->category
3621 || (p_cv->category->type != CCT_TOTAL
3622 && p_cv->category->type != CCT_SUBTOTAL
3623 && p_cv->category->type != CCT_POSTCOMPUTE
3624 && !value_equal (&p_cv->value,
3626 var_get_width (nest->vars[i]))))
3638 const struct ctables_table *t = s->table;
3639 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
3640 const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
3641 return ctables_summary_value (tc, &tc->summaries[ctx->summary_idx],
3642 &specs->specs[ctx->summary_idx]);
3646 ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
3647 const struct ctables_pcexpr *e)
3654 case CTPO_CAT_NRANGE:
3655 case CTPO_CAT_SRANGE:
3657 struct ctables_cell_value cv = {
3658 .category = ctables_find_category_for_postcompute (ctx->cats, e)
3660 assert (cv.category != NULL);
3662 struct hmap *occurrences = &ctx->section->occurrences[ctx->pc_a][ctx->pc_a_idx];
3663 const struct ctables_occurrence *o;
3666 const struct variable *var = ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx];
3667 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
3668 if (ctables_categories_match (ctx->cats, &o->value, var) == cv.category)
3670 cv.value = o->value;
3671 sum += ctables_pcexpr_evaluate_category (ctx, &cv);
3676 case CTPO_CAT_NUMBER:
3677 case CTPO_CAT_STRING:
3678 case CTPO_CAT_MISSING:
3679 case CTPO_CAT_OTHERNM:
3680 case CTPO_CAT_SUBTOTAL:
3681 case CTPO_CAT_TOTAL:
3683 struct ctables_cell_value cv = {
3684 .category = ctables_find_category_for_postcompute (ctx->cats, e),
3685 .value = { .f = e->number },
3687 assert (cv.category != NULL);
3688 return ctables_pcexpr_evaluate_category (ctx, &cv);
3692 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
3695 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_sub);
3698 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_mul);
3701 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_div);
3704 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_pow);
3707 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 1, ctpo_neg);
3713 /* XXX what if there is a postcompute in more than one dimension?? */
3714 static const struct ctables_postcompute *
3715 ctables_cell_postcompute (const struct ctables_section *s,
3716 const struct ctables_cell *cell,
3717 enum pivot_axis_type *pc_a_p,
3720 assert (cell->postcompute);
3721 for (enum pivot_axis_type pc_a = 0; ; pc_a++)
3723 assert (pc_a < PIVOT_N_AXES);
3724 for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
3726 const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
3727 if (cv->category->type == CCT_POSTCOMPUTE)
3732 *pc_a_idx_p = pc_a_idx;
3733 return cv->category->pc;
3742 ctables_cell_calculate_postcompute (const struct ctables_section *s,
3743 const struct ctables_cell *cell,
3744 const struct ctables_summary_spec *ss,
3745 struct fmt_spec *format,
3746 bool *is_ctables_format,
3749 enum pivot_axis_type pc_a;
3751 const struct ctables_postcompute *pc = ctables_cell_postcompute (
3752 s, cell, &pc_a, &pc_a_idx);
3756 for (size_t i = 0; i < pc->specs->n; i++)
3758 const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
3759 if (ss->function == ss2->function
3760 && ss->percentile == ss2->percentile)
3762 *format = ss2->format;
3763 *is_ctables_format = ss2->is_ctables_format;
3769 const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
3770 const struct ctables_categories *cats = s->table->categories[
3771 var_get_dict_index (var)];
3772 struct ctables_pcexpr_evaluate_ctx ctx = {
3777 .pc_a_idx = pc_a_idx,
3778 .summary_idx = summary_idx,
3780 return ctables_pcexpr_evaluate (&ctx, pc->expr);
3784 ctables_table_output (struct ctables *ct, struct ctables_table *t)
3786 struct pivot_table *pt = pivot_table_create__ (
3788 ? pivot_value_new_user_text (t->title, SIZE_MAX)
3789 : pivot_value_new_text (N_("Custom Tables"))),
3792 pivot_table_set_caption (
3793 pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
3795 pivot_table_set_corner_text (
3796 pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
3798 bool summary_dimension = (t->summary_axis != t->slabels_axis
3799 || (!t->slabels_visible
3800 && t->summary_specs.n > 1));
3801 if (summary_dimension)
3803 struct pivot_dimension *d = pivot_dimension_create (
3804 pt, t->slabels_axis, N_("Statistics"));
3805 const struct ctables_summary_spec_set *specs = &t->summary_specs;
3806 if (!t->slabels_visible)
3807 d->hide_all_labels = true;
3808 for (size_t i = 0; i < specs->n; i++)
3809 pivot_category_create_leaf (
3810 d->root, pivot_value_new_text (specs->specs[i].label));
3813 bool categories_dimension = t->clabels_example != NULL;
3814 if (categories_dimension)
3816 struct pivot_dimension *d = pivot_dimension_create (
3817 pt, t->label_axis[t->clabels_from_axis],
3818 t->clabels_from_axis == PIVOT_AXIS_ROW
3819 ? N_("Row Categories")
3820 : N_("Column Categories"));
3821 const struct variable *var = t->clabels_example;
3822 const struct ctables_categories *c = t->categories[var_get_dict_index (var)];
3823 for (size_t i = 0; i < t->n_clabels_values; i++)
3825 const struct ctables_value *value = t->clabels_values[i];
3826 const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
3827 assert (cat != NULL);
3828 pivot_category_create_leaf (d->root, ctables_category_create_label (
3829 c, cat, t->clabels_example,
3834 pivot_table_set_look (pt, ct->look);
3835 struct pivot_dimension *d[PIVOT_N_AXES];
3836 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3838 static const char *names[] = {
3839 [PIVOT_AXIS_ROW] = N_("Rows"),
3840 [PIVOT_AXIS_COLUMN] = N_("Columns"),
3841 [PIVOT_AXIS_LAYER] = N_("Layers"),
3843 d[a] = (t->axes[a] || a == t->summary_axis
3844 ? pivot_dimension_create (pt, a, names[a])
3849 assert (t->axes[a]);
3851 for (size_t i = 0; i < t->stacks[a].n; i++)
3853 struct ctables_nest *nest = &t->stacks[a].nests[i];
3854 struct ctables_section **sections = xnmalloc (t->n_sections,
3856 size_t n_sections = 0;
3858 size_t n_total_cells = 0;
3859 size_t max_depth = 0;
3860 for (size_t j = 0; j < t->n_sections; j++)
3861 if (t->sections[j].nests[a] == nest)
3863 struct ctables_section *s = &t->sections[j];
3864 sections[n_sections++] = s;
3865 n_total_cells += s->cells.count;
3867 size_t depth = s->nests[a]->n;
3868 max_depth = MAX (depth, max_depth);
3871 struct ctables_cell **sorted = xnmalloc (n_total_cells,
3873 size_t n_sorted = 0;
3875 for (size_t j = 0; j < n_sections; j++)
3877 struct ctables_section *s = sections[j];
3879 struct ctables_cell *cell;
3880 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
3882 sorted[n_sorted++] = cell;
3883 assert (n_sorted <= n_total_cells);
3886 struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
3887 sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
3890 for (size_t j = 0; j < n_sorted; j++)
3892 printf ("%s (%s): %f/%f = %.1f%%\n", sorted[j]->name, sorted[j]->contributes_to_domains ? "y" : "n", sorted[j]->summaries[0].count, sorted[j]->domains[CTDT_COL]->e_count, sorted[j]->summaries[0].count / sorted[j]->domains[CTDT_COL]->e_count * 100.0);
3897 struct ctables_level
3899 enum ctables_level_type
3901 CTL_VAR, /* Variable label for nest->vars[var_idx]. */
3902 CTL_CATEGORY, /* Category for nest->vars[var_idx]. */
3903 CTL_SUMMARY, /* Summary functions. */
3907 enum settings_value_show vlabel; /* CTL_VAR only. */
3910 struct ctables_level *levels = xnmalloc (1 + 2 * max_depth, sizeof *levels);
3911 size_t n_levels = 0;
3912 for (size_t k = 0; k < nest->n; k++)
3914 enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k])];
3915 if (vlabel != CTVL_NONE)
3917 levels[n_levels++] = (struct ctables_level) {
3919 .vlabel = (enum settings_value_show) vlabel,
3924 if (nest->scale_idx != k
3925 && (k != nest->n - 1 || t->label_axis[a] == a))
3927 levels[n_levels++] = (struct ctables_level) {
3928 .type = CTL_CATEGORY,
3934 if (!summary_dimension && a == t->slabels_axis)
3936 levels[n_levels++] = (struct ctables_level) {
3937 .type = CTL_SUMMARY,
3938 .var_idx = SIZE_MAX,
3942 /* Pivot categories:
3944 - variable label for nest->vars[0], if vlabel != CTVL_NONE
3945 - category for nest->vars[0], if nest->scale_idx != 0
3946 - variable label for nest->vars[1], if vlabel != CTVL_NONE
3947 - category for nest->vars[1], if nest->scale_idx != 1
3949 - variable label for nest->vars[n - 1], if vlabel != CTVL_NONE
3950 - category for nest->vars[n - 1], if t->label_axis[a] == a && nest->scale_idx != n - 1.
3951 - summary function, if 'a == t->slabels_axis && a ==
3954 Additional dimensions:
3956 - If 'a == t->slabels_axis && a != t->summary_axis', add a summary
3958 - If 't->label_axis[b] == a' for some 'b != a', add a category
3963 struct pivot_category **groups = xnmalloc (1 + 2 * max_depth, sizeof *groups);
3965 for (size_t j = 0; j < n_sorted; j++)
3967 struct ctables_cell *cell = sorted[j];
3968 struct ctables_cell *prev = j > 0 ? sorted[j - 1] : NULL;
3970 size_t n_common = 0;
3973 for (; n_common < n_levels; n_common++)
3975 const struct ctables_level *level = &levels[n_common];
3976 if (level->type == CTL_CATEGORY)
3978 size_t var_idx = level->var_idx;
3979 const struct ctables_category *c = cell->axes[a].cvs[var_idx].category;
3980 if (prev->axes[a].cvs[var_idx].category != c)
3982 else if (c->type != CCT_SUBTOTAL
3983 && c->type != CCT_TOTAL
3984 && c->type != CCT_POSTCOMPUTE
3985 && !value_equal (&prev->axes[a].cvs[var_idx].value,
3986 &cell->axes[a].cvs[var_idx].value,
3987 var_get_type (nest->vars[var_idx])))
3993 for (size_t k = n_common; k < n_levels; k++)
3995 const struct ctables_level *level = &levels[k];
3996 struct pivot_category *parent = k ? groups[k - 1] : d[a]->root;
3997 if (level->type == CTL_SUMMARY)
3999 assert (k == n_levels - 1);
4001 const struct ctables_summary_spec_set *specs = &t->summary_specs;
4002 for (size_t m = 0; m < specs->n; m++)
4004 int leaf = pivot_category_create_leaf (
4005 parent, pivot_value_new_text (specs->specs[m].label));
4012 const struct variable *var = nest->vars[level->var_idx];
4013 struct pivot_value *label;
4014 if (level->type == CTL_VAR)
4016 label = pivot_value_new_variable (var);
4017 label->variable.show = level->vlabel;
4019 else if (level->type == CTL_CATEGORY)
4021 const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
4022 label = ctables_category_create_label (
4023 t->categories[var_get_dict_index (var)],
4024 cv->category, var, &cv->value);
4029 if (k == n_levels - 1)
4030 prev_leaf = pivot_category_create_leaf (parent, label);
4032 groups[k] = pivot_category_create_group__ (parent, label);
4036 cell->axes[a].leaf = prev_leaf;
4043 for (size_t i = 0; i < t->n_sections; i++)
4045 struct ctables_section *s = &t->sections[i];
4047 struct ctables_cell *cell;
4048 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4053 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
4054 const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
4055 for (size_t j = 0; j < specs->n; j++)
4058 size_t n_dindexes = 0;
4060 if (summary_dimension)
4061 dindexes[n_dindexes++] = specs->specs[j].axis_idx;
4063 if (categories_dimension)
4065 const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
4066 const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
4067 const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
4068 const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
4071 dindexes[n_dindexes++] = ctv->leaf;
4074 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4077 int leaf = cell->axes[a].leaf;
4078 if (a == t->summary_axis && !summary_dimension)
4080 dindexes[n_dindexes++] = leaf;
4083 const struct ctables_summary_spec *ss = &specs->specs[j];
4085 struct fmt_spec format = specs->specs[j].format;
4086 bool is_ctables_format = ss->is_ctables_format;
4087 double d = (cell->postcompute
4088 ? ctables_cell_calculate_postcompute (
4089 s, cell, ss, &format, &is_ctables_format, j)
4090 : ctables_summary_value (cell, &cell->summaries[j],
4093 struct pivot_value *value;
4094 if (ct->hide_threshold != 0
4095 && d < ct->hide_threshold
4096 && ctables_summary_function_is_count (ss->function))
4098 value = pivot_value_new_user_text_nocopy (
4099 xasprintf ("<%d", ct->hide_threshold));
4101 else if (d == 0 && ct->zero)
4102 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
4103 else if (d == SYSMIS && ct->missing)
4104 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
4105 else if (is_ctables_format)
4107 char *s = data_out_stretchy (&(union value) { .f = d },
4109 &ct->ctables_formats, NULL);
4110 value = pivot_value_new_user_text_nocopy (s);
4114 value = pivot_value_new_number (d);
4115 value->numeric.format = format;
4117 pivot_table_put (pt, dindexes, n_dindexes, value);
4122 pivot_table_submit (pt);
4126 ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
4128 enum pivot_axis_type label_pos = t->label_axis[a];
4132 t->clabels_from_axis = a;
4134 const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
4135 const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
4137 const struct ctables_stack *stack = &t->stacks[a];
4141 const struct ctables_nest *n0 = &stack->nests[0];
4143 const struct variable *v0 = n0->vars[n0->n - 1];
4144 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4145 t->clabels_example = v0;
4147 for (size_t i = 0; i < c0->n_cats; i++)
4148 if (c0->cats[i].type == CCT_FUNCTION)
4150 msg (SE, _("%s=%s is not allowed with sorting based "
4151 "on a summary function."),
4152 subcommand_name, pos_name);
4155 if (n0->n - 1 == n0->scale_idx)
4157 msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
4158 "but %s is a scale variable."),
4159 subcommand_name, pos_name, var_get_name (v0));
4163 for (size_t i = 1; i < stack->n; i++)
4165 const struct ctables_nest *ni = &stack->nests[i];
4167 const struct variable *vi = ni->vars[ni->n - 1];
4168 struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
4170 if (ni->n - 1 == ni->scale_idx)
4172 msg (SE, _("%s=%s requires the variables to be moved to be "
4173 "categorical, but %s is a scale variable."),
4174 subcommand_name, pos_name, var_get_name (vi));
4177 if (var_get_width (v0) != var_get_width (vi))
4179 msg (SE, _("%s=%s requires the variables to be "
4180 "moved to have the same width, but %s has "
4181 "width %d and %s has width %d."),
4182 subcommand_name, pos_name,
4183 var_get_name (v0), var_get_width (v0),
4184 var_get_name (vi), var_get_width (vi));
4187 if (!val_labs_equal (var_get_value_labels (v0),
4188 var_get_value_labels (vi)))
4190 msg (SE, _("%s=%s requires the variables to be "
4191 "moved to have the same value labels, but %s "
4192 "and %s have different value labels."),
4193 subcommand_name, pos_name,
4194 var_get_name (v0), var_get_name (vi));
4197 if (!ctables_categories_equal (c0, ci))
4199 msg (SE, _("%s=%s requires the variables to be "
4200 "moved to have the same category "
4201 "specifications, but %s and %s have different "
4202 "category specifications."),
4203 subcommand_name, pos_name,
4204 var_get_name (v0), var_get_name (vi));
4213 ctables_prepare_table (struct ctables_table *t)
4215 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4218 t->stacks[a] = enumerate_fts (a, t->axes[a]);
4220 for (size_t j = 0; j < t->stacks[a].n; j++)
4222 struct ctables_nest *nest = &t->stacks[a].nests[j];
4223 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
4225 nest->domains[dt] = xmalloc (nest->n * sizeof *nest->domains[dt]);
4226 nest->n_domains[dt] = 0;
4228 for (size_t k = 0; k < nest->n; k++)
4230 if (k == nest->scale_idx)
4239 if (a != PIVOT_AXIS_LAYER)
4246 if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
4247 : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
4248 : a == PIVOT_AXIS_ROW)
4250 if (k == nest->n - 1
4251 || (nest->scale_idx == nest->n - 1
4252 && k == nest->n - 2))
4258 if (a == PIVOT_AXIS_COLUMN)
4263 if (a == PIVOT_AXIS_ROW)
4268 nest->domains[dt][nest->n_domains[dt]++] = k;
4275 struct ctables_nest *nest = xmalloc (sizeof *nest);
4276 *nest = (struct ctables_nest) { .n = 0 };
4277 t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
4280 struct ctables_stack *stack = &t->stacks[t->summary_axis];
4281 for (size_t i = 0; i < stack->n; i++)
4283 struct ctables_nest *nest = &stack->nests[i];
4284 if (!nest->specs[CSV_CELL].n)
4286 struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
4287 specs->specs = xmalloc (sizeof *specs->specs);
4290 enum ctables_summary_function function
4291 = specs->is_scale ? CTSF_MEAN : CTSF_COUNT;
4293 *specs->specs = (struct ctables_summary_spec) {
4294 .function = function,
4295 .format = ctables_summary_default_format (function, specs->var),
4296 .label = ctables_summary_default_label (function, 0),
4299 specs->var = nest->vars[0];
4301 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4302 &nest->specs[CSV_CELL]);
4304 else if (!nest->specs[CSV_TOTAL].n)
4305 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4306 &nest->specs[CSV_CELL]);
4308 if (t->ctables->smissing_listwise)
4310 struct variable **listwise_vars = NULL;
4312 size_t allocated = 0;
4314 for (size_t j = nest->group_head; j < stack->n; j++)
4316 const struct ctables_nest *other_nest = &stack->nests[j];
4317 if (other_nest->group_head != nest->group_head)
4320 if (nest != other_nest && other_nest->scale_idx < other_nest->n)
4323 listwise_vars = x2nrealloc (listwise_vars, &allocated,
4324 sizeof *listwise_vars);
4325 listwise_vars[n++] = other_nest->vars[other_nest->scale_idx];
4328 for (size_t j = 0; j < N_CSVS; j++)
4330 nest->specs[j].listwise_vars = listwise_vars;
4331 nest->specs[j].n_listwise_vars = n;
4336 struct ctables_summary_spec_set *merged = &t->summary_specs;
4337 struct merge_item *items = xnmalloc (2 * stack->n, sizeof *items);
4339 for (size_t j = 0; j < stack->n; j++)
4341 const struct ctables_nest *nest = &stack->nests[j];
4343 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4344 items[n_left++] = (struct merge_item) { .set = &nest->specs[sv] };
4349 struct merge_item min = items[0];
4350 for (size_t j = 1; j < n_left; j++)
4351 if (merge_item_compare_3way (&items[j], &min) < 0)
4354 if (merged->n >= merged->allocated)
4355 merged->specs = x2nrealloc (merged->specs, &merged->allocated,
4356 sizeof *merged->specs);
4357 merged->specs[merged->n++] = min.set->specs[min.ofs];
4359 for (size_t j = 0; j < n_left; )
4361 if (merge_item_compare_3way (&items[j], &min) == 0)
4363 struct merge_item *item = &items[j];
4364 item->set->specs[item->ofs].axis_idx = merged->n - 1;
4365 if (++item->ofs >= item->set->n)
4367 items[j] = items[--n_left];
4376 for (size_t j = 0; j < merged->n; j++)
4377 printf ("%s\n", ctables_summary_function_name (merged->specs[j].function));
4379 for (size_t j = 0; j < stack->n; j++)
4381 const struct ctables_nest *nest = &stack->nests[j];
4382 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4384 const struct ctables_summary_spec_set *specs = &nest->specs[sv];
4385 for (size_t k = 0; k < specs->n; k++)
4386 printf ("(%s, %zu) ", ctables_summary_function_name (specs->specs[k].function),
4387 specs->specs[k].axis_idx);
4393 return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
4394 && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
4398 ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
4399 enum pivot_axis_type a)
4401 struct ctables_stack *stack = &t->stacks[a];
4402 for (size_t i = 0; i < stack->n; i++)
4404 const struct ctables_nest *nest = &stack->nests[i];
4405 const struct variable *var = nest->vars[nest->n - 1];
4406 const union value *value = case_data (c, var);
4408 if (var_is_numeric (var) && value->f == SYSMIS)
4411 if (ctables_categories_match (t->categories [var_get_dict_index (var)],
4413 ctables_value_insert (t, value, var_get_width (var));
4418 compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
4420 const struct ctables_value *const *ap = a_;
4421 const struct ctables_value *const *bp = b_;
4422 const struct ctables_value *a = *ap;
4423 const struct ctables_value *b = *bp;
4424 const int *width = width_;
4425 return value_compare_3way (&a->value, &b->value, *width);
4429 ctables_sort_clabels_values (struct ctables_table *t)
4431 const struct variable *v0 = t->clabels_example;
4432 int width = var_get_width (v0);
4434 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4437 const struct val_labs *val_labs = var_get_value_labels (v0);
4438 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4439 vl = val_labs_next (val_labs, vl))
4440 if (ctables_categories_match (c0, &vl->value, v0))
4441 ctables_value_insert (t, &vl->value, width);
4444 size_t n = hmap_count (&t->clabels_values_map);
4445 t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
4447 struct ctables_value *clv;
4449 HMAP_FOR_EACH (clv, struct ctables_value, node, &t->clabels_values_map)
4450 t->clabels_values[i++] = clv;
4451 t->n_clabels_values = n;
4454 sort (t->clabels_values, n, sizeof *t->clabels_values,
4455 compare_clabels_values_3way, &width);
4457 for (size_t i = 0; i < n; i++)
4458 t->clabels_values[i]->leaf = i;
4462 ctables_add_category_occurrences (const struct variable *var,
4463 struct hmap *occurrences,
4464 const struct ctables_categories *cats)
4466 const struct val_labs *val_labs = var_get_value_labels (var);
4468 for (size_t i = 0; i < cats->n_cats; i++)
4470 const struct ctables_category *c = &cats->cats[i];
4474 ctables_add_occurrence (var, &(const union value) { .f = c->number },
4480 int width = var_get_width (var);
4482 value_init (&value, width);
4483 value_copy_buf_rpad (&value, width,
4484 CHAR_CAST (uint8_t *, c->string.string),
4485 c->string.length, ' ');
4486 ctables_add_occurrence (var, &value, occurrences);
4487 value_destroy (&value, width);
4492 assert (var_is_numeric (var));
4493 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4494 vl = val_labs_next (val_labs, vl))
4495 if (vl->value.f >= c->nrange[0] && vl->value.f <= c->nrange[1])
4496 ctables_add_occurrence (var, &vl->value, occurrences);
4500 assert (var_is_alpha (var));
4501 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4502 vl = val_labs_next (val_labs, vl))
4503 if (in_string_range (&vl->value, var, c->srange))
4504 ctables_add_occurrence (var, &vl->value, occurrences);
4508 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4509 vl = val_labs_next (val_labs, vl))
4510 if (var_is_value_missing (var, &vl->value))
4511 ctables_add_occurrence (var, &vl->value, occurrences);
4515 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4516 vl = val_labs_next (val_labs, vl))
4517 ctables_add_occurrence (var, &vl->value, occurrences);
4520 case CCT_POSTCOMPUTE:
4530 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4531 vl = val_labs_next (val_labs, vl))
4532 if (c->include_missing || !var_is_value_missing (var, &vl->value))
4533 ctables_add_occurrence (var, &vl->value, occurrences);
4536 case CCT_EXCLUDED_MISSING:
4543 ctables_section_recurse_add_empty_categories (
4544 struct ctables_section *s,
4545 const struct ctables_category *cats[PIVOT_N_AXES][10], struct ccase *c,
4546 enum pivot_axis_type a, size_t a_idx)
4548 if (a >= PIVOT_N_AXES)
4549 ctables_cell_insert__ (s, c, cats);
4550 else if (!s->nests[a] || a_idx >= s->nests[a]->n)
4551 ctables_section_recurse_add_empty_categories (s, cats, c, a + 1, 0);
4554 const struct variable *var = s->nests[a]->vars[a_idx];
4555 const struct ctables_categories *categories = s->table->categories[
4556 var_get_dict_index (var)];
4557 int width = var_get_width (var);
4558 const struct hmap *occurrences = &s->occurrences[a][a_idx];
4559 const struct ctables_occurrence *o;
4560 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
4562 union value *value = case_data_rw (c, var);
4563 value_destroy (value, width);
4564 value_clone (value, &o->value, width);
4565 cats[a][a_idx] = ctables_categories_match (categories, value, var);
4566 assert (cats[a][a_idx] != NULL);
4567 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4570 for (size_t i = 0; i < categories->n_cats; i++)
4572 const struct ctables_category *cat = &categories->cats[i];
4573 if (cat->type == CCT_POSTCOMPUTE)
4575 cats[a][a_idx] = cat;
4576 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4583 ctables_section_add_empty_categories (struct ctables_section *s)
4585 bool show_empty = false;
4586 for (size_t a = 0; a < PIVOT_N_AXES; a++)
4588 for (size_t k = 0; k < s->nests[a]->n; k++)
4589 if (k != s->nests[a]->scale_idx)
4591 const struct variable *var = s->nests[a]->vars[k];
4592 const struct ctables_categories *cats = s->table->categories[
4593 var_get_dict_index (var)];
4594 if (cats->show_empty)
4597 ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
4603 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
4604 struct ccase *c = case_create (dict_get_proto (s->table->ctables->dict));
4605 ctables_section_recurse_add_empty_categories (s, cats, c, 0, 0);
4610 ctables_execute (struct dataset *ds, struct ctables *ct)
4612 for (size_t i = 0; i < ct->n_tables; i++)
4614 struct ctables_table *t = ct->tables[i];
4615 t->sections = xnmalloc (MAX (1, t->stacks[PIVOT_AXIS_ROW].n) *
4616 MAX (1, t->stacks[PIVOT_AXIS_COLUMN].n) *
4617 MAX (1, t->stacks[PIVOT_AXIS_LAYER].n),
4618 sizeof *t->sections);
4619 size_t ix[PIVOT_N_AXES];
4620 ctables_table_add_section (t, 0, ix);
4623 struct casereader *input = proc_open (ds);
4624 bool warn_on_invalid = true;
4625 for (struct ccase *c = casereader_read (input); c;
4626 case_unref (c), c = casereader_read (input))
4628 double d_weight = dict_get_case_weight (dataset_dict (ds), c,
4630 double e_weight = (ct->e_weight
4631 ? var_force_valid_weight (ct->e_weight,
4632 case_num (c, ct->e_weight),
4636 for (size_t i = 0; i < ct->n_tables; i++)
4638 struct ctables_table *t = ct->tables[i];
4640 for (size_t j = 0; j < t->n_sections; j++)
4641 ctables_cell_insert (&t->sections[j], c, d_weight, e_weight);
4643 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4644 if (t->label_axis[a] != a)
4645 ctables_insert_clabels_values (t, c, a);
4648 casereader_destroy (input);
4650 for (size_t i = 0; i < ct->n_tables; i++)
4652 struct ctables_table *t = ct->tables[i];
4654 if (t->clabels_example)
4655 ctables_sort_clabels_values (t);
4657 for (size_t j = 0; j < t->n_sections; j++)
4658 ctables_section_add_empty_categories (&t->sections[j]);
4660 ctables_table_output (ct, ct->tables[i]);
4662 return proc_commit (ds);
4667 typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *,
4668 struct dictionary *);
4671 ctables_pcexpr_destroy (struct ctables_pcexpr *e)
4677 case CTPO_CAT_STRING:
4678 ss_dealloc (&e->string);
4681 case CTPO_CAT_SRANGE:
4682 for (size_t i = 0; i < 2; i++)
4683 ss_dealloc (&e->srange[i]);
4692 for (size_t i = 0; i < 2; i++)
4693 ctables_pcexpr_destroy (e->subs[i]);
4697 case CTPO_CAT_NUMBER:
4698 case CTPO_CAT_NRANGE:
4699 case CTPO_CAT_MISSING:
4700 case CTPO_CAT_OTHERNM:
4701 case CTPO_CAT_SUBTOTAL:
4702 case CTPO_CAT_TOTAL:
4706 msg_location_destroy (e->location);
4711 static struct ctables_pcexpr *
4712 ctables_pcexpr_allocate_binary (enum ctables_postcompute_op op,
4713 struct ctables_pcexpr *sub0,
4714 struct ctables_pcexpr *sub1)
4716 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4717 *e = (struct ctables_pcexpr) {
4719 .subs = { sub0, sub1 },
4720 .location = msg_location_merged (sub0->location, sub1->location),
4725 /* How to parse an operator. */
4728 enum token_type token;
4729 enum ctables_postcompute_op op;
4732 static const struct operator *
4733 ctable_pcexpr_match_operator (struct lexer *lexer,
4734 const struct operator ops[], size_t n_ops)
4736 for (const struct operator *op = ops; op < ops + n_ops; op++)
4737 if (lex_token (lexer) == op->token)
4739 if (op->token != T_NEG_NUM)
4748 static struct ctables_pcexpr *
4749 ctable_pcexpr_parse_binary_operators__ (
4750 struct lexer *lexer, struct dictionary *dict,
4751 const struct operator ops[], size_t n_ops,
4752 parse_recursively_func *parse_next_level,
4753 const char *chain_warning, struct ctables_pcexpr *lhs)
4755 for (int op_count = 0; ; op_count++)
4757 const struct operator *op
4758 = ctable_pcexpr_match_operator (lexer, ops, n_ops);
4761 if (op_count > 1 && chain_warning)
4762 msg_at (SW, lhs->location, "%s", chain_warning);
4767 struct ctables_pcexpr *rhs = parse_next_level (lexer, dict);
4770 ctables_pcexpr_destroy (lhs);
4774 lhs = ctables_pcexpr_allocate_binary (op->op, lhs, rhs);
4778 static struct ctables_pcexpr *
4779 ctable_pcexpr_parse_binary_operators (struct lexer *lexer,
4780 struct dictionary *dict,
4781 const struct operator ops[], size_t n_ops,
4782 parse_recursively_func *parse_next_level,
4783 const char *chain_warning)
4785 struct ctables_pcexpr *lhs = parse_next_level (lexer, dict);
4789 return ctable_pcexpr_parse_binary_operators__ (lexer, dict, ops, n_ops,
4791 chain_warning, lhs);
4794 static struct ctables_pcexpr *ctable_pcexpr_parse_add (struct lexer *,
4795 struct dictionary *);
4797 static struct ctables_pcexpr
4798 ctpo_cat_nrange (double low, double high)
4800 return (struct ctables_pcexpr) {
4801 .op = CTPO_CAT_NRANGE,
4802 .nrange = { low, high },
4806 static struct ctables_pcexpr *
4807 ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
4809 int start_ofs = lex_ofs (lexer);
4810 struct ctables_pcexpr e;
4811 if (lex_is_number (lexer))
4813 e = (struct ctables_pcexpr) { .op = CTPO_CONSTANT,
4814 .number = lex_number (lexer) };
4817 else if (lex_match_id (lexer, "MISSING"))
4818 e = (struct ctables_pcexpr) { .op = CTPO_CAT_MISSING };
4819 else if (lex_match_id (lexer, "OTHERNM"))
4820 e = (struct ctables_pcexpr) { .op = CTPO_CAT_OTHERNM };
4821 else if (lex_match_id (lexer, "TOTAL"))
4822 e = (struct ctables_pcexpr) { .op = CTPO_CAT_TOTAL };
4823 else if (lex_match_id (lexer, "SUBTOTAL"))
4825 size_t subtotal_index = 0;
4826 if (lex_match (lexer, T_LBRACK))
4828 if (!lex_force_int_range (lexer, "SUBTOTAL", 1, LONG_MAX))
4830 subtotal_index = lex_integer (lexer);
4832 if (!lex_force_match (lexer, T_RBRACK))
4835 e = (struct ctables_pcexpr) { .op = CTPO_CAT_SUBTOTAL,
4836 .subtotal_index = subtotal_index };
4838 else if (lex_match (lexer, T_LBRACK))
4840 if (lex_match_id (lexer, "LO"))
4842 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
4844 e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
4847 else if (lex_is_number (lexer))
4849 double number = lex_number (lexer);
4851 if (lex_match_id (lexer, "THRU"))
4853 if (lex_match_id (lexer, "HI"))
4854 e = ctpo_cat_nrange (number, DBL_MAX);
4857 if (!lex_force_num (lexer))
4859 e = ctpo_cat_nrange (number, lex_number (lexer));
4864 e = (struct ctables_pcexpr) { .op = CTPO_CAT_NUMBER,
4867 else if (lex_is_string (lexer))
4869 struct substring s = recode_substring_pool (
4870 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
4871 ss_rtrim (&s, ss_cstr (" "));
4873 e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
4878 lex_error (lexer, NULL);
4882 if (!lex_force_match (lexer, T_RBRACK))
4884 if (e.op == CTPO_CAT_STRING)
4885 ss_dealloc (&e.string);
4889 else if (lex_match (lexer, T_LPAREN))
4891 struct ctables_pcexpr *ep = ctable_pcexpr_parse_add (lexer, dict);
4894 if (!lex_force_match (lexer, T_RPAREN))
4896 ctables_pcexpr_destroy (ep);
4903 lex_error (lexer, NULL);
4907 e.location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
4908 return xmemdup (&e, sizeof e);
4911 static struct ctables_pcexpr *
4912 ctables_pcexpr_allocate_neg (struct ctables_pcexpr *sub,
4913 struct lexer *lexer, int start_ofs)
4915 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4916 *e = (struct ctables_pcexpr) {
4919 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
4924 static struct ctables_pcexpr *
4925 ctable_pcexpr_parse_exp (struct lexer *lexer, struct dictionary *dict)
4927 static const struct operator op = { T_EXP, CTPO_POW };
4929 const char *chain_warning =
4930 _("The exponentiation operator (`**') is left-associative: "
4931 "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
4932 "To disable this warning, insert parentheses.");
4934 if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
4935 return ctable_pcexpr_parse_binary_operators (lexer, dict, &op, 1,
4936 ctable_pcexpr_parse_primary,
4939 /* Special case for situations like "-5**6", which must be parsed as
4942 int start_ofs = lex_ofs (lexer);
4943 struct ctables_pcexpr *lhs = xmalloc (sizeof *lhs);
4944 *lhs = (struct ctables_pcexpr) {
4945 .op = CTPO_CONSTANT,
4946 .number = -lex_tokval (lexer),
4947 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer)),
4951 struct ctables_pcexpr *node = ctable_pcexpr_parse_binary_operators__ (
4952 lexer, dict, &op, 1,
4953 ctable_pcexpr_parse_primary, chain_warning, lhs);
4957 return ctables_pcexpr_allocate_neg (node, lexer, start_ofs);
4960 /* Parses the unary minus level. */
4961 static struct ctables_pcexpr *
4962 ctable_pcexpr_parse_neg (struct lexer *lexer, struct dictionary *dict)
4964 int start_ofs = lex_ofs (lexer);
4965 if (!lex_match (lexer, T_DASH))
4966 return ctable_pcexpr_parse_exp (lexer, dict);
4968 struct ctables_pcexpr *inner = ctable_pcexpr_parse_neg (lexer, dict);
4972 return ctables_pcexpr_allocate_neg (inner, lexer, start_ofs);
4975 /* Parses the multiplication and division level. */
4976 static struct ctables_pcexpr *
4977 ctable_pcexpr_parse_mul (struct lexer *lexer, struct dictionary *dict)
4979 static const struct operator ops[] =
4981 { T_ASTERISK, CTPO_MUL },
4982 { T_SLASH, CTPO_DIV },
4985 return ctable_pcexpr_parse_binary_operators (lexer, dict, ops,
4986 sizeof ops / sizeof *ops,
4987 ctable_pcexpr_parse_neg, NULL);
4990 /* Parses the addition and subtraction level. */
4991 static struct ctables_pcexpr *
4992 ctable_pcexpr_parse_add (struct lexer *lexer, struct dictionary *dict)
4994 static const struct operator ops[] =
4996 { T_PLUS, CTPO_ADD },
4997 { T_DASH, CTPO_SUB },
4998 { T_NEG_NUM, CTPO_ADD },
5001 return ctable_pcexpr_parse_binary_operators (lexer, dict,
5002 ops, sizeof ops / sizeof *ops,
5003 ctable_pcexpr_parse_mul, NULL);
5006 static struct ctables_postcompute *
5007 ctables_find_postcompute (struct ctables *ct, const char *name)
5009 struct ctables_postcompute *pc;
5010 HMAP_FOR_EACH_WITH_HASH (pc, struct ctables_postcompute, hmap_node,
5011 utf8_hash_case_string (name, 0), &ct->postcomputes)
5012 if (!utf8_strcasecmp (pc->name, name))
5018 ctables_parse_pcompute (struct lexer *lexer, struct dictionary *dict,
5021 int pcompute_start = lex_ofs (lexer) - 1;
5023 if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
5026 char *name = ss_xstrdup (lex_tokss (lexer));
5029 if (!lex_force_match (lexer, T_EQUALS)
5030 || !lex_force_match_id (lexer, "EXPR")
5031 || !lex_force_match (lexer, T_LPAREN))
5037 int expr_start = lex_ofs (lexer);
5038 struct ctables_pcexpr *expr = ctable_pcexpr_parse_add (lexer, dict);
5039 int expr_end = lex_ofs (lexer) - 1;
5040 if (!expr || !lex_force_match (lexer, T_RPAREN))
5045 int pcompute_end = lex_ofs (lexer) - 1;
5047 struct msg_location *location = lex_ofs_location (lexer, pcompute_start,
5050 struct ctables_postcompute *pc = ctables_find_postcompute (ct, name);
5053 msg_at (SW, location, _("New definition of &%s will override the "
5054 "previous definition."),
5056 msg_at (SN, pc->location, _("This is the previous definition."));
5058 ctables_pcexpr_destroy (pc->expr);
5059 msg_location_destroy (pc->location);
5064 pc = xmalloc (sizeof *pc);
5065 *pc = (struct ctables_postcompute) { .name = name };
5066 hmap_insert (&ct->postcomputes, &pc->hmap_node,
5067 utf8_hash_case_string (pc->name, 0));
5070 pc->location = location;
5072 pc->label = lex_ofs_representation (lexer, expr_start, expr_end);
5077 ctables_parse_pproperties_format (struct lexer *lexer,
5078 struct ctables_summary_spec_set *sss)
5080 *sss = (struct ctables_summary_spec_set) { .n = 0 };
5082 while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH
5083 && !(lex_token (lexer) == T_ID
5084 && (lex_id_match (ss_cstr ("LABEL"), lex_tokss (lexer))
5085 || lex_id_match (ss_cstr ("HIDESOURCECATS"),
5086 lex_tokss (lexer)))))
5088 /* Parse function. */
5089 enum ctables_summary_function function;
5090 if (!parse_ctables_summary_function (lexer, &function))
5093 /* Parse percentile. */
5094 double percentile = 0;
5095 if (function == CTSF_PTILE)
5097 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
5099 percentile = lex_number (lexer);
5104 struct fmt_spec format;
5105 bool is_ctables_format;
5106 if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
5109 if (sss->n >= sss->allocated)
5110 sss->specs = x2nrealloc (sss->specs, &sss->allocated,
5111 sizeof *sss->specs);
5112 sss->specs[sss->n++] = (struct ctables_summary_spec) {
5113 .function = function,
5114 .percentile = percentile,
5116 .is_ctables_format = is_ctables_format,
5122 ctables_summary_spec_set_uninit (sss);
5127 ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
5129 struct ctables_postcompute **pcs = NULL;
5131 size_t allocated_pcs = 0;
5133 while (lex_match (lexer, T_AND))
5135 if (!lex_force_id (lexer))
5137 struct ctables_postcompute *pc
5138 = ctables_find_postcompute (ct, lex_tokcstr (lexer));
5141 msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
5146 if (n_pcs >= allocated_pcs)
5147 pcs = x2nrealloc (pcs, &allocated_pcs, sizeof *pcs);
5151 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5153 if (lex_match_id (lexer, "LABEL"))
5155 lex_match (lexer, T_EQUALS);
5156 if (!lex_force_string (lexer))
5159 for (size_t i = 0; i < n_pcs; i++)
5161 free (pcs[i]->label);
5162 pcs[i]->label = ss_xstrdup (lex_tokss (lexer));
5167 else if (lex_match_id (lexer, "FORMAT"))
5169 lex_match (lexer, T_EQUALS);
5171 struct ctables_summary_spec_set sss;
5172 if (!ctables_parse_pproperties_format (lexer, &sss))
5175 for (size_t i = 0; i < n_pcs; i++)
5178 ctables_summary_spec_set_uninit (pcs[i]->specs);
5180 pcs[i]->specs = xmalloc (sizeof *pcs[i]->specs);
5181 ctables_summary_spec_set_clone (pcs[i]->specs, &sss);
5183 ctables_summary_spec_set_uninit (&sss);
5185 else if (lex_match_id (lexer, "HIDESOURCECATS"))
5187 lex_match (lexer, T_EQUALS);
5188 bool hide_source_cats;
5189 if (!parse_bool (lexer, &hide_source_cats))
5191 for (size_t i = 0; i < n_pcs; i++)
5192 pcs[i]->hide_source_cats = hide_source_cats;
5196 lex_error_expecting (lexer, "LABEL", "FORMAT", "HIDESOURCECATS");
5209 put_strftime (struct string *out, time_t now, const char *format)
5211 const struct tm *tm = localtime (&now);
5213 strftime (value, sizeof value, format, tm);
5214 ds_put_cstr (out, value);
5218 skip_prefix (struct substring *s, struct substring prefix)
5220 if (ss_starts_with (*s, prefix))
5222 ss_advance (s, prefix.length);
5230 put_table_expression (struct string *out, struct lexer *lexer,
5231 struct dictionary *dict, int expr_start, int expr_end)
5234 for (int ofs = expr_start; ofs < expr_end; ofs++)
5236 const struct token *t = lex_ofs_token (lexer, ofs);
5237 if (t->type == T_LBRACK)
5239 else if (t->type == T_RBRACK && nest > 0)
5245 else if (t->type == T_ID)
5247 const struct variable *var
5248 = dict_lookup_var (dict, t->string.string);
5249 const char *label = var ? var_get_label (var) : NULL;
5250 ds_put_cstr (out, label ? label : t->string.string);
5254 if (ofs != expr_start && t->type != T_RPAREN && ds_last (out) != ' ')
5255 ds_put_byte (out, ' ');
5257 char *repr = lex_ofs_representation (lexer, ofs, ofs);
5258 ds_put_cstr (out, repr);
5261 if (ofs + 1 != expr_end && t->type != T_LPAREN)
5262 ds_put_byte (out, ' ');
5268 put_title_text (struct string *out, struct substring in, time_t now,
5269 struct lexer *lexer, struct dictionary *dict,
5270 int expr_start, int expr_end)
5274 size_t chunk = ss_find_byte (in, ')');
5275 ds_put_substring (out, ss_head (in, chunk));
5276 ss_advance (&in, chunk);
5277 if (ss_is_empty (in))
5280 if (skip_prefix (&in, ss_cstr (")DATE")))
5281 put_strftime (out, now, "%x");
5282 else if (skip_prefix (&in, ss_cstr (")TIME")))
5283 put_strftime (out, now, "%X");
5284 else if (skip_prefix (&in, ss_cstr (")TABLE")))
5285 put_table_expression (out, lexer, dict, expr_start, expr_end);
5288 ds_put_byte (out, ')');
5289 ss_advance (&in, 1);
5295 cmd_ctables (struct lexer *lexer, struct dataset *ds)
5297 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
5298 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
5299 enum settings_value_show tvars = settings_get_show_variables ();
5300 for (size_t i = 0; i < n_vars; i++)
5301 vlabels[i] = (enum ctables_vlabel) tvars;
5303 struct pivot_table_look *look = pivot_table_look_unshare (
5304 pivot_table_look_ref (pivot_table_look_get_default ()));
5305 look->omit_empty = false;
5307 struct ctables *ct = xmalloc (sizeof *ct);
5308 *ct = (struct ctables) {
5309 .dict = dataset_dict (ds),
5311 .ctables_formats = FMT_SETTINGS_INIT,
5313 .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
5316 time_t now = time (NULL);
5321 const char *dot_string;
5322 const char *comma_string;
5324 static const struct ctf ctfs[4] = {
5325 { CTEF_NEGPAREN, "(,,,)", "(...)" },
5326 { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
5327 { CTEF_PAREN, "-,(,),", "-.(.)." },
5328 { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
5330 bool is_dot = settings_get_fmt_settings ()->decimal == '.';
5331 for (size_t i = 0; i < 4; i++)
5333 const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
5334 fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
5335 fmt_number_style_from_string (s));
5338 if (!lex_force_match (lexer, T_SLASH))
5341 while (!lex_match_id (lexer, "TABLE"))
5343 if (lex_match_id (lexer, "FORMAT"))
5345 double widths[2] = { SYSMIS, SYSMIS };
5346 double units_per_inch = 72.0;
5348 while (lex_token (lexer) != T_SLASH)
5350 if (lex_match_id (lexer, "MINCOLWIDTH"))
5352 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
5355 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
5357 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
5360 else if (lex_match_id (lexer, "UNITS"))
5362 lex_match (lexer, T_EQUALS);
5363 if (lex_match_id (lexer, "POINTS"))
5364 units_per_inch = 72.0;
5365 else if (lex_match_id (lexer, "INCHES"))
5366 units_per_inch = 1.0;
5367 else if (lex_match_id (lexer, "CM"))
5368 units_per_inch = 2.54;
5371 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
5375 else if (lex_match_id (lexer, "EMPTY"))
5380 lex_match (lexer, T_EQUALS);
5381 if (lex_match_id (lexer, "ZERO"))
5383 /* Nothing to do. */
5385 else if (lex_match_id (lexer, "BLANK"))
5386 ct->zero = xstrdup ("");
5387 else if (lex_force_string (lexer))
5389 ct->zero = ss_xstrdup (lex_tokss (lexer));
5395 else if (lex_match_id (lexer, "MISSING"))
5397 lex_match (lexer, T_EQUALS);
5398 if (!lex_force_string (lexer))
5402 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
5403 ? ss_xstrdup (lex_tokss (lexer))
5409 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
5410 "UNITS", "EMPTY", "MISSING");
5415 if (widths[0] != SYSMIS && widths[1] != SYSMIS
5416 && widths[0] > widths[1])
5418 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
5422 for (size_t i = 0; i < 2; i++)
5423 if (widths[i] != SYSMIS)
5425 int *wr = ct->look->width_ranges[TABLE_HORZ];
5426 wr[i] = widths[i] / units_per_inch * 96.0;
5431 else if (lex_match_id (lexer, "VLABELS"))
5433 if (!lex_force_match_id (lexer, "VARIABLES"))
5435 lex_match (lexer, T_EQUALS);
5437 struct variable **vars;
5439 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
5443 if (!lex_force_match_id (lexer, "DISPLAY"))
5448 lex_match (lexer, T_EQUALS);
5450 enum ctables_vlabel vlabel;
5451 if (lex_match_id (lexer, "DEFAULT"))
5452 vlabel = (enum ctables_vlabel) settings_get_show_variables ();
5453 else if (lex_match_id (lexer, "NAME"))
5455 else if (lex_match_id (lexer, "LABEL"))
5456 vlabel = CTVL_LABEL;
5457 else if (lex_match_id (lexer, "BOTH"))
5459 else if (lex_match_id (lexer, "NONE"))
5463 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
5469 for (size_t i = 0; i < n_vars; i++)
5470 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
5473 else if (lex_match_id (lexer, "MRSETS"))
5475 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
5477 lex_match (lexer, T_EQUALS);
5478 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
5481 else if (lex_match_id (lexer, "SMISSING"))
5483 if (lex_match_id (lexer, "VARIABLE"))
5484 ct->smissing_listwise = false;
5485 else if (lex_match_id (lexer, "LISTWISE"))
5486 ct->smissing_listwise = true;
5489 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
5493 else if (lex_match_id (lexer, "PCOMPUTE"))
5495 if (!ctables_parse_pcompute (lexer, dataset_dict (ds), ct))
5498 else if (lex_match_id (lexer, "PPROPERTIES"))
5500 if (!ctables_parse_pproperties (lexer, ct))
5503 else if (lex_match_id (lexer, "WEIGHT"))
5505 if (!lex_force_match_id (lexer, "VARIABLE"))
5507 lex_match (lexer, T_EQUALS);
5508 ct->e_weight = parse_variable (lexer, dataset_dict (ds));
5512 else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
5514 if (lex_match_id (lexer, "COUNT"))
5516 lex_match (lexer, T_EQUALS);
5517 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT",
5520 ct->hide_threshold = lex_integer (lexer);
5523 else if (ct->hide_threshold == 0)
5524 ct->hide_threshold = 5;
5528 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
5529 "SMISSING", "PCOMPUTE", "PPROPERTIES",
5530 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
5534 if (!lex_force_match (lexer, T_SLASH))
5538 size_t allocated_tables = 0;
5541 if (ct->n_tables >= allocated_tables)
5542 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
5543 sizeof *ct->tables);
5545 struct ctables_category *cat = xmalloc (sizeof *cat);
5546 *cat = (struct ctables_category) {
5548 .include_missing = false,
5549 .sort_ascending = true,
5552 struct ctables_categories *c = xmalloc (sizeof *c);
5553 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
5554 *c = (struct ctables_categories) {
5561 struct ctables_categories **categories = xnmalloc (n_vars,
5562 sizeof *categories);
5563 for (size_t i = 0; i < n_vars; i++)
5566 struct ctables_table *t = xmalloc (sizeof *t);
5567 *t = (struct ctables_table) {
5569 .slabels_axis = PIVOT_AXIS_COLUMN,
5570 .slabels_visible = true,
5571 .clabels_values_map = HMAP_INITIALIZER (t->clabels_values_map),
5573 [PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW,
5574 [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
5575 [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
5577 .clabels_from_axis = PIVOT_AXIS_LAYER,
5578 .categories = categories,
5579 .n_categories = n_vars,
5582 ct->tables[ct->n_tables++] = t;
5584 lex_match (lexer, T_EQUALS);
5585 int expr_start = lex_ofs (lexer);
5586 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
5588 if (lex_match (lexer, T_BY))
5590 if (!ctables_axis_parse (lexer, dataset_dict (ds),
5591 ct, t, PIVOT_AXIS_COLUMN))
5594 if (lex_match (lexer, T_BY))
5596 if (!ctables_axis_parse (lexer, dataset_dict (ds),
5597 ct, t, PIVOT_AXIS_LAYER))
5601 int expr_end = lex_ofs (lexer);
5603 if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
5604 && !t->axes[PIVOT_AXIS_LAYER])
5606 lex_error (lexer, _("At least one variable must be specified."));
5610 const struct ctables_axis *scales[PIVOT_N_AXES];
5611 size_t n_scales = 0;
5612 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5614 scales[a] = find_scale (t->axes[a]);
5620 msg (SE, _("Scale variables may appear only on one axis."));
5621 if (scales[PIVOT_AXIS_ROW])
5622 msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
5623 _("This scale variable appears on the rows axis."));
5624 if (scales[PIVOT_AXIS_COLUMN])
5625 msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
5626 _("This scale variable appears on the columns axis."));
5627 if (scales[PIVOT_AXIS_LAYER])
5628 msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
5629 _("This scale variable appears on the layer axis."));
5633 const struct ctables_axis *summaries[PIVOT_N_AXES];
5634 size_t n_summaries = 0;
5635 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5637 summaries[a] = (scales[a]
5639 : find_categorical_summary_spec (t->axes[a]));
5643 if (n_summaries > 1)
5645 msg (SE, _("Summaries may appear only on one axis."));
5646 if (summaries[PIVOT_AXIS_ROW])
5647 msg_at (SN, summaries[PIVOT_AXIS_ROW]->loc,
5648 _("This variable on the rows axis has a summary."));
5649 if (summaries[PIVOT_AXIS_COLUMN])
5650 msg_at (SN, summaries[PIVOT_AXIS_COLUMN]->loc,
5651 _("This variable on the columns axis has a summary."));
5652 if (summaries[PIVOT_AXIS_LAYER])
5653 msg_at (SN, summaries[PIVOT_AXIS_LAYER]->loc,
5654 _("This variable on the layers axis has a summary."));
5657 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5658 if (n_summaries ? summaries[a] : t->axes[a])
5660 t->summary_axis = a;
5664 if (lex_token (lexer) == T_ENDCMD)
5666 if (!ctables_prepare_table (t))
5670 if (!lex_force_match (lexer, T_SLASH))
5673 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
5675 if (lex_match_id (lexer, "SLABELS"))
5677 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5679 if (lex_match_id (lexer, "POSITION"))
5681 lex_match (lexer, T_EQUALS);
5682 if (lex_match_id (lexer, "COLUMN"))
5683 t->slabels_axis = PIVOT_AXIS_COLUMN;
5684 else if (lex_match_id (lexer, "ROW"))
5685 t->slabels_axis = PIVOT_AXIS_ROW;
5686 else if (lex_match_id (lexer, "LAYER"))
5687 t->slabels_axis = PIVOT_AXIS_LAYER;
5690 lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
5694 else if (lex_match_id (lexer, "VISIBLE"))
5696 lex_match (lexer, T_EQUALS);
5697 if (!parse_bool (lexer, &t->slabels_visible))
5702 lex_error_expecting (lexer, "POSITION", "VISIBLE");
5707 else if (lex_match_id (lexer, "CLABELS"))
5709 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5711 if (lex_match_id (lexer, "AUTO"))
5713 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
5714 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
5716 else if (lex_match_id (lexer, "ROWLABELS"))
5718 lex_match (lexer, T_EQUALS);
5719 if (lex_match_id (lexer, "OPPOSITE"))
5720 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
5721 else if (lex_match_id (lexer, "LAYER"))
5722 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
5725 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5729 else if (lex_match_id (lexer, "COLLABELS"))
5731 lex_match (lexer, T_EQUALS);
5732 if (lex_match_id (lexer, "OPPOSITE"))
5733 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
5734 else if (lex_match_id (lexer, "LAYER"))
5735 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
5738 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5744 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
5750 else if (lex_match_id (lexer, "CRITERIA"))
5752 if (!lex_force_match_id (lexer, "CILEVEL"))
5754 lex_match (lexer, T_EQUALS);
5756 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
5758 t->cilevel = lex_number (lexer);
5761 else if (lex_match_id (lexer, "CATEGORIES"))
5763 if (!ctables_table_parse_categories (lexer, dataset_dict (ds),
5767 else if (lex_match_id (lexer, "TITLES"))
5772 if (lex_match_id (lexer, "CAPTION"))
5773 textp = &t->caption;
5774 else if (lex_match_id (lexer, "CORNER"))
5776 else if (lex_match_id (lexer, "TITLE"))
5780 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
5783 lex_match (lexer, T_EQUALS);
5785 struct string s = DS_EMPTY_INITIALIZER;
5786 while (lex_is_string (lexer))
5788 if (!ds_is_empty (&s))
5789 ds_put_byte (&s, ' ');
5790 put_title_text (&s, lex_tokss (lexer), now,
5791 lexer, dataset_dict (ds),
5792 expr_start, expr_end);
5796 *textp = ds_steal_cstr (&s);
5798 while (lex_token (lexer) != T_SLASH
5799 && lex_token (lexer) != T_ENDCMD);
5801 else if (lex_match_id (lexer, "SIGTEST"))
5805 t->chisq = xmalloc (sizeof *t->chisq);
5806 *t->chisq = (struct ctables_chisq) {
5808 .include_mrsets = true,
5809 .all_visible = true,
5815 if (lex_match_id (lexer, "TYPE"))
5817 lex_match (lexer, T_EQUALS);
5818 if (!lex_force_match_id (lexer, "CHISQUARE"))
5821 else if (lex_match_id (lexer, "ALPHA"))
5823 lex_match (lexer, T_EQUALS);
5824 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
5826 t->chisq->alpha = lex_number (lexer);
5829 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5831 lex_match (lexer, T_EQUALS);
5832 if (parse_bool (lexer, &t->chisq->include_mrsets))
5835 else if (lex_match_id (lexer, "CATEGORIES"))
5837 lex_match (lexer, T_EQUALS);
5838 if (lex_match_id (lexer, "ALLVISIBLE"))
5839 t->chisq->all_visible = true;
5840 else if (lex_match_id (lexer, "SUBTOTALS"))
5841 t->chisq->all_visible = false;
5844 lex_error_expecting (lexer,
5845 "ALLVISIBLE", "SUBTOTALS");
5851 lex_error_expecting (lexer, "TYPE", "ALPHA",
5852 "INCLUDEMRSETS", "CATEGORIES");
5856 while (lex_token (lexer) != T_SLASH
5857 && lex_token (lexer) != T_ENDCMD);
5859 else if (lex_match_id (lexer, "COMPARETEST"))
5863 t->pairwise = xmalloc (sizeof *t->pairwise);
5864 *t->pairwise = (struct ctables_pairwise) {
5866 .alpha = { .05, .05 },
5867 .adjust = BONFERRONI,
5868 .include_mrsets = true,
5869 .meansvariance_allcats = true,
5870 .all_visible = true,
5879 if (lex_match_id (lexer, "TYPE"))
5881 lex_match (lexer, T_EQUALS);
5882 if (lex_match_id (lexer, "PROP"))
5883 t->pairwise->type = PROP;
5884 else if (lex_match_id (lexer, "MEAN"))
5885 t->pairwise->type = MEAN;
5888 lex_error_expecting (lexer, "PROP", "MEAN");
5892 else if (lex_match_id (lexer, "ALPHA"))
5894 lex_match (lexer, T_EQUALS);
5896 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5898 double a0 = lex_number (lexer);
5901 lex_match (lexer, T_COMMA);
5902 if (lex_is_number (lexer))
5904 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5906 double a1 = lex_number (lexer);
5909 t->pairwise->alpha[0] = MIN (a0, a1);
5910 t->pairwise->alpha[1] = MAX (a0, a1);
5913 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
5915 else if (lex_match_id (lexer, "ADJUST"))
5917 lex_match (lexer, T_EQUALS);
5918 if (lex_match_id (lexer, "BONFERRONI"))
5919 t->pairwise->adjust = BONFERRONI;
5920 else if (lex_match_id (lexer, "BH"))
5921 t->pairwise->adjust = BH;
5922 else if (lex_match_id (lexer, "NONE"))
5923 t->pairwise->adjust = 0;
5926 lex_error_expecting (lexer, "BONFERRONI", "BH",
5931 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5933 lex_match (lexer, T_EQUALS);
5934 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
5937 else if (lex_match_id (lexer, "MEANSVARIANCE"))
5939 lex_match (lexer, T_EQUALS);
5940 if (lex_match_id (lexer, "ALLCATS"))
5941 t->pairwise->meansvariance_allcats = true;
5942 else if (lex_match_id (lexer, "TESTEDCATS"))
5943 t->pairwise->meansvariance_allcats = false;
5946 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
5950 else if (lex_match_id (lexer, "CATEGORIES"))
5952 lex_match (lexer, T_EQUALS);
5953 if (lex_match_id (lexer, "ALLVISIBLE"))
5954 t->pairwise->all_visible = true;
5955 else if (lex_match_id (lexer, "SUBTOTALS"))
5956 t->pairwise->all_visible = false;
5959 lex_error_expecting (lexer, "ALLVISIBLE",
5964 else if (lex_match_id (lexer, "MERGE"))
5966 lex_match (lexer, T_EQUALS);
5967 if (!parse_bool (lexer, &t->pairwise->merge))
5970 else if (lex_match_id (lexer, "STYLE"))
5972 lex_match (lexer, T_EQUALS);
5973 if (lex_match_id (lexer, "APA"))
5974 t->pairwise->apa_style = true;
5975 else if (lex_match_id (lexer, "SIMPLE"))
5976 t->pairwise->apa_style = false;
5979 lex_error_expecting (lexer, "APA", "SIMPLE");
5983 else if (lex_match_id (lexer, "SHOWSIG"))
5985 lex_match (lexer, T_EQUALS);
5986 if (!parse_bool (lexer, &t->pairwise->show_sig))
5991 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
5992 "INCLUDEMRSETS", "MEANSVARIANCE",
5993 "CATEGORIES", "MERGE", "STYLE",
5998 while (lex_token (lexer) != T_SLASH
5999 && lex_token (lexer) != T_ENDCMD);
6003 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
6004 "CRITERIA", "CATEGORIES", "TITLES",
6005 "SIGTEST", "COMPARETEST");
6009 if (!lex_match (lexer, T_SLASH))
6013 if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
6014 && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
6016 msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
6020 if (!ctables_prepare_table (t))
6023 while (lex_token (lexer) != T_ENDCMD);
6025 bool ok = ctables_execute (ds, ct);
6026 ctables_destroy (ct);
6027 return ok ? CMD_SUCCESS : CMD_FAILURE;
6030 ctables_destroy (ct);