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 for (enum pivot_axis_type pc_a = 0; ; pc_a++)
4218 assert (pc_a < PIVOT_N_AXES);
4219 for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
4221 const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
4222 if (cv->category->type == CCT_POSTCOMPUTE)
4227 *pc_a_idx_p = pc_a_idx;
4228 return cv->category->pc;
4237 ctables_cell_calculate_postcompute (const struct ctables_section *s,
4238 const struct ctables_cell *cell,
4239 const struct ctables_summary_spec *ss,
4240 struct fmt_spec *format,
4241 bool *is_ctables_format,
4244 enum pivot_axis_type pc_a;
4246 const struct ctables_postcompute *pc = ctables_cell_postcompute (
4247 s, cell, &pc_a, &pc_a_idx);
4251 for (size_t i = 0; i < pc->specs->n; i++)
4253 const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
4254 if (ss->function == ss2->function
4255 && ss->percentile == ss2->percentile)
4257 *format = ss2->format;
4258 *is_ctables_format = ss2->is_ctables_format;
4264 const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
4265 const struct ctables_categories *cats = s->table->categories[
4266 var_get_dict_index (var)];
4267 struct ctables_pcexpr_evaluate_ctx ctx = {
4272 .pc_a_idx = pc_a_idx,
4273 .summary_idx = summary_idx,
4275 return ctables_pcexpr_evaluate (&ctx, pc->expr);
4279 ctables_table_output (struct ctables *ct, struct ctables_table *t)
4281 struct pivot_table *pt = pivot_table_create__ (
4283 ? pivot_value_new_user_text (t->title, SIZE_MAX)
4284 : pivot_value_new_text (N_("Custom Tables"))),
4287 pivot_table_set_caption (
4288 pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
4290 pivot_table_set_corner_text (
4291 pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
4293 bool summary_dimension = (t->summary_axis != t->slabels_axis
4294 || (!t->slabels_visible
4295 && t->summary_specs.n > 1));
4296 if (summary_dimension)
4298 struct pivot_dimension *d = pivot_dimension_create (
4299 pt, t->slabels_axis, N_("Statistics"));
4300 const struct ctables_summary_spec_set *specs = &t->summary_specs;
4301 if (!t->slabels_visible)
4302 d->hide_all_labels = true;
4303 for (size_t i = 0; i < specs->n; i++)
4304 pivot_category_create_leaf (
4305 d->root, ctables_summary_label (&specs->specs[i], t->cilevel));
4308 bool categories_dimension = t->clabels_example != NULL;
4309 if (categories_dimension)
4311 struct pivot_dimension *d = pivot_dimension_create (
4312 pt, t->label_axis[t->clabels_from_axis],
4313 t->clabels_from_axis == PIVOT_AXIS_ROW
4314 ? N_("Row Categories")
4315 : N_("Column Categories"));
4316 const struct variable *var = t->clabels_example;
4317 const struct ctables_categories *c = t->categories[var_get_dict_index (var)];
4318 for (size_t i = 0; i < t->n_clabels_values; i++)
4320 const struct ctables_value *value = t->clabels_values[i];
4321 const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
4322 assert (cat != NULL);
4323 pivot_category_create_leaf (d->root, ctables_category_create_label (
4324 c, cat, t->clabels_example,
4329 pivot_table_set_look (pt, ct->look);
4330 struct pivot_dimension *d[PIVOT_N_AXES];
4331 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4333 static const char *names[] = {
4334 [PIVOT_AXIS_ROW] = N_("Rows"),
4335 [PIVOT_AXIS_COLUMN] = N_("Columns"),
4336 [PIVOT_AXIS_LAYER] = N_("Layers"),
4338 d[a] = (t->axes[a] || a == t->summary_axis
4339 ? pivot_dimension_create (pt, a, names[a])
4344 assert (t->axes[a]);
4346 for (size_t i = 0; i < t->stacks[a].n; i++)
4348 struct ctables_nest *nest = &t->stacks[a].nests[i];
4349 struct ctables_section **sections = xnmalloc (t->n_sections,
4351 size_t n_sections = 0;
4353 size_t n_total_cells = 0;
4354 size_t max_depth = 0;
4355 for (size_t j = 0; j < t->n_sections; j++)
4356 if (t->sections[j].nests[a] == nest)
4358 struct ctables_section *s = &t->sections[j];
4359 sections[n_sections++] = s;
4360 n_total_cells += s->cells.count;
4362 size_t depth = s->nests[a]->n;
4363 max_depth = MAX (depth, max_depth);
4366 struct ctables_cell **sorted = xnmalloc (n_total_cells,
4368 size_t n_sorted = 0;
4370 for (size_t j = 0; j < n_sections; j++)
4372 struct ctables_section *s = sections[j];
4374 struct ctables_cell *cell;
4375 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4377 sorted[n_sorted++] = cell;
4378 assert (n_sorted <= n_total_cells);
4381 struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
4382 sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
4385 for (size_t j = 0; j < n_sorted; j++)
4387 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);
4392 struct ctables_level
4394 enum ctables_level_type
4396 CTL_VAR, /* Variable label for nest->vars[var_idx]. */
4397 CTL_CATEGORY, /* Category for nest->vars[var_idx]. */
4398 CTL_SUMMARY, /* Summary functions. */
4402 enum settings_value_show vlabel; /* CTL_VAR only. */
4405 struct ctables_level *levels = xnmalloc (1 + 2 * max_depth, sizeof *levels);
4406 size_t n_levels = 0;
4407 for (size_t k = 0; k < nest->n; k++)
4409 enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k])];
4410 if (vlabel != CTVL_NONE)
4412 levels[n_levels++] = (struct ctables_level) {
4414 .vlabel = (enum settings_value_show) vlabel,
4419 if (nest->scale_idx != k
4420 && (k != nest->n - 1 || t->label_axis[a] == a))
4422 levels[n_levels++] = (struct ctables_level) {
4423 .type = CTL_CATEGORY,
4429 if (!summary_dimension && a == t->slabels_axis)
4431 levels[n_levels++] = (struct ctables_level) {
4432 .type = CTL_SUMMARY,
4433 .var_idx = SIZE_MAX,
4437 /* Pivot categories:
4439 - variable label for nest->vars[0], if vlabel != CTVL_NONE
4440 - category for nest->vars[0], if nest->scale_idx != 0
4441 - variable label for nest->vars[1], if vlabel != CTVL_NONE
4442 - category for nest->vars[1], if nest->scale_idx != 1
4444 - variable label for nest->vars[n - 1], if vlabel != CTVL_NONE
4445 - category for nest->vars[n - 1], if t->label_axis[a] == a && nest->scale_idx != n - 1.
4446 - summary function, if 'a == t->slabels_axis && a ==
4449 Additional dimensions:
4451 - If 'a == t->slabels_axis && a != t->summary_axis', add a summary
4453 - If 't->label_axis[b] == a' for some 'b != a', add a category
4458 struct pivot_category **groups = xnmalloc (1 + 2 * max_depth, sizeof *groups);
4460 for (size_t j = 0; j < n_sorted; j++)
4462 struct ctables_cell *cell = sorted[j];
4463 struct ctables_cell *prev = j > 0 ? sorted[j - 1] : NULL;
4465 size_t n_common = 0;
4468 for (; n_common < n_levels; n_common++)
4470 const struct ctables_level *level = &levels[n_common];
4471 if (level->type == CTL_CATEGORY)
4473 size_t var_idx = level->var_idx;
4474 const struct ctables_category *c = cell->axes[a].cvs[var_idx].category;
4475 if (prev->axes[a].cvs[var_idx].category != c)
4477 else if (c->type != CCT_SUBTOTAL
4478 && c->type != CCT_TOTAL
4479 && c->type != CCT_POSTCOMPUTE
4480 && !value_equal (&prev->axes[a].cvs[var_idx].value,
4481 &cell->axes[a].cvs[var_idx].value,
4482 var_get_type (nest->vars[var_idx])))
4488 for (size_t k = n_common; k < n_levels; k++)
4490 const struct ctables_level *level = &levels[k];
4491 struct pivot_category *parent = k ? groups[k - 1] : d[a]->root;
4492 if (level->type == CTL_SUMMARY)
4494 assert (k == n_levels - 1);
4496 const struct ctables_summary_spec_set *specs = &t->summary_specs;
4497 for (size_t m = 0; m < specs->n; m++)
4499 int leaf = pivot_category_create_leaf (
4500 parent, ctables_summary_label (&specs->specs[m],
4508 const struct variable *var = nest->vars[level->var_idx];
4509 struct pivot_value *label;
4510 if (level->type == CTL_VAR)
4512 label = pivot_value_new_variable (var);
4513 label->variable.show = level->vlabel;
4515 else if (level->type == CTL_CATEGORY)
4517 const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
4518 label = ctables_category_create_label (
4519 t->categories[var_get_dict_index (var)],
4520 cv->category, var, &cv->value);
4525 if (k == n_levels - 1)
4526 prev_leaf = pivot_category_create_leaf (parent, label);
4528 groups[k] = pivot_category_create_group__ (parent, label);
4532 cell->axes[a].leaf = prev_leaf;
4539 for (size_t i = 0; i < t->n_sections; i++)
4541 struct ctables_section *s = &t->sections[i];
4543 struct ctables_cell *cell;
4544 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4549 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
4550 const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
4551 for (size_t j = 0; j < specs->n; j++)
4554 size_t n_dindexes = 0;
4556 if (summary_dimension)
4557 dindexes[n_dindexes++] = specs->specs[j].axis_idx;
4559 if (categories_dimension)
4561 const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
4562 const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
4563 const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
4564 const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
4567 dindexes[n_dindexes++] = ctv->leaf;
4570 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4573 int leaf = cell->axes[a].leaf;
4574 if (a == t->summary_axis && !summary_dimension)
4576 dindexes[n_dindexes++] = leaf;
4579 const struct ctables_summary_spec *ss = &specs->specs[j];
4581 struct fmt_spec format = specs->specs[j].format;
4582 bool is_ctables_format = ss->is_ctables_format;
4583 double d = (cell->postcompute
4584 ? ctables_cell_calculate_postcompute (
4585 s, cell, ss, &format, &is_ctables_format, j)
4586 : ctables_summary_value (cell, &cell->summaries[j],
4589 struct pivot_value *value;
4590 if (ct->hide_threshold != 0
4591 && d < ct->hide_threshold
4592 && ctables_summary_function_is_count (ss->function))
4594 value = pivot_value_new_user_text_nocopy (
4595 xasprintf ("<%d", ct->hide_threshold));
4597 else if (d == 0 && ct->zero)
4598 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
4599 else if (d == SYSMIS && ct->missing)
4600 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
4601 else if (is_ctables_format)
4603 char *s = data_out_stretchy (&(union value) { .f = d },
4605 &ct->ctables_formats, NULL);
4606 value = pivot_value_new_user_text_nocopy (s);
4610 value = pivot_value_new_number (d);
4611 value->numeric.format = format;
4613 pivot_table_put (pt, dindexes, n_dindexes, value);
4618 pivot_table_submit (pt);
4622 ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
4624 enum pivot_axis_type label_pos = t->label_axis[a];
4628 t->clabels_from_axis = a;
4630 const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
4631 const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
4633 const struct ctables_stack *stack = &t->stacks[a];
4637 const struct ctables_nest *n0 = &stack->nests[0];
4639 const struct variable *v0 = n0->vars[n0->n - 1];
4640 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4641 t->clabels_example = v0;
4643 for (size_t i = 0; i < c0->n_cats; i++)
4644 if (c0->cats[i].type == CCT_FUNCTION)
4646 msg (SE, _("%s=%s is not allowed with sorting based "
4647 "on a summary function."),
4648 subcommand_name, pos_name);
4651 if (n0->n - 1 == n0->scale_idx)
4653 msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
4654 "but %s is a scale variable."),
4655 subcommand_name, pos_name, var_get_name (v0));
4659 for (size_t i = 1; i < stack->n; i++)
4661 const struct ctables_nest *ni = &stack->nests[i];
4663 const struct variable *vi = ni->vars[ni->n - 1];
4664 struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
4666 if (ni->n - 1 == ni->scale_idx)
4668 msg (SE, _("%s=%s requires the variables to be moved to be "
4669 "categorical, but %s is a scale variable."),
4670 subcommand_name, pos_name, var_get_name (vi));
4673 if (var_get_width (v0) != var_get_width (vi))
4675 msg (SE, _("%s=%s requires the variables to be "
4676 "moved to have the same width, but %s has "
4677 "width %d and %s has width %d."),
4678 subcommand_name, pos_name,
4679 var_get_name (v0), var_get_width (v0),
4680 var_get_name (vi), var_get_width (vi));
4683 if (!val_labs_equal (var_get_value_labels (v0),
4684 var_get_value_labels (vi)))
4686 msg (SE, _("%s=%s requires the variables to be "
4687 "moved to have the same value labels, but %s "
4688 "and %s have different value labels."),
4689 subcommand_name, pos_name,
4690 var_get_name (v0), var_get_name (vi));
4693 if (!ctables_categories_equal (c0, ci))
4695 msg (SE, _("%s=%s requires the variables to be "
4696 "moved to have the same category "
4697 "specifications, but %s and %s have different "
4698 "category specifications."),
4699 subcommand_name, pos_name,
4700 var_get_name (v0), var_get_name (vi));
4709 add_sum_var (struct variable *var,
4710 struct variable ***sum_vars, size_t *n, size_t *allocated)
4712 for (size_t i = 0; i < *n; i++)
4713 if (var == (*sum_vars)[i])
4716 if (*n >= *allocated)
4717 *sum_vars = x2nrealloc (*sum_vars, allocated, sizeof **sum_vars);
4718 (*sum_vars)[*n] = var;
4723 enumerate_sum_vars (const struct ctables_axis *a,
4724 struct variable ***sum_vars, size_t *n, size_t *allocated)
4732 for (size_t i = 0; i < N_CSVS; i++)
4733 for (size_t j = 0; j < a->specs[i].n; j++)
4735 struct ctables_summary_spec *spec = &a->specs[i].specs[j];
4736 if (ctables_function_is_pctsum (spec->function))
4737 spec->sum_var_idx = add_sum_var (a->var, sum_vars, n, allocated);
4743 for (size_t i = 0; i < 2; i++)
4744 enumerate_sum_vars (a->subs[i], sum_vars, n, allocated);
4750 ctables_prepare_table (struct ctables_table *t)
4752 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4755 t->stacks[a] = enumerate_fts (a, t->axes[a]);
4757 for (size_t j = 0; j < t->stacks[a].n; j++)
4759 struct ctables_nest *nest = &t->stacks[a].nests[j];
4760 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
4762 nest->domains[dt] = xmalloc (nest->n * sizeof *nest->domains[dt]);
4763 nest->n_domains[dt] = 0;
4765 for (size_t k = 0; k < nest->n; k++)
4767 if (k == nest->scale_idx)
4776 if (a != PIVOT_AXIS_LAYER)
4783 if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
4784 : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
4785 : a == PIVOT_AXIS_ROW)
4787 if (k == nest->n - 1
4788 || (nest->scale_idx == nest->n - 1
4789 && k == nest->n - 2))
4795 if (a == PIVOT_AXIS_COLUMN)
4800 if (a == PIVOT_AXIS_ROW)
4805 nest->domains[dt][nest->n_domains[dt]++] = k;
4812 struct ctables_nest *nest = xmalloc (sizeof *nest);
4813 *nest = (struct ctables_nest) { .n = 0 };
4814 t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
4817 struct ctables_stack *stack = &t->stacks[t->summary_axis];
4818 for (size_t i = 0; i < stack->n; i++)
4820 struct ctables_nest *nest = &stack->nests[i];
4821 if (!nest->specs[CSV_CELL].n)
4823 struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
4824 specs->specs = xmalloc (sizeof *specs->specs);
4827 enum ctables_summary_function function
4828 = specs->is_scale ? CTSF_MEAN : CTSF_COUNT;
4830 *specs->specs = (struct ctables_summary_spec) {
4831 .function = function,
4832 .format = ctables_summary_default_format (function, specs->var),
4835 specs->var = nest->vars[0];
4837 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4838 &nest->specs[CSV_CELL]);
4840 else if (!nest->specs[CSV_TOTAL].n)
4841 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4842 &nest->specs[CSV_CELL]);
4844 if (t->ctables->smissing_listwise)
4846 struct variable **listwise_vars = NULL;
4848 size_t allocated = 0;
4850 for (size_t j = nest->group_head; j < stack->n; j++)
4852 const struct ctables_nest *other_nest = &stack->nests[j];
4853 if (other_nest->group_head != nest->group_head)
4856 if (nest != other_nest && other_nest->scale_idx < other_nest->n)
4859 listwise_vars = x2nrealloc (listwise_vars, &allocated,
4860 sizeof *listwise_vars);
4861 listwise_vars[n++] = other_nest->vars[other_nest->scale_idx];
4864 for (size_t j = 0; j < N_CSVS; j++)
4866 nest->specs[j].listwise_vars = listwise_vars;
4867 nest->specs[j].n_listwise_vars = n;
4872 struct ctables_summary_spec_set *merged = &t->summary_specs;
4873 struct merge_item *items = xnmalloc (N_CSVS * stack->n, sizeof *items);
4875 for (size_t j = 0; j < stack->n; j++)
4877 const struct ctables_nest *nest = &stack->nests[j];
4879 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4880 items[n_left++] = (struct merge_item) { .set = &nest->specs[sv] };
4885 struct merge_item min = items[0];
4886 for (size_t j = 1; j < n_left; j++)
4887 if (merge_item_compare_3way (&items[j], &min) < 0)
4890 if (merged->n >= merged->allocated)
4891 merged->specs = x2nrealloc (merged->specs, &merged->allocated,
4892 sizeof *merged->specs);
4893 merged->specs[merged->n++] = min.set->specs[min.ofs];
4895 for (size_t j = 0; j < n_left; )
4897 if (merge_item_compare_3way (&items[j], &min) == 0)
4899 struct merge_item *item = &items[j];
4900 item->set->specs[item->ofs].axis_idx = merged->n - 1;
4901 if (++item->ofs >= item->set->n)
4903 items[j] = items[--n_left];
4912 for (size_t j = 0; j < merged->n; j++)
4913 printf ("%s\n", ctables_summary_function_name (merged->specs[j].function));
4915 for (size_t j = 0; j < stack->n; j++)
4917 const struct ctables_nest *nest = &stack->nests[j];
4918 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4920 const struct ctables_summary_spec_set *specs = &nest->specs[sv];
4921 for (size_t k = 0; k < specs->n; k++)
4922 printf ("(%s, %zu) ", ctables_summary_function_name (specs->specs[k].function),
4923 specs->specs[k].axis_idx);
4929 size_t allocated_sum_vars = 0;
4930 enumerate_sum_vars (t->axes[t->summary_axis],
4931 &t->sum_vars, &t->n_sum_vars, &allocated_sum_vars);
4933 return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
4934 && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
4938 ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
4939 enum pivot_axis_type a)
4941 struct ctables_stack *stack = &t->stacks[a];
4942 for (size_t i = 0; i < stack->n; i++)
4944 const struct ctables_nest *nest = &stack->nests[i];
4945 const struct variable *var = nest->vars[nest->n - 1];
4946 const union value *value = case_data (c, var);
4948 if (var_is_numeric (var) && value->f == SYSMIS)
4951 if (ctables_categories_match (t->categories [var_get_dict_index (var)],
4953 ctables_value_insert (t, value, var_get_width (var));
4958 compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
4960 const struct ctables_value *const *ap = a_;
4961 const struct ctables_value *const *bp = b_;
4962 const struct ctables_value *a = *ap;
4963 const struct ctables_value *b = *bp;
4964 const int *width = width_;
4965 return value_compare_3way (&a->value, &b->value, *width);
4969 ctables_sort_clabels_values (struct ctables_table *t)
4971 const struct variable *v0 = t->clabels_example;
4972 int width = var_get_width (v0);
4974 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4977 const struct val_labs *val_labs = var_get_value_labels (v0);
4978 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4979 vl = val_labs_next (val_labs, vl))
4980 if (ctables_categories_match (c0, &vl->value, v0))
4981 ctables_value_insert (t, &vl->value, width);
4984 size_t n = hmap_count (&t->clabels_values_map);
4985 t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
4987 struct ctables_value *clv;
4989 HMAP_FOR_EACH (clv, struct ctables_value, node, &t->clabels_values_map)
4990 t->clabels_values[i++] = clv;
4991 t->n_clabels_values = n;
4994 sort (t->clabels_values, n, sizeof *t->clabels_values,
4995 compare_clabels_values_3way, &width);
4997 for (size_t i = 0; i < n; i++)
4998 t->clabels_values[i]->leaf = i;
5002 ctables_add_category_occurrences (const struct variable *var,
5003 struct hmap *occurrences,
5004 const struct ctables_categories *cats)
5006 const struct val_labs *val_labs = var_get_value_labels (var);
5008 for (size_t i = 0; i < cats->n_cats; i++)
5010 const struct ctables_category *c = &cats->cats[i];
5014 ctables_add_occurrence (var, &(const union value) { .f = c->number },
5020 int width = var_get_width (var);
5022 value_init (&value, width);
5023 value_copy_buf_rpad (&value, width,
5024 CHAR_CAST (uint8_t *, c->string.string),
5025 c->string.length, ' ');
5026 ctables_add_occurrence (var, &value, occurrences);
5027 value_destroy (&value, width);
5032 assert (var_is_numeric (var));
5033 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5034 vl = val_labs_next (val_labs, vl))
5035 if (vl->value.f >= c->nrange[0] && vl->value.f <= c->nrange[1])
5036 ctables_add_occurrence (var, &vl->value, occurrences);
5040 assert (var_is_alpha (var));
5041 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5042 vl = val_labs_next (val_labs, vl))
5043 if (in_string_range (&vl->value, var, c->srange))
5044 ctables_add_occurrence (var, &vl->value, occurrences);
5048 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5049 vl = val_labs_next (val_labs, vl))
5050 if (var_is_value_missing (var, &vl->value))
5051 ctables_add_occurrence (var, &vl->value, occurrences);
5055 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5056 vl = val_labs_next (val_labs, vl))
5057 ctables_add_occurrence (var, &vl->value, occurrences);
5060 case CCT_POSTCOMPUTE:
5070 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5071 vl = val_labs_next (val_labs, vl))
5072 if (c->include_missing || !var_is_value_missing (var, &vl->value))
5073 ctables_add_occurrence (var, &vl->value, occurrences);
5076 case CCT_EXCLUDED_MISSING:
5083 ctables_section_recurse_add_empty_categories (
5084 struct ctables_section *s,
5085 const struct ctables_category *cats[PIVOT_N_AXES][10], struct ccase *c,
5086 enum pivot_axis_type a, size_t a_idx)
5088 if (a >= PIVOT_N_AXES)
5089 ctables_cell_insert__ (s, c, cats);
5090 else if (!s->nests[a] || a_idx >= s->nests[a]->n)
5091 ctables_section_recurse_add_empty_categories (s, cats, c, a + 1, 0);
5094 const struct variable *var = s->nests[a]->vars[a_idx];
5095 const struct ctables_categories *categories = s->table->categories[
5096 var_get_dict_index (var)];
5097 int width = var_get_width (var);
5098 const struct hmap *occurrences = &s->occurrences[a][a_idx];
5099 const struct ctables_occurrence *o;
5100 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
5102 union value *value = case_data_rw (c, var);
5103 value_destroy (value, width);
5104 value_clone (value, &o->value, width);
5105 cats[a][a_idx] = ctables_categories_match (categories, value, var);
5106 assert (cats[a][a_idx] != NULL);
5107 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
5110 for (size_t i = 0; i < categories->n_cats; i++)
5112 const struct ctables_category *cat = &categories->cats[i];
5113 if (cat->type == CCT_POSTCOMPUTE)
5115 cats[a][a_idx] = cat;
5116 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
5123 ctables_section_add_empty_categories (struct ctables_section *s)
5125 bool show_empty = false;
5126 for (size_t a = 0; a < PIVOT_N_AXES; a++)
5128 for (size_t k = 0; k < s->nests[a]->n; k++)
5129 if (k != s->nests[a]->scale_idx)
5131 const struct variable *var = s->nests[a]->vars[k];
5132 const struct ctables_categories *cats = s->table->categories[
5133 var_get_dict_index (var)];
5134 if (cats->show_empty)
5137 ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
5143 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
5144 struct ccase *c = case_create (dict_get_proto (s->table->ctables->dict));
5145 ctables_section_recurse_add_empty_categories (s, cats, c, 0, 0);
5150 ctables_execute (struct dataset *ds, struct ctables *ct)
5152 for (size_t i = 0; i < ct->n_tables; i++)
5154 struct ctables_table *t = ct->tables[i];
5155 t->sections = xnmalloc (MAX (1, t->stacks[PIVOT_AXIS_ROW].n) *
5156 MAX (1, t->stacks[PIVOT_AXIS_COLUMN].n) *
5157 MAX (1, t->stacks[PIVOT_AXIS_LAYER].n),
5158 sizeof *t->sections);
5159 size_t ix[PIVOT_N_AXES];
5160 ctables_table_add_section (t, 0, ix);
5163 struct casereader *input = proc_open (ds);
5164 bool warn_on_invalid = true;
5165 for (struct ccase *c = casereader_read (input); c;
5166 case_unref (c), c = casereader_read (input))
5168 double d_weight = dict_get_case_weight (dataset_dict (ds), c,
5170 double e_weight = (ct->e_weight
5171 ? var_force_valid_weight (ct->e_weight,
5172 case_num (c, ct->e_weight),
5176 for (size_t i = 0; i < ct->n_tables; i++)
5178 struct ctables_table *t = ct->tables[i];
5180 for (size_t j = 0; j < t->n_sections; j++)
5181 ctables_cell_insert (&t->sections[j], c, d_weight, e_weight);
5183 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5184 if (t->label_axis[a] != a)
5185 ctables_insert_clabels_values (t, c, a);
5188 casereader_destroy (input);
5190 for (size_t i = 0; i < ct->n_tables; i++)
5192 struct ctables_table *t = ct->tables[i];
5194 if (t->clabels_example)
5195 ctables_sort_clabels_values (t);
5197 for (size_t j = 0; j < t->n_sections; j++)
5198 ctables_section_add_empty_categories (&t->sections[j]);
5200 ctables_table_output (ct, ct->tables[i]);
5202 return proc_commit (ds);
5207 typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *,
5208 struct dictionary *);
5211 ctables_pcexpr_destroy (struct ctables_pcexpr *e)
5217 case CTPO_CAT_STRING:
5218 ss_dealloc (&e->string);
5221 case CTPO_CAT_SRANGE:
5222 for (size_t i = 0; i < 2; i++)
5223 ss_dealloc (&e->srange[i]);
5232 for (size_t i = 0; i < 2; i++)
5233 ctables_pcexpr_destroy (e->subs[i]);
5237 case CTPO_CAT_NUMBER:
5238 case CTPO_CAT_NRANGE:
5239 case CTPO_CAT_MISSING:
5240 case CTPO_CAT_OTHERNM:
5241 case CTPO_CAT_SUBTOTAL:
5242 case CTPO_CAT_TOTAL:
5246 msg_location_destroy (e->location);
5251 static struct ctables_pcexpr *
5252 ctables_pcexpr_allocate_binary (enum ctables_postcompute_op op,
5253 struct ctables_pcexpr *sub0,
5254 struct ctables_pcexpr *sub1)
5256 struct ctables_pcexpr *e = xmalloc (sizeof *e);
5257 *e = (struct ctables_pcexpr) {
5259 .subs = { sub0, sub1 },
5260 .location = msg_location_merged (sub0->location, sub1->location),
5265 /* How to parse an operator. */
5268 enum token_type token;
5269 enum ctables_postcompute_op op;
5272 static const struct operator *
5273 ctable_pcexpr_match_operator (struct lexer *lexer,
5274 const struct operator ops[], size_t n_ops)
5276 for (const struct operator *op = ops; op < ops + n_ops; op++)
5277 if (lex_token (lexer) == op->token)
5279 if (op->token != T_NEG_NUM)
5288 static struct ctables_pcexpr *
5289 ctable_pcexpr_parse_binary_operators__ (
5290 struct lexer *lexer, struct dictionary *dict,
5291 const struct operator ops[], size_t n_ops,
5292 parse_recursively_func *parse_next_level,
5293 const char *chain_warning, struct ctables_pcexpr *lhs)
5295 for (int op_count = 0; ; op_count++)
5297 const struct operator *op
5298 = ctable_pcexpr_match_operator (lexer, ops, n_ops);
5301 if (op_count > 1 && chain_warning)
5302 msg_at (SW, lhs->location, "%s", chain_warning);
5307 struct ctables_pcexpr *rhs = parse_next_level (lexer, dict);
5310 ctables_pcexpr_destroy (lhs);
5314 lhs = ctables_pcexpr_allocate_binary (op->op, lhs, rhs);
5318 static struct ctables_pcexpr *
5319 ctable_pcexpr_parse_binary_operators (struct lexer *lexer,
5320 struct dictionary *dict,
5321 const struct operator ops[], size_t n_ops,
5322 parse_recursively_func *parse_next_level,
5323 const char *chain_warning)
5325 struct ctables_pcexpr *lhs = parse_next_level (lexer, dict);
5329 return ctable_pcexpr_parse_binary_operators__ (lexer, dict, ops, n_ops,
5331 chain_warning, lhs);
5334 static struct ctables_pcexpr *ctable_pcexpr_parse_add (struct lexer *,
5335 struct dictionary *);
5337 static struct ctables_pcexpr
5338 ctpo_cat_nrange (double low, double high)
5340 return (struct ctables_pcexpr) {
5341 .op = CTPO_CAT_NRANGE,
5342 .nrange = { low, high },
5346 static struct ctables_pcexpr *
5347 ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
5349 int start_ofs = lex_ofs (lexer);
5350 struct ctables_pcexpr e;
5351 if (lex_is_number (lexer))
5353 e = (struct ctables_pcexpr) { .op = CTPO_CONSTANT,
5354 .number = lex_number (lexer) };
5357 else if (lex_match_id (lexer, "MISSING"))
5358 e = (struct ctables_pcexpr) { .op = CTPO_CAT_MISSING };
5359 else if (lex_match_id (lexer, "OTHERNM"))
5360 e = (struct ctables_pcexpr) { .op = CTPO_CAT_OTHERNM };
5361 else if (lex_match_id (lexer, "TOTAL"))
5362 e = (struct ctables_pcexpr) { .op = CTPO_CAT_TOTAL };
5363 else if (lex_match_id (lexer, "SUBTOTAL"))
5365 size_t subtotal_index = 0;
5366 if (lex_match (lexer, T_LBRACK))
5368 if (!lex_force_int_range (lexer, "SUBTOTAL", 1, LONG_MAX))
5370 subtotal_index = lex_integer (lexer);
5372 if (!lex_force_match (lexer, T_RBRACK))
5375 e = (struct ctables_pcexpr) { .op = CTPO_CAT_SUBTOTAL,
5376 .subtotal_index = subtotal_index };
5378 else if (lex_match (lexer, T_LBRACK))
5380 if (lex_match_id (lexer, "LO"))
5382 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
5384 e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
5387 else if (lex_is_number (lexer))
5389 double number = lex_number (lexer);
5391 if (lex_match_id (lexer, "THRU"))
5393 if (lex_match_id (lexer, "HI"))
5394 e = ctpo_cat_nrange (number, DBL_MAX);
5397 if (!lex_force_num (lexer))
5399 e = ctpo_cat_nrange (number, lex_number (lexer));
5404 e = (struct ctables_pcexpr) { .op = CTPO_CAT_NUMBER,
5407 else if (lex_is_string (lexer))
5409 struct substring s = recode_substring_pool (
5410 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
5411 ss_rtrim (&s, ss_cstr (" "));
5413 e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
5418 lex_error (lexer, NULL);
5422 if (!lex_force_match (lexer, T_RBRACK))
5424 if (e.op == CTPO_CAT_STRING)
5425 ss_dealloc (&e.string);
5429 else if (lex_match (lexer, T_LPAREN))
5431 struct ctables_pcexpr *ep = ctable_pcexpr_parse_add (lexer, dict);
5434 if (!lex_force_match (lexer, T_RPAREN))
5436 ctables_pcexpr_destroy (ep);
5443 lex_error (lexer, NULL);
5447 e.location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
5448 return xmemdup (&e, sizeof e);
5451 static struct ctables_pcexpr *
5452 ctables_pcexpr_allocate_neg (struct ctables_pcexpr *sub,
5453 struct lexer *lexer, int start_ofs)
5455 struct ctables_pcexpr *e = xmalloc (sizeof *e);
5456 *e = (struct ctables_pcexpr) {
5459 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
5464 static struct ctables_pcexpr *
5465 ctable_pcexpr_parse_exp (struct lexer *lexer, struct dictionary *dict)
5467 static const struct operator op = { T_EXP, CTPO_POW };
5469 const char *chain_warning =
5470 _("The exponentiation operator (`**') is left-associative: "
5471 "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
5472 "To disable this warning, insert parentheses.");
5474 if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
5475 return ctable_pcexpr_parse_binary_operators (lexer, dict, &op, 1,
5476 ctable_pcexpr_parse_primary,
5479 /* Special case for situations like "-5**6", which must be parsed as
5482 int start_ofs = lex_ofs (lexer);
5483 struct ctables_pcexpr *lhs = xmalloc (sizeof *lhs);
5484 *lhs = (struct ctables_pcexpr) {
5485 .op = CTPO_CONSTANT,
5486 .number = -lex_tokval (lexer),
5487 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer)),
5491 struct ctables_pcexpr *node = ctable_pcexpr_parse_binary_operators__ (
5492 lexer, dict, &op, 1,
5493 ctable_pcexpr_parse_primary, chain_warning, lhs);
5497 return ctables_pcexpr_allocate_neg (node, lexer, start_ofs);
5500 /* Parses the unary minus level. */
5501 static struct ctables_pcexpr *
5502 ctable_pcexpr_parse_neg (struct lexer *lexer, struct dictionary *dict)
5504 int start_ofs = lex_ofs (lexer);
5505 if (!lex_match (lexer, T_DASH))
5506 return ctable_pcexpr_parse_exp (lexer, dict);
5508 struct ctables_pcexpr *inner = ctable_pcexpr_parse_neg (lexer, dict);
5512 return ctables_pcexpr_allocate_neg (inner, lexer, start_ofs);
5515 /* Parses the multiplication and division level. */
5516 static struct ctables_pcexpr *
5517 ctable_pcexpr_parse_mul (struct lexer *lexer, struct dictionary *dict)
5519 static const struct operator ops[] =
5521 { T_ASTERISK, CTPO_MUL },
5522 { T_SLASH, CTPO_DIV },
5525 return ctable_pcexpr_parse_binary_operators (lexer, dict, ops,
5526 sizeof ops / sizeof *ops,
5527 ctable_pcexpr_parse_neg, NULL);
5530 /* Parses the addition and subtraction level. */
5531 static struct ctables_pcexpr *
5532 ctable_pcexpr_parse_add (struct lexer *lexer, struct dictionary *dict)
5534 static const struct operator ops[] =
5536 { T_PLUS, CTPO_ADD },
5537 { T_DASH, CTPO_SUB },
5538 { T_NEG_NUM, CTPO_ADD },
5541 return ctable_pcexpr_parse_binary_operators (lexer, dict,
5542 ops, sizeof ops / sizeof *ops,
5543 ctable_pcexpr_parse_mul, NULL);
5546 static struct ctables_postcompute *
5547 ctables_find_postcompute (struct ctables *ct, const char *name)
5549 struct ctables_postcompute *pc;
5550 HMAP_FOR_EACH_WITH_HASH (pc, struct ctables_postcompute, hmap_node,
5551 utf8_hash_case_string (name, 0), &ct->postcomputes)
5552 if (!utf8_strcasecmp (pc->name, name))
5558 ctables_parse_pcompute (struct lexer *lexer, struct dictionary *dict,
5561 int pcompute_start = lex_ofs (lexer) - 1;
5563 if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
5566 char *name = ss_xstrdup (lex_tokss (lexer));
5569 if (!lex_force_match (lexer, T_EQUALS)
5570 || !lex_force_match_id (lexer, "EXPR")
5571 || !lex_force_match (lexer, T_LPAREN))
5577 int expr_start = lex_ofs (lexer);
5578 struct ctables_pcexpr *expr = ctable_pcexpr_parse_add (lexer, dict);
5579 int expr_end = lex_ofs (lexer) - 1;
5580 if (!expr || !lex_force_match (lexer, T_RPAREN))
5585 int pcompute_end = lex_ofs (lexer) - 1;
5587 struct msg_location *location = lex_ofs_location (lexer, pcompute_start,
5590 struct ctables_postcompute *pc = ctables_find_postcompute (ct, name);
5593 msg_at (SW, location, _("New definition of &%s will override the "
5594 "previous definition."),
5596 msg_at (SN, pc->location, _("This is the previous definition."));
5598 ctables_pcexpr_destroy (pc->expr);
5599 msg_location_destroy (pc->location);
5604 pc = xmalloc (sizeof *pc);
5605 *pc = (struct ctables_postcompute) { .name = name };
5606 hmap_insert (&ct->postcomputes, &pc->hmap_node,
5607 utf8_hash_case_string (pc->name, 0));
5610 pc->location = location;
5612 pc->label = lex_ofs_representation (lexer, expr_start, expr_end);
5617 ctables_parse_pproperties_format (struct lexer *lexer,
5618 struct ctables_summary_spec_set *sss)
5620 *sss = (struct ctables_summary_spec_set) { .n = 0 };
5622 while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH
5623 && !(lex_token (lexer) == T_ID
5624 && (lex_id_match (ss_cstr ("LABEL"), lex_tokss (lexer))
5625 || lex_id_match (ss_cstr ("HIDESOURCECATS"),
5626 lex_tokss (lexer)))))
5628 /* Parse function. */
5629 enum ctables_summary_function function;
5630 if (!parse_ctables_summary_function (lexer, &function))
5633 /* Parse percentile. */
5634 double percentile = 0;
5635 if (function == CTSF_PTILE)
5637 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
5639 percentile = lex_number (lexer);
5644 struct fmt_spec format;
5645 bool is_ctables_format;
5646 if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
5649 if (sss->n >= sss->allocated)
5650 sss->specs = x2nrealloc (sss->specs, &sss->allocated,
5651 sizeof *sss->specs);
5652 sss->specs[sss->n++] = (struct ctables_summary_spec) {
5653 .function = function,
5654 .percentile = percentile,
5656 .is_ctables_format = is_ctables_format,
5662 ctables_summary_spec_set_uninit (sss);
5667 ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
5669 struct ctables_postcompute **pcs = NULL;
5671 size_t allocated_pcs = 0;
5673 while (lex_match (lexer, T_AND))
5675 if (!lex_force_id (lexer))
5677 struct ctables_postcompute *pc
5678 = ctables_find_postcompute (ct, lex_tokcstr (lexer));
5681 msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
5686 if (n_pcs >= allocated_pcs)
5687 pcs = x2nrealloc (pcs, &allocated_pcs, sizeof *pcs);
5691 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5693 if (lex_match_id (lexer, "LABEL"))
5695 lex_match (lexer, T_EQUALS);
5696 if (!lex_force_string (lexer))
5699 for (size_t i = 0; i < n_pcs; i++)
5701 free (pcs[i]->label);
5702 pcs[i]->label = ss_xstrdup (lex_tokss (lexer));
5707 else if (lex_match_id (lexer, "FORMAT"))
5709 lex_match (lexer, T_EQUALS);
5711 struct ctables_summary_spec_set sss;
5712 if (!ctables_parse_pproperties_format (lexer, &sss))
5715 for (size_t i = 0; i < n_pcs; i++)
5718 ctables_summary_spec_set_uninit (pcs[i]->specs);
5720 pcs[i]->specs = xmalloc (sizeof *pcs[i]->specs);
5721 ctables_summary_spec_set_clone (pcs[i]->specs, &sss);
5723 ctables_summary_spec_set_uninit (&sss);
5725 else if (lex_match_id (lexer, "HIDESOURCECATS"))
5727 lex_match (lexer, T_EQUALS);
5728 bool hide_source_cats;
5729 if (!parse_bool (lexer, &hide_source_cats))
5731 for (size_t i = 0; i < n_pcs; i++)
5732 pcs[i]->hide_source_cats = hide_source_cats;
5736 lex_error_expecting (lexer, "LABEL", "FORMAT", "HIDESOURCECATS");
5749 put_strftime (struct string *out, time_t now, const char *format)
5751 const struct tm *tm = localtime (&now);
5753 strftime (value, sizeof value, format, tm);
5754 ds_put_cstr (out, value);
5758 skip_prefix (struct substring *s, struct substring prefix)
5760 if (ss_starts_with (*s, prefix))
5762 ss_advance (s, prefix.length);
5770 put_table_expression (struct string *out, struct lexer *lexer,
5771 struct dictionary *dict, int expr_start, int expr_end)
5774 for (int ofs = expr_start; ofs < expr_end; ofs++)
5776 const struct token *t = lex_ofs_token (lexer, ofs);
5777 if (t->type == T_LBRACK)
5779 else if (t->type == T_RBRACK && nest > 0)
5785 else if (t->type == T_ID)
5787 const struct variable *var
5788 = dict_lookup_var (dict, t->string.string);
5789 const char *label = var ? var_get_label (var) : NULL;
5790 ds_put_cstr (out, label ? label : t->string.string);
5794 if (ofs != expr_start && t->type != T_RPAREN && ds_last (out) != ' ')
5795 ds_put_byte (out, ' ');
5797 char *repr = lex_ofs_representation (lexer, ofs, ofs);
5798 ds_put_cstr (out, repr);
5801 if (ofs + 1 != expr_end && t->type != T_LPAREN)
5802 ds_put_byte (out, ' ');
5808 put_title_text (struct string *out, struct substring in, time_t now,
5809 struct lexer *lexer, struct dictionary *dict,
5810 int expr_start, int expr_end)
5814 size_t chunk = ss_find_byte (in, ')');
5815 ds_put_substring (out, ss_head (in, chunk));
5816 ss_advance (&in, chunk);
5817 if (ss_is_empty (in))
5820 if (skip_prefix (&in, ss_cstr (")DATE")))
5821 put_strftime (out, now, "%x");
5822 else if (skip_prefix (&in, ss_cstr (")TIME")))
5823 put_strftime (out, now, "%X");
5824 else if (skip_prefix (&in, ss_cstr (")TABLE")))
5825 put_table_expression (out, lexer, dict, expr_start, expr_end);
5828 ds_put_byte (out, ')');
5829 ss_advance (&in, 1);
5835 cmd_ctables (struct lexer *lexer, struct dataset *ds)
5837 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
5838 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
5839 enum settings_value_show tvars = settings_get_show_variables ();
5840 for (size_t i = 0; i < n_vars; i++)
5841 vlabels[i] = (enum ctables_vlabel) tvars;
5843 struct pivot_table_look *look = pivot_table_look_unshare (
5844 pivot_table_look_ref (pivot_table_look_get_default ()));
5845 look->omit_empty = false;
5847 struct ctables *ct = xmalloc (sizeof *ct);
5848 *ct = (struct ctables) {
5849 .dict = dataset_dict (ds),
5851 .ctables_formats = FMT_SETTINGS_INIT,
5853 .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
5856 time_t now = time (NULL);
5861 const char *dot_string;
5862 const char *comma_string;
5864 static const struct ctf ctfs[4] = {
5865 { CTEF_NEGPAREN, "(,,,)", "(...)" },
5866 { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
5867 { CTEF_PAREN, "-,(,),", "-.(.)." },
5868 { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
5870 bool is_dot = settings_get_fmt_settings ()->decimal == '.';
5871 for (size_t i = 0; i < 4; i++)
5873 const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
5874 fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
5875 fmt_number_style_from_string (s));
5878 if (!lex_force_match (lexer, T_SLASH))
5881 while (!lex_match_id (lexer, "TABLE"))
5883 if (lex_match_id (lexer, "FORMAT"))
5885 double widths[2] = { SYSMIS, SYSMIS };
5886 double units_per_inch = 72.0;
5888 while (lex_token (lexer) != T_SLASH)
5890 if (lex_match_id (lexer, "MINCOLWIDTH"))
5892 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
5895 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
5897 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
5900 else if (lex_match_id (lexer, "UNITS"))
5902 lex_match (lexer, T_EQUALS);
5903 if (lex_match_id (lexer, "POINTS"))
5904 units_per_inch = 72.0;
5905 else if (lex_match_id (lexer, "INCHES"))
5906 units_per_inch = 1.0;
5907 else if (lex_match_id (lexer, "CM"))
5908 units_per_inch = 2.54;
5911 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
5915 else if (lex_match_id (lexer, "EMPTY"))
5920 lex_match (lexer, T_EQUALS);
5921 if (lex_match_id (lexer, "ZERO"))
5923 /* Nothing to do. */
5925 else if (lex_match_id (lexer, "BLANK"))
5926 ct->zero = xstrdup ("");
5927 else if (lex_force_string (lexer))
5929 ct->zero = ss_xstrdup (lex_tokss (lexer));
5935 else if (lex_match_id (lexer, "MISSING"))
5937 lex_match (lexer, T_EQUALS);
5938 if (!lex_force_string (lexer))
5942 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
5943 ? ss_xstrdup (lex_tokss (lexer))
5949 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
5950 "UNITS", "EMPTY", "MISSING");
5955 if (widths[0] != SYSMIS && widths[1] != SYSMIS
5956 && widths[0] > widths[1])
5958 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
5962 for (size_t i = 0; i < 2; i++)
5963 if (widths[i] != SYSMIS)
5965 int *wr = ct->look->width_ranges[TABLE_HORZ];
5966 wr[i] = widths[i] / units_per_inch * 96.0;
5971 else if (lex_match_id (lexer, "VLABELS"))
5973 if (!lex_force_match_id (lexer, "VARIABLES"))
5975 lex_match (lexer, T_EQUALS);
5977 struct variable **vars;
5979 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
5983 if (!lex_force_match_id (lexer, "DISPLAY"))
5988 lex_match (lexer, T_EQUALS);
5990 enum ctables_vlabel vlabel;
5991 if (lex_match_id (lexer, "DEFAULT"))
5992 vlabel = (enum ctables_vlabel) settings_get_show_variables ();
5993 else if (lex_match_id (lexer, "NAME"))
5995 else if (lex_match_id (lexer, "LABEL"))
5996 vlabel = CTVL_LABEL;
5997 else if (lex_match_id (lexer, "BOTH"))
5999 else if (lex_match_id (lexer, "NONE"))
6003 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
6009 for (size_t i = 0; i < n_vars; i++)
6010 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
6013 else if (lex_match_id (lexer, "MRSETS"))
6015 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
6017 lex_match (lexer, T_EQUALS);
6018 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
6021 else if (lex_match_id (lexer, "SMISSING"))
6023 if (lex_match_id (lexer, "VARIABLE"))
6024 ct->smissing_listwise = false;
6025 else if (lex_match_id (lexer, "LISTWISE"))
6026 ct->smissing_listwise = true;
6029 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
6033 else if (lex_match_id (lexer, "PCOMPUTE"))
6035 if (!ctables_parse_pcompute (lexer, dataset_dict (ds), ct))
6038 else if (lex_match_id (lexer, "PPROPERTIES"))
6040 if (!ctables_parse_pproperties (lexer, ct))
6043 else if (lex_match_id (lexer, "WEIGHT"))
6045 if (!lex_force_match_id (lexer, "VARIABLE"))
6047 lex_match (lexer, T_EQUALS);
6048 ct->e_weight = parse_variable (lexer, dataset_dict (ds));
6052 else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
6054 if (lex_match_id (lexer, "COUNT"))
6056 lex_match (lexer, T_EQUALS);
6057 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT",
6060 ct->hide_threshold = lex_integer (lexer);
6063 else if (ct->hide_threshold == 0)
6064 ct->hide_threshold = 5;
6068 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
6069 "SMISSING", "PCOMPUTE", "PPROPERTIES",
6070 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
6074 if (!lex_force_match (lexer, T_SLASH))
6078 size_t allocated_tables = 0;
6081 if (ct->n_tables >= allocated_tables)
6082 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
6083 sizeof *ct->tables);
6085 struct ctables_category *cat = xmalloc (sizeof *cat);
6086 *cat = (struct ctables_category) {
6088 .include_missing = false,
6089 .sort_ascending = true,
6092 struct ctables_categories *c = xmalloc (sizeof *c);
6093 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
6094 *c = (struct ctables_categories) {
6101 struct ctables_categories **categories = xnmalloc (n_vars,
6102 sizeof *categories);
6103 for (size_t i = 0; i < n_vars; i++)
6106 struct ctables_table *t = xmalloc (sizeof *t);
6107 *t = (struct ctables_table) {
6109 .slabels_axis = PIVOT_AXIS_COLUMN,
6110 .slabels_visible = true,
6111 .clabels_values_map = HMAP_INITIALIZER (t->clabels_values_map),
6113 [PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW,
6114 [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
6115 [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
6117 .clabels_from_axis = PIVOT_AXIS_LAYER,
6118 .categories = categories,
6119 .n_categories = n_vars,
6122 ct->tables[ct->n_tables++] = t;
6124 lex_match (lexer, T_EQUALS);
6125 int expr_start = lex_ofs (lexer);
6126 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
6128 if (lex_match (lexer, T_BY))
6130 if (!ctables_axis_parse (lexer, dataset_dict (ds),
6131 ct, t, PIVOT_AXIS_COLUMN))
6134 if (lex_match (lexer, T_BY))
6136 if (!ctables_axis_parse (lexer, dataset_dict (ds),
6137 ct, t, PIVOT_AXIS_LAYER))
6141 int expr_end = lex_ofs (lexer);
6143 if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
6144 && !t->axes[PIVOT_AXIS_LAYER])
6146 lex_error (lexer, _("At least one variable must be specified."));
6150 const struct ctables_axis *scales[PIVOT_N_AXES];
6151 size_t n_scales = 0;
6152 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6154 scales[a] = find_scale (t->axes[a]);
6160 msg (SE, _("Scale variables may appear only on one axis."));
6161 if (scales[PIVOT_AXIS_ROW])
6162 msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
6163 _("This scale variable appears on the rows axis."));
6164 if (scales[PIVOT_AXIS_COLUMN])
6165 msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
6166 _("This scale variable appears on the columns axis."));
6167 if (scales[PIVOT_AXIS_LAYER])
6168 msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
6169 _("This scale variable appears on the layer axis."));
6173 const struct ctables_axis *summaries[PIVOT_N_AXES];
6174 size_t n_summaries = 0;
6175 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6177 summaries[a] = (scales[a]
6179 : find_categorical_summary_spec (t->axes[a]));
6183 if (n_summaries > 1)
6185 msg (SE, _("Summaries may appear only on one axis."));
6186 if (summaries[PIVOT_AXIS_ROW])
6187 msg_at (SN, summaries[PIVOT_AXIS_ROW]->loc,
6188 _("This variable on the rows axis has a summary."));
6189 if (summaries[PIVOT_AXIS_COLUMN])
6190 msg_at (SN, summaries[PIVOT_AXIS_COLUMN]->loc,
6191 _("This variable on the columns axis has a summary."));
6192 if (summaries[PIVOT_AXIS_LAYER])
6193 msg_at (SN, summaries[PIVOT_AXIS_LAYER]->loc,
6194 _("This variable on the layers axis has a summary."));
6197 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6198 if (n_summaries ? summaries[a] : t->axes[a])
6200 t->summary_axis = a;
6204 if (lex_token (lexer) == T_ENDCMD)
6206 if (!ctables_prepare_table (t))
6210 if (!lex_force_match (lexer, T_SLASH))
6213 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
6215 if (lex_match_id (lexer, "SLABELS"))
6217 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
6219 if (lex_match_id (lexer, "POSITION"))
6221 lex_match (lexer, T_EQUALS);
6222 if (lex_match_id (lexer, "COLUMN"))
6223 t->slabels_axis = PIVOT_AXIS_COLUMN;
6224 else if (lex_match_id (lexer, "ROW"))
6225 t->slabels_axis = PIVOT_AXIS_ROW;
6226 else if (lex_match_id (lexer, "LAYER"))
6227 t->slabels_axis = PIVOT_AXIS_LAYER;
6230 lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
6234 else if (lex_match_id (lexer, "VISIBLE"))
6236 lex_match (lexer, T_EQUALS);
6237 if (!parse_bool (lexer, &t->slabels_visible))
6242 lex_error_expecting (lexer, "POSITION", "VISIBLE");
6247 else if (lex_match_id (lexer, "CLABELS"))
6249 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
6251 if (lex_match_id (lexer, "AUTO"))
6253 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
6254 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
6256 else if (lex_match_id (lexer, "ROWLABELS"))
6258 lex_match (lexer, T_EQUALS);
6259 if (lex_match_id (lexer, "OPPOSITE"))
6260 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
6261 else if (lex_match_id (lexer, "LAYER"))
6262 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
6265 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
6269 else if (lex_match_id (lexer, "COLLABELS"))
6271 lex_match (lexer, T_EQUALS);
6272 if (lex_match_id (lexer, "OPPOSITE"))
6273 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
6274 else if (lex_match_id (lexer, "LAYER"))
6275 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
6278 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
6284 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
6290 else if (lex_match_id (lexer, "CRITERIA"))
6292 if (!lex_force_match_id (lexer, "CILEVEL"))
6294 lex_match (lexer, T_EQUALS);
6296 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
6298 t->cilevel = lex_number (lexer);
6301 else if (lex_match_id (lexer, "CATEGORIES"))
6303 if (!ctables_table_parse_categories (lexer, dataset_dict (ds),
6307 else if (lex_match_id (lexer, "TITLES"))
6312 if (lex_match_id (lexer, "CAPTION"))
6313 textp = &t->caption;
6314 else if (lex_match_id (lexer, "CORNER"))
6316 else if (lex_match_id (lexer, "TITLE"))
6320 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
6323 lex_match (lexer, T_EQUALS);
6325 struct string s = DS_EMPTY_INITIALIZER;
6326 while (lex_is_string (lexer))
6328 if (!ds_is_empty (&s))
6329 ds_put_byte (&s, ' ');
6330 put_title_text (&s, lex_tokss (lexer), now,
6331 lexer, dataset_dict (ds),
6332 expr_start, expr_end);
6336 *textp = ds_steal_cstr (&s);
6338 while (lex_token (lexer) != T_SLASH
6339 && lex_token (lexer) != T_ENDCMD);
6341 else if (lex_match_id (lexer, "SIGTEST"))
6345 t->chisq = xmalloc (sizeof *t->chisq);
6346 *t->chisq = (struct ctables_chisq) {
6348 .include_mrsets = true,
6349 .all_visible = true,
6355 if (lex_match_id (lexer, "TYPE"))
6357 lex_match (lexer, T_EQUALS);
6358 if (!lex_force_match_id (lexer, "CHISQUARE"))
6361 else if (lex_match_id (lexer, "ALPHA"))
6363 lex_match (lexer, T_EQUALS);
6364 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
6366 t->chisq->alpha = lex_number (lexer);
6369 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
6371 lex_match (lexer, T_EQUALS);
6372 if (parse_bool (lexer, &t->chisq->include_mrsets))
6375 else if (lex_match_id (lexer, "CATEGORIES"))
6377 lex_match (lexer, T_EQUALS);
6378 if (lex_match_id (lexer, "ALLVISIBLE"))
6379 t->chisq->all_visible = true;
6380 else if (lex_match_id (lexer, "SUBTOTALS"))
6381 t->chisq->all_visible = false;
6384 lex_error_expecting (lexer,
6385 "ALLVISIBLE", "SUBTOTALS");
6391 lex_error_expecting (lexer, "TYPE", "ALPHA",
6392 "INCLUDEMRSETS", "CATEGORIES");
6396 while (lex_token (lexer) != T_SLASH
6397 && lex_token (lexer) != T_ENDCMD);
6399 else if (lex_match_id (lexer, "COMPARETEST"))
6403 t->pairwise = xmalloc (sizeof *t->pairwise);
6404 *t->pairwise = (struct ctables_pairwise) {
6406 .alpha = { .05, .05 },
6407 .adjust = BONFERRONI,
6408 .include_mrsets = true,
6409 .meansvariance_allcats = true,
6410 .all_visible = true,
6419 if (lex_match_id (lexer, "TYPE"))
6421 lex_match (lexer, T_EQUALS);
6422 if (lex_match_id (lexer, "PROP"))
6423 t->pairwise->type = PROP;
6424 else if (lex_match_id (lexer, "MEAN"))
6425 t->pairwise->type = MEAN;
6428 lex_error_expecting (lexer, "PROP", "MEAN");
6432 else if (lex_match_id (lexer, "ALPHA"))
6434 lex_match (lexer, T_EQUALS);
6436 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
6438 double a0 = lex_number (lexer);
6441 lex_match (lexer, T_COMMA);
6442 if (lex_is_number (lexer))
6444 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
6446 double a1 = lex_number (lexer);
6449 t->pairwise->alpha[0] = MIN (a0, a1);
6450 t->pairwise->alpha[1] = MAX (a0, a1);
6453 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
6455 else if (lex_match_id (lexer, "ADJUST"))
6457 lex_match (lexer, T_EQUALS);
6458 if (lex_match_id (lexer, "BONFERRONI"))
6459 t->pairwise->adjust = BONFERRONI;
6460 else if (lex_match_id (lexer, "BH"))
6461 t->pairwise->adjust = BH;
6462 else if (lex_match_id (lexer, "NONE"))
6463 t->pairwise->adjust = 0;
6466 lex_error_expecting (lexer, "BONFERRONI", "BH",
6471 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
6473 lex_match (lexer, T_EQUALS);
6474 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
6477 else if (lex_match_id (lexer, "MEANSVARIANCE"))
6479 lex_match (lexer, T_EQUALS);
6480 if (lex_match_id (lexer, "ALLCATS"))
6481 t->pairwise->meansvariance_allcats = true;
6482 else if (lex_match_id (lexer, "TESTEDCATS"))
6483 t->pairwise->meansvariance_allcats = false;
6486 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
6490 else if (lex_match_id (lexer, "CATEGORIES"))
6492 lex_match (lexer, T_EQUALS);
6493 if (lex_match_id (lexer, "ALLVISIBLE"))
6494 t->pairwise->all_visible = true;
6495 else if (lex_match_id (lexer, "SUBTOTALS"))
6496 t->pairwise->all_visible = false;
6499 lex_error_expecting (lexer, "ALLVISIBLE",
6504 else if (lex_match_id (lexer, "MERGE"))
6506 lex_match (lexer, T_EQUALS);
6507 if (!parse_bool (lexer, &t->pairwise->merge))
6510 else if (lex_match_id (lexer, "STYLE"))
6512 lex_match (lexer, T_EQUALS);
6513 if (lex_match_id (lexer, "APA"))
6514 t->pairwise->apa_style = true;
6515 else if (lex_match_id (lexer, "SIMPLE"))
6516 t->pairwise->apa_style = false;
6519 lex_error_expecting (lexer, "APA", "SIMPLE");
6523 else if (lex_match_id (lexer, "SHOWSIG"))
6525 lex_match (lexer, T_EQUALS);
6526 if (!parse_bool (lexer, &t->pairwise->show_sig))
6531 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
6532 "INCLUDEMRSETS", "MEANSVARIANCE",
6533 "CATEGORIES", "MERGE", "STYLE",
6538 while (lex_token (lexer) != T_SLASH
6539 && lex_token (lexer) != T_ENDCMD);
6543 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
6544 "CRITERIA", "CATEGORIES", "TITLES",
6545 "SIGTEST", "COMPARETEST");
6549 if (!lex_match (lexer, T_SLASH))
6553 if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
6554 && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
6556 msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
6560 if (!ctables_prepare_table (t))
6563 while (lex_token (lexer) != T_ENDCMD);
6565 bool ok = ctables_execute (ds, ct);
6566 ctables_destroy (ct);
6567 return ok ? CMD_SUCCESS : CMD_FAILURE;
6570 ctables_destroy (ct);