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/casegrouper.h"
23 #include "data/casereader.h"
24 #include "data/casewriter.h"
25 #include "data/data-in.h"
26 #include "data/data-out.h"
27 #include "data/dataset.h"
28 #include "data/dictionary.h"
29 #include "data/mrset.h"
30 #include "data/subcase.h"
31 #include "data/value-labels.h"
32 #include "language/command.h"
33 #include "language/dictionary/split-file.h"
34 #include "language/lexer/format-parser.h"
35 #include "language/lexer/lexer.h"
36 #include "language/lexer/token.h"
37 #include "language/lexer/variable-parser.h"
38 #include "libpspp/array.h"
39 #include "libpspp/assertion.h"
40 #include "libpspp/hash-functions.h"
41 #include "libpspp/hmap.h"
42 #include "libpspp/i18n.h"
43 #include "libpspp/message.h"
44 #include "libpspp/string-array.h"
45 #include "math/mode.h"
46 #include "math/moments.h"
47 #include "math/percentiles.h"
48 #include "math/sort.h"
49 #include "output/pivot-table.h"
51 #include "gl/minmax.h"
52 #include "gl/xalloc.h"
55 #define _(msgid) gettext (msgid)
56 #define N_(msgid) (msgid)
60 CTVL_NONE = SETTINGS_VALUE_SHOW_DEFAULT,
61 CTVL_NAME = SETTINGS_VALUE_SHOW_VALUE,
62 CTVL_LABEL = SETTINGS_VALUE_SHOW_LABEL,
63 CTVL_BOTH = SETTINGS_VALUE_SHOW_BOTH,
67 - unweighted summaries (U*)
68 - lower confidence limits (*.LCL)
69 - upper confidence limits (*.UCL)
70 - standard error (*.SE)
73 /* All variables. */ \
74 S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT, CTFA_ALL) \
75 S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT, CTFA_ALL) \
76 S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT, CTFA_ALL) \
77 S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT, CTFA_ALL) \
78 S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT, CTFA_ALL) \
79 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT, CTFA_ALL) \
80 S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT, CTFA_ALL) \
81 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT, CTFA_ALL) \
82 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT, CTFA_ALL) \
83 S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
84 S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
85 S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
86 S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
87 S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
88 S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
89 S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
90 S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT, CTFA_ALL) \
91 S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT, CTFA_ALL) \
92 S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT, CTFA_ALL) \
93 S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
94 S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
95 S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
96 S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
98 /* All variables (unweighted.) */ \
99 S(CTSF_UCOUNT, "UCOUNT", N_("Unweighted Count"), CTF_COUNT, CTFA_ALL) \
100 S(CTSF_UROWPCT_COUNT, "UROWPCT.COUNT", N_("Unweighted Row %"), CTF_PERCENT, CTFA_ALL) \
101 S(CTSF_UCOLPCT_COUNT, "UCOLPCT.COUNT", N_("Unweighted Column %"), CTF_PERCENT, CTFA_ALL) \
102 S(CTSF_UTABLEPCT_COUNT, "UTABLEPCT.COUNT", N_("Unweighted Table %"), CTF_PERCENT, CTFA_ALL) \
103 S(CTSF_USUBTABLEPCT_COUNT, "USUBTABLEPCT.COUNT", N_("Unweighted Subtable %"), CTF_PERCENT, CTFA_ALL) \
104 S(CTSF_ULAYERPCT_COUNT, "ULAYERPCT.COUNT", N_("Unweighted Layer %"), CTF_PERCENT, CTFA_ALL) \
105 S(CTSF_ULAYERROWPCT_COUNT, "ULAYERROWPCT.COUNT", N_("Unweighted Layer Row %"), CTF_PERCENT, CTFA_ALL) \
106 S(CTSF_ULAYERCOLPCT_COUNT, "ULAYERCOLPCT.COUNT", N_("Unweighted Layer Column %"), CTF_PERCENT, CTFA_ALL) \
107 S(CTSF_UROWPCT_VALIDN, "UROWPCT.VALIDN", N_("Unweighted Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
108 S(CTSF_UCOLPCT_VALIDN, "UCOLPCT.VALIDN", N_("Unweighted Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
109 S(CTSF_UTABLEPCT_VALIDN, "UTABLEPCT.VALIDN", N_("Unweighted Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
110 S(CTSF_USUBTABLEPCT_VALIDN, "USUBTABLEPCT.VALIDN", N_("Unweighted Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
111 S(CTSF_ULAYERPCT_VALIDN, "ULAYERPCT.VALIDN", N_("Unweighted Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
112 S(CTSF_ULAYERROWPCT_VALIDN, "ULAYERROWPCT.VALIDN", N_("Unweighted Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
113 S(CTSF_ULAYERCOLPCT_VALIDN, "ULAYERCOLPCT.VALIDN", N_("Unweighted Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
114 S(CTSF_UROWPCT_TOTALN, "UROWPCT.TOTALN", N_("Unweighted Row Total N %"), CTF_PERCENT, CTFA_ALL) \
115 S(CTSF_UCOLPCT_TOTALN, "UCOLPCT.TOTALN", N_("Unweighted Column Total N %"), CTF_PERCENT, CTFA_ALL) \
116 S(CTSF_UTABLEPCT_TOTALN, "UTABLEPCT.TOTALN", N_("Unweighted Table Total N %"), CTF_PERCENT, CTFA_ALL) \
117 S(CTSF_USUBTABLEPCT_TOTALN, "USUBTABLEPCT.TOTALN", N_("Unweighted Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
118 S(CTSF_ULAYERPCT_TOTALN, "ULAYERPCT.TOTALN", N_("Unweighted Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
119 S(CTSF_ULAYERROWPCT_TOTALN, "ULAYERROWPCT.TOTALN", N_("Unweighted Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
120 S(CTSF_ULAYERCOLPCT_TOTALN, "ULAYERCOLPCT.TOTALN", N_("Unweighted Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
122 /* Scale variables, totals, and subtotals. */ \
123 S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE) \
124 S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE) \
125 S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL, CTFA_SCALE) \
126 S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL, CTFA_SCALE) \
127 S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL, CTFA_SCALE) \
128 S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL, CTFA_SCALE) \
129 S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL, CTFA_SCALE) \
130 S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL, CTFA_SCALE) \
131 S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
132 S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
133 S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL, CTFA_SCALE) \
134 S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT, CTFA_SCALE) \
135 S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT, CTFA_SCALE) \
136 S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT, CTFA_SCALE) \
137 S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT, CTFA_SCALE) \
138 S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL, CTFA_SCALE) \
139 S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
140 S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
141 S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
142 S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
143 S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
144 S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
145 S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
147 /* Scale variables, totals, and subtotals (unweighted). */ \
148 S(CTSF_UMEAN, "UMEAN", N_("Unweighted Mean"), CTF_GENERAL, CTFA_SCALE) \
149 S(CTSF_UMEDIAN, "UMEDIAN", N_("Unweighted Median"), CTF_GENERAL, CTFA_SCALE) \
150 S(CTSF_UMISSING, "UMISSING", N_("Unweighted Missing"), CTF_GENERAL, CTFA_SCALE) \
151 S(CTSF_UMODE, "UMODE", N_("Unweighted Mode"), CTF_GENERAL, CTFA_SCALE) \
152 S(CTSF_UPTILE, "UPTILE", N_("Unweighted Percentile"), CTF_GENERAL, CTFA_SCALE) \
153 S(CTSF_USEMEAN, "USEMEAN", N_("Unweighted Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
154 S(CTSF_USTDDEV, "USTDDEV", N_("Unweighted Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
155 S(CTSF_USUM, "USUM", N_("Unweighted Sum"), CTF_GENERAL, CTFA_SCALE) \
156 S(CSTF_UTOTALN, "UTOTALN", N_("Unweighted Total N"), CTF_COUNT, CTFA_SCALE) \
157 S(CTSF_UVALIDN, "UVALIDN", N_("Unweighted Valid N"), CTF_COUNT, CTFA_SCALE) \
158 S(CTSF_UVARIANCE, "UVARIANCE", N_("Unweighted Variance"), CTF_GENERAL, CTFA_SCALE) \
159 S(CTSF_UROWPCT_SUM, "UROWPCT.SUM", N_("Unweighted Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
160 S(CTSF_UCOLPCT_SUM, "UCOLPCT.SUM", N_("Unweighted Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
161 S(CTSF_UTABLEPCT_SUM, "UTABLEPCT.SUM", N_("Unweighted Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
162 S(CTSF_USUBTABLEPCT_SUM, "USUBTABLEPCT.SUM", N_("Unweighted Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
163 S(CTSF_ULAYERPCT_SUM, "ULAYERPCT.SUM", N_("Unweighted Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
164 S(CTSF_ULAYERROWPCT_SUM, "ULAYERROWPCT.SUM", N_("Unweighted Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
165 S(CTSF_ULAYERCOLPCT_SUM, "ULAYERCOLPCT.SUM", N_("Unweighted Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
167 #if 0 /* Multiple response sets not yet implemented. */
168 S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
169 S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
170 S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
171 S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT, CTFA_MRSETS) \
172 S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT, CTFA_MRSETS) \
173 S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT, CTFA_MRSETS) \
174 S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
175 S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
176 S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
177 S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
178 S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
179 S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
180 S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
181 S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
182 S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
183 S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
184 S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
185 S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
186 S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
187 S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
188 S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
189 S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
192 enum ctables_summary_function
194 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) ENUM,
200 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) +1
201 N_CTSF_FUNCTIONS = SUMMARIES
205 static bool ctables_summary_function_is_count (enum ctables_summary_function);
207 enum ctables_domain_type
209 /* Within a section, where stacked variables divide one section from
211 CTDT_TABLE, /* All layers of a whole section. */
212 CTDT_LAYER, /* One layer within a section. */
213 CTDT_LAYERROW, /* Row in one layer within a section. */
214 CTDT_LAYERCOL, /* Column in one layer within a section. */
216 /* Within a subtable, where a subtable pairs an innermost row variable with
217 an innermost column variable within a single layer. */
218 CTDT_SUBTABLE, /* Whole subtable. */
219 CTDT_ROW, /* Row within a subtable. */
220 CTDT_COL, /* Column within a subtable. */
224 struct ctables_domain
226 struct hmap_node node;
228 const struct ctables_cell *example;
230 double d_valid; /* Dictionary weight. */
233 double e_valid; /* Effective weight */
236 double u_valid; /* Unweighted. */
239 struct ctables_sum *sums;
248 enum ctables_summary_variant
257 /* In struct ctables_section's 'cells' hmap. Indexed by all the values in
258 all the axes (except the scalar variable, if any). */
259 struct hmap_node node;
261 /* The domains that contain this cell. */
262 uint32_t omit_domains;
263 struct ctables_domain *domains[N_CTDTS];
268 enum ctables_summary_variant sv;
270 struct ctables_cell_axis
272 struct ctables_cell_value
274 const struct ctables_category *category;
282 union ctables_summary *summaries;
289 const struct dictionary *dict;
290 struct pivot_table_look *look;
292 /* CTABLES has a number of extra formats that we implement via custom
293 currency specifications on an alternate fmt_settings. */
294 #define CTEF_NEGPAREN FMT_CCA
295 #define CTEF_NEQUAL FMT_CCB
296 #define CTEF_PAREN FMT_CCC
297 #define CTEF_PCTPAREN FMT_CCD
298 struct fmt_settings ctables_formats;
300 /* If this is NULL, zeros are displayed using the normal print format.
301 Otherwise, this string is displayed. */
304 /* If this is NULL, missing values are displayed using the normal print
305 format. Otherwise, this string is displayed. */
308 /* Indexed by variable dictionary index. */
309 enum ctables_vlabel *vlabels;
311 struct hmap postcomputes; /* Contains "struct ctables_postcompute"s. */
313 bool mrsets_count_duplicates; /* MRSETS. */
314 bool smissing_listwise; /* SMISSING. */
315 struct variable *e_weight; /* WEIGHT. */
316 int hide_threshold; /* HIDESMALLCOUNTS. */
318 struct ctables_table **tables;
322 static struct ctables_postcompute *ctables_find_postcompute (struct ctables *,
325 struct ctables_postcompute
327 struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
328 char *name; /* Name, without leading &. */
330 struct msg_location *location; /* Location of definition. */
331 struct ctables_pcexpr *expr;
333 struct ctables_summary_spec_set *specs;
334 bool hide_source_cats;
337 struct ctables_pcexpr
347 enum ctables_postcompute_op
350 CTPO_CONSTANT, /* 5 */
351 CTPO_CAT_NUMBER, /* [5] */
352 CTPO_CAT_STRING, /* ["STRING"] */
353 CTPO_CAT_NRANGE, /* [LO THRU 5] */
354 CTPO_CAT_SRANGE, /* ["A" THRU "B"] */
355 CTPO_CAT_MISSING, /* MISSING */
356 CTPO_CAT_OTHERNM, /* OTHERNM */
357 CTPO_CAT_SUBTOTAL, /* SUBTOTAL */
358 CTPO_CAT_TOTAL, /* TOTAL */
372 /* CTPO_CAT_NUMBER. */
375 /* CTPO_CAT_STRING, in dictionary encoding. */
376 struct substring string;
378 /* CTPO_CAT_NRANGE. */
381 /* CTPO_CAT_SRANGE. */
382 struct substring srange[2];
384 /* CTPO_CAT_SUBTOTAL. */
385 size_t subtotal_index;
387 /* Two elements: CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW.
388 One element: CTPO_NEG. */
389 struct ctables_pcexpr *subs[2];
392 /* Source location. */
393 struct msg_location *location;
396 static void ctables_pcexpr_destroy (struct ctables_pcexpr *);
397 static struct ctables_pcexpr *ctables_pcexpr_allocate_binary (
398 enum ctables_postcompute_op, struct ctables_pcexpr *sub0,
399 struct ctables_pcexpr *sub1);
401 struct ctables_summary_spec_set
403 struct ctables_summary_spec *specs;
407 /* The variable to which the summary specs are applied. */
408 struct variable *var;
410 /* Whether the variable to which the summary specs are applied is a scale
411 variable for the purpose of summarization.
413 (VALIDN and TOTALN act differently for summarizing scale and categorical
417 /* If any of these optional additional scale variables are missing, then
418 treat 'var' as if it's missing too. This is for implementing
419 SMISSING=LISTWISE. */
420 struct variable **listwise_vars;
421 size_t n_listwise_vars;
424 static void ctables_summary_spec_set_clone (struct ctables_summary_spec_set *,
425 const struct ctables_summary_spec_set *);
426 static void ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *);
428 /* A nested sequence of variables, e.g. a > b > c. */
431 struct variable **vars;
434 size_t *domains[N_CTDTS];
435 size_t n_domains[N_CTDTS];
438 struct ctables_summary_spec_set specs[N_CSVS];
441 /* A stack of nestings, e.g. nest1 + nest2 + ... + nestN. */
444 struct ctables_nest *nests;
450 struct hmap_node node;
455 struct ctables_occurrence
457 struct hmap_node node;
461 struct ctables_section
464 struct ctables_table *table;
465 struct ctables_nest *nests[PIVOT_N_AXES];
468 struct hmap *occurrences[PIVOT_N_AXES]; /* "struct ctables_occurrence"s. */
469 struct hmap cells; /* Contains "struct ctables_cell"s. */
470 struct hmap domains[N_CTDTS]; /* Contains "struct ctables_domain"s. */
475 struct ctables *ctables;
476 struct ctables_axis *axes[PIVOT_N_AXES];
477 struct ctables_stack stacks[PIVOT_N_AXES];
478 struct ctables_section *sections;
480 enum pivot_axis_type summary_axis;
481 struct ctables_summary_spec_set summary_specs;
482 struct variable **sum_vars;
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 If any category labels are moved, then 'clabels_example' is one of the
498 variables being moved (and it is otherwise NULL). All of the variables
499 being moved have the same width, value labels, and categories, so this
500 example variable can be used to find those out.
502 The remaining members in this group are relevant only if category labels
505 'clabels_values_map' holds a "struct ctables_value" for all the values
506 that appear in all of the variables in the moved categories. It is
507 accumulated as the data is read. Once the data is fully read, its
508 sorted values are put into 'clabels_values' and 'n_clabels_values'.
510 enum pivot_axis_type label_axis[PIVOT_N_AXES];
511 enum pivot_axis_type clabels_from_axis;
512 const struct variable *clabels_example;
513 struct hmap clabels_values_map;
514 struct ctables_value **clabels_values;
515 size_t n_clabels_values;
517 /* Indexed by variable dictionary index. */
518 struct ctables_categories **categories;
527 struct ctables_chisq *chisq;
528 struct ctables_pairwise *pairwise;
531 struct ctables_categories
534 struct ctables_category *cats;
539 struct ctables_category
541 enum ctables_category_type
543 /* Explicit category lists. */
546 CCT_NRANGE, /* Numerical range. */
547 CCT_SRANGE, /* String range. */
552 /* Totals and subtotals. */
556 /* Implicit category lists. */
561 /* For contributing to TOTALN. */
562 CCT_EXCLUDED_MISSING,
566 struct ctables_category *subtotal;
572 double number; /* CCT_NUMBER. */
573 struct substring string; /* CCT_STRING, in dictionary encoding. */
574 double nrange[2]; /* CCT_NRANGE. */
575 struct substring srange[2]; /* CCT_SRANGE. */
579 char *total_label; /* CCT_SUBTOTAL, CCT_TOTAL. */
580 bool hide_subcategories; /* CCT_SUBTOTAL. */
583 const struct ctables_postcompute *pc; /* CCT_POSTCOMPUTE. */
585 /* CCT_VALUE, CCT_LABEL, CCT_FUNCTION. */
588 bool include_missing;
592 enum ctables_summary_function sort_function;
593 struct variable *sort_var;
598 /* Source location. This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
599 CCT_FUNCTION, CCT_EXCLUDED_MISSING. */
600 struct msg_location *location;
604 ctables_category_uninit (struct ctables_category *cat)
615 case CCT_POSTCOMPUTE:
619 ss_dealloc (&cat->string);
623 ss_dealloc (&cat->srange[0]);
624 ss_dealloc (&cat->srange[1]);
629 free (cat->total_label);
637 case CCT_EXCLUDED_MISSING:
643 nullable_substring_equal (const struct substring *a,
644 const struct substring *b)
646 return !a->string ? !b->string : b->string && ss_equals (*a, *b);
650 ctables_category_equal (const struct ctables_category *a,
651 const struct ctables_category *b)
653 if (a->type != b->type)
659 return a->number == b->number;
662 return ss_equals (a->string, b->string);
665 return a->nrange[0] == b->nrange[0] && a->nrange[1] == b->nrange[1];
668 return (nullable_substring_equal (&a->srange[0], &b->srange[0])
669 && nullable_substring_equal (&a->srange[1], &b->srange[1]));
675 case CCT_POSTCOMPUTE:
676 return a->pc == b->pc;
680 return !strcmp (a->total_label, b->total_label);
685 return (a->include_missing == b->include_missing
686 && a->sort_ascending == b->sort_ascending
687 && a->sort_function == b->sort_function
688 && a->sort_var == b->sort_var
689 && a->percentile == b->percentile);
691 case CCT_EXCLUDED_MISSING:
699 ctables_categories_unref (struct ctables_categories *c)
704 assert (c->n_refs > 0);
708 for (size_t i = 0; i < c->n_cats; i++)
709 ctables_category_uninit (&c->cats[i]);
715 ctables_categories_equal (const struct ctables_categories *a,
716 const struct ctables_categories *b)
718 if (a->n_cats != b->n_cats || a->show_empty != b->show_empty)
721 for (size_t i = 0; i < a->n_cats; i++)
722 if (!ctables_category_equal (&a->cats[i], &b->cats[i]))
728 /* Chi-square test (SIGTEST). */
736 /* Pairwise comparison test (COMPARETEST). */
737 struct ctables_pairwise
739 enum { PROP, MEAN } type;
742 bool meansvariance_allcats;
744 enum { BONFERRONI = 1, BH } adjust;
768 struct variable *var;
770 struct ctables_summary_spec_set specs[N_CSVS];
774 struct ctables_axis *subs[2];
777 struct msg_location *loc;
780 static void ctables_axis_destroy (struct ctables_axis *);
789 enum ctables_function_availability
791 CTFA_ALL, /* Any variables. */
792 CTFA_SCALE, /* Only scale variables, totals, and subtotals. */
793 CTFA_MRSETS, /* Only multiple-response sets */
796 struct ctables_summary_spec
798 enum ctables_summary_function function;
799 double percentile; /* CTSF_PTILE only. */
802 struct fmt_spec format;
803 bool is_ctables_format; /* Is 'format' one of CTEF_*? */
810 ctables_summary_spec_clone (struct ctables_summary_spec *dst,
811 const struct ctables_summary_spec *src)
814 dst->label = xstrdup_if_nonnull (src->label);
818 ctables_summary_spec_uninit (struct ctables_summary_spec *s)
825 ctables_summary_spec_set_clone (struct ctables_summary_spec_set *dst,
826 const struct ctables_summary_spec_set *src)
828 struct ctables_summary_spec *specs = xnmalloc (src->n, sizeof *specs);
829 for (size_t i = 0; i < src->n; i++)
830 ctables_summary_spec_clone (&specs[i], &src->specs[i]);
832 *dst = (struct ctables_summary_spec_set) {
837 .is_scale = src->is_scale,
842 ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *set)
844 for (size_t i = 0; i < set->n; i++)
845 ctables_summary_spec_uninit (&set->specs[i]);
850 parse_col_width (struct lexer *lexer, const char *name, double *width)
852 lex_match (lexer, T_EQUALS);
853 if (lex_match_id (lexer, "DEFAULT"))
855 else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
857 *width = lex_number (lexer);
867 parse_bool (struct lexer *lexer, bool *b)
869 if (lex_match_id (lexer, "NO"))
871 else if (lex_match_id (lexer, "YES"))
875 lex_error_expecting (lexer, "YES", "NO");
881 static enum ctables_function_availability
882 ctables_function_availability (enum ctables_summary_function f)
884 static enum ctables_function_availability availability[] = {
885 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
890 return availability[f];
894 ctables_summary_function_is_count (enum ctables_summary_function f)
900 case CTSF_ROWPCT_COUNT:
901 case CTSF_COLPCT_COUNT:
902 case CTSF_TABLEPCT_COUNT:
903 case CTSF_SUBTABLEPCT_COUNT:
904 case CTSF_LAYERPCT_COUNT:
905 case CTSF_LAYERROWPCT_COUNT:
906 case CTSF_LAYERCOLPCT_COUNT:
908 case CTSF_UROWPCT_COUNT:
909 case CTSF_UCOLPCT_COUNT:
910 case CTSF_UTABLEPCT_COUNT:
911 case CTSF_USUBTABLEPCT_COUNT:
912 case CTSF_ULAYERPCT_COUNT:
913 case CTSF_ULAYERROWPCT_COUNT:
914 case CTSF_ULAYERCOLPCT_COUNT:
917 case CTSF_ROWPCT_VALIDN:
918 case CTSF_COLPCT_VALIDN:
919 case CTSF_TABLEPCT_VALIDN:
920 case CTSF_SUBTABLEPCT_VALIDN:
921 case CTSF_LAYERPCT_VALIDN:
922 case CTSF_LAYERROWPCT_VALIDN:
923 case CTSF_LAYERCOLPCT_VALIDN:
924 case CTSF_ROWPCT_TOTALN:
925 case CTSF_COLPCT_TOTALN:
926 case CTSF_TABLEPCT_TOTALN:
927 case CTSF_SUBTABLEPCT_TOTALN:
928 case CTSF_LAYERPCT_TOTALN:
929 case CTSF_LAYERROWPCT_TOTALN:
930 case CTSF_LAYERCOLPCT_TOTALN:
947 case CTSF_ROWPCT_SUM:
948 case CTSF_COLPCT_SUM:
949 case CTSF_TABLEPCT_SUM:
950 case CTSF_SUBTABLEPCT_SUM:
951 case CTSF_LAYERPCT_SUM:
952 case CTSF_LAYERROWPCT_SUM:
953 case CTSF_LAYERCOLPCT_SUM:
954 case CTSF_UROWPCT_VALIDN:
955 case CTSF_UCOLPCT_VALIDN:
956 case CTSF_UTABLEPCT_VALIDN:
957 case CTSF_USUBTABLEPCT_VALIDN:
958 case CTSF_ULAYERPCT_VALIDN:
959 case CTSF_ULAYERROWPCT_VALIDN:
960 case CTSF_ULAYERCOLPCT_VALIDN:
961 case CTSF_UROWPCT_TOTALN:
962 case CTSF_UCOLPCT_TOTALN:
963 case CTSF_UTABLEPCT_TOTALN:
964 case CTSF_USUBTABLEPCT_TOTALN:
965 case CTSF_ULAYERPCT_TOTALN:
966 case CTSF_ULAYERROWPCT_TOTALN:
967 case CTSF_ULAYERCOLPCT_TOTALN:
979 case CTSF_UROWPCT_SUM:
980 case CTSF_UCOLPCT_SUM:
981 case CTSF_UTABLEPCT_SUM:
982 case CTSF_USUBTABLEPCT_SUM:
983 case CTSF_ULAYERPCT_SUM:
984 case CTSF_ULAYERROWPCT_SUM:
985 case CTSF_ULAYERCOLPCT_SUM:
993 parse_ctables_summary_function (struct lexer *lexer,
994 enum ctables_summary_function *f)
998 enum ctables_summary_function function;
999 struct substring name;
1001 static struct pair names[] = {
1002 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
1003 { ENUM, SS_LITERAL_INITIALIZER (NAME) },
1006 /* The .COUNT suffix may be omitted. */
1007 S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
1008 S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
1009 S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
1010 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
1011 S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
1012 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
1013 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
1017 if (!lex_force_id (lexer))
1020 for (size_t i = 0; i < sizeof names / sizeof *names; i++)
1021 if (ss_equals_case (names[i].name, lex_tokss (lexer)))
1023 *f = names[i].function;
1028 lex_error (lexer, _("Expecting summary function name."));
1033 ctables_axis_destroy (struct ctables_axis *axis)
1041 for (size_t i = 0; i < N_CSVS; i++)
1042 ctables_summary_spec_set_uninit (&axis->specs[i]);
1047 ctables_axis_destroy (axis->subs[0]);
1048 ctables_axis_destroy (axis->subs[1]);
1051 msg_location_destroy (axis->loc);
1055 static struct ctables_axis *
1056 ctables_axis_new_nonterminal (enum ctables_axis_op op,
1057 struct ctables_axis *sub0,
1058 struct ctables_axis *sub1,
1059 struct lexer *lexer, int start_ofs)
1061 struct ctables_axis *axis = xmalloc (sizeof *axis);
1062 *axis = (struct ctables_axis) {
1064 .subs = { sub0, sub1 },
1065 .loc = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
1070 struct ctables_axis_parse_ctx
1072 struct lexer *lexer;
1073 struct dictionary *dict;
1075 struct ctables_table *t;
1078 static struct fmt_spec
1079 ctables_summary_default_format (enum ctables_summary_function function,
1080 const struct variable *var)
1082 static const enum ctables_format default_formats[] = {
1083 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
1087 switch (default_formats[function])
1090 return (struct fmt_spec) { .type = FMT_F, .w = 40 };
1093 return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
1096 return *var_get_print_format (var);
1103 static struct pivot_value *
1104 ctables_summary_label (const struct ctables_summary_spec *spec, double cilevel)
1108 static const char *default_labels[] = {
1109 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
1114 return (spec->function == CTSF_PTILE
1115 ? pivot_value_new_text_format (N_("Percentile %.2f"),
1117 : pivot_value_new_text (default_labels[spec->function]));
1121 struct substring in = ss_cstr (spec->label);
1122 struct substring target = ss_cstr (")CILEVEL");
1124 struct string out = DS_EMPTY_INITIALIZER;
1127 size_t chunk = ss_find_substring (in, target);
1128 ds_put_substring (&out, ss_head (in, chunk));
1129 ss_advance (&in, chunk);
1131 return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
1133 ss_advance (&in, target.length);
1134 ds_put_format (&out, "%g", cilevel);
1140 ctables_summary_function_name (enum ctables_summary_function function)
1142 static const char *names[] = {
1143 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = NAME,
1147 return names[function];
1151 add_summary_spec (struct ctables_axis *axis,
1152 enum ctables_summary_function function, double percentile,
1153 const char *label, const struct fmt_spec *format,
1154 bool is_ctables_format, const struct msg_location *loc,
1155 enum ctables_summary_variant sv)
1157 if (axis->op == CTAO_VAR)
1159 const char *function_name = ctables_summary_function_name (function);
1160 const char *var_name = var_get_name (axis->var);
1161 switch (ctables_function_availability (function))
1164 msg_at (SE, loc, _("Summary function %s applies only to multiple "
1165 "response sets."), function_name);
1166 msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
1175 _("Summary function %s applies only to scale variables."),
1177 msg_at (SN, axis->loc, _("'%s' is not a scale variable."),
1188 struct ctables_summary_spec_set *set = &axis->specs[sv];
1189 if (set->n >= set->allocated)
1190 set->specs = x2nrealloc (set->specs, &set->allocated,
1191 sizeof *set->specs);
1193 struct ctables_summary_spec *dst = &set->specs[set->n++];
1194 *dst = (struct ctables_summary_spec) {
1195 .function = function,
1196 .percentile = percentile,
1197 .label = xstrdup_if_nonnull (label),
1198 .format = (format ? *format
1199 : ctables_summary_default_format (function, axis->var)),
1200 .is_ctables_format = is_ctables_format,
1206 for (size_t i = 0; i < 2; i++)
1207 if (!add_summary_spec (axis->subs[i], function, percentile, label,
1208 format, is_ctables_format, loc, sv))
1214 static struct ctables_axis *ctables_axis_parse_stack (
1215 struct ctables_axis_parse_ctx *);
1218 static struct ctables_axis *
1219 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
1221 if (lex_match (ctx->lexer, T_LPAREN))
1223 struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
1224 if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
1226 ctables_axis_destroy (sub);
1232 if (!lex_force_id (ctx->lexer))
1235 int start_ofs = lex_ofs (ctx->lexer);
1236 struct variable *var = parse_variable (ctx->lexer, ctx->dict);
1240 struct ctables_axis *axis = xmalloc (sizeof *axis);
1241 *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
1243 /* XXX should figure out default measures by reading data */
1244 axis->scale = (lex_match_phrase (ctx->lexer, "[S]") ? true
1245 : lex_match_phrase (ctx->lexer, "[C]") ? false
1246 : var_get_measure (var) == MEASURE_SCALE);
1247 axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
1248 lex_ofs (ctx->lexer) - 1);
1249 if (axis->scale && var_is_alpha (var))
1251 msg_at (SE, axis->loc, _("Cannot use string variable %s as a scale "
1253 var_get_name (var));
1254 ctables_axis_destroy (axis);
1262 has_digit (const char *s)
1264 return s[strcspn (s, "0123456789")] != '\0';
1268 parse_ctables_format_specifier (struct lexer *lexer, struct fmt_spec *format,
1269 bool *is_ctables_format)
1271 char type[FMT_TYPE_LEN_MAX + 1];
1272 if (!parse_abstract_format_specifier__ (lexer, type, &format->w, &format->d))
1275 if (!strcasecmp (type, "NEGPAREN"))
1276 format->type = CTEF_NEGPAREN;
1277 else if (!strcasecmp (type, "NEQUAL"))
1278 format->type = CTEF_NEQUAL;
1279 else if (!strcasecmp (type, "PAREN"))
1280 format->type = CTEF_PAREN;
1281 else if (!strcasecmp (type, "PCTPAREN"))
1282 format->type = CTEF_PCTPAREN;
1285 *is_ctables_format = false;
1286 return (parse_format_specifier (lexer, format)
1287 && fmt_check_output (format)
1288 && fmt_check_type_compat (format, VAL_NUMERIC));
1293 msg (SE, _("Output format %s requires width 2 or greater."), type);
1296 else if (format->d > format->w - 1)
1298 msg (SE, _("Output format %s requires width greater than decimals."),
1304 *is_ctables_format = true;
1309 static struct ctables_axis *
1310 ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
1312 struct ctables_axis *sub = ctables_axis_parse_primary (ctx);
1313 if (!sub || !lex_match (ctx->lexer, T_LBRACK))
1316 enum ctables_summary_variant sv = CSV_CELL;
1319 int start_ofs = lex_ofs (ctx->lexer);
1321 /* Parse function. */
1322 enum ctables_summary_function function;
1323 if (!parse_ctables_summary_function (ctx->lexer, &function))
1326 /* Parse percentile. */
1327 double percentile = 0;
1328 if (function == CTSF_PTILE)
1330 if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
1332 percentile = lex_number (ctx->lexer);
1333 lex_get (ctx->lexer);
1338 if (lex_is_string (ctx->lexer))
1340 label = ss_xstrdup (lex_tokss (ctx->lexer));
1341 lex_get (ctx->lexer);
1345 struct fmt_spec format;
1346 const struct fmt_spec *formatp;
1347 bool is_ctables_format = false;
1348 if (lex_token (ctx->lexer) == T_ID
1349 && has_digit (lex_tokcstr (ctx->lexer)))
1351 if (!parse_ctables_format_specifier (ctx->lexer, &format,
1352 &is_ctables_format))
1362 struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
1363 lex_ofs (ctx->lexer) - 1);
1364 add_summary_spec (sub, function, percentile, label, formatp,
1365 is_ctables_format, loc, sv);
1367 msg_location_destroy (loc);
1369 lex_match (ctx->lexer, T_COMMA);
1370 if (sv == CSV_CELL && lex_match_id (ctx->lexer, "TOTALS"))
1372 if (!lex_force_match (ctx->lexer, T_LBRACK))
1376 else if (lex_match (ctx->lexer, T_RBRACK))
1378 if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
1385 ctables_axis_destroy (sub);
1389 static const struct ctables_axis *
1390 find_scale (const struct ctables_axis *axis)
1394 else if (axis->op == CTAO_VAR)
1395 return axis->scale ? axis : NULL;
1398 for (size_t i = 0; i < 2; i++)
1400 const struct ctables_axis *scale = find_scale (axis->subs[i]);
1408 static const struct ctables_axis *
1409 find_categorical_summary_spec (const struct ctables_axis *axis)
1413 else if (axis->op == CTAO_VAR)
1414 return !axis->scale && axis->specs[CSV_CELL].n ? axis : NULL;
1417 for (size_t i = 0; i < 2; i++)
1419 const struct ctables_axis *sum
1420 = find_categorical_summary_spec (axis->subs[i]);
1428 static struct ctables_axis *
1429 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
1431 int start_ofs = lex_ofs (ctx->lexer);
1432 struct ctables_axis *lhs = ctables_axis_parse_postfix (ctx);
1436 while (lex_match (ctx->lexer, T_GT))
1438 struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx);
1442 struct ctables_axis *nest = ctables_axis_new_nonterminal (
1443 CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
1445 const struct ctables_axis *outer_scale = find_scale (lhs);
1446 const struct ctables_axis *inner_scale = find_scale (rhs);
1447 if (outer_scale && inner_scale)
1449 msg_at (SE, nest->loc, _("Cannot nest scale variables."));
1450 msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
1451 msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
1452 ctables_axis_destroy (nest);
1456 const struct ctables_axis *outer_sum = find_categorical_summary_spec (lhs);
1459 msg_at (SE, nest->loc,
1460 _("Summaries may only be requested for categorical variables "
1461 "at the innermost nesting level."));
1462 msg_at (SN, outer_sum->loc,
1463 _("This outer categorical variable has a summary."));
1464 ctables_axis_destroy (nest);
1474 static struct ctables_axis *
1475 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
1477 int start_ofs = lex_ofs (ctx->lexer);
1478 struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
1482 while (lex_match (ctx->lexer, T_PLUS))
1484 struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
1488 lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
1489 ctx->lexer, start_ofs);
1496 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
1497 struct ctables *ct, struct ctables_table *t,
1498 enum pivot_axis_type a)
1500 if (lex_token (lexer) == T_BY
1501 || lex_token (lexer) == T_SLASH
1502 || lex_token (lexer) == T_ENDCMD)
1505 struct ctables_axis_parse_ctx ctx = {
1511 t->axes[a] = ctables_axis_parse_stack (&ctx);
1512 return t->axes[a] != NULL;
1516 ctables_chisq_destroy (struct ctables_chisq *chisq)
1522 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
1528 ctables_table_destroy (struct ctables_table *t)
1533 for (size_t i = 0; i < t->n_categories; i++)
1534 ctables_categories_unref (t->categories[i]);
1535 free (t->categories);
1537 ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
1538 ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
1539 ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
1543 ctables_chisq_destroy (t->chisq);
1544 ctables_pairwise_destroy (t->pairwise);
1549 ctables_destroy (struct ctables *ct)
1554 pivot_table_look_unref (ct->look);
1558 for (size_t i = 0; i < ct->n_tables; i++)
1559 ctables_table_destroy (ct->tables[i]);
1564 static struct ctables_category
1565 cct_nrange (double low, double high)
1567 return (struct ctables_category) {
1569 .nrange = { low, high }
1573 static struct ctables_category
1574 cct_srange (struct substring low, struct substring high)
1576 return (struct ctables_category) {
1578 .srange = { low, high }
1583 ctables_table_parse_subtotal (struct lexer *lexer, bool hide_subcategories,
1584 struct ctables_category *cat)
1587 if (lex_match (lexer, T_EQUALS))
1589 if (!lex_force_string (lexer))
1592 total_label = ss_xstrdup (lex_tokss (lexer));
1596 total_label = xstrdup (_("Subtotal"));
1598 *cat = (struct ctables_category) {
1599 .type = CCT_SUBTOTAL,
1600 .hide_subcategories = hide_subcategories,
1601 .total_label = total_label
1606 static struct substring
1607 parse_substring (struct lexer *lexer, struct dictionary *dict)
1609 struct substring s = recode_substring_pool (
1610 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
1611 ss_rtrim (&s, ss_cstr (" "));
1617 ctables_table_parse_explicit_category (struct lexer *lexer,
1618 struct dictionary *dict,
1620 struct ctables_category *cat)
1622 if (lex_match_id (lexer, "OTHERNM"))
1623 *cat = (struct ctables_category) { .type = CCT_OTHERNM };
1624 else if (lex_match_id (lexer, "MISSING"))
1625 *cat = (struct ctables_category) { .type = CCT_MISSING };
1626 else if (lex_match_id (lexer, "SUBTOTAL"))
1627 return ctables_table_parse_subtotal (lexer, false, cat);
1628 else if (lex_match_id (lexer, "HSUBTOTAL"))
1629 return ctables_table_parse_subtotal (lexer, true, cat);
1630 else if (lex_match_id (lexer, "LO"))
1632 if (!lex_force_match_id (lexer, "THRU"))
1634 if (lex_is_string (lexer))
1636 struct substring sr0 = { .string = NULL };
1637 struct substring sr1 = parse_substring (lexer, dict);
1638 *cat = cct_srange (sr0, sr1);
1640 else if (lex_force_num (lexer))
1642 *cat = cct_nrange (-DBL_MAX, lex_number (lexer));
1648 else if (lex_is_number (lexer))
1650 double number = lex_number (lexer);
1652 if (lex_match_id (lexer, "THRU"))
1654 if (lex_match_id (lexer, "HI"))
1655 *cat = cct_nrange (number, DBL_MAX);
1658 if (!lex_force_num (lexer))
1660 *cat = cct_nrange (number, lex_number (lexer));
1665 *cat = (struct ctables_category) {
1670 else if (lex_is_string (lexer))
1672 struct substring s = parse_substring (lexer, dict);
1673 if (lex_match_id (lexer, "THRU"))
1675 if (lex_match_id (lexer, "HI"))
1677 struct substring sr1 = { .string = NULL };
1678 *cat = cct_srange (s, sr1);
1682 if (!lex_force_string (lexer))
1684 struct substring sr1 = parse_substring (lexer, dict);
1685 *cat = cct_srange (s, sr1);
1689 *cat = (struct ctables_category) { .type = CCT_STRING, .string = s };
1691 else if (lex_match (lexer, T_AND))
1693 if (!lex_force_id (lexer))
1695 struct ctables_postcompute *pc = ctables_find_postcompute (
1696 ct, lex_tokcstr (lexer));
1699 struct msg_location *loc = lex_get_location (lexer, -1, 0);
1700 msg_at (SE, loc, _("Unknown postcompute &%s."),
1701 lex_tokcstr (lexer));
1702 msg_location_destroy (loc);
1707 *cat = (struct ctables_category) { .type = CCT_POSTCOMPUTE, .pc = pc };
1711 lex_error (lexer, NULL);
1718 static struct ctables_category *
1719 ctables_find_category_for_postcompute (const struct ctables_categories *cats,
1720 const struct ctables_pcexpr *e)
1722 struct ctables_category *best = NULL;
1723 size_t n_subtotals = 0;
1724 for (size_t i = 0; i < cats->n_cats; i++)
1726 struct ctables_category *cat = &cats->cats[i];
1729 case CTPO_CAT_NUMBER:
1730 if (cat->type == CCT_NUMBER && cat->number == e->number)
1734 case CTPO_CAT_STRING:
1735 if (cat->type == CCT_STRING && ss_equals (cat->string, e->string))
1739 case CTPO_CAT_NRANGE:
1740 if (cat->type == CCT_NRANGE
1741 && cat->nrange[0] == e->nrange[0]
1742 && cat->nrange[1] == e->nrange[1])
1746 case CTPO_CAT_SRANGE:
1747 if (cat->type == CCT_SRANGE
1748 && nullable_substring_equal (&cat->srange[0], &e->srange[0])
1749 && nullable_substring_equal (&cat->srange[1], &e->srange[1]))
1753 case CTPO_CAT_MISSING:
1754 if (cat->type == CCT_MISSING)
1758 case CTPO_CAT_OTHERNM:
1759 if (cat->type == CCT_OTHERNM)
1763 case CTPO_CAT_SUBTOTAL:
1764 if (cat->type == CCT_SUBTOTAL)
1767 if (e->subtotal_index == n_subtotals)
1769 else if (e->subtotal_index == 0)
1774 case CTPO_CAT_TOTAL:
1775 if (cat->type == CCT_TOTAL)
1789 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0 && n_subtotals > 1)
1795 ctables_recursive_check_postcompute (const struct ctables_pcexpr *e,
1796 struct ctables_category *pc_cat,
1797 const struct ctables_categories *cats,
1798 const struct msg_location *cats_location)
1802 case CTPO_CAT_NUMBER:
1803 case CTPO_CAT_STRING:
1804 case CTPO_CAT_NRANGE:
1805 case CTPO_CAT_MISSING:
1806 case CTPO_CAT_OTHERNM:
1807 case CTPO_CAT_SUBTOTAL:
1808 case CTPO_CAT_TOTAL:
1810 struct ctables_category *cat = ctables_find_category_for_postcompute (
1814 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0)
1816 size_t n_subtotals = 0;
1817 for (size_t i = 0; i < cats->n_cats; i++)
1818 n_subtotals += cats->cats[i].type == CCT_SUBTOTAL;
1819 if (n_subtotals > 1)
1821 msg_at (SE, cats_location,
1822 ngettext ("These categories include %zu instance "
1823 "of SUBTOTAL or HSUBTOTAL, so references "
1824 "from computed categories must refer to "
1825 "subtotals by position.",
1826 "These categories include %zu instances "
1827 "of SUBTOTAL or HSUBTOTAL, so references "
1828 "from computed categories must refer to "
1829 "subtotals by position.",
1832 msg_at (SN, e->location,
1833 _("This is the reference that lacks a position."));
1838 msg_at (SE, pc_cat->location,
1839 _("Computed category &%s references a category not included "
1840 "in the category list."),
1842 msg_at (SN, e->location, _("This is the missing category."));
1843 msg_at (SN, cats_location,
1844 _("To fix the problem, add the missing category to the "
1845 "list of categories here."));
1848 if (pc_cat->pc->hide_source_cats)
1862 for (size_t i = 0; i < 2; i++)
1863 if (e->subs[i] && !ctables_recursive_check_postcompute (
1864 e->subs[i], pc_cat, cats, cats_location))
1874 parse_category_string (const struct ctables_category *cat,
1875 struct substring s, struct dictionary *dict,
1876 enum fmt_type format, double *n)
1879 char *error = data_in (s, dict_get_encoding (dict), format,
1880 settings_get_fmt_settings (), &v, 0, NULL);
1883 msg_at (SE, cat->location,
1884 _("Failed to parse category specification as format %s: %s."),
1885 fmt_name (format), error);
1895 all_strings (struct variable **vars, size_t n_vars,
1896 const struct ctables_category *cat)
1898 for (size_t j = 0; j < n_vars; j++)
1899 if (var_is_numeric (vars[j]))
1901 msg_at (SE, cat->location,
1902 _("This category specification may be applied only to string "
1903 "variables, but this subcommand tries to apply it to "
1904 "numeric variable %s."),
1905 var_get_name (vars[j]));
1912 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
1913 struct ctables *ct, struct ctables_table *t)
1915 if (!lex_match_id (lexer, "VARIABLES"))
1917 lex_match (lexer, T_EQUALS);
1919 struct variable **vars;
1921 if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
1924 const struct fmt_spec *common_format = var_get_print_format (vars[0]);
1925 for (size_t i = 1; i < n_vars; i++)
1927 const struct fmt_spec *f = var_get_print_format (vars[i]);
1928 if (f->type != common_format->type)
1930 common_format = NULL;
1936 && (fmt_get_category (common_format->type)
1937 & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)));
1939 struct ctables_categories *c = xmalloc (sizeof *c);
1940 *c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
1941 for (size_t i = 0; i < n_vars; i++)
1943 struct ctables_categories **cp
1944 = &t->categories[var_get_dict_index (vars[i])];
1945 ctables_categories_unref (*cp);
1949 size_t allocated_cats = 0;
1950 if (lex_match (lexer, T_LBRACK))
1952 int cats_start_ofs = lex_ofs (lexer);
1955 if (c->n_cats >= allocated_cats)
1956 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
1958 int start_ofs = lex_ofs (lexer);
1959 struct ctables_category *cat = &c->cats[c->n_cats];
1960 if (!ctables_table_parse_explicit_category (lexer, dict, ct, cat))
1962 cat->location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
1965 lex_match (lexer, T_COMMA);
1967 while (!lex_match (lexer, T_RBRACK));
1969 struct msg_location *cats_location
1970 = lex_ofs_location (lexer, cats_start_ofs, lex_ofs (lexer) - 1);
1971 for (size_t i = 0; i < c->n_cats; i++)
1973 struct ctables_category *cat = &c->cats[i];
1976 case CCT_POSTCOMPUTE:
1977 if (!ctables_recursive_check_postcompute (cat->pc->expr, cat,
1984 for (size_t j = 0; j < n_vars; j++)
1985 if (var_is_alpha (vars[j]))
1987 msg_at (SE, cat->location,
1988 _("This category specification may be applied "
1989 "only to numeric variables, but this "
1990 "subcommand tries to apply it to string "
1992 var_get_name (vars[j]));
2001 if (!parse_category_string (cat, cat->string, dict,
2002 common_format->type, &n))
2005 ss_dealloc (&cat->string);
2007 cat->type = CCT_NUMBER;
2010 else if (!all_strings (vars, n_vars, cat))
2019 if (!cat->srange[0].string)
2021 else if (!parse_category_string (cat, cat->srange[0], dict,
2022 common_format->type, &n[0]))
2025 if (!cat->srange[1].string)
2027 else if (!parse_category_string (cat, cat->srange[1], dict,
2028 common_format->type, &n[1]))
2031 ss_dealloc (&cat->srange[0]);
2032 ss_dealloc (&cat->srange[1]);
2034 cat->type = CCT_NRANGE;
2035 cat->nrange[0] = n[0];
2036 cat->nrange[1] = n[1];
2038 else if (!all_strings (vars, n_vars, cat))
2049 case CCT_EXCLUDED_MISSING:
2055 struct ctables_category cat = {
2057 .include_missing = false,
2058 .sort_ascending = true,
2060 bool show_totals = false;
2061 char *total_label = NULL;
2062 bool totals_before = false;
2063 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
2065 if (!c->n_cats && lex_match_id (lexer, "ORDER"))
2067 lex_match (lexer, T_EQUALS);
2068 if (lex_match_id (lexer, "A"))
2069 cat.sort_ascending = true;
2070 else if (lex_match_id (lexer, "D"))
2071 cat.sort_ascending = false;
2074 lex_error_expecting (lexer, "A", "D");
2078 else if (!c->n_cats && lex_match_id (lexer, "KEY"))
2080 lex_match (lexer, T_EQUALS);
2081 if (lex_match_id (lexer, "VALUE"))
2082 cat.type = CCT_VALUE;
2083 else if (lex_match_id (lexer, "LABEL"))
2084 cat.type = CCT_LABEL;
2087 cat.type = CCT_FUNCTION;
2088 if (!parse_ctables_summary_function (lexer, &cat.sort_function))
2091 if (lex_match (lexer, T_LPAREN))
2093 cat.sort_var = parse_variable (lexer, dict);
2097 if (cat.sort_function == CTSF_PTILE)
2099 lex_match (lexer, T_COMMA);
2100 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
2102 cat.percentile = lex_number (lexer);
2106 if (!lex_force_match (lexer, T_RPAREN))
2109 else if (ctables_function_availability (cat.sort_function)
2112 bool UNUSED b = lex_force_match (lexer, T_LPAREN);
2117 else if (!c->n_cats && lex_match_id (lexer, "MISSING"))
2119 lex_match (lexer, T_EQUALS);
2120 if (lex_match_id (lexer, "INCLUDE"))
2121 cat.include_missing = true;
2122 else if (lex_match_id (lexer, "EXCLUDE"))
2123 cat.include_missing = false;
2126 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
2130 else if (lex_match_id (lexer, "TOTAL"))
2132 lex_match (lexer, T_EQUALS);
2133 if (!parse_bool (lexer, &show_totals))
2136 else if (lex_match_id (lexer, "LABEL"))
2138 lex_match (lexer, T_EQUALS);
2139 if (!lex_force_string (lexer))
2142 total_label = ss_xstrdup (lex_tokss (lexer));
2145 else if (lex_match_id (lexer, "POSITION"))
2147 lex_match (lexer, T_EQUALS);
2148 if (lex_match_id (lexer, "BEFORE"))
2149 totals_before = true;
2150 else if (lex_match_id (lexer, "AFTER"))
2151 totals_before = false;
2154 lex_error_expecting (lexer, "BEFORE", "AFTER");
2158 else if (lex_match_id (lexer, "EMPTY"))
2160 lex_match (lexer, T_EQUALS);
2161 if (lex_match_id (lexer, "INCLUDE"))
2162 c->show_empty = true;
2163 else if (lex_match_id (lexer, "EXCLUDE"))
2164 c->show_empty = false;
2167 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
2174 lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
2175 "TOTAL", "LABEL", "POSITION", "EMPTY");
2177 lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
2184 if (c->n_cats >= allocated_cats)
2185 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2186 c->cats[c->n_cats++] = cat;
2191 if (c->n_cats >= allocated_cats)
2192 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2194 struct ctables_category *totals;
2197 insert_element (c->cats, c->n_cats, sizeof *c->cats, 0);
2198 totals = &c->cats[0];
2201 totals = &c->cats[c->n_cats];
2204 *totals = (struct ctables_category) {
2206 .total_label = total_label ? total_label : xstrdup (_("Total")),
2210 struct ctables_category *subtotal = NULL;
2211 for (size_t i = totals_before ? 0 : c->n_cats;
2212 totals_before ? i < c->n_cats : i-- > 0;
2213 totals_before ? i++ : 0)
2215 struct ctables_category *cat = &c->cats[i];
2224 cat->subtotal = subtotal;
2227 case CCT_POSTCOMPUTE:
2238 case CCT_EXCLUDED_MISSING:
2247 ctables_nest_uninit (struct ctables_nest *nest)
2254 ctables_stack_uninit (struct ctables_stack *stack)
2258 for (size_t i = 0; i < stack->n; i++)
2259 ctables_nest_uninit (&stack->nests[i]);
2260 free (stack->nests);
2264 static struct ctables_stack
2265 nest_fts (struct ctables_stack s0, struct ctables_stack s1)
2272 struct ctables_stack stack = { .nests = xnmalloc (s0.n, s1.n * sizeof *stack.nests) };
2273 for (size_t i = 0; i < s0.n; i++)
2274 for (size_t j = 0; j < s1.n; j++)
2276 const struct ctables_nest *a = &s0.nests[i];
2277 const struct ctables_nest *b = &s1.nests[j];
2279 size_t allocate = a->n + b->n;
2280 struct variable **vars = xnmalloc (allocate, sizeof *vars);
2281 enum pivot_axis_type *axes = xnmalloc (allocate, sizeof *axes);
2283 for (size_t k = 0; k < a->n; k++)
2284 vars[n++] = a->vars[k];
2285 for (size_t k = 0; k < b->n; k++)
2286 vars[n++] = b->vars[k];
2287 assert (n == allocate);
2289 const struct ctables_nest *summary_src;
2290 if (!a->specs[CSV_CELL].var)
2292 else if (!b->specs[CSV_CELL].var)
2297 struct ctables_nest *new = &stack.nests[stack.n++];
2298 *new = (struct ctables_nest) {
2300 .scale_idx = (a->scale_idx != SIZE_MAX ? a->scale_idx
2301 : b->scale_idx != SIZE_MAX ? a->n + b->scale_idx
2305 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2306 ctables_summary_spec_set_clone (&new->specs[sv], &summary_src->specs[sv]);
2308 ctables_stack_uninit (&s0);
2309 ctables_stack_uninit (&s1);
2313 static struct ctables_stack
2314 stack_fts (struct ctables_stack s0, struct ctables_stack s1)
2316 struct ctables_stack stack = { .nests = xnmalloc (s0.n + s1.n, sizeof *stack.nests) };
2317 for (size_t i = 0; i < s0.n; i++)
2318 stack.nests[stack.n++] = s0.nests[i];
2319 for (size_t i = 0; i < s1.n; i++)
2321 stack.nests[stack.n] = s1.nests[i];
2322 stack.nests[stack.n].group_head += s0.n;
2325 assert (stack.n == s0.n + s1.n);
2331 static struct ctables_stack
2332 var_fts (const struct ctables_axis *a)
2334 struct variable **vars = xmalloc (sizeof *vars);
2337 struct ctables_nest *nest = xmalloc (sizeof *nest);
2338 *nest = (struct ctables_nest) {
2341 .scale_idx = a->scale ? 0 : SIZE_MAX,
2343 if (a->specs[CSV_CELL].n || a->scale)
2344 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2346 ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
2347 nest->specs[sv].var = a->var;
2348 nest->specs[sv].is_scale = a->scale;
2350 return (struct ctables_stack) { .nests = nest, .n = 1 };
2353 static struct ctables_stack
2354 enumerate_fts (enum pivot_axis_type axis_type, const struct ctables_axis *a)
2357 return (struct ctables_stack) { .n = 0 };
2365 return stack_fts (enumerate_fts (axis_type, a->subs[0]),
2366 enumerate_fts (axis_type, a->subs[1]));
2369 /* This should consider any of the scale variables found in the result to
2370 be linked to each other listwise for SMISSING=LISTWISE. */
2371 return nest_fts (enumerate_fts (axis_type, a->subs[0]),
2372 enumerate_fts (axis_type, a->subs[1]));
2378 union ctables_summary
2380 /* COUNT, VALIDN, TOTALN. */
2383 /* MINIMUM, MAXIMUM, RANGE. */
2390 /* MEAN, SEMEAN, STDDEV, SUM, VARIANCE, *.SUM. */
2391 struct moments1 *moments;
2393 /* MEDIAN, MODE, PTILE. */
2396 struct casewriter *writer;
2401 /* XXX multiple response */
2405 ctables_summary_init (union ctables_summary *s,
2406 const struct ctables_summary_spec *ss)
2408 switch (ss->function)
2412 case CTSF_ROWPCT_COUNT:
2413 case CTSF_COLPCT_COUNT:
2414 case CTSF_TABLEPCT_COUNT:
2415 case CTSF_SUBTABLEPCT_COUNT:
2416 case CTSF_LAYERPCT_COUNT:
2417 case CTSF_LAYERROWPCT_COUNT:
2418 case CTSF_LAYERCOLPCT_COUNT:
2419 case CTSF_ROWPCT_VALIDN:
2420 case CTSF_COLPCT_VALIDN:
2421 case CTSF_TABLEPCT_VALIDN:
2422 case CTSF_SUBTABLEPCT_VALIDN:
2423 case CTSF_LAYERPCT_VALIDN:
2424 case CTSF_LAYERROWPCT_VALIDN:
2425 case CTSF_LAYERCOLPCT_VALIDN:
2426 case CTSF_ROWPCT_TOTALN:
2427 case CTSF_COLPCT_TOTALN:
2428 case CTSF_TABLEPCT_TOTALN:
2429 case CTSF_SUBTABLEPCT_TOTALN:
2430 case CTSF_LAYERPCT_TOTALN:
2431 case CTSF_LAYERROWPCT_TOTALN:
2432 case CTSF_LAYERCOLPCT_TOTALN:
2439 case CTSF_UROWPCT_COUNT:
2440 case CTSF_UCOLPCT_COUNT:
2441 case CTSF_UTABLEPCT_COUNT:
2442 case CTSF_USUBTABLEPCT_COUNT:
2443 case CTSF_ULAYERPCT_COUNT:
2444 case CTSF_ULAYERROWPCT_COUNT:
2445 case CTSF_ULAYERCOLPCT_COUNT:
2446 case CTSF_UROWPCT_VALIDN:
2447 case CTSF_UCOLPCT_VALIDN:
2448 case CTSF_UTABLEPCT_VALIDN:
2449 case CTSF_USUBTABLEPCT_VALIDN:
2450 case CTSF_ULAYERPCT_VALIDN:
2451 case CTSF_ULAYERROWPCT_VALIDN:
2452 case CTSF_ULAYERCOLPCT_VALIDN:
2453 case CTSF_UROWPCT_TOTALN:
2454 case CTSF_UCOLPCT_TOTALN:
2455 case CTSF_UTABLEPCT_TOTALN:
2456 case CTSF_USUBTABLEPCT_TOTALN:
2457 case CTSF_ULAYERPCT_TOTALN:
2458 case CTSF_ULAYERROWPCT_TOTALN:
2459 case CTSF_ULAYERCOLPCT_TOTALN:
2469 s->min = s->max = SYSMIS;
2477 case CTSF_ROWPCT_SUM:
2478 case CTSF_COLPCT_SUM:
2479 case CTSF_TABLEPCT_SUM:
2480 case CTSF_SUBTABLEPCT_SUM:
2481 case CTSF_LAYERPCT_SUM:
2482 case CTSF_LAYERROWPCT_SUM:
2483 case CTSF_LAYERCOLPCT_SUM:
2488 case CTSF_UVARIANCE:
2489 case CTSF_UROWPCT_SUM:
2490 case CTSF_UCOLPCT_SUM:
2491 case CTSF_UTABLEPCT_SUM:
2492 case CTSF_USUBTABLEPCT_SUM:
2493 case CTSF_ULAYERPCT_SUM:
2494 case CTSF_ULAYERROWPCT_SUM:
2495 case CTSF_ULAYERCOLPCT_SUM:
2496 s->moments = moments1_create (MOMENT_VARIANCE);
2506 struct caseproto *proto = caseproto_create ();
2507 proto = caseproto_add_width (proto, 0);
2508 proto = caseproto_add_width (proto, 0);
2510 struct subcase ordering;
2511 subcase_init (&ordering, 0, 0, SC_ASCEND);
2512 s->writer = sort_create_writer (&ordering, proto);
2513 subcase_uninit (&ordering);
2514 caseproto_unref (proto);
2524 ctables_summary_uninit (union ctables_summary *s,
2525 const struct ctables_summary_spec *ss)
2527 switch (ss->function)
2531 case CTSF_ROWPCT_COUNT:
2532 case CTSF_COLPCT_COUNT:
2533 case CTSF_TABLEPCT_COUNT:
2534 case CTSF_SUBTABLEPCT_COUNT:
2535 case CTSF_LAYERPCT_COUNT:
2536 case CTSF_LAYERROWPCT_COUNT:
2537 case CTSF_LAYERCOLPCT_COUNT:
2538 case CTSF_ROWPCT_VALIDN:
2539 case CTSF_COLPCT_VALIDN:
2540 case CTSF_TABLEPCT_VALIDN:
2541 case CTSF_SUBTABLEPCT_VALIDN:
2542 case CTSF_LAYERPCT_VALIDN:
2543 case CTSF_LAYERROWPCT_VALIDN:
2544 case CTSF_LAYERCOLPCT_VALIDN:
2545 case CTSF_ROWPCT_TOTALN:
2546 case CTSF_COLPCT_TOTALN:
2547 case CTSF_TABLEPCT_TOTALN:
2548 case CTSF_SUBTABLEPCT_TOTALN:
2549 case CTSF_LAYERPCT_TOTALN:
2550 case CTSF_LAYERROWPCT_TOTALN:
2551 case CTSF_LAYERCOLPCT_TOTALN:
2558 case CTSF_UROWPCT_COUNT:
2559 case CTSF_UCOLPCT_COUNT:
2560 case CTSF_UTABLEPCT_COUNT:
2561 case CTSF_USUBTABLEPCT_COUNT:
2562 case CTSF_ULAYERPCT_COUNT:
2563 case CTSF_ULAYERROWPCT_COUNT:
2564 case CTSF_ULAYERCOLPCT_COUNT:
2565 case CTSF_UROWPCT_VALIDN:
2566 case CTSF_UCOLPCT_VALIDN:
2567 case CTSF_UTABLEPCT_VALIDN:
2568 case CTSF_USUBTABLEPCT_VALIDN:
2569 case CTSF_ULAYERPCT_VALIDN:
2570 case CTSF_ULAYERROWPCT_VALIDN:
2571 case CTSF_ULAYERCOLPCT_VALIDN:
2572 case CTSF_UROWPCT_TOTALN:
2573 case CTSF_UCOLPCT_TOTALN:
2574 case CTSF_UTABLEPCT_TOTALN:
2575 case CTSF_USUBTABLEPCT_TOTALN:
2576 case CTSF_ULAYERPCT_TOTALN:
2577 case CTSF_ULAYERROWPCT_TOTALN:
2578 case CTSF_ULAYERCOLPCT_TOTALN:
2594 case CTSF_ROWPCT_SUM:
2595 case CTSF_COLPCT_SUM:
2596 case CTSF_TABLEPCT_SUM:
2597 case CTSF_SUBTABLEPCT_SUM:
2598 case CTSF_LAYERPCT_SUM:
2599 case CTSF_LAYERROWPCT_SUM:
2600 case CTSF_LAYERCOLPCT_SUM:
2605 case CTSF_UVARIANCE:
2606 case CTSF_UROWPCT_SUM:
2607 case CTSF_UCOLPCT_SUM:
2608 case CTSF_UTABLEPCT_SUM:
2609 case CTSF_USUBTABLEPCT_SUM:
2610 case CTSF_ULAYERPCT_SUM:
2611 case CTSF_ULAYERROWPCT_SUM:
2612 case CTSF_ULAYERCOLPCT_SUM:
2613 moments1_destroy (s->moments);
2622 casewriter_destroy (s->writer);
2628 ctables_summary_add (union ctables_summary *s,
2629 const struct ctables_summary_spec *ss,
2630 const struct variable *var, const union value *value,
2631 bool is_scale, bool is_scale_missing,
2632 bool is_missing, bool excluded_missing,
2633 double d_weight, double e_weight)
2635 /* To determine whether a case is included in a given table for a particular
2636 kind of summary, consider the following charts for each variable in the
2637 table. Only if "yes" appears for every variable for the summary is the
2640 Categorical variables: VALIDN COUNT TOTALN
2641 Valid values in included categories yes yes yes
2642 Missing values in included categories --- yes yes
2643 Missing values in excluded categories --- --- yes
2644 Valid values in excluded categories --- --- ---
2646 Scale variables: VALIDN COUNT TOTALN
2647 Valid value yes yes yes
2648 Missing value --- yes yes
2650 Missing values include both user- and system-missing. (The system-missing
2651 value is always in an excluded category.)
2653 switch (ss->function)
2656 case CTSF_ROWPCT_TOTALN:
2657 case CTSF_COLPCT_TOTALN:
2658 case CTSF_TABLEPCT_TOTALN:
2659 case CTSF_SUBTABLEPCT_TOTALN:
2660 case CTSF_LAYERPCT_TOTALN:
2661 case CTSF_LAYERROWPCT_TOTALN:
2662 case CTSF_LAYERCOLPCT_TOTALN:
2663 s->count += d_weight;
2667 case CTSF_UROWPCT_TOTALN:
2668 case CTSF_UCOLPCT_TOTALN:
2669 case CTSF_UTABLEPCT_TOTALN:
2670 case CTSF_USUBTABLEPCT_TOTALN:
2671 case CTSF_ULAYERPCT_TOTALN:
2672 case CTSF_ULAYERROWPCT_TOTALN:
2673 case CTSF_ULAYERCOLPCT_TOTALN:
2678 case CTSF_ROWPCT_COUNT:
2679 case CTSF_COLPCT_COUNT:
2680 case CTSF_TABLEPCT_COUNT:
2681 case CTSF_SUBTABLEPCT_COUNT:
2682 case CTSF_LAYERPCT_COUNT:
2683 case CTSF_LAYERROWPCT_COUNT:
2684 case CTSF_LAYERCOLPCT_COUNT:
2685 if (is_scale || !excluded_missing)
2686 s->count += d_weight;
2690 case CTSF_UROWPCT_COUNT:
2691 case CTSF_UCOLPCT_COUNT:
2692 case CTSF_UTABLEPCT_COUNT:
2693 case CTSF_USUBTABLEPCT_COUNT:
2694 case CTSF_ULAYERPCT_COUNT:
2695 case CTSF_ULAYERROWPCT_COUNT:
2696 case CTSF_ULAYERCOLPCT_COUNT:
2697 if (is_scale || !excluded_missing)
2702 case CTSF_ROWPCT_VALIDN:
2703 case CTSF_COLPCT_VALIDN:
2704 case CTSF_TABLEPCT_VALIDN:
2705 case CTSF_SUBTABLEPCT_VALIDN:
2706 case CTSF_LAYERPCT_VALIDN:
2707 case CTSF_LAYERROWPCT_VALIDN:
2708 case CTSF_LAYERCOLPCT_VALIDN:
2712 s->count += d_weight;
2716 case CTSF_UROWPCT_VALIDN:
2717 case CTSF_UCOLPCT_VALIDN:
2718 case CTSF_UTABLEPCT_VALIDN:
2719 case CTSF_USUBTABLEPCT_VALIDN:
2720 case CTSF_ULAYERPCT_VALIDN:
2721 case CTSF_ULAYERROWPCT_VALIDN:
2722 case CTSF_ULAYERCOLPCT_VALIDN:
2731 s->count += d_weight;
2740 if (is_scale || !excluded_missing)
2741 s->count += e_weight;
2748 s->count += e_weight;
2752 s->count += e_weight;
2758 if (!is_scale_missing)
2760 assert (!var_is_alpha (var)); /* XXX? */
2761 if (s->min == SYSMIS || value->f < s->min)
2763 if (s->max == SYSMIS || value->f > s->max)
2773 case CTSF_ROWPCT_SUM:
2774 case CTSF_COLPCT_SUM:
2775 case CTSF_TABLEPCT_SUM:
2776 case CTSF_SUBTABLEPCT_SUM:
2777 case CTSF_LAYERPCT_SUM:
2778 case CTSF_LAYERROWPCT_SUM:
2779 case CTSF_LAYERCOLPCT_SUM:
2780 if (!is_scale_missing)
2781 moments1_add (s->moments, value->f, e_weight);
2788 case CTSF_UVARIANCE:
2789 case CTSF_UROWPCT_SUM:
2790 case CTSF_UCOLPCT_SUM:
2791 case CTSF_UTABLEPCT_SUM:
2792 case CTSF_USUBTABLEPCT_SUM:
2793 case CTSF_ULAYERPCT_SUM:
2794 case CTSF_ULAYERROWPCT_SUM:
2795 case CTSF_ULAYERCOLPCT_SUM:
2796 if (!is_scale_missing)
2797 moments1_add (s->moments, value->f, 1.0);
2803 d_weight = e_weight = 1.0;
2808 if (!is_scale_missing)
2810 s->ovalid += e_weight;
2812 struct ccase *c = case_create (casewriter_get_proto (s->writer));
2813 *case_num_rw_idx (c, 0) = value->f;
2814 *case_num_rw_idx (c, 1) = e_weight;
2815 casewriter_write (s->writer, c);
2821 static enum ctables_domain_type
2822 ctables_function_domain (enum ctables_summary_function function)
2852 case CTSF_UVARIANCE:
2858 case CTSF_COLPCT_COUNT:
2859 case CTSF_COLPCT_SUM:
2860 case CTSF_COLPCT_TOTALN:
2861 case CTSF_COLPCT_VALIDN:
2862 case CTSF_UCOLPCT_COUNT:
2863 case CTSF_UCOLPCT_SUM:
2864 case CTSF_UCOLPCT_TOTALN:
2865 case CTSF_UCOLPCT_VALIDN:
2868 case CTSF_LAYERCOLPCT_COUNT:
2869 case CTSF_LAYERCOLPCT_SUM:
2870 case CTSF_LAYERCOLPCT_TOTALN:
2871 case CTSF_LAYERCOLPCT_VALIDN:
2872 case CTSF_ULAYERCOLPCT_COUNT:
2873 case CTSF_ULAYERCOLPCT_SUM:
2874 case CTSF_ULAYERCOLPCT_TOTALN:
2875 case CTSF_ULAYERCOLPCT_VALIDN:
2876 return CTDT_LAYERCOL;
2878 case CTSF_LAYERPCT_COUNT:
2879 case CTSF_LAYERPCT_SUM:
2880 case CTSF_LAYERPCT_TOTALN:
2881 case CTSF_LAYERPCT_VALIDN:
2882 case CTSF_ULAYERPCT_COUNT:
2883 case CTSF_ULAYERPCT_SUM:
2884 case CTSF_ULAYERPCT_TOTALN:
2885 case CTSF_ULAYERPCT_VALIDN:
2888 case CTSF_LAYERROWPCT_COUNT:
2889 case CTSF_LAYERROWPCT_SUM:
2890 case CTSF_LAYERROWPCT_TOTALN:
2891 case CTSF_LAYERROWPCT_VALIDN:
2892 case CTSF_ULAYERROWPCT_COUNT:
2893 case CTSF_ULAYERROWPCT_SUM:
2894 case CTSF_ULAYERROWPCT_TOTALN:
2895 case CTSF_ULAYERROWPCT_VALIDN:
2896 return CTDT_LAYERROW;
2898 case CTSF_ROWPCT_COUNT:
2899 case CTSF_ROWPCT_SUM:
2900 case CTSF_ROWPCT_TOTALN:
2901 case CTSF_ROWPCT_VALIDN:
2902 case CTSF_UROWPCT_COUNT:
2903 case CTSF_UROWPCT_SUM:
2904 case CTSF_UROWPCT_TOTALN:
2905 case CTSF_UROWPCT_VALIDN:
2908 case CTSF_SUBTABLEPCT_COUNT:
2909 case CTSF_SUBTABLEPCT_SUM:
2910 case CTSF_SUBTABLEPCT_TOTALN:
2911 case CTSF_SUBTABLEPCT_VALIDN:
2912 case CTSF_USUBTABLEPCT_COUNT:
2913 case CTSF_USUBTABLEPCT_SUM:
2914 case CTSF_USUBTABLEPCT_TOTALN:
2915 case CTSF_USUBTABLEPCT_VALIDN:
2916 return CTDT_SUBTABLE;
2918 case CTSF_TABLEPCT_COUNT:
2919 case CTSF_TABLEPCT_SUM:
2920 case CTSF_TABLEPCT_TOTALN:
2921 case CTSF_TABLEPCT_VALIDN:
2922 case CTSF_UTABLEPCT_COUNT:
2923 case CTSF_UTABLEPCT_SUM:
2924 case CTSF_UTABLEPCT_TOTALN:
2925 case CTSF_UTABLEPCT_VALIDN:
2932 static enum ctables_domain_type
2933 ctables_function_is_pctsum (enum ctables_summary_function function)
2963 case CTSF_UVARIANCE:
2967 case CTSF_COLPCT_COUNT:
2968 case CTSF_COLPCT_TOTALN:
2969 case CTSF_COLPCT_VALIDN:
2970 case CTSF_UCOLPCT_COUNT:
2971 case CTSF_UCOLPCT_TOTALN:
2972 case CTSF_UCOLPCT_VALIDN:
2973 case CTSF_LAYERCOLPCT_COUNT:
2974 case CTSF_LAYERCOLPCT_TOTALN:
2975 case CTSF_LAYERCOLPCT_VALIDN:
2976 case CTSF_ULAYERCOLPCT_COUNT:
2977 case CTSF_ULAYERCOLPCT_TOTALN:
2978 case CTSF_ULAYERCOLPCT_VALIDN:
2979 case CTSF_LAYERPCT_COUNT:
2980 case CTSF_LAYERPCT_TOTALN:
2981 case CTSF_LAYERPCT_VALIDN:
2982 case CTSF_ULAYERPCT_COUNT:
2983 case CTSF_ULAYERPCT_TOTALN:
2984 case CTSF_ULAYERPCT_VALIDN:
2985 case CTSF_LAYERROWPCT_COUNT:
2986 case CTSF_LAYERROWPCT_TOTALN:
2987 case CTSF_LAYERROWPCT_VALIDN:
2988 case CTSF_ULAYERROWPCT_COUNT:
2989 case CTSF_ULAYERROWPCT_TOTALN:
2990 case CTSF_ULAYERROWPCT_VALIDN:
2991 case CTSF_ROWPCT_COUNT:
2992 case CTSF_ROWPCT_TOTALN:
2993 case CTSF_ROWPCT_VALIDN:
2994 case CTSF_UROWPCT_COUNT:
2995 case CTSF_UROWPCT_TOTALN:
2996 case CTSF_UROWPCT_VALIDN:
2997 case CTSF_SUBTABLEPCT_COUNT:
2998 case CTSF_SUBTABLEPCT_TOTALN:
2999 case CTSF_SUBTABLEPCT_VALIDN:
3000 case CTSF_USUBTABLEPCT_COUNT:
3001 case CTSF_USUBTABLEPCT_TOTALN:
3002 case CTSF_USUBTABLEPCT_VALIDN:
3003 case CTSF_TABLEPCT_COUNT:
3004 case CTSF_TABLEPCT_TOTALN:
3005 case CTSF_TABLEPCT_VALIDN:
3006 case CTSF_UTABLEPCT_COUNT:
3007 case CTSF_UTABLEPCT_TOTALN:
3008 case CTSF_UTABLEPCT_VALIDN:
3011 case CTSF_COLPCT_SUM:
3012 case CTSF_UCOLPCT_SUM:
3013 case CTSF_LAYERCOLPCT_SUM:
3014 case CTSF_ULAYERCOLPCT_SUM:
3015 case CTSF_LAYERPCT_SUM:
3016 case CTSF_ULAYERPCT_SUM:
3017 case CTSF_LAYERROWPCT_SUM:
3018 case CTSF_ULAYERROWPCT_SUM:
3019 case CTSF_ROWPCT_SUM:
3020 case CTSF_UROWPCT_SUM:
3021 case CTSF_SUBTABLEPCT_SUM:
3022 case CTSF_USUBTABLEPCT_SUM:
3023 case CTSF_TABLEPCT_SUM:
3024 case CTSF_UTABLEPCT_SUM:
3032 ctables_summary_value (const struct ctables_cell *cell,
3033 union ctables_summary *s,
3034 const struct ctables_summary_spec *ss)
3036 switch (ss->function)
3043 case CTSF_ROWPCT_COUNT:
3044 case CTSF_COLPCT_COUNT:
3045 case CTSF_TABLEPCT_COUNT:
3046 case CTSF_SUBTABLEPCT_COUNT:
3047 case CTSF_LAYERPCT_COUNT:
3048 case CTSF_LAYERROWPCT_COUNT:
3049 case CTSF_LAYERCOLPCT_COUNT:
3051 enum ctables_domain_type d = ctables_function_domain (ss->function);
3052 return (cell->domains[d]->e_count
3053 ? s->count / cell->domains[d]->e_count * 100
3057 case CTSF_UROWPCT_COUNT:
3058 case CTSF_UCOLPCT_COUNT:
3059 case CTSF_UTABLEPCT_COUNT:
3060 case CTSF_USUBTABLEPCT_COUNT:
3061 case CTSF_ULAYERPCT_COUNT:
3062 case CTSF_ULAYERROWPCT_COUNT:
3063 case CTSF_ULAYERCOLPCT_COUNT:
3065 enum ctables_domain_type d = ctables_function_domain (ss->function);
3066 return (cell->domains[d]->u_count
3067 ? s->count / cell->domains[d]->u_count * 100
3071 case CTSF_ROWPCT_VALIDN:
3072 case CTSF_COLPCT_VALIDN:
3073 case CTSF_TABLEPCT_VALIDN:
3074 case CTSF_SUBTABLEPCT_VALIDN:
3075 case CTSF_LAYERPCT_VALIDN:
3076 case CTSF_LAYERROWPCT_VALIDN:
3077 case CTSF_LAYERCOLPCT_VALIDN:
3079 enum ctables_domain_type d = ctables_function_domain (ss->function);
3080 return (cell->domains[d]->e_valid
3081 ? s->count / cell->domains[d]->e_valid * 100
3085 case CTSF_UROWPCT_VALIDN:
3086 case CTSF_UCOLPCT_VALIDN:
3087 case CTSF_UTABLEPCT_VALIDN:
3088 case CTSF_USUBTABLEPCT_VALIDN:
3089 case CTSF_ULAYERPCT_VALIDN:
3090 case CTSF_ULAYERROWPCT_VALIDN:
3091 case CTSF_ULAYERCOLPCT_VALIDN:
3093 enum ctables_domain_type d = ctables_function_domain (ss->function);
3094 return (cell->domains[d]->u_valid
3095 ? s->count / cell->domains[d]->u_valid * 100
3099 case CTSF_ROWPCT_TOTALN:
3100 case CTSF_COLPCT_TOTALN:
3101 case CTSF_TABLEPCT_TOTALN:
3102 case CTSF_SUBTABLEPCT_TOTALN:
3103 case CTSF_LAYERPCT_TOTALN:
3104 case CTSF_LAYERROWPCT_TOTALN:
3105 case CTSF_LAYERCOLPCT_TOTALN:
3107 enum ctables_domain_type d = ctables_function_domain (ss->function);
3108 return (cell->domains[d]->e_total
3109 ? s->count / cell->domains[d]->e_total * 100
3113 case CTSF_UROWPCT_TOTALN:
3114 case CTSF_UCOLPCT_TOTALN:
3115 case CTSF_UTABLEPCT_TOTALN:
3116 case CTSF_USUBTABLEPCT_TOTALN:
3117 case CTSF_ULAYERPCT_TOTALN:
3118 case CTSF_ULAYERROWPCT_TOTALN:
3119 case CTSF_ULAYERCOLPCT_TOTALN:
3121 enum ctables_domain_type d = ctables_function_domain (ss->function);
3122 return (cell->domains[d]->u_total
3123 ? s->count / cell->domains[d]->u_total * 100
3144 return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
3150 moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
3157 double weight, variance;
3158 moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
3159 return calc_semean (variance, weight);
3166 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
3167 return variance != SYSMIS ? sqrt (variance) : SYSMIS;
3173 double weight, mean;
3174 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
3175 return weight != SYSMIS && mean != SYSMIS ? weight * mean : SYSMIS;
3179 case CTSF_UVARIANCE:
3182 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
3186 case CTSF_ROWPCT_SUM:
3187 case CTSF_COLPCT_SUM:
3188 case CTSF_TABLEPCT_SUM:
3189 case CTSF_SUBTABLEPCT_SUM:
3190 case CTSF_LAYERPCT_SUM:
3191 case CTSF_LAYERROWPCT_SUM:
3192 case CTSF_LAYERCOLPCT_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].e_sum;
3201 return denom != 0 ? num / denom * 100 : SYSMIS;
3203 case CTSF_UROWPCT_SUM:
3204 case CTSF_UCOLPCT_SUM:
3205 case CTSF_UTABLEPCT_SUM:
3206 case CTSF_USUBTABLEPCT_SUM:
3207 case CTSF_ULAYERPCT_SUM:
3208 case CTSF_ULAYERROWPCT_SUM:
3209 case CTSF_ULAYERCOLPCT_SUM:
3211 double weight, mean;
3212 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
3213 if (weight == SYSMIS || mean == SYSMIS)
3215 enum ctables_domain_type d = ctables_function_domain (ss->function);
3216 double num = weight * mean;
3217 double denom = cell->domains[d]->sums[ss->sum_var_idx].u_sum;
3218 return denom != 0 ? num / denom * 100 : SYSMIS;
3227 struct casereader *reader = casewriter_make_reader (s->writer);
3230 struct percentile *ptile = percentile_create (
3231 ss->function == CTSF_PTILE ? ss->percentile : 0.5, s->ovalid);
3232 struct order_stats *os = &ptile->parent;
3233 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
3234 s->ovalue = percentile_calculate (ptile, PC_HAVERAGE);
3235 statistic_destroy (&ptile->parent.parent);
3243 struct casereader *reader = casewriter_make_reader (s->writer);
3246 struct mode *mode = mode_create ();
3247 struct order_stats *os = &mode->parent;
3248 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
3249 s->ovalue = mode->mode;
3250 statistic_destroy (&mode->parent.parent);
3258 struct ctables_cell_sort_aux
3260 const struct ctables_nest *nest;
3261 enum pivot_axis_type a;
3265 ctables_cell_compare_3way (const void *a_, const void *b_, const void *aux_)
3267 const struct ctables_cell_sort_aux *aux = aux_;
3268 struct ctables_cell *const *ap = a_;
3269 struct ctables_cell *const *bp = b_;
3270 const struct ctables_cell *a = *ap;
3271 const struct ctables_cell *b = *bp;
3273 const struct ctables_nest *nest = aux->nest;
3274 for (size_t i = 0; i < nest->n; i++)
3275 if (i != nest->scale_idx)
3277 const struct variable *var = nest->vars[i];
3278 const struct ctables_cell_value *a_cv = &a->axes[aux->a].cvs[i];
3279 const struct ctables_cell_value *b_cv = &b->axes[aux->a].cvs[i];
3280 if (a_cv->category != b_cv->category)
3281 return a_cv->category > b_cv->category ? 1 : -1;
3283 const union value *a_val = &a_cv->value;
3284 const union value *b_val = &b_cv->value;
3285 switch (a_cv->category->type)
3291 case CCT_POSTCOMPUTE:
3292 case CCT_EXCLUDED_MISSING:
3293 /* Must be equal. */
3301 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
3309 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
3311 return a_cv->category->sort_ascending ? cmp : -cmp;
3317 const char *a_label = var_lookup_value_label (var, a_val);
3318 const char *b_label = var_lookup_value_label (var, b_val);
3320 ? (b_label ? strcmp (a_label, b_label) : 1)
3321 : (b_label ? -1 : value_compare_3way (
3322 a_val, b_val, var_get_width (var))));
3324 return a_cv->category->sort_ascending ? cmp : -cmp;
3338 For each ctables_table:
3339 For each combination of row vars:
3340 For each combination of column vars:
3341 For each combination of layer vars:
3343 Make a table of row values:
3344 Sort entries by row values
3345 Assign a 0-based index to each actual value
3346 Construct a dimension
3347 Make a table of column values
3348 Make a table of layer values
3350 Fill the table entry using the indexes from before.
3353 static struct ctables_domain *
3354 ctables_domain_insert (struct ctables_section *s, struct ctables_cell *cell,
3355 enum ctables_domain_type domain)
3358 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3360 const struct ctables_nest *nest = s->nests[a];
3361 for (size_t i = 0; i < nest->n_domains[domain]; i++)
3363 size_t v_idx = nest->domains[domain][i];
3364 struct ctables_cell_value *cv = &cell->axes[a].cvs[v_idx];
3365 hash = hash_pointer (cv->category, hash);
3366 if (cv->category->type != CCT_TOTAL
3367 && cv->category->type != CCT_SUBTOTAL
3368 && cv->category->type != CCT_POSTCOMPUTE)
3369 hash = value_hash (&cv->value,
3370 var_get_width (nest->vars[v_idx]), hash);
3374 struct ctables_domain *d;
3375 HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &s->domains[domain])
3377 const struct ctables_cell *df = d->example;
3378 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3380 const struct ctables_nest *nest = s->nests[a];
3381 for (size_t i = 0; i < nest->n_domains[domain]; i++)
3383 size_t v_idx = nest->domains[domain][i];
3384 struct ctables_cell_value *cv1 = &df->axes[a].cvs[v_idx];
3385 struct ctables_cell_value *cv2 = &cell->axes[a].cvs[v_idx];
3386 if (cv1->category != cv2->category
3387 || (cv1->category->type != CCT_TOTAL
3388 && cv1->category->type != CCT_SUBTOTAL
3389 && cv1->category->type != CCT_POSTCOMPUTE
3390 && !value_equal (&cv1->value, &cv2->value,
3391 var_get_width (nest->vars[v_idx]))))
3400 struct ctables_sum *sums = (s->table->n_sum_vars
3401 ? xzalloc (s->table->n_sum_vars * sizeof *sums)
3404 d = xmalloc (sizeof *d);
3405 *d = (struct ctables_domain) { .example = cell, .sums = sums };
3406 hmap_insert (&s->domains[domain], &d->node, hash);
3410 static struct substring
3411 rtrim_value (const union value *v, const struct variable *var)
3413 struct substring s = ss_buffer (CHAR_CAST (char *, v->s),
3414 var_get_width (var));
3415 ss_rtrim (&s, ss_cstr (" "));
3420 in_string_range (const union value *v, const struct variable *var,
3421 const struct substring *srange)
3423 struct substring s = rtrim_value (v, var);
3424 return ((!srange[0].string || ss_compare (s, srange[0]) >= 0)
3425 && (!srange[1].string || ss_compare (s, srange[1]) <= 0));
3428 static const struct ctables_category *
3429 ctables_categories_match (const struct ctables_categories *c,
3430 const union value *v, const struct variable *var)
3432 if (var_is_numeric (var) && v->f == SYSMIS)
3435 const struct ctables_category *othernm = NULL;
3436 for (size_t i = c->n_cats; i-- > 0; )
3438 const struct ctables_category *cat = &c->cats[i];
3442 if (cat->number == v->f)
3447 if (ss_equals (cat->string, rtrim_value (v, var)))
3452 if ((cat->nrange[0] == -DBL_MAX || v->f >= cat->nrange[0])
3453 && (cat->nrange[1] == DBL_MAX || v->f <= cat->nrange[1]))
3458 if (in_string_range (v, var, cat->srange))
3463 if (var_is_value_missing (var, v))
3467 case CCT_POSTCOMPUTE:
3482 return (cat->include_missing || !var_is_value_missing (var, v) ? cat
3485 case CCT_EXCLUDED_MISSING:
3490 return var_is_value_missing (var, v) ? NULL : othernm;
3493 static const struct ctables_category *
3494 ctables_categories_total (const struct ctables_categories *c)
3496 const struct ctables_category *first = &c->cats[0];
3497 const struct ctables_category *last = &c->cats[c->n_cats - 1];
3498 return (first->type == CCT_TOTAL ? first
3499 : last->type == CCT_TOTAL ? last
3503 static struct ctables_cell *
3504 ctables_cell_insert__ (struct ctables_section *s, const struct ccase *c,
3505 const struct ctables_category *cats[PIVOT_N_AXES][10])
3508 enum ctables_summary_variant sv = CSV_CELL;
3509 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3511 const struct ctables_nest *nest = s->nests[a];
3512 for (size_t i = 0; i < nest->n; i++)
3513 if (i != nest->scale_idx)
3515 hash = hash_pointer (cats[a][i], hash);
3516 if (cats[a][i]->type != CCT_TOTAL
3517 && cats[a][i]->type != CCT_SUBTOTAL
3518 && cats[a][i]->type != CCT_POSTCOMPUTE)
3519 hash = value_hash (case_data (c, nest->vars[i]),
3520 var_get_width (nest->vars[i]), hash);
3526 struct ctables_cell *cell;
3527 HMAP_FOR_EACH_WITH_HASH (cell, struct ctables_cell, node, hash, &s->cells)
3529 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3531 const struct ctables_nest *nest = s->nests[a];
3532 for (size_t i = 0; i < nest->n; i++)
3533 if (i != nest->scale_idx
3534 && (cats[a][i] != cell->axes[a].cvs[i].category
3535 || (cats[a][i]->type != CCT_TOTAL
3536 && cats[a][i]->type != CCT_SUBTOTAL
3537 && cats[a][i]->type != CCT_POSTCOMPUTE
3538 && !value_equal (case_data (c, nest->vars[i]),
3539 &cell->axes[a].cvs[i].value,
3540 var_get_width (nest->vars[i])))))
3549 cell = xmalloc (sizeof *cell);
3552 cell->omit_domains = 0;
3553 cell->postcompute = false;
3554 //struct string name = DS_EMPTY_INITIALIZER;
3555 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3557 const struct ctables_nest *nest = s->nests[a];
3558 cell->axes[a].cvs = (nest->n
3559 ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
3561 for (size_t i = 0; i < nest->n; i++)
3563 const struct ctables_category *cat = cats[a][i];
3564 const struct variable *var = nest->vars[i];
3565 const union value *value = case_data (c, var);
3566 if (i != nest->scale_idx)
3568 const struct ctables_category *subtotal = cat->subtotal;
3569 if (cat->hide || (subtotal && subtotal->hide_subcategories))
3572 if (cat->type == CCT_TOTAL
3573 || cat->type == CCT_SUBTOTAL
3574 || cat->type == CCT_POSTCOMPUTE)
3576 /* XXX these should be more encompassing I think.*/
3580 case PIVOT_AXIS_COLUMN:
3581 cell->omit_domains |= ((1u << CTDT_TABLE) |
3582 (1u << CTDT_LAYER) |
3583 (1u << CTDT_LAYERCOL) |
3584 (1u << CTDT_SUBTABLE) |
3587 case PIVOT_AXIS_ROW:
3588 cell->omit_domains |= ((1u << CTDT_TABLE) |
3589 (1u << CTDT_LAYER) |
3590 (1u << CTDT_LAYERROW) |
3591 (1u << CTDT_SUBTABLE) |
3594 case PIVOT_AXIS_LAYER:
3595 cell->omit_domains |= ((1u << CTDT_TABLE) |
3596 (1u << CTDT_LAYER));
3600 if (cat->type == CCT_POSTCOMPUTE)
3601 cell->postcompute = true;
3604 cell->axes[a].cvs[i].category = cat;
3605 value_clone (&cell->axes[a].cvs[i].value, value, var_get_width (var));
3608 if (i != nest->scale_idx)
3610 if (!ds_is_empty (&name))
3611 ds_put_cstr (&name, ", ");
3612 char *value_s = data_out (value, var_get_encoding (var),
3613 var_get_print_format (var),
3614 settings_get_fmt_settings ());
3615 if (cat->type == CCT_TOTAL
3616 || cat->type == CCT_SUBTOTAL
3617 || cat->type == CCT_POSTCOMPUTE)
3618 ds_put_format (&name, "%s=total", var_get_name (var));
3620 ds_put_format (&name, "%s=%s", var_get_name (var),
3621 value_s + strspn (value_s, " "));
3627 //cell->name = ds_steal_cstr (&name);
3629 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3630 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3631 cell->summaries = xmalloc (specs->n * sizeof *cell->summaries);
3632 for (size_t i = 0; i < specs->n; i++)
3633 ctables_summary_init (&cell->summaries[i], &specs->specs[i]);
3634 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3635 cell->domains[dt] = ctables_domain_insert (s, cell, dt);
3636 hmap_insert (&s->cells, &cell->node, hash);
3641 is_scale_missing (const struct ctables_summary_spec_set *specs,
3642 const struct ccase *c)
3644 if (!specs->is_scale)
3647 if (var_is_num_missing (specs->var, case_num (c, specs->var)))
3650 for (size_t i = 0; i < specs->n_listwise_vars; i++)
3652 const struct variable *var = specs->listwise_vars[i];
3653 if (var_is_num_missing (var, case_num (c, var)))
3661 ctables_cell_add__ (struct ctables_section *s, const struct ccase *c,
3662 const struct ctables_category *cats[PIVOT_N_AXES][10],
3663 bool is_missing, bool excluded_missing,
3664 double d_weight, double e_weight)
3666 struct ctables_cell *cell = ctables_cell_insert__ (s, c, cats);
3667 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3669 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3671 bool scale_missing = is_scale_missing (specs, c);
3672 for (size_t i = 0; i < specs->n; i++)
3673 ctables_summary_add (&cell->summaries[i], &specs->specs[i],
3674 specs->var, case_data (c, specs->var), specs->is_scale,
3675 scale_missing, is_missing, excluded_missing,
3676 d_weight, e_weight);
3677 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3678 if (!(cell->omit_domains && (1u << dt)))
3680 struct ctables_domain *d = cell->domains[dt];
3681 d->d_total += d_weight;
3682 d->e_total += e_weight;
3684 if (!excluded_missing)
3686 d->d_count += d_weight;
3687 d->e_count += e_weight;
3692 d->d_valid += d_weight;
3693 d->e_valid += e_weight;
3696 for (size_t i = 0; i < s->table->n_sum_vars; i++)
3698 /* XXX listwise_missing??? */
3699 const struct variable *var = s->table->sum_vars[i];
3700 double addend = case_num (c, var);
3701 if (!var_is_num_missing (var, addend))
3703 struct ctables_sum *sum = &d->sums[i];
3704 sum->e_sum += addend * e_weight;
3705 sum->u_sum += addend;
3713 recurse_totals (struct ctables_section *s, const struct ccase *c,
3714 const struct ctables_category *cats[PIVOT_N_AXES][10],
3715 bool is_missing, bool excluded_missing,
3716 double d_weight, double e_weight,
3717 enum pivot_axis_type start_axis, size_t start_nest)
3719 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3721 const struct ctables_nest *nest = s->nests[a];
3722 for (size_t i = start_nest; i < nest->n; i++)
3724 if (i == nest->scale_idx)
3727 const struct variable *var = nest->vars[i];
3729 const struct ctables_category *total = ctables_categories_total (
3730 s->table->categories[var_get_dict_index (var)]);
3733 const struct ctables_category *save = cats[a][i];
3735 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3736 d_weight, e_weight);
3737 recurse_totals (s, c, cats, is_missing, excluded_missing,
3738 d_weight, e_weight, a, i + 1);
3747 recurse_subtotals (struct ctables_section *s, const struct ccase *c,
3748 const struct ctables_category *cats[PIVOT_N_AXES][10],
3749 bool is_missing, bool excluded_missing,
3750 double d_weight, double e_weight,
3751 enum pivot_axis_type start_axis, size_t start_nest)
3753 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3755 const struct ctables_nest *nest = s->nests[a];
3756 for (size_t i = start_nest; i < nest->n; i++)
3758 if (i == nest->scale_idx)
3761 const struct ctables_category *save = cats[a][i];
3764 cats[a][i] = save->subtotal;
3765 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3766 d_weight, e_weight);
3767 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3768 d_weight, e_weight, a, i + 1);
3777 ctables_add_occurrence (const struct variable *var,
3778 const union value *value,
3779 struct hmap *occurrences)
3781 int width = var_get_width (var);
3782 unsigned int hash = value_hash (value, width, 0);
3784 struct ctables_occurrence *o;
3785 HMAP_FOR_EACH_WITH_HASH (o, struct ctables_occurrence, node, hash,
3787 if (value_equal (value, &o->value, width))
3790 o = xmalloc (sizeof *o);
3791 value_clone (&o->value, value, width);
3792 hmap_insert (occurrences, &o->node, hash);
3796 ctables_cell_insert (struct ctables_section *s,
3797 const struct ccase *c,
3798 double d_weight, double e_weight)
3800 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
3802 /* Does at least one categorical variable have a missing value in an included
3803 or excluded category? */
3804 bool is_missing = false;
3806 /* Does at least one categorical variable have a missing value in an excluded
3808 bool excluded_missing = false;
3810 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3812 const struct ctables_nest *nest = s->nests[a];
3813 for (size_t i = 0; i < nest->n; i++)
3815 if (i == nest->scale_idx)
3818 const struct variable *var = nest->vars[i];
3819 const union value *value = case_data (c, var);
3821 bool var_missing = var_is_value_missing (var, value) != 0;
3825 cats[a][i] = ctables_categories_match (
3826 s->table->categories[var_get_dict_index (var)], value, var);
3832 static const struct ctables_category cct_excluded_missing = {
3833 .type = CCT_EXCLUDED_MISSING,
3836 cats[a][i] = &cct_excluded_missing;
3837 excluded_missing = true;
3842 if (!excluded_missing)
3843 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3845 const struct ctables_nest *nest = s->nests[a];
3846 for (size_t i = 0; i < nest->n; i++)
3847 if (i != nest->scale_idx)
3849 const struct variable *var = nest->vars[i];
3850 const union value *value = case_data (c, var);
3851 ctables_add_occurrence (var, value, &s->occurrences[a][i]);
3855 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3856 d_weight, e_weight);
3858 //if (!excluded_missing)
3860 recurse_totals (s, c, cats, is_missing, excluded_missing,
3861 d_weight, e_weight, 0, 0);
3862 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3863 d_weight, e_weight, 0, 0);
3869 const struct ctables_summary_spec_set *set;
3874 merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b)
3876 const struct ctables_summary_spec *as = &a->set->specs[a->ofs];
3877 const struct ctables_summary_spec *bs = &b->set->specs[b->ofs];
3878 if (as->function != bs->function)
3879 return as->function > bs->function ? 1 : -1;
3880 else if (as->percentile != bs->percentile)
3881 return as->percentile < bs->percentile ? 1 : -1;
3883 const char *as_label = as->label ? as->label : "";
3884 const char *bs_label = bs->label ? bs->label : "";
3885 return strcmp (as_label, bs_label);
3888 static struct pivot_value *
3889 ctables_category_create_label__ (const struct ctables_category *cat,
3890 const struct variable *var,
3891 const union value *value)
3893 return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
3894 ? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
3895 : pivot_value_new_var_value (var, value));
3898 static struct pivot_value *
3899 ctables_postcompute_label (const struct ctables_categories *cats,
3900 const struct ctables_category *cat,
3901 const struct variable *var,
3902 const union value *value)
3904 struct substring in = ss_cstr (cat->pc->label);
3905 struct substring target = ss_cstr (")LABEL[");
3907 struct string out = DS_EMPTY_INITIALIZER;
3910 size_t chunk = ss_find_substring (in, target);
3911 if (chunk == SIZE_MAX)
3913 if (ds_is_empty (&out))
3914 return pivot_value_new_user_text (in.string, in.length);
3917 ds_put_substring (&out, in);
3918 return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
3922 ds_put_substring (&out, ss_head (in, chunk));
3923 ss_advance (&in, chunk + target.length);
3925 struct substring idx_s;
3926 if (!ss_get_until (&in, ']', &idx_s))
3929 long int idx = strtol (idx_s.string, &tail, 10);
3930 if (idx < 1 || idx > cats->n_cats || tail != ss_end (idx_s))
3933 struct ctables_category *cat2 = &cats->cats[idx - 1];
3934 struct pivot_value *label2
3935 = ctables_category_create_label__ (cat2, var, value);
3936 char *label2_s = pivot_value_to_string_defaults (label2);
3937 ds_put_cstr (&out, label2_s);
3939 pivot_value_destroy (label2);
3944 return pivot_value_new_user_text (cat->pc->label, SIZE_MAX);
3947 static struct pivot_value *
3948 ctables_category_create_label (const struct ctables_categories *cats,
3949 const struct ctables_category *cat,
3950 const struct variable *var,
3951 const union value *value)
3953 return (cat->type == CCT_POSTCOMPUTE && cat->pc->label
3954 ? ctables_postcompute_label (cats, cat, var, value)
3955 : ctables_category_create_label__ (cat, var, value));
3958 static struct ctables_value *
3959 ctables_value_find__ (struct ctables_table *t, const union value *value,
3960 int width, unsigned int hash)
3962 struct ctables_value *clv;
3963 HMAP_FOR_EACH_WITH_HASH (clv, struct ctables_value, node,
3964 hash, &t->clabels_values_map)
3965 if (value_equal (value, &clv->value, width))
3971 ctables_value_insert (struct ctables_table *t, const union value *value,
3974 unsigned int hash = value_hash (value, width, 0);
3975 struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
3978 clv = xmalloc (sizeof *clv);
3979 value_clone (&clv->value, value, width);
3980 hmap_insert (&t->clabels_values_map, &clv->node, hash);
3984 static struct ctables_value *
3985 ctables_value_find (struct ctables_table *t,
3986 const union value *value, int width)
3988 return ctables_value_find__ (t, value, width,
3989 value_hash (value, width, 0));
3993 ctables_table_add_section (struct ctables_table *t, enum pivot_axis_type a,
3994 size_t ix[PIVOT_N_AXES])
3996 if (a < PIVOT_N_AXES)
3998 size_t limit = MAX (t->stacks[a].n, 1);
3999 for (ix[a] = 0; ix[a] < limit; ix[a]++)
4000 ctables_table_add_section (t, a + 1, ix);
4004 struct ctables_section *s = &t->sections[t->n_sections++];
4005 *s = (struct ctables_section) {
4007 .cells = HMAP_INITIALIZER (s->cells),
4009 for (a = 0; a < PIVOT_N_AXES; a++)
4012 struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
4014 s->occurrences[a] = xnmalloc (nest->n, sizeof *s->occurrences[a]);
4015 for (size_t i = 0; i < nest->n; i++)
4016 hmap_init (&s->occurrences[a][i]);
4018 for (size_t i = 0; i < N_CTDTS; i++)
4019 hmap_init (&s->domains[i]);
4024 ctpo_add (double a, double b)
4030 ctpo_sub (double a, double b)
4036 ctpo_mul (double a, double b)
4042 ctpo_div (double a, double b)
4044 return b ? a / b : SYSMIS;
4048 ctpo_pow (double a, double b)
4050 int save_errno = errno;
4052 double result = pow (a, b);
4060 ctpo_neg (double a, double b UNUSED)
4065 struct ctables_pcexpr_evaluate_ctx
4067 const struct ctables_cell *cell;
4068 const struct ctables_section *section;
4069 const struct ctables_categories *cats;
4070 enum pivot_axis_type pc_a;
4075 static double ctables_pcexpr_evaluate (
4076 const struct ctables_pcexpr_evaluate_ctx *, const struct ctables_pcexpr *);
4079 ctables_pcexpr_evaluate_nonterminal (
4080 const struct ctables_pcexpr_evaluate_ctx *ctx,
4081 const struct ctables_pcexpr *e, size_t n_args,
4082 double evaluate (double, double))
4084 double args[2] = { 0, 0 };
4085 for (size_t i = 0; i < n_args; i++)
4087 args[i] = ctables_pcexpr_evaluate (ctx, e->subs[i]);
4088 if (!isfinite (args[i]) || args[i] == SYSMIS)
4091 return evaluate (args[0], args[1]);
4095 ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
4096 const struct ctables_cell_value *pc_cv)
4098 const struct ctables_section *s = ctx->section;
4101 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4103 const struct ctables_nest *nest = s->nests[a];
4104 for (size_t i = 0; i < nest->n; i++)
4105 if (i != nest->scale_idx)
4107 const struct ctables_cell_value *cv
4108 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
4109 : &ctx->cell->axes[a].cvs[i]);
4110 hash = hash_pointer (cv->category, hash);
4111 if (cv->category->type != CCT_TOTAL
4112 && cv->category->type != CCT_SUBTOTAL
4113 && cv->category->type != CCT_POSTCOMPUTE)
4114 hash = value_hash (&cv->value,
4115 var_get_width (nest->vars[i]), hash);
4119 struct ctables_cell *tc;
4120 HMAP_FOR_EACH_WITH_HASH (tc, struct ctables_cell, node, hash, &s->cells)
4122 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4124 const struct ctables_nest *nest = s->nests[a];
4125 for (size_t i = 0; i < nest->n; i++)
4126 if (i != nest->scale_idx)
4128 const struct ctables_cell_value *p_cv
4129 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
4130 : &ctx->cell->axes[a].cvs[i]);
4131 const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
4132 if (p_cv->category != t_cv->category
4133 || (p_cv->category->type != CCT_TOTAL
4134 && p_cv->category->type != CCT_SUBTOTAL
4135 && p_cv->category->type != CCT_POSTCOMPUTE
4136 && !value_equal (&p_cv->value,
4138 var_get_width (nest->vars[i]))))
4150 const struct ctables_table *t = s->table;
4151 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
4152 const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
4153 return ctables_summary_value (tc, &tc->summaries[ctx->summary_idx],
4154 &specs->specs[ctx->summary_idx]);
4158 ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
4159 const struct ctables_pcexpr *e)
4166 case CTPO_CAT_NRANGE:
4167 case CTPO_CAT_SRANGE:
4169 struct ctables_cell_value cv = {
4170 .category = ctables_find_category_for_postcompute (ctx->cats, e)
4172 assert (cv.category != NULL);
4174 struct hmap *occurrences = &ctx->section->occurrences[ctx->pc_a][ctx->pc_a_idx];
4175 const struct ctables_occurrence *o;
4178 const struct variable *var = ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx];
4179 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
4180 if (ctables_categories_match (ctx->cats, &o->value, var) == cv.category)
4182 cv.value = o->value;
4183 sum += ctables_pcexpr_evaluate_category (ctx, &cv);
4188 case CTPO_CAT_NUMBER:
4189 case CTPO_CAT_STRING:
4190 case CTPO_CAT_MISSING:
4191 case CTPO_CAT_OTHERNM:
4192 case CTPO_CAT_SUBTOTAL:
4193 case CTPO_CAT_TOTAL:
4195 struct ctables_cell_value cv = {
4196 .category = ctables_find_category_for_postcompute (ctx->cats, e),
4197 .value = { .f = e->number },
4199 assert (cv.category != NULL);
4200 return ctables_pcexpr_evaluate_category (ctx, &cv);
4204 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
4207 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_sub);
4210 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_mul);
4213 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_div);
4216 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_pow);
4219 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 1, ctpo_neg);
4225 /* XXX what if there is a postcompute in more than one dimension?? */
4226 static const struct ctables_postcompute *
4227 ctables_cell_postcompute (const struct ctables_section *s,
4228 const struct ctables_cell *cell,
4229 enum pivot_axis_type *pc_a_p,
4232 assert (cell->postcompute);
4233 const struct ctables_postcompute *pc = NULL;
4234 for (enum pivot_axis_type pc_a = 0; pc_a < PIVOT_N_AXES; pc_a++)
4235 for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
4237 const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
4238 if (cv->category->type == CCT_POSTCOMPUTE)
4242 /* Multiple postcomputes cross each other. The value is
4247 pc = cv->category->pc;
4251 *pc_a_idx_p = pc_a_idx;
4255 assert (pc != NULL);
4260 ctables_cell_calculate_postcompute (const struct ctables_section *s,
4261 const struct ctables_cell *cell,
4262 const struct ctables_summary_spec *ss,
4263 struct fmt_spec *format,
4264 bool *is_ctables_format,
4267 enum pivot_axis_type pc_a = 0;
4268 size_t pc_a_idx = 0;
4269 const struct ctables_postcompute *pc = ctables_cell_postcompute (
4270 s, cell, &pc_a, &pc_a_idx);
4276 for (size_t i = 0; i < pc->specs->n; i++)
4278 const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
4279 if (ss->function == ss2->function
4280 && ss->percentile == ss2->percentile)
4282 *format = ss2->format;
4283 *is_ctables_format = ss2->is_ctables_format;
4289 const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
4290 const struct ctables_categories *cats = s->table->categories[
4291 var_get_dict_index (var)];
4292 struct ctables_pcexpr_evaluate_ctx ctx = {
4297 .pc_a_idx = pc_a_idx,
4298 .summary_idx = summary_idx,
4300 return ctables_pcexpr_evaluate (&ctx, pc->expr);
4304 ctables_table_output (struct ctables *ct, struct ctables_table *t)
4306 struct pivot_table *pt = pivot_table_create__ (
4308 ? pivot_value_new_user_text (t->title, SIZE_MAX)
4309 : pivot_value_new_text (N_("Custom Tables"))),
4312 pivot_table_set_caption (
4313 pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
4315 pivot_table_set_corner_text (
4316 pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
4318 bool summary_dimension = (t->summary_axis != t->slabels_axis
4319 || (!t->slabels_visible
4320 && t->summary_specs.n > 1));
4321 if (summary_dimension)
4323 struct pivot_dimension *d = pivot_dimension_create (
4324 pt, t->slabels_axis, N_("Statistics"));
4325 const struct ctables_summary_spec_set *specs = &t->summary_specs;
4326 if (!t->slabels_visible)
4327 d->hide_all_labels = true;
4328 for (size_t i = 0; i < specs->n; i++)
4329 pivot_category_create_leaf (
4330 d->root, ctables_summary_label (&specs->specs[i], t->cilevel));
4333 bool categories_dimension = t->clabels_example != NULL;
4334 if (categories_dimension)
4336 struct pivot_dimension *d = pivot_dimension_create (
4337 pt, t->label_axis[t->clabels_from_axis],
4338 t->clabels_from_axis == PIVOT_AXIS_ROW
4339 ? N_("Row Categories")
4340 : N_("Column Categories"));
4341 const struct variable *var = t->clabels_example;
4342 const struct ctables_categories *c = t->categories[var_get_dict_index (var)];
4343 for (size_t i = 0; i < t->n_clabels_values; i++)
4345 const struct ctables_value *value = t->clabels_values[i];
4346 const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
4347 assert (cat != NULL);
4348 pivot_category_create_leaf (d->root, ctables_category_create_label (
4349 c, cat, t->clabels_example,
4354 pivot_table_set_look (pt, ct->look);
4355 struct pivot_dimension *d[PIVOT_N_AXES];
4356 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4358 static const char *names[] = {
4359 [PIVOT_AXIS_ROW] = N_("Rows"),
4360 [PIVOT_AXIS_COLUMN] = N_("Columns"),
4361 [PIVOT_AXIS_LAYER] = N_("Layers"),
4363 d[a] = (t->axes[a] || a == t->summary_axis
4364 ? pivot_dimension_create (pt, a, names[a])
4369 assert (t->axes[a]);
4371 for (size_t i = 0; i < t->stacks[a].n; i++)
4373 struct ctables_nest *nest = &t->stacks[a].nests[i];
4374 struct ctables_section **sections = xnmalloc (t->n_sections,
4376 size_t n_sections = 0;
4378 size_t n_total_cells = 0;
4379 size_t max_depth = 0;
4380 for (size_t j = 0; j < t->n_sections; j++)
4381 if (t->sections[j].nests[a] == nest)
4383 struct ctables_section *s = &t->sections[j];
4384 sections[n_sections++] = s;
4385 n_total_cells += s->cells.count;
4387 size_t depth = s->nests[a]->n;
4388 max_depth = MAX (depth, max_depth);
4391 struct ctables_cell **sorted = xnmalloc (n_total_cells,
4393 size_t n_sorted = 0;
4395 for (size_t j = 0; j < n_sections; j++)
4397 struct ctables_section *s = sections[j];
4399 struct ctables_cell *cell;
4400 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4402 sorted[n_sorted++] = cell;
4403 assert (n_sorted <= n_total_cells);
4406 struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
4407 sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
4410 for (size_t j = 0; j < n_sorted; j++)
4412 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);
4417 struct ctables_level
4419 enum ctables_level_type
4421 CTL_VAR, /* Variable label for nest->vars[var_idx]. */
4422 CTL_CATEGORY, /* Category for nest->vars[var_idx]. */
4423 CTL_SUMMARY, /* Summary functions. */
4427 enum settings_value_show vlabel; /* CTL_VAR only. */
4430 struct ctables_level *levels = xnmalloc (1 + 2 * max_depth, sizeof *levels);
4431 size_t n_levels = 0;
4432 for (size_t k = 0; k < nest->n; k++)
4434 enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k])];
4435 if (vlabel != CTVL_NONE)
4437 levels[n_levels++] = (struct ctables_level) {
4439 .vlabel = (enum settings_value_show) vlabel,
4444 if (nest->scale_idx != k
4445 && (k != nest->n - 1 || t->label_axis[a] == a))
4447 levels[n_levels++] = (struct ctables_level) {
4448 .type = CTL_CATEGORY,
4454 if (!summary_dimension && a == t->slabels_axis)
4456 levels[n_levels++] = (struct ctables_level) {
4457 .type = CTL_SUMMARY,
4458 .var_idx = SIZE_MAX,
4462 /* Pivot categories:
4464 - variable label for nest->vars[0], if vlabel != CTVL_NONE
4465 - category for nest->vars[0], if nest->scale_idx != 0
4466 - variable label for nest->vars[1], if vlabel != CTVL_NONE
4467 - category for nest->vars[1], if nest->scale_idx != 1
4469 - variable label for nest->vars[n - 1], if vlabel != CTVL_NONE
4470 - category for nest->vars[n - 1], if t->label_axis[a] == a && nest->scale_idx != n - 1.
4471 - summary function, if 'a == t->slabels_axis && a ==
4474 Additional dimensions:
4476 - If 'a == t->slabels_axis && a != t->summary_axis', add a summary
4478 - If 't->label_axis[b] == a' for some 'b != a', add a category
4483 struct pivot_category **groups = xnmalloc (1 + 2 * max_depth, sizeof *groups);
4485 for (size_t j = 0; j < n_sorted; j++)
4487 struct ctables_cell *cell = sorted[j];
4488 struct ctables_cell *prev = j > 0 ? sorted[j - 1] : NULL;
4490 size_t n_common = 0;
4493 for (; n_common < n_levels; n_common++)
4495 const struct ctables_level *level = &levels[n_common];
4496 if (level->type == CTL_CATEGORY)
4498 size_t var_idx = level->var_idx;
4499 const struct ctables_category *c = cell->axes[a].cvs[var_idx].category;
4500 if (prev->axes[a].cvs[var_idx].category != c)
4502 else if (c->type != CCT_SUBTOTAL
4503 && c->type != CCT_TOTAL
4504 && c->type != CCT_POSTCOMPUTE
4505 && !value_equal (&prev->axes[a].cvs[var_idx].value,
4506 &cell->axes[a].cvs[var_idx].value,
4507 var_get_type (nest->vars[var_idx])))
4513 for (size_t k = n_common; k < n_levels; k++)
4515 const struct ctables_level *level = &levels[k];
4516 struct pivot_category *parent = k ? groups[k - 1] : d[a]->root;
4517 if (level->type == CTL_SUMMARY)
4519 assert (k == n_levels - 1);
4521 const struct ctables_summary_spec_set *specs = &t->summary_specs;
4522 for (size_t m = 0; m < specs->n; m++)
4524 int leaf = pivot_category_create_leaf (
4525 parent, ctables_summary_label (&specs->specs[m],
4533 const struct variable *var = nest->vars[level->var_idx];
4534 struct pivot_value *label;
4535 if (level->type == CTL_VAR)
4537 label = pivot_value_new_variable (var);
4538 label->variable.show = level->vlabel;
4540 else if (level->type == CTL_CATEGORY)
4542 const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
4543 label = ctables_category_create_label (
4544 t->categories[var_get_dict_index (var)],
4545 cv->category, var, &cv->value);
4550 if (k == n_levels - 1)
4551 prev_leaf = pivot_category_create_leaf (parent, label);
4553 groups[k] = pivot_category_create_group__ (parent, label);
4557 cell->axes[a].leaf = prev_leaf;
4564 for (size_t i = 0; i < t->n_sections; i++)
4566 struct ctables_section *s = &t->sections[i];
4568 struct ctables_cell *cell;
4569 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4574 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
4575 const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
4576 for (size_t j = 0; j < specs->n; j++)
4579 size_t n_dindexes = 0;
4581 if (summary_dimension)
4582 dindexes[n_dindexes++] = specs->specs[j].axis_idx;
4584 if (categories_dimension)
4586 const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
4587 const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
4588 const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
4589 const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
4592 dindexes[n_dindexes++] = ctv->leaf;
4595 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4598 int leaf = cell->axes[a].leaf;
4599 if (a == t->summary_axis && !summary_dimension)
4601 dindexes[n_dindexes++] = leaf;
4604 const struct ctables_summary_spec *ss = &specs->specs[j];
4606 struct fmt_spec format = specs->specs[j].format;
4607 bool is_ctables_format = ss->is_ctables_format;
4608 double d = (cell->postcompute
4609 ? ctables_cell_calculate_postcompute (
4610 s, cell, ss, &format, &is_ctables_format, j)
4611 : ctables_summary_value (cell, &cell->summaries[j],
4614 struct pivot_value *value;
4615 if (ct->hide_threshold != 0
4616 && d < ct->hide_threshold
4617 && ctables_summary_function_is_count (ss->function))
4619 value = pivot_value_new_user_text_nocopy (
4620 xasprintf ("<%d", ct->hide_threshold));
4622 else if (d == 0 && ct->zero)
4623 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
4624 else if (d == SYSMIS && ct->missing)
4625 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
4626 else if (is_ctables_format)
4628 char *s = data_out_stretchy (&(union value) { .f = d },
4630 &ct->ctables_formats, NULL);
4631 value = pivot_value_new_user_text_nocopy (s);
4635 value = pivot_value_new_number (d);
4636 value->numeric.format = format;
4638 pivot_table_put (pt, dindexes, n_dindexes, value);
4643 pivot_table_submit (pt);
4647 ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
4649 enum pivot_axis_type label_pos = t->label_axis[a];
4653 t->clabels_from_axis = a;
4655 const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
4656 const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
4658 const struct ctables_stack *stack = &t->stacks[a];
4662 const struct ctables_nest *n0 = &stack->nests[0];
4664 const struct variable *v0 = n0->vars[n0->n - 1];
4665 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4666 t->clabels_example = v0;
4668 for (size_t i = 0; i < c0->n_cats; i++)
4669 if (c0->cats[i].type == CCT_FUNCTION)
4671 msg (SE, _("%s=%s is not allowed with sorting based "
4672 "on a summary function."),
4673 subcommand_name, pos_name);
4676 if (n0->n - 1 == n0->scale_idx)
4678 msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
4679 "but %s is a scale variable."),
4680 subcommand_name, pos_name, var_get_name (v0));
4684 for (size_t i = 1; i < stack->n; i++)
4686 const struct ctables_nest *ni = &stack->nests[i];
4688 const struct variable *vi = ni->vars[ni->n - 1];
4689 struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
4691 if (ni->n - 1 == ni->scale_idx)
4693 msg (SE, _("%s=%s requires the variables to be moved to be "
4694 "categorical, but %s is a scale variable."),
4695 subcommand_name, pos_name, var_get_name (vi));
4698 if (var_get_width (v0) != var_get_width (vi))
4700 msg (SE, _("%s=%s requires the variables to be "
4701 "moved to have the same width, but %s has "
4702 "width %d and %s has width %d."),
4703 subcommand_name, pos_name,
4704 var_get_name (v0), var_get_width (v0),
4705 var_get_name (vi), var_get_width (vi));
4708 if (!val_labs_equal (var_get_value_labels (v0),
4709 var_get_value_labels (vi)))
4711 msg (SE, _("%s=%s requires the variables to be "
4712 "moved to have the same value labels, but %s "
4713 "and %s have different value labels."),
4714 subcommand_name, pos_name,
4715 var_get_name (v0), var_get_name (vi));
4718 if (!ctables_categories_equal (c0, ci))
4720 msg (SE, _("%s=%s requires the variables to be "
4721 "moved to have the same category "
4722 "specifications, but %s and %s have different "
4723 "category specifications."),
4724 subcommand_name, pos_name,
4725 var_get_name (v0), var_get_name (vi));
4734 add_sum_var (struct variable *var,
4735 struct variable ***sum_vars, size_t *n, size_t *allocated)
4737 for (size_t i = 0; i < *n; i++)
4738 if (var == (*sum_vars)[i])
4741 if (*n >= *allocated)
4742 *sum_vars = x2nrealloc (*sum_vars, allocated, sizeof **sum_vars);
4743 (*sum_vars)[*n] = var;
4748 enumerate_sum_vars (const struct ctables_axis *a,
4749 struct variable ***sum_vars, size_t *n, size_t *allocated)
4757 for (size_t i = 0; i < N_CSVS; i++)
4758 for (size_t j = 0; j < a->specs[i].n; j++)
4760 struct ctables_summary_spec *spec = &a->specs[i].specs[j];
4761 if (ctables_function_is_pctsum (spec->function))
4762 spec->sum_var_idx = add_sum_var (a->var, sum_vars, n, allocated);
4768 for (size_t i = 0; i < 2; i++)
4769 enumerate_sum_vars (a->subs[i], sum_vars, n, allocated);
4775 ctables_prepare_table (struct ctables_table *t)
4777 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4780 t->stacks[a] = enumerate_fts (a, t->axes[a]);
4782 for (size_t j = 0; j < t->stacks[a].n; j++)
4784 struct ctables_nest *nest = &t->stacks[a].nests[j];
4785 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
4787 nest->domains[dt] = xmalloc (nest->n * sizeof *nest->domains[dt]);
4788 nest->n_domains[dt] = 0;
4790 for (size_t k = 0; k < nest->n; k++)
4792 if (k == nest->scale_idx)
4801 if (a != PIVOT_AXIS_LAYER)
4808 if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
4809 : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
4810 : a == PIVOT_AXIS_ROW)
4812 if (k == nest->n - 1
4813 || (nest->scale_idx == nest->n - 1
4814 && k == nest->n - 2))
4820 if (a == PIVOT_AXIS_COLUMN)
4825 if (a == PIVOT_AXIS_ROW)
4830 nest->domains[dt][nest->n_domains[dt]++] = k;
4837 struct ctables_nest *nest = xmalloc (sizeof *nest);
4838 *nest = (struct ctables_nest) { .n = 0 };
4839 t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
4842 struct ctables_stack *stack = &t->stacks[t->summary_axis];
4843 for (size_t i = 0; i < stack->n; i++)
4845 struct ctables_nest *nest = &stack->nests[i];
4846 if (!nest->specs[CSV_CELL].n)
4848 struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
4849 specs->specs = xmalloc (sizeof *specs->specs);
4852 enum ctables_summary_function function
4853 = specs->is_scale ? CTSF_MEAN : CTSF_COUNT;
4855 *specs->specs = (struct ctables_summary_spec) {
4856 .function = function,
4857 .format = ctables_summary_default_format (function, specs->var),
4860 specs->var = nest->vars[0];
4862 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4863 &nest->specs[CSV_CELL]);
4865 else if (!nest->specs[CSV_TOTAL].n)
4866 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4867 &nest->specs[CSV_CELL]);
4869 if (t->ctables->smissing_listwise)
4871 struct variable **listwise_vars = NULL;
4873 size_t allocated = 0;
4875 for (size_t j = nest->group_head; j < stack->n; j++)
4877 const struct ctables_nest *other_nest = &stack->nests[j];
4878 if (other_nest->group_head != nest->group_head)
4881 if (nest != other_nest && other_nest->scale_idx < other_nest->n)
4884 listwise_vars = x2nrealloc (listwise_vars, &allocated,
4885 sizeof *listwise_vars);
4886 listwise_vars[n++] = other_nest->vars[other_nest->scale_idx];
4889 for (size_t j = 0; j < N_CSVS; j++)
4891 nest->specs[j].listwise_vars = listwise_vars;
4892 nest->specs[j].n_listwise_vars = n;
4897 struct ctables_summary_spec_set *merged = &t->summary_specs;
4898 struct merge_item *items = xnmalloc (N_CSVS * stack->n, sizeof *items);
4900 for (size_t j = 0; j < stack->n; j++)
4902 const struct ctables_nest *nest = &stack->nests[j];
4904 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4905 items[n_left++] = (struct merge_item) { .set = &nest->specs[sv] };
4910 struct merge_item min = items[0];
4911 for (size_t j = 1; j < n_left; j++)
4912 if (merge_item_compare_3way (&items[j], &min) < 0)
4915 if (merged->n >= merged->allocated)
4916 merged->specs = x2nrealloc (merged->specs, &merged->allocated,
4917 sizeof *merged->specs);
4918 merged->specs[merged->n++] = min.set->specs[min.ofs];
4920 for (size_t j = 0; j < n_left; )
4922 if (merge_item_compare_3way (&items[j], &min) == 0)
4924 struct merge_item *item = &items[j];
4925 item->set->specs[item->ofs].axis_idx = merged->n - 1;
4926 if (++item->ofs >= item->set->n)
4928 items[j] = items[--n_left];
4937 for (size_t j = 0; j < merged->n; j++)
4938 printf ("%s\n", ctables_summary_function_name (merged->specs[j].function));
4940 for (size_t j = 0; j < stack->n; j++)
4942 const struct ctables_nest *nest = &stack->nests[j];
4943 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4945 const struct ctables_summary_spec_set *specs = &nest->specs[sv];
4946 for (size_t k = 0; k < specs->n; k++)
4947 printf ("(%s, %zu) ", ctables_summary_function_name (specs->specs[k].function),
4948 specs->specs[k].axis_idx);
4954 size_t allocated_sum_vars = 0;
4955 enumerate_sum_vars (t->axes[t->summary_axis],
4956 &t->sum_vars, &t->n_sum_vars, &allocated_sum_vars);
4958 return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
4959 && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
4963 ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
4964 enum pivot_axis_type a)
4966 struct ctables_stack *stack = &t->stacks[a];
4967 for (size_t i = 0; i < stack->n; i++)
4969 const struct ctables_nest *nest = &stack->nests[i];
4970 const struct variable *var = nest->vars[nest->n - 1];
4971 const union value *value = case_data (c, var);
4973 if (var_is_numeric (var) && value->f == SYSMIS)
4976 if (ctables_categories_match (t->categories [var_get_dict_index (var)],
4978 ctables_value_insert (t, value, var_get_width (var));
4983 compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
4985 const struct ctables_value *const *ap = a_;
4986 const struct ctables_value *const *bp = b_;
4987 const struct ctables_value *a = *ap;
4988 const struct ctables_value *b = *bp;
4989 const int *width = width_;
4990 return value_compare_3way (&a->value, &b->value, *width);
4994 ctables_sort_clabels_values (struct ctables_table *t)
4996 const struct variable *v0 = t->clabels_example;
4997 int width = var_get_width (v0);
4999 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
5002 const struct val_labs *val_labs = var_get_value_labels (v0);
5003 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5004 vl = val_labs_next (val_labs, vl))
5005 if (ctables_categories_match (c0, &vl->value, v0))
5006 ctables_value_insert (t, &vl->value, width);
5009 size_t n = hmap_count (&t->clabels_values_map);
5010 t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
5012 struct ctables_value *clv;
5014 HMAP_FOR_EACH (clv, struct ctables_value, node, &t->clabels_values_map)
5015 t->clabels_values[i++] = clv;
5016 t->n_clabels_values = n;
5019 sort (t->clabels_values, n, sizeof *t->clabels_values,
5020 compare_clabels_values_3way, &width);
5022 for (size_t i = 0; i < n; i++)
5023 t->clabels_values[i]->leaf = i;
5027 ctables_add_category_occurrences (const struct variable *var,
5028 struct hmap *occurrences,
5029 const struct ctables_categories *cats)
5031 const struct val_labs *val_labs = var_get_value_labels (var);
5033 for (size_t i = 0; i < cats->n_cats; i++)
5035 const struct ctables_category *c = &cats->cats[i];
5039 ctables_add_occurrence (var, &(const union value) { .f = c->number },
5045 int width = var_get_width (var);
5047 value_init (&value, width);
5048 value_copy_buf_rpad (&value, width,
5049 CHAR_CAST (uint8_t *, c->string.string),
5050 c->string.length, ' ');
5051 ctables_add_occurrence (var, &value, occurrences);
5052 value_destroy (&value, width);
5057 assert (var_is_numeric (var));
5058 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5059 vl = val_labs_next (val_labs, vl))
5060 if (vl->value.f >= c->nrange[0] && vl->value.f <= c->nrange[1])
5061 ctables_add_occurrence (var, &vl->value, occurrences);
5065 assert (var_is_alpha (var));
5066 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5067 vl = val_labs_next (val_labs, vl))
5068 if (in_string_range (&vl->value, var, c->srange))
5069 ctables_add_occurrence (var, &vl->value, occurrences);
5073 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5074 vl = val_labs_next (val_labs, vl))
5075 if (var_is_value_missing (var, &vl->value))
5076 ctables_add_occurrence (var, &vl->value, occurrences);
5080 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5081 vl = val_labs_next (val_labs, vl))
5082 ctables_add_occurrence (var, &vl->value, occurrences);
5085 case CCT_POSTCOMPUTE:
5095 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
5096 vl = val_labs_next (val_labs, vl))
5097 if (c->include_missing || !var_is_value_missing (var, &vl->value))
5098 ctables_add_occurrence (var, &vl->value, occurrences);
5101 case CCT_EXCLUDED_MISSING:
5108 ctables_section_recurse_add_empty_categories (
5109 struct ctables_section *s,
5110 const struct ctables_category *cats[PIVOT_N_AXES][10], struct ccase *c,
5111 enum pivot_axis_type a, size_t a_idx)
5113 if (a >= PIVOT_N_AXES)
5114 ctables_cell_insert__ (s, c, cats);
5115 else if (!s->nests[a] || a_idx >= s->nests[a]->n)
5116 ctables_section_recurse_add_empty_categories (s, cats, c, a + 1, 0);
5119 const struct variable *var = s->nests[a]->vars[a_idx];
5120 const struct ctables_categories *categories = s->table->categories[
5121 var_get_dict_index (var)];
5122 int width = var_get_width (var);
5123 const struct hmap *occurrences = &s->occurrences[a][a_idx];
5124 const struct ctables_occurrence *o;
5125 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
5127 union value *value = case_data_rw (c, var);
5128 value_destroy (value, width);
5129 value_clone (value, &o->value, width);
5130 cats[a][a_idx] = ctables_categories_match (categories, value, var);
5131 assert (cats[a][a_idx] != NULL);
5132 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
5135 for (size_t i = 0; i < categories->n_cats; i++)
5137 const struct ctables_category *cat = &categories->cats[i];
5138 if (cat->type == CCT_POSTCOMPUTE)
5140 cats[a][a_idx] = cat;
5141 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
5148 ctables_section_add_empty_categories (struct ctables_section *s)
5150 bool show_empty = false;
5151 for (size_t a = 0; a < PIVOT_N_AXES; a++)
5153 for (size_t k = 0; k < s->nests[a]->n; k++)
5154 if (k != s->nests[a]->scale_idx)
5156 const struct variable *var = s->nests[a]->vars[k];
5157 const struct ctables_categories *cats = s->table->categories[
5158 var_get_dict_index (var)];
5159 if (cats->show_empty)
5162 ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
5168 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
5169 struct ccase *c = case_create (dict_get_proto (s->table->ctables->dict));
5170 ctables_section_recurse_add_empty_categories (s, cats, c, 0, 0);
5175 ctables_section_clear (struct ctables_section *s)
5177 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5179 const struct ctables_nest *nest = s->nests[a];
5180 for (size_t i = 0; i < nest->n; i++)
5181 if (i != nest->scale_idx)
5183 const struct variable *var = nest->vars[i];
5184 int width = var_get_width (var);
5185 struct ctables_occurrence *o, *next;
5186 struct hmap *map = &s->occurrences[a][i];
5187 HMAP_FOR_EACH_SAFE (o, next, struct ctables_occurrence, node, map)
5189 value_destroy (&o->value, width);
5190 hmap_delete (map, &o->node);
5197 struct ctables_cell *cell, *next_cell;
5198 HMAP_FOR_EACH_SAFE (cell, next_cell, struct ctables_cell, node, &s->cells)
5200 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5202 const struct ctables_nest *nest = s->nests[a];
5203 for (size_t i = 0; i < nest->n; i++)
5204 if (i != nest->scale_idx)
5205 value_destroy (&cell->axes[a].cvs[i].value,
5206 var_get_width (nest->vars[i]));
5207 free (cell->axes[a].cvs);
5210 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
5211 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
5212 for (size_t i = 0; i < specs->n; i++)
5213 ctables_summary_uninit (&cell->summaries[i], &specs->specs[i]);
5214 free (cell->summaries);
5216 hmap_delete (&s->cells, &cell->node);
5219 hmap_shrink (&s->cells);
5221 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
5223 struct ctables_domain *domain, *next_domain;
5224 HMAP_FOR_EACH_SAFE (domain, next_domain, struct ctables_domain, node,
5227 free (domain->sums);
5228 hmap_delete (&s->domains[dt], &domain->node);
5231 hmap_shrink (&s->domains[dt]);
5236 ctables_table_clear (struct ctables_table *t)
5238 for (size_t i = 0; i < t->n_sections; i++)
5239 ctables_section_clear (&t->sections[i]);
5241 if (t->clabels_example)
5243 int width = var_get_width (t->clabels_example);
5244 struct ctables_value *value, *next_value;
5245 HMAP_FOR_EACH_SAFE (value, next_value, struct ctables_value, node,
5246 &t->clabels_values_map)
5248 value_destroy (&value->value, width);
5249 hmap_delete (&t->clabels_values_map, &value->node);
5252 hmap_shrink (&t->clabels_values_map);
5254 free (t->clabels_values);
5255 t->clabels_values = NULL;
5256 t->n_clabels_values = 0;
5261 ctables_execute (struct dataset *ds, struct ctables *ct)
5263 for (size_t i = 0; i < ct->n_tables; i++)
5265 struct ctables_table *t = ct->tables[i];
5266 t->sections = xnmalloc (MAX (1, t->stacks[PIVOT_AXIS_ROW].n) *
5267 MAX (1, t->stacks[PIVOT_AXIS_COLUMN].n) *
5268 MAX (1, t->stacks[PIVOT_AXIS_LAYER].n),
5269 sizeof *t->sections);
5270 size_t ix[PIVOT_N_AXES];
5271 ctables_table_add_section (t, 0, ix);
5274 struct dictionary *dict = dataset_dict (ds);
5275 struct casereader *input = proc_open (ds);
5276 struct casegrouper *grouper
5277 = (dict_get_split_type (dict) == SPLIT_SEPARATE
5278 ? casegrouper_create_splits (input, dict)
5279 : casegrouper_create_vars (input, NULL, 0));
5280 struct casereader *group;
5281 while (casegrouper_get_next_group (grouper, &group))
5283 /* Output SPLIT FILE variables. */
5284 struct ccase *c = casereader_peek (group, 0);
5287 output_split_file_values (ds, c);
5291 bool warn_on_invalid = true;
5292 for (c = casereader_read (group); c;
5293 case_unref (c), c = casereader_read (group))
5295 double d_weight = dict_get_case_weight (dict, c, &warn_on_invalid);
5296 double e_weight = (ct->e_weight
5297 ? var_force_valid_weight (ct->e_weight,
5298 case_num (c, ct->e_weight),
5302 for (size_t i = 0; i < ct->n_tables; i++)
5304 struct ctables_table *t = ct->tables[i];
5306 for (size_t j = 0; j < t->n_sections; j++)
5307 ctables_cell_insert (&t->sections[j], c, d_weight, e_weight);
5309 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5310 if (t->label_axis[a] != a)
5311 ctables_insert_clabels_values (t, c, a);
5314 casereader_destroy (group);
5316 for (size_t i = 0; i < ct->n_tables; i++)
5318 struct ctables_table *t = ct->tables[i];
5320 if (t->clabels_example)
5321 ctables_sort_clabels_values (t);
5323 for (size_t j = 0; j < t->n_sections; j++)
5324 ctables_section_add_empty_categories (&t->sections[j]);
5326 ctables_table_output (ct, t);
5327 ctables_table_clear (t);
5330 bool ok = casegrouper_destroy (grouper);
5331 return proc_commit (ds) && ok;
5336 typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *,
5337 struct dictionary *);
5340 ctables_pcexpr_destroy (struct ctables_pcexpr *e)
5346 case CTPO_CAT_STRING:
5347 ss_dealloc (&e->string);
5350 case CTPO_CAT_SRANGE:
5351 for (size_t i = 0; i < 2; i++)
5352 ss_dealloc (&e->srange[i]);
5361 for (size_t i = 0; i < 2; i++)
5362 ctables_pcexpr_destroy (e->subs[i]);
5366 case CTPO_CAT_NUMBER:
5367 case CTPO_CAT_NRANGE:
5368 case CTPO_CAT_MISSING:
5369 case CTPO_CAT_OTHERNM:
5370 case CTPO_CAT_SUBTOTAL:
5371 case CTPO_CAT_TOTAL:
5375 msg_location_destroy (e->location);
5380 static struct ctables_pcexpr *
5381 ctables_pcexpr_allocate_binary (enum ctables_postcompute_op op,
5382 struct ctables_pcexpr *sub0,
5383 struct ctables_pcexpr *sub1)
5385 struct ctables_pcexpr *e = xmalloc (sizeof *e);
5386 *e = (struct ctables_pcexpr) {
5388 .subs = { sub0, sub1 },
5389 .location = msg_location_merged (sub0->location, sub1->location),
5394 /* How to parse an operator. */
5397 enum token_type token;
5398 enum ctables_postcompute_op op;
5401 static const struct operator *
5402 ctable_pcexpr_match_operator (struct lexer *lexer,
5403 const struct operator ops[], size_t n_ops)
5405 for (const struct operator *op = ops; op < ops + n_ops; op++)
5406 if (lex_token (lexer) == op->token)
5408 if (op->token != T_NEG_NUM)
5417 static struct ctables_pcexpr *
5418 ctable_pcexpr_parse_binary_operators__ (
5419 struct lexer *lexer, struct dictionary *dict,
5420 const struct operator ops[], size_t n_ops,
5421 parse_recursively_func *parse_next_level,
5422 const char *chain_warning, struct ctables_pcexpr *lhs)
5424 for (int op_count = 0; ; op_count++)
5426 const struct operator *op
5427 = ctable_pcexpr_match_operator (lexer, ops, n_ops);
5430 if (op_count > 1 && chain_warning)
5431 msg_at (SW, lhs->location, "%s", chain_warning);
5436 struct ctables_pcexpr *rhs = parse_next_level (lexer, dict);
5439 ctables_pcexpr_destroy (lhs);
5443 lhs = ctables_pcexpr_allocate_binary (op->op, lhs, rhs);
5447 static struct ctables_pcexpr *
5448 ctable_pcexpr_parse_binary_operators (struct lexer *lexer,
5449 struct dictionary *dict,
5450 const struct operator ops[], size_t n_ops,
5451 parse_recursively_func *parse_next_level,
5452 const char *chain_warning)
5454 struct ctables_pcexpr *lhs = parse_next_level (lexer, dict);
5458 return ctable_pcexpr_parse_binary_operators__ (lexer, dict, ops, n_ops,
5460 chain_warning, lhs);
5463 static struct ctables_pcexpr *ctable_pcexpr_parse_add (struct lexer *,
5464 struct dictionary *);
5466 static struct ctables_pcexpr
5467 ctpo_cat_nrange (double low, double high)
5469 return (struct ctables_pcexpr) {
5470 .op = CTPO_CAT_NRANGE,
5471 .nrange = { low, high },
5475 static struct ctables_pcexpr *
5476 ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
5478 int start_ofs = lex_ofs (lexer);
5479 struct ctables_pcexpr e;
5480 if (lex_is_number (lexer))
5482 e = (struct ctables_pcexpr) { .op = CTPO_CONSTANT,
5483 .number = lex_number (lexer) };
5486 else if (lex_match_id (lexer, "MISSING"))
5487 e = (struct ctables_pcexpr) { .op = CTPO_CAT_MISSING };
5488 else if (lex_match_id (lexer, "OTHERNM"))
5489 e = (struct ctables_pcexpr) { .op = CTPO_CAT_OTHERNM };
5490 else if (lex_match_id (lexer, "TOTAL"))
5491 e = (struct ctables_pcexpr) { .op = CTPO_CAT_TOTAL };
5492 else if (lex_match_id (lexer, "SUBTOTAL"))
5494 size_t subtotal_index = 0;
5495 if (lex_match (lexer, T_LBRACK))
5497 if (!lex_force_int_range (lexer, "SUBTOTAL", 1, LONG_MAX))
5499 subtotal_index = lex_integer (lexer);
5501 if (!lex_force_match (lexer, T_RBRACK))
5504 e = (struct ctables_pcexpr) { .op = CTPO_CAT_SUBTOTAL,
5505 .subtotal_index = subtotal_index };
5507 else if (lex_match (lexer, T_LBRACK))
5509 if (lex_match_id (lexer, "LO"))
5511 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
5513 e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
5516 else if (lex_is_number (lexer))
5518 double number = lex_number (lexer);
5520 if (lex_match_id (lexer, "THRU"))
5522 if (lex_match_id (lexer, "HI"))
5523 e = ctpo_cat_nrange (number, DBL_MAX);
5526 if (!lex_force_num (lexer))
5528 e = ctpo_cat_nrange (number, lex_number (lexer));
5533 e = (struct ctables_pcexpr) { .op = CTPO_CAT_NUMBER,
5536 else if (lex_is_string (lexer))
5538 struct substring s = recode_substring_pool (
5539 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
5540 ss_rtrim (&s, ss_cstr (" "));
5542 e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
5547 lex_error (lexer, NULL);
5551 if (!lex_force_match (lexer, T_RBRACK))
5553 if (e.op == CTPO_CAT_STRING)
5554 ss_dealloc (&e.string);
5558 else if (lex_match (lexer, T_LPAREN))
5560 struct ctables_pcexpr *ep = ctable_pcexpr_parse_add (lexer, dict);
5563 if (!lex_force_match (lexer, T_RPAREN))
5565 ctables_pcexpr_destroy (ep);
5572 lex_error (lexer, NULL);
5576 e.location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
5577 return xmemdup (&e, sizeof e);
5580 static struct ctables_pcexpr *
5581 ctables_pcexpr_allocate_neg (struct ctables_pcexpr *sub,
5582 struct lexer *lexer, int start_ofs)
5584 struct ctables_pcexpr *e = xmalloc (sizeof *e);
5585 *e = (struct ctables_pcexpr) {
5588 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
5593 static struct ctables_pcexpr *
5594 ctable_pcexpr_parse_exp (struct lexer *lexer, struct dictionary *dict)
5596 static const struct operator op = { T_EXP, CTPO_POW };
5598 const char *chain_warning =
5599 _("The exponentiation operator (`**') is left-associative: "
5600 "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
5601 "To disable this warning, insert parentheses.");
5603 if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
5604 return ctable_pcexpr_parse_binary_operators (lexer, dict, &op, 1,
5605 ctable_pcexpr_parse_primary,
5608 /* Special case for situations like "-5**6", which must be parsed as
5611 int start_ofs = lex_ofs (lexer);
5612 struct ctables_pcexpr *lhs = xmalloc (sizeof *lhs);
5613 *lhs = (struct ctables_pcexpr) {
5614 .op = CTPO_CONSTANT,
5615 .number = -lex_tokval (lexer),
5616 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer)),
5620 struct ctables_pcexpr *node = ctable_pcexpr_parse_binary_operators__ (
5621 lexer, dict, &op, 1,
5622 ctable_pcexpr_parse_primary, chain_warning, lhs);
5626 return ctables_pcexpr_allocate_neg (node, lexer, start_ofs);
5629 /* Parses the unary minus level. */
5630 static struct ctables_pcexpr *
5631 ctable_pcexpr_parse_neg (struct lexer *lexer, struct dictionary *dict)
5633 int start_ofs = lex_ofs (lexer);
5634 if (!lex_match (lexer, T_DASH))
5635 return ctable_pcexpr_parse_exp (lexer, dict);
5637 struct ctables_pcexpr *inner = ctable_pcexpr_parse_neg (lexer, dict);
5641 return ctables_pcexpr_allocate_neg (inner, lexer, start_ofs);
5644 /* Parses the multiplication and division level. */
5645 static struct ctables_pcexpr *
5646 ctable_pcexpr_parse_mul (struct lexer *lexer, struct dictionary *dict)
5648 static const struct operator ops[] =
5650 { T_ASTERISK, CTPO_MUL },
5651 { T_SLASH, CTPO_DIV },
5654 return ctable_pcexpr_parse_binary_operators (lexer, dict, ops,
5655 sizeof ops / sizeof *ops,
5656 ctable_pcexpr_parse_neg, NULL);
5659 /* Parses the addition and subtraction level. */
5660 static struct ctables_pcexpr *
5661 ctable_pcexpr_parse_add (struct lexer *lexer, struct dictionary *dict)
5663 static const struct operator ops[] =
5665 { T_PLUS, CTPO_ADD },
5666 { T_DASH, CTPO_SUB },
5667 { T_NEG_NUM, CTPO_ADD },
5670 return ctable_pcexpr_parse_binary_operators (lexer, dict,
5671 ops, sizeof ops / sizeof *ops,
5672 ctable_pcexpr_parse_mul, NULL);
5675 static struct ctables_postcompute *
5676 ctables_find_postcompute (struct ctables *ct, const char *name)
5678 struct ctables_postcompute *pc;
5679 HMAP_FOR_EACH_WITH_HASH (pc, struct ctables_postcompute, hmap_node,
5680 utf8_hash_case_string (name, 0), &ct->postcomputes)
5681 if (!utf8_strcasecmp (pc->name, name))
5687 ctables_parse_pcompute (struct lexer *lexer, struct dictionary *dict,
5690 int pcompute_start = lex_ofs (lexer) - 1;
5692 if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
5695 char *name = ss_xstrdup (lex_tokss (lexer));
5698 if (!lex_force_match (lexer, T_EQUALS)
5699 || !lex_force_match_id (lexer, "EXPR")
5700 || !lex_force_match (lexer, T_LPAREN))
5706 int expr_start = lex_ofs (lexer);
5707 struct ctables_pcexpr *expr = ctable_pcexpr_parse_add (lexer, dict);
5708 int expr_end = lex_ofs (lexer) - 1;
5709 if (!expr || !lex_force_match (lexer, T_RPAREN))
5714 int pcompute_end = lex_ofs (lexer) - 1;
5716 struct msg_location *location = lex_ofs_location (lexer, pcompute_start,
5719 struct ctables_postcompute *pc = ctables_find_postcompute (ct, name);
5722 msg_at (SW, location, _("New definition of &%s will override the "
5723 "previous definition."),
5725 msg_at (SN, pc->location, _("This is the previous definition."));
5727 ctables_pcexpr_destroy (pc->expr);
5728 msg_location_destroy (pc->location);
5733 pc = xmalloc (sizeof *pc);
5734 *pc = (struct ctables_postcompute) { .name = name };
5735 hmap_insert (&ct->postcomputes, &pc->hmap_node,
5736 utf8_hash_case_string (pc->name, 0));
5739 pc->location = location;
5741 pc->label = lex_ofs_representation (lexer, expr_start, expr_end);
5746 ctables_parse_pproperties_format (struct lexer *lexer,
5747 struct ctables_summary_spec_set *sss)
5749 *sss = (struct ctables_summary_spec_set) { .n = 0 };
5751 while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH
5752 && !(lex_token (lexer) == T_ID
5753 && (lex_id_match (ss_cstr ("LABEL"), lex_tokss (lexer))
5754 || lex_id_match (ss_cstr ("HIDESOURCECATS"),
5755 lex_tokss (lexer)))))
5757 /* Parse function. */
5758 enum ctables_summary_function function;
5759 if (!parse_ctables_summary_function (lexer, &function))
5762 /* Parse percentile. */
5763 double percentile = 0;
5764 if (function == CTSF_PTILE)
5766 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
5768 percentile = lex_number (lexer);
5773 struct fmt_spec format;
5774 bool is_ctables_format;
5775 if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
5778 if (sss->n >= sss->allocated)
5779 sss->specs = x2nrealloc (sss->specs, &sss->allocated,
5780 sizeof *sss->specs);
5781 sss->specs[sss->n++] = (struct ctables_summary_spec) {
5782 .function = function,
5783 .percentile = percentile,
5785 .is_ctables_format = is_ctables_format,
5791 ctables_summary_spec_set_uninit (sss);
5796 ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
5798 struct ctables_postcompute **pcs = NULL;
5800 size_t allocated_pcs = 0;
5802 while (lex_match (lexer, T_AND))
5804 if (!lex_force_id (lexer))
5806 struct ctables_postcompute *pc
5807 = ctables_find_postcompute (ct, lex_tokcstr (lexer));
5810 msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
5815 if (n_pcs >= allocated_pcs)
5816 pcs = x2nrealloc (pcs, &allocated_pcs, sizeof *pcs);
5820 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5822 if (lex_match_id (lexer, "LABEL"))
5824 lex_match (lexer, T_EQUALS);
5825 if (!lex_force_string (lexer))
5828 for (size_t i = 0; i < n_pcs; i++)
5830 free (pcs[i]->label);
5831 pcs[i]->label = ss_xstrdup (lex_tokss (lexer));
5836 else if (lex_match_id (lexer, "FORMAT"))
5838 lex_match (lexer, T_EQUALS);
5840 struct ctables_summary_spec_set sss;
5841 if (!ctables_parse_pproperties_format (lexer, &sss))
5844 for (size_t i = 0; i < n_pcs; i++)
5847 ctables_summary_spec_set_uninit (pcs[i]->specs);
5849 pcs[i]->specs = xmalloc (sizeof *pcs[i]->specs);
5850 ctables_summary_spec_set_clone (pcs[i]->specs, &sss);
5852 ctables_summary_spec_set_uninit (&sss);
5854 else if (lex_match_id (lexer, "HIDESOURCECATS"))
5856 lex_match (lexer, T_EQUALS);
5857 bool hide_source_cats;
5858 if (!parse_bool (lexer, &hide_source_cats))
5860 for (size_t i = 0; i < n_pcs; i++)
5861 pcs[i]->hide_source_cats = hide_source_cats;
5865 lex_error_expecting (lexer, "LABEL", "FORMAT", "HIDESOURCECATS");
5878 put_strftime (struct string *out, time_t now, const char *format)
5880 const struct tm *tm = localtime (&now);
5882 strftime (value, sizeof value, format, tm);
5883 ds_put_cstr (out, value);
5887 skip_prefix (struct substring *s, struct substring prefix)
5889 if (ss_starts_with (*s, prefix))
5891 ss_advance (s, prefix.length);
5899 put_table_expression (struct string *out, struct lexer *lexer,
5900 struct dictionary *dict, int expr_start, int expr_end)
5903 for (int ofs = expr_start; ofs < expr_end; ofs++)
5905 const struct token *t = lex_ofs_token (lexer, ofs);
5906 if (t->type == T_LBRACK)
5908 else if (t->type == T_RBRACK && nest > 0)
5914 else if (t->type == T_ID)
5916 const struct variable *var
5917 = dict_lookup_var (dict, t->string.string);
5918 const char *label = var ? var_get_label (var) : NULL;
5919 ds_put_cstr (out, label ? label : t->string.string);
5923 if (ofs != expr_start && t->type != T_RPAREN && ds_last (out) != ' ')
5924 ds_put_byte (out, ' ');
5926 char *repr = lex_ofs_representation (lexer, ofs, ofs);
5927 ds_put_cstr (out, repr);
5930 if (ofs + 1 != expr_end && t->type != T_LPAREN)
5931 ds_put_byte (out, ' ');
5937 put_title_text (struct string *out, struct substring in, time_t now,
5938 struct lexer *lexer, struct dictionary *dict,
5939 int expr_start, int expr_end)
5943 size_t chunk = ss_find_byte (in, ')');
5944 ds_put_substring (out, ss_head (in, chunk));
5945 ss_advance (&in, chunk);
5946 if (ss_is_empty (in))
5949 if (skip_prefix (&in, ss_cstr (")DATE")))
5950 put_strftime (out, now, "%x");
5951 else if (skip_prefix (&in, ss_cstr (")TIME")))
5952 put_strftime (out, now, "%X");
5953 else if (skip_prefix (&in, ss_cstr (")TABLE")))
5954 put_table_expression (out, lexer, dict, expr_start, expr_end);
5957 ds_put_byte (out, ')');
5958 ss_advance (&in, 1);
5964 cmd_ctables (struct lexer *lexer, struct dataset *ds)
5966 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
5967 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
5968 enum settings_value_show tvars = settings_get_show_variables ();
5969 for (size_t i = 0; i < n_vars; i++)
5970 vlabels[i] = (enum ctables_vlabel) tvars;
5972 struct pivot_table_look *look = pivot_table_look_unshare (
5973 pivot_table_look_ref (pivot_table_look_get_default ()));
5974 look->omit_empty = false;
5976 struct ctables *ct = xmalloc (sizeof *ct);
5977 *ct = (struct ctables) {
5978 .dict = dataset_dict (ds),
5980 .ctables_formats = FMT_SETTINGS_INIT,
5982 .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
5985 time_t now = time (NULL);
5990 const char *dot_string;
5991 const char *comma_string;
5993 static const struct ctf ctfs[4] = {
5994 { CTEF_NEGPAREN, "(,,,)", "(...)" },
5995 { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
5996 { CTEF_PAREN, "-,(,),", "-.(.)." },
5997 { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
5999 bool is_dot = settings_get_fmt_settings ()->decimal == '.';
6000 for (size_t i = 0; i < 4; i++)
6002 const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
6003 fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
6004 fmt_number_style_from_string (s));
6007 if (!lex_force_match (lexer, T_SLASH))
6010 while (!lex_match_id (lexer, "TABLE"))
6012 if (lex_match_id (lexer, "FORMAT"))
6014 double widths[2] = { SYSMIS, SYSMIS };
6015 double units_per_inch = 72.0;
6017 while (lex_token (lexer) != T_SLASH)
6019 if (lex_match_id (lexer, "MINCOLWIDTH"))
6021 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
6024 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
6026 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
6029 else if (lex_match_id (lexer, "UNITS"))
6031 lex_match (lexer, T_EQUALS);
6032 if (lex_match_id (lexer, "POINTS"))
6033 units_per_inch = 72.0;
6034 else if (lex_match_id (lexer, "INCHES"))
6035 units_per_inch = 1.0;
6036 else if (lex_match_id (lexer, "CM"))
6037 units_per_inch = 2.54;
6040 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
6044 else if (lex_match_id (lexer, "EMPTY"))
6049 lex_match (lexer, T_EQUALS);
6050 if (lex_match_id (lexer, "ZERO"))
6052 /* Nothing to do. */
6054 else if (lex_match_id (lexer, "BLANK"))
6055 ct->zero = xstrdup ("");
6056 else if (lex_force_string (lexer))
6058 ct->zero = ss_xstrdup (lex_tokss (lexer));
6064 else if (lex_match_id (lexer, "MISSING"))
6066 lex_match (lexer, T_EQUALS);
6067 if (!lex_force_string (lexer))
6071 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
6072 ? ss_xstrdup (lex_tokss (lexer))
6078 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
6079 "UNITS", "EMPTY", "MISSING");
6084 if (widths[0] != SYSMIS && widths[1] != SYSMIS
6085 && widths[0] > widths[1])
6087 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
6091 for (size_t i = 0; i < 2; i++)
6092 if (widths[i] != SYSMIS)
6094 int *wr = ct->look->width_ranges[TABLE_HORZ];
6095 wr[i] = widths[i] / units_per_inch * 96.0;
6100 else if (lex_match_id (lexer, "VLABELS"))
6102 if (!lex_force_match_id (lexer, "VARIABLES"))
6104 lex_match (lexer, T_EQUALS);
6106 struct variable **vars;
6108 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
6112 if (!lex_force_match_id (lexer, "DISPLAY"))
6117 lex_match (lexer, T_EQUALS);
6119 enum ctables_vlabel vlabel;
6120 if (lex_match_id (lexer, "DEFAULT"))
6121 vlabel = (enum ctables_vlabel) settings_get_show_variables ();
6122 else if (lex_match_id (lexer, "NAME"))
6124 else if (lex_match_id (lexer, "LABEL"))
6125 vlabel = CTVL_LABEL;
6126 else if (lex_match_id (lexer, "BOTH"))
6128 else if (lex_match_id (lexer, "NONE"))
6132 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
6138 for (size_t i = 0; i < n_vars; i++)
6139 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
6142 else if (lex_match_id (lexer, "MRSETS"))
6144 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
6146 lex_match (lexer, T_EQUALS);
6147 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
6150 else if (lex_match_id (lexer, "SMISSING"))
6152 if (lex_match_id (lexer, "VARIABLE"))
6153 ct->smissing_listwise = false;
6154 else if (lex_match_id (lexer, "LISTWISE"))
6155 ct->smissing_listwise = true;
6158 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
6162 else if (lex_match_id (lexer, "PCOMPUTE"))
6164 if (!ctables_parse_pcompute (lexer, dataset_dict (ds), ct))
6167 else if (lex_match_id (lexer, "PPROPERTIES"))
6169 if (!ctables_parse_pproperties (lexer, ct))
6172 else if (lex_match_id (lexer, "WEIGHT"))
6174 if (!lex_force_match_id (lexer, "VARIABLE"))
6176 lex_match (lexer, T_EQUALS);
6177 ct->e_weight = parse_variable (lexer, dataset_dict (ds));
6181 else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
6183 if (lex_match_id (lexer, "COUNT"))
6185 lex_match (lexer, T_EQUALS);
6186 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT",
6189 ct->hide_threshold = lex_integer (lexer);
6192 else if (ct->hide_threshold == 0)
6193 ct->hide_threshold = 5;
6197 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
6198 "SMISSING", "PCOMPUTE", "PPROPERTIES",
6199 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
6203 if (!lex_force_match (lexer, T_SLASH))
6207 size_t allocated_tables = 0;
6210 if (ct->n_tables >= allocated_tables)
6211 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
6212 sizeof *ct->tables);
6214 struct ctables_category *cat = xmalloc (sizeof *cat);
6215 *cat = (struct ctables_category) {
6217 .include_missing = false,
6218 .sort_ascending = true,
6221 struct ctables_categories *c = xmalloc (sizeof *c);
6222 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
6223 *c = (struct ctables_categories) {
6230 struct ctables_categories **categories = xnmalloc (n_vars,
6231 sizeof *categories);
6232 for (size_t i = 0; i < n_vars; i++)
6235 struct ctables_table *t = xmalloc (sizeof *t);
6236 *t = (struct ctables_table) {
6238 .slabels_axis = PIVOT_AXIS_COLUMN,
6239 .slabels_visible = true,
6240 .clabels_values_map = HMAP_INITIALIZER (t->clabels_values_map),
6242 [PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW,
6243 [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
6244 [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
6246 .clabels_from_axis = PIVOT_AXIS_LAYER,
6247 .categories = categories,
6248 .n_categories = n_vars,
6251 ct->tables[ct->n_tables++] = t;
6253 lex_match (lexer, T_EQUALS);
6254 int expr_start = lex_ofs (lexer);
6255 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
6257 if (lex_match (lexer, T_BY))
6259 if (!ctables_axis_parse (lexer, dataset_dict (ds),
6260 ct, t, PIVOT_AXIS_COLUMN))
6263 if (lex_match (lexer, T_BY))
6265 if (!ctables_axis_parse (lexer, dataset_dict (ds),
6266 ct, t, PIVOT_AXIS_LAYER))
6270 int expr_end = lex_ofs (lexer);
6272 if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
6273 && !t->axes[PIVOT_AXIS_LAYER])
6275 lex_error (lexer, _("At least one variable must be specified."));
6279 const struct ctables_axis *scales[PIVOT_N_AXES];
6280 size_t n_scales = 0;
6281 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6283 scales[a] = find_scale (t->axes[a]);
6289 msg (SE, _("Scale variables may appear only on one axis."));
6290 if (scales[PIVOT_AXIS_ROW])
6291 msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
6292 _("This scale variable appears on the rows axis."));
6293 if (scales[PIVOT_AXIS_COLUMN])
6294 msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
6295 _("This scale variable appears on the columns axis."));
6296 if (scales[PIVOT_AXIS_LAYER])
6297 msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
6298 _("This scale variable appears on the layer axis."));
6302 const struct ctables_axis *summaries[PIVOT_N_AXES];
6303 size_t n_summaries = 0;
6304 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6306 summaries[a] = (scales[a]
6308 : find_categorical_summary_spec (t->axes[a]));
6312 if (n_summaries > 1)
6314 msg (SE, _("Summaries may appear only on one axis."));
6315 if (summaries[PIVOT_AXIS_ROW])
6316 msg_at (SN, summaries[PIVOT_AXIS_ROW]->loc,
6317 _("This variable on the rows axis has a summary."));
6318 if (summaries[PIVOT_AXIS_COLUMN])
6319 msg_at (SN, summaries[PIVOT_AXIS_COLUMN]->loc,
6320 _("This variable on the columns axis has a summary."));
6321 if (summaries[PIVOT_AXIS_LAYER])
6322 msg_at (SN, summaries[PIVOT_AXIS_LAYER]->loc,
6323 _("This variable on the layers axis has a summary."));
6326 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
6327 if (n_summaries ? summaries[a] : t->axes[a])
6329 t->summary_axis = a;
6333 if (lex_token (lexer) == T_ENDCMD)
6335 if (!ctables_prepare_table (t))
6339 if (!lex_force_match (lexer, T_SLASH))
6342 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
6344 if (lex_match_id (lexer, "SLABELS"))
6346 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
6348 if (lex_match_id (lexer, "POSITION"))
6350 lex_match (lexer, T_EQUALS);
6351 if (lex_match_id (lexer, "COLUMN"))
6352 t->slabels_axis = PIVOT_AXIS_COLUMN;
6353 else if (lex_match_id (lexer, "ROW"))
6354 t->slabels_axis = PIVOT_AXIS_ROW;
6355 else if (lex_match_id (lexer, "LAYER"))
6356 t->slabels_axis = PIVOT_AXIS_LAYER;
6359 lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
6363 else if (lex_match_id (lexer, "VISIBLE"))
6365 lex_match (lexer, T_EQUALS);
6366 if (!parse_bool (lexer, &t->slabels_visible))
6371 lex_error_expecting (lexer, "POSITION", "VISIBLE");
6376 else if (lex_match_id (lexer, "CLABELS"))
6378 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
6380 if (lex_match_id (lexer, "AUTO"))
6382 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
6383 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
6385 else if (lex_match_id (lexer, "ROWLABELS"))
6387 lex_match (lexer, T_EQUALS);
6388 if (lex_match_id (lexer, "OPPOSITE"))
6389 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
6390 else if (lex_match_id (lexer, "LAYER"))
6391 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
6394 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
6398 else if (lex_match_id (lexer, "COLLABELS"))
6400 lex_match (lexer, T_EQUALS);
6401 if (lex_match_id (lexer, "OPPOSITE"))
6402 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
6403 else if (lex_match_id (lexer, "LAYER"))
6404 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
6407 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
6413 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
6419 else if (lex_match_id (lexer, "CRITERIA"))
6421 if (!lex_force_match_id (lexer, "CILEVEL"))
6423 lex_match (lexer, T_EQUALS);
6425 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
6427 t->cilevel = lex_number (lexer);
6430 else if (lex_match_id (lexer, "CATEGORIES"))
6432 if (!ctables_table_parse_categories (lexer, dataset_dict (ds),
6436 else if (lex_match_id (lexer, "TITLES"))
6441 if (lex_match_id (lexer, "CAPTION"))
6442 textp = &t->caption;
6443 else if (lex_match_id (lexer, "CORNER"))
6445 else if (lex_match_id (lexer, "TITLE"))
6449 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
6452 lex_match (lexer, T_EQUALS);
6454 struct string s = DS_EMPTY_INITIALIZER;
6455 while (lex_is_string (lexer))
6457 if (!ds_is_empty (&s))
6458 ds_put_byte (&s, ' ');
6459 put_title_text (&s, lex_tokss (lexer), now,
6460 lexer, dataset_dict (ds),
6461 expr_start, expr_end);
6465 *textp = ds_steal_cstr (&s);
6467 while (lex_token (lexer) != T_SLASH
6468 && lex_token (lexer) != T_ENDCMD);
6470 else if (lex_match_id (lexer, "SIGTEST"))
6474 t->chisq = xmalloc (sizeof *t->chisq);
6475 *t->chisq = (struct ctables_chisq) {
6477 .include_mrsets = true,
6478 .all_visible = true,
6484 if (lex_match_id (lexer, "TYPE"))
6486 lex_match (lexer, T_EQUALS);
6487 if (!lex_force_match_id (lexer, "CHISQUARE"))
6490 else if (lex_match_id (lexer, "ALPHA"))
6492 lex_match (lexer, T_EQUALS);
6493 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
6495 t->chisq->alpha = lex_number (lexer);
6498 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
6500 lex_match (lexer, T_EQUALS);
6501 if (parse_bool (lexer, &t->chisq->include_mrsets))
6504 else if (lex_match_id (lexer, "CATEGORIES"))
6506 lex_match (lexer, T_EQUALS);
6507 if (lex_match_id (lexer, "ALLVISIBLE"))
6508 t->chisq->all_visible = true;
6509 else if (lex_match_id (lexer, "SUBTOTALS"))
6510 t->chisq->all_visible = false;
6513 lex_error_expecting (lexer,
6514 "ALLVISIBLE", "SUBTOTALS");
6520 lex_error_expecting (lexer, "TYPE", "ALPHA",
6521 "INCLUDEMRSETS", "CATEGORIES");
6525 while (lex_token (lexer) != T_SLASH
6526 && lex_token (lexer) != T_ENDCMD);
6528 else if (lex_match_id (lexer, "COMPARETEST"))
6532 t->pairwise = xmalloc (sizeof *t->pairwise);
6533 *t->pairwise = (struct ctables_pairwise) {
6535 .alpha = { .05, .05 },
6536 .adjust = BONFERRONI,
6537 .include_mrsets = true,
6538 .meansvariance_allcats = true,
6539 .all_visible = true,
6548 if (lex_match_id (lexer, "TYPE"))
6550 lex_match (lexer, T_EQUALS);
6551 if (lex_match_id (lexer, "PROP"))
6552 t->pairwise->type = PROP;
6553 else if (lex_match_id (lexer, "MEAN"))
6554 t->pairwise->type = MEAN;
6557 lex_error_expecting (lexer, "PROP", "MEAN");
6561 else if (lex_match_id (lexer, "ALPHA"))
6563 lex_match (lexer, T_EQUALS);
6565 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
6567 double a0 = lex_number (lexer);
6570 lex_match (lexer, T_COMMA);
6571 if (lex_is_number (lexer))
6573 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
6575 double a1 = lex_number (lexer);
6578 t->pairwise->alpha[0] = MIN (a0, a1);
6579 t->pairwise->alpha[1] = MAX (a0, a1);
6582 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
6584 else if (lex_match_id (lexer, "ADJUST"))
6586 lex_match (lexer, T_EQUALS);
6587 if (lex_match_id (lexer, "BONFERRONI"))
6588 t->pairwise->adjust = BONFERRONI;
6589 else if (lex_match_id (lexer, "BH"))
6590 t->pairwise->adjust = BH;
6591 else if (lex_match_id (lexer, "NONE"))
6592 t->pairwise->adjust = 0;
6595 lex_error_expecting (lexer, "BONFERRONI", "BH",
6600 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
6602 lex_match (lexer, T_EQUALS);
6603 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
6606 else if (lex_match_id (lexer, "MEANSVARIANCE"))
6608 lex_match (lexer, T_EQUALS);
6609 if (lex_match_id (lexer, "ALLCATS"))
6610 t->pairwise->meansvariance_allcats = true;
6611 else if (lex_match_id (lexer, "TESTEDCATS"))
6612 t->pairwise->meansvariance_allcats = false;
6615 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
6619 else if (lex_match_id (lexer, "CATEGORIES"))
6621 lex_match (lexer, T_EQUALS);
6622 if (lex_match_id (lexer, "ALLVISIBLE"))
6623 t->pairwise->all_visible = true;
6624 else if (lex_match_id (lexer, "SUBTOTALS"))
6625 t->pairwise->all_visible = false;
6628 lex_error_expecting (lexer, "ALLVISIBLE",
6633 else if (lex_match_id (lexer, "MERGE"))
6635 lex_match (lexer, T_EQUALS);
6636 if (!parse_bool (lexer, &t->pairwise->merge))
6639 else if (lex_match_id (lexer, "STYLE"))
6641 lex_match (lexer, T_EQUALS);
6642 if (lex_match_id (lexer, "APA"))
6643 t->pairwise->apa_style = true;
6644 else if (lex_match_id (lexer, "SIMPLE"))
6645 t->pairwise->apa_style = false;
6648 lex_error_expecting (lexer, "APA", "SIMPLE");
6652 else if (lex_match_id (lexer, "SHOWSIG"))
6654 lex_match (lexer, T_EQUALS);
6655 if (!parse_bool (lexer, &t->pairwise->show_sig))
6660 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
6661 "INCLUDEMRSETS", "MEANSVARIANCE",
6662 "CATEGORIES", "MERGE", "STYLE",
6667 while (lex_token (lexer) != T_SLASH
6668 && lex_token (lexer) != T_ENDCMD);
6672 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
6673 "CRITERIA", "CATEGORIES", "TITLES",
6674 "SIGTEST", "COMPARETEST");
6678 if (!lex_match (lexer, T_SLASH))
6682 if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
6683 && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
6685 msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
6689 if (!ctables_prepare_table (t))
6692 while (lex_token (lexer) != T_ENDCMD);
6694 bool ok = ctables_execute (ds, ct);
6695 ctables_destroy (ct);
6696 return ok ? CMD_SUCCESS : CMD_FAILURE;
6699 ctables_destroy (ct);