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 /* All variables (unweighted.) */ \
97 S(CTSF_UCOUNT, "UCOUNT", N_("Unweighted Count"), CTF_COUNT, CTFA_ALL) \
98 S(CTSF_UROWPCT_COUNT, "UROWPCT.COUNT", N_("Unweighted Row %"), CTF_PERCENT, CTFA_ALL) \
99 S(CTSF_UCOLPCT_COUNT, "UCOLPCT.COUNT", N_("Unweighted Column %"), CTF_PERCENT, CTFA_ALL) \
100 S(CTSF_UTABLEPCT_COUNT, "UTABLEPCT.COUNT", N_("Unweighted Table %"), CTF_PERCENT, CTFA_ALL) \
101 S(CTSF_USUBTABLEPCT_COUNT, "USUBTABLEPCT.COUNT", N_("Unweighted Subtable %"), CTF_PERCENT, CTFA_ALL) \
102 S(CTSF_ULAYERPCT_COUNT, "ULAYERPCT.COUNT", N_("Unweighted Layer %"), CTF_PERCENT, CTFA_ALL) \
103 S(CTSF_ULAYERROWPCT_COUNT, "ULAYERROWPCT.COUNT", N_("Unweighted Layer Row %"), CTF_PERCENT, CTFA_ALL) \
104 S(CTSF_ULAYERCOLPCT_COUNT, "ULAYERCOLPCT.COUNT", N_("Unweighted Layer Column %"), CTF_PERCENT, CTFA_ALL) \
105 S(CTSF_UROWPCT_VALIDN, "UROWPCT.VALIDN", N_("Unweighted Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
106 S(CTSF_UCOLPCT_VALIDN, "UCOLPCT.VALIDN", N_("Unweighted Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
107 S(CTSF_UTABLEPCT_VALIDN, "UTABLEPCT.VALIDN", N_("Unweighted Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
108 S(CTSF_USUBTABLEPCT_VALIDN, "USUBTABLEPCT.VALIDN", N_("Unweighted Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
109 S(CTSF_ULAYERPCT_VALIDN, "ULAYERPCT.VALIDN", N_("Unweighted Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
110 S(CTSF_ULAYERROWPCT_VALIDN, "ULAYERROWPCT.VALIDN", N_("Unweighted Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
111 S(CTSF_ULAYERCOLPCT_VALIDN, "ULAYERCOLPCT.VALIDN", N_("Unweighted Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
112 S(CTSF_UROWPCT_TOTALN, "UROWPCT.TOTALN", N_("Unweighted Row Total N %"), CTF_PERCENT, CTFA_ALL) \
113 S(CTSF_UCOLPCT_TOTALN, "UCOLPCT.TOTALN", N_("Unweighted Column Total N %"), CTF_PERCENT, CTFA_ALL) \
114 S(CTSF_UTABLEPCT_TOTALN, "UTABLEPCT.TOTALN", N_("Unweighted Table Total N %"), CTF_PERCENT, CTFA_ALL) \
115 S(CTSF_USUBTABLEPCT_TOTALN, "USUBTABLEPCT.TOTALN", N_("Unweighted Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
116 S(CTSF_ULAYERPCT_TOTALN, "ULAYERPCT.TOTALN", N_("Unweighted Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
117 S(CTSF_ULAYERROWPCT_TOTALN, "ULAYERROWPCT.TOTALN", N_("Unweighted Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
118 S(CTSF_ULAYERCOLPCT_TOTALN, "ULAYERCOLPCT.TOTALN", N_("Unweighted Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
120 /* Scale variables, totals, and subtotals. */ \
121 S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE) \
122 S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE) \
123 S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL, CTFA_SCALE) \
124 S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL, CTFA_SCALE) \
125 S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL, CTFA_SCALE) \
126 S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL, CTFA_SCALE) \
127 S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL, CTFA_SCALE) \
128 S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL, CTFA_SCALE) \
129 S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
130 S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
131 S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL, CTFA_SCALE) \
132 S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT, CTFA_SCALE) \
133 S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT, CTFA_SCALE) \
134 S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT, CTFA_SCALE) \
135 S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT, CTFA_SCALE) \
136 S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL, CTFA_SCALE) \
137 S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
138 S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
139 S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
140 S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
141 S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
142 S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
143 S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
145 /* Scale variables, totals, and subtotals (unweighted). */ \
146 S(CTSF_UMEAN, "UMEAN", N_("Unweighted Mean"), CTF_GENERAL, CTFA_SCALE) \
147 S(CTSF_UMEDIAN, "UMEDIAN", N_("Unweighted Median"), CTF_GENERAL, CTFA_SCALE) \
148 S(CTSF_UMISSING, "UMISSING", N_("Unweighted Missing"), CTF_GENERAL, CTFA_SCALE) \
149 S(CTSF_UMODE, "UMODE", N_("Unweighted Mode"), CTF_GENERAL, CTFA_SCALE) \
150 S(CTSF_UPTILE, "UPTILE", N_("Unweighted Percentile"), CTF_GENERAL, CTFA_SCALE) \
151 S(CTSF_USEMEAN, "USEMEAN", N_("Unweighted Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
152 S(CTSF_USTDDEV, "USTDDEV", N_("Unweighted Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
153 S(CTSF_USUM, "USUM", N_("Unweighted Sum"), CTF_GENERAL, CTFA_SCALE) \
154 S(CSTF_UTOTALN, "UTOTALN", N_("Unweighted Total N"), CTF_COUNT, CTFA_SCALE) \
155 S(CTSF_UVALIDN, "UVALIDN", N_("Unweighted Valid N"), CTF_COUNT, CTFA_SCALE) \
156 S(CTSF_UVARIANCE, "UVARIANCE", N_("Unweighted Variance"), CTF_GENERAL, CTFA_SCALE) \
157 S(CTSF_UROWPCT_SUM, "UROWPCT.SUM", N_("Unweighted Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
158 S(CTSF_UCOLPCT_SUM, "UCOLPCT.SUM", N_("Unweighted Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
159 S(CTSF_UTABLEPCT_SUM, "UTABLEPCT.SUM", N_("Unweighted Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
160 S(CTSF_USUBTABLEPCT_SUM, "USUBTABLEPCT.SUM", N_("Unweighted Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
161 S(CTSF_ULAYERPCT_SUM, "ULAYERPCT.SUM", N_("Unweighted Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
162 S(CTSF_ULAYERROWPCT_SUM, "ULAYERROWPCT.SUM", N_("Unweighted Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
163 S(CTSF_ULAYERCOLPCT_SUM, "ULAYERCOLPCT.SUM", N_("Unweighted Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
165 #if 0 /* Multiple response sets not yet implemented. */
166 S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
167 S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
168 S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
169 S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT, CTFA_MRSETS) \
170 S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT, CTFA_MRSETS) \
171 S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT, CTFA_MRSETS) \
172 S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
173 S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
174 S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
175 S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
176 S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
177 S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
178 S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
179 S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
180 S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
181 S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
182 S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
183 S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
184 S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
185 S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
186 S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
187 S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
190 enum ctables_summary_function
192 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) ENUM,
198 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) +1
199 N_CTSF_FUNCTIONS = SUMMARIES
203 static bool ctables_summary_function_is_count (enum ctables_summary_function);
205 enum ctables_domain_type
207 /* Within a section, where stacked variables divide one section from
209 CTDT_TABLE, /* All layers of a whole section. */
210 CTDT_LAYER, /* One layer within a section. */
211 CTDT_LAYERROW, /* Row in one layer within a section. */
212 CTDT_LAYERCOL, /* Column in one layer within a section. */
214 /* Within a subtable, where a subtable pairs an innermost row variable with
215 an innermost column variable within a single layer. */
216 CTDT_SUBTABLE, /* Whole subtable. */
217 CTDT_ROW, /* Row within a subtable. */
218 CTDT_COL, /* Column within a subtable. */
222 struct ctables_domain
224 struct hmap_node node;
226 const struct ctables_cell *example;
228 double d_valid; /* Dictionary weight. */
231 double e_valid; /* Effective weight */
234 double u_valid; /* Unweighted. */
237 struct ctables_sum *sums;
246 enum ctables_summary_variant
255 /* In struct ctables_section's 'cells' hmap. Indexed by all the values in
256 all the axes (except the scalar variable, if any). */
257 struct hmap_node node;
259 /* The domains that contain this cell. */
260 uint32_t omit_domains;
261 struct ctables_domain *domains[N_CTDTS];
266 enum ctables_summary_variant sv;
268 struct ctables_cell_axis
270 struct ctables_cell_value
272 const struct ctables_category *category;
280 union ctables_summary *summaries;
287 const struct dictionary *dict;
288 struct pivot_table_look *look;
290 /* CTABLES has a number of extra formats that we implement via custom
291 currency specifications on an alternate fmt_settings. */
292 #define CTEF_NEGPAREN FMT_CCA
293 #define CTEF_NEQUAL FMT_CCB
294 #define CTEF_PAREN FMT_CCC
295 #define CTEF_PCTPAREN FMT_CCD
296 struct fmt_settings ctables_formats;
298 /* If this is NULL, zeros are displayed using the normal print format.
299 Otherwise, this string is displayed. */
302 /* If this is NULL, missing values are displayed using the normal print
303 format. Otherwise, this string is displayed. */
306 /* Indexed by variable dictionary index. */
307 enum ctables_vlabel *vlabels;
309 struct hmap postcomputes; /* Contains "struct ctables_postcompute"s. */
311 bool mrsets_count_duplicates; /* MRSETS. */
312 bool smissing_listwise; /* SMISSING. */
313 struct variable *e_weight; /* WEIGHT. */
314 int hide_threshold; /* HIDESMALLCOUNTS. */
316 struct ctables_table **tables;
320 static struct ctables_postcompute *ctables_find_postcompute (struct ctables *,
323 struct ctables_postcompute
325 struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
326 char *name; /* Name, without leading &. */
328 struct msg_location *location; /* Location of definition. */
329 struct ctables_pcexpr *expr;
331 struct ctables_summary_spec_set *specs;
332 bool hide_source_cats;
335 struct ctables_pcexpr
345 enum ctables_postcompute_op
348 CTPO_CONSTANT, /* 5 */
349 CTPO_CAT_NUMBER, /* [5] */
350 CTPO_CAT_STRING, /* ["STRING"] */
351 CTPO_CAT_NRANGE, /* [LO THRU 5] */
352 CTPO_CAT_SRANGE, /* ["A" THRU "B"] */
353 CTPO_CAT_MISSING, /* MISSING */
354 CTPO_CAT_OTHERNM, /* OTHERNM */
355 CTPO_CAT_SUBTOTAL, /* SUBTOTAL */
356 CTPO_CAT_TOTAL, /* TOTAL */
370 /* CTPO_CAT_NUMBER. */
373 /* CTPO_CAT_STRING, in dictionary encoding. */
374 struct substring string;
376 /* CTPO_CAT_NRANGE. */
379 /* CTPO_CAT_SRANGE. */
380 struct substring srange[2];
382 /* CTPO_CAT_SUBTOTAL. */
383 size_t subtotal_index;
385 /* Two elements: CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW.
386 One element: CTPO_NEG. */
387 struct ctables_pcexpr *subs[2];
390 /* Source location. */
391 struct msg_location *location;
394 static void ctables_pcexpr_destroy (struct ctables_pcexpr *);
395 static struct ctables_pcexpr *ctables_pcexpr_allocate_binary (
396 enum ctables_postcompute_op, struct ctables_pcexpr *sub0,
397 struct ctables_pcexpr *sub1);
399 struct ctables_summary_spec_set
401 struct ctables_summary_spec *specs;
405 /* The variable to which the summary specs are applied. */
406 struct variable *var;
408 /* Whether the variable to which the summary specs are applied is a scale
409 variable for the purpose of summarization.
411 (VALIDN and TOTALN act differently for summarizing scale and categorical
415 /* If any of these optional additional scale variables are missing, then
416 treat 'var' as if it's missing too. This is for implementing
417 SMISSING=LISTWISE. */
418 struct variable **listwise_vars;
419 size_t n_listwise_vars;
422 static void ctables_summary_spec_set_clone (struct ctables_summary_spec_set *,
423 const struct ctables_summary_spec_set *);
424 static void ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *);
426 /* A nested sequence of variables, e.g. a > b > c. */
429 struct variable **vars;
432 size_t *domains[N_CTDTS];
433 size_t n_domains[N_CTDTS];
436 struct ctables_summary_spec_set specs[N_CSVS];
439 /* A stack of nestings, e.g. nest1 + nest2 + ... + nestN. */
442 struct ctables_nest *nests;
448 struct hmap_node node;
453 struct ctables_occurrence
455 struct hmap_node node;
459 struct ctables_section
461 struct ctables_table *table;
462 struct ctables_nest *nests[PIVOT_N_AXES];
463 struct hmap *occurrences[PIVOT_N_AXES];
464 struct hmap cells; /* Contains "struct ctable_cell"s. */
465 struct hmap domains[N_CTDTS]; /* Contains "struct ctable_domain"s. */
470 struct ctables *ctables;
471 struct ctables_axis *axes[PIVOT_N_AXES];
472 struct ctables_stack stacks[PIVOT_N_AXES];
473 struct ctables_section *sections;
475 enum pivot_axis_type summary_axis;
476 struct ctables_summary_spec_set summary_specs;
477 struct variable **sum_vars;
480 const struct variable *clabels_example;
481 struct hmap clabels_values_map;
482 struct ctables_value **clabels_values;
483 size_t n_clabels_values;
485 enum pivot_axis_type slabels_axis;
486 bool slabels_visible;
488 /* The innermost category labels for axis 'a' appear on axis label_axis[a].
490 Most commonly, label_axis[a] == a, and in particular we always have
491 label_axis{PIVOT_AXIS_LAYER] == PIVOT_AXIS_LAYER.
493 If ROWLABELS or COLLABELS is specified, then one of
494 label_axis[PIVOT_AXIS_ROW] or label_axis[PIVOT_AXIS_COLUMN] can be the
495 opposite axis or PIVOT_AXIS_LAYER. Only one of them will differ.
497 enum pivot_axis_type label_axis[PIVOT_N_AXES];
498 enum pivot_axis_type clabels_from_axis;
500 /* Indexed by variable dictionary index. */
501 struct ctables_categories **categories;
510 struct ctables_chisq *chisq;
511 struct ctables_pairwise *pairwise;
514 struct ctables_categories
517 struct ctables_category *cats;
522 struct ctables_category
524 enum ctables_category_type
526 /* Explicit category lists. */
529 CCT_NRANGE, /* Numerical range. */
530 CCT_SRANGE, /* String range. */
535 /* Totals and subtotals. */
539 /* Implicit category lists. */
544 /* For contributing to TOTALN. */
545 CCT_EXCLUDED_MISSING,
549 struct ctables_category *subtotal;
555 double number; /* CCT_NUMBER. */
556 struct substring string; /* CCT_STRING, in dictionary encoding. */
557 double nrange[2]; /* CCT_NRANGE. */
558 struct substring srange[2]; /* CCT_SRANGE. */
562 char *total_label; /* CCT_SUBTOTAL, CCT_TOTAL. */
563 bool hide_subcategories; /* CCT_SUBTOTAL. */
566 const struct ctables_postcompute *pc; /* CCT_POSTCOMPUTE. */
568 /* CCT_VALUE, CCT_LABEL, CCT_FUNCTION. */
571 bool include_missing;
575 enum ctables_summary_function sort_function;
576 struct variable *sort_var;
581 /* Source location. This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
582 CCT_FUNCTION, CCT_EXCLUDED_MISSING. */
583 struct msg_location *location;
587 ctables_category_uninit (struct ctables_category *cat)
598 case CCT_POSTCOMPUTE:
602 ss_dealloc (&cat->string);
606 ss_dealloc (&cat->srange[0]);
607 ss_dealloc (&cat->srange[1]);
612 free (cat->total_label);
620 case CCT_EXCLUDED_MISSING:
626 nullable_substring_equal (const struct substring *a,
627 const struct substring *b)
629 return !a->string ? !b->string : b->string && ss_equals (*a, *b);
633 ctables_category_equal (const struct ctables_category *a,
634 const struct ctables_category *b)
636 if (a->type != b->type)
642 return a->number == b->number;
645 return ss_equals (a->string, b->string);
648 return a->nrange[0] == b->nrange[0] && a->nrange[1] == b->nrange[1];
651 return (nullable_substring_equal (&a->srange[0], &b->srange[0])
652 && nullable_substring_equal (&a->srange[1], &b->srange[1]));
658 case CCT_POSTCOMPUTE:
659 return a->pc == b->pc;
663 return !strcmp (a->total_label, b->total_label);
668 return (a->include_missing == b->include_missing
669 && a->sort_ascending == b->sort_ascending
670 && a->sort_function == b->sort_function
671 && a->sort_var == b->sort_var
672 && a->percentile == b->percentile);
674 case CCT_EXCLUDED_MISSING:
682 ctables_categories_unref (struct ctables_categories *c)
687 assert (c->n_refs > 0);
691 for (size_t i = 0; i < c->n_cats; i++)
692 ctables_category_uninit (&c->cats[i]);
698 ctables_categories_equal (const struct ctables_categories *a,
699 const struct ctables_categories *b)
701 if (a->n_cats != b->n_cats || a->show_empty != b->show_empty)
704 for (size_t i = 0; i < a->n_cats; i++)
705 if (!ctables_category_equal (&a->cats[i], &b->cats[i]))
711 /* Chi-square test (SIGTEST). */
719 /* Pairwise comparison test (COMPARETEST). */
720 struct ctables_pairwise
722 enum { PROP, MEAN } type;
725 bool meansvariance_allcats;
727 enum { BONFERRONI = 1, BH } adjust;
751 struct variable *var;
753 struct ctables_summary_spec_set specs[N_CSVS];
757 struct ctables_axis *subs[2];
760 struct msg_location *loc;
763 static void ctables_axis_destroy (struct ctables_axis *);
772 enum ctables_function_availability
774 CTFA_ALL, /* Any variables. */
775 CTFA_SCALE, /* Only scale variables, totals, and subtotals. */
776 CTFA_MRSETS, /* Only multiple-response sets */
779 struct ctables_summary_spec
781 enum ctables_summary_function function;
782 double percentile; /* CTSF_PTILE only. */
785 struct fmt_spec format;
786 bool is_ctables_format; /* Is 'format' one of CTEF_*? */
793 ctables_summary_spec_clone (struct ctables_summary_spec *dst,
794 const struct ctables_summary_spec *src)
797 dst->label = xstrdup_if_nonnull (src->label);
801 ctables_summary_spec_uninit (struct ctables_summary_spec *s)
808 ctables_summary_spec_set_clone (struct ctables_summary_spec_set *dst,
809 const struct ctables_summary_spec_set *src)
811 struct ctables_summary_spec *specs = xnmalloc (src->n, sizeof *specs);
812 for (size_t i = 0; i < src->n; i++)
813 ctables_summary_spec_clone (&specs[i], &src->specs[i]);
815 *dst = (struct ctables_summary_spec_set) {
820 .is_scale = src->is_scale,
825 ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *set)
827 for (size_t i = 0; i < set->n; i++)
828 ctables_summary_spec_uninit (&set->specs[i]);
833 parse_col_width (struct lexer *lexer, const char *name, double *width)
835 lex_match (lexer, T_EQUALS);
836 if (lex_match_id (lexer, "DEFAULT"))
838 else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
840 *width = lex_number (lexer);
850 parse_bool (struct lexer *lexer, bool *b)
852 if (lex_match_id (lexer, "NO"))
854 else if (lex_match_id (lexer, "YES"))
858 lex_error_expecting (lexer, "YES", "NO");
864 static enum ctables_function_availability
865 ctables_function_availability (enum ctables_summary_function f)
867 static enum ctables_function_availability availability[] = {
868 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
873 return availability[f];
877 ctables_summary_function_is_count (enum ctables_summary_function f)
883 case CTSF_ROWPCT_COUNT:
884 case CTSF_COLPCT_COUNT:
885 case CTSF_TABLEPCT_COUNT:
886 case CTSF_SUBTABLEPCT_COUNT:
887 case CTSF_LAYERPCT_COUNT:
888 case CTSF_LAYERROWPCT_COUNT:
889 case CTSF_LAYERCOLPCT_COUNT:
891 case CTSF_UROWPCT_COUNT:
892 case CTSF_UCOLPCT_COUNT:
893 case CTSF_UTABLEPCT_COUNT:
894 case CTSF_USUBTABLEPCT_COUNT:
895 case CTSF_ULAYERPCT_COUNT:
896 case CTSF_ULAYERROWPCT_COUNT:
897 case CTSF_ULAYERCOLPCT_COUNT:
900 case CTSF_ROWPCT_VALIDN:
901 case CTSF_COLPCT_VALIDN:
902 case CTSF_TABLEPCT_VALIDN:
903 case CTSF_SUBTABLEPCT_VALIDN:
904 case CTSF_LAYERPCT_VALIDN:
905 case CTSF_LAYERROWPCT_VALIDN:
906 case CTSF_LAYERCOLPCT_VALIDN:
907 case CTSF_ROWPCT_TOTALN:
908 case CTSF_COLPCT_TOTALN:
909 case CTSF_TABLEPCT_TOTALN:
910 case CTSF_SUBTABLEPCT_TOTALN:
911 case CTSF_LAYERPCT_TOTALN:
912 case CTSF_LAYERROWPCT_TOTALN:
913 case CTSF_LAYERCOLPCT_TOTALN:
930 case CTSF_ROWPCT_SUM:
931 case CTSF_COLPCT_SUM:
932 case CTSF_TABLEPCT_SUM:
933 case CTSF_SUBTABLEPCT_SUM:
934 case CTSF_LAYERPCT_SUM:
935 case CTSF_LAYERROWPCT_SUM:
936 case CTSF_LAYERCOLPCT_SUM:
937 case CTSF_UROWPCT_VALIDN:
938 case CTSF_UCOLPCT_VALIDN:
939 case CTSF_UTABLEPCT_VALIDN:
940 case CTSF_USUBTABLEPCT_VALIDN:
941 case CTSF_ULAYERPCT_VALIDN:
942 case CTSF_ULAYERROWPCT_VALIDN:
943 case CTSF_ULAYERCOLPCT_VALIDN:
944 case CTSF_UROWPCT_TOTALN:
945 case CTSF_UCOLPCT_TOTALN:
946 case CTSF_UTABLEPCT_TOTALN:
947 case CTSF_USUBTABLEPCT_TOTALN:
948 case CTSF_ULAYERPCT_TOTALN:
949 case CTSF_ULAYERROWPCT_TOTALN:
950 case CTSF_ULAYERCOLPCT_TOTALN:
962 case CTSF_UROWPCT_SUM:
963 case CTSF_UCOLPCT_SUM:
964 case CTSF_UTABLEPCT_SUM:
965 case CTSF_USUBTABLEPCT_SUM:
966 case CTSF_ULAYERPCT_SUM:
967 case CTSF_ULAYERROWPCT_SUM:
968 case CTSF_ULAYERCOLPCT_SUM:
976 parse_ctables_summary_function (struct lexer *lexer,
977 enum ctables_summary_function *f)
981 enum ctables_summary_function function;
982 struct substring name;
984 static struct pair names[] = {
985 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
986 { ENUM, SS_LITERAL_INITIALIZER (NAME) },
989 /* The .COUNT suffix may be omitted. */
990 S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
991 S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
992 S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
993 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
994 S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
995 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
996 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
1000 if (!lex_force_id (lexer))
1003 for (size_t i = 0; i < sizeof names / sizeof *names; i++)
1004 if (ss_equals_case (names[i].name, lex_tokss (lexer)))
1006 *f = names[i].function;
1011 lex_error (lexer, _("Expecting summary function name."));
1016 ctables_axis_destroy (struct ctables_axis *axis)
1024 for (size_t i = 0; i < N_CSVS; i++)
1025 ctables_summary_spec_set_uninit (&axis->specs[i]);
1030 ctables_axis_destroy (axis->subs[0]);
1031 ctables_axis_destroy (axis->subs[1]);
1034 msg_location_destroy (axis->loc);
1038 static struct ctables_axis *
1039 ctables_axis_new_nonterminal (enum ctables_axis_op op,
1040 struct ctables_axis *sub0,
1041 struct ctables_axis *sub1,
1042 struct lexer *lexer, int start_ofs)
1044 struct ctables_axis *axis = xmalloc (sizeof *axis);
1045 *axis = (struct ctables_axis) {
1047 .subs = { sub0, sub1 },
1048 .loc = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
1053 struct ctables_axis_parse_ctx
1055 struct lexer *lexer;
1056 struct dictionary *dict;
1058 struct ctables_table *t;
1061 static struct fmt_spec
1062 ctables_summary_default_format (enum ctables_summary_function function,
1063 const struct variable *var)
1065 static const enum ctables_format default_formats[] = {
1066 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
1070 switch (default_formats[function])
1073 return (struct fmt_spec) { .type = FMT_F, .w = 40 };
1076 return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
1079 return *var_get_print_format (var);
1086 static struct pivot_value *
1087 ctables_summary_label (const struct ctables_summary_spec *spec, double cilevel)
1091 static const char *default_labels[] = {
1092 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
1097 return (spec->function == CTSF_PTILE
1098 ? pivot_value_new_text_format (N_("Percentile %.2f"),
1100 : pivot_value_new_text (default_labels[spec->function]));
1104 struct substring in = ss_cstr (spec->label);
1105 struct substring target = ss_cstr (")CILEVEL");
1107 struct string out = DS_EMPTY_INITIALIZER;
1110 size_t chunk = ss_find_substring (in, target);
1111 ds_put_substring (&out, ss_head (in, chunk));
1112 ss_advance (&in, chunk);
1114 return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
1116 ss_advance (&in, target.length);
1117 ds_put_format (&out, "%g", cilevel);
1123 ctables_summary_function_name (enum ctables_summary_function function)
1125 static const char *names[] = {
1126 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = NAME,
1130 return names[function];
1134 add_summary_spec (struct ctables_axis *axis,
1135 enum ctables_summary_function function, double percentile,
1136 const char *label, const struct fmt_spec *format,
1137 bool is_ctables_format, const struct msg_location *loc,
1138 enum ctables_summary_variant sv)
1140 if (axis->op == CTAO_VAR)
1142 const char *function_name = ctables_summary_function_name (function);
1143 const char *var_name = var_get_name (axis->var);
1144 switch (ctables_function_availability (function))
1147 msg_at (SE, loc, _("Summary function %s applies only to multiple "
1148 "response sets."), function_name);
1149 msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
1158 _("Summary function %s applies only to scale variables."),
1160 msg_at (SN, axis->loc, _("'%s' is not a scale variable."),
1171 struct ctables_summary_spec_set *set = &axis->specs[sv];
1172 if (set->n >= set->allocated)
1173 set->specs = x2nrealloc (set->specs, &set->allocated,
1174 sizeof *set->specs);
1176 struct ctables_summary_spec *dst = &set->specs[set->n++];
1177 *dst = (struct ctables_summary_spec) {
1178 .function = function,
1179 .percentile = percentile,
1180 .label = xstrdup_if_nonnull (label),
1181 .format = (format ? *format
1182 : ctables_summary_default_format (function, axis->var)),
1183 .is_ctables_format = is_ctables_format,
1189 for (size_t i = 0; i < 2; i++)
1190 if (!add_summary_spec (axis->subs[i], function, percentile, label,
1191 format, is_ctables_format, loc, sv))
1197 static struct ctables_axis *ctables_axis_parse_stack (
1198 struct ctables_axis_parse_ctx *);
1201 static struct ctables_axis *
1202 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
1204 if (lex_match (ctx->lexer, T_LPAREN))
1206 struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
1207 if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
1209 ctables_axis_destroy (sub);
1215 if (!lex_force_id (ctx->lexer))
1218 int start_ofs = lex_ofs (ctx->lexer);
1219 struct variable *var = parse_variable (ctx->lexer, ctx->dict);
1223 struct ctables_axis *axis = xmalloc (sizeof *axis);
1224 *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
1226 /* XXX should figure out default measures by reading data */
1227 axis->scale = (lex_match_phrase (ctx->lexer, "[S]") ? true
1228 : lex_match_phrase (ctx->lexer, "[C]") ? false
1229 : var_get_measure (var) == MEASURE_SCALE);
1230 axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
1231 lex_ofs (ctx->lexer) - 1);
1232 if (axis->scale && var_is_alpha (var))
1234 msg_at (SE, axis->loc, _("Cannot use string variable %s as a scale "
1236 var_get_name (var));
1237 ctables_axis_destroy (axis);
1245 has_digit (const char *s)
1247 return s[strcspn (s, "0123456789")] != '\0';
1251 parse_ctables_format_specifier (struct lexer *lexer, struct fmt_spec *format,
1252 bool *is_ctables_format)
1254 char type[FMT_TYPE_LEN_MAX + 1];
1255 if (!parse_abstract_format_specifier__ (lexer, type, &format->w, &format->d))
1258 if (!strcasecmp (type, "NEGPAREN"))
1259 format->type = CTEF_NEGPAREN;
1260 else if (!strcasecmp (type, "NEQUAL"))
1261 format->type = CTEF_NEQUAL;
1262 else if (!strcasecmp (type, "PAREN"))
1263 format->type = CTEF_PAREN;
1264 else if (!strcasecmp (type, "PCTPAREN"))
1265 format->type = CTEF_PCTPAREN;
1268 *is_ctables_format = false;
1269 return (parse_format_specifier (lexer, format)
1270 && fmt_check_output (format)
1271 && fmt_check_type_compat (format, VAL_NUMERIC));
1276 msg (SE, _("Output format %s requires width 2 or greater."), type);
1279 else if (format->d > format->w - 1)
1281 msg (SE, _("Output format %s requires width greater than decimals."),
1287 *is_ctables_format = true;
1292 static struct ctables_axis *
1293 ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
1295 struct ctables_axis *sub = ctables_axis_parse_primary (ctx);
1296 if (!sub || !lex_match (ctx->lexer, T_LBRACK))
1299 enum ctables_summary_variant sv = CSV_CELL;
1302 int start_ofs = lex_ofs (ctx->lexer);
1304 /* Parse function. */
1305 enum ctables_summary_function function;
1306 if (!parse_ctables_summary_function (ctx->lexer, &function))
1309 /* Parse percentile. */
1310 double percentile = 0;
1311 if (function == CTSF_PTILE)
1313 if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
1315 percentile = lex_number (ctx->lexer);
1316 lex_get (ctx->lexer);
1321 if (lex_is_string (ctx->lexer))
1323 label = ss_xstrdup (lex_tokss (ctx->lexer));
1324 lex_get (ctx->lexer);
1328 struct fmt_spec format;
1329 const struct fmt_spec *formatp;
1330 bool is_ctables_format = false;
1331 if (lex_token (ctx->lexer) == T_ID
1332 && has_digit (lex_tokcstr (ctx->lexer)))
1334 if (!parse_ctables_format_specifier (ctx->lexer, &format,
1335 &is_ctables_format))
1345 struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
1346 lex_ofs (ctx->lexer) - 1);
1347 add_summary_spec (sub, function, percentile, label, formatp,
1348 is_ctables_format, loc, sv);
1350 msg_location_destroy (loc);
1352 lex_match (ctx->lexer, T_COMMA);
1353 if (sv == CSV_CELL && lex_match_id (ctx->lexer, "TOTALS"))
1355 if (!lex_force_match (ctx->lexer, T_LBRACK))
1359 else if (lex_match (ctx->lexer, T_RBRACK))
1361 if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
1368 ctables_axis_destroy (sub);
1372 static const struct ctables_axis *
1373 find_scale (const struct ctables_axis *axis)
1377 else if (axis->op == CTAO_VAR)
1378 return axis->scale ? axis : NULL;
1381 for (size_t i = 0; i < 2; i++)
1383 const struct ctables_axis *scale = find_scale (axis->subs[i]);
1391 static const struct ctables_axis *
1392 find_categorical_summary_spec (const struct ctables_axis *axis)
1396 else if (axis->op == CTAO_VAR)
1397 return !axis->scale && axis->specs[CSV_CELL].n ? axis : NULL;
1400 for (size_t i = 0; i < 2; i++)
1402 const struct ctables_axis *sum
1403 = find_categorical_summary_spec (axis->subs[i]);
1411 static struct ctables_axis *
1412 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
1414 int start_ofs = lex_ofs (ctx->lexer);
1415 struct ctables_axis *lhs = ctables_axis_parse_postfix (ctx);
1419 while (lex_match (ctx->lexer, T_GT))
1421 struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx);
1425 struct ctables_axis *nest = ctables_axis_new_nonterminal (
1426 CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
1428 const struct ctables_axis *outer_scale = find_scale (lhs);
1429 const struct ctables_axis *inner_scale = find_scale (rhs);
1430 if (outer_scale && inner_scale)
1432 msg_at (SE, nest->loc, _("Cannot nest scale variables."));
1433 msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
1434 msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
1435 ctables_axis_destroy (nest);
1439 const struct ctables_axis *outer_sum = find_categorical_summary_spec (lhs);
1442 msg_at (SE, nest->loc,
1443 _("Summaries may only be requested for categorical variables "
1444 "at the innermost nesting level."));
1445 msg_at (SN, outer_sum->loc,
1446 _("This outer categorical variable has a summary."));
1447 ctables_axis_destroy (nest);
1457 static struct ctables_axis *
1458 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
1460 int start_ofs = lex_ofs (ctx->lexer);
1461 struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
1465 while (lex_match (ctx->lexer, T_PLUS))
1467 struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
1471 lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
1472 ctx->lexer, start_ofs);
1479 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
1480 struct ctables *ct, struct ctables_table *t,
1481 enum pivot_axis_type a)
1483 if (lex_token (lexer) == T_BY
1484 || lex_token (lexer) == T_SLASH
1485 || lex_token (lexer) == T_ENDCMD)
1488 struct ctables_axis_parse_ctx ctx = {
1494 t->axes[a] = ctables_axis_parse_stack (&ctx);
1495 return t->axes[a] != NULL;
1499 ctables_chisq_destroy (struct ctables_chisq *chisq)
1505 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
1511 ctables_table_destroy (struct ctables_table *t)
1516 for (size_t i = 0; i < t->n_categories; i++)
1517 ctables_categories_unref (t->categories[i]);
1518 free (t->categories);
1520 ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
1521 ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
1522 ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
1526 ctables_chisq_destroy (t->chisq);
1527 ctables_pairwise_destroy (t->pairwise);
1532 ctables_destroy (struct ctables *ct)
1537 pivot_table_look_unref (ct->look);
1541 for (size_t i = 0; i < ct->n_tables; i++)
1542 ctables_table_destroy (ct->tables[i]);
1547 static struct ctables_category
1548 cct_nrange (double low, double high)
1550 return (struct ctables_category) {
1552 .nrange = { low, high }
1556 static struct ctables_category
1557 cct_srange (struct substring low, struct substring high)
1559 return (struct ctables_category) {
1561 .srange = { low, high }
1566 ctables_table_parse_subtotal (struct lexer *lexer, bool hide_subcategories,
1567 struct ctables_category *cat)
1570 if (lex_match (lexer, T_EQUALS))
1572 if (!lex_force_string (lexer))
1575 total_label = ss_xstrdup (lex_tokss (lexer));
1579 total_label = xstrdup (_("Subtotal"));
1581 *cat = (struct ctables_category) {
1582 .type = CCT_SUBTOTAL,
1583 .hide_subcategories = hide_subcategories,
1584 .total_label = total_label
1589 static struct substring
1590 parse_substring (struct lexer *lexer, struct dictionary *dict)
1592 struct substring s = recode_substring_pool (
1593 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
1594 ss_rtrim (&s, ss_cstr (" "));
1600 ctables_table_parse_explicit_category (struct lexer *lexer,
1601 struct dictionary *dict,
1603 struct ctables_category *cat)
1605 if (lex_match_id (lexer, "OTHERNM"))
1606 *cat = (struct ctables_category) { .type = CCT_OTHERNM };
1607 else if (lex_match_id (lexer, "MISSING"))
1608 *cat = (struct ctables_category) { .type = CCT_MISSING };
1609 else if (lex_match_id (lexer, "SUBTOTAL"))
1610 return ctables_table_parse_subtotal (lexer, false, cat);
1611 else if (lex_match_id (lexer, "HSUBTOTAL"))
1612 return ctables_table_parse_subtotal (lexer, true, cat);
1613 else if (lex_match_id (lexer, "LO"))
1615 if (!lex_force_match_id (lexer, "THRU"))
1617 if (lex_is_string (lexer))
1619 struct substring sr0 = { .string = NULL };
1620 struct substring sr1 = parse_substring (lexer, dict);
1621 *cat = cct_srange (sr0, sr1);
1623 else if (lex_force_num (lexer))
1625 *cat = cct_nrange (-DBL_MAX, lex_number (lexer));
1631 else if (lex_is_number (lexer))
1633 double number = lex_number (lexer);
1635 if (lex_match_id (lexer, "THRU"))
1637 if (lex_match_id (lexer, "HI"))
1638 *cat = cct_nrange (number, DBL_MAX);
1641 if (!lex_force_num (lexer))
1643 *cat = cct_nrange (number, lex_number (lexer));
1648 *cat = (struct ctables_category) {
1653 else if (lex_is_string (lexer))
1655 struct substring s = parse_substring (lexer, dict);
1656 if (lex_match_id (lexer, "THRU"))
1658 if (lex_match_id (lexer, "HI"))
1660 struct substring sr1 = { .string = NULL };
1661 *cat = cct_srange (s, sr1);
1665 if (!lex_force_string (lexer))
1667 struct substring sr1 = parse_substring (lexer, dict);
1668 *cat = cct_srange (s, sr1);
1672 *cat = (struct ctables_category) { .type = CCT_STRING, .string = s };
1674 else if (lex_match (lexer, T_AND))
1676 if (!lex_force_id (lexer))
1678 struct ctables_postcompute *pc = ctables_find_postcompute (
1679 ct, lex_tokcstr (lexer));
1682 struct msg_location *loc = lex_get_location (lexer, -1, 0);
1683 msg_at (SE, loc, _("Unknown postcompute &%s."),
1684 lex_tokcstr (lexer));
1685 msg_location_destroy (loc);
1690 *cat = (struct ctables_category) { .type = CCT_POSTCOMPUTE, .pc = pc };
1694 lex_error (lexer, NULL);
1701 static struct ctables_category *
1702 ctables_find_category_for_postcompute (const struct ctables_categories *cats,
1703 const struct ctables_pcexpr *e)
1705 struct ctables_category *best = NULL;
1706 size_t n_subtotals = 0;
1707 for (size_t i = 0; i < cats->n_cats; i++)
1709 struct ctables_category *cat = &cats->cats[i];
1712 case CTPO_CAT_NUMBER:
1713 if (cat->type == CCT_NUMBER && cat->number == e->number)
1717 case CTPO_CAT_STRING:
1718 if (cat->type == CCT_STRING && ss_equals (cat->string, e->string))
1722 case CTPO_CAT_NRANGE:
1723 if (cat->type == CCT_NRANGE
1724 && cat->nrange[0] == e->nrange[0]
1725 && cat->nrange[1] == e->nrange[1])
1729 case CTPO_CAT_SRANGE:
1730 if (cat->type == CCT_SRANGE
1731 && nullable_substring_equal (&cat->srange[0], &e->srange[0])
1732 && nullable_substring_equal (&cat->srange[1], &e->srange[1]))
1736 case CTPO_CAT_MISSING:
1737 if (cat->type == CCT_MISSING)
1741 case CTPO_CAT_OTHERNM:
1742 if (cat->type == CCT_OTHERNM)
1746 case CTPO_CAT_SUBTOTAL:
1747 if (cat->type == CCT_SUBTOTAL)
1750 if (e->subtotal_index == n_subtotals)
1752 else if (e->subtotal_index == 0)
1757 case CTPO_CAT_TOTAL:
1758 if (cat->type == CCT_TOTAL)
1772 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0 && n_subtotals > 1)
1778 ctables_recursive_check_postcompute (const struct ctables_pcexpr *e,
1779 struct ctables_category *pc_cat,
1780 const struct ctables_categories *cats,
1781 const struct msg_location *cats_location)
1785 case CTPO_CAT_NUMBER:
1786 case CTPO_CAT_STRING:
1787 case CTPO_CAT_NRANGE:
1788 case CTPO_CAT_MISSING:
1789 case CTPO_CAT_OTHERNM:
1790 case CTPO_CAT_SUBTOTAL:
1791 case CTPO_CAT_TOTAL:
1793 struct ctables_category *cat = ctables_find_category_for_postcompute (
1797 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0)
1799 size_t n_subtotals = 0;
1800 for (size_t i = 0; i < cats->n_cats; i++)
1801 n_subtotals += cats->cats[i].type == CCT_SUBTOTAL;
1802 if (n_subtotals > 1)
1804 msg_at (SE, cats_location,
1805 ngettext ("These categories include %zu instance "
1806 "of SUBTOTAL or HSUBTOTAL, so references "
1807 "from computed categories must refer to "
1808 "subtotals by position.",
1809 "These categories include %zu instances "
1810 "of SUBTOTAL or HSUBTOTAL, so references "
1811 "from computed categories must refer to "
1812 "subtotals by position.",
1815 msg_at (SN, e->location,
1816 _("This is the reference that lacks a position."));
1821 msg_at (SE, pc_cat->location,
1822 _("Computed category &%s references a category not included "
1823 "in the category list."),
1825 msg_at (SN, e->location, _("This is the missing category."));
1826 msg_at (SN, cats_location,
1827 _("To fix the problem, add the missing category to the "
1828 "list of categories here."));
1831 if (pc_cat->pc->hide_source_cats)
1845 for (size_t i = 0; i < 2; i++)
1846 if (e->subs[i] && !ctables_recursive_check_postcompute (
1847 e->subs[i], pc_cat, cats, cats_location))
1857 parse_category_string (const struct ctables_category *cat,
1858 struct substring s, struct dictionary *dict,
1859 enum fmt_type format, double *n)
1862 char *error = data_in (s, dict_get_encoding (dict), format,
1863 settings_get_fmt_settings (), &v, 0, NULL);
1866 msg_at (SE, cat->location,
1867 _("Failed to parse category specification as format %s: %s."),
1868 fmt_name (format), error);
1878 all_strings (struct variable **vars, size_t n_vars,
1879 const struct ctables_category *cat)
1881 for (size_t j = 0; j < n_vars; j++)
1882 if (var_is_numeric (vars[j]))
1884 msg_at (SE, cat->location,
1885 _("This category specification may be applied only to string "
1886 "variables, but this subcommand tries to apply it to "
1887 "numeric variable %s."),
1888 var_get_name (vars[j]));
1895 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
1896 struct ctables *ct, struct ctables_table *t)
1898 if (!lex_match_id (lexer, "VARIABLES"))
1900 lex_match (lexer, T_EQUALS);
1902 struct variable **vars;
1904 if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
1907 const struct fmt_spec *common_format = var_get_print_format (vars[0]);
1908 for (size_t i = 1; i < n_vars; i++)
1910 const struct fmt_spec *f = var_get_print_format (vars[i]);
1911 if (f->type != common_format->type)
1913 common_format = NULL;
1919 && (fmt_get_category (common_format->type)
1920 & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)));
1922 struct ctables_categories *c = xmalloc (sizeof *c);
1923 *c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
1924 for (size_t i = 0; i < n_vars; i++)
1926 struct ctables_categories **cp
1927 = &t->categories[var_get_dict_index (vars[i])];
1928 ctables_categories_unref (*cp);
1932 size_t allocated_cats = 0;
1933 if (lex_match (lexer, T_LBRACK))
1935 int cats_start_ofs = lex_ofs (lexer);
1938 if (c->n_cats >= allocated_cats)
1939 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
1941 int start_ofs = lex_ofs (lexer);
1942 struct ctables_category *cat = &c->cats[c->n_cats];
1943 if (!ctables_table_parse_explicit_category (lexer, dict, ct, cat))
1945 cat->location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
1948 lex_match (lexer, T_COMMA);
1950 while (!lex_match (lexer, T_RBRACK));
1952 struct msg_location *cats_location
1953 = lex_ofs_location (lexer, cats_start_ofs, lex_ofs (lexer) - 1);
1954 for (size_t i = 0; i < c->n_cats; i++)
1956 struct ctables_category *cat = &c->cats[i];
1959 case CCT_POSTCOMPUTE:
1960 if (!ctables_recursive_check_postcompute (cat->pc->expr, cat,
1967 for (size_t j = 0; j < n_vars; j++)
1968 if (var_is_alpha (vars[j]))
1970 msg_at (SE, cat->location,
1971 _("This category specification may be applied "
1972 "only to numeric variables, but this "
1973 "subcommand tries to apply it to string "
1975 var_get_name (vars[j]));
1984 if (!parse_category_string (cat, cat->string, dict,
1985 common_format->type, &n))
1988 ss_dealloc (&cat->string);
1990 cat->type = CCT_NUMBER;
1993 else if (!all_strings (vars, n_vars, cat))
2002 if (!cat->srange[0].string)
2004 else if (!parse_category_string (cat, cat->srange[0], dict,
2005 common_format->type, &n[0]))
2008 if (!cat->srange[1].string)
2010 else if (!parse_category_string (cat, cat->srange[1], dict,
2011 common_format->type, &n[1]))
2014 ss_dealloc (&cat->srange[0]);
2015 ss_dealloc (&cat->srange[1]);
2017 cat->type = CCT_NRANGE;
2018 cat->nrange[0] = n[0];
2019 cat->nrange[1] = n[1];
2021 else if (!all_strings (vars, n_vars, cat))
2032 case CCT_EXCLUDED_MISSING:
2038 struct ctables_category cat = {
2040 .include_missing = false,
2041 .sort_ascending = true,
2043 bool show_totals = false;
2044 char *total_label = NULL;
2045 bool totals_before = false;
2046 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
2048 if (!c->n_cats && lex_match_id (lexer, "ORDER"))
2050 lex_match (lexer, T_EQUALS);
2051 if (lex_match_id (lexer, "A"))
2052 cat.sort_ascending = true;
2053 else if (lex_match_id (lexer, "D"))
2054 cat.sort_ascending = false;
2057 lex_error_expecting (lexer, "A", "D");
2061 else if (!c->n_cats && lex_match_id (lexer, "KEY"))
2063 lex_match (lexer, T_EQUALS);
2064 if (lex_match_id (lexer, "VALUE"))
2065 cat.type = CCT_VALUE;
2066 else if (lex_match_id (lexer, "LABEL"))
2067 cat.type = CCT_LABEL;
2070 cat.type = CCT_FUNCTION;
2071 if (!parse_ctables_summary_function (lexer, &cat.sort_function))
2074 if (lex_match (lexer, T_LPAREN))
2076 cat.sort_var = parse_variable (lexer, dict);
2080 if (cat.sort_function == CTSF_PTILE)
2082 lex_match (lexer, T_COMMA);
2083 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
2085 cat.percentile = lex_number (lexer);
2089 if (!lex_force_match (lexer, T_RPAREN))
2092 else if (ctables_function_availability (cat.sort_function)
2095 bool UNUSED b = lex_force_match (lexer, T_LPAREN);
2100 else if (!c->n_cats && lex_match_id (lexer, "MISSING"))
2102 lex_match (lexer, T_EQUALS);
2103 if (lex_match_id (lexer, "INCLUDE"))
2104 cat.include_missing = true;
2105 else if (lex_match_id (lexer, "EXCLUDE"))
2106 cat.include_missing = false;
2109 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
2113 else if (lex_match_id (lexer, "TOTAL"))
2115 lex_match (lexer, T_EQUALS);
2116 if (!parse_bool (lexer, &show_totals))
2119 else if (lex_match_id (lexer, "LABEL"))
2121 lex_match (lexer, T_EQUALS);
2122 if (!lex_force_string (lexer))
2125 total_label = ss_xstrdup (lex_tokss (lexer));
2128 else if (lex_match_id (lexer, "POSITION"))
2130 lex_match (lexer, T_EQUALS);
2131 if (lex_match_id (lexer, "BEFORE"))
2132 totals_before = true;
2133 else if (lex_match_id (lexer, "AFTER"))
2134 totals_before = false;
2137 lex_error_expecting (lexer, "BEFORE", "AFTER");
2141 else if (lex_match_id (lexer, "EMPTY"))
2143 lex_match (lexer, T_EQUALS);
2144 if (lex_match_id (lexer, "INCLUDE"))
2145 c->show_empty = true;
2146 else if (lex_match_id (lexer, "EXCLUDE"))
2147 c->show_empty = false;
2150 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
2157 lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
2158 "TOTAL", "LABEL", "POSITION", "EMPTY");
2160 lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
2167 if (c->n_cats >= allocated_cats)
2168 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2169 c->cats[c->n_cats++] = cat;
2174 if (c->n_cats >= allocated_cats)
2175 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2177 struct ctables_category *totals;
2180 insert_element (c->cats, c->n_cats, sizeof *c->cats, 0);
2181 totals = &c->cats[0];
2184 totals = &c->cats[c->n_cats];
2187 *totals = (struct ctables_category) {
2189 .total_label = total_label ? total_label : xstrdup (_("Total")),
2193 struct ctables_category *subtotal = NULL;
2194 for (size_t i = totals_before ? 0 : c->n_cats;
2195 totals_before ? i < c->n_cats : i-- > 0;
2196 totals_before ? i++ : 0)
2198 struct ctables_category *cat = &c->cats[i];
2207 cat->subtotal = subtotal;
2210 case CCT_POSTCOMPUTE:
2221 case CCT_EXCLUDED_MISSING:
2230 ctables_nest_uninit (struct ctables_nest *nest)
2237 ctables_stack_uninit (struct ctables_stack *stack)
2241 for (size_t i = 0; i < stack->n; i++)
2242 ctables_nest_uninit (&stack->nests[i]);
2243 free (stack->nests);
2247 static struct ctables_stack
2248 nest_fts (struct ctables_stack s0, struct ctables_stack s1)
2255 struct ctables_stack stack = { .nests = xnmalloc (s0.n, s1.n * sizeof *stack.nests) };
2256 for (size_t i = 0; i < s0.n; i++)
2257 for (size_t j = 0; j < s1.n; j++)
2259 const struct ctables_nest *a = &s0.nests[i];
2260 const struct ctables_nest *b = &s1.nests[j];
2262 size_t allocate = a->n + b->n;
2263 struct variable **vars = xnmalloc (allocate, sizeof *vars);
2264 enum pivot_axis_type *axes = xnmalloc (allocate, sizeof *axes);
2266 for (size_t k = 0; k < a->n; k++)
2267 vars[n++] = a->vars[k];
2268 for (size_t k = 0; k < b->n; k++)
2269 vars[n++] = b->vars[k];
2270 assert (n == allocate);
2272 const struct ctables_nest *summary_src;
2273 if (!a->specs[CSV_CELL].var)
2275 else if (!b->specs[CSV_CELL].var)
2280 struct ctables_nest *new = &stack.nests[stack.n++];
2281 *new = (struct ctables_nest) {
2283 .scale_idx = (a->scale_idx != SIZE_MAX ? a->scale_idx
2284 : b->scale_idx != SIZE_MAX ? a->n + b->scale_idx
2288 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2289 ctables_summary_spec_set_clone (&new->specs[sv], &summary_src->specs[sv]);
2291 ctables_stack_uninit (&s0);
2292 ctables_stack_uninit (&s1);
2296 static struct ctables_stack
2297 stack_fts (struct ctables_stack s0, struct ctables_stack s1)
2299 struct ctables_stack stack = { .nests = xnmalloc (s0.n + s1.n, sizeof *stack.nests) };
2300 for (size_t i = 0; i < s0.n; i++)
2301 stack.nests[stack.n++] = s0.nests[i];
2302 for (size_t i = 0; i < s1.n; i++)
2304 stack.nests[stack.n] = s1.nests[i];
2305 stack.nests[stack.n].group_head += s0.n;
2308 assert (stack.n == s0.n + s1.n);
2314 static struct ctables_stack
2315 var_fts (const struct ctables_axis *a)
2317 struct variable **vars = xmalloc (sizeof *vars);
2320 struct ctables_nest *nest = xmalloc (sizeof *nest);
2321 *nest = (struct ctables_nest) {
2324 .scale_idx = a->scale ? 0 : SIZE_MAX,
2326 if (a->specs[CSV_CELL].n || a->scale)
2327 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2329 ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
2330 nest->specs[sv].var = a->var;
2331 nest->specs[sv].is_scale = a->scale;
2333 return (struct ctables_stack) { .nests = nest, .n = 1 };
2336 static struct ctables_stack
2337 enumerate_fts (enum pivot_axis_type axis_type, const struct ctables_axis *a)
2340 return (struct ctables_stack) { .n = 0 };
2348 return stack_fts (enumerate_fts (axis_type, a->subs[0]),
2349 enumerate_fts (axis_type, a->subs[1]));
2352 /* This should consider any of the scale variables found in the result to
2353 be linked to each other listwise for SMISSING=LISTWISE. */
2354 return nest_fts (enumerate_fts (axis_type, a->subs[0]),
2355 enumerate_fts (axis_type, a->subs[1]));
2361 union ctables_summary
2363 /* COUNT, VALIDN, TOTALN. */
2366 /* MINIMUM, MAXIMUM, RANGE. */
2373 /* MEAN, SEMEAN, STDDEV, SUM, VARIANCE, *.SUM. */
2374 struct moments1 *moments;
2376 /* MEDIAN, MODE, PTILE. */
2379 struct casewriter *writer;
2384 /* XXX multiple response */
2388 ctables_summary_init (union ctables_summary *s,
2389 const struct ctables_summary_spec *ss)
2391 switch (ss->function)
2395 case CTSF_ROWPCT_COUNT:
2396 case CTSF_COLPCT_COUNT:
2397 case CTSF_TABLEPCT_COUNT:
2398 case CTSF_SUBTABLEPCT_COUNT:
2399 case CTSF_LAYERPCT_COUNT:
2400 case CTSF_LAYERROWPCT_COUNT:
2401 case CTSF_LAYERCOLPCT_COUNT:
2402 case CTSF_ROWPCT_VALIDN:
2403 case CTSF_COLPCT_VALIDN:
2404 case CTSF_TABLEPCT_VALIDN:
2405 case CTSF_SUBTABLEPCT_VALIDN:
2406 case CTSF_LAYERPCT_VALIDN:
2407 case CTSF_LAYERROWPCT_VALIDN:
2408 case CTSF_LAYERCOLPCT_VALIDN:
2409 case CTSF_ROWPCT_TOTALN:
2410 case CTSF_COLPCT_TOTALN:
2411 case CTSF_TABLEPCT_TOTALN:
2412 case CTSF_SUBTABLEPCT_TOTALN:
2413 case CTSF_LAYERPCT_TOTALN:
2414 case CTSF_LAYERROWPCT_TOTALN:
2415 case CTSF_LAYERCOLPCT_TOTALN:
2422 case CTSF_UROWPCT_COUNT:
2423 case CTSF_UCOLPCT_COUNT:
2424 case CTSF_UTABLEPCT_COUNT:
2425 case CTSF_USUBTABLEPCT_COUNT:
2426 case CTSF_ULAYERPCT_COUNT:
2427 case CTSF_ULAYERROWPCT_COUNT:
2428 case CTSF_ULAYERCOLPCT_COUNT:
2429 case CTSF_UROWPCT_VALIDN:
2430 case CTSF_UCOLPCT_VALIDN:
2431 case CTSF_UTABLEPCT_VALIDN:
2432 case CTSF_USUBTABLEPCT_VALIDN:
2433 case CTSF_ULAYERPCT_VALIDN:
2434 case CTSF_ULAYERROWPCT_VALIDN:
2435 case CTSF_ULAYERCOLPCT_VALIDN:
2436 case CTSF_UROWPCT_TOTALN:
2437 case CTSF_UCOLPCT_TOTALN:
2438 case CTSF_UTABLEPCT_TOTALN:
2439 case CTSF_USUBTABLEPCT_TOTALN:
2440 case CTSF_ULAYERPCT_TOTALN:
2441 case CTSF_ULAYERROWPCT_TOTALN:
2442 case CTSF_ULAYERCOLPCT_TOTALN:
2452 s->min = s->max = SYSMIS;
2460 case CTSF_ROWPCT_SUM:
2461 case CTSF_COLPCT_SUM:
2462 case CTSF_TABLEPCT_SUM:
2463 case CTSF_SUBTABLEPCT_SUM:
2464 case CTSF_LAYERPCT_SUM:
2465 case CTSF_LAYERROWPCT_SUM:
2466 case CTSF_LAYERCOLPCT_SUM:
2471 case CTSF_UVARIANCE:
2472 case CTSF_UROWPCT_SUM:
2473 case CTSF_UCOLPCT_SUM:
2474 case CTSF_UTABLEPCT_SUM:
2475 case CTSF_USUBTABLEPCT_SUM:
2476 case CTSF_ULAYERPCT_SUM:
2477 case CTSF_ULAYERROWPCT_SUM:
2478 case CTSF_ULAYERCOLPCT_SUM:
2479 s->moments = moments1_create (MOMENT_VARIANCE);
2489 struct caseproto *proto = caseproto_create ();
2490 proto = caseproto_add_width (proto, 0);
2491 proto = caseproto_add_width (proto, 0);
2493 struct subcase ordering;
2494 subcase_init (&ordering, 0, 0, SC_ASCEND);
2495 s->writer = sort_create_writer (&ordering, proto);
2496 subcase_uninit (&ordering);
2497 caseproto_unref (proto);
2507 ctables_summary_uninit (union ctables_summary *s,
2508 const struct ctables_summary_spec *ss)
2510 switch (ss->function)
2514 case CTSF_ROWPCT_COUNT:
2515 case CTSF_COLPCT_COUNT:
2516 case CTSF_TABLEPCT_COUNT:
2517 case CTSF_SUBTABLEPCT_COUNT:
2518 case CTSF_LAYERPCT_COUNT:
2519 case CTSF_LAYERROWPCT_COUNT:
2520 case CTSF_LAYERCOLPCT_COUNT:
2521 case CTSF_ROWPCT_VALIDN:
2522 case CTSF_COLPCT_VALIDN:
2523 case CTSF_TABLEPCT_VALIDN:
2524 case CTSF_SUBTABLEPCT_VALIDN:
2525 case CTSF_LAYERPCT_VALIDN:
2526 case CTSF_LAYERROWPCT_VALIDN:
2527 case CTSF_LAYERCOLPCT_VALIDN:
2528 case CTSF_ROWPCT_TOTALN:
2529 case CTSF_COLPCT_TOTALN:
2530 case CTSF_TABLEPCT_TOTALN:
2531 case CTSF_SUBTABLEPCT_TOTALN:
2532 case CTSF_LAYERPCT_TOTALN:
2533 case CTSF_LAYERROWPCT_TOTALN:
2534 case CTSF_LAYERCOLPCT_TOTALN:
2541 case CTSF_UROWPCT_COUNT:
2542 case CTSF_UCOLPCT_COUNT:
2543 case CTSF_UTABLEPCT_COUNT:
2544 case CTSF_USUBTABLEPCT_COUNT:
2545 case CTSF_ULAYERPCT_COUNT:
2546 case CTSF_ULAYERROWPCT_COUNT:
2547 case CTSF_ULAYERCOLPCT_COUNT:
2548 case CTSF_UROWPCT_VALIDN:
2549 case CTSF_UCOLPCT_VALIDN:
2550 case CTSF_UTABLEPCT_VALIDN:
2551 case CTSF_USUBTABLEPCT_VALIDN:
2552 case CTSF_ULAYERPCT_VALIDN:
2553 case CTSF_ULAYERROWPCT_VALIDN:
2554 case CTSF_ULAYERCOLPCT_VALIDN:
2555 case CTSF_UROWPCT_TOTALN:
2556 case CTSF_UCOLPCT_TOTALN:
2557 case CTSF_UTABLEPCT_TOTALN:
2558 case CTSF_USUBTABLEPCT_TOTALN:
2559 case CTSF_ULAYERPCT_TOTALN:
2560 case CTSF_ULAYERROWPCT_TOTALN:
2561 case CTSF_ULAYERCOLPCT_TOTALN:
2577 case CTSF_ROWPCT_SUM:
2578 case CTSF_COLPCT_SUM:
2579 case CTSF_TABLEPCT_SUM:
2580 case CTSF_SUBTABLEPCT_SUM:
2581 case CTSF_LAYERPCT_SUM:
2582 case CTSF_LAYERROWPCT_SUM:
2583 case CTSF_LAYERCOLPCT_SUM:
2588 case CTSF_UVARIANCE:
2589 case CTSF_UROWPCT_SUM:
2590 case CTSF_UCOLPCT_SUM:
2591 case CTSF_UTABLEPCT_SUM:
2592 case CTSF_USUBTABLEPCT_SUM:
2593 case CTSF_ULAYERPCT_SUM:
2594 case CTSF_ULAYERROWPCT_SUM:
2595 case CTSF_ULAYERCOLPCT_SUM:
2596 moments1_destroy (s->moments);
2605 casewriter_destroy (s->writer);
2611 ctables_summary_add (union ctables_summary *s,
2612 const struct ctables_summary_spec *ss,
2613 const struct variable *var, const union value *value,
2614 bool is_scale, bool is_scale_missing,
2615 bool is_missing, bool excluded_missing,
2616 double d_weight, double e_weight)
2618 /* To determine whether a case is included in a given table for a particular
2619 kind of summary, consider the following charts for each variable in the
2620 table. Only if "yes" appears for every variable for the summary is the
2623 Categorical variables: VALIDN COUNT TOTALN
2624 Valid values in included categories yes yes yes
2625 Missing values in included categories --- yes yes
2626 Missing values in excluded categories --- --- yes
2627 Valid values in excluded categories --- --- ---
2629 Scale variables: VALIDN COUNT TOTALN
2630 Valid value yes yes yes
2631 Missing value --- yes yes
2633 Missing values include both user- and system-missing. (The system-missing
2634 value is always in an excluded category.)
2636 switch (ss->function)
2639 case CTSF_ROWPCT_TOTALN:
2640 case CTSF_COLPCT_TOTALN:
2641 case CTSF_TABLEPCT_TOTALN:
2642 case CTSF_SUBTABLEPCT_TOTALN:
2643 case CTSF_LAYERPCT_TOTALN:
2644 case CTSF_LAYERROWPCT_TOTALN:
2645 case CTSF_LAYERCOLPCT_TOTALN:
2646 s->count += d_weight;
2650 case CTSF_UROWPCT_TOTALN:
2651 case CTSF_UCOLPCT_TOTALN:
2652 case CTSF_UTABLEPCT_TOTALN:
2653 case CTSF_USUBTABLEPCT_TOTALN:
2654 case CTSF_ULAYERPCT_TOTALN:
2655 case CTSF_ULAYERROWPCT_TOTALN:
2656 case CTSF_ULAYERCOLPCT_TOTALN:
2661 case CTSF_ROWPCT_COUNT:
2662 case CTSF_COLPCT_COUNT:
2663 case CTSF_TABLEPCT_COUNT:
2664 case CTSF_SUBTABLEPCT_COUNT:
2665 case CTSF_LAYERPCT_COUNT:
2666 case CTSF_LAYERROWPCT_COUNT:
2667 case CTSF_LAYERCOLPCT_COUNT:
2668 if (is_scale || !excluded_missing)
2669 s->count += d_weight;
2673 case CTSF_UROWPCT_COUNT:
2674 case CTSF_UCOLPCT_COUNT:
2675 case CTSF_UTABLEPCT_COUNT:
2676 case CTSF_USUBTABLEPCT_COUNT:
2677 case CTSF_ULAYERPCT_COUNT:
2678 case CTSF_ULAYERROWPCT_COUNT:
2679 case CTSF_ULAYERCOLPCT_COUNT:
2680 if (is_scale || !excluded_missing)
2685 case CTSF_ROWPCT_VALIDN:
2686 case CTSF_COLPCT_VALIDN:
2687 case CTSF_TABLEPCT_VALIDN:
2688 case CTSF_SUBTABLEPCT_VALIDN:
2689 case CTSF_LAYERPCT_VALIDN:
2690 case CTSF_LAYERROWPCT_VALIDN:
2691 case CTSF_LAYERCOLPCT_VALIDN:
2695 s->count += d_weight;
2699 case CTSF_UROWPCT_VALIDN:
2700 case CTSF_UCOLPCT_VALIDN:
2701 case CTSF_UTABLEPCT_VALIDN:
2702 case CTSF_USUBTABLEPCT_VALIDN:
2703 case CTSF_ULAYERPCT_VALIDN:
2704 case CTSF_ULAYERROWPCT_VALIDN:
2705 case CTSF_ULAYERCOLPCT_VALIDN:
2714 s->count += d_weight;
2723 if (is_scale || !excluded_missing)
2724 s->count += e_weight;
2731 s->count += e_weight;
2735 s->count += e_weight;
2741 if (!is_scale_missing)
2743 assert (!var_is_alpha (var)); /* XXX? */
2744 if (s->min == SYSMIS || value->f < s->min)
2746 if (s->max == SYSMIS || value->f > s->max)
2756 case CTSF_ROWPCT_SUM:
2757 case CTSF_COLPCT_SUM:
2758 case CTSF_TABLEPCT_SUM:
2759 case CTSF_SUBTABLEPCT_SUM:
2760 case CTSF_LAYERPCT_SUM:
2761 case CTSF_LAYERROWPCT_SUM:
2762 case CTSF_LAYERCOLPCT_SUM:
2763 if (!is_scale_missing)
2764 moments1_add (s->moments, value->f, e_weight);
2771 case CTSF_UVARIANCE:
2772 case CTSF_UROWPCT_SUM:
2773 case CTSF_UCOLPCT_SUM:
2774 case CTSF_UTABLEPCT_SUM:
2775 case CTSF_USUBTABLEPCT_SUM:
2776 case CTSF_ULAYERPCT_SUM:
2777 case CTSF_ULAYERROWPCT_SUM:
2778 case CTSF_ULAYERCOLPCT_SUM:
2779 if (!is_scale_missing)
2780 moments1_add (s->moments, value->f, 1.0);
2786 d_weight = e_weight = 1.0;
2791 if (!is_scale_missing)
2793 s->ovalid += e_weight;
2795 struct ccase *c = case_create (casewriter_get_proto (s->writer));
2796 *case_num_rw_idx (c, 0) = value->f;
2797 *case_num_rw_idx (c, 1) = e_weight;
2798 casewriter_write (s->writer, c);
2804 static enum ctables_domain_type
2805 ctables_function_domain (enum ctables_summary_function function)
2835 case CTSF_UVARIANCE:
2841 case CTSF_COLPCT_COUNT:
2842 case CTSF_COLPCT_SUM:
2843 case CTSF_COLPCT_TOTALN:
2844 case CTSF_COLPCT_VALIDN:
2845 case CTSF_UCOLPCT_COUNT:
2846 case CTSF_UCOLPCT_SUM:
2847 case CTSF_UCOLPCT_TOTALN:
2848 case CTSF_UCOLPCT_VALIDN:
2851 case CTSF_LAYERCOLPCT_COUNT:
2852 case CTSF_LAYERCOLPCT_SUM:
2853 case CTSF_LAYERCOLPCT_TOTALN:
2854 case CTSF_LAYERCOLPCT_VALIDN:
2855 case CTSF_ULAYERCOLPCT_COUNT:
2856 case CTSF_ULAYERCOLPCT_SUM:
2857 case CTSF_ULAYERCOLPCT_TOTALN:
2858 case CTSF_ULAYERCOLPCT_VALIDN:
2859 return CTDT_LAYERCOL;
2861 case CTSF_LAYERPCT_COUNT:
2862 case CTSF_LAYERPCT_SUM:
2863 case CTSF_LAYERPCT_TOTALN:
2864 case CTSF_LAYERPCT_VALIDN:
2865 case CTSF_ULAYERPCT_COUNT:
2866 case CTSF_ULAYERPCT_SUM:
2867 case CTSF_ULAYERPCT_TOTALN:
2868 case CTSF_ULAYERPCT_VALIDN:
2871 case CTSF_LAYERROWPCT_COUNT:
2872 case CTSF_LAYERROWPCT_SUM:
2873 case CTSF_LAYERROWPCT_TOTALN:
2874 case CTSF_LAYERROWPCT_VALIDN:
2875 case CTSF_ULAYERROWPCT_COUNT:
2876 case CTSF_ULAYERROWPCT_SUM:
2877 case CTSF_ULAYERROWPCT_TOTALN:
2878 case CTSF_ULAYERROWPCT_VALIDN:
2879 return CTDT_LAYERROW;
2881 case CTSF_ROWPCT_COUNT:
2882 case CTSF_ROWPCT_SUM:
2883 case CTSF_ROWPCT_TOTALN:
2884 case CTSF_ROWPCT_VALIDN:
2885 case CTSF_UROWPCT_COUNT:
2886 case CTSF_UROWPCT_SUM:
2887 case CTSF_UROWPCT_TOTALN:
2888 case CTSF_UROWPCT_VALIDN:
2891 case CTSF_SUBTABLEPCT_COUNT:
2892 case CTSF_SUBTABLEPCT_SUM:
2893 case CTSF_SUBTABLEPCT_TOTALN:
2894 case CTSF_SUBTABLEPCT_VALIDN:
2895 case CTSF_USUBTABLEPCT_COUNT:
2896 case CTSF_USUBTABLEPCT_SUM:
2897 case CTSF_USUBTABLEPCT_TOTALN:
2898 case CTSF_USUBTABLEPCT_VALIDN:
2899 return CTDT_SUBTABLE;
2901 case CTSF_TABLEPCT_COUNT:
2902 case CTSF_TABLEPCT_SUM:
2903 case CTSF_TABLEPCT_TOTALN:
2904 case CTSF_TABLEPCT_VALIDN:
2905 case CTSF_UTABLEPCT_COUNT:
2906 case CTSF_UTABLEPCT_SUM:
2907 case CTSF_UTABLEPCT_TOTALN:
2908 case CTSF_UTABLEPCT_VALIDN:
2915 static enum ctables_domain_type
2916 ctables_function_is_pctsum (enum ctables_summary_function function)
2946 case CTSF_UVARIANCE:
2950 case CTSF_COLPCT_COUNT:
2951 case CTSF_COLPCT_TOTALN:
2952 case CTSF_COLPCT_VALIDN:
2953 case CTSF_UCOLPCT_COUNT:
2954 case CTSF_UCOLPCT_TOTALN:
2955 case CTSF_UCOLPCT_VALIDN:
2956 case CTSF_LAYERCOLPCT_COUNT:
2957 case CTSF_LAYERCOLPCT_TOTALN:
2958 case CTSF_LAYERCOLPCT_VALIDN:
2959 case CTSF_ULAYERCOLPCT_COUNT:
2960 case CTSF_ULAYERCOLPCT_TOTALN:
2961 case CTSF_ULAYERCOLPCT_VALIDN:
2962 case CTSF_LAYERPCT_COUNT:
2963 case CTSF_LAYERPCT_TOTALN:
2964 case CTSF_LAYERPCT_VALIDN:
2965 case CTSF_ULAYERPCT_COUNT:
2966 case CTSF_ULAYERPCT_TOTALN:
2967 case CTSF_ULAYERPCT_VALIDN:
2968 case CTSF_LAYERROWPCT_COUNT:
2969 case CTSF_LAYERROWPCT_TOTALN:
2970 case CTSF_LAYERROWPCT_VALIDN:
2971 case CTSF_ULAYERROWPCT_COUNT:
2972 case CTSF_ULAYERROWPCT_TOTALN:
2973 case CTSF_ULAYERROWPCT_VALIDN:
2974 case CTSF_ROWPCT_COUNT:
2975 case CTSF_ROWPCT_TOTALN:
2976 case CTSF_ROWPCT_VALIDN:
2977 case CTSF_UROWPCT_COUNT:
2978 case CTSF_UROWPCT_TOTALN:
2979 case CTSF_UROWPCT_VALIDN:
2980 case CTSF_SUBTABLEPCT_COUNT:
2981 case CTSF_SUBTABLEPCT_TOTALN:
2982 case CTSF_SUBTABLEPCT_VALIDN:
2983 case CTSF_USUBTABLEPCT_COUNT:
2984 case CTSF_USUBTABLEPCT_TOTALN:
2985 case CTSF_USUBTABLEPCT_VALIDN:
2986 case CTSF_TABLEPCT_COUNT:
2987 case CTSF_TABLEPCT_TOTALN:
2988 case CTSF_TABLEPCT_VALIDN:
2989 case CTSF_UTABLEPCT_COUNT:
2990 case CTSF_UTABLEPCT_TOTALN:
2991 case CTSF_UTABLEPCT_VALIDN:
2994 case CTSF_COLPCT_SUM:
2995 case CTSF_UCOLPCT_SUM:
2996 case CTSF_LAYERCOLPCT_SUM:
2997 case CTSF_ULAYERCOLPCT_SUM:
2998 case CTSF_LAYERPCT_SUM:
2999 case CTSF_ULAYERPCT_SUM:
3000 case CTSF_LAYERROWPCT_SUM:
3001 case CTSF_ULAYERROWPCT_SUM:
3002 case CTSF_ROWPCT_SUM:
3003 case CTSF_UROWPCT_SUM:
3004 case CTSF_SUBTABLEPCT_SUM:
3005 case CTSF_USUBTABLEPCT_SUM:
3006 case CTSF_TABLEPCT_SUM:
3007 case CTSF_UTABLEPCT_SUM:
3015 ctables_summary_value (const struct ctables_cell *cell,
3016 union ctables_summary *s,
3017 const struct ctables_summary_spec *ss)
3019 switch (ss->function)
3026 case CTSF_ROWPCT_COUNT:
3027 case CTSF_COLPCT_COUNT:
3028 case CTSF_TABLEPCT_COUNT:
3029 case CTSF_SUBTABLEPCT_COUNT:
3030 case CTSF_LAYERPCT_COUNT:
3031 case CTSF_LAYERROWPCT_COUNT:
3032 case CTSF_LAYERCOLPCT_COUNT:
3034 enum ctables_domain_type d = ctables_function_domain (ss->function);
3035 return (cell->domains[d]->e_count
3036 ? s->count / cell->domains[d]->e_count * 100
3040 case CTSF_UROWPCT_COUNT:
3041 case CTSF_UCOLPCT_COUNT:
3042 case CTSF_UTABLEPCT_COUNT:
3043 case CTSF_USUBTABLEPCT_COUNT:
3044 case CTSF_ULAYERPCT_COUNT:
3045 case CTSF_ULAYERROWPCT_COUNT:
3046 case CTSF_ULAYERCOLPCT_COUNT:
3048 enum ctables_domain_type d = ctables_function_domain (ss->function);
3049 return (cell->domains[d]->u_count
3050 ? s->count / cell->domains[d]->u_count * 100
3054 case CTSF_ROWPCT_VALIDN:
3055 case CTSF_COLPCT_VALIDN:
3056 case CTSF_TABLEPCT_VALIDN:
3057 case CTSF_SUBTABLEPCT_VALIDN:
3058 case CTSF_LAYERPCT_VALIDN:
3059 case CTSF_LAYERROWPCT_VALIDN:
3060 case CTSF_LAYERCOLPCT_VALIDN:
3062 enum ctables_domain_type d = ctables_function_domain (ss->function);
3063 return (cell->domains[d]->e_valid
3064 ? s->count / cell->domains[d]->e_valid * 100
3068 case CTSF_UROWPCT_VALIDN:
3069 case CTSF_UCOLPCT_VALIDN:
3070 case CTSF_UTABLEPCT_VALIDN:
3071 case CTSF_USUBTABLEPCT_VALIDN:
3072 case CTSF_ULAYERPCT_VALIDN:
3073 case CTSF_ULAYERROWPCT_VALIDN:
3074 case CTSF_ULAYERCOLPCT_VALIDN:
3076 enum ctables_domain_type d = ctables_function_domain (ss->function);
3077 return (cell->domains[d]->u_valid
3078 ? s->count / cell->domains[d]->u_valid * 100
3082 case CTSF_ROWPCT_TOTALN:
3083 case CTSF_COLPCT_TOTALN:
3084 case CTSF_TABLEPCT_TOTALN:
3085 case CTSF_SUBTABLEPCT_TOTALN:
3086 case CTSF_LAYERPCT_TOTALN:
3087 case CTSF_LAYERROWPCT_TOTALN:
3088 case CTSF_LAYERCOLPCT_TOTALN:
3090 enum ctables_domain_type d = ctables_function_domain (ss->function);
3091 return (cell->domains[d]->e_total
3092 ? s->count / cell->domains[d]->e_total * 100
3096 case CTSF_UROWPCT_TOTALN:
3097 case CTSF_UCOLPCT_TOTALN:
3098 case CTSF_UTABLEPCT_TOTALN:
3099 case CTSF_USUBTABLEPCT_TOTALN:
3100 case CTSF_ULAYERPCT_TOTALN:
3101 case CTSF_ULAYERROWPCT_TOTALN:
3102 case CTSF_ULAYERCOLPCT_TOTALN:
3104 enum ctables_domain_type d = ctables_function_domain (ss->function);
3105 return (cell->domains[d]->u_total
3106 ? s->count / cell->domains[d]->u_total * 100
3127 return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
3133 moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
3140 double weight, variance;
3141 moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
3142 return calc_semean (variance, weight);
3149 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
3150 return variance != SYSMIS ? sqrt (variance) : SYSMIS;
3156 double weight, mean;
3157 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
3158 return weight != SYSMIS && mean != SYSMIS ? weight * mean : SYSMIS;
3162 case CTSF_UVARIANCE:
3165 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
3169 case CTSF_ROWPCT_SUM:
3170 case CTSF_COLPCT_SUM:
3171 case CTSF_TABLEPCT_SUM:
3172 case CTSF_SUBTABLEPCT_SUM:
3173 case CTSF_LAYERPCT_SUM:
3174 case CTSF_LAYERROWPCT_SUM:
3175 case CTSF_LAYERCOLPCT_SUM:
3177 double weight, mean;
3178 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
3179 if (weight == SYSMIS || mean == SYSMIS)
3181 enum ctables_domain_type d = ctables_function_domain (ss->function);
3182 double num = weight * mean;
3183 double denom = cell->domains[d]->sums[ss->sum_var_idx].e_sum;
3184 return denom != 0 ? num / denom * 100 : SYSMIS;
3186 case CTSF_UROWPCT_SUM:
3187 case CTSF_UCOLPCT_SUM:
3188 case CTSF_UTABLEPCT_SUM:
3189 case CTSF_USUBTABLEPCT_SUM:
3190 case CTSF_ULAYERPCT_SUM:
3191 case CTSF_ULAYERROWPCT_SUM:
3192 case CTSF_ULAYERCOLPCT_SUM:
3194 double weight, mean;
3195 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
3196 if (weight == SYSMIS || mean == SYSMIS)
3198 enum ctables_domain_type d = ctables_function_domain (ss->function);
3199 double num = weight * mean;
3200 double denom = cell->domains[d]->sums[ss->sum_var_idx].u_sum;
3201 return denom != 0 ? num / denom * 100 : SYSMIS;
3210 struct casereader *reader = casewriter_make_reader (s->writer);
3213 struct percentile *ptile = percentile_create (
3214 ss->function == CTSF_PTILE ? ss->percentile : 0.5, s->ovalid);
3215 struct order_stats *os = &ptile->parent;
3216 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
3217 s->ovalue = percentile_calculate (ptile, PC_HAVERAGE);
3218 statistic_destroy (&ptile->parent.parent);
3226 struct casereader *reader = casewriter_make_reader (s->writer);
3229 struct mode *mode = mode_create ();
3230 struct order_stats *os = &mode->parent;
3231 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
3232 s->ovalue = mode->mode;
3233 statistic_destroy (&mode->parent.parent);
3241 struct ctables_cell_sort_aux
3243 const struct ctables_nest *nest;
3244 enum pivot_axis_type a;
3248 ctables_cell_compare_3way (const void *a_, const void *b_, const void *aux_)
3250 const struct ctables_cell_sort_aux *aux = aux_;
3251 struct ctables_cell *const *ap = a_;
3252 struct ctables_cell *const *bp = b_;
3253 const struct ctables_cell *a = *ap;
3254 const struct ctables_cell *b = *bp;
3256 const struct ctables_nest *nest = aux->nest;
3257 for (size_t i = 0; i < nest->n; i++)
3258 if (i != nest->scale_idx)
3260 const struct variable *var = nest->vars[i];
3261 const struct ctables_cell_value *a_cv = &a->axes[aux->a].cvs[i];
3262 const struct ctables_cell_value *b_cv = &b->axes[aux->a].cvs[i];
3263 if (a_cv->category != b_cv->category)
3264 return a_cv->category > b_cv->category ? 1 : -1;
3266 const union value *a_val = &a_cv->value;
3267 const union value *b_val = &b_cv->value;
3268 switch (a_cv->category->type)
3274 case CCT_POSTCOMPUTE:
3275 case CCT_EXCLUDED_MISSING:
3276 /* Must be equal. */
3284 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
3292 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
3294 return a_cv->category->sort_ascending ? cmp : -cmp;
3300 const char *a_label = var_lookup_value_label (var, a_val);
3301 const char *b_label = var_lookup_value_label (var, b_val);
3303 ? (b_label ? strcmp (a_label, b_label) : 1)
3304 : (b_label ? -1 : value_compare_3way (
3305 a_val, b_val, var_get_width (var))));
3307 return a_cv->category->sort_ascending ? cmp : -cmp;
3321 For each ctables_table:
3322 For each combination of row vars:
3323 For each combination of column vars:
3324 For each combination of layer vars:
3326 Make a table of row values:
3327 Sort entries by row values
3328 Assign a 0-based index to each actual value
3329 Construct a dimension
3330 Make a table of column values
3331 Make a table of layer values
3333 Fill the table entry using the indexes from before.
3336 static struct ctables_domain *
3337 ctables_domain_insert (struct ctables_section *s, struct ctables_cell *cell,
3338 enum ctables_domain_type domain)
3341 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3343 const struct ctables_nest *nest = s->nests[a];
3344 for (size_t i = 0; i < nest->n_domains[domain]; i++)
3346 size_t v_idx = nest->domains[domain][i];
3347 struct ctables_cell_value *cv = &cell->axes[a].cvs[v_idx];
3348 hash = hash_pointer (cv->category, hash);
3349 if (cv->category->type != CCT_TOTAL
3350 && cv->category->type != CCT_SUBTOTAL
3351 && cv->category->type != CCT_POSTCOMPUTE)
3352 hash = value_hash (&cv->value,
3353 var_get_width (nest->vars[v_idx]), hash);
3357 struct ctables_domain *d;
3358 HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &s->domains[domain])
3360 const struct ctables_cell *df = d->example;
3361 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3363 const struct ctables_nest *nest = s->nests[a];
3364 for (size_t i = 0; i < nest->n_domains[domain]; i++)
3366 size_t v_idx = nest->domains[domain][i];
3367 struct ctables_cell_value *cv1 = &df->axes[a].cvs[v_idx];
3368 struct ctables_cell_value *cv2 = &cell->axes[a].cvs[v_idx];
3369 if (cv1->category != cv2->category
3370 || (cv1->category->type != CCT_TOTAL
3371 && cv1->category->type != CCT_SUBTOTAL
3372 && cv1->category->type != CCT_POSTCOMPUTE
3373 && !value_equal (&cv1->value, &cv2->value,
3374 var_get_width (nest->vars[v_idx]))))
3383 struct ctables_sum *sums = (s->table->n_sum_vars
3384 ? xzalloc (s->table->n_sum_vars * sizeof *sums)
3387 d = xmalloc (sizeof *d);
3388 *d = (struct ctables_domain) { .example = cell, .sums = sums };
3389 hmap_insert (&s->domains[domain], &d->node, hash);
3393 static struct substring
3394 rtrim_value (const union value *v, const struct variable *var)
3396 struct substring s = ss_buffer (CHAR_CAST (char *, v->s),
3397 var_get_width (var));
3398 ss_rtrim (&s, ss_cstr (" "));
3403 in_string_range (const union value *v, const struct variable *var,
3404 const struct substring *srange)
3406 struct substring s = rtrim_value (v, var);
3407 return ((!srange[0].string || ss_compare (s, srange[0]) >= 0)
3408 && (!srange[1].string || ss_compare (s, srange[1]) <= 0));
3411 static const struct ctables_category *
3412 ctables_categories_match (const struct ctables_categories *c,
3413 const union value *v, const struct variable *var)
3415 if (var_is_numeric (var) && v->f == SYSMIS)
3418 const struct ctables_category *othernm = NULL;
3419 for (size_t i = c->n_cats; i-- > 0; )
3421 const struct ctables_category *cat = &c->cats[i];
3425 if (cat->number == v->f)
3430 if (ss_equals (cat->string, rtrim_value (v, var)))
3435 if ((cat->nrange[0] == -DBL_MAX || v->f >= cat->nrange[0])
3436 && (cat->nrange[1] == DBL_MAX || v->f <= cat->nrange[1]))
3441 if (in_string_range (v, var, cat->srange))
3446 if (var_is_value_missing (var, v))
3450 case CCT_POSTCOMPUTE:
3465 return (cat->include_missing || !var_is_value_missing (var, v) ? cat
3468 case CCT_EXCLUDED_MISSING:
3473 return var_is_value_missing (var, v) ? NULL : othernm;
3476 static const struct ctables_category *
3477 ctables_categories_total (const struct ctables_categories *c)
3479 const struct ctables_category *first = &c->cats[0];
3480 const struct ctables_category *last = &c->cats[c->n_cats - 1];
3481 return (first->type == CCT_TOTAL ? first
3482 : last->type == CCT_TOTAL ? last
3486 static struct ctables_cell *
3487 ctables_cell_insert__ (struct ctables_section *s, const struct ccase *c,
3488 const struct ctables_category *cats[PIVOT_N_AXES][10])
3491 enum ctables_summary_variant sv = CSV_CELL;
3492 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3494 const struct ctables_nest *nest = s->nests[a];
3495 for (size_t i = 0; i < nest->n; i++)
3496 if (i != nest->scale_idx)
3498 hash = hash_pointer (cats[a][i], hash);
3499 if (cats[a][i]->type != CCT_TOTAL
3500 && cats[a][i]->type != CCT_SUBTOTAL
3501 && cats[a][i]->type != CCT_POSTCOMPUTE)
3502 hash = value_hash (case_data (c, nest->vars[i]),
3503 var_get_width (nest->vars[i]), hash);
3509 struct ctables_cell *cell;
3510 HMAP_FOR_EACH_WITH_HASH (cell, struct ctables_cell, node, hash, &s->cells)
3512 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3514 const struct ctables_nest *nest = s->nests[a];
3515 for (size_t i = 0; i < nest->n; i++)
3516 if (i != nest->scale_idx
3517 && (cats[a][i] != cell->axes[a].cvs[i].category
3518 || (cats[a][i]->type != CCT_TOTAL
3519 && cats[a][i]->type != CCT_SUBTOTAL
3520 && cats[a][i]->type != CCT_POSTCOMPUTE
3521 && !value_equal (case_data (c, nest->vars[i]),
3522 &cell->axes[a].cvs[i].value,
3523 var_get_width (nest->vars[i])))))
3532 cell = xmalloc (sizeof *cell);
3535 cell->omit_domains = 0;
3536 cell->postcompute = false;
3537 //struct string name = DS_EMPTY_INITIALIZER;
3538 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3540 const struct ctables_nest *nest = s->nests[a];
3541 cell->axes[a].cvs = (nest->n
3542 ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
3544 for (size_t i = 0; i < nest->n; i++)
3546 const struct ctables_category *cat = cats[a][i];
3547 const struct variable *var = nest->vars[i];
3548 const union value *value = case_data (c, var);
3549 if (i != nest->scale_idx)
3551 const struct ctables_category *subtotal = cat->subtotal;
3552 if (cat->hide || (subtotal && subtotal->hide_subcategories))
3555 if (cat->type == CCT_TOTAL
3556 || cat->type == CCT_SUBTOTAL
3557 || cat->type == CCT_POSTCOMPUTE)
3559 /* XXX these should be more encompassing I think.*/
3563 case PIVOT_AXIS_COLUMN:
3564 cell->omit_domains |= ((1u << CTDT_TABLE) |
3565 (1u << CTDT_LAYER) |
3566 (1u << CTDT_LAYERCOL) |
3567 (1u << CTDT_SUBTABLE) |
3570 case PIVOT_AXIS_ROW:
3571 cell->omit_domains |= ((1u << CTDT_TABLE) |
3572 (1u << CTDT_LAYER) |
3573 (1u << CTDT_LAYERROW) |
3574 (1u << CTDT_SUBTABLE) |
3577 case PIVOT_AXIS_LAYER:
3578 cell->omit_domains |= ((1u << CTDT_TABLE) |
3579 (1u << CTDT_LAYER));
3583 if (cat->type == CCT_POSTCOMPUTE)
3584 cell->postcompute = true;
3587 cell->axes[a].cvs[i].category = cat;
3588 value_clone (&cell->axes[a].cvs[i].value, value, var_get_width (var));
3591 if (i != nest->scale_idx)
3593 if (!ds_is_empty (&name))
3594 ds_put_cstr (&name, ", ");
3595 char *value_s = data_out (value, var_get_encoding (var),
3596 var_get_print_format (var),
3597 settings_get_fmt_settings ());
3598 if (cat->type == CCT_TOTAL
3599 || cat->type == CCT_SUBTOTAL
3600 || cat->type == CCT_POSTCOMPUTE)
3601 ds_put_format (&name, "%s=total", var_get_name (var));
3603 ds_put_format (&name, "%s=%s", var_get_name (var),
3604 value_s + strspn (value_s, " "));
3610 //cell->name = ds_steal_cstr (&name);
3612 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3613 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3614 cell->summaries = xmalloc (specs->n * sizeof *cell->summaries);
3615 for (size_t i = 0; i < specs->n; i++)
3616 ctables_summary_init (&cell->summaries[i], &specs->specs[i]);
3617 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3618 cell->domains[dt] = ctables_domain_insert (s, cell, dt);
3619 hmap_insert (&s->cells, &cell->node, hash);
3624 is_scale_missing (const struct ctables_summary_spec_set *specs,
3625 const struct ccase *c)
3627 if (!specs->is_scale)
3630 if (var_is_num_missing (specs->var, case_num (c, specs->var)))
3633 for (size_t i = 0; i < specs->n_listwise_vars; i++)
3635 const struct variable *var = specs->listwise_vars[i];
3636 if (var_is_num_missing (var, case_num (c, var)))
3644 ctables_cell_add__ (struct ctables_section *s, const struct ccase *c,
3645 const struct ctables_category *cats[PIVOT_N_AXES][10],
3646 bool is_missing, bool excluded_missing,
3647 double d_weight, double e_weight)
3649 struct ctables_cell *cell = ctables_cell_insert__ (s, c, cats);
3650 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3652 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3654 bool scale_missing = is_scale_missing (specs, c);
3655 for (size_t i = 0; i < specs->n; i++)
3656 ctables_summary_add (&cell->summaries[i], &specs->specs[i],
3657 specs->var, case_data (c, specs->var), specs->is_scale,
3658 scale_missing, is_missing, excluded_missing,
3659 d_weight, e_weight);
3660 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3661 if (!(cell->omit_domains && (1u << dt)))
3663 struct ctables_domain *d = cell->domains[dt];
3664 d->d_total += d_weight;
3665 d->e_total += e_weight;
3667 if (!excluded_missing)
3669 d->d_count += d_weight;
3670 d->e_count += e_weight;
3675 d->d_valid += d_weight;
3676 d->e_valid += e_weight;
3679 for (size_t i = 0; i < s->table->n_sum_vars; i++)
3681 /* XXX listwise_missing??? */
3682 const struct variable *var = s->table->sum_vars[i];
3683 double addend = case_num (c, var);
3684 if (!var_is_num_missing (var, addend))
3686 struct ctables_sum *sum = &d->sums[i];
3687 sum->e_sum += addend * e_weight;
3688 sum->u_sum += addend;
3696 recurse_totals (struct ctables_section *s, const struct ccase *c,
3697 const struct ctables_category *cats[PIVOT_N_AXES][10],
3698 bool is_missing, bool excluded_missing,
3699 double d_weight, double e_weight,
3700 enum pivot_axis_type start_axis, size_t start_nest)
3702 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3704 const struct ctables_nest *nest = s->nests[a];
3705 for (size_t i = start_nest; i < nest->n; i++)
3707 if (i == nest->scale_idx)
3710 const struct variable *var = nest->vars[i];
3712 const struct ctables_category *total = ctables_categories_total (
3713 s->table->categories[var_get_dict_index (var)]);
3716 const struct ctables_category *save = cats[a][i];
3718 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3719 d_weight, e_weight);
3720 recurse_totals (s, c, cats, is_missing, excluded_missing,
3721 d_weight, e_weight, a, i + 1);
3730 recurse_subtotals (struct ctables_section *s, const struct ccase *c,
3731 const struct ctables_category *cats[PIVOT_N_AXES][10],
3732 bool is_missing, bool excluded_missing,
3733 double d_weight, double e_weight,
3734 enum pivot_axis_type start_axis, size_t start_nest)
3736 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3738 const struct ctables_nest *nest = s->nests[a];
3739 for (size_t i = start_nest; i < nest->n; i++)
3741 if (i == nest->scale_idx)
3744 const struct ctables_category *save = cats[a][i];
3747 cats[a][i] = save->subtotal;
3748 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3749 d_weight, e_weight);
3750 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3751 d_weight, e_weight, a, i + 1);
3760 ctables_add_occurrence (const struct variable *var,
3761 const union value *value,
3762 struct hmap *occurrences)
3764 int width = var_get_width (var);
3765 unsigned int hash = value_hash (value, width, 0);
3767 struct ctables_occurrence *o;
3768 HMAP_FOR_EACH_WITH_HASH (o, struct ctables_occurrence, node, hash,
3770 if (value_equal (value, &o->value, width))
3773 o = xmalloc (sizeof *o);
3774 value_clone (&o->value, value, width);
3775 hmap_insert (occurrences, &o->node, hash);
3779 ctables_cell_insert (struct ctables_section *s,
3780 const struct ccase *c,
3781 double d_weight, double e_weight)
3783 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
3785 /* Does at least one categorical variable have a missing value in an included
3786 or excluded category? */
3787 bool is_missing = false;
3789 /* Does at least one categorical variable have a missing value in an excluded
3791 bool excluded_missing = false;
3793 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3795 const struct ctables_nest *nest = s->nests[a];
3796 for (size_t i = 0; i < nest->n; i++)
3798 if (i == nest->scale_idx)
3801 const struct variable *var = nest->vars[i];
3802 const union value *value = case_data (c, var);
3804 bool var_missing = var_is_value_missing (var, value) != 0;
3808 cats[a][i] = ctables_categories_match (
3809 s->table->categories[var_get_dict_index (var)], value, var);
3815 static const struct ctables_category cct_excluded_missing = {
3816 .type = CCT_EXCLUDED_MISSING,
3819 cats[a][i] = &cct_excluded_missing;
3820 excluded_missing = true;
3825 if (!excluded_missing)
3826 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3828 const struct ctables_nest *nest = s->nests[a];
3829 for (size_t i = 0; i < nest->n; i++)
3830 if (i != nest->scale_idx)
3832 const struct variable *var = nest->vars[i];
3833 const union value *value = case_data (c, var);
3834 ctables_add_occurrence (var, value, &s->occurrences[a][i]);
3838 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3839 d_weight, e_weight);
3841 //if (!excluded_missing)
3843 recurse_totals (s, c, cats, is_missing, excluded_missing,
3844 d_weight, e_weight, 0, 0);
3845 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3846 d_weight, e_weight, 0, 0);
3852 const struct ctables_summary_spec_set *set;
3857 merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b)
3859 const struct ctables_summary_spec *as = &a->set->specs[a->ofs];
3860 const struct ctables_summary_spec *bs = &b->set->specs[b->ofs];
3861 if (as->function != bs->function)
3862 return as->function > bs->function ? 1 : -1;
3863 else if (as->percentile != bs->percentile)
3864 return as->percentile < bs->percentile ? 1 : -1;
3866 const char *as_label = as->label ? as->label : "";
3867 const char *bs_label = bs->label ? bs->label : "";
3868 return strcmp (as_label, bs_label);
3871 static struct pivot_value *
3872 ctables_category_create_label__ (const struct ctables_category *cat,
3873 const struct variable *var,
3874 const union value *value)
3876 return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
3877 ? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
3878 : pivot_value_new_var_value (var, value));
3881 static struct pivot_value *
3882 ctables_postcompute_label (const struct ctables_categories *cats,
3883 const struct ctables_category *cat,
3884 const struct variable *var,
3885 const union value *value)
3887 struct substring in = ss_cstr (cat->pc->label);
3888 struct substring target = ss_cstr (")LABEL[");
3890 struct string out = DS_EMPTY_INITIALIZER;
3893 size_t chunk = ss_find_substring (in, target);
3894 if (chunk == SIZE_MAX)
3896 if (ds_is_empty (&out))
3897 return pivot_value_new_user_text (in.string, in.length);
3900 ds_put_substring (&out, in);
3901 return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
3905 ds_put_substring (&out, ss_head (in, chunk));
3906 ss_advance (&in, chunk + target.length);
3908 struct substring idx_s;
3909 if (!ss_get_until (&in, ']', &idx_s))
3912 long int idx = strtol (idx_s.string, &tail, 10);
3913 if (idx < 1 || idx > cats->n_cats || tail != ss_end (idx_s))
3916 struct ctables_category *cat2 = &cats->cats[idx - 1];
3917 struct pivot_value *label2
3918 = ctables_category_create_label__ (cat2, var, value);
3919 char *label2_s = pivot_value_to_string_defaults (label2);
3920 ds_put_cstr (&out, label2_s);
3922 pivot_value_destroy (label2);
3927 return pivot_value_new_user_text (cat->pc->label, SIZE_MAX);
3930 static struct pivot_value *
3931 ctables_category_create_label (const struct ctables_categories *cats,
3932 const struct ctables_category *cat,
3933 const struct variable *var,
3934 const union value *value)
3936 return (cat->type == CCT_POSTCOMPUTE && cat->pc->label
3937 ? ctables_postcompute_label (cats, cat, var, value)
3938 : ctables_category_create_label__ (cat, var, value));
3941 static struct ctables_value *
3942 ctables_value_find__ (struct ctables_table *t, const union value *value,
3943 int width, unsigned int hash)
3945 struct ctables_value *clv;
3946 HMAP_FOR_EACH_WITH_HASH (clv, struct ctables_value, node,
3947 hash, &t->clabels_values_map)
3948 if (value_equal (value, &clv->value, width))
3954 ctables_value_insert (struct ctables_table *t, const union value *value,
3957 unsigned int hash = value_hash (value, width, 0);
3958 struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
3961 clv = xmalloc (sizeof *clv);
3962 value_clone (&clv->value, value, width);
3963 hmap_insert (&t->clabels_values_map, &clv->node, hash);
3967 static struct ctables_value *
3968 ctables_value_find (struct ctables_table *t,
3969 const union value *value, int width)
3971 return ctables_value_find__ (t, value, width,
3972 value_hash (value, width, 0));
3976 ctables_table_add_section (struct ctables_table *t, enum pivot_axis_type a,
3977 size_t ix[PIVOT_N_AXES])
3979 if (a < PIVOT_N_AXES)
3981 size_t limit = MAX (t->stacks[a].n, 1);
3982 for (ix[a] = 0; ix[a] < limit; ix[a]++)
3983 ctables_table_add_section (t, a + 1, ix);
3987 struct ctables_section *s = &t->sections[t->n_sections++];
3988 *s = (struct ctables_section) {
3990 .cells = HMAP_INITIALIZER (s->cells),
3992 for (a = 0; a < PIVOT_N_AXES; a++)
3995 struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
3997 s->occurrences[a] = xnmalloc (nest->n, sizeof *s->occurrences[a]);
3998 for (size_t i = 0; i < nest->n; i++)
3999 hmap_init (&s->occurrences[a][i]);
4001 for (size_t i = 0; i < N_CTDTS; i++)
4002 hmap_init (&s->domains[i]);
4007 ctpo_add (double a, double b)
4013 ctpo_sub (double a, double b)
4019 ctpo_mul (double a, double b)
4025 ctpo_div (double a, double b)
4027 return b ? a / b : SYSMIS;
4031 ctpo_pow (double a, double b)
4033 int save_errno = errno;
4035 double result = pow (a, b);
4043 ctpo_neg (double a, double b UNUSED)
4048 struct ctables_pcexpr_evaluate_ctx
4050 const struct ctables_cell *cell;
4051 const struct ctables_section *section;
4052 const struct ctables_categories *cats;
4053 enum pivot_axis_type pc_a;
4058 static double ctables_pcexpr_evaluate (
4059 const struct ctables_pcexpr_evaluate_ctx *, const struct ctables_pcexpr *);
4062 ctables_pcexpr_evaluate_nonterminal (
4063 const struct ctables_pcexpr_evaluate_ctx *ctx,
4064 const struct ctables_pcexpr *e, size_t n_args,
4065 double evaluate (double, double))
4067 double args[2] = { 0, 0 };
4068 for (size_t i = 0; i < n_args; i++)
4070 args[i] = ctables_pcexpr_evaluate (ctx, e->subs[i]);
4071 if (!isfinite (args[i]) || args[i] == SYSMIS)
4074 return evaluate (args[0], args[1]);
4078 ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
4079 const struct ctables_cell_value *pc_cv)
4081 const struct ctables_section *s = ctx->section;
4084 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4086 const struct ctables_nest *nest = s->nests[a];
4087 for (size_t i = 0; i < nest->n; i++)
4088 if (i != nest->scale_idx)
4090 const struct ctables_cell_value *cv
4091 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
4092 : &ctx->cell->axes[a].cvs[i]);
4093 hash = hash_pointer (cv->category, hash);
4094 if (cv->category->type != CCT_TOTAL
4095 && cv->category->type != CCT_SUBTOTAL
4096 && cv->category->type != CCT_POSTCOMPUTE)
4097 hash = value_hash (&cv->value,
4098 var_get_width (nest->vars[i]), hash);
4102 struct ctables_cell *tc;
4103 HMAP_FOR_EACH_WITH_HASH (tc, struct ctables_cell, node, hash, &s->cells)
4105 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4107 const struct ctables_nest *nest = s->nests[a];
4108 for (size_t i = 0; i < nest->n; i++)
4109 if (i != nest->scale_idx)
4111 const struct ctables_cell_value *p_cv
4112 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
4113 : &ctx->cell->axes[a].cvs[i]);
4114 const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
4115 if (p_cv->category != t_cv->category
4116 || (p_cv->category->type != CCT_TOTAL
4117 && p_cv->category->type != CCT_SUBTOTAL
4118 && p_cv->category->type != CCT_POSTCOMPUTE
4119 && !value_equal (&p_cv->value,
4121 var_get_width (nest->vars[i]))))
4133 const struct ctables_table *t = s->table;
4134 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
4135 const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
4136 return ctables_summary_value (tc, &tc->summaries[ctx->summary_idx],
4137 &specs->specs[ctx->summary_idx]);
4141 ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
4142 const struct ctables_pcexpr *e)
4149 case CTPO_CAT_NRANGE:
4150 case CTPO_CAT_SRANGE:
4152 struct ctables_cell_value cv = {
4153 .category = ctables_find_category_for_postcompute (ctx->cats, e)
4155 assert (cv.category != NULL);
4157 struct hmap *occurrences = &ctx->section->occurrences[ctx->pc_a][ctx->pc_a_idx];
4158 const struct ctables_occurrence *o;
4161 const struct variable *var = ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx];
4162 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
4163 if (ctables_categories_match (ctx->cats, &o->value, var) == cv.category)
4165 cv.value = o->value;
4166 sum += ctables_pcexpr_evaluate_category (ctx, &cv);
4171 case CTPO_CAT_NUMBER:
4172 case CTPO_CAT_STRING:
4173 case CTPO_CAT_MISSING:
4174 case CTPO_CAT_OTHERNM:
4175 case CTPO_CAT_SUBTOTAL:
4176 case CTPO_CAT_TOTAL:
4178 struct ctables_cell_value cv = {
4179 .category = ctables_find_category_for_postcompute (ctx->cats, e),
4180 .value = { .f = e->number },
4182 assert (cv.category != NULL);
4183 return ctables_pcexpr_evaluate_category (ctx, &cv);
4187 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
4190 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_sub);
4193 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_mul);
4196 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_div);
4199 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_pow);
4202 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 1, ctpo_neg);
4208 /* XXX what if there is a postcompute in more than one dimension?? */
4209 static const struct ctables_postcompute *
4210 ctables_cell_postcompute (const struct ctables_section *s,
4211 const struct ctables_cell *cell,
4212 enum pivot_axis_type *pc_a_p,
4215 assert (cell->postcompute);
4216 const struct ctables_postcompute *pc = NULL;
4217 for (enum pivot_axis_type pc_a = 0; pc_a < PIVOT_N_AXES; pc_a++)
4218 for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
4220 const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
4221 if (cv->category->type == CCT_POSTCOMPUTE)
4225 /* Multiple postcomputes cross each other. The value is
4230 pc = cv->category->pc;
4234 *pc_a_idx_p = pc_a_idx;
4238 assert (pc != NULL);
4243 ctables_cell_calculate_postcompute (const struct ctables_section *s,
4244 const struct ctables_cell *cell,
4245 const struct ctables_summary_spec *ss,
4246 struct fmt_spec *format,
4247 bool *is_ctables_format,
4250 enum pivot_axis_type pc_a;
4252 const struct ctables_postcompute *pc = ctables_cell_postcompute (
4253 s, cell, &pc_a, &pc_a_idx);
4259 for (size_t i = 0; i < pc->specs->n; i++)
4261 const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
4262 if (ss->function == ss2->function
4263 && ss->percentile == ss2->percentile)
4265 *format = ss2->format;
4266 *is_ctables_format = ss2->is_ctables_format;
4272 const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
4273 const struct ctables_categories *cats = s->table->categories[
4274 var_get_dict_index (var)];
4275 struct ctables_pcexpr_evaluate_ctx ctx = {
4280 .pc_a_idx = pc_a_idx,
4281 .summary_idx = summary_idx,
4283 return ctables_pcexpr_evaluate (&ctx, pc->expr);
4287 ctables_table_output (struct ctables *ct, struct ctables_table *t)
4289 struct pivot_table *pt = pivot_table_create__ (
4291 ? pivot_value_new_user_text (t->title, SIZE_MAX)
4292 : pivot_value_new_text (N_("Custom Tables"))),
4295 pivot_table_set_caption (
4296 pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
4298 pivot_table_set_corner_text (
4299 pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
4301 bool summary_dimension = (t->summary_axis != t->slabels_axis
4302 || (!t->slabels_visible
4303 && t->summary_specs.n > 1));
4304 if (summary_dimension)
4306 struct pivot_dimension *d = pivot_dimension_create (
4307 pt, t->slabels_axis, N_("Statistics"));
4308 const struct ctables_summary_spec_set *specs = &t->summary_specs;
4309 if (!t->slabels_visible)
4310 d->hide_all_labels = true;
4311 for (size_t i = 0; i < specs->n; i++)
4312 pivot_category_create_leaf (
4313 d->root, ctables_summary_label (&specs->specs[i], t->cilevel));
4316 bool categories_dimension = t->clabels_example != NULL;
4317 if (categories_dimension)
4319 struct pivot_dimension *d = pivot_dimension_create (
4320 pt, t->label_axis[t->clabels_from_axis],
4321 t->clabels_from_axis == PIVOT_AXIS_ROW
4322 ? N_("Row Categories")
4323 : N_("Column Categories"));
4324 const struct variable *var = t->clabels_example;
4325 const struct ctables_categories *c = t->categories[var_get_dict_index (var)];
4326 for (size_t i = 0; i < t->n_clabels_values; i++)
4328 const struct ctables_value *value = t->clabels_values[i];
4329 const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
4330 assert (cat != NULL);
4331 pivot_category_create_leaf (d->root, ctables_category_create_label (
4332 c, cat, t->clabels_example,
4337 pivot_table_set_look (pt, ct->look);
4338 struct pivot_dimension *d[PIVOT_N_AXES];
4339 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4341 static const char *names[] = {
4342 [PIVOT_AXIS_ROW] = N_("Rows"),
4343 [PIVOT_AXIS_COLUMN] = N_("Columns"),
4344 [PIVOT_AXIS_LAYER] = N_("Layers"),
4346 d[a] = (t->axes[a] || a == t->summary_axis
4347 ? pivot_dimension_create (pt, a, names[a])
4352 assert (t->axes[a]);
4354 for (size_t i = 0; i < t->stacks[a].n; i++)
4356 struct ctables_nest *nest = &t->stacks[a].nests[i];
4357 struct ctables_section **sections = xnmalloc (t->n_sections,
4359 size_t n_sections = 0;
4361 size_t n_total_cells = 0;
4362 size_t max_depth = 0;
4363 for (size_t j = 0; j < t->n_sections; j++)
4364 if (t->sections[j].nests[a] == nest)
4366 struct ctables_section *s = &t->sections[j];
4367 sections[n_sections++] = s;
4368 n_total_cells += s->cells.count;
4370 size_t depth = s->nests[a]->n;
4371 max_depth = MAX (depth, max_depth);
4374 struct ctables_cell **sorted = xnmalloc (n_total_cells,
4376 size_t n_sorted = 0;
4378 for (size_t j = 0; j < n_sections; j++)
4380 struct ctables_section *s = sections[j];
4382 struct ctables_cell *cell;
4383 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4385 sorted[n_sorted++] = cell;
4386 assert (n_sorted <= n_total_cells);
4389 struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
4390 sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
4393 for (size_t j = 0; j < n_sorted; j++)
4395 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);
4400 struct ctables_level
4402 enum ctables_level_type
4404 CTL_VAR, /* Variable label for nest->vars[var_idx]. */
4405 CTL_CATEGORY, /* Category for nest->vars[var_idx]. */
4406 CTL_SUMMARY, /* Summary functions. */
4410 enum settings_value_show vlabel; /* CTL_VAR only. */
4413 struct ctables_level *levels = xnmalloc (1 + 2 * max_depth, sizeof *levels);
4414 size_t n_levels = 0;
4415 for (size_t k = 0; k < nest->n; k++)
4417 enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k])];
4418 if (vlabel != CTVL_NONE)
4420 levels[n_levels++] = (struct ctables_level) {
4422 .vlabel = (enum settings_value_show) vlabel,
4427 if (nest->scale_idx != k
4428 && (k != nest->n - 1 || t->label_axis[a] == a))
4430 levels[n_levels++] = (struct ctables_level) {
4431 .type = CTL_CATEGORY,
4437 if (!summary_dimension && a == t->slabels_axis)
4439 levels[n_levels++] = (struct ctables_level) {
4440 .type = CTL_SUMMARY,
4441 .var_idx = SIZE_MAX,
4445 /* Pivot categories:
4447 - variable label for nest->vars[0], if vlabel != CTVL_NONE
4448 - category for nest->vars[0], if nest->scale_idx != 0
4449 - variable label for nest->vars[1], if vlabel != CTVL_NONE
4450 - category for nest->vars[1], if nest->scale_idx != 1
4452 - variable label for nest->vars[n - 1], if vlabel != CTVL_NONE
4453 - category for nest->vars[n - 1], if t->label_axis[a] == a && nest->scale_idx != n - 1.
4454 - summary function, if 'a == t->slabels_axis && a ==
4457 Additional dimensions:
4459 - If 'a == t->slabels_axis && a != t->summary_axis', add a summary
4461 - If 't->label_axis[b] == a' for some 'b != a', add a category
4466 struct pivot_category **groups = xnmalloc (1 + 2 * max_depth, sizeof *groups);
4468 for (size_t j = 0; j < n_sorted; j++)
4470 struct ctables_cell *cell = sorted[j];
4471 struct ctables_cell *prev = j > 0 ? sorted[j - 1] : NULL;
4473 size_t n_common = 0;
4476 for (; n_common < n_levels; n_common++)
4478 const struct ctables_level *level = &levels[n_common];
4479 if (level->type == CTL_CATEGORY)
4481 size_t var_idx = level->var_idx;
4482 const struct ctables_category *c = cell->axes[a].cvs[var_idx].category;
4483 if (prev->axes[a].cvs[var_idx].category != c)
4485 else if (c->type != CCT_SUBTOTAL
4486 && c->type != CCT_TOTAL
4487 && c->type != CCT_POSTCOMPUTE
4488 && !value_equal (&prev->axes[a].cvs[var_idx].value,
4489 &cell->axes[a].cvs[var_idx].value,
4490 var_get_type (nest->vars[var_idx])))
4496 for (size_t k = n_common; k < n_levels; k++)
4498 const struct ctables_level *level = &levels[k];
4499 struct pivot_category *parent = k ? groups[k - 1] : d[a]->root;
4500 if (level->type == CTL_SUMMARY)
4502 assert (k == n_levels - 1);
4504 const struct ctables_summary_spec_set *specs = &t->summary_specs;
4505 for (size_t m = 0; m < specs->n; m++)
4507 int leaf = pivot_category_create_leaf (
4508 parent, ctables_summary_label (&specs->specs[m],
4516 const struct variable *var = nest->vars[level->var_idx];
4517 struct pivot_value *label;
4518 if (level->type == CTL_VAR)
4520 label = pivot_value_new_variable (var);
4521 label->variable.show = level->vlabel;
4523 else if (level->type == CTL_CATEGORY)
4525 const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
4526 label = ctables_category_create_label (
4527 t->categories[var_get_dict_index (var)],
4528 cv->category, var, &cv->value);
4533 if (k == n_levels - 1)
4534 prev_leaf = pivot_category_create_leaf (parent, label);
4536 groups[k] = pivot_category_create_group__ (parent, label);
4540 cell->axes[a].leaf = prev_leaf;
4547 for (size_t i = 0; i < t->n_sections; i++)
4549 struct ctables_section *s = &t->sections[i];
4551 struct ctables_cell *cell;
4552 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4557 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
4558 const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
4559 for (size_t j = 0; j < specs->n; j++)
4562 size_t n_dindexes = 0;
4564 if (summary_dimension)
4565 dindexes[n_dindexes++] = specs->specs[j].axis_idx;
4567 if (categories_dimension)
4569 const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
4570 const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
4571 const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
4572 const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
4575 dindexes[n_dindexes++] = ctv->leaf;
4578 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4581 int leaf = cell->axes[a].leaf;
4582 if (a == t->summary_axis && !summary_dimension)
4584 dindexes[n_dindexes++] = leaf;
4587 const struct ctables_summary_spec *ss = &specs->specs[j];
4589 struct fmt_spec format = specs->specs[j].format;
4590 bool is_ctables_format = ss->is_ctables_format;
4591 double d = (cell->postcompute
4592 ? ctables_cell_calculate_postcompute (
4593 s, cell, ss, &format, &is_ctables_format, j)
4594 : ctables_summary_value (cell, &cell->summaries[j],
4597 struct pivot_value *value;
4598 if (ct->hide_threshold != 0
4599 && d < ct->hide_threshold
4600 && ctables_summary_function_is_count (ss->function))
4602 value = pivot_value_new_user_text_nocopy (
4603 xasprintf ("<%d", ct->hide_threshold));
4605 else if (d == 0 && ct->zero)
4606 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
4607 else if (d == SYSMIS && ct->missing)
4608 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
4609 else if (is_ctables_format)
4611 char *s = data_out_stretchy (&(union value) { .f = d },
4613 &ct->ctables_formats, NULL);
4614 value = pivot_value_new_user_text_nocopy (s);
4618 value = pivot_value_new_number (d);
4619 value->numeric.format = format;
4621 pivot_table_put (pt, dindexes, n_dindexes, value);
4626 pivot_table_submit (pt);
4630 ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
4632 enum pivot_axis_type label_pos = t->label_axis[a];
4636 t->clabels_from_axis = a;
4638 const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
4639 const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
4641 const struct ctables_stack *stack = &t->stacks[a];
4645 const struct ctables_nest *n0 = &stack->nests[0];
4647 const struct variable *v0 = n0->vars[n0->n - 1];
4648 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4649 t->clabels_example = v0;
4651 for (size_t i = 0; i < c0->n_cats; i++)
4652 if (c0->cats[i].type == CCT_FUNCTION)
4654 msg (SE, _("%s=%s is not allowed with sorting based "
4655 "on a summary function."),
4656 subcommand_name, pos_name);
4659 if (n0->n - 1 == n0->scale_idx)
4661 msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
4662 "but %s is a scale variable."),
4663 subcommand_name, pos_name, var_get_name (v0));
4667 for (size_t i = 1; i < stack->n; i++)
4669 const struct ctables_nest *ni = &stack->nests[i];
4671 const struct variable *vi = ni->vars[ni->n - 1];
4672 struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
4674 if (ni->n - 1 == ni->scale_idx)
4676 msg (SE, _("%s=%s requires the variables to be moved to be "
4677 "categorical, but %s is a scale variable."),
4678 subcommand_name, pos_name, var_get_name (vi));
4681 if (var_get_width (v0) != var_get_width (vi))
4683 msg (SE, _("%s=%s requires the variables to be "
4684 "moved to have the same width, but %s has "
4685 "width %d and %s has width %d."),
4686 subcommand_name, pos_name,
4687 var_get_name (v0), var_get_width (v0),
4688 var_get_name (vi), var_get_width (vi));
4691 if (!val_labs_equal (var_get_value_labels (v0),
4692 var_get_value_labels (vi)))
4694 msg (SE, _("%s=%s requires the variables to be "
4695 "moved to have the same value labels, but %s "
4696 "and %s have different value labels."),
4697 subcommand_name, pos_name,
4698 var_get_name (v0), var_get_name (vi));
4701 if (!ctables_categories_equal (c0, ci))
4703 msg (SE, _("%s=%s requires the variables to be "
4704 "moved to have the same category "
4705 "specifications, but %s and %s have different "
4706 "category specifications."),
4707 subcommand_name, pos_name,
4708 var_get_name (v0), var_get_name (vi));
4717 add_sum_var (struct variable *var,
4718 struct variable ***sum_vars, size_t *n, size_t *allocated)
4720 for (size_t i = 0; i < *n; i++)
4721 if (var == (*sum_vars)[i])
4724 if (*n >= *allocated)
4725 *sum_vars = x2nrealloc (*sum_vars, allocated, sizeof **sum_vars);
4726 (*sum_vars)[*n] = var;
4731 enumerate_sum_vars (const struct ctables_axis *a,
4732 struct variable ***sum_vars, size_t *n, size_t *allocated)
4740 for (size_t i = 0; i < N_CSVS; i++)
4741 for (size_t j = 0; j < a->specs[i].n; j++)
4743 struct ctables_summary_spec *spec = &a->specs[i].specs[j];
4744 if (ctables_function_is_pctsum (spec->function))
4745 spec->sum_var_idx = add_sum_var (a->var, sum_vars, n, allocated);
4751 for (size_t i = 0; i < 2; i++)
4752 enumerate_sum_vars (a->subs[i], sum_vars, n, allocated);
4758 ctables_prepare_table (struct ctables_table *t)
4760 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4763 t->stacks[a] = enumerate_fts (a, t->axes[a]);
4765 for (size_t j = 0; j < t->stacks[a].n; j++)
4767 struct ctables_nest *nest = &t->stacks[a].nests[j];
4768 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
4770 nest->domains[dt] = xmalloc (nest->n * sizeof *nest->domains[dt]);
4771 nest->n_domains[dt] = 0;
4773 for (size_t k = 0; k < nest->n; k++)
4775 if (k == nest->scale_idx)
4784 if (a != PIVOT_AXIS_LAYER)
4791 if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
4792 : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
4793 : a == PIVOT_AXIS_ROW)
4795 if (k == nest->n - 1
4796 || (nest->scale_idx == nest->n - 1
4797 && k == nest->n - 2))
4803 if (a == PIVOT_AXIS_COLUMN)
4808 if (a == PIVOT_AXIS_ROW)
4813 nest->domains[dt][nest->n_domains[dt]++] = k;
4820 struct ctables_nest *nest = xmalloc (sizeof *nest);
4821 *nest = (struct ctables_nest) { .n = 0 };
4822 t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
4825 struct ctables_stack *stack = &t->stacks[t->summary_axis];
4826 for (size_t i = 0; i < stack->n; i++)
4828 struct ctables_nest *nest = &stack->nests[i];
4829 if (!nest->specs[CSV_CELL].n)
4831 struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
4832 specs->specs = xmalloc (sizeof *specs->specs);
4835 enum ctables_summary_function function
4836 = specs->is_scale ? CTSF_MEAN : CTSF_COUNT;
4838 *specs->specs = (struct ctables_summary_spec) {
4839 .function = function,
4840 .format = ctables_summary_default_format (function, specs->var),
4843 specs->var = nest->vars[0];
4845 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4846 &nest->specs[CSV_CELL]);
4848 else if (!nest->specs[CSV_TOTAL].n)
4849 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4850 &nest->specs[CSV_CELL]);
4852 if (t->ctables->smissing_listwise)
4854 struct variable **listwise_vars = NULL;
4856 size_t allocated = 0;
4858 for (size_t j = nest->group_head; j < stack->n; j++)
4860 const struct ctables_nest *other_nest = &stack->nests[j];
4861 if (other_nest->group_head != nest->group_head)
4864 if (nest != other_nest && other_nest->scale_idx < other_nest->n)
4867 listwise_vars = x2nrealloc (listwise_vars, &allocated,
4868 sizeof *listwise_vars);
4869 listwise_vars[n++] = other_nest->vars[other_nest->scale_idx];
4872 for (size_t j = 0; j < N_CSVS; j++)
4874 nest->specs[j].listwise_vars = listwise_vars;
4875 nest->specs[j].n_listwise_vars = n;
4880 struct ctables_summary_spec_set *merged = &t->summary_specs;
4881 struct merge_item *items = xnmalloc (N_CSVS * stack->n, sizeof *items);
4883 for (size_t j = 0; j < stack->n; j++)
4885 const struct ctables_nest *nest = &stack->nests[j];
4887 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4888 items[n_left++] = (struct merge_item) { .set = &nest->specs[sv] };
4893 struct merge_item min = items[0];
4894 for (size_t j = 1; j < n_left; j++)
4895 if (merge_item_compare_3way (&items[j], &min) < 0)
4898 if (merged->n >= merged->allocated)
4899 merged->specs = x2nrealloc (merged->specs, &merged->allocated,
4900 sizeof *merged->specs);
4901 merged->specs[merged->n++] = min.set->specs[min.ofs];
4903 for (size_t j = 0; j < n_left; )
4905 if (merge_item_compare_3way (&items[j], &min) == 0)
4907 struct merge_item *item = &items[j];
4908 item->set->specs[item->ofs].axis_idx = merged->n - 1;
4909 if (++item->ofs >= item->set->n)
4911 items[j] = items[--n_left];
4920 for (size_t j = 0; j < merged->n; j++)
4921 printf ("%s\n", ctables_summary_function_name (merged->specs[j].function));
4923 for (size_t j = 0; j < stack->n; j++)
4925 const struct ctables_nest *nest = &stack->nests[j];
4926 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4928 const struct ctables_summary_spec_set *specs = &nest->specs[sv];
4929 for (size_t k = 0; k < specs->n; k++)
4930 printf ("(%s, %zu) ", ctables_summary_function_name (specs->specs[k].function),
4931 specs->specs[k].axis_idx);
4937 size_t allocated_sum_vars = 0;
4938 enumerate_sum_vars (t->axes[t->summary_axis],
4939 &t->sum_vars, &t->n_sum_vars, &allocated_sum_vars);
4941 return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
4942 && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
4946 ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
4947 enum pivot_axis_type a)
4949 struct ctables_stack *stack = &t->stacks[a];
4950 for (size_t i = 0; i < stack->n; i++)
4952 const struct ctables_nest *nest = &stack->nests[i];
4953 const struct variable *var = nest->vars[nest->n - 1];
4954 const union value *value = case_data (c, var);
4956 if (var_is_numeric (var) && value->f == SYSMIS)
4959 if (ctables_categories_match (t->categories [var_get_dict_index (var)],
4961 ctables_value_insert (t, value, var_get_width (var));
4966 compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
4968 const struct ctables_value *const *ap = a_;
4969 const struct ctables_value *const *bp = b_;
4970 const struct ctables_value *a = *ap;
4971 const struct ctables_value *b = *bp;
4972 const int *width = width_;
4973 return value_compare_3way (&a->value, &b->value, *width);
4977 ctables_sort_clabels_values (struct ctables_table *t)
4979 const struct variable *v0 = t->clabels_example;
4980 int width = var_get_width (v0);
4982 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4985 const struct val_labs *val_labs = var_get_value_labels (v0);
4986 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4987 vl = val_labs_next (val_labs, vl))
4988 if (ctables_categories_match (c0, &vl->value, v0))
4989 ctables_value_insert (t, &vl->value, width);
4992 size_t n = hmap_count (&t->clabels_values_map);
4993 t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
4995 struct ctables_value *clv;
4997 HMAP_FOR_EACH (clv, struct ctables_value, node, &t->clabels_values_map)
4998 t->clabels_values[i++] = clv;
4999 t->n_clabels_values = n;
5002 sort (t->clabels_values, n, sizeof *t->clabels_values,
5003 compare_clabels_values_3way, &width);
5005 for (size_t i = 0; i < n; i++)
5006 t->clabels_values[i]->leaf = i;
5010 ctables_add_category_occurrences (const struct variable *var,
5011 struct hmap *occurrences,
5012 const struct ctables_categories *cats)
5014 const struct val_labs *val_labs = var_get_value_labels (var);
5016 for (size_t i = 0; i < cats->n_cats; i++)
5018 const struct ctables_category *c = &cats->cats[i];
5022 ctables_add_occurrence (var, &(const union value) { .f = c->number },
5028 int width = var_get_width (var);
5030 value_init (&value, width);
5031 value_copy_buf_rpad (&value, width,
5032 CHAR_CAST (uint8_t *, c->string.string),
5033 c->string.length, ' ');
5034 ctables_add_occurrence (var, &value, occurrences);
5035 value_destroy (&value, width);
5040 assert (var_is_numeric (var));
5041 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5042 vl = val_labs_next (val_labs, vl))
5043 if (vl->value.f >= c->nrange[0] && vl->value.f <= c->nrange[1])
5044 ctables_add_occurrence (var, &vl->value, occurrences);
5048 assert (var_is_alpha (var));
5049 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5050 vl = val_labs_next (val_labs, vl))
5051 if (in_string_range (&vl->value, var, c->srange))
5052 ctables_add_occurrence (var, &vl->value, occurrences);
5056 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5057 vl = val_labs_next (val_labs, vl))
5058 if (var_is_value_missing (var, &vl->value))
5059 ctables_add_occurrence (var, &vl->value, occurrences);
5063 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5064 vl = val_labs_next (val_labs, vl))
5065 ctables_add_occurrence (var, &vl->value, occurrences);
5068 case CCT_POSTCOMPUTE:
5078 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5079 vl = val_labs_next (val_labs, vl))
5080 if (c->include_missing || !var_is_value_missing (var, &vl->value))
5081 ctables_add_occurrence (var, &vl->value, occurrences);
5084 case CCT_EXCLUDED_MISSING:
5091 ctables_section_recurse_add_empty_categories (
5092 struct ctables_section *s,
5093 const struct ctables_category *cats[PIVOT_N_AXES][10], struct ccase *c,
5094 enum pivot_axis_type a, size_t a_idx)
5096 if (a >= PIVOT_N_AXES)
5097 ctables_cell_insert__ (s, c, cats);
5098 else if (!s->nests[a] || a_idx >= s->nests[a]->n)
5099 ctables_section_recurse_add_empty_categories (s, cats, c, a + 1, 0);
5102 const struct variable *var = s->nests[a]->vars[a_idx];
5103 const struct ctables_categories *categories = s->table->categories[
5104 var_get_dict_index (var)];
5105 int width = var_get_width (var);
5106 const struct hmap *occurrences = &s->occurrences[a][a_idx];
5107 const struct ctables_occurrence *o;
5108 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
5110 union value *value = case_data_rw (c, var);
5111 value_destroy (value, width);
5112 value_clone (value, &o->value, width);
5113 cats[a][a_idx] = ctables_categories_match (categories, value, var);
5114 assert (cats[a][a_idx] != NULL);
5115 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
5118 for (size_t i = 0; i < categories->n_cats; i++)
5120 const struct ctables_category *cat = &categories->cats[i];
5121 if (cat->type == CCT_POSTCOMPUTE)
5123 cats[a][a_idx] = cat;
5124 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
5131 ctables_section_add_empty_categories (struct ctables_section *s)
5133 bool show_empty = false;
5134 for (size_t a = 0; a < PIVOT_N_AXES; a++)
5136 for (size_t k = 0; k < s->nests[a]->n; k++)
5137 if (k != s->nests[a]->scale_idx)
5139 const struct variable *var = s->nests[a]->vars[k];
5140 const struct ctables_categories *cats = s->table->categories[
5141 var_get_dict_index (var)];
5142 if (cats->show_empty)
5145 ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
5151 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
5152 struct ccase *c = case_create (dict_get_proto (s->table->ctables->dict));
5153 ctables_section_recurse_add_empty_categories (s, cats, c, 0, 0);
5158 ctables_execute (struct dataset *ds, struct ctables *ct)
5160 for (size_t i = 0; i < ct->n_tables; i++)
5162 struct ctables_table *t = ct->tables[i];
5163 t->sections = xnmalloc (MAX (1, t->stacks[PIVOT_AXIS_ROW].n) *
5164 MAX (1, t->stacks[PIVOT_AXIS_COLUMN].n) *
5165 MAX (1, t->stacks[PIVOT_AXIS_LAYER].n),
5166 sizeof *t->sections);
5167 size_t ix[PIVOT_N_AXES];
5168 ctables_table_add_section (t, 0, ix);
5171 struct casereader *input = proc_open (ds);
5172 bool warn_on_invalid = true;
5173 for (struct ccase *c = casereader_read (input); c;
5174 case_unref (c), c = casereader_read (input))
5176 double d_weight = dict_get_case_weight (dataset_dict (ds), c,
5178 double e_weight = (ct->e_weight
5179 ? var_force_valid_weight (ct->e_weight,
5180 case_num (c, ct->e_weight),
5184 for (size_t i = 0; i < ct->n_tables; i++)
5186 struct ctables_table *t = ct->tables[i];
5188 for (size_t j = 0; j < t->n_sections; j++)
5189 ctables_cell_insert (&t->sections[j], c, d_weight, e_weight);
5191 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5192 if (t->label_axis[a] != a)
5193 ctables_insert_clabels_values (t, c, a);
5196 casereader_destroy (input);
5198 for (size_t i = 0; i < ct->n_tables; i++)
5200 struct ctables_table *t = ct->tables[i];
5202 if (t->clabels_example)
5203 ctables_sort_clabels_values (t);
5205 for (size_t j = 0; j < t->n_sections; j++)
5206 ctables_section_add_empty_categories (&t->sections[j]);
5208 ctables_table_output (ct, ct->tables[i]);
5210 return proc_commit (ds);
5215 typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *,
5216 struct dictionary *);
5219 ctables_pcexpr_destroy (struct ctables_pcexpr *e)
5225 case CTPO_CAT_STRING:
5226 ss_dealloc (&e->string);
5229 case CTPO_CAT_SRANGE:
5230 for (size_t i = 0; i < 2; i++)
5231 ss_dealloc (&e->srange[i]);
5240 for (size_t i = 0; i < 2; i++)
5241 ctables_pcexpr_destroy (e->subs[i]);
5245 case CTPO_CAT_NUMBER:
5246 case CTPO_CAT_NRANGE:
5247 case CTPO_CAT_MISSING:
5248 case CTPO_CAT_OTHERNM:
5249 case CTPO_CAT_SUBTOTAL:
5250 case CTPO_CAT_TOTAL:
5254 msg_location_destroy (e->location);
5259 static struct ctables_pcexpr *
5260 ctables_pcexpr_allocate_binary (enum ctables_postcompute_op op,
5261 struct ctables_pcexpr *sub0,
5262 struct ctables_pcexpr *sub1)
5264 struct ctables_pcexpr *e = xmalloc (sizeof *e);
5265 *e = (struct ctables_pcexpr) {
5267 .subs = { sub0, sub1 },
5268 .location = msg_location_merged (sub0->location, sub1->location),
5273 /* How to parse an operator. */
5276 enum token_type token;
5277 enum ctables_postcompute_op op;
5280 static const struct operator *
5281 ctable_pcexpr_match_operator (struct lexer *lexer,
5282 const struct operator ops[], size_t n_ops)
5284 for (const struct operator *op = ops; op < ops + n_ops; op++)
5285 if (lex_token (lexer) == op->token)
5287 if (op->token != T_NEG_NUM)
5296 static struct ctables_pcexpr *
5297 ctable_pcexpr_parse_binary_operators__ (
5298 struct lexer *lexer, struct dictionary *dict,
5299 const struct operator ops[], size_t n_ops,
5300 parse_recursively_func *parse_next_level,
5301 const char *chain_warning, struct ctables_pcexpr *lhs)
5303 for (int op_count = 0; ; op_count++)
5305 const struct operator *op
5306 = ctable_pcexpr_match_operator (lexer, ops, n_ops);
5309 if (op_count > 1 && chain_warning)
5310 msg_at (SW, lhs->location, "%s", chain_warning);
5315 struct ctables_pcexpr *rhs = parse_next_level (lexer, dict);
5318 ctables_pcexpr_destroy (lhs);
5322 lhs = ctables_pcexpr_allocate_binary (op->op, lhs, rhs);
5326 static struct ctables_pcexpr *
5327 ctable_pcexpr_parse_binary_operators (struct lexer *lexer,
5328 struct dictionary *dict,
5329 const struct operator ops[], size_t n_ops,
5330 parse_recursively_func *parse_next_level,
5331 const char *chain_warning)
5333 struct ctables_pcexpr *lhs = parse_next_level (lexer, dict);
5337 return ctable_pcexpr_parse_binary_operators__ (lexer, dict, ops, n_ops,
5339 chain_warning, lhs);
5342 static struct ctables_pcexpr *ctable_pcexpr_parse_add (struct lexer *,
5343 struct dictionary *);
5345 static struct ctables_pcexpr
5346 ctpo_cat_nrange (double low, double high)
5348 return (struct ctables_pcexpr) {
5349 .op = CTPO_CAT_NRANGE,
5350 .nrange = { low, high },
5354 static struct ctables_pcexpr *
5355 ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
5357 int start_ofs = lex_ofs (lexer);
5358 struct ctables_pcexpr e;
5359 if (lex_is_number (lexer))
5361 e = (struct ctables_pcexpr) { .op = CTPO_CONSTANT,
5362 .number = lex_number (lexer) };
5365 else if (lex_match_id (lexer, "MISSING"))
5366 e = (struct ctables_pcexpr) { .op = CTPO_CAT_MISSING };
5367 else if (lex_match_id (lexer, "OTHERNM"))
5368 e = (struct ctables_pcexpr) { .op = CTPO_CAT_OTHERNM };
5369 else if (lex_match_id (lexer, "TOTAL"))
5370 e = (struct ctables_pcexpr) { .op = CTPO_CAT_TOTAL };
5371 else if (lex_match_id (lexer, "SUBTOTAL"))
5373 size_t subtotal_index = 0;
5374 if (lex_match (lexer, T_LBRACK))
5376 if (!lex_force_int_range (lexer, "SUBTOTAL", 1, LONG_MAX))
5378 subtotal_index = lex_integer (lexer);
5380 if (!lex_force_match (lexer, T_RBRACK))
5383 e = (struct ctables_pcexpr) { .op = CTPO_CAT_SUBTOTAL,
5384 .subtotal_index = subtotal_index };
5386 else if (lex_match (lexer, T_LBRACK))
5388 if (lex_match_id (lexer, "LO"))
5390 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
5392 e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
5395 else if (lex_is_number (lexer))
5397 double number = lex_number (lexer);
5399 if (lex_match_id (lexer, "THRU"))
5401 if (lex_match_id (lexer, "HI"))
5402 e = ctpo_cat_nrange (number, DBL_MAX);
5405 if (!lex_force_num (lexer))
5407 e = ctpo_cat_nrange (number, lex_number (lexer));
5412 e = (struct ctables_pcexpr) { .op = CTPO_CAT_NUMBER,
5415 else if (lex_is_string (lexer))
5417 struct substring s = recode_substring_pool (
5418 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
5419 ss_rtrim (&s, ss_cstr (" "));
5421 e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
5426 lex_error (lexer, NULL);
5430 if (!lex_force_match (lexer, T_RBRACK))
5432 if (e.op == CTPO_CAT_STRING)
5433 ss_dealloc (&e.string);
5437 else if (lex_match (lexer, T_LPAREN))
5439 struct ctables_pcexpr *ep = ctable_pcexpr_parse_add (lexer, dict);
5442 if (!lex_force_match (lexer, T_RPAREN))
5444 ctables_pcexpr_destroy (ep);
5451 lex_error (lexer, NULL);
5455 e.location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
5456 return xmemdup (&e, sizeof e);
5459 static struct ctables_pcexpr *
5460 ctables_pcexpr_allocate_neg (struct ctables_pcexpr *sub,
5461 struct lexer *lexer, int start_ofs)
5463 struct ctables_pcexpr *e = xmalloc (sizeof *e);
5464 *e = (struct ctables_pcexpr) {
5467 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
5472 static struct ctables_pcexpr *
5473 ctable_pcexpr_parse_exp (struct lexer *lexer, struct dictionary *dict)
5475 static const struct operator op = { T_EXP, CTPO_POW };
5477 const char *chain_warning =
5478 _("The exponentiation operator (`**') is left-associative: "
5479 "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
5480 "To disable this warning, insert parentheses.");
5482 if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
5483 return ctable_pcexpr_parse_binary_operators (lexer, dict, &op, 1,
5484 ctable_pcexpr_parse_primary,
5487 /* Special case for situations like "-5**6", which must be parsed as
5490 int start_ofs = lex_ofs (lexer);
5491 struct ctables_pcexpr *lhs = xmalloc (sizeof *lhs);
5492 *lhs = (struct ctables_pcexpr) {
5493 .op = CTPO_CONSTANT,
5494 .number = -lex_tokval (lexer),
5495 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer)),
5499 struct ctables_pcexpr *node = ctable_pcexpr_parse_binary_operators__ (
5500 lexer, dict, &op, 1,
5501 ctable_pcexpr_parse_primary, chain_warning, lhs);
5505 return ctables_pcexpr_allocate_neg (node, lexer, start_ofs);
5508 /* Parses the unary minus level. */
5509 static struct ctables_pcexpr *
5510 ctable_pcexpr_parse_neg (struct lexer *lexer, struct dictionary *dict)
5512 int start_ofs = lex_ofs (lexer);
5513 if (!lex_match (lexer, T_DASH))
5514 return ctable_pcexpr_parse_exp (lexer, dict);
5516 struct ctables_pcexpr *inner = ctable_pcexpr_parse_neg (lexer, dict);
5520 return ctables_pcexpr_allocate_neg (inner, lexer, start_ofs);
5523 /* Parses the multiplication and division level. */
5524 static struct ctables_pcexpr *
5525 ctable_pcexpr_parse_mul (struct lexer *lexer, struct dictionary *dict)
5527 static const struct operator ops[] =
5529 { T_ASTERISK, CTPO_MUL },
5530 { T_SLASH, CTPO_DIV },
5533 return ctable_pcexpr_parse_binary_operators (lexer, dict, ops,
5534 sizeof ops / sizeof *ops,
5535 ctable_pcexpr_parse_neg, NULL);
5538 /* Parses the addition and subtraction level. */
5539 static struct ctables_pcexpr *
5540 ctable_pcexpr_parse_add (struct lexer *lexer, struct dictionary *dict)
5542 static const struct operator ops[] =
5544 { T_PLUS, CTPO_ADD },
5545 { T_DASH, CTPO_SUB },
5546 { T_NEG_NUM, CTPO_ADD },
5549 return ctable_pcexpr_parse_binary_operators (lexer, dict,
5550 ops, sizeof ops / sizeof *ops,
5551 ctable_pcexpr_parse_mul, NULL);
5554 static struct ctables_postcompute *
5555 ctables_find_postcompute (struct ctables *ct, const char *name)
5557 struct ctables_postcompute *pc;
5558 HMAP_FOR_EACH_WITH_HASH (pc, struct ctables_postcompute, hmap_node,
5559 utf8_hash_case_string (name, 0), &ct->postcomputes)
5560 if (!utf8_strcasecmp (pc->name, name))
5566 ctables_parse_pcompute (struct lexer *lexer, struct dictionary *dict,
5569 int pcompute_start = lex_ofs (lexer) - 1;
5571 if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
5574 char *name = ss_xstrdup (lex_tokss (lexer));
5577 if (!lex_force_match (lexer, T_EQUALS)
5578 || !lex_force_match_id (lexer, "EXPR")
5579 || !lex_force_match (lexer, T_LPAREN))
5585 int expr_start = lex_ofs (lexer);
5586 struct ctables_pcexpr *expr = ctable_pcexpr_parse_add (lexer, dict);
5587 int expr_end = lex_ofs (lexer) - 1;
5588 if (!expr || !lex_force_match (lexer, T_RPAREN))
5593 int pcompute_end = lex_ofs (lexer) - 1;
5595 struct msg_location *location = lex_ofs_location (lexer, pcompute_start,
5598 struct ctables_postcompute *pc = ctables_find_postcompute (ct, name);
5601 msg_at (SW, location, _("New definition of &%s will override the "
5602 "previous definition."),
5604 msg_at (SN, pc->location, _("This is the previous definition."));
5606 ctables_pcexpr_destroy (pc->expr);
5607 msg_location_destroy (pc->location);
5612 pc = xmalloc (sizeof *pc);
5613 *pc = (struct ctables_postcompute) { .name = name };
5614 hmap_insert (&ct->postcomputes, &pc->hmap_node,
5615 utf8_hash_case_string (pc->name, 0));
5618 pc->location = location;
5620 pc->label = lex_ofs_representation (lexer, expr_start, expr_end);
5625 ctables_parse_pproperties_format (struct lexer *lexer,
5626 struct ctables_summary_spec_set *sss)
5628 *sss = (struct ctables_summary_spec_set) { .n = 0 };
5630 while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH
5631 && !(lex_token (lexer) == T_ID
5632 && (lex_id_match (ss_cstr ("LABEL"), lex_tokss (lexer))
5633 || lex_id_match (ss_cstr ("HIDESOURCECATS"),
5634 lex_tokss (lexer)))))
5636 /* Parse function. */
5637 enum ctables_summary_function function;
5638 if (!parse_ctables_summary_function (lexer, &function))
5641 /* Parse percentile. */
5642 double percentile = 0;
5643 if (function == CTSF_PTILE)
5645 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
5647 percentile = lex_number (lexer);
5652 struct fmt_spec format;
5653 bool is_ctables_format;
5654 if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
5657 if (sss->n >= sss->allocated)
5658 sss->specs = x2nrealloc (sss->specs, &sss->allocated,
5659 sizeof *sss->specs);
5660 sss->specs[sss->n++] = (struct ctables_summary_spec) {
5661 .function = function,
5662 .percentile = percentile,
5664 .is_ctables_format = is_ctables_format,
5670 ctables_summary_spec_set_uninit (sss);
5675 ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
5677 struct ctables_postcompute **pcs = NULL;
5679 size_t allocated_pcs = 0;
5681 while (lex_match (lexer, T_AND))
5683 if (!lex_force_id (lexer))
5685 struct ctables_postcompute *pc
5686 = ctables_find_postcompute (ct, lex_tokcstr (lexer));
5689 msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
5694 if (n_pcs >= allocated_pcs)
5695 pcs = x2nrealloc (pcs, &allocated_pcs, sizeof *pcs);
5699 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5701 if (lex_match_id (lexer, "LABEL"))
5703 lex_match (lexer, T_EQUALS);
5704 if (!lex_force_string (lexer))
5707 for (size_t i = 0; i < n_pcs; i++)
5709 free (pcs[i]->label);
5710 pcs[i]->label = ss_xstrdup (lex_tokss (lexer));
5715 else if (lex_match_id (lexer, "FORMAT"))
5717 lex_match (lexer, T_EQUALS);
5719 struct ctables_summary_spec_set sss;
5720 if (!ctables_parse_pproperties_format (lexer, &sss))
5723 for (size_t i = 0; i < n_pcs; i++)
5726 ctables_summary_spec_set_uninit (pcs[i]->specs);
5728 pcs[i]->specs = xmalloc (sizeof *pcs[i]->specs);
5729 ctables_summary_spec_set_clone (pcs[i]->specs, &sss);
5731 ctables_summary_spec_set_uninit (&sss);
5733 else if (lex_match_id (lexer, "HIDESOURCECATS"))
5735 lex_match (lexer, T_EQUALS);
5736 bool hide_source_cats;
5737 if (!parse_bool (lexer, &hide_source_cats))
5739 for (size_t i = 0; i < n_pcs; i++)
5740 pcs[i]->hide_source_cats = hide_source_cats;
5744 lex_error_expecting (lexer, "LABEL", "FORMAT", "HIDESOURCECATS");
5757 put_strftime (struct string *out, time_t now, const char *format)
5759 const struct tm *tm = localtime (&now);
5761 strftime (value, sizeof value, format, tm);
5762 ds_put_cstr (out, value);
5766 skip_prefix (struct substring *s, struct substring prefix)
5768 if (ss_starts_with (*s, prefix))
5770 ss_advance (s, prefix.length);
5778 put_table_expression (struct string *out, struct lexer *lexer,
5779 struct dictionary *dict, int expr_start, int expr_end)
5782 for (int ofs = expr_start; ofs < expr_end; ofs++)
5784 const struct token *t = lex_ofs_token (lexer, ofs);
5785 if (t->type == T_LBRACK)
5787 else if (t->type == T_RBRACK && nest > 0)
5793 else if (t->type == T_ID)
5795 const struct variable *var
5796 = dict_lookup_var (dict, t->string.string);
5797 const char *label = var ? var_get_label (var) : NULL;
5798 ds_put_cstr (out, label ? label : t->string.string);
5802 if (ofs != expr_start && t->type != T_RPAREN && ds_last (out) != ' ')
5803 ds_put_byte (out, ' ');
5805 char *repr = lex_ofs_representation (lexer, ofs, ofs);
5806 ds_put_cstr (out, repr);
5809 if (ofs + 1 != expr_end && t->type != T_LPAREN)
5810 ds_put_byte (out, ' ');
5816 put_title_text (struct string *out, struct substring in, time_t now,
5817 struct lexer *lexer, struct dictionary *dict,
5818 int expr_start, int expr_end)
5822 size_t chunk = ss_find_byte (in, ')');
5823 ds_put_substring (out, ss_head (in, chunk));
5824 ss_advance (&in, chunk);
5825 if (ss_is_empty (in))
5828 if (skip_prefix (&in, ss_cstr (")DATE")))
5829 put_strftime (out, now, "%x");
5830 else if (skip_prefix (&in, ss_cstr (")TIME")))
5831 put_strftime (out, now, "%X");
5832 else if (skip_prefix (&in, ss_cstr (")TABLE")))
5833 put_table_expression (out, lexer, dict, expr_start, expr_end);
5836 ds_put_byte (out, ')');
5837 ss_advance (&in, 1);
5843 cmd_ctables (struct lexer *lexer, struct dataset *ds)
5845 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
5846 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
5847 enum settings_value_show tvars = settings_get_show_variables ();
5848 for (size_t i = 0; i < n_vars; i++)
5849 vlabels[i] = (enum ctables_vlabel) tvars;
5851 struct pivot_table_look *look = pivot_table_look_unshare (
5852 pivot_table_look_ref (pivot_table_look_get_default ()));
5853 look->omit_empty = false;
5855 struct ctables *ct = xmalloc (sizeof *ct);
5856 *ct = (struct ctables) {
5857 .dict = dataset_dict (ds),
5859 .ctables_formats = FMT_SETTINGS_INIT,
5861 .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
5864 time_t now = time (NULL);
5869 const char *dot_string;
5870 const char *comma_string;
5872 static const struct ctf ctfs[4] = {
5873 { CTEF_NEGPAREN, "(,,,)", "(...)" },
5874 { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
5875 { CTEF_PAREN, "-,(,),", "-.(.)." },
5876 { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
5878 bool is_dot = settings_get_fmt_settings ()->decimal == '.';
5879 for (size_t i = 0; i < 4; i++)
5881 const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
5882 fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
5883 fmt_number_style_from_string (s));
5886 if (!lex_force_match (lexer, T_SLASH))
5889 while (!lex_match_id (lexer, "TABLE"))
5891 if (lex_match_id (lexer, "FORMAT"))
5893 double widths[2] = { SYSMIS, SYSMIS };
5894 double units_per_inch = 72.0;
5896 while (lex_token (lexer) != T_SLASH)
5898 if (lex_match_id (lexer, "MINCOLWIDTH"))
5900 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
5903 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
5905 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
5908 else if (lex_match_id (lexer, "UNITS"))
5910 lex_match (lexer, T_EQUALS);
5911 if (lex_match_id (lexer, "POINTS"))
5912 units_per_inch = 72.0;
5913 else if (lex_match_id (lexer, "INCHES"))
5914 units_per_inch = 1.0;
5915 else if (lex_match_id (lexer, "CM"))
5916 units_per_inch = 2.54;
5919 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
5923 else if (lex_match_id (lexer, "EMPTY"))
5928 lex_match (lexer, T_EQUALS);
5929 if (lex_match_id (lexer, "ZERO"))
5931 /* Nothing to do. */
5933 else if (lex_match_id (lexer, "BLANK"))
5934 ct->zero = xstrdup ("");
5935 else if (lex_force_string (lexer))
5937 ct->zero = ss_xstrdup (lex_tokss (lexer));
5943 else if (lex_match_id (lexer, "MISSING"))
5945 lex_match (lexer, T_EQUALS);
5946 if (!lex_force_string (lexer))
5950 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
5951 ? ss_xstrdup (lex_tokss (lexer))
5957 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
5958 "UNITS", "EMPTY", "MISSING");
5963 if (widths[0] != SYSMIS && widths[1] != SYSMIS
5964 && widths[0] > widths[1])
5966 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
5970 for (size_t i = 0; i < 2; i++)
5971 if (widths[i] != SYSMIS)
5973 int *wr = ct->look->width_ranges[TABLE_HORZ];
5974 wr[i] = widths[i] / units_per_inch * 96.0;
5979 else if (lex_match_id (lexer, "VLABELS"))
5981 if (!lex_force_match_id (lexer, "VARIABLES"))
5983 lex_match (lexer, T_EQUALS);
5985 struct variable **vars;
5987 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
5991 if (!lex_force_match_id (lexer, "DISPLAY"))
5996 lex_match (lexer, T_EQUALS);
5998 enum ctables_vlabel vlabel;
5999 if (lex_match_id (lexer, "DEFAULT"))
6000 vlabel = (enum ctables_vlabel) settings_get_show_variables ();
6001 else if (lex_match_id (lexer, "NAME"))
6003 else if (lex_match_id (lexer, "LABEL"))
6004 vlabel = CTVL_LABEL;
6005 else if (lex_match_id (lexer, "BOTH"))
6007 else if (lex_match_id (lexer, "NONE"))
6011 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
6017 for (size_t i = 0; i < n_vars; i++)
6018 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
6021 else if (lex_match_id (lexer, "MRSETS"))
6023 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
6025 lex_match (lexer, T_EQUALS);
6026 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
6029 else if (lex_match_id (lexer, "SMISSING"))
6031 if (lex_match_id (lexer, "VARIABLE"))
6032 ct->smissing_listwise = false;
6033 else if (lex_match_id (lexer, "LISTWISE"))
6034 ct->smissing_listwise = true;
6037 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
6041 else if (lex_match_id (lexer, "PCOMPUTE"))
6043 if (!ctables_parse_pcompute (lexer, dataset_dict (ds), ct))
6046 else if (lex_match_id (lexer, "PPROPERTIES"))
6048 if (!ctables_parse_pproperties (lexer, ct))
6051 else if (lex_match_id (lexer, "WEIGHT"))
6053 if (!lex_force_match_id (lexer, "VARIABLE"))
6055 lex_match (lexer, T_EQUALS);
6056 ct->e_weight = parse_variable (lexer, dataset_dict (ds));
6060 else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
6062 if (lex_match_id (lexer, "COUNT"))
6064 lex_match (lexer, T_EQUALS);
6065 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT",
6068 ct->hide_threshold = lex_integer (lexer);
6071 else if (ct->hide_threshold == 0)
6072 ct->hide_threshold = 5;
6076 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
6077 "SMISSING", "PCOMPUTE", "PPROPERTIES",
6078 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
6082 if (!lex_force_match (lexer, T_SLASH))
6086 size_t allocated_tables = 0;
6089 if (ct->n_tables >= allocated_tables)
6090 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
6091 sizeof *ct->tables);
6093 struct ctables_category *cat = xmalloc (sizeof *cat);
6094 *cat = (struct ctables_category) {
6096 .include_missing = false,
6097 .sort_ascending = true,
6100 struct ctables_categories *c = xmalloc (sizeof *c);
6101 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
6102 *c = (struct ctables_categories) {
6109 struct ctables_categories **categories = xnmalloc (n_vars,
6110 sizeof *categories);
6111 for (size_t i = 0; i < n_vars; i++)
6114 struct ctables_table *t = xmalloc (sizeof *t);
6115 *t = (struct ctables_table) {
6117 .slabels_axis = PIVOT_AXIS_COLUMN,
6118 .slabels_visible = true,
6119 .clabels_values_map = HMAP_INITIALIZER (t->clabels_values_map),
6121 [PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW,
6122 [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
6123 [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
6125 .clabels_from_axis = PIVOT_AXIS_LAYER,
6126 .categories = categories,
6127 .n_categories = n_vars,
6130 ct->tables[ct->n_tables++] = t;
6132 lex_match (lexer, T_EQUALS);
6133 int expr_start = lex_ofs (lexer);
6134 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
6136 if (lex_match (lexer, T_BY))
6138 if (!ctables_axis_parse (lexer, dataset_dict (ds),
6139 ct, t, PIVOT_AXIS_COLUMN))
6142 if (lex_match (lexer, T_BY))
6144 if (!ctables_axis_parse (lexer, dataset_dict (ds),
6145 ct, t, PIVOT_AXIS_LAYER))
6149 int expr_end = lex_ofs (lexer);
6151 if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
6152 && !t->axes[PIVOT_AXIS_LAYER])
6154 lex_error (lexer, _("At least one variable must be specified."));
6158 const struct ctables_axis *scales[PIVOT_N_AXES];
6159 size_t n_scales = 0;
6160 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6162 scales[a] = find_scale (t->axes[a]);
6168 msg (SE, _("Scale variables may appear only on one axis."));
6169 if (scales[PIVOT_AXIS_ROW])
6170 msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
6171 _("This scale variable appears on the rows axis."));
6172 if (scales[PIVOT_AXIS_COLUMN])
6173 msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
6174 _("This scale variable appears on the columns axis."));
6175 if (scales[PIVOT_AXIS_LAYER])
6176 msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
6177 _("This scale variable appears on the layer axis."));
6181 const struct ctables_axis *summaries[PIVOT_N_AXES];
6182 size_t n_summaries = 0;
6183 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6185 summaries[a] = (scales[a]
6187 : find_categorical_summary_spec (t->axes[a]));
6191 if (n_summaries > 1)
6193 msg (SE, _("Summaries may appear only on one axis."));
6194 if (summaries[PIVOT_AXIS_ROW])
6195 msg_at (SN, summaries[PIVOT_AXIS_ROW]->loc,
6196 _("This variable on the rows axis has a summary."));
6197 if (summaries[PIVOT_AXIS_COLUMN])
6198 msg_at (SN, summaries[PIVOT_AXIS_COLUMN]->loc,
6199 _("This variable on the columns axis has a summary."));
6200 if (summaries[PIVOT_AXIS_LAYER])
6201 msg_at (SN, summaries[PIVOT_AXIS_LAYER]->loc,
6202 _("This variable on the layers axis has a summary."));
6205 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6206 if (n_summaries ? summaries[a] : t->axes[a])
6208 t->summary_axis = a;
6212 if (lex_token (lexer) == T_ENDCMD)
6214 if (!ctables_prepare_table (t))
6218 if (!lex_force_match (lexer, T_SLASH))
6221 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
6223 if (lex_match_id (lexer, "SLABELS"))
6225 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
6227 if (lex_match_id (lexer, "POSITION"))
6229 lex_match (lexer, T_EQUALS);
6230 if (lex_match_id (lexer, "COLUMN"))
6231 t->slabels_axis = PIVOT_AXIS_COLUMN;
6232 else if (lex_match_id (lexer, "ROW"))
6233 t->slabels_axis = PIVOT_AXIS_ROW;
6234 else if (lex_match_id (lexer, "LAYER"))
6235 t->slabels_axis = PIVOT_AXIS_LAYER;
6238 lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
6242 else if (lex_match_id (lexer, "VISIBLE"))
6244 lex_match (lexer, T_EQUALS);
6245 if (!parse_bool (lexer, &t->slabels_visible))
6250 lex_error_expecting (lexer, "POSITION", "VISIBLE");
6255 else if (lex_match_id (lexer, "CLABELS"))
6257 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
6259 if (lex_match_id (lexer, "AUTO"))
6261 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
6262 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
6264 else if (lex_match_id (lexer, "ROWLABELS"))
6266 lex_match (lexer, T_EQUALS);
6267 if (lex_match_id (lexer, "OPPOSITE"))
6268 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
6269 else if (lex_match_id (lexer, "LAYER"))
6270 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
6273 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
6277 else if (lex_match_id (lexer, "COLLABELS"))
6279 lex_match (lexer, T_EQUALS);
6280 if (lex_match_id (lexer, "OPPOSITE"))
6281 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
6282 else if (lex_match_id (lexer, "LAYER"))
6283 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
6286 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
6292 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
6298 else if (lex_match_id (lexer, "CRITERIA"))
6300 if (!lex_force_match_id (lexer, "CILEVEL"))
6302 lex_match (lexer, T_EQUALS);
6304 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
6306 t->cilevel = lex_number (lexer);
6309 else if (lex_match_id (lexer, "CATEGORIES"))
6311 if (!ctables_table_parse_categories (lexer, dataset_dict (ds),
6315 else if (lex_match_id (lexer, "TITLES"))
6320 if (lex_match_id (lexer, "CAPTION"))
6321 textp = &t->caption;
6322 else if (lex_match_id (lexer, "CORNER"))
6324 else if (lex_match_id (lexer, "TITLE"))
6328 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
6331 lex_match (lexer, T_EQUALS);
6333 struct string s = DS_EMPTY_INITIALIZER;
6334 while (lex_is_string (lexer))
6336 if (!ds_is_empty (&s))
6337 ds_put_byte (&s, ' ');
6338 put_title_text (&s, lex_tokss (lexer), now,
6339 lexer, dataset_dict (ds),
6340 expr_start, expr_end);
6344 *textp = ds_steal_cstr (&s);
6346 while (lex_token (lexer) != T_SLASH
6347 && lex_token (lexer) != T_ENDCMD);
6349 else if (lex_match_id (lexer, "SIGTEST"))
6353 t->chisq = xmalloc (sizeof *t->chisq);
6354 *t->chisq = (struct ctables_chisq) {
6356 .include_mrsets = true,
6357 .all_visible = true,
6363 if (lex_match_id (lexer, "TYPE"))
6365 lex_match (lexer, T_EQUALS);
6366 if (!lex_force_match_id (lexer, "CHISQUARE"))
6369 else if (lex_match_id (lexer, "ALPHA"))
6371 lex_match (lexer, T_EQUALS);
6372 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
6374 t->chisq->alpha = lex_number (lexer);
6377 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
6379 lex_match (lexer, T_EQUALS);
6380 if (parse_bool (lexer, &t->chisq->include_mrsets))
6383 else if (lex_match_id (lexer, "CATEGORIES"))
6385 lex_match (lexer, T_EQUALS);
6386 if (lex_match_id (lexer, "ALLVISIBLE"))
6387 t->chisq->all_visible = true;
6388 else if (lex_match_id (lexer, "SUBTOTALS"))
6389 t->chisq->all_visible = false;
6392 lex_error_expecting (lexer,
6393 "ALLVISIBLE", "SUBTOTALS");
6399 lex_error_expecting (lexer, "TYPE", "ALPHA",
6400 "INCLUDEMRSETS", "CATEGORIES");
6404 while (lex_token (lexer) != T_SLASH
6405 && lex_token (lexer) != T_ENDCMD);
6407 else if (lex_match_id (lexer, "COMPARETEST"))
6411 t->pairwise = xmalloc (sizeof *t->pairwise);
6412 *t->pairwise = (struct ctables_pairwise) {
6414 .alpha = { .05, .05 },
6415 .adjust = BONFERRONI,
6416 .include_mrsets = true,
6417 .meansvariance_allcats = true,
6418 .all_visible = true,
6427 if (lex_match_id (lexer, "TYPE"))
6429 lex_match (lexer, T_EQUALS);
6430 if (lex_match_id (lexer, "PROP"))
6431 t->pairwise->type = PROP;
6432 else if (lex_match_id (lexer, "MEAN"))
6433 t->pairwise->type = MEAN;
6436 lex_error_expecting (lexer, "PROP", "MEAN");
6440 else if (lex_match_id (lexer, "ALPHA"))
6442 lex_match (lexer, T_EQUALS);
6444 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
6446 double a0 = lex_number (lexer);
6449 lex_match (lexer, T_COMMA);
6450 if (lex_is_number (lexer))
6452 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
6454 double a1 = lex_number (lexer);
6457 t->pairwise->alpha[0] = MIN (a0, a1);
6458 t->pairwise->alpha[1] = MAX (a0, a1);
6461 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
6463 else if (lex_match_id (lexer, "ADJUST"))
6465 lex_match (lexer, T_EQUALS);
6466 if (lex_match_id (lexer, "BONFERRONI"))
6467 t->pairwise->adjust = BONFERRONI;
6468 else if (lex_match_id (lexer, "BH"))
6469 t->pairwise->adjust = BH;
6470 else if (lex_match_id (lexer, "NONE"))
6471 t->pairwise->adjust = 0;
6474 lex_error_expecting (lexer, "BONFERRONI", "BH",
6479 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
6481 lex_match (lexer, T_EQUALS);
6482 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
6485 else if (lex_match_id (lexer, "MEANSVARIANCE"))
6487 lex_match (lexer, T_EQUALS);
6488 if (lex_match_id (lexer, "ALLCATS"))
6489 t->pairwise->meansvariance_allcats = true;
6490 else if (lex_match_id (lexer, "TESTEDCATS"))
6491 t->pairwise->meansvariance_allcats = false;
6494 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
6498 else if (lex_match_id (lexer, "CATEGORIES"))
6500 lex_match (lexer, T_EQUALS);
6501 if (lex_match_id (lexer, "ALLVISIBLE"))
6502 t->pairwise->all_visible = true;
6503 else if (lex_match_id (lexer, "SUBTOTALS"))
6504 t->pairwise->all_visible = false;
6507 lex_error_expecting (lexer, "ALLVISIBLE",
6512 else if (lex_match_id (lexer, "MERGE"))
6514 lex_match (lexer, T_EQUALS);
6515 if (!parse_bool (lexer, &t->pairwise->merge))
6518 else if (lex_match_id (lexer, "STYLE"))
6520 lex_match (lexer, T_EQUALS);
6521 if (lex_match_id (lexer, "APA"))
6522 t->pairwise->apa_style = true;
6523 else if (lex_match_id (lexer, "SIMPLE"))
6524 t->pairwise->apa_style = false;
6527 lex_error_expecting (lexer, "APA", "SIMPLE");
6531 else if (lex_match_id (lexer, "SHOWSIG"))
6533 lex_match (lexer, T_EQUALS);
6534 if (!parse_bool (lexer, &t->pairwise->show_sig))
6539 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
6540 "INCLUDEMRSETS", "MEANSVARIANCE",
6541 "CATEGORIES", "MERGE", "STYLE",
6546 while (lex_token (lexer) != T_SLASH
6547 && lex_token (lexer) != T_ENDCMD);
6551 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
6552 "CRITERIA", "CATEGORIES", "TITLES",
6553 "SIGTEST", "COMPARETEST");
6557 if (!lex_match (lexer, T_SLASH))
6561 if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
6562 && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
6564 msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
6568 if (!ctables_prepare_table (t))
6571 while (lex_token (lexer) != T_ENDCMD);
6573 bool ok = ctables_execute (ds, ct);
6574 ctables_destroy (ct);
6575 return ok ? CMD_SUCCESS : CMD_FAILURE;
6578 ctables_destroy (ct);